/***             analog 4.16             http://www.analog.cx/             ***/
/*** This program is copyright (c) Stephen R. E. Turner 1995 - 2001 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.   ***/

/*** anlghea3.h: third header file ***/

/*** The things in this file are not user-definable; user-definable options
 *** can be found in anlghead.h ***/

#ifndef ANLGHEAD3
#define ANLGHEAD3

/*** OS specific things ***/

#define VNUMBER "4.16"    /* the version number of this program */

#undef VERSION /* If no OS set, compilation will fail by not knowing VERSION */

#ifdef __MWERKS__
#undef MAC
#define MAC        /* So MWERKS compiler automatically switches to Mac */
#endif
#ifdef __BEOS__
#undef MAC
#define BEOS
#endif
#ifdef _WIN32
#ifndef UNIX  /* So as to signal CYGWIN */
#undef WIN32
#define WIN32
#endif
#endif

#ifdef MAC
#define MAC_EVENTS
#define NOPIPES
#define MACDIRENT
#define NOOPEN
#define LINE_PARSER
#define USE_ZLIB
#include <MacHeaders.h>
#include <sioux.h>
#define DIRSEP ':'
#define EXTSEP "."
#define VERSION VNUMBER"/Mac"
#endif

#ifdef DOS
#define NODNS
#define DIRSEP '\\'
#define EXTSEP "."
#define VERSION VNUMBER"/DOS"
#endif

#ifdef VMS
#define NOPIPES
#define VMSDIRENT
#ifdef __VMS_VER
#if (__VMS_VER < 70000000)
#define NOGMTIME
#endif
#endif
#define DIRSEP '/'
#define EXTSEP "."
#include <unixio.h>
/* old DEC C (v4.x?) <stat.h> was missing these two defines */
#ifndef S_IWUSR
#define S_IWUSR 0000200 /* write permission, owner */
#endif
#ifndef S_IRUSR
#define S_IRUSR 0000400 /* read permission, owner */
#endif
#define cma$tis_errno_get_addr CMA$TIS_ERRNO_GET_ADDR
#define FOPENR(s) fopen(s, "r", "shr=upd")
#define VERSION VNUMBER"/OpenVMS"
#endif

#ifdef OSX
#define DIRSEP '/'
#define EXTSEP "."
#define VERSION VNUMBER"/OS X"
#endif

#ifdef UNIX
#define DIRSEP '/'
#define EXTSEP "."
#define VERSION VNUMBER"/Unix"
#endif

#ifdef WIN32
#define popen _popen
#define pclose _pclose
extern void Win32Init(void);
extern void Win32Cleanup(void);
#define WIN32DIRENT  /* can comment out if POSIX dirent present, eg Borland */
#define FOPENR_BINARY
#define POPENR_BINARY
#define DIRSEP '\\'
#define EXTSEP "."
#define VERSION VNUMBER"/Win32"
#endif

#ifdef OS2
#define DIRSEP '\\'
#define EXTSEP "."
#define VERSION VNUMBER"/OS2"
#endif

#ifdef RISCOS
#define NODNS
#define NOPIPES
#define NODIRENT
#define DIRSEP '.'
#define EXTSEP "/"
#define VERSION VNUMBER"/RISCOS"
#endif

#ifdef NEXTSTEP
#define DIRSEP '/'
#define EXTSEP "."
#ifndef S_IWUSR
#define S_IWUSR 0000200 /* write permission, owner */
#endif
#ifndef S_IRUSR
#define S_IRUSR 0000400 /* read permission, owner */
#endif
#define VERSION VNUMBER"/NeXTSTEP"
#endif

#ifdef BEOS
#define DIRSEP '/'
#define EXTSEP "."
#define VERSION VNUMBER"/BeOS"
#endif

#ifdef MPEIX
#define DIRSEP '/'
#define EXTSEP "."
#define VERSION VNUMBER"/iX"
#endif

#ifdef BS2000
#define EBCDIC
#define DIRSEP '/'
#define EXTSEP "."
#define VERSION VNUMBER"/BS2000/OSD"
#endif

#ifdef AS400
#define EBCDIC
#define DIRSEP '/'
#define EXTSEP "."
#define ALIGNSIZE (16)
#define NOPIPES
#define FOPENR(s) fopen(s, "r,ccsid=37")
#define FOPENW(s) fopen(s, "w,ccsid=37")
#define FOPENA(s) fopen(s, "a,ccsid=37")
#define VERSION VNUMBER"/AS400"
#endif

/*** ANSI header files ***/

