xref: /openbsd-src/gnu/usr.bin/cvs/windows-NT/filesubr.c (revision c71bc7e269286e43816004eb0fcd7a55f036cd69)
11e72d8d2Sderaadt /* filesubr.c --- subroutines for dealing with files
21e72d8d2Sderaadt    Jim Blandy <jimb@cyclic.com>
31e72d8d2Sderaadt 
41e72d8d2Sderaadt    This file is part of GNU CVS.
51e72d8d2Sderaadt 
61e72d8d2Sderaadt    GNU CVS is free software; you can redistribute it and/or modify it
71e72d8d2Sderaadt    under the terms of the GNU General Public License as published by the
81e72d8d2Sderaadt    Free Software Foundation; either version 2, or (at your option) any
91e72d8d2Sderaadt    later version.
101e72d8d2Sderaadt 
111e72d8d2Sderaadt    This program is distributed in the hope that it will be useful,
121e72d8d2Sderaadt    but WITHOUT ANY WARRANTY; without even the implied warranty of
131e72d8d2Sderaadt    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14461cc63eStholo    GNU General Public License for more details.  */
151e72d8d2Sderaadt 
161e72d8d2Sderaadt /* These functions were moved out of subr.c because they need different
171e72d8d2Sderaadt    definitions under operating systems (like, say, Windows NT) with different
181e72d8d2Sderaadt    file system semantics.  */
191e72d8d2Sderaadt 
201e72d8d2Sderaadt #include <io.h>
21c2c61682Stholo #include <windows.h>
221e72d8d2Sderaadt 
231e72d8d2Sderaadt #include "cvs.h"
241e72d8d2Sderaadt 
251e72d8d2Sderaadt static int deep_remove_dir PROTO((const char *path));
261e72d8d2Sderaadt 
27b6c02222Stholo /* Copies "from" to "to".  Note that the functionality here is similar
28b6c02222Stholo    to the win32 function CopyFile, but (1) we copy LastAccessTime and
29b6c02222Stholo    CopyFile doesn't, (2) we set file attributes to the default set by
30b6c02222Stholo    the C library and CopyFile copies them.  Neither #1 nor #2 was intentional
31b6c02222Stholo    as far as I know, but changing them could be confusing, unless there
32b6c02222Stholo    is some reason they should be changed (this would need more
33b6c02222Stholo    investigation).  */
341e72d8d2Sderaadt void
351e72d8d2Sderaadt copy_file (from, to)
361e72d8d2Sderaadt     const char *from;
371e72d8d2Sderaadt     const char *to;
381e72d8d2Sderaadt {
391e72d8d2Sderaadt     struct stat sb;
401e72d8d2Sderaadt     struct utimbuf t;
411e72d8d2Sderaadt     int fdin, fdout;
421e72d8d2Sderaadt 
431e72d8d2Sderaadt     if (trace)
441e72d8d2Sderaadt #ifdef SERVER_SUPPORT
451e72d8d2Sderaadt 	(void) fprintf (stderr, "%c-> copy(%s,%s)\n",
461e72d8d2Sderaadt 			(server_active) ? 'S' : ' ', from, to);
471e72d8d2Sderaadt #else
481e72d8d2Sderaadt 	(void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
491e72d8d2Sderaadt #endif
501e72d8d2Sderaadt     if (noexec)
511e72d8d2Sderaadt 	return;
521e72d8d2Sderaadt 
531e72d8d2Sderaadt     if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
541e72d8d2Sderaadt 	error (1, errno, "cannot open %s for copying", from);
551e72d8d2Sderaadt     if (fstat (fdin, &sb) < 0)
561e72d8d2Sderaadt 	error (1, errno, "cannot fstat %s", from);
5750bf276cStholo     if ((fdout = open (to, O_CREAT | O_TRUNC | O_RDWR | O_BINARY,
5850bf276cStholo 		       (int) sb.st_mode & 07777)) < 0)
591e72d8d2Sderaadt 	error (1, errno, "cannot create %s for copying", to);
601e72d8d2Sderaadt     if (sb.st_size > 0)
611e72d8d2Sderaadt     {
621e72d8d2Sderaadt 	char buf[BUFSIZ];
631e72d8d2Sderaadt 	int n;
641e72d8d2Sderaadt 
651e72d8d2Sderaadt 	for (;;)
661e72d8d2Sderaadt 	{
671e72d8d2Sderaadt 	    n = read (fdin, buf, sizeof(buf));
681e72d8d2Sderaadt 	    if (n == -1)
691e72d8d2Sderaadt 	    {
701e72d8d2Sderaadt #ifdef EINTR
711e72d8d2Sderaadt 		if (errno == EINTR)
721e72d8d2Sderaadt 		    continue;
731e72d8d2Sderaadt #endif
741e72d8d2Sderaadt 		error (1, errno, "cannot read file %s for copying", from);
751e72d8d2Sderaadt 	    }
761e72d8d2Sderaadt             else if (n == 0)
771e72d8d2Sderaadt 		break;
781e72d8d2Sderaadt 
791e72d8d2Sderaadt 	    if (write(fdout, buf, n) != n) {
801e72d8d2Sderaadt 		error (1, errno, "cannot write file %s for copying", to);
811e72d8d2Sderaadt 	    }
821e72d8d2Sderaadt 	}
831e72d8d2Sderaadt 
841e72d8d2Sderaadt #ifdef HAVE_FSYNC
851e72d8d2Sderaadt 	if (fsync (fdout))
861e72d8d2Sderaadt 	    error (1, errno, "cannot fsync file %s after copying", to);
871e72d8d2Sderaadt #endif
881e72d8d2Sderaadt     }
891e72d8d2Sderaadt 
901e72d8d2Sderaadt     if (close (fdin) < 0)
911e72d8d2Sderaadt 	error (0, errno, "cannot close %s", from);
921e72d8d2Sderaadt     if (close (fdout) < 0)
931e72d8d2Sderaadt 	error (1, errno, "cannot close %s", to);
941e72d8d2Sderaadt 
951e72d8d2Sderaadt     /* now, set the times for the copied file to match those of the original */
961e72d8d2Sderaadt     memset ((char *) &t, 0, sizeof (t));
971e72d8d2Sderaadt     t.actime = sb.st_atime;
981e72d8d2Sderaadt     t.modtime = sb.st_mtime;
991e72d8d2Sderaadt     (void) utime (to, &t);
1001e72d8d2Sderaadt }
1011e72d8d2Sderaadt 
1021e72d8d2Sderaadt /* FIXME-krp: these functions would benefit from caching the char * &
1031e72d8d2Sderaadt    stat buf.  */
1041e72d8d2Sderaadt 
1051e72d8d2Sderaadt /*
1061e72d8d2Sderaadt  * Returns non-zero if the argument file is a directory, or is a symbolic
1071e72d8d2Sderaadt  * link which points to a directory.
1081e72d8d2Sderaadt  */
1091e72d8d2Sderaadt int
1101e72d8d2Sderaadt isdir (file)
1111e72d8d2Sderaadt     const char *file;
1121e72d8d2Sderaadt {
1131e72d8d2Sderaadt     struct stat sb;
1141e72d8d2Sderaadt 
1151e72d8d2Sderaadt     if (stat (file, &sb) < 0)
1161e72d8d2Sderaadt 	return (0);
1171e72d8d2Sderaadt     return (S_ISDIR (sb.st_mode));
1181e72d8d2Sderaadt }
1191e72d8d2Sderaadt 
1201e72d8d2Sderaadt /*
1211e72d8d2Sderaadt  * Returns non-zero if the argument file is a symbolic link.
1221e72d8d2Sderaadt  */
1231e72d8d2Sderaadt int
1241e72d8d2Sderaadt islink (file)
1251e72d8d2Sderaadt     const char *file;
1261e72d8d2Sderaadt {
1271e72d8d2Sderaadt #ifdef S_ISLNK
1281e72d8d2Sderaadt     struct stat sb;
1291e72d8d2Sderaadt 
1301e72d8d2Sderaadt     if (lstat (file, &sb) < 0)
1311e72d8d2Sderaadt 	return (0);
1321e72d8d2Sderaadt     return (S_ISLNK (sb.st_mode));
1331e72d8d2Sderaadt #else
1341e72d8d2Sderaadt     return (0);
1351e72d8d2Sderaadt #endif
1361e72d8d2Sderaadt }
1371e72d8d2Sderaadt 
1381e72d8d2Sderaadt /*
1391e72d8d2Sderaadt  * Returns non-zero if the argument file exists.
1401e72d8d2Sderaadt  */
1411e72d8d2Sderaadt int
1421e72d8d2Sderaadt isfile (file)
1431e72d8d2Sderaadt     const char *file;
1441e72d8d2Sderaadt {
14513571821Stholo     return isaccessible(file, F_OK);
1461e72d8d2Sderaadt }
1471e72d8d2Sderaadt 
1481e72d8d2Sderaadt /*
1491e72d8d2Sderaadt  * Returns non-zero if the argument file is readable.
1501e72d8d2Sderaadt  */
1511e72d8d2Sderaadt int
1521e72d8d2Sderaadt isreadable (file)
1531e72d8d2Sderaadt     const char *file;
1541e72d8d2Sderaadt {
15513571821Stholo     return isaccessible(file, R_OK);
1561e72d8d2Sderaadt }
1571e72d8d2Sderaadt 
1581e72d8d2Sderaadt /*
15913571821Stholo  * Returns non-zero if the argument file is writable.
1601e72d8d2Sderaadt  */
1611e72d8d2Sderaadt int
1621e72d8d2Sderaadt iswritable (file)
1631e72d8d2Sderaadt     const char *file;
1641e72d8d2Sderaadt {
16513571821Stholo     return isaccessible(file, W_OK);
16613571821Stholo }
16713571821Stholo 
16813571821Stholo /*
16913571821Stholo  * Returns non-zero if the argument file is accessable according to
17013571821Stholo  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
17113571821Stholo  * bits set.
17213571821Stholo  */
17313571821Stholo int
17413571821Stholo isaccessible (file, mode)
17513571821Stholo     const char *file;
17613571821Stholo     const int mode;
17713571821Stholo {
17813571821Stholo #ifdef SETXID_SUPPORT
17913571821Stholo     struct stat sb;
18013571821Stholo     int umask = 0;
18113571821Stholo     int gmask = 0;
18213571821Stholo     int omask = 0;
18313571821Stholo     int uid;
18413571821Stholo 
18513571821Stholo     if (stat(file, &sb) == -1)
18613571821Stholo 	return 0;
18713571821Stholo     if (mode == F_OK)
18813571821Stholo 	return 1;
18913571821Stholo 
19013571821Stholo     uid = geteuid();
19113571821Stholo     if (uid == 0)		/* superuser */
19213571821Stholo     {
19313571821Stholo 	if (mode & X_OK)
19413571821Stholo 	    return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
19513571821Stholo 	else
19613571821Stholo 	    return 1;
19713571821Stholo     }
19813571821Stholo 
19913571821Stholo     if (mode & R_OK)
20013571821Stholo     {
20113571821Stholo 	umask |= S_IRUSR;
20213571821Stholo 	gmask |= S_IRGRP;
20313571821Stholo 	omask |= S_IROTH;
20413571821Stholo     }
20513571821Stholo     if (mode & W_OK)
20613571821Stholo     {
20713571821Stholo 	umask |= S_IWUSR;
20813571821Stholo 	gmask |= S_IWGRP;
20913571821Stholo 	omask |= S_IWOTH;
21013571821Stholo     }
21113571821Stholo     if (mode & X_OK)
21213571821Stholo     {
21313571821Stholo 	umask |= S_IXUSR;
21413571821Stholo 	gmask |= S_IXGRP;
21513571821Stholo 	omask |= S_IXOTH;
21613571821Stholo     }
21713571821Stholo 
21813571821Stholo     if (sb.st_uid == uid)
21913571821Stholo 	return (sb.st_mode & umask) == umask;
22013571821Stholo     else if (sb.st_gid == getegid())
22113571821Stholo 	return (sb.st_mode & gmask) == gmask;
22213571821Stholo     else
22313571821Stholo 	return (sb.st_mode & omask) == omask;
22413571821Stholo #else
22513571821Stholo     return access(file, mode) == 0;
22613571821Stholo #endif
2271e72d8d2Sderaadt }
2281e72d8d2Sderaadt 
2291e72d8d2Sderaadt /*
2301e72d8d2Sderaadt  * Open a file and die if it fails
2311e72d8d2Sderaadt  */
2321e72d8d2Sderaadt FILE *
2331e72d8d2Sderaadt open_file (name, mode)
2341e72d8d2Sderaadt     const char *name;
2351e72d8d2Sderaadt     const char *mode;
2361e72d8d2Sderaadt {
2371e72d8d2Sderaadt     FILE *fp;
2381e72d8d2Sderaadt 
2391e72d8d2Sderaadt     if ((fp = fopen (name, mode)) == NULL)
2401e72d8d2Sderaadt 	error (1, errno, "cannot open %s", name);
2411e72d8d2Sderaadt     return (fp);
2421e72d8d2Sderaadt }
2431e72d8d2Sderaadt 
2441e72d8d2Sderaadt /*
2451e72d8d2Sderaadt  * Make a directory and die if it fails
2461e72d8d2Sderaadt  */
2471e72d8d2Sderaadt void
2481e72d8d2Sderaadt make_directory (name)
2491e72d8d2Sderaadt     const char *name;
2501e72d8d2Sderaadt {
25113571821Stholo     struct stat sb;
2521e72d8d2Sderaadt 
25313571821Stholo     if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
2541e72d8d2Sderaadt 	    error (0, 0, "%s already exists but is not a directory", name);
2551e72d8d2Sderaadt     if (!noexec && mkdir (name) < 0)
2561e72d8d2Sderaadt 	error (1, errno, "cannot make directory %s", name);
2571e72d8d2Sderaadt }
2581e72d8d2Sderaadt 
2591e72d8d2Sderaadt /*
2601e72d8d2Sderaadt  * Make a path to the argument directory, printing a message if something
2611e72d8d2Sderaadt  * goes wrong.
2621e72d8d2Sderaadt  */
2631e72d8d2Sderaadt void
2641e72d8d2Sderaadt make_directories (name)
2651e72d8d2Sderaadt     const char *name;
2661e72d8d2Sderaadt {
2671e72d8d2Sderaadt     char *cp;
2681e72d8d2Sderaadt 
2691e72d8d2Sderaadt     if (noexec)
2701e72d8d2Sderaadt 	return;
2711e72d8d2Sderaadt 
2721e72d8d2Sderaadt     if (mkdir (name) == 0 || errno == EEXIST)
2731e72d8d2Sderaadt 	return;
2741e72d8d2Sderaadt     if (errno != ENOENT)
2751e72d8d2Sderaadt     {
2761e72d8d2Sderaadt 	error (0, errno, "cannot make path to %s", name);
2771e72d8d2Sderaadt 	return;
2781e72d8d2Sderaadt     }
2791e72d8d2Sderaadt     if ((cp = strrchr (name, '/')) == NULL)
2801e72d8d2Sderaadt 	return;
2811e72d8d2Sderaadt     *cp = '\0';
2821e72d8d2Sderaadt     make_directories (name);
2831e72d8d2Sderaadt     *cp++ = '/';
2841e72d8d2Sderaadt     if (*cp == '\0')
2851e72d8d2Sderaadt 	return;
2861e72d8d2Sderaadt     (void) mkdir (name);
2871e72d8d2Sderaadt }
2881e72d8d2Sderaadt 
28950bf276cStholo /* Create directory NAME if it does not already exist; fatal error for
29050bf276cStholo    other errors.  Returns 0 if directory was created; 1 if it already
29150bf276cStholo    existed.  */
29250bf276cStholo int
29350bf276cStholo mkdir_if_needed (name)
29450bf276cStholo     char *name;
29550bf276cStholo {
29650bf276cStholo     if (mkdir (name) < 0)
29750bf276cStholo     {
29850bf276cStholo 	if (errno != EEXIST
29950bf276cStholo #ifdef EACCESS
30050bf276cStholo 	    /* This was copied over from the OS/2 code; I would guess it
30150bf276cStholo 	       isn't needed here but that has not been verified.  */
30250bf276cStholo 	    && errno != EACCESS
30350bf276cStholo #endif
30450bf276cStholo #ifdef EACCES
30550bf276cStholo 	    /* This is said to be needed by NT on Alpha or PowerPC
30650bf276cStholo 	       (not sure what version) --August, 1996.  */
30750bf276cStholo 	    && errno != EACCES
30850bf276cStholo #endif
30950bf276cStholo 	    )
31050bf276cStholo 	    error (1, errno, "cannot make directory %s", name);
31150bf276cStholo 	return 1;
31250bf276cStholo     }
31350bf276cStholo     return 0;
31450bf276cStholo }
31550bf276cStholo 
3161e72d8d2Sderaadt /*
3171e72d8d2Sderaadt  * Change the mode of a file, either adding write permissions, or removing
3181e72d8d2Sderaadt  * all write permissions.  Adding write permissions honors the current umask
3191e72d8d2Sderaadt  * setting.
3201e72d8d2Sderaadt  */
3211e72d8d2Sderaadt void
3221e72d8d2Sderaadt xchmod (fname, writable)
3231e72d8d2Sderaadt     char *fname;
3241e72d8d2Sderaadt     int writable;
3251e72d8d2Sderaadt {
3261e72d8d2Sderaadt     struct stat sb;
3271e72d8d2Sderaadt     mode_t mode, oumask;
3281e72d8d2Sderaadt 
3291e72d8d2Sderaadt     if (stat (fname, &sb) < 0)
3301e72d8d2Sderaadt     {
3311e72d8d2Sderaadt 	if (!noexec)
3321e72d8d2Sderaadt 	    error (0, errno, "cannot stat %s", fname);
3331e72d8d2Sderaadt 	return;
3341e72d8d2Sderaadt     }
3351e72d8d2Sderaadt     if (writable)
3361e72d8d2Sderaadt     {
3371e72d8d2Sderaadt 	oumask = umask (0);
3381e72d8d2Sderaadt 	(void) umask (oumask);
3391e72d8d2Sderaadt 	mode = sb.st_mode | ~oumask & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0) |
3401e72d8d2Sderaadt 				       ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0) |
3411e72d8d2Sderaadt 				       ((sb.st_mode & S_IROTH) ? S_IWOTH : 0));
3421e72d8d2Sderaadt     }
3431e72d8d2Sderaadt     else
3441e72d8d2Sderaadt     {
3451e72d8d2Sderaadt 	mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
3461e72d8d2Sderaadt     }
3471e72d8d2Sderaadt 
3481e72d8d2Sderaadt     if (trace)
3491e72d8d2Sderaadt #ifdef SERVER_SUPPORT
3501e72d8d2Sderaadt 	(void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
3511e72d8d2Sderaadt 			(server_active) ? 'S' : ' ', fname, mode);
3521e72d8d2Sderaadt #else
3531e72d8d2Sderaadt 	(void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
3541e72d8d2Sderaadt #endif
3551e72d8d2Sderaadt     if (noexec)
3561e72d8d2Sderaadt 	return;
3571e72d8d2Sderaadt 
3581e72d8d2Sderaadt     if (chmod (fname, mode) < 0)
3591e72d8d2Sderaadt 	error (0, errno, "cannot change mode of file %s", fname);
3601e72d8d2Sderaadt }
3611e72d8d2Sderaadt 
3621e72d8d2Sderaadt 
3631e72d8d2Sderaadt /* Read the value of a symbolic link.
3641e72d8d2Sderaadt    Under Windows NT, this function always returns EINVAL.  */
3651e72d8d2Sderaadt int
3661e72d8d2Sderaadt readlink (char *path, char *buf, int buf_size)
3671e72d8d2Sderaadt {
3681e72d8d2Sderaadt     errno = EINVAL;
3691e72d8d2Sderaadt     return -1;
3701e72d8d2Sderaadt }
3711e72d8d2Sderaadt 
372*c71bc7e2Stholo /* Rename for NT which works for read only files.  Apparently if we are
373*c71bc7e2Stholo    accessing FROM and TO via a Novell network, this is an issue.  */
374*c71bc7e2Stholo int
375*c71bc7e2Stholo wnt_rename (from, to)
376*c71bc7e2Stholo     const char *from;
377*c71bc7e2Stholo     const char *to;
378*c71bc7e2Stholo {
379*c71bc7e2Stholo     int result, save_errno;
380*c71bc7e2Stholo     int readonly = !iswritable (from);
381*c71bc7e2Stholo 
382*c71bc7e2Stholo     if (readonly)
383*c71bc7e2Stholo     {
384*c71bc7e2Stholo 	if (chmod (from, S_IWRITE) < 0)
385*c71bc7e2Stholo 	    return -1;
386*c71bc7e2Stholo     }
387*c71bc7e2Stholo     result = rename (from, to);
388*c71bc7e2Stholo     save_errno = errno;
389*c71bc7e2Stholo     if (readonly)
390*c71bc7e2Stholo     {
391*c71bc7e2Stholo 	if (result == 0)
392*c71bc7e2Stholo 	{
393*c71bc7e2Stholo 	    if (chmod (to, S_IREAD) < 0)
394*c71bc7e2Stholo 		return -1;
395*c71bc7e2Stholo 	}
396*c71bc7e2Stholo 	else
397*c71bc7e2Stholo 	{
398*c71bc7e2Stholo 	    /* We have a choice of which error to report, if there is
399*c71bc7e2Stholo 	       one here too; report the one from rename ().  */
400*c71bc7e2Stholo 	    chmod (from, S_IREAD);
401*c71bc7e2Stholo 	}
402*c71bc7e2Stholo 	errno = save_errno;
403*c71bc7e2Stholo     }
404*c71bc7e2Stholo     return result;
405*c71bc7e2Stholo }
406*c71bc7e2Stholo 
4071e72d8d2Sderaadt /*
4081e72d8d2Sderaadt  * Rename a file and die if it fails
4091e72d8d2Sderaadt  */
4101e72d8d2Sderaadt void
4111e72d8d2Sderaadt rename_file (from, to)
4121e72d8d2Sderaadt     const char *from;
4131e72d8d2Sderaadt     const char *to;
4141e72d8d2Sderaadt {
4151e72d8d2Sderaadt     if (trace)
4161e72d8d2Sderaadt #ifdef SERVER_SUPPORT
4171e72d8d2Sderaadt 	(void) fprintf (stderr, "%c-> rename(%s,%s)\n",
4181e72d8d2Sderaadt 			(server_active) ? 'S' : ' ', from, to);
4191e72d8d2Sderaadt #else
4201e72d8d2Sderaadt 	(void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
4211e72d8d2Sderaadt #endif
4221e72d8d2Sderaadt     if (noexec)
4231e72d8d2Sderaadt 	return;
4241e72d8d2Sderaadt 
4251e72d8d2Sderaadt     /* Win32 unlink is stupid --- it fails if the file is read-only  */
4261e72d8d2Sderaadt     chmod(to, S_IWRITE);
4271e72d8d2Sderaadt     unlink(to);
428*c71bc7e2Stholo     if (CVS_RENAME (from, to) < 0)
4291e72d8d2Sderaadt 	error (1, errno, "cannot rename file %s to %s", from, to);
4301e72d8d2Sderaadt }
4311e72d8d2Sderaadt 
4321e72d8d2Sderaadt /*
4331e72d8d2Sderaadt  * unlink a file, if possible.
4341e72d8d2Sderaadt  */
4351e72d8d2Sderaadt int
4361e72d8d2Sderaadt unlink_file (f)
4371e72d8d2Sderaadt     const char *f;
4381e72d8d2Sderaadt {
4391e72d8d2Sderaadt     if (trace)
4401e72d8d2Sderaadt #ifdef SERVER_SUPPORT
4411e72d8d2Sderaadt 	(void) fprintf (stderr, "%c-> unlink(%s)\n",
4421e72d8d2Sderaadt 			(server_active) ? 'S' : ' ', f);
4431e72d8d2Sderaadt #else
4441e72d8d2Sderaadt 	(void) fprintf (stderr, "-> unlink(%s)\n", f);
4451e72d8d2Sderaadt #endif
4461e72d8d2Sderaadt     if (noexec)
4471e72d8d2Sderaadt 	return (0);
4481e72d8d2Sderaadt 
4491e72d8d2Sderaadt     /* Win32 unlink is stupid - it fails if the file is read-only */
4501e72d8d2Sderaadt     chmod (f, _S_IWRITE);
4511e72d8d2Sderaadt     return (unlink (f));
4521e72d8d2Sderaadt }
4531e72d8d2Sderaadt 
4541e72d8d2Sderaadt /*
4551e72d8d2Sderaadt  * Unlink a file or dir, if possible.  If it is a directory do a deep
4561e72d8d2Sderaadt  * removal of all of the files in the directory.  Return -1 on error
4571e72d8d2Sderaadt  * (in which case errno is set).
4581e72d8d2Sderaadt  */
4591e72d8d2Sderaadt int
4601e72d8d2Sderaadt unlink_file_dir (f)
4611e72d8d2Sderaadt     const char *f;
4621e72d8d2Sderaadt {
4631e72d8d2Sderaadt     if (trace)
4641e72d8d2Sderaadt #ifdef SERVER_SUPPORT
4651e72d8d2Sderaadt 	(void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
4661e72d8d2Sderaadt 			(server_active) ? 'S' : ' ', f);
4671e72d8d2Sderaadt #else
4681e72d8d2Sderaadt 	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
4691e72d8d2Sderaadt #endif
4701e72d8d2Sderaadt     if (noexec)
4711e72d8d2Sderaadt 	return (0);
4721e72d8d2Sderaadt 
4731e72d8d2Sderaadt     /* Win32 unlink is stupid - it fails if the file is read-only */
4741e72d8d2Sderaadt     chmod (f, _S_IWRITE);
4751e72d8d2Sderaadt     if (unlink (f) != 0)
4761e72d8d2Sderaadt     {
4771e72d8d2Sderaadt 	/* under Windows NT, unlink returns EACCES if the path
47850bf276cStholo 	   is a directory.  Under Windows 95, ENOENT.  */
47950bf276cStholo         if (errno == EISDIR || errno == EACCES || errno == ENOENT)
4801e72d8d2Sderaadt                 return deep_remove_dir (f);
4811e72d8d2Sderaadt         else
4821e72d8d2Sderaadt 		/* The file wasn't a directory and some other
4831e72d8d2Sderaadt 		 * error occured
4841e72d8d2Sderaadt 		 */
4851e72d8d2Sderaadt                 return -1;
4861e72d8d2Sderaadt     }
4871e72d8d2Sderaadt     /* We were able to remove the file from the disk */
4881e72d8d2Sderaadt     return 0;
4891e72d8d2Sderaadt }
4901e72d8d2Sderaadt 
4911e72d8d2Sderaadt /* Remove a directory and everything it contains.  Returns 0 for
4921e72d8d2Sderaadt  * success, -1 for failure (in which case errno is set).
4931e72d8d2Sderaadt  */
4941e72d8d2Sderaadt 
4951e72d8d2Sderaadt static int
4961e72d8d2Sderaadt deep_remove_dir (path)
4971e72d8d2Sderaadt     const char *path;
4981e72d8d2Sderaadt {
4991e72d8d2Sderaadt     DIR		  *dirp;
5001e72d8d2Sderaadt     struct dirent *dp;
5011e72d8d2Sderaadt     char	   buf[PATH_MAX];
5021e72d8d2Sderaadt 
50350bf276cStholo     /* ENOTEMPTY for NT (obvious) but EACCES for Win95 (not obvious) */
50450bf276cStholo     if (rmdir (path) != 0
50550bf276cStholo 	&& (errno == ENOTEMPTY || errno == EACCES))
5061e72d8d2Sderaadt     {
5071e72d8d2Sderaadt 	if ((dirp = opendir (path)) == NULL)
5081e72d8d2Sderaadt 	    /* If unable to open the directory return
5091e72d8d2Sderaadt 	     * an error
5101e72d8d2Sderaadt 	     */
5111e72d8d2Sderaadt 	    return -1;
5121e72d8d2Sderaadt 
5131e72d8d2Sderaadt 	while ((dp = readdir (dirp)) != NULL)
5141e72d8d2Sderaadt 	{
5151e72d8d2Sderaadt 	    if (strcmp (dp->d_name, ".") == 0 ||
5161e72d8d2Sderaadt 			strcmp (dp->d_name, "..") == 0)
5171e72d8d2Sderaadt 		continue;
5181e72d8d2Sderaadt 
5191e72d8d2Sderaadt 	    sprintf (buf, "%s/%s", path, dp->d_name);
5201e72d8d2Sderaadt 
5211e72d8d2Sderaadt 	    /* Win32 unlink is stupid - it fails if the file is read-only */
5221e72d8d2Sderaadt 	    chmod (buf, _S_IWRITE);
5231e72d8d2Sderaadt 	    if (unlink (buf) != 0 )
5241e72d8d2Sderaadt 	    {
52550bf276cStholo 		/* Under Windows NT, unlink returns EACCES if the path
52650bf276cStholo 		   is a directory.  Under Windows 95, ENOENT.  It
52750bf276cStholo 		   isn't really clear to me whether checking errno is
52850bf276cStholo 		   better or worse than using _stat to check for a directory.
52950bf276cStholo 		   We aren't really trying to prevent race conditions here
53050bf276cStholo 		   (e.g. what if something changes between readdir and
53150bf276cStholo 		   unlink?)  */
53250bf276cStholo 		if (errno == EISDIR || errno == EACCES || errno == ENOENT)
5331e72d8d2Sderaadt 		{
5341e72d8d2Sderaadt 		    if (deep_remove_dir (buf))
5351e72d8d2Sderaadt 		    {
5361e72d8d2Sderaadt 			closedir (dirp);
5371e72d8d2Sderaadt 			return -1;
5381e72d8d2Sderaadt 		    }
5391e72d8d2Sderaadt 		}
5401e72d8d2Sderaadt 		else
5411e72d8d2Sderaadt 		{
5421e72d8d2Sderaadt 		    /* buf isn't a directory, or there are
5431e72d8d2Sderaadt 		     * some sort of permision problems
5441e72d8d2Sderaadt 		     */
5451e72d8d2Sderaadt 		    closedir (dirp);
5461e72d8d2Sderaadt 		    return -1;
5471e72d8d2Sderaadt 		}
5481e72d8d2Sderaadt 	    }
5491e72d8d2Sderaadt 	}
5501e72d8d2Sderaadt 	closedir (dirp);
5511e72d8d2Sderaadt 	return rmdir (path);
5521e72d8d2Sderaadt     }
5531e72d8d2Sderaadt     /* Was able to remove the directory return 0 */
5541e72d8d2Sderaadt     return 0;
5551e72d8d2Sderaadt }
5561e72d8d2Sderaadt 
5571e72d8d2Sderaadt /* Read NCHARS bytes from descriptor FD into BUF.
5581e72d8d2Sderaadt    Return the number of characters successfully read.
5591e72d8d2Sderaadt    The number returned is always NCHARS unless end-of-file or error.  */
5601e72d8d2Sderaadt static size_t
5611e72d8d2Sderaadt block_read (fd, buf, nchars)
5621e72d8d2Sderaadt     int fd;
5631e72d8d2Sderaadt     char *buf;
5641e72d8d2Sderaadt     size_t nchars;
5651e72d8d2Sderaadt {
5661e72d8d2Sderaadt     char *bp = buf;
5671e72d8d2Sderaadt     size_t nread;
5681e72d8d2Sderaadt 
5691e72d8d2Sderaadt     do
5701e72d8d2Sderaadt     {
5711e72d8d2Sderaadt 	nread = read (fd, bp, nchars);
5721e72d8d2Sderaadt 	if (nread == (size_t)-1)
5731e72d8d2Sderaadt 	{
5741e72d8d2Sderaadt #ifdef EINTR
5751e72d8d2Sderaadt 	    if (errno == EINTR)
5761e72d8d2Sderaadt 		continue;
5771e72d8d2Sderaadt #endif
5781e72d8d2Sderaadt 	    return (size_t)-1;
5791e72d8d2Sderaadt 	}
5801e72d8d2Sderaadt 
5811e72d8d2Sderaadt 	if (nread == 0)
5821e72d8d2Sderaadt 	    break;
5831e72d8d2Sderaadt 
5841e72d8d2Sderaadt 	bp += nread;
5851e72d8d2Sderaadt 	nchars -= nread;
5861e72d8d2Sderaadt     } while (nchars != 0);
5871e72d8d2Sderaadt 
5881e72d8d2Sderaadt     return bp - buf;
5891e72d8d2Sderaadt }
5901e72d8d2Sderaadt 
5911e72d8d2Sderaadt 
5921e72d8d2Sderaadt /*
5931e72d8d2Sderaadt  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
5941e72d8d2Sderaadt  */
5951e72d8d2Sderaadt int
5961e72d8d2Sderaadt xcmp (file1, file2)
5971e72d8d2Sderaadt     const char *file1;
5981e72d8d2Sderaadt     const char *file2;
5991e72d8d2Sderaadt {
6001e72d8d2Sderaadt     char *buf1, *buf2;
6011e72d8d2Sderaadt     struct stat sb1, sb2;
6021e72d8d2Sderaadt     int fd1, fd2;
6031e72d8d2Sderaadt     int ret;
6041e72d8d2Sderaadt 
6051e72d8d2Sderaadt     if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
6061e72d8d2Sderaadt 	error (1, errno, "cannot open file %s for comparing", file1);
6071e72d8d2Sderaadt     if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
6081e72d8d2Sderaadt 	error (1, errno, "cannot open file %s for comparing", file2);
6091e72d8d2Sderaadt     if (fstat (fd1, &sb1) < 0)
6101e72d8d2Sderaadt 	error (1, errno, "cannot fstat %s", file1);
6111e72d8d2Sderaadt     if (fstat (fd2, &sb2) < 0)
6121e72d8d2Sderaadt 	error (1, errno, "cannot fstat %s", file2);
6131e72d8d2Sderaadt 
6141e72d8d2Sderaadt     /* A generic file compare routine might compare st_dev & st_ino here
6151e72d8d2Sderaadt        to see if the two files being compared are actually the same file.
6161e72d8d2Sderaadt        But that won't happen in CVS, so we won't bother. */
6171e72d8d2Sderaadt 
6181e72d8d2Sderaadt     if (sb1.st_size != sb2.st_size)
6191e72d8d2Sderaadt 	ret = 1;
6201e72d8d2Sderaadt     else if (sb1.st_size == 0)
6211e72d8d2Sderaadt 	ret = 0;
6221e72d8d2Sderaadt     else
6231e72d8d2Sderaadt     {
6241e72d8d2Sderaadt 	/* FIXME: compute the optimal buffer size by computing the least
6251e72d8d2Sderaadt 	   common multiple of the files st_blocks field */
6261e72d8d2Sderaadt 	size_t buf_size = 8 * 1024;
6271e72d8d2Sderaadt 	size_t read1;
6281e72d8d2Sderaadt 	size_t read2;
6291e72d8d2Sderaadt 
6301e72d8d2Sderaadt 	buf1 = xmalloc (buf_size);
6311e72d8d2Sderaadt 	buf2 = xmalloc (buf_size);
6321e72d8d2Sderaadt 
6331e72d8d2Sderaadt 	do
6341e72d8d2Sderaadt 	{
6351e72d8d2Sderaadt 	    read1 = block_read (fd1, buf1, buf_size);
6361e72d8d2Sderaadt 	    if (read1 == (size_t)-1)
6371e72d8d2Sderaadt 		error (1, errno, "cannot read file %s for comparing", file1);
6381e72d8d2Sderaadt 
6391e72d8d2Sderaadt 	    read2 = block_read (fd2, buf2, buf_size);
6401e72d8d2Sderaadt 	    if (read2 == (size_t)-1)
6411e72d8d2Sderaadt 		error (1, errno, "cannot read file %s for comparing", file2);
6421e72d8d2Sderaadt 
6431e72d8d2Sderaadt 	    /* assert (read1 == read2); */
6441e72d8d2Sderaadt 
6451e72d8d2Sderaadt 	    ret = memcmp(buf1, buf2, read1);
6461e72d8d2Sderaadt 	} while (ret == 0 && read1 == buf_size);
6471e72d8d2Sderaadt 
6481e72d8d2Sderaadt 	free (buf1);
6491e72d8d2Sderaadt 	free (buf2);
6501e72d8d2Sderaadt     }
6511e72d8d2Sderaadt 
6521e72d8d2Sderaadt     (void) close (fd1);
6531e72d8d2Sderaadt     (void) close (fd2);
6541e72d8d2Sderaadt     return (ret);
6551e72d8d2Sderaadt }
6561e72d8d2Sderaadt 
65750bf276cStholo 
65850bf276cStholo /* Generate a unique temporary filename.  Returns a pointer to a newly
65950bf276cStholo    malloc'd string containing the name.  Returns successfully or not at
66050bf276cStholo    all.  */
66150bf276cStholo char *
66250bf276cStholo cvs_temp_name ()
66350bf276cStholo {
66450bf276cStholo     char *retval;
6651e72d8d2Sderaadt 
66650bf276cStholo     retval = _tempnam (NULL, NULL);
66750bf276cStholo     if (retval == NULL)
66850bf276cStholo 	error (1, errno, "cannot generate temporary filename");
66950bf276cStholo     return retval;
67050bf276cStholo }
67150bf276cStholo 
6721e72d8d2Sderaadt /* Return non-zero iff FILENAME is absolute.
6731e72d8d2Sderaadt    Trivial under Unix, but more complicated under other systems.  */
6741e72d8d2Sderaadt int
6751e72d8d2Sderaadt isabsolute (filename)
6761e72d8d2Sderaadt     const char *filename;
6771e72d8d2Sderaadt {
6782286d8edStholo     /* FIXME: This routine seems to interact poorly with
6792286d8edStholo        strip_trailing_slashes.  For example, specify ":local:r:\" as
6802286d8edStholo        CVSROOT.  The CVS/Root file will contain ":local:r:" and then
6812286d8edStholo        isabsolute will complain about the root not being an absolute
6822286d8edStholo        pathname.  My guess is that strip_trailing_slashes is the right
6832286d8edStholo        place to fix this.  */
6841e72d8d2Sderaadt     return (ISDIRSEP (filename[0])
6851e72d8d2Sderaadt             || (filename[0] != '\0'
6861e72d8d2Sderaadt                 && filename[1] == ':'
6871e72d8d2Sderaadt                 && ISDIRSEP (filename[2])));
6881e72d8d2Sderaadt }
6891e72d8d2Sderaadt 
6901e72d8d2Sderaadt /* Return a pointer into PATH's last component.  */
6911e72d8d2Sderaadt char *
6921e72d8d2Sderaadt last_component (char *path)
6931e72d8d2Sderaadt {
6941e72d8d2Sderaadt     char *scan;
6951e72d8d2Sderaadt     char *last = 0;
6961e72d8d2Sderaadt 
6971e72d8d2Sderaadt     for (scan = path; *scan; scan++)
6981e72d8d2Sderaadt         if (ISDIRSEP (*scan))
6991e72d8d2Sderaadt 	    last = scan;
7001e72d8d2Sderaadt 
7015e617892Stholo     if (last && (last != path))
7021e72d8d2Sderaadt         return last + 1;
7031e72d8d2Sderaadt     else
7041e72d8d2Sderaadt         return path;
7051e72d8d2Sderaadt }
7061e72d8d2Sderaadt 
7071e72d8d2Sderaadt 
70850bf276cStholo /* NT has two evironment variables, HOMEPATH and HOMEDRIVE, which,
70950bf276cStholo    when combined as ${HOMEDRIVE}${HOMEPATH}, give the unix equivalent
71050bf276cStholo    of HOME.  Some NT users are just too unixy, though, and set the
71150bf276cStholo    HOME variable themselves.  Therefore, we check for HOME first, and
712780d15dfStholo    then try to combine the other two if that fails.
713780d15dfStholo 
714780d15dfStholo    Looking for HOME strikes me as bogus, particularly if the only reason
715780d15dfStholo    is to cater to "unixy users".  On the other hand, if the reasoning is
716780d15dfStholo    there should be a single variable, rather than requiring people to
717780d15dfStholo    set both HOMEDRIVE and HOMEPATH, then it starts to make a little more
718780d15dfStholo    sense.
719780d15dfStholo 
720780d15dfStholo    Win95: The system doesn't set HOME, HOMEDRIVE, or HOMEPATH (at
721780d15dfStholo    least if you set it up as the "all users under one user ID" or
722780d15dfStholo    whatever the name of that option is).  Based on thing overheard on
723780d15dfStholo    the net, it seems that users of the pserver client have gotten in
724780d15dfStholo    the habit of setting HOME (if you don't use pserver, you can
725780d15dfStholo    probably get away without having a reasonable return from
726780d15dfStholo    get_homedir.  Of course you lose .cvsrc and .cvsignore, but many
727780d15dfStholo    users won't notice).  So it would seem that we should be somewhat
728780d15dfStholo    careful if we try to change the current behavior.
729780d15dfStholo 
730780d15dfStholo    NT 3.51 or NT 4.0: I haven't checked this myself, but I am told
731780d15dfStholo    that HOME gets set, but not to the user's home directory.  It is
732780d15dfStholo    said to be set to c:\users\default by default.  */
73350bf276cStholo 
734c26070a5Stholo char *
735c26070a5Stholo get_homedir ()
736c26070a5Stholo {
737780d15dfStholo     static char *pathbuf;
73850bf276cStholo     char *hd, *hp;
73950bf276cStholo 
740780d15dfStholo     if (pathbuf != NULL)
741780d15dfStholo 	return pathbuf;
742780d15dfStholo     else if ((hd = getenv ("HOME")))
74350bf276cStholo 	return hd;
74450bf276cStholo     else if ((hd = getenv ("HOMEDRIVE")) && (hp = getenv ("HOMEPATH")))
74550bf276cStholo     {
746780d15dfStholo 	pathbuf = xmalloc (strlen (hd) + strlen (hp) + 5);
747780d15dfStholo 	strcpy (pathbuf, hd);
748780d15dfStholo 	strcat (pathbuf, hp);
74950bf276cStholo 
75050bf276cStholo 	return pathbuf;
75150bf276cStholo     }
75250bf276cStholo     else
75350bf276cStholo 	return NULL;
754c26070a5Stholo }
755c2c61682Stholo 
756c2c61682Stholo /* See cvs.h for description.  */
757c2c61682Stholo void
758c2c61682Stholo expand_wild (argc, argv, pargc, pargv)
759c2c61682Stholo     int argc;
760c2c61682Stholo     char **argv;
761c2c61682Stholo     int *pargc;
762c2c61682Stholo     char ***pargv;
763c2c61682Stholo {
764c2c61682Stholo     int i;
765c2c61682Stholo     int new_argc;
766c2c61682Stholo     char **new_argv;
767c2c61682Stholo     /* Allocated size of new_argv.  We arrange it so there is always room for
768c2c61682Stholo 	   one more element.  */
769c2c61682Stholo     int max_new_argc;
770c2c61682Stholo 
771c2c61682Stholo     new_argc = 0;
772c2c61682Stholo     /* Add one so this is never zero.  */
773c2c61682Stholo     max_new_argc = argc + 1;
774c2c61682Stholo     new_argv = (char **) xmalloc (max_new_argc * sizeof (char *));
775c2c61682Stholo     for (i = 0; i < argc; ++i)
776c2c61682Stholo     {
777c2c61682Stholo 	HANDLE h;
778c2c61682Stholo 	WIN32_FIND_DATA fdata;
779c2c61682Stholo 
78050bf276cStholo 	/* These variables help us extract the directory name from the
78150bf276cStholo            given pathname. */
78250bf276cStholo 
78350bf276cStholo 	char *last_forw_slash, *last_back_slash, *end_of_dirname;
78450bf276cStholo 	int dirname_length = 0;
78550bf276cStholo 
786b6c02222Stholo 	/* FIXME: If argv[i] is ".", this code will expand it to the
787b6c02222Stholo 	   name of the current directory in its parent directory which
788b6c02222Stholo 	   will cause start_recursion to do all manner of strange things
789b6c02222Stholo 	   with it (culminating in an error).  This breaks "cvs co .".
790b6c02222Stholo 	   As nearly as I can guess, this bug has existed since
791b6c02222Stholo 	   expand_wild was first created.  At least, it is in CVS 1.9 (I
792b6c02222Stholo 	   just tried it).  */
793b6c02222Stholo 
79450bf276cStholo 	/* FindFirstFile doesn't return pathnames, so we have to do
79550bf276cStholo 	   this ourselves.  Luckily, it's no big deal, since globbing
79650bf276cStholo 	   characters under Win32s can only occur in the last segment
79750bf276cStholo 	   of the path.  For example,
79850bf276cStholo                 /a/path/q*.h                      valid
79950bf276cStholo 	        /w32/q*.dir/cant/do/this/q*.h     invalid */
80050bf276cStholo 
80150bf276cStholo 	/* Win32 can handle both forward and backward slashes as
80250bf276cStholo            filenames -- check for both. */
80350bf276cStholo 
80450bf276cStholo 	last_forw_slash = strrchr (argv[i], '/');
80550bf276cStholo 	last_back_slash = strrchr (argv[i], '\\');
80650bf276cStholo 
80750bf276cStholo #define cvs_max(x,y) ((x >= y) ? (x) : (y))
80850bf276cStholo 
809b6c02222Stholo 	/* FIXME: this comparing a NULL pointer to a non-NULL one is
810b6c02222Stholo 	   extremely ugly, and I strongly suspect *NOT* sanctioned by
811b6c02222Stholo 	   ANSI C.  The code should just use last_component instead.  */
81250bf276cStholo 	end_of_dirname = cvs_max (last_forw_slash, last_back_slash);
81350bf276cStholo 
81450bf276cStholo 	if (end_of_dirname == NULL)
81550bf276cStholo 	  dirname_length = 0;	/* no directory name */
81650bf276cStholo 	else
81750bf276cStholo 	  dirname_length = end_of_dirname - argv[i] + 1; /* include slash */
81850bf276cStholo 
819c2c61682Stholo 	h = FindFirstFile (argv[i], &fdata);
820c2c61682Stholo 	if (h == INVALID_HANDLE_VALUE)
821c2c61682Stholo 	{
822c2c61682Stholo 	    if (GetLastError () == ENOENT)
823c2c61682Stholo 	    {
824c2c61682Stholo 		/* No match.  The file specified didn't contain a wildcard (in which case
825c2c61682Stholo 		   we clearly should return it unchanged), or it contained a wildcard which
826c2c61682Stholo 		   didn't match (in which case it might be better for it to be an error,
827c2c61682Stholo 		   but we don't try to do that).  */
828c2c61682Stholo 		new_argv [new_argc++] = xstrdup (argv[i]);
829c2c61682Stholo 		if (new_argc == max_new_argc)
830c2c61682Stholo 		{
831c2c61682Stholo 		    max_new_argc *= 2;
832c2c61682Stholo 		    new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
833c2c61682Stholo 		}
834c2c61682Stholo 	    }
835c2c61682Stholo 	    else
836c2c61682Stholo 	    {
837c2c61682Stholo 		error (1, errno, "cannot find %s", argv[i]);
838c2c61682Stholo 	    }
839c2c61682Stholo 	}
840c2c61682Stholo 	else
841c2c61682Stholo 	{
842c2c61682Stholo 	    while (1)
843c2c61682Stholo 	    {
84450bf276cStholo 		new_argv[new_argc] =
84550bf276cStholo 		    (char *) xmalloc (strlen (fdata.cFileName) + 1
84650bf276cStholo 				      + dirname_length);
84750bf276cStholo 
84850bf276cStholo 		/* Copy the directory name, if there is one. */
84950bf276cStholo 
85050bf276cStholo 		if (dirname_length)
85150bf276cStholo 		{
85250bf276cStholo 		    strncpy (new_argv[new_argc], argv[i], dirname_length);
85350bf276cStholo 		    new_argv[new_argc][dirname_length] = '\0';
85450bf276cStholo 		}
85550bf276cStholo 		else
85650bf276cStholo 		    new_argv[new_argc][0] = '\0';
85750bf276cStholo 
85850bf276cStholo 		/* Copy the file name. */
85950bf276cStholo 
860461cc63eStholo 		if (fncmp (argv[i] + dirname_length, fdata.cFileName) == 0)
861461cc63eStholo 		    /* We didn't expand a wildcard; we just matched a filename.
862461cc63eStholo 		       Use the file name as specified rather than the filename
863461cc63eStholo 		       which exists in the directory (they may differ in case).
864461cc63eStholo 		       This is needed to make cvs add on a directory consistently
865461cc63eStholo 		       use the name specified on the command line, but it is
866461cc63eStholo 		       probably a good idea in other contexts too.  */
867461cc63eStholo 		    strcpy (new_argv[new_argc], argv[i]);
868461cc63eStholo 		else
86950bf276cStholo 		    strcat (new_argv[new_argc], fdata.cFileName);
87050bf276cStholo 
87150bf276cStholo 		new_argc++;
87250bf276cStholo 
873c2c61682Stholo 		if (new_argc == max_new_argc)
874c2c61682Stholo 		{
875c2c61682Stholo 		    max_new_argc *= 2;
876c2c61682Stholo 		    new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
877c2c61682Stholo 		}
878c2c61682Stholo 		if (!FindNextFile (h, &fdata))
879c2c61682Stholo 		{
880c2c61682Stholo 		    if (GetLastError () == ERROR_NO_MORE_FILES)
881c2c61682Stholo 			break;
882c2c61682Stholo 		    else
883c2c61682Stholo 			error (1, errno, "cannot find %s", argv[i]);
884c2c61682Stholo 		}
885c2c61682Stholo 	    }
886c2c61682Stholo 	    if (!FindClose (h))
887c2c61682Stholo 		error (1, GetLastError (), "cannot close %s", argv[i]);
888c2c61682Stholo 	}
889c2c61682Stholo     }
890c2c61682Stholo     *pargc = new_argc;
891c2c61682Stholo     *pargv = new_argv;
892c2c61682Stholo }
893b2346922Stholo 
894b2346922Stholo static void check_statbuf (const char *file, struct stat *sb)
895b2346922Stholo {
896b2346922Stholo     /* Win32 processes file times in a 64 bit format
897b2346922Stholo        (see Win32 functions SetFileTime and GetFileTime).
898b2346922Stholo        If the file time on a file doesn't fit into the
899b2346922Stholo        32 bit time_t format, then stat will set that time
900b2346922Stholo        to -1.  This would be OK, except that functions
901b2346922Stholo        like ctime() don't check for validity.  So what we
902b2346922Stholo        do here is to give a error on -1.  A cleaner solution
903b2346922Stholo        might be to change CVS's interfaces to return a time
904b2346922Stholo        in RCS format (for example), and then implement it
905b2346922Stholo        on Win32 via GetFileTime, but that would be a lot of
906b2346922Stholo        hair and I'm not sure there is much payoff.  */
907b2346922Stholo     if (sb->st_mtime == (time_t) -1)
908b2346922Stholo 	error (1, 0, "invalid modification time for %s", file);
909b2346922Stholo     if (sb->st_ctime == (time_t) -1)
910b2346922Stholo 	/* I'm not sure what this means on windows.  It
911b2346922Stholo 	   might be a creation time (unlike unix)....  */
912b2346922Stholo 	error (1, 0, "invalid ctime for %s", file);
913b2346922Stholo     if (sb->st_atime == (time_t) -1)
914b2346922Stholo 	error (1, 0, "invalid access time for %s", file);
915b2346922Stholo }
916b2346922Stholo 
917b2346922Stholo int
918b2346922Stholo wnt_stat (const char *file, struct stat *sb)
919b2346922Stholo {
920b2346922Stholo     int retval;
921b2346922Stholo 
922b2346922Stholo     retval = stat (file, sb);
923b2346922Stholo     if (retval < 0)
924b2346922Stholo 	return retval;
925b2346922Stholo     check_statbuf (file, sb);
926b2346922Stholo     return retval;
927b2346922Stholo }
928b2346922Stholo 
929b2346922Stholo int
930b2346922Stholo wnt_lstat (const char *file, struct stat *sb)
931b2346922Stholo {
932b2346922Stholo     int retval;
933b2346922Stholo 
934b2346922Stholo     retval = lstat (file, sb);
935b2346922Stholo     if (retval < 0)
936b2346922Stholo 	return retval;
937b2346922Stholo     check_statbuf (file, sb);
938b2346922Stholo     return retval;
939b2346922Stholo }
940