/*
 *  TEX Device Driver
 *  epsbox.c:	epsbox specials support routines
 *  			by uchiyama@bacchus.in.kobe-u.ac.jp		25 September 1992
 *
 *  【注意】
 *    これは松下電器産業(株)情報システム研究所によって開発された
 *    jdvi2kps を基に作成されています．したがって以下の関数は
 *    松下電器産業(株)情報システム研究所に著作権がありますので
 *    その取扱は jdvi2kps の copyright ファイルに記された内容に
 *    従わなければなりません．
 *  　■対象となる関数
 *      DoPostScriptBox()   jdvi2kps の postscript.c から必要部分を引用
 *      epsbox_pret()       同上の do_special() から必要部分を引用
 *
 *  Modified for epsfile=, EMS etc. by SHIMA            11 February  1993
 *  Modified for pbmfile=  by Yakumo                     5 February  1993
 *  Modified for adjustment of size by Yakumo, SHIMA    14 February  1993
 *  Include 淺山's GIF decoder (SHIMA)                  17 February  1993
 *  Modified for file= by SHIMA                         18 April     1993
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <io.h>
#include <dir.h>
#include <process.h>
#include <alloc.h>
#define _DEF_STDIO_H_
#include "dd.h"
#include "err.h"

#define	NEAR_WORK	0	/* Working area for GIF: near heap <-> get_work()	*/
#define	PACKED		1	/* Working data for GIF: 3 byte    <-> 4 byte		*/
#define	OLD			0	/* Old means original routine						*/

						/* different EPS/PS filenames kept for a page */
#define	MAX_PS_NAME	30	/* Use 	9 * MAX_PS_NAME byte				  */

					/* maximal number of tempolary PBM/GIF files for a page */
#define	MAX_PS		960

char *gs_exe = "gs.exe";
#define	gs_argv	((char **)(tmp_buf + 512))
#define	ps_path	(tmp_buf + 1024)

/* main.c */
extern DVIFILE_INFO dvifile;

/* option.c */
int strlcmp(char*, char*);
int length_to_sp(char *, SCALED_PT *);

/* int.c prtinit.c */
extern DIMENSION dviout_dimension;
extern int enlarge;
extern int org_mag;

/* pret.c */
PIXEL get_current_x(void);
PIXEL get_current_y(void);
PIXEL get_old_y(void);
void do_special(char *);

/* bitmap.c */
void write_pbm(PIXEL h, PIXEL v, PIXEL w, PIXEL d, char *raster);
void write_tiled_pbm(PIXEL h, PIXEL v, PIXEL w, PIXEL d, char *raster, int tl);

/* sptopxl.asm or size.c (prtsize.c) */
PIXEL sptopixel( SCALED_PT sp );
PIXEL vtopixel( SCALED_PT sp );

/* buffer.c */
int free_buffer(int mode);
void recover_buffer(void);
char *marea(unsigned int);

/* option.c */
int make_path(char *path, char *dir, char *name, char *ext);

/* putdvi.c */
extern current_page;

/* pret.c */
extern int f_break_bitmap;

/* loadpk.c */
FILE *fopenf(char *name, char *mode);

/* bitmap.c */
void boxpic(PIXEL h, PIXEL v, PIXEL w, PIXEL d);

extern DIMENSION dviout_dimension;

/* called by xxx1() in pret.c. interface is func ptr (esbox_special) */
BOOL epsbox_pret(char *special) /* interpret special. Ret TRUE if found */;
BOOL (*epsbox_special)(char *special) = epsbox_pret;
BOOL put_pbm(char *epsfname, PIXEL h, PIXEL v,
	PIXEL width, PIXEL height, int mode , BOOL fgbox);
void name_pbm(char *name);
static BOOL do_gsps(char *str);
static BOOL DoPostScriptBox(char *epsname, float width, float height,
	float hscale, float vscale, int mode);
static void do_gspostscriptbox(char *epsfname, int x, int y, int a, int b,
	FILE *fp);
static void exec_gs(char* pbm, char** argv);
static FILE *open_pbm(char *epsname, PIXEL *x, PIXEL *y, int mode);
static void putpbm(char *epsfname, PIXEL h, PIXEL v);
static void get_pbmname(char *filename);
static int find_psname(char *path);
static float get_pt(char *sf);
static void chg_pathsep(char *path, char ch);
static void adjust_width(void);
static void output_line(void);
static void count_gsps(void);
static void pret_file(char *);

#ifdef GIF
static FILE *open_gifmono(FILE *fp, PIXEL *x, PIXEL *y,int mode);
static void get_gif(FILE *fp);
#endif

/*-->DoPostScriptBox*/
/**********************************************************************/
/*************************  DoPostScriptBox  **************************/
/**********************************************************************/

#define	Min(a, b)	((a) > (b) ? (b) : (a))
#define	Max(a, b)	((a) < (b) ? (b) : (a))
#define	Abs(n)		(((n) >= 0) ? (n) : -(n))
#define False 0
#define True 1

#if	0
int	framemode	= False;	/* Framemarker compatibility mode.  */
#endif

char *pbm_path;
int	f_GS = 1;
int current_gsps;
BOOL f_GIF;
BOOL f_gbox;
BOOL f_gsize;

static float dpi, DPI, hoffset, voffset;
static int ps_page, total_gsps;

#ifdef GIF
int current_f_GIF;
#endif

#define	DO_GS		0x1
#define	DO_GSV		0x2
#define	DO_SPBM		0x3
#define	DO_PBM		0x4
#define	DO_GSS		0x5

