ftp.nice.ch/pub/next/unix/network/www/analog.NIHS.bs.gnutar.gz#/analog/output.c

This is output.c in view mode; [Download] [Up]

/*** analog 1.9beta ***/
/* Please read Readme.html, or http://www.statslab.cam.ac.uk/~sret1/analog/  */

/*** output.c; the output functions, obviously. ***/

#include "analhea2.h"

/* A few variables global to this file */
int total_succ_reqs, total_fail_reqs, total_other_reqs;
int total_succ_reqs7, total_fail_reqs7, total_other_reqs7;

/*** The first function prints the "goto" line; links to all reports except
     possibly one (the one we're on). If called gotos('\0') won't omit one.
     If called gotos('z') will omit 'Top'. ***/

void gotos(FILE *outf, char c)
{
  extern char reportorder[];
  extern flag bq, Bq, cq, dq, Dq, eq, fq, hq, Hq, iq, mq, oq, rq, Sq, Wq, xq;

  char *i;

  if (xq) {
    fprintf(outf, "\n\n<p>(<b>Go To</b>");

    if (c != 'z')
      fprintf(outf, ": <a HREF=\"#Top\">Top</a>");

    for (i = reportorder; *i != '\0'; i++) {
      if (c != *i) {   /* o/wise we don't want this one */
	switch(*i) {
	case 'b':
	  if (bq)
	    fprintf(outf, ": <a HREF=\"#Browser\">Browser summary</a>");
	  break;
	case 'B':
	  if (Bq)
	    fprintf(outf, ": <a HREF=\"#FullBrowser\">Browser report</a>");
	  break;
	case 'c':
	  if (cq)
	    fprintf(outf, ": <a HREF=\"#Status\">Status code report</a>");
	  break;
	case 'd':
	  if (dq)
	    fprintf(outf, ": <a HREF=\"#Daily\">Daily summary</a>");
	  break;
	case 'D':
	  if (Dq)
	    fprintf(outf, ": <a HREF=\"#FullDaily\">Daily report</a>");
	  break;
	case 'e':
	  if (eq)
	    fprintf(outf, ": <a HREF=\"#Error\">Error report</a>");
	  break;
	case 'f':
	  if (fq)
	    fprintf(outf, ": <a HREF=\"#Referer\">Referer report</a>");
	  break;
	case 'H':
	  if (Hq)
	    fprintf(outf, ": <a HREF=\"#FullHourly\">Hourly report</a>");
	  break;
	case 'h':
	  if (hq)
	    fprintf(outf, ": <a HREF=\"#Hourly\">Hourly summary</a>");
	  break;
	case 'i':
	  if (iq)
	    fprintf(outf, ": <a HREF=\"#Directory\">Directory report</a>");
	  break;
	case 'm':
	  if (mq)
	    fprintf(outf, ": <a HREF=\"#Monthly\">Monthly report</a>");
	  break;
	case 'o':
	  if (oq)
	    fprintf(outf, ": <a HREF=\"#Domain\">Domain report</a>");
	  break;
	case 'r':
	  if (rq)
	    fprintf(outf, ": <a HREF=\"#Request\">Request report</a>");
	  break;
	case 'S':
	  if (Sq)
	    fprintf(outf, ": <a HREF=\"#Host\">Host report</a>");
	  break;
	case 'W':
	  if (Wq)
	    fprintf(outf, ": <a HREF=\"#Weekly\">Weekly report</a>");
	  break;

	}   /* end switch */
      }     /* end if this i wanted */
    }       /* end for i */

    fprintf(outf, ")\n");

  }         /* end if xq */
}           /* end function gotos() */

/*** Next, to print strings with HTML reserved characters translated ***/

void htmlputc(char c, FILE *outf)
{
  if (c == '<')
    fprintf(outf, "&lt;");
  else if (c == '>')
    fprintf(outf, "&gt;");
  else if (c == '&')
    fprintf(outf, "&amp;");
  else if (c == '"')
    fprintf(outf, "&quot;");
  else
    putc(c, outf);
}

void htmlfprintf(FILE *outf, char string[MAXSTRINGLENGTH])
{
  char *c;

  for (c = string; *c != '\0'; c++)
    htmlputc(*c, outf);

}

/*** Now a little routine to find the correct divider for large numbers of
     bytes. Also sets bprefix[0] as a side effect. ***/

double finddivider(double bytes, char *bprefix)
{
  extern flag rawbytes;

  double bdivider;

  if (rawbytes)
    bdivider = 1.0;
  else
    for (bdivider = 1; bytes / bdivider >= 999999.5;
	 bdivider *= 1024)
      ;  /* run bdivider to right multiplier */

  if (bdivider == 1.0)
    *bprefix = '\0';
  else if (bdivider == 1024.0)
    *bprefix = 'k';
  else if (bdivider == 1048576.0)
    *bprefix = 'M';
  else if (bdivider == 1073741824.0)
    *bprefix = 'G';
  else if (bdivider == 1099511627776.0)
    *bprefix = 'T';
  else       /* 10^6 terabytes should be enough. Just about. */
    *bprefix = '?';

  return(bdivider);
}

/*** print a line across the page, assuming ASCII mode ***/

void asciiline(FILE *outf)
{
  extern int pagewidth;

  int i;

  for (i = 0; i < pagewidth; i++)
    fprintf(outf, "-");
  fprintf(outf, "\n\n");
}

/*** a barchart bar, length n, within <pre><tt> ***/

void barplot(FILE *outf, int n)
{
  extern int aq;
  extern flag graphical;
  extern char imagedir[];
  extern char markchar;

  int i, k;
  flag first = TRUE;

  if (aq || !graphical) {
    for ( ; n > 0; n--)
      fprintf(outf, "%c", markchar);
  }

  else {
    for (k = 32; k >= 1; k /= 2) {
      while (n >= k) {
	fprintf(outf, "<img src=\"");
	htmlfprintf(outf, imagedir);
	fprintf(outf, "bar%d.gif\" alt=\"", k);
	if (first) {
	  for (i = n; i > 0; i--)
	    htmlputc(markchar, outf);
	  first = FALSE;
	}
	fprintf(outf, "\">");
	n -= k;
      }
    }
  }
}

/*** A nasty header bit. Return rough floor -- accurate if negative. ***/

int whatincluded(FILE *outf, int sortby, char *minreqstr, char *minbytestr,
		 char singular[20], char plural[21], flag subdoms)
{
  extern double bytefloor();         /* in hash.c */
  extern int reqfloor();             /* in hash.c */
  extern void doublefprintf();       /* in utils.c */

  extern double total_bytes;

  int genfloor;
  int tempint;
  char tempc;

  if (sortby == BYBYTES) {
    if (minbytestr[0] == '-') {
      genfloor = (int)bytefloor(total_bytes, minbytestr);
      if (genfloor == -1)
	fprintf(outf, "Printing the first %s", singular);
      else
	fprintf(outf, "Printing the first %d %s", -genfloor, plural);
    }
    else {
      fprintf(outf, "Printing all %s", plural);
      genfloor = (int)(ceil(bytefloor(total_bytes, minbytestr)));
      if (genfloor > 0) {
	fprintf(outf, " with at least ");
	tempint = MAX((int)strlen(minbytestr) - 1, 0);
	if (minbytestr[tempint] == '%') {
	  minbytestr[tempint] = '\0';
	  doublefprintf(outf, atof(minbytestr));
	  fprintf(outf, "%% of the traffic");
	}
	else if (minbytestr[tempint] == 'k' || minbytestr[tempint] == 'M' ||
		 minbytestr[tempint] == 'G' || minbytestr[tempint] == 'T') {
	  tempc = minbytestr[tempint];
	  minbytestr[tempint] = '\0';
	  doublefprintf(outf, atof(minbytestr));
	  fprintf(outf, " %cbytes of traffic", tempc);
	}
	else {
	  doublefprintf(outf, atof(minbytestr));
	  fprintf(outf, " bytes of traffic");
	}
      }
    }
    if (subdoms)
      fprintf(outf, ".\n");
    else
      fprintf(outf, ",%ssorted by amount of traffic.\n",
	      (genfloor > 0)?"\n  ":" ");
  }
  else {   /* sortby not BYBYTES */
    genfloor = reqfloor(total_succ_reqs, minreqstr);
    if (minreqstr[0] == '-') {
      if (genfloor == -1)
	fprintf(outf, "Printing the first %s", singular);
      else
	fprintf(outf, "Printing the first %d %s", -genfloor, plural);
    }
    else {
      fprintf(outf, "Printing all %s", plural);
      if (genfloor > 0) {
	fprintf(outf, " with at least ");
	tempint = MAX((int)strlen(minreqstr) - 1, 0);
	if (minreqstr[tempint] == '%') {
	  minreqstr[tempint] = '\0';
	  doublefprintf(outf, atof(minreqstr));
	  fprintf(outf, "%% of the requests");
	}
	else
	  fprintf(outf, "%d request%s", atoi(minreqstr),
		  atoi(minreqstr) == 1?"":"s");
      }
    }
    if (subdoms)
      fprintf(outf, ".\n");
    else if (sortby == BYREQUESTS)
      fprintf(outf, ",%ssorted by number of requests.\n",
	      (genfloor > 0)?"\n  ":" ");
    else if (sortby == ALPHABETICAL)
      fprintf(outf, ",%ssorted alphabetically.\n", (genfloor > 0)?"\n  ":" ");
    else
      fprintf(outf, ", unsorted.\n");
  }

  return(genfloor);

}


/*** Generic output function for generic objects ***/

void genout(FILE *outf, struct genstruct *sorthead, int sortby,
	    char *minreqstr, char *minbytestr, int max_reqs, double max_bytes,
	    char *wantcols, char anchor[10], char title[17], char singular[13],
	    char plural[14], char codeletter, flag alphahost,
	    /* alphabetical host sort? */   flag byq, int kq, /* pagelinks? */
	    char baseurl[MAXSTRINGLENGTH]) {

  extern char *reversehostname();    /* in alias.c */
  extern flag included();            /* in alias.c */

  extern int pagewidth;
  extern int dirlevel;
  extern int host_max_length;
  extern int aq;
  extern flag rawbytes;
  extern double total_bytes;
  extern struct include *ispagehead;

  struct genstruct *p;
  int fieldwidth, bfieldwidth, graphwidth;
  int genfloor;
  double bdivider;
  char bprefix[2];
  char *cols;
  double pc;
  int pc1, pc2;
  int i, j, tempint;
  char *tempc;
  
  bprefix[0] = '\0';
  bprefix[1] = '\0';

  if (!aq) {
    fprintf(outf,
	    "\n\n<hr>\n<h2><a NAME=\"%s\">%s</a></h2>\n\n", anchor, title);
    gotos(outf, codeletter);
    fprintf(outf, "<p>");
  }
  else {
    fprintf(outf, "%s\n", title);
    for (tempc = title; *tempc != '\0'; tempc++)
      fprintf(outf, "-");
    fprintf(outf, "\n");
  }

  genfloor = whatincluded(outf, sortby, minreqstr, minbytestr, singular,
			  plural, FALSE);
  if (codeletter == 'i') {
    if (!aq)
      fprintf(outf, "<br>");
    fprintf(outf, "Printing directories to depth %d.\n", dirlevel);
  }

  if (aq)
    fprintf(outf, "\n");
  else
    fprintf(outf, "<pre>");


  tempint = 10000;
  for (fieldwidth = 5; max_reqs / tempint >= 10; fieldwidth++)
    tempint *= 10;

  if (byq) {
    if (rawbytes) {
      tempint = 100000;
      for (bfieldwidth = 6; max_bytes / tempint >= 10; bfieldwidth++)
	tempint *= 10;
    }
    else
      bfieldwidth = 6;

    bdivider = finddivider(max_bytes, bprefix);
  }

  for (cols = wantcols; *cols != '\0'; cols++) {
    switch(*cols) {
    case 'R':
      for (i = 5; i < fieldwidth; i++)
	fprintf(outf, " ");
      fprintf(outf, "#reqs: ");
      break;
    case 'r':
      fprintf(outf, " %%reqs: ");
      break;
    case 'B':
      if (byq) {
	for (i = 6; i < bfieldwidth; i++)
	  fprintf(outf, " ");
	fprintf(outf, "%sbytes: ", bprefix[0] == '\0'?" ":bprefix);
      }
      break;
    case 'b':
      if (byq)
	fprintf(outf, "%%bytes: ");
      break;
    }
  }
  fprintf(outf, "%s\n", singular);
  
  for (cols = wantcols; *cols != '\0'; cols++) {
    switch(*cols) {
    case 'R':
      for (i = 1; i <= fieldwidth; i++)
	fprintf(outf, "-");
      fprintf(outf, "  ");
      break;
    case 'r':
      fprintf(outf, "------  ");
      break;
    case 'B':
      if (byq) {
	for (i = 1; i <= bfieldwidth; i++)
	  fprintf(outf, "-");
	fprintf(outf, "  ");
      }
      break;
    case 'b':
      if (byq)
	fprintf(outf, "------  ");
      break;
    }
  }
  for (tempc = singular; *tempc != '\0'; tempc++)
    fprintf(outf, "-");
  fprintf(outf, "\n");

  if (genfloor < 0)
    j = genfloor;
  else j = 1;

  if (alphahost) {
    graphwidth = pagewidth;
    for (cols = wantcols; *cols != '\0'; cols++) {
      switch(*cols) {
      case 'R':
	graphwidth -= fieldwidth + 2;
	break;
      case 'B':
	graphwidth -= bfieldwidth + 2;
	break;
      case 'r':
      case 'b':
	graphwidth -= 8;
	break;
      }
    }
    graphwidth = MIN(graphwidth, host_max_length);
  }

  for(p = sorthead; p -> name != NULL && (j++) != 0;
      p = p -> next) {

    for (cols = wantcols; *cols != '\0'; cols++) {
      switch(*cols) {
      case 'R':
	fprintf(outf, "%*d: ", fieldwidth, p -> reqs);
	break;
      case 'r':
	pc = (p -> reqs + 0.0) / ((total_succ_reqs + 0.0) / 10000);
	pc1 = ((int)(pc + 0.5)) / 100;     /* whole no. of %reqs */
	pc2 = ((int)(pc + 0.5)) % 100;     /* remaining 100ths. */
	if (pc1 == 100)
	  fprintf(outf, "  100%%: ");
	else if (pc1 > 0 || pc2 > 0)
	  fprintf(outf, "%2d.%02d%%: ", pc1, pc2);
	else
	  fprintf(outf, "      : ");
	break;
      case 'B':
	if (byq) {
	  if (p -> bytes / bdivider > 0.5)
	    fprintf(outf, "%*.0lf", bfieldwidth, p -> bytes / bdivider);
	  else for (i = 0; i < bfieldwidth; i++)
	    fprintf(outf, " ");
	  fprintf(outf, ": ");
	}
	break;
      case 'b':
	if (byq) {
	  pc = p -> bytes / (total_bytes / 10000);
	  pc1 = ((int)(pc + 0.5)) / 100;    /* whole no. of %bytes */
	  pc2 = ((int)(pc + 0.5)) % 100;    /* remaining 100ths. */
	  if (pc1 == 100)
	    fprintf(outf, "  100%%: ");
	  else if (pc1 > 0 || pc2 > 0)
	    fprintf(outf, "%2d.%02d%%: ", pc1, pc2);
	  else
	    fprintf(outf, "      : ");
	}
	break;
      }
    }
    
    if (alphahost && !isdigit(p -> name[0])) {  /* we've swapped the names */
      reversehostname(p -> name);
      /* Also in that case right align names */
      for (i = graphwidth - (int)strlen(p -> name); i > 0; i--)
	fprintf(outf, " ");
    }

    if ((kq == 2) ||
	/* if we want to link to everything ... */
	(kq == 1 && included(p -> name, ispagehead))) {
      /* or it is a page, and we want to link to pages */
      fprintf(outf, "<a HREF=\"");
      htmlfprintf(outf, baseurl);
      htmlfprintf(outf, p -> name);
      fprintf(outf, "\">");
      htmlfprintf(outf, p -> name);
      fprintf(outf, "</a>");
    }
    else   /* (the usual case for most reports) */
      if (!aq)
	htmlfprintf(outf, p -> name);
      else
	fprintf(outf, p -> name);
    fprintf(outf, "\n");

  }
      
  if (aq)
    asciiline(outf);
  else
    fprintf(outf, "</pre>");
	
}

/*** The domain report is similar to the generic ones. It differs in that
     the domains are stored in a different structure, and that subdomains
     must be printed. ***/

void domout(FILE *outf, int firstdom)
{
  extern struct domain *domainhead[];
  extern int aq;
  extern flag byq, rawbytes;
  extern int domsortby;
  extern char domminbytestr[], domminreqstr[];
  extern char subdomminbytestr[], subdomminreqstr[];
  extern int dom_max_reqs;
  extern double dom_max_bytes;
  extern int subonumber;
  extern char domcols[];
  extern double total_bytes;

  int domfloor;

  struct domain *domp;
  double bdivider;
  char bprefix[2];
  char *cols;
  int fieldwidth, bfieldwidth;
  double pc;
  int pc1, pc2;
  int i, j, k, tempint;
  char *tempp;

  bprefix[0] = '\0';
  bprefix[1] = '\0';

  if (!aq) {
    fprintf(outf,
	    "\n\n<hr>\n<h2><a NAME=\"Domain\">Domain Report</a></h2>\n\n");
    gotos(outf, 'o');
  }
  else {
    fprintf(outf, "Domain Report\n");
    fprintf(outf, "-------------\n");
  }
  
  if (!aq)
    fprintf(outf, "<p>");

  domfloor = whatincluded(outf, domsortby, domminreqstr, domminbytestr,
			  "domain", "domains", FALSE);
  if (subonumber > 0) {
    if (!aq)
      fprintf(outf, "<br>");
    whatincluded(outf, domsortby, subdomminreqstr, subdomminbytestr,
		 "requested subdomain", "requested subdomains", TRUE);
  }

  if (aq)
    fprintf(outf, "\n");
  else
    fprintf(outf, "<pre>");
  
  tempint = 10000;
  for (fieldwidth = 5; dom_max_reqs / tempint >= 10; fieldwidth++)
    tempint *= 10;
  
  if (byq) {
    if (rawbytes) {
      tempint = 100000;
      for (bfieldwidth = 6; dom_max_bytes / tempint >= 10; bfieldwidth++)
	tempint *= 10;
    }
    else
      bfieldwidth = 6;

    bdivider = finddivider(dom_max_bytes, bprefix);
  }
  
  for (cols = domcols; *cols != '\0'; cols++) {
    switch(*cols) {
    case 'R':
      for (i = 5; i < fieldwidth; i++)
	fprintf(outf, " ");
      if (subonumber > 0)
	fprintf(outf, " #reqs : ");
      else
	fprintf(outf, "#reqs: ");
      break;
    case 'r':
      if (subonumber > 0)
	fprintf(outf, "  %%reqs : ");
      else
	fprintf(outf, " %%reqs: ");
      break;
    case 'B':
      if (byq) {
	for (i = 6; i < bfieldwidth; i++)
	  fprintf(outf, " ");
	if (subonumber > 0)
	  fprintf(outf, " %sbytes : ", bprefix[0] == '\0'?" ":bprefix);
	else
	  fprintf(outf, "%sbytes: ", bprefix[0] == '\0'?" ":bprefix);
      }
      break;
    case 'b':
      if (byq) {
	if (subonumber > 0)
	  fprintf(outf, " %%bytes : ");
	else
	  fprintf(outf, "%%bytes: ");
      }
      break;
    }
  }
  fprintf(outf, "domain\n");

  for (cols = domcols; *cols != '\0'; cols++) {
    switch(*cols) {
    case 'R':
      for (i = 1; i <= fieldwidth; i++)
	fprintf(outf, "-");
      if (subonumber > 0)
	fprintf(outf, "--");
      fprintf(outf, "  ");
      break;
    case 'r':
      if (subonumber > 0)
	fprintf(outf, "--");
      fprintf(outf, "------  ");
      break;
    case 'B':
      if (byq) {
	for (i = 1; i <= bfieldwidth; i++)
	  fprintf(outf, "-");
	if (subonumber > 0)
	  fprintf(outf, "--");
	fprintf(outf, "  ");
      }
      break;
    case 'b':
      if (byq) {
	if (subonumber > 0)
	  fprintf(outf, "--");
	fprintf(outf, "------  ");
      }
	    break;
    }
 } 
  fprintf(outf, "------\n");

  if (domfloor < 0)
    j = domfloor;
  else j = 1;

  for (i = firstdom; i >= 0 && (j++) != 0; i = domainhead[i] -> nexti) {

    if (!(i == DOMHASHSIZE - 2 && domainhead[i] -> reqs == -1)) {

      for (cols = domcols; *cols != '\0'; cols++) {
	switch(*cols) {
	case 'R':
	  if (subonumber > 0)
	    fprintf(outf, " %*d : ", fieldwidth,
		    domainhead[i] -> reqs);
	  else
	    fprintf(outf, "%*d: ", fieldwidth, domainhead[i] -> reqs);
	  break;
	case 'r':
	  pc = (domainhead[i] -> reqs + 0.0) / 
	    ((total_succ_reqs + 0.0) / 10000);
	  pc1 = ((int)(pc + 0.5)) / 100;     /* whole no. of %reqs */
	  pc2 = ((int)(pc + 0.5)) % 100;     /* remaining 100ths. */
	  if (subonumber > 0)
	    fprintf(outf, " ");
	  if (pc1 == 100)
	    fprintf(outf, "  100%%");
	  else if (pc1 > 0 || pc2 > 0)
	    fprintf(outf, "%2d.%02d%%", pc1, pc2);
	  else
	    fprintf(outf, "      ");
	  if (subonumber > 0)
	    fprintf(outf, " : ");
	  else
	    fprintf(outf, ": ");
	  break;
	case 'B':
	  if (byq) {
	    if (domainhead[i] -> bytes / bdivider > 0.5) {
	      if (subonumber > 0)
		fprintf(outf, " %*.0lf ", bfieldwidth,
			domainhead[i] -> bytes / bdivider);
	      else
		fprintf(outf, "%*.0lf", bfieldwidth,
			domainhead[i] -> bytes / bdivider);
	    }
	    else for (k = 0; k < bfieldwidth + 2 * (subonumber > 0); k++)
		fprintf(outf, " ");
	    fprintf(outf, ": ");
	  }
	  break;
	case 'b':
	  if (byq) {
	    pc = domainhead[i] -> bytes / (total_bytes / 10000);
	    pc1 = ((int)(pc + 0.5)) / 100;    /* whole no. of %bytes */
	    pc2 = ((int)(pc + 0.5)) % 100;    /* remaining 100ths. */
	    if (subonumber > 0)
	      fprintf(outf, " ");
	    if (pc1 == 100)
	      fprintf(outf, "  100%%");
	    else if (pc1 > 0 || pc2 > 0)
	      fprintf(outf, "%2d.%02d%%", pc1, pc2);
	    else
	      fprintf(outf, "      ");
	    if (subonumber > 0)
	      fprintf(outf, " : ");
	    else
	      fprintf(outf, ": ");
	  }
	  break;
	}
      }
      
      if (domainhead[i] -> id[0] == '*')
	/* flagged domains, not real domain names */
	fprintf(outf, "[%s]\n", domainhead[i] -> name);
      else if (domainhead[i] -> name[0] == '?')
	/* real domain, but don't print name */
	fprintf(outf, ".%s\n", domainhead[i] -> id);
      else
	fprintf(outf, ".%s (%s)\n", domainhead[i] -> id,
		domainhead[i] -> name);
      
      /* Now print its subdomains too. */
      
      for (domp = domainhead[i] -> next; domp -> name != NULL;
	   domp = domp -> next) {
	
	for (cols = domcols; *cols != '\0'; cols++) {
	  switch(*cols) {
	  case 'R':
	    fprintf(outf, "(%*d): ", fieldwidth, domp -> reqs);
	    break;
	  case 'r':
	    pc = (domp -> reqs + 0.0) /
	      ((total_succ_reqs + 0.0) / 10000);
	    pc1 = ((int)(pc + 0.5)) / 100;     /* whole no. of %reqs */
	    pc2 = ((int)(pc + 0.5)) % 100;     /* remaining 100ths. */
	    if (pc1 == 100)
	      fprintf(outf, "(  100%%): ");
	  else if (pc1 > 0 || pc2 > 0)
	    fprintf(outf, "(%2d.%02d%%): ", pc1, pc2);
	  else
	    fprintf(outf, "        : ");
	    break;
	  case 'B':
	    if (byq) {
	      if (domp -> bytes / bdivider > 0.5)
		fprintf(outf, "(%*.0lf)", bfieldwidth,
			domp -> bytes / bdivider);
	      else for (k = 0; k < bfieldwidth + 2; k++)
		fprintf(outf, " ");
	      fprintf(outf, ": ");
	    }
	    break;
	  case 'b':
	    if (byq) {
	      pc = domp -> bytes / (total_bytes / 10000);
	      pc1 = ((int)(pc + 0.5)) / 100;  /* whole no. of %bytes */
	      pc2 = ((int)(pc + 0.5)) % 100;  /* remaining 100ths. */
	      if (pc1 == 100)
		fprintf(outf, "(  100%%): ");
	      else if (pc1 > 0 || pc2 > 0)
		fprintf(outf, "(%2d.%02d%%): ", pc1, pc2);
	      else
		fprintf(outf, "        : ");
	    }
	    break;
	  }
	}
	
	tempp = domp -> id;
	while ((tempp = strchr(tempp, '.')) != NULL) {
	  fprintf(outf, "  "); 
	  /* print two spaces for each dot in name */
	  tempp++;
	}
	if (i == DOMHASHSIZE - 1)
	  fprintf(outf, "  ");  /* + 2 more for numerical domains */
	fprintf(outf, "%s", domp -> id);
	
	if (domp -> name[0] != '?')    /* print name */
	  fprintf(outf, " (%s)", domp -> name);
	
	fprintf(outf, "\n");
	
      }    /* end for domp */
	
    }

  }   /* end for (i = running over domains) */
    
  if (aq)
    asciiline(outf);
  else
    fprintf(outf, "</pre>");
	
}

/*** The date reports aren't quite generic enough to combine completely,
     but we can go a long way towards it. ***/
/*** First a function for printing out the headers of a report and finding
     the fieldwidths etc.; then one for printing out each individual line. ***/

void datehead(FILE *outf, int maxreq, double maxbytes,
	      char *wantcols, char *graphtype, char anchor[11],
	      char title[15], char colhead[13], char codeletter,
	      int *unit, int *fieldwidth, int *bfieldwidth, int *graphwidth,
	      double *bdivider)  /* NB: colhead: inc. leading spaces. */
              /* The last 5 args are returned altered */
{
  extern void int3printf();         /* in utils.c */

  extern int aq;
  extern flag byq, rawbytes;
  extern int pagewidth;
  extern char imagedir[];
  extern char markchar;

  char *cols;
  char bprefix[2];
  int i, j, tempint;
  char *tempc;

  bprefix[0] = '\0';
  bprefix[1] = '\0';

  if (*graphtype == 'b')
    *graphtype = 'B';

  if (!aq) {
    fprintf(outf, "<hr>\n<h2><a NAME=\"%s\">%s</a></h2>\n", anchor, title);
	  gotos(outf, codeletter);
  }
  else {
    fprintf(outf, "%s\n", title);
    for (tempc = title; *tempc != '\0'; tempc++)
      fprintf(outf, "-");
    fprintf(outf, "\n");
  }
    
  tempint = 10000;
  for (*fieldwidth = 5; maxreq / tempint >= 10; (*fieldwidth)++)
    tempint *= 10;   /* so fieldwidth is log_10(maxreq), but >= 5 */
	
  if (byq) {
    if (rawbytes || (*graphtype == 'B' && *unit > 0)) {
      tempint = 100000;
      for (*bfieldwidth = 6; maxbytes / tempint >= 10; (*bfieldwidth)++)
	tempint *= 10;
    }
    else
      *bfieldwidth = 6;

    *bdivider = finddivider(maxbytes, bprefix);
  }

  if (*unit <= 0) {   /* (o/wise just use the given amount) */

    /* Calculate the graphwidth */
    *graphwidth = pagewidth - (int)strlen(colhead) - 2;
    for (cols = wantcols; *cols != '\0'; cols++) {
      switch(*cols) {
      case 'R':
	*graphwidth -= *fieldwidth + 2;
	break;
      case 'B':
	*graphwidth -= *bfieldwidth + 2;
	break;
      case 'r':
      case 'b':
	*graphwidth -= 8;
	break;
      }
    }
    *graphwidth = MAX(*graphwidth, MINGRAPHWIDTH);  /* must be >= MGW wide */
	                                  
    if (*graphtype == 'B')
      *unit = (maxbytes - 1) / (*bdivider * *graphwidth);
    else
      *unit = (maxreq - 1) / *graphwidth;
                   	        /* except we want a 'nice' amount, so ... */
	             /* (Nice amount is 1, 1.5, 2, 2.5, 3, 4, 5, 6, 8 * 10^n */

    j = 0;
    while (*unit > 24) {
      *unit /= 10;
      j++;
    }
    if (*unit == 6)
      *unit = 7;
    else if (*unit == 8)
      *unit = 9;
    else if (*unit >= 20)
      *unit = 24;
    else if (*unit >= 15)
      *unit = 19;
    else if (*unit >= 10)
      *unit = 14;
    (*unit)++;
    for (i = 0; i < j; i++) {
      *unit *= 10;
    }

  }     /* end if (*unit <= 0) */

  else if (*graphtype == 'B') {   /* o/wise unit doesn't make sense */
    *bdivider = 1;
    bprefix[0] = '\0';
  }

  if (!aq) {
    fprintf(outf, "\n<p>Each unit (<tt><img src=\"");
    htmlfprintf(outf, imagedir);
    fprintf(outf, "bar1.gif\" alt=\"");
    htmlputc(markchar, outf);
    fprintf(outf, "\"></tt>) represents ");
    int3printf(outf, *unit);
    if (*graphtype == 'B')
      fprintf(outf, " %sbyte%s, or part thereof.", bprefix,
	      (*unit == 1)?"":"s");
    else
      fprintf(outf, " request%s.", (*unit == 1)?"":"s, or part thereof");
    fprintf(outf, "\n\n<pre width=%d><tt>\n", pagewidth);
  }
  else {
    fprintf(outf, "\nEach unit (%c) represents ", markchar);
    int3printf(outf, *unit);
    if (*graphtype == 'B')
      fprintf(outf, " %sbyte%s, or part thereof.\n\n", bprefix,
	      (*unit == 1)?"":"s");
    else
      fprintf(outf, " request%s.\n\n",
	      (*unit == 1)?"":"s, or part thereof");
  }
	
  fprintf(outf, "%s: ", colhead);
  for (cols = wantcols; *cols != '\0'; cols++) {
    switch(*cols) {
    case 'R':
      for (i = 5; i < *fieldwidth; i++)
	fprintf(outf, " ");
      fprintf(outf, "#reqs: ");
      break;
    case 'r':
      fprintf(outf, " %%reqs: ");
      break;
    case 'B':
      if (byq) {
	for (i = 6; i < *bfieldwidth; i++)
	  fprintf(outf, " ");
	fprintf(outf, "%sbytes: ", bprefix[0] == '\0'?" ":bprefix);
      }
      break;
    case 'b':
      if (byq)
	fprintf(outf, "%%bytes: ");
      break;
    }
  }

  fprintf(outf, "\n");
  for (tempc = colhead; *tempc != '\0'; tempc++)
    fprintf(outf, "-");
  fprintf(outf, "  ");
  for (cols = wantcols; *cols != '\0'; cols++) {
    switch(*cols) {
    case 'R':
      for (i = 1; i <= *fieldwidth; i++)
	fprintf(outf, "-");
      fprintf(outf, "  ");
      break;
    case 'r':
      fprintf(outf, "------  ");
      break;
    case 'B':
      if (byq) {
	for (i = 1; i <= *bfieldwidth; i++)
	  fprintf(outf, "-");
	fprintf(outf, "  ");
      }
      break;
    case 'b':
      if (byq)
	fprintf(outf, "------  ");
      break;
    }
  }
  fprintf(outf, "\n");
}

/* As promised, each separate line. We print name of date in output() though */

void dateline(FILE *outf, int reqs, double bytes, char *wantcols,
	      char graphtype, int fieldwidth, int bfieldwidth,
	      int unit, int bdivider) {
  extern double total_bytes;
  extern flag byq;

  char *cols;
  double pc;
  int pc1, pc2;

  for (cols = wantcols; *cols != '\0'; cols++) {
    switch(*cols) {
    case 'R':
      fprintf(outf, "%*d: ", fieldwidth, reqs);
      break;
    case 'r':
      pc = (reqs + 0.0) / ((total_succ_reqs + 0.0) / 10000);
      pc1 = ((int)(pc + 0.5)) / 100;     /* whole no. of %reqs */
      pc2 = ((int)(pc + 0.5)) % 100;     /* remaining 100ths. */
      if (pc1 == 100)
	fprintf(outf, "  100%%: ");
      else if (pc1 > 0 || pc2 > 0)
	fprintf(outf, "%2d.%02d%%: ", pc1, pc2);
      else
	fprintf(outf, "      : ");
      break;
    case 'B':
      if (byq)
	fprintf(outf, "%*.0lf: ", bfieldwidth, bytes / bdivider);
      break;
    case 'b':
      if (byq) {
	pc = bytes / (total_bytes / 10000);
	pc1 = ((int)(pc + 0.5)) / 100;    /* whole no. of %bytes */
	pc2 = ((int)(pc + 0.5)) % 100;    /* remaining 100ths. */
	if (pc1 == 100)
	  fprintf(outf, "  100%%: ");
	else if (pc1 > 0 || pc2 > 0)
	  fprintf(outf, "%2d.%02d%%: ", pc1, pc2);
	else
	  fprintf(outf, "      : ");
      }
      break;
    }
  }
  if (graphtype == 'B')
    barplot(outf, (int)(ceil(bytes / (unit * bdivider))));
  else
    barplot(outf, (reqs == 0)?0:((reqs - 1) / unit) + 1);
  fprintf(outf, "\n");
}

/*** The status code report (very simple) ***/

void statusout(FILE *outf)
{
  extern int status[], statusnos[];
  extern char statusstrs[NO_STATUS][MAXSTATUSLENGTH];
  extern int aq;

  int fieldwidth;
  int maxreqs = 0;
  int i;

  for (i = 0; i < NO_STATUS; i++)
    maxreqs = MAX(maxreqs, status[i]);

  if (!aq) {
    fprintf(outf, "\n\n<hr>\n<h2><a NAME=\"Status\">Status Code Report</a></h2>\n\n");
    gotos(outf, 'c');
    fprintf(outf, "<pre>");
  }
  else
    fprintf(outf, "Status Code Report\n------------------\n\n");

  i = 10000;
  for (fieldwidth = 5; maxreqs / i >= 10; fieldwidth++)
    i *= 10;

  for (i = 5; i < fieldwidth; i++)
    fprintf(outf, " ");
  fprintf(outf, "#occs: no. description\n");
  for (i = 0; i < fieldwidth; i++)
    fprintf(outf, "-");
  fprintf(outf, "  ---------------\n");

  for (i = 0; i < NO_STATUS; i++) {
    if (status[i] > 0) {
      if (statusstrs[i][0] == '0')
	fprintf(outf, "%*d:     %s\n", fieldwidth, status[i], statusstrs[i]);
      else
	fprintf(outf, "%*d: %d %s\n", fieldwidth, status[i], statusnos[i],
		statusstrs[i]);
    }
  }

  if (aq)
    asciiline(outf);
  else
    fprintf(outf, "</pre>");
}

/*** Finally in the individual report printing, the error report ***/

void errout(FILE *outf, int errorder[NO_ERRS])
{
  extern int errors[NO_ERRS];
  extern char errs[NO_ERRS][MAXERRLENGTH];
  extern int errminreqs;
  extern int aq;

  int fieldwidth;
  int i;

  if (errors[errorder[0]] >= errminreqs) {    /* o/wise no report */

    if (!aq) {
      fprintf(outf,
	      "\n\n<hr>\n<h2><a NAME=\"Error\">Error Report</a></h2>\n\n");
      gotos(outf, 'e');
      fprintf(outf, "<p>");
    }
    else
      fprintf(outf, "Error Report\n------------\n");

    if (errminreqs == 0)
      fprintf(outf, "Printing all possible errors, ");
    else
      fprintf(outf, "Printing all errors with at least %d occurence%s,\n",
	      errminreqs, (errminreqs == 1)?"":"s");
    fprintf(outf, "  sorted by number of occurrences.");

    if (aq)
      fprintf(outf, "\n\n");
    else
      fprintf(outf, "<pre>");

    i = 10000;
    for (fieldwidth = 5; errors[errorder[0]] / i >= 10; fieldwidth++)
      i *= 10;

    for (i = 5; i < fieldwidth; i++)
      fprintf(outf, " ");
    fprintf(outf, "#occs: error type\n");
    for (i = 0; i < fieldwidth; i++)
      fprintf(outf, "-");
    fprintf(outf, "  ----------\n");

    for (i = 0; errors[errorder[i]] >= errminreqs && i < NO_ERRS; i++)
      fprintf(outf, "%*d: %s\n", fieldwidth, errors[errorder[i]],
	      (errs[errorder[i]][0] == '\0')?"[unknown]":errs[errorder[i]]);

    if (aq)
      asciiline(outf);
    else
      fprintf(outf, "</pre>");
  }
}


/*** Now the main output function which calls all that stuff ***/

void output(struct genstruct *urlsorthead, struct genstruct *dirsorthead,
	    struct genstruct *hostsorthead, int firstdom,
	    struct genstruct *refsorthead, struct genstruct *browsorthead,
	    struct genstruct *fullbrowsorthead, int errorder[])
{
  extern int dayofdate();           /* in utils.c */
  extern int minsbetween();         /* in utils.c */
  extern void int3printf();         /* in utils.c */
  extern void double3printf();      /* in utils.c */

  extern char outfile[];
  extern char dayname[7][4];
  extern char monthname[12][4];
  extern int monthlength[];
  extern char hostname[];
  extern char logourl[];
  extern char hosturl[];
  extern char commandname[];
  extern char headerfile[];
  extern char footerfile[];
  extern char reportorder[];
  extern flag q7, byq, refbyq, browbyq, kq, warnq;
  extern flag mback, Dback, Wback, Hback;
  extern flag xq, dq, Dq, Wq, hq, Hq, mq, Sq, rq, oq, iq, fq, bq, Bq, cq, eq;
  extern int sq, aq;
  extern char starttimestr[];
  extern struct timestruct firsttime, lasttime, oldtime, totime, starttimec;
  extern time_t starttime, stoptime;
  extern int weekbeginson;
  extern struct monthly *firstm, *lastm;
  extern struct weekly *firstw, *lastw;
  extern struct daily *firstd, *lastd;
  extern struct hourly *firsth, *lasth;
  extern int dailyreq[], hourlyreq[];
  extern double dailybytes[], hourlybytes[];
  extern int monthlyunit, weeklyunit, hourlyunit, fullhourlyunit, dailyunit;
  extern int fulldailyunit;
  extern int cachereqs, cachereqs7, corrupt_lines, other_lines;
  extern int no_urls, no_hosts, no_urls7, no_hosts7, no_new_hosts7;
  extern double total_bytes, total_bytes7;
  extern int pagewidth;
  extern int hostsortby, reqsortby, dirsortby;
  extern int refsortby, browsortby, fullbrowsortby;
  extern char hostminreqstr[], urlminreqstr[], dirminreqstr[], refminreqstr[];
  extern char browminreqstr[], fullbrowminreqstr[];
  extern char hostminbytestr[], urlminbytestr[], dirminbytestr[];
  extern char refminbytestr[], browminbytestr[], fullbrowminbytestr[];
  extern char monthgraph, daygraph, fulldaygraph, hourgraph, fullhourgraph;
  extern char weekgraph;
  extern char monthcols[], daycols[], fulldaycols[], hourcols[], weekcols[];
  extern char fullhourcols[], reqcols[], dircols[], hostcols[], refcols[];
  extern char browcols[], fullbrowcols[];
  extern char imagedir[], baseurl[];
  extern int reqtype;
  extern int status[], status7[], statusnos[];
  extern int dir_max_reqs, host_max_reqs, url_max_reqs,
             ref_max_reqs, brow_max_reqs, fullbrow_max_reqs;
  extern double dir_max_bytes, host_max_bytes, url_max_bytes;

  FILE *outf;       /* the output file */
  int totalmins;    /* between first and last entries analysed */
  int fieldwidth;   /* Width we require to print #reqs in */
  int bfieldwidth;  /* #bytes ditto */
  char bprefix[2];  /* kilo, Mega, etc. */
  int graphwidth;   /* the width left for a graph after columns written */
  struct monthly *mp;
  struct daily *dp;
  struct weekly *wp;
  struct hourly *hp;
  int maxreq;       /* within a particular date report */
  double maxbytes;
  double bdivider;
  int year, monthno, date;
  flag finished;
  int i, j, firsti, lasti;
  char *ro;
  char tempstr[MAXSTRINGLENGTH];
  FILE *tempf;

  bprefix[0] = '\0';
  bprefix[1] = '\0';

  total_succ_reqs = cachereqs;
  total_fail_reqs = 0;
  total_other_reqs = 0;
  total_succ_reqs7 = cachereqs7;
  total_fail_reqs7 = 0;
  total_other_reqs7 = 0;
  for (i = 0; i < NO_STATUS; i++) {
    if (statusnos[i] <= 299 || statusnos[i] == 304) {
      total_succ_reqs += status[i];
      total_succ_reqs7 += status7[i];
    }
    else if (statusnos[i] <= 399) {
      total_other_reqs += status[i];
      total_other_reqs7 += status7[i];
    }
    else {
      total_fail_reqs += status[i];
      total_fail_reqs7 += status7[i];
    }
  }

  if (STREQ(outfile, "stdout"))
    outf = stdout;

  else if ((outf = fopen(outfile, "w")) == NULL) {
    fprintf(stderr, "%s: Error: failed to open output file %s for writing.\n",
	    commandname, outfile);
    exit(ERR);  /* shouldn't get here because also tested at the beginning */
  }             /* (unless it's vanished in the meantime or something) */

  if (aq == CACHE)
    fprintf(outf,
	    "CACHE type 1 produced by analog%s. Do not modify or delete!",
	    VERSION);
  else {
    if (!aq) {
      fprintf(outf, "<html>\n<head><title>Web Server Statistics for ");
      htmlfprintf(outf, hostname);
      fprintf(outf, "</title></head>\n");
      fprintf(outf, "<body>\n<h1><a NAME=\"Top\">");
      if (!STREQ(logourl, "none")) {
	fprintf(outf, "<IMG src=\"");
	htmlfprintf(outf, logourl);
	fprintf(outf, "\" alt=\"\"> ");
      }
      if (hosturl[0] == '-') {
	fprintf(outf, "Web Server Statistics</a> for ");
	htmlfprintf(outf, hostname);
      }
      else {
	fprintf(outf, "Web Server Statistics</a> for <a HREF=\"");
	htmlfprintf(outf, hosturl);
	fprintf(outf, "\">");
	htmlfprintf(outf, hostname);
	fprintf(outf, "</a>");
      }
      fprintf(outf, "</h1>\n\n");
    }
    else {
      fprintf(outf, "Web Server Statistics for %s\n", hostname);
      fprintf(outf, "==========================");
      for (i = (int)strlen(hostname); i > 0; i--)
	fprintf(outf, "=");
      fprintf(outf, "\n");
    }
    
    /* insert header file */
    
    headerfile[MAXSTRINGLENGTH - 5] = '\0';  /* for safety */
    
    if (!STREQ(headerfile, "none")) {
      if ((tempf = fopen(headerfile, "r")) == NULL) {
	if (warnq)
	  fprintf(stderr,
		  "%s: Warning: Failed to open headerfile %s: ignoring it.\n",
		  commandname, headerfile);
      }
      else {  /* can open header file */
	if (!aq)
	  fprintf(outf, "<hr>");
	else
	  fprintf(outf, "\n");
	
	while(fgets(tempstr, MAXLINELENGTH, tempf) != NULL)
	  fprintf(outf, "%s", tempstr);
	fclose(tempf);
	if (tempstr[(int)strlen(tempstr) - 1] != '\n')
	  fprintf(outf, "\n");
	
      if (aq) {
	for (i = 0; i < pagewidth; i++)
	  fprintf(outf, "-");
      }
	
      fprintf(outf, "\n");
	
      }
    }
  }

  /* Summary statistics */

  if (xq) {

    if (!aq)
      fprintf(outf, "<hr>");

    fprintf(outf,
	    "\nProgram started at %c%c%c-%c%c-%c%c%c-%c%c%c%c %c%c:%c%c local time.\n",
	   starttimestr[0], starttimestr[1], starttimestr[2],
	   (starttimestr[8]==' ')?'0':starttimestr[8], starttimestr[9],
	   starttimestr[4], starttimestr[5], starttimestr[6],
	   starttimestr[20], starttimestr[21], starttimestr[22],
	   starttimestr[23],
	   starttimestr[11], starttimestr[12],
	   starttimestr[14], starttimestr[15]);
  
    if (firsttime.code > oldtime.code)
      q7 = OFF;
  
    if (total_succ_reqs > 0) {
      totalmins = minsbetween(firsttime.date, firsttime.monthno, firsttime.year,
			      firsttime.hr, firsttime.min,
			      lasttime.date, lasttime.monthno, lasttime.year,
			      lasttime.hr, lasttime.min) + 1;
      if (!aq)
	fprintf(outf, "<br>");
      fprintf(outf,
	      "Analysed requests from %s-%02d-%s-%d %02d:%02d to %s-%02d-%s-%d %02d:%02d\n  (%.1f days).\n\n",
	     dayname[dayofdate(firsttime.date,
			       firsttime.monthno, firsttime.year)],
	     firsttime.date, monthname[firsttime.monthno], firsttime.year,
	     firsttime.hr, firsttime.min,
	     dayname[dayofdate(lasttime.date, lasttime.monthno,
			       lasttime.year)],
	     lasttime.date, monthname[lasttime.monthno], lasttime.year,
	     lasttime.hr, lasttime.min,
	     (double)totalmins / 1440.0);
    }

    if (!aq)
      fprintf(outf, "<p><b>Total completed requests:</b> ");
    else
      fprintf(outf, "Total completed requests: ");
    int3printf(outf, total_succ_reqs);
    if (q7) {
      fprintf(outf, " (");
      int3printf(outf, total_succ_reqs7);
      fprintf(outf, ")");
    }
    if (totalmins > 30) {
      if (!aq)
	fprintf(outf, "\n<br><b>Average completed requests per day:</b> ");
      else
	fprintf(outf, "\nAverage completed requests per day: ");
      if (total_succ_reqs < 2)
	fprintf(outf, "0");
      else
	double3printf(outf, ((double)(total_succ_reqs - 1)) * 1440.0 / (totalmins + 0.0));
      if (q7) {
	fprintf(outf, " (");
	int3printf(outf, total_succ_reqs7 / 7);
	fprintf(outf, ")");
      }
    }
    if (total_fail_reqs > 0) {
      if (!aq)
	fprintf(outf, "\n<br><b>Total failed requests:</b> ");
      else
	fprintf(outf, "\nTotal failed requests: ");
      int3printf(outf, total_fail_reqs);
      if (q7) {
	fprintf(outf, " (");
	int3printf(outf, total_fail_reqs7);
	fprintf(outf, ")");
      }
    }
    if (total_other_reqs > 0) {
      if (!aq)
	fprintf(outf, "\n<br><b>Total redirected requests:</b> ");
      else
	fprintf(outf, "\nTotal redirected requests: ");
      int3printf(outf, total_other_reqs);
      if (q7) {
	fprintf(outf, " (");
	int3printf(outf, total_other_reqs7);
	fprintf(outf, ")");
      }
    }
    if (rq) {   /* These data are not collected o/wise (rq => this > 0) */
      if (!aq)
	fprintf(outf, "\n<br><b>Number of distinct files requested:</b> ");
      else
	fprintf(outf, "\nNumber of distinct files requested: ");
      int3printf(outf, no_urls);
      if (q7) {
	fprintf(outf, " (");
	int3printf(outf, no_urls7);
	fprintf(outf, ")");
      }
    }
    if ((sq == ON || sq == APPROX) && no_hosts > 0) {
      if (!aq)
	fprintf(outf, "\n<br><b>%sumber of distinct hosts served:</b> ",
	       (sq == ON)?"N":"Approximate n");
      else
	fprintf(outf, "\n%sumber of distinct hosts served: ",
	       (sq == ON)?"N":"Approximate n");
      int3printf(outf, no_hosts);
      if (q7) {
	fprintf(outf, " (");
	int3printf(outf, no_hosts7);
	fprintf(outf, ")");
	if (!aq)
	  fprintf(outf,
		  "\n<br><b>%sumber of new hosts served in last 7 days:</b> ",
		 (sq == ON)?"N":"Approximate n");
	else
	  fprintf(outf, "\n%sumber of new hosts served in last 7 days: ",
		 (sq == ON)?"N":"Approximate n");
	int3printf(outf, no_new_hosts7);
      }
    }
    if (corrupt_lines > 0) {
      if (!aq)
	fprintf(outf, "\n<br><b>Corrupt logfile lines:</b> ");
      else
	fprintf(outf, "\nCorrupt logfile lines: ");
      int3printf(outf, corrupt_lines);
    }
    if (other_lines > 0) {
      if (!aq)
	fprintf(outf, "\n<br><b>Unwanted logfile entries:</b> ");
      else
	fprintf(outf, "\nUnwanted logfile entries: ");
      int3printf(outf, other_lines);
    }
    if (byq) {
      if (!aq)
	fprintf(outf, "\n<br><b>Total data transferred:</b> ");
      else
	fprintf(outf, "\nTotal data transferred: ");
      bdivider = finddivider(total_bytes, bprefix);
      double3printf(outf, ROUND(total_bytes / bdivider));
      fprintf(outf, " %sbytes", bprefix);
      if (q7) {
	fprintf(outf, " (");
	bdivider = finddivider(total_bytes7, bprefix);
	double3printf(outf, ROUND(total_bytes7 / bdivider));
	fprintf(outf, " %sbytes)", bprefix);
      }
      if (totalmins > 30) {
	if (!aq)
	  fprintf(outf, "\n<br><b>Average data transferred per day:</b> ");
	else
	  fprintf(outf, "\nAverage data transferred per day: ");
	bdivider = finddivider((total_bytes * 1440) / (totalmins + 0.0),
			       bprefix);
	double3printf(outf,
	      ROUND((total_bytes * 1440) / (totalmins + 0.0) / bdivider));
	fprintf(outf, " %sbytes", bprefix);
	if (q7) {
	  fprintf(outf, " (");
	  bdivider = finddivider(total_bytes7 / 7.0, bprefix);
	  double3printf(outf, ROUND(total_bytes7 / 7.0 / bdivider));
	  fprintf(outf, " %sbytes)", bprefix);
	}
      }
    }
    if (q7) {
      if (!aq)
	fprintf(outf, "\n<br>");
      else
	fprintf(outf, "\n");
      fprintf(outf, "(Figures in parentheses refer to the ");
      if (starttimec.code > totime.code)
	fprintf(outf, "7 days to %02d-%s-%4d).", totime.date,
		monthname[totime.monthno], totime.year);
      else
	fprintf(outf, "last 7 days).");
    }

    if (!aq && (mq || Wq || dq || Dq || hq || oq || Sq || iq || rq))
      gotos(outf, 'z');
    if (aq) {
      fprintf(outf, "\n");
      asciiline(outf);
    }

  }   /* end if xq */

