home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / analg211.zip / sscanf.c < prev    next >
C/C++ Source or Header  |  1997-03-14  |  34KB  |  1,435 lines

  1. /*** analog 2.1 ***/
  2. /* Please read Readme.html, or http://www.statslab.cam.ac.uk/~sret1/analog/  */
  3.  
  4. /*** sscanf.c; functions to replace sscanf(), which is far too slow, in
  5.      certain specific cases ***/
  6.  
  7. #include "analhea2.h"
  8.  
  9. /*** Now the scanning routines ***/
  10.  
  11. int sscanf_date(char *inputline, int *date, int *monthno, int *year, int *hr,
  12.         int *min)
  13. {    /* scanning date from common/agent/referrer log */
  14.  
  15.   register char *cin = inputline;
  16.   char *cout;
  17.   char month[4];
  18.   int i;
  19.  
  20.   if (!isdigit(*cin))
  21.     return(0);
  22.   else
  23.     *date = 10 * (*cin - '0');
  24.   cin++;
  25.   if (!isdigit(*cin))
  26.     return(0);
  27.   else
  28.     *date += (*cin - '0');
  29.  
  30.   /* read in month */
  31.   cin++;
  32.   if (*cin != '/')
  33.     return(1);
  34.   cin++;
  35.   cout = month;
  36.   for (i = 0; i < 3 && *cin != '\0'; i++) {
  37.     *cout = *cin;
  38.     cout++;
  39.     cin++;
  40.   }
  41.   if (*cin == '\0')
  42.     return(1);
  43.   *cout = '\0';
  44.   if ((*monthno = strtomonth(month)) == ERR)
  45.     return(1);
  46.  
  47.   /* read in year */
  48.   if (*cin != '/')
  49.     return(2);
  50.   cin++;
  51.   if (!isdigit(*cin))
  52.     return(2);
  53.   else
  54.     *year = 1000 * (*cin - '0');
  55.   cin++;
  56.   if (!isdigit(*cin))
  57.     return(2);
  58.   else
  59.     *year += 100 * (*cin - '0');
  60.   cin++;
  61.   if (!isdigit(*cin)) {
  62.     if (*cin != ':')
  63.       return(2);
  64.     else {     /* allow two digit years for Spyglass server */
  65.       *year /= 100;
  66.       *year += 1900;
  67.       if (*year < 1970)
  68.     *year += 100;
  69.     }
  70.   }
  71.   else {
  72.     *year += 10 * (*cin - '0');
  73.     cin++;
  74.     if (!isdigit(*cin))
  75.       return(2);
  76.     else
  77.       *year += (*cin - '0');
  78.     cin++;
  79.     if (*cin != ':')
  80.       return(3);
  81.   }
  82.  
  83.   /* read in hour */
  84.   cin++;
  85.   if (!isdigit(*cin))
  86.     return(3);
  87.   else
  88.     *hr = 10 * (*cin - '0');
  89.   cin++;
  90.   if (!isdigit(*cin))
  91.     return(3);
  92.   else
  93.     *hr += (*cin - '0');
  94.  
  95.   /* read in minute */
  96.   cin++;
  97.   if (*cin != ':')
  98.     return(4);
  99.   cin++;
  100.   if (!isdigit(*cin))
  101.     return(4);
  102.   else
  103.     *min = 10 * (*cin - '0');
  104.   cin++;
  105.   if (!isdigit(*cin))
  106.     return(4);
  107.   else
  108.     *min += (*cin - '0');
  109.  
  110.   /* don't read in second, but check it for correct form */
  111.   cin++;
  112.   if (*cin != ':')
  113.     return(4);
  114.   cin++;
  115.   if (!isdigit(*cin))
  116.     return(4);
  117.   cin++;
  118.   if (!isdigit(*cin))
  119.     return(4);
  120.  
  121.   return(5);
  122. }
  123.  
  124. int sscanf_olddate(char *inputline, int *date, int *monthno, int *year,
  125.            int *hr, int *min)
  126. {    /* the same thing for NCSA old-style and error logs */
  127.   register char *cin = inputline;
  128.   char *cout;
  129.   char month[4];
  130.   int i;
  131.  
  132.   /* ignore day of week, so scan until next ' ' */
  133.   for (cin++; *cin != ' ' && *cin != '\0'; cin++)
  134.     ;
  135.   if (*cin == '\0')
  136.     return(0);
  137.  
  138.   /* read in month */
  139.   cin++;
  140.   cout = month;
  141.   for (i = 0; i < 3 && *cin != '\0'; i++) {
  142.     *cout = *cin;
  143.     cout++;
  144.     cin++;
  145.   }
  146.   if (*cin == '\0')
  147.     return(0);
  148.   *cout = '\0';
  149.   if ((*monthno = strtomonth(month)) == ERR)
  150.     return(1);
  151.  
  152.   /* read in date */
  153.   if (*cin != ' ')
  154.     return(1);
  155.   cin++;
  156.   if (!isdigit(*cin) && *cin != ' ')
  157.     return(1);
  158.   else if (*cin != ' ')
  159.     *date = 10 * (*cin - '0');
  160.   else
  161.     *date = 0;
  162.   cin++;
  163.   if (!isdigit(*cin))
  164.     return(1);
  165.   else
  166.     *date += (*cin - '0');
  167.  
  168.   /* read in hour */
  169.   cin++;
  170.   if (*cin != ' ')
  171.     return(2);
  172.   cin++;
  173.   if (!isdigit(*cin))
  174.     return(2);
  175.   else
  176.     *hr = 10 * (*cin - '0');
  177.   cin++;
  178.   if (!isdigit(*cin))
  179.     return(2);
  180.   else
  181.     *hr += (*cin - '0');
  182.  
  183.   /* read in minute */
  184.   cin++;
  185.   if (*cin != ':')
  186.     return(3);
  187.   cin++;
  188.   if (!isdigit(*cin))
  189.     return(3);
  190.   else
  191.     *min = 10 * (*cin - '0');
  192.   cin++;
  193.   if (!isdigit(*cin))
  194.     return(3);
  195.   else
  196.     *min += (*cin - '0');
  197.   
  198.   /* ignore second (but check format) */
  199.   cin++;
  200.   if (*cin != ':')
  201.     return(4);
  202.   cin++;
  203.   if (!isdigit(*cin))
  204.     return(4);
  205.   cin++;
  206.   if (!isdigit(*cin))
  207.     return(4);
  208.   cin++;
  209.   if (*cin != ' ')
  210.     return(4);
  211.  
  212.   /* read year */
  213.   cin++;
  214.   if (!isdigit(*cin))
  215.     return(4);
  216.   else
  217.     *year = 1000 * (*cin - '0');
  218.   cin++;
  219.   if (!isdigit(*cin))
  220.     return(4);
  221.   else
  222.     *year += 100 * (*cin - '0');
  223.   cin++;
  224.   if (!isdigit(*cin))
  225.     return(4);
  226.   else
  227.     *year += 10 * (*cin - '0');
  228.   cin++;
  229.   if (!isdigit(*cin))
  230.     return(4);
  231.   else
  232.     *year += (*cin - '0');
  233.   return(5);
  234. }
  235.  
  236. int sscanf_common(char *inputline, char hostn[MAXSTRINGLENGTH], int *date,
  237.           int *monthno, int *year, int *hr, int *min,
  238.           char filename[MAXSTRINGLENGTH],
  239.           char referrer[MAXSTRINGLENGTH], char agent[MAXSTRINGLENGTH],
  240.           int *code, char bytestr[16], size_t preflength)
  241. {     /* scanning 'common' format logfile entries */
  242.   extern flag bq, Bq, fq, case_insensitive;
  243.   extern struct include *noexpandhead, *refexpandhead;
  244.  
  245.   register char *cin = inputline;      /* the character we are reading */
  246.   register char *cout;                 /* where we are putting it */
  247.   int i;
  248.  
  249.   /* read in hostname */
  250.   i = 0;
  251.   for (cout = hostn; *cin != ' ' && *cin != '\0' && i < MAXSTRINGLENGTH - 1;
  252.        cin++) { 
  253.     *cout = *cin;
  254.     cout++;
  255.     i++;
  256.   }
  257.   if (*cin != ' ')
  258.     return(0);
  259.   *cout = '\0';
  260.  
  261.   /* scan until next '[' */
  262.   for (cin++; *cin != '[' && *cin != '\0'; cin++)
  263.     ;
  264.   if (*cin == '\0')
  265.     return(1);
  266.  
  267.   /* read in date */
  268.   cin++;
  269.   if (sscanf_date(cin, date, monthno, year, hr, min) < 5)
  270.     return(1);
  271.   else
  272.     cin += 20;
  273.   
  274.   /* ignore timezone; so scan to next '"' */
  275.   for ( ; *cin != '"' && *cin != '\0'; cin++)
  276.     ;
  277.   if (*cin == '\0')
  278.     return(6);
  279.  
  280.   /* ignore method; so read to next ' ' */
  281.   for (cin++; *cin != ' ' && *cin != '\0'; cin++)
  282.     ;
  283.   if (*cin == '\0')
  284.     return(6);
  285.  
  286.   /* read in filename */
  287.   cin++;
  288.   i = 0;
  289.   for (cout = filename; *cin != ' ' && *cin != '\0' && *cin != '"' &&
  290.        *cin != '?' && i < MAXSTRINGLENGTH - 1 - preflength; cin++) {
  291.     *cout = *cin;
  292.     cout++;
  293.     i++;
  294.   }
  295.   *cout = '\0';
  296.   if (*cin == '?') {
  297.     if (case_insensitive)
  298.       strtolower(filename);   /* if no question mark, strtolower later */
  299.     if (!included(filename, UNSET, noexpandhead)) {
  300.       for ( ; *cin != ' ' && *cin != '\0' && *cin != '"' &&  /* read in args */
  301.        i < MAXSTRINGLENGTH - 1 - preflength; cin++) {
  302.     *cout = *cin;
  303.     cout++;
  304.     i++;
  305.       }
  306.       *cout = '\0';
  307.     }
  308.   }
  309.   if (*cin != ' ' && *cin != '"' && *cin != '?')
  310.     return(6);
  311.  
  312.   /* scan to next " */
  313.   for ( ; *cin != '"' && *cin != '\0' ; cin++)
  314.     ;
  315.   if (*cin == '\0')
  316.     return(7);
  317.  
  318.   /* read in return code; always 3 digits, or a - (successes; call them 299) */
  319.   cin++;
  320.   if (*cin != ' ')
  321.     return(7);
  322.   cin++;
  323.   if (!isdigit(*cin))
  324.     if (*cin == '-' && *(cin + 1) == ' ')
  325.       *code = 299;
  326.     else
  327.       return(7);
  328.   else {
  329.     *code = 100 * (*cin - '0');
  330.     cin++;
  331.     if (!isdigit(*cin))
  332.       return(7);
  333.     else
  334.       *code += 10 * (*cin - '0');
  335.     cin++;
  336.     if (!isdigit(*cin))
  337.       return(7);
  338.     else
  339.       *code += (*cin - '0');
  340.   }
  341.  
  342.   /* read in bytestr */
  343.   cin++;
  344.   if (*cin != ' ')
  345.     return (8);
  346.   cin++;
  347.   i = 0;
  348.   for (cout = bytestr; *cin != ' ' && *cin != '\n' && *cin != '\0' && i < 16;
  349.        cin++) {
  350.     *cout = *cin;
  351.     cout++;
  352.     i++;
  353.   }
  354.   *cout = '\0';
  355.  
  356.   /* Finally, try and read in referrer and agent of NCSA combined format */
  357.   if (*cin != ' ' || (!fq && !bq && !Bq))
  358.     return(9);
  359.   if (*(++cin) != '"')
  360.     return(9);
  361.   cin++;
  362.  
  363.   i = 0;
  364.   for (cout = referrer; *cin != '\0' && *cin != '"' && *cin != '?' &&
  365.        i < MAXSTRINGLENGTH - 1; cin++) {
  366.     *cout = *cin;
  367.     cout++;
  368.     i++;
  369.   }
  370.   *cout = '\0';                                    /* read in args */
  371.   if (*cin == '?' && included(referrer, UNSET, refexpandhead)) {
  372.     for ( ; *cin != '"' && *cin != '\0' && i < MAXSTRINGLENGTH - 1; cin++) {
  373.       *cout = *cin;
  374.       cout++;
  375.       i++;
  376.     }
  377.     *cout = '\0';
  378.   }
  379.   if (*cin == '?') {
  380.     while (*cin != '"' && *cin != '\0')
  381.       cin++;
  382.   }
  383.  
  384.   if (*cin != '"')
  385.     return(9);
  386.   if (*(++cin) != ' ')
  387.     return(10);
  388.   if (*(++cin) != '"')
  389.     return(10);
  390.   i = 0;
  391.   cin++;
  392.   for (cout = agent; *cin != '\0' && *cin != '"' && i < MAXSTRINGLENGTH - 1;
  393.        cin++) {
  394.     *cout = *cin;
  395.     cout++;
  396.     i++;
  397.   }
  398.   *cout = '\0';
  399.   if (*cin != '"')
  400.     return(10);
  401.   else
  402.     return(11);
  403.  
  404. }
  405.  
  406. int sscanf_ncsaold(char *inputline, char hostn[MAXSTRINGLENGTH], int *monthno,
  407.            int *date, int *hr, int *min, int *year,
  408.            char filename[MAXSTRINGLENGTH], size_t preflength)
  409. {    /* scanning NCSA old-style logfile entries */
  410.   extern struct include *noexpandhead;
  411.   extern flag case_insensitive;
  412.  
  413.   register char *cin = inputline;   /* the character we are reading */
  414.   register char *cout;              /* where we are putting it */
  415.   int i;
  416.  
  417.   /* read in hostname */
  418.   i = 0;
  419.   for (cout = hostn; *cin != ' ' && *cin != '\0' && i < MAXSTRINGLENGTH - 1;
  420.        cin++) { 
  421.     *cout = *cin;
  422.     cout++;
  423.     i++;
  424.   }
  425.   if (*cin != ' ')
  426.     return(0);
  427.   *cout = '\0';
  428.  
  429.   /* scan until next '[' */
  430.   for (cin++; *cin != '[' && *cin != '\0'; cin++)
  431.     ;
  432.   if (*cin == '\0')
  433.     return(1);
  434.  
  435.   /* read in date */
  436.   cin++;
  437.   if (sscanf_olddate(cin, date, monthno, year, hr, min) < 5)
  438.     return(1);
  439.   else
  440.     cin += 24;
  441.  
  442.   /* ignore method, so skip to second space */
  443.   for ( ; *cin != ' ' && *cin != '\0'; cin++)
  444.     ;
  445.   if (*cin == '\0')
  446.     return(6);
  447.  
  448.   for (cin++; *cin != ' ' && *cin != '\0'; cin++)
  449.     ;
  450.   if (*cin == '\0')
  451.     return(6);
  452.  
  453.   /* finally, read in the filename */
  454.   cin++;
  455.   i = 0;
  456.   for (cout = filename; *cin != ' ' && *cin != '\n' && *cin != '?' &&
  457.        *cin != '\0' && i < MAXSTRINGLENGTH - 1 - preflength; cin++) {
  458.       *cout = *cin;
  459.       cout++;
  460.     i++;
  461.   }
  462.   *cout = '\0';
  463.   if (*cin == '?') {
  464.     if (case_insensitive)
  465.       strtolower(filename);   /* if no question mark, strtolower later */
  466.     if (!included(filename, UNSET, noexpandhead)) {
  467.       for ( ; *cin != ' ' && *cin != '\0' && *cin != '"' &&  /* read in args */
  468.        i < MAXSTRINGLENGTH - 1 - preflength; cin++) {
  469.     *cout = *cin;
  470.     cout++;
  471.     i++;
  472.       }
  473.       *cout = '\0';
  474.     }
  475.   }
  476.   return (7);
  477.  
  478. }
  479.  
  480. int sscanf_domains(char *inputline, char string1[MAXSTRINGLENGTH],
  481.            char string2[MAXSTRINGLENGTH])
  482. {   /* scanning the domains file */
  483.   register char *cin = inputline;
  484.   register char *cout;
  485.   int i;
  486.  
  487.   /* run past any white space */
  488.   while (*cin == ' ' || *cin == '\t')
  489.     cin++;
  490.  
  491.   /* if no strings on this line, return 0 */
  492.   if (*cin == '#' || *cin == '\n' || *cin == '\0')
  493.     return(0);
  494.  
  495.   /* otherwise fill up string 1; coerce domains to lower case */
  496.   i = 0;
  497.   for (cout = string1; *cin != ' ' && *cin != '\t' && *cin != '#' &&
  498.        *cin != '\0' && *cin != '\n' && i < MAXSTRINGLENGTH - 1; cin++) {
  499.     *cout = tolower(*cin);
  500.     cout++;
  501.     i++;
  502.   }
  503.  
  504.   /* is that the end of the line (maybe after some white space)? */
  505.   if (*cin == '#' || *cin == '\0' || *cin == '\n' || i == MAXSTRINGLENGTH - 1)
  506.     return(1);
  507.  
  508.   *cout = '\0';
  509.   cin++;
  510.  
  511.   while (*cin == ' ' || *cin == '\t')
  512.     cin++;
  513.  
  514.   if (*cin == '#' || *cin == '\n' || *cin == '\0')
  515.     return(1);
  516.  
  517.   /* otherwise fill up string 2 */
  518.   for (cout = string2; *cin != '#' && *cin != '\n' && *cin != '\0' &&
  519.        i < MAXSTRINGLENGTH - 1; cin++) {
  520.     *cout = *cin;
  521.     cout++;
  522.     i++;
  523.   }
  524.  
  525.   *cout = '\0';
  526.   return(2);
  527.  
  528. }
  529.  
  530. int sscanf_config(char *inputline, char string1[MAXSTRINGLENGTH],
  531.           char string2[MAXSTRINGLENGTH],
  532.           char string3[MAXSTRINGLENGTH])
  533. {   /* scanning the config file */
  534.   register char *cin = inputline;
  535.   register char *cout;
  536.   int i;
  537.  
  538.   /* run past any white space */
  539.   while (*cin == ' ' || *cin == '\t')
  540.     cin++;
  541.  
  542.   /* if no strings on this line, return 0 */
  543.   if (*cin == '#' || *cin == '\n' || *cin == '\0')
  544.     return(0);
  545.  
  546.   /* otherwise fill up string 1; convert arguments to upper case */
  547.   i = 0;
  548.   for (cout = string1; *cin != ' ' && *cin != '\t' && *cin != '#' &&
  549.        *cin != '\0' && *cin != '\n' && i < MAXSTRINGLENGTH - 1; cin++) {
  550.     *cout = *cin;
  551.     cout++;
  552.     i++;
  553.   }
  554.  
  555.   *cout = '\0';
  556.  
  557.   /* is that the end of the line (maybe after some white space)? */
  558.   if (*cin == '#' || *cin == '\0' || *cin == '\n' || i == MAXSTRINGLENGTH - 1)
  559.     return(1);
  560.  
  561.   cin++;
  562.  
  563.   while (*cin == ' ' || *cin == '\t')
  564.     cin++;
  565.  
  566.   if (*cin == '#' || *cin == '\n' || *cin == '\0')
  567.     return(1);
  568.  
  569.   /* if string 2 starts with a quote mark, fill up until the next quote
  570.      mark. Otherwise, just fill until the next space */
  571.  
  572.   if (*cin == '\'') {
  573.     cin++;
  574.     for (cout = string2; *cin != '\n' && *cin != '\0' && *cin != '\'' &&
  575.      i < MAXSTRINGLENGTH - 1; cin++) {
  576.       *cout = *cin;
  577.       cout++;
  578.       i++;
  579.     }
  580.   }
  581.   else if (*cin == '"') {
  582.     cin++;
  583.     for (cout = string2; *cin != '\n' && *cin != '\0' && *cin != '"' &&
  584.      i < MAXSTRINGLENGTH - 1; cin++) {
  585.       *cout = *cin;
  586.       cout++;
  587.       i++;
  588.     }
  589.   }
  590.   else {
  591.     for (cout = string2; *cin != '#' && *cin != '\n' && *cin != '\0' &&
  592.      *cin != ' ' && *cin != '\t' && i < MAXSTRINGLENGTH - 1; cin++) {
  593.       *cout = *cin;
  594.       cout++;
  595.       i++;
  596.     }
  597.   }
  598.  
  599.   *cout = '\0';
  600.  
  601.   /* is that the end of the line (maybe after some white space)? */
  602.   if (*cin == '#' || *cin == '\0' || *cin == '\n' || i == MAXSTRINGLENGTH - 1)
  603.     return(2);
  604.  
  605.   cin++;
  606.  
  607.   while (*cin == ' ' || *cin == '\t')
  608.     cin++;
  609.  
  610.   if (*cin == '#' || *cin == '\n' || *cin == '\0')
  611.     return(2);
  612.  
  613.   /* otherwise fill up string 3 */
  614.   if (*cin == '\'') {
  615.     cin++;
  616.     for (cout = string3; *cin != '\n' && *cin != '\0' && *cin != '\'' &&
  617.      i < MAXSTRINGLENGTH - 1; cin++) {
  618.       *cout = *cin;
  619.       cout++;
  620.       i++;
  621.     }
  622.   }
  623.   else if (*cin == '"') {
  624.     cin++;
  625.     for (cout = string3; *cin != '\n' && *cin != '\0' && *cin != '"' &&
  626.      i < MAXSTRINGLENGTH - 1; cin++) {
  627.       *cout = *cin;
  628.       cout++;
  629.       i++;
  630.     }
  631.   }
  632.   else {
  633.     for (cout = string3; *cin != '#' && *cin != '\n' && *cin != '\0' &&
  634.      *cin != ' ' && *cin != '\t' && i < MAXSTRINGLENGTH - 1; cin++) {
  635.       *cout = *cin;
  636.       cout++;
  637.       i++;
  638.     }
  639.   }
  640.  
  641.   *cout = '\0';
  642.  
  643.   /* is that the end of the line (maybe after some white space)? */
  644.   if (*cin == '#' || *cin == '\0' || *cin == '\n' || i == MAXSTRINGLENGTH - 1)
  645.     return(3);
  646.  
  647.   cin++;
  648.  
  649.   while (*cin == ' ' || *cin == '\t')
  650.     cin++;
  651.  
  652.   if (*cin == '#' || *cin == '\n' || *cin == '\0')
  653.     return(3);
  654.  
  655.   return(4);   /* we don't ever want to read a fourth string; just know if
  656.           there is one for error checking */
  657.  
  658. }
  659.  
  660.  
  661. int sscanf_referrer(char *inputline, int *date, int *monthno, int *year,
  662.            int *hr, int *min, char from[MAXSTRINGLENGTH],
  663.            char to[MAXSTRINGLENGTH])
  664. {   /* scanning the referrer log */
  665.     /* The format is "[date] from -> to". The [date] is optional. */
  666.   extern struct include *refexpandhead, *noexpandhead;
  667.  
  668.   register char *cin = inputline;
  669.   register char *cout;
  670.   int i;
  671.  
  672.   /* scan the date */
  673.   if (*cin == '[') {
  674.     cin++;
  675.     if (sscanf_date(cin, date, monthno, year, hr, min) < 5)
  676.       return(0);
  677.     else
  678.       cin += 20;
  679.     if (*cin != ']')
  680.       return(5);
  681.     if (*(++cin) != ' ')
  682.       return(5);
  683.     cin++;
  684.   }
  685.   else
  686.     *date = 0;   /* as marker */
  687.  
  688.   /* now fill up the from string */
  689.  
  690.   i = 0;
  691.   for (cout = from; *cin != ' ' && *cin != '\0' && *cin != '?' &&
  692.        i < MAXSTRINGLENGTH - 1; cin++) {
  693.     *cout = *cin;
  694.     cout++;
  695.     i++;
  696.   }
  697.   *cout = '\0';                                    /* read in args */
  698.   if (*cin == '?' && included(from, UNSET, refexpandhead)) {
  699.     for ( ; *cin != ' ' && *cin != '\0' && i < MAXSTRINGLENGTH - 1; cin++) {
  700.       *cout = *cin;
  701.       cout++;
  702.       i++;
  703.     }
  704.     *cout = '\0';
  705.   }
  706.  
  707.   /* check at this point that the line syntax is ok */
  708.  
  709.   if (*cin == '?') {
  710.     while (*cin != ' ' && *cin != '\0')
  711.       cin++;
  712.   }
  713.  
  714.   if (*cin != ' ')
  715.     return(5);
  716.   cin++;
  717.   if (*cin != '-')
  718.     return(6);
  719.   cin++;
  720.   if (*cin != '>')
  721.     return(6);
  722.   cin++;
  723.   if (*cin != ' ')
  724.     return(6);
  725.   cin++;
  726.  
  727.   /* and the to string */
  728.  
  729.   i = 0;
  730.   for (cout = to; *cin != ' ' && *cin != '\0' && *cin != '\n'
  731.        && i < MAXSTRINGLENGTH - 1; cin++) {
  732.     *cout = *cin;
  733.     cout++;
  734.     i++;
  735.   }
  736.   *cout = '\0';
  737.   if (*cin == '?' && !included(to, UNSET, noexpandhead)) {  /* read in args */
  738.     for ( ; *cin != ' ' && *cin != '\0' && i < MAXSTRINGLENGTH - 1; cin++) {
  739.       *cout = *cin;
  740.       cout++;
  741.       i++;
  742.     }
  743.     *cout = '\0';
  744.   }
  745.  
  746.   if (*cin != '\n')
  747.     return(6);
  748.   else
  749.     return(7);
  750.  
  751. }
  752.  
  753. /* The function sscanf_webstar() is due to Jason Linhart (jason@summary.net) */
  754.  
  755. #ifdef WEBSTAR
  756. /*
  757.   Mac WebSTAR log files have special lines to tell use which fields are present
  758.   and in what order. Parse these so we know how to parse the other lines.
  759.   This also provides a reliable indicator of whether the log is a WebStar
  760.   log to begin with.
  761.  
  762.   A sample field name line:
  763.   !!LOG_FORMAT DATE TIME RESULT URL FROM TRANSFER_TIME BYTES_SENT USER
  764.   HOSTNAME REFERER
  765.  
  766.   The entries consist of TAB delimited fields. A sample line:
  767.   12/14/95        19:00:04        OK
  768.   :pages:Downloads.html           72      3176
  769.   indy1.indy.net. http://www.motu.com/
  770.  
  771.   We require the following fields at a minimum:
  772.   HOSTNAME, DATE, TIME, RESULT, URL, BYTES_SENT
  773.   We will also read AGENT and REFERER if present.
  774. */
  775.  
  776. enum FieldKinds {
  777.   INVALID = 0,
  778.   HOSTNAME_K,
  779.   DATE,
  780.   URL,
  781.   BYTES_SENT,
  782.   TIME,
  783.   RESULT,
  784.   AGENT,
  785.   FROM,
  786.   METHOD,
  787.   PATH_ARGS,
  788.   REFERER_K,
  789.   SEARCH_ARGS,
  790.   TRANSFER_TIME,
  791.   USER,
  792.   LAST_FIELD
  793. };
  794.  
  795. static const char *fnames[LAST_FIELD] = {
  796.   "!!LOG_FORMAT",
  797.   "HOSTNAME",
  798.   "DATE",
  799.   "URL",
  800.   "BYTES_SENT",
  801.   "TIME",
  802.   "RESULT",
  803.   "AGENT",
  804.   "FROM",
  805.   "METHOD",
  806.   "PATH_ARGS",
  807.   "REFERER",
  808.   "SEARCH_ARGS",
  809.   "TRANSFER_TIME",
  810.   "USER"
  811. };
  812.  
  813. #define FIELDS_MAX              20
  814.  
  815. static int fields[FIELDS_MAX];
  816. static int field_cnt = 0;
  817. long Mac_good_lines = 0, Mac_bad_lines = 0, Mac_tab_lines = 0;
  818.  
  819. extern flag warnq, anywarns;
  820.  
  821. int sscanf_webstar(char *inputline, char hostn[MAXSTRINGLENGTH], int *date,
  822.            int *monthno, int *year, int *hr, int *min,
  823.            char filename[MAXSTRINGLENGTH],
  824.            char referer[MAXSTRINGLENGTH], char agent[MAXSTRINGLENGTH],
  825.            int *code, char bytestr[16], size_t preflength)
  826. {     /* scanning 'WebStar' format logfile entries */
  827.   extern struct include *noexpandhead, *refexpandhead;
  828.   extern flag case_insensitive;
  829.   
  830.   register char *cin = inputline;      /* the character we are reading */
  831.   register char *cout;                 /* where we are putting it */
  832.   int i, field;
  833.   int flags;
  834.  
  835.   flags=0;
  836.   if (*cin=='!' && !memcmp((void *)cin,(void *)fnames[0],strlen(fnames[0]))) {
  837.     while (*cin && *cin!=' ') ++cin;
  838.     if (*cin) ++cin;
  839.     field_cnt=0;
  840.     while (*cin && field_cnt<FIELDS_MAX) {
  841.       for (i=1; i<LAST_FIELD && (memcmp((void *)cin,(void *)fnames[i],
  842.            strlen(fnames[i])) || (cin[strlen(fnames[i])]!=' ' &&
  843.            cin[strlen(fnames[i])]!='\n')); ++i) ;
  844.       if (i>=LAST_FIELD) i=INVALID;
  845.       fields[field_cnt]=i;
  846.       ++field_cnt;
  847.       flags|=(1<<i);
  848.       while (*cin && *cin!=' ') ++cin;
  849.       if (*cin) ++cin;
  850.     }
  851.     if (((flags>>1)&15)!=15 && warnq) {
  852.       fprintf(stderr,"WebStar logs require at least the following fields:\n");
  853.       fprintf(stderr,"  HOSTNAME, DATE, URL, and BYTES_SENT.\n");
  854.       anywarns = ON;
  855.     }
  856.     return(0);
  857.   }
  858.   else if (!field_cnt) {
  859.     ++Mac_bad_lines;
  860.     if (Mac_bad_lines<20) {
  861.       for (i=0; *cin; ++cin) if (*cin=='\t') ++i;
  862.       if (i>=3) ++Mac_tab_lines;
  863.     }
  864.     if (Mac_bad_lines==1 && i==5) {
  865.       field_cnt=6;
  866.       fields[0]=DATE;
  867.       fields[1]=TIME;
  868.       fields[2]=RESULT;
  869.       fields[3]=HOSTNAME_K;
  870.       fields[4]=URL;
  871.       fields[5]=BYTES_SENT;
  872.     }
  873.     else return(0);
  874.   }
  875.   *filename=0;
  876.   *referer=0;
  877.   *agent=0;
  878.   *hr=12;
  879.   *min=0;
  880.   *code=299;
  881.   for (field=0; field<field_cnt; ++field) {
  882.     if (*cin=='\0') break;
  883.     switch (fields[field]) {
  884.  
  885.     case HOSTNAME_K:  /* read in hostname */
  886.       i = 0;
  887.       for (cout = hostn; *cin != '\t' && *cin != '\0' &&
  888.        i < MAXSTRINGLENGTH - 1; cin++) {
  889.     *cout = *cin;
  890.     cout++;
  891.     i++;
  892.       }
  893.       if (cout>hostn && *(cout-1)=='.') --cout;
  894.       *cout = '\0';
  895.       flags|=(1<<(HOSTNAME_K-1));
  896.       break;
  897.     case DATE:              /* read in month, day, year */
  898.       /* read in month */
  899.       if (!isdigit(*cin)) return(0);
  900.       else *monthno = 10 * (*cin - '0');
  901.       cin++;
  902.       if (!isdigit(*cin)) return(0);
  903.       else *monthno += (*cin - '0') - 1;
  904.       cin++;
  905.  
  906.       if (*cin != '/') return(0);
  907.       cin++;
  908.  
  909.       /* read in day */
  910.       if (!isdigit(*cin)) return(0);
  911.       else *date = 10 * (*cin - '0');
  912.       cin++;
  913.       if (!isdigit(*cin)) return(0);
  914.       else *date += (*cin - '0');
  915.       cin++;
  916.  
  917.       if (*cin != '/') return(0);
  918.       cin++;
  919.  
  920.       /* read in year */
  921.       if (!isdigit(*cin)) return(0);
  922.       else *year = 1900 + 10 * (*cin - '0');
  923.       cin++;
  924.       if (!isdigit(*cin)) return(0);
  925.       else *year += (*cin - '0');
  926.       cin++;
  927.       if (*year < 1970) *year += 100;
  928.  
  929.       flags|=(1<<(DATE-1));
  930.       break;
  931.     case TIME:              /* read in hour, minute, skip sec */
  932.       /* read in hour */
  933.       if (!isdigit(*cin)) return(0);
  934.       else *hr = 10 * (*cin - '0');
  935.       cin++;
  936.       if (!isdigit(*cin)) return(0);
  937.       else *hr += (*cin - '0');
  938.       cin++;
  939.  
  940.       if (*cin != ':') return(0);
  941.       cin++;
  942.  
  943.       /* read in minute */
  944.       if (!isdigit(*cin)) return(0);
  945.       else *min = 10 * (*cin - '0');
  946.       cin++;
  947.       if (!isdigit(*cin)) return(0);
  948.       else *min += (*cin - '0');
  949.       cin++;
  950.  
  951.       if (*cin != ':') return(0);
  952.       cin++;
  953.  
  954.       /* don't read in second, but check it for correct form */
  955.       if (!isdigit(*cin)) return(0);
  956.       cin++;
  957.       if (!isdigit(*cin)) return(0);
  958.       cin++;
  959.  
  960.       flags|=(1<<(TIME-1));
  961.       break;
  962.     case URL:               /* read in filename */
  963.       i = 0;
  964.       for (cout = filename; *cin != '\t' && *cin != '\0' &&
  965.        i < MAXSTRINGLENGTH - 1 - preflength; cin++) {
  966.     if (*cin==':') *cout='/';
  967.     else *cout = *cin;
  968.     cout++;
  969.     i++;
  970.       }
  971.       *cout = '\0';
  972.       flags|=(1<<(URL-1));
  973.       break;
  974.     case SEARCH_ARGS:/* read in search args (part after '?') */
  975.       if (case_insensitive)
  976.     strtolower(filename);
  977.       if (*filename && !included(filename, UNSET, noexpandhead)) {
  978.     cout=filename+strlen(filename);        /* read in args */
  979.     *cout++ = '?';
  980.     for ( ; *cin != '\t' && *cin != '\0' &&
  981.          i < MAXSTRINGLENGTH - 1 - preflength; cin++) {
  982.       *cout = *cin;
  983.       cout++;
  984.       i++;
  985.     }
  986.     *cout = '\0';
  987.       }
  988.       break;
  989.     case BYTES_SENT:/* read in bytes sent */
  990.       i = 0;
  991.       for (cout = bytestr; *cin != '\t' && *cin != '\0' && i < 16; cin++) {
  992.     *cout = *cin;
  993.     cout++;
  994.       }
  995.       *cout = '\0';
  996.       flags|=(1<<(BYTES_SENT-1));
  997.       break;
  998.     case RESULT:    /* Translate WebStar result codes */
  999.       if (cin[0]=='O' && cin[1]=='K') *code=200;
  1000.       else if (cin[0]=='E' && cin[1]=='R' && cin[2]=='R' && cin[3]=='!')
  1001.     *code=404;
  1002.       else *code=299;
  1003.       flags|=(1<<(RESULT-1));
  1004.       break;
  1005.     case REFERER_K: /* read in refer */
  1006.       i = 0;
  1007.       for (cout = referer; *cin != '\0' && *cin != '\t' && *cin != '?' &&
  1008.        i < MAXSTRINGLENGTH - 1; cin++) {
  1009.     *cout = *cin;
  1010.     cout++;
  1011.     i++;
  1012.       }
  1013.       *cout = '\0';                                  /* read in args */
  1014.       if (*cin == '?' && included(referer, UNSET, refexpandhead)) {
  1015.     for ( ; *cin != '\t' && *cin != '\0' && i < MAXSTRINGLENGTH - 1;
  1016.          cin++) {
  1017.       *cout = *cin;
  1018.       cout++;
  1019.       i++;
  1020.     }
  1021.     *cout = '\0';
  1022.       }
  1023.       if (*cin == '?') {
  1024.     while (*cin != '\t' && *cin != '\0') cin++;
  1025.       }
  1026.       flags|=(1<<(REFERER_K-1));
  1027.       break;
  1028.     case AGENT:             /* read in the user agent */
  1029.       i = 0;
  1030.       for (cout = agent; *cin != '\0' && *cin != '\t' &&
  1031.        i < MAXSTRINGLENGTH - 1; cin++) {
  1032.     *cout = *cin;
  1033.     cout++;
  1034.     i++;
  1035.       }
  1036.       *cout = '\0';
  1037.       flags|=(1<<(AGENT-1));
  1038.       break;
  1039.     }
  1040.     while (*cin && *cin!='\t') ++cin;
  1041.     if (*cin) ++cin;
  1042.   }
  1043.   if ((flags&15)!=15) return(0);
  1044.   ++Mac_good_lines;
  1045.   return(11);
  1046. }
  1047. #endif
  1048.  
  1049. /* The function sscanf_netpresenz() and associated functions are due to 
  1050.    Nigel Perry (N.Perry@massey.ac.nz), September 1996 */
  1051.  
  1052. /* Parse NetPresenz 4.x format logs
  1053.  
  1054.    The format is:
  1055.  
  1056.        time\tdate\tIP\ttype\tsession\taction\tfile\t\r
  1057.        fullpath\r
  1058.        Referer: path\r
  1059.  
  1060.    Time may include am/pm. This code currently assumes date is dd/mm/yy,
  1061.    this is probably incorrect and Mac international routines should be used
  1062.    to parse both the time and date. 
  1063.  
  1064.    Type is one of HTTP, Gopher, NetPresenz ... (admin entries), or user id for
  1065.    FTP - the user id is an arbitrary string (users can type anything
  1066.    in!). Session is used only for FTP and gives the number of the current user.
  1067.  
  1068.    Action is one of get file, get dir, log in, log out, CGI, or error msg.
  1069.    The log in/out are for FTP.
  1070.  
  1071.    File is the short name, fullpath is the full (Mac) pathname, referer is the
  1072.    full URL as supplied. Lines two and three are optional.
  1073.  
  1074.    We will build an "agent" from the type & action fields, error from action
  1075.    field. Login, log out and NetPresenz admin lines will be ignored.
  1076.  
  1077.    There is no bytes sent field in the NetPresenz log, maybe this can be
  1078.    generated from the full path at some point...
  1079. */
  1080.  
  1081. #ifdef NETPRESENZ
  1082.  
  1083. static const MatchPairs entryTypes[] =
  1084. { { "HTTP", NP_HTTP },
  1085.     { "Gopher", NP_GOPHER },
  1086.     { NULL, NP_FTP }
  1087. };
  1088.  
  1089. static const MatchPairs actionTypes[] =
  1090. { { "get file", NP_GETFILE },
  1091.     { "get dir", NP_GETDIR },
  1092.     { "log in", NP_LOGIN },
  1093.     { "log out", NP_LOGOUT },
  1094.     { "CGI", NP_CGI },
  1095.     { NULL, NP_ERROR }
  1096. };
  1097.  
  1098. NP_type checkValue(char *typeStr, const MatchPairs table[])
  1099. {
  1100.   while(table->str != NULL)
  1101.     { if(strcmp(table->str, typeStr) == 0) return table->value;
  1102.       table++;
  1103.     }
  1104.   return table->value; /* last field contains default value to return */
  1105. }
  1106.  
  1107. /* the NetPresenz fields have to be treated differently as they are
  1108.    matched by prefix only */
  1109.  
  1110. NP_type checkType(char *typeStr)
  1111. { if(strncmp(typeStr, "NetPresenz", 10) == 0) return NP_ADMIN;
  1112.   return checkValue(typeStr, entryTypes);
  1113. }
  1114.  
  1115. /* make up an agent value from the type and action fields */
  1116.  
  1117. void build_agent(char agent[MAXSTRINGLENGTH], NP_type entryType,
  1118.          char *entryTypeStr, NP_type actionType, char *actionTypeStr)
  1119. { /* a simple algorithm for the moment */
  1120.   char *ans;
  1121.  
  1122.   switch(entryType)
  1123.     { case NP_HTTP:
  1124.     ans = actionType == NP_CGI ? "cgi" : "http";
  1125.     break;
  1126.       case NP_GOPHER:
  1127.     ans = "gopher";
  1128.     break;
  1129.       case NP_FTP:
  1130.     ans = "ftp";
  1131.     break;
  1132.       default:
  1133.     ans = "unknown";
  1134.     break;
  1135.       }
  1136.   strcpy(agent, ans);
  1137. }
  1138.  
  1139. /* the speed of sscanf is of concern, but I'm going to use a routine to read
  1140.    in positive numbers - the cost is small.
  1141.    The routine returns -1 on error, otherwise the value read in.
  1142.    The argument pointer is updated.
  1143.    */
  1144. int readDigits(char **from)
  1145. {
  1146.   int ans;
  1147.   char *cin = *from;
  1148.  
  1149.   if(!isdigit(*cin)) return -1;
  1150.   ans = *cin++ - '0';
  1151.   
  1152.   while(isdigit(*cin))
  1153.     { ans *= 10;
  1154.       ans += *cin++ - '0';
  1155.     }
  1156.   
  1157.   *from = cin;
  1158.   return ans;
  1159. }
  1160.  
  1161. /* scan a file name, obeying global flags
  1162.    return 0 on error, 1 if ok
  1163.    */
  1164. int scan_path(char *inputline, char filename[MAXSTRINGLENGTH], int maxlen,
  1165.           struct include *expandhead)
  1166. {
  1167.   char *cin = inputline;
  1168.   char *cout = filename;
  1169.   int ix;
  1170.  
  1171.   extern flag case_insensitive;
  1172.  
  1173.   ix = 0;
  1174.   while(*cin && *cin != '\n' &&
  1175.         *cin != '?' && ix < maxlen)
  1176.     { *cout++ = *cin++;
  1177.       ix++;
  1178.     }
  1179.   *cout = '\0';
  1180.   if (*cin == '?')
  1181.     { if (case_insensitive)
  1182.     strtolower(filename);  /* if no question mark, strtolower later */
  1183.       if (!included(filename, UNSET, expandhead))
  1184.     { while (*cin && *cin != '\n' && ix < maxlen) /* read in args */
  1185.         { *cout++ = *cin++;
  1186.           ix++;
  1187.         }
  1188.       *cout = '\0';
  1189.     }
  1190.     }
  1191.   if (*cin != '\n' && *cin != '?') return 0;    /* fouled up somewhere */
  1192.  
  1193.   return 1;
  1194. }
  1195.  
  1196. int sscanf_netpresenz(FILE *logfile, char *inputline,
  1197.               char hostn[MAXSTRINGLENGTH], int *date,
  1198.               int *monthno, int *year, int *hr, int *min,
  1199.               char filename[MAXSTRINGLENGTH],
  1200.               char referer[MAXSTRINGLENGTH],
  1201.               char agent[MAXSTRINGLENGTH],
  1202.               int *code, char bytestr[16], size_t preflength)
  1203. {
  1204.   char *cin = inputline;
  1205.   char *cout;
  1206.   int peek;
  1207.   char *entryTypeStr, *actionTypeStr;
  1208.   char *entryEnd, *actionEnd;
  1209.   NP_type entryType, actionType;
  1210.   int ix;
  1211.  
  1212.   extern struct include *noexpandhead, *refexpandhead;
  1213. #ifdef MAC
  1214.   Intl0Rec **intl_format;
  1215.   int tmp;
  1216.   static int dateOrder = -1;
  1217.   
  1218.   if (dateOrder<0) {
  1219.     intl_format=(Intl0Rec **)IUGetIntl(0);
  1220.     if (intl_format && *intl_format) dateOrder=(**intl_format).dateOrder;
  1221.     else dateOrder=0;
  1222.   }
  1223. #endif
  1224.  do_again:
  1225.   /* read time */
  1226.   /* hour */
  1227.   if((*hr = readDigits(&cin)) < 0) return 0;
  1228.  
  1229.   if(*cin++ != ':') return 0;
  1230.  
  1231.   /* min */
  1232.   if((*min = readDigits(&cin)) < 0) return 0;
  1233.  
  1234.   /* check for am/pm - we only check first character */
  1235.   while(*cin == ' ') cin++; /* skip spaces */
  1236.   switch(*cin)
  1237.     { case 'a': case 'A':
  1238.     if(*hr == 12) *hr = 0; /* 12am = 00:00 hours */
  1239.     break;
  1240.       case 'p': case 'P':
  1241.     if(*hr != 12) *hr += 12; /* 12pm = 12:00 hours */
  1242.     break;
  1243.       case '\t':
  1244.     break; /* no am/pm */
  1245.       }
  1246.  
  1247.   /* skip to next field */
  1248.   while(*cin && (*cin != '\t')) cin++;
  1249.   if(*cin++ != '\t') return 0;
  1250.  
  1251.   /* date */
  1252.   if((*monthno = readDigits(&cin)) < 0) return 0;
  1253.   if(*cin++ != '/') return 0;
  1254.   if((*date = readDigits(&cin)) < 0) return 0;
  1255.   if(*cin++ != '/') return 0;
  1256.   if((*year = readDigits(&cin)) < 0) return 0;
  1257. #ifdef MAC
  1258.   switch (dateOrder) {                    /* 0 and default - mdy */
  1259.  
  1260.   case 1:                                                 /* 1 - dmy */
  1261.     tmp = *monthno;
  1262.     *monthno = *date;
  1263.     *date=tmp;
  1264.     break;
  1265.   case 2:                                                 /* 2 - ymd */
  1266.     tmp = *monthno;
  1267.     *monthno = *date;
  1268.     *date = *year;
  1269.     *year=tmp;
  1270.     break;
  1271.   case 3:                                                 /* 3 - myd */
  1272.     tmp = *year;
  1273.     *year = *date;
  1274.     *date=tmp;
  1275.     break;
  1276.   case 4:                                                 /* 4 - dym */
  1277.     tmp = *monthno;
  1278.     *monthno = *year;
  1279.     *year = *date;
  1280.     *date=tmp;
  1281.     break;
  1282.   case 5:                                                 /* 5 - ydm */
  1283.     tmp = *monthno;
  1284.     *monthno = *year;
  1285.     *year=tmp;
  1286.     break;
  1287.   }
  1288. #endif
  1289.  
  1290.   /* adjust year */
  1291.   *year += *year < 70 ? 2000 : 1900;
  1292.   /* adjust month - this program starts at 0 */
  1293.   *monthno -= 1;
  1294.  
  1295.   /* skip to next field */
  1296.   while(*cin && (*cin != '\t')) cin++;
  1297.   if(*cin++ != '\t') return 0;
  1298.  
  1299.   /* read in hostname */
  1300.   ix = 0;
  1301.   cout = hostn;
  1302.   while(*cin != '\t' && *cin != '\0' && ix < (MAXSTRINGLENGTH - 1))
  1303.     { *cout++ = *cin++;
  1304.       ix++;
  1305.     }
  1306.   if (*cin++ != '\t') return(0);
  1307.   *cout = '\0';
  1308.   
  1309.   /* now entry type - tab terminated string */
  1310.   entryTypeStr = cin;
  1311.   while(*cin && (*cin != '\t')) cin++;
  1312.   if(*cin != '\t') return 0;
  1313.  
  1314.   entryEnd = cin++;
  1315.   /* so we can restore the tab on error */
  1316.   *entryEnd = '\0';  /* write eos over tab */
  1317.   entryType = checkType(entryTypeStr);
  1318.  
  1319.   /* now let's be smart, if the entry type is NP_ADMIN rather than return
  1320.      a bad line error, let's read a new line and start again. This stops
  1321.      lines which are not really bad from being counted as such.
  1322.      */
  1323.   if (entryType == NP_ADMIN) {
  1324.     if (!fgets(inputline, MAXLINELENGTH, logfile)) return(0);
  1325.         goto do_again;
  1326.   }
  1327.   /* we have no use for the session, skip it */
  1328.   while(isdigit(*cin)) cin++;
  1329.   if(*cin++ != '\t')
  1330.     { *entryEnd = '\t';   /* return input to pristine state */
  1331.       return 0;
  1332.     }
  1333.  
  1334.   /* now the action type - tab terminated string */
  1335.   actionTypeStr = cin;
  1336.   while(*cin && (*cin != '\t')) cin++;
  1337.   if(*cin != '\t') return 0;
  1338.  
  1339.   actionEnd = cin++;          /* so we can restore the tab on error */
  1340.   *actionEnd = '\0';          /* write eos over tab */
  1341.   actionType = checkValue(actionTypeStr, actionTypes);
  1342.  
  1343.   /* similar to above, ignore log in/out */
  1344.   if (actionType == NP_LOGIN || actionType == NP_LOGOUT) {
  1345.       if (!fgets(inputline, MAXLINELENGTH, logfile)) return(0);
  1346.           goto do_again;
  1347.     }
  1348.  
  1349.   /* now the file - this is the short version which we might discard in
  1350.      a moment if there is a full path following
  1351.      */
  1352.   cout = filename;
  1353.   ix = 0;
  1354.   /* 04/Oct/96 JTL <start> - catch missing file name */
  1355.   if (!*cin || *cin=='\t') {
  1356.         *cout++ = '/';
  1357.         ix++;
  1358.       }
  1359.   else while(*cin && (*cin != '\t') &&
  1360.          (ix < (MAXLINELENGTH - 1 - preflength)))  {
  1361.     if (*cin==':') {
  1362.       *cout++ = '/';
  1363.       ++cin;
  1364.     }
  1365.     else *cout++ = *cin++;    /* JTL <end> - and convert ':' to '/' */
  1366.     ix++;
  1367.   }
  1368.   *cout = '\0';
  1369.   /* add eos */
  1370.  
  1371.   if(*cin != '\t')
  1372.     { *entryEnd = *actionEnd = '\t';   /* return input to pristine state */
  1373.       return 0;
  1374.     }
  1375.  
  1376.   /* we now have a complete first line, finish off */
  1377.   build_agent(agent, entryType, entryTypeStr, actionType, actionTypeStr);
  1378.   /* set ok/error based on actionType */
  1379.   *code = actionType == NP_ERROR ? 404 : 200;
  1380.   /* set bytes to zero - as a string... */
  1381.   strcpy(bytestr, "0");
  1382.   /* no referer yet */
  1383.   *referer = '\0';
  1384.  
  1385.   /* now try for optional lines - first the full pathname */
  1386.   /* from this point on we always return 11 = referer & browser present,
  1387.      latter code handles there being no refer by an empty string check.
  1388.      This way we get a browser count which includes ftp & gopher
  1389.      */
  1390.   peek = fgetc(logfile);        /* peek at next char */
  1391.   if(peek == EOF) return 11;
  1392.   ungetc(peek, logfile);        /* put it back */
  1393.   if(isdigit(peek)) return 11;
  1394.  
  1395.   /* read in full path */
  1396.   fgets(inputline, MAXLINELENGTH, logfile);  /* can't fail - ungetc above */
  1397.   /* now copy it, obeying various flags - adapted from sscanf_common() */
  1398.   *filename = '/';
  1399.   /* leading / isn't there - add it */
  1400.   /* note filename+1 & -2 to account for last line */
  1401.   if(scan_path(inputline, filename+1, MAXLINELENGTH - 2 - preflength,
  1402.            noexpandhead) == 0)
  1403.     return 0;
  1404.   /* fouled up somewhere, and trashed our file :-( */
  1405.  
  1406.   /* fix :'s in Mac path */
  1407.   cin = filename;
  1408.   while(*cin)
  1409.     { if(*cin == ':') *cin = '/';
  1410.       cin++;
  1411.     }
  1412.  
  1413.   /* ok, let's try for the referer... */
  1414.   peek = fgetc(logfile);             /* peek at next char */
  1415.   if(peek == EOF) return 11;
  1416.   ungetc(peek, logfile);             /* put it back */
  1417.   if(isdigit(peek)) return 11;
  1418.  
  1419.   /* read in full line */
  1420.   fgets(inputline, MAXLINELENGTH, logfile);
  1421.  
  1422.   /* skip past "Referer: " */
  1423.   cin = inputline;
  1424.   while(*cin && *cin != ' ') cin++;
  1425.   if(*cin++ != ' ') return 11;
  1426.  
  1427.   /* copy referer */
  1428.   if(scan_path(cin, referer, MAXLINELENGTH - 1, refexpandhead) == 0)
  1429.     *referer ='\0';
  1430.  
  1431.   return 11;
  1432.   /* we got the lot! */
  1433. }
  1434. #endif /* NETPRESENZ */
  1435.