/*
 * TBCP port monitor for CUPS.
 *
 * Copyright 2007-2014 by Apple Inc.
 * Copyright 1993-2006 by Easy Software Products.
 *
 * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
 */

/*
 * Include necessary headers...
 */

#include <cups/cups-private.h>
#include <cups/ppd.h>


/*
 * Local functions...
 */

static char		*psgets(char *buf, size_t *bytes, FILE *fp);
static ssize_t		pswrite(const char *buf, size_t bytes);


/*
 * 'main()' - Main entry...
 */

int					/* O - Exit status */
main(int  argc,				/* I - Number of command-line args */
     char *argv[])			/* I - Command-line arguments */
{
  FILE		*fp;			/* File to print */
  int		copies;			/* Number of copies left */
  char		line[1024];		/* Line/buffer from stream/file */
  size_t	linelen;		/* Length of line */
  ppd_file_t	*ppd;			/* PPD file */


 /*
  * Check command-line...
  */

  if (argc < 6 || argc > 7)
  {
    _cupsLangPrintf(stderr,
                    _("Usage: %s job-id user title copies options [file]"),
		    argv[0]);
    return (1);
  }

  if (argc == 6)
  {
    copies = 1;
    fp     = stdin;
  }
  else
  {
    copies = atoi(argv[4]);
    fp     = fopen(argv[6], "rb");

    if (!fp)
    {
      perror(argv[6]);
      return (1);
    }
  }

 /*
  * Open the PPD file as needed...
  */

  ppd = ppdOpenFile(getenv("PPD"));

 /*
  * Copy the print file to stdout...
  */

  while (copies > 0)
  {
    copies --;

    if (ppd && ppd->jcl_begin)
      fputs(ppd->jcl_begin, stdout);
    if (ppd && ppd->jcl_ps)
      fputs(ppd->jcl_ps, stdout);

    if (!ppd || ppd->language_level == 1)
    {
     /*
      * Use setsoftwareiomode for BCP mode...
      */

      puts("%!PS-Adobe-3.0 ExitServer");
      puts("%%Title: (BCP - Level 1)");
      puts("%%EndComments");
      puts("%%BeginExitServer: 0");
      puts("serverdict begin 0 exitserver");
      puts("%%EndExitServer");
      puts("statusdict begin");
      puts("/setsoftwareiomode known {100 setsoftwareiomode}");
      puts("end");
      puts("%EOF");
    }
    else
    {
     /*
      * Use setdevparams for BCP mode...
      */

      puts("%!PS-Adobe-3.0");
      puts("%%Title: (BCP - Level 2)");
      puts("%%EndComments");
      puts("currentsysparams");
      puts("/CurInputDevice 2 copy known {");
      puts("get");
      puts("<</Protocol /Binary>> setdevparams");
      puts("}{");
      puts("pop pop");
      puts("} ifelse");
      puts("%EOF");
    }

    if (ppd && ppd->jcl_end)
      fputs(ppd->jcl_end, stdout);
    else if (!ppd || ppd->num_filters == 0)
      putchar(0x04);

   /*
    * Loop until we see end-of-file...
    */

    do
    {
      linelen = sizeof(line);
      if (psgets(line, &linelen, fp) == NULL)
	break;
    }
    while (pswrite(line, linelen) > 0);

    fflush(stdout);
  }

  return (0);
}


/*
 * 'psgets()' - Get a line from a file.
 *
 * Note:
 *
 *   This function differs from the gets() function in that it
 *   handles any combination of CR, LF, or CR LF to end input
 *   lines.
 */

static char *				/* O  - String or NULL if EOF */
psgets(char   *buf,			/* I  - Buffer to read into */
       size_t *bytes,			/* IO - Length of buffer */
       FILE   *fp)			/* I  - File to read from */
{
  char		*bufptr;		/* Pointer into buffer */
  int		ch;			/* Character from file */
  size_t	len;			/* Max length of string */


  len    = *bytes - 1;
  bufptr = buf;
  ch     = EOF;

  while ((size_t)(bufptr - buf) < len)
  {
    if ((ch = getc(fp)) == EOF)
      break;

    if (ch == '\r')
    {
     /*
      * Got a CR; see if there is a LF as well...
      */

      ch = getc(fp);

      if (ch != EOF && ch != '\n')
      {
        ungetc(ch, fp);	/* Nope, save it for later... */
        ch = '\r';
      }
      else
        *bufptr++ = '\r';
      break;
    }
    else if (ch == '\n')
      break;
    else
      *bufptr++ = (char)ch;
  }

 /*
  * Add a trailing newline if it is there...
  */

  if (ch == '\n' || ch == '\r')
  {
    if ((size_t)(bufptr - buf) < len)
      *bufptr++ = (char)ch;
    else
      ungetc(ch, fp);
  }

 /*
  * Nul-terminate the string and return it (or NULL for EOF).
  */

  *bufptr = '\0';
  *bytes  = (size_t)(bufptr - buf);

  if (ch == EOF && bufptr == buf)
    return (NULL);
  else
    return (buf);
}


/*
 * 'pswrite()' - Write data from a file.
 */

static ssize_t				/* O - Number of bytes written */
pswrite(const char *buf,		/* I - Buffer to write */
        size_t     bytes)		/* I - Bytes to write */
{
  size_t	count;			/* Remaining bytes */


  for (count = bytes; count > 0; count --, buf ++)
    switch (*buf)
    {
      case 0x04 : /* CTRL-D */
          if (bytes == 1)
	  {
	   /*
	    * Don't quote the last CTRL-D...
	    */

	    putchar(0x04);
	    break;
	  }

      case 0x01 : /* CTRL-A */
      case 0x03 : /* CTRL-C */
      case 0x05 : /* CTRL-E */
      case 0x11 : /* CTRL-Q */
      case 0x13 : /* CTRL-S */
      case 0x14 : /* CTRL-T */
      case 0x1c : /* CTRL-\ */
	  if (putchar(0x01) < 0)
	    return (-1);
	  if (putchar(*buf ^ 0x40) < 0)
	    return (-1);
	  break;

      default :
	  if (putchar(*buf) < 0)
	    return (-1);
	  break;
    }

  return ((ssize_t)bytes);
}
