Coverage Report

Created: 2022-10-31 07:00

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