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