#define	IS_SCALE	0x8000
#define	IS_HOFFSET	0x4000
#define	IS_VOFFSET	0x2000
#define	IS_TILE		0x1000
#define	IS_POS		0x0800
#define	IS_POSXY	0x0400

#define	MAX_KEY		5
#define	KEY_TYPE	0x0f

#define	EPSBOX		0
#define	EPS			1
#define	PS			2
#define	PBM			3
#define	GIFF		4
#define	SPFILE		5

static char *PS_KEY[MAX_KEY] = {
		"epsfile",
		"psfile",
		"pbmfile",
		"giffile",
		"file"
	};

static char	*key_opt[] ={
	"hsize", "vsize", "hoffset", "voffset", "hscale", "vscale", "scale", 
	"truesize",
#ifdef	TILE
	"tile",
#endif
	""};

enum {
	Hsize=0, Vsize, Hoffset, Voffset, Hscale, Vscale, Scale, REAL
#ifdef	TILE
	,Tile
#endif
};

static char ps_name[MAX_PS_NAME][MAXFILE];

static FILE *search_ps(char *name)
{
	FILE *fp;
	int	i, ch;

	for (i = strlen(dvifile.file_name); i >= 0; ){
		ps_path[i] = 0;
		if ((ch = dvifile.file_name[--i]) == '\\' || ch == '/' || ch == ':')
			break;
	}
	for( ;i >= 0; i--) ps_path[i] = dvifile.file_name[i]; 
	strcat(ps_path, name);
	for (i = 0; i < 2; i++){
		if ((fp = fopenf(ps_path, "r")) != NULL) break;
		make_path(ps_path, pbm_path, name, NULL);
	}
	return(fp);
}

/*
 * DoPostScriptBox:
 *  This is where the postscriptbox is handled.
 *  Note that the width and height here are in real points (1/72.27 inch)
 *  as generated from TeX, as opposed to the silly 1/72 inch PS points.
 *  width and height are given by point (1 inch = 72.27 point)
 */

