summaryrefslogtreecommitdiff
path: root/base/gp_os2.c
blob: 862522cc65bb258377508a3ef0a37f98ec4cbb08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
/* Copyright (C) 2001-2023 Artifex Software, Inc.
   All Rights Reserved.

   This software is provided AS-IS with no warranty, either express or
   implied.

   This software is distributed under license and may not be copied,
   modified or distributed except as expressly authorized under the terms
   of the license contained in the file LICENSE in this distribution.

   Refer to licensing information at http://www.artifex.com or contact
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
   CA 94129, USA, for further information.
*/


/* Common platform-specific routines for OS/2 and MS-DOS */
/* compiled with GCC/EMX */

/* prevent gp.h from defining fopen */
#define fopen fopen

#define INCL_DOS
#define INCL_SPL
#define INCL_SPLDOSPRINT
#define INCL_SPLERRORS
#define INCL_BASE
#define INCL_ERRORS
#define INCL_WIN
#include <os2.h>

#include "pipe_.h"
#include "stdio_.h"
#include "string_.h"
#include <fcntl.h>

#ifdef __IBMC__
#define popen fopen		/* doesn't support popen */
#define pclose fclose		/* doesn't support pclose */
#else
#include <dos.h>
#endif
/* Define the regs union tag for short registers. */
#  define rshort x
#define intdos(a,b) _int86(0x21, a, b)

#include "memory_.h"
#include "string_.h"
#include "gx.h"
#include "gsexit.h"
#include "gsmemory.h"
#include "gsstruct.h"

#include "gp.h"
#include "gpmisc.h"
#include "gsutil.h"
#include "stdlib.h"		/* need _osmode, exit */
#include "time_.h"
#include <time.h>		/* should this be in time_.h? */
#include "gp_os2.h"
#ifdef __EMX__
#include <sys/emxload.h>
#endif

#if defined(__DLL__) && defined( __EMX__)
/* This isn't provided in any of the libraries */
/* We set this to the process environment in gp_init */
char *fake_environ[3] =
{"", NULL, NULL};
char **environ = fake_environ;
char **_environ = fake_environ;
HWND hwndtext = (HWND) NULL;

#endif

#ifdef __DLL__
/* use longjmp instead of exit when using DLL */
#include <setjmp.h>
extern jmp_buf gsdll_env;

#endif

#ifdef __DLL__
#define isos2 TRUE
#else
#define isos2 (_osmode == OS2_MODE)
#endif
char pm_prntmp[256];		/* filename of printer spool temporary file */

/* ------ Miscellaneous ------ */

/* Get the string corresponding to an OS error number. */
/* All reasonable compilers support it. */
const char *
gp_strerror(int errnum)
{
    return strerror(errnum);
}

/* We don't have a good way to get a serial number here, so just */
/* return what we always used to: GS_SERIALNUMBER. */
int
gp_serialnumber(void)
{
    return (int)(gs_serialnumber);
}

/* use Unix version for date and time */
/* ------ Date and time ------ */

/* Read the current time (in seconds since Jan. 1, 1970) */
/* and fraction (in nanoseconds since midnight). */
void
gp_get_realtime(long *pdt)
{
    struct timeval tp;
    struct timezone tzp;

    if (gettimeofday(&tp, &tzp) == -1) {
        lprintf("Ghostscript: gettimeofday failed!\n");
        tp.tv_sec = tp.tv_usec = 0;
    }
    /* tp.tv_sec is #secs since Jan 1, 1970 */
    pdt[0] = tp.tv_sec;
    pdt[1] = tp.tv_usec * 1000;

#ifdef DEBUG_CLOCK
    printf("tp.tv_sec = %d  tp.tv_usec = %d  pdt[0] = %ld  pdt[1] = %ld\n",
           tp.tv_sec, tp.tv_usec, pdt[0], pdt[1]);
#endif
}

/* Read the current user CPU time (in seconds) */
/* and fraction (in nanoseconds).  */
void
gp_get_usertime(long *pdt)
{
    gp_get_realtime(pdt);	/* Use an approximation for now.  */
}

/* ------ Console management ------ */

/* Answer whether a given file is the console (input or output). */
/* This is not a standard gp procedure, */
/* but the MS Windows configuration needs it, */
/* and other MS-DOS configurations might need it someday. */
/* Don't know if it is needed for OS/2. */
bool
gp_file_is_console(FILE * f)
{
#ifndef __DLL__
    if (!isos2) {
        union REGS regs;

        if (f == NULL)
            return false;
        regs.h.ah = 0x44;	/* ioctl */
        regs.h.al = 0;		/* get device info */
        regs.rshort.bx = fileno(f);
        intdos(&regs, &regs);
        return ((regs.h.dl & 0x80) != 0 && (regs.h.dl & 3) != 0);
    }
#endif
    if (fileno(f) <= 2)
        return true;
    return false;
}

