#ifndef lint
static char *RCSid = "$Id: plot.c,v 1.65 1995/12/07 21:41:08 drd Exp $";
#endif


/* GNUPLOT - plot.c */
/*
 * Copyright (C) 1986 - 1993   Thomas Williams, Colin Kelley
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted, 
 * provided that the above copyright notice appear in all copies and 
 * that both that copyright notice and this permission notice appear 
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the modified code.  Modifications are to be distributed 
 * as patches to released version.
 *  
 * This software is provided "as is" without express or implied warranty.
 * 
 *
 * AUTHORS
 * 
 *   Original Software:
 *     Thomas Williams,  Colin Kelley.
 * 
 *   Gnuplot 2.0 additions:
 *       Russell Lang, Dave Kotz, John Campbell.
 *
 *   Gnuplot 3.0 additions:
 *       Gershon Elber and many others.
 * 
 * There is a mailing list for gnuplot users. Note, however, that the
 * newsgroup 
 *	comp.graphics.gnuplot 
 * is identical to the mailing list (they
 * both carry the same set of messages). We prefer that you read the
 * messages through that newsgroup, to subscribing to the mailing list.
 * (If you can read that newsgroup, and are already on the mailing list,
 * please send a message info-gnuplot-request@dartmouth.edu, asking to be
 * removed from the mailing list.)
 *
 * The address for mailing to list members is
 *	   info-gnuplot@dartmouth.edu
 * and for mailing administrative requests is 
 *	   info-gnuplot-request@dartmouth.edu
 * The mailing list for bug reports is 
 *	   bug-gnuplot@dartmouth.edu
 * The list of those interested in beta-test versions is
 *	   info-gnuplot-beta@dartmouth.edu
 */

#include <signal.h>

#include "plot.h"
#include "fit.h"
#include "setshow.h"
#include "fnproto.h"
#if defined(MSDOS) || defined(DOS386)
#include <io.h>
#endif
#ifdef vms
#ifndef __GNUC__
#include <unixio.h>
#endif
#include <smgdef.h>
extern int vms_vkid;
extern smg$create_virtual_keyboard();
#endif
#ifdef AMIGA_SC_6_1
#include <proto/dos.h>
#endif

#ifdef _Windows
#include <windows.h>
#ifndef SIGINT
#define SIGINT 2	/* for MSC */
#endif
#endif

extern FILE *outfile;
extern TBOOLEAN term_graphics, term_suspended;

TBOOLEAN interactive = TRUE;	/* FALSE if stdin not a terminal */
TBOOLEAN noinputfiles = TRUE;	/* FALSE if there are script files */

/*  these 2 could be in misc.c, but are here with all the other globals */
TBOOLEAN do_load_arg_substitution = FALSE;
char *call_args[10] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};

char *infile_name = NULL;	/* name of command file; NULL if terminal */

#if defined(__TURBOC__) && (defined(MSDOS) || defined(DOS386))  /* patch to get home dir, see command.c */
#include <string.h>
char HelpFile[80] ;
#endif             /*   - DJL */

#ifndef STDOUT
#define STDOUT 1
#endif

#ifdef _Windows
jmp_buf far env;
#else
jmp_buf env;
#endif

int main __P((int argc, char **argv));
static void load_rcfile __P((void));
RETSIGTYPE inter __P((int anint));