#include <stdio.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <float.h>
#include <time.h>
#include <signal.h>
#include <errno.h>

/*** system dependent header files ***/

#include "pcre.h"

#ifdef WIN32
#include <windows.h>
#endif

#ifdef MAC
#ifdef LINE_PARSER
#ifndef REAL_STDIO
#define fopen(name,mode) mac_fopen((name),(mode))
#define fread(ptr,siz,num,file) mac_fread(ptr,siz,num,file)
#undef getc
#define getc(file) mac_getc(file)
#define fclose(file) mac_fclose((file))
#undef feof
#define feof(file) mac_feof(file)
#endif
extern FILE *mac_fopen(const char *name,const char *mode);
extern size_t mac_fread(void *ptr, size_t size, size_t num, FILE *file);
extern int mac_getc(FILE *file);
extern int mac_fclose(FILE *file);
extern int mac_feof(FILE *file);
#endif
#ifndef REAL_ALLOC
#define malloc(size) mac_malloc((size))
#define calloc(num,size) mac_calloc((num),(size))
#define realloc(ptr,size) mac_realloc((ptr),(size))
#define free(ptr) mac_free((ptr))
#endif
extern void *mac_malloc(size_t size);
extern void *mac_calloc(size_t num, size_t size);
extern void *mac_realloc(void *ptr, size_t size);
extern void mac_free(void *ptr);
#endif

#ifdef NEED_FLOATINGPOINT_H
#include <floatingpoint.h>
#endif

#ifndef NOOPEN
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef WIN32
#include <io.h>
#define OPEN_MODE (S_IREAD | S_IWRITE)
#else
#define OPEN_MODE (S_IRUSR | S_IWUSR)
/* Most systems will accept the above too, but this is POSIXly correct. */
#endif
#endif

/* I assume the header files are well enough behaved that we can include them
   twice without harm, so that we can just do !NOSTAT and !NODIRENT indep. */
#ifndef NODIRENT
#ifndef VMSDIRENT
#ifndef MACDIRENT
#ifndef WIN32DIRENT
#include <sys/types.h>       /* POSIX dirent */
#include <dirent.h>
#include <sys/stat.h>
#else
#include <io.h>              /* Win32 dirent */
#include <sys/types.h>
#include <sys/stat.h>
#endif
#else
#include "macdir.h"          /* Mac dirent */
#endif
#else
#define VMS_FSPEC_MAX 256    /* VMS dirent */
#include <descrip.h>
#endif
#endif

#ifndef NODNS
#ifdef MAC
#include <Gestalt.h>         /* Mac DNS */
#include <MacTCP.h>
#include <OpenTransport.h>
#include <OpenTptInternet.h>
#include <AddressXlation.h>
#else
#ifdef HAVE_ADDR_T   /* Win32 and normal DNS */
#define IN_ADDR_T in_addr_t
#else
typedef unsigned long IN_ADDR_T;
#endif
#ifdef WIN32    /* Win32 DNS: NB windows.h above includes winsock.h */
#define INET_ADDR_ERR ((IN_ADDR_T)(INADDR_NONE))
#else
#include <sys/socket.h>      /* Normal DNS */
#include <netinet/in.h>
#include <netdb.h>
#ifndef BEOS
#include <arpa/inet.h>
#endif
#define INET_ADDR_ERR ((IN_ADDR_T)(-1))
#endif
#endif
#endif

/*** User-definable header file ***/

#include "anlghead.h"

/*** definitions ***/

#undef TRUE
#define TRUE (1)
#undef FALSE
#define FALSE (0)
#undef OK
#define OK (0)
#undef ERR
#define ERR (-1)
#undef UNSET
#define UNSET (-1)
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS (0)   /* ANSI, but some broken implementations lack it. */
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE (1)
#endif
#define EXIT_SIGNAL ((EXIT_FAILURE) + 1)
#ifdef WIN32
#define ERRBUFMODE (_IONBF)  /* on Windows, _IOLBF causes full buffering */
#else
#define ERRBUFMODE (_IOLBF)
#endif
#define FEW (2)
#define NEWLOGFMT (2)
#define BADLOGFMT (3)
#define CONTINUATION (-1)  /* used in warn() in utils.c */
#define REGEX_INC (2)  /* next four used in (struct include).type */
#define NORMAL_INC (1) /* see also IS_REGEXT(), IS_INC() macros below */
#define NORMAL_EXC (-1)
#define REGEX_EXC (-2)
#define BLOCK_EPSILON (30)
#define MIN_SC (100)   /* see parsescode() in input.c */
#define SC_NUMBER (600)
#define IGNORE_CODE (1000)
#define EPSILON (1e-8)
#define MINS_IN_WEEK (10080)
#define HASHSIZE (8)      /* initial size of hash table; a power of 2 */
#define TREEHASHSIZE (3)  /* a power of 2 plus 1 */
#define SIZEBINS (11)     /* in sizescore() */
#define ITEMFNS_NUMBER ((ITEM_NUMBER) + 2) /* in inpfns[] in globals.c */
#define ANALOGURL "http://www.analog.cx/"
#define FIRST_TIME (0)
#define LAST_TIME (ULONG_MAX)
#define WHITESPACE ((char)(-1))
#define DOMLEVEL_NUMBER (677)  /* 26^2 + 1 */
#define PMATCH_SIZE (30)  /* This allows PCRE to capture 9 subexpressions */
#ifdef MAC_EVENTS
#define MAC_IDLE_FREQ (200)
#endif

