#include "ppdc-private.h"
typedef enum
{
PPDC_CS_AUTO,
PPDC_CS_UTF8,
PPDC_CS_UTF16BE,
PPDC_CS_UTF16LE
} ppdc_cs_t;
static int get_utf8(char *&ptr);
static int get_utf16(cups_file_t *fp, ppdc_cs_t &cs);
static int put_utf8(int ch, char *&ptr, char *end);
static int put_utf16(cups_file_t *fp, int ch);
ppdcCatalog::ppdcCatalog(const char *l, const char *f) : ppdcShared()
{
_cups_globals_t *cg = _cupsGlobals();
PPDC_NEW;
locale = new ppdcString(l);
filename = new ppdcString(f);
messages = new ppdcArray();
if (l)
{
char pofile[1024];
snprintf(pofile, sizeof(pofile), "%s/%s/cups_%s.po", cg->localedir, l, l);
if (load_messages(pofile) && strchr(l, '_'))
{
char baseloc[3];
strlcpy(baseloc, l, sizeof(baseloc));
snprintf(pofile, sizeof(pofile), "%s/%s/cups_%s.po", cg->localedir,
baseloc, baseloc);
load_messages(pofile);
}
}
if (f)
load_messages(f);
}
ppdcCatalog::~ppdcCatalog()
{
PPDC_DELETE;
locale->release();
filename->release();
messages->release();
}
void
ppdcCatalog::add_message(
const char *id, const char *string) {
ppdcMessage *m; char text[1024];
if (!id)
return;
for (m = (ppdcMessage *)messages->first();
m;
m = (ppdcMessage *)messages->next())
if (!strcmp(m->id->value, id))
{
if (string)
{
m->string->release();
m->string = new ppdcString(string);
}
return;
}
if (!string)
{
snprintf(text, sizeof(text), "TRANSLATE %s", id);
string = text;
}
messages->add(new ppdcMessage(id, string));
}
const char * ppdcCatalog::find_message(
const char *id) {
ppdcMessage *m;
if (!*id)
return (id);
for (m = (ppdcMessage *)messages->first();
m;
m = (ppdcMessage *)messages->next())
if (!strcmp(m->id->value, id))
return (m->string->value);
return (id);
}
int ppdcCatalog::load_messages(
const char *f) {
cups_file_t *fp; char line[4096], *ptr, id[4096], str[4096]; int linenum;
if ((fp = cupsFileOpen(f, "r")) == NULL)
return (-1);
if ((ptr = (char *)strrchr(f, '.')) == NULL)
goto unknown_load_format;
else if (!strcmp(ptr, ".strings"))
{
ppdc_cs_t cs = PPDC_CS_AUTO; int ch; char *end;
id[0] = '\0';
str[0] = '\0';
ptr = NULL;
end = NULL;
while ((ch = get_utf16(fp, cs)) != 0)
{
if (ptr)
{
if (ch == '\\')
{
if ((ch = get_utf16(fp, cs)) == 0)
break;
if (ch == 'n')
ch = '\n';
else if (ch == 't')
ch = '\t';
}
else if (ch == '\"')
{
*ptr = '\0';
ptr = NULL;
}
if (ptr)
put_utf8(ch, ptr, end);
}
else if (ch == '/')
{
if ((ch = get_utf16(fp, cs)) == 0)
break;
if (ch == '*')
{
int lastch = 0;
while ((ch = get_utf16(fp, cs)) != 0)
{
if (ch == '/' && lastch == '*')
break;
lastch = ch;
}
}
else if (ch == '/')
{
while ((ch = get_utf16(fp, cs)) != 0)
if (ch == '\n')
break;
}
}
else if (ch == '\"')
{
if (id[0])
{
ptr = str;
end = str + sizeof(str) - 1;
}
else
{
ptr = id;
end = id + sizeof(id) - 1;
}
}
else if (ch == ';')
{
add_message(id, str);
id[0] = '\0';
}
}
}
else if (!strcmp(ptr, ".po") || !strcmp(ptr, ".gz"))
{
int which, haveid, havestr;
linenum = 0;
id[0] = '\0';
str[0] = '\0';
haveid = 0;
havestr = 0;
which = 0;
while (cupsFileGets(fp, line, sizeof(line)))
{
linenum ++;
if (line[0] == '#' || !line[0])
continue;
if ((ptr = (char *)strrchr(line, '\"')) == NULL)
{
_cupsLangPrintf(stderr,
_("ppdc: Expected quoted string on line %d of %s."),
linenum, f);
cupsFileClose(fp);
return (-1);
}
*ptr = '\0';
if ((ptr = strchr(line, '\"')) == NULL)
{
_cupsLangPrintf(stderr,
_("ppdc: Expected quoted string on line %d of %s."),
linenum, f);
cupsFileClose(fp);
return (-1);
}
ptr ++;
char *sptr, *dptr;
for (sptr = ptr, dptr = ptr; *sptr;)
{
if (*sptr == '\\')
{
sptr ++;
if (isdigit(*sptr))
{
*dptr = 0;
while (isdigit(*sptr))
{
*dptr = *dptr * 8 + *sptr - '0';
sptr ++;
}
dptr ++;
}
else
{
if (*sptr == 'n')
*dptr++ = '\n';
else if (*sptr == 'r')
*dptr++ = '\r';
else if (*sptr == 't')
*dptr++ = '\t';
else
*dptr++ = *sptr;
sptr ++;
}
}
else
*dptr++ = *sptr++;
}
*dptr = '\0';
if (!strncmp(line, "msgid", 5))
{
if (haveid && havestr)
add_message(id, str);
strlcpy(id, ptr, sizeof(id));
str[0] = '\0';
haveid = 1;
havestr = 0;
which = 1;
}
else if (!strncmp(line, "msgstr", 6))
{
if (!haveid)
{
_cupsLangPrintf(stderr,
_("ppdc: Need a msgid line before any "
"translation strings on line %d of %s."),
linenum, f);
cupsFileClose(fp);
return (-1);
}
strlcpy(str, ptr, sizeof(str));
havestr = 1;
which = 2;
}
else if (line[0] == '\"' && which == 2)
strlcat(str, ptr, sizeof(str));
else if (line[0] == '\"' && which == 1)
strlcat(id, ptr, sizeof(id));
else
{
_cupsLangPrintf(stderr, _("ppdc: Unexpected text on line %d of %s."),
linenum, f);
cupsFileClose(fp);
return (-1);
}
}
if (haveid && havestr)
add_message(id, str);
}
else
goto unknown_load_format;
cupsFileClose(fp);
return (0);
unknown_load_format:
_cupsLangPrintf(stderr,
_("ppdc: Unknown message catalog format for \"%s\"."), f);
cupsFileClose(fp);
return (-1);
}
int ppdcCatalog::save_messages(
const char *f) {
cups_file_t *fp; ppdcMessage *m; char *ptr; int utf16; int ch;
if ((ptr = (char *)strrchr(f, '.')) == NULL)
return (-1);
if (!strcmp(ptr, ".gz"))
fp = cupsFileOpen(f, "w9");
else
fp = cupsFileOpen(f, "w");
if (!fp)
return (-1);
utf16 = !strcmp(ptr, ".strings");
if (utf16)
put_utf16(fp, 0xfeff);
for (m = (ppdcMessage *)messages->first();
m;
m = (ppdcMessage *)messages->next())
{
if (utf16)
{
put_utf16(fp, '\"');
ptr = m->id->value;
while ((ch = get_utf8(ptr)) != 0)
switch (ch)
{
case '\n' :
put_utf16(fp, '\\');
put_utf16(fp, 'n');
break;
case '\\' :
put_utf16(fp, '\\');
put_utf16(fp, '\\');
break;
case '\"' :
put_utf16(fp, '\\');
put_utf16(fp, '\"');
break;
default :
put_utf16(fp, ch);
break;
}
put_utf16(fp, '\"');
put_utf16(fp, ' ');
put_utf16(fp, '=');
put_utf16(fp, ' ');
put_utf16(fp, '\"');
ptr = m->string->value;
while ((ch = get_utf8(ptr)) != 0)
switch (ch)
{
case '\n' :
put_utf16(fp, '\\');
put_utf16(fp, 'n');
break;
case '\\' :
put_utf16(fp, '\\');
put_utf16(fp, '\\');
break;
case '\"' :
put_utf16(fp, '\\');
put_utf16(fp, '\"');
break;
default :
put_utf16(fp, ch);
break;
}
put_utf16(fp, '\"');
put_utf16(fp, ';');
put_utf16(fp, '\n');
}
else
{
cupsFilePuts(fp, "msgid \"");
for (ptr = m->id->value; *ptr; ptr ++)
switch (*ptr)
{
case '\n' :
cupsFilePuts(fp, "\\n");
break;
case '\\' :
cupsFilePuts(fp, "\\\\");
break;
case '\"' :
cupsFilePuts(fp, "\\\"");
break;
default :
cupsFilePutChar(fp, *ptr);
break;
}
cupsFilePuts(fp, "\"\n");
cupsFilePuts(fp, "msgstr \"");
for (ptr = m->string->value; *ptr; ptr ++)
switch (*ptr)
{
case '\n' :
cupsFilePuts(fp, "\\n");
break;
case '\\' :
cupsFilePuts(fp, "\\\\");
break;
case '\"' :
cupsFilePuts(fp, "\\\"");
break;
default :
cupsFilePutChar(fp, *ptr);
break;
}
cupsFilePuts(fp, "\"\n");
cupsFilePutChar(fp, '\n');
}
}
cupsFileClose(fp);
return (0);
}
static int get_utf8(char *&ptr) {
int ch;
if ((ch = *ptr++ & 255) < 0xc0)
return (ch);
if ((ch & 0xe0) == 0xc0)
{
if ((*ptr & 0xc0) != 0x80)
return (0);
ch = ((ch & 0x1f) << 6) | (*ptr++ & 0x3f);
}
else if ((ch & 0xf0) == 0xe0)
{
if ((*ptr & 0xc0) != 0x80)
return (0);
ch = ((ch & 0x0f) << 6) | (*ptr++ & 0x3f);
if ((*ptr & 0xc0) != 0x80)
return (0);
ch = (ch << 6) | (*ptr++ & 0x3f);
}
else if ((ch & 0xf8) == 0xf0)
{
if ((*ptr & 0xc0) != 0x80)
return (0);
ch = ((ch & 0x07) << 6) | (*ptr++ & 0x3f);
if ((*ptr & 0xc0) != 0x80)
return (0);
ch = (ch << 6) | (*ptr++ & 0x3f);
if ((*ptr & 0xc0) != 0x80)
return (0);
ch = (ch << 6) | (*ptr++ & 0x3f);
}
return (ch);
}
static int get_utf16(cups_file_t *fp, ppdc_cs_t &cs) {
int ch; unsigned char buffer[3];
if (cs == PPDC_CS_AUTO)
{
if (cupsFileRead(fp, (char *)buffer, 2) != 2)
return (0);
if (buffer[0] == 0xfe && buffer[1] == 0xff)
{
cs = PPDC_CS_UTF16BE;
if (cupsFileRead(fp, (char *)buffer, 2) != 2)
return (0);
}
else if (buffer[0] == 0xff && buffer[1] == 0xfe)
{
cs = PPDC_CS_UTF16LE;
if (cupsFileRead(fp, (char *)buffer, 2) != 2)
return (0);
}
else if (buffer[0] == 0x00 && buffer[1] != 0x00)
{
cs = PPDC_CS_UTF16BE;
}
else if (buffer[0] != 0x00 && buffer[1] == 0x00)
{
cs = PPDC_CS_UTF16LE;
}
else
{
cs = PPDC_CS_UTF8;
cupsFileRewind(fp);
}
}
else if (cs != PPDC_CS_UTF8)
{
if (cupsFileRead(fp, (char *)buffer, 2) != 2)
return (0);
}
if (cs == PPDC_CS_UTF8)
{
if ((ch = cupsFileGetChar(fp)) < 0)
return (0);
if ((ch & 0xe0) == 0xc0)
{
if (cupsFileRead(fp, (char *)buffer, 1) != 1)
return (0);
if ((buffer[0] & 0xc0) != 0x80)
return (0);
ch = ((ch & 0x1f) << 6) | (buffer[0] & 0x3f);
}
else if ((ch & 0xf0) == 0xe0)
{
if (cupsFileRead(fp, (char *)buffer, 2) != 2)
return (0);
if ((buffer[0] & 0xc0) != 0x80 ||
(buffer[1] & 0xc0) != 0x80)
return (0);
ch = ((((ch & 0x0f) << 6) | (buffer[0] & 0x3f)) << 6) |
(buffer[1] & 0x3f);
}
else if ((ch & 0xf8) == 0xf0)
{
if (cupsFileRead(fp, (char *)buffer, 3) != 3)
return (0);
if ((buffer[0] & 0xc0) != 0x80 ||
(buffer[1] & 0xc0) != 0x80 ||
(buffer[2] & 0xc0) != 0x80)
return (0);
ch = ((((((ch & 0x07) << 6) | (buffer[0] & 0x3f)) << 6) |
(buffer[1] & 0x3f)) << 6) | (buffer[2] & 0x3f);
}
}
else
{
if (cs == PPDC_CS_UTF16BE)
ch = (buffer[0] << 8) | buffer[1];
else
ch = (buffer[1] << 8) | buffer[0];
if (ch >= 0xd800 && ch <= 0xdbff)
{
int lch;
if (cupsFileRead(fp, (char *)buffer, 2) != 2)
return (0);
if (cs == PPDC_CS_UTF16BE)
lch = (buffer[0] << 8) | buffer[1];
else
lch = (buffer[1] << 8) | buffer[0];
if (lch < 0xdc00 || lch >= 0xdfff)
return (0);
ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
}
}
return (ch);
}
static int put_utf8(int ch, char *&ptr, char *end) {
if (ch < 0x80)
{
if (ptr >= end)
return (-1);
*ptr++ = ch;
}
else if (ch < 0x800)
{
if ((ptr + 1) >= end)
return (-1);
*ptr++ = 0xc0 | (ch >> 6);
*ptr++ = 0x80 | (ch & 0x3f);
}
else if (ch < 0x10000)
{
if ((ptr + 2) >= end)
return (-1);
*ptr++ = 0xe0 | (ch >> 12);
*ptr++ = 0x80 | ((ch >> 6) & 0x3f);
*ptr++ = 0x80 | (ch & 0x3f);
}
else
{
if ((ptr + 3) >= end)
return (-1);
*ptr++ = 0xf0 | (ch >> 18);
*ptr++ = 0x80 | ((ch >> 12) & 0x3f);
*ptr++ = 0x80 | ((ch >> 6) & 0x3f);
*ptr++ = 0x80 | (ch & 0x3f);
}
return (0);
}
static int put_utf16(cups_file_t *fp, int ch) {
unsigned char buffer[4];
if (ch < 0x10000)
{
buffer[0] = ch >> 8;
buffer[1] = ch;
if (cupsFileWrite(fp, (char *)buffer, 2) == 2)
return (0);
}
else
{
ch -= 0x10000;
buffer[0] = 0xd8 | (ch >> 18);
buffer[1] = ch >> 10;
buffer[2] = 0xdc | ((ch >> 8) & 0x03);
buffer[3] = ch;
if (cupsFileWrite(fp, (char *)buffer, 4) == 4)
return (0);
}
return (-1);
}