]>
Commit | Line | Data |
---|---|---|
d295d770 | 1 | /* |
ef57eb7b | 2 | * Copyright (C) 1996-2016 The Squid Software Foundation and contributors |
d295d770 | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
d295d770 | 7 | */ |
8 | ||
f7f3304a | 9 | #include "squid.h" |
8a01b99e | 10 | #include "cache_cf.h" |
d295d770 | 11 | #include "ConfigParser.h" |
582c2af2 | 12 | #include "Debug.h" |
9ce629cf | 13 | #include "fatal.h" |
d295d770 | 14 | #include "globals.h" |
15 | ||
bde7a8ce CT |
16 | bool ConfigParser::RecognizeQuotedValues = true; |
17 | bool ConfigParser::StrictMode = true; | |
2eceb328 CT |
18 | std::stack<ConfigParser::CfgFile *> ConfigParser::CfgFiles; |
19 | ConfigParser::TokenType ConfigParser::LastTokenType = ConfigParser::SimpleToken; | |
bde7a8ce CT |
20 | const char *ConfigParser::CfgLine = NULL; |
21 | const char *ConfigParser::CfgPos = NULL; | |
22 | std::queue<char *> ConfigParser::CfgLineTokens_; | |
2eceb328 CT |
23 | std::queue<std::string> ConfigParser::Undo_; |
24 | bool ConfigParser::AllowMacros_ = false; | |
bde7a8ce | 25 | bool ConfigParser::ParseQuotedOrToEol_ = false; |
32fd6d8a CT |
26 | bool ConfigParser::ParseKvPair_ = false; |
27 | ConfigParser::ParsingStates ConfigParser::KvPairState_ = ConfigParser::atParseKey; | |
61a31961 | 28 | bool ConfigParser::RecognizeQuotedPair_ = false; |
bde7a8ce CT |
29 | bool ConfigParser::PreviewMode_ = false; |
30 | ||
31 | static const char *SQUID_ERROR_TOKEN = "[invalid token]"; | |
33810b1d | 32 | |
d295d770 | 33 | void |
a9f20260 | 34 | ConfigParser::destruct() |
d295d770 | 35 | { |
36 | shutting_down = 1; | |
2eceb328 CT |
37 | if (!CfgFiles.empty()) { |
38 | std::ostringstream message; | |
39 | CfgFile *f = CfgFiles.top(); | |
40 | message << "Bungled " << f->filePath << " line " << f->lineNo << | |
f53969cc | 41 | ": " << f->currentLine << std::endl; |
2eceb328 CT |
42 | CfgFiles.pop(); |
43 | delete f; | |
44 | while (!CfgFiles.empty()) { | |
45 | f = CfgFiles.top(); | |
46 | message << " included from " << f->filePath << " line " << | |
f53969cc | 47 | f->lineNo << ": " << f->currentLine << std::endl; |
2eceb328 CT |
48 | CfgFiles.pop(); |
49 | delete f; | |
50 | } | |
51 | message << " included from " << cfg_filename << " line " << | |
f53969cc | 52 | config_lineno << ": " << config_input_line << std::endl; |
2eceb328 CT |
53 | std::string msg = message.str(); |
54 | fatalf("%s", msg.c_str()); | |
55 | } else | |
56 | fatalf("Bungled %s line %d: %s", | |
57 | cfg_filename, config_lineno, config_input_line); | |
d295d770 | 58 | } |
59 | ||
33810b1d | 60 | void |
2eceb328 | 61 | ConfigParser::TokenPutBack(const char *tok) |
33810b1d CT |
62 | { |
63 | assert(tok); | |
2eceb328 | 64 | Undo_.push(tok); |
33810b1d CT |
65 | } |
66 | ||
d295d770 | 67 | char * |
2eceb328 | 68 | ConfigParser::Undo() |
d295d770 | 69 | { |
cfd861ab | 70 | static char undoToken[CONFIG_LINE_LIMIT]; |
2eceb328 | 71 | if (!Undo_.empty()) { |
cfd861ab | 72 | xstrncpy(undoToken, Undo_.front().c_str(), sizeof(undoToken)); |
2eceb328 | 73 | undoToken[sizeof(undoToken) - 1] = '\0'; |
bde7a8ce CT |
74 | if (!PreviewMode_) |
75 | Undo_.pop(); | |
2eceb328 CT |
76 | return undoToken; |
77 | } | |
78 | return NULL; | |
79 | } | |
80 | ||
81 | char * | |
82 | ConfigParser::strtokFile() | |
83 | { | |
84 | if (RecognizeQuotedValues) | |
85 | return ConfigParser::NextToken(); | |
86 | ||
d295d770 | 87 | static int fromFile = 0; |
88 | static FILE *wordFile = NULL; | |
89 | ||
2eceb328 | 90 | char *t; |
cfd861ab | 91 | static char buf[CONFIG_LINE_LIMIT]; |
d295d770 | 92 | |
bde7a8ce CT |
93 | if ((t = ConfigParser::Undo())) |
94 | return t; | |
33810b1d | 95 | |
f32cd13e | 96 | do { |
d295d770 | 97 | |
f32cd13e | 98 | if (!fromFile) { |
2eceb328 | 99 | ConfigParser::TokenType tokenType; |
bde7a8ce | 100 | t = ConfigParser::NextElement(tokenType); |
2eceb328 | 101 | if (!t) { |
26ac0430 | 102 | return NULL; |
bde7a8ce | 103 | } else if (*t == '\"' || *t == '\'') { |
f32cd13e | 104 | /* quote found, start reading from file */ |
2eceb328 | 105 | debugs(3, 8,"Quoted token found : " << t); |
bde7a8ce | 106 | char *fn = ++t; |
d295d770 | 107 | |
bde7a8ce CT |
108 | while (*t && *t != '\"' && *t != '\'') |
109 | ++t; | |
110 | ||
111 | *t = '\0'; | |
112 | ||
113 | if ((wordFile = fopen(fn, "r")) == NULL) { | |
39952fbb | 114 | debugs(3, DBG_CRITICAL, "ERROR: Can not open file " << fn << " for reading"); |
4a4b58d5 | 115 | return NULL; |
f32cd13e | 116 | } |
d295d770 | 117 | |
be266cb2 | 118 | #if _SQUID_WINDOWS_ |
f32cd13e | 119 | setmode(fileno(wordFile), O_TEXT); |
d295d770 | 120 | #endif |
121 | ||
f32cd13e AJ |
122 | fromFile = 1; |
123 | } else { | |
bde7a8ce | 124 | return t; |
f32cd13e | 125 | } |
d295d770 | 126 | } |
f32cd13e AJ |
127 | |
128 | /* fromFile */ | |
cfd861ab | 129 | if (fgets(buf, sizeof(buf), wordFile) == NULL) { |
f32cd13e AJ |
130 | /* stop reading from file */ |
131 | fclose(wordFile); | |
132 | wordFile = NULL; | |
133 | fromFile = 0; | |
134 | return NULL; | |
135 | } else { | |
136 | char *t2, *t3; | |
137 | t = buf; | |
138 | /* skip leading and trailing white space */ | |
139 | t += strspn(buf, w_space); | |
140 | t2 = t + strcspn(t, w_space); | |
d295d770 | 141 | t3 = t2 + strspn(t2, w_space); |
d295d770 | 142 | |
f32cd13e AJ |
143 | while (*t3 && *t3 != '#') { |
144 | t2 = t3 + strcspn(t3, w_space); | |
145 | t3 = t2 + strspn(t2, w_space); | |
146 | } | |
d295d770 | 147 | |
f32cd13e AJ |
148 | *t2 = '\0'; |
149 | } | |
d295d770 | 150 | |
f32cd13e | 151 | /* skip comments */ |
d295d770 | 152 | /* skip blank lines */ |
26ac0430 | 153 | } while ( *t == '#' || !*t ); |
d295d770 | 154 | |
bde7a8ce | 155 | return t; |
71be37e0 CT |
156 | } |
157 | ||
2eceb328 | 158 | char * |
bde7a8ce | 159 | ConfigParser::UnQuote(const char *token, const char **next) |
71be37e0 | 160 | { |
bde7a8ce CT |
161 | const char *errorStr = NULL; |
162 | const char *errorPos = NULL; | |
2eceb328 CT |
163 | char quoteChar = *token; |
164 | assert(quoteChar == '"' || quoteChar == '\''); | |
bde7a8ce CT |
165 | LOCAL_ARRAY(char, UnQuoted, CONFIG_LINE_LIMIT); |
166 | const char *s = token + 1; | |
167 | char *d = UnQuoted; | |
168 | /* scan until the end of the quoted string, handling escape sequences*/ | |
a59fd1df | 169 | while (*s && *s != quoteChar && !errorStr && (size_t)(d - UnQuoted) < sizeof(UnQuoted)) { |
bde7a8ce CT |
170 | if (*s == '\\') { |
171 | s++; | |
172 | switch (*s) { | |
173 | case 'r': | |
174 | *d = '\r'; | |
175 | break; | |
176 | case 'n': | |
177 | *d = '\n'; | |
178 | break; | |
179 | case 't': | |
180 | *d = '\t'; | |
181 | break; | |
182 | default: | |
183 | if (isalnum(*s)) { | |
184 | errorStr = "Unsupported escape sequence"; | |
185 | errorPos = s; | |
186 | } | |
187 | *d = *s; | |
188 | break; | |
189 | } | |
190 | #if 0 | |
2eceb328 | 191 | } else if (*s == '$' && quoteChar == '"') { |
bde7a8ce CT |
192 | errorStr = "Unsupported cfg macro"; |
193 | errorPos = s; | |
194 | #endif | |
1ee7b53a | 195 | #if 0 |
2eceb328 | 196 | } else if (*s == '%' && quoteChar == '"' && (!AllowMacros_ )) { |
bde7a8ce CT |
197 | errorStr = "Macros are not supported here"; |
198 | errorPos = s; | |
1ee7b53a | 199 | #endif |
bde7a8ce CT |
200 | } else |
201 | *d = *s; | |
95dc7ff4 | 202 | ++s; |
bde7a8ce | 203 | ++d; |
71be37e0 CT |
204 | } |
205 | ||
bde7a8ce CT |
206 | if (*s != quoteChar && !errorStr) { |
207 | errorStr = "missing quote char at the end of quoted string"; | |
208 | errorPos = s - 1; | |
71be37e0 | 209 | } |
bde7a8ce CT |
210 | // The end of token |
211 | *d = '\0'; | |
212 | ||
213 | // We are expecting a separator after quoted string, space or one of "()#" | |
214 | if (*(s + 1) != '\0' && !strchr(w_space "()#", *(s + 1)) && !errorStr) { | |
215 | errorStr = "Expecting space after the end of quoted token"; | |
216 | errorPos = token; | |
217 | } | |
218 | ||
219 | if (errorStr) { | |
220 | if (PreviewMode_) | |
cfd861ab | 221 | xstrncpy(UnQuoted, SQUID_ERROR_TOKEN, sizeof(UnQuoted)); |
bde7a8ce | 222 | else { |
39952fbb | 223 | debugs(3, DBG_CRITICAL, "FATAL: " << errorStr << ": " << errorPos); |
bde7a8ce CT |
224 | self_destruct(); |
225 | } | |
226 | } | |
227 | ||
228 | if (next) | |
229 | *next = s + 1; | |
230 | return UnQuoted; | |
2eceb328 CT |
231 | } |
232 | ||
233 | void | |
234 | ConfigParser::SetCfgLine(char *line) | |
235 | { | |
236 | CfgLine = line; | |
237 | CfgPos = line; | |
bde7a8ce CT |
238 | while (!CfgLineTokens_.empty()) { |
239 | char *token = CfgLineTokens_.front(); | |
240 | CfgLineTokens_.pop(); | |
241 | free(token); | |
242 | } | |
2eceb328 CT |
243 | } |
244 | ||
245 | char * | |
bde7a8ce | 246 | ConfigParser::TokenParse(const char * &nextToken, ConfigParser::TokenType &type) |
2eceb328 CT |
247 | { |
248 | if (!nextToken || *nextToken == '\0') | |
249 | return NULL; | |
250 | type = ConfigParser::SimpleToken; | |
251 | nextToken += strspn(nextToken, w_space); | |
bde7a8ce CT |
252 | |
253 | if (*nextToken == '#') | |
254 | return NULL; | |
255 | ||
256 | if (ConfigParser::RecognizeQuotedValues && (*nextToken == '"' || *nextToken == '\'')) { | |
2eceb328 | 257 | type = ConfigParser::QuotedToken; |
bde7a8ce CT |
258 | char *token = xstrdup(UnQuote(nextToken, &nextToken)); |
259 | CfgLineTokens_.push(token); | |
2eceb328 CT |
260 | return token; |
261 | } | |
262 | ||
bde7a8ce | 263 | const char *tokenStart = nextToken; |
2eceb328 | 264 | const char *sep; |
32fd6d8a CT |
265 | if (ConfigParser::ParseKvPair_) { |
266 | if (ConfigParser::KvPairState_ == ConfigParser::atParseKey) | |
267 | sep = "="; | |
268 | else | |
269 | sep = w_space; | |
270 | } else if (ConfigParser::ParseQuotedOrToEol_) | |
bde7a8ce | 271 | sep = "\n"; |
61a31961 AJ |
272 | else if (ConfigParser::RecognizeQuotedPair_) |
273 | sep = w_space "\\"; | |
c8b68657 AJ |
274 | else if (!ConfigParser::RecognizeQuotedValues || *nextToken == '(') |
275 | sep = w_space; | |
2eceb328 CT |
276 | else |
277 | sep = w_space "("; | |
278 | nextToken += strcspn(nextToken, sep); | |
279 | ||
c8b68657 AJ |
280 | while (ConfigParser::RecognizeQuotedPair_ && *nextToken == '\\') { |
281 | // NP: do not permit \0 terminator to be escaped. | |
282 | if (*(nextToken+1) && *(nextToken+1) != '\r' && *(nextToken+1) != '\n') { | |
f53969cc SM |
283 | nextToken += 2; // skip the quoted-pair (\-escaped) character |
284 | nextToken += strcspn(nextToken, sep); | |
c8b68657 AJ |
285 | } else { |
286 | debugs(3, DBG_CRITICAL, "FATAL: Unescaped '\' character in regex pattern: " << tokenStart); | |
287 | self_destruct(); | |
288 | } | |
61a31961 AJ |
289 | } |
290 | ||
bde7a8ce CT |
291 | if (ConfigParser::RecognizeQuotedValues && *nextToken == '(') { |
292 | if (strncmp(tokenStart, "parameters", nextToken - tokenStart) == 0) | |
293 | type = ConfigParser::FunctionParameters; | |
294 | else { | |
295 | if (PreviewMode_) { | |
296 | char *err = xstrdup(SQUID_ERROR_TOKEN); | |
297 | CfgLineTokens_.push(err); | |
298 | return err; | |
299 | } else { | |
39952fbb | 300 | debugs(3, DBG_CRITICAL, "FATAL: Unknown cfg function: " << tokenStart); |
bde7a8ce CT |
301 | self_destruct(); |
302 | } | |
303 | } | |
304 | } else | |
2eceb328 CT |
305 | type = ConfigParser::SimpleToken; |
306 | ||
bde7a8ce CT |
307 | char *token = NULL; |
308 | if (nextToken - tokenStart) { | |
309 | if (ConfigParser::StrictMode && type == ConfigParser::SimpleToken) { | |
310 | bool tokenIsNumber = true; | |
311 | for (const char *s = tokenStart; s != nextToken; ++s) { | |
312 | const bool isValidChar = isalnum(*s) || strchr(".,()-=_/:", *s) || | |
313 | (tokenIsNumber && *s == '%' && (s + 1 == nextToken)); | |
314 | ||
315 | if (!isdigit(*s)) | |
316 | tokenIsNumber = false; | |
317 | ||
318 | if (!isValidChar) { | |
319 | if (PreviewMode_) { | |
320 | char *err = xstrdup(SQUID_ERROR_TOKEN); | |
321 | CfgLineTokens_.push(err); | |
322 | return err; | |
323 | } else { | |
39952fbb | 324 | debugs(3, DBG_CRITICAL, "FATAL: Not alphanumeric character '"<< *s << "' in unquoted token " << tokenStart); |
bde7a8ce CT |
325 | self_destruct(); |
326 | } | |
327 | } | |
328 | } | |
329 | } | |
330 | token = xstrndup(tokenStart, nextToken - tokenStart + 1); | |
331 | CfgLineTokens_.push(token); | |
2eceb328 CT |
332 | } |
333 | ||
bde7a8ce CT |
334 | if (*nextToken != '\0' && *nextToken != '#') { |
335 | ++nextToken; | |
336 | } | |
2eceb328 CT |
337 | |
338 | return token; | |
339 | } | |
340 | ||
341 | char * | |
bde7a8ce | 342 | ConfigParser::NextElement(ConfigParser::TokenType &type) |
2eceb328 | 343 | { |
bde7a8ce CT |
344 | const char *pos = CfgPos; |
345 | char *token = TokenParse(pos, type); | |
346 | // If not in preview mode the next call of this method should start | |
347 | // parsing after the end of current token. | |
348 | // For function "parameters(...)" we need always to update current parsing | |
349 | // position to allow parser read the arguments of "parameters(..)" | |
350 | if (!PreviewMode_ || type == FunctionParameters) | |
351 | CfgPos = pos; | |
352 | // else next call will read the same token | |
2eceb328 CT |
353 | return token; |
354 | } | |
355 | ||
356 | char * | |
357 | ConfigParser::NextToken() | |
358 | { | |
2eceb328 | 359 | char *token = NULL; |
bde7a8ce CT |
360 | if ((token = ConfigParser::Undo())) { |
361 | debugs(3, 6, "TOKEN (undone): " << token); | |
362 | return token; | |
363 | } | |
364 | ||
2eceb328 CT |
365 | do { |
366 | while (token == NULL && !CfgFiles.empty()) { | |
367 | ConfigParser::CfgFile *wordfile = CfgFiles.top(); | |
368 | token = wordfile->parse(LastTokenType); | |
369 | if (!token) { | |
370 | assert(!wordfile->isOpen()); | |
371 | CfgFiles.pop(); | |
bde7a8ce | 372 | debugs(3, 4, "CfgFiles.pop " << wordfile->filePath); |
2eceb328 CT |
373 | delete wordfile; |
374 | } | |
375 | } | |
376 | ||
377 | if (!token) | |
378 | token = NextElement(LastTokenType); | |
71be37e0 | 379 | |
bde7a8ce CT |
380 | if (token && LastTokenType == ConfigParser::FunctionParameters) { |
381 | //Disable temporary preview mode, we need to parse function parameters | |
382 | const bool savePreview = ConfigParser::PreviewMode_; | |
383 | ConfigParser::PreviewMode_ = false; | |
384 | ||
2eceb328 CT |
385 | char *path = NextToken(); |
386 | if (LastTokenType != ConfigParser::QuotedToken) { | |
39952fbb | 387 | debugs(3, DBG_CRITICAL, "FATAL: Quoted filename missing: " << token); |
2eceb328 CT |
388 | self_destruct(); |
389 | return NULL; | |
390 | } | |
391 | ||
392 | // The next token in current cfg file line must be a ")" | |
393 | char *end = NextToken(); | |
bde7a8ce | 394 | ConfigParser::PreviewMode_ = savePreview; |
2eceb328 | 395 | if (LastTokenType != ConfigParser::SimpleToken || strcmp(end, ")") != 0) { |
39952fbb | 396 | debugs(3, DBG_CRITICAL, "FATAL: missing ')' after " << token << "(\"" << path << "\""); |
2eceb328 CT |
397 | self_destruct(); |
398 | return NULL; | |
399 | } | |
400 | ||
401 | if (CfgFiles.size() > 16) { | |
39952fbb | 402 | debugs(3, DBG_CRITICAL, "FATAL: can't open %s for reading parameters: includes are nested too deeply (>16)!\n" << path); |
2eceb328 CT |
403 | self_destruct(); |
404 | return NULL; | |
405 | } | |
406 | ||
407 | ConfigParser::CfgFile *wordfile = new ConfigParser::CfgFile(); | |
408 | if (!path || !wordfile->startParse(path)) { | |
39952fbb | 409 | debugs(3, DBG_CRITICAL, "FATAL: Error opening config file: " << token); |
2eceb328 CT |
410 | delete wordfile; |
411 | self_destruct(); | |
412 | return NULL; | |
413 | } | |
414 | CfgFiles.push(wordfile); | |
415 | token = NULL; | |
2eceb328 CT |
416 | } |
417 | } while (token == NULL && !CfgFiles.empty()); | |
418 | ||
bde7a8ce CT |
419 | return token; |
420 | } | |
421 | ||
422 | char * | |
423 | ConfigParser::PeekAtToken() | |
424 | { | |
425 | PreviewMode_ = true; | |
426 | char *token = NextToken(); | |
427 | PreviewMode_ = false; | |
428 | return token; | |
2eceb328 CT |
429 | } |
430 | ||
431 | char * | |
432 | ConfigParser::NextQuotedOrToEol() | |
433 | { | |
bde7a8ce CT |
434 | ParseQuotedOrToEol_ = true; |
435 | char *token = NextToken(); | |
436 | ParseQuotedOrToEol_ = false; | |
437 | ||
438 | // Assume end of current config line | |
439 | // Close all open configuration files for this config line | |
440 | while (!CfgFiles.empty()) { | |
441 | ConfigParser::CfgFile *wordfile = CfgFiles.top(); | |
442 | CfgFiles.pop(); | |
443 | delete wordfile; | |
444 | } | |
2eceb328 | 445 | |
bde7a8ce CT |
446 | return token; |
447 | } | |
32fd6d8a CT |
448 | |
449 | bool | |
450 | ConfigParser::NextKvPair(char * &key, char * &value) | |
451 | { | |
452 | key = value = NULL; | |
453 | ParseKvPair_ = true; | |
454 | KvPairState_ = ConfigParser::atParseKey; | |
455 | if ((key = NextToken()) != NULL) { | |
456 | KvPairState_ = ConfigParser::atParseValue; | |
457 | value = NextQuotedToken(); | |
458 | } | |
459 | ParseKvPair_ = false; | |
460 | ||
461 | if (!key) | |
462 | return false; | |
463 | if (!value) { | |
464 | debugs(3, DBG_CRITICAL, "Error while parsing key=value token. Value missing after: " << key); | |
465 | return false; | |
466 | } | |
467 | ||
468 | return true; | |
469 | } | |
bde7a8ce CT |
470 | |
471 | char * | |
472 | ConfigParser::RegexStrtokFile() | |
473 | { | |
474 | if (ConfigParser::RecognizeQuotedValues) { | |
39952fbb | 475 | debugs(3, DBG_CRITICAL, "FATAL: Can not read regex expression while configuration_includes_quoted_values is enabled"); |
2eceb328 | 476 | self_destruct(); |
2eceb328 | 477 | } |
61a31961 | 478 | ConfigParser::RecognizeQuotedPair_ = true; |
bde7a8ce | 479 | char * token = strtokFile(); |
61a31961 | 480 | ConfigParser::RecognizeQuotedPair_ = false; |
bde7a8ce CT |
481 | return token; |
482 | } | |
2eceb328 | 483 | |
bde7a8ce CT |
484 | char * |
485 | ConfigParser::RegexPattern() | |
486 | { | |
487 | if (ConfigParser::RecognizeQuotedValues) { | |
39952fbb | 488 | debugs(3, DBG_CRITICAL, "FATAL: Can not read regex expression while configuration_includes_quoted_values is enabled"); |
bde7a8ce CT |
489 | self_destruct(); |
490 | } | |
61a31961 | 491 | ConfigParser::RecognizeQuotedPair_ = true; |
bde7a8ce | 492 | char * token = NextToken(); |
61a31961 | 493 | ConfigParser::RecognizeQuotedPair_ = false; |
bde7a8ce CT |
494 | return token; |
495 | } | |
496 | ||
497 | char * | |
498 | ConfigParser::NextQuotedToken() | |
499 | { | |
500 | const bool saveRecognizeQuotedValues = ConfigParser::RecognizeQuotedValues; | |
501 | ConfigParser::RecognizeQuotedValues = true; | |
502 | char *token = NextToken(); | |
503 | ConfigParser::RecognizeQuotedValues = saveRecognizeQuotedValues; | |
504 | return token; | |
71be37e0 CT |
505 | } |
506 | ||
507 | const char * | |
b92a47f2 | 508 | ConfigParser::QuoteString(const String &var) |
71be37e0 CT |
509 | { |
510 | static String quotedStr; | |
511 | const char *s = var.termedBuf(); | |
512 | bool needQuote = false; | |
513 | ||
95dc7ff4 | 514 | for (const char *l = s; !needQuote && *l != '\0'; ++l ) |
71ee0835 | 515 | needQuote = !isalnum(*l); |
71be37e0 CT |
516 | |
517 | if (!needQuote) | |
518 | return s; | |
71ee0835 | 519 | |
71be37e0 CT |
520 | quotedStr.clean(); |
521 | quotedStr.append('"'); | |
95dc7ff4 | 522 | for (; *s != '\0'; ++s) { |
71ee0835 | 523 | if (*s == '"' || *s == '\\') |
71be37e0 CT |
524 | quotedStr.append('\\'); |
525 | quotedStr.append(*s); | |
526 | } | |
527 | quotedStr.append('"'); | |
528 | return quotedStr.termedBuf(); | |
529 | } | |
2eceb328 CT |
530 | |
531 | bool | |
532 | ConfigParser::CfgFile::startParse(char *path) | |
533 | { | |
534 | assert(wordFile == NULL); | |
bde7a8ce | 535 | debugs(3, 3, "Parsing from " << path); |
2eceb328 | 536 | if ((wordFile = fopen(path, "r")) == NULL) { |
39952fbb | 537 | debugs(3, DBG_CRITICAL, "WARNING: file :" << path << " not found"); |
2eceb328 CT |
538 | return false; |
539 | } | |
540 | ||
541 | #if _SQUID_WINDOWS_ | |
542 | setmode(fileno(wordFile), O_TEXT); | |
543 | #endif | |
544 | ||
545 | filePath = path; | |
546 | return getFileLine(); | |
547 | } | |
548 | ||
549 | bool | |
550 | ConfigParser::CfgFile::getFileLine() | |
551 | { | |
552 | // Else get the next line | |
553 | if (fgets(parseBuffer, CONFIG_LINE_LIMIT, wordFile) == NULL) { | |
554 | /* stop reading from file */ | |
555 | fclose(wordFile); | |
556 | wordFile = NULL; | |
557 | parseBuffer[0] = '\0'; | |
558 | return false; | |
559 | } | |
560 | parsePos = parseBuffer; | |
561 | currentLine = parseBuffer; | |
562 | lineNo++; | |
563 | return true; | |
564 | } | |
565 | ||
566 | char * | |
567 | ConfigParser::CfgFile::parse(ConfigParser::TokenType &type) | |
568 | { | |
569 | if (!wordFile) | |
570 | return NULL; | |
571 | ||
572 | if (!*parseBuffer) | |
573 | return NULL; | |
574 | ||
575 | char *token; | |
576 | while (!(token = nextElement(type))) { | |
577 | if (!getFileLine()) | |
578 | return NULL; | |
579 | } | |
580 | return token; | |
581 | } | |
582 | ||
583 | char * | |
584 | ConfigParser::CfgFile::nextElement(ConfigParser::TokenType &type) | |
585 | { | |
bde7a8ce CT |
586 | const char *pos = parsePos; |
587 | char *token = TokenParse(pos, type); | |
588 | if (!PreviewMode_ || type == FunctionParameters) | |
589 | parsePos = pos; | |
590 | // else next call will read the same token; | |
591 | return token; | |
2eceb328 CT |
592 | } |
593 | ||
594 | ConfigParser::CfgFile::~CfgFile() | |
595 | { | |
596 | if (wordFile) | |
597 | fclose(wordFile); | |
598 | } | |
f53969cc | 599 |