xref: /openbsd-src/gnu/usr.bin/cvs/os2/filesubr.c (revision b6f6614ecf345cfa15dd59c3c624b2ef31ac6aa3)
113571821Stholo /* filesubr.c --- subroutines for dealing with files under OS/2
213571821Stholo    Jim Blandy <jimb@cyclic.com> and Karl Fogel <kfogel@cyclic.com>
313571821Stholo 
413571821Stholo    This file is part of GNU CVS.
513571821Stholo 
613571821Stholo    GNU CVS is free software; you can redistribute it and/or modify it
713571821Stholo    under the terms of the GNU General Public License as published by the
813571821Stholo    Free Software Foundation; either version 2, or (at your option) any
913571821Stholo    later version.
1013571821Stholo 
1113571821Stholo    This program is distributed in the hope that it will be useful,
1213571821Stholo    but WITHOUT ANY WARRANTY; without even the implied warranty of
1313571821Stholo    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14461cc63eStholo    GNU General Public License for more details.  */
1513571821Stholo 
1613571821Stholo /* These functions were moved out of subr.c because they need different
1713571821Stholo    definitions under operating systems (like, say, Windows NT) with different
1813571821Stholo    file system semantics.  */
1913571821Stholo 
2013571821Stholo #include <io.h>
2113571821Stholo 
22461cc63eStholo #include "os2inc.h"
2313571821Stholo #include "cvs.h"
2413571821Stholo 
2513571821Stholo static int deep_remove_dir PROTO((const char *path));
2613571821Stholo 
2713571821Stholo /*
2813571821Stholo  * Copies "from" to "to".
2913571821Stholo  */
3013571821Stholo void
copy_file(from,to)3113571821Stholo copy_file (from, to)
3213571821Stholo     const char *from;
3313571821Stholo     const char *to;
3413571821Stholo {
3513571821Stholo     struct stat sb;
3613571821Stholo     struct utimbuf t;
3713571821Stholo     int fdin, fdout;
3813571821Stholo 
3913571821Stholo     if (trace)
4013571821Stholo #ifdef SERVER_SUPPORT
4113571821Stholo 	(void) fprintf (stderr, "%c-> copy(%s,%s)\n",
4213571821Stholo 			(server_active) ? 'S' : ' ', from, to);
4313571821Stholo #else
4413571821Stholo 	(void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
4513571821Stholo #endif
4613571821Stholo     if (noexec)
4713571821Stholo 	return;
4813571821Stholo 
4913571821Stholo     if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
5013571821Stholo 	error (1, errno, "cannot open %s for copying", from);
5113571821Stholo     if (fstat (fdin, &sb) < 0)
5213571821Stholo 	error (1, errno, "cannot fstat %s", from);
5313571821Stholo     if ((fdout = open (to, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
5413571821Stholo 					   (int) sb.st_mode & 07777)) < 0)
5513571821Stholo 	error (1, errno, "cannot create %s for copying", to);
5613571821Stholo     if (sb.st_size > 0)
5713571821Stholo     {
5813571821Stholo 	char buf[BUFSIZ];
5913571821Stholo 	int n;
6013571821Stholo 
6113571821Stholo 	for (;;)
6213571821Stholo 	{
6313571821Stholo 	    n = read (fdin, buf, sizeof(buf));
6413571821Stholo 	    if (n == -1)
6513571821Stholo 	    {
6613571821Stholo #ifdef EINTR
6713571821Stholo 		if (errno == EINTR)
6813571821Stholo 		    continue;
6913571821Stholo #endif
7013571821Stholo 		error (1, errno, "cannot read file %s for copying", from);
7113571821Stholo 	    }
7213571821Stholo             else if (n == 0)
7313571821Stholo 		break;
7413571821Stholo 
7513571821Stholo 	    if (write(fdout, buf, n) != n) {
7613571821Stholo 		error (1, errno, "cannot write file %s for copying", to);
7713571821Stholo 	    }
7813571821Stholo 	}
7913571821Stholo 
8013571821Stholo #ifdef HAVE_FSYNC
8113571821Stholo 	if (fsync (fdout))
8213571821Stholo 	    error (1, errno, "cannot fsync file %s after copying", to);
8313571821Stholo #endif
8413571821Stholo     }
8513571821Stholo 
8613571821Stholo     if (close (fdin) < 0)
8713571821Stholo 	error (0, errno, "cannot close %s", from);
8813571821Stholo     if (close (fdout) < 0)
8913571821Stholo 	error (1, errno, "cannot close %s", to);
9013571821Stholo 
9113571821Stholo     /* now, set the times for the copied file to match those of the original */
9213571821Stholo     memset ((char *) &t, 0, sizeof (t));
9313571821Stholo     t.actime = sb.st_atime;
9413571821Stholo     t.modtime = sb.st_mtime;
95461cc63eStholo     (void) utime ((char *)to, &t);
9613571821Stholo }
9713571821Stholo 
9813571821Stholo /* FIXME-krp: these functions would benefit from caching the char * &
9913571821Stholo    stat buf.  */
10013571821Stholo 
10113571821Stholo /*
10213571821Stholo  * Returns non-zero if the argument file is a directory, or is a symbolic
10313571821Stholo  * link which points to a directory.
10413571821Stholo  */
10513571821Stholo int
isdir(file)10613571821Stholo isdir (file)
10713571821Stholo     const char *file;
10813571821Stholo {
10913571821Stholo     struct stat sb;
11013571821Stholo 
11113571821Stholo     if (stat (file, &sb) < 0)
11213571821Stholo 	return (0);
11313571821Stholo     return (S_ISDIR (sb.st_mode));
11413571821Stholo }
11513571821Stholo 
11613571821Stholo /*
11713571821Stholo  * Returns non-zero if the argument file is a symbolic link.
11813571821Stholo  */
11913571821Stholo int
islink(file)12013571821Stholo islink (file)
12113571821Stholo     const char *file;
12213571821Stholo {
12313571821Stholo #ifdef S_ISLNK
12413571821Stholo     struct stat sb;
12513571821Stholo 
12613571821Stholo     if (lstat (file, &sb) < 0)
12713571821Stholo 	return (0);
12813571821Stholo     return (S_ISLNK (sb.st_mode));
12913571821Stholo #else
13013571821Stholo     return (0);
13113571821Stholo #endif
13213571821Stholo }
13313571821Stholo 
13413571821Stholo /*
13513571821Stholo  * Returns non-zero if the argument file exists.
13613571821Stholo  */
13713571821Stholo int
isfile(file)13813571821Stholo isfile (file)
13913571821Stholo     const char *file;
14013571821Stholo {
14113571821Stholo     struct stat sb;
14213571821Stholo 
14313571821Stholo     if (stat (file, &sb) < 0)
14413571821Stholo 	return (0);
14513571821Stholo     return (1);
14613571821Stholo }
14713571821Stholo 
14813571821Stholo /*
14913571821Stholo  * Returns non-zero if the argument file is readable.
15013571821Stholo  * XXX - must be careful if "cvs" is ever made setuid!
15113571821Stholo  */
15213571821Stholo int
isreadable(file)15313571821Stholo isreadable (file)
15413571821Stholo     const char *file;
15513571821Stholo {
15613571821Stholo     return (access (file, R_OK) != -1);
15713571821Stholo }
15813571821Stholo 
15913571821Stholo /*
16013571821Stholo  * Returns non-zero if the argument file is writable
16113571821Stholo  * XXX - muct be careful if "cvs" is ever made setuid!
16213571821Stholo  */
16313571821Stholo int
iswritable(file)16413571821Stholo iswritable (file)
16513571821Stholo     const char *file;
16613571821Stholo {
16713571821Stholo     return (access (file, W_OK) != -1);
16813571821Stholo }
16913571821Stholo 
17013571821Stholo /*
17113571821Stholo  * Returns non-zero if the argument file is accessable according to
17213571821Stholo  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
17313571821Stholo  * bits set.
17413571821Stholo  */
17513571821Stholo int
isaccessible(file,mode)17613571821Stholo isaccessible (file, mode)
17713571821Stholo     const char *file;
17813571821Stholo     const int mode;
17913571821Stholo {
18013571821Stholo     return access(file, mode) == 0;
18113571821Stholo }
18213571821Stholo 
18313571821Stholo 
18413571821Stholo /*
18513571821Stholo  * Open a file and die if it fails
18613571821Stholo  */
18713571821Stholo FILE *
open_file(name,mode)18813571821Stholo open_file (name, mode)
18913571821Stholo     const char *name;
19013571821Stholo     const char *mode;
19113571821Stholo {
19213571821Stholo     FILE *fp;
19313571821Stholo 
19413571821Stholo     if ((fp = fopen (name, mode)) == NULL)
19513571821Stholo 	error (1, errno, "cannot open %s", name);
19613571821Stholo     return (fp);
19713571821Stholo }
19813571821Stholo 
19913571821Stholo /*
20013571821Stholo  * Make a directory and die if it fails
20113571821Stholo  */
20213571821Stholo void
make_directory(name)20313571821Stholo make_directory (name)
20413571821Stholo     const char *name;
20513571821Stholo {
20613571821Stholo     struct stat buf;
20713571821Stholo 
20813571821Stholo     if (stat (name, &buf) == 0 && (!S_ISDIR (buf.st_mode)))
20913571821Stholo 	    error (0, 0, "%s already exists but is not a directory", name);
210461cc63eStholo     if (!noexec && mkdir ((char *)name) < 0)
21113571821Stholo 	error (1, errno, "cannot make directory %s", name);
21213571821Stholo }
21313571821Stholo 
21413571821Stholo /*
21513571821Stholo  * Make a path to the argument directory, printing a message if something
21613571821Stholo  * goes wrong.
21713571821Stholo  */
21813571821Stholo void
make_directories(name)21913571821Stholo make_directories (name)
22013571821Stholo     const char *name;
22113571821Stholo {
22213571821Stholo     char *cp;
22313571821Stholo 
22413571821Stholo     if (noexec)
22513571821Stholo 	return;
22613571821Stholo 
227461cc63eStholo     if (mkdir ((char *)name) == 0 || errno == EACCES)
22813571821Stholo 	return;
22913571821Stholo     if (! existence_error (errno))
23013571821Stholo     {
23113571821Stholo 	error (0, errno, "cannot make path to %s", name);
23213571821Stholo 	return;
23313571821Stholo     }
23413571821Stholo     if ((cp = strrchr (name, '/')) == NULL)
23513571821Stholo 	return;
23613571821Stholo     *cp = '\0';
23713571821Stholo     make_directories (name);
23813571821Stholo     *cp++ = '/';
23913571821Stholo     if (*cp == '\0')
24013571821Stholo 	return;
241461cc63eStholo     (void) mkdir ((char *)name);
24213571821Stholo }
24313571821Stholo 
24450bf276cStholo /* Create directory NAME if it does not already exist; fatal error for
24550bf276cStholo    other errors.  Returns 0 if directory was created; 1 if it already
24650bf276cStholo    existed.  */
24750bf276cStholo int
mkdir_if_needed(name)24850bf276cStholo mkdir_if_needed (name)
24950bf276cStholo     char *name;
25050bf276cStholo {
25150bf276cStholo     if (mkdir (name) < 0)
25250bf276cStholo     {
25350bf276cStholo 	/* Now, let me get this straight.  In IBM C/C++
25450bf276cStholo 	   under OS/2, the error string for EEXIST is:
25550bf276cStholo 
25650bf276cStholo 	       "The file already exists",
25750bf276cStholo 
258461cc63eStholo            and the error string for EACCES is:
25950bf276cStholo 
26050bf276cStholo 	       "The file or directory specified is read-only".
26150bf276cStholo 
262461cc63eStholo            Nonetheless, mkdir() will set EACCES if the
26350bf276cStholo 	   directory *exists*, according both to the
26450bf276cStholo 	   documentation and its actual behavior.
26550bf276cStholo 
26650bf276cStholo 	   I'm sure that this made sense, to someone,
26750bf276cStholo 	   somewhere, sometime.  Just not me, here, now.  */
26850bf276cStholo 	if (errno != EEXIST
269461cc63eStholo #ifdef EACCES
270461cc63eStholo             && errno != EACCES
27150bf276cStholo #endif
27250bf276cStholo 	    )
27350bf276cStholo 	    error (1, errno, "cannot make directory %s", name);
27450bf276cStholo 	return 1;
27550bf276cStholo     }
27650bf276cStholo     return 0;
27750bf276cStholo }
27850bf276cStholo 
27913571821Stholo /*
28013571821Stholo  * Change the mode of a file, either adding write permissions, or removing
28113571821Stholo  * all write permissions.  Adding write permissions honors the current umask
28213571821Stholo  * setting.
28313571821Stholo  */
28413571821Stholo void
xchmod(fname,writable)28513571821Stholo xchmod (fname, writable)
28613571821Stholo     char *fname;
28713571821Stholo     int writable;
28813571821Stholo {
28913571821Stholo     char *attrib_cmd;
29013571821Stholo     char *attrib_option;
29113571821Stholo     char *whole_cmd;
29213571821Stholo     char *p;
29313571821Stholo     char *q;
29413571821Stholo 
29513571821Stholo     if (!isfile (fname))
296461cc63eStholo     {
297461cc63eStholo 	error (0, 0, "cannot change mode of file %s; it does not exist",
298461cc63eStholo 	       fname);
299461cc63eStholo 	return;
300461cc63eStholo     }
30113571821Stholo 
30213571821Stholo     attrib_cmd = "attrib "; /* No, really? */
30313571821Stholo 
30413571821Stholo     if (writable)
30513571821Stholo         attrib_option = "-r ";  /* make writeable */
30613571821Stholo     else
30713571821Stholo         attrib_option = "+r ";  /* make read-only */
30813571821Stholo 
30913571821Stholo     whole_cmd = xmalloc (strlen (attrib_cmd)
31013571821Stholo                          + strlen (attrib_option)
31113571821Stholo                          + strlen (fname)
31213571821Stholo                          + 1);
31313571821Stholo 
31413571821Stholo     strcpy (whole_cmd, attrib_cmd);
31513571821Stholo     strcat (whole_cmd, attrib_option);
31613571821Stholo 
31713571821Stholo     /* Copy fname to the end of whole_cmd, translating / to \.
31813571821Stholo 	   Attrib doesn't take / but many parts of CVS rely
31913571821Stholo        on being able to use it.  */
32013571821Stholo     p = whole_cmd + strlen (whole_cmd);
32113571821Stholo     q = fname;
32213571821Stholo     while (*q)
32313571821Stholo     {
32413571821Stholo 	if (*q == '/')
32513571821Stholo 	    *p++ = '\\';
32613571821Stholo 	else
32713571821Stholo 	    *p++ = *q;
32813571821Stholo 	++q;
32913571821Stholo     }
33013571821Stholo     *p = '\0';
33113571821Stholo 
33213571821Stholo     system (whole_cmd);
33313571821Stholo     free (whole_cmd);
33413571821Stholo }
33513571821Stholo 
33613571821Stholo 
33713571821Stholo /* Read the value of a symbolic link.
33813571821Stholo    Under OS/2, this function always returns EINVAL.  */
33913571821Stholo int
readlink(char * path,char * buf,int buf_size)34013571821Stholo readlink (char *path, char *buf, int buf_size)
34113571821Stholo {
34213571821Stholo     errno = EINVAL;
34313571821Stholo     return -1;
34413571821Stholo }
34513571821Stholo 
34613571821Stholo /*
34713571821Stholo  * unlink a file, if possible.
34813571821Stholo  */
34913571821Stholo int
unlink_file(f)35013571821Stholo unlink_file (f)
35113571821Stholo     const char *f;
35213571821Stholo {
35313571821Stholo     if (trace)
35413571821Stholo #ifdef SERVER_SUPPORT
35513571821Stholo 	(void) fprintf (stderr, "%c-> unlink(%s)\n",
35613571821Stholo 			(server_active) ? 'S' : ' ', f);
35713571821Stholo #else
35813571821Stholo 	(void) fprintf (stderr, "-> unlink(%s)\n", f);
35913571821Stholo #endif
36013571821Stholo     if (noexec)
36113571821Stholo 	return (0);
36213571821Stholo 
36313571821Stholo    /* Win32 unlink is stupid - it fails if the file is read-only.
36413571821Stholo     * OS/2 is similarly stupid.  It does have a remove() function,
36513571821Stholo     * but the documentation does not make clear why remove() is or
36613571821Stholo     * isn't preferable to unlink().  I'll use unlink() because the
36713571821Stholo     * name is closer to our interface, what the heck.  Also, we know
36813571821Stholo     * unlink()'s error code when trying to remove a directory.
36913571821Stholo     */
370461cc63eStholo     if (isfile (f))
371461cc63eStholo 	xchmod ((char *)f, 1);
37213571821Stholo     return (unlink (f));
37313571821Stholo }
37413571821Stholo 
37513571821Stholo /*
37613571821Stholo  * Unlink a file or dir, if possible.  If it is a directory do a deep
37713571821Stholo  * removal of all of the files in the directory.  Return -1 on error
37813571821Stholo  * (in which case errno is set).
37913571821Stholo  */
38013571821Stholo int
unlink_file_dir(f)38113571821Stholo unlink_file_dir (f)
38213571821Stholo     const char *f;
38313571821Stholo {
38413571821Stholo     if (trace)
38513571821Stholo #ifdef SERVER_SUPPORT
38613571821Stholo 	(void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
38713571821Stholo 			(server_active) ? 'S' : ' ', f);
38813571821Stholo #else
38913571821Stholo 	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
39013571821Stholo #endif
39113571821Stholo     if (noexec)
39213571821Stholo 	return (0);
39313571821Stholo 
39413571821Stholo     if (unlink_file (f) != 0)
39513571821Stholo     {
396461cc63eStholo         /* under OS/2, unlink returns EACCES if the path
39713571821Stholo 	   is a directory.  */
398461cc63eStholo         if (errno == EACCES)
39913571821Stholo                 return deep_remove_dir (f);
40013571821Stholo         else
40113571821Stholo 		/* The file wasn't a directory and some other
40213571821Stholo 		 * error occured
40313571821Stholo 		 */
40413571821Stholo                 return -1;
40513571821Stholo     }
40613571821Stholo     /* We were able to remove the file from the disk */
40713571821Stholo     return 0;
40813571821Stholo }
40913571821Stholo 
41013571821Stholo /* Remove a directory and everything it contains.  Returns 0 for
41113571821Stholo  * success, -1 for failure (in which case errno is set).
41213571821Stholo  */
41313571821Stholo 
41413571821Stholo static int
deep_remove_dir(path)41513571821Stholo deep_remove_dir (path)
41613571821Stholo     const char *path;
41713571821Stholo {
41813571821Stholo     DIR		  *dirp;
41913571821Stholo     struct dirent *dp;
42013571821Stholo     char	   buf[PATH_MAX];
42113571821Stholo 
422461cc63eStholo     if (rmdir ((char *)path) != 0 && errno == EACCES)
42313571821Stholo     {
424461cc63eStholo 	if ((dirp = opendir ((char *)path)) == NULL)
42513571821Stholo 	    /* If unable to open the directory return
42613571821Stholo 	     * an error
42713571821Stholo 	     */
42813571821Stholo 	    return -1;
42913571821Stholo 
43013571821Stholo 	while ((dp = readdir (dirp)) != NULL)
43113571821Stholo 	{
43213571821Stholo 	    if (strcmp (dp->d_name, ".") == 0 ||
43313571821Stholo 			strcmp (dp->d_name, "..") == 0)
43413571821Stholo 		continue;
43513571821Stholo 
43613571821Stholo 	    sprintf (buf, "%s/%s", path, dp->d_name);
43713571821Stholo 
43813571821Stholo 	    if (unlink_file (buf) != 0 )
43913571821Stholo 	    {
44013571821Stholo 		if (errno == EACCES)
44113571821Stholo 		{
44213571821Stholo 		    if (deep_remove_dir (buf))
44313571821Stholo 		    {
44413571821Stholo 			closedir (dirp);
44513571821Stholo 			return -1;
44613571821Stholo 		    }
44713571821Stholo 		}
44813571821Stholo 		else
44913571821Stholo 		{
45013571821Stholo 		    /* buf isn't a directory, or there are
45113571821Stholo 		     * some sort of permision problems
45213571821Stholo 		     */
45313571821Stholo 		    closedir (dirp);
45413571821Stholo 		    return -1;
45513571821Stholo 		}
45613571821Stholo 	    }
45713571821Stholo 	}
45813571821Stholo 	closedir (dirp);
459461cc63eStholo 	return rmdir ((char *)path);
46013571821Stholo     }
46113571821Stholo     /* Was able to remove the directory return 0 */
46213571821Stholo     return 0;
46313571821Stholo }
46413571821Stholo 
46513571821Stholo 
46613571821Stholo /*
46713571821Stholo  * Rename a file and die if it fails
46813571821Stholo  */
46913571821Stholo void
rename_file(from,to)47013571821Stholo rename_file (from, to)
47113571821Stholo     const char *from;
47213571821Stholo     const char *to;
47313571821Stholo {
47413571821Stholo     if (trace)
47513571821Stholo #ifdef SERVER_SUPPORT
47613571821Stholo 	(void) fprintf (stderr, "%c-> rename(%s,%s)\n",
47713571821Stholo 			(server_active) ? 'S' : ' ', from, to);
47813571821Stholo #else
47913571821Stholo 	(void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
48013571821Stholo #endif
48113571821Stholo     if (noexec)
48213571821Stholo 	return;
48313571821Stholo 
48413571821Stholo     unlink_file (to);
48513571821Stholo     if (rename (from, to) != 0)
48613571821Stholo 	error (1, errno, "cannot rename file %s to %s", from, to);
48713571821Stholo }
48813571821Stholo 
48913571821Stholo 
49013571821Stholo /* Read NCHARS bytes from descriptor FD into BUF.
49113571821Stholo    Return the number of characters successfully read.
49213571821Stholo    The number returned is always NCHARS unless end-of-file or error.  */
49313571821Stholo static size_t
block_read(fd,buf,nchars)49413571821Stholo block_read (fd, buf, nchars)
49513571821Stholo     int fd;
49613571821Stholo     char *buf;
49713571821Stholo     size_t nchars;
49813571821Stholo {
49913571821Stholo     char *bp = buf;
50013571821Stholo     size_t nread;
50113571821Stholo 
50213571821Stholo     do
50313571821Stholo     {
50413571821Stholo 	nread = read (fd, bp, nchars);
50513571821Stholo 	if (nread == (size_t)-1)
50613571821Stholo 	{
50713571821Stholo #ifdef EINTR
50813571821Stholo 	    if (errno == EINTR)
50913571821Stholo 		continue;
51013571821Stholo #endif
51113571821Stholo 	    return (size_t)-1;
51213571821Stholo 	}
51313571821Stholo 
51413571821Stholo 	if (nread == 0)
51513571821Stholo 	    break;
51613571821Stholo 
51713571821Stholo 	bp += nread;
51813571821Stholo 	nchars -= nread;
51913571821Stholo     } while (nchars != 0);
52013571821Stholo 
52113571821Stholo     return bp - buf;
52213571821Stholo }
52313571821Stholo 
52413571821Stholo 
52513571821Stholo /*
52613571821Stholo  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
52713571821Stholo  */
52813571821Stholo int
xcmp(file1,file2)52913571821Stholo xcmp (file1, file2)
53013571821Stholo     const char *file1;
53113571821Stholo     const char *file2;
53213571821Stholo {
53313571821Stholo     char *buf1, *buf2;
53413571821Stholo     struct stat sb1, sb2;
53513571821Stholo     int fd1, fd2;
53613571821Stholo     int ret;
53713571821Stholo 
53813571821Stholo     if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
53913571821Stholo 	error (1, errno, "cannot open file %s for comparing", file1);
54013571821Stholo     if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
54113571821Stholo 	error (1, errno, "cannot open file %s for comparing", file2);
54213571821Stholo     if (fstat (fd1, &sb1) < 0)
54313571821Stholo 	error (1, errno, "cannot fstat %s", file1);
54413571821Stholo     if (fstat (fd2, &sb2) < 0)
54513571821Stholo 	error (1, errno, "cannot fstat %s", file2);
54613571821Stholo 
54713571821Stholo     /* A generic file compare routine might compare st_dev & st_ino here
54813571821Stholo        to see if the two files being compared are actually the same file.
54913571821Stholo        But that won't happen in CVS, so we won't bother. */
55013571821Stholo 
55113571821Stholo     if (sb1.st_size != sb2.st_size)
55213571821Stholo 	ret = 1;
55313571821Stholo     else if (sb1.st_size == 0)
55413571821Stholo 	ret = 0;
55513571821Stholo     else
55613571821Stholo     {
55713571821Stholo 	/* FIXME: compute the optimal buffer size by computing the least
55813571821Stholo 	   common multiple of the files st_blocks field */
55913571821Stholo 	size_t buf_size = 8 * 1024;
56013571821Stholo 	size_t read1;
56113571821Stholo 	size_t read2;
56213571821Stholo 
56313571821Stholo 	buf1 = xmalloc (buf_size);
56413571821Stholo 	buf2 = xmalloc (buf_size);
56513571821Stholo 
56613571821Stholo 	do
56713571821Stholo 	{
56813571821Stholo 	    read1 = block_read (fd1, buf1, buf_size);
56913571821Stholo 	    if (read1 == (size_t)-1)
57013571821Stholo 		error (1, errno, "cannot read file %s for comparing", file1);
57113571821Stholo 
57213571821Stholo 	    read2 = block_read (fd2, buf2, buf_size);
57313571821Stholo 	    if (read2 == (size_t)-1)
57413571821Stholo 		error (1, errno, "cannot read file %s for comparing", file2);
57513571821Stholo 
57613571821Stholo 	    /* assert (read1 == read2); */
57713571821Stholo 
57813571821Stholo 	    ret = memcmp(buf1, buf2, read1);
57913571821Stholo 	} while (ret == 0 && read1 == buf_size);
58013571821Stholo 
58113571821Stholo 	free (buf1);
58213571821Stholo 	free (buf2);
58313571821Stholo     }
58413571821Stholo 
58513571821Stholo     (void) close (fd1);
58613571821Stholo     (void) close (fd2);
58713571821Stholo     return (ret);
58813571821Stholo }
58913571821Stholo 
59013571821Stholo 
59113571821Stholo /* The equivalence class mapping for filenames.
59213571821Stholo    OS/2 filenames are case-insensitive, but case-preserving.  Both /
59313571821Stholo    and \ are path element separators.
59413571821Stholo    Thus, this table maps both upper and lower case to lower case, and
59513571821Stholo    both / and \ to /.
59613571821Stholo 
59713571821Stholo    Much thanks to Jim Blandy, who already invented this wheel in the
59813571821Stholo    Windows NT port. */
59913571821Stholo 
60013571821Stholo #if 0
60113571821Stholo main ()
60213571821Stholo {
60313571821Stholo   int c;
60413571821Stholo 
60513571821Stholo   for (c = 0; c < 256; c++)
60613571821Stholo     {
60713571821Stholo       int t;
60813571821Stholo 
60913571821Stholo       if (c == '\\')
61013571821Stholo         t = '/';
61113571821Stholo       else
61213571821Stholo         t = tolower (c);
61313571821Stholo 
61413571821Stholo       if ((c & 0x7) == 0x0)
61513571821Stholo          printf ("    ");
61613571821Stholo       printf ("0x%02x,", t);
61713571821Stholo       if ((c & 0x7) == 0x7)
61813571821Stholo          putchar ('\n');
61913571821Stholo       else if ((c & 0x7) == 0x3)
62013571821Stholo          putchar (' ');
62113571821Stholo     }
62213571821Stholo }
62313571821Stholo #endif
62413571821Stholo 
62513571821Stholo 
62613571821Stholo unsigned char
62713571821Stholo OS2_filename_classes[] =
62813571821Stholo {
62913571821Stholo     0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
63013571821Stholo     0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
63113571821Stholo     0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
63213571821Stholo     0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
63313571821Stholo     0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
63413571821Stholo     0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
63513571821Stholo     0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
63613571821Stholo     0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
63713571821Stholo     0x40,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
63813571821Stholo     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
63913571821Stholo     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
64013571821Stholo     0x78,0x79,0x7a,0x5b, 0x2f,0x5d,0x5e,0x5f,
64113571821Stholo     0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
64213571821Stholo     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
64313571821Stholo     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
64413571821Stholo     0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f,
64513571821Stholo     0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
64613571821Stholo     0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
64713571821Stholo     0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
64813571821Stholo     0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f,
64913571821Stholo     0xa0,0xa1,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,
65013571821Stholo     0xa8,0xa9,0xaa,0xab, 0xac,0xad,0xae,0xaf,
65113571821Stholo     0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
65213571821Stholo     0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
65313571821Stholo     0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
65413571821Stholo     0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
65513571821Stholo     0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
65613571821Stholo     0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
65713571821Stholo     0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
65813571821Stholo     0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
65913571821Stholo     0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
66013571821Stholo     0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
66113571821Stholo };
66213571821Stholo 
66313571821Stholo /* Like strcmp, but with the appropriate tweaks for file names.
66413571821Stholo    Under OS/2, filenames are case-insensitive but case-preserving, and
66513571821Stholo    both \ and / are path element separators.  */
66613571821Stholo int
fncmp(const char * n1,const char * n2)66713571821Stholo fncmp (const char *n1, const char *n2)
66813571821Stholo {
66913571821Stholo     while (*n1 && *n2
67013571821Stholo            && (OS2_filename_classes[(unsigned char) *n1]
67113571821Stholo 	       == OS2_filename_classes[(unsigned char) *n2]))
67213571821Stholo         n1++, n2++;
67313571821Stholo     return (OS2_filename_classes[(unsigned char) *n1]
67450bf276cStholo             - OS2_filename_classes[(unsigned char) *n2]);
67513571821Stholo }
67613571821Stholo 
67713571821Stholo /* Fold characters in FILENAME to their canonical forms.
67813571821Stholo    If FOLD_FN_CHAR is not #defined, the system provides a default
67913571821Stholo    definition for this.  */
68013571821Stholo void
fnfold(char * filename)68113571821Stholo fnfold (char *filename)
68213571821Stholo {
68313571821Stholo     while (*filename)
68413571821Stholo     {
68513571821Stholo         *filename = FOLD_FN_CHAR (*filename);
68613571821Stholo 	filename++;
68713571821Stholo     }
68813571821Stholo }
68913571821Stholo 
69050bf276cStholo 
69150bf276cStholo /* Generate a unique temporary filename.  Returns a pointer to a newly
69250bf276cStholo    malloc'd string containing the name.  Returns successfully or not at
69350bf276cStholo    all.  */
69450bf276cStholo char *
cvs_temp_name()69550bf276cStholo cvs_temp_name ()
69650bf276cStholo {
69750bf276cStholo     char value[L_tmpnam + 1];
69850bf276cStholo     char *retval;
69913571821Stholo 
70050bf276cStholo     /* FIXME: Does OS/2 have some equivalent to TMPDIR?  */
70150bf276cStholo     retval = tmpnam (value);
70250bf276cStholo     if (retval == NULL)
70350bf276cStholo 	error (1, errno, "cannot generate temporary filename");
70450bf276cStholo     return xstrdup (retval);
70550bf276cStholo }
70650bf276cStholo 
70713571821Stholo /* Return non-zero iff FILENAME is absolute.
70813571821Stholo    Trivial under Unix, but more complicated under other systems.  */
70913571821Stholo int
isabsolute(filename)71013571821Stholo isabsolute (filename)
71113571821Stholo     const char *filename;
71213571821Stholo {
71313571821Stholo     return (ISDIRSEP (filename[0])
71413571821Stholo             || (filename[0] != '\0'
71513571821Stholo                 && filename[1] == ':'
71613571821Stholo                 && ISDIRSEP (filename[2])));
71713571821Stholo }
71813571821Stholo 
71913571821Stholo /* Return a pointer into PATH's last component.  */
72013571821Stholo char *
last_component(char * path)72113571821Stholo last_component (char *path)
72213571821Stholo {
72313571821Stholo     char *scan;
72413571821Stholo     char *last = 0;
72513571821Stholo 
72613571821Stholo     for (scan = path; *scan; scan++)
72713571821Stholo         if (ISDIRSEP (*scan))
72813571821Stholo 	    last = scan;
72913571821Stholo 
730*5e617892Stholo     if (last && (last != path))
73113571821Stholo         return last + 1;
73213571821Stholo     else
73313571821Stholo         return path;
73413571821Stholo }
73513571821Stholo 
73613571821Stholo 
737c2c61682Stholo /* Return the home directory.  Returns a pointer to storage
738c2c61682Stholo    managed by this function or its callees (currently getenv).  */
739c2c61682Stholo char *
get_homedir()740c2c61682Stholo get_homedir ()
741c2c61682Stholo {
742c2c61682Stholo     return getenv ("HOME");
743c2c61682Stholo }
744c2c61682Stholo 
74550bf276cStholo /* See cvs.h for description.  */
746c2c61682Stholo void
expand_wild(argc,argv,pargc,pargv)747c2c61682Stholo expand_wild (argc, argv, pargc, pargv)
748c2c61682Stholo     int argc;
749c2c61682Stholo     char **argv;
750c2c61682Stholo     int *pargc;
751c2c61682Stholo     char ***pargv;
752c2c61682Stholo {
753c2c61682Stholo     int i;
75450bf276cStholo     int new_argc;
75550bf276cStholo     char **new_argv;
75650bf276cStholo     /* Allocated size of new_argv.  We arrange it so there is always room for
75750bf276cStholo 	   one more element.  */
75850bf276cStholo     int max_new_argc;
75950bf276cStholo 
76050bf276cStholo     new_argc = 0;
76150bf276cStholo     /* Add one so this is never zero.  */
76250bf276cStholo     max_new_argc = argc + 1;
76350bf276cStholo     new_argv = (char **) xmalloc (max_new_argc * sizeof (char *));
764c2c61682Stholo     for (i = 0; i < argc; ++i)
76550bf276cStholo     {
76650bf276cStholo 	HDIR          FindHandle = 0x0001;
76750bf276cStholo 	FILEFINDBUF3  FindBuffer;
76850bf276cStholo 	ULONG         FindCount = 1;
76950bf276cStholo 	APIRET        rc;          /* Return code */
77050bf276cStholo #define ALL_FILES (FILE_ARCHIVED|FILE_DIRECTORY|FILE_SYSTEM|FILE_HIDDEN|FILE_READONLY)
77150bf276cStholo 
772461cc63eStholo 	/* DosFindFirst, called with a string like 'dir/file' will return
773461cc63eStholo 	 * *only* the file part. So what we have to do here is to save the
774461cc63eStholo 	 * directory part, and add it later to the returned filename.
775461cc63eStholo 	 */
776461cc63eStholo 
777461cc63eStholo 	/* Path + name */
778461cc63eStholo 	char *PathName = argv [i];
779461cc63eStholo 
780461cc63eStholo 	/* Path only, including slash */
781461cc63eStholo 	char *Path = NULL;
782461cc63eStholo 
783461cc63eStholo 	/* Name without path */
784461cc63eStholo 	char *Name = last_component (PathName);
785461cc63eStholo 
786461cc63eStholo 	if (Name > PathName)
787461cc63eStholo 	{
788461cc63eStholo 	    /* We have a path component, save it */
789461cc63eStholo 	    Path = xmalloc (Name - PathName + 1);
790461cc63eStholo 	    memcpy (Path, PathName, Name - PathName);
791461cc63eStholo 	    Path [Name - PathName] = '\0';
792461cc63eStholo 	}
793461cc63eStholo 
794461cc63eStholo 	rc = DosFindFirst(PathName,     	 /* File pattern */
79550bf276cStholo 			  &FindHandle,           /* Directory search handle */
79650bf276cStholo 			  ALL_FILES,             /* Search attribute */
79750bf276cStholo 			  (PVOID) &FindBuffer,   /* Result buffer */
79850bf276cStholo 			  sizeof(FindBuffer),    /* Result buffer length */
79950bf276cStholo 			  &FindCount,            /* Number of entries to find */
80050bf276cStholo 			  FIL_STANDARD);	 /* Return level 1 file info */
80150bf276cStholo 
80250bf276cStholo 	if (rc != 0)
80350bf276cStholo 	{
804461cc63eStholo 	    if (rc == ERROR_NO_MORE_FILES)
80550bf276cStholo 	    {
80650bf276cStholo 		/* No match.  The file specified didn't contain a wildcard (in which case
80750bf276cStholo 		   we clearly should return it unchanged), or it contained a wildcard which
80850bf276cStholo 		   didn't match (in which case it might be better for it to be an error,
80950bf276cStholo 		   but we don't try to do that).  */
81050bf276cStholo 		new_argv [new_argc++] = xstrdup (argv[i]);
81150bf276cStholo 		if (new_argc == max_new_argc)
81250bf276cStholo 		{
81350bf276cStholo 		    max_new_argc *= 2;
81450bf276cStholo 		    new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
81550bf276cStholo 		}
81650bf276cStholo 	    }
81750bf276cStholo 	    else
81850bf276cStholo 	    {
819461cc63eStholo                 error (1, rc, "cannot find %s", PathName);
82050bf276cStholo 	    }
82150bf276cStholo 	}
82250bf276cStholo 	else
82350bf276cStholo 	{
82450bf276cStholo 	    while (1)
82550bf276cStholo 	    {
82650bf276cStholo 		/*
82750bf276cStholo 		 * Don't match ".", "..", and files starting with '.'
82850bf276cStholo 		 * (unless pattern also starts with '.').  This is
82950bf276cStholo 		 * (more or less) what standard Unix globbing does.
83050bf276cStholo 		 */
83150bf276cStholo 		if ((strcmp(FindBuffer.achName, ".") != 0) &&
83250bf276cStholo 		    (strcmp(FindBuffer.achName, "..") != 0) &&
83350bf276cStholo 		    ((argv[i][0] == '.') || (FindBuffer.achName[0] != '.')))
83450bf276cStholo 		{
835461cc63eStholo 		    /* Be sure to add the path if needed */
836461cc63eStholo 		    char *NewArg;
837461cc63eStholo 		    if (Path)
838461cc63eStholo 		    {
839461cc63eStholo 			unsigned Len =
840461cc63eStholo 			    strlen (Path) + strlen (FindBuffer.achName) + 1;
841461cc63eStholo 			NewArg = xmalloc (Len);
842461cc63eStholo 			strcpy (NewArg, Path);
843461cc63eStholo 			strcat (NewArg, FindBuffer.achName);
844461cc63eStholo 		    }
845461cc63eStholo 		    else
846461cc63eStholo 		    {
847461cc63eStholo 			NewArg = xstrdup (FindBuffer.achName);
848461cc63eStholo 		    }
849461cc63eStholo 		    new_argv [new_argc++] = NewArg;
85050bf276cStholo 		    if (new_argc == max_new_argc)
85150bf276cStholo 		    {
85250bf276cStholo 			max_new_argc *= 2;
85350bf276cStholo 			new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
85450bf276cStholo 		    }
85550bf276cStholo 		}
85650bf276cStholo 
85750bf276cStholo 		rc = DosFindNext (FindHandle,
85850bf276cStholo 				  (PVOID) &FindBuffer,
85950bf276cStholo 				  sizeof(FindBuffer),
86050bf276cStholo 				  &FindCount);
86150bf276cStholo 		if (rc == ERROR_NO_MORE_FILES)
86250bf276cStholo 		    break;
86350bf276cStholo 		else if (rc != NO_ERROR)
86450bf276cStholo 		    error (1, rc, "cannot find %s", argv[i]);
86550bf276cStholo 	    }
86650bf276cStholo 	    rc = DosFindClose(FindHandle);
86750bf276cStholo 	    if (rc != 0)
86850bf276cStholo 		error (1, rc, "cannot close %s", argv[i]);
86950bf276cStholo 	}
870461cc63eStholo 	if (Path != NULL)
871461cc63eStholo 	    free (Path);
87250bf276cStholo     }
87350bf276cStholo     *pargc = new_argc;
87450bf276cStholo     *pargv = new_argv;
875c2c61682Stholo }
876780d15dfStholo 
8772286d8edStholo /* Change drive and directory to path DIR.  */
8782286d8edStholo 
879780d15dfStholo int
os2_chdir(const char * Dir)880780d15dfStholo os2_chdir (const char *Dir)
881780d15dfStholo {
8822286d8edStholo     /* If the path includes a drive, change the current drive to the one
8832286d8edStholo        given.  */
884780d15dfStholo     if (strlen (Dir) >= 2 && Dir [1] == ':')
885780d15dfStholo     {
886780d15dfStholo 	/* A drive is given in Dir. Extract the drive from the string, then
887780d15dfStholo 	 * remove the drive from Dir by incrementing it.
888780d15dfStholo 	 */
889780d15dfStholo 	int Drive = Dir [0];
890780d15dfStholo 	Dir += 2;
891780d15dfStholo 
892780d15dfStholo 	/* Check if the given drive is valid, convert to a drive number
893780d15dfStholo 	 * (A: == 1, B: == 2, etc.). The compare below assumes ascii, but
894780d15dfStholo 	 * that is not a problem with OS/2.
895780d15dfStholo 	 */
896780d15dfStholo 	if (Drive >= 'a' && Drive <= 'z')
897780d15dfStholo 	{
898780d15dfStholo 	    Drive -= 'a' - 1;
899780d15dfStholo 	}
900780d15dfStholo 	else if (Drive >= 'A' && Drive <= 'Z')
901780d15dfStholo 	{
902780d15dfStholo 	    Drive -= 'A' - 1;
903780d15dfStholo 	}
904780d15dfStholo 	else
905780d15dfStholo 	{
906780d15dfStholo 	    /* An invalid drive letter. Set errno and return an error */
907780d15dfStholo 	    errno = EACCES;
908780d15dfStholo 	    return -1;
909780d15dfStholo 	}
910780d15dfStholo 
911780d15dfStholo 	/* We have a valid drive given, so change the drive now */
912780d15dfStholo 	if (DosSetDefaultDisk (Drive) != 0)
913780d15dfStholo 	{
914780d15dfStholo 	    /* We had an error. Assume that the drive does not exist */
9152286d8edStholo #ifdef ENODEV
916780d15dfStholo 	    errno = ENODEV;
9172286d8edStholo #else
9182286d8edStholo 	    /* IBM C/C++ Tools 2.01 seems to lack ENODEV.  */
9192286d8edStholo 	    errno = ENOENT;
9202286d8edStholo #endif
921780d15dfStholo 	    return -1;
922780d15dfStholo 	}
923780d15dfStholo 
924780d15dfStholo     }
925780d15dfStholo 
926780d15dfStholo     /* Now we have a path without a drive left. Make it the current dir */
927780d15dfStholo     return chdir (Dir);
928780d15dfStholo }
929780d15dfStholo 
930780d15dfStholo 
931780d15dfStholo 
932