/*========================================================================*\

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 */


/* These macros munge function declarations to make them work in both
   cases.  The P?H macros are used for declarations, the P?C for
   definitions.  Cf. <ansidecl.h> from the GNU C library.  P1H(void)
   also works for definitions of routines which take no args.  */

#if __STDC__

#define	AA(args) args /* For an arbitrary number; ARGS must be in parens.  */

#define	P1H(p1) (p1)

#define	P1C(t1,n1)(t1 n1)
#define	P2C(t1,n1, t2,n2)(t1 n1, t2 n2)
#define	P3C(t1,n1, t2,n2, t3,n3)(t1 n1, t2 n2, t3 n3)
#define	P4C(t1,n1, t2,n2, t3,n3, t4,n4)(t1 n1, t2 n2, t3 n3, t4 n4)
#define	P5C(t1,n1, t2,n2, t3,n3, t4,n4, t5,n5) \
  (t1 n1, t2 n2, t3 n3, t4 n4, t5 n5)
#define	P6C(t1,n1, t2,n2, t3,n3, t4,n4, t5,n5, t6,n6) \
  (t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6)
#define	P7C(t1,n1, t2,n2, t3,n3, t4,n4, t5,n5, t6,n6, t7,n7) \
  (t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6, t7 n7)

#else /* not __STDC__ */

#define	AA(args) ()

#define	P1H(p1) ()

#define	P1C(t1,n1) (n1) t1 n1;
#define	P2C(t1,n1, t2,n2) (n1,n2) t1 n1; t2 n2;
#define	P3C(t1,n1, t2,n2, t3,n3) (n1,n2,n3) t1 n1; t2 n2; t3 n3;
#define	P4C(t1,n1, t2,n2, t3,n3, t4,n4) (n1,n2,n3,n4) \
  t1 n1; t2 n2; t3 n3; t4 n4;
#define	P5C(t1,n1, t2,n2, t3,n3, t4,n4, t5,n5) (n1,n2,n3,n4,n5) \
  t1 n1; t2 n2; t3 n3; t4 n4; t5 n5;
#define	P6C(t1,n1, t2,n2, t3,n3, t4,n4, t5,n5, t6,n6) (n1,n2,n3,n4,n5,n6) \
  t1 n1; t2 n2; t3 n3; t4 n4; t5 n5; t6 n6;
#define	P7C(t1,n1, t2,n2, t3,n3, t4,n4, t5,n5, t6,n6, t7,n7) \
  (n1,n2,n3,n4,n5,n6,n7) \
  t1 n1; t2 n2; t3 n3; t4 n4; t5 n5; t6 n6; t7 n7;
#define	const	/* nothing */

#endif /* not __STDC__ */

#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 <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

#include <win32lib.h>
#include <gs32lib.h>
#include <gsdll.h>
HANDLE hGsThread = NULL;
HANDLE hGsDataIn = 0, hGsDataOut = 0; /* Events to synchronize threads */
/* Arguments to gs dll */
char *gs_argv[] = { "gswin32c.exe",		/* 0, */
		    "-dNOGC",			/* 1, */
		    "-dNODISPLAY",		/* 2, */
		    NULL,			/* 3, substarg */
		    "-q",			/* 4, */
		    "--",			/* 5, */
		    NULL,			/* 6, searchpath */
		    NULL,			/* 7, PSname */
		    NULL,	       /* 8, dlstring != NULL ? dlstring : "" */
		    NULL,			/* 9, specinfo */
		    NULL,			/* 10, dpistr */
		    NULL			/* 11, NULL terminator */
  };
int gs_argc = 11;

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

#if __GNUC__
#define	NORETURN	volatile
#else
#define	NORETURN	/* nothing */
#endif

#if STDC_HEADERS && __STDC__
#define	NeedVarargsPrototypes	1
#include <stdarg.h>
#else /* no prototypes */
#define	NeedVarargsPrototypes	0
#include <varargs.h>
#endif /* no prototypes */

typedef	char	Boolean;
#define	True	1
#define	False	0

#ifndef NeedWidePrototypes
#if __STDC__
#define	NeedWidePrototypes	1
#else
#define	NeedWidePrototypes	0
#endif
#endif /* NeedWidePrototypes */

#if NeedWidePrototypes
typedef	int		wide_bool;
#else
typedef	Boolean		wide_bool;
#endif

