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