]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merged from parent (trunk r11240, circa 3.2.0.5+) take03
authorAlex Rousskov <rousskov@measurement-factory.com>
Fri, 18 Feb 2011 23:58:13 +0000 (16:58 -0700)
committerAlex Rousskov <rousskov@measurement-factory.com>
Fri, 18 Feb 2011 23:58:13 +0000 (16:58 -0700)
12 files changed:
1  2 
configure.ac
src/HttpHeader.cc
src/Makefile.am
src/adaptation/ServiceConfig.cc
src/adaptation/ecap/MessageRep.cc
src/cf.data.pre
src/client_side.cc
src/client_side_request.cc
src/log/FormatSquidCustom.cc
src/log/Tokens.cc
src/log/Tokens.h
src/log/access_log.cc

diff --cc configure.ac
Simple merge
Simple merge
diff --cc src/Makefile.am
index 1402f4f106ce5613cbee8f46398e836273770a4d,97467ac1ad1f360be27bde696d03393e7179c8cb..10cc2871853204117c1097155bb3ebd2fe9effc0
@@@ -49,6 -52,20 +52,20 @@@ SSL_LIBS = 
  else
  SSL_LOCAL_LIBS =
  endif
 -SNMP_LIBS = snmp/libsnmp.la
+ DIST_SUBDIRS += ssl
+ SNMP_ALL_SOURCE = \
+       snmp_core.h \
+       snmp_core.cc \
+       snmp_agent.cc
+ if ENABLE_SNMP
+ SNMP_SOURCE = $(SNMP_ALL_SOURCE)
+ SUBDIRS += snmp
++SNMP_LIBS = snmp/libsnmp.la $(SNMPLIB)
+ else
+ SNMP_SOURCE = 
+ endif
+ DIST_SUBDIRS += snmp
  
  if USE_ADAPTATION
  SUBDIRS += adaptation
