int /* O - Exit code */ main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line arguments */ { FILE *strings; /* .strings file */ cups_array_t *po; /* .po file */ char iconv[1024]; /* iconv command */ _cups_message_t *msg; /* Current message */ if (argc != 3) { puts("Usage: po2strings filename.po filename.strings"); return (1); } /* * Use the CUPS .po loader to get the message strings... */ if ((po = _cupsMessageLoad(argv[1])) == NULL) { perror(argv[1]); return (1); } /* * Cheat by using iconv to write the .strings file with a UTF-16 encoding. * The .po file uses UTF-8... */ snprintf(iconv, sizeof(iconv), "iconv -f utf-8 -t utf-16 >'%s'", argv[2]); if ((strings = popen(iconv, "w")) == NULL) { perror(argv[2]); _cupsMessageFree(po); return (1); } for (msg = (_cups_message_t *)cupsArrayFirst(po); msg; msg = (_cups_message_t *)cupsArrayNext(po)) { write_string(strings, msg->id); fputs(" = ", strings); write_string(strings, msg->str); fputs(";\n", strings); } printf("%s: %d messages.\n", argv[2], cupsArrayCount(po)); pclose(strings); _cupsMessageFree(po); return (0); }
void cupsLangFlush(void) { cups_lang_t *lang, /* Current language */ *next; /* Next language */ /* * Free all languages in the cache... */ _cupsMutexLock(&lang_mutex); for (lang = lang_cache; lang != NULL; lang = next) { /* * Free all messages... */ _cupsMessageFree(lang->strings); /* * Then free the language structure itself... */ next = lang->next; free(lang); } lang_cache = NULL; _cupsMutexUnlock(&lang_mutex); }
cups_lang_t * /* O - Language data */ cupsLangGet(const char *language) /* I - Language or locale */ { int i; /* Looping var */ #ifndef __APPLE__ char locale[255]; /* Copy of locale name */ #endif /* !__APPLE__ */ char langname[16], /* Requested language name */ country[16], /* Country code */ charset[16], /* Character set */ *csptr, /* Pointer to CODESET string */ *ptr, /* Pointer into language/charset */ real[48]; /* Real language name */ cups_encoding_t encoding; /* Encoding to use */ cups_lang_t *lang; /* Current language... */ static const char * const locale_encodings[] = { /* Locale charset names */ "ASCII", "ISO88591", "ISO88592", "ISO88593", "ISO88594", "ISO88595", "ISO88596", "ISO88597", "ISO88598", "ISO88599", "ISO885910", "UTF8", "ISO885913", "ISO885914", "ISO885915", "CP874", "CP1250", "CP1251", "CP1252", "CP1253", "CP1254", "CP1255", "CP1256", "CP1257", "CP1258", "KOI8R", "KOI8U", "ISO885911", "ISO885916", "MACROMAN", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "CP932", "CP936", "CP949", "CP950", "CP1361", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "EUCCN", "EUCJP", "EUCKR", "EUCTW", "SHIFT_JISX0213" }; DEBUG_printf(("2cupsLangGet(language=\"%s\")", language)); #ifdef __APPLE__ /* * Set the character set to UTF-8... */ strlcpy(charset, "UTF8", sizeof(charset)); /* * Apple's setlocale doesn't give us the user's localization * preference so we have to look it up this way... */ if (!language) { if (!getenv("SOFTWARE") || (language = getenv("LANG")) == NULL) language = appleLangDefault(); DEBUG_printf(("4cupsLangGet: language=\"%s\"", language)); } #else /* * Set the charset to "unknown"... */ charset[0] = '\0'; /* * Use setlocale() to determine the currently set locale, and then * fallback to environment variables to avoid setting the locale, * since setlocale() is not thread-safe! */ if (!language) { /* * First see if the locale has been set; if it is still "C" or * "POSIX", use the environment to get the default... */ # ifdef LC_MESSAGES ptr = setlocale(LC_MESSAGES, NULL); # else ptr = setlocale(LC_ALL, NULL); # endif /* LC_MESSAGES */ DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr)); if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX")) { /* * Get the character set from the LC_CTYPE locale setting... */ if ((ptr = getenv("LC_CTYPE")) == NULL) if ((ptr = getenv("LC_ALL")) == NULL) if ((ptr = getenv("LANG")) == NULL) ptr = "en_US"; if ((csptr = strchr(ptr, '.')) != NULL) { /* * Extract the character set from the environment... */ for (ptr = charset, csptr ++; *csptr; csptr ++) if (ptr < (charset + sizeof(charset) - 1) && _cups_isalnum(*csptr)) *ptr++ = *csptr; *ptr = '\0'; } /* * Get the locale for messages from the LC_MESSAGES locale setting... */ if ((ptr = getenv("LC_MESSAGES")) == NULL) if ((ptr = getenv("LC_ALL")) == NULL) if ((ptr = getenv("LANG")) == NULL) ptr = "en_US"; } if (ptr) { strlcpy(locale, ptr, sizeof(locale)); language = locale; /* * CUPS STR #2575: Map "nb" to "no" for back-compatibility... */ if (!strncmp(locale, "nb", 2)) locale[1] = 'o'; DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language)); } } #endif /* __APPLE__ */ /* * If "language" is NULL at this point, then chances are we are using * a language that is not installed for the base OS. */ if (!language) { /* * Switch to the POSIX ("C") locale... */ language = "C"; } #ifdef CODESET /* * On systems that support the nl_langinfo(CODESET) call, use * this value as the character set... */ if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL) { /* * Copy all of the letters and numbers in the CODESET string... */ for (ptr = charset; *csptr; csptr ++) if (_cups_isalnum(*csptr) && ptr < (charset + sizeof(charset) - 1)) *ptr++ = *csptr; *ptr = '\0'; DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via " "nl_langinfo(CODESET)...", charset)); } #endif /* CODESET */ /* * If we don't have a character set by now, default to UTF-8... */ if (!charset[0]) strlcpy(charset, "UTF8", sizeof(charset)); /* * Parse the language string passed in to a locale string. "C" is the * standard POSIX locale and is copied unchanged. Otherwise the * language string is converted from ll-cc[.charset] (language-country) * to ll_CC[.CHARSET] to match the file naming convention used by all * POSIX-compliant operating systems. Invalid language names are mapped * to the POSIX locale. */ country[0] = '\0'; if (language == NULL || !language[0] || !strcmp(language, "POSIX")) strlcpy(langname, "C", sizeof(langname)); else { /* * Copy the parts of the locale string over safely... */ for (ptr = langname; *language; language ++) if (*language == '_' || *language == '-' || *language == '.') break; else if (ptr < (langname + sizeof(langname) - 1)) *ptr++ = (char)tolower(*language & 255); *ptr = '\0'; if (*language == '_' || *language == '-') { /* * Copy the country code... */ for (language ++, ptr = country; *language; language ++) if (*language == '.') break; else if (ptr < (country + sizeof(country) - 1)) *ptr++ = (char)toupper(*language & 255); *ptr = '\0'; } if (*language == '.' && !charset[0]) { /* * Copy the encoding... */ for (language ++, ptr = charset; *language; language ++) if (_cups_isalnum(*language) && ptr < (charset + sizeof(charset) - 1)) *ptr++ = (char)toupper(*language & 255); *ptr = '\0'; } /* * Force a POSIX locale for an invalid language name... */ if (strlen(langname) != 2) { strlcpy(langname, "C", sizeof(langname)); country[0] = '\0'; charset[0] = '\0'; } } DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"", langname, country, charset)); /* * Figure out the desired encoding... */ encoding = CUPS_AUTO_ENCODING; if (charset[0]) { for (i = 0; i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0])); i ++) if (!_cups_strcasecmp(charset, locale_encodings[i])) { encoding = (cups_encoding_t)i; break; } if (encoding == CUPS_AUTO_ENCODING) { /* * Map alternate names for various character sets... */ if (!_cups_strcasecmp(charset, "iso-2022-jp") || !_cups_strcasecmp(charset, "sjis")) encoding = CUPS_WINDOWS_932; else if (!_cups_strcasecmp(charset, "iso-2022-cn")) encoding = CUPS_WINDOWS_936; else if (!_cups_strcasecmp(charset, "iso-2022-kr")) encoding = CUPS_WINDOWS_949; else if (!_cups_strcasecmp(charset, "big5")) encoding = CUPS_WINDOWS_950; } } DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding, encoding == CUPS_AUTO_ENCODING ? "auto" : lang_encodings[encoding])); /* * See if we already have this language/country loaded... */ if (country[0]) snprintf(real, sizeof(real), "%s_%s", langname, country); else strlcpy(real, langname, sizeof(real)); _cupsMutexLock(&lang_mutex); if ((lang = cups_cache_lookup(real, encoding)) != NULL) { _cupsMutexUnlock(&lang_mutex); DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real)); return (lang); } /* * See if there is a free language available; if so, use that * record... */ for (lang = lang_cache; lang != NULL; lang = lang->next) if (lang->used == 0) break; if (lang == NULL) { /* * Allocate memory for the language and add it to the cache. */ if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL) { _cupsMutexUnlock(&lang_mutex); return (NULL); } lang->next = lang_cache; lang_cache = lang; } else { /* * Free all old strings as needed... */ _cupsMessageFree(lang->strings); lang->strings = NULL; } /* * Then assign the language and encoding fields... */ lang->used ++; strlcpy(lang->language, real, sizeof(lang->language)); if (encoding != CUPS_AUTO_ENCODING) lang->encoding = encoding; else lang->encoding = CUPS_UTF8; /* * Return... */ _cupsMutexUnlock(&lang_mutex); return (lang); }
int /* O - Exit code */ main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line arguments */ { int i; /* Looping var */ cups_array_t *po; /* .po file */ _cups_message_t *msg; /* Current message */ cups_array_t *idfmts, /* Format strings in msgid */ *strfmts; /* Format strings in msgstr */ char *idfmt, /* Current msgid format string */ *strfmt; /* Current msgstr format string */ int fmtidx; /* Format index */ int status, /* Exit status */ pass, /* Pass/fail status */ untranslated; /* Untranslated messages */ char idbuf[80], /* Abbreviated msgid */ strbuf[80]; /* Abbreviated msgstr */ if (argc < 2) { puts("Usage: checkpo filename.po [... filenameN.po]"); return (1); } /* * Check every .po file on the command-line... */ for (i = 1, status = 0; i < argc; i ++) { /* * Use the CUPS .po loader to get the message strings... */ if ((po = _cupsMessageLoad(argv[i], 1)) == NULL) { perror(argv[i]); return (1); } if (i > 1) putchar('\n'); printf("%s: ", argv[i]); fflush(stdout); /* * Scan every message for a % string and then match them up with * the corresponding string in the translation... */ pass = 1; untranslated = 0; for (msg = (_cups_message_t *)cupsArrayFirst(po); msg; msg = (_cups_message_t *)cupsArrayNext(po)) { /* * Make sure filter message prefixes are not translated... */ if (!strncmp(msg->id, "ALERT:", 6) || !strncmp(msg->id, "CRIT:", 5) || !strncmp(msg->id, "DEBUG:", 6) || !strncmp(msg->id, "DEBUG2:", 7) || !strncmp(msg->id, "EMERG:", 6) || !strncmp(msg->id, "ERROR:", 6) || !strncmp(msg->id, "INFO:", 5) || !strncmp(msg->id, "NOTICE:", 7) || !strncmp(msg->id, "WARNING:", 8)) { if (pass) { pass = 0; puts("FAIL"); } printf(" Bad prefix on filter message \"%s\"\n", abbreviate(msg->id, idbuf, sizeof(idbuf))); } idfmt = msg->id + strlen(msg->id) - 1; if (idfmt >= msg->id && *idfmt == '\n') { if (pass) { pass = 0; puts("FAIL"); } printf(" Trailing newline in message \"%s\"\n", abbreviate(msg->id, idbuf, sizeof(idbuf))); } for (; idfmt >= msg->id; idfmt --) if (!isspace(*idfmt & 255)) break; if (idfmt >= msg->id && *idfmt == '!') { if (pass) { pass = 0; puts("FAIL"); } printf(" Exclamation in message \"%s\"\n", abbreviate(msg->id, idbuf, sizeof(idbuf))); } if ((idfmt - 2) >= msg->id && !strncmp(idfmt - 2, "...", 3)) { if (pass) { pass = 0; puts("FAIL"); } printf(" Ellipsis in message \"%s\"\n", abbreviate(msg->id, idbuf, sizeof(idbuf))); } if (!msg->str || !msg->str[0]) { untranslated ++; continue; } else if (strchr(msg->id, '%')) { idfmts = collect_formats(msg->id); strfmts = collect_formats(msg->str); fmtidx = 0; for (strfmt = (char *)cupsArrayFirst(strfmts); strfmt; strfmt = (char *)cupsArrayNext(strfmts)) { if (isdigit(strfmt[1] & 255) && strfmt[2] == '$') { /* * Handle positioned format stuff... */ fmtidx = strfmt[1] - '1'; strfmt += 3; if ((idfmt = (char *)cupsArrayIndex(idfmts, fmtidx)) != NULL) idfmt ++; } else { /* * Compare against the current format... */ idfmt = (char *)cupsArrayIndex(idfmts, fmtidx); } fmtidx ++; if (!idfmt || strcmp(strfmt, idfmt)) break; } if (cupsArrayCount(strfmts) != cupsArrayCount(idfmts) || strfmt) { if (pass) { pass = 0; puts("FAIL"); } printf(" Bad translation string \"%s\"\n for \"%s\"\n", abbreviate(msg->str, strbuf, sizeof(strbuf)), abbreviate(msg->id, idbuf, sizeof(idbuf))); fputs(" Translation formats:", stdout); for (strfmt = (char *)cupsArrayFirst(strfmts); strfmt; strfmt = (char *)cupsArrayNext(strfmts)) printf(" %s", strfmt); fputs("\n Original formats:", stdout); for (idfmt = (char *)cupsArrayFirst(idfmts); idfmt; idfmt = (char *)cupsArrayNext(idfmts)) printf(" %s", idfmt); putchar('\n'); putchar('\n'); } free_formats(idfmts); free_formats(strfmts); } /* * Only allow \\, \n, \r, \t, \", and \### character escapes... */ for (strfmt = msg->str; *strfmt; strfmt ++) if (*strfmt == '\\' && strfmt[1] != '\\' && strfmt[1] != 'n' && strfmt[1] != 'r' && strfmt[1] != 't' && strfmt[1] != '\"' && !isdigit(strfmt[1] & 255)) { if (pass) { pass = 0; puts("FAIL"); } printf(" Bad escape \\%c in filter message \"%s\"\n" " for \"%s\"\n", strfmt[1], abbreviate(msg->str, strbuf, sizeof(strbuf)), abbreviate(msg->id, idbuf, sizeof(idbuf))); break; } } if (pass) { if ((untranslated * 10) >= cupsArrayCount(po) && strcmp(argv[i], "cups.pot")) { /* * Only allow 10% of messages to be untranslated before we fail... */ pass = 0; puts("FAIL"); printf(" Too many untranslated messages (%d of %d)\n", untranslated, cupsArrayCount(po)); } else if (untranslated > 0) printf("PASS (%d of %d untranslated)\n", untranslated, cupsArrayCount(po)); else puts("PASS"); } if (!pass) status = 1; _cupsMessageFree(po); } return (status); }