1/* gettext - retrieve text string from message catalog and print it. 2 Copyright (C) 1995-1997, 2000-2005 Free Software Foundation, Inc. 3 Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, May 1995. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 18 19#ifdef HAVE_CONFIG_H 20# include <config.h> 21#endif 22 23#include <getopt.h> 24#include <stdbool.h> 25#include <stdio.h> 26#include <stdlib.h> 27#include <string.h> 28#include <locale.h> 29 30#include "closeout.h" 31#include "error.h" 32#include "progname.h" 33#include "relocatable.h" 34#include "basename.h" 35#include "xalloc.h" 36#include "exit.h" 37 38#include "gettext.h" 39 40#define _(str) gettext (str) 41 42/* If true, add newline after last string. This makes only sense in 43 the `echo' emulation mode. */ 44static bool add_newline; 45 46/* If true, expand escape sequences in strings before looking in the 47 message catalog. */ 48static bool do_expand; 49 50/* Long options. */ 51static const struct option long_options[] = 52{ 53 { "domain", required_argument, NULL, 'd' }, 54 { "help", no_argument, NULL, 'h' }, 55 { "shell-script", no_argument, NULL, 's' }, 56 { "version", no_argument, NULL, 'V' }, 57 { NULL, 0, NULL, 0 } 58}; 59 60/* Forward declaration of local functions. */ 61static void usage (int status) 62#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2) 63 __attribute__ ((noreturn)) 64#endif 65; 66static const char *expand_escape (const char *str); 67 68int 69main (int argc, char *argv[]) 70{ 71 int optchar; 72 const char *msgid; 73 74 /* Default values for command line options. */ 75 bool do_help = false; 76 bool do_shell = false; 77 bool do_version = false; 78 const char *domain = getenv ("TEXTDOMAIN"); 79 const char *domaindir = getenv ("TEXTDOMAINDIR"); 80 add_newline = true; 81 do_expand = false; 82 83 /* Set program name for message texts. */ 84 set_program_name (argv[0]); 85 86#ifdef HAVE_SETLOCALE 87 /* Set locale via LC_ALL. */ 88 setlocale (LC_ALL, ""); 89#endif 90 91 /* Set the text message domain. */ 92 bindtextdomain (PACKAGE, relocate (LOCALEDIR)); 93 textdomain (PACKAGE); 94 95 /* Ensure that write errors on stdout are detected. */ 96 atexit (close_stdout); 97 98 /* Parse command line options. */ 99 while ((optchar = getopt_long (argc, argv, "+d:eEhnsV", long_options, NULL)) 100 != EOF) 101 switch (optchar) 102 { 103 case '\0': /* Long option. */ 104 break; 105 case 'd': 106 domain = optarg; 107 break; 108 case 'e': 109 do_expand = true; 110 break; 111 case 'E': 112 /* Ignore. Just for compatibility. */ 113 break; 114 case 'h': 115 do_help = true; 116 break; 117 case 'n': 118 add_newline = false; 119 break; 120 case 's': 121 do_shell = true; 122 break; 123 case 'V': 124 do_version = true; 125 break; 126 default: 127 usage (EXIT_FAILURE); 128 } 129 130 /* Version information is requested. */ 131 if (do_version) 132 { 133 printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION); 134 /* xgettext: no-wrap */ 135 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ 136This is free software; see the source for copying conditions. There is NO\n\ 137warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ 138"), 139 "1995-1997, 2000-2005"); 140 printf (_("Written by %s.\n"), "Ulrich Drepper"); 141 exit (EXIT_SUCCESS); 142 } 143 144 /* Help is requested. */ 145 if (do_help) 146 usage (EXIT_SUCCESS); 147 148 /* We have two major modes: use following Uniforum spec and as 149 internationalized `echo' program. */ 150 if (!do_shell) 151 { 152 /* We have to write a single strings translation to stdout. */ 153 154 /* Get arguments. */ 155 switch (argc - optind) 156 { 157 default: 158 error (EXIT_FAILURE, 0, _("too many arguments")); 159 160 case 2: 161 domain = argv[optind++]; 162 /* FALLTHROUGH */ 163 164 case 1: 165 break; 166 167 case 0: 168 error (EXIT_FAILURE, 0, _("missing arguments")); 169 } 170 171 msgid = argv[optind++]; 172 173 /* Expand escape sequences if enabled. */ 174 if (do_expand) 175 msgid = expand_escape (msgid); 176 177 /* If no domain name is given we don't translate. */ 178 if (domain == NULL || domain[0] == '\0') 179 { 180 fputs (msgid, stdout); 181 } 182 else 183 { 184 /* Bind domain to appropriate directory. */ 185 if (domaindir != NULL && domaindir[0] != '\0') 186 bindtextdomain (domain, domaindir); 187 188 /* Write out the result. */ 189 fputs (dgettext (domain, msgid), stdout); 190 } 191 } 192 else 193 { 194 if (optind < argc) 195 { 196 /* If no domain name is given we print the original string. 197 We mark this assigning NULL to domain. */ 198 if (domain == NULL || domain[0] == '\0') 199 domain = NULL; 200 else 201 /* Bind domain to appropriate directory. */ 202 if (domaindir != NULL && domaindir[0] != '\0') 203 bindtextdomain (domain, domaindir); 204 205 /* We have to simulate `echo'. All arguments are strings. */ 206 do 207 { 208 msgid = argv[optind++]; 209 210 /* Expand escape sequences if enabled. */ 211 if (do_expand) 212 msgid = expand_escape (msgid); 213 214 /* Write out the result. */ 215 fputs (domain == NULL ? msgid : dgettext (domain, msgid), 216 stdout); 217 218 /* We separate the arguments by a single ' '. */ 219 if (optind < argc) 220 fputc (' ', stdout); 221 } 222 while (optind < argc); 223 } 224 225 /* If not otherwise told: add trailing newline. */ 226 if (add_newline) 227 fputc ('\n', stdout); 228 } 229 230 exit (EXIT_SUCCESS); 231} 232 233 234/* Display usage information and exit. */ 235static void 236usage (int status) 237{ 238 if (status != EXIT_SUCCESS) 239 fprintf (stderr, _("Try `%s --help' for more information.\n"), 240 program_name); 241 else 242 { 243 /* xgettext: no-wrap */ 244 printf (_("\ 245Usage: %s [OPTION] [[TEXTDOMAIN] MSGID]\n\ 246or: %s [OPTION] -s [MSGID]...\n\ 247"), program_name, program_name); 248 printf ("\n"); 249 /* xgettext: no-wrap */ 250 printf (_("\ 251Display native language translation of a textual message.\n")); 252 printf ("\n"); 253 /* xgettext: no-wrap */ 254 printf (_("\ 255 -d, --domain=TEXTDOMAIN retrieve translated messages from TEXTDOMAIN\n\ 256 -e enable expansion of some escape sequences\n\ 257 -E (ignored for compatibility)\n\ 258 -h, --help display this help and exit\n\ 259 -n suppress trailing newline\n\ 260 -V, --version display version information and exit\n\ 261 [TEXTDOMAIN] MSGID retrieve translated message corresponding\n\ 262 to MSGID from TEXTDOMAIN\n")); 263 printf ("\n"); 264 /* xgettext: no-wrap */ 265 printf (_("\ 266If the TEXTDOMAIN parameter is not given, the domain is determined from the\n\ 267environment variable TEXTDOMAIN. If the message catalog is not found in the\n\ 268regular directory, another location can be specified with the environment\n\ 269variable TEXTDOMAINDIR.\n\ 270When used with the -s option the program behaves like the `echo' command.\n\ 271But it does not simply copy its arguments to stdout. Instead those messages\n\ 272found in the selected catalog are translated.\n\ 273Standard search directory: %s\n"), 274 getenv ("IN_HELP2MAN") == NULL ? LOCALEDIR : "@localedir@"); 275 printf ("\n"); 276 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout); 277 } 278 279 exit (status); 280} 281 282 283/* Expand some escape sequences found in the argument string. */ 284static const char * 285expand_escape (const char *str) 286{ 287 char *retval, *rp; 288 const char *cp = str; 289 290 for (;;) 291 { 292 while (cp[0] != '\0' && cp[0] != '\\') 293 ++cp; 294 if (cp[0] == '\0') 295 return str; 296 /* Found a backslash. */ 297 if (cp[1] == '\0') 298 return str; 299 if (strchr ("abcfnrtv\\01234567", cp[1]) != NULL) 300 break; 301 ++cp; 302 } 303 304 retval = (char *) xmalloc (strlen (str)); 305 306 rp = retval + (cp - str); 307 memcpy (retval, str, cp - str); 308 309 do 310 { 311 /* Here cp[0] == '\\'. */ 312 switch (*++cp) 313 { 314 case 'a': /* alert */ 315 *rp++ = '\a'; 316 ++cp; 317 break; 318 case 'b': /* backspace */ 319 *rp++ = '\b'; 320 ++cp; 321 break; 322 case 'c': /* suppress trailing newline */ 323 add_newline = false; 324 ++cp; 325 break; 326 case 'f': /* form feed */ 327 *rp++ = '\f'; 328 ++cp; 329 break; 330 case 'n': /* new line */ 331 *rp++ = '\n'; 332 ++cp; 333 break; 334 case 'r': /* carriage return */ 335 *rp++ = '\r'; 336 ++cp; 337 break; 338 case 't': /* horizontal tab */ 339 *rp++ = '\t'; 340 ++cp; 341 break; 342 case 'v': /* vertical tab */ 343 *rp++ = '\v'; 344 ++cp; 345 break; 346 case '\\': 347 *rp = '\\'; 348 ++cp; 349 break; 350 case '0': case '1': case '2': case '3': 351 case '4': case '5': case '6': case '7': 352 { 353 int ch = *cp++ - '0'; 354 355 if (*cp >= '0' && *cp <= '7') 356 { 357 ch *= 8; 358 ch += *cp++ - '0'; 359 360 if (*cp >= '0' && *cp <= '7') 361 { 362 ch *= 8; 363 ch += *cp++ - '0'; 364 } 365 } 366 *rp = ch; 367 } 368 break; 369 default: 370 *rp = '\\'; 371 break; 372 } 373 374 while (cp[0] != '\0' && cp[0] != '\\') 375 *rp++ = *cp++; 376 } 377 while (cp[0] != '\0'); 378 379 /* Terminate string. */ 380 *rp = '\0'; 381 382 return (const char *) retval; 383} 384