Simple merge
index 23a0833e5b95db98e0a69346c1c232e83d437a6c,56281fef3ace759a6cfbf271217225c0bd63b5f4..eda4f17f60464ecdb7efb4034b038df9ad310a3c
@@@ -59,19 -61,11 +62,22 @@@ Adaptation::Ecap::HeaderRep::removeAny(
          theHeader.delByName(name.image().c_str());
      else
          theHeader.delById(squidId);
+     if (squidId == HDR_CONTENT_LENGTH)
+         theMessage.content_length = theHeader.getInt64(HDR_CONTENT_LENGTH);
  }
  
 +void
 +Adaptation::Ecap::HeaderRep::visitEach(libecap::NamedValueVisitor &visitor) const
 +{
 +    HttpHeaderPos pos = HttpHeaderInitPos;
 +    while (HttpHeaderEntry *e = theHeader.getEntry(&pos)) {
 +        const Name name(e->name.termedBuf()); // optimize: find std Names
 +        name.assignHostId(e->id);
 +        visitor.visit(name, Value(e->value.rawBuf(), e->value.size()));
 +    }
 +}
 +
  libecap::Area
  Adaptation::Ecap::HeaderRep::image() const
  {
diff --cc src/cf.data.pre
Simple merge
Simple merge
Simple merge
index 0000000000000000000000000000000000000000,f1a031ceac584717455d0fd0af231791c28e1130..80db596c634aaa3a7a151ec4f21b0269d6b3809f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,818 +1,819 @@@
 -#endif
+ /*
+  * $Id$
+  *
+  * DEBUG: section 46    Access Log - Squid Custom format
+  * 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 "config.h"
+ #include "AccessLogEntry.h"
+ #include "log/File.h"
+ #include "log/Formats.h"
+ #include "log/Gadgets.h"
+ #include "log/Tokens.h"
+ #include "SquidTime.h"
+ #include "MemBuf.h"
+ #include "HttpRequest.h"
+ #include "rfc1738.h"
+ #include "err_detail_type.h"
+ #include "errorpage.h"
+ static void
+ log_quoted_string(const char *str, char *out)
+ {
+     char *p = out;
+     while (*str) {
+         int l = strcspn(str, "\"\\\r\n\t");
+         memcpy(p, str, l);
+         str += l;
+         p += l;
+         switch (*str) {
+         case '\0':
+             break;
+         case '\r':
+             *p++ = '\\';
+             *p++ = 'r';
+             str++;
+             break;
+         case '\n':
+             *p++ = '\\';
+             *p++ = 'n';
+             str++;
+             break;
+         case '\t':
+             *p++ = '\\';
+             *p++ = 't';
+             str++;
+             break;
+         default:
+             *p++ = '\\';
+             *p++ = *str;
+             str++;
+             break;
+         }
+     }
+     *p++ = '\0';
+ }
+ void
+ Log::Format::SquidCustom(AccessLogEntry * al, customlog * log)
+ {
+     logformat *lf;
+     Logfile *logfile;
+     logformat_token *fmt;
+     static MemBuf mb;
+     char tmp[1024];
+     String sb;
+     mb.reset();
+     lf = log->logFormat;
+     logfile = log->logfile;
+     for (fmt = lf->format; fmt != NULL; fmt = fmt->next) {    /* for each token */
+         const char *out = NULL;
+         int quote = 0;
+         long int outint = 0;
+         int doint = 0;
+         int dofree = 0;
+         int64_t outoff = 0;
+         int dooff = 0;
+         switch (fmt->type) {
+         case LFT_NONE:
+             out = "";
+             break;
+         case LFT_STRING:
+             out = fmt->data.string;
+             break;
+         case LFT_CLIENT_IP_ADDRESS:
+             if (al->cache.caddr.IsNoAddr()) // e.g., ICAP OPTIONS lack client
+                 out = "-";
+             else
+                 out = al->cache.caddr.NtoA(tmp,1024);
+             break;
+         case LFT_CLIENT_FQDN:
+             if (al->cache.caddr.IsAnyAddr()) // e.g., ICAP OPTIONS lack client
+                 out = "-";
+             else
+                 out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
+             if (!out) {
+                 out = al->cache.caddr.NtoA(tmp,1024);
+             }
+             break;
+         case LFT_CLIENT_PORT:
+             if (al->request) {
+                 outint = al->request->client_addr.GetPort();
+                 doint = 1;
+             }
+             break;
+ #if USE_SQUID_EUI
+         case LFT_CLIENT_EUI:
+             if (al->request) {
+                 if (al->cache.caddr.IsIPv4())
+                     al->request->client_eui48.encode(tmp, 1024);
+                 else
+                     al->request->client_eui64.encode(tmp, 1024);
+                 out = tmp;
+             }
+             break;
+ #endif
+             /* case LFT_SERVER_IP_ADDRESS: */
+         case LFT_SERVER_IP_OR_PEER_NAME:
+             out = al->hier.host;
+             break;
+             /* case LFT_SERVER_PORT: */
+         case LFT_LOCAL_IP:
+             if (al->request) {
+                 out = al->request->my_addr.NtoA(tmp,1024);
+             }
+             break;
+         case LFT_LOCAL_PORT:
+             if (al->request) {
+                 outint = al->request->my_addr.GetPort();
+                 doint = 1;
+             }
+             break;
+         case LFT_PEER_LOCAL_PORT:
+             if (al->hier.peer_local_port) {
+                 outint = al->hier.peer_local_port;
+                 doint = 1;
+             }
+             break;
+         case LFT_TIME_SECONDS_SINCE_EPOCH:
+             // some platforms store time in 32-bit, some 64-bit...
+             outoff = static_cast<int64_t>(current_time.tv_sec);
+             dooff = 1;
+             break;
+         case LFT_TIME_SUBSECOND:
+             outint = current_time.tv_usec / fmt->divisor;
+             doint = 1;
+             break;
+         case LFT_TIME_LOCALTIME:
+         case LFT_TIME_GMT: {
+             const char *spec;
+             struct tm *t;
+             spec = fmt->data.timespec;
+             if (fmt->type == LFT_TIME_LOCALTIME) {
+                 if (!spec)
+                     spec = "%d/%b/%Y:%H:%M:%S %z";
+                 t = localtime(&squid_curtime);
+             } else {
+                 if (!spec)
+                     spec = "%d/%b/%Y:%H:%M:%S";
+                 t = gmtime(&squid_curtime);
+             }
+             strftime(tmp, sizeof(tmp), spec, t);
+             out = tmp;
+         }
+         break;
+         case LFT_TIME_TO_HANDLE_REQUEST:
+             outint = al->cache.msec;
+             doint = 1;
+             break;
+         case LFT_PEER_RESPONSE_TIME:
+             if (al->hier.peer_response_time < 0) {
+                 out = "-";
+             } else {
+                 outoff = al->hier.peer_response_time;
+                 dooff = 1;
+             }
+             break;
+         case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME:
+             if (al->hier.total_response_time < 0) {
+                 out = "-";
+             } else {
+                 outoff = al->hier.total_response_time;
+                 dooff = 1;
+             }
+             break;
+         case LFT_DNS_WAIT_TIME:
+             if (al->request && al->request->dnsWait >= 0) {
+                 outint = al->request->dnsWait;
+                 doint = 1;
+             }
+             break;
+         case LFT_REQUEST_HEADER:
+             if (al->request)
+                 sb = al->request->header.getByName(fmt->data.header.header);
+             out = sb.termedBuf();
+             quote = 1;
+             break;
+         case LFT_ADAPTED_REQUEST_HEADER:
+             if (al->request)
+                 sb = al->adapted_request->header.getByName(fmt->data.header.header);
+             out = sb.termedBuf();
+             quote = 1;
+             break;
+         case LFT_REPLY_HEADER:
+             if (al->reply)
+                 sb = al->reply->header.getByName(fmt->data.header.header);
+             out = sb.termedBuf();
+             quote = 1;
+             break;
+ #if USE_ADAPTATION
+         case LTF_ADAPTATION_SUM_XACT_TIMES:
+             if (al->request) {
+                 Adaptation::History::Pointer ah = al->request->adaptHistory();
+                 if (ah != NULL)
+                     ah->sumLogString(fmt->data.string, sb);
+                 out = sb.termedBuf();
+             }
+             break;
+         case LTF_ADAPTATION_ALL_XACT_TIMES:
+             if (al->request) {
+                 Adaptation::History::Pointer ah = al->request->adaptHistory();
+                 if (ah != NULL)
+                     ah->allLogString(fmt->data.string, sb);
+                 out = sb.termedBuf();
+             }
+             break;
 -#if ICAP_CLIENT
 -        case LFT_ICAP_LAST_MATCHED_HEADER:
 -                Adaptation::Icap::History::Pointer ih = al->request->icapHistory();
 -                if (ih != NULL)
 -                    sb = ih->mergeOfIcapHeaders.getByName(fmt->data.header.header);
++        case LFT_ADAPTATION_LAST_HEADER:
+             if (al->request) {
 -        case LFT_ICAP_LAST_MATCHED_HEADER_ELEM:
++                const Adaptation::History::Pointer ah = al->request->adaptHistory();
++                if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
++                    sb = ah->allMeta.getByName(fmt->data.header.header);
+             }
++            // XXX: here and elsewhere: move such code inside the if guard
+             out = sb.termedBuf();
+             quote = 1;
+             break;
 -                Adaptation::Icap::History::Pointer ih = al->request->icapHistory();
 -                if (ih != NULL)
 -                    sb = ih->mergeOfIcapHeaders.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
++        case LFT_ADAPTATION_LAST_HEADER_ELEM:
+             if (al->request) {
 -        case LFT_ICAP_LAST_MATCHED_ALL_HEADERS:
 -            out = al->headers.icap;
++                const Adaptation::History::Pointer ah = al->request->adaptHistory();
++                if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
++                    sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+             }
+             out = sb.termedBuf();
+             quote = 1;
+             break;
++        case LFT_ADAPTATION_LAST_ALL_HEADERS:
++            out = al->headers.adapt_last;
+             quote = 1;
+             break;
++#endif
++#if ICAP_CLIENT
+         case LFT_ICAP_ADDR:
+             if (!out)
+                 out = al->icap.hostAddr.NtoA(tmp,1024);
+             break;
+         case LFT_ICAP_SERV_NAME:
+             out = al->icap.serviceName.termedBuf();
+             break;
+         case LFT_ICAP_REQUEST_URI:
+             out = al->icap.reqUri.termedBuf();
+             break;
+         case LFT_ICAP_REQUEST_METHOD:
+             out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
+             break;
+         case LFT_ICAP_BYTES_SENT:
+             outoff = al->icap.bytesSent;
+             dooff = 1;
+             break;
+         case LFT_ICAP_BYTES_READ:
+             outoff = al->icap.bytesRead;
+             dooff = 1;
+             break;
+         case LFT_ICAP_BODY_BYTES_READ:
+             if (al->icap.bodyBytesRead >= 0) {
+                 outoff = al->icap.bodyBytesRead;
+                 dooff = 1;
+             }
+             // else if icap.bodyBytesRead < 0, we do not have any http data,
+             // so just print a "-" (204 responses etc)
+             break;
+         case LFT_ICAP_REQ_HEADER:
+             if (NULL != al->icap.request) {
+                 sb = al->icap.request->header.getByName(fmt->data.header.header);
+                 out = sb.termedBuf();
+                 quote = 1;
+             }
+             break;
+         case LFT_ICAP_REQ_HEADER_ELEM:
+             if (al->request)
+                 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+             out = sb.termedBuf();
+             quote = 1;
+             break;
+         case LFT_ICAP_REQ_ALL_HEADERS:
+             if (al->icap.request) {
+                 HttpHeaderPos pos = HttpHeaderInitPos;
+                 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
+                     sb.append(e->name);
+                     sb.append(": ");
+                     sb.append(e->value);
+                     sb.append("\r\n");
+                 }
+                 out = sb.termedBuf();
+                 quote = 1;
+             }
+             break;
+         case LFT_ICAP_REP_HEADER:
+             if (NULL != al->icap.reply) {
+                 sb = al->icap.reply->header.getByName(fmt->data.header.header);
+                 out = sb.termedBuf();
+                 quote = 1;
+             }
+             break;
+         case LFT_ICAP_REP_HEADER_ELEM:
+             if (NULL != al->icap.reply)
+                 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+             out = sb.termedBuf();
+             quote = 1;
+             break;
+         case LFT_ICAP_REP_ALL_HEADERS:
+             if (al->icap.reply) {
+                 HttpHeaderPos pos = HttpHeaderInitPos;
+                 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
+                     sb.append(e->name);
+                     sb.append(": ");
+                     sb.append(e->value);
+                     sb.append("\r\n");
+                 }
+                 out = sb.termedBuf();
+                 quote = 1;
+             }
+             break;
+         case LFT_ICAP_TR_RESPONSE_TIME:
+             outint = al->icap.trTime;
+             doint = 1;
+             break;
+         case LFT_ICAP_IO_TIME:
+             outint = al->icap.ioTime;
+             doint = 1;
+             break;
+         case LFT_ICAP_STATUS_CODE:
+             outint = al->icap.resStatus;
+             doint  = 1;
+             break;
+         case LFT_ICAP_OUTCOME:
+             out = al->icap.outcome;
+             break;
+         case LFT_ICAP_TOTAL_TIME:
+             outint = al->icap.processingTime;
+             doint = 1;
+             break;
+ #endif
+         case LFT_REQUEST_HEADER_ELEM:
+             if (al->request)
+                 sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+             out = sb.termedBuf();
+             quote = 1;
+             break;
+         case LFT_ADAPTED_REQUEST_HEADER_ELEM:
+             if (al->adapted_request)
+                 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+             out = sb.termedBuf();
+             quote = 1;
+             break;
+         case LFT_REPLY_HEADER_ELEM:
+             if (al->reply)
+                 sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+             out = sb.termedBuf();
+             quote = 1;
+             break;
+         case LFT_REQUEST_ALL_HEADERS:
+             out = al->headers.request;
+             quote = 1;
+             break;
+         case LFT_ADAPTED_REQUEST_ALL_HEADERS:
+             out = al->headers.adapted_request;
+             quote = 1;
+             break;
+         case LFT_REPLY_ALL_HEADERS:
+             out = al->headers.reply;
+             quote = 1;
+             break;
+         case LFT_USER_NAME:
+             out = Log::FormatName(al->cache.authuser);
+             if (!out)
+                 out = Log::FormatName(al->cache.extuser);
+ #if USE_SSL
+             if (!out)
+                 out = Log::FormatName(al->cache.ssluser);
+ #endif
+             if (!out)
+                 out = Log::FormatName(al->cache.rfc931);
+             dofree = 1;
+             break;
+         case LFT_USER_LOGIN:
+             out = Log::FormatName(al->cache.authuser);
+             dofree = 1;
+             break;
+         case LFT_USER_IDENT:
+             out = Log::FormatName(al->cache.rfc931);
+             dofree = 1;
+             break;
+         case LFT_USER_EXTERNAL:
+             out = Log::FormatName(al->cache.extuser);
+             dofree = 1;
+             break;
+             /* case LFT_USER_REALM: */
+             /* case LFT_USER_SCHEME: */
+             // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
+             // but compiler complains if ommited
+         case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
+         case LFT_HTTP_SENT_STATUS_CODE:
+             outint = al->http.code;
+             doint = 1;
+             break;
+         case LFT_HTTP_RECEIVED_STATUS_CODE:
+             if (al->hier.peer_reply_status == HTTP_STATUS_NONE) {
+                 out = "-";
+             } else {
+                 outint = al->hier.peer_reply_status;
+                 doint = 1;
+             }
+             break;
+             /* case LFT_HTTP_STATUS:
+              *           out = statusline->text;
+              *     quote = 1;
+              *     break;
+              */
+         case LFT_HTTP_BODY_BYTES_READ:
+             if (al->hier.bodyBytesRead >= 0) {
+                 outoff = al->hier.bodyBytesRead;
+                 dooff = 1;
+             }
+             // else if hier.bodyBytesRead < 0 we did not have any data exchange with
+             // a peer server so just print a "-" (eg requests served from cache,
+             // or internal error messages).
+             break;
+         case LFT_SQUID_STATUS:
+             if (al->http.timedout || al->http.aborted) {
+                 snprintf(tmp, sizeof(tmp), "%s%s", log_tags[al->cache.code],
+                          al->http.statusSfx());
+                 out = tmp;
+             } else {
+                 out = log_tags[al->cache.code];
+             }
+             break;
+         case LFT_SQUID_ERROR:
+             if (al->request && al->request->errType != ERR_NONE)
+                 out = errorPageName(al->request->errType);
+             break;
+         case LFT_SQUID_ERROR_DETAIL:
+             if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
+                 if (al->request->errDetail > ERR_DETAIL_START  &&
+                         al->request->errDetail < ERR_DETAIL_MAX)
+                     out = errorDetailName(al->request->errDetail);
+                 else {
+                     if (al->request->errDetail >= ERR_DETAIL_EXCEPTION_START)
+                         snprintf(tmp, sizeof(tmp), "%s=0x%X",
+                                  errorDetailName(al->request->errDetail), (uint32_t) al->request->errDetail);
+                     else
+                         snprintf(tmp, sizeof(tmp), "%s=%d",
+                                  errorDetailName(al->request->errDetail), al->request->errDetail);
+                     out = tmp;
+                 }
+             }
+             break;
+         case LFT_SQUID_HIERARCHY:
+             if (al->hier.ping.timedout)
+                 mb.append("TIMEOUT_", 8);
+             out = hier_code_str[al->hier.code];
+             break;
+         case LFT_MIME_TYPE:
+             out = al->http.content_type;
+             break;
+         case LFT_REQUEST_METHOD:
+             out = al->_private.method_str;
+             break;
+         case LFT_REQUEST_URI:
+             out = al->url;
+             break;
+         case LFT_REQUEST_URLPATH:
+             if (al->request) {
+                 out = al->request->urlpath.termedBuf();
+                 quote = 1;
+             }
+             break;
+         case LFT_REQUEST_VERSION:
+             snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->http.version.major, (int) al->http.version.minor);
+             out = tmp;
+             break;
+         case LFT_REQUEST_SIZE_TOTAL:
+             outoff = al->cache.requestSize;
+             dooff = 1;
+             break;
+             /*case LFT_REQUEST_SIZE_LINE: */
+         case LFT_REQUEST_SIZE_HEADERS:
+             outoff = al->cache.requestHeadersSize;
+             dooff =1;
+             break;
+             /*case LFT_REQUEST_SIZE_BODY: */
+             /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
+         case LFT_REPLY_SIZE_TOTAL:
+             outoff = al->cache.replySize;
+             dooff = 1;
+             break;
+         case LFT_REPLY_HIGHOFFSET:
+             outoff = al->cache.highOffset;
+             dooff = 1;
+             break;
+         case LFT_REPLY_OBJECTSIZE:
+             outoff = al->cache.objectSize;
+             dooff = 1;
+             break;
+             /*case LFT_REPLY_SIZE_LINE: */
+         case LFT_REPLY_SIZE_HEADERS:
+             outint = al->cache.replyHeadersSize;
+             doint = 1;
+             break;
+             /*case LFT_REPLY_SIZE_BODY: */
+             /*case LFT_REPLY_SIZE_BODY_NO_TE: */
+         case LFT_TAG:
+             if (al->request)
+                 out = al->request->tag.termedBuf();
+             quote = 1;
+             break;
+         case LFT_IO_SIZE_TOTAL:
+             outint = al->cache.requestSize + al->cache.replySize;
+             doint = 1;
+             break;
+         case LFT_EXT_LOG:
+             if (al->request)
+                 out = al->request->extacl_log.termedBuf();
+             quote = 1;
+             break;
+         case LFT_SEQUENCE_NUMBER:
+             outoff = logfile->sequence_number;
+             dooff = 1;
+             break;
+         case LFT_PERCENT:
+             out = "%";
+             break;
+         }
+         if (dooff) {
+             snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero ? (int) fmt->width : 0, outoff);
+             out = tmp;
+         } else if (doint) {
+             snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero ? (int) fmt->width : 0, outint);
+             out = tmp;
+         }
+         if (out && *out) {
+             if (quote || fmt->quote != LOG_QUOTE_NONE) {
+                 char *newout = NULL;
+                 int newfree = 0;
+                 switch (fmt->quote) {
+                 case LOG_QUOTE_NONE:
+                     newout = rfc1738_escape_unescaped(out);
+                     break;
+                 case LOG_QUOTE_QUOTES: {
+                     size_t out_len = static_cast<size_t>(strlen(out)) * 2 + 1;
+                     if (out_len >= sizeof(tmp)) {
+                         newout = (char *)xmalloc(out_len);
+                         newfree = 1;
+                     } else
+                         newout = tmp;
+                     log_quoted_string(out, newout);
+                 }
+                 break;
+                 case LOG_QUOTE_MIMEBLOB:
+                     newout = Log::QuoteMimeBlob(out);
+                     newfree = 1;
+                     break;
+                 case LOG_QUOTE_URL:
+                     newout = rfc1738_escape(out);
+                     break;
+                 case LOG_QUOTE_RAW:
+                     break;
+                 }
+                 if (newout) {
+                     if (dofree)
+                         safe_free(out);
+                     out = newout;
+                     dofree = newfree;
+                 }
+             }
+             if (fmt->width) {
+                 if (fmt->left)
+                     mb.Printf("%-*s", (int) fmt->width, out);
+                 else
+                     mb.Printf("%*s", (int) fmt->width, out);
+             } else
+                 mb.append(out, strlen(out));
+         } else {
+             mb.append("-", 1);
+         }
+         if (fmt->space)
+             mb.append(" ", 1);
+         sb.clean();
+         if (dofree)
+             safe_free(out);
+     }
+     logfilePrintf(logfile, "%s\n", mb.buf);
+ }
index 0000000000000000000000000000000000000000,4afb57b31fe6495170e9c90660b6aae9fd834819..0d5a1795722c8957033154d0ebe1c5609dbe5577
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,690 +1,705 @@@
 -    {"icap::<last_h", LFT_ICAP_LAST_MATCHED_HEADER},