/*************************************************************/
/* from gp_iwatc.c and gp_itbc.c */

/* Intel processor, EMX/GCC specific routines for Ghostscript */
#include <signal.h>
#include "stat_.h"
#include "string_.h"

/* Library routines not declared in a standard header */
/* extern char *getenv(const char *); */

/* Forward declarations */
static void handle_FPE(int);

/* Do platform-dependent initialization. */
void
gp_init(void)
{
#if defined(__DLL__) && defined(__EMX__)
    PTIB pptib;
    PPIB pppib;
    int i;
    char *p;

    /* get environment of EXE */
    DosGetInfoBlocks(&pptib, &pppib);
    for (i = 0, p = pppib->pib_pchenv; *p; p += strlen(p) + 1)
        i++;
    _environ = environ = (char **)malloc((i + 2) * sizeof(char *));

    for (i = 0, p = pppib->pib_pchenv; *p; p += strlen(p) + 1) {
        environ[i] = p;
        i++;
    }
    environ[i] = p;
    i++;
    environ[i] = NULL;
#endif

    /* keep gsos2.exe in memory for number of minutes specified in */
    /* environment variable GS_LOAD */
#ifdef __EMX__
    _emxload_env("GS_LOAD");
#endif

    /* Set up the handler for numeric exceptions. */
    signal(SIGFPE, handle_FPE);
}

/* Trap numeric exceptions.  Someday we will do something */
/* more appropriate with these. */
static void
handle_FPE(int sig)
{
    eprintf("Numeric exception:\n");
    exit(1);
}

/* Do platform-dependent cleanup. */
void
gp_exit(int exit_status, int code)
{
#if defined(__DLL__) && defined(__EMX__)
    if (environ != fake_environ) {
        free(environ);
        environ = _environ = fake_environ;
    }
#endif
}

/* Exit the program. */
void
gp_do_exit(int exit_status)
{
    exit(exit_status);
}

/* ------ Printer accessing ------ */
static int is_os2_spool(const char *queue);

/* Put a printer file (which might be stdout) into binary or text mode. */
/* This is not a standard gp procedure, */
/* but all MS-DOS configurations need it. */
void
gp_set_file_binary(int prnfno, int binary)
{
#ifndef __IBMC__
    union REGS regs;

    regs.h.ah = 0x44;		/* ioctl */
    regs.h.al = 0;		/* get device info */
    regs.rshort.bx = prnfno;
    intdos(&regs, &regs);
    if (((regs.rshort.flags) & 1) != 0 || !(regs.h.dl & 0x80))
        return;			/* error, or not a device */
    if (binary)
        regs.h.dl |= 0x20;	/* binary (no ^Z intervention) */
    else
        regs.h.dl &= ~0x20;	/* text */
    regs.h.dh = 0;
    regs.h.ah = 0x44;		/* ioctl */
    regs.h.al = 1;		/* set device info */
    intdos(&regs, &regs);
#endif
}

/* Open a connection to a printer.  A null file name means use the */
/* standard printer connected to the machine, if any. */
/* Return NULL if the connection could not be opened. */
/* filename can be one of the following values
 *   ""                Spool in default queue
 *   "\\spool\queue"   Spool in "queue"
 *   "|command"        open an output pipe using popen()
 *   "filename"        open filename using fopen()
 *   "port"            open port using fopen()
 */
