/***             analog 4.14             http://www.analog.cx/             ***/
/*** This program is copyright (c) Stephen R. E. Turner 1995 - 2001 except as
 *** stated otherwise. Distribution, usage and modification of this program is
 *** subject to the conditions of the Licence which you should have received
 *** with it. This program comes with no warranty, expressed or implied.   ***/

/*** output.c; controls the output, mostly calling fns from output2.c ***/

#include "anlghea3.h"

#define GENSUM_RATE(x, m) (((m) < 30 || (x) <= 1)?((unsigned long)UNSET):\
  ((unsigned long)(((double)((x) - 1) * 1440.0) / (double)(m))))
void gensum(FILE *outf, choice outstyle, logical last7, unsigned long *data,
	    double bys, double bys7, Hashindex **gooditems,
	    Hashindex **baditems, Dateman *dman, logical rawbytes,
	    char sepchar, char decpt, char *compsep, char **monthname,
	    char **dayname, unsigned int monthlen, unsigned int daylen,
	    char **lngstr) {
  timecode_t totmins, totmins7;
  logical q7 = (last7 && dman->firsttime < dman->last7from &&
		dman->last7from < dman->lasttime);
  unsigned long tot, tot7;

  totmins = dman->lasttime - dman->firsttime;
  totmins7 = q7?MINS_IN_WEEK:0;
  if (q7)
    lastseven(outf, outstyle, dman->last7to, compsep, monthname, dayname,
	      monthlen, daylen, lngstr);
  gensumline(outf, outstyle, succreqs_, data[LOGDATA_SUCC],
	     q7?data[LOGDATA_SUCC7]:(unsigned long)UNSET, sepchar, compsep,
	     (logical)(!q7), lngstr);
  if (outstyle != COMPUTER) {
    gensumline(outf, outstyle, avereqs_,
	       GENSUM_RATE(data[LOGDATA_SUCC], totmins),
	       GENSUM_RATE(data[LOGDATA_SUCC7], totmins7), sepchar, compsep,
	       FALSE, lngstr);
  }
  gensumline(outf, outstyle, totunknown_, data[LOGDATA_UNKNOWN],
	     q7?data[LOGDATA_UNKNOWN7]:(unsigned long)UNSET, sepchar,
	     compsep, FALSE, lngstr);
  gensumline(outf, outstyle, totpages_, data[LOGDATA_PAGES],
	     q7?data[LOGDATA_PAGES7]:(unsigned long)UNSET, sepchar, compsep,
	     FALSE, lngstr);
  if (outstyle != COMPUTER) {
    gensumline(outf, outstyle, avepages_,
	       GENSUM_RATE(data[LOGDATA_PAGES], totmins),
	       GENSUM_RATE(data[LOGDATA_PAGES7], totmins7), sepchar, compsep,
	       FALSE, lngstr);
  }
  gensumline(outf, outstyle, totfails_, data[LOGDATA_FAIL],
	     q7?data[LOGDATA_FAIL7]:(unsigned long)UNSET, sepchar, compsep,
	     FALSE, lngstr);
  gensumline(outf, outstyle, totredirs_, data[LOGDATA_REDIR],
	     q7?data[LOGDATA_REDIR7]:(unsigned long)UNSET, sepchar, compsep,
	     FALSE, lngstr);
  gensumline(outf, outstyle, inforeqs_, data[LOGDATA_INFO],
	     q7?data[LOGDATA_INFO7]:(unsigned long)UNSET, sepchar, compsep,
	     FALSE, lngstr);
  distcount(gooditems[ITEM_FILE], baditems[ITEM_FILE], &tot, &tot7);
  gensumline(outf, outstyle, distfiles_, tot, q7?tot7:(unsigned long)UNSET,
	     sepchar, compsep, FALSE, lngstr);
  distcount(gooditems[ITEM_HOST], baditems[ITEM_HOST], &tot, &tot7);
  gensumline(outf, outstyle, disthosts_, tot, q7?tot7:(unsigned long)UNSET,
	     sepchar, compsep, FALSE, lngstr);
  gensumline(outf, outstyle, corrupt_, data[LOGDATA_CORRUPT],
	     (unsigned long)UNSET, sepchar, compsep, FALSE, lngstr);
  gensumline(outf, outstyle, unwanted_, data[LOGDATA_UNWANTED],
	     (unsigned long)UNSET, sepchar, compsep, FALSE, lngstr);
  gensumlineb(outf, outstyle, totdata_, bys, q7?bys7:UNSET, rawbytes,
	      sepchar, decpt, compsep, lngstr);
  if (outstyle != COMPUTER) {
    gensumlineb(outf, outstyle, avedata_,
		(totmins < 30)?UNSET:((bys * 1440.0) / (double)totmins),
		(q7 && totmins7 >= 30)?\
		((bys7 * 1440.0) / (double)totmins7):UNSET,
		rawbytes, sepchar, decpt, compsep, lngstr);
  }
}

