Imported Debian patch 2.5.1p1-1
[debian/amanda] / server-src / reporter.c
index a28d5b49a80830f1009d589ca57991c89cc4f45b..73acd3d183f1d79c8cdc095201f46691d0d39841 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
*     summary stats
*     details for errors
*     notes
*     success summary
+ */
 
 #include "amanda.h"
 #include "conffile.h"
@@ -58,9 +58,9 @@ 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;
 } timedata_t;
@@ -68,61 +68,72 @@ typedef struct timedata_s {
 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 strange_s {
+    char *hostname;
+    char *diskname;
+    int  level;
+    char *str;
+    struct strange_s *next;
+} strange_t;
+
+static strange_t *first_strange=NULL, *last_strange=NULL;
 
-float total_time, startup_time, planner_time;
+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 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;
 
-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 *notes = NULL;
 
 static char MaxWidthsRequested = 0;    /* determined via config data */
 
@@ -130,38 +141,55 @@ char *displayunit;
 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(void);
+static repdata_t *handle_success(logtype_t logtype);
+static void    addline(line_t **lp, char *str);
+static void    addtostrange(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_strange(void);
+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 +197,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 +214,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);
+    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,55 +239,75 @@ 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))
+       snprintf(PrtBuf, SIZEOF(PrtBuf),
          "%*s", cd->Width, "-- ");
     else
-       ap_snprintf(PrtBuf, sizeof(PrtBuf),
+       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;
@@ -257,7 +315,7 @@ char **argv;
     char *conf_infofile;
     char *logfname, *psfname, *outfname, *subj_str = NULL;
     tapetype_t *tp;
-    int fd, opt;
+    int opt;
     unsigned long malloc_hist_1, malloc_size_1;
     unsigned long malloc_hist_2, malloc_size_2;
     char *mail_cmd = NULL, *printer_cmd = NULL;
@@ -266,19 +324,21 @@ char **argv;
     char *ColumnSpec = "";
     char *errstr = NULL;
     int cn;
+    int mailout = 1;
+    char *mailto = NULL;
+    int    new_argc,   my_argc;
+    char **new_argv, **my_argv;
+    char *lbl_templ = NULL;
 
-    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);
-    }
+    safe_fd(-1, 0);
 
     set_pname("amreport");
 
+    dbopen(DBG_SUBDIR_SERVER);
+
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
+
     malloc_size_1 = malloc_inuse(&malloc_hist_1);
 
     /* Process options */
@@ -288,28 +348,48 @@ char **argv;
     psfname = NULL;
     logfname = NULL;
 
-    if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
+    if (getcwd(my_cwd, SIZEOF(my_cwd)) == NULL) {
        error("cannot determine current working directory");
+       /*NOTREACHED*/
     }
 
