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