FILE *
gp_open_printer_impl(gs_memory_t *mem,
                     const char  *fname,
                     int         *binary_mode,
                     int          (**close)(FILE *))
{
    FILE *pfile;

    if ((strlen(fname) == 0) || is_os2_spool(fname)) {
        if (isos2) {
            /* default or spool */
            if (pm_spool(mem, NULL, fname))	/* check if spool queue valid */
                return NULL;
            pfile = gp_open_scratch_file_impl(mem,
                                              gp_scratch_file_name_prefix,
                                              pm_prntmp,
                                              (binary_mode ? "wb" : "w"),
                                              0);
        } else {
            pfile = fopen("PRN", (*binary_mode ? "wb" : "w"));
        }
    } else if ((isos2) && (fname[0] == '|')) {
        /* pipe */
        pfile = popen(fname + 1, (*binary_mode ? "wb" : "w"));
        *close = isos2 ? pclose : fclose;
    else
        /* normal file or port */
        pfile = gp_fopen_impl(mem, fname, (*binary_mode ? "wb" : "w"));

    if (pfile == NULL)
        return NULL;
    if (!isos2)
        *binary_mode = 1;

    return pfile;
}

/* Close the connection to the printer. */
void
gp_close_printer(gp_file *pfile, const char *fname)
{
    gp_fclose(pfile);

    if ((strlen(fname) == 0) || is_os2_spool(fname)) {
        /* spool temporary file */
        pm_spool(mem, pm_prntmp, fname);
        unlink(pm_prntmp); /* unlink, not gp_unlink */
    }
}

/* ------ File accessing -------- */

/* Set a file into binary or text mode. */
int
gp_setmode_binary_impl(FILE * pfile, bool binary)
{
    gp_set_file_binary(fileno(pfile), binary);
    return 0;
}

/* ------ Printer Spooling ------ */
#ifndef NERR_BufTooSmall
#define NERR_BufTooSmall 2123	/* For SplEnumQueue */
#endif

/* If queue_name is NULL, list available queues */
/* If strlen(queue_name)==0, return default queue and driver name */
/* If queue_name supplied, return driver_name */
/* returns 0 if OK, non-zero for error */
int
pm_find_queue(const gs_memory_t *mem, char *queue_name, char *driver_name)
{
    SPLERR splerr;
    USHORT jobCount;
    ULONG cbBuf;
    ULONG cTotal;
    ULONG cReturned;
    ULONG cbNeeded;
    ULONG ulLevel;
    ULONG i;
    PSZ pszComputerName;
    PBYTE pBuf;
    PPRQINFO3 prq;

    ulLevel = 3L;
    pszComputerName = (PSZ) NULL;
    splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L,	/* cbBuf */
                          &cReturned, &cTotal,
                          &cbNeeded, NULL);
    if (splerr == ERROR_MORE_DATA || splerr == NERR_BufTooSmall) {
        if (!DosAllocMem((PVOID) & pBuf, cbNeeded,
                         PAG_READ | PAG_WRITE | PAG_COMMIT)) {
            cbBuf = cbNeeded;
            splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
                                  &cReturned, &cTotal,
                                  &cbNeeded, NULL);
            if (splerr == NO_ERROR) {
                /* Set pointer to point to the beginning of the buffer.           */
                prq = (PPRQINFO3) pBuf;

                /* cReturned has the count of the number of PRQINFO3 structures.  */
                for (i = 0; i < cReturned; i++) {
                    if (queue_name) {
                        /* find queue name and return driver name */
                        if (strlen(queue_name) == 0) {	/* use default queue */
                            if (prq->fsType & PRQ3_TYPE_APPDEFAULT)
                                strcpy(queue_name, prq->pszName);
                        }
                        if (strcmp(prq->pszName, queue_name) == 0) {
                            char *p;

                            for (p = prq->pszDriverName; *p && (*p != '.'); p++)
                                /* do nothing */ ;
                            *p = '\0';	/* truncate at '.' */
                            if (driver_name != NULL)
                                strcpy(driver_name, prq->pszDriverName);
                            DosFreeMem((PVOID) pBuf);
                            return 0;
                        }
                    } else {
                        /* list queue details */
                        if (prq->fsType & PRQ3_TYPE_APPDEFAULT)
                            emprintf1(mem, "  \042%s\042  (DEFAULT)\n", prq->pszName);
                        else
                            emprintf1(mem, "  \042%s\042\n", prq->pszName);
                    }
                    prq++;
                }		/*endfor cReturned */
            }
            DosFreeMem((PVOID) pBuf);
        }
    }
    /* end if Q level given */
    else {
        /* If we are here we had a bad error code. Print it and some other info. */
        emprintf4(mem,
                  "SplEnumQueue Error=%ld, Total=%ld, Returned=%ld, Needed=%ld\n",
                  splerr, cTotal, cReturned, cbNeeded);
    }
    if (splerr)
        return splerr;
    if (queue_name)
        return -1;
    return 0;
}

/* return TRUE if queue looks like a valid OS/2 queue name */
static int
is_os2_spool(const char *queue)
{
    char *prefix = "\\\\spool\\";	/* 8 characters long */
    int i;

    for (i = 0; i < 8; i++) {
        if (prefix[i] == '\\') {
            if ((*queue != '\\') && (*queue != '/'))
                return FALSE;
        } else if (tolower(*queue) != prefix[i])
            return FALSE;
        queue++;
    }
    return TRUE;
}

#define PRINT_BUF_SIZE 16384

