xref: /openbsd-src/gnu/usr.bin/cvs/emx/filesubr.c (revision b6f6614ecf345cfa15dd59c3c624b2ef31ac6aa3)
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