/*
 * $Header: /home/gene/library/website/docsrc/psw/RCS/print-postscript.c,v 395.1 2008/04/20 17:25:47 gene Exp $
 *
 * Copyright (c) 2004 Gene Michael Stover.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */
/*
 * In Windows, print a PostScript file to a PostScript printer.
 * With this version, the printer must understand PostScript in
 * its firmware.  It doesn't matter whether the printer's driver
 * understands PostScript because we by-pass the driver.
 *
 * Documentation, executable file, & source code are at
 * <http://lisp-p.org/psw/> (primary site) and
 * <http://cybertiggyr.com/gene/psw/> (mirror site).
 *
 * Index of relevant functions on MSDN is at
 * <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/prntspol_7mgj.asp>
 *
 */

/* Standard C */
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Microsloth Winders */
#include <windows.h>

/*
 */
enum { ACTION_PRINT, ACTION_ENUM_PRINTERS, ACTION_USAGE };
static int S_action = ACTION_PRINT;

/*
 */
static char *S_progname = "print-postscript";

/*
 */
static char *S_pathname = "standard input";
static FILE *S_strm = stdin;
static char *S_printername;

/*
 */
static char *S_datatype = "RAW";

static int 
S_CommandLine (int argc, char *argv[])
{
  int rc = 0, optind = 1;

  while (rc == 0 && optind < argc && argv[optind][0] == '-') {
    /* printf ("\nargv[%d] is \"%s\"", optind, argv[optind]); */
    if (strcmp (argv[optind], "-f") == 0) {
      S_pathname = argv[++optind];
      S_strm = fopen (S_pathname, "r");
      if (S_strm != NULL) {
	/* good */
      } else {
	fprintf (stderr, "\n%s:%d: fopen: %s", __FILE__, __LINE__,
		 strerror (errno));
	fprintf (stderr, "\n%s:%d: Can't open \"%s\" for input", __FILE__,
		 __LINE__, argv[optind]);
	rc = 111;
      }
    } else if (strcmp (argv[optind], "--help") == 0 ||
	       strcmp (argv[optind], "-?") == 0 ||
	       strcmp (argv[optind], "-h") == 0 ||
	       strcmp (argv[optind], "/?") == 0 ||
	       strcmp (argv[optind], "/h") == 0 ||
	       strcmp (argv[optind], "/help") == 0) {
      S_action = ACTION_USAGE;
    } else if (strcmp (argv[optind], "-l") == 0) {
      S_action = ACTION_ENUM_PRINTERS;
    } else if (strcmp (argv[optind], "-t") == 0) {
      S_datatype = argv[++optind];
    } else {
      fprintf (stderr, "\n%s:%d: Unexpected option, \"%s\".", __FILE__,
	       __LINE__, argv[optind]);
      rc = 33;
    }
    ++optind;
  }
  if (rc == 0 && S_action == ACTION_PRINT) {
    if (optind < argc) {
      S_printername = argv[optind++];
      if (optind < argc) {
	fprintf (stderr, "\n%s:%d: Ignoring excess command line arguments",
		 __FILE__, __LINE__);
      }
    } else {
      fprintf (stderr, "\n%s:%d: Missing printer name on the command line",
	       __FILE__, __LINE__);
      rc = 120;
    }
  } else {
    /* 
     * There was a problem, or the action isn't to print.  Either way,
     * we don't bother to fetch a printer name.
     */
  }
  return rc;
}

