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