1 /*
2 * PPD file generation program for the CUPS drivers.
3 *
4 * Copyright 1993-2008 by Mike Sweet and Robert Krawitz.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * Contents:
20 *
21 * main() - Process files on the command-line...
22 * cat_ppd() - Copy the named PPD to stdout.
23 * generate_ppd() - Generate a PPD file.
24 * getlangs() - Get a list of available translations.
25 * help() - Show detailed help.
26 * is_special_option() - Determine if an option should be grouped.
27 * list_ppds() - List the available drivers.
28 * print_group_close() - Close a UI group.
29 * print_group_open() - Open a new UI group.
30 * printlangs() - Print list of available translations.
31 * printmodels() - Print a list of available models.
32 * usage() - Show program usage.
33 * write_ppd() - Write a PPD file.
34 */
35
36 #include "genppd.h"
37
38 static int generate_ppd(const char *prefix, int verbose,
39 const stp_printer_t *p, const char *language,
40 ppd_type_t ppd_type, int use_compression);
41 static int generate_model_ppds(const char *prefix, int verbose,
42 const stp_printer_t *printer,
43 const char *language, int which_ppds,
44 int use_compression);
45 static void help(void);
46 static void printlangs(char** langs);
47 static void printmodels(int verbose);
48 static void usage(void);
49 static gpFile gpopen(const char *path, const char *mode, int use_compression);
50 static int gpclose(gpFile f, int use_compression);
51
52 /*
53 * 'main()' - Process files on the command-line...
54 */
55
56 int /* O - Exit status */
main(int argc,char * argv[])57 main(int argc, /* I - Number of command-line arguments */
58 char *argv[]) /* I - Command-line arguments */
59 {
60 int i; /* Looping var */
61 const char *prefix; /* Directory prefix for output */
62 const char *language = NULL; /* Language */
63 const stp_printer_t *printer; /* Pointer to printer driver */
64 int verbose = 0; /* Verbose messages */
65 char **langs = NULL; /* Available translations */
66 char **models = NULL; /* Models to output, all if NULL */
67 int opt_printlangs = 0; /* Print available translations */
68 int opt_printmodels = 0;/* Print available models */
69 int which_ppds = 2; /* Simplified PPD's = 1, full = 2,
70 no color opts = 4 */
71 unsigned parallel = 1; /* Generate PPD files in parallel */
72 unsigned rotor = 0; /* Rotor for generating PPD files in parallel */
73 unsigned test_rotor = 0; /* Testing (serialized) rotor */
74 unsigned test_rotor_circumference = 1; /* Testing (serialized) rotor size */
75 pid_t *subprocesses = NULL;
76 int parent = 1;
77 #ifdef HAVE_LIBZ
78 int use_compression = 1;
79 #else
80 int use_compression = 0;
81 #endif
82 int skip_duplicate_ppds = 0;
83
84
85 /*
86 * Parse command-line args...
87 */
88
89 prefix = CUPS_MODELDIR;
90
91 for (;;)
92 {
93 if ((i = getopt(argc, argv, "23hvqc:p:l:LMVd:saNCbZzSr:R:")) == -1)
94 break;
95
96 switch (i)
97 {
98 case '2':
99 cups_ppd_ps_level = 2;
100 break;
101 case '3':
102 cups_ppd_ps_level = 3;
103 break;
104 case 'h':
105 help();
106 exit(EXIT_SUCCESS);
107 break;
108 case 'v':
109 verbose = 1;
110 break;
111 case 'q':
112 verbose = 0;
113 break;
114 case 'c':
115 fputs("ERROR: -c option no longer supported!\n", stderr);
116 break;
117 case 'p':
118 prefix = optarg;
119 # ifdef DEBUG
120 fprintf(stderr, "DEBUG: prefix: %s\n", prefix);
121 # endif
122 break;
123 case 'l':
124 language = optarg;
125 break;
126 case 'L':
127 opt_printlangs = 1;
128 break;
129 case 'M':
130 opt_printmodels = 1;
131 break;
132 case 'd':
133 cups_modeldir = optarg;
134 break;
135 case 's':
136 which_ppds = 1;
137 break;
138 case 'a':
139 which_ppds = 3;
140 break;
141 case 'C':
142 which_ppds |= 4;
143 break;
144 case 'N':
145 localize_numbers = !localize_numbers;
146 break;
147 case 'V':
148 printf("cups-genppd version %s, "
149 "Copyright 1993-2008 by Michael R Sweet and Robert Krawitz.\n\n",
150 VERSION);
151 printf("Default CUPS PPD PostScript Level: %d\n", cups_ppd_ps_level);
152 printf("Default PPD location (prefix): %s\n", CUPS_MODELDIR);
153 printf("Default base locale directory: %s\n\n", PACKAGE_LOCALE_DIR);
154 puts("This program is free software; you can redistribute it and/or\n"
155 "modify it under the terms of the GNU General Public License,\n"
156 "version 2, as published by the Free Software Foundation.\n"
157 "\n"
158 "This program is distributed in the hope that it will be useful,\n"
159 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
160 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
161 "GNU General Public License for more details.\n");
162 exit(EXIT_SUCCESS);
163 break;
164 case 'b':
165 use_base_version = 1;
166 break;
167 case 'z':
168 #ifdef HAVE_LIBZ
169 use_compression = 1;
170 #endif
171 break;
172 case 'Z':
173 #ifdef HAVE_LIBZ
174 use_compression = 0;
175 #endif
176 break;
177 case 'S':
178 skip_duplicate_ppds = 1;
179 break;
180 case 'r':
181 test_rotor = atoi(optarg);
182 break;
183 case 'R':
184 test_rotor_circumference = atoi(optarg);
185 break;
186 default:
187 usage();
188 exit(EXIT_FAILURE);
189 break;
190 }
191 }
192 if (test_rotor_circumference < 1 || test_rotor >= test_rotor_circumference)
193 {
194 test_rotor = 0;
195 test_rotor_circumference = 1;
196 }
197 #ifdef HAVE_LIBZ
198 if (use_compression)
199 gpext = ".gz";
200 else
201 #endif
202 gpext = "";
203 if (optind < argc) {
204 int n, numargs;
205 numargs = argc-optind;
206 models = stp_malloc((numargs+1) * sizeof(char*));
207 for (n=0; n<numargs; n++)
208 {
209 models[n] = argv[optind+n];
210 }
211 models[numargs] = (char*)NULL;
212 }
213
214 /*
215 * Initialise libgutenprint
216 */
217
218 stp_init();
219
220 langs = getlangs();
221
222 /*
223 * Print lists
224 */
225
226 if (opt_printlangs)
227 {
228 printlangs(langs);
229 exit(EXIT_SUCCESS);
230 }
231
232 if (opt_printmodels)
233 {
234 printmodels(verbose);
235 exit(EXIT_SUCCESS);
236 }
237
238 /*
239 * Write PPD files...
240 */
241
242 if (getenv("STP_PARALLEL"))
243 {
244 parallel = atoi(getenv("STP_PARALLEL"));
245 if (parallel < 1 || parallel > 256)
246 parallel = 1;
247 }
248 if (parallel > 1)
249 {
250 subprocesses = stp_malloc(sizeof(pid_t) * parallel);
251 for (rotor = 0; rotor < parallel; rotor++)
252 {
253 pid_t pid = fork();
254 if (pid == 0) /* Child */
255 {
256 parent = 0;
257 break;
258 }
259 else if (pid > 0)
260 subprocesses[rotor] = pid;
261 else
262 {
263 fprintf(stderr, "Cannot fork: %s\n", strerror(errno));
264 return 1;
265 }
266 }
267 }
268 if (models)
269 {
270 int n;
271 for (n=0; models[n]; n++)
272 {
273 printer = stp_get_printer_by_driver(models[n]);
274 if (!printer)
275 printer = stp_get_printer_by_long_name(models[n]);
276
277 if (n % parallel == rotor && printer)
278 {
279 if (printer)
280 {
281 if (generate_model_ppds(prefix, verbose, printer, language,
282 which_ppds, use_compression))
283 return 1;
284 }
285 else
286 {
287 printf("Driver not found: %s\n", models[n]);
288 return (1);
289 }
290 }
291 }
292 stp_free(models);
293 }
294 else
295 {
296 int test_rotor_current = -1;
297 stp_string_list_t *seen_models = NULL;
298 if (skip_duplicate_ppds)
299 seen_models = stp_string_list_create();
300
301 for (i = 0; i < stp_printer_model_count(); i++)
302 {
303 printer = stp_get_printer_by_index(i);
304 test_rotor_current++;
305 if (skip_duplicate_ppds)
306 {
307 char model_family[128];
308 (void) snprintf(model_family, 127, "%d_%s",
309 stp_printer_get_model(printer),
310 stp_printer_get_family(printer));
311 if (stp_string_list_is_present(seen_models, model_family))
312 continue;
313 else
314 stp_string_list_add_string_unsafe(seen_models, model_family,
315 model_family);
316 }
317 if (test_rotor_current % test_rotor_circumference != test_rotor)
318 continue;
319 if (i % parallel == rotor && printer)
320 {
321 if (! verbose && (i % 100) == 0)
322 fputc('.',stderr);
323 if (generate_model_ppds(prefix, verbose, printer, language,
324 which_ppds, use_compression))
325 return 1;
326 }
327 }
328 if (seen_models)
329 stp_string_list_destroy(seen_models);
330 }
331 if (subprocesses)
332 {
333 pid_t pid;
334 do
335 {
336 int status;
337 pid = waitpid(-1, &status, 0);
338 if (pid > 0 && (!WIFEXITED(status) || WEXITSTATUS(status) != 0))
339 {
340 fprintf(stderr, "failed!\n");
341 return 1;
342 }
343 } while (pid > 0);
344 stp_free(subprocesses);
345 }
346 if (parent && !verbose)
347 fprintf(stderr, " done.\n");
348
349 return (0);
350 }
351
352 static int
generate_model_ppds(const char * prefix,int verbose,const stp_printer_t * printer,const char * language,int which_ppds,int use_compression)353 generate_model_ppds(const char *prefix, int verbose,
354 const stp_printer_t *printer, const char *language,
355 int which_ppds, int use_compression)
356 {
357 if ((which_ppds & 1) &&
358 generate_ppd(prefix, verbose, printer, language, PPD_SIMPLIFIED,
359 use_compression))
360 return (1);
361 if ((which_ppds & 2) &&
362 generate_ppd(prefix, verbose, printer, language, PPD_STANDARD,
363 use_compression))
364 return (1);
365 if ((which_ppds & 4) &&
366 generate_ppd(prefix, verbose, printer, language, PPD_NO_COLOR_OPTS,
367 use_compression))
368 return (1);
369 return 0;
370 }
371
372 /*
373 * 'generate_ppd()' - Generate a PPD file.
374 */
375
376 static int /* O - Exit status */
generate_ppd(const char * prefix,int verbose,const stp_printer_t * p,const char * language,ppd_type_t ppd_type,int use_compression)377 generate_ppd(
378 const char *prefix, /* I - PPD directory prefix */
379 int verbose, /* I - Verbosity level */
380 const stp_printer_t *p, /* I - Driver */
381 const char *language, /* I - Primary language */
382 ppd_type_t ppd_type, /* I - full, simplified, no color */
383 int use_compression) /* I - compress output */
384 {
385 int status; /* Exit status */
386 gpFile fp; /* File to write to */
387 char filename[1024], /* Filename */
388 ppd_location[1024]; /* Installed location */
389 struct stat dir; /* Prefix dir status */
390 const char *ppd_infix;
391
392 /*
393 * Skip the PostScript drivers...
394 */
395
396 if (!strcmp(stp_printer_get_family(p), "ps") ||
397 !strcmp(stp_printer_get_family(p), "raw"))
398 return (0);
399
400 /*
401 * Make sure the destination directory exists...
402 */
403
404 if (stat(prefix, &dir) && !S_ISDIR(dir.st_mode))
405 {
406 if (mkdir(prefix, 0777))
407 {
408 printf("cups-genppd: Cannot create directory %s: %s\n",
409 prefix, strerror(errno));
410 exit(EXIT_FAILURE);
411 }
412 }
413
414 /*
415 * The files will be named stp-<driver>.<major>.<minor>.ppd, for
416 * example:
417 *
418 * stp-escp2-ex.5.0.ppd
419 *
420 * or
421 *
422 * stp-escp2-ex.5.0.ppd.gz
423 */
424
425 switch (ppd_type)
426 {
427 case PPD_SIMPLIFIED:
428 ppd_infix = ".sim";
429 break;
430 case PPD_NO_COLOR_OPTS:
431 ppd_infix = ".nc";
432 break;
433 default:
434 ppd_infix = "";
435 }
436
437 snprintf(filename, sizeof(filename) - 1, "%s/stp-%s.%s%s%s%s",
438 prefix, stp_printer_get_driver(p), GUTENPRINT_RELEASE_VERSION,
439 ppd_infix, ppdext, gpext);
440
441 /*
442 * Open the PPD file...
443 */
444
445 if ((fp = gpopen(filename, "wb", use_compression)) == NULL)
446 {
447 fprintf(stderr, "cups-genppd: Unable to create file \"%s\" - %s.\n",
448 filename, strerror(errno));
449 return (2);
450 }
451
452 if (verbose)
453 fprintf(stderr, "Writing %s...\n", filename);
454
455 snprintf(ppd_location, sizeof(ppd_location), "%s%s%s/%s",
456 cups_modeldir,
457 cups_modeldir[strlen(cups_modeldir) - 1] == '/' ? "" : "/",
458 language ? language : "C",
459 basename(filename));
460
461 snprintf(filename, sizeof(filename) - 1, "stp-%s.%s%s%s",
462 stp_printer_get_driver(p), GUTENPRINT_RELEASE_VERSION,
463 ppd_infix, ppdext);
464
465 status = write_ppd(fp, p, language, ppd_location, ppd_type,
466 basename(filename), use_compression);
467
468 gpclose(fp, use_compression);
469
470 return (status);
471 }
472
473
474 /*
475 * 'help()' - Show detailed help.
476 */
477
478 void
help(void)479 help(void)
480 {
481 puts("Generate Gutenprint PPD files for use with CUPS\n\n");
482 usage();
483 puts("\nExamples: LANG=de_DE cups-genppd -p ppd -c /usr/share/locale\n"
484 " cups-genppd -L -c /usr/share/locale\n"
485 " cups-genppd -M -v\n\n"
486 "Commands:\n"
487 " -h Show this help message.\n"
488 " -L List available translations (message catalogs).\n"
489 " -M List available printer models.\n"
490 " -V Show version information and defaults.\n"
491 " The default is to output PPDs.\n");
492 puts("Options:\n"
493 " -N Localize numbers.\n"
494 " -l locale Output PPDs translated with messages for locale.\n"
495 " -p prefix Output PPDs in directory prefix.\n"
496 " -d prefix Embed directory prefix in PPD file.\n"
497 " -s Generate simplified PPD files.\n"
498 " -a Generate all (simplified and full) PPD files.\n"
499 " -q Quiet mode.\n"
500 " -v Verbose mode.\n");
501 puts(
502 #ifdef HAVE_LIBZ
503 " -z Compress PPD files.\n"
504 " -Z Don't compress PPD files.\n"
505 #endif
506 " -S Skip PPD files with duplicate model identifiers.\n"
507 " -R size Generate every size'th PPD file.\n"
508 " -r divisor Generate the PPD files (N % size == divisor).\n"
509 "\n"
510 "models:\n"
511 " A list of printer models, either the driver or quoted full name.\n");
512 }
513
514 /*
515 * 'usage()' - Show program usage.
516 */
517
518 void
usage(void)519 usage(void)
520 {
521 puts("Usage: cups-genppd "
522 "[-l locale] [-p prefix] [-s | -a] [-q] [-v] models...\n"
523 " cups-genppd -L\n"
524 " cups-genppd -M [-v]\n"
525 " cups-genppd -h\n"
526 " cups-genppd -V\n");
527 }
528
529 /*
530 * 'printlangs()' - Print list of available translations.
531 */
532
533 void
printlangs(char ** langs)534 printlangs(char **langs) /* I - Languages */
535 {
536 if (langs)
537 {
538 int n = 0;
539 while (langs && langs[n])
540 {
541 puts(langs[n]);
542 n++;
543 }
544 }
545 exit(EXIT_SUCCESS);
546 }
547
548
549 /*
550 * 'printmodels()' - Print a list of available models.
551 */
552
553 void
printmodels(int verbose)554 printmodels(int verbose) /* I - Verbosity level */
555 {
556 const stp_printer_t *p;
557 int i;
558
559 for (i = 0; i < stp_printer_model_count(); i++)
560 {
561 p = stp_get_printer_by_index(i);
562 if (p &&
563 strcmp(stp_printer_get_family(p), "ps") != 0 &&
564 strcmp(stp_printer_get_family(p), "raw") != 0)
565 {
566 if(verbose)
567 printf("%-20s%s\n", stp_printer_get_driver(p),
568 stp_printer_get_long_name(p));
569 else
570 printf("%s\n", stp_printer_get_driver(p));
571 }
572 }
573 exit(EXIT_SUCCESS);
574 }
575
576 static gpFile
gpopen(const char * path,const char * mode,int use_compression)577 gpopen(const char *path, const char *mode, int use_compression)
578 {
579 #ifdef HAVE_LIBZ
580 gpFile f = stp_malloc(sizeof(gpfile));
581 if (use_compression)
582 {
583 f->gzf = gzopen(path, mode);
584 if (!f->gzf)
585 {
586 stp_free(f);
587 return NULL;
588 }
589 return f;
590 }
591 else
592 #endif
593 {
594 FILE *fl = fopen(path, mode);
595 #ifdef HAVE_LIBZ
596 if (fl)
597 {
598 f->f = fl;
599 return f;
600 }
601 else
602 {
603 stp_free(f);
604 return NULL;
605 }
606 #else
607 return fl;
608 #endif
609 }
610 }
611
612 static int
gpclose(gpFile f,int use_compression)613 gpclose(gpFile f, int use_compression)
614 {
615 int status;
616 #ifdef HAVE_LIBZ
617 if (use_compression)
618 status = gzclose(f->gzf);
619 else
620 status = fclose(f->f);
621 stp_free(f);
622 #else
623 status = fclose(f);
624 #endif
625 return status;
626 }
627