/*** logfile line types ***/

#define LOG_COMMON1 "%S %j %u [%d/%M/%Y:%h:%n:%j] \"%j%w%r%wHTTP%j\" %c %b"
#define LOG_COMMON2 "%S %j %u [%d/%M/%Y:%h:%n:%j] \"%j%w%r\" %c %b"
#define LOG_COMMON3 "%S %j %u [%d/%M/%Y:%h:%n:%j] \"%r\" %c %b"
#define LOG_MS_COMMON1 "%S %j %u [%d/%M/%Y:%h:%n:%j] \"%j%w%r%w\"HTTP%j\" %c %b"
#define LOG_COMBINED1 \
  "%S %j %u [%d/%M/%Y:%h:%n:%j] \"%j%w%r%wHTTP%j\" %c %b \"%f\" \"%B\""
#define LOG_COMBINED2 \
  "%S %j %u [%d/%M/%Y:%h:%n:%j] \"%j%w%r\" %c %b \"%f\" \"%B\""
#define LOG_COMBINED3 \
  "%S %j %u [%d/%M/%Y:%h:%n:%j] \"%r\" %c %b \"%f\" \"%B\""
#define LOG_MS_INT1 "%S, %u, %d/%m/%y, %h:%n:%j, W3SVC%j, %j, %v, %T, %j, %b, %c, %j, %j, %r, %q,"
#define LOG_MS_INT2 "%*S, %*u, %d/%m/%y, %h:%n:%j, %j"
#define LOG_MS_NA1 "%S, %u, %m/%d/%y, %h:%n:%j, W3SVC%j, %j, %v, %T, %j, %b, %c, %j, %j, %r, %q,"
#define LOG_MS_NA2 "%*S, %*u, %m/%d/%y, %h:%n:%j, %j"
#define LOG_WEBSTAR1 "!!LOG_FORMAT%x2"
#define LOG_WEBSTAR2 "!!%j"
#define LOG_EXTENDED1 "#Fields:%x3"
#define LOG_EXTENDED2 "#%j"
#define LOG_MS_EXTENDED1 "#Fields:%x5"
#define LOG_WEBSTAR_EXTENDED1 "#Fields:%x6"
#define LOG_WEBSITE_NA \
  "%m/%d/%y %h:%n:%j\t%S\t%v\t%j\t%u\t%j\t%r\t%f\t%j\t%B\t%c\t%b\t%T"
#define LOG_WEBSITE_INT \
  "%d/%m/%y %h:%n:%j\t%S\t%v\t%j\t%u\t%j\t%r\t%f\t%j\t%B\t%c\t%b\t%T"
#define LOG_NETSCAPE "format=%x4"
#define LOG_REFERRER1 "[%d/%M/%Y:%h:%n:%j] %F -> %*r"
#define LOG_REFERRER2 "%F -> %*r"
#define LOG_BROWSER "[%d/%M/%Y:%h:%n:%j] %B"

/*** enums ***/

/* Types of item and other inputs: must have ITEMs in process_data() order:
   code, bytes, proctime, unixtime, am, date in some order (these are
   INPUT_NUMBER, not to be confused with INP_NUMBER): then rest in some order.
   NB I don't know if changing process_data() order would have side-effects. */
/* See also item_type in globals.c */
enum {ITEM_VHOST, ITEM_FILE, ITEM_USER, ITEM_REFERRER, ITEM_BROWSER, ITEM_HOST,
      INP_CODE, INP_BYTES, INP_PROCTIME, INP_UNIXTIME, INP_AM, INP_DATE,
      INP_YEAR, INP_MONTH, INP_HOUR, INP_MIN, INP_QUERY, INP_IP, INP_NUMBER};
