Blame dc/eval.c

Packit 70b277
/*
Packit 70b277
 * evaluate the dc language, from a FILE* or a string
Packit 70b277
 *
Packit 70b277
 * Copyright (C) 1994, 1997, 1998, 2000, 2003, 2005, 2006, 2008, 2010, 2012-2017
Packit 70b277
 * Free Software Foundation, Inc.
Packit 70b277
 *
Packit 70b277
 * This program is free software; you can redistribute it and/or modify
Packit 70b277
 * it under the terms of the GNU General Public License as published by
Packit 70b277
 * the Free Software Foundation; either version 3, or (at your option)
Packit 70b277
 * any later version.
Packit 70b277
 *
Packit 70b277
 * This program is distributed in the hope that it will be useful,
Packit 70b277
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 70b277
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 70b277
 * GNU General Public License for more details.
Packit 70b277
 *
Packit 70b277
 * You should have received a copy of the GNU General Public License
Packit 70b277
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 70b277
 *
Packit 70b277
 */
Packit 70b277
Packit 70b277
/* This is the only module which knows about the dc input language */
Packit 70b277
Packit 70b277
#include "config.h"
Packit 70b277
Packit 70b277
#include <stdio.h>
Packit 70b277
#ifdef HAVE_STRING_H
Packit 70b277
# include <string.h>	/* memchr */
Packit 70b277
#else
Packit 70b277
# ifdef HAVE_MEMORY_H
Packit 70b277
#  include <memory.h>	/* memchr, maybe */
Packit 70b277
# else
Packit 70b277
#  ifdef HAVE_STRINGS_H
Packit 70b277
#   include <strings.h>	/* memchr, maybe */
Packit 70b277
#  endif
Packit 70b277
#endif
Packit 70b277
#endif
Packit 70b277
#include <signal.h>
Packit 70b277
#ifdef HAVE_UNISTD_H
Packit 70b277
# include <unistd.h>
Packit 70b277
#endif
Packit 70b277
#include "dc.h"
Packit 70b277
#include "dc-proto.h"
Packit 70b277
Packit 70b277
typedef enum {DC_FALSE, DC_TRUE} dc_boolean;
Packit 70b277
Packit 70b277
typedef enum {
Packit 70b277
	DC_OKAY = DC_SUCCESS, /* no further intervention needed for this command */
Packit 70b277
	DC_EATONE,		/* caller needs to eat the lookahead char */
Packit 70b277
	DC_EVALREG,		/* caller needs to eval the string named by `peekc' */
Packit 70b277
	DC_EVALTOS,		/* caller needs to eval the string on top of the stack */
Packit 70b277
	DC_QUIT,		/* quit out of unwind_depth levels of evaluation */
Packit 70b277
	DC_INT,			/* caller needs to parse a dc_num from input stream */
Packit 70b277
	DC_STR,			/* caller needs to parse a dc_str from input stream */
Packit 70b277
	DC_SYSTEM,		/* caller needs to run a system() on next input line */
Packit 70b277
	DC_COMMENT,		/* caller needs to skip to the next input line */
Packit 70b277
	DC_NEGCMP,		/* caller needs to re-call dc_func() with `negcmp' set */
Packit 70b277
Packit 70b277
	DC_EOF_ERROR	/* unexpected end of input; abort current eval */
Packit 70b277
} dc_status;
Packit 70b277
Packit 70b277
static int dc_ibase=10;		/* input base, 2 <= dc_ibase <= DC_IBASE_MAX */
Packit 70b277
static int dc_obase=10;		/* output base, 2 <= dc_obase */
Packit 70b277
static int dc_scale=0;		/* scale (see user documentaton) */
Packit 70b277
Packit 70b277
/* for Quitting evaluations */
Packit 70b277
static int unwind_depth=0;
Packit 70b277
Packit 70b277
/* for handling SIGINT properly */
Packit 70b277
static volatile sig_atomic_t interrupt_seen=0;
Packit 70b277
Packit 70b277
/* if true, active Quit will not exit program */
Packit 70b277
static dc_boolean unwind_noexit=DC_FALSE;
Packit 70b277
Packit 70b277
/*
Packit 70b277
 * Used to synchronize lookahead on stdin for '?' command.
Packit 70b277
 * If set to EOF then lookahead is used up.
Packit 70b277
 */
Packit 70b277
static int stdin_lookahead=EOF;
Packit 70b277
Packit 70b277
/* forward reference */
Packit 70b277
static int evalstr(dc_data *string);
Packit 70b277
Packit 70b277

Packit 70b277
/* input_fil and input_str are passed as arguments to dc_getnum */
Packit 70b277
Packit 70b277
/* used by the input_* functions: */
Packit 70b277
static FILE *input_fil_fp;
Packit 70b277
static const char *input_str_string;
Packit 70b277
Packit 70b277
/* Since we have a need for two characters of pushback, and
Packit 70b277
 * ungetc() only guarantees one, we place the second pushback here
Packit 70b277
 */
