copy_banner(cupsd_client_t *con,	/* I - Client connection */
            cupsd_job_t    *job,	/* I - Job information */
            const char     *name)	/* I - Name of banner */
{
  int		i;			/* Looping var */
  int		kbytes;			/* Size of banner file in kbytes */
  char		filename[1024];		/* Job filename */
  cupsd_banner_t *banner;		/* Pointer to banner */
  cups_file_t	*in;			/* Input file */
  cups_file_t	*out;			/* Output file */
  int		ch;			/* Character from file */
  char		attrname[255],		/* Name of attribute */
		*s;			/* Pointer into name */
  ipp_attribute_t *attr;		/* Attribute */


  cupsdLogMessage(CUPSD_LOG_DEBUG2,
                  "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
                  con, con ? con->number : -1, job, job->id,
		  name ? name : "(null)");

 /*
  * Find the banner; return if not found or "none"...
  */

  if (!name || !strcmp(name, "none") ||
      (banner = cupsdFindBanner(name)) == NULL)
    return (0);

 /*
  * Open the banner and job files...
  */

  if (add_file(con, job, banner->filetype, 0))
    return (-1);

  snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
           job->num_files);
  if ((out = cupsFileOpen(filename, "w")) == NULL)
  {
    cupsdLogMessage(CUPSD_LOG_ERROR,
                    "Unable to create banner job file %s - %s",
                    filename, strerror(errno));
    job->num_files --;
    return (0);
  }

  fchmod(cupsFileNumber(out), 0640);
  fchown(cupsFileNumber(out), RunUser, Group);

 /*
  * Try the localized banner file under the subdirectory...
  */

  strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
          sizeof(attrname));
  if (strlen(attrname) > 2 && attrname[2] == '-')
  {
   /*
    * Convert ll-cc to ll_CC...
    */

    attrname[2] = '_';
    attrname[3] = (char)toupper(attrname[3] & 255);
    attrname[4] = (char)toupper(attrname[4] & 255);
  }

  snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
           attrname, name);

  if (access(filename, 0) && strlen(attrname) > 2)
  {
   /*
    * Wasn't able to find "ll_CC" locale file; try the non-national
    * localization banner directory.
    */

    attrname[2] = '\0';

    snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
             attrname, name);
  }

  if (access(filename, 0))
  {
   /*
    * Use the non-localized banner file.
    */

    snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
  }

  if ((in = cupsFileOpen(filename, "r")) == NULL)
  {
    cupsFileClose(out);
    unlink(filename);
    cupsdLogMessage(CUPSD_LOG_ERROR,
                    "Unable to open banner template file %s - %s",
                    filename, strerror(errno));
    job->num_files --;
    return (0);
  }

 /*
  * Parse the file to the end...
  */

  while ((ch = cupsFileGetChar(in)) != EOF)
    if (ch == '{')
    {
     /*
      * Get an attribute name...
      */

      for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
        if (!isalpha(ch & 255) && ch != '-' && ch != '?')
          break;
	else if (s < (attrname + sizeof(attrname) - 1))
          *s++ = (char)ch;
	else
	  break;

      *s = '\0';

      if (ch != '}')
      {
       /*
        * Ignore { followed by stuff that is not an attribute name...
	*/

        cupsFilePrintf(out, "{%s%c", attrname, ch);
	continue;
      }

     /*
      * See if it is defined...
      */

      if (attrname[0] == '?')
        s = attrname + 1;
      else
        s = attrname;

      if (!strcmp(s, "printer-name"))
      {
        cupsFilePuts(out, job->dest);
	continue;
      }
      else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
      {
       /*
        * See if we have a leading question mark...
	*/

	if (attrname[0] != '?')
	{
	 /*
          * Nope, write to file as-is; probably a PostScript procedure...
	  */

	  cupsFilePrintf(out, "{%s}", attrname);
        }

        continue;
      }

     /*
      * Output value(s)...
      */

      for (i = 0; i < attr->num_values; i ++)
      {
	if (i)
	  cupsFilePutChar(out, ',');

	switch (attr->value_tag)
	{
	  case IPP_TAG_INTEGER :
	  case IPP_TAG_ENUM :
	      if (!strncmp(s, "time-at-", 8))
	      {
	        struct timeval tv;	/* Time value */

		tv.tv_sec  = attr->values[i].integer;
		tv.tv_usec = 0;

	        cupsFilePuts(out, cupsdGetDateTime(&tv, CUPSD_TIME_STANDARD));
	      }
	      else
	        cupsFilePrintf(out, "%d", attr->values[i].integer);
	      break;

	  case IPP_TAG_BOOLEAN :
	      cupsFilePrintf(out, "%d", attr->values[i].boolean);
	      break;

	  case IPP_TAG_NOVALUE :
	      cupsFilePuts(out, "novalue");
	      break;

	  case IPP_TAG_RANGE :
	      cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
		      attr->values[i].range.upper);
	      break;

	  case IPP_TAG_RESOLUTION :
	      cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
		      attr->values[i].resolution.yres,
		      attr->values[i].resolution.units == IPP_RES_PER_INCH ?
			  "dpi" : "dpcm");
	      break;

	  case IPP_TAG_URI :
          case IPP_TAG_STRING :
	  case IPP_TAG_TEXT :
	  case IPP_TAG_NAME :
	  case IPP_TAG_KEYWORD :
	  case IPP_TAG_CHARSET :
	  case IPP_TAG_LANGUAGE :
	      if (!_cups_strcasecmp(banner->filetype->type, "postscript"))
	      {
	       /*
	        * Need to quote strings for PS banners...
		*/

	        const char *p;

		for (p = attr->values[i].string.text; *p; p ++)
		{
		  if (*p == '(' || *p == ')' || *p == '\\')
		  {
		    cupsFilePutChar(out, '\\');
		    cupsFilePutChar(out, *p);
		  }
		  else if (*p < 32 || *p > 126)
		    cupsFilePrintf(out, "\\%03o", *p & 255);
		  else
		    cupsFilePutChar(out, *p);
		}
	      }
	      else
		cupsFilePuts(out, attr->values[i].string.text);
	      break;

          default :
	      break; /* anti-compiler-warning-code */
	}
      }
    }
    else if (ch == '\\')	/* Quoted char */
    {
      ch = cupsFileGetChar(in);

      if (ch != '{')		/* Only do special handling for \{ */
        cupsFilePutChar(out, '\\');

      cupsFilePutChar(out, ch);
    }
    else
      cupsFilePutChar(out, ch);

  cupsFileClose(in);

  kbytes = (cupsFileTell(out) + 1023) / 1024;

  job->koctets += kbytes;

  if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
    attr->values[0].integer += kbytes;

  cupsFileClose(out);

  return (kbytes);
}