Imported Upstream version 2.6.0p2
[debian/amanda] / server-src / reporter.c
index a28d5b49a80830f1009d589ca57991c89cc4f45b..6f0e248678157a65224dff3e8d92f3fdd760acf1 100644 (file)
  *                        University of Maryland at College Park
  */
 /*
- * $Id: reporter.c,v 1.44.2.17.4.6.2.16.2.6 2005/03/29 16:35:11 martinea Exp $
+ * $Id: reporter.c,v 1.132 2006/08/28 17:02:48 martinea Exp $
  *
  * nightly Amanda Report generator
  */
 /*
-report format
-    tape label message
-    error messages
-    summary stats
-    details for errors
-    notes
-    success summary
-*/
+ * report format
+ *     tape label message
+ *     error messages
+ *     strange messages
+ *     summary stats
+ *     details for errors
+ *     details for strange
+ *     notes
+ *     success summary
+ */
 
 #include "amanda.h"
 #include "conffile.h"
+#include "columnar.h"
 #include "tapefile.h"
 #include "diskfile.h"
 #include "infofile.h"
 #include "logfile.h"
 #include "version.h"
 #include "util.h"
+#include "timestamp.h"
+#include "holding.h"
 
 /* don't have (or need) a skipped type except internally to reporter */
 #define L_SKIPPED      L_MARKER
 
