diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
commit | ad547a6b5986815fda458221149728d9d9ab1d87 (patch) | |
tree | 16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/gsftopk/gsftopk.c | |
parent | 947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff) |
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/gsftopk/gsftopk.c')
-rw-r--r-- | Build/source/texk/gsftopk/gsftopk.c | 2843 |
1 files changed, 2843 insertions, 0 deletions
diff --git a/Build/source/texk/gsftopk/gsftopk.c b/Build/source/texk/gsftopk/gsftopk.c new file mode 100644 index 00000000000..3cf84b31b25 --- /dev/null +++ b/Build/source/texk/gsftopk/gsftopk.c @@ -0,0 +1,2843 @@ +/*========================================================================*\ + +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 <stdlib.h> +# include <string.h> +#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 <unistd.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <memory.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <setjmp.h> +#include <signal.h> +#include <fcntl.h> + +#include <errno.h> +#ifndef errno +extern int errno; +#endif + +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#ifndef atof +double atof(); +#endif +char *getenv(); + +/* <sys/types.h> is already included. */ +#if HAVE_SYS_WAIT_H +#include <sys/wait.h> +#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 <kpathsea/config.h> +#include <kpathsea/c-errno.h> +#include <kpathsea/c-ctype.h> +#include <kpathsea/c-fopen.h> +#include <kpathsea/c-pathmx.h> +#include <kpathsea/proginit.h> +#include <kpathsea/tex-file.h> +#include <kpathsea/tex-make.h> +#include <kpathsea/variable.h> +#include <kpathsea/version.h> +#include <c-auto.h> +#include <signal.h> +#include <fcntl.h> +#include <setjmp.h> + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# 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) + +/* <sys/types.h> is already included: <kpathsea/config.h> + --> <kpathsea/c-std.h> --> <kpathsea/c-unistd.h> + --> <kpathsea/systypes.h> --> <sys/types.h>. */ +#if HAVE_SYS_WAIT_H +# include <sys/wait.h> +#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 <windows.h> + +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 <poll.h> +#else +# if HAVE_SYS_SELECT_H +# include <sys/select.h> +# else +# if HAVE_SELECT_H +# include <select.h> +# endif +# endif +#endif + +#if HAVE_VFORK_H +#include <vfork.h> +#endif + +#if _AMIGA +#include <proto/dos.h> +#include <dos/dostags.h> +#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 <stdarg.h> + +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; +} |