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