150bf276cStholo /* filesubr.c --- subroutines for dealing with files
250bf276cStholo Gratuitously adapted toward VMS quirks.
350bf276cStholo
450bf276cStholo Jim Blandy <jimb@cyclic.com>
550bf276cStholo Benjamin J. Lee <benjamin@cyclic.com>
650bf276cStholo
750bf276cStholo This file is part of GNU CVS.
850bf276cStholo
950bf276cStholo GNU CVS is free software; you can redistribute it and/or modify it
1050bf276cStholo under the terms of the GNU General Public License as published by the
1150bf276cStholo Free Software Foundation; either version 2, or (at your option) any
1250bf276cStholo later version.
1350bf276cStholo
1450bf276cStholo This program is distributed in the hope that it will be useful,
1550bf276cStholo but WITHOUT ANY WARRANTY; without even the implied warranty of
1650bf276cStholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17461cc63eStholo GNU General Public License for more details. */
1850bf276cStholo
1950bf276cStholo #include "cvs.h"
2050bf276cStholo
2150bf276cStholo static int deep_remove_dir PROTO((const char *path));
2250bf276cStholo
2350bf276cStholo /*
2450bf276cStholo * Copies "from" to "to".
2550bf276cStholo */
2650bf276cStholo void
copy_file(from_file,to_file)2750bf276cStholo copy_file (from_file, to_file)
2850bf276cStholo const char *from_file;
2950bf276cStholo const char *to_file;
3050bf276cStholo {
3150bf276cStholo char from[PATH_MAX], to[PATH_MAX];
3250bf276cStholo struct stat sb;
3350bf276cStholo struct utimbuf t;
3450bf276cStholo int fdin, fdout;
3550bf276cStholo
3650bf276cStholo /* Prefer local relative paths to files at expense of logical name
3750bf276cStholo access to files. */
3850bf276cStholo
3950bf276cStholo if (isabsolute(from_file))
4050bf276cStholo strcpy(from, from_file);
4150bf276cStholo else
4250bf276cStholo sprintf(from, "./%s", from_file);
4350bf276cStholo
4450bf276cStholo if (isabsolute(to_file))
4550bf276cStholo strcpy(to, to_file);
4650bf276cStholo else
4750bf276cStholo sprintf(to, "./%s", to_file);
4850bf276cStholo
4950bf276cStholo if (trace)
5050bf276cStholo #ifdef SERVER_SUPPORT
5150bf276cStholo (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
5250bf276cStholo (server_active) ? 'S' : ' ', from, to);
5350bf276cStholo #else
5450bf276cStholo (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
5550bf276cStholo #endif
5650bf276cStholo if (noexec)
5750bf276cStholo return;
5850bf276cStholo
5950bf276cStholo if ((fdin = open (from, O_RDONLY)) < 0)
6050bf276cStholo error (1, errno, "cannot open %s for copying", from);
6150bf276cStholo if (fstat (fdin, &sb) < 0)
6250bf276cStholo error (1, errno, "cannot fstat %s", from);
6350bf276cStholo if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
6450bf276cStholo error (1, errno, "cannot create %s for copying", to);
6550bf276cStholo if (sb.st_size > 0)
6650bf276cStholo {
6750bf276cStholo char buf[BUFSIZ];
6850bf276cStholo int n;
6950bf276cStholo
7050bf276cStholo for (;;)
7150bf276cStholo {
7250bf276cStholo n = read (fdin, buf, sizeof(buf));
7350bf276cStholo if (n == -1)
7450bf276cStholo {
7550bf276cStholo #ifdef EINTR
7650bf276cStholo if (errno == EINTR)
7750bf276cStholo continue;
7850bf276cStholo #endif
7950bf276cStholo error (1, errno, "cannot read file %s for copying", from);
8050bf276cStholo }
8150bf276cStholo else if (n == 0)
8250bf276cStholo break;
8350bf276cStholo
8450bf276cStholo if (write(fdout, buf, n) != n) {
8550bf276cStholo error (1, errno, "cannot write file %s for copying", to);
8650bf276cStholo }
8750bf276cStholo }
8850bf276cStholo
8950bf276cStholo #ifdef HAVE_FSYNC
9050bf276cStholo if (fsync (fdout))
9150bf276cStholo error (1, errno, "cannot fsync file %s after copying", to);
9250bf276cStholo #endif
9350bf276cStholo }
9450bf276cStholo
9550bf276cStholo if (close (fdin) < 0)
9650bf276cStholo error (0, errno, "cannot close %s", from);
9750bf276cStholo if (close (fdout) < 0)
9850bf276cStholo error (1, errno, "cannot close %s", to);
9950bf276cStholo
10050bf276cStholo /* now, set the times for the copied file to match those of the original */
10150bf276cStholo memset ((char *) &t, 0, sizeof (t));
10250bf276cStholo t.actime = sb.st_atime;
10350bf276cStholo t.modtime = sb.st_mtime;
10450bf276cStholo (void) utime (to, &t);
10550bf276cStholo }
10650bf276cStholo
10750bf276cStholo /* FIXME-krp: these functions would benefit from caching the char * &
10850bf276cStholo stat buf. */
10950bf276cStholo
11050bf276cStholo /*
11150bf276cStholo * Returns non-zero if the argument file is a directory, or is a symbolic
11250bf276cStholo * link which points to a directory.
11350bf276cStholo */
11450bf276cStholo int
isdir(file)11550bf276cStholo isdir (file)
11650bf276cStholo const char *file;
11750bf276cStholo {
11850bf276cStholo struct stat sb;
11950bf276cStholo
12050bf276cStholo if (stat (file, &sb) < 0)
12150bf276cStholo return (0);
12250bf276cStholo return (S_ISDIR (sb.st_mode));
12350bf276cStholo }
12450bf276cStholo
12550bf276cStholo /*
12650bf276cStholo * Returns non-zero if the argument file is a symbolic link.
12750bf276cStholo */
12850bf276cStholo int
islink(file)12950bf276cStholo islink (file)
13050bf276cStholo const char *file;
13150bf276cStholo {
13250bf276cStholo #ifdef S_ISLNK
13350bf276cStholo struct stat sb;
13450bf276cStholo
13550bf276cStholo if (lstat (file, &sb) < 0)
13650bf276cStholo return (0);
13750bf276cStholo return (S_ISLNK (sb.st_mode));
13850bf276cStholo #else
13950bf276cStholo return (0);
14050bf276cStholo #endif
14150bf276cStholo }
14250bf276cStholo
14350bf276cStholo /*
14450bf276cStholo * Returns non-zero if the argument file exists.
14550bf276cStholo */
14650bf276cStholo int
isfile(file)14750bf276cStholo isfile (file)
14850bf276cStholo const char *file;
14950bf276cStholo {
15050bf276cStholo return isaccessible(file, F_OK);
15150bf276cStholo }
15250bf276cStholo
15350bf276cStholo /*
15450bf276cStholo * Returns non-zero if the argument file is readable.
15550bf276cStholo */
15650bf276cStholo int
isreadable(file)15750bf276cStholo isreadable (file)
15850bf276cStholo const char *file;
15950bf276cStholo {
16050bf276cStholo return isaccessible(file, R_OK);
16150bf276cStholo }
16250bf276cStholo
16350bf276cStholo /*
16450bf276cStholo * Returns non-zero if the argument file is writable.
16550bf276cStholo */
16650bf276cStholo int
iswritable(file)16750bf276cStholo iswritable (file)
16850bf276cStholo const char *file;
16950bf276cStholo {
17050bf276cStholo return isaccessible(file, W_OK);
17150bf276cStholo }
17250bf276cStholo
17350bf276cStholo /*
17450bf276cStholo * Returns non-zero if the argument file is accessable according to
17550bf276cStholo * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid
17650bf276cStholo * bits set.
17750bf276cStholo */
17850bf276cStholo int
isaccessible(file,mode)17950bf276cStholo isaccessible (file, mode)
18050bf276cStholo const char *file;
18150bf276cStholo const int mode;
18250bf276cStholo {
18350bf276cStholo #ifdef SETXID_SUPPORT
18450bf276cStholo struct stat sb;
18550bf276cStholo int umask = 0;
18650bf276cStholo int gmask = 0;
18750bf276cStholo int omask = 0;
18850bf276cStholo int uid;
18950bf276cStholo
19050bf276cStholo if (stat(file, &sb) == -1)
19150bf276cStholo return 0;
19250bf276cStholo if (mode == F_OK)
19350bf276cStholo return 1;
19450bf276cStholo
19550bf276cStholo uid = geteuid();
19650bf276cStholo if (uid == 0) /* superuser */
19750bf276cStholo {
19850bf276cStholo if (mode & X_OK)
19950bf276cStholo return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
20050bf276cStholo else
20150bf276cStholo return 1;
20250bf276cStholo }
20350bf276cStholo
20450bf276cStholo if (mode & R_OK)
20550bf276cStholo {
20650bf276cStholo umask |= S_IRUSR;
20750bf276cStholo gmask |= S_IRGRP;
20850bf276cStholo omask |= S_IROTH;
20950bf276cStholo }
21050bf276cStholo if (mode & W_OK)
21150bf276cStholo {
21250bf276cStholo umask |= S_IWUSR;
21350bf276cStholo gmask |= S_IWGRP;
21450bf276cStholo omask |= S_IWOTH;
21550bf276cStholo }
21650bf276cStholo if (mode & X_OK)
21750bf276cStholo {
21850bf276cStholo umask |= S_IXUSR;
21950bf276cStholo gmask |= S_IXGRP;
22050bf276cStholo omask |= S_IXOTH;
22150bf276cStholo }
22250bf276cStholo
22350bf276cStholo if (sb.st_uid == uid)
22450bf276cStholo return (sb.st_mode & umask) == umask;
22550bf276cStholo else if (sb.st_gid == getegid())
22650bf276cStholo return (sb.st_mode & gmask) == gmask;
22750bf276cStholo else
22850bf276cStholo return (sb.st_mode & omask) == omask;
22950bf276cStholo #else
23050bf276cStholo return access(file, mode) == 0;
23150bf276cStholo #endif
23250bf276cStholo }
23350bf276cStholo
23450bf276cStholo /*
23550bf276cStholo * Open a file and die if it fails
23650bf276cStholo */
23750bf276cStholo FILE *
open_file(name,mode)23850bf276cStholo open_file (name, mode)
23950bf276cStholo const char *name;
24050bf276cStholo const char *mode;
24150bf276cStholo {
24250bf276cStholo FILE *fp;
24350bf276cStholo
24450bf276cStholo if ((fp = fopen (name, mode)) == NULL)
24550bf276cStholo error (1, errno, "cannot open %s", name);
24650bf276cStholo return (fp);
24750bf276cStholo }
24850bf276cStholo
24950bf276cStholo /*
25050bf276cStholo * Make a directory and die if it fails
25150bf276cStholo */
25250bf276cStholo void
make_directory(name)25350bf276cStholo make_directory (name)
25450bf276cStholo const char *name;
25550bf276cStholo {
25650bf276cStholo struct stat sb;
25750bf276cStholo
25850bf276cStholo if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
25950bf276cStholo error (0, 0, "%s already exists but is not a directory", name);
26050bf276cStholo if (!noexec && mkdir (name, 0777) < 0)
26150bf276cStholo error (1, errno, "cannot make directory %s", name);
26250bf276cStholo }
26350bf276cStholo
26450bf276cStholo /*
26550bf276cStholo * Make a path to the argument directory, printing a message if something
26650bf276cStholo * goes wrong.
26750bf276cStholo */
26850bf276cStholo void
make_directories(name)26950bf276cStholo make_directories (name)
27050bf276cStholo const char *name;
27150bf276cStholo {
27250bf276cStholo char *cp;
27350bf276cStholo
27450bf276cStholo if (noexec)
27550bf276cStholo return;
27650bf276cStholo
27750bf276cStholo if (mkdir (name, 0777) == 0 || errno == EEXIST)
27850bf276cStholo return;
27950bf276cStholo if (! existence_error (errno))
28050bf276cStholo {
28150bf276cStholo error (0, errno, "cannot make path to %s", name);
28250bf276cStholo return;
28350bf276cStholo }
28450bf276cStholo if ((cp = strrchr (name, '/')) == NULL)
28550bf276cStholo return;
28650bf276cStholo *cp = '\0';
28750bf276cStholo make_directories (name);
28850bf276cStholo *cp++ = '/';
28950bf276cStholo if (*cp == '\0')
29050bf276cStholo return;
29150bf276cStholo (void) mkdir (name, 0777);
29250bf276cStholo }
29350bf276cStholo
29450bf276cStholo /* Create directory NAME if it does not already exist; fatal error for
29550bf276cStholo other errors. Returns 0 if directory was created; 1 if it already
29650bf276cStholo existed. */
29750bf276cStholo int
mkdir_if_needed(name)29850bf276cStholo mkdir_if_needed (name)
29950bf276cStholo char *name;
30050bf276cStholo {
30150bf276cStholo if (mkdir (name, 0777) < 0)
30250bf276cStholo {
30350bf276cStholo if (errno != EEXIST
30450bf276cStholo #ifdef EACCESS
30550bf276cStholo /* This was copied over from the OS/2 code; I would guess it
30650bf276cStholo isn't needed here but that has not been verified. */
30750bf276cStholo && errno != EACCESS
30850bf276cStholo #endif
30950bf276cStholo )
31050bf276cStholo error (1, errno, "cannot make directory %s", name);
31150bf276cStholo return 1;
31250bf276cStholo }
31350bf276cStholo return 0;
31450bf276cStholo }
31550bf276cStholo
31650bf276cStholo /*
31750bf276cStholo * Change the mode of a file, either adding write permissions, or removing
31850bf276cStholo * all write permissions. Either change honors the current umask setting.
31950bf276cStholo */
32050bf276cStholo void
xchmod(fname_file,writable)32150bf276cStholo xchmod (fname_file, writable)
32250bf276cStholo char *fname_file;
32350bf276cStholo int writable;
32450bf276cStholo {
32550bf276cStholo char fname[PATH_MAX];
32650bf276cStholo struct stat sb;
32750bf276cStholo mode_t mode, oumask;
32850bf276cStholo
32950bf276cStholo /* Prefer local relative paths to files at expense of logical name
33050bf276cStholo access to files. */
33150bf276cStholo
33250bf276cStholo if (isabsolute(fname_file))
33350bf276cStholo strcpy(fname, fname_file);
33450bf276cStholo else
33550bf276cStholo sprintf(fname, "./%s", fname_file);
33650bf276cStholo
33750bf276cStholo if (stat (fname, &sb) < 0)
33850bf276cStholo {
33950bf276cStholo if (!noexec)
34050bf276cStholo error (0, errno, "cannot stat %s", fname);
34150bf276cStholo return;
34250bf276cStholo }
34350bf276cStholo oumask = umask (0);
34450bf276cStholo (void) umask (oumask);
34550bf276cStholo if (writable)
34650bf276cStholo {
34750bf276cStholo mode = sb.st_mode | (~oumask
34850bf276cStholo & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
34950bf276cStholo | ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
35050bf276cStholo | ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
35150bf276cStholo }
35250bf276cStholo else
35350bf276cStholo {
35450bf276cStholo mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
35550bf276cStholo }
35650bf276cStholo
35750bf276cStholo if (trace)
35850bf276cStholo #ifdef SERVER_SUPPORT
35950bf276cStholo (void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
36050bf276cStholo (server_active) ? 'S' : ' ', fname, mode);
36150bf276cStholo #else
36250bf276cStholo (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
36350bf276cStholo #endif
36450bf276cStholo if (noexec)
36550bf276cStholo return;
36650bf276cStholo
36750bf276cStholo if (chmod (fname, mode) < 0)
36850bf276cStholo error (0, errno, "cannot change mode of file %s", fname);
36950bf276cStholo }
37050bf276cStholo
37150bf276cStholo /*
37250bf276cStholo * Rename a file and die if it fails
37350bf276cStholo */
37450bf276cStholo void
rename_file(from_file,to_file)37550bf276cStholo rename_file (from_file, to_file)
37650bf276cStholo const char *from_file;
37750bf276cStholo const char *to_file;
37850bf276cStholo {
37950bf276cStholo char from[PATH_MAX], to[PATH_MAX];
38050bf276cStholo
38150bf276cStholo /* Prefer local relative paths to files at expense of logical name
38250bf276cStholo access to files. */
38350bf276cStholo
38450bf276cStholo if (isabsolute(from_file))
38550bf276cStholo strcpy(from, from_file);
38650bf276cStholo else
38750bf276cStholo sprintf(from, "./%s", from_file);
38850bf276cStholo
38950bf276cStholo if (isabsolute(to_file))
39050bf276cStholo strcpy(to, to_file);
39150bf276cStholo else
39250bf276cStholo sprintf(to, "./%s", to_file);
39350bf276cStholo
39450bf276cStholo if (trace)
39550bf276cStholo #ifdef SERVER_SUPPORT
39650bf276cStholo (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
39750bf276cStholo (server_active) ? 'S' : ' ', from, to);
39850bf276cStholo #else
39950bf276cStholo (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
40050bf276cStholo #endif
40150bf276cStholo if (noexec)
40250bf276cStholo return;
40350bf276cStholo
40450bf276cStholo if (rename (from, to) < 0)
40550bf276cStholo error (1, errno, "cannot rename file %s to %s", from, to);
40650bf276cStholo }
40750bf276cStholo
40850bf276cStholo /*
40950bf276cStholo * unlink a file, if possible.
41050bf276cStholo */
41150bf276cStholo int
unlink_file(f_file)41250bf276cStholo unlink_file (f_file)
41350bf276cStholo const char *f_file;
41450bf276cStholo {
41550bf276cStholo char f[PATH_MAX];
41650bf276cStholo
41750bf276cStholo /* Prefer local relative paths to files at expense of logical name
41850bf276cStholo access to files. */
41950bf276cStholo
42050bf276cStholo if (isabsolute(f_file))
42150bf276cStholo strcpy(f, f_file);
42250bf276cStholo else
42350bf276cStholo sprintf(f, "./%s", f_file);
42450bf276cStholo
42550bf276cStholo if (trace)
42650bf276cStholo #ifdef SERVER_SUPPORT
42750bf276cStholo (void) fprintf (stderr, "%c-> unlink(%s)\n",
42850bf276cStholo (server_active) ? 'S' : ' ', f);
42950bf276cStholo #else
43050bf276cStholo (void) fprintf (stderr, "-> unlink(%s)\n", f);
43150bf276cStholo #endif
43250bf276cStholo if (noexec)
43350bf276cStholo return (0);
43450bf276cStholo
435*43c1707eStholo return (vms_unlink (f));
43650bf276cStholo }
43750bf276cStholo
43850bf276cStholo /*
43950bf276cStholo * Unlink a file or dir, if possible. If it is a directory do a deep
44050bf276cStholo * removal of all of the files in the directory. Return -1 on error
44150bf276cStholo * (in which case errno is set).
44250bf276cStholo */
44350bf276cStholo int
unlink_file_dir(f_file)44450bf276cStholo unlink_file_dir (f_file)
44550bf276cStholo const char *f_file;
44650bf276cStholo {
44750bf276cStholo char f[PATH_MAX];
44850bf276cStholo
44950bf276cStholo /* Prefer local relative paths to files at expense of logical name
45050bf276cStholo access to files. */
45150bf276cStholo
45250bf276cStholo if (isabsolute(f_file))
45350bf276cStholo strcpy(f, f_file);
45450bf276cStholo else
45550bf276cStholo sprintf(f, "./%s", f_file);
45650bf276cStholo
45750bf276cStholo if (trace)
45850bf276cStholo #ifdef SERVER_SUPPORT
45950bf276cStholo (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
46050bf276cStholo (server_active) ? 'S' : ' ', f);
46150bf276cStholo #else
46250bf276cStholo (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
46350bf276cStholo #endif
46450bf276cStholo if (noexec)
46550bf276cStholo return (0);
46650bf276cStholo
467*43c1707eStholo if (vms_unlink (f) != 0)
46850bf276cStholo {
46950bf276cStholo /* under NEXTSTEP errno is set to return EPERM if
47050bf276cStholo * the file is a directory,or if the user is not
47150bf276cStholo * allowed to read or write to the file.
47250bf276cStholo * [This is probably a bug in the O/S]
47350bf276cStholo * other systems will return EISDIR to indicate
47450bf276cStholo * that the path is a directory.
47550bf276cStholo */
47650bf276cStholo if (errno == EISDIR || errno == EPERM)
47750bf276cStholo return deep_remove_dir (f);
47850bf276cStholo else
47950bf276cStholo /* The file wasn't a directory and some other
48050bf276cStholo * error occured
48150bf276cStholo */
48250bf276cStholo return -1;
48350bf276cStholo }
48450bf276cStholo /* We were able to remove the file from the disk */
48550bf276cStholo return 0;
48650bf276cStholo }
48750bf276cStholo
48850bf276cStholo /* Remove a directory and everything it contains. Returns 0 for
48950bf276cStholo * success, -1 for failure (in which case errno is set).
49050bf276cStholo */
49150bf276cStholo
49250bf276cStholo static int
deep_remove_dir(path)49350bf276cStholo deep_remove_dir (path)
49450bf276cStholo const char *path;
49550bf276cStholo {
49650bf276cStholo DIR *dirp;
49750bf276cStholo struct dirent *dp;
49850bf276cStholo char buf[PATH_MAX];
49950bf276cStholo
50050bf276cStholo if (rmdir (path) != 0 && (errno == ENOTEMPTY || errno == EEXIST))
50150bf276cStholo {
502*43c1707eStholo if ((dirp = CVS_OPENDIR (path)) == NULL)
50350bf276cStholo /* If unable to open the directory return
50450bf276cStholo * an error
50550bf276cStholo */
50650bf276cStholo return -1;
50750bf276cStholo
508*43c1707eStholo while ((dp = CVS_READDIR (dirp)) != NULL)
50950bf276cStholo {
51050bf276cStholo if (strcmp (dp->d_name, ".") == 0 ||
51150bf276cStholo strcmp (dp->d_name, "..") == 0)
51250bf276cStholo continue;
51350bf276cStholo
51450bf276cStholo sprintf (buf, "%s/%s", path, dp->d_name);
51550bf276cStholo
516*43c1707eStholo if (vms_unlink (buf) != 0 )
51750bf276cStholo {
51850bf276cStholo if (errno == EISDIR || errno == EPERM)
51950bf276cStholo {
52050bf276cStholo if (deep_remove_dir (buf))
52150bf276cStholo {
522*43c1707eStholo CVS_CLOSEDIR (dirp);
52350bf276cStholo return -1;
52450bf276cStholo }
52550bf276cStholo }
52650bf276cStholo else
52750bf276cStholo {
52850bf276cStholo /* buf isn't a directory, or there are
52950bf276cStholo * some sort of permision problems
53050bf276cStholo */
531*43c1707eStholo CVS_CLOSEDIR (dirp);
53250bf276cStholo return -1;
53350bf276cStholo }
53450bf276cStholo }
53550bf276cStholo }
536*43c1707eStholo CVS_CLOSEDIR (dirp);
53750bf276cStholo return rmdir (path);
53850bf276cStholo }
53950bf276cStholo
54050bf276cStholo /* Was able to remove the directory return 0 */
54150bf276cStholo return 0;
54250bf276cStholo }
54350bf276cStholo
54450bf276cStholo /* Read NCHARS bytes from descriptor FD into BUF.
54550bf276cStholo Return the number of characters successfully read.
54650bf276cStholo The number returned is always NCHARS unless end-of-file or error. */
54750bf276cStholo static size_t
block_read(fd,buf,nchars)54850bf276cStholo block_read (fd, buf, nchars)
54950bf276cStholo int fd;
55050bf276cStholo char *buf;
55150bf276cStholo size_t nchars;
55250bf276cStholo {
55350bf276cStholo char *bp = buf;
55450bf276cStholo size_t nread;
55550bf276cStholo
55650bf276cStholo do
55750bf276cStholo {
55850bf276cStholo nread = read (fd, bp, nchars);
55950bf276cStholo if (nread == (size_t)-1)
56050bf276cStholo {
56150bf276cStholo #ifdef EINTR
56250bf276cStholo if (errno == EINTR)
56350bf276cStholo continue;
56450bf276cStholo #endif
56550bf276cStholo return (size_t)-1;
56650bf276cStholo }
56750bf276cStholo
56850bf276cStholo if (nread == 0)
56950bf276cStholo break;
57050bf276cStholo
57150bf276cStholo bp += nread;
57250bf276cStholo nchars -= nread;
57350bf276cStholo } while (nchars != 0);
57450bf276cStholo
57550bf276cStholo return bp - buf;
57650bf276cStholo }
57750bf276cStholo
57850bf276cStholo
57950bf276cStholo /*
58050bf276cStholo * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
58150bf276cStholo */
58250bf276cStholo int
xcmp(file1_file,file2_file)58350bf276cStholo xcmp (file1_file, file2_file)
58450bf276cStholo const char *file1_file;
58550bf276cStholo const char *file2_file;
58650bf276cStholo {
58750bf276cStholo char file1[PATH_MAX], file2[PATH_MAX];
58850bf276cStholo char *buf1, *buf2;
58950bf276cStholo struct stat sb1, sb2;
59050bf276cStholo int fd1, fd2;
59150bf276cStholo int ret;
59250bf276cStholo
59350bf276cStholo /* Prefer local relative paths to files at expense of logical name
59450bf276cStholo access to files. */
59550bf276cStholo
59650bf276cStholo if (isabsolute(file1_file))
59750bf276cStholo strcpy(file1, file1_file);
59850bf276cStholo else
59950bf276cStholo sprintf(file1, "./%s", file1_file);
60050bf276cStholo
60150bf276cStholo if (isabsolute(file2_file))
60250bf276cStholo strcpy(file2, file2_file);
60350bf276cStholo else
60450bf276cStholo sprintf(file2, "./%s", file2_file);
60550bf276cStholo
60650bf276cStholo if ((fd1 = open (file1, O_RDONLY)) < 0)
60750bf276cStholo error (1, errno, "cannot open file %s for comparing", file1);
60850bf276cStholo if ((fd2 = open (file2, O_RDONLY)) < 0)
60950bf276cStholo error (1, errno, "cannot open file %s for comparing", file2);
61050bf276cStholo if (fstat (fd1, &sb1) < 0)
61150bf276cStholo error (1, errno, "cannot fstat %s", file1);
61250bf276cStholo if (fstat (fd2, &sb2) < 0)
61350bf276cStholo error (1, errno, "cannot fstat %s", file2);
61450bf276cStholo
61550bf276cStholo /* A generic file compare routine might compare st_dev & st_ino here
61650bf276cStholo to see if the two files being compared are actually the same file.
61750bf276cStholo But that won't happen in CVS, so we won't bother. */
61850bf276cStholo
61950bf276cStholo if (sb1.st_size != sb2.st_size)
62050bf276cStholo ret = 1;
62150bf276cStholo else if (sb1.st_size == 0)
62250bf276cStholo ret = 0;
62350bf276cStholo else
62450bf276cStholo {
62550bf276cStholo /* FIXME: compute the optimal buffer size by computing the least
62650bf276cStholo common multiple of the files st_blocks field */
62750bf276cStholo size_t buf_size = 8 * 1024;
62850bf276cStholo size_t read1;
62950bf276cStholo size_t read2;
63050bf276cStholo
63150bf276cStholo buf1 = xmalloc (buf_size);
63250bf276cStholo buf2 = xmalloc (buf_size);
63350bf276cStholo
63450bf276cStholo do
63550bf276cStholo {
63650bf276cStholo read1 = block_read (fd1, buf1, buf_size);
63750bf276cStholo if (read1 == (size_t)-1)
63850bf276cStholo error (1, errno, "cannot read file %s for comparing", file1);
63950bf276cStholo
64050bf276cStholo read2 = block_read (fd2, buf2, buf_size);
64150bf276cStholo if (read2 == (size_t)-1)
64250bf276cStholo error (1, errno, "cannot read file %s for comparing", file2);
64350bf276cStholo
64450bf276cStholo /* assert (read1 == read2); */
64550bf276cStholo
64650bf276cStholo ret = memcmp(buf1, buf2, read1);
64750bf276cStholo } while (ret == 0 && read1 == buf_size);
64850bf276cStholo
64950bf276cStholo free (buf1);
65050bf276cStholo free (buf2);
65150bf276cStholo }
65250bf276cStholo
65350bf276cStholo (void) close (fd1);
65450bf276cStholo (void) close (fd2);
65550bf276cStholo return (ret);
65650bf276cStholo }
65750bf276cStholo
65850bf276cStholo unsigned char
65950bf276cStholo VMS_filename_classes[] =
66050bf276cStholo {
66150bf276cStholo 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
66250bf276cStholo 0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
66350bf276cStholo 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
66450bf276cStholo 0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
66550bf276cStholo 0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
66650bf276cStholo 0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
66750bf276cStholo 0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
66850bf276cStholo 0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
66950bf276cStholo 0x40,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
67050bf276cStholo 0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
67150bf276cStholo 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
67250bf276cStholo 0x78,0x79,0x7a,0x5b, 0x5c,0x5d,0x5e,0x5f,
67350bf276cStholo 0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
67450bf276cStholo 0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
67550bf276cStholo 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
67650bf276cStholo 0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f,
67750bf276cStholo 0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
67850bf276cStholo 0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
67950bf276cStholo 0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
68050bf276cStholo 0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f,
68150bf276cStholo 0xa0,0xa1,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,
68250bf276cStholo 0xa8,0xa9,0xaa,0xab, 0xac,0xad,0xae,0xaf,
68350bf276cStholo 0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
68450bf276cStholo 0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
68550bf276cStholo 0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
68650bf276cStholo 0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
68750bf276cStholo 0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
68850bf276cStholo 0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
68950bf276cStholo 0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
69050bf276cStholo 0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
69150bf276cStholo 0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
69250bf276cStholo 0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
69350bf276cStholo };
69450bf276cStholo
69550bf276cStholo /* Like strcmp, but with the appropriate tweaks for file names.
69650bf276cStholo Under VMS, filenames are case-insensitive but case-preserving.
69750bf276cStholo FIXME: this should compare y.tab.c equal with y_tab.c, at least
69850bf276cStholo if fnfold is modified (see below). */
69950bf276cStholo int
fncmp(const char * n1,const char * n2)70050bf276cStholo fncmp (const char *n1, const char *n2)
70150bf276cStholo {
70250bf276cStholo while (*n1 && *n2
70350bf276cStholo && (VMS_filename_classes[(unsigned char) *n1]
70450bf276cStholo == VMS_filename_classes[(unsigned char) *n2]))
70550bf276cStholo n1++, n2++;
70650bf276cStholo return (VMS_filename_classes[(unsigned char) *n1]
70750bf276cStholo - VMS_filename_classes[(unsigned char) *n2]);
70850bf276cStholo }
70950bf276cStholo
71050bf276cStholo /* Fold characters in FILENAME to their canonical forms. FIXME: this
71150bf276cStholo probably should be mapping y.tab.c to y_tab.c but first we have to
71250bf276cStholo figure out whether fnfold is the right hook for that functionality
71350bf276cStholo (probable answer: yes, but it should not fold case on OS/2, VMS, or
71450bf276cStholo NT. You see, fnfold isn't called anywhere, so we can define it to
71550bf276cStholo mean whatever makes sense. Of course to solve the VMS y.tab.c
71650bf276cStholo problem we'd need to call it where appropriate. It would need to
71750bf276cStholo be redocumented as "fold to a form we can create in the filesystem"
71850bf276cStholo rather than "canonical form"). The idea is that files we create
71950bf276cStholo would get thusly munged, but CVS can cope with their names being
72050bf276cStholo different the same way that the NT port copes with it if the user
7212286d8edStholo renames a file from "foo" to "FOO".
7222286d8edStholo
7232286d8edStholo Alternately, this kind of handling could/should go into CVS_FOPEN
7242286d8edStholo and friends (if we want to do it like the Mac port, anyway). */
72550bf276cStholo void
fnfold(char * filename)72650bf276cStholo fnfold (char *filename)
72750bf276cStholo {
72850bf276cStholo while (*filename)
72950bf276cStholo {
73050bf276cStholo *filename = FOLD_FN_CHAR (*filename);
73150bf276cStholo filename++;
73250bf276cStholo }
73350bf276cStholo }
73450bf276cStholo
73550bf276cStholo /* Generate a unique temporary filename. Returns a pointer to a newly
73650bf276cStholo malloc'd string containing the name. Returns successfully or not at
73750bf276cStholo all. */
73850bf276cStholo char *
cvs_temp_name()73950bf276cStholo cvs_temp_name ()
74050bf276cStholo {
74150bf276cStholo char value[L_tmpnam + 1];
74250bf276cStholo char *retval;
74350bf276cStholo
74450bf276cStholo /* FIXME: what is the VMS equivalent to TMPDIR? */
74550bf276cStholo retval = tmpnam (value);
74650bf276cStholo if (retval == NULL)
74750bf276cStholo error (1, errno, "cannot generate temporary filename");
74850bf276cStholo return xstrdup (retval);
74950bf276cStholo }
75050bf276cStholo
75150bf276cStholo /* Return non-zero iff FILENAME is absolute.
75250bf276cStholo Trivial under Unix, but more complicated under other systems. */
75350bf276cStholo int
isabsolute(filename)75450bf276cStholo isabsolute (filename)
75550bf276cStholo const char *filename;
75650bf276cStholo {
75750bf276cStholo if(filename[0] == '/'
75850bf276cStholo || filename[0] == '['
75950bf276cStholo || filename[0] == '<'
76050bf276cStholo || strchr(filename, ':'))
76150bf276cStholo return 1;
76250bf276cStholo else
76350bf276cStholo return 0;
76450bf276cStholo }
76550bf276cStholo
76650bf276cStholo
76750bf276cStholo /* Return a pointer into PATH's last component. */
76850bf276cStholo char *
last_component(path)76950bf276cStholo last_component (path)
77050bf276cStholo char *path;
77150bf276cStholo {
77250bf276cStholo char *last = strrchr (path, '/');
77350bf276cStholo
7745e617892Stholo if (last && (last != path))
77550bf276cStholo return last + 1;
77650bf276cStholo else
77750bf276cStholo return path;
77850bf276cStholo }
77950bf276cStholo
78050bf276cStholo /* Return the home directory. Returns a pointer to storage
78150bf276cStholo managed by this function or its callees (currently getenv). */
78250bf276cStholo char *
get_homedir()78350bf276cStholo get_homedir ()
78450bf276cStholo {
78550bf276cStholo return getenv ("HOME");
78650bf276cStholo }
78750bf276cStholo
788*43c1707eStholo #ifndef __VMS_VER
789*43c1707eStholo #define __VMS_VER 0
790*43c1707eStholo #endif
791*43c1707eStholo #ifndef __DECC_VER
792*43c1707eStholo #define __DECC_VER 0
793*43c1707eStholo #endif
794*43c1707eStholo
795*43c1707eStholo #if __VMS_VER < 70200000 || __DECC_VER < 50700000
79650bf276cStholo /* See cvs.h for description. On VMS this currently does nothing, although
79750bf276cStholo I think we should be expanding wildcards here. */
79850bf276cStholo void
expand_wild(argc,argv,pargc,pargv)79950bf276cStholo expand_wild (argc, argv, pargc, pargv)
80050bf276cStholo int argc;
80150bf276cStholo char **argv;
80250bf276cStholo int *pargc;
80350bf276cStholo char ***pargv;
80450bf276cStholo {
80550bf276cStholo int i;
80650bf276cStholo *pargc = argc;
80750bf276cStholo *pargv = (char **) xmalloc (argc * sizeof (char *));
80850bf276cStholo for (i = 0; i < argc; ++i)
80950bf276cStholo (*pargv)[i] = xstrdup (argv[i]);
81050bf276cStholo }
811*43c1707eStholo
812*43c1707eStholo #else /* __VMS_VER >= 70200000 && __DECC_VER >= 50700000 */
813*43c1707eStholo
814*43c1707eStholo /* These global variables are necessary to pass information from the
815*43c1707eStholo * routine that calls decc$from_vms into the callback routine. In a
816*43c1707eStholo * multi-threaded environment, access to these variables MUST be
817*43c1707eStholo * serialized.
818*43c1707eStholo */
819*43c1707eStholo static char CurWorkingDir[PATH_MAX+1];
820*43c1707eStholo static char **ArgvList;
821*43c1707eStholo static int CurArg;
822*43c1707eStholo static int MaxArgs;
823*43c1707eStholo
ew_no_op(char * fname)824*43c1707eStholo static int ew_no_op (char *fname) {
825*43c1707eStholo (void) fname; /* Shut the compiler up */
826*43c1707eStholo return 1; /* Continue */
827*43c1707eStholo }
828*43c1707eStholo
ew_add_file(char * fname)829*43c1707eStholo static int ew_add_file (char *fname) {
830*43c1707eStholo char *lastslash, *firstper;
831*43c1707eStholo int i;
832*43c1707eStholo
833*43c1707eStholo if (strncmp(fname,CurWorkingDir,strlen(CurWorkingDir)) == 0) {
834*43c1707eStholo fname += strlen(CurWorkingDir);
835*43c1707eStholo }
836*43c1707eStholo lastslash = strrchr(fname,'/');
837*43c1707eStholo if (!lastslash) {
838*43c1707eStholo lastslash = fname;
839*43c1707eStholo }
840*43c1707eStholo if ((firstper=strchr(lastslash,'.')) != strrchr(lastslash,'.')) {
841*43c1707eStholo /* We have two periods -- one is to separate the version off */
842*43c1707eStholo *strrchr(fname,'.') = '\0';
843*43c1707eStholo }
844*43c1707eStholo if (firstper && firstper[1]=='\0') {
845*43c1707eStholo *firstper = '\0';
846*43c1707eStholo }
847*43c1707eStholo /* The following code is to insure that no duplicates appear,
848*43c1707eStholo * because most of the time it will just be a different version
849*43c1707eStholo */
850*43c1707eStholo for (i=0; i<CurArg && strcmp(ArgvList[i],fname)!=0; ++i) {
851*43c1707eStholo ;
852*43c1707eStholo }
853*43c1707eStholo if (i==CurArg && CurArg<MaxArgs) {
854*43c1707eStholo ArgvList[CurArg++] = strdup(fname);
855*43c1707eStholo }
856*43c1707eStholo return ArgvList[CurArg-1] != 0; /* Stop if we couldn't dup the string */
857*43c1707eStholo }
858*43c1707eStholo
859*43c1707eStholo /* The following two routines are meant to allow future versions of new_arglist
860*43c1707eStholo * routine to be multi-thread-safe. It will be necessary in that environment
861*43c1707eStholo * to serialize access to CurWorkingDir, ArgvList, MaxArg, and CurArg. We
862*43c1707eStholo * currently don't do any multi-threaded programming, so right now these
863*43c1707eStholo * routines are no-ops.
864*43c1707eStholo */
wait_and_protect_globs(void)865*43c1707eStholo static void wait_and_protect_globs (void) {
866*43c1707eStholo return;
867*43c1707eStholo }
868*43c1707eStholo
release_globs(void)869*43c1707eStholo static void release_globs (void) {
870*43c1707eStholo return;
871*43c1707eStholo }
872*43c1707eStholo
873*43c1707eStholo /*pf---------------------------------------------------------------- expand_wild
874*43c1707eStholo *
875*43c1707eStholo * New Argument List - (SDS)
876*43c1707eStholo *
877*43c1707eStholo * DESCRIPTION:
878*43c1707eStholo * This routine takes the argc, argv passed in from main() and returns a
879*43c1707eStholo * new argc, argv list, which simulates (to an extent) Unix-Style filename
880*43c1707eStholo * globbing with VMS wildcards. The key difference is that it will return
881*43c1707eStholo * Unix-style filenames, i.e., no VMS file version numbers. The complexity
882*43c1707eStholo * comes from the desire to not simply allocate 10000 argv entries.
883*43c1707eStholo *
884*43c1707eStholo * INPUTS:
885*43c1707eStholo * argc - The integer argc passed into main
886*43c1707eStholo * argv - The pointer to the array of char*'s passed into main
887*43c1707eStholo *
888*43c1707eStholo * OUTPUTS:
889*43c1707eStholo * pargv - A pointer to a (char **) to hold the new argv list
890*43c1707eStholo * pargc - A pointer to an int to hold the new argc
891*43c1707eStholo *
892*43c1707eStholo * RETURNS:
893*43c1707eStholo * NONE
894*43c1707eStholo *
895*43c1707eStholo * SIDE EFFECTS:
896*43c1707eStholo * This routine will normally modify the global statics CurArg, MaxArg,
897*43c1707eStholo * ArgvList, and CurWorkingDir.
898*43c1707eStholo *
899*43c1707eStholo * NOTES:
900*43c1707eStholo * It is ok for &argc == pargc and &argv == pargv.
901*43c1707eStholo *
902*43c1707eStholo *------------------------------------------------------------------------------
903*43c1707eStholo */
expand_wild(int argc,char ** argv,int * pargc,char *** pargv)904*43c1707eStholo void expand_wild (int argc, char **argv, int *pargc, char ***pargv) {
905*43c1707eStholo int totfiles, filesgotten;
906*43c1707eStholo int i;
907*43c1707eStholo int largc;
908*43c1707eStholo char **largv;
909*43c1707eStholo
910*43c1707eStholo /* This first loop is to find out AT MOST how big to make the
911*43c1707eStholo * pargv array.
912*43c1707eStholo */
913*43c1707eStholo for (totfiles=0,i=0; i<argc; ++i) {
914*43c1707eStholo char *arg = argv[i];
915*43c1707eStholo
916*43c1707eStholo if (arg != 0 && ( strchr(arg,' ') != 0
917*43c1707eStholo || strcmp(arg,".") == 0
918*43c1707eStholo || strcmp(arg,"..") == 0) ) {
919*43c1707eStholo ++totfiles;
920*43c1707eStholo }else if (arg != 0) {
921*43c1707eStholo int num;
922*43c1707eStholo char *p = arg;
923*43c1707eStholo /* Handle comma-separated filelists */
924*43c1707eStholo while ( (p=strchr(p,',')) != 0) {
925*43c1707eStholo *p = '\0';
926*43c1707eStholo num = decc$from_vms (arg, ew_no_op, 1);
927*43c1707eStholo totfiles += num>0 ? num : 1;
928*43c1707eStholo *p++ = ',';
929*43c1707eStholo arg = p;
930*43c1707eStholo }
931*43c1707eStholo if (*arg != '\0') {
932*43c1707eStholo num = decc$from_vms (arg, ew_no_op, 1);
933*43c1707eStholo totfiles += num>0 ? num : 1;
934*43c1707eStholo }
935*43c1707eStholo }
936*43c1707eStholo }
937*43c1707eStholo largv = 0;
938*43c1707eStholo if (totfiles) {
939*43c1707eStholo largv = malloc (sizeof*largv * (totfiles + 1));
940*43c1707eStholo }
941*43c1707eStholo filesgotten = 0;
942*43c1707eStholo if (largv != 0) {
943*43c1707eStholo int len;
944*43c1707eStholo /* All bits set to zero may not be a NULL ptr */
945*43c1707eStholo for (i=totfiles; --i>=0; ) {
946*43c1707eStholo largv[i] = 0;
947*43c1707eStholo }
948*43c1707eStholo largv[totfiles] = 0;
949*43c1707eStholo
950*43c1707eStholo wait_and_protect_globs ();
951*43c1707eStholo
952*43c1707eStholo /*--- getcwd has an OpenVMS extension that allows us to ---*/
953*43c1707eStholo /*--- get back Unix-style path names ---*/
954*43c1707eStholo (void) getcwd (CurWorkingDir, sizeof CurWorkingDir - 1, 0);
955*43c1707eStholo len = strlen (CurWorkingDir);
956*43c1707eStholo if ( len > 0 && CurWorkingDir[len-1] != '/') {
957*43c1707eStholo (void) strcat (CurWorkingDir, "/");
958*43c1707eStholo }
959*43c1707eStholo CurArg = 0;
960*43c1707eStholo ArgvList = largv;
961*43c1707eStholo MaxArgs = totfiles + 1;
962*43c1707eStholo
963*43c1707eStholo for (i=0; i<argc; ++i) {
964*43c1707eStholo char *arg = argv[i];
965*43c1707eStholo
966*43c1707eStholo if (arg != 0 && ( strchr(arg,' ') != 0
967*43c1707eStholo || strcmp(arg,".") == 0
968*43c1707eStholo || strcmp(arg,"..") == 0) ) {
969*43c1707eStholo if (CurArg < MaxArgs) {
970*43c1707eStholo ArgvList[CurArg++] = strdup(arg);
971*43c1707eStholo }
972*43c1707eStholo ++filesgotten;
973*43c1707eStholo }else if (arg != 0) {
974*43c1707eStholo char *p = arg;
975*43c1707eStholo int num;
976*43c1707eStholo /* Handle comma-separated filelists */
977*43c1707eStholo while ( (p=strchr(p,',')) != 0) {
978*43c1707eStholo *p = '\0';
979*43c1707eStholo num = decc$from_vms (arg, ew_add_file, 1);
980*43c1707eStholo if (num <= 0 && CurArg < MaxArgs) {
981*43c1707eStholo ArgvList[CurArg++] = strdup(arg);
982*43c1707eStholo }
983*43c1707eStholo filesgotten += num>0 ? num : 1;
984*43c1707eStholo *p++ = ',';
985*43c1707eStholo arg = p;
986*43c1707eStholo }
987*43c1707eStholo if (*arg != '\0') {
988*43c1707eStholo num = decc$from_vms (arg, ew_add_file, 1);
989*43c1707eStholo if (num <= 0 && CurArg < MaxArgs) {
990*43c1707eStholo ArgvList[CurArg++] = strdup(arg);
991*43c1707eStholo }
992*43c1707eStholo filesgotten += num>0 ? num : 1;
993*43c1707eStholo }
994*43c1707eStholo }
995*43c1707eStholo }
996*43c1707eStholo if (filesgotten != totfiles) {
997*43c1707eStholo /*--- Files must have been created/deleted here ---*/;
998*43c1707eStholo }
999*43c1707eStholo filesgotten = CurArg;
1000*43c1707eStholo
1001*43c1707eStholo release_globs();
1002*43c1707eStholo }
1003*43c1707eStholo if (!largv) {
1004*43c1707eStholo (*pargv) = malloc (sizeof(char *));
1005*43c1707eStholo if ((*pargv) != 0) {
1006*43c1707eStholo *(*pargv) = 0;
1007*43c1707eStholo }
1008*43c1707eStholo }else {
1009*43c1707eStholo (*pargv) = largv;
1010*43c1707eStholo }
1011*43c1707eStholo (*pargc) = largv ? filesgotten : 0;
1012*43c1707eStholo
1013*43c1707eStholo return;
1014*43c1707eStholo }
1015*43c1707eStholo
1016*43c1707eStholo #endif /* __VMS_VER >= 70200000 && __DECC_VER >= 50700000 */
1017