-    if (argc < 2) {
+    parse_server_conf(argc, argv, &new_argc, &new_argv);
+    my_argc = new_argc;
+    my_argv = new_argv;
+
+    if (my_argc < 2) {
        config_dir = stralloc2(my_cwd, "/");
        if ((config_name = strrchr(my_cwd, '/')) != NULL) {
            config_name = stralloc(config_name + 1);
        }
     } else {
-       if (argv[1][0] == '-') {
+       if (my_argv[1][0] == '-') {
            usage();
            return 1;
        }
-       config_name = stralloc(argv[1]);
+       config_name = stralloc(my_argv[1]);
        config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
-       --argc; ++argv;
-       while((opt = getopt(argc, argv, "f:l:p:")) != EOF) {
+       --my_argc; ++my_argv;
+       while((opt = getopt(my_argc, my_argv, "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");
+                   /*NOTREACHED*/
                }
                if (*optarg == '/') {
                     outfname = stralloc(optarg);
@@ -320,6 +400,7 @@ char **argv;
             case 'l':
                if (logfname != NULL) {
                    error("you may specify at most one -l");
+                   /*NOTREACHED*/
                }
                if (*optarg == '/') {
                    logfname = stralloc(optarg);
@@ -330,6 +411,7 @@ char **argv;
             case 'p':
                if (psfname != NULL) {
                    error("you may specify at most one -p");
+                   /*NOTREACHED*/
                }
                if (*optarg == '/') {
                     psfname = stralloc(optarg);
@@ -338,20 +420,21 @@ char **argv;
                }
                 break;
             case '?':
-            default:
                usage();
                return 1;
+            default:
+               break;
            }
        }
 
-       argc -= optind;
-       argv += optind;
-
-       if (argc > 1) {
-           usage();
-           return 1;
-       }
+       my_argc -= optind;
+       my_argv += optind;
     }
+    if( !mailout && mailto ){
+       printf("You cannot specify both -i & -M at the same time\n");
+       exit(1);
+    }
+
 
 #if !defined MAILER
     if(!outfname) {
@@ -366,29 +449,38 @@ char **argv;
     /* read configuration files */
 
     conffile = stralloc2(config_dir, CONFFILE_NAME);
-    if(read_conffile(conffile)) {
-        error("errors processing config file \"%s\"", conffile);
-    }
+    /* Ignore error from read_conffile */
+    read_conffile(conffile);
     amfree(conffile);
+
+    dbrename(config_name, DBG_SUBDIR_SERVER);
+
+    report_bad_conf_arg();
     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 error from read_diskfile */
+    read_diskfile(conf_diskfile, &diskq);
     amfree(conf_diskfile);
+    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 = 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);
-    }
+    /* Ignore error from read_tapelist */
+    read_tapelist(conf_tapelist);
     amfree(conf_tapelist);
     conf_infofile = getconf_str(CNF_INFOFILE);
     if (*conf_infofile == '/') {
@@ -398,11 +490,10 @@ char **argv;
     }
     if(open_infofile(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();
 
@@ -472,7 +563,10 @@ char **argv;
 
        case L_DISK:    handle_disk(); break;
 
-       case L_SUCCESS: handle_success(); break;
+       case L_SUCCESS: handle_success(curlog); break;
+       case L_CHUNKSUCCESS: handle_success(curlog); break;
+       case L_CHUNK:   handle_chunk(); break;
+       case L_PARTIAL: handle_partial(); break;
        case L_STRANGE: handle_strange(); break;
        case L_FAIL:    handle_failed(); break;
 
@@ -486,13 +580,15 @@ char **argv;
     }
     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. */
@@ -508,19 +604,30 @@ char **argv;
        /* output to a file */
        if((mailf = fopen(outfname,"w")) == NULL) {
            error("could not open output file: %s %s", outfname, strerror(errno));
+           /*NOTREACHED*/
        }
-       fprintf(mailf, "To: %s\n", getconf_str(CNF_MAILTO));
+       fprintf(mailf, "To: %s\n", 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) {
+                   printf("No mail sent! ");
+                  printf("No valid mail address has been specified in amanda.conf or on the commmand line\n");
+               }
+               mailf = NULL;
+       }
 #endif
     }
 
@@ -530,7 +637,9 @@ 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;
@@ -556,8 +665,9 @@ 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;
@@ -582,34 +692,37 @@ char **argv;
 
     amfree(subj_str);
 
+    if(mailf) {
 
-    if(!got_finish) fputs("*** THE DUMPS DID NOT FINISH PROPERLY!\n\n", mailf);
-
-    output_tapeinfo();
-
-    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();
+       output_tapeinfo();
 
-    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();
+       if(first_strange || errsum) {
+               fprintf(mailf,"\nFAILURE AND STRANGE DUMP SUMMARY:\n");
+               if(first_strange) output_strange();
+               if(errsum) output_lines(errsum, mailf);
+       }
+       fputs("\n\n", mailf);
+       
+       output_stats();
+       
+       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();
+       }
+       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 +735,10 @@ char **argv;
        afclose(postscript);
     }
     else {
-       if (postscript != NULL && pclose(postscript) != 0)
+       if (postscript != NULL && pclose(postscript) != 0) {
            error("printer command failed: %s", printer_cmd);
+           /*NOTREACHED*/
+       }
        postscript = NULL;
     }
 
@@ -631,12 +746,18 @@ char **argv;
     if(outfname) {
         afclose(mailf);
     }
-    else {
-        if(pclose(mailf) != 0)
+    else if(mailf) {
+        if(pclose(mailf) != 0) {
             error("mail command failed: %s", mail_cmd);
+           /*NOTREACHED*/
+       }
         mailf = NULL;
     }
 
+    clear_tapelist();
+    free_disklist(&diskq);
+    free_new_argv(new_argc, new_argv);
+    free_server_config();
     amfree(run_datestamp);
     amfree(tape_labels);
     amfree(config_dir);
@@ -651,6 +772,7 @@ char **argv;
        malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
     }
 
+    dbclose();
     return 0;
 }
 
