Hi all,

I am attaching a new patch which try to handle the reported problems.

Just adding a new method which do special parsing for tokens like the
regex has many effects, for example the ConfigParser::TokenUndo
mechanism can not work.

This patch:
  1) By default disables quoted tokens
("configuration_includes_quoted_values off")

  2) If configuration_includes_quoted_values is off the quoted tokens
parsed using the ConfigParser::NextToken include the quotes, to keep
compatibility with older releases.

 3) For the cases where quoted strings are required (wordlists, Notes
parsing, Headers with acl), the new ConfigParser::NextQuotedToken method
added.
   The old wordlists parser allowed escaping any character, this patch
will return an error if you try to escape alphanumeric characters. The
\r \n and \t have the C semantics.

 4) Add the ConfigParser::RegexPattern() to get the next regex token

 5) Add the ConfigParser::RegexStrtokFile() to get the next regex token
which is compatible with the old strtokFile

 6) Removes the ConfigParser::TokenUndo method. The new method
ConfigParser::NextTokenPreview() which can be used to preview the next
token is added. This method if the next token is invalid (eg unquoted
with special characters) return as token the "SQUID_ERROR_TOKEN" (we do
not want to call self_destruct while previewing next element).

 7) In this patch I kept the ConfigParser::TokenPutBack method which is
used in only one place (acl regex). However this method should removed
in the future, with the ConfigParser::Undo_ member and the
ConfigParser::Undo() method


Notes
======
1) The current trunk parser read a line, and the tokens stored in this
line and the line modified while parsed. This patch consider the line we
are parsing as const and stores parsed tokens to
ConfigParser::CfgLineTokens_ std::queue:
  - we may need to parse again the line (NextTokenPreview/NextToken) so
we do not want to modify it
  - The current line tokens must stored somewhere to support the following:
   char *name = ConfigParser::NextToken();
   char *value = ConfigParser::NextToken();
The ConfigParser::CfgLineTokens_ emptied every time new config line is read.

2) A set of new flags defined under ConfigParser class to define the
type of parsing: ParseRegex_ (next token is regex)  ParseQuotedOrToEOL_
(next token is quoted or to-EOL), PreviewMode_ (just do preview do not
pop token)
This method is not the best, but it is not so bad....

3) The goal of new parser was to have a small and simple parser, but now
looks very complex. But it very is difficult to keep compatibility with
a simple parser.
Probably we will need to re-implement it after the old configuration
file style support removed from squid.


On 07/31/2013 07:59 PM, Alex Rousskov wrote:
> Christos, Amos,
> 
>     Thank you for working on this! I hope we can avoid repeating the
> same set of mistakes thrice by carefully considering the big picture and
> upgrade path. Here is my understanding of how we want things to work,
> based on the review of the reported bugs and this thread so far:
> 
> 
> 1. configuration_includes_quoted_values defaults to off. The setting
> scope extends from the place where the directive is used to either the
> end of configuration or to the next use, whichever comes first.

OK this patch make  configuration_includes_quoted_values defaults to off
> 
> 
> 2. When configuration_includes_quoted_values is on, new "strict syntax"
> rules are enforced:
> 
> 2a. "quoted values" and function()s are supported. %Macros are supported
> inside quoted values and only inside them. Unknown functions and macros
> terminate Squid. Code uses NextToken() to get tokens by default. A small
> subset of hand-picked directives may use NextQuotedOrToEol() or other
> special methods instead of NextToken() to accommodate more legacy cases.
> Logformat is one such example.

I think this patch cover this requirement.

> 
> 2b. A % sign can be \-escaped to block macro expansion in quoted strings
> where needed. A few "standard" escape sequences are supported such as
> \n. Unknown escape sequences terminate Squid.

Any not alphanumeric character can be escaped in this patch.
Also the \n\t\r are supported.
We can add/remove more if required.

> 
> 2c. By default, token delimiter is whitespace. Bare (i.e., unquoted)
> tokens containing any character other than alphanumeric, underscore,
> period, '(', ')', '=', and '-' terminate Squid. For example, foo{bar},
> foo@bar, and foo"bar" terminate Squid in strict syntax mode.

OK.
In this patch allow the following ".,()-=_%/:"
We can easily add remove characters from this list.
If we remove from this list the ':' then we should require quotes to
define http, icap or ecap urls.
The % used to define percent values and the '/' to define filenames.


> 
> 3. When configuration_includes_quoted_values is off, old "legacy syntax"
> is supported, to the extent possible:
> 
> 3a. "quoted values", functions(), and %macros are not recognized or
> treated specially compared to Squid v3.3. Code uses NextToken() to get
> tokens by default. A small subset of hand-picked directives may use
> NextQuotedOrToEol() instead of NextToken() but that method does _not_
> treat quoted strings specially in legacy mode. Logformat is one such
> example.

OK on this.
I just add the NextQuotedToken to support quoted tokens in "legacy
syntax" mode.
We need it in wordlist tokens, Notes and Header with acls....

> 
> 3b. I am not sure about "file.cfg" include syntax in legacy mode. I
> think it would be OK _not_ to support it (because fixing configurations
> to use new parameters() syntax should not be very difficult in most
> cases), but I may be wrong. If it is easy to continue to support
> "file.cfg" include style in NextToken() working in legacy mode, then we
> should do it.

This patch supports "file.cfg" in legacy mode.

> 
> 
> 4. Exceptions: A small set of directives that already supported quoted
> strings correctly before the introduction of
> configuration_includes_quoted_values must continue to support them.
> These directives should call NextQuotedOrLegacy() method. The method
> temporary forces configuration_includes_quoted_values to ON if and only
> if the next token starts with a quote.

I used the NextQuotedToken for this ...


> 
> This may create a few upgrade problems when, for example, somebody is
> using unsupported %macros inside quoted strings with these options, but
> the support for quoted strings in those headers was added relatively
> recently so there should not be many such cases, and it is not practical
> to treat these options as a complex third class. They have to obey all
> strict syntax rules when they use quoted strings (and also when
> configuration_includes_quoted_values is on, of course).
> 
> 
> 5. Future changes:
> 
> 5a. We revisit handling of 'single quoted strings' later, after the
> above is done. They are useful to disable macros and standard escape
> sequences in strings, but they are not a "must have" for now, and we
> need to decide how to treat \-escapes inside them. We will probably
> allow escaping of two characters only: \ and '.
> 
> 5b. We revisit handling of REs, after the above is done. We will
> probably add a proper dedicated /quoting mechanism/ for them. For now,
> most REs require configuration_includes_quoted_values set to off.
> 
> 5c. I recommend combining 5a and 5b implementation to support a simple
> generic quoting mechanism with an admin-selectable quoting character.
> That is almost a must for REs but also help with general quoting of
> complex expressions. See "Quote and Quote-like Operators" in Perl
> (perlop man page) for ideas (but we only need a few lines from that
> table). Note how 2c facilitates this future change by immediately
> prohibiting unquoted tokens that may become quoted strings later. For
> example q{foobar} is prohibited if configuration_includes_quoted_values
> is on.
> 
> 
> Anything I missed or misrepresented?
> 
> 
> Thank you,
> 
> Alex.
> 

