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