static int
S_SetPostScript (HANDLE printer)
{
  static char escape[] = "\033%-12345X@PJL \n";
  static char postscript[] = "@PJL ENTER LANGUAGE = POSTSCRIPT \n";
  int rc = 0;
  DWORD written;

  if (strcmp (S_datatype, "RAW") == 0) {
    /*
     * We'll send the PostScript file as raw data.  This implies that
     * the printer understands PostScript natively, not relying on
     * a PostScript driver.  So we first send commands to ensure
     * that the printer is in PostScript mode.
     */
    if (WritePrinter (printer, escape, strlen (escape), &written) &&
	WritePrinter (printer, postscript, strlen (postscript), &written)) {
      /* good */
    } else {
      fprintf (stderr, "\n%s:%d: WritePrinter failed (%lu)", __FILE__,
	       __LINE__, (unsigned long) GetLastError ());
      rc = 3;
    }
  } else {
    /*
     * We're not sending raw data, so we rely on the driver to put the
     * printer in the proper mode, or to process the PostScript itself.
     */
  }
  return rc;
}

static HANDLE
S_StartPrinter (char printername[])
{
  HANDLE hnd = INVALID_HANDLE_VALUE;
  DOC_INFO_1 di1;
  DWORD job;

  if (OpenPrinter (printername, &hnd, NULL)) {
    di1.pDocName = S_pathname;
    di1.pOutputFile = NULL;
    di1.pDatatype = S_datatype;
    job = StartDocPrinter (hnd, 1, (LPBYTE) &di1);
    if (job != 0) {
      if (S_SetPostScript (hnd) == 0) {
	/* good */
      } else {
	fprintf (stderr, "\n%s:%d: S_SetPostScript failed", __FILE__,
		 __LINE__);
	ClosePrinter (hnd);
	hnd = INVALID_HANDLE_VALUE;
      }
    } else {
      fprintf (stderr, "\n%s:%d: StartDocPrinter failed (%lu)", __FILE__,
	       __LINE__, (unsigned long) GetLastError ());
      ClosePrinter (hnd);
      hnd = INVALID_HANDLE_VALUE;
    }
  } else {
    fprintf (stderr, "\n%s:%d: OpenPrinter failed (%lu)", __FILE__, __LINE__,
	     (unsigned long) GetLastError ());
  }
  return hnd;
}

static void
S_StopPrinter (HANDLE hnd)
{
  if (EndDocPrinter (hnd)) {
    /* good */
  } else {
    fprintf (stderr, "\n%s:%d: EndDocPrinter failed (%lu)", __FILE__,
	     __LINE__, (unsigned long) GetLastError ());
  }
  ClosePrinter (hnd);
}

static int
S_Loop (FILE *file, FILE *printer)
{
  int rc = 0;
  int count;
  char buffer[1024];
  static int buflen = sizeof buffer / sizeof buffer[0];
  DWORD written;

  do {
    count = fread (buffer, sizeof buffer[0], buflen, file);
    if (count >= 1) {
      count *= sizeof buffer[0];
      if (WritePrinter (printer, buffer, count, &written)) {
	/* good so far */
      } else {
	fprintf (stderr, "%s:%d: WritePrinter failed (%lu)", __FILE__,
		 __LINE__, (unsigned long) GetLastError ());
	rc = 12;
      }
    }
  } while (rc == 0 && count >= 1);
  return rc;
}

static int
S_Run (char pathname[])
{
  int rc = 0;
  HANDLE printer;

  printer = S_StartPrinter (S_printername);
  if (printer != INVALID_HANDLE_VALUE) {
    if (S_SetPostScript (printer) == 0) {
      if (S_Loop (S_strm, printer) == 0) {
	/* good so far */
      } else {
	fprintf (stderr, "\n%s:%d: S_Loop failed", __FILE__, __LINE__);
	rc = 11;
      }
    } else {
      fprintf (stderr, "\n%s:%d: S_SetPostScript failed", __FILE__,
	       __LINE__);
      rc = 111;
    }
    S_StopPrinter (printer);
  } else {
    fprintf (stderr, "\n%s:%d: fopen: %s", __FILE__, __LINE__,
	     strerror (errno));
    rc = 11;
  }
  return rc;
}

