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 /*** outhtml.c; HTML output ***/
13
14 #include "anlghea3.h"
15
16 /* Page width */
html_pagewidth(Outchoices * od)17 unsigned int html_pagewidth(Outchoices *od) {
18 return od->htmlpagewidth;
19 }
20
21 /* The top of the output if we are in CGI mode */
html_cgihead(FILE * outf,Outchoices * od)22 void html_cgihead(FILE *outf, Outchoices *od) {
23 fprintf(outf, "Content-Type: text/html; charset=%s\n\n",
24 od->lngstr[charset_]);
25 }
26
27 /* Stuff this output style needs in the page header */
html_stylehead(FILE * outf,Outchoices * od)28 void html_stylehead(FILE *outf, Outchoices *od) {
29 fputs("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n", outf);
30 fputs("<html>\n<head>\n", outf);
31 fprintf(outf, "<meta http-equiv=\"Content-Type\" "
32 "content=\"text/html; charset=%s\">\n", od->lngstr[charset_]);
33 if (od->norobots)
34 fputs("<meta name=\"robots\" content=\"noindex,nofollow\">\n", outf);
35 fprintf(outf, "<meta name=\"GENERATOR\" content=\"analog %s\">\n",
36 VERSION);
37 fprintf(outf, "<title>%s ", od->lngstr[webstatsfor_]);
38 htmlputs(outf, od, od->hostname, FROM_CFG);
39 fputs("</title>\n", outf);
40 if (!strcaseeq(od->stylesheet, "none")) {
41 fputs("<link href=\"", outf);
42 htmlputs(outf, od, od->stylesheet, IN_HREF);
43 fputs("\" rel=\"stylesheet\">\n", outf);
44 }
45 fputs("</head>\n", outf);
46 fputs("<body>\n", outf);
47 }
48
49 /* The title of the page, plus the user's HEADERFILE */
html_pagetitle(FILE * outf,Outchoices * od)50 void html_pagetitle(FILE *outf, Outchoices *od) {
51 fputs("<h1><a NAME=\"Top\">", outf);
52 if (!strcaseeq(od->logo, "none")) {
53 fputs("<IMG src=\"", outf);
54 if (od->logo[0] != '/' && strstr(od->logo, "://") == NULL)
55 htmlputs(outf, od, od->imagedir, IN_HREF);
56 htmlputs(outf, od, od->logo, IN_HREF);
57 if (STREQ(od->logo, "analogo"))
58 fprintf(outf, ".%s", od->pngimages?"png":"gif");
59 /* Above: '.' not EXTSEP even on RISC OS */
60 fputs("\" alt=\"\"> ", outf);
61 }
62 if (strcaseeq(od->hosturl, "none")) {
63 fprintf(outf, "%s</a> ", od->lngstr[webstatsfor_]);
64 htmlputs(outf, od, od->hostname, FROM_CFG);
65 }
66 else {
67 fprintf(outf, "%s</a> <a HREF=\"", od->lngstr[webstatsfor_]);
68 htmlputs(outf, od, od->hosturl, IN_HREF);
69 fputs("\">", outf);
70 htmlputs(outf, od, od->hostname, FROM_CFG);
71 fputs("</a>", outf);
72 }
73 fputs("</h1>\n\n", outf);
74
75 if (!strcaseeq(od->headerfile, "none"))
76 html_includefile(outf, od, od->headerfile, 'h');
77 }
78
79 /* Program start time, and logfile start and end times */
html_timings(FILE * outf,Outchoices * od,Dateman * dman)80 void html_timings(FILE *outf, Outchoices *od, Dateman *dman) {
81 extern timecode_t starttimec;
82
83 char **lngstr = od->lngstr;
84
85 double t0;
86 int t1, t2;
87
88 if (od->runtime)
89 fprintf(outf, "%s %s.\n<br>", lngstr[progstart_],
90 timesprintf(od, lngstr[datefmt2_], starttimec, UNSET));
91
92 if (dman->firsttime <= dman->lasttime) {
93 fprintf(outf, "%s %s ", lngstr[reqstart_],
94 timesprintf(od, lngstr[datefmt2_], dman->firsttime, UNSET));
95 fprintf(outf, "%s %s", lngstr[to_],
96 timesprintf(od, lngstr[datefmt2_], dman->lasttime, UNSET));
97 t0 = (dman->lasttime - dman->firsttime) / 1440.0 + 0.005;
98 t1 = (int)t0;
99 t2 = (int)(100 * (t0 - (double)t1));
100 fprintf(outf, " (%d", t1);
101 html_putch(outf, od->decpt);
102 fprintf(outf, "%02d %s).\n", t2, lngstr[days_]);
103 }
104 }
105
106 /* Finishing the top of the page */
html_closehead(FILE * outf,Outchoices * od)107 void html_closehead(FILE *outf, Outchoices *od) {
108 if (od->gotos == FEW)
109 html_gotos(outf, od, -1);
110
111 html_hrule(outf, od);
112 }
113
114 /* Starting the bottom of the page */
html_pagebotstart(FILE * outf,Outchoices * od)115 void html_pagebotstart(FILE *outf, Outchoices *od) {
116 }
117
118 /* The credit line at the bottom of the page */
html_credit(FILE * outf,Outchoices * od)119 void html_credit(FILE *outf, Outchoices *od) {
120 fprintf(outf, "<i>%s <a HREF=\"%s\">analog %s</a>.\n", od->lngstr[credit_],
121 ANALOGURL, VNUMBER);
122 }
123
124 /* The program run time */
html_runtime(FILE * outf,Outchoices * od,long secs)125 void html_runtime(FILE *outf, Outchoices *od, long secs) {
126 char **lngstr = od->lngstr;
127
128 fprintf(outf, "<br><b>%s:</b> ", lngstr[runtime_]);
129
130 if (secs == 0)
131 fprintf(outf, "%s %s.\n", lngstr[lessone_], lngstr[second_]);
132 else if (secs < 60)
133 fprintf(outf, "%ld %s.\n", secs,
134 (secs == 1)?lngstr[second_]:lngstr[seconds_]);
135 else
136 fprintf(outf, "%ld %s, %ld %s.\n", secs / 60,
137 (secs < 120)?lngstr[minute_]:lngstr[minutes_], secs % 60,
138 (secs % 60 == 1)?lngstr[second_]:lngstr[seconds_]);
139 }
140
141 /* The page footer, including the user's FOOTERFILE */
html_pagefoot(FILE * outf,Outchoices * od)142 void html_pagefoot(FILE *outf, Outchoices *od) {
143 fputs("</i>\n", outf);
144 if (od->gotos != FALSE)
145 html_gotos(outf, od, -1);
146
147 if (!strcaseeq(od->footerfile, "none"))
148 html_includefile(outf, od, od->footerfile, 'f');
149
150 fputs("<p><a href=\"http://validator.w3.org/\">\n", outf);
151 fputs("<img src=\"", outf);
152 htmlputs(outf, od, od->imagedir, IN_HREF);
153 fprintf(outf, "html2.%s\"\n", od->pngimages?"png":"gif");
154 /* Above: '.' not EXTSEP even on RISC OS */
155 fputs("alt=\"HTML 2.0 Conformant!\"></a>\n", outf);
156 }
157
158 /* Footer material for this output style */
html_stylefoot(FILE * outf,Outchoices * od)159 void html_stylefoot(FILE *outf, Outchoices *od) {
160 fputs("</body>\n</html>\n", outf);
161 }
162
163 /* Report title */
html_reporttitle(FILE * outf,Outchoices * od,choice rep)164 void html_reporttitle(FILE *outf, Outchoices *od, choice rep) {
165 extern char *anchorname[];
166 extern unsigned int *rep2lng;
167
168 fprintf(outf, "<h2><a NAME=\"%s\">%s</a></h2>\n", anchorname[rep],
169 od->lngstr[rep2lng[rep]]);
170 if (od->gotos == TRUE)
171 html_gotos(outf, od, rep);
172 }
173
174 /* Report footer */
html_reportfooter(FILE * outf,Outchoices * od,choice rep)175 void html_reportfooter(FILE *outf, Outchoices *od, choice rep) {
176 }
177
178 /* Report description */
html_reportdesc(FILE * outf,Outchoices * od,choice rep)179 void html_reportdesc(FILE *outf, Outchoices *od, choice rep) {
180 fprintf(outf, "<p><em>%s</em>\n", od->descstr[rep]);
181 }
182
183 /* The time period spanned by the report */
html_reportspan(FILE * outf,Outchoices * od,choice rep,timecode_t maxd,timecode_t mind)184 void html_reportspan(FILE *outf, Outchoices *od, choice rep, timecode_t maxd,
185 timecode_t mind) {
186 /* NB Can't combine next two lines because timesprintf uses static buffer. */
187 fprintf(outf, "<p><em>%s %s ", od->lngstr[repspan_],
188 timesprintf(od, od->lngstr[datefmt2_], mind, UNSET));
189 fprintf(outf, "%s %s.</em>\n", od->lngstr[to_],
190 timesprintf(od, od->lngstr[datefmt2_], maxd, UNSET));
191 }
192
193 /* General Summary header */
html_gensumhead(FILE * outf,Outchoices * od)194 void html_gensumhead(FILE *outf, Outchoices *od) {
195 }
196
197 /* General Summary footer */
html_gensumfoot(FILE * outf,Outchoices * od)198 void html_gensumfoot(FILE *outf, Outchoices *od) {
199 }
200
201 /* Single General Summary line, long data */
html_gensumline(FILE * outf,Outchoices * od,int namecode,unsigned long x,unsigned long x7,logical isaverage)202 void html_gensumline(FILE *outf, Outchoices *od, int namecode,
203 unsigned long x, unsigned long x7, logical isaverage) {
204
205 /* If this is the first Gen Sum line, and there is no seven-day data, then
206 start a new paragraph. Otherwise the new paragraph has already been
207 started in html_lastseven(). */
208 if (namecode == succreqs_ && x7 == (unsigned long)UNSET)
209 fprintf(outf, "<p><b>%s%s</b> ", od->lngstr[namecode], od->lngstr[colon_]);
210 else
211 fprintf(outf, "<br><b>%s%s</b> ", od->lngstr[namecode],
212 od->lngstr[colon_]);
213
214 f3printf(outf, od, (double)x, 0, od->sepchar);
215
216 if (x7 != (unsigned long)UNSET) {
217 fputs(" (", outf);
218 f3printf(outf, od, (double)x7, 0, od->sepchar);
219 putc(')', outf);
220 }
221 putc('\n', outf);
222 }
223
224 /* Single General Summary line, bytes data */
html_gensumlineb(FILE * outf,Outchoices * od,int namecode,double x,double x7,logical isaverage)225 void html_gensumlineb(FILE *outf, Outchoices *od, int namecode, double x,
226 double x7, logical isaverage) {
227 char **lngstr = od->lngstr;
228
229 unsigned int bm;
230 char *c;
231
232 fprintf(outf, "<br><b>%s%s</b> ", lngstr[namecode], lngstr[colon_]);
233
234 bm = (od->rawbytes)?0:findbmult(x, od->bytesdp);
235 printbytes(outf, od, x, bm, 0, od->sepchar);
236
237 if (bm > 0) {
238 c = strchr(lngstr[xbytes_], '?');
239 *c = '\0';
240 fprintf(outf, " %s%s%s", lngstr[xbytes_], lngstr[byteprefix_ + bm], c + 1);
241 *c = '?';
242 }
243 else
244 fprintf(outf, " %s", lngstr[bytes_]);
245
246 if (x7 != UNSET) {
247 fputs(" (", outf);
248 bm = (od->rawbytes)?0:findbmult(x7, od->bytesdp);
249 printbytes(outf, od, x7, bm, 0, od->sepchar);
250 if (bm > 0) {
251 c = strchr(lngstr[xbytes_], '?');
252 *c = '\0';
253 fprintf(outf, " %s%s%s)", lngstr[xbytes_], lngstr[byteprefix_ + bm],
254 c + 1);
255 *c = '?';
256 }
257 else
258 fprintf(outf, " %s)", lngstr[bytes_]);
259 }
260
261 putc('\n', outf);
262 }
263
264 /* "Last seven" explanation line */
html_lastseven(FILE * outf,Outchoices * od,timecode_t last7to)265 void html_lastseven(FILE *outf, Outchoices *od, timecode_t last7to) {
266 fputs("<p>", outf);
267 fprintf(outf, "(%s %s %s).\n", od->lngstr[brackets_],
268 od->lngstr[sevendaysto_],
269 timesprintf(od, od->lngstr[datefmt1_], last7to, UNSET));
270 }
271
272 /* Start of a <pre> section */
html_prestart(FILE * outf,Outchoices * od)273 void html_prestart(FILE *outf, Outchoices *od) {
274 fputs("<pre><tt>", outf);
275 }
276
277 /* End of a <pre> section */
html_preend(FILE * outf,Outchoices * od)278 void html_preend(FILE *outf, Outchoices *od) {
279 fputs("</tt></pre>\n", outf);
280 }
281
282 /* A horizontal rule */
html_hrule(FILE * outf,Outchoices * od)283 void html_hrule(FILE *outf, Outchoices *od) {
284 fputs("<hr>\n", outf);
285 }
286
287 /* An en dash */
html_endash(void)288 char *html_endash(void) {
289 return "-";
290 /* Should be – but not all browsers implement –, and when
291 they do it's usually just a regular dash. */
292 }
293
294 /* putc with special characters escaped */
html_putch(FILE * outf,char c)295 void html_putch(FILE *outf, char c) {
296 if (c == '<')
297 fputs("<", outf);
298 else if (c == '>')
299 fputs(">", outf);
300 else if (c == '&')
301 fputs("&", outf);
302 else if (c == '"')
303 fputs(""", outf);
304 else
305 putc(c, outf);
306 }
307
308 /* strlen for HTML strings. Assume string contains no &'s except as markup. */
309 /* NB This may not work well for multibyte charsets, but it's hard to know
310 whether an & is markup or just a byte of a multibyte character. Special
311 cases for particular charsets are given below and selected in init.c. */
312 /* NOTE html_strlengthgth_utf8 and _jis in outhtml only. Other outputters just
313 have _strlengthgth */
html_strlength(const char * s)314 size_t html_strlength(const char *s) {
315 const char *t;
316 logical f;
317 size_t i;
318
319 for (t = s, f = TRUE, i = 0; *t != '\0'; t++) {
320 if (*t == '&')
321 f = FALSE;
322 else if (*t == ';')
323 f = TRUE;
324 if (f)
325 i++;
326 }
327 return(f?i:strlen(s));
328 /* If !f, something went wrong (eg multibyte). Maybe the & wasn't markup. */
329 }
330
html_strlength_utf8(const char * s)331 size_t html_strlength_utf8(const char *s) {
332 /* This only knows about jp chars in the range 1110xxxx 10xxxxxx 10xxxxxx.
333 Other languages using UTF-8 would need new code. */
334 const unsigned char *t;
335 size_t i;
336
337 for (i = 0, t = (const unsigned char *)s; *t != '\0'; t++) {
338 if ((*t & 0xf0) == 0xe0 && (*(t + 1) & 0xc0) == 0x80 &&
339 (*(t + 2) & 0xc0) == 0x80) {
340 t += 2; /* plus 1 in loop */
341 i += 2;
342 /* three-character jp sequence = one jp char = length 2 wrt ASCII */
343 }
344 else
345 i++;
346 }
347 return(i);
348 }
349
html_strlength_jis(const char * s)350 size_t html_strlength_jis(const char *s) {
351 size_t i;
352
353 for (i = 0; *s != '\0'; s++) {
354 if (*s == 0x1B && (*(s + 1) == '$' || *(s + 1) == '(') && *(s + 2) == 'B')
355 s += 2; /* plus 1 in loop */ /* ignore ESC $ B and ESC ( B switches */
356 else
357 i++;
358 }
359 return(i); /* returns length in bytes, because one jp char = two bytes and
360 width of one jp char = width of two ASCII chars */
361 }
362
363 /* Allow month in dates? DO NOT enable for human-readable text because of
364 i18n problems. */
html_allowmonth(void)365 logical html_allowmonth(void) {
366 return FALSE;
367 }
368
369 /* Calculate column widths */
html_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)370 void html_calcwidths(Outchoices *od, choice rep, unsigned int width[],
371 unsigned int *bmult, unsigned int *bmult7, double *unit,
372 unsigned long maxr, unsigned long maxr7,
373 unsigned long maxp, unsigned long maxp7, double maxb,
374 double maxb7, unsigned long howmany) {
375 calcwidths(od, rep, width, bmult, bmult7, unit, maxr, maxr7, maxp, maxp7,
376 maxb, maxb7, howmany);
377 }
378
379 /* "Each unit represents" line */
html_declareunit(FILE * outf,Outchoices * od,char graphby,double unit,unsigned int bmult)380 void html_declareunit(FILE *outf, Outchoices *od, char graphby, double unit,
381 unsigned int bmult) {
382 char **lngstr = od->lngstr;
383
384 char *s;
385
386 fputs("<p>\n", outf);
387 fprintf(outf, "%s (", lngstr[eachunit_]);
388 if (ISLOWER(graphby))
389 fprintf(outf, "<tt>%c</tt>", od->markchar);
390 else {
391 fprintf(outf, "<img src=\"");
392 htmlputs(outf, od, od->imagedir, IN_HREF);
393 fprintf(outf, "bar%c1.%s\" alt=\"%c\">", od->barstyle,
394 od->pngimages?"png":"gif", od->markchar);
395 /* Above: '.' not EXTSEP even on RISC OS */
396 }
397 fprintf(outf, ") %s ", lngstr[represents_]);
398
399 if (graphby == 'R' || graphby == 'r') {
400 f3printf(outf, od, unit, 0, od->sepchar);
401 if (unit == 1.)
402 fprintf(outf, " %s.", lngstr[request_]);
403 else
404 fprintf(outf, " %s %s.", lngstr[requests_], lngstr[partof_]);
405 }
406 else if (graphby == 'P' || graphby == 'p') {
407 f3printf(outf, od, unit, 0, od->sepchar);
408 if (unit == 1.)
409 fprintf(outf, " %s.", lngstr[pagereq_]);
410 else
411 fprintf(outf, " %s %s.", lngstr[pagereqs_], lngstr[partof_]);
412 }
413 else {
414 if (bmult > 0) {
415 html_printdouble(outf, od, unit);
416 s = strchr(lngstr[xbytes_], '?'); /* checked in initialisation */
417 *s = '\0';
418 fprintf(outf, " %s%s%s %s.", lngstr[xbytes_],
419 lngstr[byteprefix_ + bmult], s + 1, lngstr[partof_]);
420 *s = '?';
421 }
422 else {
423 f3printf(outf, od, unit, 0, od->sepchar);
424 fprintf(outf, " %s %s.", lngstr[bytes_], lngstr[partof_]);
425 }
426 }
427 putc('\n', outf);
428 }
429
430 /* Start of column header line */
html_colheadstart(FILE * outf,Outchoices * od,choice rep)431 void html_colheadstart(FILE *outf, Outchoices *od, choice rep) {
432 }
433
434 /* Column header line: individual column */
html_colheadcol(FILE * outf,Outchoices * od,choice rep,choice col,unsigned int width,char * colname,logical unterminated)435 void html_colheadcol(FILE *outf, Outchoices *od, choice rep, choice col,
436 unsigned int width, char *colname, logical unterminated) {
437 if (unterminated)
438 fputs(colname, outf);
439 else
440 fprintf(outf, "%*s: ",
441 width + strlen(colname) - od->outputter->strlength(colname),
442 colname);
443 }
444
445 /* End of column header line */
html_colheadend(FILE * outf,Outchoices * od,choice rep)446 void html_colheadend(FILE *outf, Outchoices *od, choice rep) {
447 putc('\n', outf);
448 }
449
450 /* Start of column header underlining line */
html_colheadustart(FILE * outf,Outchoices * od,choice rep)451 void html_colheadustart(FILE *outf, Outchoices *od, choice rep) {
452 }
453
454 /* Underlining of one column header. */
455 /* If column is terminated, set width and leave name blank; and conversely. */
html_colheadunderline(FILE * outf,Outchoices * od,choice rep,choice col,unsigned int width,char * name)456 void html_colheadunderline(FILE *outf, Outchoices *od, choice rep, choice col,
457 unsigned int width, char *name) {
458 if (width > 0) {
459 matchlengthn(outf, od, (size_t)width, '-');
460 fputs(": ", outf);
461 }
462 else
463 matchlength(outf, od, name, '-');
464 }
465
466 /* End of column header underlining line */
html_colheaduend(FILE * outf,Outchoices * od,choice rep)467 void html_colheaduend(FILE *outf, Outchoices *od, choice rep) {
468 putc('\n', outf);
469 }
470
471 /* Start of a table row */
html_rowstart(FILE * outf,Outchoices * od,choice rep,choice * cols,int level,char * name,char * datefmt,char * timefmt)472 void html_rowstart(FILE *outf, Outchoices *od, choice rep, choice *cols,
473 int level, char *name, char *datefmt, char *timefmt) {
474 }
475
476 /* Print level in hierarchy represented by this row */
html_levelcell(FILE * outf,Outchoices * od,choice rep,int level)477 void html_levelcell(FILE *outf, Outchoices *od, choice rep, int level) {
478 }
479
480 /* Name column */
html_namecell(FILE * outf,Outchoices * od,choice rep,char * name,choice source,unsigned int width,logical name1st,logical isfirst,logical rightalign,Alias * aliashead,Include * linkhead,logical ispage,unsigned int spaces,char * baseurl)481 void html_namecell(FILE *outf, Outchoices *od, choice rep, char *name,
482 choice source, unsigned int width, logical name1st,
483 logical isfirst, logical rightalign, Alias *aliashead,
484 Include *linkhead, logical ispage, unsigned int spaces,
485 char *baseurl) {
486 extern char *workspace;
487
488 choice savemultibyte;
489 logical linked;
490 int i;
491
492 if (name1st != isfirst)
493 return;
494
495 if (isfirst)
496 matchlengthn(outf, od, width - od->outputter->strlength(name), ' ');
497 savemultibyte = od->multibyte;
498 if (rep == REP_SIZE || rep == REP_PROCTIME)
499 /* Kludge: for these two reports, we know the texts are things like
500 "< 1" and we want to convert > and < */
501 od->multibyte = FALSE;
502
503 strcpy(workspace, name);
504 do_aliasx(workspace, aliashead);
505
506 if (!isfirst) {
507 if (rightalign)
508 i = (int)width - (int)od->outputter->strlength(workspace) - (int)spaces;
509 else
510 i = (int)spaces;
511 matchlengthn(outf, od, i, ' ');
512 }
513
514 linked = (linkhead != NULL && included(name, ispage, linkhead));
515 if (linked) {
516 /* We link to the unaliased name, because the OUTPUTALIAS is usually in
517 the nature of an annotation. */
518 fputs("<a href=\"", outf);
519 if (baseurl != NULL)
520 htmlputs(outf, od, baseurl, IN_HREF);
521 html_escfprintf(outf, name);
522 fputs("\">", outf);
523 }
524
525 htmlputs(outf, od, workspace, source);
526
527 if (linked)
528 fputs("</a>", outf);
529
530 if (isfirst)
531 fputs(": ", outf);
532
533 od->multibyte = savemultibyte; /* restore multibyte */
534 }
535
536 /* Single cell, unsigned long argument */
html_ulcell(FILE * outf,Outchoices * od,choice rep,choice col,unsigned long x,unsigned int width)537 void html_ulcell(FILE *outf, Outchoices *od, choice rep, choice col,
538 unsigned long x, unsigned int width) {
539 f3printf(outf, od, (double)x, width, od->repsepchar);
540 fputs(": ", outf);
541 }
542
543 /* Single cell, TRUSTED string argument */
html_strcell(FILE * outf,Outchoices * od,choice rep,choice col,char * s,unsigned int width)544 void html_strcell(FILE *outf, Outchoices *od, choice rep, choice col,
545 char *s, unsigned int width) {
546 matchlengthn(outf, od, width - od->outputter->strlength(s), ' ');
547 htmlputs(outf, od, s, TRUSTED);
548 fputs(": ", outf);
549 }
550
551 /* Single cell, listing bytes */
html_bytescell(FILE * outf,Outchoices * od,choice rep,choice col,double b,double bmult,unsigned int width)552 void html_bytescell(FILE *outf, Outchoices *od, choice rep, choice col,
553 double b, double bmult, unsigned int width) {
554 printbytes(outf, od, b, bmult, width, od->repsepchar);
555 fputs(": ", outf);
556 }
557
558 /* Single cell, listing percentage */
html_pccell(FILE * outf,Outchoices * od,choice rep,choice col,double n,double tot,unsigned int width)559 void html_pccell(FILE *outf, Outchoices *od, choice rep, choice col, double n,
560 double tot, unsigned int width) {
561 double pc;
562 unsigned int pc1, pc2;
563
564 matchlengthn(outf, od, width - 6, ' ');
565 if (tot == 0)
566 pc = 0.0;
567 else
568 pc = n * 10000.0 / tot;
569 if (pc >= 9999.5)
570 fputs(" 100%", outf);
571 else if (pc < 0.5)
572 fputs(" ", outf);
573 else {
574 pc1 = ((int)(pc + 0.5)) / 100;
575 pc2 = ((int)(pc + 0.5)) % 100;
576 fprintf(outf, "%2d", pc1);
577 html_putch(outf, od->decpt);
578 fprintf(outf, "%02d%%", pc2);
579 }
580 fputs(": ", outf);
581 }
582
583 /* Single cell, index */
html_indexcell(FILE * outf,Outchoices * od,choice rep,choice col,long index,unsigned int width)584 void html_indexcell(FILE *outf, Outchoices *od, choice rep, choice col,
585 long index, unsigned int width) {
586 /* If index is 0 (i.e. sub-item), just print spaces */
587 if (index <= 0)
588 matchlengthn(outf, od, width, ' ');
589 else
590 f3printf(outf, od, (double)index, width, od->repsepchar);
591 fputs(": ", outf);
592 }
593
594 /* End of a table row */
html_rowend(FILE * outf,Outchoices * od,choice rep)595 void html_rowend(FILE *outf, Outchoices *od, choice rep) {
596 putc('\n', outf);
597 }
598
599 /* Blank line in time reports */
html_blankline(FILE * outf,Outchoices * od,choice * cols)600 void html_blankline(FILE *outf, Outchoices *od, choice *cols) {
601 putc('\n', outf);
602 }
603
604 /* Barchart in time reports */
html_barchart(FILE * outf,Outchoices * od,int y,char graphby)605 void html_barchart(FILE *outf, Outchoices *od, int y, char graphby) {
606 int i, j;
607 logical first = TRUE;
608
609 if (ISLOWER(graphby)) {
610 for (i = 0; i < y; i++)
611 html_putch(outf, od->markchar);
612 return;
613 }
614
615 for (j = 32; j >= 1; j /= 2) {
616 while (y >= j) {
617 fputs("<img src=\"", outf);
618 htmlputs(outf, od, od->imagedir, IN_HREF);
619 fprintf(outf, "bar%c%d.%s\" alt=\"", od->barstyle, j,
620 od->pngimages?"png":"gif"); /* '.' not EXTSEP even on RISC OS */
621 if (first) {
622 for (i = 0; i < y; i++)
623 html_putch(outf, od->markchar);
624 first = FALSE;
625 }
626 fputs("\">", outf);
627 y -= j;
628 }
629 }
630 }
631
632 /* "Busiest time period" line */
html_busyprintf(FILE * outf,Outchoices * od,choice rep,char * datefmt,unsigned long reqs,unsigned long pages,double bys,datecode_t date,unsigned int hr,unsigned int min,datecode_t newdate,unsigned int newhr,unsigned int newmin,char graphby)633 void html_busyprintf(FILE *outf, Outchoices *od, choice rep, char *datefmt,
634 unsigned long reqs, unsigned long pages, double bys,
635 datecode_t date, unsigned int hr, unsigned int min,
636 datecode_t newdate, unsigned int newhr,
637 unsigned int newmin, char graphby) {
638 extern unsigned int *rep2busystr;
639
640 char **lngstr = od->lngstr;
641 char sepchar = od->sepchar;
642
643 unsigned int bmult;
644 char *s;
645
646 fprintf(outf, "%s %s (", lngstr[rep2busystr[rep]],
647 datesprintf(od, datefmt, date, hr, min, newdate, newhr, newmin,
648 TRUE, UNSET));
649 if (TOLOWER(graphby) == 'r') {
650 f3printf(outf, od, (double)reqs, 0, sepchar);
651 fprintf(outf, " %s).\n", (reqs == 1)?lngstr[request_]:lngstr[requests_]);
652 }
653 else if (TOLOWER(graphby) == 'p') {
654 f3printf(outf, od, (double)pages, 0, sepchar);
655 fprintf(outf, " %s).\n",
656 (pages == 1)?lngstr[pagereq_]:lngstr[pagereqs_]);
657 }
658 else /* TOLOWER(graphby) == 'b' */ {
659 if (od->rawbytes)
660 bmult = 0;
661 else
662 bmult = findbmult(bys, od->bytesdp);
663 printbytes(outf, od, bys, bmult, 0, sepchar);
664 putc(' ', outf);
665 if (bmult >= 1) {
666 s = strchr(lngstr[xbytes_], '?'); /* checked in initialisation */
667 *s = '\0';
668 fprintf(outf, "%s%s%s).\n", lngstr[xbytes_],
669 lngstr[byteprefix_ + bmult], s + 1);
670 *s = '?';
671 }
672 else
673 fprintf(outf, "%s).\n", lngstr[bytes_]);
674 }
675 }
676
677 /* End of "Not listed" line. */
html_notlistedstr(FILE * outf,Outchoices * od,choice rep,unsigned long badn)678 void html_notlistedstr(FILE *outf, Outchoices *od, choice rep,
679 unsigned long badn) {
680 extern unsigned int *rep2lng, *rep2colhead;
681
682 char **lngstr = od->lngstr;
683 char *colhead = lngstr[rep2colhead[rep]];
684 char *colheadp = lngstr[rep2colhead[rep] + 1];
685 char gender = lngstr[rep2lng[rep] + 3][0];
686
687 char *notlistedstr;
688
689 if (gender == 'm')
690 notlistedstr = lngstr[notlistedm_];
691 else if (gender == 'f')
692 notlistedstr = lngstr[notlistedf_];
693 else
694 notlistedstr = lngstr[notlistedn_];
695
696 fprintf(outf, "[%s: ", notlistedstr);
697 f3printf(outf, od, (double)badn, 0, od->sepchar);
698 fprintf(outf, " %s]", (badn == 1)?colhead:colheadp);
699 }
700
701 /* The line declaring the floor and sort for a report */
html_whatincluded(FILE * outf,Outchoices * od,choice rep,unsigned long n,Dateman * dman)702 void html_whatincluded(FILE *outf, Outchoices *od, choice rep,
703 unsigned long n, Dateman *dman) {
704 whatincluded(outf, od, rep, n, dman);
705 }
706
707 /* Spacing at the start of the whatincluded line */
html_whatinchead(FILE * outf,Outchoices * od)708 void html_whatinchead(FILE *outf, Outchoices *od) {
709 fputs("<p>\n", outf);
710 }
711
712 /* Finishing the whatincluded line */
html_whatincfoot(FILE * outf,Outchoices * od)713 void html_whatincfoot(FILE *outf, Outchoices *od) {
714 putc('\n', outf);
715 }
716
717 /* Printing part of the whatincluded line */
html_whatincprintstr(FILE * outf,Outchoices * od,char * s)718 void html_whatincprintstr(FILE *outf, Outchoices *od, char *s) {
719 htmlputs(outf, od, s, TRUSTED);
720 }
721
722 /* Print a double to a nice number of decimal places */
html_printdouble(FILE * outf,Outchoices * od,double x)723 void html_printdouble(FILE *outf, Outchoices *od, double x) {
724 unsigned int prec;
725 double d;
726
727 /* first calculate how many decimal places we need */
728
729 for (prec = 0, d = x - (double)((int)(x));
730 d - (double)((int)(d + 0.000005)) > 0.00001; d *= 10)
731 prec++;
732
733 /* now print it */
734
735 if (prec > 0) {
736 fprintf(outf, "%d", (int)x);
737 html_putch(outf, od->decpt);
738 fprintf(outf, "%0*d", prec, (int)(d + EPSILON));
739 }
740 else
741 fprintf(outf, "%d", (int)(x + EPSILON));
742 }
743
744 /* Include a header file or footer file */
html_includefile(FILE * outf,Outchoices * od,char * name,char type)745 void html_includefile(FILE *outf, Outchoices *od, char *name, char type) {
746 FILE *inf;
747 char buffer[BLOCKSIZE];
748 size_t n;
749
750 if ((inf = my_fopen(name, (type == 'h')?"header file":"footer file")) !=
751 NULL) {
752 od->html = FALSE;
753 html_hrule(outf, od);
754 while ((n = fread(buffer, 1, BLOCKSIZE, inf))) /* single equals */
755 fwrite((void *)buffer, 1, n, outf);
756 if (type == 'h')
757 html_hrule(outf, od);
758 (void)my_fclose(inf, name, (type == 'h')?"header file":"footer file");
759 }
760 }
761
762 /* Filetype for RISC OS */
html_riscosfiletype(void)763 unsigned int html_riscosfiletype(void) {
764 return 0xfaf;
765 }
766
767 /* ======================================================================= */
768 /* Supporting functions for HTML */
769
770 /* Print "goto"s. Assume we've checked that we want gotos here. */
html_gotos(FILE * outf,Outchoices * od,choice rep)771 void html_gotos(FILE *outf, Outchoices *od, choice rep) {
772 extern unsigned int *rep2lng;
773 extern char *anchorname[];
774
775 choice *reporder = od->reporder;
776 char **lngstr = od->lngstr;
777 int i;
778
779 fprintf(outf, "<p>(<b>%s</b>", lngstr[goto_]);
780 fprintf(outf, "%s <a HREF=\"#Top\">%s</a>", lngstr[colon_], lngstr[top_]);
781 for (i = 0; reporder[i] != -1; i++) {
782 if (reporder[i] == rep)
783 fprintf(outf, "%s %s", lngstr[colon_], lngstr[rep2lng[reporder[i]]]);
784 else if (od->repq[reporder[i]])
785 fprintf(outf, "%s <a HREF=\"#%s\">%s</a>", lngstr[colon_],
786 anchorname[reporder[i]], lngstr[rep2lng[reporder[i]]]);
787 }
788 fputs(")\n", outf);
789 }
790
791 /* Escape names for use in hyperlinks. As with htmlputs(), don't try and
792 print character by character. */
html_escfprintf(FILE * outf,char * name)793 void html_escfprintf(FILE *outf, char *name) {
794 #ifdef EBCDIC
795 extern unsigned char os_toascii[];
796 #endif
797 char w1[64];
798 char *w = w1;
799 int len = 0;
800
801 for ( ; *name != '\0'; name++) {
802 if (ISALNUM(*name) || *name == '/' || *name == '.' || *name == ':' ||
803 *name == '-' || *name == '~' || *name == '_' || *name == '?' ||
804 *name == '%' || *name == '=' || *name == '+' ||
805 *name == ';' || *name == '@' || *name == '$' || *name == ',') {
806 /* All reserved and some unreserved chars from RFC 2396 Sec 2. */
807 /* Reserved chars are not escaped because if they are in the logfile they
808 must have their special meanings (path delimiters etc.), and escaping
809 them would change the semantics of the URL. */
810 PUTc(w, *name);
811 len += 1;
812 }
813 else if (*name == '&') {
814 PUTs(w, "&", 0);
815 len += 5;
816 }
817 else {
818 #ifdef EBCDIC
819 sprintf(w, "%%%.2X", os_toascii[*name]);
820 #else
821 sprintf(w, "%%%.2X", (unsigned char)(*name));
822 #endif
823 w += 3;
824 len += 3;
825 }
826 if (len > 58) {
827 *w = '\0';
828 fputs(w1, outf);
829 w = w1;
830 len = 0;
831 }
832 }
833 *w = '\0';
834 fputs(w1, outf);
835 }
836
837 /* htmlputs(): print a string with an appropriate amount of HTML encoding.
838 Much quicker than using html_putch(). */
839
840 /** What to convert has SECURITY IMPLICATIONS. An attacker must not be allowed
841 ** to insert abitrary data in the output.
842 **
843 ** So data is categorised according to its source, via an enum in anlghea3.h.
844 ** In the following descriptions of the security levels, "convert" means
845 ** converting e.g. < to < and "escape" means using \< to prevent this
846 ** happening. "unprintable" means characters set as unprintable in init.c:
847 ** note that this is only known unprintable characters 0x00 - 0x1F, 0x7F,
848 ** and in ISO-8859-* also 0x80-0x9F.
849 **
850 ** 1) AS_IS: Completely trusted string. Output as-is.
851 ** 2) TRUSTED: E.g. a string from a language file. Also completely trusted. In
852 ** single-byte mode, convert characters (for convenience not security), but
853 ** allow any of the special characters to be escaped, even \< .
854 ** In multibyte mode, output the string as-is.
855 ** 3) FROM_CFG: An item read in from configuration. Unless we're in CGI mode,
856 ** treat as case 2. In CGI mode, the input could have come from the form,
857 ** so be more cautious to avoid cross-site scripting attacks. Specifically,
858 ** convert all characters, allow only \& and \\ escapes, and use '?' in
859 ** place of unprintable characters.
860 ** 4) UNTRUSTED: E.g. data from the logfile. Do all conversions, don't allow
861 ** any escapes, and use '?' in place of all unprintable characters.
862 ** 5) IN_HREF: Special case for data from the config file which is being put
863 ** inside an <a href=""> or <img src="">. As 3), but use %nm in place of
864 ** unprintable characters. (NB data from the logfile which is put inside
865 ** an href uses escfprintf() instead of this function.)
866 **/
htmlputs(FILE * outf,Outchoices * od,char * s,choice source)867 void htmlputs(FILE *outf, Outchoices *od, char *s, choice source) {
868 #ifdef EBCDIC
869 extern unsigned char os_toascii[];
870 #endif
871 extern logical cgi;
872 extern logical unprintable[256];
873
874 char w1[64];
875 char *c;
876 char *w = w1;
877 int len = 0;
878
879 if (source == FROM_CFG && !cgi)
880 source = TRUSTED;
881
882 if (source == TRUSTED && od->multibyte)
883 source = AS_IS;
884
885 if (source == AS_IS) {
886 fputs(s, outf);
887 return;
888 }
889
890 for (c = s; *c != '\0'; c++) {
891 if (*c == '<') {
892 PUTs(w, "<", 0);
893 len += 4;
894 }
895 else if (*c == '>') {
896 PUTs(w, ">", 0);
897 len += 4;
898 }
899 else if (*c == '&') {
900 PUTs(w, "&", 0);
901 len += 5;
902 }
903 else if (*c == '"') {
904 PUTs(w, """, 0);
905 len += 6;
906 }
907 else if (*c == '\\' && /* escape these chars in these circumstances: */
908 ((source == TRUSTED && (*(c + 1) == '<' || *(c + 1) == '>' ||
909 *(c + 1) == '&' || *(c + 1) == '"' ||
910 *(c + 1) == '\\'))
911 || (source == FROM_CFG && (*(c + 1) == '&' ||
912 *(c + 1) == '\\')))) {
913 od->html = FALSE;
914 PUTc(w, *(++c));
915 len += 1;
916 }
917 else if (unprintable[(unsigned char)(*c)] && source != TRUSTED) {
918 /* unprintable chars */
919 if (source == IN_HREF) {
920 #ifdef EBCDIC
921 sprintf(w, "%%%.2X", os_toascii[*c]);
922 #else
923 sprintf(w, "%%%.2X", (unsigned char)(*c));
924 #endif
925 w += 3;
926 len += 3;
927 }
928 else { /* source == FROM_CFG or UNTRUSTED */
929 PUTc(w, '?');
930 len += 1;
931 }
932 }
933 else { /* output non-special characters as-is */
934 PUTc(w, *c);
935 len += 1;
936 }
937 if (len > 56) {
938 *w = '\0';
939 fputs(w1, outf);
940 w = w1;
941 len = 0;
942 }
943 }
944 *w = '\0';
945 fputs(w1, outf);
946 }
947
948 /* ======================================================================= */
949 /* Pie charts */
950 /* Shared by HTML and XHTML */
951
952 #ifndef NOGRAPHICS
953 /* First some #defines and globals */
954 #define XSIZE (600) /* Size of whole graphic */
955 #define YSIZE (270)
956 #define SHORTXSIZE (2 * (XCENTRE)) /* Size if no text on picture */
957 #define SHORTYSIZE (2 * (YCENTRE))
958 #define XCENTRE (125) /* Centre of pie */
959 #define YCENTRE (125)
960 #define DIAMETER (200) /* Diameter of pie */
961 #define BORDER (4) /* Size of border */
962 #define BOXESLEFT (XCENTRE + DIAMETER / 2 + 25) /* The coloured boxes */
963 #define BOXESTOP (YCENTRE - DIAMETER / 2 + 16)
964 #define BOXESSIZE (10)
965 #define TEXTLEFT ((BOXESLEFT) + 2 * (BOXESSIZE)) /* The labels */
966 #define TEXTOFFSET (-1)
967 #define TEXTGAP (16) /* Vertical period between successive boxes/labels */
968 #define CAPTIONLEFT (XCENTRE - DIAMETER / 2) /* Bottom caption */
969 #define CAPTIONTOP (YCENTRE + DIAMETER / 2 + 12)
970 #define NO_COLOURS (10) /* Number of text strings, excluding "Other" */
971 #define MAXCHARS (54) /* Max length of a label, INCLUDING \0. */
972 #define TWOPI (6.283185)
973 #define MINANGLE (0.01) /* Min fraction of circle we are prepared to plot */
974 #define PIE_EPSILON (0.0001)
975 gdImagePtr im;
976 gdFontPtr font;
977 logical normalchart;
978 int white, black, grey, lightgrey, colours[NO_COLOURS], col, boxesy;
979 double totangle;
980
piechart_init(char * filename)981 FILE *piechart_init(char *filename) {
982 FILE *pieoutf;
983 int xsize, ysize, b1, b2;
984
985 if ((pieoutf = FOPENWB(filename)) == NULL) {
986 warn('F', TRUE, "Failed to open pie chart file %s for writing: "
987 "ignoring it", filename);
988 return(pieoutf);
989 }
990 debug('F', "Opening %s as pie chart file", filename);
991 #ifdef RISCOS
992 _swix(OS_File, _INR(0,2), 18, filename, 0xb60); /* set PNG filetype */
993 #endif
994
995 xsize = normalchart?XSIZE:SHORTXSIZE;
996 ysize = normalchart?YSIZE:SHORTYSIZE;
997 im = gdImageCreate(xsize, ysize);
998 /* The first colour allocated in a new image is the background colour. */
999 white = gdImageColorAllocate(im, 255, 255, 255); /* white */
1000 black = gdImageColorAllocate(im, 0, 0, 0); /* black */
1001 grey = gdImageColorAllocate(im, 128, 128, 128); /* grey */
1002 lightgrey = gdImageColorAllocate(im, 217, 217, 217); /* light grey */
1003 col = 0;
1004 /* Wedge colours. If these change, so must images/sq*. */
1005 colours[col++] = gdImageColorAllocate(im, 255, 0, 0); /* red */
1006 colours[col++] = gdImageColorAllocate(im, 0, 0, 255); /* mid blue */
1007 colours[col++] = gdImageColorAllocate(im, 0, 128, 0); /* green */
1008 colours[col++] = gdImageColorAllocate(im, 255, 128, 0); /* orange */
1009 colours[col++] = gdImageColorAllocate(im, 0, 0, 128); /* navy blue */
1010 colours[col++] = gdImageColorAllocate(im, 0, 255, 0); /* pale green */
1011 colours[col++] = gdImageColorAllocate(im, 255, 128, 128); /* pink */
1012 colours[col++] = gdImageColorAllocate(im, 0, 255, 255); /* cyan */
1013 colours[col++] = gdImageColorAllocate(im, 128, 0, 128); /* purple */
1014 colours[col++] = gdImageColorAllocate(im, 255, 255, 0); /* yellow */
1015 col = 0;
1016 totangle = 0.75; /* starting at the top */
1017 boxesy = BOXESTOP;
1018 b1 = xsize - 1 - BORDER;
1019 b2 = ysize - 1 - BORDER;
1020 /* Plot outline of pie, and border of image */
1021 gdImageArc(im, XCENTRE, YCENTRE, DIAMETER + 2, DIAMETER + 2, 0, 360, black);
1022 gdImageRectangle(im, BORDER, BORDER, b1, b2, black);
1023 gdImageLine(im, xsize - 1, 0, b1, BORDER, black);
1024 gdImageLine(im, 0, ysize - 1, BORDER, b2, black);
1025 gdImageFill(im, 0, 0, lightgrey);
1026 gdImageFill(im, xsize - 1, ysize - 1, grey);
1027 gdImageLine(im, 0, 0, BORDER, BORDER, black);
1028 gdImageLine(im, xsize - 1, ysize - 1, b1, b2, black);
1029 return(pieoutf);
1030 }
1031
findwedges(Wedge wedge[NO_COLOURS],choice rep,Hashindex * items,choice chartby,Strlist * expandlist,unsigned int level,Strlist * partname,unsigned long tot,double totb,double totb7)1032 void findwedges(Wedge wedge[NO_COLOURS], choice rep, Hashindex *items,
1033 choice chartby, Strlist *expandlist, unsigned int level,
1034 Strlist *partname, unsigned long tot, double totb,
1035 double totb7) {
1036 /* Calculate which wedges we actually want, i.e. the ten with the biggest
1037 angles. But we also preserve the sort order of the "items" list. (Be
1038 careful between > and >= so as to use that order for breaking ties).
1039 Construction of name same as in printtree(). */
1040 static double smallestangle;
1041 static int smallestwedge;
1042
1043 char *name;
1044 double angle;
1045 Strlist *pn, s;
1046 size_t need = (size_t)level + 3;
1047 Hashindex *p;
1048 int i;
1049
1050 if (level == 0) { /* not recursing: initialise wedges to 0 */
1051 for (i = 0; i < NO_COLOURS; i++) {
1052 wedge[i].angle = 0.0;
1053 wedge[i].name = NULL;
1054 }
1055 smallestangle = 0.0;
1056 smallestwedge = NO_COLOURS - 1;
1057 }
1058
1059 for (pn = partname; pn != NULL; TO_NEXT(pn))
1060 need += strlen(pn->name);
1061 for (p = items; p != NULL; TO_NEXT(p)) {
1062 name = maketreename(partname, p, &pn, &s, need, rep, TRUE);
1063 if (incstrlist(name, expandlist) && p->other != NULL &&
1064 ((Hashtable *)(p->other))->head[0] != NULL)
1065 /* then find wedges in lower level of tree instead. ->head[0] != NULL
1066 must come after p->other != NULL; o/wise it may not be a treerep */
1067 findwedges(wedge, rep, ((Hashtable *)(p->other))->head[0], chartby,
1068 expandlist, level + 1, pn, tot, totb, totb7);
1069 else {
1070 if (chartby == BYTES)
1071 angle = p->own->bytes / totb;
1072 else if (chartby == BYTES7)
1073 angle = p->own->bytes7 / totb7;
1074 else
1075 angle = ((double)(p->own->data[chartby])) / ((double)tot);
1076 if (angle > smallestangle) {/* remove smallest, move along, put p last */
1077 /* We probably don't do this very often so we don't bother with keeping
1078 hold of the memory and reusing it later. */
1079 free(wedge[smallestwedge].name);
1080 for (i = smallestwedge; i < NO_COLOURS - 1; i++) {
1081 wedge[i].name = wedge[i + 1].name;
1082 wedge[i].angle = wedge[i + 1].angle;
1083 }
1084 COPYSTR(wedge[NO_COLOURS - 1].name, name);
1085 /* malloc's necessary space. Needed because next call to maketreename()
1086 will overwrite name. */
1087 wedge[NO_COLOURS - 1].angle = angle;
1088 smallestangle = wedge[0].angle; /* Recalculate smallest */
1089 smallestwedge = 0;
1090 for (i = 1; i < NO_COLOURS; i++) {
1091 if (wedge[i].angle <= smallestangle) {
1092 smallestangle = wedge[i].angle;
1093 smallestwedge = i;
1094 }
1095 }
1096 }
1097 }
1098 }
1099 }
1100
piechart_caption(FILE * outf,choice rep,choice chartby,char ** lngstr)1101 void piechart_caption(FILE *outf, choice rep, choice chartby, char **lngstr) {
1102 extern choice *rep2reqs, *rep2reqs7;
1103 extern unsigned int *method2sort;
1104
1105 static char *caption = NULL;
1106 static size_t len = 0;
1107
1108 choice requests = rep2reqs[G(rep)];
1109 choice requests7 = rep2reqs7[G(rep)];
1110
1111 ENSURE_LEN(caption, len, strlen(lngstr[chartby_]) +
1112 strlen(lngstr[method2sort[requests]]) +
1113 strlen(lngstr[method2sort[requests7]]) +
1114 strlen(lngstr[method2sort[chartby]]) + 3);
1115 /* More than we need, but that's OK. */
1116 strcpy(caption, lngstr[chartby_]);
1117 strcat(caption, " ");
1118 if (chartby == REQUESTS)
1119 strcat(caption, lngstr[method2sort[requests]]);
1120 else if (chartby == REQUESTS7)
1121 strcat(caption, lngstr[method2sort[requests7]]);
1122 else
1123 strcat(caption, lngstr[method2sort[chartby]]);
1124 strcat(caption, ".");
1125 if (normalchart) {
1126 #ifdef EBCDIC
1127 (void)strtoascii(caption);
1128 #endif
1129 gdImageString(im, font, CAPTIONLEFT, CAPTIONTOP, (unsigned char *)caption,
1130 black);
1131 }
1132 else
1133 fprintf(outf, "<p><em>%s</em></p>\n", caption);
1134 }
1135
piechart_wedge(FILE * outf,Outchoices * od,double angle,char * s)1136 int piechart_wedge(FILE *outf, Outchoices *od, double angle, char *s) {
1137 /* The angle is expressed between 0 and 1. Returns col if wedge was big
1138 enough to be coloured in, NO_COLOURS for grey, else -1. */
1139 double x, y, newangle, medangle;
1140 int colour = black, rc = -1;
1141 char t[MAXCHARS];
1142 size_t len;
1143
1144 if (angle < 0) {
1145 angle = 1.75 - totangle; /* rest of the circle because started at 0.75 */
1146 colour = grey;
1147 }
1148 else if (col >= NO_COLOURS)
1149 angle = 0; /* As a signal not to make a wedge. Can this happen? */
1150 else if (angle >= MINANGLE)
1151 colour = colours[col];
1152
1153 if (angle >= MINANGLE || (colour == grey && angle > EPSILON)) {
1154 /* plot one edge of wedge */
1155 x = (double)XCENTRE + (double)DIAMETER * cos(totangle * TWOPI) / 2. +
1156 PIE_EPSILON;
1157 y = (double)YCENTRE + (double)DIAMETER * sin(totangle * TWOPI) / 2. +
1158 PIE_EPSILON;
1159 gdImageLine(im, XCENTRE, YCENTRE, (int)x, (int)y, black);
1160
1161 /* plot other edge of wedge */
1162 newangle = totangle + angle;
1163 x = (double)XCENTRE + (double)DIAMETER * cos(newangle * TWOPI) / 2. +
1164 PIE_EPSILON;
1165 y = (double)YCENTRE + (double)DIAMETER * sin(newangle * TWOPI) / 2. +
1166 PIE_EPSILON;
1167 gdImageLine(im, XCENTRE, YCENTRE, (int)x, (int)y, black);
1168
1169 /* Fill wedge */
1170 medangle = totangle + angle / 2.;
1171 x = (double)XCENTRE + (double)DIAMETER * cos(medangle * TWOPI) / 2.5;
1172 y = (double)YCENTRE + (double)DIAMETER * sin(medangle * TWOPI) / 2.5;
1173 if (gdImageGetPixel(im, (int)x, (int)y) == white) { /* room to colour */
1174 gdImageFill(im, (int)x, (int)y, colour);
1175 /* Make label for wedge. If !normalchart, this is done in piechart_key()
1176 below instead. (See long comment near bottom of piechart().) */
1177 if (normalchart) {
1178 gdImageFilledRectangle(im, BOXESLEFT, boxesy, BOXESLEFT + BOXESSIZE,
1179 boxesy + BOXESSIZE, colour);
1180 if ((len = strlen(s)) <= MAXCHARS - 1)
1181 strcpy(t, s);
1182 else {
1183 strncpy(t, s, (MAXCHARS - 3) / 2);
1184 strcpy(t + (MAXCHARS - 3) / 2, "...");
1185 strncpy(t + (MAXCHARS + 3) / 2, s + len - (MAXCHARS - 4) / 2,
1186 (MAXCHARS - 4) / 2);
1187 t[MAXCHARS - 1] = '\0';
1188 }
1189 #ifdef EBCDIC
1190 strtoascii(t);
1191 #endif
1192 gdImageString(im, font, TEXTLEFT, boxesy + TEXTOFFSET,
1193 (unsigned char *)t, black);
1194 boxesy += TEXTGAP;
1195 }
1196 rc = (colour == grey)?NO_COLOURS:col;
1197 } /* end if (room to colour) */
1198 totangle = newangle;
1199 col++;
1200 }
1201 return(rc);
1202 }
1203
piechart_key(FILE * outf,Outchoices * od,int col,char * name,char * extension,Alias * aliashead)1204 void piechart_key(FILE *outf, Outchoices *od, int col, char *name,
1205 char *extension, Alias *aliashead) {
1206 extern char *workspace;
1207
1208 /* Only called if !normalchart and wedge was included on chart */
1209 if (od->outstyle == HTML)
1210 fputs("<br><img src=\"", outf);
1211 else /* od->outstyle == XHTML */
1212 fputs("<br /><img src=\"", outf);
1213 htmlputs(outf, od, od->imagedir, IN_HREF);
1214 if (col == NO_COLOURS)
1215 fprintf(outf, "sqg.%s", extension);
1216 else /* Above and below: '.' not EXTSEP even on RISC OS */
1217 fprintf(outf, "sq%d.%s", col, extension);
1218 if (od->outstyle == HTML)
1219 fputs("\" alt=\"\"> ", outf);
1220 else /* od->outstyle == XHTML */
1221 fputs("\" alt=\"\" /> ", outf);
1222 strcpy(workspace, name);
1223 do_aliasx(workspace, aliashead);
1224 htmlputs(outf, od, workspace, UNTRUSTED);
1225 fputs("\n", outf);
1226 }
1227
piechart_write(FILE * pieoutf,char * filename,logical jpegcharts)1228 void piechart_write(FILE *pieoutf, char *filename, logical jpegcharts) {
1229 #ifdef HAVE_GD
1230 if (jpegcharts)
1231 gdImageJpeg(im, pieoutf, 100);
1232 else
1233 #endif
1234 gdImagePng(im, pieoutf);
1235 debug('F', "Closing %s", filename);
1236 fclose(pieoutf);
1237 gdImageDestroy(im);
1238 }
1239
piechart_cleanup(Wedge wedge[NO_COLOURS])1240 void piechart_cleanup(Wedge wedge[NO_COLOURS]) {
1241 int i;
1242
1243 /* free the wedge names allocated in findwedges() */
1244 for (i = 0; i < NO_COLOURS; i++)
1245 free(wedge[i].name);
1246 }
1247
piechart(FILE * outf,Outchoices * od,choice rep,Hashindex * items,choice requests,choice requests7,choice pages,choice pages7,unsigned long totr,unsigned long totr7,unsigned long totp,unsigned long totp7,double totb,double totb7)1248 void piechart(FILE *outf, Outchoices *od, choice rep, Hashindex *items,
1249 choice requests, choice requests7, choice pages, choice pages7,
1250 unsigned long totr, unsigned long totr7, unsigned long totp,
1251 unsigned long totp7, double totb, double totb7) {
1252 extern unsigned int *rep2lng;
1253 extern char *repname[];
1254 extern char *workspace;
1255 extern char *anchorname[];
1256 static char *filename = NULL;
1257
1258 char **lngstr = od->lngstr;
1259 choice chartby = od->chartby[G(rep)];
1260 Strlist *expandlist = od->expandhead[G(rep)];
1261 char gender = lngstr[rep2lng[rep] + 3][0];
1262 char *extension = (od->jpegcharts)?"jpg":"png";
1263
1264 Wedge wedge[NO_COLOURS];
1265 FILE *pieoutf;
1266 int key[NO_COLOURS], keyg;
1267 double largestangle;
1268 unsigned long tot = 1;
1269 char *otherstr;
1270 int i;
1271
1272 /* Sort out what the chartby really means */
1273 if (chartby == CHART_NONE)
1274 return; /* We didn't want a pie chart after all */
1275 if (chartby == REQUESTS) {
1276 chartby = requests;
1277 tot = totr;
1278 }
1279 else if (chartby == REQUESTS7) {
1280 chartby = requests7;
1281 tot = totr7;
1282 }
1283 else if (chartby == PAGES) {
1284 chartby = pages;
1285 tot = totp;
1286 }
1287 else if (chartby == PAGES7) {
1288 chartby = pages7;
1289 tot = totp7;
1290 }
1291 if (tot == 0 || (chartby == BYTES && totb < 0.5) ||
1292 (chartby == BYTES7 && totb7 < 0.5)) {
1293 warn('R', TRUE, "In %s, turning off empty pie chart", repname[rep]);
1294 return;
1295 }
1296
1297 /* Calculate which wedges to include */
1298 findwedges(wedge, rep, items, chartby, expandlist, 0, NULL, tot, totb,
1299 totb7);
1300
1301 /* Check whether we still want a chart */
1302 largestangle = wedge[0].angle;
1303 for (i = 1; i < NO_COLOURS; i++)
1304 largestangle = MAX(wedge[i].angle, largestangle);
1305 if (largestangle >= 1 - EPSILON) {
1306 warn('R', TRUE, "In %s, turning off pie chart of only one wedge",
1307 repname[rep]);
1308 return;
1309 }
1310 if (largestangle == 0.) {
1311 warn('R', TRUE, "In %s, turning off pie chart with no wedges",
1312 repname[rep]);
1313 return;
1314 }
1315 if (largestangle < MINANGLE) {
1316 warn('R', TRUE, "In %s, turning off pie chart because no wedge "
1317 "large enough", repname[rep]);
1318 return;
1319 }
1320
1321 /* font and normalchart are the same for every chart, but calculating them
1322 here allows us to keep the variables only in this file */
1323 normalchart = TRUE;
1324 if (strcaseeq(lngstr[charset_], "ISO-8859-2"))
1325 font = gdFontSmall;
1326 else {
1327 font = gdFontFixed;
1328 if (!strcaseeq(lngstr[charset_], "ISO-8859-1") &&
1329 !strcaseeq(lngstr[charset_], "US-ASCII"))
1330 normalchart = FALSE;
1331 }
1332
1333 if (filename == NULL)
1334 filename = (char *)xmalloc(strlen(od->localchartdir) + 13);
1335 /* max poss length = localchartdir + anchorname ( <= 8 ) + ".png\0" */
1336 sprintf(filename, "%s%s%c%s", od->localchartdir, anchorname[rep], EXTSEP,
1337 extension);
1338 if ((pieoutf = piechart_init(filename)) == NULL)
1339 return; /* Warning message is given in piechart_init() */
1340
1341 /* Now we can finally get round to plotting the chart */
1342 for (i = 0; i < NO_COLOURS; i++) {
1343 key[i] = -1;
1344 if (wedge[i].name != NULL) {
1345 strcpy(workspace, wedge[i].name);
1346 do_aliasx(workspace, od->aliashead[G(rep)]);
1347 key[i] = piechart_wedge(outf, od, wedge[i].angle, workspace);
1348 /* retain i -> colour mapping for calling piechart_key() below */
1349 }
1350 }
1351 if (normalchart)
1352 piechart_caption(outf, rep, od->chartby[G(rep)], lngstr);
1353
1354 /* Plot the catch-all wedge and close the file */
1355 if (gender == 'm')
1356 otherstr = lngstr[otherm_];
1357 else if (gender == 'f')
1358 otherstr = lngstr[otherf_];
1359 else
1360 otherstr = lngstr[othern_];
1361 keyg = piechart_wedge(outf, od, -1., otherstr);
1362 piechart_write(pieoutf, filename, od->jpegcharts);
1363
1364 /* Now the text on the page. In CGI mode, this must be done _after_ the image
1365 is closed, or the browser may fail to find the image. This is why printing
1366 the caption and key must be done twice; above here if normalchart, below
1367 here otherwise. */
1368 if (od->outstyle == HTML)
1369 fprintf(outf, "<p><img src=\"%s%s.%s\" alt=\"\">\n", od->chartdir,
1370 anchorname[rep], extension); /* '.' not EXTSEP even on RISC OS */
1371 else /* od->outstyle == XHTML */
1372 fprintf(outf, "<p><img src=\"%s%s.%s\" alt=\"\" /></p>\n", od->chartdir,
1373 anchorname[rep], extension);
1374
1375 if (!normalchart) {
1376 piechart_caption(outf, rep, od->chartby[G(rep)], lngstr);
1377 for (i = 0; i < NO_COLOURS; i++) {
1378 if (key[i] != -1)
1379 piechart_key(outf, od, key[i], wedge[i].name, extension,
1380 od->aliashead[G(rep)]);
1381 }
1382 if (keyg != -1)
1383 piechart_key(outf, od, keyg, otherstr, extension, NULL);
1384 }
1385
1386 piechart_cleanup(wedge);
1387 }
1388 #endif /* NOGRAPHICS */
1389