Blame ppdc/ppdc.cxx

Packit 2fc92b
//
Packit 2fc92b
// PPD file compiler main entry for the CUPS PPD Compiler.
Packit 2fc92b
//
Packit 2fc92b
// Copyright 2007-2014 by Apple Inc.
Packit 2fc92b
// Copyright 2002-2007 by Easy Software Products.
Packit 2fc92b
//
Packit 2fc92b
// These coded instructions, statements, and computer programs are the
Packit 2fc92b
// property of Apple Inc. and are protected by Federal copyright
Packit 2fc92b
// law.  Distribution and use rights are outlined in the file "LICENSE.txt"
Packit 2fc92b
// which should have been included with this file.  If this file is
Packit 2fc92b
// missing or damaged, see the license at "http://www.cups.org/".
Packit 2fc92b
//
Packit 2fc92b
Packit 2fc92b
//
Packit 2fc92b
// Include necessary headers...
Packit 2fc92b
//
Packit 2fc92b
Packit 2fc92b
#include "ppdc-private.h"
Packit 2fc92b
#include <unistd.h>
Packit 2fc92b
#include <sys/stat.h>
Packit 2fc92b
#include <sys/types.h>
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
//
Packit 2fc92b
// Local functions...
Packit 2fc92b
//
Packit 2fc92b
Packit 2fc92b
static void	usage(void);
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
//
Packit 2fc92b
// 'main()' - Main entry for the PPD compiler.
Packit 2fc92b
//
Packit 2fc92b
Packit 2fc92b
int					// O - Exit status
Packit 2fc92b
main(int  argc,				// I - Number of command-line arguments
Packit 2fc92b
     char *argv[])			// I - Command-line arguments
Packit 2fc92b
{
Packit 2fc92b
  int			i, j;		// Looping vars
Packit 2fc92b
  ppdcCatalog		*catalog;	// Message catalog
Packit 2fc92b
  const char		*outdir;	// Output directory
Packit 2fc92b
  ppdcSource		*src;		// PPD source file data
Packit 2fc92b
  ppdcDriver		*d;		// Current driver
Packit 2fc92b
  cups_file_t		*fp;		// PPD file
Packit 2fc92b
  char			*opt,		// Current option
Packit 2fc92b
			*value,		// Value in option
Packit 2fc92b
			*outname,	// Output filename
Packit 2fc92b
			make_model[1024],
Packit 2fc92b
					// Make and model
Packit 2fc92b
			pcfilename[1024],
Packit 2fc92b
					// Lowercase pcfilename
Packit 2fc92b
			filename[1024];	// PPD filename
Packit 2fc92b
  int			comp,		// Compress
Packit 2fc92b
			do_test,	// Test PPD files
Packit 2fc92b
			single_language,// Generate single-language files
Packit 2fc92b
			use_model_name,	// Use ModelName for filename
Packit 2fc92b
			verbose;	// Verbosity
Packit 2fc92b
  ppdcLineEnding	le;		// Line ending to use
Packit 2fc92b
  ppdcArray		*locales;	// List of locales
Packit 2fc92b
  cups_array_t		*filenames;	// List of generated filenames
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  _cupsSetLocale(argv);
Packit 2fc92b
Packit 2fc92b
  // Scan the command-line...
Packit 2fc92b
  catalog         = NULL;
Packit 2fc92b
  comp            = 0;
Packit 2fc92b
  do_test         = 0;
Packit 2fc92b
  le              = PPDC_LFONLY;
Packit 2fc92b
  locales         = NULL;
Packit 2fc92b
  outdir          = "ppd";
Packit 2fc92b
  single_language = 0;
Packit 2fc92b
  src             = new ppdcSource();
Packit 2fc92b
  use_model_name  = 0;
Packit 2fc92b
  verbose         = 0;
Packit 2fc92b
  filenames       = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL);
Packit 2fc92b
Packit 2fc92b
  for (i = 1; i < argc; i ++)
Packit 2fc92b
    if (argv[i][0] == '-')
