xref: /aosp_15_r20/external/libcups/ppdc/ppdc.cxx (revision 5e7646d21f1134fb0638875d812ef646c12ab91e)
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 // Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
8 //
9 
10 //
11 // Include necessary headers...
12 //
13 
14 #include "ppdc-private.h"
15 #include <unistd.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 
19 
20 //
21 // Local functions...
22 //
23 
24 static void	usage(void) _CUPS_NORETURN;
25 
26 
27 //
28 // 'main()' - Main entry for the PPD compiler.
29 //
30 
31 int					// O - Exit status
main(int argc,char * argv[])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
43 			*outname,	// Output filename
44 			make_model[1024],
45 					// Make and model
46 			pcfilename[1024],
47 					// Lowercase pcfilename
48 			filename[1024];	// PPD filename
49   int			comp,		// Compress
50 			do_test,	// Test PPD files
51 			single_language,// Generate single-language files
52 			use_model_name,	// Use ModelName for filename
53 			verbose;	// Verbosity
54   ppdcLineEnding	le;		// Line ending to use
55   ppdcArray		*locales;	// List of locales
56   cups_array_t		*filenames;	// List of generated filenames
57 
58 
59   _cupsSetLocale(argv);
60 
61   // Scan the command-line...
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;
72   filenames       = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL);
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 	{
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)
101 	        _cupsLangPrintf(stdout,
102 				_("ppdc: Adding include directory \"%s\"."),
103 				argv[i]);
104 
105 	      ppdcSource::add_include(argv[i]);
106 	      break;
107 
108 	  case 'c' :			// Message catalog...
109 	      i ++;
110               if (i >= argc)
111                 usage();
112 
113               if (verbose > 1)
114 	        _cupsLangPrintf(stdout,
115 		                _("ppdc: Loading messages from \"%s\"."),
116 				argv[i]);
117 
118               if (!catalog)
119 	        catalog = new ppdcCatalog("en");
120 
121               if (catalog->load_messages(argv[i]))
122 	      {
123         	_cupsLangPrintf(stderr,
124 		                _("ppdc: Unable to load localization file "
125 				  "\"%s\" - %s"), argv[i], strerror(errno));
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)
136 	        _cupsLangPrintf(stdout,
137 				_("ppdc: Writing PPD files to directory "
138 				  "\"%s\"."), argv[i]);
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 	      {
172 	        single_language = 1;
173 
174         	if (verbose > 1)
175 	          _cupsLangPrintf(stdout,
176 		                  _("ppdc: Loading messages for locale "
177 				    "\"%s\"."), argv[i]);
178 
179         	if (catalog)
180 	          catalog->release();
181 
182         	catalog = new ppdcCatalog(argv[i]);
183 
184 		if (catalog->messages->count == 0 && strcmp(argv[i], "en"))
185 		{
186         	  _cupsLangPrintf(stderr,
187 				  _("ppdc: Unable to find localization for "
188 				    "\"%s\" - %s"), argv[i], strerror(errno));
189                   return (1);
190 		}
191 	      }
192 	      break;
193 
194           case 'm' :			// Use ModelName for filename
195 	      use_model_name = 1;
196 	      break;
197 
198           case 't' :			// Test PPDs instead of generating them
199 	      do_test = 1;
200 	      break;
201 
202           case 'v' :			// Be verbose...
203 	      verbose ++;
204 	      break;
205 
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 	      }
229 
230 	  default :			// Unknown
231 	      usage();
232 	}
233     }
234     else
235     {
236       // Open and load the driver info file...
237       if (verbose > 1)
238         _cupsLangPrintf(stdout,
239 	                _("ppdc: Loading driver information file \"%s\"."),
240 			argv[i]);
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       {
253 	_cupsLangPrintf(stderr,
254 	                _("ppdc: Unable to create output directory %s: %s"),
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     {
265       if (do_test)
266       {
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 	{
274 	  _cupsLangPrintf(stderr,
275 	                  _("ppdc: Unable to create output pipes: %s"),
276 	                  strerror(errno));
277 	  return (1);
278 	}
279 
280 	if ((pid = fork()) == 0)
281 	{
282 	  // Child process comes here...
283 	  dup2(fds[0], 0);
284 
285 	  close(fds[0]);
286 	  close(fds[1]);
287 
288 	  execlp("cupstestppd", "cupstestppd", "-", (char *)0);
289 
290 	  _cupsLangPrintf(stderr,
291 	                  _("ppdc: Unable to execute cupstestppd: %s"),
292 			  strerror(errno));
293 	  return (errno);
294 	}
295 	else if (pid < 0)
296 	{
297 	  _cupsLangPrintf(stderr, _("ppdc: Unable to execute cupstestppd: %s"),
298 			  strerror(errno));
299 	  return (errno);
300 	}
301 
302 	close(fds[0]);
303 	fp = cupsFileOpenFd(fds[1], "w");
304       }
305       else
306       {
307 	// Write the PPD file for this driver...
308 	if (use_model_name)
309 	{
310 	  if (!_cups_strncasecmp(d->model_name->value, d->manufacturer->value,
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 	}
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 ++)
335 	    pcfilename[j] = (char)tolower(outname[j] & 255);
336 
337 	  pcfilename[j] = '\0';
338 	}
339 	else
340 	{
341 	  // Leave PCFileName as-is...
342 	  strlcpy(pcfilename, outname, sizeof(pcfilename));
343 	}
344 
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 
351         if (cupsArrayFind(filenames, filename))
352 	  _cupsLangPrintf(stderr,
353 	                  _("ppdc: Warning - overlapping filename \"%s\"."),
354 			  filename);
355 	else
356 	  cupsArrayAdd(filenames, strdup(filename));
357 
358 	fp = cupsFileOpen(filename, comp ? "w9" : "w");
359 	if (!fp)
360 	{
361 	  _cupsLangPrintf(stderr,
362 	                  _("ppdc: Unable to create PPD file \"%s\" - %s."),
363 			  filename, strerror(errno));
364 	  return (1);
365 	}
366 
367 	if (verbose)
368 	  _cupsLangPrintf(stdout, _("ppdc: Writing %s."), filename);
369       }
370 
371      /*
372       * Write the PPD file...
373       */
374 
375       ppdcArray *templocales = locales;
376 
377       if (!templocales && !single_language)
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))
390       {
391 	cupsFileClose(fp);
392 	return (1);
393       }
394 
395       if (templocales && templocales != locales)
396         templocales->release();
397 
398       cupsFileClose(fp);
399     }
400   }
401   else
402     usage();
403 
404   // Delete the printer driver information...
405   src->release();
406 
407   // Message catalog...
408   if (catalog)
409     catalog->release();
410 
411   // Return with no errors.
412   return (0);
413 }
414 
415 
416 //
417 // 'usage()' - Show usage and exit.
418 //
419 
420 static void
usage(void)421 usage(void)
422 {
423   _cupsLangPuts(stdout, _("Usage: ppdc [options] filename.drv [ ... "
424                           "filenameN.drv ]"));
425   _cupsLangPuts(stdout, _("Options:"));
426   _cupsLangPuts(stdout, _("  -D name=value           Set named variable to "
427                           "value."));
428   _cupsLangPuts(stdout, _("  -I include-dir          Add include directory to "
429                           "search path."));
430   _cupsLangPuts(stdout, _("  -c catalog.po           Load the specified "
431                           "message catalog."));
432   _cupsLangPuts(stdout, _("  -d output-dir           Specify the output "
433                           "directory."));
434   _cupsLangPuts(stdout, _("  -l lang[,lang,...]      Specify the output "
435                           "language(s) (locale)."));
436   _cupsLangPuts(stdout, _("  -m                      Use the ModelName value "
437                           "as the filename."));
438   _cupsLangPuts(stdout, _("  -t                      Test PPDs instead of "
439                           "generating them."));
440   _cupsLangPuts(stdout, _("  -v                      Be verbose."));
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 "
446                           "(Windows)."));
447   _cupsLangPuts(stdout, _("  --lf                    End lines with LF "
448                           "(UNIX/Linux/macOS)."));
449 
450   exit(1);
451 }
452