What this does is convert ErrorState object to using the generic libformat.la parser and macro expansions instead of its own rather limited custom ones for error pages and deny_info URL creation.

This patch is a [PREVIEW] instead of [PATCH] because of the large performance issues detailed in NOTES below which I would like to see fixed before this goes into trunk. Any assistance there would be welcome. Other than that it appears to be fully working for at least the default level of build tests and a test page using all the codes appears to be correct.


Change Summary:
1) register the 1-byte error page macros as libformat codes
2) convert the ErrorState::Convert macro expander to use those codes instead of char literals 3) extend the libformat to handle several process-wide informational macros and a human-readabhe RFC1123 time format 4) extend the Format::assemble method to call the adapted ErrorState::Convert function when it encounters byte codes requiring ErrorState special handling 5) convert the ErrorState::ConvertText and ErrorState::DenyInfoLocation methods to use the libformat API to parse and assemble the output page

NOTES:
*a) Several of the error page macros are better served by other logformat macro expansions. However on some the interaction between deny_info and ERR_* template usages has required that the ErrorState::Convert logics be used for now. I am not sure at this poiint if they should stay in ErrorState long term and be treated like the Ssl::ErrorDetails macros, or be shifted to libformat.

*b) Doing a whole parse cycle for every error response is very costly. We are already loading the text page files on every error response. This adds the un-optimized libformat parser lag on top of that. To avoid this we really need to add a cache for error page templates and strings, such that each one is only loaded and parsed once. From that we can more speedily assemble the expansions final output.

*c) For now AccessLogEntry equired by the libformat API is generated by ErrorState just before use. Ideally the code creating the ErrorState should pass it an already filled out ALE object for the transaction. This way we will get a lot more of the libformat macros filled out.

*d) To manage libformat calling ErrorState::Convert as a method, and recursive macros saving on AccessLogEntry object creation this adds an ErrorState* to AccessLogEntry and vice versa. For now creating a circular referance loop at runtime. The ALE object is "owned" by the ErrorState so the ErriorState::ale_ is a Pointer, whereas the AccessLogEntry::errorDetails is a raw-pointer explicitly set/unset only set when teh AccessLogEntry is in use by ErrorState code, and unconditionally dropped to avoid circular existence dependencies. ** any assistance making a design to avoid this circular linkage is welcome.


Amos
=== modified file 'src/AccessLogEntry.h'
--- src/AccessLogEntry.h        2013-03-17 12:19:16 +0000
+++ src/AccessLogEntry.h        2013-04-07 02:39:03 +0000
@@ -48,10 +48,10 @@
 #include "ssl/gadgets.h"
 #endif
 
-/* forward decls */
+class ErrorState;
+class CustomLog;
 class HttpReply;
 class HttpRequest;
-class CustomLog;
 
 class AccessLogEntry: public RefCountable
 {
@@ -60,7 +60,7 @@
     typedef RefCount<AccessLogEntry> Pointer;
 
     AccessLogEntry() : url(NULL), tcpClient(), reply(NULL), request(NULL),
-            adapted_request(NULL) {}
+            adapted_request(NULL), errorDetails(NULL) {}
     ~AccessLogEntry();
 
     /// Fetch the client IP log string into the given buffer.
@@ -279,6 +279,9 @@
     }
     icap;
 #endif
+
+    /// details of the error page presented to the client (if any)
+    ErrorState *errorDetails;
 };
 
 class ACLChecklist;

=== modified file 'src/base/StringArea.h'
--- src/base/StringArea.h       2011-10-05 00:19:46 +0000
+++ src/base/StringArea.h       2013-04-07 02:39:36 +0000
@@ -35,6 +35,7 @@
 #if HAVE_CSTRING
 #include <cstring>
 #endif
+#include <ostream>
 
 /** A char* plus length combination. Useful for temporary storing
  * and quickly looking up strings.
@@ -56,6 +57,8 @@
         return (theLen < s.theLen || (theLen == s.theLen && 
memcmp(theStart,s.theStart,theLen) < 0)) ;
     }
 
+    void print(std::ostream &os) const { os.write(theStart, theLen); }
+
 private:
     /// pointed to the externally-managed memory area
     const char *theStart;
@@ -63,4 +66,11 @@
     size_t theLen;
 };
 
+inline std::ostream &
+operator <<(std::ostream &os, const StringArea &a)
+{
+    a.print(os);
+    return os;
+}
+
 #endif /* SQUID_STRINGAREA_H */

