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