/* GNUPLOT - command.c */
/*
 * Copyright (C) 1986, 1987, 1990   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.
 * 
 * send your comments or suggestions to (pixar!info-gnuplot@sun.com).
 * 
 */

#include <stdio.h>
#include <math.h>

#ifdef MSDOS
#include <process.h>

#ifdef __ZTC__
#define P_WAIT 0
#include <time.h>	/* usleep() */
#else

#ifdef __TURBOC__
#include <dos.h>	/* sleep() */

#else	/* must be MSC */
#include <time.h>	/* kludge to provide sleep() */
void sleep();		/* defined later */
#endif /* TURBOC */
#endif /* ZTC */

#endif /* MSDOS */


#include "plot.h"
#include "setshow.h"
#include "help.h"

#ifndef STDOUT
#define STDOUT 1
#endif

#ifndef HELPFILE
#define HELPFILE "docs/gnuplot.gih" /* changed by makefile */
#endif

#define inrange(z,min,max) ((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)) )

/*
 * instead of <strings.h>
 */

extern char *gets(),*getenv();
extern char *strcpy(),*strncpy(),*strcat();
extern int strlen(), strcmp();

extern double magnitude(),angle(),real(),imag();
extern struct value *const_express(), *pop(), *complex();
extern struct at_type *temp_at(), *perm_at();
extern struct udft_entry *add_udf();
extern struct udvt_entry *add_udv();
extern void squash_spaces();
extern void lower_case();

extern BOOLEAN interactive;	/* from plot.c */

/* input data, parsing variables */
struct lexical_unit token[MAX_TOKENS];
char input_line[MAX_LINE_LEN+1] = "";
int num_tokens, c_token;
int inline_num = 0;			/* input line number */

char c_dummy_var[MAX_ID_LEN+1]; 		/* current dummy var */

/* the curves of the plot */
struct curve_points *first_plot = NULL;
static struct udft_entry plot_func;
struct udft_entry *dummy_func;

/* support for replot command */
char replot_line[MAX_LINE_LEN+1] = "";
static int plot_token;					/* start of 'plot' command */

com_line()
{
	read_line(PROMPT);

	/* So we can flag any new output: if false at time of error, */
	/* we reprint the command line before printing caret. */
	/* TRUE for interactive terminals, since the command line is typed. */
	/* FALSE for non-terminal stdin, so command line is printed anyway. */
	/* (DFK 11/89) */
	screen_ok = interactive; 

	do_line();
}


do_line()	  /* also used in load_file */
{
	if (is_system(input_line[0])) {
		do_system();
		(void) fputs("!\n",stderr);
		return;
	}
	num_tokens = scanner(input_line);
	c_token = 0;
	while(c_token < num_tokens) {
		command();
		if (c_token < num_tokens)	/* something after command */
			if (equals(c_token,";"))
				c_token++;
			else
					int_error("';' expected",c_token);
	}
}