Packit 2fc92b
    {
Packit 2fc92b
      for (opt = argv[i] + 1; *opt; opt ++)
Packit 2fc92b
        switch (*opt)
Packit 2fc92b
	{
Packit 2fc92b
          case 'D' :			// Define variable
Packit 2fc92b
	      i ++;
Packit 2fc92b
	      if (i >= argc)
Packit 2fc92b
	        usage();
Packit 2fc92b
Packit 2fc92b
              if ((value = strchr(argv[i], '=')) != NULL)
Packit 2fc92b
	      {
Packit 2fc92b
	        *value++ = '\0';
Packit 2fc92b
Packit 2fc92b
	        src->set_variable(argv[i], value);
Packit 2fc92b
	      }
Packit 2fc92b
	      else
Packit 2fc92b
	        src->set_variable(argv[i], "1");
Packit 2fc92b
              break;
Packit 2fc92b
Packit 2fc92b
          case 'I' :			// Include directory...
Packit 2fc92b
	      i ++;
Packit 2fc92b
	      if (i >= argc)
Packit 2fc92b
        	usage();
Packit 2fc92b
Packit 2fc92b
              if (verbose > 1)
Packit 2fc92b
	        _cupsLangPrintf(stdout,
Packit 2fc92b
				_("ppdc: Adding include directory \"%s\"."),
Packit 2fc92b
				argv[i]);
Packit 2fc92b
Packit 2fc92b
	      ppdcSource::add_include(argv[i]);
Packit 2fc92b
	      break;
Packit 2fc92b
Packit 2fc92b
	  case 'c' :			// Message catalog...
Packit 2fc92b
	      i ++;
Packit 2fc92b
              if (i >= argc)
Packit 2fc92b
                usage();
Packit 2fc92b
Packit 2fc92b
              if (verbose > 1)
Packit 2fc92b
	        _cupsLangPrintf(stdout,
Packit 2fc92b
		                _("ppdc: Loading messages from \"%s\"."),
Packit 2fc92b
				argv[i]);
Packit 2fc92b
Packit 2fc92b
              if (!catalog)
Packit 2fc92b
	        catalog = new ppdcCatalog("en");
Packit 2fc92b
Packit 2fc92b
              if (catalog->load_messages(argv[i]))
Packit 2fc92b
	      {
Packit 2fc92b
        	_cupsLangPrintf(stderr,
Packit 2fc92b
		                _("ppdc: Unable to load localization file "
Packit 2fc92b
				  "\"%s\" - %s"), argv[i], strerror(errno));
Packit 2fc92b
                return (1);
Packit 2fc92b
	      }
Packit 2fc92b
	      break;
Packit 2fc92b
Packit 2fc92b
          case 'd' :			// Output directory...
Packit 2fc92b
	      i ++;
Packit 2fc92b
	      if (i >= argc)
Packit 2fc92b
        	usage();
Packit 2fc92b
Packit 2fc92b
              if (verbose > 1)
Packit 2fc92b
	        _cupsLangPrintf(stdout,
Packit 2fc92b
				_("ppdc: Writing PPD files to directory "
Packit 2fc92b
				  "\"%s\"."), argv[i]);
Packit 2fc92b
Packit 2fc92b
	      outdir = argv[i];
Packit 2fc92b
	      break;
Packit 2fc92b
Packit 2fc92b
          case 'l' :			// Language(s)...
Packit 2fc92b
	      i ++;
Packit 2fc92b
	      if (i >= argc)
Packit 2fc92b
        	usage();
Packit 2fc92b
Packit 2fc92b
              if (strchr(argv[i], ','))
Packit 2fc92b
	      {
Packit 2fc92b
	        // Comma-delimited list of languages...
Packit 2fc92b
		char	temp[1024],	// Copy of language list
Packit 2fc92b
			*start,		// Start of current locale name
Packit 2fc92b
			*end;		// End of current locale name
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
		locales = new ppdcArray();
Packit 2fc92b
Packit 2fc92b
		strlcpy(temp, argv[i], sizeof(temp));
Packit 2fc92b
		for (start = temp; *start; start = end)
Packit 2fc92b
		{
Packit 2fc92b
		  if ((end = strchr(start, ',')) != NULL)
Packit 2fc92b
		    *end++ = '\0';
Packit 2fc92b
		  else
Packit 2fc92b
		    end = start + strlen(start);
Packit 2fc92b
Packit 2fc92b
                  if (end > start)
Packit 2fc92b
		    locales->add(new ppdcString(start));
Packit 2fc92b
		}
Packit 2fc92b
	      }
Packit 2fc92b
	      else
Packit 2fc92b
	      {
Packit 2fc92b
	        single_language = 1;
Packit 2fc92b
Packit 2fc92b
        	if (verbose > 1)
Packit 2fc92b
	          _cupsLangPrintf(stdout,
Packit 2fc92b
		                  _("ppdc: Loading messages for locale "
Packit 2fc92b
				    "\"%s\"."), argv[i]);
Packit 2fc92b
Packit 2fc92b
        	if (catalog)
Packit 2fc92b
	          catalog->release();
Packit 2fc92b
Packit 2fc92b
        	catalog = new ppdcCatalog(argv[i]);
Packit 2fc92b
Packit 2fc92b
		if (catalog->messages->count == 0 && strcmp(argv[i], "en"))
Packit 2fc92b
		{
Packit 2fc92b
        	  _cupsLangPrintf(stderr,
Packit 2fc92b
				  _("ppdc: Unable to find localization for "
Packit 2fc92b
				    "\"%s\" - %s"), argv[i], strerror(errno));
Packit 2fc92b
                  return (1);
Packit 2fc92b
		}
Packit 2fc92b
	      }
Packit 2fc92b
	      break;
Packit 2fc92b
Packit 2fc92b
          case 'm' :			// Use ModelName for filename
Packit 2fc92b
	      use_model_name = 1;
Packit 2fc92b
	      break;
Packit 2fc92b
Packit 2fc92b
          case 't' :			// Test PPDs instead of generating them
Packit 2fc92b
	      do_test = 1;
Packit 2fc92b
	      break;
Packit 2fc92b
Packit 2fc92b
          case 'v' :			// Be verbose...
Packit 2fc92b
	      verbose ++;
Packit 2fc92b
	      break;
Packit 2fc92b
Packit 2fc92b
          case 'z' :			// Compress files...
Packit 2fc92b
	      comp = 1;
Packit 2fc92b
	      break;
Packit 2fc92b
Packit 2fc92b
	  case '-' :			// --option
Packit 2fc92b
	      if (!strcmp(opt, "-lf"))
Packit 2fc92b
	      {
Packit 2fc92b
		le  = PPDC_LFONLY;
Packit 2fc92b
		opt += strlen(opt) - 1;
Packit 2fc92b
		break;
Packit 2fc92b
	      }
Packit 2fc92b
	      else if (!strcmp(opt, "-cr"))
Packit 2fc92b
	      {
Packit 2fc92b
		le  = PPDC_CRONLY;
Packit 2fc92b
		opt += strlen(opt) - 1;
Packit 2fc92b
		break;
Packit 2fc92b
	      }
Packit 2fc92b
	      else if (!strcmp(opt, "-crlf"))
Packit 2fc92b
	      {
Packit 2fc92b
		le  = PPDC_CRLF;
Packit 2fc92b
		opt += strlen(opt) - 1;
Packit 2fc92b
		break;
Packit 2fc92b
	      }
Packit 2fc92b
Packit 2fc92b
	  default :			// Unknown
Packit 2fc92b
	      usage();
Packit 2fc92b
	      break;
Packit 2fc92b
	}
Packit 2fc92b
    }
