Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/base/gp_unix.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Unix-specific routines for Ghostscript */
18
19
#ifdef __MINGW32__
20
#  include "windows_.h"
21
#endif
22
#include "pipe_.h"
23
#include "string_.h"
24
#include "time_.h"
25
#include "gx.h"
26
#include "gsexit.h"
27
#include "gp.h"
28
#include "stream.h"
29
30
#ifdef HAVE_FONTCONFIG
31
#  include <fontconfig/fontconfig.h>
32
#endif
33
34
/*
35
 * This is the only place in Ghostscript that calls 'exit'.  Including
36
 * <stdlib.h> is overkill, but that's where it's declared on ANSI systems.
37
 * We don't have any way of detecting whether we have a standard library
38
 * (some GNU compilers perversely define __STDC__ but don't provide
39
 * an ANSI-compliant library), so we check __PROTOTYPES__ and
40
 * hope for the best.  We pick up getenv at the same time.
41
 */
42
#ifdef __PROTOTYPES__
43
#  include <stdlib.h>           /* for exit and getenv */
44
#else
45
extern void exit(int);
46
extern char *getenv(const char *);
47
#endif
48
49
#ifdef GS_DEVS_SHARED
50
#ifndef GS_DEVS_SHARED_DIR
51
#  define GS_DEVS_SHARED_DIR "/usr/lib/ghostscript/8.16"
52
#endif
53
/*
54
 * use shared library for drivers, always load them when starting, this
55
 * avoid too many modifications, and since it is supported only under linux
56
 * and applied as a patch (preferable).
57
 */
58
#include <sys/types.h>
59
#include <dirent.h>
60
#include <dlfcn.h>
61
#include <string.h>
62
63
void
64
gp_init(void)
65
{
66
  DIR*           dir = NULL;
67
  struct dirent* dirent;
68
  char           buff[1024];
69
  char*          pbuff;
70
  void*          handle;
71
  void           (*gs_shared_init)(void);
72
73
  strncpy(buff, GS_DEVS_SHARED_DIR, sizeof(buff) - 2);
74
  pbuff = buff + strlen(buff);
75
  *pbuff++ = '/'; *pbuff = '\0';
76
77
  dir = opendir(GS_DEVS_SHARED_DIR);
78
  if (dir == 0) return;
79
80
  while ((dirent = readdir(dir)) != 0) {
81
    strncpy(pbuff, dirent->d_name, sizeof(buff) - (pbuff - buff) - 1);
82
    if ((handle = dlopen(buff, RTLD_NOW)) != 0) {
83
      if ((gs_shared_init = dlsym(handle, "gs_shared_init")) != 0) {
84
        (*gs_shared_init)();
85
      } else {
86
      }
87
    }
88
  }
89
90
  closedir(dir);
91
}
92
#else
93
/* Do platform-dependent initialization. */
94
void
95
gp_init(void)
96
10.8k
{
97
10.8k
}
98
#endif
99
100
/* Do platform-dependent cleanup. */
101
void
102
gp_exit(int exit_status, int code)
103
10.8k
{
104
10.8k
}
105
106
/* Exit the program. */
107
void
108
gp_do_exit(int exit_status)
109
0
{
110
0
    exit(exit_status);
111
0
}
112
113
/* ------ Miscellaneous ------ */
114
115
/* Get the string corresponding to an OS error number. */
116
const char *
117
gp_strerror(int errnum)
118
3.09k
{
119
3.09k
#ifdef HAVE_STRERROR
120
3.09k
    return strerror(errnum);
121
#else
122
    return NULL;
123
#endif
124
3.09k
}
125
126
/* We don't have a good way to get a serial number here, so just */
127
/* return what we always used to: GS_SERIALNUMBER. */
128
int
129
gp_serialnumber(void)
130
0
{
131
0
    return (int)(gs_serialnumber);
132
0
}
133
134
/* read in a MacOS 'resource' from an extended attribute. */
135
/* we don't try to implemented this since it requires support */
136
/* for Apple's HFS(+) filesystem */
137
int
138
gp_read_macresource(byte *buf, const char *filename,
139
                    const uint type, const ushort id)
