DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T c

⟦a8a28ae41⟧ TextFile

    Length: 21346 (0x5362)
    Types: TextFile
    Names: »command.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/gnuplot/command.c« 

TextFile

/*
 *
 *    G N U P L O T  --  command.c
 *
 *  Copyright (C) 1986, 1987  Thomas Williams, Colin Kelley
 *
 *  You may use this code as you wish if credit is given and this message
 *  is retained.
 *
 *  Please e-mail any useful additions to vu-vlsi!plot so they may be
 *  included in later releases.
 *
 *  This file should be edited with 4-column tabs!  (:set ts=4 sw=4 in vi)
 */

#include <stdio.h>
#include <math.h>

#ifdef MSDOS
#include <process.h>
#endif

#include "plot.h"

#ifndef STDOUT
#define STDOUT 1
#endif

/*
 * global variables to hold status of 'set' options
 *
 */
BOOLEAN			autoscale	= TRUE;
char			dummy_var[MAX_ID_LEN+1] = "x";
enum PLOT_STYLE data_style	= POINTS,
				func_style	= LINES;
BOOLEAN			log_x		= FALSE,
				log_y		= FALSE;
FILE*			outfile;
char			outstr[MAX_ID_LEN+1] = "STDOUT";
int				samples		= SAMPLES;
int				term		= 0;				/* unknown term is 0 */
double			xmin		= -10.0,
				xmax		= 10.0,
				ymin		= -10.0,
				ymax		= 10.0;
double			zero = ZERO;			/* zero threshold, not 0! */


BOOLEAN screen_ok;
BOOLEAN term_init;
BOOLEAN undefined;

/*
 * instead of <strings.h>
 */

char *gets(),*getenv();
char *strcpy(),*strncpy(),*strcat();

char *malloc();

double magnitude(),angle(),real(),imag();
struct value *const_express(), *pop(), *complex();
struct at_type *temp_at(), *perm_at();
struct udft_entry *add_udf();
struct udvt_entry *add_udv();

extern struct termentry term_tbl[];

struct lexical_unit token[MAX_TOKENS];
char input_line[MAX_LINE_LEN+1] = "set term ";
char c_dummy_var[MAX_ID_LEN+1]; 		/* current dummy var */
int num_tokens, c_token;

struct curve_points *first_plot = NULL;
struct udft_entry plot_func, *dummy_func;

static char replot_line[MAX_LINE_LEN+1];
static int plot_token;					/* start of 'plot' command */
static char help[MAX_LINE_LEN] = HELP;

com_line()
{
	read_line();

	screen_ok = TRUE; /* so we can flag any new output */

	do_line();
}


