#include "fatal.h"
#include "globals.h"
-char *ConfigParser::lastToken = NULL;
-std::queue<std::string> ConfigParser::undo;
+int ConfigParser::RecognizeQuotedValues = 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;
+std::queue<std::string> ConfigParser::Undo_;
+bool ConfigParser::AllowMacros_ = false;
void
ConfigParser::destruct()
{
shutting_down = 1;
- fatalf("Bungled %s line %d: %s",
- cfg_filename, config_lineno, config_input_line);
+ if (!CfgFiles.empty()) {
+ std::ostringstream message;
+ CfgFile *f = CfgFiles.top();
+ message << "Bungled " << 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",
+ cfg_filename, config_lineno, config_input_line);
}
void
-ConfigParser::strtokFileUndo()
+ConfigParser::TokenUndo()
{
- assert(lastToken);
- undo.push(lastToken);
+ assert(LastToken);
+ Undo_.push(LastToken);
}
void
-ConfigParser::strtokFilePutBack(const char *tok)
+ConfigParser::TokenPutBack(const char *tok)
{
assert(tok);
- undo.push(tok);
+ Undo_.push(tok);
}
char *
-ConfigParser::strtokFile(void)
+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();
+ return undoToken;
+ }
+ return NULL;
+}
+
+char *
+ConfigParser::strtokFile()
+{
+ if (RecognizeQuotedValues)
+ return ConfigParser::NextToken();
+
static int fromFile = 0;
static FILE *wordFile = NULL;
- LOCAL_ARRAY(char, undoToken, CONFIG_LINE_LIMIT);
- char *t, *fn;
+ char *t;
LOCAL_ARRAY(char, buf, CONFIG_LINE_LIMIT);
- if (!undo.empty()) {
- strncpy(undoToken, undo.front().c_str(), sizeof(undoToken));
- undoToken[sizeof(undoToken) - 1] = '\0';
- undo.pop();
- return undoToken;
- }
+ if ((LastToken = ConfigParser::Undo()))
+ return LastToken;
- lastToken = NULL;
do {
if (!fromFile) {
- t = (strtok(NULL, w_space));
-
- if (!t || *t == '#') {
+ ConfigParser::TokenType tokenType;
+ t = ConfigParser::NextElement(tokenType, true);
+ if (!t) {
return NULL;
- } else if (*t == '\"' || *t == '\'') {
+ } else if (tokenType == ConfigParser::QuotedToken) {
/* quote found, start reading from file */
- fn = ++t;
-
- while (*t && *t != '\"' && *t != '\'')
- ++t;
+ debugs(3, 8,"Quoted token found : " << t);
- *t = '\0';
-
- if ((wordFile = fopen(fn, "r")) == NULL) {
- debugs(28, DBG_CRITICAL, "strtokFile: " << fn << " not found");
- return (NULL);
+ if ((wordFile = fopen(t, "r")) == NULL) {
+ debugs(3, DBG_CRITICAL, "Can not open file " << t << " for reading");
+ return false;
}
#if _SQUID_WINDOWS_
fromFile = 1;
} else {
- return lastToken = t;
+ return LastToken = t;
}
}
/* skip blank lines */
} while ( *t == '#' || !*t );
- return lastToken = t;
-}
-
-void
-ConfigParser::ParseQuotedString(char **var, bool *wasQuoted)
-{
- String sVar;
- ParseQuotedString(&sVar, wasQuoted);
- *var = xstrdup(sVar.termedBuf());
+ return LastToken = t;
}
-void
-ConfigParser::ParseQuotedString(String *var, bool *wasQuoted)
+char *
+ConfigParser::UnQuote(char *token, char **end)
{
- // Get all of the remaining string
- char *token = strtok(NULL, "");
- if (token == NULL)
- self_destruct();
-
- if (*token != '"') {
- token = strtok(token, w_space);
- var->reset(token);
- if (wasQuoted)
- *wasQuoted = false;
- return;
- } else if (wasQuoted)
- *wasQuoted = true;
-
+ char quoteChar = *token;
+ assert(quoteChar == '"' || quoteChar == '\'');
char *s = token + 1;
/* scan until the end of the quoted string, unescaping " and \ */
- while (*s && *s != '"') {
- if (*s == '\\') {
+ while (*s && *s != quoteChar) {
+ if (*s == '\\' && isalnum(*( s + 1))) {
+ debugs(3, DBG_CRITICAL, "Unsupported escape sequence: " << s);
+ self_destruct();
+ } else if (*s == '$' && quoteChar == '"') {
+ debugs(3, DBG_CRITICAL, "Unsupported cfg macro: " << s);
+ self_destruct();
+ } 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);
}
++s;
}
- if (*s != '"') {
- debugs(3, DBG_CRITICAL, "ParseQuotedString: missing '\"' at the end of quoted string" );
+ if (*s != quoteChar) {
+ debugs(3, DBG_CRITICAL, "missing '" << quoteChar << "' at the end of quoted string: " << (s-1));
self_destruct();
}
- strtok(s-1, "\""); /*Reset the strtok to point after the " */
- *s = '\0';
+ *end = s;
+ return (token+1);
+}
+
+void
+ConfigParser::SetCfgLine(char *line)
+{
+ CfgLine = line;
+ CfgPos = line;
+}
+
+char *
+ConfigParser::TokenParse(char * &nextToken, ConfigParser::TokenType &type, bool legacy)
+{
+ if (!nextToken || *nextToken == '\0')
+ return NULL;
+ type = ConfigParser::SimpleToken;
+ nextToken += strspn(nextToken, w_space);
+ if (*nextToken == '"' || *nextToken == '\'') {
+ type = ConfigParser::QuotedToken;
+ char *token = UnQuote(nextToken, &nextToken);
+ *nextToken = '\0';
+ ++nextToken;
+ return token;
+ }
+
+ char *token = nextToken;
+ if (char *t = strchr(nextToken, '#'))
+ *t = '\0';
+ const char *sep;
+ if (legacy)
+ sep = w_space;
+ else
+ sep = w_space "(";
+ nextToken += strcspn(nextToken, sep);
+
+ if (!legacy && *nextToken == '(')
+ type = ConfigParser::FunctionNameToken;
+ else
+ type = ConfigParser::SimpleToken;
+
+ if (*nextToken != '\0') {
+ *nextToken = '\0';
+ ++nextToken;
+ }
+
+ if (*token == '\0')
+ return NULL;
+
+ return token;
+}
+
+char *
+ConfigParser::NextElement(ConfigParser::TokenType &type, bool legacy)
+{
+ char *token = TokenParse(CfgPos, type, legacy);
+ return token;
+}
+
+char *
+ConfigParser::NextToken()
+{
+ if ((LastToken = ConfigParser::Undo()))
+ return LastToken;
+
+ char *token = NULL;
+ do {
+ while (token == NULL && !CfgFiles.empty()) {
+ ConfigParser::CfgFile *wordfile = CfgFiles.top();
+ token = wordfile->parse(LastTokenType);
+ if (!token) {
+ assert(!wordfile->isOpen());
+ CfgFiles.pop();
+ delete wordfile;
+ }
+ }
+
+ if (!token)
+ token = NextElement(LastTokenType);
- var->reset(token+1);
+ if (token && LastTokenType == ConfigParser::FunctionNameToken && strcmp("parameters", token) == 0) {
+ 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();
+ 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);
+}
+
+char *
+ConfigParser::NextQuotedOrToEol()
+{
+ char *token;
+
+ if ((token = CfgPos) == NULL) {
+ debugs(3, DBG_CRITICAL, "token is missing");
+ self_destruct();
+ return NULL;
+ }
+ 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;
+
+ CfgPos = NULL;
+ return (LastToken = token);
}
const char *
quotedStr.append('"');
return quotedStr.termedBuf();
}
+
+bool
+ConfigParser::CfgFile::startParse(char *path)
+{
+ assert(wordFile == NULL);
+ 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);
+ wordFile = NULL;
+ parseBuffer[0] = '\0';
+ return false;
+ }
+ parsePos = parseBuffer;
+ currentLine = parseBuffer;
+ lineNo++;
+ return true;
+}
+
+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);
+}
+
+ConfigParser::CfgFile::~CfgFile()
+{
+ if (wordFile)
+ fclose(wordFile);
+}
#include "SquidString.h"
#include <queue>
+#include <stack>
#if HAVE_STRING
#include <string>
#endif
{
public:
+ /**
+ * Parsed tokens type: simple tokens, quoted tokens or function
+ * like parameters.
+ */
+ enum TokenType {SimpleToken, QuotedToken, FunctionNameToken};
+
void destruct();
static void ParseUShort(unsigned short *var);
static void ParseBool(bool *var);
- static void ParseString(char **var);
- static void ParseString(String *var);
- /// Parse an unquoted token (no spaces) or a "quoted string" that
- /// may include spaces. In some contexts, quotes strings may also
- /// include macros. Quoted strings may escape any character with
- /// a backslash (\), which is currently only useful for inner
- /// quotes. TODO: support quoted strings anywhere a token is accepted.
- static void ParseQuotedString(char **var, bool *wasQuoted = NULL);
- static void ParseQuotedString(String *var, bool *wasQuoted = NULL);
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();
- static void strtokFileUndo();
- static void strtokFilePutBack(const char *);
-private:
- static char *lastToken;
- static std::queue<std::string> undo;
+
+ /**
+ * 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();
+
+ /// \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.
+ */
+ static void TokenUndo();
+
+ /**
+ * 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;
+
+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
+ 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
+ * 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.
+ */
+ static char *UnQuote(char *token, char **end = 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);
+
+ /// Wrapper method for TokenParse.
+ static char *NextElement(TokenType &type, bool legacy = false);
+ 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 bool AllowMacros_;
};
int parseConfigFile(const char *file_name);
#include "squid.h"
#include "cache_cf.h"
+#include "ConfigParser.h"
#include "Debug.h"
#include "HelperChildConfig.h"
#include "globals.h"
void
HelperChildConfig::parseConfig()
{
- char const *token = strtok(NULL, w_space);
+ char const *token = ConfigParser::NextToken();
if (!token)
self_destruct();
}
/* Parse extension options */
- for (; (token = strtok(NULL, w_space)) ;) {
+ for (; (token = ConfigParser::NextToken()) ;) {
if (strncmp(token, "startup=", 8) == 0) {
n_startup = xatoui(token + 8);
} else if (strncmp(token, "idle=", 5) == 0) {
Note::Pointer
Notes::parse(ConfigParser &parser)
{
- String key, value;
- ConfigParser::ParseString(&key);
- ConfigParser::ParseQuotedString(&value);
+ String key = ConfigParser::NextToken();
+ String value = ConfigParser::NextToken();
Note::Pointer note = add(key);
Note::Value::Pointer noteValue = note->addValue(value);
int64_t
GetInteger64(void)
{
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (token == NULL)
self_destruct();
int
GetInteger(void)
{
- char *token = ConfigParser::strtokFile();
+ char *token = ConfigParser::NextToken();
int i;
if (token == NULL)
unsigned short
GetShort(void)
{
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (token == NULL)
self_destruct();
#include "cache_cf.h"
#include "compat/strtoll.h"
#include "ConfigOption.h"
+#include "ConfigParser.h"
#include "globals.h"
#include "Parsing.h"
#include "SquidConfig.h"
ConfigOption *newOption = getOptionTree();
- while ((name = strtok(NULL, w_space)) != NULL) {
+ while ((name = ConfigParser::NextToken()) != NULL) {
value = strchr(name, '=');
if (value) {
/*Regex code needs to parse -i file*/
if ( isSet(ACL_F_REGEX_CASE))
- ConfigParser::strtokFilePutBack("-i");
+ ConfigParser::TokenPutBack("-i");
if (nextToken != NULL && strcmp(nextToken, "--") != 0 )
- ConfigParser::strtokFileUndo();
+ ConfigParser::TokenUndo();
}
const char *
/* snarf the ACL name */
- if ((t = strtok(NULL, w_space)) == NULL) {
+ if ((t = ConfigParser::NextToken()) == NULL) {
debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name.");
parser.destruct();
return;
/* snarf the ACL type */
const char *theType;
- if ((theType = strtok(NULL, w_space)) == NULL) {
+ if ((theType = ConfigParser::NextToken()) == NULL) {
debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type.");
parser.destruct();
return;
/* first expect a page name */
- if ((t = strtok(NULL, w_space)) == NULL) {
+ if ((t = ConfigParser::NextToken()) == NULL) {
debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
debugs(28, DBG_CRITICAL, "aclParseDenyInfoLine: missing 'error page' parameter.");
return;
/* next expect a list of ACL names */
Tail = &A->acl_list;
- while ((t = strtok(NULL, w_space))) {
+ while ((t = ConfigParser::NextToken())) {
L = (AclNameList *)memAllocate(MEM_ACL_NAME_LIST);
xstrncpy(L->name, t, ACL_NAME_SZ-1);
*Tail = L;
aclParseAccessLine(const char *directive, ConfigParser &, acl_access **treep)
{
/* first expect either 'allow' or 'deny' */
- const char *t = ConfigParser::strtokFile();
+ const char *t = ConfigParser::NextToken();
if (!t) {
debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
void
Adaptation::Config::ParseAccess(ConfigParser &parser)
{
- String groupId;
- ConfigParser::ParseString(&groupId);
+ String groupId = ConfigParser::NextToken();
AccessRule *r;
if (!(r=FindRuleByGroupId(groupId))) {
r = new AccessRule(groupId);
bool
Adaptation::ServiceConfig::parse()
{
- String method_point;
-
- ConfigParser::ParseString(&key);
- ConfigParser::ParseString(&method_point);
+ key = ConfigParser::NextToken();
+ String method_point = ConfigParser::NextToken();
method = parseMethod(method_point.termedBuf());
point = parseVectPoint(method_point.termedBuf());
bool onOverloadSet = false;
std::set<std::string> options;
- while (char *option = strtok(NULL, w_space)) {
+ while (char *option = ConfigParser::NextToken()) {
const char *name = option;
const char *value = "";
if (strcmp(option, "0") == 0) { // backward compatibility
void
Adaptation::ServiceGroup::parse()
{
- ConfigParser::ParseString(&id);
+ id = ConfigParser::NextToken();
wordlist *names = NULL;
ConfigParser::ParseWordList(&names);
if ((u = parseTimeUnits(units, allowMsec)) == 0)
self_destruct();
- if ((token = strtok(NULL, w_space)) == NULL)
+ if ((token = ConfigParser::NextToken()) == NULL)
self_destruct();
d = xatof(token);
if (0 == d)
(void) 0;
- else if ((token = strtok(NULL, w_space)) == NULL)
+ else if ((token = ConfigParser::NextToken()) == NULL)
debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
config_input_line << "', assuming " <<
d << " " << units );
return;
}
- if ((token = strtok(NULL, w_space)) == NULL) {
+ if ((token = ConfigParser::NextToken()) == NULL) {
self_destruct();
return;
}
if (0.0 == d)
(void) 0;
- else if ((token = strtok(NULL, w_space)) == NULL)
+ else if ((token = ConfigParser::NextToken()) == NULL)
debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
config_input_line << "', assuming " <<
d << " " << units );
return;
}
- if ((token = strtok(NULL, w_space)) == NULL) {
+ if ((token = ConfigParser::NextToken()) == NULL) {
self_destruct();
return;
}
if (0.0 == d)
(void) 0;
- else if ((token = strtok(NULL, w_space)) == NULL)
+ else if ((token = ConfigParser::NextToken()) == NULL)
debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
config_input_line << "', assuming " <<
d << " " << units );
return;
}
- if ((token = strtok(NULL, w_space)) == NULL) {
+ if ((token = ConfigParser::NextToken()) == NULL) {
self_destruct();
return;
}
if (0.0 == d)
(void) 0;
- else if ((token = strtok(NULL, w_space)) == NULL)
+ else if ((token = ConfigParser::NextToken()) == NULL)
debugs(3, DBG_CRITICAL, "WARNING: No units on '" <<
config_input_line << "', assuming " <<
d << " " << units );
static void
parse_address(Ip::Address *addr)
{
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (!token) {
self_destruct();
acl_tos *l;
acl_tos **tail = head; /* sane name below */
unsigned int tos; /* Initially uint for strtoui. Casted to tos_t before return */
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (!token) {
self_destruct();
acl_nfmark *l;
acl_nfmark **tail = head; /* sane name below */
nfmark_t mark;
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (!token) {
self_destruct();
{
char *t = NULL;
- if ((t = strtok(NULL, w_space)) == 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_access: missing header name.");
return;
{
char *t = NULL;
- if ((t = strtok(NULL, w_space)) == 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;
char *type_str;
char *param_str;
- if ((type_str = strtok(NULL, w_space)) == NULL)
+ if ((type_str = ConfigParser::NextToken()) == NULL)
self_destruct();
- if ((param_str = strtok(NULL, w_space)) == NULL)
+ if ((param_str = ConfigParser::NextToken()) == NULL)
self_destruct();
/* find a configuration for the scheme in the currently parsed configs... */
int i;
int fs;
- if ((type_str = strtok(NULL, w_space)) == NULL)
+ if ((type_str = ConfigParser::NextToken()) == NULL)
self_destruct();
- if ((path_str = strtok(NULL, w_space)) == NULL)
+ if ((path_str = ConfigParser::NextToken()) == NULL)
self_destruct();
fs = find_fstype(type_str);
{
struct servent *port = NULL;
/** Parses a port number or service name from the squid.conf */
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (token == NULL) {
self_destruct();
return 0; /* NEVER REACHED */
p->basetime = 0;
p->stats.logged_state = PEER_ALIVE;
- if ((token = strtok(NULL, w_space)) == NULL)
+ if ((token = ConfigParser::NextToken()) == NULL)
self_destruct();
p->host = xstrdup(token);
p->name = xstrdup(token);
- if ((token = strtok(NULL, w_space)) == NULL)
+ if ((token = ConfigParser::NextToken()) == NULL)
self_destruct();
p->type = parseNeighborType(token);
p->icp.port = GetUdpService();
p->connection_auth = 2; /* auto */
- while ((token = strtok(NULL, w_space))) {
+ while ((token = ConfigParser::NextToken())) {
if (!strcmp(token, "proxy-only")) {
p->options.proxy_only = true;
} else if (!strcmp(token, "no-query")) {
char *host = NULL;
CachePeer *p;
- if (!(host = strtok(NULL, w_space)))
+ if (!(host = ConfigParser::NextToken()))
self_destruct();
if ((p = peerFindByName(host)) == NULL) {
char *host = NULL;
char *domain = NULL;
- if (!(host = strtok(NULL, w_space)))
+ if (!(host = ConfigParser::NextToken()))
self_destruct();
- while ((domain = strtok(NULL, list_sep))) {
+ while ((domain = ConfigParser::NextToken())) {
CachePeerDomainList *l = NULL;
CachePeerDomainList **L = NULL;
CachePeer *p;
char *type = NULL;
char *domain = NULL;
- if (!(host = strtok(NULL, w_space)))
+ if (!(host = ConfigParser::NextToken()))
self_destruct();
- if (!(type = strtok(NULL, w_space)))
+ if (!(type = ConfigParser::NextToken()))
self_destruct();
- while ((domain = strtok(NULL, list_sep))) {
+ while ((domain = ConfigParser::NextToken())) {
NeighborTypeDomainList *l = NULL;
NeighborTypeDomainList **L = NULL;
CachePeer *p;
void
parse_onoff(int *var)
{
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (token == NULL)
self_destruct();
static void
parse_tristate(int *var)
{
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (token == NULL)
self_destruct();
debugs(0, DBG_PARSE_NOTE(2), "WARNING: 'pipeline_prefetch off' is deprecated. Please update to use '0'.");
*var = 0;
} else {
- ConfigParser::strtokFileUndo();
+ ConfigParser::TokenUndo();
parse_int(var);
}
}
int errcode;
int flags = REG_EXTENDED | REG_NOSUB;
- if ((token = strtok(NULL, w_space)) == NULL) {
+ if ((token = ConfigParser::NextToken()) == NULL) {
self_destruct();
return;
}
if (strcmp(token, "-i") == 0) {
flags |= REG_ICASE;
- token = strtok(NULL, w_space);
+ token = ConfigParser::NextToken();
} else if (strcmp(token, "+i") == 0) {
flags &= ~REG_ICASE;
- token = strtok(NULL, w_space);
+ token = ConfigParser::NextToken();
}
if (token == NULL) {
max = (time_t) (i * 60); /* convert minutes to seconds */
/* Options */
- while ((token = strtok(NULL, w_space)) != NULL) {
+ while ((token = ConfigParser::NextToken()) != NULL) {
if (!strcmp(token, "refresh-ims")) {
refresh_ims = 1;
} else if (!strcmp(token, "store-stale")) {
static void
parse_string(char **var)
{
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
safe_free(*var);
if (token == NULL)
*var = xstrdup(token);
}
-void
-ConfigParser::ParseString(char **var)
-{
- parse_string(var);
-}
-
-void
-ConfigParser::ParseString(String *var)
-{
- char *token = strtok(NULL, w_space);
-
- if (token == NULL)
- self_destruct();
-
- var->reset(token);
-}
-
static void
free_string(char **var)
{
return;
}
- unsigned char *token = (unsigned char *) strtok(NULL, null_string);
+ unsigned char *token = (unsigned char *) ConfigParser::NextQuotedOrToEol();
safe_free(*var);
if (!token) {
parse_wordlist(wordlist ** list)
{
char *token;
- char *t = strtok(NULL, "");
-
- while ((token = strwordtok(NULL, &t)))
+ while ((token = ConfigParser::NextToken()))
wordlistAdd(list, token);
}
static void
parse_uri_whitespace(int *var)
{
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (token == NULL)
self_destruct();
static void
parse_memcachemode(SquidConfig * config)
{
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (!token)
self_destruct();
Ip::Address_list *s;
Ip::Address ipa;
- while ((token = strtok(NULL, w_space))) {
+ while ((token = ConfigParser::NextToken())) {
if (GetHostWithPort(token, &ipa)) {
while (*head)
return;
}
- char *token = strtok(NULL, w_space);
+ char *token = ConfigParser::NextToken();
if (!token) {
self_destruct();
parsePortSpecification(s, token);
/* parse options ... */
- while ((token = strtok(NULL, w_space))) {
+ while ((token = ConfigParser::NextToken())) {
parse_port_option(s, token);
}
/* determine configuration style */
- const char *filename = strtok(NULL, w_space);
+ const char *filename = ConfigParser::NextToken();
if (!filename) {
self_destruct();
return;
// if logformat name is not recognized,
// put back the token; it must be an ACL name
if (!setLogformat(cl, token, false))
- ConfigParser::strtokFileUndo();
+ ConfigParser::TokenUndo();
} else { // style #4
do {
if (strncasecmp(token, "on-error=", 9) == 0) {
setLogformat(cl, token+10, true);
} else if (!strchr(token, '=')) {
// put back the token; it must be an ACL name
- ConfigParser::strtokFileUndo();
+ ConfigParser::TokenUndo();
break; // done with name=value options, now to ACLs
} else {
debugs(3, DBG_CRITICAL, "Unknown access_log option " << token);
if (!*cpuAffinityMap)
*cpuAffinityMap = new CpuAffinityMap;
- const char *const pToken = strtok(NULL, w_space);
- const char *const cToken = strtok(NULL, w_space);
+ const char *const pToken = ConfigParser::NextToken();
+ const char *const cToken = ConfigParser::NextToken();
Vector<int> processes, cores;
if (!parseNamedIntList(pToken, "process_numbers", processes)) {
debugs(3, DBG_CRITICAL, "FATAL: bad 'process_numbers' parameter " <<
time_t m;
cfg->service_failure_limit = GetInteger();
- if ((token = strtok(NULL, w_space)) == NULL)
+ if ((token = ConfigParser::NextToken()) == NULL)
return;
if (strcmp(token,"in") != 0) {
self_destruct();
}
- if ((token = strtok(NULL, w_space)) == NULL) {
+ if ((token = ConfigParser::NextToken()) == NULL) {
self_destruct();
}
if (0 == d)
(void) 0;
- else if ((token = strtok(NULL, w_space)) == NULL) {
+ else if ((token = ConfigParser::NextToken()) == NULL) {
debugs(3, DBG_CRITICAL, "No time-units on '" << config_input_line << "'");
self_destruct();
} else if ((m = parseTimeUnits(token, false)) == 0)
{
char *al;
sslproxy_cert_adapt *ca = (sslproxy_cert_adapt *) xcalloc(1, sizeof(sslproxy_cert_adapt));
- if ((al = strtok(NULL, w_space)) == NULL) {
+ if ((al = ConfigParser::NextToken()) == NULL) {
self_destruct();
return;
}
{
char *al;
sslproxy_cert_sign *cs = (sslproxy_cert_sign *) xcalloc(1, sizeof(sslproxy_cert_sign));
- if ((al = strtok(NULL, w_space)) == NULL) {
+ if ((al = ConfigParser::NextToken()) == NULL) {
self_destruct();
return;
}
static BumpCfgStyle bumpCfgStyleLast = bcsNone;
BumpCfgStyle bumpCfgStyleNow = bcsNone;
char *bm;
- if ((bm = strtok(NULL, w_space)) == NULL) {
+ if ((bm = ConfigParser::NextToken()) == NULL) {
self_destruct();
return;
}
if (!*headers) {
*headers = new HeaderWithAclList;
}
- if ((fn = strtok(NULL, w_space)) == NULL) {
+ if ((fn = ConfigParser::NextToken()) == NULL) {
self_destruct();
return;
}
if (hwa.fieldId == HDR_BAD_HDR)
hwa.fieldId = HDR_OTHER;
- String buf;
- bool wasQuoted;
- ConfigParser::ParseQuotedString(&buf, &wasQuoted);
+ Format::Format *nlf = new ::Format::Format("hdrWithAcl");
+ ConfigParser::EnableMacros();
+ String buf = ConfigParser::NextToken();
+ ConfigParser::DisableMacros();
hwa.fieldValue = buf.termedBuf();
- hwa.quoted = wasQuoted;
+ hwa.quoted = ConfigParser::LastTokenWasQuoted();
if (hwa.quoted) {
- Format::Format *nlf = new ::Format::Format("hdrWithAcl");
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);
}
configuration files.
+ Values with spaces, quotes, and other special characters
+
+ Squid supports directive parameters with spaces, quotes, and other
+ special characters. Surround such parameters with "double quotes". Use
+ the configuration_includes_quoted_values directive to enable or
+ disable that support.
+
+ Squid supports reading configuration option parameters from external
+ files using the syntax:
+ parameters("/path/filename")
+ For example:
+ acl whitelist dstdomain parameters("/etc/squid/whitelist.txt")
+
Conditional configuration
If-statements can be used to make configuration directives
-----------------------------------------------------------------------------
COMMENT_END
+NAME: configuration_includes_quoted_values
+COMMENT: on|off
+TYPE: onoff
+DEFAULT: on
+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
"{\n"
"\tchar\t*token;\n"
"\tif ((token = strtok(buff, w_space)) == NULL) \n"
- "\t\treturn 1;\t/* ignore empty lines */\n";
+ "\t\treturn 1;\t/* ignore empty lines */\n"
+ "\tConfigParser::SetCfgLine(strtok(NULL, \"\"));\n";
for (EntryList::const_iterator e = head.begin(); e != head.end(); ++e)
e->genParse(fout);
#include "cache_cf.h"
#include "client_side.h"
#include "comm/Connection.h"
+#include "ConfigParser.h"
#include "ExternalACL.h"
#include "ExternalACLEntry.h"
#include "fde.h"
a->local_addr.setLocalhost();
a->quote = external_acl::QUOTE_METHOD_URL;
- token = strtok(NULL, w_space);
+ token = ConfigParser::NextToken();
if (!token)
self_destruct();
a->name = xstrdup(token);
- token = strtok(NULL, w_space);
+ // Allow supported %macros inside quoted tokens
+ ConfigParser::EnableMacros();
+ token = ConfigParser::NextToken();
/* Parse options */
while (token) {
break;
}
- token = strtok(NULL, w_space);
+ token = ConfigParser::NextToken();
}
+ ConfigParser::DisableMacros();
/* check that child startup value is sane. */
if (a->children.n_startup > a->children.n_max)
*p = format;
p = &format->next;
- token = strtok(NULL, w_space);
+ token = ConfigParser::NextToken();
}
/* There must be at least one format token */
#include "squid.h"
+#include "ConfigParser.h"
#include "cache_cf.h"
#include "Debug.h"
#include "format/Config.h"
{
char *name, *def;
- if ((name = strtok(NULL, w_space)) == NULL)
+ if ((name = ConfigParser::NextToken()) == NULL)
self_destruct();
- if ((def = strtok(NULL, "\r\n")) == NULL) {
+ if ((def = ConfigParser::NextQuotedOrToEol()) == NULL) {
self_destruct();
return;
}
#define _SQUID_FORMAT_FORMAT_H
#include "base/RefCount.h"
+#include "ConfigParser.h"
/*
* Squid configuration allows users to define custom formats in
* several components.
{
public:
Format(const char *name);
- ~Format();
+ virtual ~Format();
/* very inefficent parser, but who cares, this needs to be simple */
/* First off, let's tokenize, we'll optimize in a second pass.
self_destruct();
#endif
- while ( (token = strtok(NULL, w_space)) ) {
+ while ( (token = ConfigParser::NextToken()) ) {
// Work out TOS or mark. Default to TOS for backwards compatibility
if (!(mark || tos)) {
#include "squid.h"
#include "cache_cf.h"
+#include "ConfigParser.h"
#include "Debug.h"
#include "log/Config.h"
{
char *name, *def;
- if ((name = strtok(NULL, w_space)) == NULL)
+ if ((name = ConfigParser::NextToken()) == NULL)
self_destruct();
- if ((def = strtok(NULL, "\r\n")) == NULL) {
+ ::Format::Format *nlf = new ::Format::Format(name);
+
+ ConfigParser::EnableMacros();
+ if ((def = ConfigParser::NextQuotedOrToEol()) == NULL) {
self_destruct();
return;
}
+ ConfigParser::DisableMacros();
debugs(3, 2, "Log Format for '" << name << "' is '" << def << "'");
- ::Format::Format *nlf = new ::Format::Format(name);
-
if (!nlf->parse(def)) {
self_destruct();
return;
void parse_time_t(time_t * var) STUB
char * strtokFile(void) STUB_RETVAL(NULL)
void ConfigParser::ParseUShort(unsigned short *var) STUB
-void ConfigParser::ParseString(String*) STUB
void dump_acl_access(StoreEntry * entry, const char *name, acl_access * head) STUB
void dump_acl_list(StoreEntry*, ACLList*) STUB
YesNoNone::operator void*() const { STUB_NOP; return NULL; }
#include "testACLMaxUserIP.h"
#include "auth/AclMaxUserIp.h"
+#include "ConfigParser.h"
#if HAVE_STDEXCEPT
#include <stdexcept>
testACLMaxUserIP::testParseLine()
{
/* a config line to pass with a lead-in token to seed the parser. */
- char * line = xstrdup("token -s 1");
+ char * line = xstrdup("-s 1");
/* seed the parser */
- strtok(line, w_space);
+ ConfigParser::SetCfgLine(line);
ACLMaxUserIP anACL("max_user_ip");
anACL.parse();
/* we want a maximum of one, and strict to be true */
#include "auth/UserRequest.h"
#include "auth/Scheme.h"
#include "auth/Config.h"
+#include "ConfigParser.h"
#include "Mem.h"
CPPUNIT_TEST_SUITE_REGISTRATION( testAuth );
for (unsigned position=0; position < param_count; ++position) {
char *param_str=xstrdup(params[position]);
strtok(param_str, w_space);
+ ConfigParser::SetCfgLine(strtok(NULL, ""));
scheme->parse(scheme, config.size(), param_str);
}
}
{
char cfgline[2048];
char cfgparam[2048];
- snprintf(cfgline, 2048, "Config %s", s);
-
- // Points to the start of quoted string
- const char *tmp = strchr(cfgline, ' ');
-
- if (tmp == NULL) {
- fprintf(stderr, "Invalid config line: %s\n", s);
- return false;
- }
+ snprintf(cfgline, 2048, "%s", s);
// Keep the initial value on cfgparam. The ConfigParser methods will write on cfgline
- strncpy(cfgparam, tmp+1, sizeof(cfgparam)-1);
+ strncpy(cfgparam, cfgline, sizeof(cfgparam)-1);
cfgparam[sizeof(cfgparam)-1] = '\0';
// Initialize parser to point to the start of quoted string
- strtok(cfgline, w_space);
- String unEscaped;
- ConfigParser::ParseQuotedString(&unEscaped);
+ ConfigParser::SetCfgLine(cfgline);
+ String unEscaped = ConfigParser::NextToken();
const bool interpOk = (unEscaped.cmp(expectInterp) == 0);
if (!interpOk) {
#define SQUID_UNIT_TEST 1
#include "squid.h"
+#include "ConfigParser.h"
#include "testCoss.h"
#include "Store.h"
#include "SwapDir.h"
char *path=xstrdup(TESTDIR);
- char *config_line=xstrdup("foo 100 max-size=102400 block-size=512 IOEngine=Blocking");
+ char *config_line=xstrdup("100 max-size=102400 block-size=512 IOEngine=Blocking");
- strtok(config_line, w_space);
+ ConfigParser::SetCfgLine(config_line);
aStore->parse(0, path);
char *path=xstrdup(TESTDIR);
- char *config_line=xstrdup("foo 100 max-size=102400 block-size=512 IOEngine=Blocking");
+ char *config_line=xstrdup("100 max-size=102400 block-size=512 IOEngine=Blocking");
- strtok(config_line, w_space);
+ ConfigParser::SetCfgLine(config_line);
aStore->parse(0, path);
commonInit();
char *path=xstrdup(TESTDIR);
- char *config_line=xstrdup("foo 100 max-size=102400 block-size=512");
- strtok(config_line, w_space);
+ char *config_line=xstrdup("100 max-size=102400 block-size=512");
+ ConfigParser::SetCfgLine(config_line);
aStore->parse(0, path);
safe_free(path);
safe_free(config_line);
#define SQUID_UNIT_TEST 1
#include "squid.h"
+#include "ConfigParser.h"
#include "DiskIO/DiskIOModule.h"
#include "fs/rock/RockSwapDir.h"
#include "globals.h"
char *path=xstrdup(TESTDIR);
- char *config_line=xstrdup("foo 10 max-size=16384");
+ char *config_line=xstrdup("10 max-size=16384");
- strtok(config_line, w_space);
+ ConfigParser::SetCfgLine(config_line);
store->parse(0, path);
store_maxobjsize = 1024*1024*2;
char *path=xstrdup(TESTDIR);
- char *config_line=xstrdup("foo 100 1 1");
+ char *config_line=xstrdup("100 1 1");
visible_appname_string = xstrdup(PACKAGE "/" VERSION);
- strtok(config_line, w_space);
+ ConfigParser::SetCfgLine(config_line);
aStore->parse(0, path);
store_maxobjsize = 1024*1024*2;
mem_policy = createRemovalPolicy(Config.replPolicy);
char *path=xstrdup(TESTDIR);
- char *config_line=xstrdup("foo 100 1 1");
- strtok(config_line, w_space);
+ char *config_line=xstrdup("100 1 1");
+ ConfigParser::SetCfgLine(config_line);
aStore->parse(0, path);
safe_free(path);
safe_free(config_line);
#include "comm/Connection.h"
#include "comm/Loops.h"
#include "compat/strsep.h"
+#include "ConfigParser.h"
#include "event.h"
#include "ip/Address.h"
#include "md5.h"
char *t;
/* Snarf the method */
- if ((t = strtok(NULL, w_space)) == NULL) {
+ if ((t = ConfigParser::NextToken()) == NULL) {
debugs(80, DBG_CRITICAL, "wccp2_*_method: missing setting.");
self_destruct();
}
char *t;
/* Snarf the method */
- if ((t = strtok(NULL, w_space)) == NULL) {
+ if ((t = ConfigParser::NextToken()) == NULL) {
debugs(80, DBG_CRITICAL, "wccp2_assignment_method: missing setting.");
self_destruct();
}
}
/* Snarf the type */
- if ((t = strtok(NULL, w_space)) == NULL) {
+ if ((t = ConfigParser::NextToken()) == NULL) {
debugs(80, DBG_CRITICAL, "wccp2ParseServiceInfo: missing service info type (standard|dynamic)");
self_destruct();
}
memset(wccp_password, 0, sizeof(wccp_password));
/* Handle password, if any */
- if ((t = strtok(NULL, w_space)) != NULL) {
+ if ((t = ConfigParser::NextToken()) != NULL) {
if (strncmp(t, "password=", 9) == 0) {
security_type = WCCP2_MD5_SECURITY;
strncpy(wccp_password, t + 9, WCCP2_PASSWORD_LEN);
}
/* Next: loop until we don't have any more tokens */
- while ((t = strtok(NULL, w_space)) != NULL) {
+ while ((t = ConfigParser::NextToken()) != NULL) {
if (strncmp(t, "flags=", 6) == 0) {
/* XXX eww, string pointer math */
flags = parse_wccp2_service_flags(t + 6);