1 /* filesubr.c --- subroutines for dealing with files
2 Gratuitously adapted toward VMS quirks.
3
4 Jim Blandy <jimb@cyclic.com>
5 Benjamin J. Lee <benjamin@cyclic.com>
6
7 This file is part of GNU CVS.
8
9 GNU CVS is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by the
11 Free Software Foundation; either version 2, or (at your option) any
12 later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details. */
18
19 #include "cvs.h"
20
21 static int deep_remove_dir PROTO((const char *path));
22
23 /*
24 * Copies "from" to "to".
25 */
26 void
copy_file(from_file,to_file)27 copy_file (from_file, to_file)
28 const char *from_file;
29 const char *to_file;
30 {
31 char from[PATH_MAX], to[PATH_MAX];
32 struct stat sb;
33 struct utimbuf t;
34 int fdin, fdout;
35
36 /* Prefer local relative paths to files at expense of logical name
37 access to files. */
38
39 if (isabsolute(from_file))
40 strcpy(from, from_file);
41 else
42 sprintf(from, "./%s", from_file);
43
44 if (isabsolute(to_file))
45 strcpy(to, to_file);
46 else
47 sprintf(to, "./%s", to_file);
48
49 if (trace)
50 #ifdef SERVER_SUPPORT
51 (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
52 (server_active) ? 'S' : ' ', from, to);
53 #else
54 (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
55 #endif
56 if (noexec)
57 return;
58
59 if ((fdin = open (from, O_RDONLY)) < 0)
60 error (1, errno, "cannot open %s for copying", from);
61 if (fstat (fdin, &sb) < 0)
62 error (1, errno, "cannot fstat %s", from);
63 if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
64 error (1, errno, "cannot create %s for copying", to);
65 if (sb.st_size > 0)
66 {
67 char buf[BUFSIZ];
68 int n;
69
70 for (;;)
71 {
72 n = read (fdin, buf, sizeof(buf));
73 if (n == -1)
74 {
75 #ifdef EINTR
76 if (errno == EINTR)
77 continue;
78 #endif
79 error (1, errno, "cannot read file %s for copying", from);
80 }
81 else if (n == 0)
82 break;
83
84 if (write(fdout, buf, n) != n) {
85 error (1, errno, "cannot write file %s for copying", to);
86 }
87 }
88
89 #ifdef HAVE_FSYNC
90 if (fsync (fdout))
91 error (1, errno, "cannot fsync file %s after copying", to);
92 #endif
93 }
94
95 if (close (fdin) < 0)
96 error (0, errno, "cannot close %s", from);
97 if (close (fdout) < 0)
98 error (1, errno, "cannot close %s", to);
99
100 /* now, set the times for the copied file to match those of the original */
101 memset ((char *) &t, 0, sizeof (t));
102 t.actime = sb.st_atime;
103 t.modtime = sb.st_mtime;
104 (void) utime (to, &t);
105 }
106
107 /* FIXME-krp: these functions would benefit from caching the char * &
108 stat buf. */
109
110 /*
111 * Returns non-zero if the argument file is a directory, or is a symbolic
112 * link which points to a directory.
113 */
114 int
isdir(file)115 isdir (file)
116 const char *file;
117 {
118 struct stat sb;
119
120 if (stat (file, &sb) < 0)
121 return (0);
122 return (S_ISDIR (sb.st_mode));
123 }
124
125 /*
126 * Returns non-zero if the argument file is a symbolic link.
127 */
128 int
islink(file)129 islink (file)
130 const char *file;
131 {
132 #ifdef S_ISLNK
133 struct stat sb;
134
135 if (lstat (file, &sb) < 0)
136 return (0);
137 return (S_ISLNK (sb.st_mode));
138 #else
139 return (0);
140 #endif
141 }
142
143 /*
144 * Returns non-zero if the argument file exists.
145 */
146 int
isfile(file)147 isfile (file)
148 const char *file;
149 {
150 return isaccessible(file, F_OK);
151 }
152
153 /*
154 * Returns non-zero if the argument file is readable.
155 */
156 int
isreadable(file)157 isreadable (file)
158 const char *file;
159 {
160 return isaccessible(file, R_OK);
161 }
162
163 /*
164 * Returns non-zero if the argument file is writable.
165 */
166 int
iswritable(file)167 iswritable (file)
168 const char *file;
169 {
170 return isaccessible(file, W_OK);
171 }
172
173 /*
174 * Returns non-zero if the argument file is accessable according to
175 * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid
176 * bits set.
177 */
178 int
isaccessible(file,mode)179 isaccessible (file, mode)
180 const char *file;
181 const int mode;
182 {
183 #ifdef SETXID_SUPPORT
184 struct stat sb;
185 int umask = 0;
186 int gmask = 0;
187 int omask = 0;
188 int uid;
189
190 if (stat(file, &sb) == -1)
191 return 0;
192 if (mode == F_OK)
193 return 1;
194
195 uid = geteuid();
196 if (uid == 0) /* superuser */
197 {
198 if (mode & X_OK)
199 return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
200 else
201 return 1;
202 }
203
204 if (mode & R_OK)
205 {
206 umask |= S_IRUSR;
207 gmask |= S_IRGRP;
208 omask |= S_IROTH;
209 }
210 if (mode & W_OK)
211 {
212 umask |= S_IWUSR;
213 gmask |= S_IWGRP;
214 omask |= S_IWOTH;
215 }
216 if (mode & X_OK)
217 {
218 umask |= S_IXUSR;
219 gmask |= S_IXGRP;
220 omask |= S_IXOTH;
221 }
222
223 if (sb.st_uid == uid)
224 return (sb.st_mode & umask) == umask;
225 else if (sb.st_gid == getegid())
226 return (sb.st_mode & gmask) == gmask;
227 else
228 return (sb.st_mode & omask) == omask;
229 #else
230 return access(file, mode) == 0;
231 #endif
232 }
233
234 /*
235 * Open a file and die if it fails
236 */
237 FILE *
open_file(name,mode)238 open_file (name, mode)
239 const char *name;
240 const char *mode;
241 {
242 FILE *fp;
243
244 if ((fp = fopen (name, mode)) == NULL)
245 error (1, errno, "cannot open %s", name);
246 return (fp);
247 }
248
249 /*
250 * Make a directory and die if it fails
251 */
252 void
make_directory(name)253 make_directory (name)
254 const char *name;
255 {
256 struct stat sb;
257
258 if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
259 error (0, 0, "%s already exists but is not a directory", name);
260 if (!noexec && mkdir (name, 0777) < 0)
261 error (1, errno, "cannot make directory %s", name);
262 }
263
264 /*
265 * Make a path to the argument directory, printing a message if something
266 * goes wrong.
267 */
268 void
make_directories(name)269 make_directories (name)
270 const char *name;
271 {
272 char *cp;
273
274 if (noexec)
275 return;
276
277 if (mkdir (name, 0777) == 0 || errno == EEXIST)
278 return;
279 if (! existence_error (errno))
280 {
281 error (0, errno, "cannot make path to %s", name);
282 return;
283 }
284 if ((cp = strrchr (name, '/')) == NULL)
285 return;
286 *cp = '\0';
287 make_directories (name);
288 *cp++ = '/';
289 if (*cp == '\0')
290 return;
291 (void) mkdir (name, 0777);
292 }
293
294 /* Create directory NAME if it does not already exist; fatal error for
295 other errors. Returns 0 if directory was created; 1 if it already
296 existed. */
297 int
mkdir_if_needed(name)298 mkdir_if_needed (name)
299 char *name;
300 {
301 if (mkdir (name, 0777) < 0)
302 {
303 if (errno != EEXIST
304 #ifdef EACCESS
305 /* This was copied over from the OS/2 code; I would guess it
306 isn't needed here but that has not been verified. */
307 && errno != EACCESS
308 #endif
309 )
310 error (1, errno, "cannot make directory %s", name);
311 return 1;
312 }
313 return 0;
314 }
315
316 /*
317 * Change the mode of a file, either adding write permissions, or removing
318 * all write permissions. Either change honors the current umask setting.
319 */
320 void
xchmod(fname_file,writable)321 xchmod (fname_file, writable)
322 char *fname_file;
323 int writable;
324 {
325 char fname[PATH_MAX];
326 struct stat sb;
327 mode_t mode, oumask;
328
329 /* Prefer local relative paths to files at expense of logical name
330 access to files. */
331
332 if (isabsolute(fname_file))
333 strcpy(fname, fname_file);
334 else
335 sprintf(fname, "./%s", fname_file);
336
337 if (stat (fname, &sb) < 0)
338 {
339 if (!noexec)
340 error (0, errno, "cannot stat %s", fname);
341 return;
342 }
343 oumask = umask (0);
344 (void) umask (oumask);
345 if (writable)
346 {
347 mode = sb.st_mode | (~oumask
348 & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
349 | ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
350 | ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
351 }
352 else
353 {
354 mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
355 }
356
357 if (trace)
358 #ifdef SERVER_SUPPORT
359 (void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
360 (server_active) ? 'S' : ' ', fname, mode);
361 #else
362 (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
363 #endif
364 if (noexec)
365 return;
366
367 if (chmod (fname, mode) < 0)
368 error (0, errno, "cannot change mode of file %s", fname);
369 }
370
371 /*
372 * Rename a file and die if it fails
373 */
374 void
rename_file(from_file,to_file)375 rename_file (from_file, to_file)
376 const char *from_file;
377 const char *to_file;
378 {
379 char from[PATH_MAX], to[PATH_MAX];
380
381 /* Prefer local relative paths to files at expense of logical name
382 access to files. */
383
384 if (isabsolute(from_file))
385 strcpy(from, from_file);
386 else
387 sprintf(from, "./%s", from_file);
388
389 if (isabsolute(to_file))
390 strcpy(to, to_file);
391 else
392 sprintf(to, "./%s", to_file);
393
394 if (trace)
395 #ifdef SERVER_SUPPORT
396 (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
397 (server_active) ? 'S' : ' ', from, to);
398 #else
399 (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
400 #endif
401 if (noexec)
402 return;
403
404 if (rename (from, to) < 0)
405 error (1, errno, "cannot rename file %s to %s", from, to);
406 }
407
408 /*
409 * unlink a file, if possible.
410 */
411 int
unlink_file(f_file)412 unlink_file (f_file)
413 const char *f_file;
414 {
415 char f[PATH_MAX];
416
417 /* Prefer local relative paths to files at expense of logical name
418 access to files. */
419
420 if (isabsolute(f_file))
421 strcpy(f, f_file);
422 else
423 sprintf(f, "./%s", f_file);
424
425 if (trace)
426 #ifdef SERVER_SUPPORT
427 (void) fprintf (stderr, "%c-> unlink(%s)\n",
428 (server_active) ? 'S' : ' ', f);
429 #else
430 (void) fprintf (stderr, "-> unlink(%s)\n", f);
431 #endif
432 if (noexec)
433 return (0);
434
435 return (vms_unlink (f));
436 }
437
438 /*
439 * Unlink a file or dir, if possible. If it is a directory do a deep
440 * removal of all of the files in the directory. Return -1 on error
441 * (in which case errno is set).
442 */
443 int
unlink_file_dir(f_file)444 unlink_file_dir (f_file)
445 const char *f_file;
446 {
447 char f[PATH_MAX];
448
449 /* Prefer local relative paths to files at expense of logical name
450 access to files. */
451
452 if (isabsolute(f_file))
453 strcpy(f, f_file);
454 else
455 sprintf(f, "./%s", f_file);
456
457 if (trace)
458 #ifdef SERVER_SUPPORT
459 (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
460 (server_active) ? 'S' : ' ', f);
461 #else
462 (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
463 #endif
464 if (noexec)
465 return (0);
466
467 if (vms_unlink (f) != 0)
468 {
469 /* under NEXTSTEP errno is set to return EPERM if
470 * the file is a directory,or if the user is not
471 * allowed to read or write to the file.
472 * [This is probably a bug in the O/S]
473 * other systems will return EISDIR to indicate
474 * that the path is a directory.
475 */
476 if (errno == EISDIR || errno == EPERM)
477 return deep_remove_dir (f);
478 else
479 /* The file wasn't a directory and some other
480 * error occured
481 */
482 return -1;
483 }
484 /* We were able to remove the file from the disk */
485 return 0;
486 }
487
488 /* Remove a directory and everything it contains. Returns 0 for
489 * success, -1 for failure (in which case errno is set).
490 */
491
492 static int
deep_remove_dir(path)493 deep_remove_dir (path)
494 const char *path;
495 {
496 DIR *dirp;
497 struct dirent *dp;
498 char buf[PATH_MAX];
499
500 if (rmdir (path) != 0 && (errno == ENOTEMPTY || errno == EEXIST))
501 {
502 if ((dirp = CVS_OPENDIR (path)) == NULL)
503 /* If unable to open the directory return
504 * an error
505 */
506 return -1;
507
508 while ((dp = CVS_READDIR (dirp)) != NULL)
509 {
510 if (strcmp (dp->d_name, ".") == 0 ||
511 strcmp (dp->d_name, "..") == 0)
512 continue;
513
514 sprintf (buf, "%s/%s", path, dp->d_name);
515
516 if (vms_unlink (buf) != 0 )
517 {
518 if (errno == EISDIR || errno == EPERM)
519 {
520 if (deep_remove_dir (buf))
521 {
522 CVS_CLOSEDIR (dirp);
523 return -1;
524 }
525 }
526 else
527 {
528 /* buf isn't a directory, or there are
529 * some sort of permision problems
530 */
531 CVS_CLOSEDIR (dirp);
532 return -1;
533 }
534 }
535 }
536 CVS_CLOSEDIR (dirp);
537 return rmdir (path);
538 }
539
540 /* Was able to remove the directory return 0 */
541 return 0;
542 }
543
544 /* Read NCHARS bytes from descriptor FD into BUF.
545 Return the number of characters successfully read.
546 The number returned is always NCHARS unless end-of-file or error. */
547 static size_t
block_read(fd,buf,nchars)548 block_read (fd, buf, nchars)
549 int fd;
550 char *buf;
551 size_t nchars;
552 {
553 char *bp = buf;
554 size_t nread;
555
556 do
557 {
558 nread = read (fd, bp, nchars);
559 if (nread == (size_t)-1)
560 {
561 #ifdef EINTR
562 if (errno == EINTR)
563 continue;
564 #endif
565 return (size_t)-1;
566 }
567
568 if (nread == 0)
569 break;
570
571 bp += nread;
572 nchars -= nread;
573 } while (nchars != 0);
574
575 return bp - buf;
576 }
577
578
579 /*
580 * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
581 */
582 int
xcmp(file1_file,file2_file)583 xcmp (file1_file, file2_file)
584 const char *file1_file;
585 const char *file2_file;
586 {
587 char file1[PATH_MAX], file2[PATH_MAX];
588 char *buf1, *buf2;
589 struct stat sb1, sb2;
590 int fd1, fd2;
591 int ret;
592
593 /* Prefer local relative paths to files at expense of logical name
594 access to files. */
595
596 if (isabsolute(file1_file))
597 strcpy(file1, file1_file);
598 else
599 sprintf(file1, "./%s", file1_file);
600
601 if (isabsolute(file2_file))
602 strcpy(file2, file2_file);
603 else
604 sprintf(file2, "./%s", file2_file);
605
606 if ((fd1 = open (file1, O_RDONLY)) < 0)
607 error (1, errno, "cannot open file %s for comparing", file1);
608 if ((fd2 = open (file2, O_RDONLY)) < 0)
609 error (1, errno, "cannot open file %s for comparing", file2);
610 if (fstat (fd1, &sb1) < 0)
611 error (1, errno, "cannot fstat %s", file1);
612 if (fstat (fd2, &sb2) < 0)
613 error (1, errno, "cannot fstat %s", file2);
614
615 /* A generic file compare routine might compare st_dev & st_ino here
616 to see if the two files being compared are actually the same file.
617 But that won't happen in CVS, so we won't bother. */
618
619 if (sb1.st_size != sb2.st_size)
620 ret = 1;
621 else if (sb1.st_size == 0)
622 ret = 0;
623 else
624 {
625 /* FIXME: compute the optimal buffer size by computing the least
626 common multiple of the files st_blocks field */
627 size_t buf_size = 8 * 1024;
628 size_t read1;
629 size_t read2;
630
631 buf1 = xmalloc (buf_size);
632 buf2 = xmalloc (buf_size);
633
634 do
635 {
636 read1 = block_read (fd1, buf1, buf_size);
637 if (read1 == (size_t)-1)
638 error (1, errno, "cannot read file %s for comparing", file1);
639
640 read2 = block_read (fd2, buf2, buf_size);
641 if (read2 == (size_t)-1)
642 error (1, errno, "cannot read file %s for comparing", file2);
643
644 /* assert (read1 == read2); */
645
646 ret = memcmp(buf1, buf2, read1);
647 } while (ret == 0 && read1 == buf_size);
648
649 free (buf1);
650 free (buf2);
651 }
652
653 (void) close (fd1);
654 (void) close (fd2);
655 return (ret);
656 }
657
658 unsigned char
659 VMS_filename_classes[] =
660 {
661 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
662 0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
663 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
664 0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
665 0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
666 0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
667 0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
668 0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
669 0x40,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
670 0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
671 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
672 0x78,0x79,0x7a,0x5b, 0x5c,0x5d,0x5e,0x5f,
673 0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
674 0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
675 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
676 0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f,
677 0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
678 0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
679 0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
680 0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f,
681 0xa0,0xa1,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,
682 0xa8,0xa9,0xaa,0xab, 0xac,0xad,0xae,0xaf,
683 0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
684 0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
685 0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
686 0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
687 0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
688 0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
689 0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
690 0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
691 0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
692 0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
693 };
694
695 /* Like strcmp, but with the appropriate tweaks for file names.
696 Under VMS, filenames are case-insensitive but case-preserving.
697 FIXME: this should compare y.tab.c equal with y_tab.c, at least
698 if fnfold is modified (see below). */
699 int
fncmp(const char * n1,const char * n2)700 fncmp (const char *n1, const char *n2)
701 {
702 while (*n1 && *n2
703 && (VMS_filename_classes[(unsigned char) *n1]
704 == VMS_filename_classes[(unsigned char) *n2]))
705 n1++, n2++;
706 return (VMS_filename_classes[(unsigned char) *n1]
707 - VMS_filename_classes[(unsigned char) *n2]);
708 }
709
710 /* Fold characters in FILENAME to their canonical forms. FIXME: this
711 probably should be mapping y.tab.c to y_tab.c but first we have to
712 figure out whether fnfold is the right hook for that functionality
713 (probable answer: yes, but it should not fold case on OS/2, VMS, or
714 NT. You see, fnfold isn't called anywhere, so we can define it to
715 mean whatever makes sense. Of course to solve the VMS y.tab.c
716 problem we'd need to call it where appropriate. It would need to
717 be redocumented as "fold to a form we can create in the filesystem"
718 rather than "canonical form"). The idea is that files we create
719 would get thusly munged, but CVS can cope with their names being
720 different the same way that the NT port copes with it if the user
721 renames a file from "foo" to "FOO".
722
723 Alternately, this kind of handling could/should go into CVS_FOPEN
724 and friends (if we want to do it like the Mac port, anyway). */
725 void
fnfold(char * filename)726 fnfold (char *filename)
727 {
728 while (*filename)
729 {
730 *filename = FOLD_FN_CHAR (*filename);
731 filename++;
732 }
733 }
734
735 /* Generate a unique temporary filename. Returns a pointer to a newly
736 malloc'd string containing the name. Returns successfully or not at
737 all. */
738 char *
cvs_temp_name()739 cvs_temp_name ()
740 {
741 char value[L_tmpnam + 1];
742 char *retval;
743
744 /* FIXME: what is the VMS equivalent to TMPDIR? */
745 retval = tmpnam (value);
746 if (retval == NULL)
747 error (1, errno, "cannot generate temporary filename");
748 return xstrdup (retval);
749 }
750
751 /* Return non-zero iff FILENAME is absolute.
752 Trivial under Unix, but more complicated under other systems. */
753 int
isabsolute(filename)754 isabsolute (filename)
755 const char *filename;
756 {
757 if(filename[0] == '/'
758 || filename[0] == '['
759 || filename[0] == '<'
760 || strchr(filename, ':'))
761 return 1;
762 else
763 return 0;
764 }
765
766
767 /* Return a pointer into PATH's last component. */
768 char *
last_component(path)769 last_component (path)
770 char *path;
771 {
772 char *last = strrchr (path, '/');
773
774 if (last && (last != path))
775 return last + 1;
776 else
777 return path;
778 }
779
780 /* Return the home directory. Returns a pointer to storage
781 managed by this function or its callees (currently getenv). */
782 char *
get_homedir()783 get_homedir ()
784 {
785 return getenv ("HOME");
786 }
787
788 #ifndef __VMS_VER
789 #define __VMS_VER 0
790 #endif
791 #ifndef __DECC_VER
792 #define __DECC_VER 0
793 #endif
794
795 #if __VMS_VER < 70200000 || __DECC_VER < 50700000
796 /* See cvs.h for description. On VMS this currently does nothing, although
797 I think we should be expanding wildcards here. */
798 void
expand_wild(argc,argv,pargc,pargv)799 expand_wild (argc, argv, pargc, pargv)
800 int argc;
801 char **argv;
802 int *pargc;
803 char ***pargv;
804 {
805 int i;
806 *pargc = argc;
807 *pargv = (char **) xmalloc (argc * sizeof (char *));
808 for (i = 0; i < argc; ++i)
809 (*pargv)[i] = xstrdup (argv[i]);
810 }
811
812 #else /* __VMS_VER >= 70200000 && __DECC_VER >= 50700000 */
813
814 /* These global variables are necessary to pass information from the
815 * routine that calls decc$from_vms into the callback routine. In a
816 * multi-threaded environment, access to these variables MUST be
817 * serialized.
818 */
819 static char CurWorkingDir[PATH_MAX+1];
820 static char **ArgvList;
821 static int CurArg;
822 static int MaxArgs;
823
ew_no_op(char * fname)824 static int ew_no_op (char *fname) {
825 (void) fname; /* Shut the compiler up */
826 return 1; /* Continue */
827 }
828
ew_add_file(char * fname)829 static int ew_add_file (char *fname) {
830 char *lastslash, *firstper;
831 int i;
832
833 if (strncmp(fname,CurWorkingDir,strlen(CurWorkingDir)) == 0) {
834 fname += strlen(CurWorkingDir);
835 }
836 lastslash = strrchr(fname,'/');
837 if (!lastslash) {
838 lastslash = fname;
839 }
840 if ((firstper=strchr(lastslash,'.')) != strrchr(lastslash,'.')) {
841 /* We have two periods -- one is to separate the version off */
842 *strrchr(fname,'.') = '\0';
843 }
844 if (firstper && firstper[1]=='\0') {
845 *firstper = '\0';
846 }
847 /* The following code is to insure that no duplicates appear,
848 * because most of the time it will just be a different version
849 */
850 for (i=0; i<CurArg && strcmp(ArgvList[i],fname)!=0; ++i) {
851 ;
852 }
853 if (i==CurArg && CurArg<MaxArgs) {
854 ArgvList[CurArg++] = strdup(fname);
855 }
856 return ArgvList[CurArg-1] != 0; /* Stop if we couldn't dup the string */
857 }
858
859 /* The following two routines are meant to allow future versions of new_arglist
860 * routine to be multi-thread-safe. It will be necessary in that environment
861 * to serialize access to CurWorkingDir, ArgvList, MaxArg, and CurArg. We
862 * currently don't do any multi-threaded programming, so right now these
863 * routines are no-ops.
864 */
wait_and_protect_globs(void)865 static void wait_and_protect_globs (void) {
866 return;
867 }
868
release_globs(void)869 static void release_globs (void) {
870 return;
871 }
872
873 /*pf---------------------------------------------------------------- expand_wild
874 *
875 * New Argument List - (SDS)
876 *
877 * DESCRIPTION:
878 * This routine takes the argc, argv passed in from main() and returns a
879 * new argc, argv list, which simulates (to an extent) Unix-Style filename
880 * globbing with VMS wildcards. The key difference is that it will return
881 * Unix-style filenames, i.e., no VMS file version numbers. The complexity
882 * comes from the desire to not simply allocate 10000 argv entries.
883 *
884 * INPUTS:
885 * argc - The integer argc passed into main
886 * argv - The pointer to the array of char*'s passed into main
887 *
888 * OUTPUTS:
889 * pargv - A pointer to a (char **) to hold the new argv list
890 * pargc - A pointer to an int to hold the new argc
891 *
892 * RETURNS:
893 * NONE
894 *
895 * SIDE EFFECTS:
896 * This routine will normally modify the global statics CurArg, MaxArg,
897 * ArgvList, and CurWorkingDir.
898 *
899 * NOTES:
900 * It is ok for &argc == pargc and &argv == pargv.
901 *
902 *------------------------------------------------------------------------------
903 */
expand_wild(int argc,char ** argv,int * pargc,char *** pargv)904 void expand_wild (int argc, char **argv, int *pargc, char ***pargv) {
905 int totfiles, filesgotten;
906 int i;
907 int largc;
908 char **largv;
909
910 /* This first loop is to find out AT MOST how big to make the
911 * pargv array.
912 */
913 for (totfiles=0,i=0; i<argc; ++i) {
914 char *arg = argv[i];
915
916 if (arg != 0 && ( strchr(arg,' ') != 0
917 || strcmp(arg,".") == 0
918 || strcmp(arg,"..") == 0) ) {
919 ++totfiles;
920 }else if (arg != 0) {
921 int num;
922 char *p = arg;
923 /* Handle comma-separated filelists */
924 while ( (p=strchr(p,',')) != 0) {
925 *p = '\0';
926 num = decc$from_vms (arg, ew_no_op, 1);
927 totfiles += num>0 ? num : 1;
928 *p++ = ',';
929 arg = p;
930 }
931 if (*arg != '\0') {
932 num = decc$from_vms (arg, ew_no_op, 1);
933 totfiles += num>0 ? num : 1;
934 }
935 }
936 }
937 largv = 0;
938 if (totfiles) {
939 largv = malloc (sizeof*largv * (totfiles + 1));
940 }
941 filesgotten = 0;
942 if (largv != 0) {
943 int len;
944 /* All bits set to zero may not be a NULL ptr */
945 for (i=totfiles; --i>=0; ) {
946 largv[i] = 0;
947 }
948 largv[totfiles] = 0;
949
950 wait_and_protect_globs ();
951
952 /*--- getcwd has an OpenVMS extension that allows us to ---*/
953 /*--- get back Unix-style path names ---*/
954 (void) getcwd (CurWorkingDir, sizeof CurWorkingDir - 1, 0);
955 len = strlen (CurWorkingDir);
956 if ( len > 0 && CurWorkingDir[len-1] != '/') {
957 (void) strcat (CurWorkingDir, "/");
958 }
959 CurArg = 0;
960 ArgvList = largv;
961 MaxArgs = totfiles + 1;
962
963 for (i=0; i<argc; ++i) {
964 char *arg = argv[i];
965
966 if (arg != 0 && ( strchr(arg,' ') != 0
967 || strcmp(arg,".") == 0
968 || strcmp(arg,"..") == 0) ) {
969 if (CurArg < MaxArgs) {
970 ArgvList[CurArg++] = strdup(arg);
971 }
972 ++filesgotten;
973 }else if (arg != 0) {
974 char *p = arg;
975 int num;
976 /* Handle comma-separated filelists */
977 while ( (p=strchr(p,',')) != 0) {
978 *p = '\0';
979 num = decc$from_vms (arg, ew_add_file, 1);
980 if (num <= 0 && CurArg < MaxArgs) {
981 ArgvList[CurArg++] = strdup(arg);
982 }
983 filesgotten += num>0 ? num : 1;
984 *p++ = ',';
985 arg = p;
986 }
987 if (*arg != '\0') {
988 num = decc$from_vms (arg, ew_add_file, 1);
989 if (num <= 0 && CurArg < MaxArgs) {
990 ArgvList[CurArg++] = strdup(arg);
991 }
992 filesgotten += num>0 ? num : 1;
993 }
994 }
995 }
996 if (filesgotten != totfiles) {
997 /*--- Files must have been created/deleted here ---*/;
998 }
999 filesgotten = CurArg;
1000
1001 release_globs();
1002 }
1003 if (!largv) {
1004 (*pargv) = malloc (sizeof(char *));
1005 if ((*pargv) != 0) {
1006 *(*pargv) = 0;
1007 }
1008 }else {
1009 (*pargv) = largv;
1010 }
1011 (*pargc) = largv ? filesgotten : 0;
1012
1013 return;
1014 }
1015
1016 #endif /* __VMS_VER >= 70200000 && __DECC_VER >= 50700000 */
1017