/* Spool file to queue */
/* return 0 if successful, non-zero if error */
/* if filename is NULL, return 0 if spool queue is valid, non-zero if error */
int
pm_spool(const gs_memory_t *mem, char *filename, const char *queue)
{
    HSPL hspl;
    PDEVOPENSTRUC pdata;
    PSZ pszToken = "*";
    ULONG jobid;
    BOOL rc;
    char queue_name[256];
    char driver_name[256];
    char *buffer;
    FILE *f;
    int count;

    if (strlen(queue) != 0) {
        /* queue specified */
        strcpy(queue_name, queue + 8);	/* skip over \\spool\ */
    } else {
        /* get default queue */
        queue_name[0] = '\0';
    }
    if (pm_find_queue(mem, queue_name, driver_name)) {
        /* error, list valid queue names */
        emprintf(mem, "Invalid queue name.  Use one of:\n");
        pm_find_queue(mem, NULL, NULL);
        return 1;
    }
    if (!filename)
        return 0;		/* we were only asked to check the queue */

    if ((buffer = malloc(PRINT_BUF_SIZE)) == (char *)NULL) {
        emprintf(mem, "Out of memory in pm_spool\n");
        return 1;
    }
    if ((f = gp_fopen(filename, "rb")) == (FILE *) NULL) {
        free(buffer);
        emprintf1(mem, "Can't open temporary file %s\n", filename);
        return 1;
    }
    /* Allocate memory for pdata */
    if (!DosAllocMem((PVOID) & pdata, sizeof(DEVOPENSTRUC),
                     (PAG_READ | PAG_WRITE | PAG_COMMIT))) {
        /* Initialize elements of pdata */
        pdata->pszLogAddress = queue_name;
        pdata->pszDriverName = driver_name;
        pdata->pdriv = NULL;
        pdata->pszDataType = "PM_Q_RAW";
        pdata->pszComment = "Ghostscript";
        pdata->pszQueueProcName = NULL;
        pdata->pszQueueProcParams = NULL;
        pdata->pszSpoolerParams = NULL;
        pdata->pszNetworkParams = NULL;

        hspl = SplQmOpen(pszToken, 4L, (PQMOPENDATA) pdata);
        if (hspl == SPL_ERROR) {
            emprintf(mem, "SplQmOpen failed.\n");
            DosFreeMem((PVOID) pdata);
            free(buffer);
            fclose(f);
            return 1;		/* failed */
        }
        rc = SplQmStartDoc(hspl, "Ghostscript");
        if (!rc) {
            emprintf(mem, "SplQmStartDoc failed.\n");
            DosFreeMem((PVOID) pdata);
            free(buffer);
            fclose(f);
            return 1;
        }
        /* loop, copying file to queue */
        while (rc && (count = fread(buffer, 1, PRINT_BUF_SIZE, f)) != 0) {
            rc = SplQmWrite(hspl, count, buffer);
            if (!rc)
                emprintf(mem, "SplQmWrite failed.\n");
        }
        free(buffer);
        fclose(f);

        if (!rc) {
            emprintf(mem, "Aborting Spooling.\n");
            SplQmAbort(hspl);
        } else {
            SplQmEndDoc(hspl);
            rc = SplQmClose(hspl);
            if (!rc)
                emprintf(mem, "SplQmClose failed.\n");
        }
    } else {
        free(buffer);
        fclose(f);
        rc = 0;			/* no memory */
    }
    return !rc;
}

/* ------ Font enumeration ------ */

 /* This is used to query the native os for a list of font names and
  * corresponding paths. The general idea is to save the hassle of
  * building a custom fontmap file.
  */

void *gp_enumerate_fonts_init(gs_memory_t *mem)
{
    return NULL;
}

int gp_enumerate_fonts_next(void *enum_state, char **fontname, char **path)
{
    return 0;
}

void gp_enumerate_fonts_free(void *enum_state)
{
}

/* --------- 64 bit file access ----------- */
/* fixme: Not implemented yet.
 * Currently we stub it with 32 bits access.
 */

int64_t gp_ftell_impl(FILE *strm)
{
    return ftell(strm);
}

int gp_fseek_impl(FILE *strm, gs_offset_t offset, int origin)
{
    long offset1 = (long)offset;

    if (offset != offset1)
        return -1;
    return fseek(strm, offset1, origin);
}

bool gp_fseekable_impl(FILE *f)
{
    struct stat s;
    int fno;

    fno = fileno(f);
    if (fno < 0)
        return(false);

    if (fstat(fno, &s) < 0)
        return(false);

    return((bool)S_ISREG(s.st_mode));
}