1 /* Extracts strings from C source file to Uniforum style .po file.
2 Copyright (C) 1995-1998, 2000-2016, 2018-2020 Free Software Foundation, Inc.
3 Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <alloca.h>
22
23 /* Specification. */
24 #include "xgettext.h"
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <getopt.h>
29 #include <stdio.h>
30 #include <time.h>
31 #include <stdlib.h>
32 #include <stdbool.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <locale.h>
36 #include <limits.h>
37
38 #if HAVE_ICONV
39 #include <iconv.h>
40 #endif
41
42 #include <textstyle.h>
43
44 #include "noreturn.h"
45 #include "rc-str-list.h"
46 #include "xg-encoding.h"
47 #include "xg-arglist-context.h"
48 #include "xg-message.h"
49 #include "closeout.h"
50 #include "dir-list.h"
51 #include "file-list.h"
52 #include "str-list.h"
53 #include "error.h"
54 #include "error-progname.h"
55 #include "progname.h"
56 #include "relocatable.h"
57 #include "basename-lgpl.h"
58 #include "xerror.h"
59 #include "xvasprintf.h"
60 #include "xalloc.h"
61 #include "xmalloca.h"
62 #include "c-strstr.h"
63 #include "xerror.h"
64 #include "filename.h"
65 #include "concat-filename.h"
66 #include "c-strcase.h"
67 #include "open-catalog.h"
68 #include "read-catalog-abstract.h"
69 #include "read-po.h"
70 #include "message.h"
71 #include "po-charset.h"
72 #include "msgl-iconv.h"
73 #include "msgl-ascii.h"
74 #include "msgl-check.h"
75 #include "po-time.h"
76 #include "write-catalog.h"
77 #include "write-po.h"
78 #include "write-properties.h"
79 #include "write-stringtable.h"
80 #include "format.h"
81 #include "propername.h"
82 #include "sentence.h"
83 #include "its.h"
84 #include "locating-rule.h"
85 #include "search-path.h"
86 #include "gettext.h"
87
88 /* A convenience macro. I don't like writing gettext() every time. */
89 #define _(str) gettext (str)
90
91
92 #include "x-po.h"
93 #include "x-properties.h"
94 #include "x-stringtable.h"
95 #include "x-c.h"
96 #include "x-python.h"
97 #include "x-java.h"
98 #include "x-csharp.h"
99 #include "x-javascript.h"
100 #include "x-scheme.h"
101 #include "x-lisp.h"
102 #include "x-elisp.h"
103 #include "x-librep.h"
104 #include "x-ruby.h"
105 #include "x-sh.h"
106 #include "x-awk.h"
107 #include "x-lua.h"
108 #include "x-smalltalk.h"
109 #include "x-vala.h"
110 #include "x-tcl.h"
111 #include "x-perl.h"
112 #include "x-php.h"
113 #include "x-ycp.h"
114 #include "x-rst.h"
115 #include "x-desktop.h"
116 #include "x-glade.h"
117 #include "x-gsettings.h"
118 #include "x-appdata.h"
119
120
121 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
122 #define ENDOF(a) ((a) + SIZEOF(a))
123
124
125 /* If true, add all comments immediately preceding one of the keywords. */
126 bool add_all_comments = false;
127
128 /* Tag used in comment of prevailing domain. */
129 char *comment_tag;
130
131 /* Name of default domain file. If not set defaults to messages.po. */
132 static const char *default_domain;
133
134 /* If called with --debug option the output reflects whether format
135 string recognition is done automatically or forced by the user. */
136 static int do_debug;
137
138 /* Content of .po files with symbols to be excluded. */
139 message_list_ty *exclude;
140
141 /* Force output of PO file even if empty. */
142 static int force_po;
143
144 /* Copyright holder of the output file and the translations. */
145 static const char *copyright_holder = "THE PACKAGE'S COPYRIGHT HOLDER";
146
147 /* Package name. */
148 static const char *package_name = NULL;
149
150 /* Package version. */
151 static const char *package_version = NULL;
152
153 /* Email address or URL for reports of bugs in msgids. */
154 static const char *msgid_bugs_address = NULL;
155
156 /* String used as prefix for msgstr. */
157 const char *msgstr_prefix;
158
159 /* String used as suffix for msgstr. */
160 const char *msgstr_suffix;
161
162 /* Directory in which output files are created. */
163 static char *output_dir;
164
165 /* The output syntax: .pot or .properties or .strings. */
166 static catalog_output_format_ty output_syntax = &output_format_po;
167
168 /* If nonzero omit header with information about this run. */
169 int xgettext_omit_header;
170
171 /* Be more verbose. */
172 int verbose = 0;
173
174 /* Table of flag_context_list_ty tables. */
175 static flag_context_list_table_ty flag_table_c;
176 static flag_context_list_table_ty flag_table_cxx_qt;
177 static flag_context_list_table_ty flag_table_cxx_kde;
178 static flag_context_list_table_ty flag_table_cxx_boost;
179 static flag_context_list_table_ty flag_table_objc;
180 static flag_context_list_table_ty flag_table_gcc_internal;
181 static flag_context_list_table_ty flag_table_python;
182 static flag_context_list_table_ty flag_table_java;
183 static flag_context_list_table_ty flag_table_csharp;
184 static flag_context_list_table_ty flag_table_javascript;
185 static flag_context_list_table_ty flag_table_scheme;
186 static flag_context_list_table_ty flag_table_lisp;
187 static flag_context_list_table_ty flag_table_elisp;
188 static flag_context_list_table_ty flag_table_librep;
189 static flag_context_list_table_ty flag_table_ruby;
190 static flag_context_list_table_ty flag_table_sh;
191 static flag_context_list_table_ty flag_table_awk;
192 static flag_context_list_table_ty flag_table_lua;
193 static flag_context_list_table_ty flag_table_vala;
194 static flag_context_list_table_ty flag_table_tcl;
195 static flag_context_list_table_ty flag_table_perl;
196 static flag_context_list_table_ty flag_table_php;
197 static flag_context_list_table_ty flag_table_ycp;
198
199 /* If true, recognize Qt format strings. */
200 static bool recognize_format_qt;
201
202 /* If true, recognize KDE format strings. */
203 static bool recognize_format_kde;
204
205 /* If true, recognize Boost format strings. */
206 static bool recognize_format_boost;
207
208 /* Syntax checks enabled by default. */
209 enum is_syntax_check default_syntax_check[NSYNTAXCHECKS];
210
211 static locating_rule_list_ty *its_locating_rules;
212
213 #define ITS_ROOT_UNTRANSLATABLE \
214 "<its:rules xmlns:its=\"http://www.w3.org/2005/11/its\"" \
215 " version=\"2.0\">" \
216 " <its:translateRule selector=\"/*\" translate=\"no\"/>" \
217 "</its:rules>"
218
219 /* If nonzero add comments used by itstool. */
220 static bool add_itstool_comments = false;
221
222 /* Long options. */
223 static const struct option long_options[] =
224 {
225 { "add-comments", optional_argument, NULL, 'c' },
226 { "add-location", optional_argument, NULL, 'n' },
227 { "boost", no_argument, NULL, CHAR_MAX + 11 },
228 { "c++", no_argument, NULL, 'C' },
229 { "check", required_argument, NULL, CHAR_MAX + 17 },
230 { "color", optional_argument, NULL, CHAR_MAX + 14 },
231 { "copyright-holder", required_argument, NULL, CHAR_MAX + 1 },
232 { "debug", no_argument, &do_debug, 1 },
233 { "default-domain", required_argument, NULL, 'd' },
234 { "directory", required_argument, NULL, 'D' },
235 { "escape", no_argument, NULL, 'E' },
236 { "exclude-file", required_argument, NULL, 'x' },
237 { "extract-all", no_argument, NULL, 'a' },
238 { "files-from", required_argument, NULL, 'f' },
239 { "flag", required_argument, NULL, CHAR_MAX + 8 },
240 { "force-po", no_argument, &force_po, 1 },
241 { "foreign-user", no_argument, NULL, CHAR_MAX + 2 },
242 { "from-code", required_argument, NULL, CHAR_MAX + 3 },
243 { "help", no_argument, NULL, 'h' },
244 { "indent", no_argument, NULL, 'i' },
245 { "its", required_argument, NULL, CHAR_MAX + 20 },
246 { "itstool", no_argument, NULL, CHAR_MAX + 19 },
247 { "join-existing", no_argument, NULL, 'j' },
248 { "kde", no_argument, NULL, CHAR_MAX + 10 },
249 { "keyword", optional_argument, NULL, 'k' },
250 { "language", required_argument, NULL, 'L' },
251 { "msgid-bugs-address", required_argument, NULL, CHAR_MAX + 5 },
252 { "msgstr-prefix", optional_argument, NULL, 'm' },
253 { "msgstr-suffix", optional_argument, NULL, 'M' },
254 { "no-escape", no_argument, NULL, 'e' },
255 { "no-location", no_argument, NULL, CHAR_MAX + 16 },
256 { "no-wrap", no_argument, NULL, CHAR_MAX + 4 },
257 { "omit-header", no_argument, &xgettext_omit_header, 1 },
258 { "output", required_argument, NULL, 'o' },
259 { "output-dir", required_argument, NULL, 'p' },
260 { "package-name", required_argument, NULL, CHAR_MAX + 12 },
261 { "package-version", required_argument, NULL, CHAR_MAX + 13 },
262 { "properties-output", no_argument, NULL, CHAR_MAX + 6 },
263 { "qt", no_argument, NULL, CHAR_MAX + 9 },
264 { "sentence-end", required_argument, NULL, CHAR_MAX + 18 },
265 { "sort-by-file", no_argument, NULL, 'F' },
266 { "sort-output", no_argument, NULL, 's' },
267 { "strict", no_argument, NULL, 'S' },
268 { "string-limit", required_argument, NULL, 'l' },
269 { "stringtable-output", no_argument, NULL, CHAR_MAX + 7 },
270 { "style", required_argument, NULL, CHAR_MAX + 15 },
271 { "trigraphs", no_argument, NULL, 'T' },
272 { "verbose", no_argument, NULL, 'v' },
273 { "version", no_argument, NULL, 'V' },
274 { "width", required_argument, NULL, 'w' },
275 { NULL, 0, NULL, 0 }
276 };
277
278
279 /* The extractors must all be functions returning void and taking as arguments
280 - the file name or file stream,
281 - the flag table,
282 - a message domain list argument in which to add the messages.
283 An extract_from_stream_func is preferred, because it supports extracting from
284 stdin. */
285 typedef void (*extract_from_stream_func) (FILE *fp, const char *real_filename,
286 const char *logical_filename,
287 flag_context_list_table_ty *flag_table,
288 msgdomain_list_ty *mdlp);
289 typedef void (*extract_from_file_func) (const char *real_filename,
290 const char *logical_filename,
291 flag_context_list_table_ty *flag_table,
292 msgdomain_list_ty *mdlp);
293
294 typedef struct extractor_ty extractor_ty;
295 struct extractor_ty
296 {
297 extract_from_stream_func extract_from_stream;
298 extract_from_file_func extract_from_file;
299 flag_context_list_table_ty *flag_table;
300 struct formatstring_parser *formatstring_parser1;
301 struct formatstring_parser *formatstring_parser2;
302 struct formatstring_parser *formatstring_parser3;
303 };
304
305
306 /* Forward declaration of local functions. */
307 _GL_NORETURN_FUNC static void usage (int status);
308 static void read_exclusion_file (char *file_name);
309 static void extract_from_file (const char *file_name, extractor_ty extractor,
310 msgdomain_list_ty *mdlp);
311 static void extract_from_xml_file (const char *file_name,
312 its_rule_list_ty *rules,
313 msgdomain_list_ty *mdlp);
314 static message_ty *construct_header (void);
315 static void finalize_header (msgdomain_list_ty *mdlp);
316 static extractor_ty language_to_extractor (const char *name);
317 static const char *extension_to_language (const char *extension);
318
319
320 int
main(int argc,char * argv[])321 main (int argc, char *argv[])
322 {
323 int optchar;
324 bool do_help = false;
325 bool do_version = false;
326 msgdomain_list_ty *mdlp;
327 bool join_existing = false;
328 bool no_default_keywords = false;
329 bool some_additional_keywords = false;
330 bool sort_by_msgid = false;
331 bool sort_by_filepos = false;
332 char **dirs;
333 char **its_dirs = NULL;
334 char *explicit_its_filename = NULL;
335 const char *file_name;
336 const char *files_from = NULL;
337 string_list_ty *file_list;
338 char *output_file = NULL;
339 const char *language = NULL;
340 extractor_ty extractor = { NULL, NULL, NULL, NULL };
341 int cnt;
342 size_t i;
343
344 /* Set program name for messages. */
345 set_program_name (argv[0]);
346 error_print_progname = maybe_print_progname;
347
348 /* Set locale via LC_ALL. */
349 setlocale (LC_ALL, "");
350
351 /* Set the text message domain. */
352 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
353 bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
354 textdomain (PACKAGE);
355
356 /* Ensure that write errors on stdout are detected. */
357 atexit (close_stdout);
358
359 /* Set initial value of variables. */
360 default_domain = MESSAGE_DOMAIN_DEFAULT;
361 xgettext_global_source_encoding = NULL;
362 init_flag_table_c ();
363 init_flag_table_objc ();
364 init_flag_table_kde ();
365 init_flag_table_python ();
366 init_flag_table_java ();
367 init_flag_table_csharp ();
368 init_flag_table_javascript ();
369 init_flag_table_scheme ();
370 init_flag_table_lisp ();
371 init_flag_table_elisp ();
372 init_flag_table_librep ();
373 init_flag_table_ruby ();
374 init_flag_table_sh ();
375 init_flag_table_awk ();
376 init_flag_table_lua ();
377 init_flag_table_vala ();
378 init_flag_table_tcl ();
379 init_flag_table_perl ();
380 init_flag_table_php ();
381 init_flag_table_gcc_internal ();
382 init_flag_table_ycp ();
383
384 while ((optchar = getopt_long (argc, argv,
385 "ac::Cd:D:eEf:Fhijk::l:L:m::M::no:p:sTvVw:W:x:",
386 long_options, NULL)) != EOF)
387 switch (optchar)
388 {
389 case '\0': /* Long option. */
390 break;
391
392 case 'a':
393 x_c_extract_all ();
394 x_sh_extract_all ();
395 x_python_extract_all ();
396 x_lisp_extract_all ();
397 x_elisp_extract_all ();
398 x_librep_extract_all ();
399 x_scheme_extract_all ();
400 x_java_extract_all ();
401 x_csharp_extract_all ();
402 x_awk_extract_all ();
403 x_tcl_extract_all ();
404 x_perl_extract_all ();
405 x_php_extract_all ();
406 x_ruby_extract_all ();
407 x_lua_extract_all ();
408 x_javascript_extract_all ();
409 x_vala_extract_all ();
410 break;
411
412 case 'c':
413 if (optarg == NULL)
414 {
415 add_all_comments = true;
416 comment_tag = NULL;
417 }
418 else
419 {
420 add_all_comments = false;
421 comment_tag = optarg;
422 /* We ignore leading white space. */
423 while (isspace ((unsigned char) *comment_tag))
424 ++comment_tag;
425 }
426 break;
427
428 case 'C':
429 language = "C++";
430 break;
431
432 case 'd':
433 default_domain = optarg;
434 break;
435
436 case 'D':
437 dir_list_append (optarg);
438 break;
439
440 case 'e':
441 message_print_style_escape (false);
442 break;
443
444 case 'E':
445 message_print_style_escape (true);
446 break;
447
448 case 'f':
449 files_from = optarg;
450 break;
451
452 case 'F':
453 sort_by_filepos = true;
454 break;
455
456 case 'h':
457 do_help = true;
458 break;
459
460 case 'i':
461 message_print_style_indent ();
462 break;
463
464 case 'j':
465 join_existing = true;
466 break;
467
468 case 'k':
469 if (optarg != NULL && *optarg == '\0')
470 /* Make "--keyword=" work like "--keyword" and "-k". */
471 optarg = NULL;
472 x_c_keyword (optarg);
473 x_objc_keyword (optarg);
474 x_sh_keyword (optarg);
475 x_python_keyword (optarg);
476 x_lisp_keyword (optarg);
477 x_elisp_keyword (optarg);
478 x_librep_keyword (optarg);
479 x_scheme_keyword (optarg);
480 x_java_keyword (optarg);
481 x_csharp_keyword (optarg);
482 x_awk_keyword (optarg);
483 x_tcl_keyword (optarg);
484 x_perl_keyword (optarg);
485 x_php_keyword (optarg);
486 x_ruby_keyword (optarg);
487 x_lua_keyword (optarg);
488 x_javascript_keyword (optarg);
489 x_vala_keyword (optarg);
490 x_desktop_keyword (optarg);
491 if (optarg == NULL)
492 no_default_keywords = true;
493 else
494 some_additional_keywords = true;
495 break;
496
497 case 'l':
498 /* Accepted for backward compatibility with 0.10.35. */
499 break;
500
501 case 'L':
502 language = optarg;
503 break;
504
505 case 'm':
506 /* -m takes an optional argument. If none is given "" is assumed. */
507 msgstr_prefix = optarg == NULL ? "" : optarg;
508 break;
509
510 case 'M':
511 /* -M takes an optional argument. If none is given "" is assumed. */
512 msgstr_suffix = optarg == NULL ? "" : optarg;
513 break;
514
515 case 'n':
516 if (handle_filepos_comment_option (optarg))
517 usage (EXIT_FAILURE);
518 break;
519
520 case 'o':
521 output_file = optarg;
522 break;
523
524 case 'p':
525 {
526 size_t len = strlen (optarg);
527
528 if (output_dir != NULL)
529 free (output_dir);
530
531 if (optarg[len - 1] == '/')
532 output_dir = xstrdup (optarg);
533 else
534 output_dir = xasprintf ("%s/", optarg);
535 }
536 break;
537
538 case 's':
539 sort_by_msgid = true;
540 break;
541
542 case 'S':
543 message_print_style_uniforum ();
544 break;
545
546 case 'T':
547 x_c_trigraphs ();
548 break;
549
550 case 'v':
551 verbose++;
552 break;
553
554 case 'V':
555 do_version = true;
556 break;
557
558 case 'w':
559 {
560 int value;
561 char *endp;
562 value = strtol (optarg, &endp, 10);
563 if (endp != optarg)
564 message_page_width_set (value);
565 }
566 break;
567
568 case 'x':
569 read_exclusion_file (optarg);
570 break;
571
572 case CHAR_MAX + 1: /* --copyright-holder */
573 copyright_holder = optarg;
574 break;
575
576 case CHAR_MAX + 2: /* --foreign-user */
577 copyright_holder = "";
578 break;
579
580 case CHAR_MAX + 3: /* --from-code */
581 xgettext_global_source_encoding = po_charset_canonicalize (optarg);
582 if (xgettext_global_source_encoding == NULL)
583 {
584 multiline_warning (xasprintf (_("warning: ")),
585 xasprintf (_("'%s' is not a valid encoding name. Using ASCII as fallback.\n"),
586 optarg));
587 xgettext_global_source_encoding = po_charset_ascii;
588 }
589 break;
590
591 case CHAR_MAX + 4: /* --no-wrap */
592 message_page_width_ignore ();
593 break;
594
595 case CHAR_MAX + 5: /* --msgid-bugs-address */
596 msgid_bugs_address = optarg;
597 break;
598
599 case CHAR_MAX + 6: /* --properties-output */
600 output_syntax = &output_format_properties;
601 break;
602
603 case CHAR_MAX + 7: /* --stringtable-output */
604 output_syntax = &output_format_stringtable;
605 break;
606
607 case CHAR_MAX + 8: /* --flag */
608 xgettext_record_flag (optarg);
609 break;
610
611 case CHAR_MAX + 9: /* --qt */
612 recognize_format_qt = true;
613 break;
614
615 case CHAR_MAX + 10: /* --kde */
616 recognize_format_kde = true;
617 activate_additional_keywords_kde ();
618 break;
619
620 case CHAR_MAX + 11: /* --boost */
621 recognize_format_boost = true;
622 break;
623
624 case CHAR_MAX + 12: /* --package-name */
625 package_name = optarg;
626 break;
627
628 case CHAR_MAX + 13: /* --package-version */
629 package_version = optarg;
630 break;
631
632 case CHAR_MAX + 14: /* --color */
633 if (handle_color_option (optarg) || color_test_mode)
634 usage (EXIT_FAILURE);
635 break;
636
637 case CHAR_MAX + 15: /* --style */
638 handle_style_option (optarg);
639 break;
640
641 case CHAR_MAX + 16: /* --no-location */
642 message_print_style_filepos (filepos_comment_none);
643 break;
644
645 case CHAR_MAX + 17: /* --check */
646 for (i = 0; i < NSYNTAXCHECKS; i++)
647 {
648 if (strcmp (optarg, syntax_check_name[i]) == 0)
649 {
650 default_syntax_check[i] = yes;
651 break;
652 }
653 }
654 if (i == NSYNTAXCHECKS)
655 error (EXIT_FAILURE, 0, _("syntax check '%s' unknown"), optarg);
656 break;
657
658 case CHAR_MAX + 18: /* --sentence-end */
659 if (strcmp (optarg, "single-space") == 0)
660 sentence_end_required_spaces = 1;
661 else if (strcmp (optarg, "double-space") == 0)
662 sentence_end_required_spaces = 2;
663 else
664 error (EXIT_FAILURE, 0, _("sentence end type '%s' unknown"), optarg);
665 break;
666
667 case CHAR_MAX + 20: /* --its */
668 explicit_its_filename = optarg;
669 break;
670
671 case CHAR_MAX + 19: /* --itstool */
672 add_itstool_comments = true;
673 break;
674
675 default:
676 usage (EXIT_FAILURE);
677 /* NOTREACHED */
678 }
679
680 /* Version information requested. */
681 if (do_version)
682 {
683 printf ("%s (GNU %s) %s\n", last_component (program_name),
684 PACKAGE, VERSION);
685 /* xgettext: no-wrap */
686 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
687 License GPLv3+: GNU GPL version 3 or later <%s>\n\
688 This is free software: you are free to change and redistribute it.\n\
689 There is NO WARRANTY, to the extent permitted by law.\n\
690 "),
691 "1995-2020", "https://gnu.org/licenses/gpl.html");
692 printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
693 exit (EXIT_SUCCESS);
694 }
695
696 /* Help is requested. */
697 if (do_help)
698 usage (EXIT_SUCCESS);
699
700 /* Verify selected options. */
701 if (sort_by_msgid && sort_by_filepos)
702 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
703 "--sort-output", "--sort-by-file");
704
705 /* We cannot support both Qt and KDE, or Qt and Boost, or KDE and Boost
706 format strings, because there are only two formatstring parsers per
707 language, and formatstring_c is the first one for C++. */
708 if (recognize_format_qt && recognize_format_kde)
709 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
710 "--qt", "--kde");
711 if (recognize_format_qt && recognize_format_boost)
712 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
713 "--qt", "--boost");
714 if (recognize_format_kde && recognize_format_boost)
715 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
716 "--kde", "--boost");
717
718 if (join_existing && strcmp (default_domain, "-") == 0)
719 error (EXIT_FAILURE, 0,
720 _("--join-existing cannot be used when output is written to stdout"));
721
722 if (no_default_keywords && !some_additional_keywords)
723 {
724 error (0, 0, _("\
725 xgettext cannot work without keywords to look for"));
726 usage (EXIT_FAILURE);
727 }
728
729 /* Test whether we have some input files given. */
730 if (files_from == NULL && optind >= argc)
731 {
732 error (EXIT_SUCCESS, 0, _("no input file given"));
733 usage (EXIT_FAILURE);
734 }
735
736 /* Explicit ITS file selection and language specification are
737 mutually exclusive. */
738 if (explicit_its_filename != NULL && language != NULL)
739 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
740 "--its", "--language");
741
742 if (explicit_its_filename == NULL)
743 {
744 its_dirs = get_search_path ("its");
745 its_locating_rules = locating_rule_list_alloc ();
746 for (dirs = its_dirs; *dirs != NULL; dirs++)
747 locating_rule_list_add_from_directory (its_locating_rules, *dirs);
748 }
749
750 /* Determine extractor from language. */
751 if (language != NULL)
752 extractor = language_to_extractor (language);
753
754 /* Canonize msgstr prefix/suffix. */
755 if (msgstr_prefix != NULL && msgstr_suffix == NULL)
756 msgstr_suffix = "";
757 else if (msgstr_prefix == NULL && msgstr_suffix != NULL)
758 msgstr_prefix = "";
759
760 /* Default output directory is the current directory. */
761 if (output_dir == NULL)
762 output_dir = ".";
763
764 /* Construct the name of the output file. If the default domain has
765 the special name "-" we write to stdout. */
766 if (output_file)
767 {
768 if (IS_RELATIVE_FILE_NAME (output_file) && strcmp (output_file, "-") != 0)
769 /* Please do NOT add a .po suffix! */
770 file_name = xconcatenated_filename (output_dir, output_file, NULL);
771 else
772 file_name = xstrdup (output_file);
773 }
774 else if (strcmp (default_domain, "-") == 0)
775 file_name = "-";
776 else
777 file_name = xconcatenated_filename (output_dir, default_domain, ".po");
778
779 /* Determine list of files we have to process. */
780 if (files_from != NULL)
781 file_list = read_names_from_file (files_from);
782 else
783 file_list = string_list_alloc ();
784 /* Append names from command line. */
785 for (cnt = optind; cnt < argc; ++cnt)
786 string_list_append_unique (file_list, argv[cnt]);
787
788 /* Allocate converter from xgettext_global_source_encoding to UTF-8 (except
789 from ASCII or UTF-8, when this conversion is a no-op). */
790 if (xgettext_global_source_encoding != NULL
791 && xgettext_global_source_encoding != po_charset_ascii
792 && xgettext_global_source_encoding != po_charset_utf8)
793 {
794 #if HAVE_ICONV
795 iconv_t cd;
796
797 /* Avoid glibc-2.1 bug with EUC-KR. */
798 # if ((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
799 && !defined _LIBICONV_VERSION
800 if (strcmp (xgettext_global_source_encoding, "EUC-KR") == 0)
801 cd = (iconv_t)(-1);
802 else
803 # endif
804 cd = iconv_open (po_charset_utf8, xgettext_global_source_encoding);
805 if (cd == (iconv_t)(-1))
806 error (EXIT_FAILURE, 0,
807 _("Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), and iconv() does not support this conversion."),
808 xgettext_global_source_encoding, po_charset_utf8,
809 last_component (program_name));
810 xgettext_global_source_iconv = cd;
811 #else
812 error (EXIT_FAILURE, 0,
813 _("Cannot convert from \"%s\" to \"%s\". %s relies on iconv(). This version was built without iconv()."),
814 xgettext_global_source_encoding, po_charset_utf8,
815 last_component (program_name));
816 #endif
817 }
818
819 /* Allocate a message list to remember all the messages. */
820 mdlp = msgdomain_list_alloc (true);
821
822 /* Generate a header, so that we know how and when this PO file was
823 created. */
824 if (!xgettext_omit_header)
825 message_list_append (mdlp->item[0]->messages, construct_header ());
826
827 /* Read in the old messages, so that we can add to them. */
828 if (join_existing)
829 {
830 /* Temporarily reset the directory list to empty, because file_name
831 is an output file and therefore should not be searched for. */
832 void *saved_directory_list = dir_list_save_reset ();
833 extractor_ty po_extractor = { extract_po, NULL, NULL, NULL };
834
835 extract_from_file (file_name, po_extractor, mdlp);
836 if (!is_ascii_msgdomain_list (mdlp))
837 mdlp = iconv_msgdomain_list (mdlp, "UTF-8", true, file_name);
838
839 dir_list_restore (saved_directory_list);
840 }
841
842 /* Process all input files. */
843 for (i = 0; i < file_list->nitems; i++)
844 {
845 const char *filename;
846 extractor_ty this_file_extractor;
847 its_rule_list_ty *its_rules = NULL;
848
849 filename = file_list->item[i];
850
851 if (extractor.extract_from_stream || extractor.extract_from_file)
852 this_file_extractor = extractor;
853 else if (explicit_its_filename != NULL)
854 {
855 its_rules = its_rule_list_alloc ();
856 if (!its_rule_list_add_from_file (its_rules,
857 explicit_its_filename))
858 error (EXIT_FAILURE, 0,
859 _("warning: ITS rule file '%s' does not exist"),
860 explicit_its_filename);
861 }
862 else
863 {
864 const char *language_from_extension = NULL;
865 const char *base;
866 char *reduced;
867
868 base = strrchr (filename, '/');
869 if (!base)
870 base = filename;
871
872 reduced = xstrdup (base);
873 /* Remove a trailing ".in" - it's a generic suffix. */
874 while (strlen (reduced) >= 3
875 && memcmp (reduced + strlen (reduced) - 3, ".in", 3) == 0)
876 reduced[strlen (reduced) - 3] = '\0';
877
878 /* If no language is specified with -L, deduce it the extension. */
879 if (language == NULL)
880 {
881 const char *p;
882
883 /* Work out what the file extension is. */
884 p = reduced + strlen (reduced);
885 for (; p > reduced && language_from_extension == NULL; p--)
886 {
887 if (*p == '.')
888 {
889 const char *extension = p + 1;
890
891 /* Derive the language from the extension, and
892 the extractor function from the language. */
893 language_from_extension =
894 extension_to_language (extension);
895 }
896 }
897 }
898
899 /* If language is not determined from the file name
900 extension, check ITS locating rules. */
901 if (language_from_extension == NULL
902 && strcmp (filename, "-") != 0)
903 {
904 const char *its_basename;
905
906 its_basename = locating_rule_list_locate (its_locating_rules,
907 filename,
908 language);
909
910 if (its_basename != NULL)
911 {
912 size_t j;
913
914 its_rules = its_rule_list_alloc ();
915
916 /* If the ITS file is identified by the name,
917 set the root element untranslatable. */
918 if (language != NULL)
919 its_rule_list_add_from_string (its_rules,
920 ITS_ROOT_UNTRANSLATABLE);
921
922 for (j = 0; its_dirs[j] != NULL; j++)
923 {
924 char *its_filename =
925 xconcatenated_filename (its_dirs[j], its_basename,
926 NULL);
927 struct stat statbuf;
928
929 if (stat (its_filename, &statbuf) == 0
930 && its_rule_list_add_from_file (its_rules,
931 its_filename))
932 {
933 /* The last element in its_dirs always points to
934 the fallback directory. */
935 if (its_dirs[j + 1] == NULL)
936 error (0, 0,
937 _("warning: a fallback ITS rule file '%s' is used; "
938 "it may not be in sync with the upstream"),
939 its_filename);
940 free (its_filename);
941 break;
942 }
943 }
944 if (its_dirs[j] == NULL)
945 {
946 error (0, 0,
947 _("warning: ITS rule file '%s' does not exist; check your gettext installation"),
948 its_basename);
949 its_rule_list_free (its_rules);
950 its_rules = NULL;
951 }
952 }
953 }
954
955 if (its_rules == NULL)
956 {
957 if (language_from_extension == NULL)
958 {
959 const char *extension = strrchr (reduced, '.');
960 if (extension == NULL)
961 extension = "";
962 else
963 extension++;
964 error (0, 0,
965 _("warning: file '%s' extension '%s' is unknown; will try C"),
966 filename, extension);
967 language_from_extension = "C";
968 }
969
970 this_file_extractor =
971 language_to_extractor (language_from_extension);
972 }
973
974 free (reduced);
975 }
976
977 if (its_rules != NULL)
978 {
979 /* Extract the strings from the file, using ITS. */
980 extract_from_xml_file (filename, its_rules, mdlp);
981 its_rule_list_free (its_rules);
982 }
983 else
984 /* Extract the strings from the file. */
985 extract_from_file (filename, this_file_extractor, mdlp);
986 }
987 string_list_free (file_list);
988
989 /* Finalize the constructed header. */
990 if (!xgettext_omit_header)
991 finalize_header (mdlp);
992
993 /* Free the allocated converter. */
994 #if HAVE_ICONV
995 if (xgettext_global_source_encoding != NULL
996 && xgettext_global_source_encoding != po_charset_ascii
997 && xgettext_global_source_encoding != po_charset_utf8)
998 iconv_close (xgettext_global_source_iconv);
999 #endif
1000
1001 /* Sorting the list of messages. */
1002 if (sort_by_filepos)
1003 msgdomain_list_sort_by_filepos (mdlp);
1004 else if (sort_by_msgid)
1005 msgdomain_list_sort_by_msgid (mdlp);
1006
1007 /* Check syntax of messages. */
1008 {
1009 int nerrors = 0;
1010
1011 for (i = 0; i < mdlp->nitems; i++)
1012 {
1013 message_list_ty *mlp = mdlp->item[i]->messages;
1014 nerrors = syntax_check_message_list (mlp);
1015 }
1016
1017 /* Exit with status 1 on any error. */
1018 if (nerrors > 0)
1019 error (EXIT_FAILURE, 0,
1020 ngettext ("found %d fatal error", "found %d fatal errors",
1021 nerrors),
1022 nerrors);
1023 }
1024
1025 /* Write the PO file. */
1026 msgdomain_list_print (mdlp, file_name, output_syntax, force_po, do_debug);
1027
1028 if (its_locating_rules)
1029 locating_rule_list_free (its_locating_rules);
1030
1031 if (its_dirs != NULL)
1032 {
1033 for (i = 0; its_dirs[i] != NULL; i++)
1034 free (its_dirs[i]);
1035 free (its_dirs);
1036 }
1037
1038 exit (EXIT_SUCCESS);
1039 }
1040
1041
1042 /* Display usage information and exit. */
1043 static void
usage(int status)1044 usage (int status)
1045 {
1046 if (status != EXIT_SUCCESS)
1047 fprintf (stderr, _("Try '%s --help' for more information.\n"),
1048 program_name);
1049 else
1050 {
1051 printf (_("\
1052 Usage: %s [OPTION] [INPUTFILE]...\n\
1053 "), program_name);
1054 printf ("\n");
1055 printf (_("\
1056 Extract translatable strings from given input files.\n\
1057 "));
1058 printf ("\n");
1059 /* xgettext: no-wrap */
1060 printf (_("\
1061 Mandatory arguments to long options are mandatory for short options too.\n\
1062 Similarly for optional arguments.\n\
1063 "));
1064 printf ("\n");
1065 printf (_("\
1066 Input file location:\n"));
1067 printf (_("\
1068 INPUTFILE ... input files\n"));
1069 printf (_("\
1070 -f, --files-from=FILE get list of input files from FILE\n"));
1071 printf (_("\
1072 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
1073 printf (_("\
1074 If input file is -, standard input is read.\n"));
1075 printf ("\n");
1076 printf (_("\
1077 Output file location:\n"));
1078 printf (_("\
1079 -d, --default-domain=NAME use NAME.po for output (instead of messages.po)\n"));
1080 printf (_("\
1081 -o, --output=FILE write output to specified file\n"));
1082 printf (_("\
1083 -p, --output-dir=DIR output files will be placed in directory DIR\n"));
1084 printf (_("\
1085 If output file is -, output is written to standard output.\n"));
1086 printf ("\n");
1087 printf (_("\
1088 Choice of input file language:\n"));
1089 printf (_("\
1090 -L, --language=NAME recognise the specified language\n\
1091 (C, C++, ObjectiveC, PO, Shell, Python, Lisp,\n\
1092 EmacsLisp, librep, Scheme, Smalltalk, Java,\n\
1093 JavaProperties, C#, awk, YCP, Tcl, Perl, PHP,\n\
1094 Ruby, GCC-source, NXStringTable, RST, RSJ,\n\
1095 Glade, Lua, JavaScript, Vala, Desktop)\n"));
1096 printf (_("\
1097 -C, --c++ shorthand for --language=C++\n"));
1098 printf (_("\
1099 By default the language is guessed depending on the input file name extension.\n"));
1100 printf ("\n");
1101 printf (_("\
1102 Input file interpretation:\n"));
1103 printf (_("\
1104 --from-code=NAME encoding of input files\n\
1105 (except for Python, Tcl, Glade)\n"));
1106 printf (_("\
1107 By default the input files are assumed to be in ASCII.\n"));
1108 printf ("\n");
1109 printf (_("\
1110 Operation mode:\n"));
1111 printf (_("\
1112 -j, --join-existing join messages with existing file\n"));
1113 printf (_("\
1114 -x, --exclude-file=FILE.po entries from FILE.po are not extracted\n"));
1115 printf (_("\
1116 -cTAG, --add-comments=TAG place comment blocks starting with TAG and\n\
1117 preceding keyword lines in output file\n\
1118 -c, --add-comments place all comment blocks preceding keyword lines\n\
1119 in output file\n"));
1120 printf (_("\
1121 --check=NAME perform syntax check on messages\n\
1122 (ellipsis-unicode, space-ellipsis,\n\
1123 quote-unicode, bullet-unicode)\n"));
1124 printf (_("\
1125 --sentence-end=TYPE type describing the end of sentence\n\
1126 (single-space, which is the default, \n\
1127 or double-space)\n"));
1128 printf ("\n");
1129 printf (_("\
1130 Language specific options:\n"));
1131 printf (_("\
1132 -a, --extract-all extract all strings\n"));
1133 printf (_("\
1134 (only languages C, C++, ObjectiveC, Shell,\n\
1135 Python, Lisp, EmacsLisp, librep, Scheme, Java,\n\
1136 C#, awk, Tcl, Perl, PHP, GCC-source, Glade,\n\
1137 Lua, JavaScript, Vala)\n"));
1138 printf (_("\
1139 -kWORD, --keyword=WORD look for WORD as an additional keyword\n\
1140 -k, --keyword do not to use default keywords\n"));
1141 printf (_("\
1142 (only languages C, C++, ObjectiveC, Shell,\n\
1143 Python, Lisp, EmacsLisp, librep, Scheme, Java,\n\
1144 C#, awk, Tcl, Perl, PHP, GCC-source, Glade,\n\
1145 Lua, JavaScript, Vala, Desktop)\n"));
1146 printf (_("\
1147 --flag=WORD:ARG:FLAG additional flag for strings inside the argument\n\
1148 number ARG of keyword WORD\n"));
1149 printf (_("\
1150 (only languages C, C++, ObjectiveC, Shell,\n\
1151 Python, Lisp, EmacsLisp, librep, Scheme, Java,\n\
1152 C#, awk, YCP, Tcl, Perl, PHP, GCC-source,\n\
1153 Lua, JavaScript, Vala)\n"));
1154 printf (_("\
1155 -T, --trigraphs understand ANSI C trigraphs for input\n"));
1156 printf (_("\
1157 (only languages C, C++, ObjectiveC)\n"));
1158 printf (_("\
1159 --its=FILE apply ITS rules from FILE\n"));
1160 printf (_("\
1161 (only XML based languages)\n"));
1162 printf (_("\
1163 --qt recognize Qt format strings\n"));
1164 printf (_("\
1165 (only language C++)\n"));
1166 printf (_("\
1167 --kde recognize KDE 4 format strings\n"));
1168 printf (_("\
1169 (only language C++)\n"));
1170 printf (_("\
1171 --boost recognize Boost format strings\n"));
1172 printf (_("\
1173 (only language C++)\n"));
1174 printf (_("\
1175 --debug more detailed formatstring recognition result\n"));
1176 printf ("\n");
1177 printf (_("\
1178 Output details:\n"));
1179 printf (_("\
1180 --color use colors and other text attributes always\n\
1181 --color=WHEN use colors and other text attributes if WHEN.\n\
1182 WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
1183 printf (_("\
1184 --style=STYLEFILE specify CSS style rule file for --color\n"));
1185 printf (_("\
1186 -e, --no-escape do not use C escapes in output (default)\n"));
1187 printf (_("\
1188 -E, --escape use C escapes in output, no extended chars\n"));
1189 printf (_("\
1190 --force-po write PO file even if empty\n"));
1191 printf (_("\
1192 -i, --indent write the .po file using indented style\n"));
1193 printf (_("\
1194 --no-location do not write '#: filename:line' lines\n"));
1195 printf (_("\
1196 -n, --add-location generate '#: filename:line' lines (default)\n"));
1197 printf (_("\
1198 --strict write out strict Uniforum conforming .po file\n"));
1199 printf (_("\
1200 --properties-output write out a Java .properties file\n"));
1201 printf (_("\
1202 --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
1203 printf (_("\
1204 --itstool write out itstool comments\n"));
1205 printf (_("\
1206 -w, --width=NUMBER set output page width\n"));
1207 printf (_("\
1208 --no-wrap do not break long message lines, longer than\n\
1209 the output page width, into several lines\n"));
1210 printf (_("\
1211 -s, --sort-output generate sorted output\n"));
1212 printf (_("\
1213 -F, --sort-by-file sort output by file location\n"));
1214 printf (_("\
1215 --omit-header don't write header with 'msgid \"\"' entry\n"));
1216 printf (_("\
1217 --copyright-holder=STRING set copyright holder in output\n"));
1218 printf (_("\
1219 --foreign-user omit FSF copyright in output for foreign user\n"));
1220 printf (_("\
1221 --package-name=PACKAGE set package name in output\n"));
1222 printf (_("\
1223 --package-version=VERSION set package version in output\n"));
1224 printf (_("\
1225 --msgid-bugs-address=EMAIL@ADDRESS set report address for msgid bugs\n"));
1226 printf (_("\
1227 -m[STRING], --msgstr-prefix[=STRING] use STRING or \"\" as prefix for msgstr\n\
1228 values\n"));
1229 printf (_("\
1230 -M[STRING], --msgstr-suffix[=STRING] use STRING or \"\" as suffix for msgstr\n\
1231 values\n"));
1232 printf ("\n");
1233 printf (_("\
1234 Informative output:\n"));
1235 printf (_("\
1236 -h, --help display this help and exit\n"));
1237 printf (_("\
1238 -V, --version output version information and exit\n"));
1239 printf (_("\
1240 -v, --verbose increase verbosity level\n"));
1241 printf ("\n");
1242 /* TRANSLATORS: The first placeholder is the web address of the Savannah
1243 project of this package. The second placeholder is the bug-reporting
1244 email address for this package. Please add _another line_ saying
1245 "Report translation bugs to <...>\n" with the address for translation
1246 bugs (typically your translation team's web or email address). */
1247 printf(_("\
1248 Report bugs in the bug tracker at <%s>\n\
1249 or by email to <%s>.\n"),
1250 "https://savannah.gnu.org/projects/gettext",
1251 "bug-gettext@gnu.org");
1252 }
1253
1254 exit (status);
1255 }
1256
1257
1258 static void
exclude_directive_domain(abstract_catalog_reader_ty * pop,char * name)1259 exclude_directive_domain (abstract_catalog_reader_ty *pop, char *name)
1260 {
1261 po_gram_error_at_line (&gram_pos,
1262 _("this file may not contain domain directives"));
1263 }
1264
1265
1266 static void
exclude_directive_message(abstract_catalog_reader_ty * pop,char * msgctxt,char * msgid,lex_pos_ty * msgid_pos,char * msgid_plural,char * msgstr,size_t msgstr_len,lex_pos_ty * msgstr_pos,char * prev_msgctxt,char * prev_msgid,char * prev_msgid_plural,bool force_fuzzy,bool obsolete)1267 exclude_directive_message (abstract_catalog_reader_ty *pop,
1268 char *msgctxt,
1269 char *msgid,
1270 lex_pos_ty *msgid_pos,
1271 char *msgid_plural,
1272 char *msgstr, size_t msgstr_len,
1273 lex_pos_ty *msgstr_pos,
1274 char *prev_msgctxt,
1275 char *prev_msgid,
1276 char *prev_msgid_plural,
1277 bool force_fuzzy, bool obsolete)
1278 {
1279 message_ty *mp;
1280
1281 /* See if this message ID has been seen before. */
1282 if (exclude == NULL)
1283 exclude = message_list_alloc (true);
1284 mp = message_list_search (exclude, msgctxt, msgid);
1285 if (mp != NULL)
1286 free (msgid);
1287 else
1288 {
1289 mp = message_alloc (msgctxt, msgid, msgid_plural, "", 1, msgstr_pos);
1290 /* Do not free msgid. */
1291 message_list_append (exclude, mp);
1292 }
1293
1294 /* All we care about is the msgid. Throw the msgstr away.
1295 Don't even check for duplicate msgids. */
1296 free (msgstr);
1297 }
1298
1299
1300 /* So that the one parser can be used for multiple programs, and also
1301 use good data hiding and encapsulation practices, an object
1302 oriented approach has been taken. An object instance is allocated,
1303 and all actions resulting from the parse will be through
1304 invocations of method functions of that object. */
1305
1306 static abstract_catalog_reader_class_ty exclude_methods =
1307 {
1308 sizeof (abstract_catalog_reader_ty),
1309 NULL, /* constructor */
1310 NULL, /* destructor */
1311 NULL, /* parse_brief */
1312 NULL, /* parse_debrief */
1313 exclude_directive_domain,
1314 exclude_directive_message,
1315 NULL, /* comment */
1316 NULL, /* comment_dot */
1317 NULL, /* comment_filepos */
1318 NULL, /* comment_special */
1319 };
1320
1321
1322 static void
read_exclusion_file(char * filename)1323 read_exclusion_file (char *filename)
1324 {
1325 char *real_filename;
1326 FILE *fp = open_catalog_file (filename, &real_filename, true);
1327 abstract_catalog_reader_ty *pop;
1328
1329 pop = catalog_reader_alloc (&exclude_methods);
1330 catalog_reader_parse (pop, fp, real_filename, filename, &input_format_po);
1331 catalog_reader_free (pop);
1332
1333 if (fp != stdin)
1334 fclose (fp);
1335 }
1336
1337
1338 static void
flag_context_list_table_insert(flag_context_list_table_ty * table,unsigned int index,const char * name_start,const char * name_end,int argnum,enum is_format value,bool pass)1339 flag_context_list_table_insert (flag_context_list_table_ty *table,
1340 unsigned int index,
1341 const char *name_start, const char *name_end,
1342 int argnum, enum is_format value, bool pass)
1343 {
1344 char *allocated_name = NULL;
1345
1346 if (table == &flag_table_lisp)
1347 {
1348 /* Convert NAME to upper case. */
1349 size_t name_len = name_end - name_start;
1350 char *name = allocated_name = (char *) xmalloca (name_len);
1351 size_t i;
1352
1353 for (i = 0; i < name_len; i++)
1354 name[i] = (name_start[i] >= 'a' && name_start[i] <= 'z'
1355 ? name_start[i] - 'a' + 'A'
1356 : name_start[i]);
1357 name_start = name;
1358 name_end = name + name_len;
1359 }
1360 else if (table == &flag_table_tcl)
1361 {
1362 /* Remove redundant "::" prefix. */
1363 if (name_end - name_start > 2
1364 && name_start[0] == ':' && name_start[1] == ':')
1365 name_start += 2;
1366 }
1367
1368 flag_context_list_table_add (table, index, name_start, name_end,
1369 argnum, value, pass);
1370
1371 if (allocated_name != NULL)
1372 freea (allocated_name);
1373 }
1374
1375 void
xgettext_record_flag(const char * optionstring)1376 xgettext_record_flag (const char *optionstring)
1377 {
1378 /* Check the string has at least two colons. (Colons in the name are
1379 allowed, needed for the Lisp and the Tcl backends.) */
1380 const char *colon1;
1381 const char *colon2;
1382
1383 for (colon2 = optionstring + strlen (optionstring); ; )
1384 {
1385 if (colon2 == optionstring)
1386 goto err;
1387 colon2--;
1388 if (*colon2 == ':')
1389 break;
1390 }
1391 for (colon1 = colon2; ; )
1392 {
1393 if (colon1 == optionstring)
1394 goto err;
1395 colon1--;
1396 if (*colon1 == ':')
1397 break;
1398 }
1399 {
1400 const char *name_start = optionstring;
1401 const char *name_end = colon1;
1402 const char *argnum_start = colon1 + 1;
1403 const char *argnum_end = colon2;
1404 const char *flag = colon2 + 1;
1405 int argnum;
1406
1407 /* Check the parts' syntax. */
1408 if (name_end == name_start)
1409 goto err;
1410 if (argnum_end == argnum_start)
1411 goto err;
1412 {
1413 char *endp;
1414 argnum = strtol (argnum_start, &endp, 10);
1415 if (endp != argnum_end)
1416 goto err;
1417 }
1418 if (argnum <= 0)
1419 goto err;
1420
1421 /* Analyze the flag part. */
1422 {
1423 bool pass;
1424
1425 pass = false;
1426 if (strlen (flag) >= 5 && memcmp (flag, "pass-", 5) == 0)
1427 {
1428 pass = true;
1429 flag += 5;
1430 }
1431
1432 /* Unlike po_parse_comment_special(), we don't accept "fuzzy",
1433 "wrap", or "check" here - it has no sense. */
1434 if (strlen (flag) >= 7
1435 && memcmp (flag + strlen (flag) - 7, "-format", 7) == 0)
1436 {
1437 const char *p;
1438 size_t n;
1439 enum is_format value;
1440 size_t type;
1441
1442 p = flag;
1443 n = strlen (flag) - 7;
1444
1445 if (n >= 3 && memcmp (p, "no-", 3) == 0)
1446 {
1447 p += 3;
1448 n -= 3;
1449 value = no;
1450 }
1451 else if (n >= 9 && memcmp (p, "possible-", 9) == 0)
1452 {
1453 p += 9;
1454 n -= 9;
1455 value = possible;
1456 }
1457 else if (n >= 11 && memcmp (p, "impossible-", 11) == 0)
1458 {
1459 p += 11;
1460 n -= 11;
1461 value = impossible;
1462 }
1463 else
1464 value = yes_according_to_context;
1465
1466 for (type = 0; type < NFORMATS; type++)
1467 if (strlen (format_language[type]) == n
1468 && memcmp (format_language[type], p, n) == 0)
1469 {
1470 switch (type)
1471 {
1472 case format_c:
1473 flag_context_list_table_insert (&flag_table_c, 0,
1474 name_start, name_end,
1475 argnum, value, pass);
1476 flag_context_list_table_insert (&flag_table_cxx_qt, 0,
1477 name_start, name_end,
1478 argnum, value, pass);
1479 flag_context_list_table_insert (&flag_table_cxx_kde, 0,
1480 name_start, name_end,
1481 argnum, value, pass);
1482 flag_context_list_table_insert (&flag_table_cxx_boost, 0,
1483 name_start, name_end,
1484 argnum, value, pass);
1485 flag_context_list_table_insert (&flag_table_objc, 0,
1486 name_start, name_end,
1487 argnum, value, pass);
1488 break;
1489 case format_objc:
1490 flag_context_list_table_insert (&flag_table_objc, 1,
1491 name_start, name_end,
1492 argnum, value, pass);
1493 break;
1494 case format_python:
1495 flag_context_list_table_insert (&flag_table_python, 0,
1496 name_start, name_end,
1497 argnum, value, pass);
1498 break;
1499 case format_python_brace:
1500 flag_context_list_table_insert (&flag_table_python, 0,
1501 name_start, name_end,
1502 argnum, value, pass);
1503 break;
1504 case format_java:
1505 flag_context_list_table_insert (&flag_table_java, 0,
1506 name_start, name_end,
1507 argnum, value, pass);
1508 break;
1509 case format_java_printf:
1510 flag_context_list_table_insert (&flag_table_java, 1,
1511 name_start, name_end,
1512 argnum, value, pass);
1513 break;
1514 case format_csharp:
1515 flag_context_list_table_insert (&flag_table_csharp, 0,
1516 name_start, name_end,
1517 argnum, value, pass);
1518 break;
1519 case format_javascript:
1520 flag_context_list_table_insert (&flag_table_javascript, 0,
1521 name_start, name_end,
1522 argnum, value, pass);
1523 break;
1524 case format_scheme:
1525 flag_context_list_table_insert (&flag_table_scheme, 0,
1526 name_start, name_end,
1527 argnum, value, pass);
1528 break;
1529 case format_lisp:
1530 flag_context_list_table_insert (&flag_table_lisp, 0,
1531 name_start, name_end,
1532 argnum, value, pass);
1533 break;
1534 case format_elisp:
1535 flag_context_list_table_insert (&flag_table_elisp, 0,
1536 name_start, name_end,
1537 argnum, value, pass);
1538 break;
1539 case format_librep:
1540 flag_context_list_table_insert (&flag_table_librep, 0,
1541 name_start, name_end,
1542 argnum, value, pass);
1543 break;
1544 case format_ruby:
1545 flag_context_list_table_insert (&flag_table_ruby, 0,
1546 name_start, name_end,
1547 argnum, value, pass);
1548 break;
1549 case format_sh:
1550 flag_context_list_table_insert (&flag_table_sh, 0,
1551 name_start, name_end,
1552 argnum, value, pass);
1553 break;
1554 case format_awk:
1555 flag_context_list_table_insert (&flag_table_awk, 0,
1556 name_start, name_end,
1557 argnum, value, pass);
1558 break;
1559 case format_lua:
1560 flag_context_list_table_insert (&flag_table_lua, 0,
1561 name_start, name_end,
1562 argnum, value, pass);
1563 break;
1564 case format_pascal:
1565 break;
1566 case format_smalltalk:
1567 break;
1568 case format_qt:
1569 flag_context_list_table_insert (&flag_table_cxx_qt, 1,
1570 name_start, name_end,
1571 argnum, value, pass);
1572 break;
1573 case format_qt_plural:
1574 flag_context_list_table_insert (&flag_table_cxx_qt, 2,
1575 name_start, name_end,
1576 argnum, value, pass);
1577 break;
1578 case format_kde:
1579 flag_context_list_table_insert (&flag_table_cxx_kde, 1,
1580 name_start, name_end,
1581 argnum, value, pass);
1582 break;
1583 case format_kde_kuit:
1584 flag_context_list_table_insert (&flag_table_cxx_kde, 2,
1585 name_start, name_end,
1586 argnum, value, pass);
1587 break;
1588 case format_boost:
1589 flag_context_list_table_insert (&flag_table_cxx_boost, 1,
1590 name_start, name_end,
1591 argnum, value, pass);
1592 break;
1593 case format_tcl:
1594 flag_context_list_table_insert (&flag_table_tcl, 0,
1595 name_start, name_end,
1596 argnum, value, pass);
1597 break;
1598 case format_perl:
1599 flag_context_list_table_insert (&flag_table_perl, 0,
1600 name_start, name_end,
1601 argnum, value, pass);
1602 break;
1603 case format_perl_brace:
1604 flag_context_list_table_insert (&flag_table_perl, 1,
1605 name_start, name_end,
1606 argnum, value, pass);
1607 break;
1608 case format_php:
1609 flag_context_list_table_insert (&flag_table_php, 0,
1610 name_start, name_end,
1611 argnum, value, pass);
1612 break;
1613 case format_gcc_internal:
1614 flag_context_list_table_insert (&flag_table_gcc_internal, 0,
1615 name_start, name_end,
1616 argnum, value, pass);
1617 break;
1618 case format_gfc_internal:
1619 flag_context_list_table_insert (&flag_table_gcc_internal, 1,
1620 name_start, name_end,
1621 argnum, value, pass);
1622 break;
1623 case format_ycp:
1624 flag_context_list_table_insert (&flag_table_ycp, 0,
1625 name_start, name_end,
1626 argnum, value, pass);
1627 break;
1628 default:
1629 abort ();
1630 }
1631 return;
1632 }
1633 /* If the flag is not among the valid values, the optionstring is
1634 invalid. */
1635 }
1636 }
1637 }
1638
1639 err:
1640 error (EXIT_FAILURE, 0,
1641 _("A --flag argument doesn't have the <keyword>:<argnum>:[pass-]<flag> syntax: %s"),
1642 optionstring);
1643 }
1644
1645
1646 /* Comment handling: There is a list of automatic comments that may be appended
1647 to the next message. Used by remember_a_message(). */
1648
1649 static string_list_ty *comment;
1650
1651 static void
xgettext_comment_add(const char * str)1652 xgettext_comment_add (const char *str)
1653 {
1654 if (comment == NULL)
1655 comment = string_list_alloc ();
1656 string_list_append (comment, str);
1657 }
1658
1659 const char *
xgettext_comment(size_t n)1660 xgettext_comment (size_t n)
1661 {
1662 if (comment == NULL || n >= comment->nitems)
1663 return NULL;
1664 return comment->item[n];
1665 }
1666
1667 void
xgettext_comment_reset(void)1668 xgettext_comment_reset (void)
1669 {
1670 if (comment != NULL)
1671 {
1672 string_list_free (comment);
1673 comment = NULL;
1674 }
1675 }
1676
1677
1678 refcounted_string_list_ty *savable_comment;
1679
1680 void
savable_comment_add(const char * str)1681 savable_comment_add (const char *str)
1682 {
1683 if (savable_comment == NULL)
1684 {
1685 savable_comment = XMALLOC (refcounted_string_list_ty);
1686 savable_comment->refcount = 1;
1687 string_list_init (&savable_comment->contents);
1688 }
1689 else if (savable_comment->refcount > 1)
1690 {
1691 /* Unshare the list by making copies. */
1692 struct string_list_ty *oldcontents;
1693 size_t i;
1694
1695 savable_comment->refcount--;
1696 oldcontents = &savable_comment->contents;
1697
1698 savable_comment = XMALLOC (refcounted_string_list_ty);
1699 savable_comment->refcount = 1;
1700 string_list_init (&savable_comment->contents);
1701 for (i = 0; i < oldcontents->nitems; i++)
1702 string_list_append (&savable_comment->contents, oldcontents->item[i]);
1703 }
1704 string_list_append (&savable_comment->contents, str);
1705 }
1706
1707 void
savable_comment_reset()1708 savable_comment_reset ()
1709 {
1710 drop_reference (savable_comment);
1711 savable_comment = NULL;
1712 }
1713
1714 void
savable_comment_to_xgettext_comment(refcounted_string_list_ty * rslp)1715 savable_comment_to_xgettext_comment (refcounted_string_list_ty *rslp)
1716 {
1717 xgettext_comment_reset ();
1718 if (rslp != NULL)
1719 {
1720 size_t i;
1721
1722 for (i = 0; i < rslp->contents.nitems; i++)
1723 xgettext_comment_add (rslp->contents.item[i]);
1724 }
1725 }
1726
1727
1728 /* xgettext_find_file and xgettext_open look up a file, taking into account
1729 the --directory options.
1730 xgettext_find_file merely returns the file name. This function is useful
1731 for parsers implemented as separate programs.
1732 xgettext_open returns the open file stream. This function is useful for
1733 built-in parsers. */
1734
1735 static void
xgettext_find_file(const char * fn,char ** logical_file_name_p,char ** real_file_name_p)1736 xgettext_find_file (const char *fn,
1737 char **logical_file_name_p, char **real_file_name_p)
1738 {
1739 char *new_name;
1740 char *logical_file_name;
1741 struct stat statbuf;
1742
1743 /* We cannot handle "-" here. "/dev/fd/0" is not portable, and it cannot
1744 be opened multiple times. */
1745 if (IS_RELATIVE_FILE_NAME (fn))
1746 {
1747 int j;
1748
1749 for (j = 0; ; ++j)
1750 {
1751 const char *dir = dir_list_nth (j);
1752
1753 if (dir == NULL)
1754 error (EXIT_FAILURE, ENOENT,
1755 _("error while opening \"%s\" for reading"), fn);
1756
1757 new_name = xconcatenated_filename (dir, fn, NULL);
1758
1759 if (stat (new_name, &statbuf) == 0)
1760 break;
1761
1762 if (errno != ENOENT)
1763 error (EXIT_FAILURE, errno,
1764 _("error while opening \"%s\" for reading"), new_name);
1765 free (new_name);
1766 }
1767
1768 /* Note that the NEW_NAME variable contains the actual file name
1769 and the logical file name is what is reported by xgettext. In
1770 this case NEW_NAME is set to the file which was found along the
1771 directory search path, and LOGICAL_FILE_NAME is is set to the
1772 file name which was searched for. */
1773 logical_file_name = xstrdup (fn);
1774 }
1775 else
1776 {
1777 new_name = xstrdup (fn);
1778 if (stat (fn, &statbuf) != 0)
1779 error (EXIT_FAILURE, errno,
1780 _("error while opening \"%s\" for reading"), fn);
1781 logical_file_name = xstrdup (new_name);
1782 }
1783
1784 *logical_file_name_p = logical_file_name;
1785 *real_file_name_p = new_name;
1786 }
1787
1788 static FILE *
xgettext_open(const char * fn,char ** logical_file_name_p,char ** real_file_name_p)1789 xgettext_open (const char *fn,
1790 char **logical_file_name_p, char **real_file_name_p)
1791 {
1792 FILE *fp;
1793 char *new_name;
1794 char *logical_file_name;
1795
1796 if (strcmp (fn, "-") == 0)
1797 {
1798 new_name = xstrdup (_("standard input"));
1799 logical_file_name = xstrdup (new_name);
1800 fp = stdin;
1801 }
1802 else if (IS_RELATIVE_FILE_NAME (fn))
1803 {
1804 int j;
1805
1806 for (j = 0; ; ++j)
1807 {
1808 const char *dir = dir_list_nth (j);
1809
1810 if (dir == NULL)
1811 error (EXIT_FAILURE, ENOENT,
1812 _("error while opening \"%s\" for reading"), fn);
1813
1814 new_name = xconcatenated_filename (dir, fn, NULL);
1815
1816 fp = fopen (new_name, "r");
1817 if (fp != NULL)
1818 break;
1819
1820 if (errno != ENOENT)
1821 error (EXIT_FAILURE, errno,
1822 _("error while opening \"%s\" for reading"), new_name);
1823 free (new_name);
1824 }
1825
1826 /* Note that the NEW_NAME variable contains the actual file name
1827 and the logical file name is what is reported by xgettext. In
1828 this case NEW_NAME is set to the file which was found along the
1829 directory search path, and LOGICAL_FILE_NAME is is set to the
1830 file name which was searched for. */
1831 logical_file_name = xstrdup (fn);
1832 }
1833 else
1834 {
1835 new_name = xstrdup (fn);
1836 fp = fopen (fn, "r");
1837 if (fp == NULL)
1838 error (EXIT_FAILURE, errno,
1839 _("error while opening \"%s\" for reading"), fn);
1840 logical_file_name = xstrdup (new_name);
1841 }
1842
1843 *logical_file_name_p = logical_file_name;
1844 *real_file_name_p = new_name;
1845 return fp;
1846 }
1847
1848
1849 /* Language dependent format string parser.
1850 NULL if the language has no notion of format strings. */
1851 struct formatstring_parser *current_formatstring_parser1;
1852 struct formatstring_parser *current_formatstring_parser2;
1853 struct formatstring_parser *current_formatstring_parser3;
1854
1855
1856 static void
extract_from_file(const char * file_name,extractor_ty extractor,msgdomain_list_ty * mdlp)1857 extract_from_file (const char *file_name, extractor_ty extractor,
1858 msgdomain_list_ty *mdlp)
1859 {
1860 char *logical_file_name;
1861 char *real_file_name;
1862
1863 current_formatstring_parser1 = extractor.formatstring_parser1;
1864 current_formatstring_parser2 = extractor.formatstring_parser2;
1865 current_formatstring_parser3 = extractor.formatstring_parser3;
1866
1867 if (extractor.extract_from_stream)
1868 {
1869 FILE *fp = xgettext_open (file_name, &logical_file_name, &real_file_name);
1870
1871 /* Set the default for the source file encoding. May be overridden by
1872 the extractor function. */
1873 xgettext_current_source_encoding =
1874 (xgettext_global_source_encoding != NULL ? xgettext_global_source_encoding :
1875 po_charset_ascii);
1876 #if HAVE_ICONV
1877 xgettext_current_source_iconv = xgettext_global_source_iconv;
1878 #endif
1879
1880 extractor.extract_from_stream (fp, real_file_name, logical_file_name,
1881 extractor.flag_table, mdlp);
1882
1883 if (fp != stdin)
1884 fclose (fp);
1885 }
1886 else
1887 {
1888 xgettext_find_file (file_name, &logical_file_name, &real_file_name);
1889
1890 extractor.extract_from_file (real_file_name, logical_file_name,
1891 extractor.flag_table, mdlp);
1892 }
1893 free (logical_file_name);
1894 free (real_file_name);
1895 }
1896
1897 static message_ty *
xgettext_its_extract_callback(message_list_ty * mlp,const char * msgctxt,const char * msgid,lex_pos_ty * pos,const char * extracted_comment,const char * marker,enum its_whitespace_type_ty whitespace)1898 xgettext_its_extract_callback (message_list_ty *mlp,
1899 const char *msgctxt,
1900 const char *msgid,
1901 lex_pos_ty *pos,
1902 const char *extracted_comment,
1903 const char *marker,
1904 enum its_whitespace_type_ty whitespace)
1905 {
1906 message_ty *message;
1907
1908 message = remember_a_message (mlp,
1909 msgctxt == NULL ? NULL : xstrdup (msgctxt),
1910 xstrdup (msgid),
1911 false, false,
1912 null_context, pos,
1913 extracted_comment, NULL, false);
1914
1915 if (add_itstool_comments)
1916 {
1917 char *dot = xasprintf ("(itstool) path: %s", marker);
1918 message_comment_dot_append (message, dot);
1919 free (dot);
1920
1921 if (whitespace == ITS_WHITESPACE_PRESERVE)
1922 message->do_wrap = no;
1923 }
1924
1925 return message;
1926 }
1927
1928 static void
extract_from_xml_file(const char * file_name,its_rule_list_ty * rules,msgdomain_list_ty * mdlp)1929 extract_from_xml_file (const char *file_name,
1930 its_rule_list_ty *rules,
1931 msgdomain_list_ty *mdlp)
1932 {
1933 char *logical_file_name;
1934 char *real_file_name;
1935 FILE *fp = xgettext_open (file_name, &logical_file_name, &real_file_name);
1936
1937 /* The default encoding for XML is UTF-8. It can be overridden by
1938 an XML declaration in the XML file itself, not through the
1939 --from-code option. */
1940 xgettext_current_source_encoding = po_charset_utf8;
1941
1942 #if HAVE_ICONV
1943 xgettext_current_source_iconv = xgettext_global_source_iconv;
1944 #endif
1945
1946 its_rule_list_extract (rules, fp, real_file_name, logical_file_name,
1947 NULL,
1948 mdlp,
1949 xgettext_its_extract_callback);
1950
1951 if (fp != stdin)
1952 fclose (fp);
1953 free (logical_file_name);
1954 free (real_file_name);
1955 }
1956
1957
1958 bool
recognize_qt_formatstrings(void)1959 recognize_qt_formatstrings (void)
1960 {
1961 return recognize_format_qt
1962 && current_formatstring_parser3 == &formatstring_qt_plural;
1963 }
1964
1965
1966 static message_ty *
construct_header()1967 construct_header ()
1968 {
1969 char *project_id_version;
1970 time_t now;
1971 char *timestring;
1972 message_ty *mp;
1973 char *msgstr;
1974 char *comment;
1975 static lex_pos_ty pos = { __FILE__, __LINE__ };
1976
1977 if (package_name != NULL)
1978 {
1979 if (package_version != NULL)
1980 project_id_version = xasprintf ("%s %s", package_name, package_version);
1981 else
1982 project_id_version = xasprintf ("%s", package_name);
1983 }
1984 else
1985 project_id_version = xstrdup ("PACKAGE VERSION");
1986
1987 if (msgid_bugs_address != NULL && msgid_bugs_address[0] == '\0')
1988 multiline_warning (xasprintf (_("warning: ")),
1989 xstrdup (_("\
1990 The option --msgid-bugs-address was not specified.\n\
1991 If you are using a 'Makevars' file, please specify\n\
1992 the MSGID_BUGS_ADDRESS variable there; otherwise please\n\
1993 specify an --msgid-bugs-address command line option.\n\
1994 ")));
1995
1996 time (&now);
1997 timestring = po_strftime (&now);
1998
1999 msgstr = xasprintf ("\
2000 Project-Id-Version: %s\n\
2001 Report-Msgid-Bugs-To: %s\n\
2002 POT-Creation-Date: %s\n\
2003 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n\
2004 Last-Translator: FULL NAME <EMAIL@ADDRESS>\n\
2005 Language-Team: LANGUAGE <LL@li.org>\n\
2006 Language: \n\
2007 MIME-Version: 1.0\n\
2008 Content-Type: text/plain; charset=CHARSET\n\
2009 Content-Transfer-Encoding: 8bit\n",
2010 project_id_version,
2011 msgid_bugs_address != NULL ? msgid_bugs_address : "",
2012 timestring);
2013 free (timestring);
2014 free (project_id_version);
2015
2016 mp = message_alloc (NULL, "", NULL, msgstr, strlen (msgstr) + 1, &pos);
2017
2018 if (copyright_holder[0] != '\0')
2019 comment = xasprintf ("\
2020 SOME DESCRIPTIVE TITLE.\n\
2021 Copyright (C) YEAR %s\n\
2022 This file is distributed under the same license as the %s package.\n\
2023 FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n",
2024 copyright_holder,
2025 package_name != NULL ? package_name : "PACKAGE");
2026 else
2027 comment = xstrdup ("\
2028 SOME DESCRIPTIVE TITLE.\n\
2029 This file is put in the public domain.\n\
2030 FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n");
2031 message_comment_append (mp, comment);
2032 free (comment);
2033
2034 mp->is_fuzzy = true;
2035
2036 return mp;
2037 }
2038
2039 static void
finalize_header(msgdomain_list_ty * mdlp)2040 finalize_header (msgdomain_list_ty *mdlp)
2041 {
2042 /* If the generated PO file has plural forms, add a Plural-Forms template
2043 to the constructed header. */
2044 {
2045 bool has_plural;
2046 size_t i, j;
2047
2048 has_plural = false;
2049 for (i = 0; i < mdlp->nitems; i++)
2050 {
2051 message_list_ty *mlp = mdlp->item[i]->messages;
2052
2053 for (j = 0; j < mlp->nitems; j++)
2054 {
2055 message_ty *mp = mlp->item[j];
2056
2057 if (mp->msgid_plural != NULL)
2058 {
2059 has_plural = true;
2060 break;
2061 }
2062 }
2063 if (has_plural)
2064 break;
2065 }
2066
2067 if (has_plural)
2068 {
2069 message_ty *header =
2070 message_list_search (mdlp->item[0]->messages, NULL, "");
2071 if (header != NULL
2072 && c_strstr (header->msgstr, "Plural-Forms:") == NULL)
2073 {
2074 size_t insertpos = strlen (header->msgstr);
2075 const char *suffix;
2076 size_t suffix_len;
2077 char *new_msgstr;
2078
2079 suffix = "\nPlural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n";
2080 if (insertpos == 0 || header->msgstr[insertpos-1] == '\n')
2081 suffix++;
2082 suffix_len = strlen (suffix);
2083 new_msgstr = XNMALLOC (header->msgstr_len + suffix_len, char);
2084 memcpy (new_msgstr, header->msgstr, insertpos);
2085 memcpy (new_msgstr + insertpos, suffix, suffix_len);
2086 memcpy (new_msgstr + insertpos + suffix_len,
2087 header->msgstr + insertpos,
2088 header->msgstr_len - insertpos);
2089 header->msgstr = new_msgstr;
2090 header->msgstr_len = header->msgstr_len + suffix_len;
2091 }
2092 }
2093 }
2094
2095 /* If not all the strings were plain ASCII, or if the output syntax
2096 requires a charset conversion, set the charset in the header to UTF-8.
2097 All messages have already been converted to UTF-8 in remember_a_message
2098 and remember_a_message_plural. */
2099 {
2100 bool has_nonascii = false;
2101 size_t i;
2102
2103 for (i = 0; i < mdlp->nitems; i++)
2104 {
2105 message_list_ty *mlp = mdlp->item[i]->messages;
2106
2107 if (!is_ascii_message_list (mlp))
2108 has_nonascii = true;
2109 }
2110
2111 if (has_nonascii || output_syntax->requires_utf8)
2112 {
2113 message_list_ty *mlp = mdlp->item[0]->messages;
2114
2115 iconv_message_list (mlp, po_charset_utf8, po_charset_utf8, NULL);
2116 }
2117 }
2118 }
2119
2120
2121 static extractor_ty
language_to_extractor(const char * name)2122 language_to_extractor (const char *name)
2123 {
2124 struct table_ty
2125 {
2126 const char *name;
2127 extract_from_stream_func extract_from_stream;
2128 extract_from_file_func extract_from_file;
2129 flag_context_list_table_ty *flag_table;
2130 struct formatstring_parser *formatstring_parser1;
2131 struct formatstring_parser *formatstring_parser2;
2132 };
2133 typedef struct table_ty table_ty;
2134
2135 static table_ty table[] =
2136 {
2137 SCANNERS_PO
2138 SCANNERS_PROPERTIES
2139 SCANNERS_STRINGTABLE
2140 SCANNERS_C
2141 SCANNERS_PYTHON
2142 SCANNERS_JAVA
2143 SCANNERS_CSHARP
2144 SCANNERS_JAVASCRIPT
2145 SCANNERS_SCHEME
2146 SCANNERS_LISP
2147 SCANNERS_ELISP
2148 SCANNERS_LIBREP
2149 SCANNERS_RUBY
2150 SCANNERS_SH
2151 SCANNERS_AWK
2152 SCANNERS_LUA
2153 SCANNERS_SMALLTALK
2154 SCANNERS_VALA
2155 SCANNERS_TCL
2156 SCANNERS_PERL
2157 SCANNERS_PHP
2158 SCANNERS_YCP
2159 SCANNERS_RST
2160 SCANNERS_DESKTOP
2161 SCANNERS_GLADE
2162 SCANNERS_GSETTINGS
2163 SCANNERS_APPDATA
2164 /* Here may follow more languages and their scanners: pike, etc...
2165 Make sure new scanners honor the --exclude-file option. */
2166 };
2167
2168 table_ty *tp;
2169
2170 for (tp = table; tp < ENDOF(table); ++tp)
2171 if (c_strcasecmp (name, tp->name) == 0)
2172 {
2173 extractor_ty result;
2174
2175 result.extract_from_stream = tp->extract_from_stream;
2176 result.extract_from_file = tp->extract_from_file;
2177 result.flag_table = tp->flag_table;
2178 result.formatstring_parser1 = tp->formatstring_parser1;
2179 result.formatstring_parser2 = tp->formatstring_parser2;
2180 result.formatstring_parser3 = NULL;
2181
2182 /* Handle --qt. It's preferrable to handle this facility here rather
2183 than through an option --language=C++/Qt because the latter would
2184 conflict with the language "C++" regarding the file extensions. */
2185 if (recognize_format_qt && strcmp (tp->name, "C++") == 0)
2186 {
2187 result.flag_table = &flag_table_cxx_qt;
2188 result.formatstring_parser2 = &formatstring_qt;
2189 result.formatstring_parser3 = &formatstring_qt_plural;
2190 }
2191 /* Likewise for --kde. */
2192 if (recognize_format_kde && strcmp (tp->name, "C++") == 0)
2193 {
2194 result.flag_table = &flag_table_cxx_kde;
2195 result.formatstring_parser2 = &formatstring_kde;
2196 result.formatstring_parser3 = &formatstring_kde_kuit;
2197 }
2198 /* Likewise for --boost. */
2199 if (recognize_format_boost && strcmp (tp->name, "C++") == 0)
2200 {
2201 result.flag_table = &flag_table_cxx_boost;
2202 result.formatstring_parser2 = &formatstring_boost;
2203 }
2204
2205 return result;
2206 }
2207
2208 error (EXIT_FAILURE, 0, _("language '%s' unknown"), name);
2209 /* NOTREACHED */
2210 {
2211 extractor_ty result = { NULL, NULL, NULL, NULL };
2212 return result;
2213 }
2214 }
2215
2216
2217 static const char *
extension_to_language(const char * extension)2218 extension_to_language (const char *extension)
2219 {
2220 struct table_ty
2221 {
2222 const char *extension;
2223 const char *language;
2224 };
2225 typedef struct table_ty table_ty;
2226
2227 static table_ty table[] =
2228 {
2229 EXTENSIONS_PO
2230 EXTENSIONS_PROPERTIES
2231 EXTENSIONS_STRINGTABLE
2232 EXTENSIONS_C
2233 EXTENSIONS_PYTHON
2234 EXTENSIONS_JAVA
2235 EXTENSIONS_CSHARP
2236 EXTENSIONS_JAVASCRIPT
2237 EXTENSIONS_SCHEME
2238 EXTENSIONS_LISP
2239 EXTENSIONS_ELISP
2240 EXTENSIONS_LIBREP
2241 EXTENSIONS_RUBY
2242 EXTENSIONS_SH
2243 EXTENSIONS_AWK
2244 EXTENSIONS_LUA
2245 EXTENSIONS_SMALLTALK
2246 EXTENSIONS_VALA
2247 EXTENSIONS_TCL
2248 EXTENSIONS_PERL
2249 EXTENSIONS_PHP
2250 EXTENSIONS_YCP
2251 EXTENSIONS_RST
2252 EXTENSIONS_DESKTOP
2253 EXTENSIONS_GLADE
2254 EXTENSIONS_GSETTINGS
2255 EXTENSIONS_APPDATA
2256 /* Here may follow more file extensions... */
2257 };
2258
2259 table_ty *tp;
2260
2261 for (tp = table; tp < ENDOF(table); ++tp)
2262 if (strcmp (extension, tp->extension) == 0)
2263 return tp->language;
2264 return NULL;
2265 }
2266