void genrep(FILE *outf, choice rep, choice outstyle, logical multibyte,
	    Hashindex **gooditems, Hashindex **baditems, Alias *aliashead,
	    Include *wanthead, choice requests, choice date, Floor *floor,
	    choice sortby, logical alphaback, unsigned int pagewidth,
	    char sepchar, char repsepchar, char decpt, char *compsep,
	    logical rawbytes, choice *cols, char *colhead, char *colheadp,
	    char *gens, char *genp, char gender, logical *html,
	    timecode_t firsttime, char **monthname, char **dayname,
	    unsigned int monthlen, unsigned int daylen,
	    unsigned int plainmonthlen, unsigned int plaindaylen,
	    char **lngstr) {
  extern char *workspace;   /* see top of alias.c */

  Hashindex *ip;
  Hashentry *badp;
  unsigned long totr = 0, totp = 0, maxr = 0, maxp = 0, badn = 0, goodn = 0;
  double totb = 0.0, maxb = 0.0, unit = 1.0;
  unsigned int width[COL_NUMBER], bmult, tw = 0;
  logical possrightalign = FALSE, rightalign;
  char *notlistedstr;

  /* run through all items finding totals and maxima for floors */
  for (ip = *gooditems; ip != NULL; TO_NEXT(ip)) {
    if (ip->own != NULL) {
      totr += ip->own->data[requests];
      totp += ip->own->data[PAGES];
      totb += ip->own->bytes;
      maxr = MAX(maxr, ip->own->data[requests]);
      maxp = MAX(maxp, ip->own->data[PAGES]);
      maxb = MAX(maxb, ip->own->bytes);
    }
  }
  for (ip = *baditems; ip != NULL; TO_NEXT(ip)) {
    if (ip->own != NULL) {
      totr += ip->own->data[requests];
      totp += ip->own->data[PAGES];
      totb += ip->own->bytes;
      maxr = MAX(maxr, ip->own->data[requests]);
      maxp = MAX(maxp, ip->own->data[PAGES]);
      maxb = MAX(maxb, ip->own->bytes);
    }
  }

  if (rep != REP_SIZE && rep != REP_PROCTIME)
    /* so as not to get rid of 0's and to preserve order */
    my_sort(gooditems, baditems, NULL, NULL, NULL, 0, -1, floor, sortby,
	    alphaback, wanthead, requests, date, totr, totp, totb, maxr, maxp,
	    maxb);

  /* accumulate all unwanted items into a single entry */
  badp = newhashentry(FALSE);
  for (ip = *baditems; ip != NULL; TO_NEXT(ip)) {
    if (ip->own != NULL && ip->own->data[requests] > 0) {
      badp->data[requests] += ip->own->data[requests];
      badp->data[PAGES] += ip->own->data[PAGES];
      badp->bytes += ip->own->bytes;
      badp->data[date] = MAX(badp->data[date], ip->own->data[date]);
      badn++;
    }
  }

  /* now run through all good items, finding maxima of those for report */
  maxr = badp->data[requests];
  maxp = badp->data[PAGES];
  maxb = badp->bytes;
  for (ip = *gooditems; ip != NULL; TO_NEXT(ip)) {
    if (ip->own != NULL) {  /* should obey this already, but for safety... */
      maxr = MAX(maxr, ip->own->data[requests]);
      maxp = MAX(maxp, ip->own->data[PAGES]);
      maxb = MAX(maxb, ip->own->bytes);
      goodn++;
    }
  }

  if (rep == REP_SIZE || rep == REP_PROCTIME) {
    width[COL_TITLE] = htmlstrlen(colhead, outstyle);
    for (ip = *gooditems; ip != NULL; TO_NEXT(ip))
      width[COL_TITLE] = MAX(strlen(ip->name), /* no HTML codes used */
			     width[COL_TITLE]);
  }
  else
    width[COL_TITLE] = 0;
  calcsizes(outstyle, width, &bmult, &unit, maxr, maxp, maxb, goodn,
	    pagewidth, 0, '\0', repsepchar, rawbytes, cols, monthlen, daylen,
	    lngstr);
  if (alphaback && sortby == ALPHABETICAL) {
    for (ip = *gooditems; ip != NULL; TO_NEXT(ip)) {
      strcpy(workspace, ip->name);
      do_aliasx(workspace, aliashead);
      /* Only use do_aliasx() not do_aliasrep(), because it's just for
	 measuring width. */
      tw = MAX(tw, htmlstrlen(workspace, outstyle));
    }
    width[COL_TITLE] = MIN(tw, width[COL_TITLE]);
    possrightalign = TRUE;
  }
  if (rep != REP_SIZE && rep != REP_PROCTIME)
    whatincluded(outf, rep, outstyle, goodn, pagewidth, floor, sortby, decpt,
		 compsep, gens, genp, gender, requests, date, firsttime,
		 monthname, dayname, plainmonthlen, plaindaylen, lngstr);
  PRESTART();
  colheads(outf, cols, outstyle, width, bmult, colhead,
	   (logical)(rep == REP_SIZE || rep == REP_PROCTIME), lngstr);

  /* run through all items printing them. Reuse goodn here. */
  for (goodn = 0, ip = *gooditems; ip != NULL; TO_NEXT(ip)) {
    if (possrightalign &&
	(!ISDIGIT(ip->name[0]) || !ISDIGIT(ip->name[strlen(ip->name) - 1])))
      rightalign = TRUE;
    else
      rightalign = FALSE;
    printcols(outf, rep, cols, outstyle, multibyte, ip->own->data[requests],
	      ip->own->data[PAGES], ip->own->bytes, (long)(++goodn), 0,
	      totr, totp, totb, width, bmult, '\0', unit, '\0', '\0',
	      repsepchar, decpt, compsep,
	      (logical)(rep == REP_SIZE || rep == REP_PROCTIME), FALSE,
	      rightalign, NULL, html, ip->name, FALSE, ip->own->ispage, 0,
	      aliashead, NULL, NULL, lngstr[genrepdate_], lngstr[genreptime_],
	      ip->own->data[date] / 1440, (ip->own->data[date] % 1440) / 60,
	      ip->own->data[date] % 60, 0, 0, monthname, dayname,
	      monthlen, daylen, plainmonthlen, plaindaylen);
  }
  if (badn > 0) {
    printcols(outf, rep, cols, outstyle, multibyte, badp->data[requests],
	      badp->data[PAGES], badp->bytes, -1, 0, totr, totp, totb, width,
	      bmult, '\0', unit, '\0', '\0', repsepchar, decpt, compsep,
	      (logical)(rep == REP_SIZE || rep == REP_PROCTIME), FALSE, FALSE,
	      NULL, html, NULL, FALSE, FALSE, 0, NULL, NULL, NULL,
	      lngstr[genrepdate_], lngstr[genreptime_],
	      badp->data[date] / 1440, (badp->data[date] % 1440) / 60,
	      badp->data[date] % 60, 0, 0, monthname, dayname, monthlen,
	      daylen, plainmonthlen, plaindaylen);
    if (gender == 'm')
      notlistedstr = lngstr[notlistedm_];
    else if (gender == 'f')
      notlistedstr = lngstr[notlistedf_];
    else
      notlistedstr = lngstr[notlistedn_];
    fprintf(outf, "[%s: ", notlistedstr);
    f3printf(outf, outstyle, (double)badn, 0, sepchar);
    if (outstyle == COMPUTER)
      fprintf(outf, "]\n");
    else
      fprintf(outf, " %s]\n", (badn == 1)?colhead:colheadp);
  }
  PREEND();
}

