]>
git.ipfire.org Git - thirdparty/cups.git/blob - ppdc/ppdc-catalog.cxx
4 // Shared message catalog class for the CUPS PPD Compiler.
6 // Copyright 2007-2009 by Apple Inc.
7 // Copyright 2002-2006 by Easy Software Products.
9 // These coded instructions, statements, and computer programs are the
10 // property of Apple Inc. and are protected by Federal copyright
11 // law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 // which should have been included with this file. If this file is
13 // file is missing or damaged, see the license at "http://www.cups.org/".
17 // ppdcCatalog::ppdcCatalog() - Create a shared message catalog.
18 // ppdcCatalog::~ppdcCatalog() - Destroy a shared message catalog.
19 // ppdcCatalog::add_message() - Add a new message.
20 // ppdcCatalog::find_message() - Find a message in a catalog...
21 // ppdcCatalog::load_messages() - Load messages from a .po file.
22 // ppdcCatalog::save_messages() - Save the messages to a .po file.
23 // get_utf8() - Get a UTF-8 character.
24 // get_utf16() - Get a UTF-16 character...
25 // put_utf8() - Add a UTF-8 character to a string.
26 // put_utf16() - Write a UTF-16 character to a file.
30 // Include necessary headers...
34 #include <cups/globals.h>
38 // Character encodings...
54 static int get_utf8(char *&ptr
);
55 static int get_utf16(cups_file_t
*fp
, ppdc_cs_t
&cs
);
56 static int put_utf8(int ch
, char *&ptr
, char *end
);
57 static int put_utf16(cups_file_t
*fp
, int ch
);
61 // 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog.
64 ppdcCatalog::ppdcCatalog(const char *l
, // I - Locale
65 const char *f
) // I - Message catalog file
68 _cups_globals_t
*cg
= _cupsGlobals();
74 locale
= new ppdcString(l
);
75 filename
= new ppdcString(f
);
76 messages
= new ppdcArray();
80 // Try loading the base messages for this locale...
81 char pofile
[1024]; // Message catalog file
84 snprintf(pofile
, sizeof(pofile
), "%s/%s/cups_%s.po", cg
->localedir
, l
, l
);
86 if (load_messages(pofile
) && strchr(l
, '_'))
88 // Try the base locale...
89 char baseloc
[3]; // Base locale...
92 strlcpy(baseloc
, l
, sizeof(baseloc
));
93 snprintf(pofile
, sizeof(pofile
), "%s/%s/cups_%s.po", cg
->localedir
,
96 load_messages(pofile
);
106 // 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog.
109 ppdcCatalog::~ppdcCatalog()
120 // 'ppdcCatalog::add_message()' - Add a new message.
124 ppdcCatalog::add_message(
125 const char *id
, // I - Message ID to add
126 const char *string
) // I - Translation string
128 ppdcMessage
*m
; // Current message
129 char text
[1024]; // Text to translate
132 // Range check input...
136 // Verify that we don't already have the message ID...
137 for (m
= (ppdcMessage
*)messages
->first();
139 m
= (ppdcMessage
*)messages
->next())
140 if (!strcmp(m
->id
->value
, id
))
144 m
->string
->release();
145 m
->string
= new ppdcString(string
);
150 // Add the message...
153 snprintf(text
, sizeof(text
), "TRANSLATE %s", id
);
157 messages
->add(new ppdcMessage(id
, string
));
162 // 'ppdcCatalog::find_message()' - Find a message in a catalog...
165 const char * // O - Message text
166 ppdcCatalog::find_message(
167 const char *id
) // I - Message ID
169 ppdcMessage
*m
; // Current message
172 for (m
= (ppdcMessage
*)messages
->first();
174 m
= (ppdcMessage
*)messages
->next())
175 if (!strcmp(m
->id
->value
, id
))
176 return (m
->string
->value
);
183 // 'ppdcCatalog::load_messages()' - Load messages from a .po file.
186 int // O - 0 on success, -1 on failure
187 ppdcCatalog::load_messages(
188 const char *f
) // I - Message catalog file
190 cups_file_t
*fp
; // Message file
191 char line
[4096], // Line buffer
192 *ptr
, // Pointer into buffer
193 id
[4096], // Translation ID
194 str
[4096]; // Translation string
195 int linenum
; // Line number
198 // Open the message catalog file...
199 if ((fp
= cupsFileOpen(f
, "r")) == NULL
)
202 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
203 goto unknown_load_format
;
204 else if (!strcmp(ptr
, ".strings"))
207 * Read messages in Mac OS X ".strings" format, which are UTF-16 text
208 * files of the format:
212 * Strings files can also contain C-style comments.
215 ppdc_cs_t cs
= PPDC_CS_AUTO
; // Character set for file
216 int ch
; // Current character from file
217 char *end
; // End of buffer
225 while ((ch
= get_utf16(fp
, cs
)) != 0)
231 if ((ch
= get_utf16(fp
, cs
)) == 0)
246 put_utf8(ch
, ptr
, end
);
250 // Start of a comment?
251 if ((ch
= get_utf16(fp
, cs
)) == 0)
259 while ((ch
= get_utf16(fp
, cs
)) != 0)
261 if (ch
== '/' && lastch
== '*')
269 // Skip C++ comment...
270 while ((ch
= get_utf16(fp
, cs
)) != 0)
277 // Start quoted string...
281 end
= str
+ sizeof(str
) - 1;
286 end
= id
+ sizeof(id
) - 1;
292 add_message(id
, str
);
297 else if (!strcmp(ptr
, ".po") || !strcmp(ptr
, ".gz"))
300 * Read messages from the catalog file until EOF...
302 * The format is the GNU gettext .po format, which is fairly simple:
305 * msgstr "localized text"
307 * The ID and localized text can span multiple lines using the form:
312 * "localized text spanning "
316 int which
, // In msgid?
317 haveid
, // Did we get a msgid string?
318 havestr
; // Did we get a msgstr string?
327 while (cupsFileGets(fp
, line
, sizeof(line
)))
331 // Skip blank and comment lines...
332 if (line
[0] == '#' || !line
[0])
335 // Strip the trailing quote...
336 if ((ptr
= (char *)strrchr(line
, '\"')) == NULL
)
338 _cupsLangPrintf(stderr
,
339 _("ERROR: Expected quoted string on line %d of %s!\n"),
347 // Find start of value...
348 if ((ptr
= strchr(line
, '\"')) == NULL
)
350 _cupsLangPrintf(stderr
,
351 _("ERROR: Expected quoted string on line %d of %s!\n"),
359 // Unquote the text...
360 char *sptr
, *dptr
; // Source/destination pointers
362 for (sptr
= ptr
, dptr
= ptr
; *sptr
;)
371 while (isdigit(*sptr
))
373 *dptr
= *dptr
* 8 + *sptr
- '0';
383 else if (*sptr
== 'r')
385 else if (*sptr
== 't')
399 // Create or add to a message...
400 if (!strncmp(line
, "msgid", 5))
402 if (haveid
&& havestr
)
403 add_message(id
, str
);
405 strlcpy(id
, ptr
, sizeof(id
));
411 else if (!strncmp(line
, "msgstr", 6))
415 _cupsLangPrintf(stderr
,
416 _("ERROR: Need a msgid line before any "
417 "translation strings on line %d of %s!\n"),
423 strlcpy(str
, ptr
, sizeof(str
));
427 else if (line
[0] == '\"' && which
== 2)
428 strlcat(str
, ptr
, sizeof(str
));
429 else if (line
[0] == '\"' && which
== 1)
430 strlcat(id
, ptr
, sizeof(id
));
433 _cupsLangPrintf(stderr
, _("ERROR: Unexpected text on line %d of %s!\n"),
440 if (haveid
&& havestr
)
441 add_message(id
, str
);
444 goto unknown_load_format
;
447 * Close the file and return...
455 * Unknown format error...
460 _cupsLangPrintf(stderr
,
461 _("ERROR: Unknown message catalog format for \"%s\"!\n"), f
);
468 // 'ppdcCatalog::save_messages()' - Save the messages to a .po file.
471 int // O - 0 on success, -1 on error
472 ppdcCatalog::save_messages(
473 const char *f
) // I - File to save to
475 cups_file_t
*fp
; // Message file
476 ppdcMessage
*m
; // Current message
477 char *ptr
; // Pointer into string
478 int utf16
; // Output UTF-16 .strings file?
479 int ch
; // Current character
483 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
486 if (!strcmp(ptr
, ".gz"))
487 fp
= cupsFileOpen(f
, "w9");
489 fp
= cupsFileOpen(f
, "w");
494 // For .strings files, write a BOM for big-endian output...
495 utf16
= !strcmp(ptr
, ".strings");
498 put_utf16(fp
, 0xfeff);
500 // Loop through all of the messages...
501 for (m
= (ppdcMessage
*)messages
->first();
503 m
= (ppdcMessage
*)messages
->next())
510 while ((ch
= get_utf8(ptr
)) != 0)
536 ptr
= m
->string
->value
;
537 while ((ch
= get_utf8(ptr
)) != 0)
563 cupsFilePuts(fp
, "msgid \"");
564 for (ptr
= m
->id
->value
; *ptr
; ptr
++)
568 cupsFilePuts(fp
, "\\n");
571 cupsFilePuts(fp
, "\\\\");
574 cupsFilePuts(fp
, "\\\"");
577 cupsFilePutChar(fp
, *ptr
);
580 cupsFilePuts(fp
, "\"\n");
582 cupsFilePuts(fp
, "msgstr \"");
583 for (ptr
= m
->string
->value
; *ptr
; ptr
++)
587 cupsFilePuts(fp
, "\\n");
590 cupsFilePuts(fp
, "\\\\");
593 cupsFilePuts(fp
, "\\\"");
596 cupsFilePutChar(fp
, *ptr
);
599 cupsFilePuts(fp
, "\"\n");
601 cupsFilePutChar(fp
, '\n');
612 // 'get_utf8()' - Get a UTF-8 character.
615 static int // O - Unicode character or 0 on EOF
616 get_utf8(char *&ptr
) // IO - Pointer to character
618 int ch
; // Current character
621 if ((ch
= *ptr
++ & 255) < 0xc0)
624 if ((ch
& 0xe0) == 0xc0)
627 if ((*ptr
& 0xc0) != 0x80)
630 ch
= ((ch
& 0x1f) << 6) | (*ptr
++ & 0x3f);
632 else if ((ch
& 0xf0) == 0xe0)
634 // Three-byte UTF-8...
635 if ((*ptr
& 0xc0) != 0x80)
638 ch
= ((ch
& 0x0f) << 6) | (*ptr
++ & 0x3f);
640 if ((*ptr
& 0xc0) != 0x80)
643 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
645 else if ((ch
& 0xf8) == 0xf0)
647 // Four-byte UTF-8...
648 if ((*ptr
& 0xc0) != 0x80)
651 ch
= ((ch
& 0x07) << 6) | (*ptr
++ & 0x3f);
653 if ((*ptr
& 0xc0) != 0x80)
656 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
658 if ((*ptr
& 0xc0) != 0x80)
661 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
669 // 'get_utf16()' - Get a UTF-16 character...
672 static int // O - Unicode character or 0 on EOF
673 get_utf16(cups_file_t
*fp
, // I - File to read from
674 ppdc_cs_t
&cs
) // IO - Character set of file
676 int ch
; // Current character
677 unsigned char buffer
[3]; // Bytes
680 if (cs
== PPDC_CS_AUTO
)
682 // Get byte-order-mark, if present...
683 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
686 if (buffer
[0] == 0xfe && buffer
[1] == 0xff)
688 // Big-endian UTF-16...
689 cs
= PPDC_CS_UTF16BE
;
691 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
694 else if (buffer
[0] == 0xff && buffer
[1] == 0xfe)
696 // Little-endian UTF-16...
697 cs
= PPDC_CS_UTF16LE
;
699 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
702 else if (buffer
[0] == 0x00 && buffer
[1] != 0x00)
704 // No BOM, assume big-endian UTF-16...
705 cs
= PPDC_CS_UTF16BE
;
707 else if (buffer
[0] != 0x00 && buffer
[1] == 0x00)
709 // No BOM, assume little-endian UTF-16...
710 cs
= PPDC_CS_UTF16LE
;
714 // No BOM, assume UTF-8...
720 else if (cs
!= PPDC_CS_UTF8
)
722 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
726 if (cs
== PPDC_CS_UTF8
)
728 // UTF-8 character...
729 if ((ch
= cupsFileGetChar(fp
)) < 0)
732 if ((ch
& 0xe0) == 0xc0)
735 if (cupsFileRead(fp
, (char *)buffer
, 1) != 1)
738 if ((buffer
[0] & 0xc0) != 0x80)
741 ch
= ((ch
& 0x1f) << 6) | (buffer
[0] & 0x3f);
743 else if ((ch
& 0xf0) == 0xe0)
745 // Three-byte UTF-8...
746 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
749 if ((buffer
[0] & 0xc0) != 0x80 ||
750 (buffer
[1] & 0xc0) != 0x80)
753 ch
= ((((ch
& 0x0f) << 6) | (buffer
[0] & 0x3f)) << 6) |
756 else if ((ch
& 0xf8) == 0xf0)
758 // Four-byte UTF-8...
759 if (cupsFileRead(fp
, (char *)buffer
, 3) != 3)
762 if ((buffer
[0] & 0xc0) != 0x80 ||
763 (buffer
[1] & 0xc0) != 0x80 ||
764 (buffer
[2] & 0xc0) != 0x80)
767 ch
= ((((((ch
& 0x07) << 6) | (buffer
[0] & 0x3f)) << 6) |
768 (buffer
[1] & 0x3f)) << 6) | (buffer
[2] & 0x3f);
773 // UTF-16 character...
774 if (cs
== PPDC_CS_UTF16BE
)
775 ch
= (buffer
[0] << 8) | buffer
[1];
777 ch
= (buffer
[1] << 8) | buffer
[0];
779 if (ch
>= 0xd800 && ch
<= 0xdbff)
781 // Handle multi-word encoding...
784 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
787 if (cs
== PPDC_CS_UTF16BE
)
788 lch
= (buffer
[0] << 8) | buffer
[1];
790 lch
= (buffer
[1] << 8) | buffer
[0];
792 if (lch
< 0xdc00 || lch
>= 0xdfff)
795 ch
= (((ch
& 0x3ff) << 10) | (lch
& 0x3ff)) + 0x10000;
804 // 'put_utf8()' - Add a UTF-8 character to a string.
807 static int // O - 0 on success, -1 on failure
808 put_utf8(int ch
, // I - Unicode character
809 char *&ptr
, // IO - String pointer
810 char *end
) // I - End of buffer
823 if ((ptr
+ 1) >= end
)
826 *ptr
++ = 0xc0 | (ch
>> 6);
827 *ptr
++ = 0x80 | (ch
& 0x3f);
829 else if (ch
< 0x10000)
831 // Three-byte UTF-8...
832 if ((ptr
+ 2) >= end
)
835 *ptr
++ = 0xe0 | (ch
>> 12);
836 *ptr
++ = 0x80 | ((ch
>> 6) & 0x3f);
837 *ptr
++ = 0x80 | (ch
& 0x3f);
841 // Four-byte UTF-8...
842 if ((ptr
+ 3) >= end
)
845 *ptr
++ = 0xf0 | (ch
>> 18);
846 *ptr
++ = 0x80 | ((ch
>> 12) & 0x3f);
847 *ptr
++ = 0x80 | ((ch
>> 6) & 0x3f);
848 *ptr
++ = 0x80 | (ch
& 0x3f);
856 // 'put_utf16()' - Write a UTF-16 character to a file.
859 static int // O - 0 on success, -1 on failure
860 put_utf16(cups_file_t
*fp
, // I - File to write to
861 int ch
) // I - Unicode character
863 unsigned char buffer
[4]; // Output buffer
868 // One-word UTF-16 big-endian...
872 if (cupsFileWrite(fp
, (char *)buffer
, 2) == 2)
877 // Two-word UTF-16 big-endian...
880 buffer
[0] = 0xd8 | (ch
>> 18);
881 buffer
[1] = ch
>> 10;
882 buffer
[2] = 0xdc | ((ch
>> 8) & 0x03);
885 if (cupsFileWrite(fp
, (char *)buffer
, 4) == 4)