=== modified file 'src/ConfigParser.cc'
--- src/ConfigParser.cc	2013-07-22 01:26:09 +0000
+++ src/ConfigParser.cc	2013-08-07 07:12:25 +0000
@@ -21,377 +21,495 @@
  *  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.
  *
  *
  * Copyright (c) 2003, Robert Collins <robe...@squid-cache.org>
  */
 
 #include "squid.h"
 #include "cache_cf.h"
 #include "ConfigParser.h"
 #include "Debug.h"
 #include "fatal.h"
 #include "globals.h"
 
-int ConfigParser::RecognizeQuotedValues = true;
+bool ConfigParser::RecognizeQuotedValues = true;
+bool ConfigParser::StrictMode = true;
 std::stack<ConfigParser::CfgFile *> ConfigParser::CfgFiles;
 ConfigParser::TokenType ConfigParser::LastTokenType = ConfigParser::SimpleToken;
-char *ConfigParser::LastToken = NULL;
-char *ConfigParser::CfgLine = NULL;
-char *ConfigParser::CfgPos = NULL;
+const char *ConfigParser::CfgLine = NULL;
+const char *ConfigParser::CfgPos = NULL;
+std::queue<char *> ConfigParser::CfgLineTokens_;
 std::queue<std::string> ConfigParser::Undo_;
 bool ConfigParser::AllowMacros_ = false;