+ /*
+  * $Id$
+  *
+  * DEBUG: section 46    Access Log Format Tokens
+  * 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 "config.h"
+ #include "log/Tokens.h"
+ #include "Store.h"
+ const char *log_tags[] = {
+     "NONE",
+     "TCP_HIT",
+     "TCP_MISS",
+     "TCP_REFRESH_UNMODIFIED",
+     "TCP_REFRESH_FAIL", // same tag logged for LOG_TCP_REFRESH_FAIL_OLD and
+     "TCP_REFRESH_FAIL", // LOG_TCP_REFRESH_FAIL_ERR for backward-compatibility
+     "TCP_REFRESH_MODIFIED",
+     "TCP_CLIENT_REFRESH_MISS",
+     "TCP_IMS_HIT",
+     "TCP_SWAPFAIL_MISS",
+     "TCP_NEGATIVE_HIT",
+     "TCP_MEM_HIT",
+     "TCP_DENIED",
+     "TCP_DENIED_REPLY",
+     "TCP_OFFLINE_HIT",
+ #if LOG_TCP_REDIRECTS
+     "TCP_REDIRECT",
+ #endif
+     "UDP_HIT",
+     "UDP_MISS",
+     "UDP_DENIED",
+     "UDP_INVALID",
+     "UDP_MISS_NOFETCH",
+     "ICP_QUERY",
+     "LOG_TYPE_MAX"
+ };
+ #if USE_ADAPTATION
+ bool alLogformatHasAdaptToken = false;
+ #endif
+ #if ICAP_CLIENT
+ bool alLogformatHasIcapToken = false;
+ #endif
+ struct logformat_token_table_entry logformat_token_table[] = {
+     {">a", LFT_CLIENT_IP_ADDRESS},
+     {">p", LFT_CLIENT_PORT},
+     {">A", LFT_CLIENT_FQDN},
+ #if USE_SQUID_EUI
+     {">eui", LFT_CLIENT_EUI},
+ #endif
+     /*{ "<a", LFT_SERVER_IP_ADDRESS }, */
+     /*{ "<p", LFT_SERVER_PORT }, */
+     {"<A", LFT_SERVER_IP_OR_PEER_NAME},
+     /* {"oa", LFT_OUTGOING_IP}, */
+     /* {"ot", LFT_OUTGOING_TOS}, */
+     {"la", LFT_LOCAL_IP},
+     {"lp", LFT_LOCAL_PORT},
+     /*{ "lA", LFT_LOCAL_NAME }, */
+     {"<lp", LFT_PEER_LOCAL_PORT},
+     {"ts", LFT_TIME_SECONDS_SINCE_EPOCH},
+     {"tu", LFT_TIME_SUBSECOND},
+     {"tl", LFT_TIME_LOCALTIME},
+     {"tg", LFT_TIME_GMT},
+     {"tr", LFT_TIME_TO_HANDLE_REQUEST},
+     {"<pt", LFT_PEER_RESPONSE_TIME},
+     {"<tt", LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME},
+     {"dt", LFT_DNS_WAIT_TIME},
+     {">ha", LFT_ADAPTED_REQUEST_HEADER},
+     {">ha", LFT_ADAPTED_REQUEST_ALL_HEADERS},
+     {">h", LFT_REQUEST_HEADER},
+     {">h", LFT_REQUEST_ALL_HEADERS},
+     {"<h", LFT_REPLY_HEADER},
+     {"<h", LFT_REPLY_ALL_HEADERS},
+     {"un", LFT_USER_NAME},
+     {"ul", LFT_USER_LOGIN},
+     /*{ "ur", LFT_USER_REALM }, */
+     /*{ "us", LFT_USER_SCHEME }, */
+     {"ui", LFT_USER_IDENT},
+     {"ue", LFT_USER_EXTERNAL},
+     {"Hs", LFT_HTTP_SENT_STATUS_CODE_OLD_30},
+     {">Hs", LFT_HTTP_SENT_STATUS_CODE},
+     {"<Hs", LFT_HTTP_RECEIVED_STATUS_CODE},
+     /*{ "Ht", LFT_HTTP_STATUS }, */
+     {"<bs", LFT_HTTP_BODY_BYTES_READ},
+     {"Ss", LFT_SQUID_STATUS},
+     { "err_code", LFT_SQUID_ERROR },
+     { "err_detail", LFT_SQUID_ERROR_DETAIL },
+     {"Sh", LFT_SQUID_HIERARCHY},
+     {"mt", LFT_MIME_TYPE},
+     {"rm", LFT_REQUEST_METHOD},
+     {"ru", LFT_REQUEST_URI},  /* doesn't include the query-string */
+     {"rp", LFT_REQUEST_URLPATH},      /* doesn't include the host */
+     /* { "rq", LFT_REQUEST_QUERY }, * /     / * the query-string, INCLUDING the leading ? */
+     {">v", LFT_REQUEST_VERSION},
+     {"rv", LFT_REQUEST_VERSION},
+     { ">st", LFT_REQUEST_SIZE_TOTAL },
+     /*{ ">sl", LFT_REQUEST_SIZE_LINE }, * / / * the request line "GET ... " */
+     { ">sh", LFT_REQUEST_SIZE_HEADERS },
+     /*{ ">sb", LFT_REQUEST_SIZE_BODY }, */
+     /*{ ">sB", LFT_REQUEST_SIZE_BODY_NO_TE }, */
+     {"<st", LFT_REPLY_SIZE_TOTAL},
+     {"<sH", LFT_REPLY_HIGHOFFSET},
+     {"<sS", LFT_REPLY_OBJECTSIZE},
+     /*{ "<sl", LFT_REPLY_SIZE_LINE }, * /   / * the reply line (protocol, code, text) */
+     { "<sh", LFT_REPLY_SIZE_HEADERS },
+     /*{ "<sb", LFT_REPLY_SIZE_BODY }, */
+     /*{ "<sB", LFT_REPLY_SIZE_BODY_NO_TE }, */
+     {"et", LFT_TAG},
+     {"st", LFT_IO_SIZE_TOTAL},
+     {"ea", LFT_EXT_LOG},
+     {"sn", LFT_SEQUENCE_NUMBER},
+     {"%", LFT_PERCENT},
+ #if USE_ADAPTATION
+     {"adapt::all_trs", LTF_ADAPTATION_ALL_XACT_TIMES},
+     {"adapt::sum_trs", LTF_ADAPTATION_SUM_XACT_TIMES},
++    {"adapt::<last_h", LFT_ADAPTATION_LAST_HEADER},
+ #endif
+ #if ICAP_CLIENT
+     {"icap::tt", LFT_ICAP_TOTAL_TIME},
 -#if ICAP_CLIENT
 -    case LFT_ICAP_LAST_MATCHED_HEADER:
