xref: /plan9/sys/src/cmd/gs/src/gp_mswin.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 /* Copyright (C) 1992, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gp_mswin.c,v 1.7 2001/09/22 07:07:02 ghostgum Exp $ */
20 /*
21  * Microsoft Windows platform support for Ghostscript.
22  *
23  * Original version by Russell Lang and Maurice Castro with help from
24  * Programming Windows, 2nd Ed., Charles Petzold, Microsoft Press;
25  * initially created from gp_dosfb.c and gp_itbc.c 5th June 1992.
26  */
27 
28 /* Modified for Win32 & Microsoft C/C++ 8.0 32-Bit, 26.Okt.1994 */
29 /* by Friedrich Nowak                                           */
30 
31 /* Original EXE and GSview specific code removed */
32 /* DLL version must now be used under MS-Windows */
33 /* Russell Lang 16 March 1996 */
34 
35 #include "stdio_.h"
36 #include "string_.h"
37 #include "memory_.h"
38 #include <stdlib.h>
39 #include <stdarg.h>
40 #include "ctype_.h"
41 #include "dos_.h"
42 #include <io.h>
43 #include "malloc_.h"
44 #include <fcntl.h>
45 #include <signal.h>
46 #include "gx.h"
47 #include "gp.h"
48 #include "gpcheck.h"
49 #include "gpmisc.h"
50 #include "gserrors.h"
51 #include "gsexit.h"
52 
53 #include "windows_.h"
54 #include <shellapi.h>
55 #include <winspool.h>
56 #include "gp_mswin.h"
57 
58 /* Library routines not declared in a standard header */
59 extern char *getenv(P1(const char *));
60 
61 /* limits */
62 #define MAXSTR 255
63 
64 /* GLOBAL VARIABLE that needs to be removed */
65 char win_prntmp[MAXSTR];	/* filename of PRN temporary file */
66 
67 /* GLOBAL VARIABLES - initialised at DLL load time */
68 HINSTANCE phInstance;
69 BOOL is_win32s = FALSE;
70 
71 const LPSTR szAppName = "Ghostscript";
72 private int is_printer(const char *name);
73 
74 
75 /* ====== Generic platform procedures ====== */
76 
77 /* ------ Initialization/termination (from gp_itbc.c) ------ */
78 
79 /* Do platform-dependent initialization. */
80 void
81 gp_init(void)
82 {
83 }
84 
85 /* Do platform-dependent cleanup. */
86 void
87 gp_exit(int exit_status, int code)
88 {
89 }
90 
91 /* Exit the program. */
92 void
93 gp_do_exit(int exit_status)
94 {
95 }
96 
97 /* ------ Printer accessing ------ */
98 
99 /* Forward references */
100 private int gp_printfile(P2(const char *, const char *));
101 
102 /* Open a connection to a printer.  A null file name means use the */
103 /* standard printer connected to the machine, if any. */
104 /* Return NULL if the connection could not be opened. */
105 FILE *
106 gp_open_printer(char fname[gp_file_name_sizeof], int binary_mode)
107 {
108     if (is_printer(fname)) {
109 	FILE *pfile;
110 
111 	/* Open a scratch file, which we will send to the */
112 	/* actual printer in gp_close_printer. */
113 	pfile = gp_open_scratch_file(gp_scratch_file_name_prefix,
114 				     win_prntmp, "wb");
115 	return pfile;
116     } else
117 	return fopen(fname, (binary_mode ? "wb" : "w"));
118 }
119 
120 /* Close the connection to the printer. */
121 void
122 gp_close_printer(FILE * pfile, const char *fname)
123 {
124     fclose(pfile);
125     if (!is_printer(fname))
126 	return;			/* a file, not a printer */
127 
128     gp_printfile(win_prntmp, fname);
129     unlink(win_prntmp);
130 }
131 
132 
133 /* Dialog box to select printer port */
134 BOOL CALLBACK
135 SpoolDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
136 {
137     LPSTR entry;
138 
139     switch (message) {
140 	case WM_INITDIALOG:
141 	    entry = (LPSTR) lParam;
142 	    while (*entry) {
143 		SendDlgItemMessage(hDlg, SPOOL_PORT, LB_ADDSTRING, 0, (LPARAM) entry);
144 		entry += lstrlen(entry) + 1;
145 	    }
146 	    SendDlgItemMessage(hDlg, SPOOL_PORT, LB_SETCURSEL, 0, (LPARAM) 0);
147 	    return TRUE;
148 	case WM_COMMAND:
149 	    switch (LOWORD(wParam)) {
150 		case SPOOL_PORT:
151 		    if (HIWORD(wParam) == LBN_DBLCLK)
152 			PostMessage(hDlg, WM_COMMAND, IDOK, 0L);
153 		    return FALSE;
154 		case IDOK:
155 		    EndDialog(hDlg, 1 + (int)SendDlgItemMessage(hDlg, SPOOL_PORT, LB_GETCURSEL, 0, 0L));
156 		    return TRUE;
157 		case IDCANCEL:
158 		    EndDialog(hDlg, 0);
159 		    return TRUE;
160 	    }
161     }
162     return FALSE;
163 }
164 
165 /* return TRUE if queue looks like a valid printer name */
166 int
167 is_spool(const char *queue)
168 {
169     char *prefix = "\\\\spool";	/* 7 characters long */
170     int i;
171 
172     for (i = 0; i < 7; i++) {
173 	if (prefix[i] == '\\') {
174 	    if ((*queue != '\\') && (*queue != '/'))
175 		return FALSE;
176 	} else if (tolower(*queue) != prefix[i])
177 	    return FALSE;
178 	queue++;
179     }
180     if (*queue && (*queue != '\\') && (*queue != '/'))
181 	return FALSE;
182     return TRUE;
183 }
184 
185 
186 private int
187 is_printer(const char *name)
188 {
189     char buf[128];
190 
191     /* is printer if no name given */
192     if (strlen(name) == 0)
193 	return TRUE;
194 
195     /*  is printer if name appears in win.ini [ports] section */
196     GetProfileString("ports", name, "XYZ", buf, sizeof(buf));
197     if (strlen(name) == 0 || strcmp(buf, "XYZ"))
198 	return TRUE;
199 
200     /* is printer if name prefixed by \\spool\ */
201     if (is_spool(name))
202 	return TRUE;
203 
204     return FALSE;
205 }
206 
207 
208 /******************************************************************/
209 /* Print File to port or queue */
210 /* port==NULL means prompt for port or queue with dialog box */
211 
212 /* This is messy because Microsoft changed the spooler interface */
213 /* between Windows 3.1 and Windows 95/NT */
214 /* and didn't provide the spooler interface in Win32s */
215 
216 /* Win95, WinNT: Use OpenPrinter, WritePrinter etc. */
217 private int gp_printfile_win32(const char *filename, char *port);
218 
219 /* Win32s: Pass to Win16 spooler via gs16spl.exe */
220 private int gp_printfile_gs16spl(const char *filename, const char *port);
221 
222 /*
223  * Valid values for pmport are:
224  *   ""
225  *      action: WinNT and Win95 use default queue, Win32s prompts for port
226  *   "LPT1:" (or other port that appears in win.ini [ports]
227  *      action: start gs16spl.exe to print to the port
228  *   "\\spool\printer name"
229  *      action: send to printer using WritePrinter (WinNT and Win95).
230  *      action: translate to port name using win.ini [Devices]
231  *              then use gs16spl.exe (Win32s).
232  *   "\\spool"
233  *      action: prompt for queue name then send to printer using
234  *              WritePrinter (WinNT and Win95).
235  *      action: prompt for port then use gs16spl.exe (Win32s).
236  */
237 /* Print File */
238 private int
239 gp_printfile(const char *filename, const char *pmport)
240 {
241     /* treat WinNT and Win95 differently to Win32s */
242     if (!is_win32s) {
243 	if (strlen(pmport) == 0) {	/* get default printer */
244 	    char buf[256];
245 	    char *p;
246 
247 	    /* WinNT stores default printer in registry and win.ini */
248 	    /* Win95 stores default printer in win.ini */
249 	    GetProfileString("windows", "device", "", buf, sizeof(buf));
250 	    if ((p = strchr(buf, ',')) != NULL)
251 		*p = '\0';
252 	    return gp_printfile_win32(filename, buf);
253 	} else if (is_spool(pmport)) {
254 	    if (strlen(pmport) >= 8)
255 		return gp_printfile_win32(filename, (char *)pmport + 8);
256 	    else
257 		return gp_printfile_win32(filename, (char *)NULL);
258 	} else
259 	    return gp_printfile_gs16spl(filename, pmport);
260     } else {
261 	/* Win32s */
262 	if (is_spool(pmport)) {
263 	    if (strlen(pmport) >= 8) {
264 		/* extract port name from win.ini */
265 		char driverbuf[256];
266 		char *output;
267 
268 		GetProfileString("Devices", pmport + 8, "", driverbuf, sizeof(driverbuf));
269 		strtok(driverbuf, ",");
270 		output = strtok(NULL, ",");
271 		return gp_printfile_gs16spl(filename, output);
272 	    } else
273 		return gp_printfile_gs16spl(filename, (char *)NULL);
274 	} else
275 	    return gp_printfile_gs16spl(filename, pmport);
276     }
277 }
278 
279 #define PRINT_BUF_SIZE 16384u
280 #define PORT_BUF_SIZE 4096
281 
282 char *
283 get_queues(void)
284 {
285     int i;
286     DWORD count, needed;
287     PRINTER_INFO_1 *prinfo;
288     char *enumbuffer;
289     char *buffer;
290     char *p;
291 
292     /* enumerate all available printers */
293     EnumPrinters(PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL, NULL, 1, NULL, 0, &needed, &count);
294     if (needed == 0) {
295 	/* no printers */
296 	enumbuffer = malloc(4);
297 	if (enumbuffer == (char *)NULL)
298 	    return NULL;
299 	memset(enumbuffer, 0, 4);
300 	return enumbuffer;
301     }
302     enumbuffer = malloc(needed);
303     if (enumbuffer == (char *)NULL)
304 	return NULL;
305     if (!EnumPrinters(PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL, NULL, 1, (LPBYTE) enumbuffer, needed, &needed, &count)) {
306 	char buf[256];
307 
308 	free(enumbuffer);
309 	sprintf(buf, "EnumPrinters() failed, error code = %d", GetLastError());
310 	MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
311 	return NULL;
312     }
313     prinfo = (PRINTER_INFO_1 *) enumbuffer;
314     if ((buffer = malloc(PORT_BUF_SIZE)) == (char *)NULL) {
315 	free(enumbuffer);
316 	return NULL;
317     }
318     /* copy printer names to single buffer */
319     p = buffer;
320     for (i = 0; i < count; i++) {
321 	if (strlen(prinfo[i].pName) + 1 < (PORT_BUF_SIZE - (p - buffer))) {
322 	    strcpy(p, prinfo[i].pName);
323 	    p += strlen(p) + 1;
324 	}
325     }
326     *p = '\0';			/* double null at end */
327     free(enumbuffer);
328     return buffer;
329 }
330 
331 
332 char *
333 get_ports(void)
334 {
335     char *buffer;
336 
337     if (!is_win32s)
338 	return get_queues();
339 
340     if ((buffer = malloc(PORT_BUF_SIZE)) == (char *)NULL)
341 	return NULL;
342     GetProfileString("ports", NULL, "", buffer, PORT_BUF_SIZE);
343     return buffer;
344 }
345 
346 /* return TRUE if queuename available */
347 /* return FALSE if cancelled or error */
348 /* if queue non-NULL, use as suggested queue */
349 BOOL
350 get_queuename(char *portname, const char *queue)
351 {
352     char *buffer;
353     char *p;
354     int i, iport;
355 
356     buffer = get_queues();
357     if (buffer == NULL)
358 	return FALSE;
359     if ((queue == (char *)NULL) || (strlen(queue) == 0)) {
360 	/* select a queue */
361 	iport = DialogBoxParam(phInstance, "QueueDlgBox", (HWND) NULL, SpoolDlgProc, (LPARAM) buffer);
362 	if (!iport) {
363 	    free(buffer);
364 	    return FALSE;
365 	}
366 	p = buffer;
367 	for (i = 1; i < iport && strlen(p) != 0; i++)
368 	    p += lstrlen(p) + 1;
369 	/* prepend \\spool\ which is used by Ghostscript to distinguish */
370 	/* real files from queues */
371 	strcpy(portname, "\\\\spool\\");
372 	strcat(portname, p);
373     } else {
374 	strcpy(portname, "\\\\spool\\");
375 	strcat(portname, queue);
376     }
377 
378     free(buffer);
379     return TRUE;
380 }
381 
382 /* return TRUE if portname available */
383 /* return FALSE if cancelled or error */
384 /* if port non-NULL, use as suggested port */
385 BOOL
386 get_portname(char *portname, const char *port)
387 {
388     char *buffer;
389     char *p;
390     int i, iport;
391     char filename[MAXSTR];
392 
393     buffer = get_ports();
394     if (buffer == NULL)
395 	return FALSE;
396     if ((port == (char *)NULL) || (strlen(port) == 0)) {
397 	if (buffer == (char *)NULL)
398 	    return FALSE;
399 	/* select a port */
400 	iport = DialogBoxParam(phInstance, "SpoolDlgBox", (HWND) NULL, SpoolDlgProc, (LPARAM) buffer);
401 	if (!iport) {
402 	    free(buffer);
403 	    return FALSE;
404 	}
405 	p = buffer;
406 	for (i = 1; i < iport && strlen(p) != 0; i++)
407 	    p += lstrlen(p) + 1;
408 	strcpy(portname, p);
409     } else
410 	strcpy(portname, port);
411 
412     if (strlen(portname) == 0)
413 	return FALSE;
414     if (strcmp(portname, "FILE:") == 0) {
415 	OPENFILENAME ofn;
416 
417 	filename[0] = '\0';
418 	memset(&ofn, 0, sizeof(OPENFILENAME));
419 	ofn.lStructSize = sizeof(OPENFILENAME);
420 	ofn.hwndOwner = (HWND) NULL;
421 	ofn.lpstrFile = filename;
422 	ofn.nMaxFile = sizeof(filename);
423 	ofn.Flags = OFN_PATHMUSTEXIST;
424 	if (!GetSaveFileName(&ofn)) {
425 	    free(buffer);
426 	    return FALSE;
427 	}
428 	strcpy(portname, filename);
429     }
430     free(buffer);
431     return TRUE;
432 }
433 
434 
435 /* True Win32 method, using OpenPrinter, WritePrinter etc. */
436 private int
437 gp_printfile_win32(const char *filename, char *port)
438 {
439     DWORD count;
440     char *buffer;
441     char portname[MAXSTR];
442     FILE *f;
443     HANDLE printer;
444     DOC_INFO_1 di;
445     DWORD written;
446 
447     if (!get_queuename(portname, port))
448 	return FALSE;
449     port = portname + 8;	/* skip over \\spool\ */
450 
451     if ((buffer = malloc(PRINT_BUF_SIZE)) == (char *)NULL)
452 	return FALSE;
453 
454     if ((f = fopen(filename, "rb")) == (FILE *) NULL) {
455 	free(buffer);
456 	return FALSE;
457     }
458     /* open a printer */
459     if (!OpenPrinter(port, &printer, NULL)) {
460 	char buf[256];
461 
462 	sprintf(buf, "OpenPrinter() failed for \042%s\042, error code = %d", port, GetLastError());
463 	MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
464 	free(buffer);
465 	return FALSE;
466     }
467     /* from here until ClosePrinter, should AbortPrinter on error */
468 
469     di.pDocName = szAppName;
470     di.pOutputFile = NULL;
471     di.pDatatype = "RAW";	/* for available types see EnumPrintProcessorDatatypes */
472     if (!StartDocPrinter(printer, 1, (LPBYTE) & di)) {
473 	char buf[256];
474 
475 	sprintf(buf, "StartDocPrinter() failed, error code = %d", GetLastError());
476 	MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
477 	AbortPrinter(printer);
478 	free(buffer);
479 	return FALSE;
480     }
481     /* copy file to printer */
482     while ((count = fread(buffer, 1, PRINT_BUF_SIZE, f)) != 0) {
483 	if (!WritePrinter(printer, (LPVOID) buffer, count, &written)) {
484 	    free(buffer);
485 	    fclose(f);
486 	    AbortPrinter(printer);
487 	    return FALSE;
488 	}
489     }
490     fclose(f);
491     free(buffer);
492 
493     if (!EndDocPrinter(printer)) {
494 	char buf[256];
495 
496 	sprintf(buf, "EndDocPrinter() failed, error code = %d", GetLastError());
497 	MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
498 	AbortPrinter(printer);
499 	return FALSE;
500     }
501     if (!ClosePrinter(printer)) {
502 	char buf[256];
503 
504 	sprintf(buf, "ClosePrinter() failed, error code = %d", GetLastError());
505 	MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
506 	return FALSE;
507     }
508     return TRUE;
509 }
510 
511 
512 /* Start a 16-bit application gs16spl.exe to print a file */
513 /* Intended for Win32s where 16-bit spooler functions are not available */
514 /* and Win32 spooler functions are not implemented. */
515 int
516 gp_printfile_gs16spl(const char *filename, const char *port)
517 {
518 /* Get printer port list from win.ini */
519     char portname[MAXSTR];
520     HINSTANCE hinst;
521     char command[MAXSTR];
522     char *p;
523     HWND hwndspl;
524 
525     if (!get_portname(portname, port))
526 	return FALSE;
527 
528     /* get path to EXE - same as DLL */
529     GetModuleFileName(phInstance, command, sizeof(command));
530     if ((p = strrchr(command, '\\')) != (char *)NULL)
531 	p++;
532     else
533 	p = command;
534     *p = '\0';
535     sprintf(command + strlen(command), "gs16spl.exe %s %s",
536 	    portname, filename);
537 
538     hinst = (HINSTANCE) WinExec(command, SW_SHOWNORMAL);
539     if (hinst < (HINSTANCE) HINSTANCE_ERROR) {
540 	char buf[MAXSTR];
541 
542 	sprintf(buf, "Can't run: %s", command);
543 	MessageBox((HWND) NULL, buf, szAppName, MB_OK | MB_ICONSTOP);
544 	return FALSE;
545     }
546     hwndspl = FindWindow(NULL, "GS Win32s/Win16 spooler");
547 
548     while (IsWindow(hwndspl)) {
549 	gp_check_interrupts();
550     }
551 
552     return 0;
553 }
554 
555 
556 
557 
558 /* ------ File naming and accessing ------ */
559 
560 /* Create and open a scratch file with a given name prefix. */
561 /* Write the actual file name at fname. */
562 FILE *
563 gp_open_scratch_file(const char *prefix, char *fname, const char *mode)
564 {	/* The -7 is for XXXXXX plus a possible final \. */
565     int prefix_length = strlen(prefix);
566     int len = gp_file_name_sizeof - prefix_length - 7;
567 
568     if (gp_file_name_is_absolute(prefix, prefix_length) ||
569 	gp_gettmpdir(fname, &len) != 0
570 	)
571 	*fname = 0;
572     else {
573 	char *temp;
574 
575 	/* Prevent X's in path from being converted by mktemp. */
576 	for (temp = fname; *temp; temp++)
577 	    *temp = tolower(*temp);
578 	if (strlen(fname) && (fname[strlen(fname) - 1] != '\\'))
579 	    strcat(fname, "\\");
580     }
581     if (strlen(fname) + prefix_length + 7 >= gp_file_name_sizeof)
582 	return 0;		/* file name too long */
583     strcat(fname, prefix);
584     strcat(fname, "XXXXXX");
585     mktemp(fname);
586     return gp_fopentemp(fname, mode);
587 }
588 
589 /* Open a file with the given name, as a stream of uninterpreted bytes. */
590 FILE *
591 gp_fopen(const char *fname, const char *mode)
592 {
593     return fopen(fname, mode);
594 }
595 
596