1 /* Reading tcl/msgcat .msg files. 2 Copyright (C) 2002-2003, 2005-2006 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 2, or (at your option) 8 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, write to the Free Software Foundation, 17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 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 "pathname.h" 34 #include "sh-quote.h" 35 #include "pipe.h" 36 #include "wait-process.h" 37 #include "read-catalog.h" 38 #include "read-po.h" 39 #include "xallocsa.h" 40 #include "error.h" 41 #include "exit.h" 42 #include "gettext.h" 43 44 #define _(str) gettext (str) 45 46 47 /* A Tcl .msg file contains Tcl commands. It is best interpreted by Tcl 48 itself. But we redirect the msgcat::mcset function so that it passes 49 the msgid/msgstr pair to us, instead of storing it in the hash table. */ 50 51 msgdomain_list_ty * 52 msgdomain_read_tcl (const char *locale_name, const char *directory) 53 { 54 const char *gettextdatadir; 55 char *tclscript; 56 size_t len; 57 char *frobbed_locale_name; 58 char *p; 59 char *file_name; 60 char *argv[4]; 61 pid_t child; 62 int fd[1]; 63 FILE *fp; 64 msgdomain_list_ty *mdlp; 65 int exitstatus; 66 size_t k; 67 68 /* Make it possible to override the msgunfmt.tcl location. This is 69 necessary for running the testsuite before "make install". */ 70 gettextdatadir = getenv ("GETTEXTDATADIR"); 71 if (gettextdatadir == NULL || gettextdatadir[0] == '\0') 72 gettextdatadir = relocate (GETTEXTDATADIR); 73 74 tclscript = concatenated_pathname (gettextdatadir, "msgunfmt.tcl", NULL); 75 76 /* Convert the locale name to lowercase and remove any encoding. */ 77 len = strlen (locale_name); 78 frobbed_locale_name = (char *) xallocsa (len + 1); 79 memcpy (frobbed_locale_name, locale_name, len + 1); 80 for (p = frobbed_locale_name; *p != '\0'; p++) 81 if (*p >= 'A' && *p <= 'Z') 82 *p = *p - 'A' + 'a'; 83 else if (*p == '.') 84 { 85 *p = '\0'; 86 break; 87 } 88 89 file_name = concatenated_pathname (directory, frobbed_locale_name, ".msg"); 90 91 freesa (frobbed_locale_name); 92 93 /* Prepare arguments. */ 94 argv[0] = "tclsh"; 95 argv[1] = tclscript; 96 argv[2] = file_name; 97 argv[3] = NULL; 98 99 if (verbose) 100 { 101 char *command = shell_quote_argv (argv); 102 printf ("%s\n", command); 103 free (command); 104 } 105 106 /* Open a pipe to the Tcl interpreter. */ 107 child = create_pipe_in ("tclsh", "tclsh", argv, DEV_NULL, false, true, true, 108 fd); 109 110 fp = fdopen (fd[0], "r"); 111 if (fp == NULL) 112 error (EXIT_FAILURE, errno, _("fdopen() failed")); 113 114 /* Read the message list. */ 115 mdlp = read_catalog_stream (fp, "(pipe)", "(pipe)", &input_format_po); 116 117 fclose (fp); 118 119 /* Remove zombie process from process list, and retrieve exit status. */ 120 exitstatus = wait_subprocess (child, "tclsh", false, false, true, true); 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 } 159