/* Copyright (C) 1989, 1995 Aladdin Enterprises.  All rights reserved.
  
  This file is part of Aladdin Ghostscript.
  
  Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  or distributor accepts any responsibility for the consequences of using it,
  or for whether it serves any particular purpose or works at all, unless he
  or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  License (the "License") for full details.
  
  Every copy of Aladdin Ghostscript must include a copy of the License,
  normally in a plain ASCII text file named PUBLIC.  The License grants you
  the right to copy, modify and redistribute Aladdin Ghostscript, but only
  under certain conditions described in the License.  Among other things, the
  License requires that the copyright notice and this notice be preserved on
  all copies.
*/
/* Portions Copyright (C) 1994, 1995, Russell Lang.  All rights reserved. */


/* gsdll.c */
/* Dynamic Link Library interface for OS/2 and MS-Windows Ghostscript */
/* derived from gs.c */

/* Define PROGRAM_NAME before we include std.h */
#define PROGRAM_NAME gs_product
#include "ctype_.h"
#include "memory_.h"
#include <stdarg.h>
#include "string_.h"
/* Capture stdin/out/err before gs.h redefines them. */
/****** SINGLE-INSTANCE HACK ******/
#include <stdio.h>
static FILE *real_stdin, *real_stdout, *real_stderr;
static void
get_real(void)
{	real_stdin = stdin, real_stdout = stdout, real_stderr = stderr;
}
#include "ghost.h"
#include "gscdefs.h"
#include "gxdevice.h"
#include "gxdevmem.h"
#include "stream.h"
#include "errors.h"
#include "estack.h"
#include "ialloc.h"
#include "ivmspace.h"
#include "sfilter.h"		/* for iscan.h */
#include "ostack.h"		/* must precede iscan.h */
#include "iscan.h"
#include "main.h"
#include "store.h"
#include "files.h"				/* requires stream.h */
#include "interp.h"
#ifdef _Windows
#include "windows_.h"
#define GSDLLEXPORT _export
#endif
#include "gsdll.h"		/* header for DLLs */
#include <setjmp.h>

jmp_buf gsdll_env;		/* used by gp_do_exit in DLL configurations */

#ifndef GS_LIB
#  define GS_LIB "GS_LIB"
#endif

/* Library routines not declared in a standard header */
extern char *getenv(P1(const char *));

/* Forward references */
private int gsdll_doopt(char *opt);
private int gsdll_dodef(char sw, char *arg);

private int gsdll_usage;	/* should be needed only for 16-bit SHARED DATA */

/****** SINGLE-INSTANCE HACK ******/
private gs_main_instance *gsdll_minst;	/* instance data */
GSDLL_CALLBACK pgsdll_callback;	/* callback for messages and stdio to caller */


/* ---------- DLL exported functions ---------- */

/* arguments are:
 * 1. callback function for stdio and for notification of 
 *   sync_output, output_page and resize events
 * 2. a string containing a subset of Ghostscript command line options,
 *   each separated by a \0 and terminated by \0\0
 */
int GSDLLAPI 
gsdll_init(GSDLL_CALLBACK callback, char *str)
{	
int argc = 0;
char *p;
int exit_code;
ref error_object;
int code;
	if (gsdll_usage)
	    return -1;	/* DLL can't be used by multiple programs under Win16 */
	gsdll_usage++;

	if (setjmp(gsdll_env))
	    return gs_exit_status;	/* error */

	/****** SINGLE-INSTANCE HACK ******/
	gsdll_minst = gs_main_instance_default();
	/****** SINGLE-INSTANCE HACK ******/
	pgsdll_callback = callback;

	p = str;
	while (*p) {	/* count arguments */
	    argc++;
	    p += strlen(p) + 1;
	}

	get_real();
	gs_main_init0(gsdll_minst, real_stdin, real_stdout, real_stderr, argc);
	   {	char *lib = getenv(GS_LIB);
		if ( lib != 0 ) 
		   {	int len = strlen(lib);
			char *path = gs_malloc(len + 1, 1, "GS_LIB");
			strcpy(path, lib);
			gsdll_minst->lib_env_path = path;
		   }
	   }
	/* defines that must be made before gs_init.ps */
	{
	    char *next;
	    p = str;
	    while (*p) {
	        next = p + strlen(p) + 1;
		gsdll_doopt(p);
		p = next;
	    }
	}

	gs_main_init2(gsdll_minst);

	return 0;
}

/* if return value < 0, then error occured and caller should call */
/* gsdll_exit, then unload library */
int GSDLLAPI
gsdll_execute_begin(void)
{	int exit_code;
	ref error_object;
	int code;
	if (setjmp(gsdll_env))
	    return gs_exit_status;	/* error */
	code = gs_main_run_string_begin(gsdll_minst, 0, &exit_code, &error_object);
	return code;
}

/* if return value < 0, then error occured and caller should call */
/* gsdll_execute_end, then gsdll_exit, then unload library */
int GSDLLAPI
gsdll_execute_cont(char *str, int len)
{	int exit_code;
	ref error_object;
	int code;
	if (setjmp(gsdll_env))
	    return gs_exit_status;	/* error */
	code = gs_main_run_string_continue(gsdll_minst, str, len, 0, &exit_code, &error_object);
	if (code == e_NeedInput)
	    code = 0;	/* this is not an error */
	return code;
}

