]> git.ipfire.org Git - thirdparty/cups.git/blame - locale/checkpo.c
Import CUPS 1.4svn-r7226.
[thirdparty/cups.git] / locale / checkpo.c
CommitLineData
c277e2f8 1/*
2e4ff8af 2 * "$Id: checkpo.c 6922 2007-09-06 14:18:02Z mike $"
c277e2f8
MS
3 *
4 * Verify that translations in the .po file have the same number and type of
5 * printf-style format strings.
6 *
7 * Usage:
8 *
9 * checkpo filename.po [... filenameN.po]
10 *
11 * Compile with:
12 *
13 * gcc -o checkpo checkpo.c `cups-config --libs`
14 *
15 * Contents:
16 *
17 * main() - Validate .po files.
18 * abbreviate() - Abbreviate a message string as needed.
19 * collect_formats() - Collect all of the format strings in the msgid.
20 * free_formats() - Free all of the format strings.
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <cups/string.h>
26#include <cups/i18n.h>
27
28
29/*
30 * Local functions...
31 */
32
33static char *abbreviate(const char *s, char *buf, int bufsize);
34static cups_array_t *collect_formats(const char *id);
35static void free_formats(cups_array_t *fmts);
36
37
38/*
39 * 'main()' - Validate .po files.
40 */
41
42int /* O - Exit code */
43main(int argc, /* I - Number of command-line args */
44 char *argv[]) /* I - Command-line arguments */
45{
46 int i; /* Looping var */
47 cups_array_t *po; /* .po file */
48 _cups_message_t *msg; /* Current message */
49 cups_array_t *idfmts, /* Format strings in msgid */
50 *strfmts; /* Format strings in msgstr */
51 char *idfmt, /* Current msgid format string */
52 *strfmt; /* Current msgstr format string */
91c84a35 53 int fmtidx; /* Format index */
c277e2f8
MS
54 int status, /* Exit status */
55 pass, /* Pass/fail status */
56 untranslated; /* Untranslated messages */
57 char idbuf[80], /* Abbreviated msgid */
58 strbuf[80]; /* Abbreviated msgstr */
59
60
61 if (argc < 2)
62 {
63 puts("Usage: checkpo filename.po [... filenameN.po]");
64 return (1);
65 }
66
67 /*
68 * Check every .po file on the command-line...
69 */
70
71 for (i = 1, status = 0; i < argc; i ++)
72 {
73 /*
74 * Use the CUPS .po loader to get the message strings...
75 */
76
77 if ((po = _cupsMessageLoad(argv[i])) == NULL)
78 {
79 perror(argv[i]);
80 return (1);
81 }
82
83 printf("%s: ", argv[i]);
84 fflush(stdout);
85
86 /*
87 * Scan every message for a % string and then match them up with
88 * the corresponding string in the translation...
89 */
90
91 pass = 1;
92 untranslated = 0;
93
94 for (msg = (_cups_message_t *)cupsArrayFirst(po);
95 msg;
96 msg = (_cups_message_t *)cupsArrayNext(po))
97 {
98 if (!msg->str || !msg->str[0])
99 {
100 untranslated ++;
101 continue;
102 }
103 else if (strchr(msg->id, '%'))
104 {
105 idfmts = collect_formats(msg->id);
106 strfmts = collect_formats(msg->str);
107 fmtidx = 0;
108
109 for (strfmt = (char *)cupsArrayFirst(strfmts);
110 strfmt;
111 strfmt = (char *)cupsArrayNext(strfmts))
112 {
113 if (isdigit(strfmt[1] & 255) && strfmt[2] == '$')
114 {
115 /*
116 * Handle positioned format stuff...
117 */
118
119 fmtidx = strfmt[1] - '1';
120 strfmt += 3;
121 if ((idfmt = (char *)cupsArrayIndex(idfmts, fmtidx)) != NULL)
122 idfmt ++;
123 }
124 else
125 {
126 /*
127 * Compare against the current format...
128 */
129
130 idfmt = (char *)cupsArrayIndex(idfmts, fmtidx);
131 }
132
133 fmtidx ++;
134
135 if (!idfmt || strcmp(strfmt, idfmt))
136 break;
c277e2f8
MS
137 }
138
139 if (cupsArrayCount(strfmts) != cupsArrayCount(idfmts) || strfmt)
140 {
141 if (pass)
142 {
143 pass = 0;
144 puts("FAIL");
145 }
146
147 printf(" Bad translation string \"%s\"\n for \"%s\"\n",
148 abbreviate(msg->str, strbuf, sizeof(strbuf)),
149 abbreviate(msg->id, idbuf, sizeof(idbuf)));
150 fputs(" Translation formats:", stdout);
151 for (strfmt = (char *)cupsArrayFirst(strfmts);
152 strfmt;
153 strfmt = (char *)cupsArrayNext(strfmts))
154 printf(" %s", strfmt);
155 fputs("\n Original formats:", stdout);
156 for (idfmt = (char *)cupsArrayFirst(idfmts);
157 idfmt;
158 idfmt = (char *)cupsArrayNext(idfmts))
159 printf(" %s", idfmt);
160 putchar('\n');
161 }
162
163 free_formats(idfmts);
164 free_formats(strfmts);
165 }
166
167 if ((!strncmp(msg->id, "ALERT:", 6) && strncmp(msg->str, "ALERT:", 6)) ||
168 (!strncmp(msg->id, "CRIT:", 5) && strncmp(msg->str, "CRIT:", 5)) ||
169 (!strncmp(msg->id, "DEBUG:", 6) && strncmp(msg->str, "DEBUG:", 6)) ||
170 (!strncmp(msg->id, "DEBUG2:", 7) && strncmp(msg->str, "DEBUG2:", 7)) ||
171 (!strncmp(msg->id, "EMERG:", 6) && strncmp(msg->str, "EMERG:", 6)) ||
172 (!strncmp(msg->id, "ERROR:", 6) && strncmp(msg->str, "ERROR:", 6)) ||
173 (!strncmp(msg->id, "INFO:", 5) && strncmp(msg->str, "INFO:", 5)) ||
174 (!strncmp(msg->id, "NOTICE:", 7) && strncmp(msg->str, "NOTICE:", 7)) ||
175 (!strncmp(msg->id, "WARNING:", 8) && strncmp(msg->str, "WARNING:", 8)))
176 {
177 if (pass)
178 {
179 pass = 0;
180 puts("FAIL");
181 }
182
183 printf(" Bad prefix on filter message \"%s\"\n for \"%s\"\n",
184 abbreviate(msg->str, strbuf, sizeof(strbuf)),
185 abbreviate(msg->id, idbuf, sizeof(idbuf)));
186 }
187 }
188
189 if (pass)
190 {
191 if ((untranslated * 10) >= cupsArrayCount(po))
192 {
193 /*
194 * Only allow 10% of messages to be untranslated before we fail...
195 */
196
197 pass = 0;
198 puts("FAIL");
199 printf(" Too many untranslated messages (%d of %d)\n", untranslated,
200 cupsArrayCount(po));
201 }
202 else if (untranslated > 0)
203 printf("PASS (%d of %d untranslated)\n", untranslated,
204 cupsArrayCount(po));
205 else
206 puts("PASS");
207 }
208
209 if (!pass)
210 status = 1;
211
212 _cupsMessageFree(po);
213 }
214
215 return (status);
216}
217
218
219/*
220 * 'abbreviate()' - Abbreviate a message string as needed.
221 */
222
223static char * /* O - Abbreviated string */
224abbreviate(const char *s, /* I - String to abbreviate */
225 char *buf, /* I - Buffer */
226 int bufsize) /* I - Size of buffer */
227{
228 char *bufptr; /* Pointer into buffer */
229
230
231 for (bufptr = buf, bufsize -= 4; *s && bufsize > 0; s ++)
232 {
233 if (*s == '\n')
234 {
235 if (bufsize < 2)
236 break;
237
238 *bufptr++ = '\\';
239 *bufptr++ = 'n';
240 bufsize -= 2;
241 }
242 else if (*s == '\t')
243 {
244 if (bufsize < 2)
245 break;
246
247 *bufptr++ = '\\';
248 *bufptr++ = 't';
249 bufsize -= 2;
250 }
251 else if (*s >= 0 && *s < ' ')
252 {
253 if (bufsize < 4)
254 break;
255
256 sprintf(bufptr, "\\%03o", *s);
257 bufptr += 4;
258 bufsize -= 4;
259 }
260 else
261 {
262 *bufptr++ = *s;
263 bufsize --;
264 }
265 }
266
267 if (*s)
268 strcpy(bufptr, "...");
269 else
270 *bufptr = '\0';
271
272 return (buf);
273}
274
275
276/*
277 * 'collect_formats()' - Collect all of the format strings in the msgid.
278 */
279
280static cups_array_t * /* O - Array of format strings */
281collect_formats(const char *id) /* I - msgid string */
282{
283 cups_array_t *fmts; /* Array of format strings */
284 char buf[255], /* Format string buffer */
285 *bufptr; /* Pointer into format string */
286
287
288 fmts = cupsArrayNew(NULL, NULL);
289
290 while ((id = strchr(id, '%')) != NULL)
291 {
292 if (id[1] == '%')
293 {
294 /*
295 * Skip %%...
296 */
297
298 id += 2;
299 continue;
300 }
301
302 for (bufptr = buf; *id && bufptr < (buf + sizeof(buf) - 1); id ++)
303 {
304 *bufptr++ = *id;
305
306 if (strchr("CDEFGIOSUXcdeifgopsux", *id))
307 {
308 id ++;
309 break;
310 }
311 }
312
313 *bufptr = '\0';
314 cupsArrayAdd(fmts, strdup(buf));
315 }
316
317 return (fmts);
318}
319
320
321/*
322 * 'free_formats()' - Free all of the format strings.
323 */
324
325static void
326free_formats(cups_array_t *fmts) /* I - Array of format strings */
327{
328 char *s; /* Current string */
329
330
331 for (s = (char *)cupsArrayFirst(fmts); s; s = (char *)cupsArrayNext(fmts))
332 free(s);
333
334 cupsArrayDelete(fmts);
335}
336
337
338/*
2e4ff8af 339 * End of "$Id: checkpo.c 6922 2007-09-06 14:18:02Z mike $".
c277e2f8 340 */