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