@@ -665,34 +787,44 @@ char **argv;
 #define divzero(fp,a,b)                            \
     do {                                           \
        double q = (b);                     \
-       if (q == 0.0)                       \
+       if (!isnormal(q))                   \
            fprintf((fp),"  -- ");          \
        else if ((q = (a)/q) >= 999.95)     \
            fprintf((fp), "###.#");         \
        else                                \
-           fprintf((fp), "%5.1f",q);       \
+           fprintf((fp), "%5.1lf",q);      \
     } while(0)
 #define divzero_wide(fp,a,b)               \
     do {                                           \
        double q = (b);                     \
-       if (q == 0.0)                       \
+       if (!isnormal(q))                   \
            fprintf((fp),"    -- ");        \
        else if ((q = (a)/q) >= 99999.95)   \
            fprintf((fp), "#####.#");       \
        else                                \
-           fprintf((fp), "%7.1f",q);       \
+           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,6 +836,9 @@ void output_stats()
     if(!got_finish)    /* no driver finish line, estimate total run time */
        total_time = stats[2].taper_time + planner_time;
 
+    idle_time = (total_time - startup_time) - stats[2].taper_time;
+    if(idle_time < 0) idle_time = 0.0;
+
     fprintf(mailf,"STATISTICS:\n");
     fprintf(mailf,
            "                          Total       Full      Incr.\n");
@@ -722,11 +857,11 @@ void output_stats()
            hrmn(stats[1].dumper_time));
 
     fprintf(mailf,
-           "Output Size (meg)      %8.1f   %8.1f   %8.1f\n",
+           "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",
+           "Original Size (meg)    %8.1lf   %8.1lf   %8.1lf\n",
            mb(stats[2].origsize), mb(stats[0].origsize),
            mb(stats[1].origsize));
 
@@ -770,16 +905,16 @@ void output_stats()
            hrmn(stats[1].taper_time));
 
     fprintf(mailf,
-           "Tape Size (meg)        %8.1f   %8.1f   %8.1f\n",
+           "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);
+    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),tapesize);
+    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),tapesize);
+    divzero(mailf, pct(stats[1].tapesize+marksize*(stats[1].tapedisks+stats[1].tapechunks)),(double)tapesize);
 
     if(stats[1].tapedisks > 0) fputs("   (level:#disks ...)", mailf);
     putc('\n', mailf);
@@ -799,6 +934,24 @@ void output_stats()
     }
     putc('\n', mailf);
 
+    if(stats[1].tapechunks > 0) fputs("   (level:#chunks ...)", mailf);
+    putc('\n', mailf);
+
+    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;
+           fprintf(mailf, "%d:%d", lv, tapechunks[lv]);
+       }
+       putc(')', mailf);
+    }
+    putc('\n', mailf);
+
     fprintf(mailf, "Avg Tp Write Rate (k/s) ");
     divzero_wide(mailf, stats[2].tapesize,stats[2].taper_time);
     fputs("    ", mailf);
@@ -808,27 +961,28 @@ void output_stats()
     putc('\n', mailf);
 
     if(stats_by_tape) {
-       int label_length = strlen(stats_by_tape->label) + 5;
+       int label_length = (int)strlen(stats_by_tape->label) + 5;
        fprintf(mailf,"\nUSAGE BY TAPE:\n");
-       fprintf(mailf,"  %-*s  Time      Size      %%    Nb\n",
+       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);
+           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);
+           fprintf(mailf, "  %4d", current_tape->tapedisks);
+           fprintf(mailf, "  %4d\n", current_tape->tapechunks);
        }
     }
-
 }
 
 /* ----- */
 
