
/* 
 * neoXwww.c --
 *
 *      Contains the NeoSoft TCL world-wide web extensions.
 *-----------------------------------------------------------------------------
 * Copyright 1995-1998 NeoSoft.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies.  NeoSoft makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 *
 *-----------------------------------------------------------------------------
 * $Id: neoXwww.c,v 1.1.1.1 1999/03/31 20:34:37 damon Exp $
 *-----------------------------------------------------------------------------
 */

#include "neo.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define BUFSIZE 4096
#define NEO_MAXDEPTH 4


/*
 *-----------------------------------------------------------------------------
 *
 * Neo_wwwIncr --
 *     Helper function to increment an array element, creating it
 *     first if necessary.
 *
 * Results:
 *     Standard TCL results.
 *
 *-----------------------------------------------------------------------------
 */
int
Neo_wwwIncr (interp, arrayName, elementName, incrValue)
    Tcl_Interp *interp;
    char       *arrayName;
    char       *elementName;
    int        incrValue;
{
    int currentValue;
    char *currentValueString;
    char newValueText[16];

#ifdef BIG_DEBUG
printf("wwwincr %s %s\n", arrayName, elementName);
#endif

    currentValueString = Tcl_GetVar2 (interp, 
			     arrayName, 
			     elementName,
			     TCL_GLOBAL_ONLY);

    if (currentValueString == NULL) {
	currentValue = 0;
    } else {
	if (Tcl_GetInt (interp, currentValueString, &currentValue) != TCL_OK) {
	    Tcl_AddErrorInfo (interp,
		"\n    (reading value of variable to increment)");
		return TCL_ERROR;
	}
    }
    currentValue += incrValue;
    sprintf (newValueText, "%d", currentValue);
    Tcl_SetVar2 (interp, arrayName, elementName, newValueText, TCL_GLOBAL_ONLY);
    return TCL_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Neo_LoadNeoWebStatsCmd --
 *     Implements the Neo TCL load_neo_webstats command:
 *         load_neo_webstats arrayName statfile
 *
 * Results:
 *     Standard TCL results.
 *
 *-----------------------------------------------------------------------------
 */
int
Neo_LoadNeoWebStatsCmd (clientData, interp, argc, argv)
    ClientData  clientData;
    Tcl_Interp *interp;
    int         argc;
    char      **argv;
{
    int largc;
    char **largv;
    char hourNumberString[32];
    long dateSeconds, firstDate;
    char *virtualHost;
    char *endptr;
    long startSeekOffset = 0, endSeekOffset = 0;

    char *urlPtr;
    char *urlPiece;
char *remoteName, *remoteLogname, *command, *status, *bytesSent, *user;
    int depth;
    long accessCount = 0, errorCount = 0;
    char *urlPtrs[NEO_MAXDEPTH+1];
    int i, walkStart;
    Tcl_DString dsArrayData;
    Tcl_DString dsElementData;
    Tcl_DString dsInputData;
    Tcl_DString	*dsArray = &dsArrayData;
    Tcl_DString *dsElement = &dsElementData;
    Tcl_DString *dsInput = &dsInputData;
    char *date;
    char *commandVerb, *url, *httpType;
    char *hourlyArrayName;
    Tcl_Channel chanHandle;

    if (argc != 3 && argc != 5) {
        Tcl_AppendResult (interp, 
	    "wrong # args: ",
	    argv[0],
	    " hourlyArrayName",
	    " webstatsFilename",
	    " [startSeekOffset endSeekOffset]",
	    (char *) NULL);
        return TCL_ERROR;
    }

    if (argc == 5) {
	startSeekOffset = strtol(argv[3], &endptr, 10);
	if ((endptr == argv[3]) || (*endptr != '\0')) {
	    Tcl_AppendResult (interp, 
	        "%s: non-numeric-startseek: ",
		argv[3],
		" ",
		interp->result,
		(char *)NULL);
	    return TCL_ERROR;
	}
	endSeekOffset = strtol(argv[4], &endptr, 10);
	if ((endptr == argv[4]) || (*endptr != '\0')) {
	    Tcl_AppendResult (interp, 
	        "%s: non-numeric-endseek: ",
		argv[4],
		" ",
		interp->result,
		(char *)NULL);
	    return TCL_ERROR;
	}
    }

    hourlyArrayName = argv[1];

    if ((chanHandle = Tcl_GetChannel (interp, argv[2], (int *)NULL)) == NULL)
	return TCL_ERROR;

    if (Tcl_Seek (chanHandle, startSeekOffset, SEEK_SET) < 0) {
	return TCL_ERROR;
    }

    while (!Tcl_Eof (chanHandle) 
	&& (Tcl_Tell (chanHandle) < endSeekOffset)) {

	Tcl_ResetResult (interp);

	Tcl_DStringInit (dsInput);
	if (Tcl_Gets (chanHandle, dsInput) < 0) {
	    if (Tcl_Eof (chanHandle)) break;
	    return TCL_ERROR;
	}

	accessCount++;

/*
893859545 205.130.235.207 {} {} 200 3138 www.Powersource.com {GET /cccs/richard/wage.htm HTTP/1.0} {Mozilla/3.0Gold (WinNT; I)}
*/

        if (Tcl_SplitList (interp, Tcl_DStringValue(dsInput), &largc, &largv) == TCL_ERROR) {
	    /* fprintf (stderr, "%s: %s\n", interp->result, buf); */
	    continue;
	}
	Tcl_DStringFree (dsInput);

        /* kill invalid log entries */
        if (largc != 9) {
            continue;
        }

	/* sanity check on string length */
	if (Tcl_DStringLength (dsInput) > 4096) continue;

	date = largv[0];
	remoteName = largv[1];
	remoteLogname = largv[2];
	user = largv[3];
	status = largv[4];
	bytesSent = largv[5];
	virtualHost = largv[6];
	command = largv[7];

	dateSeconds = strtol(date, &endptr, 10);
	if ((endptr == date) || (*endptr != 0)) {
	    /* fprintf (stderr, "%s: non-numeric-date: %s\n", interp->result, buf); */
	    continue;
	}

        sprintf (hourNumberString, "%ld", dateSeconds / 3600);
	Neo_wwwIncr (interp, hourlyArrayName, hourNumberString, 1);

        if (accessCount == 1) {
	    firstDate = dateSeconds;
	}

	if (*status == '\0') continue;
	if (*status != '2' && *status != '3') {
	    errorCount++;
	    continue;
	}

	/* Convert virtualHost to lowercase, because DNS isn't case-sensitive,
	 * and neither should we.  - eugene@neosoft.com
	 */

	for (urlPtr = virtualHost; *urlPtr != '\0'; urlPtr++) {
	    if (isupper((int)(*urlPtr))) {
	        *urlPtr = (char)tolower((int)(*urlPtr));
	    }
	}

        /* parse out the command in detail */
	commandVerb = strtok (command, " ");
	if (commandVerb == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("commandVerb %s\n", commandVerb);
#endif

	url = strtok ((char *)NULL, " ");
	if (url == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("url %s\n", url);
#endif

	httpType = strtok ((char *)NULL, "");
	if (httpType == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("httpType %s\n\n", httpType);
#endif

	/* Convert URL to lowercase.
	 *
	 * Also if we see a '?', the remainder of the URL is parameters
	 * to a CGI, so we terminate the string at the '?' (deleting the
	 * question mark) and stop scanning.
	 */
        for (urlPtr = url; *urlPtr != '\0'; urlPtr++) {
	    if ((*urlPtr >= 'A') && (*urlPtr <= 'Z')) {
		*urlPtr = *urlPtr - 'A' + 'a';
	    } else if (*urlPtr == '?') {
		*urlPtr = '\0';
		break;
	    }
	}

	urlPiece = strtok (url, "/");
	/* strip all "../" leading entries */
	while (urlPiece != (char *)NULL && (STREQU (urlPiece, "..") || (STREQU (urlPiece, ".")))) {
	    urlPiece = strtok ((char *)NULL, "/");
	}

	Tcl_DStringInit (dsElement);
	Tcl_DStringAppend (dsElement, virtualHost, -1);
	Tcl_DStringAppend (dsElement, "/", 1);
	Neo_wwwIncr (interp, "/", Tcl_DStringValue(dsElement), 1);
	Tcl_DStringFree (dsElement);

	Tcl_DStringInit (dsArray);
	Tcl_DStringAppend (dsArray, "/", 1);
	Tcl_DStringAppend (dsArray, virtualHost, -1);
	Tcl_DStringAppend (dsArray, "/", 1);

	/* if we don't have anything, it's a top-level match,
	 * say so and get on with the next one */
	if (urlPiece == (char *)NULL) {
	    Neo_wwwIncr (interp, Tcl_DStringValue(dsArray), "", 1);
	    Tcl_DStringFree (dsArray);
	    continue;
	}

	/* kludge to unescape tildes, whole string should be unescaped */
	urlPtrs[0] = urlPiece;
	if ((*urlPiece == '%') && (*(urlPiece + 1) == '7')
	    && ((*(urlPiece + 2) == 'E') || (*(urlPiece + 2) == 'e'))) {
		*(urlPiece + 2) = '~';
		urlPtrs[0] += 2;
	}

	/* crack the pathname of the URL into separate fields and
	 * store pointers to them in the urlPtrs array, for up to
	 * NEO_MAXDEPTH entries */
	depth = 1;
	while ((urlPiece = strtok ((char *)NULL, "/")) != NULL) {

	    /* if it's "./", blow off the dot... it's redundant */
	    if (STREQU (urlPiece, ".")) continue;

	    /* if it's "../", drop back a level, but don't go
	     * above the top level */
	    if (STREQU (urlPiece, "..")) {
		if (depth > 0) {
		    depth--;
		}
		continue;
	    }

	    if (depth < NEO_MAXDEPTH) {
	        urlPtrs[depth++] = urlPiece;
	    }
	}

	/* if the first directory is /users, it's in the form
	 * like /users/k/kmac, so urlPtrs[0] is "users",
	 * urlPtrs[1] is "k", and urlPtrs[2] is "kmac".
	 *
	 * We change the kmac to ~kmac by overwriting the
	 * terminating null byte of urlPtrs[1], descending
	 * the urlPtrs[2] pointer by one and making that
	 * char be a tilde.
	 */
	if (STREQU (urlPtrs[0], "users")) {
	    *(--urlPtrs[2]) = '~';
	    walkStart = 2;
	} else {
	    walkStart = 0;
	}

	/* walk through the URL pathname fragments, constructing references */
        for (i = walkStart; 
	     i < depth;
	     i++) {

	    Tcl_DStringInit (dsElement);
	    Tcl_DStringAppend (dsElement, urlPtrs[i], -1);

	    if (i < depth - 1) {
		Tcl_DStringAppend (dsElement, "/", 1);
	    }

#ifdef BIG_DEBUG
printf("incr %s %s\n", Tcl_DStringValue(dsArray), Tcl_DStringValue(dsElement));
#endif
	    Neo_wwwIncr (interp, 
		Tcl_DStringValue(dsArray), 
		Tcl_DStringValue(dsElement),
		1);

	    Tcl_DStringFree (dsElement);

	    Tcl_DStringAppend (dsArray, urlPtrs[i], -1);
	    if (i < depth - 1) {
	        Tcl_DStringAppend (dsArray, "/", 1);
	    }
	}
	Tcl_DStringFree (dsArray);
	ckfree ((char *)largv);
    }

    Tcl_ResetResult (interp);
    sprintf (interp->result, "%ld %ld %ld %ld", 
	accessCount,
	errorCount,
	firstDate,
	dateSeconds);
    return TCL_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Neo_LoadWebStatsCmd --
 *     Implements the Neo TCL load_webstats command:
 *         load_webstats arrayName statfile
 *
 * Results:
 *     Standard TCL results.
 *
 *-----------------------------------------------------------------------------
 */
int
Neo_LoadWebStatsCmd (clientData, interp, argc, argv)
    ClientData  clientData;
    Tcl_Interp *interp;
    int         argc;
    char      **argv;
{
    FILE *fp;
    char buf[BUFSIZE];
    char *urlPtr;
    char *urlPiece;
char *remoteName, *remoteLogname, *command, *status, *bytesSent, *user;
    int depth;
    long accessCount = 0, errorCount = 0;
    char *urlPtrs[NEO_MAXDEPTH+1];
    int i, walkStart;
    Tcl_DString dsArrayData;
    Tcl_DString dsElementData;
    Tcl_DString	*dsArray = &dsArrayData;
    Tcl_DString *dsElement = &dsElementData;
    char *dummy;
    char *date, *firstDate;
    char *commandVerb, *url, *httpType;
    char *hourlyArrayName;
    char *fileName;
    char savechar;

    if (argc != 3) {
        Tcl_AppendResult (interp, 
	    "wrong # args: ",
	    argv[0],
	    " hourlyArrayName",
	    " webstatsFilename",
	    (char *) NULL);
        return TCL_ERROR;
    }

    hourlyArrayName = argv[1];
    fileName = argv[2];

    if ((fp = fopen (fileName, "r")) == NULL) {
	interp->result = Tcl_PosixError (interp);
	return TCL_ERROR;
    }

    while ((fgets (buf, BUFSIZE, fp)) != (char *)NULL) {
	accessCount++;

        /* Parse out the site */
	remoteName = strtok (buf, " ");
	if (remoteName == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("remoteName %s\n", remoteName);
#endif

	/* Parse out the remote logname (it is probably just "-") */
	remoteLogname = strtok ((char *)NULL, " ");
	if (remoteLogname == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("remoteLogname %s\n", remoteLogname);
#endif

	/* Parse out the user (it is probably just "-") */
	user = strtok ((char *)NULL, " ");
	if (user == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("user %s\n", user);
#endif


	/* The date everything up to the close-brace. */
	date = strtok ((char *)NULL, "]");
	if (date == (char *)NULL) continue;
	date++;
#ifdef BIG_DEBUG
printf("date %s\n", date);
#endif

    savechar = date[14];
    date[14] = '\0';
    Neo_wwwIncr (interp, hourlyArrayName, date, 1);
    date[14] = savechar;

        if (accessCount == 1) {
	    firstDate = ckalloc (strlen (date) + 1);
	    strcpy (firstDate, date);
	}

	/* Skip everything up to the double quote that starts the command */
	dummy = strtok ((char *)NULL, "\"");
	if (dummy == (char *)NULL) continue;

	/* Parse command (take token up to to the closing double quote) */
	command = strtok ((char *)NULL, "\"");
	if (command == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("command %s\n", command);
#endif

	/* Parse out the status */
	status = strtok ((char *)NULL, " ");
	if (status == (char *)NULL) continue;
	if (*status != '2' && *status != '3') {
	    errorCount++;
	    continue;
	}
#ifdef BIG_DEBUG
printf("status %s\n", status);
#endif

        /* parse out the number of bytes sent */
	bytesSent = strtok ((char *)NULL, "");
	if (bytesSent == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("bytesSent %s\n", bytesSent);
#endif

        /* parse out the command in detail */
	commandVerb = strtok (command, " ");
	if (commandVerb == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("commandVerb %s\n", commandVerb);
#endif

	url = strtok ((char *)NULL, " ");
	if (url == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("url %s\n", url);
#endif

	httpType = strtok ((char *)NULL, "");
	if (httpType == (char *)NULL) continue;
#ifdef BIG_DEBUG
printf("httpType %s\n\n", httpType);
#endif

	/* Convert URL to lowercase.
	 *
	 * Also if we see a '?', the remainder of the URL is parameters
	 * to a CGI, so we terminate the string at the '?' (deleting the
	 * question mark) and bail.
	 */
        for (urlPtr = url; *urlPtr != '\0'; urlPtr++) {
	    if ((*urlPtr >= 'A') && (*urlPtr <= 'Z')) {
		*urlPtr = *urlPtr - 'A' + 'a';
	    } else if (*urlPtr == '?') {
		*urlPtr = '\0';
		break;
	    }
	}

	urlPiece = strtok (url, "/");
	/* strip all "../" leading entries */
	while (urlPiece != (char *)NULL && (STREQU (urlPiece, "..") || (STREQU (urlPiece, ".")))) {
	    urlPiece = strtok ((char *)NULL, "/");
	}

	/* if we don't have anything, it's a top-level match,
	 * say so and get on with the next one */
	if (urlPiece == (char *)NULL) {
	    Neo_wwwIncr (interp, "/", "", 1);
	    continue;
	}

	/* kludge to unescape tildes, whole string should be unescaped */
	urlPtrs[0] = urlPiece;
	if ((*urlPiece == '%') && (*(urlPiece + 1) == '7')
	    && ((*(urlPiece + 2) == 'E') || (*(urlPiece + 2) == 'e'))) {
		*(urlPiece + 2) = '~';
		urlPtrs[0] += 2;
	}

	/* crack the pathname of the URL into separate fields and
	 * store pointers to them in the urlPtrs array, for up to
	 * MAXDEPTH entries */
	depth = 1;
	while ((urlPiece = strtok ((char *)NULL, "/")) != NULL) {

	    /* if it's "./", blow off the dot... it's redundant */
	    if (STREQU (urlPiece, ".")) continue;

	    /* if it's "../", drop back a level, but don't go
	     * above the top level */
	    if (STREQU (urlPiece, "..")) {
		if (depth > 0) {
		    depth--;
		}
		continue;
	    }

	    if (depth < NEO_MAXDEPTH) {
	        urlPtrs[depth++] = urlPiece;
	    }
	}
	Tcl_DStringInit (dsArray);
	Tcl_DStringAppend (dsArray, "/", 1);

	/* if the first directory is /users, it's in the form
	 * like /users/k/kmac, so urlPtrs[0] is "users",
	 * urlPtrs[1] is "k", and urlPtrs[2] is "kmac".
	 *
	 * We change the kmac to ~kmac by overwriting the
	 * terminating null byte of urlPtrs[1], descending
	 * the urlPtrs[2] pointer by one and making that
	 * char be a tilde.
	 */
	if (STREQU (urlPtrs[0], "users")) {
	    *(--urlPtrs[2]) = '~';
	    walkStart = 2;
	} else {
	    walkStart = 0;
	}

	/* walk through the URL pathname fragments, constructing references */
        for (i = walkStart; 
	     i < depth;
	     i++) {

	    Tcl_DStringInit (dsElement);
	    Tcl_DStringAppend (dsElement, urlPtrs[i], -1);

	    if (i < depth - 1) {
		Tcl_DStringAppend (dsElement, "/", 1);
	    }

#ifdef BIG_DEBUG
printf("incr %s %s\n", Tcl_DStringValue(dsArray), Tcl_DStringValue(dsElement));
#endif
	    Neo_wwwIncr (interp, 
		Tcl_DStringValue(dsArray), 
		Tcl_DStringValue(dsElement),
		1);

	    Tcl_DStringAppend (dsArray, urlPtrs[i], -1);
	    if (i < depth - 1) {
	        Tcl_DStringAppend (dsArray, "/", 1);
	    }
	}
    }
    Tcl_DStringFree (dsArray);
    Tcl_DStringFree (dsElement);

    if (ferror (fp)) {
	interp->result = Tcl_PosixError (interp);
	return TCL_ERROR;
    }
    fclose (fp);
    sprintf (interp->result, "%ld %ld {%s} {%s}", 
	accessCount,
	errorCount,
	firstDate,
	date);
    ckfree (firstDate);
    return TCL_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Neo_XhexToDigit --
 *     Helper function to convert a hex character into the equivalent integer.
 *
 * Results:
 *     The integer, or -1 if an illegal hex character is encountered.
 *
 *-----------------------------------------------------------------------------
 */
int 
Neo_XhexToDigit(char c) {

    if (c >= 'a' && c <= 'f') {
	return (c - 'a' + 10);
    }

    if (c >= 'A' && c <= 'F') {
	return (c - 'A' + 10);
    }

    if (c >= '0' && c <= '9') {
	return (c - '0');
    }
    return (-1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Neo_UnescapeStringCmd --
 *     Implements the Neo TCL www_unescape_string command:
 *         www_unescape_string string
 *
 * Results:
 *     Standard TCL results.
 *
 *-----------------------------------------------------------------------------
 */
int
Neo_UnescapeStringCmd (clientData, interp, argc, argv)
    ClientData  clientData;
    Tcl_Interp *interp;
    int         argc;
    char      **argv;
{
    char *origString, *newString, *origStringP, *newStringP;
    int digit1, digit2;

    if (argc != 2) {
	Tcl_AppendResult (interp, 
	    "wrong # args: ",
	    argv[0],
	    " string",
	    (char *) NULL);
	return TCL_ERROR;
    }

    origString = argv[1];
    newString = ckalloc (strlen (origString) + 1);

    /* for all the characters in the source string */
    for (origStringP = origString, newStringP = newString;
	 *origStringP != '\0';
	 origStringP++) {
	 char c = *origStringP;
	 char c2;

         /* map plus to space */
	 if (c == '+') {
	     *newStringP++ = ' ';
	     continue;
	 }

         /* if there's a percent sign, decode the two-character
	  * hex sequence that follows and copy it to the target
	  * string */
	 if (c == '%') {
	     digit1 = Neo_XhexToDigit(c = *++origStringP);
	     digit2 = Neo_XhexToDigit(c2 = *++origStringP);

	     if (digit1 == -1 || digit2 == -1) {
		 sprintf (interp->result,
		     "%s: bad char in hex sequence '%%%c%c'",
		     argv[0], c, c2);
		return TCL_ERROR;
	     }

	     *newStringP++ = (digit1 * 16 + digit2);
	     continue;
	 }

         /* it wasn't a plus or percent, just copy the char across */
	 *newStringP++ = c;
    }
    /* Don't forget to null-terminate the target string */
    *newStringP = '\0';
    Tcl_SetResult (interp, newString, TCL_DYNAMIC);
    return TCL_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Neo_EscapeShellCommandCmd --
 *     Implements the Neo TCL www_escape_shell_command command:
 *         www_escape_shell_command string
 *
 * Results:
 *     Standard TCL results.
 *
 *-----------------------------------------------------------------------------
 */
int
Neo_EscapeShellCommandCmd (clientData, interp, argc, argv)
    ClientData  clientData;
    Tcl_Interp *interp;
    int         argc;
    char      **argv;
{
    char *origString, *newString, *origStringP, *newStringP, *checkP;

    if (argc != 2) {
	Tcl_AppendResult (interp, 
	    "wrong # args: ",
	    argv[0],
	    " string",
	    (char *) NULL);
	return TCL_ERROR;
    }

    origString = argv[1];
    newString = ckalloc (strlen (origString) * 2 + 1);

    /* for all the characters in the source string */
    for (origStringP = origString, newStringP = newString;
	*origStringP != '\0';
	origStringP++) {
	char c = *origStringP;

	/* if the character is a shell metacharacter, quote it */
	for (checkP = "&;`'|*?-~<>^()[]{}$\\"; *checkP != '\0'; checkP++) {
	    if (c == *checkP) {
		*newStringP++ = '\\';
		break;
	    }
	}

	*newStringP++ = c;
    }
    /* Don't forget to null-terminate the target string */
    *newStringP = '\0';
    Tcl_SetResult (interp, newString, TCL_DYNAMIC);
    return TCL_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Neo_LoadWebAgentLogCmd --
 *     Implements the Neo TCL www_load_referer_log command:
 *         www_load_referer_log arrayName statfile
 *
 * Results:
 *     Standard TCL results.
 *
 *-----------------------------------------------------------------------------
 */
int
Neo_LoadWebAgentLogCmd (clientData, interp, argc, argv)
    ClientData  clientData;
    Tcl_Interp *interp;
    int         argc;
    char      **argv;
{
    FILE *fp;
    char buf[BUFSIZE];
    long accessCount = 0;
    char *refererArrayName;
    char *fileName;

    if (argc != 3) {
        Tcl_AppendResult (interp, 
	    "wrong # args: ",
	    argv[0],
	    " refererArrayName",
	    " webstatsFilename",
	    (char *) NULL);
        return TCL_ERROR;
    }

    refererArrayName = argv[1];
    fileName = argv[2];

    if ((fp = fopen (fileName, "r")) == NULL) {
	interp->result = Tcl_PosixError (interp);
	return TCL_ERROR;
    }

    while ((fgets (buf, BUFSIZE, fp)) != (char *)NULL) {
	accessCount++;

	Neo_wwwIncr (interp, refererArrayName, buf, 1);

    }

    if (ferror (fp)) {
	interp->result = Tcl_PosixError (interp);
	return TCL_ERROR;
    }
    fclose (fp);
    sprintf (interp->result, "%ld", accessCount);
    return TCL_OK;
}

  
/*     
 *-----------------------------------------------------------------------------
 * TclXLib_Init --
 *     
 *   Initialize the Extended Tcl library facility commands.
 *-----------------------------------------------------------------------------
 */  
int
Neo_XwwwInit (interp)
    Tcl_Interp *interp;
{
    Tcl_CreateCommand (interp, "www_load_stats", Neo_LoadWebStatsCmd,
                      (ClientData) NULL, (void (*)()) NULL);

    Tcl_CreateCommand (interp, "www_load_neo_stats", Neo_LoadNeoWebStatsCmd,
                      (ClientData) NULL, (void (*)()) NULL);

    Tcl_CreateCommand (interp, "www_unescape_string", Neo_UnescapeStringCmd,
                      (ClientData) NULL, (void (*)()) NULL);

    Tcl_CreateCommand (interp, "www_escape_shell_command", 
		      Neo_EscapeShellCommandCmd,
                      (ClientData) NULL, (void (*)()) NULL);

    Tcl_CreateCommand (interp, "www_load_agent_log", 
		      Neo_LoadWebAgentLogCmd,
                      (ClientData) NULL, (void (*)()) NULL);

    return TCL_OK;
}

