]> git.cworth.org Git - tar/blob - src/transform.c
Imported Upstream version 1.20
[tar] / src / transform.c
1 /* This file is part of GNU tar. 
2    Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3, or (at your option) any later
7    version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
12    Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation, Inc.,
16    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 #include <system.h>
19 #include <regex.h>
20 #include "common.h"
21
22 enum transform_type
23   {
24     transform_first,
25     transform_global
26   };
27
28 enum replace_segm_type
29   {
30     segm_literal,   /* Literal segment */
31     segm_backref,   /* Back-reference segment */
32     segm_case_ctl   /* Case control segment (GNU extension) */
33   };
34
35 enum case_ctl_type
36   {
37     ctl_stop,       /* Stop case conversion */ 
38     ctl_upcase_next,/* Turn the next character to uppercase */ 
39     ctl_locase_next,/* Turn the next character to lowercase */
40     ctl_upcase,     /* Turn the replacement to uppercase until ctl_stop */
41     ctl_locase      /* Turn the replacement to lowercase until ctl_stop */
42   };
43
44 struct replace_segm
45 {
46   struct replace_segm *next;
47   enum replace_segm_type type;
48   union
49   {
50     struct
51     {
52       char *ptr;
53       size_t size;
54     } literal;                /* type == segm_literal */   
55     size_t ref;               /* type == segm_backref */
56     enum case_ctl_type ctl;   /* type == segm_case_ctl */ 
57   } v;
58 };
59
60 struct transform
61 {
62   struct transform *next;
63   enum transform_type transform_type;
64   unsigned match_number;
65   regex_t regex;
66   /* Compiled replacement expression */
67   struct replace_segm *repl_head, *repl_tail;
68   size_t segm_count; /* Number of elements in the above list */
69 };
70
71 \f
72 static struct transform *transform_head, *transform_tail;
73
74 static struct transform *
75 new_transform ()
76 {
77   struct transform *p = xzalloc (sizeof *p);
78   if (transform_tail)
79     transform_tail->next = p;
80   else
81     transform_head = p;
82   transform_tail = p;
83   return p;
84 }
85
86 static struct replace_segm *
87 add_segment (struct transform *tf)
88 {
89   struct replace_segm *segm = xmalloc (sizeof *segm);
90   segm->next = NULL;
91   if (tf->repl_tail)
92     tf->repl_tail->next = segm;
93   else
94     tf->repl_head = segm;
95   tf->repl_tail = segm;
96   tf->segm_count++;
97   return segm;
98 }
99
100 static void
101 add_literal_segment (struct transform *tf, char *str, char *end)
102 {
103   size_t len = end - str;
104   if (len)
105     {
106       struct replace_segm *segm = add_segment (tf);
107       segm->type = segm_literal;
108       segm->v.literal.ptr = xmalloc (len + 1);
109       memcpy (segm->v.literal.ptr, str, len);
110       segm->v.literal.ptr[len] = 0;
111       segm->v.literal.size = len;
112     }
113 }
114
115 static void
116 add_char_segment (struct transform *tf, int chr)
117 {
118   struct replace_segm *segm = add_segment (tf);
119   segm->type = segm_literal;
120   segm->v.literal.ptr = xmalloc (2);
121   segm->v.literal.ptr[0] = chr;
122   segm->v.literal.ptr[1] = 0;
123   segm->v.literal.size = 1;
124 }
125
126 static void
127 add_backref_segment (struct transform *tf, size_t ref)
128 {
129   struct replace_segm *segm = add_segment (tf);
130   segm->type = segm_backref;
131   segm->v.ref = ref;
132 }
133
134 static void
135 add_case_ctl_segment (struct transform *tf, enum case_ctl_type ctl)
136 {
137   struct replace_segm *segm = add_segment (tf);
138   segm->type = segm_case_ctl;
139   segm->v.ctl = ctl;
140 }
141
142 static const char *
143 parse_transform_expr (const char *expr)
144 {
145   int delim;
146   int i, j, rc;
147   char *str, *beg, *cur;
148   const char *p;
149   int cflags = 0;
150   struct transform *tf = new_transform ();
151
152   if (expr[0] != 's')
153     USAGE_ERROR ((0, 0, _("Invalid transform expression")));
154
155   delim = expr[1];
156
157   /* Scan regular expression */
158   for (i = 2; expr[i] && expr[i] != delim; i++)
159     if (expr[i] == '\\' && expr[i+1])
160       i++;
161
162   if (expr[i] != delim)
163     USAGE_ERROR ((0, 0, _("Invalid transform expression")));
164
165   /* Scan replacement expression */
166   for (j = i + 1; expr[j] && expr[j] != delim; j++)
167     if (expr[j] == '\\' && expr[j+1])
168       j++;
169
170   if (expr[j] != delim)
171     USAGE_ERROR ((0, 0, _("Invalid transform expression")));
172
173   /* Check flags */
174   tf->transform_type = transform_first;
175   for (p = expr + j + 1; *p && *p != ';'; p++)
176     switch (*p)
177       {
178       case 'g':
179         tf->transform_type = transform_global;
180         break;
181
182       case 'i':
183         cflags |= REG_ICASE;
184         break;
185
186       case 'x':
187         cflags |= REG_EXTENDED;
188         break;
189         
190       case '0': case '1': case '2': case '3': case '4':
191       case '5': case '6': case '7': case '8': case '9':
192         tf->match_number = strtoul (p, (char**) &p, 0);
193         p--;
194         break;
195
196       default:
197         USAGE_ERROR ((0, 0, _("Unknown flag in transform expression: %c"),
198                       *p));
199       }
200
201   if (*p == ';')
202     p++;
203   
204   /* Extract and compile regex */
205   str = xmalloc (i - 1);
206   memcpy (str, expr + 2, i - 2);
207   str[i - 2] = 0;
208
209   rc = regcomp (&tf->regex, str, cflags);
210   
211   if (rc)
212     {
213       char errbuf[512];
214       regerror (rc, &tf->regex, errbuf, sizeof (errbuf));
215       USAGE_ERROR ((0, 0, _("Invalid transform expression: %s"), errbuf));
216     }
217
218   if (str[0] == '^' || str[strlen (str) - 1] == '$')
219     tf->transform_type = transform_first;
220   
221   free (str);
222
223   /* Extract and compile replacement expr */
224   i++;
225   str = xmalloc (j - i + 1);
226   memcpy (str, expr + i, j - i);
227   str[j - i] = 0;
228
229   for (cur = beg = str; *cur;)
230     {
231       if (*cur == '\\')
232         {
233           size_t n;
234           
235           add_literal_segment (tf, beg, cur);
236           switch (*++cur)
237             {
238             case '0': case '1': case '2': case '3': case '4':
239             case '5': case '6': case '7': case '8': case '9':
240               n = strtoul (cur, &cur, 10);
241               if (n > tf->regex.re_nsub)
242                 USAGE_ERROR ((0, 0, _("Invalid transform replacement: back reference out of range")));
243               add_backref_segment (tf, n);
244               break;
245
246             case '\\':
247               add_char_segment (tf, '\\');
248               cur++;
249               break;
250
251             case 'a':
252               add_char_segment (tf, '\a');
253               cur++;
254               break;
255               
256             case 'b':
257               add_char_segment (tf, '\b');
258               cur++;
259               break;
260               
261             case 'f':
262               add_char_segment (tf, '\f');
263               cur++;
264               break;
265               
266             case 'n':
267               add_char_segment (tf, '\n');
268               cur++;
269               break;
270               
271             case 'r':
272               add_char_segment (tf, '\r');
273               cur++;
274               break;
275               
276             case 't':
277               add_char_segment (tf, '\t');
278               cur++;
279               break;
280               
281             case 'v':
282               add_char_segment (tf, '\v');
283               cur++;
284               break;
285
286             case '&':
287               add_char_segment (tf, '&');
288               cur++;
289               break;
290               
291             case 'L':
292               /* Turn the replacement to lowercase until a `\U' or `\E'
293                  is found, */
294               add_case_ctl_segment (tf, ctl_locase);
295               cur++;
296               break;
297  
298             case 'l':
299               /* Turn the next character to lowercase, */
300               add_case_ctl_segment (tf, ctl_locase_next);
301               cur++;
302               break;
303               
304             case 'U':
305               /* Turn the replacement to uppercase until a `\L' or `\E'
306                  is found, */
307               add_case_ctl_segment (tf, ctl_upcase);
308               cur++;
309               break;
310               
311             case 'u':
312               /* Turn the next character to uppercase, */
313               add_case_ctl_segment (tf, ctl_upcase_next);
314               cur++;
315               break;
316               
317             case 'E':
318               /* Stop case conversion started by `\L' or `\U'. */
319               add_case_ctl_segment (tf, ctl_stop);
320               cur++;
321               break;
322   
323             default:
324               /* Try to be nice */
325               {
326                 char buf[2];
327                 buf[0] = '\\';
328                 buf[1] = *cur;
329                 add_literal_segment (tf, buf, buf + 2);
330               }
331               cur++;
332               break;
333             }
334           beg = cur;
335         }
336       else if (*cur == '&')
337         {
338           add_literal_segment (tf, beg, cur);
339           add_backref_segment (tf, 0);
340           beg = ++cur;
341         }
342       else
343         cur++;
344     }
345   add_literal_segment (tf, beg, cur);
346
347   return p;
348 }
349
350 void
351 set_transform_expr (const char *expr)
352 {
353   while (*expr)
354     expr = parse_transform_expr (expr);
355 }
356
357 /* Run case conversion specified by CASE_CTL on array PTR of SIZE
358    characters. Returns pointer to statically allocated storage. */
359 static char *
360 run_case_conv (enum case_ctl_type case_ctl, char *ptr, size_t size)
361 {
362   static char *case_ctl_buffer;
363   static size_t case_ctl_bufsize;
364   char *p;
365   
366   if (case_ctl_bufsize < size)
367     {
368       case_ctl_bufsize = size;
369       case_ctl_buffer = xrealloc (case_ctl_buffer, case_ctl_bufsize);
370     }
371   memcpy (case_ctl_buffer, ptr, size);
372   switch (case_ctl)
373     {
374     case ctl_upcase_next:
375       case_ctl_buffer[0] = toupper (case_ctl_buffer[0]);
376       break;
377       
378     case ctl_locase_next:
379       case_ctl_buffer[0] = tolower (case_ctl_buffer[0]);
380       break;
381       
382     case ctl_upcase:
383       for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
384         *p = toupper (*p);
385       break;
386       
387     case ctl_locase:
388       for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
389         *p = tolower (*p);
390       break;
391
392     case ctl_stop:
393       break;
394     }
395   return case_ctl_buffer;
396 }
397
398 \f
399 static struct obstack stk;
400 static bool stk_init;
401
402 void
403 _single_transform_name_to_obstack (struct transform *tf, char *input)
404 {
405   regmatch_t *rmp;
406   int rc;
407   size_t nmatches = 0;
408   enum case_ctl_type case_ctl = ctl_stop,  /* Current case conversion op */
409                      save_ctl = ctl_stop;  /* Saved case_ctl for \u and \l */
410   
411   /* Reset case conversion after a single-char operation */
412 #define CASE_CTL_RESET()  if (case_ctl == ctl_upcase_next     \
413                               || case_ctl == ctl_locase_next) \
414                             {                                 \
415                               case_ctl = save_ctl;            \
416                               save_ctl = ctl_stop;            \
417                             }
418   
419   rmp = xmalloc ((tf->regex.re_nsub + 1) * sizeof (*rmp));
420
421   while (*input)
422     {
423       size_t disp;
424       char *ptr;
425       
426       rc = regexec (&tf->regex, input, tf->regex.re_nsub + 1, rmp, 0);
427       
428       if (rc == 0)
429         {
430           struct replace_segm *segm;
431           
432           disp = rmp[0].rm_eo;
433
434           if (rmp[0].rm_so)
435             obstack_grow (&stk, input, rmp[0].rm_so);
436
437           nmatches++;
438           if (tf->match_number && nmatches < tf->match_number)
439             {
440               obstack_grow (&stk, input, disp);
441               input += disp;
442               continue;
443             }
444
445           for (segm = tf->repl_head; segm; segm = segm->next)
446             {
447               switch (segm->type)
448                 {
449                 case segm_literal:    /* Literal segment */
450                   if (case_ctl == ctl_stop)
451                     ptr = segm->v.literal.ptr;
452                   else
453                     {
454                       ptr = run_case_conv (case_ctl,
455                                            segm->v.literal.ptr,
456                                            segm->v.literal.size);
457                       CASE_CTL_RESET();
458                     }
459                   obstack_grow (&stk, ptr, segm->v.literal.size);
460                   break;
461               
462                 case segm_backref:    /* Back-reference segment */
463                   if (rmp[segm->v.ref].rm_so != -1
464                       && rmp[segm->v.ref].rm_eo != -1)
465                     {
466                       size_t size = rmp[segm->v.ref].rm_eo
467                                       - rmp[segm->v.ref].rm_so;
468                       ptr = input + rmp[segm->v.ref].rm_so;
469                       if (case_ctl != ctl_stop)
470                         {
471                           ptr = run_case_conv (case_ctl, ptr, size);
472                           CASE_CTL_RESET();
473                         }
474                       
475                       obstack_grow (&stk, ptr, size);
476                     }
477                   break;
478
479                 case segm_case_ctl:
480                   switch (segm->v.ctl)
481                     {
482                     case ctl_upcase_next:
483                     case ctl_locase_next:
484                       switch (save_ctl)
485                         {
486                         case ctl_stop:
487                         case ctl_upcase:
488                         case ctl_locase:
489                           save_ctl = case_ctl;
490                         default:
491                           break;
492                         }
493                       /*FALL THROUGH*/
494                       
495                     case ctl_upcase:
496                     case ctl_locase:
497                     case ctl_stop:
498                       case_ctl = segm->v.ctl;
499                     }
500                 }
501             }
502         }
503       else
504         {
505           disp = strlen (input);
506           obstack_grow (&stk, input, disp);
507         }
508
509       input += disp;
510
511       if (tf->transform_type == transform_first)
512         {
513           obstack_grow (&stk, input, strlen (input));
514           break;
515         }
516     }
517
518   obstack_1grow (&stk, 0);
519   free (rmp);
520 }
521
522 bool
523 _transform_name_to_obstack (char *input, char **output)
524 {
525   struct transform *tf;
526
527   if (!stk_init)
528     {
529       obstack_init (&stk);
530       stk_init = true;
531     }
532   
533   for (tf = transform_head; tf; tf = tf->next)
534     {
535       _single_transform_name_to_obstack (tf, input);
536       input = obstack_finish (&stk);
537     }
538   *output = input;
539   return transform_head != NULL;
540 }
541   
542 bool
543 transform_name_fp (char **pinput, char *(*fun)(char *, void *), void *dat)
544 {
545     char *str;
546     bool ret = _transform_name_to_obstack (*pinput, &str);
547     if (ret)
548       {
549         assign_string (pinput, fun ? fun (str, dat) : str);
550         obstack_free (&stk, str);
551       }
552     else if (fun)
553       {
554         *pinput = NULL;
555         assign_string (pinput, fun (str, dat));
556         free (str);
557         ret = true;
558       }
559     return ret;
560 }
561
562 bool
563 transform_name (char **pinput)
564 {
565   return transform_name_fp (pinput, NULL, NULL);
566 }
567