140
0
{
141
0
    return 0;
142
0
}
143
144
/* ------ Date and time ------ */
145
146
/* Read the current time (in seconds since Jan. 1, 1970) */
147
/* and fraction (in nanoseconds). */
148
void
149
gp_get_realtime(long *pdt)
150
21.6k
{
151
21.6k
    struct timeval tp;
152
153
#if gettimeofday_no_timezone    /* older versions of SVR4 */
154
    {
155
        if (gettimeofday(&tp) == -1) {
156
            lprintf("Ghostscript: gettimeofday failed!\n");
157
            tp.tv_sec = tp.tv_usec = 0;
158
        }
159
    }
160
#else /* All other systems */
161
21.6k
    {
162
21.6k
        struct timezone tzp;
163
164
21.6k
        if (gettimeofday(&tp, &tzp) == -1) {
165
0
            lprintf("Ghostscript: gettimeofday failed!\n");
166
0
            tp.tv_sec = tp.tv_usec = 0;
167
0
        }
168
21.6k
    }
169
21.6k
#endif
170
171
    /* tp.tv_sec is #secs since Jan 1, 1970 */
172
21.6k
    pdt[0] = tp.tv_sec;
173
174
    /* Some Unix systems (e.g., Interactive 3.2 r3.0) return garbage */
175
    /* in tp.tv_usec.  Try to filter out the worst of it here. */
176
21.6k
    pdt[1] = tp.tv_usec >= 0 && tp.tv_usec < 1000000 ? tp.tv_usec * 1000 : 0;
177
178
#ifdef DEBUG_CLOCK
179
    printf("tp.tv_sec = %d  tp.tv_usec = %d  pdt[0] = %ld  pdt[1] = %ld\n",
180
           tp.tv_sec, tp.tv_usec, pdt[0], pdt[1]);
181
#endif
182
21.6k
}
183
184
/* Read the current user CPU time (in seconds) */
185
/* and fraction (in nanoseconds).  */
186
void
187
gp_get_usertime(long *pdt)
188
19
{
189
19
#if use_times_for_usertime
190
19
    struct tms tms;
191
19
    long ticks;
192
19
    const long ticks_per_sec = CLK_TCK;
193
194
19
    times(&tms);
195
19
    ticks = tms.tms_utime + tms.tms_stime + tms.tms_cutime + tms.tms_cstime;
196
19
    pdt[0] = ticks / ticks_per_sec;
197
19
    pdt[1] = (ticks % ticks_per_sec) * (1000000000 / ticks_per_sec);
198
#else
199
    gp_get_realtime(pdt);       /* Use an approximation on other hosts.  */
200
#endif
201
19
}
202
203
/* ------ Screen management ------ */
204
205
/* Get the environment variable that specifies the display to use. */
206
const char *
207
gp_getenv_display(void)
208
0
{
209
0
    return getenv("DISPLAY");
210
0
}
211
212
/* ------ Printer accessing ------ */
213
214
extern gp_file *
215
gp_fopen_unix(const gs_memory_t *mem, const char *fname, const char *mode, int pipe);
216
217
/* Open a connection to a printer.  See gp.h for details. */
218
FILE *
219
gp_open_printer_impl(gs_memory_t *mem,
220
                     const char  *fname,
221
                     int         *binary_mode,
222
                     int          (**close)(FILE *))
223
5.17k
{
224
#ifdef GS_NO_FILESYSTEM
225
    return NULL;
226
#else
227
5.17k
    const char *fmode = (*binary_mode ? "wb" : "w");
228
5.17k
    *close = fname[0] == '|' ? pclose : fclose;
229
5.17k
    return gp_fopen_impl(mem, fname, fmode);
230
5.17k
#endif
231
5.17k
}
232
233
/* Close the connection to the printer. */
234
void
235
gp_close_printer(gp_file * pfile, const char *fname)
236
5.17k
{
237
5.17k
#ifndef GS_NO_FILESYSTEM
238
5.17k
    gp_fclose(pfile);
239
5.17k
#endif
240
5.17k
}
241
242
/* ------ Font enumeration ------ */
243
244
 /* This is used to query the native os for a list of font names and
245
  * corresponding paths. The general idea is to save the hassle of
246
  * building a custom fontmap file.
247
  */
