Vidalia
0.2.17
|
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.torproject.org/projects/vidalia.html. No part of Vidalia, 00007 ** including this file, may be copied, modified, propagated, or distributed 00008 ** except according to the 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