command()
{
    char sv_file[MAX_LINE_LEN+1];
    /* string holding name of save or load file */

	c_dummy_var[0] = '\0';		/* no dummy variable */

	if (is_definition(c_token))
		define();
	else if (equals(c_token,"help") || equals(c_token,"?")) {
	    c_token++;
	    do_help();
	}
	else if (almost_equals(c_token,"test")) {
	    c_token++;
		test_term();
	}
	else if (almost_equals(c_token,"pa$use")) {
		struct value a;
		int stime, text=0;
		char buf[MAX_LINE_LEN+1];

		c_token++;
		stime = (int )real(const_express(&a));
		if (!(END_OF_COMMAND)) {
			if (!isstring(c_token))
				int_error("expecting string",c_token);
			else {
				quotel_str(buf,c_token);
				(void) fprintf (stderr, "%s",buf);
				text = 1;
			}
		}
		if (stime < 0) (void) fgets (buf,MAX_LINE_LEN,stdin);  
						/* Hold until CR hit. */
#ifdef __ZTC__
		if (stime > 0) usleep((unsigned long) stime);
#else
		if (stime > 0) sleep((unsigned int) stime);
#endif
		if (text != 0 && stime >= 0) (void) fprintf (stderr,"\n");
		c_token++;
		screen_ok = FALSE;
	}
	else if (almost_equals(c_token,"pr$int")) {
		struct value a;

		c_token++;
		(void) const_express(&a);
		(void) putc('\t',stderr);
		disp_value(stderr,&a);
		(void) putc('\n',stderr);
		screen_ok = FALSE;
	}
	else if (almost_equals(c_token,"p$lot")) {
		plot_token = c_token++;
		plotrequest();
	}
	else if (almost_equals(c_token,"rep$lot")) {
		if (replot_line[0] == '\0') 
			int_error("no previous plot",c_token);
		c_token++;
		replotrequest();
	}
	else if (almost_equals(c_token,"se$t"))
		set_command();
	else if (almost_equals(c_token,"sh$ow"))
		show_command();
	else if (almost_equals(c_token,"cl$ear")) {
		if (!term_init) {
			(*term_tbl[term].init)();
			term_init = TRUE;
		}
		(*term_tbl[term].graphics)();
		(*term_tbl[term].text)();
		(void) fflush(outfile);
		screen_ok = FALSE;
		c_token++;
	}
	else if (almost_equals(c_token,"she$ll")) {
		do_shell();
		screen_ok = FALSE;
		c_token++;
	}
	else if (almost_equals(c_token,"sa$ve")) {
		if (almost_equals(++c_token,"f$unctions")) {
			if (!isstring(++c_token))
				int_error("expecting filename",c_token);
			else {
				quote_str(sv_file,c_token);
				save_functions(fopen(sv_file,"w"));
			}
		}
		else if (almost_equals(c_token,"v$ariables")) {
			if (!isstring(++c_token))
				int_error("expecting filename",c_token);
			else {
				quote_str(sv_file,c_token);
				save_variables(fopen(sv_file,"w"));
			}
		}
		else if (almost_equals(c_token,"s$et")) {
			if (!isstring(++c_token))
				int_error("expecting filename",c_token);
			else {
				quote_str(sv_file,c_token);
				save_set(fopen(sv_file,"w"));
			}
		}
		else if (isstring(c_token)) {
			quote_str(sv_file,c_token);
			save_all(fopen(sv_file,"w"));
		}
		else {
			int_error(
		"filename or keyword 'functions', 'variables', or 'set' expected",
					c_token);
		}
		c_token++;
	}
	else if (almost_equals(c_token,"l$oad")) {
		if (!isstring(++c_token))
			int_error("expecting filename",c_token);
		else {
			quote_str(sv_file,c_token);
			load_file(fopen(sv_file,"r"), sv_file);	
		/* input_line[] and token[] now destroyed! */
			c_token = num_tokens = 0;
		}
	}
	else if (almost_equals(c_token,"ex$it") ||
			almost_equals(c_token,"q$uit")) {
		done(IO_SUCCESS);
	}
	else if (!equals(c_token,";")) {		/* null statement */
		int_error("invalid command",c_token);
	}
}

replotrequest()
{
char str[MAX_LINE_LEN+1];
		if(equals(c_token,"["))
			int_error("cannot set range with replot",c_token);
		if (!END_OF_COMMAND) {
			capture(str,c_token,num_tokens-1);
			if ( (strlen(str) + strlen(input_line)) <= MAX_LINE_LEN-1) {
				(void) strcat(replot_line,",");
				(void) strcat(replot_line,str); 
			} else {
				int_error("plot line too long with replot arguments",c_token);
			}
		}
		(void) strcpy(input_line,replot_line);
		screen_ok = FALSE;
		num_tokens = scanner(input_line);
		c_token = 1;					/* skip the 'plot' part */
		plotrequest();
}


plotrequest()
{
     BOOLEAN changed;
	int dummy_token = -1;

     autoscale_lx = autoscale_x;
     autoscale_ly = autoscale_y;
	
	if (!term)					/* unknown */
		int_error("use 'set term' to set terminal type first",c_token);

	if (equals(c_token,"[")) {
		c_token++;
		if (isletter(c_token)) {
		    if (equals(c_token+1,"=")) {
			   dummy_token = c_token;
			   c_token += 2;
		    } else {
			   /* oops; probably an expression with a variable. */
			   /* Parse it as an xmin expression. */
			   /* used to be: int_error("'=' expected",c_token); */
		    }
		}
		changed = load_range(&xmin,&xmax);
		if (!equals(c_token,"]"))
			int_error("']' expected",c_token);
		c_token++;
		if (changed)
		  autoscale_lx = FALSE;
	}

	if (equals(c_token,"[")) { /* set optional y ranges */
		c_token++;
		changed = load_range(&ymin,&ymax);
		if (!equals(c_token,"]"))
			int_error("']' expected",c_token);
		c_token++;
		if (changed)
		  autoscale_ly = FALSE;
	}

     /* use the default dummy variable unless changed */
	if (dummy_token >= 0)
	  copy_str(c_dummy_var,dummy_token);
	else
	  (void) strcpy(c_dummy_var,dummy_var);

	eval_plots();
}