static BOOL DoPostScriptBox(char *epsfname, float width, float height,
	float hsc, float vsc, int mode)
{
	BOOL found = FALSE;
	BOOL foundsize = FALSE;
	char *s;
	FILE *spfp, *outfp;
	char line[257];
	float x1, x2, y1, y2;
	float bbx, bby, bbh, bbw;
#if	0
	char dummy[10];
#endif
	float ho, vo;
	PIXEL xofs, yofs;
	PIXEL xsize, ysize;
	PIXEL xrealofs, yrealofs;
	PIXEL xrealsize, yrealsize;
	PIXEL xadd, yadd;
	PIXEL x, y;
	char *p;
	struct ftime ftime0;
	struct ftime ftime1;

#define ADD_LENGTH 72 /* big point */

	ENTER("DoPostScriptBox")

  /*
   * Open the PS file.
   */
	if ((spfp = fopenf(epsfname, "r")) == NULL){
		if((spfp = search_ps(epsfname)) != NULL)
			epsfname = ps_path;
		else{
			error(WARNING, "unable to open %s(PS)", epsfname);
			return(found);
		}
	}
  /*
   * Get time stamp.
   */
	getftime(fileno(spfp), &ftime0);

  /*
   * Search for BoundingBox or Frame, and rewind.
   */
	if ((mode & KEY_TYPE) == EPSBOX || (mode & KEY_TYPE) == EPS){
		while(fgets(line, 256, spfp)) {
			if((p = strstr(line, "%%BoundingBox:" )) != NULL){
				while (*p++ != ':' );
				if(sscanf(p, "%f %f %f %f", &x1, &y1, &x2, &y2) == 4) {
					bbx = Min(x1, x2);
					bby = Min(y1, y2);
					bbw = Abs(x2 - x1);
					bbh = Abs(y2 - y1);
					foundsize = True;
					break;
				}
			}
#if	0
			if(framemode && sscanf(line, " %f %f %f %f %[C] ",
			  &x1, &y1, &x2, &y2, &dummy[0]) == 5) {
				bbx = Min(x1, x2);
				bby = Min(y1, y2);
				bbw = Abs(x2 - x1);
				bbh = Abs(y2 - y1);
				foundsize = True;
				break;
			}
#endif
		}
		if(! foundsize) {
			fprintf(stderr, "no BoundingBox found: assuming unit square.");
			bbx = 0.0;
			bby = 0.0;
			bbw = 1.0;
			bbh = 1.0;
		}
  	}
	fclose(spfp);

  /*
   * Emit preamble.
   *  The PS box thinks it will print in bbw PSpoints, or bbw/72 inches.
   *  TeX wants it to fit into width real points, or width/72.27 inches.
   *  The current scale has been reset to 72 dots per inch.
   * Scale = (width/72.27) / (bbw/72).
   *  72.27/72 = 1.00375
   */
	if ((mode & KEY_TYPE) == PS){
		if (width  == 0) width  = hsc*1.00375;
		if (height == 0) height = vsc*1.00375;
	}
	else if (mode & IS_SCALE){
org:	width  = bbw * hsc * 1.00375;
		height = bbh * vsc * 1.00375;
	}
	else{
		if (width == 0){
			if (height == 0){
				hsc = vsc = 1;
				goto org;
			}
			else width  = bbw * height / bbh;
		}
		else if (height == 0){
			height = bbh * width / bbw;
		}
		hsc = width /(bbw * 1.00375);
		vsc = height/(bbh * 1.00375);
	}
	xadd = (PIXEL)ROUND(dpi * Abs(hsc) * ADD_LENGTH / 72.0);
	yadd = (PIXEL)ROUND(DPI * Abs(vsc) * ADD_LENGTH / 72.0);
	xrealsize = (PIXEL)ROUND((Abs(width)  * dpi / 72.0));
	yrealsize = (PIXEL)ROUND((Abs(height) * DPI / 72.0));
	xsize = xrealsize+xadd*2;
	ysize = yrealsize+yadd*2;
	xrealofs = get_current_x();
	yrealofs = get_current_y();
	
	if (hsc < 0) xrealofs -= xrealsize;
	if ((mode & KEY_TYPE) == EPSBOX || vsc < 0)	yrealofs -= yrealsize;
	
	xofs = xrealofs - xadd;
	yofs = yrealofs - yadd;

/****************************************************************************
 1  f_GS(画像表示)？
 2    Y-> 通常の向き？
 3    |   Y-> PBM が存在？
 4    |   |   Y-> PBM が PS より新しい？
 5    |   |   |   Y-> サイズが正しい、または、SPBM？
 6    |   |   |   |   Y-> PBM ファイルを読んで表示する -> put_pbm()
 7    |   |   |   |   N-> PS をこのページで未登録で登録可 -> find_psname()
 8    |   |   |   |       Y-> PBM または、SPBM？
 9    |   |   |   |       |   Y->23 へ
10    |   |   |   |       |   N->GhostScript を起動して PBM を作成後 6へ
11    |   |   |   |       N-> tmpps%d.pbm として作成するため、18 へ
12    |   |   |   N-> PS をこのページで未登録で登録可
13    |   |   |       Y-> 8 へ
14    |   |   |       N-> PS 登録はオーバーフロー？
15    |   |   |           Y-> tmpps%d.pbm として作成するため、18 へ
16    |   |   |           N-> 終了(gs_exec.bat 作成のとき起こりうる)
17    |   |   N-> 12 へ
18    |   N-> GSS(gs_exec.bat作成)ならばワーニングを出す。tmpps%d.pbm を得る。
      | 　    current_gsps <= total_gsps？
19    |       Y-> GSS(gs_exec.bat作成)？
20    |       |   Y-> 23 へ
21    |       |   N-> 6 へ
22    |       N-> total_gsps = current_gsps として 8 へ
23    N-> 必要なら外枠表示 -> boxpic() で、終了

注: tmpps%d.pbm の %d は、通常そのページの先頭からのテンポラリPSファイルの通し
　　番号となる。ただし、GSS のときは、「対応するPSファイルの前に存在するそのペ
    ージの先頭からの異なるGhostscriptでの処理の数」をさらに足した数となる。
***************************************************************************/

	count_gsps();
	if (!f_GS) goto show_box;
	strcpy(line, epsfname);
	if (hsc < 0.0 || vsc < 0.0){		/* if not normal PBM		*/
tmp_pbm:
		get_pbmname(line);				/* get PBM name from line	*/
		if (f_GS == DO_GSS && current_gsps > total_gsps)
			error(WARNING, "Need temporary PBM for PS");
										/* check if it has been made */
		if (current_gsps <= total_gsps){
			if (f_GS != DO_GSS) goto use_pbm;
			goto show_box;
		}
	}
										/* check if PBM exists */
	else if ((spfp = open_pbm(epsfname, &x, &y, 1)) != NULL){
		getftime(fileno(spfp), &ftime1);
		fclose(spfp);
										/*   compare timestamp */
		if (*((ulong*)&ftime0) <= *((ulong*)&ftime1)){
										/* compare size  */
			if (f_GS == DO_SPBM
			  || (y == ysize && (((x+7) & 0xfff8) == ((xsize+7) & 0xfff8))))
										/* use it if it coincides */
				goto use_pbm;
		}
					/* check if the PBM has been made in the current page */
		if (find_psname(epsfname) == 0) goto tmp_pbm;
	}
	else{
		switch(find_psname(epsfname)){
			case 0: if (f_GS != DO_GSS)		/* exist */
						goto show_box;
			case 1:	goto tmp_pbm;			/* overflow */
/*			case 2:	break;						saved */
		}
	}
	if (f_GS == DO_PBM || f_GS == DO_SPBM) goto show_box;

	outfp = fopenf("tmp$.ps", "wt");
	fputs("/NumbDict countdictstack def\n1 dict begin\n/showpage {} def\n",outfp);
	fputs("userdict begin\n",outfp);
		fprintf(outfp, "%f %f translate\n",
			Abs(hsc)*ADD_LENGTH, Abs(vsc)*ADD_LENGTH);
    if((mode & KEY_TYPE) != PS)
    if (hsc < 0.0 || vsc < 0.0 || (mode & (IS_HOFFSET|IS_VOFFSET) )) {
    	fprintf(outfp, "%f %f translate\n",
     		(hsc<0.0?-width:0.0) + hoffset/1.00375,
			(vsc<0.0?-height:0.0) + voffset/1.00375);
    }
    fprintf(outfp, "%f %f scale\n", hsc, vsc);
		fprintf(outfp, "%f %f translate\n", -bbx, -bby);

	do_gspostscriptbox(epsfname, (PIXEL)ROUND(dpi), (PIXEL)ROUND(DPI), 
		xsize, ysize, outfp);
	if (f_GS == DO_GSS) goto show_box;
use_pbm:
	put_pbm(line, xofs, yofs, xsize, ysize, IS_POS, 0);
show_box:
	if (f_gbox)
		boxpic(xrealofs, yrealofs, xrealsize, yrealsize);
end:
	if (total_gsps < current_gsps) total_gsps = current_gsps;
	RETURN(found);
}

static char *find_key(char *str, char *key)
{
	if ((str = strstr(str, key)) != NULL){
		str += strlen(key);
		if (!isalpha(*str)){
			while (*str == ' ') str++;
			if (*str == '=')
				while (*(++str) == ' ');
			return(str);
		}
	}
	return(NULL);
}

