Blame gettext-runtime/src/gettext.c

Packit 5b56b6
/* gettext - retrieve text string from message catalog and print it.
Packit 5b56b6
   Copyright (C) 1995-1997, 2000-2007, 2012, 2015 Free Software
Packit 5b56b6
   Foundation, Inc.
Packit 5b56b6
   Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, May 1995.
Packit 5b56b6
Packit 5b56b6
   This program is free software: you can redistribute it and/or modify
Packit 5b56b6
   it under the terms of the GNU General Public License as published by
Packit 5b56b6
   the Free Software Foundation; either version 3 of the License, or
Packit 5b56b6
   (at your option) any later version.
Packit 5b56b6
Packit 5b56b6
   This program is distributed in the hope that it will be useful,
Packit 5b56b6
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5b56b6
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 5b56b6
   GNU General Public License for more details.
Packit 5b56b6
Packit 5b56b6
   You should have received a copy of the GNU General Public License
Packit 5b56b6
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 5b56b6
Packit 5b56b6
#ifdef HAVE_CONFIG_H
Packit 5b56b6
# include <config.h>
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#include <getopt.h>
Packit 5b56b6
#include <stdbool.h>
Packit 5b56b6
#include <stdio.h>
Packit 5b56b6
#include <stdlib.h>
Packit 5b56b6
#include <string.h>
Packit 5b56b6
#include <locale.h>
Packit 5b56b6
Packit 5b56b6
#include "closeout.h"
Packit 5b56b6
#include "error.h"
Packit 5b56b6
#include "progname.h"
Packit 5b56b6
#include "relocatable.h"
Packit 5b56b6
#include "basename.h"
Packit 5b56b6
#include "xalloc.h"
Packit 5b56b6
#include "propername.h"
Packit 5b56b6
#include "gettext.h"
Packit 5b56b6
Packit 5b56b6
#define _(str) gettext (str)
Packit 5b56b6
Packit 5b56b6
/* If true, add newline after last string.  This makes only sense in
Packit 5b56b6
   the 'echo' emulation mode.  */
Packit 5b56b6
static bool add_newline;
Packit 5b56b6
Packit 5b56b6
/* If true, expand escape sequences in strings before looking in the
Packit 5b56b6
   message catalog.  */
Packit 5b56b6
static bool do_expand;
Packit 5b56b6
Packit 5b56b6
/* Long options.  */
Packit 5b56b6
static const struct option long_options[] =
Packit 5b56b6
{
Packit 5b56b6
  { "domain", required_argument, NULL, 'd' },
Packit 5b56b6
  { "help", no_argument, NULL, 'h' },
Packit 5b56b6
  { "shell-script", no_argument, NULL, 's' },
Packit 5b56b6
  { "version", no_argument, NULL, 'V' },
Packit 5b56b6
  { NULL, 0, NULL, 0 }
Packit 5b56b6
};
Packit 5b56b6
Packit 5b56b6
/* Forward declaration of local functions.  */
Packit 5b56b6
static void usage (int status)
Packit 5b56b6
#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
Packit 5b56b6
     __attribute__ ((noreturn))
