1 //
2 // PPD file merge utility for the CUPS PPD Compiler.
3 //
4 // Copyright © 2007-2018 by Apple Inc.
5 // Copyright © 2002-2007 by Easy Software Products.
6 //
7 // Licensed under Apache License v2.0. See the file "LICENSE" for more
8 // information.
9 //
10
11 //
12 // Include necessary headers...
13 //
14
15 #include <cups/cups-private.h>
16 #include <cups/ppd-private.h>
17 #include <cups/array.h>
18
19
20 //
21 // Local functions...
22 //
23
24 static const char *ppd_locale(ppd_file_t *ppd);
25 static void usage(void);
26
27
28 //
29 // 'main()' - Main entry for the PPD merge utility.
30 //
31
32 int // O - Exit status
main(int argc,char * argv[])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)
42 char bckname[1024]; // Backup filename
43 cups_file_t *infile, // Input file
44 *outfile; // Output file
45 cups_array_t *languages; // Languages in file
46 const char *locale; // Current locale
47 char line[1024]; // Line from file
48
49
50 _cupsSetLocale(argv);
51
52 // Scan the command-line...
53 inname = NULL;
54 outname = NULL;
55 outfile = NULL;
56 languages = NULL;
57 ppds = cupsArrayNew(NULL, NULL);
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 {
83 // Open and load the PPD file...
84 if ((infile = cupsFileOpen(argv[i], "r")) == NULL)
85 {
86 _cupsLangPrintf(stderr, _("%s: Unable to open %s: %s"), "ppdmerge",
87 argv[i], strerror(errno));
88 return (1);
89 }
90
91 // Open the PPD file...
92 if ((ppd = ppdOpen2(infile)) == NULL)
93 {
94 ppd_status_t status; // PPD open status
95 int curline, // Current line
96 linenum; // Line number
97
98
99 status = ppdLastError(&linenum);
100
101 _cupsLangPrintf(stderr,
102 _("%s: Unable to open PPD file: %s on line %d."),
103 "ppdmerge", ppdErrorString(status), linenum);
104 cupsFileRewind(infile);
105
106 line[0] = '\0';
107 curline = 0;
108
109 while (cupsFileGets(infile, line, sizeof(line)))
110 {
111 curline ++;
112 if (curline >= linenum)
113 break;
114 }
115
116 _cupsLangPrintf(stderr, "%d: %s", linenum, line);
117
118 cupsFileClose(infile);
119 return (1);
120 }
121
122 // Figure out the locale...
123 if ((locale = ppd_locale(ppd)) == NULL)
124 {
125 _cupsLangPrintf(stderr,
126 _("ppdmerge: Bad LanguageVersion \"%s\" in %s."),
127 ppd->lang_version, argv[i]);
128 cupsFileClose(infile);
129 ppdClose(ppd);
130 return (1);
131 }
132
133 if (!strcmp(locale, "en") && !inname && !outfile)
134 {
135 // Set the English PPD's filename...
136 inname = argv[i];
137 languages = _ppdGetLanguages(ppd);
138
139 if (outname && !strcmp(inname, outname))
140 {
141 // Rename input filename so that we don't overwrite it...
142 snprintf(bckname, sizeof(bckname), "%s.bck", inname);
143
144 if (rename(inname, bckname))
145 {
146 _cupsLangPrintf(stderr,
147 _("ppdmerge: Unable to backup %s to %s - %s"),
148 inname, bckname, strerror(errno));
149 return (1);
150 }
151
152 inname = bckname;
153 }
154 }
155 else if (strcmp(locale, "en"))
156 {
157 // Save this PPD for later processing...
158 cupsArrayAdd(ppds, ppd);
159 }
160 else
161 {
162 // Don't need this PPD...
163 _cupsLangPrintf(stderr, _("ppdmerge: Ignoring PPD file %s."),
164 argv[i]);
165 ppdClose(ppd);
166 }
167
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();
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...
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
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);
241
242 cupsFilePrintf(outfile, "*%% %s localization\n", ppd->lang_version);
243 cupsFilePrintf(outfile, "*%s.Translation ModelName/%s: \"\"\n", locale,
244 ppd->modelname);
245
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);
250
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);
255
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);
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 }
270 }
271 }
272
273 ppdClose(ppd);
274 }
275
276 cupsArrayDelete(ppds);
277
278 cupsFileClose(outfile);
279
280 // Return with no errors.
281 return (0);
282 }
283
284
285 //
286 // 'ppd_locale()' - Return the locale associated with a PPD file.
287 //
288
289 static const char * // O - Locale string
ppd_locale(ppd_file_t * ppd)290 ppd_locale(ppd_file_t *ppd) // I - PPD file
291 {
292 int i; // Looping var
293 size_t vlen; // Length of LanguageVersion string
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 {
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" }
324 };
325
326
327 for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
328 {
329 vlen = strlen(languages[i].version);
330
331 if (!_cups_strncasecmp(ppd->lang_version, languages[i].version, vlen))
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
usage(void)352 usage(void)
353 {
354 _cupsLangPuts(stdout, _("Usage: ppdmerge [options] filename.ppd [ ... "
355 "filenameN.ppd ]"));
356 _cupsLangPuts(stdout, _("Options:"));
357 _cupsLangPuts(stdout, _(" -o filename.ppd[.gz] Set output file "
358 "(otherwise stdout)."));
359
360 exit(1);
361 }
362