#define ITEM_NUMBER (INP_CODE)
#define INPUT_NUMBER (INP_YEAR)  /* NB There is INP_NUMBER and INPUT_NUMBER */

/* Types of report: must have date reports then GENSUM then other reports,
   finishing with derv(tree)reps, then SIZE, CODE, PROCTIME in some order. */
/* See also repcodes, anchorname and repname in globals.c */
enum {REP_MONTH, REP_WEEK, REP_DAYREP, REP_DAYSUM, REP_HOURREP,	REP_HOURSUM,
      REP_QUARTER, REP_FIVE, REP_GENSUM, REP_REQ, REP_REDIR, REP_FAIL,
      REP_TYPE, REP_DIR, REP_HOST, REP_DOM, REP_REF, REP_REFSITE, REP_REDIRREF,
      REP_FAILREF, REP_BROWREP, REP_VHOST, REP_USER, REP_FAILUSER, REP_ORG,
      REP_SEARCHREP, REP_SEARCHSUM, REP_BROWSUM, REP_OS, REP_SIZE, REP_CODE,
      REP_PROCTIME, REP_NUMBER};
#define DATEREP_NUMBER (REP_GENSUM)
#define FIRST_GENREP (REP_GENSUM + 1)
#define GENREP_NUMBER (REP_NUMBER - FIRST_GENREP)
#define FIRST_DERVREP (REP_SEARCHREP)
#define DERVREP_NUMBER (4)
#define FIRST_ARRAYREP (REP_SIZE)
#define ARRAYREP_NUMBER (3)
#define LAST_NORMALREP (REP_OS)

/* Types of status code. Order insignificant. */
enum {SUCCESS, FAILURE, REDIRECT, INFO, UNWANTED};

/* Counts then dates collected about each item, and then other floor/sort
   criteria: order otherwise insignificant. See also methodname in globals.c */
enum {REQUESTS, PAGES, REDIR, FAIL, SUCCDATE, REDIRDATE, FAILDATE, BYTES,
	ALPHABETICAL, RANDOM, METHOD_NUMBER};
#define COUNT_NUMBER (SUCCDATE)
#define DATESORT (SUCCDATE)
#define DATA_NUMBER (BYTES)

/* Data collected about total requests etc. in each logfile */
enum {LOGDATA_UNWANTED, LOGDATA_UNKNOWN, LOGDATA_UNKNOWN7, LOGDATA_INFO,
      LOGDATA_INFO7, LOGDATA_SUCC, LOGDATA_SUCC7, LOGDATA_PAGES,
      LOGDATA_PAGES7, LOGDATA_REDIR, LOGDATA_REDIR7, LOGDATA_FAIL,
      LOGDATA_FAIL7, LOGDATA_CORRUPT, LOGDATA_NUMBER};

/* Poss. columns: COL_TITLE must be first; see also configcols() in init2.c */
enum {COL_TITLE, COL_REQS, COL_PAGES, COL_BYTES, COL_PREQS, COL_PPAGES,
	COL_PBYTES, COL_DATE, COL_TIME, COL_INDEX, COL_NUMBER};

/* Levels of DNS lookup */
enum {DNS_NONE, DNS_READ, DNS_LOOKUP, DNS_WRITE};

/* Output types: start with HTML then PLAIN */
enum {HTML, PLAIN, ASCII, COMPUTER, OUT_NONE};

/* days of the week */
enum {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY};

/* languages; see also country in globals.c */
enum {ARMENIAN, BOSNIAN, BULGARIAN, CATALAN, SIMP_CHINESE, TRAD_CHINESE,
      CROATIAN, CZECH, DANISH, DUTCH, ENGLISH, US_ENGLISH, FINNISH, FRENCH,
      GERMAN, GREEK, HUNGARIAN, ICELANDIC, ITALIAN, JAPANESE, KOREAN,
      NORWEGIAN, NYNORSK, POLISH, PORTUGUESE, BR_PORTUGUESE, ROMANIAN, RUSSIAN,
      SERBIAN, SLOVAK, SLOVENE, SPANISH, SWEDISH, TURKISH, UKRAINIAN};