static int
S_EnumPrintProcessorDatatypes (char processor[], char server[], FILE *fp)
{
  int rc = 0;
  static DWORD level = 1;
  DATATYPES_INFO_1 *di1 = NULL;
  DWORD sz = 0, count = 0, i;

  EnumPrintProcessorDatatypes (server, processor, level, NULL, 0, &sz, &count);
  if (sz > 0) {
    di1 = malloc (sz);
    if (di1 != NULL) {
      if (EnumPrintProcessorDatatypes (server, processor, level, (LPBYTE) di1,
				       sz, &sz, &count)) {
	fprintf (fp, "\n                    (Datatypes");
	for (i = 0; i < count; ++i) {
	  fprintf (fp, " \"%s\"", di1[i].pName);
	}
	fprintf (fp, ")");
      } else {
	fprintf (stderr, "\n%s:%d: EnumPrintProcessorDatatypes failed (%lu)",
		 __FILE__, __LINE__, (unsigned long) GetLastError ());
	rc = 333;
      }
      free (di1);
    } else {
      fprintf (stderr, "\n%s:%d: malloc: %s", __FILE__, __LINE__,
	       strerror (errno));
      rc = 301;
    }
  } else {
    fprintf (stderr, "\n%s:%d:", __FILE__, __LINE__);
    fprintf (stderr, " EnumPrintProcessorDatatypes to get size failed (%lu)",
	     (unsigned long) GetLastError ());
    rc = 333;
  }
  return rc;
}

static void
S_PrintPrintProcessor (char processor[], char server[], FILE *fp)
{
  fprintf (fp, "(PrintProcessor");
  fprintf (fp, "\n                    (name . \"%s\")", processor);
  fprintf (fp, "\n                    (server . \"%s\")", server);
    S_EnumPrintProcessorDatatypes (processor, server, fp);
  fprintf (fp, ")");
}

static void
S_PrintPrinterInfo2 (PRINTER_INFO_2 *pi2, FILE *fp)
{
  static char sep[] = "\n    ";         /* field separator */

  fprintf (fp, "\n(PRINTER_INFO_2");
  if (pi2->pServerName) {
    fprintf (fp, "%s(pServerName . \"%s\")", sep, pi2->pServerName);
  } else {
    fprintf (fp, "%s(pServerName . NIL)", sep);
  }
  fprintf (fp, "%s(pPrinterName . \"%s\")", sep, pi2->pPrinterName);
  fprintf (fp, "%s(pShareName . \"%s\")", sep, pi2->pShareName);
  fprintf (fp, "%s(pPortName . \"%s\")", sep, pi2->pPortName);
  fprintf (fp, "%s(pDriverName . \"%s\")", sep, pi2->pDriverName);
  fprintf (fp, "%s(pComment . \"%s\")", sep, pi2->pComment);
  fprintf (fp, "%s(pLocation . \"%s\")", sep, pi2->pLocation);
  fprintf (fp, "%s(pDevMode . <DEVMODE %p>)", sep, pi2->pDevMode);
  fprintf (fp, "%s(pSepFile . \"%s\")", sep, pi2->pSepFile);
  fprintf (fp, "%s(pPrintProcessor . ", sep);
  S_PrintPrintProcessor (pi2->pPrintProcessor, pi2->pServerName, fp);
  fprintf (fp, ")");
  fprintf (fp, "%s(pDatatype . \"%s\")", sep, pi2->pDatatype);
  fprintf (fp, "%s(pParameters . \"%s\")", sep, pi2->pParameters);
  fprintf (fp, "%s(pSecurityDescriptor . <SECURITY_DESCRIPTOR %p>)",
	   sep, pi2->pSecurityDescriptor);
  fprintf (fp, "%s(Attributes . 0x%lx)", sep, (unsigned long) pi2->Attributes);
  fprintf (fp, "%s(Priority . 0x%04X)", sep, pi2->Priority);
  fprintf (fp, "%sDefaultPriority . 0x%04X", sep, pi2->DefaultPriority);
  fprintf (fp, "%s(StartTime . 0x%04X)", sep, pi2->StartTime);
  fprintf (fp, "%s(UntilTime . 0x%04X)", sep, pi2->UntilTime);
  fprintf (fp, "%s(Status . 0x%04X)", sep, pi2->Status);
  fprintf (fp, "%s(cJobs . 0x%04X)", sep, pi2->cJobs);
  fprintf (fp, "%(sAveragePPM . 0x%04X)", sep, pi2->AveragePPM);
  fprintf (fp, ")");
}

