You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
7.7 KiB
395 lines
7.7 KiB
/*
|
|
* Convert a GNU gettext .po file to an Apple .strings file.
|
|
*
|
|
* Copyright 2007-2017 by Apple Inc.
|
|
*
|
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more information.
|
|
*
|
|
* Usage:
|
|
*
|
|
* po2strings filename.strings filename.po
|
|
*
|
|
* Compile with:
|
|
*
|
|
* gcc -o po2strings po2strings.c `cups-config --libs`
|
|
*/
|
|
|
|
#include <cups/cups-private.h>
|
|
|
|
|
|
/*
|
|
* The .strings file format is simple:
|
|
*
|
|
* // comment
|
|
* "msgid" = "msgstr";
|
|
*
|
|
* The GNU gettext .po format is also fairly simple:
|
|
*
|
|
* #. comment
|
|
* msgid "some text"
|
|
* msgstr "localized text"
|
|
*
|
|
* The comment, msgid, and msgstr text can span multiple lines using the form:
|
|
*
|
|
* #. comment
|
|
* #. more comments
|
|
* msgid ""
|
|
* "some long text"
|
|
* msgstr ""
|
|
* "localized text spanning "
|
|
* "multiple lines"
|
|
*
|
|
* Both the msgid and msgstr strings use standard C quoting for special
|
|
* characters like newline and the double quote character.
|
|
*/
|
|
|
|
static char *normalize_string(const char *idstr, char *buffer, size_t bufsize);
|
|
|
|
|
|
/*
|
|
* main() - Convert .po file to .strings.
|
|
*/
|
|
|
|
int /* O - Exit code */
|
|
main(int argc, /* I - Number of command-line args */
|
|
char *argv[]) /* I - Command-line arguments */
|
|
{
|
|
int i; /* Looping var */
|
|
const char *pofile, /* .po filename */
|
|
*stringsfile; /* .strings filename */
|
|
cups_file_t *po, /* .po file */
|
|
*strings; /* .strings file */
|
|
char s[4096], /* String buffer */
|
|
*ptr, /* Pointer into buffer */
|
|
*temp, /* New string */
|
|
*msgid, /* msgid string */
|
|
*msgstr, /* msgstr string */
|
|
normalized[8192];/* Normalized msgid string */
|
|
size_t length; /* Length of combined strings */
|
|
int use_msgid; /* Use msgid strings for msgstr? */
|
|
|
|
|
|
/*
|
|
* Process command-line arguments...
|
|
*/
|
|
|
|
pofile = NULL;
|
|
stringsfile = NULL;
|
|
use_msgid = 0;
|
|
|
|
for (i = 1; i < argc; i ++)
|
|
{
|
|
if (!strcmp(argv[i], "-m"))
|
|
use_msgid = 1;
|
|
else if (argv[i][0] == '-')
|
|
{
|
|
puts("Usage: po2strings [-m] filename.po filename.strings");
|
|
return (1);
|
|
}
|
|
else if (!pofile)
|
|
pofile = argv[i];
|
|
else if (!stringsfile)
|
|
stringsfile = argv[i];
|
|
else
|
|
{
|
|
puts("Usage: po2strings [-m] filename.po filename.strings");
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
if (!pofile || !stringsfile)
|
|
{
|
|
puts("Usage: po2strings [-m] filename.po filename.strings");
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Read strings from the .po file and write to the .strings file...
|
|
*/
|
|
|
|
if ((po = cupsFileOpen(pofile, "r")) == NULL)
|
|
{
|
|
perror(pofile);
|
|
return (1);
|
|
}
|
|
|
|
if ((strings = cupsFileOpen(stringsfile, "w")) == NULL)
|
|
{
|
|
perror(stringsfile);
|
|
cupsFileClose(po);
|
|
return (1);
|
|
}
|
|
|
|
msgid = msgstr = NULL;
|
|
|
|
while (cupsFileGets(po, s, sizeof(s)) != NULL)
|
|
{
|
|
if (s[0] == '#' && s[1] == '.')
|
|
{
|
|
/*
|
|
* Copy comment string...
|
|
*/
|
|
|
|
if (msgid && msgstr)
|
|
{
|
|
/*
|
|
* First output the last localization string...
|
|
*/
|
|
|
|
if (*msgid)
|
|
cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid,
|
|
(use_msgid || !*msgstr) ? msgid : msgstr);
|
|
|
|
free(msgid);
|
|
free(msgstr);
|
|
msgid = msgstr = NULL;
|
|
}
|
|
|
|
cupsFilePrintf(strings, "//%s\n", s + 2);
|
|
}
|
|
else if (s[0] == '#' || !s[0])
|
|
{
|
|
/*
|
|
* Skip blank and file comment lines...
|
|
*/
|
|
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Strip the trailing quote...
|
|
*/
|
|
|
|
if ((ptr = strrchr(s, '\"')) == NULL)
|
|
continue;
|
|
|
|
*ptr = '\0';
|
|
|
|
/*
|
|
* Find start of value...
|
|
*/
|
|
|
|
if ((ptr = strchr(s, '\"')) == NULL)
|
|
continue;
|
|
|
|
ptr ++;
|
|
|
|
/*
|
|
* Create or add to a message...
|
|
*/
|
|
|
|
if (!strncmp(s, "msgid", 5))
|
|
{
|
|
/*
|
|
* Output previous message as needed...
|
|
*/
|
|
|
|
if (msgid && msgstr)
|
|
{
|
|
if (*msgid)
|
|
cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
|
|
}
|
|
|
|
if (msgid)
|
|
free(msgid);
|
|
|
|
if (msgstr)
|
|
free(msgstr);
|
|
|
|
msgid = strdup(ptr);
|
|
msgstr = NULL;
|
|
}
|
|
else if (s[0] == '\"' && (msgid || msgstr))
|
|
{
|
|
/*
|
|
* Append to current string...
|
|
*/
|
|
|
|
size_t ptrlen = strlen(ptr); /* Length of string */
|
|
|
|
length = strlen(msgstr ? msgstr : msgid);
|
|
|
|
if ((temp = realloc(msgstr ? msgstr : msgid,
|
|
length + ptrlen + 1)) == NULL)
|
|
{
|
|
free(msgid);
|
|
if (msgstr)
|
|
free(msgstr);
|
|
perror("Unable to allocate string");
|
|
return (1);
|
|
}
|
|
|
|
if (msgstr)
|
|
{
|
|
/*
|
|
* Copy the new portion to the end of the msgstr string - safe
|
|
* to use strcpy because the buffer is allocated to the correct
|
|
* size...
|
|
*/
|
|
|
|
msgstr = temp;
|
|
|
|
memcpy(msgstr + length, ptr, ptrlen + 1);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Copy the new portion to the end of the msgid string - safe
|
|
* to use strcpy because the buffer is allocated to the correct
|
|
* size...
|
|
*/
|
|
|
|
msgid = temp;
|
|
|
|
memcpy(msgid + length, ptr, ptrlen + 1);
|
|
}
|
|
}
|
|
else if (!strncmp(s, "msgstr", 6) && msgid)
|
|
{
|
|
/*
|
|
* Set the string...
|
|
*/
|
|
|
|
if (msgstr)
|
|
free(msgstr);
|
|
|
|
if ((msgstr = strdup(ptr)) == NULL)
|
|
{
|
|
free(msgid);
|
|
perror("Unable to allocate msgstr");
|
|
return (1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (msgid && msgstr)
|
|
{
|
|
if (*msgid)
|
|
cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
|
|
}
|
|
|
|
if (msgid)
|
|
free(msgid);
|
|
|
|
if (msgstr)
|
|
free(msgstr);
|
|
|
|
cupsFileClose(po);
|
|
cupsFileClose(strings);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'normalize_string()' - Normalize a msgid string.
|
|
*
|
|
* This function converts ASCII ellipsis and double quotes to their Unicode
|
|
* counterparts.
|
|
*/
|
|
|
|
static char * /* O - Normalized string */
|
|
normalize_string(const char *idstr, /* I - msgid string */
|
|
char *buffer, /* I - Normalized string buffer */
|
|
size_t bufsize) /* I - Size of string buffer */
|
|
{
|
|
char *bufptr = buffer, /* Pointer into buffer */
|
|
*bufend = buffer + bufsize - 3; /* End of buffer */
|
|
int quote = 0, /* Quote direction */
|
|
html = 0; /* HTML text */
|
|
|
|
|
|
while (*idstr && bufptr < bufend)
|
|
{
|
|
if (!strncmp(idstr, "<A ", 3))
|
|
html = 1;
|
|
else if (html && *idstr == '>')
|
|
html = 0;
|
|
|
|
if (*idstr == '.' && idstr[1] == '.' && idstr[2] == '.')
|
|
{
|
|
/*
|
|
* Convert ... to Unicode ellipsis...
|
|
*/
|
|
|
|
*bufptr++ = (char)0xE2;
|
|
*bufptr++ = (char)0x80;
|
|
*bufptr++ = (char)0xA6;
|
|
idstr += 2;
|
|
}
|
|
else if (!html && *idstr == '\\' && idstr[1] == '\"')
|
|
{
|
|
if (quote)
|
|
{
|
|
/*
|
|
* Convert second \" to Unicode right (curley) double quote.
|
|
*/
|
|
|
|
*bufptr++ = (char)0xE2;
|
|
*bufptr++ = (char)0x80;
|
|
*bufptr++ = (char)0x9D;
|
|
quote = 0;
|
|
}
|
|
else if (strchr(idstr + 2, '\"') != NULL)
|
|
{
|
|
/*
|
|
* Convert first \" to Unicode left (curley) double quote.
|
|
*/
|
|
|
|
*bufptr++ = (char)0xE2;
|
|
*bufptr++ = (char)0x80;
|
|
*bufptr++ = (char)0x9C;
|
|
quote = 1;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Convert lone \" to Unicode double prime.
|
|
*/
|
|
|
|
*bufptr++ = (char)0xE2;
|
|
*bufptr++ = (char)0x80;
|
|
*bufptr++ = (char)0xB3;
|
|
}
|
|
|
|
idstr ++;
|
|
}
|
|
else if (*idstr == '\'')
|
|
{
|
|
if (strchr(idstr + 1, '\'') == NULL || quote)
|
|
{
|
|
/*
|
|
* Convert second ' (or ' used for a contraction) to Unicode right
|
|
* (curley) single quote.
|
|
*/
|
|
|
|
*bufptr++ = (char)0xE2;
|
|
*bufptr++ = (char)0x80;
|
|
*bufptr++ = (char)0x99;
|
|
quote = 0;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Convert first ' to Unicode left (curley) single quote.
|
|
*/
|
|
|
|
*bufptr++ = (char)0xE2;
|
|
*bufptr++ = (char)0x80;
|
|
*bufptr++ = (char)0x98;
|
|
quote = 1;
|
|
}
|
|
}
|
|
else
|
|
*bufptr++ = *idstr;
|
|
|
|
idstr ++;
|
|
}
|
|
|
|
*bufptr = '\0';
|
|
|
|
return (buffer);
|
|
}
|