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 /* 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
gp_init(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
gp_init(void)94 gp_init(void)
95 {
96 }
97 #endif
98
99 /* Do platform-dependent cleanup. */
100 void
gp_exit(int exit_status,int code)101 gp_exit(int exit_status, int code)
102 {
103 }
104
105 /* Exit the program. */
106 void
gp_do_exit(int exit_status)107 gp_do_exit(int exit_status)
108 {
109 exit(exit_status);
110 }
111
112 /* ------ Miscellaneous ------ */
113
114 /* Get the string corresponding to an OS error number. */
115 const char *
gp_strerror(int errnum)116 gp_strerror(int errnum)
117 {
118 #ifdef HAVE_STRERROR
119 return strerror(errnum);
120 #else
121 return NULL;
122 #endif
123 }
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
gp_serialnumber(void)128 gp_serialnumber(void)
129 {
130 return (int)(gs_serialnumber);
131 }
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
gp_read_macresource(byte * buf,const char * filename,const uint type,const ushort id)137 gp_read_macresource(byte *buf, const char *filename,
138 const uint type, const ushort id)
139 {
140 return 0;
141 }
142
143 /* ------ Date and time ------ */
144
145 /* Read the current time (in seconds since Jan. 1, 1970) */
146 /* and fraction (in nanoseconds). */
147 void
gp_get_realtime(long * pdt)148 gp_get_realtime(long *pdt)
149 {
150 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 {
161 struct timezone tzp;
162
163 if (gettimeofday(&tp, &tzp) == -1) {
164 lprintf("Ghostscript: gettimeofday failed!\n");
165 tp.tv_sec = tp.tv_usec = 0;
166 }
167 }
168 #endif
169
170 /* tp.tv_sec is #secs since Jan 1, 1970 */
171 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 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 }
182
183 /* Read the current user CPU time (in seconds) */
184 /* and fraction (in nanoseconds). */
185 void
gp_get_usertime(long * pdt)186 gp_get_usertime(long *pdt)
187 {
188 #if use_times_for_usertime
189 struct tms tms;
190 long ticks;
191 const long ticks_per_sec = CLK_TCK;
192
193 times(&tms);
194 ticks = tms.tms_utime + tms.tms_stime + tms.tms_cutime + tms.tms_cstime;
195 pdt[0] = ticks / ticks_per_sec;
196 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 }
201
202 /* ------ Screen management ------ */
203
204 /* Get the environment variable that specifies the display to use. */
205 const char *
gp_getenv_display(void)206 gp_getenv_display(void)
207 {
208 return getenv("DISPLAY");
209 }
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 *
gp_open_printer_impl(gs_memory_t * mem,const char * fname,int * binary_mode,int (** close)(FILE *))218 gp_open_printer_impl(gs_memory_t *mem,
219 const char *fname,
220 int *binary_mode,
221 int (**close)(FILE *))
222 {
223 #ifdef GS_NO_FILESYSTEM
224 return NULL;
225 #else
226 const char *fmode = (*binary_mode ? "wb" : "w");
227 *close = fname[0] == '|' ? pclose : fclose;
228 return gp_fopen_impl(mem, fname, fmode);
229 #endif
230 }
231
232 /* Close the connection to the printer. */
233 void
gp_close_printer(gp_file * pfile,const char * fname)234 gp_close_printer(gp_file * pfile, const char *fname)
235 {
236 #ifndef GS_NO_FILESYSTEM
237 gp_fclose(pfile);
238 #endif
239 }
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
makePSFontName(char * family,int weight,int slant,char * buf,int bufsize)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
gp_enumerate_fonts_init(gs_memory_t * mem)319 void *gp_enumerate_fonts_init(gs_memory_t *mem)
320 {
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 NULL);
377 os = FcObjectSetBuild(FC_FILE, FC_OUTLINE,
378 FC_FAMILY, FC_WEIGHT, FC_SLANT,
379 NULL);
380 /* We free the data allocated by FcFontList() when
381 gp_enumerate_fonts_free() calls FcFontSetDestroy(), but FcFontSetDestroy()
382 has been seen to leak blocks according to valgrind and asan. E.g. this can
383 be seen by running:
384 ./sanbin/gs -dNODISPLAY -dBATCH -dNOPAUSE -c "/A_Font findfont"
385 */
386 state->font_list = FcFontList(0, pat, os);
387 FcPatternDestroy(pat);
388 FcObjectSetDestroy(os);
389 if (state->font_list == NULL) {
390 free(state);
391 state = NULL;
392 return NULL; /* Failed to generate font list */
393 }
394 return (void *)state;
395 #else
396 return NULL;
397 #endif
398 }
399
gp_enumerate_fonts_next(void * enum_state,char ** fontname,char ** path)400 int gp_enumerate_fonts_next(void *enum_state, char **fontname, char **path)
401 {
402 #ifdef HAVE_FONTCONFIG
403 unix_fontenum_t* state = (unix_fontenum_t *)enum_state;
404 FcChar8 *file_fc = NULL;
405 FcChar8 *family_fc = NULL;
406 int outline_fc, slant_fc, weight_fc;
407 FcPattern *font;
408 FcResult result;
409
410 if (state == NULL) {
411 return 0; /* gp_enumerate_fonts_init failed for some reason */
412 }
413
414 if (state->index == state->font_list->nfont) {
415 return 0; /* we've run out of fonts */
416 }
417
418 /* Bits of the following were borrowed from Red Hat's
419 * fontconfig patch for Ghostscript 7 */
420 font = state->font_list->fonts[state->index];
421
422 result = FcPatternGetString (font, FC_FAMILY, 0, &family_fc);
423 if (result != FcResultMatch || family_fc == NULL) {
424 dmlprintf(state->mem, "DEBUG: FC_FAMILY mismatch\n");
425 return 0;
426 }
427
428 result = FcPatternGetString (font, FC_FILE, 0, &file_fc);
429 if (result != FcResultMatch || file_fc == NULL) {
430 dmlprintf(state->mem, "DEBUG: FC_FILE mismatch\n");
431 return 0;
432 }
433
434 result = FcPatternGetBool (font, FC_OUTLINE, 0, &outline_fc);
435 if (result != FcResultMatch) {
436 dmlprintf1(state->mem, "DEBUG: FC_OUTLINE failed to match on %s\n", (char*)family_fc);
437 return 0;
438 }
439
440 result = FcPatternGetInteger (font, FC_SLANT, 0, &slant_fc);
441 if (result != FcResultMatch) {
442 dmlprintf(state->mem, "DEBUG: FC_SLANT didn't match\n");
443 return 0;
444 }
445
446 result = FcPatternGetInteger (font, FC_WEIGHT, 0, &weight_fc);
447 if (result != FcResultMatch) {
448 dmlprintf(state->mem, "DEBUG: FC_WEIGHT didn't match\n");
449 return 0;
450 }
451
452 /* Gross hack to work around Fontconfig's inability to tell
453 * us the font's PostScript name - generate it ourselves.
454 * We must free the memory allocated here next time around. */
455 makePSFontName((char *)family_fc, weight_fc, slant_fc,
456 (char *)&state->name, sizeof(state->name));
457 *fontname = (char *)&state->name;
458
459 /* return the font path straight out of fontconfig */
460 *path = (char*)file_fc;
461
462 state->index ++;
463 return 1;
464 #else
465 return 0;
466 #endif
467 }
468
gp_enumerate_fonts_free(void * enum_state)469 void gp_enumerate_fonts_free(void *enum_state)
470 {
471 #ifdef HAVE_FONTCONFIG
472 unix_fontenum_t* state = (unix_fontenum_t *)enum_state;
473 if (state != NULL) {
474 if (state->font_list != NULL)
475 FcFontSetDestroy(state->font_list);
476 if (state->fc)
477 FcConfigDestroy(state->fc);
478 free(state);
479 }
480 #endif
481 }
482
483 /* A function to decode the next codepoint of the supplied args from the
484 * local windows codepage, or -1 for EOF.
485 * (copied from gp_win32.c)
486 */
487
488 #ifdef __MINGW32__
489 int
gp_local_arg_encoding_get_codepoint(FILE * file,const char ** astr)490 gp_local_arg_encoding_get_codepoint(FILE *file, const char **astr)
491 {
492 int len;
493 int c;
494 char arg[3];
495 wchar_t unicode[2];
496 char utf8[4];
497
498 if (file) {
499 c = fgetc(file);
500 if (c == EOF)
501 return EOF;
502 } else if (**astr) {
503 c = *(*astr)++;
504 if (c == 0)
505 return EOF;
506 } else {
507 return EOF;
508 }
509
510 arg[0] = c;
511 if (IsDBCSLeadByte(c)) {
512 if (file) {
513 c = fgetc(file);
514 if (c == EOF)
515 return EOF;
516 } else if (**astr) {
517 c = *(*astr)++;
518 if (c == 0)
519 return EOF;
520 }
521 arg[1] = c;
522 len = 2;
523 } else {
524 len = 1;
525 }
526
527 /* Convert the string (unterminated in, unterminated out) */
528 len = MultiByteToWideChar(CP_ACP, 0, arg, len, unicode, 2);
529
530 return unicode[0];
531 }
532 #endif
533