-void output_tapeinfo()
+static void
+output_tapeinfo(void)
 {
     tape_t *tp, *lasttp;
     int run_tapes;
@@ -858,17 +1012,22 @@ void output_tapeinfo()
 
     run_tapes = getconf_int(CNF_RUNTAPES);
 
-    if (run_tapes <= 1)
+    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: ",
+    else if(run_tapes > 1)
+       fprintf(mailf, "The next %d tapes Amanda expects to use are: ",
                run_tapes);
     
     while(run_tapes > 0) {
-       if(tp != NULL)
+       if(tp != NULL) {
            fprintf(mailf, "%s", tp->label);
-       else
-           fputs("a new tape", mailf);
+       } else {
+           if (run_tapes == 1)
+               fprintf(mailf, "a new tape");
+           else
+               fprintf(mailf, "%d new tapes", run_tapes);
+           run_tapes = 1;
+       }
 
        if(run_tapes > 1) fputs(", ", mailf);
 
@@ -880,9 +1039,9 @@ void output_tapeinfo()
 
     lasttp = lookup_tapepos(lookup_nb_tape());
     run_tapes = getconf_int(CNF_RUNTAPES);
-    if(lasttp && run_tapes > 0 && lasttp->datestamp == 0) {
+    if(lasttp && run_tapes > 0 && strcmp(lasttp->datestamp,"0") == 0) {
        int c = 0;
-       while(lasttp && run_tapes > 0 && lasttp->datestamp == 0) {
+       while(lasttp && run_tapes > 0 && strcmp(lasttp->datestamp,"0") == 0) {
            c++;
            lasttp = lasttp->prev;
            run_tapes--;
@@ -897,7 +1056,7 @@ void output_tapeinfo()
                    lasttp->label);
            lasttp = lasttp->prev;
            c--;
-           while(lasttp && c > 0 && lasttp->datestamp == 0) {
+           while(lasttp && c > 0 && strcmp(lasttp->datestamp,"0") == 0) {
                fprintf(mailf, ", %s", lasttp->label);
                lasttp = lasttp->prev;
                c--;
@@ -908,10 +1067,31 @@ void output_tapeinfo()
 }
 
 /* ----- */
+static void
+output_strange(void)
+{
+    size_t len_host=0, len_disk=0;
+    strange_t *strange;
+    char *str = NULL;
+
+    for(strange=first_strange; 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; strange != NULL; strange = strange->next) {
+       str = vstralloc("  ", prefixstrange(strange->hostname, strange->diskname, strange->level, len_host, len_disk),
+                       "  ", strange->str, NULL);
+       fprintf(mailf, "%s\n", str);
+       amfree(str);
+    }
+}
 
-void output_lines(lp, f)
-line_t *lp;
-FILE *f;
+static void
+output_lines(
+    line_t *   lp,
+    FILE *     f)
 {
     line_t *next;
 
@@ -927,14 +1107,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 +1119,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 +1134,47 @@ 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),
+
+       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),
+       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 +1191,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,12 +1201,11 @@ void CalcMaxWidth() {
      *                                                 ElB, 1999-02-24.
      */
     disk_t *dp;
-    float f;
+    double f;
     repdata_t *repdata;
     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);
@@ -1022,9 +1214,12 @@ void CalcMaxWidth() {
                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(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(dp->compress == COMP_NONE)
                    f = 0.0;
                else 
@@ -1033,40 +1228,41 @@ void CalcMaxWidth() {
                        sDivZero(pct(repdata->dumper.outsize), f, Compress));
 
                if(!amflush_run)
-                   ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+                   snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                                "%3d:%02d", mnsc(repdata->dumper.sec));
                else
-                   ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+                   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)
+               snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer), 
                  "%3d:%02d", mnsc(repdata->taper.sec));
            else
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+               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 +1271,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,19 +1295,19 @@ 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);
+    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);
@@ -1151,7 +1347,8 @@ void output_summary()
        ColumnInfo *cd;
        char TimeRateBuffer[40];
        for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
-           int devlen;
+           char *devname;
+           size_t devlen;
 
            cd= &ColumnData[HostName];
            fprintf(mailf, "%*s", cd->PrefixSpace, "");
@@ -1159,15 +1356,16 @@ void output_summary()
 
            cd= &ColumnData[Disk];
            fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           devlen= strlen(dp->name);
-           if (devlen > cd->Width) {
+           devname = sanitize_string(dp->name);
+           devlen = strlen(devname);
+           if (devlen > (size_t)cd->Width) {
                fputc('-', mailf); 
                fprintf(mailf, cd->Format, cd->Width-1, cd->Precision-1,
-                 dp->name+devlen - (cd->Width-1) );
+                 devname+devlen - (cd->Width-1) );
            }
            else
-               fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->name);
-
+               fprintf(mailf, cd->Format, cd->Width, cd->Width, devname);
+           amfree(devname);
            cd= &ColumnData[Level];
            if (repdata->dumper.result == L_BOGUS &&
                repdata->taper.result  == L_BOGUS) {
@@ -1191,26 +1389,36 @@ void output_summary()
                amfree(tmp);
                continue;
            }
