Imported Upstream version 3.3.3
[debian/amanda] / server-src / planner.c
index 92de8d830fda7a8856edb83ce25b2a75d61c9470..f7a38be691eeedbdf900a7043fc8dc3e9fbde377 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
  * Copyright (c) 1991-1999 University of Maryland at College Park
+ * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
  * All Rights Reserved.
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: planner.c,v 1.180 2006/03/10 13:51:06 martinea Exp $
+ * $Id: planner.c 10421 2008-03-06 18:48:30Z martineau $
  *
  * backup schedule planner for the Amanda backup system.
  */
 #include "amanda.h"
 #include "arglist.h"
+#include "find.h"
 #include "conffile.h"
 #include "diskfile.h"
 #include "tapefile.h"
 #include "amfeatures.h"
 #include "server_util.h"
 #include "holding.h"
+#include "timestamp.h"
+#include "amxml.h"
+
+#define planner_debug(i,x) do {                \
+       if ((i) <= debug_planner) {     \
+           dbprintf(x);                \
+       }                               \
+} while (0)
 
 #define MAX_LEVELS                 3   /* max# of estimates per filesys */
 
 
 /* configuration file stuff */
 
-char *conf_tapetype;
-am64_t conf_maxdumpsize;
-int conf_runtapes;
-int conf_dumpcycle;
-int conf_runspercycle;
-int conf_tapecycle;
-int conf_etimeout;
-int conf_reserve;
-int conf_autoflush;
+char * conf_tapetype;
+gint64         conf_maxdumpsize;
+int    conf_runtapes;
+int    conf_dumpcycle;
+int    conf_runspercycle;
+int    conf_tapecycle;
+time_t conf_etimeout;
+int    conf_reserve;
+int    conf_autoflush;
+int    conf_usetimestamps;
 
 #define HOST_READY                             ((void *)0)     /* must be 0 */
 #define HOST_ACTIVE                            ((void *)1)
@@ -72,41 +83,50 @@ int conf_autoflush;
 #define DISK_PARTIALY_DONE                     2
 #define DISK_DONE                              3
 
+typedef struct one_est_s {
+    int     level;
+    gint64  nsize;     /* native size     */
+    gint64  csize;     /* compressed size */
+    char   *dumpdate;
+    int     guessed;    /* If server guessed the estimate size */
+} one_est_t;
+static one_est_t default_one_est = {-1, -1, -1, "INVALID_DATE", 0};
+
 typedef struct est_s {
     int state;
     int got_estimate;
     int dump_priority;
-    int dump_level;
-    long dump_size;
-    int degr_level;    /* if dump_level == 0, what would be the inc level */
-    long degr_size;
+    one_est_t *dump_est;
+    one_est_t *degr_est;
+    one_est_t  estimate[MAX_LEVELS];
     int last_level;
-    long last_lev0size;
+    gint64 last_lev0size;
     int next_level0;
     int level_days;
     int promote;
+    int post_dle;
     double fullrate, incrrate;
     double fullcomp, incrcomp;
     char *errstr;
-    int level[MAX_LEVELS];
-    char *dumpdate[MAX_LEVELS];
-    long est_size[MAX_LEVELS];
+    char *degr_mesg;
+    info_t *info;
 } est_t;
 
 #define est(dp)        ((est_t *)(dp)->up)
 
 /* pestq = partial estimate */
 disklist_t startq, waitq, pestq, estq, failq, schedq;
-am64_t total_size;
+gint64 total_size;
 double total_lev0, balanced_size, balance_threshold;
-am64_t tape_length, tape_mark;
+gint64 tape_length;
+size_t tape_mark;
 
 tapetype_t *tape;
-long tt_blocksize;
-long tt_blocksize_kb;
+size_t tt_blocksize;
+size_t tt_blocksize_kb;
 int runs_per_cycle = 0;
 time_t today;
-char *datestamp = NULL;
+char *planner_timestamp = NULL;
 
 static am_feature_t *our_features = NULL;
 static char *our_feature_string = NULL;
@@ -122,7 +142,8 @@ typedef struct bi_s {
     int deleted;               /* 0=modified, 1=deleted */
     disk_t *dp;                        /* The disk that was changed */
     int level;                 /* The original level */
-    long size;                 /* The original size */
+    gint64 nsize;              /* The original native size */
+    gint64 csize;              /* The original compressed size */
     char *errstr;              /* A message describing why this disk is here */
 } bi_t;
 
@@ -138,100 +159,140 @@ bilist_t biq;                   /* The BI queue itself */
  *
  */
 
-static void setup_estimate P((disk_t *dp));
-static void get_estimates P((void));
-static void analyze_estimate P((disk_t *dp));
-static void handle_failed P((disk_t *dp));
-static void delay_dumps P((void));
-static int promote_highest_priority_incremental P((void));
-static int promote_hills P((void));
-static void output_scheduleline P((disk_t *dp));
-int main P((int, char **));
-
-int main(argc, argv)
-int argc;
-char **argv;
+static void setup_estimate(disk_t *dp);
+static void get_estimates(void);
+static void analyze_estimate(disk_t *dp);
+static void handle_failed(disk_t *dp);
+static void delay_dumps(void);
+static int promote_highest_priority_incremental(void);
+static int promote_hills(void);
+static void output_scheduleline(disk_t *dp);
+static void server_estimate(disk_t *dp, int i, info_t *info, int level);
+int main(int, char **);
+
+int
+main(
+    int                argc,
+    char **    argv)
 {
     disklist_t origq;
     disk_t *dp;
     int moved_one;
-    unsigned long malloc_hist_1, malloc_size_1;
-    unsigned long malloc_hist_2, malloc_size_2;
-    long initial_size;
+    int diskarg_offset;
+    gint64 initial_size;
     int i;
-    char *conffile;
     char *conf_diskfile;
     char *conf_tapelist;
     char *conf_infofile;
     times_t section_start;
+    char *qname;
+    int    nb_disk;
+    char  *errstr = NULL;
+    config_overrides_t *cfg_ovr = NULL;
+    char *cfg_opt = NULL;
+    int    planner_setuid;
+    int exit_status = EXIT_SUCCESS;
+    gboolean no_taper = FALSE;
+    gboolean from_client = FALSE;
+    gboolean exact_match = FALSE;
+
+    if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
+       printf("planner-%s\n", VERSION);
+       return (0);
+    }
+
+    /*
+     * Configure program for internationalization:
+     *   1) Only set the message locale for now.
+     *   2) Set textdomain for all amanda related programs to "amanda"
+     *      We don't want to be forced to support dozens of message catalogs.
+     */  
+    setlocale(LC_MESSAGES, "C");
+    textdomain("amanda"); 
+
+    /* drop root privileges */
+    planner_setuid = set_root_privs(0);
 
     safe_fd(-1, 0);
 
-    setvbuf(stderr, (char *)NULL, _IOLBF, 0);
+    set_pname("planner");
 
-    if (argc > 1) {
-       config_name = stralloc(argv[1]);
-       config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
-    } else {
-       char my_cwd[STR_SIZE];
+    dbopen(DBG_SUBDIR_SERVER);
 
-       if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
-           error("cannot determine current working directory");
-       }
-       config_dir = stralloc2(my_cwd, "/");
-       if ((config_name = strrchr(my_cwd, '/')) != NULL) {
-           config_name = stralloc(config_name + 1);
-       }
-    }
+    cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
+    if (argc > 1) 
+       cfg_opt = argv[1];
 
-    safe_cd();
+    set_config_overrides(cfg_ovr);
+    config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
 
-    set_pname("planner");
+    /* conf_diskfile is freed later, as it may be used in an error message */
+    conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
+    read_diskfile(conf_diskfile, &origq);
+    disable_skip_disk(&origq);
 
     /* Don't die when child closes pipe */
     signal(SIGPIPE, SIG_IGN);
 
-    malloc_size_1 = malloc_inuse(&malloc_hist_1);
+    setvbuf(stderr, (char *)NULL, (int)_IOLBF, 0);
+
+    add_amanda_log_handler(amanda_log_stderr);
+    add_amanda_log_handler(amanda_log_trace_log);
+
+    if (!planner_setuid) {
+       error(_("planner must be run setuid root"));
+    }
+
+    if (config_errors(NULL) >= CFGERR_ERRORS) {
+       g_critical(_("errors processing config file"));
+    }
+
+    safe_cd();
+
+    check_running_as(RUNNING_AS_ROOT | RUNNING_AS_UID_ONLY);
+
+    dbrename(get_config_name(), DBG_SUBDIR_SERVER);
 
-    erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
-    set_logerror(logerror);
     startclock();
     section_start = curclock();
 
     our_features = am_init_feature_set();
     our_feature_string = am_feature_to_string(our_features);
 
-    fprintf(stderr, "%s: pid %ld executable %s version %s\n",
-           get_pname(), (long) getpid(), argv[0], version());
+    log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
+    g_fprintf(stderr, _("%s: pid %ld executable %s version %s\n"),
+           get_pname(), (long) getpid(), argv[0], VERSION);
     for (i = 0; version_info[i] != NULL; i++)
-       fprintf(stderr, "%s: %s", get_pname(), version_info[i]);
+       g_fprintf(stderr, _("%s: %s"), get_pname(), version_info[i]);
 
-    /*
-     * 1. Networking Setup
-     *
-     * Planner runs setuid to get a priviledged socket for BSD security.
-     * We get the socket right away as root, then setuid back to a normal
-     * user.  If we are not using BSD security, planner is not installed
-     * setuid root.
-     */
+    diskarg_offset = 2;
+    if (argc - diskarg_offset > 1 && strcmp(argv[diskarg_offset], "--starttime") == 0) {
+       planner_timestamp = stralloc(argv[diskarg_offset+1]);
+       diskarg_offset += 2;
+    }
+    if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--no-taper") == 0) {
+       no_taper = TRUE;
+       diskarg_offset += 1;
+    }
+    if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--from-client") == 0) {
+       from_client = TRUE;
+       diskarg_offset += 1;
+    }
+    if (argc - diskarg_offset > 0 && g_str_equal(argv[diskarg_offset],
+                                                 "--exact_match")) {
+       exact_match = TRUE;
+       diskarg_offset += 1;
+    }
 
-    protocol_init();
 
-    if(geteuid() == 0) {
-       uid_t ruid = getuid();
-       setuid(0);
-       seteuid(ruid);
-       setgid(getgid());
-    }
+    run_server_global_scripts(EXECUTE_ON_PRE_ESTIMATE, get_config_name());
 
     /*
-     * From this point on we are running under our real uid, so we don't
-     * have to worry about opening security holes below.  Make sure we
-     * are a valid user.
+     * 1. Networking Setup
+     *
      */
 
-    if(getpwuid(getuid()) == NULL)
-       error("can't get login name for my uid %ld", (long)getuid());
+    protocol_init();
 
     /*
      * 2. Read in Configuration Information
@@ -239,66 +300,101 @@ char **argv;
      * All the Amanda configuration files are loaded before we begin.
      */
 
-    fprintf(stderr,"READING CONF FILES...\n");
+    g_fprintf(stderr,_("READING CONF INFO...\n"));
 
