copy_model(cupsd_client_t *con,		/* I - Client connection */
           const char     *from,	/* I - Source file */
           const char     *to)		/* I - Destination file */
{
  fd_set	input;			/* select() input set */
  struct timeval timeout;		/* select() timeout */
  int		maxfd;			/* Max file descriptor for select() */
  char		tempfile[1024];		/* Temporary PPD file */
  int		tempfd;			/* Temporary PPD file descriptor */
  int		temppid;		/* Process ID of cups-driverd */
  int		temppipe[2];		/* Temporary pipes */
  char		*argv[4],		/* Command-line arguments */
		*envp[MAX_ENV];		/* Environment */
  cups_file_t	*src,			/* Source file */
		*dst;			/* Destination file */
  ppd_file_t	*ppd;			/* PPD file */
  int		bytes,			/* Bytes from pipe */
		total;			/* Total bytes from pipe */
  char		buffer[2048];		/* Copy buffer */
  int		i;			/* Looping var */
  char		option[PPD_MAX_NAME],	/* Option name */
		choice[PPD_MAX_NAME];	/* Choice name */
  ppd_size_t	*size;			/* Default size */
  int		num_defaults;		/* Number of default options */
  cups_option_t	*defaults;		/* Default options */
  char		cups_protocol[PPD_MAX_LINE];
					/* cupsProtocol attribute */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_model(con=%p, from=\"%s\", to=\"%s\")", con, from, to);

 /*
  * Run cups-driverd to get the PPD file...
  */

  argv[0] = "cups-driverd";
  argv[1] = "cat";
  argv[2] = (char *)from;
  argv[3] = NULL;

  cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));

  snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
  snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->number);
  tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
  if (tempfd < 0 || cupsdOpenPipe(temppipe))
    return (-1);

  cupsdLogMessage(CUPSD_LOG_DEBUG,
                  "copy_model: Running \"cups-driverd cat %s\"...", from);

  if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
                         -1, -1, 0, DefaultProfile, NULL, &temppid))
  {
    close(tempfd);
    unlink(tempfile);

    return (-1);
  }

  close(temppipe[1]);

 /*
  * Wait up to 30 seconds for the PPD file to be copied...
  */

  total = 0;

  if (temppipe[0] > CGIPipes[0])
    maxfd = temppipe[0] + 1;
  else
    maxfd = CGIPipes[0] + 1;

  for (;;)
  {
   /*
    * See if we have data ready...
    */

    FD_ZERO(&input);
    FD_SET(temppipe[0], &input);
    FD_SET(CGIPipes[0], &input);

    timeout.tv_sec  = 30;
    timeout.tv_usec = 0;

    if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
    {
      if (errno == EINTR)
        continue;
      else
        break;
    }
    else if (i == 0)
    {
     /*
      * We have timed out...
      */

      break;
    }

    if (FD_ISSET(temppipe[0], &input))
    {
     /*
      * Read the PPD file from the pipe, and write it to the PPD file.
      */

      if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
      {
	if (write(tempfd, buffer, (size_t)bytes) < bytes)
          break;

	total += bytes;
      }
      else
	break;
    }

    if (FD_ISSET(CGIPipes[0], &input))
      cupsdUpdateCGI();
  }

  close(temppipe[0]);
  close(tempfd);

  if (!total)
  {
   /*
    * No data from cups-deviced...
    */

    cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file");
    unlink(tempfile);
    return (-1);
  }

 /*
  * Open the source file for a copy...
  */

  if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
  {
    unlink(tempfile);
    return (-1);
  }

 /*
  * Read the source file and see what page sizes are supported...
  */

  if ((ppd = _ppdOpen(src, _PPD_LOCALIZATION_NONE)) == NULL)
  {
    cupsFileClose(src);
    unlink(tempfile);
    return (-1);
  }

 /*
  * Open the destination (if possible) and set the default options...
  */

  num_defaults     = 0;
  defaults         = NULL;
  cups_protocol[0] = '\0';

  if ((dst = cupsFileOpen(to, "rb")) != NULL)
  {
   /*
    * Read all of the default lines from the old PPD...
    */

    while (cupsFileGets(dst, buffer, sizeof(buffer)))
      if (!strncmp(buffer, "*Default", 8))
      {
       /*
	* Add the default option...
	*/

        if (!ppd_parse_line(buffer, option, sizeof(option),
	                    choice, sizeof(choice)))
        {
	  ppd_option_t	*ppdo;		/* PPD option */


         /*
	  * Only add the default if the default hasn't already been
	  * set and the choice exists in the new PPD...
	  */

	  if (!cupsGetOption(option, num_defaults, defaults) &&
	      (ppdo = ppdFindOption(ppd, option)) != NULL &&
	      ppdFindChoice(ppdo, choice))
            num_defaults = cupsAddOption(option, choice, num_defaults,
	                                 &defaults);
        }
      }
      else if (!strncmp(buffer, "*cupsProtocol:", 14))
        strlcpy(cups_protocol, buffer, sizeof(cups_protocol));

    cupsFileClose(dst);
  }
  else if ((size = ppdPageSize(ppd, DefaultPaperSize)) != NULL)
  {
   /*
    * Add the default media sizes...
    */

    num_defaults = cupsAddOption("PageSize", size->name,
                                 num_defaults, &defaults);
    num_defaults = cupsAddOption("PageRegion", size->name,
                                 num_defaults, &defaults);
    num_defaults = cupsAddOption("PaperDimension", size->name,
                                 num_defaults, &defaults);
    num_defaults = cupsAddOption("ImageableArea", size->name,
                                 num_defaults, &defaults);
  }

  ppdClose(ppd);

 /*
  * Open the destination file for a copy...
  */

  if ((dst = cupsdCreateConfFile(to, ConfigFilePerm)) == NULL)
  {
    cupsFreeOptions(num_defaults, defaults);
    cupsFileClose(src);
    unlink(tempfile);
    return (-1);
  }

 /*
  * Copy the source file to the destination...
  */

  cupsFileRewind(src);

  while (cupsFileGets(src, buffer, sizeof(buffer)))
  {
    if (!strncmp(buffer, "*Default", 8))
    {
     /*
      * Check for an previous default option choice...
      */

      if (!ppd_parse_line(buffer, option, sizeof(option),
	                  choice, sizeof(choice)))
      {
        const char	*val;		/* Default option value */


        if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
	{
	 /*
	  * Substitute the previous choice...
	  */

	  snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
	}
      }
    }

    cupsFilePrintf(dst, "%s\n", buffer);
  }

  if (cups_protocol[0])
    cupsFilePrintf(dst, "%s\n", cups_protocol);

  cupsFreeOptions(num_defaults, defaults);

 /*
  * Close both files and return...
  */

  cupsFileClose(src);

  unlink(tempfile);

  return (cupsdCloseCreatedConfFile(dst, to));
}