re-mark 1.29b-2 as not yet uploaded (merge madness!)
[debian/tar] / src / transform.c
1 /* This file is part of GNU tar.
2    Copyright 2006-2008, 2013-2014, 2016 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               if (*cur == delim)
382                 add_char_segment (tf, delim);
383               else
384                 {
385                   char buf[2];
386                   buf[0] = '\\';
387                   buf[1] = *cur;
388                   add_literal_segment (tf, buf, buf + 2);
389                 }
390               cur++;
391               break;
392             }
393           beg = cur;
394         }
395       else if (*cur == '&')
396         {
397           add_literal_segment (tf, beg, cur);
398           add_backref_segment (tf, 0);
399           beg = ++cur;
400         }
401       else
402         cur++;
403     }
404   add_literal_segment (tf, beg, cur);
405
406   return p;
407 }
408
409 void
410 set_transform_expr (const char *expr)
411 {
412   while (*expr)
413     expr = parse_transform_expr (expr);
414 }
415
416 /* Run case conversion specified by CASE_CTL on array PTR of SIZE
417    characters. Returns pointer to statically allocated storage. */
418 static char *
419 run_case_conv (enum case_ctl_type case_ctl, char *ptr, size_t size)
420 {
421   static char *case_ctl_buffer;
422   static size_t case_ctl_bufsize;
423   char *p;
424
425   if (case_ctl_bufsize < size)
426     {
427       case_ctl_bufsize = size;
428       case_ctl_buffer = xrealloc (case_ctl_buffer, case_ctl_bufsize);
429     }
430   memcpy (case_ctl_buffer, ptr, size);
431   switch (case_ctl)
432     {
433     case ctl_upcase_next:
434       case_ctl_buffer[0] = toupper ((unsigned char) case_ctl_buffer[0]);
435       break;
436
437     case ctl_locase_next:
438       case_ctl_buffer[0] = tolower ((unsigned char) case_ctl_buffer[0]);
439       break;
440
441     case ctl_upcase:
442       for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
443         *p = toupper ((unsigned char) *p);
444       break;
445
446     case ctl_locase:
447       for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
448         *p = tolower ((unsigned char) *p);
449       break;
450
451     case ctl_stop:
452       break;
453     }
454   return case_ctl_buffer;
455 }
456
457 \f
458 static struct obstack stk;
459 static bool stk_init;
460
461 static void
462 _single_transform_name_to_obstack (struct transform *tf, char *input)
463 {
464   regmatch_t *rmp;
465   int rc;
466   size_t nmatches = 0;
467   enum case_ctl_type case_ctl = ctl_stop,  /* Current case conversion op */
468                      save_ctl = ctl_stop;  /* Saved case_ctl for \u and \l */
469
470   /* Reset case conversion after a single-char operation */
471 #define CASE_CTL_RESET()  if (case_ctl == ctl_upcase_next     \
472                               || case_ctl == ctl_locase_next) \
473                             {                                 \
474                               case_ctl = save_ctl;            \
475                               save_ctl = ctl_stop;            \
476                             }
477
478   rmp = xmalloc ((tf->regex.re_nsub + 1) * sizeof (*rmp));
479
480   while (*input)
481     {
482       size_t disp;
483       char *ptr;
484
485       rc = regexec (&tf->regex, input, tf->regex.re_nsub + 1, rmp, 0);
486
487       if (rc == 0)
488         {
489           struct replace_segm *segm;
490
491           disp = rmp[0].rm_eo;
492
493           if (rmp[0].rm_so)
494             obstack_grow (&stk, input, rmp[0].rm_so);
495
496           nmatches++;
497           if (tf->match_number && nmatches < tf->match_number)
498             {
499               obstack_grow (&stk, input, disp);
500               input += disp;
501               continue;
502             }
503
504           for (segm = tf->repl_head; segm; segm = segm->next)
505             {
506               switch (segm->type)
507                 {
508                 case segm_literal:    /* Literal segment */
509                   if (case_ctl == ctl_stop)
510                     ptr = segm->v.literal.ptr;
511                   else
512                     {
513                       ptr = run_case_conv (case_ctl,
514                                            segm->v.literal.ptr,
515                                            segm->v.literal.size);
516                       CASE_CTL_RESET();
517                     }
518                   obstack_grow (&stk, ptr, segm->v.literal.size);
519                   break;
520
521                 case segm_backref:    /* Back-reference segment */
522                   if (rmp[segm->v.ref].rm_so != -1
523                       && rmp[segm->v.ref].rm_eo != -1)
524                     {
525                       size_t size = rmp[segm->v.ref].rm_eo
526                                       - rmp[segm->v.ref].rm_so;
527                       ptr = input + rmp[segm->v.ref].rm_so;
528                       if (case_ctl != ctl_stop)
529                         {
530                           ptr = run_case_conv (case_ctl, ptr, size);
531                           CASE_CTL_RESET();
532                         }
533
534                       obstack_grow (&stk, ptr, size);
535                     }
536                   break;
537
538                 case segm_case_ctl:
539                   switch (segm->v.ctl)
540                     {
541                     case ctl_upcase_next:
542                     case ctl_locase_next:
543                       switch (save_ctl)
544                         {
545                         case ctl_stop:
546                         case ctl_upcase:
547                         case ctl_locase:
548                           save_ctl = case_ctl;
549                         default:
550                           break;
551                         }
552                       /*FALL THROUGH*/
553
554                     case ctl_upcase:
555                     case ctl_locase:
556                     case ctl_stop:
557                       case_ctl = segm->v.ctl;
558                     }
559                 }
560             }
561         }
562       else
563         {
564           disp = strlen (input);
565           obstack_grow (&stk, input, disp);
566         }
567
568       input += disp;
569
570       if (tf->transform_type == transform_first)
571         {
572           obstack_grow (&stk, input, strlen (input));
573           break;
574         }
575     }
576
577   obstack_1grow (&stk, 0);
578   free (rmp);
579 }
580
581 static bool
582 _transform_name_to_obstack (int flags, char *input, char **output)
583 {
584   struct transform *tf;
585   bool alloced = false;
586
587   if (!stk_init)
588     {
589       obstack_init (&stk);
590       stk_init = true;
591     }
592
593   for (tf = transform_head; tf; tf = tf->next)
594     {
595       if (tf->flags & flags)
596         {
597           _single_transform_name_to_obstack (tf, input);
598           input = obstack_finish (&stk);
599           alloced = true;
600         }
601     }
602   *output = input;
603   return alloced;
604 }
605
606 bool
607 transform_name_fp (char **pinput, int flags,
608                    char *(*fun)(char *, void *), void *dat)
609 {
610     char *str;
611     bool ret = _transform_name_to_obstack (flags, *pinput, &str);
612     if (ret)
613       {
614         assign_string (pinput, fun ? fun (str, dat) : str);
615         obstack_free (&stk, str);
616       }
617     else if (fun)
618       {
619         *pinput = NULL;
620         assign_string (pinput, fun (str, dat));
621         free (str);
622         ret = true;
623       }
624     return ret;
625 }
626
627 bool
628 transform_name (char **pinput, int type)
629 {
630   return transform_name_fp (pinput, type, NULL, NULL);
631 }
632
633 bool
634 transform_program_p (void)
635 {
636   return transform_head != NULL;
637 }