/* if return value < 0, then error occured and caller should call */
/* gsdll_exit, then unload library */
int GSDLLAPI
gsdll_execute_end(void)
{	int exit_code;
	ref error_object;
	int code;
	if (setjmp(gsdll_env))
	    return gs_exit_status;	/* error */
	code = gs_main_run_string_end(gsdll_minst, 0, &exit_code, &error_object);
	return code;
}

int GSDLLAPI
gsdll_exit(void)
{	int exit_code;
	ref error_object;
	int code;
	/* don't alter gsdll_usage */
	/* DLL must be unloaded before it can be used again */
	/* gsdll_usage--; */

	if (setjmp(gsdll_env))
	    return gs_exit_status;	/* error */
	/* don't call gs_exit() since this would cause caller to exit */
	gs_finit(0, 0);
	pgsdll_callback = (GSDLL_CALLBACK)NULL;
	return 0;
}

/* return revision numbers and strings of Ghostscript */
/* Used for determining if wrong GSDLL loaded */
/* this may be called before gsdll_init */
int GSDLLAPI
gsdll_revision(char **product, char **copyright, long *revision, long *revisiondate)
{
	if (product)
	    *product = (char *)gs_product;
	if (copyright)
	    *copyright = (char *)gs_copyright;
	if (revision)
	    *revision = gs_revision;
	if (revisiondate)
	    *revisiondate = gs_revisiondate;
	return 0;
}

/* ---------- DLL internal functions ---------- */

private int
gsdll_doopt(char *opt)
{
char sw;
    fprintf(stdout, "Processing option: %s\n", opt);
    if (*opt == '-') {
	sw = *(opt+1);
	if (sw=='d' || sw=='D' || sw=='s' || sw=='S')
	    gsdll_dodef(sw, opt+2);
	else if (sw=='I') {
	    char *path = gs_malloc(strlen(opt+2)+ 1, 1, "gsdll_init");
	    strcpy(path, opt+2);
	    gs_add_lib_path(path);
	}
	else if (sw=='Z') {
	    char *arg = opt+2;
	    while (*arg)
		gs_debug[*arg++ & 127] = 0xff;
	}
	else {
	    fprintf(stdout, "gsdll_init: unknown argument. Only -I -d -D -s -S supported.\n");
	    return 1;
	}
    }
    else if (*opt == '@') {
	/* This isn't implemented the same way as gs.c */
	/* Here we require one option per line. */
	FILE *optfile;
	char line[256];
	char *p;
	int len;
	if ((optfile = fopen(opt+1, "r")) == (FILE *)NULL) {
	    fprintf(stdout, "gsdll_init: can't open @file %s\n", opt+1);
	}
	else {
	    char *s, *d;
	    int inquote, prevquote;
	    int c;
	    while (fgets(line, sizeof(line), optfile)) {
		len = strlen(line);
		if ( (len) && (line[len-1] == '\n') )
		    line[len-1] = '\0'; /* remove newline */
		/* remove quotes */
		d = line;
		s = line;
		inquote = 0;
		prevquote = 0;
		c = *s;
		while (c) {
		    if (c == '\042') {
			if (prevquote && !inquote)
				*d++ = c;	/* allow embedded quotes in @ files */
			inquote = !inquote;
		    }
		    else
			*d++ = c;
		    prevquote = ( c == '\042' );
		    s++;
		    c = *s;
		}
		*d = '\0';
		if (line[0] != '\0')
		    gsdll_doopt(line);
	    }
	    fclose(optfile);
	}
    }
    else {
	fprintf(stdout, "gsdll_init: arguments must begin with '-' or '@'\n");
	return 1;
    }
    return 0;
}

private int
gsdll_dodef(char sw, char *arg)
{
char *adef;
char *eqp;
bool isd;
ref value;
    if (*arg == '\0')
	return 0;
    /* copy argument to heap */
    adef = gs_malloc(strlen(arg) + 1, 1, "gsdll_dodef");
    if (adef == (char *)NULL) {
	lprintf("Out of memory!\n");
	return -1;
    }
    strcpy(adef, arg);
    eqp = strchr(adef, '=');
    isd = (sw == 'D' || sw == 'd');
	
		if ( eqp == NULL )
			eqp = strchr(adef, '#');
		/* Initialize the object memory, scanner, and */
		/* name table now if needed. */
		gs_init1();
		if ( eqp == adef )
		   {
			fprintf(stdout, "gsdll_dodef: Usage: dname, dname=token, sname=string\n");
			return -1;
		   }
		if ( eqp == NULL )
		   {
			if ( isd )
				make_true(&value);
			else
				make_string(&value, a_readonly, 0, NULL);
		   }
		else
		   {	int code;
			*eqp++ = 0;
			if ( isd )
			   {	stream astream;
				scanner_state state;
				sread_string(&astream,
					     (const byte *)eqp, strlen(eqp));
				scanner_state_init(&state, false);
				code = scan_token(&astream, &value, &state);
				if ( code )
				   {	fprintf(stdout, "gsdll_dodef: dname= must be followed by a valid token\n");
					return -1;
				   }
			   }
			else
			   {	int len = strlen(eqp);
				char *str = gs_malloc((uint)len, 1, "-s");
				if ( str == 0 )
				   {	lprintf("Out of memory!\n");
					return -1;
				   }
				memcpy(str, eqp, len);
				make_const_string(&value, a_readonly + avm_foreign, len, (const byte *)str);
			   }
		   }
		/* Enter the name in systemdict. */
		initial_enter_name(adef, &value);
	return 0;
}

/* end gsdll.c */
