]>
git.ipfire.org Git - thirdparty/cups.git/blob - locale/checkpo.c
4 * Verify that translations in the .po file have the same number and type of
5 * printf-style format strings.
7 * Copyright 2007-2012 by Apple Inc.
8 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
10 * These coded instructions, statements, and computer programs are the
11 * property of Apple Inc. and are protected by Federal copyright
12 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
13 * which should have been included with this file. If this file is
14 * file is missing or damaged, see the license at "http://www.cups.org/".
18 * checkpo filename.po [... filenameN.po]
22 * gcc -o checkpo checkpo.c `cups-config --libs`
26 * main() - Validate .po files.
27 * abbreviate() - Abbreviate a message string as needed.
28 * collect_formats() - Collect all of the format strings in the msgid.
29 * free_formats() - Free all of the format strings.
32 #include <cups/cups-private.h>
39 static char *abbreviate(const char *s
, char *buf
, int bufsize
);
40 static cups_array_t
*collect_formats(const char *id
);
41 static void free_formats(cups_array_t
*fmts
);
45 * 'main()' - Validate .po files.
48 int /* O - Exit code */
49 main(int argc
, /* I - Number of command-line args */
50 char *argv
[]) /* I - Command-line arguments */
52 int i
; /* Looping var */
53 cups_array_t
*po
; /* .po file */
54 _cups_message_t
*msg
; /* Current message */
55 cups_array_t
*idfmts
, /* Format strings in msgid */
56 *strfmts
; /* Format strings in msgstr */
57 char *idfmt
, /* Current msgid format string */
58 *strfmt
; /* Current msgstr format string */
59 int fmtidx
; /* Format index */
60 int status
, /* Exit status */
61 pass
, /* Pass/fail status */
62 untranslated
; /* Untranslated messages */
63 char idbuf
[80], /* Abbreviated msgid */
64 strbuf
[80]; /* Abbreviated msgstr */
69 puts("Usage: checkpo filename.po [... filenameN.po]");
74 * Check every .po file on the command-line...
77 for (i
= 1, status
= 0; i
< argc
; i
++)
80 * Use the CUPS .po loader to get the message strings...
83 if ((po
= _cupsMessageLoad(argv
[i
], 1)) == NULL
)
91 printf("%s: ", argv
[i
]);
95 * Scan every message for a % string and then match them up with
96 * the corresponding string in the translation...
102 for (msg
= (_cups_message_t
*)cupsArrayFirst(po
);
104 msg
= (_cups_message_t
*)cupsArrayNext(po
))
107 * Make sure filter message prefixes are not translated...
110 if (!strncmp(msg
->id
, "ALERT:", 6) || !strncmp(msg
->id
, "CRIT:", 5) ||
111 !strncmp(msg
->id
, "DEBUG:", 6) || !strncmp(msg
->id
, "DEBUG2:", 7) ||
112 !strncmp(msg
->id
, "EMERG:", 6) || !strncmp(msg
->id
, "ERROR:", 6) ||
113 !strncmp(msg
->id
, "INFO:", 5) || !strncmp(msg
->id
, "NOTICE:", 7) ||
114 !strncmp(msg
->id
, "WARNING:", 8))
122 printf(" Bad prefix on filter message \"%s\"\n",
123 abbreviate(msg
->id
, idbuf
, sizeof(idbuf
)));
126 idfmt
= msg
->id
+ strlen(msg
->id
) - 1;
127 if (idfmt
>= msg
->id
&& *idfmt
== '\n')
135 printf(" Trailing newline in message \"%s\"\n",
136 abbreviate(msg
->id
, idbuf
, sizeof(idbuf
)));
139 for (; idfmt
>= msg
->id
; idfmt
--)
140 if (!isspace(*idfmt
& 255))
143 if (idfmt
>= msg
->id
&& *idfmt
== '!')
151 printf(" Exclamation in message \"%s\"\n",
152 abbreviate(msg
->id
, idbuf
, sizeof(idbuf
)));
155 if ((idfmt
- 2) >= msg
->id
&& !strncmp(idfmt
- 2, "...", 3))
163 printf(" Ellipsis in message \"%s\"\n",
164 abbreviate(msg
->id
, idbuf
, sizeof(idbuf
)));
168 if (!msg
->str
|| !msg
->str
[0])
173 else if (strchr(msg
->id
, '%'))
175 idfmts
= collect_formats(msg
->id
);
176 strfmts
= collect_formats(msg
->str
);
179 for (strfmt
= (char *)cupsArrayFirst(strfmts
);
181 strfmt
= (char *)cupsArrayNext(strfmts
))
183 if (isdigit(strfmt
[1] & 255) && strfmt
[2] == '$')
186 * Handle positioned format stuff...
189 fmtidx
= strfmt
[1] - '1';
191 if ((idfmt
= (char *)cupsArrayIndex(idfmts
, fmtidx
)) != NULL
)
197 * Compare against the current format...
200 idfmt
= (char *)cupsArrayIndex(idfmts
, fmtidx
);
205 if (!idfmt
|| strcmp(strfmt
, idfmt
))
209 if (cupsArrayCount(strfmts
) != cupsArrayCount(idfmts
) || strfmt
)
217 printf(" Bad translation string \"%s\"\n for \"%s\"\n",
218 abbreviate(msg
->str
, strbuf
, sizeof(strbuf
)),
219 abbreviate(msg
->id
, idbuf
, sizeof(idbuf
)));
220 fputs(" Translation formats:", stdout
);
221 for (strfmt
= (char *)cupsArrayFirst(strfmts
);
223 strfmt
= (char *)cupsArrayNext(strfmts
))
224 printf(" %s", strfmt
);
225 fputs("\n Original formats:", stdout
);
226 for (idfmt
= (char *)cupsArrayFirst(idfmts
);
228 idfmt
= (char *)cupsArrayNext(idfmts
))
229 printf(" %s", idfmt
);
234 free_formats(idfmts
);
235 free_formats(strfmts
);
239 * Only allow \\, \n, \r, \t, \", and \### character escapes...
242 for (strfmt
= msg
->str
; *strfmt
; strfmt
++)
243 if (*strfmt
== '\\' &&
244 strfmt
[1] != '\\' && strfmt
[1] != 'n' && strfmt
[1] != 'r' &&
245 strfmt
[1] != 't' && strfmt
[1] != '\"' && !isdigit(strfmt
[1] & 255))
253 printf(" Bad escape \\%c in filter message \"%s\"\n"
254 " for \"%s\"\n", strfmt
[1],
255 abbreviate(msg
->str
, strbuf
, sizeof(strbuf
)),
256 abbreviate(msg
->id
, idbuf
, sizeof(idbuf
)));
263 if ((untranslated
* 10) >= cupsArrayCount(po
) &&
264 strcmp(argv
[i
], "cups.pot"))
267 * Only allow 10% of messages to be untranslated before we fail...
272 printf(" Too many untranslated messages (%d of %d)\n",
273 untranslated
, cupsArrayCount(po
));
275 else if (untranslated
> 0)
276 printf("PASS (%d of %d untranslated)\n", untranslated
,
285 _cupsMessageFree(po
);
293 * 'abbreviate()' - Abbreviate a message string as needed.
296 static char * /* O - Abbreviated string */
297 abbreviate(const char *s
, /* I - String to abbreviate */
298 char *buf
, /* I - Buffer */
299 int bufsize
) /* I - Size of buffer */
301 char *bufptr
; /* Pointer into buffer */
304 for (bufptr
= buf
, bufsize
-= 4; *s
&& bufsize
> 0; s
++)
324 else if (*s
>= 0 && *s
< ' ')
329 sprintf(bufptr
, "\\%03o", *s
);
341 memcpy(bufptr
, "...", 4);
350 * 'collect_formats()' - Collect all of the format strings in the msgid.
353 static cups_array_t
* /* O - Array of format strings */
354 collect_formats(const char *id
) /* I - msgid string */
356 cups_array_t
*fmts
; /* Array of format strings */
357 char buf
[255], /* Format string buffer */
358 *bufptr
; /* Pointer into format string */
361 fmts
= cupsArrayNew(NULL
, NULL
);
363 while ((id
= strchr(id
, '%')) != NULL
)
375 for (bufptr
= buf
; *id
&& bufptr
< (buf
+ sizeof(buf
) - 1); id
++)
379 if (strchr("CDEFGIOSUXcdeifgopsux", *id
))
387 cupsArrayAdd(fmts
, strdup(buf
));
395 * 'free_formats()' - Free all of the format strings.
399 free_formats(cups_array_t
*fmts
) /* I - Array of format strings */
401 char *s
; /* Current string */
404 for (s
= (char *)cupsArrayFirst(fmts
); s
; s
= (char *)cupsArrayNext(fmts
))
407 cupsArrayDelete(fmts
);