/* lngstrs; charset_ must be first */
enum {charset_, weekbeg_, month_, day_, days_, hr_, minute_, minutes_, second_,
      seconds_, byte_, bytes_, request_, requests_, date_, datetime_,
      firstdate_, firsttime_, lastdate_, lasttime_, file_, files_, host_,
      hosts_, vhost_, vhosts_, dir_, dirs_, dom_, doms_, org_, orgs_, ext_,
      exts_, url_, urls_, browser_, browsers_, os_, oss_, size_, search_,
      searches_, site_, sites_, user_, users_, code_, codes_, webstatsfor_,
      gensum_, monthrep_, busymonth_, weekrep_, busyweek_, daysum_, dayrep_,
      busyday_, hourrep_, hoursum_, busyhour_, quarter_, busyquarter_, five_,
      busyfive_, hostrep_, hostgs_, hostgp_, hostgen_, dirrep_, dirgs_, dirgp_,
      dirgen_, typerep_, extgs_, extgp_, extgen_, reqrep_, filegs_, filegp_,
      filegen_, redirrep_, redirgs_, redirgp_, redirgen_, failrep_, failgs_,
      failgp_, failgen_, refrep_, refgs_, refgp_, refgen_, refsiterep_,
      refsitegs_, refsitegp_, refsitegen_, redirrefrep_, redirrefgs_,
      redirrefgp_, redirrefgen_, failrefrep_, failrefgs_, failrefgp_,
      failrefgen_, searchrep_, searchrepgs_, searchrepgp_, searchrepgen_,
      searchsum_, searchsumgs_, searchsumgp_, searchsumgen_, vhostrep_,
      vhostgs_, vhostgp_, vhostgen_, userrep_, usergs_, usergp_, usergen_,
      failuserrep_, failusergs_, failusergp_, failusergen_, browsum_, browgs_,
      browgp_, browgen_, browrep_, browrepgs_, browrepgp_, browrepgen_, osrep_,
      osgs_, osgp_, osgen_, domrep_, domgs_, domgp_, domgen_, orgrep_, orggs_,
      orggp_, orggen_, statrep_, codegs_, codegp_, codegen_, proctimerep_,
      sizerep_, credit_, runtime_, lessone_, eachunit_, represents_, partof_,
      pagereq_, pagereqs_, notlistedm_, notlistedf_, notlistedn_, progstart_,
      reqstart_, to_, succreqs_, avereqs_, totpages_, avepages_, totunknown_,
      totfails_, totredirs_, inforeqs_, distfiles_, disthosts_, corrupt_,
      unwanted_, totdata_, avedata_, brackets_, sevendaysto_, lastsevendays_,
      goto_, top_, unresolved_, nodomain_, unkdomain_, rootdir_, nodir_,
      noext_, brkdirs_, unkwin_, unkmac_, unkux_, unkos_, nreqs_, preqs_,
      npgs_, ppgs_, nbytes_, pbytes_, index_, firstsm_, firstsf_, firstsn_,
      firstdsm_, firstdsf_, firstdsn_, allsm_, allsf_, allsn_, floorby_,
      atleast_, redirreq_, redirreqs_, failreq_, failreqs_, ptraffic_,
      pmtraffic_, btraffic_, sincedate_, sinceredirdate_, sincefaildate_,
      sortedm_, sortedf_, sortedn_, traffic_, prequests_, pmrequests_,
      nrequests_, ppages_, pmpages_, npages_, predirs_, pmredirs_, nredirs_,
      pfails_, pmfails_, nfails_, succdate_, redirdate_, faildate_,
      alphasortm_, alphasortf_, alphasortn_, numsortm_, numsortf_, numsortn_,
      unsortedm_, unsortedf_, unsortedn_, colon_, datefmt1_, datefmt2_,
      dayrepfmt_, hourrepfmt_, quarterfmt_, weekfmt_, monthfmt_, genrepdate_,
      genreptime_, whatincfmt_,
      code100_, code101_, code199_, code200_, code201_, code202_,
      code203_, code204_, code205_, code206_, code299_, code300_, code301_,
      code302_, code303_, code304_, code_305, code306_, code307_, code399_,
      code400_, code401_, code402_, code403_, code404_, code405_, code406_,
      code407_, code408_, code409_, code410_, code411_, code412_, code413_,
      code414_, code415_, code416_, code417_, code499_,
      code500_, code501_, code502_, code503_, code504_, code505_, code506_,
      code599_, code999_, LNGSTR_NUMBER};

/* return codes from strtoinfmt() */
/* see also configlogfmt() in init2.c */
enum {FMT_OK, FMT_NOPC, FMT_DUP, FMT_BADCHAR, FMT_NOTERM, FMT_BADBUILTIN,
      FMT_BADPC, FMT_PARTTIME, FMT_QBUTNOR};

