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