]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/rcpgenerator.cc
Merge pull request #8223 from PowerDNS/omoerbeek-patch-1
[thirdparty/pdns.git] / pdns / rcpgenerator.cc
CommitLineData
4192ca66 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
4192ca66 25#include "rcpgenerator.hh"
a416d398 26#include "ascii.hh"
20133c59 27#include "dnsparser.hh"
cbf0e7f3 28#include "misc.hh"
3897b9e1 29#include "utility.hh"
12b33ac2 30#include <boost/algorithm/string.hpp>
ff7ac440
AT
31#include <boost/algorithm/string/classification.hpp>
32
4192ca66 33#include <iostream>
1c4d88c5 34#include "base32.hh"
8c1c9170 35#include "base64.hh"
d2d6cf61 36#include "namespaces.hh"
4192ca66 37
3716f081 38RecordTextReader::RecordTextReader(const string& str, const DNSName& zone) : d_string(str), d_zone(zone), d_pos(0)
4192ca66 39{
ff7ac440 40 /* remove whitespace */
13653a1a 41 if(!d_string.empty() && ( dns_isspace(*d_string.begin()) || dns_isspace(*d_string.rbegin()) ))
5ead94ed 42 boost::trim_if(d_string, dns_isspace);
ff7ac440 43 d_end = d_string.size();
4192ca66
BH
44}
45
341930bb
BH
46void RecordTextReader::xfr48BitInt(uint64_t &val)
47{
48 xfr64BitInt(val);
335da0ba
AT
49 if (val > 281474976710655LL)
50 throw RecordTextException("Overflow reading 48 bit integer from record content"); // fixme improve
341930bb
BH
51}
52
53void RecordTextReader::xfr64BitInt(uint64_t &val)
54{
55 skipSpaces();
56
57 if(!isdigit(d_string.at(d_pos)))
335da0ba 58 throw RecordTextException("expected digits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
341930bb 59
335da0ba
AT
60 size_t pos;
61 val=std::stoull(d_string.substr(d_pos), &pos);
341930bb 62
335da0ba 63 d_pos += pos;
341930bb
BH
64}
65
66
cbf0e7f3 67void RecordTextReader::xfr32BitInt(uint32_t &val)
4192ca66
BH
68{
69 skipSpaces();
70
71 if(!isdigit(d_string.at(d_pos)))
335da0ba 72 throw RecordTextException("expected digits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
4192ca66 73
335da0ba 74 size_t pos;
aa7b2405 75 val=pdns_stou(d_string.c_str()+d_pos, &pos);
335da0ba
AT
76
77 d_pos += pos;
4192ca66
BH
78}
79
8bf26468
BH
80void RecordTextReader::xfrTime(uint32_t &val)
81{
82 struct tm tm;
83 memset(&tm, 0, sizeof(tm));
84
4a51ff72 85 uint64_t itmp;
7c06b0d7 86 xfr64BitInt(itmp);
4a51ff72 87
ed07d647
SB
88 if (itmp <= (uint32_t)~0) {
89 // formatted as seconds since epoch, not as YYYYMMDDHHmmSS:
90 val = (uint32_t) itmp;
91 return;
92 }
93
4a51ff72
PD
94 ostringstream tmp;
95
96 tmp<<itmp;
8bf26468 97
4a51ff72 98 sscanf(tmp.str().c_str(), "%04d%02d%02d" "%02d%02d%02d",
4957a608
BH
99 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
100 &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
8bf26468
BH
101
102 tm.tm_year-=1900;
12b33ac2 103 tm.tm_mon-=1;
49f72da1 104 val=(uint32_t)Utility::timegm(&tm);
8bf26468
BH
105}
106
cbf0e7f3
BH
107void RecordTextReader::xfrIP(uint32_t &val)
108{
109 skipSpaces();
110
111 if(!isdigit(d_string.at(d_pos)))
335da0ba 112 throw RecordTextException("while parsing IP address, expected digits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
1149fa0b
BH
113
114 uint32_t octet=0;
115 val=0;
116 char count=0;
267951e6
SB
117 bool last_was_digit = false;
118
1149fa0b
BH
119 for(;;) {
120 if(d_string.at(d_pos)=='.') {
267951e6
SB
121 if (!last_was_digit)
122 throw RecordTextException(string("unable to parse IP address, dot without previous digit"));
123 last_was_digit = false;
1149fa0b
BH
124 val<<=8;
125 val+=octet;
126 octet=0;
127 count++;
128 if(count > 3)
267951e6 129 throw RecordTextException(string("unable to parse IP address, too many dots"));
1149fa0b
BH
130 }
131 else if(isdigit(d_string.at(d_pos))) {
267951e6 132 last_was_digit = true;
1149fa0b
BH
133 octet*=10;
134 octet+=d_string.at(d_pos) - '0';
135 if(octet > 255)
4957a608 136 throw RecordTextException("unable to parse IP address");
1149fa0b
BH
137 }
138 else if(dns_isspace(d_string.at(d_pos)))
139 break;
87c837a3
BH
140 else {
141 throw RecordTextException(string("unable to parse IP address, strange character: ")+d_string.at(d_pos));
142 }
1149fa0b
BH
143 d_pos++;
144 if(d_pos == d_string.length())
145 break;
146 }
267951e6
SB
147 if (count != 3)
148 throw RecordTextException(string("unable to parse IP address, not enough dots"));
149 if (!last_was_digit)
150 throw RecordTextException(string("unable to parse IP address, trailing dot"));
151 val<<=8;
152 val+=octet;
1149fa0b 153 val=ntohl(val);
cbf0e7f3
BH
154}
155
156
b9b28916
AT
157void RecordTextReader::xfrIP6(std::string &val)
158{
b9b28916
AT
159 struct in6_addr tmpbuf;
160
161 skipSpaces();
b9b28916
AT
162
163 size_t len;
cf90f8e1 164 // lookup end of value - think of ::ffff encoding too, has dots in it!
8d388c48 165 for(len=0;
cf90f8e1 166 d_pos+len < d_string.length() && (isxdigit(d_string.at(d_pos+len)) || d_string.at(d_pos+len) == ':' || d_string.at(d_pos+len)=='.');
8d388c48 167 len++);
b9b28916 168
8d388c48 169 if(!len)
335da0ba 170 throw RecordTextException("while parsing IPv6 address, expected xdigits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
b9b28916 171
8d388c48 172 // end of value is here, try parse as IPv6
173 string address=d_string.substr(d_pos, len);
174
175 if (inet_pton(AF_INET6, address.c_str(), &tmpbuf) != 1) {
176 throw RecordTextException("while parsing IPv6 address: '" + address + "' is invalid");
b9b28916
AT
177 }
178
179 val = std::string((char*)tmpbuf.s6_addr, 16);
180
181 d_pos += len;
182}
183
f4352636
PD
184void RecordTextReader::xfrCAWithoutPort(uint8_t version, ComboAddress &val)
185{
186 if (version == 4) {
187 uint32_t ip;
188 xfrIP(ip);
189 val = makeComboAddressFromRaw(4, string((const char*) &ip, 4));
190 }
191 else if (version == 6) {
192 string ip;
193 xfrIP6(ip);
194 val = makeComboAddressFromRaw(6, ip);
195 }
196 else throw RecordTextException("invalid address family");
197}
198
199void RecordTextReader::xfrCAPort(ComboAddress &val)
200{
201 uint16_t port;
202 xfr16BitInt(port);
203 val.sin4.sin_port = port;
204}
205
20133c59
BH
206bool RecordTextReader::eof()
207{
208 return d_pos==d_end;
209}
210
cbf0e7f3
BH
211void RecordTextReader::xfr16BitInt(uint16_t &val)
212{
213 uint32_t tmp;
214 xfr32BitInt(tmp);
215 val=tmp;
216 if(val!=tmp)
217 throw RecordTextException("Overflow reading 16 bit integer from record content"); // fixme improve
218}
219
8c1c9170
BH
220void RecordTextReader::xfr8BitInt(uint8_t &val)
221{
222 uint32_t tmp;
223 xfr32BitInt(tmp);
224 val=tmp;
225 if(val!=tmp)
226 throw RecordTextException("Overflow reading 8 bit integer from record content"); // fixme improve
227}
228
7ecd3576 229// this code should leave all the escapes around
f21fc0aa 230void RecordTextReader::xfrName(DNSName& val, bool, bool)
4192ca66
BH
231{
232 skipSpaces();
3716f081 233 DNSName sval;
29a14b24 234
d39704c0 235 const char* strptr=d_string.c_str();
83b77f90 236 string::size_type begin_pos = d_pos;
29a14b24 237 while(d_pos < d_end) {
0a1825d6 238 if(strptr[d_pos]!='\r' && dns_isspace(strptr[d_pos]))
29a14b24 239 break;
7ecd3576 240
4192ca66 241 d_pos++;
29a14b24 242 }
3716f081 243 sval = DNSName(std::string(strptr+begin_pos, strptr+d_pos));
83b77f90 244
4a51ff72
PD
245 if(sval.empty())
246 sval=d_zone;
3716f081
AT
247 else if(!d_zone.empty())
248 sval+=d_zone;
249 val = sval;
4192ca66
BH
250}
251
2fe9d6f7 252static bool isbase64(char c, bool acceptspace)
12b33ac2
BH
253{
254 if(dns_isspace(c))
2fe9d6f7 255 return acceptspace;
12b33ac2
BH
256 if(c >= '0' && c <= '9')
257 return true;
258 if(c >= 'a' && c <= 'z')
259 return true;
260 if(c >= 'A' && c <= 'Z')
261 return true;
262 if(c=='+' || c=='/' || c=='=')
263 return true;
264 return false;
265}
266
2fe9d6f7
AT
267void RecordTextReader::xfrBlobNoSpaces(string& val, int len) {
268 skipSpaces();
269 int pos=(int)d_pos;
270 const char* strptr=d_string.c_str();
271 while(d_pos < d_end && isbase64(strptr[d_pos], false))
272 d_pos++;
273
274 string tmp;
275 tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
276 boost::erase_all(tmp," ");
277 val.clear();
278 B64Decode(tmp, val);
279
280 if (len>-1 && val.size() != static_cast<size_t>(len))
335da0ba 281 throw RecordTextException("Record length "+std::to_string(val.size()) + " does not match expected length '"+std::to_string(len));
2fe9d6f7
AT
282}
283
06ffdc52 284void RecordTextReader::xfrBlob(string& val, int)
8c1c9170
BH
285{
286 skipSpaces();
705f31ae 287 int pos=(int)d_pos;
d39704c0 288 const char* strptr=d_string.c_str();
2fe9d6f7 289 while(d_pos < d_end && isbase64(strptr[d_pos], true))
8c1c9170 290 d_pos++;
12b33ac2 291
20133c59
BH
292 string tmp;
293 tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
12b33ac2 294 boost::erase_all(tmp," ");
20133c59
BH
295 val.clear();
296 B64Decode(tmp, val);
8c1c9170
BH
297}
298
59a0f653
BH
299
300static inline uint8_t hextodec(uint8_t val)
301{
302 if(val >= '0' && val<='9')
303 return val-'0';
304 else if(val >= 'A' && val<='F')
305 return 10+(val-'A');
306 else if(val >= 'a' && val<='f')
307 return 10+(val-'a');
308 else
335da0ba 309 throw RecordTextException("Unknown hexadecimal character '"+std::to_string(val)+"'");
59a0f653
BH
310}
311
312
4e75f84a 313void HEXDecode(const char* begin, const char* end, string& out)
59a0f653 314{
3c873e66 315 if(end - begin == 1 && *begin=='-') {
4e75f84a 316 out.clear();
3c873e66
BH
317 return;
318 }
4e75f84a
BH
319 out.clear();
320 out.reserve((end-begin)/2);
321 uint8_t mode=0, val=0;
322 for(; begin != end; ++begin) {
323 if(!isalnum(*begin))
324 continue;
325 if(mode==0) {
326 val = 16*hextodec(*begin);
327 mode=1;
328 } else {
329 val += hextodec(*begin);
330 out.append(1, (char) val);
331 mode = 0;
332 val = 0;
333 }
59a0f653 334 }
4e75f84a
BH
335 if(mode)
336 out.append(1, (char) val);
337
59a0f653
BH
338}
339
e4090157 340void RecordTextReader::xfrHexBlob(string& val, bool keepReading)
59a0f653
BH
341{
342 skipSpaces();
705f31ae 343 int pos=(int)d_pos;
e4090157 344 while(d_pos < d_end && (keepReading || !dns_isspace(d_string[d_pos])))
59a0f653
BH
345 d_pos++;
346
347 HEXDecode(d_string.c_str()+pos, d_string.c_str() + d_pos, val);
348}
349
8b606f6e
BH
350void RecordTextReader::xfrBase32HexBlob(string& val)
351{
352 skipSpaces();
353 int pos=(int)d_pos;
354 while(d_pos < d_end && !dns_isspace(d_string[d_pos]))
355 d_pos++;
356
357 val=fromBase32Hex(string(d_string.c_str()+pos, d_pos-pos));
358}
359
360
1c4d88c5
BH
361void RecordTextWriter::xfrBase32HexBlob(const string& val)
362{
363 if(!d_string.empty())
364 d_string.append(1,' ');
365
1bad4190 366 d_string.append(toUpper(toBase32Hex(val)));
1c4d88c5
BH
367}
368
369
84e1142d 370void RecordTextReader::xfrText(string& val, bool multi, bool lenField)
4192ca66 371{
4192ca66
BH
372 val.clear();
373 val.reserve(d_end - d_pos);
ef6a78d5
BH
374
375 while(d_pos != d_end) {
376 if(!val.empty())
377 val.append(1, ' ');
378
379 skipSpaces();
2220e01f
BH
380 if(d_string[d_pos]!='"') { // special case 'plenus' - without quotes
381 string::size_type pos = d_pos;
382 while(pos != d_end && isalnum(d_string[pos]))
383 pos++;
384 if(pos == d_end) {
385 val.append(1, '"');
386 val.append(d_string.c_str() + d_pos, d_end - d_pos);
387 val.append(1, '"');
388 d_pos = d_end;
389 break;
390 }
335da0ba 391 throw RecordTextException("Data field in DNS should start with quote (\") at position "+std::to_string(d_pos)+" of '"+d_string+"'");
2220e01f 392 }
ef6a78d5
BH
393 val.append(1, '"');
394 while(++d_pos < d_end && d_string[d_pos]!='"') {
395 if(d_string[d_pos]=='\\' && d_pos+1!=d_end) {
4957a608 396 val.append(1, d_string[d_pos++]);
ef6a78d5
BH
397 }
398 val.append(1, d_string[d_pos]);
4192ca66 399 }
ef6a78d5
BH
400 val.append(1,'"');
401 if(d_pos == d_end)
402 throw RecordTextException("Data field in DNS should end on a quote (\") in '"+d_string+"'");
403 d_pos++;
404 if(!multi)
405 break;
4192ca66 406 }
4192ca66
BH
407}
408
948a927f
PL
409void RecordTextReader::xfrUnquotedText(string& val, bool lenField)
410{
411 val.clear();
412 val.reserve(d_end - d_pos);
413
414 if(!val.empty())
415 val.append(1, ' ');
416
417 skipSpaces();
418 val.append(1, d_string[d_pos]);
419 while(++d_pos < d_end && d_string[d_pos] != ' '){
420 val.append(1, d_string[d_pos]);
421 }
422}
423
20133c59
BH
424void RecordTextReader::xfrType(uint16_t& val)
425{
426 skipSpaces();
705f31ae 427 int pos=(int)d_pos;
ec486449 428 while(d_pos < d_end && !dns_isspace(d_string[d_pos]))
20133c59
BH
429 d_pos++;
430
431 string tmp;
432 tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
433
434 val=DNSRecordContent::TypeToNumber(tmp);
435}
4192ca66 436
8c1c9170 437
4192ca66
BH
438void RecordTextReader::skipSpaces()
439{
d39704c0
BH
440 const char* strptr = d_string.c_str();
441 while(d_pos < d_end && dns_isspace(strptr[d_pos]))
4192ca66 442 d_pos++;
4192ca66
BH
443 if(d_pos == d_end)
444 throw RecordTextException("missing field at the end of record content '"+d_string+"'");
445}
446
447
f21fc0aa 448RecordTextWriter::RecordTextWriter(string& str, bool noDot) : d_string(str)
4192ca66
BH
449{
450 d_string.clear();
f21fc0aa 451 d_nodot=noDot;
4192ca66
BH
452}
453
341930bb 454void RecordTextWriter::xfr48BitInt(const uint64_t& val)
4192ca66
BH
455{
456 if(!d_string.empty())
457 d_string.append(1,' ');
335da0ba 458 d_string+=std::to_string(val);
4192ca66
BH
459}
460
20133c59 461
341930bb
BH
462void RecordTextWriter::xfr32BitInt(const uint32_t& val)
463{
464 if(!d_string.empty())
465 d_string.append(1,' ');
335da0ba 466 d_string+=std::to_string(val);
341930bb 467}
20133c59
BH
468
469void RecordTextWriter::xfrType(const uint16_t& val)
470{
471 if(!d_string.empty())
472 d_string.append(1,' ');
473 d_string+=DNSRecordContent::NumberToType(val);
474}
475
ec486449 476// this function is on the fast path for the pdns_recursor
cbf0e7f3
BH
477void RecordTextWriter::xfrIP(const uint32_t& val)
478{
479 if(!d_string.empty())
480 d_string.append(1,' ');
481
ec486449 482 char tmp[17];
79a9e9ad 483 uint32_t ip=val;
1149fa0b
BH
484 uint8_t vals[4];
485
486 memcpy(&vals[0], &ip, sizeof(ip));
487
488 char *pos=tmp;
489
490 for(int n=0; n < 4; ++n) {
491 if(vals[n]<10) {
492 *(pos++)=vals[n]+'0';
493 } else if(vals[n] < 100) {
494 *(pos++)=(vals[n]/10) +'0';
495 *(pos++)=(vals[n]%10) +'0';
496 } else {
497 *(pos++)=(vals[n]/100) +'0';
498 vals[n]%=100;
499 *(pos++)=(vals[n]/10) +'0';
500 *(pos++)=(vals[n]%10) +'0';
501 }
502 if(n!=3)
503 *(pos++)='.';
504 }
505 *pos=0;
506 d_string.append(tmp, pos);
cbf0e7f3
BH
507}
508
b9b28916
AT
509void RecordTextWriter::xfrIP6(const std::string& val)
510{
511 char tmpbuf[16];
512 char addrbuf[40];
513
514 if(!d_string.empty())
515 d_string.append(1,' ');
516
517 val.copy(tmpbuf,16);
518
519 if (inet_ntop(AF_INET6, tmpbuf, addrbuf, sizeof addrbuf) == NULL)
520 throw RecordTextException("Unable to convert to ipv6 address");
521
522 d_string += std::string(addrbuf);
523}
cbf0e7f3 524
f4352636
PD
525void RecordTextWriter::xfrCAWithoutPort(uint8_t version, ComboAddress &val)
526{
527 string ip = val.toString();
528
529 if(!d_string.empty())
530 d_string.append(1,' ');
531
532 d_string += ip;
533}
534
535void RecordTextWriter::xfrCAPort(ComboAddress &val)
536{
537 xfr16BitInt(val.sin4.sin_port);
538}
539
8bf26468
BH
540void RecordTextWriter::xfrTime(const uint32_t& val)
541{
542 if(!d_string.empty())
543 d_string.append(1,' ');
544
545 struct tm tm;
546 time_t time=val; // Y2038 bug!
c96765da 547 gmtime_r(&time, &tm);
76473b92 548
3cbe2773
CHB
549 static const boost::format fmt("%04d%02d%02d" "%02d%02d%02d");
550 d_string += boost::str(boost::format(fmt) % (tm.tm_year+1900) % (tm.tm_mon+1) % tm.tm_mday % tm.tm_hour % tm.tm_min % tm.tm_sec);
8bf26468
BH
551}
552
553
cbf0e7f3
BH
554void RecordTextWriter::xfr16BitInt(const uint16_t& val)
555{
556 xfr32BitInt(val);
557}
558
8c1c9170
BH
559void RecordTextWriter::xfr8BitInt(const uint8_t& val)
560{
561 xfr32BitInt(val);
562}
563
7ecd3576 564// should not mess with the escapes
f21fc0aa 565void RecordTextWriter::xfrName(const DNSName& val, bool, bool noDot)
4192ca66
BH
566{
567 if(!d_string.empty())
568 d_string.append(1,' ');
7ecd3576 569
f21fc0aa 570 if(d_nodot) {
3b295666 571 d_string+=val.toStringRootDot();
f21fc0aa
PD
572 }
573 else
574 {
575 d_string+=val.toString();
576 }
4192ca66
BH
577}
578
2fe9d6f7
AT
579void RecordTextWriter::xfrBlobNoSpaces(const string& val, int size)
580{
581 xfrBlob(val, size);
582}
583
06ffdc52 584void RecordTextWriter::xfrBlob(const string& val, int)
8c1c9170
BH
585{
586 if(!d_string.empty())
587 d_string.append(1,' ');
588
589 d_string+=Base64Encode(val);
590}
591
e4090157 592void RecordTextWriter::xfrHexBlob(const string& val, bool)
59a0f653
BH
593{
594 if(!d_string.empty())
595 d_string.append(1,' ');
596
3c873e66
BH
597 if(val.empty()) {
598 d_string.append(1,'-');
599 return;
600 }
601
59a0f653
BH
602 string::size_type limit=val.size();
603 char tmp[5];
604 for(string::size_type n = 0; n < limit; ++n) {
9b2244e1 605 snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)val[n]);
59a0f653
BH
606 d_string+=tmp;
607 }
608}
609
84e1142d 610void RecordTextWriter::xfrText(const string& val, bool multi, bool lenField)
4192ca66
BH
611{
612 if(!d_string.empty())
613 d_string.append(1,' ');
4192ca66 614
ef6a78d5 615 d_string.append(val);
4192ca66
BH
616}
617
948a927f
PL
618void RecordTextWriter::xfrUnquotedText(const string& val, bool lenField)
619{
620 if(!d_string.empty())
621 d_string.append(1,' ');
622 d_string.append(val);
623}
4192ca66
BH
624
625#ifdef TESTING
626
627int main(int argc, char**argv)
628try
629{
630 RecordTextReader rtr(argv[1], argv[2]);
631
632 unsigned int order, pref;
633 string flags, services, regexp, replacement;
634 string mx;
635
636 rtr.xfrInt(order);
637 rtr.xfrInt(pref);
cbf0e7f3
BH
638 rtr.xfrText(flags);
639 rtr.xfrText(services);
640 rtr.xfrText(regexp);
ad8fa726 641 rtr.xfrName(replacement);
4192ca66
BH
642
643 cout<<"order: "<<order<<", pref: "<<pref<<"\n";
644 cout<<"flags: \""<<flags<<"\", services: \""<<services<<"\", regexp: \""<<regexp<<"\", replacement: "<<replacement<<"\n";
645
646 string out;
647 RecordTextWriter rtw(out);
648
649 rtw.xfrInt(order);
650 rtw.xfrInt(pref);
cbf0e7f3
BH
651 rtw.xfrText(flags);
652 rtw.xfrText(services);
653 rtw.xfrText(regexp);
ad8fa726 654 rtw.xfrName(replacement);
4192ca66
BH
655
656 cout<<"Regenerated: '"<<out<<"'\n";
657
658}
adc10f99 659catch(std::exception& e)
4192ca66
BH
660{
661 cerr<<"Fatal: "<<e.what()<<endl;
662}
663
664#endif