-    conffile = stralloc2(config_dir, CONFFILE_NAME);
-    if(read_conffile(conffile)) {
-       error("errors processing config file \"%s\"", conffile);
+    if(origq.head == NULL) {
+       error(_("empty disklist \"%s\""), conf_diskfile);
+       /*NOTREACHED*/
     }
-    amfree(conffile);
 
-    conf_diskfile = getconf_str(CNF_DISKFILE);
-    if (*conf_diskfile == '/') {
-       conf_diskfile = stralloc(conf_diskfile);
-    } else {
-       conf_diskfile = stralloc2(config_dir, conf_diskfile);
-    }
-    if (read_diskfile(conf_diskfile, &origq) < 0) {
-       error("could not load disklist \"%s\"", conf_diskfile);
-    }
-    match_disklist(&origq, argc-2, argv+2);
-    for(dp = origq.head; dp != NULL; dp = dp->next) {
-       if(dp->todo)
-           log_add(L_DISK, "%s %s", dp->host->hostname, dp->name);
-    }
     amfree(conf_diskfile);
 
-    conf_tapelist = getconf_str(CNF_TAPELIST);
-    if (*conf_tapelist == '/') {
-       conf_tapelist = stralloc(conf_tapelist);
-    } else {
-       conf_tapelist = stralloc2(config_dir, conf_tapelist);
-    }
+    conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
     if(read_tapelist(conf_tapelist)) {
-       error("could not load tapelist \"%s\"", conf_tapelist);
+       error(_("could not load tapelist \"%s\""), conf_tapelist);
+       /*NOTREACHED*/
     }
     amfree(conf_tapelist);
 
-    conf_infofile = getconf_str(CNF_INFOFILE);
-    if (*conf_infofile == '/') {
-       conf_infofile = stralloc(conf_infofile);
-    } else {
-       conf_infofile = stralloc2(config_dir, conf_infofile);
-    }
+    conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
     if(open_infofile(conf_infofile)) {
-       error("could not open info db \"%s\"", conf_infofile);
+       error(_("could not open info db \"%s\""), conf_infofile);
+       /*NOTREACHED*/
+    }
+    if (check_infofile(conf_infofile, &origq, &errstr) == -1) {
+       log_add(L_WARNING, "problem copying infofile: %s", errstr);
+       amfree(errstr);
     }
     amfree(conf_infofile);
 
     conf_tapetype = getconf_str(CNF_TAPETYPE);
-    conf_maxdumpsize = getconf_am64(CNF_MAXDUMPSIZE);
+    conf_maxdumpsize = getconf_int64(CNF_MAXDUMPSIZE);
     conf_runtapes = getconf_int(CNF_RUNTAPES);
     conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
     conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
     conf_tapecycle = getconf_int(CNF_TAPECYCLE);
-    conf_etimeout = getconf_int(CNF_ETIMEOUT);
+    conf_etimeout = (time_t)getconf_int(CNF_ETIMEOUT);
     conf_reserve  = getconf_int(CNF_RESERVE);
-    conf_autoflush = getconf_int(CNF_AUTOFLUSH);
+    conf_autoflush = getconf_no_yes_all(CNF_AUTOFLUSH);
+    conf_usetimestamps = getconf_boolean(CNF_USETIMESTAMPS);
 
-    amfree(datestamp);
     today = time(0);
-    datestamp = construct_datestamp(NULL);
-    log_add(L_START, "date %s", datestamp);
+    if (planner_timestamp) {
+       if (conf_usetimestamps == 0) {
+           planner_timestamp[8] = '\0';
+       }
+    } else if(conf_usetimestamps == 0) {
+       planner_timestamp = get_datestamp_from_time(0);
+    }
+    else {
+       planner_timestamp = get_timestamp_from_time(0);
+    }
+    log_add(L_START, _("date %s"), planner_timestamp);
+    g_printf("DATE %s\n", planner_timestamp);
+    fflush(stdout);
+    g_fprintf(stderr, _("%s: timestamp %s\n"),
+                   get_pname(), planner_timestamp);
+
+    errstr = match_disklist(&origq, exact_match, argc-diskarg_offset,
+                                   argv+diskarg_offset);
+    if (errstr) {
+       g_fprintf(stderr,"%s",errstr);
+        exit_status = EXIT_FAILURE;
+    }
+
+    for (dp = origq.head; dp != NULL; dp = dp->next) {
+       if (dp->todo) {
+           if (from_client) {
+               if (!dp->dump_limit || !dp->dump_limit->same_host)
+                   dp->todo = 0;
+           } else {
+               if (dp->dump_limit && !dp->dump_limit->server)
+                   dp->todo = 0;
+           }
+       }
+    }
+
+    nb_disk = 0;
+    for (dp = origq.head; dp != NULL; dp = dp->next) {
+       if (dp->todo) {
+           qname = quote_string(dp->name);
+           log_add(L_DISK, "%s %s", dp->host->hostname, qname);
+           amfree(qname);
+           nb_disk++;
+       }
+    }
+
+    if (nb_disk == 0) {
+       if (errstr) {
+           error(_("no DLE to backup; %s"), errstr);
+       } else {
+           error(_("no DLE to backup"));
+       }
+       /*NOTREACHED*/
+    } else if (errstr) {
+       log_add(L_WARNING, "WARNING: %s", errstr);
+    }
+    amfree(errstr);
 
     /* some initializations */
 
@@ -317,22 +413,28 @@ char **argv;
      * do some basic sanity checking
      */
      if(conf_tapecycle <= runs_per_cycle) {
-       log_add(L_WARNING, "tapecycle (%d) <= runspercycle (%d)",
+       log_add(L_WARNING, _("tapecycle (%d) <= runspercycle (%d)"),
                conf_tapecycle, runs_per_cycle);
      }
     
     tape = lookup_tapetype(conf_tapetype);
-    if(conf_maxdumpsize > 0) {
+    if(conf_maxdumpsize > (gint64)0) {
        tape_length = conf_maxdumpsize;
+       g_fprintf(stderr, "planner: tape_length is set from maxdumpsize (%jd KB)\n",
+                        (intmax_t)conf_maxdumpsize);
     }
     else {
-       tape_length = tape->length * conf_runtapes;
-    }
-    tape_mark   = tape->filemark;
-    tt_blocksize_kb = tape->blocksize;
+       tape_length = tapetype_get_length(tape) * (gint64)conf_runtapes;
+       g_fprintf(stderr, "planner: tape_length is set from tape length (%jd KB) * runtapes (%d) == %jd KB\n",
+                        (intmax_t)tapetype_get_length(tape),
+                        conf_runtapes,
+                        (intmax_t)tape_length);
+    }
+    tape_mark = (size_t)tapetype_get_filemark(tape);
+    tt_blocksize_kb = (size_t)tapetype_get_blocksize(tape);
     tt_blocksize = tt_blocksize_kb * 1024;
 
-    fprintf(stderr, "%s: time %s: startup took %s secs\n",
+    g_fprintf(stderr, _("%s: time %s: startup took %s secs\n"),
                    get_pname(),
                    walltime_str(curclock()),
                    walltime_str(timessub(curclock(), section_start)));
@@ -344,38 +446,61 @@ char **argv;
      * dump schedule.
      */
 
-    fprintf(stderr,"\nSENDING FLUSHES...\n");
+    g_fprintf(stderr,_("\nSENDING FLUSHES...\n"));
+
+    if(conf_autoflush && !no_taper) {
+       dumpfile_t  file;
+       GSList *holding_list, *holding_file;
+       char *qdisk, *qhname;
 
-    if(conf_autoflush) {
-       dumpfile_t file;
-       sl_t *holding_list;
-       sle_t *holding_file;
-       holding_list = get_flush(NULL, NULL, 0, 0);
-       for(holding_file=holding_list->first; holding_file != NULL;
+       /* get *all* flushable files in holding, without checking against
+        * the disklist (which may not contain some of the dumps) */
+       holding_list = holding_get_files_for_flush(NULL);
+       for(holding_file=holding_list; holding_file != NULL;
                                       holding_file = holding_file->next) {
-           get_dumpfile(holding_file->name, &file);
-           
-           log_add(L_DISK, "%s %s", file.name, file.disk);
-           fprintf(stderr,
+           holding_file_get_dumpfile((char *)holding_file->data, &file);
+
+           if (holding_file_size((char *)holding_file->data, 1) <= 0) {
+               log_add(L_INFO, "%s: removing file with no data.",
+                       (char *)holding_file->data);
+               holding_file_unlink((char *)holding_file->data);
+               dumpfile_free_data(&file);
+               continue;
+           }
+
+           /* see if this matches the command-line arguments */
+           if (conf_autoflush == 1 &&
+               !match_dumpfile(&file, exact_match, argc-diskarg_offset,
+                                      argv+diskarg_offset)) {
+               continue;
+           }
+
+           qdisk = quote_string(file.disk);
+           qhname = quote_string((char *)holding_file->data);
+           log_add(L_DISK, "%s %s", file.name, qdisk);
+           g_fprintf(stderr,
                    "FLUSH %s %s %s %d %s\n",
                    file.name,
-                   file.disk,
+                   qdisk,
                    file.datestamp,
                    file.dumplevel,
-                   holding_file->name);
-           fprintf(stdout,
+                   qhname);
+           g_fprintf(stdout,
                    "FLUSH %s %s %s %d %s\n",
                    file.name,
-                   file.disk,
+                   qdisk,
                    file.datestamp,
                    file.dumplevel,
-                   holding_file->name);
+                   qhname);
+           amfree(qdisk);
+           amfree(qhname);
+           dumpfile_free_data(&file);
        }
-       free_sl(holding_list);
+       slist_free_full(holding_list, g_free);
        holding_list = NULL;
     }
-    fprintf(stderr, "ENDFLUSH\n");
-    fprintf(stdout, "ENDFLUSH\n");
+    g_fprintf(stderr, _("ENDFLUSH\n"));
+    g_fprintf(stdout, _("ENDFLUSH\n"));
     fflush(stdout);
 
     /*
@@ -386,7 +511,7 @@ char **argv;
      * based on the curinfo database.
      */
 
-    fprintf(stderr,"\nSETTING UP FOR ESTIMATES...\n");
+    g_fprintf(stderr,_("\nSETTING UP FOR ESTIMATES...\n"));
     section_start = curclock();
 
     startq.head = startq.tail = NULL;
@@ -397,7 +522,7 @@ char **argv;
        }
     }
 
-    fprintf(stderr, "%s: time %s: setting up estimates took %s secs\n",
+    g_fprintf(stderr, _("%s: time %s: setting up estimates took %s secs\n"),
                    get_pname(),
                    walltime_str(curclock()),
                    walltime_str(timessub(curclock(), section_start)));
@@ -412,7 +537,7 @@ char **argv;
 
     /* go out and get the dump estimates */
 
-    fprintf(stderr,"\nGETTING ESTIMATES...\n");
+    g_fprintf(stderr,_("\nGETTING ESTIMATES...\n"));
     section_start = curclock();
 
     estq.head = estq.tail = NULL;
@@ -422,7 +547,7 @@ char **argv;
 
     get_estimates();
 
-    fprintf(stderr, "%s: time %s: getting estimates took %s secs\n",
+    g_fprintf(stderr, _("%s: time %s: getting estimates took %s secs\n"),
                    get_pname(),
                    walltime_str(curclock()),
                    walltime_str(timessub(curclock(), section_start)));
@@ -436,6 +561,9 @@ char **argv;
     dump_queue("FAILED", failq, 15, stderr);
     dump_queue("DONE", estq, 15, stderr);
 
+    if (!empty(failq)) {
+        exit_status = EXIT_FAILURE;
+    }
 
     /*
      * 6. Analyze Dump Estimates
@@ -446,11 +574,11 @@ char **argv;
      * total expected size is accumulated as well.
      */
 
-    fprintf(stderr,"\nANALYZING ESTIMATES...\n");
+    g_fprintf(stderr,_("\nANALYZING ESTIMATES...\n"));
     section_start = curclock();
 
                        /* an empty tape still has a label and an endmark */
-    total_size = (tt_blocksize_kb + tape_mark) * 2;
+    total_size = ((gint64)tt_blocksize_kb + (gint64)tape_mark) * (gint64)2;
     total_lev0 = 0.0;
     balanced_size = 0.0;
 
@@ -458,6 +586,8 @@ char **argv;
     while(!empty(estq)) analyze_estimate(dequeue_disk(&estq));
     while(!empty(failq)) handle_failed(dequeue_disk(&failq));
 
+    run_server_global_scripts(EXECUTE_ON_POST_ESTIMATE, get_config_name());
+
     /*
      * At this point, all the disks are on schedq sorted by priority.
      * The total estimated size of the backups is in total_size.
@@ -466,11 +596,16 @@ char **argv;
     {
        disk_t *dp;
 
-       fprintf(stderr, "INITIAL SCHEDULE (size " AM64_FMT "):\n", total_size);
+       g_fprintf(stderr, _("INITIAL SCHEDULE (size %lld):\n"),
+               (long long)total_size);
        for(dp = schedq.head; dp != NULL; dp = dp->next) {
-           fprintf(stderr, "  %s %s pri %d lev %d size %ld\n",
-                   dp->host->hostname, dp->name, est(dp)->dump_priority,
-                   est(dp)->dump_level, est(dp)->dump_size);
+           qname = quote_string(dp->name);
+           g_fprintf(stderr, _("  %s %s pri %d lev %d nsize %lld csize %lld\n"),
+                   dp->host->hostname, qname, est(dp)->dump_priority,
+                   est(dp)->dump_est->level,
+                   (long long)est(dp)->dump_est->nsize,
+                    (long long)est(dp)->dump_est->csize);
+           amfree(qname);
        }
     }
 
@@ -487,17 +622,20 @@ char **argv;
      * until the dumps fit on the tape.
      */
 
-    fprintf(stderr,
-      "\nDELAYING DUMPS IF NEEDED, total_size " AM64_FMT ", tape length " AM64_FMT " mark " AM64_FMT "\n",
-           total_size, tape_length, tape_mark);
+    g_fprintf(stderr, _("\nDELAYING DUMPS IF NEEDED, total_size %lld, tape length %lld mark %zu\n"),
+           (long long)total_size,
+           (long long)tape_length,
+           tape_mark);
 
     initial_size = total_size;
 
     delay_dumps();
 
     /* XXX - why bother checking this? */
-    if(empty(schedq) && total_size < initial_size)
-       error("cannot fit anything on tape, bailing out");
+    if(empty(schedq) && total_size < initial_size) {
+       error(_("cannot fit anything on tape, bailing out"));
+       /*NOTREACHED*/
+    }
 
 
     /*
@@ -518,8 +656,8 @@ char **argv;
      * a big bump.
      */
 
-    fprintf(stderr,
-     "\nPROMOTING DUMPS IF NEEDED, total_lev0 %1.0f, balanced_size %1.0f...\n",
+    g_fprintf(stderr,
+     _("\nPROMOTING DUMPS IF NEEDED, total_lev0 %1.0lf, balanced_size %1.0lf...\n"),
            total_lev0, balanced_size);
 
     balance_threshold = balanced_size * PROMOTE_THRESHOLD;
@@ -529,7 +667,7 @@ char **argv;
 
     moved_one = promote_hills();
 
-    fprintf(stderr, "%s: time %s: analysis took %s secs\n",
+    g_fprintf(stderr, _("%s: time %s: analysis took %s secs\n"),
                    get_pname(),
                    walltime_str(curclock()),
                    walltime_str(timessub(curclock(), section_start)));
@@ -542,28 +680,28 @@ char **argv;
      * on stderr for the debug file.
      */
 
-    fprintf(stderr,"\nGENERATING SCHEDULE:\n--------\n");
-
-    while(!empty(schedq)) output_scheduleline(dequeue_disk(&schedq));
-    fprintf(stderr, "--------\n");
+    g_fprintf(stderr,_("\nGENERATING SCHEDULE:\n--------\n"));
+    if (empty(schedq)) {
+        exit_status = EXIT_FAILURE;
+        g_fprintf(stderr, _("--> Generated empty schedule! <--\n"));
+    } else {
+        while(!empty(schedq)) output_scheduleline(dequeue_disk(&schedq));
+    }
+    g_fprintf(stderr, _("--------\n"));
 
     close_infofile();
-    log_add(L_FINISH, "date %s time %s", datestamp, walltime_str(curclock()));
+    log_add(L_FINISH, _("date %s time %s"), planner_timestamp, walltime_str(curclock()));
+    log_add(L_INFO, "pid-done %ld", (long)getpid());
 
-    amfree(datestamp);
-    amfree(config_dir);
-    amfree(config_name);
+    clear_tapelist();
+    amfree(planner_timestamp);
     amfree(our_feature_string);
     am_release_feature_set(our_features);
     our_features = NULL;
 
-    malloc_size_2 = malloc_inuse(&malloc_hist_2);
-
-    if(malloc_size_1 != malloc_size_2) {
-       malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
-    }
+    dbclose();
 
-    return 0;
+    return exit_status;
 }
 
 
@@ -574,81 +712,126 @@ char **argv;
  *
  */
 
-static void askfor P((est_t *, int, int, info_t *));
-static int last_level P((info_t *info));                 /* subroutines */
-static long est_size P((disk_t *dp, int level));
-static long est_tape_size P((disk_t *dp, int level));
-static int next_level0 P((disk_t *dp, info_t *info));
-static int runs_at P((info_t *info, int lev));
-static long bump_thresh P((int level, long size_level_0, int bumppercent, int bumpsize, double bumpmult));
-static int when_overwrite P((char *label));
-
-static void askfor(ep, seq, lev, info)
-est_t *ep;     /* esimate data block */
-int seq;       /* sequence number of request */
-int lev;       /* dump level being requested */
-info_t *info;  /* info block for disk */
+static void askfor(est_t *, int, int, info_t *);
+static int last_level(info_t *info);             /* subroutines */
+static one_est_t *est_for_level(disk_t *dp, int level);
+static void est_csize(disk_t *dp, one_est_t *one_est);
+static gint64 est_tape_size(disk_t *dp, int level);
+static int next_level0(disk_t *dp, info_t *info);
+static int runs_at(info_t *info, int lev);
+static gint64 bump_thresh(int level, gint64 size_level_0, int bumppercent, gint64 bumpsize, double bumpmult);
+static int when_overwrite(char *label);
+
+static void askfor(
+    est_t *ep, /* esimate data block */
+    int seq,   /* sequence number of request */
+    int lev,   /* dump level being requested */
+    info_t *info)      /* info block for disk */
 {
     if(seq < 0 || seq >= MAX_LEVELS) {
-       error("error [planner askfor: seq out of range 0..%d: %d]",
+       error(_("error [planner askfor: seq out of range 0..%d: %d]"),
              MAX_LEVELS, seq);
+       /*NOTREACHED*/
     }
     if(lev < -1 || lev >= DUMP_LEVELS) {
-       error("error [planner askfor: lev out of range -1..%d: %d]",
+       error(_("error [planner askfor: lev out of range -1..%d: %d]"),
              DUMP_LEVELS, lev);
+       /*NOTREACHED*/
     }
 
     if (lev == -1) {
-       ep->level[seq] = -1;
-       ep->dumpdate[seq] = (char *)0;
-       ep->est_size[seq] = -2;
+       ep->estimate[seq].level = -1;
+       ep->estimate[seq].dumpdate = (char *)0;
+       ep->estimate[seq].nsize = (gint64)-3;
+       ep->estimate[seq].csize = (gint64)-3;
+       ep->estimate[seq].guessed = 0;
        return;
     }
 
-    ep->level[seq] = lev;
+    ep->estimate[seq].level = lev;
 
-    ep->dumpdate[seq] = stralloc(get_dumpdate(info,lev));
-    malloc_mark(ep->dumpdate[seq]);
+    ep->estimate[seq].dumpdate = stralloc(get_dumpdate(info,lev));
 
-    ep->est_size[seq] = -2;
+    ep->estimate[seq].nsize = (gint64)-3;
+    ep->estimate[seq].csize = (gint64)-3;
+    ep->estimate[seq].guessed = 0;
 
     return;
 }
 
 static void
-setup_estimate(dp)
-     disk_t *dp;
+setup_estimate(
+     disk_t *dp)
 {
     est_t *ep;
-    info_t info;
+    info_t *info;
     int i;
+    char *qname;
+    int overwrite_runs;
 
     assert(dp && dp->host);
-    fprintf(stderr, "%s: time %s: setting up estimates for %s:%s\n",
+
+    qname = quote_string(dp->name);
+    g_fprintf(stderr, _("%s: time %s: setting up estimates for %s:%s\n"),
                    get_pname(), walltime_str(curclock()),
-                   dp->host->hostname, dp->name);
+                   dp->host->hostname, qname);
 
     /* get current information about disk */
 
-    if(get_info(dp->host->hostname, dp->name, &info)) {
+    info = g_new0(info_t, 1);
+    if(get_info(dp->host->hostname, dp->name, info)) {
        /* no record for this disk, make a note of it */
-       log_add(L_INFO, "Adding new disk %s:%s.", dp->host->hostname, dp->name);
+       log_add(L_INFO, _("Adding new disk %s:%s."), dp->host->hostname, qname);
+    }
+
+    if (dp->data_path == DATA_PATH_DIRECTTCP) {
+       if (dp->compress != COMP_NONE) {
+           log_add(L_FAIL, _("%s %s %s 0 [Can't compress directtcp data-path]"),
+                   dp->host->hostname, qname, planner_timestamp);
+           g_fprintf(stderr,_("%s:%s lev 0 skipped can't compress directtcp data-path\n"),
+                     dp->host->hostname, qname);
+           amfree(qname);
+           return;
+       }
+       if (dp->encrypt != ENCRYPT_NONE) {
+           log_add(L_FAIL, _("%s %s %s 0 [Can't encrypt directtcp data-path]"),
+                   dp->host->hostname, qname, planner_timestamp);
+           g_fprintf(stderr,_("%s:%s lev 0 skipped can't encrypt directtcp data-path\n"),
+                     dp->host->hostname, qname);
+           amfree(qname);
+           return;
+       }
+       if (dp->to_holdingdisk == HOLD_REQUIRED) {
+           log_add(L_FAIL, _("%s %s %s 0 [Holding disk can't be use for directtcp data-path]"),
+                   dp->host->hostname, qname, planner_timestamp);
+           g_fprintf(stderr,_("%s:%s lev 0 skipped Holding disk can't be use for directtcp data-path\n"),
+                     dp->host->hostname, qname);
+           amfree(qname);
+           return;
+       } else if (dp->to_holdingdisk == HOLD_AUTO) {
+           g_fprintf(stderr,_("%s:%s Disabling holding disk\n"),
+                     dp->host->hostname, qname);
+           dp->to_holdingdisk = HOLD_NEVER;
+       }
     }
 
     /* setup working data struct for disk */
 
-    ep = alloc(sizeof(est_t));
-    malloc_mark(ep);
+    ep = alloc(SIZEOF(est_t));
     dp->up = (void *) ep;
+    ep->info = info;
     ep->state = DISK_READY;
-    ep->dump_size = -1;
     ep->dump_priority = dp->priority;
     ep->errstr = 0;
     ep->promote = 0;
+    ep->post_dle = 0;
+    ep->degr_mesg = NULL;
+    ep->dump_est = &default_one_est;
+    ep->degr_est = &default_one_est;
 
     /* calculated fields */
 
-    if (ISSET(info.command, FORCE_FULL)) {
+    if (ISSET(info->command, FORCE_FULL)) {
        /* force a level 0, kind of like a new disk */
        if(dp->strategy == DS_NOFULL) {
            /*
@@ -662,59 +845,84 @@ setup_estimate(dp)
             * hosed when that tape gets re-used next.  Disallow this for
             * now.
             */
-           log_add(L_ERROR,
-                   "Cannot force full dump of %s:%s with no-full option.",
-                   dp->host->hostname, dp->name);
+           log_add(L_WARNING,
+                   _("Cannot force full dump of %s:%s with no-full option."),
+                   dp->host->hostname, qname);
 
            /* clear force command */
-           CLR(info.command, FORCE_FULL);
-           if(put_info(dp->host->hostname, dp->name, &info))
-               error("could not put info record for %s:%s: %s",
-                     dp->host->hostname, dp->name, strerror(errno));
-           ep->last_level = last_level(&info);
-           ep->next_level0 = next_level0(dp, &info);
-       }
-       else {
+           CLR(info->command, FORCE_FULL);
+           ep->last_level = last_level(info);
+           ep->next_level0 = next_level0(dp, info);
+       } else if (dp->strategy == DS_INCRONLY) {
+           log_add(L_WARNING,
+                   _("Cannot force full dump of %s:%s with incronly option."),
+                   dp->host->hostname, qname);
+
+           /* clear force command */
+           CLR(info->command, FORCE_FULL);
+           ep->last_level = last_level(info);
+           ep->next_level0 = next_level0(dp, info);
+       } else {
+           ep->degr_mesg = _("Skipping: force-full disk can't be dumped in degraded mode");
            ep->last_level = -1;
            ep->next_level0 = -conf_dumpcycle;
-           log_add(L_INFO, "Forcing full dump of %s:%s as directed.",
-                   dp->host->hostname, dp->name);
+           log_add(L_INFO, _("Forcing full dump of %s:%s as directed."),
+                   dp->host->hostname, qname);
        }
     }
     else if(dp->strategy == DS_NOFULL) {
        /* force estimate of level 1 */
        ep->last_level = 1;
-       ep->next_level0 = next_level0(dp, &info);
+       ep->next_level0 = next_level0(dp, info);
     }
     else {
-       ep->last_level = last_level(&info);
-       ep->next_level0 = next_level0(dp, &info);
+       ep->last_level = last_level(info);
+       ep->next_level0 = next_level0(dp, info);
     }
 
     /* adjust priority levels */
 
+    /* warn if dump will be overwritten */
+    if (ep->last_level > -1 && strlen(info->inf[0].label) > 0) {
+       overwrite_runs = when_overwrite(info->inf[0].label);
+       if(overwrite_runs == 0) {
+           log_add(L_WARNING, _("Last full dump of %s:%s "
+                   "on tape %s overwritten on this run."),
+                   dp->host->hostname, qname, info->inf[0].label);
+       } else if(overwrite_runs <= RUNS_REDZONE) {
+           log_add(L_WARNING,
+                   plural(_("Last full dump of %s:%s on tape %s overwritten in %d run."),
+                          _("Last full dump of %s:%s on tape %s overwritten in %d runs."), overwrite_runs),
+                   dp->host->hostname, qname, info->inf[0].label,
+                   overwrite_runs);
+       }
+    }
+
+    /* warn if last level 1 will be overwritten */
+    if (ep->last_level > 1 && strlen(info->inf[1].label) > 0) {
+       overwrite_runs = when_overwrite(info->inf[1].label);
+       if(overwrite_runs == 0) {
+           log_add(L_WARNING, _("Last level 1 dump of %s:%s "
+                   "on tape %s overwritten on this run, resetting to level 1"),
+                   dp->host->hostname, qname, info->inf[1].label);
+           ep->last_level = 0;
+       } else if(overwrite_runs <= RUNS_REDZONE) {
+           log_add(L_WARNING,
+                   plural(_("Last level 1 dump of %s:%s on tape %s overwritten in %d run."),
+                          _("Last level 1 dump of %s:%s on tape %s overwritten in %d runs."), overwrite_runs),
+                   dp->host->hostname, qname, info->inf[1].label,
+                   overwrite_runs);
+       }
+    }
+
     if(ep->next_level0 < 0) {
-       fprintf(stderr,"%s:%s overdue %d day%s for level 0\n",
-               dp->host->hostname, dp->name,
-               - ep->next_level0, ((- ep->next_level0) == 1) ? "" : "s");
+       g_fprintf(stderr,plural(_("%s:%s overdue %d day for level 0\n"),
+                             _("%s:%s overdue %d days for level 0\n"),
+                             (-ep->next_level0)),
+               dp->host->hostname, qname, (-ep->next_level0));
        ep->dump_priority -= ep->next_level0;
-       /* warn if dump will be overwritten */
-       if(ep->last_level > -1) {
-           int overwrite_runs = when_overwrite(info.inf[0].label);
-           if(overwrite_runs == 0) {
-               log_add(L_WARNING,
-                 "Last full dump of %s:%s on tape %s overwritten on this run.",
-                       dp->host->hostname, dp->name, info.inf[0].label);
-           }
-           else if(overwrite_runs < RUNS_REDZONE) {
-               log_add(L_WARNING,
-                 "Last full dump of %s:%s on tape %s overwritten in %d run%s.",
-                       dp->host->hostname, dp->name, info.inf[0].label,
-                   overwrite_runs, overwrite_runs == 1? "" : "s");
-           }
-       }
     }
-    else if (ISSET(info.command, FORCE_FULL))
+    else if (ISSET(info->command, FORCE_FULL))
        ep->dump_priority += 1;
     /* else XXX bump up the priority of incrementals that failed last night */
 
@@ -723,25 +931,28 @@ setup_estimate(dp)
     if(dp->skip_full && dp->strategy != DS_NOINC) {
        if(ep->next_level0 <= 0) {
            /* update the date field */
-           info.inf[0].date = today;
-           CLR(info.command, FORCE_FULL);
+           info->inf[0].date = today;
+           CLR(info->command, FORCE_FULL);
            ep->next_level0 += conf_dumpcycle;
            ep->last_level = 0;
-           if(put_info(dp->host->hostname, dp->name, &info))
-               error("could not put info record for %s:%s: %s",
-                     dp->host->hostname, dp->name, strerror(errno));
-           log_add(L_INFO, "Skipping full dump of %s:%s today.",
-                   dp->host->hostname, dp->name);
-           fprintf(stderr,"%s:%s lev 0 skipped due to skip-full flag\n",
-                   dp->host->hostname, dp->name);
+           if(put_info(dp->host->hostname, dp->name, info)) {
+               error(_("could not put info record for %s:%s: %s"),
+                     dp->host->hostname, qname, strerror(errno));
+               /*NOTREACHED*/
+           }
+           log_add(L_INFO, _("Skipping full dump of %s:%s today."),
+                   dp->host->hostname, qname);
+           g_fprintf(stderr,_("%s:%s lev 0 skipped due to skip-full flag\n"),
+                   dp->host->hostname, qname);
            /* don't enqueue the disk */
-           askfor(ep, 0, -1, &info);
-           askfor(ep, 1, -1, &info);
-           askfor(ep, 2, -1, &info);
-           fprintf(stderr, "%s: SKIPPED %s %s 0 [skip-full]\n",
-                   get_pname(), dp->host->hostname, dp->name);
-           log_add(L_SUCCESS, "%s %s %s 0 [skipped: skip-full]",
-                   dp->host->hostname, dp->name, datestamp);
+           askfor(ep, 0, -1, info);
+           askfor(ep, 1, -1, info);
+           askfor(ep, 2, -1, info);
+           g_fprintf(stderr, _("%s: SKIPPED %s %s 0 [skip-full]\n"),
+                   get_pname(), dp->host->hostname, qname);
+           log_add(L_SUCCESS, _("%s %s %s 0 [skipped: skip-full]"),
+                   dp->host->hostname, qname, planner_timestamp);
+           amfree(qname);
            return;
        }
 
@@ -751,59 +962,62 @@ setup_estimate(dp)
        }
 
        if(ep->next_level0 == 1) {
-           log_add(L_WARNING, "Skipping full dump of %s:%s tomorrow.",
-                   dp->host->hostname, dp->name);
+           log_add(L_WARNING, _("Skipping full dump of %s:%s tomorrow."),
+                   dp->host->hostname, qname);
        }
     }
 
-    if(dp->strategy == DS_INCRONLY && ep->last_level == -1 && !ISSET(info.command, FORCE_FULL)) {
+    if(dp->strategy == DS_INCRONLY && ep->last_level == -1 && !ISSET(info->command, FORCE_FULL)) {
        /* don't enqueue the disk */
-       askfor(ep, 0, -1, &info);
-       askfor(ep, 1, -1, &info);
-       askfor(ep, 2, -1, &info);
-       log_add(L_FAIL, "%s %s 19000101 1 [Skipping incronly because no full dump were done]",
-               dp->host->hostname, dp->name);
-       fprintf(stderr,"%s:%s lev 1 skipped due to strategy incronly and no full dump were done\n",
-               dp->host->hostname, dp->name);
+       askfor(ep, 0, -1, info);
+       askfor(ep, 1, -1, info);
+       askfor(ep, 2, -1, info);
+       log_add(L_FAIL, _("%s %s 19000101 1 [Skipping incronly because no full dump were done]"),
+               dp->host->hostname, qname);
+       g_fprintf(stderr,_("%s:%s lev 1 skipped due to strategy incronly and no full dump were done\n"),
+               dp->host->hostname, qname);
+       amfree(qname);
        return;
     }
 
     /* handle "skip-incr" type archives */
 
     if(dp->skip_incr && ep->next_level0 > 0) {
-       fprintf(stderr,"%s:%s lev 1 skipped due to skip-incr flag\n",
-               dp->host->hostname, dp->name);
+       g_fprintf(stderr,_("%s:%s lev 1 skipped due to skip-incr flag\n"),
+               dp->host->hostname, qname);
        /* don't enqueue the disk */
-       askfor(ep, 0, -1, &info);
-       askfor(ep, 1, -1, &info);
-       askfor(ep, 2, -1, &info);
+       askfor(ep, 0, -1, info);
+       askfor(ep, 1, -1, info);
+       askfor(ep, 2, -1, info);
 
-       fprintf(stderr, "%s: SKIPPED %s %s 1 [skip-incr]\n",
-               get_pname(), dp->host->hostname, dp->name);
+       g_fprintf(stderr, _("%s: SKIPPED %s %s 1 [skip-incr]\n"),
+               get_pname(), dp->host->hostname, qname);
 
-       log_add(L_SUCCESS, "%s %s %s 1 [skipped: skip-incr]",
-               dp->host->hostname, dp->name, datestamp);
+       log_add(L_SUCCESS, _("%s %s %s 1 [skipped: skip-incr]"),
+               dp->host->hostname, qname, planner_timestamp);
+       amfree(qname);
        return;
     }
 
     if( ep->last_level == -1 && ep->next_level0 > 0 && 
        dp->strategy != DS_NOFULL && dp->strategy != DS_INCRONLY &&
        conf_reserve == 100) {
-       log_add(L_WARNING,
-            "%s:%s mismatch: no tapelist record, but curinfo next_level0: %d.",
-               dp->host->hostname, dp->name, ep->next_level0);
+       log_add(L_WARNING, _("%s:%s mismatch: no tapelist record, "
+               "but curinfo next_level0: %d."),
+               dp->host->hostname, qname, ep->next_level0);
        ep->next_level0 = 0;
     }
 
-    if(ep->last_level == 0) ep->level_days = 0;
-    else ep->level_days = runs_at(&info, ep->last_level);
-    ep->last_lev0size = info.inf[0].csize;
+    //if(ep->last_level == 0) ep->level_days = 0;
+    //else ep->level_days = runs_at(info, ep->last_level);
+    ep->level_days = runs_at(info, ep->last_level);
+    ep->last_lev0size = info->inf[0].csize;
 
-    ep->fullrate = perf_average(info.full.rate, 0.0);
-    ep->incrrate = perf_average(info.incr.rate, 0.0);
+    ep->fullrate = perf_average(info->full.rate, 0.0);
+    ep->incrrate = perf_average(info->incr.rate, 0.0);
 
-    ep->fullcomp = perf_average(info.full.comp, dp->comprate[0]);
-    ep->incrcomp = perf_average(info.incr.comp, dp->comprate[1]);
+    ep->fullcomp = perf_average(info->full.comp, dp->comprate[0]);
+    ep->incrcomp = perf_average(info->incr.comp, dp->comprate[1]);
 
     /* determine which estimates to get */
 
@@ -811,27 +1025,31 @@ setup_estimate(dp)
 
     if (dp->strategy == DS_NOINC ||
        (!dp->skip_full &&
-        (!ISSET(info.command, FORCE_BUMP) ||
+        (!ISSET(info->command, FORCE_BUMP) ||
          dp->skip_incr ||
          ep->last_level == -1))) {
-       if(info.command & FORCE_BUMP && ep->last_level == -1) {
+       if(ISSET(info->command, FORCE_BUMP) && ep->last_level == -1) {
            log_add(L_INFO,
-                 "Remove force-bump command of %s:%s because it's a new disk.",
-                   dp->host->hostname, dp->name);
+                 _("Remove force-bump command of %s:%s because it's a new disk."),
+                   dp->host->hostname, qname);
        }
        switch (dp->strategy) {
        case DS_STANDARD: 
        case DS_NOINC:
-           askfor(ep, i++, 0, &info);
+           askfor(ep, i++, 0, info);
+           if (ep->last_level == -1)
+               ep->degr_mesg = _("Skipping: new disk can't be dumped in degraded mode");
+           else
+               ep->degr_mesg = _("Skipping: strategy NOINC can't be dumped in degraded mode");
            if(dp->skip_full) {
-               log_add(L_INFO,
-                  "Ignoring skip_full for %s:%s because the strategy is NOINC.",
-                       dp->host->hostname, dp->name);
+               log_add(L_INFO, _("Ignoring skip-full for %s:%s "
+                       "because the strategy is NOINC."),
+                       dp->host->hostname, qname);
            }
-           if(info.command & FORCE_BUMP) {
+           if(ISSET(info->command, FORCE_BUMP)) {
                log_add(L_INFO,
-                "Ignoring FORCE_BUMP for %s:%s because the strategy is NOINC.",
-                       dp->host->hostname, dp->name);
+                _("Ignoring FORCE_BUMP for %s:%s because the strategy is NOINC."),
+                       dp->host->hostname, qname);
            }
            
            break;
@@ -840,16 +1058,18 @@ setup_estimate(dp)
            break;
 
        case DS_INCRONLY:
-           if (ISSET(info.command, FORCE_FULL))
-               askfor(ep, i++, 0, &info);
+           if (ISSET(info->command, FORCE_FULL))
+               ep->last_level = 0;
            break;
        }
     }
 
     if(!dp->skip_incr && !(dp->strategy == DS_NOINC)) {
        if(ep->last_level == -1) {              /* a new disk */
+           if (ep->degr_mesg == NULL)
+               ep->degr_mesg = _("Skipping: new disk can't be dumped in degraded mode");
            if(dp->strategy == DS_NOFULL || dp->strategy == DS_INCRONLY) {
-               askfor(ep, i++, 1, &info);
+               askfor(ep, i++, 1, info);
            } else {
                assert(!dp->skip_full);         /* should be handled above */
            }
@@ -858,21 +1078,23 @@ setup_estimate(dp)
 
            curr_level = ep->last_level;
 
-           if (ISSET(info.command, FORCE_NO_BUMP)) {
+           if (ISSET(info->command, FORCE_NO_BUMP)) {
                if(curr_level > 0) { /* level 0 already asked for */
-                   askfor(ep, i++, curr_level, &info);
+                   askfor(ep, i++, curr_level, info);
                }
-               log_add(L_INFO,"Preventing bump of %s:%s as directed.",
-                       dp->host->hostname, dp->name);
-           } else if (ISSET(info.command, FORCE_BUMP)
+               log_add(L_INFO,_("Preventing bump of %s:%s as directed."),
+                       dp->host->hostname, qname);
+               ep->degr_mesg = _("Skipping: force-no-bump disk can't be dumped in degraded mode");
+           } else if (ISSET(info->command, FORCE_BUMP)
                       && curr_level + 1 < DUMP_LEVELS) {
-               askfor(ep, i++, curr_level+1, &info);
-               log_add(L_INFO,"Bumping of %s:%s at level %d as directed.",
-                       dp->host->hostname, dp->name, curr_level+1);
+               askfor(ep, i++, curr_level+1, info);
+               log_add(L_INFO,_("Bumping of %s:%s at level %d as directed."),
+                       dp->host->hostname, qname, curr_level+1);
+               ep->degr_mesg = _("Skipping: force-bump disk can't be dumped in degraded mode");
            } else if (curr_level == 0) {
-               askfor(ep, i++, 1, &info);
+               askfor(ep, i++, 1, info);
            } else {
-               askfor(ep, i++, curr_level, &info);
+               askfor(ep, i++, curr_level, info);
                /*
                 * If last time we dumped less than the threshold, then this
                 * time we will too, OR the extra size will be charged to both
@@ -880,81 +1102,95 @@ setup_estimate(dp)
                 * if we haven't been at this level 2 days, or the dump failed
                 * last night, we can't bump.
                 */
-               if((info.inf[curr_level].size == 0 || /* no data, try it anyway */
-                   (((info.inf[curr_level].size > bump_thresh(curr_level, info.inf[0].size,dp->bumppercent, dp->bumpsize, dp->bumpmult)))
+               if((info->inf[curr_level].size == (gint64)0 || /* no data, try it anyway */
+                   (((info->inf[curr_level].size > bump_thresh(curr_level, info->inf[0].size,dp->bumppercent, dp->bumpsize, dp->bumpmult)))
                     && ep->level_days >= dp->bumpdays))
                   && curr_level + 1 < DUMP_LEVELS) {
-                   askfor(ep, i++, curr_level+1, &info);
+                   askfor(ep, i++, curr_level+1, info);
                }
-           } 
+           }
        }
     }
 
-    while(i < MAX_LEVELS)      /* mark end of estimates */
-       askfor(ep, i++, -1, &info);
+    while(i < MAX_LEVELS)      /* mark end of estimates */
+       askfor(ep, i++, -1, info);
 
     /* debug output */
 
-    fprintf(stderr, "setup_estimate: %s:%s: command %d, options: %s    last_level %d next_level0 %d level_days %d    getting estimates %d (%ld) %d (%ld) %d (%ld)\n",
-           dp->host->hostname, dp->name, info.command,
+    g_fprintf(stderr, _("setup_estimate: %s:%s: command %u, options: %s    "
+           "last_level %d next_level0 %d level_days %d    getting estimates "
+           "%d (%lld) %d (%lld) %d (%lld)\n"),
+           dp->host->hostname, qname, info->command,
            dp->strategy == DS_NOFULL ? "no-full" :
                 dp->strategy == DS_INCRONLY ? "incr-only" :
                 dp->skip_full ? "skip-full" :
                 dp->skip_incr ? "skip-incr" : "none",
            ep->last_level, ep->next_level0, ep->level_days,
-           ep->level[0], ep->est_size[0],
-           ep->level[1], ep->est_size[1],
-           ep->level[2], ep->est_size[2]);
+           ep->estimate[0].level, (long long)ep->estimate[0].nsize,
+           ep->estimate[1].level, (long long)ep->estimate[1].nsize,
+           ep->estimate[2].level, (long long)ep->estimate[2].nsize);
 
-    assert(ep->level[0] != -1);
+    assert(ep->estimate[0].level != -1);
     enqueue_disk(&startq, dp);
+    amfree(qname);
 }
 
-static int when_overwrite(label)
-char *label;
+static int when_overwrite(
+    char *label)
 {
     tape_t *tp;
+    int runtapes;
+
+    runtapes = conf_runtapes;
+    if(runtapes == 0) runtapes = 1;
 
     if((tp = lookup_tapelabel(label)) == NULL)
        return 1;       /* "shouldn't happen", but trigger warning message */
-    else if(!reusable_tape(tp))
+    else if(tp->reuse == 0)
        return 1024;
     else if(lookup_nb_tape() > conf_tapecycle)
-       return (lookup_nb_tape() - tp->position) / conf_runtapes;
+       return (lookup_nb_tape() - tp->position) / runtapes;
     else
-       return (conf_tapecycle - tp->position) / conf_runtapes;
+       return (conf_tapecycle - tp->position) / runtapes;
 }
 
 /* Return the estimated size for a particular dump */
-static long est_size(dp, level)
-disk_t *dp;
-int level;
+static one_est_t *
+est_for_level(
+    disk_t *dp,
+    int level)
 {
     int i;
 
-    for(i = 0; i < MAX_LEVELS; i++) {
-       if(level == est(dp)->level[i])
-           return est(dp)->est_size[i];
+    if (level < 0 || level >= DUMP_LEVELS)
+       return &default_one_est;
+
+    for (i = 0; i < MAX_LEVELS; i++) {
+        if (level == est(dp)->estimate[i].level) {
+            if (est(dp)->estimate[i].csize <= -1) {
+                est_csize(dp, &est(dp)->estimate[i]);
+            }
+           return &est(dp)->estimate[i];
+       }
     }
-    return -1;
+    return &default_one_est;
 }
 
 /* Return the estimated on-tape size of a particular dump */
-static long est_tape_size(dp, level)
-disk_t *dp;
-int level;
+static void
+est_csize(
+    disk_t    *dp,
+    one_est_t *one_est)
 {
-    long size;
+    gint64 size = one_est->nsize;
     double ratio;
 
-    size = est_size(dp, level);
-
-    if(size == -1) return size;
-
-    if(dp->compress == COMP_NONE)
-       return size;
+    if (dp->compress == COMP_NONE) {
+        one_est->csize = one_est->nsize;
+        return;
+    }
 
-    if(level == 0) ratio = est(dp)->fullcomp;
+    if (one_est->level == 0) ratio = est(dp)->fullcomp;
     else ratio = est(dp)->incrcomp;
 
     /*
@@ -966,26 +1202,38 @@ int level;
      * (RUG@USM.Uni-Muenchen.DE)
      */
 
-    if(ratio > 1.1) ratio = 1.1;
+    if (ratio > 1.1) ratio = 1.1;
 
-    size *= ratio;
+    size = (gint64)((double)size * ratio);
 
     /*
      * Ratio can be very small in some error situations, so make sure
      * size goes back greater than zero.  It may not be right, but
      * indicates we did get an estimate.
      */
-    if(size <= 0) {
-       size = 1;
+    if (size <= (gint64)0) {
+       size = (gint64)1;
     }
 
-    return size;
+    one_est->csize = size;
+}
+
+static gint64 est_tape_size(
+    disk_t *dp,
+    int level)
+{
+    one_est_t *dump_est;
+
+    dump_est = est_for_level(dp, level);
+    if (dump_est->level >= 0 && dump_est->csize <= -1)
+       est_csize(dp, dump_est);
+    return dump_est->csize;
 }
 
 
 /* what was the level of the last successful dump to tape? */
-static int last_level(info)
-info_t *info;
+static int last_level(
+    info_t *info)
 {
     int min_pos, min_level, i;
     time_t lev0_date, last_date;
@@ -1026,9 +1274,9 @@ info_t *info;
 
 /* when is next level 0 due? 0 = today, 1 = tomorrow, etc*/
 static int
-next_level0(dp, info)
-     disk_t *dp;
-     info_t *info;
+next_level0(
+    disk_t *dp,
+    info_t *info)
 {
     if(dp->strategy == DS_NOFULL || dp->strategy == DS_INCRONLY)
        return 1;               /* fake it */
@@ -1041,50 +1289,52 @@ next_level0(dp, info)
 }
 
 /* how many runs at current level? */
-static int runs_at(info, lev)
-info_t *info;
-int lev;
+static int runs_at(
+    info_t *info,
+    int lev)
 {
     tape_t *cur_tape, *old_tape;
     int last, nb_runs;
 
     last = last_level(info);
     if(lev != last) return 0;
-    if(lev == 0) return 1;
-
     if(info->consecutive_runs != -1)
        return info->consecutive_runs;
+    if(lev == 0) return 1;
 
     /* to keep compatibility with old infofile */
     cur_tape = lookup_tapelabel(info->inf[lev].label);
     old_tape = lookup_tapelabel(info->inf[lev-1].label);
     if(cur_tape == NULL || old_tape == NULL) return 0;
 
-    nb_runs = (old_tape->position - cur_tape->position) / conf_runtapes;
+    if(conf_runtapes == 0)
+       nb_runs = (old_tape->position - cur_tape->position) / 1;
+    else
+       nb_runs = (old_tape->position - cur_tape->position) / conf_runtapes;
     info->consecutive_runs = nb_runs;
 
     return nb_runs;
 }
 
 
-static long bump_thresh(level, size_level_0, bumppercent, bumpsize, bumpmult)
-int level;
-long size_level_0;
-int bumppercent;
-int bumpsize;
-double bumpmult;
+static gint64 bump_thresh(
+    int level,
+    gint64 size_level_0,
+    int bumppercent,
+    gint64 bumpsize,
+    double bumpmult)
 {
     double bump;
 
-    if(bumppercent != 0 && size_level_0 > 1024) {
-       bump = (size_level_0 * bumppercent)/100.0;
+    if ((bumppercent != 0) && (size_level_0 > (gint64)1024)) {
+       bump = ((double)size_level_0 * (double)bumppercent) / 100.0;
     }
     else {
-       bump = bumpsize;
+       bump = (double)bumpsize;
     }
     while(--level) bump = bump * bumpmult;
 
-    return (long)bump;
+    return (gint64)bump;
 }
 
 
@@ -1095,15 +1345,15 @@ double bumpmult;
  *
  */
 
-static void getsize P((am_host_t *hostp));
-static disk_t *lookup_hostdisk P((am_host_t *hp, char *str));
-static void handle_result P((void *datap, pkt_t *pkt, security_handle_t *sech));
+static void getsize(am_host_t *hostp);
+static disk_t *lookup_hostdisk(am_host_t *hp, char *str);
+static void handle_result(void *datap, pkt_t *pkt, security_handle_t *sech);
 
 
-static void get_estimates P((void))
+static void get_estimates(void)
 {
     am_host_t *hostp;
-    disk_t *dp;
+    disk_t *dp, *dp1;
     int something_started;
 
     something_started = 1;
@@ -1113,6 +1363,14 @@ static void get_estimates P((void))
            hostp = dp->host;
            if(hostp->up == HOST_READY) {
                something_started = 1;
+               run_server_host_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE,
+                                       get_config_name(), hostp);
+               for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
+                   if (dp1->todo)
+                       run_server_dle_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE,
+                                          get_config_name(), dp1,
+                                          est(dp1)->estimate[0].level);
+               }
                getsize(hostp);
                protocol_check();
                /*
@@ -1127,83 +1385,58 @@ static void get_estimates P((void))
 
     while(!empty(waitq)) {
        disk_t *dp = dequeue_disk(&waitq);
-       est(dp)->errstr = "hmm, disk was stranded on waitq";
+       est(dp)->errstr = _("hmm, disk was stranded on waitq");
        enqueue_disk(&failq, dp);
     }
 
     while(!empty(pestq)) {
        disk_t *dp = dequeue_disk(&pestq);
-
-       if(est(dp)->level[0] != -1 && est(dp)->est_size[0] < 0) {
-           if(est(dp)->est_size[0] == -1) {
-               log_add(L_WARNING,
-                       "disk %s:%s, estimate of level %d failed.",
-                       dp->host->hostname, dp->name,
-                       est(dp)->level[0]);
-           }
-           else {
-               log_add(L_WARNING,
-                       "disk %s:%s, estimate of level %d timed out.",
-                       dp->host->hostname, dp->name,
-                       est(dp)->level[0]);
-           }
-           est(dp)->level[0] = -1;
-       }
-
-       if(est(dp)->level[1] != -1 && est(dp)->est_size[1] < 0) {
-           if(est(dp)->est_size[1] == -1) {
-               log_add(L_WARNING,
-                       "disk %s:%s, estimate of level %d failed.",
-                       dp->host->hostname, dp->name,
-                       est(dp)->level[1]);
-           }
-           else {
-               log_add(L_WARNING,
-                       "disk %s:%s, estimate of level %d timed out.",
-                       dp->host->hostname, dp->name,
-                       est(dp)->level[1]);
-           }
-           est(dp)->level[1] = -1;
-       }
-
-       if(est(dp)->level[2] != -1 && est(dp)->est_size[2] < 0) {
-           if(est(dp)->est_size[2] == -1) {
-               log_add(L_WARNING,
-                       "disk %s:%s, estimate of level %d failed.",
-                       dp->host->hostname, dp->name,
-                       est(dp)->level[2]);
-           }
-           else {
-               log_add(L_WARNING,
-                       "disk %s:%s, estimate of level %d timed out.",
-                       dp->host->hostname, dp->name,
-                       est(dp)->level[2]);
-           }
-           est(dp)->level[2] = -1;
+       char *  qname = quote_string(dp->name);
+       int i;
+
+       for (i=0; i < MAX_LEVELS; i++) {
+           if (est(dp)->estimate[i].level != -1 &&
+               est(dp)->estimate[i].nsize < (gint64)0) {
+               if (est(dp)->estimate[i].nsize == (gint64)-3) {
+                   log_add(L_WARNING,
+                           _("disk %s:%s, estimate of level %d timed out."),
+                           dp->host->hostname, qname, est(dp)->estimate[i].level);
+               }
+               est(dp)->estimate[i].level = -1;
+            }
        }
 
-       if((est(dp)->level[0] != -1 && est(dp)->est_size[0] > 0) ||
-          (est(dp)->level[1] != -1 && est(dp)->est_size[1] > 0) ||
-          (est(dp)->level[2] != -1 && est(dp)->est_size[2] > 0)) {
+       if ((est(dp)->estimate[0].level != -1 &&
+            est(dp)->estimate[0].nsize > (gint64)0) ||
+           (est(dp)->estimate[1].level != -1 &&
+             est(dp)->estimate[1].nsize > (gint64)0) ||
+           (est(dp)->estimate[2].level != -1 &&
+             est(dp)->estimate[2].nsize > (gint64)0)) {
            enqueue_disk(&estq, dp);
        }
        else {
-          est(dp)->errstr = vstralloc("disk ", dp->name,
-                                      ", all estimate timed out", NULL);
+          est(dp)->errstr = vstralloc("disk ", qname,
+                                      _(", all estimate timed out"), NULL);
           enqueue_disk(&failq, dp);
        }
+       amfree(qname);
     }
 }
 
-static void getsize(hostp)
-am_host_t *hostp;
+static void getsize(
+    am_host_t *hostp)
 {
-    char number[NUM_STR_SIZE], *req;
-    disk_t *dp;
-    int i, estimates, timeout, req_len;
-    const security_driver_t *secdrv;
-    char *dumper;
-    char *calcsize;
+    char       number[NUM_STR_SIZE], *req;
+    disk_t *   dp;
+    int                i;
+    time_t     estimates, timeout;
+    size_t     req_len;
+    const      security_driver_t *secdrv;
+    char *     calcsize;
+    char *     qname, *b64disk = NULL;
+    char *     qdevice, *b64device = NULL;
+    estimate_t     estimate;
+    estimatelist_t el;
 
     assert(hostp->disks != NULL);
 
@@ -1228,8 +1461,10 @@ am_host_t *hostp;
                                          fe_req_options_hostname);
        int has_maxdumps = am_has_feature(hostp->features,
                                          fe_req_options_maxdumps);
+       int has_config   = am_has_feature(hostp->features,
+                                         fe_req_options_config);
 
-       snprintf(number, sizeof(number), "%d", hostp->maxdumps);
+       g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
        req = vstralloc("SERVICE ", "sendsize", "\n",
                        "OPTIONS ",
                        has_features ? "features=" : "",
@@ -1241,213 +1476,285 @@ am_host_t *hostp;
                        has_hostname ? "hostname=" : "",
                        has_hostname ? hostp->hostname : "",
                        has_hostname ? ";" : "",
+                       has_config   ? "config=" : "",
+                       has_config   ? get_config_name() : "",
+                       has_config   ? ";" : "",
                        "\n",
                        NULL);
        req_len = strlen(req);
-       req_len += 128;                             /* room for SECURITY ... */
+       req_len += 128;                 /* room for SECURITY ... */
        estimates = 0;
        for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
            char *s = NULL;
-           int s_len = 0;
+           char *es;
+           size_t s_len = 0;
+           GPtrArray *errarray;
 
            if(dp->todo == 0) continue;
 
            if(est(dp)->state != DISK_READY) continue;
 
            est(dp)->got_estimate = 0;
-           if(est(dp)->level[0] == -1) {
+           if (est(dp)->estimate[0].level == -1) {
                est(dp)->state = DISK_DONE;
                continue;
            }
 
-           if(dp->estimate == ES_CLIENT ||
-              dp->estimate == ES_CALCSIZE) {
-               nb_client++;
-
-               for(i = 0; i < MAX_LEVELS; i++) {
-                   char *l;
-                   char *exclude1 = "";
-                   char *exclude2 = "";
-                   char *excludefree = NULL;
-                   char spindle[NUM_STR_SIZE];
-                   char level[NUM_STR_SIZE];
-                   int lev = est(dp)->level[i];
-
-                   if(lev == -1) break;
+           qname = quote_string(dp->name);
 
-                   snprintf(level, sizeof(level), "%d", lev);
-                   snprintf(spindle, sizeof(spindle), "%d", dp->spindle);
-                   if(am_has_feature(hostp->features,fe_sendsize_req_options)){
-                       exclude1 = " OPTIONS |";
-                       exclude2 = optionstr(dp, hostp->features, NULL);
-                       excludefree = exclude2;
-                   }
-                   else {
-                       if(dp->exclude_file &&
-                          dp->exclude_file->nb_element == 1) {
-                           exclude1 = " exclude-file=";
-                           exclude2 = dp->exclude_file->first->name;
-                       }
-                       else if(dp->exclude_list &&
-                               dp->exclude_list->nb_element == 1) {
-                           exclude1 = " exclude-list=";
-                           exclude2 = dp->exclude_list->first->name;
-                       }
-                   }
-
-                   if(dp->estimate == ES_CALCSIZE &&
-                      !am_has_feature(hostp->features, fe_calcsize_estimate)) {
-                       log_add(L_WARNING,"%s:%s does not support CALCSIZE for estimate, using CLIENT.\n",
-                               hostp->hostname, dp->name);
-                       dp->estimate = ES_CLIENT;
-                   }
-                   if(dp->estimate == ES_CLIENT)
-                       calcsize = "";
-                   else
-                       calcsize = "CALCSIZE ";
-
-                   if(strncmp(dp->program,"DUMP",4) == 0 || 
-                      strncmp(dp->program,"GNUTAR",6) == 0) {
-                       dumper = "";
-                   } else {
-                       dumper = "DUMPER ";
-                   }
-                   l = vstralloc(calcsize,
-                                 dumper,
-                                 dp->program,
-                                 " ", dp->name,
-                                 " ", dp->device ? dp->device : "",
-                                 " ", level,
-                                 " ", est(dp)->dumpdate[i],
-                                 " ", spindle,
-                                 " ", exclude1, exclude2,
-                                 "\n",
-                                 NULL);
-                   strappend(s, l);
-                   s_len += strlen(l);
-                   amfree(l);
-                   amfree(excludefree);
+           errarray = validate_optionstr(dp);
+           if (errarray->len > 0) {
+               guint i;
+               for (i=0; i < errarray->len; i++) {
+                   log_add(L_FAIL, _("%s %s %s 0 [%s]"),
+                           dp->host->hostname, qname,
+                           planner_timestamp,
+                           (char *)g_ptr_array_index(errarray, i));
                }
-               /*
-                * Allow 2X for err response.
-                */
-               if(req_len + s_len > MAX_PACKET / 2) {
-                   amfree(s);
+               amfree(qname);
+               est(dp)->state = DISK_DONE;
+               continue;
+           }
+                   
+           b64disk = amxml_format_tag("disk", dp->name);
+           qdevice = quote_string(dp->device);
+           estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data);
+           if (dp->device)
+               b64device = amxml_format_tag("diskdevice", dp->device);
+
+           estimate = ES_CLIENT;
+           for (el = dp->estimatelist; el != NULL; el = el->next) {
+               estimate = (estimate_t)GPOINTER_TO_INT(el->data);
+               if (estimate == ES_SERVER)
                    break;
-               }
-               if (s != NULL) {
-                   estimates += i;
-                   strappend(req, s);
-                   req_len += s_len;
-                   amfree(s);
-               }
-               est(dp)->state = DISK_ACTIVE;
-               remove_disk(&startq, dp);
            }
-           else if (dp->estimate == ES_SERVER) {
+           if (estimate == ES_SERVER) {
                info_t info;
                nb_server++;
                get_info(dp->host->hostname, dp->name, &info);
                for(i = 0; i < MAX_LEVELS; i++) {
-                   int j;
-                   int lev = est(dp)->level[i];
+                   int lev = est(dp)->estimate[i].level;
 
                    if(lev == -1) break;
-                   if(lev == 0) { /* use latest level 0, should do extrapolation */
-                       long est_size = 0;
-                       int nb_est = 0;
-
-                       for(j=NB_HISTORY-2;j>=0;j--) {
-                           if(info.history[j].level == 0) {
-                               if(info.history[j].size < 0) continue;
-                               est_size = info.history[j].size;
-                               nb_est++;
-                           }
-                       }
-                       if(nb_est > 0) {
-                           est(dp)->est_size[i] = est_size;
-                       }
-                       else if(info.inf[lev].size > 1000) { /* stats */
-                           est(dp)->est_size[i] = info.inf[lev].size;
-                       }
-                       else {
-                           est(dp)->est_size[i] = 1000000;
+                   server_estimate(dp, i, &info, lev);
+               }
+               g_fprintf(stderr,_("%s time %s: got result for host %s disk %s:"),
+                       get_pname(), walltime_str(curclock()),
+                       dp->host->hostname, qname);
+               g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
+                         est(dp)->estimate[0].level,
+                         (long long)est(dp)->estimate[0].nsize,
+                         est(dp)->estimate[1].level,
+                          (long long)est(dp)->estimate[1].nsize,
+                         est(dp)->estimate[2].level,
+                          (long long)est(dp)->estimate[2].nsize);
+               if (!am_has_feature(hostp->features, fe_xml_estimate)) {
+                   est(dp)->state = DISK_DONE;
+                   remove_disk(&startq, dp);
+                   enqueue_disk(&estq, dp);
+               }
+           }
+
+           estimate = ES_SERVER;
+           for (el = dp->estimatelist; el != NULL; el = el->next) {
+               estimate = (estimate_t)GPOINTER_TO_INT(el->data);
+               if (estimate == ES_CLIENT || estimate == ES_CALCSIZE)
+                   break;
+           }
+           if (estimate == ES_CLIENT ||
+               estimate == ES_CALCSIZE ||
+               (am_has_feature(hostp->features, fe_req_xml) &&
+                am_has_feature(hostp->features, fe_xml_estimate))) {
+               nb_client++;
+               i = 0;
+
+               if (am_has_feature(hostp->features, fe_req_xml)) {
+                   char *levelstr = NULL;
+                   char *spindlestr = NULL;
+                   char level[NUM_STR_SIZE];
+                   char spindle[NUM_STR_SIZE];
+                   char *o;
+                   char *l;
+                   info_t info;
+
+                   get_info(dp->host->hostname, dp->name, &info);
+                   for(i = 0; i < MAX_LEVELS; i++) {
+                       char *server;
+                       int lev = est(dp)->estimate[i].level;
+                       if (lev == -1) break;
+                       g_snprintf(level, SIZEOF(level), "%d", lev);
+                       if (am_has_feature(hostp->features, fe_xml_level_server) &&
+                           server_can_do_estimate(dp, &info, lev)) {
+                           server = "<server>YES</server>";
+                       } else {
+                           server = "";
                        }
+                       vstrextend(&levelstr, "  <level>",
+                                  level, server,
+                                  "</level>\n", NULL);
                    }
-                   else if(lev == est(dp)->last_level) {
-                       /* means of all X day at the same level */
-                       #define NB_DAY 30
-                       int nb_day = 0;
-                       long est_size_day[NB_DAY];
-                       int nb_est_day[NB_DAY];
-                       for(j=0;j<NB_DAY;j++) {
-                           est_size_day[j]=0;
-                           nb_est_day[j]=0;
+                   g_snprintf(spindle, SIZEOF(spindle), "%d", dp->spindle);
+                   spindlestr = vstralloc("  <spindle>",
+                                          spindle,
+                                          "</spindle>\n", NULL);
+                   o = xml_optionstr(dp, 0);
+                   
+                   if (strcmp(dp->program,"DUMP") == 0 ||
+                       strcmp(dp->program,"GNUTAR") == 0) {
+                       l = vstralloc("<dle>\n",
+                                     "  <program>",
+                                     dp->program,
+                                     "</program>\n", NULL);
+                   } else {
+                       l = vstralloc("<dle>\n",
+                                     "  <program>APPLICATION</program>\n",
+                                     NULL);
+                       if (dp->application) {
+                           application_t *application;
+                           char *xml_app;
+
+                           application = lookup_application(dp->application);
+                           g_assert(application != NULL);
+                           xml_app = xml_application(dp, application,
+                                                     hostp->features);
+                           vstrextend(&l, xml_app, NULL);
+                           amfree(xml_app);
                        }
+                   }
 
-                       for(j=NB_HISTORY-2;j>=0;j--) {
-                           if(info.history[j].level <= 0) continue;
-                           if(info.history[j].size < 0) continue;
-                           if(info.history[j].level==info.history[j+1].level) {
-                               if(nb_day <NB_DAY-1) nb_day++;
-                               est_size_day[nb_day] += info.history[j].size;
-                               nb_est_day[nb_day]++;
+                   es = xml_estimate(dp->estimatelist, hostp->features);
+                   vstrextend(&l, es, "\n", NULL);
+                   amfree(es);
+                   vstrextend(&l, "  ", b64disk, "\n", NULL);
+                   if (dp->device)
+                       vstrextend(&l, "  ", b64device, "\n", NULL);
+                   vstrextend(&l, levelstr, spindlestr, o, "</dle>\n", NULL);
+                   strappend(s, l);
+                   s_len += strlen(l);
+                   amfree(l);
+                   amfree(levelstr);
+                   amfree(spindlestr);
+                   amfree(o);
+               } else if (strcmp(dp->program,"DUMP") != 0 &&
+                          strcmp(dp->program,"GNUTAR") != 0) {
+                   est(dp)->errstr = newvstrallocf(est(dp)->errstr,
+                                       _("does not support application-api"));
+               } else {
+                   for(i = 0; i < MAX_LEVELS; i++) {
+                       char *l;
+                       char *exclude1 = "";
+                       char *exclude2 = "";
+                       char *excludefree = NULL;
+                       char *include1 = "";
+                       char *include2 = "";
+                       char *includefree = NULL;
+                       char spindle[NUM_STR_SIZE];
+                       char level[NUM_STR_SIZE];
+                       int lev = est(dp)->estimate[i].level;
+
+                       if(lev == -1) break;
+
+                       g_snprintf(level, SIZEOF(level), "%d", lev);
+                       g_snprintf(spindle, SIZEOF(spindle), "%d", dp->spindle);
+                       if (am_has_feature(hostp->features,
+                                          fe_sendsize_req_options)){
+                           exclude1 = " OPTIONS |";
+                           exclude2 = optionstr(dp);
+                           if ( exclude2 == NULL ) {
+                               error(_("problem with option string, check the dumptype definition.\n"));
+                           }
+                           excludefree = exclude2;
+                           includefree = NULL;
+                       } else {
+                           if (dp->exclude_file &&
+                               dp->exclude_file->nb_element == 1) {
+                               exclude1 = " exclude-file=";
+                               exclude2 = quote_string(
+                                               dp->exclude_file->first->name);
+                               excludefree = exclude2;
+                           }
+                           else if (dp->exclude_list &&
+                                    dp->exclude_list->nb_element == 1) {
+                               exclude1 = " exclude-list=";
+                               exclude2 = quote_string(
+                                               dp->exclude_list->first->name);
+                               excludefree = exclude2;
                            }
-                           else {
-                               nb_day=0;
+                           if (dp->include_file &&
+                               dp->include_file->nb_element == 1) {
+                               include1 = " include-file=";
+                               include2 = quote_string(
+                                               dp->include_file->first->name);
+                               includefree = include2;
+                           }
+                           else if (dp->include_list &&
+                                    dp->include_list->nb_element == 1) {
+                               include1 = " include-list=";
+                               include2 = quote_string(
+                                               dp->include_list->first->name);
+                               includefree = include2;
                            }
                        }
-                       nb_day = info.consecutive_runs + 1;
-                       if(nb_day > NB_DAY-1) nb_day = NB_DAY-1;
-
-                       while(nb_day > 0 && nb_est_day[nb_day] == 0) nb_day--;
 
-                       if(nb_est_day[nb_day] > 0) {
-                           est(dp)->est_size[i] =
-                                   est_size_day[nb_day] / nb_est_day[nb_day];
-                       }
-                       else if(info.inf[lev].size > 1000) { /* stats */
-                           est(dp)->est_size[i] = info.inf[lev].size;
-                       }
-                       else {
-                           est(dp)->est_size[i] = 10000;
+                       if (estimate == ES_CALCSIZE &&
+                           !am_has_feature(hostp->features,
+                                           fe_calcsize_estimate)) {
+                           log_add(L_WARNING,
+                                   _("%s:%s does not support CALCSIZE for estimate, using CLIENT.\n"),
+                                   hostp->hostname, qname);
+                           estimate = ES_CLIENT;
                        }
+                       if(estimate == ES_CLIENT)
+                           calcsize = "";
+                       else
+                           calcsize = "CALCSIZE ";
+
+                       l = vstralloc(calcsize,
+                                     dp->program,
+                                     " ", qname,
+                                     " ", dp->device ? qdevice : "",
+                                     " ", level,
+                                     " ", est(dp)->estimate[i].dumpdate,
+                                     " ", spindle,
+                                     " ", exclude1, exclude2,
+                                     ((includefree != NULL) ? " " : ""),
+                                       include1, include2,
+                                     "\n",
+                                     NULL);
+                       strappend(s, l);
+                       s_len += strlen(l);
+                       amfree(l);
+                       amfree(includefree);
+                       amfree(excludefree);
                    }
-                   else if(lev == est(dp)->last_level + 1) {
-                       /* means of all first day at a new level */
-                       long est_size = 0;
-                       int nb_est = 0;
-
-                       for(j=NB_HISTORY-2;j>=0;j--) {
-                           if(info.history[j].level <= 0) continue;
-                           if(info.history[j].size < 0) continue;
-                           if(info.history[j].level == info.history[j+1].level + 1 ) {
-                               est_size += info.history[j].size;
-                               nb_est++;
-                           }
-                       }
-                       if(nb_est > 0) {
-                           est(dp)->est_size[i] = est_size / nb_est;
-                       }
-                       else if(info.inf[lev].size > 1000) { /* stats */
-                           est(dp)->est_size[i] = info.inf[lev].size;
-                       }
-                       else {
-                           est(dp)->est_size[i] = 100000;
-                       }
+               }
+               if (s != NULL) {
+                   estimates += i;
+                   strappend(req, s);
+                   req_len += s_len;
+                   amfree(s);
+                   if (est(dp)->state == DISK_DONE) {
+                       remove_disk(&estq, dp);
+                       est(dp)->state = DISK_PARTIALY_DONE;
+                       enqueue_disk(&pestq, dp);
+                   } else {
+                       remove_disk(&startq, dp);
+                       est(dp)->state = DISK_ACTIVE;
                    }
-               }
-               fprintf(stderr,"%s time %s: got result for host %s disk %s:",
-                       get_pname(), walltime_str(curclock()),
-                       dp->host->hostname, dp->name);
-               fprintf(stderr," %d -> %ldK, %d -> %ldK, %d -> %ldK\n",
-                       est(dp)->level[0], est(dp)->est_size[0],
-                       est(dp)->level[1], est(dp)->est_size[1],
-                       est(dp)->level[2], est(dp)->est_size[2]);
-               est(dp)->state = DISK_DONE;
-               remove_disk(&startq, dp);
-               enqueue_disk(&estq, dp);
+               } else if (est(dp)->state != DISK_DONE) {
+                   remove_disk(&startq, dp);
+                   est(dp)->state = DISK_DONE;
+                   if (est(dp)->errstr == NULL) {
+                       est(dp)->errstr = vstrallocf(
+                                               _("Can't request estimate"));
+                   }
+                   enqueue_disk(&failq, dp);
+               }
            }
+           amfree(b64disk);
+           amfree(b64device);
+           amfree(qname);
+           amfree(qdevice);
        }
 
        if(estimates == 0) {
@@ -1471,13 +1778,18 @@ am_host_t *hostp;
         * We use ctimeout for the "noop" request because it should be
         * very fast and etimeout has other side effects.
         */
-       timeout = getconf_int(CNF_CTIMEOUT);
+       timeout = (time_t)getconf_int(CNF_CTIMEOUT);
     }
 
-    secdrv = security_getdriver(hostp->disks->security_driver);
+    dbprintf(_("send request:\n----\n%s\n----\n\n"), req);
+    secdrv = security_getdriver(hostp->disks->auth);
     if (secdrv == NULL) {
-       error("could not find security driver '%s' for host '%s'",
-           hostp->disks->security_driver, hostp->hostname);
+       hostp->up = HOST_DONE;
+       log_add(L_ERROR,
+               _("Could not find security driver '%s' for host '%s'"),
+               hostp->disks->auth, hostp->hostname);
+       amfree(req);
+       return;
     }
     hostp->up = HOST_ACTIVE;
 
@@ -1491,14 +1803,15 @@ am_host_t *hostp;
        }
     }
 
-    protocol_sendreq(hostp->hostname, secdrv, generic_get_security_conf, 
+    protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf, 
        req, timeout, handle_result, hostp);
+
     amfree(req);
 }
 
-static disk_t *lookup_hostdisk(hp, str)
-am_host_t *hp;
-char *str;
+static disk_t *lookup_hostdisk(
+    /*@keep@*/ am_host_t *hp,
+    char *str)
 {
     disk_t *dp;
 
@@ -1509,16 +1822,16 @@ char *str;
 }
 
 
-static void handle_result(datap, pkt, sech)
-void *datap;
-pkt_t *pkt;
-security_handle_t *sech;
+static void handle_result(
+    void *datap,
+    pkt_t *pkt,
+    security_handle_t *sech)
 {
     int level, i;
-    long size;
+    gint64 size;
     disk_t *dp;
     am_host_t *hostp;
-    char *msgdisk=NULL, *msgdisk_undo=NULL, msgdisk_undo_ch = '\0';
+    char *msg, msg_undo;
     char *remoterr, *errbuf = NULL;
     char *s;
     char *t;
@@ -1526,21 +1839,26 @@ security_handle_t *sech;
     char *line;
     int ch;
     int tch;
+    char *qname;
+    char *disk;
+    long long size_;
 
     hostp = (am_host_t *)datap;
     hostp->up = HOST_READY;
 
     if (pkt == NULL) {
-       errbuf = vstralloc("Request to ", hostp->hostname, " failed: ", 
-           security_geterror(sech), NULL);
+       if (strcmp(security_geterror(sech), "timeout waiting for REP") == 0) {
+           errbuf = vstrallocf("Some estimate timeout on %s, using server estimate if possible", hostp->hostname);
+       } else {
+           errbuf = vstrallocf(_("Request to %s failed: %s"),
+                       hostp->hostname, security_geterror(sech));
+       }
        goto error_return;
     }
     if (pkt->type == P_NAK) {
-#define sc "ERROR "
-       if(strncmp(pkt->body, sc, sizeof(sc)-1) == 0) {
-           s = pkt->body + sizeof(sc)-1;
+       s = pkt->body;
+       if(strncmp_const_skip(s, "ERROR ", s, ch) == 0) {
            ch = *s++;
-#undef sc
        } else {
            goto NAK_parse_failed;
        }
@@ -1552,57 +1870,48 @@ security_handle_t *sech;
                *s = '\0';
        }
        if (strcmp(remoterr, "unknown service: noop") != 0
-                  && strcmp(remoterr, "noop: invalid service") != 0) {
+               && strcmp(remoterr, "noop: invalid service") != 0) {
            errbuf = vstralloc(hostp->hostname, " NAK: ", remoterr, NULL);
            if(s) *s = '\n';
            goto error_return;
        }
     }
 
-    msgdisk_undo = NULL;
+    dbprintf(_("got reply:\n----\n%s\n----\n\n"), pkt->body);
     s = pkt->body;
     ch = *s++;
     while(ch) {
        line = s - 1;
-       skip_line(s, ch);
-       if (s[-2] == '\n') {
-           s[-2] = '\0';
-       }
-
-#define sc "OPTIONS "
-       if(strncmp(line, sc, sizeof(sc)-1) == 0) {
-#undef sc
 
-#define sc "features="
-           t = strstr(line, sc);
-           if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
-               t += sizeof(sc)-1;
-#undef sc
+       if(strncmp_const(line, "OPTIONS ") == 0) {
+           t = strstr(line, "features=");
+           if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) {
+               char *u = strchr(t, ';');
+               if (u)
+                  *u = '\0';
+               t += SIZEOF("features=")-1;
                am_release_feature_set(hostp->features);
                if((hostp->features = am_string_to_feature(t)) == NULL) {
-                   errbuf = vstralloc(hostp->hostname,
-                                      ": bad features value: ",
-                                      line,
-                                      "\n",
-                                      NULL);
+                   errbuf = vstrallocf(hostp->hostname,
+                                      _(": bad features value: %s\n"), line);
                    goto error_return;
                }
+               if (u)
+                  *u = ';';
            }
-
+           skip_quoted_line(s, ch);
            continue;
        }
 
-#define sc "ERROR "
-       if(strncmp(line, sc, sizeof(sc)-1) == 0) {
-           t = line + sizeof(sc)-1;
-           tch = t[-1];
-#undef sc
-
+       t = line;
+       if ((strncmp_const_skip(t, "ERROR ", t, tch) == 0) ||
+           (strncmp_const_skip(t, "WARNING ", t, tch) == 0)) { 
            fp = t - 1;
            skip_whitespace(t, tch);
            if (tch == '\n') {
                t[-1] = '\0';
            }
+
            /*
             * If the "error" is that the "noop" service is unknown, it
             * just means the client is "old" (does not support the servie).
@@ -1611,50 +1920,89 @@ security_handle_t *sech;
            if(hostp->features == NULL
               && pkt->type == P_NAK
               && (strcmp(t - 1, "unknown service: noop") == 0
-                  || strcmp(t - 1, "noop: invalid service") == 0)) {
+                  || strcmp(t - 1, "noop: invalid service") == 0)) {
+               skip_quoted_line(s, ch);
                continue;
-           } else {
-               errbuf = vstralloc(hostp->hostname,
-                                  (pkt->type == P_NAK) ? "NAK " : "",
-                                  ": ",
-                                  fp,
-                                  NULL);
-               goto error_return;
            }
+           t = strchr(t,'\n');
+           if (t) /* truncate after the first line */
+                *t = '\0';
+           errbuf = vstralloc(hostp->hostname,
+                                  (pkt->type == P_NAK) ? "NAK " : "",
+                                  ": ",
+                                  fp,
+                                  NULL);
+           goto error_return;
        }
 
-       msgdisk = t = line;
-       tch = *t++;
-       skip_non_whitespace(t, tch);
-       msgdisk_undo = t - 1;
-       msgdisk_undo_ch = *msgdisk_undo;
-       *msgdisk_undo = '\0';
+       msg = t = line;
+       tch = *(t++);
+       skip_quoted_string(t, tch);
+       t[-1] = '\0';
+       disk = unquote_string(msg);
 
        skip_whitespace(t, tch);
-       if (sscanf(t - 1, "%d SIZE %ld", &level, &size) != 2) {
+
+       if (sscanf(t - 1, "%d", &level) != 1) {
            goto bad_msg;
        }
 
-       dp = lookup_hostdisk(hostp, msgdisk);
-
-       *msgdisk_undo = msgdisk_undo_ch;        /* for error message */
-       msgdisk_undo = NULL;
+       skip_integer(t, tch);
+       skip_whitespace(t, tch);
 
+       dp = lookup_hostdisk(hostp, disk);
        if(dp == NULL) {
-           log_add(L_ERROR, "%s: invalid reply from sendsize: `%s'\n",
+           log_add(L_ERROR, _("%s: invalid reply from sendsize: `%s'\n"),
                    hostp->hostname, line);
+           goto bad_msg;
+       }
+
+       size = (gint64)-1;
+       if (strncmp_const(t-1,"SIZE ") == 0) {
+           if (sscanf(t - 1, "SIZE %lld", &size_) != 1) {
+               goto bad_msg;
+           }
+           size = (gint64)size_;
+       } else if ((strncmp_const(t-1,"ERROR ") == 0) ||
+                  (strncmp_const(t-1,"WARNING ") == 0)) {
+           skip_non_whitespace(t, tch);
+           skip_whitespace(t, tch);
+           msg = t-1;
+           skip_quoted_string(t,tch);
+           msg_undo = t[-1];
+           t[-1] = '\0';
+           if (pkt->type == P_REP && !est(dp)->errstr) {
+               est(dp)->errstr = unquote_string(msg);
+           }
+           t[-1] = msg_undo;
        } else {
-           for(i = 0; i < MAX_LEVELS; i++) {
-               if(est(dp)->level[i] == level) {
-                   est(dp)->est_size[i] = size;
-                   break;
+           goto bad_msg;
+       }
+
+       amfree(disk);
+
+       for (i = 0; i < MAX_LEVELS; i++) {
+           if (est(dp)->estimate[i].level == level) {
+               if (size == (gint64)-2) {
+                   est(dp)->estimate[i].nsize = -1; /* remove estimate */
+                   est(dp)->estimate[i].guessed = 0;
+               } else if (size > (gint64)-1) {
+                   /* take the size returned by the client */
+                   est(dp)->estimate[i].nsize = size;
+                   est(dp)->estimate[i].guessed = 0;
                }
+               break;
            }
-           if(i == MAX_LEVELS) {
-               goto bad_msg;                   /* this est wasn't requested */
-           }
-           est(dp)->got_estimate++;
        }
+       if (i == MAX_LEVELS && level > 0) {
+                       /* client always report level 0 for some error */
+           goto bad_msg;               /* this est wasn't requested */
+       }
+       est(dp)->got_estimate++;
+
+       s = t;
+       ch = tch;
+       skip_quoted_line(s, ch);
     }
 
     if(hostp->up == HOST_READY && hostp->features == NULL) {
@@ -1662,11 +2010,11 @@ security_handle_t *sech;
         * The client does not support the features list, so give it an
         * empty one.
         */
-       dbprintf(("%s: no feature set from host %s\n",
-                 debug_prefix_time(NULL), hostp->hostname));
+       dbprintf(_("no feature set from host %s\n"), hostp->hostname);
        hostp->features = am_set_default_feature_set();
     }
 
+    security_close_connection(sech, hostp->hostname);
 
     /* XXX what about disks that only got some estimates...  do we care? */
     /* XXX amanda 2.1 treated that case as a bad msg */
@@ -1690,112 +2038,126 @@ security_handle_t *sech;
            est(dp)->state = DISK_PARTIALY_DONE;
        }
 
-       if(est(dp)->level[0] == -1) continue;   /* ignore this disk */
+       if (est(dp)->estimate[0].level == -1) continue;   /* ignore this disk */
 
 
+       qname = quote_string(dp->name);
        if(pkt->type == P_PREP) {
-               fprintf(stderr,"%s: time %s: got partial result for host %s disk %s:",
+               g_fprintf(stderr,_("%s: time %s: got partial result for host %s disk %s:"),
                        get_pname(), walltime_str(curclock()),
-                       dp->host->hostname, dp->name);
-               fprintf(stderr," %d -> %ldK, %d -> %ldK, %d -> %ldK\n",
-                       est(dp)->level[0], est(dp)->est_size[0],
-                       est(dp)->level[1], est(dp)->est_size[1],
-                       est(dp)->level[2], est(dp)->est_size[2]);
+                       dp->host->hostname, qname);
+               g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
+                         est(dp)->estimate[0].level,
+                          (long long)est(dp)->estimate[0].nsize,
+                         est(dp)->estimate[1].level,
+                          (long long)est(dp)->estimate[1].nsize,
+                         est(dp)->estimate[2].level,
+                          (long long)est(dp)->estimate[2].nsize);
            enqueue_disk(&pestq, dp);
        }
        else if(pkt->type == P_REP) {
-               fprintf(stderr,"%s: time %s: got result for host %s disk %s:",
+               g_fprintf(stderr,_("%s: time %s: got result for host %s disk %s:"),
                        get_pname(), walltime_str(curclock()),
-                       dp->host->hostname, dp->name);
-               fprintf(stderr," %d -> %ldK, %d -> %ldK, %d -> %ldK\n",
-                       est(dp)->level[0], est(dp)->est_size[0],
-                       est(dp)->level[1], est(dp)->est_size[1],
-                       est(dp)->level[2], est(dp)->est_size[2]);
-               if((est(dp)->level[0] != -1 && est(dp)->est_size[0] > 0) ||
-                  (est(dp)->level[1] != -1 && est(dp)->est_size[1] > 0) ||
-                  (est(dp)->level[2] != -1 && est(dp)->est_size[2] > 0)) {
-
-                   if(est(dp)->level[2] != -1 && est(dp)->est_size[2] < 0) {
-                       log_add(L_WARNING,
-                               "disk %s:%s, estimate of level %d failed.",
-                               dp->host->hostname, dp->name,
-                               est(dp)->level[2]);
-                       est(dp)->level[2] = -1;
-                   }
-                   if(est(dp)->level[1] != -1 && est(dp)->est_size[1] < 0) {
-                       log_add(L_WARNING,
-                               "disk %s:%s, estimate of level %d failed.",
-                               dp->host->hostname, dp->name,
-                               est(dp)->level[1]);
-                       est(dp)->level[1] = -1;
-                   }
-                   if(est(dp)->level[0] != -1 && est(dp)->est_size[0] < 0) {
-                       log_add(L_WARNING,
-                               "disk %s:%s, estimate of level %d failed.",
-                               dp->host->hostname, dp->name,
-                               est(dp)->level[0]);
-                       est(dp)->level[0] = -1;
+                       dp->host->hostname, qname);
+               g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
+                         est(dp)->estimate[0].level,
+                          (long long)est(dp)->estimate[0].nsize,
+                         est(dp)->estimate[1].level,
+                          (long long)est(dp)->estimate[1].nsize,
+                         est(dp)->estimate[2].level,
+                          (long long)est(dp)->estimate[2].nsize);
+               if ((est(dp)->estimate[0].level != -1 &&
+                     est(dp)->estimate[0].nsize > (gint64)0) ||
+                   (est(dp)->estimate[1].level != -1 &&
+                     est(dp)->estimate[1].nsize > (gint64)0) ||
+                   (est(dp)->estimate[2].level != -1 &&
+                     est(dp)->estimate[2].nsize > (gint64)0)) {
+
+                    for (i=MAX_LEVELS-1; i >=0; i--) {
+                       if (est(dp)->estimate[i].level != -1 &&
+                            est(dp)->estimate[i].nsize < (gint64)0) {
+                           est(dp)->estimate[i].level = -1;
+                       }
                    }
                    enqueue_disk(&estq, dp);
            }
            else {
                enqueue_disk(&failq, dp);
-               if(est(dp)->got_estimate) {
-                   est(dp)->errstr = vstralloc("disk ", dp->name,
-                                               ", all estimate failed", NULL);
+               if(est(dp)->got_estimate && !est(dp)->errstr) {
+                   est(dp)->errstr = vstrallocf("disk %s, all estimate failed",
+                                                qname);
                }
                else {
-                   fprintf(stderr, "error result for host %s disk %s: missing estimate\n",
-                           dp->host->hostname, dp->name);
-                   est(dp)->errstr = vstralloc("missing result for ", dp->name,
-                                               " in ", dp->host->hostname,
-                                               " response",
-                                               NULL);
+                   g_fprintf(stderr,
+                        _("error result for host %s disk %s: missing estimate\n"),
+                        dp->host->hostname, qname);
+                   if (est(dp)->errstr == NULL) {
+                       est(dp)->errstr = vstrallocf(_("missing result for %s in %s response"),
+                                                   qname, dp->host->hostname);
+                   }
                }
            }
+           hostp->up = HOST_DONE;
+       }
+       if (est(dp)->post_dle == 0 &&
+           (pkt->type == P_REP ||
+            ((est(dp)->estimate[0].level == -1 ||
+               est(dp)->estimate[0].nsize > (gint64)0) &&
+             (est(dp)->estimate[1].level == -1 ||
+               est(dp)->estimate[1].nsize > (gint64)0) &&
+             (est(dp)->estimate[2].level == -1 ||
+               est(dp)->estimate[2].nsize > (gint64)0)))) {
+           run_server_dle_scripts(EXECUTE_ON_POST_DLE_ESTIMATE,
+                              get_config_name(), dp,
+                               est(dp)->estimate[0].level);
+           est(dp)->post_dle = 1;
+       }
+       amfree(qname);
+    }
+
+    if(hostp->up == HOST_DONE) {
+       if (pkt->type == P_REP) {
+           run_server_host_scripts(EXECUTE_ON_POST_HOST_ESTIMATE,
+                                   get_config_name(), hostp);
        }
     }
+
     getsize(hostp);
+    /* try to clean up any defunct processes, since Amanda doesn't wait() for
+       them explicitly */
+    while(waitpid(-1, NULL, WNOHANG)> 0);
     return;
 
  NAK_parse_failed:
 
-    /* msgdisk_undo is always NULL */
-    /* if(msgdisk_undo) { */
-    /*         *msgdisk_undo = msgdisk_undo_ch; */
-    /* msgdisk_undo = NULL; */
-    /* } */
-    errbuf = stralloc2(hostp->hostname, " NAK: [NAK parse failed]");
-    fprintf(stderr, "got strange nak from %s:\n----\n%s----\n\n",
+    errbuf = vstrallocf(_("%s NAK: [NAK parse failed]"), hostp->hostname);
+    g_fprintf(stderr, _("got strange nak from %s:\n----\n%s----\n\n"),
            hostp->hostname, pkt->body);
     goto error_return;
 
  bad_msg:
-
-    if(msgdisk_undo) {
-       *msgdisk_undo = msgdisk_undo_ch;
-       msgdisk_undo = NULL;
-    }
-    fprintf(stderr,"got a bad message, stopped at:\n");
-    fprintf(stderr,"----\n%s----\n\n", line);
-    errbuf = stralloc2("badly formatted response from ", hostp->hostname);
-    /* fall through to ... */
+    g_fprintf(stderr,_("got a bad message, stopped at:\n"));
+    /*@ignore@*/
+    g_fprintf(stderr,_("----\n%s----\n\n"), line);
+    errbuf = stralloc2(_("badly formatted response from "), hostp->hostname);
+    /*@end@*/
 
  error_return:
-
     i = 0;
     for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
-       if(est(dp)->state != DISK_ACTIVE) continue;
-       est(dp)->state = DISK_DONE;
-       if(est(dp)->state == DISK_ACTIVE) {
-           est(dp)->state = DISK_DONE;
-           remove_disk(&waitq, dp);
-           enqueue_disk(&failq, dp);
-           i++;
+       if (dp->todo) {
+           if(est(dp)->state == DISK_ACTIVE) {
+               qname = quote_string(dp->name);
+               est(dp)->state = DISK_DONE;
+               remove_disk(&waitq, dp);
+               enqueue_disk(&failq, dp);
+               i++;
 
-           est(dp)->errstr = stralloc(errbuf);
-           fprintf(stderr, "error result for host %s disk %s: %s\n",
-               dp->host->hostname, dp->name, errbuf);
+               est(dp)->errstr = stralloc(errbuf);
+               g_fprintf(stderr, _("error result for host %s disk %s: %s\n"),
+                         dp->host->hostname, qname, errbuf);
+               amfree(qname);
+           }
        }
     }
     if(i == 0) {
@@ -1804,9 +2166,25 @@ security_handle_t *sech;
         * reported.
         */
        log_add(L_ERROR, "%s", errbuf);
+       for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
+           if (dp->todo) {
+               qname = quote_string(dp->name);
+               est(dp)->state = DISK_DONE;
+               remove_disk(&waitq, dp);
+               enqueue_disk(&failq, dp);
+
+               est(dp)->errstr = g_strdup(errbuf);
+               g_fprintf(stderr, _("error result for host %s disk %s: %s\n"),
+                         dp->host->hostname, qname, errbuf);
+               amfree(qname);
+           }
+       }
     }
     hostp->up = HOST_DONE;
     amfree(errbuf);
+    /* try to clean up any defunct processes, since Amanda doesn't wait() for
+       them explicitly */
+    while(waitpid(-1, NULL, WNOHANG)> 0);
 }
 
 
@@ -1818,235 +2196,271 @@ security_handle_t *sech;
  *
  */
 
-static int schedule_order P((disk_t *a, disk_t *b));     /* subroutines */
-static int pick_inclevel P((disk_t *dp));
+static int schedule_order(disk_t *a, disk_t *b);         /* subroutines */
+static one_est_t *pick_inclevel(disk_t *dp);
 
-static void analyze_estimate(dp)
-disk_t *dp;
+static void analyze_estimate(
+    disk_t *dp)
 {
     est_t *ep;
     info_t info;
     int have_info = 0;
+    char *qname = quote_string(dp->name);
 
     ep = est(dp);
 
-    fprintf(stderr, "pondering %s:%s... ",
-           dp->host->hostname, dp->name);
-    fprintf(stderr, "next_level0 %d last_level %d ",
+    g_fprintf(stderr, _("pondering %s:%s... "),
+           dp->host->hostname, qname);
+    g_fprintf(stderr, _("next_level0 %d last_level %d "),
            ep->next_level0, ep->last_level);
 
     if(get_info(dp->host->hostname, dp->name, &info) == 0) {
        have_info = 1;
     }
 
-    ep->degr_level = -1;
-    ep->degr_size = -1;
+    ep->degr_est = &default_one_est;
 
-    if(ep->next_level0 <= 0
-       || (have_info && ep->last_level == 0 && (info.command & FORCE_NO_BUMP))) {
-       if(ep->next_level0 <= 0) {
-           fprintf(stderr,"(due for level 0) ");
-       }
-       ep->dump_level = 0;
-       ep->dump_size = est_tape_size(dp, 0);
-       if(ep->dump_size <= 0) {
-           fprintf(stderr,
-                   "(no estimate for level 0, picking an incr level)\n");
-           ep->dump_level = pick_inclevel(dp);
-           ep->dump_size = est_tape_size(dp, ep->dump_level);
-
-           if(ep->dump_size == -1) {
-               ep->dump_level = ep->dump_level + 1;
-               ep->dump_size = est_tape_size(dp, ep->dump_level);
+    if (ep->next_level0 <= 0 || (have_info && ep->last_level == 0
+       && (ISSET(info.command, FORCE_NO_BUMP)))) {
+       if (ep->next_level0 <= 0) {
+           g_fprintf(stderr,_("(due for level 0) "));
+       }
+       ep->dump_est = est_for_level(dp, 0);
+       if (ep->dump_est->csize <= (gint64)0) {
+           g_fprintf(stderr,
+                   _("(no estimate for level 0, picking an incr level)\n"));
+           ep->dump_est = pick_inclevel(dp);
+
+           if (ep->dump_est->nsize == (gint64)-1) {
+               ep->dump_est = est_for_level(dp, ep->dump_est->level + 1);
            }
        }
        else {
-           total_lev0 += (double) ep->dump_size;
+           total_lev0 += (double) ep->dump_est->csize;
            if(ep->last_level == -1 || dp->skip_incr) {
-               fprintf(stderr,"(%s disk, can't switch to degraded mode)\n",
-                       dp->skip_incr? "skip-incr":"new");
-               ep->degr_level = -1;
-               ep->degr_size = -1;
+               g_fprintf(stderr,_("(%s disk, can't switch to degraded mode)\n"),
+                       dp->skip_incr? "skip-incr":_("new"));
+               if (dp->skip_incr  && ep->degr_mesg == NULL) {
+                   ep->degr_mesg = _("Skpping: skip-incr disk can't be dumped in degraded mode");
+               }
+               ep->degr_est = &default_one_est;
            }
            else {
                /* fill in degraded mode info */
-               fprintf(stderr,"(picking inclevel for degraded mode)");
-               ep->degr_level = pick_inclevel(dp);
-               ep->degr_size = est_tape_size(dp, ep->degr_level);
-               if(ep->degr_size == -1) {
-                   ep->degr_level = ep->degr_level + 1;
-                   ep->degr_size = est_tape_size(dp, ep->degr_level);
+               g_fprintf(stderr,_("(picking inclevel for degraded mode)"));
+               ep->degr_est = pick_inclevel(dp);
+               if (ep->degr_est->level >= 0 &&
+                   ep->degr_est->csize == (gint64)-1) {
+                    ep->degr_est = est_for_level(dp, ep->degr_est->level + 1);
                }
-               if(ep->degr_size == -1) {
-                   fprintf(stderr,"(no inc estimate)");
-                   ep->degr_level = -1;
+               if (ep->degr_est->csize == (gint64)-1) {
+                   g_fprintf(stderr,_("(no inc estimate)"));
+                   if (ep->degr_mesg == NULL)
+                       ep->degr_mesg = _("Skipping: an incremental estimate could not be performed, so disk cannot be dumped in degraded mode");
+                   ep->degr_est = &default_one_est;
                }
-               fprintf(stderr,"\n");
+               g_fprintf(stderr,"\n");
            }
        }
     }
     else {
-       fprintf(stderr,"(not due for a full dump, picking an incr level)\n");
+       g_fprintf(stderr,_("(not due for a full dump, picking an incr level)\n"));
        /* XXX - if this returns -1 may be we should force a total? */
-       ep->dump_level = pick_inclevel(dp);
-       ep->dump_size = est_tape_size(dp, ep->dump_level);
+       ep->dump_est = pick_inclevel(dp);
 
-       if(ep->dump_size == -1) {
-           ep->dump_level = ep->last_level;
-           ep->dump_size = est_tape_size(dp, ep->dump_level);
+       if (ep->dump_est->csize == (gint64)-1) {
+           ep->dump_est = est_for_level(dp, ep->last_level);
+       }
+       if (ep->dump_est->csize == (gint64)-1) {
+           ep->dump_est = est_for_level(dp, ep->last_level + 1);
        }
-       if(ep->dump_size == -1) {
-           ep->dump_level = ep->last_level + 1;
-           ep->dump_size = est_tape_size(dp, ep->dump_level);
+       if (ep->dump_est->csize == (gint64)-1) {
+           ep->dump_est = est_for_level(dp, 0);
        }
-       if(ep->dump_size == -1) {
-           ep->dump_level = 0;
-           ep->dump_size = est_tape_size(dp, ep->dump_level);
+       if (ep->degr_mesg == NULL) {
+           ep->degr_mesg = _("Skipping: a full is not planned, so can't dump in degraded mode");
        }
     }
 
-    fprintf(stderr,"  curr level %d size %ld ", ep->dump_level, ep->dump_size);
+    if (ep->dump_est->level < 0) {
+       int   i;
+       char *q = quote_string("no estimate");
+
+       g_fprintf(stderr,_("  no valid estimate\n"));
+       for(i=0; i<MAX_LEVELS; i++) {
+           if (est(dp)->estimate[i].level >= 0) {
+               g_fprintf(stderr,("    level: %d  nsize: %lld csize: %lld\n"),
+                         est(dp)->estimate[i].level,
+                         (long long)est(dp)->estimate[i].nsize,
+                         (long long)est(dp)->estimate[i].csize);
+           }
+       }
+       log_add(L_WARNING, _("%s %s %s 0 %s"), dp->host->hostname, qname,
+               planner_timestamp, q);
+       amfree(q);
+    }
+
+    g_fprintf(stderr,_("  curr level %d nsize %lld csize %lld "),
+              ep->dump_est->level, (long long)ep->dump_est->nsize,
+              (long long)ep->dump_est->csize);
 
     insert_disk(&schedq, dp, schedule_order);
 
-    total_size += tt_blocksize_kb + ep->dump_size + tape_mark;
+    total_size += (gint64)tt_blocksize_kb + ep->dump_est->csize + tape_mark;
 
     /* update the balanced size */
     if(!(dp->skip_full || dp->strategy == DS_NOFULL || 
         dp->strategy == DS_INCRONLY)) {
-       long lev0size;
+       gint64 lev0size;
 
        lev0size = est_tape_size(dp, 0);
-       if(lev0size == -1) lev0size = ep->last_lev0size;
+       if(lev0size == (gint64)-1) lev0size = ep->last_lev0size;
+
+       if (dp->strategy == DS_NOINC) {
+           balanced_size += (double)lev0size;
+       } else if (dp->dumpcycle == 0) {
+           balanced_size += (double)(lev0size * conf_dumpcycle / (gint64)runs_per_cycle);
+       } else if (dp->dumpcycle != conf_dumpcycle) {
+           balanced_size += (double)(lev0size * (conf_dumpcycle / dp->dumpcycle) / (gint64)runs_per_cycle);
+       } else {
+           balanced_size += (double)(lev0size / (gint64)runs_per_cycle);
+       }
+    }
+
+    g_fprintf(stderr,_("total size %lld total_lev0 %1.0lf balanced-lev0size %1.0lf\n"),
+           (long long)total_size, total_lev0, balanced_size);
 
-       balanced_size += lev0size / runs_per_cycle;
+    /* Log errstr even if the estimate succeeded */
+    /* It can be an error from a script          */
+    if (est(dp)->errstr) {
+       char *qerrstr = quote_string(est(dp)->errstr);
+       /* Log only a warning if a server estimate is available */
+       if (est(dp)->estimate[0].nsize > 0 ||
+           est(dp)->estimate[1].nsize > 0 ||
+           est(dp)->estimate[2].nsize > 0) {
+           log_add(L_WARNING, _("%s %s %s 0 %s"), dp->host->hostname, qname, 
+                   planner_timestamp, qerrstr);
+       } else {
+           log_add(L_FAIL, _("%s %s %s 0 %s"), dp->host->hostname, qname, 
+                   planner_timestamp, qerrstr);
+       }
+       amfree(qerrstr);
     }
 
-    fprintf(stderr,"total size " AM64_FMT " total_lev0 %1.0f balanced-lev0size %1.0f\n",
-           total_size, total_lev0, balanced_size);
+    amfree(qname);
 }
 
-static void handle_failed(dp)
-disk_t *dp;
+static void handle_failed(
+    disk_t *dp)
 {
-    char *errstr;
-
-/*
- * From George Scott <George.Scott@cc.monash.edu.au>:
- * --------
- * If a machine is down when the planner is run it guesses from historical
- * data what the size of tonights dump is likely to be and schedules a
- * dump anyway.  The dumper then usually discovers that that machine is
- * still down and ends up with a half full tape.  Unfortunately the
- * planner had to delay another dump because it thought that the tape was
- * full.  The fix here is for the planner to ignore unavailable machines
- * rather than ignore the fact that they are unavailable.
- * --------
- */
+    char *errstr, *errstr1, *qerrstr;
+    char *qname = quote_string(dp->name);
 
-#ifdef old_behavior
-    if(est(dp)->last_level != -1) {
-       log_add(L_WARNING,
-               "Could not get estimate for %s:%s, using historical data.",
-               dp->host->hostname, dp->name);
-       analyze_estimate(dp);
-       return;
-    }
-#endif
+    errstr = est(dp)->errstr? est(dp)->errstr : _("hmm, no error indicator!");
+    errstr1 = vstralloc("[",errstr,"]", NULL);
+    qerrstr = quote_string(errstr1);
+    amfree(errstr1);
 
-    errstr = est(dp)->errstr? est(dp)->errstr : "hmm, no error indicator!";
+    g_fprintf(stderr, _("%s: FAILED %s %s %s 0 %s\n"),
+       get_pname(), dp->host->hostname, qname, planner_timestamp, qerrstr);
 
-    fprintf(stderr, "%s: FAILED %s %s %s 0 [%s]\n",
-       get_pname(), dp->host->hostname, dp->name, datestamp, errstr);
-
-    log_add(L_FAIL, "%s %s %s 0 [%s]", dp->host->hostname, dp->name, 
-           datestamp, errstr);
+    log_add(L_FAIL, _("%s %s %s 0 %s"), dp->host->hostname, qname, 
+           planner_timestamp, qerrstr);
 
+    amfree(qerrstr);
+    amfree(qname);
     /* XXX - memory leak with *dp */
 }
 
 
-static int schedule_order(a, b)
-disk_t *a, *b;
 /*
  * insert-sort by decreasing priority, then
  * by decreasing size within priority levels.
  */
+
+static int schedule_order(
+    disk_t *a,
+    disk_t *b)
 {
     int diff;
-    long ldiff;
+    gint64 ldiff;
 
     diff = est(b)->dump_priority - est(a)->dump_priority;
     if(diff != 0) return diff;
 
-    ldiff = est(b)->dump_size - est(a)->dump_size;
-    if(ldiff < 0) return -1; /* XXX - there has to be a better way to dothis */
-    if(ldiff > 0) return 1;
+    ldiff = est(b)->dump_est->csize - est(a)->dump_est->csize;
+    if(ldiff < (gint64)0) return -1; /* XXX - there has to be a better way to dothis */
+    if(ldiff > (gint64)0) return 1;
     return 0;
 }
 
 
-static int pick_inclevel(dp)
-disk_t *dp;
+static one_est_t *pick_inclevel(
+    disk_t *dp)
 {
-    int base_level, bump_level;
-    long base_size, bump_size;
-    long thresh;
+    one_est_t *level0_est, *base_est, *bump_est;
+    gint64 thresh;
+    char *qname;
 
-    base_level = est(dp)->last_level;
+    level0_est = est_for_level(dp, 0);
+    base_est = est_for_level(dp, est(dp)->last_level);
 
     /* if last night was level 0, do level 1 tonight, no ifs or buts */
-    if(base_level == 0) {
-       fprintf(stderr,"   picklev: last night 0, so tonight level 1\n");
-       return 1;
+    if (base_est->level == 0) {
+       g_fprintf(stderr,_("   picklev: last night 0, so tonight level 1\n"));
+       return est_for_level(dp, 1);
     }
 
     /* if no-full option set, always do level 1 */
     if(dp->strategy == DS_NOFULL) {
-       fprintf(stderr,"   picklev: no-full set, so always level 1\n");
-       return 1;
+       g_fprintf(stderr,_("   picklev: no-full set, so always level 1\n"));
+       return est_for_level(dp, 1);
     }
 
-    base_size = est_size(dp, base_level);
-
     /* if we didn't get an estimate, we can't do an inc */
-    if(base_size == -1) {
-       base_size = est_size(dp, base_level+1);
-       if(base_size > 0) /* FORCE_BUMP */
-           return base_level+1;
-       fprintf(stderr,"   picklev: no estimate for level %d, so no incs\n", base_level);
-       return base_level;
+    if (base_est->nsize == (gint64)-1) {
+       bump_est = est_for_level(dp, est(dp)->last_level + 1);
+       if (bump_est->nsize > (gint64)0) { /* FORCE_BUMP */
+           g_fprintf(stderr,_("   picklev: bumping to level %d\n"), bump_est->level);
+           return bump_est;
+       }
+       g_fprintf(stderr,_("   picklev: no estimate for level %d, so no incs\n"), base_est->level);
+       return base_est;
     }
 
-    thresh = bump_thresh(base_level, est_size(dp, 0), dp->bumppercent, dp->bumpsize, dp->bumpmult);
+    thresh = bump_thresh(base_est->level, level0_est->nsize, dp->bumppercent,
+                         dp->bumpsize, dp->bumpmult);
 
-    fprintf(stderr,
-           "   pick: size %ld level %d days %d (thresh %ldK, %d days)\n",
-           base_size, base_level, est(dp)->level_days,
-           thresh, dp->bumpdays);
+    g_fprintf(stderr,
+           _("   pick: size %lld level %d days %d (thresh %lldK, %d days)\n"),
+           (long long)base_est->nsize, base_est->level, est(dp)->level_days,
+           (long long)thresh, dp->bumpdays);
 
-    if(base_level == 9
+    if(base_est->level == (DUMP_LEVELS - 1)
        || est(dp)->level_days < dp->bumpdays
-       || base_size <= thresh)
-           return base_level;
+       || base_est->nsize <= thresh)
+           return base_est;
 
-    bump_level = base_level + 1;
-    bump_size = est_size(dp, bump_level);
+    bump_est = est_for_level(dp, base_est->level + 1);
 
-    if(bump_size == -1) return base_level;
+    if (bump_est->nsize == (gint64)-1)
+        return base_est;
 
-    fprintf(stderr, "   pick: next size %ld... ", bump_size);
+    g_fprintf(stderr, _("   pick: next size %lld... "),
+              (long long)bump_est->nsize);
 
-    if(base_size - bump_size < thresh) {
-       fprintf(stderr, "not bumped\n");
-       return base_level;
+    if (base_est->nsize - bump_est->nsize < thresh) {
+       g_fprintf(stderr, _("not bumped\n"));
+       return base_est;
     }
 
-    fprintf(stderr, "BUMPED\n");
-    log_add(L_INFO, "Incremental of %s:%s bumped to level %d.",
-           dp->host->hostname, dp->name, bump_level);
+    qname = quote_string(dp->name);
+    g_fprintf(stderr, _("BUMPED\n"));
+    log_add(L_INFO, _("Incremental of %s:%s bumped to level %d."),
+           dp->host->hostname, qname, bump_est->level);
+    amfree(qname);
 
-    return bump_level;
+    return bump_est;
 }
 
 
@@ -2076,19 +2490,28 @@ disk_t *dp;
 ** more balanced cycle.
 */
 
-static void delay_one_dump P((disk_t *dp, int delete, ...));
+static void delay_one_dump(disk_t *dp, int delete, ...);
+static int promote_highest_priority_incremental(void);
+static int promote_hills(void);
 
-static void delay_dumps P((void))
 /* delay any dumps that will not fit */
+static void delay_dumps(void)
 {
-    disk_t *dp, *ndp, *preserve;
-    bi_t *bi, *nbi;
-    am64_t new_total;  /* New total_size */
-    char est_kb[20];     /* Text formatted dump size */
-    int nb_forced_level_0;
-    info_t info;
-    int delete;
-    char *message;
+    disk_t *   dp;
+    disk_t *   ndp;
+    disk_t *   preserve;
+    disk_t *   delayed_dp;
+    bi_t *     bi;
+    bi_t  *    nbi;
+    gint64     new_total;              /* New total_size */
+    char       est_kb[20];             /* Text formatted dump size */
+    int                nb_forced_level_0;
+    info_t     info;
+    int                delete;
+    char *     message;
+    gint64     full_size;
+    time_t      timestamps;
+    int         priority;
 
     biq.head = biq.tail = NULL;
 
@@ -2105,47 +2528,64 @@ static void delay_dumps P((void))
 
     for(dp = schedq.head; dp != NULL; dp = ndp) {
        int avail_tapes = 1;
-       if (dp->tape_splitsize > 0)
+       if (dp->splitsize > (gint64)0 || dp->allow_split)
            avail_tapes = conf_runtapes;
 
        ndp = dp->next; /* remove_disk zaps this */
 
-       if (est(dp)->dump_size == -1 ||
-           est(dp)->dump_size <= tape->length * avail_tapes) {
+       full_size = est_tape_size(dp, 0);
+       if (full_size > tapetype_get_length(tape) * (gint64)avail_tapes) {
+           char *qname = quote_string(dp->name);
+           if (conf_runtapes > 1 && dp->splitsize == (gint64)0) {
+               log_add(L_WARNING, _("disk %s:%s, full dump (%lldKB) will be larger than available tape space"
+                       ", you could define a splitsize"),
+                       dp->host->hostname, qname,
+                       (long long)full_size);
+           } else {
+               log_add(L_WARNING, _("disk %s:%s, full dump (%lldKB) will be larger than available tape space"),
+                       dp->host->hostname, qname,
+                       (long long)full_size);
+           }
+           amfree(qname);
+       }
+
+       if (est(dp)->dump_est->csize == (gint64)-1 ||
+           est(dp)->dump_est->csize <= tapetype_get_length(tape) * (gint64)avail_tapes) {
            continue;
        }
 
-        /* Format dumpsize for messages */
-       snprintf(est_kb, 20, "%ld KB,", est(dp)->dump_size);
+       /* Format dumpsize for messages */
+       g_snprintf(est_kb, 20, "%lld KB,",
+                   (long long)est(dp)->dump_est->csize);
 
-       if(est(dp)->dump_level == 0) {
+       if(est(dp)->dump_est->level == 0) {
            if(dp->skip_incr) {
                delete = 1;
-               message = "but cannot incremental dump skip-incr disk";
+               message = _("but cannot incremental dump skip-incr disk");
            }
            else if(est(dp)->last_level < 0) {
                delete = 1;
-               message = "but cannot incremental dump new disk";
+               message = _("but cannot incremental dump new disk");
            }
-           else if(est(dp)->degr_level < 0) {
+           else if(est(dp)->degr_est->level < 0) {
                delete = 1;
-               message = "but no incremental estimate";
+               message = _("but no incremental estimate");
            }
-           else if (est(dp)->degr_size > tape->length) {
+           else if (est(dp)->degr_est->csize > tapetype_get_length(tape)) {
                delete = 1;
-               message = "incremental dump also larger than tape";
+               message = _("incremental dump also larger than tape");
            }
            else {
                delete = 0;
-               message = "full dump delayed";
+               message = _("full dump delayed, doing incremental");
            }
        }
        else {
            delete = 1;
-           message = "skipping incremental";
+           message = _("skipping incremental");
        }
-       delay_one_dump(dp, delete, "dump larger than available tape space,", est_kb,
-                      message, NULL);
+       delay_one_dump(dp, delete, _("dump larger than available tape space,"),
+                      est_kb, message, NULL);
     }
 
     /*
@@ -2163,50 +2603,71 @@ static void delay_dumps P((void))
 
     nb_forced_level_0 = 0;
     preserve = NULL;
-    for(dp = schedq.head; dp != NULL && preserve == NULL; dp = dp->next)
-       if(est(dp)->dump_level == 0)
-           preserve = dp;
+    timestamps = 2147483647;
+    priority = 0;
+    for(dp = schedq.head; dp != NULL; dp = dp->next) {
+       if (est(dp)->dump_est->level == 0) {
+           if (!preserve ||
+               est(dp)->dump_priority > priority ||
+               (est(dp)->dump_priority == priority &&
+                est(dp)->info->inf[0].date < timestamps)) {
+               priority = est(dp)->dump_priority;
+               timestamps = est(dp)->info->inf[0].date;
+               preserve = dp;
+           }
+       }
+    }
 
     /* 2.a. Do not delay forced full */
-    for(dp = schedq.tail;
+    delayed_dp = NULL;
+    do {
+       delayed_dp = 0;
+       timestamps = 0;
+       for(dp = schedq.tail;
                dp != NULL && total_size > tape_length;
                dp = ndp) {
-       ndp = dp->prev;
-
-       if(est(dp)->dump_level != 0) continue;
+           ndp = dp->prev;
 
-       get_info(dp->host->hostname, dp->name, &info);
-       if(info.command & FORCE_FULL) {
-           nb_forced_level_0 += 1;
-           preserve = dp;
-           continue;
-       }
+           if(est(dp)->dump_est->level != 0) continue;
 
-       if(dp != preserve) {
+           get_info(dp->host->hostname, dp->name, &info);
+           if(ISSET(info.command, FORCE_FULL)) {
+               nb_forced_level_0 += 1;
+               preserve = dp;
+               continue;
+           }
 
-            /* Format dumpsize for messages */
-           snprintf(est_kb, 20, "%ld KB,", est(dp)->dump_size);
+           if (dp != preserve &&
+               est(dp)->info->inf[0].date > timestamps) {
+               delayed_dp = dp;
+               timestamps = est(dp)->info->inf[0].date;
+           }
+       }
+       if (delayed_dp) {
+           /* Format dumpsize for messages */
+           g_snprintf(est_kb, 20, "%lld KB,",
+                       (long long)est(delayed_dp)->dump_est->csize);
 
-           if(dp->skip_incr) {
+           if(delayed_dp->skip_incr) {
                delete = 1;
-               message = "but cannot incremental dump skip-incr disk";
+               message = _("but cannot incremental dump skip-incr disk");
            }
-           else if(est(dp)->last_level < 0) {
+           else if(est(delayed_dp)->last_level < 0) {
                delete = 1;
-               message = "but cannot incremental dump new disk";
+               message = _("but cannot incremental dump new disk");
            }
-           else if(est(dp)->degr_level < 0) {
+           else if(est(delayed_dp)->degr_est->level < 0) {
                delete = 1;
-               message = "but no incremental estimate";
+               message = _("but no incremental estimate");
            }
            else {
                delete = 0;
-               message = "full dump delayed";
+               message = _("full dump delayed, doing incremental");
            }
-           delay_one_dump(dp, delete, "dumps too big,", est_kb,
+           delay_one_dump(delayed_dp, delete, _("dumps too big,"), est_kb,
                           message, NULL);
        }
-    }
+    } while (delayed_dp);
 
     /* 2.b. Delay forced full if needed */
     if(nb_forced_level_0 > 0 && total_size > tape_length) {
@@ -2215,28 +2676,29 @@ static void delay_dumps P((void))
                dp = ndp) {
            ndp = dp->prev;
 
-           if(est(dp)->dump_level == 0 && dp != preserve) {
+           if(est(dp)->dump_est->level == 0 && dp != preserve) {
 
                /* Format dumpsize for messages */
-               snprintf(est_kb, 20, "%ld KB,", est(dp)->dump_size);
+               g_snprintf(est_kb, 20, "%lld KB,",
+                           (long long)est(dp)->dump_est->csize);
 
                if(dp->skip_incr) {
                    delete = 1;
-                   message = "but cannot incremental dump skip-incr disk";
+                   message = _("but cannot incremental dump skip-incr disk");
                }
                else if(est(dp)->last_level < 0) {
                    delete = 1;
-                   message = "but cannot incremental dump new disk";
+                   message = _("but cannot incremental dump new disk");
                }
-               else if(est(dp)->degr_level < 0) {
+               else if(est(dp)->degr_est->level < 0) {
                    delete = 1;
-                   message = "but no incremental estimate";
+                   message = _("but no incremental estimate");
                }
                else {
                    delete = 0;
-                   message = "full dump delayed";
+                   message = _("full dump delayed");
                }
-               delay_one_dump(dp, delete, "dumps too big,", est_kb,
+               delay_one_dump(dp, delete, _("dumps too big,"), est_kb,
                               message, NULL);
            }
        }
@@ -2255,15 +2717,16 @@ static void delay_dumps P((void))
            dp = ndp) {
        ndp = dp->prev;
 
-       if(est(dp)->dump_level != 0) {
+       if(est(dp)->dump_est->level != 0) {
 
-            /* Format dumpsize for messages */
-           snprintf(est_kb, 20, "%ld KB,", est(dp)->dump_size);
+           /* Format dumpsize for messages */
+           g_snprintf(est_kb, 20, "%lld KB,",
+                       (long long)est(dp)->dump_est->csize);
 
            delay_one_dump(dp, 1,
-                          "dumps way too big,",
+                          _("dumps way too big,"),
                           est_kb,
-                          "must skip incremental dumps",
+                          _("must skip incremental dumps"),
                           NULL);
        }
     }
@@ -2277,29 +2740,31 @@ static void delay_dumps P((void))
     ** in but why complicate the code?
     */
 
-    for(bi = biq.tail; bi != NULL; bi = nbi) {
+/*@i@*/ for(bi = biq.tail; bi != NULL; bi = nbi) {
        int avail_tapes = 1;
        nbi = bi->prev;
        dp = bi->dp;
-       if(dp->tape_splitsize > 0) avail_tapes = conf_runtapes;
-
-       if(bi->deleted)
-           new_total = total_size + tt_blocksize_kb + bi->size + tape_mark;
-       else
-           new_total = total_size - est(dp)->dump_size + bi->size;
+       if(dp->splitsize > (gint64)0)
+           avail_tapes = conf_runtapes;
 
-       if(new_total <= tape_length && bi->size < tape->length * avail_tapes) {
+       if(bi->deleted) {
+           new_total = total_size + (gint64)tt_blocksize_kb +
+                       bi->csize + (gint64)tape_mark;
+       } else {
+           new_total = total_size - est(dp)->dump_est->csize + bi->csize;
+       }
+       if((new_total <= tape_length) &&
+         (bi->csize < (tapetype_get_length(tape) * (gint64)avail_tapes))) {
            /* reinstate it */
            total_size = new_total;
            if(bi->deleted) {
                if(bi->level == 0) {
-                   total_lev0 += (double) bi->size;
+                   total_lev0 += (double) bi->csize;
                }
                insert_disk(&schedq, dp, schedule_order);
            }
            else {
-               est(dp)->dump_level = bi->level;
-               est(dp)->dump_size = bi->size;
+               est(dp)->dump_est = est_for_level(dp, bi->level);
            }
 
            /* Keep it clean */
@@ -2311,6 +2776,7 @@ static void delay_dumps P((void))
                biq.head = bi->next;
            else
                (bi->prev)->next = bi->next;
+
            amfree(bi->errstr);
            amfree(bi);
        }
@@ -2324,26 +2790,26 @@ static void delay_dumps P((void))
     ** now.
     */
 
-    for(bi = biq.head; bi != NULL; bi = nbi) {
+/*@i@*/ for(bi = biq.head; bi != NULL; bi = nbi) {
        nbi = bi->next;
-
        if(bi->deleted) {
-           fprintf(stderr, "%s: FAILED %s\n", get_pname(), bi->errstr);
+           g_fprintf(stderr, "%s: FAILED %s\n", get_pname(), bi->errstr);
            log_add(L_FAIL, "%s", bi->errstr);
        }
        else {
            dp = bi->dp;
-           fprintf(stderr, "  delay: %s now at level %d\n",
-               bi->errstr, est(dp)->dump_level);
+           g_fprintf(stderr, _("  delay: %s now at level %d\n"),
+               bi->errstr, est(dp)->dump_est->level);
            log_add(L_INFO, "%s", bi->errstr);
        }
-
-       /* Clean up - dont be too fancy! */
+       /*@ignore@*/
        amfree(bi->errstr);
        amfree(bi);
+       /*@end@*/
     }
 
-    fprintf(stderr, "  delay: Total size now " AM64_FMT ".\n", total_size);
+    g_fprintf(stderr, _("  delay: Total size now %lld.\n"),
+            (long long)total_size);
 
     return;
 }
@@ -2353,24 +2819,27 @@ static void delay_dumps P((void))
  * Remove a dump or modify it from full to incremental.
  * Keep track of it on the bi q in case we can add it back later.
  */
-arglist_function1(static void delay_one_dump,
-                 disk_t *, dp,
-                 int, delete)
+arglist_function1(
+    static void delay_one_dump,
+    disk_t *, dp,
+    int, delete)
 {
     bi_t *bi;
     va_list argp;
     char level_str[NUM_STR_SIZE];
     char *sep;
     char *next;
+    char *qname = quote_string(dp->name);
+    char *errstr, *qerrstr;
 
     arglist_start(argp, delete);
 
-    total_size -= tt_blocksize_kb + est(dp)->dump_size + tape_mark;
-    if(est(dp)->dump_level == 0) {
-       total_lev0 -= (double) est(dp)->dump_size;
+    total_size -= (gint64)tt_blocksize_kb + est(dp)->dump_est->csize + (gint64)tape_mark;
+    if(est(dp)->dump_est->level == 0) {
+       total_lev0 -= (double) est(dp)->dump_est->csize;
     }
 
-    bi = alloc(sizeof(bi_t));
+    bi = alloc(SIZEOF(bi_t));
     bi->next = NULL;
     bi->prev = biq.tail;
     if(biq.tail == NULL)
@@ -2381,42 +2850,48 @@ arglist_function1(static void delay_one_dump,
 
     bi->deleted = delete;
     bi->dp = dp;
-    bi->level = est(dp)->dump_level;
-    bi->size = est(dp)->dump_size;
+    bi->level = est(dp)->dump_est->level;
+    bi->nsize = est(dp)->dump_est->nsize;
+    bi->csize = est(dp)->dump_est->csize;
 
-    snprintf(level_str, sizeof(level_str), "%d", est(dp)->dump_level);
+    g_snprintf(level_str, SIZEOF(level_str), "%d", est(dp)->dump_est->level);
     bi->errstr = vstralloc(dp->host->hostname,
-                          " ", dp->name,
-                          " ", datestamp ? datestamp : "?",
+                          " ", qname,
+                          " ", planner_timestamp ? planner_timestamp : "?",
                           " ", level_str,
                           NULL);
-    sep = " [";
+    errstr = NULL;
+    sep = "[";
     while ((next = arglist_val(argp, char *)) != NULL) {
-       bi->errstr = newvstralloc(bi->errstr, bi->errstr, sep, next, NULL);
+       vstrextend(&errstr, sep, next, NULL);
        sep = " ";
     }
-    strappend(bi->errstr, "]");
+    strappend(errstr, "]");
+    qerrstr = quote_string(errstr);
+    vstrextend(&bi->errstr, " ", qerrstr, NULL);
+    amfree(errstr);
+    amfree(qerrstr);
     arglist_end(argp);
 
     if (delete) {
        remove_disk(&schedq, dp);
     } else {
-       est(dp)->dump_level = est(dp)->degr_level;
-       est(dp)->dump_size = est(dp)->degr_size;
-       total_size += tt_blocksize_kb + est(dp)->dump_size + tape_mark;
+       est(dp)->dump_est = est(dp)->degr_est;
+       total_size += (gint64)tt_blocksize_kb + est(dp)->dump_est->csize + (gint64)tape_mark;
     }
-
+    amfree(qname);
     return;
 }
 
 
-static int promote_highest_priority_incremental P((void))
+static int promote_highest_priority_incremental(void)
 {
     disk_t *dp, *dp1, *dp_promote;
-    long new_size, new_total, new_lev0;
+    gint64 new_total, new_lev0;
     int check_days;
     int nb_today, nb_same_day, nb_today2;
     int nb_disk_today, nb_disk_same_day;
+    char *qname;
 
     /*
      * return 1 if did so; must update total_size correctly; must not
@@ -2425,10 +2900,10 @@ static int promote_highest_priority_incremental P((void))
 
     dp_promote = NULL;
     for(dp = schedq.head; dp != NULL; dp = dp->next) {
-
+       one_est_t *level0_est = est_for_level(dp, 0);
        est(dp)->promote = -1000;
 
-       if(est_size(dp,0) <= 0)
+       if (level0_est->nsize <= (gint64)0)
            continue;
 
        if(est(dp)->next_level0 <= 0)
@@ -2437,21 +2912,20 @@ static int promote_highest_priority_incremental P((void))
        if(est(dp)->next_level0 > dp->maxpromoteday)
            continue;
 
-       new_size = est_tape_size(dp, 0);
-       new_total = total_size - est(dp)->dump_size + new_size;
-       new_lev0 = total_lev0 + new_size;
+       new_total = total_size - est(dp)->dump_est->csize + level0_est->csize;
+       new_lev0 = (gint64)total_lev0 + level0_est->csize;
 
        nb_today = 0;
        nb_same_day = 0;
        nb_disk_today = 0;
        nb_disk_same_day = 0;
-        for(dp1 = schedq.head; dp1 != NULL; dp1 = dp1->next) {
-           if(est(dp1)->dump_level == 0)
+       for(dp1 = schedq.head; dp1 != NULL; dp1 = dp1->next) {
+           if(est(dp1)->dump_est->level == 0)
                nb_disk_today++;
            else if(est(dp1)->next_level0 == est(dp)->next_level0)
                nb_disk_same_day++;
            if(strcmp(dp->host->hostname, dp1->host->hostname) == 0) {
-               if(est(dp1)->dump_level == 0)
+               if(est(dp1)->dump_est->level == 0)
                    nb_today++;
                else if(est(dp1)->next_level0 == est(dp)->next_level0)
                    nb_same_day++;
@@ -2459,18 +2933,22 @@ static int promote_highest_priority_incremental P((void))
        }
 
        /* do not promote if overflow tape */
-       if(new_total > tape_length) continue;
+       if(new_total > tape_length)
+           continue;
 
        /* do not promote if overflow balanced size and something today */
        /* promote if nothing today */
-       if(new_lev0 > balanced_size+balance_threshold && nb_disk_today > 0)
+       if((new_lev0 > (gint64)(balanced_size + balance_threshold)) &&
+               (nb_disk_today > 0))
            continue;
 
        /* do not promote if only one disk due that day and nothing today */
-       if(nb_disk_same_day == 1 && nb_disk_today == 0) continue;
+       if(nb_disk_same_day == 1 && nb_disk_today == 0)
+           continue;
 
        nb_today2 = nb_today*nb_today;
-       if(nb_today == 0 && nb_same_day > 1) nb_same_day++;
+       if(nb_today == 0 && nb_same_day > 1)
+           nb_same_day++;
 
        if(nb_same_day >= nb_today2) {
            est(dp)->promote = ((nb_same_day - nb_today2)*(nb_same_day - nb_today2)) + 
@@ -2481,61 +2959,65 @@ static int promote_highest_priority_incremental P((void))
                               conf_dumpcycle - est(dp)->next_level0;
        }
 
-        if(!dp_promote || est(dp_promote)->promote < est(dp)->promote) {
+       qname = quote_string(dp->name);
+       if(!dp_promote || est(dp_promote)->promote < est(dp)->promote) {
            dp_promote = dp;
-           fprintf(stderr,"   try %s:%s %d %d %d = %d\n",
-                   dp->host->hostname, dp->name, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote);
+           g_fprintf(stderr,"   try %s:%s %d %d %d = %d\n",
+                   dp->host->hostname, qname, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote);
        }
-        else {
-           fprintf(stderr,"no try %s:%s %d %d %d = %d\n",
-                   dp->host->hostname, dp->name, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote);
+       else {
+           g_fprintf(stderr,"no try %s:%s %d %d %d = %d\n",
+                   dp->host->hostname, qname, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote);
        }
+       amfree(qname);
     }
 
     if(dp_promote) {
+       one_est_t *level0_est;
        dp = dp_promote;
+       level0_est = est_for_level(dp, 0);
 
-       new_size = est_tape_size(dp, 0);
-       new_total = total_size - est(dp)->dump_size + new_size;
-       new_lev0 = total_lev0 + new_size;
+       qname = quote_string(dp->name);
+       new_total = total_size - est(dp)->dump_est->csize + level0_est->csize;
+       new_lev0 = (gint64)total_lev0 + level0_est->csize;
 
        total_size = new_total;
-       total_lev0 = new_lev0;
+       total_lev0 = (double)new_lev0;
        check_days = est(dp)->next_level0;
-       est(dp)->degr_level = est(dp)->dump_level;
-       est(dp)->degr_size = est(dp)->dump_size;
-       est(dp)->dump_level = 0;
-       est(dp)->dump_size = new_size;
+       est(dp)->degr_est = est(dp)->dump_est;
+       est(dp)->dump_est = level0_est;
        est(dp)->next_level0 = 0;
 
-       fprintf(stderr,
-             "   promote: moving %s:%s up, total_lev0 %1.0f, total_size " AM64_FMT "\n",
-               dp->host->hostname, dp->name,
-               total_lev0, total_size);
+       g_fprintf(stderr,
+             _("   promote: moving %s:%s up, total_lev0 %1.0lf, total_size %lld\n"),
+               dp->host->hostname, qname,
+               total_lev0, (long long)total_size);
 
        log_add(L_INFO,
-               "Full dump of %s:%s promoted from %d day%s ahead.",
-               dp->host->hostname, dp->name,
-               check_days, (check_days == 1) ? "" : "s");
+               plural(_("Full dump of %s:%s promoted from %d day ahead."),
+                      _("Full dump of %s:%s promoted from %d days ahead."),
+                     check_days),
+               dp->host->hostname, qname, check_days);
+       amfree(qname);
        return 1;
     }
     return 0;
 }
 
 
-static int promote_hills P((void))
+static int promote_hills(void)
 {
     disk_t *dp;
     struct balance_stats {
        int disks;
-       long size;
+       gint64 size;
     } *sp = NULL;
     int days;
     int hill_days = 0;
-    long hill_size;
-    long new_size;
-    long new_total;
+    gint64 hill_size;
+    gint64 new_total;
     int my_dumpcycle;
+    char *qname;
 
     /* If we are already doing a level 0 don't bother */
     if(total_lev0 > 0)
@@ -2546,13 +3028,16 @@ static int promote_hills P((void))
     if(my_dumpcycle > 10000) my_dumpcycle = 10000;
 
     sp = (struct balance_stats *)
-       alloc(sizeof(struct balance_stats) * my_dumpcycle);
+       alloc(SIZEOF(struct balance_stats) * my_dumpcycle);
 
-    for(days = 0; days < my_dumpcycle; days++)
-       sp[days].disks = sp[days].size = 0;
+    for(days = 0; days < my_dumpcycle; days++) {
+       sp[days].disks = 0;
+       sp[days].size = (gint64)0;
+    }
 
     for(dp = schedq.head; dp != NULL; dp = dp->next) {
-       days = est(dp)->next_level0;   /* This is > 0 by definition */
+       days = est(dp)->next_level0;
+       if (days < 0) days = 0;
        if(days<my_dumpcycle && !dp->skip_full && dp->strategy != DS_NOFULL &&
           dp->strategy != DS_INCRONLY) {
            sp[days].disks++;
@@ -2563,7 +3048,7 @@ static int promote_hills P((void))
     /* Search for a suitable big hill and cut it down */
     while(1) {
        /* Find the tallest hill */
-       hill_size = 0;
+       hill_size = (gint64)0;
        for(days = 0; days < my_dumpcycle; days++) {
            if(sp[days].disks > 1 && sp[days].size > hill_size) {
                hill_size = sp[days].size;
@@ -2571,39 +3056,43 @@ static int promote_hills P((void))
            }
        }
 
-       if(hill_size <= 0) break;       /* no suitable hills */
+       if(hill_size <= (gint64)0) break;       /* no suitable hills */
 
        /* Find all the dumps in that hill and try and remove one */
        for(dp = schedq.head; dp != NULL; dp = dp->next) {
+           one_est_t *level0_est;
            if(est(dp)->next_level0 != hill_days ||
               est(dp)->next_level0 > dp->maxpromoteday ||
               dp->skip_full ||
               dp->strategy == DS_NOFULL ||
               dp->strategy == DS_INCRONLY)
                continue;
-           new_size = est_tape_size(dp, 0);
-           new_total = total_size - est(dp)->dump_size + new_size;
+           level0_est = est_for_level(dp, 0);
+           if (level0_est->nsize <= (gint64)0)
+               continue;
+           new_total = total_size - est(dp)->dump_est->csize + level0_est->csize;
            if(new_total > tape_length)
                continue;
            /* We found a disk we can promote */
+           qname = quote_string(dp->name);
            total_size = new_total;
-           total_lev0 += new_size;
-           est(dp)->degr_level = est(dp)->dump_level;
-           est(dp)->degr_size = est(dp)->dump_size;
-           est(dp)->dump_level = 0;
+           total_lev0 += (double)level0_est->csize;
+            est(dp)->degr_est = est(dp)->dump_est;
+            est(dp)->dump_est = level0_est;
            est(dp)->next_level0 = 0;
-           est(dp)->dump_size = new_size;
 
-           fprintf(stderr,
-                   "   promote: moving %s:%s up, total_lev0 %1.0f, total_size " AM64_FMT "\n",
-                   dp->host->hostname, dp->name,
-                   total_lev0, total_size);
+           g_fprintf(stderr,
+                   _("   promote: moving %s:%s up, total_lev0 %1.0lf, total_size %lld\n"),
+                   dp->host->hostname, qname,
+                   total_lev0, (long long)total_size);
 
            log_add(L_INFO,
-                   "Full dump of %s:%s specially promoted from %d day%s ahead.",
-                   dp->host->hostname, dp->name,
-                   hill_days, (hill_days == 1) ? "" : "s");
+                   plural(_("Full dump of %s:%s specially promoted from %d day ahead."),
+                          _("Full dump of %s:%s specially promoted from %d days ahead."),
+                          hill_days),
+                   dp->host->hostname, qname, hill_days);
 
+           amfree(qname);
            amfree(sp);
            return 1;
        }
@@ -2621,94 +3110,142 @@ static int promote_hills P((void))
  *
  * XXX - memory leak - we shouldn't just throw away *dp
  */
-static void output_scheduleline(dp)
-    disk_t *dp;
+static void output_scheduleline(
+    disk_t *dp)
 {
     est_t *ep;
-    long dump_time = 0, degr_time = 0;
+    time_t dump_time = 0, degr_time = 0;
+    double dump_kps = 0, degr_kps = 0;
     char *schedline = NULL, *degr_str = NULL;
     char dump_priority_str[NUM_STR_SIZE];
     char dump_level_str[NUM_STR_SIZE];
-    char dump_size_str[NUM_STR_SIZE];
+    char dump_nsize_str[NUM_STR_SIZE];
+    char dump_csize_str[NUM_STR_SIZE];
     char dump_time_str[NUM_STR_SIZE];
+    char dump_kps_str[NUM_STR_SIZE];
     char degr_level_str[NUM_STR_SIZE];
-    char degr_size_str[NUM_STR_SIZE];
+    char degr_nsize_str[NUM_STR_SIZE];
+    char degr_csize_str[NUM_STR_SIZE];
     char degr_time_str[NUM_STR_SIZE];
+    char degr_kps_str[NUM_STR_SIZE];
     char *dump_date, *degr_date;
     char *features;
-    int i;
+    char *qname = quote_string(dp->name);
 
     ep = est(dp);
 
-    if(ep->dump_size == -1) {
+    if(ep->dump_est->csize == (gint64)-1) {
        /* no estimate, fail the disk */
-       fprintf(stderr,
-               "%s: FAILED %s %s %s %d [no estimate]\n",
+       g_fprintf(stderr,
+               _("%s: FAILED %s %s %s %d \"[no estimate]\"\n"),
                get_pname(),
-               dp->host->hostname, dp->name, datestamp, ep->dump_level);
-       log_add(L_FAIL, "%s %s %s %d [no estimate]",
-               dp->host->hostname, dp->name, datestamp, ep->dump_level);
+               dp->host->hostname, qname, planner_timestamp, ep->dump_est->level);
+       log_add(L_FAIL, _("%s %s %s %d [no estimate]"),
+               dp->host->hostname, qname, planner_timestamp, ep->dump_est->level);
+       amfree(qname);
        return;
     }
 
-    dump_date = degr_date = (char *)0;
-    for(i = 0; i < MAX_LEVELS; i++) {
-       if(ep->dump_level == ep->level[i])
-           dump_date = ep->dumpdate[i];
-       if(ep->degr_level == ep->level[i])
-           degr_date = ep->dumpdate[i];
-    }
+    dump_date = ep->dump_est->dumpdate;
+    degr_date = ep->degr_est->dumpdate;
 
 #define fix_rate(rate) (rate < 1.0 ? DEFAULT_DUMPRATE : rate)
 
-    if(ep->dump_level == 0) {
-       dump_time = ep->dump_size / fix_rate(ep->fullrate);
+    if(ep->dump_est->level == 0) {
+       dump_kps = fix_rate(ep->fullrate);
+       dump_time = (time_t)((double)ep->dump_est->csize / dump_kps);
 
-       if(ep->degr_size != -1) {
-           degr_time = ep->degr_size / fix_rate(ep->incrrate);
+       if(ep->degr_est->csize != (gint64)-1) {
+           degr_kps = fix_rate(ep->incrrate);
+           degr_time = (time_t)((double)ep->degr_est->csize / degr_kps);
        }
     }
     else {
-       dump_time = ep->dump_size / fix_rate(ep->incrrate);
-    }
-
-    if(ep->dump_level == 0 && ep->degr_size != -1) {
-       snprintf(degr_level_str, sizeof(degr_level_str),
-                   "%d", ep->degr_level);
-       snprintf(degr_size_str, sizeof(degr_size_str),
-                   "%ld", ep->degr_size);
-       snprintf(degr_time_str, sizeof(degr_time_str),
-                   "%ld", degr_time);
+       dump_kps = fix_rate(ep->incrrate);
+       dump_time = (time_t)((double)ep->dump_est->csize / dump_kps);
+    }
+
+    if(ep->dump_est->level == 0 && ep->degr_est->csize != (gint64)-1) {
+       g_snprintf(degr_level_str, sizeof(degr_level_str),
+                   "%d", ep->degr_est->level);
+       g_snprintf(degr_nsize_str, sizeof(degr_nsize_str),
+                   "%lld", (long long)ep->degr_est->nsize);
+       g_snprintf(degr_csize_str, sizeof(degr_csize_str),
+                   "%lld", (long long)ep->degr_est->csize);
+       g_snprintf(degr_time_str, sizeof(degr_time_str),
+                   "%lld", (long long)degr_time);
+       g_snprintf(degr_kps_str, sizeof(degr_kps_str),
+                   "%.0lf", degr_kps);
        degr_str = vstralloc(" ", degr_level_str,
                             " ", degr_date,
-                            " ", degr_size_str,
+                            " ", degr_nsize_str,
+                            " ", degr_csize_str,
                             " ", degr_time_str,
+                            " ", degr_kps_str,
                             NULL);
+    } else {
+       char *degr_mesg;
+       if (ep->degr_mesg) {
+           degr_mesg = quote_string(ep->degr_mesg);
+       } else {
+           degr_mesg = quote_string(_("Skipping: cannot dump in degraded mode for unknown reason"));
+       }
+       degr_str = vstralloc(" ", degr_mesg, NULL);
+       amfree(degr_mesg);
     }
-    snprintf(dump_priority_str, sizeof(dump_priority_str),
+    g_snprintf(dump_priority_str, SIZEOF(dump_priority_str),
                "%d", ep->dump_priority);
-    snprintf(dump_level_str, sizeof(dump_level_str),
-               "%d", ep->dump_level);
-    snprintf(dump_size_str, sizeof(dump_size_str),
-               "%ld", ep->dump_size);
-    snprintf(dump_time_str, sizeof(dump_time_str),
-               "%ld", dump_time);
+    g_snprintf(dump_level_str, SIZEOF(dump_level_str),
+               "%d", ep->dump_est->level);
+    g_snprintf(dump_nsize_str, sizeof(dump_nsize_str),
+               "%lld", (long long)ep->dump_est->nsize);
+    g_snprintf(dump_csize_str, sizeof(dump_csize_str),
+               "%lld", (long long)ep->dump_est->csize);
+    g_snprintf(dump_time_str, sizeof(dump_time_str),
+               "%lld", (long long)dump_time);
+    g_snprintf(dump_kps_str, sizeof(dump_kps_str),
+               "%.0lf", dump_kps);
     features = am_feature_to_string(dp->host->features);
     schedline = vstralloc("DUMP ",dp->host->hostname,
                          " ", features,
-                         " ", dp->name,
-                         " ", datestamp,
+                         " ", qname,
+                         " ", planner_timestamp,
                          " ", dump_priority_str,
                          " ", dump_level_str,
                          " ", dump_date,
-                         " ", dump_size_str,
+                         " ", dump_nsize_str,
+                         " ", dump_csize_str,
                          " ", dump_time_str,
+                         " ", dump_kps_str,
                          degr_str ? degr_str : "",
                          "\n", NULL);
 
+    if (est(dp)->dump_est->guessed == 1) {
+        log_add(L_WARNING, _("WARNING: no history available for %s:%s; guessing that size will be %lld KB\n"), dp->host->hostname, qname, (long long)est(dp)->dump_est->csize);
+    }
     fputs(schedline, stdout);
     fputs(schedline, stderr);
     amfree(features);
     amfree(schedline);
     amfree(degr_str);
+    amfree(qname);
+}
+
+static void
+server_estimate(
+    disk_t *dp,
+    int     i,
+    info_t *info,
+    int     level)
+{
+    int    stats;
+    gint64 size;
+
+    size = internal_server_estimate(dp, info, level, &stats);
+
+    est(dp)->dump_est = &est(dp)->estimate[i];
+    est(dp)->estimate[i].nsize = size;
+    if (stats == 0) {
+       est(dp)->estimate[i].guessed = 1;
+    }
 }