mixmaster

mixmaster 3.0 patched for libressl
git clone git://parazyd.org/mixmaster.git
Log | Files | Refs | README

util.c (15615B)


      1 /* Mixmaster version 3.0  --  (C) 1999 - 2006 Anonymizer Inc. and others.
      2 
      3    Mixmaster may be redistributed and modified under certain conditions.
      4    This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
      5    ANY KIND, either express or implied. See the file COPYRIGHT for
      6    details.
      7 
      8    Utility functions
      9    $Id: util.c 934 2006-06-24 13:40:39Z rabbi $ */
     10 
     11 #include "mix3.h"
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <ctype.h>
     15 #include <fcntl.h>
     16 #include <sys/types.h>
     17 #include <sys/stat.h>
     18 #ifdef POSIX
     19 #include <signal.h>
     20 #include <errno.h>
     21 #include <unistd.h>
     22 #include <sys/file.h>
     23 #include <termios.h>
     24 #else /* end of POSIX */
     25 #include <io.h>
     26 #endif /* else if not POSIX */
     27 #ifdef HAVE_GETKEY
     28 #include <pc.h>
     29 #endif /* HAVE_GETKEY */
     30 #include <assert.h>
     31 
     32 /** string comparison functions. return 1 on match, 0 otherwise ********/
     33 
     34 int strileft(const char *string, const char *keyword)
     35 {
     36   register unsigned int i;
     37 
     38   for (i = 0; keyword[i] != '\0'; i++)
     39     if (tolower(string[i]) != tolower(keyword[i]))
     40       return 0;
     41   return 1;
     42 }
     43 
     44 int striright(const char *string, const char *keyword)
     45 {
     46   int l;
     47   l = strlen(string) - strlen(keyword);
     48   return (l >= 0 ? strieq(string + l, keyword) : -1);
     49 }
     50 
     51 int strleft(const char *string, const char *keyword)
     52 {
     53   register unsigned int i;
     54 
     55   for (i = 0; keyword[i] != '\0'; i++)
     56     if (string[i] != keyword[i])
     57       return 0;
     58   return 1;
     59 }
     60 
     61 int strifind(const char *string, const char *keyword)
     62 {
     63   register unsigned int i, j;
     64   char k;
     65 
     66   k = tolower(keyword[0]);
     67   for (i = 0; string[i] != '\0'; i++) {
     68     if (tolower(string[i]) == k) {
     69       for (j = 1; keyword[j] != '\0'; j++)
     70 	if (tolower(string[i + j]) != tolower(keyword[j]))
     71 	  goto next;
     72       return 1;
     73     }
     74   next:
     75     ;
     76   }
     77   return 0;
     78 }
     79 
     80 int strieq(const char *s1, const char *s2)
     81 {
     82   register unsigned int i = 0;
     83 
     84   do
     85     if (tolower(s1[i]) != tolower(s2[i]))
     86       return 0;
     87   while (s1[i++] != '\0') ;
     88   return 1;
     89 }
     90 
     91 int streq(const char *a, const char *b)
     92 {
     93   return (strcmp(a, b) == 0);
     94 }
     95 
     96 int strfind(const char *a, const char *keyword)
     97 {
     98   return (strstr(a, keyword) != NULL);
     99 }
    100 
    101 void strcatn(char *dest, const char *src, int n)
    102 {
    103   int l;
    104   l = strlen(dest);
    105   if (l < n)
    106     strncpy(dest + l, src, n - l - 1);
    107   dest[n-1] = '\0';
    108 }
    109 
    110 /** files **************************************************************/
    111 
    112 int mixfile(char *path, const char *name)
    113 {
    114   char *h;
    115   assert(path != NULL && name != NULL);
    116 
    117 #ifdef POSIX
    118   if (name[0] == '~' && name[1] == DIRSEP && (h = getenv("HOME")) != NULL) {
    119     strncpy(path, h, PATHMAX);
    120     path[PATHMAX-1] = '\0';
    121     strcatn(path, name + 1, PATHMAX);
    122   } else
    123 #endif /* POSIX */
    124   if (name[0] == DIRSEP || (isalpha(name[0]) && name[1] == ':') || MIXDIR == NULL) {
    125     strncpy(path, name, PATHMAX);
    126     path[PATHMAX-1] = '\0';
    127   } else {
    128     strncpy(path, MIXDIR, PATHMAX);
    129     path[PATHMAX-1] = '\0';
    130     strcatn(path, name, PATHMAX);
    131   }
    132   return (0);
    133 }
    134 
    135 FILE *mix_openfile(const char *name, const char *a)
    136 {
    137   char path[PATHMAX];
    138 
    139   mixfile(path, name);
    140   return (fopen(path, a));
    141 }
    142 
    143 FILE *openpipe(const char *prog)
    144 {
    145   FILE *p = NULL;
    146 
    147 #ifdef POSIX
    148   p = popen(prog, "w");
    149 #endif /* POSIX */
    150 #ifdef _MSC
    151   p = _popen(prog, "w");
    152 #endif /* _MSC */
    153 
    154   if (p == NULL)
    155     errlog(ERRORMSG, "Unable to open pipe to %s\n", prog);
    156   return p;
    157 }
    158 
    159 int
    160 file_to_out(const char *filename)
    161 {
    162     int len;
    163     FILE *fp;
    164     char chunk[1024];
    165 
    166     if ((fp = mix_openfile(filename, "r")) == NULL)
    167     	return -1;
    168     while ((len = fread(chunk, 1, sizeof(chunk), fp)) > 0)
    169     	{
    170 	    fwrite(chunk, 1, len, stdout);
    171 	}
    172     fclose (fp);
    173     return (len == 0 ? 0 : (-1));
    174 }
    175 
    176 int closepipe(FILE *p)
    177 {
    178 #ifdef POSIX
    179   return (pclose(p));
    180 #elif defined(_MSC) /* end of POSIX */
    181   return (_pclose(p));
    182 #else /* end of defined(_MSC) */
    183   return -1;
    184 #endif /* else if not defined(_MSC), POSIX */
    185 }
    186 
    187 /** Base 64 encoding ****************************************************/
    188 
    189 static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    190 static byte asctobin[] =
    191 {
    192   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    193   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    194   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    195   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    196   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    197   0x80, 0x80, 0x80, 0076, 0x80, 0x80, 0x80, 0077,
    198   0064, 0065, 0066, 0067, 0070, 0071, 0072, 0073,
    199   0074, 0075, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    200   0x80, 0000, 0001, 0002, 0003, 0004, 0005, 0006,
    201   0007, 0010, 0011, 0012, 0013, 0014, 0015, 0016,
    202   0017, 0020, 0021, 0022, 0023, 0024, 0025, 0026,
    203   0027, 0030, 0031, 0x80, 0x80, 0x80, 0x80, 0x80,
    204   0x80, 0032, 0033, 0034, 0035, 0036, 0037, 0040,
    205   0041, 0042, 0043, 0044, 0045, 0046, 0047, 0050,
    206   0051, 0052, 0053, 0054, 0055, 0056, 0057, 0060,
    207   0061, 0062, 0063, 0x80, 0x80, 0x80, 0x80, 0x80,
    208 
    209   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    210   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    211   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    212   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    213   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    214   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    215   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    216   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    217   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    218   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    219   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    220   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    221   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    222   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    223   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    224   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
    225 };
    226 
    227 void id_encode(byte id[], byte *s)
    228 {
    229   sprintf
    230     (s, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
    231      id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9],
    232      id[10], id[11], id[12], id[13], id[14], id[15]);
    233 }
    234 
    235 void id_decode(byte *s, byte id[])
    236 {
    237   int i, x[16];
    238 
    239   sscanf
    240     (s, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
    241      x, x + 1, x + 2, x + 3, x + 4, x + 5, x + 6, x + 7, x + 8,
    242      x + 9, x + 10, x + 11, x + 12, x + 13, x + 14, x + 15);
    243   for (i = 0; i < 16; i++)
    244     id[i] = x[i];
    245 }
    246 
    247 int encode(BUFFER *in, int linelen)
    248 {
    249   byte *b, *e;
    250   int i, l, m;
    251   unsigned long u;
    252   BUFFER *out;
    253 
    254   out = buf_new();
    255 
    256   l = in->length;
    257   if (l % 3 != 0)
    258     l += 2;
    259   l = l / 3 * 4;
    260 
    261   if (linelen) {
    262     l += l / linelen + (l % linelen > 0 ? 1 : 0);
    263   }
    264   linelen /= 4;			/* blocks of 4 characters */
    265 
    266   buf_prepare(out, l);
    267 
    268   b = in->data;
    269   e = out->data;
    270   m = in->length - 2;
    271   for (i = 0, l = 0; i < m; i += 3) {
    272     u = ((unsigned long) b[i] << 16) | ((unsigned long) b[i + 1] << 8) |
    273 	b[i + 2];
    274     *e++ = bintoasc[(u >> 18) & 0x3f];
    275     *e++ = bintoasc[(u >> 12) & 0x3f];
    276     *e++ = bintoasc[(u >> 6) & 0x3f];
    277     *e++ = bintoasc[u & 0x3f];
    278     if (linelen && ++l >= linelen) {
    279       l = 0;
    280       *e++ = '\n';
    281     }
    282   }
    283   if (i < in->length) {
    284     *e++ = bintoasc[b[i] >> 2];
    285     *e++ = bintoasc[((b[i] << 4) & 0x30) | ((b[i + 1] >> 4) & 0x0f)];
    286     if (i + 1 == in->length)
    287       *e++ = '=';
    288     else
    289       *e++ = bintoasc[((b[i + 1] << 2) & 0x3c) | ((b[i + 2] >> 6) & 0x03)];
    290     *e++ = '=';
    291     ++l;
    292   }
    293   if (linelen && l != 0)
    294     *e++ = '\n';
    295   *e = '\0';
    296 
    297   assert(out->data + out->length == e);
    298   buf_move(in, out);
    299   buf_free(out);
    300   return (0);
    301 }
    302 
    303 int decode(BUFFER *in, BUFFER *out)
    304 {
    305   int err = 0;
    306   register byte c0 = 0, c1 = 0, c2 = 0, c3 = 0;
    307   register byte *a, *d, *end;
    308   int tempbuf = 0;
    309   int i;
    310 
    311   if (in == out) {
    312     out = buf_new();
    313     tempbuf = 1;
    314   }
    315   buf_prepare(out, 3 * (in->length - in->ptr) / 4);
    316 
    317   a = in->data + in->ptr;
    318   end = in->data + in->length - 3;
    319   d = out->data;
    320   i = 0;
    321 
    322   while (a < end) {
    323     if ((c0 = asctobin[a[0]]) & 0x80 ||
    324 	(c1 = asctobin[a[1]]) & 0x80 ||
    325 	(c2 = asctobin[a[2]]) & 0x80 ||
    326 	(c3 = asctobin[a[3]]) & 0x80) {
    327       if (a[0] == '\n') {	/* ignore newline */
    328 	a++;
    329 	continue;
    330       } else if (a[0] == '\r' && a[1] == '\n') {	/* ignore crlf */
    331 	a += 2;
    332 	continue;
    333       } else if (a[0] == '=' && a[1] == '4' && a[2] == '6' && !(asctobin[a[5]] & 0x80) ) {
    334 	a += 2;			/* '=46' at the left of a line really is 'F' */
    335 	*a = 'F';		/* fix in memory ... */
    336 	continue;
    337       } else if (a[2] == '=' || a[3] == '=') {
    338 	if (a[0] & 0x80 || (c0 = asctobin[a[0]]) & 0x80 ||
    339 	    a[1] & 0x80 || (c1 = asctobin[a[1]]) & 0x80)
    340 	  err = -1;
    341 	else if (a[2] == '=')
    342 	  c2 = 0, i += 1;
    343 	else if (a[2] & 0x80 || (c2 = asctobin[a[2]]) & 0x80)
    344 	  err = -1;
    345 	else
    346 	  i += 2;
    347 	if (err == 0) {
    348 	  /* read the correct final block */
    349 	  *d++ = (byte) ((c0 << 2) | (c1 >> 4));
    350 	  *d++ = (byte) ((c1 << 4) | (c2 >> 2));
    351 	  if (a[3] != '=')
    352 	    *d++ = (byte) ((c2 << 6));
    353 #if 1
    354 	  if (a + 4 < in->data + in->length) {
    355 	    a += 4;
    356 	    continue;		/* support Mixmaster 2.0.3 encoding */
    357 	  }
    358 #endif /* 1 */
    359 	  break;
    360 	}
    361       }
    362       err = -1;
    363       break;
    364     }
    365     a += 4;
    366 
    367     *d++ = (byte) ((c0 << 2) | (c1 >> 4));
    368     *d++ = (byte) ((c1 << 4) | (c2 >> 2));
    369     *d++ = (byte) ((c2 << 6) | c3);
    370     i += 3;
    371   }
    372 
    373   in->ptr = a - in->data;
    374 
    375   assert(i <= out->length);
    376   out->length = i;
    377 
    378   if (tempbuf) {
    379     buf_move(in, out);
    380     buf_free(out);
    381   }
    382   return (err);
    383 }
    384 
    385 LOCK *lockfile(char *filename)
    386 {
    387   LOCK *l;
    388   char name[LINELEN];
    389 
    390   strcpy(name, "lck");
    391   if (strchr(filename, DIRSEP))
    392     strcatn(name, strrchr(filename, DIRSEP), LINELEN);
    393   else
    394     strcatn(name, filename, LINELEN);
    395   l = malloc(sizeof(LOCK));
    396 
    397   l->name = malloc(PATHMAX);
    398   mixfile(l->name, name);
    399   l->f = mix_openfile(l->name, "w+");
    400   if (l->f)
    401     lock(l->f);
    402   return (l);
    403 }
    404 
    405 int unlockfile(LOCK *l)
    406 {
    407   if (l->f) {
    408     unlock(l->f);
    409     fclose(l->f);
    410   }
    411   unlink(l->name);
    412   free(l->name);
    413   free(l);
    414   return (0);
    415 }
    416 
    417 int lock(FILE *f)
    418 {
    419 #ifndef WIN32
    420   struct flock lockstruct;
    421 
    422   lockstruct.l_type = F_WRLCK;
    423   lockstruct.l_whence = 0;
    424   lockstruct.l_start = 0;
    425   lockstruct.l_len = 0;
    426   return (fcntl(fileno(f), F_SETLKW, &lockstruct));
    427 #else /* end of WIN32 */
    428   return (0);
    429 #endif /* else if not WIN32 */
    430 }
    431 
    432 int unlock(FILE *f)
    433 {
    434 #ifndef WIN32
    435 
    436   struct flock lockstruct;
    437 
    438   lockstruct.l_type = F_UNLCK;
    439   lockstruct.l_whence = 0;
    440   lockstruct.l_start = 0;
    441   lockstruct.l_len = 0;
    442   return (fcntl(fileno(f), F_SETLKW, &lockstruct));
    443 #else /* end of not WIN32 */
    444   return (0);
    445 #endif /* else if WIN32 */
    446 }
    447 
    448 /* get passphrase ******************************************************/
    449 
    450 static int getuserpass(BUFFER *b, int mode)
    451 {
    452   char p[LINELEN];
    453   int fd;
    454   int n;
    455 
    456 #ifdef HAVE_TERMIOS
    457   struct termios attr;
    458 
    459 #endif /* HAVE_TERMIOS */
    460 
    461   if (mode == 0)
    462     fprintf(stderr, "enter passphrase: ");
    463   else
    464     fprintf(stderr, "re-enter passphrase: ");
    465   fflush(stderr);
    466 #ifndef UNIX
    467 #ifdef HAVE_GETKEY
    468   for (n = 0; p[n] != '\n' && n < LINELEN; n++) {
    469     p[n] = getkey();
    470   }
    471   p[n] = 0;
    472 #else /* end of HAVE_GETKEY */
    473   scanf("%127s", p);
    474 #endif /* else if not HAVE_GETKEY */
    475 #else /* end of not UNIX */
    476   fd = open("/dev/tty", O_RDONLY);
    477   if (tcgetattr(fd, &attr) != 0)
    478     return (-1);
    479   attr.c_lflag &= ~ECHO;
    480   attr.c_lflag |= ICANON;
    481   if (tcsetattr(fd, TCSAFLUSH, &attr) != 0)
    482     return (-1);
    483 
    484   n = read(fd, p, LINELEN);
    485 
    486   attr.c_lflag |= ECHO;
    487   if (tcsetattr(fd, TCSAFLUSH, &attr) != 0)
    488     return (-1);
    489 
    490   close(fd);
    491   fprintf(stderr, "\n");
    492   p[n - 1] = 0;
    493 #endif /* else if UNIX */
    494   if (mode == 0)
    495     buf_appends(b, p);
    496   else
    497     return (bufeq(b, p));
    498   return (0);
    499 }
    500 
    501 static BUFFER *userpass = NULL;
    502 
    503 int user_pass(BUFFER *key)
    504 {
    505   if (userpass == NULL) {
    506     userpass = buf_new();
    507     userpass->sensitive = 1;
    508     if (getenv("MIXPASS"))
    509       buf_sets(userpass, getenv("MIXPASS"));
    510     else if (menu_getuserpass(userpass, 0) == -1)
    511       getuserpass(userpass, 0);
    512   }
    513   buf_set(key, userpass);
    514   key->sensitive = 1;
    515   return (0);
    516 }
    517 
    518 int user_confirmpass(BUFFER *key)
    519 {
    520   int ok;
    521 
    522   ok = menu_getuserpass(key, 1);
    523   if (ok == -1)
    524     ok = getuserpass(key, 1);
    525   return (ok);
    526 }
    527 
    528 void user_delpass(void)
    529 {
    530   if (userpass)
    531     buf_free(userpass);
    532   userpass = NULL;
    533 }
    534 
    535 int write_pidfile(char *pidfile)
    536 {
    537   int err = 0;
    538 #ifdef POSIX
    539   FILE *f;
    540   char host[LINELEN], myhostname[LINELEN];
    541   int pid, mypid;
    542   int assigned;
    543 
    544   mypid = getpid();
    545   gethostname(myhostname, LINELEN);
    546   myhostname[LINELEN-1] = '\0';
    547 
    548   f = mix_openfile(pidfile, "r+");
    549   if (f != NULL) {
    550     assert(LINELEN > 71);
    551     assigned = fscanf(f, "%d %70s", &pid, host);
    552     if (assigned == 2) {
    553       if (strcmp(host, myhostname) == 0) {
    554 	if (kill (pid, 0) == -1) {
    555 	  if (errno == ESRCH) {
    556 	    fprintf(stderr, "Rewriting stale pid file.\n");
    557 	    rewind(f);
    558 	    ftruncate(fileno(f), 0);
    559 	    fprintf(f, "%d %s\n", mypid, myhostname);
    560 	  } else {
    561 	    fprintf(stderr, "Pid file exists and process still running.\n");
    562 	    err = -1;
    563 	  }
    564 	} else {
    565 	  fprintf(stderr, "Pid file exists and process still running.\n");
    566 	  err = -1;
    567 	}
    568       } else {
    569 	/* Pid file was written on another host, fail */
    570 	fprintf(stderr, "Pid file exists and was created on another host (%s).\n", host);
    571 	err = -1;
    572       }
    573     } else {
    574       fprintf(stderr, "Pid file exists and and could not be parsed.\n");
    575       err = -1;
    576     }
    577   } else {
    578     if (errno == ENOENT) {
    579       f = mix_openfile(pidfile, "w+");
    580       if (f != NULL) {
    581 	fprintf(f, "%d %s\n", mypid, myhostname);
    582       } else {
    583 	fprintf(stderr, "Could not open pidfile for writing: %s\n", strerror(errno));
    584 	err = -1;
    585       }
    586     } else {
    587       fprintf(stderr, "Could not open pidfile for readwrite: %s\n", strerror(errno));
    588       err = -1;
    589     };
    590   }
    591   if(f)
    592     fclose(f);
    593 #endif /* POSIX */
    594   return (err);
    595 }
    596 
    597 int clear_pidfile(char *pidfile)
    598 {
    599 #ifdef POSIX
    600   char path[PATHMAX];
    601 
    602   mixfile(path, pidfile);
    603   return (unlink(path));
    604 #else /* end of POSIX */
    605   return (0);
    606 #endif /* else if not POSIX */
    607 }
    608 
    609 time_t parse_yearmonthday(char* str)
    610 {
    611   time_t date;
    612   int day, month, year;
    613 
    614   if (sscanf( str, "%d-%d-%d", &year, &month, &day) == 3 ) {
    615     struct tm timestruct;
    616     char *tz;
    617 
    618     tz = getenv("TZ");
    619 #ifdef HAVE_SETENV
    620     setenv("TZ", "GMT", 1);
    621 #else /* end of HAVE_SETENV */
    622     putenv("TZ=GMT");
    623 #endif /* else if not HAVE_SETENV */
    624     tzset();
    625     memset(&timestruct, 0, sizeof(timestruct));
    626     timestruct.tm_mday = day;
    627     timestruct.tm_mon = month - 1;
    628     timestruct.tm_year = year - 1900;
    629     date = mktime(&timestruct);
    630 #ifdef HAVE_SETENV
    631     if (tz)
    632       setenv("TZ", tz, 1);
    633     else
    634       unsetenv("TZ");
    635 #else  /* end of HAVE_SETENV */
    636     if (tz) {
    637       char envstr[LINELEN];
    638       snprintf(envstr, LINELEN, "TZ=%s", tz);
    639       putenv(envstr);
    640     } else
    641       putenv("TZ=");
    642 #endif /* else if not HAVE_SETENV */
    643     tzset();
    644     return date;
    645   } else
    646     return -1;
    647 }
    648 
    649 /* functions missing on some systems *************************************/
    650 
    651 #ifdef __RSXNT__
    652 int fileno(FILE *f)
    653 {
    654   return (f->_handle);
    655 }
    656 
    657 #endif /* __RSXNT__ */
    658 
    659 #ifdef _MSC	/* Visual C lacks dirent */
    660 
    661 DIR *opendir(const char *name)
    662 {
    663   DIR *dir;
    664   WIN32_FIND_DATA d;
    665   char path[PATHMAX];
    666 
    667   dir = malloc(sizeof(HANDLE));
    668 
    669   sprintf(path, "%s%c*", name, DIRSEP);
    670   *dir = FindFirstFile(path, &d);
    671   /* first file found is "." -- can be safely ignored here */
    672 
    673   if (*dir == INVALID_HANDLE_VALUE) {
    674     free(dir);
    675     return (NULL);
    676   } else
    677     return (dir);
    678 }
    679 
    680 struct dirent e;
    681 struct dirent *readdir(DIR *dir)
    682 {
    683   WIN32_FIND_DATA d;
    684   int ok;
    685 
    686   ok = FindNextFile(*dir, &d);
    687   if (ok) {
    688     strncpy(e.d_name, d.cFileName, PATHMAX);
    689     return (&e);
    690   } else
    691     return (NULL);
    692 }
    693 
    694 int closedir(DIR *dir)
    695 {
    696   if (dir) {
    697     FindClose(*dir);
    698     free(dir);
    699     return (0);
    700   }
    701   return (-1);
    702 }
    703 
    704 #endif /* _MSC */