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(®s, ®s);
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(®s, ®s);
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(®s, ®s);
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