---------------------
PatchSet 5326 
Date: 2002/10/09 14:36:40
Author: rbcollins
Branch: rbcollins_cxxtest
Tag: (none) 
Log:
and more

Members: 
	src/Makefile.am:1.29.2.22->1.29.2.23 
	src/tools.c:1.28.6.1->1.28.6.2(DEAD) 
	src/tools.cc:1.1->1.1.2.1 
	src/url.c:1.11->1.11.6.1(DEAD) 
	src/url.cc:1.1->1.1.2.1 
	src/useragent.c:1.7->1.7.56.1(DEAD) 
	src/useragent.cc:1.1->1.1.2.1 

Index: squid/src/Makefile.am
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/Makefile.am,v
retrieving revision 1.29.2.22
retrieving revision 1.29.2.23
diff -u -r1.29.2.22 -r1.29.2.23
--- squid/src/Makefile.am	9 Oct 2002 14:14:17 -0000	1.29.2.22
+++ squid/src/Makefile.am	9 Oct 2002 14:36:40 -0000	1.29.2.23
@@ -217,12 +217,12 @@
 	store_swapmeta.cc \
 	store_swapout.c \
 	structs.h \
-	tools.c \
+	tools.cc \
 	typedefs.h \
 	$(UNLINKDSOURCE) \
-	url.c \
+	url.cc \
 	urn.cc \
-	useragent.c \
+	useragent.cc \
 	wais.cc \
 	wccp.cc \
 	whois.cc \
