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