void subdayrep(FILE *outf, choice rep, Daysdata *firstdp, Daysdata *lastdp,
	       datecode_t firstdate, datecode_t lastdate, choice outstyle,
	       unsigned int granularity, unsigned int repgran,
	       unsigned int pagewidth, unsigned int mingraphwidth,
	       logical back, logical rawbytes, choice *cols,
	       unsigned int rows, char graphby, char barstyle, char markchar,
	       char sepchar, char repsepchar, char decpt, char *compsep,
	       choice weekbeginson, char *imagedir, logical *html,
	       char **monthname, char **dayname, unsigned int monthlen,
	       unsigned int daylen, unsigned int plainmonthlen,
	       unsigned int plaindaylen, char **lngstr, char *datefmt,
	       char *colhead, char *busystr) {
  Daysdata *dp;
  Timerep *trhead, *trp, *oldtrp;
  int hr, min = 0, newmin = 0, busyhr = 0, busymin = 0;
  datecode_t busydate = 0, i;
  int j, firsttime, lasttime;
  unsigned int k;
  unsigned int relgran = granularity / repgran; /* guaranteed to be integer */
  unsigned long reqs, pages, totr = 0, totp = 0;
  unsigned long maxr = 0, maxp = 0, busyr = 0, busyp = 0;
  double bys, totb = 0.0, maxb = 0.0, busyb = 0.0, unit = 0.0;
  unsigned int width[COL_NUMBER], bmult, rowsdone = 0;
  logical first = TRUE;

  dp = lastdp;
  trhead = (Timerep *)xmalloc(sizeof(Timerep));
  trp = trhead;

  if (rows == 0)
    rows = INT_MAX;
  for (firsttime = 0; firsttime < (int)granularity &&
	 firstdp->reqs[firsttime] == 0; firsttime++)
    ;    /* run to first time */
  for (lasttime = granularity - 1;
       lasttime >= 0 && lastdp->reqs[lasttime] == 0; lasttime--)
    ;
  for (i = lastdate; i >= firstdate; i--) {
    for (j = (int)(granularity - relgran); j >= 0; j -= relgran) {
      reqs = 0;
      pages = 0;
      bys = 0.0;
      for (k = 0; k < relgran; k++) {
	reqs += dp->reqs[j + k];
	pages += dp->pages[j + k];
	bys += dp->bytes[j + k];
      }
      if ((dp != lastdp || j <= lasttime) &&
	  (dp != firstdp || j + (int)relgran > firsttime)) {
	totr += reqs;
	totp += pages;
	totb += bys;
	if (((graphby == 'R' || graphby == 'r') && reqs >= busyr) ||
	    ((graphby == 'P' || graphby == 'p') && pages >= busyp) ||
	    ((graphby == 'B' || graphby == 'b') && bys >= busyb)) {
	  busyr = reqs;
	  busyp = pages;
	  busyb = bys;
	  busydate = i;
	  busymin = (1440 * j) / granularity;
	}
	if (rowsdone < rows) {
	  maxr = MAX(maxr, reqs);
	  maxp = MAX(maxp, pages);
	  maxb = MAX(maxb, bys);
	  trp->prev = (Timerep *)xmalloc(sizeof(Timerep));
	  trp->prev->next = trp;
	  trp = trp->prev;
	  trp->reqs = reqs;
	  trp->pages = pages;
	  trp->bytes = bys;
	  trp->date = i;
	  trp->prev = NULL;
	  rowsdone++;
	  if (rowsdone == rows) /* set now because j will be destroyed */
	    min = (1440 * j) / granularity;
	}
      }
    }
    dp = dp->prev;
  }

  width[COL_TITLE] = MAX(datefmtlen(datefmt, monthlen, daylen, compsep,
				    (logical)(outstyle == COMPUTER)),
			 htmlstrlen(colhead, outstyle));
  calcsizes(outstyle, width, &bmult, &unit, maxr, maxp, maxb, 0, pagewidth,
	    mingraphwidth, graphby, repsepchar, rawbytes, cols, 0, 0, lngstr);
  declareunit(outf, outstyle, pagewidth, graphby, unit, bmult, barstyle,
	      markchar, sepchar, decpt, imagedir, lngstr);

  if (back || rowsdone != rows)  /* o/wise set above */
    min = (int)((1440 * ((back?lasttime:firsttime) / relgran)) / repgran);
  hr = min / 60;
  min %= 60;
  PRESTART();
  colheads(outf, cols, outstyle, width, bmult, colhead, TRUE, lngstr);
  for (trp = back?(trhead->prev):trp; trp != (back?NULL:trhead); ) {
    if (outstyle != COMPUTER && !first &&
	((rep == REP_HOURREP && hr == (back?23:0)) ||
	 (rep == REP_QUARTER && min == (back?45:0) &&
	  (hr % 4) == (back?3:0)) ||
	 (rep == REP_FIVE && min == (back?55:0)) ||
	 (rep == REP_DAYREP && DAYOFWEEK(trp->date) ==
	  (back?((weekbeginson + 6) % 7):weekbeginson))))
      fputc('\n', outf);
    if (rep == REP_QUARTER)
      newmin = (min + 15) % 60;
    else if (rep == REP_FIVE)
      newmin = (min + 5) % 60;
    printcols(outf, rep, cols, outstyle, FALSE, trp->reqs, trp->pages,
	      trp->bytes, -1, -1, totr, totp, totb, width, bmult, graphby,
	      unit, barstyle, markchar, repsepchar, decpt, compsep, TRUE, TRUE,
	      FALSE, imagedir, html, "", TRUE, FALSE, 0, NULL, NULL, NULL,
	      datefmt, NULL, trp->date, (unsigned int)hr, (unsigned int)min,
	      (unsigned int)((newmin == 0)?(hr + 1):(hr)),
	      (unsigned int)newmin, monthname, dayname, monthlen, daylen,
	      plainmonthlen, plaindaylen);
    first = FALSE;
    if (rep == REP_HOURREP)
      hr += back?(-1):(+1);
    else if (rep == REP_QUARTER)
      min += back?(-15):(+15);
    else if (rep == REP_FIVE)
      min += back?(-5):(+5);
    if (min >= 60) {
      min -= 60;
      hr++;
    }
    else if (min < 0) {
      min += 60;
      hr--;
    }
    if (hr >= 24)
      hr -= 24;
    else if (hr < 0)
      hr += 24;
    oldtrp = trp;
    trp = back?(trp->prev):(trp->next);
    free((void *)oldtrp);
  }
  PREEND();
  busyhr = busymin / 60;
  busymin %= 60;
  if (rep == REP_QUARTER)
    newmin = (busymin + 15) % 60;
  else if (rep == REP_FIVE)
    newmin = (busymin + 5) % 60;
  busyprintf(outf, outstyle, datefmt, busyr, busyp, busyb, busydate,
	     (unsigned int)busyhr, (unsigned int)busymin,
	     (unsigned int)((newmin == 0)?(busyhr + 1):(busyhr)),
	     (unsigned int)newmin, graphby, sepchar, decpt, rawbytes, lngstr,
	     monthname, dayname, plainmonthlen, plaindaylen, busystr);
}