define()
{
register int start_token;  /* the 1st token in the function definition */
register struct udvt_entry *udv;
register struct udft_entry *udf;

	if (equals(c_token+1,"(")) {
		/* function ! */
		start_token = c_token;
		copy_str(c_dummy_var, c_token + 2);
		c_token += 5; /* skip (, dummy, ) and = */
		if (END_OF_COMMAND)
			int_error("function definition expected",c_token);
		udf = dummy_func = add_udf(start_token);
		if (udf->at)				/* already a dynamic a.t. there */
			free((char *)udf->at);	/* so free it first */
		if ((udf->at = perm_at()) == (struct at_type *)NULL)
			int_error("not enough memory for function",start_token);
		m_capture(&(udf->definition),start_token,c_token-1);
	}
	else {
		/* variable ! */
		start_token = c_token;
		c_token +=2;
		udv = add_udv(start_token);
		(void) const_express(&(udv->udv_value));
		udv->udv_undef = FALSE;
	}
}


get_data(this_plot)
struct curve_points *this_plot;
{
static char data_file[MAX_LINE_LEN+1], line[MAX_LINE_LEN+1];
register int i, l_num, datum;
register FILE *fp;
float x, y;
int npoints;				/* number of points and breaks read */

	quote_str(data_file, c_token);
	this_plot->plot_type = DATA;
	if ((fp = fopen(data_file, "r")) == (FILE *)NULL)
		os_error("can't open data file", c_token);

	l_num = 0;
     datum = 0;
	i = 0;
     npoints = 0;
	while ( fgets(line, MAX_LINE_LEN, fp) != (char *)NULL ) {
		l_num++;
		if (is_comment(line[0]))
			continue;		/* ignore comments */
		npoints++;
		if (i >= samples)	/* should never be > samples */
		  continue;		/* Overflow! keep looping to get count */
		if (!line[1]) { /* is it blank line ? */
			/* break in data, make next point undefined */
			this_plot->points[i].type = UNDEFINED;
			i++;
			continue;
		}

		switch (sscanf(line, "%f %f", &x, &y)) {
		    case 1: {		/* only one number on the line */
			   y = x;		/* assign that number to y */
			   x = datum;	/* and make the index into x */
			   /* no break; !!! */
		    }
		    case 2: {
			   datum++;
			   this_plot->points[i].type = INRANGE;

			   if (log_x) {
				  if (x < 0.0) {
					 this_plot->points[i].type = UNDEFINED;
				  } else if (x == 0.0) {
					 this_plot->points[i].type = OUTRANGE;
					 this_plot->points[i].x = -VERYLARGE;
				  } else
				    this_plot->points[i].x = log10(x);
			   } else
				this_plot->points[i].x = x;

			   if (log_y) {
				  if (y < 0.0) {
					 this_plot->points[i].type = UNDEFINED;
				  } else if (y == 0.0) {
					 this_plot->points[i].type = OUTRANGE;
					 this_plot->points[i].y = -VERYLARGE;
				  } else
				    this_plot->points[i].y = log10(y);
			   } else
				this_plot->points[i].y = y;

			   if (this_plot->points[i].type == INRANGE)
				if (   (autoscale_lx || inrange(x,xmin,xmax))
				    && (autoscale_ly || inrange(y,ymin,ymax) || polar)) {
				    if (autoscale_lx) {
					   if (x < xmin) xmin = x;
					   if (x > xmax) xmax = x;
				    }
				    if (autoscale_ly) {
					   if (y < ymin) ymin = y;
					   if (y > ymax) ymax = y;
				    }
				} else {
				    this_plot->points[i].type = OUTRANGE;
				}
			   i++;
			   break;
		    }
		    default: {
			   (void) sprintf(line, "bad data on line %d", l_num);
			   int_error(line,c_token);
		    }
		}
	}
	if (npoints > samples) {
		(void) sprintf(line,
 "%d data points and breaks found--samples must be set at least this high",
					npoints); 
		int_error(line,c_token);
	}
	this_plot->p_count = i;
     (void) fclose(fp);
}

