1/*
2 * evaluate the dc language, from a FILE* or a string
3 *
4 * Copyright (C) 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you can either send email to this
18 * program's author (see below) or write to:
19 *   The Free Software Foundation, Inc.
20 *   59 Temple Place, Suite 330
21 *   Boston, MA 02111 USA
22 */
23
24/* This is the only module which knows about the dc input language */
25
26#include "config.h"
27
28#include <stdio.h>
29#ifdef HAVE_STRING_H
30# include <string.h>	/* memchr */
31#else
32# ifdef HAVE_MEMORY_H
33#  include <memory.h>	/* memchr, maybe */
34# else
35#  ifdef HAVE_STRINGS_H
36#   include <strings.h>	/* memchr, maybe */
37#  endif
38#endif
39#endif
40#include "dc.h"
41#include "dc-proto.h"
42
43typedef enum {DC_FALSE, DC_TRUE} dc_boolean;
44
45typedef enum {
46	DC_OKAY = DC_SUCCESS, /* no further intervention needed for this command */
47	DC_EATONE,		/* caller needs to eat the lookahead char */
48	DC_QUIT,		/* quit out of unwind_depth levels of evaluation */
49
50	/* with the following return values, the caller does not have to
51	 * fret about stdin_lookahead's value
52	 */
53	DC_INT,			/* caller needs to parse a dc_num from input stream */
54	DC_STR,			/* caller needs to parse a dc_str from input stream */
55	DC_SYSTEM,		/* caller needs to run a system() on next input line */
56	DC_COMMENT,		/* caller needs to skip to the next input line */
57	DC_NEGCMP,		/* caller needs to re-call dc_func() with `negcmp' set */
58
59	DC_EOF_ERROR	/* unexpected end of input; abort current eval */
60} dc_status;
61
62static int dc_ibase=10;		/* input base, 2 <= dc_ibase <= DC_IBASE_MAX */
63static int dc_obase=10;		/* output base, 2 <= dc_obase */
64static int dc_scale=0;		/* scale (see user documentaton) */
65
66/* for Quitting evaluations */
67static int unwind_depth=0;
68
69/* if true, active Quit will not exit program */
70static dc_boolean unwind_noexit=DC_FALSE;
71
72/*
73 * Used to synchronize lookahead on stdin for '?' command.
74 * If set to EOF then lookahead is used up.
75 */
76static int stdin_lookahead=EOF;
77
78
79/* input_fil and input_str are passed as arguments to dc_getnum */
80
81/* used by the input_* functions: */
82static FILE *input_fil_fp;
83static const char *input_str_string;
84
85/* Since we have a need for two characters of pushback, and
86 * ungetc() only guarantees one, we place the second pushback here
87 */
88static int input_pushback;
89
90/* passed as an argument to dc_getnum */
91static int
92input_fil DC_DECLVOID()
93{
94	if (input_pushback != EOF){
95		int c = input_pushback;
96		input_pushback = EOF;
97		return c;
98	}
99	return getc(input_fil_fp);
100}
101
102/* passed as an argument to dc_getnum */
103static int
104input_str DC_DECLVOID()
105{
106	if (!*input_str_string)
107		return EOF;
108	return *input_str_string++;
109}
110
111
112
113/* takes a string and evals it; frees the string when done */
114/* Wrapper around dc_evalstr to avoid duplicating the free call
115 * at all possible return points.
116 */
117static int
118dc_eval_and_free_str DC_DECLARG((string))
119	dc_data string DC_DECLEND
120{
121	dc_status status;
122
123	status = dc_evalstr(string);
124	if (string.dc_type == DC_STRING)
125		dc_free_str(&string.v.string);
126	return status;
127}
128
129
130/* dc_func does the grunt work of figuring out what each input
131 * character means; used by both dc_evalstr and dc_evalfile
132 *
133 * c -> the "current" input character under consideration
134 * peekc -> the lookahead input character
135 * negcmp -> negate comparison test (for <,=,> commands)
136 */
137static dc_status
138dc_func DC_DECLARG((c, peekc, negcmp))
139	int c DC_DECLSEP
140	int peekc DC_DECLSEP
141	int negcmp DC_DECLEND
142{
143	/* we occasionally need these for temporary data */
144	/* Despite the GNU coding standards, it is much easier
145	 * to have these declared once here, since this function
146	 * is just one big switch statement.
147	 */
148	dc_data datum;
149	int tmpint;
150
151	switch (c){
152	case '_': case '.':
153	case '0': case '1': case '2': case '3':
154	case '4': case '5': case '6': case '7':
155	case '8': case '9': case 'A': case 'B':
156	case 'C': case 'D': case 'E': case 'F':
157		return DC_INT;
158	case ' ':
159	case '\t':
160	case '\n':
161		/* standard command separators */
162		break;
163
164	case '+':	/* add top two stack elements */
165		dc_binop(dc_add, dc_scale);
166		break;
167	case '-':	/* subtract top two stack elements */
168		dc_binop(dc_sub, dc_scale);
169		break;
170	case '*':	/* multiply top two stack elements */
171		dc_binop(dc_mul, dc_scale);
172		break;
173	case '/':	/* divide top two stack elements */
174		dc_binop(dc_div, dc_scale);
175		break;
176	case '%':
177		/* take the remainder from division of the top two stack elements */
178		dc_binop(dc_rem, dc_scale);
179		break;
180	case '~':
181		/* Do division on the top two stack elements.  Return the
182		 * quotient as next-to-top of stack and the remainder as
183		 * top-of-stack.
184		 */
185		dc_binop2(dc_divrem, dc_scale);
186		break;
187	case '|':
188		/* Consider the top three elements of the stack as (base, exp, mod),
189		 * where mod is top-of-stack, exp is next-to-top, and base is
190		 * second-from-top. Mod must be non-zero, exp must be non-negative,
191		 * and all three must be integers. Push the result of raising
192		 * base to the exp power, reduced modulo mod. If we had base in
193		 * register b, exp in register e, and mod in register m then this
194		 * is conceptually equivalent to "lble^lm%", but it is implemented
195		 * in a more efficient manner, and can handle arbritrarily large
196		 * values for exp.
197		 */
198		dc_triop(dc_modexp, dc_scale);
199		break;
200	case '^':	/* exponientiation of the top two stack elements */
201		dc_binop(dc_exp, dc_scale);
202		break;
203	case '<':
204		/* eval register named by peekc if
205		 * less-than holds for top two stack elements
206		 */
207		if (peekc == EOF)
208			return DC_EOF_ERROR;
209		if ( (dc_cmpop() <  0) == !negcmp )
210			if (dc_register_get(peekc, &datum) == DC_SUCCESS)
211				if (dc_eval_and_free_str(datum) == DC_QUIT)
212					return DC_QUIT;
213		return DC_EATONE;
214	case '=':
215		/* eval register named by peekc if
216		 * equal-to holds for top two stack elements
217		 */
218		if (peekc == EOF)
219			return DC_EOF_ERROR;
220		if ( (dc_cmpop() == 0) == !negcmp )
221			if (dc_register_get(peekc, &datum) == DC_SUCCESS)
222				if (dc_eval_and_free_str(datum) == DC_QUIT)
223					return DC_QUIT;
224		return DC_EATONE;
225	case '>':
226		/* eval register named by peekc if
227		 * greater-than holds for top two stack elements
228		 */
229		if (peekc == EOF)
230			return DC_EOF_ERROR;
231		if ( (dc_cmpop() >  0) == !negcmp )
232			if (dc_register_get(peekc, &datum) == DC_SUCCESS)
233				if (dc_eval_and_free_str(datum) == DC_QUIT)
234					return DC_QUIT;
235		return DC_EATONE;
236	case '?':	/* read a line from standard-input and eval it */
237		if (stdin_lookahead != EOF){
238			ungetc(stdin_lookahead, stdin);
239			stdin_lookahead = EOF;
240		}
241		if (dc_eval_and_free_str(dc_readstring(stdin, '\n', '\n')) == DC_QUIT)
242			return DC_QUIT;
243		return DC_OKAY;
244	case '[':	/* read to balancing ']' into a dc_str */
245		return DC_STR;
246	case '!':	/* read to newline and call system() on resulting string */
247		if (peekc == '<' || peekc == '=' || peekc == '>')
248			return DC_NEGCMP;
249		return DC_SYSTEM;
250	case '#':	/* comment; skip remainder of current line */
251		return DC_COMMENT;
252
253	case 'a':	/* Convert top of stack to an ascii character. */
254		if (dc_pop(&datum) == DC_SUCCESS){
255			char tmps;
256			if (datum.dc_type == DC_NUMBER){
257				tmps = (char) dc_num2int(datum.v.number, DC_TOSS);
258			}else if (datum.dc_type == DC_STRING){
259				tmps = *dc_str2charp(datum.v.string);
260				dc_free_str(&datum.v.string);
261			}else{
262				dc_garbage("at top of stack", -1);
263			}
264			dc_push(dc_makestring(&tmps, 1));
265		}
266		break;
267	case 'c':	/* clear whole stack */
268		dc_clear_stack();
269		break;
270	case 'd':	/* duplicate the datum on the top of stack */
271		if (dc_top_of_stack(&datum) == DC_SUCCESS)
272			dc_push(dc_dup(datum));
273		break;
274	case 'f':	/* print list of all stack items */
275		dc_printall(dc_obase);
276		break;
277	case 'i':	/* set input base to value on top of stack */
278		if (dc_pop(&datum) == DC_SUCCESS){
279			tmpint = 0;
280			if (datum.dc_type == DC_NUMBER)
281				tmpint = dc_num2int(datum.v.number, DC_TOSS);
282			if ( ! (2 <= tmpint  &&  tmpint <= DC_IBASE_MAX) )
283				fprintf(stderr,
284						"%s: input base must be a number \
285between 2 and %d (inclusive)\n",
286						progname, DC_IBASE_MAX);
287			else
288				dc_ibase = tmpint;
289		}
290		break;
291	case 'k':	/* set scale to value on top of stack */
292		if (dc_pop(&datum) == DC_SUCCESS){
293			tmpint = -1;
294			if (datum.dc_type == DC_NUMBER)
295				tmpint = dc_num2int(datum.v.number, DC_TOSS);
296			if ( ! (tmpint >= 0) )
297				fprintf(stderr,
298						"%s: scale must be a nonnegative number\n",
299						progname);
300			else
301				dc_scale = tmpint;
302		}
303		break;
304	case 'l':	/* "load" -- push value on top of register stack named
305				 * by peekc onto top of evaluation stack; does not
306				 * modify the register stack
307				 */
308		if (peekc == EOF)
309			return DC_EOF_ERROR;
310		if (dc_register_get(peekc, &datum) == DC_SUCCESS)
311			dc_push(datum);
312		return DC_EATONE;
313	case 'n':	/* print the value popped off of top-of-stack;
314				 * do not add a trailing newline
315				 */
316		if (dc_pop(&datum) == DC_SUCCESS)
317			dc_print(datum, dc_obase, DC_NONL, DC_TOSS);
318		break;
319	case 'o':	/* set output base to value on top of stack */
320		if (dc_pop(&datum) == DC_SUCCESS){
321			tmpint = 0;
322			if (datum.dc_type == DC_NUMBER)
323				tmpint = dc_num2int(datum.v.number, DC_TOSS);
324			if ( ! (tmpint > 1) )
325				fprintf(stderr,
326						"%s: output base must be a number greater than 1\n",
327						progname);
328			else
329				dc_obase = tmpint;
330		}
331		break;
332	case 'p':	/* print the datum on the top of stack,
333				 * with a trailing newline
334				 */
335		if (dc_top_of_stack(&datum) == DC_SUCCESS)
336			dc_print(datum, dc_obase, DC_WITHNL, DC_KEEP);
337		break;
338	case 'q':	/* quit two levels of evaluation, posibly exiting program */
339		unwind_depth = 1; /* the return below is the first level of returns */
340		unwind_noexit = DC_FALSE;
341		return DC_QUIT;
342	case 'r':	/* rotate (swap) the top two elements on the stack
343				 */
344		if (dc_pop(&datum) == DC_SUCCESS) {
345			dc_data datum2;
346			int two_status;
347			two_status = dc_pop(&datum2);
348			dc_push(datum);
349			if (two_status == DC_SUCCESS)
350				dc_push(datum2);
351		}
352		break;
353	case 's':	/* "store" -- replace top of register stack named
354				 * by peekc with the value popped from the top
355				 * of the evaluation stack
356				 */
357		if (peekc == EOF)
358			return DC_EOF_ERROR;
359		if (dc_pop(&datum) == DC_SUCCESS)
360			dc_register_set(peekc, datum);
361		return DC_EATONE;
362	case 'v':	/* replace top of stack with its square root */
363		if (dc_pop(&datum) == DC_SUCCESS){
364			dc_num tmpnum;
365			if (datum.dc_type != DC_NUMBER){
366				fprintf(stderr,
367						"%s: square root of nonnumeric attempted\n",
368						progname);
369			}else if (dc_sqrt(datum.v.number, dc_scale, &tmpnum) == DC_SUCCESS){
370				dc_free_num(&datum.v.number);
371				datum.v.number = tmpnum;
372				dc_push(datum);
373			}
374		}
375		break;
376	case 'x':	/* eval the datum popped from top of stack */
377		if (dc_pop(&datum) == DC_SUCCESS){
378			if (datum.dc_type == DC_STRING){
379				if (dc_eval_and_free_str(datum) == DC_QUIT)
380					return DC_QUIT;
381			}else if (datum.dc_type == DC_NUMBER){
382				dc_push(datum);
383			}else{
384				dc_garbage("at top of stack", -1);
385			}
386		}
387		break;
388	case 'z':	/* push the current stack depth onto the top of stack */
389		dc_push(dc_int2data(dc_tell_stackdepth()));
390		break;
391
392	case 'I':	/* push the current input base onto the stack */
393		dc_push(dc_int2data(dc_ibase));
394		break;
395	case 'K':	/* push the current scale onto the stack */
396		dc_push(dc_int2data(dc_scale));
397		break;
398	case 'L':	/* pop a value off of register stack named by peekc
399				 * and push it onto the evaluation stack
400				 */
401		if (peekc == EOF)
402			return DC_EOF_ERROR;
403		if (dc_register_pop(peekc, &datum) == DC_SUCCESS)
404			dc_push(datum);
405		return DC_EATONE;
406	case 'O':	/* push the current output base onto the stack */
407		dc_push(dc_int2data(dc_obase));
408		break;
409	case 'P':
410		/* Pop the value off the top of a stack.  If it is
411		 * a number, dump out the integer portion of its
412		 * absolute value as a "base UCHAR_MAX+1" byte stream;
413		 * if it is a string, just print it.
414		 * In either case, do not append a trailing newline.
415		 */
416		if (dc_pop(&datum) == DC_SUCCESS){
417			if (datum.dc_type == DC_NUMBER)
418				dc_dump_num(datum.v.number, DC_TOSS);
419			else if (datum.dc_type == DC_STRING)
420				dc_out_str(datum.v.string, DC_NONL, DC_TOSS);
421			else
422				dc_garbage("at top of stack", -1);
423		}
424		break;
425	case 'Q':	/* quit out of top-of-stack nested evals;
426				 * pops value from stack;
427				 * does not exit program (stops short if necessary)
428				 */
429		if (dc_pop(&datum) == DC_SUCCESS){
430			unwind_depth = 0;
431			unwind_noexit = DC_TRUE;
432			if (datum.dc_type == DC_NUMBER)
433				unwind_depth = dc_num2int(datum.v.number, DC_TOSS);
434			if (unwind_depth-- > 0)
435				return DC_QUIT;
436			unwind_depth = 0;	/* paranoia */
437			fprintf(stderr,
438					"%s: Q command requires a number >= 1\n",
439					progname);
440		}
441		break;
442#if 0
443	case 'R':	/* pop a value off of the evaluation stack,;
444				 * rotate the top
445				 remaining stack elements that many
446				 * places forward (negative numbers mean rotate
447				 * backward).
448				 */
449		if (dc_pop(&datum) == DC_SUCCESS){
450			tmpint = 0;
451			if (datum.dc_type == DC_NUMBER)
452				tmpint = dc_num2int(datum.v.number, DC_TOSS);
453			dc_stack_rotate(tmpint);
454		}
455		break;
456#endif
457	case 'S':	/* pop a value off of the evaluation stack
458				 * and push it onto the register stack named by peekc
459				 */
460		if (peekc == EOF)
461			return DC_EOF_ERROR;
462		if (dc_pop(&datum) == DC_SUCCESS)
463			dc_register_push(peekc, datum);
464		return DC_EATONE;
465	case 'X':	/* replace the number on top-of-stack with its scale factor */
466		if (dc_pop(&datum) == DC_SUCCESS){
467			tmpint = 0;
468			if (datum.dc_type == DC_NUMBER)
469				tmpint = dc_tell_scale(datum.v.number, DC_TOSS);
470			dc_push(dc_int2data(tmpint));
471		}
472		break;
473	case 'Z':	/* replace the datum on the top-of-stack with its length */
474		if (dc_pop(&datum) == DC_SUCCESS)
475			dc_push(dc_int2data(dc_tell_length(datum, DC_TOSS)));
476		break;
477
478	case ':':	/* store into array */
479		if (peekc == EOF)
480			return DC_EOF_ERROR;
481		if (dc_pop(&datum) == DC_SUCCESS){
482			tmpint = -1;
483			if (datum.dc_type == DC_NUMBER)
484				tmpint = dc_num2int(datum.v.number, DC_TOSS);
485			if (dc_pop(&datum) == DC_SUCCESS){
486				if (tmpint < 0)
487					fprintf(stderr,
488							"%s: array index must be a nonnegative integer\n",
489							progname);
490				else
491					dc_array_set(peekc, tmpint, datum);
492			}
493		}
494		return DC_EATONE;
495	case ';':	/* retreive from array */
496		if (peekc == EOF)
497			return DC_EOF_ERROR;
498		if (dc_pop(&datum) == DC_SUCCESS){
499			tmpint = -1;
500			if (datum.dc_type == DC_NUMBER)
501				tmpint = dc_num2int(datum.v.number, DC_TOSS);
502			if (tmpint < 0)
503				fprintf(stderr,
504						"%s: array index must be a nonnegative integer\n",
505						progname);
506			else
507				dc_push(dc_array_get(peekc, tmpint));
508		}
509		return DC_EATONE;
510
511	default:	/* What did that user mean? */
512		fprintf(stderr, "%s: ", progname);
513		dc_show_id(stdout, c, " unimplemented\n");
514		break;
515	}
516	return DC_OKAY;
517}
518
519
520/* takes a string and evals it */
521int
522dc_evalstr DC_DECLARG((string))
523	dc_data string DC_DECLEND
524{
525	const char *s;
526	const char *end;
527	const char *p;
528	size_t len;
529	int c;
530	int peekc;
531	int count;
532	int negcmp;
533	int next_negcmp = 0;
534
535	if (string.dc_type != DC_STRING){
536		fprintf(stderr,
537				"%s: eval called with non-string argument\n",
538				progname);
539		return DC_OKAY;
540	}
541	s = dc_str2charp(string.v.string);
542	end = s + dc_strlen(string.v.string);
543	while (s < end){
544		c = *(const unsigned char *)s++;
545		peekc = EOF;
546		if (s < end)
547			peekc = *(const unsigned char *)s;
548		negcmp = next_negcmp;
549		next_negcmp = 0;
550		switch (dc_func(c, peekc, negcmp)){
551		case DC_OKAY:
552			break;
553		case DC_EATONE:
554			if (peekc != EOF)
555				++s;
556			break;
557		case DC_QUIT:
558			if (unwind_depth > 0){
559				--unwind_depth;
560				return DC_QUIT;
561			}
562			return DC_OKAY;
563
564		case DC_INT:
565			input_str_string = s - 1;
566			dc_push(dc_getnum(input_str, dc_ibase, &peekc));
567			s = input_str_string;
568			if (peekc != EOF)
569				--s;
570			break;
571		case DC_STR:
572			count = 1;
573			for (p=s; p<end && count>0; ++p)
574				if (*p == ']')
575					--count;
576				else if (*p == '[')
577					++count;
578			len = p - s;
579			dc_push(dc_makestring(s, len-1));
580			s = p;
581			break;
582		case DC_SYSTEM:
583			s = dc_system(s);
584		case DC_COMMENT:
585			s = memchr(s, '\n', (size_t)(end-s));
586			if (!s)
587				s = end;
588			else
589				++s;
590			break;
591		case DC_NEGCMP:
592			next_negcmp = 1;
593			break;
594
595		case DC_EOF_ERROR:
596			fprintf(stderr, "%s: unexpected EOS\n", progname);
597			return DC_OKAY;
598		}
599	}
600	return DC_OKAY;
601}
602
603
604/* This is the main function of the whole DC program.
605 * Reads the file described by fp, calls dc_func to do
606 * the dirty work, and takes care of dc_func's shortcomings.
607 */
608int
609dc_evalfile DC_DECLARG((fp))
610	FILE *fp DC_DECLEND
611{
612	int c;
613	int peekc;
614	int negcmp;
615	int next_negcmp = 0;
616	dc_data datum;
617
618	stdin_lookahead = EOF;
619	for (c=getc(fp); c!=EOF; c=peekc){
620		peekc = getc(fp);
621		/*
622		 * The following if() is the only place where ``stdin_lookahead''
623		 * might be set to other than EOF:
624		 */
625		if (fp == stdin)
626			stdin_lookahead = peekc;
627		negcmp = next_negcmp;
628		next_negcmp = 0;
629		switch (dc_func(c, peekc, negcmp)){
630		case DC_OKAY:
631			if (stdin_lookahead != peekc  &&  fp == stdin)
632				peekc = getc(fp);
633			break;
634		case DC_EATONE:
635			peekc = getc(fp);
636			break;
637		case DC_QUIT:
638			if (unwind_noexit != DC_TRUE)
639				return DC_SUCCESS;
640			fprintf(stderr,
641					"%s: Q command argument exceeded string execution depth\n",
642					progname);
643			if (stdin_lookahead != peekc  &&  fp == stdin)
644				peekc = getc(fp);
645			break;
646
647		case DC_INT:
648			input_fil_fp = fp;
649			input_pushback = c;
650			ungetc(peekc, fp);
651			dc_push(dc_getnum(input_fil, dc_ibase, &peekc));
652			break;
653		case DC_STR:
654			ungetc(peekc, fp);
655			datum = dc_readstring(fp, '[', ']');
656			dc_push(datum);
657			peekc = getc(fp);
658			break;
659		case DC_SYSTEM:
660			ungetc(peekc, fp);
661			datum = dc_readstring(stdin, '\n', '\n');
662			(void)dc_system(dc_str2charp(datum.v.string));
663			dc_free_str(&datum.v.string);
664			peekc = getc(fp);
665			break;
666		case DC_COMMENT:
667			while (peekc!=EOF && peekc!='\n')
668				peekc = getc(fp);
669			if (peekc != EOF)
670				peekc = getc(fp);
671			break;
672		case DC_NEGCMP:
673			next_negcmp = 1;
674			break;
675
676		case DC_EOF_ERROR:
677			fprintf(stderr, "%s: unexpected EOF\n", progname);
678			return DC_FAIL;
679		}
680	}
681	return DC_SUCCESS;
682}
683