]>
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-2014 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 // Include necessary headers...
20 #include "ppdc-private.h"
24 // Character encodings...
40 static int get_utf8(char *&ptr
);
41 static int get_utf16(cups_file_t
*fp
, ppdc_cs_t
&cs
);
42 static int put_utf8(int ch
, char *&ptr
, char *end
);
43 static int put_utf16(cups_file_t
*fp
, int ch
);
47 // 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog.
50 ppdcCatalog::ppdcCatalog(const char *l
, // I - Locale
51 const char *f
) // I - Message catalog file
54 _cups_globals_t
*cg
= _cupsGlobals();
60 locale
= new ppdcString(l
);
61 filename
= new ppdcString(f
);
62 messages
= new ppdcArray();
66 // Try loading the base messages for this locale...
67 char pofile
[1024]; // Message catalog file
70 snprintf(pofile
, sizeof(pofile
), "%s/%s/cups_%s.po", cg
->localedir
, l
, l
);
72 if (load_messages(pofile
) && strchr(l
, '_'))
74 // Try the base locale...
75 char baseloc
[3]; // Base locale...
78 strlcpy(baseloc
, l
, sizeof(baseloc
));
79 snprintf(pofile
, sizeof(pofile
), "%s/%s/cups_%s.po", cg
->localedir
,
82 load_messages(pofile
);
92 // 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog.
95 ppdcCatalog::~ppdcCatalog()
106 // 'ppdcCatalog::add_message()' - Add a new message.
110 ppdcCatalog::add_message(
111 const char *id
, // I - Message ID to add
112 const char *string
) // I - Translation string
114 ppdcMessage
*m
; // Current message
115 char text
[1024]; // Text to translate
118 // Range check input...
122 // Verify that we don't already have the message ID...
123 for (m
= (ppdcMessage
*)messages
->first();
125 m
= (ppdcMessage
*)messages
->next())
126 if (!strcmp(m
->id
->value
, id
))
130 m
->string
->release();
131 m
->string
= new ppdcString(string
);
136 // Add the message...
139 snprintf(text
, sizeof(text
), "TRANSLATE %s", id
);
143 messages
->add(new ppdcMessage(id
, string
));
148 // 'ppdcCatalog::find_message()' - Find a message in a catalog...
151 const char * // O - Message text
152 ppdcCatalog::find_message(
153 const char *id
) // I - Message ID
155 ppdcMessage
*m
; // Current message
161 for (m
= (ppdcMessage
*)messages
->first();
163 m
= (ppdcMessage
*)messages
->next())
164 if (!strcmp(m
->id
->value
, id
))
165 return (m
->string
->value
);
172 // 'ppdcCatalog::load_messages()' - Load messages from a .po file.
175 int // O - 0 on success, -1 on failure
176 ppdcCatalog::load_messages(
177 const char *f
) // I - Message catalog file
179 cups_file_t
*fp
; // Message file
180 char line
[4096], // Line buffer
181 *ptr
, // Pointer into buffer
182 id
[4096], // Translation ID
183 str
[4096]; // Translation string
184 int linenum
; // Line number
187 // Open the message catalog file...
188 if ((fp
= cupsFileOpen(f
, "r")) == NULL
)
191 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
192 goto unknown_load_format
;
193 else if (!strcmp(ptr
, ".strings"))
196 * Read messages in OS X ".strings" format, which are UTF-16 text files of
201 * Strings files can also contain C-style comments.
204 ppdc_cs_t cs
= PPDC_CS_AUTO
; // Character set for file
205 int ch
; // Current character from file
206 char *end
; // End of buffer
214 while ((ch
= get_utf16(fp
, cs
)) != 0)
220 if ((ch
= get_utf16(fp
, cs
)) == 0)
235 put_utf8(ch
, ptr
, end
);
239 // Start of a comment?
240 if ((ch
= get_utf16(fp
, cs
)) == 0)
248 while ((ch
= get_utf16(fp
, cs
)) != 0)
250 if (ch
== '/' && lastch
== '*')
258 // Skip C++ comment...
259 while ((ch
= get_utf16(fp
, cs
)) != 0)
266 // Start quoted string...
270 end
= str
+ sizeof(str
) - 1;
275 end
= id
+ sizeof(id
) - 1;
281 add_message(id
, str
);
286 else if (!strcmp(ptr
, ".po") || !strcmp(ptr
, ".gz"))
289 * Read messages from the catalog file until EOF...
291 * The format is the GNU gettext .po format, which is fairly simple:
294 * msgstr "localized text"
296 * The ID and localized text can span multiple lines using the form:
301 * "localized text spanning "
305 int which
, // In msgid?
306 haveid
, // Did we get a msgid string?
307 havestr
; // Did we get a msgstr string?
316 while (cupsFileGets(fp
, line
, sizeof(line
)))
320 // Skip blank and comment lines...
321 if (line
[0] == '#' || !line
[0])
324 // Strip the trailing quote...
325 if ((ptr
= (char *)strrchr(line
, '\"')) == NULL
)
327 _cupsLangPrintf(stderr
,
328 _("ppdc: Expected quoted string on line %d of %s."),
336 // Find start of value...
337 if ((ptr
= strchr(line
, '\"')) == NULL
)
339 _cupsLangPrintf(stderr
,
340 _("ppdc: Expected quoted string on line %d of %s."),
348 // Unquote the text...
349 char *sptr
, *dptr
; // Source/destination pointers
351 for (sptr
= ptr
, dptr
= ptr
; *sptr
;)
360 while (isdigit(*sptr
))
362 *dptr
= *dptr
* 8 + *sptr
- '0';
372 else if (*sptr
== 'r')
374 else if (*sptr
== 't')
388 // Create or add to a message...
389 if (!strncmp(line
, "msgid", 5))
391 if (haveid
&& havestr
)
392 add_message(id
, str
);
394 strlcpy(id
, ptr
, sizeof(id
));
400 else if (!strncmp(line
, "msgstr", 6))
404 _cupsLangPrintf(stderr
,
405 _("ppdc: Need a msgid line before any "
406 "translation strings on line %d of %s."),
412 strlcpy(str
, ptr
, sizeof(str
));
416 else if (line
[0] == '\"' && which
== 2)
417 strlcat(str
, ptr
, sizeof(str
));
418 else if (line
[0] == '\"' && which
== 1)
419 strlcat(id
, ptr
, sizeof(id
));
422 _cupsLangPrintf(stderr
, _("ppdc: Unexpected text on line %d of %s."),
429 if (haveid
&& havestr
)
430 add_message(id
, str
);
433 goto unknown_load_format
;
436 * Close the file and return...
444 * Unknown format error...
449 _cupsLangPrintf(stderr
,
450 _("ppdc: Unknown message catalog format for \"%s\"."), f
);
457 // 'ppdcCatalog::save_messages()' - Save the messages to a .po file.
460 int // O - 0 on success, -1 on error
461 ppdcCatalog::save_messages(
462 const char *f
) // I - File to save to
464 cups_file_t
*fp
; // Message file
465 ppdcMessage
*m
; // Current message
466 char *ptr
; // Pointer into string
467 int utf16
; // Output UTF-16 .strings file?
468 int ch
; // Current character
472 if ((ptr
= (char *)strrchr(f
, '.')) == NULL
)
475 if (!strcmp(ptr
, ".gz"))
476 fp
= cupsFileOpen(f
, "w9");
478 fp
= cupsFileOpen(f
, "w");
483 // For .strings files, write a BOM for big-endian output...
484 utf16
= !strcmp(ptr
, ".strings");
487 put_utf16(fp
, 0xfeff);
489 // Loop through all of the messages...
490 for (m
= (ppdcMessage
*)messages
->first();
492 m
= (ppdcMessage
*)messages
->next())
499 while ((ch
= get_utf8(ptr
)) != 0)
525 ptr
= m
->string
->value
;
526 while ((ch
= get_utf8(ptr
)) != 0)
552 cupsFilePuts(fp
, "msgid \"");
553 for (ptr
= m
->id
->value
; *ptr
; ptr
++)
557 cupsFilePuts(fp
, "\\n");
560 cupsFilePuts(fp
, "\\\\");
563 cupsFilePuts(fp
, "\\\"");
566 cupsFilePutChar(fp
, *ptr
);
569 cupsFilePuts(fp
, "\"\n");
571 cupsFilePuts(fp
, "msgstr \"");
572 for (ptr
= m
->string
->value
; *ptr
; ptr
++)
576 cupsFilePuts(fp
, "\\n");
579 cupsFilePuts(fp
, "\\\\");
582 cupsFilePuts(fp
, "\\\"");
585 cupsFilePutChar(fp
, *ptr
);
588 cupsFilePuts(fp
, "\"\n");
590 cupsFilePutChar(fp
, '\n');
601 // 'get_utf8()' - Get a UTF-8 character.
604 static int // O - Unicode character or 0 on EOF
605 get_utf8(char *&ptr
) // IO - Pointer to character
607 int ch
; // Current character
610 if ((ch
= *ptr
++ & 255) < 0xc0)
613 if ((ch
& 0xe0) == 0xc0)
616 if ((*ptr
& 0xc0) != 0x80)
619 ch
= ((ch
& 0x1f) << 6) | (*ptr
++ & 0x3f);
621 else if ((ch
& 0xf0) == 0xe0)
623 // Three-byte UTF-8...
624 if ((*ptr
& 0xc0) != 0x80)
627 ch
= ((ch
& 0x0f) << 6) | (*ptr
++ & 0x3f);
629 if ((*ptr
& 0xc0) != 0x80)
632 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
634 else if ((ch
& 0xf8) == 0xf0)
636 // Four-byte UTF-8...
637 if ((*ptr
& 0xc0) != 0x80)
640 ch
= ((ch
& 0x07) << 6) | (*ptr
++ & 0x3f);
642 if ((*ptr
& 0xc0) != 0x80)
645 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
647 if ((*ptr
& 0xc0) != 0x80)
650 ch
= (ch
<< 6) | (*ptr
++ & 0x3f);
658 // 'get_utf16()' - Get a UTF-16 character...
661 static int // O - Unicode character or 0 on EOF
662 get_utf16(cups_file_t
*fp
, // I - File to read from
663 ppdc_cs_t
&cs
) // IO - Character set of file
665 int ch
; // Current character
666 unsigned char buffer
[3]; // Bytes
669 if (cs
== PPDC_CS_AUTO
)
671 // Get byte-order-mark, if present...
672 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
675 if (buffer
[0] == 0xfe && buffer
[1] == 0xff)
677 // Big-endian UTF-16...
678 cs
= PPDC_CS_UTF16BE
;
680 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
683 else if (buffer
[0] == 0xff && buffer
[1] == 0xfe)
685 // Little-endian UTF-16...
686 cs
= PPDC_CS_UTF16LE
;
688 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
691 else if (buffer
[0] == 0x00 && buffer
[1] != 0x00)
693 // No BOM, assume big-endian UTF-16...
694 cs
= PPDC_CS_UTF16BE
;
696 else if (buffer
[0] != 0x00 && buffer
[1] == 0x00)
698 // No BOM, assume little-endian UTF-16...
699 cs
= PPDC_CS_UTF16LE
;
703 // No BOM, assume UTF-8...
709 else if (cs
!= PPDC_CS_UTF8
)
711 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
715 if (cs
== PPDC_CS_UTF8
)
717 // UTF-8 character...
718 if ((ch
= cupsFileGetChar(fp
)) < 0)
721 if ((ch
& 0xe0) == 0xc0)
724 if (cupsFileRead(fp
, (char *)buffer
, 1) != 1)
727 if ((buffer
[0] & 0xc0) != 0x80)
730 ch
= ((ch
& 0x1f) << 6) | (buffer
[0] & 0x3f);
732 else if ((ch
& 0xf0) == 0xe0)
734 // Three-byte UTF-8...
735 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
738 if ((buffer
[0] & 0xc0) != 0x80 ||
739 (buffer
[1] & 0xc0) != 0x80)
742 ch
= ((((ch
& 0x0f) << 6) | (buffer
[0] & 0x3f)) << 6) |
745 else if ((ch
& 0xf8) == 0xf0)
747 // Four-byte UTF-8...
748 if (cupsFileRead(fp
, (char *)buffer
, 3) != 3)
751 if ((buffer
[0] & 0xc0) != 0x80 ||
752 (buffer
[1] & 0xc0) != 0x80 ||
753 (buffer
[2] & 0xc0) != 0x80)
756 ch
= ((((((ch
& 0x07) << 6) | (buffer
[0] & 0x3f)) << 6) |
757 (buffer
[1] & 0x3f)) << 6) | (buffer
[2] & 0x3f);
762 // UTF-16 character...
763 if (cs
== PPDC_CS_UTF16BE
)
764 ch
= (buffer
[0] << 8) | buffer
[1];
766 ch
= (buffer
[1] << 8) | buffer
[0];
768 if (ch
>= 0xd800 && ch
<= 0xdbff)
770 // Handle multi-word encoding...
773 if (cupsFileRead(fp
, (char *)buffer
, 2) != 2)
776 if (cs
== PPDC_CS_UTF16BE
)
777 lch
= (buffer
[0] << 8) | buffer
[1];
779 lch
= (buffer
[1] << 8) | buffer
[0];
781 if (lch
< 0xdc00 || lch
>= 0xdfff)
784 ch
= (((ch
& 0x3ff) << 10) | (lch
& 0x3ff)) + 0x10000;
793 // 'put_utf8()' - Add a UTF-8 character to a string.
796 static int // O - 0 on success, -1 on failure
797 put_utf8(int ch
, // I - Unicode character
798 char *&ptr
, // IO - String pointer
799 char *end
) // I - End of buffer
812 if ((ptr
+ 1) >= end
)
815 *ptr
++ = (char)(0xc0 | (ch
>> 6));
816 *ptr
++ = (char)(0x80 | (ch
& 0x3f));
818 else if (ch
< 0x10000)
820 // Three-byte UTF-8...
821 if ((ptr
+ 2) >= end
)
824 *ptr
++ = (char)(0xe0 | (ch
>> 12));
825 *ptr
++ = (char)(0x80 | ((ch
>> 6) & 0x3f));
826 *ptr
++ = (char)(0x80 | (ch
& 0x3f));
830 // Four-byte UTF-8...
831 if ((ptr
+ 3) >= end
)
834 *ptr
++ = (char)(0xf0 | (ch
>> 18));
835 *ptr
++ = (char)(0x80 | ((ch
>> 12) & 0x3f));
836 *ptr
++ = (char)(0x80 | ((ch
>> 6) & 0x3f));
837 *ptr
++ = (char)(0x80 | (ch
& 0x3f));
845 // 'put_utf16()' - Write a UTF-16 character to a file.
848 static int // O - 0 on success, -1 on failure
849 put_utf16(cups_file_t
*fp
, // I - File to write to
850 int ch
) // I - Unicode character
852 unsigned char buffer
[4]; // Output buffer
857 // One-word UTF-16 big-endian...
858 buffer
[0] = (unsigned char)(ch
>> 8);
859 buffer
[1] = (unsigned char)ch
;
861 if (cupsFileWrite(fp
, (char *)buffer
, 2) == 2)
866 // Two-word UTF-16 big-endian...
869 buffer
[0] = (unsigned char)(0xd8 | (ch
>> 18));
870 buffer
[1] = (unsigned char)(ch
>> 10);
871 buffer
[2] = (unsigned char)(0xdc | ((ch
>> 8) & 0x03));
872 buffer
[3] = (unsigned char)ch
;
874 if (cupsFileWrite(fp
, (char *)buffer
, 4) == 4)