struct ft_entry GPFAR ft[] = {	/* built-in function table */

/* internal functions: */
	{"push", (FUNC_PTR)f_push},	{"pushc", (FUNC_PTR)f_pushc},
	{"pushd1", (FUNC_PTR)f_pushd1},	{"pushd2", (FUNC_PTR)f_pushd2},
	{"pushd", (FUNC_PTR)f_pushd},	{"call", (FUNC_PTR)f_call},
	{"calln", (FUNC_PTR)f_calln},	{"lnot", (FUNC_PTR)f_lnot},
	{"bnot", (FUNC_PTR)f_bnot},	{"uminus", (FUNC_PTR)f_uminus},
	{"lor", (FUNC_PTR)f_lor},	{"land", (FUNC_PTR)f_land},
	{"bor", (FUNC_PTR)f_bor},	{"xor", (FUNC_PTR)f_xor},
	{"band", (FUNC_PTR)f_band},	{"eq", (FUNC_PTR)f_eq},	
	{"ne", (FUNC_PTR)f_ne},		{"gt", (FUNC_PTR)f_gt},
	{"lt", (FUNC_PTR)f_lt},		{"ge", (FUNC_PTR)f_ge},
	{"le", (FUNC_PTR)f_le},		{"plus", (FUNC_PTR)f_plus},
	{"minus", (FUNC_PTR)f_minus},	{"mult", (FUNC_PTR)f_mult},
	{"div", (FUNC_PTR)f_div},	{"mod", (FUNC_PTR)f_mod},
	{"power", (FUNC_PTR)f_power},	{"factorial", (FUNC_PTR)f_factorial},
	{"bool", (FUNC_PTR)f_bool},
	{"dollars", (FUNC_PTR)f_dollars},  /* for using extension */

	{"jump", f_jump},		{"jumpz", f_jumpz},
	{"jumpnz",f_jumpnz},		{"jtern", f_jtern},

/* standard functions: */
	{"real", (FUNC_PTR)f_real},	{"imag", (FUNC_PTR)f_imag},
	{"arg", (FUNC_PTR)f_arg},	{"conjg", (FUNC_PTR)f_conjg},
	{"sin", (FUNC_PTR)f_sin},	{"cos", (FUNC_PTR)f_cos},
	{"tan", (FUNC_PTR)f_tan},	{"asin", (FUNC_PTR)f_asin},
	{"acos", (FUNC_PTR)f_acos},	{"atan", (FUNC_PTR)f_atan},
	{"sinh", (FUNC_PTR)f_sinh},	{"cosh", (FUNC_PTR)f_cosh},
	{"tanh", (FUNC_PTR)f_tanh},	{"int", (FUNC_PTR)f_int},
	{"abs", (FUNC_PTR)f_abs},	{"sgn", (FUNC_PTR)f_sgn},
	{"sqrt", (FUNC_PTR)f_sqrt},	{"exp", (FUNC_PTR)f_exp},
	{"log10", (FUNC_PTR)f_log10},	{"log", (FUNC_PTR)f_log},
	{"besj0", (FUNC_PTR)f_besj0},	{"besj1", (FUNC_PTR)f_besj1},
	{"besy0", (FUNC_PTR)f_besy0},	{"besy1", (FUNC_PTR)f_besy1},
        {"erf", (FUNC_PTR)f_erf},	{"erfc", (FUNC_PTR)f_erfc},
	{"gamma", (FUNC_PTR)f_gamma},	{"lgamma", (FUNC_PTR)f_lgamma},
        {"ibeta", (FUNC_PTR)f_ibeta},	{"igamma", (FUNC_PTR)f_igamma},
	{"rand", (FUNC_PTR)f_rand},	{"floor", (FUNC_PTR)f_floor},
	{"ceil", (FUNC_PTR)f_ceil},

	{"norm", (FUNC_PTR)f_normal},			/* XXX-JG */
	{"inverf", (FUNC_PTR)f_inverse_erf},		/* XXX-JG */
	{"invnorm", (FUNC_PTR)f_inverse_normal},	/* XXX-JG */
	{"column", (FUNC_PTR)f_column},   /* for using */
	{"valid",  (FUNC_PTR)f_valid},    /* for using */

	{NULL, NULL}
};

static struct udvt_entry udv_pi = {NULL, "pi",FALSE};
									/* first in linked list */
struct udvt_entry *first_udv = &udv_pi;
struct udft_entry *first_udf = NULL;



#ifdef vms

#define HOME "sys$login:"

#else /* vms */
#if defined(MSDOS) ||  defined(AMIGA_AC_5) || defined(AMIGA_SC_6_1) || defined(ATARI) || defined(OS2) || defined(_Windows) || defined(DOS386)

#define HOME "GNUPLOT"

#else /* MSDOS || AMIGA || ATARI || OS2 || _Windows || defined(DOS386)*/

