Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "sal/config.h"
21 :
22 : #include <cstddef>
23 : #include <cstdlib>
24 : #include <fstream>
25 : #include <iostream>
26 : #include <string>
27 :
28 : #include "boost/noncopyable.hpp"
29 : #include "osl/file.h"
30 : #include "osl/file.hxx"
31 : #include "osl/thread.h"
32 : #include "rtl/string.h"
33 : #include "rtl/string.hxx"
34 : #include "rtl/textcvt.h"
35 : #include "rtl/strbuf.hxx"
36 : #include "rtl/ustring.h"
37 : #include "rtl/ustring.hxx"
38 : #include "sal/macros.h"
39 : #include "sal/main.h"
40 : #include "sal/types.h"
41 :
42 : #include "po.hxx"
43 :
44 : using namespace std;
45 :
46 : namespace {
47 :
48 : class TempFile: private boost::noncopyable {
49 : public:
50 0 : TempFile() {
51 0 : if (osl::FileBase::createTempFile(0, 0, &url_) != osl::FileBase::E_None)
52 : {
53 0 : cerr << "osl::FileBase::createTempFile() failed\n";
54 0 : throw false; //TODO
55 : }
56 0 : }
57 :
58 0 : ~TempFile() {
59 0 : if (osl::File::remove(url_) != osl::FileBase::E_None) {
60 0 : cerr << "Warning: failure removing temporary " << url_ << '\n';
61 : }
62 0 : }
63 :
64 0 : OUString getUrl() const { return url_; }
65 :
66 : private:
67 : OUString url_;
68 : };
69 :
70 : struct AsciiString {
71 : char const * string;
72 : sal_Int32 length;
73 : };
74 :
75 0 : bool matchList(
76 : OUString const & url, AsciiString const * list, size_t length)
77 : {
78 0 : for (size_t i = 0; i != length; ++i) {
79 0 : if (url.endsWithAsciiL(list[i].string, list[i].length)) {
80 0 : return true;
81 : }
82 : }
83 0 : return false;
84 : }
85 :
86 0 : bool passesNegativeList(OUString const & url) {
87 : static AsciiString const list[] = {
88 : { RTL_CONSTASCII_STRINGPARAM("/dictionaries.xcu") },
89 : { RTL_CONSTASCII_STRINGPARAM(
90 : "/dictionaries/da_DK/help/da/help.tree") },
91 : { RTL_CONSTASCII_STRINGPARAM(
92 : "/dictionaries/da_DK/help/da/"
93 : "org.openoffice.da.hunspell.dictionaries/page1.xhp") },
94 : { RTL_CONSTASCII_STRINGPARAM(
95 : "/dictionaries/da_DK/help/da/"
96 : "org.openoffice.da.hunspell.dictionaries/page2.xhp") },
97 : { RTL_CONSTASCII_STRINGPARAM(
98 : "/dictionaries/hu_HU/help/hu/help.tree") },
99 : { RTL_CONSTASCII_STRINGPARAM(
100 : "/dictionaries/hu_HU/help/hu/"
101 : "org.openoffice.hu.hunspell.dictionaries/page1.xhp") },
102 : { RTL_CONSTASCII_STRINGPARAM("/hidother.src") },
103 : { RTL_CONSTASCII_STRINGPARAM(
104 : "/officecfg/registry/data/org/openoffice/Office/"
105 : "Accelerators.xcu") },
106 : { RTL_CONSTASCII_STRINGPARAM(
107 : "/officecfg/registry/data/org/openoffice/Office/Labels.xcu") },
108 : { RTL_CONSTASCII_STRINGPARAM(
109 : "/officecfg/registry/data/org/openoffice/Office/SFX.xcu") }
110 : };
111 0 : return !matchList(url, list, SAL_N_ELEMENTS(list));
112 : }
113 :
114 0 : bool passesPositiveList(OUString const & url) {
115 : static AsciiString const list[] = {
116 : { RTL_CONSTASCII_STRINGPARAM(
117 : "/chart2/source/controller/dialogs/res_DataLabel_tmpl.hrc") },
118 : { RTL_CONSTASCII_STRINGPARAM(
119 : "/chart2/source/controller/dialogs/res_ErrorBar_tmpl.hrc") },
120 : { RTL_CONSTASCII_STRINGPARAM(
121 : "/chart2/source/controller/dialogs/res_LegendPosition_tmpl.hrc") },
122 : { RTL_CONSTASCII_STRINGPARAM(
123 : "/chart2/source/controller/dialogs/"
124 : "res_SecondaryAxisCheckBoxes_tmpl.hrc") },
125 : { RTL_CONSTASCII_STRINGPARAM(
126 : "/chart2/source/controller/dialogs/res_Statistic_tmpl.hrc") },
127 : { RTL_CONSTASCII_STRINGPARAM(
128 : "/chart2/source/controller/dialogs/res_Titlesx_tmpl.hrc") },
129 : { RTL_CONSTASCII_STRINGPARAM(
130 : "/chart2/source/controller/dialogs/res_Trendline_tmpl.hrc") },
131 : { RTL_CONSTASCII_STRINGPARAM(
132 : "/chart2/source/controller/menu/MenuItems_tmpl.hrc") },
133 : { RTL_CONSTASCII_STRINGPARAM(
134 : "/dbaccess/source/ui/dlg/AutoControls_tmpl.hrc") },
135 : { RTL_CONSTASCII_STRINGPARAM(
136 : "/dbaccess/source/ui/inc/toolbox_tmpl.hrc") },
137 : { RTL_CONSTASCII_STRINGPARAM("/description.xml") },
138 : { RTL_CONSTASCII_STRINGPARAM("/android/sdremote/res/values/strings.xml") },
139 : { RTL_CONSTASCII_STRINGPARAM("/offmgr/inc/offmenu_tmpl.hrc") },
140 : { RTL_CONSTASCII_STRINGPARAM(
141 : "/offmgr/source/offapp/intro/intro_tmpl.hrc") },
142 : { RTL_CONSTASCII_STRINGPARAM("/svx/inc/globlmn_tmpl.hrc") },
143 : { RTL_CONSTASCII_STRINGPARAM("/svx/source/intro/intro_tmpl.hrc") },
144 : { RTL_CONSTASCII_STRINGPARAM(
145 : "/svx/source/unodialogs/textconversiondlgs/"
146 : "chinese_direction_tmpl.hrc") },
147 : { RTL_CONSTASCII_STRINGPARAM("/sw/source/ui/inc/swacc_tmpl.hrc") },
148 : { RTL_CONSTASCII_STRINGPARAM("/sw/source/ui/inc/swmn_tmpl.hrc") },
149 : { RTL_CONSTASCII_STRINGPARAM("/sw/source/ui/inc/toolbox_tmpl.hrc") }
150 : };
151 0 : return matchList(url, list, SAL_N_ELEMENTS(list));
152 : }
153 :
154 0 : void handleCommand(
155 : OString const & project, OString const & projectRoot,
156 : OUString const & url, OString const & actualPotDir,
157 : PoOfstream & rPoOutPut, OString const & executable, bool positive)
158 : {
159 0 : if (positive ? passesPositiveList(url) : passesNegativeList(url)) {
160 :
161 : //Get input file path
162 0 : OString inPath;
163 : {
164 0 : OUString inPathTmp;
165 0 : if (osl::FileBase::getSystemPathFromFileURL(url, inPathTmp) !=
166 : osl::FileBase::E_None)
167 : {
168 : cerr
169 0 : << "osl::FileBase::getSystemPathFromFileURL(" << url
170 0 : << ") failed\n";
171 0 : throw false; //TODO
172 : }
173 0 : inPath = OUStringToOString( inPathTmp, RTL_TEXTENCODING_UTF8 );
174 : }
175 :
176 : //Get output file path
177 0 : TempFile temp;
178 0 : OString outPath;
179 : {
180 0 : OUString outPathTmp;
181 0 : if (osl::FileBase::getSystemPathFromFileURL(temp.getUrl(),outPathTmp)
182 : != osl::FileBase::E_None)
183 : {
184 : cerr
185 0 : << "osl::FileBase::getSystemPathFromFileURL("
186 0 : << temp.getUrl() << ") failed\n";
187 0 : throw false; //TODO
188 : }
189 0 : outPath = OUStringToOString( outPathTmp, RTL_TEXTENCODING_UTF8 );
190 : }
191 :
192 : //Call the executable
193 : {
194 0 : OStringBuffer buf(OString(getenv("SOLARVER")));
195 0 : buf.append('/');
196 0 : buf.append(OString(getenv("INPATH_FOR_BUILD")));
197 0 : buf.append("/bin/");
198 0 : buf.append(executable);
199 0 : buf.append(" -p ");
200 0 : buf.append(project);
201 0 : buf.append(" -r ");
202 0 : buf.append(projectRoot);
203 0 : buf.append(" -i ");
204 0 : buf.append(inPath);
205 0 : buf.append(" -o ");
206 0 : buf.append(outPath);
207 0 : buf.append(" -l en-US");
208 :
209 0 : const OString cmd = buf.makeStringAndClear();
210 0 : if (system(cmd.getStr()) != 0) {
211 0 : cerr << "Error: Failed to execute " << cmd.getStr() << '\n';
212 0 : throw false; //TODO
213 0 : }
214 : }
215 :
216 0 : ifstream in(outPath.getStr());
217 0 : if (!in.is_open()) {
218 0 : cerr << "Error: Cannot open " << outPath.getStr() << "\n";
219 0 : throw false; //TODO
220 : }
221 :
222 0 : string s;
223 0 : getline(in, s);
224 0 : if (!in.eof() && !rPoOutPut.isOpen())
225 : {
226 : //Create directory for po file
227 : {
228 : OUString outDir =
229 : OStringToOUString(
230 : actualPotDir.copy(0,actualPotDir.lastIndexOf('/')),
231 0 : RTL_TEXTENCODING_UTF8);
232 0 : OUString outDirUrl;
233 0 : if (osl::FileBase::getFileURLFromSystemPath(outDir, outDirUrl)
234 : != osl::FileBase::E_None)
235 : {
236 0 : cerr << "Error: Cannot convert pathname to URL in " << __FILE__ << ", in line " << __LINE__ << "\n"
237 0 : << " outDir: " << OUStringToOString(outDir, RTL_TEXTENCODING_ASCII_US).getStr() << "\n";
238 0 : throw false; //TODO
239 : }
240 0 : osl::Directory::createPath(outDirUrl);
241 : }
242 :
243 : //Open po file
244 : {
245 0 : OString outFilePath = actualPotDir.concat(".pot");
246 0 : rPoOutPut.open(outFilePath.getStr());
247 0 : if (!rPoOutPut.isOpen())
248 : {
249 : cerr
250 0 : << "Error: Cannot open po file "
251 0 : << outFilePath.getStr() << "\n";
252 0 : throw false; //TODO
253 0 : }
254 : }
255 :
256 : //Add header to po file
257 : {
258 0 : const sal_Int32 nProjectInd = inPath.indexOf(project);
259 : const OString relativPath =
260 : inPath.copy(
261 0 : nProjectInd, inPath.lastIndexOf('/')- nProjectInd);
262 :
263 0 : PoHeader aTmp(relativPath);
264 0 : rPoOutPut.writeHeader(aTmp);
265 : }
266 : }
267 0 : while (!in.eof())
268 : {
269 0 : OString sLine = OString(s.data(),s.length());
270 : try
271 : {
272 0 : if (!sLine.getToken(PoEntry::TEXT,'\t').isEmpty())
273 0 : rPoOutPut.writeEntry(PoEntry(sLine));
274 0 : if (!sLine.getToken(PoEntry::QUICKHELPTEXT,'\t').isEmpty())
275 0 : rPoOutPut.writeEntry(PoEntry(sLine,PoEntry::TQUICKHELPTEXT));
276 0 : if (!sLine.getToken(PoEntry::TITLE,'\t').isEmpty())
277 0 : rPoOutPut.writeEntry(PoEntry(sLine,PoEntry::TTITLE));
278 : }
279 0 : catch(PoEntry::Exception& aException)
280 : {
281 0 : if(aException == PoEntry::INVALIDSDFLINE)
282 : {
283 : cerr
284 0 : << executable.getStr()
285 0 : << "'s output is invalid:\n"
286 0 : << sLine.replaceAll("\t","\\t").getStr()
287 0 : << endl;
288 0 : throw false; //TODO
289 : }
290 : }
291 0 : getline(in, s);
292 0 : };
293 0 : in.close();
294 : }
295 0 : }
296 :
297 0 : void handleFile(
298 : OString const & project, OString const & projectRoot,
299 : OUString const & url, OString const & actualPotDir,
300 : PoOfstream & rPoOutPut)
301 : {
302 0 : struct Command {
303 : char const * extension;
304 : sal_Int32 extensionLength;
305 : OString executable;
306 : bool positive;
307 : };
308 : static Command const commands[] = {
309 : { RTL_CONSTASCII_STRINGPARAM(".src"), "transex3", false },
310 : { RTL_CONSTASCII_STRINGPARAM(".hrc"), "transex3", true },
311 : { RTL_CONSTASCII_STRINGPARAM(".ulf"), "ulfex", false },
312 : { RTL_CONSTASCII_STRINGPARAM(".xcu"), "cfgex", false },
313 : { RTL_CONSTASCII_STRINGPARAM(".xrm"), "xrmex", false },
314 : { RTL_CONSTASCII_STRINGPARAM("description.xml"), "xrmex", true },
315 : { RTL_CONSTASCII_STRINGPARAM("strings.xml"), "stringex", true },
316 : { RTL_CONSTASCII_STRINGPARAM(".xhp"), "helpex", false },
317 : { RTL_CONSTASCII_STRINGPARAM(".properties"), "propex", false },
318 : { RTL_CONSTASCII_STRINGPARAM(".ui"), "uiex", false },
319 0 : { RTL_CONSTASCII_STRINGPARAM(".tree"), "treex", false } };
320 0 : for (size_t i = 0; i != SAL_N_ELEMENTS(commands); ++i)
321 : {
322 0 : if (url.endsWithAsciiL(
323 0 : commands[i].extension, commands[i].extensionLength) &&
324 0 : (commands[i].executable != "propex" || url.indexOf("en_US") != -1)
325 : )
326 : {
327 : handleCommand(
328 : project, projectRoot, url, actualPotDir, rPoOutPut,
329 0 : commands[i].executable, commands[i].positive);
330 0 : break;
331 : }
332 : }
333 0 : }
334 :
335 0 : bool includeProject(OString const & project) {
336 : static OString projects[] = {
337 : "accessibility",
338 : "android",
339 : "avmedia",
340 : "basctl",
341 : "basic",
342 : "chart2",
343 : "connectivity",
344 : "cui",
345 : "dbaccess",
346 : "desktop",
347 : "dictionaries",
348 : "editeng",
349 : "extensions",
350 : "filter",
351 : "forms",
352 : "formula",
353 : "fpicker",
354 : "framework",
355 : "helpcontent2",
356 : "instsetoo_native",
357 : "librelogo",
358 : "mysqlc",
359 : "nlpsolver",
360 : "officecfg",
361 : "padmin",
362 : "readlicense_oo",
363 : "reportbuilder",
364 : "reportdesign",
365 : "sc",
366 : "scaddins",
367 : "sccomp",
368 : "scp2",
369 : "sd",
370 : "sdext",
371 : "setup_native",
372 : "sfx2",
373 : "shell",
374 : "starmath",
375 : "svl",
376 : "svtools",
377 : "svx",
378 : "sw",
379 : "swext",
380 : "sysui",
381 : "tubes",
382 : "uui",
383 : "vcl",
384 : "wizards",
385 0 : "xmlsecurity" };
386 0 : for (size_t i = 0; i != SAL_N_ELEMENTS(projects); ++i) {
387 0 : if (project == projects[i]) {
388 0 : return true;
389 : }
390 : }
391 0 : return false;
392 : }
393 :
394 0 : bool excludeDirectory(OString const & directory) {
395 : // Cf. OUTPATH=* in configure.in:
396 : static OString const excluded[] = {
397 : "callcatcher",
398 : "unxaig",
399 : "unxand",
400 : "unxdfly",
401 : "unxfbsd",
402 : "unxios",
403 : "unxkfg",
404 : "unxlng",
405 : "unxmac",
406 : "unxnbsd",
407 : "unxobsd",
408 : "unxsog",
409 : "unxsol",
410 : "unxubt",
411 0 : "wntmsc" };
412 0 : for (size_t i = 0; i != SAL_N_ELEMENTS(excluded); ++i) {
413 0 : if (directory.startsWith(excluded[i])) {
414 0 : return true;
415 : }
416 : }
417 0 : return false;
418 : }
419 :
420 : /// Handle one directory in the hierarchy.
421 : ///
422 : /// Ignores symlinks and instead explicitly descends into clone/* or src/*,
423 : /// as the Cygwin symlinks are not supported by osl::Directory on Windows.
424 : ///
425 : /// @param url the absolute file URL of this directory
426 : ///
427 : /// @param level 0 if this is either the root directory that contains the
428 : /// projects or one of the clone/* or src/* directories that contain the
429 : /// additional projects; -1 if this is the clone directory; 1 if this
430 : /// is a project directory; 2 if this is a directory inside a project
431 : ///
432 : /// @param project the name of the project (empty and ignored if level <= 0)
433 : ///
434 : /// @param the relative path back to the project root (empty and ignored if
435 : /// level <= 0)
436 : /// @param actualPotDir the path of pot directory
437 0 : void handleDirectory(
438 : OUString const & url, int level, OString const & project,
439 : OString const & projectRoot, OString const & actualPotDir)
440 : {
441 0 : PoOfstream aPoOutPut;
442 0 : osl::Directory dir(url);
443 0 : if (dir.open() != osl::FileBase::E_None) {
444 : cerr
445 0 : << "Error: Cannot open directory: " << url << '\n';
446 0 : throw false; //TODO
447 : }
448 0 : for (;;) {
449 0 : osl::DirectoryItem item;
450 0 : osl::FileBase::RC e = dir.getNextItem(item);
451 0 : if (e == osl::FileBase::E_NOENT) {
452 : break;
453 : }
454 0 : if (e != osl::FileBase::E_None) {
455 0 : cerr << "Error: Cannot read directory\n";
456 0 : throw false; //TODO
457 : }
458 : osl::FileStatus stat(
459 : osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName
460 0 : | osl_FileStatus_Mask_FileURL);
461 0 : if (item.getFileStatus(stat) != osl::FileBase::E_None) {
462 0 : cerr << "Error: Cannot get file status\n";
463 0 : throw false; //TODO
464 : }
465 : const OString sFileName =
466 0 : OUStringToOString(stat.getFileName(),RTL_TEXTENCODING_UTF8);
467 0 : switch (level) {
468 : case -1: // the clone or src directory
469 0 : if (stat.getFileType() == osl::FileStatus::Directory) {
470 : handleDirectory(
471 : stat.getFileURL(), 0, OString(),
472 0 : OString(), actualPotDir);
473 : }
474 0 : break;
475 : case 0: // a root directory
476 0 : if (stat.getFileType() == osl::FileStatus::Directory) {
477 0 : if (includeProject(sFileName)) {
478 : handleDirectory(
479 : stat.getFileURL(), 1, sFileName,
480 : OString(), actualPotDir.concat("/").
481 0 : concat(sFileName));
482 0 : } else if ( sFileName == "clone" ||
483 0 : sFileName == "src" )
484 : {
485 : handleDirectory(
486 : stat.getFileURL(), -1, OString(),
487 0 : OString(), actualPotDir);
488 : }
489 : }
490 0 : break;
491 : default:
492 0 : if (stat.getFileType() == osl::FileStatus::Directory) {
493 0 : if (level == 2 || !excludeDirectory(sFileName)) {
494 0 : OString pr(projectRoot);
495 0 : if (!pr.isEmpty()) {
496 0 : pr += OString('/');
497 : }
498 0 : pr += OString("..");
499 : handleDirectory(stat.getFileURL(), 2, project, pr,
500 0 : actualPotDir.concat("/").concat(sFileName));
501 : }
502 : } else {
503 : handleFile(project, projectRoot,
504 0 : stat.getFileURL(), actualPotDir, aPoOutPut);
505 : }
506 0 : break;
507 : }
508 0 : }
509 0 : if (aPoOutPut.isOpen())
510 0 : aPoOutPut.close();
511 0 : if (dir.close() != osl::FileBase::E_None) {
512 0 : cerr << "Error: Cannot close directory\n";
513 0 : throw false; //TODO
514 0 : }
515 0 : }
516 :
517 0 : void handleProjects(char * sourceRoot, char const * destRoot)
518 : {
519 0 : OUString root16;
520 0 : if (!rtl_convertStringToUString(
521 : &root16.pData, sourceRoot, rtl_str_getLength(sourceRoot),
522 0 : osl_getThreadTextEncoding(),
523 : (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
524 : | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
525 0 : | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
526 : {
527 0 : cerr << "Error: Cannot convert pathname to UTF-16\n";
528 0 : throw false; //TODO
529 : }
530 0 : OUString rootUrl;
531 0 : if (osl::FileBase::getFileURLFromSystemPath(root16, rootUrl)
532 : != osl::FileBase::E_None)
533 : {
534 0 : cerr << "Error: Cannot convert pathname to URL in " << __FILE__ << ", in line " << __LINE__ << "\n"
535 0 : << " root16: " << OUStringToOString(root16, RTL_TEXTENCODING_ASCII_US).getStr() << "\n";
536 0 : throw false; //TODO
537 : }
538 0 : handleDirectory(rootUrl, 0, OString(), OString(), OString(destRoot));
539 0 : }
540 : }
541 :
542 0 : SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) {
543 0 : if (argc != 3) {
544 : cerr
545 : << ("localize (c)2001 by Sun Microsystems\n\n"
546 : "As part of the L10N framework, localize extracts en-US\n"
547 : "strings for translation out of the toplevel modules defined\n"
548 : "in projects array in l10ntools/source/localize.cxx.\n\n"
549 0 : "Syntax: localize <source-root> <outfile>\n");
550 0 : exit(EXIT_FAILURE);
551 : }
552 : try {
553 0 : handleProjects(argv[1],argv[2]);
554 0 : } catch (bool) { //TODO
555 0 : return EXIT_FAILURE;
556 : }
557 0 : return EXIT_SUCCESS;
558 0 : }
559 :
560 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|