summaryrefslogtreecommitdiff
path: root/src/cups/i18n.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cups/i18n.c')
-rw-r--r--src/cups/i18n.c481
1 files changed, 481 insertions, 0 deletions
diff --git a/src/cups/i18n.c b/src/cups/i18n.c
new file mode 100644
index 0000000..59406ec
--- /dev/null
+++ b/src/cups/i18n.c
@@ -0,0 +1,481 @@
+/*
+ * "$Id: i18n.c,v 1.6 2008/08/16 16:56:06 rlk Exp $"
+ *
+ * Internationalization functions for CUPS drivers.
+ *
+ * Copyright 2008 Michael Sweet (mike@easysw.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Contents:
+ *
+ * stp_i18n_load() - Load a message catalog for a locale.
+ * stp_i18n_lookup() - Lookup a string in the message catalog...
+ * stp_i18n_printf() - Send a formatted string to stderr.
+ * stpi_unquote() - Unquote characters in strings.
+ */
+
+/*
+ * Include necessary files...
+ */
+
+#include "i18n.h"
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <iconv.h>
+
+
+/*
+ * GNU gettext uses a simple .po file format:
+ *
+ * # comment
+ * msgid "id"
+ * "optional continuation"
+ * msgstr "str"
+ * "optional continuation"
+ *
+ * Both the id and str strings use standard C quoting for special characters
+ * like newline and the double quote character.
+ */
+
+
+/*
+ * Cache structure...
+ */
+
+typedef struct stpi_i18n_s
+{
+ struct stpi_i18n_s *next; /* Next catalog */
+ char locale[6]; /* Locale */
+ stp_string_list_t *po; /* Message catalog */
+} stpi_i18n_t;
+
+
+/*
+ * Local functions...
+ */
+
+static void stpi_unquote(char *s);
+
+
+/*
+ * Local globals...
+ */
+
+static stpi_i18n_t *stpi_pocache = NULL;
+
+
+/*
+ * 'stp_i18n_load()' - Load a message catalog for a locale.
+ */
+
+stp_string_list_t * /* O - Message catalog */
+stp_i18n_load(const char *locale) /* I - Locale name */
+{
+ stp_string_list_t *po; /* Message catalog */
+ char ll_CC[6], /* Locale ID */
+ poname[1024]; /* .po filename */
+ stpi_i18n_t *pocache; /* Current cache entry */
+ FILE *pofile; /* .po file */
+ const char *stp_localedir; /* STP_LOCALEDIR environment variable */
+ char line[4096], /* Line buffer */
+ *ptr, /* Pointer into buffer */
+ id[4096], /* Translation ID */
+ str[4096], /* Translation string */
+ utf8str[4096]; /* UTF-8 translation string */
+ int in_id, /* Processing "id" string? */
+ in_str, /* Processing "str" string? */
+ linenum; /* Line number in .po file */
+ iconv_t ic; /* Transcoder to UTF-8 */
+ size_t inbytes, /* Number of input buffer bytes */
+ outbytes; /* Number of output buffer bytes */
+ char *inptr, /* Pointer into input buffer */
+ *outptr; /* Pointer into output buffer */
+ int fuzzy = 0; /* Fuzzy translation? */
+
+
+ if (!locale)
+ return (NULL);
+
+ /*
+ * See if the locale is already loaded...
+ */
+
+ for (pocache = stpi_pocache; pocache; pocache = pocache->next)
+ if (!strcmp(locale, pocache->locale))
+ return (pocache->po);
+
+ /*
+ * Find the message catalog for the given locale...
+ */
+
+ if ((stp_localedir = getenv("STP_LOCALEDIR")) == NULL)
+ stp_localedir = PACKAGE_LOCALE_DIR;
+
+ strncpy(ll_CC, locale, sizeof(ll_CC) - 1);
+ ll_CC[sizeof(ll_CC) - 1] = '\0';
+
+ if ((ptr = strchr(ll_CC, '.')) != NULL)
+ *ptr = '\0';
+
+ snprintf(poname, sizeof(poname), "%s/%s/gutenprint_%s.po", stp_localedir,
+ ll_CC, ll_CC);
+ if (access(poname, 0) && strlen(ll_CC) > 2)
+ {
+ ll_CC[2] = '\0';
+
+ snprintf(poname, sizeof(poname), "%s/%s/gutenprint_%s.po", stp_localedir,
+ ll_CC, ll_CC);
+ }
+
+ if ((pofile = fopen(poname, "rb")) == NULL)
+ return (NULL);
+
+ /*
+ * Read the messages and add them to a string list...
+ */
+
+ if ((po = stp_string_list_create()) == NULL)
+ {
+ fclose(pofile);
+ return (NULL);
+ }
+
+ linenum = 0;
+ id[0] = '\0';
+ str[0] = '\0';
+ in_id = 0;
+ in_str = 0;
+ ic = 0;
+
+ while (fgets(line, sizeof(line), pofile))
+ {
+ linenum ++;
+
+ /*
+ * Skip blank and comment lines...
+ */
+
+ if (line[0] == '#')
+ {
+ if (line[1] == ':')
+ fuzzy = 0;
+
+ if (strstr(line, "fuzzy"))
+ fuzzy = 1;
+ }
+
+ if (fuzzy || line[0] == '#' || line[0] == '\n')
+ continue;
+
+ /*
+ * Strip the trailing quote...
+ */
+
+ if ((ptr = (char *)strrchr(line, '\"')) == NULL)
+ {
+ fprintf(stderr, "DEBUG: Expected quoted string on line %d of %s!\n",
+ linenum, poname);
+ break;
+ }
+
+ *ptr = '\0';
+
+ /*
+ * Find start of value...
+ */
+
+ if ((ptr = strchr(line, '\"')) == NULL)
+ {
+ fprintf(stderr, "DEBUG: Expected quoted string on line %d of %s!\n",
+ linenum, poname);
+ break;
+ }
+
+ ptr ++;
+
+ /*
+ * Create or add to a message...
+ */
+
+ if (!strncmp(line, "msgid", 5))
+ {
+ in_id = 1;
+ in_str = 0;
+
+ if (id[0] && str[0])
+ {
+ stpi_unquote(id);
+
+ if (ic)
+ {
+ /*
+ * Convert string to UTF-8...
+ */
+
+ inbytes = strlen(str);
+ inptr = str;
+ outbytes = sizeof(utf8str);
+ outptr = utf8str;
+
+ iconv(ic, &inptr, &inbytes, &outptr, &outbytes);
+ *outptr = '\0';
+
+ /*
+ * Add it to the string list...
+ */
+
+ stpi_unquote(utf8str);
+ stp_string_list_add_string(po, id, utf8str);
+ }
+ else
+ {
+ stpi_unquote(str);
+ stp_string_list_add_string(po, id, str);
+ }
+ }
+ else if (!id[0] && str[0] && !ic)
+ {
+ /*
+ * Look for the character set...
+ */
+
+ const char *charset = strstr(str, "charset=");
+ /* Source character set definition */
+ char fromcode[255], /* Source character set */
+ *fromptr; /* Pointer into fromcode */
+
+ if (charset)
+ {
+ /*
+ * Extract character set and setup a transcode context...
+ */
+
+ strncpy(fromcode, charset + 8, sizeof(fromcode) - 1);
+ fromcode[sizeof(fromcode) - 1] = '\0';
+ for (fromptr = fromcode; *fromptr; fromptr ++)
+ if (!isalnum(*fromptr & 255) && *fromptr != '-')
+ break;
+ *fromptr = '\0';
+
+ if (strcasecmp(fromcode, "utf-8"))
+ {
+ if ((ic = iconv_open("UTF-8", fromcode)) == (iconv_t)-1)
+ {
+ fprintf(stderr,
+ "DEBUG: Unable to convert character set \"%s\": %s\n",
+ fromcode, strerror(errno));
+ ic = 0;
+ }
+ }
+ }
+ }
+
+ strncpy(id, ptr, sizeof(id) - 1);
+ id[sizeof(id) - 1] = '\0';
+ str[0] = '\0';
+ }
+ else if (!strncmp(line, "msgstr", 6))
+ {
+ in_id = 0;
+ in_str = 1;
+
+ strncpy(str, ptr, sizeof(str) - 1);
+ str[sizeof(str) - 1] = '\0';
+ }
+ else if (line[0] == '\"' && in_str)
+ {
+ int str_len = strlen(str),
+ ptr_len = strlen(ptr);
+
+
+ if ((str_len + ptr_len + 1) > sizeof(str))
+ ptr_len = sizeof(str) - str_len - 1;
+
+ if (ptr_len > 0)
+ {
+ memcpy(str + str_len, ptr, ptr_len);
+ str[str_len + ptr_len] = '\0';
+ }
+ }
+ else if (line[0] == '\"' && in_id)
+ {
+ int id_len = strlen(id),
+ ptr_len = strlen(ptr);
+
+
+ if ((id_len + ptr_len + 1) > sizeof(id))
+ ptr_len = sizeof(id) - id_len - 1;
+
+ if (ptr_len > 0)
+ {
+ memcpy(id + id_len, ptr, ptr_len);
+ id[id_len + ptr_len] = '\0';
+ }
+ }
+ else
+ {
+ fprintf(stderr, "DEBUG: Unexpected text on line %d of %s!\n",
+ linenum, poname);
+ break;
+ }
+ }
+
+ if (id[0] && str[0])
+ {
+ stpi_unquote(id);
+
+ if (ic)
+ {
+ /*
+ * Convert string to UTF-8...
+ */
+
+ inbytes = strlen(str);
+ inptr = str;
+ outbytes = sizeof(utf8str);
+ outptr = utf8str;
+
+ iconv(ic, &inptr, &inbytes, &outptr, &outbytes);
+ *outptr = '\0';
+
+ /*
+ * Add it to the string list...
+ */
+
+ stpi_unquote(utf8str);
+ stp_string_list_add_string(po, id, utf8str);
+ }
+ else
+ {
+ stpi_unquote(str);
+ stp_string_list_add_string(po, id, str);
+ }
+ }
+
+ fclose(pofile);
+
+ /*
+ * Add this to the cache...
+ */
+
+ if ((pocache = calloc(1, sizeof(stpi_i18n_t))) != NULL)
+ {
+ strncpy(pocache->locale, locale, sizeof(pocache->locale) - 1);
+ pocache->po = po;
+ pocache->next = stpi_pocache;
+ stpi_pocache = pocache;
+ }
+
+ return (po);
+}
+
+
+/*
+ * 'stp_i18n_lookup()' - Lookup a string in the message catalog...
+ */
+
+const char * /* O - Localized message */
+stp_i18n_lookup(
+ stp_string_list_t *po, /* I - Message catalog */
+ const char *message) /* I - Message */
+{
+ stp_param_string_t *param; /* Matching message */
+
+
+ if (po && (param = stp_string_list_find(po, message)) != NULL && param->text)
+ return (param->text);
+ else
+ return (message);
+}
+
+
+/*
+ * 'stp_i18n_printf()' - Send a formatted string to stderr.
+ */
+
+void
+stp_i18n_printf(
+ stp_string_list_t *po, /* I - Message catalog */
+ const char *message, /* I - Printf-style message */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Argument pointer */
+
+
+ va_start(ap, message);
+ vfprintf(stderr, stp_i18n_lookup(po, message), ap);
+ va_end(ap);
+}
+
+
+/*
+ * 'stpi_unquote()' - Unquote characters in strings.
+ */
+
+static void
+stpi_unquote(char *s) /* IO - Original string */
+{
+ char *d = s; /* Destination pointer */
+
+
+ while (*s)
+ {
+ if (*s == '\\')
+ {
+ s ++;
+ if (isdigit(*s))
+ {
+ *d = 0;
+
+ while (isdigit(*s))
+ {
+ *d = *d * 8 + *s - '0';
+ s ++;
+ }
+
+ d ++;
+ }
+ else
+ {
+ if (*s == 'n')
+ *d ++ = '\n';
+ else if (*s == 'r')
+ *d ++ = '\r';
+ else if (*s == 't')
+ *d ++ = '\t';
+ else
+ *d++ = *s;
+
+ s ++;
+ }
+ }
+ else
+ *d++ = *s++;
+ }
+
+ *d = '\0';
+}
+
+
+/*
+ * End of "$Id: i18n.c,v 1.6 2008/08/16 16:56:06 rlk Exp $".
+ */