#define HOME "HOME"

#endif /* MSDOS || AMIGA || ATARI || OS2 || _Windows || defined(DOS386)*/
#endif /* vms */

#ifdef OS2
#define INCL_DOS
#define INCL_REXXSAA
#include <os2.h>
#include <process.h>
ULONG RexxInterface( PRXSTRING, PUSHORT, PRXSTRING ) ;
int   ExecuteMacro( char* ) ;  
#endif

#if defined(unix) || defined(AMIGA_AC_5) || defined(AMIGA_SC_6_1) || defined(OSK)
#define PLOTRC ".gnuplot"
#else /* AMIGA || unix */
#define PLOTRC "gnuplot.ini"
#endif /* AMIGA || unix */

#if defined(ATARI) || defined(MTOS)
void appl_exit(void);
void MTOS_open_pipe(void);
extern int aesid;
#endif

RETSIGTYPE inter (anint)
int anint;
{
#ifdef OS2
        (void) signal(anint, SIG_ACK);
#else
        (void) signal(SIGINT, (sigfunc)inter);
#endif

#ifndef DOSX286
	(void) signal(SIGFPE, SIG_DFL);	/* turn off FPE trapping */
#endif
	if (term && term_init)
		(*term->text)();	/* hopefully reset text mode */
	(void) fflush(outfile);
	(void) putc('\n',stderr);
	longjmp(env, TRUE);		/* return to prompt */
}