/*** typedefs ***/

typedef unsigned char logical;
typedef signed char choice;
typedef unsigned int datecode_t;
typedef unsigned long timecode_t;

/*** structs used in next section */

typedef struct {
  unsigned long data[DATA_NUMBER];  /* some are really timecode_t */
  double bytes;
  logical last7, reused;  /* for reused, see newtreedata() in tree.c */
  choice ispage;
} Hashentry;

typedef struct hashindex {
  char *name;
  Hashentry *own;
  void *other;
  struct hashindex *next;
} Hashindex;

typedef struct aliasto {
  char *string;
  choice after;
  struct aliasto *next;
} AliasTo;

typedef struct alias {
  char *from;
  pcre *pattern;
  AliasTo *to;
  logical isregex;
  struct alias *next;
} Alias;
/* For a normal pattern, nmatch is no. of *'s. For a regular expression, it's
   -3 * (no. of capturing subexpressions): this is what pcre_exec needs. */

typedef struct strpair {
  char *name, *data;
  struct strpair *next;
} Strpair;

/*** typedef function pointers ***/

typedef void (*cutfnp)(char **, char **, char *, logical);
typedef void (*dcutfnp)(char **, char **, char *, struct strpair *);
typedef struct hashindex *(*mergefnp)(struct hashindex *, struct hashindex *,
				      unsigned long, unsigned long, choice);
typedef void (*conffnp)(void *, char *, char *, char *, int);
typedef choice (*inputfnp)(FILE *, void *, char);

/*** rest of structs ***/

typedef struct {
  char *name;
  conffnp fn;
  void *opt;
} Configfns;

typedef struct {
  char code;
  choice type;
  inputfnp fn;
  void *opt;
} Inputfns;

typedef struct inputformat {
  Inputfns *inpfns;
  char sep;
  struct inputformat *next;
} Inputformat;

typedef struct inputformatlist {
  Inputformat *form;
  choice count[INPUT_NUMBER]; /* 0 = not present, 1 = starred, 2 = unstarred */
  logical used;
  struct inputformatlist *next;
} Inputformatlist;

typedef struct filelist {
  char *name;
  Inputformatlist *format;
  timecode_t from, to;
  unsigned long data[LOGDATA_NUMBER];
  double bytes, bytes7;
  char *prefix;
  size_t prefixlen;
  int pvpos, tz;
  logical ispipe;
  struct filelist *next;
} Filelist;

typedef struct mmlist {
  void *pos;
  struct mmlist *next;
} Mmlist;

typedef struct {
  void *block_end, *curr_pos, *next_pos;
  Mmlist *first, *last;
  unsigned short alignment;
} Memman;

typedef struct daysdata {
  unsigned long *reqs, *pages;
  double *bytes;
  struct daysdata *prev, *next;
} Daysdata;

typedef struct {
  char *fromstr, *tostr;
  timecode_t from, to, last7from, last7to, firsttime, lasttime;
  datecode_t firstdate, lastdate, currdate;
  /* perhaps bad name: currdate is date of current logfile entry */
  Daysdata *firstdp, *lastdp, *currdp;
} Dateman;

typedef struct timerep {
  unsigned long reqs, pages;
  double bytes;
  datecode_t date;
  struct timerep *prev, *next;
} Timerep;

typedef struct {
  double threshold;
  unsigned long reqs, pages;
  double bytes;
  timecode_t lastdate;
} Arraydata;

typedef struct {
  Hashindex **head;
  unsigned long size;
  unsigned long n;
} Hashtable;

typedef struct {
  Hashtable *tree;
  cutfnp cutfn;
  Memman *space;
} Tree;

typedef struct {
  Hashtable *table;
  dcutfnp cutfn;
  Memman *space;
  Strpair **list;
} Derv;

typedef struct strlist {
  char *name;
  struct strlist *next;
} Strlist;

typedef struct include {
  char *name;
  pcre *pattern;
  choice type;
  struct include *next;
} Include;

typedef struct {
  double min;
  char qual;
  choice floorby;
} Floor;

typedef struct {
  choice code;
  char *file;
} Lang;