void superdayrep(FILE *outf, choice rep, Daysdata *firstdp, Daysdata *lastdp,
		 datecode_t firstdate, datecode_t lastdate, choice outstyle,
		 unsigned int granularity, unsigned int pagewidth,
		 unsigned int mingraphwidth, logical back, logical rawbytes,
		 choice *cols, unsigned int rows, char graphby, char barstyle,
		 char markchar, char sepchar, char repsepchar, char decpt,
		 char *compsep, choice weekbeginson, char *imagedir,
		 logical *html, char **monthname, unsigned int monthlen,
		 unsigned int plainmonthlen, char **lngstr, char *datefmt,
		 char *colhead, char *busystr) {
  Daysdata *dp;
  Timerep *trhead, *trp, *oldtrp;
  unsigned long reqs = 0, pages = 0, totr = 0, totp = 0;
  unsigned long maxr = 0, maxp = 0, busyr = 0, busyp = 0;
  double bys = 0.0, totb = 0.0, maxb = 0.0, busyb = 0.0, unit = 0.0;
  unsigned int width[COL_NUMBER], bmult, date, dummy1, dummy2;
  logical save, first = TRUE;
  datecode_t busydate = 0, i;
  unsigned long rowsdone = 0;
  unsigned int j;

  dp = lastdp;
  trhead = (Timerep *)xmalloc(sizeof(Timerep));
  trp = trhead;

  if (rows == 0)
    rows = INT_MAX;
  for (i = lastdate; i >= firstdate; i--) {
    for (j = 0; j < granularity; j++) {
      reqs += dp->reqs[j];
      pages += dp->pages[j];
      bys += dp->bytes[j];
    }
    if (rep == REP_MONTH) {
      code2date(i, &date, &dummy1, &dummy2);
      save = (date == 1) || (i == firstdate);
    }
    else
      save = (DAYOFWEEK(i) == weekbeginson) || (i == firstdate);
    if (save) {
      totr += reqs;
      totp += pages;
      totb += bys;
      if (((graphby == 'R' || graphby == 'r') && reqs >= busyr) ||
	  ((graphby == 'P' || graphby == 'p') && pages >= busyp) ||
	  ((graphby == 'B' || graphby == 'b') && bys >= busyb)) {
	busyr = reqs;
	busyp = pages;
	busyb = bys;
	busydate = i;  /* busydate always set coz busyr is initialised to 0 */
      }
      if (rowsdone < rows) {
	maxr = MAX(maxr, reqs);
	maxp = MAX(maxp, pages);
	maxb = MAX(maxb, bys);
	trp->prev = (Timerep *)xmalloc(sizeof(Timerep));
	trp->prev->next = trp;
	trp = trp->prev;
	trp->reqs = reqs;
	trp->pages = pages;
	trp->bytes = bys;
	while (rep == REP_WEEK && DAYOFWEEK(i) != weekbeginson)
	  i--;  /* destroys i, but in this case we've finished with it */
	trp->date = i;
	trp->prev = NULL;
	rowsdone++;
      }
      reqs = 0;
      pages = 0;
      bys = 0.0;
    }
    dp = dp->prev;
  }

  width[COL_TITLE] = MAX(datefmtlen(datefmt, monthlen, 0, compsep,
				    (logical)(outstyle == COMPUTER)),
			 htmlstrlen(colhead, outstyle));
  calcsizes(outstyle, width, &bmult, &unit, maxr, maxp, maxb, 0, pagewidth,
	    mingraphwidth, graphby, repsepchar, rawbytes, cols, 0, 0, lngstr);
  declareunit(outf, outstyle, pagewidth, graphby, unit, bmult, barstyle,
	      markchar, sepchar, decpt, imagedir, lngstr);
  PRESTART();
  colheads(outf, cols, outstyle, width, bmult, colhead, TRUE, lngstr);
  for (trp = back?(trhead->prev):trp; trp != (back?NULL:trhead); ) {
    if (rep == REP_MONTH) {
      code2date(trp->date, &dummy1, &date, &dummy2);
      if (date == (unsigned int)(back?11:0) && outstyle != COMPUTER && !first)
	fputc('\n', outf);
      first = FALSE;
    }
    printcols(outf, rep, cols, outstyle, FALSE, trp->reqs, trp->pages,
	      trp->bytes, -1, -1, totr, totp, totb, width, bmult, graphby,
	      unit, barstyle, markchar, repsepchar, decpt, compsep, TRUE, TRUE,
	      FALSE, imagedir, html, "", TRUE, FALSE, 0, NULL, NULL, NULL,
	      datefmt, NULL, trp->date, 0, 0, 0, 0, monthname, NULL, monthlen,
	      0, plainmonthlen, 0);
    oldtrp = trp;
    trp = back?(trp->prev):(trp->next);
    free((void *)oldtrp);
  }
  PREEND();
  while (rep == REP_WEEK && DAYOFWEEK(busydate) != weekbeginson)
    busydate--;  /* if first week is busiest */
  busyprintf(outf, outstyle, datefmt, busyr, busyp, busyb, busydate, 0, 0, 0,
	     0, graphby, sepchar, decpt, rawbytes, lngstr, monthname, NULL,
	     plainmonthlen, 0, busystr);
}

void daysum(FILE *outf, Daysdata *firstdp, datecode_t firstdate,
	    datecode_t lastdate, choice outstyle, unsigned int granularity,
	    choice weekbeginson, unsigned int pagewidth,
	    unsigned int mingraphwidth, logical rawbytes, choice *cols,
	    char graphby, char barstyle, char markchar, char sepchar,
	    char repsepchar, char decpt, char *compsep, char *imagedir,
	    logical *html, char *colhead, char **dayname, unsigned int daylen,
	    char **lngstr) {
  Daysdata *dp;
  unsigned long reqs[7], pages[7], totr = 0, totp = 0;
  double bys[7], totb = 0.0, unit = 0.0;
  unsigned int width[COL_NUMBER], bmult;
  choice weekday;
  datecode_t date;
  unsigned int i;

  for (i = 0; i < 7; i++) {
    reqs[i] = 0;
    pages[i] = 0;
    bys[i] = 0.0;
  }

  dp = firstdp;
  for (date = firstdate; date <= lastdate; date++) {
    weekday = DAYOFWEEK(date);
    for (i = 0; i < granularity; i++) {
      reqs[weekday] += dp->reqs[i];
      totr += dp->reqs[i];
      pages[weekday] += dp->pages[i];
      totp += dp->pages[i];
      bys[weekday] += dp->bytes[i];
      totb += dp->bytes[i];
    }
    dp = dp->next;
  }

  width[COL_TITLE] = MAX(daylen, htmlstrlen(lngstr[day_], outstyle));

  calcsizes(outstyle, width, &bmult, &unit, arraymaxl(reqs, 7),
	    arraymaxl(pages, 7), arraymaxd(bys, 7), 0, pagewidth,
	    mingraphwidth, graphby, repsepchar, rawbytes, cols, 0, 0, lngstr);
  declareunit(outf, outstyle, pagewidth, graphby, unit, bmult, barstyle,
	      markchar, sepchar, decpt, imagedir, lngstr);
  PRESTART();
  colheads(outf, cols, outstyle, width, bmult, colhead, TRUE, lngstr);
  for (i = 0; i < 7; i++) {
    weekday = (weekbeginson + i) % 7;
    printcols(outf, REP_DAYSUM, cols, outstyle, FALSE, reqs[weekday],
	      pages[weekday], bys[weekday], -1, -1, totr, totp, totb, width,
	      bmult, graphby, unit, barstyle, markchar, repsepchar, decpt,
	      compsep, TRUE, TRUE, FALSE, imagedir, html, dayname[weekday],
	      FALSE, FALSE, 0, NULL, NULL, NULL, "", "", 0, 0, 0, 0, 0, NULL,
	      NULL, 0, 0, 0, 0);
  }
  PREEND();
}

