1 /* Manipulates attributes of messages in translation catalogs.
2 Copyright (C) 2001-2005 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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 2, or (at your option)
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.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
33 #include "error-progname.h"
35 #include "relocatable.h"
43 #define _(str) gettext (str)
46 /* Force output of PO file even if empty. */
49 /* Bit mask of subsets to remove. */
52 REMOVE_UNTRANSLATED
= 1 << 0,
53 REMOVE_TRANSLATED
= 1 << 1,
54 REMOVE_FUZZY
= 1 << 2,
55 REMOVE_NONFUZZY
= 1 << 3,
56 REMOVE_OBSOLETE
= 1 << 4,
57 REMOVE_NONOBSOLETE
= 1 << 5
61 /* Bit mask of actions to perform on all messages. */
66 SET_OBSOLETE
= 1 << 2,
67 RESET_OBSOLETE
= 1 << 3
72 static const struct option long_options
[] =
74 { "add-location", no_argument
, &line_comment
, 1 },
75 { "clear-fuzzy", no_argument
, NULL
, CHAR_MAX
+ 8 },
76 { "clear-obsolete", no_argument
, NULL
, CHAR_MAX
+ 10 },
77 { "directory", required_argument
, NULL
, 'D' },
78 { "escape", no_argument
, NULL
, 'E' },
79 { "force-po", no_argument
, &force_po
, 1 },
80 { "fuzzy", no_argument
, NULL
, CHAR_MAX
+ 11 },
81 { "help", no_argument
, NULL
, 'h' },
82 { "ignore-file", required_argument
, NULL
, CHAR_MAX
+ 15 },
83 { "indent", no_argument
, NULL
, 'i' },
84 { "no-escape", no_argument
, NULL
, 'e' },
85 { "no-fuzzy", no_argument
, NULL
, CHAR_MAX
+ 3 },
86 { "no-location", no_argument
, &line_comment
, 0 },
87 { "no-obsolete", no_argument
, NULL
, CHAR_MAX
+ 5 },
88 { "no-wrap", no_argument
, NULL
, CHAR_MAX
+ 13 },
89 { "obsolete", no_argument
, NULL
, CHAR_MAX
+ 12 },
90 { "only-file", required_argument
, NULL
, CHAR_MAX
+ 14 },
91 { "only-fuzzy", no_argument
, NULL
, CHAR_MAX
+ 4 },
92 { "only-obsolete", no_argument
, NULL
, CHAR_MAX
+ 6 },
93 { "output-file", required_argument
, NULL
, 'o' },
94 { "properties-input", no_argument
, NULL
, 'P' },
95 { "properties-output", no_argument
, NULL
, 'p' },
96 { "set-fuzzy", no_argument
, NULL
, CHAR_MAX
+ 7 },
97 { "set-obsolete", no_argument
, NULL
, CHAR_MAX
+ 9 },
98 { "sort-by-file", no_argument
, NULL
, 'F' },
99 { "sort-output", no_argument
, NULL
, 's' },
100 { "stringtable-input", no_argument
, NULL
, CHAR_MAX
+ 16 },
101 { "stringtable-output", no_argument
, NULL
, CHAR_MAX
+ 17 },
102 { "strict", no_argument
, NULL
, 'S' },
103 { "translated", no_argument
, NULL
, CHAR_MAX
+ 1 },
104 { "untranslated", no_argument
, NULL
, CHAR_MAX
+ 2 },
105 { "version", no_argument
, NULL
, 'V' },
106 { "width", required_argument
, NULL
, 'w', },
111 /* Forward declaration of local functions. */
112 static void usage (int status
)
113 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
114 __attribute__ ((noreturn
))
117 static msgdomain_list_ty
*process_msgdomain_list (msgdomain_list_ty
*mdlp
,
118 msgdomain_list_ty
*only_mdlp
,
119 msgdomain_list_ty
*ignore_mdlp
);
123 main (int argc
, char **argv
)
129 const char *input_file
;
130 const char *only_file
;
131 const char *ignore_file
;
132 msgdomain_list_ty
*only_mdlp
;
133 msgdomain_list_ty
*ignore_mdlp
;
134 msgdomain_list_ty
*result
;
135 bool sort_by_msgid
= false;
136 bool sort_by_filepos
= false;
138 /* Set program name for messages. */
139 set_program_name (argv
[0]);
140 error_print_progname
= maybe_print_progname
;
142 #ifdef HAVE_SETLOCALE
143 /* Set locale via LC_ALL. */
144 setlocale (LC_ALL
, "");
147 /* Set the text message domain. */
148 bindtextdomain (PACKAGE
, relocate (LOCALEDIR
));
149 textdomain (PACKAGE
);
151 /* Ensure that write errors on stdout are detected. */
152 atexit (close_stdout
);
154 /* Set default values for variables. */
162 while ((optchar
= getopt_long (argc
, argv
, "D:eEFhino:pPsVw:", long_options
,
166 case '\0': /* Long option. */
170 dir_list_append (optarg
);
174 message_print_style_escape (false);
178 message_print_style_escape (true);
182 sort_by_filepos
= true;
190 message_print_style_indent ();
198 output_file
= optarg
;
202 message_print_syntax_properties ();
206 input_syntax
= syntax_properties
;
210 sort_by_msgid
= true;
214 message_print_style_uniforum ();
225 value
= strtol (optarg
, &endp
, 10);
227 message_page_width_set (value
);
231 case CHAR_MAX
+ 1: /* --translated */
232 to_remove
|= REMOVE_UNTRANSLATED
;
235 case CHAR_MAX
+ 2: /* --untranslated */
236 to_remove
|= REMOVE_TRANSLATED
;
239 case CHAR_MAX
+ 3: /* --no-fuzzy */
240 to_remove
|= REMOVE_FUZZY
;
243 case CHAR_MAX
+ 4: /* --only-fuzzy */
244 to_remove
|= REMOVE_NONFUZZY
;
247 case CHAR_MAX
+ 5: /* --no-obsolete */
248 to_remove
|= REMOVE_OBSOLETE
;
251 case CHAR_MAX
+ 6: /* --only-obsolete */
252 to_remove
|= REMOVE_NONOBSOLETE
;
255 case CHAR_MAX
+ 7: /* --set-fuzzy */
256 to_change
|= SET_FUZZY
;
259 case CHAR_MAX
+ 8: /* --clear-fuzzy */
260 to_change
|= RESET_FUZZY
;
263 case CHAR_MAX
+ 9: /* --set-obsolete */
264 to_change
|= SET_OBSOLETE
;
267 case CHAR_MAX
+ 10: /* --clear-obsolete */
268 to_change
|= RESET_OBSOLETE
;
271 case CHAR_MAX
+ 11: /* --fuzzy */
272 to_remove
|= REMOVE_NONFUZZY
;
273 to_change
|= RESET_FUZZY
;
276 case CHAR_MAX
+ 12: /* --obsolete */
277 to_remove
|= REMOVE_NONOBSOLETE
;
278 to_change
|= RESET_OBSOLETE
;
281 case CHAR_MAX
+ 13: /* --no-wrap */
282 message_page_width_ignore ();
285 case CHAR_MAX
+ 14: /* --only-file */
289 case CHAR_MAX
+ 15: /* --ignore-file */
290 ignore_file
= optarg
;
293 case CHAR_MAX
+ 16: /* --stringtable-input */
294 input_syntax
= syntax_stringtable
;
297 case CHAR_MAX
+ 17: /* --stringtable-output */
298 message_print_syntax_stringtable ();
302 usage (EXIT_FAILURE
);
306 /* Version information requested. */
309 printf ("%s (GNU %s) %s\n", basename (program_name
), PACKAGE
, VERSION
);
310 /* xgettext: no-wrap */
311 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
312 This is free software; see the source for copying conditions. There is NO\n\
313 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
316 printf (_("Written by %s.\n"), "Bruno Haible");
320 /* Help is requested. */
322 usage (EXIT_SUCCESS
);
324 /* Test whether we have an .po file name as argument. */
327 else if (optind
+ 1 == argc
)
328 input_file
= argv
[optind
];
331 error (EXIT_SUCCESS
, 0, _("at most one input file allowed"));
332 usage (EXIT_FAILURE
);
335 /* Verify selected options. */
336 if (!line_comment
&& sort_by_filepos
)
337 error (EXIT_FAILURE
, 0, _("%s and %s are mutually exclusive"),
338 "--no-location", "--sort-by-file");
340 if (sort_by_msgid
&& sort_by_filepos
)
341 error (EXIT_FAILURE
, 0, _("%s and %s are mutually exclusive"),
342 "--sort-output", "--sort-by-file");
344 /* Read input file. */
345 result
= read_po_file (input_file
);
347 /* Read optional files that limit the extent of the attribute changes. */
348 only_mdlp
= (only_file
!= NULL
? read_po_file (only_file
) : NULL
);
349 ignore_mdlp
= (ignore_file
!= NULL
? read_po_file (ignore_file
) : NULL
);
351 /* Filter the messages and manipulate the attributes. */
352 result
= process_msgdomain_list (result
, only_mdlp
, ignore_mdlp
);
354 /* Sorting the list of messages. */
356 msgdomain_list_sort_by_filepos (result
);
357 else if (sort_by_msgid
)
358 msgdomain_list_sort_by_msgid (result
);
360 /* Write the PO file. */
361 msgdomain_list_print (result
, output_file
, force_po
, false);
367 /* Display usage information and exit. */
371 if (status
!= EXIT_SUCCESS
)
372 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
377 Usage: %s [OPTION] [INPUTFILE]\n\
380 /* xgettext: no-wrap */
382 Filters the messages of a translation catalog according to their attributes,\n\
383 and manipulates the attributes.\n"));
386 Mandatory arguments to long options are mandatory for short options too.\n"));
389 Input file location:\n"));
391 INPUTFILE input PO file\n"));
393 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
395 If no input file is given or if it is -, standard input is read.\n"));
398 Output file location:\n"));
400 -o, --output-file=FILE write output to specified file\n"));
402 The results are written to standard output if no output file is specified\n\
406 Message selection:\n"));
408 --translated keep translated, remove untranslated messages\n"));
410 --untranslated keep untranslated, remove translated messages\n"));
412 --no-fuzzy remove 'fuzzy' marked messages\n"));
414 --only-fuzzy keep 'fuzzy' marked messages\n"));
416 --no-obsolete remove obsolete #~ messages\n"));
418 --only-obsolete keep obsolete #~ messages\n"));
421 Attribute manipulation:\n"));
423 --set-fuzzy set all messages 'fuzzy'\n"));
425 --clear-fuzzy set all messages non-'fuzzy'\n"));
427 --set-obsolete set all messages obsolete\n"));
429 --clear-obsolete set all messages non-obsolete\n"));
431 --only-file=FILE.po manipulate only entries listed in FILE.po\n"));
433 --ignore-file=FILE.po manipulate only entries not listed in FILE.po\n"));
435 --fuzzy synonym for --only-fuzzy --clear-fuzzy\n"));
437 --obsolete synonym for --only-obsolete --clear-obsolete\n"));
440 Input file syntax:\n"));
442 -P, --properties-input input file is in Java .properties syntax\n"));
444 --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
447 Output details:\n"));
449 -e, --no-escape do not use C escapes in output (default)\n"));
451 -E, --escape use C escapes in output, no extended chars\n"));
453 --force-po write PO file even if empty\n"));
455 -i, --indent write the .po file using indented style\n"));
457 --no-location do not write '#: filename:line' lines\n"));
459 -n, --add-location generate '#: filename:line' lines (default)\n"));
461 --strict write out strict Uniforum conforming .po file\n"));
463 -p, --properties-output write out a Java .properties file\n"));
465 --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
467 -w, --width=NUMBER set output page width\n"));
469 --no-wrap do not break long message lines, longer than\n\
470 the output page width, into several lines\n"));
472 -s, --sort-output generate sorted output\n"));
474 -F, --sort-by-file sort output by file location\n"));
477 Informative output:\n"));
479 -h, --help display this help and exit\n"));
481 -V, --version output version information and exit\n"));
483 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
491 /* Return true if a message should be kept. */
493 is_message_selected (const message_ty
*mp
)
495 /* Always keep the header entry. */
496 if (mp
->msgid
[0] == '\0')
499 if ((to_remove
& (REMOVE_UNTRANSLATED
| REMOVE_TRANSLATED
))
500 && (mp
->msgstr
[0] == '\0'
501 ? to_remove
& REMOVE_UNTRANSLATED
502 : to_remove
& REMOVE_TRANSLATED
))
505 if ((to_remove
& (REMOVE_FUZZY
| REMOVE_NONFUZZY
))
507 ? to_remove
& REMOVE_FUZZY
508 : to_remove
& REMOVE_NONFUZZY
))
511 if ((to_remove
& (REMOVE_OBSOLETE
| REMOVE_NONOBSOLETE
))
513 ? to_remove
& REMOVE_OBSOLETE
514 : to_remove
& REMOVE_NONOBSOLETE
))
522 process_message_list (message_list_ty
*mlp
,
523 message_list_ty
*only_mlp
, message_list_ty
*ignore_mlp
)
525 /* Keep only the selected messages. */
526 message_list_remove_if_not (mlp
, is_message_selected
);
528 /* Change the attributes. */
533 for (j
= 0; j
< mlp
->nitems
; j
++)
535 message_ty
*mp
= mlp
->item
[j
];
537 /* Attribute changes only affect messages listed in --only-file
538 and not listed in --ignore-file. */
540 ? message_list_search (only_mlp
, mp
->msgid
) != NULL
543 ? message_list_search (ignore_mlp
, mp
->msgid
) == NULL
546 if (to_change
& SET_FUZZY
)
548 if (to_change
& RESET_FUZZY
)
549 mp
->is_fuzzy
= false;
550 /* Always keep the header entry non-obsolete. */
551 if ((to_change
& SET_OBSOLETE
) && (mp
->msgid
[0] != '\0'))
553 if (to_change
& RESET_OBSOLETE
)
554 mp
->obsolete
= false;
561 static msgdomain_list_ty
*
562 process_msgdomain_list (msgdomain_list_ty
*mdlp
,
563 msgdomain_list_ty
*only_mdlp
,
564 msgdomain_list_ty
*ignore_mdlp
)
568 for (k
= 0; k
< mdlp
->nitems
; k
++)
569 process_message_list (mdlp
->item
[k
]->messages
,
571 ? msgdomain_list_sublist (only_mdlp
,
572 mdlp
->item
[k
]->domain
,
576 ? msgdomain_list_sublist (ignore_mdlp
,
577 mdlp
->item
[k
]->domain
,