Kea 1.5.0
log/compiler/message.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2016 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
63
67
68void
70 cout << VERSION << "\n";
71}
72
76
77void
79 cout <<
80 "Usage: kea-msg-compiler [-h] [-v] [-d dir] <message-file>\n" <<
81 "\n" <<
82 "-h Print this message and exit\n" <<
83 "-v Print the program version and exit\n" <<
84 "-d <dir> Place output files in given directory\n" <<
85 "\n" <<
86 "<message-file> is the name of the input message file.\n";
87}
88
89
95
96string
98
99 // Get a text representation of the current time.
100 time_t curtime;
101 time(&curtime);
102 struct tm* timeinfo;
103 timeinfo = localtime(&curtime);
104
105 char buffer[80];
106 strftime(buffer, 80, "%a %b %d %Y %H:%M", timeinfo);
107
108 return (std::string(buffer));
109}
110
111
121
122string
124
125 string name = file.name();
126 string ext = file.extension();
127 string sentinel_text = name + "_" + ext.substr(1);
128 isc::util::str::uppercase(sentinel_text);
129 return (sentinel_text);
130}
131
132
138
139string
140quoteString(const string& instring) {
141
142 // Create the output string and reserve the space needed to hold the input
143 // string. (Most input strings will not contain quotes, so this single
144 // reservation should be all that is needed.)
145 string outstring;
146 outstring.reserve(instring.size());
147
148 // Iterate through the input string, preceding quotes with a slash.
149 for (size_t i = 0; i < instring.size(); ++i) {
150 if (instring[i] == '"') {
151 outstring += '\\';
152 }
153 outstring += instring[i];
154 }
155
156 return (outstring);
157}
158
159
168
169vector<string>
171 vector<string> ident;
172
173 for (MessageDictionary::const_iterator i = dictionary.begin();
174 i != dictionary.end(); ++i) {
175 ident.push_back(i->first);
176 }
177 sort(ident.begin(), ident.end());
178
179 return (ident);
180}
181
182
196
197vector<string>
198splitNamespace(string ns) {
199
200 // Namespaces components are separated by double colon characters -
201 // convert to single colons.
202 size_t dcolon;
203 while ((dcolon = ns.find("::")) != string::npos) {
204 ns.replace(dcolon, 2, ":");
205 }
206
207 // ... and return the vector of namespace components split on the single
208 // colon.
209 return (isc::util::str::tokens(ns, ":"));
210}
211
212
216void
217writeOpeningNamespace(ostream& output, const vector<string>& ns) {
218 if (!ns.empty()) {
219
220 // Output namespaces in correct order
221 for (vector<string>::size_type i = 0; i < ns.size(); ++i) {
222 output << "namespace " << ns[i] << " {\n";
223 }
224 output << "\n";
225 }
226}
227
228
232void
233writeClosingNamespace(ostream& output, const vector<string>& ns) {
234 if (!ns.empty()) {
235 for (int i = ns.size() - 1; i >= 0; --i) {
236 output << "} // namespace " << ns[i] << "\n";
237 }
238 output << "\n";
239 }
240}
241
257
258void
259writeHeaderFile(const string& file, const vector<string>& ns_components,
260 MessageDictionary& dictionary, const char* output_directory)
261{
262 Filename message_file(file);
263 Filename header_file(Filename(message_file.name()).useAsDefault(".h"));
264 if (output_directory != NULL) {
265 header_file.setDirectory(output_directory);
266 }
267
268 // Text to use as the sentinels.
269 string sentinel_text = sentinel(header_file);
270
271 // zero out the errno to be safe
272 errno = 0;
273
274 // Open the output file for writing
275 ofstream hfile(header_file.fullName().c_str());
276
277 if (hfile.fail()) {
278 isc_throw_4(MessageException, "Failed to open output file",
279 LOG_OPEN_OUTPUT_FAIL, header_file.fullName(),
280 strerror(errno), 0);
281 }
282
283 // Write the header preamble. If there is an error, we'll pick it up
284 // after the last write.
285
286 hfile <<
287 "// File created from " << message_file.fullName() << " on " <<
288 currentTime() << "\n" <<
289 "\n" <<
290 "#ifndef " << sentinel_text << "\n" <<
291 "#define " << sentinel_text << "\n" <<
292 "\n" <<
293 "#include <log/message_types.h>\n" <<
294 "\n";
295
296 // Write the message identifiers, bounded by a namespace declaration
297 writeOpeningNamespace(hfile, ns_components);
298
299 vector<string> idents = sortedIdentifiers(dictionary);
300 for (vector<string>::const_iterator j = idents.begin();
301 j != idents.end(); ++j) {
302 hfile << "extern const isc::log::MessageID " << *j << ";\n";
303 }
304 hfile << "\n";
305
306 writeClosingNamespace(hfile, ns_components);
307
308 // ... and finally the postamble
309 hfile << "#endif // " << sentinel_text << "\n";
310
311 // Report errors (if any) and exit
312 if (hfile.fail()) {
313 isc_throw_4(MessageException, "Error writing to output file",
314 LOG_WRITE_ERROR, header_file.fullName(), strerror(errno),
315 0);
316 }
317
318 hfile.close();
319}
320
321
325
326char
328 return (isalnum(c) ? c : '_');
329}
330
331
365void
366writeProgramFile(const string& file, const vector<string>& ns_components,
367 MessageDictionary& dictionary,
368 const char* output_directory)
369{
370 Filename message_file(file);
371 Filename program_file(Filename(message_file.name()).useAsDefault(".cc"));
372 if (output_directory) {
373 program_file.setDirectory(output_directory);
374 }
375
376 // zero out the errno to be safe
377 errno = 0;
378
379 // Open the output file for writing
380 ofstream ccfile(program_file.fullName().c_str());
381
382 if (ccfile.fail()) {
383 isc_throw_4(MessageException, "Error opening output file",
384 LOG_OPEN_OUTPUT_FAIL, program_file.fullName(),
385 strerror(errno), 0);
386 }
387
388 // Write the preamble. If there is an error, we'll pick it up after
389 // the last write.
390
391 ccfile <<
392 "// File created from " << message_file.fullName() << " on " <<
393 currentTime() << "\n" <<
394 "\n" <<
395 "#include <cstddef>\n" <<
396 "#include <log/message_types.h>\n" <<
397 "#include <log/message_initializer.h>\n" <<
398 "\n";
399
400 // Declare the message symbols themselves.
401
402 writeOpeningNamespace(ccfile, ns_components);
403
404 vector<string> idents = sortedIdentifiers(dictionary);
405 for (vector<string>::const_iterator j = idents.begin();
406 j != idents.end(); ++j) {
407 ccfile << "extern const isc::log::MessageID " << *j <<
408 " = \"" << *j << "\";\n";
409 }
410 ccfile << "\n";
411
412 writeClosingNamespace(ccfile, ns_components);
413
414 // Now the code for the message initialization.
415
416 ccfile <<
417 "namespace {\n" <<
418 "\n" <<
419 "const char* values[] = {\n";
420
421 // Output the identifiers and the associated text.
422 idents = sortedIdentifiers(dictionary);
423 for (vector<string>::const_iterator i = idents.begin();
424 i != idents.end(); ++i) {
425 ccfile << " \"" << *i << "\", \"" <<
426 quoteString(dictionary.getText(*i)) << "\",\n";
427 }
428
429
430 // ... and the postamble
431 ccfile <<
432 " NULL\n" <<
433 "};\n" <<
434 "\n" <<
435 "const isc::log::MessageInitializer initializer(values);\n" <<
436 "\n" <<
437 "} // Anonymous namespace\n" <<
438 "\n";
439
440 // Report errors (if any) and exit
441 if (ccfile.fail()) {
442 isc_throw_4(MessageException, "Error writing to output file",
443 LOG_WRITE_ERROR, program_file.fullName(), strerror(errno),
444 0);
445 }
446
447 ccfile.close();
448}
449
450
457
458void
460
461 // Get the duplicates (the overflow) and, if present, sort them into some
462 // order and remove those which occur more than once (which mean that they
463 // occur more than twice in the input file).
465 if (!duplicates.empty()) {
466 cout << "Error: the following duplicate IDs were found:\n";
467
468 sort(duplicates.begin(), duplicates.end());
469 MessageReader::MessageIDCollection::iterator new_end =
470 unique(duplicates.begin(), duplicates.end());
471 for (MessageReader::MessageIDCollection::iterator i = duplicates.begin();
472 i != new_end; ++i) {
473 cout << " " << *i << "\n";
474 }
475 exit(1);
476 }
477}
478
479
484
485int
486main(int argc, char* argv[]) {
487
488 const char* soptions = "hvpd:"; // Short options
489
490 optind = 1; // Ensure we start a new scan
491 int opt; // Value of the option
492
493 const char *output_directory = NULL;
494
495 while ((opt = getopt(argc, argv, soptions)) != -1) {
496 switch (opt) {
497 case 'd':
498 output_directory = optarg;
499 break;
500
501 case 'h':
502 usage();
503 return (0);
504
505 case 'v':
506 version();
507 return (0);
508
509 default:
510 // A message will have already been output about the error.
511 return (1);
512 }
513 }
514
515 // Do we have the message file?
516 if (optind < (argc - 1)) {
517 cout << "Error: excess arguments in command line\n";
518 usage();
519 return (1);
520 } else if (optind >= argc) {
521 cout << "Error: missing message file\n";
522 usage();
523 return (1);
524 }
525 string message_file = argv[optind];
526
527 try {
528 // Have identified the file, so process it. First create a local
529 // dictionary into which the data will be put.
530 MessageDictionary dictionary;
531
532 // Read the data into it.
533 MessageReader reader(&dictionary);
534 reader.readFile(message_file);
535
536 // Error (and quit) if there are of any duplicates encountered.
537 errorDuplicates(reader);
538
539 // Get the namespace into which the message definitions will be put and
540 // split it into components.
541 vector<string> ns_components =
543
544 // Write the header file.
545 writeHeaderFile(message_file, ns_components, dictionary,
546 output_directory);
547
548 // Write the file that defines the message symbols and text
549 writeProgramFile(message_file, ns_components, dictionary,
550 output_directory);
551
552 }
553 catch (const MessageException& e) {
554 // Create an error message from the ID and the text
556 string text = e.id();
557 text += ", ";
558 text += global->getText(e.id());
559 // Format with arguments
560 vector<string> args(e.arguments());
561 for (size_t i(0); i < args.size(); ++ i) {
562 try {
563 replacePlaceholder(&text, args[i], i + 1);
564 } catch (...) {
565 // Error in error handling: nothing right to do...
566 }
567 }
568
569 cerr << text << "\n";
570
571 return (1);
572 }
573
574 return (0);
575
576}
static const MessageDictionaryPtr & globalDictionary()
Return Global Dictionary.
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:94
std::string name() const
Definition: filename.h:89
std::string fullName() const
Definition: filename.h:72
void setDirectory(const std::string &new_directory)
Set directory for the file.
Definition: filename.cc:130
std::string useAsDefault(const std::string &name) const
Use as Default and Substitute into String.
Definition: filename.cc:106
#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.
string currentTime()
Create Time.
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(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:110
Definition: edns.h:19