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