-           if (repdata->dumper.result == L_FAIL) {
+           if (repdata->dumper.result == L_FAIL && (repdata->chunker.result != L_PARTIAL && repdata->taper.result  != L_PARTIAL)) {
                fprintf(mailf, "%s\n",
                        tmp=TextRule(OrigKB, TapeRate, "FAILED"));
                amfree(tmp);
                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_PARTIAL ||
+              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
                outsize  = repdata->dumper.outsize;
 
            cd= &ColumnData[OrigKB];
            fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(origsize != 0.0)
+           if(isnormal(origsize))
                fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(origsize));
            else
                fprintf(mailf, "%*.*s", cd->Width, cd->Width, "N/A");
@@ -1234,17 +1442,19 @@ void output_summary()
 
            cd= &ColumnData[DumpTime];
            fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(repdata->dumper.result == L_SUCCESS)
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+           if(repdata->dumper.result == L_SUCCESS ||
+              repdata->dumper.result == L_CHUNKSUCCESS)
+               snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                  "%3d:%02d", mnsc(repdata->dumper.sec));
            else
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+               snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                  "N/A ");
            fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
 
            cd= &ColumnData[DumpRate];
            fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(repdata->dumper.result == L_SUCCESS)
+           if(repdata->dumper.result == L_SUCCESS ||
+                   repdata->dumper.result == L_CHUNKSUCCESS)
                fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->dumper.kps);
            else
                fprintf(mailf, "%*s", cd->Width, "N/A ");
@@ -1258,58 +1468,79 @@ void output_summary()
                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)
+               snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                  "%3d:%02d", mnsc(repdata->taper.sec));
            else
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+               snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
                  "N/A ");
            fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
 
            cd= &ColumnData[TapeRate];
            fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(repdata->taper.result == L_SUCCESS)
+           if(repdata->taper.result == L_SUCCESS || 
+              repdata->taper.result == L_PARTIAL ||
+              repdata->taper.result == L_CHUNKSUCCESS)
                fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
            else
                fprintf(mailf, "%*s", cd->Width, "N/A ");
+
+           if(repdata->chunker.result == L_PARTIAL ||
+              repdata->taper.result == L_PARTIAL) {
+               fprintf(mailf, " PARTIAL");
+           }
            fputc('\n', mailf);
        }
       }
     }
 }
 
-void bogus_line()
+static void
+bogus_line(
+    const char *err_text)
 {
-    printf("line %d of log is bogus\n", curlinenum);
+    printf("line %d of log is bogus: <%s>\n", curlinenum, curstr);
+    printf("  Scan failed at: <%s>\n", err_text);
 }
 
 
-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];
+    char date[9];
+    int  numdate;
     static char *months[13] = { "BogusMonth",
        "January", "February", "March", "April", "May", "June",
        "July", "August", "September", "October", "November", "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);
+    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;
@@ -1323,36 +1554,36 @@ void handle_start()
 
        skip_whitespace(s, ch);
 #define sc "datestamp"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
-           bogus_line();
+       if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
+           bogus_line(s - 1);
            return;
        }
-       s += sizeof(sc)-1;
+       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(s - 1, sc, SIZEOF(sc)-1) != 0) {
+           bogus_line(s - 1);
            return;
        }
-       s += sizeof(sc)-1;
+       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 +1591,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 +1604,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 +1615,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;
 
@@ -1406,22 +1638,22 @@ void handle_start()
 
        skip_whitespace(s, ch);
 #define sc "date"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
+       if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
            return;                             /* ignore bogus line */
        }
-       s += sizeof(sc)-1;
+       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;
     }
@@ -1433,11 +1665,12 @@ void handle_start()
 }
 
 
-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;
@@ -1445,40 +1678,40 @@ void handle_finish()
 
        skip_whitespace(s, ch);
 #define sc "date"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
-           bogus_line();
+       if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
+           bogus_line(s - 1);
            return;
        }
-       s += sizeof(sc)-1;
+       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(s - 1, sc, SIZEOF(sc)-1) != 0) {
            /* older planner doesn't write time */
            if(curprog == P_PLANNER) return;
-           bogus_line();
+           bogus_line(s - 1);
            return;
        }
