po2nsh.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 #include <QHash>
00012 #include <QFile>
00013 #include <QTextStream>
00014 #include <QTextCodec>
00015 #include <stdlib.h>
00016 
00017 
00018 /** Parse the context name from <b>str</b>, where the context name is of the
00019  * form DQUOTE ContextName DQUOTE. */
00020 QString
00021 parse_message_context(const QString &str)
00022 {
00023   QString out = str.trimmed();
00024   out = out.replace("\"", "");
00025   return out;
00026 }
00027 
00028 /** Parse the context name from <b>str</b>, where <b>str</b> is of the
00029  * form ContextName#Number. This is the format used by translate-toolkit. */
00030 QString
00031 parse_message_context_lame(const QString &str)
00032 {
00033   if (str.contains("#"))
00034     return str.section("#", 0, 0);
00035   return QString();
00036 }
00037 
00038 /** Parse the PO-formatted message string from <b>msg</b>. If <b>msg</b> is a
00039  * multiline string, the extra double quotes will be replaced with newlines
00040  * appropriately. */
00041 QString
00042 parse_message_string(const QString &msg)
00043 {
00044   QString out = msg.trimmed(); 
00045   
00046   out.replace("\"\n\"", "");
00047   if (out.startsWith("\""))
00048     out = out.remove(0, 1);
00049   if (out.endsWith("\""))
00050     out.chop(1);
00051   out.replace("\\\"", "\"");
00052   out.replace("\\n", "\\r\\n");
00053   out.replace("$\\r\\n", "$\\r$\\n");
00054   return out;
00055 }
00056 
00057 /** Read and return the next non-empty line from <b>stream</b>. */
00058 QString
00059 read_next_line(QTextStream *stream)
00060 {
00061   stream->skipWhiteSpace();
00062   return stream->readLine().append("\n");
00063 }
00064 
00065 /** Skip past the header portion of the PO file and any leading whitespace. 
00066  * The next line read from <b>po</b> will be the first non-header line in the
00067  * document. */
00068 void
00069 skip_po_header(QTextStream *po)
00070 {
00071   QString line;
00072   /* Skip any leading whitespace before the header */
00073   po->skipWhiteSpace();
00074   /* Read to the first empty line */
00075   line = po->readLine();
00076   while (!po->atEnd() && !line.isEmpty())
00077     line = po->readLine();
00078 }
00079 
00080 /** Convert <b>po</b> from the PO format to a NSIS-formatted .nsh document.
00081  * <b>nsh</b> will be set to the resulting .nsh document. Return the number of
00082  * converted strings on success, or -1 on error and <b>errorMessage</b> will
00083  * be set. */
00084 int
00085 po2nsh(QTextStream *po, QString *nsh, const QString &language,
00086       QString *errorMessage)
00087 {
00088   QString line;
00089   QString msgctxt, msgid, msgstr;
00090   QHash<QString,QString> header;
00091   QTextCodec *codec;
00092   int n_strings = 0;
00093   
00094   Q_ASSERT(po);
00095   Q_ASSERT(nsh);
00096   Q_ASSERT(errorMessage);
00097   
00098   skip_po_header(po);
00099   line = read_next_line(po);
00100   while (!po->atEnd()) {
00101     /* Ignore all "#" lines except "#:" */
00102     while (line.startsWith("#")) {
00103       if (line.startsWith("#:")) {
00104         /* Context was specified with the stupid overloaded "#:" syntax.*/
00105         msgctxt = line.section(" ", 1);
00106         msgctxt = parse_message_context_lame(msgctxt);
00107       }
00108       line = read_next_line(po);
00109     }
00110 
00111     /* A context specified on a "msgctxt" line takes precedence over a context
00112      * specified using the overload "#:" notation. */
00113     if (line.startsWith("msgctxt ")) {    
00114       msgctxt = line.section(" ", 1);
00115       msgctxt = parse_message_context(msgctxt);
00116       line = read_next_line(po);
00117     }
00118     
00119     /* Parse the (possibly multiline) message source string */
00120     if (!line.startsWith("msgid ")) {
00121       *errorMessage = "expected 'msgid' line";
00122       return -1;
00123     }
00124     msgid = line.section(" ", 1);
00125     
00126     line = read_next_line(po);
00127     while (line.startsWith("\"")) {
00128       msgid.append(line);
00129       line = read_next_line(po);
00130     }
00131     msgid = parse_message_string(msgid);
00132 
00133     /* Parse the (possibly multiline) translated string */
00134     if (!line.startsWith("msgstr ")) {
00135       *errorMessage = "expected 'msgstr' line";
00136       return -1;
00137     }
00138     msgstr = line.section(" ", 1);
00139     
00140     line = read_next_line(po);
00141     while (line.startsWith("\"")) {
00142       msgstr.append(line);
00143       line = read_next_line(po);
00144     }
00145     msgstr = parse_message_string(msgstr);
00146 
00147     /* Add the message translation to the .nsh document */
00148     nsh->append(QString("LangString "));
00149     nsh->append(msgctxt);
00150     nsh->append(QString(" ${LANG_%1} ").arg(language));
00151     if (msgstr.isEmpty())
00152       nsh->append("\"" + msgid + "\"");
00153     else
00154       nsh->append("\"" + msgstr + "\"");
00155     nsh->append("\n");
00156 
00157     n_strings++;
00158   }
00159   return n_strings;
00160 }
00161 
00162 /** Display application usage and exit. */
00163 void
00164 print_usage_and_exit()
00165 {
00166   QTextStream error(stderr);
00167   error << "usage: po2nsh [-q] -i <infile.po> -o <outfile.nsh> "
00168            "-l <language> [-f <from-encoding>] [-t <to-encoding>]\n";
00169   error << "  -q (optional)      Quiet mode (errors are still displayed)\n";
00170   error << "  -i <infile.po>     Input .po file\n";
00171   error << "  -o <outfile.nsh>   Output .nsh file\n";
00172   error << "  -l <language>      NSIS language table name\n";
00173   error << "  -f <from-encoding> .po file encoding (default: utf-8)\n";
00174   error << "  -t <to-encoding>   .nsh file encoding (default: iso-8859-1)\n";
00175   error.flush();
00176   exit(1);
00177 }
00178 
00179 int
00180 main(int argc, char *argv[])
00181 {
00182   QTextStream error(stderr);
00183   QString language, errorMessage;
00184   char *infile, *outfile;
00185   bool quiet = false;
00186   QTextCodec *from_codec = QTextCodec::codecForName("utf-8");
00187   QTextCodec *to_codec   = QTextCodec::codecForName("iso-8859-1");
00188   
00189   /* Check for the correct number of input parameters. */
00190   if (argc < 7 || argc > 12)
00191     print_usage_and_exit();
00192   for (int i = 1; i < argc; i++) {
00193     QString arg(argv[i]);
00194     if (!arg.compare("-q", Qt::CaseInsensitive))
00195       quiet = true;
00196     else if (!arg.compare("-i", Qt::CaseInsensitive) && ++i < argc)
00197       infile = argv[i];
00198     else if (!arg.compare("-o", Qt::CaseInsensitive) && ++i < argc)
00199       outfile = argv[i];
00200     else if (!arg.compare("-l", Qt::CaseInsensitive) && ++i < argc)
00201       language = QString(argv[i]).toUpper();
00202     else if (!arg.compare("-f", Qt::CaseInsensitive) && ++i < argc) {
00203       from_codec = QTextCodec::codecForName(argv[i]);
00204       if (!from_codec) {
00205         error << "Invalid input encoding: " << argv[i] << "\n";
00206         return 1;
00207       }
00208     } else if (!arg.compare("-t", Qt::CaseInsensitive) && ++i < argc) {
00209       to_codec = QTextCodec::codecForName(argv[i]);
00210       if (!to_codec) {
00211         error << "Invalid output encoding: " << argv[i] << "\n";
00212         return 1;
00213       }
00214     } else
00215       print_usage_and_exit(); 
00216   }
00217 
00218   /* Open the input PO file for reading. */
00219   QFile poFile(infile);
00220   if (!poFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
00221     error << QString("Unable to open '%1' for reading: %2\n").arg(infile)
00222                                                 .arg(poFile.errorString());
00223     return 2;
00224   }
00225 
00226   QString nsh;
00227   QTextStream po(&poFile);
00228   po.setCodec(from_codec);
00229   int n_strings = po2nsh(&po, &nsh, language, &errorMessage);
00230   if (n_strings < 0) {
00231     error << QString("Unable to convert '%1': %2\n").arg(infile)
00232                                                     .arg(errorMessage);
00233     return 3;
00234   }
00235   
00236   /* Open the NSH file for writing. */
00237   QFile nshFile(outfile);
00238   if (!nshFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
00239     error << QString("Unable to open '%1' for writing: %2\n").arg(outfile)
00240                                                 .arg(nshFile.errorString());
00241     return 4;
00242   }
00243 
00244   /* Finally write the .nsh output. */
00245   QTextStream out(&nshFile);
00246   out.setCodec(to_codec);
00247   out << nsh;
00248 
00249   if (!quiet) {
00250     QTextStream results(stdout);
00251     results << QString("Converted %1 strings from %2 to %3.\n").arg(n_strings)
00252                                                                .arg(infile)
00253                                                                .arg(outfile);
00254   }
00255   return 0;
00256 }
00257 

Generated on Mon Aug 30 19:14:02 2010 for Vidalia by  doxygen 1.5.9