// system_messages [-o <output-file>] <files>
//
// If no output file is specified, output is written to stdout.
+// The produced format is docbook XML.
#include <algorithm>
#include <fstream>
#include <stdlib.h>
#include <string.h>
-#ifdef USE_BOOST_FILESYSTEM
-#include <boost/filesystem.hpp>
-#endif
#include <boost/lexical_cast.hpp>
-typedef std::vector<std::string> lines_type;
+typedef std::vector<std::string> LinesType;
-/// dictionary values
+/// @brief dictionary values
struct Details {
std::string text;
- lines_type description;
+ LinesType description;
std::string sname;
std::string filename;
};
-/// Main dictionary holding all the messages. The messages are
-/// accumulated here before being printed in alphabetical order.
-typedef std::map<const std::string, Details> dictionary_type;
-dictionary_type dictionary;
+/// @brief Main dictionary holding all the messages.
+/// The messages are accumulated here before being printed in
+/// alphabetical order.
+typedef std::map<const std::string, Details> dictionaryType;
+dictionaryType dictionary;
-// The structure of the output page is:
+/// @brief The structure of the output page
//
-// header
-// section header
-// message
-// separator
-// message
-// separator
-// :
-// separator
-// message
-// section trailer
-// separator
-// section header
-// :
-// section trailer
-// trailer
+/// header
+/// section header
+/// message
+/// separator
+/// message
+/// separator
+/// :
+/// separator
+/// message
+/// section trailer
+/// separator
+/// section header
+/// :
+/// section trailer
+/// trailer
//
-// (Indentation is not relevant - it has only been added to the above
-// illustration to make the structure clearer.) The text of these section is:
+/// (Indentation is not relevant - it has only been added to the above
+/// illustration to make the structure clearer.) The text of these section is:
-// Header - this is output before anything else.
+/// @name Constants for the output page
+//@{
+
+/// @brief File header
+/// this is output before anything else.
const std::string FILE_HEADER =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook XML V4.2//EN\"\n\
<title>Kea Messages Manual</title>\n\
\n\
<copyright>\n\
- <year>2011-2014</year><holder>Internet Systems Consortium, Inc.</holder>\n\
+ <year>2011-2015</year><holder>Internet Systems Consortium, Inc.</holder>\n\
</copyright>\n\
\n\
<abstract>\n\
<chapter id=\"messages\">\n\
<title>Kea Log Messages</title>\n";
-// This is output one for each module. $M substitution token is the name.
+/// @brief Section header
+/// This is output one for each module. $M substitution token is the name.
const std::string SECTION_HEADER = " <section id=\"$M\">\n\
<title>$M Module</title>\n\
<para>\n\
<variablelist>\n";
-// This is output once for each message. The string contains
-// substitution tokens: $I is replaced by the message identification,
-// $T by the message text, and $D by the message description.
+/// @brief message ID
+/// This is output once for each message. The string contains
+/// substitution tokens: $I is replaced by the message identification,
+/// $T by the message text, and $D by the message description.
const std::string ID_MESSAGE =
"<varlistentry id=\"$I\">\n\
<term>$I $T</term>\n\
$D</para></listitem>\n\
</varlistentry>";
-// A description may contain blank lines intended to separate
-// paragraphs. If so, each blank line is replaced by the following.
+/// @brief Blank line
+/// A description may contain blank lines intended to separate
+/// paragraphs. If so, each blank line is replaced by the following.
const std::string BLANK = "</para><para>";
-// The separator is copied to the output verbatim after each message except
-// the last.
+/// @brief Separator
+/// The separator is copied to the output verbatim after each message except
+/// the last.
const std::string SEPARATOR = "";
-// The trailier is copied to the output verbatim after the last message.
+/// @brief Section trailer
+/// The trailer is copied to the output verbatim after the last message.
const std::string SECTION_TRAILER =
" </variablelist>\n\
</para>\n\
</section>";
-// The trailier is copied to the output verbatim after the last section.
+/// @brief File trailer
+/// The trailier is copied to the output verbatim after the last section.
const std::string FILE_TRAILER =
" </chapter>\n\
</book>";
+//@}
+
+/// @name Utility routines
+//@{
-/// Report an error and exit
+/// @brief Report an error and exit
void reportError(const std::string& filename, const std::string& what)
{
std::cerr << "*** ERROR in " << filename << "\n";
exit(1);
}
-
+/// @brief Replace tag
/// Replaces the '<' and '>' in text about to be inserted into the template
/// sections above with < and > to avoid problems with message text
/// being interpreted as XML text.
result.push_back(*it);
}
}
- return result;
+ return (result);
}
-/// Replace $c in a string
+/// @brief Replace shell
+/// Replace $c in a string (or with other words performs macro expansion
+/// with '$' for introducing a macro followed by a character selecting
+/// a specific macro.
+///
+/// @param src source string
+/// @param c character selecting a macro when it follows '$'
+/// @param val value which
+///
+/// @return the source string where all occurrences of $c were
+/// replaced by val
std::string replaceShell(const std::string& src, char c,
const std::string& val)
{
result.push_back(*it);
}
}
- return result;
+ return (result);
}
+/// @brief Replace blank lines
/// Replaces blank lines in an array with the contents of the 'blank' section.
-lines_type replaceBlankLines(const lines_type lines)
+LinesType replaceBlankLines(const LinesType lines)
{
- lines_type result;
- for (lines_type::const_iterator l = lines.begin(); l != lines.end(); ++l) {
+ LinesType result;
+ for (LinesType::const_iterator l = lines.begin(); l != lines.end(); ++l) {
if (l->empty()) {
result.push_back(BLANK);
} else {
result.push_back(*l);
}
}
- return result;
+ return (result);
}
+//@}
+
+/// @name Printing functions
+//@{
-/// Printing functions
+/// @brief Print file header
void printHeader() {
std::cout << FILE_HEADER << "\n";
}
+/// @brief Print separator
void printSeparator() {
std::cout << SEPARATOR << "\n";
}
+/// @brief Print section header
void printSectionHeader(const std::string& sname)
{
// In the section name, replace "<" and ">" with XML-safe versions and
std::cout << replaceShell(SECTION_HEADER, 'M', replaceTag(sname));
}
+/// @brief print message id
void printMessage(const std::string& msgid)
{
// In the message ID, replace "<" and ">" with XML-safe versions and
// Do the same for the description then replace blank lines with the
// specified separator. (We do this in that order to avoid replacing
// the "<" and ">" in the XML tags in the separator.)
- lines_type desc0 = dictionary[msgid].description;
- lines_type desc1;
- for (lines_type::iterator l = desc0.begin(); l != desc0.end(); ++l) {
+ LinesType desc0 = dictionary[msgid].description;
+ LinesType desc1;
+ for (LinesType::iterator l = desc0.begin(); l != desc0.end(); ++l) {
desc1.push_back(replaceTag(*l));
}
- lines_type desc2 = replaceBlankLines(desc1);
+ LinesType desc2 = replaceBlankLines(desc1);
// Join the lines together to form a single string and insert into
// current text.
std::string m3;
- for (lines_type::iterator l = desc2.begin(); l != desc2.end(); ++l) {
+ for (LinesType::iterator l = desc2.begin(); l != desc2.end(); ++l) {
m3.append(*l);
m3.push_back('\n');
}
std::cout << replaceShell(m2, 'D', m3) << "\n";
}
+/// @brief print section trailer
void printSectionTrailer() {
std::cout << SECTION_TRAILER << "\n";
}
+/// @brief print file trailer
void printTrailer() {
std::cout << FILE_TRAILER << "\n";
}
+//@}
-/// Removes leading and trailing empty lines.
+/// @brief Removes leading and trailing empty lines.
///
/// A list of strings is passed as argument, some of which may be empty.
/// This function removes from the start and end of list a contiguous
/// sequence of empty lines and returns the result. Embedded sequence of
/// empty lines are not touched.
///
-/// Parameters:
-/// lines List of strings to be modified.
+/// @param lines List of strings to be modified.
///
-/// Return:
-/// Input list of strings with leading/trailing blank line sequences
-/// removed.
-lines_type removeEmptyLeadingTrailing(lines_type lines)
+/// @return Input list of strings with leading/trailing blank line
+/// sequences removed.
+LinesType removeEmptyLeadingTrailing(const LinesType& lines)
{
- lines_type retlines = lines;
+ LinesType retlines = lines;
// Dispose of degenerate case of empty array
if (retlines.empty()) {
- return retlines;
+ return (retlines);
}
// Search for first non-blank line
for (;;) {
- lines_type::iterator start = retlines.begin();
+ LinesType::iterator start = retlines.begin();
if (start == retlines.end()) {
- return retlines;
+ return (retlines);
}
if (start->empty()) {
retlines.erase(start);
// Search for last non-blank line
for (;;) {
- lines_type::reverse_iterator finish = retlines.rbegin();
+ LinesType::reverse_iterator finish = retlines.rbegin();
if (finish == retlines.rend()) {
- return retlines;
+ return (retlines);
}
if (finish->empty()) {
retlines.erase(retlines.end() - 1);
}
}
- return retlines;
+ return (retlines);
}
-/// Add the current message ID and associated information to the global
-/// dictionary. If a message with that ID already exists, loop appending
-/// suffixes of the form "(n)" to it until one is found that doesn't.
+/// @brief Add the current message ID and associated information to the global
+/// dictionary.
+/// If a message with that ID already exists, loop appending suffixes
+/// of the form "(n)" to it until one is found that doesn't.
///
-/// Parameters:
-/// msgid Message ID
-/// msgtext Message text
-/// desc Message description
-/// filename File from which the message came. Currently this is
-/// not used, but a future enhancement may wish to include the
-/// name of the message file in the messages manual.
+/// @param msgid Message ID
+/// @param msgtext Message text
+/// @param desc Message description
+/// @param filename File from which the message came. Currently this is
+/// not used, but a future enhancement may wish to
+/// include the name of the message file in the
+/// messages manual.
void addToDictionary(const std::string& msgid,
const std::string& msgtext,
- const lines_type& desc,
+ const LinesType& desc,
const std::string& filename)
{
// If the ID is in the dictionary, append a "(n)" to the name - this will
}
-/// Processes file content. Messages and descriptions are identified and
-/// added to a dictionary (keyed by message ID). If the key already exists,
-/// a numeric suffix is added to it.
+/// @brief Processes file content.
+/// Messages and descriptions are identified and added to a dictionary
+/// (keyed by message ID). If the key already exists, a numeric
+/// suffix is added to it.
+
+/// The format of .mes files is fully described in src/lib/log/logging.dox
///
-/// Parameters:
-/// filename Name of the message file being processed
-/// lines Lines read from the file
+/// @param filename Name of the message file being processed
+/// @param lines Lines read from the file
void processFileContent(const std::string& filename,
- const lines_type& lines)
+ const LinesType& lines)
{
std::string prefix; // Last prefix encountered
std::string msgid; // Last message ID encountered
std::string msgtext; // Text of the message
- lines_type description; // Description
+ LinesType description; // Description
- for (lines_type::const_iterator l = lines.begin(); l != lines.end(); ++l) {
+ for (LinesType::const_iterator l = lines.begin(); l != lines.end(); ++l) {
if (l->empty()) {
description.push_back(*l);
} else if (l->at(0) == '$') {
}
}
-/// Processes a file by reading it in and stripping out all comments and
-/// and directives. Leading and trailing blank lines in the file are removed
-/// and the remainder passed for message processing.
+/// @brief Process a file
+/// Read it in and strip out all comments and and directives. Leading
+/// and trailing blank lines in the file are removed and the remainder
+/// passed for message processing.
///
-/// Parameters:
-/// filename Name of the message file to process
+/// @param filename Name of the message file to process
void processFile(const std::string& filename)
{
std::ifstream cin;
cin.open(filename, std::ios::in);
- lines_type lines0;
+ if (!cin.is_open()) {
+ reportError(filename, "open for read failure");
+ }
+ LinesType lines0;
while (!cin.eof()) {
std::string line;
getline(cin, line);
lines0.push_back(line);
}
+ cin.close();
// Trim leading and trailing spaces from each line, and remove comments.
- lines_type lines1;
- for (lines_type::iterator l = lines0.begin(); l != lines0.end(); ++l) {
+ LinesType lines1;
+ for (LinesType::iterator l = lines0.begin(); l != lines0.end(); ++l) {
std::string line = *l;
if (line.empty()) {
lines1.push_back(line);
}
// Remove leading/trailing empty line sequences from the result
- lines_type lines2 = removeEmptyLeadingTrailing(lines1);
+ LinesType lines2 = removeEmptyLeadingTrailing(lines1);
// Interpret content
processFileContent(filename, lines2);
}
-/// Iterates through all files in the tree starting at the given root and
-/// calls processFile for all .mes files found.
-///
-/// Parameters:
-/// root Directory that is the root of the source tree
-#ifdef USE_BOOST_FILESYSTEM
-void processAllFiles(const boost::filesystem::path& root)
-{
- boost::filesystem::directory_iterator endd;
- for (boost::filesystem::directory_iterator file(root);
- file != endd;
- ++file) {
- boost::filesystem::path path = file->path();
- if (boost::filesystem::is_directory(path)) {
- processAllFiles(path);
- } else if (boost::filesystem::is_regular_file(path)) {
- boost::filesystem::path extension = path.extension();
- // Identify message files
- if (extension == ".mes") {
- processFile(path.native());
- }
- }
- }
-}
-#endif
-
+/// @brief Usage error routine
void usage(char* progname)
{
-#ifdef USE_BOOST_FILESYSTEM
- std::cerr << "Usage: " << progname <<
- " [--help | options] root|files\n";
-#else
std::cerr << "Usage: " << progname <<
" [--help | options] files\n";
-#endif
std::cerr << " options: --output file: " <<
"output file name (default to stdout)\n";
}
+/// @brief Main (entry point)
int main(int argc, char* argv[])
{
char* progname = argv[0];
exit(-1);
}
fout.open(argv[0], std::ofstream::out | std::ofstream::trunc);
+ if (!fout.is_open()) {
+ reportError(argv[0], "open for write failure");
+ }
std::cout.rdbuf(fout.rdbuf());
--argc;
++argv;
+ break;
}
}
usage(progname);
exit(-1);
}
-#ifdef USE_BOOST_FILESYSTEM
- if (argc > 1) {
- for (int i = 0; i < argc; ++i) {
- processFile(argv[i]);
- }
- } else {
- boost::filesystem::path root(argv[0]);
- if (boost::filesystem::is_directory(root)) {
- processAllFiles(root);
- } else {
- processFile(argv[0]);
- }
- }
-#else
for (int i = 0; i < argc; ++i) {
processFile(argv[i]);
}
-#endif
// Now just print out everything we've read (in alphabetical order).
bool first = true;
std::string sname;
printHeader();
- for (dictionary_type::iterator it = dictionary.begin();
+ for (dictionaryType::iterator it = dictionary.begin();
it != dictionary.end();
++it) {
if (sname.compare(it->second.sname) != 0) {
+++ /dev/null
-# Copyright (C) 2011, 2015 Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-# Produce System Messages Manual
-#
-# This tool reads all the .mes files in the directory tree whose root is given
-# on the command line and interprets them as message files. It pulls
-# all the messages and description out, sorts them by message ID, and writes
-# them out as a single (formatted) file.
-#
-# Invocation:
-# The code is invoked using the command line:
-#
-# python system_messages.py [-o <output-file>] <top-source-directory>|<files>
-#
-# If no output file is specified, output is written to stdout.
-
-import re
-import os
-import sys
-from optparse import OptionParser
-
-# Main dictionary holding all the messages. The messages are accumulated here
-# before being printed in alphabetical order.
-dictionary = {}
-
-# The structure of the output page is:
-#
-# header
-# section header
-# message
-# separator
-# message
-# separator
-# :
-# separator
-# message
-# section trailer
-# separator
-# section header
-# :
-# section trailer
-# trailer
-#
-# (Indentation is not relevant - it has only been added to the above
-# illustration to make the structure clearer.) The text of these section is:
-
-# Header - this is output before anything else.
-FILE_HEADER = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
-<!ENTITY mdash "—" >
-<!ENTITY % version SYSTEM "version.ent">
-%version;
-]>
-<!--
- This XML document is generated using the system_messages.py tool
- based on the .mes message files.
-
- Do not edit this file.
--->
-<book>
- <?xml-stylesheet href="kea-guide.css" type="text/css"?>
-
- <bookinfo>
- <title>Kea Messages Manual</title>
-
- <copyright>
- <year>2011-2014</year><holder>Internet Systems Consortium, Inc.</holder>
- </copyright>
-
- <abstract>
- <para>
- This is the messages manual for Kea version &__VERSION__;.
- The most up-to-date version of this document, along with
- other documents for Kea, can be found at
- <ulink url="http://kea.isc.org/docs"/>.
- </para>
- </abstract>
-
- <releaseinfo>This is the messages manual for Kea version
- &__VERSION__;.</releaseinfo>
- </bookinfo>
-
- <chapter id="intro">
- <title>Introduction</title>
- <para>
- This document lists each message that can be logged by the
- programs in the Kea package. Each entry in this manual
- is of the form:
- <screen>IDENTIFICATION message-text</screen>
- ... where "IDENTIFICATION" is the message identification included
- in each message logged and "message-text" is the accompanying
- message text. The "message-text" may include placeholders of the
- form "%1", "%2" etc.; these parameters are replaced by relevant
- values when the message is logged.
- </para>
- <para>
- Each entry is also accompanied by a description giving more
- information about the circumstances that result in the message
- being logged.
- </para>
- <para>
- For information on configuring and using Kea logging,
- refer to the <ulink url="kea-guide.html">Kea Guide</ulink>.
- </para>
- </chapter>
-
- <chapter id="messages">
- <title>Kea Log Messages</title>
-"""
-
-# This is output one for each module. $M substitution token is the name.
-SECTION_HEADER = """ <section id="$M">
- <title>$M Module</title>
- <para>
- <variablelist>"""
-
-# This is output once for each message. The string contains substitution
-# tokens: $I is replaced by the message identification, $T by the message text,
-# and $D by the message description.
-ID_MESSAGE = """<varlistentry id="$I">
-<term>$I $T</term>
-<listitem><para>
-$D
-</para></listitem>
-</varlistentry>"""
-
-# A description may contain blank lines intended to separate
-# paragraphs. If so, each blank line is replaced by the following.
-BLANK = "</para><para>"
-
-# The separator is copied to the output verbatim after each message or
-# section except the last.
-SEPARATOR = ""
-
-# The trailer is copied to the output verbatim after the last message.
-SECTION_TRAILER = """ </variablelist>
- </para>
- </section>"""
-
-# The trailer is copied to the output verbatim after the last section.
-FILE_TRAILER = """ </chapter>
-</book>"""
-
-
-def reportError(filename, what):
- """Report an error and exit"""
- print("*** ERROR in ", filename, file=sys.stderr)
- print("*** REASON: ", what, file=sys.stderr)
- print("*** System message generator terminating", file=sys.stderr)
- sys.exit(1)
-
-
-def replaceTag(string):
- """Replaces the '<' and '>' in text about to be inserted into the template
- sections above with < and > to avoid problems with message text
- being interpreted as XML text.
- """
- string1 = string.replace("<", "<")
- string2 = string1.replace(">", ">")
- return string2
-
-def replaceBlankLines(lines):
- """Replaces blank lines in an array with the contents of the 'blank'
- section.
- """
- result = []
- for l in lines:
- if len(l) == 0:
- result.append(BLANK)
- else:
- result.append(l)
-
- return result
-
-
-# Printing functions
-def printHeader():
- print(FILE_HEADER)
-
-def printSeparator():
- print(SEPARATOR)
-
-def printSectionHeader(sname):
- # In the section name, replace "<" and ">" with XML-safe versions and
- # substitute into the data.
- m = SECTION_HEADER.replace("$M", replaceTag(sname));
-
- print(m)
-
-def printMessage(msgid):
- # In the message ID, replace "<" and ">" with XML-safe versions and
- # substitute into the data.
- m1 = ID_MESSAGE.replace("$I", replaceTag(msgid))
-
- # Do the same for the message text.
- m2 = m1.replace("$T", replaceTag(dictionary[msgid]['text']))
-
- # Do the same for the description then replace blank lines with the
- # specified separator. (We do this in that order to avoid replacing
- # the "<" and ">" in the XML tags in the separator.)
- desc1 = [replaceTag(l) for l in dictionary[msgid]['description']]
- desc2 = replaceBlankLines(desc1)
-
- # Join the lines together to form a single string and insert into
- # current text.
- m3 = m2.replace("$D", "\n".join(desc2))
-
- print(m3)
-
-def printSectionTrailer():
- print(SECTION_TRAILER)
-
-def printTrailer():
- print(FILE_TRAILER)
-
-
-def removeEmptyLeadingTrailing(lines):
- """Removes leading and trailing empty lines.
-
- A list of strings is passed as argument, some of which may be empty.
- This function removes from the start and end of list a contiguous
- sequence of empty lines and returns the result. Embedded sequence of
- empty lines are not touched.
-
- Parameters:
- lines List of strings to be modified.
-
- Return:
- Input list of strings with leading/trailing blank line sequences
- removed.
- """
-
- retlines = []
-
- # Dispose of degenerate case of empty array
- if len(lines) == 0:
- return retlines
-
- # Search for first non-blank line
- start = 0
- while start < len(lines):
- if len(lines[start]) > 0:
- break
- start = start + 1
-
- # Handle case when entire list is empty
- if start >= len(lines):
- return retlines
-
- # Search for last non-blank line
- finish = len(lines) - 1
- while finish >= 0:
- if len(lines[finish]) > 0:
- break
- finish = finish - 1
-
- retlines = lines[start:finish + 1]
- return retlines
-
-
-def addToDictionary(msgid, msgtext, desc, filename):
- """Add the current message ID and associated information to the global
- dictionary. If a message with that ID already exists, loop appending
- suffixes of the form "(n)" to it until one is found that doesn't.
-
- Parameters:
- msgid Message ID
- msgtext Message text
- desc Message description
- sname Section name (part before the first _ of the ID)
- filename File from which the message came. Currently this is
- not used, but a future enhancement may wish to include the
- name of the message file in the messages manual.
- """
-
- # If the ID is in the dictionary, append a "(n)" to the name - this will
- # flag that there are multiple instances. (However, this is an error -
- # each ID should be unique in the code.)
- if msgid in dictionary:
- i = 1
- while msgid + " (" + str(i) + ")" in dictionary:
- i = i + 1
- msgid = msgid + " (" + str(i) + ")"
-
- # Remove leading and trailing blank lines in the description, then
- # add everything into a subdictionary which is then added to the main
- # one.
- details = {}
- words = re.split("_", msgid)
- details['text'] = msgtext
- details['description'] = removeEmptyLeadingTrailing(desc)
- details['sname'] = words[0]
- details['filename'] = filename
- dictionary[msgid] = details
-
-
-def processFileContent(filename, lines):
- """Processes file content. Messages and descriptions are identified and
- added to a dictionary (keyed by message ID). If the key already exists,
- a numeric suffix is added to it.
-
- Parameters:
- filename Name of the message file being processed
- lines Lines read from the file
- """
-
- prefix = "" # Last prefix encountered
- msgid = "" # Last message ID encountered
- msgtext = "" # Text of the message
- description = [] # Description
-
- for l in lines:
- if l.startswith("$"):
- # Starts with "$". Ignore anything other than $PREFIX
- words = re.split("\s+", l)
- if words[0].upper() == "$PREFIX":
- if len(words) == 1:
- prefix = ""
- else:
- prefix = words[1]
-
- elif l.startswith("%"):
- # Start of a message. Add the message we were processing to the
- # dictionary and clear everything apart from the file name.
- if msgid != "":
- addToDictionary(msgid, msgtext, description, filename)
-
- msgid = ""
- msgtext = ""
- description = []
-
- # Start of a message
- l = l[1:].strip() # Remove "%" and trim leading spaces
- if len(l) == 0:
- printError(filename, "Line with single % found")
- next
-
- # Split into words. The first word is the message ID
- words = re.split("\s+", l)
- msgid = (prefix + words[0]).upper()
- msgtext = l[len(words[0]):].strip()
-
- else:
- # Part of a description, so add to the current description array
- description.append(l)
-
- # All done, add the last message to the global dictionaty.
- if msgid != "":
- addToDictionary(msgid, msgtext, description, filename)
-
-
-def processFile(filename):
- """Processes a file by reading it in and stripping out all comments and
- and directives. Leading and trailing blank lines in the file are removed
- and the remainder passed for message processing.
-
- Parameters:
- filename Name of the message file to process
- """
- lines = open(filename).readlines();
-
- # Trim leading and trailing spaces from each line, and remove comments.
- lines = [l.strip() for l in lines]
- lines = [l for l in lines if not l.startswith("#")]
-
- # Remove leading/trailing empty line sequences from the result
- lines = removeEmptyLeadingTrailing(lines)
-
- # Interpret content
- processFileContent(filename, lines)
-
-
-def processAllFiles(root):
- """Iterates through all files in the tree starting at the given root and
- calls processFile for all .mes files found.
-
- Parameters:
- root Directory that is the root of the source tree
- """
- for (path, dirs, files) in os.walk(root):
-
- # Identify message files
- mes_files = [f for f in files if f.endswith(".mes")]
-
- # ... and process each file in the list
- for m in mes_files:
- processFile(path + os.sep + m)
-
-
-# Main program
-if __name__ == "__main__":
- parser = OptionParser(usage="Usage: %prog [--help | options] root|files")
- parser.add_option("-o", "--output", dest="output", default=None,
- metavar="FILE",
- help="output file name (default to stdout)")
- (options, args) = parser.parse_args()
-
- if len(args) == 0:
- parser.error("Must supply directory at which to begin search or files")
-
- # Redirect output if specified (errors are written to stderr)
- if options.output is not None:
- sys.stdout = open(options.output, 'w')
-
- # Read the files and load the data
- if (len(args) > 1) or (not os.path.isdir(args[0])):
- for file in args:
- processFile(file)
- else:
- processAllFiles(args[0])
-
- # Now just print out everything we've read (in alphabetical order).
- sname = ""
- printHeader()
- for msgid in sorted(dictionary):
- if dictionary[msgid]['sname'] != sname:
- if sname != "":
- printSectionTrailer()
- printSeparator()
- sname = dictionary[msgid]['sname']
- printSectionHeader(sname)
- count = 1
- if count > 1:
- printSeparator()
- count = count + 1
- printMessage(msgid)
- if sname !="":
- printSectionTrailer()
- printTrailer()