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