]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - bin/pax/pat_rep.c
MFC r292877:
[FreeBSD/stable/10.git] / bin / pax / pat_rep.c
1 /*-
2  * Copyright (c) 1992 Keith Muller.
3  * Copyright (c) 1992, 1993
4  *      The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Keith Muller of the University of California, San Diego.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)pat_rep.c   8.2 (Berkeley) 4/18/94";
37 #endif
38 #endif /* not lint */
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #ifdef NET2_REGEX
48 #include <regexp.h>
49 #else
50 #include <regex.h>
51 #endif
52 #include "pax.h"
53 #include "pat_rep.h"
54 #include "extern.h"
55
56 /*
57  * routines to handle pattern matching, name modification (regular expression
58  * substitution and interactive renames), and destination name modification for
59  * copy (-rw). Both file name and link names are adjusted as required in these
60  * routines.
61  */
62
63 #define MAXSUBEXP       10              /* max subexpressions, DO NOT CHANGE */
64 static PATTERN *pathead = NULL;         /* file pattern match list head */
65 static PATTERN *pattail = NULL;         /* file pattern match list tail */
66 static REPLACE *rephead = NULL;         /* replacement string list head */
67 static REPLACE *reptail = NULL;         /* replacement string list tail */
68
69 static int rep_name(char *, int *, int);
70 static int tty_rename(ARCHD *);
71 static int fix_path(char *, int *, char *, int);
72 static int fn_match(char *, char *, char **);
73 static char * range_match(char *, int);
74 #ifdef NET2_REGEX
75 static int resub(regexp *, char *, char *, char *);
76 #else
77 static int resub(regex_t *, regmatch_t *, char *, char *, char *, char *);
78 #endif
79
80 /*
81  * rep_add()
82  *      parses the -s replacement string; compiles the regular expression
83  *      and stores the compiled value and it's replacement string together in
84  *      replacement string list. Input to this function is of the form:
85  *              /old/new/pg
86  *      The first char in the string specifies the delimiter used by this
87  *      replacement string. "Old" is a regular expression in "ed" format which
88  *      is compiled by regcomp() and is applied to filenames. "new" is the
89  *      substitution string; p and g are options flags for printing and global
90  *      replacement (over the single filename)
91  * Return:
92  *      0 if a proper replacement string and regular expression was added to
93  *      the list of replacement patterns; -1 otherwise.
94  */
95
96 int
97 rep_add(char *str)
98 {
99         char *pt1;
100         char *pt2;
101         REPLACE *rep;
102 #       ifndef NET2_REGEX
103         int res;
104         char rebuf[BUFSIZ];
105 #       endif
106
107         /*
108          * throw out the bad parameters
109          */
110         if ((str == NULL) || (*str == '\0')) {
111                 paxwarn(1, "Empty replacement string");
112                 return(-1);
113         }
114
115         /*
116          * first character in the string specifies what the delimiter is for
117          * this expression
118          */
119         if ((pt1 = strchr(str+1, *str)) == NULL) {
120                 paxwarn(1, "Invalid replacement string %s", str);
121                 return(-1);
122         }
123
124         /*
125          * allocate space for the node that handles this replacement pattern
126          * and split out the regular expression and try to compile it
127          */
128         if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) {
129                 paxwarn(1, "Unable to allocate memory for replacement string");
130                 return(-1);
131         }
132
133         *pt1 = '\0';
134 #       ifdef NET2_REGEX
135         if ((rep->rcmp = regcomp(str+1)) == NULL) {
136 #       else
137         if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) {
138                 regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf));
139                 paxwarn(1, "%s while compiling regular expression %s", rebuf, str);
140 #       endif
141                 free(rep);
142                 return(-1);
143         }
144
145         /*
146          * put the delimiter back in case we need an error message and
147          * locate the delimiter at the end of the replacement string
148          * we then point the node at the new substitution string
149          */
150         *pt1++ = *str;
151         if ((pt2 = strchr(pt1, *str)) == NULL) {
152 #               ifdef NET2_REGEX
153                 free(rep->rcmp);
154 #               else
155                 regfree(&rep->rcmp);
156 #               endif
157                 free(rep);
158                 paxwarn(1, "Invalid replacement string %s", str);
159                 return(-1);
160         }
161
162         *pt2 = '\0';
163         rep->nstr = pt1;
164         pt1 = pt2++;
165         rep->flgs = 0;
166
167         /*
168          * set the options if any
169          */
170         while (*pt2 != '\0') {
171                 switch(*pt2) {
172                 case 'g':
173                 case 'G':
174                         rep->flgs  |= GLOB;
175                         break;
176                 case 'p':
177                 case 'P':
178                         rep->flgs  |= PRNT;
179                         break;
180                 default:
181 #                       ifdef NET2_REGEX
182                         free(rep->rcmp);
183 #                       else
184                         regfree(&rep->rcmp);
185 #                       endif
186                         free(rep);
187                         *pt1 = *str;
188                         paxwarn(1, "Invalid replacement string option %s", str);
189                         return(-1);
190                 }
191                 ++pt2;
192         }
193
194         /*
195          * all done, link it in at the end
196          */
197         rep->fow = NULL;
198         if (rephead == NULL) {
199                 reptail = rephead = rep;
200                 return(0);
201         }
202         reptail->fow = rep;
203         reptail = rep;
204         return(0);
205 }
206
207 /*
208  * pat_add()
209  *      add a pattern match to the pattern match list. Pattern matches are used
210  *      to select which archive members are extracted. (They appear as
211  *      arguments to pax in the list and read modes). If no patterns are
212  *      supplied to pax, all members in the archive will be selected (and the
213  *      pattern match list is empty).
214  * Return:
215  *      0 if the pattern was added to the list, -1 otherwise
216  */
217
218 int
219 pat_add(char *str, char *chdnam)
220 {
221         PATTERN *pt;
222
223         /*
224          * throw out the junk
225          */
226         if ((str == NULL) || (*str == '\0')) {
227                 paxwarn(1, "Empty pattern string");
228                 return(-1);
229         }
230
231         /*
232          * allocate space for the pattern and store the pattern. the pattern is
233          * part of argv so do not bother to copy it, just point at it. Add the
234          * node to the end of the pattern list
235          */
236         if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) {
237                 paxwarn(1, "Unable to allocate memory for pattern string");
238                 return(-1);
239         }
240
241         pt->pstr = str;
242         pt->pend = NULL;
243         pt->plen = strlen(str);
244         pt->fow = NULL;
245         pt->flgs = 0;
246         pt->chdname = chdnam;
247
248         if (pathead == NULL) {
249                 pattail = pathead = pt;
250                 return(0);
251         }
252         pattail->fow = pt;
253         pattail = pt;
254         return(0);
255 }
256
257 /*
258  * pat_chk()
259  *      complain if any the user supplied pattern did not result in a match to
260  *      a selected archive member.
261  */
262
263 void
264 pat_chk(void)
265 {
266         PATTERN *pt;
267         int wban = 0;
268
269         /*
270          * walk down the list checking the flags to make sure MTCH was set,
271          * if not complain
272          */
273         for (pt = pathead; pt != NULL; pt = pt->fow) {
274                 if (pt->flgs & MTCH)
275                         continue;
276                 if (!wban) {
277                         paxwarn(1, "WARNING! These patterns were not matched:");
278                         ++wban;
279                 }
280                 (void)fprintf(stderr, "%s\n", pt->pstr);
281         }
282 }
283
284 /*
285  * pat_sel()
286  *      the archive member which matches a pattern was selected. Mark the
287  *      pattern as having selected an archive member. arcn->pat points at the
288  *      pattern that was matched. arcn->pat is set in pat_match()
289  *
290  *      NOTE: When the -c option is used, we are called when there was no match
291  *      by pat_match() (that means we did match before the inverted sense of
292  *      the logic). Now this seems really strange at first, but with -c  we
293  *      need to keep track of those patterns that cause an archive member to NOT
294  *      be selected (it found an archive member with a specified pattern)
295  * Return:
296  *      0 if the pattern pointed at by arcn->pat was tagged as creating a
297  *      match, -1 otherwise.
298  */
299
300 int
301 pat_sel(ARCHD *arcn)
302 {
303         PATTERN *pt;
304         PATTERN **ppt;
305         int len;
306
307         /*
308          * if no patterns just return
309          */
310         if ((pathead == NULL) || ((pt = arcn->pat) == NULL))
311                 return(0);
312
313         /*
314          * when we are NOT limited to a single match per pattern mark the
315          * pattern and return
316          */
317         if (!nflag) {
318                 pt->flgs |= MTCH;
319                 return(0);
320         }
321
322         /*
323          * we reach this point only when we allow a single selected match per
324          * pattern, if the pattern matches a directory and we do not have -d
325          * (dflag) we are done with this pattern. We may also be handed a file
326          * in the subtree of a directory. in that case when we are operating
327          * with -d, this pattern was already selected and we are done
328          */
329         if (pt->flgs & DIR_MTCH)
330                 return(0);
331
332         if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) {
333                 /*
334                  * ok we matched a directory and we are allowing
335                  * subtree matches but because of the -n only its children will
336                  * match. This is tagged as a DIR_MTCH type.
337                  * WATCH IT, the code assumes that pt->pend points
338                  * into arcn->name and arcn->name has not been modified.
339                  * If not we will have a big mess. Yup this is another kludge
340                  */
341
342                 /*
343                  * if this was a prefix match, remove trailing part of path
344                  * so we can copy it. Future matches will be exact prefix match
345                  */
346                 if (pt->pend != NULL)
347                         *pt->pend = '\0';
348
349                 if ((pt->pstr = strdup(arcn->name)) == NULL) {
350                         paxwarn(1, "Pattern select out of memory");
351                         if (pt->pend != NULL)
352                                 *pt->pend = '/';
353                         pt->pend = NULL;
354                         return(-1);
355                 }
356
357                 /*
358                  * put the trailing / back in the source string
359                  */
360                 if (pt->pend != NULL) {
361                         *pt->pend = '/';
362                         pt->pend = NULL;
363                 }
364                 pt->plen = strlen(pt->pstr);
365
366                 /*
367                  * strip off any trailing /, this should really never happen
368                  */
369                 len = pt->plen - 1;
370                 if (*(pt->pstr + len) == '/') {
371                         *(pt->pstr + len) = '\0';
372                         pt->plen = len;
373                 }
374                 pt->flgs = DIR_MTCH | MTCH;
375                 arcn->pat = pt;
376                 return(0);
377         }
378
379         /*
380          * we are then done with this pattern, so we delete it from the list
381          * because it can never be used for another match.
382          * Seems kind of strange to do for a -c, but the pax spec is really
383          * vague on the interaction of -c -n and -d. We assume that when -c
384          * and the pattern rejects a member (i.e. it matched it) it is done.
385          * In effect we place the order of the flags as having -c last.
386          */
387         pt = pathead;
388         ppt = &pathead;
389         while ((pt != NULL) && (pt != arcn->pat)) {
390                 ppt = &(pt->fow);
391                 pt = pt->fow;
392         }
393
394         if (pt == NULL) {
395                 /*
396                  * should never happen....
397                  */
398                 paxwarn(1, "Pattern list inconsistent");
399                 return(-1);
400         }
401         *ppt = pt->fow;
402         free(pt);
403         arcn->pat = NULL;
404         return(0);
405 }
406
407 /*
408  * pat_match()
409  *      see if this archive member matches any supplied pattern, if a match
410  *      is found, arcn->pat is set to point at the potential pattern. Later if
411  *      this archive member is "selected" we process and mark the pattern as
412  *      one which matched a selected archive member (see pat_sel())
413  * Return:
414  *      0 if this archive member should be processed, 1 if it should be
415  *      skipped and -1 if we are done with all patterns (and pax should quit
416  *      looking for more members)
417  */
418
419 int
420 pat_match(ARCHD *arcn)
421 {
422         PATTERN *pt;
423
424         arcn->pat = NULL;
425
426         /*
427          * if there are no more patterns and we have -n (and not -c) we are
428          * done. otherwise with no patterns to match, matches all
429          */
430         if (pathead == NULL) {
431                 if (nflag && !cflag)
432                         return(-1);
433                 return(0);
434         }
435
436         /*
437          * have to search down the list one at a time looking for a match.
438          */
439         pt = pathead;
440         while (pt != NULL) {
441                 /*
442                  * check for a file name match unless we have DIR_MTCH set in
443                  * this pattern then we want a prefix match
444                  */
445                 if (pt->flgs & DIR_MTCH) {
446                         /*
447                          * this pattern was matched before to a directory
448                          * as we must have -n set for this (but not -d). We can
449                          * only match CHILDREN of that directory so we must use
450                          * an exact prefix match (no wildcards).
451                          */
452                         if ((arcn->name[pt->plen] == '/') &&
453                             (strncmp(pt->pstr, arcn->name, pt->plen) == 0))
454                                 break;
455                 } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0)
456                         break;
457                 pt = pt->fow;
458         }
459
460         /*
461          * return the result, remember that cflag (-c) inverts the sense of a
462          * match
463          */
464         if (pt == NULL)
465                 return(cflag ? 0 : 1);
466
467         /*
468          * We had a match, now when we invert the sense (-c) we reject this
469          * member. However we have to tag the pattern a being successful, (in a
470          * match, not in selecting an archive member) so we call pat_sel() here.
471          */
472         arcn->pat = pt;
473         if (!cflag)
474                 return(0);
475
476         if (pat_sel(arcn) < 0)
477                 return(-1);
478         arcn->pat = NULL;
479         return(1);
480 }
481
482 /*
483  * fn_match()
484  * Return:
485  *      0 if this archive member should be processed, 1 if it should be
486  *      skipped and -1 if we are done with all patterns (and pax should quit
487  *      looking for more members)
488  *      Note: *pend may be changed to show where the prefix ends.
489  */
490
491 static int
492 fn_match(char *pattern, char *string, char **pend)
493 {
494         char c;
495         char test;
496
497         *pend = NULL;
498         for (;;) {
499                 switch (c = *pattern++) {
500                 case '\0':
501                         /*
502                          * Ok we found an exact match
503                          */
504                         if (*string == '\0')
505                                 return(0);
506
507                         /*
508                          * Check if it is a prefix match
509                          */
510                         if ((dflag == 1) || (*string != '/'))
511                                 return(-1);
512
513                         /*
514                          * It is a prefix match, remember where the trailing
515                          * / is located
516                          */
517                         *pend = string;
518                         return(0);
519                 case '?':
520                         if ((test = *string++) == '\0')
521                                 return (-1);
522                         break;
523                 case '*':
524                         c = *pattern;
525                         /*
526                          * Collapse multiple *'s.
527                          */
528                         while (c == '*')
529                                 c = *++pattern;
530
531                         /*
532                          * Optimized hack for pattern with a * at the end
533                          */
534                         if (c == '\0')
535                                 return (0);
536
537                         /*
538                          * General case, use recursion.
539                          */
540                         while ((test = *string) != '\0') {
541                                 if (!fn_match(pattern, string, pend))
542                                         return (0);
543                                 ++string;
544                         }
545                         return (-1);
546                 case '[':
547                         /*
548                          * range match
549                          */
550                         if (((test = *string++) == '\0') ||
551                             ((pattern = range_match(pattern, test)) == NULL))
552                                 return (-1);
553                         break;
554                 case '\\':
555                 default:
556                         if (c != *string++)
557                                 return (-1);
558                         break;
559                 }
560         }
561         /* NOTREACHED */
562 }
563
564 static char *
565 range_match(char *pattern, int test)
566 {
567         char c;
568         char c2;
569         int negate;
570         int ok = 0;
571
572         if ((negate = (*pattern == '!')) != 0)
573                 ++pattern;
574
575         while ((c = *pattern++) != ']') {
576                 /*
577                  * Illegal pattern
578                  */
579                 if (c == '\0')
580                         return (NULL);
581
582                 if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') &&
583                     (c2 != ']')) {
584                         if ((c <= test) && (test <= c2))
585                                 ok = 1;
586                         pattern += 2;
587                 } else if (c == test)
588                         ok = 1;
589         }
590         return (ok == negate ? NULL : pattern);
591 }
592
593 /*
594  * mod_name()
595  *      modify a selected file name. first attempt to apply replacement string
596  *      expressions, then apply interactive file rename. We apply replacement
597  *      string expressions to both filenames and file links (if we didn't the
598  *      links would point to the wrong place, and we could never be able to
599  *      move an archive that has a file link in it). When we rename files
600  *      interactively, we store that mapping (old name to user input name) so
601  *      if we spot any file links to the old file name in the future, we will
602  *      know exactly how to fix the file link.
603  * Return:
604  *      0 continue to  process file, 1 skip this file, -1 pax is finished
605  */
606
607 int
608 mod_name(ARCHD *arcn)
609 {
610         int res = 0;
611
612         /*
613          * Strip off leading '/' if appropriate.
614          * Currently, this option is only set for the tar format.
615          */
616         if (rmleadslash && arcn->name[0] == '/') {
617                 if (arcn->name[1] == '\0') {
618                         arcn->name[0] = '.';
619                 } else {
620                         (void)memmove(arcn->name, &arcn->name[1],
621                             strlen(arcn->name));
622                         arcn->nlen--;
623                 }
624                 if (rmleadslash < 2) {
625                         rmleadslash = 2;
626                         paxwarn(0, "Removing leading / from absolute path names in the archive");
627                 }
628         }
629         if (rmleadslash && arcn->ln_name[0] == '/' &&
630             (arcn->type == PAX_HLK || arcn->type == PAX_HRG)) {
631                 if (arcn->ln_name[1] == '\0') {
632                         arcn->ln_name[0] = '.';
633                 } else {
634                         (void)memmove(arcn->ln_name, &arcn->ln_name[1],
635                             strlen(arcn->ln_name));
636                         arcn->ln_nlen--;
637                 }
638                 if (rmleadslash < 2) {
639                         rmleadslash = 2;
640                         paxwarn(0, "Removing leading / from absolute path names in the archive");
641                 }
642         }
643
644         /*
645          * IMPORTANT: We have a problem. what do we do with symlinks?
646          * Modifying a hard link name makes sense, as we know the file it
647          * points at should have been seen already in the archive (and if it
648          * wasn't seen because of a read error or a bad archive, we lose
649          * anyway). But there are no such requirements for symlinks. On one
650          * hand the symlink that refers to a file in the archive will have to
651          * be modified to so it will still work at its new location in the
652          * file system. On the other hand a symlink that points elsewhere (and
653          * should continue to do so) should not be modified. There is clearly
654          * no perfect solution here. So we handle them like hardlinks. Clearly
655          * a replacement made by the interactive rename mapping is very likely
656          * to be correct since it applies to a single file and is an exact
657          * match. The regular expression replacements are a little harder to
658          * justify though. We claim that the symlink name is only likely
659          * to be replaced when it points within the file tree being moved and
660          * in that case it should be modified. what we really need to do is to
661          * call an oracle here. :)
662          */
663         if (rephead != NULL) {
664                 /*
665                  * we have replacement strings, modify the name and the link
666                  * name if any.
667                  */
668                 if ((res = rep_name(arcn->name, &(arcn->nlen), 1)) != 0)
669                         return(res);
670
671                 if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||
672                     (arcn->type == PAX_HRG)) &&
673                     ((res = rep_name(arcn->ln_name, &(arcn->ln_nlen), 0)) != 0))
674                         return(res);
675         }
676
677         if (iflag) {
678                 /*
679                  * perform interactive file rename, then map the link if any
680                  */
681                 if ((res = tty_rename(arcn)) != 0)
682                         return(res);
683                 if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||
684                     (arcn->type == PAX_HRG))
685                         sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name));
686         }
687         return(res);
688 }
689
690 /*
691  * tty_rename()
692  *      Prompt the user for a replacement file name. A "." keeps the old name,
693  *      a empty line skips the file, and an EOF on reading the tty, will cause
694  *      pax to stop processing and exit. Otherwise the file name input, replaces
695  *      the old one.
696  * Return:
697  *      0 process this file, 1 skip this file, -1 we need to exit pax
698  */
699
700 static int
701 tty_rename(ARCHD *arcn)
702 {
703         char tmpname[PAXPATHLEN+2];
704         int res;
705
706         /*
707          * prompt user for the replacement name for a file, keep trying until
708          * we get some reasonable input. Archives may have more than one file
709          * on them with the same name (from updates etc). We print verbose info
710          * on the file so the user knows what is up.
711          */
712         tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0);
713
714         for (;;) {
715                 ls_tty(arcn);
716                 tty_prnt("Input new name, or a \".\" to keep the old name, ");
717                 tty_prnt("or a \"return\" to skip this file.\n");
718                 tty_prnt("Input > ");
719                 if (tty_read(tmpname, sizeof(tmpname)) < 0)
720                         return(-1);
721                 if (strcmp(tmpname, "..") == 0) {
722                         tty_prnt("Try again, illegal file name: ..\n");
723                         continue;
724                 }
725                 if (strlen(tmpname) > PAXPATHLEN) {
726                         tty_prnt("Try again, file name too long\n");
727                         continue;
728                 }
729                 break;
730         }
731
732         /*
733          * empty file name, skips this file. a "." leaves it alone
734          */
735         if (tmpname[0] == '\0') {
736                 tty_prnt("Skipping file.\n");
737                 return(1);
738         }
739         if ((tmpname[0] == '.') && (tmpname[1] == '\0')) {
740                 tty_prnt("Processing continues, name unchanged.\n");
741                 return(0);
742         }
743
744         /*
745          * ok the name changed. We may run into links that point at this
746          * file later. we have to remember where the user sent the file
747          * in order to repair any links.
748          */
749         tty_prnt("Processing continues, name changed to: %s\n", tmpname);
750         res = add_name(arcn->name, arcn->nlen, tmpname);
751         arcn->nlen = l_strncpy(arcn->name, tmpname, sizeof(arcn->name) - 1);
752         arcn->name[arcn->nlen] = '\0';
753         if (res < 0)
754                 return(-1);
755         return(0);
756 }
757
758 /*
759  * set_dest()
760  *      fix up the file name and the link name (if any) so this file will land
761  *      in the destination directory (used during copy() -rw).
762  * Return:
763  *      0 if ok, -1 if failure (name too long)
764  */
765
766 int
767 set_dest(ARCHD *arcn, char *dest_dir, int dir_len)
768 {
769         if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0)
770                 return(-1);
771
772         /*
773          * It is really hard to deal with symlinks here, we cannot be sure
774          * if the name they point was moved (or will be moved). It is best to
775          * leave them alone.
776          */
777         if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG))
778                 return(0);
779
780         if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0)
781                 return(-1);
782         return(0);
783 }
784
785 /*
786  * fix_path
787  *      concatenate dir_name and or_name and store the result in or_name (if
788  *      it fits). This is one ugly function.
789  * Return:
790  *      0 if ok, -1 if the final name is too long
791  */
792
793 static int
794 fix_path( char *or_name, int *or_len, char *dir_name, int dir_len)
795 {
796         char *src;
797         char *dest;
798         char *start;
799         int len;
800
801         /*
802          * we shift the or_name to the right enough to tack in the dir_name
803          * at the front. We make sure we have enough space for it all before
804          * we start. since dest always ends in a slash, we skip of or_name
805          * if it also starts with one.
806          */
807         start = or_name;
808         src = start + *or_len;
809         dest = src + dir_len;
810         if (*start == '/') {
811                 ++start;
812                 --dest;
813         }
814         if ((len = dest - or_name) > PAXPATHLEN) {
815                 paxwarn(1, "File name %s/%s, too long", dir_name, start);
816                 return(-1);
817         }
818         *or_len = len;
819
820         /*
821          * enough space, shift
822          */
823         while (src >= start)
824                 *dest-- = *src--;
825         src = dir_name + dir_len - 1;
826
827         /*
828          * splice in the destination directory name
829          */
830         while (src >= dir_name)
831                 *dest-- = *src--;
832
833         *(or_name + len) = '\0';
834         return(0);
835 }
836
837 /*
838  * rep_name()
839  *      walk down the list of replacement strings applying each one in order.
840  *      when we find one with a successful substitution, we modify the name
841  *      as specified. if required, we print the results. if the resulting name
842  *      is empty, we will skip this archive member. We use the regexp(3)
843  *      routines (regexp() ought to win a prize as having the most cryptic
844  *      library function manual page).
845  *      --Parameters--
846  *      name is the file name we are going to apply the regular expressions to
847  *      (and may be modified)
848  *      nlen is the length of this name (and is modified to hold the length of
849  *      the final string).
850  *      prnt is a flag that says whether to print the final result.
851  * Return:
852  *      0 if substitution was successful, 1 if we are to skip the file (the name
853  *      ended up empty)
854  */
855
856 static int
857 rep_name(char *name, int *nlen, int prnt)
858 {
859         REPLACE *pt;
860         char *inpt;
861         char *outpt;
862         char *endpt;
863         char *rpt;
864         int found = 0;
865         int res;
866 #       ifndef NET2_REGEX
867         regmatch_t pm[MAXSUBEXP];
868 #       endif
869         char nname[PAXPATHLEN+1];       /* final result of all replacements */
870         char buf1[PAXPATHLEN+1];        /* where we work on the name */
871
872         /*
873          * copy the name into buf1, where we will work on it. We need to keep
874          * the orig string around so we can print out the result of the final
875          * replacement. We build up the final result in nname. inpt points at
876          * the string we apply the regular expression to. prnt is used to
877          * suppress printing when we handle replacements on the link field
878          * (the user already saw that substitution go by)
879          */
880         pt = rephead;
881         (void)strlcpy(buf1, name, sizeof(buf1));
882         inpt = buf1;
883         outpt = nname;
884         endpt = outpt + PAXPATHLEN;
885
886         /*
887          * try each replacement string in order
888          */
889         while (pt != NULL) {
890                 do {
891                         /*
892                          * check for a successful substitution, if not go to
893                          * the next pattern, or cleanup if we were global
894                          */
895 #                       ifdef NET2_REGEX
896                         if (regexec(pt->rcmp, inpt) == 0)
897 #                       else
898                         if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0)
899 #                       endif
900                                 break;
901
902                         /*
903                          * ok we found one. We have three parts, the prefix
904                          * which did not match, the section that did and the
905                          * tail (that also did not match). Copy the prefix to
906                          * the final output buffer (watching to make sure we
907                          * do not create a string too long).
908                          */
909                         found = 1;
910 #                       ifdef NET2_REGEX
911                         rpt = pt->rcmp->startp[0];
912 #                       else
913                         rpt = inpt + pm[0].rm_so;
914 #                       endif
915
916                         while ((inpt < rpt) && (outpt < endpt))
917                                 *outpt++ = *inpt++;
918                         if (outpt == endpt)
919                                 break;
920
921                         /*
922                          * for the second part (which matched the regular
923                          * expression) apply the substitution using the
924                          * replacement string and place it the prefix in the
925                          * final output. If we have problems, skip it.
926                          */
927 #                       ifdef NET2_REGEX
928                         if ((res = resub(pt->rcmp,pt->nstr,outpt,endpt)) < 0) {
929 #                       else
930                         if ((res = resub(&(pt->rcmp),pm,inpt,pt->nstr,outpt,endpt))
931                             < 0) {
932 #                       endif
933                                 if (prnt)
934                                         paxwarn(1, "Replacement name error %s",
935                                             name);
936                                 return(1);
937                         }
938                         outpt += res;
939
940                         /*
941                          * we set up to look again starting at the first
942                          * character in the tail (of the input string right
943                          * after the last character matched by the regular
944                          * expression (inpt always points at the first char in
945                          * the string to process). If we are not doing a global
946                          * substitution, we will use inpt to copy the tail to
947                          * the final result. Make sure we do not overrun the
948                          * output buffer
949                          */
950 #                       ifdef NET2_REGEX
951                         inpt = pt->rcmp->endp[0];
952 #                       else
953                         inpt += pm[0].rm_eo - pm[0].rm_so;
954 #                       endif
955
956                         if ((outpt == endpt) || (*inpt == '\0'))
957                                 break;
958
959                         /*
960                          * if the user wants global we keep trying to
961                          * substitute until it fails, then we are done.
962                          */
963                 } while (pt->flgs & GLOB);
964
965                 if (found)
966                         break;
967
968                 /*
969                  * a successful substitution did NOT occur, try the next one
970                  */
971                 pt = pt->fow;
972         }
973
974         if (found) {
975                 /*
976                  * we had a substitution, copy the last tail piece (if there is
977                  * room) to the final result
978                  */
979                 while ((outpt < endpt) && (*inpt != '\0'))
980                         *outpt++ = *inpt++;
981
982                 *outpt = '\0';
983                 if ((outpt == endpt) && (*inpt != '\0')) {
984                         if (prnt)
985                                 paxwarn(1,"Replacement name too long %s >> %s",
986                                     name, nname);
987                         return(1);
988                 }
989
990                 /*
991                  * inform the user of the result if wanted
992                  */
993                 if (prnt && (pt->flgs & PRNT)) {
994                         if (*nname == '\0')
995                                 (void)fprintf(stderr,"%s >> <empty string>\n",
996                                     name);
997                         else
998                                 (void)fprintf(stderr,"%s >> %s\n", name, nname);
999                 }
1000
1001                 /*
1002                  * if empty inform the caller this file is to be skipped
1003                  * otherwise copy the new name over the orig name and return
1004                  */
1005                 if (*nname == '\0')
1006                         return(1);
1007                 *nlen = l_strncpy(name, nname, PAXPATHLEN + 1);
1008                 name[PAXPATHLEN] = '\0';
1009         }
1010         return(0);
1011 }
1012
1013 #ifdef NET2_REGEX
1014 /*
1015  * resub()
1016  *      apply the replacement to the matched expression. expand out the old
1017  *      style ed(1) subexpression expansion.
1018  * Return:
1019  *      -1 if error, or the number of characters added to the destination.
1020  */
1021
1022 static int
1023 resub(regexp *prog, char *src, char *dest, char *destend)
1024 {
1025         char *spt;
1026         char *dpt;
1027         char c;
1028         int no;
1029         int len;
1030
1031         spt = src;
1032         dpt = dest;
1033         while ((dpt < destend) && ((c = *spt++) != '\0')) {
1034                 if (c == '&')
1035                         no = 0;
1036                 else if ((c == '\\') && (*spt >= '0') && (*spt <= '9'))
1037                         no = *spt++ - '0';
1038                 else {
1039                         if ((c == '\\') && ((*spt == '\\') || (*spt == '&')))
1040                                 c = *spt++;
1041                         *dpt++ = c;
1042                         continue;
1043                 }
1044                 if ((prog->startp[no] == NULL) || (prog->endp[no] == NULL) ||
1045                     ((len = prog->endp[no] - prog->startp[no]) <= 0))
1046                         continue;
1047
1048                 /*
1049                  * copy the subexpression to the destination.
1050                  * fail if we run out of space or the match string is damaged
1051                  */
1052                 if (len > (destend - dpt))
1053                         len = destend - dpt;
1054                 if (l_strncpy(dpt, prog->startp[no], len) != len)
1055                         return(-1);
1056                 dpt += len;
1057         }
1058         return(dpt - dest);
1059 }
1060
1061 #else
1062
1063 /*
1064  * resub()
1065  *      apply the replacement to the matched expression. expand out the old
1066  *      style ed(1) subexpression expansion.
1067  * Return:
1068  *      -1 if error, or the number of characters added to the destination.
1069  */
1070
1071 static int
1072 resub(regex_t *rp, regmatch_t *pm, char *orig, char *src, char *dest,
1073         char *destend)
1074 {
1075         char *spt;
1076         char *dpt;
1077         char c;
1078         regmatch_t *pmpt;
1079         int len;
1080         int subexcnt;
1081
1082         spt =  src;
1083         dpt = dest;
1084         subexcnt = rp->re_nsub;
1085         while ((dpt < destend) && ((c = *spt++) != '\0')) {
1086                 /*
1087                  * see if we just have an ordinary replacement character
1088                  * or we refer to a subexpression.
1089                  */
1090                 if (c == '&') {
1091                         pmpt = pm;
1092                 } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) {
1093                         /*
1094                          * make sure there is a subexpression as specified
1095                          */
1096                         if ((len = *spt++ - '0') > subexcnt)
1097                                 return(-1);
1098                         pmpt = pm + len;
1099                 } else {
1100                         /*
1101                          * Ordinary character, just copy it
1102                          */
1103                         if ((c == '\\') && ((*spt == '\\') || (*spt == '&')))
1104                                 c = *spt++;
1105                         *dpt++ = c;
1106                         continue;
1107                 }
1108
1109                 /*
1110                  * continue if the subexpression is bogus
1111                  */
1112                 if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) ||
1113                     ((len = pmpt->rm_eo - pmpt->rm_so) <= 0))
1114                         continue;
1115
1116                 /*
1117                  * copy the subexpression to the destination.
1118                  * fail if we run out of space or the match string is damaged
1119                  */
1120                 if (len > (destend - dpt))
1121                         len = destend - dpt;
1122                 if (l_strncpy(dpt, orig + pmpt->rm_so, len) != len)
1123                         return(-1);
1124                 dpt += len;
1125         }
1126         return(dpt - dest);
1127 }
1128 #endif