Packit 5b56b6
#endif
Packit 5b56b6
;
Packit 5b56b6
static const char *expand_escape (const char *str);
Packit 5b56b6
Packit 5b56b6
int
Packit 5b56b6
main (int argc, char *argv[])
Packit 5b56b6
{
Packit 5b56b6
  int optchar;
Packit 5b56b6
  const char *msgid;
Packit 5b56b6
Packit 5b56b6
  /* Default values for command line options.  */
Packit 5b56b6
  bool do_help = false;
Packit 5b56b6
  bool do_shell = false;
Packit 5b56b6
  bool do_version = false;
Packit 5b56b6
  const char *domain = getenv ("TEXTDOMAIN");
Packit 5b56b6
  const char *domaindir = getenv ("TEXTDOMAINDIR");
Packit 5b56b6
  add_newline = true;
Packit 5b56b6
  do_expand = false;
Packit 5b56b6
Packit 5b56b6
  /* Set program name for message texts.  */
Packit 5b56b6
  set_program_name (argv[0]);
Packit 5b56b6
Packit 5b56b6
#ifdef HAVE_SETLOCALE
Packit 5b56b6
  /* Set locale via LC_ALL.  */
Packit 5b56b6
  setlocale (LC_ALL, "");
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
  /* Set the text message domain.  */
Packit 5b56b6
  bindtextdomain (PACKAGE, relocate (LOCALEDIR));
Packit 5b56b6
  textdomain (PACKAGE);
Packit 5b56b6
Packit 5b56b6
  /* Ensure that write errors on stdout are detected.  */
Packit 5b56b6
  atexit (close_stdout);
Packit 5b56b6
Packit 5b56b6
  /* Parse command line options.  */
Packit 5b56b6
  while ((optchar = getopt_long (argc, argv, "+d:eEhnsV", long_options, NULL))
Packit 5b56b6
         != EOF)
Packit 5b56b6
    switch (optchar)
Packit 5b56b6
    {
Packit 5b56b6
    case '\0':          /* Long option.  */
Packit 5b56b6
      break;
Packit 5b56b6
    case 'd':
Packit 5b56b6
      domain = optarg;
Packit 5b56b6
      break;
Packit 5b56b6
    case 'e':
Packit 5b56b6
      do_expand = true;
Packit 5b56b6
      break;
Packit 5b56b6
    case 'E':
Packit 5b56b6
      /* Ignore.  Just for compatibility.  */
Packit 5b56b6
      break;
Packit 5b56b6
    case 'h':
Packit 5b56b6
      do_help = true;
Packit 5b56b6
      break;
Packit 5b56b6
    case 'n':
Packit 5b56b6
      add_newline = false;
Packit 5b56b6
      break;
Packit 5b56b6
    case 's':
Packit 5b56b6
      do_shell = true;
Packit 5b56b6
      break;
Packit 5b56b6
    case 'V':
Packit 5b56b6
      do_version = true;
Packit 5b56b6
      break;
Packit 5b56b6
    default:
Packit 5b56b6
      usage (EXIT_FAILURE);
Packit 5b56b6
    }
Packit 5b56b6
Packit 5b56b6
  /* Version information is requested.  */
Packit 5b56b6
  if (do_version)
Packit 5b56b6
    {
Packit 5b56b6
      printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
Packit 5b56b6
      /* xgettext: no-wrap */
Packit 5b56b6
      printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
Packit 5b56b6
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
Packit 5b56b6
This is free software: you are free to change and redistribute it.\n\
Packit 5b56b6
There is NO WARRANTY, to the extent permitted by law.\n\
Packit 5b56b6
"),
Packit 5b56b6
              "1995-1997, 2000-2007");
Packit 5b56b6
      printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
Packit 5b56b6
      exit (EXIT_SUCCESS);
Packit 5b56b6
    }
Packit 5b56b6
Packit 5b56b6
  /* Help is requested.  */
Packit 5b56b6
  if (do_help)
Packit 5b56b6
    usage (EXIT_SUCCESS);
Packit 5b56b6
Packit 5b56b6
  /* We have two major modes: use following Uniforum spec and as
Packit 5b56b6
     internationalized 'echo' program.  */
Packit 5b56b6
  if (!do_shell)