Packit 70b277
static int input_pushback;
Packit 70b277
Packit 70b277
/* passed as an argument to dc_getnum */
Packit 70b277
static int
Packit 70b277
input_fil DC_DECLVOID()
Packit 70b277
{
Packit 70b277
	if (input_pushback != EOF){
Packit 70b277
		int c = input_pushback;
Packit 70b277
		input_pushback = EOF;
Packit 70b277
		return c;
Packit 70b277
	}
Packit 70b277
	return getc(input_fil_fp);
Packit 70b277
}
Packit 70b277
Packit 70b277
/* passed as an argument to dc_getnum */
Packit 70b277
static int
Packit 70b277
input_str DC_DECLVOID()
Packit 70b277
{
Packit 70b277
	if (*input_str_string == '\0')
Packit 70b277
		return EOF;
Packit 70b277
	return *(const unsigned char *)input_str_string++;
Packit 70b277
}
Packit 70b277
Packit 70b277

Packit 70b277
Packit 70b277
/* takes a string and evals it; frees the string when done */
Packit 70b277
/* Wrapper around evalstr to avoid duplicating the free call
Packit 70b277
 * at all possible return points.
Packit 70b277
 */
Packit 70b277
static int
Packit 70b277
dc_eval_and_free_str DC_DECLARG((string))
Packit 70b277
	dc_data *string DC_DECLEND
Packit 70b277
{
Packit 70b277
	dc_status status;
Packit 70b277
Packit 70b277
	status = evalstr(string);
Packit 70b277
	if (string->dc_type == DC_STRING)
Packit 70b277
		dc_free_str(&string->v.string);
Packit 70b277
	return status;
Packit 70b277
}
Packit 70b277
Packit 70b277

Packit 70b277
/* notice when an interrupt event happens */
Packit 70b277
static void
Packit 70b277
dc_trap_interrupt DC_DECLARG((signo))
Packit 70b277
	int signo DC_DECLEND
Packit 70b277
{
Packit 70b277
	signal(signo, dc_trap_interrupt);
Packit 70b277
	interrupt_seen = 1;
Packit 70b277
}
Packit 70b277
Packit 70b277

Packit 70b277
/* step pointer past next end-of-line (or to end-of-string) */
Packit 70b277
static const char *
Packit 70b277
skip_past_eol DC_DECLARG((strptr, strend))
Packit 70b277
	const char *strptr DC_DECLSEP
Packit 70b277
	const char *strend DC_DECLEND
Packit 70b277
{
Packit 70b277
	const char *p = memchr(strptr, '\n', (size_t)(strend-strptr));
Packit 70b277
	if (p != NULL)
Packit 70b277
		return p+1;
Packit 70b277
	return strend;
Packit 70b277
}
Packit 70b277
Packit 70b277

Packit 70b277
/* dc_func does the grunt work of figuring out what each input
Packit 70b277
 * character means; used by both dc_evalstr and dc_evalfile
Packit 70b277
 *
Packit 70b277
 * c -> the "current" input character under consideration
Packit 70b277
 * peekc -> the lookahead input character
Packit 70b277
 * negcmp -> negate comparison test (for <,=,> commands)
Packit 70b277
 */
Packit 70b277
static dc_status
Packit 70b277
dc_func DC_DECLARG((c, peekc, negcmp))
Packit 70b277
	int c DC_DECLSEP
Packit 70b277
	int peekc DC_DECLSEP
Packit 70b277
	int negcmp DC_DECLEND
