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. & 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