Now that we have Subversion's file renaming ability, it's time at
[u/mdw/putty] / windows / winprint.c
diff --git a/windows/winprint.c b/windows/winprint.c
new file mode 100644 (file)
index 0000000..b869652
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Printing interface for PuTTY.
+ */
+
+#include "putty.h"
+#include <winspool.h>
+
+struct printer_enum_tag {
+    int nprinters;
+    DWORD enum_level;
+    union {
+       LPPRINTER_INFO_4 i4;
+       LPPRINTER_INFO_5 i5;
+    } info;
+};
+
+struct printer_job_tag {
+    HANDLE hprinter;
+};
+
+static char *printer_add_enum(int param, DWORD level, char *buffer,
+                              int offset, int *nprinters_ptr)
+{
+    DWORD needed, nprinters;
+
+    buffer = sresize(buffer, offset+512, char);
+
+    /*
+     * Exploratory call to EnumPrinters to determine how much space
+     * we'll need for the output. Discard the return value since it
+     * will almost certainly be a failure due to lack of space.
+     */
+    EnumPrinters(param, NULL, level, buffer+offset, 512,
+                &needed, &nprinters);
+
+    if (needed < 512)
+        needed = 512;
+
+    buffer = sresize(buffer, offset+needed, char);
+
+    if (EnumPrinters(param, NULL, level, buffer+offset,
+                     needed, &needed, &nprinters) == 0)
+        return NULL;
+
+    *nprinters_ptr += nprinters;
+
+    return buffer;
+}
+
+printer_enum *printer_start_enum(int *nprinters_ptr)
+{
+    printer_enum *ret = snew(printer_enum);
+    char *buffer = NULL, *retval;
+
+    *nprinters_ptr = 0;                       /* default return value */
+    buffer = snewn(512, char);
+
+    /*
+     * Determine what enumeration level to use.
+     * When enumerating printers, we need to use PRINTER_INFO_4 on
+     * NT-class systems to avoid Windows looking too hard for them and
+     * slowing things down; and we need to avoid PRINTER_INFO_5 as
+     * we've seen network printers not show up.
+     * On 9x-class systems, PRINTER_INFO_4 isn't available and
+     * PRINTER_INFO_5 is recommended.
+     * Bletch.
+     */
+    if (osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT) {
+       ret->enum_level = 5;
+    } else {
+       ret->enum_level = 4;
+    }
+
+    retval = printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
+                             ret->enum_level, buffer, 0, nprinters_ptr);
+    if (!retval)
+        goto error;
+    else
+        buffer = retval;
+
+    switch (ret->enum_level) {
+      case 4:
+       ret->info.i4 = (LPPRINTER_INFO_4)buffer;
+       break;
+      case 5:
+       ret->info.i5 = (LPPRINTER_INFO_5)buffer;
+       break;
+    }
+    ret->nprinters = *nprinters_ptr;
+    
+    return ret;
+
+    error:
+    sfree(buffer);
+    sfree(ret);
+    *nprinters_ptr = 0;
+    return NULL;
+}
+
+char *printer_get_name(printer_enum *pe, int i)
+{
+    if (!pe)
+       return NULL;
+    if (i < 0 || i >= pe->nprinters)
+       return NULL;
+    switch (pe->enum_level) {
+      case 4:
+       return pe->info.i4[i].pPrinterName;
+      case 5:
+       return pe->info.i5[i].pPrinterName;
+      default:
+       return NULL;
+    }
+}
+
+void printer_finish_enum(printer_enum *pe)
+{
+    if (!pe)
+       return;
+    switch (pe->enum_level) {
+      case 4:
+       sfree(pe->info.i4);
+       break;
+      case 5:
+       sfree(pe->info.i5);
+       break;
+    }
+    sfree(pe);
+}
+
+printer_job *printer_start_job(char *printer)
+{
+    printer_job *ret = snew(printer_job);
+    DOC_INFO_1 docinfo;
+    int jobstarted = 0, pagestarted = 0;
+
+    ret->hprinter = NULL;
+    if (!OpenPrinter(printer, &ret->hprinter, NULL))
+       goto error;
+
+    docinfo.pDocName = "PuTTY remote printer output";
+    docinfo.pOutputFile = NULL;
+    docinfo.pDatatype = "RAW";
+
+    if (!StartDocPrinter(ret->hprinter, 1, (LPSTR)&docinfo))
+       goto error;
+    jobstarted = 1;
+
+    if (!StartPagePrinter(ret->hprinter))
+       goto error;
+    pagestarted = 1;
+
+    return ret;
+
+    error:
+    if (pagestarted)
+       EndPagePrinter(ret->hprinter);
+    if (jobstarted)
+       EndDocPrinter(ret->hprinter);
+    if (ret->hprinter)
+       ClosePrinter(ret->hprinter);
+    sfree(ret);
+    return NULL;
+}
+
+void printer_job_data(printer_job *pj, void *data, int len)
+{
+    DWORD written;
+
+    if (!pj)
+       return;
+
+    WritePrinter(pj->hprinter, data, len, &written);
+}
+
+void printer_finish_job(printer_job *pj)
+{
+    if (!pj)
+       return;
+
+    EndPagePrinter(pj->hprinter);
+    EndDocPrinter(pj->hprinter);
+    ClosePrinter(pj->hprinter);
+    sfree(pj);
+}