12286d8edStholo /* filesubr.c --- subroutines for dealing with files
22286d8edStholo Jim Blandy <jimb@cyclic.com>
32286d8edStholo
42286d8edStholo This file is part of GNU CVS.
52286d8edStholo
62286d8edStholo GNU CVS is free software; you can redistribute it and/or modify it
72286d8edStholo under the terms of the GNU General Public License as published by the
82286d8edStholo Free Software Foundation; either version 2, or (at your option) any
92286d8edStholo later version.
102286d8edStholo
112286d8edStholo This program is distributed in the hope that it will be useful,
122286d8edStholo but WITHOUT ANY WARRANTY; without even the implied warranty of
132286d8edStholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
142286d8edStholo GNU General Public License for more details. */
152286d8edStholo
162286d8edStholo /* These functions were moved out of subr.c because they need different
172286d8edStholo definitions under operating systems (like, say, Windows NT) with different
182286d8edStholo file system semantics. */
192286d8edStholo
202286d8edStholo #include "cvs.h"
212286d8edStholo #include <sys/param.h>
222286d8edStholo
232286d8edStholo /*
242286d8edStholo * I don't know of a convenient way to test this at configure time, or else
252286d8edStholo * I'd certainly do it there.
262286d8edStholo */
272286d8edStholo #if defined(NeXT)
282286d8edStholo #define LOSING_TMPNAM_FUNCTION
292286d8edStholo #endif
302286d8edStholo
312286d8edStholo static int deep_remove_dir PROTO((const char *path));
322286d8edStholo
332286d8edStholo /*
342286d8edStholo * Copies "from" to "to".
352286d8edStholo */
362286d8edStholo void
copy_file(from,to)372286d8edStholo copy_file (from, to)
382286d8edStholo const char *from;
392286d8edStholo const char *to;
402286d8edStholo {
412286d8edStholo struct stat sb;
422286d8edStholo struct utimbuf t;
432286d8edStholo int fdin, fdout;
442286d8edStholo
452286d8edStholo if (trace)
462286d8edStholo #ifdef SERVER_SUPPORT
472286d8edStholo (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
482286d8edStholo (server_active) ? 'S' : ' ', from, to);
492286d8edStholo #else
502286d8edStholo (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
512286d8edStholo #endif
522286d8edStholo if (noexec)
532286d8edStholo return;
542286d8edStholo
552286d8edStholo if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
562286d8edStholo error (1, errno, "cannot open %s for copying", from);
572286d8edStholo if (fstat (fdin, &sb) < 0)
582286d8edStholo error (1, errno, "cannot fstat %s", from);
592286d8edStholo if ((fdout = open (to, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
602286d8edStholo (int) sb.st_mode & 07777)) < 0)
612286d8edStholo error (1, errno, "cannot create %s for copying", to);
622286d8edStholo if (sb.st_size > 0)
632286d8edStholo {
642286d8edStholo char buf[BUFSIZ];
652286d8edStholo int n;
662286d8edStholo
672286d8edStholo for (;;)
682286d8edStholo {
692286d8edStholo n = read (fdin, buf, sizeof(buf));
702286d8edStholo if (n == -1)
712286d8edStholo {
722286d8edStholo #ifdef EINTR
732286d8edStholo if (errno == EINTR)
742286d8edStholo continue;
752286d8edStholo #endif
762286d8edStholo error (1, errno, "cannot read file %s for copying", from);
772286d8edStholo }
782286d8edStholo else if (n == 0)
792286d8edStholo break;
802286d8edStholo
812286d8edStholo if (write(fdout, buf, n) != n) {
822286d8edStholo error (1, errno, "cannot write file %s for copying", to);
832286d8edStholo }
842286d8edStholo }
852286d8edStholo
862286d8edStholo #ifdef HAVE_FSYNC
872286d8edStholo if (fsync (fdout))
882286d8edStholo error (1, errno, "cannot fsync file %s after copying", to);
892286d8edStholo #endif
902286d8edStholo }
912286d8edStholo
922286d8edStholo if (close (fdin) < 0)
932286d8edStholo error (0, errno, "cannot close %s", from);
942286d8edStholo if (close (fdout) < 0)
952286d8edStholo error (1, errno, "cannot close %s", to);
962286d8edStholo
972286d8edStholo /* now, set the times for the copied file to match those of the original */
982286d8edStholo memset ((char *) &t, 0, sizeof (t));
992286d8edStholo t.actime = sb.st_atime;
1002286d8edStholo t.modtime = sb.st_mtime;
1012286d8edStholo (void) utime (to, &t);
1022286d8edStholo }
1032286d8edStholo
1042286d8edStholo /* FIXME-krp: these functions would benefit from caching the char * &
1052286d8edStholo stat buf. */
1062286d8edStholo
1072286d8edStholo /*
1082286d8edStholo * Returns non-zero if the argument file is a directory, or is a symbolic
1092286d8edStholo * link which points to a directory.
1102286d8edStholo */
1112286d8edStholo int
isdir(file)1122286d8edStholo isdir (file)
1132286d8edStholo const char *file;
1142286d8edStholo {
1152286d8edStholo struct stat sb;
1162286d8edStholo
1172286d8edStholo if (stat (file, &sb) < 0)
1182286d8edStholo return (0);
1192286d8edStholo return (S_ISDIR (sb.st_mode));
1202286d8edStholo }
1212286d8edStholo
1222286d8edStholo /*
1232286d8edStholo * Returns non-zero if the argument file is a symbolic link.
1242286d8edStholo */
1252286d8edStholo int
islink(file)1262286d8edStholo islink (file)
1272286d8edStholo const char *file;
1282286d8edStholo {
1292286d8edStholo #ifdef S_ISLNK
1302286d8edStholo struct stat sb;
1312286d8edStholo
1322286d8edStholo if (lstat (file, &sb) < 0)
1332286d8edStholo return (0);
1342286d8edStholo return (S_ISLNK (sb.st_mode));
1352286d8edStholo #else
1362286d8edStholo return (0);
1372286d8edStholo #endif
1382286d8edStholo }
1392286d8edStholo
1402286d8edStholo /*
1412286d8edStholo * Returns non-zero if the argument file exists.
1422286d8edStholo */
1432286d8edStholo int
isfile(file)1442286d8edStholo isfile (file)
1452286d8edStholo const char *file;
1462286d8edStholo {
1472286d8edStholo return isaccessible(file, F_OK);
1482286d8edStholo }
1492286d8edStholo
1502286d8edStholo /*
1512286d8edStholo * Returns non-zero if the argument file is readable.
1522286d8edStholo */
1532286d8edStholo int
isreadable(file)1542286d8edStholo isreadable (file)
1552286d8edStholo const char *file;
1562286d8edStholo {
1572286d8edStholo return isaccessible(file, R_OK);
1582286d8edStholo }
1592286d8edStholo
1602286d8edStholo /*
1612286d8edStholo * Returns non-zero if the argument file is writable.
1622286d8edStholo */
1632286d8edStholo int
iswritable(file)1642286d8edStholo iswritable (file)
1652286d8edStholo const char *file;
1662286d8edStholo {
1672286d8edStholo return isaccessible(file, W_OK);
1682286d8edStholo }
1692286d8edStholo
1702286d8edStholo /*
1712286d8edStholo * Returns non-zero if the argument file is accessable according to
1722286d8edStholo * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid
1732286d8edStholo * bits set.
1742286d8edStholo */
1752286d8edStholo int
isaccessible(file,mode)1762286d8edStholo isaccessible (file, mode)
1772286d8edStholo const char *file;
1782286d8edStholo const int mode;
1792286d8edStholo {
1802286d8edStholo #ifdef SETXID_SUPPORT
1812286d8edStholo struct stat sb;
1822286d8edStholo int umask = 0;
1832286d8edStholo int gmask = 0;
1842286d8edStholo int omask = 0;
1852286d8edStholo int uid;
1862286d8edStholo
1872286d8edStholo if (stat(file, &sb) == -1)
1882286d8edStholo return 0;
1892286d8edStholo if (mode == F_OK)
1902286d8edStholo return 1;
1912286d8edStholo
1922286d8edStholo uid = geteuid();
1932286d8edStholo if (uid == 0) /* superuser */
1942286d8edStholo {
1952286d8edStholo if (mode & X_OK)
1962286d8edStholo return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
1972286d8edStholo else
1982286d8edStholo return 1;
1992286d8edStholo }
2002286d8edStholo
2012286d8edStholo if (mode & R_OK)
2022286d8edStholo {
2032286d8edStholo umask |= S_IRUSR;
2042286d8edStholo gmask |= S_IRGRP;
2052286d8edStholo omask |= S_IROTH;
2062286d8edStholo }
2072286d8edStholo if (mode & W_OK)
2082286d8edStholo {
2092286d8edStholo umask |= S_IWUSR;
2102286d8edStholo gmask |= S_IWGRP;
2112286d8edStholo omask |= S_IWOTH;
2122286d8edStholo }
2132286d8edStholo if (mode & X_OK)
2142286d8edStholo {
2152286d8edStholo umask |= S_IXUSR;
2162286d8edStholo gmask |= S_IXGRP;
2172286d8edStholo omask |= S_IXOTH;
2182286d8edStholo }
2192286d8edStholo
2202286d8edStholo if (sb.st_uid == uid)
2212286d8edStholo return (sb.st_mode & umask) == umask;
2222286d8edStholo else if (sb.st_gid == getegid())
2232286d8edStholo return (sb.st_mode & gmask) == gmask;
2242286d8edStholo else
2252286d8edStholo return (sb.st_mode & omask) == omask;
2262286d8edStholo #else
2272286d8edStholo return access(file, mode) == 0;
2282286d8edStholo #endif
2292286d8edStholo }
2302286d8edStholo
2312286d8edStholo /*
2322286d8edStholo * Open a file and die if it fails
2332286d8edStholo */
2342286d8edStholo FILE *
open_file(name,mode)2352286d8edStholo open_file (name, mode)
2362286d8edStholo const char *name;
2372286d8edStholo const char *mode;
2382286d8edStholo {
2392286d8edStholo FILE *fp;
2402286d8edStholo
2412286d8edStholo if ((fp = fopen (name, mode)) == NULL)
2422286d8edStholo error (1, errno, "cannot open %s", name);
2432286d8edStholo return (fp);
2442286d8edStholo }
2452286d8edStholo
2462286d8edStholo /*
2472286d8edStholo * Make a directory and die if it fails
2482286d8edStholo */
2492286d8edStholo void
make_directory(name)2502286d8edStholo make_directory (name)
2512286d8edStholo const char *name;
2522286d8edStholo {
2532286d8edStholo struct stat sb;
2542286d8edStholo
2552286d8edStholo if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
2562286d8edStholo error (0, 0, "%s already exists but is not a directory", name);
2572286d8edStholo if (!noexec && mkdir (name, 0777) < 0)
2582286d8edStholo error (1, errno, "cannot make directory %s", name);
2592286d8edStholo }
2602286d8edStholo
2612286d8edStholo /*
2622286d8edStholo * Make a path to the argument directory, printing a message if something
2632286d8edStholo * goes wrong.
2642286d8edStholo */
2652286d8edStholo void
make_directories(name)2662286d8edStholo make_directories (name)
2672286d8edStholo const char *name;
2682286d8edStholo {
2692286d8edStholo char *cp;
2702286d8edStholo
2712286d8edStholo if (noexec)
2722286d8edStholo return;
2732286d8edStholo
2742286d8edStholo if (mkdir (name, 0777) == 0 || errno == EEXIST)
2752286d8edStholo return;
2762286d8edStholo if (! existence_error (errno))
2772286d8edStholo {
2782286d8edStholo error (0, errno, "cannot make path to %s", name);
2792286d8edStholo return;
2802286d8edStholo }
2812286d8edStholo if ((cp = strrchr (name, '/')) == NULL)
2822286d8edStholo return;
2832286d8edStholo *cp = '\0';
2842286d8edStholo make_directories (name);
2852286d8edStholo *cp++ = '/';
2862286d8edStholo if (*cp == '\0')
2872286d8edStholo return;
2882286d8edStholo (void) mkdir (name, 0777);
2892286d8edStholo }
2902286d8edStholo
2912286d8edStholo /* Create directory NAME if it does not already exist; fatal error for
2922286d8edStholo other errors. Returns 0 if directory was created; 1 if it already
2932286d8edStholo existed. */
2942286d8edStholo int
mkdir_if_needed(name)2952286d8edStholo mkdir_if_needed (name)
2962286d8edStholo char *name;
2972286d8edStholo {
2982286d8edStholo if (mkdir (name, 0777) < 0)
2992286d8edStholo {
3002286d8edStholo if (errno != EEXIST)
3012286d8edStholo error (1, errno, "cannot make directory %s", name);
3022286d8edStholo return 1;
3032286d8edStholo }
3042286d8edStholo return 0;
3052286d8edStholo }
3062286d8edStholo
3072286d8edStholo /*
3082286d8edStholo * Change the mode of a file, either adding write permissions, or removing
3092286d8edStholo * all write permissions. Either change honors the current umask setting.
3102286d8edStholo * The EMX doc (0.9c, emxlib.doc) says that chmod sets/clears the readonly
3112286d8edStholo * bit. But it always seemed to be a noop when I tried it. Therefore,
3122286d8edStholo * I've copied over the "attrib" code from os2/filesubr.c.
3132286d8edStholo */
3142286d8edStholo void
xchmod(fname,writable)3152286d8edStholo xchmod (fname, writable)
3162286d8edStholo char *fname;
3172286d8edStholo int writable;
3182286d8edStholo {
3192286d8edStholo char *attrib_cmd;
3202286d8edStholo char *attrib_option;
3212286d8edStholo char *whole_cmd;
3222286d8edStholo char *p;
3232286d8edStholo char *q;
3242286d8edStholo
3252286d8edStholo if (!isfile (fname))
3262286d8edStholo {
3272286d8edStholo error (0, 0, "cannot change mode of file %s; it does not exist",
3282286d8edStholo fname);
3292286d8edStholo return;
3302286d8edStholo }
3312286d8edStholo
3322286d8edStholo attrib_cmd = "attrib "; /* No, really? */
3332286d8edStholo
3342286d8edStholo if (writable)
3352286d8edStholo attrib_option = "-r "; /* make writeable */
3362286d8edStholo else
3372286d8edStholo attrib_option = "+r "; /* make read-only */
3382286d8edStholo
3392286d8edStholo whole_cmd = xmalloc (strlen (attrib_cmd)
3402286d8edStholo + strlen (attrib_option)
3412286d8edStholo + strlen (fname)
3422286d8edStholo + 1);
3432286d8edStholo
3442286d8edStholo strcpy (whole_cmd, attrib_cmd);
3452286d8edStholo strcat (whole_cmd, attrib_option);
3462286d8edStholo
3472286d8edStholo /* Copy fname to the end of whole_cmd, translating / to \.
3482286d8edStholo Attrib doesn't take / but many parts of CVS rely
3492286d8edStholo on being able to use it. */
3502286d8edStholo p = whole_cmd + strlen (whole_cmd);
3512286d8edStholo q = fname;
3522286d8edStholo while (*q)
3532286d8edStholo {
3542286d8edStholo if (*q == '/')
3552286d8edStholo *p++ = '\\';
3562286d8edStholo else
3572286d8edStholo *p++ = *q;
3582286d8edStholo ++q;
3592286d8edStholo }
3602286d8edStholo *p = '\0';
3612286d8edStholo
3622286d8edStholo system (whole_cmd);
3632286d8edStholo free (whole_cmd);
3642286d8edStholo }
3652286d8edStholo
3662286d8edStholo /*
3672286d8edStholo * Rename a file and die if it fails
3682286d8edStholo */
3692286d8edStholo void
rename_file(from,to)3702286d8edStholo rename_file (from, to)
3712286d8edStholo const char *from;
3722286d8edStholo const char *to;
3732286d8edStholo {
3742286d8edStholo if (trace)
3752286d8edStholo #ifdef SERVER_SUPPORT
3762286d8edStholo (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
3772286d8edStholo (server_active) ? 'S' : ' ', from, to);
3782286d8edStholo #else
3792286d8edStholo (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
3802286d8edStholo #endif
3812286d8edStholo if (noexec)
3822286d8edStholo return;
3832286d8edStholo
3842286d8edStholo unlink_file (to);
3852286d8edStholo if (rename (from, to) != 0)
3862286d8edStholo error (1, errno, "cannot rename file %s to %s", from, to);
3872286d8edStholo }
3882286d8edStholo
3892286d8edStholo /*
3902286d8edStholo * unlink a file, if possible.
3912286d8edStholo */
3922286d8edStholo int
unlink_file(f)3932286d8edStholo unlink_file (f)
3942286d8edStholo const char *f;
3952286d8edStholo {
3962286d8edStholo if (trace)
3972286d8edStholo #ifdef SERVER_SUPPORT
3982286d8edStholo (void) fprintf (stderr, "%c-> unlink(%s)\n",
3992286d8edStholo (server_active) ? 'S' : ' ', f);
4002286d8edStholo #else
4012286d8edStholo (void) fprintf (stderr, "-> unlink(%s)\n", f);
4022286d8edStholo #endif
4032286d8edStholo if (noexec)
4042286d8edStholo return (0);
4052286d8edStholo
4062286d8edStholo if (isfile (f))
4072286d8edStholo xchmod ((char *)f, 1);
4082286d8edStholo return (unlink (f));
4092286d8edStholo }
4102286d8edStholo
4112286d8edStholo /*
4122286d8edStholo * Unlink a file or dir, if possible. If it is a directory do a deep
4132286d8edStholo * removal of all of the files in the directory. Return -1 on error
4142286d8edStholo * (in which case errno is set).
4152286d8edStholo */
4162286d8edStholo int
unlink_file_dir(f)4172286d8edStholo unlink_file_dir (f)
4182286d8edStholo const char *f;
4192286d8edStholo {
4202286d8edStholo if (trace)
4212286d8edStholo #ifdef SERVER_SUPPORT
4222286d8edStholo (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
4232286d8edStholo (server_active) ? 'S' : ' ', f);
4242286d8edStholo #else
4252286d8edStholo (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
4262286d8edStholo #endif
4272286d8edStholo if (noexec)
4282286d8edStholo return (0);
4292286d8edStholo
4302286d8edStholo /* For at least some unices, if root tries to unlink() a directory,
4312286d8edStholo instead of doing something rational like returning EISDIR,
4322286d8edStholo the system will gleefully go ahead and corrupt the filesystem.
4332286d8edStholo So we first call isdir() to see if it is OK to call unlink(). This
4342286d8edStholo doesn't quite work--if someone creates a directory between the
4352286d8edStholo call to isdir() and the call to unlink(), we'll still corrupt
4362286d8edStholo the filesystem. Where is the Unix Haters Handbook when you need
4372286d8edStholo it? */
4382286d8edStholo if (isdir(f))
4392286d8edStholo return deep_remove_dir(f);
4402286d8edStholo else
4412286d8edStholo {
4422286d8edStholo if (unlink (f) != 0)
4432286d8edStholo return -1;
4442286d8edStholo }
4452286d8edStholo /* We were able to remove the file from the disk */
4462286d8edStholo return 0;
4472286d8edStholo }
4482286d8edStholo
4492286d8edStholo /* Remove a directory and everything it contains. Returns 0 for
4502286d8edStholo * success, -1 for failure (in which case errno is set).
4512286d8edStholo */
4522286d8edStholo
4532286d8edStholo static int
deep_remove_dir(path)4542286d8edStholo deep_remove_dir (path)
4552286d8edStholo const char *path;
4562286d8edStholo {
4572286d8edStholo DIR *dirp;
4582286d8edStholo struct dirent *dp;
4592286d8edStholo char buf[PATH_MAX];
4602286d8edStholo
4612286d8edStholo if (rmdir (path) != 0)
4622286d8edStholo {
4632286d8edStholo if (errno == ENOTEMPTY
4642286d8edStholo || errno == EEXIST
4652286d8edStholo /* Ugly workaround for ugly AIX 4.1 (and 3.2) header bug
4662286d8edStholo (it defines ENOTEMPTY and EEXIST to 17 but actually
4672286d8edStholo returns 87). */
4682286d8edStholo || (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87))
4692286d8edStholo {
4702286d8edStholo if ((dirp = opendir (path)) == NULL)
4712286d8edStholo /* If unable to open the directory return
4722286d8edStholo * an error
4732286d8edStholo */
4742286d8edStholo return -1;
4752286d8edStholo
4762286d8edStholo while ((dp = readdir (dirp)) != NULL)
4772286d8edStholo {
4782286d8edStholo if (strcmp (dp->d_name, ".") == 0 ||
4792286d8edStholo strcmp (dp->d_name, "..") == 0)
4802286d8edStholo continue;
4812286d8edStholo
4822286d8edStholo sprintf (buf, "%s/%s", path, dp->d_name);
4832286d8edStholo
4842286d8edStholo /* See comment in unlink_file_dir explanation of why we use
4852286d8edStholo isdir instead of just calling unlink and checking the
4862286d8edStholo status. */
4872286d8edStholo if (isdir(buf))
4882286d8edStholo {
4892286d8edStholo if (deep_remove_dir(buf))
4902286d8edStholo {
4912286d8edStholo closedir(dirp);
4922286d8edStholo return -1;
4932286d8edStholo }
4942286d8edStholo }
4952286d8edStholo else
4962286d8edStholo {
4972286d8edStholo if (unlink (buf) != 0)
4982286d8edStholo {
4992286d8edStholo closedir(dirp);
5002286d8edStholo return -1;
5012286d8edStholo }
5022286d8edStholo }
5032286d8edStholo }
5042286d8edStholo closedir (dirp);
5052286d8edStholo return rmdir (path);
5062286d8edStholo }
5072286d8edStholo else
5082286d8edStholo return -1;
5092286d8edStholo }
5102286d8edStholo
5112286d8edStholo /* Was able to remove the directory return 0 */
5122286d8edStholo return 0;
5132286d8edStholo }
5142286d8edStholo
5152286d8edStholo /* Read NCHARS bytes from descriptor FD into BUF.
5162286d8edStholo Return the number of characters successfully read.
5172286d8edStholo The number returned is always NCHARS unless end-of-file or error. */
5182286d8edStholo static size_t
block_read(fd,buf,nchars)5192286d8edStholo block_read (fd, buf, nchars)
5202286d8edStholo int fd;
5212286d8edStholo char *buf;
5222286d8edStholo size_t nchars;
5232286d8edStholo {
5242286d8edStholo char *bp = buf;
5252286d8edStholo size_t nread;
5262286d8edStholo
5272286d8edStholo do
5282286d8edStholo {
5292286d8edStholo nread = read (fd, bp, nchars);
5302286d8edStholo if (nread == (size_t)-1)
5312286d8edStholo {
5322286d8edStholo #ifdef EINTR
5332286d8edStholo if (errno == EINTR)
5342286d8edStholo continue;
5352286d8edStholo #endif
5362286d8edStholo return (size_t)-1;
5372286d8edStholo }
5382286d8edStholo
5392286d8edStholo if (nread == 0)
5402286d8edStholo break;
5412286d8edStholo
5422286d8edStholo bp += nread;
5432286d8edStholo nchars -= nread;
5442286d8edStholo } while (nchars != 0);
5452286d8edStholo
5462286d8edStholo return bp - buf;
5472286d8edStholo }
5482286d8edStholo
5492286d8edStholo
5502286d8edStholo /*
5512286d8edStholo * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
5522286d8edStholo */
5532286d8edStholo int
xcmp(file1,file2)5542286d8edStholo xcmp (file1, file2)
5552286d8edStholo const char *file1;
5562286d8edStholo const char *file2;
5572286d8edStholo {
5582286d8edStholo char *buf1, *buf2;
5592286d8edStholo struct stat sb1, sb2;
5602286d8edStholo int fd1, fd2;
5612286d8edStholo int ret;
5622286d8edStholo
5632286d8edStholo if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
5642286d8edStholo error (1, errno, "cannot open file %s for comparing", file1);
5652286d8edStholo if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
5662286d8edStholo error (1, errno, "cannot open file %s for comparing", file2);
5672286d8edStholo if (fstat (fd1, &sb1) < 0)
5682286d8edStholo error (1, errno, "cannot fstat %s", file1);
5692286d8edStholo if (fstat (fd2, &sb2) < 0)
5702286d8edStholo error (1, errno, "cannot fstat %s", file2);
5712286d8edStholo
5722286d8edStholo /* A generic file compare routine might compare st_dev & st_ino here
5732286d8edStholo to see if the two files being compared are actually the same file.
5742286d8edStholo But that won't happen in CVS, so we won't bother. */
5752286d8edStholo
5762286d8edStholo if (sb1.st_size != sb2.st_size)
5772286d8edStholo ret = 1;
5782286d8edStholo else if (sb1.st_size == 0)
5792286d8edStholo ret = 0;
5802286d8edStholo else
5812286d8edStholo {
5822286d8edStholo /* FIXME: compute the optimal buffer size by computing the least
5832286d8edStholo common multiple of the files st_blocks field */
5842286d8edStholo size_t buf_size = 8 * 1024;
5852286d8edStholo size_t read1;
5862286d8edStholo size_t read2;
5872286d8edStholo
5882286d8edStholo buf1 = xmalloc (buf_size);
5892286d8edStholo buf2 = xmalloc (buf_size);
5902286d8edStholo
5912286d8edStholo do
5922286d8edStholo {
5932286d8edStholo read1 = block_read (fd1, buf1, buf_size);
5942286d8edStholo if (read1 == (size_t)-1)
5952286d8edStholo error (1, errno, "cannot read file %s for comparing", file1);
5962286d8edStholo
5972286d8edStholo read2 = block_read (fd2, buf2, buf_size);
5982286d8edStholo if (read2 == (size_t)-1)
5992286d8edStholo error (1, errno, "cannot read file %s for comparing", file2);
6002286d8edStholo
6012286d8edStholo /* assert (read1 == read2); */
6022286d8edStholo
6032286d8edStholo ret = memcmp(buf1, buf2, read1);
6042286d8edStholo } while (ret == 0 && read1 == buf_size);
6052286d8edStholo
6062286d8edStholo free (buf1);
6072286d8edStholo free (buf2);
6082286d8edStholo }
6092286d8edStholo
6102286d8edStholo (void) close (fd1);
6112286d8edStholo (void) close (fd2);
6122286d8edStholo return (ret);
6132286d8edStholo }
6142286d8edStholo
6152286d8edStholo
6162286d8edStholo /* Just in case this implementation does not define this. */
6172286d8edStholo #ifndef L_tmpnam
6182286d8edStholo #define L_tmpnam 50
6192286d8edStholo #endif
6202286d8edStholo
6212286d8edStholo
6222286d8edStholo #ifdef LOSING_TMPNAM_FUNCTION
6232286d8edStholo char *
cvs_temp_name()6242286d8edStholo cvs_temp_name ()
6252286d8edStholo {
6262286d8edStholo char value[L_tmpnam + 1];
6272286d8edStholo
6282286d8edStholo /* FIXME: Should be using TMPDIR. */
6292286d8edStholo strcpy (value, "/tmp/cvsXXXXXX");
6302286d8edStholo mktemp (value);
6312286d8edStholo return xstrdup (value);
6322286d8edStholo }
6332286d8edStholo #else
6342286d8edStholo /* Generate a unique temporary filename. Returns a pointer to a newly
6352286d8edStholo malloc'd string containing the name. Returns successfully or not at
6362286d8edStholo all. */
6372286d8edStholo char *
cvs_temp_name()6382286d8edStholo cvs_temp_name ()
6392286d8edStholo {
6402286d8edStholo char value[L_tmpnam + 1];
6412286d8edStholo char *retval;
6422286d8edStholo
6432286d8edStholo /* FIXME: should be using TMPDIR, perhaps by using tempnam on systems
6442286d8edStholo which have it. */
6452286d8edStholo retval = tmpnam (value);
6462286d8edStholo if (retval == NULL)
6472286d8edStholo error (1, errno, "cannot generate temporary filename");
6482286d8edStholo return xstrdup (retval);
6492286d8edStholo }
6502286d8edStholo #endif
6512286d8edStholo
6522286d8edStholo
6532286d8edStholo /* Return non-zero iff FILENAME is absolute.
6542286d8edStholo Trivial under Unix, but more complicated under other systems.
6552286d8edStholo Under EMX let _fnisabs do all this work. */
6562286d8edStholo int
isabsolute(filename)6572286d8edStholo isabsolute (filename)
6582286d8edStholo const char *filename;
6592286d8edStholo {
6602286d8edStholo return _fnisabs(filename);
6612286d8edStholo }
6622286d8edStholo
6632286d8edStholo
6642286d8edStholo /* Return a pointer into PATH's last component. */
6652286d8edStholo char *
last_component(path)6662286d8edStholo last_component (path)
6672286d8edStholo char *path;
6682286d8edStholo {
6692286d8edStholo char *last;
6702286d8edStholo
6712286d8edStholo /* We can't be sure here if 'path' is already slashified. */
6722286d8edStholo _fnslashify (path);
6732286d8edStholo
6742286d8edStholo last = strrchr (path, '/');
6752286d8edStholo
676*5e617892Stholo if (last && (last != path))
6772286d8edStholo return last + 1;
6782286d8edStholo else
6792286d8edStholo return path;
6802286d8edStholo }
6812286d8edStholo
6822286d8edStholo /* Return the home directory. Returns a pointer to storage
6832286d8edStholo managed by this function or its callees (currently getenv).
6842286d8edStholo This function will return the same thing every time it is
6852286d8edStholo called. */
6862286d8edStholo char *
get_homedir()6872286d8edStholo get_homedir ()
6882286d8edStholo {
6892286d8edStholo static char *home = NULL;
6902286d8edStholo char *env = getenv ("HOME");
6912286d8edStholo struct passwd *pw;
6922286d8edStholo
6932286d8edStholo if (home != NULL)
6942286d8edStholo return home;
6952286d8edStholo
6962286d8edStholo if (env)
6972286d8edStholo home = env;
6982286d8edStholo else if ((pw = (struct passwd *) getpwuid (getuid ()))
6992286d8edStholo && pw->pw_dir)
7002286d8edStholo home = xstrdup (pw->pw_dir);
7012286d8edStholo else
7022286d8edStholo return 0;
7032286d8edStholo
7042286d8edStholo return home;
7052286d8edStholo }
7062286d8edStholo
7072286d8edStholo /* See cvs.h for description. On unix this does nothing, because the
7082286d8edStholo shell expands the wildcards. Under EMX, use _fnexplode to get the
7092286d8edStholo expanded filenames */
7102286d8edStholo void
expand_wild(argc,argv,pargc,pargv)7112286d8edStholo expand_wild (argc, argv, pargc, pargv)
7122286d8edStholo int argc;
7132286d8edStholo char **argv;
7142286d8edStholo int *pargc;
7152286d8edStholo char ***pargv;
7162286d8edStholo {
7172286d8edStholo int i;
7182286d8edStholo *pargc = argc;
7192286d8edStholo *pargv = (char **) xmalloc (argc * sizeof (char *));
7202286d8edStholo for (i = 0; i < argc; ++i)
7212286d8edStholo (*pargv)[i] = xstrdup (argv[i]);
7222286d8edStholo }
7232286d8edStholo
7242286d8edStholo unsigned char
7252286d8edStholo OS2_filename_classes[] =
7262286d8edStholo {
7272286d8edStholo 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
7282286d8edStholo 0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
7292286d8edStholo 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
7302286d8edStholo 0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
7312286d8edStholo 0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
7322286d8edStholo 0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
7332286d8edStholo 0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
7342286d8edStholo 0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
7352286d8edStholo 0x40,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
7362286d8edStholo 0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
7372286d8edStholo 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
7382286d8edStholo 0x78,0x79,0x7a,0x5b, 0x2f,0x5d,0x5e,0x5f,
7392286d8edStholo 0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
7402286d8edStholo 0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
7412286d8edStholo 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
7422286d8edStholo 0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f,
7432286d8edStholo 0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
7442286d8edStholo 0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
7452286d8edStholo 0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
7462286d8edStholo 0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f,
7472286d8edStholo 0xa0,0xa1,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,
7482286d8edStholo 0xa8,0xa9,0xaa,0xab, 0xac,0xad,0xae,0xaf,
7492286d8edStholo 0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
7502286d8edStholo 0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
7512286d8edStholo 0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
7522286d8edStholo 0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
7532286d8edStholo 0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
7542286d8edStholo 0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
7552286d8edStholo 0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
7562286d8edStholo 0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
7572286d8edStholo 0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
7582286d8edStholo 0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
7592286d8edStholo };
7602286d8edStholo
7612286d8edStholo
7622286d8edStholo /* Like strcmp, but with the appropriate tweaks for file names.
7632286d8edStholo Under OS/2, filenames are case-insensitive but case-preserving, and
7642286d8edStholo both \ and / are path element separators. */
7652286d8edStholo int
fncmp(const char * n1,const char * n2)7662286d8edStholo fncmp (const char *n1, const char *n2)
7672286d8edStholo {
7682286d8edStholo char fn1[MAXNAMLEN], fn2[MAXNAMLEN];
7692286d8edStholo
7702286d8edStholo strcpy (fn1, n1); _fnslashify(fn1);
7712286d8edStholo strcpy (fn2, n2); _fnslashify(fn2);
7722286d8edStholo
7732286d8edStholo return _fncmp ((unsigned char *) fn1, (unsigned char *) fn2);
7742286d8edStholo }
7752286d8edStholo
7762286d8edStholo
7772286d8edStholo /* Fold characters in FILENAME to their canonical forms.
7782286d8edStholo If FOLD_FN_CHAR is not #defined, the system provides a default
7792286d8edStholo definition for this. */
7802286d8edStholo void
fnfold(char * filename)7812286d8edStholo fnfold (char *filename)
7822286d8edStholo {
7832286d8edStholo while (*filename)
7842286d8edStholo {
7852286d8edStholo *filename = FOLD_FN_CHAR (*filename);
7862286d8edStholo filename++;
7872286d8edStholo }
7882286d8edStholo }
789