]> git.ipfire.org Git - thirdparty/cups.git/blob - locale/po2strings.c
Remove all of the Subversion keywords from various source files.
[thirdparty/cups.git] / locale / po2strings.c
1 /*
2 * Convert a GNU gettext .po file to an Apple .strings file.
3 *
4 * Copyright 2007-2015 by Apple Inc.
5 *
6 * These coded instructions, statements, and computer programs are the
7 * property of Apple Inc. and are protected by Federal copyright
8 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
9 * which should have been included with this file. If this file is
10 * file is missing or damaged, see the license at "http://www.cups.org/".
11 *
12 * Usage:
13 *
14 * po2strings filename.strings filename.po
15 *
16 * Compile with:
17 *
18 * gcc -o po2strings po2strings.c `cups-config --libs`
19 */
20
21 #include <cups/cups-private.h>
22
23
24 /*
25 * The .strings file format is simple:
26 *
27 * // comment
28 * "msgid" = "msgstr";
29 *
30 * The GNU gettext .po format is also fairly simple:
31 *
32 * #. comment
33 * msgid "some text"
34 * msgstr "localized text"
35 *
36 * The comment, msgid, and msgstr text can span multiple lines using the form:
37 *
38 * #. comment
39 * #. more comments
40 * msgid ""
41 * "some long text"
42 * msgstr ""
43 * "localized text spanning "
44 * "multiple lines"
45 *
46 * Both the msgid and msgstr strings use standard C quoting for special
47 * characters like newline and the double quote character.
48 */
49
50 static char *normalize_string(const char *idstr, char *buffer, size_t bufsize);
51
52
53 /*
54 * main() - Convert .po file to .strings.
55 */
56
57 int /* O - Exit code */
58 main(int argc, /* I - Number of command-line args */
59 char *argv[]) /* I - Command-line arguments */
60 {
61 int i; /* Looping var */
62 const char *pofile, /* .po filename */
63 *stringsfile; /* .strings filename */
64 cups_file_t *po, /* .po file */
65 *strings; /* .strings file */
66 char s[4096], /* String buffer */
67 *ptr, /* Pointer into buffer */
68 *temp, /* New string */
69 *msgid, /* msgid string */
70 *msgstr, /* msgstr string */
71 normalized[8192];/* Normalized msgid string */
72 size_t length; /* Length of combined strings */
73 int use_msgid; /* Use msgid strings for msgstr? */
74
75
76 /*
77 * Process command-line arguments...
78 */
79
80 pofile = NULL;
81 stringsfile = NULL;
82 use_msgid = 0;
83
84 for (i = 1; i < argc; i ++)
85 {
86 if (!strcmp(argv[i], "-m"))
87 use_msgid = 1;
88 else if (argv[i][0] == '-')
89 {
90 puts("Usage: po2strings [-m] filename.po filename.strings");
91 return (1);
92 }
93 else if (!pofile)
94 pofile = argv[i];
95 else if (!stringsfile)
96 stringsfile = argv[i];
97 else
98 {
99 puts("Usage: po2strings [-m] filename.po filename.strings");
100 return (1);
101 }
102 }
103
104 if (!pofile || !stringsfile)
105 {
106 puts("Usage: po2strings [-m] filename.po filename.strings");
107 return (1);
108 }
109
110 /*
111 * Read strings from the .po file and write to the .strings file...
112 */
113
114 if ((po = cupsFileOpen(pofile, "r")) == NULL)
115 {
116 perror(pofile);
117 return (1);
118 }
119
120 if ((strings = cupsFileOpen(stringsfile, "w")) == NULL)
121 {
122 perror(stringsfile);
123 cupsFileClose(po);
124 return (1);
125 }
126
127 msgid = msgstr = NULL;
128
129 while (cupsFileGets(po, s, sizeof(s)) != NULL)
130 {
131 if (s[0] == '#' && s[1] == '.')
132 {
133 /*
134 * Copy comment string...
135 */
136
137 if (msgid && msgstr)
138 {
139 /*
140 * First output the last localization string...
141 */
142
143 if (*msgid)
144 cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid,
145 (use_msgid || !*msgstr) ? msgid : msgstr);
146
147 free(msgid);
148 free(msgstr);
149 msgid = msgstr = NULL;
150 }
151
152 cupsFilePrintf(strings, "//%s\n", s + 2);
153 }
154 else if (s[0] == '#' || !s[0])
155 {
156 /*
157 * Skip blank and file comment lines...
158 */
159
160 continue;
161 }
162 else
163 {
164 /*
165 * Strip the trailing quote...
166 */
167
168 if ((ptr = strrchr(s, '\"')) == NULL)
169 continue;
170
171 *ptr = '\0';
172
173 /*
174 * Find start of value...
175 */
176
177 if ((ptr = strchr(s, '\"')) == NULL)
178 continue;
179
180 ptr ++;
181
182 /*
183 * Create or add to a message...
184 */
185
186 if (!strncmp(s, "msgid", 5))
187 {
188 /*
189 * Output previous message as needed...
190 */
191
192 if (msgid && msgstr)
193 {
194 if (*msgid)
195 cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
196 }
197
198 if (msgid)
199 free(msgid);
200
201 if (msgstr)
202 free(msgstr);
203
204 msgid = strdup(ptr);
205 msgstr = NULL;
206 }
207 else if (s[0] == '\"' && (msgid || msgstr))
208 {
209 /*
210 * Append to current string...
211 */
212
213 size_t ptrlen = strlen(ptr); /* Length of string */
214
215 length = strlen(msgstr ? msgstr : msgid);
216
217 if ((temp = realloc(msgstr ? msgstr : msgid,
218 length + ptrlen + 1)) == NULL)
219 {
220 free(msgid);
221 if (msgstr)
222 free(msgstr);
223 perror("Unable to allocate string");
224 return (1);
225 }
226
227 if (msgstr)
228 {
229 /*
230 * Copy the new portion to the end of the msgstr string - safe
231 * to use strcpy because the buffer is allocated to the correct
232 * size...
233 */
234
235 msgstr = temp;
236
237 memcpy(msgstr + length, ptr, ptrlen + 1);
238 }
239 else
240 {
241 /*
242 * Copy the new portion to the end of the msgid string - safe
243 * to use strcpy because the buffer is allocated to the correct
244 * size...
245 */
246
247 msgid = temp;
248
249 memcpy(msgid + length, ptr, ptrlen + 1);
250 }
251 }
252 else if (!strncmp(s, "msgstr", 6) && msgid)
253 {
254 /*
255 * Set the string...
256 */
257
258 if (msgstr)
259 free(msgstr);
260
261 if ((msgstr = strdup(ptr)) == NULL)
262 {
263 free(msgid);
264 perror("Unable to allocate msgstr");
265 return (1);
266 }
267 }
268 }
269 }
270
271 if (msgid && msgstr)
272 {
273 if (*msgid)
274 cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
275 }
276
277 if (msgid)
278 free(msgid);
279
280 if (msgstr)
281 free(msgstr);
282
283 cupsFileClose(po);
284 cupsFileClose(strings);
285
286 return (0);
287 }
288
289
290 /*
291 * 'normalize_string()' - Normalize a msgid string.
292 *
293 * This function converts ASCII ellipsis and double quotes to their Unicode
294 * counterparts.
295 */
296
297 static char * /* O - Normalized string */
298 normalize_string(const char *idstr, /* I - msgid string */
299 char *buffer, /* I - Normalized string buffer */
300 size_t bufsize) /* I - Size of string buffer */
301 {
302 char *bufptr = buffer, /* Pointer into buffer */
303 *bufend = buffer + bufsize - 3; /* End of buffer */
304 int quote = 0, /* Quote direction */
305 html = 0; /* HTML text */
306
307
308 while (*idstr && bufptr < bufend)
309 {
310 if (!strncmp(idstr, "<A ", 3))
311 html = 1;
312 else if (html && *idstr == '>')
313 html = 0;
314
315 if (*idstr == '.' && idstr[1] == '.' && idstr[2] == '.')
316 {
317 /*
318 * Convert ... to Unicode ellipsis...
319 */
320
321 *bufptr++ = (char)0xE2;
322 *bufptr++ = (char)0x80;
323 *bufptr++ = (char)0xA6;
324 idstr += 2;
325 }
326 else if (!html && *idstr == '\\' && idstr[1] == '\"' && (quote || strchr(idstr + 2, '\"') != NULL))
327 {
328 if (quote)
329 {
330 /*
331 * Convert \" to Unicode right (curley) double quote.
332 */
333
334 *bufptr++ = (char)0xE2;
335 *bufptr++ = (char)0x80;
336 *bufptr++ = (char)0x9D;
337 }
338 else
339 {
340 /*
341 * Convert \" to Unicode left (curley) double quote.
342 */
343
344 *bufptr++ = (char)0xE2;
345 *bufptr++ = (char)0x80;
346 *bufptr++ = (char)0x9C;
347 }
348
349 quote = !quote;
350 idstr ++;
351 }
352 else
353 *bufptr++ = *idstr;
354
355 idstr ++;
356 }
357
358 *bufptr = '\0';
359
360 return (buffer);
361 }