]> Dogcows Code - chaz/tar/blob - src/transform.c
New file
[chaz/tar] / src / transform.c
1 /* This file is part of GNU tar.
2 Copyright (C) 2006 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 2, 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_none,
25 transform_first,
26 transform_global
27 }
28 transform_type = transform_none;
29 static regex_t regex;
30 static struct obstack stk;
31
32 enum replace_segm_type
33 {
34 segm_literal, /* Literal segment */
35 segm_backref, /* Back-reference segment */
36 };
37
38 struct replace_segm
39 {
40 struct replace_segm *next;
41 enum replace_segm_type type;
42 union
43 {
44 struct
45 {
46 char *ptr;
47 size_t size;
48 } literal;
49 size_t ref;
50 } v;
51 };
52
53 static struct replace_segm *repl_head, *repl_tail;
54 static segm_count;
55
56 static struct replace_segm *
57 add_segment (void)
58 {
59 struct replace_segm *segm = xmalloc (sizeof *segm);
60 segm->next = NULL;
61 if (repl_tail)
62 repl_tail->next = segm;
63 else
64 repl_head = segm;
65 repl_tail = segm;
66 segm_count++;
67 return segm;
68 }
69
70 static void
71 add_literal_segment (char *str, char *end)
72 {
73 size_t len = end - str;
74 if (len)
75 {
76 struct replace_segm *segm = add_segment ();
77 segm->type = segm_literal;
78 segm->v.literal.ptr = xmalloc (len + 1);
79 memcpy (segm->v.literal.ptr, str, len);
80 segm->v.literal.ptr[len] = 0;
81 segm->v.literal.size = len;
82 }
83 }
84
85 static void
86 add_char_segment (int chr)
87 {
88 struct replace_segm *segm = add_segment ();
89 segm->type = segm_literal;
90 segm->v.literal.ptr = xmalloc (2);
91 segm->v.literal.ptr[0] = chr;
92 segm->v.literal.ptr[1] = 0;
93 segm->v.literal.size = 2;
94 }
95
96 static void
97 add_backref_segment (size_t ref)
98 {
99 struct replace_segm *segm = add_segment ();
100 segm->type = segm_backref;
101 segm->v.ref = ref;
102 }
103
104 void
105 set_transform_expr (const char *expr)
106 {
107 int delim;
108 int i, j, rc;
109 char *str, *beg, *cur;
110 const char *p;
111 int cflags = 0;
112
113 if (transform_type == transform_none)
114 obstack_init (&stk);
115 else
116 {
117 /* Redefinition of the transform expression */
118 regfree (&regex);
119 }
120
121 if (expr[0] != 's')
122 USAGE_ERROR ((0, 0, _("Invalid transform expression")));
123
124 delim = expr[1];
125
126 /* Scan regular expression */
127 for (i = 2; expr[i] && expr[i] != delim; i++)
128 if (expr[i] == '\\' && expr[i+1])
129 i++;
130
131 if (expr[i] != delim)
132 USAGE_ERROR ((0, 0, _("Invalid transform expression")));
133
134 /* Scan replacement expression */
135 for (j = i + 1; expr[j] && expr[j] != delim; j++)
136 if (expr[j] == '\\' && expr[j+1])
137 j++;
138
139 if (expr[j] != delim)
140 USAGE_ERROR ((0, 0, _("Invalid transform expression")));
141
142 /* Check flags */
143 transform_type = transform_first;
144 for (p = expr + j + 1; *p; p++)
145 switch (*p)
146 {
147 case 'g':
148 transform_type = transform_global;
149 break;
150
151 case 'i':
152 cflags |= REG_ICASE;
153 break;
154
155 case 'x':
156 cflags |= REG_EXTENDED;
157 break;
158
159 default:
160 USAGE_ERROR ((0, 0, _("Unknown flag in transform expression")));
161 }
162
163 /* Extract and compile regex */
164 str = xmalloc (i - 1);
165 memcpy (str, expr + 2, i - 2);
166 str[i - 2] = 0;
167
168 rc = regcomp (&regex, str, cflags);
169
170 if (rc)
171 {
172 char errbuf[512];
173 regerror (rc, &regex, errbuf, sizeof (errbuf));
174 USAGE_ERROR ((0, 0, _("Invalid transform expression: %s"), errbuf));
175 }
176
177 if (str[0] == '^' || str[strlen (str) - 1] == '$')
178 transform_type = transform_first;
179
180 free (str);
181
182 /* Extract and compile replacement expr */
183 i++;
184 str = xmalloc (j - i + 1);
185 memcpy (str, expr + i, j - i);
186 str[j - i] = 0;
187
188 for (cur = beg = str; *cur;)
189 {
190 if (*cur == '\\')
191 {
192 size_t n;
193
194 add_literal_segment (beg, cur);
195 switch (*++cur)
196 {
197 case '0': case '1': case '2': case '3': case '4':
198 case '5': case '6': case '7': case '8': case '9':
199 n = strtoul (cur, &cur, 10);
200 if (n > regex.re_nsub)
201 USAGE_ERROR ((0, 0, _("Invalid transform replacement: back reference out of range")));
202 add_backref_segment (n);
203 break;
204
205 case '\\':
206 add_char_segment ('\\');
207 cur++;
208 break;
209
210 case 'a':
211 add_char_segment ('\a');
212 cur++;
213 break;
214
215 case 'b':
216 add_char_segment ('\b');
217 cur++;
218 break;
219
220 case 'f':
221 add_char_segment ('\f');
222 cur++;
223 break;
224
225 case 'n':
226 add_char_segment ('\n');
227 cur++;
228 break;
229
230 case 'r':
231 add_char_segment ('\r');
232 cur++;
233 break;
234
235 case 't':
236 add_char_segment ('\t');
237 cur++;
238 break;
239
240 case 'v':
241 add_char_segment ('\v');
242 cur++;
243 break;
244
245 case '&':
246 add_char_segment ('&');
247 cur++;
248 break;
249
250 default:
251 /* Try to be nice */
252 {
253 char buf[2];
254 buf[0] = '\\';
255 buf[1] = *cur;
256 add_literal_segment (buf, buf + 2);
257 }
258 cur++;
259 break;
260 }
261 beg = cur;
262 }
263 else if (*cur == '&')
264 {
265 add_literal_segment (beg, cur);
266 add_backref_segment (0);
267 beg = ++cur;
268 }
269 else
270 cur++;
271 }
272 add_literal_segment (beg, cur);
273
274 }
275
276 bool
277 _transform_name_to_obstack (char *input)
278 {
279 regmatch_t *rmp;
280 char *p;
281 int rc;
282
283 if (transform_type == transform_none)
284 return false;
285
286 rmp = xmalloc ((regex.re_nsub + 1) * sizeof (*rmp));
287
288 while (*input)
289 {
290 size_t disp;
291
292 rc = regexec (&regex, input, regex.re_nsub + 1, rmp, 0);
293
294 if (rc == 0)
295 {
296 struct replace_segm *segm;
297
298 disp = rmp[0].rm_eo;
299
300 if (rmp[0].rm_so)
301 obstack_grow (&stk, input, rmp[0].rm_so);
302
303 for (segm = repl_head; segm; segm = segm->next)
304 {
305 switch (segm->type)
306 {
307 case segm_literal: /* Literal segment */
308 obstack_grow (&stk, segm->v.literal.ptr,
309 segm->v.literal.size);
310 break;
311
312 case segm_backref: /* Back-reference segment */
313 if (rmp[segm->v.ref].rm_so != -1
314 && rmp[segm->v.ref].rm_eo != -1)
315 obstack_grow (&stk,
316 input + rmp[segm->v.ref].rm_so,
317 rmp[segm->v.ref].rm_eo - rmp[segm->v.ref].rm_so);
318 break;
319 }
320 }
321 }
322 else
323 {
324 disp = strlen (input);
325 obstack_grow (&stk, input, disp);
326 }
327
328 input += disp;
329
330 if (transform_type == transform_first)
331 {
332 obstack_grow (&stk, input, strlen (input));
333 break;
334 }
335 }
336
337 obstack_1grow (&stk, 0);
338 free (rmp);
339 return true;
340 }
341
342 bool
343 transform_name_fp (char **pinput, char *(*fun)(char *))
344 {
345 char *str, *p;
346 bool ret = _transform_name_to_obstack (*pinput);
347 if (ret)
348 {
349 str = obstack_finish (&stk);
350 assign_string (pinput, fun ? fun (str) : str);
351 obstack_free (&stk, str);
352 }
353 return ret;
354 }
355
356 bool
357 transform_name (char **pinput)
358 {
359 return transform_name_fp (pinput, NULL);
360 }
361
362 #if 0
363 void
364 read_and_transform_loop ()
365 {
366 char buf[512];
367 while (fgets (buf, sizeof buf, stdin))
368 {
369 char *p = buf + strlen (buf);
370 if (p[-1] == '\n')
371 p[-1] = 0;
372 if (transform_name (buf, &p))
373 printf ("=> %s\n", p);
374 else
375 printf ("=\n");
376 }
377 }
378 #endif
This page took 0.051581 seconds and 5 git commands to generate.