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