1 /*- 2 * Copyright (c) 1992 Keith Muller. 3 * Copyright (c) 1992, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * Keith Muller of the University of California, San Diego. 8 * 9 * %sccs.include.redist.c% 10 */ 11 12 #ifndef lint 13 static char sccsid[] = "@(#)pat_rep.c 8.2 (Berkeley) 04/18/94"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/time.h> 18 #include <sys/stat.h> 19 #include <sys/param.h> 20 #include <stdio.h> 21 #include <ctype.h> 22 #include <string.h> 23 #include <unistd.h> 24 #include <stdlib.h> 25 #ifdef NET2_REGEX 26 #include <regexp.h> 27 #else 28 #include <regex.h> 29 #endif 30 #include "pax.h" 31 #include "pat_rep.h" 32 #include "extern.h" 33 34 /* 35 * routines to handle pattern matching, name modification (regular expression 36 * substitution and interactive renames), and destination name modification for 37 * copy (-rw). Both file name and link names are adjusted as required in these 38 * routines. 39 */ 40 41 #define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */ 42 static PATTERN *pathead = NULL; /* file pattern match list head */ 43 static PATTERN *pattail = NULL; /* file pattern match list tail */ 44 static REPLACE *rephead = NULL; /* replacement string list head */ 45 static REPLACE *reptail = NULL; /* replacement string list tail */ 46 47 static int rep_name __P((char *, int *, int)); 48 static int tty_rename __P((register ARCHD *)); 49 static int fix_path __P((char *, int *, char *, int)); 50 static int fn_match __P((register char *, register char *, char **)); 51 static char * range_match __P((register char *, register int)); 52 #ifdef NET2_REGEX 53 static int resub __P((regexp *, char *, char *, register char *)); 54 #else 55 static int resub __P((regex_t *, regmatch_t *, char *, char *, char *)); 56 #endif 57 58 /* 59 * rep_add() 60 * parses the -s replacement string; compiles the regular expression 61 * and stores the compiled value and it's replacement string together in 62 * replacement string list. Input to this function is of the form: 63 * /old/new/pg 64 * The first char in the string specifies the delimiter used by this 65 * replacement string. "Old" is a regular expression in "ed" format which 66 * is compiled by regcomp() and is applied to filenames. "new" is the 67 * substitution string; p and g are options flags for printing and global 68 * replacement (over the single filename) 69 * Return: 70 * 0 if a proper replacement string and regular expression was added to 71 * the list of replacement patterns; -1 otherwise. 72 */ 73 74 #if __STDC__ 75 int 76 rep_add(register char *str) 77 #else 78 int 79 rep_add(str) 80 register char *str; 81 #endif 82 { 83 register char *pt1; 84 register char *pt2; 85 register REPLACE *rep; 86 # ifndef NET2_REGEX 87 register int res; 88 char rebuf[BUFSIZ]; 89 # endif 90 91 /* 92 * throw out the bad parameters 93 */ 94 if ((str == NULL) || (*str == '\0')) { 95 warn(1, "Empty replacement string"); 96 return(-1); 97 } 98 99 /* 100 * first character in the string specifies what the delimiter is for 101 * this expression 102 */ 103 if ((pt1 = strchr(str+1, *str)) == NULL) { 104 warn(1, "Invalid replacement string %s", str); 105 return(-1); 106 } 107 108 /* 109 * allocate space for the node that handles this replacement pattern 110 * and split out the regular expression and try to compile it 111 */ 112 if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) { 113 warn(1, "Unable to allocate memory for replacement string"); 114 return(-1); 115 } 116 117 *pt1 = '\0'; 118 # ifdef NET2_REGEX 119 if ((rep->rcmp = regcomp(str+1)) == NULL) { 120 # else 121 if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { 122 regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); 123 warn(1, "%s while compiling regular expression %s", rebuf, str); 124 # endif 125 (void)free((char *)rep); 126 return(-1); 127 } 128 129 /* 130 * put the delimiter back in case we need an error message and 131 * locate the delimiter at the end of the replacement string 132 * we then point the node at the new substitution string 133 */ 134 *pt1++ = *str; 135 if ((pt2 = strchr(pt1, *str)) == NULL) { 136 # ifdef NET2_REGEX 137 (void)free((char *)rep->rcmp); 138 # else 139 regfree(&(rep->rcmp)); 140 # endif 141 (void)free((char *)rep); 142 warn(1, "Invalid replacement string %s", str); 143 return(-1); 144 } 145 146 *pt2 = '\0'; 147 rep->nstr = pt1; 148 pt1 = pt2++; 149 rep->flgs = 0; 150 151 /* 152 * set the options if any 153 */ 154 while (*pt2 != '\0') { 155 switch(*pt2) { 156 case 'g': 157 case 'G': 158 rep->flgs |= GLOB; 159 break; 160 case 'p': 161 case 'P': 162 rep->flgs |= PRNT; 163 break; 164 default: 165 # ifdef NET2_REGEX 166 (void)free((char *)rep->rcmp); 167 # else 168 regfree(&(rep->rcmp)); 169 # endif 170 (void)free((char *)rep); 171 *pt1 = *str; 172 warn(1, "Invalid replacement string option %s", str); 173 return(-1); 174 } 175 ++pt2; 176 } 177 178 /* 179 * all done, link it in at the end 180 */ 181 rep->fow = NULL; 182 if (rephead == NULL) { 183 reptail = rephead = rep; 184 return(0); 185 } 186 reptail->fow = rep; 187 reptail = rep; 188 return(0); 189 } 190 191 /* 192 * pat_add() 193 * add a pattern match to the pattern match list. Pattern matches are used 194 * to select which archive members are extracted. (They appear as 195 * arguments to pax in the list and read modes). If no patterns are 196 * supplied to pax, all members in the archive will be selected (and the 197 * pattern match list is empty). 198 * Return: 199 * 0 if the pattern was added to the list, -1 otherwise 200 */ 201 202 #if __STDC__ 203 int 204 pat_add(char *str) 205 #else 206 int 207 pat_add(str) 208 char *str; 209 #endif 210 { 211 register PATTERN *pt; 212 213 /* 214 * throw out the junk 215 */ 216 if ((str == NULL) || (*str == '\0')) { 217 warn(1, "Empty pattern string"); 218 return(-1); 219 } 220 221 /* 222 * allocate space for the pattern and store the pattern. the pattern is 223 * part of argv so do not bother to copy it, just point at it. Add the 224 * node to the end of the pattern list 225 */ 226 if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { 227 warn(1, "Unable to allocate memory for pattern string"); 228 return(-1); 229 } 230 231 pt->pstr = str; 232 pt->pend = NULL; 233 pt->plen = strlen(str); 234 pt->fow = NULL; 235 pt->flgs = 0; 236 if (pathead == NULL) { 237 pattail = pathead = pt; 238 return(0); 239 } 240 pattail->fow = pt; 241 pattail = pt; 242 return(0); 243 } 244 245 /* 246 * pat_chk() 247 * complain if any the user supplied pattern did not result in a match to 248 * a selected archive member. 249 */ 250 251 #if __STDC__ 252 void 253 pat_chk(void) 254 #else 255 void 256 pat_chk() 257 #endif 258 { 259 register PATTERN *pt; 260 register int wban = 0; 261 262 /* 263 * walk down the list checking the flags to make sure MTCH was set, 264 * if not complain 265 */ 266 for (pt = pathead; pt != NULL; pt = pt->fow) { 267 if (pt->flgs & MTCH) 268 continue; 269 if (!wban) { 270 warn(1, "WARNING! These patterns were not matched:"); 271 ++wban; 272 } 273 (void)fprintf(stderr, "%s\n", pt->pstr); 274 } 275 } 276 277 /* 278 * pat_sel() 279 * the archive member which matches a pattern was selected. Mark the 280 * pattern as having selected an archive member. arcn->pat points at the 281 * pattern that was matched. arcn->pat is set in pat_match() 282 * 283 * NOTE: When the -c option is used, we are called when there was no match 284 * by pat_match() (that means we did match before the inverted sense of 285 * the logic). Now this seems really strange at first, but with -c we 286 * need to keep track of those patterns that cause a archive member to NOT 287 * be selected (it found an archive member with a specified pattern) 288 * Return: 289 * 0 if the pattern pointed at by arcn->pat was tagged as creating a 290 * match, -1 otherwise. 291 */ 292 293 #if __STDC__ 294 int 295 pat_sel(register ARCHD *arcn) 296 #else 297 int 298 pat_sel(arcn) 299 register ARCHD *arcn; 300 #endif 301 { 302 register PATTERN *pt; 303 register PATTERN **ppt; 304 register int len; 305 306 /* 307 * if no patterns just return 308 */ 309 if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) 310 return(0); 311 312 /* 313 * when we are NOT limited to a single match per pattern mark the 314 * pattern and return 315 */ 316 if (!nflag) { 317 pt->flgs |= MTCH; 318 return(0); 319 } 320 321 /* 322 * we reach this point only when we allow a single selected match per 323 * pattern, if the pattern matches a directory and we do not have -d 324 * (dflag) we are done with this pattern. We may also be handed a file 325 * in the subtree of a directory. in that case when we are operating 326 * with -d, this pattern was already selected and we are done 327 */ 328 if (pt->flgs & DIR_MTCH) 329 return(0); 330 331 if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { 332 /* 333 * ok we matched a directory and we are allowing 334 * subtree matches but because of the -n only its children will 335 * match. This is tagged as a DIR_MTCH type. 336 * WATCH IT, the code assumes that pt->pend points 337 * into arcn->name and arcn->name has not been modified. 338 * If not we will have a big mess. Yup this is another kludge 339 */ 340 341 /* 342 * if this was a prefix match, remove trailing part of path 343 * so we can copy it. Future matches will be exact prefix match 344 */ 345 if (pt->pend != NULL) 346 *pt->pend = '\0'; 347 348 if ((pt->pstr = strdup(arcn->name)) == NULL) { 349 warn(1, "Pattern select out of memory"); 350 if (pt->pend != NULL) 351 *pt->pend = '/'; 352 pt->pend = NULL; 353 return(-1); 354 } 355 356 /* 357 * put the trailing / back in the source string 358 */ 359 if (pt->pend != NULL) { 360 *pt->pend = '/'; 361 pt->pend = NULL; 362 } 363 pt->plen = strlen(pt->pstr); 364 365 /* 366 * strip off any trailing /, this should really never happen 367 */ 368 len = pt->plen - 1; 369 if (*(pt->pstr + len) == '/') { 370 *(pt->pstr + len) = '\0'; 371 pt->plen = len; 372 } 373 pt->flgs = DIR_MTCH | MTCH; 374 arcn->pat = pt; 375 return(0); 376 } 377 378 /* 379 * we are then done with this pattern, so we delete it from the list 380 * because it can never be used for another match. 381 * Seems kind of strange to do for a -c, but the pax spec is really 382 * vague on the interaction of -c -n and -d. We assume that when -c 383 * and the pattern rejects a member (i.e. it matched it) it is done. 384 * In effect we place the order of the flags as having -c last. 385 */ 386 pt = pathead; 387 ppt = &pathead; 388 while ((pt != NULL) && (pt != arcn->pat)) { 389 ppt = &(pt->fow); 390 pt = pt->fow; 391 } 392 393 if (pt == NULL) { 394 /* 395 * should never happen.... 396 */ 397 warn(1, "Pattern list inconsistant"); 398 return(-1); 399 } 400 *ppt = pt->fow; 401 (void)free((char *)pt); 402 arcn->pat = NULL; 403 return(0); 404 } 405 406 /* 407 * pat_match() 408 * see if this archive member matches any supplied pattern, if a match 409 * is found, arcn->pat is set to point at the potential pattern. Later if 410 * this archive member is "selected" we process and mark the pattern as 411 * one which matched a selected archive member (see pat_sel()) 412 * Return: 413 * 0 if this archive member should be processed, 1 if it should be 414 * skipped and -1 if we are done with all patterns (and pax should quit 415 * looking for more members) 416 */ 417 418 #if __STDC__ 419 int 420 pat_match(register ARCHD *arcn) 421 #else 422 int 423 pat_match(arcn) 424 register ARCHD *arcn; 425 #endif 426 { 427 register PATTERN *pt; 428 429 arcn->pat = NULL; 430 431 /* 432 * if there are no more patterns and we have -n (and not -c) we are 433 * done. otherwise with no patterns to match, matches all 434 */ 435 if (pathead == NULL) { 436 if (nflag && !cflag) 437 return(-1); 438 return(0); 439 } 440 441 /* 442 * have to search down the list one at a time looking for a match. 443 */ 444 pt = pathead; 445 while (pt != NULL) { 446 /* 447 * check for a file name match unless we have DIR_MTCH set in 448 * this pattern then we want a prefix match 449 */ 450 if (pt->flgs & DIR_MTCH) { 451 /* 452 * this pattern was matched before to a directory 453 * as we must have -n set for this (but not -d). We can 454 * only match CHILDREN of that directory so we must use 455 * an exact prefix match (no wildcards). 456 */ 457 if ((arcn->name[pt->plen] == '/') && 458 (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) 459 break; 460 } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) 461 break; 462 pt = pt->fow; 463 } 464 465 /* 466 * return the result, remember that cflag (-c) inverts the sense of a 467 * match 468 */ 469 if (pt == NULL) 470 return(cflag ? 0 : 1); 471 472 /* 473 * we had a match, now when we invert the sense (-c) we reject this 474 * member. However we have to tag the pattern a being successful, (in a 475 * match, not in selecting a archive member) so we call pat_sel() here. 476 */ 477 arcn->pat = pt; 478 if (!cflag) 479 return(0); 480 481 if (pat_sel(arcn) < 0) 482 return(-1); 483 arcn->pat = NULL; 484 return(1); 485 } 486 487 /* 488 * fn_match() 489 * Return: 490 * 0 if this archive member should be processed, 1 if it should be 491 * skipped and -1 if we are done with all patterns (and pax should quit 492 * looking for more members) 493 * Note: *pend may be changed to show where the prefix ends. 494 */ 495 496 #if __STDC__ 497 static int 498 fn_match(register char *pattern, register char *string, char **pend) 499 #else 500 static int 501 fn_match(pattern, string, pend) 502 register char *pattern; 503 register char *string; 504 char **pend; 505 #endif 506 { 507 register char c; 508 char test; 509 510 *pend = NULL; 511 for (;;) { 512 switch (c = *pattern++) { 513 case '\0': 514 /* 515 * Ok we found an exact match 516 */ 517 if (*string == '\0') 518 return(0); 519 520 /* 521 * Check if it is a prefix match 522 */ 523 if ((dflag == 1) || (*string != '/')) 524 return(-1); 525 526 /* 527 * It is a prefix match, remember where the trailing 528 * / is located 529 */ 530 *pend = string; 531 return(0); 532 case '?': 533 if ((test = *string++) == '\0') 534 return (-1); 535 break; 536 case '*': 537 c = *pattern; 538 /* 539 * Collapse multiple *'s. 540 */ 541 while (c == '*') 542 c = *++pattern; 543 544 /* 545 * Optimized hack for pattern with a * at the end 546 */ 547 if (c == '\0') 548 return (0); 549 550 /* 551 * General case, use recursion. 552 */ 553 while ((test = *string) != '\0') { 554 if (!fn_match(pattern, string, pend)) 555 return (0); 556 ++string; 557 } 558 return (-1); 559 case '[': 560 /* 561 * range match 562 */ 563 if (((test = *string++) == '\0') || 564 ((pattern = range_match(pattern, test)) == NULL)) 565 return (-1); 566 break; 567 case '\\': 568 default: 569 if (c != *string++) 570 return (-1); 571 break; 572 } 573 } 574 /* NOTREACHED */ 575 } 576 577 #ifdef __STDC__ 578 static char * 579 range_match(register char *pattern, register int test) 580 #else 581 static char * 582 range_match(pattern, test) 583 register char *pattern; 584 register int test; 585 #endif 586 { 587 register char c; 588 register char c2; 589 int negate; 590 int ok = 0; 591 592 if (negate = (*pattern == '!')) 593 ++pattern; 594 595 while ((c = *pattern++) != ']') { 596 /* 597 * Illegal pattern 598 */ 599 if (c == '\0') 600 return (NULL); 601 602 if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') && 603 (c2 != ']')) { 604 if ((c <= test) && (test <= c2)) 605 ok = 1; 606 pattern += 2; 607 } else if (c == test) 608 ok = 1; 609 } 610 return (ok == negate ? NULL : pattern); 611 } 612 613 /* 614 * mod_name() 615 * modify a selected file name. first attempt to apply replacement string 616 * expressions, then apply interactive file rename. We apply replacement 617 * string expressions to both filenames and file links (if we didn't the 618 * links would point to the wrong place, and we could never be able to 619 * move an archive that has a file link in it). When we rename files 620 * interactively, we store that mapping (old name to user input name) so 621 * if we spot any file links to the old file name in the future, we will 622 * know exactly how to fix the file link. 623 * Return: 624 * 0 continue to process file, 1 skip this file, -1 pax is finished 625 */ 626 627 #if __STDC__ 628 int 629 mod_name(register ARCHD *arcn) 630 #else 631 int 632 mod_name(arcn) 633 register ARCHD *arcn; 634 #endif 635 { 636 register int res = 0; 637 638 /* 639 * IMPORTANT: We have a problem. what do we do with symlinks? 640 * Modifying a hard link name makes sense, as we know the file it 641 * points at should have been seen already in the archive (and if it 642 * wasn't seen because of a read error or a bad archive, we lose 643 * anyway). But there are no such requirements for symlinks. On one 644 * hand the symlink that refers to a file in the archive will have to 645 * be modified to so it will still work at its new location in the 646 * file system. On the other hand a symlink that points elsewhere (and 647 * should continue to do so) should not be modified. There is clearly 648 * no perfect solution here. So we handle them like hardlinks. Clearly 649 * a replacement made by the interactive rename mapping is very likely 650 * to be correct since it applies to a single file and is an exact 651 * match. The regular expression replacements are a little harder to 652 * justify though. We claim that the symlink name is only likely 653 * to be replaced when it points within the file tree being moved and 654 * in that case it should be modified. what we really need to do is to 655 * call an oracle here. :) 656 */ 657 if (rephead != NULL) { 658 /* 659 * we have replacement strings, modify the name and the link 660 * name if any. 661 */ 662 if ((res = rep_name(arcn->name, &(arcn->nlen), 1)) != 0) 663 return(res); 664 665 if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || 666 (arcn->type == PAX_HRG)) && 667 ((res = rep_name(arcn->ln_name, &(arcn->ln_nlen), 0)) != 0)) 668 return(res); 669 } 670 671 if (iflag) { 672 /* 673 * perform interactive file rename, then map the link if any 674 */ 675 if ((res = tty_rename(arcn)) != 0) 676 return(res); 677 if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || 678 (arcn->type == PAX_HRG)) 679 sub_name(arcn->ln_name, &(arcn->ln_nlen)); 680 } 681 return(res); 682 } 683 684 /* 685 * tty_rename() 686 * Prompt the user for a replacement file name. A "." keeps the old name, 687 * a empty line skips the file, and an EOF on reading the tty, will cause 688 * pax to stop processing and exit. Otherwise the file name input, replaces 689 * the old one. 690 * Return: 691 * 0 process this file, 1 skip this file, -1 we need to exit pax 692 */ 693 694 #if __STDC__ 695 static int 696 tty_rename(register ARCHD *arcn) 697 #else 698 static int 699 tty_rename(arcn) 700 register ARCHD *arcn; 701 #endif 702 { 703 char tmpname[PAXPATHLEN+2]; 704 int res; 705 706 /* 707 * prompt user for the replacement name for a file, keep trying until 708 * we get some reasonable input. Archives may have more than one file 709 * on them with the same name (from updates etc). We print verbose info 710 * on the file so the user knows what is up. 711 */ 712 tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0); 713 714 for (;;) { 715 ls_tty(arcn); 716 tty_prnt("Input new name, or a \".\" to keep the old name, "); 717 tty_prnt("or a \"return\" to skip this file.\n"); 718 tty_prnt("Input > "); 719 if (tty_read(tmpname, sizeof(tmpname)) < 0) 720 return(-1); 721 if (strcmp(tmpname, "..") == 0) { 722 tty_prnt("Try again, illegal file name: ..\n"); 723 continue; 724 } 725 if (strlen(tmpname) > PAXPATHLEN) { 726 tty_prnt("Try again, file name too long\n"); 727 continue; 728 } 729 break; 730 } 731 732 /* 733 * empty file name, skips this file. a "." leaves it alone 734 */ 735 if (tmpname[0] == '\0') { 736 tty_prnt("Skipping file.\n"); 737 return(1); 738 } 739 if ((tmpname[0] == '.') && (tmpname[1] == '\0')) { 740 tty_prnt("Processing continues, name unchanged.\n"); 741 return(0); 742 } 743 744 /* 745 * ok the name changed. We may run into links that point at this 746 * file later. we have to remember where the user sent the file 747 * in order to repair any links. 748 */ 749 tty_prnt("Processing continues, name changed to: %s\n", tmpname); 750 res = add_name(arcn->name, arcn->nlen, tmpname); 751 arcn->nlen = l_strncpy(arcn->name, tmpname, PAXPATHLEN+1); 752 if (res < 0) 753 return(-1); 754 return(0); 755 } 756 757 /* 758 * set_dest() 759 * fix up the file name and the link name (if any) so this file will land 760 * in the destination directory (used during copy() -rw). 761 * Return: 762 * 0 if ok, -1 if failure (name too long) 763 */ 764 765 #if __STDC__ 766 int 767 set_dest(register ARCHD *arcn, char *dest_dir, int dir_len) 768 #else 769 int 770 set_dest(arcn, dest_dir, dir_len) 771 register ARCHD *arcn; 772 char *dest_dir; 773 int dir_len; 774 #endif 775 { 776 if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0) 777 return(-1); 778 779 /* 780 * It is really hard to deal with symlinks here, we cannot be sure 781 * if the name they point was moved (or will be moved). It is best to 782 * leave them alone. 783 */ 784 if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG)) 785 return(0); 786 787 if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0) 788 return(-1); 789 return(0); 790 } 791 792 /* 793 * fix_path 794 * concatenate dir_name and or_name and store the result in or_name (if 795 * it fits). This is one ugly function. 796 * Return: 797 * 0 if ok, -1 if the final name is too long 798 */ 799 800 #if __STDC__ 801 static int 802 fix_path( char *or_name, int *or_len, char *dir_name, int dir_len) 803 #else 804 static int 805 fix_path(or_name, or_len, dir_name, dir_len) 806 char *or_name; 807 int *or_len; 808 char *dir_name; 809 int dir_len; 810 #endif 811 { 812 register char *src; 813 register char *dest; 814 register char *start; 815 int len; 816 817 /* 818 * we shift the or_name to the right enough to tack in the dir_name 819 * at the front. We make sure we have enough space for it all before 820 * we start. since dest always ends in a slash, we skip of or_name 821 * if it also starts with one. 822 */ 823 start = or_name; 824 src = start + *or_len; 825 dest = src + dir_len; 826 if (*start == '/') { 827 ++start; 828 --dest; 829 } 830 if ((len = dest - or_name) > PAXPATHLEN) { 831 warn(1, "File name %s/%s, too long", dir_name, start); 832 return(-1); 833 } 834 *or_len = len; 835 836 /* 837 * enough space, shift 838 */ 839 while (src >= start) 840 *dest-- = *src--; 841 src = dir_name + dir_len - 1; 842 843 /* 844 * splice in the destination directory name 845 */ 846 while (src >= dir_name) 847 *dest-- = *src--; 848 849 *(or_name + len) = '\0'; 850 return(0); 851 } 852 853 /* 854 * rep_name() 855 * walk down the list of replacement strings applying each one in order. 856 * when we find one with a successful substitution, we modify the name 857 * as specified. if required, we print the results. if the resulting name 858 * is empty, we will skip this archive member. We use the regexp(3) 859 * routines (regexp() ought to win a prize as having the most cryptic 860 * library function manual page). 861 * --Parameters-- 862 * name is the file name we are going to apply the regular expressions to 863 * (and may be modified) 864 * nlen is the length of this name (and is modified to hold the length of 865 * the final string). 866 * prnt is a flag that says whether to print the final result. 867 * Return: 868 * 0 if substitution was successful, 1 if we are to skip the file (the name 869 * ended up empty) 870 */ 871 872 #if __STDC__ 873 static int 874 rep_name(char *name, int *nlen, int prnt) 875 #else 876 static int 877 rep_name(name, nlen, prnt) 878 char *name; 879 int *nlen; 880 int prnt; 881 #endif 882 { 883 register REPLACE *pt; 884 register char *inpt; 885 register char *outpt; 886 register char *endpt; 887 register char *rpt; 888 register int found = 0; 889 register int res; 890 # ifndef NET2_REGEX 891 regmatch_t pm[MAXSUBEXP]; 892 # endif 893 char nname[PAXPATHLEN+1]; /* final result of all replacements */ 894 char buf1[PAXPATHLEN+1]; /* where we work on the name */ 895 896 /* 897 * copy the name into buf1, where we will work on it. We need to keep 898 * the orig string around so we can print out the result of the final 899 * replacement. We build up the final result in nname. inpt points at 900 * the string we apply the regular expression to. prnt is used to 901 * suppress printing when we handle replacements on the link field 902 * (the user already saw that substitution go by) 903 */ 904 pt = rephead; 905 (void)strcpy(buf1, name); 906 inpt = buf1; 907 outpt = nname; 908 endpt = outpt + PAXPATHLEN; 909 910 /* 911 * try each replacement string in order 912 */ 913 while (pt != NULL) { 914 do { 915 /* 916 * check for a successful substitution, if not go to 917 * the next pattern, or cleanup if we were global 918 */ 919 # ifdef NET2_REGEX 920 if (regexec(pt->rcmp, inpt) == 0) 921 # else 922 if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0) 923 # endif 924 break; 925 926 /* 927 * ok we found one. We have three parts, the prefix 928 * which did not match, the section that did and the 929 * tail (that also did not match). Copy the prefix to 930 * the final output buffer (watching to make sure we 931 * do not create a string too long). 932 */ 933 found = 1; 934 # ifdef NET2_REGEX 935 rpt = pt->rcmp->startp[0]; 936 # else 937 rpt = inpt + pm[0].rm_so; 938 # endif 939 940 while ((inpt < rpt) && (outpt < endpt)) 941 *outpt++ = *inpt++; 942 if (outpt == endpt) 943 break; 944 945 /* 946 * for the second part (which matched the regular 947 * expression) apply the substitution using the 948 * replacement string and place it the prefix in the 949 * final output. If we have problems, skip it. 950 */ 951 # ifdef NET2_REGEX 952 if ((res = resub(pt->rcmp,pt->nstr,outpt,endpt)) < 0) { 953 # else 954 if ((res = resub(&(pt->rcmp),pm,pt->nstr,outpt,endpt)) 955 < 0) { 956 # endif 957 if (prnt) 958 warn(1, "Replacement name error %s", 959 name); 960 return(1); 961 } 962 outpt += res; 963 964 /* 965 * we set up to look again starting at the first 966 * character in the tail (of the input string right 967 * after the last character matched by the regular 968 * expression (inpt always points at the first char in 969 * the string to process). If we are not doing a global 970 * substitution, we will use inpt to copy the tail to 971 * the final result. Make sure we do not overrun the 972 * output buffer 973 */ 974 # ifdef NET2_REGEX 975 inpt = pt->rcmp->endp[0]; 976 # else 977 inpt += pm[0].rm_eo; 978 # endif 979 980 if ((outpt == endpt) || (*inpt == '\0')) 981 break; 982 983 /* 984 * if the user wants global we keep trying to 985 * substitute until it fails, then we are done. 986 */ 987 } while (pt->flgs & GLOB); 988 989 if (found) 990 break; 991 992 /* 993 * a successful substitution did NOT occur, try the next one 994 */ 995 pt = pt->fow; 996 } 997 998 if (found) { 999 /* 1000 * we had a substitution, copy the last tail piece (if there is 1001 * room) to the final result 1002 */ 1003 while ((outpt < endpt) && (*inpt != '\0')) 1004 *outpt++ = *inpt++; 1005 1006 *outpt = '\0'; 1007 if ((outpt == endpt) && (*inpt != '\0')) { 1008 if (prnt) 1009 warn(1,"Replacement name too long %s >> %s", 1010 name, nname); 1011 return(1); 1012 } 1013 1014 /* 1015 * inform the user of the result if wanted 1016 */ 1017 if (prnt && (pt->flgs & PRNT)) { 1018 if (*nname == '\0') 1019 (void)fprintf(stderr,"%s >> <empty string>\n", 1020 name); 1021 else 1022 (void)fprintf(stderr,"%s >> %s\n", name, nname); 1023 } 1024 1025 /* 1026 * if empty inform the caller this file is to be skipped 1027 * otherwise copy the new name over the orig name and return 1028 */ 1029 if (*nname == '\0') 1030 return(1); 1031 *nlen = l_strncpy(name, nname, PAXPATHLEN + 1); 1032 } 1033 return(0); 1034 } 1035 1036 #ifdef NET2_REGEX 1037 /* 1038 * resub() 1039 * apply the replacement to the matched expression. expand out the old 1040 * style ed(1) subexpression expansion. 1041 * Return: 1042 * -1 if error, or the number of characters added to the destination. 1043 */ 1044 1045 #if __STDC__ 1046 static int 1047 resub(regexp *prog, char *src, char *dest, register char *destend) 1048 #else 1049 static int 1050 resub(prog, src, dest, destend) 1051 regexp *prog; 1052 char *src; 1053 char *dest; 1054 register char *destend; 1055 #endif 1056 { 1057 register char *spt; 1058 register char *dpt; 1059 register char c; 1060 register int no; 1061 register int len; 1062 1063 spt = src; 1064 dpt = dest; 1065 while ((dpt < destend) && ((c = *spt++) != '\0')) { 1066 if (c == '&') 1067 no = 0; 1068 else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) 1069 no = *spt++ - '0'; 1070 else { 1071 if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) 1072 c = *spt++; 1073 *dpt++ = c; 1074 continue; 1075 } 1076 if ((prog->startp[no] == NULL) || (prog->endp[no] == NULL) || 1077 ((len = prog->endp[no] - prog->startp[no]) <= 0)) 1078 continue; 1079 1080 /* 1081 * copy the subexpression to the destination. 1082 * fail if we run out of space or the match string is damaged 1083 */ 1084 if (len > (destend - dpt)) 1085 len = destend - dpt; 1086 if (l_strncpy(dpt, prog->startp[no], len) != len) 1087 return(-1); 1088 dpt += len; 1089 } 1090 return(dpt - dest); 1091 } 1092 1093 #else 1094 1095 /* 1096 * resub() 1097 * apply the replacement to the matched expression. expand out the old 1098 * style ed(1) subexpression expansion. 1099 * Return: 1100 * -1 if error, or the number of characters added to the destination. 1101 */ 1102 1103 #if __STDC__ 1104 static int 1105 resub(regex_t *rp, register regmatch_t *pm, char *src, char *dest, 1106 register char *destend) 1107 #else 1108 static int 1109 resub(rp, pm, src, dest, destend) 1110 regex_t *rp; 1111 register regmatch_t *pm; 1112 char *src; 1113 char *dest; 1114 register char *destend; 1115 #endif 1116 { 1117 register char *spt; 1118 register char *dpt; 1119 register char c; 1120 register regmatch_t *pmpt; 1121 register int len; 1122 int subexcnt; 1123 1124 spt = src; 1125 dpt = dest; 1126 subexcnt = rp->re_nsub; 1127 while ((dpt < destend) && ((c = *spt++) != '\0')) { 1128 /* 1129 * see if we just have an ordinary replacement character 1130 * or we refer to a subexpression. 1131 */ 1132 if (c == '&') { 1133 pmpt = pm; 1134 } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) { 1135 /* 1136 * make sure there is a subexpression as specified 1137 */ 1138 if ((len = *spt++ - '0') > subexcnt) 1139 return(-1); 1140 pmpt = pm + len; 1141 } else { 1142 /* 1143 * Ordinary character, just copy it 1144 */ 1145 if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) 1146 c = *spt++; 1147 *dpt++ = c; 1148 continue; 1149 } 1150 1151 /* 1152 * continue if the subexpression is bogus 1153 */ 1154 if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) || 1155 ((len = pmpt->rm_eo - pmpt->rm_so) <= 0)) 1156 continue; 1157 1158 /* 1159 * copy the subexpression to the destination. 1160 * fail if we run out of space or the match string is damaged 1161 */ 1162 if (len > (destend - dpt)) 1163 len = destend - dpt; 1164 if (l_strncpy(dpt, src + pmpt->rm_so, len) != len) 1165 return(-1); 1166 dpt += len; 1167 } 1168 return(dpt - dest); 1169 } 1170 #endif 1171