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