#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;
const char	*mapfile	= NULL;
const 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},
#ifdef KPATHSEA
		{"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.
 */

NORETURN void
exit_toto_too P1H(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",
		    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.
 */

#if NeedVarargsPrototypes
NORETURN void
oops(const char *message, ...)
#else
/* VARARGS */
NORETURN void
oops(va_alist)
	va_dcl
#endif
{
#if !NeedVarargsPrototypes
	const char *message;
#endif
	va_list	args;

#if NeedVarargsPrototypes
	va_start(args, message);
#else
	va_start(args);
	message = va_arg(args, const char *);
#endif
	if (col != 0) putchar('\n');
	vfprintf(stderr, message, args);
	va_end(args);
	putc('\n', stderr);
	exit_toto_too();
}


/*
 *	Same as oops, but with arguments.
 */
#if NeedVarargsPrototypes
NORETURN void
opt_oops(const char *message, ...)
#else
/* VARARGS */
NORETURN void
opt_oops(va_alist)
	va_dcl
#endif
{
#if !NeedVarargsPrototypes
	const char *message;
#endif
	va_list	args;

#if NeedVarargsPrototypes
	va_start(args, message);
#else
	va_start(args);
	message = va_arg(args, const char *);
#endif
	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 P1C(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 P2C(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).
 */

char *
get_one_arg P1C(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.
 */

static	Boolean	got_sigchld	= False;

#if WIN32

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");
		exit_toto_too();
		return FALSE;
	    default:
		fprintf(stderr, "... not exiting\n");
		return TRUE;
        }
}

#else /* not WIN32 */

/* ARGSUSED */
static RETSIGTYPE
handle_sigchild P1C(int, signo)
{
	got_sigchld = True;
}

/* ARGSUSED */
static RETSIGTYPE
handle_sigterm P1C(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.  */
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;

    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:
#if 0
    fprintf(stderr, "GS: Poll sent (%d)!\n", 0);
#endif
    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.
*/
DWORD WINAPI Win32GsSendData(LPVOID lpParam)
{
  int ret;

  gs_dll_initialize();

  ret = (*pgsdll_init)(gsdll_callback,
		       NULL,
		       gs_argc,
		       gs_argv);

  switch (ret) {
  case 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); */
    break;
  case GSDLL_INIT_QUIT:
    break;
  default:
    (*pgsdll_exit)();
    break;
  }
#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 P1H(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 P1H(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 P1H(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 P1H(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 P1C(byte, c)
{
	if (data_out <= buffer)
	    oops("Too many calls to data_ungetc()");

	*--data_out = c;
}

static int
data_read P2C(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 P2C(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 P2C(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 P7C(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 P3C(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 */

Boolean
fgets_long P1C(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;
}


void
#ifndef KPATHSEA
getdefaults P1C(FILE *, f)
#else
getdefaults P1C(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);
}


Boolean
scan_map_file P1C(FILE *, f)
{
	while (fgets_long(f))
	    if (memcmp(long_line, fontname, fontlen) == 0
	      && (long_line[fontlen] == '\0' || isspace(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;

void
addtodls P1C(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;
}



long
getlong P1C(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];

void
expect P1C(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
	    }
	}
}

void
whitespace P1H(void)
{
	char	c;

	for (;;) {
	    c = data_getc();
	    if (c == '#')
		do c = data_getc(); while (!data_eof && c != '\n');
	    else if (!isspace(c)) {
		data_ungetc(c);
		break;
	    }
	}
}

int
getint P1H(void)
{
	char	c;
	int	i	= 0;

	do c = data_getc(); while (isspace(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 P1C(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 P1C(int, n)
{
	if (odd) {
	    *bitmap_end++ = (part << 4) | n;
	    odd = False;
	}
	else {
	    part = n;
	    odd = True;
	}
}

static void
pk_put_long P1C(int, n)
{
	if (n >= 16) {
	    pk_put_nyb(0);
	    pk_put_long(n / 16);
	}
	pk_put_nyb(n % 16);
}

static void
pk_put_count P1C(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 P1H(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
#ifdef KPATHSEA
     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
#ifdef KPATHSEA
     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
#ifdef KPATHSEA
     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
#ifdef KPATHSEA
     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 P1H(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;
	bzero(deltas, 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
#ifdef KPATHSEA
	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 %d\n", cost,
		2 * (bitmap_end - bitmap));
	pk_len = bitmap_end - bitmap;
	return True;
}

static void
pk_bm_cvt P1H(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 P1C(short, w)
{
	putc(w >> 8, pk_file);
	putc(w, pk_file);
}

static void
putmed P1C(long, w)
{
	putc(w >> 16, pk_file);
	putc(w >> 8, pk_file);
	putc(w, pk_file);
}

static void
putlong P1C(long, w)
{
	putc(w >> 24, pk_file);
	putc(w >> 16, pk_file);
	putc(w >> 8, pk_file);
	putc(w, pk_file);
}

static void
putglyph P1C(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 P2C(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 P2C(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;
#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) {
#ifdef KPATHSEA
	    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
		{
		    extern KPSEDLL char *kpathsea_version_string;

		    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
	kpse_set_progname(argv[0]);
	kpse_init_prog("GSFTOPK", (int) (dpi + 0.5), NULL, "cmr10");
	if (!test)
	    xputenv_int("KPATHSEA_DPI", (int) (dpi + 0.5));
#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(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(*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(*p)) ++p;
		endc = *p;
		*p = '\0';
#ifdef KPATHSEA
		searchpath = kpse_find_file(q, kpse_tex_ps_header_format,true);
#ifdef WIN32
		/* Be safe for Ghostscript's sake */
		dostounix_filename(searchpath);
#endif /* WIN32 */
		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(*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 -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", 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 */

	if (gs_locate() == NULL) {
	  fprintf(stderr, "\nCan't locate Ghostscript ! Exiting ...\n");
	  return EXIT_FAILURE;
	}

	SetConsoleCtrlHandler(handle_sigterm, TRUE);

	hGsDataIn = CreateEvent(NULL, FALSE, FALSE, "gsDataIn");
	hGsDataOut = CreateEvent(NULL, FALSE, FALSE, "gsDataOut");

	gs_argv[3] = substarg;
	gs_argv[6] = searchpath;
	gs_argv[7] = PSname;
	gs_argv[8] = dlstring != NULL ? dlstring : "";
	gs_argv[9] = specinfo;
	gs_argv[10] = dpistr;

	buffer_stdin = concat(designstr, charlist);

	if ((hGsThread = CreateThread(NULL,            /* 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",
		    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;
}
