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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 * 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 * 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 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 824 static int ew_no_op (char *fname) { 825 (void) fname; /* Shut the compiler up */ 826 return 1; /* Continue */ 827 } 828 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 */ 865 static void wait_and_protect_globs (void) { 866 return; 867 } 868 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 */ 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