+
+#define STATUS_STRANGE   2
+#define STATUS_FAILED    4
+#define STATUS_MISSING   8
+#define STATUS_TAPE     16
+
 typedef struct line_s {
     struct line_s *next;
     char *str;
@@ -58,110 +69,145 @@ typedef struct line_s {
 
 typedef struct timedata_s {
     logtype_t result;
-    float origsize, outsize;
+    double origsize, outsize;
     char *datestamp;
-    float sec, kps;
+    double sec, kps;
     int filenum;
     char *tapelabel;
+    int totpart;
 } timedata_t;
 
 typedef struct repdata_s {
     disk_t *disk;
     char *datestamp;
+    double est_nsize, est_csize;
     timedata_t taper;
     timedata_t dumper;
+    timedata_t chunker;
     int level;
     struct repdata_s *next;
 } repdata_t;
 
 #define data(dp) ((repdata_t *)(dp)->up)
 
-struct cumulative_stats {
-    int dumpdisks, tapedisks;
+static struct cumulative_stats {
+    int dumpdisks, tapedisks, tapechunks;
     double taper_time, dumper_time;
     double outsize, origsize, tapesize;
     double coutsize, corigsize;                        /* compressed dump only */
 } stats[3];
 
-int dumpdisks[10], tapedisks[10];      /* by-level breakdown of disk count */
+static int dumpdisks[10], tapedisks[10], tapechunks[10];       /* by-level breakdown of disk count */
 
 typedef struct taper_s {
     char *label;
     double taper_time;
     double coutsize, corigsize;
-    int tapedisks;
+    int tapedisks, tapechunks;
     struct taper_s *next;
 } taper_t;
 
-taper_t *stats_by_tape = NULL;
-taper_t *current_tape = NULL;
+static taper_t *stats_by_tape = NULL;
+static taper_t *current_tape = NULL;
+
+typedef struct X_summary_s {
+    char *hostname;
+    char *diskname;
+    int  level;
+    char *str;
+    struct X_summary_s *next;
+} X_summary_t;
 
-float total_time, startup_time, planner_time;
+static X_summary_t *first_strange=NULL, *last_strange=NULL;
+static X_summary_t *first_failed=NULL, *last_failed=NULL;
+
+static double total_time, startup_time, planner_time;
 
 /* count files to tape */
-int tapefcount = 0;
+static int tapefcount = 0;
 
-char *run_datestamp;
-char *today_datestamp;
-char *tape_labels = NULL;
-int last_run_tapes = 0;
+static int exit_status = 0;
+static char *run_datestamp;
+static char *tape_labels = NULL;
+static int last_run_tapes = 0;
 static int degraded_mode = 0; /* defined in driverio too */
-int normal_run = 0;
-int amflush_run = 0;
-int got_finish = 0;
+static int normal_run = 0;
+static int amflush_run = 0;
+static int got_finish = 0;
+static int cmdlogfname = 0;
+static char *ghostname = NULL;
 
-char *tapestart_error = NULL;
+static char *tapestart_error = NULL;
 
-FILE *logfile, *mailf;
+static FILE *logfile, *mailf;
 
-FILE *postscript;
-char *printer;
+static FILE *postscript;
+static char *printer;
 
-disklist_t *diskq;
-disklist_t sortq;
+static disklist_t diskq;
+static disklist_t sortq;
 
-line_t *errsum = NULL;
-line_t *errdet = NULL;
-line_t *notes = NULL;
+static line_t *errsum = NULL;
+static line_t *errdet = NULL;
+static line_t *strangedet = NULL;
+static line_t *notes = NULL;
 
 static char MaxWidthsRequested = 0;    /* determined via config data */
 
-char *displayunit;
-long int unitdivisor;
+static char *displayunit;
+static long int unitdivisor;
 
 /* local functions */
-int contline_next P((void));
-void addline P((line_t **lp, char *str));
-void usage P((void));
-int main P((int argc, char **argv));
-
-void copy_template_file P((char *lbl_templ));
-void do_postscript_output P((void));
-void handle_start P((void));
-void handle_finish P((void));
-void handle_note P((void));
-void handle_summary P((void));
-void handle_stats P((void));
-void handle_error P((void));
-void handle_disk P((void));
-repdata_t *handle_success P((void));
-void handle_strange P((void));
-void handle_failed P((void));
-void generate_missing P((void));
-void output_tapeinfo P((void));
-void output_lines P((line_t *lp, FILE *f));
-void output_stats P((void));
-void output_summary P((void));
-void sort_disks P((void));
-int sort_by_time P((disk_t *a, disk_t *b));
-int sort_by_name P((disk_t *a, disk_t *b));
-void bogus_line P((void));
-char *nicedate P((int datestamp));
-static char *prefix P((char *host, char *disk, int level));
-repdata_t *find_repdata P((disk_t *dp, char *datestamp, int level));
-
-
-static int ColWidth(int From, int To) {
+int main(int argc, char **argv);
+
+static char *  nicedate(const char * datestamp);
+static char *  prefix(char *host, char *disk, int level);
+static char *  prefixstrange(char *host, char *disk, int level,
+                       size_t len_host, size_t len_disk);
+static char *  Rule(int From, int To);
+static char *  sDivZero(double a, double b, int cn);
+static char *  TextRule(int From, int To, char *s);
+static int     ColWidth(int From, int To);
+static int     contline_next(void);
+static int     sort_by_name(disk_t *a, disk_t *b);
+static repdata_t *find_repdata(disk_t *dp, char *datestamp, int level);
+static repdata_t *handle_chunk(logtype_t logtype);
+static repdata_t *handle_success(logtype_t logtype);
+static void    addline(line_t **lp, char *str);
+static void    addtoX_summary(X_summary_t **first, X_summary_t **last,
+                              char *host, char *disk, int level, char *str);
+static void    bogus_line(const char *);
+static void    CalcMaxWidth(void);
+static void    CheckFloatMax(ColumnInfo *cd, double d);
+static void    CheckIntMax(ColumnInfo *cd, int n);
+static void    CheckStringMax(ColumnInfo *cd, char *s);
+static void    copy_template_file(char *lbl_templ);
+static void    do_postscript_output(void);
+static void    generate_missing(void);
+static void    generate_bad_estimate(void);
+static void    handle_disk(void);
+static void    handle_error(void);
+static void    handle_failed(void);
+static void    handle_finish(void);
+static void    handle_note(void);
+static void    handle_partial(void);
+static void    handle_start(void);
+static void    handle_stats(void);
+static void    handle_strange(void);
+static void    handle_summary(void);
+static void    output_lines(line_t *lp, FILE *f);
+static void    output_stats(void);
+static void    output_X_summary(X_summary_t *first);
+static void    output_summary(void);
+static void    output_tapeinfo(void);
+static void    sort_disks(void);
+static void    usage(void);
+
+static int
+ColWidth(
+    int                From,
+    int                To)
+{
     int i, Width= 0;
     for (i=From; i<=To && ColumnData[i].Name != NULL; i++) {
        Width+= ColumnData[i].PrefixSpace + ColumnData[i].Width;
@@ -169,10 +215,14 @@ static int ColWidth(int From, int To) {
     return Width;
 }
 
-static char *Rule(int From, int To) {
+static char *
+Rule(
+    int                From,
+    int                To)
+{
     int i, ThisLeng;
     int Leng= ColWidth(0, ColumnDataCount());
-    char *RuleSpace= alloc(Leng+1);
+    char *RuleSpace= alloc((size_t)(Leng+1));
     ThisLeng= ColWidth(From, To);
     for (i=0;i<ColumnData[From].PrefixSpace; i++)
        RuleSpace[i]= ' ';
@@ -182,17 +232,23 @@ static char *Rule(int From, int To) {
     return RuleSpace;
 }
 
-static char *TextRule(int From, int To, char *s) {
+static char *
+TextRule(
+    int                From,
+    int                To,
+    char *     s)
+{
     ColumnInfo *cd= &ColumnData[From];
-    int leng, nbrules, i, txtlength;
+    int leng;
+    int nbrules, i, txtlength;
     int RuleSpaceSize= ColWidth(0, ColumnDataCount());
-    char *RuleSpace= alloc(RuleSpaceSize), *tmp;
+    char *RuleSpace= alloc((size_t)RuleSpaceSize), *tmp;
 
-    lengstrlen(s);
+    leng = (int)strlen(s);
     if(leng >= (RuleSpaceSize - cd->PrefixSpace))
        leng = RuleSpaceSize - cd->PrefixSpace - 1;
-    ap_snprintf(RuleSpace, RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "", 
-               leng, leng, s);
+    g_snprintf(RuleSpace, (size_t)RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "", 
+            leng, leng, s);
     txtlength = cd->PrefixSpace + leng + 1;
     nbrules = ColWidth(From,To) - txtlength;
     for(tmp=RuleSpace + txtlength, i=nbrules ; i>0; tmp++,i--)
@@ -201,85 +257,111 @@ static char *TextRule(int From, int To, char *s) {
     return RuleSpace;
 }
 
-char *sDivZero(float a, float b, int cn) {
+static char *
+sDivZero(
+    double     a,
+    double     b,
+    int                cn)
+{
     ColumnInfo *cd= &ColumnData[cn];
     static char PrtBuf[256];
-    if (b == 0.0)
-       ap_snprintf(PrtBuf, sizeof(PrtBuf),
+    if (!isnormal(b))
+       g_snprintf(PrtBuf, SIZEOF(PrtBuf),
          "%*s", cd->Width, "-- ");
     else
-       ap_snprintf(PrtBuf, sizeof(PrtBuf),
+       g_snprintf(PrtBuf, SIZEOF(PrtBuf),
          cd->Format, cd->Width, cd->Precision, a/b);
     return PrtBuf;
 }
 
-
-
-int contline_next()
+static int
+contline_next(void)
 {
     int ch;
 
-    ch = getc(logfile);
-    ungetc(ch, logfile);
-
+    if ((ch = getc(logfile)) != EOF) {
+           if (ungetc(ch, logfile) == EOF) {
+               if (ferror(logfile)) {
+                   error(_("ungetc failed: %s\n"), strerror(errno));
+                   /*NOTREACHED*/
+               }
+               error(_("ungetc failed: EOF\n"));
+               /*NOTREACHED*/
+           }
+    }
     return ch == ' ';
 }
 
-void addline(lp, str)
-line_t **lp;
-char *str;
+static void
+addline(
+    line_t **  lp,
+    char *     str)
 {
-    line_t *new, *p, *q;
+    line_t *new, *p;
 
     /* allocate new line node */
-    new = (line_t *) alloc(sizeof(line_t));
+    new = (line_t *) alloc(SIZEOF(line_t));
     new->next = NULL;
     new->str = stralloc(str);
 
     /* add to end of list */
-    for(p = *lp, q = NULL; p != NULL; q = p, p = p->next);
-    if(q == NULL) *lp = new;
-    else q->next = new;
+    p = *lp;
+    if (p == NULL) {
+       *lp = new;
+    } else {
+       while (p->next != NULL)
+           p = p->next;
+       p->next = new;  
+    }
 }
 
-void usage()
+static void
+usage(void)
 {
-    error("Usage: amreport conf [-f output-file] [-l logfile] [-p postscript-file]");
+    error(_("Usage: amreport conf [-i] [-M address] [-f output-file] [-l logfile] [-p postscript-file] [-o configoption]*"));
+    /*NOTREACHED*/
 }
 
-int main(argc, argv)
-int argc;
-char **argv;
+int
+main(
+    int                argc,
+    char **    argv)
 {
-    char *conffile;
     char *conf_diskfile;
     char *conf_tapelist;
     char *conf_infofile;
     char *logfname, *psfname, *outfname, *subj_str = NULL;
     tapetype_t *tp;
-    int fd, opt;
-    unsigned long malloc_hist_1, malloc_size_1;
-    unsigned long malloc_hist_2, malloc_size_2;
+    int opt;
     char *mail_cmd = NULL, *printer_cmd = NULL;
     extern int optind;
-    char my_cwd[STR_SIZE];
+    char * cwd = NULL;
     char *ColumnSpec = "";
     char *errstr = NULL;
     int cn;
-
-    for(fd = 3; fd < FD_SETSIZE; fd++) {
-       /*
-        * Make sure nobody spoofs us with a lot of extra open files
-        * that would cause an open we do to get a very high file
-        * descriptor, which in turn might be used as an index into
-        * an array (e.g. an fd_set).
-        */
-       close(fd);
-    }
+    int mailout = 1;
+    char *mailto = NULL;
+    char *lbl_templ = NULL;
+    config_overwrites_t *cfg_ovr = NULL;
+    char *cfg_opt = NULL;
+
+    /*
+     * 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"); 
+
+    safe_fd(-1, 0);
 
     set_pname("amreport");
 
-    malloc_size_1 = malloc_inuse(&malloc_hist_1);
+    dbopen(DBG_SUBDIR_SERVER);
+
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
 
     /* Process options */
     
@@ -287,127 +369,145 @@ char **argv;
     outfname = NULL;
     psfname = NULL;
     logfname = NULL;
+    cmdlogfname = 0;
 
-    if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
-       error("cannot determine current working directory");
+    cwd = g_get_current_dir();
+    if (cwd == NULL) {
+       error(_("Cannot determine current working directory: %s"),
+             strerror(errno));
+       /*NOTREACHED*/
     }
 
-    if (argc < 2) {
-       config_dir = stralloc2(my_cwd, "/");
-       if ((config_name = strrchr(my_cwd, '/')) != NULL) {
-           config_name = stralloc(config_name + 1);
-       }
-    } else {
+    if (argc >= 2) {
        if (argv[1][0] == '-') {
            usage();
            return 1;
        }
-       config_name = stralloc(argv[1]);
-       config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
+
+       /* get the config name and move past it */
+       cfg_opt = argv[1];
        --argc; ++argv;
-       while((opt = getopt(argc, argv, "f:l:p:")) != EOF) {
+
+       cfg_ovr = new_config_overwrites(argc/2);
+       while((opt = getopt(argc, argv, "o:M:f:l:p:i")) != EOF) {
            switch(opt) {
+           case 'i': 
+               mailout = 0;
+               break;
+            case 'M':
+               if (mailto != NULL) {
+                   error(_("you may specify at most one -M"));
+                   /*NOTREACHED*/
+               }
+                mailto = stralloc(optarg);
+               if(!validate_mailto(mailto)) {
+                   error(_("mail address has invalid characters"));
+                   /*NOTREACHED*/
+               }
+                break;
             case 'f':
                if (outfname != NULL) {
-                   error("you may specify at most one -f");
+                   error(_("you may specify at most one -f"));
+                   /*NOTREACHED*/
                }
                if (*optarg == '/') {
                     outfname = stralloc(optarg);
                } else {
-                    outfname = vstralloc(my_cwd, "/", optarg, NULL);
+                    outfname = vstralloc(cwd, "/", optarg, NULL);
                }
                 break;
             case 'l':
+               cmdlogfname = 1;
                if (logfname != NULL) {
-                   error("you may specify at most one -l");
+                   error(_("you may specify at most one -l"));
+                   /*NOTREACHED*/
                }
                if (*optarg == '/') {
                    logfname = stralloc(optarg);
                } else {
-                    logfname = vstralloc(my_cwd, "/", optarg, NULL);
+                    logfname = vstralloc(cwd, "/", optarg, NULL);
                }
                 break;
             case 'p':
                if (psfname != NULL) {
-                   error("you may specify at most one -p");
+                   error(_("you may specify at most one -p"));
+                   /*NOTREACHED*/
                }
                if (*optarg == '/') {
                     psfname = stralloc(optarg);
                } else {
-                    psfname = vstralloc(my_cwd, "/", optarg, NULL);
+                    psfname = vstralloc(cwd, "/", optarg, NULL);
                }
                 break;
+           case 'o':
+               add_config_overwrite_opt(cfg_ovr, optarg);
+               break;
             case '?':
-            default:
                usage();
                return 1;
+            default:
+               break;
            }
        }
 
        argc -= optind;
        argv += optind;
-
-       if (argc > 1) {
-           usage();
-           return 1;
-       }
+    }
+    if( !mailout && mailto ){
+       g_printf(_("You cannot specify both -i & -M at the same time\n"));
+       exit(1);
     }
 
+    amfree(cwd);
+
 #if !defined MAILER
     if(!outfname) {
-       printf("You must run amreport with '-f <output file>' because configure\n");
-       printf("didn't find a mailer.\n");
+       g_printf(_("You must run amreport with '-f <output file>' because configure\n"));
+       g_printf(_("didn't find a mailer.\n"));
        exit (1);
     }
 #endif
 
-    safe_cd();
-
     /* read configuration files */
 
-    conffile = stralloc2(config_dir, CONFFILE_NAME);
-    if(read_conffile(conffile)) {
-        error("errors processing config file \"%s\"", conffile);
-    }
-    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((diskq = read_diskfile(conf_diskfile)) == NULL) {
-       error("could not load disklist \"%s\"", conf_diskfile);
-    }
+    /* ignore any errors reading the config file (amreport can run without a config) */
+    config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
+    if (cfg_ovr) apply_config_overwrites(cfg_ovr);
+
+    check_running_as(RUNNING_AS_DUMPUSER);
+
+    dbrename(config_name, DBG_SUBDIR_SERVER);
+
+    safe_cd(); /* must be called *after* config_init() */
+
+    conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
+    /* Ignore error from read_diskfile */
+    read_diskfile(conf_diskfile, &diskq);
     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);
-    }
-    if(read_tapelist(conf_tapelist)) {
-       error("could not read tapelist \"%s\"", conf_tapelist);
+    if(mailout && !mailto && 
+       getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0) {
+               mailto = getconf_str(CNF_MAILTO);
+                if(!validate_mailto(mailto)){
+                  mailto = NULL;
+                }
     }
+    
+    conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
+    /* Ignore error from read_tapelist */
+    read_tapelist(conf_tapelist);
     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*/
     }
     amfree(conf_infofile);
 
-    today_datestamp = construct_datestamp(NULL);
-
     displayunit = getconf_str(CNF_DISPLAYUNIT);
     unitdivisor = getconf_unit_divisor();
 
     ColumnSpec = getconf_str(CNF_COLUMNSPEC);
-    if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
+    if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
        curlog = L_ERROR;
        curprog = P_REPORTER;
        curstr = errstr;
@@ -415,7 +515,7 @@ char **argv;
         amfree(errstr);
        curstr = NULL;
        ColumnSpec = "";                /* use the default */
-       if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
+       if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
            curlog = L_ERROR;
            curprog = P_REPORTER;
            curstr = errstr;
@@ -434,12 +534,7 @@ char **argv;
     if(!logfname) {
        char *conf_logdir;
 
-       conf_logdir = getconf_str(CNF_LOGDIR);
-       if (*conf_logdir == '/') {
-           conf_logdir = stralloc(conf_logdir);
-       } else {
-           conf_logdir = stralloc2(config_dir, conf_logdir);
-       }
+       conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
        logfname = vstralloc(conf_logdir, "/", "log", NULL);
        amfree(conf_logdir);
     }
@@ -447,7 +542,7 @@ char **argv;
     if((logfile = fopen(logfname, "r")) == NULL) {
        curlog = L_ERROR;
        curprog = P_REPORTER;
-       curstr = vstralloc("could not open log ",
+       curstr = vstralloc(_("could not open log "),
                           logfname,
                           ": ",
                           strerror(errno),
@@ -458,41 +553,49 @@ char **argv;
 
     while(logfile && get_logline(logfile)) {
        switch(curlog) {
-       case L_START:   handle_start(); break;
-       case L_FINISH:  handle_finish(); break;
+       case L_START:        handle_start(); break;
+       case L_FINISH:       handle_finish(); break;
 
-       case L_INFO:    handle_note(); break;
-       case L_WARNING: handle_note(); break;
+       case L_INFO:         handle_note(); break;
+       case L_WARNING:      handle_note(); break;
 
-       case L_SUMMARY: handle_summary(); break;
-       case L_STATS:   handle_stats(); break;
+       case L_SUMMARY:      handle_summary(); break;
+       case L_STATS:        handle_stats(); break;
 
-       case L_ERROR:   handle_error(); break;
-       case L_FATAL:   handle_error(); break;
+       case L_ERROR:        handle_error(); break;
+       case L_FATAL:        handle_error(); break;
 
-       case L_DISK:    handle_disk(); break;
+       case L_DISK:         handle_disk(); break;
 
-       case L_SUCCESS: handle_success(); break;
-       case L_STRANGE: handle_strange(); break;
-       case L_FAIL:    handle_failed(); break;
+       case L_DONE:         handle_success(curlog); break;
+       case L_SUCCESS:      handle_success(curlog); break;
+       case L_CHUNKSUCCESS: handle_success(curlog); break;
+       case L_PART:         handle_chunk(curlog); break;
+       case L_PARTPARTIAL:  handle_chunk(curlog); break;
+       case L_CHUNK:        handle_chunk(curlog); break;
+       case L_PARTIAL:      handle_partial(); break;
+       case L_STRANGE:      handle_strange(); break;
+       case L_FAIL:         handle_failed(); break;
 
        default:
            curlog = L_ERROR;
            curprog = P_REPORTER;
-           curstr = stralloc2("unexpected log line: ", curstr);
+           curstr = vstrallocf(_("unexpected log line: %s"), curstr);
            handle_error();
            amfree(curstr);
        }
     }
     afclose(logfile);
     close_infofile();
-    if(!amflush_run)
+    if(!amflush_run) {
        generate_missing();
+       generate_bad_estimate();
+    }
 
     subj_str = vstralloc(getconf_str(CNF_ORG),
                         " ", amflush_run ? "AMFLUSH" : "AMANDA",
                         " ", "MAIL REPORT FOR",
-                        " ", nicedate(run_datestamp ? atoi(run_datestamp) : 0),
+                        " ", nicedate(run_datestamp ? run_datestamp : "0"),
                         NULL);
        
     /* lookup the tapetype and printer type from the amanda.conf file. */
@@ -507,20 +610,33 @@ char **argv;
     if(outfname) {
        /* output to a file */
        if((mailf = fopen(outfname,"w")) == NULL) {
-           error("could not open output file: %s %s", outfname, strerror(errno));
+           error(_("could not open output file: %s %s"), outfname, strerror(errno));
+           /*NOTREACHED*/
+       }
+        if (mailto != NULL) {
+               g_fprintf(mailf, "To: %s\n", mailto);
+               g_fprintf(mailf, "Subject: %s\n\n", subj_str);
        }
-       fprintf(mailf, "To: %s\n", getconf_str(CNF_MAILTO));
-       fprintf(mailf, "Subject: %s\n\n", subj_str);
 
     } else {
 #ifdef MAILER
-       mail_cmd = vstralloc(MAILER,
+       if(mailto) {
+               mail_cmd = vstralloc(MAILER,
                             " -s", " \"", subj_str, "\"",
-                            " ", getconf_str(CNF_MAILTO),
-                            NULL);
-       if((mailf = popen(mail_cmd, "w")) == NULL)
-           error("could not open pipe to \"%s\": %s",
-                 mail_cmd, strerror(errno));
+                            " ", mailto, NULL);
+               if((mailf = popen(mail_cmd, "w")) == NULL) {
+               error(_("could not open pipe to \"%s\": %s"),
+                       mail_cmd, strerror(errno));
+               /*NOTREACHED*/
+               }
+       }
+       else {
+               if(mailout) {
+                   g_printf(_("No mail sent! "));
+                  g_printf(_("No valid mail address has been specified in amanda.conf or on the commmand line\n"));
+               }
+               mailf = NULL;
+       }
 #endif
     }
 
@@ -530,15 +646,15 @@ char **argv;
        /* if the postscript_label_template (tp->lbl_templ) field is not */
        /* the empty string (i.e. it is set to something), open the      */
        /* postscript debugging file for writing.                        */
-       if ((strcmp(tp->lbl_templ, "")) != 0) {
+       if (tp)
+           lbl_templ = tapetype_get_lbl_templ(tp);
+       if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
            if ((postscript = fopen(psfname, "w")) == NULL) {
                curlog = L_ERROR;
                curprog = P_REPORTER;
-               curstr = vstralloc("could not open ",
+               curstr = vstrallocf(_("could not open %s: %s"),
                                   psfname,
-                                  ": ",
-                                  strerror(errno),
-                                  NULL);
+                                  strerror(errno));
                handle_error();
                amfree(curstr);
            }
@@ -556,24 +672,22 @@ char **argv;
            /* print to the default printer */
            printer_cmd = vstralloc(LPRCMD, NULL);
 #endif
-
-       if ((strcmp(tp->lbl_templ, "")) != 0) {
+       if (tp)
+           lbl_templ = tapetype_get_lbl_templ(tp);
+       if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
 #ifdef LPRCMD
            if ((postscript = popen(printer_cmd, "w")) == NULL) {
                curlog = L_ERROR;
                curprog = P_REPORTER;
-               curstr = vstralloc("could not open pipe to ",
-                                  printer_cmd,
-                                  ": ",
-                                  strerror(errno),
-                                  NULL);
+               curstr = vstrallocf(_("could not open pipe to %s: %s"),
+                                  printer_cmd, strerror(errno));
                handle_error();
                amfree(curstr);
            }
 #else
            curlog = L_ERROR;
            curprog = P_REPORTER;
-           curstr = stralloc("no printer command defined");
+           curstr = vstrallocf(_("no printer command defined"));
            handle_error();
            amfree(curstr);
 #endif
@@ -582,34 +696,59 @@ char **argv;
 
     amfree(subj_str);
 
+    sort_disks();
 
-    if(!got_finish) fputs("*** THE DUMPS DID NOT FINISH PROPERLY!\n\n", mailf);
-
-    output_tapeinfo();
+    if(mailf) {
 
-    if(errsum) {
-       fprintf(mailf,"\nFAILURE AND STRANGE DUMP SUMMARY:\n");
-       output_lines(errsum, mailf);
-    }
-    fputs("\n\n", mailf);
+       if(!got_finish) fputs(_("*** THE DUMPS DID NOT FINISH PROPERLY!\n\n"), mailf);
 
-    output_stats();
+       if (ghostname) {
+           g_fprintf(mailf, _("Hostname: %s\n"), ghostname);
+           g_fprintf(mailf, _("Org     : %s\n"), getconf_str(CNF_ORG));
+           g_fprintf(mailf, _("Config  : %s\n"), config_name);
+           g_fprintf(mailf, _("Date    : %s\n"),
+                   nicedate(run_datestamp ? run_datestamp : "0"));
+           g_fprintf(mailf,"\n");
+       }
 
-    if(errdet) {
-       fprintf(mailf,"\n\014\nFAILED AND STRANGE DUMP DETAILS:\n");
-       output_lines(errdet, mailf);
-    }
-    if(notes) {
-       fprintf(mailf,"\n\014\nNOTES:\n");
-       output_lines(notes, mailf);
-    }
-    sort_disks();
-    if(sortq.head != NULL) {
-       fprintf(mailf,"\n\014\nDUMP SUMMARY:\n");
-       output_summary();
+       output_tapeinfo();
+
+       if(first_failed || errsum) {
+               g_fprintf(mailf,_("\nFAILURE DUMP SUMMARY:\n"));
+               if(first_failed) output_X_summary(first_failed);
+               if(errsum) output_lines(errsum, mailf);
+       }
+       if(first_strange) {
+               g_fprintf(mailf,_("\nSTRANGE DUMP SUMMARY:\n"));
+               if(first_strange) output_X_summary(first_strange);
+       }
+       fputs("\n\n", mailf);
+       
+       output_stats();
+       
+       if(errdet) {
+               g_fprintf(mailf,"\n\f\n");
+               g_fprintf(mailf,_("FAILED DUMP DETAILS:\n"));
+               output_lines(errdet, mailf);
+       }
+       if(strangedet) {
+               g_fprintf(mailf,"\n\f\n");
+               g_fprintf(mailf,_("STRANGE DUMP DETAILS:\n"));
+               output_lines(strangedet, mailf);
+       }
+       if(notes) {
+               g_fprintf(mailf,"\n\f\n");
+               g_fprintf(mailf,_("NOTES:\n"));
+               output_lines(notes, mailf);
+       }
+       if(sortq.head != NULL) {
+               g_fprintf(mailf,"\n\f\n");
+               g_fprintf(mailf,_("DUMP SUMMARY:\n"));
+               output_summary();
+       }
+       g_fprintf(mailf,_("\n(brought to you by Amanda version %s)\n"),
+               version());
     }
-    fprintf(mailf,"\n(brought to you by Amanda version %s)\n",
-           version());
 
     if (postscript) {
        do_postscript_output();
@@ -622,8 +761,10 @@ char **argv;
        afclose(postscript);
     }
     else {
-       if (postscript != NULL && pclose(postscript) != 0)
-           error("printer command failed: %s", printer_cmd);
+       if (postscript != NULL && pclose(postscript) != 0) {
+           error(_("printer command failed: %s"), printer_cmd);
+           /*NOTREACHED*/
+       }
        postscript = NULL;
     }
 
@@ -631,27 +772,26 @@ char **argv;
     if(outfname) {
         afclose(mailf);
     }
-    else {
-        if(pclose(mailf) != 0)
-            error("mail command failed: %s", mail_cmd);
+    else if(mailf) {
+       int exitcode;
+        if((exitcode = pclose(mailf)) != 0) {
+           char *exitstr = str_exit_status("mail command", exitcode);
+            error(exitstr);
+           /*NOTREACHED*/
+       }
         mailf = NULL;
     }
 
+    clear_tapelist();
+    free_disklist(&diskq);
     amfree(run_datestamp);
     amfree(tape_labels);
-    amfree(config_dir);
-    amfree(config_name);
     amfree(printer_cmd);
     amfree(mail_cmd);
     amfree(logfname);
 
-    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);
-    }
-
-    return 0;
+    dbclose();
+    return exit_status;
 }
 
 /* ----- */
@@ -665,34 +805,44 @@ char **argv;
 #define divzero(fp,a,b)                            \
     do {                                           \
        double q = (b);                     \
-       if (q == 0.0)                       \
-           fprintf((fp),"  -- ");          \
+       if (!isnormal(q))                   \
+           g_fprintf((fp),"  -- ");        \
        else if ((q = (a)/q) >= 999.95)     \
-           fprintf((fp), "###.#");         \
+           g_fprintf((fp), "###.#");       \
        else                                \
-           fprintf((fp), "%5.1f",q);       \
+           g_fprintf((fp), "%5.1lf",q);            \
     } while(0)
 #define divzero_wide(fp,a,b)               \
     do {                                           \
        double q = (b);                     \
-       if (q == 0.0)                       \
-           fprintf((fp),"    -- ");        \
+       if (!isnormal(q))                   \
+           g_fprintf((fp),"    -- ");      \
        else if ((q = (a)/q) >= 99999.95)   \
-           fprintf((fp), "#####.#");       \
+           g_fprintf((fp), "#####.#");     \
        else                                \
-           fprintf((fp), "%7.1f",q);       \
+           g_fprintf((fp), "%7.1lf",q);            \
     } while(0)
 
-void output_stats()
+static void
+output_stats(void)
 {
+    double idle_time;
     tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
-    int tapesize, marksize, lv, first;
+    off_t tapesize;
+    off_t marksize;
+    int lv, first;
 
-    tapesize = tp->length;
-    marksize = tp->filemark;
+    if (tp) {
+       tapesize = tapetype_get_length(tp);
+       marksize = tapetype_get_filemark(tp);
+    } else {
+       tapesize = 100 * 1024 * 1024;
+       marksize = 1   * 1024 * 1024;
+    }
 
     stats[2].dumpdisks   = stats[0].dumpdisks   + stats[1].dumpdisks;
     stats[2].tapedisks   = stats[0].tapedisks   + stats[1].tapedisks;
+    stats[2].tapechunks  = stats[0].tapechunks  + stats[1].tapechunks;
     stats[2].outsize     = stats[0].outsize     + stats[1].outsize;
     stats[2].origsize    = stats[0].origsize    + stats[1].origsize;
     stats[2].tapesize    = stats[0].tapesize    + stats[1].tapesize;
@@ -704,214 +854,280 @@ void output_stats()
     if(!got_finish)    /* no driver finish line, estimate total run time */
        total_time = stats[2].taper_time + planner_time;
 
-    fprintf(mailf,"STATISTICS:\n");
-    fprintf(mailf,
-           "                          Total       Full      Incr.\n");
-    fprintf(mailf,
-           "                        --------   --------   --------\n");
+    idle_time = (total_time - startup_time) - stats[2].taper_time;
+    if(idle_time < 0) idle_time = 0.0;
+
+    g_fprintf(mailf,_("STATISTICS:\n"));
+    g_fprintf(mailf,
+           _("                          Total       Full      Incr.\n"));
+    g_fprintf(mailf,
+           _("                        --------   --------   --------\n"));
 
-    fprintf(mailf,
-           "Estimate Time (hrs:min)   %2d:%02d\n", hrmn(planner_time));
+    g_fprintf(mailf,
+           _("Estimate Time (hrs:min)   %2d:%02d\n"), hrmn(planner_time));
 
-    fprintf(mailf,
-           "Run Time (hrs:min)        %2d:%02d\n", hrmn(total_time));
+    g_fprintf(mailf,
+           _("Run Time (hrs:min)        %2d:%02d\n"), hrmn(total_time));
 
-    fprintf(mailf,
-           "Dump Time (hrs:min)       %2d:%02d      %2d:%02d      %2d:%02d\n",
+    g_fprintf(mailf,
+           _("Dump Time (hrs:min)       %2d:%02d      %2d:%02d      %2d:%02d\n"),
            hrmn(stats[2].dumper_time), hrmn(stats[0].dumper_time),
            hrmn(stats[1].dumper_time));
 
-    fprintf(mailf,
-           "Output Size (meg)      %8.1f   %8.1f   %8.1f\n",
+    g_fprintf(mailf,
+           _("Output Size (meg)      %8.1lf   %8.1lf   %8.1lf\n"),
            mb(stats[2].outsize), mb(stats[0].outsize), mb(stats[1].outsize));
 
-    fprintf(mailf,
-           "Original Size (meg)    %8.1f   %8.1f   %8.1f\n",
+    g_fprintf(mailf,
+           _("Original Size (meg)    %8.1lf   %8.1lf   %8.1lf\n"),
            mb(stats[2].origsize), mb(stats[0].origsize),
            mb(stats[1].origsize));
 
-    fprintf(mailf, "Avg Compressed Size (%%)   ");
+    g_fprintf(mailf, _("Avg Compressed Size (%%)   "));
     divzero(mailf, pct(stats[2].coutsize),stats[2].corigsize);
-    fputs("      ", mailf);
+    fputs(_("      "), mailf);
     divzero(mailf, pct(stats[0].coutsize),stats[0].corigsize);
-    fputs("      ", mailf);
+    fputs(_("      "), mailf);
     divzero(mailf, pct(stats[1].coutsize),stats[1].corigsize);
 
-    if(stats[1].dumpdisks > 0) fputs("   (level:#disks ...)", mailf);
+    if(stats[1].dumpdisks > 0) fputs(_("   (level:#disks ...)"), mailf);
     putc('\n', mailf);
 
-    fprintf(mailf,
-           "Filesystems Dumped         %4d       %4d       %4d",
+    g_fprintf(mailf,
+           _("Filesystems Dumped         %4d       %4d       %4d"),
            stats[2].dumpdisks, stats[0].dumpdisks, stats[1].dumpdisks);
 
     if(stats[1].dumpdisks > 0) {
        first = 1;
        for(lv = 1; lv < 10; lv++) if(dumpdisks[lv]) {
-           fputs(first?"   (":" ", mailf);
+           fputs(first?_("   ("):_(" "), mailf);
            first = 0;
-           fprintf(mailf, "%d:%d", lv, dumpdisks[lv]);
+           g_fprintf(mailf, _("%d:%d"), lv, dumpdisks[lv]);
        }
        putc(')', mailf);
     }
     putc('\n', mailf);
 
-    fprintf(mailf, "Avg Dump Rate (k/s)     ");
+    g_fprintf(mailf, _("Avg Dump Rate (k/s)     "));
     divzero_wide(mailf, stats[2].outsize,stats[2].dumper_time);
-    fputs("    ", mailf);
+    fputs(_("    "), mailf);
     divzero_wide(mailf, stats[0].outsize,stats[0].dumper_time);
-    fputs("    ", mailf);
+    fputs(_("    "), mailf);
     divzero_wide(mailf, stats[1].outsize,stats[1].dumper_time);
     putc('\n', mailf);
 
     putc('\n', mailf);
-    fprintf(mailf,
-           "Tape Time (hrs:min)       %2d:%02d      %2d:%02d      %2d:%02d\n",
+    g_fprintf(mailf,
+           _("Tape Time (hrs:min)       %2d:%02d      %2d:%02d      %2d:%02d\n"),
            hrmn(stats[2].taper_time), hrmn(stats[0].taper_time),
            hrmn(stats[1].taper_time));
 
-    fprintf(mailf,
-           "Tape Size (meg)        %8.1f   %8.1f   %8.1f\n",
+    g_fprintf(mailf,
+           _("Tape Size (meg)        %8.1lf   %8.1lf   %8.1lf\n"),
            mb(stats[2].tapesize), mb(stats[0].tapesize),
            mb(stats[1].tapesize));
 
-    fprintf(mailf, "Tape Used (%%)             ");
-    divzero(mailf, pct(stats[2].tapesize+marksize*stats[2].tapedisks),tapesize);
-    fputs("      ", mailf);
-    divzero(mailf, pct(stats[0].tapesize+marksize*stats[0].tapedisks),tapesize);
-    fputs("      ", mailf);
-    divzero(mailf, pct(stats[1].tapesize+marksize*stats[1].tapedisks),tapesize);
+    g_fprintf(mailf, _("Tape Used (%%)             "));
+    divzero(mailf, pct(stats[2].tapesize+marksize*(stats[2].tapedisks+stats[2].tapechunks)),(double)tapesize);
+    fputs(_("      "), mailf);
+    divzero(mailf, pct(stats[0].tapesize+marksize*(stats[0].tapedisks+stats[0].tapechunks)),(double)tapesize);
+    fputs(_("      "), mailf);
+    divzero(mailf, pct(stats[1].tapesize+marksize*(stats[1].tapedisks+stats[1].tapechunks)),(double)tapesize);
 
-    if(stats[1].tapedisks > 0) fputs("   (level:#disks ...)", mailf);
+    if(stats[1].tapedisks > 0) fputs(_("   (level:#disks ...)"), mailf);
     putc('\n', mailf);
 
-    fprintf(mailf,
-           "Filesystems Taped          %4d       %4d       %4d",
+    g_fprintf(mailf,
+           _("Filesystems Taped          %4d       %4d       %4d"),
            stats[2].tapedisks, stats[0].tapedisks, stats[1].tapedisks);
 
     if(stats[1].tapedisks > 0) {
        first = 1;
        for(lv = 1; lv < 10; lv++) if(tapedisks[lv]) {
-           fputs(first?"   (":" ", mailf);
+           fputs(first?_("   ("):_(" "), mailf);
            first = 0;
-           fprintf(mailf, "%d:%d", lv, tapedisks[lv]);
+           g_fprintf(mailf, _("%d:%d"), lv, tapedisks[lv]);
        }
        putc(')', mailf);
     }
     putc('\n', mailf);
 
-    fprintf(mailf, "Avg Tp Write Rate (k/s) ");
+    if(stats[1].tapechunks > 0) fputs(_("   (level:#chunks ...)"), mailf);
+    putc('\n', mailf);
+
+    g_fprintf(mailf,
+           _("Chunks Taped               %4d       %4d       %4d"),
+           stats[2].tapechunks, stats[0].tapechunks, stats[1].tapechunks);
+
+    if(stats[1].tapechunks > 0) {
+       first = 1;
+       for(lv = 1; lv < 10; lv++) if(tapechunks[lv]) {
+           fputs(first?_("   ("):_(" "), mailf);
+           first = 0;
+           g_fprintf(mailf, _("%d:%d"), lv, tapechunks[lv]);
+       }
+       putc(')', mailf);
+    }
+    putc('\n', mailf);
+
+    g_fprintf(mailf, _("Avg Tp Write Rate (k/s) "));
     divzero_wide(mailf, stats[2].tapesize,stats[2].taper_time);
-    fputs("    ", mailf);
+    fputs(_("    "), mailf);
     divzero_wide(mailf, stats[0].tapesize,stats[0].taper_time);
-    fputs("    ", mailf);
+    fputs(_("    "), mailf);
     divzero_wide(mailf, stats[1].tapesize,stats[1].taper_time);
     putc('\n', mailf);
 
     if(stats_by_tape) {
-       int label_length = strlen(stats_by_tape->label) + 5;
-       fprintf(mailf,"\nUSAGE BY TAPE:\n");
-       fprintf(mailf,"  %-*s  Time      Size      %%    Nb\n",
-               label_length, "Label");
+       int label_length = (int)strlen(stats_by_tape->label) + 5;
+       g_fprintf(mailf,_("\nUSAGE BY TAPE:\n"));
+       g_fprintf(mailf,_("  %-*s  Time      Size      %%    Nb    Nc\n"),
+               label_length, _("Label"));
        for(current_tape = stats_by_tape; current_tape != NULL;
            current_tape = current_tape->next) {
-           fprintf(mailf, "  %-*s", label_length, current_tape->label);
-           fprintf(mailf, " %2d:%02d", hrmn(current_tape->taper_time));
-           fprintf(mailf, " %8.0f%s  ", du(current_tape->coutsize), displayunit);
-           divzero(mailf, pct(current_tape->coutsize + 
-                              marksize * current_tape->tapedisks),
-                          tapesize);
-           fprintf(mailf, "  %4d\n", current_tape->tapedisks);
+           g_fprintf(mailf, _("  %-*s"), label_length, current_tape->label);
+           g_fprintf(mailf, _(" %2d:%02d"), hrmn(current_tape->taper_time));
+           g_fprintf(mailf, _(" %8.0lf%s  "), du(current_tape->coutsize), displayunit);
+           divzero(mailf, pct(current_tape->coutsize + marksize *
+                  (current_tape->tapedisks+current_tape->tapechunks)),
+                  (double)tapesize);
+           g_fprintf(mailf, _("  %4d"), current_tape->tapedisks);
+           g_fprintf(mailf, _("  %4d\n"), current_tape->tapechunks);
        }
     }
-
 }
 
 /* ----- */
 
-void output_tapeinfo()
+static void
+output_tapeinfo(void)
 {
-    tape_t *tp, *lasttp;
+    tape_t *tp;
     int run_tapes;
     int skip = 0;
+    int i, nb_new_tape;
 
     if (last_run_tapes > 0) {
        if(amflush_run)
-           fprintf(mailf, "The dumps were flushed to tape%s %s.\n",
-                   last_run_tapes == 1 ? "" : "s",
+           g_fprintf(mailf,
+                   plural(_("The dumps were flushed to tape %s.\n"),
+                          _("The dumps were flushed to tapes %s.\n"),
+                          last_run_tapes),
                    tape_labels ? tape_labels : "");
        else
-           fprintf(mailf, "These dumps were to tape%s %s.\n",
-                   last_run_tapes == 1 ? "" : "s",
+           g_fprintf(mailf,
+                   plural(_("These dumps were to tape %s.\n"),
+                          _("These dumps were to tapes %s.\n"),
+                          last_run_tapes),
                    tape_labels ? tape_labels : "");
     }
 
     if(degraded_mode) {
-       fprintf(mailf,
-               "*** A TAPE ERROR OCCURRED: %s.\n", tapestart_error);
-       fputs("Some dumps may have been left in the holding disk.\n", mailf);
-       fprintf(mailf,
-               "Run amflush%s to flush them to tape.\n",
-               amflush_run ? " again" : "");
+       g_fprintf(mailf,
+               _("*** A TAPE ERROR OCCURRED: %s.\n"), tapestart_error);
+    }
+    if (cmdlogfname == 1) {
+       if(degraded_mode) {
+           fputs(_("Some dumps may have been left in the holding disk.\n"),
+                 mailf);
+           g_fprintf(mailf,"\n");
+       }
+    }  else {
+       GSList *holding_list, *holding_file;
+       off_t  h_size = 0, mh_size;
+
+       holding_list = holding_get_files_for_flush(NULL);
+       for(holding_file=holding_list; holding_file != NULL;
+                                      holding_file = holding_file->next) {
+           mh_size = holding_file_size((char *)holding_file->data, 1);
+           if (mh_size > 0)
+               h_size += mh_size;
+       }
+
+       if (h_size > 0) {
+           g_fprintf(mailf,
+                   _("There are %lld%s of dumps left in the holding disk.\n"),
+                   (long long)h_size, displayunit);
+           if (getconf_boolean(CNF_AUTOFLUSH)) {
+               g_fprintf(mailf, _("They will be flushed on the next run.\n"));
+           } else {
+               g_fprintf(mailf, _("Run amflush to flush them to tape.\n"));
+           }
+           g_fprintf(mailf,"\n");
+       } else if (degraded_mode) {
+           g_fprintf(mailf, _("No dumps are left in the holding disk. %lld%s\n"), (long long)h_size, displayunit);
+           g_fprintf(mailf,"\n");
+       }
     }
 
     tp = lookup_last_reusable_tape(skip);
 
     run_tapes = getconf_int(CNF_RUNTAPES);
 
-    if (run_tapes <= 1)
-       fputs("The next tape Amanda expects to use is: ", mailf);
-    else
-       fprintf(mailf, "The next %d tapes Amanda expects to used are: ",
+    if (run_tapes == 1)
+       fputs(_("The next tape Amanda expects to use is: "), mailf);
+    else if(run_tapes > 1)
+       g_fprintf(mailf, _("The next %d tapes Amanda expects to use are: "),
                run_tapes);
-    
-    while(run_tapes > 0) {
-       if(tp != NULL)
-           fprintf(mailf, "%s", tp->label);
-       else
-           fputs("a new tape", mailf);
 
-       if(run_tapes > 1) fputs(", ", mailf);
-
-       run_tapes -= 1;
+    nb_new_tape = 0;
+    for (i=0 ; i < run_tapes ; i++) {
+       if(tp != NULL) {
+           if (nb_new_tape > 0) {
+               if (nb_new_tape == 1)
+                   g_fprintf(mailf, _("1 new tape, "));
+               else
+                   g_fprintf(mailf, _("%d new tapes, "), nb_new_tape);
+               nb_new_tape = 0;
+           }
+           g_fprintf(mailf, "%s", tp->label);
+           if (i < run_tapes-1) fputs(", ", mailf);
+       } else {
+           nb_new_tape++;
+       }
        skip++;
+
        tp = lookup_last_reusable_tape(skip);
     }
+    if (nb_new_tape > 0) {
+       if (nb_new_tape == 1)
+           g_fprintf(mailf, _("1 new tape"));
+       else
+           g_fprintf(mailf, _("%d new tapes"), nb_new_tape);
+    }
     fputs(".\n", mailf);
 
-    lasttp = lookup_tapepos(lookup_nb_tape());
     run_tapes = getconf_int(CNF_RUNTAPES);
-    if(lasttp && run_tapes > 0 && lasttp->datestamp == 0) {
-       int c = 0;
-       while(lasttp && run_tapes > 0 && lasttp->datestamp == 0) {
-           c++;
-           lasttp = lasttp->prev;
-           run_tapes--;
-       }
-       lasttp = lookup_tapepos(lookup_nb_tape());
-       if(c == 1) {
-           fprintf(mailf, "The next new tape already labelled is: %s.\n",
-                   lasttp->label);
-       }
-       else {
-           fprintf(mailf, "The next %d new tapes already labelled are: %s", c,
-                   lasttp->label);
-           lasttp = lasttp->prev;
-           c--;
-           while(lasttp && c > 0 && lasttp->datestamp == 0) {
-               fprintf(mailf, ", %s", lasttp->label);
-               lasttp = lasttp->prev;
-               c--;
-           }
-           fprintf(mailf, ".\n");
-       }
-    }
+    print_new_tapes(mailf, run_tapes);
 }
 
 /* ----- */
+static void
+output_X_summary(
+    X_summary_t *first)
+{
+    size_t len_host=0, len_disk=0;
+    X_summary_t *strange;
+    char *str = NULL;
 
-void output_lines(lp, f)
-line_t *lp;
-FILE *f;
+    for(strange=first; strange != NULL; strange = strange->next) {
+       if(strlen(strange->hostname) > len_host)
+           len_host = strlen(strange->hostname);
+       if(strlen(strange->diskname) > len_disk)
+           len_disk = strlen(strange->diskname);
+    }
+    for(strange=first; strange != NULL; strange = strange->next) {
+       str = vstralloc("  ", prefixstrange(strange->hostname, strange->diskname, strange->level, len_host, len_disk),
+                       "  ", strange->str, NULL);
+       g_fprintf(mailf, "%s\n", str);
+       amfree(str);
+    }
+}
+
+static void
+output_lines(
+    line_t *   lp,
+    FILE *     f)
 {
     line_t *next;
 
@@ -927,14 +1143,10 @@ FILE *f;
 
 /* ----- */
 
-int sort_by_time(a, b)
-disk_t *a, *b;
-{
-    return data(b)->dumper.sec - data(a)->dumper.sec;
-}
-
-int sort_by_name(a, b)
-disk_t *a, *b;
+static int
+sort_by_name(
+    disk_t *   a,
+    disk_t *   b)
 {
     int rc;
 
@@ -943,13 +1155,14 @@ disk_t *a, *b;
     return rc;
 }
 
-void sort_disks()
+static void
+sort_disks(void)
 {
     disk_t *dp;
 
     sortq.head = sortq.tail = NULL;
-    while(!empty(*diskq)) {
-       dp = dequeue_disk(diskq);
+    while(!empty(diskq)) {
+       dp = dequeue_disk(&diskq);
        if(data(dp) == NULL) { /* create one */
            find_repdata(dp, run_datestamp, 0);
        }
@@ -957,33 +1170,48 @@ void sort_disks()
     }
 }
 
-void CheckStringMax(ColumnInfo *cd, char *s) {
+static void
+CheckStringMax(
+    ColumnInfo *cd,
+    char *     s)
+{
     if (cd->MaxWidth) {
-       int l= strlen(s);
+       int l = (int)strlen(s);
+
        if (cd->Width < l)
            cd->Width= l;
     }
 }
 
-void CheckIntMax(ColumnInfo *cd, int n) {
+static void
+CheckIntMax(
+    ColumnInfo *cd,
+    int                n)
+{
     if (cd->MaxWidth) {
        char testBuf[200];
        int l;
-       ap_snprintf(testBuf, sizeof(testBuf),
+
+       g_snprintf(testBuf, SIZEOF(testBuf),
          cd->Format, cd->Width, cd->Precision, n);
-       lstrlen(testBuf);
+       l = (int)strlen(testBuf);
        if (cd->Width < l)
            cd->Width= l;
     }
 }
 
-void CheckFloatMax(ColumnInfo *cd, double d) {
+static void
+CheckFloatMax(
+    ColumnInfo *cd,
+    double     d)
+{
     if (cd->MaxWidth) {
        char testBuf[200];
        int l;
-       ap_snprintf(testBuf, sizeof(testBuf),
+
+       g_snprintf(testBuf, SIZEOF(testBuf),
          cd->Format, cd->Width, cd->Precision, d);
-       lstrlen(testBuf);
+       l = (int)strlen(testBuf);
        if (cd->Width < l)
            cd->Width= l;
     }
@@ -1000,7 +1228,9 @@ static int DumpRate;
 static int TapeTime;
 static int TapeRate;
 
-void CalcMaxWidth() {
+static void
+CalcMaxWidth(void)
+{
     /* we have to look for columspec's, that require the recalculation.
      * we do here the same loops over the sortq as is done in
      * output_summary. So, if anything is changed there, we have to
@@ -1008,65 +1238,81 @@ void CalcMaxWidth() {
      *                                                 ElB, 1999-02-24.
      */
     disk_t *dp;
-    float f;
+    double f;
     repdata_t *repdata;
+    char *qdevname;
+    int i, l;
+
+    for (i=0;ColumnData[i].Name != NULL; i++) {
+       if (ColumnData[i].MaxWidth) {
+           l = (int)strlen(ColumnData[i].Title);
+           if (ColumnData[i].Width < l)
+               ColumnData[i].Width= l;
+       }
+    }
+
     for(dp = sortq.head; dp != NULL; dp = dp->next) {
       if(dp->todo) {
        for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
-           ColumnInfo *cd;
            char TimeRateBuffer[40];
 
            CheckStringMax(&ColumnData[HostName], dp->host->hostname);
-           CheckStringMax(&ColumnData[Disk], dp->name);
+           qdevname = quote_string(dp->name);
+           CheckStringMax(&ColumnData[Disk], qdevname);
+           amfree(qdevname);
            if (repdata->dumper.result == L_BOGUS && 
                repdata->taper.result == L_BOGUS)
                continue;
            CheckIntMax(&ColumnData[Level], repdata->level);
-           if(repdata->dumper.result == L_SUCCESS) {
-               CheckFloatMax(&ColumnData[OrigKB], du(repdata->dumper.origsize));
-               CheckFloatMax(&ColumnData[OutKB], du(repdata->dumper.outsize));
-               if(dp->compress == COMP_NONE)
+            if(repdata->dumper.result == L_SUCCESS || 
+                   repdata->dumper.result == L_CHUNKSUCCESS) {
+               CheckFloatMax(&ColumnData[OrigKB],
+                             (double)du(repdata->dumper.origsize));
+               CheckFloatMax(&ColumnData[OutKB],
+                             (double)du(repdata->dumper.outsize));
+               if(abs(repdata->dumper.outsize - repdata->dumper.origsize)< 32)
                    f = 0.0;
                else 
                    f = repdata->dumper.origsize;
-               CheckStringMax(&ColumnData[Disk], 
+               CheckStringMax(&ColumnData[Compress], 
                        sDivZero(pct(repdata->dumper.outsize), f, Compress));
 
                if(!amflush_run)
-                   ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+                   g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                                "%3d:%02d", mnsc(repdata->dumper.sec));
                else
-                   ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+                   g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                                "N/A ");
                CheckStringMax(&ColumnData[DumpTime], TimeRateBuffer);
 
                CheckFloatMax(&ColumnData[DumpRate], repdata->dumper.kps); 
            }
 
-           cd= &ColumnData[TapeTime];
            if(repdata->taper.result == L_FAIL) {
-               CheckStringMax(cd, "FAILED");
+               CheckStringMax(&ColumnData[TapeTime], "FAILED");
                continue;
            }
-           if(repdata->taper.result == L_SUCCESS)
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer), 
+           if(repdata->taper.result == L_SUCCESS ||
+              repdata->taper.result == L_CHUNKSUCCESS)
+               g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer), 
                  "%3d:%02d", mnsc(repdata->taper.sec));
            else
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+               g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                  "N/A ");
-           CheckStringMax(cd, TimeRateBuffer);
+           CheckStringMax(&ColumnData[TapeTime], TimeRateBuffer);
 
-           cd= &ColumnData[TapeRate];
-           if(repdata->taper.result == L_SUCCESS)
-               CheckFloatMax(cd, repdata->taper.kps);
+           if(repdata->taper.result == L_SUCCESS ||
+                   repdata->taper.result == L_CHUNKSUCCESS)
+               CheckFloatMax(&ColumnData[TapeRate], repdata->taper.kps);
            else
-               CheckStringMax(cd, "N/A ");
+               CheckStringMax(&ColumnData[TapeRate], "N/A ");
        }
       }
     }
 }
 
-void output_summary()
+static void
+output_summary(void)
 {
     disk_t *dp;
     repdata_t *repdata;
@@ -1075,8 +1321,8 @@ void output_summary()
     char *tmp;
 
     int i, h, w1, wDump, wTape;
-    float outsize, origsize;
-    float f;
+    double outsize, origsize;
+    double f;
 
     HostName = StringToColumn("HostName");
     Disk = StringToColumn("Disk");
@@ -1099,29 +1345,29 @@ void output_summary()
     wTape= ColWidth(TapeTime, TapeRate);
 
     /* print centered top titles */
-    hstrlen(ds);
+    h = (int)strlen(ds);
     if (h > wDump) {
-       h= 0;
+       h = 0;
     } else {
-       h= (wDump-h)/2;
+       h = (wDump-h)/2;
     }
-    fprintf(mailf, "%*s", w1+h, "");
-    fprintf(mailf, "%-*s", wDump-h, ds);
-    hstrlen(ts);
+    g_fprintf(mailf, "%*s", w1+h, "");
+    g_fprintf(mailf, "%-*s", wDump-h, ds);
+    h = (int)strlen(ts);
     if (h > wTape) {
-       h= 0;
+       h = 0;
     } else {
-       h= (wTape-h)/2;
+       h = (wTape-h)/2;
     }
-    fprintf(mailf, "%*s", h, "");
-    fprintf(mailf, "%-*s", wTape-h, ts);
+    g_fprintf(mailf, "%*s", h, "");
+    g_fprintf(mailf, "%-*s", wTape-h, ts);
     fputc('\n', mailf);
 
     /* print the titles */
     for (i=0; ColumnData[i].Name != NULL; i++) {
        char *fmt;
        ColumnInfo *cd= &ColumnData[i];
-       fprintf(mailf, "%*s", cd->PrefixSpace, "");
+       g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
        if (cd->Format[1] == '-')
            fmt= "%-*s";
        else
@@ -1136,7 +1382,7 @@ void output_summary()
            cd->Title = stralloc("OUT-KB");
            cd->Title[4] = displayunit[0];
        }
-       fprintf(mailf, fmt, cd->Width, cd->Title);
+       g_fprintf(mailf, fmt, cd->Width, cd->Title);
     }
     fputc('\n', mailf);
 
@@ -1151,79 +1397,96 @@ void output_summary()
        ColumnInfo *cd;
        char TimeRateBuffer[40];
        for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
-           int devlen;
+           char *devname;
+           char *qdevname;
+           size_t devlen;
 
            cd= &ColumnData[HostName];
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->host->hostname);
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           g_fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->host->hostname);
 
            cd= &ColumnData[Disk];
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           devlen= strlen(dp->name);
-           if (devlen > cd->Width) {
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           devname = sanitize_string(dp->name);
+           qdevname = quote_string(devname);
+           devlen = strlen(qdevname);
+           if (devlen > (size_t)cd->Width) {
                fputc('-', mailf); 
-               fprintf(mailf, cd->Format, cd->Width-1, cd->Precision-1,
-                 dp->name+devlen - (cd->Width-1) );
+               g_fprintf(mailf, cd->Format, cd->Width-1, cd->Precision-1,
+                       qdevname+devlen - (cd->Width-1) );
            }
            else
-               fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->name);
-
+               g_fprintf(mailf, cd->Format, cd->Width, cd->Width, qdevname);
+           amfree(devname);
+           amfree(qdevname);
            cd= &ColumnData[Level];
            if (repdata->dumper.result == L_BOGUS &&
                repdata->taper.result  == L_BOGUS) {
              if(amflush_run){
-               fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
+               g_fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
                        tmp=TextRule(OrigKB, TapeRate, "NO FILE TO FLUSH"));
              } else {
-               fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
+               g_fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
                        tmp=TextRule(OrigKB, TapeRate, "MISSING"));
              }
              amfree(tmp);
              continue;
            }
            
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           fprintf(mailf, cd->Format, cd->Width, cd->Precision,repdata->level);
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           g_fprintf(mailf, cd->Format, cd->Width, cd->Precision,repdata->level);
 
            if (repdata->dumper.result == L_SKIPPED) {
-               fprintf(mailf, "%s\n",
+               g_fprintf(mailf, "%s\n",
                        tmp=TextRule(OrigKB, TapeRate, "SKIPPED"));
                amfree(tmp);
                continue;
            }
-           if (repdata->dumper.result == L_FAIL) {
-               fprintf(mailf, "%s\n",
+           if (repdata->dumper.result == L_FAIL && (repdata->chunker.result != L_PARTIAL && repdata->taper.result  != L_PARTIAL)) {
+               g_fprintf(mailf, "%s\n",
                        tmp=TextRule(OrigKB, TapeRate, "FAILED"));
                amfree(tmp);
+               exit_status |= STATUS_FAILED;
                continue;
            }
 
-           if(repdata->dumper.result == L_SUCCESS)
+           if(repdata->dumper.result == L_SUCCESS ||
+              repdata->dumper.result == L_CHUNKSUCCESS)
                origsize = repdata->dumper.origsize;
-           else
+           else if(repdata->taper.result == L_SUCCESS ||
+                   repdata->taper.result == L_PARTIAL)
                origsize = repdata->taper.origsize;
+           else
+               origsize = repdata->chunker.origsize;
 
-           if(repdata->taper.result == L_SUCCESS) 
+           if(repdata->taper.result == L_SUCCESS ||
+              repdata->taper.result == L_CHUNKSUCCESS)
+               outsize  = repdata->taper.outsize;
+           else if(repdata->chunker.result == L_SUCCESS ||
+                   repdata->chunker.result == L_PARTIAL ||
+                   repdata->chunker.result == L_CHUNKSUCCESS)
+               outsize  = repdata->chunker.outsize;
+           else if (repdata->taper.result == L_PARTIAL)
                outsize  = repdata->taper.outsize;
            else
                outsize  = repdata->dumper.outsize;
 
            cd= &ColumnData[OrigKB];
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(origsize != 0.0)
-               fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(origsize));
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           if(isnormal(origsize))
+               g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(origsize));
            else
-               fprintf(mailf, "%*.*s", cd->Width, cd->Width, "N/A");
+               g_fprintf(mailf, "%*.*s", cd->Width, cd->Width, "N/A");
 
            cd= &ColumnData[OutKB];
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
 
-           fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(outsize));
+           g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(outsize));
                
            cd= &ColumnData[Compress];
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
 
-           if(dp->compress == COMP_NONE)
+           if(abs(outsize - origsize) < 32)
                f = 0.0;
            else if(origsize < 1.0)
                f = 0.0;
@@ -1233,83 +1496,124 @@ void output_summary()
            fputs(sDivZero(pct(outsize), f, Compress), mailf);
 
            cd= &ColumnData[DumpTime];
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(repdata->dumper.result == L_SUCCESS)
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           if(repdata->dumper.result == L_SUCCESS ||
+              repdata->dumper.result == L_CHUNKSUCCESS)
+               g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                  "%3d:%02d", mnsc(repdata->dumper.sec));
            else
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+               g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                  "N/A ");
-           fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
+           g_fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
 
            cd= &ColumnData[DumpRate];
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(repdata->dumper.result == L_SUCCESS)
-               fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->dumper.kps);
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           if(repdata->dumper.result == L_SUCCESS ||
+                   repdata->dumper.result == L_CHUNKSUCCESS)
+               g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->dumper.kps);
            else
-               fprintf(mailf, "%*s", cd->Width, "N/A ");
+               g_fprintf(mailf, "%*s", cd->Width, "N/A ");
 
            cd= &ColumnData[TapeTime];
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
            if(repdata->taper.result == L_FAIL) {
-               fprintf(mailf, "%s\n",
+               g_fprintf(mailf, "%s\n",
                        tmp=TextRule(TapeTime, TapeRate, "FAILED "));
                amfree(tmp);
                continue;
            }
 
-           if(repdata->taper.result == L_SUCCESS)
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+           if(repdata->taper.result == L_SUCCESS || 
+              repdata->taper.result == L_PARTIAL ||
+              repdata->taper.result == L_CHUNKSUCCESS)
+               g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                  "%3d:%02d", mnsc(repdata->taper.sec));
            else
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+               g_snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                  "N/A ");
-           fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
+           g_fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
 
            cd= &ColumnData[TapeRate];
-           fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(repdata->taper.result == L_SUCCESS)
-               fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
+           g_fprintf(mailf, "%*s", cd->PrefixSpace, "");
+           if(repdata->taper.result == L_SUCCESS || 
+              repdata->taper.result == L_PARTIAL ||
+              repdata->taper.result == L_CHUNKSUCCESS)
+               g_fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
            else
-               fprintf(mailf, "%*s", cd->Width, "N/A ");
+               g_fprintf(mailf, "%*s", cd->Width, "N/A ");
+
+           if (repdata->chunker.result == L_PARTIAL)
+               g_fprintf(mailf, " PARTIAL");
+           else if(repdata->taper.result == L_PARTIAL)
+               g_fprintf(mailf, " TAPE-PARTIAL");
+
            fputc('\n', mailf);
        }
       }
     }
 }
 
-void bogus_line()
+static void
+bogus_line(
+    const char *err_text)
 {
-    printf("line %d of log is bogus\n", curlinenum);
+    char * s;
+    s = g_strdup_printf(_("line %d of log is bogus: <%s %s %s>\n"),
+                        curlinenum, 
+                        logtype_str[curlog], program_str[curprog], curstr);
+    g_printf("%s\n", s);
+    g_printf(_("  Scan failed at: <%s>\n"), err_text);
+    addline(&errsum, s);
+    amfree(s);
 }
 
 
-char *nicedate(datestamp)
-int datestamp;
 /*
- * Formats an integer of the form YYYYMMDD into the string
+ * Formats an integer of the form YYYYMMDDHHMMSS into the string
  * "Monthname DD, YYYY".  A pointer to the statically allocated string
  * is returned, so it must be copied to other storage (or just printed)
  * before calling nicedate() again.
  */
+static char *
+nicedate(
+    const char *datestamp)
 {
     static char nice[64];
-    static char *months[13] = { "BogusMonth",
-       "January", "February", "March", "April", "May", "June",
-       "July", "August", "September", "October", "November", "December"
+    char date[9];
+    int  numdate;
+    static char *months[13] = {
+               T_("BogusMonth"),
+               T_("January"),
+               T_("February"),
+               T_("March"),
+               T_("April"),
+               T_("May"),
+               T_("June"),
+               T_("July"),
+               T_("August"),
+               T_("September"),
+               T_("October"),
+               T_("November"),
+               T_("December")
     };
     int year, month, day;
 
-    year  = datestamp / 10000;
-    day   = datestamp % 100;
-    month = (datestamp / 100) % 100;
+    strncpy(date, datestamp, 8);
+    date[8] = '\0';
+    numdate = atoi(date);
+    year  = numdate / 10000;
+    day   = numdate % 100;
+    month = (numdate / 100) % 100;
+    if (month > 12 )
+       month = 0;
 
-    ap_snprintf(nice, sizeof(nice), "%s %d, %d", months[month], day, year);
+    g_snprintf(nice, SIZEOF(nice), "%s %d, %d", _(months[month]), day, year);
 
     return nice;
 }
 
-void handle_start()
+static void
+handle_start(void)
 {
     static int started = 0;
     char *label;
@@ -1322,37 +1626,31 @@ void handle_start()
        ch = *s++;
 
        skip_whitespace(s, ch);
-#define sc "datestamp"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
-           bogus_line();
+       if(ch == '\0' || strncmp_const_skip(s - 1, "datestamp", s, ch) != 0) {
+           bogus_line(s - 1);
            return;
        }
-       s += sizeof(sc)-1;
-       ch = s[-1];
-#undef sc
+
        skip_whitespace(s, ch);
        if(ch == '\0') {
-           bogus_line();
+           bogus_line(s - 1);
            return;
        }
        fp = s - 1;
        skip_non_whitespace(s, ch);
        s[-1] = '\0';
        run_datestamp = newstralloc(run_datestamp, fp);
-       s[-1] = ch;
+       s[-1] = (char)ch;
 
        skip_whitespace(s, ch);
-#define sc "label"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
-           bogus_line();
+       if(ch == '\0' || strncmp_const_skip(s - 1, "label", s, ch) != 0) {
+           bogus_line(s - 1);
            return;
        }
-       s += sizeof(sc)-1;
-       ch = s[-1];
-#undef sc
+
        skip_whitespace(s, ch);
        if(ch == '\0') {
-           bogus_line();
+           bogus_line(s - 1);
            return;
        }
        fp = s - 1;
@@ -1360,7 +1658,7 @@ void handle_start()
        s[-1] = '\0';
 
        label = stralloc(fp);
-       s[-1] = ch;
+       s[-1] = (char)ch;
 
        if(tape_labels) {
            fp = vstralloc(tape_labels, ", ", label, NULL);
@@ -1373,10 +1671,10 @@ void handle_start()
        last_run_tapes++;
 
        if(stats_by_tape == NULL) {
-           stats_by_tape = current_tape = (taper_t *)alloc(sizeof(taper_t));
+           stats_by_tape = current_tape = (taper_t *)alloc(SIZEOF(taper_t));
        }
        else {
-           current_tape->next = (taper_t *)alloc(sizeof(taper_t));
+           current_tape->next = (taper_t *)alloc(SIZEOF(taper_t));
            current_tape = current_tape->next;
        }
        current_tape->label = label;
@@ -1384,6 +1682,7 @@ void handle_start()
        current_tape->coutsize = 0.0;
        current_tape->corigsize = 0.0;
        current_tape->tapedisks = 0;
+       current_tape->tapechunks = 0;
        current_tape->next = NULL;
        tapefcount = 0;
 
@@ -1405,80 +1704,70 @@ void handle_start()
        ch = *s++;
 
        skip_whitespace(s, ch);
-#define sc "date"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
+       if(ch == '\0' || strncmp_const_skip(s - 1, "date", s, ch) != 0) {
            return;                             /* ignore bogus line */
        }
-       s += sizeof(sc)-1;
-       ch = s[-1];
-#undef sc
+
        skip_whitespace(s, ch);
        if(ch == '\0') {
-           bogus_line();
+           bogus_line(s - 1);
            return;
        }
        fp = s - 1;
        skip_non_whitespace(s, ch);
        s[-1] = '\0';
        run_datestamp = newstralloc(run_datestamp, fp);
-       s[-1] = ch;
+       s[-1] = (char)ch;
 
        started = 1;
     }
     if(amflush_run && normal_run) {
        amflush_run = 0;
        addline(&notes,
-     "  reporter: both amflush and planner output in log, ignoring amflush.");
+     _("  reporter: both amflush and planner output in log, ignoring amflush."));
     }
 }
 
 
-void handle_finish()
+static void
+handle_finish(void)
 {
     char *s;
     int ch;
-    float a_time;
+    double a_time;
 
     if(curprog == P_DRIVER || curprog == P_AMFLUSH || curprog == P_PLANNER) {
        s = curstr;
        ch = *s++;
 
        skip_whitespace(s, ch);
-#define sc "date"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
-           bogus_line();
+       if(ch == '\0' || strncmp_const_skip(s - 1, "date", s, ch) != 0) {
+           bogus_line(s - 1);
            return;
        }
-       s += sizeof(sc)-1;
-       ch = s[-1];
-#undef sc
 
        skip_whitespace(s, ch);
        if(ch == '\0') {
-           bogus_line();
+           bogus_line(s - 1);
            return;
        }
        skip_non_whitespace(s, ch);     /* ignore the date string */
 
        skip_whitespace(s, ch);
-#define sc "time"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
+       if(ch == '\0' || strncmp_const_skip(s - 1, "time", s, ch) != 0) {
            /* older planner doesn't write time */
            if(curprog == P_PLANNER) return;
-           bogus_line();
+           bogus_line(s - 1);
            return;
        }
-       s += sizeof(sc)-1;
-       ch = s[-1];
-#undef sc
 
        skip_whitespace(s, ch);
        if(ch == '\0') {
-           bogus_line();
+           bogus_line(s - 1);
            return;
        }
-       if(sscanf(s - 1, "%f", &a_time) != 1) {
-           bogus_line();
+       if(sscanf(s - 1, "%lf", &a_time) != 1) {
+           bogus_line(s - 1);
            return;
        }
        if(curprog == P_PLANNER) {
@@ -1491,44 +1780,144 @@ void handle_finish()
     }
 }
 
-void handle_stats()
+static void
+handle_stats(void)
 {
-    char *s;
+    char *s, *fp;
     int ch;
+    char *hostname, *diskname, *datestamp, *qdiskname;
+    int level = 0;
+    double sec, kps, nbytes, cbytes;
+    repdata_t *repdata;
+    disk_t *dp;
 
     if(curprog == P_DRIVER) {
        s = curstr;
        ch = *s++;
 
        skip_whitespace(s, ch);
-#define sc "startup time"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
-           bogus_line();
-           return;
+       if(ch != '\0' && strncmp_const_skip(s - 1, "startup time", s, ch) == 0) {
+           skip_whitespace(s, ch);
+           if(ch == '\0') {
+               bogus_line(s - 1);
+               return;
+           }
+           if(sscanf(s - 1, "%lf", &startup_time) != 1) {
+               bogus_line(s - 1);
+               return;
+           }
+           planner_time = startup_time;
        }
-       s += sizeof(sc)-1;
-       ch = s[-1];
-#undef sc
+       else if(ch != '\0' && strncmp_const_skip(s - 1, "hostname", s, ch) == 0) {
+           skip_whitespace(s, ch);
+           if(ch == '\0') {
+               bogus_line(s - 1);
+               return;
+           }
+           ghostname = stralloc(s-1);
+       }
+       else if(ch != '\0' && strncmp_const_skip(s - 1, "estimate", s, ch) == 0) {
+           skip_whitespace(s, ch);
+           if(ch == '\0') {
+               bogus_line(s - 1);
+               return;
+           }
+           fp = s - 1;
+           skip_non_whitespace(s, ch);
+           s[-1] = '\0';
+           hostname = stralloc(fp);
+           s[-1] = (char)ch;
+
+           skip_whitespace(s, ch);
+           if(ch == '\0') {
+               bogus_line(s - 1);
+               amfree(hostname);
+               return;
+           }
 
-       skip_whitespace(s, ch);
-       if(ch == '\0') {
-           bogus_line();
-           return;
+           qdiskname = s - 1;
+           skip_quoted_string(s, ch);
+           s[-1] = '\0';
+           diskname = unquote_string(qdiskname);
+           s[-1] = (char)ch;
+
+           skip_whitespace(s, ch);
+           if(ch == '\0') {
+               bogus_line(s - 1);
+               amfree(hostname);
+               amfree(diskname);
+               return;
+           }
+           fp = s - 1;
+           skip_non_whitespace(s, ch);
+           s[-1] = '\0';
+           datestamp = stralloc(fp);
+           s[-1] = (char)ch;
+           skip_whitespace(s, ch);
+
+           if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
+               bogus_line(s - 1);
+               amfree(hostname);
+               amfree(diskname);
+               amfree(datestamp);
+               return;
+           }
+           skip_integer(s, ch);
+           if(level < 0 || level > 9) {
+               amfree(hostname);
+               amfree(diskname);
+               amfree(datestamp);
+               return;
+           }
+
+           skip_whitespace(s, ch);
+
+           if(sscanf(s - 1,"[sec %lf nkb %lf ckb %lf kps %lf",
+                     &sec, &nbytes, &cbytes, &kps) != 4)  {
+               bogus_line(s - 1);
+               amfree(hostname);
+               amfree(diskname);
+               amfree(datestamp);
+               return;
+           }
+
+           dp = lookup_disk(hostname, diskname);
+           if(dp == NULL) {
+               addtoX_summary(&first_failed, &last_failed,
+                              hostname, diskname, level,
+                              _("ERROR [not in disklist]"));
+               exit_status |= STATUS_FAILED;
+               amfree(hostname);
+               amfree(diskname);
+               amfree(datestamp);
+               return;
+           }
+
+           repdata = find_repdata(dp, datestamp, level);
+
+           repdata->est_nsize = nbytes;
+           repdata->est_csize = cbytes;
+
+           amfree(hostname);
+           amfree(diskname);
+           amfree(datestamp);
        }
-       if(sscanf(s - 1, "%f", &startup_time) != 1) {
-           bogus_line();
+       else {
+           bogus_line(s - 1);
            return;
        }
-       planner_time = startup_time;
+#undef sc
+
     }
 }
 
 
-void handle_note()
+static void
+handle_note(void)
 {
     char *str = NULL;
 
-    str = vstralloc("  ", program_str[curprog], ": ", curstr, NULL);
+    str = vstrallocf("  %s: %s", program_str[curprog], curstr);
     addline(&notes, str);
     amfree(str);
 }
@@ -1536,7 +1925,8 @@ void handle_note()
 
 /* ----- */
 
-void handle_error()
+static void
+handle_error(void)
 {
     char *s = NULL, *nl;
     int ch;
@@ -1546,57 +1936,54 @@ void handle_error()
        ch = *s++;
 
        skip_whitespace(s, ch);
-#define sc "no-tape"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
-           bogus_line();
-           return;
-       }
-       s += sizeof(sc)-1;
-       ch = s[-1];
-#undef sc
-
-       skip_whitespace(s, ch);
-       if(ch != '\0') {
-           if((nl = strchr(s - 1, '\n')) != NULL) {
-               *nl = '\0';
+       if(ch != '\0' && strncmp_const_skip(s - 1, "no-tape", s, ch) == 0) {
+           skip_whitespace(s, ch);
+           if(ch != '\0') {
+               if((nl = strchr(s - 1, '\n')) != NULL) {
+                   *nl = '\0';
+               }
+               tapestart_error = newstralloc(tapestart_error, s - 1);
+               if(nl) *nl = '\n';
+               degraded_mode = 1;
+               exit_status |= STATUS_TAPE;;
+               return;
            }
-           tapestart_error = newstralloc(tapestart_error, s - 1);
-           if(nl) *nl = '\n';
-           degraded_mode = 1;
-           return;
+           /* else some other tape error, handle like other errors */
        }
        /* else some other tape error, handle like other errors */
     }
-    s = vstralloc("  ", program_str[curprog], ": ",
-                 logtype_str[curlog], " ", curstr, NULL);
+    s = vstrallocf("  %s: %s %s", program_str[curprog],
+                 logtype_str[curlog], curstr);
     addline(&errsum, s);
     amfree(s);
 }
 
 /* ----- */
 
-void handle_summary()
+static void
+handle_summary(void)
 {
-    bogus_line();
+    bogus_line(curstr);
 }
 
 /* ----- */
 
-int nb_disk=0;
-void handle_disk()
+static int nb_disk=0;
+static void
+handle_disk(void)
 {
     disk_t *dp;
-    char *s, *fp;
+    char *s, *fp, *qdiskname;
     int ch;
     char *hostname = NULL, *diskname = NULL;
 
     if(curprog != P_PLANNER && curprog != P_AMFLUSH) {
-       bogus_line();
+       bogus_line(curstr);
        return;
     }
 
     if(nb_disk==0) {
-       for(dp = diskq->head; dp != NULL; dp = dp->next)
+       for(dp = diskq.head; dp != NULL; dp = dp->next)
            dp->todo = 0;
     }
     nb_disk++;
@@ -1606,29 +1993,30 @@ void handle_disk()
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
        return;
     }
     fp = s - 1;
     skip_non_whitespace(s, ch);
     s[-1] = '\0';
     hostname = newstralloc(hostname, fp);
-    s[-1] = ch;
+    s[-1] = (char)ch;
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
+       amfree(hostname);
        return;
     }
-    fp = s - 1;
-    skip_non_whitespace(s, ch);
+    qdiskname = s - 1;
+    skip_quoted_string(s, ch);
     s[-1] = '\0';
-    diskname = newstralloc(diskname, fp);
-    s[-1] = ch;
+    diskname = unquote_string(qdiskname);
+    s[-1] = (char)ch;
 
     dp = lookup_disk(hostname, diskname);
     if(dp == NULL) {
-       dp = add_disk(hostname, diskname);
+       dp = add_disk(&diskq, hostname, diskname);
     }
 
     amfree(hostname);
@@ -1636,10 +2024,16 @@ void handle_disk()
     dp->todo = 1;
 }
 
-repdata_t *handle_success()
+/* XXX Just a placeholder, in case we decide to do something with L_CHUNK
+ * log entries.  Right now they're just the equivalent of L_SUCCESS, but only
+ * for a split chunk of the overall dumpfile.
+ */
+static repdata_t *
+handle_chunk(
+    logtype_t logtype)
 {
     disk_t *dp;
-    float sec, kps, kbytes, origkb;
+    double sec, kps, kbytes;
     timedata_t *sp;
     int i;
     char *s, *fp;
@@ -1647,11 +2041,195 @@ repdata_t *handle_success()
     char *hostname = NULL;
     char *diskname = NULL;
     repdata_t *repdata;
-    int level;
+    int level, chunk;
+    char *datestamp;
+    char *label = NULL;
+    int fileno;
+    int totpart;
+    
+    if(curprog != P_TAPER) {
+       bogus_line(curstr);
+       return NULL;
+    }
+    
+    s = curstr;
+    ch = *s++;
+
+    skip_whitespace(s, ch);
+    if(ch == '\0') {
+       bogus_line(s - 1);
+       return NULL;
+    }
+
+    if (logtype == L_PART || logtype == L_PARTPARTIAL) {
+       fp = s - 1;
+       skip_non_whitespace(s, ch);
+       s[-1] = '\0';
+       label = stralloc(fp);
+       s[-1] = (char)ch;
+    
+       skip_whitespace(s, ch);
+       if(ch == '\0' || sscanf(s - 1, "%d", &fileno) != 1) {
+           bogus_line(s - 1);
+           amfree(label);
+           return NULL;
+       }
+       skip_integer(s, ch);
+       skip_whitespace(s, ch);
+       if(ch == '\0') {
+           bogus_line(s - 1);
+           amfree(label);
+           return NULL;
+       }
+       amfree(label);
+    }
+
+    fp = s - 1;
+    skip_non_whitespace(s, ch);
+    s[-1] = '\0';
+    hostname = stralloc(fp);
+    s[-1] = (char)ch;
+    
+    skip_whitespace(s, ch);
+    if(ch == '\0') {
+       bogus_line(s - 1);
+       amfree(hostname);
+       return NULL;
+    }
+    fp = s - 1;
+    skip_quoted_string(s, ch);
+    s[-1] = '\0';
+    diskname = unquote_string(fp);
+    s[-1] = (char)ch;
+    
+    skip_whitespace(s, ch);
+    if(ch == '\0') {
+       bogus_line(s - 1);
+       amfree(hostname);
+       amfree(diskname);
+       return NULL;
+    }
+    fp = s - 1;
+    skip_non_whitespace(s, ch);
+    s[-1] = '\0';
+    datestamp = stralloc(fp);
+    s[-1] = (char)ch;
+    skip_whitespace(s, ch);
+    if(ch == '\0' || sscanf(s - 1, "%d", &chunk) != 1) {
+       bogus_line(s - 1);
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+    skip_integer(s, ch);
+
+    if (ch != '\0' && s[-1] == '/') {
+       s++; ch = s[-1];
+       if (sscanf(s - 1, "%d", &totpart) != 1) {
+           bogus_line(s - 1);
+           amfree(hostname);
+           amfree(diskname);
+           amfree(datestamp);
+           return NULL;
+       }
+       skip_integer(s, ch);
+    }
+
+    skip_whitespace(s, ch);
+    if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
+       bogus_line(s - 1);
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+    skip_integer(s, ch);
+    
+    /*@ignore@*/
+    if(level < 0 || level > 9) {
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+    /*@end@*/
+    skip_whitespace(s, ch);
+    if(sscanf(s - 1,"[sec %lf kb %lf kps %lf", &sec, &kbytes, &kps) != 3)  {
+       bogus_line(s - 1);
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+    
+    
+    dp = lookup_disk(hostname, diskname);
+    if(dp == NULL) {
+       char *str = NULL;
+       
+       str = vstrallocf(_("  %s ERROR [not in disklist]"),
+                       prefix(hostname, diskname, level));
+       addline(&errsum, str);
+       amfree(str);
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+    
+    repdata = find_repdata(dp, datestamp, level);
+    
+    sp = &(repdata->taper);
+    
+    i = level > 0;
+    
+    amfree(hostname);
+    amfree(diskname);
+    amfree(datestamp);
+    
+    if(current_tape == NULL) {
+       error("current_tape == NULL");
+    }
+    if (sp->filenum == 0) {
+       sp->filenum = ++tapefcount;
+       sp->tapelabel = current_tape->label;
+    }
+    tapechunks[level] +=1;
+    stats[i].tapechunks +=1;
+    current_tape->taper_time += sec;
+    current_tape->coutsize += kbytes;
+    current_tape->tapechunks += 1;
+    return repdata;
+}
+
+static repdata_t *
+handle_success(
+    logtype_t  logtype)
+{
+    disk_t *dp;
+    double sec = 0.0;
+    double kps = 0.0;
+    double kbytes = 0.0;
+    double origkb = 0.0;
+    timedata_t *sp;
+    int i;
+    char *s, *fp, *qdiskname;
+    int ch;
+    char *hostname = NULL;
+    char *diskname = NULL;
+    repdata_t *repdata;
+    int level = 0;
+    int totpart = 0;
     char *datestamp;
 
-    if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER) {
-       bogus_line();
+    (void)logtype;
+
+    if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER &&
+       curprog != P_CHUNKER) {
+       bogus_line(curstr);
        return NULL;
     }
 
@@ -1660,30 +2238,29 @@ repdata_t *handle_success()
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
        return NULL;
     }
     fp = s - 1;
     skip_non_whitespace(s, ch);
     s[-1] = '\0';
     hostname = stralloc(fp);
-    s[-1] = ch;
+    s[-1] = (char)ch;
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
        amfree(hostname);
        return NULL;
     }
-    fp = s - 1;
-    skip_non_whitespace(s, ch);
+    qdiskname = s - 1;
+    skip_quoted_string(s, ch);
     s[-1] = '\0';
-    diskname = stralloc(fp);
-    s[-1] = ch;
+    diskname = unquote_string(qdiskname);
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
        amfree(hostname);
        amfree(diskname);
        return NULL;
@@ -1692,16 +2269,17 @@ repdata_t *handle_success()
     skip_non_whitespace(s, ch);
     s[-1] = '\0';
     datestamp = stralloc(fp);
-    s[-1] = ch;
+    s[-1] = (char)ch;
 
-    level = atoi(datestamp);
-    if(level < 100)  {
+    //datestamp is optional
+    if(strlen(datestamp) < 6) {
+       totpart = atoi(datestamp);
        datestamp = newstralloc(datestamp, run_datestamp);
     }
     else {
        skip_whitespace(s, ch);
-       if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
-           bogus_line();
+       if(ch == '\0' || sscanf(s - 1, "%d", &totpart) != 1) {
+           bogus_line(s - 1);
            amfree(hostname);
            amfree(diskname);
            amfree(datestamp);
@@ -1711,16 +2289,45 @@ repdata_t *handle_success()
     }
 
     skip_whitespace(s, ch);
+
+    //totpart is optional
+    if (*(s-1) == '"')
+       s++;
+    if (*(s-1) == '[') {
+       level = totpart;
+       totpart = -1;
+    } else {
+       if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
+           bogus_line(s - 1);
+           amfree(hostname);
+           amfree(diskname);
+           amfree(datestamp);
+           return NULL;
+       }
+       skip_integer(s, ch);
+       skip_whitespace(s, ch);
+    }
+
+
+    if(level < 0 || level > 9) {
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+
                                /* Planner success messages (for skipped
                                   dumps) do not contain statistics */
     if(curprog != P_PLANNER) {
-       if(curprog != P_DUMPER ||
-          sscanf(s - 1,"[sec %f kb %f kps %f orig-kb %f", 
-                 &sec, &kbytes, &kps, &origkb) != 4)  {
+       if(*(s - 1) == '"')
+           s++;
+       if((curprog != P_DUMPER)
+           || (sscanf(s - 1,"[sec %lf kb %lf kps %lf orig-kb %lf", 
+                 &sec, &kbytes, &kps, &origkb) != 4))  {
            origkb = -1;
-           if(sscanf(s - 1,"[sec %f kb %f kps %f",
+           if(sscanf(s - 1,"[sec %lf kb %lf kps %lf",
                      &sec, &kbytes, &kps) != 3) {
-               bogus_line();
+               bogus_line(s - 1);
                amfree(hostname);
                amfree(diskname);
                amfree(datestamp);
@@ -1728,20 +2335,17 @@ repdata_t *handle_success()
            }
        }
        else {
-           if(origkb == 0.0) origkb = 0.1;
+           if(!isnormal(origkb))
+               origkb = 0.1;
        }
     }
 
 
     dp = lookup_disk(hostname, diskname);
     if(dp == NULL) {
-       char *str = NULL;
-
-       str = vstralloc("  ", prefix(hostname, diskname, level),
-                       " ", "ERROR [not in disklist]",
-                       NULL);
-       addline(&errsum, str);
-       amfree(str);
+       addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
+                      _("ERROR [not in disklist]"));
+       exit_status |= STATUS_FAILED;
        amfree(hostname);
        amfree(diskname);
        amfree(datestamp);
@@ -1760,19 +2364,30 @@ repdata_t *handle_success()
 
     if(curprog == P_TAPER)
        sp = &(repdata->taper);
-    else sp = &(repdata->dumper);
+    else if(curprog == P_DUMPER)
+       sp = &(repdata->dumper);
+    else sp = &(repdata->chunker);
 
     i = level > 0;
 
-    if(origkb == -1) {
+    if (origkb < 0.0 && (curprog == P_CHUNKER || curprog == P_TAPER) &&
+       isnormal(repdata->dumper.outsize)) {
+       /* take origkb from DUMPER line */
+       origkb = repdata->dumper.outsize;
+    } else if (origkb < 0.0) {
+       /* take origkb from infofile, needed for amflush */
        info_t inf;
        struct tm *tm;
        int Idatestamp;
 
        get_info(hostname, diskname, &inf);
         tm = localtime(&inf.inf[level].date);
-        Idatestamp = 10000*(tm->tm_year+1900) +
-                      100*(tm->tm_mon+1) + tm->tm_mday;
+       if (tm) {
+            Idatestamp = 10000*(tm->tm_year+1900) +
+                        100*(tm->tm_mon+1) + tm->tm_mday;
+       } else {
+           Idatestamp = 19000101;
+       }
 
        if(atoi(datestamp) == Idatestamp) {
            /* grab original size from record */
@@ -1781,6 +2396,13 @@ repdata_t *handle_success()
        else
            origkb = 0.0;
     }
+
+    if (curprog == P_DUMPER &&
+       (sp->result == L_FAIL || sp->result == L_PARTIAL)) {
+       addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
+                      _("was successfully retried"));
+    }
+
     amfree(hostname);
     amfree(diskname);
     amfree(datestamp);
@@ -1794,82 +2416,125 @@ repdata_t *handle_success()
 
     if(curprog == P_TAPER) {
        if(current_tape == NULL) {
-           error("current_tape == NULL");
+           error(_("current_tape == NULL"));
+           /*NOTREACHED*/
        }
        stats[i].taper_time += sec;
        sp->filenum = ++tapefcount;
        sp->tapelabel = current_tape->label;
+       sp->totpart = totpart;
        tapedisks[level] +=1;
        stats[i].tapedisks +=1;
        stats[i].tapesize += kbytes;
-       current_tape->taper_time += sec;
-       current_tape->coutsize += kbytes;
-       current_tape->corigsize += origkb;
+       sp->outsize = kbytes;
+       if(!isnormal(repdata->chunker.outsize) && isnormal(repdata->dumper.outsize)) { /* dump to tape */
+           stats[i].outsize += kbytes;
+           if (abs(kbytes - origkb) >= 32) {
+               /* server compressed */
+               stats[i].corigsize += origkb;
+               stats[i].coutsize += kbytes;
+           }
+       }
        current_tape->tapedisks += 1;
     }
 
     if(curprog == P_DUMPER) {
        stats[i].dumper_time += sec;
-       if(dp->compress == COMP_NONE) {
+       if (abs(kbytes - origkb) < 32) {
+           /* not client compressed */
            sp->origsize = kbytes;
        }
        else {
-           stats[i].coutsize += kbytes;
+           /* client compressed */
            stats[i].corigsize += sp->origsize;
+           stats[i].coutsize += kbytes;
        }
        dumpdisks[level] +=1;
        stats[i].dumpdisks +=1;
        stats[i].origsize += sp->origsize;
-       stats[i].outsize += kbytes;
     }
 
+    if(curprog == P_CHUNKER) {
+       sp->outsize = kbytes;
+       stats[i].outsize += kbytes;
+       if (abs(kbytes - origkb) >= 32) {
+           /* server compressed */
+           stats[i].corigsize += origkb;
+           stats[i].coutsize += kbytes;
+       }
+    }
     return repdata;
 }
 
-void handle_strange()
+static void
+handle_partial(void)
+{
+    repdata_t *repdata;
+    timedata_t *sp;
+
+    repdata = handle_success(L_PARTIAL);
+    if (!repdata)
+       return;
+
+    if(curprog == P_TAPER)
+       sp = &(repdata->taper);
+    else if(curprog == P_DUMPER)
+       sp = &(repdata->dumper);
+    else sp = &(repdata->chunker);
+
+    sp->result = L_PARTIAL;
+}
+
+static void
+handle_strange(void)
 {
     char *str = NULL;
     char *strangestr = NULL;
     repdata_t *repdata;
+    char *qdisk;
+
+    repdata = handle_success(L_SUCCESS);
+    if (!repdata)
+       return;
 
-    repdata = handle_success();
+    qdisk = quote_string(repdata->disk->name);
 
-    addline(&errdet,"");
-    str = vstralloc("/-- ", prefix(repdata->disk->host->hostname, 
-                                  repdata->disk->name, repdata->level),
-                   " ", "STRANGE",
-                   NULL);
-    addline(&errdet, str);
+    addline(&strangedet,"");
+    str = vstrallocf("/-- %s STRANGE",
+               prefix(repdata->disk->host->hostname, qdisk, repdata->level));
+    addline(&strangedet, str);
     amfree(str);
 
     while(contline_next()) {
+       char *s, ch;
        get_logline(logfile);
-#define sc "sendbackup: warning "
-       if(strncmp(curstr, sc, sizeof(sc)-1) == 0) {
-           strangestr = newstralloc(strangestr, curstr+sizeof(sc)-1);
+       s = curstr;
+       if(strncmp_const_skip(curstr, "sendbackup: warning ", s, ch) == 0) {
+           strangestr = newstralloc(strangestr, s);
        }
-       addline(&errdet, curstr);
+       addline(&strangedet, curstr);
     }
-    addline(&errdet,"\\--------");
+    addline(&strangedet,"\\--------");
 
-    str = vstralloc("  ", prefix(repdata->disk->host->hostname, 
-                                repdata->disk->name, repdata->level),
-                   " ", "STRANGE", " ", strangestr,
-                   NULL);
-    addline(&errsum, str);
+    str = vstrallocf("STRANGE %s", strangestr? strangestr : _("(see below)"));
+    addtoX_summary(&first_strange, &last_strange,
+                  repdata->disk->host->hostname, qdisk, repdata->level, str);
+    exit_status |= STATUS_STRANGE;
+    amfree(qdisk);
     amfree(str);
     amfree(strangestr);
 }
 
-void handle_failed()
+static void
+handle_failed(void)
 {
     disk_t *dp;
     char *hostname;
     char *diskname;
     char *datestamp;
     char *errstr;
-    int level;
-    char *s, *fp;
+    int level = 0;
+    char *s, *fp, *qdiskname;
     int ch;
     char *str = NULL;
     repdata_t *repdata;
@@ -1883,7 +2548,7 @@ void handle_failed()
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
        return;
     }
     hostname = s - 1;
@@ -1892,16 +2557,18 @@ void handle_failed()
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
        return;
     }
-    diskname = s - 1;
-    skip_non_whitespace(s, ch);
+    qdiskname = s - 1;
+    skip_quoted_string(s, ch);
     s[-1] = '\0';
+    diskname = unquote_string(qdiskname);
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
+       amfree(diskname);
        return;
     }
     fp = s - 1;
@@ -1916,8 +2583,9 @@ void handle_failed()
     else { /* read the level */
        skip_whitespace(s, ch);
        if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
-           bogus_line();
+           bogus_line(s - 1);
            amfree(datestamp);
+           amfree(diskname);
            return;
        }
        skip_integer(s, ch);
@@ -1925,8 +2593,9 @@ void handle_failed()
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
        amfree(datestamp);
+       amfree(diskname);
        return;
     }
     errstr = s - 1;
@@ -1935,12 +2604,10 @@ void handle_failed()
     }
 
     dp = lookup_disk(hostname, diskname);
+    amfree(diskname);
     if(dp == NULL) {
-       str = vstralloc("  ", prefix(hostname, diskname, level),
-                       " ", "ERROR [not in disklist]",
-                       NULL);
-       addline(&errsum, str);
-       amfree(str);
+       addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
+                      _("ERROR [not in disklist]"));
     } else {
        repdata = find_repdata(dp, datestamp, level);
 
@@ -1953,19 +2620,16 @@ void handle_failed()
     }
     amfree(datestamp);
 
-    str = vstralloc("  ", prefix(hostname, diskname, level),
-                   " ", "FAILED",
-                   " ", errstr,
-                   NULL);
-    addline(&errsum, str);
+    str = vstrallocf(_("FAILED %s"), errstr);
+    addtoX_summary(&first_failed, &last_failed, hostname, qdiskname, level,
+                  str);
     amfree(str);
 
     if(curprog == P_DUMPER) {
        addline(&errdet,"");
-       str = vstralloc("/-- ", prefix(hostname, diskname, level),
-                       " ", "FAILED",
-                       " ", errstr,
-                       NULL);
+       str = vstrallocf("/-- %s FAILED %s",
+                       prefix(hostname, qdiskname, level), 
+                       errstr);
        addline(&errdet, str);
        amfree(str);
        while(contline_next()) {
@@ -1973,92 +2637,209 @@ void handle_failed()
            addline(&errdet, curstr);
        }
        addline(&errdet,"\\--------");
+       exit_status |= STATUS_FAILED;
     }
     return;
 }
 
-void generate_missing()
+
+static void
+generate_missing(void)
 {
     disk_t *dp;
-    char *str = NULL;
+    char *qdisk;
 
-    for(dp = diskq->head; dp != NULL; dp = dp->next) {
+    for(dp = diskq.head; dp != NULL; dp = dp->next) {
        if(dp->todo && data(dp) == NULL) {
-           str = vstralloc("  ", prefix(dp->host->hostname, dp->name, -987),
-                           " ", "RESULTS MISSING",
-                           NULL);
-           addline(&errsum, str);
-           amfree(str);
+           qdisk = quote_string(dp->name);
+           addtoX_summary(&first_failed, &last_failed, dp->host->hostname,
+                          qdisk, -987, _("RESULTS MISSING"));
+           exit_status |= STATUS_MISSING;
+           amfree(qdisk);
+       }
+    }
+}
+
+static void
+generate_bad_estimate(void)
+{
+    disk_t *dp;
+    repdata_t *repdata;
+    char s[1000];
+    double outsize;
+
+    for(dp = diskq.head; dp != NULL; dp = dp->next) {
+       if(dp->todo) {
+           for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
+               if(repdata->est_csize >= 0.1) {
+                   if(repdata->taper.result == L_SUCCESS ||
+                      repdata->taper.result == L_CHUNKSUCCESS)
+                       outsize  = repdata->taper.outsize;
+                   else if(repdata->chunker.result == L_SUCCESS ||
+                           repdata->chunker.result == L_PARTIAL ||
+                           repdata->chunker.result == L_CHUNKSUCCESS)
+                       outsize  = repdata->chunker.outsize;
+                   else if(repdata->taper.result == L_PARTIAL)
+                       outsize  = repdata->taper.outsize;
+                   else
+                       outsize  = repdata->dumper.outsize;
+
+                   if(repdata->est_csize * 0.9 > outsize) {
+                       g_snprintf(s, 1000,
+                               _("  big estimate: %s %s %d"),
+                                repdata->disk->host->hostname,
+                                repdata->disk->name,
+                                repdata->level);
+                       s[999] = '\0';
+                       addline(&notes, s);
+                       g_snprintf(s, 1000,
+                                _("                est: %.0lf%s    out %.0lf%s"),
+                                du(repdata->est_csize), displayunit,
+                                du(outsize), displayunit);
+                       s[999] = '\0';
+                       addline(&notes, s);
+                   }
+                   else if(repdata->est_csize * 1.1 < outsize) {
+                       g_snprintf(s, 1000,
+                               _("  small estimate: %s %s %d"),
+                                repdata->disk->host->hostname,
+                                repdata->disk->name,
+                                repdata->level);
+                       s[999] = '\0';
+                       addline(&notes, s);
+                       g_snprintf(s, 1000,
+                                _("                  est: %.0lf%s    out %.0lf%s"),
+                                du(repdata->est_csize), displayunit,
+                                du(outsize), displayunit);
+                       s[999] = '\0';
+                       addline(&notes, s);
+                   }
+               }
+           }
        }
     }
 }
 
 static char *
-prefix (host, disk, level)
-    char *host;
-    char *disk;
-    int level;
+prefix (
+    char *     host,
+    char *     disk,
+    int                level)
+{
+    static char *str = NULL;
+
+    if (level == -987) {
+       str = newvstrallocf(str, " %s %s",
+                       host ? host : _("(host?)"),
+                       disk ? disk : _("(disk?)"));
+    } else {
+       str = newvstrallocf(str, " %s %s lev %d",
+                       host ? host : _("(host?)"),
+                       disk ? disk : _("(disk?)"),
+                       level);
+    }
+    return str;
+}
+
+
+static char *
+prefixstrange (
+    char *     host,
+    char *     disk,
+    int                level,
+    size_t     len_host,
+    size_t     len_disk)
 {
-    char h[10+1];
-    int l;
-    char number[NUM_STR_SIZE];
+    char *h, *d;
+    size_t l;
     static char *str = NULL;
 
-    ap_snprintf(number, sizeof(number), "%d", level);
+    h=alloc(len_host+1);
     if(host) {
-       strncpy(h, host, sizeof(h)-1);
+       strncpy(h, host, len_host);
     } else {
-       strncpy(h, "(host?)", sizeof(h)-1);
+       strncpy(h, _("(host?)"), len_host);
     }
-    h[sizeof(h)-1] = '\0';
-    for(l = strlen(h); l < sizeof(h)-1; l++) {
+    h[len_host] = '\0';
+    for(l = strlen(h); l < len_host; l++) {
        h[l] = ' ';
     }
-    str = newvstralloc(str,
-                      h,
-                      " ", disk ? disk : "(disk?)",
-                      level != -987 ? " lev " : "",
-                      level != -987 ? number : "",
-                      NULL);
+    d=alloc(len_disk+1);
+    if(disk) {
+       strncpy(d, disk, len_disk);
+    } else {
+       strncpy(d, _("(disk?)"), len_disk);
+    }
+    d[len_disk] = '\0';
+    for(l = strlen(d); l < len_disk; l++) {
+       d[l] = ' ';
+    }
+    if (level == -987) {
+       str = newvstrallocf(str, " %s %s", h, d);
+    } else {
+       str = newvstrallocf(str, " %s %s lev %d", h, d, level);
+    }
+    amfree(h);
+    amfree(d);
     return str;
 }
 
-void copy_template_file(lbl_templ)
-char *lbl_templ;
+
+static void
+addtoX_summary (
+    X_summary_t **first,
+    X_summary_t **last,
+    char        *host,
+    char        *disk,
+    int                  level,
+    char        *str)
+{
+    X_summary_t *X_summary;
+
+    X_summary = alloc(SIZEOF(X_summary_t));
+    X_summary->hostname = stralloc(host);
+    X_summary->diskname = stralloc(disk);
+    X_summary->level    = level;
+    X_summary->str      = stralloc(str);
+    X_summary->next = NULL;
+    if (*first == NULL) {
+       *first = X_summary;
+    }
+    else {
+        (*last)->next = X_summary;
+    }
+    *last = X_summary;
+}
+
+static void
+copy_template_file(
+    char *     lbl_templ)
 {
   char buf[BUFSIZ];
   int fd;
-  int numread;
+  ssize_t numread;
 
-  if (strchr(lbl_templ, '/') == NULL) {
-    lbl_templ = stralloc2(config_dir, lbl_templ);
-  } else {
-    lbl_templ = stralloc(lbl_templ);
-  }
+  lbl_templ = config_dir_relative(lbl_templ);
   if ((fd = open(lbl_templ, 0)) < 0) {
     curlog = L_ERROR;
     curprog = P_REPORTER;
-    curstr = vstralloc("could not open PostScript template file ",
-                      lbl_templ,
-                      ": ",
-                      strerror(errno),
-                      NULL);
+    curstr = vstrallocf(_("could not open PostScript template file %s: %s"),
+                      lbl_templ, strerror(errno));
     handle_error();
     amfree(curstr);
+    amfree(lbl_templ);
     afclose(postscript);
     return;
   }
-  while ((numread = read(fd, buf, sizeof(buf))) > 0) {
-    if (fwrite(buf, numread, 1, postscript) != 1) {
+  while ((numread = read(fd, buf, SIZEOF(buf))) > 0) {
+    if (fwrite(buf, (size_t)numread, 1, postscript) != 1) {
       curlog = L_ERROR;
       curprog = P_REPORTER;
-      curstr = vstralloc("error copying PostScript template file ",
-                        lbl_templ,
-                        ": ",
-                        strerror(errno),
-                        NULL);
+      curstr = vstrallocf(_("error copying PostScript template file %s: %s"),
+                        lbl_templ, strerror(errno));
       handle_error();
       amfree(curstr);
+      amfree(lbl_templ);
       afclose(postscript);
       return;
     }
@@ -2066,13 +2847,11 @@ char *lbl_templ;
   if (numread < 0) {
     curlog = L_ERROR;
     curprog = P_REPORTER;
-    curstr = vstralloc("error reading PostScript template file ",
-                      lbl_templ,
-                      ": ",
-                      strerror(errno),
-                      NULL);
+    curstr = vstrallocf(_("error reading PostScript template file %s: %s"),
+                      lbl_templ, strerror(errno));
     handle_error();
     amfree(curstr);
+    amfree(lbl_templ);
     afclose(postscript);
     return;
   }
@@ -2080,10 +2859,11 @@ char *lbl_templ;
   amfree(lbl_templ);
 }
 
-repdata_t *find_repdata(dp, datestamp, level)
-disk_t *dp;
-char *datestamp;
-int level;
+static repdata_t *
+find_repdata(
+    /*@keep@*/ disk_t *dp,
+               char *  datestamp,
+               int     level)
 {
     repdata_t *repdata, *prev;
 
@@ -2094,8 +2874,8 @@ int level;
        prev = repdata;
     }
     if(!repdata) {
-       repdata = (repdata_t *)alloc(sizeof(repdata_t));
-       memset(repdata, '\0',sizeof(repdata_t));
+       repdata = (repdata_t *)alloc(SIZEOF(repdata_t));
+       memset(repdata, '\0', SIZEOF(repdata_t));
        repdata->disk = dp;
        repdata->datestamp = stralloc(datestamp ? datestamp : "");
        repdata->level = level;
@@ -2111,16 +2891,21 @@ int level;
 }
 
 
-void do_postscript_output()
+static void
+do_postscript_output(void)
 {
     tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
     disk_t *dp;
     repdata_t *repdata;
-    float outsize, origsize;
-    int tapesize, marksize;
+    double outsize, origsize;
+    off_t tapesize;
+    off_t marksize;
+
+    if (!tp)
+       return;
 
-    tapesize = tp->length;
-    marksize = tp->filemark;
+    tapesize = tapetype_get_length(tp);
+    marksize = tapetype_get_filemark(tp);
 
     for(current_tape = stats_by_tape; current_tape != NULL;
            current_tape = current_tape->next) {
@@ -2129,31 +2914,34 @@ void do_postscript_output()
            break;
        }
 
-       copy_template_file(tp->lbl_templ);
+       copy_template_file(tapetype_get_lbl_templ(tp));
+
+       if (postscript == NULL)
+           return;
 
        /* generate a few elements */
-       fprintf(postscript,"(%s) DrawDate\n\n",
-                   nicedate(run_datestamp ? atoi(run_datestamp) : 0));
-       fprintf(postscript,"(Amanda Version %s) DrawVers\n",version());
-       fprintf(postscript,"(%s) DrawTitle\n", current_tape->label);
+       g_fprintf(postscript,"(%s) DrawDate\n\n",
+                   nicedate(run_datestamp ? run_datestamp : "0"));
+       g_fprintf(postscript,_("(Amanda Version %s) DrawVers\n"),version());
+       g_fprintf(postscript,"(%s) DrawTitle\n", current_tape->label);
 
        /* Stats */
-       fprintf(postscript, "(Total Size:        %6.1f MB) DrawStat\n",
+       g_fprintf(postscript, "(Total Size:        %6.1lf MB) DrawStat\n",
              mb(current_tape->coutsize));
-       fprintf(postscript, "(Tape Used (%%)       ");
+       g_fprintf(postscript, _("(Tape Used (%%)       "));
        divzero(postscript, pct(current_tape->coutsize + 
-                               marksize * current_tape->tapedisks),
-                               tapesize);
-       fprintf(postscript," %%) DrawStat\n");
-       fprintf(postscript, "(Compression Ratio:  ");
+                               marksize * (current_tape->tapedisks + current_tape->tapechunks)),
+                               (double)tapesize);
+       g_fprintf(postscript," %%) DrawStat\n");
+       g_fprintf(postscript, _("(Compression Ratio:  "));
        divzero(postscript, pct(current_tape->coutsize),current_tape->corigsize);
-       fprintf(postscript," %%) DrawStat\n");
-       fprintf(postscript,"(Filesystems Taped: %4d) DrawStat\n",
+       g_fprintf(postscript," %%) DrawStat\n");
+       g_fprintf(postscript,_("(Filesystems Taped: %4d) DrawStat\n"),
                  current_tape->tapedisks);
 
        /* Summary */
 
-       fprintf(postscript,
+       g_fprintf(postscript,
              "(-) (%s) (-) (  0) (      32) (      32) DrawHost\n",
              current_tape->label);
 
@@ -2167,25 +2955,28 @@ void do_postscript_output()
                    continue;
                }
 
-               if(repdata->dumper.result == L_SUCCESS)
+               if(repdata->dumper.result == L_SUCCESS ||
+                  repdata->dumper.result == L_PARTIAL)
                    origsize = repdata->dumper.origsize;
                else
                    origsize = repdata->taper.origsize;
 
-               if(repdata->taper.result == L_SUCCESS) 
+               if(repdata->taper.result == L_SUCCESS ||
+                  repdata->taper.result == L_PARTIAL)
                    outsize  = repdata->taper.outsize;
                else
                    outsize  = repdata->dumper.outsize;
 
-               if (repdata->taper.result == L_SUCCESS) {
-                   if(origsize != 0.0) {
-                       fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8.0f) (%8.0f) DrawHost\n",
+               if (repdata->taper.result == L_SUCCESS ||
+                   repdata->taper.result == L_PARTIAL) {
+                   if(isnormal(origsize)) {
+                       g_fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8.0lf) (%8.0lf) DrawHost\n",
                            dp->host->hostname, dp->name, repdata->level,
                            repdata->taper.filenum, origsize, 
                            outsize);
                    }
                    else {
-                       fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8s) (%8.0f) DrawHost\n",
+                       g_fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8s) (%8.0lf) DrawHost\n",
                            dp->host->hostname, dp->name, repdata->level,
                            repdata->taper.filenum, "N/A", 
                            outsize);
@@ -2194,6 +2985,6 @@ void do_postscript_output()
            }
        }
        
-       fprintf(postscript,"\nshowpage\n");
+       g_fprintf(postscript,"\nshowpage\n");
     }
 }