static float get_pt(char *sf)
{
	
	float len;
	SCALED_PT sp;

	if (length_to_sp(sf, &sp) == 0) return((float)sp/65536.0);
	else return(atof(sf));
}

BOOL epsbox_pret(char *str)		/* interpret special. Ret TRUE if found */
{
	BOOL found = TRUE;
	char *sf = NULL;
	int i, mode;
	float scale;
	char epsname[MAXPATH];
	float width=0, height=0, hscale=1, vscale=1;

	if (!f_GS && !f_gbox) return(FALSE);

	hoffset = voffset = mode = 0;
#ifdef	GIF
	current_f_GIF = f_GIF;
#endif
	scale = (float)dviout_dimension.mag / 1000.0;
	if (enlarge) scale = scale * enlarge / 1000.0;
	if (f_gsize) scale = scale * 1000.0 / org_mag;
	dpi = dviout_dimension.dpi*scale;
	DPI = dviout_dimension.DPI*scale;

  /* Handling psfig macros */

  /* skip blank */
	while(*str == ' ') str++;
	if (strlcmp(str, "ps:") == 0){
    	do_gsps(&str[3]);
	} else if (strlcmp(str,"postscriptbox") == 0) {
  	/*
	 * Read args
	 */
		if (sscanf(str+13, "{%fpt}{%fpt}{%80[^ {}]}", 
		  &width, &height, epsname) != 3)
			error(PROGRAM_STOP, "badly formed PS command:%s", str);

dobox:	DoPostScriptBox(epsname, width, height, hscale, vscale, mode);

	} else if (strlcmp(str, "\" ") == 0) {
    	do_gsps(&str[2]);
  	} else {
		for (mode = 0; ; ){
			if( (sf = find_key(str, PS_KEY[mode++])) != NULL ) break;
			if (mode >= MAX_KEY){
				found = FALSE;
				goto quit;
			}
		}

		sscanf(sf, "%80[^, ]", epsname);
		for (i = 0; *key_opt[i]; i++){
			if ((sf = find_key(str, key_opt[i])) == NULL) continue;
			switch (i){
				case Hsize:
						width  = get_pt(sf);
						break;

				case Vsize:
						height = get_pt(sf);
						break;

				case Hoffset:
						hoffset = get_pt(sf);
						mode |= IS_HOFFSET;
						break;

				case Voffset:
						voffset = get_pt(sf);
						mode |= IS_VOFFSET;
						break;

				case Scale:
						vscale = atof(sf);

				case Hscale:
						hscale = atof(sf);
						mode |= IS_SCALE;
						break;

				case Vscale:
						vscale = atof(sf);
						mode |= IS_SCALE;
						break;

				case REAL:
						scale = 1;
						dpi = dviout_dimension.dpi;
						DPI = dviout_dimension.DPI;
						break;

#ifdef	TILE
				case Tile:
						mode |= IS_TILE;
						break;
#endif
			}
		}

		switch(mode & KEY_TYPE){
			case EPS:					/* epsfile= */
			case PS :					/* psfile= */
					goto dobox;

			case PBM:					/* pbmfile= */
#ifdef GIF
					current_f_GIF = 0;
#endif
get_size:			if (mode & IS_SCALE){
						width  = hscale*scale*100;
						height = vscale*scale*100;
					}else{
						width  *= dpi/72.27;
						height *= DPI/72.27;
					}
					put_pbm(epsname, 0, 0,
						(PIXEL)ROUND(width), (PIXEL)ROUND(height),
						mode & ~KEY_TYPE, f_gbox);
					break;
#ifdef GIF
			case GIFF:					/* giffile= */
					current_f_GIF = 1;
					goto get_size;
#endif
			case SPFILE:				/* file= */
					pret_file(epsname);
					break;
		}
	}
quit:
	RETURN(found);
}

void pret_file(char *fname)
{
	#define	MAX_LINE_BUF 0x400

	FILE *fp;
	char line_buf[MAX_LINE_BUF];
	int ch, i, f_comment, nest;
	char *SP_KEY = "special";

	if ((fp = search_ps(fname)) == NULL){
		error(NO_FILE, "%s", fname);
		return;
	}
	nest = f_comment = 0;
	while((ch = fgetc(fp)) != -1){
		if (ch == '%'){
			f_comment = 1;
			continue;
		}
		if (f_comment){
			if (ch == 0x0a) f_comment = 0;
			continue;
		}
		if (!nest){
			if (ch == '\\'){
				for (i = 0; SP_KEY[i]; i++){
					if (fgetc(fp) != SP_KEY[i]) goto skip;
				}
				nest = 1;
skip:;		}
		}
		else{						/* \special ... */
			if (nest == 1){
				if (ch == '{'){
					i = 0;
					nest = 2;
				}
				else if (isalpha(ch)) nest = 0;
				else continue;
			}
			else if (ch == '}'){
				if (--nest != 1) goto keep;
				line_buf[i] = nest = 0;
				do_special(line_buf);
			}
			else{
				if (ch < ' ') ch = ' ';
keep:			line_buf[i++] = ch;
				if (ch == '{') nest++;
				if (i >= MAX_LINE_BUF - 1){
					error(WARNING, "too long special in %s", fname);
					goto end;
				}
			}
		}
	}
end:
	fclose(fp);
}

