]> git.sur5r.net Git - bacula/bacula/commitdiff
ebl Commit file relocation
authorEric Bollengier <eric@eb.homelinux.org>
Sun, 22 Apr 2007 18:16:43 +0000 (18:16 +0000)
committerEric Bollengier <eric@eb.homelinux.org>
Sun, 22 Apr 2007 18:16:43 +0000 (18:16 +0000)
git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@4594 91ce42f0-d328-0410-95d8-f526ca767f89

16 files changed:
bacula/src/dird/dird.h
bacula/src/dird/dird_conf.c
bacula/src/dird/dird_conf.h
bacula/src/dird/restore.c
bacula/src/dird/ua_restore.c
bacula/src/dird/ua_run.c
bacula/src/filed/filed.h
bacula/src/filed/job.c
bacula/src/jcr.h
bacula/src/lib/Makefile.in
bacula/src/lib/attr.c
bacula/src/lib/breg.c [new file with mode: 0644]
bacula/src/lib/breg.h [new file with mode: 0644]
bacula/src/lib/bregex.c
bacula/src/lib/bregex.h
bacula/src/lib/jcr.c

index c0b5393e9227100dd522923f73b8e29b53a49ccb..baab1821c5465a88ca3faeea873d2103e950152e 100644 (file)
@@ -34,6 +34,7 @@
  */
 
 #include "lib/runscript.h"
+#include "lib/breg.h"
 #include "dird_conf.h"
 
 #define DIRECTOR_DAEMON 1