++    {"icap::<last_h", LFT_ADAPTATION_LAST_HEADER}, // deprecated
+     {"icap::<A",  LFT_ICAP_ADDR},
+     {"icap::<service_name",  LFT_ICAP_SERV_NAME},
+     {"icap::ru",  LFT_ICAP_REQUEST_URI},
+     {"icap::rm",  LFT_ICAP_REQUEST_METHOD},
+     {"icap::>st",  LFT_ICAP_BYTES_SENT},
+     {"icap::<st",  LFT_ICAP_BYTES_READ},
+     {"icap::<bs", LFT_ICAP_BODY_BYTES_READ},
+     {"icap::>h",  LFT_ICAP_REQ_HEADER},
+     {"icap::<h",  LFT_ICAP_REP_HEADER},
+     {"icap::tr",  LFT_ICAP_TR_RESPONSE_TIME},
+     {"icap::tio",  LFT_ICAP_IO_TIME},
+     {"icap::to",  LFT_ICAP_OUTCOME},
+     {"icap::Hs",  LFT_ICAP_STATUS_CODE},
+ #endif
+     {NULL, LFT_NONE}          /* this must be last */
+ };
+ /* parses a single token. Returns the token length in characters,
+  * and fills in the lt item with the token information.
+  * def is for sure null-terminated
+  */
+ int
+ accessLogGetNewLogFormatToken(logformat_token * lt, char *def, enum log_quote *quote)
+ {
+     char *cur = def;
+     struct logformat_token_table_entry *lte;
+     int l;
+     memset(lt, 0, sizeof(*lt));
+     l = strcspn(cur, "%");
+     if (l > 0) {
+         char *cp;
+         /* it's a string for sure, until \0 or the next % */
+         cp = (char *)xmalloc(l + 1);
+         xstrncpy(cp, cur, l + 1);
+         lt->type = LFT_STRING;
+         lt->data.string = cp;
+         while (l > 0) {
+             switch (*cur) {
+             case '"':
+                 if (*quote == LOG_QUOTE_NONE)
+                     *quote = LOG_QUOTE_QUOTES;
+                 else if (*quote == LOG_QUOTE_QUOTES)
+                     *quote = LOG_QUOTE_NONE;
+                 break;
+             case '[':
+                 if (*quote == LOG_QUOTE_NONE)
+                     *quote = LOG_QUOTE_MIMEBLOB;
+                 break;
+             case ']':
+                 if (*quote == LOG_QUOTE_MIMEBLOB)
+                     *quote = LOG_QUOTE_NONE;
+                 break;
+             }
+             cur++;
+             l--;
+         }
+         goto done;
+     }
+     if (!*cur)
+         goto done;
+     cur++;
+     switch (*cur) {
+     case '"':
+         lt->quote = LOG_QUOTE_QUOTES;
+         cur++;
+         break;
+     case '\'':
+         lt->quote = LOG_QUOTE_RAW;
+         cur++;
+         break;
+     case '[':
+         lt->quote = LOG_QUOTE_MIMEBLOB;
+         cur++;
+         break;
+     case '#':
+         lt->quote = LOG_QUOTE_URL;
+         cur++;
+         break;
+     default:
+         lt->quote = *quote;
+         break;
+     }
+     if (*cur == '-') {
+         lt->left = 1;
+         cur++;
+     }
+     if (*cur == '0') {
+         lt->zero = 1;
+         cur++;
+     }
+     if (xisdigit(*cur))
+         lt->width = strtol(cur, &cur, 10);
+     if (*cur == '.')
+         lt->precision = strtol(cur + 1, &cur, 10);
+     if (*cur == '{') {
+         char *cp;
+         cur++;
+         l = strcspn(cur, "}");
+         cp = (char *)xmalloc(l + 1);
+         xstrncpy(cp, cur, l + 1);
+         lt->data.string = cp;
+         cur += l;
+         if (*cur == '}')
+             cur++;
+     }
+     // For upward compatibility, assume "http::" prefix as default prefix
+     // for all log access formating codes, except those starting
+     // from "icap::", "adapt::" and "%"
+     if (strncmp(cur,"http::", 6) == 0 &&
+             strncmp(cur+6, "icap::", 6) != 0  &&
+             strncmp(cur+6, "adapt::", 12) != 0 && *(cur+6) != '%' ) {
+         cur += 6;
+     }
+     lt->type = LFT_NONE;
+     for (lte = logformat_token_table; lte->config != NULL; lte++) {
+         if (strncmp(lte->config, cur, strlen(lte->config)) == 0) {
+             lt->type = lte->token_type;
+             cur += strlen(lte->config);
+             break;
+         }
+     }
+     if (lt->type == LFT_NONE) {
+         fatalf("Can't parse configuration token: '%s'\n",
+                def);
+     }
+     if (*cur == ' ') {
+         lt->space = 1;
+         cur++;
+     }
+ done:
+     switch (lt->type) {
 -#if ICAP_CLIENT
 -                case LFT_ICAP_LAST_MATCHED_HEADER:
 -                    lt->type = LFT_ICAP_LAST_MATCHED_HEADER_ELEM;
++#if USE_ADAPTATION
++    case LFT_ADAPTATION_LAST_HEADER:
++#endif
++#if ICAP_CLIENT
+     case LFT_ICAP_REQ_HEADER:
+     case LFT_ICAP_REP_HEADER:
+ #endif
+     case LFT_ADAPTED_REQUEST_HEADER:
+     case LFT_REQUEST_HEADER:
+     case LFT_REPLY_HEADER:
+         if (lt->data.string) {
+             char *header = lt->data.string;
+             char *cp = strchr(header, ':');
+             if (cp) {
+                 *cp++ = '\0';
+                 if (*cp == ',' || *cp == ';' || *cp == ':')
+                     lt->data.header.separator = *cp++;
+                 else
+                     lt->data.header.separator = ',';
+                 lt->data.header.element = cp;
+                 switch (lt->type) {
+                 case LFT_REQUEST_HEADER:
+                     lt->type = LFT_REQUEST_HEADER_ELEM;
+                     break;
+                 case LFT_ADAPTED_REQUEST_HEADER:
+                     lt->type = LFT_ADAPTED_REQUEST_HEADER_ELEM;
+                     break;
+                 case LFT_REPLY_HEADER:
+                     lt->type = LFT_REPLY_HEADER_ELEM;
+                     break;
 -#if ICAP_CLIENT
 -            case LFT_ICAP_LAST_MATCHED_HEADER:
 -                lt->type = LFT_ICAP_LAST_MATCHED_ALL_HEADERS;
++#if USE_ADAPTATION
++                case LFT_ADAPTATION_LAST_HEADER:
++                    lt->type = LFT_ADAPTATION_LAST_HEADER_ELEM;
+                     break;
++#endif
++#if ICAP_CLIENT
+                 case LFT_ICAP_REQ_HEADER:
+                     lt->type = LFT_ICAP_REQ_HEADER_ELEM;
+                     break;
+                 case LFT_ICAP_REP_HEADER:
+                     lt->type = LFT_ICAP_REP_HEADER_ELEM;
+                     break;
+ #endif
+                 default:
+                     break;
+                 }
+             }
+             lt->data.header.header = header;
+         } else {
+             switch (lt->type) {
+             case LFT_REQUEST_HEADER:
+                 lt->type = LFT_REQUEST_ALL_HEADERS;
+                 break;
+             case LFT_ADAPTED_REQUEST_HEADER:
+                 lt->type = LFT_ADAPTED_REQUEST_ALL_HEADERS;
+                 break;
+             case LFT_REPLY_HEADER:
+                 lt->type = LFT_REPLY_ALL_HEADERS;
+                 break;
 -                case LFT_ICAP_LAST_MATCHED_HEADER_ELEM:
++#if USE_ADAPTATION
++            case LFT_ADAPTATION_LAST_HEADER:
++                lt->type = LFT_ADAPTATION_LAST_ALL_HEADERS;
+                 break;
++#endif
++#if ICAP_CLIENT
+             case LFT_ICAP_REQ_HEADER:
+                 lt->type = LFT_ICAP_REQ_ALL_HEADERS;
+                 break;
+             case LFT_ICAP_REP_HEADER:
+                 lt->type = LFT_ICAP_REP_ALL_HEADERS;
+                 break;
+ #endif
+             default:
+                 break;
+             }
+             Config.onoff.log_mime_hdrs = 1;
+         }
+         break;
+     case LFT_CLIENT_FQDN:
+         Config.onoff.log_fqdn = 1;
+         break;
+     case LFT_TIME_SUBSECOND:
+         lt->divisor = 1000;
+         if (lt->precision) {
+             int i;
+             lt->divisor = 1000000;
+             for (i = lt->precision; i > 1; i--)
+                 lt->divisor /= 10;
+             if (!lt->divisor)
+                 lt->divisor = 0;
+         }
+         break;
+     case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
+         debugs(46, 0, "WARNING: the \"Hs\" formating code is deprecated use the \">Hs\" instead");
+         lt->type = LFT_HTTP_SENT_STATUS_CODE;
+         break;
+     default:
+         break;
+     }
+     return (cur - def);
+ }
+ int
+ accessLogParseLogFormat(logformat_token ** fmt, char *def)
+ {
+     char *cur, *eos;
+     logformat_token *new_lt, *last_lt;
+     enum log_quote quote = LOG_QUOTE_NONE;
+     debugs(46, 2, "accessLogParseLogFormat: got definition '" << def << "'");
+     /* very inefficent parser, but who cares, this needs to be simple */
+     /* First off, let's tokenize, we'll optimize in a second pass.
+      * A token can either be a %-prefixed sequence (usually a dynamic
+      * token but it can be an escaped sequence), or a string. */
+     cur = def;
+     eos = def + strlen(def);
+     *fmt = new_lt = last_lt = (logformat_token *)xmalloc(sizeof(logformat_token));
+     cur += accessLogGetNewLogFormatToken(new_lt, cur, &quote);
+     while (cur < eos) {
+         new_lt = (logformat_token *)xmalloc(sizeof(logformat_token));
+         last_lt->next = new_lt;
+         last_lt = new_lt;
+         cur += accessLogGetNewLogFormatToken(new_lt, cur, &quote);
+     }
+     return 1;
+ }
+ void
+ accessLogDumpLogFormat(StoreEntry * entry, const char *name, logformat * definitions)
+ {
+     logformat_token *t;
+     logformat *format;
+     struct logformat_token_table_entry *te;
+     debugs(46, 4, "accessLogDumpLogFormat called");
+     for (format = definitions; format; format = format->next) {
+         debugs(46, 3, "Dumping logformat definition for " << format->name);
+         storeAppendPrintf(entry, "logformat %s ", format->name);
+         for (t = format->format; t; t = t->next) {
+             if (t->type == LFT_STRING)
+                 storeAppendPrintf(entry, "%s", t->data.string);
+             else {
+                 char argbuf[256];
+                 char *arg = NULL;
+                 logformat_bcode_t type = t->type;
+                 switch (type) {
+                     /* special cases */
+                 case LFT_STRING:
+                     break;
++#if USE_ADAPTATION
++                case LFT_ADAPTATION_LAST_HEADER_ELEM:
++#endif
+ #if ICAP_CLIENT
 -                        type = LFT_REQUEST_HEADER_ELEM;
+                 case LFT_ICAP_REQ_HEADER_ELEM:
+                 case LFT_ICAP_REP_HEADER_ELEM:
+ #endif
+                 case LFT_REQUEST_HEADER_ELEM:
+                 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
+                 case LFT_REPLY_HEADER_ELEM:
+                     if (t->data.header.separator != ',')
+                         snprintf(argbuf, sizeof(argbuf), "%s:%c%s", t->data.header.header, t->data.header.separator, t->data.header.element);
+                     else
+                         snprintf(argbuf, sizeof(argbuf), "%s:%s", t->data.header.header, t->data.header.element);
+                     arg = argbuf;
+                     switch (type) {
+                     case LFT_REQUEST_HEADER_ELEM:
 -                        type = LFT_ADAPTED_REQUEST_HEADER_ELEM;
++                        type = LFT_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
+                         break;
+                     case LFT_ADAPTED_REQUEST_HEADER_ELEM:
 -                        type = LFT_REPLY_HEADER_ELEM;
++                        type = LFT_ADAPTED_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
+                         break;
+                     case LFT_REPLY_HEADER_ELEM:
 -#if ICAP_CLIENT
 -                    case LFT_ICAP_LAST_MATCHED_HEADER_ELEM:
 -                        type = LFT_ICAP_LAST_MATCHED_HEADER;
++                        type = LFT_REPLY_HEADER_ELEM; // XXX: remove _ELEM?
+                         break;
 -                case LFT_ICAP_LAST_MATCHED_ALL_HEADERS:
++#if USE_ADAPTATION
++                    case LFT_ADAPTATION_LAST_HEADER_ELEM:
++                        type = LFT_ADAPTATION_LAST_HEADER;
+                         break;
++#endif
++#if ICAP_CLIENT
+                     case LFT_ICAP_REQ_HEADER_ELEM:
+                         type = LFT_ICAP_REQ_HEADER;
+                         break;
+                     case LFT_ICAP_REP_HEADER_ELEM:
+                         type = LFT_ICAP_REP_HEADER;
+                         break;
+ #endif
+                     default:
+                         break;
+                     }
+                     break;
+                 case LFT_REQUEST_ALL_HEADERS:
+                 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
+                 case LFT_REPLY_ALL_HEADERS:
++#if USE_ADAPTATION
++                case LFT_ADAPTATION_LAST_ALL_HEADERS:
++#endif
+ #if ICAP_CLIENT
 -#if ICAP_CLIENT
 -                    case LFT_ICAP_LAST_MATCHED_ALL_HEADERS:
 -                        type = LFT_ICAP_LAST_MATCHED_HEADER;
+                 case LFT_ICAP_REQ_ALL_HEADERS:
+                 case LFT_ICAP_REP_ALL_HEADERS:
+ #endif
+                     switch (type) {
+                     case LFT_REQUEST_ALL_HEADERS:
+                         type = LFT_REQUEST_HEADER;
+                         break;
+                     case LFT_ADAPTED_REQUEST_ALL_HEADERS:
+                         type = LFT_ADAPTED_REQUEST_HEADER;
+                         break;
+                     case LFT_REPLY_ALL_HEADERS:
+                         type = LFT_REPLY_HEADER;
+                         break;
++#if USE_ADAPTATION
++                    case LFT_ADAPTATION_LAST_ALL_HEADERS:
++                        type = LFT_ADAPTATION_LAST_HEADER;
+                         break;
++#endif
++#if ICAP_CLIENT
+                     case LFT_ICAP_REQ_ALL_HEADERS:
+                         type = LFT_ICAP_REQ_HEADER;
+                         break;
+                     case LFT_ICAP_REP_ALL_HEADERS:
+                         type = LFT_ICAP_REP_HEADER;
+                         break;
+ #endif
+                     default:
+                         break;
+                     }
+                     break;
+                 default:
+                     if (t->data.string)
+                         arg = t->data.string;
+                     break;
+                 }
+                 entry->append("%", 1);
+                 switch (t->quote) {
+                 case LOG_QUOTE_QUOTES:
+                     entry->append("\"", 1);
+                     break;
+                 case LOG_QUOTE_MIMEBLOB:
+                     entry->append("[", 1);
+                     break;
+                 case LOG_QUOTE_URL:
+                     entry->append("#", 1);
+                     break;
+                 case LOG_QUOTE_RAW:
+                     entry->append("'", 1);
+                     break;
+                 case LOG_QUOTE_NONE:
+                     break;
+                 }
+                 if (t->left)
+                     entry->append("-", 1);
+                 if (t->zero)
+                     entry->append("0", 1);
+                 if (t->width)
+                     storeAppendPrintf(entry, "%d", (int) t->width);
+                 if (t->precision)
+                     storeAppendPrintf(entry, ".%d", (int) t->precision);
+                 if (arg)
+                     storeAppendPrintf(entry, "{%s}", arg);
+                 for (te = logformat_token_table; te->config != NULL; te++) {
+                     if (te->token_type == type) {
+                         storeAppendPrintf(entry, "%s", te->config);
+                         break;
+                     }
+                 }
+                 if (t->space)
+                     entry->append(" ", 1);
+                 assert(te->config != NULL);
+             }
+         }
+         entry->append("\n", 1);
+     }
+ }
+ void
+ accessLogFreeLogFormat(logformat_token ** tokens)
+ {
+     while (*tokens) {
+         logformat_token *token = *tokens;
+         *tokens = token->next;
+         safe_free(token->data.string);
+         xfree(token);
+     }
+ }
+ logformat::logformat(const char *n) :
+         format(NULL),
+         next(NULL)
+ {
+     name = xstrdup(n);
+ }
+ logformat::~logformat()
+ {
+     // erase the list without consuming stack space
+     while (next) {
+         // unlink the next entry for deletion
+         logformat *temp = next;
+         next = temp->next;
+         temp->next = NULL;
+         delete temp;
+     }
+     // remove locals
+     xfree(name);
+     accessLogFreeLogFormat(&format);
+ }
index 0000000000000000000000000000000000000000,36316d454e6c455c45ed3797d6339534a7a49a6b..54cffff0d0271f3fd40668bdd4cbdc7fc2793253
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,246 +1,246 @@@
 -    LFT_ICAP_LAST_MATCHED_HEADER,
 -    LFT_ICAP_LAST_MATCHED_HEADER_ELEM,
 -    LFT_ICAP_LAST_MATCHED_ALL_HEADERS,
