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