do_line()	  /* also used in load_file */
{
	if (is_comment(input_line[0]))
		return;
	if (is_system(input_line[0])) {
		do_system();
		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()
{
static 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,"?")) {
		register int len;
		register char *help_ptr;

		c_token++;
		if ((help_ptr = getenv("GNUHELP")))	/* initial command */
			(void) strncpy(help,help_ptr,sizeof(help) - 1);
		else
			(void) strncpy(help,HELP,sizeof(help) - 1);

		while (!(END_OF_COMMAND)) {
			len = strlen(help);
			help[len] = ' ';   /* put blank between help segments */
			copy_str(help+len+1,c_token++);
		}
		do_help();
		screen_ok = FALSE;
		c_token++;
	}
	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);
		(void) strcpy(input_line,replot_line);
		screen_ok = FALSE;
		num_tokens = scanner(input_line);
		c_token = 1;					/* skip the 'plot' part */
		plotrequest();
	}
	else if (almost_equals(c_token,"se$t"))
		set_stuff();
	else if (almost_equals(c_token,"sh$ow"))
		show_stuff();
	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 (isstring(c_token)) {
			quote_str(sv_file,c_token);
			save_all(fopen(sv_file,"w"));
		}
		else {
			int_error(
		"filename or keyword 'functions' or 'variables' 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"));	
		/* 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);
	}
}


enum PLOT_STYLE
get_style()
{
register enum PLOT_STYLE ps;

	c_token++;
	if (almost_equals(c_token,"l$ines"))
		ps = LINES;
	else if (almost_equals(c_token,"i$mpulses"))
		ps = IMPULSES;
	else if (almost_equals(c_token,"p$oints"))
		ps = POINTS;
	else
		int_error("expecting 'lines', 'points', or 'impulses'",c_token);
	c_token++;
	return(ps);
}


set_stuff()
{
static char testfile[MAX_LINE_LEN+1];

	if (almost_equals(++c_token,"a$utoscale")) {
		autoscale = TRUE;
		c_token++;
	}
	else if (almost_equals(c_token,"noa$utoscale")) {
		autoscale = FALSE;
		c_token++;
	}
	else if (almost_equals(c_token,"d$ata")) {
		c_token++;
		if (!almost_equals(c_token,"s$tyle"))
			int_error("expecting keyword 'style'",c_token);
		data_style = get_style();
	}
	else if (almost_equals(c_token,"d$ummy")) {
		c_token++;
		copy_str(dummy_var,c_token++);
	}
	else if (almost_equals(c_token,"f$unction")) {
		c_token++;
		if (!almost_equals(c_token,"s$tyle"))
			int_error("expecting keyword 'style'",c_token);
		func_style = get_style();
	}
	else if (almost_equals(c_token,"l$ogscale")) {
		c_token++;
		if (equals(c_token,"x")) {
			log_y = FALSE;
			log_x = TRUE;
			c_token++;
		}
		else if (equals(c_token,"y")) {
			log_x = FALSE;
			log_y = TRUE;
			c_token++;
		}
		else if (equals(c_token,"xy") || equals(c_token,"yx")) {
			log_x = log_y = TRUE;
			c_token++;
		}
		else
			int_error("expecting 'x', 'y', or 'xy'",c_token);
	}
	else if (almost_equals(c_token,"nol$ogscale")) {
		log_x = log_y = FALSE;
		c_token++;
	}
	else if (almost_equals(c_token,"o$utput")) {
		register FILE *f;

		c_token++;
		if (END_OF_COMMAND) {	/* no file specified */
			(void) fclose(outfile);
			outfile = fdopen(dup(STDOUT), "w");
			term_init = FALSE;
			(void) strcpy(outstr,"STDOUT");
		} else if (!isstring(c_token))
			int_error("expecting filename",c_token);
		else {
			quote_str(testfile,c_token);
			if (!(f = fopen(testfile,"w"))) {
			  os_error("cannot open file; output not changed",c_token);
			}
			(void) fclose(outfile);
			outfile = f;
			term_init = FALSE;
			outstr[0] = '\'';
			(void) strcat(strcpy(outstr+1,testfile),"'");
		}
		c_token++;
	}
	else if (almost_equals(c_token,"sa$mples")) {
		register int tsamp;
		struct value a;

		c_token++;
		tsamp = (int)magnitude(const_express(&a));
		if (tsamp < 1)
			int_error("sampling rate must be > 0; sampling unchanged",
				c_token);
		else {
			register struct curve_points *f_p = first_plot;

			first_plot = NULL;
			cp_free(f_p);
			samples = tsamp;
		}
	}
	else if (almost_equals(c_token,"t$erminal")) {
		c_token++;
		if (END_OF_COMMAND) {
			list_terms();
			screen_ok = FALSE;
		}
		else
			term = set_term(c_token);
		c_token++;
	}
	else if (almost_equals(c_token,"x$range")) {
		c_token++;
		if (!equals(c_token,"["))
			int_error("expecting '['",c_token);
		c_token++;
		load_range(&xmin,&xmax);
		if (!equals(c_token,"]"))
			int_error("expecting ']'",c_token);
		c_token++;
	}
	else if (almost_equals(c_token,"y$range")) {
		c_token++;
		if (!equals(c_token,"["))
			int_error("expecting '['",c_token);
		c_token++;
		load_range(&ymin,&ymax);
		autoscale = FALSE;
		if (!equals(c_token,"]"))
			int_error("expecting ']'",c_token);
		c_token++;
	}
	else if (almost_equals(c_token,"z$ero")) {
		struct value a;
		c_token++;
		zero = magnitude(const_express(&a));
	}
	else
		int_error(
	"valid set options:  '[no]autoscale', 'data', 'dummy', 'function',\n\
'[no]logscale', 'output', 'samples', 'terminal', 'xrange', 'yrange', 'zero'",
	c_token);
}