248
249
/* Mangle the FontConfig family and style information into a
250
 * PostScript font name */
251
#ifdef HAVE_FONTCONFIG
252
static void makePSFontName(char* family, int weight, int slant, char *buf, int bufsize)
253
{
254
    int bytesCopied, length, i;
255
    const char *slantname, *weightname;
256
257
    switch (slant) {
258
        case FC_SLANT_ROMAN:   slantname=""; break;;
259
        case FC_SLANT_OBLIQUE: slantname="Oblique"; break;;
260
        case FC_SLANT_ITALIC:  slantname="Italic"; break;;
261
        default:               slantname="Unknown"; break;;
262
    }
263
264
    switch (weight) {
265
        case FC_WEIGHT_MEDIUM:   weightname=""; break;;
266
        case FC_WEIGHT_LIGHT:    weightname="Light"; break;;
267
        case FC_WEIGHT_DEMIBOLD: weightname="Demi"; break;;
268
        case FC_WEIGHT_BOLD:     weightname="Bold"; break;;
269
        case FC_WEIGHT_BLACK:    weightname="Black"; break;;
270
        default:                 weightname="Unknown"; break;;
271
    }
272
273
    length = strlen(family);
274
    if (length >= bufsize)
275
        length = bufsize;
276
    /* Copy the family name, stripping spaces */
277
    bytesCopied=0;
278
    for (i = 0; i < length; i++)
279
        if (family[i] != ' ')
280
            buf[bytesCopied++] = family[i];
281
282
    if ( ((slant != FC_SLANT_ROMAN) || (weight != FC_WEIGHT_MEDIUM)) \
283
            && bytesCopied < bufsize )
284
    {
285
        buf[bytesCopied] = '-';
286
        bytesCopied++;
287
        if (weight != FC_WEIGHT_MEDIUM)
288
        {
289
            length = strlen(family);
290
            if ((length + bytesCopied) >= bufsize)
291
                length = bufsize - bytesCopied - 1;
292
            strncpy(buf+bytesCopied, weightname, length);
293
            bytesCopied += length;
294
        }
295
        if (slant != FC_SLANT_ROMAN)
296
        {
297
            length = strlen(family);
298
            if ((length + bytesCopied) >= bufsize)
299
                length = bufsize - bytesCopied - 1;
300
            strncpy(buf+bytesCopied, slantname, length);
301
            bytesCopied += length;
302
        }
303
    }
304
    buf[bytesCopied] = '\0';
305
}
306
#endif
307
308
/* State struct for font iteration
309
 * - passed as an opaque 'void*' through the rest of gs */
