]>
Commit | Line | Data |
---|---|---|
ac884b6a MS |
1 | // |
2 | // "$Id$" | |
3 | // | |
4 | // Shared message catalog class for the CUPS PPD Compiler. | |
5 | // | |
6 | // Copyright 2007-2008 by Apple Inc. | |
7 | // Copyright 2002-2006 by Easy Software Products. | |
8 | // | |
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/". | |
14 | // | |
15 | // Contents: | |
16 | // | |
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 | // | |
24 | ||
25 | // | |
26 | // Include necessary headers... | |
27 | // | |
28 | ||
29 | #include "ppdc.h" | |
30 | #include <cups/globals.h> | |
31 | ||
32 | ||
33 | // | |
34 | // 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog. | |
35 | // | |
36 | ||
37 | ppdcCatalog::ppdcCatalog(const char *l, // I - Locale | |
38 | const char *f) // I - Message catalog file | |
39 | : ppdcShared() | |
40 | { | |
41 | _cups_globals_t *cg = _cupsGlobals(); | |
42 | // Global information | |
43 | ||
44 | ||
45 | locale = new ppdcString(l); | |
46 | filename = new ppdcString(f); | |
47 | messages = new ppdcArray(); | |
48 | ||
49 | if (l) | |
50 | { | |
51 | // Try loading the base messages for this locale... | |
52 | char pofile[1024]; // Message catalog file | |
53 | ||
54 | ||
55 | snprintf(pofile, sizeof(pofile), "%s/%s/ppdc_%s.po", cg->localedir, l, l); | |
56 | ||
57 | if (load_messages(pofile) && strchr(l, '_')) | |
58 | { | |
59 | // Try the base locale... | |
60 | char baseloc[3]; // Base locale... | |
61 | ||
62 | ||
63 | strlcpy(baseloc, l, sizeof(baseloc)); | |
64 | snprintf(pofile, sizeof(pofile), "%s/%s/ppdc_%s.po", cg->localedir, | |
65 | baseloc, baseloc); | |
66 | ||
67 | load_messages(pofile); | |
68 | } | |
69 | } | |
70 | ||
71 | if (f) | |
72 | load_messages(f); | |
73 | } | |
74 | ||
75 | ||
76 | // | |
77 | // 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog. | |
78 | // | |
79 | ||
80 | ppdcCatalog::~ppdcCatalog() | |
81 | { | |
82 | delete locale; | |
83 | delete filename; | |
84 | delete messages; | |
85 | } | |
86 | ||
87 | ||
88 | // | |
89 | // 'ppdcCatalog::add_message()' - Add a new message. | |
90 | // | |
91 | ||
92 | void | |
93 | ppdcCatalog::add_message(const char *id)// I - Message ID to add | |
94 | { | |
95 | ppdcMessage *m; // Current message | |
96 | char text[1024]; // Text to translate | |
97 | ||
98 | ||
99 | // Range check input... | |
100 | if (!id || !*id) | |
101 | return; | |
102 | ||
103 | // Verify that we don't already have the message ID... | |
104 | for (m = (ppdcMessage *)messages->first(); | |
105 | m; | |
106 | m = (ppdcMessage *)messages->next()) | |
107 | if (!strcmp(m->id->value, id)) | |
108 | return; | |
109 | ||
110 | // Add the message... | |
111 | snprintf(text, sizeof(text), "TRANSLATE %s", id); | |
112 | messages->add(new ppdcMessage(id, text)); | |
113 | } | |
114 | ||
115 | ||
116 | // | |
117 | // 'ppdcCatalog::find_message()' - Find a message in a catalog... | |
118 | // | |
119 | ||
120 | const char * // O - Message text | |
121 | ppdcCatalog::find_message( | |
122 | const char *id) // I - Message ID | |
123 | { | |
124 | ppdcMessage *m; // Current message | |
125 | ||
126 | ||
127 | for (m = (ppdcMessage *)messages->first(); | |
128 | m; | |
129 | m = (ppdcMessage *)messages->next()) | |
130 | if (!strcmp(m->id->value, id)) | |
131 | return (m->string->value); | |
132 | ||
133 | return (id); | |
134 | } | |
135 | ||
136 | ||
137 | // | |
138 | // 'ppdcCatalog::load_messages()' - Load messages from a .po file. | |
139 | // | |
140 | ||
141 | int // O - 0 on success, -1 on failure | |
142 | ppdcCatalog::load_messages( | |
143 | const char *f) // I - Message catalog file | |
144 | { | |
145 | cups_file_t *fp; // Message file | |
146 | ppdcMessage *temp; // Current message | |
147 | char line[4096], // Line buffer | |
148 | *ptr, // Pointer into buffer | |
149 | id[4096], // Translation ID | |
150 | str[4096]; // Translation string | |
151 | int linenum; // Line number | |
152 | ||
153 | ||
154 | // Open the message catalog file... | |
155 | if ((fp = cupsFileOpen(f, "r")) == NULL) | |
156 | return (-1); | |
157 | ||
158 | /* | |
159 | * Read messages from the catalog file until EOF... | |
160 | * | |
161 | * The format is the GNU gettext .po format, which is fairly simple: | |
162 | * | |
163 | * msgid "some text" | |
164 | * msgstr "localized text" | |
165 | * | |
166 | * The ID and localized text can span multiple lines using the form: | |
167 | * | |
168 | * msgid "" | |
169 | * "some long text" | |
170 | * msgstr "" | |
171 | * "localized text spanning " | |
172 | * "multiple lines" | |
173 | */ | |
174 | ||
175 | linenum = 0; | |
176 | id[0] = '\0'; | |
177 | str[0] = '\0'; | |
178 | ||
179 | while (cupsFileGets(fp, line, sizeof(line))) | |
180 | { | |
181 | linenum ++; | |
182 | ||
183 | // Skip blank and comment lines... | |
184 | if (line[0] == '#' || !line[0]) | |
185 | continue; | |
186 | ||
187 | // Strip the trailing quote... | |
188 | if ((ptr = strrchr(line, '\"')) == NULL) | |
189 | { | |
190 | fprintf(stderr, "load_messages: Expected quoted string on line %d of %s!\n", | |
191 | linenum, f); | |
192 | cupsFileClose(fp); | |
193 | return (-1); | |
194 | } | |
195 | ||
196 | *ptr = '\0'; | |
197 | ||
198 | // Find start of value... | |
199 | if ((ptr = strchr(line, '\"')) == NULL) | |
200 | { | |
201 | fprintf(stderr, "load_messages: Expected quoted string on line %d of %s!\n", | |
202 | linenum, f); | |
203 | cupsFileClose(fp); | |
204 | return (-1); | |
205 | } | |
206 | ||
207 | ptr ++; | |
208 | ||
209 | // Unquote the text... | |
210 | char *sptr, *dptr; // Source/destination pointers | |
211 | ||
212 | for (sptr = ptr, dptr = ptr; *sptr;) | |
213 | { | |
214 | if (*sptr == '\\') | |
215 | { | |
216 | sptr ++; | |
217 | if (isdigit(*sptr)) | |
218 | { | |
219 | *dptr = 0; | |
220 | ||
221 | while (isdigit(*sptr)) | |
222 | { | |
223 | *dptr = *dptr * 8 + *sptr - '0'; | |
224 | sptr ++; | |
225 | } | |
226 | ||
227 | dptr ++; | |
228 | } | |
229 | else | |
230 | { | |
231 | if (*sptr == 'n') | |
232 | *dptr++ = '\n'; | |
233 | else if (*sptr == 'r') | |
234 | *dptr++ = '\r'; | |
235 | else if (*sptr == 't') | |
236 | *dptr++ = '\t'; | |
237 | else | |
238 | *dptr++ = *sptr; | |
239 | ||
240 | sptr ++; | |
241 | } | |
242 | } | |
243 | else | |
244 | *dptr++ = *sptr++; | |
245 | } | |
246 | ||
247 | *dptr = '\0'; | |
248 | ||
249 | // Create or add to a message... | |
250 | if (!strncmp(line, "msgid", 5)) | |
251 | { | |
252 | if (id[0] && str[0]) | |
253 | { | |
254 | temp = new ppdcMessage(id, str); | |
255 | ||
256 | messages->add(temp); | |
257 | } | |
258 | ||
259 | strlcpy(id, ptr, sizeof(id)); | |
260 | str[0] = '\0'; | |
261 | } | |
262 | else if (!strncmp(line, "msgstr", 6)) | |
263 | { | |
264 | if (!id[0]) | |
265 | { | |
266 | fprintf(stderr, "load_messages: Need a msgid line before any " | |
267 | "translation strings on line %d of %s!\n", | |
268 | linenum, f); | |
269 | cupsFileClose(fp); | |
270 | return (-1); | |
271 | } | |
272 | ||
273 | strlcpy(str, ptr, sizeof(str)); | |
274 | } | |
275 | else if (line[0] == '\"' && str[0]) | |
276 | strlcat(str, ptr, sizeof(str)); | |
277 | else if (line[0] == '\"' && id[0]) | |
278 | strlcat(id, ptr, sizeof(id)); | |
279 | else | |
280 | { | |
281 | fprintf(stderr, "load_messages: Unexpected text on line %d of %s!\n", | |
282 | linenum, f); | |
283 | cupsFileClose(fp); | |
284 | return (-1); | |
285 | } | |
286 | } | |
287 | ||
288 | if (id[0] && str[0]) | |
289 | { | |
290 | temp = new ppdcMessage(id, str); | |
291 | ||
292 | messages->add(temp); | |
293 | } | |
294 | ||
295 | cupsFileClose(fp); | |
296 | ||
297 | return (0); | |
298 | } | |
299 | ||
300 | ||
301 | // | |
302 | // 'ppdcCatalog::save_messages()' - Save the messages to a .po file. | |
303 | // | |
304 | ||
305 | int // O - 0 on success, -1 on error | |
306 | ppdcCatalog::save_messages( | |
307 | const char *f) // I - File to save to | |
308 | { | |
309 | cups_file_t *fp; // Message file | |
310 | ppdcMessage *m; // Current message | |
311 | const char *ptr; // Pointer into string | |
312 | ||
313 | ||
314 | if ((fp = cupsFileOpen(f, "w")) == NULL) | |
315 | return (-1); | |
316 | ||
317 | for (m = (ppdcMessage *)messages->first(); | |
318 | m; | |
319 | m = (ppdcMessage *)messages->next()) | |
320 | { | |
321 | cupsFilePuts(fp, "msgid \""); | |
322 | for (ptr = m->id->value; *ptr; ptr ++) | |
323 | switch (*ptr) | |
324 | { | |
325 | case '\n' : | |
326 | cupsFilePuts(fp, "\\n"); | |
327 | break; | |
328 | case '\\' : | |
329 | cupsFilePuts(fp, "\\\\"); | |
330 | break; | |
331 | case '\"' : | |
332 | cupsFilePuts(fp, "\\\""); | |
333 | break; | |
334 | default : | |
335 | cupsFilePutChar(fp, *ptr); | |
336 | break; | |
337 | } | |
338 | cupsFilePuts(fp, "\"\n"); | |
339 | ||
340 | cupsFilePuts(fp, "msgstr \""); | |
341 | for (ptr = m->string->value; *ptr; ptr ++) | |
342 | switch (*ptr) | |
343 | { | |
344 | case '\n' : | |
345 | cupsFilePuts(fp, "\\n"); | |
346 | break; | |
347 | case '\\' : | |
348 | cupsFilePuts(fp, "\\\\"); | |
349 | break; | |
350 | case '\"' : | |
351 | cupsFilePuts(fp, "\\\""); | |
352 | break; | |
353 | default : | |
354 | cupsFilePutChar(fp, *ptr); | |
355 | break; | |
356 | } | |
357 | cupsFilePuts(fp, "\"\n"); | |
358 | ||
359 | cupsFilePutChar(fp, '\n'); | |
360 | } | |
361 | ||
362 | cupsFileClose(fp); | |
363 | ||
364 | return (0); | |
365 | } | |
366 | ||
367 | ||
368 | // | |
369 | // End of "$Id$". | |
370 | // |