]>
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-2012 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
174 for (m
= (ppdcMessage
*)messages
->first();
176 m
= (ppdcMessage
*)messages
->next())
177 if (!strcmp(m
->id
->value
, id
))
178 return (m
->string
->value
);
185 // 'ppdcCatalog::load_messages()' - Load messages from a .po file.
188 int // O - 0 on success, -1 on failure
189 ppdcCatalog::load_messages(
190 const char *f
) // I - Message catalog file
192 cups_file_t
*fp
; // Message file
193 char line
[4096], // Line buffer
194 *ptr
, // Pointer into buffer
195 id
[4096], // Translation ID
196 str
[4096]; // Translation string
197 int linenum
; // Line number
200 // Open the message catalog file...
201 if ((fp
= cupsFileOpen(f
, "r")) == NULL
)
204 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
205 goto unknown_load_format
;
206 else if (!strcmp(ptr
, ".strings"))
209 * Read messages in OS X ".strings" format, which are UTF-16 text files of
214 * Strings files can also contain C-style comments.
217 ppdc_cs_t cs
= PPDC_CS_AUTO
; // Character set for file
218 int ch
; // Current character from file
219 char *end
; // End of buffer
227 while ((ch
= get_utf16(fp
, cs
)) != 0)
233 if ((ch
= get_utf16(fp
, cs
)) == 0)
248 put_utf8(ch
, ptr
, end
);
252 // Start of a comment?
253 if ((ch
= get_utf16(fp
, cs
)) == 0)
261 while ((ch
= get_utf16(fp
, cs
)) != 0)
263 if (ch
== '/' && lastch
== '*')
271 // Skip C++ comment...
272 while ((ch
= get_utf16(fp
, cs
)) != 0)
279 // Start quoted string...
283 end
= str
+ sizeof(str
) - 1;
288 end
= id
+ sizeof(id
) - 1;
294 add_message(id
, str
);
299 else if (!strcmp(ptr
, ".po") || !strcmp(ptr
, ".gz"))
302 * Read messages from the catalog file until EOF...
304 * The format is the GNU gettext .po format, which is fairly simple:
307 * msgstr "localized text"
309 * The ID and localized text can span multiple lines using the form:
314 * "localized text spanning "
318 int which
, // In msgid?
319 haveid
, // Did we get a msgid string?
320 havestr
; // Did we get a msgstr string?
329 while (cupsFileGets(fp
, line
, sizeof(line
)))
333 // Skip blank and comment lines...
334 if (line
[0] == '#' || !line
[0])
337 // Strip the trailing quote...
338 if ((ptr
= (char *)strrchr(line
, '\"')) == NULL
)
340 _cupsLangPrintf(stderr
,
341 _("ppdc: Expected quoted string on line %d of %s."),
349 // Find start of value...
350 if ((ptr
= strchr(line
, '\"')) == NULL
)
352 _cupsLangPrintf(stderr
,
353 _("ppdc: Expected quoted string on line %d of %s."),
361 // Unquote the text...
362 char *sptr
, *dptr
; // Source/destination pointers
364 for (sptr
= ptr
, dptr
= ptr
; *sptr
;)
373 while (isdigit(*sptr
))
375 *dptr
= *dptr
* 8 + *sptr
- '0';
385 else if (*sptr
== 'r')
387 else if (*sptr
== 't')
401 // Create or add to a message...
402 if (!strncmp(line
, "msgid", 5))
404 if (haveid
&& havestr
)
405 add_message(id
, str
);
407 strlcpy(id
, ptr
, sizeof(id
));
413 else if (!strncmp(line
, "msgstr", 6))
417 _cupsLangPrintf(stderr
,
418 _("ppdc: Need a msgid line before any "
419 "translation strings on line %d of %s."),
425 strlcpy(str
, ptr
, sizeof(str
));
429 else if (line
[0] == '\"' && which
== 2)
430 strlcat(str
, ptr
, sizeof(str
));
431 else if (line
[0] == '\"' && which
== 1)
432 strlcat(id
, ptr
, sizeof(id
));
435 _cupsLangPrintf(stderr
, _("ppdc: Unexpected text on line %d of %s."),
442 if (haveid
&& havestr
)
443 add_message(id
, str
);
446 goto unknown_load_format
;
449 * Close the file and return...
457 * Unknown format error...
462 _cupsLangPrintf(stderr
,
463 _("ppdc: Unknown message catalog format for \"%s\"."), f
);
470 // 'ppdcCatalog::save_messages()' - Save the messages to a .po file.
473 int // O - 0 on success, -1 on error
474 ppdcCatalog::save_messages(
475 const char *f
) // I - File to save to
477 cups_file_t
*fp
; // Message file
478 ppdcMessage
*m
; // Current message
479 char *ptr
; // Pointer into string
480 int utf16
; // Output UTF-16 .strings file?
481 int ch
; // Current character
485 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
488 if (!strcmp(ptr
, ".gz"))
489 fp
= cupsFileOpen(f
, "w9");
491 fp
= cupsFileOpen(f
, "w");
496 // For .strings files, write a BOM for big-endian output...
497 utf16
= !strcmp(ptr
, ".strings");
500 put_utf16(fp
, 0xfeff);
502 // Loop through all of the messages...
503 for (m
= (ppdcMessage
*)messages
->first();
505 m
= (ppdcMessage
*)messages
->next())
512 while ((ch
= get_utf8(ptr
)) != 0)
538 ptr
= m
->string
->value
;
539 while ((ch
= get_utf8(ptr
)) != 0)
565 cupsFilePuts(fp
, "msgid \"");
566 for (ptr
= m
->id
->value
; *ptr
; ptr
++)
570 cupsFilePuts(fp
, "\\n");
573 cupsFilePuts(fp
, "\\\\");
576 cupsFilePuts(fp
, "\\\"");
579 cupsFilePutChar(fp
, *ptr
);
582 cupsFilePuts(fp
, "\"\n");
584 cupsFilePuts(fp
, "msgstr \"");
585 for (ptr
= m
->string
->value
; *ptr
; ptr
++)
589 cupsFilePuts(fp
, "\\n");
592 cupsFilePuts(fp
, "\\\\");
595 cupsFilePuts(fp
, "\\\"");
598 cupsFilePutChar(fp
, *ptr
);
601 cupsFilePuts(fp
, "\"\n");
603 cupsFilePutChar(fp
, '\n');
614 // 'get_utf8()' - Get a UTF-8 character.
617 static int // O - Unicode character or 0 on EOF
618 get_utf8(char *&ptr
) // IO - Pointer to character
620 int ch
; // Current character
623 if ((ch
= *ptr
++ & 255) < 0xc0)
626 if ((ch
& 0xe0) == 0xc0)
629 if ((*ptr
& 0xc0) != 0x80)
632 ch
= ((ch
& 0x1f) << 6) | (*ptr
++ & 0x3f);
634 else if ((ch
& 0xf0) == 0xe0)
636 // Three-byte UTF-8...
637 if ((*ptr
& 0xc0) != 0x80)
640 ch
= ((ch
& 0x0f) << 6) | (*ptr
++ & 0x3f);
642 if ((*ptr
& 0xc0) != 0x80)
645 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
647 else if ((ch
& 0xf8) == 0xf0)
649 // Four-byte UTF-8...
650 if ((*ptr
& 0xc0) != 0x80)
653 ch
= ((ch
& 0x07) << 6) | (*ptr
++ & 0x3f);
655 if ((*ptr
& 0xc0) != 0x80)
658 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
660 if ((*ptr
& 0xc0) != 0x80)
663 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
671 // 'get_utf16()' - Get a UTF-16 character...
674 static int // O - Unicode character or 0 on EOF
675 get_utf16(cups_file_t
*fp
, // I - File to read from
676 ppdc_cs_t
&cs
) // IO - Character set of file
678 int ch
; // Current character
679 unsigned char buffer
[3]; // Bytes
682 if (cs
== PPDC_CS_AUTO
)
684 // Get byte-order-mark, if present...
685 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
688 if (buffer
[0] == 0xfe && buffer
[1] == 0xff)
690 // Big-endian UTF-16...
691 cs
= PPDC_CS_UTF16BE
;
693 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
696 else if (buffer
[0] == 0xff && buffer
[1] == 0xfe)
698 // Little-endian UTF-16...
699 cs
= PPDC_CS_UTF16LE
;
701 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
704 else if (buffer
[0] == 0x00 && buffer
[1] != 0x00)
706 // No BOM, assume big-endian UTF-16...
707 cs
= PPDC_CS_UTF16BE
;
709 else if (buffer
[0] != 0x00 && buffer
[1] == 0x00)
711 // No BOM, assume little-endian UTF-16...
712 cs
= PPDC_CS_UTF16LE
;
716 // No BOM, assume UTF-8...
722 else if (cs
!= PPDC_CS_UTF8
)
724 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
728 if (cs
== PPDC_CS_UTF8
)
730 // UTF-8 character...
731 if ((ch
= cupsFileGetChar(fp
)) < 0)
734 if ((ch
& 0xe0) == 0xc0)
737 if (cupsFileRead(fp
, (char *)buffer
, 1) != 1)
740 if ((buffer
[0] & 0xc0) != 0x80)
743 ch
= ((ch
& 0x1f) << 6) | (buffer
[0] & 0x3f);
745 else if ((ch
& 0xf0) == 0xe0)
747 // Three-byte UTF-8...
748 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
751 if ((buffer
[0] & 0xc0) != 0x80 ||
752 (buffer
[1] & 0xc0) != 0x80)
755 ch
= ((((ch
& 0x0f) << 6) | (buffer
[0] & 0x3f)) << 6) |
758 else if ((ch
& 0xf8) == 0xf0)
760 // Four-byte UTF-8...
761 if (cupsFileRead(fp
, (char *)buffer
, 3) != 3)
764 if ((buffer
[0] & 0xc0) != 0x80 ||
765 (buffer
[1] & 0xc0) != 0x80 ||
766 (buffer
[2] & 0xc0) != 0x80)
769 ch
= ((((((ch
& 0x07) << 6) | (buffer
[0] & 0x3f)) << 6) |
770 (buffer
[1] & 0x3f)) << 6) | (buffer
[2] & 0x3f);
775 // UTF-16 character...
776 if (cs
== PPDC_CS_UTF16BE
)
777 ch
= (buffer
[0] << 8) | buffer
[1];
779 ch
= (buffer
[1] << 8) | buffer
[0];
781 if (ch
>= 0xd800 && ch
<= 0xdbff)
783 // Handle multi-word encoding...
786 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
789 if (cs
== PPDC_CS_UTF16BE
)
790 lch
= (buffer
[0] << 8) | buffer
[1];
792 lch
= (buffer
[1] << 8) | buffer
[0];
794 if (lch
< 0xdc00 || lch
>= 0xdfff)
797 ch
= (((ch
& 0x3ff) << 10) | (lch
& 0x3ff)) + 0x10000;
806 // 'put_utf8()' - Add a UTF-8 character to a string.
809 static int // O - 0 on success, -1 on failure
810 put_utf8(int ch
, // I - Unicode character
811 char *&ptr
, // IO - String pointer
812 char *end
) // I - End of buffer
825 if ((ptr
+ 1) >= end
)
828 *ptr
++ = 0xc0 | (ch
>> 6);
829 *ptr
++ = 0x80 | (ch
& 0x3f);
831 else if (ch
< 0x10000)
833 // Three-byte UTF-8...
834 if ((ptr
+ 2) >= end
)
837 *ptr
++ = 0xe0 | (ch
>> 12);
838 *ptr
++ = 0x80 | ((ch
>> 6) & 0x3f);
839 *ptr
++ = 0x80 | (ch
& 0x3f);
843 // Four-byte UTF-8...
844 if ((ptr
+ 3) >= end
)
847 *ptr
++ = 0xf0 | (ch
>> 18);
848 *ptr
++ = 0x80 | ((ch
>> 12) & 0x3f);
849 *ptr
++ = 0x80 | ((ch
>> 6) & 0x3f);
850 *ptr
++ = 0x80 | (ch
& 0x3f);
858 // 'put_utf16()' - Write a UTF-16 character to a file.
861 static int // O - 0 on success, -1 on failure
862 put_utf16(cups_file_t
*fp
, // I - File to write to
863 int ch
) // I - Unicode character
865 unsigned char buffer
[4]; // Output buffer
870 // One-word UTF-16 big-endian...
874 if (cupsFileWrite(fp
, (char *)buffer
, 2) == 2)
879 // Two-word UTF-16 big-endian...
882 buffer
[0] = 0xd8 | (ch
>> 18);
883 buffer
[1] = ch
>> 10;
884 buffer
[2] = 0xdc | ((ch
>> 8) & 0x03);
887 if (cupsFileWrite(fp
, (char *)buffer
, 4) == 4)