Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1073 lines (866 sloc) 29.8 KB
/*** analog 6.0 http://www.analog.cx/ ***/
/*** This program is copyright (c) Stephen R. E. Turner 1995 - 2004 except as
*** stated otherwise.
***
*** This program is free software. You can redistribute it and/or modify it
*** under the terms of version 2 of the GNU General Public License, which you
*** should have received with it.
***
*** This program is distributed in the hope that it will be useful, but
*** without any warranty, expressed or implied. ***/
// outxml.c - XML output module for analog (http://www.analog.cx)
// written by and copyright Per Jessen, per@computer.org, February 2003.
#define _GNU_SOURCE
#define NDEBUG
#include <stdio.h>
#include <stddef.h>
#include <time.h>
#include <assert.h>
#include <stdarg.h>
#include <string.h>
#include "anlghea3.h"
#include "outxml.h"
// we use this to borrow a copy of 'outf' such that function that
// don't have an 'outf' argument can use XML_TRACE() anyway.
static FILE *outf = 0;
static FILE **outfp = &outf;
static choice this_report;
static Options *opts;
static int array_index;
// ==================================================================================
/* Page width */
unsigned int xml_pagewidth(Outchoices *od) { XML_TRACE(0); return 0; }
/* The top of the output if we are in CGI mode */
void xml_cgihead(FILE *outf, Outchoices *od) { XML_TRACE(0); }
/* Stuff this output style needs in the page header */
void xml_stylehead(FILE *outf, Outchoices *od)
{
extern timecode_t starttimec;
unsigned int year, month, day, i;
struct tm temptime;
char *xmlversion = "1.0";
char *encoding = "UTF-8";
time_t t;
char buffer[256];
// borrow a copy of the outfile handle
*outfp=outf;
// KLUDGE!!!!
opts=(Options*)((int)od-offsetof(Options,outopts));
t=time(0);
strftime( buffer, 256, "%Y%m%d%H%M%S", gmtime(&t) );
XML_OUT("<?xml version=\"%s\" encoding=\"%s\" standalone=\"no\" ?>"
XMLDBG "<!DOCTYPE analog-data PUBLIC \"::timian/analog-data::\" \"http://timian.jessen.ch/dtd/analog-data.dtd\">"
XMLDBG "<analog-data version=\"%s\" timestamp=\"%s\">"
XMLDBG "<analog>",
xmlversion,
encoding,
VNUMBER, // VERSION instead? VERSION includes platform it was built.
buffer );
// set up appropriate defaults for XML output.
xml_defaults( opts );
// extract a build timestamp from __DATE__ and __TIME__ -- locale dependent?! need to force "C" locale ?
strptime( __DATE__" "__TIME__, "%b%n%d%n%Y%n%T", &temptime );
sprintf( buffer, "%04d%02d%02d%02d%02d",
temptime.tm_year+1900, temptime.tm_mon+1, temptime.tm_mday, temptime.tm_hour, temptime.tm_min );
code2date( starttimec/1440, &day, &month, &year);
XML_OUT( XMLDBG "<property name=\"version\" content=\"%s\"/>", VERSION );
XML_OUT( XMLDBG "<property name=\"built\" content=\"%s\"/>", buffer );
XML_OUT( XMLDBG "<property name=\"starttime\" content=\"%04d%02d%02d%02d%02d\"/> ",
year, month+1, day, (int)((starttimec%1440)/60), (int)(starttimec%60) );
// XML_OUT( XMLDBG "<property name=\"304issuccess\" content=\"%s\"/>", opts.succ304?"yes":"no" );
// XML_OUT( XMLDBG "<property name=\"logtimeoffset\" content=\"%d\"/>", stz );
i=i; // mustn't upset the compiler :-)
}
/* The title of the page, plus the user's HEADERFILE */
void xml_pagetitle(FILE *outf, Outchoices *od) { XML_TRACE(0); }
/* Program start time, and logfile start and end times */
void xml_timings(FILE *outf, Outchoices *od, Dateman *dman)
{
unsigned int year, month, day;
XML_TRACE(0);
code2date( dman->firsttime/1440, &day, &month, &year);
XML_OUT( XMLDBG"<timespan from=\"%04d%02d%02d%02d%02d\" ",
year, month+1, day, (int)((dman->firsttime%1440)/60), (int)(dman->firsttime%60) );
code2date( dman->lasttime/1440, &day, &month, &year);
XML_OUT( "to=\"%04d%02d%02d%02d%02d\" days=\"%f\"/>",
year, month+1, day, (int)((dman->lasttime%1440)/60), (int)(dman->lasttime%60),
(dman->lasttime-dman->firsttime)/1440.0+0.005 );
}
/* Finishing the top of the page */
void xml_closehead(FILE *outf, Outchoices *od) { XML_TRACE(0); XML_OUT(XMLDBG"</analog>"); }
/* Starting the bottom of the page */
void xml_pagebotstart(FILE *outf, Outchoices *od) { XML_TRACE(0); }
/* The credit line at the bottom of the page */
void xml_credit(FILE *outf, Outchoices *od)
{
XML_TRACE(0);
}
/* The program run time */
void xml_runtime(FILE *outf, Outchoices *od, long secs)
{
XML_TRACE(0);
// XML_OUT( XMLDBG"<property name=\"%s\" contents=\"%u\"/>",
// "processingtime", secs);
}
/* The page footer, including the user's FOOTERFILE */
void xml_pagefoot(FILE *outf, Outchoices *od) { XML_TRACE(0); }
/* Footer material for this output style */
void xml_stylefoot(FILE *outf, Outchoices *od)
{
XML_TRACE(0);
XML_OUT( XMLDBG"</analog-data>" );
}
/* Report title */
void xml_reporttitle(FILE *outf, Outchoices *od, choice rep)
{
char *dir;
XML_TRACE(0);
assert( ((char*)od-(char*)opts)==offsetof(Options,outopts) );
array_index=0;
XML_OUT( XMLDBG"<report name=\"%s\">", strtolower(report_name[rep]) );
this_report=rep;
switch (rep) {
case REP_GENSUM:
break;
case REP_YEAR:
case REP_QUARTERLY:
case REP_MONTH:
case REP_WEEK:
case REP_DAYREP:
case REP_HOURREP:
case REP_QUARTERREP:
case REP_FIVEREP:
XML_OUT( XMLDBG "<rowlimit count=\"%d\"/>", opts->outopts.rows[rep] );
case REP_DAYSUM:
case REP_HOURSUM:
case REP_WEEKHOUR:
case REP_QUARTERSUM:
case REP_FIVESUM:
// only time reports (for the time being) have a 'back' setting to set their sort-order.
XML_OUT( XMLDBG "<sort dir=\"%s\" by=\"\"/>", od->back[rep]? "descending" : "ascending" );
break;
case REP_SIZE:
case REP_PROCTIME:
case REP_HOST:
case REP_REDIRHOST:
case REP_FAILHOST:
case REP_VHOST:
case REP_REDIRVHOST:
case REP_FAILVHOST:
case REP_USER:
case REP_REDIRUSER:
case REP_FAILUSER:
case REP_SEARCHREP:
case REP_SEARCHSUM:
case REP_INTSEARCHREP:
case REP_INTSEARCHSUM:
case REP_BROWSUM:
case REP_CODE:
od->subsortby[rep]=
od->subfloor[rep].floorby=UNSET;
case REP_TYPE: // the following reports can also have subfloor/sort setting
case REP_DIR:
case REP_REQ:
case REP_REDIR:
case REP_FAIL:
case REP_DOM:
case REP_REF:
case REP_BROWREP:
case REP_OS:
case REP_REDIRREF:
case REP_FAILREF:
case REP_ORG:
case REP_REFSITE:
dir="ascending"; // analog will sort all of these in ascending order.
XML_OUT( XMLDBG "<sort dir=\"%s\" by=\"%s\"/>", dir, od->sortby[rep]==UNSET?"":criteria[od->sortby[rep]] );
// 5.90b1 there's something funky going on with subsortby
// 5.90b2 ?
assert( od->subsortby[rep]==UNSET || (od->subsortby[rep]>=REQUESTS && od->subsortby[rep]<=METHOD_NUMBER) );
if ( od->sortby[rep]!=UNSET )
if ( od->subsortby[rep]!=UNSET ) // is this dependable ??
// XML_OUT( XMLDBG "<sort dir=\"%s\" by=\"%s\"/>", dir, criteria[od->subsortby[rep]] );
XML_OUT( XMLDBG "<sort dir=\"%s\" by=\"%u\"/>", dir, od->subsortby[rep] );
// the *FLOOR setting
XML_OUT( XMLDBG "<floor level=\"%f\" ", od->floor[rep].min );
if ( od->floor[rep].qual ) XML_OUT( "qual=\"%c\" ", od->floor[rep].qual );
XML_OUT( "by=\"%s\"/>", od->floor[rep].floorby==UNSET?"":criteria[od->floor[rep].floorby] );
if ( od->subfloor[rep].floorby!=UNSET )
{
XML_OUT( XMLDBG "<floor level=\"%f\" ", od->subfloor[rep].min );
// if ( od->subfloor[rep].qual ) XML_OUT( "qual=\"%c\" ", od->subfloor[rep].qual )
XML_OUT( "by=\"%s\"/>", criteria[od->floor[rep].floorby] );
}
break;
default:
puts(report_name[rep]);
assert(0);
break;
}
}
/* Report footer */
void xml_reportfooter(FILE *outf, Outchoices *od, choice rep) { XML_TRACE(0); }
/* Report description */
void xml_reportdesc(FILE *outf, Outchoices *od, choice rep) { XML_TRACE(0); }
/* The time period spanned by the report */
void xml_reportspan(FILE *outf, Outchoices *od, choice rep, timecode_t maxd, timecode_t mind)
{
unsigned int year, month, day;
XML_TRACE(0);
code2date( mind/1440, &day, &month, &year);
XML_OUT( XMLDBG"<timespan from=\"%04d%02d%02d%02d%02d\" ",
year, month+1, day, (int)((mind%1440)/60), (int)(mind%60) );
code2date( maxd/1440, &day, &month, &year);
XML_OUT( "to=\"%04d%02d%02d%02d%02d\" days=\"%f\"/>",
year, month+1, day, (int)((maxd%1440)/60), (int)(maxd%60),
(maxd-mind)/1440.0+0.005 );
}
/* General Summary header */
void xml_gensumhead(FILE *outf, Outchoices *od) { XML_TRACE(0); }
/* General Summary footer */
void xml_gensumfoot(FILE *outf, Outchoices *od) { XML_TRACE(0); }
/* Single General Summary line, long data */
void xml_gensumline(FILE *outf, Outchoices *od, int namecode, unsigned long x, unsigned long x7, logical isaverage)
{
if ( x7==(unsigned long)UNSET ) x7=0;
xml_gensumlineb(outf,od,namecode,(double)x,(double)x7,isaverage);
}
/* Single General Summary line, bytes data */
void xml_gensumlineb(FILE *outf, Outchoices *od, int namecode, double x, double x7, logical isaverage)
{
char *name;
switch( namecode ) {
case succreqs_: name="succreqs_"; break;
case avereqs_: name="avereqs_"; break;
case totpages_: name="totpages_"; break;
case avepages_: name="avepages_"; break;
case totunknown_: name="totunknown_"; break;
case totfails_: name="totfails_"; break;
case totredirs_: name="totredirs_"; break;
case inforeqs_: name="inforeqs_"; break;
case distfiles_: name="distfiles_"; break;
case disthosts_: name="disthosts_"; break;
case corrupt_: name="corrupt_"; break;
case unwanted_: name="unwanted_"; break;
case totdata_: name="totdata_"; break;
case avedata_: name="avedata_"; break;
default: name="__unknown__"; break;
}
XML_OUT( XMLDBG"<row>"
"<col name=\"name\"><i18n key=\"%s\"/></col>", name );
XML_OUT( XMLDBG"<col name=\"%s\">%f</col>", strtolower(column_name[COL_REQS]), x );
XML_OUT( XMLDBG"<col name=\"%s\">%f</col>", strtolower(column_name[COL_REQS7]), x7 );
XML_OUT( XMLDBG"</row>" );
}
/* "Last seven" explanation line */
void xml_lastseven(FILE *outf, Outchoices *od, timecode_t last7to)
{
unsigned int year, month, day;
XML_TRACE(0);
code2date( last7to/1440, &day, &month, &year);
XML_OUT( XMLDBG "<!-- lastseven: %04d%02d%02d%02d%02d -->",
year, month+1, day, (int)((last7to%1440)/60), (int)(last7to%60) );
}
/* Start of a <pre> section */
void xml_prestart(FILE *outf, Outchoices *od) { XML_TRACE(0); }
/* strlen */
size_t xml_strlength(const char *s) { XML_TRACE(0); return strlen(s); }
/* End of a <pre> section */
void xml_preend(FILE *outf, Outchoices *od) { XML_TRACE(0); }
/* A horizontal rule */
void xml_hrule(FILE *outf, Outchoices *od)
{
// an odd place to place this, but the only option.
XML_OUT( XMLDBG"</report><!-- name=\"%s\" -->", strtolower(report_name[this_report]) );
}
/* An en dash */
char *xml_endash(void) { XML_TRACE(0); return "-"; }
/* putc with special characters escaped */
void xml_putch(FILE *outf, char c) { XML_TRACE(0); }
/* Allow month in dates? DO NOT enable for human-readable text because of
i18n problems. */
logical xml_allowmonth(void) { XML_TRACE(0); return TRUE; }
/* Calculate column widths */
void xml_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)
{
XML_TRACE(0);
// outxml has no need to do this, but analog will segfault otherwise.
calcwidths(od, rep, width, bmult, bmult7, unit, maxr, maxr7, maxp, maxp7,
maxb, maxb7, howmany);
}
/* "Each unit represents" line */
void xml_declareunit(FILE *outf, Outchoices *od, char graphby, double unit,
unsigned int bmult) { XML_TRACE(0); }
/* Start of column header line */
void xml_colheadstart(FILE *outf, Outchoices *od, choice rep) { XML_TRACE(0); }
/* Column header line: individual column */
void xml_colheadcol(FILE *outf, Outchoices *od, choice rep, choice col, unsigned int width, char *colname, logical unterminated) {XML_TRACE(0);}
/* End of column header line */
void xml_colheadend(FILE *outf, Outchoices *od, choice rep) {XML_TRACE(0);}
/* Start of column header underlining line */
void xml_colheadustart(FILE *outf, Outchoices *od, choice rep) {XML_TRACE(0);}
/* Underlining of one column header. */
/* If column is terminated, set width and leave name blank; and conversely. */
void xml_colheadunderline(FILE *outf, Outchoices *od, choice rep, choice col, unsigned int width, char *name) {XML_TRACE(0);}
/* End of column header underlining line */
void xml_colheaduend(FILE *outf, Outchoices *od, choice rep) { XML_TRACE(0); }
/* Start of a table row */
void xml_rowstart(FILE *outf, Outchoices *od, choice rep, choice *cols,
int level, char *name, char *datefmt, char *timefmt)
{
XML_TRACE(0);
if ( level!=UNSET )
XML_OUT( XMLDBG"<row level=\"%d\">", level );
else
XML_OUT( XMLDBG"<row>" );
}
/* Print level in hierarchy represented by this row */
void xml_levelcell(FILE *outf, Outchoices *od, choice rep, int level)
{
XML_TRACE(0);
// XML_OUT( XMLDBG"<col name=\"COL_LEVEL\">%d</col>", level );
}
/* Name column */
void xml_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)
{
char *p, *q;
int n;
XML_TRACE(0);
// no output aliasing is done yet. not sure it will be done either.
// we don't care, but by testing it we avoid printing the column twice.
if ( !isfirst && name )
{
XML_OUT( XMLDBG "<col name=\"name\">" );
switch( rep) {
case REP_MONTH:
case REP_YEAR:
case REP_WEEK:
case REP_QUARTERLY:
case REP_DAYREP:
case REP_HOURREP:
case REP_QUARTERREP:
case REP_FIVEREP:
case REP_DAYSUM:
case REP_HOURSUM:
case REP_QUARTERSUM:
case REP_FIVESUM:
case REP_WEEKHOUR:
// this is split because strtok needs to be called in the right order.
XML_OUT( "<timespan from=\"%s\" ", strtok(name," ") );
XML_OUT( "to=\"%s\" ", strtok( NULL," ") );
switch( rep ) {
case REP_DAYSUM:
XML_OUT( "dayofweek=\"%s\" />", strtok( NULL," ") );
break;
case REP_HOURSUM:
XML_OUT( "hour=\"%s\" />", strtok( NULL," ") );
break;
case REP_QUARTERSUM:
XML_OUT( "minute=\"%s\" />", strtok( NULL," ") );
break;
case REP_FIVESUM:
XML_OUT( "minute=\"%s\" />", strtok( NULL," ") );
break;
case REP_WEEKHOUR:
XML_OUT( "dayofweek=\"%s\" ", strtok( NULL," ") );
XML_OUT( "hour=\"%s\" />", strtok( NULL," ") );
break;
default:
XML_OUT( "/>" );
}
break;
case REP_CODE:
strtok(name, " "); // assumption: name = "nnn text"
XML_OUT( name );
break;
// n=array_index++;
// XML_OUT( "%d", (int)(opts->arraydata[rep-FIRST_ARRAYREP][n].threshold) );
// break;
case REP_OS:
// kludge: the hardcoded OS names from tree.c are turned into i18n
// keys - hardly a well thought out interface, but the best we can do
// for the moment.
p=strtolower(name); // all lowercase
while ( (q=strchr(p,' ')) ) *q='-'; // replace ' ' with '-'
while ( (q=strchr(p,'/')) ) *q='-';
XML_OUT( "<i18n key=\"os-%s\"/>", p );
break;
case REP_SIZE:
case REP_PROCTIME:
n=array_index++;
XML_OUT( "%f", opts->arraydata[rep-FIRST_ARRAYREP][n].threshold );
break;
case REP_DOM:
case REP_TYPE:
// kludge: if the string supplied is delimited by square brackets,
// we turn it into an i18n key.
if ( *name=='[' && *(name+strlen(name)-1)==']' )
{
p=strtolower(name+1);
*(p+strlen(p)-1)=0;
while ( (q=strchr(p,' ')) ) *q='-';
XML_OUT( "<i18n key=\"%s-%s\"/>", rep==REP_DOM?"domain":"type", p );
break;
}
// this is a delibrate fall-thru.
default:
XML_OUT( "<![CDATA[%s]]>", cleanprint(name) );
break;
}
XML_OUT( "</col>" );
}
#if 0
extern char *workspace;
choice savemultibyte;
logical linked;
int i;
if (name1st != isfirst)
return;
if (isfirst)
matchlengthn(outf, od, width - od->outputter->strlen(name), ' ');
savemultibyte = od->multibyte;
if (rep == REP_SIZE || rep == REP_PROCTIME)
/* Kludge: for these two reports, we know the texts are things like
"< 1" and we want to convert > and < */
od->multibyte = FALSE;
strcpy(workspace, name);
do_aliasx(workspace, aliashead);
if (!isfirst) {
if (rightalign)
i = (int)width - (int)od->outputter->strlen(workspace) - (int)spaces;
else
i = (int)spaces;
matchlengthn(outf, od, i, ' ');
}
linked = (linkhead != NULL && included(name, ispage, linkhead));
if (linked) {
/* We link to the unaliased name, because the OUTPUTALIAS is usually in
the nature of an annotation. */
fputs("<a href=\"", outf);
if (baseurl != NULL)
htmlputs(outf, od, baseurl, IN_HREF);
// xml_escfprintf(outf, name);
fputs("\">", outf);
}
htmlputs(outf, od, workspace, UNTRUSTED);
if (linked)
fputs("</a>", outf);
if (isfirst)
fputs(": ", outf);
od->multibyte = savemultibyte; /* restore multibyte */
#endif
}
/* Single cell, unsigned long argument */
void xml_ulcell(FILE *outf, Outchoices *od, choice rep, choice col, unsigned long x, unsigned int width)
{
XML_TRACE(0);
XML_OUT( XMLDBG"<col name=\"%s\">%lu</col>", strtolower(column_name[col]), x );
}
/* Single cell, TRUSTED string argument */
void xml_strcell(FILE *outf, Outchoices *od, choice rep, choice col, char *s, unsigned int width)
{
XML_TRACE(0);
XML_OUT( XMLDBG "<col name=\"%s\">", strtolower(column_name[col]) );
switch( col )
{
case COL_TIME:
case COL_FIRSTT:
case COL_DATE:
case COL_FIRSTD:
if ( strlen(s) )
XML_OUT( "<timespan from=\"%s\"/>", strtok(s, " ") );
break;
default:
XML_OUT( s );
break;
}
XML_OUT( "</col>" );
}
/* Single cell, listing bytes */
void xml_bytescell(FILE *outf, Outchoices *od, choice rep, choice col, double b, double bmult, unsigned int width)
{
XML_TRACE(0);
XML_OUT( XMLDBG"<col name=\"%s\">%f</col>", strtolower(column_name[col]), b );
}
/* Single cell, listing percentage */
void xml_pccell(FILE *outf, Outchoices *od, choice rep, choice col, double n, double tot, unsigned int width)
{
XML_TRACE(0);
XML_OUT( XMLDBG"<col name=\"%s\">%f</col>", strtolower(column_name[col]),
tot>0 ? 100*n/tot : 0.0 );
}
/* Single cell, index */
void xml_indexcell(FILE *outf, Outchoices *od, choice rep, choice col, long index, unsigned int width)
{
XML_TRACE(0);
XML_OUT( XMLDBG"<col name=\"%s\">%ld</col>", strtolower(column_name[col]), index );
}
/* End of a table row */
void xml_rowend(FILE *outf, Outchoices *od, choice rep) { XML_TRACE(0); XML_OUT(XMLDBG"</row>"); }
/* Blank line in time reports */
void xml_blankline(FILE *outf, Outchoices *od, choice *cols) { XML_TRACE(0); }
/* Barchart in time reports */
void xml_barchart(FILE *outf, Outchoices *od, int y, char graphby) { XML_TRACE(0); }
/* "Busiest time period" line */
void xml_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)
{
unsigned int year, month, day;
XML_TRACE(0);
code2date( date, &day, &month, &year);
XML_OUT( XMLDBG"<busiest time=\"%04d%02d%02d%02d%02d\" ",
year, month+1, day, hr, min );
switch( graphby ) {
case 'R':
case 'r':
XML_OUT( "reqs=\"%lu\"", reqs );
break;
case 'P':
case 'p':
XML_OUT( "pages=\"%lu\"", pages );
break;
case 'B':
case 'b':
XML_OUT( "bytes=\"%f\"", bys );
break;
default:
// what happens to busiest time when no graphby is set?
assert(0);
break;
}
XML_OUT( "/>" );
#if 0
extern unsigned int *rep2busystr;
char **lngstr = od->lngstr;
char sepchar = od->sepchar;
unsigned int bmult;
char *s;
fprintf(outf, "%s %s (", lngstr[rep2busystr[rep]],
datesprintf(od, datefmt, date, hr, min, newdate, newhr, newmin,
TRUE, UNSET));
if (TOLOWER(graphby) == 'r') {
f3printf(outf, od, (double)reqs, 0, sepchar);
fprintf(outf, " %s).\n", (reqs == 1)?lngstr[request_]:lngstr[requests_]);
}
else if (TOLOWER(graphby) == 'p') {
f3printf(outf, od, (double)pages, 0, sepchar);
fprintf(outf, " %s).\n",
(pages == 1)?lngstr[pagereq_]:lngstr[pagereqs_]);
}
else /* TOLOWER(graphby) == 'b' */ {
if (od->rawbytes)
bmult = 0;
else
bmult = findbmult(bys, od->bytesdp);
printbytes(outf, od, bys, bmult, 0, sepchar);
putc(' ', outf);
if (bmult >= 1) {
s = strchr(lngstr[xbytes_], '?'); /* checked in initialisation */
*s = '\0';
fprintf(outf, "%s%s%s).\n", lngstr[xbytes_],
lngstr[byteprefix_ + bmult], s + 1);
*s = '?';
}
else
fprintf(outf, "%s).\n", lngstr[bytes_]);
}
#endif
}
/* End of "Not listed" line. */
void xml_notlistedstr(FILE *outf, Outchoices *od, choice rep, unsigned long badn)
{
XML_TRACE(0);
XML_OUT( XMLDBG "<col name=\"name\"><i18n key=\"notlisted-%s\" var=\"%lu\"/></col>", strtolower(report_name[rep]), badn );
#if 0
extern unsigned int *rep2lng, *rep2colhead;
char **lngstr = od->lngstr;
char *colhead = lngstr[rep2colhead[rep]];
char *colheadp = lngstr[rep2colhead[rep] + 1];
char gender = lngstr[rep2lng[rep] + 3][0];
char *notlistedstr;
if (gender == 'm')
notlistedstr = lngstr[notlistedm_];
else if (gender == 'f')
notlistedstr = lngstr[notlistedf_];
else
notlistedstr = lngstr[notlistedn_];
fprintf(outf, "[%s: ", notlistedstr);
f3printf(outf, od, (double)badn, 0, od->sepchar);
fprintf(outf, " %s]\n", (badn == 1)?colhead:colheadp);
#endif
}
/* The line declaring the floor and sort for a report */
void xml_whatincluded(FILE *outf, Outchoices *od, choice rep,
unsigned long n, Dateman *dman) {
XML_TRACE(0);
// floor_element( outf, od, rep );
#ifndef NDEBUG
XML_OUT( XMLDBG "<!-- a textual representation of the FLOOR and SORT settings. -->" );
whatincluded(outf, od, rep, n, dman);
#endif
}
/* Spacing at the start of the whatincluded line */
void xml_whatinchead(FILE *outf, Outchoices *od)
{
XML_TRACE(0); XML_OUT( "\n<!-- whatincluded: \"" );
}
/* Finishing the whatincluded line */
void xml_whatincfoot(FILE *outf, Outchoices *od)
{
XML_TRACE(0); XML_OUT( "\" -->" );
}
/* Printing part of the whatincluded line */
void xml_whatincprintstr(FILE *outf, Outchoices *od, char *s)
{
XML_TRACE(0); XML_OUT( s );
}
/* Print a double */
void xml_printdouble(FILE *outf, Outchoices *od, double x)
{
XML_OUT( "%g", x );
}
/* Include a header file or footer file */
void xml_includefile(FILE *outf, Outchoices *od, char *name, char type) { XML_TRACE(0); }
/* Filetype for RISC OS */
unsigned int xml_riscosfiletype(void) {
return 0xf80;
}
// ======= miscellaneous helper functions ===============================================
#ifdef _NEED_XML_PRINTF
static int xml_printf( char *format, ... )
{
va_list v;
int r;
va_start( v, format );
r=vfprintf( outf, format, v );
va_end( v );
return r;
}
#endif
static char* strtolower( const char *s )
{
static char *buffer=0;
char *p;
// buffer is freed upon program termination.
buffer=realloc(buffer, max(strlen(s)+1,1024));
for( p=buffer; *s; p++, s++ )
{
*p=tolower(*s);
}
*p=0;
return buffer;
}
// converts a null-terminated array of null-terminated strings to lower case.
static void all2lower( char** strings )
{
char *p;
while( *strings )
{
for ( p=*strings; *p; p++ )
{
*p=tolower(*p);
}
strings++;
}
}
// this function sets the output options as they are required for XML output.
// it's a bit of a kludge, but will have to do till Stephen implements a method for
// letting an output module set its own defaults.
static void xml_defaults( Options *op)
{
extern logical cgi;
Outchoices *od=&(opts->outopts);
choice timerepcols[] = { COL_REQS, COL_PREQS, COL_PAGES, COL_PPAGES, COL_BYTES, COL_PBYTES, COL_NUMBER };
choice genrepcols[] = { COL_REQS, COL_PREQS, COL_REQS7, COL_PREQS7,
COL_PAGES, COL_PPAGES, COL_PAGES7, COL_PPAGES7,
COL_BYTES, COL_PBYTES, COL_BYTES7, COL_PBYTES7,
COL_TIME, COL_FIRSTT,
COL_INDEX, COL_NUMBER };
int i;
static char *numbers[]={"0","1","2","3","4","5","6","7","8","9","10","11","12",0};
od->rawbytes=TRUE; // we always want bytes listed as raw numbers.
od->gensumlines="BCDEFGHIJKLMN"; // we always want all the general summary lines
// always select english as output language ??
for( i=REP_YEAR; i<DATEREP_NUMBER; i++ ) // timereports will always list all columns.
memcpy( od->cols[i], timerepcols, sizeof(timerepcols) );
for( i=REP_REQ; i<REP_NUMBER; i++ ) // all other reports will always list all columns.
memcpy( od->cols[i], genrepcols, sizeof(genrepcols) );
for( i=0; i<7; i++ ) // ignore i18n daynames, just use 0..6
od->dayname[i]=numbers[i];
od->lngstr[genreptime_]=
od->lngstr[genrepdate_]=
od->lngstr[datefmt1_]=
od->lngstr[datefmt2_]=
od->lngstr[weekfmt_]=
od->lngstr[yearfmt_]=
od->lngstr[dayrepfmt_]=
od->lngstr[quarterlyfmt_]=
od->lngstr[hourrepfmt_]=
od->lngstr[quarterrepfmt_]=
od->lngstr[monthfmt_]="%Y%M%D%H%n %Z%L%E%I%o";
od->lngstr[quartersumfmt_]="%Y%M%D%H%n %Z%L%E%I%o %n";
od->lngstr[hoursumfmt_]="%Y%M%D%H%n %Z%L%E%I%o %h";
od->lngstr[daysumfmt_]="%Y%M%D%H%n %Z%L%E%I%o %w";
od->lngstr[weekhoursumfmt_]="%Y%M%D%H%n %Z%L%E%I%o %w %h";
for( i=0; i<11; i++ )
od->lngstr[filesize0_+i]=numbers[i];
cgi=FALSE; // not possible
for( i=0; i<REP_NUMBER; i++ ) // always list all reports in this order
od->reporder[i]=i; // (depends on ALL ON in analog.cfg)
// override reports settings ??
// opts.outopts.repspan=
// od->repspan=TRUE; // analogs manual says this could be memory consuming?
// opts.outopts.rsthresh=-1; // list reportspan on all reports
od->last7=TRUE;
}
#if 0
static char* utf8( char* in )
{
static char *out=NULL;
static size_t outsz=4;
size_t inlen, outlen;
char *inptr, *outptr;
int rc, i;
if ( out==NULL ) out=malloc(outsz); // initial output buffer
iconv( cd, NULL, NULL, NULL, NULL); // reset conversion state
outlen=outsz; // initial outbuffer size
inlen=strlen(in); // input buffer length
inptr=in;
outptr=out;
while( inlen )
{
// fprintf(stderr, "inlen=%d, string=\"%s\"\n", inlen, in);
rc=iconv( cd, &inptr, &inlen, &outptr, &outlen );
if ( rc==-1 )
switch( errno ) {
case E2BIG: // buffer not big enough?
outlen+=outsz; // adding this much space to output buffer
out=realloc( out, outsz+=outsz ); // allocate bigger buffer
// fprintf(stderr, "outsz=%d, inlen=%d, outlen=%d\n", outsz, inlen, outlen);
break;
case EILSEQ: // An invalid multibyte sequence is encountered in the input.
// fprintf( stderr, "%s:%d: error %d: %s\n", __FILE__, __LINE__, errno, strerror(errno) );
// inptr++; inlen--; // try just skipping one input char
// break;
case EINVAL: // An incomplete multibyte sequence is encountered in the input.
fprintf( stderr, "%s:%d: error %d: %s\n", __FILE__, __LINE__, errno, strerror(errno) );
fprintf( stderr, "%s:%d: string=\"%s\"\n", __FILE__, __LINE__, in, strerror(errno) );
inlen=0; //terminate conversion here.
break;
default:
fprintf( stderr, "%s:%d: error %d: %s\n", __FILE__, __LINE__, errno, strerror(errno) );
my_exit(8);
}
}
// the majority of conversions will be one-to-one, so don't bother telling us about them
if ( strlen(in)!=(outptr-out) )
fprintf( stderr, "%d chars in converted to %d chars out\n", strlen(in), outptr-out );
*outptr=0; // terminate output buffer
return out;
}
#endif
char *cleanprint( char *name )
{
static char *out=NULL;
static size_t outsz=4;
char *outptr, c;
if ( out==NULL ) out=malloc(outsz);
outptr=out;
// cleanprint: printable characters are printed straight, unprintable as hexadecimal codes.
while( (c=*name++) )
{
if ( (outsz-(int)(outptr-out))<6 )
{
outptr=(char*)(outptr-out); // save old
out=realloc(out, outsz*=2); // reallocate buffer
outptr=out+(int)outptr; // repoint
}
if ( isprint( c ) ) *outptr++=c;
else outptr+=sprintf( outptr, "[0x%02X]", (unsigned char)c );
}
*outptr=0;
return out;
}
// I am keeping the name constants below in upper case to make comparison with anlghea3.h easier.
// Before they're used in the output, they're converted to lower case.
// It would be better to convert them prior to use, but this will do for now.
// this is text representation of the column name enumeration from anlghea3.h
static const char *column_name[] = {
"COL_TITLE",
"COL_REQS",
"COL_REQS7",
"COL_PAGES",
"COL_PAGES7",
"COL_BYTES",
"COL_BYTES7",
"COL_PREQS",
"COL_PREQS7",
"COL_PPAGES",
"COL_PPAGES7",
"COL_PBYTES",
"COL_PBYTES7",
"COL_DATE",
"COL_TIME",
"COL_FIRSTD",
"COL_FIRSTT",
"COL_INDEX" };
// this is a text representation of the report name enumeration from anlghea3.h
static const char *report_name[] = {
"REP_YEAR",
"REP_QUARTERLY",
"REP_MONTH",
"REP_WEEK",
"REP_DAYREP",
"REP_HOURREP",
"REP_QUARTERREP",
"REP_FIVEREP",
"REP_DAYSUM",
"REP_HOURSUM",
"REP_WEEKHOUR",
"REP_QUARTERSUM",
"REP_FIVESUM",
"REP_GENSUM",
"REP_REQ",
"REP_REDIR",
"REP_FAIL",
"REP_TYPE",
"REP_DIR",
"REP_HOST",
"REP_REDIRHOST",
"REP_FAILHOST",
"REP_DOMAIN", // "REP_DOM",
"REP_REF",
"REP_REFSITE",
"REP_REDIRREF",
"REP_FAILREF",
"REP_BROWREP",
"REP_VHOST",
"REP_REDIRVHOST",
"REP_FAILVHOST",
"REP_USER",
"REP_REDIRUSER",
"REP_FAILUSER",
"REP_ORG",
"REP_SEARCHREP",
"REP_SEARCHSUM",
"REP_INTSEARCHREP",
"REP_INTSEARCHSUM",
"REP_BROWSUM",
"REP_OS",
"REP_SIZE",
"REP_CODE",
"REP_PROCTIME" };
// this is a text representation of floor and sort settings .....
static const char *criteria[] = {
"REQUESTS", "REQUESTS7", "PAGES", "PAGES7", "REDIR", "REDIR7", "FAIL", "FAIL7", "SUCCDATE",
"REDIRDATE", "FAILDATE", "SUCCFIRSTD", "REDIRFIRSTD", "FAILFIRSTD", "BYTES", "BYTES7",
"ALPHABETICAL", "RANDOM" };