]>
Commit | Line | Data |
---|---|---|
ac884b6a | 1 | // |
be76a973 | 2 | // PPD file compiler main entry for the CUPS PPD Compiler. |
ac884b6a | 3 | // |
be76a973 MS |
4 | // Copyright 2007-2014 by Apple Inc. |
5 | // Copyright 2002-2007 by Easy Software Products. | |
ac884b6a | 6 | // |
e3101897 | 7 | // Licensed under Apache License v2.0. See the file "LICENSE" for more information. |
ac884b6a MS |
8 | // |
9 | ||
10 | // | |
11 | // Include necessary headers... | |
12 | // | |
13 | ||
38e73f87 | 14 | #include "ppdc-private.h" |
bdd6c45b | 15 | #include <unistd.h> |
ac884b6a MS |
16 | #include <sys/stat.h> |
17 | #include <sys/types.h> | |
18 | ||
19 | ||
20 | // | |
21 | // Local functions... | |
22 | // | |
23 | ||
10d19ff1 | 24 | static void usage(void) _CUPS_NORETURN; |
ac884b6a MS |
25 | |
26 | ||
27 | // | |
28 | // 'main()' - Main entry for the PPD compiler. | |
29 | // | |
30 | ||
31 | int // O - Exit status | |
32 | main(int argc, // I - Number of command-line arguments | |
33 | char *argv[]) // I - Command-line arguments | |
34 | { | |
35 | int i, j; // Looping vars | |
36 | ppdcCatalog *catalog; // Message catalog | |
37 | const char *outdir; // Output directory | |
38 | ppdcSource *src; // PPD source file data | |
39 | ppdcDriver *d; // Current driver | |
40 | cups_file_t *fp; // PPD file | |
41 | char *opt, // Current option | |
42 | *value, // Value in option | |
bdd6c45b | 43 | *outname, // Output filename |
41681883 MS |
44 | make_model[1024], |
45 | // Make and model | |
ac884b6a MS |
46 | pcfilename[1024], |
47 | // Lowercase pcfilename | |
48 | filename[1024]; // PPD filename | |
bdd6c45b MS |
49 | int comp, // Compress |
50 | do_test, // Test PPD files | |
5a6b583a | 51 | single_language,// Generate single-language files |
bdd6c45b MS |
52 | use_model_name, // Use ModelName for filename |
53 | verbose; // Verbosity | |
ac884b6a | 54 | ppdcLineEnding le; // Line ending to use |
bdd6c45b | 55 | ppdcArray *locales; // List of locales |
18ecb428 | 56 | cups_array_t *filenames; // List of generated filenames |
ac884b6a MS |
57 | |
58 | ||
61cf44e2 MS |
59 | _cupsSetLocale(argv); |
60 | ||
ac884b6a | 61 | // Scan the command-line... |
5a6b583a MS |
62 | catalog = NULL; |
63 | comp = 0; | |
64 | do_test = 0; | |
65 | le = PPDC_LFONLY; | |
66 | locales = NULL; | |
67 | outdir = "ppd"; | |
68 | single_language = 0; | |
69 | src = new ppdcSource(); | |
70 | use_model_name = 0; | |
71 | verbose = 0; | |
88f9aafc | 72 | filenames = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL); |
ac884b6a MS |
73 | |
74 | for (i = 1; i < argc; i ++) | |
75 | if (argv[i][0] == '-') | |
76 | { | |
77 | for (opt = argv[i] + 1; *opt; opt ++) | |
78 | switch (*opt) | |
79 | { | |
bdd6c45b MS |
80 | case 'D' : // Define variable |
81 | i ++; | |
82 | if (i >= argc) | |
83 | usage(); | |
84 | ||
85 | if ((value = strchr(argv[i], '=')) != NULL) | |
86 | { | |
87 | *value++ = '\0'; | |
88 | ||
89 | src->set_variable(argv[i], value); | |
90 | } | |
91 | else | |
92 | src->set_variable(argv[i], "1"); | |
93 | break; | |
94 | ||
95 | case 'I' : // Include directory... | |
96 | i ++; | |
97 | if (i >= argc) | |
98 | usage(); | |
99 | ||
100 | if (verbose > 1) | |
61cf44e2 | 101 | _cupsLangPrintf(stdout, |
0837b7e8 | 102 | _("ppdc: Adding include directory \"%s\"."), |
61cf44e2 | 103 | argv[i]); |
bdd6c45b MS |
104 | |
105 | ppdcSource::add_include(argv[i]); | |
106 | break; | |
107 | ||
ac884b6a MS |
108 | case 'c' : // Message catalog... |
109 | i ++; | |
110 | if (i >= argc) | |
111 | usage(); | |
112 | ||
113 | if (verbose > 1) | |
61cf44e2 | 114 | _cupsLangPrintf(stdout, |
0837b7e8 | 115 | _("ppdc: Loading messages from \"%s\"."), |
61cf44e2 | 116 | argv[i]); |
ac884b6a MS |
117 | |
118 | if (!catalog) | |
119 | catalog = new ppdcCatalog("en"); | |
120 | ||
121 | if (catalog->load_messages(argv[i])) | |
122 | { | |
61cf44e2 MS |
123 | _cupsLangPrintf(stderr, |
124 | _("ppdc: Unable to load localization file " | |
0837b7e8 | 125 | "\"%s\" - %s"), argv[i], strerror(errno)); |
ac884b6a MS |
126 | return (1); |
127 | } | |
128 | break; | |
129 | ||
130 | case 'd' : // Output directory... | |
131 | i ++; | |
132 | if (i >= argc) | |
133 | usage(); | |
134 | ||
135 | if (verbose > 1) | |
61cf44e2 MS |
136 | _cupsLangPrintf(stdout, |
137 | _("ppdc: Writing PPD files to directory " | |
0837b7e8 | 138 | "\"%s\"."), argv[i]); |
ac884b6a MS |
139 | |
140 | outdir = argv[i]; | |
141 | break; | |
142 | ||
143 | case 'l' : // Language(s)... | |
144 | i ++; | |
145 | if (i >= argc) | |
146 | usage(); | |
147 | ||
148 | if (strchr(argv[i], ',')) | |
149 | { | |
150 | // Comma-delimited list of languages... | |
151 | char temp[1024], // Copy of language list | |
152 | *start, // Start of current locale name | |
153 | *end; // End of current locale name | |
154 | ||
155 | ||
156 | locales = new ppdcArray(); | |
157 | ||
158 | strlcpy(temp, argv[i], sizeof(temp)); | |
159 | for (start = temp; *start; start = end) | |
160 | { | |
161 | if ((end = strchr(start, ',')) != NULL) | |
162 | *end++ = '\0'; | |
163 | else | |
164 | end = start + strlen(start); | |
165 | ||
166 | if (end > start) | |
167 | locales->add(new ppdcString(start)); | |
168 | } | |
169 | } | |
170 | else | |
171 | { | |
5a6b583a MS |
172 | single_language = 1; |
173 | ||
ac884b6a | 174 | if (verbose > 1) |
61cf44e2 MS |
175 | _cupsLangPrintf(stdout, |
176 | _("ppdc: Loading messages for locale " | |
0837b7e8 | 177 | "\"%s\"."), argv[i]); |
ac884b6a MS |
178 | |
179 | if (catalog) | |
e4572d57 | 180 | catalog->release(); |
ac884b6a MS |
181 | |
182 | catalog = new ppdcCatalog(argv[i]); | |
183 | ||
be76a973 | 184 | if (catalog->messages->count == 0 && strcmp(argv[i], "en")) |
ac884b6a | 185 | { |
61cf44e2 MS |
186 | _cupsLangPrintf(stderr, |
187 | _("ppdc: Unable to find localization for " | |
0837b7e8 | 188 | "\"%s\" - %s"), argv[i], strerror(errno)); |
ac884b6a MS |
189 | return (1); |
190 | } | |
191 | } | |
192 | break; | |
193 | ||
bdd6c45b MS |
194 | case 'm' : // Use ModelName for filename |
195 | use_model_name = 1; | |
196 | break; | |
ac884b6a | 197 | |
bdd6c45b MS |
198 | case 't' : // Test PPDs instead of generating them |
199 | do_test = 1; | |
ac884b6a MS |
200 | break; |
201 | ||
202 | case 'v' : // Be verbose... | |
203 | verbose ++; | |
204 | break; | |
88f9aafc | 205 | |
ac884b6a MS |
206 | case 'z' : // Compress files... |
207 | comp = 1; | |
208 | break; | |
209 | ||
210 | case '-' : // --option | |
211 | if (!strcmp(opt, "-lf")) | |
212 | { | |
213 | le = PPDC_LFONLY; | |
214 | opt += strlen(opt) - 1; | |
215 | break; | |
216 | } | |
217 | else if (!strcmp(opt, "-cr")) | |
218 | { | |
219 | le = PPDC_CRONLY; | |
220 | opt += strlen(opt) - 1; | |
221 | break; | |
222 | } | |
223 | else if (!strcmp(opt, "-crlf")) | |
224 | { | |
225 | le = PPDC_CRLF; | |
226 | opt += strlen(opt) - 1; | |
227 | break; | |
228 | } | |
88f9aafc | 229 | |
ac884b6a MS |
230 | default : // Unknown |
231 | usage(); | |
ac884b6a MS |
232 | } |
233 | } | |
234 | else | |
235 | { | |
236 | // Open and load the driver info file... | |
237 | if (verbose > 1) | |
61cf44e2 | 238 | _cupsLangPrintf(stdout, |
0837b7e8 | 239 | _("ppdc: Loading driver information file \"%s\"."), |
61cf44e2 | 240 | argv[i]); |
ac884b6a MS |
241 | |
242 | src->read_file(argv[i]); | |
243 | } | |
244 | ||
245 | ||
246 | if (src->drivers->count > 0) | |
247 | { | |
248 | // Create the output directory... | |
249 | if (mkdir(outdir, 0777)) | |
250 | { | |
251 | if (errno != EEXIST) | |
252 | { | |
61cf44e2 | 253 | _cupsLangPrintf(stderr, |
0837b7e8 | 254 | _("ppdc: Unable to create output directory %s: %s"), |
ac884b6a MS |
255 | outdir, strerror(errno)); |
256 | return (1); | |
257 | } | |
258 | } | |
259 | ||
260 | // Write PPD files... | |
261 | for (d = (ppdcDriver *)src->drivers->first(); | |
262 | d; | |
263 | d = (ppdcDriver *)src->drivers->next()) | |
264 | { | |
bdd6c45b | 265 | if (do_test) |
ac884b6a | 266 | { |
bdd6c45b MS |
267 | // Test the PPD file for this driver... |
268 | int pid, // Process ID | |
269 | fds[2]; // Pipe file descriptors | |
270 | ||
271 | ||
272 | if (pipe(fds)) | |
273 | { | |
61cf44e2 | 274 | _cupsLangPrintf(stderr, |
0837b7e8 | 275 | _("ppdc: Unable to create output pipes: %s"), |
61cf44e2 | 276 | strerror(errno)); |
bdd6c45b MS |
277 | return (1); |
278 | } | |
279 | ||
280 | if ((pid = fork()) == 0) | |
281 | { | |
282 | // Child process comes here... | |
97c9a8d7 | 283 | dup2(fds[0], 0); |
bdd6c45b MS |
284 | |
285 | close(fds[0]); | |
286 | close(fds[1]); | |
287 | ||
288 | execlp("cupstestppd", "cupstestppd", "-", (char *)0); | |
289 | ||
61cf44e2 | 290 | _cupsLangPrintf(stderr, |
0837b7e8 | 291 | _("ppdc: Unable to execute cupstestppd: %s"), |
61cf44e2 | 292 | strerror(errno)); |
bdd6c45b MS |
293 | return (errno); |
294 | } | |
295 | else if (pid < 0) | |
296 | { | |
0837b7e8 | 297 | _cupsLangPrintf(stderr, _("ppdc: Unable to execute cupstestppd: %s"), |
61cf44e2 | 298 | strerror(errno)); |
bdd6c45b MS |
299 | return (errno); |
300 | } | |
ac884b6a | 301 | |
bdd6c45b MS |
302 | close(fds[0]); |
303 | fp = cupsFileOpenFd(fds[1], "w"); | |
ac884b6a MS |
304 | } |
305 | else | |
306 | { | |
bdd6c45b MS |
307 | // Write the PPD file for this driver... |
308 | if (use_model_name) | |
41681883 | 309 | { |
88f9aafc | 310 | if (!_cups_strncasecmp(d->model_name->value, d->manufacturer->value, |
41681883 MS |
311 | strlen(d->manufacturer->value))) |
312 | { | |
313 | // Model name already starts with the manufacturer... | |
314 | outname = d->model_name->value; | |
315 | } | |
316 | else | |
317 | { | |
318 | // Add manufacturer to the front of the model name... | |
319 | snprintf(make_model, sizeof(make_model), "%s %s", | |
320 | d->manufacturer->value, d->model_name->value); | |
321 | outname = make_model; | |
322 | } | |
323 | } | |
bdd6c45b MS |
324 | else if (d->file_name) |
325 | outname = d->file_name->value; | |
326 | else | |
327 | outname = d->pc_file_name->value; | |
328 | ||
329 | if (strstr(outname, ".PPD")) | |
330 | { | |
331 | // Convert PCFileName to lowercase... | |
332 | for (j = 0; | |
333 | outname[j] && j < (int)(sizeof(pcfilename) - 1); | |
334 | j ++) | |
7e86f2f6 | 335 | pcfilename[j] = (char)tolower(outname[j] & 255); |
ac884b6a | 336 | |
bdd6c45b MS |
337 | pcfilename[j] = '\0'; |
338 | } | |
339 | else | |
340 | { | |
341 | // Leave PCFileName as-is... | |
342 | strlcpy(pcfilename, outname, sizeof(pcfilename)); | |
343 | } | |
ac884b6a | 344 | |
bdd6c45b MS |
345 | // Open the PPD file for writing... |
346 | if (comp) | |
347 | snprintf(filename, sizeof(filename), "%s/%s.gz", outdir, pcfilename); | |
348 | else | |
349 | snprintf(filename, sizeof(filename), "%s/%s", outdir, pcfilename); | |
350 | ||
18ecb428 MS |
351 | if (cupsArrayFind(filenames, filename)) |
352 | _cupsLangPrintf(stderr, | |
0837b7e8 | 353 | _("ppdc: Warning - overlapping filename \"%s\"."), |
18ecb428 MS |
354 | filename); |
355 | else | |
356 | cupsArrayAdd(filenames, strdup(filename)); | |
357 | ||
bdd6c45b MS |
358 | fp = cupsFileOpen(filename, comp ? "w9" : "w"); |
359 | if (!fp) | |
360 | { | |
61cf44e2 | 361 | _cupsLangPrintf(stderr, |
0837b7e8 | 362 | _("ppdc: Unable to create PPD file \"%s\" - %s."), |
61cf44e2 | 363 | filename, strerror(errno)); |
bdd6c45b MS |
364 | return (1); |
365 | } | |
366 | ||
367 | if (verbose) | |
0837b7e8 | 368 | _cupsLangPrintf(stdout, _("ppdc: Writing %s."), filename); |
ac884b6a MS |
369 | } |
370 | ||
bdd6c45b MS |
371 | /* |
372 | * Write the PPD file... | |
373 | */ | |
ac884b6a | 374 | |
97c9a8d7 MS |
375 | ppdcArray *templocales = locales; |
376 | ||
5a6b583a | 377 | if (!templocales && !single_language) |
97c9a8d7 MS |
378 | { |
379 | templocales = new ppdcArray(); | |
380 | for (ppdcCatalog *tempcatalog = (ppdcCatalog *)src->po_files->first(); | |
381 | tempcatalog; | |
382 | tempcatalog = (ppdcCatalog *)src->po_files->next()) | |
383 | { | |
384 | tempcatalog->locale->retain(); | |
385 | templocales->add(tempcatalog->locale); | |
386 | } | |
387 | } | |
388 | ||
389 | if (d->write_ppd_file(fp, catalog, templocales, src, le)) | |
ac884b6a MS |
390 | { |
391 | cupsFileClose(fp); | |
392 | return (1); | |
393 | } | |
394 | ||
7a767e0b | 395 | if (templocales && templocales != locales) |
97c9a8d7 MS |
396 | templocales->release(); |
397 | ||
ac884b6a MS |
398 | cupsFileClose(fp); |
399 | } | |
400 | } | |
401 | else | |
402 | usage(); | |
403 | ||
404 | // Delete the printer driver information... | |
e4572d57 | 405 | src->release(); |
ac884b6a MS |
406 | |
407 | // Message catalog... | |
408 | if (catalog) | |
e4572d57 | 409 | catalog->release(); |
ac884b6a MS |
410 | |
411 | // Return with no errors. | |
412 | return (0); | |
413 | } | |
414 | ||
415 | ||
416 | // | |
417 | // 'usage()' - Show usage and exit. | |
418 | // | |
419 | ||
420 | static void | |
421 | usage(void) | |
422 | { | |
0837b7e8 MS |
423 | _cupsLangPuts(stdout, _("Usage: ppdc [options] filename.drv [ ... " |
424 | "filenameN.drv ]")); | |
425 | _cupsLangPuts(stdout, _("Options:")); | |
84315f46 | 426 | _cupsLangPuts(stdout, _(" -D name=value Set named variable to " |
0837b7e8 | 427 | "value.")); |
84315f46 | 428 | _cupsLangPuts(stdout, _(" -I include-dir Add include directory to " |
0837b7e8 | 429 | "search path.")); |
84315f46 MS |
430 | _cupsLangPuts(stdout, _(" -c catalog.po Load the specified " |
431 | "message catalog.")); | |
432 | _cupsLangPuts(stdout, _(" -d output-dir Specify the output " | |
0837b7e8 | 433 | "directory.")); |
84315f46 | 434 | _cupsLangPuts(stdout, _(" -l lang[,lang,...] Specify the output " |
0837b7e8 | 435 | "language(s) (locale).")); |
84315f46 MS |
436 | _cupsLangPuts(stdout, _(" -m Use the ModelName value " |
437 | "as the filename.")); | |
438 | _cupsLangPuts(stdout, _(" -t Test PPDs instead of " | |
0837b7e8 | 439 | "generating them.")); |
f3c17241 | 440 | _cupsLangPuts(stdout, _(" -v Be verbose.")); |
84315f46 MS |
441 | _cupsLangPuts(stdout, _(" -z Compress PPD files using " |
442 | "GNU zip.")); | |
443 | _cupsLangPuts(stdout, _(" --cr End lines with CR (Mac " | |
444 | "OS 9).")); | |
445 | _cupsLangPuts(stdout, _(" --crlf End lines with CR + LF " | |
0837b7e8 | 446 | "(Windows).")); |
84315f46 | 447 | _cupsLangPuts(stdout, _(" --lf End lines with LF " |
8072030b | 448 | "(UNIX/Linux/macOS).")); |
ac884b6a MS |
449 | |
450 | exit(1); | |
451 | } |