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