+ /*
+  * $Id$
+  *
+  * DEBUG: section 46    Access Log
+  * 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.
+  *
+  */
+ #ifndef _SQUID_LOG_TOKENS_H
+ #define _SQUID_LOG_TOKENS_H
+ class StoreEntry;
+ #define LOG_BUF_SZ (MAX_URL<<2)
+ /*
+  * Bytecodes for the configureable logformat stuff
+  */
+ typedef enum {
+     LFT_NONE,                 /* dummy */
+     LFT_STRING,
+     LFT_CLIENT_IP_ADDRESS,
+     LFT_CLIENT_FQDN,
+     LFT_CLIENT_PORT,
+ #if USE_SQUID_EUI
+     LFT_CLIENT_EUI,
+ #endif
+     /*LFT_SERVER_IP_ADDRESS, */
+     LFT_SERVER_IP_OR_PEER_NAME,
+     /*LFT_SERVER_PORT, */
+     LFT_LOCAL_IP,
+     LFT_LOCAL_PORT,
+     /*LFT_LOCAL_NAME, */
+     LFT_PEER_LOCAL_PORT,
+     LFT_TIME_SECONDS_SINCE_EPOCH,
+     LFT_TIME_SUBSECOND,
+     LFT_TIME_LOCALTIME,
+     LFT_TIME_GMT,
+     LFT_TIME_TO_HANDLE_REQUEST,
+     LFT_PEER_RESPONSE_TIME,
+     LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME,
+     LFT_DNS_WAIT_TIME,
+     LFT_REQUEST_HEADER,
+     LFT_REQUEST_HEADER_ELEM,
+     LFT_REQUEST_ALL_HEADERS,
+     LFT_ADAPTED_REQUEST_HEADER,
+     LFT_ADAPTED_REQUEST_HEADER_ELEM,
+     LFT_ADAPTED_REQUEST_ALL_HEADERS,
+     LFT_REPLY_HEADER,
+     LFT_REPLY_HEADER_ELEM,
+     LFT_REPLY_ALL_HEADERS,
+     LFT_USER_NAME,
+     LFT_USER_LOGIN,
+     LFT_USER_IDENT,
+     /*LFT_USER_REALM, */
+     /*LFT_USER_SCHEME, */
+     LFT_USER_EXTERNAL,
+     LFT_HTTP_SENT_STATUS_CODE_OLD_30,
+     LFT_HTTP_SENT_STATUS_CODE,
+     LFT_HTTP_RECEIVED_STATUS_CODE,
+     /*LFT_HTTP_STATUS, */
+     LFT_HTTP_BODY_BYTES_READ,
+     LFT_SQUID_STATUS,
+     LFT_SQUID_ERROR,
+     LFT_SQUID_ERROR_DETAIL,
+     LFT_SQUID_HIERARCHY,
+     LFT_MIME_TYPE,
+     LFT_REQUEST_METHOD,
+     LFT_REQUEST_URI,
+     LFT_REQUEST_URLPATH,
+     /*LFT_REQUEST_QUERY, * // * this is not needed. see strip_query_terms */
+     LFT_REQUEST_VERSION,
+     LFT_REQUEST_SIZE_TOTAL,
+     /*LFT_REQUEST_SIZE_LINE, */
+     LFT_REQUEST_SIZE_HEADERS,
+     /*LFT_REQUEST_SIZE_BODY, */
+     /*LFT_REQUEST_SIZE_BODY_NO_TE, */
+     LFT_REPLY_SIZE_TOTAL,
+     LFT_REPLY_HIGHOFFSET,
+     LFT_REPLY_OBJECTSIZE,
+     /*LFT_REPLY_SIZE_LINE, */
+     LFT_REPLY_SIZE_HEADERS,
+     /*LFT_REPLY_SIZE_BODY, */
+     /*LFT_REPLY_SIZE_BODY_NO_TE, */
+     LFT_TAG,
+     LFT_IO_SIZE_TOTAL,
+     LFT_EXT_LOG,
+     LFT_SEQUENCE_NUMBER,
+ #if USE_ADAPTATION
+     LTF_ADAPTATION_SUM_XACT_TIMES,
+     LTF_ADAPTATION_ALL_XACT_TIMES,
++    LFT_ADAPTATION_LAST_HEADER,
++    LFT_ADAPTATION_LAST_HEADER_ELEM,
++    LFT_ADAPTATION_LAST_ALL_HEADERS,
+ #endif
+ #if ICAP_CLIENT
+     LFT_ICAP_TOTAL_TIME,
+     LFT_ICAP_ADDR,
+     LFT_ICAP_SERV_NAME,
+     LFT_ICAP_REQUEST_URI,
+     LFT_ICAP_REQUEST_METHOD,
+     LFT_ICAP_BYTES_SENT,
+     LFT_ICAP_BYTES_READ,
+     LFT_ICAP_BODY_BYTES_READ,
+     LFT_ICAP_REQ_HEADER,
+     LFT_ICAP_REQ_HEADER_ELEM,
+     LFT_ICAP_REQ_ALL_HEADERS,
+     LFT_ICAP_REP_HEADER,
+     LFT_ICAP_REP_HEADER_ELEM,
+     LFT_ICAP_REP_ALL_HEADERS,
+     LFT_ICAP_TR_RESPONSE_TIME,
+     LFT_ICAP_IO_TIME,
+     LFT_ICAP_OUTCOME,
+     LFT_ICAP_STATUS_CODE,
+ #endif
+     LFT_PERCENT                       /* special string cases for escaped chars */
+ } logformat_bcode_t;
+ enum log_quote {
+     LOG_QUOTE_NONE = 0,
+     LOG_QUOTE_QUOTES,
+     LOG_QUOTE_MIMEBLOB,
+     LOG_QUOTE_URL,
+     LOG_QUOTE_RAW
+ };
+ /* FIXME: public class so we can pre-define its type. */
+ class logformat_token
+ {
+ public:
+     logformat_bcode_t type;
+     union {
+         char *string;
+         struct {
+             char *header;
+             char *element;
+             char separator;
+         } header;
+         char *timespec;
+     } data;
+     unsigned char width;
+     unsigned char precision;
+     enum log_quote quote;
+     unsigned int left:1;
+     unsigned int space:1;
+     unsigned int zero:1;
+     int divisor;
+     logformat_token *next;    /* todo: move from linked list to array */
+ };
+ struct logformat_token_table_entry {
+     const char *config;
+     logformat_bcode_t token_type;
+     int options;
+ };
+ class logformat
+ {
+ public:
+     logformat(const char *name);
+     ~logformat();
+     char *name;
+     logformat_token *format;
+     logformat *next;
+ };
+ extern const char *log_tags[];
+ extern struct logformat_token_table_entry logformat_token_table[];
+ #if USE_ADAPTATION
+ extern bool alLogformatHasAdaptToken;
+ #endif
+ #if ICAP_CLIENT
+ extern bool alLogformatHasIcapToken;
+ #endif
+ /* parses a single token. Returns the token length in characters,
+  * and fills in the lt item with the token information.
+  * def is for sure null-terminated
+  */
+ int accessLogGetNewLogFormatToken(logformat_token * lt, char *def, enum log_quote *quote);
+ /* very inefficent parser, but who cares, this needs to be simple */
+ /* First off, let's tokenize, we'll optimize in a second pass.
+  * A token can either be a %-prefixed sequence (usually a dynamic
+  * token but it can be an escaped sequence), or a string. */
+ int accessLogParseLogFormat(logformat_token ** fmt, char *def);
+ void accessLogDumpLogFormat(StoreEntry * entry, const char *name, logformat * definitions);
+ void accessLogFreeLogFormat(logformat_token ** tokens);
+ #endif /* _SQUID_LOG_TOKENS_H */
Simple merge