]>
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...
33 #include "ppdc-private.h"
37 // Character encodings...
53 static int get_utf8(char *&ptr
);
54 static int get_utf16(cups_file_t
*fp
, ppdc_cs_t
&cs
);
55 static int put_utf8(int ch
, char *&ptr
, char *end
);
56 static int put_utf16(cups_file_t
*fp
, int ch
);
60 // 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog.
63 ppdcCatalog::ppdcCatalog(const char *l
, // I - Locale
64 const char *f
) // I - Message catalog file
67 _cups_globals_t
*cg
= _cupsGlobals();
73 locale
= new ppdcString(l
);
74 filename
= new ppdcString(f
);
75 messages
= new ppdcArray();
79 // Try loading the base messages for this locale...
80 char pofile
[1024]; // Message catalog file
83 snprintf(pofile
, sizeof(pofile
), "%s/%s/cups_%s.po", cg
->localedir
, l
, l
);
85 if (load_messages(pofile
) && strchr(l
, '_'))
87 // Try the base locale...
88 char baseloc
[3]; // Base locale...
91 strlcpy(baseloc
, l
, sizeof(baseloc
));
92 snprintf(pofile
, sizeof(pofile
), "%s/%s/cups_%s.po", cg
->localedir
,
95 load_messages(pofile
);
105 // 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog.
108 ppdcCatalog::~ppdcCatalog()
119 // 'ppdcCatalog::add_message()' - Add a new message.
123 ppdcCatalog::add_message(
124 const char *id
, // I - Message ID to add
125 const char *string
) // I - Translation string
127 ppdcMessage
*m
; // Current message
128 char text
[1024]; // Text to translate
131 // Range check input...
135 // Verify that we don't already have the message ID...
136 for (m
= (ppdcMessage
*)messages
->first();
138 m
= (ppdcMessage
*)messages
->next())
139 if (!strcmp(m
->id
->value
, id
))
143 m
->string
->release();
144 m
->string
= new ppdcString(string
);
149 // Add the message...
152 snprintf(text
, sizeof(text
), "TRANSLATE %s", id
);
156 messages
->add(new ppdcMessage(id
, string
));
161 // 'ppdcCatalog::find_message()' - Find a message in a catalog...
164 const char * // O - Message text
165 ppdcCatalog::find_message(
166 const char *id
) // I - Message ID
168 ppdcMessage
*m
; // Current message
171 for (m
= (ppdcMessage
*)messages
->first();
173 m
= (ppdcMessage
*)messages
->next())
174 if (!strcmp(m
->id
->value
, id
))
175 return (m
->string
->value
);
182 // 'ppdcCatalog::load_messages()' - Load messages from a .po file.
185 int // O - 0 on success, -1 on failure
186 ppdcCatalog::load_messages(
187 const char *f
) // I - Message catalog file
189 cups_file_t
*fp
; // Message file
190 char line
[4096], // Line buffer
191 *ptr
, // Pointer into buffer
192 id
[4096], // Translation ID
193 str
[4096]; // Translation string
194 int linenum
; // Line number
197 // Open the message catalog file...
198 if ((fp
= cupsFileOpen(f
, "r")) == NULL
)
201 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
202 goto unknown_load_format
;
203 else if (!strcmp(ptr
, ".strings"))
206 * Read messages in Mac OS X ".strings" format, which are UTF-16 text
207 * files of the format:
211 * Strings files can also contain C-style comments.
214 ppdc_cs_t cs
= PPDC_CS_AUTO
; // Character set for file
215 int ch
; // Current character from file
216 char *end
; // End of buffer
224 while ((ch
= get_utf16(fp
, cs
)) != 0)
230 if ((ch
= get_utf16(fp
, cs
)) == 0)
245 put_utf8(ch
, ptr
, end
);
249 // Start of a comment?
250 if ((ch
= get_utf16(fp
, cs
)) == 0)
258 while ((ch
= get_utf16(fp
, cs
)) != 0)
260 if (ch
== '/' && lastch
== '*')
268 // Skip C++ comment...
269 while ((ch
= get_utf16(fp
, cs
)) != 0)
276 // Start quoted string...
280 end
= str
+ sizeof(str
) - 1;
285 end
= id
+ sizeof(id
) - 1;
291 add_message(id
, str
);
296 else if (!strcmp(ptr
, ".po") || !strcmp(ptr
, ".gz"))
299 * Read messages from the catalog file until EOF...
301 * The format is the GNU gettext .po format, which is fairly simple:
304 * msgstr "localized text"
306 * The ID and localized text can span multiple lines using the form:
311 * "localized text spanning "
315 int which
, // In msgid?
316 haveid
, // Did we get a msgid string?
317 havestr
; // Did we get a msgstr string?
326 while (cupsFileGets(fp
, line
, sizeof(line
)))
330 // Skip blank and comment lines...
331 if (line
[0] == '#' || !line
[0])
334 // Strip the trailing quote...
335 if ((ptr
= (char *)strrchr(line
, '\"')) == NULL
)
337 _cupsLangPrintf(stderr
,
338 _("ERROR: Expected quoted string on line %d of %s\n"),
346 // Find start of value...
347 if ((ptr
= strchr(line
, '\"')) == NULL
)
349 _cupsLangPrintf(stderr
,
350 _("ERROR: Expected quoted string on line %d of %s\n"),
358 // Unquote the text...
359 char *sptr
, *dptr
; // Source/destination pointers
361 for (sptr
= ptr
, dptr
= ptr
; *sptr
;)
370 while (isdigit(*sptr
))
372 *dptr
= *dptr
* 8 + *sptr
- '0';
382 else if (*sptr
== 'r')
384 else if (*sptr
== 't')
398 // Create or add to a message...
399 if (!strncmp(line
, "msgid", 5))
401 if (haveid
&& havestr
)
402 add_message(id
, str
);
404 strlcpy(id
, ptr
, sizeof(id
));
410 else if (!strncmp(line
, "msgstr", 6))
414 _cupsLangPrintf(stderr
,
415 _("ERROR: Need a msgid line before any "
416 "translation strings on line %d of %s\n"),
422 strlcpy(str
, ptr
, sizeof(str
));
426 else if (line
[0] == '\"' && which
== 2)
427 strlcat(str
, ptr
, sizeof(str
));
428 else if (line
[0] == '\"' && which
== 1)
429 strlcat(id
, ptr
, sizeof(id
));
432 _cupsLangPrintf(stderr
, _("ERROR: Unexpected text on line %d of %s\n"),
439 if (haveid
&& havestr
)
440 add_message(id
, str
);
443 goto unknown_load_format
;
446 * Close the file and return...
454 * Unknown format error...
459 _cupsLangPrintf(stderr
,
460 _("ERROR: Unknown message catalog format for \"%s\"\n"), f
);
467 // 'ppdcCatalog::save_messages()' - Save the messages to a .po file.
470 int // O - 0 on success, -1 on error
471 ppdcCatalog::save_messages(
472 const char *f
) // I - File to save to
474 cups_file_t
*fp
; // Message file
475 ppdcMessage
*m
; // Current message
476 char *ptr
; // Pointer into string
477 int utf16
; // Output UTF-16 .strings file?
478 int ch
; // Current character
482 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
485 if (!strcmp(ptr
, ".gz"))
486 fp
= cupsFileOpen(f
, "w9");
488 fp
= cupsFileOpen(f
, "w");
493 // For .strings files, write a BOM for big-endian output...
494 utf16
= !strcmp(ptr
, ".strings");
497 put_utf16(fp
, 0xfeff);
499 // Loop through all of the messages...
500 for (m
= (ppdcMessage
*)messages
->first();
502 m
= (ppdcMessage
*)messages
->next())
509 while ((ch
= get_utf8(ptr
)) != 0)
535 ptr
= m
->string
->value
;
536 while ((ch
= get_utf8(ptr
)) != 0)
562 cupsFilePuts(fp
, "msgid \"");
563 for (ptr
= m
->id
->value
; *ptr
; ptr
++)
567 cupsFilePuts(fp
, "\\n");
570 cupsFilePuts(fp
, "\\\\");
573 cupsFilePuts(fp
, "\\\"");
576 cupsFilePutChar(fp
, *ptr
);
579 cupsFilePuts(fp
, "\"\n");
581 cupsFilePuts(fp
, "msgstr \"");
582 for (ptr
= m
->string
->value
; *ptr
; ptr
++)
586 cupsFilePuts(fp
, "\\n");
589 cupsFilePuts(fp
, "\\\\");
592 cupsFilePuts(fp
, "\\\"");
595 cupsFilePutChar(fp
, *ptr
);
598 cupsFilePuts(fp
, "\"\n");
600 cupsFilePutChar(fp
, '\n');
611 // 'get_utf8()' - Get a UTF-8 character.
614 static int // O - Unicode character or 0 on EOF
615 get_utf8(char *&ptr
) // IO - Pointer to character
617 int ch
; // Current character
620 if ((ch
= *ptr
++ & 255) < 0xc0)
623 if ((ch
& 0xe0) == 0xc0)
626 if ((*ptr
& 0xc0) != 0x80)
629 ch
= ((ch
& 0x1f) << 6) | (*ptr
++ & 0x3f);
631 else if ((ch
& 0xf0) == 0xe0)
633 // Three-byte UTF-8...
634 if ((*ptr
& 0xc0) != 0x80)
637 ch
= ((ch
& 0x0f) << 6) | (*ptr
++ & 0x3f);
639 if ((*ptr
& 0xc0) != 0x80)
642 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
644 else if ((ch
& 0xf8) == 0xf0)
646 // Four-byte UTF-8...
647 if ((*ptr
& 0xc0) != 0x80)
650 ch
= ((ch
& 0x07) << 6) | (*ptr
++ & 0x3f);
652 if ((*ptr
& 0xc0) != 0x80)
655 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
657 if ((*ptr
& 0xc0) != 0x80)
660 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
668 // 'get_utf16()' - Get a UTF-16 character...
671 static int // O - Unicode character or 0 on EOF
672 get_utf16(cups_file_t
*fp
, // I - File to read from
673 ppdc_cs_t
&cs
) // IO - Character set of file
675 int ch
; // Current character
676 unsigned char buffer
[3]; // Bytes
679 if (cs
== PPDC_CS_AUTO
)
681 // Get byte-order-mark, if present...
682 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
685 if (buffer
[0] == 0xfe && buffer
[1] == 0xff)
687 // Big-endian UTF-16...
688 cs
= PPDC_CS_UTF16BE
;
690 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
693 else if (buffer
[0] == 0xff && buffer
[1] == 0xfe)
695 // Little-endian UTF-16...
696 cs
= PPDC_CS_UTF16LE
;
698 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
701 else if (buffer
[0] == 0x00 && buffer
[1] != 0x00)
703 // No BOM, assume big-endian UTF-16...
704 cs
= PPDC_CS_UTF16BE
;
706 else if (buffer
[0] != 0x00 && buffer
[1] == 0x00)
708 // No BOM, assume little-endian UTF-16...
709 cs
= PPDC_CS_UTF16LE
;
713 // No BOM, assume UTF-8...
719 else if (cs
!= PPDC_CS_UTF8
)
721 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
725 if (cs
== PPDC_CS_UTF8
)
727 // UTF-8 character...
728 if ((ch
= cupsFileGetChar(fp
)) < 0)
731 if ((ch
& 0xe0) == 0xc0)
734 if (cupsFileRead(fp
, (char *)buffer
, 1) != 1)
737 if ((buffer
[0] & 0xc0) != 0x80)
740 ch
= ((ch
& 0x1f) << 6) | (buffer
[0] & 0x3f);
742 else if ((ch
& 0xf0) == 0xe0)
744 // Three-byte UTF-8...
745 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
748 if ((buffer
[0] & 0xc0) != 0x80 ||
749 (buffer
[1] & 0xc0) != 0x80)
752 ch
= ((((ch
& 0x0f) << 6) | (buffer
[0] & 0x3f)) << 6) |
755 else if ((ch
& 0xf8) == 0xf0)
757 // Four-byte UTF-8...
758 if (cupsFileRead(fp
, (char *)buffer
, 3) != 3)
761 if ((buffer
[0] & 0xc0) != 0x80 ||
762 (buffer
[1] & 0xc0) != 0x80 ||
763 (buffer
[2] & 0xc0) != 0x80)
766 ch
= ((((((ch
& 0x07) << 6) | (buffer
[0] & 0x3f)) << 6) |
767 (buffer
[1] & 0x3f)) << 6) | (buffer
[2] & 0x3f);
772 // UTF-16 character...
773 if (cs
== PPDC_CS_UTF16BE
)
774 ch
= (buffer
[0] << 8) | buffer
[1];
776 ch
= (buffer
[1] << 8) | buffer
[0];
778 if (ch
>= 0xd800 && ch
<= 0xdbff)
780 // Handle multi-word encoding...
783 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
786 if (cs
== PPDC_CS_UTF16BE
)
787 lch
= (buffer
[0] << 8) | buffer
[1];
789 lch
= (buffer
[1] << 8) | buffer
[0];
791 if (lch
< 0xdc00 || lch
>= 0xdfff)
794 ch
= (((ch
& 0x3ff) << 10) | (lch
& 0x3ff)) + 0x10000;
803 // 'put_utf8()' - Add a UTF-8 character to a string.
806 static int // O - 0 on success, -1 on failure
807 put_utf8(int ch
, // I - Unicode character
808 char *&ptr
, // IO - String pointer
809 char *end
) // I - End of buffer
822 if ((ptr
+ 1) >= end
)
825 *ptr
++ = 0xc0 | (ch
>> 6);
826 *ptr
++ = 0x80 | (ch
& 0x3f);
828 else if (ch
< 0x10000)
830 // Three-byte UTF-8...
831 if ((ptr
+ 2) >= end
)
834 *ptr
++ = 0xe0 | (ch
>> 12);
835 *ptr
++ = 0x80 | ((ch
>> 6) & 0x3f);
836 *ptr
++ = 0x80 | (ch
& 0x3f);
840 // Four-byte UTF-8...
841 if ((ptr
+ 3) >= end
)
844 *ptr
++ = 0xf0 | (ch
>> 18);
845 *ptr
++ = 0x80 | ((ch
>> 12) & 0x3f);
846 *ptr
++ = 0x80 | ((ch
>> 6) & 0x3f);
847 *ptr
++ = 0x80 | (ch
& 0x3f);
855 // 'put_utf16()' - Write a UTF-16 character to a file.
858 static int // O - 0 on success, -1 on failure
859 put_utf16(cups_file_t
*fp
, // I - File to write to
860 int ch
) // I - Unicode character
862 unsigned char buffer
[4]; // Output buffer
867 // One-word UTF-16 big-endian...
871 if (cupsFileWrite(fp
, (char *)buffer
, 2) == 2)
876 // Two-word UTF-16 big-endian...
879 buffer
[0] = 0xd8 | (ch
>> 18);
880 buffer
[1] = ch
>> 10;
881 buffer
[2] = 0xdc | ((ch
>> 8) & 0x03);
884 if (cupsFileWrite(fp
, (char *)buffer
, 4) == 4)