]>
Commit | Line | Data |
---|---|---|
ac884b6a | 1 | // |
7e86f2f6 | 2 | // PPD file merge utility for the CUPS PPD Compiler. |
ac884b6a | 3 | // |
70bdce16 MS |
4 | // Copyright © 2007-2018 by Apple Inc. |
5 | // Copyright © 2002-2007 by Easy Software Products. | |
ac884b6a | 6 | // |
70bdce16 MS |
7 | // Licensed under Apache License v2.0. See the file "LICENSE" for more |
8 | // information. | |
ac884b6a MS |
9 | // |
10 | ||
11 | // | |
12 | // Include necessary headers... | |
13 | // | |
14 | ||
71e16022 | 15 | #include <cups/cups-private.h> |
61cf44e2 | 16 | #include <cups/ppd-private.h> |
ac884b6a | 17 | #include <cups/array.h> |
ac884b6a MS |
18 | |
19 | ||
20 | // | |
21 | // Local functions... | |
22 | // | |
23 | ||
24 | static const char *ppd_locale(ppd_file_t *ppd); | |
10d19ff1 | 25 | static void usage(void) _CUPS_NORETURN; |
ac884b6a MS |
26 | |
27 | ||
28 | // | |
29 | // 'main()' - Main entry for the PPD merge utility. | |
30 | // | |
31 | ||
32 | int // O - Exit status | |
33 | main(int argc, // I - Number of command-line arguments | |
34 | char *argv[]) // I - Command-line arguments | |
35 | { | |
36 | int i; // Looping var | |
37 | char *opt; // Current option | |
38 | ppd_file_t *ppd; // PPD file | |
39 | cups_array_t *ppds; // Array of PPD files | |
40 | const char *inname, // First input filename | |
41 | *outname; // Output filename (if any) | |
70bdce16 | 42 | char bckname[1024]; // Backup filename |
ac884b6a MS |
43 | cups_file_t *infile, // Input file |
44 | *outfile; // Output file | |
61cf44e2 MS |
45 | cups_array_t *languages; // Languages in file |
46 | const char *locale; // Current locale | |
47 | char line[1024]; // Line from file | |
ac884b6a MS |
48 | |
49 | ||
61cf44e2 MS |
50 | _cupsSetLocale(argv); |
51 | ||
ac884b6a | 52 | // Scan the command-line... |
61cf44e2 MS |
53 | inname = NULL; |
54 | outname = NULL; | |
55 | outfile = NULL; | |
56 | languages = NULL; | |
57 | ppds = cupsArrayNew(NULL, NULL); | |
ac884b6a MS |
58 | |
59 | for (i = 1; i < argc; i ++) | |
60 | if (argv[i][0] == '-') | |
61 | { | |
62 | for (opt = argv[i] + 1; *opt; opt ++) | |
63 | switch (*opt) | |
64 | { | |
65 | case 'o' : // Output file | |
66 | if (outname) | |
67 | usage(); | |
68 | ||
69 | i ++; | |
70 | if (i >= argc) | |
71 | usage(); | |
72 | ||
73 | outname = argv[i]; | |
74 | break; | |
75 | ||
76 | default : // Unknown | |
77 | usage(); | |
78 | break; | |
79 | } | |
80 | } | |
81 | else | |
82 | { | |
61cf44e2 | 83 | // Open and load the PPD file... |
ac884b6a MS |
84 | if ((infile = cupsFileOpen(argv[i], "r")) == NULL) |
85 | { | |
0837b7e8 | 86 | _cupsLangPrintf(stderr, _("%s: Unable to open %s: %s"), "ppdmerge", |
61cf44e2 MS |
87 | argv[i], strerror(errno)); |
88 | return (1); | |
ac884b6a MS |
89 | } |
90 | ||
91 | // Open the PPD file... | |
92 | if ((ppd = ppdOpen2(infile)) == NULL) | |
93 | { | |
94 | ppd_status_t status; // PPD open status | |
0837b7e8 MS |
95 | int curline, // Current line |
96 | linenum; // Line number | |
88f9aafc MS |
97 | |
98 | ||
ac884b6a | 99 | status = ppdLastError(&linenum); |
88f9aafc | 100 | |
0837b7e8 MS |
101 | _cupsLangPrintf(stderr, |
102 | _("%s: Unable to open PPD file: %s on line %d."), | |
f0ab5bff | 103 | "ppdmerge", ppdErrorString(status), linenum); |
ac884b6a | 104 | cupsFileRewind(infile); |
88f9aafc | 105 | |
ac884b6a | 106 | line[0] = '\0'; |
0837b7e8 | 107 | curline = 0; |
88f9aafc | 108 | |
ac884b6a MS |
109 | while (cupsFileGets(infile, line, sizeof(line))) |
110 | { | |
0837b7e8 MS |
111 | curline ++; |
112 | if (curline >= linenum) | |
ac884b6a MS |
113 | break; |
114 | } | |
0837b7e8 MS |
115 | |
116 | _cupsLangPrintf(stderr, "%d: %s", linenum, line); | |
117 | ||
ac884b6a | 118 | cupsFileClose(infile); |
61cf44e2 | 119 | return (1); |
ac884b6a | 120 | } |
88f9aafc | 121 | |
ac884b6a | 122 | // Figure out the locale... |
61cf44e2 | 123 | if ((locale = ppd_locale(ppd)) == NULL) |
ac884b6a | 124 | { |
61cf44e2 | 125 | _cupsLangPrintf(stderr, |
0837b7e8 | 126 | _("ppdmerge: Bad LanguageVersion \"%s\" in %s."), |
61cf44e2 | 127 | ppd->lang_version, argv[i]); |
ac884b6a MS |
128 | cupsFileClose(infile); |
129 | ppdClose(ppd); | |
61cf44e2 | 130 | return (1); |
ac884b6a | 131 | } |
61cf44e2 | 132 | |
ac884b6a MS |
133 | if (!strcmp(locale, "en") && !inname && !outfile) |
134 | { | |
135 | // Set the English PPD's filename... | |
61cf44e2 MS |
136 | inname = argv[i]; |
137 | languages = _ppdGetLanguages(ppd); | |
138 | ||
ac884b6a | 139 | if (outname && !strcmp(inname, outname)) |
88f9aafc | 140 | { |
ac884b6a | 141 | // Rename input filename so that we don't overwrite it... |
ac884b6a | 142 | snprintf(bckname, sizeof(bckname), "%s.bck", inname); |
88f9aafc | 143 | |
ac884b6a MS |
144 | if (rename(inname, bckname)) |
145 | { | |
61cf44e2 | 146 | _cupsLangPrintf(stderr, |
0837b7e8 | 147 | _("ppdmerge: Unable to backup %s to %s - %s"), |
61cf44e2 | 148 | inname, bckname, strerror(errno)); |
ac884b6a MS |
149 | return (1); |
150 | } | |
ac884b6a | 151 | |
61cf44e2 | 152 | inname = bckname; |
ac884b6a MS |
153 | } |
154 | } | |
61cf44e2 | 155 | else if (strcmp(locale, "en")) |
ac884b6a MS |
156 | { |
157 | // Save this PPD for later processing... | |
158 | cupsArrayAdd(ppds, ppd); | |
159 | } | |
160 | else | |
161 | { | |
162 | // Don't need this PPD... | |
0837b7e8 | 163 | _cupsLangPrintf(stderr, _("ppdmerge: Ignoring PPD file %s."), |
61cf44e2 | 164 | argv[i]); |
ac884b6a MS |
165 | ppdClose(ppd); |
166 | } | |
88f9aafc | 167 | |
ac884b6a MS |
168 | // Close and move on... |
169 | cupsFileClose(infile); | |
170 | } | |
171 | ||
172 | // If no PPDs have been loaded, display the program usage message. | |
173 | if (!inname) | |
174 | usage(); | |
61cf44e2 MS |
175 | |
176 | // Loop through the PPD files we loaded to generate a new language list... | |
177 | if (!languages) | |
178 | languages = cupsArrayNew((cups_array_func_t)strcmp, NULL); | |
179 | ||
180 | for (ppd = (ppd_file_t *)cupsArrayFirst(ppds); | |
181 | ppd; | |
182 | ppd = (ppd_file_t *)cupsArrayNext(ppds)) | |
183 | { | |
184 | locale = ppd_locale(ppd); | |
185 | ||
186 | if (cupsArrayFind(languages, (void *)locale)) | |
187 | { | |
188 | // Already have this language, remove the PPD from the list. | |
189 | ppdClose(ppd); | |
190 | cupsArrayRemove(ppds, ppd); | |
191 | } | |
192 | else | |
193 | cupsArrayAdd(languages, (void *)locale); | |
194 | } | |
195 | ||
196 | // Copy the English PPD starting with a cupsLanguages line... | |
197 | infile = cupsFileOpen(inname, "r"); | |
198 | ||
199 | if (outname) | |
200 | { | |
201 | const char *ext = strrchr(outname, '.'); | |
202 | if (ext && !strcmp(ext, ".gz")) | |
203 | outfile = cupsFileOpen(outname, "w9"); | |
204 | else | |
205 | outfile = cupsFileOpen(outname, "w"); | |
206 | } | |
207 | else | |
208 | outfile = cupsFileStdout(); | |
209 | ||
210 | cupsFileGets(infile, line, sizeof(line)); | |
211 | cupsFilePrintf(outfile, "%s\n", line); | |
212 | if ((locale = (char *)cupsArrayFirst(languages)) != NULL) | |
213 | { | |
214 | cupsFilePrintf(outfile, "*cupsLanguages: \"%s", locale); | |
215 | while ((locale = (char *)cupsArrayNext(languages)) != NULL) | |
216 | cupsFilePrintf(outfile, " %s", locale); | |
217 | cupsFilePuts(outfile, "\"\n"); | |
218 | } | |
219 | ||
220 | while (cupsFileGets(infile, line, sizeof(line))) | |
221 | { | |
222 | if (strncmp(line, "*cupsLanguages:", 15)) | |
223 | cupsFilePrintf(outfile, "%s\n", line); | |
224 | } | |
225 | ||
226 | // Loop through the other PPD files we loaded to provide the translations... | |
ac884b6a MS |
227 | for (ppd = (ppd_file_t *)cupsArrayFirst(ppds); |
228 | ppd; | |
229 | ppd = (ppd_file_t *)cupsArrayNext(ppds)) | |
230 | { | |
231 | // Output all of the UI text for this language... | |
232 | int j, k, l; // Looping vars | |
233 | ppd_group_t *g; // Option group | |
234 | ppd_option_t *o; // Option | |
235 | ppd_choice_t *c; // Choice | |
61cf44e2 MS |
236 | ppd_coption_t *co; // Custom option |
237 | ppd_cparam_t *cp; // Custom parameter | |
238 | ppd_attr_t *attr; // PPD attribute | |
239 | ||
240 | locale = ppd_locale(ppd); | |
ac884b6a | 241 | |
ac884b6a | 242 | cupsFilePrintf(outfile, "*%% %s localization\n", ppd->lang_version); |
ac884b6a MS |
243 | cupsFilePrintf(outfile, "*%s.Translation ModelName/%s: \"\"\n", locale, |
244 | ppd->modelname); | |
61cf44e2 | 245 | |
ac884b6a MS |
246 | for (j = ppd->num_groups, g = ppd->groups; j > 0; j --, g ++) |
247 | { | |
248 | cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale, | |
249 | g->name, g->text); | |
61cf44e2 | 250 | |
ac884b6a MS |
251 | for (k = g->num_options, o = g->options; k > 0; k --, o ++) |
252 | { | |
253 | cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale, | |
254 | o->keyword, o->text); | |
61cf44e2 | 255 | |
ac884b6a MS |
256 | for (l = o->num_choices, c = o->choices; l > 0; l --, c ++) |
257 | cupsFilePrintf(outfile, "*%s.%s %s/%s: \"\"\n", locale, | |
258 | o->keyword, c->choice, c->text); | |
61cf44e2 MS |
259 | |
260 | if ((co = ppdFindCustomOption(ppd, o->keyword)) != NULL) | |
261 | { | |
262 | snprintf(line, sizeof(line), "Custom%s", o->keyword); | |
263 | attr = ppdFindAttr(ppd, line, "True"); | |
264 | cupsFilePrintf(outfile, "*%s.Custom%s True/%s: \"\"\n", locale, | |
265 | o->keyword, attr->text); | |
266 | for (cp = ppdFirstCustomParam(co); cp; cp = ppdNextCustomParam(co)) | |
267 | cupsFilePrintf(outfile, "*%s.ParamCustom%s %s/%s: \"\"\n", locale, | |
268 | o->keyword, cp->name, cp->text); | |
269 | } | |
ac884b6a MS |
270 | } |
271 | } | |
ac884b6a MS |
272 | |
273 | ppdClose(ppd); | |
274 | } | |
275 | ||
276 | cupsArrayDelete(ppds); | |
277 | ||
ac884b6a MS |
278 | cupsFileClose(outfile); |
279 | ||
280 | // Return with no errors. | |
281 | return (0); | |
ac884b6a MS |
282 | } |
283 | ||
284 | ||
285 | // | |
286 | // 'ppd_locale()' - Return the locale associated with a PPD file. | |
287 | // | |
288 | ||
289 | static const char * // O - Locale string | |
290 | ppd_locale(ppd_file_t *ppd) // I - PPD file | |
291 | { | |
7e86f2f6 MS |
292 | int i; // Looping var |
293 | size_t vlen; // Length of LanguageVersion string | |
ac884b6a MS |
294 | static char locale[255]; // Locale string |
295 | static struct // LanguageVersion translation table | |
296 | { | |
297 | const char *version, // LanguageVersion string */ | |
298 | *language; // Language code */ | |
299 | } languages[] = | |
300 | { | |
dd1abb6b MS |
301 | { "chinese", "zh" }, |
302 | { "czech", "cs" }, | |
303 | { "danish", "da" }, | |
304 | { "dutch", "nl" }, | |
305 | { "english", "en" }, | |
306 | { "finnish", "fi" }, | |
307 | { "french", "fr" }, | |
308 | { "german", "de" }, | |
309 | { "greek", "el" }, | |
310 | { "hungarian", "hu" }, | |
311 | { "italian", "it" }, | |
312 | { "japanese", "ja" }, | |
313 | { "korean", "ko" }, | |
314 | { "norwegian", "no" }, | |
315 | { "polish", "pl" }, | |
316 | { "portuguese", "pt" }, | |
317 | { "russian", "ru" }, | |
318 | { "simplified chinese", "zh_CN" }, | |
319 | { "slovak", "sk" }, | |
320 | { "spanish", "es" }, | |
321 | { "swedish", "sv" }, | |
322 | { "traditional chinese", "zh_TW" }, | |
323 | { "turkish", "tr" } | |
ac884b6a MS |
324 | }; |
325 | ||
326 | ||
327 | for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++) | |
328 | { | |
329 | vlen = strlen(languages[i].version); | |
330 | ||
88f9aafc | 331 | if (!_cups_strncasecmp(ppd->lang_version, languages[i].version, vlen)) |
ac884b6a MS |
332 | { |
333 | if (ppd->lang_version[vlen] == '-' || | |
334 | ppd->lang_version[vlen] == '_') | |
335 | snprintf(locale, sizeof(locale), "%s_%s", languages[i].language, | |
336 | ppd->lang_version + vlen + 1); | |
337 | else | |
338 | strlcpy(locale, languages[i].language, sizeof(locale)); | |
339 | ||
340 | return (locale); | |
341 | } | |
342 | } | |
343 | ||
344 | return (NULL); | |
345 | } | |
346 | ||
347 | // | |
348 | // 'usage()' - Show usage and exit. | |
349 | // | |
350 | ||
351 | static void | |
352 | usage(void) | |
353 | { | |
0837b7e8 MS |
354 | _cupsLangPuts(stdout, _("Usage: ppdmerge [options] filename.ppd [ ... " |
355 | "filenameN.ppd ]")); | |
356 | _cupsLangPuts(stdout, _("Options:")); | |
84315f46 MS |
357 | _cupsLangPuts(stdout, _(" -o filename.ppd[.gz] Set output file " |
358 | "(otherwise stdout).")); | |
ac884b6a MS |
359 | |
360 | exit(1); | |
361 | } |