]>
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-2008 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.
26 // Include necessary headers...
30 #include <cups/globals.h>
34 // Character encodings...
50 static int get_utf8(char *&ptr
);
51 static int get_utf16(cups_file_t
*fp
, ppdc_cs_t
&cs
);
52 static int put_utf8(int ch
, char *&ptr
, char *end
);
53 static int put_utf16(cups_file_t
*fp
, int ch
);
57 // 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog.
60 ppdcCatalog::ppdcCatalog(const char *l
, // I - Locale
61 const char *f
) // I - Message catalog file
64 _cups_globals_t
*cg
= _cupsGlobals();
68 locale
= new ppdcString(l
);
69 filename
= new ppdcString(f
);
70 messages
= new ppdcArray();
74 // Try loading the base messages for this locale...
75 char pofile
[1024]; // Message catalog file
78 snprintf(pofile
, sizeof(pofile
), "%s/%s/ppdc_%s.po", cg
->localedir
, l
, l
);
80 if (load_messages(pofile
) && strchr(l
, '_'))
82 // Try the base locale...
83 char baseloc
[3]; // Base locale...
86 strlcpy(baseloc
, l
, sizeof(baseloc
));
87 snprintf(pofile
, sizeof(pofile
), "%s/%s/ppdc_%s.po", cg
->localedir
,
90 load_messages(pofile
);
100 // 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog.
103 ppdcCatalog::~ppdcCatalog()
112 // 'ppdcCatalog::add_message()' - Add a new message.
116 ppdcCatalog::add_message(const char *id
)// I - Message ID to add
118 ppdcMessage
*m
; // Current message
119 char text
[1024]; // Text to translate
122 // Range check input...
126 // Verify that we don't already have the message ID...
127 for (m
= (ppdcMessage
*)messages
->first();
129 m
= (ppdcMessage
*)messages
->next())
130 if (!strcmp(m
->id
->value
, id
))
133 // Add the message...
134 snprintf(text
, sizeof(text
), "TRANSLATE %s", id
);
135 messages
->add(new ppdcMessage(id
, text
));
140 // 'ppdcCatalog::find_message()' - Find a message in a catalog...
143 const char * // O - Message text
144 ppdcCatalog::find_message(
145 const char *id
) // I - Message ID
147 ppdcMessage
*m
; // Current message
150 for (m
= (ppdcMessage
*)messages
->first();
152 m
= (ppdcMessage
*)messages
->next())
153 if (!strcmp(m
->id
->value
, id
))
154 return (m
->string
->value
);
161 // 'ppdcCatalog::load_messages()' - Load messages from a .po file.
164 int // O - 0 on success, -1 on failure
165 ppdcCatalog::load_messages(
166 const char *f
) // I - Message catalog file
168 cups_file_t
*fp
; // Message file
169 ppdcMessage
*temp
; // Current message
170 char line
[4096], // Line buffer
171 *ptr
, // Pointer into buffer
172 id
[4096], // Translation ID
173 str
[4096]; // Translation string
174 int linenum
; // Line number
177 // Open the message catalog file...
178 if ((fp
= cupsFileOpen(f
, "r")) == NULL
)
181 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
182 goto unknown_load_format
;
183 else if (!strcmp(ptr
, ".strings"))
186 * Read messages in Mac OS X ".strings" format, which are UTF-16 text
187 * files of the format:
191 * Strings files can also contain C-style comments.
194 ppdc_cs_t cs
= PPDC_CS_AUTO
; // Character set for file
195 int ch
; // Current character from file
196 char *end
; // End of buffer
204 while ((ch
= get_utf16(fp
, cs
)) != 0)
210 if ((ch
= get_utf16(fp
, cs
)) == 0)
219 put_utf8(ch
, ptr
, end
);
223 // Start of a comment?
224 if ((ch
= get_utf16(fp
, cs
)) == 0)
232 while ((ch
= get_utf16(fp
, cs
)) != 0)
234 if (ch
== '/' && lastch
== '*')
242 // Skip C++ comment...
243 while ((ch
= get_utf16(fp
, cs
)) != 0)
250 // Start or finish quoted string...
259 end
= str
+ sizeof(str
) - 1;
264 end
= id
+ sizeof(id
) - 1;
270 temp
= new ppdcMessage(id
, str
);
276 else if (!strcmp(ptr
, ".po") || !strcmp(ptr
, ".gz"))
279 * Read messages from the catalog file until EOF...
281 * The format is the GNU gettext .po format, which is fairly simple:
284 * msgstr "localized text"
286 * The ID and localized text can span multiple lines using the form:
291 * "localized text spanning "
299 while (cupsFileGets(fp
, line
, sizeof(line
)))
303 // Skip blank and comment lines...
304 if (line
[0] == '#' || !line
[0])
307 // Strip the trailing quote...
308 if ((ptr
= (char *)strrchr(line
, '\"')) == NULL
)
310 fprintf(stderr
, "ERROR: Expected quoted string on line %d of %s!\n",
318 // Find start of value...
319 if ((ptr
= strchr(line
, '\"')) == NULL
)
321 fprintf(stderr
, "ERROR: Expected quoted string on line %d of %s!\n",
329 // Unquote the text...
330 char *sptr
, *dptr
; // Source/destination pointers
332 for (sptr
= ptr
, dptr
= ptr
; *sptr
;)
341 while (isdigit(*sptr
))
343 *dptr
= *dptr
* 8 + *sptr
- '0';
353 else if (*sptr
== 'r')
355 else if (*sptr
== 't')
369 // Create or add to a message...
370 if (!strncmp(line
, "msgid", 5))
374 temp
= new ppdcMessage(id
, str
);
379 strlcpy(id
, ptr
, sizeof(id
));
382 else if (!strncmp(line
, "msgstr", 6))
386 fprintf(stderr
, "ERROR: Need a msgid line before any "
387 "translation strings on line %d of %s!\n",
393 strlcpy(str
, ptr
, sizeof(str
));
395 else if (line
[0] == '\"' && str
[0])
396 strlcat(str
, ptr
, sizeof(str
));
397 else if (line
[0] == '\"' && id
[0])
398 strlcat(id
, ptr
, sizeof(id
));
401 fprintf(stderr
, "ERROR: Unexpected text on line %d of %s!\n",
410 temp
= new ppdcMessage(id
, str
);
416 goto unknown_load_format
;
419 * Close the file and return...
427 * Unknown format error...
432 fprintf(stderr
, "ERROR: Unknown message catalog format for \"%s\"!\n", f
);
439 // 'ppdcCatalog::save_messages()' - Save the messages to a .po file.
442 int // O - 0 on success, -1 on error
443 ppdcCatalog::save_messages(
444 const char *f
) // I - File to save to
446 cups_file_t
*fp
; // Message file
447 ppdcMessage
*m
; // Current message
448 char *ptr
; // Pointer into string
449 int utf16
; // Output UTF-16 .strings file?
450 int ch
; // Current character
454 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
457 if (!strcmp(ptr
, ".gz"))
458 fp
= cupsFileOpen(f
, "w9");
460 fp
= cupsFileOpen(f
, "w");
465 // For .strings files, write a BOM for big-endian output...
466 utf16
= !strcmp(ptr
, ".strings");
469 put_utf16(fp
, 0xfeff);
471 // Loop through all of the messages...
472 for (m
= (ppdcMessage
*)messages
->first();
474 m
= (ppdcMessage
*)messages
->next())
481 while ((ch
= get_utf8(ptr
)) != 0)
507 ptr
= m
->string
->value
;
508 while ((ch
= get_utf8(ptr
)) != 0)
534 cupsFilePuts(fp
, "msgid \"");
535 for (ptr
= m
->id
->value
; *ptr
; ptr
++)
539 cupsFilePuts(fp
, "\\n");
542 cupsFilePuts(fp
, "\\\\");
545 cupsFilePuts(fp
, "\\\"");
548 cupsFilePutChar(fp
, *ptr
);
551 cupsFilePuts(fp
, "\"\n");
553 cupsFilePuts(fp
, "msgstr \"");
554 for (ptr
= m
->string
->value
; *ptr
; ptr
++)
558 cupsFilePuts(fp
, "\\n");
561 cupsFilePuts(fp
, "\\\\");
564 cupsFilePuts(fp
, "\\\"");
567 cupsFilePutChar(fp
, *ptr
);
570 cupsFilePuts(fp
, "\"\n");
572 cupsFilePutChar(fp
, '\n');
583 // 'get_utf8()' - Get a UTF-8 character.
586 static int // O - Unicode character or 0 on EOF
587 get_utf8(char *&ptr
) // IO - Pointer to character
589 int ch
; // Current character
592 if ((ch
= *ptr
++ & 255) < 0xc0)
595 if ((ch
& 0xe0) == 0xc0)
598 if ((*ptr
& 0xc0) != 0x80)
601 ch
= ((ch
& 0x1f) << 6) | (*ptr
++ & 0x3f);
603 else if ((ch
& 0xf0) == 0xe0)
605 // Three-byte UTF-8...
606 if ((*ptr
& 0xc0) != 0x80)
609 ch
= ((ch
& 0x0f) << 6) | (*ptr
++ & 0x3f);
611 if ((*ptr
& 0xc0) != 0x80)
614 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
616 else if ((ch
& 0xf8) == 0xf0)
618 // Four-byte UTF-8...
619 if ((*ptr
& 0xc0) != 0x80)
622 ch
= ((ch
& 0x07) << 6) | (*ptr
++ & 0x3f);
624 if ((*ptr
& 0xc0) != 0x80)
627 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
629 if ((*ptr
& 0xc0) != 0x80)
632 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
640 // 'get_utf16()' - Get a UTF-16 character...
643 static int // O - Unicode character or 0 on EOF
644 get_utf16(cups_file_t
*fp
, // I - File to read from
645 ppdc_cs_t
&cs
) // IO - Character set of file
647 int ch
; // Current character
648 unsigned char buffer
[3]; // Bytes
651 if (cs
== PPDC_CS_AUTO
)
653 // Get byte-order-mark, if present...
654 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
657 if (buffer
[0] == 0xfe && buffer
[1] == 0xff)
659 // Big-endian UTF-16...
660 cs
= PPDC_CS_UTF16BE
;
662 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
665 else if (buffer
[0] == 0xff && buffer
[1] == 0xfe)
667 // Little-endian UTF-16...
668 cs
= PPDC_CS_UTF16LE
;
670 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
673 else if (buffer
[0] == 0x00 && buffer
[1] != 0x00)
675 // No BOM, assume big-endian UTF-16...
676 cs
= PPDC_CS_UTF16BE
;
678 else if (buffer
[0] != 0x00 && buffer
[1] == 0x00)
680 // No BOM, assume little-endian UTF-16...
681 cs
= PPDC_CS_UTF16LE
;
685 // No BOM, assume UTF-8...
691 else if (cs
!= PPDC_CS_UTF8
)
693 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
697 if (cs
== PPDC_CS_UTF8
)
699 // UTF-8 character...
700 ch
= cupsFileGetChar(fp
);
702 if ((ch
& 0xe0) == 0xc0)
705 if (cupsFileRead(fp
, (char *)buffer
, 1) != 1)
708 if ((buffer
[0] & 0xc0) != 0x80)
711 ch
= ((ch
& 0x1f) << 6) | (buffer
[0] & 0x3f);
713 else if ((ch
& 0xf0) == 0xe0)
715 // Three-byte UTF-8...
716 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
719 if ((buffer
[0] & 0xc0) != 0x80 ||
720 (buffer
[1] & 0xc0) != 0x80)
723 ch
= ((((ch
& 0x0f) << 6) | (buffer
[0] & 0x3f)) << 6) |
726 else if ((ch
& 0xf8) == 0xf0)
728 // Four-byte UTF-8...
729 if (cupsFileRead(fp
, (char *)buffer
, 3) != 3)
732 if ((buffer
[0] & 0xc0) != 0x80 ||
733 (buffer
[1] & 0xc0) != 0x80 ||
734 (buffer
[2] & 0xc0) != 0x80)
737 ch
= ((((((ch
& 0x07) << 6) | (buffer
[0] & 0x3f)) << 6) |
738 (buffer
[1] & 0x3f)) << 6) | (buffer
[2] & 0x3f);
743 // UTF-16 character...
744 if (cs
== PPDC_CS_UTF16BE
)
745 ch
= (buffer
[0] << 8) | buffer
[1];
747 ch
= (buffer
[1] << 8) | buffer
[0];
749 if (ch
>= 0xd800 && ch
<= 0xdbff)
751 // Handle multi-word encoding...
754 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
757 if (cs
== PPDC_CS_UTF16BE
)
758 lch
= (buffer
[0] << 8) | buffer
[1];
760 lch
= (buffer
[1] << 8) | buffer
[0];
762 if (lch
< 0xdc00 || lch
>= 0xdfff)
765 ch
= (((ch
& 0x3ff) << 10) | (lch
& 0x3ff)) + 0x10000;
774 // 'put_utf8()' - Add a UTF-8 character to a string.
777 static int // O - 0 on success, -1 on failure
778 put_utf8(int ch
, // I - Unicode character
779 char *&ptr
, // IO - String pointer
780 char *end
) // I - End of buffer
793 if ((ptr
+ 1) >= end
)
796 *ptr
++ = 0xc0 | (ch
>> 6);
797 *ptr
++ = 0x80 | (ch
& 0x3f);
799 else if (ch
< 0x10000)
801 // Three-byte UTF-8...
802 if ((ptr
+ 2) >= end
)
805 *ptr
++ = 0xe0 | (ch
>> 12);
806 *ptr
++ = 0x80 | ((ch
>> 6) & 0x3f);
807 *ptr
++ = 0x80 | (ch
& 0x3f);
811 // Four-byte UTF-8...
812 if ((ptr
+ 3) >= end
)
815 *ptr
++ = 0xf0 | (ch
>> 18);
816 *ptr
++ = 0x80 | ((ch
>> 12) & 0x3f);
817 *ptr
++ = 0x80 | ((ch
>> 6) & 0x3f);
818 *ptr
++ = 0x80 | (ch
& 0x3f);
826 // 'put_utf16()' - Write a UTF-16 character to a file.
829 static int // O - 0 on success, -1 on failure
830 put_utf16(cups_file_t
*fp
, // I - File to write to
831 int ch
) // I - Unicode character
833 unsigned char buffer
[4]; // Output buffer
838 // One-word UTF-16 big-endian...
842 if (cupsFileWrite(fp
, (char *)buffer
, 2) == 2)
847 // Two-word UTF-16 big-endian...
850 buffer
[0] = 0xd8 | (ch
>> 18);
851 buffer
[1] = ch
>> 10;
852 buffer
[2] = 0xdc | ((ch
>> 8) & 0x03);
855 if (cupsFileWrite(fp
, (char *)buffer
, 4) == 4)