📄 pat_rep.c
字号:
/*- * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)pat_rep.c 8.2 (Berkeley) 4/18/94";#endif /* not lint */#include <sys/types.h>#include <sys/time.h>#include <sys/stat.h>#include <sys/param.h>#include <stdio.h>#include <ctype.h>#include <string.h>#include <unistd.h>#include <stdlib.h>#ifdef NET2_REGEX#include <regexp.h>#else#include <regex.h>#endif#include "pax.h"#include "pat_rep.h"#include "extern.h"/* * routines to handle pattern matching, name modification (regular expression * substitution and interactive renames), and destination name modification for * copy (-rw). Both file name and link names are adjusted as required in these * routines. */#define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */static PATTERN *pathead = NULL; /* file pattern match list head */static PATTERN *pattail = NULL; /* file pattern match list tail */static REPLACE *rephead = NULL; /* replacement string list head */static REPLACE *reptail = NULL; /* replacement string list tail */static int rep_name __P((char *, int *, int));static int tty_rename __P((register ARCHD *));static int fix_path __P((char *, int *, char *, int));static int fn_match __P((register char *, register char *, char **));static char * range_match __P((register char *, register int));#ifdef NET2_REGEXstatic int resub __P((regexp *, char *, char *, register char *));#elsestatic int resub __P((regex_t *, regmatch_t *, char *, char *, char *));#endif/* * rep_add() * parses the -s replacement string; compiles the regular expression * and stores the compiled value and it's replacement string together in * replacement string list. Input to this function is of the form: * /old/new/pg * The first char in the string specifies the delimiter used by this * replacement string. "Old" is a regular expression in "ed" format which * is compiled by regcomp() and is applied to filenames. "new" is the * substitution string; p and g are options flags for printing and global * replacement (over the single filename) * Return: * 0 if a proper replacement string and regular expression was added to * the list of replacement patterns; -1 otherwise. */#if __STDC__intrep_add(register char *str)#elseintrep_add(str) register char *str;#endif{ register char *pt1; register char *pt2; register REPLACE *rep;# ifndef NET2_REGEX register int res; char rebuf[BUFSIZ];# endif /* * throw out the bad parameters */ if ((str == NULL) || (*str == '\0')) { warn(1, "Empty replacement string"); return(-1); } /* * first character in the string specifies what the delimiter is for * this expression */ if ((pt1 = strchr(str+1, *str)) == NULL) { warn(1, "Invalid replacement string %s", str); return(-1); } /* * allocate space for the node that handles this replacement pattern * and split out the regular expression and try to compile it */ if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) { warn(1, "Unable to allocate memory for replacement string"); return(-1); } *pt1 = '\0';# ifdef NET2_REGEX if ((rep->rcmp = regcomp(str+1)) == NULL) {# else if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); warn(1, "%s while compiling regular expression %s", rebuf, str);# endif (void)free((char *)rep); return(-1); } /* * put the delimiter back in case we need an error message and * locate the delimiter at the end of the replacement string * we then point the node at the new substitution string */ *pt1++ = *str; if ((pt2 = strchr(pt1, *str)) == NULL) {# ifdef NET2_REGEX (void)free((char *)rep->rcmp);# else regfree(&(rep->rcmp));# endif (void)free((char *)rep); warn(1, "Invalid replacement string %s", str); return(-1); } *pt2 = '\0'; rep->nstr = pt1; pt1 = pt2++; rep->flgs = 0; /* * set the options if any */ while (*pt2 != '\0') { switch(*pt2) { case 'g': case 'G': rep->flgs |= GLOB; break; case 'p': case 'P': rep->flgs |= PRNT; break; default:# ifdef NET2_REGEX (void)free((char *)rep->rcmp);# else regfree(&(rep->rcmp));# endif (void)free((char *)rep); *pt1 = *str; warn(1, "Invalid replacement string option %s", str); return(-1); } ++pt2; } /* * all done, link it in at the end */ rep->fow = NULL; if (rephead == NULL) { reptail = rephead = rep; return(0); } reptail->fow = rep; reptail = rep; return(0);}/* * pat_add() * add a pattern match to the pattern match list. Pattern matches are used * to select which archive members are extracted. (They appear as * arguments to pax in the list and read modes). If no patterns are * supplied to pax, all members in the archive will be selected (and the * pattern match list is empty). * Return: * 0 if the pattern was added to the list, -1 otherwise */#if __STDC__intpat_add(char *str)#elseintpat_add(str) char *str;#endif{ register PATTERN *pt; /* * throw out the junk */ if ((str == NULL) || (*str == '\0')) { warn(1, "Empty pattern string"); return(-1); } /* * allocate space for the pattern and store the pattern. the pattern is * part of argv so do not bother to copy it, just point at it. Add the * node to the end of the pattern list */ if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { warn(1, "Unable to allocate memory for pattern string"); return(-1); } pt->pstr = str; pt->pend = NULL; pt->plen = strlen(str); pt->fow = NULL; pt->flgs = 0; if (pathead == NULL) { pattail = pathead = pt; return(0); } pattail->fow = pt; pattail = pt; return(0);}/* * pat_chk() * complain if any the user supplied pattern did not result in a match to * a selected archive member. */#if __STDC__voidpat_chk(void)#elsevoidpat_chk()#endif{ register PATTERN *pt; register int wban = 0; /* * walk down the list checking the flags to make sure MTCH was set, * if not complain */ for (pt = pathead; pt != NULL; pt = pt->fow) { if (pt->flgs & MTCH) continue; if (!wban) { warn(1, "WARNING! These patterns were not matched:"); ++wban; } (void)fprintf(stderr, "%s\n", pt->pstr); }}/* * pat_sel() * the archive member which matches a pattern was selected. Mark the * pattern as having selected an archive member. arcn->pat points at the * pattern that was matched. arcn->pat is set in pat_match() * * NOTE: When the -c option is used, we are called when there was no match * by pat_match() (that means we did match before the inverted sense of * the logic). Now this seems really strange at first, but with -c we * need to keep track of those patterns that cause a archive member to NOT * be selected (it found an archive member with a specified pattern) * Return: * 0 if the pattern pointed at by arcn->pat was tagged as creating a * match, -1 otherwise. */#if __STDC__intpat_sel(register ARCHD *arcn)#elseintpat_sel(arcn) register ARCHD *arcn;#endif{ register PATTERN *pt; register PATTERN **ppt; register int len; /* * if no patterns just return */ if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) return(0); /* * when we are NOT limited to a single match per pattern mark the * pattern and return */ if (!nflag) { pt->flgs |= MTCH; return(0); } /* * we reach this point only when we allow a single selected match per * pattern, if the pattern matches a directory and we do not have -d * (dflag) we are done with this pattern. We may also be handed a file * in the subtree of a directory. in that case when we are operating * with -d, this pattern was already selected and we are done */ if (pt->flgs & DIR_MTCH) return(0); if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { /* * ok we matched a directory and we are allowing * subtree matches but because of the -n only its children will * match. This is tagged as a DIR_MTCH type. * WATCH IT, the code assumes that pt->pend points * into arcn->name and arcn->name has not been modified. * If not we will have a big mess. Yup this is another kludge */ /* * if this was a prefix match, remove trailing part of path * so we can copy it. Future matches will be exact prefix match */ if (pt->pend != NULL) *pt->pend = '\0'; if ((pt->pstr = strdup(arcn->name)) == NULL) { warn(1, "Pattern select out of memory"); if (pt->pend != NULL) *pt->pend = '/'; pt->pend = NULL; return(-1); } /* * put the trailing / back in the source string */ if (pt->pend != NULL) { *pt->pend = '/'; pt->pend = NULL; } pt->plen = strlen(pt->pstr); /* * strip off any trailing /, this should really never happen */ len = pt->plen - 1; if (*(pt->pstr + len) == '/') { *(pt->pstr + len) = '\0'; pt->plen = len; } pt->flgs = DIR_MTCH | MTCH; arcn->pat = pt; return(0); } /* * we are then done with this pattern, so we delete it from the list * because it can never be used for another match. * Seems kind of strange to do for a -c, but the pax spec is really * vague on the interaction of -c -n and -d. We assume that when -c * and the pattern rejects a member (i.e. it matched it) it is done. * In effect we place the order of the flags as having -c last. */ pt = pathead; ppt = &pathead; while ((pt != NULL) && (pt != arcn->pat)) { ppt = &(pt->fow); pt = pt->fow; } if (pt == NULL) { /* * should never happen.... */ warn(1, "Pattern list inconsistant"); return(-1); } *ppt = pt->fow; (void)free((char *)pt); arcn->pat = NULL; return(0);}/* * pat_match() * see if this archive member matches any supplied pattern, if a match * is found, arcn->pat is set to point at the potential pattern. Later if * this archive member is "selected" we process and mark the pattern as * one which matched a selected archive member (see pat_sel()) * Return: * 0 if this archive member should be processed, 1 if it should be * skipped and -1 if we are done with all patterns (and pax should quit * looking for more members) */#if __STDC__intpat_match(register ARCHD *arcn)#elseintpat_match(arcn) register ARCHD *arcn;#endif{ register PATTERN *pt; arcn->pat = NULL; /* * if there are no more patterns and we have -n (and not -c) we are * done. otherwise with no patterns to match, matches all */ if (pathead == NULL) { if (nflag && !cflag) return(-1); return(0); } /* * have to search down the list one at a time looking for a match. */ pt = pathead; while (pt != NULL) { /* * check for a file name match unless we have DIR_MTCH set in * this pattern then we want a prefix match */ if (pt->flgs & DIR_MTCH) { /* * this pattern was matched before to a directory * as we must have -n set for this (but not -d). We can * only match CHILDREN of that directory so we must use * an exact prefix match (no wildcards). */ if ((arcn->name[pt->plen] == '/') && (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) break; } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) break; pt = pt->fow; } /* * return the result, remember that cflag (-c) inverts the sense of a * match */ if (pt == NULL) return(cflag ? 0 : 1); /* * we had a match, now when we invert the sense (-c) we reject this * member. However we have to tag the pattern a being successful, (in a * match, not in selecting a archive member) so we call pat_sel() here. */ arcn->pat = pt; if (!cflag) return(0); if (pat_sel(arcn) < 0) return(-1); arcn->pat = NULL; return(1);}/* * fn_match() * Return: * 0 if this archive member should be processed, 1 if it should be * skipped and -1 if we are done with all patterns (and pax should quit * looking for more members) * Note: *pend may be changed to show where the prefix ends. */#if __STDC__static intfn_match(register char *pattern, register char *string, char **pend)#elsestatic intfn_match(pattern, string, pend) register char *pattern; register char *string; char **pend;#endif{ register char c; char test; *pend = NULL; for (;;) { switch (c = *pattern++) { case '\0': /* * Ok we found an exact match */ if (*string == '\0') return(0); /* * Check if it is a prefix match */ if ((dflag == 1) || (*string != '/')) return(-1); /* * It is a prefix match, remember where the trailing * / is located */ *pend = string; return(0); case '?': if ((test = *string++) == '\0') return (-1); break; case '*': c = *pattern; /* * Collapse multiple *'s. */ while (c == '*') c = *++pattern; /* * Optimized hack for pattern with a * at the end */ if (c == '\0') return (0); /* * General case, use recursion. */ while ((test = *string) != '\0') { if (!fn_match(pattern, string, pend)) return (0); ++string; } return (-1); case '[': /* * range match */ if (((test = *string++) == '\0') || ((pattern = range_match(pattern, test)) == NULL)) return (-1); break; case '\\': default: if (c != *string++) return (-1); break; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -