1 /***             analog 6.0             http://www.analog.cx/             ***/
2 /*** This program is copyright (c) Stephen R. E. Turner 1995 - 2004 except as
3  *** stated otherwise.
4  ***
5  *** This program is free software. You can redistribute it and/or modify it
6  *** under the terms of version 2 of the GNU General Public License, which you
7  *** should have received with it.
8  ***
9  *** This program is distributed in the hope that it will be useful, but
10  *** without any warranty, expressed or implied.   ***/
11 
12 /*** output2.c; subsiduary output functions ***/
13 
14 #include "anlghea3.h"
15 
16 extern unsigned int *rep2lng;
17 extern choice *rep2type, *rep2reqs, *rep2reqs7, *rep2date, *rep2firstd;
18 extern char *repname[];
19 
report_title(FILE * outf,Outchoices * od,choice rep)20 void report_title(FILE *outf, Outchoices *od, choice rep) {
21 
22   /* The actual report title */
23   od->outputter->reporttitle(outf, od, rep);
24 
25   /* The report description text */
26   if (od->descriptions && od->descstr[rep] != NULL)
27     od->outputter->reportdesc(outf, od, rep);
28 }
29 
report_footer(FILE * outf,Outchoices * od,choice rep)30 void report_footer(FILE *outf, Outchoices *od, choice rep) {
31   od->outputter->reportfooter(outf, od, rep);
32   od->outputter->hrule(outf, od);
33 }
34 
35 /* The period represented by the report. At the moment, this is a function of
36    the report, not the underlying item type. Either choice makes some sense,
37    though, and it would just be a matter of changing the calculation of maxd &
38    min before passing them into this function. */
39 /* Assume od->repspan already tested. */
reportspan(FILE * outf,Outchoices * od,choice rep,timecode_t maxd,timecode_t mind,Dateman * dman)40 void reportspan(FILE *outf, Outchoices *od, choice rep, timecode_t maxd,
41 		timecode_t mind, Dateman *dman) {
42   if (maxd == FIRST_TIME || mind == LAST_TIME ||
43       (mind - dman->firsttime < od->rsthresh &&
44        dman->lasttime - maxd < od->rsthresh))
45     return;
46 
47   od->outputter->reportspan(outf, od, rep, maxd, mind);
48 }
49 
matchlength(FILE * outf,Outchoices * od,char * s,char c)50 void matchlength(FILE *outf, Outchoices *od, char *s, char c) {
51   size_t i;
52 
53   for (i = od->outputter->strlength(s); i > 0; i--)
54     od->outputter->putch(outf, c);
55 }
56 
matchlengthn(FILE * outf,Outchoices * od,int width,char c)57 void matchlengthn(FILE *outf, Outchoices *od, int width, char c) {
58   for ( ; width > 0; width--)
59     od->outputter->putch(outf, c);
60 }
61 
62 /*** Date printing routine ***/
63 
datefmtlen(Outchoices * od,char * fmt)64 size_t datefmtlen(Outchoices *od, char *fmt) {
65   /* Page width required for formatted date. All dates should be the same,
66      so just format an arbitrary one and measure it. */
67   return(od->outputter->strlength(datesprintf(od, fmt, 1, 23, 59, 1, 23, 59,
68 					   FALSE, UNSET)));
69 }
70 
datesprintf(Outchoices * od,char * fmt,datecode_t date,unsigned int hr,unsigned int min,datecode_t newdate,unsigned int newhr,unsigned int newmin,logical running,choice allowmonth)71 char *datesprintf(Outchoices *od, char *fmt, datecode_t date, unsigned int hr,
72 		  unsigned int min, datecode_t newdate, unsigned int newhr,
73 		  unsigned int newmin, logical running, choice allowmonth) {
74   /* Formats date. Allocates space as necessary, but 2nd call will overwrite */
75   /* If od is NULL, must have running == TRUE and allowmonth != UNSET.
76      Otherwise, if allowmonth is UNSET, follow what the od->outputter says. */
77   extern char *engmonths[], *engshortdays[];
78   static char *ans = NULL;
79   static size_t len = 0;
80 
81   size_t (*strlenp)(const char *);
82   char * (*endashp)(void);
83   char **monthname, **dayname, *compsep;
84   size_t monthlen, daylen, ampmlen, plainmonthlen, plaindaylen, plainampmlen;
85   size_t current, increment;
86   unsigned int d, m, y, d2, m2, y2, n, i;
87   char *s, *c, *am, *pm;
88 
89   if (od == NULL) {   /* Not in output routine */
90     strlenp = &strlen;
91     endashp = &plain_endash;
92     monthname = engmonths;
93     dayname = engshortdays;
94     compsep = NULL;
95     am = "am";
96     pm = "pm";
97     plainmonthlen = ENGMONTHLEN;
98     plaindaylen = ENGSHORTDAYLEN;
99     plainampmlen = 2;
100   }
101   else {
102     strlenp = od->outputter->strlength;
103     endashp = od->outputter->endash;
104     monthname = od->monthname;
105     dayname = od->dayname;
106     compsep = od->compsep;
107     am = od->lngstr[am_];
108     pm = od->lngstr[pm_];
109     plainmonthlen = od->plainmonthlen;
110     plaindaylen = od->plaindaylen;
111     plainampmlen = od->plainampmlen;
112     if (allowmonth == UNSET)
113       allowmonth = od->outputter->allowmonth();
114   }
115   if (running) {    /* Running text: no extra spacing to line things up */
116     monthlen = 0;
117     daylen = 0;
118     ampmlen = 0;
119   }
120   else {
121     monthlen = od->monthlen;
122     daylen = od->daylen;
123     ampmlen = od->ampmlen;
124   }
125   increment = monthlen + plainmonthlen + daylen + plaindaylen + ampmlen +
126     plainampmlen + ((compsep == NULL)?0:strlen(compsep)) + 5;
127   /* A (naive) upper bound on the amount by which the length of the answer
128      might grow in one step; cf comment under plainmonthlen in init.c. */
129 
130   if (date == 0 || date == LAST_DATE) {
131     n = chrn(fmt, '\b');
132     ENSURE_LEN(ans, len, n * ((compsep == NULL)?0:strlen(compsep)) + 1);
133     s = ans;
134     if (compsep != NULL) {
135       for (i = 0; i < n; i++)
136 	PUTs(s, compsep, 0);
137     }
138     *s = '\0';
139     return(ans);
140   }
141   code2date(date, &d, &m, &y);
142   code2date(newdate, &d2, &m2, &y2);
143   ENSURE_LEN(ans, len, 1);  /* in case fmt is "" */
144   for (c = fmt, s = ans; *c != '\0'; c++) {
145     current = (ans == NULL)?0:(size_t)(s - ans);
146     ENSURE_LEN(ans, len, current + increment);
147     s = ans + current;   /* in case ans was moved when realloc'ed */
148     if (*c == '%' && *(c + 1) != '\0') {
149       c++;
150       switch (*c) {
151       case '%':
152 	PUTc(s, '%');
153 	break;
154       case 'd':
155 	PUT2d(s, d);
156 	break;
157       case 'D':
158 	PUT02d(s, d);
159 	break;
160       case 'e':
161 	PUT2d(s, d2);
162 	break;
163       case 'E':
164 	PUT02d(s, d2);
165 	break;
166       case 'l':
167 	if (monthname != NULL)
168 	  PUTs(s, monthname[m2],
169 	       (int)monthlen - (int)strlenp(monthname[m2]));
170 	break;
171       case 'L':
172 	if (allowmonth)
173 	  PUT02d(s, m2 + 1);
174 	break;
175       case 'm':
176 	if (monthname != NULL)
177 	  PUTs(s, monthname[m],
178 	       (int)monthlen - (int)strlenp(monthname[m]));
179 	break;
180       case 'M':
181 	if (allowmonth)
182 	  PUT02d(s, m + 1);
183 	break;
184       case 'q':
185 	PUT1d(s, (m / 3) + 1);
186 	break;
187       case '\b':  /* \b only used internally */
188 	if (compsep != NULL)
189 	  PUTs(s, compsep, 0);
190 	break;
191       case 'y':
192 	PUT02d(s, y % 100);
193 	break;
194       case 'Y':
195 	PUT04d(s, y);
196 	break;
197       case 'z':
198 	PUT02d(s, y2 % 100);
199 	break;
200       case 'Z':
201 	PUT04d(s, y2);
202 	break;
203       case 'X':
204 	PUT04d(s, y);
205 	PUTc(s, '-');
206 	PUT02d(s, m + 1);
207 	PUTc(s, '-');
208 	PUT02d(s, d);
209 	break;
210       case 'h':
211 	PUT2d(s, hr);
212 	break;
213       case 'H':
214 	PUT02d(s, hr);
215 	break;
216       case 'j':
217 	i = hr % 12;
218 	if (i == 0)
219 	  i = 12;
220 	PUT2d(s, i);
221 	break;
222       case 'a':
223 	if (hr < 12 || hr == 24)
224 	  PUTs(s, am, (int)ampmlen - (int)strlenp(am))
225 	else       /* no semicolon above because of definition of PUTs */
226 	  PUTs(s, pm, (int)ampmlen - (int)strlenp(pm));
227 	break;
228       case 'i':
229 	PUT2d(s, newhr);
230 	break;
231       case 'I':
232 	PUT02d(s, newhr);
233 	break;
234       case 'k':
235 	i = newhr % 12;
236 	if (i == 0)
237 	  i = 12;
238 	PUT2d(s, i);
239 	break;
240       case 'b':
241 	if (newhr < 12 || newhr == 24)
242 	  PUTs(s, am, (int)ampmlen - (int)strlenp(am))
243 	else       /* no semicolon above because of definition of PUTs */
244 	  PUTs(s, pm, (int)ampmlen - (int)strlenp(pm));
245 	break;
246       case 'n':
247 	PUT02d(s, min);
248 	break;
249       case 'o':
250 	PUT02d(s, newmin);
251 	break;
252       case 'w':
253 	if (dayname != NULL)
254 	  PUTs(s, dayname[DAYOFWEEK(date)],
255 	       (int)daylen - (int)strlenp(dayname[DAYOFWEEK(date)]));
256 	break;
257       case 'x':
258 	PUTs(s, endashp(), 0);
259 	break;
260       }  /* switch *c */
261     }    /* if *c == '%' */
262     else
263       PUTc(s, *c);
264   }  /* for c */
265   *s = '\0';
266   return(ans);
267 }
268 
timesprintf(Outchoices * od,char * fmt,timecode_t t,choice allowmonth)269 char *timesprintf(Outchoices *od, char *fmt, timecode_t t, choice allowmonth) {
270   /* Just a wrapper for the most common case of datesprintf(). */
271   return(datesprintf(od, fmt, t / 1440, (t % 1440) / 60, t % 60, 0, 0, 0, TRUE,
272 		     allowmonth));
273 }
274 
f3printf(FILE * outf,Outchoices * od,double x,unsigned int width,char sepchar)275 int f3printf(FILE *outf, Outchoices *od, double x, unsigned int width,
276 	     char sepchar) {
277   /* Return number of characters printed, but counting e.g. &amp; as 1. */
278   /* NB The sepchar is sometimes repsepchar */
279   int ans, i;
280 
281   x += EPSILON;   /* just to make sure rounding down works OK */
282   if (sepchar == '\0')
283     return(fprintf(outf, "%*.0f", width, x));
284 
285   for (i = 0; x >= 1000; i++)
286     x /= 1000;  /* find out how big x is to get number of leading spaces */
287   ans = fprintf(outf, "%*d", MAX((int)width - 4 * i, 0), (int)x);
288   ans += 4 * i;
289   /* now run down again, printing each clump */
290   for ( ; i > 0; i--) {
291     od->outputter->putch(outf, sepchar);
292     x -= (int)x;
293     x *= 1000;
294     fprintf(outf, "%03d", (int)x);
295   }
296   return(ans);
297 }
298 
printbytes(FILE * outf,Outchoices * od,double bytes,unsigned int bmult,unsigned int width,char sepchar)299 void printbytes(FILE *outf, Outchoices *od, double bytes, unsigned int bmult,
300 		unsigned int width, char sepchar) {
301 
302   unsigned int dp = od->bytesdp;
303 
304   int by1;
305   double by2, rounder;
306   unsigned int j;
307 
308   if (bmult == 0)
309     (void)f3printf(outf, od, bytes, width, sepchar);
310   else {
311     for (j = 0; j < bmult; j++)
312       bytes /= 1024; /* divide bytes to get kilobytes, megabytes or whatever */
313 
314     /* Add some amount in order to round to the correct number of decimal
315        places accurately: 0.5 for 0 d.p.s, 0.05 for 1 d.p. etc. */
316     rounder = 0.5;
317     for (j = 0; j < dp; j++)
318       rounder /= 10.0;
319     bytes += rounder;
320 
321     if (dp == 0) {  /* fractional part not wanted */
322       fprintf(outf, "%*d", width, (int)bytes);
323     }
324     else {
325       by1 = (int)bytes;    /* whole number of kilo/mega/etc. bytes */
326       width -= MIN(width, dp + 1);  /* leave room for fractional part */
327       fprintf(outf, "%*d", width, by1);
328       by2 = (bytes - (double)by1);  /* fractional part */
329       for (j = 0; j < dp; j++)
330 	by2 *= 10;
331       od->outputter->putch(outf, od->decpt);
332       fprintf(outf, "%0*d", dp, (int)by2);
333     }
334   }
335 }
336 
findunit(Outchoices * od,double n,unsigned int width[],choice * cols)337 double findunit(Outchoices *od, double n, unsigned int width[], choice *cols) {
338   int w;
339   double unit;
340   int c;
341   int i, j;
342 
343   w = (int)(od->outputter->pagewidth(od)) - (int)width[COL_TITLE] - 2;
344   for (c = 0; cols[c] != COL_NUMBER; c++)
345     w -= (int)width[cols[c]] + 2;
346   w = MAX(w, (int)(od->mingraphwidth));
347   /* unit must be nice amount: i.e., {1, 1.5, 2, 2.5, 3, 4, 5, 6, 8} * 10^n */
348   unit = ((n - 1) / (double)w);
349   j = 0;
350   while (unit > 24.) {
351     unit /= 10.;
352     j++;
353   }
354   unit = (double)((int)unit);
355   if (unit == 6.)
356     unit = 7.;
357   else if (unit == 8.)
358       unit = 9.;
359   else if (unit >= 20.)
360     unit = 24.;
361   else if (unit >= 15.)
362     unit = 19.;
363   else if (unit >= 10.)
364     unit = 14.;
365   unit += 1.;
366   for (i = 0; i < j; i++) {
367     unit *= 10.;
368   }
369   return(unit);
370 }
371 
372 /* The widths which columns want to take. The od->outputters have their own
373    version of this function, but usually they just call this one */
calcwidths(Outchoices * od,choice rep,unsigned int width[],unsigned int * bmult,unsigned int * bmult7,double * unit,unsigned long maxr,unsigned long maxr7,unsigned long maxp,unsigned long maxp7,double maxb,double maxb7,unsigned long howmany)374 void calcwidths(Outchoices *od, choice rep, unsigned int width[],
375 		unsigned int *bmult, unsigned int *bmult7, double *unit,
376 		unsigned long maxr, unsigned long maxr7, unsigned long maxp,
377 		unsigned long maxp7, double maxb, double maxb7,
378 		unsigned long howmany) {
379   /* width[COL_TITLE] should be set before calling this function. */
380   /* width[COL_TITLE] == 0 signifies that the title is last and this function
381      should calculate the remaining width. */
382   /* *unit == 0 for timegraphs (and it's then set here); non-zero otherwise. */
383   extern unsigned int *col2colhead;
384 
385   char repsepchar = od->repsepchar;
386   char graphby = od->graph[rep];
387   choice *cols = od->cols[rep];
388   char **lngstr = od->lngstr;
389 
390   int w;
391   unsigned int i;
392 
393   width[COL_REQS] = MAX(LEN3(log10i(maxr) + 1, repsepchar),
394 			od->outputter->strlength(lngstr[col2colhead[COL_REQS]]));
395   width[COL_REQS7] = MAX(LEN3(log10i(maxr7) + 1, repsepchar),
396 			 od->outputter->strlength(lngstr[col2colhead[COL_REQS7]]));
397   width[COL_PAGES] = MAX(LEN3(log10i(maxp) + 1, repsepchar),
398 			 od->outputter->strlength(lngstr[col2colhead[COL_PAGES]]));
399   width[COL_PAGES7] = MAX(LEN3(log10i(maxp7) + 1, repsepchar),
400 			  od->outputter->strlength(lngstr[col2colhead[COL_PAGES7]]));
401   if (od->rawbytes || maxb < 1024.0) {
402     width[COL_BYTES] = MAX(LEN3(log10x(maxb) + 1, repsepchar),
403 			   od->outputter->strlength(lngstr[col2colhead[COL_BYTES]]));
404     *bmult = 0;
405   }
406   else {
407     *bmult = findbmult(maxb, od->bytesdp);
408     width[COL_BYTES] =
409       MAX(3 + od->bytesdp + (od->bytesdp != 0),
410 	  od->outputter->strlength(lngstr[col2colhead[COL_BYTES] + 1])
411 	  + od->outputter->strlength(lngstr[byteprefixabbr_ + *bmult]) - 1);
412   }
413   /* I have some misgivings about allowing the bmult7 to be different from
414      the bmult. It's less immediately readable. But I think it's necessary,
415      because maxb and maxb7 are quite often different orders of magnitude. */
416   if (od->rawbytes || maxb7 < 1024.0) {
417     width[COL_BYTES7] =
418       MAX(LEN3(log10x(maxb7) + 1, repsepchar),
419 	  od->outputter->strlength(lngstr[col2colhead[COL_BYTES7]]));
420     *bmult7 = 0;
421   }
422   else {
423     *bmult7 = findbmult(maxb7, od->bytesdp);
424     width[COL_BYTES7] =
425       MAX(3 + od->bytesdp + (od->bytesdp != 0),
426 	  od->outputter->strlength(lngstr[col2colhead[COL_BYTES7] + 1])
427 	  + od->outputter->strlength(lngstr[byteprefixabbr_ + *bmult7]) - 1);
428   }
429   width[COL_PREQS] = MAX(6, od->outputter->strlength(lngstr[col2colhead[COL_PREQS]]));
430   width[COL_PREQS7] =
431     MAX(6, od->outputter->strlength(lngstr[col2colhead[COL_PREQS7]]));
432   width[COL_PPAGES] =
433     MAX(6, od->outputter->strlength(lngstr[col2colhead[COL_PPAGES]]));
434   width[COL_PPAGES7] =
435     MAX(6, od->outputter->strlength(lngstr[col2colhead[COL_PPAGES7]]));
436   width[COL_PBYTES] =
437     MAX(6, od->outputter->strlength(lngstr[col2colhead[COL_PBYTES]]));
438   width[COL_PBYTES7] =
439     MAX(6, od->outputter->strlength(lngstr[col2colhead[COL_PBYTES7]]));
440   width[COL_DATE] = MAX(datefmtlen(od, lngstr[genrepdate_]),
441 			od->outputter->strlength(lngstr[col2colhead[COL_DATE]]));
442   width[COL_TIME] = MAX(datefmtlen(od, lngstr[genreptime_]),
443 			od->outputter->strlength(lngstr[col2colhead[COL_TIME]]));
444   width[COL_FIRSTD] = MAX(datefmtlen(od, lngstr[genrepdate_]),
445 			  od->outputter->strlength(lngstr[col2colhead[COL_FIRSTD]]));
446   width[COL_FIRSTT] = MAX(datefmtlen(od, lngstr[genreptime_]),
447 			  od->outputter->strlength(lngstr[col2colhead[COL_FIRSTT]]));
448   width[COL_INDEX] = MAX(LEN3(log10i(howmany) + 1, repsepchar),
449 			 od->outputter->strlength(lngstr[col2colhead[COL_INDEX]]));
450   if (*unit == 0) { /* i.e. a timegraph */
451     if (graphby == 'R' || graphby == 'r')
452       *unit = findunit(od, (double)maxr, width, cols);
453     else if (graphby == 'P' || graphby == 'p')
454       *unit = findunit(od, (double)maxp, width, cols);
455     else {
456       for (i = 0; i < *bmult; i++)
457 	maxb /= 1024;
458       if (*bmult > 0)
459 	maxb *= 1000;
460       *unit = findunit(od, maxb, width, cols);
461       if (*bmult > 0)
462 	*unit /= 1000;
463     }
464   }
465   if (width[COL_TITLE] == 0) {
466     w = (int)(od->outputter->pagewidth(od));
467     for (i = 0; cols[i] != COL_NUMBER; i++)
468       w -= (int)width[cols[i]] + 2;
469     width[COL_TITLE] = (unsigned int)MAX(0, w);
470   }
471 
472 }
473 
alphatreewidth(Outchoices * od,choice rep,Hashtable * tree,unsigned int level,Strlist * partname)474 unsigned int alphatreewidth(Outchoices *od, choice rep, Hashtable *tree,
475 			    unsigned int level, Strlist *partname) {
476   /* Calculate width needed for Organisation Report.
477      Constructing the name is basically the same code as printtree(). */
478   extern char *workspace;
479 
480   char *name;
481   size_t need = (size_t)level + 3;
482   Strlist *pn, s;
483   Hashindex *p;
484   unsigned int tw = 0, tmp;
485 
486   if (tree == NULL || tree->head[0] == NULL)
487     return(0);
488   for (p = tree->head[0]; p != NULL; TO_NEXT(p)) {
489     name = maketreename(partname, p, &pn, &s, need, rep, TRUE);
490     if (!STREQ(name, LNGSTR_NODOMAIN) && !STREQ(name, LNGSTR_UNKDOMAIN) &&
491 	!ISDIGIT(name[strlen(name) - 1])) { /* ignore left-aligned ones */
492       strcpy(workspace, name);
493       do_aliasx(workspace, od->aliashead[G(rep)]);
494       tmp = od->outputter->strlength(workspace) + 2 * level;
495                        /* will be printed with 2 trailing spaces per level */
496       tw = MAX(tw, tmp);
497       tmp = alphatreewidth(od, rep, (Hashtable *)(p->other), level + 1, pn);
498       tw = MAX(tw, tmp);
499       /* The second tmp will of course be bigger unless there are aliases
500 	 (if there are any children at all). */
501     }
502   }
503   return(tw);
504 }
505 
506 /* Declare the floor and sortby for a report. The od->outputters have their own
507    version of this function, but most of them just call this one to sort out
508    all the language strings. */
whatincluded(FILE * outf,Outchoices * od,choice rep,unsigned long n,Dateman * dman)509 void whatincluded(FILE *outf, Outchoices *od, choice rep, unsigned long n,
510 		  Dateman *dman) {
511   extern char *byteprefix;
512   extern unsigned int *method2sing, *method2pl, *method2date, *method2pc;
513   extern unsigned int *method2relpc, *method2sort;
514 
515   char **lngstr = od->lngstr;
516   choice sortby = od->sortby[G(rep)];
517   double floormin = od->floor[G(rep)].min;
518   char floorqual = od->floor[G(rep)].qual;
519   choice floorby = od->floor[G(rep)].floorby;
520   char *gens = lngstr[rep2lng[rep] + 1];
521   char *genp = lngstr[rep2lng[rep] + 2];
522   char gender = lngstr[rep2lng[rep] + 3][0];
523   choice requests = rep2reqs[G(rep)];
524   choice requests7 = rep2reqs7[G(rep)];
525   choice date = rep2date[G(rep)];
526   choice firstd = rep2firstd[G(rep)];
527 
528   int firsts, firstds, alls, sorted, alphsort, unsort, bmult;
529   char *lngs, *c;
530   static char *t = NULL;
531   static size_t tlen = 0;
532   unsigned long temp = 0;
533   unsigned long temp2;
534   int i;
535   timecode_t tempd;
536 
537   if (gender == 'm') {
538     firsts = firstsm_;
539     firstds = firstdsm_;
540     alls = allsm_;
541     sorted = sortedm_;
542     alphsort = STREQ(gens, lngstr[codegs_])?numsortm_:alphasortm_;
543     unsort = unsortedm_;            /* quickest kludge for only one report */
544   }
545   else if (gender == 'f') {
546     firsts = firstsf_;
547     firstds = firstdsf_;
548     alls = allsf_;
549     sorted = sortedf_;
550     alphsort = STREQ(gens, lngstr[codegs_])?numsortf_:alphasortf_;
551     unsort = unsortedf_;
552   }
553   else { /* gender == 'n' */
554     firsts = firstsn_;
555     firstds = firstdsn_;
556     alls = allsn_;
557     sorted = sortedn_;
558     alphsort = STREQ(gens, lngstr[codegs_])?numsortn_:alphasortn_;
559     unsort = unsortedn_;
560   }
561 
562   /* see also report_floor() in settings.c */
563   od->outputter->whatinchead(outf, od);
564   if (floormin < 0 && n < (unsigned long)(-floormin + EPSILON))
565     floormin = 1;  /* not enough items for requested -ve floor */
566   /* floormin = 1 will work even for date sort because it will be before
567      dman->firsttime. With very high probability. :) */
568   if (floormin < 0) {
569     temp = (unsigned long)(-floormin + EPSILON);
570     if (temp == 1) {
571       ENSURE_LEN(t, tlen, strlen(lngstr[firsts]) + strlen(gens) + 1);
572       sprintf(t, lngstr[firsts], gens);
573       od->outputter->whatincprintstr(outf, od, t);
574     }
575     else {
576       for (temp2 = temp, i = 1; temp2 >= 10; temp2 /= 10)
577 	i++;
578       ENSURE_LEN(t, tlen, strlen(lngstr[firstds]) + strlen(genp) + i + 1);
579       sprintf(t, lngstr[firstds], temp, genp);
580       od->outputter->whatincprintstr(outf, od, t);
581     }
582     od->outputter->whatincprintstr(outf, od, " ");
583     od->outputter->whatincprintstr(outf, od, lngstr[floorby_]);
584     od->outputter->whatincprintstr(outf, od, " ");
585     if (floorby == REQUESTS)
586       od->outputter->whatincprintstr(outf, od, lngstr[method2sort[requests]]);
587     else if (floorby == REQUESTS7)
588       od->outputter->whatincprintstr(outf, od, lngstr[method2sort[requests7]]);
589     else if (floorby == DATESORT)
590       od->outputter->whatincprintstr(outf, od, lngstr[method2sort[date]]);
591     else if (floorby == FIRSTDATE)
592       od->outputter->whatincprintstr(outf, od, lngstr[method2sort[firstd]]);
593     else
594       od->outputter->whatincprintstr(outf, od, lngstr[method2sort[floorby]]);
595   }
596   else {   /* floormin >= 0 */
597     ENSURE_LEN(t, tlen, strlen(lngstr[alls]) + strlen(genp) + 1);
598     sprintf(t, lngstr[alls], genp);
599     od->outputter->whatincprintstr(outf, od, t);
600     if (floormin < 2 - EPSILON && floorqual == '\0' && floorby == REQUESTS)
601       floormin = 0;  /* Report 1r as 0r */
602     if (floorby == DATESORT || floorby == FIRSTDATE) {
603       tempd = (timecode_t)(floormin + EPSILON);
604       if (tempd > dman->firsttime) {
605 	od->outputter->whatincprintstr(outf, od, " ");
606 	od->outputter->whatincprintstr(outf, od,
607 		   lngstr[method2date[(floorby == DATESORT)?date:firstd]]);
608 	od->outputter->whatincprintstr(outf, od, " ");
609 	od->outputter->whatincprintstr(outf, od, timesprintf(od, lngstr[whatincfmt_],
610 						     tempd, UNSET));
611       }
612     }
613     else if (floormin > EPSILON) {
614       od->outputter->whatincprintstr(outf, od, " ");
615       od->outputter->whatincprintstr(outf, od, lngstr[atleast_]);
616       od->outputter->whatincprintstr(outf, od, " ");
617       if (floorqual == '\0') {
618 	temp = (unsigned long)(floormin + EPSILON);
619 	for (temp2 = temp, i = 1; temp2 >= 10; temp2 /= 10)
620 	  i++;
621 	ENSURE_LEN(t, tlen, i + 1);
622 	sprintf(t, "%lu", temp);
623 	od->outputter->whatincprintstr(outf, od, t);
624 	od->outputter->whatincprintstr(outf, od, " ");
625 	if (floorby == REQUESTS)
626 	  od->outputter->whatincprintstr(outf, od, (temp == 1)?\
627 		  lngstr[method2sing[requests]]:lngstr[method2pl[requests]]);
628 	else if (floorby == REQUESTS7)
629 	  od->outputter->whatincprintstr(outf, od, (temp == 1)?\
630 		  lngstr[method2sing[requests7]]:\
631 		  lngstr[method2pl[requests7]]);
632 	else
633 	  od->outputter->whatincprintstr(outf, od, (temp == 1)?\
634 		  lngstr[method2sing[floorby]]:lngstr[method2pl[floorby]]);
635       }
636       else {  /* floorqual != '\0' */
637 	od->outputter->printdouble(outf, od, floormin);
638 	if (floorqual == '%') {
639 	  if (floorby == REQUESTS)
640 	    c = lngstr[method2pc[requests]];
641 	  else if (floorby == REQUESTS7)
642 	    c = lngstr[method2pc[requests7]];
643 	  else
644 	    c = lngstr[method2pc[floorby]];
645 	  od->outputter->whatincprintstr(outf, od, c);
646 	}
647 	else if (floorqual == ':') {
648 	  if (floorby == REQUESTS)
649 	    c = lngstr[method2relpc[requests]];
650 	  else if (floorby == REQUESTS7)
651 	    c = lngstr[method2relpc[requests7]];
652 	  else
653 	    c = lngstr[method2relpc[floorby]];
654 	  od->outputter->whatincprintstr(outf, od, c);
655 	}
656 	else { /* if qual is anything else, must be (k|M|G|T|etc.)bytes */
657 	  lngs = (floorby == BYTES)?lngstr[xbytestraffic_]:\
658 	    lngstr[xbytestraffic7_];
659 	  if (strchr(byteprefix, floorqual) == NULL)  /* shouldn't happen */
660 	    bmult = 1;
661 	  else
662 	    bmult = strchr(byteprefix, floorqual) - byteprefix;
663 	  c = strchr(lngs, '?');  /* checked during initialisation */
664 	  *c = '\0';
665 	  od->outputter->whatincprintstr(outf, od, " ");
666 	  od->outputter->whatincprintstr(outf, od, lngs);
667 	  od->outputter->whatincprintstr(outf, od,
668 					 lngstr[byteprefix_ + bmult]);
669 	  od->outputter->whatincprintstr(outf, od, c + 1);
670 	  *c = '?';
671 	}
672       }   /* end floorqual != '\0' */
673     }     /* end floormin > EPSILON */
674   }       /* end floormin > 0 */
675   /* That completes the floor; now we are just left with the sortby */
676   if (floormin >= 0 || temp != 1) { /* else only one item, so no sort */
677     if (floormin < 0 && sortby == RANDOM)
678       sortby = floorby;
679     od->outputter->whatincprintstr(outf, od, ", ");
680     if (sortby == ALPHABETICAL)
681       od->outputter->whatincprintstr(outf, od, lngstr[alphsort]);
682     else if (sortby == RANDOM)
683       od->outputter->whatincprintstr(outf, od, lngstr[unsort]);
684     else {
685       od->outputter->whatincprintstr(outf, od, lngstr[sorted]);
686       od->outputter->whatincprintstr(outf, od, " ");
687       if (sortby == REQUESTS)
688 	od->outputter->whatincprintstr(outf, od,
689 				       lngstr[method2sort[requests]]);
690       else if (sortby == REQUESTS7)
691 	od->outputter->whatincprintstr(outf, od,
692 				       lngstr[method2sort[requests7]]);
693       else if (sortby == DATESORT)
694 	od->outputter->whatincprintstr(outf, od, lngstr[method2sort[date]]);
695       else if (sortby == FIRSTDATE)
696 	od->outputter->whatincprintstr(outf, od, lngstr[method2sort[firstd]]);
697       else
698 	od->outputter->whatincprintstr(outf, od, lngstr[method2sort[sortby]]);
699     }
700   }
701   od->outputter->whatincprintstr(outf, od, ".");
702   od->outputter->whatincfoot(outf, od);
703 }
704 
barchart(FILE * outf,Outchoices * od,char graphby,unsigned long reqs,unsigned long pages,double bys,double unit)705 void barchart(FILE *outf, Outchoices *od, char graphby, unsigned long reqs,
706 	      unsigned long pages, double bys, double unit) {
707   double x;
708   int y;
709 
710   if (graphby == 'P' || graphby == 'p')
711     x = (double)pages - 0.5;
712   else if (graphby == 'R' || graphby == 'r')
713     x = (double)reqs - 0.5;
714   else
715     x = bys;
716   x /= unit;
717   x += 1;
718   y = (int)x;
719 
720   od->outputter->barchart(outf, od, y, graphby);
721 }
722 
colheads(FILE * outf,Outchoices * od,choice rep,unsigned int width[],unsigned int bmult,unsigned int bmult7,logical name1st)723 void colheads(FILE *outf, Outchoices *od, choice rep, unsigned int width[],
724 	      unsigned int bmult, unsigned int bmult7, logical name1st) {
725   extern unsigned int *col2colhead, *rep2colhead;
726   static char *bcolname = NULL;
727   static size_t bcolnamelen = 0;
728 
729   char **lngstr = od->lngstr;
730   choice *cols = od->cols[rep];
731 
732   char *colname, *d;
733   unsigned int c, bm;
734 
735   /* Column headers */
736 
737   od->outputter->colheadstart(outf, od, rep);
738 
739   if (name1st)
740     od->outputter->colheadcol(outf, od, rep, COL_TITLE, width[COL_TITLE],
741 			      lngstr[rep2colhead[rep]], FALSE);
742   for (c = 0; cols[c] != COL_NUMBER; c++) {
743     if (cols[c] == COL_BYTES)
744       bm = bmult;
745     else if (cols[c] == COL_BYTES7)
746       bm = bmult7;
747     else
748       bm = 0;
749 
750     if (bm == 0)
751       colname = lngstr[col2colhead[cols[c]]];
752     else {  /* special case for bytes: insert kilo, mega, etc. */
753       colname = lngstr[col2colhead[cols[c]] + 1];
754       d = strchr(colname, '?');  /* checked during initialisation */
755       *d = '\0';
756       ENSURE_LEN(bcolname, bcolnamelen,
757 		 strlen(colname) + strlen(lngstr[byteprefixabbr_ + bm]) +
758 		 strlen(d + 1) + 1);
759       sprintf(bcolname, "%s%s%s", colname, lngstr[byteprefixabbr_ + bm],
760 	      d + 1);
761       *d = '?';
762       colname = bcolname;
763     }
764     od->outputter->colheadcol(outf, od, rep, cols[c], width[cols[c]], colname,
765 			      FALSE);
766   }  /* for (c = 0) */
767   if (!name1st)
768     od->outputter->colheadcol(outf, od, rep, COL_TITLE, width[COL_TITLE],
769 			      lngstr[rep2colhead[rep]], TRUE);
770 
771   od->outputter->colheadend(outf, od, rep);
772 
773   /* Underlinings */
774 
775   od->outputter->colheadustart(outf, od, rep);
776 
777   if (name1st)
778     od->outputter->colheadunderline(outf, od, rep, COL_TITLE,
779 				width[COL_TITLE], "");
780   for (c = 0; cols[c] != COL_NUMBER; c++)
781     od->outputter->colheadunderline(outf, od, rep, cols[c], width[cols[c]],
782 				    "");
783   if (!name1st)
784     od->outputter->colheadunderline(outf, od, rep, COL_TITLE, 0,
785 				lngstr[rep2colhead[rep]]);
786 
787   od->outputter->colheaduend(outf, od, rep);
788 }
789 
printcols(FILE * outf,Outchoices * od,choice rep,unsigned long reqs,unsigned long reqs7,unsigned long pages,unsigned long pages7,double bys,double bys7,long index,int level,unsigned long totr,unsigned long totr7,unsigned long totp,unsigned long totp7,double totb,double totb7,unsigned int width[],unsigned int bmult,unsigned int bmult7,double unit,logical name1st,logical rightalign,char * name,logical ispage,unsigned int spaces,Include * linkhead,char * baseurl,char * datefmt,char * timefmt,datecode_t date,unsigned int hr,unsigned int min,datecode_t date2,unsigned int hr2,unsigned int min2)790 void printcols(FILE *outf, Outchoices *od, choice rep, unsigned long reqs,
791 	       unsigned long reqs7, unsigned long pages, unsigned long pages7,
792 	       double bys, double bys7, long index, int level,
793 	       unsigned long totr, unsigned long totr7, unsigned long totp,
794 	       unsigned long totp7, double totb, double totb7,
795 	       unsigned int width[], unsigned int bmult, unsigned int bmult7,
796 	       double unit, logical name1st, logical rightalign, char *name,
797 	       logical ispage, unsigned int spaces, Include *linkhead,
798 	       char *baseurl, char *datefmt, char *timefmt, datecode_t date,
799 	       unsigned int hr, unsigned int min, datecode_t date2,
800 	       unsigned int hr2, unsigned int min2) {
801   /* 'level' is -1 for time reports, 0 for other non-hierarchical reports,
802      and starts at 1 for hierarchical reports. */
803   /* For time reps, date2, hr2 & min2 carry the end of the interval; for
804      genreps, date2, hr2 & min2 carry the time of first request. */
805 
806   choice *cols = od->cols[rep];
807   logical timerep = (rep < DATEREP_NUMBER);
808   char graphby = timerep?(od->graph[rep]):'\0';
809 
810   choice source;
811   char *datestr;
812   unsigned int c, i;
813 
814   od->outputter->rowstart(outf, od, rep, cols, level, name, datefmt, timefmt);
815 
816   if (timerep) {
817     name = datesprintf(od, datefmt, date, hr, min, date2, hr2, min2, FALSE,
818 		       UNSET);
819     source = AS_IS;
820   }
821   else if (rep == REP_SIZE || rep == REP_PROCTIME)
822     source = TRUSTED;
823   else
824     source = UNTRUSTED;
825 
826   od->outputter->namecell(outf, od, rep, name, source, width[COL_TITLE],
827 			  name1st, TRUE, FALSE, NULL, NULL, FALSE, 0, NULL);
828 
829   od->outputter->levelcell(outf, od, rep, level);
830 
831   for (c = 0; cols[c] != COL_NUMBER; c++) {
832     switch(cols[c]) {
833     case COL_REQS:
834       od->outputter->ulcell(outf, od, rep, cols[c], reqs, width[cols[c]]);
835       break;
836     case COL_REQS7:
837       od->outputter->ulcell(outf, od, rep, cols[c], reqs7, width[cols[c]]);
838       break;
839     case COL_PREQS:
840       od->outputter->pccell(outf, od, rep, cols[c], (double)reqs, (double)totr,
841 			width[cols[c]]);
842       break;
843     case COL_PREQS7:
844       od->outputter->pccell(outf, od, rep, cols[c], (double)reqs7,
845 			    (double)totr7, width[cols[c]]);
846       break;
847     case COL_PAGES:
848       od->outputter->ulcell(outf, od, rep, cols[c], pages, width[cols[c]]);
849       break;
850     case COL_PAGES7:
851       od->outputter->ulcell(outf, od, rep, cols[c], pages7, width[cols[c]]);
852       break;
853     case COL_PPAGES:
854       od->outputter->pccell(outf, od, rep, cols[c], (double)pages,
855 			    (double)totp, width[cols[c]]);
856       break;
857     case COL_PPAGES7:
858       od->outputter->pccell(outf, od, rep, cols[c], (double)pages7,
859 			    (double)totp7, width[cols[c]]);
860       break;
861     case COL_BYTES:
862       od->outputter->bytescell(outf, od, rep, cols[c], bys, bmult,
863 			       width[cols[c]]);
864       break;
865     case COL_BYTES7:
866       od->outputter->bytescell(outf, od, rep, cols[c], bys7, bmult7,
867 			   width[cols[c]]);
868       break;
869     case COL_PBYTES:
870       od->outputter->pccell(outf, od, rep, cols[c], bys, totb, width[cols[c]]);
871       break;
872     case COL_PBYTES7:
873       od->outputter->pccell(outf, od, rep, cols[c], bys7, totb7,
874 			    width[cols[c]]);
875       break;
876     case COL_DATE:
877     case COL_TIME:
878     case COL_FIRSTD:
879     case COL_FIRSTT:
880       if (cols[c] == COL_DATE || cols[c] == COL_TIME)
881 	datestr = datesprintf(od, (cols[c] == COL_DATE)?datefmt:timefmt,
882 			      date, hr, min, 0, 0, 0, FALSE, UNSET);
883       else
884 	datestr = datesprintf(od, (cols[c] == COL_FIRSTD)?datefmt:timefmt,
885 			      date2, hr2, min2, 0, 0, 0, FALSE, UNSET);
886       od->outputter->strcell(outf, od, rep, cols[c], datestr, width[cols[c]]);
887       break;
888     case COL_INDEX:
889       od->outputter->indexcell(outf, od, rep, cols[c], index, width[cols[c]]);
890       break;
891     }
892   }
893 
894   if (timerep) {
895     for (i = 0; i < (int)bmult; i++)
896       bys /= 1024;
897     barchart(outf, od, graphby, reqs, pages, bys, unit);
898   }
899 
900   if (name == NULL)
901      return;   /* calling function supplies name and newline */
902 
903   if (rep == REP_CODE)
904     source = TRUSTED;
905   else
906     source = UNTRUSTED;
907   od->outputter->namecell(outf, od, rep, name, source, width[COL_TITLE],
908 			  name1st, FALSE, rightalign,
909 			  G(rep)>=0?od->aliashead[G(rep)]:NULL, linkhead,
910 			  ispage, spaces, baseurl);
911 
912   od->outputter->rowend(outf, od, rep);
913 }
914 
915 /*** Now some stuff for the General Summary ***/
916 
distcount(Hashindex * gooditems,Hashindex * baditems,choice requests,choice requests7,unsigned long * tot,unsigned long * tot7)917 void distcount(Hashindex *gooditems, Hashindex *baditems, choice requests,
918 	       choice requests7, unsigned long *tot, unsigned long *tot7) {
919   Hashindex *p;
920 
921   for (p = gooditems, *tot = 0, *tot7 = 0; p != NULL; TO_NEXT(p)) {
922     if (p->own != NULL) {
923       if (p->own->data[requests] > 0)
924 	(*tot)++;
925       if (p->own->data[requests7] > 0)
926 	(*tot7)++;
927     }
928   }
929   for (p = baditems; p != NULL; TO_NEXT(p)) {
930     if (p->own != NULL) {
931       if (p->own->data[requests] > 0)
932 	(*tot)++;
933       if (p->own->data[requests7] > 0)
934 	(*tot7)++;
935     }
936   }
937 }
938 
gensumline(FILE * outf,Outchoices * od,char codeletter,int namecode,unsigned long x,unsigned long x7,logical isaverage)939 void gensumline(FILE *outf, Outchoices *od, char codeletter, int namecode,
940 		unsigned long x, unsigned long x7, logical isaverage) {
941   if (strchr(od->gensumlines, codeletter) == NULL)
942     return;
943 
944   if (x == (unsigned long)UNSET)
945     return;
946 
947   if (x == 0 && namecode != succreqs_)
948     return;
949 
950   od->outputter->gensumline(outf, od, namecode, x, x7, isaverage);
951 }
952 
gensumlineb(FILE * outf,Outchoices * od,char codeletter,int namecode,double x,double x7,logical isaverage)953 void gensumlineb(FILE *outf, Outchoices *od, char codeletter, int namecode,
954 		 double x, double x7, logical isaverage) {
955   /* same as gensumline() but for bytes */
956 
957   if (strchr(od->gensumlines, codeletter) == NULL)
958     return;
959 
960   if (x <= 0)
961     return;
962 
963   od->outputter->gensumlineb(outf, od, namecode, x, x7, isaverage);
964 }
965 
checkonerep(Outchoices * od,Hashindex * gp,choice rep,choice requests,cutfnp cutfn,dcutfnp dcutfn,void * darg)966 logical checkonerep(Outchoices *od, Hashindex *gp, choice rep, choice requests,
967 		    cutfnp cutfn, dcutfnp dcutfn, void *darg) {
968   extern Memman *amemman;
969 
970   static char *newname = NULL, *dnewname = NULL;
971   static size_t len = 0, dlen = 0;
972 
973   char *namestart, *nameend, *name;
974   choice rc;
975 
976   /* Procedure: go through all the entries. If the report will use that entry
977      (it has any requests, and the name is included()) turn the report on.
978      To calculate the name, we have to call cutfn if the report is a tree
979      report, and/or (dcutfn and alias) if it is a dervrep. */
980   for ( ; gp != NULL; TO_NEXT(gp)) {
981     if (gp->own != NULL && gp->own->data[requests] > 0) {
982       name = gp->name;
983       if (cutfn == NULL && dcutfn != NULL) {
984 	/* The search reports. Here dcutfn can produce zero or multiple answers
985 	   for each name, and we have to check them all. */
986 	namestart = NULL;
987 	for (dcutfn(&namestart, &nameend, name, darg); namestart != NULL;
988 	     dcutfn(&namestart, &nameend, name, darg)) {
989 	  ENSURE_LEN(dnewname, dlen, (size_t)(nameend - namestart + 1));
990 	  memcpy((void *)dnewname, (void *)namestart,
991 		 (size_t)(nameend - namestart));
992 	  dnewname[nameend - namestart] = '\0';
993 	  if ((rc = do_alias(dnewname, amemman, NULL, NULL, 0, FALSE,
994 			     od->convfloor, od->multibyte, rep)) != ERR) {
995 	    name = rc?((char *)(amemman->curr_pos)):dnewname;
996 	    if (included(name, FALSE, od->wanthead[G(rep)]))
997 	      return(TRUE);
998 	  }
999 	}
1000       }
1001       else {
1002 	/* otherwise each name produces just one answer to check; the name
1003 	   itself, or if a tree report the name at the top level of the tree */
1004 	if (cutfn != NULL) {  /* if it's a tree report */
1005 	  if (dcutfn != NULL) {  /* if it's also a derv report */
1006 	    /* Here we rely on the fact that if it's both a tree rep and a derv
1007 	       rep, then dcutfn will produce exactly one name. (See comment on
1008 	       dcutfnp in tree.c). */
1009 	    namestart = NULL;
1010 	    dcutfn(&namestart, &nameend, name, darg);
1011 	    ENSURE_LEN(dnewname, dlen, (size_t)(nameend - namestart + 1));
1012 	    memcpy((void *)dnewname, (void *)namestart,
1013 		   (size_t)(nameend - namestart));
1014 	    dnewname[nameend - namestart] = '\0';
1015 	    if ((rc = do_alias(dnewname, amemman, NULL, NULL, 0, FALSE,
1016 			       od->convfloor, od->multibyte, rep)) == ERR)
1017 	      name = NULL;
1018 	    else
1019 	      name = rc?((char *)(amemman->curr_pos)):dnewname;
1020 	  }
1021 	  if (name != NULL) {
1022 	    namestart = NULL;
1023 	    cutfn(&namestart, &nameend, name, FALSE);
1024 	    ENSURE_LEN(newname, len, (size_t)(nameend - namestart + 1));
1025 	    memcpy((void *)newname, (void *)namestart,
1026 		   (size_t)(nameend - namestart));
1027 	    newname[nameend - namestart] = '\0';
1028 	    name = newname;
1029 	  }
1030 	}
1031 	if (name != NULL &&
1032 	    included(name, gp->own->ispage, od->wanthead[G(rep)]))
1033 	  return(TRUE);
1034       }
1035     }
1036   }
1037   return(FALSE);  /* nothing matched, so turn the report off */
1038 }
1039 
checktreerep(Outchoices * od,Hashtable * tp,choice rep,choice requests,cutfnp cutfn)1040 logical checktreerep(Outchoices *od, Hashtable *tp, choice rep,
1041 		     choice requests, cutfnp cutfn) {
1042   unsigned long i;
1043 
1044   for (i = 0; i < tp->size; i++) {
1045     if (checkonerep(od, tp->head[i], rep, requests, cutfn, NULL, NULL))
1046       return(TRUE);
1047   }
1048   return(FALSE);
1049 }
1050 
checkarrayrep(Arraydata * array)1051 logical checkarrayrep(Arraydata *array) {
1052   choice i;
1053 
1054   for (i = 0; ; i++) {
1055     if (array[i].reqs > 0)
1056       return(TRUE);
1057     if (array[i].threshold < -0.5)
1058       return(FALSE);
1059   }
1060 }
1061 
checkreps(Outchoices * od,Dateman * dman,Hashindex ** gooditems,Arraydata ** arraydata,choice data2cols[ITEM_NUMBER][DATA_NUMBER])1062 void checkreps(Outchoices *od, Dateman *dman, Hashindex **gooditems,
1063 	       Arraydata **arraydata,
1064 	       choice data2cols[ITEM_NUMBER][DATA_NUMBER]) {
1065   extern logical *repistree;
1066 
1067   logical *repq = od->repq;
1068 
1069   cutfnp cutfn;
1070   dcutfnp dcutfn;
1071   void *darg;
1072   choice rep;
1073   int j;
1074   choice ok;
1075 
1076   if (dman->currdp == NULL) {
1077     for (rep = 0; rep < DATEREP_NUMBER; rep++) {
1078       if (repq[rep]) {
1079 	warn('R', TRUE, "Turning off empty time reports");
1080 	for ( ; rep < DATEREP_NUMBER; rep++)
1081 	  repq[rep] = FALSE;
1082       }
1083     }
1084   }
1085   for (rep = FIRST_GENREP; rep <= LAST_NORMALREP; rep++) {
1086     cutfn = repistree[G(rep)]?(od->tree[G(rep)]->cutfn):NULL;
1087     dcutfn = (rep >= FIRST_DERVREP)?(od->derv[rep - FIRST_DERVREP]->cutfn):\
1088       NULL;
1089     darg = (rep >= FIRST_DERVREP)?(od->derv[rep - FIRST_DERVREP]->arg):NULL;
1090     for (ok = 0, j = 0; od->alltrees[j] != REP_NUMBER; j++) {
1091       if (rep == od->alltrees[j])
1092 	ok = 1;
1093     }
1094     for (j = 0; od->alldervs[j] != REP_NUMBER; j++) {
1095       if (rep == od->alldervs[j])
1096 	ok = 2;
1097     }
1098     if (ok) {
1099       if (!checktreerep(od, (ok == 1)?(od->tree[G(rep)]->tree):\
1100 			(od->derv[rep - FIRST_DERVREP]->table), rep,
1101 			data2cols[rep2type[rep]][rep2reqs[G(rep)]],
1102 			(ok == 1)?NULL:cutfn)) {
1103 	/* If ok == 1, tree made, so done cutfn already; if ok == 2, made derv
1104 	   but not tree, so still need cutfn. NB i in alltrees or alldervs
1105 	   implies repq so don't have to check that. */
1106 	warn('R', TRUE, "Turning off empty %s", repname[rep]);
1107 	repq[rep] = FALSE;
1108       }
1109     }
1110     else if (repq[rep]) {
1111       if (!checkonerep(od, gooditems[rep2type[rep]], rep,
1112 		       data2cols[rep2type[rep]][rep2reqs[G(rep)]], cutfn,
1113 		       dcutfn, darg)) {
1114 	warn('R', TRUE, "Turning off empty %s", repname[rep]);
1115 	repq[rep] = FALSE;
1116       }
1117     }
1118   }
1119   for ( ; rep < REP_NUMBER; rep++) {
1120     if (repq[rep] && !checkarrayrep(arraydata[rep - FIRST_ARRAYREP])) {
1121       warn('R', TRUE, "Turning off empty %s", repname[rep]);
1122       repq[rep] = FALSE;
1123     }
1124   }
1125 }
1126