310
#ifdef HAVE_FONTCONFIG
311
typedef struct {
312
    int index;              /* current index of iteration over font_list */
313
    FcConfig* fc;           /* FontConfig library handle */
314
    FcFontSet* font_list;   /* FontConfig font list */
315
    char name[255];         /* name of last font */
316
    gs_memory_t *mem;       /* Memory pointer */
317
} unix_fontenum_t;
318
#endif
319
320
void *gp_enumerate_fonts_init(gs_memory_t *mem)
321
200k
{
322
#ifdef HAVE_FONTCONFIG
323
    unix_fontenum_t *state;
324
    FcPattern *pat;
325
    FcObjectSet *os;
326
    FcStrList *fdirlist = NULL;
327
    FcChar8 *dirstr;
328
    int code;
329
330
    state = (unix_fontenum_t *)malloc(sizeof(unix_fontenum_t));
331
    if (state == NULL)
332
        return NULL;    /* Failed to allocate state */
333
334
    state->index     = 0;
335
    state->fc        = NULL;
336
    state->font_list = NULL;
337
    state->mem       = mem;
338
339
    /* Load the fontconfig library */
340
    state->fc = FcInitLoadConfigAndFonts();
341
    if (state->fc == NULL) {
342
        free(state);
343
        state = NULL;
344
        dmlprintf(mem, "destroyed state - fontconfig init failed");
345
        return NULL;  /* Failed to open fontconfig library */
346
    }
347
348
    fdirlist = FcConfigGetFontDirs(state->fc);
349
    if (fdirlist == NULL) {
350
        FcConfigDestroy(state->fc);
351
        free(state);
352
        return NULL;
353
    }
354
355
    /* We're going to trust what fontconfig tells us, and add it's known directories
356
     * to our permitted read paths
357
     */
358
    code = 0;
359
    while ((dirstr = FcStrListNext(fdirlist)) != NULL && code >= 0) {
360
        char dirstr2[gp_file_name_sizeof];
361
        dirstr2[0] = '\0';
362
        strncat(dirstr2, (char *)dirstr, gp_file_name_sizeof - 2);
363
        strncat(dirstr2, "/", gp_file_name_sizeof - 1);
364
        code = gs_add_control_path(mem, gs_permit_file_reading, (char *)dirstr2);
365
    }
366
    FcStrListDone(fdirlist);
367
    if (code < 0) {
368
        FcConfigDestroy(state->fc);
369
        free(state);
370
        return NULL;
371
    }
372
373
    /* load the font set that we'll iterate over */
374
    pat = FcPatternBuild(NULL,
375
            FC_OUTLINE, FcTypeBool, 1,
376
            FC_SCALABLE, FcTypeBool, 1,
377
#if defined(FC_MAJOR) && FC_MAJOR >= 2 && defined(FC_MINOR) && FC_MINOR >= 13
378
            FC_VARIABLE, FcTypeBool, 0,
379
#endif
380
            NULL);
381
    os = FcObjectSetBuild(FC_FILE, FC_OUTLINE,
382
            FC_FAMILY, FC_WEIGHT, FC_SLANT,
383
            NULL);
384
    /* We free the data allocated by FcFontList() when
385
    gp_enumerate_fonts_free() calls FcFontSetDestroy(), but FcFontSetDestroy()
386
    has been seen to leak blocks according to valgrind and asan. E.g. this can
387
    be seen by running:
388
        ./sanbin/gs -dNODISPLAY -dBATCH -dNOPAUSE -c "/A_Font findfont"
389
    */
390
    state->font_list = FcFontList(0, pat, os);
391
    FcPatternDestroy(pat);
392
    FcObjectSetDestroy(os);
393
    if (state->font_list == NULL) {
394
        free(state);
395
        state = NULL;
396
        return NULL;  /* Failed to generate font list */
397
    }
398
    return (void *)state;
399
#else
400
200k
    return NULL;
401
200k
#endif
402
200k
}
403
404
int gp_enumerate_fonts_next(void *enum_state, char **fontname, char **path)
405
0
{
406
#ifdef HAVE_FONTCONFIG
407
    unix_fontenum_t* state = (unix_fontenum_t *)enum_state;
408
    FcChar8 *file_fc = NULL;
409
    FcChar8 *family_fc = NULL;
410
    int outline_fc, slant_fc, weight_fc;
411
    FcPattern *font;
412
    FcResult result;
413
414
    if (state == NULL) {
415
        return 0;   /* gp_enumerate_fonts_init failed for some reason */
416
    }
417
418
    /* We use the loop so we can skip over fonts that return errors */
419
    while(1) {
420
        if (state->index == state->font_list->nfont) {
421
            return 0; /* we've run out of fonts */
422
        }
423
424
        /* Bits of the following were borrowed from Red Hat's
425
         * fontconfig patch for Ghostscript 7 */
426
        font = state->font_list->fonts[state->index];
427
        state->index++;
428
429
        /* We do the FC_FILE first because this *should* never fail
430
         * and it gives us a string to use in later debug prints
431
         */
432
        result = FcPatternGetString (font, FC_FILE, 0, &file_fc);
433
        if (result != FcResultMatch || file_fc == NULL) {
434
            dmlprintf(state->mem, "DEBUG: FC_FILE mismatch\n");
435
            continue;
436
        }
437
438
        result = FcPatternGetString (font, FC_FAMILY, 0, &family_fc);
439
        if (result != FcResultMatch || family_fc == NULL) {
440
            dmlprintf1(state->mem, "DEBUG: FC_FAMILY mismatch in %s\n", (char *)file_fc);
441
            continue;
442
        }
443
444
        result = FcPatternGetBool (font, FC_OUTLINE, 0, &outline_fc);
445
        if (result != FcResultMatch) {
446
            dmlprintf2(state->mem, "DEBUG: FC_OUTLINE failed to match on %s in %s\n", (char*)family_fc, (char *)file_fc);
447
            continue;
448
        }
449
450
        result = FcPatternGetInteger (font, FC_SLANT, 0, &slant_fc);
451
        if (result != FcResultMatch) {
452
            dmlprintf1(state->mem, "DEBUG: FC_SLANT didn't match in %s\n", (char *)file_fc);
453
            continue;
454
        }
455
456
        result = FcPatternGetInteger (font, FC_WEIGHT, 0, &weight_fc);
457
        if (result != FcResultMatch) {
458
            dmlprintf1(state->mem, "DEBUG: FC_WEIGHT didn't match in %s\n", (char *)file_fc);
459
            continue;
460
        }
461
        break;
462
    }
463
464
    /* Gross hack to work around Fontconfig's inability to tell
465
     * us the font's PostScript name - generate it ourselves.
466
     * We must free the memory allocated here next time around. */
467
    makePSFontName((char *)family_fc, weight_fc, slant_fc,
468
        (char *)&state->name, sizeof(state->name));
469
    *fontname = (char *)&state->name;
470
471
    /* return the font path straight out of fontconfig */
472
    *path = (char*)file_fc;
473
474
    return 1;
475
#else
476
0
    return 0;
477
0
#endif
478
0
}
479
480
void gp_enumerate_fonts_free(void *enum_state)
481
199k
{
482
#ifdef HAVE_FONTCONFIG
483
    unix_fontenum_t* state = (unix_fontenum_t *)enum_state;
484
    if (state != NULL) {
485
        if (state->font_list != NULL)
486
            FcFontSetDestroy(state->font_list);
487
        if (state->fc)
488
            FcConfigDestroy(state->fc);
489
        free(state);
490
    }
491
#endif
492
199k
}
493
494
/* A function to decode the next codepoint of the supplied args from the
495
 * local windows codepage, or -1 for EOF.
496
 * (copied from gp_win32.c)
497
 */
498
499
#ifdef __MINGW32__
500
int
501
gp_local_arg_encoding_get_codepoint(stream *s, const char **astr)
502
{
503
    int len;
504
    int c;
505
    char arg[3];
506
    wchar_t unicode[2];
507
    char utf8[4];
508
509
    if (s) {
510
        c = spgetc(s);
511
        if (c == EOF)
512
            return EOF;
513
    } else if (**astr) {
514
        c = *(*astr)++;
515
        if (c == 0)
516
            return EOF;
517
    } else {
518
        return EOF;
519
    }
520
521
    arg[0] = c;
522
    if (IsDBCSLeadByte(c)) {
523
        if (s) {
524
            c = spgetc(s);
525
            if (c == EOF)
526
                return EOF;
527
        } else if (**astr) {
528
            c = *(*astr)++;
529
            if (c == 0)
530
                return EOF;
531
        }
532
        arg[1] = c;
533
        len = 2;
534
    } else {
535
        len = 1;
536
    }
537
538
    /* Convert the string (unterminated in, unterminated out) */
539
    len = MultiByteToWideChar(CP_ACP, 0, arg, len, unicode, 2);
540
541
    return unicode[0];
542
}
543
#endif