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(&regs, &regs);
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(&regs, &regs);
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(&regs, &regs);
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