]> git.cworth.org Git - tar/blob - src/transform.c
Imported Upstream version 1.21
[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   int flags;
65   unsigned match_number;
66   regex_t regex;
67   /* Compiled replacement expression */
68   struct replace_segm *repl_head, *repl_tail;
69   size_t segm_count; /* Number of elements in the above list */
70 };
71
72 \f
73
74 int transform_flags = XFORM_ALL;
75 static struct transform *transform_head, *transform_tail;
76
77 static struct transform *
78 new_transform ()
79 {
80   struct transform *p = xzalloc (sizeof *p);
81   if (transform_tail)
82     transform_tail->next = p;
83   else
84     transform_head = p;
85   transform_tail = p;
86   return p;
87 }
88
89 static struct replace_segm *
90 add_segment (struct transform *tf)
91 {
92   struct replace_segm *segm = xmalloc (sizeof *segm);
93   segm->next = NULL;
94   if (tf->repl_tail)
95     tf->repl_tail->next = segm;
96   else
97     tf->repl_head = segm;
98   tf->repl_tail = segm;
99   tf->segm_count++;
100   return segm;
101 }
102
103 static void
104 add_literal_segment (struct transform *tf, char *str, char *end)
105 {
106   size_t len = end - str;
107   if (len)
108     {
109       struct replace_segm *segm = add_segment (tf);
110       segm->type = segm_literal;
111       segm->v.literal.ptr = xmalloc (len + 1);
112       memcpy (segm->v.literal.ptr, str, len);
113       segm->v.literal.ptr[len] = 0;
114       segm->v.literal.size = len;
115     }
116 }
117
118 static void
119 add_char_segment (struct transform *tf, int chr)
120 {
121   struct replace_segm *segm = add_segment (tf);
122   segm->type = segm_literal;
123   segm->v.literal.ptr = xmalloc (2);
124   segm->v.literal.ptr[0] = chr;
125   segm->v.literal.ptr[1] = 0;
126   segm->v.literal.size = 1;
127 }
128
129 static void
130 add_backref_segment (struct transform *tf, size_t ref)
131 {
132   struct replace_segm *segm = add_segment (tf);
133   segm->type = segm_backref;
134   segm->v.ref = ref;
135 }
136
137 static int
138 parse_xform_flags (int *pflags, int c)
139 {
140   switch (c)
141     {
142     case 'r':
143       *pflags |= XFORM_REGFILE;
144       break;
145
146     case 'R':
147       *pflags &= ~XFORM_REGFILE;
148       break;
149         
150     case 'h':
151       *pflags |= XFORM_LINK;
152       break;
153
154     case 'H':
155       *pflags &= ~XFORM_LINK;
156       break;
157         
158     case 's':
159       *pflags |= XFORM_SYMLINK;
160       break;
161
162     case 'S':
163       *pflags &= ~XFORM_SYMLINK;
164       break;
165
166     default:
167       return 1;
168     }
169   return 0;
170 }
171
172 static void
173 add_case_ctl_segment (struct transform *tf, enum case_ctl_type ctl)
174 {
175   struct replace_segm *segm = add_segment (tf);
176   segm->type = segm_case_ctl;
177   segm->v.ctl = ctl;
178 }
179
180 static const char *
181 parse_transform_expr (const char *expr)
182 {
183   int delim;
184   int i, j, rc;
185   char *str, *beg, *cur;
186   const char *p;
187   int cflags = 0;
188   struct transform *tf = new_transform ();
189
190   if (expr[0] != 's')
191     {
192       if (strncmp (expr, "flags=", 6) == 0)
193         {
194           transform_flags = 0;
195           for (expr += 6; *expr; expr++)
196             {
197               if (*expr == ';')
198                 {
199                   expr++;
200                   break;
201                 }
202               if (parse_xform_flags (&transform_flags, *expr))
203                 USAGE_ERROR ((0, 0, _("Unknown transform flag: %c"),
204                               *expr));
205             }
206           return expr;
207         }         
208       USAGE_ERROR ((0, 0, _("Invalid transform expression")));
209     }
210   
211   delim = expr[1];
212
213   /* Scan regular expression */
214   for (i = 2; expr[i] && expr[i] != delim; i++)
215     if (expr[i] == '\\' && expr[i+1])
216       i++;
217
218   if (expr[i] != delim)
219     USAGE_ERROR ((0, 0, _("Invalid transform expression")));
220
221   /* Scan replacement expression */
222   for (j = i + 1; expr[j] && expr[j] != delim; j++)
223     if (expr[j] == '\\' && expr[j+1])
224       j++;
225
226   if (expr[j] != delim)
227     USAGE_ERROR ((0, 0, _("Invalid transform expression")));
228
229   /* Check flags */
230   tf->transform_type = transform_first;
231   tf->flags = transform_flags;
232   for (p = expr + j + 1; *p && *p != ';'; p++)
233     switch (*p)
234       {
235       case 'g':
236         tf->transform_type = transform_global;
237         break;
238
239       case 'i':
240         cflags |= REG_ICASE;
241         break;
242
243       case 'x':
244         cflags |= REG_EXTENDED;
245         break;
246
247       case '0': case '1': case '2': case '3': case '4':
248       case '5': case '6': case '7': case '8': case '9':
249         tf->match_number = strtoul (p, (char**) &p, 0);
250         p--;
251         break;
252
253       default:
254         if (parse_xform_flags (&tf->flags, *p))
255           USAGE_ERROR ((0, 0, _("Unknown flag in transform expression: %c"),
256                         *p));
257       }
258
259   if (*p == ';')
260     p++;
261   
262   /* Extract and compile regex */
263   str = xmalloc (i - 1);
264   memcpy (str, expr + 2, i - 2);
265   str[i - 2] = 0;
266
267   rc = regcomp (&tf->regex, str, cflags);
268   
269   if (rc)
270     {
271       char errbuf[512];
272       regerror (rc, &tf->regex, errbuf, sizeof (errbuf));
273       USAGE_ERROR ((0, 0, _("Invalid transform expression: %s"), errbuf));
274     }
275
276   if (str[0] == '^' || str[strlen (str) - 1] == '$')
277     tf->transform_type = transform_first;
278   
279   free (str);
280
281   /* Extract and compile replacement expr */
282   i++;
283   str = xmalloc (j - i + 1);
284   memcpy (str, expr + i, j - i);
285   str[j - i] = 0;
286
287   for (cur = beg = str; *cur;)
288     {
289       if (*cur == '\\')
290         {
291           size_t n;
292           
293           add_literal_segment (tf, beg, cur);
294           switch (*++cur)
295             {
296             case '0': case '1': case '2': case '3': case '4':
297             case '5': case '6': case '7': case '8': case '9':
298               n = strtoul (cur, &cur, 10);
299               if (n > tf->regex.re_nsub)
300                 USAGE_ERROR ((0, 0, _("Invalid transform replacement: back reference out of range")));
301               add_backref_segment (tf, n);
302               break;
303
304             case '\\':
305               add_char_segment (tf, '\\');
306               cur++;
307               break;
308
309             case 'a':
310               add_char_segment (tf, '\a');
311               cur++;
312               break;
313               
314             case 'b':
315               add_char_segment (tf, '\b');
316               cur++;
317               break;
318               
319             case 'f':
320               add_char_segment (tf, '\f');
321               cur++;
322               break;
323               
324             case 'n':
325               add_char_segment (tf, '\n');
326               cur++;
327               break;
328               
329             case 'r':
330               add_char_segment (tf, '\r');
331               cur++;
332               break;
333               
334             case 't':
335               add_char_segment (tf, '\t');
336               cur++;
337               break;
338               
339             case 'v':
340               add_char_segment (tf, '\v');
341               cur++;
342               break;
343
344             case '&':
345               add_char_segment (tf, '&');
346               cur++;
347               break;
348               
349             case 'L':
350               /* Turn the replacement to lowercase until a `\U' or `\E'
351                  is found, */
352               add_case_ctl_segment (tf, ctl_locase);
353               cur++;
354               break;
355  
356             case 'l':
357               /* Turn the next character to lowercase, */
358               add_case_ctl_segment (tf, ctl_locase_next);
359               cur++;
360               break;
361               
362             case 'U':
363               /* Turn the replacement to uppercase until a `\L' or `\E'
364                  is found, */
365               add_case_ctl_segment (tf, ctl_upcase);
366               cur++;
367               break;
368               
369             case 'u':
370               /* Turn the next character to uppercase, */
371               add_case_ctl_segment (tf, ctl_upcase_next);
372               cur++;
373               break;
374               
375             case 'E':
376               /* Stop case conversion started by `\L' or `\U'. */
377               add_case_ctl_segment (tf, ctl_stop);
378               cur++;
379               break;
380   
381             default:
382               /* Try to be nice */
383               {
384                 char buf[2];
385                 buf[0] = '\\';
386                 buf[1] = *cur;
387                 add_literal_segment (tf, buf, buf + 2);
388               }
389               cur++;
390               break;
391             }
392           beg = cur;
393         }
394       else if (*cur == '&')
395         {
396           add_literal_segment (tf, beg, cur);
397           add_backref_segment (tf, 0);
398           beg = ++cur;
399         }
400       else
401         cur++;
402     }
403   add_literal_segment (tf, beg, cur);
404
405   return p;
406 }
407
408 void
409 set_transform_expr (const char *expr)
410 {
411   while (*expr)
412     expr = parse_transform_expr (expr);
413 }
414
415 /* Run case conversion specified by CASE_CTL on array PTR of SIZE
416    characters. Returns pointer to statically allocated storage. */
417 static char *
418 run_case_conv (enum case_ctl_type case_ctl, char *ptr, size_t size)
419 {
420   static char *case_ctl_buffer;
421   static size_t case_ctl_bufsize;
422   char *p;
423   
424   if (case_ctl_bufsize < size)
425     {
426       case_ctl_bufsize = size;
427       case_ctl_buffer = xrealloc (case_ctl_buffer, case_ctl_bufsize);
428     }
429   memcpy (case_ctl_buffer, ptr, size);
430   switch (case_ctl)
431     {
432     case ctl_upcase_next:
433       case_ctl_buffer[0] = toupper (case_ctl_buffer[0]);
434       break;
435       
436     case ctl_locase_next:
437       case_ctl_buffer[0] = tolower (case_ctl_buffer[0]);
438       break;
439       
440     case ctl_upcase:
441       for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
442         *p = toupper (*p);
443       break;
444       
445     case ctl_locase:
446       for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
447         *p = tolower (*p);
448       break;
449
450     case ctl_stop:
451       break;
452     }
453   return case_ctl_buffer;
454 }
455
456 \f
457 static struct obstack stk;
458 static bool stk_init;
459
460 void
461 _single_transform_name_to_obstack (struct transform *tf, char *input)
462 {
463   regmatch_t *rmp;
464   int rc;
465   size_t nmatches = 0;
466   enum case_ctl_type case_ctl = ctl_stop,  /* Current case conversion op */
467                      save_ctl = ctl_stop;  /* Saved case_ctl for \u and \l */
468   
469   /* Reset case conversion after a single-char operation */
470 #define CASE_CTL_RESET()  if (case_ctl == ctl_upcase_next     \
471                               || case_ctl == ctl_locase_next) \
472                             {                                 \
473                               case_ctl = save_ctl;            \
474                               save_ctl = ctl_stop;            \
475                             }
476   
477   rmp = xmalloc ((tf->regex.re_nsub + 1) * sizeof (*rmp));
478
479   while (*input)
480     {
481       size_t disp;
482       char *ptr;
483       
484       rc = regexec (&tf->regex, input, tf->regex.re_nsub + 1, rmp, 0);
485       
486       if (rc == 0)
487         {
488           struct replace_segm *segm;
489           
490           disp = rmp[0].rm_eo;
491
492           if (rmp[0].rm_so)
493             obstack_grow (&stk, input, rmp[0].rm_so);
494
495           nmatches++;
496           if (tf->match_number && nmatches < tf->match_number)
497             {
498               obstack_grow (&stk, input, disp);
499               input += disp;
500               continue;
501             }
502
503           for (segm = tf->repl_head; segm; segm = segm->next)
504             {
505               switch (segm->type)
506                 {
507                 case segm_literal:    /* Literal segment */
508                   if (case_ctl == ctl_stop)
509                     ptr = segm->v.literal.ptr;
510                   else
511                     {
512                       ptr = run_case_conv (case_ctl,
513                                            segm->v.literal.ptr,
514                                            segm->v.literal.size);
515                       CASE_CTL_RESET();
516                     }
517                   obstack_grow (&stk, ptr, segm->v.literal.size);
518                   break;
519               
520                 case segm_backref:    /* Back-reference segment */
521                   if (rmp[segm->v.ref].rm_so != -1
522                       && rmp[segm->v.ref].rm_eo != -1)
523                     {
524                       size_t size = rmp[segm->v.ref].rm_eo
525                                       - rmp[segm->v.ref].rm_so;
526                       ptr = input + rmp[segm->v.ref].rm_so;
527                       if (case_ctl != ctl_stop)
528                         {
529                           ptr = run_case_conv (case_ctl, ptr, size);
530                           CASE_CTL_RESET();
531                         }
532                       
533                       obstack_grow (&stk, ptr, size);
534                     }
535                   break;
536
537                 case segm_case_ctl:
538                   switch (segm->v.ctl)
539                     {
540                     case ctl_upcase_next:
541                     case ctl_locase_next:
542                       switch (save_ctl)
543                         {
544                         case ctl_stop:
545                         case ctl_upcase:
546                         case ctl_locase:
547                           save_ctl = case_ctl;
548                         default:
549                           break;
550                         }
551                       /*FALL THROUGH*/
552                       
553                     case ctl_upcase:
554                     case ctl_locase:
555                     case ctl_stop:
556                       case_ctl = segm->v.ctl;
557                     }
558                 }
559             }
560         }
561       else
562         {
563           disp = strlen (input);
564           obstack_grow (&stk, input, disp);
565         }
566
567       input += disp;
568
569       if (tf->transform_type == transform_first)
570         {
571           obstack_grow (&stk, input, strlen (input));
572           break;
573         }
574     }
575
576   obstack_1grow (&stk, 0);
577   free (rmp);
578 }
579
580 bool
581 _transform_name_to_obstack (int flags, char *input, char **output)
582 {
583   struct transform *tf;
584   bool alloced = false;
585   
586   if (!stk_init)
587     {
588       obstack_init (&stk);
589       stk_init = true;
590     }
591   
592   for (tf = transform_head; tf; tf = tf->next)
593     {
594       if (tf->flags & flags)
595         {
596           _single_transform_name_to_obstack (tf, input);
597           input = obstack_finish (&stk);
598           alloced = true;
599         }
600     }
601   *output = input;
602   return alloced;
603 }
604   
605 bool
606 transform_name_fp (char **pinput, int flags,
607                    char *(*fun)(char *, void *), void *dat)
608 {
609     char *str;
610     bool ret = _transform_name_to_obstack (flags, *pinput, &str);
611     if (ret)
612       {
613         assign_string (pinput, fun ? fun (str, dat) : str);
614         obstack_free (&stk, str);
615       }
616     else if (fun)
617       {
618         *pinput = NULL;
619         assign_string (pinput, fun (str, dat));
620         free (str);
621         ret = true;
622       }
623     return ret;
624 }
625
626 bool
627 transform_name (char **pinput, int type)
628 {
629   return transform_name_fp (pinput, type, NULL, NULL);
630 }
631