summaryrefslogtreecommitdiff
path: root/dc/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'dc/eval.c')
-rw-r--r--dc/eval.c237
1 files changed, 174 insertions, 63 deletions
diff --git a/dc/eval.c b/dc/eval.c
index 21592d9..4af7200 100644
--- a/dc/eval.c
+++ b/dc/eval.c
@@ -1,7 +1,8 @@
-/*
+/*
* evaluate the dc language, from a FILE* or a string
*
- * Copyright (C) 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
+ * Copyright (C) 1994, 1997, 1998, 2000, 2003, 2005, 2006 Free Software
+ * Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,8 +18,8 @@
* along with this program; if not, you can either send email to this
* program's author (see below) or write to:
* The Free Software Foundation, Inc.
- * 59 Temple Place, Suite 330
- * Boston, MA 02111 USA
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
*/
/* This is the only module which knows about the dc input language */
@@ -37,6 +38,7 @@
# endif
#endif
#endif
+#include <signal.h>
#include "dc.h"
#include "dc-proto.h"
@@ -45,11 +47,9 @@ typedef enum {DC_FALSE, DC_TRUE} dc_boolean;
typedef enum {
DC_OKAY = DC_SUCCESS, /* no further intervention needed for this command */
DC_EATONE, /* caller needs to eat the lookahead char */
+ DC_EVALREG, /* caller needs to eval the string named by `peekc' */
+ DC_EVALTOS, /* caller needs to eval the string on top of the stack */
DC_QUIT, /* quit out of unwind_depth levels of evaluation */
-
- /* with the following return values, the caller does not have to
- * fret about stdin_lookahead's value
- */
DC_INT, /* caller needs to parse a dc_num from input stream */
DC_STR, /* caller needs to parse a dc_str from input stream */
DC_SYSTEM, /* caller needs to run a system() on next input line */
@@ -66,6 +66,9 @@ static int dc_scale=0; /* scale (see user documentaton) */
/* for Quitting evaluations */
static int unwind_depth=0;
+/* for handling SIGINT properly */
+static volatile sig_atomic_t interrupt_seen=0;
+
/* if true, active Quit will not exit program */
static dc_boolean unwind_noexit=DC_FALSE;
@@ -103,7 +106,7 @@ input_fil DC_DECLVOID()
static int
input_str DC_DECLVOID()
{
- if (!*input_str_string)
+ if (*input_str_string == '\0')
return EOF;
return *input_str_string++;
}
@@ -116,17 +119,40 @@ input_str DC_DECLVOID()
*/
static int
dc_eval_and_free_str DC_DECLARG((string))
- dc_data string DC_DECLEND
+ dc_data *string DC_DECLEND
{
dc_status status;
status = dc_evalstr(string);
- if (string.dc_type == DC_STRING)
- dc_free_str(&string.v.string);
+ if (string->dc_type == DC_STRING)
+ dc_free_str(&string->v.string);
return status;
}
+/* notice when an interrupt event happens */
+static void
+dc_trap_interrupt DC_DECLARG((signo))
+ int signo DC_DECLEND
+{
+ signal(signo, dc_trap_interrupt);
+ interrupt_seen = 1;
+}
+
+
+/* step pointer past next end-of-line (or to end-of-string) */
+static const char *
+skip_past_eol DC_DECLARG((strptr, strend))
+ const char *strptr DC_DECLSEP
+ const char *strend DC_DECLEND
+{
+ const char *p = memchr(strptr, '\n', (size_t)(strend-strptr));
+ if (p != NULL)
+ return p+1;
+ return strend;
+}
+
+
/* dc_func does the grunt work of figuring out what each input
* character means; used by both dc_evalstr and dc_evalfile
*
@@ -140,11 +166,6 @@ dc_func DC_DECLARG((c, peekc, negcmp))
int peekc DC_DECLSEP
int negcmp DC_DECLEND
{
- /* we occasionally need these for temporary data */
- /* Despite the GNU coding standards, it is much easier
- * to have these declared once here, since this function
- * is just one big switch statement.
- */
dc_data datum;
int tmpint;
@@ -206,10 +227,8 @@ dc_func DC_DECLARG((c, peekc, negcmp))
*/
if (peekc == EOF)
return DC_EOF_ERROR;
- if ( (dc_cmpop() < 0) == !negcmp )
- if (dc_register_get(peekc, &datum) == DC_SUCCESS)
- if (dc_eval_and_free_str(datum) == DC_QUIT)
- return DC_QUIT;
+ if ( (dc_cmpop() < 0) == (negcmp==0) )
+ return DC_EVALREG;
return DC_EATONE;
case '=':
/* eval register named by peekc if
@@ -217,10 +236,8 @@ dc_func DC_DECLARG((c, peekc, negcmp))
*/
if (peekc == EOF)
return DC_EOF_ERROR;
- if ( (dc_cmpop() == 0) == !negcmp )
- if (dc_register_get(peekc, &datum) == DC_SUCCESS)
- if (dc_eval_and_free_str(datum) == DC_QUIT)
- return DC_QUIT;
+ if ( (dc_cmpop() == 0) == (negcmp==0) )
+ return DC_EVALREG;
return DC_EATONE;
case '>':
/* eval register named by peekc if
@@ -228,19 +245,19 @@ dc_func DC_DECLARG((c, peekc, negcmp))
*/
if (peekc == EOF)
return DC_EOF_ERROR;
- if ( (dc_cmpop() > 0) == !negcmp )
- if (dc_register_get(peekc, &datum) == DC_SUCCESS)
- if (dc_eval_and_free_str(datum) == DC_QUIT)
- return DC_QUIT;
+ if ( (dc_cmpop() > 0) == (negcmp==0) )
+ return DC_EVALREG;
return DC_EATONE;
case '?': /* read a line from standard-input and eval it */
if (stdin_lookahead != EOF){
ungetc(stdin_lookahead, stdin);
stdin_lookahead = EOF;
}
- if (dc_eval_and_free_str(dc_readstring(stdin, '\n', '\n')) == DC_QUIT)
- return DC_QUIT;
- return DC_OKAY;
+ datum = dc_readstring(stdin, '\n', '\n');
+ if (ferror(stdin))
+ return DC_EOF_ERROR;
+ dc_push(datum);
+ return DC_EVALTOS;
case '[': /* read to balancing ']' into a dc_str */
return DC_STR;
case '!': /* read to newline and call system() on resulting string */
@@ -279,13 +296,13 @@ dc_func DC_DECLARG((c, peekc, negcmp))
tmpint = 0;
if (datum.dc_type == DC_NUMBER)
tmpint = dc_num2int(datum.v.number, DC_TOSS);
- if ( ! (2 <= tmpint && tmpint <= DC_IBASE_MAX) )
+ if (2 <= tmpint && tmpint <= DC_IBASE_MAX)
+ dc_ibase = tmpint;
+ else
fprintf(stderr,
"%s: input base must be a number \
between 2 and %d (inclusive)\n",
progname, DC_IBASE_MAX);
- else
- dc_ibase = tmpint;
}
break;
case 'k': /* set scale to value on top of stack */
@@ -341,7 +358,7 @@ between 2 and %d (inclusive)\n",
return DC_QUIT;
case 'r': /* rotate (swap) the top two elements on the stack
*/
- if (dc_pop(&datum) == DC_SUCCESS) {
+ if (dc_pop(&datum) == DC_SUCCESS){
dc_data datum2;
int two_status;
two_status = dc_pop(&datum2);
@@ -374,17 +391,7 @@ between 2 and %d (inclusive)\n",
}
break;
case 'x': /* eval the datum popped from top of stack */
- if (dc_pop(&datum) == DC_SUCCESS){
- if (datum.dc_type == DC_STRING){
- if (dc_eval_and_free_str(datum) == DC_QUIT)
- return DC_QUIT;
- }else if (datum.dc_type == DC_NUMBER){
- dc_push(datum);
- }else{
- dc_garbage("at top of stack", -1);
- }
- }
- break;
+ return DC_EVALTOS;
case 'z': /* push the current stack depth onto the top of stack */
dc_push(dc_int2data(dc_tell_stackdepth()));
break;
@@ -441,8 +448,7 @@ between 2 and %d (inclusive)\n",
break;
#if 0
case 'R': /* pop a value off of the evaluation stack,;
- * rotate the top
- remaining stack elements that many
+ * rotate the top remaining stack elements that many
* places forward (negative numbers mean rotate
* backward).
*/
@@ -520,7 +526,7 @@ between 2 and %d (inclusive)\n",
/* takes a string and evals it */
int
dc_evalstr DC_DECLARG((string))
- dc_data string DC_DECLEND
+ dc_data *string DC_DECLEND
{
const char *s;
const char *end;
@@ -531,16 +537,19 @@ dc_evalstr DC_DECLARG((string))
int count;
int negcmp;
int next_negcmp = 0;
+ int tail_depth = 1; /* how much tail recursion is active */
+ dc_data evalstr;
- if (string.dc_type != DC_STRING){
+ if (string->dc_type != DC_STRING){
fprintf(stderr,
"%s: eval called with non-string argument\n",
progname);
return DC_OKAY;
}
- s = dc_str2charp(string.v.string);
- end = s + dc_strlen(string.v.string);
- while (s < end){
+ interrupt_seen = 0;
+ s = dc_str2charp(string->v.string);
+ end = s + dc_strlen(string->v.string);
+ while (s < end && interrupt_seen==0){
c = *(const unsigned char *)s++;
peekc = EOF;
if (s < end)
@@ -554,9 +563,44 @@ dc_evalstr DC_DECLARG((string))
if (peekc != EOF)
++s;
break;
+ case DC_EVALREG:
+ /*commands which return this guarantee that peekc!=EOF*/
+ ++s;
+ if (dc_register_get(peekc, &evalstr) != DC_SUCCESS)
+ break;
+ dc_push(evalstr);
+ /*@fallthrough@*/
+ case DC_EVALTOS:
+ /*skip trailing whitespace to assist tail-recursion detection*/
+ while (s<end && (*s==' '||*s=='\t'||*s=='\n'||*s=='#')){
+ if (*s++ == '#')
+ s = skip_past_eol(s, end);
+ }
+ if (dc_pop(&evalstr) == DC_SUCCESS){
+ if (evalstr.dc_type == DC_NUMBER){
+ dc_push(evalstr);
+ return DC_OKAY;
+ }else if (evalstr.dc_type != DC_STRING){
+ dc_garbage("at top of stack", -1);
+ }else if (s == end){
+ /*handle tail recursion*/
+ dc_free_str(&string->v.string);
+ *string = evalstr;
+ s = dc_str2charp(string->v.string);
+ end = s + dc_strlen(string->v.string);
+ ++tail_depth;
+ }else if (dc_eval_and_free_str(&evalstr) == DC_QUIT){
+ if (unwind_depth > 0){
+ --unwind_depth;
+ return DC_QUIT;
+ }
+ return DC_OKAY;
+ }
+ }
+ break;
case DC_QUIT:
- if (unwind_depth > 0){
- --unwind_depth;
+ if (unwind_depth >= tail_depth){
+ unwind_depth -= tail_depth;
return DC_QUIT;
}
return DC_OKAY;
@@ -575,24 +619,26 @@ dc_evalstr DC_DECLARG((string))
--count;
else if (*p == '[')
++count;
- len = p - s;
+ len = (size_t) (p - s);
dc_push(dc_makestring(s, len-1));
s = p;
break;
case DC_SYSTEM:
s = dc_system(s);
+ /*@fallthrough@*/
case DC_COMMENT:
- s = memchr(s, '\n', (size_t)(end-s));
- if (!s)
- s = end;
- else
- ++s;
+ s = skip_past_eol(s, end);
break;
case DC_NEGCMP:
next_negcmp = 1;
break;
case DC_EOF_ERROR:
+ if (ferror(stdin)) {
+ fprintf(stderr, "%s: ", progname);
+ perror("error reading stdin");
+ return DC_FAIL;
+ }
fprintf(stderr, "%s: unexpected EOS\n", progname);
return DC_OKAY;
}
@@ -615,6 +661,7 @@ dc_evalfile DC_DECLARG((fp))
int next_negcmp = 0;
dc_data datum;
+ signal(SIGINT, dc_trap_interrupt);
stdin_lookahead = EOF;
for (c=getc(fp); c!=EOF; c=peekc){
peekc = getc(fp);
@@ -624,6 +671,20 @@ dc_evalfile DC_DECLARG((fp))
*/
if (fp == stdin)
stdin_lookahead = peekc;
+ /*
+ * In the switch(), cases which naturally update peekc
+ * (unconditionally) do not have to update or reference
+ * stdin_lookahead; other functions use the predicate:
+ * stdin_lookahead != peekc && fp == stdin
+ * to recognize the case where:
+ * a) stdin_lookahead == EOF (stdin and peekc are not in sync)
+ * b) peekc != EOF (resync is possible)
+ * c) fp == stdin (resync is relevant)
+ * The whole stdin_lookahead complication arises because the
+ * '?' command may be invoked from an arbritrarily deeply
+ * nested dc_evalstr(), '?' reads exclusively from stdin,
+ * and this winds up making peekc invalid when fp==stdin.
+ */
negcmp = next_negcmp;
next_negcmp = 0;
switch (dc_func(c, peekc, negcmp)){
@@ -634,6 +695,33 @@ dc_evalfile DC_DECLARG((fp))
case DC_EATONE:
peekc = getc(fp);
break;
+ case DC_EVALREG:
+ /*commands which send us here shall guarantee that peekc!=EOF*/
+ c = peekc;
+ peekc = getc(fp);
+ stdin_lookahead = peekc;
+ if (dc_register_get(c, &datum) != DC_SUCCESS)
+ break;
+ dc_push(datum);
+ /*@fallthrough@*/
+ case DC_EVALTOS:
+ if (stdin_lookahead != peekc && fp == stdin)
+ peekc = getc(fp);
+ if (dc_pop(&datum) == DC_SUCCESS){
+ if (datum.dc_type == DC_NUMBER){
+ dc_push(datum);
+ }else if (datum.dc_type == DC_STRING){
+ if (dc_eval_and_free_str(&datum) == DC_QUIT){
+ if (unwind_noexit != DC_TRUE)
+ return DC_SUCCESS;
+ fprintf(stderr, "%s: Q command argument exceeded \
+string execution depth\n", progname);
+ }
+ }else{
+ dc_garbage("at top of stack", -1);
+ }
+ }
+ break;
case DC_QUIT:
if (unwind_noexit != DC_TRUE)
return DC_SUCCESS;
@@ -649,16 +737,22 @@ dc_evalfile DC_DECLARG((fp))
input_pushback = c;
ungetc(peekc, fp);
dc_push(dc_getnum(input_fil, dc_ibase, &peekc));
+ if (ferror(fp))
+ goto error_fail;
break;
case DC_STR:
ungetc(peekc, fp);
datum = dc_readstring(fp, '[', ']');
+ if (ferror(fp))
+ goto error_fail;
dc_push(datum);
peekc = getc(fp);
break;
case DC_SYSTEM:
ungetc(peekc, fp);
datum = dc_readstring(stdin, '\n', '\n');
+ if (ferror(stdin))
+ goto error_fail;
(void)dc_system(dc_str2charp(datum.v.string));
dc_free_str(&datum.v.string);
peekc = getc(fp);
@@ -674,9 +768,26 @@ dc_evalfile DC_DECLARG((fp))
break;
case DC_EOF_ERROR:
+ if (ferror(fp))
+ goto error_fail;
fprintf(stderr, "%s: unexpected EOF\n", progname);
return DC_FAIL;
}
}
- return DC_SUCCESS;
+ if (!ferror(fp))
+ return DC_SUCCESS;
+
+error_fail:
+ fprintf(stderr, "%s: ", progname);
+ perror("error reading input");
+ return DC_FAIL;
}
+
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 4
+ * End:
+ * vi: set ts=4 :
+ */