/* This parses the plot command after any range specifications. 
 * To support autoscaling on the x axis, we want any data files to 
 * define the x range, then to plot any functions using that range. 
 * We thus parse the input twice, once to pick up the data files, 
 * and again to pick up the functions. Definitions are processed 
 * twice, but that won't hurt.
 */
eval_plots()
{
register int i;
register struct curve_points *this_plot, **tp_ptr;
register int start_token, mysamples;
register int begin_token;
register double x_min, x_max, y_min, y_max, x;
register double xdiff, temp;
static struct value a;
BOOLEAN some_data_files = FALSE;
int plot_num, line_num, point_num;

	mysamples = samples;

	if (autoscale_ly) {
		ymin = VERYLARGE;
		ymax = -VERYLARGE;
	} else if (log_y && (ymin <= 0.0 || ymax <= 0.0))
			int_error("y range must be above 0 for log scale!",
				NO_CARET);

	tp_ptr = &(first_plot);
	plot_num = 0;
	line_num = 0; 	/* default line type */
	point_num = 0;	/* default point type */

     begin_token = c_token;

/*** First Pass: Read through data files ***/
/* This pass serves to set the xrange and to parse the command, as well 
 * as filling in every thing except the function data. That is done after
 * the xrange is defined.
 */
	while (TRUE) {
		if (END_OF_COMMAND)
			int_error("function to plot expected",c_token);

		start_token = c_token;

		if (is_definition(c_token)) {
			define();
		} else {
			plot_num++;
			if (*tp_ptr)
				this_plot = *tp_ptr;
			else {		/* no memory malloc()'d there yet */
				this_plot = (struct curve_points *)
					alloc((unsigned int) (sizeof(struct curve_points) -
					(MAX_POINTS -(samples+1))*sizeof(struct coordinate)),
					"curve points");
				this_plot->next_cp = NULL;
				this_plot->title = NULL;
				*tp_ptr = this_plot;
			}

			if (isstring(c_token)) {			/* data file to plot */
				if (!some_data_files && autoscale_lx) {
				    xmin = VERYLARGE;
				    xmax = -VERYLARGE;
				}
				some_data_files = TRUE;

				this_plot->plot_type = DATA;
				this_plot->plot_style = data_style;
				get_data(this_plot);
				c_token++;
			} 
			else {							/* function to plot */
				this_plot->plot_type = FUNC;
				this_plot->plot_style = func_style;
				dummy_func = &plot_func;
				plot_func.at = temp_at();
				/* ignore it for now */
			}

			if (almost_equals(c_token,"t$itle")) {
				c_token++;
				if ( (input_line[token[c_token].start_index]=='\'')
				   ||(input_line[token[c_token].start_index]=='"') ) {
					m_quote_capture(&(this_plot->title),c_token,c_token);
				}
				else {
					int_error("expecting \"title\" for plot",c_token);
				}
				c_token++;
			}
			else {
				m_capture(&(this_plot->title),start_token,c_token-1);
			}

			this_plot->line_type = line_num;
			this_plot->point_type = point_num;

			if (almost_equals(c_token,"w$ith")) {
			    this_plot->plot_style = get_style();
			}

			if ( !equals(c_token,",") && !END_OF_COMMAND ) {
				struct value t;
				this_plot->line_type = (int)real(const_express(&t))-1;
			}
			if ( !equals(c_token,",") && !END_OF_COMMAND ) {
				struct value t;
				this_plot->point_type = (int)real(const_express(&t))-1;
			}
			if ( (this_plot->plot_style == POINTS) ||
				 (this_plot->plot_style == LINESPOINTS) )
					point_num++;
			line_num++;

			tp_ptr = &(this_plot->next_cp);
		}

		if (equals(c_token,",")) 
			c_token++;
		else  
			break;
	}

/*** Second Pass: Evaluate the functions ***/
/* Everything is defined now, except the function data. We expect
 * no syntax errors, etc, since the above parsed it all. This makes 
 * the code below simpler. If autoscale_ly, the yrange may still change.
 */
     if (xmin == xmax)
	  if (autoscale_lx) {
		 fprintf(stderr, "Warning: empty x range [%g:%g], ", xmin,xmax);
		 if (xmin == 0.0) {
			/* completely arbitary */
			xmin = -1.;
			xmax = 1.;
		 } else {
			/* expand range by 10% in either direction */
			xmin = xmin * 0.9;
			xmax = xmax * 1.1;
		 }
		 fprintf(stderr, "adjusting to [%g:%g]\n", xmin,xmax);
	  } else {
		 int_error("x range is empty", c_token);
	  }

    if (log_x) {
	   if (xmin <= 0.0 || xmax <= 0.0)
		int_error("x range must be greater than 0 for log scale!",NO_CARET);
	   x_min = log10(xmin);
	   x_max = log10(xmax);
    } else {
	   x_min = xmin;
	   x_max = xmax;
    }
    
    xdiff = (x_max - x_min) / mysamples;
    
    plot_num = 0;
    this_plot = first_plot;
    c_token = begin_token;	/* start over */

    /* Read through functions */
	while (TRUE) {
		if (is_definition(c_token)) {
			define();
		} else {
			plot_num++;
			if (isstring(c_token)) {			/* data file to plot */
			    /* ignore this now */
				c_token++;
			} 
			else {							/* function to plot */
				dummy_func = &plot_func;
				plot_func.at = temp_at(); /* reparse function */
    
				for (i = 0; i <= mysamples; i++) {
				    if (i == samples+1)
					 int_error("number of points exceeded samples",
							 NO_CARET);
				    x = x_min + i*xdiff;
				    /* if (log_x) PEM fix logscale x axis */
				    /* x = pow(10.0,x); 26-Sep-89 */
				    (void) complex(&plot_func.dummy_value,
							    log_x ? pow(10.0,x) : x,
							    0.0);
				    
				    evaluate_at(plot_func.at,&a);
				    
				    if (undefined || (fabs(imag(&a)) > zero)) {
					   this_plot->points[i].type = UNDEFINED;
					   continue;
				    }
				    
				    temp = real(&a);

				    if (log_y && temp < 0.0) {
					   this_plot->points[i].type = UNDEFINED;
					   continue;
				    }

				    this_plot->points[i].x = x;
				    if (log_y) {
					   if (temp == 0.0) {
						  this_plot->points[i].type = OUTRANGE;
						  this_plot->points[i].y = -VERYLARGE;
						  continue;
					   } else {
						  this_plot->points[i].y = log10(temp);
					   }
				    } else
					 this_plot->points[i].y = temp;

				    if (autoscale_ly || polar
					   || inrange(temp, ymin, ymax)) {
					   this_plot->points[i].type = INRANGE;
					   if (autoscale_ly && temp < ymin) ymin = temp;
					   if (autoscale_ly && temp > ymax) ymax = temp;
				    } else
					 this_plot->points[i].type = OUTRANGE;
				}
				this_plot->p_count = i; /* mysamples + 1 */
			 }
			
			/* title was handled above */
			if (almost_equals(c_token,"t$itle")) {
				c_token++;
				c_token++;
			}

			/* style was handled above */
			if (almost_equals(c_token,"w$ith")) {
			    c_token++;
			    c_token++;
			}

			/* line and point types were handled above */
			if ( !equals(c_token,",") && !END_OF_COMMAND ) {
				struct value t;
				(void)real(const_express(&t));
			}
			if ( !equals(c_token,",") && !END_OF_COMMAND ) {
				struct value t;
				(void)real(const_express(&t));
			}

			this_plot = this_plot->next_cp;
		 }
		
		if (equals(c_token,",")) 
		  c_token++;
		else  
		  break;
	 }

    if (ymin == ymax)
	 /* if autoscale, widen range */
	 if (autoscale_ly) {
		fprintf(stderr, "Warning: empty y range [%g:%g], ", ymin, ymax);
		if (ymin == 0.0) {
		    ymin = -1.;
		    ymax = 1.;
		} else {
		    /* expand range by 10% in either direction */
		    ymin = ymin * 0.9;
		    ymax = ymax * 1.1;
		}
		fprintf(stderr, "adjusting to [%g:%g]\n", ymin, ymax);
	 } else {
		int_error("y range is empty", c_token);
	 }

/* Now we finally know the real ymin and ymax */
	if (log_y) {
		y_min = log10(ymin);
		y_max = log10(ymax);
	} else {
		y_min = ymin;
		y_max = ymax;
	}
	capture(replot_line,plot_token,c_token);	
	do_plot(first_plot,plot_num,x_min,x_max,y_min,y_max);
}



