pat_rep.c revision 114583
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1992 Keith Muller. 31556Srgrimes * Copyright (c) 1992, 1993 41556Srgrimes * The Regents of the University of California. All rights reserved. 51556Srgrimes * 61556Srgrimes * This code is derived from software contributed to Berkeley by 71556Srgrimes * Keith Muller of the University of California, San Diego. 81556Srgrimes * 91556Srgrimes * Redistribution and use in source and binary forms, with or without 101556Srgrimes * modification, are permitted provided that the following conditions 111556Srgrimes * are met: 121556Srgrimes * 1. Redistributions of source code must retain the above copyright 131556Srgrimes * notice, this list of conditions and the following disclaimer. 141556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 151556Srgrimes * notice, this list of conditions and the following disclaimer in the 161556Srgrimes * documentation and/or other materials provided with the distribution. 171556Srgrimes * 3. All advertising materials mentioning features or use of this software 181556Srgrimes * must display the following acknowledgement: 191556Srgrimes * This product includes software developed by the University of 201556Srgrimes * California, Berkeley and its contributors. 211556Srgrimes * 4. Neither the name of the University nor the names of its contributors 221556Srgrimes * may be used to endorse or promote products derived from this software 231556Srgrimes * without specific prior written permission. 241556Srgrimes * 251556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 261556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 271556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 281556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 291556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 301556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 311556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 321556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 331556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 341556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 351556Srgrimes * SUCH DAMAGE. 361556Srgrimes */ 371556Srgrimes 381556Srgrimes#ifndef lint 3936049Scharnier#if 0 4036049Scharnierstatic char sccsid[] = "@(#)pat_rep.c 8.2 (Berkeley) 4/18/94"; 4136049Scharnier#endif 421556Srgrimes#endif /* not lint */ 4399110Sobrien#include <sys/cdefs.h> 4499110Sobrien__FBSDID("$FreeBSD: head/bin/pax/pat_rep.c 114583 2003-05-03 16:39:34Z markm $"); 451556Srgrimes 461556Srgrimes#include <sys/types.h> 471556Srgrimes#include <sys/stat.h> 481556Srgrimes#include <stdio.h> 491556Srgrimes#include <string.h> 501556Srgrimes#include <unistd.h> 511556Srgrimes#include <stdlib.h> 5276351Skris#include <errno.h> 531556Srgrimes#ifdef NET2_REGEX 541556Srgrimes#include <regexp.h> 551556Srgrimes#else 561556Srgrimes#include <regex.h> 571556Srgrimes#endif 581556Srgrimes#include "pax.h" 591556Srgrimes#include "pat_rep.h" 601556Srgrimes#include "extern.h" 611556Srgrimes 621556Srgrimes/* 631556Srgrimes * routines to handle pattern matching, name modification (regular expression 641556Srgrimes * substitution and interactive renames), and destination name modification for 651556Srgrimes * copy (-rw). Both file name and link names are adjusted as required in these 661556Srgrimes * routines. 671556Srgrimes */ 681556Srgrimes 691556Srgrimes#define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */ 701556Srgrimesstatic PATTERN *pathead = NULL; /* file pattern match list head */ 711556Srgrimesstatic PATTERN *pattail = NULL; /* file pattern match list tail */ 721556Srgrimesstatic REPLACE *rephead = NULL; /* replacement string list head */ 731556Srgrimesstatic REPLACE *reptail = NULL; /* replacement string list tail */ 741556Srgrimes 7590110Simpstatic int rep_name(char *, int *, int); 7690113Simpstatic int tty_rename(ARCHD *); 7790110Simpstatic int fix_path(char *, int *, char *, int); 7890113Simpstatic int fn_match(char *, char *, char **); 7990113Simpstatic char * range_match(char *, int); 801556Srgrimes#ifdef NET2_REGEX 8190113Simpstatic int resub(regexp *, char *, char *, char *); 821556Srgrimes#else 8390110Simpstatic int resub(regex_t *, regmatch_t *, char *, char *, char *); 841556Srgrimes#endif 851556Srgrimes 861556Srgrimes/* 871556Srgrimes * rep_add() 881556Srgrimes * parses the -s replacement string; compiles the regular expression 891556Srgrimes * and stores the compiled value and it's replacement string together in 901556Srgrimes * replacement string list. Input to this function is of the form: 918855Srgrimes * /old/new/pg 921556Srgrimes * The first char in the string specifies the delimiter used by this 931556Srgrimes * replacement string. "Old" is a regular expression in "ed" format which 941556Srgrimes * is compiled by regcomp() and is applied to filenames. "new" is the 951556Srgrimes * substitution string; p and g are options flags for printing and global 961556Srgrimes * replacement (over the single filename) 971556Srgrimes * Return: 981556Srgrimes * 0 if a proper replacement string and regular expression was added to 991556Srgrimes * the list of replacement patterns; -1 otherwise. 1001556Srgrimes */ 1011556Srgrimes 1021556Srgrimesint 10390113Simprep_add(char *str) 1041556Srgrimes{ 10590113Simp char *pt1; 10690113Simp char *pt2; 10790113Simp REPLACE *rep; 1081556Srgrimes# ifndef NET2_REGEX 10990113Simp int res; 1101556Srgrimes char rebuf[BUFSIZ]; 1111556Srgrimes# endif 1121556Srgrimes 1131556Srgrimes /* 1141556Srgrimes * throw out the bad parameters 1151556Srgrimes */ 1161556Srgrimes if ((str == NULL) || (*str == '\0')) { 11776017Skris paxwarn(1, "Empty replacement string"); 1181556Srgrimes return(-1); 1191556Srgrimes } 1201556Srgrimes 1211556Srgrimes /* 1221556Srgrimes * first character in the string specifies what the delimiter is for 1231556Srgrimes * this expression 1241556Srgrimes */ 1251556Srgrimes if ((pt1 = strchr(str+1, *str)) == NULL) { 12676017Skris paxwarn(1, "Invalid replacement string %s", str); 1271556Srgrimes return(-1); 1281556Srgrimes } 1291556Srgrimes 1301556Srgrimes /* 1311556Srgrimes * allocate space for the node that handles this replacement pattern 1321556Srgrimes * and split out the regular expression and try to compile it 1331556Srgrimes */ 1341556Srgrimes if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) { 13576017Skris paxwarn(1, "Unable to allocate memory for replacement string"); 1361556Srgrimes return(-1); 1371556Srgrimes } 1381556Srgrimes 1391556Srgrimes *pt1 = '\0'; 1401556Srgrimes# ifdef NET2_REGEX 1411556Srgrimes if ((rep->rcmp = regcomp(str+1)) == NULL) { 1421556Srgrimes# else 1431556Srgrimes if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { 1441556Srgrimes regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); 14576017Skris paxwarn(1, "%s while compiling regular expression %s", rebuf, str); 1461556Srgrimes# endif 1471556Srgrimes (void)free((char *)rep); 1481556Srgrimes return(-1); 1491556Srgrimes } 1501556Srgrimes 1511556Srgrimes /* 1521556Srgrimes * put the delimiter back in case we need an error message and 1531556Srgrimes * locate the delimiter at the end of the replacement string 1541556Srgrimes * we then point the node at the new substitution string 1551556Srgrimes */ 1561556Srgrimes *pt1++ = *str; 1571556Srgrimes if ((pt2 = strchr(pt1, *str)) == NULL) { 1581556Srgrimes# ifdef NET2_REGEX 1591556Srgrimes (void)free((char *)rep->rcmp); 1601556Srgrimes# else 1611556Srgrimes regfree(&(rep->rcmp)); 1621556Srgrimes# endif 1631556Srgrimes (void)free((char *)rep); 16476017Skris paxwarn(1, "Invalid replacement string %s", str); 1651556Srgrimes return(-1); 1661556Srgrimes } 1671556Srgrimes 1681556Srgrimes *pt2 = '\0'; 1691556Srgrimes rep->nstr = pt1; 1701556Srgrimes pt1 = pt2++; 1711556Srgrimes rep->flgs = 0; 1721556Srgrimes 1731556Srgrimes /* 1741556Srgrimes * set the options if any 1751556Srgrimes */ 1761556Srgrimes while (*pt2 != '\0') { 1771556Srgrimes switch(*pt2) { 1781556Srgrimes case 'g': 1791556Srgrimes case 'G': 1801556Srgrimes rep->flgs |= GLOB; 1811556Srgrimes break; 1821556Srgrimes case 'p': 1831556Srgrimes case 'P': 1841556Srgrimes rep->flgs |= PRNT; 1851556Srgrimes break; 1861556Srgrimes default: 1871556Srgrimes# ifdef NET2_REGEX 1881556Srgrimes (void)free((char *)rep->rcmp); 1891556Srgrimes# else 1901556Srgrimes regfree(&(rep->rcmp)); 1911556Srgrimes# endif 1921556Srgrimes (void)free((char *)rep); 1931556Srgrimes *pt1 = *str; 19476017Skris paxwarn(1, "Invalid replacement string option %s", str); 1951556Srgrimes return(-1); 1961556Srgrimes } 1971556Srgrimes ++pt2; 1981556Srgrimes } 1991556Srgrimes 2001556Srgrimes /* 2011556Srgrimes * all done, link it in at the end 2021556Srgrimes */ 2031556Srgrimes rep->fow = NULL; 2041556Srgrimes if (rephead == NULL) { 2051556Srgrimes reptail = rephead = rep; 2061556Srgrimes return(0); 2071556Srgrimes } 2081556Srgrimes reptail->fow = rep; 2091556Srgrimes reptail = rep; 2101556Srgrimes return(0); 2111556Srgrimes} 2121556Srgrimes 2131556Srgrimes/* 2141556Srgrimes * pat_add() 2151556Srgrimes * add a pattern match to the pattern match list. Pattern matches are used 2161556Srgrimes * to select which archive members are extracted. (They appear as 2171556Srgrimes * arguments to pax in the list and read modes). If no patterns are 2181556Srgrimes * supplied to pax, all members in the archive will be selected (and the 2191556Srgrimes * pattern match list is empty). 2201556Srgrimes * Return: 2211556Srgrimes * 0 if the pattern was added to the list, -1 otherwise 2221556Srgrimes */ 2231556Srgrimes 2241556Srgrimesint 225114583Smarkmpat_add(char *str, char *chdnam) 2261556Srgrimes{ 22790113Simp PATTERN *pt; 2281556Srgrimes 2291556Srgrimes /* 2301556Srgrimes * throw out the junk 2311556Srgrimes */ 2321556Srgrimes if ((str == NULL) || (*str == '\0')) { 23376017Skris paxwarn(1, "Empty pattern string"); 2341556Srgrimes return(-1); 2351556Srgrimes } 2361556Srgrimes 2371556Srgrimes /* 2381556Srgrimes * allocate space for the pattern and store the pattern. the pattern is 2391556Srgrimes * part of argv so do not bother to copy it, just point at it. Add the 2401556Srgrimes * node to the end of the pattern list 2411556Srgrimes */ 2421556Srgrimes if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { 24376017Skris paxwarn(1, "Unable to allocate memory for pattern string"); 2441556Srgrimes return(-1); 2451556Srgrimes } 2461556Srgrimes 2471556Srgrimes pt->pstr = str; 2481556Srgrimes pt->pend = NULL; 2491556Srgrimes pt->plen = strlen(str); 2501556Srgrimes pt->fow = NULL; 2511556Srgrimes pt->flgs = 0; 252114583Smarkm pt->chdname = chdnam; 25376351Skris 2541556Srgrimes if (pathead == NULL) { 2551556Srgrimes pattail = pathead = pt; 2561556Srgrimes return(0); 2571556Srgrimes } 2581556Srgrimes pattail->fow = pt; 2591556Srgrimes pattail = pt; 2601556Srgrimes return(0); 2611556Srgrimes} 2621556Srgrimes 2631556Srgrimes/* 2641556Srgrimes * pat_chk() 2651556Srgrimes * complain if any the user supplied pattern did not result in a match to 2661556Srgrimes * a selected archive member. 2671556Srgrimes */ 2681556Srgrimes 2691556Srgrimesvoid 2701556Srgrimespat_chk(void) 2711556Srgrimes{ 27290113Simp PATTERN *pt; 27390113Simp int wban = 0; 2741556Srgrimes 2751556Srgrimes /* 2761556Srgrimes * walk down the list checking the flags to make sure MTCH was set, 2771556Srgrimes * if not complain 2781556Srgrimes */ 2791556Srgrimes for (pt = pathead; pt != NULL; pt = pt->fow) { 2801556Srgrimes if (pt->flgs & MTCH) 2811556Srgrimes continue; 2821556Srgrimes if (!wban) { 28376017Skris paxwarn(1, "WARNING! These patterns were not matched:"); 2841556Srgrimes ++wban; 2851556Srgrimes } 2861556Srgrimes (void)fprintf(stderr, "%s\n", pt->pstr); 2871556Srgrimes } 2881556Srgrimes} 2891556Srgrimes 2901556Srgrimes/* 2911556Srgrimes * pat_sel() 2921556Srgrimes * the archive member which matches a pattern was selected. Mark the 2931556Srgrimes * pattern as having selected an archive member. arcn->pat points at the 2941556Srgrimes * pattern that was matched. arcn->pat is set in pat_match() 2951556Srgrimes * 2961556Srgrimes * NOTE: When the -c option is used, we are called when there was no match 2971556Srgrimes * by pat_match() (that means we did match before the inverted sense of 2981556Srgrimes * the logic). Now this seems really strange at first, but with -c we 299108533Sschweikh * need to keep track of those patterns that cause an archive member to NOT 3001556Srgrimes * be selected (it found an archive member with a specified pattern) 3011556Srgrimes * Return: 3021556Srgrimes * 0 if the pattern pointed at by arcn->pat was tagged as creating a 3031556Srgrimes * match, -1 otherwise. 3041556Srgrimes */ 3051556Srgrimes 3061556Srgrimesint 30790113Simppat_sel(ARCHD *arcn) 3081556Srgrimes{ 30990113Simp PATTERN *pt; 31090113Simp PATTERN **ppt; 31190113Simp int len; 3121556Srgrimes 3131556Srgrimes /* 3141556Srgrimes * if no patterns just return 3151556Srgrimes */ 3161556Srgrimes if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) 3171556Srgrimes return(0); 3181556Srgrimes 3191556Srgrimes /* 3201556Srgrimes * when we are NOT limited to a single match per pattern mark the 3211556Srgrimes * pattern and return 3221556Srgrimes */ 3231556Srgrimes if (!nflag) { 3241556Srgrimes pt->flgs |= MTCH; 3251556Srgrimes return(0); 3261556Srgrimes } 3271556Srgrimes 3281556Srgrimes /* 3291556Srgrimes * we reach this point only when we allow a single selected match per 3308855Srgrimes * pattern, if the pattern matches a directory and we do not have -d 3311556Srgrimes * (dflag) we are done with this pattern. We may also be handed a file 3321556Srgrimes * in the subtree of a directory. in that case when we are operating 3331556Srgrimes * with -d, this pattern was already selected and we are done 3341556Srgrimes */ 3351556Srgrimes if (pt->flgs & DIR_MTCH) 3361556Srgrimes return(0); 3371556Srgrimes 3381556Srgrimes if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { 3391556Srgrimes /* 3401556Srgrimes * ok we matched a directory and we are allowing 3411556Srgrimes * subtree matches but because of the -n only its children will 3421556Srgrimes * match. This is tagged as a DIR_MTCH type. 3431556Srgrimes * WATCH IT, the code assumes that pt->pend points 3441556Srgrimes * into arcn->name and arcn->name has not been modified. 3451556Srgrimes * If not we will have a big mess. Yup this is another kludge 3461556Srgrimes */ 3471556Srgrimes 3481556Srgrimes /* 3491556Srgrimes * if this was a prefix match, remove trailing part of path 3501556Srgrimes * so we can copy it. Future matches will be exact prefix match 3511556Srgrimes */ 3521556Srgrimes if (pt->pend != NULL) 3531556Srgrimes *pt->pend = '\0'; 3548855Srgrimes 3551556Srgrimes if ((pt->pstr = strdup(arcn->name)) == NULL) { 35676017Skris paxwarn(1, "Pattern select out of memory"); 3571556Srgrimes if (pt->pend != NULL) 3581556Srgrimes *pt->pend = '/'; 3591556Srgrimes pt->pend = NULL; 3601556Srgrimes return(-1); 3611556Srgrimes } 3621556Srgrimes 3631556Srgrimes /* 3641556Srgrimes * put the trailing / back in the source string 3651556Srgrimes */ 3661556Srgrimes if (pt->pend != NULL) { 3671556Srgrimes *pt->pend = '/'; 3681556Srgrimes pt->pend = NULL; 3691556Srgrimes } 3701556Srgrimes pt->plen = strlen(pt->pstr); 3711556Srgrimes 3721556Srgrimes /* 3731556Srgrimes * strip off any trailing /, this should really never happen 3741556Srgrimes */ 3751556Srgrimes len = pt->plen - 1; 3761556Srgrimes if (*(pt->pstr + len) == '/') { 3771556Srgrimes *(pt->pstr + len) = '\0'; 3781556Srgrimes pt->plen = len; 3798855Srgrimes } 3801556Srgrimes pt->flgs = DIR_MTCH | MTCH; 3811556Srgrimes arcn->pat = pt; 3821556Srgrimes return(0); 3831556Srgrimes } 3841556Srgrimes 3851556Srgrimes /* 3861556Srgrimes * we are then done with this pattern, so we delete it from the list 3871556Srgrimes * because it can never be used for another match. 3881556Srgrimes * Seems kind of strange to do for a -c, but the pax spec is really 3891556Srgrimes * vague on the interaction of -c -n and -d. We assume that when -c 3901556Srgrimes * and the pattern rejects a member (i.e. it matched it) it is done. 3911556Srgrimes * In effect we place the order of the flags as having -c last. 3921556Srgrimes */ 3931556Srgrimes pt = pathead; 3941556Srgrimes ppt = &pathead; 3951556Srgrimes while ((pt != NULL) && (pt != arcn->pat)) { 3961556Srgrimes ppt = &(pt->fow); 3971556Srgrimes pt = pt->fow; 3981556Srgrimes } 3991556Srgrimes 4001556Srgrimes if (pt == NULL) { 4011556Srgrimes /* 4021556Srgrimes * should never happen.... 4031556Srgrimes */ 40476017Skris paxwarn(1, "Pattern list inconsistant"); 4051556Srgrimes return(-1); 4061556Srgrimes } 4071556Srgrimes *ppt = pt->fow; 4081556Srgrimes (void)free((char *)pt); 4091556Srgrimes arcn->pat = NULL; 4101556Srgrimes return(0); 4111556Srgrimes} 4121556Srgrimes 4131556Srgrimes/* 4141556Srgrimes * pat_match() 4151556Srgrimes * see if this archive member matches any supplied pattern, if a match 4161556Srgrimes * is found, arcn->pat is set to point at the potential pattern. Later if 4171556Srgrimes * this archive member is "selected" we process and mark the pattern as 4181556Srgrimes * one which matched a selected archive member (see pat_sel()) 4191556Srgrimes * Return: 4208855Srgrimes * 0 if this archive member should be processed, 1 if it should be 4211556Srgrimes * skipped and -1 if we are done with all patterns (and pax should quit 4221556Srgrimes * looking for more members) 4231556Srgrimes */ 4241556Srgrimes 4251556Srgrimesint 42690113Simppat_match(ARCHD *arcn) 4271556Srgrimes{ 42890113Simp PATTERN *pt; 4291556Srgrimes 4301556Srgrimes arcn->pat = NULL; 4311556Srgrimes 4321556Srgrimes /* 4331556Srgrimes * if there are no more patterns and we have -n (and not -c) we are 4341556Srgrimes * done. otherwise with no patterns to match, matches all 4351556Srgrimes */ 4361556Srgrimes if (pathead == NULL) { 4371556Srgrimes if (nflag && !cflag) 4381556Srgrimes return(-1); 4391556Srgrimes return(0); 4401556Srgrimes } 4411556Srgrimes 4421556Srgrimes /* 4431556Srgrimes * have to search down the list one at a time looking for a match. 4441556Srgrimes */ 4451556Srgrimes pt = pathead; 4461556Srgrimes while (pt != NULL) { 4471556Srgrimes /* 4481556Srgrimes * check for a file name match unless we have DIR_MTCH set in 4491556Srgrimes * this pattern then we want a prefix match 4501556Srgrimes */ 4511556Srgrimes if (pt->flgs & DIR_MTCH) { 4521556Srgrimes /* 4531556Srgrimes * this pattern was matched before to a directory 4541556Srgrimes * as we must have -n set for this (but not -d). We can 4551556Srgrimes * only match CHILDREN of that directory so we must use 4561556Srgrimes * an exact prefix match (no wildcards). 4571556Srgrimes */ 4581556Srgrimes if ((arcn->name[pt->plen] == '/') && 4591556Srgrimes (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) 4601556Srgrimes break; 4611556Srgrimes } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) 4621556Srgrimes break; 4631556Srgrimes pt = pt->fow; 4641556Srgrimes } 4651556Srgrimes 4661556Srgrimes /* 4671556Srgrimes * return the result, remember that cflag (-c) inverts the sense of a 4681556Srgrimes * match 4691556Srgrimes */ 4701556Srgrimes if (pt == NULL) 4711556Srgrimes return(cflag ? 0 : 1); 4721556Srgrimes 4731556Srgrimes /* 474108533Sschweikh * We had a match, now when we invert the sense (-c) we reject this 4751556Srgrimes * member. However we have to tag the pattern a being successful, (in a 476108533Sschweikh * match, not in selecting an archive member) so we call pat_sel() here. 4771556Srgrimes */ 4781556Srgrimes arcn->pat = pt; 4791556Srgrimes if (!cflag) 4801556Srgrimes return(0); 4811556Srgrimes 4821556Srgrimes if (pat_sel(arcn) < 0) 4831556Srgrimes return(-1); 4841556Srgrimes arcn->pat = NULL; 4851556Srgrimes return(1); 4861556Srgrimes} 4871556Srgrimes 4881556Srgrimes/* 4891556Srgrimes * fn_match() 4901556Srgrimes * Return: 4918855Srgrimes * 0 if this archive member should be processed, 1 if it should be 4921556Srgrimes * skipped and -1 if we are done with all patterns (and pax should quit 4931556Srgrimes * looking for more members) 4941556Srgrimes * Note: *pend may be changed to show where the prefix ends. 4951556Srgrimes */ 4961556Srgrimes 4971556Srgrimesstatic int 49890113Simpfn_match(char *pattern, char *string, char **pend) 4991556Srgrimes{ 50090113Simp char c; 5011556Srgrimes char test; 5021556Srgrimes 5031556Srgrimes *pend = NULL; 5041556Srgrimes for (;;) { 5051556Srgrimes switch (c = *pattern++) { 5061556Srgrimes case '\0': 5071556Srgrimes /* 5081556Srgrimes * Ok we found an exact match 5091556Srgrimes */ 5101556Srgrimes if (*string == '\0') 5111556Srgrimes return(0); 5121556Srgrimes 5131556Srgrimes /* 5141556Srgrimes * Check if it is a prefix match 5151556Srgrimes */ 5161556Srgrimes if ((dflag == 1) || (*string != '/')) 5171556Srgrimes return(-1); 5181556Srgrimes 5191556Srgrimes /* 5201556Srgrimes * It is a prefix match, remember where the trailing 5211556Srgrimes * / is located 5221556Srgrimes */ 5231556Srgrimes *pend = string; 5241556Srgrimes return(0); 5251556Srgrimes case '?': 5261556Srgrimes if ((test = *string++) == '\0') 5271556Srgrimes return (-1); 5281556Srgrimes break; 5291556Srgrimes case '*': 5301556Srgrimes c = *pattern; 5311556Srgrimes /* 5328855Srgrimes * Collapse multiple *'s. 5331556Srgrimes */ 5341556Srgrimes while (c == '*') 5351556Srgrimes c = *++pattern; 5361556Srgrimes 5371556Srgrimes /* 5381556Srgrimes * Optimized hack for pattern with a * at the end 5391556Srgrimes */ 5401556Srgrimes if (c == '\0') 5411556Srgrimes return (0); 5421556Srgrimes 5431556Srgrimes /* 5441556Srgrimes * General case, use recursion. 5451556Srgrimes */ 5461556Srgrimes while ((test = *string) != '\0') { 5471556Srgrimes if (!fn_match(pattern, string, pend)) 5481556Srgrimes return (0); 5491556Srgrimes ++string; 5501556Srgrimes } 5511556Srgrimes return (-1); 5521556Srgrimes case '[': 5531556Srgrimes /* 5541556Srgrimes * range match 5551556Srgrimes */ 5561556Srgrimes if (((test = *string++) == '\0') || 5571556Srgrimes ((pattern = range_match(pattern, test)) == NULL)) 5581556Srgrimes return (-1); 5591556Srgrimes break; 5601556Srgrimes case '\\': 5611556Srgrimes default: 5621556Srgrimes if (c != *string++) 5631556Srgrimes return (-1); 5641556Srgrimes break; 5651556Srgrimes } 5661556Srgrimes } 5671556Srgrimes /* NOTREACHED */ 5681556Srgrimes} 5691556Srgrimes 5701556Srgrimesstatic char * 57190113Simprange_match(char *pattern, int test) 5721556Srgrimes{ 57390113Simp char c; 57490113Simp char c2; 5751556Srgrimes int negate; 5761556Srgrimes int ok = 0; 5771556Srgrimes 57876351Skris if ((negate = (*pattern == '!')) != 0) 5791556Srgrimes ++pattern; 5801556Srgrimes 5811556Srgrimes while ((c = *pattern++) != ']') { 5821556Srgrimes /* 5831556Srgrimes * Illegal pattern 5841556Srgrimes */ 5851556Srgrimes if (c == '\0') 5861556Srgrimes return (NULL); 5871556Srgrimes 5881556Srgrimes if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') && 5891556Srgrimes (c2 != ']')) { 5901556Srgrimes if ((c <= test) && (test <= c2)) 5911556Srgrimes ok = 1; 5921556Srgrimes pattern += 2; 5931556Srgrimes } else if (c == test) 5941556Srgrimes ok = 1; 5951556Srgrimes } 5961556Srgrimes return (ok == negate ? NULL : pattern); 5971556Srgrimes} 5981556Srgrimes 5991556Srgrimes/* 6001556Srgrimes * mod_name() 6011556Srgrimes * modify a selected file name. first attempt to apply replacement string 6021556Srgrimes * expressions, then apply interactive file rename. We apply replacement 6031556Srgrimes * string expressions to both filenames and file links (if we didn't the 6041556Srgrimes * links would point to the wrong place, and we could never be able to 6051556Srgrimes * move an archive that has a file link in it). When we rename files 6061556Srgrimes * interactively, we store that mapping (old name to user input name) so 6071556Srgrimes * if we spot any file links to the old file name in the future, we will 6081556Srgrimes * know exactly how to fix the file link. 6091556Srgrimes * Return: 6108855Srgrimes * 0 continue to process file, 1 skip this file, -1 pax is finished 6111556Srgrimes */ 6121556Srgrimes 6131556Srgrimesint 61490113Simpmod_name(ARCHD *arcn) 6151556Srgrimes{ 61690113Simp int res = 0; 6171556Srgrimes 6181556Srgrimes /* 61976351Skris * Strip off leading '/' if appropriate. 62076351Skris * Currently, this option is only set for the tar format. 62176351Skris */ 62276351Skris if (rmleadslash && arcn->name[0] == '/') { 62376351Skris if (arcn->name[1] == '\0') { 62476351Skris arcn->name[0] = '.'; 62576351Skris } else { 62676351Skris (void)memmove(arcn->name, &arcn->name[1], 62776351Skris strlen(arcn->name)); 62876351Skris arcn->nlen--; 62976351Skris } 63076351Skris if (rmleadslash < 2) { 63176351Skris rmleadslash = 2; 63276351Skris paxwarn(0, "Removing leading / from absolute path names in the archive"); 63376351Skris } 63476351Skris } 63576351Skris if (rmleadslash && arcn->ln_name[0] == '/' && 63676351Skris (arcn->type == PAX_HLK || arcn->type == PAX_HRG)) { 63776351Skris if (arcn->ln_name[1] == '\0') { 63876351Skris arcn->ln_name[0] = '.'; 63976351Skris } else { 64076351Skris (void)memmove(arcn->ln_name, &arcn->ln_name[1], 64176351Skris strlen(arcn->ln_name)); 64276351Skris arcn->ln_nlen--; 64376351Skris } 64476351Skris if (rmleadslash < 2) { 64576351Skris rmleadslash = 2; 64676351Skris paxwarn(0, "Removing leading / from absolute path names in the archive"); 64776351Skris } 64876351Skris } 64976351Skris 65076351Skris /* 6511556Srgrimes * IMPORTANT: We have a problem. what do we do with symlinks? 6521556Srgrimes * Modifying a hard link name makes sense, as we know the file it 6531556Srgrimes * points at should have been seen already in the archive (and if it 6541556Srgrimes * wasn't seen because of a read error or a bad archive, we lose 6551556Srgrimes * anyway). But there are no such requirements for symlinks. On one 6561556Srgrimes * hand the symlink that refers to a file in the archive will have to 6571556Srgrimes * be modified to so it will still work at its new location in the 658102230Strhodes * file system. On the other hand a symlink that points elsewhere (and 6591556Srgrimes * should continue to do so) should not be modified. There is clearly 6601556Srgrimes * no perfect solution here. So we handle them like hardlinks. Clearly 6611556Srgrimes * a replacement made by the interactive rename mapping is very likely 6621556Srgrimes * to be correct since it applies to a single file and is an exact 6631556Srgrimes * match. The regular expression replacements are a little harder to 6641556Srgrimes * justify though. We claim that the symlink name is only likely 6651556Srgrimes * to be replaced when it points within the file tree being moved and 6661556Srgrimes * in that case it should be modified. what we really need to do is to 6671556Srgrimes * call an oracle here. :) 6681556Srgrimes */ 6691556Srgrimes if (rephead != NULL) { 6701556Srgrimes /* 6711556Srgrimes * we have replacement strings, modify the name and the link 6721556Srgrimes * name if any. 6731556Srgrimes */ 6741556Srgrimes if ((res = rep_name(arcn->name, &(arcn->nlen), 1)) != 0) 6751556Srgrimes return(res); 6761556Srgrimes 6771556Srgrimes if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || 6781556Srgrimes (arcn->type == PAX_HRG)) && 6791556Srgrimes ((res = rep_name(arcn->ln_name, &(arcn->ln_nlen), 0)) != 0)) 6801556Srgrimes return(res); 6811556Srgrimes } 6821556Srgrimes 6831556Srgrimes if (iflag) { 6841556Srgrimes /* 6851556Srgrimes * perform interactive file rename, then map the link if any 6861556Srgrimes */ 6871556Srgrimes if ((res = tty_rename(arcn)) != 0) 6881556Srgrimes return(res); 6891556Srgrimes if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || 6901556Srgrimes (arcn->type == PAX_HRG)) 69176351Skris sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name)); 6921556Srgrimes } 6931556Srgrimes return(res); 6941556Srgrimes} 6951556Srgrimes 6961556Srgrimes/* 6971556Srgrimes * tty_rename() 6981556Srgrimes * Prompt the user for a replacement file name. A "." keeps the old name, 6991556Srgrimes * a empty line skips the file, and an EOF on reading the tty, will cause 7001556Srgrimes * pax to stop processing and exit. Otherwise the file name input, replaces 7011556Srgrimes * the old one. 7021556Srgrimes * Return: 7031556Srgrimes * 0 process this file, 1 skip this file, -1 we need to exit pax 7041556Srgrimes */ 7051556Srgrimes 7061556Srgrimesstatic int 70790113Simptty_rename(ARCHD *arcn) 7081556Srgrimes{ 7091556Srgrimes char tmpname[PAXPATHLEN+2]; 7101556Srgrimes int res; 7111556Srgrimes 7121556Srgrimes /* 7131556Srgrimes * prompt user for the replacement name for a file, keep trying until 7141556Srgrimes * we get some reasonable input. Archives may have more than one file 7151556Srgrimes * on them with the same name (from updates etc). We print verbose info 7161556Srgrimes * on the file so the user knows what is up. 7171556Srgrimes */ 7181556Srgrimes tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0); 7191556Srgrimes 7201556Srgrimes for (;;) { 7211556Srgrimes ls_tty(arcn); 7221556Srgrimes tty_prnt("Input new name, or a \".\" to keep the old name, "); 7231556Srgrimes tty_prnt("or a \"return\" to skip this file.\n"); 7241556Srgrimes tty_prnt("Input > "); 7251556Srgrimes if (tty_read(tmpname, sizeof(tmpname)) < 0) 7261556Srgrimes return(-1); 7271556Srgrimes if (strcmp(tmpname, "..") == 0) { 7281556Srgrimes tty_prnt("Try again, illegal file name: ..\n"); 7291556Srgrimes continue; 7301556Srgrimes } 7311556Srgrimes if (strlen(tmpname) > PAXPATHLEN) { 7321556Srgrimes tty_prnt("Try again, file name too long\n"); 7331556Srgrimes continue; 7341556Srgrimes } 7351556Srgrimes break; 7361556Srgrimes } 7371556Srgrimes 7381556Srgrimes /* 7391556Srgrimes * empty file name, skips this file. a "." leaves it alone 7401556Srgrimes */ 7411556Srgrimes if (tmpname[0] == '\0') { 7421556Srgrimes tty_prnt("Skipping file.\n"); 7431556Srgrimes return(1); 7441556Srgrimes } 7451556Srgrimes if ((tmpname[0] == '.') && (tmpname[1] == '\0')) { 7461556Srgrimes tty_prnt("Processing continues, name unchanged.\n"); 7471556Srgrimes return(0); 7481556Srgrimes } 7491556Srgrimes 7501556Srgrimes /* 7511556Srgrimes * ok the name changed. We may run into links that point at this 7521556Srgrimes * file later. we have to remember where the user sent the file 7531556Srgrimes * in order to repair any links. 7541556Srgrimes */ 7551556Srgrimes tty_prnt("Processing continues, name changed to: %s\n", tmpname); 7561556Srgrimes res = add_name(arcn->name, arcn->nlen, tmpname); 75776351Skris arcn->nlen = l_strncpy(arcn->name, tmpname, sizeof(arcn->name) - 1); 75876351Skris arcn->name[arcn->nlen] = '\0'; 7591556Srgrimes if (res < 0) 7601556Srgrimes return(-1); 7611556Srgrimes return(0); 7621556Srgrimes} 7631556Srgrimes 7641556Srgrimes/* 7651556Srgrimes * set_dest() 7661556Srgrimes * fix up the file name and the link name (if any) so this file will land 7671556Srgrimes * in the destination directory (used during copy() -rw). 7681556Srgrimes * Return: 7691556Srgrimes * 0 if ok, -1 if failure (name too long) 7701556Srgrimes */ 7711556Srgrimes 7721556Srgrimesint 77390113Simpset_dest(ARCHD *arcn, char *dest_dir, int dir_len) 7741556Srgrimes{ 7751556Srgrimes if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0) 7761556Srgrimes return(-1); 7771556Srgrimes 7781556Srgrimes /* 7791556Srgrimes * It is really hard to deal with symlinks here, we cannot be sure 7801556Srgrimes * if the name they point was moved (or will be moved). It is best to 7811556Srgrimes * leave them alone. 7821556Srgrimes */ 7831556Srgrimes if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG)) 7841556Srgrimes return(0); 7851556Srgrimes 7861556Srgrimes if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0) 7871556Srgrimes return(-1); 7881556Srgrimes return(0); 7891556Srgrimes} 7901556Srgrimes 7911556Srgrimes/* 7921556Srgrimes * fix_path 7931556Srgrimes * concatenate dir_name and or_name and store the result in or_name (if 7941556Srgrimes * it fits). This is one ugly function. 7951556Srgrimes * Return: 7961556Srgrimes * 0 if ok, -1 if the final name is too long 7971556Srgrimes */ 7981556Srgrimes 7991556Srgrimesstatic int 8001556Srgrimesfix_path( char *or_name, int *or_len, char *dir_name, int dir_len) 8011556Srgrimes{ 80290113Simp char *src; 80390113Simp char *dest; 80490113Simp char *start; 8051556Srgrimes int len; 8061556Srgrimes 8071556Srgrimes /* 8081556Srgrimes * we shift the or_name to the right enough to tack in the dir_name 8091556Srgrimes * at the front. We make sure we have enough space for it all before 8101556Srgrimes * we start. since dest always ends in a slash, we skip of or_name 8111556Srgrimes * if it also starts with one. 8121556Srgrimes */ 8131556Srgrimes start = or_name; 8141556Srgrimes src = start + *or_len; 8151556Srgrimes dest = src + dir_len; 8161556Srgrimes if (*start == '/') { 8171556Srgrimes ++start; 8181556Srgrimes --dest; 8191556Srgrimes } 8201556Srgrimes if ((len = dest - or_name) > PAXPATHLEN) { 82176017Skris paxwarn(1, "File name %s/%s, too long", dir_name, start); 8221556Srgrimes return(-1); 8231556Srgrimes } 8241556Srgrimes *or_len = len; 8251556Srgrimes 8261556Srgrimes /* 8278855Srgrimes * enough space, shift 8281556Srgrimes */ 8291556Srgrimes while (src >= start) 8301556Srgrimes *dest-- = *src--; 8311556Srgrimes src = dir_name + dir_len - 1; 8321556Srgrimes 8331556Srgrimes /* 8341556Srgrimes * splice in the destination directory name 8351556Srgrimes */ 8361556Srgrimes while (src >= dir_name) 8371556Srgrimes *dest-- = *src--; 8381556Srgrimes 8391556Srgrimes *(or_name + len) = '\0'; 8401556Srgrimes return(0); 8411556Srgrimes} 8421556Srgrimes 8431556Srgrimes/* 8441556Srgrimes * rep_name() 8451556Srgrimes * walk down the list of replacement strings applying each one in order. 8461556Srgrimes * when we find one with a successful substitution, we modify the name 8471556Srgrimes * as specified. if required, we print the results. if the resulting name 8481556Srgrimes * is empty, we will skip this archive member. We use the regexp(3) 8491556Srgrimes * routines (regexp() ought to win a prize as having the most cryptic 8501556Srgrimes * library function manual page). 8511556Srgrimes * --Parameters-- 8521556Srgrimes * name is the file name we are going to apply the regular expressions to 8531556Srgrimes * (and may be modified) 8541556Srgrimes * nlen is the length of this name (and is modified to hold the length of 8551556Srgrimes * the final string). 8561556Srgrimes * prnt is a flag that says whether to print the final result. 8571556Srgrimes * Return: 8581556Srgrimes * 0 if substitution was successful, 1 if we are to skip the file (the name 8591556Srgrimes * ended up empty) 8601556Srgrimes */ 8611556Srgrimes 8621556Srgrimesstatic int 8631556Srgrimesrep_name(char *name, int *nlen, int prnt) 8641556Srgrimes{ 86590113Simp REPLACE *pt; 86690113Simp char *inpt; 86790113Simp char *outpt; 86890113Simp char *endpt; 86990113Simp char *rpt; 87090113Simp int found = 0; 87190113Simp int res; 8721556Srgrimes# ifndef NET2_REGEX 8731556Srgrimes regmatch_t pm[MAXSUBEXP]; 8741556Srgrimes# endif 8751556Srgrimes char nname[PAXPATHLEN+1]; /* final result of all replacements */ 8761556Srgrimes char buf1[PAXPATHLEN+1]; /* where we work on the name */ 8771556Srgrimes 8781556Srgrimes /* 8791556Srgrimes * copy the name into buf1, where we will work on it. We need to keep 8801556Srgrimes * the orig string around so we can print out the result of the final 8811556Srgrimes * replacement. We build up the final result in nname. inpt points at 8821556Srgrimes * the string we apply the regular expression to. prnt is used to 8831556Srgrimes * suppress printing when we handle replacements on the link field 8841556Srgrimes * (the user already saw that substitution go by) 8851556Srgrimes */ 8861556Srgrimes pt = rephead; 8871556Srgrimes (void)strcpy(buf1, name); 8881556Srgrimes inpt = buf1; 8891556Srgrimes outpt = nname; 8901556Srgrimes endpt = outpt + PAXPATHLEN; 8911556Srgrimes 8921556Srgrimes /* 8931556Srgrimes * try each replacement string in order 8941556Srgrimes */ 8951556Srgrimes while (pt != NULL) { 8961556Srgrimes do { 8971556Srgrimes /* 8981556Srgrimes * check for a successful substitution, if not go to 8991556Srgrimes * the next pattern, or cleanup if we were global 9001556Srgrimes */ 9011556Srgrimes# ifdef NET2_REGEX 9021556Srgrimes if (regexec(pt->rcmp, inpt) == 0) 9031556Srgrimes# else 9041556Srgrimes if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0) 9051556Srgrimes# endif 9061556Srgrimes break; 9071556Srgrimes 9081556Srgrimes /* 9091556Srgrimes * ok we found one. We have three parts, the prefix 9101556Srgrimes * which did not match, the section that did and the 9111556Srgrimes * tail (that also did not match). Copy the prefix to 9121556Srgrimes * the final output buffer (watching to make sure we 9131556Srgrimes * do not create a string too long). 9141556Srgrimes */ 9151556Srgrimes found = 1; 9161556Srgrimes# ifdef NET2_REGEX 9171556Srgrimes rpt = pt->rcmp->startp[0]; 9181556Srgrimes# else 9191556Srgrimes rpt = inpt + pm[0].rm_so; 9201556Srgrimes# endif 9211556Srgrimes 9221556Srgrimes while ((inpt < rpt) && (outpt < endpt)) 9231556Srgrimes *outpt++ = *inpt++; 9241556Srgrimes if (outpt == endpt) 9251556Srgrimes break; 9261556Srgrimes 9271556Srgrimes /* 9281556Srgrimes * for the second part (which matched the regular 9291556Srgrimes * expression) apply the substitution using the 9301556Srgrimes * replacement string and place it the prefix in the 9311556Srgrimes * final output. If we have problems, skip it. 9321556Srgrimes */ 9331556Srgrimes# ifdef NET2_REGEX 9341556Srgrimes if ((res = resub(pt->rcmp,pt->nstr,outpt,endpt)) < 0) { 9351556Srgrimes# else 9361556Srgrimes if ((res = resub(&(pt->rcmp),pm,pt->nstr,outpt,endpt)) 9371556Srgrimes < 0) { 9381556Srgrimes# endif 9391556Srgrimes if (prnt) 94076017Skris paxwarn(1, "Replacement name error %s", 9411556Srgrimes name); 9421556Srgrimes return(1); 9431556Srgrimes } 9441556Srgrimes outpt += res; 9451556Srgrimes 9461556Srgrimes /* 9471556Srgrimes * we set up to look again starting at the first 9481556Srgrimes * character in the tail (of the input string right 9491556Srgrimes * after the last character matched by the regular 9501556Srgrimes * expression (inpt always points at the first char in 9511556Srgrimes * the string to process). If we are not doing a global 9521556Srgrimes * substitution, we will use inpt to copy the tail to 9531556Srgrimes * the final result. Make sure we do not overrun the 9541556Srgrimes * output buffer 9551556Srgrimes */ 9561556Srgrimes# ifdef NET2_REGEX 9571556Srgrimes inpt = pt->rcmp->endp[0]; 9581556Srgrimes# else 95941539Sobrien inpt += pm[0].rm_eo - pm[0].rm_so; 9601556Srgrimes# endif 9611556Srgrimes 9621556Srgrimes if ((outpt == endpt) || (*inpt == '\0')) 9631556Srgrimes break; 9641556Srgrimes 9651556Srgrimes /* 9661556Srgrimes * if the user wants global we keep trying to 9671556Srgrimes * substitute until it fails, then we are done. 9681556Srgrimes */ 9691556Srgrimes } while (pt->flgs & GLOB); 9701556Srgrimes 9718855Srgrimes if (found) 9721556Srgrimes break; 9731556Srgrimes 9741556Srgrimes /* 9751556Srgrimes * a successful substitution did NOT occur, try the next one 9761556Srgrimes */ 9771556Srgrimes pt = pt->fow; 9781556Srgrimes } 9791556Srgrimes 9801556Srgrimes if (found) { 9811556Srgrimes /* 9821556Srgrimes * we had a substitution, copy the last tail piece (if there is 9831556Srgrimes * room) to the final result 9841556Srgrimes */ 9851556Srgrimes while ((outpt < endpt) && (*inpt != '\0')) 9861556Srgrimes *outpt++ = *inpt++; 9871556Srgrimes 9881556Srgrimes *outpt = '\0'; 9891556Srgrimes if ((outpt == endpt) && (*inpt != '\0')) { 9901556Srgrimes if (prnt) 99176017Skris paxwarn(1,"Replacement name too long %s >> %s", 9921556Srgrimes name, nname); 9931556Srgrimes return(1); 9948855Srgrimes } 9951556Srgrimes 9961556Srgrimes /* 9971556Srgrimes * inform the user of the result if wanted 9981556Srgrimes */ 9991556Srgrimes if (prnt && (pt->flgs & PRNT)) { 10001556Srgrimes if (*nname == '\0') 10011556Srgrimes (void)fprintf(stderr,"%s >> <empty string>\n", 10021556Srgrimes name); 10038855Srgrimes else 10041556Srgrimes (void)fprintf(stderr,"%s >> %s\n", name, nname); 10051556Srgrimes } 10061556Srgrimes 10071556Srgrimes /* 10081556Srgrimes * if empty inform the caller this file is to be skipped 10091556Srgrimes * otherwise copy the new name over the orig name and return 10101556Srgrimes */ 10118855Srgrimes if (*nname == '\0') 10121556Srgrimes return(1); 10131556Srgrimes *nlen = l_strncpy(name, nname, PAXPATHLEN + 1); 101426363Scharnier name[PAXPATHLEN] = '\0'; 10151556Srgrimes } 10161556Srgrimes return(0); 10171556Srgrimes} 10181556Srgrimes 10191556Srgrimes#ifdef NET2_REGEX 10201556Srgrimes/* 10211556Srgrimes * resub() 10221556Srgrimes * apply the replacement to the matched expression. expand out the old 10231556Srgrimes * style ed(1) subexpression expansion. 10241556Srgrimes * Return: 10251556Srgrimes * -1 if error, or the number of characters added to the destination. 10261556Srgrimes */ 10271556Srgrimes 10281556Srgrimesstatic int 102990113Simpresub(regexp *prog, char *src, char *dest, char *destend) 10301556Srgrimes{ 103190113Simp char *spt; 103290113Simp char *dpt; 103390113Simp char c; 103490113Simp int no; 103590113Simp int len; 10361556Srgrimes 10371556Srgrimes spt = src; 10381556Srgrimes dpt = dest; 10391556Srgrimes while ((dpt < destend) && ((c = *spt++) != '\0')) { 10401556Srgrimes if (c == '&') 10411556Srgrimes no = 0; 10421556Srgrimes else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) 10431556Srgrimes no = *spt++ - '0'; 10441556Srgrimes else { 10451556Srgrimes if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) 10461556Srgrimes c = *spt++; 10471556Srgrimes *dpt++ = c; 10481556Srgrimes continue; 10491556Srgrimes } 10501556Srgrimes if ((prog->startp[no] == NULL) || (prog->endp[no] == NULL) || 10511556Srgrimes ((len = prog->endp[no] - prog->startp[no]) <= 0)) 10521556Srgrimes continue; 10531556Srgrimes 10541556Srgrimes /* 10551556Srgrimes * copy the subexpression to the destination. 10561556Srgrimes * fail if we run out of space or the match string is damaged 10571556Srgrimes */ 10581556Srgrimes if (len > (destend - dpt)) 10591556Srgrimes len = destend - dpt; 10601556Srgrimes if (l_strncpy(dpt, prog->startp[no], len) != len) 10611556Srgrimes return(-1); 10621556Srgrimes dpt += len; 10631556Srgrimes } 10641556Srgrimes return(dpt - dest); 10651556Srgrimes} 10661556Srgrimes 10671556Srgrimes#else 10681556Srgrimes 10691556Srgrimes/* 10701556Srgrimes * resub() 10711556Srgrimes * apply the replacement to the matched expression. expand out the old 10721556Srgrimes * style ed(1) subexpression expansion. 10731556Srgrimes * Return: 10741556Srgrimes * -1 if error, or the number of characters added to the destination. 10751556Srgrimes */ 10761556Srgrimes 10771556Srgrimesstatic int 107890113Simpresub(regex_t *rp, regmatch_t *pm, char *src, char *dest, 107990113Simp char *destend) 10801556Srgrimes{ 108190113Simp char *spt; 108290113Simp char *dpt; 108390113Simp char c; 108490113Simp regmatch_t *pmpt; 108590113Simp int len; 10861556Srgrimes int subexcnt; 10871556Srgrimes 10881556Srgrimes spt = src; 10891556Srgrimes dpt = dest; 10901556Srgrimes subexcnt = rp->re_nsub; 10911556Srgrimes while ((dpt < destend) && ((c = *spt++) != '\0')) { 10921556Srgrimes /* 10931556Srgrimes * see if we just have an ordinary replacement character 10941556Srgrimes * or we refer to a subexpression. 10951556Srgrimes */ 10961556Srgrimes if (c == '&') { 10971556Srgrimes pmpt = pm; 10981556Srgrimes } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) { 10991556Srgrimes /* 11001556Srgrimes * make sure there is a subexpression as specified 11011556Srgrimes */ 11021556Srgrimes if ((len = *spt++ - '0') > subexcnt) 11031556Srgrimes return(-1); 11041556Srgrimes pmpt = pm + len; 11051556Srgrimes } else { 11061556Srgrimes /* 11071556Srgrimes * Ordinary character, just copy it 11081556Srgrimes */ 11091556Srgrimes if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) 11101556Srgrimes c = *spt++; 11111556Srgrimes *dpt++ = c; 11121556Srgrimes continue; 11131556Srgrimes } 11141556Srgrimes 11151556Srgrimes /* 11161556Srgrimes * continue if the subexpression is bogus 11171556Srgrimes */ 11181556Srgrimes if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) || 11191556Srgrimes ((len = pmpt->rm_eo - pmpt->rm_so) <= 0)) 11201556Srgrimes continue; 11211556Srgrimes 11221556Srgrimes /* 11231556Srgrimes * copy the subexpression to the destination. 11241556Srgrimes * fail if we run out of space or the match string is damaged 11251556Srgrimes */ 11261556Srgrimes if (len > (destend - dpt)) 11271556Srgrimes len = destend - dpt; 11281556Srgrimes if (l_strncpy(dpt, src + pmpt->rm_so, len) != len) 11291556Srgrimes return(-1); 11301556Srgrimes dpt += len; 11311556Srgrimes } 11321556Srgrimes return(dpt - dest); 11331556Srgrimes} 11341556Srgrimes#endif 1135