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