/*** analog 4.13 http://www.analog.cx/ ***/ /*** This program is copyright (c) Stephen R. E. Turner 1995 - 2000 except as *** stated otherwise. Distribution, usage and modification of this program is *** subject to the conditions of the Licence which you should have received *** with it. This program comes with no warranty, expressed or implied. ***/ /*** init.c; initialisation routines ***/ /* See also init2.c and globals.c */ #include "anlghea3.h" void initialise(int argc, char *argv[], Options *op) { #ifdef MAC_EVENTS MacInit(&argc, &argv); #endif #ifdef WIN32 Win32Init(); #endif globals(argv[0]); defaults(op); settings(op, argc, argv); correct(op); finalinit(op); } void confline(Options *op, char *cmd, char *arg1, char *arg2, int rc) { extern Options opts; extern Configfns cf[]; extern char *pos; char u[520]; /* see nextconfline(); why this high though? */ char *savepos; int i; logical done = FALSE; strtoupper(cmd); if (rc >= 2 && STREQ(cmd, "SUBDOMAIN")) { /* ugly but easy */ sprintf(u, "%s (%s)", arg1, arg2); if (strchr(u, '$') != NULL || strchr(u, '*') != NULL) warn('C', TRUE, "Can't have $ or * in argument to SUBDOMAIN"); else configalias((void *)&(op->outopts.aliashead[G(REP_DOM)]), "SUBDOMAIN", arg1, u, -1); rc--; /* to avoid error message */ } for (i = 0; cf[i].fn != NULL && !done; i++) { if (STREQ(cmd, cf[i].name)) { memcpy((void *)&opts, (void *)op, sizeof(Options)); cf[i].fn(cf[i].opt, cmd, arg1, arg2, rc); memcpy((void *)op, (void *)&opts, sizeof(Options)); if (cf[i].fn == &configcall && rc != '\0' && !IS_EMPTY_STRING(arg1)) { savepos = pos; strcpy(u, arg1); /* or name will get obliterated before fclose */ (void)config(u, op); pos = savepos; } done = TRUE; } } if (!done) unknownwarn(cmd, arg1, arg2); } choice config(char *filename, Options *op) { extern Inputformatlist *logformat; extern char *pos; static int no_confs = 0; FILE *f; char *cmd, *arg1, *arg2; int rc; if ((f = my_fopen(filename, "configuration file")) == NULL) return(ERR); if (no_confs++ >= MAX_CONFIGS) error("Attempted to read more than %d configuration files", MAX_CONFIGS); configstrlist((void *)&(op->conffilelist), "", filename, NULL, -1); pos = NULL; while ((rc = nextconfline(f, &cmd, &arg1, &arg2)) != EOF) confline(op, cmd, arg1, arg2, rc); (void)my_fclose(f, filename, "configuration file"); if (!(logformat->used)) warn('D', TRUE, "LOGFORMAT in configuration file %s with no subsequent LOGFILE", filename); configlogfmt((void *)&logformat, "LOGFORMAT", "DEFAULT", NULL, -1); return(OK); } void settings(Options *op, int argc, char *argv[]) { extern Inputformatlist *logformat; extern logical newloglist; int i; logical done; newloglist = TRUE; /* once through command line arguments to see if we just want help */ for (i = 0; i < argc; i++) { if (STREQ(argv[i], "-help") || STREQ(argv[i], "-version") || STREQ(argv[i], "--help") || STREQ(argv[i], "--version")) { fprintf(stderr, "This is analog version %s\n", VERSION); fprintf(stderr, "For help see docs/Readme.html, or %s\n", ANALOGURL); my_exit(EXIT_SUCCESS); } } /* once through command line arguments to see if default config wanted */ if (!strcaseeq(DEFAULTCONFIGFILE, "none")) { for (i = argc - 1, done = FALSE; i >= 1 && !done; i--) { if (!IS_EMPTY_STRING(argv[i]) && argv[i][1] == 'G' && (argv[i][0] == '+' || argv[i][0] == '-')) { done = TRUE; CLLONGCHECK(if (argv[i][0] == '+') (void)config(DEFAULTCONFIGFILE, op);) } } if (!done) (void)config(DEFAULTCONFIGFILE, op); } /* now read in rest of command line arguments */ newloglist = TRUE; clargs(op, argc, argv); if (!(logformat->used)) warn('D', TRUE, "LOGFORMAT on command line with no subsequent logfile"); configlogfmt((void *)&logformat, "LOGFORMAT", "DEFAULT", NULL, -1); newloglist = TRUE; /* finally, read in mandatory config file, aborting if not found */ if (!strcaseeq(MANDATORYCONFIGFILE, "none") && config(MANDATORYCONFIGFILE, op) == ERR) error("Cannot ignore mandatory configuration file"); } Inputformatlist *correctlogfmt(Filelist *lp, Include **wanthead, choice *code2type, Dateman *dman) { extern choice wantitem[]; extern Inputfns inpfns[], pjinpfn; extern char *item_type[]; Inputformatlist *fmt; Inputformat *fns; logical noitem[ITEM_NUMBER], nodates, nocodes, nonames, nobytes; int count, i, j; for (count = 0, fmt = lp->format; fmt != NULL; TO_NEXT(fmt)) { if (!wantitem[INP_CODE] && fmt->count[INP_CODE] == 2) fmt->count[INP_CODE] = 1; if (!wantitem[INP_DATE] && fmt->count[INP_DATE] == 2) fmt->count[INP_DATE] = 1; /* NB read date even if not filtered */ for (fns = fmt->form; fns->inpfns != NULL; TO_NEXT(fns)) { for (i = 0; i < ITEMFNS_NUMBER; i++) { if (fns->inpfns == &inpfns[i]) { j = inpfns[i].type; if (!wantitem[j] && fmt->count[j] == 2 && !(j == ITEM_FILE && (fmt->count[INP_DATE] == 2 || fmt->count[INP_CODE] > 0 || fmt->count[INP_BYTES] > 0 || fmt->count[INP_PROCTIME] > 0))) fmt->count[j] = 1; if (wanthead[j] == NULL && fmt->count[j] == 1 && j != ITEM_FILE && !(j == ITEM_VHOST && lp->pvpos != UNSET)) { if (inpfns[i].fn != &parseref) fns->inpfns = &pjinpfn; fmt->count[j] = 0; } count += (int)(fmt->count[j] == 2); } } /* end for i through ITEMFNS */ if (fns->inpfns->fn == &parselogfmt) { count++; for (i = 0; i < INPUT_NUMBER; i++) fmt->count[i] = 1; /* just so doesn't trigger warnings below */ } } /* end for fns in fmt */ } /* end for fmt through formats */ if (count == 0) lp->format = NULL; /* i.e., mark logfile to be ignored */ else { /* logical noitem[ITEM_NUMBER], nodates, nocodes, nonames, nobytes;*/ for (i = 0; i < ITEM_NUMBER; i++) noitem[i] = FALSE; nodates = FALSE; nocodes = FALSE; nonames = FALSE; nobytes = FALSE; for (fmt = lp->format; fmt != NULL; TO_NEXT(fmt)) { for (count = 0, j = 0; j < INPUT_NUMBER; j++) count += (int)(fmt->count[j] == 2); if (count > 0) { for (i = 0; i < ITEM_NUMBER; i++) { if (fmt->count[i] == 0) noitem[i] = TRUE; } if (fmt->count[INP_DATE] == 0) nodates = TRUE; if (fmt->count[INP_CODE] == 0) nocodes = TRUE; if (fmt->count[ITEM_FILE] == 0) nonames = TRUE; if (fmt->count[INP_BYTES] == 0) nobytes = TRUE; } } for (i = 0; i < ITEM_NUMBER; i++) { if (wanthead[i] != NULL && noitem[i]) warn('M', TRUE, "Logfile %s contains lines with no %s, which are " "being filtered", lp->name, item_type[i]); } if (code2type[0] != UNSET && nocodes) warn('M', TRUE, "Logfile %s contains lines with no status codes, " "which are being filtered", lp->name); if ((dman->from > FIRST_TIME || dman->to < LAST_TIME) && nodates) warn('M', TRUE, "Logfile %s contains lines with no dates, which are " "being filtered", lp->name); if (nonames) warn('M', TRUE, "Logfile %s contains lines with no file names: " "page counts may be low", lp->name); if (nobytes) warn('M', TRUE, "Logfile %s contains lines with no bytes: byte counts " "may be low", lp->name); } return(lp->format); } void correct(Options *op) { extern time_t origstarttime, starttime; extern timecode_t starttimec; extern int stz; extern Inputformatlist *deflogformat; extern char *repname[], *methodname[]; extern unsigned int *rep2gran; extern choice *rep2type; extern choice wantitem[]; extern logical vblesonly; char *sf = "SUBFLOOR"; char *af = "ARGSFLOOR"; char *ss = "SUBSORTBY"; char *as = "ARGSSORTBY"; Filelist *lp; Include *incp, *lastincp; choice rep, *cols, floor, subfloor, sortby, subsortby; char *subf, *subs; logical colsinc[COL_NUMBER], istree, templ; char graph; int i, j, k; /* NB some options, e.g. reportorder, corrected when parsed */ origstarttime = starttime; starttime = shifttime(starttime, stz); starttimec += stz; if (op->outopts.markchar == '\0') { warn('C', TRUE, "MARKCHAR none not allowed. Using + instead."); op->outopts.markchar = '+'; } if (op->outopts.decpt == '\0') { warn('C', TRUE, "DECPOINT none not allowed. Using '.' instead."); op->outopts.decpt = '.'; } op->dman.from = FIRST_TIME; if (op->dman.fromstr != NULL && /* so parse unless fromstr is NULL */ parsedate(starttime, op->dman.fromstr, &(op->dman.from), TRUE, FALSE) == ERR) warn('C', TRUE, "Invalid FROM string %s: ignoring it", op->dman.fromstr); op->dman.to = LAST_TIME; if (op->dman.tostr != NULL && parsedate(starttime, op->dman.tostr, &(op->dman.to), FALSE, FALSE) == ERR) warn('C', TRUE, "Invalid TO string %s: ignoring it", op->dman.tostr); if (op->dman.from > op->dman.to) { warn('C', TRUE, "FROM time is later than TO time: " "would exclude everything so ignoring them"); op->dman.from = FIRST_TIME; op->dman.to = LAST_TIME; } for (i = 0, lp = op->miscopts.logfile; lp != NULL; TO_NEXT(lp)) i = MAX(i, lp->tz); if (op->dman.from > starttimec + i + 60) /* one hour's grace */ warn('D', TRUE, "FROM time is later than the present"); if (op->dman.to < LAST_TIME) { op->dman.last7from = op->dman.to - MINS_IN_WEEK; op->dman.last7to = op->dman.to; } else { op->dman.last7from = starttimec - MINS_IN_WEEK; op->dman.last7to = starttimec; } if (op->outopts.outstyle == HTML) { if (op->outopts.htmlpagewidth == 0) op->outopts.htmlpagewidth = 1; else if (op->outopts.htmlpagewidth > MAXPAGEWIDTH) { warn('C', TRUE, "HTMLPAGEWIDTH %u too large: using maximum allowed " "value of %u", op->outopts.htmlpagewidth, MAXPAGEWIDTH); op->outopts.htmlpagewidth = MAXPAGEWIDTH; } op->outopts.pagewidth = op->outopts.htmlpagewidth; } else if (op->outopts.outstyle == ASCII || op->outopts.outstyle == PLAIN) { if (op->outopts.plainpagewidth == 0) op->outopts.plainpagewidth = 1; else if (op->outopts.plainpagewidth > MAXPAGEWIDTH) { warn('C', TRUE, "PLAINPAGEWIDTH %u too large: using maximum allowed " "value of %u", op->outopts.plainpagewidth, MAXPAGEWIDTH); op->outopts.plainpagewidth = MAXPAGEWIDTH; } op->outopts.pagewidth = op->outopts.plainpagewidth; } for (i = 0, j = 0, k = 0; i < DATEREP_NUMBER; i++) { if (i != REP_DAYSUM && i != REP_HOURSUM) { j += (int)(op->outopts.repq[i] && op->outopts.back[i]); k += (int)(op->outopts.repq[i]); } } for (lastincp = NULL, incp = op->argshead, templ = FALSE; incp != NULL; TO_NEXT(incp)) { if (STREQ((char *)(incp->name), "pages")) { if (!templ) warn('C', TRUE, "ARGSINCLUDE/EXCLUDE can't include 'pages'"); templ = TRUE; if (lastincp == NULL) op->argshead = incp->next; else lastincp->next = incp->next; } else lastincp = incp; } for (lastincp = NULL, incp = op->refargshead, templ = FALSE; incp != NULL; TO_NEXT(incp)) { if (STREQ((char *)(incp->name), "pages")) { if (!templ) warn('C', TRUE, "REFARGSINCLUDE/EXCLUDE can't include 'pages'"); templ = TRUE; if (lastincp == NULL) op->refargshead = incp->next; else lastincp->next = incp->next; } else lastincp = incp; } if (j != 0 && j != k) warn('D', TRUE, "Time reports have not all got same value of BACK"); /* The next bit is totally foul, so here's a guide. Numbers are repeated in comments below. [C] problems are overridden, [D] are just warned. for (i through reports turned on, except GENSUM) { if (time report) { *** (1) *** check COLS don't include D, d or N [C] check GRAPH included in COLS [D] } else (non-time reports) { *** (2) *** if (REP_REQ || REP_TYPE) { *** (3) *** Check P not in COLS or (sub)SORTBY or (sub)FLOOR [C] } else if (REP_REDIR || REP_FAIL || REP_REDIRREF || REP_FAILREF || REP_FAILUSER || REP_CODE) { *** (4) *** Check P, p, B, b not in COLS or (sub)SORTBY or (sub)FLOOR [C] } if (REP_SIZE || REP_PROCTIME) { *** (5) *** Check N not in COLS [C] } else { *** (6) *** Check SORTBY matches SUBSORTBY [D] Check FLOOR matches SUBFLOOR [D] Check SORTBY matches FLOOR [D] Check no column N if SORTBY ALPHABETICAL or RANDOM [D] Check (sub)SORTBY and (sub)FLOOR in COLS [D] } } Check COLS non-empty [D] *** (7) *** } */ for (i = 0; op->outopts.reporder[i] != -1; i++) { rep = op->outopts.reporder[i]; if (rep != REP_GENSUM && op->outopts.repq[rep]) { cols = op->outopts.cols[rep]; for (j = 0; j < COL_NUMBER; j++) colsinc[j] = FALSE; for (j = 0; cols[j] != COL_NUMBER; j++) colsinc[cols[j]] = TRUE; if (rep < DATEREP_NUMBER) { /* *** (1) *** time reports */ graph = op->outopts.graph[rep]; if (colsinc[COL_DATE] || colsinc[COL_TIME] || colsinc[COL_INDEX]) { warn('C', TRUE, "In %s, D, d and N not allowed in COLS: ignoring them", repname[rep]); for (j = 0; cols[j] != COL_NUMBER; j++) { if (cols[j] == COL_DATE || cols[j] == COL_TIME || cols[j] == COL_INDEX) { for (k = j; cols[k] != COL_NUMBER; k++) cols[k] = cols[k + 1]; j--; } } } if (cols[0] != COL_NUMBER) { /* o/wise different warning below */ if (((graph == 'R' || graph == 'r') && !colsinc[COL_REQS] && !colsinc[COL_PREQS]) || ((graph == 'P' || graph == 'p') && !colsinc[COL_PAGES] && !colsinc[COL_PPAGES]) || ((graph == 'B' || graph == 'b') && !colsinc[COL_BYTES] && !colsinc[COL_PBYTES])) warn('D', TRUE, "In %s, GRAPH (%c) isn't included in COLS", repname[rep], graph); } } /* end if time report */ else { /* *** (2) *** non-time reports */ sortby = op->outopts.sortby[G(rep)]; floor = op->outopts.floor[G(rep)].floorby; subsortby = op->outopts.subsortby[G(rep)]; subfloor = op->outopts.subfloor[G(rep)].floorby; if (rep == REP_REQ || rep == REP_FAIL || rep == REP_REDIR || rep == REP_REF || rep == REP_FAILREF || rep == REP_REDIRREF) { subf = af; /* *** (3) *** */ subs = as; } else { subf = sf; subs = ss; } istree = sublevels(op->outopts.tree[G(rep)]->tree); /* this (istree) is not perfect, because ARGSINCLUDE isn't analysed so there may not actually be a tree in practice */ if (rep == REP_REQ || rep == REP_TYPE) { if (colsinc[COL_PAGES]) { warn('C', TRUE, "In %s, P not allowed in COLS: ignoring it", repname[rep]); for (j = 0; cols[j] != COL_NUMBER; j++) { if (cols[j] == COL_PAGES) { for (k = j; cols[k] != COL_NUMBER; k++) cols[k] = cols[k + 1]; j--; } } colsinc[COL_PAGES] = FALSE; } if (sortby == PAGES) { warn('C', TRUE, "In %s, illegal SORTBY (pages): " "will sort by requests instead", repname[rep]); op->outopts.sortby[G(rep)] = REQUESTS; sortby = REQUESTS; } if (floor == PAGES) { warn('C', TRUE, "In %s, illegal FLOOR (pages): will use requests instead", repname[rep]); op->outopts.floor[G(rep)].floorby = REQUESTS; floor = REQUESTS; } if (istree) { if (subsortby == PAGES) { warn('C', TRUE, "In %s, illegal %s (pages): " "will sort by requests instead", repname[rep], subs); op->outopts.subsortby[G(rep)] = REQUESTS; subsortby = REQUESTS; } if (subfloor == PAGES) { warn('C', TRUE, "In %s, illegal %s (pages): will use " "requests instead", repname[rep], subf); op->outopts.subfloor[G(rep)].floorby = REQUESTS; subfloor = REQUESTS; } } } /* end rep == REP_REQ || rep == REP_TYPE */ else if (rep == REP_REDIR || rep == REP_FAIL || rep == REP_REDIRREF || rep == REP_FAILREF || rep == REP_FAILUSER || rep == REP_CODE) { /* *** (4) *** */ if (colsinc[COL_PAGES] || colsinc[COL_PPAGES] || colsinc[COL_BYTES] || colsinc[COL_PBYTES]) { warn('C', TRUE, "In %s, P, p, B and b not allowed in COLS: ignoring them", repname[rep]); for (j = 0; cols[j] != COL_NUMBER; j++) { if (cols[j] == COL_PAGES || cols[j] == COL_PPAGES || cols[j] == COL_BYTES || cols[j] == COL_PBYTES) { for (k = j; cols[k] != COL_NUMBER; k++) cols[k] = cols[k + 1]; j--; } } colsinc[COL_PAGES] = FALSE; colsinc[COL_PPAGES] = FALSE; colsinc[COL_BYTES] = FALSE; colsinc[COL_PBYTES] = FALSE; } if (sortby == PAGES || sortby == BYTES) { warn('C', TRUE, "In %s, illegal SORTBY (%s): will sort by requests instead", repname[rep], methodname[sortby]); op->outopts.sortby[G(rep)] = REQUESTS; sortby = REQUESTS; } if (floor == PAGES || floor == BYTES) { warn('C', TRUE, "In %s, illegal FLOOR (%s): will use -50r instead", repname[rep], methodname[floor]); op->outopts.floor[G(rep)].min = -50; op->outopts.floor[G(rep)].qual = '\0'; op->outopts.floor[G(rep)].floorby = REQUESTS; floor = REQUESTS; } if (istree) { if (subsortby == PAGES || subsortby == BYTES) { warn('C', TRUE, "In %s, illegal %s (%s): will sort by requests instead", repname[rep], subs, methodname[subsortby]); op->outopts.subsortby[G(rep)] = REQUESTS; subsortby = REQUESTS; } if (subfloor == PAGES || subfloor == BYTES) { warn('C', TRUE, "In %s, illegal %s (%s): will use -1r instead", repname[rep], subf, methodname[subfloor]); op->outopts.subfloor[G(rep)].min = -1; op->outopts.subfloor[G(rep)].qual = '\0'; op->outopts.subfloor[G(rep)].floorby = REQUESTS; subfloor = REQUESTS; } } } /* end rep == (list of 6 reps) */ if (rep == REP_SIZE || rep == REP_PROCTIME) { /* *** (5) *** */ if (colsinc[COL_INDEX]) { warn('C', TRUE, "In %s, N not allowed in COLS: ignoring it", repname[rep]); for (j = 0; cols[j] != COL_NUMBER; j++) { if (cols[j] == COL_INDEX) { for (k = j; cols[k] != COL_NUMBER; k++) cols[k] = cols[k + 1]; j--; } } } } else { /* *** (6) *** rep != REP_SIZE && rep != REP_PROCTIME */ /* check SORTBYs (SIZE & PROCTIME don't have SORTBY's) */ if (istree) { if (sortby != subsortby && subsortby != ALPHABETICAL && subsortby != RANDOM) warn('D', TRUE, "In %s, SORTBY (%s) doesn't match %s (%s)", repname[rep], methodname[sortby], subs, methodname[subsortby]); if (floor != subfloor) warn('D', TRUE, "In %s, FLOOR (%s) doesn't match %s (%s)", repname[rep], methodname[floor], subf, methodname[subfloor]); if ((subsortby == REQUESTS || subsortby == PAGES || subsortby == BYTES) && (subfloor == REQUESTS || subfloor == PAGES || subfloor == BYTES) && subsortby != subfloor) warn('D', TRUE, "In %s, %s (%s) doesn't match %s (%s)", repname[rep], subs, methodname[subsortby], subf, methodname[subfloor]); } if ((sortby == REQUESTS || sortby == PAGES || sortby == BYTES) && (floor == REQUESTS || floor == PAGES || floor == BYTES) && sortby != floor) warn('D', TRUE, "In %s, SORTBY (%s) doesn't match FLOOR (%s)", repname[rep], methodname[sortby], methodname[floor]); if (sortby == ALPHABETICAL && colsinc[COL_INDEX]) warn('D', TRUE, "In %s, column N with SORTBY ALPHABETICAL", repname[rep]); else if (sortby == RANDOM && colsinc[COL_INDEX]) warn('D', TRUE, "In %s, column N with SORTBY RANDOM", repname[rep]); if (cols[0] != COL_NUMBER) { /* o/wise different warning below */ if ((sortby == REQUESTS && !colsinc[COL_REQS] && !colsinc[COL_PREQS]) || (sortby == PAGES && !colsinc[COL_PAGES] && !colsinc[COL_PPAGES]) || (sortby == BYTES && !colsinc[COL_BYTES] && !colsinc[COL_PBYTES]) || (sortby == DATESORT && !colsinc[COL_DATE] && !colsinc[COL_TIME])) warn('D', TRUE, "In %s, SORTBY (%s) isn't included in COLS", repname[rep], methodname[sortby]); if ((floor == REQUESTS && !colsinc[COL_REQS] && !colsinc[COL_PREQS]) || (floor == PAGES && !colsinc[COL_PAGES] && !colsinc[COL_PPAGES]) || (floor == BYTES && !colsinc[COL_BYTES] && !colsinc[COL_PBYTES]) || (floor == DATESORT && !colsinc[COL_DATE] && !colsinc[COL_TIME])) warn('D', TRUE, "In %s, FLOOR (%s) isn't included in COLS", repname[rep], methodname[floor]); if (istree) { if ((subsortby == REQUESTS && !colsinc[COL_REQS] && !colsinc[COL_PREQS]) || (subsortby == PAGES && !colsinc[COL_PAGES] && !colsinc[COL_PPAGES]) || (subsortby == BYTES && !colsinc[COL_BYTES] && !colsinc[COL_PBYTES]) || (subsortby == DATESORT && !colsinc[COL_DATE] && !colsinc[COL_TIME])) warn('D', TRUE, "In %s, %s (%s) isn't included in COLS", repname[rep], subs, methodname[subsortby]); if ((subfloor == REQUESTS && !colsinc[COL_REQS] && !colsinc[COL_PREQS]) || (subfloor == PAGES && !colsinc[COL_PAGES] && !colsinc[COL_PPAGES]) || (subfloor == BYTES && !colsinc[COL_BYTES] && !colsinc[COL_PBYTES]) || (subfloor == DATESORT && !colsinc[COL_DATE] && !colsinc[COL_TIME])) warn('D', TRUE, "In %s, %s (%s) isn't included in COLS", repname[rep], subf, methodname[subfloor]); } } } } if (cols[0] == COL_NUMBER) /* *** (7) *** */ warn('D', TRUE, "%s contains no COLS", repname[rep]); } } /* end for i through reports */ /* change logformats to ignore items which are not wanted (see 28/10/97) */ for (lp = op->miscopts.logfile; lp != NULL; TO_NEXT(lp)) { if (lp->format->form->inpfns->fn == &parselogfmt && lp->format->form->sep == '0') /* DEFAULT format */ lp->format = deflogformat; } if (!vblesonly) { if (!STREQ(op->outopts.cacheoutfile, "none")) { if (op->outopts.outstyle != OUT_NONE) { if (STREQ(op->outopts.cacheoutfile, op->outopts.outfile) && !STREQ(op->outopts.outfile, "-") && !STREQ(op->outopts.outfile, "stdout")) error("OUTFILE and CACHEOUTFILE are the same"); /* won't catch same file under different names, but cache file opening will still fail later in that case */ if ((STREQ(op->outopts.outfile, "-") || STREQ(op->outopts.outfile, "stdout")) && (STREQ(op->outopts.cacheoutfile, "-") || STREQ(op->outopts.cacheoutfile, "stdout"))) error("OUTFILE and CACHEOUTFILE both set to stdout"); } for (i = 0; i < INPUT_NUMBER; i++) wantitem[i] = TRUE; op->miscopts.granularity = rep2gran[REP_FIVE]; for (i = 0, j = 0; i < ITEM_NUMBER; i++) j += (int)(op->miscopts.lowmem[i] >= 3); if (j != 0) warn('D', TRUE, "LOWMEM 3 prevents that item being cached"); } else if (op->outopts.outstyle == OUT_NONE) error("OUTPUT NONE and CACHEOUTFILE none selected"); else { /* cachefile == none */ for (i = 0; i < INPUT_NUMBER; i++) wantitem[i] = FALSE; for (op->miscopts.granularity = 1, i = 0; op->outopts.reporder[i] != -1; i++) { rep = op->outopts.reporder[i]; if (rep < DATEREP_NUMBER && op->outopts.repq[rep]) op->miscopts.granularity = MAX(op->miscopts.granularity, rep2gran[rep]); if (op->outopts.repq[rep] && rep2type[rep] != UNSET) wantitem[rep2type[rep]] = TRUE; } } /* end cachefile == none */ for (lp = op->miscopts.logfile, templ = FALSE; lp != NULL; TO_NEXT(lp)) { (void)correctlogfmt(lp, op->wanthead, op->code2type, &(op->dman)); if (op->miscopts.lowmem[ITEM_VHOST] >= 3 && lp->pvpos != UNSET) { if (!templ) { warn('C', TRUE, "Ignoring %%v in logfile prefixes because of VHOSTLOWMEM 3"); templ = TRUE; } lp->pvpos = UNSET; } } } if (op->outopts.outstyle == COMPUTER) { op->outopts.sepchar = '\0'; op->outopts.repsepchar = '\0'; op->outopts.decpt = '.'; op->outopts.rawbytes = TRUE; } /* lower case appropriate aliases and in/excludes */ /* NB could send them through all fixed aliases, but others probably never needed and could cause confusion */ toloweralias(op->aliashead[ITEM_HOST], TRUE); toloweralias(op->outopts.aliashead[G(REP_HOST)], FALSE); toloweralias(op->outopts.aliashead[G(REP_DOM)], FALSE); toloweralias(op->outopts.aliashead[G(REP_ORG)], FALSE); tolowerinc(op->wanthead[ITEM_HOST]); tolowerinc(op->outopts.wanthead[G(REP_HOST)]); tolowerinc(op->outopts.wanthead[G(REP_DOM)]); tolowerinc(op->outopts.wanthead[G(REP_ORG)]); toloweralias(op->aliashead[ITEM_VHOST], TRUE); toloweralias(op->outopts.aliashead[G(REP_VHOST)], FALSE); tolowerinc(op->wanthead[ITEM_VHOST]); tolowerinc(op->outopts.wanthead[G(REP_VHOST)]); toloweralias(op->aliashead[ITEM_USER], TRUE); toloweralias(op->outopts.aliashead[G(REP_USER)], FALSE); toloweralias(op->outopts.aliashead[G(REP_FAILUSER)], FALSE); tolowerinc(op->wanthead[ITEM_USER]); tolowerinc(op->outopts.wanthead[G(REP_USER)]); tolowerinc(op->outopts.wanthead[G(REP_FAILUSER)]); tolowerinc(op->outopts.wanthead[G(REP_SEARCHREP)]); tolowerinc(op->outopts.wanthead[G(REP_SEARCHSUM)]); if (op->miscopts.case_insensitive) { toloweralias(op->aliashead[ITEM_FILE], TRUE); toloweralias(op->outopts.aliashead[G(REP_REQ)], FALSE); toloweralias(op->outopts.aliashead[G(REP_REDIR)], FALSE); toloweralias(op->outopts.aliashead[G(REP_FAIL)], FALSE); toloweralias(op->outopts.aliashead[G(REP_TYPE)], FALSE); toloweralias(op->outopts.aliashead[G(REP_DIR)], FALSE); tolowerinc(op->wanthead[ITEM_FILE]); tolowerinc(op->ispagehead); /* this gets refs too, but shame... */ tolowerinc(op->argshead); tolowerinc(op->outopts.wanthead[G(REP_REQ)]); tolowerinc(op->outopts.wanthead[G(REP_REDIR)]); tolowerinc(op->outopts.wanthead[G(REP_FAIL)]); tolowerinc(op->outopts.wanthead[G(REP_TYPE)]); tolowerinc(op->outopts.wanthead[G(REP_DIR)]); } } #define POSSTREE(r) if (op->miscopts.lowmem[rep2type[r]] >= 3 && \ op->outopts.repq[r]) \ op->outopts.alltrees[i++] = r #define POSSDERV(r) if (op->miscopts.lowmem[rep2type[r]] >= 3 && \ op->outopts.repq[r]) \ op->outopts.alldervs[i++] = r void finalinit(Options *op) { #ifndef NODNS extern choice dnslevel; extern char *dnsfile, *dnslockfile; extern FILE *dnsfilep, *dnslock; extern Hashtable *dnstable; extern unsigned int dnsgoodhrs, dnsbadhrs; timecode_t timec, goodtimec, badtimec; char *name, *alias; choice rc; #endif extern timecode_t starttimeuxc; extern char *country[]; extern Memman *xmemman; extern choice *rep2type; extern char *pos; extern logical vblesonly; FILE *tempf; Strlist *sp; int i; /* set code2type variable */ for (i = MIN_SC; i < SC_NUMBER; i++) { if (op->code2type[i] == UNWANTED || (op->code2type[i] == UNSET && op->code2type[0] == SUCCESS)) op->code2type[i] = UNWANTED; else if (i <= 199) op->code2type[i] = INFO; else if (i <= 299 || (i == 304 && op->succ304)) op->code2type[i] = SUCCESS; else if (i <= 399) op->code2type[i] = REDIRECT; else op->code2type[i] = FAILURE; } /* swap aliases round */ for (i = 0; i < ITEM_NUMBER; i++) reversealias(&(op->aliashead[i])); for (i = 0; i < GENREP_NUMBER; i++) reversealias(&(op->outopts.aliashead[i])); /* read in language information */ if (op->outopts.lang.file == NULL) selectlang(country[op->outopts.lang.code], &(op->outopts)); if (op->outopts.domainsfile == NULL) configstr(&(op->outopts.domainsfile), NULL, LANGDIR"ukdom"EXTSEP"tab", NULL, -1); if (!vblesonly) { /* persists to end of function */ if (op->outopts.outstyle == PLAIN) op->outopts.outstyle = ASCII; /* The only difference is the langfile */ if ((tempf = my_fopen(op->outopts.lang.file, "language file")) == NULL) error("Can't read language file %s", op->outopts.lang.file); op->outopts.dayname = (char **)xmalloc(7 * sizeof(char *)); op->outopts.monthname = (char **)xmalloc(12 * sizeof(char *)); op->outopts.lngstr = (char **)xmalloc(LNGSTR_NUMBER * sizeof(char *)); pos = NULL; DEFAULTSTR(op->outopts.lngstr[charset_], nextlngstr(tempf, op->outopts.lang.file, TRUE)); op->outopts.multibyte = FALSE; if (*(op->outopts.lngstr[charset_]) == '*') { op->outopts.multibyte = TRUE; (op->outopts.lngstr[charset_])++; op->outopts.html = FALSE; } for (i = 0; i < 7; i++) DEFAULTSTR(op->outopts.dayname[i], nextlngstr(tempf, op->outopts.lang.file, TRUE)); for (i = 0; i < 12; i++) DEFAULTSTR(op->outopts.monthname[i], nextlngstr(tempf, op->outopts.lang.file, TRUE)); for (i = 1; i < LNGSTR_NUMBER; i++) DEFAULTSTR(op->outopts.lngstr[i], nextlngstr(tempf, op->outopts.lang.file, TRUE)); (void)nextlngstr(tempf, op->outopts.lang.file, FALSE); /* This last nextlngstr checks the language file isn't too long */ op->outopts.plainmonthlen = (unsigned int)arraymaxlen(op->outopts.monthname, 12, ASCII); /* plain is longer: by pretending to be ASCII it includes whole strlen */ op->outopts.monthlen = (unsigned int)arraymaxlen(op->outopts.monthname, 12, op->outopts.outstyle); op->outopts.plaindaylen = (unsigned int)arraymaxlen(op->outopts.dayname, 7, ASCII); op->outopts.daylen = (unsigned int)arraymaxlen(op->outopts.dayname, 7, op->outopts.outstyle); /* Set the convfloor (see do_alias(n|N)). We only do this approximately: convert A0-FF for ISO-8859-*, 80-FF o/wise. This may still include some non-printable characters! But we can't have a table for every charset.*/ if (!op->outopts.searchconv || strcaseeq(op->outopts.lngstr[charset_], "US-ASCII")) op->outopts.convfloor = 0; else if (substrcaseeq(op->outopts.lngstr[charset_], "iso-8859-") || strcaseeq(op->outopts.lngstr[charset_], "armscii-8")) op->outopts.convfloor = 0xA0; else op->outopts.convfloor = 0x80; if (op->outopts.baseurl != NULL && STREQ(op->outopts.baseurl, "none")) op->outopts.baseurl = NULL; if (op->outopts.outstyle == COMPUTER) { DEFAULTSTR(op->outopts.lngstr[succreqs_], "SRS7"); DEFAULTSTR(op->outopts.lngstr[totunknown_], "NCC7"); DEFAULTSTR(op->outopts.lngstr[totpages_], "PRP7"); DEFAULTSTR(op->outopts.lngstr[totfails_], "FLF7"); DEFAULTSTR(op->outopts.lngstr[totredirs_], "RRR7"); DEFAULTSTR(op->outopts.lngstr[inforeqs_], "NII7"); DEFAULTSTR(op->outopts.lngstr[distfiles_], "NFN7"); DEFAULTSTR(op->outopts.lngstr[disthosts_], "NHH7"); DEFAULTSTR(op->outopts.lngstr[corrupt_], "CL"); DEFAULTSTR(op->outopts.lngstr[unwanted_], "UL"); DEFAULTSTR(op->outopts.lngstr[totdata_], "BTB7"); DEFAULTSTR(op->outopts.lngstr[dayrepfmt_], "%Y%\b%M%\b%D"); DEFAULTSTR(op->outopts.lngstr[hourrepfmt_], "%Y%\b%M%\b%D%\b%H"); DEFAULTSTR(op->outopts.lngstr[quarterfmt_], "%Y%\b%M%\b%D%\b%H%\b%n"); DEFAULTSTR(op->outopts.lngstr[weekfmt_], "%Y%\b%M%\b%D"); DEFAULTSTR(op->outopts.lngstr[monthfmt_], "%Y%\b%M"); DEFAULTSTR(op->outopts.lngstr[genrepdate_], "%Y%\b%M%\b%D"); DEFAULTSTR(op->outopts.lngstr[genreptime_], "%Y%\b%M%\b%D%\b%H%\b%n"); DEFAULTSTR(op->outopts.lngstr[datefmt1_], "%Y%\b%M%\b%D%\b%H%\b%n"); DEFAULTSTR(op->outopts.lngstr[datefmt2_], "%Y%\b%M%\b%D%\b%H%\b%n"); } (void)my_fclose(tempf, op->outopts.lang.file, "language file"); op->miscopts.dirsufflength = strlen(op->miscopts.dirsuffix); for (i = 0; i < GENREP_NUMBER; i++) allgraft(op->outopts.tree[i]->tree, op->outopts.tree[i]->space); tempf = my_fopen(op->outopts.domainsfile, "domains file"); if (tempf != NULL) { process_domainsfile(tempf, op); (void)my_fclose(tempf, op->outopts.domainsfile, "domains file"); } for (sp = op->outopts.suborgs; sp != NULL; TO_NEXT(sp)) confline(op, "SUBORG2", sp->name, NULL, 1); op->outopts.alltrees = (choice *)submalloc(xmemman, 6 * sizeof(choice)); i = 0; POSSTREE(REP_DIR); POSSTREE(REP_DOM); POSSTREE(REP_ORG); POSSTREE(REP_TYPE); POSSTREE(REP_REFSITE); /* if adding more, change '6' above */ op->outopts.alltrees[i] = REP_NUMBER; op->outopts.alldervs = (choice *)submalloc(xmemman, 5 * sizeof(choice)); i = 0; POSSDERV(REP_SEARCHREP); POSSDERV(REP_SEARCHSUM); POSSDERV(REP_BROWSUM); POSSDERV(REP_OS); /* if adding more, change '5' above */ op->outopts.alldervs[i] = REP_NUMBER; confline(op, "DOMOUTPUTALIAS", "\f", op->outopts.lngstr[unresolved_], -1); confline(op, "DOMOUTPUTALIAS", "\b", op->outopts.lngstr[nodomain_], -1); confline(op, "DOMOUTPUTALIAS", "\v", op->outopts.lngstr[unkdomain_], -1); confline(op, "ORGOUTPUTALIAS", "\f", op->outopts.lngstr[unresolved_], -1); confline(op, "ORGOUTPUTALIAS", "\b", op->outopts.lngstr[nodomain_], -1); confline(op, "ORGOUTPUTALIAS", "\v", op->outopts.lngstr[unkdomain_], -1); confline(op, "DIROUTPUTALIAS", "/", op->outopts.lngstr[rootdir_], -1); confline(op, "DIROUTPUTALIAS", "//", op->outopts.lngstr[nodir_], -1); confline(op, "TYPEOUTPUTALIAS", ".", op->outopts.lngstr[noext_], -1); confline(op, "TYPEOUTPUTALIAS", "./", op->outopts.lngstr[brkdirs_], -1); confline(op, "OSOUTPUTALIAS", "unkwin", op->outopts.lngstr[unkwin_], -1); confline(op, "OSOUTPUTALIAS", "unkmac", op->outopts.lngstr[unkmac_], -1); confline(op, "OSOUTPUTALIAS", "unkux", op->outopts.lngstr[unkux_], -1); confline(op, "OSOUTPUTALIAS", "unkos", op->outopts.lngstr[unkos_], -1); #ifndef NODNS if (dnslevel != DNS_NONE) { dnstable = rehash(NULL, HASHSIZE, NULL); if ((tempf = my_fopen(dnsfile, "DNS input file")) != NULL) { pos = NULL; goodtimec = (starttimeuxc > (timecode_t)dnsgoodhrs * 60)?\ (starttimeuxc - (timecode_t)dnsgoodhrs * 60):0; badtimec = (starttimeuxc > (timecode_t)dnsbadhrs * 60)?\ (starttimeuxc - (timecode_t)dnsbadhrs * 60):0; while((rc = nextdnsline(tempf, &timec, &name, &alias)) != EOF) { if (rc == TRUE && ((STREQ(alias, "*") && timec >= badtimec) || (!STREQ(alias, "*") && timec >= goodtimec)) && !(timec > starttimeuxc + MINS_IN_WEEK)) do_dns(name, alias, dnslevel); } (void)my_fclose(tempf, dnsfile, "DNS input file"); } if (dnslevel == DNS_WRITE) { #ifdef NOOPEN /* The ANSI, but less preferred, option. There is a race problem. Also if we have got overwrite access but not read, it will go wrong. */ if ((tempf = FOPENR(dnslockfile)) == NULL) { if ((dnslock = FOPENW(dnslockfile)) == NULL) { warn('F', TRUE, "Failed to create DNS lock file %s: " "backing off to DNS LOOKUP", dnslockfile); dnslevel = DNS_LOOKUP; } else debug('F', "Creating %s as DNS lock file", dnslockfile); } else { fclose(tempf); warn('F', TRUE, "DNS lock file %s already exists: backing off to DNS LOOKUP", dnslockfile); dnslevel = DNS_LOOKUP; } #else /* The following is not the strictly correct procedure on Unix. NFS is broken, so there can still be a race condition. One should really @ create a guaranteed unique file; @ hard link the lock file to the unique file; @ stat the unique file, testing for links == 2. However, this has the disadvantage that it can leave files with weird names lying around. Also it can't then share code with other platforms. I think that the chance of a problem is so small, and the consequences sufficiently non-serious, that this is good enough. */ if ((i = open(dnslockfile, O_WRONLY | O_CREAT | O_EXCL, OPEN_MODE)) < 0) { if (errno == EEXIST) warn('F', TRUE, "DNS lock file %s already exists: backing off to DNS LOOKUP", dnslockfile); else warn('F', TRUE, "Failed to create DNS lock file %s: " "backing off to DNS LOOKUP", dnslockfile); dnslevel = DNS_LOOKUP; } else if ((dnslock = fdopen(i, "w")) == NULL) { /* can this happen? */ /* We don't actually write to the dnslock. But this is convenient for compatibility with the #ifdef NOOPEN approach. */ warn('F', TRUE, "Failed to create DNS lock file %s: backing off to DNS LOOKUP", dnslockfile); dnslevel = DNS_LOOKUP; } else debug('F', "Creating %s as DNS lock file", dnslockfile); #endif if (dnslevel == DNS_WRITE) { if ((dnsfilep = FOPENA(dnsfile)) == NULL) { warn('F', TRUE, "Failed to open DNS output file %s for writing: " "backing off to DNS LOOKUP", dnsfile); dnslevel = DNS_LOOKUP; fclose(dnslock); dnslock = NULL; if (remove(dnslockfile) != 0) warn('F', TRUE, "Trouble deleting DNS lock file %s", dnslockfile); else debug('F', "Deleting DNS lock file %s", dnslockfile); } else debug('F', "Opening %s as DNS output file", dnsfile); } } } /* end if dnslevel != DNS_NONE */ #endif } /* end if !vblesonly */ } /* Now functions for turning strings into log formats */ choice strtoinfmt(Inputformat **ans, char *s, choice *count) { extern Memman *xmemman; extern Inputfns inpfns[], pnlinpfn, ccinpfn; Inputformat *ifp; logical done, count_this, typedone[INP_NUMBER]; char tempchar = '\0'; char *c; int i; if (strchr(s, '%') == NULL) return(FMT_NOPC); for (i = 0; i < ITEM_NUMBER; i++) count[i] = 0; for (i = 0; i < INP_NUMBER; i++) typedone[i] = FALSE; *ans = (Inputformat *)submalloc(xmemman, sizeof(Inputformat)); ifp = *ans; /* First a messy special case. If %s occurs but not %S, promote %s to %S */ for (c = s, done = FALSE; *c != '\0' && !done; c++) { if (*c == '%') { c++; if (*c == '*') c++; if (*c == 'S') done = TRUE; c++; } } if (!done) { for (c = s; *c != '\0'; c++) { if (*c == '%') { c++; if (*c == '*') c++; if (*c == 's') *c = 'S'; c++; } } } /* Now the main routine */ for (c = s; *c != '\0'; c++) { if (*c == '%') { c++; if (*c == '%') { ifp->inpfns = &ccinpfn; ifp->sep = *c; } else { if (*c == '*') { count_this = FALSE; c++; } else count_this = TRUE; done = FALSE; for (i = 0; !done && inpfns[i].code != '\0'; i++) { if (*c == inpfns[i].code) { if (inpfns[i].type != UNSET) { if (typedone[inpfns[i].type]) return(FMT_DUP); typedone[inpfns[i].type] = TRUE; } ifp->inpfns = &inpfns[i]; if (inpfns[i].fn == &parsestring || inpfns[i].fn == &parseref || inpfns[i].fn == &parsemsbrow || inpfns[i].fn == &parsejunk || inpfns[i].fn == &parsecode) { c++; if (*c == '\0') { ifp->sep = '\n'; c--; } else if (*c == '\\') { c++; if (*c == '\\') ifp->sep = *c; else if (*c == 'n' || *c == 'r') ifp->sep = '\n'; else if (*c == 't') ifp->sep = '\t'; else return(FMT_BADCHAR); } else if (*c == '%') { c++; if (*c == '%') ifp->sep = *c; else if (*c == 'w' && inpfns[i].fn != &parseref) { ifp->sep = WHITESPACE; /* parseref can't take %w */ c -= 2; /* need to parsespace() too */ } else return(FMT_NOTERM); } else ifp->sep = *c; } else if (inpfns[i].fn == &parselogfmt) { c++; if (*c < '0' || *c > '6') return(FMT_BADBUILTIN); else ifp->sep = *c; } else /* fn != parse(string|ref|msbrow|junk|code|logfmt) */ ifp->sep = '\0'; if (i < ITEMFNS_NUMBER) count[inpfns[i].type] = count_this?2:1; done = TRUE; } /* end if *c == inpfns[i].code */ } /* end for i */ if (!done) return(FMT_BADPC); } } /* end if *c == '%' */ else if (*c == '\\') { c++; if (*c == '\\') { ifp->inpfns = &ccinpfn; ifp->sep = *c; } else if (*c == 'n' || *c == 'r') { ifp->inpfns = &pnlinpfn; ifp->sep = '\n'; } else if (*c == 't') { ifp->inpfns = &ccinpfn; ifp->sep = '\t'; } else return(FMT_BADCHAR); } /* end if *c == '\\' */ else { ifp->inpfns = &ccinpfn; ifp->sep = *c; } ifp->next = (Inputformat *)submalloc(xmemman, sizeof(Inputformat)); tempchar = ifp->sep; TO_NEXT(ifp); } if (tempchar != '\n') { ifp->inpfns = &pnlinpfn; ifp->next = (Inputformat *)submalloc(xmemman, sizeof(Inputformat)); TO_NEXT(ifp); } ifp->inpfns = NULL; if (typedone[INP_YEAR] || typedone[INP_MONTH] || typedone[INP_DATE] || typedone[INP_HOUR] || typedone[INP_MIN] || typedone[INP_AM]) { if (typedone[INP_UNIXTIME]) return(FMT_DUP); if (!(typedone[INP_YEAR] && typedone[INP_MONTH] && typedone[INP_DATE] && typedone[INP_HOUR] && typedone[INP_MIN])) return(FMT_PARTTIME); /* partial time info is corrupt */ count[INP_DATE] = (count[ITEM_FILE] == 2)?2:1; count[INP_AM] = (choice)typedone[INP_AM]; count[INP_UNIXTIME] = 0; } else if (typedone[INP_UNIXTIME]) { count[INP_DATE] = (count[ITEM_FILE] == 2)?2:1; count[INP_AM] = 0; count[INP_UNIXTIME] = 1; } else { count[INP_DATE] = 0; count[INP_AM] = 0; count[INP_UNIXTIME] = 0; } if (typedone[INP_QUERY] && !typedone[ITEM_FILE]) return(FMT_QBUTNOR); count[INP_BYTES] = 2 * (choice)(typedone[INP_BYTES] && count[ITEM_FILE] == 2); count[INP_PROCTIME] = 2 * (choice)(typedone[INP_PROCTIME] && count[ITEM_FILE] == 2); if (typedone[INP_CODE] && count[ITEM_FILE] == 2) count[INP_CODE] = 2; else if (typedone[INP_CODE]) count[INP_CODE] = 1; else count[INP_CODE] = 0; return(FMT_OK); } char *apachelogfmt(char *fmt) { extern char *workspace; /* assume large enough, as usual */ char *p, *q; workspace[0] = '\0'; for (p = fmt; *p != '\0'; p++) { if (*p == '\\' && *(p + 1) != '\0') p++; if (*p == '%' && *(p + 1) != '\0') { p++; while (ISDIGIT(*p) || *p == ',' || *p == '!' || *p == '<' || *p == '>') p++; if (*p == 'b') strcat(workspace, "%b"); else if (*p == 'u') strcat(workspace, "%u"); else if (*p == 'v' || *p == 'V') strcat(workspace, "%v"); else if (*p == 'h' || *p == 'a') strcat(workspace, "%S"); else if (*p == 's') strcat(workspace, "%c"); else if (*p == 'U') strcat(workspace, "%r"); else if (*p == 'r') strcat(workspace, "%j%w%r%wHTTP%j"); else if (*p == 't') strcat(workspace, "[%d/%M/%Y:%h:%n:%j]"); else if (*p == 'T') strcat(workspace, "%t"); else if (*p == 'q') strcat(workspace, "%q"); else if (substrcaseeq(p, "{user-agent}") && *(p + 12) == 'i') { strcat(workspace, "%B"); p += 12; } else if (substrcaseeq(p, "{referer}") && *(p + 9) == 'i') { strcat(workspace, "%f"); p += 9; } else { /* unknown code */ if (*p == '{') { while (*p != '}' && *p != '\0') p++; if (*p == '}') p++; if (*p == 't') return(NULL); } strcat(workspace, "%j"); } } else { q = strchr(workspace, '\0'); *(q++) = *p; *q = '\0'; } } return(workspace); } /*** Finally we move on to the command line argument processing. ***/ void clconfline(Options *op, char *s) { char *cmd = NULL, *arg1 = NULL, *arg2 = NULL; int rc; if ((rc = parseconfline(s, &cmd, &arg1, &arg2)) != -1) confline(op, cmd, arg1, arg2, rc); } void clgenrep(Options *op, choice rep, char *arg) { size_t len; char c, *d; op->outopts.repq[rep] = (arg[0] == '+')?TRUE:FALSE; if (arg[0] == '-') { if (arg[2] != '\0') CLLONGWARN(arg); } else if (arg[2] != '\0') { /* parse sort method */ rep = G(rep); /* future args are genargs only */ if (!ISALPHA(arg[2])) d = arg + 2; else { d = arg + 3; c = TOLOWER(arg[2]); if (c == 'r') op->outopts.sortby[rep] = REQUESTS; else if (c == 'p') op->outopts.sortby[rep] = PAGES; else if (c == 'b') op->outopts.sortby[rep] = BYTES; else if (c == 'a') op->outopts.sortby[rep] = ALPHABETICAL; else if (c == 'd') op->outopts.sortby[rep] = DATESORT; else if (c == 'x') op->outopts.sortby[rep] = RANDOM; else { warn('C', TRUE, "Unknown sort method in command line option %s", arg); return; } } if (*d != '\0') { /* parse floor */ len = strlen(d); c = TOLOWER(*(d + len - 1)); /* final character */ if (c != 'r' && c != 'p' && c != 'b' && c != 'd') { if (d == arg + 2) { warn('C', TRUE, "No sort method or floor given in command line option %s", arg); return; } /* else deduce floor method */ c = arg[2]; memmove((void *)(arg + 2), (void *)(arg + 3), len + 1); if (c == 'a' || c == 'x') *(d + len - 1) = 'r'; else *(d + len - 1) = c; configfloor((void *)&(op->outopts.floor[rep]), arg, arg + 2, NULL, -2); } else configfloor((void *)&(op->outopts.floor[rep]), arg, d, NULL, -2); } } } void cldebug(char **s, char *arg) { if (arg[0] == '-') { if (arg[2] != '\0') CLLONGWARN(arg); configdebug(s, arg, "FALSE", NULL, -2); } else if (arg[2] == '\0') configdebug(s, arg, "TRUE", NULL, -2); else configdebug(s, arg, arg + 2, NULL, -2); } void clargs(Options *op, int argc, char *argv[]) { extern char repcodes[]; extern logical vblesonly; extern char *debug_args, *warn_args; int i; choice j; for (i = 1; i < argc; i++) { if (strlen(argv[i]) > 255) { argv[i][70] = '\0'; warn('C', TRUE, "Ignoring long command line argument starting\n%s", argv[i]); } else if (!IS_EMPTY_STRING(argv[i])) { if (argv[i][0] != '+' && argv[i][0] != '-') configlogfile((void *)&(op->miscopts.logfile), argv[i], argv[i], NULL, -2); else switch (argv[i][1]) { case '\0': configlogfile((void *)&(op->miscopts.logfile), argv[i], "stdin", NULL, -2); break; case '4': case '5': case 'd': case 'D': case 'H': case 'h': case 'm': case 'P': case 'W': case 'x': case 'z': for (j = 0; repcodes[j] != argv[i][1]; j++) ; CLREPTOGGLE(j); break; case 'b': case 'B': case 'c': case 'E': case 'f': case 'i': case 'I': case 'J': case 'k': case 'K': case 'n': case 'N': case 'p': case 'o': case 'r': case 'S': case 't': case 'u': case 'v': case 'Z': for (j = 0; repcodes[j] != argv[i][1]; j++) ; clgenrep(op, j, argv[i]); break; case 's': if (STREQ(argv[i] + 2, "ettings")) vblesonly = TRUE; else clgenrep(op, REP_REFSITE, argv[i]); break; case 'a': CLOUTSTYLE(op->outopts.outstyle); break; case 'A': CLLONGCHECK(configall((void *)(op->outopts.repq), argv[i], (argv[i][0] == '+')?"ON":"OFF", NULL, -2)); break; case 'C': CLSHORTCHECK(clconfline(op, argv[i] + 2)); break; case 'F': if (argv[i][0] == '-') { CLLONGCHECK(op->dman.fromstr = NULL); } else CLSHORTCHECK(confline(op, "FROM", argv[i] + 2, NULL, -2)); break; case 'g': CLSHORTCHECK((void)config(argv[i] + 2, op)); break; case 'G': /* mandatory config file: already dealt with */ break; case 'O': CLSHORTCHECK(configstr((void *)&(op->outopts.outfile), NULL, argv[i] + 2, NULL, -1)); break; case 'q': cldebug(&warn_args, argv[i]); break; case 'T': if (argv[i][0] == '-') { CLLONGCHECK(op->dman.tostr = NULL); } else CLSHORTCHECK(confline(op, "TO", argv[i] + 2, NULL, -2)); break; case 'U': CLSHORTCHECK(confline(op, "CACHEFILE", argv[i] + 2, NULL, -2)); break; case 'V': cldebug(&debug_args, argv[i]); break; case 'X': CLGOTOS(op->outopts.gotos); break; case '-': if (STREQ(argv[i] + 2, "settings")) vblesonly = TRUE; else warn('C', TRUE, "Ignoring unknown command line argument %s", argv[i]); /* --help and --version are detected earlier, */ break; /* in settings() */ default: warn('C', TRUE, "Ignoring unknown command line argument %s", argv[i]); break; } } } }