done(status)
int status;
{
	if (term && term_init)
		(*term_tbl[term].reset)();
#ifdef VMS
	vms_reset();
#endif
	exit(status);
}

#ifdef MSDOS
#ifndef __TURBOC__	/* Turbo C already has sleep() */
#ifndef __ZTC__ 	/* ZTC already has usleep() */
/* kludge to provide sleep() for msc 5.1 */
void sleep(delay)
unsigned int delay;
{
unsigned long time_is_up;
	time_is_up = time(NULL) + (unsigned long) delay; 
	while (time(NULL)<time_is_up)
		/* wait */ ;
}
#endif /* not ZTC */
#endif /* not TURBOC */
#endif /* MSDOS */


/* Support for input, shell, and help for various systems */

#ifdef vms

#include <descrip.h>
#include <rmsdef.h>
#include <errno.h>

extern lib$get_input(), lib$put_output();

int vms_len;

unsigned int status[2] = {1, 0};

static char help[MAX_LINE_LEN+1] = "gnuplot";

$DESCRIPTOR(prompt_desc,PROMPT);
$DESCRIPTOR(line_desc,input_line);

$DESCRIPTOR(help_desc,help);
$DESCRIPTOR(helpfile_desc,"GNUPLOT$HELP");


read_line(prompt)
char *prompt;
{
    int more, start=0;
    char expand_prompt[40];

    prompt_desc.dsc$w_length = strlen (prompt);
    prompt_desc.dsc$a_pointer = prompt;
    (void) strcpy (expand_prompt, "_");
    (void) strncat (expand_prompt, prompt, 38);
    do {
        line_desc.dsc$w_length = MAX_LINE_LEN - start;
        line_desc.dsc$a_pointer = &input_line[start];
        switch(status[1] = lib$get_input(&line_desc, &prompt_desc, &vms_len)){
		  case RMS$_EOF:
		  done(IO_SUCCESS);	/* ^Z isn't really an error */
		  break;
		  case RMS$_TNS:	/* didn't press return in time *
						   /
						   vms_len--; /* skip the last character */
		  break;			/* and parse anyway */
		  case RMS$_BES:	/* Bad Escape Sequence */
		  case RMS$_PES:	/* Partial Escape Sequence */
		  sys$putmsg(status);
		  vms_len = 0;		/* ignore the line */
		  break;
		  case SS$_NORMAL:
		  break;			/* everything's fine */
		  default:
		  done(status[1]);	/* give the error message */
        }
        start += vms_len;
        input_line[start] = '\0';
	   inline_num++;
        if (input_line[start-1] == '\\') {
		  /* Allow for a continuation line. */
		  prompt_desc.dsc$w_length = strlen (expand_prompt);
		  prompt_desc.dsc$a_pointer = expand_prompt;
		  more = 1;
		  --start;
        }
        else {
		  line_desc.dsc$w_length = strlen(input_line);
		  line_desc.dsc$a_pointer = input_line;
		  more = 0;
        }
    } while (more);
}


