]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/breg.c
Backport from Bacula Enterprise
[bacula/bacula] / bacula / src / lib / breg.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2007-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  * Manipulation routines for BREGEXP list
22  *
23  *  Eric Bollengier, March 2007
24  *
25  */
26
27
28 #include "bacula.h"
29
30 #include "breg.h"
31 #include "mem_pool.h"
32
33 BREGEXP *new_bregexp(const char *motif)
34 {
35    Dmsg0(500, "bregexp: creating new bregexp object\n");
36    BREGEXP *self = (BREGEXP *)bmalloc(sizeof(BREGEXP));
37    memset(self, 0, sizeof(BREGEXP));
38
39    if (!self->extract_regexp(motif)) {
40       Dmsg0(100, "bregexp: extract_regexp error\n");
41       free_bregexp(self);
42       return NULL;
43    }
44
45    self->result = get_pool_memory(PM_FNAME);
46    self->result[0] = '\0';
47
48    return self;
49 }
50
51 void free_bregexp(BREGEXP *self)
52 {
53    Dmsg0(500, "bregexp: freeing BREGEXP object\n");
54
55    if (!self) {
56       return;
57    }
58
59    if (self->expr) {
60       bfree(self->expr);
61    }
62    if (self->result) {
63       free_pool_memory(self->result);
64    }
65    regfree(&self->preg);
66    bfree(self);
67 }
68
69 /* Free a bregexps alist
70  */
71 void free_bregexps(alist *bregexps)
72 {
73    Dmsg0(500, "bregexp: freeing all BREGEXP object\n");
74
75    BREGEXP *elt;
76    foreach_alist(elt, bregexps) {
77       free_bregexp(elt);
78    }
79 }
80
81 /* Apply all regexps to fname
82  */
83 bool apply_bregexps(const char *fname, alist *bregexps, char **result)
84 {
85    BREGEXP *elt;
86    bool ok=false;
87
88    char *ret = (char *) fname;
89    foreach_alist(elt, bregexps) {
90       ret = elt->replace(ret);
91       ok = ok || elt->success;
92    }
93    Dmsg2(500, "bregexp: fname=%s ret=%s\n", fname, ret);
94
95    *result = ret;
96    return ok;
97 }
98
99 /* return an alist of BREGEXP or return NULL if it's not a
100  * where=!tmp!opt!ig,!temp!opt!i
101  */
102 alist *get_bregexps(const char *where)
103 {
104    char *p = (char *)where;
105    alist *list = New(alist(10, not_owned_by_alist));
106    BREGEXP *reg;
107
108    reg = new_bregexp(p);
109
110    while(reg) {
111       p = reg->eor;
112       list->append(reg);
113       reg = new_bregexp(p);
114    }
115
116    if (list->size()) {
117       return list;
118    } else {
119       delete list;
120       return NULL;
121    }
122 }
123
124 bool BREGEXP::extract_regexp(const char *motif)
125 {
126    if ( !motif ) {
127       return false;
128    }
129
130    char sep = motif[0];
131
132    if (!(sep == '!' ||
133          sep == ':' ||
134          sep == ';' ||
135          sep == '|' ||
136          sep == ',' ||
137          sep == '&' ||
138          sep == '%' ||
139          sep == '=' ||
140          sep == '~' ||
141          sep == '/' ||
142          sep == '<' ||
143          sep == '#'   ))
144    {
145       return false;
146    }
147
148    char *search = (char *) motif + 1;
149    int options = REG_EXTENDED | REG_NEWLINE;
150    bool ok = false;
151
152    /* extract 1st part */
153    char *dest = expr = bstrdup(motif);
154
155    while (*search && !ok) {
156       if (search[0] == '\\' && search[1] == sep) {
157          *dest++ = *++search;       /* we skip separator */
158
159       } else if (search[0] == '\\' && search[1] == '\\') {
160          *dest++ = *++search;       /* we skip the second \ */
161
162       } else if (*search == sep) {  /* we found end of expression */
163          *dest++ = '\0';
164
165          if (subst) {           /* already have found motif */
166             ok = true;
167
168          } else {
169             *dest++ = *++search; /* we skip separator */
170             subst = dest;        /* get replaced string */
171          }
172
173       } else {
174          *dest++ = *search++;
175       }
176    }
177    *dest = '\0';                /* in case of */
178
179    if (!ok || !subst) {
180       /* bad regexp */
181       return false;
182    }
183
184    ok = false;
185    /* find options */
186    while (*search && !ok) {
187       if (*search == 'i') {
188          options |= REG_ICASE;
189
190       } else if (*search == 'g') {
191               /* recherche multiple*/
192
193       } else if (*search == sep) {
194          /* skip separator */
195
196       } else {                  /* end of options */
197          ok = true;
198       }
199       search++;
200    }
201
202    int rc = regcomp(&preg, expr, options);
203    if (rc != 0) {
204       char prbuf[500];
205       regerror(rc, &preg, prbuf, sizeof(prbuf));
206       Dmsg1(100, "bregexp: compile error: %s\n", prbuf);
207       return false;
208    }
209
210    eor = search;                /* useful to find the next regexp in where */
211
212    return true;
213 }
214
215 /* return regexp->result */
216 char *BREGEXP::replace(const char *fname)
217 {
218    success = false;             /* use this.success to known if it's ok */
219    int flen = strlen(fname);
220    int rc = regexec(&preg, fname, BREG_NREGS, regs, 0);
221
222    if (rc == REG_NOMATCH) {
223       Dmsg0(500, "bregexp: regex mismatch\n");
224       return return_fname(fname, flen);
225    }
226
227    int len = compute_dest_len(fname, regs);
228
229    if (len) {
230       result = check_pool_memory_size(result, len);
231       edit_subst(fname, regs);
232       success = true;
233       Dmsg2(500, "bregexp: len = %i, result_len = %i\n", len, strlen(result));
234
235    } else {                     /* error in substitution */
236       Dmsg0(100, "bregexp: error in substitution\n");
237       return return_fname(fname, flen);
238    }
239
240    return result;
241 }
242
243 char *BREGEXP::return_fname(const char *fname, int len)
244 {
245    result = check_pool_memory_size(result, len+1);
246    strcpy(result,fname);
247    return result;
248 }
249
250 int BREGEXP::compute_dest_len(const char *fname, regmatch_t breg[])
251 {
252    int len=0;
253    char *p;
254    char *psubst = subst;
255    int no;
256
257    if (!fname || !breg) {
258       return 0;
259    }
260
261    /* match failed ? */
262    if (breg[0].rm_so < 0) {
263       return 0;
264    }
265
266    for (p = psubst++; *p ; p = psubst++) {
267       /* match $1 \1 back references */
268       if ((*p == '$' || *p == '\\') && ('0' <= *psubst && *psubst <= '9')) {
269          no = *psubst++ - '0';
270
271          /* we check if the back reference exists */
272          /* references can not match if we are using (..)? */
273
274          if (breg[no].rm_so >= 0 && breg[no].rm_eo >= 0) {
275             len += breg[no].rm_eo - breg[no].rm_so;
276          }
277
278       } else {
279          len++;
280       }
281    }
282
283    /* $0 is replaced by subst */
284    len -= breg[0].rm_eo - breg[0].rm_so;
285    len += strlen(fname) + 1;
286
287    return len;
288 }
289
290 char *BREGEXP::edit_subst(const char *fname, regmatch_t breg[])
291 {
292    int i;
293    char *p;
294    char *psubst = subst;
295    int no;
296    int len;
297
298    /* il faut recopier fname dans dest
299     *  on recopie le debut fname -> breg->start[0]
300     */
301
302    for (i = 0; i < breg[0].rm_so ; i++) {
303       result[i] = fname[i];
304    }
305
306    /* on recopie le motif de remplacement (avec tous les $x) */
307
308    for (p = psubst++; *p ; p = psubst++) {
309       /* match $1 \1 back references */
310       if ((*p == '$' || *p == '\\') && ('0' <= *psubst && *psubst <= '9')) {
311          no = *psubst++ - '0';
312
313          /* have a back reference ? */
314          if (breg[no].rm_so >= 0 && breg[no].rm_eo >= 0) {
315             len = breg[no].rm_eo - breg[no].rm_so;
316             bstrncpy(result + i, fname + breg[no].rm_so, len + 1);
317             i += len ;
318          }
319
320       } else {
321          result[i++] = *p;
322       }
323    }
324
325    /* we copy what is out of the match */
326    strcpy(result + i, fname + breg[0].rm_eo);
327
328    return result;
329 }
330
331 /* escape sep char and \
332  * dest must be long enough (src*2+1)
333  * return end of the string */
334 char *bregexp_escape_string(char *dest, const char *src, const char sep)
335 {
336    char *ret = dest;
337    while (*src)
338    {
339       if (*src == sep) {
340          *dest++ = '\\';
341       } else if (*src == '\\') {
342          *dest++ = '\\';
343       }
344       *dest++ = *src++;
345    }
346    *dest = '\0';
347
348    return ret;
349 }
350
351 static const char regexp_sep = '!';
352 static const char *str_strip_prefix = "!%s!!i";
353 static const char *str_add_prefix   = "!^!%s!";
354 static const char *str_add_suffix   = "!([^/])$!$1%s!";
355
356 int bregexp_get_build_where_size(char *strip_prefix,
357                                  char *add_prefix,
358                                  char *add_suffix)
359 {
360    int str_size = ((strip_prefix?strlen(strip_prefix)+strlen(str_strip_prefix):0) +
361                    (add_prefix?strlen(add_prefix)+strlen(str_add_prefix)      :0) +
362                    (add_suffix?strlen(add_suffix)+strlen(str_add_suffix)      :0) )
363          /* escape + 3*, + \0 */
364             * 2    + 3   + 1;
365
366    Dmsg1(200, "bregexp_get_build_where_size = %i\n", str_size);
367    return str_size;
368 }
369
370 /* build a regexp string with user arguments
371  * Usage :
372  *
373  * int len = bregexp_get_build_where_size(a,b,c) ;
374  * char *dest = (char *) bmalloc (len * sizeof(char));
375  * bregexp_build_where(dest, len, a, b, c);
376  * bfree(dest);
377  *
378  */
379 char *bregexp_build_where(char *dest, int str_size,
380                           char *strip_prefix,
381                           char *add_prefix,
382                           char *add_suffix)
383 {
384    int len=0;
385
386    POOLMEM *str_tmp = get_memory(str_size);
387
388    *str_tmp = *dest = '\0';
389
390    if (strip_prefix) {
391       len += bsnprintf(dest, str_size - len, str_strip_prefix,
392                        bregexp_escape_string(str_tmp, strip_prefix, regexp_sep));
393    }
394
395    if (add_suffix) {
396       if (len) dest[len++] = ',';
397
398       len += bsnprintf(dest + len,  str_size - len, str_add_suffix,
399                        bregexp_escape_string(str_tmp, add_suffix, regexp_sep));
400    }
401
402    if (add_prefix) {
403       if (len) dest[len++] = ',';
404
405       len += bsnprintf(dest + len, str_size - len, str_add_prefix,
406                        bregexp_escape_string(str_tmp, add_prefix, regexp_sep));
407    }
408
409    free_pool_memory(str_tmp);
410
411    return dest;
412 }
413
414
415 void BREGEXP::debug()
416 {
417    printf("expr=[%s]\n", expr);
418    printf("subst=[%s]\n", subst);
419    printf("result=%s\n", NPRT(result));
420 }