xref: /plan9/sys/src/cmd/gs/src/gp_msprn.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2001 artofcode LLC.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gp_msprn.c,v 1.4 2004/07/07 09:07:35 ghostgum Exp $ */
18 /* %printer% IODevice */
19 
20 #include "windows_.h"
21 #include "errno_.h"
22 #include "stdio_.h"
23 #include "string_.h"
24 #include "ctype_.h"
25 #include "fcntl_.h"
26 #include <io.h>
27 #include "gp.h"
28 #include "gscdefs.h"
29 #include "gserrors.h"
30 #include "gserror.h"
31 #include "gstypes.h"
32 #include "gsmemory.h"		/* for gxiodev.h */
33 #include "gxiodev.h"
34 
35 /* The MS-Windows printer IODevice */
36 
37 /*
38  * This allows a MS-Windows printer to be specified as an
39  * output using
40  *  -sOutputFile="%printer%HP DeskJet 500"
41  *
42  * To write to a remote printer on another server
43  *  -sOutputFile="%printer%\\server\printer name"
44  *
45  * If you don't supply a printer name you will get
46  *  Error: /undefinedfilename in --.outputpage--
47  * If the printer name is invalid you will get
48  *  Error: /invalidfileaccess in --.outputpage--
49  *
50  * This is implemented by returning the file pointer
51  * for the write end of a pipe, and starting a thread
52  * which reads the pipe and writes to a Windows printer.
53  * This will not work in Win32s.
54  *
55  * The old method provided by gp_open_printer()
56  *  -sOutputFile="\\spool\HP DeskJet 500"
57  * should not be used except on Win32s.
58  * The "\\spool\" is not a UNC name and causes confusion.
59  */
60 
61 private iodev_proc_init(mswin_printer_init);
62 private iodev_proc_fopen(mswin_printer_fopen);
63 private iodev_proc_fclose(mswin_printer_fclose);
64 const gx_io_device gs_iodev_printer = {
65     "%printer%", "FileSystem",
66     {mswin_printer_init, iodev_no_open_device,
67      NULL /*iodev_os_open_file */ , mswin_printer_fopen, mswin_printer_fclose,
68      iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,
69      iodev_no_enumerate_files, NULL, NULL,
70      iodev_no_get_params, iodev_no_put_params
71     }
72 };
73 
74 typedef struct tid_s {
75     unsigned long tid;
76 } tid_t;
77 
78 
mswin_printer_thread(void * arg)79 void mswin_printer_thread(void *arg)
80 {
81     int fd = (int)arg;
82     char pname[gp_file_name_sizeof];
83     char data[4096];
84     HANDLE hprinter = INVALID_HANDLE_VALUE;
85     int count;
86     DWORD written;
87     DOC_INFO_1 di;
88 
89     /* Read from pipe and write to Windows printer.
90      * First gp_file_name_sizeof bytes are name of the printer.
91      */
92     if (read(fd, pname, sizeof(pname)) != sizeof(pname)) {
93 	/* Didn't get the printer name */
94 	close(fd);
95 	return;
96     }
97 
98     while ( (count = read(fd, data, sizeof(data))) > 0 ) {
99 	if (hprinter == INVALID_HANDLE_VALUE) {
100 	    if (!OpenPrinter(pname, &hprinter, NULL)) {
101 		close(fd);
102 		return;
103 	    }
104 	    di.pDocName = (LPTSTR)gs_product;
105 	    di.pOutputFile = NULL;
106 	    di.pDatatype = "RAW";
107 	    if (!StartDocPrinter(hprinter, 1, (LPBYTE) & di)) {
108 		AbortPrinter(hprinter);
109 		close(fd);
110 		return;
111 	    }
112 	}
113 	if (!WritePrinter(hprinter, (LPVOID) data, count, &written)) {
114 	    AbortPrinter(hprinter);
115 	    close(fd);
116 	    return;
117 	}
118     }
119     if (hprinter != INVALID_HANDLE_VALUE) {
120 	if (count == 0) {
121 	    /* EOF */
122 	    EndDocPrinter(hprinter);
123 	    ClosePrinter(hprinter);
124 	}
125 	else {
126 	    /* Error */
127 	    AbortPrinter(hprinter);
128 	}
129     }
130     close(fd);
131 }
132 
133 /* The file device procedures */
134 private int
mswin_printer_init(gx_io_device * iodev,gs_memory_t * mem)135 mswin_printer_init(gx_io_device * iodev, gs_memory_t * mem)
136 {
137     /* state -> structure containing thread handle */
138     iodev->state = gs_alloc_bytes(mem, sizeof(tid_t), "mswin_printer_init");
139     if (iodev->state == NULL)
140         return_error(gs_error_VMerror);
141     ((tid_t *)iodev->state)->tid = -1;
142     return 0;
143 }
144 
145 
146 private int
mswin_printer_fopen(gx_io_device * iodev,const char * fname,const char * access,FILE ** pfile,char * rfname,uint rnamelen)147 mswin_printer_fopen(gx_io_device * iodev, const char *fname, const char *access,
148 	   FILE ** pfile, char *rfname, uint rnamelen)
149 {
150     DWORD version = GetVersion();
151     HANDLE hprinter;
152     int pipeh[2];
153     unsigned long tid;
154     HANDLE hthread;
155     char pname[gp_file_name_sizeof];
156     unsigned long *ptid = &((tid_t *)(iodev->state))->tid;
157 
158     /* Win32s supports neither pipes nor Win32 printers. */
159     if (((HIWORD(version) & 0x8000) != 0) &&
160 	((HIWORD(version) & 0x4000) == 0))
161 	return_error(gs_error_invalidfileaccess);
162 
163     /* Make sure that printer exists. */
164     if (!OpenPrinter((LPTSTR)fname, &hprinter, NULL))
165 	return_error(gs_error_invalidfileaccess);
166     ClosePrinter(hprinter);
167 
168     /* Create a pipe to connect a FILE pointer to a Windows printer. */
169     if (_pipe(pipeh, 4096, _O_BINARY) != 0)
170 	return_error(gs_fopen_errno_to_code(errno));
171 
172     *pfile = fdopen(pipeh[1], (char *)access);
173     if (*pfile == NULL) {
174 	close(pipeh[0]);
175 	close(pipeh[1]);
176 	return_error(gs_fopen_errno_to_code(errno));
177     }
178 
179     /* start a thread to read the pipe */
180     tid = _beginthread(&mswin_printer_thread, 32768, pipeh[0]);
181     if (tid == -1) {
182 	fclose(*pfile);
183 	close(pipeh[0]);
184 	return_error(gs_error_invalidfileaccess);
185     }
186     /* Duplicate thread handle so we can wait on it
187      * even if original handle is closed by CRTL
188      * when the thread finishes.
189      */
190     if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)tid,
191 	GetCurrentProcess(), &hthread,
192 	0, FALSE, DUPLICATE_SAME_ACCESS)) {
193 	fclose(*pfile);
194 	return_error(gs_error_invalidfileaccess);
195     }
196     *ptid = (unsigned long)hthread;
197 
198     /* Give the name of the printer to the thread by writing
199      * it to the pipe.  This is avoids elaborate thread
200      * synchronisation code.
201      */
202     strncpy(pname, fname, sizeof(pname));
203     fwrite(pname, 1, sizeof(pname), *pfile);
204 
205     return 0;
206 }
207 
208 private int
mswin_printer_fclose(gx_io_device * iodev,FILE * file)209 mswin_printer_fclose(gx_io_device * iodev, FILE * file)
210 {
211     unsigned long *ptid = &((tid_t *)(iodev->state))->tid;
212     HANDLE hthread;
213     fclose(file);
214     if (*ptid != -1) {
215 	/* Wait until the print thread finishes before continuing */
216 	hthread = (HANDLE)*ptid;
217 	WaitForSingleObject(hthread, 60000);
218 	CloseHandle(hthread);
219 	*ptid = -1;
220     }
221     return 0;
222 }
223 
224 
225