1 /* Copyright (C) 2001-2008 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: gp_os2.c 9052 2008-08-30 13:31:29Z ghostgum $ */
15 /* Common platform-specific routines for OS/2 and MS-DOS */
16 /* compiled with GCC/EMX */
17 
18 #define INCL_DOS
19 #define INCL_SPL
20 #define INCL_SPLDOSPRINT
21 #define INCL_SPLERRORS
22 #define INCL_BASE
23 #define INCL_ERRORS
24 #define INCL_WIN
25 #include <os2.h>
26 
27 #include "pipe_.h"
28 #include "stdio_.h"
29 #include "string_.h"
30 #include <fcntl.h>
31 
32 #ifdef __IBMC__
33 #define popen fopen		/* doesn't support popen */
34 #define pclose fclose		/* doesn't support pclose */
35 #else
36 #include <dos.h>
37 #endif
38 /* Define the regs union tag for short registers. */
39 #  define rshort x
40 #define intdos(a,b) _int86(0x21, a, b)
41 
42 #include "memory_.h"
43 #include "string_.h"
44 #include "gx.h"
45 #include "gsexit.h"
46 #include "gsmemory.h"
47 #include "gsstruct.h"
48 #include "gp.h"
49 #include "gpmisc.h"
50 #include "gsutil.h"
51 #include "stdlib.h"		/* need _osmode, exit */
52 #include "time_.h"
53 #include <time.h>		/* should this be in time_.h? */
54 #include "gp_os2.h"
55 #ifdef __EMX__
56 #include <sys/emxload.h>
57 #endif
58 
59 #if defined(__DLL__) && defined( __EMX__)
60 /* This isn't provided in any of the libraries */
61 /* We set this to the process environment in gp_init */
62 char *fake_environ[3] =
63 {"", NULL, NULL};
64 char **environ = fake_environ;
65 char **_environ = fake_environ;
66 HWND hwndtext = (HWND) NULL;
67 
68 #endif
69 
70 #ifdef __DLL__
71 /* use longjmp instead of exit when using DLL */
72 #include <setjmp.h>
73 extern jmp_buf gsdll_env;
74 
75 #endif
76 
77 #ifdef __DLL__
78 #define isos2 TRUE
79 #else
80 #define isos2 (_osmode == OS2_MODE)
81 #endif
82 char pm_prntmp[256];		/* filename of printer spool temporary file */
83 
84 
85 /* ------ Miscellaneous ------ */
86 
87 /* Get the string corresponding to an OS error number. */
88 /* All reasonable compilers support it. */
89 const char *
gp_strerror(int errnum)90 gp_strerror(int errnum)
91 {
92     return strerror(errnum);
93 }
94 
95 /* use Unix version for date and time */
96 /* ------ Date and time ------ */
97 
98 /* Read the current time (in seconds since Jan. 1, 1970) */
99 /* and fraction (in nanoseconds since midnight). */
100 void
gp_get_realtime(long * pdt)101 gp_get_realtime(long *pdt)
102 {
103     struct timeval tp;
104     struct timezone tzp;
105 
106     if (gettimeofday(&tp, &tzp) == -1) {
107 	lprintf("Ghostscript: gettimeofday failed!\n");
108 	tp.tv_sec = tp.tv_usec = 0;
109     }
110     /* tp.tv_sec is #secs since Jan 1, 1970 */
111     pdt[0] = tp.tv_sec;
112     pdt[1] = tp.tv_usec * 1000;
113 
114 #ifdef DEBUG_CLOCK
115     printf("tp.tv_sec = %d  tp.tv_usec = %d  pdt[0] = %ld  pdt[1] = %ld\n",
116 	   tp.tv_sec, tp.tv_usec, pdt[0], pdt[1]);
117 #endif
118 }
119 
120 /* Read the current user CPU time (in seconds) */
121 /* and fraction (in nanoseconds).  */
122 void
gp_get_usertime(long * pdt)123 gp_get_usertime(long *pdt)
124 {
125     gp_get_realtime(pdt);	/* Use an approximation for now.  */
126 }
127 
128 
129 /* ------ Console management ------ */
130 
131 /* Answer whether a given file is the console (input or output). */
132 /* This is not a standard gp procedure, */
133 /* but the MS Windows configuration needs it, */
134 /* and other MS-DOS configurations might need it someday. */
135 /* Don't know if it is needed for OS/2. */
136 bool
gp_file_is_console(FILE * f)137 gp_file_is_console(FILE * f)
138 {
139 #ifndef __DLL__
140     if (!isos2) {
141 	union REGS regs;
142 
143 	if (f == NULL)
144 	    return false;
145 	regs.h.ah = 0x44;	/* ioctl */
146 	regs.h.al = 0;		/* get device info */
147 	regs.rshort.bx = fileno(f);
148 	intdos(&regs, &regs);
149 	return ((regs.h.dl & 0x80) != 0 && (regs.h.dl & 3) != 0);
150     }
151 #endif
152     if (fileno(f) <= 2)
153 	return true;
154     return false;
155 }
156 
157 /* ------ Persistent data cache ------*/
158 
159 /* insert a buffer under a (type, key) pair */
gp_cache_insert(int type,byte * key,int keylen,void * buffer,int buflen)160 int gp_cache_insert(int type, byte *key, int keylen, void *buffer, int buflen)
161 {
162     /* not yet implemented */
163     return 0;
164 }
165 
166 /* look up a (type, key) in the cache */
gp_cache_query(int type,byte * key,int keylen,void ** buffer,gp_cache_alloc alloc,void * userdata)167 int gp_cache_query(int type, byte* key, int keylen, void **buffer,
168     gp_cache_alloc alloc, void *userdata)
169 {
170     /* not yet implemented */
171     return -1;
172 }
173 
174 
175 /*************************************************************/
176 /* from gp_iwatc.c and gp_itbc.c */
177 
178 /* Intel processor, EMX/GCC specific routines for Ghostscript */
179 #include <signal.h>
180 #include "stat_.h"
181 #include "string_.h"
182 
183 /* Library routines not declared in a standard header */
184 /* extern char *getenv(const char *); */
185 
186 /* Forward declarations */
187 static void handle_FPE(int);
188 
189 /* Do platform-dependent initialization. */
190 void
gp_init(void)191 gp_init(void)
192 {
193 #if defined(__DLL__) && defined(__EMX__)
194     PTIB pptib;
195     PPIB pppib;
196     int i;
197     char *p;
198 
199     /* get environment of EXE */
200     DosGetInfoBlocks(&pptib, &pppib);
201     for (i = 0, p = pppib->pib_pchenv; *p; p += strlen(p) + 1)
202 	i++;
203     _environ = environ = (char **)malloc((i + 2) * sizeof(char *));
204 
205     for (i = 0, p = pppib->pib_pchenv; *p; p += strlen(p) + 1) {
206 	environ[i] = p;
207 	i++;
208     }
209     environ[i] = p;
210     i++;
211     environ[i] = NULL;
212 #endif
213 
214     /* keep gsos2.exe in memory for number of minutes specified in */
215     /* environment variable GS_LOAD */
216 #ifdef __EMX__
217     _emxload_env("GS_LOAD");
218 #endif
219 
220     /* Set up the handler for numeric exceptions. */
221     signal(SIGFPE, handle_FPE);
222 }
223 
224 
225 /* Trap numeric exceptions.  Someday we will do something */
226 /* more appropriate with these. */
227 static void
handle_FPE(int sig)228 handle_FPE(int sig)
229 {
230     eprintf("Numeric exception:\n");
231     exit(1);
232 }
233 
234 /* Do platform-dependent cleanup. */
235 void
gp_exit(int exit_status,int code)236 gp_exit(int exit_status, int code)
237 {
238 #if defined(__DLL__) && defined(__EMX__)
239     if (environ != fake_environ) {
240 	free(environ);
241 	environ = _environ = fake_environ;
242     }
243 #endif
244 }
245 
246 /* Exit the program. */
247 void
gp_do_exit(int exit_status)248 gp_do_exit(int exit_status)
249 {
250     exit(exit_status);
251 }
252 
253 /* ------ Printer accessing ------ */
254 static int is_os2_spool(const char *queue);
255 
256 /* Put a printer file (which might be stdout) into binary or text mode. */
257 /* This is not a standard gp procedure, */
258 /* but all MS-DOS configurations need it. */
259 void
gp_set_file_binary(int prnfno,int binary)260 gp_set_file_binary(int prnfno, int binary)
261 {
262 #ifndef __IBMC__
263     union REGS regs;
264 
265     regs.h.ah = 0x44;		/* ioctl */
266     regs.h.al = 0;		/* get device info */
267     regs.rshort.bx = prnfno;
268     intdos(&regs, &regs);
269     if (((regs.rshort.flags) & 1) != 0 || !(regs.h.dl & 0x80))
270 	return;			/* error, or not a device */
271     if (binary)
272 	regs.h.dl |= 0x20;	/* binary (no ^Z intervention) */
273     else
274 	regs.h.dl &= ~0x20;	/* text */
275     regs.h.dh = 0;
276     regs.h.ah = 0x44;		/* ioctl */
277     regs.h.al = 1;		/* set device info */
278     intdos(&regs, &regs);
279 #endif
280 }
281 
282 /* Open a connection to a printer.  A null file name means use the */
283 /* standard printer connected to the machine, if any. */
284 /* Return NULL if the connection could not be opened. */
285 /* filename can be one of the following values
286  *   ""                Spool in default queue
287  *   "\\spool\queue"   Spool in "queue"
288  *   "|command"        open an output pipe using popen()
289  *   "filename"        open filename using fopen()
290  *   "port"            open port using fopen()
291  */
292 FILE *
gp_open_printer(char fname[gp_file_name_sizeof],int binary_mode)293 gp_open_printer(char fname[gp_file_name_sizeof], int binary_mode)
294 {
295     FILE *pfile;
296 
297     if ((strlen(fname) == 0) || is_os2_spool(fname)) {
298 	if (isos2) {
299 	    /* default or spool */
300 	    if (pm_spool(NULL, fname))	/* check if spool queue valid */
301 		return NULL;
302 	    pfile = gp_open_scratch_file(gp_scratch_file_name_prefix,
303 				     pm_prntmp, (binary_mode ? "wb" : "w"));
304 	} else
305 	    pfile = fopen("PRN", (binary_mode ? "wb" : "w"));
306     } else if ((isos2) && (fname[0] == '|'))
307 	/* pipe */
308 	pfile = popen(fname + 1, (binary_mode ? "wb" : "w"));
309     else
310 	/* normal file or port */
311 	pfile = fopen(fname, (binary_mode ? "wb" : "w"));
312 
313     if (pfile == (FILE *) NULL)
314 	return (FILE *) NULL;
315     if (!isos2)
316 	gp_set_file_binary(fileno(pfile), binary_mode);
317     return pfile;
318 }
319 
320 /* Close the connection to the printer. */
321 void
gp_close_printer(FILE * pfile,const char * fname)322 gp_close_printer(FILE * pfile, const char *fname)
323 {
324     if (isos2 && (fname[0] == '|'))
325 	pclose(pfile);
326     else
327 	fclose(pfile);
328 
329     if ((strlen(fname) == 0) || is_os2_spool(fname)) {
330 	/* spool temporary file */
331 	pm_spool(pm_prntmp, fname);
332 	unlink(pm_prntmp);
333     }
334 }
335 
336 /* ------ File accessing -------- */
337 
338 /* Set a file into binary or text mode. */
339 int
gp_setmode_binary(FILE * pfile,bool binary)340 gp_setmode_binary(FILE * pfile, bool binary)
341 {
342     gp_set_file_binary(fileno(pfile), binary);
343     return 0;
344 }
345 
346 /* ------ Printer Spooling ------ */
347 #ifndef NERR_BufTooSmall
348 #define NERR_BufTooSmall 2123	/* For SplEnumQueue */
349 #endif
350 
351 /* If queue_name is NULL, list available queues */
352 /* If strlen(queue_name)==0, return default queue and driver name */
353 /* If queue_name supplied, return driver_name */
354 /* returns 0 if OK, non-zero for error */
355 int
pm_find_queue(char * queue_name,char * driver_name)356 pm_find_queue(char *queue_name, char *driver_name)
357 {
358     SPLERR splerr;
359     USHORT jobCount;
360     ULONG cbBuf;
361     ULONG cTotal;
362     ULONG cReturned;
363     ULONG cbNeeded;
364     ULONG ulLevel;
365     ULONG i;
366     PSZ pszComputerName;
367     PBYTE pBuf;
368     PPRQINFO3 prq;
369 
370     ulLevel = 3L;
371     pszComputerName = (PSZ) NULL;
372     splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L,	/* cbBuf */
373 			  &cReturned, &cTotal,
374 			  &cbNeeded, NULL);
375     if (splerr == ERROR_MORE_DATA || splerr == NERR_BufTooSmall) {
376 	if (!DosAllocMem((PVOID) & pBuf, cbNeeded,
377 			 PAG_READ | PAG_WRITE | PAG_COMMIT)) {
378 	    cbBuf = cbNeeded;
379 	    splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
380 				  &cReturned, &cTotal,
381 				  &cbNeeded, NULL);
382 	    if (splerr == NO_ERROR) {
383 		/* Set pointer to point to the beginning of the buffer.           */
384 		prq = (PPRQINFO3) pBuf;
385 
386 		/* cReturned has the count of the number of PRQINFO3 structures.  */
387 		for (i = 0; i < cReturned; i++) {
388 		    if (queue_name) {
389 			/* find queue name and return driver name */
390 			if (strlen(queue_name) == 0) {	/* use default queue */
391 			    if (prq->fsType & PRQ3_TYPE_APPDEFAULT)
392 				strcpy(queue_name, prq->pszName);
393 			}
394 			if (strcmp(prq->pszName, queue_name) == 0) {
395 			    char *p;
396 
397 			    for (p = prq->pszDriverName; *p && (*p != '.'); p++)
398 				/* do nothing */ ;
399 			    *p = '\0';	/* truncate at '.' */
400 			    if (driver_name != NULL)
401 				strcpy(driver_name, prq->pszDriverName);
402 			    DosFreeMem((PVOID) pBuf);
403 			    return 0;
404 			}
405 		    } else {
406 			/* list queue details */
407 			if (prq->fsType & PRQ3_TYPE_APPDEFAULT)
408 			    eprintf1("  \042%s\042  (DEFAULT)\n", prq->pszName);
409 			else
410 			    eprintf1("  \042%s\042\n", prq->pszName);
411 		    }
412 		    prq++;
413 		}		/*endfor cReturned */
414 	    }
415 	    DosFreeMem((PVOID) pBuf);
416 	}
417     }
418     /* end if Q level given */
419     else {
420 	/* If we are here we had a bad error code. Print it and some other info. */
421 	eprintf4("SplEnumQueue Error=%ld, Total=%ld, Returned=%ld, Needed=%ld\n",
422 		splerr, cTotal, cReturned, cbNeeded);
423     }
424     if (splerr)
425 	return splerr;
426     if (queue_name)
427 	return -1;
428     return 0;
429 }
430 
431 
432 /* return TRUE if queue looks like a valid OS/2 queue name */
433 static int
is_os2_spool(const char * queue)434 is_os2_spool(const char *queue)
435 {
436     char *prefix = "\\\\spool\\";	/* 8 characters long */
437     int i;
438 
439     for (i = 0; i < 8; i++) {
440 	if (prefix[i] == '\\') {
441 	    if ((*queue != '\\') && (*queue != '/'))
442 		return FALSE;
443 	} else if (tolower(*queue) != prefix[i])
444 	    return FALSE;
445 	queue++;
446     }
447     return TRUE;
448 }
449 
450 #define PRINT_BUF_SIZE 16384
451 
452 /* Spool file to queue */
453 /* return 0 if successful, non-zero if error */
454 /* if filename is NULL, return 0 if spool queue is valid, non-zero if error */
455 int
pm_spool(char * filename,const char * queue)456 pm_spool(char *filename, const char *queue)
457 {
458     HSPL hspl;
459     PDEVOPENSTRUC pdata;
460     PSZ pszToken = "*";
461     ULONG jobid;
462     BOOL rc;
463     char queue_name[256];
464     char driver_name[256];
465     char *buffer;
466     FILE *f;
467     int count;
468 
469     if (strlen(queue) != 0) {
470 	/* queue specified */
471 	strcpy(queue_name, queue + 8);	/* skip over \\spool\ */
472     } else {
473 	/* get default queue */
474 	queue_name[0] = '\0';
475     }
476     if (pm_find_queue(queue_name, driver_name)) {
477 	/* error, list valid queue names */
478 	eprintf("Invalid queue name.  Use one of:\n");
479 	pm_find_queue(NULL, NULL);
480 	return 1;
481     }
482     if (!filename)
483 	return 0;		/* we were only asked to check the queue */
484 
485 
486     if ((buffer = malloc(PRINT_BUF_SIZE)) == (char *)NULL) {
487 	eprintf("Out of memory in pm_spool\n");
488 	return 1;
489     }
490     if ((f = fopen(filename, "rb")) == (FILE *) NULL) {
491 	free(buffer);
492 	eprintf1("Can't open temporary file %s\n", filename);
493 	return 1;
494     }
495     /* Allocate memory for pdata */
496     if (!DosAllocMem((PVOID) & pdata, sizeof(DEVOPENSTRUC),
497 		     (PAG_READ | PAG_WRITE | PAG_COMMIT))) {
498 	/* Initialize elements of pdata */
499 	pdata->pszLogAddress = queue_name;
500 	pdata->pszDriverName = driver_name;
501 	pdata->pdriv = NULL;
502 	pdata->pszDataType = "PM_Q_RAW";
503 	pdata->pszComment = "Ghostscript";
504 	pdata->pszQueueProcName = NULL;
505 	pdata->pszQueueProcParams = NULL;
506 	pdata->pszSpoolerParams = NULL;
507 	pdata->pszNetworkParams = NULL;
508 
509 	hspl = SplQmOpen(pszToken, 4L, (PQMOPENDATA) pdata);
510 	if (hspl == SPL_ERROR) {
511 	    eprintf("SplQmOpen failed.\n");
512 	    DosFreeMem((PVOID) pdata);
513 	    free(buffer);
514 	    fclose(f);
515 	    return 1;		/* failed */
516 	}
517 	rc = SplQmStartDoc(hspl, "Ghostscript");
518 	if (!rc) {
519 	    eprintf("SplQmStartDoc failed.\n");
520 	    DosFreeMem((PVOID) pdata);
521 	    free(buffer);
522 	    fclose(f);
523 	    return 1;
524 	}
525 	/* loop, copying file to queue */
526 	while (rc && (count = fread(buffer, 1, PRINT_BUF_SIZE, f)) != 0) {
527 	    rc = SplQmWrite(hspl, count, buffer);
528 	    if (!rc)
529 		eprintf("SplQmWrite failed.\n");
530 	}
531 	free(buffer);
532 	fclose(f);
533 
534 	if (!rc) {
535 	    eprintf("Aborting Spooling.\n");
536 	    SplQmAbort(hspl);
537 	} else {
538 	    SplQmEndDoc(hspl);
539 	    rc = SplQmClose(hspl);
540 	    if (!rc)
541 		eprintf("SplQmClose failed.\n");
542 	}
543     } else
544 	rc = 0;			/* no memory */
545     return !rc;
546 }
547 
548 
549 
550 /* ------ Font enumeration ------ */
551 
552  /* This is used to query the native os for a list of font names and
553   * corresponding paths. The general idea is to save the hassle of
554   * building a custom fontmap file.
555   */
556 
gp_enumerate_fonts_init(gs_memory_t * mem)557 void *gp_enumerate_fonts_init(gs_memory_t *mem)
558 {
559     return NULL;
560 }
561 
gp_enumerate_fonts_next(void * enum_state,char ** fontname,char ** path)562 int gp_enumerate_fonts_next(void *enum_state, char **fontname, char **path)
563 {
564     return 0;
565 }
566 
gp_enumerate_fonts_free(void * enum_state)567 void gp_enumerate_fonts_free(void *enum_state)
568 {
569 }
570 
571 /* --------- 64 bit file access ----------- */
572 /* fixme: Not implemented yet.
573  * Currently we stub it with 32 bits access.
574  */
575 
gp_fopen_64(const char * filename,const char * mode)576 FILE *gp_fopen_64(const char *filename, const char *mode)
577 {
578     return fopen(filename, mode);
579 }
580 
gp_open_scratch_file_64(const char * prefix,char fname[gp_file_name_sizeof],const char * mode)581 FILE *gp_open_scratch_file_64(const char *prefix,
582 			   char fname[gp_file_name_sizeof],
583 			   const char *mode)
584 {
585     return gp_open_scratch_file(prefix, fname, mode);
586 }
587 
gp_open_printer_64(char fname[gp_file_name_sizeof],int binary_mode)588 FILE *gp_open_printer_64(char fname[gp_file_name_sizeof], int binary_mode)
589 {
590     return gp_open_printer(fname, binary_mode);
591 }
592 
gp_ftell_64(FILE * strm)593 int64_t gp_ftell_64(FILE *strm)
594 {
595     return ftell(strm);
596 }
597 
gp_fseek_64(FILE * strm,int64_t offset,int origin)598 int gp_fseek_64(FILE *strm, int64_t offset, int origin)
599 {
600     long offset1 = (long)offset;
601 
602     if (offset != offset1)
603 	return -1;
604     return fseek(strm, offset1, origin);
605 }
606