Packit 70b277
{
Packit 70b277
	dc_data datum;
Packit 70b277
	int tmpint;
Packit 70b277
Packit 70b277
	switch (c){
Packit 70b277
	case '_': case '.':
Packit 70b277
	case '0': case '1': case '2': case '3':
Packit 70b277
	case '4': case '5': case '6': case '7':
Packit 70b277
	case '8': case '9': case 'A': case 'B':
Packit 70b277
	case 'C': case 'D': case 'E': case 'F':
Packit 70b277
		return DC_INT;
Packit 70b277
	case ' ':
Packit 70b277
	case '\t':
Packit 70b277
	case '\n':
Packit 70b277
		/* standard command separators */
Packit 70b277
		break;
Packit 70b277
Packit 70b277
	case '+':	/* add top two stack elements */
Packit 70b277
		dc_binop(dc_add, dc_scale);
Packit 70b277
		break;
Packit 70b277
	case '-':	/* subtract top two stack elements */
Packit 70b277
		dc_binop(dc_sub, dc_scale);
Packit 70b277
		break;
Packit 70b277
	case '*':	/* multiply top two stack elements */
Packit 70b277
		dc_binop(dc_mul, dc_scale);
Packit 70b277
		break;
Packit 70b277
	case '/':	/* divide top two stack elements */
Packit 70b277
		dc_binop(dc_div, dc_scale);
Packit 70b277
		break;
Packit 70b277
	case '%':
Packit 70b277
		/* take the remainder from division of the top two stack elements */
Packit 70b277
		dc_binop(dc_rem, dc_scale);
Packit 70b277
		break;
Packit 70b277
	case '~':
Packit 70b277
		/* Do division on the top two stack elements.  Return the
Packit 70b277
		 * quotient as next-to-top of stack and the remainder as
Packit 70b277
		 * top-of-stack.
Packit 70b277
		 */
Packit 70b277
		dc_binop2(dc_divrem, dc_scale);
Packit 70b277
		break;
Packit 70b277
	case '|':
Packit 70b277
		/* Consider the top three elements of the stack as (base, exp, mod),
Packit 70b277
		 * where mod is top-of-stack, exp is next-to-top, and base is
Packit 70b277
		 * second-from-top. Mod must be non-zero, exp must be non-negative,
Packit 70b277
		 * and all three must be integers. Push the result of raising
Packit 70b277
		 * base to the exp power, reduced modulo mod. If we had base in
Packit 70b277
		 * register b, exp in register e, and mod in register m then this
Packit 70b277
		 * is conceptually equivalent to "lble^lm%", but it is implemented
Packit 70b277
		 * in a more efficient manner, and can handle arbritrarily large
Packit 70b277
		 * values for exp.
Packit 70b277
		 */
Packit 70b277
		dc_triop(dc_modexp, dc_scale);
Packit 70b277
		break;
Packit 70b277
	case '^':	/* exponientiation of the top two stack elements */
Packit 70b277
		dc_binop(dc_exp, dc_scale);
Packit 70b277
		break;
Packit 70b277
	case '<':
Packit 70b277
		/* eval register named by peekc if
Packit 70b277
		 * less-than holds for top two stack elements
Packit 70b277
		 */
Packit 70b277
		if (peekc == EOF)
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		if ( (dc_cmpop() <  0) == (negcmp==0) )
Packit 70b277
			return DC_EVALREG;
Packit 70b277
		return DC_EATONE;
Packit 70b277
	case '=':
Packit 70b277
		/* eval register named by peekc if
Packit 70b277
		 * equal-to holds for top two stack elements
Packit 70b277
		 */
Packit 70b277
		if (peekc == EOF)
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		if ( (dc_cmpop() == 0) == (negcmp==0) )
Packit 70b277
			return DC_EVALREG;
Packit 70b277
		return DC_EATONE;
Packit 70b277
	case '>':
Packit 70b277
		/* eval register named by peekc if
Packit 70b277
		 * greater-than holds for top two stack elements
Packit 70b277
		 */
Packit 70b277
		if (peekc == EOF)
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		if ( (dc_cmpop() >  0) == (negcmp==0) )
Packit 70b277
			return DC_EVALREG;
Packit 70b277
		return DC_EATONE;
Packit 70b277
	case '?':	/* read a line from standard-input and eval it */
Packit 70b277
		if (stdin_lookahead != EOF){
Packit 70b277
			ungetc(stdin_lookahead, stdin);
Packit 70b277
			stdin_lookahead = EOF;
Packit 70b277
		}
Packit 70b277
		datum = dc_readstring(stdin, '\n', '\n');
Packit 70b277
		if (ferror(stdin))
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		dc_push(datum);
Packit 70b277
		return DC_EVALTOS;
Packit 70b277
	case '[':	/* read to balancing ']' into a dc_str */
Packit 70b277
		return DC_STR;
Packit 70b277
	case '!':	/* read to newline and call system() on resulting string */
Packit 70b277
		if (peekc == '<' || peekc == '=' || peekc == '>')
Packit 70b277
			return DC_NEGCMP;
Packit 70b277
		return DC_SYSTEM;
Packit 70b277
	case '#':	/* comment; skip remainder of current line */
Packit 70b277
		return DC_COMMENT;
Packit 70b277
Packit 70b277
	case 'a':	/* Convert top of stack to an ascii character. */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			char tmps;
Packit 70b277
			if (datum.dc_type == DC_NUMBER){
Packit 70b277
				tmps = (char) dc_num2int(datum.v.number, DC_TOSS);
Packit 70b277
			}else if (datum.dc_type == DC_STRING){
Packit 70b277
				tmps = *dc_str2charp(datum.v.string);
Packit 70b277
				dc_free_str(&datum.v.string);
Packit 70b277
			}else{
Packit 70b277
				dc_garbage("at top of stack", -1);
Packit 70b277
			}
Packit 70b277
			dc_push(dc_makestring(&tmps, 1));
Packit 70b277
		}
Packit 70b277
		break;
Packit 70b277
	case 'c':	/* clear whole stack */
Packit 70b277
		dc_clear_stack();
Packit 70b277
		break;
Packit 70b277
	case 'd':	/* duplicate the datum on the top of stack */
Packit 70b277
		if (dc_top_of_stack(&datum) == DC_SUCCESS)
Packit 70b277
			dc_push(dc_dup(datum));
Packit 70b277
		break;
Packit 70b277
	case 'f':	/* print list of all stack items */
Packit 70b277
		dc_printall(dc_obase);
Packit 70b277
		break;
Packit 70b277
	case 'i':	/* set input base to value on top of stack */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			tmpint = 0;
Packit 70b277
			if (datum.dc_type == DC_NUMBER)
Packit 70b277
				tmpint = dc_num2int(datum.v.number, DC_TOSS);
Packit 70b277
			if (2 <= tmpint  &&  tmpint <= DC_IBASE_MAX)
Packit 70b277
				dc_ibase = tmpint;
Packit 70b277
			else
Packit 70b277
				fprintf(stderr,
Packit 70b277
						"%s: input base must be a number \
Packit 70b277
between 2 and %d (inclusive)\n",
Packit 70b277
						progname, DC_IBASE_MAX);
Packit 70b277
		}
Packit 70b277
		break;
Packit 70b277
	case 'k':	/* set scale to value on top of stack */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			tmpint = -1;
