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