Packit 5b56b6
    {
Packit 5b56b6
      /* We have to write a single strings translation to stdout.  */
Packit 5b56b6
Packit 5b56b6
      /* Get arguments.  */
Packit 5b56b6
      switch (argc - optind)
Packit 5b56b6
        {
Packit 5b56b6
          default:
Packit 5b56b6
            error (EXIT_FAILURE, 0, _("too many arguments"));
Packit 5b56b6
Packit 5b56b6
          case 2:
Packit 5b56b6
            domain = argv[optind++];
Packit 5b56b6
            /* FALLTHROUGH */
Packit 5b56b6
Packit 5b56b6
          case 1:
Packit 5b56b6
            break;
Packit 5b56b6
Packit 5b56b6
          case 0:
Packit 5b56b6
            error (EXIT_FAILURE, 0, _("missing arguments"));
Packit 5b56b6
        }
Packit 5b56b6
Packit 5b56b6
      msgid = argv[optind++];
Packit 5b56b6
Packit 5b56b6
      /* Expand escape sequences if enabled.  */
Packit 5b56b6
      if (do_expand)
Packit 5b56b6
        msgid = expand_escape (msgid);
Packit 5b56b6
Packit 5b56b6
      /* If no domain name is given we don't translate.  */
Packit 5b56b6
      if (domain == NULL || domain[0] == '\0')
Packit 5b56b6
        {
Packit 5b56b6
          fputs (msgid, stdout);
Packit 5b56b6
        }
Packit 5b56b6
      else
Packit 5b56b6
        {
Packit 5b56b6
          /* Bind domain to appropriate directory.  */
Packit 5b56b6
          if (domaindir != NULL && domaindir[0] != '\0')
Packit 5b56b6
            bindtextdomain (domain, domaindir);
Packit 5b56b6
Packit 5b56b6
          /* Write out the result.  */
Packit 5b56b6
          fputs (dgettext (domain, msgid), stdout);
Packit 5b56b6
        }
Packit 5b56b6
    }
Packit 5b56b6
  else
Packit 5b56b6
    {
Packit 5b56b6
      if (optind < argc)
Packit 5b56b6
        {
Packit 5b56b6
          /* If no domain name is given we print the original string.
Packit 5b56b6
             We mark this assigning NULL to domain.  */
Packit 5b56b6
          if (domain == NULL || domain[0] == '\0')
Packit 5b56b6
            domain = NULL;
Packit 5b56b6
          else
Packit 5b56b6
            /* Bind domain to appropriate directory.  */
Packit 5b56b6
            if (domaindir != NULL && domaindir[0] != '\0')
Packit 5b56b6
              bindtextdomain (domain, domaindir);
Packit 5b56b6
Packit 5b56b6
          /* We have to simulate 'echo'.  All arguments are strings.  */
Packit 5b56b6
          do
Packit 5b56b6
            {
Packit 5b56b6
              msgid = argv[optind++];
Packit 5b56b6
Packit 5b56b6
              /* Expand escape sequences if enabled.  */
Packit 5b56b6
              if (do_expand)
Packit 5b56b6
                msgid = expand_escape (msgid);
Packit 5b56b6
Packit 5b56b6
              /* Write out the result.  */
Packit 5b56b6
              fputs (domain == NULL ? msgid : dgettext (domain, msgid),
Packit 5b56b6
                     stdout);
Packit 5b56b6
Packit 5b56b6
              /* We separate the arguments by a single ' '.  */
Packit 5b56b6
              if (optind < argc)
Packit 5b56b6
                fputc (' ', stdout);
Packit 5b56b6
            }
Packit 5b56b6
          while (optind < argc);
Packit 5b56b6
        }
Packit 5b56b6
Packit 5b56b6
      /* If not otherwise told: add trailing newline.  */
Packit 5b56b6
      if (add_newline)
Packit 5b56b6
        fputc ('\n', stdout);
Packit 5b56b6
    }