=== modified file 'src/errorpage.cc'
--- src/errorpage.cc    2013-03-17 12:19:16 +0000
+++ src/errorpage.cc    2013-04-07 02:39:42 +0000
@@ -31,11 +31,13 @@
  */
 #include "squid.h"
 #include "cache_cf.h"
+#include "client_side.h"
 #include "comm/Connection.h"
 #include "comm/Write.h"
 #include "disk.h"
 #include "err_detail_type.h"
 #include "errorpage.h"
+#include "format/Format.h"
 #include "ftp.h"
 #include "Store.h"
 #include "html_quote.h"
@@ -591,7 +593,9 @@
 #if USE_SSL
         detail(NULL),
 #endif
-        detailCode(ERR_DETAIL_NONE)
+        detailCode(ERR_DETAIL_NONE),
+        building_deny_info_url(false),
+        allowRecursion(true)
 {
     memset(&ftp, 0, sizeof(ftp));
 
@@ -694,6 +698,10 @@
 
 ErrorState::~ErrorState()
 {
+    if (ale_ != NULL) {
+        // ensure that the AccessLogEntry object has no reference to us
+        ale_->errorDetails = NULL;
+    }
     HTTPMSGUNLOCK(request);
     safe_free(redirect_url);
     safe_free(url);
@@ -796,7 +804,7 @@
 #define CVT_BUF_SZ 512
 
 const char *
-ErrorState::Convert(char token, bool building_deny_info_url, bool 
allowRecursion)
+ErrorState::Convert(Format::ByteCode_t token)
 {
     static MemBuf mb;
     const char *p = NULL;      /* takes priority over mb if set */
@@ -808,30 +816,7 @@
 
     switch (token) {
 
-    case 'a':
-#if USE_AUTH
-        if (request && request->auth_user_request != NULL)
-            p = request->auth_user_request->username();
-        if (!p)
-#endif
-            p = "-";
-        break;
-
-    case 'b':
-        mb.Printf("%d", getMyPort());
-        break;
-
-    case 'B':
-        if (building_deny_info_url) break;
-        p = request ? ftpUrlWith2f(request) : "[no URL]";
-        break;
-
-    case 'c':
-        if (building_deny_info_url) break;
-        p = errorPageName(type);
-        break;
-
-    case 'D':
+    case Format::LFT_ERR_PAGE_UD: // '%D'
         if (!allowRecursion)
             p = "%D";  // if recursion is not allowed, do not convert
 #if USE_SSL
@@ -840,7 +825,9 @@
             detail->useRequest(request);
             const String &errDetail = detail->toString();
             if (errDetail.defined()) {
-                MemBuf *detail_mb  = ConvertText(errDetail.termedBuf(), false);
+                allowRecursion = false;
+                MemBuf *detail_mb  = ConvertText(errDetail.termedBuf());
+                allowRecursion = true;
                 mb.append(detail_mb->content(), detail_mb->contentSize());
                 delete detail_mb;
                 do_quote = 0;
@@ -851,18 +838,18 @@
             mb.Printf("[No Error Detail]");
         break;
 
-    case 'e':
+    case Format::LFT_ERR_PAGE_LE: // '%e'
         mb.Printf("%d", xerrno);
         break;
 
-    case 'E':
+    case Format::LFT_ERR_PAGE_UE: // '%E'
         if (xerrno)
             mb.Printf("(%d) %s", xerrno, strerror(xerrno));
         else
             mb.Printf("[No Error]");
         break;
 
-    case 'f':
+    case Format::LFT_ERR_PAGE_LF: // '%f'
         if (building_deny_info_url) break;
         /* FTP REQUEST LINE */
         if (ftp.request)
@@ -871,7 +858,7 @@
             p = "nothing";
         break;
 
-    case 'F':
+    case Format::LFT_ERR_PAGE_UF: // '%F'
         if (building_deny_info_url) break;
         /* FTP REPLY LINE */
         if (ftp.reply)
@@ -880,7 +867,7 @@
             p = "nothing";
         break;
 
-    case 'g':
+    case Format::LFT_ERR_PAGE_LG: // '%g'
         if (building_deny_info_url) break;
         /* FTP SERVER RESPONSE */
         if (ftp.listing) {
@@ -891,38 +878,13 @@
         }
         break;
 
-    case 'h':
-        mb.Printf("%s", getMyHostname());
-        break;
-
-    case 'H':
-        if (request) {
-            if (request->hier.host[0] != '\0') // if non-empty string.
-                p = request->hier.host;
-            else
-                p = request->GetHost();
-        } else if (!building_deny_info_url)
-            p = "[unknown host]";
-        break;
-
-    case 'i':
-        mb.Printf("%s", src_addr.NtoA(ntoabuf,MAX_IPSTRLEN));
-        break;
-
-    case 'I':
-        if (request && request->hier.tcpServer != NULL)
-            p = request->hier.tcpServer->remote.NtoA(ntoabuf,MAX_IPSTRLEN);
-        else if (!building_deny_info_url)
-            p = "[unknown]";
-        break;
-
-    case 'l':
+    case Format::LFT_ERR_PAGE_LL: // '%l'
         if (building_deny_info_url) break;
         mb.append(error_stylesheet.content(), error_stylesheet.contentSize());
         do_quote = 0;
         break;
 
-    case 'L':
+    case Format::LFT_ERR_PAGE_UL: // '%L'
         if (building_deny_info_url) break;
         if (Config.errHtmlText) {
             mb.Printf("%s", Config.errHtmlText);
@@ -931,7 +893,7 @@
             p = "[not available]";
         break;
 
-    case 'm':
+    case Format::LFT_ERR_PAGE_LM: // '%m'
         if (building_deny_info_url) break;
 #if USE_AUTH
         p = auth_user_request->denyMessage("[not available]");
@@ -940,28 +902,13 @@
 #endif
         break;
 
-    case 'M':
-        if (request)
-            p = RequestMethodStr(request->method);
-        else if (!building_deny_info_url)
-            p= "[unknown method]";
-        break;
-
-    case 'o':
+    case Format::LFT_ERR_PAGE_LO: // '%o'
         p = request ? request->extacl_message.termedBuf() : 
external_acl_message;
         if (!p && !building_deny_info_url)
             p = "[not available]";
         break;
 
-    case 'p':
-        if (request) {
-            mb.Printf("%d", (int) request->port);
-        } else if (!building_deny_info_url) {
-            p = "[unknown port]";
-        }
-        break;
-
-    case 'P':
+    case Format::LFT_ERR_PAGE_UP: // '%P'
         if (request) {
             p = AnyP::ProtocolType_str[request->protocol];
         } else if (!building_deny_info_url) {
@@ -969,7 +916,7 @@
         }
         break;
 
-    case 'R':
+    case Format::LFT_ERR_PAGE_UR: // '%R'
         if (building_deny_info_url) {
             p = (request->urlpath.size() != 0 ? request->urlpath.termedBuf() : 
"/");
             no_urlescape = 1;
@@ -999,7 +946,7 @@
         }
         break;
 
-    case 's':
+    case Format::LFT_ERR_PAGE_LS: // '%s'
         /* for backward compat we make %s show the full URL. Drop this in some 
future release. */
         if (building_deny_info_url) {
             p = request ? urlCanonical(request) : url;
@@ -1008,7 +955,7 @@
             p = visible_appname_string;
         break;
 
-    case 'S':
+    case Format::LFT_ERR_PAGE_US: // '%S'
         if (building_deny_info_url) {
             p = visible_appname_string;
             break;
@@ -1029,49 +976,14 @@
         }
         break;
 
-    case 't':
-        mb.Printf("%s", Time::FormatHttpd(squid_curtime));
-        break;
-
-    case 'T':
-        mb.Printf("%s", mkrfc1123(squid_curtime));
-        break;
-
-    case 'U':
-        /* Using the fake-https version of canonical so error pages see 
https:// */
-        /* even when the url-path cannot be shown as more than '*' */
-        if (request)
-            p = urlCanonicalFakeHttps(request);
-        else if (url)
-            p = url;
-        else if (!building_deny_info_url)
-            p = "[no URL]";
-        break;
-
-    case 'u':
-        if (request)
-            p = urlCanonical(request);
-        else if (url)
-            p = url;
-        else if (!building_deny_info_url)
-            p = "[no URL]";
-        break;
-
-    case 'w':
-        if (Config.adminEmail)
-            mb.Printf("%s", Config.adminEmail);
-        else if (!building_deny_info_url)
-            p = "[unknown]";
-        break;
-
-    case 'W':
+    case Format::LFT_ERR_PAGE_UW: // '%W'
         if (building_deny_info_url) break;
         if (Config.adminEmail && Config.onoff.emailErrData)
             Dump(&mb);
         no_urlescape = 1;
         break;
 
-    case 'x':
+    case Format::LFT_ERR_PAGE_LX: // '%x'
 #if USE_SSL
         if (detail)
             mb.Printf("%s", detail->errorName());
@@ -1081,7 +993,7 @@
                 p = "[Unknown Error Code]";
         break;
 
-    case 'z':
+    case Format::LFT_ERR_PAGE_LZ: // '%z'
         if (building_deny_info_url) break;
         if (dnsError.size() > 0)
             p = dnsError.termedBuf();
@@ -1091,7 +1003,7 @@
             p = "[unknown]";
         break;
 
-    case 'Z':
+    case Format::LFT_ERR_PAGE_UZ: // '%Z'
         if (building_deny_info_url) break;
         if (err_msg)
             p = err_msg;
@@ -1099,13 +1011,7 @@
             p = "[unknown]";
         break;
 
-    case '%':
-        p = "%";
-        break;
-
     default:
-        mb.Printf("%%%c", token);
-        do_quote = 0;
         break;
     }
 
@@ -1128,24 +1034,27 @@
 void
 ErrorState::DenyInfoLocation(const char *name, HttpRequest *aRequest, MemBuf 
&result)
 {
+    building_deny_info_url = true;
+
+    // XXX: make this process much faster by pre-parsing the deny_info pattern
+    // and just assembling the Location: output here.
+
     char const *m = name;
-    char const *p = m;
-    char const *t;
 
     if (m[0] == '3')
         m += 4; // skip "3xx:"
 
-    while ((p = strchr(m, '%'))) {
-        result.append(m, p - m);       /* copy */
-        t = Convert(*++p, true, true);       /* convert */
-        result.Printf("%s", t);        /* copy */
-        m = p + 1;                     /* advance */
-    }
-
-    if (*m)
-        result.Printf("%s", m);        /* copy tail */
-
-    assert((size_t)result.contentSize() == strlen(result.content()));
+    MemBuf *res = ConvertText(m);
+
+    // yuck! steal the res buffer for our output
+    result.buf = res->buf;
+    result.size = res->size;
+    result.capacity = res->capacity;
+    result.max_capacity = res->max_capacity;
+    res->stolen = true; // prevent delete erasing the buffer we stole.
+    delete res;
+
+    building_deny_info_url = false;
 }
 
 HttpReply *
@@ -1278,7 +1187,7 @@
         debugs(4, 2, HERE << "No existing error page language negotiated for " 
<< errorPageName(page_id) << ". Using default error file.");
     }
 
-    MemBuf *result = ConvertText(m, true);
+    MemBuf *result = ConvertText(m);
 #if USE_ERR_LOCALES
     if (localeTmpl)
         delete localeTmpl;
@@ -1286,25 +1195,46 @@
     return result;
 }
 
-MemBuf *ErrorState::ConvertText(const char *text, bool allowRecursion)
+MemBuf *
+ErrorState::ConvertText(const char *text)
 {
+    // parse the text using Format:: parser
+    Format::Format fmt("errorPageTemporary");
+    fmt.parse(text);
+
+    // XXX: if request exists, we might use ALE from 
request->lientConnectionManager->http.al ??
+
+    // if we are in a recursive pattern (%D or %S) ALE may already be setup
+    // or if the creator of this ErrorState was kind enough to supply the real 
transaction ALE object
+    if (ale_ == NULL) {
+        ale_ = new AccessLogEntry();
+
+        // fill ale_ with all the details Format::assemble() will need to 
process error page codes.
+
+        assert(request);
+
+        if (request->auth_user_request != NULL)
+            ale_->cache.authuser = request->auth_user_request->username();
+        // XXX: we only know about one request here.
+        ale_->request = request;
+        HTTPMSGLOCK(request);
+        ale_->adapted_request = request;
+        HTTPMSGLOCK(request);
+        ale_->_private.method_str = RequestMethodStr(request->method);
+        const char *hierHost = (request->hier.host[0] != '\0' ? 
request->hier.host : request->GetHost());
+        ale_->hier.note(request->hier.tcpServer, hierHost);
+
+        if (request->clientConnectionManager.valid())
+            ale_->tcpClient = 
request->clientConnectionManager->clientConnection;
+
+        ale_->url = (request ? urlCanonicalFakeHttps(request) : url);
+    }
+    ale_->errorDetails = this;
+
+    // build the actual object text
     MemBuf *content = new MemBuf;
-    const char *p;
-    const char *m = text;
-    assert(m);
     content->init();
-
-    while ((p = strchr(m, '%'))) {
-        content->append(m, p - m);     /* copy */
-        const char *t = Convert(*++p, false, allowRecursion);  /* convert */
-        content->Printf("%s", t);      /* copy */
-        m = p + 1;                     /* advance */
-    }
-
-    if (*m)
-        content->Printf("%s", m);      /* copy tail */
-
-    assert((size_t)content->contentSize() == strlen(content->content()));
+    fmt.assemble(*content, ale_, 0);
 
     return content;
 }

=== modified file 'src/errorpage.h'
--- src/errorpage.h     2013-03-16 04:57:43 +0000
+++ src/errorpage.h     2013-04-07 02:39:42 +0000
@@ -34,10 +34,12 @@
 #ifndef   SQUID_ERRORPAGE_H
 #define   SQUID_ERRORPAGE_H
 
+#include "AccessLogEntry.h"
 #include "cbdata.h"
 #include "comm/forward.h"
 #include "err_detail_type.h"
 #include "err_type.h"
+#include "format/ByteCode.h"
 #include "http/StatusCode.h"
 #include "ip/Address.h"
 #include "SquidString.h"
@@ -109,6 +111,17 @@
     /// set error type-specific detail code
     void detailError(int dCode) {detailCode = dCode;}
 
+    /** \deprecated
+     * Map some Error page and deny_info template % codes into textual output.
+     * Only the LFT_ERR_PAGE_* ones are mapped, others are ignored.
+     *
+     * Several of the codes produce blocks of non-URL compatible results.
+     * When processing the deny_info location URL they will be skipped.
+     *
+     * \param token                    The token following % which need to be 
converted
+     */
+    const char *Convert(Format::ByteCode_t token);
+
 private:
     /**
      * Locates error page template to be used for this error
@@ -120,9 +133,8 @@
      * Convert the given template string into textual output
      *
      * \param text            The string to be converted
-     * \param allowRecursion  Whether to convert codes which output may 
contain codes
      */
-    MemBuf *ConvertText(const char *text, bool allowRecursion);
+    MemBuf *ConvertText(const char *text);
 
     /**
      * Generates the Location: header value for a deny_info error page
@@ -131,18 +143,6 @@
     void DenyInfoLocation(const char *name, HttpRequest *request, MemBuf 
&result);
 
     /**
-     * Map the Error page and deny_info template % codes into textual output.
-     *
-     * Several of the codes produce blocks of non-URL compatible results.
-     * When processing the deny_info location URL they will be skipped.
-     *
-     * \param token                    The token following % which need to be 
converted
-     * \param building_deny_info_url   Perform special deny_info actions, such 
as URL-encoding and token skipping.
-     * \ allowRecursion   True if the codes which do recursions should 
converted
-     */
-    const char *Convert(char token, bool building_deny_info_url, bool 
allowRecursion);
-
-    /**
      * CacheManager / Debug dump of the ErrorState object.
      * Writes output into the given MemBuf.
      \retval 0 successful completion.
@@ -164,7 +164,7 @@
     String dnsError; ///< DNS lookup error message
     time_t ttl;
 
-    Ip::Address src_addr;
+    Ip::Address src_addr; // client Comm::Connection ??
     char *redirect_url;
     ERCB *callback;
     void *callback_data;
@@ -186,7 +186,17 @@
     /// type-specific detail about the transaction error;
     /// overwrites xerrno; overwritten by detail, if any.
     int detailCode;
+
 private:
+    /// Perform special deny_info actions, such as URL-encoding and token 
skipping.
+    bool building_deny_info_url;
+
+    /// Whether to convert codes which output may contain codes
+    bool allowRecursion;
+
+    /// an AccessLogEntry we are using to generate output
+    AccessLogEntry::Pointer ale_;
+
     CBDATA_CLASS2(ErrorState);
 };
 

=== modified file 'src/format/ByteCode.h'
--- src/format/ByteCode.h       2013-02-11 23:11:12 +0000
+++ src/format/ByteCode.h       2013-04-07 02:39:45 +0000
@@ -136,6 +136,7 @@
     LFT_TIME_SUBSECOND,
     LFT_TIME_LOCALTIME,
     LFT_TIME_GMT,
+    LFT_TIME_GMT_RFC1123,
 
     /* processing time details */
     LFT_TIME_TO_HANDLE_REQUEST,
@@ -143,6 +144,12 @@
     LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME,
     LFT_DNS_WAIT_TIME,
 
+    /* Squid application details */
+    LFT_SQUID_KID_HOSTNAME,
+    LFT_SQUID_KID_PORT,
+    LFT_SQUID_KID_APPSTRING,
+    LFT_SQUID_KID_ADMINEMAIL,
+
     /* Squid internal processing details */
     LFT_SQUID_STATUS,
     LFT_SQUID_ERROR,
@@ -196,6 +203,26 @@
     LFT_SSL_USER_CERT_ISSUER,
 #endif
 
+    /* Squid Error Page special items U=Upper, L=lower cased alphabet codes */
+    LFT_ERR_PAGE_UD,
+    LFT_ERR_PAGE_LE,
+    LFT_ERR_PAGE_UE,
+    LFT_ERR_PAGE_LF,
+    LFT_ERR_PAGE_UF,
+    LFT_ERR_PAGE_LG,
+    LFT_ERR_PAGE_LL,
+    LFT_ERR_PAGE_UL,
+    LFT_ERR_PAGE_LM,
+    LFT_ERR_PAGE_LO,
+    LFT_ERR_PAGE_UP,
+    LFT_ERR_PAGE_UR,
+    LFT_ERR_PAGE_LS,
+    LFT_ERR_PAGE_US,
+    LFT_ERR_PAGE_UW,
+    LFT_ERR_PAGE_LX,
+    LFT_ERR_PAGE_LZ,
+    LFT_ERR_PAGE_UZ,
+
     LFT_NOTE,
     LFT_PERCENT                        /* special string cases for escaped 
chars */
 } ByteCode_t;

=== modified file 'src/format/Format.cc'
--- src/format/Format.cc        2013-03-16 04:57:43 +0000
+++ src/format/Format.cc        2013-04-07 02:39:45 +0000
@@ -9,11 +9,14 @@
 #include "format/Quoting.h"
 #include "format/Token.h"
 #include "fqdncache.h"
+#include "globals.h"
 #include "HttpRequest.h"
 #include "MemBuf.h"
 #include "rfc1738.h"
+#include "SquidConfig.h"
 #include "SquidTime.h"
 #include "Store.h"
+#include "tools.h"
 #include "URL.h"
 #if USE_SSL
 #include "ssl/ErrorDetail.h"
@@ -442,7 +445,7 @@
             break;
 
         case LFT_TIME_LOCALTIME:
-
+        case LFT_TIME_GMT_RFC1123:
         case LFT_TIME_GMT: {
             const char *spec;
 
@@ -453,6 +456,12 @@
                 if (!spec)
                     spec = "%d/%b/%Y:%H:%M:%S %z";
                 t = localtime(&squid_curtime);
+
+            } else if (fmt->type == LFT_TIME_GMT_RFC1123) {
+                if (!spec)
+                    spec = "%a, %d %b %Y %H:%M:%S GMT";
+                t = gmtime(&squid_curtime);
+
             } else {
                 if (!spec)
                     spec = "%d/%b/%Y:%H:%M:%S";
@@ -819,6 +828,23 @@
             // or internal error messages).
             break;
 
+        case LFT_SQUID_KID_HOSTNAME:
+            out = getMyHostname();
+            break;
+
+        case LFT_SQUID_KID_PORT:
+            outint = getMyPort();
+            doint = 1;
+            break;
+
+        case LFT_SQUID_KID_APPSTRING:
+            out = visible_appname_string;
+            break;
+
+        case LFT_SQUID_KID_ADMINEMAIL:
+            out = Config.adminEmail;
+            break;
+
         case LFT_SQUID_STATUS:
             if (al->http.timedout || al->http.aborted) {
                 snprintf(tmp, sizeof(tmp), "%s%s", LogTags_str[al->cache.code],
@@ -860,6 +886,31 @@
                 }
             break;
 
+        case LFT_ERR_PAGE_UD:
+        case LFT_ERR_PAGE_LE:
+        case LFT_ERR_PAGE_UE:
+        case LFT_ERR_PAGE_LF:
+        case LFT_ERR_PAGE_UF:
+        case LFT_ERR_PAGE_LG:
+        case LFT_ERR_PAGE_LL:
+        case LFT_ERR_PAGE_UL:
+        case LFT_ERR_PAGE_LM:
+        case LFT_ERR_PAGE_LO:
+        case LFT_ERR_PAGE_UP:
+        case LFT_ERR_PAGE_UR:
+        case LFT_ERR_PAGE_LS:
+        case LFT_ERR_PAGE_US:
+        case LFT_ERR_PAGE_UW:
+        case LFT_ERR_PAGE_LX:
+        case LFT_ERR_PAGE_LZ:
+        case LFT_ERR_PAGE_UZ:
+            // NP: these code types are used only by the ErrorState page 
generator
+            // for backward compatibility use the old code to generate the 
display output
+            if (al->errorDetails) {
+                out = al->errorDetails->Convert(fmt->type);
+            }
+            break;
+
         case LFT_SQUID_HIERARCHY:
             if (al->hier.ping.timedout)
                 mb.append("TIMEOUT_", 8);

=== modified file 'src/format/Token.cc'
--- src/format/Token.cc 2013-02-11 23:11:12 +0000
+++ src/format/Token.cc 2013-04-07 02:39:46 +0000
@@ -1,4 +1,5 @@
 #include "squid.h"
+#include "base/StringArea.h"
 #include "format/Config.h"
 #include "format/Token.h"
 #include "format/TokenTableEntry.h"
@@ -31,6 +32,46 @@
 
     {"%", LFT_PERCENT},
 
+    // 1-byte Tokens used by the Error page templates
+
+    // these ones map easily ..
+    {"a", LFT_USER_LOGIN},
+    {"b", LFT_SQUID_KID_PORT},
+    {"B", LFT_SERVER_REQ_URI},
+    {"c", LFT_SQUID_ERROR},
+    /*{"d", LFT_TIME_TO_HANDLE_REQUEST},*/
+    {"h", LFT_SQUID_KID_HOSTNAME},
+    {"H", LFT_SERVER_FQDN_OR_PEER_NAME},
+    {"i", LFT_CLIENT_IP_ADDRESS},
+    {"I", LFT_SERVER_IP_ADDRESS},
+    {"M", LFT_REQUEST_METHOD},
+    {"p", LFT_SERVER_PORT},
+    {"t", LFT_TIME_LOCALTIME},
+    {"T", LFT_TIME_GMT_RFC1123},
+    {"u", LFT_CLIENT_REQ_URI},
+    {"U", LFT_REQUEST_URI},
+    {"w", LFT_SQUID_KID_ADMINEMAIL},
+
+    // NP: this bunch are specific to the ErrorState object generator
+    {"D", LFT_ERR_PAGE_UD},
+    {"e", LFT_ERR_PAGE_LE},
+    {"E", LFT_ERR_PAGE_UE},
+    {"f", LFT_ERR_PAGE_LF},
+    {"F", LFT_ERR_PAGE_UF},
+    {"g", LFT_ERR_PAGE_LG},
+    {"l", LFT_ERR_PAGE_LL},
+    {"L", LFT_ERR_PAGE_UL},
+    {"m", LFT_ERR_PAGE_LM},
+    {"o", LFT_ERR_PAGE_LO},
+    {"P", LFT_ERR_PAGE_UP},
+    {"R", LFT_ERR_PAGE_UR},
+    {"s", LFT_ERR_PAGE_LS},
+    {"S", LFT_ERR_PAGE_US},
+    {"W", LFT_ERR_PAGE_UW},
+    {"x", LFT_ERR_PAGE_LX},
+    {"z", LFT_ERR_PAGE_LZ},
+    {"Z", LFT_ERR_PAGE_UZ},
+
     {NULL, LFT_NONE}           /* this must be last */
 };
 
@@ -373,7 +414,12 @@
     }
 
     if (type == LFT_NONE) {
-        fatalf("Can't parse configuration token: '%s'\n", def);
+        debugs(46, 3, "Can't parse configuration token: '" << 
StringArea(def,min(strlen(def), static_cast<size_t>(10))) << "'");
+        // NP: unknown token found. Assume it should have been %% instead of %.
+        if (def[0] == '%') {
+            type = LFT_PERCENT;
+            ++cur;
+        }
     }
 
     if (*cur == ' ') {

Reply via email to