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