]> git.ipfire.org Git - thirdparty/cups.git/blame - locale/po2strings.c
Fix source file header text duplication text duplication.
[thirdparty/cups.git] / locale / po2strings.c
CommitLineData
bc44d920 1/*
7e86f2f6 2 * Convert a GNU gettext .po file to an Apple .strings file.
71e16022 3 *
3b20f9f5 4 * Copyright 2007-2015 by Apple Inc.
71e16022 5 *
7e86f2f6
MS
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
57b7b66b 10 * missing or damaged, see the license at "http://www.cups.org/".
bc44d920 11 *
12 * Usage:
13 *
14 * po2strings filename.strings filename.po
15 *
16 * Compile with:
17 *
18 * gcc -o po2strings po2strings.c `cups-config --libs`
bc44d920 19 */
20
71e16022 21#include <cups/cups-private.h>
bc44d920 22
23
24/*
25 * The .strings file format is simple:
26 *
27 * // comment
0837b7e8 28 * "msgid" = "msgstr";
bc44d920 29 *
0837b7e8
MS
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.
bc44d920 48 */
49
3b20f9f5
MS
50static char *normalize_string(const char *idstr, char *buffer, size_t bufsize);
51
52
bc44d920 53/*
54 * main() - Convert .po file to .strings.
55 */
56
57int /* O - Exit code */
58main(int argc, /* I - Number of command-line args */
59 char *argv[]) /* I - Command-line arguments */
60{
745129be 61 int i; /* Looping var */
0837b7e8
MS
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 */
3b20f9f5
MS
70 *msgstr, /* msgstr string */
71 normalized[8192];/* Normalized msgid string */
7e86f2f6 72 size_t length; /* Length of combined strings */
745129be 73 int use_msgid; /* Use msgid strings for msgstr? */
bc44d920 74
75
0837b7e8
MS
76 /*
77 * Process command-line arguments...
78 */
745129be 79
0837b7e8
MS
80 pofile = NULL;
81 stringsfile = NULL;
82 use_msgid = 0;
745129be
MS
83
84 for (i = 1; i < argc; i ++)
0837b7e8 85 {
745129be
MS
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 }
0837b7e8
MS
93 else if (!pofile)
94 pofile = argv[i];
95 else if (!stringsfile)
96 stringsfile = argv[i];
745129be
MS
97 else
98 {
99 puts("Usage: po2strings [-m] filename.po filename.strings");
100 return (1);
101 }
0837b7e8 102 }
745129be 103
0837b7e8 104 if (!pofile || !stringsfile)
bc44d920 105 {
745129be 106 puts("Usage: po2strings [-m] filename.po filename.strings");
bc44d920 107 return (1);
108 }
109
110 /*
0837b7e8 111 * Read strings from the .po file and write to the .strings file...
bc44d920 112 */
113
0837b7e8 114 if ((po = cupsFileOpen(pofile, "r")) == NULL)
bc44d920 115 {
0837b7e8 116 perror(pofile);
bc44d920 117 return (1);
118 }
119
0837b7e8 120 if ((strings = cupsFileOpen(stringsfile, "w")) == NULL)
bc44d920 121 {
0837b7e8
MS
122 perror(stringsfile);
123 cupsFileClose(po);
bc44d920 124 return (1);
125 }
126
0837b7e8
MS
127 msgid = msgstr = NULL;
128
129 while (cupsFileGets(po, s, sizeof(s)) != NULL)
bc44d920 130 {
84315f46 131 if (s[0] == '#' && s[1] == '.')
0837b7e8
MS
132 {
133 /*
84315f46 134 * Copy comment string...
0837b7e8 135 */
bc44d920 136
84315f46
MS
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);
0837b7e8 153 }
84315f46 154 else if (s[0] == '#' || !s[0])
0837b7e8
MS
155 {
156 /*
84315f46 157 * Skip blank and file comment lines...
0837b7e8 158 */
bc44d920 159
84315f46 160 continue;
0837b7e8
MS
161 }
162 else
163 {
164 /*
165 * Strip the trailing quote...
166 */
bc44d920 167
0837b7e8
MS
168 if ((ptr = strrchr(s, '\"')) == NULL)
169 continue;
bc44d920 170
0837b7e8 171 *ptr = '\0';
bc44d920 172
0837b7e8
MS
173 /*
174 * Find start of value...
175 */
82cc1f9a 176
0837b7e8
MS
177 if ((ptr = strchr(s, '\"')) == NULL)
178 continue;
bc44d920 179
0837b7e8 180 ptr ++;
bc44d920 181
0837b7e8
MS
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)
3b20f9f5 195 cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
82cc1f9a 196 }
0837b7e8 197
82cc1f9a 198 if (msgid)
0837b7e8 199 free(msgid);
82cc1f9a
MS
200
201 if (msgstr)
0837b7e8 202 free(msgstr);
0837b7e8
MS
203
204 msgid = strdup(ptr);
205 msgstr = NULL;
206 }
82cc1f9a 207 else if (s[0] == '\"' && (msgid || msgstr))
0837b7e8
MS
208 {
209 /*
210 * Append to current string...
211 */
212
5a9febac
MS
213 size_t ptrlen = strlen(ptr); /* Length of string */
214
7e86f2f6 215 length = strlen(msgstr ? msgstr : msgid);
0837b7e8
MS
216
217 if ((temp = realloc(msgstr ? msgstr : msgid,
5a9febac 218 length + ptrlen + 1)) == NULL)
0837b7e8 219 {
82cc1f9a
MS
220 free(msgid);
221 if (msgstr)
222 free(msgstr);
0837b7e8
MS
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
5a9febac 237 memcpy(msgstr + length, ptr, ptrlen + 1);
0837b7e8
MS
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
5a9febac 249 memcpy(msgid + length, ptr, ptrlen + 1);
0837b7e8
MS
250 }
251 }
252 else if (!strncmp(s, "msgstr", 6) && msgid)
253 {
254 /*
255 * Set the string...
256 */
257
82cc1f9a
MS
258 if (msgstr)
259 free(msgstr);
260
0837b7e8
MS
261 if ((msgstr = strdup(ptr)) == NULL)
262 {
82cc1f9a 263 free(msgid);
0837b7e8
MS
264 perror("Unable to allocate msgstr");
265 return (1);
266 }
267 }
bc44d920 268 }
0837b7e8
MS
269 }
270
271 if (msgid && msgstr)
272 {
273 if (*msgid)
3b20f9f5 274 cupsFilePrintf(strings, "\"%s\" = \"%s\";\n", msgid, normalize_string((use_msgid || !*msgstr) ? msgid : msgstr, normalized, sizeof(normalized)));
82cc1f9a 275 }
bc44d920 276
82cc1f9a 277 if (msgid)
0837b7e8 278 free(msgid);
82cc1f9a
MS
279
280 if (msgstr)
0837b7e8 281 free(msgstr);
bc44d920 282
0837b7e8
MS
283 cupsFileClose(po);
284 cupsFileClose(strings);
285
286 return (0);
bc44d920 287}
288
289
3b20f9f5
MS
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
297static char * /* O - Normalized string */
298normalize_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 */
08b1b9ba
MS
304 int quote = 0, /* Quote direction */
305 html = 0; /* HTML text */
3b20f9f5
MS
306
307
308 while (*idstr && bufptr < bufend)
309 {
08b1b9ba
MS
310 if (!strncmp(idstr, "<A ", 3))
311 html = 1;
312 else if (html && *idstr == '>')
313 html = 0;
314
3b20f9f5
MS
315 if (*idstr == '.' && idstr[1] == '.' && idstr[2] == '.')
316 {
317 /*
71936a32 318 * Convert ... to Unicode ellipsis...
3b20f9f5
MS
319 */
320
321 *bufptr++ = (char)0xE2;
322 *bufptr++ = (char)0x80;
323 *bufptr++ = (char)0xA6;
324 idstr += 2;
325 }
08b1b9ba 326 else if (!html && *idstr == '\\' && idstr[1] == '\"' && (quote || strchr(idstr + 2, '\"') != NULL))
3b20f9f5
MS
327 {
328 if (quote)
329 {
330 /*
71936a32 331 * Convert \" to Unicode right (curley) double quote.
3b20f9f5
MS
332 */
333
334 *bufptr++ = (char)0xE2;
335 *bufptr++ = (char)0x80;
336 *bufptr++ = (char)0x9D;
337 }
338 else
339 {
340 /*
71936a32 341 * Convert \" to Unicode left (curley) double quote.
3b20f9f5
MS
342 */
343
344 *bufptr++ = (char)0xE2;
345 *bufptr++ = (char)0x80;
346 *bufptr++ = (char)0x9C;
347 }
348
349 quote = !quote;
71936a32 350 idstr ++;
3b20f9f5
MS
351 }
352 else
353 *bufptr++ = *idstr;
354
355 idstr ++;
356 }
357
358 *bufptr = '\0';
359
360 return (buffer);
361}