static int
S_EnumPrinters ()
{
  int rc = 0;
  static DWORD flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
  static DWORD level = 2;
  PRINTER_INFO_2 *pi2 = NULL;
  DWORD sz = 0, count = 0, i;
  
  EnumPrinters (flags, NULL, level, NULL, 0, &sz, &count);
  if (sz > 0) {
    pi2 = malloc (sz);
    if (pi2 != NULL) {
      if (EnumPrinters (flags, NULL, level, (LPBYTE) pi2, sz, &sz, &count)) {
	for (i = 0; i < count; ++i) {
	  S_PrintPrinterInfo2 (&pi2[i], stdout);
	}
      } else {
	fprintf (stderr, "\n%s:%d: EnumPrinters failed", __FILE__, __LINE__);
	rc = 333;
      }
      free (pi2);
    } else {
      fprintf (stderr, "\n%s:%d: malloc: %s", __FILE__, __LINE__,
	       strerror (errno));
      rc = 301;
    }
  } else {
    fprintf (stderr, "\n%s:%d: EnumPrinters to get size failed", __FILE__,
	     __LINE__);
    rc = 333;
  }
  return rc;
}

static void
S_Usage (char progname[])
{
  printf ("\nprint-postscript, by Gene Michael Stover");
  printf ("\nCopyright (c) 2004 Gene Michael Stover.  All rights reserved.");
  printf ("\nLicensed according to the Gnu General Public License");
  printf (" agreement.");
  printf ("\n");
  printf ("\nUsage: %s --help", progname);
  printf ("\n       %s -?", progname);
  printf ("\n       %s -h", progname);
  printf ("\n       %s /?", progname);
  printf ("\n       %s /h", progname);
  printf ("\n       %s /help", progname);
  printf ("\n       %s [-f PATHNAME] PRINTERNAME", progname);
  printf ("\n       %s -l", progname);
  printf ("\nDocumentation is at");
  printf (" <http://lisp-p.org/psw/>");
  printf (" and");
  printf (" <http://cybertiggyr.com/gene/psw/>");
  printf ("\n");
}

static int
S_Dispatch (int action)
{
  int rc = 0;

  switch (action) {
  case ACTION_PRINT:
    if (S_Run (S_pathname) == 0) {
      /* good */
    } else {
      fprintf (stderr, "\n%s:%d: S_Run failed", __FILE__, __LINE__);
      rc = 4;
    }
    break;
  case ACTION_ENUM_PRINTERS:
    if (S_EnumPrinters () == 0) {
      /* good */
    } else {
      fprintf (stderr, "\n%s:%d: S_EnumPrinters failed", __FILE__, __LINE__);
      rc = 4;
    }
    break;
  case ACTION_USAGE:
    S_Usage (S_progname);
    break;
  default:
    fprintf (stderr, "\n%s:%d:", __FILE__, __LINE__);
    fprintf (stderr, " Don't know what to do with action %d.", action);
    rc = 3;
  }
  return rc;
}

int
main (int argc, char *argv[])
{
  int rc = 0;

  if (S_CommandLine (argc, argv) == 0) {
    if (S_Dispatch (S_action) == 0) {
      /* good */
    } else {
      fprintf (stderr, "\n%s:%d: S_Run failed", __FILE__, __LINE__);
      rc = 42;
    }
  } else {
    fprintf (stderr, "\n%s:%d: S_CommandLine failed", __FILE__, __LINE__);
    rc = 22;
  }
  return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

/* --- end of file --- */