do_help()
{
	help_desc.dsc$w_length = strlen(help);
	if ((vaxc$errno = lbr$output_help(lib$put_output,0,&help_desc,
		&helpfile_desc,0,lib$get_input)) != SS$_NORMAL)
			os_error("can't open GNUPLOT$HELP");
}


do_shell()
{
	if ((vaxc$errno = lib$spawn()) != SS$_NORMAL) {
		os_error("spawn error",NO_CARET);
	}
}


do_system()
{
	input_line[0] = ' ';	/* an embarrassment, but... */

	if ((vaxc$errno = lib$spawn(&line_desc)) != SS$_NORMAL)
		os_error("spawn error",NO_CARET);

	(void) putc('\n',stderr);
}

#else /* vms */

/* do_help: (not VMS, although it would work)
 * Give help to the user. 
 * It parses the command line into helpbuf and supplies help for that 
 * string. Then, if there are subtopics available for that key,
 * it prompts the user with this string. If more input is
 * given, do_help is called recursively, with the argument the index of 
 * null character in the string. Thus a more specific help can be 
 * supplied. This can be done repeatedly. 
 * If null input is given, the function returns, effecting a
 * backward climb up the tree.
 * David Kotz (dfk@cs.duke.edu) 10/89
 */
do_help()
{
    static char helpbuf[MAX_LINE_LEN] = "";
    static char prompt[MAX_LINE_LEN] = "";
    int base;				/* index of first char AFTER help string */
    int len;				/* length of current help string */
    BOOLEAN more_help;
    BOOLEAN only;			/* TRUE if only printing subtopics */
    int subtopics;			/* 0 if no subtopics for this topic */
    int start;				/* starting token of help string */
	char *help_ptr;			/* name of help file */

	if ( (help_ptr = getenv("GNUHELP")) == (char *)NULL )
		/* if can't find environment variable then just use HELPFILE */
		help_ptr = HELPFILE;

    len = base = strlen(helpbuf);

    /* find the end of the help command */
    for (start = c_token; !(END_OF_COMMAND); c_token++)
	 ;
    /* copy new help input into helpbuf */
    if (len > 0)
	 helpbuf[len++] = ' ';	/* add a space */
    capture(helpbuf+len, start, c_token-1);
    squash_spaces(helpbuf+base); /* only bother with new stuff */
    lower_case(helpbuf+base); /* only bother with new stuff */
    len = strlen(helpbuf);

    /* now, a lone ? will print subtopics only */
    if (strcmp(helpbuf + (base ? base+1 : 0), "?") == 0) {
	   /* subtopics only */
	   subtopics = 1;
	   only = TRUE;
	   helpbuf[base] = '\0';	/* cut off question mark */
    } else {
	   /* normal help request */
	   subtopics = 0;
	   only = FALSE;
    }

    switch (help(helpbuf, help_ptr, &subtopics)) {
	   case H_FOUND: {
		  /* already printed the help info */
		  /* subtopics now is true if there were any subtopics */
		  screen_ok = FALSE;
    
		  do {
			 if (subtopics && !only) {
				/* prompt for subtopic with current help string */
				if (len > 0)
				  (void) sprintf(prompt, "Subtopic of %s: ", helpbuf);
				else
				  (void) strcpy(prompt, "Help topic: ");
				read_line(prompt);
				num_tokens = scanner(input_line);
				c_token = 0;
				more_help = !(END_OF_COMMAND);
				if (more_help)
				  /* base for next level is all of current helpbuf */
				  do_help();
			 } else 
			   more_help = FALSE;
		  } while(more_help);
    
		  break;
	   }
	   case H_NOTFOUND: {
		  printf("Sorry, no help for '%s'\n", helpbuf);
		  break;
	   }
	   case H_ERROR: {
		  perror(help_ptr);
		  break;
	   }
	   default: {		/* defensive programming */
		  int_error("Impossible case in switch\n", NO_CARET);
		  /* NOTREACHED */
	   }
    }
    
    helpbuf[base] = '\0';	/* cut it off where we started */
}