void hoursum(FILE *outf, Daysdata *firstdp, Daysdata *lastdp, choice outstyle,
	     unsigned int granularity, unsigned int pagewidth,
	     unsigned int mingraphwidth, logical rawbytes, choice *cols,
	     char graphby, char barstyle, char markchar, char sepchar,
	     char repsepchar, char decpt, char *compsep, char *imagedir,
	     logical *html, char *colhead, char **lngstr) {
  Daysdata *dp;
  unsigned long reqs[24], pages[24], totr = 0, totp = 0;
  double bys[24], totb = 0.0, unit = 0.0;
  unsigned int width[COL_NUMBER], bmult;
  logical finished = FALSE;
  char temps[3];
  unsigned int i;

  for (i = 0; i < 24; i++) {
    reqs[i] = 0;
    pages[i] = 0;
    bys[i] = 0.0;
  }
  
  for (dp = firstdp; !finished; TO_NEXT(dp)) {
    for (i = 0; i < granularity; i++) {
      reqs[(i * 24) / granularity] += dp->reqs[i];
      totr += dp->reqs[i];
      pages[(i * 24) / granularity] += dp->pages[i];
      totp += dp->pages[i];
      bys[(i * 24) / granularity] += dp->bytes[i];
      totb += dp->bytes[i];
    }
    if (dp == lastdp)
      finished = TRUE;
  }

  width[COL_TITLE] = MAX(2, htmlstrlen(lngstr[hr_], outstyle));
  calcsizes(outstyle, width, &bmult, &unit, arraymaxl(reqs, 24),
	    arraymaxl(pages, 24), arraymaxd(bys, 24), 0, pagewidth,
	    mingraphwidth, graphby, repsepchar, rawbytes, cols, 0, 0, lngstr);
  declareunit(outf, outstyle, pagewidth, graphby, unit, bmult, barstyle,
	      markchar, sepchar, decpt, imagedir, lngstr);
  PRESTART();
  colheads(outf, cols, outstyle, width, bmult, colhead, TRUE, lngstr);
  for (i = 0; i < 24; i++) {
    sprintf(temps, "%d", i);
    printcols(outf, REP_HOURSUM, cols, outstyle, FALSE, reqs[i], pages[i],
	      bys[i], -1, -1, totr, totp, totb, width, bmult, graphby, unit,
	      barstyle, markchar, repsepchar, decpt, compsep, TRUE, TRUE,
	      FALSE, imagedir, html, temps, FALSE, FALSE, 0, NULL, NULL, NULL,
	      "", "", 0, 0, 0, 0, 0, NULL, NULL, 0, 0, 0, 0);
  }
  PREEND();
}

void printtree(FILE *outf, choice rep, choice outstyle, logical multibyte,
	       Hashtable *tree, choice requests, choice date, Hashentry *badp,
	       unsigned long badn, unsigned int level, Strlist *partname,
	       Alias *aliashead, Include *linkhead, char *baseurl,
	       unsigned long totr, unsigned long totp, double totb,
	       unsigned int width[], logical possrightalign,
	       unsigned int bmult, double unit, char sepchar, char repsepchar,
	       char decpt, char *compsep, logical rawbytes, choice *cols,
	       char *colhead, char *colheadp, char gender, logical *html,
	       char **monthname, char **dayname, unsigned int monthlen,
	       unsigned int daylen, unsigned int plainmonthlen,
	       unsigned int plaindaylen, char **lngstr) {
  char *name;
  size_t need = (size_t)level + 3;
  logical rightalign;
  Hashindex *p;
  Strlist *pn, s;
  char *notlistedstr;
  unsigned long goodn = 0;

  if (tree != NULL) {
    for (pn = partname; pn != NULL; TO_NEXT(pn))
      need += strlen(pn->name);
    for (p = tree->head[0]; p != NULL; TO_NEXT(p)) {
      name = maketreename(partname, p, &pn, &s, need, rep, TRUE);
      if (name[0] == '\0' || name[1] == '\0')  /* in REP_ORG, a code */
	rightalign = FALSE;
      else
	rightalign = possrightalign;
      printcols(outf, rep, cols, outstyle, multibyte, p->own->data[requests],
		p->own->data[PAGES], p->own->bytes,
		(level == 0)?((long)(++goodn)):(-1), (int)level + 1, totr,
		totp, totb, width, bmult, '\0', unit, '\0', '\0', repsepchar,
		decpt, compsep, FALSE, FALSE, rightalign, NULL, html, name,
		FALSE, (logical)(p->own->ispage), 2 * level, aliashead,
		linkhead, baseurl, lngstr[genrepdate_], lngstr[genreptime_],
		p->own->data[date] / 1440, (p->own->data[date] % 1440) / 60,
		p->own->data[date] % 60, 0, 0, monthname, dayname, monthlen,
		daylen, plainmonthlen, plaindaylen);
      printtree(outf, rep, outstyle, multibyte, (Hashtable *)(p->other),
		requests, date,	NULL, 0, level + 1, pn, aliashead, linkhead,
		baseurl, totr, totp, totb, width, possrightalign, bmult, unit,
		sepchar, repsepchar, decpt, compsep, rawbytes, cols, NULL,
		NULL, gender, html, monthname, dayname, monthlen, daylen,
		plainmonthlen, plaindaylen, lngstr);
    }
  }
  if (badn > 0 && level == 0) {
    printcols(outf, rep, cols, outstyle, multibyte, badp->data[requests],
	      badp->data[PAGES], badp->bytes, -1, 1, totr, totp, totb, width,
	      bmult, '\0', unit, '\0', '\0', repsepchar, decpt, compsep, FALSE,
	      FALSE, FALSE, NULL, html, NULL, FALSE, FALSE, 0, NULL, NULL,
	      NULL, lngstr[genrepdate_], lngstr[genreptime_],
	      badp->data[date] / 1440, (badp->data[date] % 1440) / 60,
	      badp->data[date] % 60, 0, 0, monthname, dayname, monthlen,
	      daylen, plainmonthlen, plaindaylen);
    if (gender == 'm')
      notlistedstr = lngstr[notlistedm_];
    else if (gender == 'f')
      notlistedstr = lngstr[notlistedf_];
    else
      notlistedstr = lngstr[notlistedn_];
    fprintf(outf, "[%s: ", notlistedstr);
    f3printf(outf, outstyle, (double)badn, 0, sepchar);
    if (outstyle == COMPUTER)
      fprintf(outf, "]\n");
    else
      fprintf(outf, " %s]\n", (badn == 1)?colhead:colheadp);
  }
}

