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