index c18eed8552c5aebcd46a1eefa19b42751236d126..43fe648a4e4440eefe1263dda002ccb810df583a 100644 (file)
@@ -268,6 +268,10 @@ RES_ITEM job_items[] = {
    {"run",       store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
    /* Root of where to restore files */
    {"where",    store_dir,      ITEM(res_job.RestoreWhere), 0, 0, 0},
+   {"whereuseregexp", store_bool, ITEM(res_job.where_use_regexp), 0, 0, 0},
+   {"stripprefix",    store_str,  ITEM(res_job.strip_prefix), 0, 0, 0},
+   {"addprefix",    store_str,  ITEM(res_job.add_prefix), 0, 0, 0},
+   {"addsuffix",    store_str,  ITEM(res_job.add_suffix), 0, 0, 0},
    /* Where to find bootstrap during restore */
    {"bootstrap",store_dir,      ITEM(res_job.RestoreBootstrap), 0, 0, 0},
    /* Where to write bootstrap file during backup */
@@ -611,6 +615,9 @@ void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fm
       if (res->res_job.RestoreWhere) {
          sendit(sock, _("  --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
       }
+      if (res->res_job.where_use_regexp) {
+         sendit(sock, _("  --> RWhere=%u\n"), res->res_job.where_use_regexp);
+      }
       if (res->res_job.RestoreBootstrap) {
          sendit(sock, _("  --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
       }
@@ -1143,6 +1150,15 @@ void free_resource(RES *sres, int type)
       if (res->res_job.RestoreWhere) {
          free(res->res_job.RestoreWhere);
       }
+      if (res->res_job.strip_prefix) {
+         free(res->res_job.strip_prefix);
+      }
+      if (res->res_job.add_prefix) {
+         free(res->res_job.add_prefix);
+      }
+      if (res->res_job.add_suffix) {
+         free(res->res_job.add_suffix);
+      }
       if (res->res_job.RestoreBootstrap) {
          free(res->res_job.RestoreBootstrap);
       }
@@ -1299,6 +1315,25 @@ void save_resource(int type, RES_ITEM *items, int pass)
          res->res_job.jobdefs    = res_all.res_job.jobdefs;
          res->res_job.run_cmds   = res_all.res_job.run_cmds;
          res->res_job.RunScripts = res_all.res_job.RunScripts;
+        if (res->res_job.strip_prefix ||
+            res->res_job.add_suffix   ||
+            res->res_job.add_prefix)
+        {
+           if (res->res_job.RestoreWhere) {
+              free(res->res_job.RestoreWhere);
+           }
+           int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
+                                                  res->res_job.add_prefix,
+                                                  res->res_job.add_suffix);
+           res->res_job.RestoreWhere = (char *) bmalloc (len * sizeof(char));
+           bregexp_build_where(res->res_job.RestoreWhere, len,
+                               res->res_job.strip_prefix,
+                               res->res_job.add_prefix,
+                               res->res_job.add_suffix);
+           res->res_job.where_use_regexp = true;
+
+           /* TODO: test bregexp */
+        }
          break;
       case R_COUNTER:
          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
index 0bb04551586bd71fa5f6b6cf8030e4a0e70e127e..0364f5b5fe52c7f5f9e3749999e269cf18cde37b 100644 (file)
@@ -356,6 +356,10 @@ public:
    int   Priority;                    /* Job priority */
    int   RestoreJobId;                /* What -- JobId to restore */
    char *RestoreWhere;                /* Where on disk to restore -- directory */
+   char *strip_prefix;                /* remove prefix from filename  */
+   char *add_prefix;                  /* add prefix to filename  */
+   char *add_suffix;                  /* add suffix to filename -- .old */
+   bool  where_use_regexp;            /* true if RestoreWhere is a BREGEXP */
    char *RestoreBootstrap;            /* Bootstrap file */
    alist *RunScripts;                 /* Run {client} program {after|before} Job */
    union {
index 186e665580aebbc2e8b823b5805580400fd895bc..cb6eed17cd970822a2fe4a24d141298a14bdcf8d 100644 (file)
@@ -50,8 +50,9 @@
 #include "dird.h"
 
 /* Commands sent to File daemon */
-static char restorecmd[]   = "restore replace=%c prelinks=%d where=%s\n";
-static char storaddr[]     = "storage address=%s port=%d ssl=0\n";
+static char restorecmd[]        = "restore replace=%c prelinks=%d where=%s\n";
+static char restorecmdR[] = "restore replace=%c prelinks=%d rwhere=%s\n";
+static char storaddr[]   = "storage address=%s port=%d ssl=0\n";
 
 /* Responses received from File daemon */
 static char OKrestore[]   = "2000 OK restore\n";
@@ -172,7 +173,7 @@ bool do_restore(JCR *jcr)
    }
 
    /* Send restore command */
-   char replace, *where;
+   char replace, *where, *cmd;
    char empty = '\0';
 
    if (jcr->replace != 0) {
@@ -189,9 +190,17 @@ bool do_restore(JCR *jcr)
    } else {
       where = &empty;                 /* None */
    }
+   
    jcr->prefix_links = jcr->job->PrefixLinks;
+
+   if (jcr->where_use_regexp) {
+      cmd = restorecmdR;
+   } else {
+      cmd = restorecmd;
+   }
+
    bash_spaces(where);
-   bnet_fsend(fd, restorecmd, replace, jcr->prefix_links, where);
+   bnet_fsend(fd, cmd, replace, jcr->prefix_links, where);
    unbash_spaces(where);
 
    if (!response(jcr, fd, OKrestore, "Restore", DISPLAY_ERROR)) {
index b3ec5f420668fbfc6952c708f14b7aab94407911..3c2579a966c1a1c21911f4fba4fdd05738fa74f8 100644 (file)
@@ -44,7 +44,6 @@
 #include "bacula.h"
 #include "dird.h"
 
-
 /* Imported functions */
 extern void print_bsr(UAContext *ua, RBSR *bsr);
 
@@ -83,6 +82,9 @@ int restore_cmd(UAContext *ua, const char *cmd)
    JCR *jcr = ua->jcr;
    char *escaped_bsr_name = NULL;
    char *escaped_where_name = NULL;
+   bool where_use_regexp = false;
+   char *strip_prefix, *add_prefix, *add_suffix, *regexp;
+   strip_prefix = add_prefix = add_suffix = regexp = NULL;
 
    memset(&rx, 0, sizeof(rx));
    rx.path = get_pool_memory(PM_FNAME);
@@ -94,6 +96,45 @@ int restore_cmd(UAContext *ua, const char *cmd)
    i = find_arg_with_value(ua, "where");
    if (i >= 0) {
       rx.where = ua->argv[i];
+   }
+
+   i = find_arg_with_value(ua, "strip_prefix");
+   if (i >= 0) {
+      strip_prefix = ua->argv[i];
+   }
+
+   i = find_arg_with_value(ua, "add_prefix");
+   if (i >= 0) {
+      add_prefix = ua->argv[i];
+   }
+
+   i = find_arg_with_value(ua, "add_suffix");
+   if (i >= 0) {
+      add_suffix = ua->argv[i];
+   }
+
+   i = find_arg(ua, "where_use_regexp");
+   if (i >= 0) {
+      where_use_regexp = true;
+   }
+
+   i = find_arg_with_value(ua, "rwhere");
+   if (i >= 0) {
+      where_use_regexp = true;
+      rx.where = ua->argv[i];
+   }
+
+   if (strip_prefix || add_suffix || add_prefix) {
+      int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
+      regexp = (char *) bmalloc (len * sizeof(char));
+
+      bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
+      where_use_regexp = true;
+      
+      rx.where = regexp;
+   }
+
+   if (rx.where) {
       if (!acl_access_ok(ua, Where_ACL, rx.where)) {
          ua->error_msg(_("\"where\" specification not authorized.\n"));
          goto bail_out;
@@ -195,9 +236,10 @@ int restore_cmd(UAContext *ua, const char *cmd)
 
       Mmsg(ua->cmd,
           "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
-          " where=\"%s\" files=%d catalog=\"%s\"",
+          " %swhere=\"%s\" files=%d catalog=\"%s\"",
           job->name(), rx.ClientName, rx.store?rx.store->name():"",
           escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
+          where_use_regexp ? "r" : "",
           escaped_where_name ? escaped_where_name : rx.where,
           rx.selected_files, ua->catalog->name());
    } else {
@@ -216,6 +258,10 @@ int restore_cmd(UAContext *ua, const char *cmd)
    if (escaped_where_name != NULL) {
       bfree(escaped_where_name);
    }
+   
+   if (regexp) {
+      bfree(regexp);
+   }
 
    if (find_arg(ua, NT_("yes")) > 0) {
       pm_strcat(ua->cmd, " yes");    /* pass it on to the run command */
@@ -235,6 +281,10 @@ bail_out:
       bfree(escaped_where_name);
    }
 
+   if (regexp) {
+      bfree(regexp);
+   }
+
    free_rx(&rx);
    return 0;
 
@@ -333,23 +383,28 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
 
    const char *kw[] = {
        /* These keywords are handled in a for loop */
-      "jobid",     /* 0 */
-      "current",   /* 1 */
-      "before",    /* 2 */
-      "file",      /* 3 */
-      "directory", /* 4 */
-      "select",    /* 5 */
-      "pool",      /* 6 */
-      "all",       /* 7 */
+      "jobid",       /* 0 */
+      "current",     /* 1 */
+      "before",      /* 2 */
+      "file",        /* 3 */
+      "directory",   /* 4 */
+      "select",      /* 5 */
+      "pool",        /* 6 */
+      "all",         /* 7 */
 
       /* The keyword below are handled by individual arg lookups */
-      "client",    /* 8 */
-      "storage",   /* 9 */
-      "fileset",   /* 10 */
-      "where",     /* 11 */
-      "yes",       /* 12 */
-      "bootstrap", /* 13 */
-      "done",      /* 14 */
+      "client",       /* 8 */
+      "storage",      /* 9 */
+      "fileset",      /* 10 */
+      "where",        /* 11 */
+      "yes",          /* 12 */
+      "bootstrap",    /* 13 */
+      "done",         /* 14 */
+      "strip_prefix", /* 15 */
+      "add_prefix",   /* 16 */
+      "add_suffix",   /* 17 */
+      "where_use_regexp",/* 18 */
+      "rwhere",       /* 19 like where + where_use_regexp */
       NULL
    };
 
index 9ddb351f5582dca3d9ad4e114940742ed768ffe3..914b82661701f458220ae41f661ab89d251888a0 100644 (file)
@@ -41,6 +41,7 @@
 static void select_job_level(UAContext *ua, JCR *jcr);
 static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *verify_list, 
    char *jid, const char *replace);
+static void select_where_regexp(UAContext *ua, JCR *jcr);
 
 
 /* Imported variables */
@@ -71,6 +72,7 @@ int run_cmd(UAContext *ua, const char *cmd)
    int Priority = 0;
    int i, j, opt, files = 0;
    bool kw_ok;
+   bool where_use_regexp = false;
    JOB *job = NULL;
    JOB *verify_job = NULL;
    JOB *previous_job = NULL;
@@ -87,7 +89,7 @@ int run_cmd(UAContext *ua, const char *cmd)
       "level",                        /* 5 */
       "storage",                      /* 6 */
       "sd",                           /* 7 */
-      "pool",                         /* 8 */
+      "rwhere",                       /* 8 where string as a bregexp */
       "where",                        /* 9 */
       "bootstrap",                    /* 10 */
       "replace",                      /* 11 */
@@ -101,6 +103,7 @@ int run_cmd(UAContext *ua, const char *cmd)
       "cloned",                       /* 19 cloned */
       "verifylist",                   /* 20 verify output list */
       "migrationjob",                 /* 21 migration job name */
+      "pool",                         /* 22 */
       NULL};
 
 #define YES_POS 14
@@ -188,15 +191,11 @@ int run_cmd(UAContext *ua, const char *cmd)
                store_name = ua->argv[i];
                kw_ok = true;
                break;
-            case 8: /* pool */
-               if (pool_name) {
-                  ua->send_msg(_("Pool specified twice.\n"));
-                  return 0;
-               }
-               pool_name = ua->argv[i];
-               kw_ok = true;
-               break;
+            case 8: /* rwhere */
             case 9: /* where */
+               /* TODO: this is ugly ... */
+               where_use_regexp = (j == 9)?false:true; /* rwhere or where ? */
+
                if (where) {
                   ua->send_msg(_("Where specified twice.\n"));
                   return 0;
@@ -287,7 +286,14 @@ int run_cmd(UAContext *ua, const char *cmd)
                previous_job_name = ua->argv[i];
                kw_ok = true;
                break;
-
+            case 22: /* pool */
+               if (pool_name) {
+                  ua->send_msg(_("Pool specified twice.\n"));
+                  return 0;
+               }
+               pool_name = ua->argv[i];
+               kw_ok = true;
+               break;
 
             default:
                break;
@@ -478,6 +484,7 @@ int run_cmd(UAContext *ua, const char *cmd)
          free(jcr->where);
       }
       jcr->where = bstrdup(where);
+      jcr->where_use_regexp = where_use_regexp;
    }
 
    if (when) {
@@ -595,8 +602,9 @@ try_again:
       } else if (jcr->JobType == JT_RESTORE) {
          add_prompt(ua, _("Bootstrap"));     /* 7 */
          add_prompt(ua, _("Where"));         /* 8 */
-         add_prompt(ua, _("Replace"));       /* 9 */
-         add_prompt(ua, _("JobId"));         /* 10 */
+         add_prompt(ua, _("File Relocation"));/* 9 */   
+         add_prompt(ua, _("Replace"));       /* 10 */
+         add_prompt(ua, _("JobId"));         /* 11 */
       }
       switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
       case 0:
@@ -719,8 +727,13 @@ try_again:
             ua->cmd[0] = 0;
          }
          jcr->where = bstrdup(ua->cmd);
+        jcr->where_use_regexp = false;
          goto try_again;
-      case 9:
+      case 9: 
+        /* File relocation */
+        select_where_regexp(ua, jcr);
+        goto try_again;
+      case 10:
          /* Replace */
          start_prompt(ua, _("Replace:\n"));
          for (i=0; ReplaceOptions[i].name; i++) {
@@ -731,7 +744,7 @@ try_again:
             jcr->replace = ReplaceOptions[opt].token;
          }
          goto try_again;
-      case 10:
+      case 11:
          /* JobId */
          jid = NULL;                  /* force reprompt */
          jcr->RestoreJobId = 0;
@@ -775,6 +788,134 @@ bail_out:
    return 0;                       /* do not run */
 }
 
+static void select_where_regexp(UAContext *ua, JCR *jcr)
+{
+   alist *regs;
+   char *strip_prefix, *add_prefix, *add_suffix, *rwhere;
+   strip_prefix = add_suffix = rwhere = add_prefix = NULL;
+
+try_again_reg:
+   ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s\n"),
+               NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix));
+
+   start_prompt(ua, _("This will replace your current Where value\n"));
+   add_prompt(ua, _("Strip prefix"));                /* 0 */
+   add_prompt(ua, _("Add prefix"));                  /* 1 */
+   add_prompt(ua, _("Add file suffix"));             /* 2 */
+   add_prompt(ua, _("Enter a regexp"));              /* 3 */
+   add_prompt(ua, _("Test filename manipulation"));  /* 4 */
+   add_prompt(ua, _("Use this ?"));                  /* 5 */
+   
+   switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) {
+   case 0:
+      /* Strip prefix */
+      if (get_cmd(ua, _("Please enter path prefix to strip: "))) {
+        if (strip_prefix) bfree(strip_prefix);
+        strip_prefix = bstrdup(ua->cmd);
+      }
+      
+      goto try_again_reg;
+   case 1:
+      /* Add prefix */
+      if (get_cmd(ua, _("Please enter path prefix to add (/ for none): "))) {
+        if (IsPathSeparator(ua->cmd[0]) && ua->cmd[1] == '\0') {
+           ua->cmd[0] = 0;
+        }
+
+        if (add_prefix) bfree(add_prefix);
+        add_prefix = bstrdup(ua->cmd);
+      }
+      goto try_again_reg;
+   case 2:
+      /* Add suffix */
+      if (get_cmd(ua, _("Please enter file suffix to add: "))) {
+        if (add_suffix) bfree(add_suffix);
+        add_suffix = bstrdup(ua->cmd);
+      }      
+      goto try_again_reg;
+   case 3:
+      /* Add rwhere */
+      if (get_cmd(ua, _("Please enter a valid regexp (!from!to!): "))) {
+        if (rwhere) bfree(rwhere);
+        rwhere = bstrdup(ua->cmd);
+      }
+      
+      goto try_again_reg;      
+   case 4:
+      /* Test regexp */ 
+      char *result;
+      char *regexp;
+      
+      if (rwhere && rwhere[0] != '\0') {
+        regs = get_bregexps(rwhere);
+        ua->send_msg(_("rwhere=%s\n"), NPRT(rwhere));
+      } else {
+        int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
+        regexp = (char *) bmalloc (len * sizeof(char));
+        bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
+        regs = get_bregexps(regexp);
+        ua->send_msg(_("strip_prefix=%s add_prefix=%s add_suffix=%s result=%s\n"),
+                     NPRT(strip_prefix), NPRT(add_prefix), NPRT(add_suffix), NPRT(regexp));
+        
+        bfree(regexp);
+      }
+
+      if (!regs) {
+        ua->send_msg(_("Cannot use your regexp\n"));
+        goto try_again_reg;
+      }
+
+      while (get_cmd(ua, _("Please enter filename to test: "))) {
+        apply_bregexps(ua->cmd, regs, &result);
+        ua->send_msg(_("%s -> %s\n"), ua->cmd, result);
+      }
+      free_bregexps(regs);
+      delete regs;
+      goto try_again_reg;
+
+   case 5:
+      /* OK */
+      break;
+   case -1:                        /* error or cancel */
+      goto bail_out_reg;
+   default:
+      goto try_again_reg;
+   }
+
+   /* replace the existing where */
+   if (jcr->where) {
+      bfree(jcr->where);
+      jcr->where = NULL;
+   }
+
+   if (rwhere) {
+      jcr->where = bstrdup(rwhere);
+   } else if (strip_prefix || add_prefix || add_suffix) {
+      int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
+      jcr->where = (char *) bmalloc(len*sizeof(char));
+      bregexp_build_where(jcr->where, len, strip_prefix, add_prefix, add_suffix);
+   }
+
+   regs = get_bregexps(jcr->where);
+   if (regs) {
+      free_bregexps(regs);
+      delete regs;
+      jcr->where_use_regexp = true;
+   } else {
+      if (jcr->where) {
+        bfree(jcr->where);
+        jcr->where = NULL;
+      }
+      ua->send_msg(_("Cannot use your regexp.\n"));
+   }
+
+bail_out_reg:
+   if (strip_prefix) bfree(strip_prefix);
+   if (add_prefix)   bfree(add_prefix);
+   if (add_suffix)   bfree(add_suffix);
+   if (rwhere)       bfree(rwhere);
+}
+
 static void select_job_level(UAContext *ua, JCR *jcr)
 {
    if (jcr->JobType == JT_BACKUP) {
@@ -938,7 +1079,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *veri
          ua->send_msg(_("Run Restore job\n"
                         "JobName:    %s\n"
                         "Bootstrap:  %s\n"
-                        "Where:      %s\n"
+                        "%s     %s\n"             /* Where or RWhere */
                         "Replace:    %s\n"
                         "FileSet:    %s\n"
                         "Client:     %s\n"
@@ -948,6 +1089,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *veri
                         "Priority:   %d\n"),
               job->name(),
               NPRT(jcr->RestoreBootstrap),
+              jcr->where_use_regexp?"RWhere:":"Where: ",
               jcr->where?jcr->where:NPRT(job->RestoreWhere),
               replace,
               jcr->fileset->name(),
@@ -961,7 +1103,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *veri
          ua->send_msg(_("Run Restore job\n"
                        "JobName:    %s\n"
                        "Bootstrap:  %s\n"
-                       "Where:      %s\n"
+                       "%s     %s\n"              /* Where or RWhere */
                        "Replace:    %s\n"
                        "Client:     %s\n"
                        "Storage:    %s\n"
@@ -971,6 +1113,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, char *veri
                        "Priority:   %d\n"),
               job->name(),
               NPRT(jcr->RestoreBootstrap),
+              jcr->where_use_regexp?"RWhere:":"Where: ",
               jcr->where?jcr->where:NPRT(job->RestoreWhere),
               replace,
               jcr->client->name(),
index 67ee9b3c186035d02195ff93279edaf1514367bb..56cc79c431f97235dd49a7ee62c03e5f09897b0e 100644 (file)
@@ -40,6 +40,7 @@
 #include "acl.h"
 #include "protos.h"                   /* file daemon prototypes */
 #include "lib/runscript.h"
+#include "lib/breg.h"
 #ifdef HAVE_LIBZ
 #include <zlib.h>                     /* compression headers */
 #else
index 13b094ec263345bd4f6e9098adb6d32d3352d962..55ec529734a63334c043da810f148b8d50eacdce 100644 (file)
@@ -115,6 +115,7 @@ static char storaddr[]    = "storage address=%s port=%d ssl=%d";
 static char sessioncmd[]  = "session %127s %ld %ld %ld %ld %ld %ld\n";
 static char restorecmd[]  = "restore replace=%c prelinks=%d where=%s\n";
 static char restorecmd1[] = "restore replace=%c prelinks=%d where=\n";
+static char restorecmdR[] = "restore replace=%c prelinks=%d rwhere=%s\n";
 static char verifycmd[]   = "verify level=%30s";
 static char estimatecmd[] = "estimate listing=%d";
 static char runbefore[]   = "RunBeforeJob %s";
@@ -1586,12 +1587,15 @@ static int restore_cmd(JCR *jcr)
    *where = 0;
 
    if (sscanf(dir->msg, restorecmd, &replace, &prefix_links, where) != 3) {
-      if (sscanf(dir->msg, restorecmd1, &replace, &prefix_links) != 2) {
-         pm_strcpy(jcr->errmsg, dir->msg);
-         Jmsg(jcr, M_FATAL, 0, _("Bad replace command. CMD=%s\n"), jcr->errmsg);
-         return 0;
+      if (sscanf(dir->msg, restorecmdR, &replace, &prefix_links, where) != 3){
+         if (sscanf(dir->msg, restorecmd1, &replace, &prefix_links) != 2) {
+            pm_strcpy(jcr->errmsg, dir->msg);
+            Jmsg(jcr, M_FATAL, 0, _("Bad replace command. CMD=%s\n"), jcr->errmsg);
+            return 0;
+         }
+         *where = 0;
       }
-      *where = 0;
+      jcr->where_use_regexp = true;
    }
    /* Turn / into nothing */
    if (IsPathSeparator(where[0]) && where[1] == '\0') {
@@ -1601,6 +1605,15 @@ static int restore_cmd(JCR *jcr)
    Dmsg2(150, "Got replace %c, where=%s\n", replace, where);
    unbash_spaces(where);
    jcr->where = bstrdup(where);
+
+   if (jcr->where_use_regexp) {
+      jcr->where_bregexp = get_bregexps(jcr->where);
+      if (!jcr->where_bregexp) {
+        Jmsg(jcr, M_FATAL, 0, _("Bad where regexp. where=%s\n"), jcr->where);
+        free_pool_memory(where);
+        return 0;
+      }
+   }
    free_pool_memory(where);
    jcr->replace = replace;
    jcr->prefix_links = prefix_links;
index ff06afc9b2c6e567704ef8652a175e57973c751f..c07e305e8a6d30f42f0dc962fc23773aab205416 100644 (file)
@@ -173,6 +173,8 @@ public:
    MSGS *jcr_msgs;                    /* Copy of message resource -- actually used */
    uint32_t ClientId;                 /* Client associated with Job */
    char *where;                       /* prefix to restore files to */
+   bool where_use_regexp;             /* True if where is a bregexp */
+   alist *where_bregexp;              /* BREGEXP alist for path manipulation */
    int cached_pnl;                    /* cached path length */
    POOLMEM *cached_path;              /* cached path */
    bool prefix_links;                 /* Prefix links with Where path */
index 683eed556b0b2b6a34a8f7fe40d97a4ac338392a..166fba57d85e6e80410e7ed496b9b68888988093 100644 (file)
@@ -32,7 +32,7 @@ LIBSRCS = attr.c base64.c berrno.c bsys.c bget_msg.c \
          res.c rwlock.c scan.c serial.c sha1.c \
          signal.c smartall.c rblist.c tls.c tree.c \
          util.c var.c watchdog.c workq.c btimers.c \
-         address_conf.c pythonlib.c
+         address_conf.c pythonlib.c breg.c
 
 
 LIBOBJS = attr.o base64.o berrno.o bsys.o bget_msg.o \
@@ -45,7 +45,7 @@ LIBOBJS = attr.o base64.o berrno.o bsys.o bget_msg.o \
          res.o rwlock.o scan.o serial.o sha1.o \
          signal.o smartall.o rblist.o tls.o tree.o \
          util.o var.o watchdog.o workq.o btimers.o \
-         address_conf.o pythonlib.o
+         address_conf.o pythonlib.o breg.o
 
 
 EXTRAOBJS = @OBJLIST@
index 47a5ee1b9ed172652bc333dd6122b45f11a69394..138dad8ad4b2b61288c5051f2bb3a53d610af472 100644 (file)
@@ -35,7 +35,7 @@
 
 #include "bacula.h"
 #include "jcr.h"
-
+#include "lib/breg.h"
 
 ATTR *new_attr()
 {
@@ -148,9 +148,30 @@ void build_attr_output_fnames(JCR *jcr, ATTR *attr)
     *   every filename if a prefix is supplied.
     *
     */
+
    if (jcr->where[0] == 0) {
       pm_strcpy(attr->ofname, attr->fname);
       pm_strcpy(attr->olname, attr->lname);
+
+   } else if (jcr->where_bregexp) { 
+      char *ret;
+      apply_bregexps(attr->fname, jcr->where_bregexp, &ret);
+      pm_strcpy(attr->ofname, ret);
+
+      if (attr->type == FT_LNKSAVED || attr->type == FT_LNK) {
+         /* Always add prefix to hard links (FT_LNKSAVED) and
+          *  on user request to soft links
+          */
+
+         if ((attr->type == FT_LNKSAVED || jcr->prefix_links)) {
+            apply_bregexps(attr->lname, jcr->where_bregexp, &ret);
+            pm_strcpy(attr->olname, ret);
+
+         } else {
+            pm_strcpy(attr->olname, attr->lname);
+         }
+      }
+      
    } else {
       const char *fn;
       int wherelen = strlen(jcr->where);
diff --git a/bacula/src/lib/breg.c b/bacula/src/lib/breg.c
new file mode 100644 (file)
index 0000000..6c9a4ef
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * Manipulation routines for BREGEXP list
+ *
+ *  Eric Bollengier, March 2007
+ *
+ *  Version $Id$
+ *
+ */
+/*
+   Bacula\81Â\81® - The Network Backup Solution
+
+   Copyright (C) 2006-2006 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version two of the GNU General Public
+   License as published by the Free Software Foundation plus additions
+   that are listed in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula\81Â\81® is a registered trademark of John Walker.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Z\81Ã\81¼rich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+
+
+#include "bacula.h"
+
+#include "breg.h"
+#include "mem_pool.h"
+
+BREGEXP *new_bregexp(const char *motif)
+{
+   Dmsg0(500, "bregexp: creating new bregexp object\n");
+   BREGEXP *self = (BREGEXP *)bmalloc(sizeof(BREGEXP));
+   memset(self, 0, sizeof(BREGEXP));
+   
+   if (!self->extract_regexp(motif)) {
+      Dmsg0(100, "bregexp: extract_regexp error\n");
+      free_bregexp(self);
+      return NULL;
+   }
+
+   self->result = get_pool_memory(PM_FNAME);
+   self->result[0] = '\0';
+
+#ifdef HAVE_REGEX_H
+   /* TODO: que devient cette memoire... */
+   self->_regs_match = (int *) bmalloc (2*RE_NREGS * sizeof(int));
+
+   self->regs.num_regs = RE_NREGS;
+   self->regs.start = self->_regs_match;
+   self->regs.end   = self->_regs_match+(RE_NREGS * sizeof(int));
+#endif 
+
+   return self;
+}
+
+void free_bregexp(BREGEXP *self)
+{
+   Dmsg0(500, "bregexp: freeing BREGEXP object\n");
+
+   if (!self) {
+      return;
+   }
+
+   if (self->expr) {
+      bfree(self->expr);
+   }
+   if (self->result) {
+      free_pool_memory(self->result);
+   }
+   if (self->_regs_match) {
+      bfree(self->_regs_match);
+   }
+
+   regfree(&self->preg);
+   bfree(self);
+}
+
+/* Free a bregexps alist
+ */
+void free_bregexps(alist *bregexps)
+{
+   Dmsg0(500, "bregexp: freeing all BREGEXP object\n");
+
+   BREGEXP *elt;
+   foreach_alist(elt, bregexps) {
+      free_bregexp(elt);
+   }
+}
+
+/* Apply all regexps to fname
+ */
+bool apply_bregexps(const char *fname, alist *bregexps, char **result)
+{
+   BREGEXP *elt;
+   bool ok=false;
+
+   char *ret = (char *) fname;
+   foreach_alist(elt, bregexps) {
+      ret = elt->replace(ret);
+      ok = ok || elt->success;
+   }
+   Dmsg2(500, "bregexp: fname=%s ret=%s\n", fname, ret);
+
+   *result = ret;
+   return ok;
+}
+
+/* return an alist of BREGEXP or return NULL if it's not a 
+ * where=!tmp!opt!ig,!temp!opt!i
+ */
+alist *get_bregexps(const char *where)
+{
+   char *p = (char *)where;
+   alist *list = New(alist(10, not_owned_by_alist));
+   BREGEXP *reg;
+
+   reg = new_bregexp(p);
+
+   while(reg) {
+      p = reg->eor;
+      list->append(reg);
+      reg = new_bregexp(p);
+   }
+
+   if (list->size()) {
+      return list;      
+   } else {
+      delete list;
+      return NULL;
+   }
+}
+
+bool BREGEXP::extract_regexp(const char *motif)
+{
+   if ( !motif ) {
+      return false;
+   }
+   
+   char sep = motif[0];
+
+   if (!(sep == '!' || 
+        sep == ':' || 
+        sep == ';' || 
+        sep == '|' || 
+        sep == ',' || 
+        sep == '&' || 
+        sep == '%' || 
+        sep == '=' || 
+        sep == '~' ||
+        sep == '/' ||
+        sep == '#'   )) 
+   {
+      return false;
+   }
+
+   char *search = (char *) motif + 1;
+   int options = REG_EXTENDED | REG_NEWLINE;
+   bool ok = false;
+
+   /* extract 1st part */
+   char *dest = expr = bstrdup(motif);
+
+   while (*search && !ok) {
+      if (search[0] == '\\' && search[1] == sep) {
+        *dest++ = *++search;       /* we skip separator */ 
+
+      } else if (search[0] == '\\' && search[1] == '\\') {
+        *dest++ = *++search;       /* we skip the second \ */
+
+      } else if (*search == sep) {  /* we found end of expression */
+        *dest++ = '\0';
+
+        if (subst) {           /* already have found motif */
+           ok = true;
+
+        } else {
+           *dest++ = *++search; /* we skip separator */ 
+           subst = dest;        /* get replaced string */
+        }
+
+      } else {
+        *dest++ = *search++;
+      }
+   }
+   *dest = '\0';               /* in case of */
+   
+   if (!ok || !subst) {
+      /* bad regexp */
+      return false;
+   }
+
+   ok = false;
+   /* find options */
+   while (*search && !ok) {
+      if (*search == 'i') {
+        options |= REG_ICASE;
+
+      } else if (*search == 'g') {
+             /* recherche multiple*/
+
+      } else if (*search == sep) {
+        /* skip separator */
+
+      } else {                 /* end of options */
+        ok = true;
+      }
+      search++;
+   }
+
+   int rc = regcomp(&preg, expr, options);
+   if (rc != 0) {
+      char prbuf[500];
+      regerror(rc, &preg, prbuf, sizeof(prbuf));
+      Dmsg1(100, "bregexp: compile error: %s\n", prbuf);
+      return false;
+   }
+
+   eor = search;               /* useful to find the next regexp in where */
+
+   return true;
+}
+
+#ifndef HAVE_REGEX_H
+ #define BREGEX_CAST unsigned
+#else
+ #define BREGEX_CAST const
+#endif
+
+/* return regexp->result */
+char *BREGEXP::replace(const char *fname)
+{
+   success = false;            /* use this.success to known if it's ok */
+   int flen = strlen(fname);
+#ifndef HAVE_REGEX_H
+   int i;
+   check_pool_memory_size(lcase, flen);
+   for(i=0; fname[i] ; i++) {
+      lcase[i] = tolower(fname[i]);
+   }
+   lcase[i] = '\0';
+   int rc = re_search(&preg, (BREGEX_CAST char*) lcase, flen, 0, flen, &regs);
+#else
+   int rc = re_search(&preg, (BREGEX_CAST char*) fname, flen, 0, flen, &regs);
+#endif
+
+   if (rc < 0) {
+      Dmsg0(500, "bregexp: regex mismatch\n");
+      return return_fname(fname, flen);
+   }
+
+   int len = compute_dest_len(fname, &regs);
+
+   if (len) {
+      result = check_pool_memory_size(result, len);
+      edit_subst(fname, &regs);
+      success = true;
+      Dmsg2(500, "bregexp: len = %i, result_len = %i\n", len, strlen(result));
+
+   } else {                    /* error in substitution */
+      Dmsg0(100, "bregexp: error in substitution\n");
+      return return_fname(fname, flen);
+   }
+
+   return result;
+} 
+
+char *BREGEXP::return_fname(const char *fname, int len)
+{
+   result = check_pool_memory_size(result, len+1);
+   strcpy(result,fname);
+   return result;
+}
+
+int BREGEXP::compute_dest_len(const char *fname, struct re_registers *regs)
+{
+   int len=0;
+   char *p;
+   char *psubst = subst;
+   int no;
+
+   if (!fname || !regs) {
+      return 0;
+   }
+
+   /* match failed ? */
+   if (regs->start[0] < 0) {
+      return 0;
+   }
+
+   for (p = psubst++; *p ; p = psubst++) {
+      /* match $1 \1 back references */
+      if ((*p == '$' || *p == '\\') && ('0' <= *psubst && *psubst <= '9')) {
+        no = *psubst++ - '0';
+
+        /* we check if the back reference exists */
+        /* references can not match if we are using (..)? */
+
+        if (regs->start[no] >= 0 && regs->end[no] >= 0) { 
+           len += regs->end[no] - regs->start[no];
+        }
+        
+      } else {
+        len++;
+      }
+   }
+
+   /* $0 is replaced by subst */
+   len -= regs->end[0] - regs->start[0];
+   len += strlen(fname) + 1;
+
+   return len;
+}
+
+char *BREGEXP::edit_subst(const char *fname, struct re_registers *regs)
+{
+   int i;
+   char *p;
+   char *psubst = subst;
+   int no;
+   int len;
+
+   /* il faut recopier fname dans dest
+    *  on recopie le debut fname -> regs->start[0]
+    */
+   
+   for (i = 0; i < regs->start[0] ; i++) {
+      result[i] = fname[i];
+   }
+
+   /* on recopie le motif de remplacement (avec tous les $x) */
+
+   for (p = psubst++; *p ; p = psubst++) {
+      /* match $1 \1 back references */
+      if ((*p == '$' || *p == '\\') && ('0' <= *psubst && *psubst <= '9')) {
+        no = *psubst++ - '0';
+
+        /* have a back reference ? */
+        if (regs->start[no] >= 0 && regs->end[no] >= 0) {
+           len = regs->end[no] - regs->start[no];
+           bstrncpy(result + i, fname + regs->start[no], len + 1);
+           i += len ;
+        }
+
+      } else {
+        result[i++] = *p;
+      }
+   }
+
+   /* we copy what is out of the match */
+   strcpy(result + i, fname + regs->end[0]);
+
+   return result;
+}
+
+/* escape sep char and \
+ * dest must be long enough (src*2+1)
+ * return end of the string */
+char *bregexp_escape_string(char *dest, char *src, char sep)
+{
+   char *ret = dest;
+   while (*src)
+   {
+      if (*src == sep) {
+        *dest++ = '\\';
+      } else if (*src == '\\') {
+        *dest++ = '\\';
+      }
+      *dest++ = *src++;
+   }
+   *dest = '\0';
+
+   return ret; 
+}
+
+static char regexp_sep = '!';
+static char *str_strip_prefix = "!%s!!i";
+static char *str_add_prefix   = "!^!%s!";
+static char *str_add_suffix   = "!([^/])$!$1%s!";
+
+int bregexp_get_build_where_size(char *strip_prefix, 
+                                char *add_prefix, 
+                                char *add_suffix)
+{
+   int str_size = ((strip_prefix?strlen(strip_prefix)+strlen(str_strip_prefix):0) +
+                  (add_prefix?strlen(add_prefix)+strlen(str_add_prefix)      :0) +  
+                  (add_suffix?strlen(add_suffix)+strlen(str_add_suffix)      :0) )
+         /* escape + 3*, + \0 */
+           * 2    + 3   + 1;
+
+   Dmsg1(1, "bregexp_get_build_where_size = %i\n", str_size);
+   return str_size;
+}
+
+/* build a regexp string with user arguments
+ * Usage :
+ * 
+ * int len = bregexp_get_build_where_size(a,b,c) ;
+ * char *dest = (char *) bmalloc (len * sizeof(char));
+ * bregexp_build_where(dest, len, a, b, c);
+ * bfree(dest);
+ * 
+ */
+char *bregexp_build_where(char *dest, int str_size,
+                         char *strip_prefix, 
+                          char *add_prefix, 
+                          char *add_suffix)
+{
+   int len=0;
+
+   POOLMEM *str_tmp = get_memory(str_size);
+
+   *str_tmp = *dest = '\0';
+   
+   if (strip_prefix) {
+      len += bsnprintf(dest, str_size - len, str_strip_prefix,
+                      bregexp_escape_string(str_tmp, strip_prefix, regexp_sep));
+   }
+
+   if (add_suffix) {
+      if (len) dest[len++] = ',';
+
+      len += bsnprintf(dest + len,  str_size - len, str_add_suffix,
+                      bregexp_escape_string(str_tmp, add_suffix, regexp_sep));
+   }
+
+   if (add_prefix) {
+      if (len) dest[len++] = ',';
+
+      len += bsnprintf(dest + len, str_size - len, str_add_prefix, 
+                      bregexp_escape_string(str_tmp, add_prefix, regexp_sep));
+   }
+
+   free_pool_memory(str_tmp);
+
+   return dest;
+}
+
+
+void BREGEXP::debug()
+{
+   printf("expr=[%s]\n", expr);
+   printf("subst=[%s]\n", subst);
+   printf("result=%s\n", NPRT(result));
+}
diff --git a/bacula/src/lib/breg.h b/bacula/src/lib/breg.h
new file mode 100644 (file)
index 0000000..fa639f1
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Bacula BREGEXP Structure definition for FileDaemon
+ * Eric Bollengier March 2007
+ * Version $Id$
+ */
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2006-2006 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version two of the GNU General Public
+   License as published by the Free Software Foundation plus additions
+   that are listed in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of John Walker.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+
+
+#ifndef __BREG_H_
+#define __BREG_H_ 1
+
+//#undef HAVE_REGEX_H
+
+#ifndef HAVE_REGEX_H
+#include "bregex.h"
+#else
+#include <regex.h>
+#endif
+
+/* Usage:
+ *
+ * #include "lib/breg.h"
+ * 
+ * BREGEXP *breg = new_bregexp("!/prod!/test!");
+ * char *filename = breg->replace("/prod/data.dat");
+ *   or
+ * char *filename = breg->result;
+ * free_bregexp(breg);
+ */
+
+/*
+ * Structure for BREGEXP ressource
+ */
+class BREGEXP {
+public:
+   POOLMEM *result;            /* match result */
+   bool success;               /* match is ok */
+
+   char *replace(const char *fname); /* return this.result */
+   void debug();
+
+   /* private */
+   POOLMEM *expr;              /* search epression */
+   POOLMEM *subst;             /* substitution */
+   POOLMEM *lcase;             /* fname in lower case when using bregexp.c */
+   regex_t preg;               /* regex_t result of regcomp() */
+   struct re_registers regs;   /* contains match */
+   char *eor;                  /* end of regexp in expr */
+
+   int *_regs_match;
+   
+   char *return_fname(const char *fname, int len); /* return fname as result */
+   char *edit_subst(const char *fname, struct re_registers *regs);
+   int compute_dest_len(const char *fname, struct re_registers *regs);
+   bool extract_regexp(const char *motif);
+};
+
+/* create new BREGEXP and compile regex_t */
+BREGEXP *new_bregexp(const char *motif); 
+
+/* launch each bregexp on filename */
+int run_bregexp(alist *bregexps, const char *fname); 
+
+/* free BREGEXP (and all POOLMEM) */
+void free_bregexp(BREGEXP *script);
+
+/* fill an alist with BREGEXP from where */
+alist *get_bregexps(const char *where);
+
+/* apply every regexps from the alist */
+bool apply_bregexps(const char *fname, alist *bregexps, char **result);
+
+/* foreach_alist free RUNSCRIPT */
+void free_bregexps(alist *bregexps); /* you have to free alist */
+
+/* get regexp size */
+int bregexp_get_build_where_size(char *strip_prefix, 
+                                char *add_prefix, 
+                                char *add_suffix);
+
+/* get a bregexp string from user arguments 
+ * you must allocate it with bregexp_get_build_where_size();
+ */
+char *bregexp_build_where(char *dest, int str_size,
+                         char *strip_prefix, 
+                          char *add_prefix, 
+                          char *add_suffix);
+
+/* escape a string to regexp format (sep and \) 
+ * dest must be long enough (dest = 2*src + 1)
+ */
+char *bregexp_escape_string(char *dest, char *src, char sep);
+
+#endif /* __BREG_H_ */
index aa8b80244d66c3a4ae31031f83a1563c38e47c89..9ded1b67be35535cf74fb066b84dce386b5c5ec5 100644 (file)
@@ -1459,6 +1459,10 @@ if (translate) \
 int regcomp(regex_t * bufp, const char *regex, int cflags)
 {
    memset(bufp, 0, sizeof(regex_t));
+   bufp->cflags = cflags;
+   if (bufp->cflags & REG_ICASE) {
+      // ICI passer regex en lcase
+   }
    re_compile_pattern(bufp, (unsigned char *)regex);
    if (got_error) {
       return -1;
@@ -1471,6 +1475,7 @@ int regexec(regex_t * preg, const char *string, size_t nmatch,
 {
    int stat;
    int len = strlen(string);
+   // ICI passer string en lcase
    struct re_registers regs;
    stat = re_search(preg, (unsigned char *)string, len, 0, len, &regs);
    /* stat is the start position in the string base 0 where       
index ed5cb1f37983dbbbc10f12d671f0785d40bf489d..8c7778d1c357472c5e2388548d2b7907ba36a34e 100644 (file)
@@ -106,6 +106,8 @@ struct regex_t
    int num_registers;     /* number of registers used */
    unsigned char anchor;           /* anchor: 0=none 1=begline 2=begbuf */
    char *errmsg;
+   int cflags;                    /* compilation flags */
+   POOLMEM *str_lcase;            /* use to store expression in lcase */
 };        
 
 
index 982f4d0a9b37f3cf3434781ffaf7fdf61fdcf27d..76c164fa375072d2352cffbf130f0029bc5aef6c 100644 (file)
@@ -56,6 +56,9 @@
 /* External variables we reference */
 extern time_t watchdog_time;
 
+/* External referenced functions */
+void free_bregexps(alist *bregexps);
+
 /* Forward referenced functions */
 extern "C" void timeout_handler(int sig);
 static void jcr_timeout_check(watchdog_t *self);
@@ -381,6 +384,11 @@ static void free_common_jcr(JCR *jcr)
       free(jcr->where);
       jcr->where = NULL;
    }
+   if (jcr->where_bregexp) {
+      free_bregexps(jcr->where_bregexp);
+      delete jcr->where_bregexp;
+      jcr->where_bregexp = NULL;
+   }
    if (jcr->cached_path) {
       free_pool_memory(jcr->cached_path);
       jcr->cached_path = NULL;