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