typedef struct {
  char *outfile, *cacheoutfile, *domainsfile, *headerfile, *footerfile;
  choice outstyle, gotos;
  logical multibyte, html;
  choice reporder[REP_NUMBER + 1];
  logical cgi, repq[REP_NUMBER], last7, runtime;
  choice cols[REP_NUMBER][COL_NUMBER];
  unsigned int rows[DATEREP_NUMBER];
  char graph[DATEREP_NUMBER];
  logical back[DATEREP_NUMBER];
  choice sortby[GENREP_NUMBER], subsortby[GENREP_NUMBER];
  Floor floor[GENREP_NUMBER], subfloor[GENREP_NUMBER];
  Lang lang;
  char *dayname[7], *monthname[12], **lngstr;
  size_t daylen, plaindaylen, monthlen, plainmonthlen;
  char *hostname, *hosturl, *logo, *imagedir, *stylesheet;
  char markchar;
  logical rawbytes;
  logical norobots;
  choice weekbeginson;
  unsigned int plainpagewidth, htmlpagewidth, pagewidth, mingraphwidth;
  char sepchar, repsepchar, decpt, barstyle;
  char *compsep;
  Tree *tree[GENREP_NUMBER];
  Strlist *suborgs;
  Derv *derv[DERVREP_NUMBER];
  choice *alltrees, *alldervs;
  Include *wanthead[GENREP_NUMBER];
  Alias *aliashead[GENREP_NUMBER];
  Include *link, *reflink;
  char *baseurl;
  logical searchconv;
  unsigned char convfloor;
} Outchoices;

typedef struct {
  Filelist *logfile;
  Filelist *cachefile;
  unsigned long data[LOGDATA_NUMBER];
  double bytes, bytes7;
  char *dirsuffix;
  unsigned int dirsufflength;
  choice lowmem[ITEM_NUMBER];
  logical case_insensitive, usercase_insensitive;
  unsigned int granularity;
} Miscoptions;

typedef struct {
  Hashtable *hash[ITEM_NUMBER];
  Arraydata *arraydata[ARRAYREP_NUMBER];
  Hashindex **gooditems, **baditems;
  Include *wanthead[ITEM_NUMBER], *ispagehead, *argshead, *refargshead;
  choice code2type[SC_NUMBER];
  logical succ304;
  Alias *aliashead[ITEM_NUMBER];
  Strpair *searchengines;
  Dateman dman;
  Outchoices outopts;
  Miscoptions miscopts;
  Strlist *conffilelist;
} Options;

/*** macros ***/

#undef MAX
#define MAX(a, b) (((a) > (b))?(a):(b))
#undef MIN
#define MIN(a, b) (((a) < (b))?(a):(b))
/* a whole set of macros to cope with to...() and is...() commands if
   char is unsigned. */
#define ISALNUM(c) isalnum((unsigned char)(c))
#define ISALPHA(c) isalpha((unsigned char)(c))
#define ISDIGIT(c) isdigit((unsigned char)(c))
#define ISLOWER(c) islower((unsigned char)(c))
#define ISSPACE(c) isspace((unsigned char)(c))
#define ISUPPER(c) isupper((unsigned char)(c))
#define TOLOWER(c) ((char)tolower((unsigned char)(c)))
#define TOUPPER(c) ((char)toupper((unsigned char)(c)))
#ifdef NEED_STRCMP
#define strcmp my_strcmp
#endif
#ifdef NEED_DIFFTIME
#define difftime(t, s) ((double)((t) - (s)))
#endif
#ifdef NEED_STRTOUL
#define strtoul(a, b, c) ((unsigned long)(strtol(a, b, c)))
/* This is not quite right because of the different ranges of long and
   unsigned long, but it's probably OK for our purposes */
#endif
#define STREQ(s, t) (strcmp(s, t) == 0)
#define MATCHES(s, p) (matchq(s, (void *)p, FALSE, NULL))
#define DAYOFWEEK(d) ((choice)(((d) + 3) % 7))   /* by defns. in dates.c */
#define IS_EMPTY_STRING(s) ((s)[0] == '\0')
#define IS_STDIN(s) (STREQ((s), "-") || strcaseeq((s), "stdin"))
#define TO_NEXT(x) (x) = (x)->next
#ifndef FOPENR
#ifdef FOPENR_BINARY
#define FOPENR(s) fopen(s, "rb")
#else
#define FOPENR(s) fopen(s, "r")
#endif
#endif
#ifndef FOPENW
#define FOPENW(s) fopen(s, "w")
#endif
#ifndef FOPENA
#define FOPENA(s) fopen(s, "a")
#endif
#ifndef POPENR
#ifdef POPENR_BINARY
#define POPENR(s) popen(s, "rb")
#else
#define POPENR(s) popen(s, "r")
#endif
#endif
#define ENTRY_WANTED(e) ((e) != unwanted_entry)
#define ENTRY_BLANK(e) ((e) == blank_entry)
#define LEN3(n, c) ((unsigned int)((c == '\0')?(n):((n) + (((n) - 1) / 3))))
/* The length of an n digit number with seps; e.g. 56789 [5] -> 56,789 [6] */
#define DEFAULTSTR(s, n) {s = NULL; \
                          configstr((void *)(&(s)), NULL, n, NULL, -1);}