#ifdef _Windows
int gnu_main(argc, argv)
#else
int main(argc, argv)
#endif
	int argc;
	char **argv;
{
#ifdef LINUX
	LINUX_setup();
#endif
/* make sure that we really have revoked root access, this might happen if
   gnuplot is compiled without vga support but is installed suid by mistake */
#ifdef __linux__
	setuid(getuid());
#endif
#if defined(MSDOS) && !defined(_Windows) && !defined(__GNUC__)
  PC_setup();
#endif /* MSDOS !Windows */

#ifdef OSK	/* malloc large blocks, otherwise problems with fragmented mem */
   _mallocmin (102400);
#endif
	
#ifdef MALLOCDEBUG
malloc_debug(7);
#endif

#ifndef DOSX286
#ifndef _Windows
#if defined (__TURBOC__) && (defined (MSDOS) || defined(DOS386))
strcpy (HelpFile,argv[0]) ;                       /* got helpfile from */
strcpy (strrchr(HelpFile,'\\'),"\\gnuplot.gih") ; /* home directory    */
                                                  /*   - DJL */
#endif
#endif
#endif

#ifdef VMS
unsigned int status[2] = {1, 0};
#endif

#ifdef X11
     { extern int X11_args __P((int argc, char *argv[]));
       int n = X11_args(argc, argv);
       argv += n;
       argc -= n;
     }
#endif 

#ifdef apollo
    apollo_pfm_catch();
#endif

/* moved to ATARI_init in atariaes.trm */
/* #ifdef ATARI
	void application_init(void);
	application_init();
#endif */ 

#ifdef MTOS
	MTOS_open_pipe();
#endif

#ifdef OS2
     RexxRegisterSubcomExe( "GNUPLOT", (PFN) RexxInterface, NULL ) ;
#endif

	setbuf(stderr,(char *)NULL);
#ifdef UNIX
	setlinebuf(stdout);
#endif
	outfile = stdout;
	(void) Gcomplex(&udv_pi.udv_value, Pi, 0.0);

     init_memory();

     interactive = FALSE;
     init_terminal();		/* can set term type if it likes */

#ifdef AMIGA_SC_6_1
     if (IsInteractive(Input()) == DOSTRUE) interactive = TRUE;
     else interactive = FALSE;
#else
#if (defined(__MSC__) && defined(_Windows)) || defined(__WIN32__)
     interactive = TRUE;
#else
     interactive = isatty(fileno(stdin));
#endif
#endif
     if (argc > 1)
	  interactive = noinputfiles = FALSE;
     else
	  noinputfiles = TRUE;

     if (interactive)
	  show_version();
#ifdef vms   /* initialise screen management routines for command recall */
          if (status[1] = smg$create_virtual_keyboard(&vms_vkid) != SS$_NORMAL)
               done(status[1]);
#endif

	if (!setjmp(env)) {
	    /* first time */
	    interrupt_setup();
	    load_rcfile();
	    init_fit ();	/* Initialization of fitting module */

	    if (interactive && term != 0)	/* not unknown */
		 fprintf(stderr, "\nTerminal type set to '%s'\n", 
			    term->name);
	} else {	
	    /* come back here from int_error() */
#ifdef AMIGA_SC_6_1
	    (void) rawcon(0);
#endif
	    load_file_error();	/* if we were in load_file(), cleanup */
#ifdef _Windows
	SetCursor(LoadCursor((HINSTANCE)NULL, IDC_ARROW));
#endif
#ifdef vms
	    /* after catching interrupt */
	    /* VAX stuffs up stdout on SIGINT while writing to stdout,
		  so reopen stdout. */
	    if (outfile == stdout) {
		   if ( (stdout = freopen("SYS$OUTPUT","w",stdout))  == NULL) {
			  /* couldn't reopen it so try opening it instead */
			  if ( (stdout = fopen("SYS$OUTPUT","w"))  == NULL) {
				 /* don't use int_error here - causes infinite loop! */
				 fprintf(stderr,"Error opening SYS$OUTPUT as stdout\n");
			  }
		   }
		   outfile = stdout;
	    }
#endif					/* VMS */
	    if (!interactive && !noinputfiles) {
			if (term && term_init)
				(*term->reset)();
#ifdef vms
			vms_reset();
#endif
#if defined(ATARI) || defined(MTOS)
			if (aesid > -1) atexit(appl_exit);
#endif
			return(IO_ERROR);	/* exit on non-interactive error */
 		}
	}

     if (argc > 1) {
	    /* load filenames given as arguments */
	    while (--argc > 0) {
		   ++argv;
		   c_token = NO_CARET; /* in case of file not found */
		   load_file(fopen(*argv,"r"), *argv, FALSE);	    }
	} else {
	    /* take commands from stdin */
	    while(!com_line());
	}

	if (term_graphics)
	{
		/* end-of-file while plot active */

		if (multiplot && term_suspended && term->resume)
		{
			(*term->resume)();
			term_suspended = FALSE;
		}
		(*term->text)();
		term_graphics = FALSE;
	}

	if (term && term_init)
		(*term->reset)();
#ifdef vms
	vms_reset();
#endif
#ifdef OS2
     RexxDeregisterSubcom( "GNUPLOT", NULL ) ;
#endif
#if defined(ATARI) || defined(MTOS)
	if (aesid > -1) atexit(appl_exit);
#endif
    return(IO_SUCCESS);
}

#if (defined(ATARI) && defined(__PUREC__)) || (defined(MTOS) && defined(__PUREC__))
#include <math.h>
int purec_matherr(struct exception *e)
{	char *c;
	switch (e->type) {
	    case DOMAIN:    c = "domain error"; break;
	    case SING  :    c = "argument singularity"; break;
	    case OVERFLOW:  c = "overflow range"; break;
	    case UNDERFLOW: c = "underflow range"; break;
	    default:		c = "(unknown error"; break;
	}
	fprintf(stderr, "math exception : %s\n", c);
	fprintf(stderr, "    name : %s\n", e->name);
	fprintf(stderr, "    arg 1: %e\n", e->arg1);
	fprintf(stderr, "    arg 2: %e\n", e->arg2);
	fprintf(stderr, "    ret  : %e\n", e->retval);
	return 1;
}
#endif


/* Set up to catch interrupts */
void interrupt_setup()
{
#ifdef __PUREC__
        setmatherr(purec_matherr);
#endif

	(void) signal(SIGINT, (sigfunc)inter);

/* ignore pipe errors, this might happen with set output "|head" */
#ifdef SIGPIPE
		(void) signal(SIGPIPE, SIG_IGN);
#endif /* SIGPIPE */
}


