=== 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 == ' ') {


