Kea 2.2.0
log/compiler/message.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <cctype>
10#include <cstddef>
11#include <fstream>
12#include <iostream>
13#include <string>
14#include <vector>
15
16#include <errno.h>
17#include <getopt.h>
18#include <string.h>
19#include <time.h>
20#include <unistd.h>
21
23
24#include <util/filename.h>
25#include <util/strutil.h>
26
27#include <log/log_messages.h>
30#include <log/message_reader.h>
31
32#include <log/logger.h>
33
34#include <boost/foreach.hpp>
35
36using namespace std;
37using namespace isc::log;
38using namespace isc::util;
39
62
66
67void
69 cout << VERSION << "\n";
70}
71
75
76void
78 cout <<
79 "Usage: kea-msg-compiler [-h] [-v] [-d dir] <message-file>\n" <<
80 "\n" <<
81 "-h Print this message and exit\n" <<
82 "-v Print the program version and exit\n" <<
83 "-d <dir> Place output files in given directory\n" <<
84 "\n" <<
85 "<message-file> is the name of the input message file.\n";
86}
87
97
98string
100
101 string name = file.name();
102 string ext = file.extension();
103 string sentinel_text = name + "_" + ext.substr(1);
104 isc::util::str::uppercase(sentinel_text);
105 return (sentinel_text);
106}
107
113
114string
115quoteString(const string& instring) {
116
117 // Create the output string and reserve the space needed to hold the input
118 // string. (Most input strings will not contain quotes, so this single
119 // reservation should be all that is needed.)
120 string outstring;
121 outstring.reserve(instring.size());
122
123 // Iterate through the input string, preceding quotes with a slash.
124 for (size_t i = 0; i < instring.size(); ++i) {
125 if (instring[i] == '"') {
126 outstring += '\\';
127 }
128 outstring += instring[i];
129 }
130
131 return (outstring);
132}
133
142
143vector<string>
145 vector<string> ident;
146
147 for (MessageDictionary::const_iterator i = dictionary.begin();
148 i != dictionary.end(); ++i) {
149 ident.push_back(i->first);
150 }
151 sort(ident.begin(), ident.end());
152
153 return (ident);
154}
155
169
170vector<string>
171splitNamespace(string ns) {
172
173 // Namespaces components are separated by double colon characters -
174 // convert to single colons.
175 size_t dcolon;
176 while ((dcolon = ns.find("::")) != string::npos) {
177 ns.replace(dcolon, 2, ":");
178 }
179
180 // ... and return the vector of namespace components split on the single
181 // colon.
182 return (isc::util::str::tokens(ns, ":"));
183}
184
188void
189writeOpeningNamespace(ostream& output, const vector<string>& ns) {
190 if (!ns.empty()) {
191
192 // Output namespaces in correct order
193 for (vector<string>::size_type i = 0; i < ns.size(); ++i) {
194 output << "namespace " << ns[i] << " {\n";
195 }
196 output << "\n";
197 }
198}
199
203void
204writeClosingNamespace(ostream& output, const vector<string>& ns) {
205 if (!ns.empty()) {
206 for (int i = ns.size() - 1; i >= 0; --i) {
207 output << "} // namespace " << ns[i] << "\n";
208 }
209 output << "\n";
210 }
211}
212
228void
229writeHeaderFile(const string& file,
230 const vector<string>& ns_components,
231 MessageDictionary& dictionary,
232 const char* output_directory) {
233 Filename message_file(file);
234 Filename header_file(Filename(message_file.name()).useAsDefault(".h"));
235 if (output_directory != NULL) {
236 header_file.setDirectory(output_directory);
237 }
238
239 // Text to use as the sentinels.
240 string sentinel_text = sentinel(header_file);
241
242 // zero out the errno to be safe
243 errno = 0;
244
245 // Open the output file for writing
246 ofstream hfile(header_file.fullName().c_str());
247
248 if (hfile.fail()) {
249 isc_throw_4(MessageException, "Failed to open output file",
250 LOG_OPEN_OUTPUT_FAIL, header_file.fullName(),
251 strerror(errno), 0);
252 }
253
254 // Write the header preamble. If there is an error, we'll pick it up
255 // after the last write.
256
257 hfile <<
258 "// File created from " << message_file.fullName() << "\n" <<
259 "\n" <<
260 "#ifndef " << sentinel_text << "\n" <<
261 "#define " << sentinel_text << "\n" <<
262 "\n" <<
263 "#include <log/message_types.h>\n" <<
264 "\n";
265
266 // Write the message identifiers, bounded by a namespace declaration
267 writeOpeningNamespace(hfile, ns_components);
268
269 vector<string> idents = sortedIdentifiers(dictionary);
270 for (vector<string>::const_iterator j = idents.begin();
271 j != idents.end(); ++j) {
272 hfile << "extern const isc::log::MessageID " << *j << ";\n";
273 }
274 hfile << "\n";
275
276 writeClosingNamespace(hfile, ns_components);
277
278 // ... and finally the postamble
279 hfile << "#endif // " << sentinel_text << "\n";
280
281 // Report errors (if any) and exit
282 if (hfile.fail()) {
283 isc_throw_4(MessageException, "Error writing to output file",
284 LOG_WRITE_ERROR, header_file.fullName(), strerror(errno),
285 0);
286 }
287
288 hfile.close();
289}
290
294char
296 return (isalnum(c) ? c : '_');
297}
298
332void
333writeProgramFile(const string& file,
334 const vector<string>& ns_components,
335 MessageDictionary& dictionary,
336 const char* output_directory) {
337 Filename message_file(file);
338 Filename program_file(Filename(message_file.name()).useAsDefault(".cc"));
339 if (output_directory) {
340 program_file.setDirectory(output_directory);
341 }
342
343 // zero out the errno to be safe
344 errno = 0;
345
346 // Open the output file for writing
347 ofstream ccfile(program_file.fullName().c_str());
348
349 if (ccfile.fail()) {
350 isc_throw_4(MessageException, "Error opening output file",
351 LOG_OPEN_OUTPUT_FAIL, program_file.fullName(),
352 strerror(errno), 0);
353 }
354
355 // Write the preamble. If there is an error, we'll pick it up after
356 // the last write.
357
358 ccfile <<
359 "// File created from " << message_file.fullName() << "\n" <<
360 "\n" <<
361 "#include <cstddef>\n" <<
362 "#include <log/message_types.h>\n" <<
363 "#include <log/message_initializer.h>\n" <<
364 "\n";
365
366 // Declare the message symbols themselves.
367
368 writeOpeningNamespace(ccfile, ns_components);
369
370 vector<string> idents = sortedIdentifiers(dictionary);
371 for (vector<string>::const_iterator j = idents.begin();
372 j != idents.end(); ++j) {
373 ccfile << "extern const isc::log::MessageID " << *j <<
374 " = \"" << *j << "\";\n";
375 }
376 ccfile << "\n";
377
378 writeClosingNamespace(ccfile, ns_components);
379
380 // Now the code for the message initialization.
381
382 ccfile <<
383 "namespace {\n" <<
384 "\n" <<
385 "const char* values[] = {\n";
386
387 // Output the identifiers and the associated text.
388 idents = sortedIdentifiers(dictionary);
389 for (vector<string>::const_iterator i = idents.begin();
390 i != idents.end(); ++i) {
391 ccfile << " \"" << *i << "\", \"" <<
392 quoteString(dictionary.getText(*i)) << "\",\n";
393 }
394
395 // ... and the postamble
396 ccfile <<
397 " NULL\n" <<
398 "};\n" <<
399 "\n" <<
400 "const isc::log::MessageInitializer initializer(values);\n" <<
401 "\n" <<
402 "} // Anonymous namespace\n" <<
403 "\n";
404
405 // Report errors (if any) and exit
406 if (ccfile.fail()) {
407 isc_throw_4(MessageException, "Error writing to output file",
408 LOG_WRITE_ERROR, program_file.fullName(), strerror(errno),
409 0);
410 }
411
412 ccfile.close();
413}
414
421void
423
424 // Get the duplicates (the overflow) and, if present, sort them into some
425 // order and remove those which occur more than once (which mean that they
426 // occur more than twice in the input file).
428 if (!duplicates.empty()) {
429 cout << "Error: the following duplicate IDs were found:\n";
430
431 sort(duplicates.begin(), duplicates.end());
432 MessageReader::MessageIDCollection::iterator new_end =
433 unique(duplicates.begin(), duplicates.end());
434 for (MessageReader::MessageIDCollection::iterator i = duplicates.begin();
435 i != new_end; ++i) {
436 cout << " " << *i << "\n";
437 }
438 exit(1);
439 }
440}
441
446int
447main(int argc, char* argv[]) {
448
449 const char* soptions = "hvpd:"; // Short options
450
451 optind = 1; // Ensure we start a new scan
452 int opt; // Value of the option
453
454 const char *output_directory = NULL;
455
456 while ((opt = getopt(argc, argv, soptions)) != -1) {
457 switch (opt) {
458 case 'd':
459 output_directory = optarg;
460 break;
461
462 case 'h':
463 usage();
464 return (0);
465
466 case 'v':
467 version();
468 return (0);
469
470 default:
471 // A message will have already been output about the error.
472 return (1);
473 }
474 }
475
476 // Do we have the message file?
477 if (optind < (argc - 1)) {
478 cout << "Error: excess arguments in command line\n";
479 usage();
480 return (1);
481 } else if (optind >= argc) {
482 cout << "Error: missing message file\n";
483 usage();
484 return (1);
485 }
486 string message_file = argv[optind];
487
488 try {
489 // Have identified the file, so process it. First create a local
490 // dictionary into which the data will be put.
491 MessageDictionary dictionary;
492
493 // Read the data into it.
494 MessageReader reader(&dictionary);
495 reader.readFile(message_file);
496
497 // Error (and quit) if there are of any duplicates encountered.
498 errorDuplicates(reader);
499
500 // Get the namespace into which the message definitions will be put and
501 // split it into components.
502 vector<string> ns_components =
504
505 // Write the header file.
506 writeHeaderFile(message_file, ns_components, dictionary,
507 output_directory);
508
509 // Write the file that defines the message symbols and text
510 writeProgramFile(message_file, ns_components, dictionary,
511 output_directory);
512
513 } catch (const MessageException& e) {
514 // Create an error message from the ID and the text
515 const MessageDictionaryPtr& global = MessageDictionary::globalDictionary();
516 string text = e.id();
517 text += ", ";
518 text += global->getText(e.id());
519 // Format with arguments
520 vector<string> args(e.arguments());
521 for (size_t i(0); i < args.size(); ++ i) {
522 try {
523 replacePlaceholder(text, args[i], i + 1);
524 } catch (...) {
525 // Error in error handling: nothing right to do...
526 }
527 }
528
529 cerr << text << "\n";
530
531 return (1);
532 } catch (const std::exception& ex) {
533 cerr << "Fatal error: " << ex.what() << "\n";
534
535 return (1);
536 } catch (...) {
537 cerr << "Fatal error\n";
538
539 return (1);
540 }
541
542 return (0);
543}
virtual const std::string & getText(const MessageID &ident) const
Get Message Text.
const_iterator end() const
Return end() iterator of internal map.
const_iterator begin() const
Return begin() iterator of internal map.
Dictionary::const_iterator const_iterator
std::vector< std::string > arguments() const
Return Arguments.
MessageID id() const
Return Message ID.
Read Message File.
std::vector< std::string > MessageIDCollection
Visible collection types.
MessageIDCollection getNotAdded() const
Get Not-Added List.
virtual std::string getNamespace() const
Get Namespace.
virtual void readFile(const std::string &file, Mode mode=ADD)
Read File.
Class to Manipulate Filenames.
Definition: filename.h:50
std::string extension() const
Definition: filename.h:93
std::string name() const
Definition: filename.h:88
std::string fullName() const
Definition: filename.h:71
void setDirectory(const std::string &new_directory)
Set directory for the file.
Definition: filename.cc:129
std::string useAsDefault(const std::string &name) const
Use as Default and Substitute into String.
Definition: filename.cc:105
#define isc_throw_4(type, stream, param1, param2, param3, param4)
Similar as isc_throw, but allows the exception to have four additional parameters (the stream/text go...
void writeHeaderFile(const string &file, const vector< string > &ns_components, MessageDictionary &dictionary, const char *output_directory)
Write Header File.
int main(int argc, char *argv[])
Main Program.
char replaceNonAlphaNum(char c)
Convert Non Alpha-Numeric Characters to Underscores.
vector< string > splitNamespace(string ns)
Split Namespace.
void usage()
Print Usage.
void writeClosingNamespace(ostream &output, const vector< string > &ns)
Write Closing Namespace(s)
void writeProgramFile(const string &file, const vector< string > &ns_components, MessageDictionary &dictionary, const char *output_directory)
Write Program File.
void writeOpeningNamespace(ostream &output, const vector< string > &ns)
Write Opening Namespace(s)
void version()
Print Version.
string quoteString(const string &instring)
Quote String.
vector< string > sortedIdentifiers(MessageDictionary &dictionary)
Sorted Identifiers.
string sentinel(Filename &file)
Create Header Sentinel.
void errorDuplicates(MessageReader &reader)
Error and exit if there are duplicate entries.
const isc::log::MessageID LOG_OPEN_OUTPUT_FAIL
Definition: log_messages.h:24
void replacePlaceholder(std::string &message, const string &arg, const unsigned placeholder)
The internal replacement routine.
boost::shared_ptr< MessageDictionary > MessageDictionaryPtr
Shared pointer to the MessageDictionary.
const isc::log::MessageID LOG_WRITE_ERROR
Definition: log_messages.h:30
vector< string > tokens(const std::string &text, const std::string &delim, bool escape)
Split String into Tokens.
Definition: strutil.cc:77
void uppercase(std::string &text)
Uppercase String.
Definition: strutil.h:127
Definition: edns.h:19