--- squid/src/tools.c	Wed Feb 14 01:07:38 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,1098 +0,0 @@
-
-/*
- * $Id: tools.c,v 1.28.6.1 2002/10/06 04:01:15 rbcollins Exp $
- *
- * DEBUG: section 21    Misc Functions
- * AUTHOR: Harvest Derived
- *
- * SQUID Web Proxy Cache          http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- *  Squid is the result of efforts by numerous individuals from
- *  the Internet community; see the CONTRIBUTORS file for full
- *  details.   Many organizations have provided support for Squid's
- *  development; see the SPONSORS file for full details.  Squid is
- *  Copyrighted (C) 2001 by the Regents of the University of
- *  California; see the COPYRIGHT file for full details.  Squid
- *  incorporates software developed and/or copyrighted by other
- *  sources; see the CREDITS file for full details.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *  
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *  
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
- *
- */
-
-#include "squid.h"
-
-#define DEAD_MSG "\
-The Squid Cache (version %s) died.\n\
-\n\
-You've encountered a fatal error in the Squid Cache version %s.\n\
-If a core file was created (possibly in the swap directory),\n\
-please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\
-and report the trace back to squid-bugs@squid-cache.org.\n\
-\n\
-Thanks!\n"
-
-static void fatal_common(const char *);
-static void fatalvf(const char *fmt, va_list args);
-static void mail_warranty(void);
-#if MEM_GEN_TRACE
-extern void log_trace_done();
-extern void log_trace_init(char *);
-#endif
-
-#ifdef _SQUID_LINUX_
-/* Workaround for crappy glic header files */
-extern int backtrace(void *, int);
-extern void backtrace_symbols_fd(void *, int, int);
-extern int setresuid(uid_t, uid_t, uid_t);
-#endif /* _SQUID_LINUX */
-
-extern void (*failure_notify) (const char *);
-
-MemPool *dlink_node_pool = NULL;
-
-void
-releaseServerSockets(void)
-{
-    int i;
-    /* Release the main ports as early as possible */
-    for (i = 0; i < NHttpSockets; i++) {
-	if (HttpSockets[i] >= 0)
-	    close(HttpSockets[i]);
-    }
-    if (theInIcpConnection >= 0)
-	close(theInIcpConnection);
-    if (theOutIcpConnection >= 0 && theOutIcpConnection != theInIcpConnection)
-	close(theOutIcpConnection);
-}
-
-static char *
-dead_msg(void)
-{
-    LOCAL_ARRAY(char, msg, 1024);
-    snprintf(msg, 1024, DEAD_MSG, version_string, version_string);
-    return msg;
-}
-
-static void
-mail_warranty(void)
-{
-    FILE *fp = NULL;
-    static char command[256];
-#if HAVE_MKSTEMP
-    char filename[] = "/tmp/squid-XXXXXX";
-    int tfd = mkstemp(filename);
-    if (tfd < 0)
-	return;
-    if ((fp = fdopen(tfd, "w")) == NULL)
-	return;
-#else
-    char *filename;
-    if ((filename = tempnam(NULL, appname)) == NULL)
-	return;
-    if ((fp = fopen(filename, "w")) == NULL)
-	return;
-#endif
-    fprintf(fp, "From: %s\n", appname);
-    fprintf(fp, "To: %s\n", Config.adminEmail);
-    fprintf(fp, "Subject: %s\n", dead_msg());
-    fclose(fp);
-    snprintf(command, 256, "mail %s < %s", Config.adminEmail, filename);
-    system(command);		/* XXX should avoid system(3) */
-    unlink(filename);
-}
-
-void
-dumpMallocStats(void)
-{
-#if HAVE_MSTATS && HAVE_GNUMALLOC_H
-    struct mstats ms = mstats();
-    fprintf(debug_log, "\ttotal space in arena:  %6d KB\n",
-	(int) (ms.bytes_total >> 10));
-    fprintf(debug_log, "\tTotal free:            %6d KB %d%%\n",
-	(int) (ms.bytes_free >> 10),
-	percent(ms.bytes_free, ms.bytes_total));
-#elif HAVE_MALLINFO && HAVE_STRUCT_MALLINFO
-    struct mallinfo mp;
-    int t;
-    if (!do_mallinfo)
-	return;
-    mp = mallinfo();
-    fprintf(debug_log, "Memory usage for %s via mallinfo():\n", appname);
-    fprintf(debug_log, "\ttotal space in arena:  %6d KB\n",
-	mp.arena >> 10);
-    fprintf(debug_log, "\tOrdinary blocks:       %6d KB %6d blks\n",
-	mp.uordblks >> 10, mp.ordblks);
-    fprintf(debug_log, "\tSmall blocks:          %6d KB %6d blks\n",
-	mp.usmblks >> 10, mp.smblks);
-    fprintf(debug_log, "\tHolding blocks:        %6d KB %6d blks\n",
-	mp.hblkhd >> 10, mp.hblks);
-    fprintf(debug_log, "\tFree Small blocks:     %6d KB\n",
-	mp.fsmblks >> 10);
-    fprintf(debug_log, "\tFree Ordinary blocks:  %6d KB\n",
-	mp.fordblks >> 10);
-    t = mp.uordblks + mp.usmblks + mp.hblkhd;
-    fprintf(debug_log, "\tTotal in use:          %6d KB %d%%\n",
-	t >> 10, percent(t, mp.arena));
-    t = mp.fsmblks + mp.fordblks;
-    fprintf(debug_log, "\tTotal free:            %6d KB %d%%\n",
-	t >> 10, percent(t, mp.arena));
-#if HAVE_STRUCT_MALLINFO_MXFAST
-    fprintf(debug_log, "\tmax size of small blocks:\t%d\n",
-	mp.mxfast);
-    fprintf(debug_log, "\tnumber of small blocks in a holding block:\t%d\n",
-	mp.nlblks);
-    fprintf(debug_log, "\tsmall block rounding factor:\t%d\n",
-	mp.grain);
-    fprintf(debug_log, "\tspace (including overhead) allocated in ord. blks:\t%d\n",
-	mp.uordbytes);
-    fprintf(debug_log, "\tnumber of ordinary blocks allocated:\t%d\n",
-	mp.allocated);
-    fprintf(debug_log, "\tbytes used in maintaining the free tree:\t%d\n",
-	mp.treeoverhead);
-#endif /* HAVE_STRUCT_MALLINFO_MXFAST */
-#endif /* HAVE_MALLINFO */
-}
-
-void
-squid_getrusage(struct rusage *r)
-{
-    memset(r, '\0', sizeof(struct rusage));
-#if HAVE_GETRUSAGE && defined(RUSAGE_SELF)
-#ifdef _SQUID_SOLARIS_
-    /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */
-    enter_suid();
-#endif
-    getrusage(RUSAGE_SELF, r);
-#ifdef _SQUID_SOLARIS_
-    leave_suid();
-#endif
-#endif
-}
-
-double
-rusage_cputime(struct rusage *r)
-{
-    return (double) r->ru_stime.tv_sec +
-	(double) r->ru_utime.tv_sec +
-	(double) r->ru_stime.tv_usec / 1000000.0 +
-	(double) r->ru_utime.tv_usec / 1000000.0;
-}
-
-/* Hack for some HP-UX preprocessors */
-#ifndef HAVE_GETPAGESIZE
-#define HAVE_GETPAGESIZE 0
-#endif
-
-int
-rusage_maxrss(struct rusage *r)
-{
-#if defined(_SQUID_SGI_) && _ABIAPI
-    return r->ru_pad[0];
-#elif defined(_SQUID_SGI_)
-    return r->ru_maxrss;
-#elif defined(_SQUID_OSF_)
-    return r->ru_maxrss;
-#elif defined(BSD4_4)
-    return r->ru_maxrss;
-#elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0
-    return (r->ru_maxrss * getpagesize()) >> 10;
-#elif defined(PAGESIZE)
-    return (r->ru_maxrss * PAGESIZE) >> 10;
-#else
-    return r->ru_maxrss;
-#endif
-}
-
-int
-rusage_pagefaults(struct rusage *r)
-{
-#if defined(_SQUID_SGI_) && _ABIAPI
-    return r->ru_pad[5];
-#else
-    return r->ru_majflt;
-#endif
-}
-
-
-void
-PrintRusage(void)
-{
-    struct rusage rusage;
-    squid_getrusage(&rusage);
-    fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n",
-	rusage_cputime(&rusage),
-	rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0),
-	rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0));
-    fprintf(debug_log, "Maximum Resident Size: %d KB\n",
-	rusage_maxrss(&rusage));
-    fprintf(debug_log, "Page faults with physical i/o: %d\n",
-	rusage_pagefaults(&rusage));
-}
-
-
-void
-death(int sig)
-{
-    if (sig == SIGSEGV)
-	fprintf(debug_log, "FATAL: Received Segment Violation...dying.\n");
-    else if (sig == SIGBUS)
-	fprintf(debug_log, "FATAL: Received Bus Error...dying.\n");
-    else
-	fprintf(debug_log, "FATAL: Received signal %d...dying.\n", sig);
-
-#ifdef PRINT_STACK_TRACE
-#ifdef _SQUID_HPUX_
-    {
-	extern void U_STACK_TRACE(void);	/* link with -lcl */
-	fflush(debug_log);
-	dup2(fileno(debug_log), 2);
-	U_STACK_TRACE();
-    }
-#endif /* _SQUID_HPUX_ */
-#ifdef _SQUID_SOLARIS_
-    {				/* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */
-	extern void opcom_stack_trace(void);	/* link with -lopcom_stack */
-	fflush(debug_log);
-	dup2(fileno(debug_log), fileno(stdout));
-	opcom_stack_trace();
-	fflush(stdout);
-    }
-#endif /* _SQUID_SOLARIS_ */
-#if HAVE_BACKTRACE_SYMBOLS_FD
-    {
-	static void *(callarray[8192]);
-	int n;
-	n = backtrace(callarray, 8192);
-	backtrace_symbols_fd(callarray, n, fileno(debug_log));
-    }
-#endif
-#endif /* PRINT_STACK_TRACE */
-
-#if SA_RESETHAND == 0
-    signal(SIGSEGV, SIG_DFL);
-    signal(SIGBUS, SIG_DFL);
-    signal(sig, SIG_DFL);
-#endif
-    releaseServerSockets();
-    storeDirWriteCleanLogs(0);
-    PrintRusage();
-    dumpMallocStats();
-    if (squid_curtime - SQUID_RELEASE_TIME < 864000) {
-	/* skip if more than 10 days old */
-	if (Config.adminEmail)
-	    mail_warranty();
-	else
-	    puts(dead_msg());
-    }
-    abort();
-}
-
-
-void
-sigusr2_handle(int sig)
-{
-    static int state = 0;
-    /* no debug() here; bad things happen if the signal is delivered during _db_print() */
-    if (state == 0) {
-#ifndef MEM_GEN_TRACE
-	_db_init(Config.Log.log, "ALL,10");
-#else
-	log_trace_done();
-#endif
-	state = 1;
-    } else {
-#ifndef MEM_GEN_TRACE
-	_db_init(Config.Log.log, Config.debugOptions);
-#else
-	log_trace_init("/tmp/squid.alloc");
-#endif
-	state = 0;
-    }
-#if !HAVE_SIGACTION
-    signal(sig, sigusr2_handle);	/* reinstall */
-#endif
-}
-
-static void
-fatal_common(const char *message)
-{
-#if HAVE_SYSLOG
-    syslog(LOG_ALERT, "%s", message);
-#endif
-    fprintf(debug_log, "FATAL: %s\n", message);
-    if (opt_debug_stderr > 0 && debug_log != stderr)
-	fprintf(stderr, "FATAL: %s\n", message);
-    fprintf(debug_log, "Squid Cache (Version %s): Terminated abnormally.\n",
-	version_string);
-    fflush(debug_log);
-    PrintRusage();
-    dumpMallocStats();
-}
-
-/* fatal */
-void
-fatal(const char *message)
-{
-    releaseServerSockets();
-    /* check for store_dirs_rebuilding because fatal() is often
-     * used in early initialization phases, long before we ever
-     * get to the store log. */
-    if (0 == store_dirs_rebuilding)
-	storeDirWriteCleanLogs(0);
-    fatal_common(message);
-    if (shutting_down)
-	exit(0);
-    else
-	abort();
-}
-
-/* printf-style interface for fatal */
-#if STDC_HEADERS
-void
-fatalf(const char *fmt,...)
-{
-    va_list args;
-    va_start(args, fmt);
-#else
-void
-fatalf(va_alist)
-     va_dcl
-{
-    va_list args;
-    const char *fmt = NULL;
-    va_start(args);
-    fmt = va_arg(args, char *);
-#endif
-    fatalvf(fmt, args);
-    va_end(args);
-}
-
-
-/* used by fatalf */
-static void
-fatalvf(const char *fmt, va_list args)
-{
-    static char fatal_str[BUFSIZ];
-    vsnprintf(fatal_str, sizeof(fatal_str), fmt, args);
-    fatal(fatal_str);
-}
-
-/* fatal with dumping core */
-void
-fatal_dump(const char *message)
-{
-    failure_notify = NULL;
-    releaseServerSockets();
-    if (message)
-	fatal_common(message);
-    if (opt_catch_signals)
-	storeDirWriteCleanLogs(0);
-    abort();
-}
-
-void
-debug_trap(const char *message)
-{
-    if (!opt_catch_signals)
-	fatal_dump(message);
-    _db_print("WARNING: %s\n", message);
-}
-
-void
-sig_child(int sig)
-{
-#ifdef _SQUID_NEXT_
-    union wait status;
-#else
-    int status;
-#endif
-    pid_t pid;
-
-    do {
-#ifdef _SQUID_NEXT_
-	pid = wait3(&status, WNOHANG, NULL);
-#else
-	pid = waitpid(-1, &status, WNOHANG);
-#endif
-	/* no debug() here; bad things happen if the signal is delivered during _db_print() */
-#if HAVE_SIGACTION
-    } while (pid > 0);
-#else
-    } while (pid > 0 || (pid < 0 && errno == EINTR));
-    signal(sig, sig_child);
-#endif
-}
-
-const char *
-getMyHostname(void)
-{
-    LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
-    static int present = 0;
-    const struct hostent *h = NULL;
-    struct in_addr sa;
-    if (Config.visibleHostname != NULL)
-	return Config.visibleHostname;
-    if (present)
-	return host;
-    host[0] = '\0';
-    memcpy(&sa, &any_addr, sizeof(sa));
-    if (Config.Sockaddr.http && sa.s_addr == any_addr.s_addr)
-	memcpy(&sa, &Config.Sockaddr.http->s.sin_addr, sizeof(sa));
-#if USE_SSL
-    if (Config.Sockaddr.https && sa.s_addr == any_addr.s_addr)
-	memcpy(&sa, &Config.Sockaddr.https->s.sin_addr, sizeof(sa));
-#endif
-    /*
-     * If the first http_port address has a specific address, try a
-     * reverse DNS lookup on it.
-     */
-    if (sa.s_addr != any_addr.s_addr) {
-	h = gethostbyaddr((char *) &sa,
-	    sizeof(sa), AF_INET);
-	if (h != NULL) {
-	    /* DNS lookup successful */
-	    /* use the official name from DNS lookup */
-	    xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN);
-	    debug(50, 4) ("getMyHostname: resolved %s to '%s'\n",
-		inet_ntoa(sa),
-		host);
-	    present = 1;
-	    if (strchr(host, '.'))
-		return host;
-
-	}
-	debug(50, 1) ("WARNING: failed to resolve %s to a fully qualified hostname\n",
-	    inet_ntoa(sa));
-    }
-    /*
-     * Get the host name and store it in host to return
-     */
-    if (gethostname(host, SQUIDHOSTNAMELEN) < 0) {
-	debug(50, 1) ("WARNING: gethostname failed: %s\n", xstrerror());
-    } else if ((h = gethostbyname(host)) == NULL) {
-	debug(50, 1) ("WARNING: gethostbyname failed for %s\n", host);
-    } else {
-	debug(50, 6) ("getMyHostname: '%s' resolved into '%s'\n",
-	    host, h->h_name);
-	/* DNS lookup successful */
-	/* use the official name from DNS lookup */
-	xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN);
-	present = 1;
-	if (strchr(host, '.'))
-	    return host;
-    }
-    fatal("Could not determine fully qualified hostname.  Please set 'visible_hostname'\n");
-    return NULL;		/* keep compiler happy */
-}
-
-const char *
-uniqueHostname(void)
-{
-    return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname();
-}
-
-void
-safeunlink(const char *s, int quiet)
-{
-    statCounter.syscalls.disk.unlinks++;
-    if (unlink(s) < 0 && !quiet)
-	debug(50, 1) ("safeunlink: Couldn't delete %s: %s\n", s, xstrerror());
-}
-
-/* leave a privilegied section. (Give up any privilegies)
- * Routines that need privilegies can rap themselves in enter_suid()
- * and leave_suid()
- * To give upp all posibilites to gain privilegies use no_suid()
- */
-void
-leave_suid(void)
-{
-    debug(21, 3) ("leave_suid: PID %d called\n", (int) getpid());
-    if (geteuid() != 0)
-	return;
-    /* Started as a root, check suid option */
-    if (Config.effectiveUser == NULL)
-	return;
-#if HAVE_SETGROUPS
-    setgroups(1, &Config2.effectiveGroupID);
-#endif
-    if (setgid(Config2.effectiveGroupID) < 0)
-	debug(50, 0) ("ALERT: setgid: %s\n", xstrerror());
-    debug(21, 3) ("leave_suid: PID %d giving up root, becoming '%s'\n",
-	(int) getpid(), Config.effectiveUser);
-#if HAVE_SETRESUID
-    if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0)
-	debug(50, 0) ("ALERT: setresuid: %s\n", xstrerror());
-#elif HAVE_SETEUID
-    if (seteuid(Config2.effectiveUserID) < 0)
-	debug(50, 0) ("ALERT: seteuid: %s\n", xstrerror());
-#else
-    if (setuid(Config2.effectiveUserID) < 0)
-	debug(50, 0) ("ALERT: setuid: %s\n", xstrerror());
-#endif
-}
-
-/* Enter a privilegied section */
-void
-enter_suid(void)
-{
-    debug(21, 3) ("enter_suid: PID %d taking root priveleges\n", (int) getpid());
-#if HAVE_SETRESUID
-    setresuid(-1, 0, -1);
-#else
-    setuid(0);
-#endif
-}
-
-/* Give up the posibility to gain privilegies.
- * this should be used before starting a sub process
- */
-void
-no_suid(void)
-{
-    uid_t uid;
-    leave_suid();
-    uid = geteuid();
-    debug(21, 3) ("leave_suid: PID %d giving up root priveleges forever\n", (int) getpid());
-#if HAVE_SETRESUID
-    if (setresuid(uid, uid, uid) < 0)
-	debug(50, 1) ("no_suid: setresuid: %s\n", xstrerror());
-#else
-    setuid(0);
-    if (setuid(uid) < 0)
-	debug(50, 1) ("no_suid: setuid: %s\n", xstrerror());
-#endif
-}
-
-void
-writePidFile(void)
-{
-    int fd;
-    const char *f = NULL;
-    mode_t old_umask;
-    char buf[32];
-    if ((f = Config.pidFilename) == NULL)
-	return;
-    if (!strcmp(Config.pidFilename, "none"))
-	return;
-    enter_suid();
-    old_umask = umask(022);
-    fd = file_open(f, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT);
-    umask(old_umask);
-    leave_suid();
-    if (fd < 0) {
-	debug(50, 0) ("%s: %s\n", f, xstrerror());
-	debug_trap("Could not write pid file");
-	return;
-    }
-    snprintf(buf, 32, "%d\n", (int) getpid());
-    FD_WRITE_METHOD(fd, buf, strlen(buf));
-    file_close(fd);
-}
-
-
-pid_t
-readPidFile(void)
-{
-    FILE *pid_fp = NULL;
-    const char *f = Config.pidFilename;
-    pid_t pid = -1;
-    int i;
-
-    if (f == NULL || !strcmp(Config.pidFilename, "none")) {
-	fprintf(stderr, "%s: ERROR: No pid file name defined\n", appname);
-	exit(1);
-    }
-    pid_fp = fopen(f, "r");
-    if (pid_fp != NULL) {
-	pid = 0;
-	if (fscanf(pid_fp, "%d", &i) == 1)
-	    pid = (pid_t) i;
-	fclose(pid_fp);
-    } else {
-	if (errno != ENOENT) {
-	    fprintf(stderr, "%s: ERROR: Could not read pid file\n", appname);
-	    fprintf(stderr, "\t%s: %s\n", f, xstrerror());
-	    exit(1);
-	}
-    }
-    return pid;
-}
-
-
-void
-setMaxFD(void)
-{
-#if HAVE_SETRLIMIT
-    /* try to use as many file descriptors as possible */
-    /* System V uses RLIMIT_NOFILE and BSD uses RLIMIT_OFILE */
-    struct rlimit rl;
-#if defined(RLIMIT_NOFILE)
-    if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
-	debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror());
-    } else {
-	rl.rlim_cur = Squid_MaxFD;
-	if (rl.rlim_cur > rl.rlim_max)
-	    Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
-	if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
-	    snprintf(tmp_error_buf, ERROR_BUF_SZ,
-		"setrlimit: RLIMIT_NOFILE: %s", xstrerror());
-	    fatal_dump(tmp_error_buf);
-	}
-    }
-#elif defined(RLIMIT_OFILE)
-    if (getrlimit(RLIMIT_OFILE, &rl) < 0) {
-	debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror());
-    } else {
-	rl.rlim_cur = Squid_MaxFD;
-	if (rl.rlim_cur > rl.rlim_max)
-	    Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
-	if (setrlimit(RLIMIT_OFILE, &rl) < 0) {
-	    snprintf(tmp_error_buf, ERROR_BUF_SZ,
-		"setrlimit: RLIMIT_OFILE: %s", xstrerror());
-	    fatal_dump(tmp_error_buf);
-	}
-    }
-#endif
-#else /* HAVE_SETRLIMIT */
-    debug(21, 1) ("setMaxFD: Cannot increase: setrlimit() not supported on this system\n");
-#endif /* HAVE_SETRLIMIT */
-
-#if HAVE_SETRLIMIT && defined(RLIMIT_DATA)
-    if (getrlimit(RLIMIT_DATA, &rl) < 0) {
-	debug(50, 0) ("getrlimit: RLIMIT_DATA: %s\n", xstrerror());
-    } else if (rl.rlim_max > rl.rlim_cur) {
-	rl.rlim_cur = rl.rlim_max;	/* set it to the max */
-	if (setrlimit(RLIMIT_DATA, &rl) < 0) {
-	    snprintf(tmp_error_buf, ERROR_BUF_SZ,
-		"setrlimit: RLIMIT_DATA: %s", xstrerror());
-	    fatal_dump(tmp_error_buf);
-	}
-    }
-#endif /* RLIMIT_DATA */
-#if HAVE_SETRLIMIT && defined(RLIMIT_VMEM)
-    if (getrlimit(RLIMIT_VMEM, &rl) < 0) {
-	debug(50, 0) ("getrlimit: RLIMIT_VMEM: %s\n", xstrerror());
-    } else if (rl.rlim_max > rl.rlim_cur) {
-	rl.rlim_cur = rl.rlim_max;	/* set it to the max */
-	if (setrlimit(RLIMIT_VMEM, &rl) < 0) {
-	    snprintf(tmp_error_buf, ERROR_BUF_SZ,
-		"setrlimit: RLIMIT_VMEM: %s", xstrerror());
-	    fatal_dump(tmp_error_buf);
-	}
-    }
-#endif /* RLIMIT_VMEM */
-}
-
-time_t
-getCurrentTime(void)
-{
-#if GETTIMEOFDAY_NO_TZP
-    gettimeofday(&current_time);
-#else
-    gettimeofday(&current_time, NULL);
-#endif
-    current_dtime = (double) current_time.tv_sec +
-	(double) current_time.tv_usec / 1000000.0;
-    return squid_curtime = current_time.tv_sec;
-}
-
-int
-percent(int a, int b)
-{
-    return b ? ((int) (100.0 * a / b + 0.5)) : 0;
-}
-
-double
-dpercent(double a, double b)
-{
-    return b ? (100.0 * a / b) : 0.0;
-}
-
-void
-squid_signal(int sig, SIGHDLR * func, int flags)
-{
-#if HAVE_SIGACTION
-    struct sigaction sa;
-    sa.sa_handler = func;
-    sa.sa_flags = flags;
-    sigemptyset(&sa.sa_mask);
-    if (sigaction(sig, &sa, NULL) < 0)
-	debug(50, 0) ("sigaction: sig=%d func=%p: %s\n", sig, func, xstrerror());
-#else
-    signal(sig, func);
-#endif
-}
-
-struct in_addr
-inaddrFromHostent(const struct hostent *hp)
-{
-    struct in_addr s;
-    xmemcpy(&s.s_addr, hp->h_addr, sizeof(s.s_addr));
-    return s;
-}
-
-double
-doubleAverage(double cur, double new, int N, int max)
-{
-    if (N > max)
-	N = max;
-    return (cur * (N - 1.0) + new) / N;
-}
-
-int
-intAverage(int cur, int new, int n, int max)
-{
-    if (n > max)
-	n = max;
-    return (cur * (n - 1) + new) / n;
-}
-
-void
-logsFlush(void)
-{
-    if (debug_log)
-	fflush(debug_log);
-}
-
-const char *
-checkNullString(const char *p)
-{
-    return p ? p : "(NULL)";
-}
-
-dlink_node *
-dlinkNodeNew()
-{
-    if (dlink_node_pool == NULL)
-	dlink_node_pool = memPoolCreate("Dlink list nodes", sizeof(dlink_node));
-    /* where should we call memPoolDestroy(dlink_node_pool); */
-    return memPoolAlloc(dlink_node_pool);
-}
-
-/* the node needs to be unlinked FIRST */
-void
-dlinkNodeDelete(dlink_node * m)
-{
-    if (m == NULL)
-	return;
-    memPoolFree(dlink_node_pool, m);
-}
-
-void
-dlinkAdd(void *data, dlink_node * m, dlink_list * list)
-{
-    m->data = data;
-    m->prev = NULL;
-    m->next = list->head;
-    if (list->head)
-	list->head->prev = m;
-    list->head = m;
-    if (list->tail == NULL)
-	list->tail = m;
-}
-
-void
-dlinkAddAfter(void *data, dlink_node * m, dlink_node * n, dlink_list * list)
-{
-    m->data = data;
-    m->prev = n;
-    m->next = n->next;
-    if (n->next)
-	n->next->prev = m;
-    else {
-	assert(list->tail == n);
-	list->tail = m;
-    }
-    n->next = m;
-}
-
-void
-dlinkAddTail(void *data, dlink_node * m, dlink_list * list)
-{
-    m->data = data;
-    m->next = NULL;
-    m->prev = list->tail;
-    if (list->tail)
-	list->tail->next = m;
-    list->tail = m;
-    if (list->head == NULL)
-	list->head = m;
-}
-
-void
-dlinkDelete(dlink_node * m, dlink_list * list)
-{
-    if (m->next)
-	m->next->prev = m->prev;
-    if (m->prev)
-	m->prev->next = m->next;
-    if (m == list->head)
-	list->head = m->next;
-    if (m == list->tail)
-	list->tail = m->prev;
-    m->next = m->prev = NULL;
-}
-
-void
-kb_incr(kb_t * k, size_t v)
-{
-    k->bytes += v;
-    k->kb += (k->bytes >> 10);
-    k->bytes &= 0x3FF;
-}
-
-void
-debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm)
-{
-    MemBuf mb;
-    Packer p;
-    assert(label && obj && pm);
-    memBufDefInit(&mb);
-    packerToMemInit(&p, &mb);
-    (*pm) (obj, &p);
-    debug(section, level) ("%s%s", label, mb.buf);
-    packerClean(&p);
-    memBufClean(&mb);
-}
-
-int
-stringHasWhitespace(const char *s)
-{
-    return strpbrk(s, w_space) != NULL;
-}
-
-void
-linklistPush(link_list ** L, void *p)
-{
-    link_list *l = memAllocate(MEM_LINK_LIST);
-    l->next = NULL;
-    l->ptr = p;
-    while (*L)
-	L = &(*L)->next;
-    *L = l;
-}
-
-void *
-linklistShift(link_list ** L)
-{
-    void *p;
-    link_list *l;
-    if (NULL == *L)
-	return NULL;
-    l = *L;
-    p = l->ptr;
-    *L = (*L)->next;
-    memFree(l, MEM_LINK_LIST);
-    return p;
-}
-
-/*
- * Same as rename(2) but complains if something goes wrong;
- * the caller is responsible for handing and explaining the 
- * consequences of errors.
- */
-int
-xrename(const char *from, const char *to)
-{
-    debug(21, 2) ("xrename: renaming %s to %s\n", from, to);
-#ifdef _SQUID_MSWIN_
-    remove(to);
-#endif
-    if (0 == rename(from, to))
-	return 0;
-    debug(21, errno == ENOENT ? 2 : 1) ("xrename: Cannot rename %s to %s: %s\n",
-	from, to, xstrerror());
-    return -1;
-}
-
-int
-stringHasCntl(const char *s)
-{
-    unsigned char c;
-    while ((c = (unsigned char) *s++) != '\0') {
-	if (c <= 0x1f)
-	    return 1;
-	if (c >= 0x7f && c <= 0x9f)
-	    return 1;
-    }
-    return 0;
-}
-
-/*
- * isPowTen returns true if its argument is an integer power of
- * 10.  Its used for logging of certain error messages that can
- * occur often, but that we don't want to fill cache.log with.
- */
-int
-isPowTen(int count)
-{
-    double x = log(count) / log(10.0);
-    if (0.0 != x - (double) (int) x)
-	return 0;
-    return 1;
-}
-
-void
-parseEtcHosts(void)
-{
-    FILE *fp;
-    char buf[1024];
-    char buf2[512];
-    char *nt = buf;
-    char *lt = buf;
-
-    if (NULL == Config.etcHostsPath)
-	return;
-    if (0 == strcmp(Config.etcHostsPath, "none"))
-	return;
-    fp = fopen(Config.etcHostsPath, "r");
-    if (fp == NULL) {
-	debug(1, 1) ("parseEtcHosts: %s: %s\n",
-	    Config.etcHostsPath, xstrerror());
-	return;
-    }
-#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
-    setmode(fileno(fp), O_TEXT);
-#endif
-    while (fgets(buf, 1024, fp)) {	/* for each line */
-	wordlist *hosts = NULL;
-	char *addr;
-	if (buf[0] == '#')	/* MS-windows likes to add comments */
-	    continue;
-	lt = buf;
-	addr = buf;
-	debug(1, 5) ("etc_hosts: line is '%s'\n", buf);
-	nt = strpbrk(lt, w_space);
-	if (nt == NULL)		/* empty line */
-	    continue;
-	*nt = '\0';		/* null-terminate the address */
-	debug(1, 5) ("etc_hosts: address is '%s'\n", addr);
-	lt = nt + 1;
-	while ((nt = strpbrk(lt, w_space))) {
-	    char *host = NULL;
-	    if (nt == lt) {	/* multiple spaces */
-		debug(1, 5) ("etc_hosts: multiple spaces, skipping\n");
-		lt = nt + 1;
-		continue;
-	    }
-	    *nt = '\0';
-	    debug(1, 5) ("etc_hosts: got hostname '%s'\n", lt);
-	    if (Config.appendDomain && !strchr(lt, '.')) {
-		/* I know it's ugly, but it's only at reconfig */
-		strncpy(buf2, lt, 512);
-		strncat(buf2, Config.appendDomain, 512 - strlen(lt));
-		host = buf2;
-	    } else {
-		host = lt;
-	    }
-	    if (ipcacheAddEntryFromHosts(host, addr) != 0)
-		goto skip;	/* invalid address, continuing is useless */
-	    wordlistAdd(&hosts, host);
-	    lt = nt + 1;
-	}
-	fqdncacheAddEntryFromHosts(addr, hosts);
-      skip:
-	wordlistDestroy(&hosts);
-    }
-}
-
-int
-getMyPort(void)
-{
-    if (Config.Sockaddr.http)
-	return ntohs(Config.Sockaddr.http->s.sin_port);
-#if USE_SSL
-    if (Config.Sockaddr.https)
-	return ntohs(Config.Sockaddr.https->s.sin_port);
-#endif
-    fatal("No port defined");
-    return 0;			/* NOT REACHED */
-}
-
-/*
- * Similar to strtok, but has some rudimentary knowledge
- * of quoting
- */
-char *
-strwordtok(char *buf, char **t)
-{
-    unsigned char *word = NULL;
-    unsigned char *p = (unsigned char *) buf;
-    unsigned char *d;
-    unsigned char ch;
-    int quoted = 0;
-    if (!p)
-	p = (unsigned char *) *t;
-    if (!p)
-	goto error;
-    while (*p && isspace(*p))
-	p++;
-    if (!*p)
-	goto error;
-    word = d = p;
-    while ((ch = *p)) {
-	switch (ch) {
-	case '\\':
-	    p++;
-	    *d++ = ch = *p;
-	    if (ch)
-		p++;
-	    break;
-	case '"':
-	    quoted = !quoted;
-	    p++;
-	default:
-	    if (!quoted && isspace(*p)) {
-		p++;
-		goto done;
-	    }
-	    *d++ = *p++;
-	    break;
-	}
-    }
-  done:
-    *d++ = '\0';
-  error:
-    *t = (char *) p;
-    return (char *) word;
-}
-
-/*
- * Inverse of strwordtok. Quotes a word if needed
- */
-void
-strwordquote(MemBuf * mb, const char *str)
-{
-    int quoted = 0;
-    if (strchr(str, ' ')) {
-	quoted = 1;
-	memBufAppend(mb, "\"", 1);
-    }
-    while (*str) {
-	int l = strcspn(str, "\"\\");
-	memBufAppend(mb, str, l);
-	str += l;
-	while (*str == '"' || *str == '\\') {
-	    memBufAppend(mb, "\\", 1);
-	    memBufAppend(mb, str, 1);
-	    str++;
-	}
-    }
-    if (quoted)
-	memBufAppend(mb, "\"", 1);
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/tools.cc	Wed Feb 14 01:07:38 2007
@@ -0,0 +1,1098 @@
+
+/*
+ * $Id: tools.cc,v 1.1.2.1 2002/10/09 14:36:47 rbcollins Exp $
+ *
+ * DEBUG: section 21    Misc Functions
+ * AUTHOR: Harvest Derived
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+#define DEAD_MSG "\
+The Squid Cache (version %s) died.\n\
+\n\
+You've encountered a fatal error in the Squid Cache version %s.\n\
+If a core file was created (possibly in the swap directory),\n\
+please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\
+and report the trace back to squid-bugs@squid-cache.org.\n\
+\n\
+Thanks!\n"
+
+static void fatal_common(const char *);
+static void fatalvf(const char *fmt, va_list args);
+static void mail_warranty(void);
+#if MEM_GEN_TRACE
+extern void log_trace_done();
+extern void log_trace_init(char *);
+#endif
+
+#ifdef _SQUID_LINUX_
+/* Workaround for crappy glic header files */
+SQUIDCEXTERN int backtrace(void *, int);
+SQUIDCEXTERN void backtrace_symbols_fd(void *, int, int);
+SQUIDCEXTERN int setresuid(uid_t, uid_t, uid_t);
+#endif /* _SQUID_LINUX */
+
+extern void (*failure_notify) (const char *);
+
+MemPool *dlink_node_pool = NULL;
+
+void
+releaseServerSockets(void)
+{
+    int i;
+    /* Release the main ports as early as possible */
+    for (i = 0; i < NHttpSockets; i++) {
+	if (HttpSockets[i] >= 0)
+	    close(HttpSockets[i]);
+    }
+    if (theInIcpConnection >= 0)
+	close(theInIcpConnection);
+    if (theOutIcpConnection >= 0 && theOutIcpConnection != theInIcpConnection)
+	close(theOutIcpConnection);
+}
+
+static char *
+dead_msg(void)
+{
+    LOCAL_ARRAY(char, msg, 1024);
+    snprintf(msg, 1024, DEAD_MSG, version_string, version_string);
+    return msg;
+}
+
+static void
+mail_warranty(void)
+{
+    FILE *fp = NULL;
+    static char command[256];
+#if HAVE_MKSTEMP
+    char filename[] = "/tmp/squid-XXXXXX";
+    int tfd = mkstemp(filename);
+    if (tfd < 0)
+	return;
+    if ((fp = fdopen(tfd, "w")) == NULL)
+	return;
+#else
+    char *filename;
+    if ((filename = tempnam(NULL, appname)) == NULL)
+	return;
+    if ((fp = fopen(filename, "w")) == NULL)
+	return;
+#endif
+    fprintf(fp, "From: %s\n", appname);
+    fprintf(fp, "To: %s\n", Config.adminEmail);
+    fprintf(fp, "Subject: %s\n", dead_msg());
+    fclose(fp);
+    snprintf(command, 256, "mail %s < %s", Config.adminEmail, filename);
+    system(command);		/* XXX should avoid system(3) */
+    unlink(filename);
+}
+
+void
+dumpMallocStats(void)
+{
+#if HAVE_MSTATS && HAVE_GNUMALLOC_H
+    struct mstats ms = mstats();
+    fprintf(debug_log, "\ttotal space in arena:  %6d KB\n",
+	(int) (ms.bytes_total >> 10));
+    fprintf(debug_log, "\tTotal free:            %6d KB %d%%\n",
+	(int) (ms.bytes_free >> 10),
+	percent(ms.bytes_free, ms.bytes_total));
+#elif HAVE_MALLINFO && HAVE_STRUCT_MALLINFO
+    struct mallinfo mp;
+    int t;
+    if (!do_mallinfo)
+	return;
+    mp = mallinfo();
+    fprintf(debug_log, "Memory usage for %s via mallinfo():\n", appname);
+    fprintf(debug_log, "\ttotal space in arena:  %6d KB\n",
+	mp.arena >> 10);
+    fprintf(debug_log, "\tOrdinary blocks:       %6d KB %6d blks\n",
+	mp.uordblks >> 10, mp.ordblks);
+    fprintf(debug_log, "\tSmall blocks:          %6d KB %6d blks\n",
+	mp.usmblks >> 10, mp.smblks);
+    fprintf(debug_log, "\tHolding blocks:        %6d KB %6d blks\n",
+	mp.hblkhd >> 10, mp.hblks);
+    fprintf(debug_log, "\tFree Small blocks:     %6d KB\n",
+	mp.fsmblks >> 10);
+    fprintf(debug_log, "\tFree Ordinary blocks:  %6d KB\n",
+	mp.fordblks >> 10);
+    t = mp.uordblks + mp.usmblks + mp.hblkhd;
+    fprintf(debug_log, "\tTotal in use:          %6d KB %d%%\n",
+	t >> 10, percent(t, mp.arena));
+    t = mp.fsmblks + mp.fordblks;
+    fprintf(debug_log, "\tTotal free:            %6d KB %d%%\n",
+	t >> 10, percent(t, mp.arena));
+#if HAVE_STRUCT_MALLINFO_MXFAST
+    fprintf(debug_log, "\tmax size of small blocks:\t%d\n",
+	mp.mxfast);
+    fprintf(debug_log, "\tnumber of small blocks in a holding block:\t%d\n",
+	mp.nlblks);
+    fprintf(debug_log, "\tsmall block rounding factor:\t%d\n",
+	mp.grain);
+    fprintf(debug_log, "\tspace (including overhead) allocated in ord. blks:\t%d\n",
+	mp.uordbytes);
+    fprintf(debug_log, "\tnumber of ordinary blocks allocated:\t%d\n",
+	mp.allocated);
+    fprintf(debug_log, "\tbytes used in maintaining the free tree:\t%d\n",
+	mp.treeoverhead);
+#endif /* HAVE_STRUCT_MALLINFO_MXFAST */
+#endif /* HAVE_MALLINFO */
+}
+
+void
+squid_getrusage(struct rusage *r)
+{
+    memset(r, '\0', sizeof(struct rusage));
+#if HAVE_GETRUSAGE && defined(RUSAGE_SELF)
+#ifdef _SQUID_SOLARIS_
+    /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */
+    enter_suid();
+#endif
+    getrusage(RUSAGE_SELF, r);
+#ifdef _SQUID_SOLARIS_
+    leave_suid();
+#endif
+#endif
+}
+
+double
+rusage_cputime(struct rusage *r)
+{
+    return (double) r->ru_stime.tv_sec +
+	(double) r->ru_utime.tv_sec +
+	(double) r->ru_stime.tv_usec / 1000000.0 +
+	(double) r->ru_utime.tv_usec / 1000000.0;
+}
+
+/* Hack for some HP-UX preprocessors */
+#ifndef HAVE_GETPAGESIZE
+#define HAVE_GETPAGESIZE 0
+#endif
+
+int
+rusage_maxrss(struct rusage *r)
+{
+#if defined(_SQUID_SGI_) && _ABIAPI
+    return r->ru_pad[0];
+#elif defined(_SQUID_SGI_)
+    return r->ru_maxrss;
+#elif defined(_SQUID_OSF_)
+    return r->ru_maxrss;
+#elif defined(BSD4_4)
+    return r->ru_maxrss;
+#elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0
+    return (r->ru_maxrss * getpagesize()) >> 10;
+#elif defined(PAGESIZE)
+    return (r->ru_maxrss * PAGESIZE) >> 10;
+#else
+    return r->ru_maxrss;
+#endif
+}
+
+int
+rusage_pagefaults(struct rusage *r)
+{
+#if defined(_SQUID_SGI_) && _ABIAPI
+    return r->ru_pad[5];
+#else
+    return r->ru_majflt;
+#endif
+}
+
+
+void
+PrintRusage(void)
+{
+    struct rusage rusage;
+    squid_getrusage(&rusage);
+    fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n",
+	rusage_cputime(&rusage),
+	rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0),
+	rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0));
+    fprintf(debug_log, "Maximum Resident Size: %d KB\n",
+	rusage_maxrss(&rusage));
+    fprintf(debug_log, "Page faults with physical i/o: %d\n",
+	rusage_pagefaults(&rusage));
+}
+
+
+void
+death(int sig)
+{
+    if (sig == SIGSEGV)
+	fprintf(debug_log, "FATAL: Received Segment Violation...dying.\n");
+    else if (sig == SIGBUS)
+	fprintf(debug_log, "FATAL: Received Bus Error...dying.\n");
+    else
+	fprintf(debug_log, "FATAL: Received signal %d...dying.\n", sig);
+
+#ifdef PRINT_STACK_TRACE
+#ifdef _SQUID_HPUX_
+    {
+	extern void U_STACK_TRACE(void);	/* link with -lcl */
+	fflush(debug_log);
+	dup2(fileno(debug_log), 2);
+	U_STACK_TRACE();
+    }
+#endif /* _SQUID_HPUX_ */
+#ifdef _SQUID_SOLARIS_
+    {				/* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */
+	extern void opcom_stack_trace(void);	/* link with -lopcom_stack */
+	fflush(debug_log);
+	dup2(fileno(debug_log), fileno(stdout));
+	opcom_stack_trace();
+	fflush(stdout);
+    }
+#endif /* _SQUID_SOLARIS_ */
+#if HAVE_BACKTRACE_SYMBOLS_FD
+    {
+	static void *(callarray[8192]);
+	int n;
+	n = backtrace(callarray, 8192);
+	backtrace_symbols_fd(callarray, n, fileno(debug_log));
+    }
+#endif
+#endif /* PRINT_STACK_TRACE */
+
+#if SA_RESETHAND == 0
+    signal(SIGSEGV, SIG_DFL);
+    signal(SIGBUS, SIG_DFL);
+    signal(sig, SIG_DFL);
+#endif
+    releaseServerSockets();
+    storeDirWriteCleanLogs(0);
+    PrintRusage();
+    dumpMallocStats();
+    if (squid_curtime - SQUID_RELEASE_TIME < 864000) {
+	/* skip if more than 10 days old */
+	if (Config.adminEmail)
+	    mail_warranty();
+	else
+	    puts(dead_msg());
+    }
+    abort();
+}
+
+
+void
+sigusr2_handle(int sig)
+{
+    static int state = 0;
+    /* no debug() here; bad things happen if the signal is delivered during _db_print() */
+    if (state == 0) {
+#ifndef MEM_GEN_TRACE
+	_db_init(Config.Log.log, "ALL,10");
+#else
+	log_trace_done();
+#endif
+	state = 1;
+    } else {
+#ifndef MEM_GEN_TRACE
+	_db_init(Config.Log.log, Config.debugOptions);
+#else
+	log_trace_init("/tmp/squid.alloc");
+#endif
+	state = 0;
+    }
+#if !HAVE_SIGACTION
+    signal(sig, sigusr2_handle);	/* reinstall */
+#endif
+}
+
+static void
+fatal_common(const char *message)
+{
+#if HAVE_SYSLOG
+    syslog(LOG_ALERT, "%s", message);
+#endif
+    fprintf(debug_log, "FATAL: %s\n", message);
+    if (opt_debug_stderr > 0 && debug_log != stderr)
+	fprintf(stderr, "FATAL: %s\n", message);
+    fprintf(debug_log, "Squid Cache (Version %s): Terminated abnormally.\n",
+	version_string);
+    fflush(debug_log);
+    PrintRusage();
+    dumpMallocStats();
+}
+
+/* fatal */
+void
+fatal(const char *message)
+{
+    releaseServerSockets();
+    /* check for store_dirs_rebuilding because fatal() is often
+     * used in early initialization phases, long before we ever
+     * get to the store log. */
+    if (0 == store_dirs_rebuilding)
+	storeDirWriteCleanLogs(0);
+    fatal_common(message);
+    if (shutting_down)
+	exit(0);
+    else
+	abort();
+}
+
+/* printf-style interface for fatal */
+#if STDC_HEADERS
+void
+fatalf(const char *fmt,...)
+{
+    va_list args;
+    va_start(args, fmt);
+#else
+void
+fatalf(va_alist)
+     va_dcl
+{
+    va_list args;
+    const char *fmt = NULL;
+    va_start(args);
+    fmt = va_arg(args, char *);
+#endif
+    fatalvf(fmt, args);
+    va_end(args);
+}
+
+
+/* used by fatalf */
+static void
+fatalvf(const char *fmt, va_list args)
+{
+    static char fatal_str[BUFSIZ];
+    vsnprintf(fatal_str, sizeof(fatal_str), fmt, args);
+    fatal(fatal_str);
+}
+
+/* fatal with dumping core */
+void
+fatal_dump(const char *message)
+{
+    failure_notify = NULL;
+    releaseServerSockets();
+    if (message)
+	fatal_common(message);
+    if (opt_catch_signals)
+	storeDirWriteCleanLogs(0);
+    abort();
+}
+
+void
+debug_trap(const char *message)
+{
+    if (!opt_catch_signals)
+	fatal_dump(message);
+    _db_print("WARNING: %s\n", message);
+}
+
+void
+sig_child(int sig)
+{
+#ifdef _SQUID_NEXT_
+    union wait status;
+#else
+    int status;
+#endif
+    pid_t pid;
+
+    do {
+#ifdef _SQUID_NEXT_
+	pid = wait3(&status, WNOHANG, NULL);
+#else
+	pid = waitpid(-1, &status, WNOHANG);
+#endif
+	/* no debug() here; bad things happen if the signal is delivered during _db_print() */
+#if HAVE_SIGACTION
+    } while (pid > 0);
+#else
+    } while (pid > 0 || (pid < 0 && errno == EINTR));
+    signal(sig, sig_child);
+#endif
+}
+
+const char *
+getMyHostname(void)
+{
+    LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
+    static int present = 0;
+    const struct hostent *h = NULL;
+    struct in_addr sa;
+    if (Config.visibleHostname != NULL)
+	return Config.visibleHostname;
+    if (present)
+	return host;
+    host[0] = '\0';
+    memcpy(&sa, &any_addr, sizeof(sa));
+    if (Config.Sockaddr.http && sa.s_addr == any_addr.s_addr)
+	memcpy(&sa, &Config.Sockaddr.http->s.sin_addr, sizeof(sa));
+#if USE_SSL
+    if (Config.Sockaddr.https && sa.s_addr == any_addr.s_addr)
+	memcpy(&sa, &Config.Sockaddr.https->s.sin_addr, sizeof(sa));
+#endif
+    /*
+     * If the first http_port address has a specific address, try a
+     * reverse DNS lookup on it.
+     */
+    if (sa.s_addr != any_addr.s_addr) {
+	h = gethostbyaddr((char *) &sa,
+	    sizeof(sa), AF_INET);
+	if (h != NULL) {
+	    /* DNS lookup successful */
+	    /* use the official name from DNS lookup */
+	    xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN);
+	    debug(50, 4) ("getMyHostname: resolved %s to '%s'\n",
+		inet_ntoa(sa),
+		host);
+	    present = 1;
+	    if (strchr(host, '.'))
+		return host;
+
+	}
+	debug(50, 1) ("WARNING: failed to resolve %s to a fully qualified hostname\n",
+	    inet_ntoa(sa));
+    }
+    /*
+     * Get the host name and store it in host to return
+     */
+    if (gethostname(host, SQUIDHOSTNAMELEN) < 0) {
+	debug(50, 1) ("WARNING: gethostname failed: %s\n", xstrerror());
+    } else if ((h = gethostbyname(host)) == NULL) {
+	debug(50, 1) ("WARNING: gethostbyname failed for %s\n", host);
+    } else {
+	debug(50, 6) ("getMyHostname: '%s' resolved into '%s'\n",
+	    host, h->h_name);
+	/* DNS lookup successful */
+	/* use the official name from DNS lookup */
+	xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN);
+	present = 1;
+	if (strchr(host, '.'))
+	    return host;
+    }
+    fatal("Could not determine fully qualified hostname.  Please set 'visible_hostname'\n");
+    return NULL;		/* keep compiler happy */
+}
+
+const char *
+uniqueHostname(void)
+{
+    return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname();
+}
+
+void
+safeunlink(const char *s, int quiet)
+{
+    statCounter.syscalls.disk.unlinks++;
+    if (unlink(s) < 0 && !quiet)
+	debug(50, 1) ("safeunlink: Couldn't delete %s: %s\n", s, xstrerror());
+}
+
+/* leave a privilegied section. (Give up any privilegies)
+ * Routines that need privilegies can rap themselves in enter_suid()
+ * and leave_suid()
+ * To give upp all posibilites to gain privilegies use no_suid()
+ */
+void
+leave_suid(void)
+{
+    debug(21, 3) ("leave_suid: PID %d called\n", (int) getpid());
+    if (geteuid() != 0)
+	return;
+    /* Started as a root, check suid option */
+    if (Config.effectiveUser == NULL)
+	return;
+#if HAVE_SETGROUPS
+    setgroups(1, &Config2.effectiveGroupID);
+#endif
+    if (setgid(Config2.effectiveGroupID) < 0)
+	debug(50, 0) ("ALERT: setgid: %s\n", xstrerror());
+    debug(21, 3) ("leave_suid: PID %d giving up root, becoming '%s'\n",
+	(int) getpid(), Config.effectiveUser);
+#if HAVE_SETRESUID
+    if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0)
+	debug(50, 0) ("ALERT: setresuid: %s\n", xstrerror());
+#elif HAVE_SETEUID
+    if (seteuid(Config2.effectiveUserID) < 0)
+	debug(50, 0) ("ALERT: seteuid: %s\n", xstrerror());
+#else
+    if (setuid(Config2.effectiveUserID) < 0)
+	debug(50, 0) ("ALERT: setuid: %s\n", xstrerror());
+#endif
+}
+
+/* Enter a privilegied section */
+void
+enter_suid(void)
+{
+    debug(21, 3) ("enter_suid: PID %d taking root priveleges\n", (int) getpid());
+#if HAVE_SETRESUID
+    setresuid((uid_t)-1, 0, (uid_t)-1);
+#else
+    setuid(0);
+#endif
+}
+
+/* Give up the posibility to gain privilegies.
+ * this should be used before starting a sub process
+ */
+void
+no_suid(void)
+{
+    uid_t uid;
+    leave_suid();
+    uid = geteuid();
+    debug(21, 3) ("leave_suid: PID %d giving up root priveleges forever\n", (int) getpid());
+#if HAVE_SETRESUID
+    if (setresuid(uid, uid, uid) < 0)
+	debug(50, 1) ("no_suid: setresuid: %s\n", xstrerror());
+#else
+    setuid(0);
+    if (setuid(uid) < 0)
+	debug(50, 1) ("no_suid: setuid: %s\n", xstrerror());
+#endif
+}
+
+void
+writePidFile(void)
+{
+    int fd;
+    const char *f = NULL;
+    mode_t old_umask;
+    char buf[32];
+    if ((f = Config.pidFilename) == NULL)
+	return;
+    if (!strcmp(Config.pidFilename, "none"))
+	return;
+    enter_suid();
+    old_umask = umask(022);
+    fd = file_open(f, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT);
+    umask(old_umask);
+    leave_suid();
+    if (fd < 0) {
+	debug(50, 0) ("%s: %s\n", f, xstrerror());
+	debug_trap("Could not write pid file");
+	return;
+    }
+    snprintf(buf, 32, "%d\n", (int) getpid());
+    FD_WRITE_METHOD(fd, buf, strlen(buf));
+    file_close(fd);
+}
+
+
+pid_t
+readPidFile(void)
+{
+    FILE *pid_fp = NULL;
+    const char *f = Config.pidFilename;
+    pid_t pid = -1;
+    int i;
+
+    if (f == NULL || !strcmp(Config.pidFilename, "none")) {
+	fprintf(stderr, "%s: ERROR: No pid file name defined\n", appname);
+	exit(1);
+    }
+    pid_fp = fopen(f, "r");
+    if (pid_fp != NULL) {
+	pid = 0;
+	if (fscanf(pid_fp, "%d", &i) == 1)
+	    pid = (pid_t) i;
+	fclose(pid_fp);
+    } else {
+	if (errno != ENOENT) {
+	    fprintf(stderr, "%s: ERROR: Could not read pid file\n", appname);
+	    fprintf(stderr, "\t%s: %s\n", f, xstrerror());
+	    exit(1);
+	}
+    }
+    return pid;
+}
+
+
+void
+setMaxFD(void)
+{
+#if HAVE_SETRLIMIT
+    /* try to use as many file descriptors as possible */
+    /* System V uses RLIMIT_NOFILE and BSD uses RLIMIT_OFILE */
+    struct rlimit rl;
+#if defined(RLIMIT_NOFILE)
+    if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
+	debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror());
+    } else {
+	rl.rlim_cur = Squid_MaxFD;
+	if (rl.rlim_cur > rl.rlim_max)
+	    Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
+	if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+	    snprintf(tmp_error_buf, ERROR_BUF_SZ,
+		"setrlimit: RLIMIT_NOFILE: %s", xstrerror());
+	    fatal_dump(tmp_error_buf);
+	}
+    }
+#elif defined(RLIMIT_OFILE)
+    if (getrlimit(RLIMIT_OFILE, &rl) < 0) {
+	debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror());
+    } else {
+	rl.rlim_cur = Squid_MaxFD;
+	if (rl.rlim_cur > rl.rlim_max)
+	    Squid_MaxFD = rl.rlim_cur = rl.rlim_max;
+	if (setrlimit(RLIMIT_OFILE, &rl) < 0) {
+	    snprintf(tmp_error_buf, ERROR_BUF_SZ,
+		"setrlimit: RLIMIT_OFILE: %s", xstrerror());
+	    fatal_dump(tmp_error_buf);
+	}
+    }
+#endif
+#else /* HAVE_SETRLIMIT */
+    debug(21, 1) ("setMaxFD: Cannot increase: setrlimit() not supported on this system\n");
+#endif /* HAVE_SETRLIMIT */
+
+#if HAVE_SETRLIMIT && defined(RLIMIT_DATA)
+    if (getrlimit(RLIMIT_DATA, &rl) < 0) {
+	debug(50, 0) ("getrlimit: RLIMIT_DATA: %s\n", xstrerror());
+    } else if (rl.rlim_max > rl.rlim_cur) {
+	rl.rlim_cur = rl.rlim_max;	/* set it to the max */
+	if (setrlimit(RLIMIT_DATA, &rl) < 0) {
+	    snprintf(tmp_error_buf, ERROR_BUF_SZ,
+		"setrlimit: RLIMIT_DATA: %s", xstrerror());
+	    fatal_dump(tmp_error_buf);
+	}
+    }
+#endif /* RLIMIT_DATA */
+#if HAVE_SETRLIMIT && defined(RLIMIT_VMEM)
+    if (getrlimit(RLIMIT_VMEM, &rl) < 0) {
+	debug(50, 0) ("getrlimit: RLIMIT_VMEM: %s\n", xstrerror());
+    } else if (rl.rlim_max > rl.rlim_cur) {
+	rl.rlim_cur = rl.rlim_max;	/* set it to the max */
+	if (setrlimit(RLIMIT_VMEM, &rl) < 0) {
+	    snprintf(tmp_error_buf, ERROR_BUF_SZ,
+		"setrlimit: RLIMIT_VMEM: %s", xstrerror());
+	    fatal_dump(tmp_error_buf);
+	}
+    }
+#endif /* RLIMIT_VMEM */
+}
+
+time_t
+getCurrentTime(void)
+{
+#if GETTIMEOFDAY_NO_TZP
+    gettimeofday(&current_time);
+#else
+    gettimeofday(&current_time, NULL);
+#endif
+    current_dtime = (double) current_time.tv_sec +
+	(double) current_time.tv_usec / 1000000.0;
+    return squid_curtime = current_time.tv_sec;
+}
+
+int
+percent(int a, int b)
+{
+    return b ? ((int) (100.0 * a / b + 0.5)) : 0;
+}
+
+double
+dpercent(double a, double b)
+{
+    return b ? (100.0 * a / b) : 0.0;
+}
+
+void
+squid_signal(int sig, SIGHDLR * func, int flags)
+{
+#if HAVE_SIGACTION
+    struct sigaction sa;
+    sa.sa_handler = func;
+    sa.sa_flags = flags;
+    sigemptyset(&sa.sa_mask);
+    if (sigaction(sig, &sa, NULL) < 0)
+	debug(50, 0) ("sigaction: sig=%d func=%p: %s\n", sig, func, xstrerror());
+#else
+    signal(sig, func);
+#endif
+}
+
+struct in_addr
+inaddrFromHostent(const struct hostent *hp)
+{
+    struct in_addr s;
+    xmemcpy(&s.s_addr, hp->h_addr, sizeof(s.s_addr));
+    return s;
+}
+
+double
+doubleAverage(double cur, double newD, int N, int max)
+{
+    if (N > max)
+	N = max;
+    return (cur * (N - 1.0) + newD) / N;
+}
+
+int
+intAverage(int cur, int newI, int n, int max)
+{
+    if (n > max)
+	n = max;
+    return (cur * (n - 1) + newI) / n;
+}
+
+void
+logsFlush(void)
+{
+    if (debug_log)
+	fflush(debug_log);
+}
+
+const char *
+checkNullString(const char *p)
+{
+    return p ? p : "(NULL)";
+}
+
+dlink_node *
+dlinkNodeNew()
+{
+    if (dlink_node_pool == NULL)
+	dlink_node_pool = memPoolCreate("Dlink list nodes", sizeof(dlink_node));
+    /* where should we call memPoolDestroy(dlink_node_pool); */
+    return (dlink_node *)memPoolAlloc(dlink_node_pool);
+}
+
+/* the node needs to be unlinked FIRST */
+void
+dlinkNodeDelete(dlink_node * m)
+{
+    if (m == NULL)
+	return;
+    memPoolFree(dlink_node_pool, m);
+}
+
+void
+dlinkAdd(void *data, dlink_node * m, dlink_list * list)
+{
+    m->data = data;
+    m->prev = NULL;
+    m->next = list->head;
+    if (list->head)
+	list->head->prev = m;
+    list->head = m;
+    if (list->tail == NULL)
+	list->tail = m;
+}
+
+void
+dlinkAddAfter(void *data, dlink_node * m, dlink_node * n, dlink_list * list)
+{
+    m->data = data;
+    m->prev = n;
+    m->next = n->next;
+    if (n->next)
+	n->next->prev = m;
+    else {
+	assert(list->tail == n);
+	list->tail = m;
+    }
+    n->next = m;
+}
+
+void
+dlinkAddTail(void *data, dlink_node * m, dlink_list * list)
+{
+    m->data = data;
+    m->next = NULL;
+    m->prev = list->tail;
+    if (list->tail)
+	list->tail->next = m;
+    list->tail = m;
+    if (list->head == NULL)
+	list->head = m;
+}
+
+void
+dlinkDelete(dlink_node * m, dlink_list * list)
+{
+    if (m->next)
+	m->next->prev = m->prev;
+    if (m->prev)
+	m->prev->next = m->next;
+    if (m == list->head)
+	list->head = m->next;
+    if (m == list->tail)
+	list->tail = m->prev;
+    m->next = m->prev = NULL;
+}
+
+void
+kb_incr(kb_t * k, size_t v)
+{
+    k->bytes += v;
+    k->kb += (k->bytes >> 10);
+    k->bytes &= 0x3FF;
+}
+
+void
+debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm)
+{
+    MemBuf mb;
+    Packer p;
+    assert(label && obj && pm);
+    memBufDefInit(&mb);
+    packerToMemInit(&p, &mb);
+    (*pm) (obj, &p);
+    debug(section, level) ("%s%s", label, mb.buf);
+    packerClean(&p);
+    memBufClean(&mb);
+}
+
+int
+stringHasWhitespace(const char *s)
+{
+    return strpbrk(s, w_space) != NULL;
+}
+
+void
+linklistPush(link_list ** L, void *p)
+{
+    link_list *l = (link_list *)memAllocate(MEM_LINK_LIST);
+    l->next = NULL;
+    l->ptr = p;
+    while (*L)
+	L = &(*L)->next;
+    *L = l;
+}
+
+void *
+linklistShift(link_list ** L)
+{
+    void *p;
+    link_list *l;
+    if (NULL == *L)
+	return NULL;
+    l = *L;
+    p = l->ptr;
+    *L = (*L)->next;
+    memFree(l, MEM_LINK_LIST);
+    return p;
+}
+
+/*
+ * Same as rename(2) but complains if something goes wrong;
+ * the caller is responsible for handing and explaining the 
+ * consequences of errors.
+ */
+int
+xrename(const char *from, const char *to)
+{
+    debug(21, 2) ("xrename: renaming %s to %s\n", from, to);
+#ifdef _SQUID_MSWIN_
+    remove(to);
+#endif
+    if (0 == rename(from, to))
+	return 0;
+    debug(21, errno == ENOENT ? 2 : 1) ("xrename: Cannot rename %s to %s: %s\n",
+	from, to, xstrerror());
+    return -1;
+}
+
+int
+stringHasCntl(const char *s)
+{
+    unsigned char c;
+    while ((c = (unsigned char) *s++) != '\0') {
+	if (c <= 0x1f)
+	    return 1;
+	if (c >= 0x7f && c <= 0x9f)
+	    return 1;
+    }
+    return 0;
+}
+
+/*
+ * isPowTen returns true if its argument is an integer power of
+ * 10.  Its used for logging of certain error messages that can
+ * occur often, but that we don't want to fill cache.log with.
+ */
+int
+isPowTen(int count)
+{
+    double x = log(count) / log(10.0);
+    if (0.0 != x - (double) (int) x)
+	return 0;
+    return 1;
+}
+
+void
+parseEtcHosts(void)
+{
+    FILE *fp;
+    char buf[1024];
+    char buf2[512];
+    char *nt = buf;
+    char *lt = buf;
+
+    if (NULL == Config.etcHostsPath)
+	return;
+    if (0 == strcmp(Config.etcHostsPath, "none"))
+	return;
+    fp = fopen(Config.etcHostsPath, "r");
+    if (fp == NULL) {
+	debug(1, 1) ("parseEtcHosts: %s: %s\n",
+	    Config.etcHostsPath, xstrerror());
+	return;
+    }
+#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
+    setmode(fileno(fp), O_TEXT);
+#endif
+    while (fgets(buf, 1024, fp)) {	/* for each line */
+	wordlist *hosts = NULL;
+	char *addr;
+	if (buf[0] == '#')	/* MS-windows likes to add comments */
+	    continue;
+	lt = buf;
+	addr = buf;
+	debug(1, 5) ("etc_hosts: line is '%s'\n", buf);
+	nt = strpbrk(lt, w_space);
+	if (nt == NULL)		/* empty line */
+	    continue;
+	*nt = '\0';		/* null-terminate the address */
+	debug(1, 5) ("etc_hosts: address is '%s'\n", addr);
+	lt = nt + 1;
+	while ((nt = strpbrk(lt, w_space))) {
+	    char *host = NULL;
+	    if (nt == lt) {	/* multiple spaces */
+		debug(1, 5) ("etc_hosts: multiple spaces, skipping\n");
+		lt = nt + 1;
+		continue;
+	    }
+	    *nt = '\0';
+	    debug(1, 5) ("etc_hosts: got hostname '%s'\n", lt);
+	    if (Config.appendDomain && !strchr(lt, '.')) {
+		/* I know it's ugly, but it's only at reconfig */
+		strncpy(buf2, lt, 512);
+		strncat(buf2, Config.appendDomain, 512 - strlen(lt));
+		host = buf2;
+	    } else {
+		host = lt;
+	    }
+	    if (ipcacheAddEntryFromHosts(host, addr) != 0)
+		goto skip;	/* invalid address, continuing is useless */
+	    wordlistAdd(&hosts, host);
+	    lt = nt + 1;
+	}
+	fqdncacheAddEntryFromHosts(addr, hosts);
+      skip:
+	wordlistDestroy(&hosts);
+    }
+}
+
+int
+getMyPort(void)
+{
+    if (Config.Sockaddr.http)
+	return ntohs(Config.Sockaddr.http->s.sin_port);
+#if USE_SSL
+    if (Config.Sockaddr.https)
+	return ntohs(Config.Sockaddr.https->s.sin_port);
+#endif
+    fatal("No port defined");
+    return 0;			/* NOT REACHED */
+}
+
+/*
+ * Similar to strtok, but has some rudimentary knowledge
+ * of quoting
+ */
+char *
+strwordtok(char *buf, char **t)
+{
+    unsigned char *word = NULL;
+    unsigned char *p = (unsigned char *) buf;
+    unsigned char *d;
+    unsigned char ch;
+    int quoted = 0;
+    if (!p)
+	p = (unsigned char *) *t;
+    if (!p)
+	goto error;
+    while (*p && isspace(*p))
+	p++;
+    if (!*p)
+	goto error;
+    word = d = p;
+    while ((ch = *p)) {
+	switch (ch) {
+	case '\\':
+	    p++;
+	    *d++ = ch = *p;
+	    if (ch)
+		p++;
+	    break;
+	case '"':
+	    quoted = !quoted;
+	    p++;
+	default:
+	    if (!quoted && isspace(*p)) {
+		p++;
+		goto done;
+	    }
+	    *d++ = *p++;
+	    break;
+	}
+    }
+  done:
+    *d++ = '\0';
+  error:
+    *t = (char *) p;
+    return (char *) word;
+}
+
+/*
+ * Inverse of strwordtok. Quotes a word if needed
+ */
+void
+strwordquote(MemBuf * mb, const char *str)
+{
+    int quoted = 0;
+    if (strchr(str, ' ')) {
+	quoted = 1;
+	memBufAppend(mb, "\"", 1);
+    }
+    while (*str) {
+	int l = strcspn(str, "\"\\");
+	memBufAppend(mb, str, l);
+	str += l;
+	while (*str == '"' || *str == '\\') {
+	    memBufAppend(mb, "\\", 1);
+	    memBufAppend(mb, str, 1);
+	    str++;
+	}
+    }
+    if (quoted)
+	memBufAppend(mb, "\"", 1);
+}
--- squid/src/url.c	Wed Feb 14 01:07:38 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,637 +0,0 @@
-
-/*
- * $Id: url.c,v 1.11 2002/09/12 20:56:25 squidadm Exp $
- *
- * DEBUG: section 23    URL Parsing
- * AUTHOR: Duane Wessels
- *
- * SQUID Web Proxy Cache          http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- *  Squid is the result of efforts by numerous individuals from
- *  the Internet community; see the CONTRIBUTORS file for full
- *  details.   Many organizations have provided support for Squid's
- *  development; see the SPONSORS file for full details.  Squid is
- *  Copyrighted (C) 2001 by the Regents of the University of
- *  California; see the COPYRIGHT file for full details.  Squid
- *  incorporates software developed and/or copyrighted by other
- *  sources; see the CREDITS file for full details.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *  
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *  
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
- *
- */
-
-#include "squid.h"
-
-const char *RequestMethodStr[] =
-{
-    "NONE",
-    "GET",
-    "POST",
-    "PUT",
-    "HEAD",
-    "CONNECT",
-    "TRACE",
-    "PURGE",
-    "OPTIONS",
-    "DELETE",
-    "PROPFIND",
-    "PROPPATCH",
-    "MKCOL",
-    "COPY",
-    "MOVE",
-    "LOCK",
-    "UNLOCK",
-    "BMOVE",
-    "BDELETE",
-    "BPROPFIND",
-    "BPROPPATCH",
-    "BCOPY",
-    "SEARCH",
-    "SUBSCRIBE",
-    "UNSUBSCRIBE",
-    "POLL",
-    "%EXT00",
-    "%EXT01",
-    "%EXT02",
-    "%EXT03",
-    "%EXT04",
-    "%EXT05",
-    "%EXT06",
-    "%EXT07",
-    "%EXT08",
-    "%EXT09",
-    "%EXT10",
-    "%EXT11",
-    "%EXT12",
-    "%EXT13",
-    "%EXT14",
-    "%EXT15",
-    "%EXT16",
-    "%EXT17",
-    "%EXT18",
-    "%EXT19",
-    "ERROR"
-};
-
-const char *ProtocolStr[] =
-{
-    "NONE",
-    "http",
-    "ftp",
-    "gopher",
-    "wais",
-    "cache_object",
-    "icp",
-#if USE_HTCP
-    "htcp",
-#endif
-    "urn",
-    "whois",
-    "internal",
-    "https",
-    "TOTAL"
-};
-
-static request_t *urnParse(method_t method, char *urn);
-static const char *const valid_hostname_chars =
-#if ALLOW_HOSTNAME_UNDERSCORES
-"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-"abcdefghijklmnopqrstuvwxyz"
-"0123456789-._";
-#else
-"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-"abcdefghijklmnopqrstuvwxyz"
-"0123456789-.";
-#endif
-
-/* convert %xx in url string to a character 
- * Allocate a new string and return a pointer to converted string */
-
-char *
-url_convert_hex(char *org_url, int allocate)
-{
-    static char code[] = "00";
-    char *url = NULL;
-    char *s = NULL;
-    char *t = NULL;
-    url = allocate ? (char *) xstrdup(org_url) : org_url;
-    if ((int) strlen(url) < 3 || !strchr(url, '%'))
-	return url;
-    for (s = t = url; *s; s++) {
-	if (*s == '%' && *(s + 1) && *(s + 2)) {
-	    code[0] = *(++s);
-	    code[1] = *(++s);
-	    *t++ = (char) strtol(code, NULL, 16);
-	} else {
-	    *t++ = *s;
-	}
-    }
-    do {
-	*t++ = *s;
-    } while (*s++);
-    return url;
-}
-
-void
-urlInitialize(void)
-{
-    debug(23, 5) ("urlInitialize: Initializing...\n");
-    assert(sizeof(ProtocolStr) == (PROTO_MAX + 1) * sizeof(char *));
-    memset(&null_request_flags, '\0', sizeof(null_request_flags));
-    /*
-     * These test that our matchDomainName() function works the
-     * way we expect it to.
-     */
-    assert(0 == matchDomainName("foo.com", "foo.com"));
-    assert(0 == matchDomainName(".foo.com", "foo.com"));
-    assert(0 == matchDomainName("foo.com", ".foo.com"));
-    assert(0 == matchDomainName(".foo.com", ".foo.com"));
-    assert(0 == matchDomainName("x.foo.com", ".foo.com"));
-    assert(0 != matchDomainName("x.foo.com", "foo.com"));
-    assert(0 != matchDomainName("foo.com", "x.foo.com"));
-    assert(0 != matchDomainName("bar.com", "foo.com"));
-    assert(0 != matchDomainName(".bar.com", "foo.com"));
-    assert(0 != matchDomainName(".bar.com", ".foo.com"));
-    assert(0 != matchDomainName("bar.com", ".foo.com"));
-    assert(0 < matchDomainName("zzz.com", "foo.com"));
-    assert(0 > matchDomainName("aaa.com", "foo.com"));
-    assert(0 == matchDomainName("FOO.com", "foo.COM"));
-    assert(0 < matchDomainName("bfoo.com", "afoo.com"));
-    assert(0 > matchDomainName("afoo.com", "bfoo.com"));
-    assert(0 < matchDomainName("x-foo.com", ".foo.com"));
-    /* more cases? */
-}
-
-method_t
-urlParseMethod(const char *s)
-{
-    method_t method = METHOD_NONE;
-    /*
-     * This check for '%' makes sure that we don't
-     * match one of the extension method placeholders,
-     * which have the form %EXT[0-9][0-9]
-     */
-    if (*s == '%')
-	return METHOD_NONE;
-    for (method++; method < METHOD_ENUM_END; method++) {
-	if (0 == strcasecmp(s, RequestMethodStr[method]))
-	    return method;
-    }
-    return METHOD_NONE;
-}
-
-
-protocol_t
-urlParseProtocol(const char *s)
-{
-    /* test common stuff first */
-    if (strcasecmp(s, "http") == 0)
-	return PROTO_HTTP;
-    if (strcasecmp(s, "ftp") == 0)
-	return PROTO_FTP;
-    if (strcasecmp(s, "https") == 0)
-	return PROTO_HTTPS;
-    if (strcasecmp(s, "file") == 0)
-	return PROTO_FTP;
-    if (strcasecmp(s, "gopher") == 0)
-	return PROTO_GOPHER;
-    if (strcasecmp(s, "wais") == 0)
-	return PROTO_WAIS;
-    if (strcasecmp(s, "cache_object") == 0)
-	return PROTO_CACHEOBJ;
-    if (strcasecmp(s, "urn") == 0)
-	return PROTO_URN;
-    if (strcasecmp(s, "whois") == 0)
-	return PROTO_WHOIS;
-    if (strcasecmp(s, "internal") == 0)
-	return PROTO_INTERNAL;
-    return PROTO_NONE;
-}
-
-
-int
-urlDefaultPort(protocol_t p)
-{
-    switch (p) {
-    case PROTO_HTTP:
-	return 80;
-    case PROTO_HTTPS:
-	return 443;
-    case PROTO_FTP:
-	return 21;
-    case PROTO_GOPHER:
-	return 70;
-    case PROTO_WAIS:
-	return 210;
-    case PROTO_CACHEOBJ:
-    case PROTO_INTERNAL:
-	return CACHE_HTTP_PORT;
-    case PROTO_WHOIS:
-	return 43;
-    default:
-	return 0;
-    }
-}
-
-request_t *
-urlParse(method_t method, char *url)
-{
-    LOCAL_ARRAY(char, proto, MAX_URL);
-    LOCAL_ARRAY(char, login, MAX_URL);
-    LOCAL_ARRAY(char, host, MAX_URL);
-    LOCAL_ARRAY(char, urlpath, MAX_URL);
-    request_t *request = NULL;
-    char *t = NULL;
-    char *q = NULL;
-    int port;
-    protocol_t protocol = PROTO_NONE;
-    int l;
-    proto[0] = host[0] = urlpath[0] = login[0] = '\0';
-
-    if ((l = strlen(url)) + Config.appendDomainLen > (MAX_URL - 1)) {
-	/* terminate so it doesn't overflow other buffers */
-	*(url + (MAX_URL >> 1)) = '\0';
-	debug(23, 1) ("urlParse: URL too large (%d bytes)\n", l);
-	return NULL;
-    }
-    if (method == METHOD_CONNECT) {
-	port = CONNECT_PORT;
-	if (sscanf(url, "%[^:]:%d", host, &port) < 1)
-	    return NULL;
-    } else if (!strncmp(url, "urn:", 4)) {
-	return urnParse(method, url);
-    } else {
-	if (sscanf(url, "%[^:]://%[^/]%[^\r\n]", proto, host, urlpath) < 2)
-	    return NULL;
-	protocol = urlParseProtocol(proto);
-	port = urlDefaultPort(protocol);
-	/* Is there any login informaiton? */
-	if ((t = strrchr(host, '@'))) {
-	    strcpy((char *) login, (char *) host);
-	    t = strrchr(login, '@');
-	    *t = 0;
-	    strcpy((char *) host, t + 1);
-	}
-	if ((t = strrchr(host, ':'))) {
-	    *t++ = '\0';
-	    if (*t != '\0')
-		port = atoi(t);
-	}
-    }
-    for (t = host; *t; t++)
-	*t = xtolower(*t);
-    if (stringHasWhitespace(host)) {
-	if (URI_WHITESPACE_STRIP == Config.uri_whitespace) {
-	    t = q = host;
-	    while (*t) {
-		if (!xisspace(*t))
-		    *q++ = *t;
-		t++;
-	    }
-	    *q = '\0';
-	}
-    }
-    if (Config.onoff.check_hostnames && strspn(host, valid_hostname_chars) != strlen(host)) {
-	debug(23, 1) ("urlParse: Illegal character in hostname '%s'\n", host);
-	return NULL;
-    }
-    /* remove trailing dots from hostnames */
-    while ((l = strlen(host)) > 0 && host[--l] == '.')
-	host[l] = '\0';
-    /* remove duplicate dots */
-    while ((t = strstr(host, "..")))
-	xmemmove(t, t + 1, strlen(t));
-    if (Config.appendDomain && !strchr(host, '.'))
-	strncat(host, Config.appendDomain, SQUIDHOSTNAMELEN);
-    if (port < 1 || port > 65535) {
-	debug(23, 3) ("urlParse: Invalid port '%d'\n", port);
-	return NULL;
-    }
-#ifdef HARDCODE_DENY_PORTS
-    /* These ports are filtered in the default squid.conf, but
-     * maybe someone wants them hardcoded... */
-    if (port == 7 || port == 9 || port == 19) {
-	debug(23, 0) ("urlParse: Deny access to port %d\n", port);
-	return NULL;
-    }
-#endif
-    if (stringHasWhitespace(urlpath)) {
-	debug(23, 2) ("urlParse: URI has whitespace: {%s}\n", url);
-	switch (Config.uri_whitespace) {
-	case URI_WHITESPACE_DENY:
-	    return NULL;
-	case URI_WHITESPACE_ALLOW:
-	    break;
-	case URI_WHITESPACE_ENCODE:
-	    t = rfc1738_escape_unescaped(urlpath);
-	    xstrncpy(urlpath, t, MAX_URL);
-	    break;
-	case URI_WHITESPACE_CHOP:
-	    *(urlpath + strcspn(urlpath, w_space)) = '\0';
-	    break;
-	case URI_WHITESPACE_STRIP:
-	default:
-	    t = q = urlpath;
-	    while (*t) {
-		if (!xisspace(*t))
-		    *q++ = *t;
-		t++;
-	    }
-	    *q = '\0';
-	}
-    }
-    request = requestCreate(method, protocol, urlpath);
-    xstrncpy(request->host, host, SQUIDHOSTNAMELEN);
-    xstrncpy(request->login, login, MAX_LOGIN_SZ);
-    request->port = (u_short) port;
-    return request;
-}
-
-static request_t *
-urnParse(method_t method, char *urn)
-{
-    debug(50, 5) ("urnParse: %s\n", urn);
-    return requestCreate(method, PROTO_URN, urn + 4);
-}
-
-const char *
-urlCanonical(request_t * request)
-{
-    LOCAL_ARRAY(char, portbuf, 32);
-    LOCAL_ARRAY(char, urlbuf, MAX_URL);
-    if (request->canonical)
-	return request->canonical;
-    if (request->protocol == PROTO_URN) {
-	snprintf(urlbuf, MAX_URL, "urn:%s", strBuf(request->urlpath));
-    } else {
-	switch (request->method) {
-	case METHOD_CONNECT:
-	    snprintf(urlbuf, MAX_URL, "%s:%d", request->host, request->port);
-	    break;
-	default:
-	    portbuf[0] = '\0';
-	    if (request->port != urlDefaultPort(request->protocol))
-		snprintf(portbuf, 32, ":%d", request->port);
-	    snprintf(urlbuf, MAX_URL, "%s://%s%s%s%s%s",
-		ProtocolStr[request->protocol],
-		request->login,
-		*request->login ? "@" : null_string,
-		request->host,
-		portbuf,
-		strBuf(request->urlpath));
-	    break;
-	}
-    }
-    return (request->canonical = xstrdup(urlbuf));
-}
-
-char *
-urlCanonicalClean(const request_t * request)
-{
-    LOCAL_ARRAY(char, buf, MAX_URL);
-    LOCAL_ARRAY(char, portbuf, 32);
-    LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1);
-    char *t;
-    if (request->protocol == PROTO_URN) {
-	snprintf(buf, MAX_URL, "urn:%s", strBuf(request->urlpath));
-    } else {
-	switch (request->method) {
-	case METHOD_CONNECT:
-	    snprintf(buf, MAX_URL, "%s:%d", request->host, request->port);
-	    break;
-	default:
-	    portbuf[0] = '\0';
-	    if (request->port != urlDefaultPort(request->protocol))
-		snprintf(portbuf, 32, ":%d", request->port);
-	    loginbuf[0] = '\0';
-	    if ((int) strlen(request->login) > 0) {
-		strcpy(loginbuf, request->login);
-		if ((t = strchr(loginbuf, ':')))
-		    *t = '\0';
-		strcat(loginbuf, "@");
-	    }
-	    snprintf(buf, MAX_URL, "%s://%s%s%s%s",
-		ProtocolStr[request->protocol],
-		loginbuf,
-		request->host,
-		portbuf,
-		strBuf(request->urlpath));
-	    /*
-	     * strip arguments AFTER a question-mark
-	     */
-	    if (Config.onoff.strip_query_terms)
-		if ((t = strchr(buf, '?')))
-		    *(++t) = '\0';
-	    break;
-	}
-    }
-    if (stringHasCntl(buf))
-	xstrncpy(buf, rfc1738_escape_unescaped(buf), MAX_URL);
-    return buf;
-}
-
-/*
- * matchDomainName() compares a hostname with a domainname according
- * to the following rules:
- * 
- *    HOST          DOMAIN        MATCH?
- * ------------- -------------    ------
- *    foo.com       foo.com         YES
- *   .foo.com       foo.com         YES
- *  x.foo.com       foo.com          NO
- *    foo.com      .foo.com         YES
- *   .foo.com      .foo.com         YES
- *  x.foo.com      .foo.com         YES
- *
- *  We strip leading dots on hosts (but not domains!) so that
- *  ".foo.com" is is always the same as "foo.com".
- *
- *  Return values:
- *     0 means the host matches the domain
- *     1 means the host is greater than the domain
- *    -1 means the host is less than the domain
- */
-
-int
-matchDomainName(const char *h, const char *d)
-{
-    int dl;
-    int hl;
-    while ('.' == *h)
-	h++;
-    hl = strlen(h);
-    dl = strlen(d);
-    /*
-     * Start at the ends of the two strings and work towards the
-     * beginning.
-     */
-    while (xtolower(h[--hl]) == xtolower(d[--dl])) {
-	if (hl == 0 && dl == 0) {
-	    /*
-	     * We made it all the way to the beginning of both
-	     * strings without finding any difference.
-	     */
-	    return 0;
-	}
-	if (0 == hl) {
-	    /* 
-	     * The host string is shorter than the domain string.
-	     * There is only one case when this can be a match.
-	     * If the domain is just one character longer, and if
-	     * that character is a leading '.' then we call it a
-	     * match.
-	     */
-	    if (1 == dl && '.' == d[0])
-		return 0;
-	    else
-		return -1;
-	}
-	if (0 == dl) {
-	    /*
-	     * The domain string is shorter than the host string.
-	     * This is a match only if the first domain character
-	     * is a leading '.'.
-	     */
-	    if ('.' == d[0])
-		return 0;
-	    else
-		return 1;
-	}
-    }
-    /*
-     * We found different characters in the same position (from the end).
-     */
-    /*
-     * If one of those character is '.' then its special.  In order
-     * for splay tree sorting to work properly, "x-foo.com" must
-     * be greater than ".foo.com" even though '-' is less than '.'.
-     */
-    if ('.' == d[dl])
-	return 1;
-    if ('.' == h[hl])
-	return -1;
-    return (xtolower(h[hl]) - xtolower(d[dl]));
-}
-
-int
-urlCheckRequest(const request_t * r)
-{
-    int rc = 0;
-    /* protocol "independent" methods */
-    if (r->method == METHOD_CONNECT)
-	return 1;
-    if (r->method == METHOD_TRACE)
-	return 1;
-    if (r->method == METHOD_PURGE)
-	return 1;
-    /* does method match the protocol? */
-    switch (r->protocol) {
-    case PROTO_URN:
-    case PROTO_HTTP:
-    case PROTO_CACHEOBJ:
-	rc = 1;
-	break;
-    case PROTO_FTP:
-	if (r->method == METHOD_PUT)
-	    rc = 1;
-    case PROTO_GOPHER:
-    case PROTO_WAIS:
-    case PROTO_WHOIS:
-	if (r->method == METHOD_GET)
-	    rc = 1;
-	else if (r->method == METHOD_HEAD)
-	    rc = 1;
-	break;
-    case PROTO_HTTPS:
-#ifdef USE_SSL
-	rc = 1;
-	break;
-#else
-	/*
-	 * Squid can't originate an SSL connection, so it should
-	 * never receive an "https:" URL.  It should always be
-	 * CONNECT instead.
-	 */
-	rc = 0;
-#endif
-    default:
-	break;
-    }
-    return rc;
-}
-
-/*
- * Quick-n-dirty host extraction from a URL.  Steps:
- *      Look for a colon
- *      Skip any '/' after the colon
- *      Copy the next SQUID_MAXHOSTNAMELEN bytes to host[]
- *      Look for an ending '/' or ':' and terminate
- *      Look for login info preceeded by '@'
- */
-char *
-urlHostname(const char *url)
-{
-    LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN);
-    char *t;
-    host[0] = '\0';
-    if (NULL == (t = strchr(url, ':')))
-	return NULL;
-    t++;
-    while (*t != '\0' && *t == '/')
-	t++;
-    xstrncpy(host, t, SQUIDHOSTNAMELEN);
-    if ((t = strchr(host, '/')))
-	*t = '\0';
-    if ((t = strchr(host, ':')))
-	*t = '\0';
-    if ((t = strrchr(host, '@'))) {
-	t++;
-	xmemmove(host, t, strlen(t) + 1);
-    }
-    return host;
-}
-
-static void
-urlExtMethodAdd(const char *mstr)
-{
-    method_t method = 0;
-    for (method++; method < METHOD_ENUM_END; method++) {
-	if (0 == strcmp(mstr, RequestMethodStr[method])) {
-	    debug(23, 2) ("Extension method '%s' already exists\n", mstr);
-	    return;
-	}
-	if (0 != strncmp("%EXT", RequestMethodStr[method], 4))
-	    continue;
-	/* Don't free statically allocated "%EXTnn" string */
-	RequestMethodStr[method] = xstrdup(mstr);
-	debug(23, 1) ("Extension method '%s' added, enum=%d\n", mstr, (int) method);
-	return;
-    }
-    debug(23, 1) ("WARNING: Could not add new extension method '%s' due to lack of array space\n", mstr);
-}
-
-void
-urlExtMethodConfigure(void)
-{
-    wordlist *w = Config.ext_methods;
-    while (w) {
-	char *s;
-	for (s = w->key; *s; s++)
-	    *s = xtoupper(*s);
-	urlExtMethodAdd(w->key);
-	w = w->next;
-    }
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/url.cc	Wed Feb 14 01:07:38 2007
@@ -0,0 +1,644 @@
+
+/*
+ * $Id: url.cc,v 1.1.2.1 2002/10/09 14:36:48 rbcollins Exp $
+ *
+ * DEBUG: section 23    URL Parsing
+ * AUTHOR: Duane Wessels
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+const char *RequestMethodStr[] =
+{
+    "NONE",
+    "GET",
+    "POST",
+    "PUT",
+    "HEAD",
+    "CONNECT",
+    "TRACE",
+    "PURGE",
+    "OPTIONS",
+    "DELETE",
+    "PROPFIND",
+    "PROPPATCH",
+    "MKCOL",
+    "COPY",
+    "MOVE",
+    "LOCK",
+    "UNLOCK",
+    "BMOVE",
+    "BDELETE",
+    "BPROPFIND",
+    "BPROPPATCH",
+    "BCOPY",
+    "SEARCH",
+    "SUBSCRIBE",
+    "UNSUBSCRIBE",
+    "POLL",
+    "%EXT00",
+    "%EXT01",
+    "%EXT02",
+    "%EXT03",
+    "%EXT04",
+    "%EXT05",
+    "%EXT06",
+    "%EXT07",
+    "%EXT08",
+    "%EXT09",
+    "%EXT10",
+    "%EXT11",
+    "%EXT12",
+    "%EXT13",
+    "%EXT14",
+    "%EXT15",
+    "%EXT16",
+    "%EXT17",
+    "%EXT18",
+    "%EXT19",
+    "ERROR"
+};
+
+const char *ProtocolStr[] =
+{
+    "NONE",
+    "http",
+    "ftp",
+    "gopher",
+    "wais",
+    "cache_object",
+    "icp",
+#if USE_HTCP
+    "htcp",
+#endif
+    "urn",
+    "whois",
+    "internal",
+    "https",
+    "TOTAL"
+};
+
+static request_t *urnParse(method_t method, char *urn);
+static const char *const valid_hostname_chars =
+#if ALLOW_HOSTNAME_UNDERSCORES
+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+"abcdefghijklmnopqrstuvwxyz"
+"0123456789-._";
+#else
+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+"abcdefghijklmnopqrstuvwxyz"
+"0123456789-.";
+#endif
+
+/* convert %xx in url string to a character 
+ * Allocate a new string and return a pointer to converted string */
+
+char *
+url_convert_hex(char *org_url, int allocate)
+{
+    static char code[] = "00";
+    char *url = NULL;
+    char *s = NULL;
+    char *t = NULL;
+    url = allocate ? (char *) xstrdup(org_url) : org_url;
+    if ((int) strlen(url) < 3 || !strchr(url, '%'))
+	return url;
+    for (s = t = url; *s; s++) {
+	if (*s == '%' && *(s + 1) && *(s + 2)) {
+	    code[0] = *(++s);
+	    code[1] = *(++s);
+	    *t++ = (char) strtol(code, NULL, 16);
+	} else {
+	    *t++ = *s;
+	}
+    }
+    do {
+	*t++ = *s;
+    } while (*s++);
+    return url;
+}
+
+void
+urlInitialize(void)
+{
+    debug(23, 5) ("urlInitialize: Initializing...\n");
+    assert(sizeof(ProtocolStr) == (PROTO_MAX + 1) * sizeof(char *));
+    memset(&null_request_flags, '\0', sizeof(null_request_flags));
+    /*
+     * These test that our matchDomainName() function works the
+     * way we expect it to.
+     */
+    assert(0 == matchDomainName("foo.com", "foo.com"));
+    assert(0 == matchDomainName(".foo.com", "foo.com"));
+    assert(0 == matchDomainName("foo.com", ".foo.com"));
+    assert(0 == matchDomainName(".foo.com", ".foo.com"));
+    assert(0 == matchDomainName("x.foo.com", ".foo.com"));
+    assert(0 != matchDomainName("x.foo.com", "foo.com"));
+    assert(0 != matchDomainName("foo.com", "x.foo.com"));
+    assert(0 != matchDomainName("bar.com", "foo.com"));
+    assert(0 != matchDomainName(".bar.com", "foo.com"));
+    assert(0 != matchDomainName(".bar.com", ".foo.com"));
+    assert(0 != matchDomainName("bar.com", ".foo.com"));
+    assert(0 < matchDomainName("zzz.com", "foo.com"));
+    assert(0 > matchDomainName("aaa.com", "foo.com"));
+    assert(0 == matchDomainName("FOO.com", "foo.COM"));
+    assert(0 < matchDomainName("bfoo.com", "afoo.com"));
+    assert(0 > matchDomainName("afoo.com", "bfoo.com"));
+    assert(0 < matchDomainName("x-foo.com", ".foo.com"));
+    /* more cases? */
+}
+
+method_t &operator++ (method_t &aMethod)
+{
+    aMethod = (method_t)(++(int)aMethod);
+    return aMethod;
+}
+
+
+method_t
+urlParseMethod(const char *s)
+{
+    method_t method = METHOD_NONE;
+    /*
+     * This check for '%' makes sure that we don't
+     * match one of the extension method placeholders,
+     * which have the form %EXT[0-9][0-9]
+     */
+    if (*s == '%')
+	return METHOD_NONE;
+    for (++method; method < METHOD_ENUM_END; ++method) {
+	if (0 == strcasecmp(s, RequestMethodStr[method]))
+	    return method;
+    }
+    return METHOD_NONE;
+}
+
+
+protocol_t
+urlParseProtocol(const char *s)
+{
+    /* test common stuff first */
+    if (strcasecmp(s, "http") == 0)
+	return PROTO_HTTP;
+    if (strcasecmp(s, "ftp") == 0)
+	return PROTO_FTP;
+    if (strcasecmp(s, "https") == 0)
+	return PROTO_HTTPS;
+    if (strcasecmp(s, "file") == 0)
+	return PROTO_FTP;
+    if (strcasecmp(s, "gopher") == 0)
+	return PROTO_GOPHER;
+    if (strcasecmp(s, "wais") == 0)
+	return PROTO_WAIS;
+    if (strcasecmp(s, "cache_object") == 0)
+	return PROTO_CACHEOBJ;
+    if (strcasecmp(s, "urn") == 0)
+	return PROTO_URN;
+    if (strcasecmp(s, "whois") == 0)
+	return PROTO_WHOIS;
+    if (strcasecmp(s, "internal") == 0)
+	return PROTO_INTERNAL;
+    return PROTO_NONE;
+}
+
+
+int
+urlDefaultPort(protocol_t p)
+{
+    switch (p) {
+    case PROTO_HTTP:
+	return 80;
+    case PROTO_HTTPS:
+	return 443;
+    case PROTO_FTP:
+	return 21;
+    case PROTO_GOPHER:
+	return 70;
+    case PROTO_WAIS:
+	return 210;
+    case PROTO_CACHEOBJ:
+    case PROTO_INTERNAL:
+	return CACHE_HTTP_PORT;
+    case PROTO_WHOIS:
+	return 43;
+    default:
+	return 0;
+    }
+}
+
+request_t *
+urlParse(method_t method, char *url)
+{
+    LOCAL_ARRAY(char, proto, MAX_URL);
+    LOCAL_ARRAY(char, login, MAX_URL);
+    LOCAL_ARRAY(char, host, MAX_URL);
+    LOCAL_ARRAY(char, urlpath, MAX_URL);
+    request_t *request = NULL;
+    char *t = NULL;
+    char *q = NULL;
+    int port;
+    protocol_t protocol = PROTO_NONE;
+    int l;
+    proto[0] = host[0] = urlpath[0] = login[0] = '\0';
+
+    if ((l = strlen(url)) + Config.appendDomainLen > (MAX_URL - 1)) {
+	/* terminate so it doesn't overflow other buffers */
+	*(url + (MAX_URL >> 1)) = '\0';
+	debug(23, 1) ("urlParse: URL too large (%d bytes)\n", l);
+	return NULL;
+    }
+    if (method == METHOD_CONNECT) {
+	port = CONNECT_PORT;
+	if (sscanf(url, "%[^:]:%d", host, &port) < 1)
+	    return NULL;
+    } else if (!strncmp(url, "urn:", 4)) {
+	return urnParse(method, url);
+    } else {
+	if (sscanf(url, "%[^:]://%[^/]%[^\r\n]", proto, host, urlpath) < 2)
+	    return NULL;
+	protocol = urlParseProtocol(proto);
+	port = urlDefaultPort(protocol);
+	/* Is there any login informaiton? */
+	if ((t = strrchr(host, '@'))) {
+	    strcpy((char *) login, (char *) host);
+	    t = strrchr(login, '@');
+	    *t = 0;
+	    strcpy((char *) host, t + 1);
+	}
+	if ((t = strrchr(host, ':'))) {
+	    *t++ = '\0';
+	    if (*t != '\0')
+		port = atoi(t);
+	}
+    }
+    for (t = host; *t; t++)
+	*t = xtolower(*t);
+    if (stringHasWhitespace(host)) {
+	if (URI_WHITESPACE_STRIP == Config.uri_whitespace) {
+	    t = q = host;
+	    while (*t) {
+		if (!xisspace(*t))
+		    *q++ = *t;
+		t++;
+	    }
+	    *q = '\0';
+	}
+    }
+    if (Config.onoff.check_hostnames && strspn(host, valid_hostname_chars) != strlen(host)) {
+	debug(23, 1) ("urlParse: Illegal character in hostname '%s'\n", host);
+	return NULL;
+    }
+    /* remove trailing dots from hostnames */
+    while ((l = strlen(host)) > 0 && host[--l] == '.')
+	host[l] = '\0';
+    /* remove duplicate dots */
+    while ((t = strstr(host, "..")))
+	xmemmove(t, t + 1, strlen(t));
+    if (Config.appendDomain && !strchr(host, '.'))
+	strncat(host, Config.appendDomain, SQUIDHOSTNAMELEN);
+    if (port < 1 || port > 65535) {
+	debug(23, 3) ("urlParse: Invalid port '%d'\n", port);
+	return NULL;
+    }
+#ifdef HARDCODE_DENY_PORTS
+    /* These ports are filtered in the default squid.conf, but
+     * maybe someone wants them hardcoded... */
+    if (port == 7 || port == 9 || port == 19) {
+	debug(23, 0) ("urlParse: Deny access to port %d\n", port);
+	return NULL;
+    }
+#endif
+    if (stringHasWhitespace(urlpath)) {
+	debug(23, 2) ("urlParse: URI has whitespace: {%s}\n", url);
+	switch (Config.uri_whitespace) {
+	case URI_WHITESPACE_DENY:
+	    return NULL;
+	case URI_WHITESPACE_ALLOW:
+	    break;
+	case URI_WHITESPACE_ENCODE:
+	    t = rfc1738_escape_unescaped(urlpath);
+	    xstrncpy(urlpath, t, MAX_URL);
+	    break;
+	case URI_WHITESPACE_CHOP:
+	    *(urlpath + strcspn(urlpath, w_space)) = '\0';
+	    break;
+	case URI_WHITESPACE_STRIP:
+	default:
+	    t = q = urlpath;
+	    while (*t) {
+		if (!xisspace(*t))
+		    *q++ = *t;
+		t++;
+	    }
+	    *q = '\0';
+	}
+    }
+    request = requestCreate(method, protocol, urlpath);
+    xstrncpy(request->host, host, SQUIDHOSTNAMELEN);
+    xstrncpy(request->login, login, MAX_LOGIN_SZ);
+    request->port = (u_short) port;
+    return request;
+}
+
+static request_t *
+urnParse(method_t method, char *urn)
+{
+    debug(50, 5) ("urnParse: %s\n", urn);
+    return requestCreate(method, PROTO_URN, urn + 4);
+}
+
+const char *
+urlCanonical(request_t * request)
+{
+    LOCAL_ARRAY(char, portbuf, 32);
+    LOCAL_ARRAY(char, urlbuf, MAX_URL);
+    if (request->canonical)
+	return request->canonical;
+    if (request->protocol == PROTO_URN) {
+	snprintf(urlbuf, MAX_URL, "urn:%s", strBuf(request->urlpath));
+    } else {
+	switch (request->method) {
+	case METHOD_CONNECT:
+	    snprintf(urlbuf, MAX_URL, "%s:%d", request->host, request->port);
+	    break;
+	default:
+	    portbuf[0] = '\0';
+	    if (request->port != urlDefaultPort(request->protocol))
+		snprintf(portbuf, 32, ":%d", request->port);
+	    snprintf(urlbuf, MAX_URL, "%s://%s%s%s%s%s",
+		ProtocolStr[request->protocol],
+		request->login,
+		*request->login ? "@" : null_string,
+		request->host,
+		portbuf,
+		strBuf(request->urlpath));
+	    break;
+	}
+    }
+    return (request->canonical = xstrdup(urlbuf));
+}
+
+char *
+urlCanonicalClean(const request_t * request)
+{
+    LOCAL_ARRAY(char, buf, MAX_URL);
+    LOCAL_ARRAY(char, portbuf, 32);
+    LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1);
+    char *t;
+    if (request->protocol == PROTO_URN) {
+	snprintf(buf, MAX_URL, "urn:%s", strBuf(request->urlpath));
+    } else {
+	switch (request->method) {
+	case METHOD_CONNECT:
+	    snprintf(buf, MAX_URL, "%s:%d", request->host, request->port);
+	    break;
+	default:
+	    portbuf[0] = '\0';
+	    if (request->port != urlDefaultPort(request->protocol))
+		snprintf(portbuf, 32, ":%d", request->port);
+	    loginbuf[0] = '\0';
+	    if ((int) strlen(request->login) > 0) {
+		strcpy(loginbuf, request->login);
+		if ((t = strchr(loginbuf, ':')))
+		    *t = '\0';
+		strcat(loginbuf, "@");
+	    }
+	    snprintf(buf, MAX_URL, "%s://%s%s%s%s",
+		ProtocolStr[request->protocol],
+		loginbuf,
+		request->host,
+		portbuf,
+		strBuf(request->urlpath));
+	    /*
+	     * strip arguments AFTER a question-mark
+	     */
+	    if (Config.onoff.strip_query_terms)
+		if ((t = strchr(buf, '?')))
+		    *(++t) = '\0';
+	    break;
+	}
+    }
+    if (stringHasCntl(buf))
+	xstrncpy(buf, rfc1738_escape_unescaped(buf), MAX_URL);
+    return buf;
+}
+
+/*
+ * matchDomainName() compares a hostname with a domainname according
+ * to the following rules:
+ * 
+ *    HOST          DOMAIN        MATCH?
+ * ------------- -------------    ------
+ *    foo.com       foo.com         YES
+ *   .foo.com       foo.com         YES
+ *  x.foo.com       foo.com          NO
+ *    foo.com      .foo.com         YES
+ *   .foo.com      .foo.com         YES
+ *  x.foo.com      .foo.com         YES
+ *
+ *  We strip leading dots on hosts (but not domains!) so that
+ *  ".foo.com" is is always the same as "foo.com".
+ *
+ *  Return values:
+ *     0 means the host matches the domain
+ *     1 means the host is greater than the domain
+ *    -1 means the host is less than the domain
+ */
+
+int
+matchDomainName(const char *h, const char *d)
+{
+    int dl;
+    int hl;
+    while ('.' == *h)
+	h++;
+    hl = strlen(h);
+    dl = strlen(d);
+    /*
+     * Start at the ends of the two strings and work towards the
+     * beginning.
+     */
+    while (xtolower(h[--hl]) == xtolower(d[--dl])) {
+	if (hl == 0 && dl == 0) {
+	    /*
+	     * We made it all the way to the beginning of both
+	     * strings without finding any difference.
+	     */
+	    return 0;
+	}
+	if (0 == hl) {
+	    /* 
+	     * The host string is shorter than the domain string.
+	     * There is only one case when this can be a match.
+	     * If the domain is just one character longer, and if
+	     * that character is a leading '.' then we call it a
+	     * match.
+	     */
+	    if (1 == dl && '.' == d[0])
+		return 0;
+	    else
+		return -1;
+	}
+	if (0 == dl) {
+	    /*
+	     * The domain string is shorter than the host string.
+	     * This is a match only if the first domain character
+	     * is a leading '.'.
+	     */
+	    if ('.' == d[0])
+		return 0;
+	    else
+		return 1;
+	}
+    }
+    /*
+     * We found different characters in the same position (from the end).
+     */
+    /*
+     * If one of those character is '.' then its special.  In order
+     * for splay tree sorting to work properly, "x-foo.com" must
+     * be greater than ".foo.com" even though '-' is less than '.'.
+     */
+    if ('.' == d[dl])
+	return 1;
+    if ('.' == h[hl])
+	return -1;
+    return (xtolower(h[hl]) - xtolower(d[dl]));
+}
+
+int
+urlCheckRequest(const request_t * r)
+{
+    int rc = 0;
+    /* protocol "independent" methods */
+    if (r->method == METHOD_CONNECT)
+	return 1;
+    if (r->method == METHOD_TRACE)
+	return 1;
+    if (r->method == METHOD_PURGE)
+	return 1;
+    /* does method match the protocol? */
+    switch (r->protocol) {
+    case PROTO_URN:
+    case PROTO_HTTP:
+    case PROTO_CACHEOBJ:
+	rc = 1;
+	break;
+    case PROTO_FTP:
+	if (r->method == METHOD_PUT)
+	    rc = 1;
+    case PROTO_GOPHER:
+    case PROTO_WAIS:
+    case PROTO_WHOIS:
+	if (r->method == METHOD_GET)
+	    rc = 1;
+	else if (r->method == METHOD_HEAD)
+	    rc = 1;
+	break;
+    case PROTO_HTTPS:
+#ifdef USE_SSL
+	rc = 1;
+	break;
+#else
+	/*
+	 * Squid can't originate an SSL connection, so it should
+	 * never receive an "https:" URL.  It should always be
+	 * CONNECT instead.
+	 */
+	rc = 0;
+#endif
+    default:
+	break;
+    }
+    return rc;
+}
+
+/*
+ * Quick-n-dirty host extraction from a URL.  Steps:
+ *      Look for a colon
+ *      Skip any '/' after the colon
+ *      Copy the next SQUID_MAXHOSTNAMELEN bytes to host[]
+ *      Look for an ending '/' or ':' and terminate
+ *      Look for login info preceeded by '@'
+ */
+char *
+urlHostname(const char *url)
+{
+    LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN);
+    char *t;
+    host[0] = '\0';
+    if (NULL == (t = strchr(url, ':')))
+	return NULL;
+    t++;
+    while (*t != '\0' && *t == '/')
+	t++;
+    xstrncpy(host, t, SQUIDHOSTNAMELEN);
+    if ((t = strchr(host, '/')))
+	*t = '\0';
+    if ((t = strchr(host, ':')))
+	*t = '\0';
+    if ((t = strrchr(host, '@'))) {
+	t++;
+	xmemmove(host, t, strlen(t) + 1);
+    }
+    return host;
+}
+
+static void
+urlExtMethodAdd(const char *mstr)
+{
+    method_t method = METHOD_NONE;
+    for (++method; method < METHOD_ENUM_END; ++method) {
+	if (0 == strcmp(mstr, RequestMethodStr[method])) {
+	    debug(23, 2) ("Extension method '%s' already exists\n", mstr);
+	    return;
+	}
+	if (0 != strncmp("%EXT", RequestMethodStr[method], 4))
+	    continue;
+	/* Don't free statically allocated "%EXTnn" string */
+	RequestMethodStr[method] = xstrdup(mstr);
+	debug(23, 1) ("Extension method '%s' added, enum=%d\n", mstr, (int) method);
+	return;
+    }
+    debug(23, 1) ("WARNING: Could not add new extension method '%s' due to lack of array space\n", mstr);
+}
+
+void
+urlExtMethodConfigure(void)
+{
+    wordlist *w = Config.ext_methods;
+    while (w) {
+	char *s;
+	for (s = w->key; *s; s++)
+	    *s = xtoupper(*s);
+	urlExtMethodAdd(w->key);
+	w = w->next;
+    }
+}
--- squid/src/useragent.c	Wed Feb 14 01:07:38 2007
+++ /dev/null	Wed Feb 14 01:07:22 2007
@@ -1,95 +0,0 @@
-
-/*
- * $Id: useragent.c,v 1.7 2001/07/17 06:57:31 squidadm Exp $
- *
- * DEBUG: section 40    User-Agent logging
- * AUTHOR: Joe Ramey <ramey@csc.ti.com>
- *
- * SQUID Web Proxy Cache          http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- *  Squid is the result of efforts by numerous individuals from
- *  the Internet community; see the CONTRIBUTORS file for full
- *  details.   Many organizations have provided support for Squid's
- *  development; see the SPONSORS file for full details.  Squid is
- *  Copyrighted (C) 2001 by the Regents of the University of
- *  California; see the COPYRIGHT file for full details.  Squid
- *  incorporates software developed and/or copyrighted by other
- *  sources; see the CREDITS file for full details.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *  
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *  
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
- *
- */
-
-#include "squid.h"
-
-#if USE_USERAGENT_LOG
-static Logfile *useragentlog = NULL;
-#endif
-
-void
-useragentOpenLog(void)
-{
-#if USE_USERAGENT_LOG
-    assert(NULL == useragentlog);
-    if (!Config.Log.useragent || (0 == strcmp(Config.Log.useragent, "none"))) {
-	debug(40, 1) ("User-Agent logging is disabled.\n");
-	return;
-    }
-    useragentlog = logfileOpen(Config.Log.useragent, 0, 1);
-#endif
-}
-
-void
-useragentRotateLog(void)
-{
-#if USE_USERAGENT_LOG
-    if (NULL == useragentlog)
-	return;
-    logfileRotate(useragentlog);
-#endif
-}
-
-void
-logUserAgent(const char *client, const char *agent)
-{
-#if USE_USERAGENT_LOG
-    static time_t last_time = 0;
-    static char time_str[128];
-    const char *s;
-    if (NULL == useragentlog)
-	return;
-    if (squid_curtime != last_time) {
-	s = mkhttpdlogtime(&squid_curtime);
-	strcpy(time_str, s);
-	last_time = squid_curtime;
-    }
-    logfilePrintf(useragentlog, "%s [%s] \"%s\"\n",
-	client,
-	time_str,
-	agent);
-#endif
-}
-
-void
-useragentLogClose(void)
-{
-#if USE_USERAGENT_LOG
-    if (NULL == useragentlog)
-	return;
-    logfileClose(useragentlog);
-    useragentlog = NULL;
-#endif
-}
--- /dev/null	Wed Feb 14 01:07:22 2007
+++ squid/src/useragent.cc	Wed Feb 14 01:07:38 2007
@@ -0,0 +1,95 @@
+
+/*
+ * $Id: useragent.cc,v 1.1.2.1 2002/10/09 14:36:50 rbcollins Exp $
+ *
+ * DEBUG: section 40    User-Agent logging
+ * AUTHOR: Joe Ramey <ramey@csc.ti.com>
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+#if USE_USERAGENT_LOG
+static Logfile *useragentlog = NULL;
+#endif
+
+void
+useragentOpenLog(void)
+{
+#if USE_USERAGENT_LOG
+    assert(NULL == useragentlog);
+    if (!Config.Log.useragent || (0 == strcmp(Config.Log.useragent, "none"))) {
+	debug(40, 1) ("User-Agent logging is disabled.\n");
+	return;
+    }
+    useragentlog = logfileOpen(Config.Log.useragent, 0, 1);
+#endif
+}
+
+void
+useragentRotateLog(void)
+{
+#if USE_USERAGENT_LOG
+    if (NULL == useragentlog)
+	return;
+    logfileRotate(useragentlog);
+#endif
+}
+
+void
+logUserAgent(const char *client, const char *agent)
+{
+#if USE_USERAGENT_LOG
+    static time_t last_time = 0;
+    static char time_str[128];
+    const char *s;
+    if (NULL == useragentlog)
+	return;
+    if (squid_curtime != last_time) {
+	s = mkhttpdlogtime(&squid_curtime);
+	strcpy(time_str, s);
+	last_time = squid_curtime;
+    }
+    logfilePrintf(useragentlog, "%s [%s] \"%s\"\n",
+	client,
+	time_str,
+	agent);
+#endif
+}
+
+void
+useragentLogClose(void)
+{
+#if USE_USERAGENT_LOG
+    if (NULL == useragentlog)
+	return;
+    logfileClose(useragentlog);
+    useragentlog = NULL;
+#endif
+}
