1198157Srrs/* 2198157Srrs * "$Id: i18n.c,v 1.8 2009/04/11 19:05:12 rlk Exp $" 3198157Srrs * 4198157Srrs * Internationalization functions for CUPS drivers. 5198157Srrs * 6198157Srrs * Copyright 2008 Michael Sweet (mike@easysw.com) 7198157Srrs * 8198157Srrs * This program is free software; you can redistribute it and/or modify it 9198157Srrs * under the terms of the GNU General Public License as published by the Free 10198157Srrs * Software Foundation; either version 2 of the License, or (at your option) 11198157Srrs * any later version. 12198157Srrs * 13198157Srrs * This program is distributed in the hope that it will be useful, but 14198157Srrs * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15198157Srrs * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16198157Srrs * for more details. 17198157Srrs * 18198157Srrs * You should have received a copy of the GNU General Public License 19198157Srrs * along with this program; if not, write to the Free Software 20198157Srrs * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21198157Srrs * 22198157Srrs * Contents: 23198157Srrs * 24198157Srrs * stp_i18n_load() - Load a message catalog for a locale. 25198157Srrs * stp_i18n_lookup() - Lookup a string in the message catalog... 26198157Srrs * stp_i18n_printf() - Send a formatted string to stderr. 27198157Srrs * stpi_unquote() - Unquote characters in strings. 28198157Srrs */ 29202066Simp 30202066Simp/* 31198157Srrs * Include necessary files... 32202066Simp */ 33202066Simp 34198157Srrs#include "i18n.h" 35198157Srrs#include <config.h> 36198157Srrs#include <stdio.h> 37198157Srrs#include <stdlib.h> 38198157Srrs#include <stdarg.h> 39198157Srrs#include <string.h> 40198157Srrs#include <ctype.h> 41198157Srrs#include <unistd.h> 42198157Srrs#include <errno.h> 43198157Srrs#include <iconv.h> 44198157Srrs 45198157Srrs 46212763Sjchandra/* 47212763Sjchandra * GNU gettext uses a simple .po file format: 48212763Sjchandra * 49212763Sjchandra * # comment 50212763Sjchandra * msgid "id" 51212763Sjchandra * "optional continuation" 52198157Srrs * msgstr "str" 53212763Sjchandra * "optional continuation" 54212763Sjchandra * 55198157Srrs * Both the id and str strings use standard C quoting for special characters 56198157Srrs * like newline and the double quote character. 57198157Srrs */ 58202066Simp 59198157Srrs 60212763Sjchandra/* 61198157Srrs * Cache structure... 62198157Srrs */ 63198157Srrs 64212763Sjchandratypedef struct stpi_i18n_s 65212763Sjchandra{ 66212763Sjchandra struct stpi_i18n_s *next; /* Next catalog */ 67198157Srrs char locale[6]; /* Locale */ 68198627Srrs stp_string_list_t *po; /* Message catalog */ 69198627Srrs} stpi_i18n_t; 70198627Srrs 71198157Srrs 72198157Srrs/* 73198157Srrs * Local functions... 74198627Srrs */ 75198627Srrs 76198627Srrsstatic void stpi_unquote(char *s); 77198627Srrs 78198157Srrs 79212763Sjchandra/* 80212763Sjchandra * Local globals... 81212763Sjchandra */ 82212763Sjchandra 83212763Sjchandrastatic stpi_i18n_t *stpi_pocache = NULL; 84227843Smarius 85198157Srrs 86198157Srrs/* 87198157Srrs * 'stp_i18n_load()' - Load a message catalog for a locale. 88198627Srrs */ 89198627Srrs 90198627Srrsconst stp_string_list_t * /* O - Message catalog */ 91198157Srrsstp_i18n_load(const char *locale) /* I - Locale name */ 92198157Srrs{ 93198157Srrs stp_string_list_t *po; /* Message catalog */ 94198157Srrs char ll_CC[6], /* Locale ID */ 95198157Srrs poname[1024]; /* .po filename */ 96198157Srrs stpi_i18n_t *pocache; /* Current cache entry */ 97198157Srrs FILE *pofile; /* .po file */ 98198157Srrs const char *stp_localedir; /* STP_LOCALEDIR environment variable */ 99198157Srrs char line[4096], /* Line buffer */ 100212763Sjchandra *ptr, /* Pointer into buffer */ 101212763Sjchandra id[4096], /* Translation ID */ 102198627Srrs str[4096], /* Translation string */ 103198157Srrs utf8str[4096]; /* UTF-8 translation string */ 104198157Srrs int in_id, /* Processing "id" string? */ 105198157Srrs in_str, /* Processing "str" string? */ 106198157Srrs linenum; /* Line number in .po file */ 107198157Srrs iconv_t ic; /* Transcoder to UTF-8 */ 108198157Srrs size_t inbytes, /* Number of input buffer bytes */ 109198157Srrs outbytes; /* Number of output buffer bytes */ 110198157Srrs char *inptr, /* Pointer into input buffer */ 111198627Srrs *outptr; /* Pointer into output buffer */ 112198157Srrs int fuzzy = 0; /* Fuzzy translation? */ 113198627Srrs 114212763Sjchandra 115212763Sjchandra if (!locale) 116212763Sjchandra return (NULL); 117198627Srrs 118198627Srrs /* 119198627Srrs * See if the locale is already loaded... 120198627Srrs */ 121212763Sjchandra 122198627Srrs for (pocache = stpi_pocache; pocache; pocache = pocache->next) 123198157Srrs if (!strcmp(locale, pocache->locale)) 124212763Sjchandra return (pocache->po); 125198627Srrs 126198157Srrs /* 127212763Sjchandra * Find the message catalog for the given locale... 128198627Srrs */ 129198157Srrs 130212763Sjchandra if ((stp_localedir = getenv("STP_LOCALEDIR")) == NULL) 131198627Srrs stp_localedir = PACKAGE_LOCALE_DIR; 132198157Srrs 133212763Sjchandra strncpy(ll_CC, locale, sizeof(ll_CC) - 1); 134198627Srrs ll_CC[sizeof(ll_CC) - 1] = '\0'; 135198157Srrs 136212763Sjchandra if ((ptr = strchr(ll_CC, '.')) != NULL) 137198627Srrs *ptr = '\0'; 138198157Srrs 139212763Sjchandra snprintf(poname, sizeof(poname), "%s/%s/gutenprint_%s.po", stp_localedir, 140198627Srrs ll_CC, ll_CC); 141198157Srrs if (access(poname, 0) && strlen(ll_CC) > 2) 142212763Sjchandra { 143198627Srrs ll_CC[2] = '\0'; 144198157Srrs 145198627Srrs snprintf(poname, sizeof(poname), "%s/%s/gutenprint_%s.po", stp_localedir, 146212763Sjchandra ll_CC, ll_CC); 147198627Srrs } 148198157Srrs 149198157Srrs if ((pofile = fopen(poname, "rb")) == NULL) 150198627Srrs return (NULL); 151198157Srrs 152198157Srrs /* 153198157Srrs * Read the messages and add them to a string list... 154198157Srrs */ 155198157Srrs 156198157Srrs if ((po = stp_string_list_create()) == NULL) 157198157Srrs { 158198157Srrs fclose(pofile); 159198157Srrs return (NULL); 160198627Srrs } 161198627Srrs 162198627Srrs linenum = 0; 163198627Srrs id[0] = '\0'; 164198157Srrs str[0] = '\0'; 165198627Srrs in_id = 0; 166198627Srrs in_str = 0; 167198627Srrs ic = 0; 168198627Srrs 169198627Srrs while (fgets(line, sizeof(line), pofile)) 170198627Srrs { 171198627Srrs linenum ++; 172198627Srrs 173198627Srrs /* 174198157Srrs * Skip blank and comment lines... 175198627Srrs */ 176198157Srrs 177198157Srrs if (line[0] == '#') 178198157Srrs { 179198157Srrs if (line[1] == ':') 180198157Srrs fuzzy = 0; 181198157Srrs 182198157Srrs if (strstr(line, "fuzzy")) 183198157Srrs fuzzy = 1; 184212763Sjchandra } 185198157Srrs 186198627Srrs if (fuzzy || line[0] == '#' || line[0] == '\n') 187212763Sjchandra continue; 188198627Srrs 189198627Srrs /* 190198157Srrs * Strip the trailing quote... 191198627Srrs */ 192198627Srrs 193198157Srrs if ((ptr = (char *)strrchr(line, '\"')) == NULL) 194198627Srrs { 195198627Srrs fprintf(stderr, "DEBUG: Expected quoted string on line %d of %s!\n", 196198627Srrs linenum, poname); 197198627Srrs break; 198198627Srrs } 199198627Srrs 200198627Srrs *ptr = '\0'; 201198627Srrs 202198627Srrs /* 203198627Srrs * Find start of value... 204198627Srrs */ 205198627Srrs 206198627Srrs if ((ptr = strchr(line, '\"')) == NULL) 207198627Srrs { 208198157Srrs fprintf(stderr, "DEBUG: Expected quoted string on line %d of %s!\n", 209198627Srrs linenum, poname); 210198627Srrs break; 211198627Srrs } 212198627Srrs 213198627Srrs ptr ++; 214198627Srrs 215212763Sjchandra /* 216212763Sjchandra * Create or add to a message... 217198627Srrs */ 218198627Srrs 219198627Srrs if (!strncmp(line, "msgid", 5)) 220198627Srrs { 221198627Srrs in_id = 1; 222198627Srrs in_str = 0; 223212763Sjchandra 224212763Sjchandra if (id[0] && str[0]) 225212763Sjchandra { 226212763Sjchandra stpi_unquote(id); 227212763Sjchandra 228198627Srrs if (ic) 229198157Srrs { 230198627Srrs /* 231198627Srrs * Convert string to UTF-8... 232198627Srrs */ 233198627Srrs 234198627Srrs inbytes = strlen(str); 235198627Srrs inptr = str; 236198627Srrs outbytes = sizeof(utf8str); 237198627Srrs outptr = utf8str; 238198627Srrs 239198627Srrs iconv(ic, &inptr, &inbytes, &outptr, &outbytes); 240198627Srrs *outptr = '\0'; 241198627Srrs 242198627Srrs /* 243198627Srrs * Add it to the string list... 244198627Srrs */ 245198627Srrs 246198627Srrs stpi_unquote(utf8str); 247198627Srrs stp_string_list_add_string(po, id, utf8str); 248198627Srrs } 249198627Srrs else 250198627Srrs { 251198627Srrs stpi_unquote(str); 252198627Srrs stp_string_list_add_string(po, id, str); 253198627Srrs } 254198627Srrs } 255198627Srrs else if (!id[0] && str[0] && !ic) 256198627Srrs { 257198627Srrs /* 258198627Srrs * Look for the character set... 259198627Srrs */ 260198627Srrs 261198627Srrs const char *charset = strstr(str, "charset="); 262198627Srrs /* Source character set definition */ 263198627Srrs char fromcode[255], /* Source character set */ 264198627Srrs *fromptr; /* Pointer into fromcode */ 265198627Srrs 266198627Srrs if (charset) 267198627Srrs { 268198627Srrs /* 269198627Srrs * Extract character set and setup a transcode context... 270198627Srrs */ 271198627Srrs 272198627Srrs strncpy(fromcode, charset + 8, sizeof(fromcode) - 1); 273198627Srrs fromcode[sizeof(fromcode) - 1] = '\0'; 274198157Srrs for (fromptr = fromcode; *fromptr; fromptr ++) 275198627Srrs if (!isalnum(*fromptr & 255) && *fromptr != '-') 276198627Srrs break; 277198157Srrs *fromptr = '\0'; 278198157Srrs 279198157Srrs if (strcasecmp(fromcode, "utf-8")) 280198157Srrs { 281198157Srrs if ((ic = iconv_open("UTF-8", fromcode)) == (iconv_t)-1) 282198157Srrs { 283198157Srrs fprintf(stderr, 284198157Srrs "DEBUG: Unable to convert character set \"%s\": %s\n", 285212763Sjchandra fromcode, strerror(errno)); 286198157Srrs ic = 0; 287212763Sjchandra } 288198627Srrs } 289198627Srrs } 290198157Srrs } 291198627Srrs 292198627Srrs strncpy(id, ptr, sizeof(id) - 1); 293198157Srrs id[sizeof(id) - 1] = '\0'; 294198627Srrs str[0] = '\0'; 295198627Srrs } 296198627Srrs else if (!strncmp(line, "msgstr", 6)) 297198157Srrs { 298198627Srrs in_id = 0; 299198627Srrs in_str = 1; 300198157Srrs 301198157Srrs strncpy(str, ptr, sizeof(str) - 1); 302198157Srrs str[sizeof(str) - 1] = '\0'; 303198157Srrs } 304198627Srrs else if (line[0] == '\"' && in_str) 305198627Srrs { 306198157Srrs int str_len = strlen(str), 307198627Srrs ptr_len = strlen(ptr); 308198627Srrs 309198157Srrs 310198627Srrs if ((str_len + ptr_len + 1) > sizeof(str)) 311198627Srrs ptr_len = sizeof(str) - str_len - 1; 312198157Srrs 313198627Srrs if (ptr_len > 0) 314198157Srrs { 315198157Srrs memcpy(str + str_len, ptr, ptr_len); 316198627Srrs str[str_len + ptr_len] = '\0'; 317198627Srrs } 318198627Srrs } 319198627Srrs else if (line[0] == '\"' && in_id) 320198627Srrs { 321198627Srrs int id_len = strlen(id), 322198157Srrs ptr_len = strlen(ptr); 323198627Srrs 324198627Srrs 325198627Srrs if ((id_len + ptr_len + 1) > sizeof(id)) 326198627Srrs ptr_len = sizeof(id) - id_len - 1; 327198627Srrs 328198157Srrs if (ptr_len > 0) 329198627Srrs { 330198627Srrs memcpy(id + id_len, ptr, ptr_len); 331198627Srrs id[id_len + ptr_len] = '\0'; 332198627Srrs } 333198627Srrs } 334198157Srrs else 335198627Srrs { 336198627Srrs fprintf(stderr, "DEBUG: Unexpected text on line %d of %s!\n", 337198627Srrs linenum, poname); 338198627Srrs break; 339198157Srrs } 340198627Srrs } 341198627Srrs 342198157Srrs if (id[0] && str[0]) 343198157Srrs { 344198157Srrs stpi_unquote(id); 345198157Srrs 346198157Srrs if (ic) 347212763Sjchandra { 348198157Srrs /* 349212763Sjchandra * Convert string to UTF-8... 350198627Srrs */ 351198627Srrs 352198627Srrs inbytes = strlen(str); 353198627Srrs inptr = str; 354198157Srrs outbytes = sizeof(utf8str); 355198627Srrs outptr = utf8str; 356198627Srrs 357198627Srrs iconv(ic, &inptr, &inbytes, &outptr, &outbytes); 358198627Srrs *outptr = '\0'; 359198627Srrs 360198627Srrs /* 361198627Srrs * Add it to the string list... 362198627Srrs */ 363198627Srrs 364198157Srrs stpi_unquote(utf8str); 365198627Srrs stp_string_list_add_string(po, id, utf8str); 366198627Srrs } 367198627Srrs else 368198627Srrs { 369198627Srrs stpi_unquote(str); 370198627Srrs stp_string_list_add_string(po, id, str); 371198627Srrs } 372198627Srrs } 373198627Srrs 374198627Srrs fclose(pofile); 375198627Srrs 376198157Srrs /* 377198627Srrs * Add this to the cache... 378198627Srrs */ 379198627Srrs 380198627Srrs if ((pocache = calloc(1, sizeof(stpi_i18n_t))) != NULL) 381198627Srrs { 382198627Srrs strncpy(pocache->locale, locale, sizeof(pocache->locale) - 1); 383198627Srrs pocache->po = po; 384198627Srrs pocache->next = stpi_pocache; 385198627Srrs stpi_pocache = pocache; 386198627Srrs } 387198627Srrs 388198627Srrs if (ic) 389198627Srrs iconv_close(ic); 390198627Srrs return (po); 391198627Srrs} 392198627Srrs 393198627Srrs 394198627Srrs/* 395198627Srrs * 'stp_i18n_lookup()' - Lookup a string in the message catalog... 396198627Srrs */ 397198627Srrs 398198627Srrsconst char * /* O - Localized message */ 399198627Srrsstp_i18n_lookup( 400198627Srrs const stp_string_list_t *po, /* I - Message catalog */ 401198627Srrs const char *message) /* I - Message */ 402198627Srrs{ 403198627Srrs stp_param_string_t *param; /* Matching message */ 404198627Srrs 405198627Srrs 406198627Srrs if (po && (param = stp_string_list_find(po, message)) != NULL && param->text) 407198627Srrs return (param->text); 408198627Srrs else 409198627Srrs return (message); 410198627Srrs} 411198627Srrs 412198627Srrs 413198627Srrs/* 414198627Srrs * 'stp_i18n_printf()' - Send a formatted string to stderr. 415198627Srrs */ 416198627Srrs 417198627Srrsvoid 418198627Srrsstp_i18n_printf( 419198627Srrs const stp_string_list_t *po, /* I - Message catalog */ 420198627Srrs const char *message, /* I - Printf-style message */ 421198157Srrs ...) /* I - Additional arguments as needed */ 422198627Srrs{ 423198157Srrs va_list ap; /* Argument pointer */ 424198627Srrs 425198627Srrs 426198627Srrs va_start(ap, message); 427198627Srrs vfprintf(stderr, stp_i18n_lookup(po, message), ap); 428198627Srrs va_end(ap); 429198627Srrs} 430198627Srrs 431198627Srrs 432198627Srrs/* 433198627Srrs * 'stpi_unquote()' - Unquote characters in strings. 434198627Srrs */ 435198157Srrs 436198627Srrsstatic void 437198627Srrsstpi_unquote(char *s) /* IO - Original string */ 438198627Srrs{ 439198627Srrs char *d = s; /* Destination pointer */ 440198627Srrs 441198157Srrs 442198627Srrs while (*s) 443198627Srrs { 444198627Srrs if (*s == '\\') 445198627Srrs { 446198627Srrs s ++; 447198627Srrs if (isdigit(*s)) 448198627Srrs { 449198157Srrs *d = 0; 450198627Srrs 451198627Srrs while (isdigit(*s)) 452198627Srrs { 453198627Srrs *d = *d * 8 + *s - '0'; 454198627Srrs s ++; 455198627Srrs } 456198157Srrs 457198627Srrs d ++; 458198627Srrs } 459198627Srrs else 460198157Srrs { 461198627Srrs if (*s == 'n') 462198627Srrs *d ++ = '\n'; 463198627Srrs else if (*s == 'r') 464198627Srrs *d ++ = '\r'; 465198627Srrs else if (*s == 't') 466198627Srrs *d ++ = '\t'; 467198157Srrs else 468198627Srrs *d++ = *s; 469198627Srrs 470198627Srrs s ++; 471198627Srrs } 472198627Srrs } 473198627Srrs else 474198157Srrs *d++ = *s++; 475198157Srrs } 476198627Srrs 477198157Srrs *d = '\0'; 478198157Srrs} 479198627Srrs 480198627Srrs 481198627Srrs/* 482198627Srrs * End of "$Id: i18n.c,v 1.8 2009/04/11 19:05:12 rlk Exp $". 483198157Srrs */ 484198627Srrs