]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/zoneparser-tng.cc
ZoneParserTNG: Cap the width of $GENERATE template to the output size
[thirdparty/pdns.git] / pdns / zoneparser-tng.cc
CommitLineData
f814d7c8 1/*
6edbf68a
PL
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
a416d398 25#include "ascii.hh"
f814d7c8
BH
26#include "dnsparser.hh"
27#include "sstuff.hh"
28#include "misc.hh"
29#include "dnswriter.hh"
30#include "dnsrecords.hh"
f814d7c8
BH
31#include "misc.hh"
32#include <fstream>
33#include "dns.hh"
34#include "zoneparser-tng.hh"
125e4840
BH
35#include <deque>
36#include <boost/algorithm/string.hpp>
e0e48a7a 37#include <system_error>
1817fef6 38#include <cinttypes>
f814d7c8 39
3108d502 40static string g_INstr("IN");
41
1290c1d2
RP
42ZoneParserTNG::ZoneParserTNG(const string& fname, DNSName zname, string reldir, bool upgradeContent):
43 d_reldir(std::move(reldir)), d_zonename(std::move(zname)), d_defaultttl(3600),
b4db4fe4
CH
44 d_templatecounter(0), d_templatestop(0), d_templatestep(0),
45 d_havedollarttl(false), d_fromfile(true), d_upgradeContent(upgradeContent)
46{
bf503cc0
BH
47 stackFile(fname);
48}
49
1290c1d2
RP
50ZoneParserTNG::ZoneParserTNG(const vector<string>& zonedata, DNSName zname, bool upgradeContent):
51 d_zonename(std::move(zname)), d_zonedata(zonedata), d_defaultttl(3600),
8a70e507 52 d_templatecounter(0), d_templatestop(0), d_templatestep(0),
b4db4fe4 53 d_havedollarttl(false), d_fromfile(false), d_upgradeContent(upgradeContent)
0f0e73fe 54{
0f0e73fe 55 d_zonedataline = d_zonedata.begin();
0f0e73fe
MS
56}
57
bf503cc0
BH
58void ZoneParserTNG::stackFile(const std::string& fname)
59{
60 FILE *fp=fopen(fname.c_str(), "r");
e0e48a7a
PL
61 if(!fp) {
62 std::error_code ec (errno,std::generic_category());
63 throw std::system_error(ec, "Unable to open file '"+fname+"': "+stringerror());
64 }
cfe397d5
BH
65
66 filestate fs(fp, fname);
67 d_filestates.push(fs);
0f0e73fe 68 d_fromfile = true;
f814d7c8
BH
69}
70
71ZoneParserTNG::~ZoneParserTNG()
72{
cfe397d5
BH
73 while(!d_filestates.empty()) {
74 fclose(d_filestates.top().d_fp);
75 d_filestates.pop();
bf503cc0 76 }
f814d7c8
BH
77}
78
125e4840
BH
79static string makeString(const string& line, const pair<string::size_type, string::size_type>& range)
80{
81 return string(line.c_str() + range.first, range.second - range.first);
82}
83
cfe397d5
BH
84static bool isTimeSpec(const string& nextpart)
85{
86 if(nextpart.empty())
87 return false;
88 for(string::const_iterator iter = nextpart.begin(); iter != nextpart.end(); ++iter) {
89 if(isdigit(*iter))
90 continue;
91 if(iter+1 != nextpart.end())
92 return false;
93 char c=tolower(*iter);
2326ec3f 94 return (c=='s' || c=='m' || c=='h' || c=='d' || c=='w' || c=='y');
cfe397d5
BH
95 }
96 return true;
97}
98
99
100unsigned int ZoneParserTNG::makeTTLFromZone(const string& str)
125e4840
BH
101{
102 if(str.empty())
103 return 0;
104
97ce13be
RG
105 unsigned int val;
106 try {
107 val=pdns_stou(str);
108 }
109 catch (const std::out_of_range& oor) {
110 throw PDNSException("Unable to parse time specification '"+str+"' "+getLineOfFile());
111 }
112
3108d502 113 char lc=dns_tolower(str[str.length()-1]);
125e4840
BH
114 if(!isdigit(lc))
115 switch(lc) {
3108d502 116 case 's':
2326ec3f 117 break;
3108d502 118 case 'm':
38e655b6
BH
119 val*=60; // minutes, not months!
120 break;
3108d502 121 case 'h':
125e4840
BH
122 val*=3600;
123 break;
3108d502 124 case 'd':
125e4840
BH
125 val*=3600*24;
126 break;
3108d502 127 case 'w':
125e4840
BH
128 val*=3600*24*7;
129 break;
3108d502 130 case 'y': // ? :-)
125e4840
BH
131 val*=3600*24*365;
132 break;
cfe397d5 133
125e4840 134 default:
3fed7dbd 135 throw PDNSException("Unable to parse time specification '"+str+"' "+getLineOfFile());
125e4840
BH
136 }
137 return val;
138}
139
2e83ba09
BH
140bool ZoneParserTNG::getTemplateLine()
141{
b8c3ea84 142 if(d_templateparts.empty() || d_templatecounter > d_templatestop) // no template, or done with
2e83ba09
BH
143 return false;
144
145 string retline;
146 for(parts_t::const_iterator iter = d_templateparts.begin() ; iter != d_templateparts.end(); ++iter) {
147 if(iter != d_templateparts.begin())
148 retline+=" ";
149
150 string part=makeString(d_templateline, *iter);
151
152 /* a part can contain a 'naked' $, an escaped $ (\$), or ${offset,width,radix}, with width defaulting to 0,
1817fef6 153 and radix being 'd', 'o', 'x' or 'X', defaulting to 'd' (so ${offset} is valid).
2e83ba09 154
1817fef6 155 The width is zero-padded, so if the counter is at 1, the offset is 15, width is 3, and the radix is 'x',
2e83ba09
BH
156 output will be '010', from the input of ${15,3,x}
157 */
158
159 string outpart;
160 outpart.reserve(part.size()+5);
161 bool inescape=false;
162
163 for(string::size_type pos = 0; pos < part.size() ; ++pos) {
164 char c=part[pos];
165 if(inescape) {
4957a608
BH
166 outpart.append(1, c);
167 inescape=false;
168 continue;
2e83ba09 169 }
4957a608 170
2e83ba09 171 if(part[pos]=='\\') {
4957a608
BH
172 inescape=true;
173 continue;
2e83ba09
BH
174 }
175 if(c=='$') {
4957a608 176 if(pos + 1 == part.size() || part[pos+1]!='{') { // a trailing $, or not followed by {
335da0ba 177 outpart.append(std::to_string(d_templatecounter));
4957a608
BH
178 continue;
179 }
180
181 // need to deal with { case
182
183 pos+=2;
184 string::size_type startPos=pos;
185 for(; pos < part.size() && part[pos]!='}' ; ++pos)
186 ;
187
188 if(pos == part.size()) // partial spec
189 break;
190
191 // we are on the '}'
192
193 string spec(part.c_str() + startPos, part.c_str() + pos);
194 int offset=0, width=0;
195 char radix='d';
1817fef6
RG
196 // parse format specifier
197 int extracted = sscanf(spec.c_str(), "%d,%d,%c", &offset, &width, &radix);
198 if (extracted < 1) {
199 throw PDNSException("Unable to parse offset, width and radix for $GENERATE's lhs from '"+spec+"' "+getLineOfFile());
200 }
2651d2f1
RG
201 if (width < 0) {
202 throw PDNSException("Invalid width ("+std::to_string(width)+") for $GENERATE's lhs from '"+spec+"' "+getLineOfFile());
203 }
204 /* a width larger than the output buffer does not make any sense */
205 width = std::min(width, 80);
4957a608 206
4957a608 207 char tmp[80];
4338e69f
OM
208 switch (radix) {
209 case 'o':
210 snprintf(tmp, sizeof(tmp), "%0*o", width, d_templatecounter + offset);
211 break;
212 case 'x':
213 snprintf(tmp, sizeof(tmp), "%0*x", width, d_templatecounter + offset);
214 break;
215 case 'X':
216 snprintf(tmp, sizeof(tmp), "%0*X", width, d_templatecounter + offset);
217 break;
218 case 'd':
219 default:
220 snprintf(tmp, sizeof(tmp), "%0*d", width, d_templatecounter + offset);
221 break;
222 }
4957a608 223 outpart+=tmp;
2e83ba09
BH
224 }
225 else
4957a608 226 outpart.append(1, c);
2e83ba09
BH
227 }
228 retline+=outpart;
229 }
230 d_templatecounter+=d_templatestep;
231
232 d_line = retline;
233 return true;
234}
235
050e6877 236static void chopComment(string& line)
a09683af 237{
3108d502 238 if(line.find(';')==string::npos)
239 return;
a09683af
BH
240 string::size_type pos, len = line.length();
241 bool inQuote=false;
242 for(pos = 0 ; pos < len; ++pos) {
243 if(line[pos]=='\\')
244 pos++;
245 else if(line[pos]=='"')
246 inQuote=!inQuote;
247 else if(line[pos]==';' && !inQuote)
248 break;
249 }
250 if(pos != len)
251 line.resize(pos);
252}
253
050e6877 254static bool findAndElide(string& line, char c)
9f0076d7
BH
255{
256 string::size_type pos, len = line.length();
257 bool inQuote=false;
258 for(pos = 0 ; pos < len; ++pos) {
259 if(line[pos]=='\\')
260 pos++;
261 else if(line[pos]=='"')
262 inQuote=!inQuote;
263 else if(line[pos]==c && !inQuote)
264 break;
265 }
266 if(pos != len) {
267 line.erase(pos, 1);
268 return true;
269 }
270 return false;
271}
272
d16a2ccf
PL
273DNSName ZoneParserTNG::getZoneName()
274{
275 return d_zonename;
276}
277
d27ea394
BH
278string ZoneParserTNG::getLineOfFile()
279{
0f0e73fe 280 if (d_zonedata.size() > 0)
335da0ba 281 return "on line "+std::to_string(std::distance(d_zonedata.begin(), d_zonedataline))+" of given string";
0f0e73fe 282
a1239f65
RG
283 if (d_filestates.empty())
284 return "";
285
335da0ba 286 return "on line "+std::to_string(d_filestates.top().d_lineno)+" of file '"+d_filestates.top().d_filename+"'";
d27ea394 287}
cfe397d5 288
c91effc8 289pair<string,int> ZoneParserTNG::getLineNumAndFile()
290{
9923fc22
PD
291 if (d_filestates.empty())
292 return {"", 0};
293 else
294 return {d_filestates.top().d_filename, d_filestates.top().d_lineno};
c91effc8 295}
296
4950b140 297bool ZoneParserTNG::get(DNSResourceRecord& rr, std::string* comment)
f814d7c8
BH
298{
299 retry:;
2e83ba09 300 if(!getTemplateLine() && !getLine())
f814d7c8 301 return false;
125e4840 302
dc593046 303 boost::trim_right_if(d_line, boost::is_any_of(" \t\r\n\x1a"));
a5a1f447 304 if(comment)
305 comment->clear();
306 if(comment && d_line.find(';') != string::npos)
307 *comment = d_line.substr(d_line.find(';'));
125e4840 308
9bbcf03a
RG
309 d_parts.clear();
310 vstringtok(d_parts, d_line);
311
312 if(d_parts.empty())
125e4840
BH
313 goto retry;
314
9bbcf03a 315 if(d_parts[0].first != d_parts[0].second && d_line[d_parts[0].first]==';') // line consisting of nothing but comments
4d2c97aa
BH
316 goto retry;
317
125e4840 318 if(d_line[0]=='$') {
9bbcf03a
RG
319 string command=makeString(d_line, d_parts[0]);
320 if(pdns_iequals(command,"$TTL") && d_parts.size() > 1) {
dc593046 321 d_defaultttl=makeTTLFromZone(trim_right_copy_if(makeString(d_line, d_parts[1]), boost::is_any_of(";")));
df1d406a
BH
322 d_havedollarttl=true;
323 }
9bbcf03a
RG
324 else if(pdns_iequals(command,"$INCLUDE") && d_parts.size() > 1 && d_fromfile) {
325 string fname=unquotify(makeString(d_line, d_parts[1]));
da042e6e 326 if(!fname.empty() && fname[0]!='/' && !d_reldir.empty())
4957a608 327 fname=d_reldir+"/"+fname;
da042e6e 328 stackFile(fname);
bf503cc0 329 }
9bbcf03a
RG
330 else if(pdns_iequals(command, "$ORIGIN") && d_parts.size() > 1) {
331 d_zonename = DNSName(makeString(d_line, d_parts[1]));
2e83ba09 332 }
9bbcf03a 333 else if(pdns_iequals(command, "$GENERATE") && d_parts.size() > 2) {
91a862bf
RG
334 if (!d_generateEnabled) {
335 throw exception("$GENERATE is not allowed in this zone");
336 }
bf503cc0 337 // $GENERATE 1-127 $ CNAME $.0
1817fef6
RG
338 // The range part can be one of two forms: start-stop or start-stop/step. If the first
339 // form is used, then step is set to 1. start, stop and step must be positive
340 // integers between 0 and (2^31)-1. start must not be larger than stop.
9bbcf03a 341 string range=makeString(d_line, d_parts[1]);
2e83ba09
BH
342 d_templatestep=1;
343 d_templatestop=0;
1817fef6
RG
344 int extracted = sscanf(range.c_str(),"%" SCNu32 "-%" SCNu32 "/%" SCNu32, &d_templatecounter, &d_templatestop, &d_templatestep);
345 if (extracted == 2) {
346 d_templatestep=1;
347 }
348 else if (extracted != 3) {
349 throw exception("Invalid range from $GENERATE parameters '" + range + "'");
350 }
775a673a
OM
351 if (d_templatestep < 1 ||
352 d_templatestop < d_templatecounter) {
b4ab09cd 353 throw exception("Invalid $GENERATE parameters");
775a673a 354 }
ba3d53d1
RG
355 if (d_maxGenerateSteps != 0) {
356 size_t numberOfSteps = (d_templatestop - d_templatecounter) / d_templatestep;
357 if (numberOfSteps > d_maxGenerateSteps) {
358 throw exception("The number of $GENERATE steps (" + std::to_string(numberOfSteps) + ") is too high, the maximum is set to " + std::to_string(d_maxGenerateSteps));
359 }
360 }
2e83ba09 361 d_templateline=d_line;
9bbcf03a
RG
362 d_parts.pop_front();
363 d_parts.pop_front();
2e83ba09 364
9bbcf03a 365 d_templateparts=d_parts;
2e83ba09 366 goto retry;
bf503cc0 367 }
125e4840 368 else
d27ea394 369 throw exception("Can't parse zone line '"+d_line+"' "+getLineOfFile());
f814d7c8 370 goto retry;
f814d7c8 371 }
125e4840 372
e720f311 373 bool prevqname=false;
9bbcf03a 374 string qname = makeString(d_line, d_parts[0]); // Don't use DNSName here!
3108d502 375 if(dns_isspace(d_line[0])) {
125e4840 376 rr.qname=d_prevqname;
e720f311
KM
377 prevqname=true;
378 }else {
8171ab83 379 rr.qname=DNSName(qname);
9bbcf03a 380 d_parts.pop_front();
e720f311 381 if(qname.empty() || qname[0]==';')
125e4840
BH
382 goto retry;
383 }
d66269ef 384 if(qname=="@")
125e4840 385 rr.qname=d_zonename;
e720f311 386 else if(!prevqname && !isCanonical(qname))
675fa24c 387 rr.qname += d_zonename;
125e4840
BH
388 d_prevqname=rr.qname;
389
9bbcf03a 390 if(d_parts.empty())
d27ea394 391 throw exception("Line with too little parts "+getLineOfFile());
125e4840 392
125e4840 393 string nextpart;
f814d7c8 394
125e4840 395 rr.ttl=d_defaultttl;
b4db4fe4
CH
396 bool haveTTL{false}, haveQTYPE{false};
397 string qtypeString;
125e4840
BH
398 pair<string::size_type, string::size_type> range;
399
9bbcf03a
RG
400 while(!d_parts.empty()) {
401 range=d_parts.front();
402 d_parts.pop_front();
125e4840
BH
403 nextpart=makeString(d_line, range);
404 if(nextpart.empty())
405 break;
406
a5a1f447 407 if(nextpart.find(';')!=string::npos) {
125e4840 408 break;
a5a1f447 409 }
125e4840
BH
410
411 // cout<<"Next part: '"<<nextpart<<"'"<<endl;
3108d502 412
413 if(pdns_iequals(nextpart, g_INstr)) {
125e4840
BH
414 // cout<<"Ignoring 'IN'\n";
415 continue;
416 }
cfe397d5 417 if(!haveTTL && !haveQTYPE && isTimeSpec(nextpart)) {
125e4840 418 rr.ttl=makeTTLFromZone(nextpart);
7fd6c67e 419 if(!d_havedollarttl)
420 d_defaultttl = rr.ttl;
125e4840
BH
421 haveTTL=true;
422 // cout<<"ttl is probably: "<<rr.ttl<<endl;
423 continue;
424 }
425 if(haveQTYPE)
426 break;
427
428 try {
b4db4fe4 429 rr.qtype = DNSRecordContent::TypeToNumber(nextpart);
125e4840 430 // cout<<"Got qtype ("<<rr.qtype.getCode()<<")\n";
b4db4fe4
CH
431 qtypeString = nextpart;
432 haveQTYPE = true;
125e4840
BH
433 continue;
434 }
435 catch(...) {
d27ea394 436 throw runtime_error("Parsing zone content "+getLineOfFile()+
232f0877
CH
437 ": '"+nextpart+
438 "' doesn't look like a qtype, stopping loop");
125e4840
BH
439 }
440 }
441 if(!haveQTYPE)
d27ea394 442 throw exception("Malformed line "+getLineOfFile()+": '"+d_line+"'");
125e4840 443
3108d502 444 // rr.content=d_line.substr(range.first);
445 rr.content.assign(d_line, range.first, string::npos);
a09683af 446 chopComment(rr.content);
dc593046 447 trim_if(rr.content, boost::is_any_of(" \r\n\t\x1a"));
849fde0b 448
3108d502 449 if(rr.content.size()==1 && rr.content[0]=='@')
90c3521b 450 rr.content=d_zonename.toString();
b33702d5 451
9f0076d7
BH
452 if(findAndElide(rr.content, '(')) { // have found a ( and elided it
453 if(!findAndElide(rr.content, ')')) {
454 while(getLine()) {
dc593046 455 boost::trim_right(d_line);
4957a608 456 chopComment(d_line);
dc593046 457 boost::trim(d_line);
4957a608
BH
458
459 bool ended = findAndElide(d_line, ')');
460 rr.content+=" "+d_line;
461 if(ended)
462 break;
125e4840 463 }
125e4840
BH
464 }
465 }
dc593046 466 boost::trim_if(rr.content, boost::is_any_of(" \r\n\t\x1a"));
9f0076d7 467
b4db4fe4
CH
468 if (d_upgradeContent && DNSRecordContent::isUnknownType(qtypeString)) {
469 rr.content = DNSRecordContent::upgradeContent(rr.qname, rr.qtype, rr.content);
470 }
471
43f40013 472 vector<string> recparts;
125e4840
BH
473 switch(rr.qtype.getCode()) {
474 case QType::MX:
43f40013
BH
475 stringtok(recparts, rr.content);
476 if(recparts.size()==2) {
1293f91e
PL
477 if (recparts[1]!=".") {
478 try {
479 recparts[1] = toCanonic(d_zonename, recparts[1]).toStringRootDot();
480 } catch (std::exception &e) {
d5fcd583 481 throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what());
1293f91e
PL
482 }
483 }
43f40013
BH
484 rr.content=recparts[0]+" "+recparts[1];
485 }
486 break;
25e0cd7f 487
6348d406
PD
488 case QType::RP:
489 stringtok(recparts, rr.content);
490 if(recparts.size()==2) {
0c0e717c
PD
491 recparts[0] = toCanonic(d_zonename, recparts[0]).toStringRootDot();
492 recparts[1] = toCanonic(d_zonename, recparts[1]).toStringRootDot();
6348d406
PD
493 rr.content=recparts[0]+" "+recparts[1];
494 }
495 break;
496
25e0cd7f
BH
497 case QType::SRV:
498 stringtok(recparts, rr.content);
499 if(recparts.size()==4) {
1293f91e
PL
500 if(recparts[3]!=".") {
501 try {
502 recparts[3] = toCanonic(d_zonename, recparts[3]).toStringRootDot();
503 } catch (std::exception &e) {
d5fcd583 504 throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what());
1293f91e
PL
505 }
506 }
25e0cd7f
BH
507 rr.content=recparts[0]+" "+recparts[1]+" "+recparts[2]+" "+recparts[3];
508 }
509 break;
510
43f40013 511
125e4840
BH
512 case QType::NS:
513 case QType::CNAME:
8dee0750 514 case QType::DNAME:
125e4840 515 case QType::PTR:
1293f91e
PL
516 try {
517 rr.content = toCanonic(d_zonename, rr.content).toStringRootDot();
518 } catch (std::exception &e) {
d5fcd583 519 throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what());
1293f91e 520 }
125e4840 521 break;
319d6e47 522 case QType::AFSDB:
9f067a15
JJ
523 stringtok(recparts, rr.content);
524 if(recparts.size() == 2) {
525 try {
319d6e47 526 recparts[1]=toCanonic(d_zonename, recparts[1]).toStringRootDot();
9f067a15 527 } catch (std::exception &e) {
d5fcd583 528 throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what());
319d6e47 529 }
9f067a15 530 } else {
86f1af1c 531 throw PDNSException("AFSDB record for "+rr.qname.toLogString()+" invalid");
9f067a15
JJ
532 }
533 rr.content.clear();
534 for(string::size_type n = 0; n < recparts.size(); ++n) {
535 if(n)
536 rr.content.append(1,' ');
125e4840 537
9f067a15 538 rr.content+=recparts[n];
319d6e47
JJ
539 }
540 break;
125e4840 541 case QType::SOA:
43f40013 542 stringtok(recparts, rr.content);
0c306c7c 543 if(recparts.size() > 7)
86f1af1c 544 throw PDNSException("SOA record contents for "+rr.qname.toLogString()+" contains too many parts");
43f40013 545 if(recparts.size() > 1) {
562c0b13
PL
546 try {
547 recparts[0]=toCanonic(d_zonename, recparts[0]).toStringRootDot();
548 recparts[1]=toCanonic(d_zonename, recparts[1]).toStringRootDot();
1293f91e 549 } catch (std::exception &e) {
d5fcd583 550 throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.toString() + "': " + e.what());
562c0b13 551 }
125e4840
BH
552 }
553 rr.content.clear();
43f40013 554 for(string::size_type n = 0; n < recparts.size(); ++n) {
125e4840 555 if(n)
4957a608 556 rr.content.append(1,' ');
c9f935fa 557
82cc6877 558 if(n > 1)
335da0ba 559 rr.content+=std::to_string(makeTTLFromZone(recparts[n]));
82cc6877 560 else
43f40013 561 rr.content+=recparts[n];
125e4840 562 }
38e655b6 563 break;
125e4840
BH
564 default:;
565 }
f814d7c8
BH
566 return true;
567}
568
834942f1 569
f814d7c8
BH
570bool ZoneParserTNG::getLine()
571{
0f0e73fe
MS
572 if (d_zonedata.size() > 0) {
573 if (d_zonedataline != d_zonedata.end()) {
574 d_line = *d_zonedataline;
cb6655e7 575 ++d_zonedataline;
0f0e73fe
MS
576 return true;
577 }
578 return false;
579 }
cfe397d5 580 while(!d_filestates.empty()) {
834942f1 581 if(stringfgets(d_filestates.top().d_fp, d_line)) {
cfe397d5 582 d_filestates.top().d_lineno++;
bf503cc0
BH
583 return true;
584 }
cfe397d5
BH
585 fclose(d_filestates.top().d_fp);
586 d_filestates.pop();
f814d7c8
BH
587 }
588 return false;
589}