-       s += sizeof(sc)-1;
+       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,10 +1724,16 @@ void handle_finish()
     }
 }
 
-void handle_stats()
+static void
+handle_stats(void)
 {
-    char *s;
+    char *s, *fp;
     int ch;
+    char *hostname, *diskname, *datestamp;
+    int level = 0;
+    double sec, kps, nbytes, cbytes;
+    repdata_t *repdata;
+    disk_t *dp;
 
     if(curprog == P_DRIVER) {
        s = curstr;
@@ -1502,29 +1741,122 @@ void handle_stats()
 
        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(s - 1, sc, sizeof(sc)-1) == 0) {
+           s += sizeof(sc)-1;
+           ch = s[-1];
+#undef sc
+
+           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];
+#define sc "estimate"
+       else if(ch != '\0' && strncmp(s - 1, sc, sizeof(sc)-1) == 0) {
+           s += sizeof(sc)-1;
+           ch = s[-1];
 #undef sc
 
-       skip_whitespace(s, ch);
-       if(ch == '\0') {
-           bogus_line();
-           return;
+           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;
+           }
+           fp = s - 1;
+           skip_non_whitespace(s, ch);
+           s[-1] = '\0';
+           diskname = stralloc(fp);
+           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) {
+               addtostrange(hostname, diskname, level,
+                            "ERROR [not in disklist]");
+               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;
 
@@ -1536,7 +1868,8 @@ void handle_note()
 
 /* ----- */
 
-void handle_error()
+static void
+handle_error(void)
 {
     char *s = NULL, *nl;
     int ch;
@@ -1547,23 +1880,22 @@ void handle_error()
 
        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];
+       if(ch != '\0' && strncmp(s - 1, sc, SIZEOF(sc)-1) == 0) {
+           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';
+           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;
+               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 */
     }
@@ -1575,28 +1907,30 @@ void handle_error()
 
 /* ----- */
 
-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 +1940,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 +1971,15 @@ 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(void)
 {
     disk_t *dp;
-    float sec, kps, kbytes, origkb;
+    double sec, kps, kbytes;
     timedata_t *sp;
     int i;
     char *s, *fp;
@@ -1647,11 +1987,154 @@ repdata_t *handle_success()
     char *hostname = NULL;
     char *diskname = NULL;
     repdata_t *repdata;
-    int level;
+    int level, chunk;
+    char *datestamp;
+    
+    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;
+    }
+    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);
+
+    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 = vstralloc("  ", prefix(hostname, diskname, level),
+                       " ", "ERROR [not in disklist]",
+                       NULL);
+       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;
     char *datestamp;
 
-    if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER) {
-       bogus_line();
+    if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER &&
+       curprog != P_CHUNKER) {
+       bogus_line(curstr);
        return NULL;
     }
 
@@ -1660,30 +2143,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 +2174,16 @@ 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)  {
+    if(strlen(datestamp) < 3) {
+       level = atoi(datestamp);
        datestamp = newstralloc(datestamp, run_datestamp);
     }
     else {
        skip_whitespace(s, ch);
        if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
-           bogus_line();
+           bogus_line(s - 1);
            amfree(hostname);
            amfree(diskname);
            amfree(datestamp);
@@ -1710,17 +2192,26 @@ repdata_t *handle_success()
        skip_integer(s, ch);
     }
 
+    if(level < 0 || level > 9) {
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+
     skip_whitespace(s, ch);
                                /* 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 +2219,15 @@ 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);
+       addtostrange(hostname, qdiskname, level, "ERROR [not in disklist]");
        amfree(hostname);
        amfree(diskname);
        amfree(datestamp);
@@ -1760,19 +2246,25 @@ 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) {
        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 +2273,12 @@ repdata_t *handle_success()
        else
            origkb = 0.0;
     }
+
+    if (curprog == P_DUMPER &&
+       (sp->result == L_FAIL || sp->result == L_PARTIAL)) {
+       addtostrange(hostname, qdiskname, level, "was successfully retried");
+    }
+
     amfree(hostname);
     amfree(diskname);
     amfree(datestamp);
@@ -1795,6 +2293,7 @@ repdata_t *handle_success()
     if(curprog == P_TAPER) {
        if(current_tape == NULL) {
            error("current_tape == NULL");
+           /*NOTREACHED*/
        }
        stats[i].taper_time += sec;
        sp->filenum = ++tapefcount;
@@ -1802,8 +2301,17 @@ repdata_t *handle_success()
        tapedisks[level] +=1;
        stats[i].tapedisks +=1;
        stats[i].tapesize += kbytes;
-       current_tape->taper_time += sec;
-       current_tape->coutsize += kbytes;
+       sp->outsize = kbytes;
+       if(!isnormal(repdata->chunker.outsize) && isnormal(repdata->dumper.outsize)) { /* dump to tape */
+           stats[i].outsize += kbytes;
+           if(dp->compress != COMP_NONE) {
+               stats[i].coutsize += kbytes;
+           }
+       }
+       if (logtype == L_SUCCESS || logtype== L_PARTIAL) {
+           current_tape->taper_time += sec;
+           current_tape->coutsize += kbytes;
+       }
        current_tape->corigsize += origkb;
        current_tape->tapedisks += 1;
     }
