home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / infoserv / gopher / Unix / GopherTools / glog / glog34.c.Z / glog34.c
Encoding:
C/C++ Source or Header  |  1994-04-07  |  40.3 KB  |  1,574 lines

  1. /*** glog34.c -- analysis tool for Unix gopherd logs ***/
  2. /******************** START CONFIGURATION ***************************/
  3.  
  4. /* Define NODETAIL if you don't want detail listings to be kept. Detail 
  5.  * listings can double the pointer memory required and slow things down.
  6.  */
  7.  
  8. /* #define NODETAIL    */  /* uncomment if you DON'T want detail listings */
  9. /* #define NOSEARCHTXT */  /* uncomment if you DON'T want to output WHAT  */
  10.                            /*    was searched for                         */
  11.  
  12. #define MAXGLINESIZE    500 /* Maximum length of a gopher log line */
  13. /******************** END CONFIGURATION ***************************/
  14. /* That was really hard wasn't it :) */
  15.  
  16. /* Bug Reports/suggestions goto */
  17. #define EMAIL "awick@csugrad.cs.vt.edu"
  18.  
  19. #define    GLOG_VERSION "Gopher Log Analyzer 3.4"
  20. /* Version 3.4 4/4/94 awick
  21.  * Improved coding efficiency in ProcessLine() routine
  22.  * Fixed bug when identifying NOPASV ftp types (previously they went
  23.  *  into the error log). If you use the NOPASV patch, you know what I mean.
  24.  * Search Type now shows what word(s) was(were) searched for...UNLESS
  25.  *  NOSEARCHTXT defined
  26.  * Cleaned up the code alot, and changed the way reports are produced
  27.  * Added the -g histograms option
  28.  * Added the -a averaging/estimating stuff
  29.  * Added the -i infile support
  30.  * Added the -o outfile support
  31.  * Added support for VMS dennis_sherman@unc.edu
  32.  *
  33.  * Version 3.3 7/20/93 jdc@selway.umt.edu
  34.  * fixed up main() routine so that: errors on the command line (or -h as
  35.  * first parameter) cause glog to print the help information and abort.
  36.  * fixed up PrintHelp() routine so help message is more understandable
  37.  * changed FILETYPE character from ' ' to 'I' (What about Image?)
  38.  *
  39.  * Version 3.2 
  40.  * Fixed a small bug with search
  41.  *
  42.  * Version 3.1  7/11/93 Andy Wick - awick@csugrad.cs.vt.edu
  43.  * Added supported for the "missing" gopher types, time plots, month
  44.  * reports and several other things that I forgot :)
  45.  * Also added -b and -e options.
  46.  *
  47.  * Version 3.0 
  48.  * by: Andy Wick - awick@csugrad.cs.vt.edu
  49.  * This version is an almost TOTAL rewrite of glog.  It now reads all
  50.  * the information into memory before it does ANYTHING.  It then goes
  51.  * through the arguments one at a time.  So in order to effect something
  52.  * you must change it before the report.  ie.  Argument order matters now.
  53.  *
  54.  * Version 2.2
  55.  * by: Chuck Shotton - cshotton@oac.hsc.uth.tmc.edu
  56.  *
  57.  * Version 2.1 12/29.92
  58.  * by: Michael Mealling - Georgia Institute of Technology
  59.  *       Office of Information Technology
  60.  *       michael.meallingl@oit.gatech.edu
  61.  *
  62.  * Versions 1.0 6/17/92
  63.  * by: Chuck Shotton - U of Texas Health Science Center - Houston,
  64.  *    Office    of Academic Computing
  65.  *    cshotton@oac.hsc.uth.tmc.edu
  66.  */
  67.  
  68.  
  69. #include <stdio.h>
  70. #include <string.h>
  71. /* Some machines don't have a stdlib.  You can remove it if need be
  72.  *  it is here for type checking, usually :) 
  73.  */
  74. #include <stdlib.h>
  75. #ifdef THINK_C
  76. #include <console.h> 
  77. #endif
  78.  
  79. /********************** THINGS THAT EFFECT INPUT/OUTPUT ********************
  80.  
  81. /* These are the different types of data that are currently reconized. 
  82.    Each must be unique and it only effects the output. */
  83. #define FILETYPE    'I'
  84. #define BINARYTYPE    'B'
  85. #define SOUNDTYPE    'S'
  86. #define DIRTYPE        'D'
  87. #define MAILDIRTYPE    'M'
  88. #define FTPTYPE        'F'
  89. #define RANGETYPE    'R'
  90. #define SEARCHTYPE    '?'
  91.  
  92. char     *ROOTNAME = "Root Connections";
  93. char     *AverageStrs[6] = {""," per year"," per month"," per week"," per day",
  94.             " per hour"};
  95.  
  96. /* How they are in your gopher log file */
  97. char    *Days[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  98. char    *Months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  99.                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  100.  
  101. /* The base file name for gnuplot reports */
  102. char DEFAULTBASE[7] = "gopher"; 
  103. /**********************************************************************/
  104.  
  105. /* GENERAL STUFF */
  106. typedef unsigned char byte;
  107.  
  108. /* Error log link list */
  109. typedef    struct enode_list {
  110.     char      *data;
  111.     struct enode_list *next;
  112. } ELIST_REC, *ELIST_PTR;
  113.  
  114. /* GOPHER LINE STUFF */
  115.  
  116. /* One line of the gopher log is stored in here */
  117. typedef struct gopher_line {
  118.    byte     day;
  119.    byte     month;
  120.    byte        hour;
  121.    short     date;
  122.    char     *hostname;
  123.    char     *path;
  124.    char     type;
  125. } GOPHER_REC, *GOPHER_PTR;
  126.  
  127. /* A Linked list of gopher lines */
  128. typedef    struct node_list {
  129.     GOPHER_PTR      data;
  130.         short         hits;
  131.     struct node_list *next;
  132. }  LIST_REC, *LIST_PTR;
  133.  
  134. /* Main tree */
  135. typedef    struct node_rec    {
  136.     GOPHER_PTR    data;
  137. #ifndef NODETAIL
  138.     LIST_REC     *llist;
  139. #endif
  140.         short         hits;
  141.     struct node_rec    *left, *right;
  142. } NODE_REC, *NODE_PTR;
  143.  
  144. /* The cruft list is a general list for things that aren't parse-able by
  145.  * ProcessLine().  "cruft" kept for historical reasons.
  146.  */
  147. ELIST_PTR     cruft = NULL;
  148.  
  149. /*
  150.  * The following lists are maintained.
  151.  */
  152. NODE_PTR     hosts = NULL;
  153. NODE_PTR     docs  = NULL;
  154. NODE_PTR     dais  = NULL; /* Can't be days because of VMS :) */
  155. NODE_PTR     dates = NULL;
  156. NODE_PTR     types = NULL;
  157. NODE_PTR     times = NULL;
  158.  
  159. long int     TotalConnects = 0;
  160.  
  161. char    *in_file_name = NULL; /* NULL if stdin */
  162.  
  163. /* Used to tell the Info routines that you are starting and stoping */
  164. #define STARTINFO    (GOPHER_PTR)0
  165. #define ENDINFO        (GOPHER_PTR)1
  166.  
  167. /* Width of reports, Width is a major hack, so be careful */
  168. byte     Width     =     62; /* 79 - WIDTHSUB */
  169. #define WIDTHSUB     17  /* The width of the standard print stuff */
  170.  
  171. /* Information */
  172.    /* Internal */
  173. #define NOINFO        0
  174. #define DETAILINFO    1
  175.    /* Changing these will change the command line options also */
  176.    /* Error log requested, but not a valid SORT TYPE */
  177. #define ERRORINFO    'E'
  178.    /* SORT TYPES */
  179. #define DATAINFO     'D'
  180. #define HOSTINFO    'H'
  181. #define WEEKDAYINFO    'W'
  182. #define MONTHDATEINFO    'M'
  183. #define TYPEINFO    'T'
  184. #define TIMEINFO    'I'
  185.  
  186. char *base = DEFAULTBASE;
  187.  
  188. /* Start stop dates */
  189. char start_date[13], stop_date[13];
  190.  
  191. /* Start stop months */
  192. int mbegin = 1, mend = 12;
  193.  
  194. /* Averages */
  195. byte AverageStrPos = 0; /* Everything */
  196. float AverageDiv = 1.0; /* Don't change the value */
  197.  
  198. /* The only forward decleration needed, since I wrote this the pascal way,
  199.  * the way all programs should be written. :) You don't need all the stupid
  200.  * forward declerations, or prototypes.
  201.  */
  202. void PrintInfo(NODE_PTR tree, void print(GOPHER_PTR), int cmp(GOPHER_PTR a, GOPHER_PTR b), byte DetailType);
  203.  
  204. /* Simple define that cleans up the code a little */
  205. #define INFOERROR(str, strparam) {fprintf(stderr, (str), (strparam)); exit(1);}
  206. #define EXITERROR(str) {fprintf(stderr, (str)); exit(1);}
  207. #define HELPERROR(str) {fprintf(stderr, (str)); PrintHelp(stderr,0);}
  208.  
  209. /*******************************/
  210. /* My StrStr, since it is not standard on all unix machines (sun 4.0.3) */
  211. char *mystrstr(char *s1, char *s2)
  212. {
  213. register int len;
  214.  
  215.    if((len = strlen(s2)) == 0)
  216.       return s1;
  217.  
  218.    while (*s1 != '\0')
  219.    {
  220.       if (strncmp(s1, s2, len) == 0)
  221.          return s1;
  222.       s1++;
  223.    }
  224.  
  225.    return NULL;
  226. }
  227. /*******************************/
  228. /* Add item to error log */
  229. ELIST_PTR InsertErrorLog(ELIST_PTR list, char *data)
  230. {
  231. ELIST_PTR temp;
  232. static ELIST_PTR ende;
  233.  
  234.    if (NULL == (temp = (ELIST_PTR)malloc(sizeof(ELIST_REC))))
  235.       EXITERROR("Not enough memory to add to ErrorLog\n");
  236.    if (NULL == (temp->data = (char *)malloc(sizeof(char) * (strlen(data) +1))))
  237.       EXITERROR("Not enough memory to add to ErrorLog\n");
  238.  
  239.    strcpy(temp->data, data);
  240.    temp->next = NULL;
  241.  
  242.    if (list == NULL)
  243.       return (ende = temp);
  244.  
  245.    ende->next = temp;
  246.    ende = ende->next;
  247.  
  248.    return(list);
  249. }
  250. #ifndef NODETAIL
  251. /*******************************/
  252. LIST_PTR InsertDetail(LIST_PTR list, GOPHER_PTR data)
  253. {
  254. LIST_PTR temp;
  255.  
  256.       if (NULL == (temp = (LIST_PTR)malloc(sizeof(LIST_REC))))
  257.          EXITERROR("Not enough memory to add to DetailList\n");
  258.       temp->data = data;
  259.       temp->next = list;
  260.       temp->hits = 1;
  261.       return(temp);
  262. }
  263. #endif
  264. /*******************************/
  265. /* Insert data into tree, if that element is already in the tree
  266.  * then increment the number of hits.  cmp is used to find the location
  267.  * in the tree.
  268.  */
  269.  
  270. NODE_PTR Insert(NODE_PTR tree, GOPHER_PTR data, int cmp(GOPHER_PTR a, GOPHER_PTR b))
  271. {
  272. int i;
  273.  
  274.    if (tree == NULL)
  275.    {
  276.       if (NULL == (tree = (NODE_PTR) malloc(sizeof(NODE_REC))))
  277.          EXITERROR("No memory for InsertHost\n");
  278.       tree->data = data;
  279.       tree->left = tree->right = NULL;
  280. #ifndef NODETAIL
  281.       tree->llist = InsertDetail(NULL, data);
  282. #endif
  283.       tree->hits = 1;
  284.       return(tree);
  285.    }
  286.  
  287.    i=cmp(data, tree->data);
  288.  
  289.    if (i > 0)
  290.       tree->right = Insert(tree->right, data, cmp);
  291.    else if (i<0) 
  292.       tree->left = Insert(tree->left, data, cmp);
  293.    else
  294.    {
  295.       tree->hits += 1;
  296. #ifndef NODETAIL
  297.       tree->llist = InsertDetail(tree->llist, data);
  298. #endif
  299.    }
  300.  
  301.    return(tree);
  302. }
  303. /*******************************/
  304. /* Return the node with the data in the tree, using the cmp routine */
  305. NODE_PTR Find(NODE_PTR tree, GOPHER_PTR data, int cmp(GOPHER_PTR a, GOPHER_PTR b))
  306. {
  307. int i;
  308.  
  309.    if (tree == NULL)
  310.       return (NULL);
  311.  
  312.    i=cmp(data, tree->data);
  313.  
  314.    if (i > 0)
  315.       return(Find(tree->right, data, cmp));
  316.    if (i<0) 
  317.       return(Find(tree->left, data, cmp));
  318.  
  319.    return(tree);
  320. }
  321. /*******************************/
  322. /* Get a single field from temp, and return the new spot */
  323. char *getf(char *temp, char *field)
  324. {
  325.    while(*temp == ' ')
  326.       temp++;
  327.  
  328.    *field = '\0';
  329.    if (*temp == '\n')
  330.       return(temp);
  331.  
  332.    while ((*temp != ' ') && (*temp != '\0'))
  333.      *field++ = *temp++;
  334.  
  335.    *field = '\0';
  336.    return(temp);
  337. }
  338. /*******************************/
  339. int TypesCmp(GOPHER_PTR a, GOPHER_PTR b)
  340. {
  341.    return(a->type - b->type); 
  342. }
  343. /*******************************/
  344. int TimesCmp(GOPHER_PTR a, GOPHER_PTR b)
  345.    return(a->hour - b->hour); 
  346. }
  347. /*******************************/
  348. int HostsCmp(GOPHER_PTR a, GOPHER_PTR b)
  349.    return(strcmp(a->hostname, b->hostname)); 
  350. }
  351. /*******************************/
  352. int DocsCmp(GOPHER_PTR a, GOPHER_PTR b)
  353.    return(strcmp(a->path, b->path)); 
  354. }
  355. /*******************************/
  356. int DaysCmp(GOPHER_PTR a, GOPHER_PTR b)
  357.    return(a->day - b->day); 
  358. }
  359. /*******************************/
  360. int DatesCmp(GOPHER_PTR a, GOPHER_PTR b)
  361. {
  362. int i = a->month - b->month;
  363.    if (i == 0)
  364.       return(a->date - b->date);
  365.    else
  366.       return(i); 
  367. }
  368. /*******************************/
  369. byte MonthStr2Num(char *str)
  370. {
  371. static char lastmonth[4] = "Jan"; /* Who knows if saving the last month */
  372. static int lastmonthnum = 1;      /* really makes it faster */
  373. int i;
  374.  
  375.    if (strcmp(lastmonth, str) == 0)
  376.       return(lastmonthnum);
  377.  
  378.    for(i=0;i<12;i++)
  379.       if (strcmp(Months[i], str) == 0)
  380.       {
  381.          strcpy(lastmonth, Months[i]);
  382.      lastmonthnum = i+1;
  383.      return(lastmonthnum);
  384.       }
  385.    return(13); 
  386. }
  387. /*******************************/
  388. byte DayStr2Num(char *str)
  389. {
  390. static char lastday[4] = "Sun"; /* Same here.  Is there a better way? */
  391. static int lastdaynum = 1;
  392. int i;
  393.  
  394.    if (strcmp(lastday, str) == 0)
  395.       return(lastdaynum);
  396.  
  397.    for(i=0;i<7;i++)
  398.       if (strcmp(Days[i], str) == 0)
  399.       {
  400.          strcpy(lastday, Days[i]);
  401.      lastdaynum = i+1;
  402.      return(lastdaynum);
  403.       }
  404.    return(8); 
  405. }
  406. /*******************************/
  407. /* This routine adds all the data to all the trees.  Now that I have done
  408.  * it this way, I really hate it.  What was I thinking? :)  It makes no
  409.  * sense to have the dates sort be a tree, since it is always the worst
  410.  * case tree :(.  Oh well maybe in 4.0 someone will fix it 
  411.  */
  412. void AddData(GOPHER_PTR data)
  413. {
  414.    if ((data->month >= mbegin) && (data->month <= mend))
  415.    {
  416.       hosts = Insert(hosts, data, HostsCmp);
  417.       docs  = Insert(docs, data, DocsCmp);
  418.       dates = Insert(dates, data, DatesCmp); /* Slow */
  419.       types = Insert(types, data, TypesCmp);
  420.       times = Insert(times, data, TimesCmp); 
  421.       dais  = Insert(dais, data, DaysCmp);
  422.       /* Small hack to have start and stop dates */
  423.       if (start_date[0] == '\0')
  424.       {
  425.          sprintf(start_date, "%s %s %d", Days[data->day-1], Months[data->month-1],
  426.             data->date);
  427.       }
  428.       sprintf(stop_date, "%s %s %d", Days[data->day-1], Months[data->month-1],
  429.          data->date);
  430.       TotalConnects++;
  431.    }
  432. }
  433.  
  434. /*******************************/
  435. /* Process a single line completely.  It checks to make sure it is a 
  436.  * Valid line, if not it inserts it into the cruft
  437.  */
  438. void ProcessLine(char *line)
  439. {
  440. GOPHER_PTR data;
  441. short len;
  442. char *temp; /* Used to save line, incase it is needed for cruft */
  443. char junk[MAXGLINESIZE];
  444. char message1[MAXGLINESIZE];
  445. char message2[MAXGLINESIZE];
  446.  
  447.    if (NULL == (data = (GOPHER_PTR)malloc(sizeof(GOPHER_REC))))
  448.       EXITERROR("Not enough memory to process line. Sorry\n");
  449.  
  450.    temp = line;
  451.  
  452.    temp = getf(temp, junk); /* Day */
  453.    if (8 == (data->day = DayStr2Num(junk))) 
  454.    { /* Not a real day of week */
  455.       free(data);
  456.       cruft = InsertErrorLog(cruft, line);
  457.       return;
  458.    }
  459.    temp = getf(temp, junk); /* Month */
  460.    if (13 == (data->month = MonthStr2Num(junk)))
  461.    { /* Not a real month */
  462.       free(data);
  463.       cruft = InsertErrorLog(cruft, line);
  464.       return;
  465.    }
  466.    temp = getf(temp, junk); /* Date */
  467.    data->date = atoi(junk);
  468.    temp = getf(temp, junk); /* Time */
  469.    junk[3] = '\0';
  470.    data->hour = atoi(junk); /* Hour */
  471.    temp = getf(temp, junk); /* Year */
  472.    temp = getf(temp, junk); /* Process ID */
  473.    temp = getf(temp ,junk); /* Hostname */
  474.    if (junk[0] == ':')
  475.    { /* A colon in the hostfield...baaad */
  476.       free(data);
  477.       cruft = InsertErrorLog(cruft, line);
  478.       return;
  479.    } 
  480.  
  481. /* This one is for that annoying 0.0.0.* IP address then gets stuck
  482.  * in the log when someone is trying to access something you ain't got
  483.  */
  484.    if (strncmp(junk,"0.0.0", 5) == 0)
  485.    { 
  486.       free(data);
  487.       cruft = InsertErrorLog(cruft, line);
  488.       return;
  489.    } 
  490.  
  491.    if (NULL == (data->hostname = (char *)malloc(sizeof(char) * (strlen(junk)+1))))
  492.       EXITERROR("Not enough memory. Sorry\n");
  493.    strcpy(data->hostname, junk);
  494.  
  495.    temp = getf(temp, junk); /* this should make junk[0]=':' */
  496.    if (junk[0] != ':')
  497.    { /* Now we don't have a colon */
  498.       free(data->hostname);
  499.       free(data);
  500.       cruft = InsertErrorLog(cruft, line);
  501.       return;
  502.    } 
  503.    temp = getf(temp, message1);
  504.    temp = getf(temp, message2);
  505.    while((*temp == ' ') && (*temp != '\0'))
  506.        temp++;
  507.    data->path = (char *)malloc(sizeof(char)*(strlen(temp)+1));
  508.    strcpy(data->path, temp);
  509.    data->path[strlen(temp)] = '\0';
  510.  
  511.  
  512.    if (0 != (len = strlen(data->path)))
  513.    {
  514.       if (data->path[len-1] == '\n')
  515.          data->path[len-1] = '\0';
  516.    }
  517.  
  518.    if (strcmp(message1, "Root") == 0)
  519.    {
  520.       data->type = DIRTYPE;
  521.       free(data->path);
  522.       data->path = ROOTNAME;
  523.       AddData(data);
  524.    }
  525.    else if ((strcmp(message1, "retrieved") == 0) && (strcmp(data->path, "/") == 0))
  526.    {
  527.       data->type = DIRTYPE;
  528.       free(data->path);
  529.       data->path = ROOTNAME;
  530.       AddData(data);
  531.    }
  532.    else if (strcmp(message1, "search") == 0)
  533.    {
  534.       strcpy(junk, message2);
  535. #ifdef NOSEARCHTXT
  536. /*This finds and removes everything after the " for " in a search line*/
  537.       if (strncmp(data->path, "for ", 4) == 0)
  538.       {
  539.          /* We found it at the beginning of data->path */
  540.          temp = data->path;
  541.       }
  542.       else if (NULL == (temp = mystrstr(data->path, " for ")))
  543.       { /* No " for " in the search */
  544.          free(data->path);
  545.          free(data->hostname);
  546.          free(data);
  547.          cruft = InsertErrorLog(cruft, line);
  548.          return;
  549.       }
  550.       *temp = '\0';
  551. #endif
  552.       strcat(junk, " "); /* There is at least one space here */
  553.       strcat(junk, data->path);
  554.       free(data->path);
  555.       data->path = (char *)malloc(sizeof(char)*(strlen(junk)+1));
  556.       strcpy(data->path, junk);
  557.       data->type = SEARCHTYPE;
  558.  
  559.       AddData(data);
  560.    }
  561.    /* changed from "ftp:",4 to "ftp",3 because of NO-PASV patch */
  562.    /* which uses ftp-vms: and ftp-nopasv to denote special ftp types */
  563.    else if (strncmp(message2, "ftp", 3) == 0)
  564.    {
  565.       strcpy(junk, data->path); /* Incase there was a space in the path */
  566.       free(data->path); /* Then we have to save off path, since it contains it*/
  567.       data->path = (char *)malloc(sizeof(char)*(strlen(message2)+strlen(junk)-2));
  568.       strcpy(data->path, message2+4);
  569.       if (strlen(junk) > 0)
  570.       {
  571.          strcat(data->path, " ");
  572.          strcat(data->path, junk);
  573.       }
  574.       data->type = FTPTYPE;
  575.  
  576.       AddData(data);
  577.    }
  578.    else if (strcmp(message1, "retrieved") == 0)
  579.    {
  580.       if (data->path[0] == '\0')
  581.       { /* We some how retrieved nothing */
  582.          free(data->path);
  583.          free(data->hostname);
  584.          free(data);
  585.          cruft = InsertErrorLog(cruft, line);
  586.          return;
  587.       } 
  588.  
  589.       if (strcmp(message2, "directory") == 0)
  590.          data->type = DIRTYPE;
  591.       else if (strcmp(message2, "maildir") == 0)
  592.          data->type = MAILDIRTYPE;
  593.       else if (strcmp(message2, "file") == 0)
  594.          data->type = FILETYPE;
  595.       else if (strcmp(message2, "binary") == 0)
  596.          data->type = BINARYTYPE;
  597.       else if (strcmp(message2, "sound") == 0)
  598.          data->type = SOUNDTYPE;
  599.       else if (strcmp(message2, "range") == 0)
  600.          data->type = RANGETYPE;
  601.       else
  602.       {
  603.          free(data->path);
  604.          free(data->hostname);
  605.          free(data);
  606.          cruft = InsertErrorLog(cruft, line);
  607.          return;
  608.       } 
  609.       AddData(data);
  610.  
  611.    }
  612.    else /* wasn't anything we know about, g+ maybe?*/
  613.    {
  614.       free(data->path);
  615.       free(data->hostname);
  616.       free(data);
  617.       cruft = InsertErrorLog(cruft, line);
  618.       return;
  619.    } 
  620.  
  621.    return;
  622. }
  623.  
  624. /*******************************/
  625. /* The main loop for gathering the data from stdin */
  626. void GatherInfo(void)
  627. {
  628. char line[MAXGLINESIZE];
  629. FILE *fp;
  630.  
  631.    start_date[0] = '\0';
  632.  
  633.    if (in_file_name == NULL)
  634.       fp = stdin;
  635.    else if (NULL == (fp = fopen(in_file_name, "r")))
  636.       INFOERROR("Error opening input file %s\n", in_file_name);
  637.    while(!feof(fp))
  638.    {
  639.       fgets(line, MAXGLINESIZE, fp);
  640.       if (feof(fp))
  641.          break;
  642.       ProcessLine(line);
  643.    }
  644. }
  645. /*******************************/
  646. /* These vars are only valid right after a call to TreeTo?List.  I could have
  647.  *  done some fancy var passing, but why bother. :)  All the print/plot/graph
  648.  *  routines should use the G vars.  Which are modified for averages.
  649.  */
  650. LIST_PTR GByNum;
  651. int GByNumMin; /* These two are used for histograms */
  652. int GByNumMax;
  653. int GNodes;
  654. long int GTotalConnects;
  655.  
  656. /*******************************/
  657. /* Insert the data and number of hits that data got, into a sorted (by hits)
  658.  * link list (GByNum).
  659.  */
  660. void InsertSByNum(GOPHER_PTR data, short hits)
  661. {
  662. LIST_PTR temp, temp2;
  663.  
  664.    if (NULL == (temp = (LIST_PTR)malloc(sizeof(LIST_REC))))
  665.       EXITERROR("Not enough memory in InsertByNum\n");
  666.    temp->data = data;
  667.    temp->next = NULL;
  668.    temp->hits = hits;
  669.  
  670. /* Figure out some vars */
  671.    if (hits < GByNumMin)
  672.       GByNumMin = hits;
  673.    if (hits > GByNumMax)
  674.       GByNumMax = hits;
  675.    GNodes++;
  676.    
  677.    if (GByNum == NULL)
  678.       GByNum = temp;
  679.    else if (GByNum->hits < hits)
  680.    {
  681.       temp->next = GByNum;
  682.       GByNum = temp;
  683.    }
  684.    else
  685.    {
  686.       temp2 = GByNum;
  687.       while (temp2->next != NULL)
  688.       {
  689.          if (temp2->next->hits < hits)
  690.      {
  691.         temp->next = temp2->next;
  692.         temp2->next = temp;
  693.         return;
  694.      }
  695.      temp2 = temp2->next;
  696.       }
  697.       temp2->next = temp;
  698.    }
  699. }
  700.  
  701. /*******************************/
  702. /* Place data and hits onto the front of the list GByNum */
  703. void InsertUByNum(GOPHER_PTR data, short hits)
  704. {
  705. LIST_PTR temp;
  706.  
  707.    if (NULL == (temp = (LIST_PTR)malloc(sizeof(LIST_REC))))
  708.       EXITERROR("Not enough memory in InsertUByNum\n");
  709.    temp->data = data;
  710.    temp->next = NULL;
  711.    temp->hits = hits;
  712.  
  713. /* Figure out some vars */
  714.    if (hits < GByNumMin)
  715.       GByNumMin = hits;
  716.    if (hits > GByNumMax)
  717.       GByNumMax = hits;
  718.    GNodes++;
  719.  
  720.    if (GByNum == NULL)
  721.       GByNum = temp;
  722.  
  723.    else
  724.    {
  725.       temp->next = GByNum;
  726.       GByNum = temp;
  727.    }
  728. }
  729. /*******************************/
  730. /* I did two different routines so it would be faster :).  I know this
  731.  * doesn't follow the logic of the rest of the program, but oh well.
  732.  * Do Inorder so that they remain in order, if two have the same
  733.  * num of hits.  S<orted> mean use InsertSByNum. U<nsorted> means use
  734.  * InsertUByNum.
  735.  */
  736. void TreeToSList(NODE_PTR tree)
  737. {
  738.    if (tree == NULL)
  739.       return;
  740.    
  741.    TreeToSList(tree->left);
  742.    InsertSByNum(tree->data, tree->hits);
  743.    TreeToSList(tree->right);
  744. }
  745.  
  746. /*******************************/
  747. /* See above :) */
  748. void TreeToUList(NODE_PTR tree)
  749. {
  750.    if (tree == NULL)
  751.       return;
  752.    
  753.    TreeToUList(tree->right);
  754.    InsertUByNum(tree->data, tree->hits);
  755.    TreeToUList(tree->left);
  756. }
  757.  
  758. /*******************************/
  759. NODE_PTR ListToTree(LIST_PTR list, int cmp(GOPHER_PTR, GOPHER_PTR))
  760. {
  761. NODE_PTR temptree = NULL;
  762.    for(;list != NULL; list = list->next)
  763.       temptree = Insert(temptree, list->data, cmp);
  764.    return(temptree);
  765. }
  766. /*******************************/
  767. void FreeList(LIST_PTR list)
  768. {
  769. LIST_PTR temp;
  770.    while (list != NULL)
  771.    {
  772.       temp = list;
  773.       list = list->next;
  774.       free(temp);
  775.    }
  776. }
  777. /*******************************/
  778. void FreeTree(NODE_PTR tree)
  779. {
  780.    if (tree == NULL)
  781.       return;
  782.    FreeTree(tree->left);
  783.    FreeTree(tree->right);
  784. #ifndef NODETAIL
  785.    FreeList(tree->llist);
  786. #endif
  787.    free(tree);
  788.    return;
  789. }
  790. /*******************************/
  791. int SizeTree(NODE_PTR tree)
  792. {
  793.    if (tree == NULL)
  794.       return 0;
  795.    return (1 + SizeTree(tree->left) + SizeTree(tree->right));
  796. }
  797. /*******************************/
  798. /* Given a string and and len, left justify and fill with fill */
  799. void printl(char *str, int len, char fill)
  800. {
  801.    while (len > 0)
  802.    {
  803.       if (*str == '\n')
  804.          str++;
  805.       if (*str == '\0')
  806.      putc(fill, stdout);
  807.       else
  808.          putc(*str++, stdout);
  809.       len--;
  810.    }
  811. }
  812. /*******************************/
  813. /* Given a string center justify and fill with fill */
  814. void printc(char *str, char fill)
  815. {
  816. int i, len = strlen(str);
  817. int start = (Width + WIDTHSUB - len)/2;
  818.  
  819.    for(i = 0; i < start; i++)
  820.       putc(fill, stdout);
  821.  
  822.    fputs(str, stdout);
  823.  
  824.    if (fill != ' ')
  825.       for(i = start+len; i < Width + WIDTHSUB ; i++)
  826.          putc(fill, stdout);
  827.    putc('\n', stdout);
  828. }
  829. /*******************************/
  830. char *TypeNames(char type)
  831. {
  832.    switch(type)
  833.    {
  834.    case FILETYPE:
  835.       return("File");
  836.    case SOUNDTYPE:
  837.       return("Sound");
  838.    case BINARYTYPE:
  839.       return("Binary File");
  840.    case DIRTYPE:
  841.       return("Directory");
  842.    case MAILDIRTYPE:
  843.       return("Mail Directory");
  844.    case FTPTYPE:
  845.       return("FTP");
  846.    case RANGETYPE:
  847.       return("Range");
  848.    case SEARCHTYPE:
  849.       return("Search");
  850.    }
  851.    return("Unknown");
  852. }
  853. /*******************************/
  854. void PrintData(GOPHER_PTR data)
  855. {
  856.    if (data == STARTINFO)
  857.          printf("Data access%s:\n", AverageStrs[AverageStrPos]);
  858.    else if (data == ENDINFO)
  859.    {
  860.       printf("%d nodes accessed.  Average of %d connections per node.\n",
  861.          GNodes, GTotalConnects/GNodes);
  862.    }
  863.    else
  864.    {
  865.       printf("%c ",data->type);
  866.       printl(data->path, Width - 2, ' ');
  867.    }
  868. }
  869. /*******************************/
  870. void PrintTime(GOPHER_PTR data)
  871. {
  872.    if (data == STARTINFO)
  873.       printf("Times%s:\n", AverageStrs[AverageStrPos]);
  874.    else if (data == ENDINFO)
  875.    {
  876.       printf("Average of %d connections per hour.\n",
  877.          GTotalConnects/GNodes);
  878.    }
  879.    else
  880.    {
  881.       printf("%2d:00",data->hour);
  882.       printl("", Width - 5, ' ');
  883.    }
  884. }
  885. /*******************************/
  886. void PrintType(GOPHER_PTR data)
  887. {
  888. char *temp;
  889.    if (data == STARTINFO)
  890.       printf("Types%s:\n", AverageStrs[AverageStrPos]);
  891.    else if (data == ENDINFO)
  892.    {
  893.       printf("Average of %d connections per type.\n",
  894.          GTotalConnects/GNodes);
  895.    }
  896.    else
  897.    {
  898.       temp = TypeNames(data->type);
  899.       printl(temp, Width, ' ');
  900.    }
  901. }
  902. /*******************************/
  903. void PrintHost(GOPHER_PTR data)
  904. {
  905.    if (data == STARTINFO)
  906.       printf("Hosts%s:\n", AverageStrs[AverageStrPos]);
  907.    else if (data == ENDINFO)
  908.    {
  909.       printf("%d hosts connected.  Average of %d connections per host.\n",
  910.          GNodes, GTotalConnects/GNodes);
  911.    }
  912.    else
  913.    {
  914.       printl(data->hostname, Width, ' ');
  915.    }
  916.  
  917. }
  918. /*******************************/
  919. void PrintDay(GOPHER_PTR data)
  920. {
  921.    if (data == STARTINFO)
  922.       printf("Days%s:\n", AverageStrs[AverageStrPos]);
  923.    else if (data == ENDINFO)
  924.    {
  925.       printf("Average of %d connections per day of week.\n",
  926.          GTotalConnects/GNodes);
  927.    }
  928.    else
  929.    {
  930.       printl(Days[data->day-1], Width, ' ');
  931.    }
  932. }
  933. /*******************************/
  934. void PrintDate(GOPHER_PTR data)
  935. {
  936.    if (data == STARTINFO)
  937.       printf("Dates %s:\n", AverageStrs[AverageStrPos]);
  938.    else if (data == ENDINFO)
  939.    {
  940.       printf("Connections occured on %d different days.\n", GNodes);
  941.       printf("Average of %d connections per day.\n", GTotalConnects/GNodes);
  942.    }
  943.    else
  944.    {
  945.       
  946.       printf("%3s %3s %d", Days[data->day-1], Months[data->month-1], data->date);
  947.       if (data->date < 10)
  948.          printl("\0", Width - 9, ' ');
  949.       else
  950.          printl("\0", Width - 10, ' ');
  951.    }
  952. }
  953. /*******************************/
  954. void PlotData(FILE *rfp, int num, GOPHER_PTR data)
  955. {
  956.    if (data == STARTINFO)
  957.       INFOERROR("Plot of Data is not currently supported. Mail me ideas: %s\n", EMAIL);
  958. }
  959. /*******************************/
  960. void PlotType(FILE *rfp, int num, GOPHER_PTR data)
  961. {
  962. char *temp = NULL;
  963.    if (data == STARTINFO)
  964.    {
  965.       fprintf(rfp,"set xtics (");
  966.    }
  967.    else if (data == ENDINFO)
  968.    {
  969.       fprintf(rfp,"\"\" %d)\n", num);
  970.       fprintf(rfp,"set data style linespoints\n");
  971.       fprintf(rfp,"set tics out\n");
  972.       fprintf(rfp,"set grid\n");
  973.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  974.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  975.    }
  976.    else
  977.    {
  978.       temp = TypeNames(data->type);
  979.       fprintf(rfp,"\"%s\" %d,", temp, num);
  980.    }
  981.  
  982. }
  983. /*******************************/
  984. void PlotHost(FILE *rfp, int num, GOPHER_PTR data)
  985. {
  986.    if (data == STARTINFO)
  987.       INFOERROR("Plot of Hosts is not currently supported.  Mail me ideas: %s\n", EMAIL);
  988. }
  989. /*******************************/
  990. void PlotDay(FILE *rfp, int num, GOPHER_PTR data)
  991. {
  992.    if (data == STARTINFO)
  993.    {
  994.       fprintf(rfp,"set xtics (");
  995.    }
  996.    else if (data == ENDINFO)
  997.    {
  998.       fprintf(rfp,"\"\" %d)\n", num);
  999.       fprintf(rfp,"set data style linespoints\n");
  1000.       fprintf(rfp,"set tics out\n");
  1001.       fprintf(rfp,"set grid\n");
  1002.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1003.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1004.    }
  1005.    else
  1006.    {
  1007.       fprintf(rfp,"\"%s\" %d,",Days[data->day-1], num);
  1008.    }
  1009. }
  1010. /*******************************/
  1011. void PlotTime(FILE *rfp, int num, GOPHER_PTR data)
  1012. {
  1013.    if (data == STARTINFO)
  1014.    {
  1015.       fprintf(rfp,"set xtics (");
  1016.    }
  1017.    else if (data == ENDINFO)
  1018.    {
  1019.       fprintf(rfp,"\"\" %d)\n", num);
  1020.       fprintf(rfp,"set data style linespoints\n");
  1021.       fprintf(rfp,"set tics out\n");
  1022.       fprintf(rfp,"set grid\n");
  1023.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1024.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1025.    }
  1026.    else
  1027.    {
  1028.       fprintf(rfp,"\"%2d\" %d,",data->hour, num);
  1029.    }
  1030. }
  1031. /*******************************/
  1032. void PlotDate(FILE *rfp, int num, GOPHER_PTR data)
  1033. {
  1034.    if (data == STARTINFO)
  1035.    {
  1036.       fprintf(rfp,"set xtics (");
  1037.    }
  1038.    else if (data == ENDINFO)
  1039.    {
  1040.       fprintf(rfp,"\"\" %d)\n", num);
  1041.       fprintf(rfp,"set data style linespoints\n");
  1042.       fprintf(rfp,"set tics out\n");
  1043.       fprintf(rfp,"set grid\n");
  1044.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1045.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1046.    }
  1047.    else
  1048.    {
  1049.       if ((data->date == 1) || (data->date == 15) || (num == 1))
  1050.          fprintf(rfp,"\"%s/%d\" %d,",Months[data->month-1], data->date, num);
  1051.    }
  1052. }
  1053. #ifndef NODETAIL
  1054. /*******************************/
  1055. void DoDetail(NODE_PTR tree, byte DetailType)
  1056. {
  1057. NODE_PTR newtree;
  1058.    switch(DetailType)
  1059.    {
  1060.    case DATAINFO:
  1061.       newtree = ListToTree(tree->llist, DocsCmp);
  1062.       PrintInfo(newtree, PrintData, DocsCmp, DETAILINFO);
  1063.       break;
  1064.    case HOSTINFO:
  1065.       newtree = ListToTree(tree->llist, HostsCmp);
  1066.       PrintInfo(newtree, PrintHost, HostsCmp, DETAILINFO);
  1067.       break;
  1068.    case WEEKDAYINFO:
  1069.       newtree = ListToTree(tree->llist, DaysCmp);
  1070.       PrintInfo(newtree, PrintDay, DaysCmp, DETAILINFO);
  1071.       break;
  1072.    case MONTHDATEINFO:
  1073.       newtree = ListToTree(tree->llist, DatesCmp);
  1074.       PrintInfo(newtree, PrintDate, DatesCmp, DETAILINFO);
  1075.       break;
  1076.    case TYPEINFO:
  1077.       newtree = ListToTree(tree->llist, TypesCmp);
  1078.       PrintInfo(newtree, PrintType, TypesCmp, DETAILINFO);
  1079.       break;
  1080.    case TIMEINFO:
  1081.       newtree = ListToTree(tree->llist, TimesCmp);
  1082.       PrintInfo(newtree, PrintTime, TimesCmp, DETAILINFO);
  1083.       break;
  1084.    default:
  1085.       newtree = NULL;
  1086.       break;
  1087.    }
  1088.    FreeTree(newtree);
  1089. }
  1090. #endif
  1091. /*******************************/
  1092. void PrintInfo(NODE_PTR tree, void print(GOPHER_PTR), int cmp(GOPHER_PTR a, GOPHER_PTR b), byte DetailType)
  1093. {
  1094. LIST_PTR temp;
  1095. LIST_PTR ByNum;
  1096. int Nodes;
  1097.  
  1098.    GByNum = NULL; /* Init the vars for the TreeToList function */
  1099.    GNodes = 0;
  1100.    TreeToSList(tree);
  1101.  
  1102.    if (DetailType != DETAILINFO)
  1103.    { /* We are not printing Detail info now, so do headers */
  1104.       print(STARTINFO);
  1105.       printc("", '=');
  1106.    }
  1107.  
  1108.    ByNum = GByNum; /* Save off and clear the globals vars */
  1109.    Nodes = GNodes;
  1110.    GByNum = NULL;
  1111.  
  1112.    temp = ByNum;
  1113.    while (temp != NULL)
  1114.    {
  1115. #ifndef NODETAIL
  1116.       if (DetailType == DETAILINFO)
  1117.          printf("   ");
  1118. #endif
  1119.  
  1120.       print(temp->data);
  1121.       printf(" %4d (%2.2f%%)\n", (int)(temp->hits/AverageDiv), (float)temp->hits*100.0/TotalConnects);
  1122. #ifndef NODETAIL
  1123.       if ((DetailType != NOINFO) && (DetailType != DETAILINFO))
  1124.          DoDetail(Find(tree, temp->data, cmp), DetailType);
  1125.      /* Don't generate Detail for NOINFO or if we are already doing detail */
  1126. #endif
  1127.       temp = temp->next;
  1128.    }
  1129.    if (DetailType != DETAILINFO)
  1130.    { /* We are not printing Detail info now, so do footers */
  1131.       printf("\n");
  1132.       GNodes = Nodes; /* Restore, incase detail messed it up */
  1133.       GTotalConnects = TotalConnects/AverageDiv;
  1134.       print(ENDINFO);
  1135.    }
  1136.    printf("\n");
  1137.    FreeList(ByNum);
  1138. }
  1139. /*******************************/
  1140. void PlotInfo(NODE_PTR tree, void plot(FILE *, int, GOPHER_PTR))
  1141. {
  1142. LIST_PTR temp;
  1143. FILE *rfp, *dfp;
  1144. char *fn;
  1145. int points = 1;
  1146.  
  1147.    fn = (char *)malloc(strlen(base) + 5);
  1148.    sprintf(fn,"%s.run", base);
  1149.    if (NULL == (rfp = fopen(fn, "w")))
  1150.    {
  1151.       fprintf(stderr, "Could not open file \"%s\" for plot run\n", fn);
  1152.       free(fn);
  1153.       return;
  1154.    }
  1155.    sprintf(fn,"%s.dat", base);
  1156.    if (NULL == (dfp = fopen(fn, "w")))
  1157.    {
  1158.       fprintf(stderr, "Could not open file \"%s\" for plot data\n", fn);
  1159.       free(fn);
  1160.       return;
  1161.    }
  1162.    free(fn);
  1163.  
  1164.    plot(rfp, 0, STARTINFO);
  1165.    GByNum = NULL; /* Init the vars for the TreeToList function */
  1166.    GByNumMax = 0;
  1167.    GByNumMin = 36000;
  1168.    TreeToUList(tree);
  1169.  
  1170.    temp = GByNum;
  1171.    while (temp != NULL)
  1172.    {
  1173.       plot(rfp, points, temp->data);
  1174.       fprintf(dfp, "%d %d\n", points++, temp->hits);
  1175.       temp = temp->next;
  1176.    }
  1177.    plot(rfp, points, ENDINFO);
  1178.    printf("\n");
  1179.    FreeList(GByNum);
  1180.    fclose(rfp);
  1181.    fclose(dfp);
  1182. }
  1183. /*******************************/
  1184. void GraphInfo(NODE_PTR tree, void graph(GOPHER_PTR), int strwidth)
  1185. {
  1186. LIST_PTR temp;
  1187. LIST_PTR ByNum;
  1188. int Nodes;
  1189. int i, max, oldwidth = Width, MyWidth;
  1190.  
  1191.  
  1192.    GByNum = NULL; /* Init the vars for the TreeToList function */
  1193.    GNodes = 0;
  1194.    GByNumMax = 0;
  1195.    GByNumMin = 36000;
  1196.    TreeToUList(tree);
  1197.  
  1198.    ByNum = GByNum; /* Save off and clear the globals vars */
  1199.    Nodes = GNodes;
  1200.    GByNum = NULL;
  1201.  
  1202.    graph(STARTINFO);
  1203.    printc("", '=');
  1204.  
  1205.    /* Do the width stuff after printc, yes its a major hack. :) */
  1206.    MyWidth = Width - strwidth;
  1207.    Width = strwidth;
  1208.  
  1209.    temp = ByNum;
  1210.    while (temp != NULL)
  1211.    {
  1212.       graph(temp->data);
  1213.       printf(" %4d (%5.2f%%) ", (int)(temp->hits/AverageDiv), (float)temp->hits*100.0/TotalConnects);
  1214.       max = (int)((temp->hits/AverageDiv) * (MyWidth/(float)GByNumMax));
  1215.       for(i=0; i < max; i++)
  1216.      printf("#");
  1217.       printf("\n");
  1218.       temp = temp->next;
  1219.    }
  1220.    GNodes = Nodes; /* Restore */
  1221.    GTotalConnects = TotalConnects/AverageDiv;
  1222.    printf("\n");
  1223.    graph(ENDINFO);
  1224.    printf("\n");
  1225.    FreeList(ByNum);
  1226.    Width=oldwidth;
  1227. }
  1228. /*******************************/
  1229. void PrintErrorInfo(void)
  1230. {
  1231. ELIST_PTR temp = cruft;
  1232.  
  1233.    printf("Exception/Problem Report\n");
  1234.    printf("NOTE: THESE ENTRIES MAY DENOTE A SERVER PROBLEM. THEY SHOULD BE LOOKED OVER!\n");
  1235.    printc("", '=');
  1236.    while (temp != NULL)
  1237.    {
  1238.       printf(temp->data);
  1239.       temp = temp->next;
  1240.    }
  1241.    printf("\n");
  1242. }
  1243.  
  1244. /*******************************/
  1245.  
  1246. void PrintHelp(FILE *fp, int needreturn)
  1247. {
  1248.   fprintf(fp,"\nUsage: glog [<REPORTTYPE> | -%c | -h] ", ERRORINFO);
  1249. #ifdef VMS
  1250.    fprintf(fp,"[OPTIONS] -i INFILENAME [-o OUTFILENAME]\n");
  1251. #else
  1252.    fprintf(fp,"[OPTIONS] [-i INFILENAME] [-o OUTFILENAME]\n");
  1253. #endif
  1254.    fprintf(fp,"  -h prints this help information\n");
  1255.    fprintf(fp,"  -%c displays an ERROR LOG\n", ERRORINFO);
  1256.    fprintf(fp,"  -i INFILENAME specifies the input file (your gopher logfile)\n");
  1257. #ifndef VMS
  1258.    fprintf(fp,"     stdin is expected to be your gopher logfile, unless -i is used\n");
  1259. #endif
  1260.    fprintf(fp,"  -o OUTFILENAME specifies the output file.  If an output file\n");
  1261.    fprintf(fp,"     is not specified, output will be to the screen.\n\n");
  1262.    
  1263.    fprintf(fp,"REPORTTYPE is:\n");
  1264.    fprintf(fp,"   -g<SORTTYPE> for a histogram\n");
  1265.    fprintf(fp,"   -p<SORTTYPE> to output plot data (gnuplot required to display plot)\n");
  1266.    fprintf(fp,"   -r<SORTTYPE>[<SORTTYPE>] for a report [with detail]\n\n");
  1267.    
  1268. #ifdef VMS
  1269.    fprintf(fp,"SORTTYPE is: (must be inclosed in quotes)\n");
  1270. #else
  1271.    fprintf(fp,"SORTTYPE is:\n");
  1272. #endif
  1273.    fprintf(fp,"   %c = Host Names       %c = Day of Week \n",
  1274.       HOSTINFO, WEEKDAYINFO);
  1275.    fprintf(fp,"   %c = Document Names   %c = Month/Day\n",
  1276.     DATAINFO, MONTHDATEINFO);
  1277.    fprintf(fp,"   %c = Type             %c = Time\n\n", TYPEINFO, TIMEINFO);
  1278.    if (needreturn)
  1279.    {
  1280.       fprintf(stderr, "PRESS [ENTER] TO CONTINUE\n");
  1281.       fflush(stderr);
  1282.       getc(stdin);
  1283.    }
  1284.    fprintf(fp,"OPTIONS are\n");
  1285.    fprintf(fp,"   [-b <beginning month #>] [-e <ending month #>]\n");
  1286.    fprintf(fp,"   [-w <width of report>] [-f <base filename for plot>]\n");
  1287.    fprintf(fp,"   [-a<AVERAGETYPE>]\n\n");
  1288. #ifdef VMS
  1289.    fprintf(fp,"AVERAGETYPE is: (must be inclosed in quotes)\n");
  1290. #else
  1291.    fprintf(fp,"AVERAGETYPE is:\n");
  1292. #endif
  1293.    fprintf(fp,"   E = Everything       Y = Per Year\n");
  1294.    fprintf(fp,"   M = Per Month        W = Per Week\n");
  1295.    fprintf(fp,"   D = Per Day          H = Per Hour\n\n");
  1296.    fprintf(fp,"WARNING:  All arguments are evaluated from left to right,\n");
  1297.    fprintf(fp,"   except for -i, -o, -b, and -e.  Which are evaluated only at\n");
  1298.    fprintf(fp,"   the start of execution.  \n\n");
  1299.  
  1300. #ifdef VMS
  1301.    fprintf(fp,"Example: glog -w 132 -g\"I\" -p\"T\" -a\"Y\" -r\"H\" -i gopher.log\n");
  1302.    fprintf(fp,"   (Remember to enclose capital letters in double quotes!)\n");
  1303. #else
  1304.    fprintf(fp,"Example: glog34 -w 132 -gI -pT -aY -rH < gopher.log | more\n");
  1305. #endif /* VMS */
  1306.    fprintf(fp,"Width of 132, give a Time Histogram, a Type Plot and a Host Report.\n");
  1307. #ifdef VMS
  1308.    fprintf(fp,"Input from gopher.log, output to the screen except for the plot data.\n");
  1309. #else
  1310.    fprintf(fp,"Input from gopher.log output to stdout, except for the plot\n");
  1311. #endif /* VMS */
  1312.    fprintf(fp, "The report is made to look like the logfile contained a years worth of data.\n\n");
  1313.  
  1314.    fflush(fp);
  1315. }
  1316.  
  1317. /*******************************/
  1318. void PrintHdr()
  1319. {
  1320. char center[80];
  1321.  
  1322.    printf("\n");
  1323.    printc(GLOG_VERSION,' ');
  1324.    fflush(stdout);
  1325.  
  1326.    sprintf(center, "%s to %s", start_date, stop_date);
  1327.    printc(center, ' ');
  1328.    if (AverageStrPos == 0)
  1329.    {
  1330.       sprintf(center, "%d connections", TotalConnects);
  1331.       printc(center, ' ');
  1332.    }
  1333.    else
  1334.    {
  1335.       sprintf(center, "%d real connections", TotalConnects);
  1336.       printc(center, ' ');
  1337.       printf("\n");
  1338.       sprintf(center, "%d estimated connections%s", 
  1339.           (int)(TotalConnects/AverageDiv), AverageStrs[AverageStrPos]);
  1340.       printc(center, ' ');
  1341.    }
  1342.    printf("\n\n");
  1343.    fflush(stdout);
  1344.    fflush(stderr);
  1345. }
  1346. /*******************************/
  1347. /* Yes I KNOW main is more than 1 page */
  1348. int main(int argc, char **argv)
  1349. int i;
  1350. int NumOfDays;
  1351.  
  1352. #ifdef THINK_C
  1353.     argc = ccommand(&argv); 
  1354. #endif   
  1355.  
  1356.    if  (1 == argc || argv[1][1] =='h')
  1357.    {
  1358.       PrintHelp(stdout,1); /* Clueless */
  1359.       exit(-1);
  1360.    }
  1361.  
  1362.    i = 1;
  1363. /* We must go through the arguments twice.  Once for the the arguments that
  1364.  * can only be set once.  I could add argument checking here, but why bother
  1365.  */
  1366.    while (i<argc)
  1367.    {
  1368.       if (argv[i][0] == '-')
  1369.       switch (argv[i][1])
  1370.       {
  1371.       case 'b':
  1372.          if (i<argc-1)
  1373.         mbegin = atoi(argv[++i]);
  1374.      else
  1375.         HELPERROR("\nexpected beginning month argument for -b\n");
  1376.          break;
  1377.       case 'e':
  1378.          if (i<argc-1)
  1379.         mend = atoi(argv[++i]);
  1380.      else
  1381.         HELPERROR("\nexpected ending month argument for -e\n");
  1382.          break;
  1383.       case 'i':
  1384.          if (i<argc-1)
  1385.         in_file_name = argv[++i];
  1386.      else
  1387.         HELPERROR("\nexpected input file name for -i\n");
  1388.          break;
  1389.       case 'o':
  1390.          if (i<argc-1)
  1391.          {
  1392.             i++;
  1393.             if (freopen(argv[i], "w", stdout) == NULL)
  1394.                INFOERROR("Unable to open requested output file %s\n", argv[i]);
  1395.          }
  1396.      else
  1397.         HELPERROR("\nexpected output file name for -o\n");
  1398.          break;
  1399.       }
  1400.       i++;
  1401.    }
  1402. #ifdef VMS
  1403.       /* VMS barfs on stdin as input so FORCE filename */
  1404.       if (in_file_name == NULL)
  1405.          EXITERROR("No input file name on command line%\n");
  1406. #endif /* VMS */
  1407.  
  1408.    GatherInfo();
  1409.  
  1410.    if (TotalConnects == 0)
  1411.       EXITERROR("\nNo [matching] data to process\n");
  1412.  
  1413.    NumOfDays = SizeTree(dates);
  1414.    fflush(stdout);
  1415.    fflush(stderr);
  1416.  
  1417.    i = 1;
  1418.    /* Now go through them again and actually do them, from left to right */
  1419.    while (i<argc)
  1420.    {
  1421.       switch (argv[i][1])
  1422.       {
  1423.       case 'a': /* average changes */
  1424.          switch(argv[i][2])
  1425.      {
  1426.      case 'E':
  1427.         AverageStrPos = 0;
  1428.         AverageDiv = 1.0;
  1429.         break;
  1430.      case 'Y':
  1431.         AverageStrPos = 1;
  1432.         AverageDiv = NumOfDays/365.0;
  1433.         break;
  1434.      case 'M':
  1435.         AverageStrPos = 2;
  1436.         AverageDiv = NumOfDays/(365.0/12.0);
  1437.         break;
  1438.      case 'W':
  1439.         AverageStrPos = 3;
  1440.         AverageDiv = NumOfDays/7.0;
  1441.         break;
  1442.      case 'D':
  1443.         AverageStrPos = 4;
  1444.         AverageDiv = NumOfDays;
  1445.         break;
  1446.      case 'H':
  1447.         AverageStrPos = 5;
  1448.         AverageDiv = NumOfDays*24.0;
  1449.         break;
  1450.      }
  1451.      break;
  1452.       case ERRORINFO:
  1453.      PrintErrorInfo();
  1454.      break;
  1455.       case 'r': /*custom reports*/
  1456.      PrintHdr();
  1457.          switch(argv[i][2])
  1458.      {
  1459.          case DATAINFO:
  1460.         PrintInfo(docs, PrintData, DocsCmp, argv[i][3]);
  1461.         break;
  1462.          case TYPEINFO:
  1463.         PrintInfo(types, PrintType, TypesCmp, argv[i][3]);
  1464.         break;
  1465.          case TIMEINFO:
  1466.         PrintInfo(times, PrintTime, TimesCmp, argv[i][3]);
  1467.         break;
  1468.          case WEEKDAYINFO:
  1469.             PrintInfo(dais, PrintDay, DaysCmp, argv[i][3]);
  1470.         break;
  1471.          case MONTHDATEINFO:
  1472.         PrintInfo(dates, PrintDate, DatesCmp, argv[i][3]);
  1473.         break;
  1474.          case HOSTINFO:
  1475.            PrintInfo(hosts, PrintHost, HostsCmp, argv[i][3]);
  1476.         break;
  1477.      default:
  1478.         HELPERROR("expected SORTTYPE argument for -r\n");
  1479.         break;
  1480.      }
  1481.          printf(" \n");
  1482.      break;
  1483.       case 'p': /*custom plots*/
  1484.          switch (argv[i][2])
  1485.      {
  1486.          case DATAINFO:
  1487.             PlotInfo(docs, PlotData);
  1488.         break;
  1489.          case TYPEINFO:
  1490.         PlotInfo(types, PlotType);
  1491.         break;
  1492.          case TIMEINFO:
  1493.         PlotInfo(times, PlotTime);
  1494.         break;
  1495.          case WEEKDAYINFO:
  1496.         PlotInfo(dais, PlotDay);
  1497.         break;
  1498.          case MONTHDATEINFO:
  1499.         PlotInfo(dates, PlotDate);
  1500.         break;
  1501.          case HOSTINFO:
  1502.         PlotInfo(hosts, PlotHost);
  1503.         break;
  1504.      default:
  1505.         HELPERROR("expected SORTTYPE argument for -p\n");
  1506.         break;
  1507.      }
  1508.       break;
  1509.       case 'g': /*custom graphs*/
  1510.      PrintHdr();
  1511.          switch (argv[i][2])
  1512.      {
  1513.          case DATAINFO:
  1514.             GraphInfo(docs, PrintData, 40);
  1515.         break;
  1516.          case TYPEINFO:
  1517.         GraphInfo(types, PrintType, 15);
  1518.         break;
  1519.          case TIMEINFO:
  1520.         GraphInfo(times, PrintTime,15);
  1521.         break;
  1522.          case WEEKDAYINFO:
  1523.         GraphInfo(dais, PrintDay, 15);
  1524.         break;
  1525.          case MONTHDATEINFO:
  1526.         GraphInfo(dates, PrintDate,15);
  1527.         break;
  1528.          case HOSTINFO:
  1529.         GraphInfo(hosts, PrintHost, 35);
  1530.         break;
  1531.      default:
  1532.         HELPERROR("expected SORTTYPE argument for -g\n");
  1533.         break;
  1534.      }
  1535.          printf(" \n");
  1536.       break;
  1537.       case 'w':
  1538.          if (i<argc-1)
  1539.         Width = atoi(argv[++i]) - WIDTHSUB;
  1540.      else
  1541.         HELPERROR("expected width argument for -w\n");
  1542.      break;
  1543.       case 'f':
  1544.          if (i<argc-1)
  1545.         base = argv[++i];
  1546.      else
  1547.         HELPERROR("expected filename argument for -f\n");
  1548.      break;
  1549.       case 'o':
  1550.       case 'i':
  1551.       case 'b':
  1552.       case 'e':
  1553.          i++;   /* Skip over their arguments */
  1554.          break; /* These are before GatherInfo arguments */
  1555.       case '?':
  1556.       default:
  1557.          fprintf(stderr, "Unknown option \"%c\" Use -h for help\n", argv[i][1]);
  1558.      break;
  1559.       } /*switch*/
  1560.     
  1561.       i++; /* next arg...*/
  1562.         
  1563.    } /*while*/
  1564.   
  1565.    exit(0);
  1566. }
  1567.  
  1568.  
  1569.