void treerep(FILE *outf, choice rep, choice outstyle, logical multibyte,
	     Tree *treex, Hashindex *gooditems, Hashindex *baditems,
	     Alias *aliashead, Include *wanthead, Include *linkhead,
	     char *baseurl, choice *alltrees, choice requests, choice date,
	     Floor *floor, choice sortby, Floor *subfloor, choice subsortby,
	     logical alphaback, unsigned int pagewidth, char sepchar,
	     char repsepchar, char decpt, char *compsep, logical rawbytes,
	     choice *cols, char *colhead, char *colheadp, char *gens,
	     char *genp, char gender, logical *html, timecode_t firsttime,
	     char **monthname, char **dayname, unsigned int monthlen,
	     unsigned int daylen, unsigned int plainmonthlen,
	     unsigned int plaindaylen, char **lngstr) {
  /* alphaback only really makes sense if no hierarchy: kludge to allow REP_ORG
     to be implemented as treerep() instead of dervrep() for simplicity */
  Hashtable *tree;
  Hashindex *p;
  Hashentry *badp;
  unsigned long totr = 0, totp = 0, maxr = 0, maxp = 0, goodn = 0, badn, i;
  double totb = 0.0, maxb = 0.0, unit = 1.0;
  unsigned int width[COL_NUMBER], bmult, tw;
  logical possrightalign = FALSE, templ = FALSE;

  for (i = 0; alltrees[i] != REP_NUMBER; i++)
    if (rep == alltrees[i])
      templ = TRUE;   /* tree already constructed */
  if (!templ)
    maketree(treex, gooditems, baditems);
  tree = treex->tree;
  for (i = 0; i < tree->size; i++) {
    for (p = tree->head[i]; p != NULL; TO_NEXT(p)) {
      totr += p->own->data[requests]; /* tot of top level = overall tot */
      totp += p->own->data[PAGES];    /* also max's all on top level */
      totb += p->own->bytes;
      maxr = MAX(maxr, p->own->data[requests]);
      maxp = MAX(maxp, p->own->data[PAGES]);
      maxb = MAX(maxb, p->own->bytes);
    }
  }

  tree->head[0] = sorttree(tree, rep, floor, sortby, subfloor, subsortby,
			   alphaback, 0, NULL, wanthead,
			   (rep == REP_DOM)?aliashead:NULL, requests, date,
			   totr, totp, totb, maxr, maxp, maxb, &badp, &badn,
			   treex->space);
  maxr = badp->data[requests];
  maxp = badp->data[PAGES];
  maxb = badp->bytes;
  for (p = tree->head[0]; p != NULL; TO_NEXT(p)) {
    maxr = MAX(maxr, p->own->data[requests]);
    maxp = MAX(maxp, p->own->data[PAGES]);
    maxb = MAX(maxb, p->own->bytes);
    goodn++;
  }
  width[COL_TITLE] = 0;
  calcsizes(outstyle, width, &bmult, &unit, maxr, maxp, maxb, goodn, pagewidth,
	    0, '\0', repsepchar, rawbytes, cols, monthlen, daylen, lngstr);
  if (alphaback && sortby == ALPHABETICAL) {
    tw = alphatreewidth(rep, tree, outstyle, 0, NULL, aliashead);
    width[COL_TITLE] = MIN(tw, width[COL_TITLE]);
    possrightalign = TRUE;
  }
  whatincluded(outf, rep, outstyle, goodn, pagewidth, floor, sortby, decpt,
	       compsep, gens, genp, gender, requests, date, firsttime,
	       monthname, dayname, plainmonthlen, plaindaylen, lngstr);
  PRESTART();
  colheads(outf, cols, outstyle, width, bmult, colhead, FALSE, lngstr);
  printtree(outf, rep, outstyle, multibyte, tree, requests, date, badp, badn,
	    0, NULL, aliashead, linkhead, baseurl, totr, totp, totb, width,
	    possrightalign, bmult, unit, sepchar, repsepchar, decpt, compsep,
	    rawbytes, cols, colhead, colheadp, gender, html, monthname,
	    dayname, monthlen, daylen, plainmonthlen, plaindaylen, lngstr);
  PREEND();

  freemm(treex->space);
}

void dervrep(FILE *outf, choice rep, choice outstyle, logical multibyte,
	     Derv *derv, Hashindex *gooditems, Hashindex *baditems,
	     Include *wanthead, choice *alldervs, Floor *floor, choice sortby,
	     unsigned int pagewidth, char sepchar, char repsepchar, char decpt,
	     char *compsep, logical rawbytes, unsigned char convfloor,
	     choice *cols, char *colhead, char *colheadp, char *gens,
	     char *genp, char gender, logical *html, timecode_t firsttime,
	     char **monthname, char **dayname, unsigned int monthlen,
	     unsigned int daylen, unsigned int plainmonthlen,
	     unsigned int plaindaylen, char **lngstr) {
  Hashindex *good = NULL, *bad = NULL;
  choice i;
  logical templ = FALSE;
  /* strategy: build list of items then hand off to genrep() */

  for(i = 0; alldervs[i] != REP_NUMBER; i++)
    if (rep == alldervs[i])
      templ = TRUE;
  if (!templ)
    makederived(derv, gooditems, baditems, convfloor, multibyte, rep);
  unhash(derv->table, &good, &bad);
  genrep(outf, rep, outstyle, multibyte, &good, &bad, NULL, wanthead, REQUESTS,
	 SUCCDATE, floor, sortby, FALSE, pagewidth, sepchar, repsepchar, decpt,
	 compsep, rawbytes, cols, colhead, colheadp, gens, genp, gender, html,
	 firsttime, monthname, dayname, monthlen, daylen, plainmonthlen,
	 plaindaylen, lngstr);
}

void dervtreerep(FILE *outf, choice rep, choice outstyle, logical multibyte,
		 Derv *derv, Tree *treex, Hashindex *gooditems,
		 Hashindex *baditems, Alias *aliashead, Include *wanthead,
		 choice *alltrees, choice *alldervs, Floor *floor,
		 choice sortby, Floor *subfloor, choice subsortby,
		 unsigned int pagewidth, char sepchar, char repsepchar,
		 char decpt, char *compsep, logical rawbytes,
		 unsigned char convfloor, choice *cols, char *colhead,
		 char *colheadp, char *gens, char *genp, char gender,
		 logical *html, timecode_t firsttime, char **monthname,
		 char **dayname, unsigned int monthlen, unsigned int daylen,
		 unsigned int plainmonthlen, unsigned int plaindaylen,
		 char **lngstr) {
  /* same as dervrep() except hand off to treerep() at the end */
  Hashindex *good = NULL, *bad = NULL;
  choice i;
  logical templ = FALSE;

  for(i = 0; alldervs[i] != REP_NUMBER; i++)
    if (rep == alldervs[i])
      templ = TRUE;
  if (!templ)
    makederived(derv, gooditems, baditems, convfloor, multibyte, rep);
  unhash(derv->table, &good, &bad);
  treerep(outf, rep, outstyle, multibyte, treex, good, bad, aliashead,
	  wanthead, NULL, NULL, alltrees, REQUESTS, SUCCDATE, floor, sortby,
	  subfloor, subsortby, FALSE, pagewidth, sepchar, repsepchar, decpt,
	  compsep, rawbytes, cols, colhead, colheadp, gens, genp, gender, html,
	  firsttime, monthname, dayname, monthlen, daylen, plainmonthlen,
	  plaindaylen, lngstr);
}

/* names for each bucket in arrayrep: see thresholds at top of defaults()...
   in globals.c. (Names for code buckets are in the language files). */

char *sizenames[] =
{"0", "1b-  10b", "11b- 100b", "101b-  1kb", "1kb- 10kb", "10kb-100kb",
 "100kb-  1Mb", "1Mb- 10Mb", "10Mb-100Mb", "100Mb-  1Gb", "> 1Gb"};

char *ptnames1[] =
{"0", "<= 0.01", "0.01-0.02", "0.02-0.05", "0.05-0.1 ", "0.1 -0.2 ",
 "0.2 -0.5 ", "0.5 -1   ", "1-  2 ", "2-  5 ", "5- 10 ", "10- 20 ",
 "20- 60 ", "60-120 ", "120-300 ", "> 300 "};

char *ptnames2[] =
{"0", "", "", "", "", "", "", "1", "2", "3-  5", "5- 10", "10- 20", "20- 60",
 "60-120", "120-300", "> 300"};  /* empty string signifies don't use */

