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