Packit 5b56b6
Packit 5b56b6
  exit (EXIT_SUCCESS);
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
Packit 5b56b6
/* Display usage information and exit.  */
Packit 5b56b6
static void
Packit 5b56b6
usage (int status)
Packit 5b56b6
{
Packit 5b56b6
  if (status != EXIT_SUCCESS)
Packit 5b56b6
    fprintf (stderr, _("Try '%s --help' for more information.\n"),
Packit 5b56b6
             program_name);
Packit 5b56b6
  else
Packit 5b56b6
    {
Packit 5b56b6
      /* xgettext: no-wrap */
Packit 5b56b6
      printf (_("\
Packit 5b56b6
Usage: %s [OPTION] [[TEXTDOMAIN] MSGID]\n\
Packit 5b56b6
or:    %s [OPTION] -s [MSGID]...\n\
Packit 5b56b6
"), program_name, program_name);
Packit 5b56b6
      printf ("\n");
Packit 5b56b6
      /* xgettext: no-wrap */
Packit 5b56b6
      printf (_("\
Packit 5b56b6
Display native language translation of a textual message.\n"));
Packit 5b56b6
      printf ("\n");
Packit 5b56b6
      /* xgettext: no-wrap */
Packit 5b56b6
      printf (_("\
Packit 5b56b6
  -d, --domain=TEXTDOMAIN   retrieve translated messages from TEXTDOMAIN\n\
Packit 5b56b6
  -e                        enable expansion of some escape sequences\n\
Packit 5b56b6
  -E                        (ignored for compatibility)\n\
Packit 5b56b6
  -h, --help                display this help and exit\n\
Packit 5b56b6
  -n                        suppress trailing newline\n\
Packit 5b56b6
  -V, --version             display version information and exit\n\
Packit 5b56b6
  [TEXTDOMAIN] MSGID        retrieve translated message corresponding\n\
Packit 5b56b6
                            to MSGID from TEXTDOMAIN\n"));
Packit 5b56b6
      printf ("\n");
Packit 5b56b6
      /* xgettext: no-wrap */
Packit 5b56b6
      printf (_("\
Packit 5b56b6
If the TEXTDOMAIN parameter is not given, the domain is determined from the\n\
Packit 5b56b6
environment variable TEXTDOMAIN.  If the message catalog is not found in the\n\
Packit 5b56b6
regular directory, another location can be specified with the environment\n\
Packit 5b56b6
variable TEXTDOMAINDIR.\n\
Packit 5b56b6
When used with the -s option the program behaves like the 'echo' command.\n\
Packit 5b56b6
But it does not simply copy its arguments to stdout.  Instead those messages\n\
Packit 5b56b6
found in the selected catalog are translated.\n\
Packit 5b56b6
Standard search directory: %s\n"),
Packit 5b56b6
              getenv ("IN_HELP2MAN") == NULL ? LOCALEDIR : "@localedir@");
Packit 5b56b6
      printf ("\n");
Packit 5b56b6
      /* TRANSLATORS: The placeholder indicates the bug-reporting address
Packit 5b56b6
         for this package.  Please add _another line_ saying
Packit 5b56b6
         "Report translation bugs to <...>\n" with the address for translation
Packit 5b56b6
         bugs (typically your translation team's web or email address).  */
Packit 5b56b6
      fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout);
Packit 5b56b6
    }
Packit 5b56b6
Packit 5b56b6
  exit (status);
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
Packit 5b56b6
/* Expand some escape sequences found in the argument string.  */
Packit 5b56b6
static const char *
Packit 5b56b6
expand_escape (const char *str)
Packit 5b56b6
{
Packit 5b56b6
  char *retval, *rp;
Packit 5b56b6
  const char *cp = str;
Packit 5b56b6
Packit 5b56b6
  for (;;)
Packit 5b56b6
    {
Packit 5b56b6
      while (cp[0] != '\0' && cp[0] != '\\')
Packit 5b56b6
        ++cp;
Packit 5b56b6
      if (cp[0] == '\0')
Packit 5b56b6
        return str;
Packit 5b56b6
      /* Found a backslash.  */
Packit 5b56b6
      if (cp[1] == '\0')
Packit 5b56b6
        return str;
Packit 5b56b6
      if (strchr ("abcfnrtv\\01234567", cp[1]) != NULL)
Packit 5b56b6
        break;
Packit 5b56b6
      ++cp;
Packit 5b56b6
    }
Packit 5b56b6
Packit 5b56b6
  retval = XNMALLOC (strlen (str), char);
Packit 5b56b6
Packit 5b56b6
  rp = retval + (cp - str);
Packit 5b56b6
  memcpy (retval, str, cp - str);
Packit 5b56b6
Packit 5b56b6
  do
Packit 5b56b6
    {
Packit 5b56b6
      /* Here cp[0] == '\\'.  */
Packit 5b56b6
      switch (*++cp)
Packit 5b56b6
        {
Packit 5b56b6
        case 'a':               /* alert */
Packit 5b56b6
          *rp++ = '\a';
Packit 5b56b6
          ++cp;
Packit 5b56b6
          break;
Packit 5b56b6
        case 'b':               /* backspace */
Packit 5b56b6
          *rp++ = '\b';
Packit 5b56b6
          ++cp;
Packit 5b56b6
          break;
Packit 5b56b6
        case 'c':               /* suppress trailing newline */
Packit 5b56b6
          add_newline = false;
Packit 5b56b6
          ++cp;
Packit 5b56b6
          break;
Packit 5b56b6
        case 'f':               /* form feed */
Packit 5b56b6
          *rp++ = '\f';
Packit 5b56b6
          ++cp;
Packit 5b56b6
          break;
Packit 5b56b6
        case 'n':               /* new line */
Packit 5b56b6
          *rp++ = '\n';
Packit 5b56b6
          ++cp;
Packit 5b56b6
          break;
Packit 5b56b6
        case 'r':               /* carriage return */
Packit 5b56b6
          *rp++ = '\r';
Packit 5b56b6
          ++cp;
Packit 5b56b6
          break;
Packit 5b56b6
        case 't':               /* horizontal tab */
Packit 5b56b6
          *rp++ = '\t';
Packit 5b56b6
          ++cp;
Packit 5b56b6
          break;
Packit 5b56b6
        case 'v':               /* vertical tab */
Packit 5b56b6
          *rp++ = '\v';
Packit 5b56b6
          ++cp;
Packit 5b56b6
          break;
Packit 5b56b6
        case '\\':
Packit 5b56b6
          *rp = '\\';
Packit 5b56b6
          ++cp;
Packit 5b56b6
          break;
Packit 5b56b6
        case '0': case '1': case '2': case '3':
Packit 5b56b6
        case '4': case '5': case '6': case '7':
Packit 5b56b6
          {
Packit 5b56b6
            int ch = *cp++ - '0';
Packit 5b56b6
Packit 5b56b6
            if (*cp >= '0' && *cp <= '7')
Packit 5b56b6
              {
Packit 5b56b6
                ch *= 8;
Packit 5b56b6
                ch += *cp++ - '0';
Packit 5b56b6
Packit 5b56b6
                if (*cp >= '0' && *cp <= '7')
Packit 5b56b6
                  {
Packit 5b56b6
                    ch *= 8;
Packit 5b56b6
                    ch += *cp++ - '0';
Packit 5b56b6
                  }
Packit 5b56b6
              }
Packit 5b56b6
            *rp = ch;
Packit 5b56b6
          }
Packit 5b56b6
          break;
Packit 5b56b6
        default:
Packit 5b56b6
          *rp = '\\';
Packit 5b56b6
          break;
Packit 5b56b6
        }
Packit 5b56b6
Packit 5b56b6
      while (cp[0] != '\0' && cp[0] != '\\')
Packit 5b56b6
        *rp++ = *cp++;
Packit 5b56b6
    }
Packit 5b56b6
  while (cp[0] != '\0');
Packit 5b56b6
Packit 5b56b6
  /* Terminate string.  */
Packit 5b56b6
  *rp = '\0';
Packit 5b56b6
Packit 5b56b6
  return (const char *) retval;
Packit 5b56b6
}