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 <stdlib.h> 471556Srgrimes#ifdef NET2_REGEX 481556Srgrimes#include <regexp.h> 491556Srgrimes#else 501556Srgrimes#include <regex.h> 511556Srgrimes#endif 521556Srgrimes#include "pax.h" 531556Srgrimes#include "pat_rep.h" 541556Srgrimes#include "extern.h" 551556Srgrimes 561556Srgrimes/* 571556Srgrimes * routines to handle pattern matching, name modification (regular expression 581556Srgrimes * substitution and interactive renames), and destination name modification for 591556Srgrimes * copy (-rw). Both file name and link names are adjusted as required in these 601556Srgrimes * routines. 611556Srgrimes */ 621556Srgrimes 631556Srgrimes#define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */ 641556Srgrimesstatic PATTERN *pathead = NULL; /* file pattern match list head */ 651556Srgrimesstatic PATTERN *pattail = NULL; /* file pattern match list tail */ 661556Srgrimesstatic REPLACE *rephead = NULL; /* replacement string list head */ 671556Srgrimesstatic REPLACE *reptail = NULL; /* replacement string list tail */ 681556Srgrimes 6990110Simpstatic int rep_name(char *, int *, int); 7090113Simpstatic int tty_rename(ARCHD *); 7190110Simpstatic int fix_path(char *, int *, char *, int); 7290113Simpstatic int fn_match(char *, char *, char **); 7390113Simpstatic char * range_match(char *, int); 741556Srgrimes#ifdef NET2_REGEX 7590113Simpstatic int resub(regexp *, char *, char *, char *); 761556Srgrimes#else 77208484Suqsstatic int resub(regex_t *, regmatch_t *, char *, char *, char *, char *); 781556Srgrimes#endif 791556Srgrimes 801556Srgrimes/* 811556Srgrimes * rep_add() 821556Srgrimes * parses the -s replacement string; compiles the regular expression 831556Srgrimes * and stores the compiled value and it's replacement string together in 841556Srgrimes * replacement string list. Input to this function is of the form: 858855Srgrimes * /old/new/pg 861556Srgrimes * The first char in the string specifies the delimiter used by this 871556Srgrimes * replacement string. "Old" is a regular expression in "ed" format which 881556Srgrimes * is compiled by regcomp() and is applied to filenames. "new" is the 891556Srgrimes * substitution string; p and g are options flags for printing and global 901556Srgrimes * replacement (over the single filename) 911556Srgrimes * Return: 921556Srgrimes * 0 if a proper replacement string and regular expression was added to 931556Srgrimes * the list of replacement patterns; -1 otherwise. 941556Srgrimes */ 951556Srgrimes 961556Srgrimesint 9790113Simprep_add(char *str) 981556Srgrimes{ 9990113Simp char *pt1; 10090113Simp char *pt2; 10190113Simp REPLACE *rep; 1021556Srgrimes# ifndef NET2_REGEX 10390113Simp int res; 1041556Srgrimes char rebuf[BUFSIZ]; 1051556Srgrimes# endif 1061556Srgrimes 1071556Srgrimes /* 1081556Srgrimes * throw out the bad parameters 1091556Srgrimes */ 1101556Srgrimes if ((str == NULL) || (*str == '\0')) { 11176017Skris paxwarn(1, "Empty replacement string"); 1121556Srgrimes return(-1); 1131556Srgrimes } 1141556Srgrimes 1151556Srgrimes /* 1161556Srgrimes * first character in the string specifies what the delimiter is for 1171556Srgrimes * this expression 1181556Srgrimes */ 1191556Srgrimes if ((pt1 = strchr(str+1, *str)) == NULL) { 12076017Skris paxwarn(1, "Invalid replacement string %s", str); 1211556Srgrimes return(-1); 1221556Srgrimes } 1231556Srgrimes 1241556Srgrimes /* 1251556Srgrimes * allocate space for the node that handles this replacement pattern 1261556Srgrimes * and split out the regular expression and try to compile it 1271556Srgrimes */ 1281556Srgrimes if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) { 12976017Skris paxwarn(1, "Unable to allocate memory for replacement string"); 1301556Srgrimes return(-1); 1311556Srgrimes } 1321556Srgrimes 1331556Srgrimes *pt1 = '\0'; 1341556Srgrimes# ifdef NET2_REGEX 1351556Srgrimes if ((rep->rcmp = regcomp(str+1)) == NULL) { 1361556Srgrimes# else 1371556Srgrimes if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { 1381556Srgrimes regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); 13976017Skris paxwarn(1, "%s while compiling regular expression %s", rebuf, str); 1401556Srgrimes# endif 141169993Sbrian free(rep); 1421556Srgrimes return(-1); 1431556Srgrimes } 1441556Srgrimes 1451556Srgrimes /* 1461556Srgrimes * put the delimiter back in case we need an error message and 1471556Srgrimes * locate the delimiter at the end of the replacement string 1481556Srgrimes * we then point the node at the new substitution string 1491556Srgrimes */ 1501556Srgrimes *pt1++ = *str; 1511556Srgrimes if ((pt2 = strchr(pt1, *str)) == NULL) { 1521556Srgrimes# ifdef NET2_REGEX 153169993Sbrian free(rep->rcmp); 1541556Srgrimes# else 155169993Sbrian regfree(&rep->rcmp); 1561556Srgrimes# endif 157169993Sbrian free(rep); 15876017Skris paxwarn(1, "Invalid replacement string %s", str); 1591556Srgrimes return(-1); 1601556Srgrimes } 1611556Srgrimes 1621556Srgrimes *pt2 = '\0'; 1631556Srgrimes rep->nstr = pt1; 1641556Srgrimes pt1 = pt2++; 1651556Srgrimes rep->flgs = 0; 1661556Srgrimes 1671556Srgrimes /* 1681556Srgrimes * set the options if any 1691556Srgrimes */ 1701556Srgrimes while (*pt2 != '\0') { 1711556Srgrimes switch(*pt2) { 1721556Srgrimes case 'g': 1731556Srgrimes case 'G': 1741556Srgrimes rep->flgs |= GLOB; 1751556Srgrimes break; 1761556Srgrimes case 'p': 1771556Srgrimes case 'P': 1781556Srgrimes rep->flgs |= PRNT; 1791556Srgrimes break; 1801556Srgrimes default: 1811556Srgrimes# ifdef NET2_REGEX 182169993Sbrian free(rep->rcmp); 1831556Srgrimes# else 184169993Sbrian regfree(&rep->rcmp); 1851556Srgrimes# endif 186169993Sbrian free(rep); 1871556Srgrimes *pt1 = *str; 18876017Skris paxwarn(1, "Invalid replacement string option %s", str); 1891556Srgrimes return(-1); 1901556Srgrimes } 1911556Srgrimes ++pt2; 1921556Srgrimes } 1931556Srgrimes 1941556Srgrimes /* 1951556Srgrimes * all done, link it in at the end 1961556Srgrimes */ 1971556Srgrimes rep->fow = NULL; 1981556Srgrimes if (rephead == NULL) { 1991556Srgrimes reptail = rephead = rep; 2001556Srgrimes return(0); 2011556Srgrimes } 2021556Srgrimes reptail->fow = rep; 2031556Srgrimes reptail = rep; 2041556Srgrimes return(0); 2051556Srgrimes} 2061556Srgrimes 2071556Srgrimes/* 2081556Srgrimes * pat_add() 2091556Srgrimes * add a pattern match to the pattern match list. Pattern matches are used 2101556Srgrimes * to select which archive members are extracted. (They appear as 2111556Srgrimes * arguments to pax in the list and read modes). If no patterns are 2121556Srgrimes * supplied to pax, all members in the archive will be selected (and the 2131556Srgrimes * pattern match list is empty). 2141556Srgrimes * Return: 2151556Srgrimes * 0 if the pattern was added to the list, -1 otherwise 2161556Srgrimes */ 2171556Srgrimes 2181556Srgrimesint 219114583Smarkmpat_add(char *str, char *chdnam) 2201556Srgrimes{ 22190113Simp PATTERN *pt; 2221556Srgrimes 2231556Srgrimes /* 2241556Srgrimes * throw out the junk 2251556Srgrimes */ 2261556Srgrimes if ((str == NULL) || (*str == '\0')) { 22776017Skris paxwarn(1, "Empty pattern string"); 2281556Srgrimes return(-1); 2291556Srgrimes } 2301556Srgrimes 2311556Srgrimes /* 2321556Srgrimes * allocate space for the pattern and store the pattern. the pattern is 2331556Srgrimes * part of argv so do not bother to copy it, just point at it. Add the 2341556Srgrimes * node to the end of the pattern list 2351556Srgrimes */ 2361556Srgrimes if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { 23776017Skris paxwarn(1, "Unable to allocate memory for pattern string"); 2381556Srgrimes return(-1); 2391556Srgrimes } 2401556Srgrimes 2411556Srgrimes pt->pstr = str; 2421556Srgrimes pt->pend = NULL; 2431556Srgrimes pt->plen = strlen(str); 2441556Srgrimes pt->fow = NULL; 2451556Srgrimes pt->flgs = 0; 246114583Smarkm pt->chdname = chdnam; 24776351Skris 2481556Srgrimes if (pathead == NULL) { 2491556Srgrimes pattail = pathead = pt; 2501556Srgrimes return(0); 2511556Srgrimes } 2521556Srgrimes pattail->fow = pt; 2531556Srgrimes pattail = pt; 2541556Srgrimes return(0); 2551556Srgrimes} 2561556Srgrimes 2571556Srgrimes/* 2581556Srgrimes * pat_chk() 2591556Srgrimes * complain if any the user supplied pattern did not result in a match to 2601556Srgrimes * a selected archive member. 2611556Srgrimes */ 2621556Srgrimes 2631556Srgrimesvoid 2641556Srgrimespat_chk(void) 2651556Srgrimes{ 26690113Simp PATTERN *pt; 26790113Simp int wban = 0; 2681556Srgrimes 2691556Srgrimes /* 2701556Srgrimes * walk down the list checking the flags to make sure MTCH was set, 2711556Srgrimes * if not complain 2721556Srgrimes */ 2731556Srgrimes for (pt = pathead; pt != NULL; pt = pt->fow) { 2741556Srgrimes if (pt->flgs & MTCH) 2751556Srgrimes continue; 2761556Srgrimes if (!wban) { 27776017Skris paxwarn(1, "WARNING! These patterns were not matched:"); 2781556Srgrimes ++wban; 2791556Srgrimes } 2801556Srgrimes (void)fprintf(stderr, "%s\n", pt->pstr); 2811556Srgrimes } 2821556Srgrimes} 2831556Srgrimes 2841556Srgrimes/* 2851556Srgrimes * pat_sel() 2861556Srgrimes * the archive member which matches a pattern was selected. Mark the 2871556Srgrimes * pattern as having selected an archive member. arcn->pat points at the 2881556Srgrimes * pattern that was matched. arcn->pat is set in pat_match() 2891556Srgrimes * 2901556Srgrimes * NOTE: When the -c option is used, we are called when there was no match 2911556Srgrimes * by pat_match() (that means we did match before the inverted sense of 2921556Srgrimes * the logic). Now this seems really strange at first, but with -c we 293108533Sschweikh * need to keep track of those patterns that cause an archive member to NOT 2941556Srgrimes * be selected (it found an archive member with a specified pattern) 2951556Srgrimes * Return: 2961556Srgrimes * 0 if the pattern pointed at by arcn->pat was tagged as creating a 2971556Srgrimes * match, -1 otherwise. 2981556Srgrimes */ 2991556Srgrimes 3001556Srgrimesint 30190113Simppat_sel(ARCHD *arcn) 3021556Srgrimes{ 30390113Simp PATTERN *pt; 30490113Simp PATTERN **ppt; 30590113Simp int len; 3061556Srgrimes 3071556Srgrimes /* 3081556Srgrimes * if no patterns just return 3091556Srgrimes */ 3101556Srgrimes if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) 3111556Srgrimes return(0); 3121556Srgrimes 3131556Srgrimes /* 3141556Srgrimes * when we are NOT limited to a single match per pattern mark the 3151556Srgrimes * pattern and return 3161556Srgrimes */ 3171556Srgrimes if (!nflag) { 3181556Srgrimes pt->flgs |= MTCH; 3191556Srgrimes return(0); 3201556Srgrimes } 3211556Srgrimes 3221556Srgrimes /* 3231556Srgrimes * we reach this point only when we allow a single selected match per 3248855Srgrimes * pattern, if the pattern matches a directory and we do not have -d 3251556Srgrimes * (dflag) we are done with this pattern. We may also be handed a file 3261556Srgrimes * in the subtree of a directory. in that case when we are operating 3271556Srgrimes * with -d, this pattern was already selected and we are done 3281556Srgrimes */ 3291556Srgrimes if (pt->flgs & DIR_MTCH) 3301556Srgrimes return(0); 3311556Srgrimes 3321556Srgrimes if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { 3331556Srgrimes /* 3341556Srgrimes * ok we matched a directory and we are allowing 3351556Srgrimes * subtree matches but because of the -n only its children will 3361556Srgrimes * match. This is tagged as a DIR_MTCH type. 3371556Srgrimes * WATCH IT, the code assumes that pt->pend points 3381556Srgrimes * into arcn->name and arcn->name has not been modified. 3391556Srgrimes * If not we will have a big mess. Yup this is another kludge 3401556Srgrimes */ 3411556Srgrimes 3421556Srgrimes /* 3431556Srgrimes * if this was a prefix match, remove trailing part of path 3441556Srgrimes * so we can copy it. Future matches will be exact prefix match 3451556Srgrimes */ 3461556Srgrimes if (pt->pend != NULL) 3471556Srgrimes *pt->pend = '\0'; 3488855Srgrimes 3491556Srgrimes if ((pt->pstr = strdup(arcn->name)) == NULL) { 35076017Skris paxwarn(1, "Pattern select out of memory"); 3511556Srgrimes if (pt->pend != NULL) 3521556Srgrimes *pt->pend = '/'; 3531556Srgrimes pt->pend = NULL; 3541556Srgrimes return(-1); 3551556Srgrimes } 3561556Srgrimes 3571556Srgrimes /* 3581556Srgrimes * put the trailing / back in the source string 3591556Srgrimes */ 3601556Srgrimes if (pt->pend != NULL) { 3611556Srgrimes *pt->pend = '/'; 3621556Srgrimes pt->pend = NULL; 3631556Srgrimes } 3641556Srgrimes pt->plen = strlen(pt->pstr); 3651556Srgrimes 3661556Srgrimes /* 3671556Srgrimes * strip off any trailing /, this should really never happen 3681556Srgrimes */ 3691556Srgrimes len = pt->plen - 1; 3701556Srgrimes if (*(pt->pstr + len) == '/') { 3711556Srgrimes *(pt->pstr + len) = '\0'; 3721556Srgrimes pt->plen = len; 3738855Srgrimes } 3741556Srgrimes pt->flgs = DIR_MTCH | MTCH; 3751556Srgrimes arcn->pat = pt; 3761556Srgrimes return(0); 3771556Srgrimes } 3781556Srgrimes 3791556Srgrimes /* 3801556Srgrimes * we are then done with this pattern, so we delete it from the list 3811556Srgrimes * because it can never be used for another match. 3821556Srgrimes * Seems kind of strange to do for a -c, but the pax spec is really 3831556Srgrimes * vague on the interaction of -c -n and -d. We assume that when -c 3841556Srgrimes * and the pattern rejects a member (i.e. it matched it) it is done. 3851556Srgrimes * In effect we place the order of the flags as having -c last. 3861556Srgrimes */ 3871556Srgrimes pt = pathead; 3881556Srgrimes ppt = &pathead; 3891556Srgrimes while ((pt != NULL) && (pt != arcn->pat)) { 3901556Srgrimes ppt = &(pt->fow); 3911556Srgrimes pt = pt->fow; 3921556Srgrimes } 3931556Srgrimes 3941556Srgrimes if (pt == NULL) { 3951556Srgrimes /* 3961556Srgrimes * should never happen.... 3971556Srgrimes */ 398222177Suqs paxwarn(1, "Pattern list inconsistent"); 3991556Srgrimes return(-1); 4001556Srgrimes } 4011556Srgrimes *ppt = pt->fow; 402169993Sbrian free(pt); 4031556Srgrimes arcn->pat = NULL; 4041556Srgrimes return(0); 4051556Srgrimes} 4061556Srgrimes 4071556Srgrimes/* 4081556Srgrimes * pat_match() 4091556Srgrimes * see if this archive member matches any supplied pattern, if a match 4101556Srgrimes * is found, arcn->pat is set to point at the potential pattern. Later if 4111556Srgrimes * this archive member is "selected" we process and mark the pattern as 4121556Srgrimes * one which matched a selected archive member (see pat_sel()) 4131556Srgrimes * Return: 4148855Srgrimes * 0 if this archive member should be processed, 1 if it should be 4151556Srgrimes * skipped and -1 if we are done with all patterns (and pax should quit 4161556Srgrimes * looking for more members) 4171556Srgrimes */ 4181556Srgrimes 4191556Srgrimesint 42090113Simppat_match(ARCHD *arcn) 4211556Srgrimes{ 42290113Simp PATTERN *pt; 4231556Srgrimes 4241556Srgrimes arcn->pat = NULL; 4251556Srgrimes 4261556Srgrimes /* 4271556Srgrimes * if there are no more patterns and we have -n (and not -c) we are 4281556Srgrimes * done. otherwise with no patterns to match, matches all 4291556Srgrimes */ 4301556Srgrimes if (pathead == NULL) { 4311556Srgrimes if (nflag && !cflag) 4321556Srgrimes return(-1); 4331556Srgrimes return(0); 4341556Srgrimes } 4351556Srgrimes 4361556Srgrimes /* 4371556Srgrimes * have to search down the list one at a time looking for a match. 4381556Srgrimes */ 4391556Srgrimes pt = pathead; 4401556Srgrimes while (pt != NULL) { 4411556Srgrimes /* 4421556Srgrimes * check for a file name match unless we have DIR_MTCH set in 4431556Srgrimes * this pattern then we want a prefix match 4441556Srgrimes */ 4451556Srgrimes if (pt->flgs & DIR_MTCH) { 4461556Srgrimes /* 4471556Srgrimes * this pattern was matched before to a directory 4481556Srgrimes * as we must have -n set for this (but not -d). We can 4491556Srgrimes * only match CHILDREN of that directory so we must use 4501556Srgrimes * an exact prefix match (no wildcards). 4511556Srgrimes */ 4521556Srgrimes if ((arcn->name[pt->plen] == '/') && 4531556Srgrimes (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) 4541556Srgrimes break; 4551556Srgrimes } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) 4561556Srgrimes break; 4571556Srgrimes pt = pt->fow; 4581556Srgrimes } 4591556Srgrimes 4601556Srgrimes /* 4611556Srgrimes * return the result, remember that cflag (-c) inverts the sense of a 4621556Srgrimes * match 4631556Srgrimes */ 4641556Srgrimes if (pt == NULL) 4651556Srgrimes return(cflag ? 0 : 1); 4661556Srgrimes 4671556Srgrimes /* 468108533Sschweikh * We had a match, now when we invert the sense (-c) we reject this 4691556Srgrimes * member. However we have to tag the pattern a being successful, (in a 470108533Sschweikh * match, not in selecting an archive member) so we call pat_sel() here. 4711556Srgrimes */ 4721556Srgrimes arcn->pat = pt; 4731556Srgrimes if (!cflag) 4741556Srgrimes return(0); 4751556Srgrimes 4761556Srgrimes if (pat_sel(arcn) < 0) 4771556Srgrimes return(-1); 4781556Srgrimes arcn->pat = NULL; 4791556Srgrimes return(1); 4801556Srgrimes} 4811556Srgrimes 4821556Srgrimes/* 4831556Srgrimes * fn_match() 4841556Srgrimes * Return: 4858855Srgrimes * 0 if this archive member should be processed, 1 if it should be 4861556Srgrimes * skipped and -1 if we are done with all patterns (and pax should quit 4871556Srgrimes * looking for more members) 4881556Srgrimes * Note: *pend may be changed to show where the prefix ends. 4891556Srgrimes */ 4901556Srgrimes 4911556Srgrimesstatic int 49290113Simpfn_match(char *pattern, char *string, char **pend) 4931556Srgrimes{ 49490113Simp char c; 4951556Srgrimes char test; 4961556Srgrimes 4971556Srgrimes *pend = NULL; 4981556Srgrimes for (;;) { 4991556Srgrimes switch (c = *pattern++) { 5001556Srgrimes case '\0': 5011556Srgrimes /* 5021556Srgrimes * Ok we found an exact match 5031556Srgrimes */ 5041556Srgrimes if (*string == '\0') 5051556Srgrimes return(0); 5061556Srgrimes 5071556Srgrimes /* 5081556Srgrimes * Check if it is a prefix match 5091556Srgrimes */ 5101556Srgrimes if ((dflag == 1) || (*string != '/')) 5111556Srgrimes return(-1); 5121556Srgrimes 5131556Srgrimes /* 5141556Srgrimes * It is a prefix match, remember where the trailing 5151556Srgrimes * / is located 5161556Srgrimes */ 5171556Srgrimes *pend = string; 5181556Srgrimes return(0); 5191556Srgrimes case '?': 5201556Srgrimes if ((test = *string++) == '\0') 5211556Srgrimes return (-1); 5221556Srgrimes break; 5231556Srgrimes case '*': 5241556Srgrimes c = *pattern; 5251556Srgrimes /* 5268855Srgrimes * Collapse multiple *'s. 5271556Srgrimes */ 5281556Srgrimes while (c == '*') 5291556Srgrimes c = *++pattern; 5301556Srgrimes 5311556Srgrimes /* 5321556Srgrimes * Optimized hack for pattern with a * at the end 5331556Srgrimes */ 5341556Srgrimes if (c == '\0') 5351556Srgrimes return (0); 5361556Srgrimes 5371556Srgrimes /* 5381556Srgrimes * General case, use recursion. 5391556Srgrimes */ 5401556Srgrimes while ((test = *string) != '\0') { 5411556Srgrimes if (!fn_match(pattern, string, pend)) 5421556Srgrimes return (0); 5431556Srgrimes ++string; 5441556Srgrimes } 5451556Srgrimes return (-1); 5461556Srgrimes case '[': 5471556Srgrimes /* 5481556Srgrimes * range match 5491556Srgrimes */ 5501556Srgrimes if (((test = *string++) == '\0') || 5511556Srgrimes ((pattern = range_match(pattern, test)) == NULL)) 5521556Srgrimes return (-1); 5531556Srgrimes break; 5541556Srgrimes case '\\': 5551556Srgrimes default: 5561556Srgrimes if (c != *string++) 5571556Srgrimes return (-1); 5581556Srgrimes break; 5591556Srgrimes } 5601556Srgrimes } 5611556Srgrimes /* NOTREACHED */ 5621556Srgrimes} 5631556Srgrimes 5641556Srgrimesstatic char * 56590113Simprange_match(char *pattern, int test) 5661556Srgrimes{ 56790113Simp char c; 56890113Simp char c2; 5691556Srgrimes int negate; 5701556Srgrimes int ok = 0; 5711556Srgrimes 57276351Skris if ((negate = (*pattern == '!')) != 0) 5731556Srgrimes ++pattern; 5741556Srgrimes 5751556Srgrimes while ((c = *pattern++) != ']') { 5761556Srgrimes /* 5771556Srgrimes * Illegal pattern 5781556Srgrimes */ 5791556Srgrimes if (c == '\0') 5801556Srgrimes return (NULL); 5811556Srgrimes 5821556Srgrimes if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') && 5831556Srgrimes (c2 != ']')) { 5841556Srgrimes if ((c <= test) && (test <= c2)) 5851556Srgrimes ok = 1; 5861556Srgrimes pattern += 2; 5871556Srgrimes } else if (c == test) 5881556Srgrimes ok = 1; 5891556Srgrimes } 5901556Srgrimes return (ok == negate ? NULL : pattern); 5911556Srgrimes} 5921556Srgrimes 5931556Srgrimes/* 5941556Srgrimes * mod_name() 5951556Srgrimes * modify a selected file name. first attempt to apply replacement string 5961556Srgrimes * expressions, then apply interactive file rename. We apply replacement 5971556Srgrimes * string expressions to both filenames and file links (if we didn't the 5981556Srgrimes * links would point to the wrong place, and we could never be able to 5991556Srgrimes * move an archive that has a file link in it). When we rename files 6001556Srgrimes * interactively, we store that mapping (old name to user input name) so 6011556Srgrimes * if we spot any file links to the old file name in the future, we will 6021556Srgrimes * know exactly how to fix the file link. 6031556Srgrimes * Return: 6048855Srgrimes * 0 continue to process file, 1 skip this file, -1 pax is finished 6051556Srgrimes */ 6061556Srgrimes 6071556Srgrimesint 60890113Simpmod_name(ARCHD *arcn) 6091556Srgrimes{ 61090113Simp int res = 0; 6111556Srgrimes 6121556Srgrimes /* 61376351Skris * Strip off leading '/' if appropriate. 61476351Skris * Currently, this option is only set for the tar format. 61576351Skris */ 61676351Skris if (rmleadslash && arcn->name[0] == '/') { 61776351Skris if (arcn->name[1] == '\0') { 61876351Skris arcn->name[0] = '.'; 61976351Skris } else { 62076351Skris (void)memmove(arcn->name, &arcn->name[1], 62176351Skris strlen(arcn->name)); 62276351Skris arcn->nlen--; 62376351Skris } 62476351Skris if (rmleadslash < 2) { 62576351Skris rmleadslash = 2; 62676351Skris paxwarn(0, "Removing leading / from absolute path names in the archive"); 62776351Skris } 62876351Skris } 62976351Skris if (rmleadslash && arcn->ln_name[0] == '/' && 63076351Skris (arcn->type == PAX_HLK || arcn->type == PAX_HRG)) { 63176351Skris if (arcn->ln_name[1] == '\0') { 63276351Skris arcn->ln_name[0] = '.'; 63376351Skris } else { 63476351Skris (void)memmove(arcn->ln_name, &arcn->ln_name[1], 63576351Skris strlen(arcn->ln_name)); 63676351Skris arcn->ln_nlen--; 63776351Skris } 63876351Skris if (rmleadslash < 2) { 63976351Skris rmleadslash = 2; 64076351Skris paxwarn(0, "Removing leading / from absolute path names in the archive"); 64176351Skris } 64276351Skris } 64376351Skris 64476351Skris /* 6451556Srgrimes * IMPORTANT: We have a problem. what do we do with symlinks? 6461556Srgrimes * Modifying a hard link name makes sense, as we know the file it 6471556Srgrimes * points at should have been seen already in the archive (and if it 6481556Srgrimes * wasn't seen because of a read error or a bad archive, we lose 6491556Srgrimes * anyway). But there are no such requirements for symlinks. On one 6501556Srgrimes * hand the symlink that refers to a file in the archive will have to 6511556Srgrimes * be modified to so it will still work at its new location in the 652102230Strhodes * file system. On the other hand a symlink that points elsewhere (and 6531556Srgrimes * should continue to do so) should not be modified. There is clearly 6541556Srgrimes * no perfect solution here. So we handle them like hardlinks. Clearly 6551556Srgrimes * a replacement made by the interactive rename mapping is very likely 6561556Srgrimes * to be correct since it applies to a single file and is an exact 6571556Srgrimes * match. The regular expression replacements are a little harder to 6581556Srgrimes * justify though. We claim that the symlink name is only likely 6591556Srgrimes * to be replaced when it points within the file tree being moved and 6601556Srgrimes * in that case it should be modified. what we really need to do is to 6611556Srgrimes * call an oracle here. :) 6621556Srgrimes */ 6631556Srgrimes if (rephead != NULL) { 6641556Srgrimes /* 6651556Srgrimes * we have replacement strings, modify the name and the link 6661556Srgrimes * name if any. 6671556Srgrimes */ 6681556Srgrimes if ((res = rep_name(arcn->name, &(arcn->nlen), 1)) != 0) 6691556Srgrimes return(res); 6701556Srgrimes 6711556Srgrimes if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || 6721556Srgrimes (arcn->type == PAX_HRG)) && 6731556Srgrimes ((res = rep_name(arcn->ln_name, &(arcn->ln_nlen), 0)) != 0)) 6741556Srgrimes return(res); 6751556Srgrimes } 6761556Srgrimes 6771556Srgrimes if (iflag) { 6781556Srgrimes /* 6791556Srgrimes * perform interactive file rename, then map the link if any 6801556Srgrimes */ 6811556Srgrimes if ((res = tty_rename(arcn)) != 0) 6821556Srgrimes return(res); 6831556Srgrimes if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || 6841556Srgrimes (arcn->type == PAX_HRG)) 68576351Skris sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name)); 6861556Srgrimes } 6871556Srgrimes return(res); 6881556Srgrimes} 6891556Srgrimes 6901556Srgrimes/* 6911556Srgrimes * tty_rename() 6921556Srgrimes * Prompt the user for a replacement file name. A "." keeps the old name, 6931556Srgrimes * a empty line skips the file, and an EOF on reading the tty, will cause 6941556Srgrimes * pax to stop processing and exit. Otherwise the file name input, replaces 6951556Srgrimes * the old one. 6961556Srgrimes * Return: 6971556Srgrimes * 0 process this file, 1 skip this file, -1 we need to exit pax 6981556Srgrimes */ 6991556Srgrimes 7001556Srgrimesstatic int 70190113Simptty_rename(ARCHD *arcn) 7021556Srgrimes{ 7031556Srgrimes char tmpname[PAXPATHLEN+2]; 7041556Srgrimes int res; 7051556Srgrimes 7061556Srgrimes /* 7071556Srgrimes * prompt user for the replacement name for a file, keep trying until 7081556Srgrimes * we get some reasonable input. Archives may have more than one file 7091556Srgrimes * on them with the same name (from updates etc). We print verbose info 7101556Srgrimes * on the file so the user knows what is up. 7111556Srgrimes */ 7121556Srgrimes tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0); 7131556Srgrimes 7141556Srgrimes for (;;) { 7151556Srgrimes ls_tty(arcn); 7161556Srgrimes tty_prnt("Input new name, or a \".\" to keep the old name, "); 7171556Srgrimes tty_prnt("or a \"return\" to skip this file.\n"); 7181556Srgrimes tty_prnt("Input > "); 7191556Srgrimes if (tty_read(tmpname, sizeof(tmpname)) < 0) 7201556Srgrimes return(-1); 7211556Srgrimes if (strcmp(tmpname, "..") == 0) { 7221556Srgrimes tty_prnt("Try again, illegal file name: ..\n"); 7231556Srgrimes continue; 7241556Srgrimes } 7251556Srgrimes if (strlen(tmpname) > PAXPATHLEN) { 7261556Srgrimes tty_prnt("Try again, file name too long\n"); 7271556Srgrimes continue; 7281556Srgrimes } 7291556Srgrimes break; 7301556Srgrimes } 7311556Srgrimes 7321556Srgrimes /* 7331556Srgrimes * empty file name, skips this file. a "." leaves it alone 7341556Srgrimes */ 7351556Srgrimes if (tmpname[0] == '\0') { 7361556Srgrimes tty_prnt("Skipping file.\n"); 7371556Srgrimes return(1); 7381556Srgrimes } 7391556Srgrimes if ((tmpname[0] == '.') && (tmpname[1] == '\0')) { 7401556Srgrimes tty_prnt("Processing continues, name unchanged.\n"); 7411556Srgrimes return(0); 7421556Srgrimes } 7431556Srgrimes 7441556Srgrimes /* 7451556Srgrimes * ok the name changed. We may run into links that point at this 7461556Srgrimes * file later. we have to remember where the user sent the file 7471556Srgrimes * in order to repair any links. 7481556Srgrimes */ 7491556Srgrimes tty_prnt("Processing continues, name changed to: %s\n", tmpname); 7501556Srgrimes res = add_name(arcn->name, arcn->nlen, tmpname); 75176351Skris arcn->nlen = l_strncpy(arcn->name, tmpname, sizeof(arcn->name) - 1); 75276351Skris arcn->name[arcn->nlen] = '\0'; 7531556Srgrimes if (res < 0) 7541556Srgrimes return(-1); 7551556Srgrimes return(0); 7561556Srgrimes} 7571556Srgrimes 7581556Srgrimes/* 7591556Srgrimes * set_dest() 7601556Srgrimes * fix up the file name and the link name (if any) so this file will land 7611556Srgrimes * in the destination directory (used during copy() -rw). 7621556Srgrimes * Return: 7631556Srgrimes * 0 if ok, -1 if failure (name too long) 7641556Srgrimes */ 7651556Srgrimes 7661556Srgrimesint 76790113Simpset_dest(ARCHD *arcn, char *dest_dir, int dir_len) 7681556Srgrimes{ 7691556Srgrimes if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0) 7701556Srgrimes return(-1); 7711556Srgrimes 7721556Srgrimes /* 7731556Srgrimes * It is really hard to deal with symlinks here, we cannot be sure 7741556Srgrimes * if the name they point was moved (or will be moved). It is best to 7751556Srgrimes * leave them alone. 7761556Srgrimes */ 7771556Srgrimes if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG)) 7781556Srgrimes return(0); 7791556Srgrimes 7801556Srgrimes if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0) 7811556Srgrimes return(-1); 7821556Srgrimes return(0); 7831556Srgrimes} 7841556Srgrimes 7851556Srgrimes/* 7861556Srgrimes * fix_path 7871556Srgrimes * concatenate dir_name and or_name and store the result in or_name (if 7881556Srgrimes * it fits). This is one ugly function. 7891556Srgrimes * Return: 7901556Srgrimes * 0 if ok, -1 if the final name is too long 7911556Srgrimes */ 7921556Srgrimes 7931556Srgrimesstatic int 7941556Srgrimesfix_path( char *or_name, int *or_len, char *dir_name, int dir_len) 7951556Srgrimes{ 79690113Simp char *src; 79790113Simp char *dest; 79890113Simp char *start; 7991556Srgrimes int len; 8001556Srgrimes 8011556Srgrimes /* 8021556Srgrimes * we shift the or_name to the right enough to tack in the dir_name 8031556Srgrimes * at the front. We make sure we have enough space for it all before 8041556Srgrimes * we start. since dest always ends in a slash, we skip of or_name 8051556Srgrimes * if it also starts with one. 8061556Srgrimes */ 8071556Srgrimes start = or_name; 8081556Srgrimes src = start + *or_len; 8091556Srgrimes dest = src + dir_len; 8101556Srgrimes if (*start == '/') { 8111556Srgrimes ++start; 8121556Srgrimes --dest; 8131556Srgrimes } 8141556Srgrimes if ((len = dest - or_name) > PAXPATHLEN) { 81576017Skris paxwarn(1, "File name %s/%s, too long", dir_name, start); 8161556Srgrimes return(-1); 8171556Srgrimes } 8181556Srgrimes *or_len = len; 8191556Srgrimes 8201556Srgrimes /* 8218855Srgrimes * enough space, shift 8221556Srgrimes */ 8231556Srgrimes while (src >= start) 8241556Srgrimes *dest-- = *src--; 8251556Srgrimes src = dir_name + dir_len - 1; 8261556Srgrimes 8271556Srgrimes /* 8281556Srgrimes * splice in the destination directory name 8291556Srgrimes */ 8301556Srgrimes while (src >= dir_name) 8311556Srgrimes *dest-- = *src--; 8321556Srgrimes 8331556Srgrimes *(or_name + len) = '\0'; 8341556Srgrimes return(0); 8351556Srgrimes} 8361556Srgrimes 8371556Srgrimes/* 8381556Srgrimes * rep_name() 8391556Srgrimes * walk down the list of replacement strings applying each one in order. 8401556Srgrimes * when we find one with a successful substitution, we modify the name 8411556Srgrimes * as specified. if required, we print the results. if the resulting name 8421556Srgrimes * is empty, we will skip this archive member. We use the regexp(3) 8431556Srgrimes * routines (regexp() ought to win a prize as having the most cryptic 8441556Srgrimes * library function manual page). 8451556Srgrimes * --Parameters-- 8461556Srgrimes * name is the file name we are going to apply the regular expressions to 8471556Srgrimes * (and may be modified) 8481556Srgrimes * nlen is the length of this name (and is modified to hold the length of 8491556Srgrimes * the final string). 8501556Srgrimes * prnt is a flag that says whether to print the final result. 8511556Srgrimes * Return: 8521556Srgrimes * 0 if substitution was successful, 1 if we are to skip the file (the name 8531556Srgrimes * ended up empty) 8541556Srgrimes */ 8551556Srgrimes 8561556Srgrimesstatic int 8571556Srgrimesrep_name(char *name, int *nlen, int prnt) 8581556Srgrimes{ 85990113Simp REPLACE *pt; 86090113Simp char *inpt; 86190113Simp char *outpt; 86290113Simp char *endpt; 86390113Simp char *rpt; 86490113Simp int found = 0; 86590113Simp int res; 8661556Srgrimes# ifndef NET2_REGEX 8671556Srgrimes regmatch_t pm[MAXSUBEXP]; 8681556Srgrimes# endif 8691556Srgrimes char nname[PAXPATHLEN+1]; /* final result of all replacements */ 8701556Srgrimes char buf1[PAXPATHLEN+1]; /* where we work on the name */ 8711556Srgrimes 8721556Srgrimes /* 8731556Srgrimes * copy the name into buf1, where we will work on it. We need to keep 8741556Srgrimes * the orig string around so we can print out the result of the final 8751556Srgrimes * replacement. We build up the final result in nname. inpt points at 8761556Srgrimes * the string we apply the regular expression to. prnt is used to 8771556Srgrimes * suppress printing when we handle replacements on the link field 8781556Srgrimes * (the user already saw that substitution go by) 8791556Srgrimes */ 8801556Srgrimes pt = rephead; 881293238Spfg (void)strlcpy(buf1, name, sizeof(buf1)); 8821556Srgrimes inpt = buf1; 8831556Srgrimes outpt = nname; 8841556Srgrimes endpt = outpt + PAXPATHLEN; 8851556Srgrimes 8861556Srgrimes /* 8871556Srgrimes * try each replacement string in order 8881556Srgrimes */ 8891556Srgrimes while (pt != NULL) { 8901556Srgrimes do { 8911556Srgrimes /* 8921556Srgrimes * check for a successful substitution, if not go to 8931556Srgrimes * the next pattern, or cleanup if we were global 8941556Srgrimes */ 8951556Srgrimes# ifdef NET2_REGEX 8961556Srgrimes if (regexec(pt->rcmp, inpt) == 0) 8971556Srgrimes# else 8981556Srgrimes if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0) 8991556Srgrimes# endif 9001556Srgrimes break; 9011556Srgrimes 9021556Srgrimes /* 9031556Srgrimes * ok we found one. We have three parts, the prefix 9041556Srgrimes * which did not match, the section that did and the 9051556Srgrimes * tail (that also did not match). Copy the prefix to 9061556Srgrimes * the final output buffer (watching to make sure we 9071556Srgrimes * do not create a string too long). 9081556Srgrimes */ 9091556Srgrimes found = 1; 9101556Srgrimes# ifdef NET2_REGEX 9111556Srgrimes rpt = pt->rcmp->startp[0]; 9121556Srgrimes# else 9131556Srgrimes rpt = inpt + pm[0].rm_so; 9141556Srgrimes# endif 9151556Srgrimes 9161556Srgrimes while ((inpt < rpt) && (outpt < endpt)) 9171556Srgrimes *outpt++ = *inpt++; 9181556Srgrimes if (outpt == endpt) 9191556Srgrimes break; 9201556Srgrimes 9211556Srgrimes /* 9221556Srgrimes * for the second part (which matched the regular 9231556Srgrimes * expression) apply the substitution using the 9241556Srgrimes * replacement string and place it the prefix in the 9251556Srgrimes * final output. If we have problems, skip it. 9261556Srgrimes */ 9271556Srgrimes# ifdef NET2_REGEX 9281556Srgrimes if ((res = resub(pt->rcmp,pt->nstr,outpt,endpt)) < 0) { 9291556Srgrimes# else 930208484Suqs if ((res = resub(&(pt->rcmp),pm,inpt,pt->nstr,outpt,endpt)) 9311556Srgrimes < 0) { 9321556Srgrimes# endif 9331556Srgrimes if (prnt) 93476017Skris paxwarn(1, "Replacement name error %s", 9351556Srgrimes name); 9361556Srgrimes return(1); 9371556Srgrimes } 9381556Srgrimes outpt += res; 9391556Srgrimes 9401556Srgrimes /* 9411556Srgrimes * we set up to look again starting at the first 9421556Srgrimes * character in the tail (of the input string right 9431556Srgrimes * after the last character matched by the regular 9441556Srgrimes * expression (inpt always points at the first char in 9451556Srgrimes * the string to process). If we are not doing a global 9461556Srgrimes * substitution, we will use inpt to copy the tail to 9471556Srgrimes * the final result. Make sure we do not overrun the 9481556Srgrimes * output buffer 9491556Srgrimes */ 9501556Srgrimes# ifdef NET2_REGEX 9511556Srgrimes inpt = pt->rcmp->endp[0]; 9521556Srgrimes# else 95341539Sobrien inpt += pm[0].rm_eo - pm[0].rm_so; 9541556Srgrimes# endif 9551556Srgrimes 9561556Srgrimes if ((outpt == endpt) || (*inpt == '\0')) 9571556Srgrimes break; 9581556Srgrimes 9591556Srgrimes /* 9601556Srgrimes * if the user wants global we keep trying to 9611556Srgrimes * substitute until it fails, then we are done. 9621556Srgrimes */ 9631556Srgrimes } while (pt->flgs & GLOB); 9641556Srgrimes 9658855Srgrimes if (found) 9661556Srgrimes break; 9671556Srgrimes 9681556Srgrimes /* 9691556Srgrimes * a successful substitution did NOT occur, try the next one 9701556Srgrimes */ 9711556Srgrimes pt = pt->fow; 9721556Srgrimes } 9731556Srgrimes 9741556Srgrimes if (found) { 9751556Srgrimes /* 9761556Srgrimes * we had a substitution, copy the last tail piece (if there is 9771556Srgrimes * room) to the final result 9781556Srgrimes */ 9791556Srgrimes while ((outpt < endpt) && (*inpt != '\0')) 9801556Srgrimes *outpt++ = *inpt++; 9811556Srgrimes 9821556Srgrimes *outpt = '\0'; 9831556Srgrimes if ((outpt == endpt) && (*inpt != '\0')) { 9841556Srgrimes if (prnt) 98576017Skris paxwarn(1,"Replacement name too long %s >> %s", 9861556Srgrimes name, nname); 9871556Srgrimes return(1); 9888855Srgrimes } 9891556Srgrimes 9901556Srgrimes /* 9911556Srgrimes * inform the user of the result if wanted 9921556Srgrimes */ 9931556Srgrimes if (prnt && (pt->flgs & PRNT)) { 9941556Srgrimes if (*nname == '\0') 9951556Srgrimes (void)fprintf(stderr,"%s >> <empty string>\n", 9961556Srgrimes name); 9978855Srgrimes else 9981556Srgrimes (void)fprintf(stderr,"%s >> %s\n", name, nname); 9991556Srgrimes } 10001556Srgrimes 10011556Srgrimes /* 10021556Srgrimes * if empty inform the caller this file is to be skipped 10031556Srgrimes * otherwise copy the new name over the orig name and return 10041556Srgrimes */ 10058855Srgrimes if (*nname == '\0') 10061556Srgrimes return(1); 10071556Srgrimes *nlen = l_strncpy(name, nname, PAXPATHLEN + 1); 100826363Scharnier name[PAXPATHLEN] = '\0'; 10091556Srgrimes } 10101556Srgrimes return(0); 10111556Srgrimes} 10121556Srgrimes 10131556Srgrimes#ifdef NET2_REGEX 10141556Srgrimes/* 10151556Srgrimes * resub() 10161556Srgrimes * apply the replacement to the matched expression. expand out the old 10171556Srgrimes * style ed(1) subexpression expansion. 10181556Srgrimes * Return: 10191556Srgrimes * -1 if error, or the number of characters added to the destination. 10201556Srgrimes */ 10211556Srgrimes 10221556Srgrimesstatic int 102390113Simpresub(regexp *prog, char *src, char *dest, char *destend) 10241556Srgrimes{ 102590113Simp char *spt; 102690113Simp char *dpt; 102790113Simp char c; 102890113Simp int no; 102990113Simp int len; 10301556Srgrimes 10311556Srgrimes spt = src; 10321556Srgrimes dpt = dest; 10331556Srgrimes while ((dpt < destend) && ((c = *spt++) != '\0')) { 10341556Srgrimes if (c == '&') 10351556Srgrimes no = 0; 10361556Srgrimes else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) 10371556Srgrimes no = *spt++ - '0'; 10381556Srgrimes else { 10391556Srgrimes if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) 10401556Srgrimes c = *spt++; 10411556Srgrimes *dpt++ = c; 10421556Srgrimes continue; 10431556Srgrimes } 10441556Srgrimes if ((prog->startp[no] == NULL) || (prog->endp[no] == NULL) || 10451556Srgrimes ((len = prog->endp[no] - prog->startp[no]) <= 0)) 10461556Srgrimes continue; 10471556Srgrimes 10481556Srgrimes /* 10491556Srgrimes * copy the subexpression to the destination. 10501556Srgrimes * fail if we run out of space or the match string is damaged 10511556Srgrimes */ 10521556Srgrimes if (len > (destend - dpt)) 10531556Srgrimes len = destend - dpt; 10541556Srgrimes if (l_strncpy(dpt, prog->startp[no], len) != len) 10551556Srgrimes return(-1); 10561556Srgrimes dpt += len; 10571556Srgrimes } 10581556Srgrimes return(dpt - dest); 10591556Srgrimes} 10601556Srgrimes 10611556Srgrimes#else 10621556Srgrimes 10631556Srgrimes/* 10641556Srgrimes * resub() 10651556Srgrimes * apply the replacement to the matched expression. expand out the old 10661556Srgrimes * style ed(1) subexpression expansion. 10671556Srgrimes * Return: 10681556Srgrimes * -1 if error, or the number of characters added to the destination. 10691556Srgrimes */ 10701556Srgrimes 10711556Srgrimesstatic int 1072208484Suqsresub(regex_t *rp, regmatch_t *pm, char *orig, char *src, char *dest, 107390113Simp char *destend) 10741556Srgrimes{ 107590113Simp char *spt; 107690113Simp char *dpt; 107790113Simp char c; 107890113Simp regmatch_t *pmpt; 107990113Simp int len; 10801556Srgrimes int subexcnt; 10811556Srgrimes 10821556Srgrimes spt = src; 10831556Srgrimes dpt = dest; 10841556Srgrimes subexcnt = rp->re_nsub; 10851556Srgrimes while ((dpt < destend) && ((c = *spt++) != '\0')) { 10861556Srgrimes /* 10871556Srgrimes * see if we just have an ordinary replacement character 10881556Srgrimes * or we refer to a subexpression. 10891556Srgrimes */ 10901556Srgrimes if (c == '&') { 10911556Srgrimes pmpt = pm; 10921556Srgrimes } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) { 10931556Srgrimes /* 10941556Srgrimes * make sure there is a subexpression as specified 10951556Srgrimes */ 10961556Srgrimes if ((len = *spt++ - '0') > subexcnt) 10971556Srgrimes return(-1); 10981556Srgrimes pmpt = pm + len; 10991556Srgrimes } else { 11001556Srgrimes /* 11011556Srgrimes * Ordinary character, just copy it 11021556Srgrimes */ 11031556Srgrimes if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) 11041556Srgrimes c = *spt++; 11051556Srgrimes *dpt++ = c; 11061556Srgrimes continue; 11071556Srgrimes } 11081556Srgrimes 11091556Srgrimes /* 11101556Srgrimes * continue if the subexpression is bogus 11111556Srgrimes */ 11121556Srgrimes if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) || 11131556Srgrimes ((len = pmpt->rm_eo - pmpt->rm_so) <= 0)) 11141556Srgrimes continue; 11151556Srgrimes 11161556Srgrimes /* 11171556Srgrimes * copy the subexpression to the destination. 11181556Srgrimes * fail if we run out of space or the match string is damaged 11191556Srgrimes */ 11201556Srgrimes if (len > (destend - dpt)) 11211556Srgrimes len = destend - dpt; 1122208484Suqs if (l_strncpy(dpt, orig + pmpt->rm_so, len) != len) 11231556Srgrimes return(-1); 11241556Srgrimes dpt += len; 11251556Srgrimes } 11261556Srgrimes return(dpt - dest); 11271556Srgrimes} 11281556Srgrimes#endif 1129