void exec_gs(char *pbm, char **argv0)
{
	char temp[100];
	char exe[100];
	char newenv1[14];
	char newenv2[16];
	char *pt;
	char **argv;
	static int f_free;

	if (f_GS == DO_GSS){
		if (current_gsps <= total_gsps)
			return;						/* done already */
		gs_exe = "gssub.exe";
	}
										/* get memory to execute gs_exe */
	free_buffer(f_free);

	sprintf(newenv1, "page=%d", current_page);
	putenv(newenv1);
	sprintf(newenv2, "info=%Fp", (DIMENSION far *)&dviout_dimension);
	putenv(newenv2);
	strcpy(temp, "-sOUTPUTFILE=");
	strcat(temp, pbm);
	chg_pathsep(temp, '/');
	argv = argv0;
	*--argv = temp;

#ifdef GIF
	*--argv = (current_f_GIF)?"-sDEVICE=gifmono":"-sDEVICE=pbmraw";
#else
	*--argv = "-sDEVICE=pbmraw";
#endif
	*--argv = "-dNOPAUSE";
	if(f_GS == DO_GS) *--argv = "-q";

	strncpy(exe, gs_exe, 100);
	pt = exe + strlen(exe);
	while (--pt >= exe){
		if (*pt == '^'){
			*pt = 0;
			*--argv = pt + 1;
		}
	}
	*--argv = exe;

	for (;;){
		fprintf(stderr, "\nCalling %s to make %s[%d:%ld]\n", gs_exe,
			pbm, f_free, farcoreleft());
		if (spawnvp(P_WAIT, exe, argv) != -1
		   && (f_GS == DO_GSS || access(pbm, 0) != -1))
				break;
		if (f_free == 0)
			free_buffer(++f_free);
		else{
			if(f_GS != DO_GSV)
				error(PROGRAM_STOP, "Cannot execute %s(%d)", gs_exe, errno);
			break;
		}
	}
	recover_buffer();
	if (f_break_bitmap && total_gsps < current_gsps)
		total_gsps = current_gsps;
}


static void do_gspostscriptbox(char *epsfname, int x, int y, int a, int b,
	FILE *fp)
{
	char res[80];
	char geo[30];

	strncpy(res, epsfname, 80);
	chg_pathsep(res, '/');
	fprintf(fp, "(%s) run\n", res);
	fputs("countdictstack NumbDict sub {end} repeat\nshowpage\n",fp);
	fclose(fp);

	sprintf(res, (x==y)?"-r%d":"-r%dx%d", x, y);
	sprintf(geo, "-g%dx%d", a, b);
	if (f_GS != DO_GSS) unlink(tmp_buf);
	gs_argv[0] = res;
	gs_argv[1] = geo;
	gs_argv[2] = "--";
	gs_argv[3] = "tmp$.ps";
	gs_argv[4] = NULL;
	exec_gs(tmp_buf, gs_argv);
}

static void count_gsps(void)
{
	if (ps_page != current_page){			/* New page */
		ps_page = current_page;
		current_gsps = total_gsps = ps_name[0][0] = 0;
	}
	current_gsps++;
}

/*
 * 0: exist  1:overflow  2:not exist
 */
static int find_psname(char *path)
{
	int	i, j;
	char drive[MAXDRIVE], dir[MAXDIR], name[MAXFILE], ext[MAXEXT];

	fnsplit(path, drive, dir, name, ext);	/* get name from path */

	for (i = 0 ; i < MAX_PS_NAME; i++ ){
		if (*ps_name[i] == 0){
			strcpy(ps_name[i], name);			/* register the name */
			break;
		}
		if (strcmp(ps_name[i], name) == 0) return(0);	/* same PS file */
	}
								/* No same PS file or overflow MAX_PS_NAME */
	return((i == MAX_PS_NAME)? 1:2);
}

static void get_pbmname(char *filename)
{
	if (current_gsps > MAX_PS)
		error(PROGRAM_STOP, "Too many PS codes in a page");
	sprintf(filename, "tmpps%d.pbm", current_gsps );
	name_pbm(filename);
}

static BOOL do_gsps(char *str)
{
	char res[30];
	char pbm[30];
	FILE *fp;

	if (f_GS != DO_GS && f_GS != DO_GSV){
		if(f_GS == DO_GSS) error(WARNING, "PS code exist");
		else return(FALSE);
	}
	count_gsps();
	get_pbmname(pbm);

	if (total_gsps < current_gsps){
		if ((fp = fopenf("tmp$$.ps", "wb")) == NULL)
			error(PROGRAM_STOP, "Can't open temporary PS file");
	   	fprintf(fp, "72 0 translate\n"); 
		fwrite(str, strlen(str), sizeof(char), fp);
		fprintf(fp, "\nshowpage\nquit\n" );
		fclose(fp);
		gs_argv[3] = NULL;
		sprintf(res, "-r%dx%d", dviout_dimension.dpi, dviout_dimension.DPI);
		unlink(tmp_buf);
		gs_argv[0] = res;
		gs_argv[1] = "--";
		gs_argv[2] = "tmp$$.ps";
		gs_argv[3] = NULL;
		exec_gs(tmp_buf, gs_argv);
	}
	put_pbm(pbm, 0, 0, 0, 0, IS_POSXY, f_gbox);
	if (total_gsps < current_gsps) total_gsps = current_gsps;
	return(True);
}

static void read_wh(FILE *fp, PIXEL *x, PIXEL *y)
{
	int	temp;
	char c;
	
	temp = 0;
	while( isdigit(c = getc(fp)) )
		temp = temp * 10 + c - '0';
	*x = temp;
	while( !isdigit(c = getc(fp)) );
	temp = c - '0';
	while( isdigit(c = getc(fp)) )
		temp = temp * 10 + c - '0';
	*y = temp;
}

