1//
2// "$Id: ppdc.cxx 3796 2012-04-23 22:54:48Z msweet $"
3//
4//   PPD file compiler main entry for the CUPS PPD Compiler.
5//
6//   Copyright 2007-2012 by Apple Inc.
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
25#include "ppdc-private.h"
26#include <unistd.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29
30
31//
32// Local functions...
33//
34
35static void	usage(void);
36
37
38//
39// 'main()' - Main entry for the PPD compiler.
40//
41
42int					// O - Exit status
43main(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
54			*outname,	// Output filename
55			make_model[1024],
56					// Make and model
57			pcfilename[1024],
58					// Lowercase pcfilename
59			filename[1024];	// PPD filename
60  int			comp,		// Compress
61			do_test,	// Test PPD files
62			single_language,// Generate single-language files
63			use_model_name,	// Use ModelName for filename
64			verbose;	// Verbosity
65  ppdcLineEnding	le;		// Line ending to use
66  ppdcArray		*locales;	// List of locales
67  cups_array_t		*filenames;	// List of generated filenames
68
69
70  _cupsSetLocale(argv);
71
72  // Scan the command-line...
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)_cups_strcasecmp, NULL);
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	{
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)
112	        _cupsLangPrintf(stdout,
113				_("ppdc: Adding include directory \"%s\"."),
114				argv[i]);
115
116	      ppdcSource::add_include(argv[i]);
117	      break;
118
119	  case 'c' :			// Message catalog...
120	      i ++;
121              if (i >= argc)
122                usage();
123
124              if (verbose > 1)
125	        _cupsLangPrintf(stdout,
126		                _("ppdc: Loading messages from \"%s\"."),
127				argv[i]);
128
129              if (!catalog)
130	        catalog = new ppdcCatalog("en");
131
132              if (catalog->load_messages(argv[i]))
133	      {
134        	_cupsLangPrintf(stderr,
135		                _("ppdc: Unable to load localization file "
136				  "\"%s\" - %s"), argv[i], strerror(errno));
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)
147	        _cupsLangPrintf(stdout,
148				_("ppdc: Writing PPD files to directory "
149				  "\"%s\"."), argv[i]);
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	      {
183	        single_language = 1;
184
185        	if (verbose > 1)
186	          _cupsLangPrintf(stdout,
187		                  _("ppdc: Loading messages for locale "
188				    "\"%s\"."), argv[i]);
189
190        	if (catalog)
191	          catalog->release();
192
193        	catalog = new ppdcCatalog(argv[i]);
194
195		if (catalog->messages->count == 0)
196		{
197        	  _cupsLangPrintf(stderr,
198				  _("ppdc: Unable to find localization for "
199				    "\"%s\" - %s"), argv[i], strerror(errno));
200                  return (1);
201		}
202	      }
203	      break;
204
205          case 'm' :			// Use ModelName for filename
206	      use_model_name = 1;
207	      break;
208
209          case 't' :			// Test PPDs instead of generating them
210	      do_test = 1;
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)
250        _cupsLangPrintf(stdout,
251	                _("ppdc: Loading driver information file \"%s\"."),
252			argv[i]);
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      {
265	_cupsLangPrintf(stderr,
266	                _("ppdc: Unable to create output directory %s: %s"),
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    {
277      if (do_test)
278      {
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	{
286	  _cupsLangPrintf(stderr,
287	                  _("ppdc: Unable to create output pipes: %s"),
288	                  strerror(errno));
289	  return (1);
290	}
291
292	if ((pid = fork()) == 0)
293	{
294	  // Child process comes here...
295	  dup2(fds[0], 0);
296
297	  close(fds[0]);
298	  close(fds[1]);
299
300	  execlp("cupstestppd", "cupstestppd", "-", (char *)0);
301
302	  _cupsLangPrintf(stderr,
303	                  _("ppdc: Unable to execute cupstestppd: %s"),
304			  strerror(errno));
305	  return (errno);
306	}
307	else if (pid < 0)
308	{
309	  _cupsLangPrintf(stderr, _("ppdc: Unable to execute cupstestppd: %s"),
310			  strerror(errno));
311	  return (errno);
312	}
313
314	close(fds[0]);
315	fp = cupsFileOpenFd(fds[1], "w");
316      }
317      else
318      {
319	// Write the PPD file for this driver...
320	if (use_model_name)
321	{
322	  if (!_cups_strncasecmp(d->model_name->value, d->manufacturer->value,
323	                   strlen(d->manufacturer->value)))
324	  {
325	    // Model name already starts with the manufacturer...
326            outname = d->model_name->value;
327	  }
328	  else
329	  {
330	    // Add manufacturer to the front of the model name...
331	    snprintf(make_model, sizeof(make_model), "%s %s",
332	             d->manufacturer->value, d->model_name->value);
333	    outname = make_model;
334	  }
335	}
336	else if (d->file_name)
337	  outname = d->file_name->value;
338	else
339	  outname = d->pc_file_name->value;
340
341	if (strstr(outname, ".PPD"))
342	{
343	  // Convert PCFileName to lowercase...
344	  for (j = 0;
345	       outname[j] && j < (int)(sizeof(pcfilename) - 1);
346	       j ++)
347	    pcfilename[j] = tolower(outname[j] & 255);
348
349	  pcfilename[j] = '\0';
350	}
351	else
352	{
353	  // Leave PCFileName as-is...
354	  strlcpy(pcfilename, outname, sizeof(pcfilename));
355	}
356
357	// Open the PPD file for writing...
358	if (comp)
359	  snprintf(filename, sizeof(filename), "%s/%s.gz", outdir, pcfilename);
360	else
361	  snprintf(filename, sizeof(filename), "%s/%s", outdir, pcfilename);
362
363        if (cupsArrayFind(filenames, filename))
364	  _cupsLangPrintf(stderr,
365	                  _("ppdc: Warning - overlapping filename \"%s\"."),
366			  filename);
367	else
368	  cupsArrayAdd(filenames, strdup(filename));
369
370	fp = cupsFileOpen(filename, comp ? "w9" : "w");
371	if (!fp)
372	{
373	  _cupsLangPrintf(stderr,
374	                  _("ppdc: Unable to create PPD file \"%s\" - %s."),
375			  filename, strerror(errno));
376	  return (1);
377	}
378
379	if (verbose)
380	  _cupsLangPrintf(stdout, _("ppdc: Writing %s."), filename);
381      }
382
383     /*
384      * Write the PPD file...
385      */
386
387      ppdcArray *templocales = locales;
388
389      if (!templocales && !single_language)
390      {
391	templocales = new ppdcArray();
392	for (ppdcCatalog *tempcatalog = (ppdcCatalog *)src->po_files->first();
393	     tempcatalog;
394	     tempcatalog = (ppdcCatalog *)src->po_files->next())
395	{
396	  tempcatalog->locale->retain();
397	  templocales->add(tempcatalog->locale);
398	}
399      }
400
401      if (d->write_ppd_file(fp, catalog, templocales, src, le))
402      {
403	cupsFileClose(fp);
404	return (1);
405      }
406
407      if (templocales != locales)
408        templocales->release();
409
410      cupsFileClose(fp);
411    }
412  }
413  else
414    usage();
415
416  // Delete the printer driver information...
417  src->release();
418
419  // Message catalog...
420  if (catalog)
421    catalog->release();
422
423  // Return with no errors.
424  return (0);
425}
426
427
428//
429// 'usage()' - Show usage and exit.
430//
431
432static void
433usage(void)
434{
435  _cupsLangPuts(stdout, _("Usage: ppdc [options] filename.drv [ ... "
436                          "filenameN.drv ]"));
437  _cupsLangPuts(stdout, _("Options:"));
438  _cupsLangPuts(stdout, _("  -D name=value           Set named variable to "
439                          "value."));
440  _cupsLangPuts(stdout, _("  -I include-dir          Add include directory to "
441                          "search path."));
442  _cupsLangPuts(stdout, _("  -c catalog.po           Load the specified "
443                          "message catalog."));
444  _cupsLangPuts(stdout, _("  -d output-dir           Specify the output "
445                          "directory."));
446  _cupsLangPuts(stdout, _("  -l lang[,lang,...]      Specify the output "
447                          "language(s) (locale)."));
448  _cupsLangPuts(stdout, _("  -m                      Use the ModelName value "
449                          "as the filename."));
450  _cupsLangPuts(stdout, _("  -t                      Test PPDs instead of "
451                          "generating them."));
452  _cupsLangPuts(stdout, _("  -v                      Be verbose."));
453  _cupsLangPuts(stdout, _("  -z                      Compress PPD files using "
454                          "GNU zip."));
455  _cupsLangPuts(stdout, _("  --cr                    End lines with CR (Mac "
456                          "OS 9)."));
457  _cupsLangPuts(stdout, _("  --crlf                  End lines with CR + LF "
458                          "(Windows)."));
459  _cupsLangPuts(stdout, _("  --lf                    End lines with LF "
460                          "(UNIX/Linux/OS X)."));
461
462  exit(1);
463}
464
465
466//
467// End of "$Id: ppdc.cxx 3796 2012-04-23 22:54:48Z msweet $".
468//
469