@@ -1814,29 +2322,59 @@ repdata_t *handle_success()
            sp->origsize = kbytes;
        }
        else {
-           stats[i].coutsize += kbytes;
            stats[i].corigsize += sp->origsize;
        }
        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(dp->compress != COMP_NONE) {
+           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();
+    repdata = handle_success(L_SUCCESS);
+    if (!repdata)
+       return;
+
+    qdisk = quote_string(repdata->disk->name);
 
     addline(&errdet,"");
     str = vstralloc("/-- ", prefix(repdata->disk->host->hostname, 
-                                  repdata->disk->name, repdata->level),
+                                  qdisk, repdata->level),
                    " ", "STRANGE",
                    NULL);
     addline(&errdet, str);
@@ -1845,31 +2383,30 @@ void handle_strange()
     while(contline_next()) {
        get_logline(logfile);
 #define sc "sendbackup: warning "
-       if(strncmp(curstr, sc, sizeof(sc)-1) == 0) {
-           strangestr = newstralloc(strangestr, curstr+sizeof(sc)-1);
+       if(strncmp(curstr, sc, SIZEOF(sc)-1) == 0) {
+           strangestr = newstralloc(strangestr, curstr+SIZEOF(sc)-1);
        }
        addline(&errdet, curstr);
     }
     addline(&errdet,"\\--------");
 
-    str = vstralloc("  ", prefix(repdata->disk->host->hostname, 
-                                repdata->disk->name, repdata->level),
-                   " ", "STRANGE", " ", strangestr,
-                   NULL);
-    addline(&errsum, str);
+    str = vstralloc("STRANGE", " ", strangestr, NULL);
+    addtostrange(repdata->disk->host->hostname, qdisk, repdata->level, str);
+    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 +2420,7 @@ void handle_failed()
 
     skip_whitespace(s, ch);
     if(ch == '\0') {
-       bogus_line();
+       bogus_line(s - 1);
        return;
     }
     hostname = s - 1;
@@ -1892,16 +2429,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 +2455,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 +2465,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 +2476,9 @@ 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);
+       addtostrange(hostname, qdiskname, level, "ERROR [not in disklist]");
     } else {
        repdata = find_repdata(dp, datestamp, level);
 
@@ -1953,16 +2491,13 @@ void handle_failed()
     }
     amfree(datestamp);
 