static FILE *open_pbm(char *epsfname, int *x, int *y, int mode)
{
	int	ch;
	FILE *fp;

	name_pbm(epsfname);
	if ((fp=fopenf(tmp_buf, "rb") ) == NULL) {
		if (!mode) error(WARNING, "Cannot get %s", tmp_buf);
		return(NULL);
	}

#ifdef GIF
	if (current_f_GIF) return(open_gifmono(fp, x, y, mode));
#endif

	if (getc(fp) != 'P' || getc(fp) != '4' || !isspace(getc(fp))){
		if (!mode) error(FILE_FAULT, "%s isn't a pbm file", tmp_buf);
		fclose(fp);
		return(NULL);
	}
	while ((ch = getc(fp)) == '#'){			/* skip comment lines */
		while (getc(fp) != 0x0a && !feof(fp));
	}
	ungetc(ch, fp);
	read_wh(fp, x, y);
	return(fp);
}

static uint byte_width;
static uint new_byte_width;
static PIXEL x_shift;
static PIXEL x_size, y_size;
static PIXEL x_pos, y_pos;
static PIXEL cur_y, data_y;
static PIXEL pixel_width;
static ulong delta_ox;
static ulong delta_nx;
static ulong delta_xend;
static ulong delta_oy;
static ulong delta_ny;

#ifdef GIF
static void disp_gif(FILE *fp);

#if	PACKED
typedef struct gif_t_s {
        int dummy : 1;
        unsigned int top : 1;
        unsigned int data : 1;
        int prev : 13;
} gif_t;
#else
typedef struct gif_t_s {
        int prev;
        unsigned char data;
} gif_t;
#endif

#if	NEAR_WORK
static unsigned char *output_stack;
static gif_t *entry_table;
#else
static unsigned char far *output_stack;
static gif_t far *entry_table;
#endif

static PIXEL cur_pixel;
static BOOL reverse_f = FALSE;
#endif

#ifdef	TILE
static int f_tile;
#endif

#define	high(x) *( (int*)(&(x)) + 1)
static uchar ROW[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};

static void adjust_width(void)
{
	int	bit, x;
												/* clear buffer */
	for (x = new_byte_width + byte_width; x > byte_width; )
		tmp_buf[--x] = 0;
												/* arrange 1 raster line */
	for (bit = 0x80, x = byte_width, delta_ox = (delta_nx >> 1); ; ){
		if (tmp_buf[high(delta_ox) >> 3] & ROW[high(delta_ox) & 7] )
				tmp_buf[x] |= bit;
		if (!(bit >>= 1)){
			bit = 0x80;
			x++;
		}
		if( (delta_ox += delta_nx) >= delta_xend ) break;
	}
}

static void output_line(void)
{
	int	count = 0;

	while(cur_y < y_size && data_y >= ((delta_oy + delta_ny) >> 16)){
		if (x_shift > 0 && count++ == 0) adjust_width();
#ifdef	TILE
		write_tiled_pbm(x_pos, y_pos++, x_size, y_size, tmp_buf + x_shift,
			f_tile);
#else
		write_pbm(x_pos, y_pos++, x_size, 1, tmp_buf + x_shift);
#endif
		cur_y++;
		delta_oy += delta_ny;
	}
	data_y++;
#ifdef GIF
	cur_pixel = 0;
#endif
}

BOOL put_pbm(char *pbmfname, PIXEL h, PIXEL v, 
	PIXEL width, PIXEL height, int mode, BOOL fgbox)
{
	FILE *fp;
	int i, j;
	PIXEL x, y, x2 = 0, y2 = 0;

	if ( f_break_bitmap
	  || (fp = open_pbm(pbmfname, &x2, &y2, (f_GS == DO_GSS)?1:0)) == NULL)
			return(FALSE);

	if (mode & IS_SCALE){
			x = (PIXEL)ROUND((((float)x2) *  width) / 100.0);
			y = (PIXEL)ROUND((((float)y2) * height) / 100.0);
	}
	else{
skip:	y = height;
		if (width == 0){
			if (height == 0){
				x = x2;
				y = y2;
			}
			else
				x = (((long)y) * x2 + (y2 >> 1))/y2;
		}
		else{
			x = width;
			if (height == 0)
				y = (((long)x) * y2 + (x2 >> 1))/x2;
		}
	}
#ifdef	TILE
	f_tile = mode & IS_TILE;
#endif

	if (x + width > 8 * (COMMON_SIZE - 258)) {
		error(WARNING, "tmp_buf overflow!: %ddots", width);
		fclose(fp);
		return FALSE;
	}

	if ( !(mode & IS_POS)
#ifdef	TILE
		&& !f_tile
#endif
		) {
		x_pos = get_current_x();
		y_pos = get_current_y();
	}
	else{
		x_pos = h;
		y_pos = v;
	}
	x_size = x;
	y_size = y;
	if (mode & IS_POSXY){
		x_pos -= dpi;
		y_pos += -y_size;
	}
	if (fgbox)	boxpic(x_pos, y_pos, x_size, y_size);

	delta_nx = (((long)x2) << 16) / x_size;
	delta_xend = delta_nx * x_size + (delta_nx >> 1);
	delta_ny = (((long)y2) << 16) / y_size;
	delta_oy = -(delta_ny >> 1);
	byte_width = (x2 + 7) / 8;
	pixel_width = x2;
	new_byte_width = (x_size + 7) / 8;
	x_shift = (((x_size + 7) & 0xfff8) == ((x2 + 7) & 0xfff8))? 0:byte_width;
	cur_y = data_y = 0;
	if (x_shift != 0 || f_GS != DO_GS)
		printf("Figure (%d,%d)->(%d,%d)\n",
			x2, y2,(x_shift == 0)?x2:x_size, y_size);
#ifdef GIF
	if (current_f_GIF)	disp_gif(fp);
	else
#endif
	for(i = 0; i < y2; i++){
		fread(tmp_buf, sizeof(char), byte_width, fp);
		output_line();
	}
	fclose(fp);
	return TRUE;
}

