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