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