void name_pbm(char *path)
{
	char drive[MAXDRIVE], dir[MAXDIR], name[MAXFILE], ext[MAXEXT];

	fnsplit(path, drive, dir, name, ext);	/* get name from path */
#ifdef GIF
	make_path(tmp_buf, pbm_path, name, (current_f_GIF)?".gif":".pbm");
#else
	make_path(tmp_buf, pbm_path, name, ".pbm");
#endif
}

static void chg_pathsep(char *path, char ch)
{
	while(*path){
		if (*path == '/' || *path =='\\') *path = ch;
		path++;
	}
}

#ifdef GIF
/*
 *		GIF decoder by 淺山
 *		Slightly modifed by SHIMA
 */

#define	output_buf	tmp_buf				/* this should not be changed */
#define	input_buf	(tmp_buf+(COMMON_SIZE-256))

FILE *open_gifmono(FILE *fp, PIXEL *x, PIXEL *y, int mode)
{
	int c;
	
	fread(input_buf, 1, 13, fp);
	if (strlcmp(input_buf, "GIF87a") != 0 && strlcmp(input_buf, "GIF89a") != 0)
		goto non_gif;

	c = input_buf[10] & 0x07;
	if (c != 0x00 && c != 0x02) {
non_gif:
		if (!mode) error(WARNING, "%s isn't monochrome GIF file.", tmp_buf);
close:
		fclose(fp);
		return(NULL);
	}
	if (input_buf[10] & 0x80) { /* global color map exists. */
		fread(input_buf, 1, (0x01<<(c+1))*3, fp);
		reverse_f = !input_buf[0];
	}
	else {
		reverse_f = FALSE;
	}

	c = fgetc(fp);
	if (c  == '!') {	/* Skip */
		int len;
		
		fgetc(fp);
		while((len = fgetc(fp)) != 0) {
			fread(input_buf, len, 1, fp);
		}
		c = fgetc(fp);
	}
	if (c != ',') {
		error(WARNING, "Wrong separator in GIF");
		goto close;
	}
	fread(input_buf, 1, 9, fp);

	*x = input_buf[4];
	*x += ((int)input_buf[5] << 8);
	*y = input_buf[6];
	*y += ((int)input_buf[7] << 8);

	c = (input_buf[8] & 0x07);
	if (c != 0x00 && c != 0x02) {
		goto non_gif;
	}
	else if ((input_buf[8] & 0x40) != 0x00) {
		if (!mode)
			error(WARNING, "Not supported interlace mode for %s",tmp_buf);
		goto close;
	}
	if (input_buf[8] & 0x80) {
		fread(input_buf, 1, (0x01<<(c+1))*3, fp);
		reverse_f = !input_buf[0];
	}
	return(fp);
}

#if	0
static int get_nbits(FILE *fp,int bits) /* if bits == 0  initialize. */
{
	static int len;
	static int byte_ptr;
	static int shift;
	int i;
	int c = 0;
	
	if (bits == 0) {
		len = 0;
		byte_ptr = -1;
		shift = 8;
	}
	for (i=0;i<bits;++i) {
		if (shift == 8) {
			if (byte_ptr == len-1) {
				if ((len = fgetc(fp)) == 0) return -1;
				if (fread(tmp_buf,(size_t)len,1,fp) < 1) {
					fputs("Read error.\n",stderr);
					return -1;
				}
				byte_ptr = 0;
			}
			else byte_ptr++;
			shift = 0;
		}
		c |= (((input_buf[byte_ptr]>>shift) & 0x01)<<i);
		shift++;
	}
	return c;
}
#else

static int get_nbits(FILE *fp,int bits) /* if bits == 0  initialize. */
{
	static int len;
	static int byte_ptr;
	static int shift;
	int top0, cut;
	int c = 0, i = 0;
	
	if (bits == 0) {
		len = 0;
		byte_ptr = -1;
		shift = 8;
	}
	else{
		for (;;){
			if (shift == 8) {
				if (byte_ptr == len - 1) {
					if ((len = fgetc(fp)) == 0
					 || fread(input_buf, (size_t)len, 1, fp) < 1)
						return -1;
					byte_ptr = 0;
				}
				else byte_ptr++;
				shift = 0;
			}
			cut = ((top0 = 8 + i - bits - shift) > 0)?(0xff>>top0):0xff;
			c |= (((input_buf[byte_ptr] & cut) >> shift) << i);
			if (top0 >= 0){
				shift = 8 - top0;
				break;
			}
			else{
				shift = 8;
				i = bits + top0;
			}
		}
	}
	return c;
}
#endif

static void init_table(int s)
{
	int i;

	for (i=0;i<s;++i) {
		entry_table[i].data
#if PACKED
		= entry_table[i].top
#endif
		= i;
		entry_table[i].prev = -1;
	}
}

static void clear_line(void)
{
	int i;
	
	if (reverse_f) {
		for (i=byte_width-1;i>=0;i--)
			output_buf[i] = 0xff;
	}
	else {
		for (i=byte_width-1;i>=0;i--)
			output_buf[i] = 0;
	}
}

static void output_code(unsigned char c)
{
	if (c)
		output_buf[cur_pixel>>3] ^= ROW[cur_pixel&0x07];
	cur_pixel++;
	if (cur_pixel >= pixel_width) {
		output_line();
		clear_line();
	}
}