Packit 2fc92b
    else
Packit 2fc92b
    {
Packit 2fc92b
      // Open and load the driver info file...
Packit 2fc92b
      if (verbose > 1)
Packit 2fc92b
        _cupsLangPrintf(stdout,
Packit 2fc92b
	                _("ppdc: Loading driver information file \"%s\"."),
Packit 2fc92b
			argv[i]);
Packit 2fc92b
Packit 2fc92b
      src->read_file(argv[i]);
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  if (src->drivers->count > 0)
Packit 2fc92b
  {
Packit 2fc92b
    // Create the output directory...
Packit 2fc92b
    if (mkdir(outdir, 0777))
Packit 2fc92b
    {
Packit 2fc92b
      if (errno != EEXIST)
Packit 2fc92b
      {
Packit 2fc92b
	_cupsLangPrintf(stderr,
Packit 2fc92b
	                _("ppdc: Unable to create output directory %s: %s"),
Packit 2fc92b
	        outdir, strerror(errno));
Packit 2fc92b
        return (1);
Packit 2fc92b
      }
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
    // Write PPD files...
Packit 2fc92b
    for (d = (ppdcDriver *)src->drivers->first();
Packit 2fc92b
         d;
Packit 2fc92b
	 d = (ppdcDriver *)src->drivers->next())
Packit 2fc92b
    {
Packit 2fc92b
      if (do_test)
Packit 2fc92b
      {
Packit 2fc92b
        // Test the PPD file for this driver...
Packit 2fc92b
	int	pid,			// Process ID
Packit 2fc92b
		fds[2];			// Pipe file descriptors
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
        if (pipe(fds))
Packit 2fc92b
	{
Packit 2fc92b
	  _cupsLangPrintf(stderr,
Packit 2fc92b
	                  _("ppdc: Unable to create output pipes: %s"),
Packit 2fc92b
	                  strerror(errno));
Packit 2fc92b
	  return (1);
Packit 2fc92b
	}
Packit 2fc92b
Packit 2fc92b
	if ((pid = fork()) == 0)
Packit 2fc92b
	{
Packit 2fc92b
	  // Child process comes here...
Packit 2fc92b
	  dup2(fds[0], 0);
Packit 2fc92b
Packit 2fc92b
	  close(fds[0]);
Packit 2fc92b
	  close(fds[1]);
Packit 2fc92b
Packit 2fc92b
	  execlp("cupstestppd", "cupstestppd", "-", (char *)0);
Packit 2fc92b
Packit 2fc92b
	  _cupsLangPrintf(stderr,
Packit 2fc92b
	                  _("ppdc: Unable to execute cupstestppd: %s"),
Packit 2fc92b
			  strerror(errno));
Packit 2fc92b
	  return (errno);
Packit 2fc92b
	}
Packit 2fc92b
	else if (pid < 0)
Packit 2fc92b
	{
Packit 2fc92b
	  _cupsLangPrintf(stderr, _("ppdc: Unable to execute cupstestppd: %s"),
Packit 2fc92b
			  strerror(errno));
Packit 2fc92b
	  return (errno);
Packit 2fc92b
	}
Packit 2fc92b
Packit 2fc92b
	close(fds[0]);
Packit 2fc92b
	fp = cupsFileOpenFd(fds[1], "w");
Packit 2fc92b
      }
Packit 2fc92b
      else
Packit 2fc92b
      {
Packit 2fc92b
	// Write the PPD file for this driver...
Packit 2fc92b
	if (use_model_name)
Packit 2fc92b
	{
Packit 2fc92b
	  if (!_cups_strncasecmp(d->model_name->value, d->manufacturer->value,
Packit 2fc92b
	                   strlen(d->manufacturer->value)))
Packit 2fc92b
	  {
Packit 2fc92b
	    // Model name already starts with the manufacturer...
Packit 2fc92b
            outname = d->model_name->value;
Packit 2fc92b
	  }
Packit 2fc92b
	  else
Packit 2fc92b
	  {
Packit 2fc92b
	    // Add manufacturer to the front of the model name...
Packit 2fc92b
	    snprintf(make_model, sizeof(make_model), "%s %s",
Packit 2fc92b
	             d->manufacturer->value, d->model_name->value);
Packit 2fc92b
	    outname = make_model;
Packit 2fc92b
	  }
Packit 2fc92b
	}
Packit 2fc92b
	else if (d->file_name)
Packit 2fc92b
	  outname = d->file_name->value;
Packit 2fc92b
	else
Packit 2fc92b
	  outname = d->pc_file_name->value;
Packit 2fc92b
Packit 2fc92b
	if (strstr(outname, ".PPD"))
Packit 2fc92b
	{
Packit 2fc92b
	  // Convert PCFileName to lowercase...
Packit 2fc92b
	  for (j = 0;
Packit 2fc92b
	       outname[j] && j < (int)(sizeof(pcfilename) - 1);
Packit 2fc92b
	       j ++)
Packit 2fc92b
	    pcfilename[j] = (char)tolower(outname[j] & 255);
Packit 2fc92b
Packit 2fc92b
	  pcfilename[j] = '\0';
Packit 2fc92b
	}
Packit 2fc92b
	else
Packit 2fc92b
	{
Packit 2fc92b
	  // Leave PCFileName as-is...
Packit 2fc92b
	  strlcpy(pcfilename, outname, sizeof(pcfilename));
Packit 2fc92b
	}
Packit 2fc92b
Packit 2fc92b
	// Open the PPD file for writing...
Packit 2fc92b
	if (comp)
Packit 2fc92b
	  snprintf(filename, sizeof(filename), "%s/%s.gz", outdir, pcfilename);
Packit 2fc92b
	else
Packit 2fc92b
	  snprintf(filename, sizeof(filename), "%s/%s", outdir, pcfilename);
Packit 2fc92b
Packit 2fc92b
        if (cupsArrayFind(filenames, filename))
Packit 2fc92b
	  _cupsLangPrintf(stderr,
Packit 2fc92b
	                  _("ppdc: Warning - overlapping filename \"%s\"."),
Packit 2fc92b
			  filename);
Packit 2fc92b
	else
Packit 2fc92b
	  cupsArrayAdd(filenames, strdup(filename));
Packit 2fc92b
Packit 2fc92b
	fp = cupsFileOpen(filename, comp ? "w9" : "w");
Packit 2fc92b
	if (!fp)
Packit 2fc92b
	{
Packit 2fc92b
	  _cupsLangPrintf(stderr,
Packit 2fc92b
	                  _("ppdc: Unable to create PPD file \"%s\" - %s."),
Packit 2fc92b
			  filename, strerror(errno));
Packit 2fc92b
	  return (1);
Packit 2fc92b
	}
Packit 2fc92b
Packit 2fc92b
	if (verbose)
Packit 2fc92b
	  _cupsLangPrintf(stdout, _("ppdc: Writing %s."), filename);
Packit 2fc92b
      }
Packit 2fc92b
Packit 2fc92b
     /*
Packit 2fc92b
      * Write the PPD file...
Packit 2fc92b
      */
Packit 2fc92b
Packit 2fc92b
      ppdcArray *templocales = locales;
Packit 2fc92b
Packit 2fc92b
      if (!templocales && !single_language)
Packit 2fc92b
      {
Packit 2fc92b
	templocales = new ppdcArray();
Packit 2fc92b
	for (ppdcCatalog *tempcatalog = (ppdcCatalog *)src->po_files->first();
Packit 2fc92b
	     tempcatalog;
Packit 2fc92b
	     tempcatalog = (ppdcCatalog *)src->po_files->next())
Packit 2fc92b
	{
Packit 2fc92b
	  tempcatalog->locale->retain();
Packit 2fc92b
	  templocales->add(tempcatalog->locale);
Packit 2fc92b
	}
Packit 2fc92b
      }
Packit 2fc92b
Packit 2fc92b
      if (d->write_ppd_file(fp, catalog, templocales, src, le))
Packit 2fc92b
      {
Packit 2fc92b
	cupsFileClose(fp);
Packit 2fc92b
	return (1);
Packit 2fc92b
      }
Packit 2fc92b
Packit 2fc92b
      if (templocales != locales)
Packit 2fc92b
        templocales->release();
Packit 2fc92b
Packit 2fc92b
      cupsFileClose(fp);
Packit 2fc92b
    }
Packit 2fc92b
  }
Packit 2fc92b
  else
Packit 2fc92b
    usage();
Packit 2fc92b
Packit 2fc92b
  // Delete the printer driver information...
Packit 2fc92b
  src->release();
Packit 2fc92b
Packit 2fc92b
  // Message catalog...
Packit 2fc92b
  if (catalog)
Packit 2fc92b
    catalog->release();
Packit 2fc92b
Packit 2fc92b
  // Return with no errors.
Packit 2fc92b
  return (0);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
//
Packit 2fc92b
// 'usage()' - Show usage and exit.
Packit 2fc92b
//
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
usage(void)
Packit 2fc92b
{
Packit 2fc92b
  _cupsLangPuts(stdout, _("Usage: ppdc [options] filename.drv [ ... "
Packit 2fc92b
                          "filenameN.drv ]"));
Packit 2fc92b
  _cupsLangPuts(stdout, _("Options:"));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  -D name=value           Set named variable to "
Packit 2fc92b
                          "value."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  -I include-dir          Add include directory to "
Packit 2fc92b
                          "search path."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  -c catalog.po           Load the specified "
Packit 2fc92b
                          "message catalog."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  -d output-dir           Specify the output "
Packit 2fc92b
                          "directory."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  -l lang[,lang,...]      Specify the output "
Packit 2fc92b
                          "language(s) (locale)."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  -m                      Use the ModelName value "
Packit 2fc92b
                          "as the filename."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  -t                      Test PPDs instead of "
Packit 2fc92b
                          "generating them."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  -v                      Be verbose."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  -z                      Compress PPD files using "
Packit 2fc92b
                          "GNU zip."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  --cr                    End lines with CR (Mac "
Packit 2fc92b
                          "OS 9)."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  --crlf                  End lines with CR + LF "
Packit 2fc92b
                          "(Windows)."));
Packit 2fc92b
  _cupsLangPuts(stdout, _("  --lf                    End lines with LF "
Packit 2fc92b
                          "(UNIX/Linux/macOS)."));
Packit 2fc92b
Packit 2fc92b
  exit(1);
Packit 2fc92b
}