Imported Upstream version 3.2.0
[debian/amanda] / client-src / client_util.c
index 7765532e1e0dd525729736246d271d767a574df9..5dc4974f8f31dd586f8f2f79468af9eb53e4a8ab 100644 (file)
 #include "client_util.h"
 #include "getfsent.h"
 #include "util.h"
+#include "glib-util.h"
 #include "timestamp.h"
 #include "pipespawn.h"
+#include "amxml.h"
+#include "glob.h"
+#include "clock.h"
+#include "amandates.h"
 
 #define MAXMAXDUMPS 16
 
@@ -116,7 +121,7 @@ build_name(
        /*NOTREACHED*/
     }
     test_name = get_name(diskname, exin,
-                        curtime - (AMANDA_DEBUG_DAYS * 24 * 60 * 60), 0);
+                        curtime - (getconf_int(CNF_DEBUG_DAYS) * 24 * 60 * 60), 0);
     match_len = strlen(get_pname()) + strlen(diskname) + 2;
     while((entry = readdir(d)) != NULL) {
        if(is_dot_or_dotdot(entry->d_name)) {
@@ -210,7 +215,8 @@ add_include(
     int nb_exp=0;
     char *quoted, *file;
 
-    (void)disk;        /* Quiet unused parameter warning */
+    (void)disk;                /* Quiet unused parameter warning */
+    (void)device;      /* Quiet unused parameter warning */
 
     l = strlen(ainc);
     if(ainc[l-1] == '\n') {
@@ -227,8 +233,11 @@ add_include(
     }
     else {
        char *incname = ainc+2;
+       int set_root;
 
-       if(strchr(incname, '/')) {
+        set_root = set_root_privs(1);
+       /* Take as is if not root && many '/' */
+       if(!set_root && strchr(incname, '/')) {
             file = quoted = quote_string(ainc);
            if (*file == '"') {
                file[strlen(file) - 1] = '\0';
@@ -239,40 +248,34 @@ add_include(
            nb_exp++;
        }
        else {
-           char *regex;
-           DIR *d;
-           struct dirent *entry;
-
-           regex = glob_to_regex(incname);
-           if((d = opendir(device)) == NULL) {
-               quoted = quote_string(device);
-               dbprintf(_("Can't open disk %s\n"), quoted);
-               if(verbose) {
-                   g_printf(_("ERROR [Can't open disk %s]\n"), quoted);
-               }
-               amfree(quoted);
+           int nb;
+           glob_t globbuf;
+           char *cwd;
+
+           globbuf.gl_offs = 0;
+
+           cwd = g_get_current_dir();
+           if (chdir(device) != 0) {
+               error(_("Failed to chdir(%s): %s\n"), device, strerror(errno));
            }
-           else {
-               while((entry = readdir(d)) != NULL) {
-                   if(is_dot_or_dotdot(entry->d_name)) {
-                       continue;
-                   }
-                   if(match(regex, entry->d_name)) {
-                       incname = vstralloc("./", entry->d_name, NULL);
-                       file = quoted = quote_string(incname);
-                       if (*file == '"') {
-                           file[strlen(file) - 1] = '\0';
-                           file++;
-                       }
-                       g_fprintf(file_include, "%s\n", file);
-                       amfree(quoted);
-                       amfree(incname);
-                       nb_exp++;
-                   }
+           glob(incname, 0, NULL, &globbuf);
+           if (chdir(cwd) != 0) {
+               error(_("Failed to chdir(%s): %s\n"), cwd, strerror(errno));
+           }
+           if (set_root)
+               set_root_privs(0);
+           nb_exp = globbuf.gl_pathc;
+           for (nb=0; nb < nb_exp; nb++) {
+               file = stralloc2("./", globbuf.gl_pathv[nb]);
+               quoted = quote_string(file);
+               if (*file == '"') {
+                   file[strlen(file) - 1] = '\0';
+                   file++;
                }
-               closedir(d);
+               g_fprintf(file_include, "%s\n", file);
+               amfree(quoted);
+               amfree(file);
            }
-           amfree(regex);
        }
     }
     return nb_exp;
@@ -280,10 +283,8 @@ add_include(
 
 char *
 build_exclude(
-    char *     disk,
-    char *     device,
-    option_t * options,
-    int                verbose)
+    dle_t   *dle,
+    int             verbose)
 {
     char *filename;
     FILE *file_exclude;
@@ -293,26 +294,26 @@ build_exclude(
     int nb_exclude = 0;
     char *quoted;
 
-    if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
-    if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
+    if (dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
+    if (dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
 
-    if(nb_exclude == 0) return NULL;
+    if (nb_exclude == 0) return NULL;
 
-    if((filename = build_name(disk, "exclude", verbose)) != NULL) {
-       if((file_exclude = fopen(filename,"w")) != NULL) {
+    if ((filename = build_name(dle->disk, "exclude", verbose)) != NULL) {
+       if ((file_exclude = fopen(filename,"w")) != NULL) {
 
-           if(options->exclude_file) {
-               for(excl = options->exclude_file->first; excl != NULL;
+           if (dle->exclude_file) {
+               for(excl = dle->exclude_file->first; excl != NULL;
                    excl = excl->next) {
                    add_exclude(file_exclude, excl->name,
-                               verbose && options->exclude_optional == 0);
+                               verbose && dle->exclude_optional == 0);
                }
            }
 
-           if(options->exclude_list) {
-               for(excl = options->exclude_list->first; excl != NULL;
+           if (dle->exclude_list) {
+               for(excl = dle->exclude_list->first; excl != NULL;
                    excl = excl->next) {
-                   char *exclname = fixup_relative(excl->name, device);
+                   char *exclname = fixup_relative(excl->name, dle->device);
                    if((exclude = fopen(exclname, "r")) != NULL) {
                        while ((aexc = agets(exclude)) != NULL) {
                            if (aexc[0] == '\0') {
@@ -320,7 +321,7 @@ build_exclude(
                                continue;
                            }
                            add_exclude(file_exclude, aexc,
-                                       verbose && options->exclude_optional == 0);
+                                       verbose && dle->exclude_optional == 0);
                            amfree(aexc);
                        }
                        fclose(exclude);
@@ -329,7 +330,7 @@ build_exclude(
                        quoted = quote_string(exclname);
                        dbprintf(_("Can't open exclude file %s (%s)\n"),
                                  quoted, strerror(errno));
-                       if(verbose && (options->exclude_optional == 0 ||
+                       if(verbose && (dle->exclude_optional == 0 ||
                                       errno != ENOENT)) {
                            g_printf(_("ERROR [Can't open exclude file %s (%s)]\n"),
                                   quoted, strerror(errno));
@@ -340,12 +341,11 @@ build_exclude(
                }
            }
             fclose(file_exclude);
-       }
-       else {
+       } else {
            quoted = quote_string(filename);
            dbprintf(_("Can't create exclude file %s (%s)\n"),
                      quoted, strerror(errno));
-           if(verbose) {
+           if (verbose) {
                g_printf(_("ERROR [Can't create exclude file %s (%s)]\n"),
                        quoted, strerror(errno));
            }
@@ -358,10 +358,8 @@ build_exclude(
 
 char *
 build_include(
-    char *     disk,
-    char *     device,
-    option_t * options,
-    int                verbose)
+    dle_t   *dle,
+    int             verbose)
 {
     char *filename;
     FILE *file_include;
@@ -372,36 +370,36 @@ build_include(
     int nb_exp = 0;
     char *quoted;
 
-    if(options->include_file) nb_include += options->include_file->nb_element;
-    if(options->include_list) nb_include += options->include_list->nb_element;
+    if (dle->include_file) nb_include += dle->include_file->nb_element;
+    if (dle->include_list) nb_include += dle->include_list->nb_element;
 
-    if(nb_include == 0) return NULL;
+    if (nb_include == 0) return NULL;
 
-    if((filename = build_name(disk, "include", verbose)) != NULL) {
-       if((file_include = fopen(filename,"w")) != NULL) {
+    if ((filename = build_name(dle->disk, "include", verbose)) != NULL) {
+       if ((file_include = fopen(filename,"w")) != NULL) {
 
-           if(options->include_file) {
-               for(incl = options->include_file->first; incl != NULL;
+           if (dle->include_file) {
+               for (incl = dle->include_file->first; incl != NULL;
                    incl = incl->next) {
-                   nb_exp += add_include(disk, device, file_include,
+                   nb_exp += add_include(dle->disk, dle->device, file_include,
                                  incl->name,
-                                 verbose && options->include_optional == 0);
+                                 verbose && dle->include_optional == 0);
                }
            }
 
-           if(options->include_list) {
-               for(incl = options->include_list->first; incl != NULL;
+           if (dle->include_list) {
+               for (incl = dle->include_list->first; incl != NULL;
                    incl = incl->next) {
-                   char *inclname = fixup_relative(incl->name, device);
-                   if((include = fopen(inclname, "r")) != NULL) {
+                   char *inclname = fixup_relative(incl->name, dle->device);
+                   if ((include = fopen(inclname, "r")) != NULL) {
                        while ((ainc = agets(include)) != NULL) {
                            if (ainc[0] == '\0') {
                                amfree(ainc);
                                continue;
                            }
-                           nb_exp += add_include(disk, device,
+                           nb_exp += add_include(dle->disk, dle->device,
                                                  file_include, ainc,
-                                                 verbose && options->include_optional == 0);
+                                                 verbose && dle->include_optional == 0);
                            amfree(ainc);
                        }
                        fclose(include);
@@ -410,7 +408,7 @@ build_include(
                        quoted = quote_string(inclname);
                        dbprintf(_("Can't open include file %s (%s)\n"),
                                  quoted, strerror(errno));
-                       if(verbose && (options->include_optional == 0 ||
+                       if (verbose && (dle->include_optional == 0 ||
                                       errno != ENOENT)) {
                            g_printf(_("ERROR [Can't open include file %s (%s)]\n"),
                                   quoted, strerror(errno));
@@ -421,12 +419,11 @@ build_include(
                }
            }
             fclose(file_include);
-       }
-       else {
+       } else {
            quoted = quote_string(filename);
            dbprintf(_("Can't create include file %s (%s)\n"),
                      quoted, strerror(errno));
-           if(verbose) {
+           if (verbose) {
                g_printf(_("ERROR [Can't create include file %s (%s)]\n"),
                        quoted, strerror(errno));
            }
@@ -434,10 +431,10 @@ build_include(
        }
     }
        
-    if(nb_exp == 0) {
-       quoted = quote_string(disk);
+    if (nb_exp == 0) {
+       quoted = quote_string(dle->disk);
        dbprintf(_("No include for %s\n"), quoted);
-       if(verbose && options->include_optional == 0) {
+       if (verbose && dle->include_optional == 0) {
            g_printf(_("ERROR [No include for %s]\n"), quoted);
        }
        amfree(quoted);
@@ -448,59 +445,24 @@ build_include(
 
 
 void
-init_options(
-    option_t *options)
-{
-    options->str = NULL;
-    options->compress = COMP_NONE;
-    options->srvcompprog = NULL;
-    options->clntcompprog = NULL;
-    options->encrypt = ENCRYPT_NONE;
-    options->kencrypt = 0;
-    options->srv_encrypt = NULL;
-    options->clnt_encrypt = NULL;
-    options->srv_decrypt_opt = NULL;
-    options->clnt_decrypt_opt = NULL;
-    options->no_record = 0;
-    options->createindex = 0;
-    options->auth = NULL;
-    options->exclude_file = NULL;
-    options->exclude_list = NULL;
-    options->include_file = NULL;
-    options->include_list = NULL;
-    options->exclude_optional = 0;
-    options->include_optional = 0;
-}
-
-
-option_t *
 parse_options(
-    char *str,
-    char *disk,
-    char *device,
+    char         *str,
+    dle_t        *dle,
     am_feature_t *fs,
-    int verbose)
+    int           verbose)
 {
     char *exc;
     char *inc;
-    option_t *options;
     char *p, *tok;
     char *quoted;
 
-    (void)disk;                /* Quiet unused parameter warning */
-    (void)device;      /* Quiet unused parameter warning */
-
-    options = alloc(SIZEOF(option_t));
-    init_options(options);
-    options->str = stralloc(str);
-
     p = stralloc(str);
     tok = strtok(p,";");
 
     while (tok != NULL) {
        if(am_has_feature(fs, fe_options_auth)
           && BSTRNCMP(tok,"auth=") == 0) {
-           if(options->auth != NULL) {
+           if (dle->auth != NULL) {
                quoted = quote_string(tok + 5);
                dbprintf(_("multiple auth option %s\n"), quoted);
                if(verbose) {
@@ -508,174 +470,164 @@ parse_options(
                }
                amfree(quoted);
            }
-           options->auth = stralloc(&tok[5]);
+           dle->auth = stralloc(&tok[5]);
        }
        else if(am_has_feature(fs, fe_options_bsd_auth)
           && BSTRNCMP(tok, "bsd-auth") == 0) {
-           if(options->auth != NULL) {
+           if (dle->auth != NULL) {
                dbprintf(_("multiple auth option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple auth option]\n"));
                }
            }
-           options->auth = stralloc("bsd");
+           dle->auth = stralloc("bsd");
        }
-       else if(am_has_feature(fs, fe_options_krb4_auth)
-          && BSTRNCMP(tok, "krb4-auth") == 0) {
-           if(options->auth != NULL) {
-               dbprintf(_("multiple auth option\n"));
-               if(verbose) {
-                   g_printf(_("ERROR [multiple auth option]\n"));
-               }
-           }
-           options->auth = stralloc("krb4");
-       }
-       else if(BSTRNCMP(tok, "compress-fast") == 0) {
-           if(options->compress != COMP_NONE) {
+       else if (BSTRNCMP(tok, "compress-fast") == 0) {
+           if (dle->compress != COMP_NONE) {
                dbprintf(_("multiple compress option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple compress option]\n"));
                }
            }
-           options->compress = COMP_FAST;
+           dle->compress = COMP_FAST;
        }
-       else if(BSTRNCMP(tok, "compress-best") == 0) {
-           if(options->compress != COMP_NONE) {
+       else if (BSTRNCMP(tok, "compress-best") == 0) {
+           if (dle->compress != COMP_NONE) {
                dbprintf(_("multiple compress option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple compress option]\n"));
                }
            }
-           options->compress = COMP_BEST;
+           dle->compress = COMP_BEST;
        }
-       else if(BSTRNCMP(tok, "srvcomp-fast") == 0) {
-           if(options->compress != COMP_NONE) {
+       else if (BSTRNCMP(tok, "srvcomp-fast") == 0) {
+           if (dle->compress != COMP_NONE) {
                dbprintf(_("multiple compress option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple compress option]\n"));
                }
            }
-           options->compress = COMP_SERVER_FAST;
+           dle->compress = COMP_SERVER_FAST;
        }
-       else if(BSTRNCMP(tok, "srvcomp-best") == 0) {
-           if(options->compress != COMP_NONE) {
+       else if (BSTRNCMP(tok, "srvcomp-best") == 0) {
+           if (dle->compress != COMP_NONE) {
                dbprintf(_("multiple compress option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple compress option]\n"));
                }
            }
-           options->compress = COMP_SERVER_BEST;
+           dle->compress = COMP_SERVER_BEST;
        }
-       else if(BSTRNCMP(tok, "srvcomp-cust=") == 0) {
-           if(options->compress != COMP_NONE) {
+       else if (BSTRNCMP(tok, "srvcomp-cust=") == 0) {
+           if (dle->compress != COMP_NONE) {
                dbprintf(_("multiple compress option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple compress option]\n"));
                }
            }
-           options->srvcompprog = stralloc(tok + SIZEOF("srvcomp-cust=") -1);
-           options->compress = COMP_SERVER_CUST;
+           dle->compprog = stralloc(tok + SIZEOF("srvcomp-cust=") -1);
+           dle->compress = COMP_SERVER_CUST;
        }
-       else if(BSTRNCMP(tok, "comp-cust=") == 0) {
-           if(options->compress != COMP_NONE) {
+       else if (BSTRNCMP(tok, "comp-cust=") == 0) {
+           if (dle->compress != COMP_NONE) {
                dbprintf(_("multiple compress option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple compress option]\n"));
                }
            }
-           options->clntcompprog = stralloc(tok + SIZEOF("comp-cust=") -1);
-           options->compress = COMP_CUST;
+           dle->compprog = stralloc(tok + SIZEOF("comp-cust=") -1);
+           dle->compress = COMP_CUST;
            /* parse encryption options */
        } 
-       else if(BSTRNCMP(tok, "encrypt-serv-cust=") == 0) {
-           if(options->encrypt != ENCRYPT_NONE) {
+       else if (BSTRNCMP(tok, "encrypt-serv-cust=") == 0) {
+           if (dle->encrypt != ENCRYPT_NONE) {
                dbprintf(_("multiple encrypt option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple encrypt option]\n"));
                }
            }
-           options->srv_encrypt = stralloc(tok + SIZEOF("encrypt-serv-cust=") -1);
-           options->encrypt = ENCRYPT_SERV_CUST;
+           dle->srv_encrypt = stralloc(tok + SIZEOF("encrypt-serv-cust=") -1);
+           dle->encrypt = ENCRYPT_SERV_CUST;
        } 
-       else if(BSTRNCMP(tok, "encrypt-cust=") == 0) {
-           if(options->encrypt != ENCRYPT_NONE) {
+       else if (BSTRNCMP(tok, "encrypt-cust=") == 0) {
+           if (dle->encrypt != ENCRYPT_NONE) {
                dbprintf(_("multiple encrypt option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple encrypt option]\n"));
                }
            }
-           options->clnt_encrypt= stralloc(tok + SIZEOF("encrypt-cust=") -1);
-           options->encrypt = ENCRYPT_CUST;
+           dle->clnt_encrypt= stralloc(tok + SIZEOF("encrypt-cust=") -1);
+           dle->encrypt = ENCRYPT_CUST;
        } 
-       else if(BSTRNCMP(tok, "server-decrypt-option=") == 0) {
-         options->srv_decrypt_opt = stralloc(tok + SIZEOF("server-decrypt-option=") -1);
+       else if (BSTRNCMP(tok, "server-decrypt-option=") == 0) {
+         dle->srv_decrypt_opt = stralloc(tok + SIZEOF("server-decrypt-option=") -1);
        }
-       else if(BSTRNCMP(tok, "client-decrypt-option=") == 0) {
-         options->clnt_decrypt_opt = stralloc(tok + SIZEOF("client-decrypt-option=") -1);
+       else if (BSTRNCMP(tok, "client-decrypt-option=") == 0) {
+         dle->clnt_decrypt_opt = stralloc(tok + SIZEOF("client-decrypt-option=") -1);
        }
-       else if(BSTRNCMP(tok, "no-record") == 0) {
-           if(options->no_record != 0) {
+       else if (BSTRNCMP(tok, "no-record") == 0) {
+           if (dle->record != 1) {
                dbprintf(_("multiple no-record option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple no-record option]\n"));
                }
            }
-           options->no_record = 1;
+           dle->record = 0;
        }
-       else if(BSTRNCMP(tok, "index") == 0) {
-           if(options->createindex != 0) {
+       else if (BSTRNCMP(tok, "index") == 0) {
+           if (dle->create_index != 0) {
                dbprintf(_("multiple index option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple index option]\n"));
                }
            }
-           options->createindex = 1;
+           dle->create_index = 1;
        }
-       else if(BSTRNCMP(tok, "exclude-optional") == 0) {
-           if(options->exclude_optional != 0) {
+       else if (BSTRNCMP(tok, "exclude-optional") == 0) {
+           if (dle->exclude_optional != 0) {
                dbprintf(_("multiple exclude-optional option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple exclude-optional option]\n"));
                }
            }
-           options->exclude_optional = 1;
+           dle->exclude_optional = 1;
        }
-       else if(strcmp(tok, "include-optional") == 0) {
-           if(options->include_optional != 0) {
+       else if (strcmp(tok, "include-optional") == 0) {
+           if (dle->include_optional != 0) {
                dbprintf(_("multiple include-optional option\n"));
-               if(verbose) {
+               if (verbose) {
                    g_printf(_("ERROR [multiple include-optional option]\n"));
                }
            }
-           options->include_optional = 1;
+           dle->include_optional = 1;
        }
-       else if(BSTRNCMP(tok,"exclude-file=") == 0) {
+       else if (BSTRNCMP(tok,"exclude-file=") == 0) {
            exc = unquote_string(&tok[13]);
-           options->exclude_file = append_sl(options->exclude_file, exc);
+           dle->exclude_file = append_sl(dle->exclude_file, exc);
            amfree(exc);
        }
-       else if(BSTRNCMP(tok,"exclude-list=") == 0) {
+       else if (BSTRNCMP(tok,"exclude-list=") == 0) {
            exc = unquote_string(&tok[13]);
-           options->exclude_list = append_sl(options->exclude_list, exc);
+           dle->exclude_list = append_sl(dle->exclude_list, exc);
            amfree(exc);
        }
-       else if(BSTRNCMP(tok,"include-file=") == 0) {
+       else if (BSTRNCMP(tok,"include-file=") == 0) {
            inc = unquote_string(&tok[13]);
-           options->include_file = append_sl(options->include_file, inc);
+           dle->include_file = append_sl(dle->include_file, inc);
            amfree(inc);
        }
-       else if(BSTRNCMP(tok,"include-list=") == 0) {
+       else if (BSTRNCMP(tok,"include-list=") == 0) {
            inc = unquote_string(&tok[13]);
-           options->include_list = append_sl(options->include_list, inc);
+           dle->include_list = append_sl(dle->include_list, inc);
            amfree(inc);
        }
-       else if(BSTRNCMP(tok,"kencrypt") == 0) {
-           options->kencrypt = 1;
+       else if (BSTRNCMP(tok,"kencrypt") == 0) {
+           dle->kencrypt = 1;
        }
-       else if(strcmp(tok,"|") != 0) {
+       else if (strcmp(tok,"|") != 0) {
            quoted = quote_string(tok);
            dbprintf(_("unknown option %s\n"), quoted);
-           if(verbose) {
+           if (verbose) {
                g_printf(_("ERROR [unknown option: %s]\n"), quoted);
            }
            amfree(quoted);
@@ -683,64 +635,79 @@ parse_options(
        tok = strtok(NULL, ";");
     }
     amfree(p);
-    return options;
 }
 
 void
-output_tool_property(
-    FILE     *tool,
-    option_t *options)
+application_property_add_to_argv(
+    GPtrArray *argv_ptr,
+    dle_t *dle,
+    backup_support_option_t *bsu,
+    am_feature_t *amfeatures)
 {
-    sle_t *sle;
-    char *q;
+    sle_t *incl, *excl;
 
-    if (!is_empty_sl(options->exclude_file)) {
-       for(sle = options->exclude_file->first ; sle != NULL; sle=sle->next) {
-           q = quote_string(sle->name);
-           g_fprintf(tool, "EXCLUDE-FILE %s\n", q);
-           amfree(q);
+    if (bsu) {
+       if (bsu->include_file && dle->include_file) {
+           for (incl = dle->include_file->first; incl != NULL;
+                incl = incl->next) {
+               g_ptr_array_add(argv_ptr, stralloc("--include-file"));
+               g_ptr_array_add(argv_ptr, stralloc(incl->name));
+           }
        }
-    }
-
-    if (!is_empty_sl(options->exclude_list)) {
-       for(sle = options->exclude_list->first ; sle != NULL; sle=sle->next) {
-           q = quote_string(sle->name);
-           g_fprintf(tool, "EXCLUDE-LIST %s\n", q);
-           amfree(q);
+       if (bsu->include_list && dle->include_list) {
+           for (incl = dle->include_list->first; incl != NULL;
+                incl = incl->next) {
+               g_ptr_array_add(argv_ptr, stralloc("--include-list"));
+               g_ptr_array_add(argv_ptr, stralloc(incl->name));
+           }
+       }
+       if (bsu->include_optional && dle->include_optional) {
+           g_ptr_array_add(argv_ptr, stralloc("--include-optional"));
+           g_ptr_array_add(argv_ptr, stralloc("yes"));
        }
-    }
 
-    if (!is_empty_sl(options->include_file)) {
-       for(sle = options->include_file->first ; sle != NULL; sle=sle->next) {
-           q = quote_string(sle->name);
-           g_fprintf(tool, "INCLUDE-FILE %s\n", q);
-           amfree(q);
+       if (bsu->exclude_file && dle->exclude_file) {
+           for (excl = dle->exclude_file->first; excl != NULL;
+                excl = excl->next) {
+               g_ptr_array_add(argv_ptr, stralloc("--exclude-file"));
+               g_ptr_array_add(argv_ptr, stralloc(excl->name));
+           }
+       }
+       if (bsu->exclude_list && dle->exclude_list) {
+           for (excl = dle->exclude_list->first; excl != NULL;
+               excl = excl->next) {
+               g_ptr_array_add(argv_ptr, stralloc("--exclude-list"));
+               g_ptr_array_add(argv_ptr, stralloc(excl->name));
+           }
+       }
+       if (bsu->exclude_optional && dle->exclude_optional) {
+           g_ptr_array_add(argv_ptr, stralloc("--exclude-optional"));
+           g_ptr_array_add(argv_ptr, stralloc("yes"));
        }
-    }
 
-    if (!is_empty_sl(options->include_list)) {
-       for(sle = options->include_list->first ; sle != NULL; sle=sle->next) {
-           q = quote_string(sle->name);
-           g_fprintf(tool, "INCLUDE-LIST %s\n", q);
-           amfree(q);
+       if (bsu->features && amfeatures) {
+           char *feature_string = am_feature_to_string(amfeatures);
+           g_ptr_array_add(argv_ptr, stralloc("--amfeatures"));
+           g_ptr_array_add(argv_ptr, feature_string);
        }
-    }
 
-    if (!is_empty_sl(options->exclude_file) ||
-       !is_empty_sl(options->exclude_list)) {
-       if (options->exclude_optional)
-           g_fprintf(tool, "EXCLUDE-OPTIONAL YES\n");
-       else
-           g_fprintf(tool, "EXCLUDE-OPTIONAL NO\n");
-    }
+       if (dle->data_path == DATA_PATH_DIRECTTCP &&
+           bsu->data_path_set & DATA_PATH_DIRECTTCP) {
+           GSList *directtcp;
 
-    if (!is_empty_sl(options->include_file) ||
-       !is_empty_sl(options->include_list)) {
-       if (options->include_optional)
-           g_fprintf(tool, "INCLUDE-OPTIONAL YES\n");
-       else
-           g_fprintf(tool, "INCLUDE-OPTIONAL NO\n");
+           g_ptr_array_add(argv_ptr, stralloc("--data-path"));
+           g_ptr_array_add(argv_ptr, stralloc("directtcp"));
+           for (directtcp = dle->directtcp_list; directtcp != NULL;
+                                                 directtcp = directtcp->next) {
+               g_ptr_array_add(argv_ptr, stralloc("--direct-tcp"));
+               g_ptr_array_add(argv_ptr, stralloc(directtcp->data));
+               break; /* XXX temporary; apps only support one ip:port pair */
+           }
+       }
     }
+
+    property_add_to_argv(argv_ptr, dle->application_property);
+    return;
 }
 
 backup_support_option_t *
@@ -748,49 +715,58 @@ backup_support_option(
     char       *program,
     g_option_t *g_options,
     char       *disk,
-    char       *amdevice)
+    char       *amdevice,
+    GPtrArray **errarray)
 {
     pid_t   supportpid;
     int     supportin, supportout, supporterr;
     char   *cmd;
-    char  **argvchild;
-    int     i;
+    GPtrArray *argv_ptr = g_ptr_array_new();
     FILE   *streamout;
+    FILE   *streamerr;
     char   *line;
+    int     status;
+    char   *err = NULL;
     backup_support_option_t *bsu;
 
-    cmd = vstralloc(DUMPER_DIR, "/", program, NULL);
-    argvchild = malloc(5 * SIZEOF(char *));
-    i = 0;
-    argvchild[i++] = program;
-    argvchild[i++] = "support";
+    *errarray = g_ptr_array_new();
+    cmd = vstralloc(APPLICATION_DIR, "/", program, NULL);
+    g_ptr_array_add(argv_ptr, stralloc(program));
+    g_ptr_array_add(argv_ptr, stralloc("support"));
     if (g_options->config) {
-       argvchild[i++] = "--config";
-       argvchild[i++] = g_options->config;
+       g_ptr_array_add(argv_ptr, stralloc("--config"));
+       g_ptr_array_add(argv_ptr, stralloc(g_options->config));
     }
     if (g_options->hostname) {
-       argvchild[i++] = "--host";
-       argvchild[i++] = g_options->hostname;
+       g_ptr_array_add(argv_ptr, stralloc("--host"));
+       g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
     }
     if (disk) {
-       argvchild[i++] = "--disk";
-       argvchild[i++] = disk;
+       g_ptr_array_add(argv_ptr, stralloc("--disk"));
+       g_ptr_array_add(argv_ptr, stralloc(disk));
     }
     if (amdevice) {
-       argvchild[i++] = "--device";
-       argvchild[i++] = amdevice;
+       g_ptr_array_add(argv_ptr, stralloc("--device"));
+       g_ptr_array_add(argv_ptr, stralloc(amdevice));
     }
-    argvchild[i++] = NULL;
+    g_ptr_array_add(argv_ptr, NULL);
 
     supporterr = fileno(stderr);
-    supportpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE, &supportin,
-                           &supportout, &supporterr, argvchild);
+    supportpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
+                           &supportin, &supportout, &supporterr,
+                           (char **)argv_ptr->pdata);
 
     aclose(supportin);
 
-    bsu = malloc(SIZEOF(*bsu));
-    memset(bsu, '\0', SIZEOF(*bsu));
+    bsu = g_new0(backup_support_option_t, 1);
+    bsu->config = 1;
+    bsu->host = 1;
+    bsu->disk = 1;
     streamout = fdopen(supportout, "r");
+    if (!streamout) {
+       error(_("Error opening pipe to child: %s"), strerror(errno));
+       /* NOTREACHED */
+    }
     while((line = agets(streamout)) != NULL) {
        dbprintf(_("support line: %s\n"), line);
        if (strncmp(line,"CONFIG ", 7) == 0) {
@@ -801,7 +777,7 @@ backup_support_option(
            bsu->host = 1;
        } else if (strncmp(line,"DISK ", 5) == 0) {
            if (strcmp(line+5, "YES") == 0)
-               bsu->host = 1;
+               bsu->disk = 1;
        } else if (strncmp(line,"INDEX-LINE ", 11) == 0) {
            if (strcmp(line+11, "YES") == 0)
                bsu->index_line = 1;
@@ -823,24 +799,914 @@ backup_support_option(
        } else if (strncmp(line,"INCLUDE-LIST ", 13) == 0) {
            if (strcmp(line+13, "YES") == 0)
                bsu->include_list = 1;
+       } else if (strncmp(line,"INCLUDE-LIST-GLOB ", 17) == 0) {
+           if (strcmp(line+17, "YES") == 0)
+               bsu->include_list_glob = 1;
+       } else if (strncmp(line,"INCLUDE-OPTIONAL ", 17) == 0) {
+           if (strcmp(line+17, "YES") == 0)
+               bsu->include_optional = 1;
        } else if (strncmp(line,"EXCLUDE-FILE ", 13) == 0) {
            if (strcmp(line+13, "YES") == 0)
                bsu->exclude_file = 1;
        } else if (strncmp(line,"EXCLUDE-LIST ", 13) == 0) {
            if (strcmp(line+13, "YES") == 0)
                bsu->exclude_list = 1;
+       } else if (strncmp(line,"EXCLUDE-LIST-GLOB ", 17) == 0) {
+           if (strcmp(line+17, "YES") == 0)
+               bsu->exclude_list_glob = 1;
+       } else if (strncmp(line,"EXCLUDE-OPTIONAL ", 17) == 0) {
+           if (strcmp(line+17, "YES") == 0)
+               bsu->exclude_optional = 1;
        } else if (strncmp(line,"COLLECTION ", 11) == 0) {
            if (strcmp(line+11, "YES") == 0)
                bsu->collection = 1;
+       } else if (strncmp(line,"CALCSIZE ", 9) == 0) {
+           if (strcmp(line+9, "YES") == 0)
+               bsu->calcsize = 1;
+       } else if (strncmp(line,"CLIENT-ESTIMATE ", 16) == 0) {
+           if (strcmp(line+16, "YES") == 0)
+               bsu->client_estimate = 1;
+       } else if (strncmp(line,"MULTI-ESTIMATE ", 15) == 0) {
+           if (strcmp(line+15, "YES") == 0)
+               bsu->multi_estimate = 1;
        } else if (strncmp(line,"MAX-LEVEL ", 10) == 0) {
            bsu->max_level  = atoi(line+10);
+       } else if (strncmp(line,"RECOVER-MODE ", 13) == 0) {
+           if (strcasecmp(line+13, "SMB") == 0)
+               bsu->smb_recover_mode = 1;
+       } else if (strncmp(line,"DATA-PATH ", 10) == 0) {
+           if (strcasecmp(line+10, "AMANDA") == 0)
+               bsu->data_path_set |= DATA_PATH_AMANDA;
+           else if (strcasecmp(line+10, "DIRECTTCP") == 0)
+               bsu->data_path_set |= DATA_PATH_DIRECTTCP;
+       } else if (strncmp(line,"RECOVER-PATH ", 13) == 0) {
+           if (strcasecmp(line+13, "CWD") == 0)
+               bsu->recover_path = RECOVER_PATH_CWD;
+           else if (strcasecmp(line+13, "REMOTE") == 0)
+               bsu->recover_path = RECOVER_PATH_REMOTE;
+       } else if (strncmp(line,"AMFEATURES ", 11) == 0) {
+           if (strcmp(line+11, "YES") == 0)
+               bsu->features = 1;
        } else {
            dbprintf(_("Invalid support line: %s\n"), line);
        }
        amfree(line);
     }
-    aclose(supportout);
+    fclose(streamout);
+
+    if (bsu->data_path_set == 0)
+       bsu->data_path_set = DATA_PATH_AMANDA;
+
+    streamerr = fdopen(supporterr, "r");
+    if (!streamerr) {
+       error(_("Error opening pipe to child: %s"), strerror(errno));
+       /* NOTREACHED */
+    }
+    while((line = agets(streamerr)) != NULL) {
+       if (strlen(line) > 0) {
+           g_ptr_array_add(*errarray, line);
+           dbprintf("Application '%s': %s\n", program, line);
+       }
+       amfree(bsu);
+    }
+    fclose(streamerr);
 
-    return NULL;
+    if (waitpid(supportpid, &status, 0) < 0) {
+       err = vstrallocf(_("waitpid failed: %s"), strerror(errno));
+    } else if (!WIFEXITED(status)) {
+       err = vstrallocf(_("exited with signal %d"), WTERMSIG(status));
+    } else if (WEXITSTATUS(status) != 0) {
+       err = vstrallocf(_("exited with status %d"), WEXITSTATUS(status));
+    }
+
+    if (err) {
+       g_ptr_array_add(*errarray, err);
+       dbprintf("Application '%s': %s\n", program, err);
+       amfree(bsu);
+    }
+    g_ptr_array_free_full(argv_ptr);
+    amfree(cmd);
+    return bsu;
+}
+
+void
+run_client_script(
+    script_t     *script,
+    execute_on_t  execute_on,
+    g_option_t   *g_options,
+    dle_t       *dle)
+{
+    pid_t     scriptpid;
+    int       scriptin, scriptout, scripterr;
+    char     *cmd;
+    GPtrArray *argv_ptr = g_ptr_array_new();
+    FILE     *streamout;
+    FILE     *streamerr;
+    char     *line;
+    amwait_t  wait_status;
+    char     *command = NULL;
+
+    if ((script->execute_on & execute_on) == 0)
+       return;
+    if (script->execute_where != ES_CLIENT)
+       return;
+
+    cmd = vstralloc(APPLICATION_DIR, "/", script->plugin, NULL);
+    g_ptr_array_add(argv_ptr, stralloc(script->plugin));
+
+    switch (execute_on) {
+    case EXECUTE_ON_PRE_DLE_AMCHECK:
+       command = "PRE-DLE-AMCHECK";
+       break;
+    case EXECUTE_ON_PRE_HOST_AMCHECK:
+       command = "PRE-HOST-AMCHECK";
+       break;
+    case EXECUTE_ON_POST_DLE_AMCHECK:
+       command = "POST-DLE-AMCHECK";
+       break;
+    case EXECUTE_ON_POST_HOST_AMCHECK:
+       command = "POST-HOST-AMCHECK";
+       break;
+    case EXECUTE_ON_PRE_DLE_ESTIMATE:
+       command = "PRE-DLE-ESTIMATE";
+       break;
+    case EXECUTE_ON_PRE_HOST_ESTIMATE:
+       command = "PRE-HOST-ESTIMATE";
+       break;
+    case EXECUTE_ON_POST_DLE_ESTIMATE:
+       command = "POST-DLE-ESTIMATE";
+       break;
+    case EXECUTE_ON_POST_HOST_ESTIMATE:
+       command = "POST-HOST-ESTIMATE";
+       break;
+    case EXECUTE_ON_PRE_DLE_BACKUP:
+       command = "PRE-DLE-BACKUP";
+       break;
+    case EXECUTE_ON_PRE_HOST_BACKUP:
+       command = "PRE-HOST-BACKUP";
+       break;
+    case EXECUTE_ON_POST_DLE_BACKUP:
+       command = "POST-DLE-BACKUP";
+       break;
+    case EXECUTE_ON_POST_HOST_BACKUP:
+       command = "POST-HOST-BACKUP";
+       break;
+    case EXECUTE_ON_PRE_RECOVER:
+       command = "PRE-RECOVER";
+       break;
+    case EXECUTE_ON_POST_RECOVER:
+       command = "POST-RECOVER";
+       break;
+    case EXECUTE_ON_PRE_LEVEL_RECOVER:
+       command = "PRE-LEVEL-RECOVER";
+       break;
+    case EXECUTE_ON_POST_LEVEL_RECOVER:
+       command = "POST-LEVEL-RECOVER";
+       break;
+    case EXECUTE_ON_INTER_LEVEL_RECOVER:
+       command = "INTER-LEVEL-RECOVER";
+       break;
+    }
+    g_ptr_array_add(argv_ptr, stralloc(command));
+    g_ptr_array_add(argv_ptr, stralloc("--execute-where"));
+    g_ptr_array_add(argv_ptr, stralloc("client"));
+
+    if (g_options->config) {
+       g_ptr_array_add(argv_ptr, stralloc("--config"));
+       g_ptr_array_add(argv_ptr, stralloc(g_options->config));
+    }
+    if (g_options->hostname) {
+       g_ptr_array_add(argv_ptr, stralloc("--host"));
+       g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
+    }
+    if (dle->disk) {
+       g_ptr_array_add(argv_ptr, stralloc("--disk"));
+       g_ptr_array_add(argv_ptr, stralloc(dle->disk));
+    }
+    if (dle->device) {
+       g_ptr_array_add(argv_ptr, stralloc("--device"));
+       g_ptr_array_add(argv_ptr, stralloc(dle->device));
+    }
+    if (dle->levellist) {
+       levellist_t levellist;
+       char number[NUM_STR_SIZE];
+       for (levellist=dle->levellist; levellist; levellist=levellist->next) {
+           level_t *alevel = (level_t *)levellist->data;
+           g_ptr_array_add(argv_ptr, stralloc("--level"));
+           g_snprintf(number, SIZEOF(number), "%d", alevel->level);
+           g_ptr_array_add(argv_ptr, stralloc(number));
+       }
+    }
+    property_add_to_argv(argv_ptr, script->property);
+    g_ptr_array_add(argv_ptr, NULL);
+
+    scriptpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
+                          &scriptin, &scriptout, &scripterr,
+                          (char **)argv_ptr->pdata);
+
+    close(scriptin);
+
+    script->result = g_new0(client_script_result_t, 1);
+    script->result->proplist =
+                 g_hash_table_new_full(g_str_hash, g_str_equal,
+                                       &g_free, &g_slist_free_full_gpointer);
+    script->result->output = g_ptr_array_new();
+    script->result->err = g_ptr_array_new();
+
+    streamout = fdopen(scriptout, "r");
+    if (streamout) {
+        while((line = agets(streamout)) != NULL) {
+            dbprintf("script: %s\n", line);
+            if (BSTRNCMP(line, "PROPERTY ") == 0) {
+               char *property_name, *property_value;
+               property_name = line + 9;
+               property_value = strchr(property_name,' ');
+               if (property_value == NULL) {
+                   char *msg = g_strdup_printf(
+                                       "ERROR %s: Bad output property: %s",
+                                       script->plugin, line);
+                   g_ptr_array_add(script->result->output, msg);
+               } else {
+                   property_t *property;
+
+                   *property_value++ = '\0';
+                   property_name = stralloc(property_name);
+                   property_value = stralloc(property_value);
+                   property = g_hash_table_lookup(script->result->proplist,
+                                                  property_name);
+                   if (!property) {
+                       property = g_new0(property_t, 1);
+                       g_hash_table_insert(script->result->proplist,
+                                           property_name, property);
+                   }
+                   property->values = g_slist_append(property->values,
+                                                     property_value);
+               }
+               amfree(line);
+            } else {
+                g_ptr_array_add(script->result->output, line);
+            }
+        }
+    }
+    fclose(streamout);
+
+    streamerr = fdopen(scripterr, "r");
+    if (streamerr) {
+        while((line = agets(streamerr)) != NULL) {
+           g_ptr_array_add(script->result->err,
+                           g_strdup_printf(_("Script '%s' command '%s': %s"),
+                                           script->plugin, command, line));
+           amfree(line);
+       }
+    }
+
+    waitpid(scriptpid, &wait_status, 0);
+    if (WIFSIGNALED(wait_status)) {
+       g_ptr_array_add(script->result->err,
+                       g_strdup_printf(_("Script '%s' command '%s' terminated with signal %d: see %s"),
+                                       script->plugin, command,
+                                       WTERMSIG(wait_status),
+                                       dbfn()));
+    } else if (WIFEXITED(wait_status)) {
+        if (WEXITSTATUS(wait_status) != 0) {
+           g_ptr_array_add(script->result->err,
+                           g_strdup_printf(_("Script '%s' command '%s' exited with status %d: see %s"),
+                                           script->plugin, command,
+                                           WEXITSTATUS(wait_status),
+                                           dbfn()));
+        } else {
+            /* Normal exit */
+        }
+    }
+    amfree(cmd);
+    g_ptr_array_free_full(argv_ptr);
+}
+
+void run_client_script_output(gpointer data, gpointer user_data);
+void run_client_script_err_amcheck(gpointer data, gpointer user_data);
+void run_client_script_err_estimate(gpointer data, gpointer user_data);
+void run_client_script_err_backup(gpointer data, gpointer user_data);
+void run_client_script_err_recover(gpointer data, gpointer user_data);
+
+typedef struct script_output_s {
+    FILE  *stream;
+    dle_t *dle;
+} script_output_t;
+
+void
+run_client_script_output(
+    gpointer data,
+    gpointer user_data)
+{
+    char            *line = data;
+    script_output_t *so   = user_data;
+
+    if (line && so->stream) {
+       g_fprintf(so->stream, "%s\n", line);
+    }
+}
+
+void
+run_client_script_err_amcheck(
+    gpointer data,
+    gpointer user_data)
+{
+    char            *line  = data;
+    script_output_t *so    = user_data;
+
+    if (line && so->stream) {
+       g_fprintf(so->stream, "ERROR %s\n", line);
+    }
+}
+
+void
+run_client_script_err_estimate(
+    gpointer data,
+    gpointer user_data)
+{
+    char            *line  = data;
+    script_output_t *so    = user_data;
+
+    if (line && so->stream) {
+       char *qdisk = quote_string(so->dle->disk);
+       g_fprintf(so->stream, "%s 0 WARNING \"%s\"\n", qdisk, line);
+       amfree(qdisk);
+    }
+}
+
+void
+run_client_script_err_backup(
+    gpointer data,
+    gpointer user_data)
+{
+    char            *line  = data;
+    script_output_t *so    = user_data;
+
+    if (line && so->stream) {
+       g_fprintf(so->stream, "? %s\n", line);
+    }
+}
+
+void
+run_client_script_err_recover(
+    gpointer data,
+    gpointer user_data)
+{
+    char            *line  = data;
+    script_output_t *so    = user_data;
+
+    if (line && so->stream) {
+       g_fprintf(so->stream, "%s\n", line);
+    }
+}
+
+void
+run_client_scripts(
+    execute_on_t  execute_on,
+    g_option_t   *g_options,
+    dle_t       *dle,
+    FILE         *streamout)
+{
+    GSList          *scriptlist;
+    script_t        *script;
+    GFunc            client_script_err = NULL;
+    script_output_t  so = { streamout, dle };
+
+    for (scriptlist = dle->scriptlist; scriptlist != NULL;
+        scriptlist = scriptlist->next) {
+       script = (script_t *)scriptlist->data;
+       run_client_script(script, execute_on, g_options, dle);
+       if (script->result && script->result->output) {
+           g_ptr_array_foreach(script->result->output,
+                               run_client_script_output,
+                               &so);
+           g_ptr_array_free(script->result->output, TRUE);
+           script->result->output = NULL;
+       }
+       if (script->result && script->result->err) {
+           switch (execute_on) {
+           case EXECUTE_ON_PRE_DLE_AMCHECK:
+           case EXECUTE_ON_PRE_HOST_AMCHECK:
+           case EXECUTE_ON_POST_DLE_AMCHECK:
+           case EXECUTE_ON_POST_HOST_AMCHECK:
+                client_script_err = run_client_script_err_amcheck;
+                break;
+           case EXECUTE_ON_PRE_DLE_ESTIMATE:
+           case EXECUTE_ON_PRE_HOST_ESTIMATE:
+           case EXECUTE_ON_POST_DLE_ESTIMATE:
+           case EXECUTE_ON_POST_HOST_ESTIMATE:
+                if (am_has_feature(g_options->features,
+                                   fe_sendsize_rep_warning)) {
+                    client_script_err = run_client_script_err_estimate;
+                }
+                break;
+           case EXECUTE_ON_PRE_DLE_BACKUP:
+           case EXECUTE_ON_PRE_HOST_BACKUP:
+           case EXECUTE_ON_POST_DLE_BACKUP:
+           case EXECUTE_ON_POST_HOST_BACKUP:
+                client_script_err = run_client_script_err_backup;
+                break;
+           case EXECUTE_ON_PRE_RECOVER:
+           case EXECUTE_ON_POST_RECOVER:
+           case EXECUTE_ON_PRE_LEVEL_RECOVER:
+           case EXECUTE_ON_POST_LEVEL_RECOVER:
+           case EXECUTE_ON_INTER_LEVEL_RECOVER:
+                client_script_err = run_client_script_err_recover;
+           }
+           if (client_script_err != NULL) {
+               g_ptr_array_foreach(script->result->err,
+                                   client_script_err,
+                                   &so);
+           }
+           g_ptr_array_free(script->result->err, TRUE);
+           script->result->err = NULL;
+       }
+       amfree(script->result);
+    }
+}
+
+
+void
+run_calcsize(
+    char   *config,
+    char   *program,
+    char   *disk,
+    char   *dirname,
+    GSList *levels,
+    char   *file_exclude,
+    char   *file_include)
+{
+    char        *cmd, *cmdline;
+    char       *command;
+    GPtrArray   *argv_ptr = g_ptr_array_new();
+    char         tmppath[PATH_MAX];
+    char         number[NUM_STR_SIZE];
+    GSList      *alevel;
+    guint        level;
+    guint        i;
+    char        *match_expr;
+    int          pipefd = -1, nullfd = -1;
+    pid_t        calcpid;
+    times_t      start_time;
+    FILE        *dumpout = NULL;
+    int          dumpsince;
+    char        *errmsg = NULL;
+    off_t        size = (off_t)1;
+    char        *line = NULL;
+    amwait_t     wait_status;
+    int          len;
+    char        *qdisk;
+    amandates_t *amdp;
+    char        *amandates_file;
+
+    qdisk = quote_string(disk);
+
+    amandates_file = getconf_str(CNF_AMANDATES);
+    if(!start_amandates(amandates_file, 0)) {
+       char *errstr = strerror(errno);
+       char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
+       char *qerrmsg = quote_string(errmsg);
+       g_printf(_("ERROR %s\n"), qerrmsg);
+       amfree(qdisk);
+       amfree(errmsg);
+       amfree(qerrmsg);
+       return;
+    }
+
+    startclock();
+    cmd = vstralloc(amlibexecdir, "/", "calcsize", NULL);
+
+
+    g_ptr_array_add(argv_ptr, stralloc("calcsize"));
+    if (config)
+       g_ptr_array_add(argv_ptr, stralloc(config));
+    else
+       g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
+
+    g_ptr_array_add(argv_ptr, stralloc(program));
+
+    canonicalize_pathname(disk, tmppath);
+    g_ptr_array_add(argv_ptr, stralloc(tmppath));
+    canonicalize_pathname(dirname, tmppath);
+    g_ptr_array_add(argv_ptr, stralloc(tmppath));
+
+    if (file_exclude) {
+       g_ptr_array_add(argv_ptr, stralloc("-X"));
+       g_ptr_array_add(argv_ptr, stralloc(file_exclude));
+    }
+
+    if (file_include) {
+       g_ptr_array_add(argv_ptr, stralloc("-I"));
+       g_ptr_array_add(argv_ptr, stralloc(file_include));
+    }
+
+    for (alevel = levels; alevel != NULL; alevel = alevel->next) {
+       amdp = amandates_lookup(disk);
+       level = GPOINTER_TO_INT(alevel->data);
+       dbprintf("level: %d\n", level);
+       dumpsince = 0;
+       for (i=0; i < level; i++) {
+           if (dumpsince < amdp->dates[i])
+               dumpsince = amdp->dates[i];
+       }
+       g_snprintf(number, SIZEOF(number), "%d", level);
+       g_ptr_array_add(argv_ptr, stralloc(number));
+       g_snprintf(number, SIZEOF(number), "%d", dumpsince);
+       g_ptr_array_add(argv_ptr, stralloc(number));
+    }
+
+    g_ptr_array_add(argv_ptr, NULL);
+    command = (char *)g_ptr_array_index(argv_ptr, 0);
+    cmdline = stralloc(command);
+    for(i = 1; i < argv_ptr->len - 1; i++)
+       cmdline = vstrextend(&cmdline, " ",
+                            (char *)g_ptr_array_index(argv_ptr,i), NULL);
+    dbprintf(_("running: \"%s\"\n"), cmdline);
+    amfree(cmdline);
+
+    start_time = curclock();
+
+    fflush(stderr); fflush(stdout);
+
+    if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
+       errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
+                           strerror(errno));
+       dbprintf("%s\n", errmsg);
+       goto common_exit;
+    }
+
+    calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
+                        &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
+    amfree(cmd);
+
+    dumpout = fdopen(pipefd,"r");
+    if (!dumpout) {
+       error(_("Can't fdopen: %s"), strerror(errno));
+       /*NOTREACHED*/
+    }
+
+    match_expr = vstralloc(" %d SIZE %lld", NULL);
+    len = strlen(qdisk);
+    for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
+       long long size_ = (long long)0;
+       if (line[0] == '\0' || (int)strlen(line) <= len)
+           continue;
+       /* Don't use sscanf for qdisk because it can have a '%'. */
+       if (strncmp(line, qdisk, len) == 0 &&
+           sscanf(line+len, match_expr, &level, &size_) == 2) {
+           g_printf("%d %lld %d\n", level, size_, 1); /* write to sendsize */
+           dbprintf(_("estimate size for %s level %d: %lld KB\n"),
+                    qdisk, level, size_);
+       }
+       size = (off_t)size_;
+    }
+    amfree(match_expr);
+
+    dbprintf(_("waiting for %s %s child (pid=%d)\n"),
+            command, qdisk, (int)calcpid);
+    waitpid(calcpid, &wait_status, 0);
+    if (WIFSIGNALED(wait_status)) {
+       errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
+                           "calcsize", WTERMSIG(wait_status),
+                           dbfn());
+    } else if (WIFEXITED(wait_status)) {
+       if (WEXITSTATUS(wait_status) != 0) {
+           errmsg = vstrallocf(_("%s exited with status %d: see %s"),
+                               "calcsize", WEXITSTATUS(wait_status),
+                               dbfn());
+       } else {
+           /* Normal exit */
+       }
+    } else {
+       errmsg = vstrallocf(_("%s got bad exit: see %s"),
+                           "calcsize", dbfn());
+    }
+
+    dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
+            command, qdisk,
+            (int)calcpid, WEXITSTATUS(wait_status));
+
+    dbprintf(_(".....\n"));
+    dbprintf(_("estimate time for %s: %s\n"),
+            qdisk,
+            walltime_str(timessub(curclock(), start_time)));
+
+common_exit:
+    if (errmsg && errmsg[0] != '\0') {
+       char *qerrmsg = quote_string(errmsg);
+       dbprintf(_("errmsg is %s\n"), errmsg);
+       g_printf("ERROR %s\n", qerrmsg);
+       amfree(qerrmsg);
+    }
+    amfree(qdisk);
+    amfree(errmsg);
+    g_ptr_array_free_full(argv_ptr);
+    amfree(cmd);
+
+}
+
+
+void
+check_access(
+    char *     filename,
+    int                mode)
+{
+    char *noun, *adjective;
+    char *quoted = quote_string(filename);
+
+    if(mode == F_OK)
+        noun = "find", adjective = "exists";
+    else if((mode & X_OK) == X_OK)
+       noun = "execute", adjective = "executable";
+    else if((mode & (W_OK|R_OK)) == (W_OK|R_OK))
+       noun = "read/write", adjective = "read/writable";
+    else 
+       noun = "access", adjective = "accessible";
+
+    if(access(filename, mode) == -1)
+       g_printf(_("ERROR [can not %s %s: %s]\n"), noun, quoted, strerror(errno));
+    else
+       g_printf(_("OK %s %s\n"), quoted, adjective);
+    amfree(quoted);
+}
+
+void
+check_file(
+    char *     filename,
+    int                mode)
+{
+    struct stat stat_buf;
+    char *quoted;
+
+    if(!stat(filename, &stat_buf)) {
+       if(!S_ISREG(stat_buf.st_mode)) {
+           quoted = quote_string(filename);
+           g_printf(_("ERROR [%s is not a file]\n"), quoted);
+           amfree(quoted);
+       }
+    } else {
+       int save_errno = errno;
+       quoted = quote_string(filename);
+       g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
+                strerror(save_errno));
+       amfree(quoted);
+    }
+    if (getuid() == geteuid()) {
+       check_access(filename, mode);
+    }
+}
+
+void
+check_dir(
+    char *     dirname,
+    int                mode)
+{
+    struct stat stat_buf;
+    char *quoted;
+    char *dir;
+
+    if(!stat(dirname, &stat_buf)) {
+       if(!S_ISDIR(stat_buf.st_mode)) {
+           quoted = quote_string(dirname);
+           g_printf(_("ERROR [%s is not a directory]\n"), quoted);
+           amfree(quoted);
+       }
+    } else {
+       int save_errno = errno;
+       quoted = quote_string(dirname);
+       g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
+                strerror(save_errno));
+       amfree(quoted);
+    }
+    if (getuid() == geteuid()) {
+       dir = stralloc2(dirname, "/.");
+       check_access(dir, mode);
+       amfree(dir);
+    }
+}
+
+void
+check_suid(
+    char *     filename)
+{
+#ifndef SINGLE_USERID
+    struct stat stat_buf;
+    char *quoted = quote_string(filename);
+
+    if(!stat(filename, &stat_buf)) {
+       if(stat_buf.st_uid != 0 ) {
+           g_printf(_("ERROR [%s is not owned by root]\n"), quoted);
+       }
+       if((stat_buf.st_mode & S_ISUID) != S_ISUID) {
+           g_printf(_("ERROR [%s is not SUID root]\n"), quoted);
+       }
+    }
+    else {
+       g_printf(_("ERROR [can not stat %s: %s]\n"), quoted, strerror(errno));
+    }
+    amfree(quoted);
+#else
+    (void)filename;    /* Quiet unused parameter warning */
+#endif
+}
+
+/*
+ * Returns the value of the first integer in a string.
+ */
+
+double
+the_num(
+    char *      str,
+    int         pos)
+{
+    char *num;
+    int ch;
+    double d;
+
+    do {
+       ch = *str++;
+       while(ch && !isdigit(ch)) ch = *str++;
+       if (pos == 1) break;
+       pos--;
+       while(ch && (isdigit(ch) || ch == '.')) ch = *str++;
+    } while (ch);
+    num = str - 1;
+    while(isdigit(ch) || ch == '.') ch = *str++;
+    str[-1] = '\0';
+    d = atof(num);
+    str[-1] = (char)ch;
+    return d;
+}
+
+
+char *
+config_errors_to_error_string(
+    GSList *errlist)
+{
+    char *errmsg;
+    gboolean multiple_errors = FALSE;
+
+    if (errlist) {
+       errmsg = (char *)errlist->data;
+       if (errlist->next)
+           multiple_errors = TRUE;
+    } else {
+       errmsg = _("(no error message)");
+    }
+
+    return vstrallocf("ERROR %s%s", errmsg,
+       multiple_errors? _(" (additional errors not displayed)"):"");
+}
+
+
+void
+add_type_table(
+    dmpline_t   typ,
+    amregex_t **re_table,
+    amregex_t  *orig_re_table,
+    GSList     *normal_message,
+    GSList     *ignore_message,
+    GSList     *strange_message)
+{
+    amregex_t *rp;
+
+    for(rp = orig_re_table; rp->regex != NULL; rp++) {
+       if (rp->typ == typ) {
+           int     found = 0;
+           GSList *mes;
+
+           for (mes = normal_message; mes != NULL; mes = mes->next) {
+               if (strcmp(rp->regex, (char *)mes->data) == 0)
+                   found = 1;
+           }
+           for (mes = ignore_message; mes != NULL; mes = mes->next) {
+               if (strcmp(rp->regex, (char *)mes->data) == 0)
+                   found = 1;
+           }
+           for (mes = strange_message; mes != NULL; mes = mes->next) {
+               if (strcmp(rp->regex, (char *)mes->data) == 0)
+                   found = 1;
+           }
+           if (found == 0) {
+               (*re_table)->regex   = rp->regex;
+               (*re_table)->srcline = rp->srcline;
+               (*re_table)->scale   = rp->scale;
+               (*re_table)->field   = rp->field;
+               (*re_table)->typ     = rp->typ;
+               (*re_table)++;
+           }
+       }
+    }
+}
+
+void
+add_list_table(
+    dmpline_t   typ,
+    amregex_t **re_table,
+    GSList     *message)
+{
+    GSList *mes;
+
+    for (mes = message; mes != NULL; mes = mes->next) {
+       (*re_table)->regex = (char *)mes->data;
+       (*re_table)->srcline = 0;
+       (*re_table)->scale   = 0;
+       (*re_table)->field   = 0;
+       (*re_table)->typ     = typ;
+       (*re_table)++;
+    }
+}
+
+amregex_t *
+build_re_table(
+    amregex_t *orig_re_table,
+    GSList    *normal_message,
+    GSList    *ignore_message,
+    GSList    *strange_message)
+{
+    int        nb = 0;
+    amregex_t *rp;
+    amregex_t *re_table, *new_re_table;
+
+    for(rp = orig_re_table; rp->regex != NULL; rp++) {
+       nb++;
+    }
+    nb += g_slist_length(normal_message);
+    nb += g_slist_length(ignore_message);
+    nb += g_slist_length(strange_message);
+    nb ++;
+
+    re_table =  new_re_table = malloc(nb * sizeof(amregex_t));
+    
+    /* add SIZE from orig_re_table */
+    add_type_table(DMP_SIZE, &re_table, orig_re_table,
+                  normal_message, ignore_message, strange_message);
+
+    /* add ignore_message */
+    add_list_table(DMP_IGNORE, &re_table, ignore_message);
+
+    /* add IGNORE from orig_re_table */
+    add_type_table(DMP_IGNORE, &re_table, orig_re_table,
+                  normal_message, ignore_message, strange_message);
+
+    /* add normal_message */
+    add_list_table(DMP_NORMAL, &re_table, normal_message);
+
+    /* add NORMAL from orig_re_table */
+    add_type_table(DMP_NORMAL, &re_table, orig_re_table,
+                  normal_message, ignore_message, strange_message);
+
+    /* add strange_message */
+    add_list_table(DMP_STRANGE, &re_table, strange_message);
+
+    /* add STRANGE from orig_re_table */
+    add_type_table(DMP_STRANGE, &re_table, orig_re_table,
+                  normal_message, ignore_message, strange_message);
+
+    /* Add DMP_STRANGE with NULL regex,       */
+    /* it is not copied by previous statement */
+    re_table->regex = NULL;
+    re_table->srcline = 0;
+    re_table->scale = 0;
+    re_table->field = 0;
+    re_table->typ = DMP_STRANGE;
+
+    return new_re_table;
+}
+
+typedef struct {
+    proplist_t result;
+} merge_property_t;
+
+static void
+merge_property(
+    gpointer key_p,
+    gpointer value_p,
+    gpointer user_data_p)
+{
+    char *property_s = key_p;
+    GSList *value_s = value_p;
+    merge_property_t *merge_p = user_data_p;
+    GSList *value = g_hash_table_lookup(merge_p->result, property_s);
+
+    if (value) { /* remove old value */
+       g_hash_table_remove(merge_p->result, key_p);
+    }
+    g_hash_table_insert(merge_p->result, key_p, value_s);
+}
+
+void
+merge_properties(
+    proplist_t proplist1,
+    proplist_t proplist2)
+{
+    merge_property_t merge_p = {proplist1};
+
+    if (proplist2 == NULL) {
+       return;
+    }
+    g_hash_table_foreach(proplist2,
+                         &merge_property,
+                         &merge_p);
 }