/* Look for a gnuplot start-up file */
static void load_rcfile()
{
    register FILE *plotrc;
    char home[80]; 
    char rcfile[sizeof(PLOTRC)+80];
#if defined(ATARI) || defined(MTOS)
    #include <support.h> 
    char *ini_ptr;
    char const *const ext[] = {NULL};
#endif
    /* Look for a gnuplot init file in . or home directory */
#ifdef vms
    (void) strcpy(home,HOME);
#else /* vms */
    char *tmp_home=getenv(HOME);
    char *p;	/* points to last char in home path, or to \0, if none */
    char c='\0';/* character that should be added, or \0, if none */

    if(tmp_home) {
    	strcpy(home,tmp_home);
	if( strlen(home) ) p = &home[strlen(home)-1];
	else		   p = home;
#if defined(MSDOS) || defined(ATARI) || defined( OS2 ) || defined(_Windows) || defined(DOS386)
	if( *p!='\\' && *p!='\0' ) c='\\';
#else
#if defined(MTOS)
	if( *p!='\\' && *p!='/' && *p!='\0' ) c='\\';
#else
#if defined(AMIGA_AC_5)
	if( *p!='/' && *p!=':' && *p!='\0' ) c='/';
#else /* that leaves unix and OSK */
	c='/';
#endif
#endif
#endif
	if(c) {
	    if(*p) p++;
	    *p++=c;
	    *p='\0';
	}
    }
#endif /* vms */

#ifdef NOCWDRC
    /* inhibit check of init file in current directory for security reasons */
    {
#else
    (void) strcpy(rcfile, PLOTRC);
    plotrc = fopen(rcfile,"r");
    if (plotrc == (FILE *)NULL) {
#endif
#ifndef vms
	if( tmp_home ) {
#endif
	   (void) sprintf(rcfile, "%s%s", home, PLOTRC);
	   plotrc = fopen(rcfile,"r");
#if defined(ATARI) || defined(MTOS)
	}
	if (plotrc == (FILE *)NULL) {
	   ini_ptr = findfile(PLOTRC,getenv("GNUPLOTPATH"),ext);
	   if (ini_ptr)  plotrc = fopen(ini_ptr,"r");
        }
#endif
#if !defined(vms) && !defined(ATARI) && !defined(MTOS)
	} else
	   plotrc=NULL;
#endif
    }
    if (plotrc)
	 load_file(plotrc, rcfile, FALSE);
}
#ifdef OS2
int ExecuteMacro( char *pszName ) 
    {
    RXSTRING argv[1] ;
    RXSTRING rxRc ;
    char pszRc[256] ;
    LONG lRc ;
    HAB hab ;
    int rc ;
    
    MAKERXSTRING( argv[0], pszName, strlen( pszName ) ) ;
    MAKERXSTRING( rxRc, pszRc, 256 ) ;

    rc = RexxStart( 1,
                    argv,
                    pszName,
                    NULL,
                    "GNUPLOT",
                    RXCOMMAND,
                    NULL,
                    &lRc,
                    &rxRc ) ;
    if(rc==-4)rc=0; /*run was cancelled-don't give error message*/ 
    return rc ;
    }

ULONG RexxInterface( PRXSTRING rxCmd, PUSHORT pusErr, PRXSTRING rxRc ) 
/*
** Rexx command line interface
*/
    {
    int iCmd ;
    int rc ;
    static jmp_buf keepenv;
    memcpy( keepenv, env, sizeof(jmp_buf)) ;
  if (!setjmp(env)) {
    set_input_line( rxCmd->strptr, rxCmd->strlength ) ;
    rc = do_line() ;
    *pusErr = RXSUBCOM_OK ;
    rxRc->strptr[0] = rc + '0' ;
    rxRc->strptr[1] = '\0' ;
    rxRc->strlength = strlen( rxRc->strptr ) ;    
  } else {
    *pusErr = RXSUBCOM_ERROR ;
    RexxSetHalt( getpid(), 1 ) ;
  }
    memcpy( env, keepenv, sizeof(jmp_buf)) ;
    return 0 ;    
    }
#endif

