Imported Upstream version 2.4.5
[debian/amanda] / server-src / reporter.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /*
28  * $Id: reporter.c,v 1.44.2.17.4.6.2.16.2.6 2005/03/29 16:35:11 martinea Exp $
29  *
30  * nightly Amanda Report generator
31  */
32 /*
33 report format
34     tape label message
35     error messages
36     summary stats
37     details for errors
38     notes
39     success summary
40 */
41
42 #include "amanda.h"
43 #include "conffile.h"
44 #include "tapefile.h"
45 #include "diskfile.h"
46 #include "infofile.h"
47 #include "logfile.h"
48 #include "version.h"
49 #include "util.h"
50
51 /* don't have (or need) a skipped type except internally to reporter */
52 #define L_SKIPPED       L_MARKER
53
54 typedef struct line_s {
55     struct line_s *next;
56     char *str;
57 } line_t;
58
59 typedef struct timedata_s {
60     logtype_t result;
61     float origsize, outsize;
62     char *datestamp;
63     float sec, kps;
64     int filenum;
65     char *tapelabel;
66 } timedata_t;
67
68 typedef struct repdata_s {
69     disk_t *disk;
70     char *datestamp;
71     timedata_t taper;
72     timedata_t dumper;
73     int level;
74     struct repdata_s *next;
75 } repdata_t;
76
77 #define data(dp) ((repdata_t *)(dp)->up)
78
79 struct cumulative_stats {
80     int dumpdisks, tapedisks;
81     double taper_time, dumper_time;
82     double outsize, origsize, tapesize;
83     double coutsize, corigsize;                 /* compressed dump only */
84 } stats[3];
85
86 int dumpdisks[10], tapedisks[10];       /* by-level breakdown of disk count */
87
88 typedef struct taper_s {
89     char *label;
90     double taper_time;
91     double coutsize, corigsize;
92     int tapedisks;
93     struct taper_s *next;
94 } taper_t;
95
96 taper_t *stats_by_tape = NULL;
97 taper_t *current_tape = NULL;
98
99 float total_time, startup_time, planner_time;
100
101 /* count files to tape */
102 int tapefcount = 0;
103
104 char *run_datestamp;
105 char *today_datestamp;
106 char *tape_labels = NULL;
107 int last_run_tapes = 0;
108 static int degraded_mode = 0; /* defined in driverio too */
109 int normal_run = 0;
110 int amflush_run = 0;
111 int got_finish = 0;
112
113 char *tapestart_error = NULL;
114
115 FILE *logfile, *mailf;
116
117 FILE *postscript;
118 char *printer;
119
120 disklist_t *diskq;
121 disklist_t sortq;
122
123 line_t *errsum = NULL;
124 line_t *errdet = NULL;
125 line_t *notes = NULL;
126
127 static char MaxWidthsRequested = 0;     /* determined via config data */
128
129 char *displayunit;
130 long int unitdivisor;
131
132 /* local functions */
133 int contline_next P((void));
134 void addline P((line_t **lp, char *str));
135 void usage P((void));
136 int main P((int argc, char **argv));
137
138 void copy_template_file P((char *lbl_templ));
139 void do_postscript_output P((void));
140 void handle_start P((void));
141 void handle_finish P((void));
142 void handle_note P((void));
143 void handle_summary P((void));
144 void handle_stats P((void));
145 void handle_error P((void));
146 void handle_disk P((void));
147 repdata_t *handle_success P((void));
148 void handle_strange P((void));
149 void handle_failed P((void));
150 void generate_missing P((void));
151 void output_tapeinfo P((void));
152 void output_lines P((line_t *lp, FILE *f));
153 void output_stats P((void));
154 void output_summary P((void));
155 void sort_disks P((void));
156 int sort_by_time P((disk_t *a, disk_t *b));
157 int sort_by_name P((disk_t *a, disk_t *b));
158 void bogus_line P((void));
159 char *nicedate P((int datestamp));
160 static char *prefix P((char *host, char *disk, int level));
161 repdata_t *find_repdata P((disk_t *dp, char *datestamp, int level));
162
163
164 static int ColWidth(int From, int To) {
165     int i, Width= 0;
166     for (i=From; i<=To && ColumnData[i].Name != NULL; i++) {
167         Width+= ColumnData[i].PrefixSpace + ColumnData[i].Width;
168     }
169     return Width;
170 }
171
172 static char *Rule(int From, int To) {
173     int i, ThisLeng;
174     int Leng= ColWidth(0, ColumnDataCount());
175     char *RuleSpace= alloc(Leng+1);
176     ThisLeng= ColWidth(From, To);
177     for (i=0;i<ColumnData[From].PrefixSpace; i++)
178         RuleSpace[i]= ' ';
179     for (; i<ThisLeng; i++)
180         RuleSpace[i]= '-';
181     RuleSpace[ThisLeng]= '\0';
182     return RuleSpace;
183 }
184
185 static char *TextRule(int From, int To, char *s) {
186     ColumnInfo *cd= &ColumnData[From];
187     int leng, nbrules, i, txtlength;
188     int RuleSpaceSize= ColWidth(0, ColumnDataCount());
189     char *RuleSpace= alloc(RuleSpaceSize), *tmp;
190
191     leng= strlen(s);
192     if(leng >= (RuleSpaceSize - cd->PrefixSpace))
193         leng = RuleSpaceSize - cd->PrefixSpace - 1;
194     ap_snprintf(RuleSpace, RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "", 
195                 leng, leng, s);
196     txtlength = cd->PrefixSpace + leng + 1;
197     nbrules = ColWidth(From,To) - txtlength;
198     for(tmp=RuleSpace + txtlength, i=nbrules ; i>0; tmp++,i--)
199         *tmp='-';
200     *tmp = '\0';
201     return RuleSpace;
202 }
203
204 char *sDivZero(float a, float b, int cn) {
205     ColumnInfo *cd= &ColumnData[cn];
206     static char PrtBuf[256];
207     if (b == 0.0)
208         ap_snprintf(PrtBuf, sizeof(PrtBuf),
209           "%*s", cd->Width, "-- ");
210     else
211         ap_snprintf(PrtBuf, sizeof(PrtBuf),
212           cd->Format, cd->Width, cd->Precision, a/b);
213     return PrtBuf;
214 }
215
216
217
218 int contline_next()
219 {
220     int ch;
221
222     ch = getc(logfile);
223     ungetc(ch, logfile);
224
225     return ch == ' ';
226 }
227
228 void addline(lp, str)
229 line_t **lp;
230 char *str;
231 {
232     line_t *new, *p, *q;
233
234     /* allocate new line node */
235     new = (line_t *) alloc(sizeof(line_t));
236     new->next = NULL;
237     new->str = stralloc(str);
238
239     /* add to end of list */
240     for(p = *lp, q = NULL; p != NULL; q = p, p = p->next);
241     if(q == NULL) *lp = new;
242     else q->next = new;
243 }
244
245 void usage()
246 {
247     error("Usage: amreport conf [-f output-file] [-l logfile] [-p postscript-file]");
248 }
249
250 int main(argc, argv)
251 int argc;
252 char **argv;
253 {
254     char *conffile;
255     char *conf_diskfile;
256     char *conf_tapelist;
257     char *conf_infofile;
258     char *logfname, *psfname, *outfname, *subj_str = NULL;
259     tapetype_t *tp;
260     int fd, opt;
261     unsigned long malloc_hist_1, malloc_size_1;
262     unsigned long malloc_hist_2, malloc_size_2;
263     char *mail_cmd = NULL, *printer_cmd = NULL;
264     extern int optind;
265     char my_cwd[STR_SIZE];
266     char *ColumnSpec = "";
267     char *errstr = NULL;
268     int cn;
269
270     for(fd = 3; fd < FD_SETSIZE; fd++) {
271         /*
272          * Make sure nobody spoofs us with a lot of extra open files
273          * that would cause an open we do to get a very high file
274          * descriptor, which in turn might be used as an index into
275          * an array (e.g. an fd_set).
276          */
277         close(fd);
278     }
279
280     set_pname("amreport");
281
282     malloc_size_1 = malloc_inuse(&malloc_hist_1);
283
284     /* Process options */
285     
286     erroutput_type = ERR_INTERACTIVE;
287     outfname = NULL;
288     psfname = NULL;
289     logfname = NULL;
290
291     if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
292         error("cannot determine current working directory");
293     }
294
295     if (argc < 2) {
296         config_dir = stralloc2(my_cwd, "/");
297         if ((config_name = strrchr(my_cwd, '/')) != NULL) {
298             config_name = stralloc(config_name + 1);
299         }
300     } else {
301         if (argv[1][0] == '-') {
302             usage();
303             return 1;
304         }
305         config_name = stralloc(argv[1]);
306         config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
307         --argc; ++argv;
308         while((opt = getopt(argc, argv, "f:l:p:")) != EOF) {
309             switch(opt) {
310             case 'f':
311                 if (outfname != NULL) {
312                     error("you may specify at most one -f");
313                 }
314                 if (*optarg == '/') {
315                     outfname = stralloc(optarg);
316                 } else {
317                     outfname = vstralloc(my_cwd, "/", optarg, NULL);
318                 }
319                 break;
320             case 'l':
321                 if (logfname != NULL) {
322                     error("you may specify at most one -l");
323                 }
324                 if (*optarg == '/') {
325                     logfname = stralloc(optarg);
326                 } else {
327                     logfname = vstralloc(my_cwd, "/", optarg, NULL);
328                 }
329                 break;
330             case 'p':
331                 if (psfname != NULL) {
332                     error("you may specify at most one -p");
333                 }
334                 if (*optarg == '/') {
335                     psfname = stralloc(optarg);
336                 } else {
337                     psfname = vstralloc(my_cwd, "/", optarg, NULL);
338                 }
339                 break;
340             case '?':
341             default:
342                 usage();
343                 return 1;
344             }
345         }
346
347         argc -= optind;
348         argv += optind;
349
350         if (argc > 1) {
351             usage();
352             return 1;
353         }
354     }
355
356 #if !defined MAILER
357     if(!outfname) {
358         printf("You must run amreport with '-f <output file>' because configure\n");
359         printf("didn't find a mailer.\n");
360         exit (1);
361     }
362 #endif
363
364     safe_cd();
365
366     /* read configuration files */
367
368     conffile = stralloc2(config_dir, CONFFILE_NAME);
369     if(read_conffile(conffile)) {
370         error("errors processing config file \"%s\"", conffile);
371     }
372     amfree(conffile);
373     conf_diskfile = getconf_str(CNF_DISKFILE);
374     if (*conf_diskfile == '/') {
375         conf_diskfile = stralloc(conf_diskfile);
376     } else {
377         conf_diskfile = stralloc2(config_dir, conf_diskfile);
378     }
379     if((diskq = read_diskfile(conf_diskfile)) == NULL) {
380         error("could not load disklist \"%s\"", conf_diskfile);
381     }
382     amfree(conf_diskfile);
383     conf_tapelist = getconf_str(CNF_TAPELIST);
384     if (*conf_tapelist == '/') {
385         conf_tapelist = stralloc(conf_tapelist);
386     } else {
387         conf_tapelist = stralloc2(config_dir, conf_tapelist);
388     }
389     if(read_tapelist(conf_tapelist)) {
390         error("could not read tapelist \"%s\"", conf_tapelist);
391     }
392     amfree(conf_tapelist);
393     conf_infofile = getconf_str(CNF_INFOFILE);
394     if (*conf_infofile == '/') {
395         conf_infofile = stralloc(conf_infofile);
396     } else {
397         conf_infofile = stralloc2(config_dir, conf_infofile);
398     }
399     if(open_infofile(conf_infofile)) {
400         error("could not open info db \"%s\"", conf_infofile);
401     }
402     amfree(conf_infofile);
403
404     today_datestamp = construct_datestamp(NULL);
405
406     displayunit = getconf_str(CNF_DISPLAYUNIT);
407     unitdivisor = getconf_unit_divisor();
408
409     ColumnSpec = getconf_str(CNF_COLUMNSPEC);
410     if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
411         curlog = L_ERROR;
412         curprog = P_REPORTER;
413         curstr = errstr;
414         handle_error();
415         amfree(errstr);
416         curstr = NULL;
417         ColumnSpec = "";                /* use the default */
418         if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
419             curlog = L_ERROR;
420             curprog = P_REPORTER;
421             curstr = errstr;
422             handle_error();
423             amfree(errstr);
424             curstr = NULL;
425         }
426     }
427     for (cn = 0; ColumnData[cn].Name != NULL; cn++) {
428         if (ColumnData[cn].MaxWidth) {
429             MaxWidthsRequested = 1;
430             break;
431         }
432     }
433
434     if(!logfname) {
435         char *conf_logdir;
436
437         conf_logdir = getconf_str(CNF_LOGDIR);
438         if (*conf_logdir == '/') {
439             conf_logdir = stralloc(conf_logdir);
440         } else {
441             conf_logdir = stralloc2(config_dir, conf_logdir);
442         }
443         logfname = vstralloc(conf_logdir, "/", "log", NULL);
444         amfree(conf_logdir);
445     }
446
447     if((logfile = fopen(logfname, "r")) == NULL) {
448         curlog = L_ERROR;
449         curprog = P_REPORTER;
450         curstr = vstralloc("could not open log ",
451                            logfname,
452                            ": ",
453                            strerror(errno),
454                            NULL);
455         handle_error();
456         amfree(curstr);
457     }
458
459     while(logfile && get_logline(logfile)) {
460         switch(curlog) {
461         case L_START:   handle_start(); break;
462         case L_FINISH:  handle_finish(); break;
463
464         case L_INFO:    handle_note(); break;
465         case L_WARNING: handle_note(); break;
466
467         case L_SUMMARY: handle_summary(); break;
468         case L_STATS:   handle_stats(); break;
469
470         case L_ERROR:   handle_error(); break;
471         case L_FATAL:   handle_error(); break;
472
473         case L_DISK:    handle_disk(); break;
474
475         case L_SUCCESS: handle_success(); break;
476         case L_STRANGE: handle_strange(); break;
477         case L_FAIL:    handle_failed(); break;
478
479         default:
480             curlog = L_ERROR;
481             curprog = P_REPORTER;
482             curstr = stralloc2("unexpected log line: ", curstr);
483             handle_error();
484             amfree(curstr);
485         }
486     }
487     afclose(logfile);
488     close_infofile();
489     if(!amflush_run)
490         generate_missing();
491
492     subj_str = vstralloc(getconf_str(CNF_ORG),
493                          " ", amflush_run ? "AMFLUSH" : "AMANDA",
494                          " ", "MAIL REPORT FOR",
495                          " ", nicedate(run_datestamp ? atoi(run_datestamp) : 0),
496                          NULL);
497         
498     /* lookup the tapetype and printer type from the amanda.conf file. */
499     tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
500     printer = getconf_str(CNF_PRINTER);
501
502     /* ignore SIGPIPE so if a child process dies we do not also go away */
503     signal(SIGPIPE, SIG_IGN);
504
505     /* open pipe to mailer */
506
507     if(outfname) {
508         /* output to a file */
509         if((mailf = fopen(outfname,"w")) == NULL) {
510             error("could not open output file: %s %s", outfname, strerror(errno));
511         }
512         fprintf(mailf, "To: %s\n", getconf_str(CNF_MAILTO));
513         fprintf(mailf, "Subject: %s\n\n", subj_str);
514
515     } else {
516 #ifdef MAILER
517         mail_cmd = vstralloc(MAILER,
518                              " -s", " \"", subj_str, "\"",
519                              " ", getconf_str(CNF_MAILTO),
520                              NULL);
521         if((mailf = popen(mail_cmd, "w")) == NULL)
522             error("could not open pipe to \"%s\": %s",
523                   mail_cmd, strerror(errno));
524 #endif
525     }
526
527     /* open pipe to print spooler if necessary) */
528
529     if(psfname) {
530         /* if the postscript_label_template (tp->lbl_templ) field is not */
531         /* the empty string (i.e. it is set to something), open the      */
532         /* postscript debugging file for writing.                        */
533         if ((strcmp(tp->lbl_templ, "")) != 0) {
534             if ((postscript = fopen(psfname, "w")) == NULL) {
535                 curlog = L_ERROR;
536                 curprog = P_REPORTER;
537                 curstr = vstralloc("could not open ",
538                                    psfname,
539                                    ": ",
540                                    strerror(errno),
541                                    NULL);
542                 handle_error();
543                 amfree(curstr);
544             }
545         }
546     } else {
547 #ifdef LPRCMD
548         if (strcmp(printer, "") != 0)   /* alternate printer is defined */
549             /* print to the specified printer */
550 #ifdef LPRFLAG
551             printer_cmd = vstralloc(LPRCMD, " ", LPRFLAG, printer, NULL);
552 #else
553             printer_cmd = vstralloc(LPRCMD, NULL);
554 #endif
555         else
556             /* print to the default printer */
557             printer_cmd = vstralloc(LPRCMD, NULL);
558 #endif
559
560         if ((strcmp(tp->lbl_templ, "")) != 0) {
561 #ifdef LPRCMD
562             if ((postscript = popen(printer_cmd, "w")) == NULL) {
563                 curlog = L_ERROR;
564                 curprog = P_REPORTER;
565                 curstr = vstralloc("could not open pipe to ",
566                                    printer_cmd,
567                                    ": ",
568                                    strerror(errno),
569                                    NULL);
570                 handle_error();
571                 amfree(curstr);
572             }
573 #else
574             curlog = L_ERROR;
575             curprog = P_REPORTER;
576             curstr = stralloc("no printer command defined");
577             handle_error();
578             amfree(curstr);
579 #endif
580         }
581     }
582
583     amfree(subj_str);
584
585
586     if(!got_finish) fputs("*** THE DUMPS DID NOT FINISH PROPERLY!\n\n", mailf);
587
588     output_tapeinfo();
589
590     if(errsum) {
591         fprintf(mailf,"\nFAILURE AND STRANGE DUMP SUMMARY:\n");
592         output_lines(errsum, mailf);
593     }
594     fputs("\n\n", mailf);
595
596     output_stats();
597
598     if(errdet) {
599         fprintf(mailf,"\n\014\nFAILED AND STRANGE DUMP DETAILS:\n");
600         output_lines(errdet, mailf);
601     }
602     if(notes) {
603         fprintf(mailf,"\n\014\nNOTES:\n");
604         output_lines(notes, mailf);
605     }
606     sort_disks();
607     if(sortq.head != NULL) {
608         fprintf(mailf,"\n\014\nDUMP SUMMARY:\n");
609         output_summary();
610     }
611     fprintf(mailf,"\n(brought to you by Amanda version %s)\n",
612             version());
613
614     if (postscript) {
615         do_postscript_output();
616     }
617
618
619     /* close postscript file */
620     if (psfname && postscript) {
621         /* it may be that postscript is NOT opened */
622         afclose(postscript);
623     }
624     else {
625         if (postscript != NULL && pclose(postscript) != 0)
626             error("printer command failed: %s", printer_cmd);
627         postscript = NULL;
628     }
629
630     /* close output file */
631     if(outfname) {
632         afclose(mailf);
633     }
634     else {
635         if(pclose(mailf) != 0)
636             error("mail command failed: %s", mail_cmd);
637         mailf = NULL;
638     }
639
640     amfree(run_datestamp);
641     amfree(tape_labels);
642     amfree(config_dir);
643     amfree(config_name);
644     amfree(printer_cmd);
645     amfree(mail_cmd);
646     amfree(logfname);
647
648     malloc_size_2 = malloc_inuse(&malloc_hist_2);
649
650     if(malloc_size_1 != malloc_size_2) {
651         malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
652     }
653
654     return 0;
655 }
656
657 /* ----- */
658
659 #define mb(f)   ((f)/1024)              /* kbytes -> mbutes */
660 #define du(f)   ((f)/unitdivisor)       /* kbytes -> displayunit */
661 #define pct(f)  ((f)*100.0)             /* percent */
662 #define hrmn(f) ((int)(f)+30)/3600, (((int)(f)+30)%3600)/60
663 #define mnsc(f) ((int)(f+0.5))/60, ((int)(f+0.5)) % 60
664
665 #define divzero(fp,a,b)                     \
666     do {                                    \
667         double q = (b);                     \
668         if (q == 0.0)                       \
669             fprintf((fp),"  -- ");          \
670         else if ((q = (a)/q) >= 999.95)     \
671             fprintf((fp), "###.#");         \
672         else                                \
673             fprintf((fp), "%5.1f",q);       \
674     } while(0)
675 #define divzero_wide(fp,a,b)                \
676     do {                                    \
677         double q = (b);                     \
678         if (q == 0.0)                       \
679             fprintf((fp),"    -- ");        \
680         else if ((q = (a)/q) >= 99999.95)   \
681             fprintf((fp), "#####.#");       \
682         else                                \
683             fprintf((fp), "%7.1f",q);       \
684     } while(0)
685
686 void output_stats()
687 {
688     tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
689     int tapesize, marksize, lv, first;
690
691     tapesize = tp->length;
692     marksize = tp->filemark;
693
694     stats[2].dumpdisks   = stats[0].dumpdisks   + stats[1].dumpdisks;
695     stats[2].tapedisks   = stats[0].tapedisks   + stats[1].tapedisks;
696     stats[2].outsize     = stats[0].outsize     + stats[1].outsize;
697     stats[2].origsize    = stats[0].origsize    + stats[1].origsize;
698     stats[2].tapesize    = stats[0].tapesize    + stats[1].tapesize;
699     stats[2].coutsize    = stats[0].coutsize    + stats[1].coutsize;
700     stats[2].corigsize   = stats[0].corigsize   + stats[1].corigsize;
701     stats[2].taper_time  = stats[0].taper_time  + stats[1].taper_time;
702     stats[2].dumper_time = stats[0].dumper_time + stats[1].dumper_time;
703
704     if(!got_finish)     /* no driver finish line, estimate total run time */
705         total_time = stats[2].taper_time + planner_time;
706
707     fprintf(mailf,"STATISTICS:\n");
708     fprintf(mailf,
709             "                          Total       Full      Incr.\n");
710     fprintf(mailf,
711             "                        --------   --------   --------\n");
712
713     fprintf(mailf,
714             "Estimate Time (hrs:min)   %2d:%02d\n", hrmn(planner_time));
715
716     fprintf(mailf,
717             "Run Time (hrs:min)        %2d:%02d\n", hrmn(total_time));
718
719     fprintf(mailf,
720             "Dump Time (hrs:min)       %2d:%02d      %2d:%02d      %2d:%02d\n",
721             hrmn(stats[2].dumper_time), hrmn(stats[0].dumper_time),
722             hrmn(stats[1].dumper_time));
723
724     fprintf(mailf,
725             "Output Size (meg)      %8.1f   %8.1f   %8.1f\n",
726             mb(stats[2].outsize), mb(stats[0].outsize), mb(stats[1].outsize));
727
728     fprintf(mailf,
729             "Original Size (meg)    %8.1f   %8.1f   %8.1f\n",
730             mb(stats[2].origsize), mb(stats[0].origsize),
731             mb(stats[1].origsize));
732
733     fprintf(mailf, "Avg Compressed Size (%%)   ");
734     divzero(mailf, pct(stats[2].coutsize),stats[2].corigsize);
735     fputs("      ", mailf);
736     divzero(mailf, pct(stats[0].coutsize),stats[0].corigsize);
737     fputs("      ", mailf);
738     divzero(mailf, pct(stats[1].coutsize),stats[1].corigsize);
739
740     if(stats[1].dumpdisks > 0) fputs("   (level:#disks ...)", mailf);
741     putc('\n', mailf);
742
743     fprintf(mailf,
744             "Filesystems Dumped         %4d       %4d       %4d",
745             stats[2].dumpdisks, stats[0].dumpdisks, stats[1].dumpdisks);
746
747     if(stats[1].dumpdisks > 0) {
748         first = 1;
749         for(lv = 1; lv < 10; lv++) if(dumpdisks[lv]) {
750             fputs(first?"   (":" ", mailf);
751             first = 0;
752             fprintf(mailf, "%d:%d", lv, dumpdisks[lv]);
753         }
754         putc(')', mailf);
755     }
756     putc('\n', mailf);
757
758     fprintf(mailf, "Avg Dump Rate (k/s)     ");
759     divzero_wide(mailf, stats[2].outsize,stats[2].dumper_time);
760     fputs("    ", mailf);
761     divzero_wide(mailf, stats[0].outsize,stats[0].dumper_time);
762     fputs("    ", mailf);
763     divzero_wide(mailf, stats[1].outsize,stats[1].dumper_time);
764     putc('\n', mailf);
765
766     putc('\n', mailf);
767     fprintf(mailf,
768             "Tape Time (hrs:min)       %2d:%02d      %2d:%02d      %2d:%02d\n",
769             hrmn(stats[2].taper_time), hrmn(stats[0].taper_time),
770             hrmn(stats[1].taper_time));
771
772     fprintf(mailf,
773             "Tape Size (meg)        %8.1f   %8.1f   %8.1f\n",
774             mb(stats[2].tapesize), mb(stats[0].tapesize),
775             mb(stats[1].tapesize));
776
777     fprintf(mailf, "Tape Used (%%)             ");
778     divzero(mailf, pct(stats[2].tapesize+marksize*stats[2].tapedisks),tapesize);
779     fputs("      ", mailf);
780     divzero(mailf, pct(stats[0].tapesize+marksize*stats[0].tapedisks),tapesize);
781     fputs("      ", mailf);
782     divzero(mailf, pct(stats[1].tapesize+marksize*stats[1].tapedisks),tapesize);
783
784     if(stats[1].tapedisks > 0) fputs("   (level:#disks ...)", mailf);
785     putc('\n', mailf);
786
787     fprintf(mailf,
788             "Filesystems Taped          %4d       %4d       %4d",
789             stats[2].tapedisks, stats[0].tapedisks, stats[1].tapedisks);
790
791     if(stats[1].tapedisks > 0) {
792         first = 1;
793         for(lv = 1; lv < 10; lv++) if(tapedisks[lv]) {
794             fputs(first?"   (":" ", mailf);
795             first = 0;
796             fprintf(mailf, "%d:%d", lv, tapedisks[lv]);
797         }
798         putc(')', mailf);
799     }
800     putc('\n', mailf);
801
802     fprintf(mailf, "Avg Tp Write Rate (k/s) ");
803     divzero_wide(mailf, stats[2].tapesize,stats[2].taper_time);
804     fputs("    ", mailf);
805     divzero_wide(mailf, stats[0].tapesize,stats[0].taper_time);
806     fputs("    ", mailf);
807     divzero_wide(mailf, stats[1].tapesize,stats[1].taper_time);
808     putc('\n', mailf);
809
810     if(stats_by_tape) {
811         int label_length = strlen(stats_by_tape->label) + 5;
812         fprintf(mailf,"\nUSAGE BY TAPE:\n");
813         fprintf(mailf,"  %-*s  Time      Size      %%    Nb\n",
814                 label_length, "Label");
815         for(current_tape = stats_by_tape; current_tape != NULL;
816             current_tape = current_tape->next) {
817             fprintf(mailf, "  %-*s", label_length, current_tape->label);
818             fprintf(mailf, " %2d:%02d", hrmn(current_tape->taper_time));
819             fprintf(mailf, " %8.0f%s  ", du(current_tape->coutsize), displayunit);
820             divzero(mailf, pct(current_tape->coutsize + 
821                                marksize * current_tape->tapedisks),
822                            tapesize);
823             fprintf(mailf, "  %4d\n", current_tape->tapedisks);
824         }
825     }
826
827 }
828
829 /* ----- */
830
831 void output_tapeinfo()
832 {
833     tape_t *tp, *lasttp;
834     int run_tapes;
835     int skip = 0;
836
837     if (last_run_tapes > 0) {
838         if(amflush_run)
839             fprintf(mailf, "The dumps were flushed to tape%s %s.\n",
840                     last_run_tapes == 1 ? "" : "s",
841                     tape_labels ? tape_labels : "");
842         else
843             fprintf(mailf, "These dumps were to tape%s %s.\n",
844                     last_run_tapes == 1 ? "" : "s",
845                     tape_labels ? tape_labels : "");
846     }
847
848     if(degraded_mode) {
849         fprintf(mailf,
850                 "*** A TAPE ERROR OCCURRED: %s.\n", tapestart_error);
851         fputs("Some dumps may have been left in the holding disk.\n", mailf);
852         fprintf(mailf,
853                 "Run amflush%s to flush them to tape.\n",
854                 amflush_run ? " again" : "");
855     }
856
857     tp = lookup_last_reusable_tape(skip);
858
859     run_tapes = getconf_int(CNF_RUNTAPES);
860
861     if (run_tapes <= 1)
862         fputs("The next tape Amanda expects to use is: ", mailf);
863     else
864         fprintf(mailf, "The next %d tapes Amanda expects to used are: ",
865                 run_tapes);
866     
867     while(run_tapes > 0) {
868         if(tp != NULL)
869             fprintf(mailf, "%s", tp->label);
870         else
871             fputs("a new tape", mailf);
872
873         if(run_tapes > 1) fputs(", ", mailf);
874
875         run_tapes -= 1;
876         skip++;
877         tp = lookup_last_reusable_tape(skip);
878     }
879     fputs(".\n", mailf);
880
881     lasttp = lookup_tapepos(lookup_nb_tape());
882     run_tapes = getconf_int(CNF_RUNTAPES);
883     if(lasttp && run_tapes > 0 && lasttp->datestamp == 0) {
884         int c = 0;
885         while(lasttp && run_tapes > 0 && lasttp->datestamp == 0) {
886             c++;
887             lasttp = lasttp->prev;
888             run_tapes--;
889         }
890         lasttp = lookup_tapepos(lookup_nb_tape());
891         if(c == 1) {
892             fprintf(mailf, "The next new tape already labelled is: %s.\n",
893                     lasttp->label);
894         }
895         else {
896             fprintf(mailf, "The next %d new tapes already labelled are: %s", c,
897                     lasttp->label);
898             lasttp = lasttp->prev;
899             c--;
900             while(lasttp && c > 0 && lasttp->datestamp == 0) {
901                 fprintf(mailf, ", %s", lasttp->label);
902                 lasttp = lasttp->prev;
903                 c--;
904             }
905             fprintf(mailf, ".\n");
906         }
907     }
908 }
909
910 /* ----- */
911
912 void output_lines(lp, f)
913 line_t *lp;
914 FILE *f;
915 {
916     line_t *next;
917
918     while(lp) {
919         fputs(lp->str, f);
920         amfree(lp->str);
921         fputc('\n', f);
922         next = lp->next;
923         amfree(lp);
924         lp = next;
925     }
926 }
927
928 /* ----- */
929
930 int sort_by_time(a, b)
931 disk_t *a, *b;
932 {
933     return data(b)->dumper.sec - data(a)->dumper.sec;
934 }
935
936 int sort_by_name(a, b)
937 disk_t *a, *b;
938 {
939     int rc;
940
941     rc = strcmp(a->host->hostname, b->host->hostname);
942     if(rc == 0) rc = strcmp(a->name, b->name);
943     return rc;
944 }
945
946 void sort_disks()
947 {
948     disk_t *dp;
949
950     sortq.head = sortq.tail = NULL;
951     while(!empty(*diskq)) {
952         dp = dequeue_disk(diskq);
953         if(data(dp) == NULL) { /* create one */
954             find_repdata(dp, run_datestamp, 0);
955         }
956         insert_disk(&sortq, dp, sort_by_name);
957     }
958 }
959
960 void CheckStringMax(ColumnInfo *cd, char *s) {
961     if (cd->MaxWidth) {
962         int l= strlen(s);
963         if (cd->Width < l)
964             cd->Width= l;
965     }
966 }
967
968 void CheckIntMax(ColumnInfo *cd, int n) {
969     if (cd->MaxWidth) {
970         char testBuf[200];
971         int l;
972         ap_snprintf(testBuf, sizeof(testBuf),
973           cd->Format, cd->Width, cd->Precision, n);
974         l= strlen(testBuf);
975         if (cd->Width < l)
976             cd->Width= l;
977     }
978 }
979
980 void CheckFloatMax(ColumnInfo *cd, double d) {
981     if (cd->MaxWidth) {
982         char testBuf[200];
983         int l;
984         ap_snprintf(testBuf, sizeof(testBuf),
985           cd->Format, cd->Width, cd->Precision, d);
986         l= strlen(testBuf);
987         if (cd->Width < l)
988             cd->Width= l;
989     }
990 }
991
992 static int HostName;
993 static int Disk;
994 static int Level;
995 static int OrigKB;
996 static int OutKB;
997 static int Compress;
998 static int DumpTime;
999 static int DumpRate;
1000 static int TapeTime;
1001 static int TapeRate;
1002
1003 void CalcMaxWidth() {
1004     /* we have to look for columspec's, that require the recalculation.
1005      * we do here the same loops over the sortq as is done in
1006      * output_summary. So, if anything is changed there, we have to
1007      * change this here also.
1008      *                                                  ElB, 1999-02-24.
1009      */
1010     disk_t *dp;
1011     float f;
1012     repdata_t *repdata;
1013     for(dp = sortq.head; dp != NULL; dp = dp->next) {
1014       if(dp->todo) {
1015         for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1016             ColumnInfo *cd;
1017             char TimeRateBuffer[40];
1018
1019             CheckStringMax(&ColumnData[HostName], dp->host->hostname);
1020             CheckStringMax(&ColumnData[Disk], dp->name);
1021             if (repdata->dumper.result == L_BOGUS && 
1022                 repdata->taper.result == L_BOGUS)
1023                 continue;
1024             CheckIntMax(&ColumnData[Level], repdata->level);
1025             if(repdata->dumper.result == L_SUCCESS) {
1026                 CheckFloatMax(&ColumnData[OrigKB], du(repdata->dumper.origsize));
1027                 CheckFloatMax(&ColumnData[OutKB], du(repdata->dumper.outsize));
1028                 if(dp->compress == COMP_NONE)
1029                     f = 0.0;
1030                 else 
1031                     f = repdata->dumper.origsize;
1032                 CheckStringMax(&ColumnData[Disk], 
1033                         sDivZero(pct(repdata->dumper.outsize), f, Compress));
1034
1035                 if(!amflush_run)
1036                     ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1037                                 "%3d:%02d", mnsc(repdata->dumper.sec));
1038                 else
1039                     ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1040                                 "N/A ");
1041                 CheckStringMax(&ColumnData[DumpTime], TimeRateBuffer);
1042
1043                 CheckFloatMax(&ColumnData[DumpRate], repdata->dumper.kps); 
1044             }
1045
1046             cd= &ColumnData[TapeTime];
1047             if(repdata->taper.result == L_FAIL) {
1048                 CheckStringMax(cd, "FAILED");
1049                 continue;
1050             }
1051             if(repdata->taper.result == L_SUCCESS)
1052                 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer), 
1053                   "%3d:%02d", mnsc(repdata->taper.sec));
1054             else
1055                 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1056                   "N/A ");
1057             CheckStringMax(cd, TimeRateBuffer);
1058
1059             cd= &ColumnData[TapeRate];
1060             if(repdata->taper.result == L_SUCCESS)
1061                 CheckFloatMax(cd, repdata->taper.kps);
1062             else
1063                 CheckStringMax(cd, "N/A ");
1064         }
1065       }
1066     }
1067 }
1068
1069 void output_summary()
1070 {
1071     disk_t *dp;
1072     repdata_t *repdata;
1073     char *ds="DUMPER STATS";
1074     char *ts=" TAPER STATS";
1075     char *tmp;
1076
1077     int i, h, w1, wDump, wTape;
1078     float outsize, origsize;
1079     float f;
1080
1081     HostName = StringToColumn("HostName");
1082     Disk = StringToColumn("Disk");
1083     Level = StringToColumn("Level");
1084     OrigKB = StringToColumn("OrigKB");
1085     OutKB = StringToColumn("OutKB");
1086     Compress = StringToColumn("Compress");
1087     DumpTime = StringToColumn("DumpTime");
1088     DumpRate = StringToColumn("DumpRate");
1089     TapeTime = StringToColumn("TapeTime");
1090     TapeRate = StringToColumn("TapeRate");
1091
1092     /* at first determine if we have to recalculate our widths */
1093     if (MaxWidthsRequested)
1094         CalcMaxWidth();
1095
1096     /* title for Dumper-Stats */
1097     w1= ColWidth(HostName, Level);
1098     wDump= ColWidth(OrigKB, DumpRate);
1099     wTape= ColWidth(TapeTime, TapeRate);
1100
1101     /* print centered top titles */
1102     h= strlen(ds);
1103     if (h > wDump) {
1104         h= 0;
1105     } else {
1106         h= (wDump-h)/2;
1107     }
1108     fprintf(mailf, "%*s", w1+h, "");
1109     fprintf(mailf, "%-*s", wDump-h, ds);
1110     h= strlen(ts);
1111     if (h > wTape) {
1112         h= 0;
1113     } else {
1114         h= (wTape-h)/2;
1115     }
1116     fprintf(mailf, "%*s", h, "");
1117     fprintf(mailf, "%-*s", wTape-h, ts);
1118     fputc('\n', mailf);
1119
1120     /* print the titles */
1121     for (i=0; ColumnData[i].Name != NULL; i++) {
1122         char *fmt;
1123         ColumnInfo *cd= &ColumnData[i];
1124         fprintf(mailf, "%*s", cd->PrefixSpace, "");
1125         if (cd->Format[1] == '-')
1126             fmt= "%-*s";
1127         else
1128             fmt= "%*s";
1129         if(strcmp(cd->Title,"ORIG-KB") == 0) {
1130             /* cd->Title must be re-allocated in write-memory */
1131             cd->Title = stralloc("ORIG-KB");
1132             cd->Title[5] = displayunit[0];
1133         }
1134         if(strcmp(cd->Title,"OUT-KB") == 0) {
1135             /* cd->Title must be re-allocated in write-memory */
1136             cd->Title = stralloc("OUT-KB");
1137             cd->Title[4] = displayunit[0];
1138         }
1139         fprintf(mailf, fmt, cd->Width, cd->Title);
1140     }
1141     fputc('\n', mailf);
1142
1143     /* print the rules */
1144     fputs(tmp=Rule(HostName, Level), mailf); amfree(tmp);
1145     fputs(tmp=Rule(OrigKB, DumpRate), mailf); amfree(tmp);
1146     fputs(tmp=Rule(TapeTime, TapeRate), mailf); amfree(tmp);
1147     fputc('\n', mailf);
1148
1149     for(dp = sortq.head; dp != NULL; dp = dp->next) {
1150       if(dp->todo) {
1151         ColumnInfo *cd;
1152         char TimeRateBuffer[40];
1153         for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1154             int devlen;
1155
1156             cd= &ColumnData[HostName];
1157             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1158             fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->host->hostname);
1159
1160             cd= &ColumnData[Disk];
1161             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1162             devlen= strlen(dp->name);
1163             if (devlen > cd->Width) {
1164                 fputc('-', mailf); 
1165                 fprintf(mailf, cd->Format, cd->Width-1, cd->Precision-1,
1166                   dp->name+devlen - (cd->Width-1) );
1167             }
1168             else
1169                 fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->name);
1170
1171             cd= &ColumnData[Level];
1172             if (repdata->dumper.result == L_BOGUS &&
1173                 repdata->taper.result  == L_BOGUS) {
1174               if(amflush_run){
1175                 fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1176                         tmp=TextRule(OrigKB, TapeRate, "NO FILE TO FLUSH"));
1177               } else {
1178                 fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1179                         tmp=TextRule(OrigKB, TapeRate, "MISSING"));
1180               }
1181               amfree(tmp);
1182               continue;
1183             }
1184             
1185             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1186             fprintf(mailf, cd->Format, cd->Width, cd->Precision,repdata->level);
1187
1188             if (repdata->dumper.result == L_SKIPPED) {
1189                 fprintf(mailf, "%s\n",
1190                         tmp=TextRule(OrigKB, TapeRate, "SKIPPED"));
1191                 amfree(tmp);
1192                 continue;
1193             }
1194             if (repdata->dumper.result == L_FAIL) {
1195                 fprintf(mailf, "%s\n",
1196                         tmp=TextRule(OrigKB, TapeRate, "FAILED"));
1197                 amfree(tmp);
1198                 continue;
1199             }
1200
1201             if(repdata->dumper.result == L_SUCCESS)
1202                 origsize = repdata->dumper.origsize;
1203             else
1204                 origsize = repdata->taper.origsize;
1205
1206             if(repdata->taper.result == L_SUCCESS) 
1207                 outsize  = repdata->taper.outsize;
1208             else
1209                 outsize  = repdata->dumper.outsize;
1210
1211             cd= &ColumnData[OrigKB];
1212             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1213             if(origsize != 0.0)
1214                 fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(origsize));
1215             else
1216                 fprintf(mailf, "%*.*s", cd->Width, cd->Width, "N/A");
1217
1218             cd= &ColumnData[OutKB];
1219             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1220
1221             fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(outsize));
1222                 
1223             cd= &ColumnData[Compress];
1224             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1225
1226             if(dp->compress == COMP_NONE)
1227                 f = 0.0;
1228             else if(origsize < 1.0)
1229                 f = 0.0;
1230             else
1231                 f = origsize;
1232
1233             fputs(sDivZero(pct(outsize), f, Compress), mailf);
1234
1235             cd= &ColumnData[DumpTime];
1236             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1237             if(repdata->dumper.result == L_SUCCESS)
1238                 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1239                   "%3d:%02d", mnsc(repdata->dumper.sec));
1240             else
1241                 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1242                   "N/A ");
1243             fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1244
1245             cd= &ColumnData[DumpRate];
1246             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1247             if(repdata->dumper.result == L_SUCCESS)
1248                 fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->dumper.kps);
1249             else
1250                 fprintf(mailf, "%*s", cd->Width, "N/A ");
1251
1252             cd= &ColumnData[TapeTime];
1253             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1254             if(repdata->taper.result == L_FAIL) {
1255                 fprintf(mailf, "%s\n",
1256                         tmp=TextRule(TapeTime, TapeRate, "FAILED "));
1257                 amfree(tmp);
1258                 continue;
1259             }
1260
1261             if(repdata->taper.result == L_SUCCESS)
1262                 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1263                   "%3d:%02d", mnsc(repdata->taper.sec));
1264             else
1265                 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1266                   "N/A ");
1267             fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1268
1269             cd= &ColumnData[TapeRate];
1270             fprintf(mailf, "%*s", cd->PrefixSpace, "");
1271             if(repdata->taper.result == L_SUCCESS)
1272                 fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
1273             else
1274                 fprintf(mailf, "%*s", cd->Width, "N/A ");
1275             fputc('\n', mailf);
1276         }
1277       }
1278     }
1279 }
1280
1281 void bogus_line()
1282 {
1283     printf("line %d of log is bogus\n", curlinenum);
1284 }
1285
1286
1287 char *nicedate(datestamp)
1288 int datestamp;
1289 /*
1290  * Formats an integer of the form YYYYMMDD into the string
1291  * "Monthname DD, YYYY".  A pointer to the statically allocated string
1292  * is returned, so it must be copied to other storage (or just printed)
1293  * before calling nicedate() again.
1294  */
1295 {
1296     static char nice[64];
1297     static char *months[13] = { "BogusMonth",
1298         "January", "February", "March", "April", "May", "June",
1299         "July", "August", "September", "October", "November", "December"
1300     };
1301     int year, month, day;
1302
1303     year  = datestamp / 10000;
1304     day   = datestamp % 100;
1305     month = (datestamp / 100) % 100;
1306
1307     ap_snprintf(nice, sizeof(nice), "%s %d, %d", months[month], day, year);
1308
1309     return nice;
1310 }
1311
1312 void handle_start()
1313 {
1314     static int started = 0;
1315     char *label;
1316     char *s, *fp;
1317     int ch;
1318
1319     switch(curprog) {
1320     case P_TAPER:
1321         s = curstr;
1322         ch = *s++;
1323
1324         skip_whitespace(s, ch);
1325 #define sc "datestamp"
1326         if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1327             bogus_line();
1328             return;
1329         }
1330         s += sizeof(sc)-1;
1331         ch = s[-1];
1332 #undef sc
1333         skip_whitespace(s, ch);
1334         if(ch == '\0') {
1335             bogus_line();
1336             return;
1337         }
1338         fp = s - 1;
1339         skip_non_whitespace(s, ch);
1340         s[-1] = '\0';
1341         run_datestamp = newstralloc(run_datestamp, fp);
1342         s[-1] = ch;
1343
1344         skip_whitespace(s, ch);
1345 #define sc "label"
1346         if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1347             bogus_line();
1348             return;
1349         }
1350         s += sizeof(sc)-1;
1351         ch = s[-1];
1352 #undef sc
1353         skip_whitespace(s, ch);
1354         if(ch == '\0') {
1355             bogus_line();
1356             return;
1357         }
1358         fp = s - 1;
1359         skip_non_whitespace(s, ch);
1360         s[-1] = '\0';
1361
1362         label = stralloc(fp);
1363         s[-1] = ch;
1364
1365         if(tape_labels) {
1366             fp = vstralloc(tape_labels, ", ", label, NULL);
1367             amfree(tape_labels);
1368             tape_labels = fp;
1369         } else {
1370             tape_labels = stralloc(label);
1371         }
1372
1373         last_run_tapes++;
1374
1375         if(stats_by_tape == NULL) {
1376             stats_by_tape = current_tape = (taper_t *)alloc(sizeof(taper_t));
1377         }
1378         else {
1379             current_tape->next = (taper_t *)alloc(sizeof(taper_t));
1380             current_tape = current_tape->next;
1381         }
1382         current_tape->label = label;
1383         current_tape->taper_time = 0.0;
1384         current_tape->coutsize = 0.0;
1385         current_tape->corigsize = 0.0;
1386         current_tape->tapedisks = 0;
1387         current_tape->next = NULL;
1388         tapefcount = 0;
1389
1390         return;
1391     case P_PLANNER:
1392         normal_run = 1;
1393         break;
1394     case P_DRIVER:
1395         break;
1396     case P_AMFLUSH:
1397         amflush_run = 1;
1398         break;
1399     default:
1400         ;
1401     }
1402
1403     if(!started) {
1404         s = curstr;
1405         ch = *s++;
1406
1407         skip_whitespace(s, ch);
1408 #define sc "date"
1409         if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1410             return;                             /* ignore bogus line */
1411         }
1412         s += sizeof(sc)-1;
1413         ch = s[-1];
1414 #undef sc
1415         skip_whitespace(s, ch);
1416         if(ch == '\0') {
1417             bogus_line();
1418             return;
1419         }
1420         fp = s - 1;
1421         skip_non_whitespace(s, ch);
1422         s[-1] = '\0';
1423         run_datestamp = newstralloc(run_datestamp, fp);
1424         s[-1] = ch;
1425
1426         started = 1;
1427     }
1428     if(amflush_run && normal_run) {
1429         amflush_run = 0;
1430         addline(&notes,
1431      "  reporter: both amflush and planner output in log, ignoring amflush.");
1432     }
1433 }
1434
1435
1436 void handle_finish()
1437 {
1438     char *s;
1439     int ch;
1440     float a_time;
1441
1442     if(curprog == P_DRIVER || curprog == P_AMFLUSH || curprog == P_PLANNER) {
1443         s = curstr;
1444         ch = *s++;
1445
1446         skip_whitespace(s, ch);
1447 #define sc "date"
1448         if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1449             bogus_line();
1450             return;
1451         }
1452         s += sizeof(sc)-1;
1453         ch = s[-1];
1454 #undef sc
1455
1456         skip_whitespace(s, ch);
1457         if(ch == '\0') {
1458             bogus_line();
1459             return;
1460         }
1461         skip_non_whitespace(s, ch);     /* ignore the date string */
1462
1463         skip_whitespace(s, ch);
1464 #define sc "time"
1465         if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1466             /* older planner doesn't write time */
1467             if(curprog == P_PLANNER) return;
1468             bogus_line();
1469             return;
1470         }
1471         s += sizeof(sc)-1;
1472         ch = s[-1];
1473 #undef sc
1474
1475         skip_whitespace(s, ch);
1476         if(ch == '\0') {
1477             bogus_line();
1478             return;
1479         }
1480         if(sscanf(s - 1, "%f", &a_time) != 1) {
1481             bogus_line();
1482             return;
1483         }
1484         if(curprog == P_PLANNER) {
1485             planner_time = a_time;
1486         }
1487         else {
1488             total_time = a_time;
1489             got_finish = 1;
1490         }
1491     }
1492 }
1493
1494 void handle_stats()
1495 {
1496     char *s;
1497     int ch;
1498
1499     if(curprog == P_DRIVER) {
1500         s = curstr;
1501         ch = *s++;
1502
1503         skip_whitespace(s, ch);
1504 #define sc "startup time"
1505         if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1506             bogus_line();
1507             return;
1508         }
1509         s += sizeof(sc)-1;
1510         ch = s[-1];
1511 #undef sc
1512
1513         skip_whitespace(s, ch);
1514         if(ch == '\0') {
1515             bogus_line();
1516             return;
1517         }
1518         if(sscanf(s - 1, "%f", &startup_time) != 1) {
1519             bogus_line();
1520             return;
1521         }
1522         planner_time = startup_time;
1523     }
1524 }
1525
1526
1527 void handle_note()
1528 {
1529     char *str = NULL;
1530
1531     str = vstralloc("  ", program_str[curprog], ": ", curstr, NULL);
1532     addline(&notes, str);
1533     amfree(str);
1534 }
1535
1536
1537 /* ----- */
1538
1539 void handle_error()
1540 {
1541     char *s = NULL, *nl;
1542     int ch;
1543
1544     if(curlog == L_ERROR && curprog == P_TAPER) {
1545         s = curstr;
1546         ch = *s++;
1547
1548         skip_whitespace(s, ch);
1549 #define sc "no-tape"
1550         if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1551             bogus_line();
1552             return;
1553         }
1554         s += sizeof(sc)-1;
1555         ch = s[-1];
1556 #undef sc
1557
1558         skip_whitespace(s, ch);
1559         if(ch != '\0') {
1560             if((nl = strchr(s - 1, '\n')) != NULL) {
1561                 *nl = '\0';
1562             }
1563             tapestart_error = newstralloc(tapestart_error, s - 1);
1564             if(nl) *nl = '\n';
1565             degraded_mode = 1;
1566             return;
1567         }
1568         /* else some other tape error, handle like other errors */
1569     }
1570     s = vstralloc("  ", program_str[curprog], ": ",
1571                   logtype_str[curlog], " ", curstr, NULL);
1572     addline(&errsum, s);
1573     amfree(s);
1574 }
1575
1576 /* ----- */
1577
1578 void handle_summary()
1579 {
1580     bogus_line();
1581 }
1582
1583 /* ----- */
1584
1585 int nb_disk=0;
1586 void handle_disk()
1587 {
1588     disk_t *dp;
1589     char *s, *fp;
1590     int ch;
1591     char *hostname = NULL, *diskname = NULL;
1592
1593     if(curprog != P_PLANNER && curprog != P_AMFLUSH) {
1594         bogus_line();
1595         return;
1596     }
1597
1598     if(nb_disk==0) {
1599         for(dp = diskq->head; dp != NULL; dp = dp->next)
1600             dp->todo = 0;
1601     }
1602     nb_disk++;
1603
1604     s = curstr;
1605     ch = *s++;
1606
1607     skip_whitespace(s, ch);
1608     if(ch == '\0') {
1609         bogus_line();
1610         return;
1611     }
1612     fp = s - 1;
1613     skip_non_whitespace(s, ch);
1614     s[-1] = '\0';
1615     hostname = newstralloc(hostname, fp);
1616     s[-1] = ch;
1617
1618     skip_whitespace(s, ch);
1619     if(ch == '\0') {
1620         bogus_line();
1621         return;
1622     }
1623     fp = s - 1;
1624     skip_non_whitespace(s, ch);
1625     s[-1] = '\0';
1626     diskname = newstralloc(diskname, fp);
1627     s[-1] = ch;
1628
1629     dp = lookup_disk(hostname, diskname);
1630     if(dp == NULL) {
1631         dp = add_disk(hostname, diskname);
1632     }
1633
1634     amfree(hostname);
1635     amfree(diskname);
1636     dp->todo = 1;
1637 }
1638
1639 repdata_t *handle_success()
1640 {
1641     disk_t *dp;
1642     float sec, kps, kbytes, origkb;
1643     timedata_t *sp;
1644     int i;
1645     char *s, *fp;
1646     int ch;
1647     char *hostname = NULL;
1648     char *diskname = NULL;
1649     repdata_t *repdata;
1650     int level;
1651     char *datestamp;
1652
1653     if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER) {
1654         bogus_line();
1655         return NULL;
1656     }
1657
1658     s = curstr;
1659     ch = *s++;
1660
1661     skip_whitespace(s, ch);
1662     if(ch == '\0') {
1663         bogus_line();
1664         return NULL;
1665     }
1666     fp = s - 1;
1667     skip_non_whitespace(s, ch);
1668     s[-1] = '\0';
1669     hostname = stralloc(fp);
1670     s[-1] = ch;
1671
1672     skip_whitespace(s, ch);
1673     if(ch == '\0') {
1674         bogus_line();
1675         amfree(hostname);
1676         return NULL;
1677     }
1678     fp = s - 1;
1679     skip_non_whitespace(s, ch);
1680     s[-1] = '\0';
1681     diskname = stralloc(fp);
1682     s[-1] = ch;
1683
1684     skip_whitespace(s, ch);
1685     if(ch == '\0') {
1686         bogus_line();
1687         amfree(hostname);
1688         amfree(diskname);
1689         return NULL;
1690     }
1691     fp = s - 1;
1692     skip_non_whitespace(s, ch);
1693     s[-1] = '\0';
1694     datestamp = stralloc(fp);
1695     s[-1] = ch;
1696
1697     level = atoi(datestamp);
1698     if(level < 100)  {
1699         datestamp = newstralloc(datestamp, run_datestamp);
1700     }
1701     else {
1702         skip_whitespace(s, ch);
1703         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1704             bogus_line();
1705             amfree(hostname);
1706             amfree(diskname);
1707             amfree(datestamp);
1708             return NULL;
1709         }
1710         skip_integer(s, ch);
1711     }
1712
1713     skip_whitespace(s, ch);
1714                                 /* Planner success messages (for skipped
1715                                    dumps) do not contain statistics */
1716     if(curprog != P_PLANNER) {
1717         if(curprog != P_DUMPER ||
1718            sscanf(s - 1,"[sec %f kb %f kps %f orig-kb %f", 
1719                   &sec, &kbytes, &kps, &origkb) != 4)  {
1720             origkb = -1;
1721             if(sscanf(s - 1,"[sec %f kb %f kps %f",
1722                       &sec, &kbytes, &kps) != 3) {
1723                 bogus_line();
1724                 amfree(hostname);
1725                 amfree(diskname);
1726                 amfree(datestamp);
1727                 return NULL;
1728             }
1729         }
1730         else {
1731             if(origkb == 0.0) origkb = 0.1;
1732         }
1733     }
1734
1735
1736     dp = lookup_disk(hostname, diskname);
1737     if(dp == NULL) {
1738         char *str = NULL;
1739
1740         str = vstralloc("  ", prefix(hostname, diskname, level),
1741                         " ", "ERROR [not in disklist]",
1742                         NULL);
1743         addline(&errsum, str);
1744         amfree(str);
1745         amfree(hostname);
1746         amfree(diskname);
1747         amfree(datestamp);
1748         return NULL;
1749     }
1750
1751     repdata = find_repdata(dp, datestamp, level);
1752
1753     if(curprog == P_PLANNER) {
1754         repdata->dumper.result = L_SKIPPED;
1755         amfree(hostname);
1756         amfree(diskname);
1757         amfree(datestamp);
1758         return repdata;
1759     }
1760
1761     if(curprog == P_TAPER)
1762         sp = &(repdata->taper);
1763     else sp = &(repdata->dumper);
1764
1765     i = level > 0;
1766
1767     if(origkb == -1) {
1768         info_t inf;
1769         struct tm *tm;
1770         int Idatestamp;
1771
1772         get_info(hostname, diskname, &inf);
1773         tm = localtime(&inf.inf[level].date);
1774         Idatestamp = 10000*(tm->tm_year+1900) +
1775                       100*(tm->tm_mon+1) + tm->tm_mday;
1776
1777         if(atoi(datestamp) == Idatestamp) {
1778             /* grab original size from record */
1779             origkb = (double)inf.inf[level].size;
1780         }
1781         else
1782             origkb = 0.0;
1783     }
1784     amfree(hostname);
1785     amfree(diskname);
1786     amfree(datestamp);
1787
1788     sp->result = L_SUCCESS;
1789     sp->datestamp = repdata->datestamp;
1790     sp->sec = sec;
1791     sp->kps = kps;
1792     sp->origsize = origkb;
1793     sp->outsize = kbytes;
1794
1795     if(curprog == P_TAPER) {
1796         if(current_tape == NULL) {
1797             error("current_tape == NULL");
1798         }
1799         stats[i].taper_time += sec;
1800         sp->filenum = ++tapefcount;
1801         sp->tapelabel = current_tape->label;
1802         tapedisks[level] +=1;
1803         stats[i].tapedisks +=1;
1804         stats[i].tapesize += kbytes;
1805         current_tape->taper_time += sec;
1806         current_tape->coutsize += kbytes;
1807         current_tape->corigsize += origkb;
1808         current_tape->tapedisks += 1;
1809     }
1810
1811     if(curprog == P_DUMPER) {
1812         stats[i].dumper_time += sec;
1813         if(dp->compress == COMP_NONE) {
1814             sp->origsize = kbytes;
1815         }
1816         else {
1817             stats[i].coutsize += kbytes;
1818             stats[i].corigsize += sp->origsize;
1819         }
1820         dumpdisks[level] +=1;
1821         stats[i].dumpdisks +=1;
1822         stats[i].origsize += sp->origsize;
1823         stats[i].outsize += kbytes;
1824     }
1825
1826     return repdata;
1827 }
1828
1829 void handle_strange()
1830 {
1831     char *str = NULL;
1832     char *strangestr = NULL;
1833     repdata_t *repdata;
1834
1835     repdata = handle_success();
1836
1837     addline(&errdet,"");
1838     str = vstralloc("/-- ", prefix(repdata->disk->host->hostname, 
1839                                    repdata->disk->name, repdata->level),
1840                     " ", "STRANGE",
1841                     NULL);
1842     addline(&errdet, str);
1843     amfree(str);
1844
1845     while(contline_next()) {
1846         get_logline(logfile);
1847 #define sc "sendbackup: warning "
1848         if(strncmp(curstr, sc, sizeof(sc)-1) == 0) {
1849             strangestr = newstralloc(strangestr, curstr+sizeof(sc)-1);
1850         }
1851         addline(&errdet, curstr);
1852     }
1853     addline(&errdet,"\\--------");
1854
1855     str = vstralloc("  ", prefix(repdata->disk->host->hostname, 
1856                                  repdata->disk->name, repdata->level),
1857                     " ", "STRANGE", " ", strangestr,
1858                     NULL);
1859     addline(&errsum, str);
1860     amfree(str);
1861     amfree(strangestr);
1862 }
1863
1864 void handle_failed()
1865 {
1866     disk_t *dp;
1867     char *hostname;
1868     char *diskname;
1869     char *datestamp;
1870     char *errstr;
1871     int level;
1872     char *s, *fp;
1873     int ch;
1874     char *str = NULL;
1875     repdata_t *repdata;
1876     timedata_t *sp;
1877
1878     hostname = NULL;
1879     diskname = NULL;
1880
1881     s = curstr;
1882     ch = *s++;
1883
1884     skip_whitespace(s, ch);
1885     if(ch == '\0') {
1886         bogus_line();
1887         return;
1888     }
1889     hostname = s - 1;
1890     skip_non_whitespace(s, ch);
1891     s[-1] = '\0';
1892
1893     skip_whitespace(s, ch);
1894     if(ch == '\0') {
1895         bogus_line();
1896         return;
1897     }
1898     diskname = s - 1;
1899     skip_non_whitespace(s, ch);
1900     s[-1] = '\0';
1901
1902     skip_whitespace(s, ch);
1903     if(ch == '\0') {
1904         bogus_line();
1905         return;
1906     }
1907     fp = s - 1;
1908     skip_non_whitespace(s, ch);
1909     s[-1] = '\0';
1910     datestamp = stralloc(fp);
1911
1912     if(strlen(datestamp) < 3) { /* there is no datestamp, it's the level */
1913         level = atoi(datestamp);
1914         datestamp = newstralloc(datestamp, run_datestamp);
1915     }
1916     else { /* read the level */
1917         skip_whitespace(s, ch);
1918         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1919             bogus_line();
1920             amfree(datestamp);
1921             return;
1922         }
1923         skip_integer(s, ch);
1924     }
1925
1926     skip_whitespace(s, ch);
1927     if(ch == '\0') {
1928         bogus_line();
1929         amfree(datestamp);
1930         return;
1931     }
1932     errstr = s - 1;
1933     if((s = strchr(errstr, '\n')) != NULL) {
1934         *s = '\0';
1935     }
1936
1937     dp = lookup_disk(hostname, diskname);
1938     if(dp == NULL) {
1939         str = vstralloc("  ", prefix(hostname, diskname, level),
1940                         " ", "ERROR [not in disklist]",
1941                         NULL);
1942         addline(&errsum, str);
1943         amfree(str);
1944     } else {
1945         repdata = find_repdata(dp, datestamp, level);
1946
1947         if(curprog == P_TAPER)
1948             sp = &(repdata->taper);
1949         else sp = &(repdata->dumper);
1950
1951         if(sp->result != L_SUCCESS)
1952             sp->result = L_FAIL;
1953     }
1954     amfree(datestamp);
1955
1956     str = vstralloc("  ", prefix(hostname, diskname, level),
1957                     " ", "FAILED",
1958                     " ", errstr,
1959                     NULL);
1960     addline(&errsum, str);
1961     amfree(str);
1962
1963     if(curprog == P_DUMPER) {
1964         addline(&errdet,"");
1965         str = vstralloc("/-- ", prefix(hostname, diskname, level),
1966                         " ", "FAILED",
1967                         " ", errstr,
1968                         NULL);
1969         addline(&errdet, str);
1970         amfree(str);
1971         while(contline_next()) {
1972             get_logline(logfile);
1973             addline(&errdet, curstr);
1974         }
1975         addline(&errdet,"\\--------");
1976     }
1977     return;
1978 }
1979
1980 void generate_missing()
1981 {
1982     disk_t *dp;
1983     char *str = NULL;
1984
1985     for(dp = diskq->head; dp != NULL; dp = dp->next) {
1986         if(dp->todo && data(dp) == NULL) {
1987             str = vstralloc("  ", prefix(dp->host->hostname, dp->name, -987),
1988                             " ", "RESULTS MISSING",
1989                             NULL);
1990             addline(&errsum, str);
1991             amfree(str);
1992         }
1993     }
1994 }
1995
1996 static char *
1997 prefix (host, disk, level)
1998     char *host;
1999     char *disk;
2000     int level;
2001 {
2002     char h[10+1];
2003     int l;
2004     char number[NUM_STR_SIZE];
2005     static char *str = NULL;
2006
2007     ap_snprintf(number, sizeof(number), "%d", level);
2008     if(host) {
2009         strncpy(h, host, sizeof(h)-1);
2010     } else {
2011         strncpy(h, "(host?)", sizeof(h)-1);
2012     }
2013     h[sizeof(h)-1] = '\0';
2014     for(l = strlen(h); l < sizeof(h)-1; l++) {
2015         h[l] = ' ';
2016     }
2017     str = newvstralloc(str,
2018                        h,
2019                        " ", disk ? disk : "(disk?)",
2020                        level != -987 ? " lev " : "",
2021                        level != -987 ? number : "",
2022                        NULL);
2023     return str;
2024 }
2025
2026 void copy_template_file(lbl_templ)
2027 char *lbl_templ;
2028 {
2029   char buf[BUFSIZ];
2030   int fd;
2031   int numread;
2032
2033   if (strchr(lbl_templ, '/') == NULL) {
2034     lbl_templ = stralloc2(config_dir, lbl_templ);
2035   } else {
2036     lbl_templ = stralloc(lbl_templ);
2037   }
2038   if ((fd = open(lbl_templ, 0)) < 0) {
2039     curlog = L_ERROR;
2040     curprog = P_REPORTER;
2041     curstr = vstralloc("could not open PostScript template file ",
2042                        lbl_templ,
2043                        ": ",
2044                        strerror(errno),
2045                        NULL);
2046     handle_error();
2047     amfree(curstr);
2048     afclose(postscript);
2049     return;
2050   }
2051   while ((numread = read(fd, buf, sizeof(buf))) > 0) {
2052     if (fwrite(buf, numread, 1, postscript) != 1) {
2053       curlog = L_ERROR;
2054       curprog = P_REPORTER;
2055       curstr = vstralloc("error copying PostScript template file ",
2056                          lbl_templ,
2057                          ": ",
2058                          strerror(errno),
2059                          NULL);
2060       handle_error();
2061       amfree(curstr);
2062       afclose(postscript);
2063       return;
2064     }
2065   }
2066   if (numread < 0) {
2067     curlog = L_ERROR;
2068     curprog = P_REPORTER;
2069     curstr = vstralloc("error reading PostScript template file ",
2070                        lbl_templ,
2071                        ": ",
2072                        strerror(errno),
2073                        NULL);
2074     handle_error();
2075     amfree(curstr);
2076     afclose(postscript);
2077     return;
2078   }
2079   close(fd);
2080   amfree(lbl_templ);
2081 }
2082
2083 repdata_t *find_repdata(dp, datestamp, level)
2084 disk_t *dp;
2085 char *datestamp;
2086 int level;
2087 {
2088     repdata_t *repdata, *prev;
2089
2090     if(!datestamp)
2091         datestamp = run_datestamp;
2092     prev = NULL;
2093     for(repdata = data(dp); repdata != NULL && (repdata->level != level || strcmp(repdata->datestamp,datestamp)!=0); repdata = repdata->next) {
2094         prev = repdata;
2095     }
2096     if(!repdata) {
2097         repdata = (repdata_t *)alloc(sizeof(repdata_t));
2098         memset(repdata, '\0',sizeof(repdata_t));
2099         repdata->disk = dp;
2100         repdata->datestamp = stralloc(datestamp ? datestamp : "");
2101         repdata->level = level;
2102         repdata->dumper.result = L_BOGUS;
2103         repdata->taper.result = L_BOGUS;
2104         repdata->next = NULL;
2105         if(prev)
2106             prev->next = repdata;
2107         else
2108             dp->up = (void *)repdata;
2109     }
2110     return repdata;
2111 }
2112
2113
2114 void do_postscript_output()
2115 {
2116     tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
2117     disk_t *dp;
2118     repdata_t *repdata;
2119     float outsize, origsize;
2120     int tapesize, marksize;
2121
2122     tapesize = tp->length;
2123     marksize = tp->filemark;
2124
2125     for(current_tape = stats_by_tape; current_tape != NULL;
2126             current_tape = current_tape->next) {
2127
2128         if (current_tape->label == NULL) {
2129             break;
2130         }
2131
2132         copy_template_file(tp->lbl_templ);
2133
2134         /* generate a few elements */
2135         fprintf(postscript,"(%s) DrawDate\n\n",
2136                     nicedate(run_datestamp ? atoi(run_datestamp) : 0));
2137         fprintf(postscript,"(Amanda Version %s) DrawVers\n",version());
2138         fprintf(postscript,"(%s) DrawTitle\n", current_tape->label);
2139
2140         /* Stats */
2141         fprintf(postscript, "(Total Size:        %6.1f MB) DrawStat\n",
2142               mb(current_tape->coutsize));
2143         fprintf(postscript, "(Tape Used (%%)       ");
2144         divzero(postscript, pct(current_tape->coutsize + 
2145                                 marksize * current_tape->tapedisks),
2146                                 tapesize);
2147         fprintf(postscript," %%) DrawStat\n");
2148         fprintf(postscript, "(Compression Ratio:  ");
2149         divzero(postscript, pct(current_tape->coutsize),current_tape->corigsize);
2150         fprintf(postscript," %%) DrawStat\n");
2151         fprintf(postscript,"(Filesystems Taped: %4d) DrawStat\n",
2152                   current_tape->tapedisks);
2153
2154         /* Summary */
2155
2156         fprintf(postscript,
2157               "(-) (%s) (-) (  0) (      32) (      32) DrawHost\n",
2158               current_tape->label);
2159
2160         for(dp = sortq.head; dp != NULL; dp = dp->next) {
2161             if (dp->todo == 0) {
2162                  continue;
2163             }
2164             for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
2165
2166                 if(repdata->taper.tapelabel != current_tape->label) {
2167                     continue;
2168                 }
2169
2170                 if(repdata->dumper.result == L_SUCCESS)
2171                     origsize = repdata->dumper.origsize;
2172                 else
2173                     origsize = repdata->taper.origsize;
2174
2175                 if(repdata->taper.result == L_SUCCESS) 
2176                     outsize  = repdata->taper.outsize;
2177                 else
2178                     outsize  = repdata->dumper.outsize;
2179
2180                 if (repdata->taper.result == L_SUCCESS) {
2181                     if(origsize != 0.0) {
2182                         fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8.0f) (%8.0f) DrawHost\n",
2183                             dp->host->hostname, dp->name, repdata->level,
2184                             repdata->taper.filenum, origsize, 
2185                             outsize);
2186                     }
2187                     else {
2188                         fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8s) (%8.0f) DrawHost\n",
2189                             dp->host->hostname, dp->name, repdata->level,
2190                             repdata->taper.filenum, "N/A", 
2191                             outsize);
2192                     }
2193                 }
2194             }
2195         }
2196         
2197         fprintf(postscript,"\nshowpage\n");
2198     }
2199 }