Packit 70b277
			if (datum.dc_type == DC_NUMBER)
Packit 70b277
				tmpint = dc_num2int(datum.v.number, DC_TOSS);
Packit 70b277
			if ( ! (tmpint >= 0) )
Packit 70b277
				fprintf(stderr,
Packit 70b277
						"%s: scale must be a nonnegative number\n",
Packit 70b277
						progname);
Packit 70b277
			else
Packit 70b277
				dc_scale = tmpint;
Packit 70b277
		}
Packit 70b277
		break;
Packit 70b277
	case 'l':	/* "load" -- push value on top of register stack named
Packit 70b277
				 * by peekc onto top of evaluation stack; does not
Packit 70b277
				 * modify the register stack
Packit 70b277
				 */
Packit 70b277
		if (peekc == EOF)
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		if (dc_register_get(peekc, &datum) == DC_SUCCESS)
Packit 70b277
			dc_push(datum);
Packit 70b277
		return DC_EATONE;
Packit 70b277
	case 'n':	/* print the value popped off of top-of-stack;
Packit 70b277
				 * do not add a trailing newline
Packit 70b277
				 */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS)
Packit 70b277
			dc_print(datum, dc_obase, DC_NONL, DC_TOSS);
Packit 70b277
		break;
Packit 70b277
	case 'o':	/* set output base to value on top of stack */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			tmpint = 0;
Packit 70b277
			if (datum.dc_type == DC_NUMBER)
Packit 70b277
				tmpint = dc_num2int(datum.v.number, DC_TOSS);
Packit 70b277
			if ( ! (tmpint > 1) )
Packit 70b277
				fprintf(stderr,
Packit 70b277
						"%s: output base must be a number greater than 1\n",
Packit 70b277
						progname);
Packit 70b277
			else
Packit 70b277
				dc_obase = tmpint;
Packit 70b277
		}
Packit 70b277
		break;
Packit 70b277
	case 'p':	/* print the datum on the top of stack,
Packit 70b277
				 * with a trailing newline
Packit 70b277
				 */
Packit 70b277
		if (dc_top_of_stack(&datum) == DC_SUCCESS)
Packit 70b277
			dc_print(datum, dc_obase, DC_WITHNL, DC_KEEP);
Packit 70b277
		break;
Packit 70b277
	case 'q':	/* quit two levels of evaluation, posibly exiting program */
Packit 70b277
		unwind_depth = 1; /* the return below is the first level of returns */
Packit 70b277
		unwind_noexit = DC_FALSE;
Packit 70b277
		return DC_QUIT;
Packit 70b277
	case 'r':	/* rotate (swap) the top two elements on the stack */
Packit 70b277
		dc_stack_rotate(2);
Packit 70b277
		break;
Packit 70b277
	case 's':	/* "store" -- replace top of register stack named
Packit 70b277
				 * by peekc with the value popped from the top
Packit 70b277
				 * of the evaluation stack
Packit 70b277
				 */
Packit 70b277
		if (peekc == EOF)
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS)
Packit 70b277
			dc_register_set(peekc, datum);
Packit 70b277
		return DC_EATONE;
Packit 70b277
	case 'v':	/* replace top of stack with its square root */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			dc_num tmpnum;
Packit 70b277
			if (datum.dc_type != DC_NUMBER){
Packit 70b277
				fprintf(stderr,
Packit 70b277
						"%s: square root of nonnumeric attempted\n",
Packit 70b277
						progname);
Packit 70b277
			}else if (dc_sqrt(datum.v.number, dc_scale, &tmpnum) == DC_SUCCESS){
Packit 70b277
				dc_free_num(&datum.v.number);
Packit 70b277
				datum.v.number = tmpnum;
Packit 70b277
				dc_push(datum);
Packit 70b277
			}
Packit 70b277
		}
Packit 70b277
		break;
Packit 70b277
	case 'x':	/* eval the datum popped from top of stack */
Packit 70b277
		return DC_EVALTOS;
Packit 70b277
	case 'z':	/* push the current stack depth onto the top of stack */
Packit 70b277
		dc_push(dc_int2data(dc_tell_stackdepth()));
Packit 70b277
		break;
Packit 70b277
Packit 70b277
	case 'I':	/* push the current input base onto the stack */
Packit 70b277
		dc_push(dc_int2data(dc_ibase));
Packit 70b277
		break;
Packit 70b277
	case 'K':	/* push the current scale onto the stack */
Packit 70b277
		dc_push(dc_int2data(dc_scale));
Packit 70b277
		break;
Packit 70b277
	case 'L':	/* pop a value off of register stack named by peekc
Packit 70b277
				 * and push it onto the evaluation stack
Packit 70b277
				 */
Packit 70b277
		if (peekc == EOF)
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		if (dc_register_pop(peekc, &datum) == DC_SUCCESS)
Packit 70b277
			dc_push(datum);
Packit 70b277
		return DC_EATONE;
Packit 70b277
	case 'O':	/* push the current output base onto the stack */
Packit 70b277
		dc_push(dc_int2data(dc_obase));
Packit 70b277
		break;
Packit 70b277
	case 'P':
