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