show_stuff()
{
	if (almost_equals(++c_token,"ac$tion_table") ||
			 equals(c_token,"at") ) {
		c_token++;
		show_at();
		c_token++;
	}
	else if (almost_equals(c_token,"au$toscale")) {
		(void) putc('\n',stderr);
		show_autoscale();
		c_token++;
	}
	else if (almost_equals(c_token,"d$ata")) {
		c_token++;
		if (!almost_equals(c_token,"s$tyle"))
			int_error("expecting keyword 'style'",c_token);
		(void) putc('\n',stderr);
		show_style("data",data_style);
		c_token++;
	}
	else if (almost_equals(c_token,"d$ummy")) {
		fprintf(stderr,"\n\tdummy variable is %s\n",dummy_var);
		c_token++;
	}
	else if (almost_equals(c_token,"f$unctions")) {
		c_token++;
		if (almost_equals(c_token,"s$tyle"))  {
			(void) putc('\n',stderr);
			show_style("functions",func_style);
			c_token++;
		}
		else
			show_functions();
	}
	else if (almost_equals(c_token,"l$ogscale")) {
		(void) putc('\n',stderr);
		show_logscale();
		c_token++;
	}
	else if (almost_equals(c_token,"o$utput")) {
		(void) putc('\n',stderr);
		show_output();
		c_token++;
	}
	else if (almost_equals(c_token,"sa$mples")) {
		(void) putc('\n',stderr);
		show_samples();
		c_token++;
	}
	else if (almost_equals(c_token,"t$erminal")) {
		(void) putc('\n',stderr);
		show_term();
		c_token++;
	}
	else if (almost_equals(c_token,"v$ariables")) {
		show_variables();
		c_token++;
	}
	else if (almost_equals(c_token,"ve$rsion")) {
		show_version();
		c_token++;
	}
	else if (almost_equals(c_token,"x$range")) {
		(void) putc('\n',stderr);
		show_range('x',xmin,xmax);
		c_token++;
	}
	else if (almost_equals(c_token,"y$range")) {
		(void) putc('\n',stderr);
		show_range('y',ymin,ymax);
		c_token++;
	}
	else if (almost_equals(c_token,"z$ero")) {
		(void) putc('\n',stderr);
		show_zero();
		c_token++;
	}
	else if (almost_equals(c_token,"a$ll")) {
		c_token++;
		show_version();
		fprintf(stderr,"\tdummy variable is %s\n",dummy_var);
		show_style("data",data_style);
		show_style("functions",func_style);
		show_output();
		show_term();
		show_samples();
		show_logscale();
		show_autoscale();
		show_zero();
		show_range('x',xmin,xmax);
		show_range('y',ymin,ymax);
		show_variables();
		show_functions();
		c_token++;
	}
	else
		int_error(
	"valid show options:  'action_table', 'all', 'autoscale', 'data',\n\
'dummy', 'function', 'logscale', 'output', 'samples', 'terminal',\n\
'variables', 'version', 'xrange', 'yrange', 'zero'", c_token);
	screen_ok = FALSE;
	(void) putc('\n',stderr);
}


load_range(a,b)
double *a,*b;
{
struct value t;

	if (equals(c_token,"]"))
		return;
	if (END_OF_COMMAND) {
	    int_error("starting range value or ':' expected",c_token);
	} else if (!equals(c_token,"to") && !equals(c_token,":"))  {
		*a = real(const_express(&t));
	}	
	if (!equals(c_token,"to") && !equals(c_token,":"))
		int_error("':' expected",c_token);
	c_token++;
	if (!equals(c_token,"]"))
		*b = real(const_express(&t));
}


plotrequest()
{
	
	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)) {
			copy_str(c_dummy_var,c_token++);
			if (equals(c_token,"="))
				c_token++;
			else
				int_error("'=' expected",c_token);
		}
		load_range(&xmin,&xmax);
		if (!equals(c_token,"]"))
			int_error("']' expected",c_token);
		c_token++;
	}

	if (equals(c_token,"[")) { /* set optional y ranges */
		c_token++;
		load_range(&ymin,&ymax);
		autoscale = FALSE;
		if (!equals(c_token,"]"))
			int_error("']' expected",c_token);
		c_token++;
	}

	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()))
			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, overflow, l_num;
register FILE *fp;
float x, y;

	quote_str(data_file, c_token);
	this_plot->plot_type = DATA;
	if (!(fp = fopen(data_file, "r")))
		os_error("can't open data file", c_token);

	l_num = 0;

	overflow = i = 0;

	while (fgets(line, MAX_LINE_LEN, fp)) {
		l_num++;
		if (is_comment(line[0]) || ! line[1])	/* line[0] will be '\n' */
			continue;		/* ignore comments  and blank lines */

		if (i == samples+1) {
			overflow = i;	/* keep track for error message later */
			i--;			/* so we don't fall off end of points[i] */
		}
		switch (sscanf(line, "%f %f", &x, &y)) {
			case 1:			/* only one number on the line */
				y = x;		/* so use it as the y value, */
				x = i;		/* and use the index as the x */
			/* no break; !!! */
			case 2:
				this_plot->points[i].undefined = TRUE;
				if (x >= xmin && x <= xmax && (autoscale ||
					(y >= ymin && y <= ymax))) {
					if (log_x) {
						if (x <= 0.0)
							break;
						this_plot->points[i].x = log10(x);
					} else
						this_plot->points[i].x = x;
					if (log_y) {
						if (y <= 0.0)
							break;
						this_plot->points[i].y = log10(y);
					} else
						this_plot->points[i].y = y;
					if (autoscale) {
						if (y < ymin) ymin = y;
						if (y > ymax) ymax = y;
					}

					this_plot->points[i].undefined = FALSE;
				}
				if (overflow)
					overflow++;
				else
					i++;
				break;

			default:
				(void) sprintf(line, "bad data on line %d", l_num);
				int_error(line,c_token);
		}
	}
	if (overflow) {
		(void) sprintf(line,
	"%d data points found--samples must be set at least this high",overflow);
				/* actually, samples can be one less! */
		int_error(line,c_token);
	}
	this_plot->p_count = i;
	(void) fclose(fp);
}


