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

Generated on Wed Nov 26 21:03:58 2008 for Vidalia by  doxygen 1.5.6