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