eval_plots()
{
register int i;
register struct curve_points *this_plot, **tp_ptr;
register int start_token, mysamples;
register double x_min, x_max, y_min, y_max, x;
register double xdiff, temp;
register int plot_num;
static struct value a;

	/* don't sample higher than output device can handle! */
	mysamples = (samples <= term_tbl[term].xmax) ?samples :term_tbl[term].xmax;

	if (log_x) {
		if (xmin < 0.0 || xmax < 0.0)
			int_error("x range must be above 0 for log scale!",NO_CARET);
		x_min = log10(xmin);
		x_max = log10(xmax);
	} else {
		x_min = xmin;
		x_max = xmax;
	}

	if (autoscale) {
		ymin = HUGE;
		ymax = -HUGE;
	} else if (log_y && (ymin <= 0.0 || ymax <= 0.0))
			int_error("y range must be above 0 for log scale!",
				NO_CARET);

	xdiff = (x_max - x_min) / mysamples;

	tp_ptr = &(first_plot);
	plot_num = 0;

	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 *)
					malloc((unsigned int) (sizeof(struct curve_points) -
					(MAX_POINTS - (samples+1))*sizeof(struct coordinate)));
				if (!this_plot)
					int_error("out of memory",c_token);
				this_plot->next_cp = NULL;
				this_plot->title = NULL;
				*tp_ptr = this_plot;
			}

			if (isstring(c_token)) {			/* data file to plot */
				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;

				(void) strcpy(c_dummy_var,dummy_var);
				dummy_func = &plot_func;

				plot_func.at = temp_at();

				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)
						x = pow(10.0,x);
					(void) complex(&plot_func.dummy_value, x, 0.0);
					
					evaluate_at(plot_func.at,&a);

					if (this_plot->points[i].undefined =
						undefined || (fabs(imag(&a)) > zero))
							continue;

					temp = real(&a);

					if (log_y && temp <= 0.0) {
							this_plot->points[i].undefined = TRUE;
							continue;
					}
					if (autoscale) {
						if (temp < ymin) ymin = temp;
						if (temp > ymax) ymax = temp;
					} else if (temp < ymin || temp > ymax) {
						this_plot->points[i].undefined = TRUE;
						continue;
					}

					this_plot->points[i].y = log_y ? log10(temp) : temp;
				}
				this_plot->p_count = i; /* mysamples + 1 */
			}
			m_capture(&(this_plot->title),start_token,c_token-1);
			if (almost_equals(c_token,"w$ith"))
				this_plot->plot_style = get_style();
			tp_ptr = &(this_plot->next_cp);
		}

		if (equals(c_token,","))
			c_token++;
		else
			break;
	}

	if (autoscale && (ymin == ymax))
		ymax += 1.0;	/* kludge to avoid divide-by-zero in do_plot */

	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_tbl[term].reset)();
	exit(status);
}


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

$DESCRIPTOR(prompt_desc,PROMPT);
$DESCRIPTOR(line_desc,input_line);

$DESCRIPTOR(help_desc,help);
$DESCRIPTOR(helpfile_desc,"GNUPLOT$HELP");


read_line()
{
	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 */
	}
	input_line[vms_len] = '\0';
}


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()
{
	if (system(help))
		os_error("can't spawn help",c_token);
}


do_system()
{
	if (system(input_line + 1))
		os_error("system() failed",NO_CARET);
}

#ifdef MSDOS

read_line()
{
register int i;

	input_line[0] = MAX_LINE_LEN - 1;
	cputs(PROMPT);
	cgets(input_line);			/* console input so CED will work */
	(void) putc('\n',stderr);
	if (input_line[2] == 26) {
		(void) putc('\n',stderr);		/* end-of-file */
		done(IO_SUCCESS);
	}

	i = 0;
	while (input_line[i] = input_line[i+2])
		i++;		/* yuck!  move everything down two characters */
}


do_shell()
{
register char *comspec;
	if (!(comspec = getenv("COMSPEC")))
		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()
{
	fputs(PROMPT,stderr);
	if (!gets(input_line)) {
		(void) putc('\n',stderr);		/* end-of-file */
		done(IO_SUCCESS);
	}
}

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