  else if (aq == ASCII)
    printf("\n");

  /* Now for the rest of the reports, in reportorder order */

  for (ro = reportorder; *ro != '\0'; ro++) {

    switch(*ro) {

    case 'm':    /* Monthly report */

      if (mq) {

	maxreq = 0;
	maxbytes = 0.0;
	finished = FALSE;
	for (mp = mback?lastm:firstm; !finished; mp = mp -> next) {
	  for (i = 0; i < 12; i++) {
	    maxreq = MAX(maxreq, mp -> reqs[i]);
	    maxbytes = MAX(maxbytes, mp -> bytes[i]);
	  }
	  if (mp == (mback?firstm:lastm))
	    finished = TRUE;
	}

	datehead(outf, maxreq, maxbytes, monthcols, &monthgraph,
		 "Monthly", "Monthly Report", "   month", 'm', &monthlyunit,
		 &fieldwidth, &bfieldwidth, &graphwidth, &bdivider);

	finished = FALSE;
	year = (mback?lasttime:firsttime).year;
	for (mp = mback?lastm:firstm; !finished; mp = mp -> next) {
	  if (mp == firstm) {
	    firsti = firsttime.monthno;
	    if (mback)
	      finished = TRUE;
	  }
	  else
	    firsti = 0;
	  if (mp == lastm) {
	    lasti = lasttime.monthno;
	    if (!mback)
	      finished = TRUE;
	  }
	  else
	    lasti = 11;
	  for (i = mback?lasti:firsti; mback?(i >= firsti):(i <= lasti);
	       i += mback?(-1):1) {  /* run through months in chosen order */
	    fprintf(outf, "%s %d: ", monthname[i], year);
	    dateline(outf, mp -> reqs[i], mp -> bytes[i], monthcols,
		     monthgraph, fieldwidth, bfieldwidth, monthlyunit,
		     bdivider);
	  }
	  year += mback?(-1):1;
	}
	
	if (aq)
	  asciiline(outf);
	else
	  fprintf(outf, "</tt></pre>");
	
      }

      break;
      
    case 'W':      /* Weekly report */

      if (Wq) {

	maxreq = 0;
	maxbytes = 0.0;
	finished = FALSE;
	for (wp = Wback?lastw:firstw; !finished; wp = wp -> next) {
	  maxreq = MAX(maxreq, wp -> reqs);
	  maxbytes = MAX(maxbytes, wp -> bytes);
	  if (wp == (Wback?firstw:lastw))
	    finished = TRUE;
	}

	datehead(outf, maxreq, maxbytes, weekcols, &weekgraph,
		 "Weekly", "Weekly Report", "week beg.", 'W', &weeklyunit,
		 &fieldwidth, &bfieldwidth, &graphwidth, &bdivider);

	finished = FALSE;
	for (wp = Wback?lastw:firstw; !finished; wp = wp -> next) {
	  fprintf(outf, "%2d/%s/%02d: ", wp -> start.date,
		 monthname[wp -> start.monthno], wp -> start.year % 100);
	  dateline(outf, wp -> reqs, wp -> bytes, weekcols, weekgraph,
		   fieldwidth, bfieldwidth, weeklyunit, bdivider);
	  if (wp == (Wback?firstw:lastw))
	    finished = TRUE;
	  
	}     /* end running through weeks */

	if (aq)
	  asciiline(outf);
	else
	  fprintf(outf, "</tt></pre>");
	
      }   /* end if Wq */

      break;

    case 'd':      /* Daily summary */

      if (dq) {
	
	maxreq = 0;
	maxbytes = 0.0;
	for (i = 0; i <= 6; i++) {
	  maxreq = MAX(maxreq, dailyreq[i]);
	  maxbytes = MAX(maxbytes, dailybytes[i]);
	}
	
	datehead(outf, maxreq, maxbytes, daycols, &daygraph,
		 "Daily", "Daily Summary", "day", 'd', &dailyunit,
		 &fieldwidth, &bfieldwidth, &graphwidth, &bdivider);

	for(i = 0; i <= 6; i++) {
	  j = (weekbeginson + i) % 7;
	  fprintf(outf, "%s: ", dayname[j]);
	  dateline(outf, dailyreq[j], dailybytes[j], daycols, daygraph,
		   fieldwidth, bfieldwidth, dailyunit, bdivider);
	}
	
	if (aq)
	  asciiline(outf);
	else
	  fprintf(outf, "</tt></pre>");
	
      }

      break;
      
    case 'D':       /* Full daily report */

      if (Dq) {

	maxreq = 0;
	maxbytes = 0.0;
	finished = FALSE;
	for (dp = Dback?lastd:firstd; !finished; dp = dp -> next) {
	  for (i = 0; i < 31; i++) {
	    maxreq = MAX(maxreq, dp -> reqs[i]);
	    maxbytes = MAX(maxbytes, dp -> bytes[i]);
	  }
	  if (dp == (Dback?firstd:lastd))
	    finished = TRUE;
	}

	datehead(outf, maxreq, maxbytes, fulldaycols, &fulldaygraph,
		 "FullDaily", "Daily Report", "     date", 'D',
		 &fulldailyunit, &fieldwidth, &bfieldwidth, &graphwidth,
		 &bdivider);
	
	finished = FALSE;
	year = (Dback?lasttime:firsttime).year;
	monthno = (Dback?lasttime:firsttime).monthno;
	for (dp = Dback?lastd:firstd; !finished; dp = dp -> next) {
	  if (dp == firstd) {
	    firsti = firsttime.date - 1;
	    if (Dback)
	      finished = TRUE;
	  }
	  else
	    firsti = 0;
	  if (dp == lastd) {
	    lasti = lasttime.date - 1;
	    if (!Dback)
	      finished = TRUE;
	  }
	  else
	    lasti = monthlength[monthno] + ISLEAPFEB(monthno, year) - 1;
	  for (i = Dback?lasti:firsti; Dback?(i >= firsti):(i <= lasti);
	       i += Dback?(-1):1) {  /* run through days in chosen order */
	    fprintf(outf, "%2d/%s/%02d: ", i + 1, monthname[monthno],
		    year % 100);
	    dateline(outf, dp -> reqs[i], dp -> bytes[i], fulldaycols,
		     fulldaygraph, fieldwidth, bfieldwidth, fulldailyunit,
		     bdivider);
	    if (((dayofdate(i + 1, monthno, year) + (!Dback)) % 7 ==
		 weekbeginson) && !(finished && i == (Dback?firsti:lasti)))
	      fprintf(outf, "\n");
                         /* extra blank line after each week (not last) */
	  }

	  if (Dback) {
	    if ((--monthno) == -1) {
	      monthno = 11;
	      --year;
	    }
	  }
	  else {
	    if ((++monthno) == 12) {
	      monthno = 0;
	      ++year;
	    }
	  }
	  
	}     /* end running through dp's */

	if (aq)
	  asciiline(outf);
	else
	  fprintf(outf, "</tt></pre>");
	
      }   /* end if Dq */

      break;

    case 'H':       /* Full hourly report */

      if (Hq) {

	maxreq = 0;
	maxbytes = 0.0;
	finished = FALSE;
	for (hp = Hback?lasth:firsth; !finished; hp = hp -> next) {
	  for (i = 0; i < 24; i++) {
	    maxreq = MAX(maxreq, hp -> reqs[i]);
	    maxbytes = MAX(maxbytes, hp -> bytes[i]);
	  }
	  if (hp == (Hback?firsth:lasth))
	    finished = TRUE;
	}

	if (aq != CACHE)
	  datehead(outf, maxreq, maxbytes, fullhourcols, &fullhourgraph,
		   "FullHourly", "Hourly Report", "     date:hr", 'H',
		   &fullhourlyunit, &fieldwidth, &bfieldwidth, &graphwidth,
		   &bdivider);
	
	finished = FALSE;
	year = (Hback?lasttime:firsttime).year;
	monthno = (Hback?lasttime:firsttime).monthno;
	date = (Hback?lasttime:firsttime).date;
	for (hp = Hback?lasth:firsth; !finished; hp = hp -> next) {
	  if (hp == firsth) {
	    firsti = firsttime.hr;
	    if (Hback)
	      finished = TRUE;
	  }
	  else
	    firsti = 0;
	  if (hp == lasth) {
	    lasti = lasttime.hr;
	    if (!Hback)
	      finished = TRUE;
	  }
	  else
	    lasti = 23;
	  for (i = Hback?lasti:firsti; Hback?(i >= firsti):(i <= lasti);
	       i += Hback?(-1):1) {  /* run through hours in chosen order */
	    if (aq == CACHE) {
	      if (i == 0 || (hp == firsth && i == firsti))
		fprintf(outf, "\n%d%02d%02d%02d", year, monthno + 1, date, i);
	      fprintf(outf, ":%d:%.0lf", hp -> reqs[i], hp -> bytes[i]);
	    }
	    else {
	      fprintf(outf, "%2d/%s/%02d:%02d: ", date, monthname[monthno],
		      year % 100, i);
	      dateline(outf, hp -> reqs[i], hp -> bytes[i], fullhourcols,
		       fullhourgraph, fieldwidth, bfieldwidth, fullhourlyunit,
		       bdivider);
	      if (i == (Hback?0:23) && !finished)
		fprintf(outf, "\n");
	      /* extra blank line after each day (not last) */
	    }
	  }

	  if (Hback) {
	    if ((--date) == 0) {
	      if ((--monthno) == -1) {
		monthno = 11;
		--year;
	      }
	      date = monthlength[monthno] + ISLEAPFEB(monthno, year);
	    }
	  }
	  else {
	    if ((++date) > monthlength[monthno] + ISLEAPFEB(monthno, year)) {
	      if ((++monthno) == 12) {
		monthno = 0;
		++year;
	      }
	      date = 1;
	    }
	  }
	  
	}     /* end running through hp's */

	if (aq == CACHE)
	  fprintf(outf, ":*\n");
	else if (aq)
	  asciiline(outf);
	else if (!aq)
	  fprintf(outf, "</tt></pre>");
	
      }   /* end if Hq */
    
      break;

    case 'h': /* Hourly summary */

      if (hq) {

	maxreq = 0;
	maxbytes = 0.0;
	for (i = 0; i <= 23; i++) {
	  maxreq = MAX(maxreq, hourlyreq[i]);
	  maxbytes = MAX(maxbytes, hourlybytes[i]);
	}

	datehead(outf, maxreq, maxbytes, hourcols, &hourgraph, "Hourly",
		 "Hourly Summary", "hr", 'h', &hourlyunit, &fieldwidth,
		 &bfieldwidth, &graphwidth, &bdivider);
	
	for(i = 0; i <= 23; i++) {
	  fprintf(outf, "%2d: ", i);
	  dateline(outf, hourlyreq[i], hourlybytes[i], hourcols, hourgraph,
		   fieldwidth, bfieldwidth, hourlyunit, bdivider);
	}
	
	if (aq)
	  asciiline(outf);
	else
	  fprintf(outf, "</tt></pre>");
	
      }
      
      break;

    case 'o':    /* Domain report */

      if (oq)
	domout(outf, firstdom);
	
      break;

    case 'S':    /* Host report */
   
      if (Sq)
 	genout(outf, hostsorthead, hostsortby, hostminreqstr, hostminbytestr,
	       host_max_reqs, host_max_bytes, hostcols,
	       "Host", "Host Report", "host", "hosts", 'S',
	       hostsortby == ALPHABETICAL, byq, OFF, "");
      break;
    
    case 'i':   /* Directory report */
   
      if (iq)
	genout(outf, dirsorthead, dirsortby, dirminreqstr, dirminbytestr,
	       dir_max_reqs, dir_max_bytes, dircols, "Directory",
	       "Directory Report", "directory", "directories", 'i', FALSE,
	       byq, OFF, "");
      break;

    case 'r':    /* Request report */
      
      if (rq) {
	if (aq)
	  kq = 0;    /* no links in ASCII output! */
	else if (reqtype == PAGES)
	  kq = 2;    /* If only printing pages, any linked = all linked */
	genout(outf, urlsorthead, reqsortby, urlminreqstr, urlminbytestr,
	       url_max_reqs, url_max_bytes, reqcols, "Request", 
	       "Request Report", (reqtype == PAGES)?"page":"file",
	       (reqtype == PAGES)?"pages":"files", 'r', FALSE, byq, kq,
	       baseurl);
      }
      break;

    case 'f':    /* Referer report */

      if (fq)
	genout(outf, refsorthead, refsortby, refminreqstr, refminbytestr,
	       ref_max_reqs, 0, refcols, "Referer", "Referer Report",
	       "refering URL", "refering URLs", 'f', FALSE, refbyq, !aq, "");
      break;

    case 'b':    /* Browser summary */

      if (bq)
	genout(outf, browsorthead, browsortby, browminreqstr, browminbytestr,
	       brow_max_reqs, 0, browcols, "Browser", "Browser Summary",
	       "browser", "browsers", 'b', FALSE, browbyq, OFF, "");
      break;

    case 'B':    /* Full browser report */

      if (Bq)
	genout(outf, fullbrowsorthead, fullbrowsortby, fullbrowminreqstr,
	       fullbrowminbytestr, fullbrow_max_reqs, 0, fullbrowcols,
	       "FullBrowser", "Browser Report", "browser", "browsers", 'B',
	       FALSE, browbyq, OFF, "");
      break;

    case 'c':

      if (cq)
	statusout(outf);
      break;

    case 'e':

      if (eq)
	errout(outf, errorder);
      break;

    }    /* end switch */
  }      /* end for ro */


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