+bool ConfigParser::ParseRegex_ = false;
+bool ConfigParser::ParseQuotedOrToEOL_ = false;
+bool ConfigParser::PreviewMode_ = false;
+
+static const char *SQUID_ERROR_TOKEN = "SQUID_ERROR_TOKEN";
 
 void
 ConfigParser::destruct()
 {
     shutting_down = 1;
     if (!CfgFiles.empty()) {
         std::ostringstream message;
         CfgFile *f = CfgFiles.top();
-        message << "Bungled " << f->filePath << " line " << f->lineNo <<
+        message << "Bungled (#1)" << f->filePath << " line " << f->lineNo <<
         ": " << f->currentLine << std::endl;
         CfgFiles.pop();
         delete f;
         while (!CfgFiles.empty()) {
             f = CfgFiles.top();
             message << " included from " << f->filePath << " line " <<
             f->lineNo << ": " << f->currentLine << std::endl;
             CfgFiles.pop();
             delete f;
         }
         message << " included from " <<  cfg_filename << " line " <<
         config_lineno << ": " << config_input_line << std::endl;
         std::string msg = message.str();
         fatalf("%s", msg.c_str());
     } else
-        fatalf("Bungled %s line %d: %s",
+        fatalf("Bungled (#2) %s line %d: %s",
                cfg_filename, config_lineno, config_input_line);
 }
 
 void
-ConfigParser::TokenUndo()
-{
-    assert(LastToken);
-    Undo_.push(LastToken);
-}
-
-void
 ConfigParser::TokenPutBack(const char *tok)
 {
     assert(tok);
     Undo_.push(tok);
 }
 
 char *
 ConfigParser::Undo()
 {
     LOCAL_ARRAY(char, undoToken, CONFIG_LINE_LIMIT);
     if (!Undo_.empty()) {
         strncpy(undoToken, Undo_.front().c_str(), sizeof(undoToken));
         undoToken[sizeof(undoToken) - 1] = '\0';
-        Undo_.pop();
+        if (!PreviewMode_)
+            Undo_.pop();
         return undoToken;
     }
     return NULL;
 }
 
 char *
 ConfigParser::strtokFile()
 {
     if (RecognizeQuotedValues)
         return ConfigParser::NextToken();
 
     static int fromFile = 0;
     static FILE *wordFile = NULL;
 
     char *t;
     LOCAL_ARRAY(char, buf, CONFIG_LINE_LIMIT);
 
-    if ((LastToken = ConfigParser::Undo()))
-        return LastToken;
+    if ((t = ConfigParser::Undo()))
+        return t;
 
     do {
 
         if (!fromFile) {
             ConfigParser::TokenType tokenType;
-            t = ConfigParser::NextElement(tokenType, true);
+            t = ConfigParser::NextElement(tokenType);
             if (!t) {
                 return NULL;
-            } else if (tokenType == ConfigParser::QuotedToken) {
-                /* quote found, start reading from file */
+            } else if (*t == '\"' || *t == '\'') {
+                /* quote found, start reading from file */                
                 debugs(3, 8,"Quoted token found : " << t);
+                char *fn = ++t;
 
-                if ((wordFile = fopen(t, "r")) == NULL) {
+                while (*t && *t != '\"' && *t != '\'')
+                    ++t;
+
+                *t = '\0';
+
+                if ((wordFile = fopen(fn, "r")) == NULL) {
                     debugs(3, DBG_CRITICAL, "Can not open file " << t << " for reading");
                     return NULL;
                 }
 
 #if _SQUID_WINDOWS_
                 setmode(fileno(wordFile), O_TEXT);
 #endif
 
                 fromFile = 1;
             } else {
-                return LastToken = t;
+                return t;
             }
         }
 
         /* fromFile */
         if (fgets(buf, CONFIG_LINE_LIMIT, wordFile) == NULL) {
             /* stop reading from file */
             fclose(wordFile);
             wordFile = NULL;
             fromFile = 0;
             return NULL;
         } else {
             char *t2, *t3;
             t = buf;
             /* skip leading and trailing white space */
             t += strspn(buf, w_space);
             t2 = t + strcspn(t, w_space);
             t3 = t2 + strspn(t2, w_space);
 
             while (*t3 && *t3 != '#') {
                 t2 = t3 + strcspn(t3, w_space);
                 t3 = t2 + strspn(t2, w_space);
             }
 
             *t2 = '\0';
         }
 
         /* skip comments */
         /* skip blank lines */
     } while ( *t == '#' || !*t );
 
-    return LastToken = t;
+    return t;
 }
 
 char *
-ConfigParser::UnQuote(char *token, char **end)
+ConfigParser::UnQuote(const char *token, const char **next)
 {
+    const char *errorStr = NULL;
+    const char *errorPos = NULL;
     char quoteChar = *token;
     assert(quoteChar == '"' || quoteChar == '\'');
-    char  *s = token + 1;
+    LOCAL_ARRAY(char, UnQuoted, CONFIG_LINE_LIMIT);
+    const char  *s = token + 1;
+    char *d = UnQuoted;
     /* scan until the end of the quoted string, unescaping " and \  */
-    while (*s && *s != quoteChar) {
-        if (*s == '\\' && isalnum(*( s + 1))) {
-            debugs(3, DBG_CRITICAL, "Unsupported escape sequence: " << s);
-            self_destruct();
+    while (*s && *s != quoteChar && !errorStr && (d - UnQuoted) < sizeof(UnQuoted)) {
+        if (*s == '\\') {
+            s++;
+            switch (*s) {
+            case 'r':
+                *d = '\r';
+                break;
+            case 'n':
+                *d = '\n';
+                break;
+            case 't':
+                *d = '\t';
+                break;
+            default:
+                if (isalnum(*s)) {
+                    errorStr = "Unsupported escape sequence";
+                    errorPos = s; 
+                }
+                *d = *s;
+                break;
+            }
         } else if (*s == '$' && quoteChar == '"') {
-            debugs(3, DBG_CRITICAL, "Unsupported cfg macro: " << s);
-            self_destruct();
+            errorStr = "Unsupported cfg macro";
+            errorPos = s;
         } else if (*s == '%' && quoteChar == '"' && (!AllowMacros_ )) {
-            debugs(3, DBG_CRITICAL, "Macros are not supported here: " << s);
-            self_destruct();
-        } else if (*s == '\\') {
-            const char * next = s+1; // may point to 0
-            memmove(s, next, strlen(next) + 1);
-        }
+            errorStr = "Macros are not supported here";
+            errorPos = s;
+        } else
+            *d = *s;
         ++s;
+        ++d;
+    }
+
+    if (*s != quoteChar && !errorStr) {
+        errorStr = "missing quote char at the end of quoted string";
+        errorPos = s - 1;
     }
+    // The end of token
+    *d = '\0';
 
-    if (*s != quoteChar) {
-        debugs(3, DBG_CRITICAL, "missing '" << quoteChar << "' at the end of quoted string: " << (s-1));
-        self_destruct();
+    // We are expecting a separator after quoted string, space or one of "()#"
+    if (*(s + 1) != '\0' && !strchr(w_space "()#", *(s + 1)) && !errorStr) {
+        errorStr = "Expecting space after the end of quoted token";
+        errorPos = token;
+    }
+
+    if (errorStr) {
+        if (PreviewMode_)
+            strncpy(UnQuoted, SQUID_ERROR_TOKEN, sizeof(UnQuoted));
+        else {
+            debugs(3, DBG_CRITICAL, errorStr << ": " << errorPos);
+            self_destruct();
+        }
     }
-    *end = s;
-    return (token+1);
+
+    if (next)
+        *next = s + 1;
+    return UnQuoted;
 }
 
 void
 ConfigParser::SetCfgLine(char *line)
 {
     CfgLine = line;
     CfgPos = line;
+    while (!CfgLineTokens_.empty()) {
+        char *token = CfgLineTokens_.front();
+        CfgLineTokens_.pop();
+        free(token);
+    }
 }
 
 char *
-ConfigParser::TokenParse(char * &nextToken, ConfigParser::TokenType &type, bool legacy)
+ConfigParser::TokenParse(const char * &nextToken, ConfigParser::TokenType &type)
 {
     if (!nextToken || *nextToken == '\0')
         return NULL;
     type = ConfigParser::SimpleToken;
     nextToken += strspn(nextToken, w_space);
-    if (*nextToken == '"' || *nextToken == '\'') {
+
+    if (*nextToken == '#')
+        return NULL;
+
+    if (!ConfigParser::ParseRegex_ && ConfigParser::RecognizeQuotedValues && (*nextToken == '"' || *nextToken == '\'')) {
         type = ConfigParser::QuotedToken;
-        char *token = UnQuote(nextToken, &nextToken);
-        *nextToken = '\0';
-        ++nextToken;
+        char *token = xstrdup(UnQuote(nextToken, &nextToken));
+        CfgLineTokens_.push(token);
         return token;
     }
 
-    char *token = nextToken;
-    if (char *t = strchr(nextToken, '#'))
-        *t = '\0';
+    const char *tokenStart = nextToken;
     const char *sep;
-    if (legacy)
+    if (ConfigParser::ParseQuotedOrToEOL_)
+        sep = "\n";
+    else if (!ConfigParser::RecognizeQuotedValues || ConfigParser::ParseRegex_ || *nextToken == '(')
         sep = w_space;
     else
         sep = w_space "(";
     nextToken += strcspn(nextToken, sep);
 
-    if (!legacy && *nextToken == '(')
-        type = ConfigParser::FunctionNameToken;
-    else
-        type = ConfigParser::SimpleToken;
+    if (ConfigParser::RecognizeQuotedValues && *nextToken == '(') {
+        if (strncmp(tokenStart, "parameters", nextToken - tokenStart) == 0)
+            type = ConfigParser::FunctionParameters;
+        else {
+            if (PreviewMode_) {
+                char *err = xstrdup(SQUID_ERROR_TOKEN);
+                CfgLineTokens_.push(err);
+                return err;
+            } else {
+                debugs(3, DBG_CRITICAL, "Unknown cfg function: " << tokenStart);
+                self_destruct();
+            }
+        }
+    } else
+        type = (ConfigParser::ParseRegex_ ? ConfigParser::RegexPatternToken : ConfigParser::SimpleToken);
 
-    if (*nextToken != '\0') {
-        *nextToken = '\0';
-        ++nextToken;
+    char *token = NULL;
+    if (nextToken - tokenStart) {
+        if (ConfigParser::StrictMode && type == ConfigParser::SimpleToken) {
+            for (const char *s = tokenStart; s != nextToken; ++s) {
+                if (!isalnum(*s) && !strchr(".,()-=_%/:", *s)) {
+                    if (PreviewMode_) {
+                        char *err = xstrdup(SQUID_ERROR_TOKEN);
+                        CfgLineTokens_.push(err);
+                        return err;
+                    } else {
+                        debugs(3, DBG_CRITICAL, "Not alphanumeric character '"<< *s << "' in unquoted token " << tokenStart);
+                        self_destruct();
+                    }
+                }
+            }
+        }
+        token = xstrndup(tokenStart, nextToken - tokenStart + 1);
+        CfgLineTokens_.push(token);
     }
 
-    if (*token == '\0')
-        return NULL;
+    if (*nextToken != '\0' && *nextToken != '#') {
+        ++nextToken;
+    }
 
     return token;
 }
 
 char *
-ConfigParser::NextElement(ConfigParser::TokenType &type, bool legacy)
+ConfigParser::NextElement(ConfigParser::TokenType &type)
 {
-    char *token = TokenParse(CfgPos, type, legacy);
+    const char *pos = CfgPos;
+    char *token = TokenParse(pos, type);
+    if (!PreviewMode_ || type == FunctionParameters)
+        CfgPos = pos;
+    // else next call will read the same token
     return token;
 }
 
 char *
 ConfigParser::NextToken()
 {
-    if ((LastToken = ConfigParser::Undo()))
-        return LastToken;
-
     char *token = NULL;
+    if ((token = ConfigParser::Undo())) {
+        debugs(3, 6, "TOKEN (undone): " << token);
+        return token;
+    }
+
     do {
         while (token == NULL && !CfgFiles.empty()) {
             ConfigParser::CfgFile *wordfile = CfgFiles.top();
             token = wordfile->parse(LastTokenType);
             if (!token) {
                 assert(!wordfile->isOpen());
                 CfgFiles.pop();
+                debugs(3, 4, "CfgFiles.pop " << wordfile->filePath);
                 delete wordfile;
             }
         }
 
         if (!token)
             token = NextElement(LastTokenType);
 
-        if (token &&  LastTokenType == ConfigParser::FunctionNameToken && strcmp("parameters", token) == 0) {
+        if (token &&  LastTokenType == ConfigParser::FunctionParameters) {
+            //Disable temporary preview mode, we need to parse function parameters
+            bool savePreview = ConfigParser::PreviewMode_;
+            ConfigParser::PreviewMode_ = false;
+
             char *path = NextToken();
             if (LastTokenType != ConfigParser::QuotedToken) {
                 debugs(3, DBG_CRITICAL, "Quoted filename missing: " << token);
                 self_destruct();
                 return NULL;
             }
 
             // The next token in current cfg file line must be a ")"
             char *end = NextToken();
+            ConfigParser::PreviewMode_ = savePreview;
             if (LastTokenType != ConfigParser::SimpleToken || strcmp(end, ")") != 0) {
                 debugs(3, DBG_CRITICAL, "missing ')' after " << token << "(\"" << path << "\"");
                 self_destruct();
                 return NULL;
             }
 
             if (CfgFiles.size() > 16) {
                 debugs(3, DBG_CRITICAL, "WARNING: can't open %s for reading parameters: includes are nested too deeply (>16)!\n" << path);
                 self_destruct();
                 return NULL;
             }
 
             ConfigParser::CfgFile *wordfile = new ConfigParser::CfgFile();
             if (!path || !wordfile->startParse(path)) {
                 debugs(3, DBG_CRITICAL, "Error opening config file: " << token);
                 delete wordfile;
                 self_destruct();
                 return NULL;
             }
             CfgFiles.push(wordfile);
             token = NULL;
-        } else if (token &&  LastTokenType == ConfigParser::FunctionNameToken) {
-            debugs(3, DBG_CRITICAL, "Unknown cfg function: " << token);
-            self_destruct();
-            return NULL;
         }
     } while (token == NULL && !CfgFiles.empty());
 
-    return (LastToken = token);
+    return token;
 }
 
 char *
-ConfigParser::NextQuotedOrToEol()
+ConfigParser::NextTokenPreview()
 {
-    char *token;
+    PreviewMode_ = true;
+    char *token = NextToken();
+    PreviewMode_ = false;
+    return token;
+}
 
-    if ((token = CfgPos) == NULL) {
-        debugs(3, DBG_CRITICAL, "token is missing");
-        self_destruct();
-        return NULL;
+char *
+ConfigParser::NextQuotedOrToEol()
+{
+    ParseQuotedOrToEOL_ = true;
+    char *token = NextToken();
+    ParseQuotedOrToEOL_ = false;
+
+    // Assume end of current config line
+    // Close all open configuration files for this config line
+    while (!CfgFiles.empty()) {
+        ConfigParser::CfgFile *wordfile = CfgFiles.top();
+        CfgFiles.pop();
+        delete wordfile;
     }
-    token += strspn(token, w_space);
 
-    if (*token == '\"' || *token == '\'') {
-        //TODO: eat the spaces at the end and check if it is untill the end of file.
-        char *end;
-        token = UnQuote(token, &end);
-        *end = '\0';
-        CfgPos = end + 1;
-        LastTokenType = ConfigParser::QuotedToken;
-    } else
-        LastTokenType = ConfigParser::SimpleToken;
+    return token;
+}
+
+char *
+ConfigParser::RegexStrtokFile()
+{
+    ParseRegex_ = true;
+    char * token = strtokFile();
+    ParseRegex_ = false;
+    return token;
+}
 
-    CfgPos = NULL;
-    return (LastToken = token);
+char *
+ConfigParser::RegexPattern()
+{
+    ParseRegex_ = true;
+    char * token = NextToken();
+    ParseRegex_ = false;
+    return token;
+}
+
+char *
+ConfigParser::NextQuotedToken()
+{
+    bool saveRecognizeQuotedValues = ConfigParser::RecognizeQuotedValues;
+    ConfigParser::RecognizeQuotedValues = true;
+    char *token = NextToken();
+    ConfigParser::RecognizeQuotedValues = saveRecognizeQuotedValues;
+    return token;
 }
 
 const char *
 ConfigParser::QuoteString(const String &var)
 {
     static String quotedStr;
     const char *s = var.termedBuf();
     bool  needQuote = false;
 
     for (const char *l = s; !needQuote &&  *l != '\0'; ++l  )
         needQuote = !isalnum(*l);
 
     if (!needQuote)
         return s;
 
     quotedStr.clean();
     quotedStr.append('"');
     for (; *s != '\0'; ++s) {
         if (*s == '"' || *s == '\\')
             quotedStr.append('\\');
         quotedStr.append(*s);
     }
     quotedStr.append('"');
     return quotedStr.termedBuf();
 }
 
 bool
 ConfigParser::CfgFile::startParse(char *path)
 {
     assert(wordFile == NULL);
+    debugs(3, 3, "Parsing from " << path);
     if ((wordFile = fopen(path, "r")) == NULL) {
         debugs(3, DBG_CRITICAL, "file :" << path << " not found");
         return false;
     }
 
 #if _SQUID_WINDOWS_
     setmode(fileno(wordFile), O_TEXT);
 #endif
 
     filePath = path;
     return getFileLine();
 }
 
 bool
 ConfigParser::CfgFile::getFileLine()
 {
     // Else get the next line
     if (fgets(parseBuffer, CONFIG_LINE_LIMIT, wordFile) == NULL) {
         /* stop reading from file */
         fclose(wordFile);
@@ -408,28 +526,33 @@
 char *
 ConfigParser::CfgFile::parse(ConfigParser::TokenType &type)
 {
     if (!wordFile)
         return NULL;
 
     if (!*parseBuffer)
         return NULL;
 
     char *token;
     while (!(token = nextElement(type))) {
         if (!getFileLine())
             return NULL;
     }
     return token;
 }
 
 char *
 ConfigParser::CfgFile::nextElement(ConfigParser::TokenType &type)
 {
-    return TokenParse(parsePos, type);
+    const char *pos = parsePos;
+    char *token = TokenParse(pos, type);
+    if (!PreviewMode_ || type == FunctionParameters)
+        parsePos = pos;
+    // else next call will read the same token;
+    return token;
 }
 
 ConfigParser::CfgFile::~CfgFile()
 {
     if (wordFile)
         fclose(wordFile);
 }

=== modified file 'src/ConfigParser.h'
--- src/ConfigParser.h	2013-07-21 19:24:35 +0000
+++ src/ConfigParser.h	2013-08-07 06:39:13 +0000
@@ -53,158 +53,182 @@
 #define CONFIG_LINE_LIMIT	2048
 
 /**
  * A configuration file Parser. Instances of this class track
  * parsing state and perform tokenisation. Syntax is currently
  * taken care of outside this class.
  *
  * One reason for this class is to allow testing of configuration
  * using modules without linking cache_cf.o in - because that drags
  * in all of squid by reference. Instead the tokeniser only is
  * brought in.
  */
 class ConfigParser
 {
 
 public:
     /**
      * Parsed tokens type: simple tokens, quoted tokens or function
      * like parameters.
      */
-    enum TokenType {SimpleToken, QuotedToken, FunctionNameToken};
+    enum TokenType {SimpleToken, QuotedToken, RegexPatternToken, FunctionParameters};
 
     void destruct();
     static void ParseUShort(unsigned short *var);
     static void ParseBool(bool *var);
     static const char *QuoteString(const String &var);
     static void ParseWordList(wordlist **list);
 
     /**
      * Backward compatibility wrapper for the ConfigParser::NextToken method.
      * If the configuration_includes_quoted_values configuration parameter is
      * set to 'off' this interprets the quoted tokens as filenames.
      */
     static char * strtokFile();
 
     /**
      * Returns the body of the next element. The element is either a token or
      * a quoted string with optional escape sequences and/or macros. The body
      * of a quoted string element does not include quotes or escape sequences.
      * Future code will want to see Elements and not just their bodies.
      */
     static char *NextToken();
 
+    /**
+     * Backward compatibility wrapper for ConfigParser::RegexPattern method.
+     * If the configuration_includes_quoted_values configuration parameter is
+     * set to 'off' this interprets the quoted tokens as filenames.
+     */
+    static char *RegexStrtokFile();
+
+    /**
+     * Parse the next token as a regex patern. The regex patterns are non quoted
+     * tokens.
+     */
+    static char *RegexPattern();
+
+    /**
+     * Parse the next token with support for quoted values enabled even if 
+     * the configuration_includes_quoted_values is set to off
+     */
+    static char *NextQuotedToken();
+
     /// \return true if the last parsed token was quoted
     static bool LastTokenWasQuoted() {return (LastTokenType == ConfigParser::QuotedToken);}
 
     /**
      * \return the next quoted string or the raw string data until the end of line.
      * This method allows %macros in unquoted strings to keep compatibility
      * for the logformat option.
      */
     static char *NextQuotedOrToEol();
 
     /**
-     * Undo last NextToken call. The next call to NextToken() method will return
-     * again the last parsed element.
-     * Can not be called repeatedly to undo multiple NextToken calls. In this case
-     * the behaviour is undefined.
+     * Preview the next token. The next NextToken() and strtokFile() call
+     * will return the same token.
+     * On parse error (eg invalid characters in token) will return an
+     * error message as token.
      */
-    static void TokenUndo();
+    static char *NextTokenPreview();
 
     /**
      * The next NextToken call will return the token as next element
      * It can be used repeatedly to add more than one tokens in a FIFO list.
      */
     static void TokenPutBack(const char *token);
 
     /// Set the configuration file line to parse.
     static void SetCfgLine(char *line);
 
     /// Allow %macros inside quoted strings
     static void EnableMacros() {AllowMacros_ = true;}
 
     /// Do not allow %macros inside quoted strings
     static void DisableMacros() {AllowMacros_ = false;}
 
     /// configuration_includes_quoted_values in squid.conf
-    static int RecognizeQuotedValues;
+    static bool RecognizeQuotedValues;
+
+    /// Strict syntax mode. Does not allow not alphanumeric characters in unquoted tokens
+    static bool StrictMode;
 
 protected:
     /**
      * Class used to store required information for the current
      * configuration file.
      */
     class CfgFile
     {
     public:
         CfgFile(): wordFile(NULL), parsePos(NULL), lineNo(0) { parseBuffer[0] = '\0';}
         ~CfgFile();
         /// True if the configuration file is open
         bool isOpen() {return wordFile != NULL;}
 
         /**
          * Open the file given by 'path' and initializes the CfgFile object
          * to start parsing
          */
         bool startParse(char *path);
 
         /**
          * Do the next parsing step:
          * reads the next line from file if required.
          * \return the body of next element or a NULL pointer if there are no more token elements in the file.
          * \param type will be filled with the ConfigParse::TokenType for any element found, or left unchanged if NULL is returned.
          */
         char *parse(TokenType &type);
 
     private:
         bool getFileLine();   ///< Read the next line from the file
         /**
          * Return the body of the next element. If the wasQuoted is given
          * set to true if the element was quoted.
          */
         char *nextElement(TokenType &type);
         FILE *wordFile; ///< Pointer to the file.
         char parseBuffer[CONFIG_LINE_LIMIT]; ///< Temporary buffer to store data to parse
-        char *parsePos; ///< The next element position in parseBuffer string
+        const char *parsePos; ///< The next element position in parseBuffer string
     public:
         std::string filePath; ///< The file path
         std::string currentLine; ///< The current line to parse
         int lineNo; ///< Current line number
     };
 
     /**
-     * Return the last TokenUndo() or TokenPutBack() queued element, or NULL
+     * Return the last TokenPutBack() queued element, or NULL
      * if none exist
      */
     static char *Undo();
 
     /**
      * Unquotes the token, which must be quoted.
-     * \param end if it is not NULL, it is set to the end of token.
+     * \param next if it is not NULL, it is set after the end of token.
      */
-    static char *UnQuote(char *token, char **end = NULL);
+    static char *UnQuote(const char *token, const char **next = NULL);
 
     /**
      * Does the real tokens parsing job: Ignore comments, unquote an
      * element if required.
      * \return the next token, or NULL if there are no available tokens in the nextToken string.
      * \param nextToken updated to point to the pos after parsed token.
      * \param type      The token type
-     * \param legacy    If it is true function-like parameters are not allowed
      */
-    static char *TokenParse(char * &nextToken, TokenType &type, bool legacy = false);
+    static char *TokenParse(const char * &nextToken, TokenType &type);
 
     /// Wrapper method for TokenParse.
-    static char *NextElement(TokenType &type, bool legacy = false);
+    static char *NextElement(TokenType &type);
     static std::stack<CfgFile *> CfgFiles; ///< The stack of open cfg files
     static TokenType LastTokenType; ///< The type of last parsed element
-    static char *LastToken; ///< Points to the last parsed token
-    static char *CfgLine; ///< The current line to parse
-    static char *CfgPos; ///< Pointer to the next element in cfgLine string
-    static std::queue<std::string> Undo_; ///< The list with TokenUndo() or TokenPutBack() queued elements
+    static const char *CfgLine; ///< The current line to parse
+    static const char *CfgPos; ///< Pointer to the next element in cfgLine string
+    static std::queue<char *> CfgLineTokens_; ///< Store the list of tokens for current configuration line
+    static std::queue<std::string> Undo_; ///< The list with TokenPutBack() queued elements
     static bool AllowMacros_;
+    static bool ParseRegex_; ///< The next tokens will be handled as regex patterns
+    static bool ParseQuotedOrToEOL_; ///< The next tokens will be handled as quoted or to_eol token
+    static bool PreviewMode_; ///< The next token will not poped from cfg files, will just previewd.
 };
 
 int parseConfigFile(const char *file_name);
 
 #endif /* SQUID_CONFIGPARSER_H */

=== modified file 'src/Notes.cc'
--- src/Notes.cc	2013-07-21 19:24:35 +0000
+++ src/Notes.cc	2013-08-05 20:05:41 +0000
@@ -76,41 +76,41 @@
 }
 
 Note::Pointer
 Notes::add(const String &noteKey)
 {
     typedef Notes::NotesList::iterator AMLI;
     for (AMLI i = notes.begin(); i != notes.end(); ++i) {
         if ((*i)->key == noteKey)
             return (*i);
     }
 
     Note::Pointer note = new Note(noteKey);
     notes.push_back(note);
     return note;
 }
 
 Note::Pointer
 Notes::parse(ConfigParser &parser)
 {
     String key = ConfigParser::NextToken();
-    String value = ConfigParser::NextToken();
+    String value = ConfigParser::NextQuotedToken();
     Note::Pointer note = add(key);
     Note::Value::Pointer noteValue = note->addValue(value);
 
     String label(key);
     label.append('=');
     label.append(value);
     aclParseAclList(parser, &noteValue->aclList, label.termedBuf());
 
     if (blacklisted) {
         for (int i = 0; blacklisted[i] != NULL; ++i) {
             if (note->key.caseCmp(blacklisted[i]) == 0) {
                 fatalf("%s:%d: meta key \"%s\" is a reserved %s name",
                        cfg_filename, config_lineno, note->key.termedBuf(),
                        descr ? descr : "");
             }
         }
     }
 
     return note;
 }

=== modified file 'src/acl/Acl.cc'
--- src/acl/Acl.cc	2013-07-21 19:24:35 +0000
+++ src/acl/Acl.cc	2013-08-06 19:24:10 +0000
@@ -38,62 +38,59 @@
 #include "dlink.h"
 #include "globals.h"
 #include "profiler/Profiler.h"
 #include "SquidConfig.h"
 
 const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
 
 const char *AclMatchedName = NULL;
 
 bool ACLFlags::supported(const ACLFlag f) const
 {
     if (f == ACL_F_REGEX_CASE)
         return true;
     return (supported_.find(f) != std::string::npos);
 }
 
 void
 ACLFlags::parseFlags()
 {
     char *nextToken;
-    while ((nextToken = ConfigParser::strtokFile()) != NULL && nextToken[0] == '-') {
-
+    while ((nextToken = ConfigParser::NextTokenPreview()) != NULL && nextToken[0] == '-') {
+        (void)ConfigParser::NextToken(); //Get token fron cfg line
         //if token is the "--" break flag
         if (strcmp(nextToken, "--") == 0)
             break;
 
         for (const char *flg = nextToken+1; *flg!='\0'; flg++ ) {
             if (supported(*flg)) {
                 makeSet(*flg);
             } else {
                 debugs(28, 0, HERE << "Flag '" << *flg << "' not supported");
                 self_destruct();
             }
         }
     }
 
     /*Regex code needs to parse -i file*/
     if ( isSet(ACL_F_REGEX_CASE))
         ConfigParser::TokenPutBack("-i");
-
-    if (nextToken != NULL && strcmp(nextToken, "--") != 0 )
-        ConfigParser::TokenUndo();
 }
 
 const char *
 ACLFlags::flagsStr() const
 {
     static char buf[64];
     if (flags_ == 0)
         return "";
 
     char *s = buf;
     *s++ = '-';
     for (ACLFlag f = 'A'; f <= 'z'; f++) {
         // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
         if (isSet(f) && f != ACL_F_REGEX_CASE)
             *s++ = f;
     }
     *s = '\0';
     return buf;
 }
 

=== modified file 'src/acl/RegexData.cc'
--- src/acl/RegexData.cc	2012-09-06 11:56:46 +0000
+++ src/acl/RegexData.cc	2013-08-05 09:51:23 +0000
@@ -305,41 +305,41 @@
             flags &= ~REG_ICASE;
         } else {
             newTail = compileRE( Tail, wl->key , flags );
             if (newTail == NULL)
                 debugs(28, DBG_CRITICAL, "ERROR: Skipping regular expression. Compile failed: '" << wl->key << "'");
             else
                 Tail = newTail;
         }
         wl = wl->next;
     }
 }
 
 static void
 aclParseRegexList(RegexList **curlist)
 {
     char *t;
     wordlist *wl = NULL;
 
     debugs(28, 2, HERE << "aclParseRegexList: new Regex line or file");
 
-    while ((t = ConfigParser::strtokFile()) != NULL) {
+    while ((t = ConfigParser::RegexStrtokFile()) != NULL) {
         const char *clean = removeUnnecessaryWildcards(t);
         if (strlen(clean) > BUFSIZ-1) {
             debugs(28, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
             debugs(28, DBG_CRITICAL, "ERROR: Skipping regular expression. Larger than " << BUFSIZ-1 << " characters: '" << clean << "'");
         } else {
             debugs(28, 3, "aclParseRegexList: buffering RE '" << clean << "'");
             wordlistAdd(&wl, clean);
         }
     }
 
     if (!compileOptimisedREs(curlist, wl)) {
         debugs(28, DBG_IMPORTANT, "WARNING: optimisation of regular expressions failed; using fallback method without optimisation");
         compileUnoptimisedREs(curlist, wl);
     }
 
     wordlistDestroy(&wl);
 }
 
 void
 ACLRegexData::parse()

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2013-07-30 00:44:04 +0000
+++ src/cache_cf.cc	2013-08-06 19:29:07 +0000
@@ -243,40 +243,44 @@
 static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign);
 static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
 static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt);
 static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
 static void parse_sslproxy_ssl_bump(acl_access **ssl_bump);
 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump);
 static void free_sslproxy_ssl_bump(acl_access **ssl_bump);
 #endif /* USE_SSL */
 
 static void parse_b_size_t(size_t * var);
 static void parse_b_int64_t(int64_t * var);
 
 static bool parseNamedIntList(const char *data, const String &name, Vector<int> &list);
 
 static void parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
 static void dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap);
 static void free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
 
 static int parseOneConfigFile(const char *file_name, unsigned int depth);
 
+static void parse_configuration_includes_quoted_values(bool *recognizeQuotedValues);
+static void dump_configuration_includes_quoted_values(StoreEntry *const entry, const char *const name, bool recognizeQuotedValues);
+static void free_configuration_includes_quoted_values(bool *recognizeQuotedValues);
+
 /*
  * LegacyParser is a parser for legacy code that uses the global
  * approach.  This is static so that it is only exposed to cache_cf.
  * Other modules needing access to a ConfigParser should have it
  * provided to them in their parserFOO methods.
  */
 static ConfigParser LegacyParser = ConfigParser();
 
 void
 self_destruct(void)
 {
     LegacyParser.destruct();
 }
 
 static void
 update_maxobjsize(void)
 {
     int64_t ms = -1;
 
     // determine the maximum size object that can be stored to disk
@@ -1777,41 +1781,41 @@
 }
 
 static void
 dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers)
 {
     if (manglers)
         manglers->dumpReplacement(entry, name);
 }
 
 static void
 parse_http_header_replace(HeaderManglers **pm)
 {
     char *t = NULL;
 
     if ((t = ConfigParser::NextToken()) == NULL) {
         debugs(3, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
         debugs(3, DBG_CRITICAL, "parse_http_header_replace: missing header name.");
         return;
     }
 
-    const char *value = t + strlen(t) + 1;
+    const char *value = ConfigParser::NextQuotedOrToEol();
 
     if (!*pm)
         *pm = new HeaderManglers;
     HeaderManglers *manglers = *pm;
     manglers->setReplacement(t, value);
 }
 
 #endif
 
 static void
 dump_cachedir(StoreEntry * entry, const char *name, SquidConfig::_cacheSwap swap)
 {
     SwapDir *s;
     int i;
     assert (entry);
 
     for (i = 0; i < swap.n_configured; ++i) {
         s = dynamic_cast<SwapDir *>(swap.swapDirs[i].getRaw());
         if (!s) continue;
         storeAppendPrintf(entry, "%s %s %s", name, s->type(), s->path);
@@ -2682,55 +2686,57 @@
         debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'enable' is deprecated. Please update to use value 'on'.");
         *var = 1;
     } else if (!strcmp(token, "warn")) {
         *var = -1;
     } else if (!strcmp(token, "off")) {
         *var = 0;
     } else if (!strcmp(token, "disable")) {
         debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'disable' is deprecated. Please update to use value 'off'.");
         *var = 0;
     } else {
         debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: Invalid option: Tristate options can only be 'on', 'off', or 'warn'.");
         self_destruct();
     }
 }
 
 #define free_tristate free_int
 
 void
 parse_pipelinePrefetch(int *var)
 {
-    char *token = ConfigParser::strtokFile();
+    char *token = ConfigParser::NextTokenPreview();
 
     if (token == NULL)
         self_destruct();
 
     if (!strcmp(token, "on")) {
         debugs(0, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: 'pipeline_prefetch on' is deprecated. Please update to use 1 (or a higher number).");
         *var = 1;
+        //pop the token
+        (void)ConfigParser::NextToken();
     } else if (!strcmp(token, "off")) {
         debugs(0, DBG_PARSE_NOTE(2), "WARNING: 'pipeline_prefetch off' is deprecated. Please update to use '0'.");
         *var = 0;
-    } else {
-        ConfigParser::TokenUndo();
+        //pop the token
+        (void)ConfigParser::NextToken();
+    } else
         parse_int(var);
-    }
 }
 
 #define free_pipelinePrefetch free_int
 #define dump_pipelinePrefetch dump_int
 
 static void
 dump_refreshpattern(StoreEntry * entry, const char *name, RefreshPattern * head)
 {
     while (head != NULL) {
         storeAppendPrintf(entry, "%s%s %s %d %d%% %d",
                           name,
                           head->flags.icase ? " -i" : null_string,
                           head->pattern,
                           (int) head->min / 60,
                           (int) (100.0 * head->pct + 0.5),
                           (int) head->max / 60);
 
         if (head->max_stale >= 0)
             storeAppendPrintf(entry, " max-stale=%d", head->max_stale);
 
@@ -2787,48 +2793,48 @@
     int max_stale = -1;
 
 #if USE_HTTP_VIOLATIONS
 
     int override_expire = 0;
     int override_lastmod = 0;
     int reload_into_ims = 0;
     int ignore_reload = 0;
     int ignore_no_store = 0;
     int ignore_must_revalidate = 0;
     int ignore_private = 0;
     int ignore_auth = 0;
 #endif
 
     int i;
     RefreshPattern *t;
     regex_t comp;
     int errcode;
     int flags = REG_EXTENDED | REG_NOSUB;
 
-    if ((token = ConfigParser::NextToken()) != NULL) {
+    if ((token = ConfigParser::RegexPattern()) != NULL) {
 
         if (strcmp(token, "-i") == 0) {
             flags |= REG_ICASE;
-            token = ConfigParser::NextToken();
+            token = ConfigParser::RegexPattern();
         } else if (strcmp(token, "+i") == 0) {
             flags &= ~REG_ICASE;
-            token = ConfigParser::NextToken();
+            token = ConfigParser::RegexPattern();
         }
 
     }
 
     if (token == NULL) {
         debugs(3, DBG_CRITICAL, "FATAL: refresh_pattern missing the regex pattern parameter");
         self_destruct();
         return;
     }
 
     pattern = xstrdup(token);
 
     i = GetInteger();		/* token: min */
 
     /* catch negative and insanely huge values close to 32-bit wrap */
     if (i < 0) {
         debugs(3, DBG_IMPORTANT, "WARNING: refresh_pattern minimum age negative. Cropped back to zero.");
         i = 0;
     }
     if (i > 60*24*365) {
@@ -3228,41 +3234,41 @@
 
 static void
 dump_wordlist(StoreEntry * entry, const char *name, wordlist * list)
 {
     while (list != NULL) {
         storeAppendPrintf(entry, "%s %s\n", name, list->key);
         list = list->next;
     }
 }
 
 void
 ConfigParser::ParseWordList(wordlist ** list)
 {
     parse_wordlist(list);
 }
 
 void
 parse_wordlist(wordlist ** list)
 {
     char *token;
-    while ((token = ConfigParser::NextToken()))
+    while ((token = ConfigParser::NextQuotedToken()))
         wordlistAdd(list, token);
 }
 
 #if 0 /* now unused */
 static int
 check_null_wordlist(wordlist * w)
 {
     return w == NULL;
 }
 #endif
 
 static int
 check_null_acl_access(acl_access * a)
 {
     return a == NULL;
 }
 
 #define free_wordlist wordlistDestroy
 
 #define free_uri_whitespace free_int
@@ -4059,73 +4065,75 @@
     /* determine configuration style */
 
     const char *filename = ConfigParser::NextToken();
     if (!filename) {
         self_destruct();
         return;
     }
 
     if (strcmp(filename, "none") == 0) {
         cl->type = Log::Format::CLF_NONE;
         aclParseAclList(LegacyParser, &cl->aclList, filename);
         while (*logs)
             logs = &(*logs)->next;
         *logs = cl;
         return;
     }
 
     cl->filename = xstrdup(filename);
     cl->type = Log::Format::CLF_UNKNOWN;
 
-    const char *token = ConfigParser::strtokFile();
+    const char *token = ConfigParser::NextTokenPreview();
     if (!token) { // style #1
         // no options to deal with
     } else if (!strchr(token, '=')) { // style #3
-        // if logformat name is not recognized,
-        // put back the token; it must be an ACL name
-        if (!setLogformat(cl, token, false))
-            ConfigParser::TokenUndo();
+        // if logformat name is recognized,
+        // pop the previewed token; Else it must be an ACL name
+        if (setLogformat(cl, token, false))
+            (void)ConfigParser::NextToken();
     } else { // style #4
         do {
             if (strncasecmp(token, "on-error=", 9) == 0) {
                 if (strncasecmp(token+9, "die", 3) == 0) {
                     cl->fatal = true;
                 } else if (strncasecmp(token+9, "drop", 4) == 0) {
                     cl->fatal = false;
                 } else {
                     debugs(3, DBG_CRITICAL, "Unknown value for on-error '" <<
                            token << "' expected 'drop' or 'die'");
                     self_destruct();
                 }
             } else if (strncasecmp(token, "buffer-size=", 12) == 0) {
                 parseBytesOptionValue(&cl->bufferSize, B_BYTES_STR, token+12);
             } else if (strncasecmp(token, "logformat=", 10) == 0) {
                 setLogformat(cl, token+10, true);
             } else if (!strchr(token, '=')) {
-                // put back the token; it must be an ACL name
-                ConfigParser::TokenUndo();
+                // Do not pop the token; it must be an ACL name
                 break; // done with name=value options, now to ACLs
             } else {
                 debugs(3, DBG_CRITICAL, "Unknown access_log option " << token);
                 self_destruct();
             }
-        } while ((token = ConfigParser::strtokFile()) != NULL);
+            // Pop the token, it was a valid "name=value" option
+            (void)ConfigParser::NextToken();
+            // Get next with preview ConfigParser::NextToken call.
+        } while ((token = ConfigParser::NextTokenPreview()) != NULL);
     }
 
     // set format if it has not been specified explicitly
     if (cl->type == Log::Format::CLF_UNKNOWN)
         setLogformat(cl, "squid", true);
 
     aclParseAclList(LegacyParser, &cl->aclList, cl->filename);
 
     while (*logs)
         logs = &(*logs)->next;
 
     *logs = cl;
 }
 
 /// sets CustomLog::type and, if needed, CustomLog::lf
 /// returns false iff there is no named log format
 static bool
 setLogformat(CustomLog *cl, const char *logdef_name, const bool dieWhenMissing)
 {
     assert(cl);
@@ -4743,41 +4751,41 @@
 }
 
 static void parse_HeaderWithAclList(HeaderWithAclList **headers)
 {
     char *fn;
     if (!*headers) {
         *headers = new HeaderWithAclList;
     }
     if ((fn = ConfigParser::NextToken()) == NULL) {
         self_destruct();
         return;
     }
     HeaderWithAcl hwa;
     hwa.fieldName = fn;
     hwa.fieldId = httpHeaderIdByNameDef(fn, strlen(fn));
     if (hwa.fieldId == HDR_BAD_HDR)
         hwa.fieldId = HDR_OTHER;
 
     Format::Format *nlf =  new ::Format::Format("hdrWithAcl");
     ConfigParser::EnableMacros();
-    String buf = ConfigParser::NextToken();
+    String buf = ConfigParser::NextQuotedToken();
     ConfigParser::DisableMacros();
     hwa.fieldValue = buf.termedBuf();
     hwa.quoted = ConfigParser::LastTokenWasQuoted();
     if (hwa.quoted) {
         if (!nlf->parse(hwa.fieldValue.c_str())) {
             self_destruct();
             return;
         }
         hwa.valueFormat = nlf;
     } else
         delete nlf;
     aclParseAclList(LegacyParser, &hwa.aclList, (hwa.fieldName + ':' + hwa.fieldValue).c_str());
     (*headers)->push_back(hwa);
 }
 
 static void free_HeaderWithAclList(HeaderWithAclList **header)
 {
     if (!(*header))
         return;
 
@@ -4792,20 +4800,50 @@
     }
     delete *header;
     *header = NULL;
 }
 
 static void parse_note(Notes *notes)
 {
     assert(notes);
     notes->parse(LegacyParser);
 }
 
 static void dump_note(StoreEntry *entry, const char *name, Notes &notes)
 {
     notes.dump(entry, name);
 }
 
 static void free_note(Notes *notes)
 {
     notes->clean();
 }
+
+static void
+parse_configuration_includes_quoted_values(bool *recognizeQuotedValues)
+{
+    int val = 0;
+    parse_onoff(&val);
+
+    // If quoted values is set to on then enable new strict mode parsing
+    if (val) {
+        ConfigParser::RecognizeQuotedValues = true;
+        ConfigParser::StrictMode = true;
+    } else {
+        ConfigParser::RecognizeQuotedValues = false;
+        ConfigParser::StrictMode = false;
+    }
+}
+
+static void
+dump_configuration_includes_quoted_values(StoreEntry *const entry, const char *const name, bool recognizeQuotedValues)
+{
+    int val = ConfigParser::RecognizeQuotedValues ? 1 : 0;
+    dump_onoff(entry, name, val);
+}
+
+static void
+free_configuration_includes_quoted_values(bool *recognizeQuotedValues)
+{
+    ConfigParser::RecognizeQuotedValues = false;
+    ConfigParser::StrictMode = false;
+}

=== modified file 'src/cf.data.depend'
--- src/cf.data.depend	2013-05-26 01:57:47 +0000
+++ src/cf.data.depend	2013-08-06 06:47:45 +0000
@@ -1,36 +1,37 @@
 # type			dependencies
 access_log		acl	logformat
 acl			external_acl_type auth_param
 acl_access		acl
 acl_address		acl
 acl_b_size_t		acl
 acl_tos			acl
 acl_nfmark		acl
 address
 authparam
 b_int64_t
 b_size_t
 b_ssize_t
 cachedir		cache_replacement_policy
 cachemgrpasswd
 ConfigAclTos
+configuration_includes_quoted_values
 CpuAffinityMap
 debug
 delay_pool_access	acl	delay_class
 delay_pool_class	delay_pools
 delay_pool_count
 delay_pool_rates	delay_class
 client_delay_pool_access	acl
 client_delay_pool_count
 client_delay_pool_rates
 denyinfo		acl
 eol
 externalAclHelper	auth_param
 HelperChildConfig
 hostdomain		cache_peer
 hostdomaintype		cache_peer
 http_header_access	acl
 http_header_replace
 HeaderWithAclList	acl
 adaptation_access_type	adaptation_service_set adaptation_service_chain acl icap_service icap_class
 adaptation_service_set_type	icap_service ecap_service

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2013-07-27 13:37:29 +0000
+++ src/cf.data.pre	2013-08-06 06:54:23 +0000
@@ -8452,42 +8452,42 @@
 DOC_START
 	The size, low-, and high-water marks for the IP cache.
 DOC_END
 
 NAME: fqdncache_size
 COMMENT: (number of entries)
 TYPE: int
 DEFAULT: 1024
 LOC: Config.fqdncache.size
 DOC_START
 	Maximum number of FQDN cache entries.
 DOC_END
 
 COMMENT_START
  MISCELLANEOUS
  -----------------------------------------------------------------------------
 COMMENT_END
 
 NAME: configuration_includes_quoted_values
 COMMENT: on|off
-TYPE: onoff
-DEFAULT: on
+TYPE: configuration_includes_quoted_values
+DEFAULT: off
 LOC: ConfigParser::RecognizeQuotedValues
 DOC_START
 	If set, Squid will recognize each "quoted string" after a configuration
 	directive as a single parameter. The quotes are stripped before the
 	parameter value is interpreted or used.
 	See "Values with spaces, quotes, and other special characters"
 	section for more details.
 DOC_END
 
 NAME: memory_pools
 COMMENT: on|off
 TYPE: onoff
 DEFAULT: on
 LOC: Config.onoff.mem_pools
 DOC_START
 	If set, Squid will keep pools of allocated (but unused) memory
 	available for future use.  If memory is a premium on your
 	system and you believe your malloc library outperforms Squid
 	routines, disable this.
 DOC_END

Reply via email to