-    str = vstralloc("  ", prefix(hostname, diskname, level),
-                   " ", "FAILED",
-                   " ", errstr,
-                   NULL);
-    addline(&errsum, str);
+    str = vstralloc("FAILED", " ", errstr, NULL);
+    addtostrange(hostname, qdiskname, level, str);
     amfree(str);
 
     if(curprog == P_DUMPER) {
        addline(&errdet,"");
-       str = vstralloc("/-- ", prefix(hostname, diskname, level),
+       str = vstralloc("/-- ", prefix(hostname, qdiskname, level),
                        " ", "FAILED",
                        " ", errstr,
                        NULL);
@@ -1977,58 +2512,179 @@ void handle_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);
+           addtostrange(dp->host->hostname, qdisk, -987, "RESULTS 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_PARTIAL ||
+                      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
+                       outsize  = repdata->dumper.outsize;
+
+                   if(repdata->est_csize * 0.9 > outsize) {
+                       snprintf(s, 1000,
+                               "  big estimate: %s %s %d",
+                                repdata->disk->host->hostname,
+                                repdata->disk->name,
+                                repdata->level);
+                       s[999] = '\0';
+                       addline(&notes, s);
+                       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) {
+                       snprintf(s, 1000,
+                               "  small estimate: %s %s %d",
+                                repdata->disk->host->hostname,
+                                repdata->disk->name,
+                                repdata->level);
+                       s[999] = '\0';
+                       addline(&notes, s);
+                       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)
 {
-    char h[10+1];
-    int l;
     char number[NUM_STR_SIZE];
     static char *str = NULL;
 
-    ap_snprintf(number, sizeof(number), "%d", level);
+    snprintf(number, SIZEOF(number), "%d", level);
+    str = newvstralloc(str,
+                      " ", host ? host : "(host?)",
+                      " ", disk ? disk : "(disk?)",
+                      level != -987 ? " lev " : "",
+                      level != -987 ? number : "",
+                      NULL);
+    return str;
+}
+
+
+static char *
+prefixstrange (
+    char *     host,
+    char *     disk,
+    int                level,
+    size_t     len_host,
+    size_t     len_disk)
+{
+    char *h, *d;
+    size_t l;
+    char number[NUM_STR_SIZE];
+    static char *str = NULL;
+
+    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] = ' ';
     }
+    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] = ' ';
+    }
     str = newvstralloc(str,
                       h,
-                      " ", disk ? disk : "(disk?)",
-                      level != -987 ? " lev " : "",
+                      "  ", d,
+                      level != -987 ? "  lev " : "",
                       level != -987 ? number : "",
                       NULL);
+    amfree(h);
+    amfree(d);
     return str;
 }
 
-void copy_template_file(lbl_templ)
-char *lbl_templ;
+
+static void
+addtostrange (
+    char *     host,
+    char *     disk,
+    int                level,
+    char *     str)
+{
+    strange_t *strange;
+
+    strange = alloc(SIZEOF(strange_t));
+    strange->hostname = stralloc(host);
+    strange->diskname = stralloc(disk);
+    strange->level    = level;
+    strange->str      = stralloc(str);
+    strange->next = NULL;
+    if(first_strange == NULL) {
+       first_strange = strange;
+    }
+    else {
+        last_strange->next = strange;
+    }
+    last_strange = strange;
+}
+
+
+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);
@@ -2045,11 +2701,12 @@ char *lbl_templ;
                       NULL);
     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 ",
@@ -2059,6 +2716,7 @@ char *lbl_templ;
                         NULL);
       handle_error();
       amfree(curstr);
+      amfree(lbl_templ);
       afclose(postscript);
       return;
     }
@@ -2073,6 +2731,7 @@ char *lbl_templ;
                       NULL);
     handle_error();
     amfree(curstr);
+    amfree(lbl_templ);
     afclose(postscript);
     return;
   }
@@ -2080,10 +2739,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 +2754,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 +2771,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,21 +2794,21 @@ void do_postscript_output()
            break;
        }
 
-       copy_template_file(tp->lbl_templ);
+       copy_template_file(tapetype_get_lbl_templ(tp));
 
        /* generate a few elements */
        fprintf(postscript,"(%s) DrawDate\n\n",
-                   nicedate(run_datestamp ? atoi(run_datestamp) : 0));
+                   nicedate(run_datestamp ? run_datestamp : "0"));
        fprintf(postscript,"(Amanda Version %s) DrawVers\n",version());
        fprintf(postscript,"(%s) DrawTitle\n", current_tape->label);
 
        /* Stats */
-       fprintf(postscript, "(Total Size:        %6.1f MB) DrawStat\n",
+       fprintf(postscript, "(Total Size:        %6.1lf MB) DrawStat\n",
              mb(current_tape->coutsize));
        fprintf(postscript, "(Tape Used (%%)       ");
        divzero(postscript, pct(current_tape->coutsize + 
-                               marksize * current_tape->tapedisks),
-                               tapesize);
+                               marksize * (current_tape->tapedisks + current_tape->tapechunks)),
+                               (double)tapesize);
        fprintf(postscript," %%) DrawStat\n");
        fprintf(postscript, "(Compression Ratio:  ");
        divzero(postscript, pct(current_tape->coutsize),current_tape->corigsize);
@@ -2167,25 +2832,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)) {
+                       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",
+                       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);