Packit 70b277
		/* Pop the value off the top of a stack.  If it is
Packit 70b277
		 * a number, dump out the integer portion of its
Packit 70b277
		 * absolute value as a "base UCHAR_MAX+1" byte stream;
Packit 70b277
		 * if it is a string, just print it.
Packit 70b277
		 * In either case, do not append a trailing newline.
Packit 70b277
		 */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			if (datum.dc_type == DC_NUMBER)
Packit 70b277
				dc_dump_num(datum.v.number, DC_TOSS);
Packit 70b277
			else if (datum.dc_type == DC_STRING)
Packit 70b277
				dc_out_str(datum.v.string, DC_TOSS);
Packit 70b277
			else
Packit 70b277
				dc_garbage("at top of stack", -1);
Packit 70b277
		}
Packit 70b277
		fflush(stdout);
Packit 70b277
		break;
Packit 70b277
	case 'Q':	/* quit out of top-of-stack nested evals;
Packit 70b277
				 * pops value from stack;
Packit 70b277
				 * does not exit program (stops short if necessary)
Packit 70b277
				 */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			unwind_depth = 0;
Packit 70b277
			unwind_noexit = DC_TRUE;
Packit 70b277
			if (datum.dc_type == DC_NUMBER)
Packit 70b277
				unwind_depth = dc_num2int(datum.v.number, DC_TOSS);
Packit 70b277
			if (unwind_depth-- > 0)
Packit 70b277
				return DC_QUIT;
Packit 70b277
			unwind_depth = 0;	/* paranoia */
Packit 70b277
			fprintf(stderr,
Packit 70b277
					"%s: Q command requires a number >= 1\n",
Packit 70b277
					progname);
Packit 70b277
		}
Packit 70b277
		break;
Packit 70b277
	case 'R':	/* pop a value off of the evaluation stack,;
Packit 70b277
				 * rotate the top remaining stack elements that many
Packit 70b277
				 * places forward (negative numbers mean rotate
Packit 70b277
				 * backward).
Packit 70b277
				 */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			tmpint = 0;
Packit 70b277
			if (datum.dc_type == DC_NUMBER)
Packit 70b277
				tmpint = dc_num2int(datum.v.number, DC_TOSS);
Packit 70b277
			dc_stack_rotate(tmpint);
Packit 70b277
		}
Packit 70b277
		break;
Packit 70b277
	case 'S':	/* pop a value off of the evaluation stack
Packit 70b277
				 * and push it onto the register stack named by peekc
Packit 70b277
				 */
Packit 70b277
		if (peekc == EOF)
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS)
Packit 70b277
			dc_register_push(peekc, datum);
Packit 70b277
		return DC_EATONE;
Packit 70b277
	case 'X':	/* replace the number on top-of-stack with its scale factor */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			tmpint = 0;
Packit 70b277
			if (datum.dc_type == DC_NUMBER)
Packit 70b277
				tmpint = dc_tell_scale(datum.v.number, DC_TOSS);
Packit 70b277
			dc_push(dc_int2data(tmpint));
Packit 70b277
		}
Packit 70b277
		break;
Packit 70b277
	case 'Z':	/* replace the datum on the top-of-stack with its length */
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS)
Packit 70b277
			dc_push(dc_int2data(dc_tell_length(datum, DC_TOSS)));
Packit 70b277
		break;
Packit 70b277
Packit 70b277
	case ':':	/* store into array */
Packit 70b277
		if (peekc == EOF)
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			tmpint = -1;
Packit 70b277
			if (datum.dc_type == DC_NUMBER)
Packit 70b277
				tmpint = dc_num2int(datum.v.number, DC_TOSS);
Packit 70b277
			if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
				if (tmpint < 0)
Packit 70b277
					fprintf(stderr,
Packit 70b277
							"%s: array index must be a nonnegative integer\n",
Packit 70b277
							progname);
Packit 70b277
				else
Packit 70b277
					dc_array_set(peekc, tmpint, datum);
Packit 70b277
			}
Packit 70b277
		}
Packit 70b277
		return DC_EATONE;
Packit 70b277
	case ';':	/* retreive from array */
Packit 70b277
		if (peekc == EOF)
Packit 70b277
			return DC_EOF_ERROR;
Packit 70b277
		if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
			tmpint = -1;
Packit 70b277
			if (datum.dc_type == DC_NUMBER)
Packit 70b277
				tmpint = dc_num2int(datum.v.number, DC_TOSS);
Packit 70b277
			if (tmpint < 0)
Packit 70b277
				fprintf(stderr,
Packit 70b277
						"%s: array index must be a nonnegative integer\n",
Packit 70b277
						progname);
Packit 70b277
			else
Packit 70b277
				dc_push(dc_array_get(peekc, tmpint));
Packit 70b277
		}
Packit 70b277
		return DC_EATONE;
Packit 70b277
Packit 70b277
	default:	/* What did that user mean? */
Packit 70b277
		fprintf(stderr, "%s: ", progname);
Packit 70b277
		dc_show_id(stdout, c, " unimplemented\n");
Packit 70b277
		break;
Packit 70b277
	}
Packit 70b277
	return DC_OKAY;
Packit 70b277
}
Packit 70b277
Packit 70b277

Packit 70b277
/* takes a string and evals it */
Packit 70b277
static int
Packit 70b277
evalstr DC_DECLARG((string))
Packit 70b277
	dc_data *string DC_DECLEND
