No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / msgattrib.c
blob9e612429893d3dc01f5c0a1cf460adeea458d080
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)
8 any later version.
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. */
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
24 #include <getopt.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <locale.h>
30 #include "closeout.h"
31 #include "dir-list.h"
32 #include "error.h"
33 #include "error-progname.h"
34 #include "progname.h"
35 #include "relocatable.h"
36 #include "basename.h"
37 #include "message.h"
38 #include "read-po.h"
39 #include "write-po.h"
40 #include "exit.h"
41 #include "gettext.h"
43 #define _(str) gettext (str)
46 /* Force output of PO file even if empty. */
47 static int force_po;
49 /* Bit mask of subsets to remove. */
50 enum
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
59 static int to_remove;
61 /* Bit mask of actions to perform on all messages. */
62 enum
64 SET_FUZZY = 1 << 0,
65 RESET_FUZZY = 1 << 1,
66 SET_OBSOLETE = 1 << 2,
67 RESET_OBSOLETE = 1 << 3
69 static int to_change;
71 /* Long options. */
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', },
107 { NULL, 0, NULL, 0 }
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))
115 #endif
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)
125 int optchar;
126 bool do_help;
127 bool do_version;
128 char *output_file;
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, "");
145 #endif
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. */
155 do_help = false;
156 do_version = false;
157 output_file = NULL;
158 input_file = NULL;
159 only_file = NULL;
160 ignore_file = NULL;
162 while ((optchar = getopt_long (argc, argv, "D:eEFhino:pPsVw:", long_options,
163 NULL)) != EOF)
164 switch (optchar)
166 case '\0': /* Long option. */
167 break;
169 case 'D':
170 dir_list_append (optarg);
171 break;
173 case 'e':
174 message_print_style_escape (false);
175 break;
177 case 'E':
178 message_print_style_escape (true);
179 break;
181 case 'F':
182 sort_by_filepos = true;
183 break;
185 case 'h':
186 do_help = true;
187 break;
189 case 'i':
190 message_print_style_indent ();
191 break;
193 case 'n':
194 line_comment = 1;
195 break;
197 case 'o':
198 output_file = optarg;
199 break;
201 case 'p':
202 message_print_syntax_properties ();
203 break;
205 case 'P':
206 input_syntax = syntax_properties;
207 break;
209 case 's':
210 sort_by_msgid = true;
211 break;
213 case 'S':
214 message_print_style_uniforum ();
215 break;
217 case 'V':
218 do_version = true;
219 break;
221 case 'w':
223 int value;
224 char *endp;
225 value = strtol (optarg, &endp, 10);
226 if (endp != optarg)
227 message_page_width_set (value);
229 break;
231 case CHAR_MAX + 1: /* --translated */
232 to_remove |= REMOVE_UNTRANSLATED;
233 break;
235 case CHAR_MAX + 2: /* --untranslated */
236 to_remove |= REMOVE_TRANSLATED;
237 break;
239 case CHAR_MAX + 3: /* --no-fuzzy */
240 to_remove |= REMOVE_FUZZY;
241 break;
243 case CHAR_MAX + 4: /* --only-fuzzy */
244 to_remove |= REMOVE_NONFUZZY;
245 break;
247 case CHAR_MAX + 5: /* --no-obsolete */
248 to_remove |= REMOVE_OBSOLETE;
249 break;
251 case CHAR_MAX + 6: /* --only-obsolete */
252 to_remove |= REMOVE_NONOBSOLETE;
253 break;
255 case CHAR_MAX + 7: /* --set-fuzzy */
256 to_change |= SET_FUZZY;
257 break;
259 case CHAR_MAX + 8: /* --clear-fuzzy */
260 to_change |= RESET_FUZZY;
261 break;
263 case CHAR_MAX + 9: /* --set-obsolete */
264 to_change |= SET_OBSOLETE;
265 break;
267 case CHAR_MAX + 10: /* --clear-obsolete */
268 to_change |= RESET_OBSOLETE;
269 break;
271 case CHAR_MAX + 11: /* --fuzzy */
272 to_remove |= REMOVE_NONFUZZY;
273 to_change |= RESET_FUZZY;
274 break;
276 case CHAR_MAX + 12: /* --obsolete */
277 to_remove |= REMOVE_NONOBSOLETE;
278 to_change |= RESET_OBSOLETE;
279 break;
281 case CHAR_MAX + 13: /* --no-wrap */
282 message_page_width_ignore ();
283 break;
285 case CHAR_MAX + 14: /* --only-file */
286 only_file = optarg;
287 break;
289 case CHAR_MAX + 15: /* --ignore-file */
290 ignore_file = optarg;
291 break;
293 case CHAR_MAX + 16: /* --stringtable-input */
294 input_syntax = syntax_stringtable;
295 break;
297 case CHAR_MAX + 17: /* --stringtable-output */
298 message_print_syntax_stringtable ();
299 break;
301 default:
302 usage (EXIT_FAILURE);
303 /* NOTREACHED */
306 /* Version information requested. */
307 if (do_version)
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\
315 "2001-2005");
316 printf (_("Written by %s.\n"), "Bruno Haible");
317 exit (EXIT_SUCCESS);
320 /* Help is requested. */
321 if (do_help)
322 usage (EXIT_SUCCESS);
324 /* Test whether we have an .po file name as argument. */
325 if (optind == argc)
326 input_file = "-";
327 else if (optind + 1 == argc)
328 input_file = argv[optind];
329 else
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. */
355 if (sort_by_filepos)
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);
363 exit (EXIT_SUCCESS);
367 /* Display usage information and exit. */
368 static void
369 usage (int status)
371 if (status != EXIT_SUCCESS)
372 fprintf (stderr, _("Try `%s --help' for more information.\n"),
373 program_name);
374 else
376 printf (_("\
377 Usage: %s [OPTION] [INPUTFILE]\n\
378 "), program_name);
379 printf ("\n");
380 /* xgettext: no-wrap */
381 printf (_("\
382 Filters the messages of a translation catalog according to their attributes,\n\
383 and manipulates the attributes.\n"));
384 printf ("\n");
385 printf (_("\
386 Mandatory arguments to long options are mandatory for short options too.\n"));
387 printf ("\n");
388 printf (_("\
389 Input file location:\n"));
390 printf (_("\
391 INPUTFILE input PO file\n"));
392 printf (_("\
393 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
394 printf (_("\
395 If no input file is given or if it is -, standard input is read.\n"));
396 printf ("\n");
397 printf (_("\
398 Output file location:\n"));
399 printf (_("\
400 -o, --output-file=FILE write output to specified file\n"));
401 printf (_("\
402 The results are written to standard output if no output file is specified\n\
403 or if it is -.\n"));
404 printf ("\n");
405 printf (_("\
406 Message selection:\n"));
407 printf (_("\
408 --translated keep translated, remove untranslated messages\n"));
409 printf (_("\
410 --untranslated keep untranslated, remove translated messages\n"));
411 printf (_("\
412 --no-fuzzy remove 'fuzzy' marked messages\n"));
413 printf (_("\
414 --only-fuzzy keep 'fuzzy' marked messages\n"));
415 printf (_("\
416 --no-obsolete remove obsolete #~ messages\n"));
417 printf (_("\
418 --only-obsolete keep obsolete #~ messages\n"));
419 printf ("\n");
420 printf (_("\
421 Attribute manipulation:\n"));
422 printf (_("\
423 --set-fuzzy set all messages 'fuzzy'\n"));
424 printf (_("\
425 --clear-fuzzy set all messages non-'fuzzy'\n"));
426 printf (_("\
427 --set-obsolete set all messages obsolete\n"));
428 printf (_("\
429 --clear-obsolete set all messages non-obsolete\n"));
430 printf (_("\
431 --only-file=FILE.po manipulate only entries listed in FILE.po\n"));
432 printf (_("\
433 --ignore-file=FILE.po manipulate only entries not listed in FILE.po\n"));
434 printf (_("\
435 --fuzzy synonym for --only-fuzzy --clear-fuzzy\n"));
436 printf (_("\
437 --obsolete synonym for --only-obsolete --clear-obsolete\n"));
438 printf ("\n");
439 printf (_("\
440 Input file syntax:\n"));
441 printf (_("\
442 -P, --properties-input input file is in Java .properties syntax\n"));
443 printf (_("\
444 --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
445 printf ("\n");
446 printf (_("\
447 Output details:\n"));
448 printf (_("\
449 -e, --no-escape do not use C escapes in output (default)\n"));
450 printf (_("\
451 -E, --escape use C escapes in output, no extended chars\n"));
452 printf (_("\
453 --force-po write PO file even if empty\n"));
454 printf (_("\
455 -i, --indent write the .po file using indented style\n"));
456 printf (_("\
457 --no-location do not write '#: filename:line' lines\n"));
458 printf (_("\
459 -n, --add-location generate '#: filename:line' lines (default)\n"));
460 printf (_("\
461 --strict write out strict Uniforum conforming .po file\n"));
462 printf (_("\
463 -p, --properties-output write out a Java .properties file\n"));
464 printf (_("\
465 --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
466 printf (_("\
467 -w, --width=NUMBER set output page width\n"));
468 printf (_("\
469 --no-wrap do not break long message lines, longer than\n\
470 the output page width, into several lines\n"));
471 printf (_("\
472 -s, --sort-output generate sorted output\n"));
473 printf (_("\
474 -F, --sort-by-file sort output by file location\n"));
475 printf ("\n");
476 printf (_("\
477 Informative output:\n"));
478 printf (_("\
479 -h, --help display this help and exit\n"));
480 printf (_("\
481 -V, --version output version information and exit\n"));
482 printf ("\n");
483 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
484 stdout);
487 exit (status);
491 /* Return true if a message should be kept. */
492 static bool
493 is_message_selected (const message_ty *mp)
495 /* Always keep the header entry. */
496 if (mp->msgid[0] == '\0')
497 return true;
499 if ((to_remove & (REMOVE_UNTRANSLATED | REMOVE_TRANSLATED))
500 && (mp->msgstr[0] == '\0'
501 ? to_remove & REMOVE_UNTRANSLATED
502 : to_remove & REMOVE_TRANSLATED))
503 return false;
505 if ((to_remove & (REMOVE_FUZZY | REMOVE_NONFUZZY))
506 && (mp->is_fuzzy
507 ? to_remove & REMOVE_FUZZY
508 : to_remove & REMOVE_NONFUZZY))
509 return false;
511 if ((to_remove & (REMOVE_OBSOLETE | REMOVE_NONOBSOLETE))
512 && (mp->obsolete
513 ? to_remove & REMOVE_OBSOLETE
514 : to_remove & REMOVE_NONOBSOLETE))
515 return false;
517 return true;
521 static void
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. */
529 if (to_change)
531 size_t j;
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. */
539 if ((only_mlp
540 ? message_list_search (only_mlp, mp->msgid) != NULL
541 : true)
542 && (ignore_mlp
543 ? message_list_search (ignore_mlp, mp->msgid) == NULL
544 : true))
546 if (to_change & SET_FUZZY)
547 mp->is_fuzzy = true;
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'))
552 mp->obsolete = true;
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)
566 size_t k;
568 for (k = 0; k < mdlp->nitems; k++)
569 process_message_list (mdlp->item[k]->messages,
570 only_mdlp
571 ? msgdomain_list_sublist (only_mdlp,
572 mdlp->item[k]->domain,
573 true)
574 : NULL,
575 ignore_mdlp
576 ? msgdomain_list_sublist (ignore_mdlp,
577 mdlp->item[k]->domain,
578 false)
579 : NULL);
581 return mdlp;