xref: /openbsd-src/gnu/usr.bin/cvs/windows-NT/filesubr.c (revision 43c1707e6f6829177cb1974ee6615ce6c1307689)
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
copy_file(from,to)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
isdir(file)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
islink(file)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
isfile(file)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
isreadable(file)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
iswritable(file)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
isaccessible(file,mode)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 *
open_file(name,mode)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
make_directory(name)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
make_directories(name)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
mkdir_if_needed(name)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
xchmod(fname,writable)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
readlink(char * path,char * buf,int buf_size)3661e72d8d2Sderaadt readlink (char *path, char *buf, int buf_size)
3671e72d8d2Sderaadt {
3681e72d8d2Sderaadt     errno = EINVAL;
3691e72d8d2Sderaadt     return -1;
3701e72d8d2Sderaadt }
3711e72d8d2Sderaadt 
372c71bc7e2Stholo /* Rename for NT which works for read only files.  Apparently if we are
373c71bc7e2Stholo    accessing FROM and TO via a Novell network, this is an issue.  */
374c71bc7e2Stholo int
wnt_rename(from,to)375c71bc7e2Stholo wnt_rename (from, to)
376c71bc7e2Stholo     const char *from;
377c71bc7e2Stholo     const char *to;
378c71bc7e2Stholo {
379c71bc7e2Stholo     int result, save_errno;
380c71bc7e2Stholo     int readonly = !iswritable (from);
381c71bc7e2Stholo 
382c71bc7e2Stholo     if (readonly)
383c71bc7e2Stholo     {
384c71bc7e2Stholo 	if (chmod (from, S_IWRITE) < 0)
385c71bc7e2Stholo 	    return -1;
386c71bc7e2Stholo     }
387c71bc7e2Stholo     result = rename (from, to);
388c71bc7e2Stholo     save_errno = errno;
389c71bc7e2Stholo     if (readonly)
390c71bc7e2Stholo     {
391c71bc7e2Stholo 	if (result == 0)
392c71bc7e2Stholo 	{
393c71bc7e2Stholo 	    if (chmod (to, S_IREAD) < 0)
394c71bc7e2Stholo 		return -1;
395c71bc7e2Stholo 	}
396c71bc7e2Stholo 	else
397c71bc7e2Stholo 	{
398c71bc7e2Stholo 	    /* We have a choice of which error to report, if there is
399c71bc7e2Stholo 	       one here too; report the one from rename ().  */
400c71bc7e2Stholo 	    chmod (from, S_IREAD);
401c71bc7e2Stholo 	}
402c71bc7e2Stholo 	errno = save_errno;
403c71bc7e2Stholo     }
404c71bc7e2Stholo     return result;
405c71bc7e2Stholo }
406c71bc7e2Stholo 
4071e72d8d2Sderaadt /*
4081e72d8d2Sderaadt  * Rename a file and die if it fails
4091e72d8d2Sderaadt  */
4101e72d8d2Sderaadt void
rename_file(from,to)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);
428c71bc7e2Stholo     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
unlink_file(f)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
unlink_file_dir(f)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
deep_remove_dir(path)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
block_read(fd,buf,nchars)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
xcmp(file1,file2)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 /* Generate a unique temporary filename.  Returns a pointer to a newly
658*43c1707eStholo  * malloc'd string containing the name.  Returns successfully or not at
659*43c1707eStholo  * all.
660*43c1707eStholo  *
661*43c1707eStholo  *     THIS FUNCTION IS DEPRECATED!!!  USE cvs_temp_file INSTEAD!!!
662*43c1707eStholo  *
663*43c1707eStholo  * and yes, I know about the way the rcs commands use temp files.  I think
664*43c1707eStholo  * they should be converted too but I don't have time to look into it right
665*43c1707eStholo  * now.
666*43c1707eStholo  */
66750bf276cStholo char *
cvs_temp_name()66850bf276cStholo cvs_temp_name ()
66950bf276cStholo {
670*43c1707eStholo     char *fn;
671*43c1707eStholo     FILE *fp;
6721e72d8d2Sderaadt 
673*43c1707eStholo     fp = cvs_temp_file (&fn);
674*43c1707eStholo     if (fp == NULL)
675*43c1707eStholo 	error (1, errno, "Failed to create temporary file");
676*43c1707eStholo     if (fclose (fp) == EOF)
677*43c1707eStholo 	error (0, errno, "Failed to close temporary file %s", fn);
678*43c1707eStholo     return fn;
67950bf276cStholo }
680*43c1707eStholo 
681*43c1707eStholo /* Generate a unique temporary filename and return an open file stream
682*43c1707eStholo  * to the truncated file by that name
683*43c1707eStholo  *
684*43c1707eStholo  *  INPUTS
685*43c1707eStholo  *	filename	where to place the pointer to the newly allocated file
686*43c1707eStholo  *   			name string
687*43c1707eStholo  *
688*43c1707eStholo  *  OUTPUTS
689*43c1707eStholo  *	filename	dereferenced, will point to the newly allocated file
690*43c1707eStholo  *			name string.  This value is undefined if the function
691*43c1707eStholo  *			returns an error.
692*43c1707eStholo  *
693*43c1707eStholo  *  RETURNS
694*43c1707eStholo  *	An open file pointer to a read/write mode empty temporary file with the
695*43c1707eStholo  *	unique file name or NULL on failure.
696*43c1707eStholo  *
697*43c1707eStholo  *  ERRORS
698*43c1707eStholo  *	on error, errno will be set to some value either by CVS_FOPEN or
699*43c1707eStholo  *	whatever system function is called to generate the temporary file name
700*43c1707eStholo  */
cvs_temp_file(filename)701*43c1707eStholo FILE *cvs_temp_file (filename)
702*43c1707eStholo     char **filename;
703*43c1707eStholo {
704*43c1707eStholo     char *fn;
705*43c1707eStholo     FILE *fp;
706*43c1707eStholo 
707*43c1707eStholo     /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
708*43c1707eStholo      * some of the rcs & diff functions which rely on a temp file run in
709*43c1707eStholo      * noexec mode too.
710*43c1707eStholo      */
711*43c1707eStholo 
712*43c1707eStholo     /* assert (filename != NULL); */
713*43c1707eStholo 
714*43c1707eStholo     fn = _tempnam (Tmpdir, "cvs");
715*43c1707eStholo     if (fn == NULL) fp = NULL;
716*43c1707eStholo     else if ((fp = CVS_FOPEN (fn, "w+")) == NULL) free (fn);
717*43c1707eStholo 
718*43c1707eStholo     /* tempnam returns a pointer to a newly malloc'd string, so there's
719*43c1707eStholo      * no need for a xstrdup
720*43c1707eStholo      */
721*43c1707eStholo 
722*43c1707eStholo     *filename = fn;
723*43c1707eStholo     return fp;
724*43c1707eStholo }
725*43c1707eStholo 
7261e72d8d2Sderaadt /* Return non-zero iff FILENAME is absolute.
7271e72d8d2Sderaadt    Trivial under Unix, but more complicated under other systems.  */
7281e72d8d2Sderaadt int
isabsolute(filename)7291e72d8d2Sderaadt isabsolute (filename)
7301e72d8d2Sderaadt     const char *filename;
7311e72d8d2Sderaadt {
7322286d8edStholo     /* FIXME: This routine seems to interact poorly with
7332286d8edStholo        strip_trailing_slashes.  For example, specify ":local:r:\" as
7342286d8edStholo        CVSROOT.  The CVS/Root file will contain ":local:r:" and then
7352286d8edStholo        isabsolute will complain about the root not being an absolute
7362286d8edStholo        pathname.  My guess is that strip_trailing_slashes is the right
7372286d8edStholo        place to fix this.  */
7381e72d8d2Sderaadt     return (ISDIRSEP (filename[0])
7391e72d8d2Sderaadt             || (filename[0] != '\0'
7401e72d8d2Sderaadt                 && filename[1] == ':'
7411e72d8d2Sderaadt                 && ISDIRSEP (filename[2])));
7421e72d8d2Sderaadt }
7431e72d8d2Sderaadt 
7441e72d8d2Sderaadt /* Return a pointer into PATH's last component.  */
7451e72d8d2Sderaadt char *
last_component(char * path)7461e72d8d2Sderaadt last_component (char *path)
7471e72d8d2Sderaadt {
7481e72d8d2Sderaadt     char *scan;
7491e72d8d2Sderaadt     char *last = 0;
7501e72d8d2Sderaadt 
7511e72d8d2Sderaadt     for (scan = path; *scan; scan++)
7521e72d8d2Sderaadt         if (ISDIRSEP (*scan))
7531e72d8d2Sderaadt 	    last = scan;
7541e72d8d2Sderaadt 
7555e617892Stholo     if (last && (last != path))
7561e72d8d2Sderaadt         return last + 1;
7571e72d8d2Sderaadt     else
7581e72d8d2Sderaadt         return path;
7591e72d8d2Sderaadt }
7601e72d8d2Sderaadt 
7611e72d8d2Sderaadt 
76250bf276cStholo /* NT has two evironment variables, HOMEPATH and HOMEDRIVE, which,
76350bf276cStholo    when combined as ${HOMEDRIVE}${HOMEPATH}, give the unix equivalent
76450bf276cStholo    of HOME.  Some NT users are just too unixy, though, and set the
76550bf276cStholo    HOME variable themselves.  Therefore, we check for HOME first, and
766780d15dfStholo    then try to combine the other two if that fails.
767780d15dfStholo 
768780d15dfStholo    Looking for HOME strikes me as bogus, particularly if the only reason
769780d15dfStholo    is to cater to "unixy users".  On the other hand, if the reasoning is
770780d15dfStholo    there should be a single variable, rather than requiring people to
771780d15dfStholo    set both HOMEDRIVE and HOMEPATH, then it starts to make a little more
772780d15dfStholo    sense.
773780d15dfStholo 
774780d15dfStholo    Win95: The system doesn't set HOME, HOMEDRIVE, or HOMEPATH (at
775780d15dfStholo    least if you set it up as the "all users under one user ID" or
776780d15dfStholo    whatever the name of that option is).  Based on thing overheard on
777780d15dfStholo    the net, it seems that users of the pserver client have gotten in
778780d15dfStholo    the habit of setting HOME (if you don't use pserver, you can
779780d15dfStholo    probably get away without having a reasonable return from
780780d15dfStholo    get_homedir.  Of course you lose .cvsrc and .cvsignore, but many
781780d15dfStholo    users won't notice).  So it would seem that we should be somewhat
782780d15dfStholo    careful if we try to change the current behavior.
783780d15dfStholo 
784780d15dfStholo    NT 3.51 or NT 4.0: I haven't checked this myself, but I am told
785780d15dfStholo    that HOME gets set, but not to the user's home directory.  It is
786780d15dfStholo    said to be set to c:\users\default by default.  */
78750bf276cStholo 
788c26070a5Stholo char *
get_homedir()789c26070a5Stholo get_homedir ()
790c26070a5Stholo {
791780d15dfStholo     static char *pathbuf;
79250bf276cStholo     char *hd, *hp;
79350bf276cStholo 
794780d15dfStholo     if (pathbuf != NULL)
795780d15dfStholo 	return pathbuf;
796780d15dfStholo     else if ((hd = getenv ("HOME")))
79750bf276cStholo 	return hd;
79850bf276cStholo     else if ((hd = getenv ("HOMEDRIVE")) && (hp = getenv ("HOMEPATH")))
79950bf276cStholo     {
800780d15dfStholo 	pathbuf = xmalloc (strlen (hd) + strlen (hp) + 5);
801780d15dfStholo 	strcpy (pathbuf, hd);
802780d15dfStholo 	strcat (pathbuf, hp);
80350bf276cStholo 
80450bf276cStholo 	return pathbuf;
80550bf276cStholo     }
80650bf276cStholo     else
80750bf276cStholo 	return NULL;
808c26070a5Stholo }
809c2c61682Stholo 
810c2c61682Stholo /* See cvs.h for description.  */
811c2c61682Stholo void
expand_wild(argc,argv,pargc,pargv)812c2c61682Stholo expand_wild (argc, argv, pargc, pargv)
813c2c61682Stholo     int argc;
814c2c61682Stholo     char **argv;
815c2c61682Stholo     int *pargc;
816c2c61682Stholo     char ***pargv;
817c2c61682Stholo {
818c2c61682Stholo     int i;
819c2c61682Stholo     int new_argc;
820c2c61682Stholo     char **new_argv;
821c2c61682Stholo     /* Allocated size of new_argv.  We arrange it so there is always room for
822c2c61682Stholo 	   one more element.  */
823c2c61682Stholo     int max_new_argc;
824c2c61682Stholo 
825c2c61682Stholo     new_argc = 0;
826c2c61682Stholo     /* Add one so this is never zero.  */
827c2c61682Stholo     max_new_argc = argc + 1;
828c2c61682Stholo     new_argv = (char **) xmalloc (max_new_argc * sizeof (char *));
829c2c61682Stholo     for (i = 0; i < argc; ++i)
830c2c61682Stholo     {
831c2c61682Stholo 	HANDLE h;
832c2c61682Stholo 	WIN32_FIND_DATA fdata;
833c2c61682Stholo 
83450bf276cStholo 	/* These variables help us extract the directory name from the
83550bf276cStholo            given pathname. */
83650bf276cStholo 
83750bf276cStholo 	char *last_forw_slash, *last_back_slash, *end_of_dirname;
83850bf276cStholo 	int dirname_length = 0;
83950bf276cStholo 
840b6c02222Stholo 	/* FIXME: If argv[i] is ".", this code will expand it to the
841b6c02222Stholo 	   name of the current directory in its parent directory which
842b6c02222Stholo 	   will cause start_recursion to do all manner of strange things
843b6c02222Stholo 	   with it (culminating in an error).  This breaks "cvs co .".
844b6c02222Stholo 	   As nearly as I can guess, this bug has existed since
845b6c02222Stholo 	   expand_wild was first created.  At least, it is in CVS 1.9 (I
846b6c02222Stholo 	   just tried it).  */
847b6c02222Stholo 
84850bf276cStholo 	/* FindFirstFile doesn't return pathnames, so we have to do
84950bf276cStholo 	   this ourselves.  Luckily, it's no big deal, since globbing
85050bf276cStholo 	   characters under Win32s can only occur in the last segment
85150bf276cStholo 	   of the path.  For example,
85250bf276cStholo                 /a/path/q*.h                      valid
85350bf276cStholo 	        /w32/q*.dir/cant/do/this/q*.h     invalid */
85450bf276cStholo 
85550bf276cStholo 	/* Win32 can handle both forward and backward slashes as
85650bf276cStholo            filenames -- check for both. */
85750bf276cStholo 
85850bf276cStholo 	last_forw_slash = strrchr (argv[i], '/');
85950bf276cStholo 	last_back_slash = strrchr (argv[i], '\\');
86050bf276cStholo 
86150bf276cStholo #define cvs_max(x,y) ((x >= y) ? (x) : (y))
86250bf276cStholo 
863b6c02222Stholo 	/* FIXME: this comparing a NULL pointer to a non-NULL one is
864b6c02222Stholo 	   extremely ugly, and I strongly suspect *NOT* sanctioned by
865b6c02222Stholo 	   ANSI C.  The code should just use last_component instead.  */
86650bf276cStholo 	end_of_dirname = cvs_max (last_forw_slash, last_back_slash);
86750bf276cStholo 
86850bf276cStholo 	if (end_of_dirname == NULL)
86950bf276cStholo 	  dirname_length = 0;	/* no directory name */
87050bf276cStholo 	else
87150bf276cStholo 	  dirname_length = end_of_dirname - argv[i] + 1; /* include slash */
87250bf276cStholo 
873c2c61682Stholo 	h = FindFirstFile (argv[i], &fdata);
874c2c61682Stholo 	if (h == INVALID_HANDLE_VALUE)
875c2c61682Stholo 	{
876c2c61682Stholo 	    if (GetLastError () == ENOENT)
877c2c61682Stholo 	    {
878c2c61682Stholo 		/* No match.  The file specified didn't contain a wildcard (in which case
879c2c61682Stholo 		   we clearly should return it unchanged), or it contained a wildcard which
880c2c61682Stholo 		   didn't match (in which case it might be better for it to be an error,
881c2c61682Stholo 		   but we don't try to do that).  */
882c2c61682Stholo 		new_argv [new_argc++] = xstrdup (argv[i]);
883c2c61682Stholo 		if (new_argc == max_new_argc)
884c2c61682Stholo 		{
885c2c61682Stholo 		    max_new_argc *= 2;
886c2c61682Stholo 		    new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
887c2c61682Stholo 		}
888c2c61682Stholo 	    }
889c2c61682Stholo 	    else
890c2c61682Stholo 	    {
891c2c61682Stholo 		error (1, errno, "cannot find %s", argv[i]);
892c2c61682Stholo 	    }
893c2c61682Stholo 	}
894c2c61682Stholo 	else
895c2c61682Stholo 	{
896c2c61682Stholo 	    while (1)
897c2c61682Stholo 	    {
89850bf276cStholo 		new_argv[new_argc] =
89950bf276cStholo 		    (char *) xmalloc (strlen (fdata.cFileName) + 1
90050bf276cStholo 				      + dirname_length);
90150bf276cStholo 
90250bf276cStholo 		/* Copy the directory name, if there is one. */
90350bf276cStholo 
90450bf276cStholo 		if (dirname_length)
90550bf276cStholo 		{
90650bf276cStholo 		    strncpy (new_argv[new_argc], argv[i], dirname_length);
90750bf276cStholo 		    new_argv[new_argc][dirname_length] = '\0';
90850bf276cStholo 		}
90950bf276cStholo 		else
91050bf276cStholo 		    new_argv[new_argc][0] = '\0';
91150bf276cStholo 
91250bf276cStholo 		/* Copy the file name. */
91350bf276cStholo 
914461cc63eStholo 		if (fncmp (argv[i] + dirname_length, fdata.cFileName) == 0)
915461cc63eStholo 		    /* We didn't expand a wildcard; we just matched a filename.
916461cc63eStholo 		       Use the file name as specified rather than the filename
917461cc63eStholo 		       which exists in the directory (they may differ in case).
918461cc63eStholo 		       This is needed to make cvs add on a directory consistently
919461cc63eStholo 		       use the name specified on the command line, but it is
920461cc63eStholo 		       probably a good idea in other contexts too.  */
921461cc63eStholo 		    strcpy (new_argv[new_argc], argv[i]);
922461cc63eStholo 		else
92350bf276cStholo 		    strcat (new_argv[new_argc], fdata.cFileName);
92450bf276cStholo 
92550bf276cStholo 		new_argc++;
92650bf276cStholo 
927c2c61682Stholo 		if (new_argc == max_new_argc)
928c2c61682Stholo 		{
929c2c61682Stholo 		    max_new_argc *= 2;
930c2c61682Stholo 		    new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
931c2c61682Stholo 		}
932c2c61682Stholo 		if (!FindNextFile (h, &fdata))
933c2c61682Stholo 		{
934c2c61682Stholo 		    if (GetLastError () == ERROR_NO_MORE_FILES)
935c2c61682Stholo 			break;
936c2c61682Stholo 		    else
937c2c61682Stholo 			error (1, errno, "cannot find %s", argv[i]);
938c2c61682Stholo 		}
939c2c61682Stholo 	    }
940c2c61682Stholo 	    if (!FindClose (h))
941c2c61682Stholo 		error (1, GetLastError (), "cannot close %s", argv[i]);
942c2c61682Stholo 	}
943c2c61682Stholo     }
944c2c61682Stholo     *pargc = new_argc;
945c2c61682Stholo     *pargv = new_argv;
946c2c61682Stholo }
947b2346922Stholo 
check_statbuf(const char * file,struct stat * sb)948b2346922Stholo static void check_statbuf (const char *file, struct stat *sb)
949b2346922Stholo {
950*43c1707eStholo     struct tm *newtime;
951*43c1707eStholo     time_t long_time;
952*43c1707eStholo 
953b2346922Stholo     /* Win32 processes file times in a 64 bit format
954b2346922Stholo        (see Win32 functions SetFileTime and GetFileTime).
955b2346922Stholo        If the file time on a file doesn't fit into the
956b2346922Stholo        32 bit time_t format, then stat will set that time
957b2346922Stholo        to -1.  This would be OK, except that functions
958b2346922Stholo        like ctime() don't check for validity.  So what we
959b2346922Stholo        do here is to give a error on -1.  A cleaner solution
960b2346922Stholo        might be to change CVS's interfaces to return a time
961b2346922Stholo        in RCS format (for example), and then implement it
962b2346922Stholo        on Win32 via GetFileTime, but that would be a lot of
963b2346922Stholo        hair and I'm not sure there is much payoff.  */
964b2346922Stholo     if (sb->st_mtime == (time_t) -1)
965b2346922Stholo 	error (1, 0, "invalid modification time for %s", file);
966b2346922Stholo     if (sb->st_ctime == (time_t) -1)
967b2346922Stholo 	/* I'm not sure what this means on windows.  It
968b2346922Stholo 	   might be a creation time (unlike unix)....  */
969b2346922Stholo 	error (1, 0, "invalid ctime for %s", file);
970b2346922Stholo     if (sb->st_atime == (time_t) -1)
971b2346922Stholo 	error (1, 0, "invalid access time for %s", file);
972*43c1707eStholo 
973*43c1707eStholo     time( &long_time );			/* Get time as long integer. */
974*43c1707eStholo     newtime = localtime( &long_time );	/* Convert to local time. */
975*43c1707eStholo 
976*43c1707eStholo     /* we know for a fact that the stat function under Windoze NT 4.0 and,
977*43c1707eStholo      * by all reports, many other Windoze systems, will return file times
978*43c1707eStholo      * 3600 seconds too big when daylight savings time is in effect.  This is
979*43c1707eStholo      * a bug since it is defined as returning the time in UTC.
980*43c1707eStholo      *
981*43c1707eStholo      * So correct for it for now.
982*43c1707eStholo      */
983*43c1707eStholo     if (newtime->tm_isdst == 1)
984*43c1707eStholo     {
985*43c1707eStholo 	sb->st_ctime -= 3600;
986*43c1707eStholo 	sb->st_mtime -= 3600;
987*43c1707eStholo 	sb->st_atime -= 3600;
988*43c1707eStholo     }
989b2346922Stholo }
990b2346922Stholo 
991b2346922Stholo int
wnt_stat(const char * file,struct stat * sb)992b2346922Stholo wnt_stat (const char *file, struct stat *sb)
993b2346922Stholo {
994b2346922Stholo     int retval;
995b2346922Stholo 
996b2346922Stholo     retval = stat (file, sb);
997b2346922Stholo     if (retval < 0)
998b2346922Stholo 	return retval;
999b2346922Stholo     check_statbuf (file, sb);
1000b2346922Stholo     return retval;
1001b2346922Stholo }
1002b2346922Stholo 
1003b2346922Stholo int
wnt_lstat(const char * file,struct stat * sb)1004b2346922Stholo wnt_lstat (const char *file, struct stat *sb)
1005b2346922Stholo {
1006b2346922Stholo     int retval;
1007b2346922Stholo 
1008b2346922Stholo     retval = lstat (file, sb);
1009b2346922Stholo     if (retval < 0)
1010b2346922Stholo 	return retval;
1011b2346922Stholo     check_statbuf (file, sb);
1012b2346922Stholo     return retval;
1013b2346922Stholo }
1014