Packit 70b277
{
Packit 70b277
	const char *s;
Packit 70b277
	const char *end;
Packit 70b277
	const char *p;
Packit 70b277
	size_t len;
Packit 70b277
	int c;
Packit 70b277
	int peekc;
Packit 70b277
	int count;
Packit 70b277
	int negcmp;
Packit 70b277
	int next_negcmp = 0;
Packit 70b277
	int tail_depth = 1; /* how much tail recursion is active */
Packit 70b277
	dc_data evalstr;
Packit 70b277
Packit 70b277
	if (string->dc_type != DC_STRING){
Packit 70b277
		fprintf(stderr,
Packit 70b277
				"%s: eval called with non-string argument\n",
Packit 70b277
				progname);
Packit 70b277
		return DC_OKAY;
Packit 70b277
	}
Packit 70b277
	interrupt_seen = 0;
Packit 70b277
	s = dc_str2charp(string->v.string);
Packit 70b277
	end = s + dc_strlen(string->v.string);
Packit 70b277
	while (s < end && interrupt_seen==0){
Packit 70b277
		c = *(const unsigned char *)s++;
Packit 70b277
		peekc = EOF;
Packit 70b277
		if (s < end)
Packit 70b277
			peekc = *(const unsigned char *)s;
Packit 70b277
		negcmp = next_negcmp;
Packit 70b277
		next_negcmp = 0;
Packit 70b277
		switch (dc_func(c, peekc, negcmp)){
Packit 70b277
		case DC_OKAY:
Packit 70b277
			break;
Packit 70b277
		case DC_EATONE:
Packit 70b277
			if (peekc != EOF)
Packit 70b277
				++s;
Packit 70b277
			break;
Packit 70b277
		case DC_EVALREG:
Packit 70b277
			/*commands which return this guarantee that peekc!=EOF*/
Packit 70b277
			++s;
Packit 70b277
			if (dc_register_get(peekc, &evalstr) != DC_SUCCESS)
Packit 70b277
				break;
Packit 70b277
			dc_push(evalstr);
Packit 70b277
			/*@fallthrough@*/
Packit 70b277
		case DC_EVALTOS:
Packit 70b277
			/*skip trailing whitespace to assist tail-recursion detection*/
Packit 70b277
			while (s
Packit 70b277
				if (*s++ == '#')
Packit 70b277
					s = skip_past_eol(s, end);
Packit 70b277
			}
Packit 70b277
			if (dc_pop(&evalstr) == DC_SUCCESS){
Packit 70b277
				if (evalstr.dc_type == DC_NUMBER){
Packit 70b277
					dc_push(evalstr);
Packit 70b277
				}else if (evalstr.dc_type != DC_STRING){
Packit 70b277
					dc_garbage("at top of stack", -1);
Packit 70b277
				}else if (s == end){
Packit 70b277
					/*handle tail recursion*/
Packit 70b277
					dc_free_str(&string->v.string);
Packit 70b277
					*string = evalstr;
Packit 70b277
					s = dc_str2charp(string->v.string);
Packit 70b277
					end = s + dc_strlen(string->v.string);
Packit 70b277
					++tail_depth;
Packit 70b277
				}else if (dc_eval_and_free_str(&evalstr) == DC_QUIT){
Packit 70b277
					if (unwind_depth > 0){
Packit 70b277
						--unwind_depth;
Packit 70b277
						return DC_QUIT;
Packit 70b277
					}
Packit 70b277
					return DC_OKAY;
Packit 70b277
				}
Packit 70b277
			}
Packit 70b277
			break;
Packit 70b277
		case DC_QUIT:
Packit 70b277
			if (unwind_depth >= tail_depth){
Packit 70b277
				unwind_depth -= tail_depth;
Packit 70b277
				return DC_QUIT;
Packit 70b277
			}
Packit 70b277
			/*adjust tail recursion accounting and continue*/
Packit 70b277
			tail_depth -= unwind_depth;
Packit 70b277
			break;
Packit 70b277
Packit 70b277
		case DC_INT:
Packit 70b277
			input_str_string = s - 1;
Packit 70b277
			dc_push(dc_getnum(input_str, dc_ibase, &peekc));
Packit 70b277
			s = input_str_string;
Packit 70b277
			if (peekc != EOF)
Packit 70b277
				--s;
Packit 70b277
			break;
Packit 70b277
		case DC_STR:
Packit 70b277
			count = 1;
Packit 70b277
			for (p=s; p<end && count>0; ++p)
Packit 70b277
				if (*p == ']')
Packit 70b277
					--count;
Packit 70b277
				else if (*p == '[')
Packit 70b277
					++count;
Packit 70b277
			len = (size_t) (p - s);
Packit 70b277
			dc_push(dc_makestring(s, (count==0 ? len-1 : len)));
Packit 70b277
			s = p;
Packit 70b277
			break;
Packit 70b277
		case DC_SYSTEM:
Packit 70b277
			s = dc_system(s);
Packit 70b277
			break;
Packit 70b277
		case DC_COMMENT:
Packit 70b277
			s = skip_past_eol(s, end);
Packit 70b277
			break;
Packit 70b277
		case DC_NEGCMP:
Packit 70b277
			next_negcmp = 1;
Packit 70b277
			break;
Packit 70b277
Packit 70b277
		case DC_EOF_ERROR:
Packit 70b277
			if (ferror(stdin)) {
Packit 70b277
				fprintf(stderr, "%s: ", progname);
Packit 70b277
				perror("error reading stdin");
Packit 70b277
				return DC_FAIL;
Packit 70b277
			}
Packit 70b277
			fprintf(stderr, "%s: unexpected EOS\n", progname);
Packit 70b277
			return DC_OKAY;
Packit 70b277
		}
Packit 70b277
	}
Packit 70b277
	return DC_OKAY;
Packit 70b277
}
Packit 70b277
Packit 70b277
/* wrapper around evalstr, to handle top-level QUIT requests correctly*/
Packit 70b277
int
Packit 70b277
dc_evalstr(dc_data *string)
Packit 70b277
{
Packit 70b277
   switch (evalstr(string)) {
Packit 70b277
   case DC_OKAY:
Packit 70b277
	   return DC_SUCCESS;
Packit 70b277
   case DC_QUIT:
Packit 70b277
	   if (unwind_noexit != DC_TRUE)
Packit 70b277
		   return DC_FAIL;
Packit 70b277
	   return DC_SUCCESS;
Packit 70b277
   default:
Packit 70b277
	   return DC_FAIL;
Packit 70b277
   }
Packit 70b277
}
Packit 70b277
Packit 70b277
Packit 70b277

