]> git.ipfire.org Git - thirdparty/cups.git/blob - ppdc/ppdc-catalog.cxx
Merge CUPS 1.4svn-r7319.
[thirdparty/cups.git] / ppdc / ppdc-catalog.cxx
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 //