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