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