xref: /openbsd-src/gnu/usr.bin/cvs/windows-NT/filesubr.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /* filesubr.c --- subroutines for dealing with files
2    Jim Blandy <jimb@cyclic.com>
3 
4    This file is part of GNU CVS.
5 
6    GNU CVS is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.  */
15 
16 /* These functions were moved out of subr.c because they need different
17    definitions under operating systems (like, say, Windows NT) with different
18    file system semantics.  */
19 
20 #include <io.h>
21 #include <windows.h>
22 
23 #include "cvs.h"
24 
25 static int deep_remove_dir PROTO((const char *path));
26 
27 /* Copies "from" to "to".  Note that the functionality here is similar
28    to the win32 function CopyFile, but (1) we copy LastAccessTime and
29    CopyFile doesn't, (2) we set file attributes to the default set by
30    the C library and CopyFile copies them.  Neither #1 nor #2 was intentional
31    as far as I know, but changing them could be confusing, unless there
32    is some reason they should be changed (this would need more
33    investigation).  */
34 void
35 copy_file (from, to)
36     const char *from;
37     const char *to;
38 {
39     struct stat sb;
40     struct utimbuf t;
41     int fdin, fdout;
42 
43     if (trace)
44 #ifdef SERVER_SUPPORT
45 	(void) fprintf (stderr, "%c-> copy(%s,%s)\n",
46 			(server_active) ? 'S' : ' ', from, to);
47 #else
48 	(void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
49 #endif
50     if (noexec)
51 	return;
52 
53     if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
54 	error (1, errno, "cannot open %s for copying", from);
55     if (fstat (fdin, &sb) < 0)
56 	error (1, errno, "cannot fstat %s", from);
57     if ((fdout = open (to, O_CREAT | O_TRUNC | O_RDWR | O_BINARY,
58 		       (int) sb.st_mode & 07777)) < 0)
59 	error (1, errno, "cannot create %s for copying", to);
60     if (sb.st_size > 0)
61     {
62 	char buf[BUFSIZ];
63 	int n;
64 
65 	for (;;)
66 	{
67 	    n = read (fdin, buf, sizeof(buf));
68 	    if (n == -1)
69 	    {
70 #ifdef EINTR
71 		if (errno == EINTR)
72 		    continue;
73 #endif
74 		error (1, errno, "cannot read file %s for copying", from);
75 	    }
76             else if (n == 0)
77 		break;
78 
79 	    if (write(fdout, buf, n) != n) {
80 		error (1, errno, "cannot write file %s for copying", to);
81 	    }
82 	}
83 
84 #ifdef HAVE_FSYNC
85 	if (fsync (fdout))
86 	    error (1, errno, "cannot fsync file %s after copying", to);
87 #endif
88     }
89 
90     if (close (fdin) < 0)
91 	error (0, errno, "cannot close %s", from);
92     if (close (fdout) < 0)
93 	error (1, errno, "cannot close %s", to);
94 
95     /* now, set the times for the copied file to match those of the original */
96     memset ((char *) &t, 0, sizeof (t));
97     t.actime = sb.st_atime;
98     t.modtime = sb.st_mtime;
99     (void) utime (to, &t);
100 }
101 
102 /* FIXME-krp: these functions would benefit from caching the char * &
103    stat buf.  */
104 
105 /*
106  * Returns non-zero if the argument file is a directory, or is a symbolic
107  * link which points to a directory.
108  */
109 int
110 isdir (file)
111     const char *file;
112 {
113     struct stat sb;
114 
115     if (stat (file, &sb) < 0)
116 	return (0);
117     return (S_ISDIR (sb.st_mode));
118 }
119 
120 /*
121  * Returns non-zero if the argument file is a symbolic link.
122  */
123 int
124 islink (file)
125     const char *file;
126 {
127 #ifdef S_ISLNK
128     struct stat sb;
129 
130     if (lstat (file, &sb) < 0)
131 	return (0);
132     return (S_ISLNK (sb.st_mode));
133 #else
134     return (0);
135 #endif
136 }
137 
138 /*
139  * Returns non-zero if the argument file exists.
140  */
141 int
142 isfile (file)
143     const char *file;
144 {
145     return isaccessible(file, F_OK);
146 }
147 
148 /*
149  * Returns non-zero if the argument file is readable.
150  */
151 int
152 isreadable (file)
153     const char *file;
154 {
155     return isaccessible(file, R_OK);
156 }
157 
158 /*
159  * Returns non-zero if the argument file is writable.
160  */
161 int
162 iswritable (file)
163     const char *file;
164 {
165     return isaccessible(file, W_OK);
166 }
167 
168 /*
169  * Returns non-zero if the argument file is accessable according to
170  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
171  * bits set.
172  */
173 int
174 isaccessible (file, mode)
175     const char *file;
176     const int mode;
177 {
178 #ifdef SETXID_SUPPORT
179     struct stat sb;
180     int umask = 0;
181     int gmask = 0;
182     int omask = 0;
183     int uid;
184 
185     if (stat(file, &sb) == -1)
186 	return 0;
187     if (mode == F_OK)
188 	return 1;
189 
190     uid = geteuid();
191     if (uid == 0)		/* superuser */
192     {
193 	if (mode & X_OK)
194 	    return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
195 	else
196 	    return 1;
197     }
198 
199     if (mode & R_OK)
200     {
201 	umask |= S_IRUSR;
202 	gmask |= S_IRGRP;
203 	omask |= S_IROTH;
204     }
205     if (mode & W_OK)
206     {
207 	umask |= S_IWUSR;
208 	gmask |= S_IWGRP;
209 	omask |= S_IWOTH;
210     }
211     if (mode & X_OK)
212     {
213 	umask |= S_IXUSR;
214 	gmask |= S_IXGRP;
215 	omask |= S_IXOTH;
216     }
217 
218     if (sb.st_uid == uid)
219 	return (sb.st_mode & umask) == umask;
220     else if (sb.st_gid == getegid())
221 	return (sb.st_mode & gmask) == gmask;
222     else
223 	return (sb.st_mode & omask) == omask;
224 #else
225     return access(file, mode) == 0;
226 #endif
227 }
228 
229 /*
230  * Open a file and die if it fails
231  */
232 FILE *
233 open_file (name, mode)
234     const char *name;
235     const char *mode;
236 {
237     FILE *fp;
238 
239     if ((fp = fopen (name, mode)) == NULL)
240 	error (1, errno, "cannot open %s", name);
241     return (fp);
242 }
243 
244 /*
245  * Make a directory and die if it fails
246  */
247 void
248 make_directory (name)
249     const char *name;
250 {
251     struct stat sb;
252 
253     if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
254 	    error (0, 0, "%s already exists but is not a directory", name);
255     if (!noexec && mkdir (name) < 0)
256 	error (1, errno, "cannot make directory %s", name);
257 }
258 
259 /*
260  * Make a path to the argument directory, printing a message if something
261  * goes wrong.
262  */
263 void
264 make_directories (name)
265     const char *name;
266 {
267     char *cp;
268 
269     if (noexec)
270 	return;
271 
272     if (mkdir (name) == 0 || errno == EEXIST)
273 	return;
274     if (errno != ENOENT)
275     {
276 	error (0, errno, "cannot make path to %s", name);
277 	return;
278     }
279     if ((cp = strrchr (name, '/')) == NULL)
280 	return;
281     *cp = '\0';
282     make_directories (name);
283     *cp++ = '/';
284     if (*cp == '\0')
285 	return;
286     (void) mkdir (name);
287 }
288 
289 /* Create directory NAME if it does not already exist; fatal error for
290    other errors.  Returns 0 if directory was created; 1 if it already
291    existed.  */
292 int
293 mkdir_if_needed (name)
294     char *name;
295 {
296     if (mkdir (name) < 0)
297     {
298 	if (errno != EEXIST
299 #ifdef EACCESS
300 	    /* This was copied over from the OS/2 code; I would guess it
301 	       isn't needed here but that has not been verified.  */
302 	    && errno != EACCESS
303 #endif
304 #ifdef EACCES
305 	    /* This is said to be needed by NT on Alpha or PowerPC
306 	       (not sure what version) --August, 1996.  */
307 	    && errno != EACCES
308 #endif
309 	    )
310 	    error (1, errno, "cannot make directory %s", name);
311 	return 1;
312     }
313     return 0;
314 }
315 
316 /*
317  * Change the mode of a file, either adding write permissions, or removing
318  * all write permissions.  Adding write permissions honors the current umask
319  * setting.
320  */
321 void
322 xchmod (fname, writable)
323     char *fname;
324     int writable;
325 {
326     struct stat sb;
327     mode_t mode, oumask;
328 
329     if (stat (fname, &sb) < 0)
330     {
331 	if (!noexec)
332 	    error (0, errno, "cannot stat %s", fname);
333 	return;
334     }
335     if (writable)
336     {
337 	oumask = umask (0);
338 	(void) umask (oumask);
339 	mode = sb.st_mode | ~oumask & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0) |
340 				       ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0) |
341 				       ((sb.st_mode & S_IROTH) ? S_IWOTH : 0));
342     }
343     else
344     {
345 	mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
346     }
347 
348     if (trace)
349 #ifdef SERVER_SUPPORT
350 	(void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
351 			(server_active) ? 'S' : ' ', fname, mode);
352 #else
353 	(void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
354 #endif
355     if (noexec)
356 	return;
357 
358     if (chmod (fname, mode) < 0)
359 	error (0, errno, "cannot change mode of file %s", fname);
360 }
361 
362 
363 /* Read the value of a symbolic link.
364    Under Windows NT, this function always returns EINVAL.  */
365 int
366 readlink (char *path, char *buf, int buf_size)
367 {
368     errno = EINVAL;
369     return -1;
370 }
371 
372 /* Rename for NT which works for read only files.  Apparently if we are
373    accessing FROM and TO via a Novell network, this is an issue.  */
374 int
375 wnt_rename (from, to)
376     const char *from;
377     const char *to;
378 {
379     int result, save_errno;
380     int readonly = !iswritable (from);
381 
382     if (readonly)
383     {
384 	if (chmod (from, S_IWRITE) < 0)
385 	    return -1;
386     }
387     result = rename (from, to);
388     save_errno = errno;
389     if (readonly)
390     {
391 	if (result == 0)
392 	{
393 	    if (chmod (to, S_IREAD) < 0)
394 		return -1;
395 	}
396 	else
397 	{
398 	    /* We have a choice of which error to report, if there is
399 	       one here too; report the one from rename ().  */
400 	    chmod (from, S_IREAD);
401 	}
402 	errno = save_errno;
403     }
404     return result;
405 }
406 
407 /*
408  * Rename a file and die if it fails
409  */
410 void
411 rename_file (from, to)
412     const char *from;
413     const char *to;
414 {
415     if (trace)
416 #ifdef SERVER_SUPPORT
417 	(void) fprintf (stderr, "%c-> rename(%s,%s)\n",
418 			(server_active) ? 'S' : ' ', from, to);
419 #else
420 	(void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
421 #endif
422     if (noexec)
423 	return;
424 
425     /* Win32 unlink is stupid --- it fails if the file is read-only  */
426     chmod(to, S_IWRITE);
427     unlink(to);
428     if (CVS_RENAME (from, to) < 0)
429 	error (1, errno, "cannot rename file %s to %s", from, to);
430 }
431 
432 /*
433  * unlink a file, if possible.
434  */
435 int
436 unlink_file (f)
437     const char *f;
438 {
439     if (trace)
440 #ifdef SERVER_SUPPORT
441 	(void) fprintf (stderr, "%c-> unlink(%s)\n",
442 			(server_active) ? 'S' : ' ', f);
443 #else
444 	(void) fprintf (stderr, "-> unlink(%s)\n", f);
445 #endif
446     if (noexec)
447 	return (0);
448 
449     /* Win32 unlink is stupid - it fails if the file is read-only */
450     chmod (f, _S_IWRITE);
451     return (unlink (f));
452 }
453 
454 /*
455  * Unlink a file or dir, if possible.  If it is a directory do a deep
456  * removal of all of the files in the directory.  Return -1 on error
457  * (in which case errno is set).
458  */
459 int
460 unlink_file_dir (f)
461     const char *f;
462 {
463     if (trace)
464 #ifdef SERVER_SUPPORT
465 	(void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
466 			(server_active) ? 'S' : ' ', f);
467 #else
468 	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
469 #endif
470     if (noexec)
471 	return (0);
472 
473     /* Win32 unlink is stupid - it fails if the file is read-only */
474     chmod (f, _S_IWRITE);
475     if (unlink (f) != 0)
476     {
477 	/* under Windows NT, unlink returns EACCES if the path
478 	   is a directory.  Under Windows 95, ENOENT.  */
479         if (errno == EISDIR || errno == EACCES || errno == ENOENT)
480                 return deep_remove_dir (f);
481         else
482 		/* The file wasn't a directory and some other
483 		 * error occured
484 		 */
485                 return -1;
486     }
487     /* We were able to remove the file from the disk */
488     return 0;
489 }
490 
491 /* Remove a directory and everything it contains.  Returns 0 for
492  * success, -1 for failure (in which case errno is set).
493  */
494 
495 static int
496 deep_remove_dir (path)
497     const char *path;
498 {
499     DIR		  *dirp;
500     struct dirent *dp;
501     char	   buf[PATH_MAX];
502 
503     /* ENOTEMPTY for NT (obvious) but EACCES for Win95 (not obvious) */
504     if (rmdir (path) != 0
505 	&& (errno == ENOTEMPTY || errno == EACCES))
506     {
507 	if ((dirp = opendir (path)) == NULL)
508 	    /* If unable to open the directory return
509 	     * an error
510 	     */
511 	    return -1;
512 
513 	while ((dp = readdir (dirp)) != NULL)
514 	{
515 	    if (strcmp (dp->d_name, ".") == 0 ||
516 			strcmp (dp->d_name, "..") == 0)
517 		continue;
518 
519 	    sprintf (buf, "%s/%s", path, dp->d_name);
520 
521 	    /* Win32 unlink is stupid - it fails if the file is read-only */
522 	    chmod (buf, _S_IWRITE);
523 	    if (unlink (buf) != 0 )
524 	    {
525 		/* Under Windows NT, unlink returns EACCES if the path
526 		   is a directory.  Under Windows 95, ENOENT.  It
527 		   isn't really clear to me whether checking errno is
528 		   better or worse than using _stat to check for a directory.
529 		   We aren't really trying to prevent race conditions here
530 		   (e.g. what if something changes between readdir and
531 		   unlink?)  */
532 		if (errno == EISDIR || errno == EACCES || errno == ENOENT)
533 		{
534 		    if (deep_remove_dir (buf))
535 		    {
536 			closedir (dirp);
537 			return -1;
538 		    }
539 		}
540 		else
541 		{
542 		    /* buf isn't a directory, or there are
543 		     * some sort of permision problems
544 		     */
545 		    closedir (dirp);
546 		    return -1;
547 		}
548 	    }
549 	}
550 	closedir (dirp);
551 	return rmdir (path);
552     }
553     /* Was able to remove the directory return 0 */
554     return 0;
555 }
556 
557 /* Read NCHARS bytes from descriptor FD into BUF.
558    Return the number of characters successfully read.
559    The number returned is always NCHARS unless end-of-file or error.  */
560 static size_t
561 block_read (fd, buf, nchars)
562     int fd;
563     char *buf;
564     size_t nchars;
565 {
566     char *bp = buf;
567     size_t nread;
568 
569     do
570     {
571 	nread = read (fd, bp, nchars);
572 	if (nread == (size_t)-1)
573 	{
574 #ifdef EINTR
575 	    if (errno == EINTR)
576 		continue;
577 #endif
578 	    return (size_t)-1;
579 	}
580 
581 	if (nread == 0)
582 	    break;
583 
584 	bp += nread;
585 	nchars -= nread;
586     } while (nchars != 0);
587 
588     return bp - buf;
589 }
590 
591 
592 /*
593  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
594  */
595 int
596 xcmp (file1, file2)
597     const char *file1;
598     const char *file2;
599 {
600     char *buf1, *buf2;
601     struct stat sb1, sb2;
602     int fd1, fd2;
603     int ret;
604 
605     if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
606 	error (1, errno, "cannot open file %s for comparing", file1);
607     if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
608 	error (1, errno, "cannot open file %s for comparing", file2);
609     if (fstat (fd1, &sb1) < 0)
610 	error (1, errno, "cannot fstat %s", file1);
611     if (fstat (fd2, &sb2) < 0)
612 	error (1, errno, "cannot fstat %s", file2);
613 
614     /* A generic file compare routine might compare st_dev & st_ino here
615        to see if the two files being compared are actually the same file.
616        But that won't happen in CVS, so we won't bother. */
617 
618     if (sb1.st_size != sb2.st_size)
619 	ret = 1;
620     else if (sb1.st_size == 0)
621 	ret = 0;
622     else
623     {
624 	/* FIXME: compute the optimal buffer size by computing the least
625 	   common multiple of the files st_blocks field */
626 	size_t buf_size = 8 * 1024;
627 	size_t read1;
628 	size_t read2;
629 
630 	buf1 = xmalloc (buf_size);
631 	buf2 = xmalloc (buf_size);
632 
633 	do
634 	{
635 	    read1 = block_read (fd1, buf1, buf_size);
636 	    if (read1 == (size_t)-1)
637 		error (1, errno, "cannot read file %s for comparing", file1);
638 
639 	    read2 = block_read (fd2, buf2, buf_size);
640 	    if (read2 == (size_t)-1)
641 		error (1, errno, "cannot read file %s for comparing", file2);
642 
643 	    /* assert (read1 == read2); */
644 
645 	    ret = memcmp(buf1, buf2, read1);
646 	} while (ret == 0 && read1 == buf_size);
647 
648 	free (buf1);
649 	free (buf2);
650     }
651 
652     (void) close (fd1);
653     (void) close (fd2);
654     return (ret);
655 }
656 
657 
658 /* Generate a unique temporary filename.  Returns a pointer to a newly
659    malloc'd string containing the name.  Returns successfully or not at
660    all.  */
661 char *
662 cvs_temp_name ()
663 {
664     char *retval;
665 
666     retval = _tempnam (NULL, NULL);
667     if (retval == NULL)
668 	error (1, errno, "cannot generate temporary filename");
669     return retval;
670 }
671 
672 /* Return non-zero iff FILENAME is absolute.
673    Trivial under Unix, but more complicated under other systems.  */
674 int
675 isabsolute (filename)
676     const char *filename;
677 {
678     /* FIXME: This routine seems to interact poorly with
679        strip_trailing_slashes.  For example, specify ":local:r:\" as
680        CVSROOT.  The CVS/Root file will contain ":local:r:" and then
681        isabsolute will complain about the root not being an absolute
682        pathname.  My guess is that strip_trailing_slashes is the right
683        place to fix this.  */
684     return (ISDIRSEP (filename[0])
685             || (filename[0] != '\0'
686                 && filename[1] == ':'
687                 && ISDIRSEP (filename[2])));
688 }
689 
690 /* Return a pointer into PATH's last component.  */
691 char *
692 last_component (char *path)
693 {
694     char *scan;
695     char *last = 0;
696 
697     for (scan = path; *scan; scan++)
698         if (ISDIRSEP (*scan))
699 	    last = scan;
700 
701     if (last && (last != path))
702         return last + 1;
703     else
704         return path;
705 }
706 
707 
708 /* NT has two evironment variables, HOMEPATH and HOMEDRIVE, which,
709    when combined as ${HOMEDRIVE}${HOMEPATH}, give the unix equivalent
710    of HOME.  Some NT users are just too unixy, though, and set the
711    HOME variable themselves.  Therefore, we check for HOME first, and
712    then try to combine the other two if that fails.
713 
714    Looking for HOME strikes me as bogus, particularly if the only reason
715    is to cater to "unixy users".  On the other hand, if the reasoning is
716    there should be a single variable, rather than requiring people to
717    set both HOMEDRIVE and HOMEPATH, then it starts to make a little more
718    sense.
719 
720    Win95: The system doesn't set HOME, HOMEDRIVE, or HOMEPATH (at
721    least if you set it up as the "all users under one user ID" or
722    whatever the name of that option is).  Based on thing overheard on
723    the net, it seems that users of the pserver client have gotten in
724    the habit of setting HOME (if you don't use pserver, you can
725    probably get away without having a reasonable return from
726    get_homedir.  Of course you lose .cvsrc and .cvsignore, but many
727    users won't notice).  So it would seem that we should be somewhat
728    careful if we try to change the current behavior.
729 
730    NT 3.51 or NT 4.0: I haven't checked this myself, but I am told
731    that HOME gets set, but not to the user's home directory.  It is
732    said to be set to c:\users\default by default.  */
733 
734 char *
735 get_homedir ()
736 {
737     static char *pathbuf;
738     char *hd, *hp;
739 
740     if (pathbuf != NULL)
741 	return pathbuf;
742     else if ((hd = getenv ("HOME")))
743 	return hd;
744     else if ((hd = getenv ("HOMEDRIVE")) && (hp = getenv ("HOMEPATH")))
745     {
746 	pathbuf = xmalloc (strlen (hd) + strlen (hp) + 5);
747 	strcpy (pathbuf, hd);
748 	strcat (pathbuf, hp);
749 
750 	return pathbuf;
751     }
752     else
753 	return NULL;
754 }
755 
756 /* See cvs.h for description.  */
757 void
758 expand_wild (argc, argv, pargc, pargv)
759     int argc;
760     char **argv;
761     int *pargc;
762     char ***pargv;
763 {
764     int i;
765     int new_argc;
766     char **new_argv;
767     /* Allocated size of new_argv.  We arrange it so there is always room for
768 	   one more element.  */
769     int max_new_argc;
770 
771     new_argc = 0;
772     /* Add one so this is never zero.  */
773     max_new_argc = argc + 1;
774     new_argv = (char **) xmalloc (max_new_argc * sizeof (char *));
775     for (i = 0; i < argc; ++i)
776     {
777 	HANDLE h;
778 	WIN32_FIND_DATA fdata;
779 
780 	/* These variables help us extract the directory name from the
781            given pathname. */
782 
783 	char *last_forw_slash, *last_back_slash, *end_of_dirname;
784 	int dirname_length = 0;
785 
786 	/* FIXME: If argv[i] is ".", this code will expand it to the
787 	   name of the current directory in its parent directory which
788 	   will cause start_recursion to do all manner of strange things
789 	   with it (culminating in an error).  This breaks "cvs co .".
790 	   As nearly as I can guess, this bug has existed since
791 	   expand_wild was first created.  At least, it is in CVS 1.9 (I
792 	   just tried it).  */
793 
794 	/* FindFirstFile doesn't return pathnames, so we have to do
795 	   this ourselves.  Luckily, it's no big deal, since globbing
796 	   characters under Win32s can only occur in the last segment
797 	   of the path.  For example,
798                 /a/path/q*.h                      valid
799 	        /w32/q*.dir/cant/do/this/q*.h     invalid */
800 
801 	/* Win32 can handle both forward and backward slashes as
802            filenames -- check for both. */
803 
804 	last_forw_slash = strrchr (argv[i], '/');
805 	last_back_slash = strrchr (argv[i], '\\');
806 
807 #define cvs_max(x,y) ((x >= y) ? (x) : (y))
808 
809 	/* FIXME: this comparing a NULL pointer to a non-NULL one is
810 	   extremely ugly, and I strongly suspect *NOT* sanctioned by
811 	   ANSI C.  The code should just use last_component instead.  */
812 	end_of_dirname = cvs_max (last_forw_slash, last_back_slash);
813 
814 	if (end_of_dirname == NULL)
815 	  dirname_length = 0;	/* no directory name */
816 	else
817 	  dirname_length = end_of_dirname - argv[i] + 1; /* include slash */
818 
819 	h = FindFirstFile (argv[i], &fdata);
820 	if (h == INVALID_HANDLE_VALUE)
821 	{
822 	    if (GetLastError () == ENOENT)
823 	    {
824 		/* No match.  The file specified didn't contain a wildcard (in which case
825 		   we clearly should return it unchanged), or it contained a wildcard which
826 		   didn't match (in which case it might be better for it to be an error,
827 		   but we don't try to do that).  */
828 		new_argv [new_argc++] = xstrdup (argv[i]);
829 		if (new_argc == max_new_argc)
830 		{
831 		    max_new_argc *= 2;
832 		    new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
833 		}
834 	    }
835 	    else
836 	    {
837 		error (1, errno, "cannot find %s", argv[i]);
838 	    }
839 	}
840 	else
841 	{
842 	    while (1)
843 	    {
844 		new_argv[new_argc] =
845 		    (char *) xmalloc (strlen (fdata.cFileName) + 1
846 				      + dirname_length);
847 
848 		/* Copy the directory name, if there is one. */
849 
850 		if (dirname_length)
851 		{
852 		    strncpy (new_argv[new_argc], argv[i], dirname_length);
853 		    new_argv[new_argc][dirname_length] = '\0';
854 		}
855 		else
856 		    new_argv[new_argc][0] = '\0';
857 
858 		/* Copy the file name. */
859 
860 		if (fncmp (argv[i] + dirname_length, fdata.cFileName) == 0)
861 		    /* We didn't expand a wildcard; we just matched a filename.
862 		       Use the file name as specified rather than the filename
863 		       which exists in the directory (they may differ in case).
864 		       This is needed to make cvs add on a directory consistently
865 		       use the name specified on the command line, but it is
866 		       probably a good idea in other contexts too.  */
867 		    strcpy (new_argv[new_argc], argv[i]);
868 		else
869 		    strcat (new_argv[new_argc], fdata.cFileName);
870 
871 		new_argc++;
872 
873 		if (new_argc == max_new_argc)
874 		{
875 		    max_new_argc *= 2;
876 		    new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
877 		}
878 		if (!FindNextFile (h, &fdata))
879 		{
880 		    if (GetLastError () == ERROR_NO_MORE_FILES)
881 			break;
882 		    else
883 			error (1, errno, "cannot find %s", argv[i]);
884 		}
885 	    }
886 	    if (!FindClose (h))
887 		error (1, GetLastError (), "cannot close %s", argv[i]);
888 	}
889     }
890     *pargc = new_argc;
891     *pargv = new_argv;
892 }
893 
894 static void check_statbuf (const char *file, struct stat *sb)
895 {
896     /* Win32 processes file times in a 64 bit format
897        (see Win32 functions SetFileTime and GetFileTime).
898        If the file time on a file doesn't fit into the
899        32 bit time_t format, then stat will set that time
900        to -1.  This would be OK, except that functions
901        like ctime() don't check for validity.  So what we
902        do here is to give a error on -1.  A cleaner solution
903        might be to change CVS's interfaces to return a time
904        in RCS format (for example), and then implement it
905        on Win32 via GetFileTime, but that would be a lot of
906        hair and I'm not sure there is much payoff.  */
907     if (sb->st_mtime == (time_t) -1)
908 	error (1, 0, "invalid modification time for %s", file);
909     if (sb->st_ctime == (time_t) -1)
910 	/* I'm not sure what this means on windows.  It
911 	   might be a creation time (unlike unix)....  */
912 	error (1, 0, "invalid ctime for %s", file);
913     if (sb->st_atime == (time_t) -1)
914 	error (1, 0, "invalid access time for %s", file);
915 }
916 
917 int
918 wnt_stat (const char *file, struct stat *sb)
919 {
920     int retval;
921 
922     retval = stat (file, sb);
923     if (retval < 0)
924 	return retval;
925     check_statbuf (file, sb);
926     return retval;
927 }
928 
929 int
930 wnt_lstat (const char *file, struct stat *sb)
931 {
932     int retval;
933 
934     retval = lstat (file, sb);
935     if (retval < 0)
936 	return retval;
937     check_statbuf (file, sb);
938     return retval;
939 }
940