#define TOO_FULL(n, z) ((n) > (z))   /* defn for hash table too full */
#define TOO_FULL_TREE TOO_FULL
#define NEW_SIZE(z) (2 * (z))        /* new size of the hash table */
#define NEW_SIZE_TREE(z) (2 * (z) - 1)
/* see ref to IS_REGEXT above; NB can't use >> with negative numbers */
#define IS_REGEXT(type) ((type) / 2)  /* for includes */
#define IS_INC(type) ((type) > 0)     /* for includes */
/* static char *s = NULL; static size_t len = 0; ENSURE_LEN(s, len, req_len) */
#define ENSURE_LEN(s, l, n) if ((n) > (l)) { \
                              s = (char *)xrealloc((void *)s, n); \
			      l = n;}
/* These PUT's are used in datesprintf() etc.: quicker than using sprintf() */
#define PUT02d(s, d) {*((s)++) = ((d) / 10) + '0'; \
			*((s)++) = ((d) % 10) + '0'; }
#define PUT04d(s, d) {*((s)++) = ((d) / 1000) + '0'; \
			*((s)++) = ((d) / 100) % 10 + '0'; \
			*((s)++) = ((d) / 10) % 10 + '0'; \
			*((s)++) = ((d) % 10) + '0'; }
#define PUT2d(s, d) {*((s)++) = ((d) < 10)?' ':(((d) / 10) + '0'); \
		       *((s)++) = ((d) % 10) + '0'; }
#define PUTc(s, c) {*((s)++) = (c); }
#define PUTs(s, t, n) {int z; size_t l = strlen(t); \
			  memcpy((void *)s, (void *)t, l); s += l; \
                          for (z = n; z > 0; z--) *((s)++) = ' '; }
/* Some simple command line processing parts implemented as macros */
#define CLSHORTWARN(arg) warn('C', TRUE, "No argument specified after %s command line option (or space wrongly left before argument)", arg)
#define CLLONGWARN(arg) warn('C', TRUE, "Command line argument %s too long: ignoring end of it", arg)
#define CLSHORTCHECK(command) if (argv[i][2] == '\0') CLSHORTWARN(argv[i]); \
                              else command
#define CLLONGCHECK(command) if (argv[i][2] != '\0') CLLONGWARN(argv[i]); \
                            command
#define CLTOGGLE(f) CLLONGCHECK(f = (argv[i][0] == '+')?TRUE:FALSE)
#define CLREPTOGGLE(n) CLTOGGLE(op->outopts.repq[n])
#define CLOUTSTYLE CLTOGGLE
#define CLGOTOS CLTOGGLE
/* these happen to work because PLAIN == TRUE == ON and HTML == FALSE == OFF */
/* abbreviations for output */
#define PRESTART() if (outstyle == HTML) fputs("<pre><tt>", outf);\
                   else if (outstyle == ASCII) fputc('\n', outf);
#define PREEND() if (outstyle == HTML) fputs("</tt></pre>\n", outf);
/* an abbreviation for arrays with size GENREP_NUMBER */
#define G(r) ((r) - FIRST_GENREP)
/* magicno as macro not fn. for speed: x is unsigned long to contain answer,
   s is (char *) to be magicked,
   b is unsigned long base (no. of hash bins); power of 2 in practice.
   Formula is (\sum_i 9^i c_i) [mod (ULONG_MAX + 1)] mod b */
#define MAGICNO(x, s, b) {register unsigned char *r; \
		    for(x = 0, r = (unsigned char *)(s); *r != '\0'; r++) { \
		    x = (x << 3) + x + (long)(*r); } \
		    x %= b; }
/* for tree magic number, t is end of string, bins are power of 2 plus 1,
   and formula has 33 in place of 9 */
#define MAGICNOTREE(x, s, t, b) {register unsigned char *r; \
		    for(x = 0, r = (unsigned char *)(s); \
			r < (unsigned char *)(t); r++) { \
		    x = (x << 5) + x + (long)(*r); } \
		    x %= (b - 1); x++; }

#include "anlghea4.h"  /* import function declarations */

#endif  /* anlghea3.h not previously included */