void arrayrep(FILE *outf, choice rep, choice outstyle, logical multibyte,
	      Arraydata *array, Floor *floor, choice sortby,
	      unsigned int pagewidth, char sepchar, char repsepchar,
	      char decpt, char *compsep, logical rawbytes, choice *cols,
	      char *colhead, char *colheadp, char *gens, char *genp,
	      char gender, logical *html, timecode_t firsttime,
	      char **monthname, char **dayname, unsigned int monthlen,
	      unsigned int daylen, unsigned int plainmonthlen,
	      unsigned int plaindaylen, char **lngstr) {

  extern Memman *xmemman;

  Hashindex *good = NULL, *bad = NULL, *gp = NULL;
  char **names;
  choice i, lasti;
  logical done;
  /* strategy: construct list of (Hashindex *), and pass to genrep() */

  if (rep == REP_CODE)
    names = &(lngstr[code100_]);
  else if (rep == REP_SIZE)
    names = sizenames;
  else if (array[1].reqs + array[2].reqs + array[3].reqs + array[4].reqs +
	   array[5].reqs + array[6].reqs == 0)  /* assume %t not %T */
    names = ptnames2;
  else
    names = ptnames1;

  /* calculate lasti */
  for (lasti = 0; array[lasti].threshold >= -0.5; lasti++)
    ;
  if (rep != REP_CODE) {
    for ( ; array[lasti].reqs == 0; lasti--)
      ;
  }
  for (i = 0, done = FALSE; !done; i++) {
    if (array[i].reqs > 0 || (rep != REP_CODE && !IS_EMPTY_STRING(names[i]))) {
      if (good == NULL) {
	gp = (Hashindex *)submalloc(xmemman, sizeof(Hashindex));
	good = gp;
      }
      else {
	gp->next = (Hashindex *)submalloc(xmemman, sizeof(Hashindex));
	TO_NEXT(gp);
      }
      gp->name = names[i];
      gp->own = newhashentry(FALSE);
      gp->own->data[REQUESTS] = array[i].reqs;
      gp->own->data[PAGES] = array[i].pages;
      gp->own->data[SUCCDATE] = array[i].lastdate;
      gp->own->bytes = array[i].bytes;
      gp->next = NULL;
    }
    if (i == lasti)
      done = TRUE;
  }
  genrep(outf, rep, outstyle, (logical)((rep == REP_CODE)?multibyte:FALSE),
	 &good, &bad, NULL, NULL, REQUESTS, SUCCDATE, floor, sortby, FALSE,
	 pagewidth, sepchar, repsepchar, decpt, compsep, rawbytes, cols,
	 colhead, colheadp, gens, genp, gender, html, firsttime, monthname,
	 dayname, monthlen, daylen, plainmonthlen, plaindaylen, lngstr);
}