  if (aq != CACHE) {
    if (!aq)
      fprintf(outf, "\n\n<hr>\n<i>This analysis was produced by <a HREF=\"http://www.statslab.cam.ac.uk/~sret1/analog/\">analog%s</a>.\n", VERSION);
    else
      fprintf(outf, "This analysis was produced by analog%s.\n", VERSION);

    time(&stoptime);
  
    stoptime -= starttime;   /* so now measures elapsed time */

    if (stoptime == 0) {
      if (!aq)
	fprintf(outf, "<br><b>Running time:</b> Less than 1 second.</i>\n");
      else
	fprintf(outf, "Running time: Less than 1 second.\n");
    }

    else if (stoptime < 60) {
      if (!aq)
	fprintf(outf, "<br><b>Running time:</b> %ld second%s.</i>\n",
		stoptime, (stoptime == 1)?"":"s");
      else
	fprintf(outf, "Running time: %ld second%s.\n",
		stoptime, (stoptime == 1)?"":"s");
    }
  
    else {
      if (!aq)
	fprintf(outf,
		"<br><b>Running time:</b> %ld minute%s, %ld second%s.</i>\n",
		stoptime / 60, (stoptime / 60 == 1)?"":"s",
		stoptime % 60, (stoptime % 60 == 1)?"":"s");
      else
	fprintf(outf, "Running time: %ld minute%s, %ld second%s.\n",
		stoptime / 60, (stoptime / 60 == 1)?"":"s",
		stoptime % 60, (stoptime % 60 == 1)?"":"s");
    }
  
    if (!aq && (mq || Wq || dq || Dq || hq || oq || Sq || iq || rq)) {
      gotos(outf, '\0');
    }

    /* Finally, insert footer file */

    footerfile[MAXSTRINGLENGTH - 5] = '\0';  /* for safety */

    if (!STREQ(footerfile, "none")) {
      if ((tempf = fopen(footerfile, "r")) == NULL) {
	if (warnq)
	  fprintf(stderr,
		  "%s: Warning: Failed to open footer file %s: ignoring it.\n",
		  commandname, footerfile);
      }
      else {  /* can open footer file */
	if (!aq)
	  fprintf(outf, "<hr>");
	else {
	  for (i = 0; i < pagewidth; i++)
	    fprintf(outf, "-");
	}
	fprintf(outf, "\n\n");
	fflush(stdout);

	while(fgets(tempstr, MAXLINELENGTH, tempf) != NULL)
	  fprintf(outf, "%s", tempstr);
	fclose(tempf);
	if (tempstr[(int)strlen(tempstr) - 1] != '\n')
	  fprintf(outf, "\n");

	fprintf(outf, "\n");
      }
    }

    if (!aq) {
      if (STREQ(headerfile, "none") && STREQ(footerfile, "none")) {
	fprintf(outf,
		"<P> <A HREF=\"http://www.webtechs.com/html-val-svc/\">\n");
	fprintf(outf, "<IMG SRC=\"");
	htmlfprintf(outf, imagedir);
	fprintf(outf, "html2.gif\"\n");
	fprintf(outf, "ALT=\"HTML 2.0 Compliant!\"></A>\n");
      }
      fprintf(outf, "\n</body>\n</html>\n");
    }
  }

  fclose(outf);

}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.