/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1992 Keith Muller.4* Copyright (c) 1992, 19935* The Regents of the University of California. All rights reserved.6*7* This code is derived from software contributed to Berkeley by8* Keith Muller of the University of California, San Diego.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18* 3. Neither the name of the University nor the names of its contributors19* may be used to endorse or promote products derived from this software20* without specific prior written permission.21*22* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND23* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE24* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE25* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE26* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL27* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS28* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)29* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT30* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY31* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF32* SUCH DAMAGE.33*/3435#include <sys/types.h>36#include <sys/stat.h>37#include <stdio.h>38#include <string.h>39#include <stdlib.h>40#include <regex.h>41#include "pax.h"42#include "pat_rep.h"43#include "extern.h"4445/*46* routines to handle pattern matching, name modification (regular expression47* substitution and interactive renames), and destination name modification for48* copy (-rw). Both file name and link names are adjusted as required in these49* routines.50*/5152#define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */53static PATTERN *pathead = NULL; /* file pattern match list head */54static PATTERN *pattail = NULL; /* file pattern match list tail */55static REPLACE *rephead = NULL; /* replacement string list head */56static REPLACE *reptail = NULL; /* replacement string list tail */5758static int rep_name(char *, int *, int);59static int tty_rename(ARCHD *);60static int fix_path(char *, int *, char *, int);61static int fn_match(char *, char *, char **);62static char * range_match(char *, int);63static int resub(regex_t *, regmatch_t *, char *, char *, char *, char *);6465/*66* rep_add()67* parses the -s replacement string; compiles the regular expression68* and stores the compiled value and it's replacement string together in69* replacement string list. Input to this function is of the form:70* /old/new/pg71* The first char in the string specifies the delimiter used by this72* replacement string. "Old" is a regular expression in "ed" format which73* is compiled by regcomp() and is applied to filenames. "new" is the74* substitution string; p and g are options flags for printing and global75* replacement (over the single filename)76* Return:77* 0 if a proper replacement string and regular expression was added to78* the list of replacement patterns; -1 otherwise.79*/8081int82rep_add(char *str)83{84char *pt1;85char *pt2;86REPLACE *rep;87int res;88char rebuf[BUFSIZ];8990/*91* throw out the bad parameters92*/93if ((str == NULL) || (*str == '\0')) {94paxwarn(1, "Empty replacement string");95return(-1);96}9798/*99* first character in the string specifies what the delimiter is for100* this expression101*/102if ((pt1 = strchr(str+1, *str)) == NULL) {103paxwarn(1, "Invalid replacement string %s", str);104return(-1);105}106107/*108* allocate space for the node that handles this replacement pattern109* and split out the regular expression and try to compile it110*/111if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) {112paxwarn(1, "Unable to allocate memory for replacement string");113return(-1);114}115116*pt1 = '\0';117if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) {118regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf));119paxwarn(1, "%s while compiling regular expression %s", rebuf, str);120free(rep);121return(-1);122}123124/*125* put the delimiter back in case we need an error message and126* locate the delimiter at the end of the replacement string127* we then point the node at the new substitution string128*/129*pt1++ = *str;130if ((pt2 = strchr(pt1, *str)) == NULL) {131regfree(&rep->rcmp);132free(rep);133paxwarn(1, "Invalid replacement string %s", str);134return(-1);135}136137*pt2 = '\0';138rep->nstr = pt1;139pt1 = pt2++;140rep->flgs = 0;141142/*143* set the options if any144*/145while (*pt2 != '\0') {146switch(*pt2) {147case 'g':148case 'G':149rep->flgs |= GLOB;150break;151case 'p':152case 'P':153rep->flgs |= PRNT;154break;155default:156regfree(&rep->rcmp);157free(rep);158*pt1 = *str;159paxwarn(1, "Invalid replacement string option %s", str);160return(-1);161}162++pt2;163}164165/*166* all done, link it in at the end167*/168rep->fow = NULL;169if (rephead == NULL) {170reptail = rephead = rep;171return(0);172}173reptail->fow = rep;174reptail = rep;175return(0);176}177178/*179* pat_add()180* add a pattern match to the pattern match list. Pattern matches are used181* to select which archive members are extracted. (They appear as182* arguments to pax in the list and read modes). If no patterns are183* supplied to pax, all members in the archive will be selected (and the184* pattern match list is empty).185* Return:186* 0 if the pattern was added to the list, -1 otherwise187*/188189int190pat_add(char *str, char *chdnam)191{192PATTERN *pt;193194/*195* throw out the junk196*/197if ((str == NULL) || (*str == '\0')) {198paxwarn(1, "Empty pattern string");199return(-1);200}201202/*203* allocate space for the pattern and store the pattern. the pattern is204* part of argv so do not bother to copy it, just point at it. Add the205* node to the end of the pattern list206*/207if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) {208paxwarn(1, "Unable to allocate memory for pattern string");209return(-1);210}211212pt->pstr = str;213pt->pend = NULL;214pt->plen = strlen(str);215pt->fow = NULL;216pt->flgs = 0;217pt->chdname = chdnam;218219if (pathead == NULL) {220pattail = pathead = pt;221return(0);222}223pattail->fow = pt;224pattail = pt;225return(0);226}227228/*229* pat_chk()230* complain if any the user supplied pattern did not result in a match to231* a selected archive member.232*/233234void235pat_chk(void)236{237PATTERN *pt;238int wban = 0;239240/*241* walk down the list checking the flags to make sure MTCH was set,242* if not complain243*/244for (pt = pathead; pt != NULL; pt = pt->fow) {245if (pt->flgs & MTCH)246continue;247if (!wban) {248paxwarn(1, "WARNING! These patterns were not matched:");249++wban;250}251(void)fprintf(stderr, "%s\n", pt->pstr);252}253}254255/*256* pat_sel()257* the archive member which matches a pattern was selected. Mark the258* pattern as having selected an archive member. arcn->pat points at the259* pattern that was matched. arcn->pat is set in pat_match()260*261* NOTE: When the -c option is used, we are called when there was no match262* by pat_match() (that means we did match before the inverted sense of263* the logic). Now this seems really strange at first, but with -c we264* need to keep track of those patterns that cause an archive member to NOT265* be selected (it found an archive member with a specified pattern)266* Return:267* 0 if the pattern pointed at by arcn->pat was tagged as creating a268* match, -1 otherwise.269*/270271int272pat_sel(ARCHD *arcn)273{274PATTERN *pt;275PATTERN **ppt;276int len;277278/*279* if no patterns just return280*/281if ((pathead == NULL) || ((pt = arcn->pat) == NULL))282return(0);283284/*285* when we are NOT limited to a single match per pattern mark the286* pattern and return287*/288if (!nflag) {289pt->flgs |= MTCH;290return(0);291}292293/*294* we reach this point only when we allow a single selected match per295* pattern, if the pattern matches a directory and we do not have -d296* (dflag) we are done with this pattern. We may also be handed a file297* in the subtree of a directory. in that case when we are operating298* with -d, this pattern was already selected and we are done299*/300if (pt->flgs & DIR_MTCH)301return(0);302303if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) {304/*305* ok we matched a directory and we are allowing306* subtree matches but because of the -n only its children will307* match. This is tagged as a DIR_MTCH type.308* WATCH IT, the code assumes that pt->pend points309* into arcn->name and arcn->name has not been modified.310* If not we will have a big mess. Yup this is another kludge311*/312313/*314* if this was a prefix match, remove trailing part of path315* so we can copy it. Future matches will be exact prefix match316*/317if (pt->pend != NULL)318*pt->pend = '\0';319320if ((pt->pstr = strdup(arcn->name)) == NULL) {321paxwarn(1, "Pattern select out of memory");322if (pt->pend != NULL)323*pt->pend = '/';324pt->pend = NULL;325return(-1);326}327328/*329* put the trailing / back in the source string330*/331if (pt->pend != NULL) {332*pt->pend = '/';333pt->pend = NULL;334}335pt->plen = strlen(pt->pstr);336337/*338* strip off any trailing /, this should really never happen339*/340len = pt->plen - 1;341if (*(pt->pstr + len) == '/') {342*(pt->pstr + len) = '\0';343pt->plen = len;344}345pt->flgs = DIR_MTCH | MTCH;346arcn->pat = pt;347return(0);348}349350/*351* we are then done with this pattern, so we delete it from the list352* because it can never be used for another match.353* Seems kind of strange to do for a -c, but the pax spec is really354* vague on the interaction of -c -n and -d. We assume that when -c355* and the pattern rejects a member (i.e. it matched it) it is done.356* In effect we place the order of the flags as having -c last.357*/358pt = pathead;359ppt = &pathead;360while ((pt != NULL) && (pt != arcn->pat)) {361ppt = &(pt->fow);362pt = pt->fow;363}364365if (pt == NULL) {366/*367* should never happen....368*/369paxwarn(1, "Pattern list inconsistent");370return(-1);371}372*ppt = pt->fow;373free(pt);374arcn->pat = NULL;375return(0);376}377378/*379* pat_match()380* see if this archive member matches any supplied pattern, if a match381* is found, arcn->pat is set to point at the potential pattern. Later if382* this archive member is "selected" we process and mark the pattern as383* one which matched a selected archive member (see pat_sel())384* Return:385* 0 if this archive member should be processed, 1 if it should be386* skipped and -1 if we are done with all patterns (and pax should quit387* looking for more members)388*/389390int391pat_match(ARCHD *arcn)392{393PATTERN *pt;394395arcn->pat = NULL;396397/*398* if there are no more patterns and we have -n (and not -c) we are399* done. otherwise with no patterns to match, matches all400*/401if (pathead == NULL) {402if (nflag && !cflag)403return(-1);404return(0);405}406407/*408* have to search down the list one at a time looking for a match.409*/410pt = pathead;411while (pt != NULL) {412/*413* check for a file name match unless we have DIR_MTCH set in414* this pattern then we want a prefix match415*/416if (pt->flgs & DIR_MTCH) {417/*418* this pattern was matched before to a directory419* as we must have -n set for this (but not -d). We can420* only match CHILDREN of that directory so we must use421* an exact prefix match (no wildcards).422*/423if ((arcn->name[pt->plen] == '/') &&424(strncmp(pt->pstr, arcn->name, pt->plen) == 0))425break;426} else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0)427break;428pt = pt->fow;429}430431/*432* return the result, remember that cflag (-c) inverts the sense of a433* match434*/435if (pt == NULL)436return(cflag ? 0 : 1);437438/*439* We had a match, now when we invert the sense (-c) we reject this440* member. However we have to tag the pattern a being successful, (in a441* match, not in selecting an archive member) so we call pat_sel() here.442*/443arcn->pat = pt;444if (!cflag)445return(0);446447if (pat_sel(arcn) < 0)448return(-1);449arcn->pat = NULL;450return(1);451}452453/*454* fn_match()455* Return:456* 0 if this archive member should be processed, 1 if it should be457* skipped and -1 if we are done with all patterns (and pax should quit458* looking for more members)459* Note: *pend may be changed to show where the prefix ends.460*/461462static int463fn_match(char *pattern, char *string, char **pend)464{465char c;466char test;467468*pend = NULL;469for (;;) {470switch (c = *pattern++) {471case '\0':472/*473* Ok we found an exact match474*/475if (*string == '\0')476return(0);477478/*479* Check if it is a prefix match480*/481if ((dflag == 1) || (*string != '/'))482return(-1);483484/*485* It is a prefix match, remember where the trailing486* / is located487*/488*pend = string;489return(0);490case '?':491if ((test = *string++) == '\0')492return (-1);493break;494case '*':495c = *pattern;496/*497* Collapse multiple *'s.498*/499while (c == '*')500c = *++pattern;501502/*503* Optimized hack for pattern with a * at the end504*/505if (c == '\0')506return (0);507508/*509* General case, use recursion.510*/511while ((test = *string) != '\0') {512if (!fn_match(pattern, string, pend))513return (0);514++string;515}516return (-1);517case '[':518/*519* range match520*/521if (((test = *string++) == '\0') ||522((pattern = range_match(pattern, test)) == NULL))523return (-1);524break;525case '\\':526default:527if (c != *string++)528return (-1);529break;530}531}532/* NOTREACHED */533}534535static char *536range_match(char *pattern, int test)537{538char c;539char c2;540int negate;541int ok = 0;542543if ((negate = (*pattern == '!')) != 0)544++pattern;545546while ((c = *pattern++) != ']') {547/*548* Illegal pattern549*/550if (c == '\0')551return (NULL);552553if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') &&554(c2 != ']')) {555if ((c <= test) && (test <= c2))556ok = 1;557pattern += 2;558} else if (c == test)559ok = 1;560}561return (ok == negate ? NULL : pattern);562}563564/*565* mod_name()566* modify a selected file name. first attempt to apply replacement string567* expressions, then apply interactive file rename. We apply replacement568* string expressions to both filenames and file links (if we didn't the569* links would point to the wrong place, and we could never be able to570* move an archive that has a file link in it). When we rename files571* interactively, we store that mapping (old name to user input name) so572* if we spot any file links to the old file name in the future, we will573* know exactly how to fix the file link.574* Return:575* 0 continue to process file, 1 skip this file, -1 pax is finished576*/577578int579mod_name(ARCHD *arcn)580{581int res = 0;582583/*584* Strip off leading '/' if appropriate.585* Currently, this option is only set for the tar format.586*/587if (rmleadslash && arcn->name[0] == '/') {588if (arcn->name[1] == '\0') {589arcn->name[0] = '.';590} else {591(void)memmove(arcn->name, &arcn->name[1],592strlen(arcn->name));593arcn->nlen--;594}595if (rmleadslash < 2) {596rmleadslash = 2;597paxwarn(0, "Removing leading / from absolute path names in the archive");598}599}600if (rmleadslash && arcn->ln_name[0] == '/' &&601(arcn->type == PAX_HLK || arcn->type == PAX_HRG)) {602if (arcn->ln_name[1] == '\0') {603arcn->ln_name[0] = '.';604} else {605(void)memmove(arcn->ln_name, &arcn->ln_name[1],606strlen(arcn->ln_name));607arcn->ln_nlen--;608}609if (rmleadslash < 2) {610rmleadslash = 2;611paxwarn(0, "Removing leading / from absolute path names in the archive");612}613}614615/*616* IMPORTANT: We have a problem. what do we do with symlinks?617* Modifying a hard link name makes sense, as we know the file it618* points at should have been seen already in the archive (and if it619* wasn't seen because of a read error or a bad archive, we lose620* anyway). But there are no such requirements for symlinks. On one621* hand the symlink that refers to a file in the archive will have to622* be modified to so it will still work at its new location in the623* file system. On the other hand a symlink that points elsewhere (and624* should continue to do so) should not be modified. There is clearly625* no perfect solution here. So we handle them like hardlinks. Clearly626* a replacement made by the interactive rename mapping is very likely627* to be correct since it applies to a single file and is an exact628* match. The regular expression replacements are a little harder to629* justify though. We claim that the symlink name is only likely630* to be replaced when it points within the file tree being moved and631* in that case it should be modified. what we really need to do is to632* call an oracle here. :)633*/634if (rephead != NULL) {635/*636* we have replacement strings, modify the name and the link637* name if any.638*/639if ((res = rep_name(arcn->name, &(arcn->nlen), 1)) != 0)640return(res);641642if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||643(arcn->type == PAX_HRG)) &&644((res = rep_name(arcn->ln_name, &(arcn->ln_nlen), 0)) != 0))645return(res);646}647648if (iflag) {649/*650* perform interactive file rename, then map the link if any651*/652if ((res = tty_rename(arcn)) != 0)653return(res);654if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||655(arcn->type == PAX_HRG))656sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name));657}658return(res);659}660661/*662* tty_rename()663* Prompt the user for a replacement file name. A "." keeps the old name,664* a empty line skips the file, and an EOF on reading the tty, will cause665* pax to stop processing and exit. Otherwise the file name input, replaces666* the old one.667* Return:668* 0 process this file, 1 skip this file, -1 we need to exit pax669*/670671static int672tty_rename(ARCHD *arcn)673{674char tmpname[PAXPATHLEN+2];675int res;676677/*678* prompt user for the replacement name for a file, keep trying until679* we get some reasonable input. Archives may have more than one file680* on them with the same name (from updates etc). We print verbose info681* on the file so the user knows what is up.682*/683tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0);684685for (;;) {686ls_tty(arcn);687tty_prnt("Input new name, or a \".\" to keep the old name, ");688tty_prnt("or a \"return\" to skip this file.\n");689tty_prnt("Input > ");690if (tty_read(tmpname, sizeof(tmpname)) < 0)691return(-1);692if (strcmp(tmpname, "..") == 0) {693tty_prnt("Try again, illegal file name: ..\n");694continue;695}696if (strlen(tmpname) > PAXPATHLEN) {697tty_prnt("Try again, file name too long\n");698continue;699}700break;701}702703/*704* empty file name, skips this file. a "." leaves it alone705*/706if (tmpname[0] == '\0') {707tty_prnt("Skipping file.\n");708return(1);709}710if ((tmpname[0] == '.') && (tmpname[1] == '\0')) {711tty_prnt("Processing continues, name unchanged.\n");712return(0);713}714715/*716* ok the name changed. We may run into links that point at this717* file later. we have to remember where the user sent the file718* in order to repair any links.719*/720tty_prnt("Processing continues, name changed to: %s\n", tmpname);721res = add_name(arcn->name, arcn->nlen, tmpname);722arcn->nlen = l_strncpy(arcn->name, tmpname, sizeof(arcn->name) - 1);723arcn->name[arcn->nlen] = '\0';724if (res < 0)725return(-1);726return(0);727}728729/*730* set_dest()731* fix up the file name and the link name (if any) so this file will land732* in the destination directory (used during copy() -rw).733* Return:734* 0 if ok, -1 if failure (name too long)735*/736737int738set_dest(ARCHD *arcn, char *dest_dir, int dir_len)739{740if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0)741return(-1);742743/*744* It is really hard to deal with symlinks here, we cannot be sure745* if the name they point was moved (or will be moved). It is best to746* leave them alone.747*/748if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG))749return(0);750751if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0)752return(-1);753return(0);754}755756/*757* fix_path758* concatenate dir_name and or_name and store the result in or_name (if759* it fits). This is one ugly function.760* Return:761* 0 if ok, -1 if the final name is too long762*/763764static int765fix_path( char *or_name, int *or_len, char *dir_name, int dir_len)766{767char *src;768char *dest;769char *start;770int len;771772/*773* we shift the or_name to the right enough to tack in the dir_name774* at the front. We make sure we have enough space for it all before775* we start. since dest always ends in a slash, we skip of or_name776* if it also starts with one.777*/778start = or_name;779src = start + *or_len;780dest = src + dir_len;781if (*start == '/') {782++start;783--dest;784}785if ((len = dest - or_name) > PAXPATHLEN) {786paxwarn(1, "File name %s/%s, too long", dir_name, start);787return(-1);788}789*or_len = len;790791/*792* enough space, shift793*/794while (src >= start)795*dest-- = *src--;796src = dir_name + dir_len - 1;797798/*799* splice in the destination directory name800*/801while (src >= dir_name)802*dest-- = *src--;803804*(or_name + len) = '\0';805return(0);806}807808/*809* rep_name()810* walk down the list of replacement strings applying each one in order.811* when we find one with a successful substitution, we modify the name812* as specified. if required, we print the results. if the resulting name813* is empty, we will skip this archive member. We use the regexp(3)814* routines (regexp() ought to win a prize as having the most cryptic815* library function manual page).816* --Parameters--817* name is the file name we are going to apply the regular expressions to818* (and may be modified)819* nlen is the length of this name (and is modified to hold the length of820* the final string).821* prnt is a flag that says whether to print the final result.822* Return:823* 0 if substitution was successful, 1 if we are to skip the file (the name824* ended up empty)825*/826827static int828rep_name(char *name, int *nlen, int prnt)829{830REPLACE *pt;831char *inpt;832char *outpt;833char *endpt;834char *rpt;835int found = 0;836int res;837regmatch_t pm[MAXSUBEXP];838char nname[PAXPATHLEN+1]; /* final result of all replacements */839char buf1[PAXPATHLEN+1]; /* where we work on the name */840841/*842* copy the name into buf1, where we will work on it. We need to keep843* the orig string around so we can print out the result of the final844* replacement. We build up the final result in nname. inpt points at845* the string we apply the regular expression to. prnt is used to846* suppress printing when we handle replacements on the link field847* (the user already saw that substitution go by)848*/849pt = rephead;850(void)strlcpy(buf1, name, sizeof(buf1));851inpt = buf1;852outpt = nname;853endpt = outpt + PAXPATHLEN;854855/*856* try each replacement string in order857*/858while (pt != NULL) {859do {860/*861* check for a successful substitution, if not go to862* the next pattern, or cleanup if we were global863*/864if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0)865break;866867/*868* ok we found one. We have three parts, the prefix869* which did not match, the section that did and the870* tail (that also did not match). Copy the prefix to871* the final output buffer (watching to make sure we872* do not create a string too long).873*/874found = 1;875rpt = inpt + pm[0].rm_so;876877while ((inpt < rpt) && (outpt < endpt))878*outpt++ = *inpt++;879if (outpt == endpt)880break;881882/*883* for the second part (which matched the regular884* expression) apply the substitution using the885* replacement string and place it the prefix in the886* final output. If we have problems, skip it.887*/888if ((res = resub(&(pt->rcmp),pm,inpt,pt->nstr,outpt,endpt))889< 0) {890if (prnt)891paxwarn(1, "Replacement name error %s",892name);893return(1);894}895outpt += res;896897/*898* we set up to look again starting at the first899* character in the tail (of the input string right900* after the last character matched by the regular901* expression (inpt always points at the first char in902* the string to process). If we are not doing a global903* substitution, we will use inpt to copy the tail to904* the final result. Make sure we do not overrun the905* output buffer906*/907inpt += pm[0].rm_eo - pm[0].rm_so;908909if ((outpt == endpt) || (*inpt == '\0'))910break;911912/*913* if the user wants global we keep trying to914* substitute until it fails, then we are done.915*/916} while (pt->flgs & GLOB);917918if (found)919break;920921/*922* a successful substitution did NOT occur, try the next one923*/924pt = pt->fow;925}926927if (found) {928/*929* we had a substitution, copy the last tail piece (if there is930* room) to the final result931*/932while ((outpt < endpt) && (*inpt != '\0'))933*outpt++ = *inpt++;934935*outpt = '\0';936if ((outpt == endpt) && (*inpt != '\0')) {937if (prnt)938paxwarn(1,"Replacement name too long %s >> %s",939name, nname);940return(1);941}942943/*944* inform the user of the result if wanted945*/946if (prnt && (pt->flgs & PRNT)) {947if (*nname == '\0')948(void)fprintf(stderr,"%s >> <empty string>\n",949name);950else951(void)fprintf(stderr,"%s >> %s\n", name, nname);952}953954/*955* if empty inform the caller this file is to be skipped956* otherwise copy the new name over the orig name and return957*/958if (*nname == '\0')959return(1);960*nlen = l_strncpy(name, nname, PAXPATHLEN + 1);961name[PAXPATHLEN] = '\0';962}963return(0);964}965966967/*968* resub()969* apply the replacement to the matched expression. expand out the old970* style ed(1) subexpression expansion.971* Return:972* -1 if error, or the number of characters added to the destination.973*/974975static int976resub(regex_t *rp, regmatch_t *pm, char *orig, char *src, char *dest,977char *destend)978{979char *spt;980char *dpt;981char c;982regmatch_t *pmpt;983int len;984int subexcnt;985986spt = src;987dpt = dest;988subexcnt = rp->re_nsub;989while ((dpt < destend) && ((c = *spt++) != '\0')) {990/*991* see if we just have an ordinary replacement character992* or we refer to a subexpression.993*/994if (c == '&') {995pmpt = pm;996} else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) {997/*998* make sure there is a subexpression as specified999*/1000if ((len = *spt++ - '0') > subexcnt)1001return(-1);1002pmpt = pm + len;1003} else {1004/*1005* Ordinary character, just copy it1006*/1007if ((c == '\\') && ((*spt == '\\') || (*spt == '&')))1008c = *spt++;1009*dpt++ = c;1010continue;1011}10121013/*1014* continue if the subexpression is bogus1015*/1016if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) ||1017((len = pmpt->rm_eo - pmpt->rm_so) <= 0))1018continue;10191020/*1021* copy the subexpression to the destination.1022* fail if we run out of space or the match string is damaged1023*/1024if (len > (destend - dpt))1025len = destend - dpt;1026if (l_strncpy(dpt, orig + pmpt->rm_so, len) != len)1027return(-1);1028dpt += len;1029}1030return(dpt - dest);1031}103210331034