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