do_system()
{
#ifndef TOS
	if (system(input_line + 1))
#else /* TOS */
      /* let the shell (if it exists) do the job */
	char buf[MAX_LINE_LEN+40];
	char * shell;
	if( (shell = getenv("SHELL")) != NULL )
		strcpy(buf,shell);
	strcat(buf," ");
	strcat(buf,input_line+1);
	if (system( buf ))
#endif
		os_error("system() failed",NO_CARET);
}

#ifdef MSDOS

read_line(prompt)
	char *prompt;
{
    register int i;
    int start = 0;
    BOOLEAN more;
    int last;
    
#ifndef __ZTC__
	if (interactive) { /* if interactive use console IO so CED will work */
		cputs(prompt);
		do {
		   input_line[start] = MAX_LINE_LEN - start - 1;
		   cgets(&(input_line[start]));
		   (void) putc('\n',stderr);
		   if (input_line[start+2] == 26) {
			  /* end-of-file */
			  (void) putc('\n',stderr);
			  input_line[start] = '\0';
			  inline_num++;
			  if (start > 0)	/* don't quit yet - process what we have */
				more = FALSE;
			  else {
				 (void) putc('\n',stderr);
				 done(IO_SUCCESS);
				 /* NOTREACHED */
			  }
		   } else {
			  /* normal line input */
			  register i = start;
			  while ( (input_line[i] = input_line[i+2]) != (char)NULL )
				i++;		/* yuck!  move everything down two characters */

			  inline_num++;
			  last = strlen(input_line) - 1;
			  if (last + 1 >= MAX_LINE_LEN)
				int_error("Input line too long",NO_CARET);
					 
			  if (input_line[last] == '\\') { /* line continuation */
				 start = last;
				 more = TRUE;
			  } else
				more = FALSE;
		   }
		   if (more)
			cputs("> ");
		} while(more);
	}
	else { /* not interactive */
#endif /* not ZTC */
		if (interactive)
		 fputs(prompt,stderr);
		do {
		   /* grab some input */
		   if ( fgets(&(input_line[start]), MAX_LINE_LEN - start, stdin) 
					== (char *)NULL ) {
			  /* end-of-file */
			  if (interactive)
				(void) putc('\n',stderr);
			  input_line[start] = '\0';
			  inline_num++;
			  if (start > 0)	/* don't quit yet - process what we have */
				more = FALSE;
			  else
				done(IO_SUCCESS); /* no return */
		   } else {
			  /* normal line input */
			  last = strlen(input_line) - 1;
			  if (input_line[last] == '\n') { /* remove any newline */
				 input_line[last] = '\0';
				 /* Watch out that we don't backup beyond 0 (1-1-1) */
				 if (last > 0) --last;
				 inline_num++;
			  } else if (last+1 >= MAX_LINE_LEN)
				int_error("Input line too long",NO_CARET);
					 
			  if (input_line[last] == '\\') { /* line continuation */
				 start = last;
				 more = TRUE;
			  } else
				more = FALSE;
		   }
			if (more && interactive)
			fputs("> ", stderr);
		} while(more);
#ifndef __ZTC
	}
#endif
}