void output(Outchoices *od, Hashindex **gooditems, Hashindex **baditems,
	    Dateman *dman, Arraydata **arraydata, unsigned long *sumdata,
	    double totbytes, double totbytes7, unsigned int granularity) {
  extern choice *rep2type, *rep2reqs, *rep2date;
  extern unsigned int *rep2gran, *rep2lng, *rep2datefmt, *rep2colhead;
  extern unsigned int *rep2busystr;
  extern char *workspace;

  FILE *outf;
  Include *linkhead;
  char *baseurl;
  int ro;
  choice rep;

  /* first open output file */
  
  if (strcaseeq(od->outfile, "stdout") || STREQ(od->outfile, "-")) {
    outf = stdout;
    debug('F', "Opening stdout as output file");
  }
  else {
    (void)datesprintf(workspace, od->outfile, OUT_NONE, dman->last7to / 1440,
		      (dman->last7to % 1440) / 60, dman->last7to % 60,
		      0, 0, od->monthname, od->dayname, 0, 0, NULL, TRUE);
    if ((outf = FOPENW(workspace)) == NULL)
      error("failed to open output file %s for writing", workspace);
    else
      debug('F', "Opening %s as output file", workspace);
  }

  /* remove any reports not wanted */

  checkreps(od, od->repq, dman, od->alltrees, od->alldervs, gooditems,
	    od->tree, od->derv, arraydata);

  /* page header */

  pagetop(outf, od, dman);
    
  /* Now the main reports */

  for (ro = 0; od->reporder[ro] != -1; ro++) {
    rep = od->reporder[ro];

    if (od->repq[rep]) {
      report_title(outf, od->lngstr[rep2lng[rep]], rep, od->gotos,
		   od->repq, od->lngstr, od->reporder, od->outstyle);
      switch(rep) {
      case (REP_GENSUM):
	gensum(outf, od->outstyle, od->last7, sumdata, totbytes, totbytes7,
	       gooditems, baditems, dman, od->rawbytes, od->sepchar, od->decpt,
	       od->compsep, od->monthname, od->dayname, od->plainmonthlen,
	       od->plaindaylen, od->lngstr);
	break;
      case (REP_DAYREP):
      case (REP_HOURREP):
      case (REP_QUARTER):
      case (REP_FIVE):
	subdayrep(outf, rep, dman->firstdp, dman->lastdp, dman->firstdate,
		  dman->lastdate, od->outstyle, granularity, rep2gran[rep],
		  od->pagewidth, od->mingraphwidth, od->back[rep],
		  od->rawbytes, od->cols[rep], od->rows[rep], od->graph[rep],
		  od->barstyle, od->markchar, od->sepchar, od->repsepchar,
		  od->decpt, od->compsep, od->weekbeginson, od->imagedir,
		  &(od->html), od->monthname, od->dayname, od->monthlen,
		  od->daylen, od->plainmonthlen, od->plaindaylen, od->lngstr,
		  od->lngstr[rep2datefmt[rep]], od->lngstr[rep2colhead[rep]],
		  od->lngstr[rep2busystr[rep]]);
	break;
      case (REP_WEEK):
      case (REP_MONTH):
	superdayrep(outf, rep, dman->firstdp, dman->lastdp, dman->firstdate,
		    dman->lastdate, od->outstyle, granularity, od->pagewidth,
		    od->mingraphwidth, od->back[rep], od->rawbytes,
		    od->cols[rep], od->rows[rep], od->graph[rep], od->barstyle,
		    od->markchar, od->sepchar, od->repsepchar, od->decpt,
		    od->compsep, od->weekbeginson, od->imagedir, &(od->html),
		    od->monthname, od->monthlen, od->plainmonthlen, od->lngstr,
		    od->lngstr[rep2datefmt[rep]], od->lngstr[rep2colhead[rep]],
		    od->lngstr[rep2busystr[rep]]);
	break;
      case (REP_DAYSUM):
	daysum(outf, dman->firstdp, dman->firstdate, dman->lastdate,
	       od->outstyle, granularity, od->weekbeginson, od->pagewidth,
	       od->mingraphwidth, od->rawbytes, od->cols[rep], od->graph[rep],
	       od->barstyle, od->markchar, od->sepchar, od->repsepchar,
	       od->decpt, od->compsep, od->imagedir, &(od->html),
	       od->lngstr[rep2colhead[rep]], od->dayname, od->daylen,
	       od->lngstr);
	break;
      case (REP_HOURSUM):
	hoursum(outf, dman->firstdp, dman->lastdp, od->outstyle, granularity,
		od->pagewidth, od->mingraphwidth, od->rawbytes, od->cols[rep],
		od->graph[rep], od->barstyle, od->markchar, od->sepchar,
		od->repsepchar, od->decpt, od->compsep, od->imagedir,
		&(od->html), od->lngstr[rep2colhead[rep]], od->lngstr);
	break;
      case (REP_HOST):
      case (REP_BROWREP):
      case (REP_VHOST):
      case (REP_USER):
      case (REP_FAILUSER):
	genrep(outf, rep, od->outstyle, od->multibyte,
	       &(gooditems[rep2type[rep]]), &(baditems[rep2type[rep]]),
	       od->aliashead[G(rep)], od->wanthead[G(rep)], rep2reqs[G(rep)],
	       rep2date[G(rep)], &(od->floor[G(rep)]), od->sortby[G(rep)],
	       (logical)(rep == REP_HOST || rep == REP_VHOST), od->pagewidth,
	       od->sepchar, od->repsepchar, od->decpt, od->compsep,
	       od->rawbytes, od->cols[rep], od->lngstr[rep2colhead[rep]],
	       od->lngstr[rep2colhead[rep] + 1], od->lngstr[rep2lng[rep] + 1],
	       od->lngstr[rep2lng[rep] + 2], od->lngstr[rep2lng[rep] + 3][0],
	       &(od->html), dman->firsttime, od->monthname, od->dayname,
	       od->monthlen, od->daylen, od->plainmonthlen, od->plaindaylen,
	       od->lngstr);
	break;
      case (REP_REQ):
      case (REP_REDIR):
      case (REP_FAIL):
      case (REP_REF):
      case (REP_REDIRREF):
      case (REP_FAILREF):
      case (REP_TYPE):
      case (REP_DIR):
      case (REP_DOM):
      case (REP_ORG):
      case (REP_REFSITE):
	baseurl = NULL;
	if (rep == REP_REQ) {
	  linkhead = od->link;
	  baseurl = od->baseurl;
	}
	else if (rep == REP_REF || rep == REP_REDIRREF || rep == REP_FAILREF)
	  linkhead = od->reflink;
	else
	  linkhead = NULL;
	treerep(outf, rep, od->outstyle, od->multibyte, od->tree[G(rep)],
		gooditems[rep2type[rep]], baditems[rep2type[rep]],
		od->aliashead[G(rep)], od->wanthead[G(rep)], linkhead, baseurl,
		od->alltrees, rep2reqs[G(rep)], rep2date[G(rep)],
		&(od->floor[G(rep)]), od->sortby[G(rep)],
		&(od->subfloor[G(rep)]), od->subsortby[G(rep)],
		(logical)(rep == REP_ORG), od->pagewidth, od->sepchar,
		od->repsepchar, od->decpt, od->compsep, od->rawbytes,
		od->cols[rep], od->lngstr[rep2colhead[rep]],
		od->lngstr[rep2colhead[rep] + 1], od->lngstr[rep2lng[rep] + 1],
		od->lngstr[rep2lng[rep] + 2], od->lngstr[rep2lng[rep] + 3][0],
		&(od->html), dman->firsttime, od->monthname, od->dayname,
		od->monthlen, od->daylen, od->plainmonthlen, od->plaindaylen,
		od->lngstr);
	break;
      case (REP_SEARCHREP):
      case (REP_SEARCHSUM):
	dervrep(outf, rep, od->outstyle, od->multibyte,
		od->derv[rep - FIRST_DERVREP], gooditems[rep2type[rep]],
		baditems[rep2type[rep]], od->wanthead[G(rep)], od->alldervs,
		&(od->floor[G(rep)]), od->sortby[G(rep)], od->pagewidth,
		od->sepchar, od->repsepchar, od->decpt, od->compsep,
		od->rawbytes, od->convfloor, od->cols[rep],
		od->lngstr[rep2colhead[rep]], od->lngstr[rep2colhead[rep] + 1],
		od->lngstr[rep2lng[rep] + 1], od->lngstr[rep2lng[rep] + 2],
		od->lngstr[rep2lng[rep] + 3][0], &(od->html), dman->firsttime,
		od->monthname, od->dayname, od->monthlen, od->daylen,
		od->plainmonthlen, od->plaindaylen, od->lngstr);
	break;
      case (REP_BROWSUM):
      case (REP_OS):
	dervtreerep(outf, rep, od->outstyle, od->multibyte,
		    od->derv[rep - FIRST_DERVREP], od->tree[G(rep)],
		    gooditems[rep2type[rep]], baditems[rep2type[rep]],
		    od->aliashead[G(rep)], od->wanthead[G(rep)], od->alltrees,
		    od->alldervs, &(od->floor[G(rep)]), od->sortby[G(rep)],
		    &(od->subfloor[G(rep)]), od->subsortby[G(rep)],
		    od->pagewidth, od->sepchar, od->repsepchar, od->decpt,
		    od->compsep, od->rawbytes, od->convfloor, od->cols[rep],
		    od->lngstr[rep2colhead[rep]],
		    od->lngstr[rep2colhead[rep] + 1],
		    od->lngstr[rep2lng[rep] + 1], od->lngstr[rep2lng[rep] + 2],
		    od->lngstr[rep2lng[rep] + 3][0], &(od->html),
		    dman->firsttime, od->monthname, od->dayname, od->monthlen,
		    od->daylen, od->plainmonthlen, od->plaindaylen,
		    od->lngstr);
	break;
      case (REP_SIZE):
      case (REP_CODE):
      case (REP_PROCTIME):
	arrayrep(outf, rep, od->outstyle, od->multibyte,
		 arraydata[rep - FIRST_ARRAYREP], &(od->floor[G(rep)]),
		 od->sortby[G(rep)], od->pagewidth, od->sepchar,
		 od->repsepchar, od->decpt, od->compsep, od->rawbytes,
		 od->cols[rep], od->lngstr[rep2colhead[rep]],
		 od->lngstr[rep2colhead[rep] + 1],
		 od->lngstr[rep2lng[rep] + 1], od->lngstr[rep2lng[rep] + 2],
		 od->lngstr[rep2lng[rep] + 3][0], &(od->html), dman->firsttime,
		 od->monthname, od->dayname, od->monthlen, od->daylen,
		 od->plainmonthlen, od->plaindaylen, od->lngstr);
	break;
      }  /* end switch rep */
      hrule(outf, od->outstyle, od->pagewidth);
    }    /* end if rep wanted */
  }      /* end for ro */

  /*** Bit at the bottom of the page ***/

  pagebot(outf, od);
  if (!strcaseeq(od->outfile, "stdout") && !STREQ(od->outfile, "-")) {
    debug('F', "Closing %s",
	  datesprintf(workspace, od->outfile, OUT_NONE, dman->last7to / 1440,
		      (dman->last7to % 1440) / 60, dman->last7to % 60,
		      0, 0, od->monthname, od->dayname, 0, 0, NULL, TRUE));
    fclose(outf);
  }
}
