(root)/
gettext-0.22.4/
gettext-tools/
src/
read-tcl.c
       1  /* Reading tcl/msgcat .msg files.
       2     Copyright (C) 2002-2003, 2005-2008, 2010-2011, 2018, 2020 Free Software
       3     Foundation, Inc.
       4     Written by Bruno Haible <bruno@clisp.org>, 2002.
       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  #include <alloca.h>
      23  
      24  /* Specification.  */
      25  #include "read-tcl.h"
      26  
      27  #include <errno.h>
      28  #include <stdio.h>
      29  #include <stdlib.h>
      30  
      31  #include "msgunfmt.h"
      32  #include "relocatable.h"
      33  #include "concat-filename.h"
      34  #include "sh-quote.h"
      35  #include "spawn-pipe.h"
      36  #include "wait-process.h"
      37  #include "read-catalog.h"
      38  #include "read-po.h"
      39  #include "xmalloca.h"
      40  #include "error.h"
      41  #include "gettext.h"
      42  
      43  #define _(str) gettext (str)
      44  
      45  
      46  /* A Tcl .msg file contains Tcl commands.  It is best interpreted by Tcl
      47     itself.  But we redirect the msgcat::mcset function so that it passes
      48     the msgid/msgstr pair to us, instead of storing it in the hash table.  */
      49  
      50  msgdomain_list_ty *
      51  msgdomain_read_tcl (const char *locale_name, const char *directory)
      52  {
      53    const char *gettextdatadir;
      54    char *tclscript;
      55    size_t len;
      56    char *frobbed_locale_name;
      57    char *p;
      58    char *file_name;
      59    const char *argv[4];
      60    pid_t child;
      61    int fd[1];
      62    FILE *fp;
      63    msgdomain_list_ty *mdlp;
      64    int exitstatus;
      65    size_t k;
      66  
      67    /* Make it possible to override the msgunfmt.tcl location.  This is
      68       necessary for running the testsuite before "make install".  */
      69    gettextdatadir = getenv ("GETTEXTTCLDIR");
      70    if (gettextdatadir == NULL || gettextdatadir[0] == '\0')
      71      gettextdatadir = relocate (GETTEXTDATADIR);
      72  
      73    tclscript = xconcatenated_filename (gettextdatadir, "msgunfmt.tcl", NULL);
      74  
      75    /* Convert the locale name to lowercase and remove any encoding.  */
      76    len = strlen (locale_name);
      77    frobbed_locale_name = (char *) xmalloca (len + 1);
      78    memcpy (frobbed_locale_name, locale_name, len + 1);
      79    for (p = frobbed_locale_name; *p != '\0'; p++)
      80      if (*p >= 'A' && *p <= 'Z')
      81        *p = *p - 'A' + 'a';
      82      else if (*p == '.')
      83        {
      84          *p = '\0';
      85          break;
      86        }
      87  
      88    file_name = xconcatenated_filename (directory, frobbed_locale_name, ".msg");
      89  
      90    freea (frobbed_locale_name);
      91  
      92    /* Prepare arguments.  */
      93    argv[0] = "tclsh";
      94    argv[1] = tclscript;
      95    argv[2] = file_name;
      96    argv[3] = NULL;
      97  
      98    if (verbose)
      99      {
     100        char *command = shell_quote_argv (argv);
     101        printf ("%s\n", command);
     102        free (command);
     103      }
     104  
     105    /* Open a pipe to the Tcl interpreter.  */
     106    child = create_pipe_in ("tclsh", "tclsh", argv, NULL,
     107                            DEV_NULL, false, true, true, fd);
     108  
     109    fp = fdopen (fd[0], "r");
     110    if (fp == NULL)
     111      error (EXIT_FAILURE, errno, _("fdopen() failed"));
     112  
     113    /* Read the message list.  */
     114    mdlp = read_catalog_stream (fp, "(pipe)", "(pipe)", &input_format_po);
     115  
     116    fclose (fp);
     117  
     118    /* Remove zombie process from process list, and retrieve exit status.  */
     119    exitstatus =
     120      wait_subprocess (child, "tclsh", false, false, true, true, NULL);
     121    if (exitstatus != 0)
     122      {
     123        if (exitstatus == 2)
     124          /* Special exitcode provided by msgunfmt.tcl.  */
     125          error (EXIT_FAILURE, ENOENT,
     126                 _("error while opening \"%s\" for reading"), file_name);
     127        else
     128          error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
     129                 "tclsh", exitstatus);
     130      }
     131  
     132    free (tclscript);
     133  
     134    /* Move the header entry to the beginning.  */
     135    for (k = 0; k < mdlp->nitems; k++)
     136      {
     137        message_list_ty *mlp = mdlp->item[k]->messages;
     138        size_t j;
     139  
     140        for (j = 0; j < mlp->nitems; j++)
     141          if (is_header (mlp->item[j]))
     142            {
     143              /* Found the header entry.  */
     144              if (j > 0)
     145                {
     146                  message_ty *header = mlp->item[j];
     147                  size_t i;
     148  
     149                  for (i = j; i > 0; i--)
     150                    mlp->item[i] = mlp->item[i - 1];
     151                  mlp->item[0] = header;
     152                }
     153              break;
     154            }
     155      }
     156  
     157    return mdlp;
     158  }