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