Vidalia 0.3.1
po2nsh.cpp
Go to the documentation of this file.
1/*
2** This file is part of Vidalia, and is subject to the license terms in the
3** LICENSE file, found in the top level directory of this distribution. If you
4** did not receive the LICENSE file with this file, you may obtain it from the
5** Vidalia source package distributed by the Vidalia Project at
6** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7** including this file, may be copied, modified, propagated, or distributed
8** except according to the terms described in the LICENSE file.
9*/
10
11#include <QHash>
12#include <QFile>
13#include <QTextStream>
14#include <QTextCodec>
15#include <stdlib.h>
16
17
18/** Parse the context name from <b>str</b>, where the context name is of the
19 * form DQUOTE ContextName DQUOTE. */
20QString
21parse_message_context(const QString &str)
22{
23 QString out = str.trimmed();
24 out = out.replace("\"", "");
25 return out;
26}
27
28/** Parse the context name from <b>str</b>, where <b>str</b> is of the
29 * form ContextName#Number. This is the format used by translate-toolkit. */
30QString
31parse_message_context_lame(const QString &str)
32{
33 if (str.contains("#"))
34 return str.section("#", 0, 0);
35 return QString();
36}
37
38/** Parse the PO-formatted message string from <b>msg</b>. If <b>msg</b> is a
39 * multiline string, the extra double quotes will be replaced with newlines
40 * appropriately. */
41QString
42parse_message_string(const QString &msg)
43{
44 QString out = msg.trimmed();
45
46 out.replace("\"\n\"", "");
47 if (out.startsWith("\""))
48 out = out.remove(0, 1);
49 if (out.endsWith("\""))
50 out.chop(1);
51 out.replace("\\\"", "\"");
52 out.replace("\\n", "\\r\\n");
53 out.replace("$\\r\\n", "$\\r$\\n");
54 return out;
55}
56
57/** Read and return the next non-empty line from <b>stream</b>. */
58QString
59read_next_line(QTextStream *stream)
60{
61 stream->skipWhiteSpace();
62 return stream->readLine().append("\n");
63}
64
65/** Skip past the header portion of the PO file and any leading whitespace.
66 * The next line read from <b>po</b> will be the first non-header line in the
67 * document. */
68void
69skip_po_header(QTextStream *po)
70{
71 QString line;
72 /* Skip any leading whitespace before the header */
73 po->skipWhiteSpace();
74 /* Read to the first empty line */
75 line = po->readLine();
76 while (!po->atEnd() && !line.isEmpty())
77 line = po->readLine();
78}
79
80/** Convert <b>po</b> from the PO format to a NSIS-formatted .nsh document.
81 * <b>nsh</b> will be set to the resulting .nsh document. Return the number of
82 * converted strings on success, or -1 on error and <b>errorMessage</b> will
83 * be set. */
84int
85po2nsh(QTextStream *po, QString *nsh, const QString &language,
86 QString *errorMessage)
87{
88 QString line;
89 QString msgctxt, msgid, msgstr;
90 QHash<QString,QString> header;
91 QTextCodec *codec;
92 int n_strings = 0;
93
94 Q_ASSERT(po);
95 Q_ASSERT(nsh);
96 Q_ASSERT(errorMessage);
97
99 line = read_next_line(po);
100 while (!po->atEnd()) {
101 /* Ignore all "#" lines except "#:" */
102 while (line.startsWith("#")) {
103 if (line.startsWith("#:")) {
104 /* Context was specified with the stupid overloaded "#:" syntax.*/
105 msgctxt = line.section(" ", 1);
106 msgctxt = parse_message_context_lame(msgctxt);
107 }
108 line = read_next_line(po);
109 }
110
111 /* A context specified on a "msgctxt" line takes precedence over a context
112 * specified using the overload "#:" notation. */
113 if (line.startsWith("msgctxt ")) {
114 msgctxt = line.section(" ", 1);
115 msgctxt = parse_message_context(msgctxt);
116 line = read_next_line(po);
117 }
118
119 /* Parse the (possibly multiline) message source string */
120 if (!line.startsWith("msgid ")) {
121 *errorMessage = "expected 'msgid' line";
122 return -1;
123 }
124 msgid = line.section(" ", 1);
125
126 line = read_next_line(po);
127 while (line.startsWith("\"")) {
128 msgid.append(line);
129 line = read_next_line(po);
130 }
131 msgid = parse_message_string(msgid);
132
133 /* Parse the (possibly multiline) translated string */
134 if (!line.startsWith("msgstr ")) {
135 *errorMessage = "expected 'msgstr' line";
136 return -1;
137 }
138 msgstr = line.section(" ", 1);
139
140 line = read_next_line(po);
141 while (line.startsWith("\"")) {
142 msgstr.append(line);
143 line = read_next_line(po);
144 }
145 msgstr = parse_message_string(msgstr);
146
147 /* Add the message translation to the .nsh document */
148 nsh->append(QString("LangString "));
149 nsh->append(msgctxt);
150 nsh->append(QString(" ${LANG_%1} ").arg(language));
151 if (msgstr.isEmpty())
152 nsh->append("\"" + msgid + "\"");
153 else
154 nsh->append("\"" + msgstr + "\"");
155 nsh->append("\n");
156
157 n_strings++;
158 }
159 return n_strings;
160}
161
162/** Display application usage and exit. */
163void
165{
166 QTextStream error(stderr);
167 error << "usage: po2nsh [-q] -i <infile.po> -o <outfile.nsh> "
168 "-l <language> [-f <from-encoding>] [-t <to-encoding>]\n";
169 error << " -q (optional) Quiet mode (errors are still displayed)\n";
170 error << " -i <infile.po> Input .po file\n";
171 error << " -o <outfile.nsh> Output .nsh file\n";
172 error << " -l <language> NSIS language table name\n";
173 error << " -f <from-encoding> .po file encoding (default: utf-8)\n";
174 error << " -t <to-encoding> .nsh file encoding (default: iso-8859-1)\n";
175 error.flush();
176 exit(1);
177}
178
179int
180main(int argc, char *argv[])
181{
182 QTextStream error(stderr);
183 QString language, errorMessage;
184 char *infile, *outfile;
185 bool quiet = false;
186 QTextCodec *from_codec = QTextCodec::codecForName("utf-8");
187 QTextCodec *to_codec = QTextCodec::codecForName("iso-8859-1");
188
189 /* Check for the correct number of input parameters. */
190 if (argc < 7 || argc > 12)
192 for (int i = 1; i < argc; i++) {
193 QString arg(argv[i]);
194 if (!arg.compare("-q", Qt::CaseInsensitive))
195 quiet = true;
196 else if (!arg.compare("-i", Qt::CaseInsensitive) && ++i < argc)
197 infile = argv[i];
198 else if (!arg.compare("-o", Qt::CaseInsensitive) && ++i < argc)
199 outfile = argv[i];
200 else if (!arg.compare("-l", Qt::CaseInsensitive) && ++i < argc)
201 language = QString(argv[i]).toUpper();
202 else if (!arg.compare("-f", Qt::CaseInsensitive) && ++i < argc) {
203 from_codec = QTextCodec::codecForName(argv[i]);
204 if (!from_codec) {
205 error << "Invalid input encoding: " << argv[i] << "\n";
206 return 1;
207 }
208 } else if (!arg.compare("-t", Qt::CaseInsensitive) && ++i < argc) {
209 to_codec = QTextCodec::codecForName(argv[i]);
210 if (!to_codec) {
211 error << "Invalid output encoding: " << argv[i] << "\n";
212 return 1;
213 }
214 } else
216 }
217
218 /* Open the input PO file for reading. */
219 QFile poFile(infile);
220 if (!poFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
221 error << QString("Unable to open '%1' for reading: %2\n").arg(infile)
222 .arg(poFile.errorString());
223 return 2;
224 }
225
226 QString nsh;
227 QTextStream po(&poFile);
228 po.setCodec(from_codec);
229 int n_strings = po2nsh(&po, &nsh, language, &errorMessage);
230 if (n_strings < 0) {
231 error << QString("Unable to convert '%1': %2\n").arg(infile)
232 .arg(errorMessage);
233 return 3;
234 }
235
236 /* Open the NSH file for writing. */
237 QFile nshFile(outfile);
238 if (!nshFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
239 error << QString("Unable to open '%1' for writing: %2\n").arg(outfile)
240 .arg(nshFile.errorString());
241 return 4;
242 }
243
244 /* Finally write the .nsh output. */
245 QTextStream out(&nshFile);
246 out.setCodec(to_codec);
247 out << nsh;
248
249 if (!quiet) {
250 QTextStream results(stdout);
251 results << QString("Converted %1 strings from %2 to %3.\n").arg(n_strings)
252 .arg(infile)
253 .arg(outfile);
254 }
255 return 0;
256}
257
DebugMessage arg(const QString &a)
Definition: tcglobal.h:48
QString i(QString str)
Definition: html.cpp:32
DebugMessage error(const QString &fmt)
Definition: tcglobal.cpp:40
QString parse_message_context_lame(const QString &str)
Definition: po2nsh.cpp:31
void print_usage_and_exit()
Definition: po2nsh.cpp:164
int main(int argc, char *argv[])
Definition: po2nsh.cpp:180
QString parse_message_string(const QString &msg)
Definition: po2nsh.cpp:42
QString read_next_line(QTextStream *stream)
Definition: po2nsh.cpp:59
QString parse_message_context(const QString &str)
Definition: po2nsh.cpp:21
int po2nsh(QTextStream *po, QString *nsh, const QString &language, QString *errorMessage)
Definition: po2nsh.cpp:85
void skip_po_header(QTextStream *po)
Definition: po2nsh.cpp:69