#if OLD
static void output_table(int p)
{
	int sp = 0;
	
	while(entry_table[p].prev != -1) {
		output_stack[sp++] = entry_table[p].data;
		p = entry_table[p].prev;
	}
	output_stack[sp] = entry_table[p].data;
	while(sp >= 0) {
		if (cur_bit == -1) {
			if (cur_byte + 1 == byte_width)
				output_line();
			else
				cur_byte++;
			cur_bit = 7;
			output_buf[cur_byte] = 0;
		}
		output_buf[cur_byte] =
			(output_buf[cur_byte]<<1) | output_stack[sp];
		sp--;
		cur_bit--;
	}
}
#else

static void output_table(int p)
{
	int sp;
	uint bits,bits_limit;
	uchar far *out_ptr;
	uint cur_byte,cur_bit;
	static uint mask[] = {0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};
	
	*(out_ptr = output_stack) = 0;
	for(bits = 1;;){
		if(entry_table[p].data)
			*out_ptr |= bits;
		if ((p = entry_table[p].prev) == -1) break;
		if ((bits <<= 1) == 0x100){
			*(++out_ptr) = 0;
			bits = 1;
		}
	}
	sp = (out_ptr - output_stack)<<3;
	for (bits >>= 1; bits ; bits >>= 1)
		sp++;

	while (sp >= 0){
		cur_byte = cur_pixel>>3;
		cur_bit = cur_pixel&0x07;
		
		bits_limit = (8-cur_bit+cur_pixel > pixel_width)
										? pixel_width-cur_pixel : 8-cur_bit;
		bits = (sp+1 >= bits_limit) ? bits_limit : sp+1;
				/* the following code depends on CPU */
		output_buf[cur_byte] ^=
			((*(uint far*)(out_ptr-1)>>((sp&0x07)+1))&mask[bits])>>cur_bit;
		
		if ((sp&0x07)+1 <= bits)
			out_ptr--;
		cur_pixel += bits;
		sp -= bits;
		
		if (cur_pixel >= pixel_width) {
			output_line();
			clear_line();
		}
	}
}
#endif

static void disp_gif(FILE *fp)
{
#if	OLD
	int codesize;
	int clearcode;
	int endcode;
#else
	#define	codesize	2
	#define	clearcode	4
	#define	endcode		5
#endif
	int nextcode;
	int bitlen;
	int code;
	int tbl_last;
 	int s;
	int p;
	unsigned char c;

	cur_pixel = 0;

#define	MAXTBL	4096

#if	NEAR_WORK
	/* 1 byte shift of output_stack is nesessary for output_table() */
 #if OLD
	output_stack = (unsigned char *) marea(MAXTBL * sizeof(char));
 #else
	output_stack = (unsigned char *) marea(MAXTBL/8+2);
	*output_stack++ = 0;
 #endif
	entry_table = (gif_t *) marea(sizeof(gif_t)*MAXTBL);
#else
 #if OLD
	output_stack = get_work((sizeof(gif_t) + sizeof(char)) * MAXTBL);
	entry_table = (gif_t far*)(output_stack + MAXTBL * sizeof(char));
 #else
	output_stack = get_work(sizeof(gif_t) * MAXTBL + (MAXTBL/8+2));
	*output_stack++ = 0;
	entry_table = (gif_t far*)(output_stack + (MAXTBL/8+1));
 #endif
	if (output_stack == NULL){
		error(WARNING, "No working space for GIF");
		return;
	}
#endif
#if	OLD
	if ((codesize = fgetc(fp)) != 2) {
#else
	if (fgetc(fp) != 2) {
#endif
		error(WARNING, "Not monochrome GIF");
		return;
	}
#if	OLD
	clearcode = (1<<codesize);
	endcode = clearcode+1;
#endif
	bitlen = codesize+1;
	init_table(clearcode);
	tbl_last = clearcode+2;
	get_nbits(fp,0);

	clear_line();
	for ( ; ; ) {
		if ((nextcode = get_nbits(fp, bitlen)) == -1)
			break;
		if (nextcode == endcode) break;
		if (nextcode == clearcode) {
			tbl_last = clearcode+2;
			bitlen = codesize+1;
			if ((code = get_nbits(fp, bitlen)) == -1)
				break;
			if (code == endcode)
				break;
			output_code(entry_table[code].data);
		}
		else {
			if (nextcode < tbl_last) {
#if PACKED
				c = entry_table[nextcode].top;
#else
				p = nextcode;
				while(entry_table[p].prev != -1) {
					p = entry_table[p].prev;
					if (p < 0 || p >= tbl_last) goto data_err;
				}
				c = entry_table[p].data;
#endif
				output_table(nextcode);
			}
			else {
#if PACKED
				c = entry_table[code].top;
#else
				p = code;
				while(entry_table[p].prev != -1) {
					p = entry_table[p].prev;
					if (p < 0 || p >= tbl_last){
data_err:				error(WARNING, "Data error in GIF");
						return;
					}
				}
				c = entry_table[p].data;
#endif
				output_table(code);
				output_code(c);
			}
			if (tbl_last < MAXTBL) {
				entry_table[tbl_last].prev = code;
				entry_table[tbl_last].data = c;
#if PACKED
				entry_table[tbl_last].top = entry_table[code].top;
#endif
				if (tbl_last == (1<<bitlen)-1 && bitlen != 12)
					bitlen++;
				tbl_last++;
			}
			code = nextcode;
		}
	}
#if	NEAR_WORK
	free(output_stack);
	free(entry_table);
#endif
}
#endif /* GIF */
