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. */
20 QString
21 parse_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. */
30 QString
31 parse_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. */
41 QString
42 parse_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>. */
58 QString
59 read_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. */
68 void
69 skip_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. */
84 int
85 po2nsh(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 
98  skip_po_header(po);
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. */
163 void
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 
179 int
180 main(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 error(const QString &fmt)
Definition: tcglobal.cpp:40
int po2nsh(QTextStream *po, QString *nsh, const QString &language, QString *errorMessage)
Definition: po2nsh.cpp:85
int main(int argc, char *argv[])
Definition: po2nsh.cpp:180
QString parse_message_string(const QString &msg)
Definition: po2nsh.cpp:42
QString i(QString str)
Definition: html.cpp:32
void skip_po_header(QTextStream *po)
Definition: po2nsh.cpp:69
void print_usage_and_exit()
Definition: po2nsh.cpp:164
QString parse_message_context_lame(const QString &str)
Definition: po2nsh.cpp:31
QString read_next_line(QTextStream *stream)
Definition: po2nsh.cpp:59
QString parse_message_context(const QString &str)
Definition: po2nsh.cpp:21