do_shell()
{
register char *comspec;
	if ((comspec = getenv("COMSPEC")) == (char *)NULL)
		comspec = "\command.com";
	if (spawnl(P_WAIT,comspec,NULL) == -1)
		os_error("unable to spawn shell",NO_CARET);
}

#else /* MSDOS */
		/* plain old Unix */

read_line(prompt)
	char *prompt;
{
    int start = 0;
    BOOLEAN more;
    int last = 0;

    if (interactive)
	 fputs(prompt,stderr);
    do {
	   /* grab some input */
	   if ( fgets(&(input_line[start]), MAX_LINE_LEN - start, stdin) 
				== (char *)NULL ) {
		  /* end-of-file */
		  if (interactive)
		    (void) putc('\n',stderr);
		  input_line[start] = '\0';
		  inline_num++;
		  if (start > 0)	/* don't quit yet - process what we have */
		    more = FALSE;
		  else
		    done(IO_SUCCESS); /* no return */
	   } else {
		  /* normal line input */
		  last = strlen(input_line) - 1;
		  if (input_line[last] == '\n') { /* remove any newline */
			 input_line[last] = '\0';
                /* Watch out that we don't backup beyond 0 (1-1-1) */
			 if (last > 0) --last;
			 inline_num++;
		  } else if (last+1 >= MAX_LINE_LEN)
		    int_error("Input line too long",NO_CARET);
				 
		  if (input_line[last] == '\\') { /* line continuation */
			 start = last;
			 more = TRUE;
		  } else
		    more = FALSE;
	   }
        if (more && interactive)
		fputs("> ", stderr);
    } while(more);
}

#ifdef VFORK

do_shell()
{
register char *shell;
register int p;
static int execstat;
	if (!(shell = getenv("SHELL")))
		shell = SHELL;
	if ((p = vfork()) == 0) {
		execstat = execl(shell,shell,NULL);
		_exit(1);
	} else if (p == -1)
		os_error("vfork failed",c_token);
	else
		while (wait(NULL) != p)
			;
	if (execstat == -1)
		os_error("shell exec failed",c_token);
	(void) putc('\n',stderr);
}
#else /* VFORK */

#define EXEC "exec "
do_shell()
{
static char exec[100] = EXEC;
register char *shell;
	if (!(shell = getenv("SHELL")))
		shell = SHELL;

	if (system(strncpy(&exec[sizeof(EXEC)-1],shell,
		sizeof(exec)-sizeof(EXEC)-1)))
		os_error("system() failed",NO_CARET);

	(void) putc('\n',stderr);
}
#endif /* VFORK */
#endif /* MSDOS */
#endif /* vms */