Packit 70b277
/* This is the main function of the whole DC program.
Packit 70b277
 * Reads the file described by fp, calls dc_func to do
Packit 70b277
 * the dirty work, and takes care of dc_func's shortcomings.
Packit 70b277
 */
Packit 70b277
int
Packit 70b277
dc_evalfile DC_DECLARG((fp))
Packit 70b277
	FILE *fp DC_DECLEND
Packit 70b277
{
Packit 70b277
	int c;
Packit 70b277
	int peekc;
Packit 70b277
	int negcmp;
Packit 70b277
	int next_negcmp = 0;
Packit 70b277
	typedef void (*handler_t)(int);
Packit 70b277
	handler_t sigint_handler = dc_trap_interrupt;
Packit 70b277
	handler_t sigint_default = signal(SIGINT, SIG_IGN);
Packit 70b277
	dc_data datum;
Packit 70b277
Packit 70b277
	/* Signals are awkward: we want to allow interactive users
Packit 70b277
	 * to break out of long running macros, but otherwise we
Packit 70b277
	 * prefer that SIGINT not be given any special treatment.
Packit 70b277
	 * Sometimes "no special treatment" means to continue to
Packit 70b277
	 * *ignore* the signal, but usually it means to kill the program.
Packit 70b277
	 */
Packit 70b277
	signal(SIGINT, sigint_default);
Packit 70b277
#ifdef HAVE_UNISTD_H
Packit 70b277
	/* don't trap SIGINT if we can tell that we are not reading from a tty */
Packit 70b277
	if ( ! isatty(fileno(fp)) )
Packit 70b277
		sigint_handler = sigint_default;
Packit 70b277
#endif
Packit 70b277
Packit 70b277
	stdin_lookahead = EOF;
Packit 70b277
	for (c=getc(fp); c!=EOF; c=peekc){
Packit 70b277
		peekc = getc(fp);
Packit 70b277
		/*
Packit 70b277
		 * The following if() is the only place where ``stdin_lookahead''
Packit 70b277
		 * might be set to other than EOF:
Packit 70b277
		 */
Packit 70b277
		if (fp == stdin)
Packit 70b277
			stdin_lookahead = peekc;
Packit 70b277
		/*
Packit 70b277
		 * In the switch(), cases which naturally update peekc
Packit 70b277
		 * (unconditionally) do not have to update or reference
Packit 70b277
		 * stdin_lookahead; other functions use the predicate:
Packit 70b277
		 *    stdin_lookahead != peekc  &&  fp == stdin
Packit 70b277
		 * to recognize the case where:
Packit 70b277
		 *   a) stdin_lookahead == EOF (stdin and peekc are not in sync)
Packit 70b277
		 *   b) peekc != EOF (resync is possible)
Packit 70b277
		 *   c) fp == stdin (resync is relevant)
Packit 70b277
		 * The whole stdin_lookahead complication arises because the
Packit 70b277
		 * '?' command may be invoked from an arbritrarily deeply
Packit 70b277
		 * nested dc_evalstr(), '?' reads exclusively from stdin,
Packit 70b277
		 * and this winds up making peekc invalid when fp==stdin.
Packit 70b277
		 */
Packit 70b277
		negcmp = next_negcmp;
Packit 70b277
		next_negcmp = 0;
Packit 70b277
		signal(SIGINT, sigint_handler);
Packit 70b277
		switch (dc_func(c, peekc, negcmp)){
Packit 70b277
		case DC_OKAY:
Packit 70b277
			if (stdin_lookahead != peekc  &&  fp == stdin)
Packit 70b277
				peekc = getc(fp);
Packit 70b277
			break;
Packit 70b277
		case DC_EATONE:
Packit 70b277
			peekc = getc(fp);
Packit 70b277
			break;
Packit 70b277
		case DC_EVALREG:
Packit 70b277
			/*commands which send us here shall guarantee that peekc!=EOF*/
Packit 70b277
			c = peekc;
Packit 70b277
			peekc = getc(fp);
Packit 70b277
			stdin_lookahead = peekc;
Packit 70b277
			if (dc_register_get(c, &datum) != DC_SUCCESS)
Packit 70b277
				break;
Packit 70b277
			dc_push(datum);
Packit 70b277
			/*@fallthrough@*/
Packit 70b277
		case DC_EVALTOS:
Packit 70b277
			if (stdin_lookahead != peekc  &&  fp == stdin)
Packit 70b277
				peekc = getc(fp);
Packit 70b277
			if (dc_pop(&datum) == DC_SUCCESS){
Packit 70b277
				if (datum.dc_type == DC_NUMBER){
Packit 70b277
					dc_push(datum);
Packit 70b277
				}else if (datum.dc_type == DC_STRING){
Packit 70b277
					if (dc_eval_and_free_str(&datum) == DC_QUIT){
Packit 70b277
						if (unwind_noexit != DC_TRUE)
Packit 70b277
							goto reset_and_exit_quit;
Packit 70b277
						fprintf(stderr, "%s: Q command argument exceeded \
Packit 70b277
string execution depth\n", progname);
Packit 70b277
					}
Packit 70b277
				}else{
Packit 70b277
					dc_garbage("at top of stack", -1);
Packit 70b277
				}
Packit 70b277
			}
Packit 70b277
			break;
Packit 70b277
		case DC_QUIT:
Packit 70b277
			if (unwind_noexit != DC_TRUE)
Packit 70b277
				goto reset_and_exit_quit;
Packit 70b277
			fprintf(stderr,
Packit 70b277
					"%s: Q command argument exceeded string execution depth\n",
Packit 70b277
					progname);
Packit 70b277
			if (stdin_lookahead != peekc  &&  fp == stdin)
Packit 70b277
				peekc = getc(fp);
Packit 70b277
			break;
Packit 70b277
Packit 70b277
		case DC_INT:
Packit 70b277
			input_fil_fp = fp;
Packit 70b277
			input_pushback = c;
Packit 70b277
			ungetc(peekc, fp);
Packit 70b277
			dc_push(dc_getnum(input_fil, dc_ibase, &peekc));
Packit 70b277
			if (ferror(fp))
Packit 70b277
				goto error_fail;
Packit 70b277
			break;
Packit 70b277
		case DC_STR:
Packit 70b277
			ungetc(peekc, fp);
Packit 70b277
			datum = dc_readstring(fp, '[', ']');
Packit 70b277
			if (ferror(fp))
Packit 70b277
				goto error_fail;
Packit 70b277
			dc_push(datum);
Packit 70b277
			peekc = getc(fp);
Packit 70b277
			break;
Packit 70b277
		case DC_SYSTEM:
Packit 70b277
			ungetc(peekc, fp);
Packit 70b277
			datum = dc_readstring(fp, '\n', '\n');
Packit 70b277
			if (ferror(fp))
Packit 70b277
				goto error_fail;
Packit 70b277
			(void)dc_system(dc_str2charp(datum.v.string));
Packit 70b277
			dc_free_str(&datum.v.string);
Packit 70b277
			peekc = getc(fp);
Packit 70b277
			break;
Packit 70b277
		case DC_COMMENT:
Packit 70b277
			while (peekc!=EOF && peekc!='\n')
Packit 70b277
				peekc = getc(fp);
Packit 70b277
			if (peekc != EOF)
Packit 70b277
				peekc = getc(fp);
Packit 70b277
			break;
Packit 70b277
		case DC_NEGCMP:
Packit 70b277
			next_negcmp = 1;
Packit 70b277
			break;
Packit 70b277
Packit 70b277
		case DC_EOF_ERROR:
Packit 70b277
			if (ferror(fp))
Packit 70b277
				goto error_fail;
Packit 70b277
			fprintf(stderr, "%s: unexpected EOF\n", progname);
Packit 70b277
			goto reset_and_exit_fail;
Packit 70b277
		}
Packit 70b277
Packit 70b277
		if (interrupt_seen)
Packit 70b277
			fprintf(stderr, "\nInterrupt!\n");
Packit 70b277
		interrupt_seen = 0;
Packit 70b277
		signal(SIGINT, sigint_default);
Packit 70b277
	}
Packit 70b277
	if (!ferror(fp))
Packit 70b277
		goto reset_and_exit_success;
Packit 70b277
Packit 70b277
error_fail:
Packit 70b277
	fprintf(stderr, "%s: ", progname);
Packit 70b277
	perror("error reading input");
Packit 70b277
	return DC_FAIL;
Packit 70b277
reset_and_exit_quit:
Packit 70b277
reset_and_exit_fail:
Packit 70b277
	signal(SIGINT, sigint_default);
Packit 70b277
	return DC_FAIL;
Packit 70b277
reset_and_exit_success:
Packit 70b277
	signal(SIGINT, sigint_default);
Packit 70b277
	return DC_SUCCESS;
Packit 70b277
}
Packit 70b277
Packit 70b277

Packit 70b277
/*
Packit 70b277
 * Local Variables:
Packit 70b277
 * mode: C
Packit 70b277
 * tab-width: 4
Packit 70b277
 * End:
Packit 70b277
 * vi: set ts=4 :
Packit 70b277
 */