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 * 4. Neither the name of the University nor the names of its contributors 181556Srgrimes * may be used to endorse or promote products derived from this software 191556Srgrimes * without specific prior written permission. 201556Srgrimes * 211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311556Srgrimes * SUCH DAMAGE. 321556Srgrimes */ 331556Srgrimes 341556Srgrimes#ifndef lint 3536049Scharnier#if 0 3636049Scharnierstatic char sccsid[] = "@(#)pat_rep.c 8.2 (Berkeley) 4/18/94"; 3736049Scharnier#endif 381556Srgrimes#endif /* not lint */ 3999110Sobrien#include <sys/cdefs.h> 4099110Sobrien__FBSDID("$FreeBSD$"); 411556Srgrimes 421556Srgrimes#include <sys/types.h> 431556Srgrimes#include <sys/stat.h> 441556Srgrimes#include <stdio.h> 451556Srgrimes#include <string.h> 461556Srgrimes#include <unistd.h> 471556Srgrimes#include <stdlib.h> 4876351Skris#include <errno.h> 491556Srgrimes#ifdef NET2_REGEX 501556Srgrimes#include <regexp.h> 511556Srgrimes#else 521556Srgrimes#include <regex.h> 531556Srgrimes#endif 541556Srgrimes#include "pax.h" 551556Srgrimes#include "pat_rep.h" 561556Srgrimes#include "extern.h" 571556Srgrimes 581556Srgrimes/* 591556Srgrimes * routines to handle pattern matching, name modification (regular expression 601556Srgrimes * substitution and interactive renames), and destination name modification for 611556Srgrimes * copy (-rw). Both file name and link names are adjusted as required in these 621556Srgrimes * routines. 631556Srgrimes */ 641556Srgrimes 651556Srgrimes#define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */ 661556Srgrimesstatic PATTERN *pathead = NULL; /* file pattern match list head */ 671556Srgrimesstatic PATTERN *pattail = NULL; /* file pattern match list tail */ 681556Srgrimesstatic REPLACE *rephead = NULL; /* replacement string list head */ 691556Srgrimesstatic REPLACE *reptail = NULL; /* replacement string list tail */ 701556Srgrimes 7190110Simpstatic int rep_name(char *, int *, int); 7290113Simpstatic int tty_rename(ARCHD *); 7390110Simpstatic int fix_path(char *, int *, char *, int); 7490113Simpstatic int fn_match(char *, char *, char **); 7590113Simpstatic char * range_match(char *, int); 761556Srgrimes#ifdef NET2_REGEX 7790113Simpstatic int resub(regexp *, char *, char *, char *); 781556Srgrimes#else 79208484Suqsstatic int resub(regex_t *, regmatch_t *, char *, char *, char *, char *); 801556Srgrimes#endif 811556Srgrimes 821556Srgrimes/* 831556Srgrimes * rep_add() 841556Srgrimes * parses the -s replacement string; compiles the regular expression 851556Srgrimes * and stores the compiled value and it's replacement string together in 861556Srgrimes * replacement string list. Input to this function is of the form: 878855Srgrimes * /old/new/pg 881556Srgrimes * The first char in the string specifies the delimiter used by this 891556Srgrimes * replacement string. "Old" is a regular expression in "ed" format which 901556Srgrimes * is compiled by regcomp() and is applied to filenames. "new" is the 911556Srgrimes * substitution string; p and g are options flags for printing and global 921556Srgrimes * replacement (over the single filename) 931556Srgrimes * Return: 941556Srgrimes * 0 if a proper replacement string and regular expression was added to 951556Srgrimes * the list of replacement patterns; -1 otherwise. 961556Srgrimes */ 971556Srgrimes 981556Srgrimesint 9990113Simprep_add(char *str) 1001556Srgrimes{ 10190113Simp char *pt1; 10290113Simp char *pt2; 10390113Simp REPLACE *rep; 1041556Srgrimes# ifndef NET2_REGEX 10590113Simp int res; 1061556Srgrimes char rebuf[BUFSIZ]; 1071556Srgrimes# endif 1081556Srgrimes 1091556Srgrimes /* 1101556Srgrimes * throw out the bad parameters 1111556Srgrimes */ 1121556Srgrimes if ((str == NULL) || (*str == '\0')) { 11376017Skris paxwarn(1, "Empty replacement string"); 1141556Srgrimes return(-1); 1151556Srgrimes } 1161556Srgrimes 1171556Srgrimes /* 1181556Srgrimes * first character in the string specifies what the delimiter is for 1191556Srgrimes * this expression 1201556Srgrimes */ 1211556Srgrimes if ((pt1 = strchr(str+1, *str)) == NULL) { 12276017Skris paxwarn(1, "Invalid replacement string %s", str); 1231556Srgrimes return(-1); 1241556Srgrimes } 1251556Srgrimes 1261556Srgrimes /* 1271556Srgrimes * allocate space for the node that handles this replacement pattern 1281556Srgrimes * and split out the regular expression and try to compile it 1291556Srgrimes */ 1301556Srgrimes if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) { 13176017Skris paxwarn(1, "Unable to allocate memory for replacement string"); 1321556Srgrimes return(-1); 1331556Srgrimes } 1341556Srgrimes 1351556Srgrimes *pt1 = '\0'; 1361556Srgrimes# ifdef NET2_REGEX 1371556Srgrimes if ((rep->rcmp = regcomp(str+1)) == NULL) { 1381556Srgrimes# else 1391556Srgrimes if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { 1401556Srgrimes regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); 14176017Skris paxwarn(1, "%s while compiling regular expression %s", rebuf, str); 1421556Srgrimes# endif 143169993Sbrian free(rep); 1441556Srgrimes return(-1); 1451556Srgrimes } 1461556Srgrimes 1471556Srgrimes /* 1481556Srgrimes * put the delimiter back in case we need an error message and 1491556Srgrimes * locate the delimiter at the end of the replacement string 1501556Srgrimes * we then point the node at the new substitution string 1511556Srgrimes */ 1521556Srgrimes *pt1++ = *str; 1531556Srgrimes if ((pt2 = strchr(pt1, *str)) == NULL) { 1541556Srgrimes# ifdef NET2_REGEX 155169993Sbrian free(rep->rcmp); 1561556Srgrimes# else 157169993Sbrian regfree(&rep->rcmp); 1581556Srgrimes# endif 159169993Sbrian free(rep); 16076017Skris paxwarn(1, "Invalid replacement string %s", str); 1611556Srgrimes return(-1); 1621556Srgrimes } 1631556Srgrimes 1641556Srgrimes *pt2 = '\0'; 1651556Srgrimes rep->nstr = pt1; 1661556Srgrimes pt1 = pt2++; 1671556Srgrimes rep->flgs = 0; 1681556Srgrimes 1691556Srgrimes /* 1701556Srgrimes * set the options if any 1711556Srgrimes */ 1721556Srgrimes while (*pt2 != '\0') { 1731556Srgrimes switch(*pt2) { 1741556Srgrimes case 'g': 1751556Srgrimes case 'G': 1761556Srgrimes rep->flgs |= GLOB; 1771556Srgrimes break; 1781556Srgrimes case 'p': 1791556Srgrimes case 'P': 1801556Srgrimes rep->flgs |= PRNT; 1811556Srgrimes break; 1821556Srgrimes default: 1831556Srgrimes# ifdef NET2_REGEX 184169993Sbrian free(rep->rcmp); 1851556Srgrimes# else 186169993Sbrian regfree(&rep->rcmp); 1871556Srgrimes# endif 188169993Sbrian free(rep); 1891556Srgrimes *pt1 = *str; 19076017Skris paxwarn(1, "Invalid replacement string option %s", str); 1911556Srgrimes return(-1); 1921556Srgrimes } 1931556Srgrimes ++pt2; 1941556Srgrimes } 1951556Srgrimes 1961556Srgrimes /* 1971556Srgrimes * all done, link it in at the end 1981556Srgrimes */ 1991556Srgrimes rep->fow = NULL; 2001556Srgrimes if (rephead == NULL) { 2011556Srgrimes reptail = rephead = rep; 2021556Srgrimes return(0); 2031556Srgrimes } 2041556Srgrimes reptail->fow = rep; 2051556Srgrimes reptail = rep; 2061556Srgrimes return(0); 2071556Srgrimes} 2081556Srgrimes 2091556Srgrimes/* 2101556Srgrimes * pat_add() 2111556Srgrimes * add a pattern match to the pattern match list. Pattern matches are used 2121556Srgrimes * to select which archive members are extracted. (They appear as 2131556Srgrimes * arguments to pax in the list and read modes). If no patterns are 2141556Srgrimes * supplied to pax, all members in the archive will be selected (and the 2151556Srgrimes * pattern match list is empty). 2161556Srgrimes * Return: 2171556Srgrimes * 0 if the pattern was added to the list, -1 otherwise 2181556Srgrimes */ 2191556Srgrimes 2201556Srgrimesint 221114583Smarkmpat_add(char *str, char *chdnam) 2221556Srgrimes{ 22390113Simp PATTERN *pt; 2241556Srgrimes 2251556Srgrimes /* 2261556Srgrimes * throw out the junk 2271556Srgrimes */ 2281556Srgrimes if ((str == NULL) || (*str == '\0')) { 22976017Skris paxwarn(1, "Empty pattern string"); 2301556Srgrimes return(-1); 2311556Srgrimes } 2321556Srgrimes 2331556Srgrimes /* 2341556Srgrimes * allocate space for the pattern and store the pattern. the pattern is 2351556Srgrimes * part of argv so do not bother to copy it, just point at it. Add the 2361556Srgrimes * node to the end of the pattern list 2371556Srgrimes */ 2381556Srgrimes if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { 23976017Skris paxwarn(1, "Unable to allocate memory for pattern string"); 2401556Srgrimes return(-1); 2411556Srgrimes } 2421556Srgrimes 2431556Srgrimes pt->pstr = str; 2441556Srgrimes pt->pend = NULL; 2451556Srgrimes pt->plen = strlen(str); 2461556Srgrimes pt->fow = NULL; 2471556Srgrimes pt->flgs = 0; 248114583Smarkm pt->chdname = chdnam; 24976351Skris 2501556Srgrimes if (pathead == NULL) { 2511556Srgrimes pattail = pathead = pt; 2521556Srgrimes return(0); 2531556Srgrimes } 2541556Srgrimes pattail->fow = pt; 2551556Srgrimes pattail = pt; 2561556Srgrimes return(0); 2571556Srgrimes} 2581556Srgrimes 2591556Srgrimes/* 2601556Srgrimes * pat_chk() 2611556Srgrimes * complain if any the user supplied pattern did not result in a match to 2621556Srgrimes * a selected archive member. 2631556Srgrimes */ 2641556Srgrimes 2651556Srgrimesvoid 2661556Srgrimespat_chk(void) 2671556Srgrimes{ 26890113Simp PATTERN *pt; 26990113Simp int wban = 0; 2701556Srgrimes 2711556Srgrimes /* 2721556Srgrimes * walk down the list checking the flags to make sure MTCH was set, 2731556Srgrimes * if not complain 2741556Srgrimes */ 2751556Srgrimes for (pt = pathead; pt != NULL; pt = pt->fow) { 2761556Srgrimes if (pt->flgs & MTCH) 2771556Srgrimes continue; 2781556Srgrimes if (!wban) { 27976017Skris paxwarn(1, "WARNING! These patterns were not matched:"); 2801556Srgrimes ++wban; 2811556Srgrimes } 2821556Srgrimes (void)fprintf(stderr, "%s\n", pt->pstr); 2831556Srgrimes } 2841556Srgrimes} 2851556Srgrimes 2861556Srgrimes/* 2871556Srgrimes * pat_sel() 2881556Srgrimes * the archive member which matches a pattern was selected. Mark the 2891556Srgrimes * pattern as having selected an archive member. arcn->pat points at the 2901556Srgrimes * pattern that was matched. arcn->pat is set in pat_match() 2911556Srgrimes * 2921556Srgrimes * NOTE: When the -c option is used, we are called when there was no match 2931556Srgrimes * by pat_match() (that means we did match before the inverted sense of 2941556Srgrimes * the logic). Now this seems really strange at first, but with -c we 295108533Sschweikh * need to keep track of those patterns that cause an archive member to NOT 2961556Srgrimes * be selected (it found an archive member with a specified pattern) 2971556Srgrimes * Return: 2981556Srgrimes * 0 if the pattern pointed at by arcn->pat was tagged as creating a 2991556Srgrimes * match, -1 otherwise. 3001556Srgrimes */ 3011556Srgrimes 3021556Srgrimesint 30390113Simppat_sel(ARCHD *arcn) 3041556Srgrimes{ 30590113Simp PATTERN *pt; 30690113Simp PATTERN **ppt; 30790113Simp int len; 3081556Srgrimes 3091556Srgrimes /* 3101556Srgrimes * if no patterns just return 3111556Srgrimes */ 3121556Srgrimes if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) 3131556Srgrimes return(0); 3141556Srgrimes 3151556Srgrimes /* 3161556Srgrimes * when we are NOT limited to a single match per pattern mark the 3171556Srgrimes * pattern and return 3181556Srgrimes */ 3191556Srgrimes if (!nflag) { 3201556Srgrimes pt->flgs |= MTCH; 3211556Srgrimes return(0); 3221556Srgrimes } 3231556Srgrimes 3241556Srgrimes /* 3251556Srgrimes * we reach this point only when we allow a single selected match per 3268855Srgrimes * pattern, if the pattern matches a directory and we do not have -d 3271556Srgrimes * (dflag) we are done with this pattern. We may also be handed a file 3281556Srgrimes * in the subtree of a directory. in that case when we are operating 3291556Srgrimes * with -d, this pattern was already selected and we are done 3301556Srgrimes */ 3311556Srgrimes if (pt->flgs & DIR_MTCH) 3321556Srgrimes return(0); 3331556Srgrimes 3341556Srgrimes if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { 3351556Srgrimes /* 3361556Srgrimes * ok we matched a directory and we are allowing 3371556Srgrimes * subtree matches but because of the -n only its children will 3381556Srgrimes * match. This is tagged as a DIR_MTCH type. 3391556Srgrimes * WATCH IT, the code assumes that pt->pend points 3401556Srgrimes * into arcn->name and arcn->name has not been modified. 3411556Srgrimes * If not we will have a big mess. Yup this is another kludge 3421556Srgrimes */ 3431556Srgrimes 3441556Srgrimes /* 3451556Srgrimes * if this was a prefix match, remove trailing part of path 3461556Srgrimes * so we can copy it. Future matches will be exact prefix match 3471556Srgrimes */ 3481556Srgrimes if (pt->pend != NULL) 3491556Srgrimes *pt->pend = '\0'; 3508855Srgrimes 3511556Srgrimes if ((pt->pstr = strdup(arcn->name)) == NULL) { 35276017Skris paxwarn(1, "Pattern select out of memory"); 3531556Srgrimes if (pt->pend != NULL) 3541556Srgrimes *pt->pend = '/'; 3551556Srgrimes pt->pend = NULL; 3561556Srgrimes return(-1); 3571556Srgrimes } 3581556Srgrimes 3591556Srgrimes /* 3601556Srgrimes * put the trailing / back in the source string 3611556Srgrimes */ 3621556Srgrimes if (pt->pend != NULL) { 3631556Srgrimes *pt->pend = '/'; 3641556Srgrimes pt->pend = NULL; 3651556Srgrimes } 3661556Srgrimes pt->plen = strlen(pt->pstr); 3671556Srgrimes 3681556Srgrimes /* 3691556Srgrimes * strip off any trailing /, this should really never happen 3701556Srgrimes */ 3711556Srgrimes len = pt->plen - 1; 3721556Srgrimes if (*(pt->pstr + len) == '/') { 3731556Srgrimes *(pt->pstr + len) = '\0'; 3741556Srgrimes pt->plen = len; 3758855Srgrimes } 3761556Srgrimes pt->flgs = DIR_MTCH | MTCH; 3771556Srgrimes arcn->pat = pt; 3781556Srgrimes return(0); 3791556Srgrimes } 3801556Srgrimes 3811556Srgrimes /* 3821556Srgrimes * we are then done with this pattern, so we delete it from the list 3831556Srgrimes * because it can never be used for another match. 3841556Srgrimes * Seems kind of strange to do for a -c, but the pax spec is really 3851556Srgrimes * vague on the interaction of -c -n and -d. We assume that when -c 3861556Srgrimes * and the pattern rejects a member (i.e. it matched it) it is done. 3871556Srgrimes * In effect we place the order of the flags as having -c last. 3881556Srgrimes */ 3891556Srgrimes pt = pathead; 3901556Srgrimes ppt = &pathead; 3911556Srgrimes while ((pt != NULL) && (pt != arcn->pat)) { 3921556Srgrimes ppt = &(pt->fow); 3931556Srgrimes pt = pt->fow; 3941556Srgrimes } 3951556Srgrimes 3961556Srgrimes if (pt == NULL) { 3971556Srgrimes /* 3981556Srgrimes * should never happen.... 3991556Srgrimes */ 400222177Suqs paxwarn(1, "Pattern list inconsistent"); 4011556Srgrimes return(-1); 4021556Srgrimes } 4031556Srgrimes *ppt = pt->fow; 404169993Sbrian free(pt); 4051556Srgrimes arcn->pat = NULL; 4061556Srgrimes return(0); 4071556Srgrimes} 4081556Srgrimes 4091556Srgrimes/* 4101556Srgrimes * pat_match() 4111556Srgrimes * see if this archive member matches any supplied pattern, if a match 4121556Srgrimes * is found, arcn->pat is set to point at the potential pattern. Later if 4131556Srgrimes * this archive member is "selected" we process and mark the pattern as 4141556Srgrimes * one which matched a selected archive member (see pat_sel()) 4151556Srgrimes * Return: 4168855Srgrimes * 0 if this archive member should be processed, 1 if it should be 4171556Srgrimes * skipped and -1 if we are done with all patterns (and pax should quit 4181556Srgrimes * looking for more members) 4191556Srgrimes */ 4201556Srgrimes 4211556Srgrimesint 42290113Simppat_match(ARCHD *arcn) 4231556Srgrimes{ 42490113Simp PATTERN *pt; 4251556Srgrimes 4261556Srgrimes arcn->pat = NULL; 4271556Srgrimes 4281556Srgrimes /* 4291556Srgrimes * if there are no more patterns and we have -n (and not -c) we are 4301556Srgrimes * done. otherwise with no patterns to match, matches all 4311556Srgrimes */ 4321556Srgrimes if (pathead == NULL) { 4331556Srgrimes if (nflag && !cflag) 4341556Srgrimes return(-1); 4351556Srgrimes return(0); 4361556Srgrimes } 4371556Srgrimes 4381556Srgrimes /* 4391556Srgrimes * have to search down the list one at a time looking for a match. 4401556Srgrimes */ 4411556Srgrimes pt = pathead; 4421556Srgrimes while (pt != NULL) { 4431556Srgrimes /* 4441556Srgrimes * check for a file name match unless we have DIR_MTCH set in 4451556Srgrimes * this pattern then we want a prefix match 4461556Srgrimes */ 4471556Srgrimes if (pt->flgs & DIR_MTCH) { 4481556Srgrimes /* 4491556Srgrimes * this pattern was matched before to a directory 4501556Srgrimes * as we must have -n set for this (but not -d). We can 4511556Srgrimes * only match CHILDREN of that directory so we must use 4521556Srgrimes * an exact prefix match (no wildcards). 4531556Srgrimes */ 4541556Srgrimes if ((arcn->name[pt->plen] == '/') && 4551556Srgrimes (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) 4561556Srgrimes break; 4571556Srgrimes } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) 4581556Srgrimes break; 4591556Srgrimes pt = pt->fow; 4601556Srgrimes } 4611556Srgrimes 4621556Srgrimes /* 4631556Srgrimes * return the result, remember that cflag (-c) inverts the sense of a 4641556Srgrimes * match 4651556Srgrimes */ 4661556Srgrimes if (pt == NULL) 4671556Srgrimes return(cflag ? 0 : 1); 4681556Srgrimes 4691556Srgrimes /* 470108533Sschweikh * We had a match, now when we invert the sense (-c) we reject this 4711556Srgrimes * member. However we have to tag the pattern a being successful, (in a 472108533Sschweikh * match, not in selecting an archive member) so we call pat_sel() here. 4731556Srgrimes */ 4741556Srgrimes arcn->pat = pt; 4751556Srgrimes if (!cflag) 4761556Srgrimes return(0); 4771556Srgrimes 4781556Srgrimes if (pat_sel(arcn) < 0) 4791556Srgrimes return(-1); 4801556Srgrimes arcn->pat = NULL; 4811556Srgrimes return(1); 4821556Srgrimes} 4831556Srgrimes 4841556Srgrimes/* 4851556Srgrimes * fn_match() 4861556Srgrimes * Return: 4878855Srgrimes * 0 if this archive member should be processed, 1 if it should be 4881556Srgrimes * skipped and -1 if we are done with all patterns (and pax should quit 4891556Srgrimes * looking for more members) 4901556Srgrimes * Note: *pend may be changed to show where the prefix ends. 4911556Srgrimes */ 4921556Srgrimes 4931556Srgrimesstatic int 49490113Simpfn_match(char *pattern, char *string, char **pend) 4951556Srgrimes{ 49690113Simp char c; 4971556Srgrimes char test; 4981556Srgrimes 4991556Srgrimes *pend = NULL; 5001556Srgrimes for (;;) { 5011556Srgrimes switch (c = *pattern++) { 5021556Srgrimes case '\0': 5031556Srgrimes /* 5041556Srgrimes * Ok we found an exact match 5051556Srgrimes */ 5061556Srgrimes if (*string == '\0') 5071556Srgrimes return(0); 5081556Srgrimes 5091556Srgrimes /* 5101556Srgrimes * Check if it is a prefix match 5111556Srgrimes */ 5121556Srgrimes if ((dflag == 1) || (*string != '/')) 5131556Srgrimes return(-1); 5141556Srgrimes 5151556Srgrimes /* 5161556Srgrimes * It is a prefix match, remember where the trailing 5171556Srgrimes * / is located 5181556Srgrimes */ 5191556Srgrimes *pend = string; 5201556Srgrimes return(0); 5211556Srgrimes case '?': 5221556Srgrimes if ((test = *string++) == '\0') 5231556Srgrimes return (-1); 5241556Srgrimes break; 5251556Srgrimes case '*': 5261556Srgrimes c = *pattern; 5271556Srgrimes /* 5288855Srgrimes * Collapse multiple *'s. 5291556Srgrimes */ 5301556Srgrimes while (c == '*') 5311556Srgrimes c = *++pattern; 5321556Srgrimes 5331556Srgrimes /* 5341556Srgrimes * Optimized hack for pattern with a * at the end 5351556Srgrimes */ 5361556Srgrimes if (c == '\0') 5371556Srgrimes return (0); 5381556Srgrimes 5391556Srgrimes /* 5401556Srgrimes * General case, use recursion. 5411556Srgrimes */ 5421556Srgrimes while ((test = *string) != '\0') { 5431556Srgrimes if (!fn_match(pattern, string, pend)) 5441556Srgrimes return (0); 5451556Srgrimes ++string; 5461556Srgrimes } 5471556Srgrimes return (-1); 5481556Srgrimes case '[': 5491556Srgrimes /* 5501556Srgrimes * range match 5511556Srgrimes */ 5521556Srgrimes if (((test = *string++) == '\0') || 5531556Srgrimes ((pattern = range_match(pattern, test)) == NULL)) 5541556Srgrimes return (-1); 5551556Srgrimes break; 5561556Srgrimes case '\\': 5571556Srgrimes default: 5581556Srgrimes if (c != *string++) 5591556Srgrimes return (-1); 5601556Srgrimes break; 5611556Srgrimes } 5621556Srgrimes } 5631556Srgrimes /* NOTREACHED */ 5641556Srgrimes} 5651556Srgrimes 5661556Srgrimesstatic char * 56790113Simprange_match(char *pattern, int test) 5681556Srgrimes{ 56990113Simp char c; 57090113Simp char c2; 5711556Srgrimes int negate; 5721556Srgrimes int ok = 0; 5731556Srgrimes 57476351Skris if ((negate = (*pattern == '!')) != 0) 5751556Srgrimes ++pattern; 5761556Srgrimes 5771556Srgrimes while ((c = *pattern++) != ']') { 5781556Srgrimes /* 5791556Srgrimes * Illegal pattern 5801556Srgrimes */ 5811556Srgrimes if (c == '\0') 5821556Srgrimes return (NULL); 5831556Srgrimes 5841556Srgrimes if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') && 5851556Srgrimes (c2 != ']')) { 5861556Srgrimes if ((c <= test) && (test <= c2)) 5871556Srgrimes ok = 1; 5881556Srgrimes pattern += 2; 5891556Srgrimes } else if (c == test) 5901556Srgrimes ok = 1; 5911556Srgrimes } 5921556Srgrimes return (ok == negate ? NULL : pattern); 5931556Srgrimes} 5941556Srgrimes 5951556Srgrimes/* 5961556Srgrimes * mod_name() 5971556Srgrimes * modify a selected file name. first attempt to apply replacement string 5981556Srgrimes * expressions, then apply interactive file rename. We apply replacement 5991556Srgrimes * string expressions to both filenames and file links (if we didn't the 6001556Srgrimes * links would point to the wrong place, and we could never be able to 6011556Srgrimes * move an archive that has a file link in it). When we rename files 6021556Srgrimes * interactively, we store that mapping (old name to user input name) so 6031556Srgrimes * if we spot any file links to the old file name in the future, we will 6041556Srgrimes * know exactly how to fix the file link. 6051556Srgrimes * Return: 6068855Srgrimes * 0 continue to process file, 1 skip this file, -1 pax is finished 6071556Srgrimes */ 6081556Srgrimes 6091556Srgrimesint 61090113Simpmod_name(ARCHD *arcn) 6111556Srgrimes{ 61290113Simp int res = 0; 6131556Srgrimes 6141556Srgrimes /* 61576351Skris * Strip off leading '/' if appropriate. 61676351Skris * Currently, this option is only set for the tar format. 61776351Skris */ 61876351Skris if (rmleadslash && arcn->name[0] == '/') { 61976351Skris if (arcn->name[1] == '\0') { 62076351Skris arcn->name[0] = '.'; 62176351Skris } else { 62276351Skris (void)memmove(arcn->name, &arcn->name[1], 62376351Skris strlen(arcn->name)); 62476351Skris arcn->nlen--; 62576351Skris } 62676351Skris if (rmleadslash < 2) { 62776351Skris rmleadslash = 2; 62876351Skris paxwarn(0, "Removing leading / from absolute path names in the archive"); 62976351Skris } 63076351Skris } 63176351Skris if (rmleadslash && arcn->ln_name[0] == '/' && 63276351Skris (arcn->type == PAX_HLK || arcn->type == PAX_HRG)) { 63376351Skris if (arcn->ln_name[1] == '\0') { 63476351Skris arcn->ln_name[0] = '.'; 63576351Skris } else { 63676351Skris (void)memmove(arcn->ln_name, &arcn->ln_name[1], 63776351Skris strlen(arcn->ln_name)); 63876351Skris arcn->ln_nlen--; 63976351Skris } 64076351Skris if (rmleadslash < 2) { 64176351Skris rmleadslash = 2; 64276351Skris paxwarn(0, "Removing leading / from absolute path names in the archive"); 64376351Skris } 64476351Skris } 64576351Skris 64676351Skris /* 6471556Srgrimes * IMPORTANT: We have a problem. what do we do with symlinks? 6481556Srgrimes * Modifying a hard link name makes sense, as we know the file it 6491556Srgrimes * points at should have been seen already in the archive (and if it 6501556Srgrimes * wasn't seen because of a read error or a bad archive, we lose 6511556Srgrimes * anyway). But there are no such requirements for symlinks. On one 6521556Srgrimes * hand the symlink that refers to a file in the archive will have to 6531556Srgrimes * be modified to so it will still work at its new location in the 654102230Strhodes * file system. On the other hand a symlink that points elsewhere (and 6551556Srgrimes * should continue to do so) should not be modified. There is clearly 6561556Srgrimes * no perfect solution here. So we handle them like hardlinks. Clearly 6571556Srgrimes * a replacement made by the interactive rename mapping is very likely 6581556Srgrimes * to be correct since it applies to a single file and is an exact 6591556Srgrimes * match. The regular expression replacements are a little harder to 6601556Srgrimes * justify though. We claim that the symlink name is only likely 6611556Srgrimes * to be replaced when it points within the file tree being moved and 6621556Srgrimes * in that case it should be modified. what we really need to do is to 6631556Srgrimes * call an oracle here. :) 6641556Srgrimes */ 6651556Srgrimes if (rephead != NULL) { 6661556Srgrimes /* 6671556Srgrimes * we have replacement strings, modify the name and the link 6681556Srgrimes * name if any. 6691556Srgrimes */ 6701556Srgrimes if ((res = rep_name(arcn->name, &(arcn->nlen), 1)) != 0) 6711556Srgrimes return(res); 6721556Srgrimes 6731556Srgrimes if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || 6741556Srgrimes (arcn->type == PAX_HRG)) && 6751556Srgrimes ((res = rep_name(arcn->ln_name, &(arcn->ln_nlen), 0)) != 0)) 6761556Srgrimes return(res); 6771556Srgrimes } 6781556Srgrimes 6791556Srgrimes if (iflag) { 6801556Srgrimes /* 6811556Srgrimes * perform interactive file rename, then map the link if any 6821556Srgrimes */ 6831556Srgrimes if ((res = tty_rename(arcn)) != 0) 6841556Srgrimes return(res); 6851556Srgrimes if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || 6861556Srgrimes (arcn->type == PAX_HRG)) 68776351Skris sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name)); 6881556Srgrimes } 6891556Srgrimes return(res); 6901556Srgrimes} 6911556Srgrimes 6921556Srgrimes/* 6931556Srgrimes * tty_rename() 6941556Srgrimes * Prompt the user for a replacement file name. A "." keeps the old name, 6951556Srgrimes * a empty line skips the file, and an EOF on reading the tty, will cause 6961556Srgrimes * pax to stop processing and exit. Otherwise the file name input, replaces 6971556Srgrimes * the old one. 6981556Srgrimes * Return: 6991556Srgrimes * 0 process this file, 1 skip this file, -1 we need to exit pax 7001556Srgrimes */ 7011556Srgrimes 7021556Srgrimesstatic int 70390113Simptty_rename(ARCHD *arcn) 7041556Srgrimes{ 7051556Srgrimes char tmpname[PAXPATHLEN+2]; 7061556Srgrimes int res; 7071556Srgrimes 7081556Srgrimes /* 7091556Srgrimes * prompt user for the replacement name for a file, keep trying until 7101556Srgrimes * we get some reasonable input. Archives may have more than one file 7111556Srgrimes * on them with the same name (from updates etc). We print verbose info 7121556Srgrimes * on the file so the user knows what is up. 7131556Srgrimes */ 7141556Srgrimes tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0); 7151556Srgrimes 7161556Srgrimes for (;;) { 7171556Srgrimes ls_tty(arcn); 7181556Srgrimes tty_prnt("Input new name, or a \".\" to keep the old name, "); 7191556Srgrimes tty_prnt("or a \"return\" to skip this file.\n"); 7201556Srgrimes tty_prnt("Input > "); 7211556Srgrimes if (tty_read(tmpname, sizeof(tmpname)) < 0) 7221556Srgrimes return(-1); 7231556Srgrimes if (strcmp(tmpname, "..") == 0) { 7241556Srgrimes tty_prnt("Try again, illegal file name: ..\n"); 7251556Srgrimes continue; 7261556Srgrimes } 7271556Srgrimes if (strlen(tmpname) > PAXPATHLEN) { 7281556Srgrimes tty_prnt("Try again, file name too long\n"); 7291556Srgrimes continue; 7301556Srgrimes } 7311556Srgrimes break; 7321556Srgrimes } 7331556Srgrimes 7341556Srgrimes /* 7351556Srgrimes * empty file name, skips this file. a "." leaves it alone 7361556Srgrimes */ 7371556Srgrimes if (tmpname[0] == '\0') { 7381556Srgrimes tty_prnt("Skipping file.\n"); 7391556Srgrimes return(1); 7401556Srgrimes } 7411556Srgrimes if ((tmpname[0] == '.') && (tmpname[1] == '\0')) { 7421556Srgrimes tty_prnt("Processing continues, name unchanged.\n"); 7431556Srgrimes return(0); 7441556Srgrimes } 7451556Srgrimes 7461556Srgrimes /* 7471556Srgrimes * ok the name changed. We may run into links that point at this 7481556Srgrimes * file later. we have to remember where the user sent the file 7491556Srgrimes * in order to repair any links. 7501556Srgrimes */ 7511556Srgrimes tty_prnt("Processing continues, name changed to: %s\n", tmpname); 7521556Srgrimes res = add_name(arcn->name, arcn->nlen, tmpname); 75376351Skris arcn->nlen = l_strncpy(arcn->name, tmpname, sizeof(arcn->name) - 1); 75476351Skris arcn->name[arcn->nlen] = '\0'; 7551556Srgrimes if (res < 0) 7561556Srgrimes return(-1); 7571556Srgrimes return(0); 7581556Srgrimes} 7591556Srgrimes 7601556Srgrimes/* 7611556Srgrimes * set_dest() 7621556Srgrimes * fix up the file name and the link name (if any) so this file will land 7631556Srgrimes * in the destination directory (used during copy() -rw). 7641556Srgrimes * Return: 7651556Srgrimes * 0 if ok, -1 if failure (name too long) 7661556Srgrimes */ 7671556Srgrimes 7681556Srgrimesint 76990113Simpset_dest(ARCHD *arcn, char *dest_dir, int dir_len) 7701556Srgrimes{ 7711556Srgrimes if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0) 7721556Srgrimes return(-1); 7731556Srgrimes 7741556Srgrimes /* 7751556Srgrimes * It is really hard to deal with symlinks here, we cannot be sure 7761556Srgrimes * if the name they point was moved (or will be moved). It is best to 7771556Srgrimes * leave them alone. 7781556Srgrimes */ 7791556Srgrimes if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG)) 7801556Srgrimes return(0); 7811556Srgrimes 7821556Srgrimes if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0) 7831556Srgrimes return(-1); 7841556Srgrimes return(0); 7851556Srgrimes} 7861556Srgrimes 7871556Srgrimes/* 7881556Srgrimes * fix_path 7891556Srgrimes * concatenate dir_name and or_name and store the result in or_name (if 7901556Srgrimes * it fits). This is one ugly function. 7911556Srgrimes * Return: 7921556Srgrimes * 0 if ok, -1 if the final name is too long 7931556Srgrimes */ 7941556Srgrimes 7951556Srgrimesstatic int 7961556Srgrimesfix_path( char *or_name, int *or_len, char *dir_name, int dir_len) 7971556Srgrimes{ 79890113Simp char *src; 79990113Simp char *dest; 80090113Simp char *start; 8011556Srgrimes int len; 8021556Srgrimes 8031556Srgrimes /* 8041556Srgrimes * we shift the or_name to the right enough to tack in the dir_name 8051556Srgrimes * at the front. We make sure we have enough space for it all before 8061556Srgrimes * we start. since dest always ends in a slash, we skip of or_name 8071556Srgrimes * if it also starts with one. 8081556Srgrimes */ 8091556Srgrimes start = or_name; 8101556Srgrimes src = start + *or_len; 8111556Srgrimes dest = src + dir_len; 8121556Srgrimes if (*start == '/') { 8131556Srgrimes ++start; 8141556Srgrimes --dest; 8151556Srgrimes } 8161556Srgrimes if ((len = dest - or_name) > PAXPATHLEN) { 81776017Skris paxwarn(1, "File name %s/%s, too long", dir_name, start); 8181556Srgrimes return(-1); 8191556Srgrimes } 8201556Srgrimes *or_len = len; 8211556Srgrimes 8221556Srgrimes /* 8238855Srgrimes * enough space, shift 8241556Srgrimes */ 8251556Srgrimes while (src >= start) 8261556Srgrimes *dest-- = *src--; 8271556Srgrimes src = dir_name + dir_len - 1; 8281556Srgrimes 8291556Srgrimes /* 8301556Srgrimes * splice in the destination directory name 8311556Srgrimes */ 8321556Srgrimes while (src >= dir_name) 8331556Srgrimes *dest-- = *src--; 8341556Srgrimes 8351556Srgrimes *(or_name + len) = '\0'; 8361556Srgrimes return(0); 8371556Srgrimes} 8381556Srgrimes 8391556Srgrimes/* 8401556Srgrimes * rep_name() 8411556Srgrimes * walk down the list of replacement strings applying each one in order. 8421556Srgrimes * when we find one with a successful substitution, we modify the name 8431556Srgrimes * as specified. if required, we print the results. if the resulting name 8441556Srgrimes * is empty, we will skip this archive member. We use the regexp(3) 8451556Srgrimes * routines (regexp() ought to win a prize as having the most cryptic 8461556Srgrimes * library function manual page). 8471556Srgrimes * --Parameters-- 8481556Srgrimes * name is the file name we are going to apply the regular expressions to 8491556Srgrimes * (and may be modified) 8501556Srgrimes * nlen is the length of this name (and is modified to hold the length of 8511556Srgrimes * the final string). 8521556Srgrimes * prnt is a flag that says whether to print the final result. 8531556Srgrimes * Return: 8541556Srgrimes * 0 if substitution was successful, 1 if we are to skip the file (the name 8551556Srgrimes * ended up empty) 8561556Srgrimes */ 8571556Srgrimes 8581556Srgrimesstatic int 8591556Srgrimesrep_name(char *name, int *nlen, int prnt) 8601556Srgrimes{ 86190113Simp REPLACE *pt; 86290113Simp char *inpt; 86390113Simp char *outpt; 86490113Simp char *endpt; 86590113Simp char *rpt; 86690113Simp int found = 0; 86790113Simp int res; 8681556Srgrimes# ifndef NET2_REGEX 8691556Srgrimes regmatch_t pm[MAXSUBEXP]; 8701556Srgrimes# endif 8711556Srgrimes char nname[PAXPATHLEN+1]; /* final result of all replacements */ 8721556Srgrimes char buf1[PAXPATHLEN+1]; /* where we work on the name */ 8731556Srgrimes 8741556Srgrimes /* 8751556Srgrimes * copy the name into buf1, where we will work on it. We need to keep 8761556Srgrimes * the orig string around so we can print out the result of the final 8771556Srgrimes * replacement. We build up the final result in nname. inpt points at 8781556Srgrimes * the string we apply the regular expression to. prnt is used to 8791556Srgrimes * suppress printing when we handle replacements on the link field 8801556Srgrimes * (the user already saw that substitution go by) 8811556Srgrimes */ 8821556Srgrimes pt = rephead; 8831556Srgrimes (void)strcpy(buf1, name); 8841556Srgrimes inpt = buf1; 8851556Srgrimes outpt = nname; 8861556Srgrimes endpt = outpt + PAXPATHLEN; 8871556Srgrimes 8881556Srgrimes /* 8891556Srgrimes * try each replacement string in order 8901556Srgrimes */ 8911556Srgrimes while (pt != NULL) { 8921556Srgrimes do { 8931556Srgrimes /* 8941556Srgrimes * check for a successful substitution, if not go to 8951556Srgrimes * the next pattern, or cleanup if we were global 8961556Srgrimes */ 8971556Srgrimes# ifdef NET2_REGEX 8981556Srgrimes if (regexec(pt->rcmp, inpt) == 0) 8991556Srgrimes# else 9001556Srgrimes if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0) 9011556Srgrimes# endif 9021556Srgrimes break; 9031556Srgrimes 9041556Srgrimes /* 9051556Srgrimes * ok we found one. We have three parts, the prefix 9061556Srgrimes * which did not match, the section that did and the 9071556Srgrimes * tail (that also did not match). Copy the prefix to 9081556Srgrimes * the final output buffer (watching to make sure we 9091556Srgrimes * do not create a string too long). 9101556Srgrimes */ 9111556Srgrimes found = 1; 9121556Srgrimes# ifdef NET2_REGEX 9131556Srgrimes rpt = pt->rcmp->startp[0]; 9141556Srgrimes# else 9151556Srgrimes rpt = inpt + pm[0].rm_so; 9161556Srgrimes# endif 9171556Srgrimes 9181556Srgrimes while ((inpt < rpt) && (outpt < endpt)) 9191556Srgrimes *outpt++ = *inpt++; 9201556Srgrimes if (outpt == endpt) 9211556Srgrimes break; 9221556Srgrimes 9231556Srgrimes /* 9241556Srgrimes * for the second part (which matched the regular 9251556Srgrimes * expression) apply the substitution using the 9261556Srgrimes * replacement string and place it the prefix in the 9271556Srgrimes * final output. If we have problems, skip it. 9281556Srgrimes */ 9291556Srgrimes# ifdef NET2_REGEX 9301556Srgrimes if ((res = resub(pt->rcmp,pt->nstr,outpt,endpt)) < 0) { 9311556Srgrimes# else 932208484Suqs if ((res = resub(&(pt->rcmp),pm,inpt,pt->nstr,outpt,endpt)) 9331556Srgrimes < 0) { 9341556Srgrimes# endif 9351556Srgrimes if (prnt) 93676017Skris paxwarn(1, "Replacement name error %s", 9371556Srgrimes name); 9381556Srgrimes return(1); 9391556Srgrimes } 9401556Srgrimes outpt += res; 9411556Srgrimes 9421556Srgrimes /* 9431556Srgrimes * we set up to look again starting at the first 9441556Srgrimes * character in the tail (of the input string right 9451556Srgrimes * after the last character matched by the regular 9461556Srgrimes * expression (inpt always points at the first char in 9471556Srgrimes * the string to process). If we are not doing a global 9481556Srgrimes * substitution, we will use inpt to copy the tail to 9491556Srgrimes * the final result. Make sure we do not overrun the 9501556Srgrimes * output buffer 9511556Srgrimes */ 9521556Srgrimes# ifdef NET2_REGEX 9531556Srgrimes inpt = pt->rcmp->endp[0]; 9541556Srgrimes# else 95541539Sobrien inpt += pm[0].rm_eo - pm[0].rm_so; 9561556Srgrimes# endif 9571556Srgrimes 9581556Srgrimes if ((outpt == endpt) || (*inpt == '\0')) 9591556Srgrimes break; 9601556Srgrimes 9611556Srgrimes /* 9621556Srgrimes * if the user wants global we keep trying to 9631556Srgrimes * substitute until it fails, then we are done. 9641556Srgrimes */ 9651556Srgrimes } while (pt->flgs & GLOB); 9661556Srgrimes 9678855Srgrimes if (found) 9681556Srgrimes break; 9691556Srgrimes 9701556Srgrimes /* 9711556Srgrimes * a successful substitution did NOT occur, try the next one 9721556Srgrimes */ 9731556Srgrimes pt = pt->fow; 9741556Srgrimes } 9751556Srgrimes 9761556Srgrimes if (found) { 9771556Srgrimes /* 9781556Srgrimes * we had a substitution, copy the last tail piece (if there is 9791556Srgrimes * room) to the final result 9801556Srgrimes */ 9811556Srgrimes while ((outpt < endpt) && (*inpt != '\0')) 9821556Srgrimes *outpt++ = *inpt++; 9831556Srgrimes 9841556Srgrimes *outpt = '\0'; 9851556Srgrimes if ((outpt == endpt) && (*inpt != '\0')) { 9861556Srgrimes if (prnt) 98776017Skris paxwarn(1,"Replacement name too long %s >> %s", 9881556Srgrimes name, nname); 9891556Srgrimes return(1); 9908855Srgrimes } 9911556Srgrimes 9921556Srgrimes /* 9931556Srgrimes * inform the user of the result if wanted 9941556Srgrimes */ 9951556Srgrimes if (prnt && (pt->flgs & PRNT)) { 9961556Srgrimes if (*nname == '\0') 9971556Srgrimes (void)fprintf(stderr,"%s >> <empty string>\n", 9981556Srgrimes name); 9998855Srgrimes else 10001556Srgrimes (void)fprintf(stderr,"%s >> %s\n", name, nname); 10011556Srgrimes } 10021556Srgrimes 10031556Srgrimes /* 10041556Srgrimes * if empty inform the caller this file is to be skipped 10051556Srgrimes * otherwise copy the new name over the orig name and return 10061556Srgrimes */ 10078855Srgrimes if (*nname == '\0') 10081556Srgrimes return(1); 10091556Srgrimes *nlen = l_strncpy(name, nname, PAXPATHLEN + 1); 101026363Scharnier name[PAXPATHLEN] = '\0'; 10111556Srgrimes } 10121556Srgrimes return(0); 10131556Srgrimes} 10141556Srgrimes 10151556Srgrimes#ifdef NET2_REGEX 10161556Srgrimes/* 10171556Srgrimes * resub() 10181556Srgrimes * apply the replacement to the matched expression. expand out the old 10191556Srgrimes * style ed(1) subexpression expansion. 10201556Srgrimes * Return: 10211556Srgrimes * -1 if error, or the number of characters added to the destination. 10221556Srgrimes */ 10231556Srgrimes 10241556Srgrimesstatic int 102590113Simpresub(regexp *prog, char *src, char *dest, char *destend) 10261556Srgrimes{ 102790113Simp char *spt; 102890113Simp char *dpt; 102990113Simp char c; 103090113Simp int no; 103190113Simp int len; 10321556Srgrimes 10331556Srgrimes spt = src; 10341556Srgrimes dpt = dest; 10351556Srgrimes while ((dpt < destend) && ((c = *spt++) != '\0')) { 10361556Srgrimes if (c == '&') 10371556Srgrimes no = 0; 10381556Srgrimes else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) 10391556Srgrimes no = *spt++ - '0'; 10401556Srgrimes else { 10411556Srgrimes if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) 10421556Srgrimes c = *spt++; 10431556Srgrimes *dpt++ = c; 10441556Srgrimes continue; 10451556Srgrimes } 10461556Srgrimes if ((prog->startp[no] == NULL) || (prog->endp[no] == NULL) || 10471556Srgrimes ((len = prog->endp[no] - prog->startp[no]) <= 0)) 10481556Srgrimes continue; 10491556Srgrimes 10501556Srgrimes /* 10511556Srgrimes * copy the subexpression to the destination. 10521556Srgrimes * fail if we run out of space or the match string is damaged 10531556Srgrimes */ 10541556Srgrimes if (len > (destend - dpt)) 10551556Srgrimes len = destend - dpt; 10561556Srgrimes if (l_strncpy(dpt, prog->startp[no], len) != len) 10571556Srgrimes return(-1); 10581556Srgrimes dpt += len; 10591556Srgrimes } 10601556Srgrimes return(dpt - dest); 10611556Srgrimes} 10621556Srgrimes 10631556Srgrimes#else 10641556Srgrimes 10651556Srgrimes/* 10661556Srgrimes * resub() 10671556Srgrimes * apply the replacement to the matched expression. expand out the old 10681556Srgrimes * style ed(1) subexpression expansion. 10691556Srgrimes * Return: 10701556Srgrimes * -1 if error, or the number of characters added to the destination. 10711556Srgrimes */ 10721556Srgrimes 10731556Srgrimesstatic int 1074208484Suqsresub(regex_t *rp, regmatch_t *pm, char *orig, char *src, char *dest, 107590113Simp char *destend) 10761556Srgrimes{ 107790113Simp char *spt; 107890113Simp char *dpt; 107990113Simp char c; 108090113Simp regmatch_t *pmpt; 108190113Simp int len; 10821556Srgrimes int subexcnt; 10831556Srgrimes 10841556Srgrimes spt = src; 10851556Srgrimes dpt = dest; 10861556Srgrimes subexcnt = rp->re_nsub; 10871556Srgrimes while ((dpt < destend) && ((c = *spt++) != '\0')) { 10881556Srgrimes /* 10891556Srgrimes * see if we just have an ordinary replacement character 10901556Srgrimes * or we refer to a subexpression. 10911556Srgrimes */ 10921556Srgrimes if (c == '&') { 10931556Srgrimes pmpt = pm; 10941556Srgrimes } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) { 10951556Srgrimes /* 10961556Srgrimes * make sure there is a subexpression as specified 10971556Srgrimes */ 10981556Srgrimes if ((len = *spt++ - '0') > subexcnt) 10991556Srgrimes return(-1); 11001556Srgrimes pmpt = pm + len; 11011556Srgrimes } else { 11021556Srgrimes /* 11031556Srgrimes * Ordinary character, just copy it 11041556Srgrimes */ 11051556Srgrimes if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) 11061556Srgrimes c = *spt++; 11071556Srgrimes *dpt++ = c; 11081556Srgrimes continue; 11091556Srgrimes } 11101556Srgrimes 11111556Srgrimes /* 11121556Srgrimes * continue if the subexpression is bogus 11131556Srgrimes */ 11141556Srgrimes if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) || 11151556Srgrimes ((len = pmpt->rm_eo - pmpt->rm_so) <= 0)) 11161556Srgrimes continue; 11171556Srgrimes 11181556Srgrimes /* 11191556Srgrimes * copy the subexpression to the destination. 11201556Srgrimes * fail if we run out of space or the match string is damaged 11211556Srgrimes */ 11221556Srgrimes if (len > (destend - dpt)) 11231556Srgrimes len = destend - dpt; 1124208484Suqs if (l_strncpy(dpt, orig + pmpt->rm_so, len) != len) 11251556Srgrimes return(-1); 11261556Srgrimes dpt += len; 11271556Srgrimes } 11281556Srgrimes return(dpt - dest); 11291556Srgrimes} 11301556Srgrimes#endif 1131