/*========================================================================*\ Copyright (c) 1993-2000 Paul Vojta Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL PAUL VOJTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \*========================================================================*/ #if !lint static char copyright[] = "@(#) Copyright (c) 1993-1998 Paul Vojta.\n"; #endif /* * Reference for PK file format: * * Tomas Rokicki, Packed (PK) font file format, TUGBoat 6 (1985) 115-120. */ /* * Kpathsea version by Thomas Esser, John Interrante, Yves Arrouye, Karl Berry. */ #include "version.h" #ifndef KPATHSEA #include "config.h" /* Some O/S dependent kludges. */ #if _AIX #define _ALL_SOURCE 1 #endif #if __hpux #define _HPUX_SOURCE 1 #endif #if STDC_HEADERS # include # include #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr(), *strrchr(); # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy((s), (d), (n)) # define memmove(d, s, n) bcopy((s), (d), (n)) # define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) # endif #endif #if HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include #ifndef errno extern int errno; #endif #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #ifndef atof double atof(); #endif char *getenv(); /* is already included. */ #if HAVE_SYS_WAIT_H #include #endif #ifndef WIFSTOPPED #define WIFSTOPPED(stat_val) (((stat_val) & 0377) == 0177) #endif #ifndef WIFSIGNALED #define WIFSIGNALED(stat_val) (((stat_val) & 0377) != 0) #endif #ifndef WTERMSIG #define WTERMSIG(stat_val) ((stat_val) & 0177) #endif #ifndef WEXITSTATUS #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef SIGCHLD #define SIGCHLD SIGCLD #endif /* How to open a text file for reading: */ #ifndef FOPEN_R_MODE #define FOPEN_R_MODE "r" #endif /* How to open a binary file for reading: */ #ifndef FOPEN_RBIN_MODE #if DOS #define FOPEN_RBIN_MODE "r+b" #elif VMS || VMCMS || OS2 || WIN32 #define FOPEN_RBIN_MODE "rb" #else #define FOPEN_RBIN_MODE "r" #endif #endif /* undef FOPEN_RBIN_MODE */ /* How to open a binary file for writing: */ #ifndef FOPEN_WBIN_MODE #if DOS #define FOPEN_WBIN_MODE "w+b" #elif OS2 || WIN32 #define FOPEN_WBIN_MODE "wb" #elif VMCMS #define FOPEN_WBIN_MODE "wb, lrecl=1024, recfm=f" #else #define FOPEN_WBIN_MODE "w" #endif #endif /* undef FOPEN_WBIN_MODE */ #else /* KPATHSEA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if !HAVE_STRCHR #define strchr index #endif #if !HAVE_STRRCHR #define strrchr rindex #endif #if HAVE_POLL && !HAVE_POLL_H #undef HAVE_POLL #endif /* Add two new flags to kpathsea_debug for debugging gsftopk */ #define GSPK_DEBUG_BITMAP (KPSE_LAST_DEBUG + 1) #define GSPK_DEBUG_PK (KPSE_LAST_DEBUG + 2) /* is already included: --> --> --> --> . */ #if HAVE_SYS_WAIT_H # include #endif #if WIN32 /* code from Akira Kakuto */ /* message values for callback */ #define GSDLL_STDIN 1 #define GSDLL_STDOUT 2 #define GSDLL_DEVICE 3 #define GSDLL_SYNC 4 #define GSDLL_PAGE 5 #define GSDLL_SIZE 6 #define GSDLL_POLL 7 /* return values from gsdll_init() */ #define GSDLL_INIT_IN_USE 100 #define GSDLL_INIT_QUIT 101 #include HINSTANCE hgsdll = NULL; PROC pgsdll_init = NULL; PROC pgsdll_exit = NULL; PROC pgsdll_revision = NULL; #if 0 /* unused */ static long revision_n = 0; static char product_str[128]; static char copyright_str[128]; static long revdate_n; #endif /* 0, unused */ static void Win32Error(const char *s) { fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); } #ifdef _WIN64 #define GSDLLNAME "gsdll64.dll" #else #define GSDLLNAME "gsdll32.dll" #endif static HINSTANCE gs_locate(void) { hgsdll = GetModuleHandle(GSDLLNAME); if(hgsdll == NULL) { hgsdll = LoadLibrary(GSDLLNAME); } return hgsdll; } static void gs_dll_release(void) { FreeLibrary(hgsdll); } static void gs_dll_initialize(void) { pgsdll_init = GetProcAddress(hgsdll, "gsdll_init"); pgsdll_exit = GetProcAddress(hgsdll, "gsdll_exit"); pgsdll_revision = GetProcAddress(hgsdll, "gsdll_revision"); if(pgsdll_init == NULL || pgsdll_exit == NULL || pgsdll_revision == NULL) { fprintf(stderr, "Failed to get proc addresses in GSDLL32.\n"); gs_dll_release(); exit(100); } } /* end of code from Akira Kakuto */ HANDLE hGsThread = NULL; HANDLE hGsDataIn = 0, hGsDataOut = 0; /* Events to synchronize threads */ /* Arguments to gs dll */ const char *gs_argv[] = { "rungs.exe", /* 0, dummy */ "-dNODISPLAY", /* 1, */ "-dNOGC", /* 2, */ "-dNOSAFER", /* 3, */ NULL, /* 4, substarg */ "-q", /* 5, */ "--", /* 6, */ NULL, /* 7, searchpath */ NULL, /* 8, PSname */ NULL, /* 9, dlstring != NULL ? dlstring : "" */ NULL, /* 10, specinfo */ NULL, /* 11, dpistr */ NULL /* 12, NULL terminator */ }; int gs_argc = 12; char *buffer_stdin; /* This is the buffer from where data are taken. */ #else /* not WIN32 */ #ifndef WEXITSTATUS #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFSTOPPED #define WIFSTOPPED(stat_val) (((stat_val) & 0377) == 0177) #endif #ifndef WIFSIGNALED #define WIFSIGNALED(stat_val) (((stat_val) & 0377) != 0) #endif #ifndef WTERMSIG #define WTERMSIG(stat_val) ((stat_val) & 0177) #endif #endif /* not WIN32 */ #endif /* KPATHSEA */ #if HAVE_POLL # include #else # if HAVE_SYS_SELECT_H # include # else # if HAVE_SELECT_H # include # endif # endif #endif #if HAVE_VFORK_H #include #endif #if _AMIGA #include #include #endif #define NUMBER(x) (sizeof (x) / sizeof *(x)) /* if POSIX O_NONBLOCK is not available, use O_NDELAY */ #if !defined(O_NONBLOCK) && defined(O_NDELAY) #define O_NONBLOCK O_NDELAY #endif #ifndef S_ISDIR #if defined(S_IFMT) && defined(S_IFDIR) #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif #endif #ifndef GS_PATH #define GS_PATH "gs" #endif #include typedef char Boolean; #define True 1 #define False 0 typedef int wide_bool; #ifndef MAXPATHLEN #define MAXPATHLEN 256 #endif #define PK_PRE (char) 247 #define PK_ID (char) 89 #define PK_SPC (char) 240 #define PK_POST (char) 245 #define PK_NOP (char) 246 #ifndef KPATHSEA char progname[] = "gsftopk "; #else char progname[] = "gsftopk(k) "; #endif char version[] = VERSION; /* * Command line arguments */ #define OPT_DBG 0x101 Boolean test = False; char *fontname; int fontlen; char *mapline = NULL; char *mapfile = NULL; char gspath[] = GS_PATH; /* gs interpreter path */ Boolean dosnames = False; Boolean quiet = False; struct option { const char *longname; short shortname; Boolean has_arg; void *addr; int value; }; static const struct option options[] = { {"test", 't', False, &test, True}, {"mapline", 0, True, &mapline, 0}, {"mapfile", 0, True, &mapfile, 0}, {"interpreter", 'i', True, &gspath, 0}, {"dosnames", 0, False, &dosnames, True}, {"quiet", 'q', False, &quiet, True}, #if defined(KPATHSEA) && defined (KPSE_DEBUG) {"debug", OPT_DBG,True, NULL, 0}, #endif {"version", 'v', False, NULL, 0}, {"help", 'h', False, NULL, 0}}; FILE *pk_file = NULL; char *xfilename; int col = 0; /* current column number */ const char *specinfo = ""; pid_t gs_pid = 0; /* * Config file options */ #ifndef KPATHSEA /* 'H' option */ const char *config_file_header_path = HEADERPATH; #else const char *config_file_header_path = NULL; #endif struct p_list { /* list of 'p' options */ struct p_list *next; const char *value; }; /* Initialize this list to "psfonts.map". */ struct p_list psfonts_map = {NULL, "psfonts.map"}; struct p_list *p_head = &psfonts_map; struct p_list **p_tail = &psfonts_map.next; /* * Reading from the pipe from ghostscript */ Boolean data_eof = False; #if !_AMIGA #define BUFSIZE 512 typedef unsigned char byte; int data_fd; byte buffer[BUFSIZE]; byte *data_out = buffer; byte *data_end = buffer; #else /* _AMIGA */ FILE *data_file; /* This string will be used to open pipes in and out */ char tmpname[] = "gsftopkXXXXXX"; #endif /* _AMIGA */ /* * Information from the .tfm file. */ int tfm_lengths[12]; #define lh tfm_lengths[1] #define bc tfm_lengths[2] #define ec tfm_lengths[3] #define nw tfm_lengths[4] long checksum; long design; byte width_index[256]; long tfm_widths[256]; /* * Information on the bitmap currently being worked on. */ byte *bitmap; int width; int skip; int height; int hoff; int voff; int bytes_wide; unsigned int bm_size; byte *bitmap_end; int pk_len; #if !_AMIGA /* * Exit, and kill the child process, too. */ static void exit_toto_too(void) { #if !WIN32 if (gs_pid != 0) kill(gs_pid, SIGKILL); #else if (hGsThread) { switch (WaitForSingleObject(hGsThread, 2000)) { case WAIT_OBJECT_0: CloseHandle(hGsThread); hGsThread = NULL; break; case WAIT_TIMEOUT: fprintf(stderr, "Timeout waiting for Gs thread.\n"); break; case WAIT_FAILED: fprintf(stderr, "WaitForSingleObject failed on Gs thread (Error code %d).\n", (int)GetLastError()); break; default: break; } if (hGsThread) { if (TerminateThread(hGsThread, 1) == 0) { fprintf(stderr, "... couldn't terminate gs thread\n"); } CloseHandle(hGsThread); /* FIXME : is it right to call this ? */ gs_dll_release(); } } if (hGsDataIn) CloseHandle(hGsDataIn); if (hGsDataOut) CloseHandle(hGsDataOut); #endif if (pk_file != NULL) { fclose(pk_file); if (unlink(xfilename) != 0) perror("unlink"); } _exit(1); } #else /* _AMIGA */ #define exit_toto_too() exit(1) #endif /* _AMIGA */ /* * Print error message and quit. */ static void oops(const char *message, ...) { va_list args; va_start(args, message); if (col != 0) putchar('\n'); vfprintf(stderr, message, args); va_end(args); putc('\n', stderr); exit_toto_too(); } /* * Same as oops, but with arguments. */ static void opt_oops(const char *message, ...) { va_list args; va_start(args, message); fputs("gsftopk: ", stderr); vfprintf(stderr, message, args); va_end(args); fputs("\nTry `gsftopk --help' for more information.\n", stderr); exit(1); } #ifndef KPATHSEA /* * Either allocate storage or fail. */ static void * xmalloc(unsigned size) { void *mem = (void *) malloc(size); if (mem == NULL) oops("gsftopk: Cannot allocate %u bytes.\n", size); return mem; } /* * Either reallocate storage or fail. */ static void * xrealloc(char *oldp, unsigned size) { void *mem; mem = oldp == NULL ? (void *) malloc(size) : (void *) realloc(oldp, size); if (mem == NULL) oops("gsftopk: Cannot reallocate %u bytes.\n", size); return mem; } #endif /* not KPATHSEA */ /* * Get a single white-space-delimited argument (or fail). */ static char * get_one_arg(const char *src) { char *dest; const char *p; unsigned int len; len = strlen(src); p = memchr(src, ' ', len); if (p != NULL) len = p - src; p = memchr(src, '\t', len); if (p != NULL) len = p - src; dest = xmalloc(len + 1); memcpy(dest, src, len); dest[len] = '\0'; return dest; } #if !_AMIGA /* * Signal handlers. */ #if WIN32 static BOOL WINAPI handle_sigterm(DWORD dwCtrlType) { /* * Fix me: There is a problem if a system() command is running. * We should wait for the child process to be interrupted. * Only way I can think of to do that : rewrite system() based on * spawn() with parsing of the command line and set a global pid * Next cwait(pid) in the HandlerRoutine. */ switch (dwCtrlType) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: fprintf(stderr, "...exiting\n"); if (hGsThread) { if (TerminateThread(hGsThread, 1) == 0) { fprintf(stderr, "... couldn't terminate gs thread\n"); } } exit_toto_too(); return FALSE; default: fprintf(stderr, "... not exiting\n"); return TRUE; } } #else /* not WIN32 */ static Boolean got_sigchld = False; /* ARGSUSED */ static RETSIGTYPE handle_sigchild(int signo) { got_sigchld = True; } /* ARGSUSED */ static RETSIGTYPE handle_sigterm(int signo) { exit_toto_too(); } #endif /* WIN32 */ #define gs_is_done (gs_pid == 0) typedef int gsf_wait_t; #if WIN32 /* This is the callback function for gs. It is mainly used to read and write data on gs stdin/stdout. Data exchanges happen through buffers. */ static int __cdecl gsdll_callback(int message, char *str, unsigned long count) { int n; static char **pin = &buffer_stdin; /* not yet allocated, so used a pointer on it. */ switch (message) { case GSDLL_STDIN: /* Put count chars on gs stdin */ #if DEBUG fprintf(stderr, "gs wants %d chars\n", count); #endif strncpy(str, *pin, count); *pin += count; return strlen(str); case GSDLL_STDOUT: /* Fill the buffer in, wait for gsftopk to ask for data. */ WaitForSingleObject(hGsDataOut, INFINITE); #if DEBUG fprintf(stderr, "gs gives %d chars\n", count); #endif data_out = buffer; if (str == (char *)NULL || count == 0) { data_eof = True; data_end = data_out; /* Tell data_fillbuf() */ SetEvent(hGsDataIn); return 0; } n = (count >= BUFSIZE ? BUFSIZE : count); memcpy(data_out, str, n); data_end = data_out + n; /* Tell data_fillbuf() that data are available */ if (SetEvent(hGsDataIn) == FALSE) Win32Error("gsdll_callback/SetEvent"); /* return the number of chars read */ return n; case GSDLL_DEVICE: #if DEBUG fprintf(stdout,"Callback: DEVICE %p %s\n", str, count ? "open" : "close"); #endif break; case GSDLL_SYNC: #if DEBUG fprintf(stdout,"Callback: SYNC %p\n", str); #endif break; case GSDLL_PAGE: fprintf(stdout,"Callback: PAGE %p\n", str); break; case GSDLL_SIZE: #if DEBUG fprintf(stdout,"Callback: SIZE %p width=%d height=%d\n", str, (int)(count & 0xffff), (int)((count>>16) & 0xffff) ); #endif break; case GSDLL_POLL: return 0; /* no error ? */ default: fprintf(stdout,"%s: gs callback: unknown message=%d\n",progname, message); break; } return 0; } /* This is the thread function that will load the gs dll and send it the data. */ static DWORD WINAPI Win32GsSendData(LPVOID lpParam) { int ret; if (gs_locate() == NULL) { fprintf(stderr, "Can't locate Ghostscript ! Exiting ...\n"); return EXIT_FAILURE; } gs_dll_initialize(); ret = (*pgsdll_init)(gsdll_callback, NULL, gs_argc, gs_argv); if (ret == 0) { /* Should not happen : gs should quit right after being initialized. */ (*pgsdll_exit)(); /* FIXME: this is working, but we could expect something cleaner ! */ /* GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0); */ return EXIT_FAILURE; } if (ret == GSDLL_INIT_QUIT) { gs_dll_release(); return 0; } if (ret == GSDLL_INIT_IN_USE) { fprintf(stderr, "gsdll_init returned %d\n", ret); gs_dll_release(); return EXIT_FAILURE; } (*pgsdll_exit)(); #if DEBUG fprintf(stderr, "%s: gsdll_init returned %d\n", progname, ret); #endif WaitForSingleObject(hGsDataOut, INFINITE); data_eof = True; data_end = data_out; /* Tell data_fillbuf() */ SetEvent(hGsDataIn); gs_dll_release(); return 0; } #else /* not WIN32 */ static void wait_for_gs(void) { gsf_wait_t status; #if _AMIGA /* Do nothing */ #else /* not _AMIGA */ got_sigchld = False; for (;;) { if (wait(&status) != -1) break; /* if (errno == EINTR) continue; */ if (errno != EINTR) { perror("wait"); exit_toto_too(); } } if (WIFSTOPPED(status)) return; gs_pid = 0; if (WIFSIGNALED(status)) oops("gs died due to signal %d\n", WTERMSIG(status)); /* otherwise, it exited */ if (WEXITSTATUS(status) != 0) oops("gs terminated abnormally with status %d\n", WEXITSTATUS(status)); #endif /* not _AMIGA */ } #endif /* not WIN32 */ #endif /* not _AMIGA */ /* * Routines to read from the data file. */ #if _AMIGA #define data_getc() getc(data_file) #define data_ungetc(c) ungetc(c, data_file) #define data_read(p, l) fread(p, 1, l, data_file) #else /* not _AMIGA */ #if WIN32 static void data_fillbuf(void) { if (data_eof) return; /* wait until data are available. First tell gs it can fill the buffer in. */ SetEvent(hGsDataOut); /* wait for the data. */ switch (WaitForSingleObject(hGsDataIn, INFINITE)) { case WAIT_OBJECT_0: /* normal case */ break; case WAIT_TIMEOUT: /* should not happen */ fprintf(stderr, "Gs did not return on time from callback.\n"); break; case WAIT_FAILED: break; default: fprintf(stderr, "WaitForSingleObject failed for unknown reason.\n"); } } #else /* not WIN32 */ #if HAVE_POLL #define ISSET(a, b) ((poll_fd.revents & POLLIN) != 0) #else #define ISSET(a, b) FD_ISSET(a, b) #endif static void data_fillbuf(void) { int n; #if HAVE_POLL static struct pollfd poll_fd = {0, POLLIN, 0}; #else fd_set read_fds; struct timeval timeout; #endif if (data_eof) return; /* wait for readable data */ if (!gs_is_done) { for (;;) { if (!got_sigchld) { #if HAVE_POLL poll_fd.fd = data_fd; poll_fd.revents = 0; if (poll(&poll_fd, 1, 5000) == -1) { if (errno != EINTR) { perror("poll"); sleep(4); } continue; } #else FD_ZERO(&read_fds); FD_SET(data_fd, &read_fds); timeout.tv_sec = 5; timeout.tv_usec = 0; if (select(data_fd + 1, &read_fds, (fd_set *) NULL, (fd_set *) NULL, &timeout) == -1) { if (errno != EINTR) { perror("select"); sleep(4); } continue; } #endif } if (got_sigchld) { wait_for_gs(); break; } if (ISSET(data_fd, &read_fds)) break; } } /* read the data */ for (;;) { n = read(data_fd, (void *) (data_out = buffer), BUFSIZE); if (n >= 0) break; if (errno == EINTR) continue; if (gs_is_done && errno == EAGAIN) { n = 0; break; } perror("read from gs"); sleep(4); } data_end = data_out + n; if (n == 0) data_eof = True; } #endif /* not WIN32 */ static byte data_fgetc(void) { if (data_out >= data_end) data_fillbuf(); return data_eof ? EOF : *data_out++; } #define data_getc() (data_out < data_end ? *data_out++ : data_fgetc()) static void data_ungetc(byte c) { if (data_out <= buffer) oops("Too many calls to data_ungetc()"); *--data_out = c; } static int data_read(byte *buf, int n) { byte *buf1 = buf; byte *buf_end = buf + n; int n1; if (buf1 >= buf_end) return 0; while (!data_eof) { n1 = buf_end - buf1; if (n1 > data_end - data_out) n1 = data_end - data_out; memcpy(buf1, data_out, n1); buf1 += n1; data_out += n1; if (buf1 >= buf_end) break; data_fillbuf(); } return buf1 - buf; } static void data_gets(byte *buf, int n) { byte *buf1 = buf; byte *buf_end = buf + n - 1; int n1; byte *p1; if (n <= 0) return; for (;;) { if (data_eof) oops("Premature end of file"); n1 = buf_end - buf1; if (n1 > data_end - data_out) n1 = data_end - data_out; p1 = (byte *) memchr((char *) data_out, '\n', n1); if (p1 != NULL) n1 = p1 + 1 - data_out; memcpy((char *) buf1, (char *) data_out, n1); buf1 += n1; data_out += n1; if (p1 != NULL || buf1 >= buf_end) break; data_fillbuf(); } *buf1 = '\0'; return; } #endif /* not _AMIGA */ /* * Here's the patch searching stuff. First the typedefs and variables. */ #ifndef KPATHSEA static char searchpath[MAXPATHLEN + 1]; #else static char *searchpath; #endif #define HUNKSIZE (MAXPATHLEN + 2) struct spacenode { /* used for storage of directory names */ struct spacenode *next; char *sp_end; /* end of data for this chunk */ char sp[HUNKSIZE]; } firstnode; #ifndef KPATHSEA static jmp_buf found_env; static FILE *searchfile; static const char *searchname; static int searchnamelen; static const char * find_dbl_slash(const char *sp_bgn, const char *sp_end) { const char *p; for (;;) { p = memchr(sp_bgn, '/', sp_end - sp_bgn); if (p == NULL) return sp_end; if (p[1] == '/') return p; sp_bgn = p + 1; } } static void main_search_proc(char *matpos, const char *sp_pos, const char *sp_slash, const char *sp_end, wide_bool skip_subdirs, struct spacenode *space, char *spacenext) { char *mp; struct stat statbuf; DIR *dir; struct dirent *entry; int lenleft; int len; struct spacenode *space1; char *spacenext1; mp = matpos + (sp_slash - sp_pos); /* check length */ if (mp + searchnamelen >= searchpath + sizeof(searchpath) - 2) return; memcpy(matpos, sp_pos, sp_slash - sp_pos); if (sp_slash == sp_end) { /* try for a file */ *mp = '/'; strcpy(mp + (mp == searchpath || mp[-1] != '/'), searchname); searchfile = fopen(searchpath, "r"); if (searchfile != NULL) longjmp(found_env, True); } else {/* try for a subdirectory */ *mp = '\0'; if (stat(searchpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { *mp++ = '/'; main_search_proc(mp, sp_slash + 2, find_dbl_slash(sp_slash + 2, sp_end), sp_end, statbuf.st_nlink <= 2, space, spacenext); } } if (skip_subdirs) return; *matpos = '\0'; dir = opendir(searchpath); if (dir == NULL) return; lenleft = searchpath + sizeof(searchpath) - matpos; space1 = space; spacenext1 = spacenext; for (;;) { entry = readdir(dir); if (entry == NULL) break; len = NAMLEN(entry) + 1; if (len > lenleft) continue; /* too long */ strcpy(matpos, entry->d_name); if (*matpos == '.' && (matpos[1] == '\0' || (matpos[1] == '.' && matpos[2] == '\0'))) continue; /* ignore . and .. */ if (stat(searchpath, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) continue; /* if not a directory */ if (statbuf.st_nlink > 2) ++len; if (spacenext1 + len > space1->sp + HUNKSIZE) { space1->sp_end = spacenext1; if (space1->next == NULL) { space1->next = xmalloc(sizeof(struct spacenode)); space1->next->next = NULL; } space1 = space1->next; spacenext1 = space1->sp; } if (statbuf.st_nlink > 2) { *spacenext1++ = '/'; --len; } memcpy(spacenext1, entry->d_name, len - 1); spacenext1[len - 1] = '\0'; spacenext1 += len; } closedir(dir); for (;;) { space1->sp_end = spacenext1; if (spacenext == space->sp_end) { if (space == space1) break; space = space->next; spacenext = space->sp; } skip_subdirs = True; if (*spacenext == '/') { ++spacenext; skip_subdirs = False; } len = strlen(spacenext); memcpy(matpos, spacenext, len); matpos[len] = '/'; main_search_proc(matpos + len + 1, sp_pos, sp_slash, sp_end, skip_subdirs, space1, spacenext1); spacenext += len + 1; } } static FILE * search(const char *path, const char *path_var, const char *name) { const char *env_path = NULL; FILE *f; if (path_var != NULL) { if (*name == '/') { strcpy(searchpath, name); return fopen(searchpath, "r"); } env_path = getenv(path_var); } if (env_path == NULL) { env_path = path; path = NULL; } searchname = name; searchnamelen = strlen(name); for (;;) { const char *p; p = strchr(env_path, ':'); if (p == NULL) p = env_path + strlen(env_path); if (p == env_path) { if (path != NULL) { f = search(path, (char *) NULL, name); if (f != NULL) return f; } } else { if (setjmp(found_env)) return searchfile; main_search_proc(searchpath, env_path, find_dbl_slash(env_path, p), p, True, &firstnode, firstnode.sp); } if (*p == '\0') return NULL; env_path = p + 1; } } #endif /* not KPATHSEA */ /* * Reading configuration and map files. */ char *long_line = NULL; /* for reading config and map files */ int long_line_len = 82; /* allocated length of the above */ static Boolean fgets_long(FILE *f) { int len; if (fgets(long_line, long_line_len, f) == NULL) return False; len = 0; for (;;) { len += strlen(long_line + len); if (len > 0 && long_line[len - 1] == '\n') { long_line[--len] = '\0'; break; } if (len < long_line_len - 1) break; long_line_len += 80; long_line = xrealloc(long_line, long_line_len); fgets(long_line + len, long_line_len - len, f); } return True; } static void #ifndef KPATHSEA getdefaults(FILE *f) #else getdefaults(const char *name) #endif { #ifdef KPATHSEA FILE *f; #endif char *p; char c; struct p_list *p_node; #ifdef KPATHSEA p = kpse_find_file(name, kpse_dvips_config_format, false); if (p == NULL) return; f = fopen(p, FOPEN_R_MODE); if (f == NULL) return; #endif while (fgets_long(f)) { p = long_line; while (*p == ' ' || *p == '\t') ++p; c = *p; if (c == '\0') continue; do ++p; while (*p == ' ' || *p == '\t'); switch (c) { case 'H': config_file_header_path = get_one_arg(p); break; case 'p': if (*p == '+') do ++p; while (*p == ' ' || *p == '\t'); else p_tail = &p_head; /* discard old list */ p_node = xmalloc(sizeof *p_node); p_node->value = get_one_arg(p); *p_tail = p_node; p_tail = &p_node->next; break; } } fclose(f); } static Boolean scan_map_file(FILE *f) { while (fgets_long(f)) if (memcmp(long_line, fontname, fontlen) == 0 && (long_line[fontlen] == '\0' || isspace((unsigned char)long_line[fontlen]))) { fclose(f); return True; } fclose(f); return False; } /* * Add to dlstring */ char *dlstring = NULL; unsigned int dls_len = 0; unsigned int dls_max = 0; static void addtodls(const char *s) { int len = strlen(s); if (dls_len + len >= dls_max) { unsigned int newsize = dls_max + 80; if (newsize <= dls_len + len) newsize = dls_len + len + 1; dlstring = xrealloc(dlstring, dls_max = newsize); } strcpy(dlstring + dls_len, s); dls_len += len; } static long getlong(FILE *f) { int value; value = (int) ((byte) getc(f)) << 24; value |= (int) ((byte) getc(f)) << 16; value |= (int) ((byte) getc(f)) << 8; value |= (int) ((byte) getc(f)); return value; } char line[82]; static void expect(const char *waitingfor) { for (;;) { #if !_AMIGA data_gets((byte *) line, sizeof(line)); #else if (fgets(line, sizeof(line), data_file) == NULL) oops("Premature end of file"); #endif if (memcmp(line, waitingfor, strlen(waitingfor)) == 0) return; fputs("gs: ", stdout); for (;;) { fputs(line, stdout); if (*line == '\0' || line[strlen(line) - 1] == '\n') break; #if !_AMIGA data_gets((byte *) line, sizeof(line)); #else if (fgets(line, sizeof(line), data_file) == NULL) oops("Premature end of file"); #endif } } } static void whitespace(void) { char c; for (;;) { c = data_getc(); if (c == '#') do c = data_getc(); while (!data_eof && c != '\n'); else if (!isspace((unsigned char)c)) { data_ungetc(c); break; } } } static int getint(void) { char c; int i = 0; do c = data_getc(); while (isspace((unsigned char)c)); if (c < '0' || c > '9') oops("digit expected"); do { i = i * 10 + (c - '0'); c = data_getc(); } while (c >= '0' && c <= '9'); if (!data_eof) data_ungetc(c); return i; } static byte masks[] = {0, 1, 3, 7, 017, 037, 077, 0177, 0377}; byte flag; int pk_dyn_f; int pk_dyn_g; int base; /* cost of this character if pk_dyn_f = 0 */ int deltas[13]; /* cost of increasing pk_dyn_f from i to i+1 */ /* * Add up statistics for putting out the given shift count */ static void tallyup(int n) { int m; if (n > 208) { ++base; n -= 192; for (m = 0x100; m != 0 && m < n; m <<= 4) base += 2; if (m != 0 && (m = (m - n) / 15) < 13) deltas[m] += 2; } else if (n > 13) ++deltas[(208 - n) / 15]; else --deltas[n - 1]; } /* * Routines for storing the shift counts */ static Boolean odd = False; static byte part; static void pk_put_nyb(int n) { if (odd) { *bitmap_end++ = (part << 4) | n; odd = False; } else { part = n; odd = True; } } static void pk_put_long(int n) { if (n >= 16) { pk_put_nyb(0); pk_put_long(n / 16); } pk_put_nyb(n % 16); } static void pk_put_count(int n) { if (n > pk_dyn_f) { if (n > pk_dyn_g) pk_put_long(n - pk_dyn_g + 15); else { pk_put_nyb(pk_dyn_f + (n - pk_dyn_f + 15) / 16); pk_put_nyb((n - pk_dyn_f - 1) % 16); } } else pk_put_nyb(n); } static void trim_bitmap(void) { byte *p; byte mask; /* clear out garbage bits in bitmap */ if (width % 8 != 0) { mask = ~masks[8 - width % 8]; for (p = bitmap + bytes_wide - 1; p < bitmap_end; p += bytes_wide) *p &= mask; } /* * Find the bounding box of the bitmap. */ /* trim top */ skip = 0; mask = 0; for (;;) { if (bitmap >= bitmap_end) { /* if bitmap is empty */ width = height = hoff = voff = 0; return; } p = bitmap + bytes_wide; while (p > bitmap) mask |= *--p; if (mask) break; ++skip; bitmap += bytes_wide; } height -= skip; voff -= skip; #if DEBUG #if defined(KPATHSEA) && defined (KPSE_DEBUG) if (KPSE_DEBUG_P (GSPK_DEBUG_PK)) #endif if (skip < 2 || skip > 3) printf("Character has %d empty rows at top\n", skip); #endif /* DEBUG */ /* trim bottom */ skip = 0; mask = 0; for (;;) { p = bitmap_end - bytes_wide; while (p < bitmap_end) mask |= *p++; if (mask) break; ++skip; bitmap_end -= bytes_wide; } height -= skip; #if DEBUG #if defined(KPATHSEA) && defined (KPSE_DEBUG) if (KPSE_DEBUG_P (GSPK_DEBUG_PK)) #endif if (skip < 2 || skip > 3) printf("Character has %d empty rows at bottom\n", skip); #endif /* DEBUG */ /* trim right */ skip = 0; --width; for (;;) { mask = 0; for (p = bitmap + width / 8; p < bitmap_end; p += bytes_wide) mask |= *p; if (mask & (0x80 >> (width % 8))) break; --width; ++skip; } ++width; #if DEBUG #if defined(KPATHSEA) && defined (KPSE_DEBUG) if (KPSE_DEBUG_P (GSPK_DEBUG_PK)) #endif if (skip < 2 || skip > 3) printf("Character has %d empty columns at right\n", skip); #endif /* DEBUG */ /* trim left */ skip = 0; for (;;) { mask = 0; for (p = bitmap + skip / 8; p < bitmap_end; p += bytes_wide) mask |= *p; if (mask & (0x80 >> (skip % 8))) break; ++skip; } width -= skip; hoff -= skip; #if DEBUG #if defined(KPATHSEA) && defined (KPSE_DEBUG) if (KPSE_DEBUG_P (GSPK_DEBUG_PK)) #endif if (skip < 2 || skip > 3) printf("Character has %d empty columns at left\n", skip); #endif /* DEBUG */ bitmap += skip / 8; skip = skip % 8; } /* * Pack the bitmap using the rll method. (Return false if it's better * to just pack the bits.) */ static Boolean pk_rll_cvt(void) { static int *counts = NULL; /* area for saving bit counts */ static int maxcounts = 0; /* size of this area */ unsigned int ncounts; /* max to allow this time */ int *nextcount; /* next count value */ int *counts_end; /* pointer to end */ byte *rowptr; byte *p; byte mask; byte *rowdup; /* last row checked for dup */ byte paint_switch; /* 0 or 0xff */ int bits_left; /* bits left in row */ int cost; int i; /* * Allocate space for bit counts. */ ncounts = (width * height + 3) / 4; if (ncounts > maxcounts) { if (counts != NULL) free(counts); counts = xmalloc((ncounts + 2) * sizeof(int)); maxcounts = ncounts; } counts_end = counts + ncounts; /* * Form bit counts and collect statistics */ base = 0; memset(deltas, 0, sizeof(deltas)); rowdup = NULL; /* last row checked for duplicates */ p = rowptr = bitmap; mask = 0x80 >> skip; flag = 0; paint_switch = 0; if (*p & mask) { flag = 8; paint_switch = 0xff; } bits_left = width; nextcount = counts; while (rowptr < bitmap_end) { /* loop over shift counts */ int shift_count = bits_left; for (;;) { if (bits_left == 0) { if ((p = rowptr += bytes_wide) >= bitmap_end) break; mask = 0x80 >> skip; bits_left = width; shift_count += width; } if (((*p ^ paint_switch) & mask) != 0) break; --bits_left; mask >>= 1; if (mask == 0) { ++p; while (*p == paint_switch && bits_left >= 8) { ++p; bits_left -= 8; } mask = 0x80; } } if (nextcount >= counts_end) return False; shift_count -= bits_left; *nextcount++ = shift_count; tallyup(shift_count); /* check for duplicate rows */ if (rowptr != rowdup && bits_left != width) { byte *p1 = rowptr; byte *q = rowptr + bytes_wide; int repeat_count; while (q < bitmap_end && *p1 == *q) ++p1, ++q; repeat_count = (p1 - rowptr) / bytes_wide; if (repeat_count > 0) { *nextcount++ = -repeat_count; if (repeat_count == 1) --base; else { ++base; tallyup(repeat_count); } rowptr += repeat_count * bytes_wide; } rowdup = rowptr; } paint_switch = ~paint_switch; } #if DEBUG #if defined(KPATHSEA) && defined (KPSE_DEBUG) if (KPSE_DEBUG_P (GSPK_DEBUG_BITMAP)) #endif { /* * Dump the bitmap */ for (p = bitmap; p < bitmap_end; p += bytes_wide) { byte *p1 = p; int j; mask = 0x80 >> skip; for (j = 0; j < width; ++j) { putchar(*p1 & mask ? '@' : '.'); if ((mask >>= 1) == 0) mask = 0x80, ++p1; } putchar('\n'); } putchar('\n'); } #endif /* DEBUG */ /* * Determine the best pk_dyn_f */ pk_dyn_f = 0; cost = base += 2 * (nextcount - counts); for (i = 1; i < 14; ++i) { base += deltas[i - 1]; if (base < cost) { pk_dyn_f = i; cost = base; } } /* last chance to bail out */ if (cost * 4 > width * height) return False; /* * Pack the bit counts */ pk_dyn_g = 208 - 15 * pk_dyn_f; flag |= pk_dyn_f << 4; bitmap_end = bitmap; *nextcount = 0; nextcount = counts; while (*nextcount != 0) { if (*nextcount > 0) pk_put_count(*nextcount); else if (*nextcount == -1) pk_put_nyb(15); else { pk_put_nyb(14); pk_put_count(-*nextcount); } ++nextcount; } if (odd) { pk_put_nyb(0); ++cost; } if (cost != 2 * (bitmap_end - bitmap)) printf("Cost miscalculation: expected %d, got %ld\n", cost, 2 * (long) (bitmap_end - bitmap)); pk_len = bitmap_end - bitmap; return True; } static void pk_bm_cvt(void) { byte *rowptr; byte *p; int blib1; /* bits left in byte */ int bits_left; /* bits left in row */ byte *q; int blib2; byte nextbyte; flag = 14 << 4; q = bitmap; blib2 = 8; nextbyte = 0; for (rowptr = bitmap; rowptr < bitmap_end; rowptr += bytes_wide) { p = rowptr; blib1 = 8 - skip; bits_left = width; if (blib2 != 8) { int n; if (blib1 < blib2) { nextbyte |= *p << (blib2 - blib1); n = blib1; } else { nextbyte |= *p >> (blib1 - blib2); n = blib2; } blib2 -= n; if ((bits_left -= n) < 0) { blib2 -= bits_left; continue; } if ((blib1 -= n) == 0) { blib1 = 8; ++p; if (blib2 > 0) { nextbyte |= *p >> (8 - blib2); blib1 -= blib2; bits_left -= blib2; if (bits_left < 0) { blib2 = -bits_left; continue; } } } *q++ = nextbyte; } /* fill up whole (destination) bytes */ while (bits_left >= 8) { nextbyte = *p++ << (8 - blib1); *q++ = nextbyte | (*p >> blib1); bits_left -= 8; } /* now do the remainder */ nextbyte = *p << (8 - blib1); if (bits_left > blib1) nextbyte |= p[1] >> blib1; blib2 = 8 - bits_left; } if (blib2 != 8) *q++ = nextbyte; pk_len = q - bitmap; } static void putshort(int w) { putc(w >> 8, pk_file); putc(w, pk_file); } static void putmed(long w) { putc(w >> 16, pk_file); putc(w >> 8, pk_file); putc(w, pk_file); } static void putlong(long w) { putc(w >> 24, pk_file); putc(w >> 16, pk_file); putc(w >> 8, pk_file); putc(w, pk_file); } static void putglyph(int cc) { static Boolean have_first_line = False; static int llx, lly, urx, ury; static float char_width; static byte *area1 = NULL; static unsigned int size1 = 0; static int i; long dm; long tfm_wid; byte *p; if (!quiet) { int wid; static const char *s = ""; wid = (cc >= 100) + (cc >= 10) + 4; if (col + wid > 80) { s = "\n"; col = 0; } printf("%s[%d", s, cc); fflush(stdout); col += wid; s = " "; } if (!have_first_line) { expect("#^"); if (sscanf(line, "#^ %d %d %d %d %d %f\n", &i, &llx, &lly, &urx, &ury, &char_width) != 6) oops("Cannot scanf first line"); } if (i < cc) oops("Character %d received, %d expected", i, cc); if (i > cc) { fprintf(stderr, "Character %d is missing.\n", cc); have_first_line = True; return; } have_first_line = False; hoff = -llx + 2; voff = ury + 2 - 1; expect("P4\n"); whitespace(); width = getint(); whitespace(); height = getint(); (void) data_getc(); if (width != urx - llx + 4 || height != ury - lly + 4) oops("Dimensions do not match: %d %d %d %d %d %d", llx, lly, urx, ury, width, height); bytes_wide = (width + 7) / 8; bm_size = bytes_wide * height; if (size1 < bm_size) { if (area1 != NULL) free(area1); area1 = xmalloc(bm_size); size1 = bm_size; } for (p = area1 + (height - 1) * bytes_wide; p >= area1; p -= bytes_wide) if (data_read(p, bytes_wide) != bytes_wide) oops("Cannot read bitmap of size %u", bm_size); bitmap = area1; bitmap_end = bitmap + bm_size; trim_bitmap(); if (height == 0 || !pk_rll_cvt()) pk_bm_cvt(); tfm_wid = tfm_widths[width_index[cc]]; dm = (long) (char_width + 0.5) - (char_width < -0.5); if (pk_len + 8 < 4 * 256 && tfm_wid < (1<<24) && dm >= 0 && dm < 256 && width < 256 && height < 256 && hoff >= -128 && hoff < 128 && voff >= -128 && voff < 128) { putc(flag | ((pk_len + 8) >> 8), pk_file); putc(pk_len + 8, pk_file); putc(cc, pk_file); putmed(tfm_wid); putc(dm, pk_file); putc(width, pk_file); putc(height, pk_file); putc(hoff, pk_file); putc(voff, pk_file); } else if (pk_len + 13 < 3 * 65536L && tfm_wid < (1<<24) && dm >= 0 && dm < 65536L && width < 65536L && height < 65536L && hoff >= -65536L && hoff < 65536L && voff >= -65536L && voff < 65536L) { putc(flag | 4 | ((pk_len + 13) >> 16), pk_file); putshort(pk_len + 13); putc(cc, pk_file); putmed(tfm_wid); putshort(dm); putshort(width); putshort(height); putshort(hoff); putshort(voff); } else { putc(flag | 7, pk_file); putlong(pk_len + 28); putlong(cc); putlong(tfm_wid); putlong((long) (char_width * 65536.0 + 0.5) - (char_width < -0.5)); putlong(0); putlong(width); putlong(height); putlong(hoff); putlong(voff); } fwrite(bitmap, 1, pk_len, pk_file); if (!quiet) { putchar(']'); fflush(stdout); } } static void putspecl(const char *str1, const char *str2) { int len1 = strlen(str1); int len2 = 0; if (str2 != NULL) len2 = strlen(str2); if (len1 + len2 > 255) return; putc(PK_SPC, pk_file); putc(len1 + len2, pk_file); fwrite(str1, 1, len1, pk_file); if (len2 != 0) fwrite(str2, 1, len2, pk_file); } int main(int argc, char **argv) { FILE *config_file; FILE *render_ps; FILE *tfm_file; char **argp; float dpi; const char *dvipsrc; char *p; char *PSname = NULL; char *specinf = NULL; char *specp = NULL; /* NULL pacifies lint */ char charlist[10*2 + 90*3 + 156*4 + 1]; char designstr[20]; char dpistr[20]; #if HAVE_SIGACTION struct sigaction sigact; #endif #if _AMIGA char fngs[50]; char fngsf[50]; char tfm_path[256]; BPTR in, out; #else char *substarg; #endif #if WIN32 DWORD idGsThread; SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; #else /* not WIN32 */ #if !_AMIGA int std_out[2]; #endif int std_in[2]; #endif /* not WIN32 */ int cc; int ppp; int i; argp = argv; while (++argp < argv + argc && (*argp)[0] == '-') { const struct option *opt_ptr; const struct option *opt; char *arg = *argp + 1; if (*arg == '\0') --arg; /* this will flag an error later */ if (*arg != '-') { /* if short argument */ opt = options; for (;;) { if (*arg == opt->shortname) break; if (++opt >= options + NUMBER(options)) opt_oops("invalid option -- %c", *arg); } if (opt->has_arg) { ++arg; if (*arg == '\0') { if (++argp >= argv + argc) opt_oops("option requires an argument -- %c", arg[-1]); arg = *argp; } } else { if (arg[1] != '\0') opt_oops("invalid number of bytes in option `%s'", arg - 1); } } else { /* long argument */ int len; char *arg1; ++arg; if (*arg == '\0') { /* if -- */ ++argp; break; } len = strlen(arg); arg1 = memchr(arg, '=', len); if (arg1 != NULL) { len = arg1 - arg; ++arg1; } opt = NULL; for (opt_ptr = options; opt_ptr < options + NUMBER(options); ++opt_ptr) if (memcmp(arg, opt_ptr->longname, len) == 0) { if (opt != NULL) opt_oops("option `%s' is ambiguous.", arg - 2); opt = opt_ptr; } if (opt == NULL) opt_oops("unrecognized option `%s'", arg - 2); if (opt->has_arg) { if (arg1 == NULL) { if (++argp >= argv + argc) opt_oops("option `--%s' requires an argument.", opt->longname); arg1 = *argp; } } else { if (arg1 != NULL) opt_oops("option `--%s' doesn't allow an argument.", opt->longname); } arg = arg1; } /* end long argument */ if (opt->addr != NULL) { if (opt->has_arg) *((char **) opt->addr) = arg; else *((Boolean *) opt->addr) = opt->value; } switch (opt->shortname) { #if defined(KPATHSEA) && defined (KPSE_DEBUG) case OPT_DBG: kpathsea_debug |= atoi(arg); break; #endif case 'h': #ifndef KPATHSEA puts("\ Usage: gsftopk [OPTION] FONT DPI\n\ Translate the PostScript Type 1 font FONT to PK bitmap format at DPI dpi.\n\ \n\ -t, --test check for presence of font in .map file.\n\ --mapline=LINE use LINE as the line from the .map file.\n\ --mapfile=FILE use FILE as a .map file; default psfonts.map.\n\ -i GS, --interpreter=GS use GS as Ghostscript interpreter.\n\ --dosnames short pk filename (cmr10.pk instead of cmr10.600pk).\n\ -q, --quiet don't print progress information to standard output.\n\ -h, --help print this message and exit.\n\ -v, --version print version number and exit.\n"); #else puts("\ Usage: gsftopk [OPTION] FONT DPI\n\ Translate the PostScript Type 1 font FONT to PK bitmap format at DPI dpi.\n\ \n\ -t, --test check for presence of font in .map file.\n\ --mapline=LINE use LINE as the line from the .map file.\n\ --mapfile=FILE use FILE as a .map file; default psfonts.map.\n\ -i GS, --interpreter=GS use GS as Ghostscript interpreter.\n\ --dosnames short pk filename (cmr10.pk instead of cmr10.600pk).\n\ -q, --quiet don't print progress information to standard output.\n\ --debug=NUM set debugging flags.\n\ -h, --help print this message and exit.\n\ -v, --version print version number and exit.\n"); #endif return 0; case 'v': #ifndef KPATHSEA printf("gsftopk %s\n", version); #else { printf("gsftopk(k) %s\n", version); puts(kpathsea_version_string); puts("Copyright (C) 1993-1998 Paul Vojta.\n\ There is NO warranty. You may redistribute this software\n\ under the terms of the GNU General Public License\n\ and the standard X consortium copyright notice.\n\ For more information about these matters, see the files\n\ named COPYING and gsftopk.c.\n\ Author of gsftopk: Paul Vojta."); } #endif return 0; } } if (mapfile != NULL && mapline != NULL) opt_oops("cannot specify both `--mapline' and `--mapfile'"); if (argp >= argv + argc) opt_oops(test ? "must provide a font name" : "must provide a font name and resolution"); fontname = *argp++; fontlen = strlen(fontname); if (argp >= argv + argc) { if (!test) opt_oops("must provide rendering resolution"); dpi = 0.0; } else { dpi = atof(*argp++); if (dpi <= 0.0) opt_oops("DPI argument `%s' must be a positive number", *argp); } if (argp < argv + argc) opt_oops("no more than two arguments are allowed"); #ifdef KPATHSEA #ifdef WIN32 setmode(fileno(stdout), _O_BINARY); #endif kpse_set_program_name(argv[0], "gsftopk"); kpse_init_prog("GSFTOPK", (int) (dpi + 0.5), NULL, "cmr10"); if (!test) xputenv_int("KPATHSEA_DPI", (int) (dpi + 0.5)); #if defined(WIN32) /* TeXLive uses its own Ghostscript */ #if !defined(_WIN64) texlive_gs_init(); #endif #endif #endif #if _AMIGA /* [CL] 21-Jun-97 This is quite silly but it really helps things when determining the font supplier and family. */ putenv("GSFTOPKTFM="); #endif /* * Read the dvips-style config file(s). */ long_line = xmalloc(long_line_len); /* initialize fgets_long */ #ifndef KPATHSEA config_file = search(CONFIGPATH, "TEXCONFIG", "config.ps"); if (config_file != NULL) getdefaults(config_file); dvipsrc = getenv("DVIPSRC"); if (dvipsrc == NULL) { dvipsrc = getenv("HOME"); if (dvipsrc != NULL) { i = strlen(dvipsrc); p = xmalloc(i + 10); memcpy(p, dvipsrc, i); memcpy(p + i, "/.dvipsrc", 10); } } if (dvipsrc != NULL) { config_file = fopen(dvipsrc, "r"); if (config_file != NULL) getdefaults(config_file); } config_file = search(CONFIGPATH, "TEXCONFIG", "config.gsftopk"); if (config_file != NULL) getdefaults(config_file); #else getdefaults("config.ps"); dvipsrc = kpse_var_value("DVIPSRC"); getdefaults(dvipsrc != NULL ? dvipsrc : "$HOME/.dvipsrc"); getdefaults("config.gsftopk"); /* Set HEADERPATH from config file. */ if (config_file_header_path != NULL) kpse_format_info[kpse_tex_ps_header_format].client_path = config_file_header_path; #endif /* * Get the map line. */ if (mapline != NULL) { if (memcmp(mapline, fontname, fontlen) != 0 || (mapline[fontlen] != '\0' && !isspace((unsigned char)mapline[fontlen]))) oops("font name does not match --mapline argument"); } else { Boolean font_found; if (mapfile != NULL) { #ifndef KPATHSEA config_file = search(CONFIGPATH, "TEXCONFIG", mapfile); #else config_file = kpse_open_file(mapfile, kpse_fontmap_format); #endif if (config_file == NULL) { perror(mapfile); exit(1); } font_found = scan_map_file(config_file); } else { struct p_list *p_node; font_found = False; *p_tail = NULL; for (p_node = p_head; p_node != NULL; p_node = p_node->next) { #ifndef KPATHSEA config_file = search(CONFIGPATH, "TEXCONFIG", p_node->value); #else config_file = kpse_open_file(p_node->value, kpse_fontmap_format); #endif if (config_file != NULL) if (scan_map_file(config_file)) { font_found = True; break; } } } if (!font_found) { if (test) exit(1); else oops("Cannot find font %s in map file(s).", fontname); } mapline = long_line; } if (test) exit(0); if (!quiet) { printf("%sversion %s", progname, version); fflush(stdout); col = 1; /* any nonzero value will do */ } /* * Parse the line from the map file. */ for (p = mapline + fontlen; *p != '\0'; ++p) { if (isspace((unsigned char)*p)) continue; if (*p == '<') { char *q; char endc; char c; FILE *f; ++p; /* There may be two '<'s in front of the filename. */ if (*p == '<') ++p; /* ... and maybe a '[' */ if (*p == '[') ++p; q = p; while (*p != '\0' && !isspace((unsigned char)*p)) ++p; endc = *p; *p = '\0'; #ifdef KPATHSEA searchpath = kpse_find_file(q, kpse_tex_ps_header_format,true); f = searchpath ? fopen(searchpath, FOPEN_R_MODE) : NULL; #else f = search(config_file_header_path, "DVIPSHEADERS", q); #endif if (f == NULL) oops("Cannot find font file %s", q); /* search() also sets searchpath */ addtodls(" ("); addtodls(searchpath); c = getc(f); addtodls(c == '\0' ? ") ttload" : c == '\200' ? ") brun" : ") run"); fclose(f); if (endc == '\0') break; continue; } else if (*p == '"') { char *q; if (specinf != NULL) *specp++ = ' '; else specinf = specp = xmalloc(strlen(p)); ++p; q = strchr(p, '"'); if (q == NULL) q = p + strlen(p); memcpy(specp, p, q - p); specp += q - p; p = q; if (*p == '\0') break; else continue; } else { PSname = p; while (*p != '\0' && !isspace((unsigned char)*p)) ++p; if (*p == '\0') break; } *p = '\0'; } #if OLD_DVIPS /* Parse lines like `Symbol-Slanted "/Symbol .167 SlantFont"'. */ if (*(p = specinf) == '/') { PSname = ++p; while (*p && !isspace(*p)) ++p; if (*p) *p++ = '\0'; specinf = p; } #endif /* OLD_DVIPS */ if (specinf != NULL) { *specp = '\0'; specinfo = specinf; } if (PSname == NULL) PSname = fontname; /* * Start up Ghostscript. */ #ifdef KPATHSEA searchpath = kpse_find_file("render.ps", kpse_tex_ps_header_format, true); render_ps = searchpath ? fopen (searchpath, FOPEN_R_MODE) : NULL; #else render_ps = search(config_file_header_path, "DVIPSHEADERS", "render.ps"); #endif if (render_ps == NULL) oops("Cannot find PS driver file \"render.ps\"."); fclose(render_ps); #if !_AMIGA substarg = xmalloc(strlen(PSname) + 13); sprintf(substarg, "-sSUBSTFONT=%s", PSname); #endif sprintf(dpistr, "%f", dpi); #if _AMIGA mktemp(tmpname); sprintf(fngs, "fifo:%s/rweK", tmpname); sprintf(fngsf, "fifo:%s/rweKm", tmpname); std_in[1] = open(fngsf, O_WRONLY|O_TRUNC|O_CREAT, 511); if (std_in[1] == -1) { perror("pipe"); return 1; } in = Open(fngs, MODE_OLDFILE); out = Open(fngs, MODE_NEWFILE); if (in && out) { int error; char *cmd; char formatstr[] = "%s -dNODISPLAY -dNOGC -dNOSAFER -sSUBSTFONT=\"%s\" -q -- \"%s\" \"%s\" \"%s\" \"%s\" \"%s\""; unsigned int len; len = sizeof formatstr + strlen(gspath) + strlen(searchpath) + 2 * strlen(PSname) + (dlstring != NULL ? strlen(dlstring) : 0) + strlen(specinfo) + strlen(dpistr); cmd = xmalloc(len); sprintf(cmd, formatstr, PSname, gspath, searchpath, PSname, dlstring != NULL ? dlstring : "", specinfo, dpistr); error = SystemTags(cmd, SYS_Input, in, SYS_Output, out, SYS_Asynch, TRUE, /* NP_StackSize, 50000, */ TAG_DONE, TAG_DONE); free(cmd); if (error == -1) { Close(in); Close(out); PrintFault(error, "System(gs)"); exit(1); } } else { perror("pipes"); if (in) Close(in); if (out) Close(out); exit(1); } #elif WIN32 /* (later) */ #else /* neither _AMIGA nor WIN32 */ if (pipe(std_in) != 0 || pipe(std_out) != 0) { perror("pipe"); return 1; } /* Catch the signal for death of the child process. */ #if HAVE_SIGACTION sigact.sa_handler = handle_sigchild; (void) sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_NOCLDSTOP; (void) sigaction(SIGCHLD, &sigact, (struct sigaction *) NULL); #else (void) signal(SIGCHLD, handle_sigchild); #endif /* * Also catch various termination signals, so that we can kill the * gs process before terminating. */ (void) signal(SIGHUP, handle_sigterm); (void) signal(SIGINT, handle_sigterm); (void) signal(SIGQUIT, handle_sigterm); (void) signal(SIGTERM, handle_sigterm); fflush(stderr); /* to avoid double flushing */ gs_pid = vfork(); if (gs_pid == 0) { close(std_in[1]); dup2(std_in[0], 0); close(std_in[0]); close(std_out[0]); dup2(std_out[1], 1); close(std_out[1]); execlp(gspath, "gs", "-dNODISPLAY", "-dNOGC", "-dNOSAFER", substarg, "-q", "--", /* render.ps */ searchpath, PSname, dlstring != NULL ? dlstring : "", specinfo, dpistr, NULL); if (col != 0) { putc('\n', stderr); col = 0; } perror(gspath); exit(1); } if (gs_pid == -1) { perror("fork"); exit(1); } #endif /* neither _AMIGA nor WIN32 */ /* * Open and read the tfm file. If this takes a while, at least * it can overlap with the startup of Ghostscript. */ xfilename = xmalloc(fontlen + 10); strcpy(xfilename, fontname); #ifdef KPATHSEA #if _AMIGA strcpy(tfm_path, kpse_find_file(xfilename, kpse_tfm_format, true)); tfm_file = fopen(tfm_path, "r"); #else tfm_file = kpse_open_file(xfilename, kpse_tfm_format); #endif /* _AMIGA */ #else /* not KPATHSEA */ strcpy(xfilename + fontlen, ".tfm"); tfm_file = search(TFMPATH, "TEXFONTS", xfilename); #endif /* not KPATHSEA */ if (tfm_file == NULL) oops("Cannot find tfm file."); for (i = 0; i < 12; ++i) { int j; j = (int) ((byte) getc(tfm_file)) << 8; tfm_lengths[i] = j | (int) ((byte) getc(tfm_file)); } checksum = getlong(tfm_file); design = getlong(tfm_file); fseek(tfm_file, 4 * (lh + 6), 0); p = charlist; for (cc = bc; cc <= ec; ++cc) { width_index[cc] = (byte) getc(tfm_file); if (width_index[cc] != 0) { sprintf(p, "%d ", cc); p += strlen(p); } (void) getc(tfm_file); (void) getc(tfm_file); (void) getc(tfm_file); } for (i = 0; i < nw; ++i) tfm_widths[i] = getlong(tfm_file); fclose(tfm_file); p[-1] = '\n'; sprintf(designstr, "%f\n", (float) design / (1 << 20)); #if !WIN32 /* write the design size and character list to the file */ write(std_in[1], designstr, strlen(designstr)); write(std_in[1], charlist, p - charlist); close(std_in[1]); #else /* WIN32 */ SetConsoleCtrlHandler(handle_sigterm, TRUE); hGsDataIn = CreateEvent(&sa, FALSE, FALSE, "gsDataIn"); hGsDataOut = CreateEvent(&sa, FALSE, FALSE, "gsDataOut"); gs_argv[4] = substarg; gs_argv[7] = searchpath; gs_argv[8] = PSname; gs_argv[9] = dlstring != NULL ? dlstring : ""; gs_argv[10] = specinfo; gs_argv[11] = dpistr; buffer_stdin = concat(designstr, charlist); if ((hGsThread = CreateThread(&sa, /* security attributes */ 0, /* default stack size */ Win32GsSendData, /* start address of thread */ 0, /* parameter */ 0, /* creation flags */ &idGsThread /* thread id */ )) == NULL) Win32Error("CreateThread"); #endif /* WIN32 */ /* * Read the output from Ghostscript. */ #if _AMIGA if ((data_file = fopen(fngsf, "r")) == NULL) { perror("GS_out"); exit(1); } #elif WIN32 /* Nothing */ #else /* neither _AMIGA nor WIN32 */ data_fd = std_out[0]; /* Set data_fd for non-blocking I/O */ if (fcntl(data_fd, F_SETFL, fcntl(data_fd, F_GETFL, 0) | O_NONBLOCK) == -1) perror("fcntl"); #endif /* neither _AMIGA nor WIN32 */ /* * Create pk file and write preamble. */ if (dosnames) strcpy(xfilename + fontlen, ".pk"); else sprintf(xfilename + fontlen, ".%dpk", (int) (dpi + 0.5)); if ((pk_file = fopen(xfilename, FOPEN_WBIN_MODE)) == NULL) { perror(xfilename); exit_toto_too(); } putc(PK_PRE, pk_file); putc(PK_ID, pk_file); expect("V"); /* get GS version */ i = strlen(line) - 2; if (i < 0 || i > 10) i = 0; line[1] = '/'; if (!quiet) { if (i > 0) fwrite(line + 1, 1, i, stdout); putchar('\n'); col = 0; } putc(sizeof(progname) + sizeof(version) + i - 2, pk_file); fwrite(progname, 1, sizeof(progname) - 1, pk_file); fwrite(version, 1, sizeof(version) - 1, pk_file); if (i >= 0) { fwrite(line + 1, 1, i, pk_file); } putlong(design); putlong(checksum); ppp = dpi / 72.27 * 65536.0 + 0.5; putlong(ppp); /* hppp */ putlong(ppp); /* vppp */ /* * Write the actual characters. */ for (cc = bc; cc <= ec; ++cc) if (width_index[cc] != 0) putglyph(cc); #if _AMIGA fclose(data_file); #elif WIN32 /* Release data buffer, enable thread to terminate */ SetEvent(hGsDataOut); #ifdef DEBUG fprintf(stderr, "Waiting for thread ... \n"); #endif if (hGsThread) { switch (WaitForSingleObject(hGsThread, 2000)) { case WAIT_OBJECT_0: CloseHandle(hGsThread); hGsThread = NULL; break; case WAIT_TIMEOUT: fprintf(stderr, "Timeout waiting for Gs thread.\n"); break; case WAIT_FAILED: fprintf(stderr, "WaitForSingleObject failed on Gs thread (Error code %d).\n", (int)GetLastError()); break; default: break; } } if (hGsThread) { if (TerminateThread(hGsThread, 1) == 0) { fprintf(stderr, "... couldn't terminate gs thread\n"); } CloseHandle(hGsThread); /* FIXME : is it right to call this ? */ gs_dll_release(); } if (hGsDataIn) CloseHandle(hGsDataIn); if (hGsDataOut) CloseHandle(hGsDataOut); #else /* neither _AMIGA nor WIN32 */ close(data_fd); if (!gs_is_done) wait_for_gs(); #endif /* neither _AMIGA nor WIN32 */ /* * Write out identifying specials: * jobname=(font) * mag=1 * mode=modeless * pixels_per_inch=(dpi) */ putspecl("jobname=", fontname); putspecl("mag=1", NULL); putspecl("mode=modeless", NULL); sprintf(dpistr, "%d", (int) dpi); putspecl("pixels_per_inch=", dpistr); /* * Postamble */ putc(PK_POST, pk_file); while (ftell(pk_file) % 4 != 0) putc(PK_NOP, pk_file); fclose(pk_file); if (!quiet) putchar('\n'); col = 0; #if _AMIGA /* [CL] 21-Jun-97 The same silly thing to indicate the path of the tfm file */ { char tmpstr[80]; sprintf(tmpstr, "GSFTOPKTFM=%s", tfm_path); putenv(tmpstr); } #endif return 0; }