(root)/
gettext-0.22.4/
gettext-tools/
src/
msgcat.c
       1  /* Concatenates several translation catalogs.
       2     Copyright (C) 2001-2007, 2009-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <haible@clisp.cons.org>, 2001.
       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  
      19  #ifdef HAVE_CONFIG_H
      20  # include "config.h"
      21  #endif
      22  
      23  #include <getopt.h>
      24  #include <limits.h>
      25  #include <stdio.h>
      26  #include <stdlib.h>
      27  #include <locale.h>
      28  
      29  #include <textstyle.h>
      30  
      31  #include "noreturn.h"
      32  #include "closeout.h"
      33  #include "dir-list.h"
      34  #include "str-list.h"
      35  #include "file-list.h"
      36  #include "error.h"
      37  #include "error-progname.h"
      38  #include "progname.h"
      39  #include "relocatable.h"
      40  #include "basename-lgpl.h"
      41  #include "message.h"
      42  #include "read-catalog.h"
      43  #include "read-po.h"
      44  #include "read-properties.h"
      45  #include "read-stringtable.h"
      46  #include "write-catalog.h"
      47  #include "write-po.h"
      48  #include "write-properties.h"
      49  #include "write-stringtable.h"
      50  #include "msgl-cat.h"
      51  #include "msgl-header.h"
      52  #include "propername.h"
      53  #include "gettext.h"
      54  
      55  #define _(str) gettext (str)
      56  
      57  
      58  /* Force output of PO file even if empty.  */
      59  static int force_po;
      60  
      61  /* Target encoding.  */
      62  static const char *to_code;
      63  
      64  /* Long options.  */
      65  static const struct option long_options[] =
      66  {
      67    { "add-location", optional_argument, NULL, 'n' },
      68    { "color", optional_argument, NULL, CHAR_MAX + 5 },
      69    { "directory", required_argument, NULL, 'D' },
      70    { "escape", no_argument, NULL, 'E' },
      71    { "files-from", required_argument, NULL, 'f' },
      72    { "force-po", no_argument, &force_po, 1 },
      73    { "help", no_argument, NULL, 'h' },
      74    { "indent", no_argument, NULL, 'i' },
      75    { "lang", required_argument, NULL, CHAR_MAX + 7 },
      76    { "no-escape", no_argument, NULL, 'e' },
      77    { "no-location", no_argument, NULL, CHAR_MAX + 8 },
      78    { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
      79    { "output-file", required_argument, NULL, 'o' },
      80    { "properties-input", no_argument, NULL, 'P' },
      81    { "properties-output", no_argument, NULL, 'p' },
      82    { "sort-by-file", no_argument, NULL, 'F' },
      83    { "sort-output", no_argument, NULL, 's' },
      84    { "strict", no_argument, NULL, 'S' },
      85    { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
      86    { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
      87    { "style", required_argument, NULL, CHAR_MAX + 6 },
      88    { "to-code", required_argument, NULL, 't' },
      89    { "unique", no_argument, NULL, 'u' },
      90    { "use-first", no_argument, NULL, CHAR_MAX + 1 },
      91    { "version", no_argument, NULL, 'V' },
      92    { "width", required_argument, NULL, 'w' },
      93    { "more-than", required_argument, NULL, '>' },
      94    { "less-than", required_argument, NULL, '<' },
      95    { NULL, 0, NULL, 0 }
      96  };
      97  
      98  
      99  /* Forward declaration of local functions.  */
     100  _GL_NORETURN_FUNC static void usage (int status);
     101  
     102  
     103  int
     104  main (int argc, char **argv)
     105  {
     106    int cnt;
     107    int optchar;
     108    bool do_help;
     109    bool do_version;
     110    char *output_file;
     111    const char *files_from;
     112    string_list_ty *file_list;
     113    msgdomain_list_ty *result;
     114    catalog_input_format_ty input_syntax = &input_format_po;
     115    catalog_output_format_ty output_syntax = &output_format_po;
     116    bool sort_by_msgid = false;
     117    bool sort_by_filepos = false;
     118    /* Language (ISO-639 code) and optional territory (ISO-3166 code).  */
     119    const char *catalogname = NULL;
     120  
     121    /* Set program name for messages.  */
     122    set_program_name (argv[0]);
     123    error_print_progname = maybe_print_progname;
     124  
     125    /* Set locale via LC_ALL.  */
     126    setlocale (LC_ALL, "");
     127  
     128    /* Set the text message domain.  */
     129    bindtextdomain (PACKAGE, relocate (LOCALEDIR));
     130    bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
     131    textdomain (PACKAGE);
     132  
     133    /* Ensure that write errors on stdout are detected.  */
     134    atexit (close_stdout);
     135  
     136    /* Set default values for variables.  */
     137    do_help = false;
     138    do_version = false;
     139    output_file = NULL;
     140    files_from = NULL;
     141    more_than = 0;
     142    less_than = INT_MAX;
     143    use_first = false;
     144  
     145    while ((optchar = getopt_long (argc, argv, "<:>:D:eEf:Fhino:pPst:uVw:",
     146                                   long_options, NULL)) != EOF)
     147      switch (optchar)
     148        {
     149        case '\0':                /* Long option.  */
     150          break;
     151  
     152        case '>':
     153          {
     154            int value;
     155            char *endp;
     156            value = strtol (optarg, &endp, 10);
     157            if (endp != optarg)
     158              more_than = value;
     159          }
     160          break;
     161  
     162        case '<':
     163          {
     164            int value;
     165            char *endp;
     166            value = strtol (optarg, &endp, 10);
     167            if (endp != optarg)
     168              less_than = value;
     169          }
     170          break;
     171  
     172        case 'D':
     173          dir_list_append (optarg);
     174          break;
     175  
     176        case 'e':
     177          message_print_style_escape (false);
     178          break;
     179  
     180        case 'E':
     181          message_print_style_escape (true);
     182          break;
     183  
     184        case 'f':
     185          files_from = optarg;
     186          break;
     187  
     188        case 'F':
     189          sort_by_filepos = true;
     190          break;
     191  
     192        case 'h':
     193          do_help = true;
     194          break;
     195  
     196        case 'i':
     197          message_print_style_indent ();
     198          break;
     199  
     200        case 'n':
     201          if (handle_filepos_comment_option (optarg))
     202            usage (EXIT_FAILURE);
     203          break;
     204  
     205        case 'o':
     206          output_file = optarg;
     207          break;
     208  
     209        case 'p':
     210          output_syntax = &output_format_properties;
     211          break;
     212  
     213        case 'P':
     214          input_syntax = &input_format_properties;
     215          break;
     216  
     217        case 's':
     218          sort_by_msgid = true;
     219          break;
     220  
     221        case 'S':
     222          message_print_style_uniforum ();
     223          break;
     224  
     225        case 't':
     226          to_code = optarg;
     227          break;
     228  
     229        case 'u':
     230          less_than = 2;
     231          break;
     232  
     233        case 'V':
     234          do_version = true;
     235          break;
     236  
     237        case 'w':
     238          {
     239            int value;
     240            char *endp;
     241            value = strtol (optarg, &endp, 10);
     242            if (endp != optarg)
     243              message_page_width_set (value);
     244          }
     245          break;
     246  
     247        case CHAR_MAX + 1:
     248          use_first = true;
     249          break;
     250  
     251        case CHAR_MAX + 2: /* --no-wrap */
     252          message_page_width_ignore ();
     253          break;
     254  
     255        case CHAR_MAX + 3: /* --stringtable-input */
     256          input_syntax = &input_format_stringtable;
     257          break;
     258  
     259        case CHAR_MAX + 4: /* --stringtable-output */
     260          output_syntax = &output_format_stringtable;
     261          break;
     262  
     263        case CHAR_MAX + 5: /* --color */
     264          if (handle_color_option (optarg))
     265            usage (EXIT_FAILURE);
     266          break;
     267  
     268        case CHAR_MAX + 6: /* --style */
     269          handle_style_option (optarg);
     270          break;
     271  
     272        case CHAR_MAX + 7: /* --lang */
     273          catalogname = optarg;
     274          break;
     275  
     276        case CHAR_MAX + 8: /* --no-location */
     277          message_print_style_filepos (filepos_comment_none);
     278          break;
     279  
     280        default:
     281          usage (EXIT_FAILURE);
     282          /* NOTREACHED */
     283        }
     284  
     285    /* Version information requested.  */
     286    if (do_version)
     287      {
     288        printf ("%s (GNU %s) %s\n", last_component (program_name),
     289                PACKAGE, VERSION);
     290        /* xgettext: no-wrap */
     291        printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
     292  License GPLv3+: GNU GPL version 3 or later <%s>\n\
     293  This is free software: you are free to change and redistribute it.\n\
     294  There is NO WARRANTY, to the extent permitted by law.\n\
     295  "),
     296                "2001-2023", "https://gnu.org/licenses/gpl.html");
     297        printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
     298        exit (EXIT_SUCCESS);
     299      }
     300  
     301    /* Help is requested.  */
     302    if (do_help)
     303      usage (EXIT_SUCCESS);
     304  
     305    if (color_test_mode)
     306      {
     307        print_color_test ();
     308        exit (EXIT_SUCCESS);
     309      }
     310  
     311    /* Verify selected options.  */
     312    if (sort_by_msgid && sort_by_filepos)
     313      error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
     314             "--sort-output", "--sort-by-file");
     315  
     316    /* Check the message selection criteria for sanity.  */
     317    if (more_than >= less_than || less_than < 2)
     318      error (EXIT_FAILURE, 0,
     319             _("impossible selection criteria specified (%d < n < %d)"),
     320             more_than, less_than);
     321  
     322    /* Determine list of files we have to process.  */
     323    if (files_from != NULL)
     324      file_list = read_names_from_file (files_from);
     325    else
     326      file_list = string_list_alloc ();
     327    /* Append names from command line.  */
     328    for (cnt = optind; cnt < argc; ++cnt)
     329      string_list_append_unique (file_list, argv[cnt]);
     330  
     331    /* Read input files, then filter, convert and merge messages.  */
     332    result =
     333      catenate_msgdomain_list (file_list, input_syntax,
     334                               output_syntax->requires_utf8 ? "UTF-8" : to_code);
     335  
     336    string_list_free (file_list);
     337  
     338    /* Sorting the list of messages.  */
     339    if (sort_by_filepos)
     340      msgdomain_list_sort_by_filepos (result);
     341    else if (sort_by_msgid)
     342      msgdomain_list_sort_by_msgid (result);
     343  
     344    /* Set the Language field in the header.  */
     345    if (catalogname != NULL)
     346      msgdomain_list_set_header_field (result, "Language:", catalogname);
     347  
     348    /* Write the PO file.  */
     349    msgdomain_list_print (result, output_file, output_syntax, force_po, false);
     350  
     351    exit (error_message_count > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
     352  }
     353  
     354  
     355  /* Display usage information and exit.  */
     356  static void
     357  usage (int status)
     358  {
     359    if (status != EXIT_SUCCESS)
     360      fprintf (stderr, _("Try '%s --help' for more information.\n"),
     361               program_name);
     362    else
     363      {
     364        printf (_("\
     365  Usage: %s [OPTION] [INPUTFILE]...\n\
     366  "), program_name);
     367        printf ("\n");
     368        /* xgettext: no-wrap */
     369        printf (_("\
     370  Concatenates and merges the specified PO files.\n\
     371  Find messages which are common to two or more of the specified PO files.\n\
     372  By using the --more-than option, greater commonality may be requested\n\
     373  before messages are printed.  Conversely, the --less-than option may be\n\
     374  used to specify less commonality before messages are printed (i.e.\n\
     375  --less-than=2 will only print the unique messages).  Translations,\n\
     376  comments, extracted comments, and file positions will be cumulated, except\n\
     377  that if --use-first is specified, they will be taken from the first PO file\n\
     378  to define them.\n\
     379  "));
     380        printf ("\n");
     381        printf (_("\
     382  Mandatory arguments to long options are mandatory for short options too.\n"));
     383        printf ("\n");
     384        printf (_("\
     385  Input file location:\n"));
     386        printf (_("\
     387    INPUTFILE ...               input files\n"));
     388        printf (_("\
     389    -f, --files-from=FILE       get list of input files from FILE\n"));
     390        printf (_("\
     391    -D, --directory=DIRECTORY   add DIRECTORY to list for input files search\n"));
     392        printf (_("\
     393  If input file is -, standard input is read.\n"));
     394        printf ("\n");
     395        printf (_("\
     396  Output file location:\n"));
     397        printf (_("\
     398    -o, --output-file=FILE      write output to specified file\n"));
     399        printf (_("\
     400  The results are written to standard output if no output file is specified\n\
     401  or if it is -.\n"));
     402        printf ("\n");
     403        printf (_("\
     404  Message selection:\n"));
     405        printf (_("\
     406    -<, --less-than=NUMBER      print messages with less than this many\n\
     407                                definitions, defaults to infinite if not set\n"));
     408        printf (_("\
     409    ->, --more-than=NUMBER      print messages with more than this many\n\
     410                                definitions, defaults to 0 if not set\n"));
     411        printf (_("\
     412    -u, --unique                shorthand for --less-than=2, requests\n\
     413                                that only unique messages be printed\n"));
     414        printf ("\n");
     415        printf (_("\
     416  Input file syntax:\n"));
     417        printf (_("\
     418    -P, --properties-input      input files are in Java .properties syntax\n"));
     419        printf (_("\
     420        --stringtable-input     input files are in NeXTstep/GNUstep .strings\n\
     421                                syntax\n"));
     422        printf ("\n");
     423        printf (_("\
     424  Output details:\n"));
     425        printf (_("\
     426    -t, --to-code=NAME          encoding for output\n"));
     427        printf (_("\
     428        --use-first             use first available translation for each\n\
     429                                message, don't merge several translations\n"));
     430        printf (_("\
     431        --lang=CATALOGNAME      set 'Language' field in the header entry\n"));
     432        printf (_("\
     433        --color                 use colors and other text attributes always\n\
     434        --color=WHEN            use colors and other text attributes if WHEN.\n\
     435                                WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
     436        printf (_("\
     437        --style=STYLEFILE       specify CSS style rule file for --color\n"));
     438        printf (_("\
     439    -e, --no-escape             do not use C escapes in output (default)\n"));
     440        printf (_("\
     441    -E, --escape                use C escapes in output, no extended chars\n"));
     442        printf (_("\
     443        --force-po              write PO file even if empty\n"));
     444        printf (_("\
     445    -i, --indent                write the .po file using indented style\n"));
     446        printf (_("\
     447        --no-location           do not write '#: filename:line' lines\n"));
     448        printf (_("\
     449    -n, --add-location          generate '#: filename:line' lines (default)\n"));
     450        printf (_("\
     451        --strict                write out strict Uniforum conforming .po file\n"));
     452        printf (_("\
     453    -p, --properties-output     write out a Java .properties file\n"));
     454        printf (_("\
     455        --stringtable-output    write out a NeXTstep/GNUstep .strings file\n"));
     456        printf (_("\
     457    -w, --width=NUMBER          set output page width\n"));
     458        printf (_("\
     459        --no-wrap               do not break long message lines, longer than\n\
     460                                the output page width, into several lines\n"));
     461        printf (_("\
     462    -s, --sort-output           generate sorted output\n"));
     463        printf (_("\
     464    -F, --sort-by-file          sort output by file location\n"));
     465        printf ("\n");
     466        printf (_("\
     467  Informative output:\n"));
     468        printf (_("\
     469    -h, --help                  display this help and exit\n"));
     470        printf (_("\
     471    -V, --version               output version information and exit\n"));
     472        printf ("\n");
     473        /* TRANSLATORS: The first placeholder is the web address of the Savannah
     474           project of this package.  The second placeholder is the bug-reporting
     475           email address for this package.  Please add _another line_ saying
     476           "Report translation bugs to <...>\n" with the address for translation
     477           bugs (typically your translation team's web or email address).  */
     478        printf(_("\
     479  Report bugs in the bug tracker at <%s>\n\
     480  or by email to <%s>.\n"),
     481               "https://savannah.gnu.org/projects/gettext",
     482               "bug-gettext@gnu.org");
     483      }
     484  
     485    exit (status);
     486  }