diff options
author | steve <steve@FreeBSD.org> | 1996-11-04 04:24:33 +0000 |
---|---|---|
committer | steve <steve@FreeBSD.org> | 1996-11-04 04:24:33 +0000 |
commit | 70ff03393a156ba7278858a8f8cf05a8c83491c9 (patch) | |
tree | 9e720074711c6d935b3da3d22b6e13294832a10f /gnu/usr.bin/dc | |
parent | 0be58f23d8dd463dea864d0ad7a6fb8fcb617d73 (diff) | |
download | FreeBSD-src-70ff03393a156ba7278858a8f8cf05a8c83491c9.zip FreeBSD-src-70ff03393a156ba7278858a8f8cf05a8c83491c9.tar.gz |
Upgrade to dc version 1.0 which comes bundled with
bc version 1.03.
Diffstat (limited to 'gnu/usr.bin/dc')
-rw-r--r-- | gnu/usr.bin/dc/ChangeLog | 77 | ||||
-rw-r--r-- | gnu/usr.bin/dc/Makefile | 6 | ||||
-rw-r--r-- | gnu/usr.bin/dc/NEWS | 7 | ||||
-rw-r--r-- | gnu/usr.bin/dc/README | 13 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc-array.c | 105 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc-eval.c | 569 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc-misc.c | 224 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc-number.c | 478 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc-proto.h | 76 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc-regdef.h | 38 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc-stack.c | 367 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc-string.c | 193 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc-version.h | 22 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc.1 | 513 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc.c | 925 | ||||
-rw-r--r-- | gnu/usr.bin/dc/dc.h | 81 | ||||
-rw-r--r-- | gnu/usr.bin/dc/decimal.c | 1235 | ||||
-rw-r--r-- | gnu/usr.bin/dc/decimal.h | 93 | ||||
-rw-r--r-- | gnu/usr.bin/dc/doc/dc.texinfo | 314 |
19 files changed, 2648 insertions, 2688 deletions
diff --git a/gnu/usr.bin/dc/ChangeLog b/gnu/usr.bin/dc/ChangeLog deleted file mode 100644 index 09aaf47..0000000 --- a/gnu/usr.bin/dc/ChangeLog +++ /dev/null @@ -1,77 +0,0 @@ -Fri May 21 15:02:52 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu) - - * Version 0.2 released. - -Fri May 21 11:48:11 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) - - * decimal.c (decimal_rem): Update to match fixes in decimal_div. - -Thu May 20 03:12:41 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu) - - * Makefile.in (realclean): Delete dc.info* and configure. - (DISTFILES): Add `texinfo.tex' and `NEWS'. - texinfo.tex: New file (symlink to canonical source). - NEWS: New file. - -Wed May 19 11:30:09 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) - - * dc.c (dec_read): Accept only A through F. - -Tue May 18 12:35:54 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) - - * dc.c (read_string): New arg STARTC to handle nested brackets. - (execute): Change calls to read_string. - (condop): Don't assume result of decimal_compare has abs value <= 1. - (popmacro): If no macro in progress, exit. - -Sun May 2 00:42:47 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) - - * decimal.c (decimal_div): Include in trial_dividend the digit - at length2 + i - 2, if there is one. - -Sat May 1 09:54:35 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) - - * decimal.c (decimal_parse): Don't use digits without recalculation - if some digit exceeds the radix. - - * dc.c (execute): Treat A...F as digits. - (dec_read): Treat A...F as digits. - -Thu Apr 29 14:17:30 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) - - * decimal.h (bcopy): Use memcpy, not memmove. - - * decimal.c (flush_trailing_digits): Use explicit loop, not bcopy. - -Tue Apr 20 17:21:27 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu) - - * dc.c (pushsqrt): `precision' is an argument to `decimal_sqrt', not - `push'. - -Sat Apr 17 15:47:55 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu) - - * All files: Updated GPL version number. - - * decimal.c: Include decimal.h and delete duplicate declarations. - - * decimal.h [!HAVE_BCOPY]: #define bcopy. - [!HAVE_BZERO]: #define bzero. - -Sun Feb 10 22:06:15 1991 Richard Stallman (rms at mole.ai.mit.edu) - - * dc.c (execute): Insert break; in \n case. - -Sun Jul 29 17:50:14 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) - - * decimal.c (decimal_neg): New function. - -Fri Jul 27 04:11:34 1990 David J. MacKenzie (djm at albert.ai.mit.edu) - - * bceval.c, bclex.c, bcprint.c, bcsym.c: Declare some functions - static. - -Mon Dec 25 03:01:49 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) - - * Makefile: add some missing rules. - - * decimal.c: change a 'max' to 'MAX'. diff --git a/gnu/usr.bin/dc/Makefile b/gnu/usr.bin/dc/Makefile index 57b2ebd..a20cee3 100644 --- a/gnu/usr.bin/dc/Makefile +++ b/gnu/usr.bin/dc/Makefile @@ -1,8 +1,10 @@ PROG= dc -SRCS= dc.c decimal.c -CFLAGS+=-I${.CURDIR} -DHAVE_BCOPY=1 -DHAVE_BZERO=1 +SRCS= dc-array.c dc-eval.c dc-misc.c dc-number.c dc-stack.c dc-string.c number.c +CFLAGS+=-I${.CURDIR} -I${.CURDIR}/../bc -D_POSIX_SOURCE DPADD= ${LIBM} LDADD= -lm SUBDIR+= doc +.PATH: ${.CURDIR}/../bc + .include <bsd.prog.mk> diff --git a/gnu/usr.bin/dc/NEWS b/gnu/usr.bin/dc/NEWS deleted file mode 100644 index 6486afb..0000000 --- a/gnu/usr.bin/dc/NEWS +++ /dev/null @@ -1,7 +0,0 @@ -Changes between version 0.2 and 0.1: - -* You can now have nested square bracket pairs within a string. - -* The letters A-F can now be part of a number when the input radix is -large enough to make them meaningful. - diff --git a/gnu/usr.bin/dc/README b/gnu/usr.bin/dc/README deleted file mode 100644 index c23cc66..0000000 --- a/gnu/usr.bin/dc/README +++ /dev/null @@ -1,13 +0,0 @@ -This is a preliminary release of GNU `dc', since people asked for it. GNU -`bc' (which doesn't rely on a separate `dc') has been available separately -for a couple of years. Eventually this version of `dc' will be merged with -the bc package. - -See comments in the file decimal.c for some limitations in the arbitrary -precision library. It's questionable whether it's worth fixing these -problems since the merged dc will probably use bc's math library instead. -However, you might want to be aware of known problems. - -See the file `INSTALL' for instructions on building and installing dc. - -Please report bugs to bug-gnu-utils@prep.ai.mit.edu. diff --git a/gnu/usr.bin/dc/dc-array.c b/gnu/usr.bin/dc/dc-array.c new file mode 100644 index 0000000..dd16ed9 --- /dev/null +++ b/gnu/usr.bin/dc/dc-array.c @@ -0,0 +1,105 @@ +/* + * implement arrays for dc + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +/* This module is the only one that knows what arrays look like. */ + +#include "config.h" + +#include <stdio.h> /* "dc-proto.h" wants this */ +#include <stdlib.h> +#include "dc.h" +#include "dc-proto.h" +#include "dc-regdef.h" + +/* what's most useful: quick access or sparse arrays? */ +/* I'll go with sparse arrays for now */ +struct dc_array { + int Index; + dc_data value; + struct dc_array *next; +}; +typedef struct dc_array dc_array; + +/* I can find no reason not to place arrays in their own namespace... */ +static dc_array *dc_array_register[DC_REGCOUNT]; + + +/* initialize the arrays to their initial values */ +void +dc_array_init DC_DECLVOID() +{ + int i; + + for (i=0; i<DC_REGCOUNT; ++i) + dc_array_register[i] = NULL; +} + +/* store value into array_id[Index] */ +void +dc_array_set DC_DECLARG((array_id, Index, value)) + int array_id DC_DECLSEP + int Index DC_DECLSEP + dc_data value DC_DECLEND +{ + dc_array *cur; + dc_array *prev=NULL; + dc_array *newentry; + + array_id = regmap(array_id); + cur = dc_array_register[array_id]; + while (cur && cur->Index < Index){ + prev = cur; + cur = cur->next; + } + if (cur && cur->Index == Index){ + if (cur->value.dc_type == DC_NUMBER) + dc_free_num(&cur->value.v.number); + else if (cur->value.dc_type == DC_STRING) + dc_free_str(&cur->value.v.string); + else + dc_garbage(" in array", array_id); + cur->value = value; + }else{ + newentry = dc_malloc(sizeof *newentry); + newentry->Index = Index; + newentry->value = value; + newentry->next = cur; + if (prev) + prev->next = newentry; + else + dc_array_register[array_id] = newentry; + } +} + +/* retrieve a dup of a value from array_id[Index] */ +/* A zero value is returned if the specified value is unintialized. */ +dc_data +dc_array_get DC_DECLARG((array_id, Index)) + int array_id DC_DECLSEP + int Index DC_DECLEND +{ + dc_array *cur; + + for (cur=dc_array_register[regmap(array_id)]; cur; cur=cur->next) + if (cur->Index == Index) + return dc_dup(cur->value); + return dc_int2data(0); +} diff --git a/gnu/usr.bin/dc/dc-eval.c b/gnu/usr.bin/dc/dc-eval.c new file mode 100644 index 0000000..e368caa --- /dev/null +++ b/gnu/usr.bin/dc/dc-eval.c @@ -0,0 +1,569 @@ +/* + * evaluate the dc language, from a FILE* or a string + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +/* This is the only module which knows about the dc input language */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include "dc.h" +#include "dc-proto.h" + +typedef enum { + DC_OKAY, /* no further intervention needed for this command */ + DC_EATONE, /* caller needs to eat the lookahead char */ + DC_QUIT, /* quit out of unwind_depth levels of evaluation */ + + /* with the following return values, the caller does not have to + * fret about rescan_stdin'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 */ + DC_COMMENT, /* caller needs to skip to the next input line */ + + DC_EOF_ERROR /* unexpected end of input; abort current eval */ +} dc_status; + +static int dc_ibase=10; /* input base, 2 <= dc_ibase <= DC_IBASE_MAX */ +static int dc_obase=10; /* output base, 2 <= dc_obase */ +static int dc_scale=0; /* scale (see user documentaton) */ + +/* forward reference */ +static dc_status dc_evalstr DC_PROTO((dc_data)); + +/* for Quitting evaluations */ +static int unwind_depth=0; + +/* if true, active Quit will not exit program */ +static dc_boolean unwind_noexit=DC_FALSE; + +/* if true, stdin has been mucked with, dc_evalfile() needs to resyncronize */ +static dc_boolean rescan_stdin=DC_FALSE; + + +/* input_fil and input_str are passed as arguments to dc_getnum */ + +/* used by the input_* functions: */ +static FILE *input_fil_fp; +static const char *input_str_string; + +/* Since we have a need for two characters of pushback, and + * ungetc() only guarantees one, we place the second pushback here + */ +static int input_pushback; + +/* passed as an argument to dc_getnum */ +static int +input_fil DC_DECLVOID() +{ + if (input_pushback != EOF){ + int c = input_pushback; + input_pushback = EOF; + return c; + } + return getc(input_fil_fp); +} + +/* passed as an argument to dc_getnum */ +static int +input_str DC_DECLVOID() +{ + if (!*input_str_string) + return EOF; + return *input_str_string++; +} + + + +/* takes a string and evals it; frees the string when done */ +/* Wrapper around dc_evalstr to avoid duplicating the free call + * at all possible return points. + */ +static dc_status +dc_eval_and_free_str DC_DECLARG((string)) + dc_data string DC_DECLEND +{ + dc_status status; + + status = dc_evalstr(string); + if (string.dc_type == DC_STRING) + dc_free_str(&string.v.string); + return status; +} + + +/* dc_func does the grunt work of figuring out what each input + * character means; used by both dc_evalstr and dc_evalfile + * + * c -> the "current" input character under consideration + * peekc -> the lookahead input character + */ +static dc_status +dc_func DC_DECLARG((c, peekc)) + int c DC_DECLSEP + int peekc DC_DECLEND +{ + /* we occasionally need these for temporary data */ + /* Despite the GNU coding standards, it is much easier + * to have these decared once here, since this function + * is just one big switch statement. + */ + dc_data datum; + int tmpint; + + switch (c){ + case '_': case '.': + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': case 'A': case 'B': + case 'C': case 'D': case 'E': case 'F': + return DC_INT; + case ' ': + case '\t': + case '\n': + /* standard command separators */ + break; + + case '+': /* add top two stack elements */ + dc_binop(dc_add, dc_scale); + break; + case '-': /* subtract top two stack elements */ + dc_binop(dc_sub, dc_scale); + break; + case '*': /* multiply top two stack elements */ + dc_binop(dc_mul, dc_scale); + break; + case '/': /* divide top two stack elements */ + dc_binop(dc_div, dc_scale); + break; + case '%': + /* take the remainder from division of the top two stack elements */ + dc_binop(dc_rem, dc_scale); + break; + case '^': /* exponientiation of the top two stack elements */ + dc_binop(dc_exp, dc_scale); + break; + case '<': + /* eval register named by peekc if + * less-than holds for top two stack elements + */ + if (peekc == EOF) + return DC_EOF_ERROR; + if (dc_cmpop() < 0) + if (dc_register_get(peekc, &datum) == DC_SUCCESS) + if (dc_eval_and_free_str(datum) == DC_QUIT) + return DC_QUIT; + return DC_EATONE; + case '=': + /* eval register named by peekc if + * equal-to holds for top two stack elements + */ + if (peekc == EOF) + return DC_EOF_ERROR; + if (dc_cmpop() == 0) + if (dc_register_get(peekc, &datum) == DC_SUCCESS) + if (dc_eval_and_free_str(datum) == DC_QUIT) + return DC_QUIT; + return DC_EATONE; + case '>': + /* eval register named by peekc if + * greater-than holds for top two stack elements + */ + if (peekc == EOF) + return DC_EOF_ERROR; + if (dc_cmpop() > 0) + if (dc_register_get(peekc, &datum) == DC_SUCCESS) + if (dc_eval_and_free_str(datum) == DC_QUIT) + return DC_QUIT; + return DC_EATONE; + case '?': /* read a lnie from standard-input and eval it */ + for (c=peekc; c=='\n'; c=getc(stdin)) + ; + ungetc(c, stdin); + if (dc_eval_and_free_str(dc_readstring(stdin, '\n', '\n')) == DC_QUIT) + return DC_QUIT; + rescan_stdin = DC_TRUE; + return DC_OKAY; + case '[': /* read to balancing ']' into a dc_str */ + return DC_STR; + case '!': /* read to newline and call system() on resulting string */ + return DC_SYSTEM; + case '#': /* comment; skip remainder of current line */ + return DC_COMMENT; + + case 'c': /* clear whole stack */ + dc_clear_stack(); + break; + case 'd': /* duplicate the datum on the top of stack */ + if (dc_top_of_stack(&datum) == DC_SUCCESS) + dc_push(dc_dup(datum)); + break; + case 'f': /* print list of all stack items */ + dc_printall(dc_obase); + break; + case 'i': /* set input base to value on top of stack */ + if (dc_pop(&datum) == DC_SUCCESS){ + tmpint = 0; + if (datum.dc_type == DC_NUMBER) + tmpint = dc_num2int(datum.v.number, DC_TRUE); + if ( ! (2 <= tmpint && tmpint <= DC_IBASE_MAX) ) + 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 */ + if (dc_pop(&datum) == DC_SUCCESS){ + tmpint = -1; + if (datum.dc_type == DC_NUMBER) + tmpint = dc_num2int(datum.v.number, DC_TRUE); + if ( ! (tmpint >= 0) ) + fprintf(stderr, + "%s: scale must be a nonnegative number\n", + progname); + else + dc_scale = tmpint; + } + break; + case 'l': /* "load" -- push value on top of register stack named + * by peekc onto top of evaluation stack; does not + * modify the register stack + */ + if (peekc == EOF) + return DC_EOF_ERROR; + if (dc_register_get(peekc, &datum) == DC_SUCCESS) + dc_push(datum); + return DC_EATONE; + case 'o': /* set output base to value on top of stack */ + if (dc_pop(&datum) == DC_SUCCESS){ + tmpint = 0; + if (datum.dc_type == DC_NUMBER) + tmpint = dc_num2int(datum.v.number, DC_TRUE); + if ( ! (tmpint > 1) ) + fprintf(stderr, + "%s: output base must be a number greater than 1\n", + progname); + else + dc_obase = tmpint; + } + break; + case 'p': /* print the datum on the top of stack */ + if (dc_top_of_stack(&datum) == DC_SUCCESS) + dc_print(datum, dc_obase); + break; + case 'q': /* quit two levels of evaluation, posibly exiting program */ + unwind_depth = 2; + unwind_noexit = DC_FALSE; + return DC_QUIT; + case 's': /* "store" -- replace top of register stack named + * by peekc with the value popped from the top + * of the evaluation stack + */ + if (peekc == EOF) + return DC_EOF_ERROR; + if (dc_pop(&datum) == DC_SUCCESS) + dc_register_set(peekc, datum); + return DC_EATONE; + case 'v': /* replace top of stack with its square root */ + if (dc_pop(&datum) == DC_SUCCESS){ + dc_num tmpnum; + if (datum.dc_type != DC_NUMBER){ + fprintf(stderr, + "%s: square root of nonnumeric attempted\n", + progname); + }else if (dc_sqrt(datum.v.number, dc_scale, &tmpnum) == DC_SUCCESS){ + dc_free_num(&datum.v.number); + datum.v.number = tmpnum; + dc_push(datum); + } + } + 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; + case 'z': /* push the current stack depth onto the top of stack */ + dc_push(dc_int2data(dc_tell_stackdepth())); + break; + + case 'I': /* push the current input base onto the stack */ + dc_push(dc_int2data(dc_ibase)); + break; + case 'K': /* push the current scale onto the stack */ + dc_push(dc_int2data(dc_scale)); + break; + case 'L': /* pop a value off of register stack named by peekc + * and push it onto the evaluation stack + */ + if (peekc == EOF) + return DC_EOF_ERROR; + if (dc_register_pop(peekc, &datum) == DC_SUCCESS) + dc_push(datum); + return DC_EATONE; + case 'O': /* push the current output base onto the stack */ + dc_push(dc_int2data(dc_obase)); + break; + case 'P': /* print the value popped off of top-of-stack; + * do not add a trailing newline + */ + if (dc_pop(&datum) == DC_SUCCESS){ + if (datum.dc_type == DC_STRING) + dc_out_str(datum.v.string, DC_FALSE, DC_TRUE); + else if (datum.dc_type == DC_NUMBER) + dc_out_num(datum.v.number, dc_obase, DC_FALSE, DC_TRUE); + else + dc_garbage("at top of stack", -1); + } + break; + case 'Q': /* quit out of top-of-stack nested evals; + * pops value from stack; + * does not exit program (stops short if necessary) + */ + if (dc_pop(&datum) == DC_SUCCESS){ + unwind_depth = 0; + unwind_noexit = DC_TRUE; + if (datum.dc_type == DC_NUMBER) + unwind_depth = dc_num2int(datum.v.number, DC_TRUE); + if (unwind_depth > 0) + return DC_QUIT; + fprintf(stderr, + "%s: Q command requires a positive number\n", + progname); + } + break; + case 'S': /* pop a value off of the evaluation stack + * and push it onto the register stack named by peekc + */ + if (peekc == EOF) + return DC_EOF_ERROR; + if (dc_pop(&datum) == DC_SUCCESS) + dc_register_push(peekc, datum); + return DC_EATONE; + case 'X': /* replace the number on top-of-stack with its scale factor */ + if (dc_pop(&datum) == DC_SUCCESS){ + tmpint = 0; + if (datum.dc_type == DC_NUMBER) + tmpint = dc_tell_scale(datum.v.number, DC_TRUE); + dc_push(dc_int2data(tmpint)); + } + break; + case 'Z': /* replace the datum on the top-of-stack with its length */ + if (dc_pop(&datum) == DC_SUCCESS) + dc_push(dc_int2data(dc_tell_length(datum, DC_TRUE))); + break; + + case ':': /* store into array */ + if (peekc == EOF) + return DC_EOF_ERROR; + if (dc_pop(&datum) == DC_SUCCESS){ + tmpint = -1; + if (datum.dc_type == DC_NUMBER) + tmpint = dc_num2int(datum.v.number, DC_TRUE); + if (dc_pop(&datum) == DC_SUCCESS){ + if (tmpint < 0) + fprintf(stderr, + "%s: array index must be a nonnegative integer\n", + progname); + else + dc_array_set(peekc, tmpint, datum); + } + } + return DC_EATONE; + case ';': /* retreive from array */ + if (peekc == EOF) + return DC_EOF_ERROR; + if (dc_pop(&datum) == DC_SUCCESS){ + tmpint = -1; + if (datum.dc_type == DC_NUMBER) + tmpint = dc_num2int(datum.v.number, DC_TRUE); + if (tmpint < 0) + fprintf(stderr, + "%s: array index must be a nonnegative integer\n", + progname); + else + dc_push(dc_array_get(peekc, tmpint)); + } + return DC_EATONE; + + default: /* What did that user mean? */ + fprintf(stderr, "%s: ", progname); + dc_show_id(stdout, c, " unimplemented\n"); + break; + } + return DC_OKAY; +} + + +/* takes a string and evals it */ +static dc_status +dc_evalstr DC_DECLARG((string)) + dc_data string DC_DECLEND +{ + const char *s; + const char *end; + const char *p; + size_t len; + int c; + int peekc; + int count; + + 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){ + c = *(const unsigned char *)s++; + peekc = EOF; + if (s < end) + peekc = *(const unsigned char *)s; + switch (dc_func(c, peekc)){ + case DC_OKAY: + break; + case DC_EATONE: + if (peekc != EOF) + ++s; + break; + case DC_QUIT: + if (unwind_depth > 0){ + --unwind_depth; + return DC_QUIT; + } + return DC_OKAY; + + case DC_INT: + input_str_string = s - 1; + dc_push(dc_getnum(input_str, dc_ibase, &peekc)); + s = input_str_string; + if (peekc != EOF) + --s; + break; + case DC_STR: + count = 1; + for (p=s; p<end && count>0; ++p) + if (*p == ']') + --count; + else if (*p == '[') + ++count; + len = p - s; + dc_push(dc_makestring(s, len-1)); + s = p; + break; + case DC_SYSTEM: + s = dc_system(s); + case DC_COMMENT: + s = memchr(s, '\n', (size_t)(end-s)); + if (!s) + s = end; + ++s; + break; + + case DC_EOF_ERROR: + fprintf(stderr, "%s: unexpected EOS\n", progname); + return DC_OKAY; + } + } + return DC_OKAY; +} + + +/* This is the main function of the whole DC program. + * Reads the file described by fp, calls dc_func to do + * the dirty work, and takes care of dc_func's shortcomings. + */ +int +dc_evalfile DC_DECLARG((fp)) + FILE *fp DC_DECLEND +{ + int c; + int peekc; + dc_data datum; + + for (c=getc(fp); c!=EOF; c=peekc){ + peekc = getc(fp); + rescan_stdin = DC_FALSE; + switch (dc_func(c, peekc)){ + case DC_OKAY: + if (rescan_stdin == DC_TRUE && fp == stdin) + peekc = getc(fp); + break; + case DC_EATONE: + peekc = getc(fp); + break; + case DC_QUIT: + if (unwind_noexit != DC_TRUE) + return DC_SUCCESS; + fprintf(stderr, + "%s: Q command argument exceeded string execution depth\n", + progname); + if (rescan_stdin == DC_TRUE && fp == stdin) + peekc = getc(fp); + break; + + case DC_INT: + input_fil_fp = fp; + input_pushback = c; + ungetc(peekc, fp); + dc_push(dc_getnum(input_fil, dc_ibase, &peekc)); + break; + case DC_STR: + ungetc(peekc, fp); + datum = dc_readstring(fp, '[', ']'); + dc_push(datum); + peekc = getc(fp); + break; + case DC_SYSTEM: + ungetc(peekc, fp); + datum = dc_readstring(stdin, '\n', '\n'); + (void)dc_system(dc_str2charp(datum.v.string)); + dc_free_str(&datum.v.string); + peekc = getc(fp); + break; + case DC_COMMENT: + while (peekc!=EOF && peekc!='\n') + peekc = getc(fp); + if (peekc != EOF) + peekc = getc(fp); + break; + + case DC_EOF_ERROR: + fprintf(stderr, "%s: unexpected EOF\n", progname); + return DC_FAIL; + } + } + return DC_SUCCESS; +} diff --git a/gnu/usr.bin/dc/dc-misc.c b/gnu/usr.bin/dc/dc-misc.c new file mode 100644 index 0000000..6443c60 --- /dev/null +++ b/gnu/usr.bin/dc/dc-misc.c @@ -0,0 +1,224 @@ +/* + * implement the "dc" Desk Calculator language. + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +/* Written with strong hiding of implementation details + * in their own specialized modules. + */ +/* This module contains miscelaneous functions that have no + * special knowledge of any private data structures. + * They could all be moved to their own separate modules, but + * are agglomerated here for convenience. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "dc.h" +#include "dc-proto.h" + +#include "dc-version.h" + +#ifndef EXIT_SUCCESS /* C89 <stdlib.h> */ +# define EXIT_SUCCESS 0 +#endif +#ifndef EXIT_FAILURE /* C89 <stdlib.h> */ +# define EXIT_FAILURE 1 +#endif + +const char *progname; /* basename of program invocation */ + +/* your generic usage function */ +static void +usage DC_DECLARG((f)) + FILE *f DC_DECLEND +{ + fprintf(f, "Usage: %s [OPTION]\n", progname); + fprintf(f, " --help display this help and exit\n"); + fprintf(f, " --version output version information and exit\n"); +} + +/* returns a pointer to one past the last occurance of c in s, + * or s if c does not occur in s. + */ +static char * +r1bindex DC_DECLARG((s, c)) + char *s DC_DECLSEP + int c DC_DECLEND +{ + char *p = strrchr(s, c); + + if (!p) + return s; + return p + 1; +} + + +int +main DC_DECLARG((argc, argv)) + int argc DC_DECLSEP + char **argv DC_DECLEND +{ + progname = r1bindex(*argv, '/'); + if (argc>1 && strcmp(argv[1], "--version")==0){ + printf("%s\n", Version); + return EXIT_SUCCESS; + }else if (argc>1 && strcmp(argv[1], "--help")==0){ + usage(stdout); + return EXIT_SUCCESS; + }else if (argc==2 && strcmp(argv[1], "--")==0){ + /*just ignore it*/ + }else if (argc != 1){ + usage(stderr); + return EXIT_FAILURE; + } + + dc_math_init(); + dc_string_init(); + dc_register_init(); + dc_array_init(); + dc_evalfile(stdin); + return EXIT_SUCCESS; +} + + +/* print an "out of memory" diagnostic and exit program */ +void +dc_memfail DC_DECLVOID() +{ + fprintf(stderr, "%s: out of memory\n", progname); + exit(EXIT_FAILURE); +} + +/* malloc or die */ +void * +dc_malloc DC_DECLARG((len)) + size_t len DC_DECLEND +{ + void *result = malloc(len); + + if (!result) + dc_memfail(); + return result; +} + + +/* print the id in a human-understandable form + * fp is the output stream to place the output on + * id is the name of the register (or command) to be printed + * suffix is a modifier (such as "stack") to be printed + */ +void +dc_show_id DC_DECLARG((fp, id, suffix)) + FILE *fp DC_DECLSEP + int id DC_DECLSEP + const char *suffix DC_DECLEND +{ + if (isgraph(id)) + fprintf(fp, "'%c' (%#o)%s", id, id, suffix); + else + fprintf(fp, "%#o%s", id, suffix); +} + + +/* report that corrupt data has been detected; + * use the msg and regid (if nonnegative) to give information + * about where the garbage was found, + * + * will abort() so that a debugger might be used to help find + * the bug + */ +/* If this routine is called, then there is a bug in the code; + * i.e. it is _not_ a data or user error + */ +void +dc_garbage DC_DECLARG((msg, regid)) + const char *msg DC_DECLSEP + int regid DC_DECLEND +{ + if (regid < 0){ + fprintf(stderr, "%s: garbage %s\n", progname, msg); + }else{ + fprintf(stderr, "%s:%s register ", progname, msg); + dc_show_id(stderr, regid, " is garbage\n"); + } + abort(); +} + + +/* call system() with the passed string; + * if the string contains a newline, terminate the string + * there before calling system. + * Return a pointer to the first unused character in the string + * (i.e. past the '\n' if there was one, to the '\0' otherwise). + */ +const char * +dc_system DC_DECLARG((s)) + const char *s DC_DECLEND +{ + const char *p; + char *tmpstr; + size_t len; + + p = strchr(s, '\n'); + if (p){ + len = p - s; + tmpstr = dc_malloc(len + 1); + strncpy(tmpstr, s, len); + tmpstr[len] = '\0'; + system(tmpstr); + free(tmpstr); + return p + 1; + } + system(s); + return s + strlen(s); +} + + +/* print out the indicated value */ +void +dc_print DC_DECLARG((value, obase)) + dc_data value DC_DECLSEP + int obase DC_DECLEND +{ + if (value.dc_type == DC_NUMBER){ + dc_out_num(value.v.number, obase, DC_TRUE, DC_FALSE); + }else if (value.dc_type == DC_STRING){ + dc_out_str(value.v.string, DC_TRUE, DC_FALSE); + }else{ + dc_garbage("in data being printed", -1); + } +} + +/* return a duplicate of the passed value, regardless of type */ +dc_data +dc_dup DC_DECLARG((value)) + dc_data value DC_DECLEND +{ + if (value.dc_type!=DC_NUMBER && value.dc_type!=DC_STRING) + dc_garbage("in value being duplicated", -1); + if (value.dc_type == DC_NUMBER) + return dc_dup_num(value.v.number); + /*else*/ + return dc_dup_str(value.v.string); +} diff --git a/gnu/usr.bin/dc/dc-number.c b/gnu/usr.bin/dc/dc-number.c new file mode 100644 index 0000000..8007506 --- /dev/null +++ b/gnu/usr.bin/dc/dc-number.c @@ -0,0 +1,478 @@ +/* + * interface dc to the bc numeric routines + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +/* This should be the only module that knows the internals of type dc_num */ + +#include "config.h" + +#include <stdio.h> +#include <ctype.h> +#include "bcdefs.h" +#include "proto.h" +#include "global.h" +#include "dc.h" +#include "dc-proto.h" + +/* convert an opaque dc_num into a real bc_num */ +#define CastNum(x) ((bc_num)(x)) + +/* add two dc_nums, place into *result; + * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error + */ +int +dc_add DC_DECLARG((a, b, kscale, result)) + dc_num a DC_DECLSEP + dc_num b DC_DECLSEP + int kscale DC_DECLSEP + dc_num *result DC_DECLEND +{ + init_num((bc_num *)result); + bc_add(CastNum(a), CastNum(b), (bc_num *)result); + return DC_SUCCESS; +} + +/* subtract two dc_nums, place into *result; + * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error + */ +int +dc_sub DC_DECLARG((a, b, kscale, result)) + dc_num a DC_DECLSEP + dc_num b DC_DECLSEP + int kscale DC_DECLSEP + dc_num *result DC_DECLEND +{ + init_num((bc_num *)result); + bc_sub(CastNum(a), CastNum(b), (bc_num *)result); + return DC_SUCCESS; +} + +/* multiply two dc_nums, place into *result; + * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error + */ +int +dc_mul DC_DECLARG((a, b, kscale, result)) + dc_num a DC_DECLSEP + dc_num b DC_DECLSEP + int kscale DC_DECLSEP + dc_num *result DC_DECLEND +{ + init_num((bc_num *)result); + bc_multiply(CastNum(a), CastNum(b), (bc_num *)result, kscale); + return DC_SUCCESS; +} + +/* divide two dc_nums, place into *result; + * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error + */ +int +dc_div DC_DECLARG((a, b, kscale, result)) + dc_num a DC_DECLSEP + dc_num b DC_DECLSEP + int kscale DC_DECLSEP + dc_num *result DC_DECLEND +{ + init_num((bc_num *)result); + if (bc_divide(CastNum(a), CastNum(b), (bc_num *)result, kscale)){ + fprintf(stderr, "%s: divide by zero\n", progname); + return DC_DOMAIN_ERROR; + } + return DC_SUCCESS; +} + +/* place the reminder of dividing a by b into *result; + * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error + */ +int +dc_rem DC_DECLARG((a, b, kscale, result)) + dc_num a DC_DECLSEP + dc_num b DC_DECLSEP + int kscale DC_DECLSEP + dc_num *result DC_DECLEND +{ + init_num((bc_num *)result); + if (bc_modulo(CastNum(a), CastNum(b), (bc_num *)result, kscale)){ + fprintf(stderr, "%s: remainder by zero\n", progname); + return DC_DOMAIN_ERROR; + } + return DC_SUCCESS; +} + +/* place the result of exponentiationg a by b into *result; + * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error + */ +int +dc_exp DC_DECLARG((a, b, kscale, result)) + dc_num a DC_DECLSEP + dc_num b DC_DECLSEP + int kscale DC_DECLSEP + dc_num *result DC_DECLEND +{ + init_num((bc_num *)result); + bc_raise(CastNum(a), CastNum(b), (bc_num *)result, kscale); + return DC_SUCCESS; +} + +/* take the square root of the value, place into *result; + * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error + */ +int +dc_sqrt DC_DECLARG((value, kscale, result)) + dc_num value DC_DECLSEP + int kscale DC_DECLSEP + dc_num *result DC_DECLEND +{ + bc_num tmp; + + tmp = copy_num(CastNum(value)); + if (!bc_sqrt(&tmp, kscale)){ + fprintf(stderr, "%s: square root of negative number\n", progname); + free_num(&tmp); + return DC_DOMAIN_ERROR; + } + *((bc_num *)result) = tmp; + return DC_SUCCESS; +} + +/* compare dc_nums a and b; + * return a negative value if a < b; + * return a positive value if a > b; + * return zero value if a == b + */ +int +dc_compare DC_DECLARG((a, b)) + dc_num a DC_DECLSEP + dc_num b DC_DECLEND +{ + return bc_compare(CastNum(a), CastNum(b)); +} + +/* attempt to convert a dc_num to its corresponding int value + * If discard_flag is true then deallocate the value after use. + */ +int +dc_num2int DC_DECLARG((value, discard_flag)) + dc_num value DC_DECLSEP + dc_boolean discard_flag DC_DECLEND +{ + long result; + + result = num2long(CastNum(value)); + if (discard_flag) + dc_free_num(&value); + return (int)result; +} + +/* convert a C integer value into a dc_num */ +/* For convenience of the caller, package the dc_num + * into a dc_data result. + */ +dc_data +dc_int2data DC_DECLARG((value)) + int value DC_DECLEND +{ + dc_data result; + + init_num((bc_num *)&result.v.number); + int2num((bc_num *)&result.v.number, value); + result.dc_type = DC_NUMBER; + return result; +} + +/* get a dc_num from some input stream; + * input is a function which knows how to read the desired input stream + * ibase is the input base (2<=ibase<=DC_IBASE_MAX) + * *readahead will be set to the readahead character consumed while + * looking for the end-of-number + */ +/* For convenience of the caller, package the dc_num + * into a dc_data result. + */ +dc_data +dc_getnum DC_DECLARG((input, ibase, readahead)) + int (*input) DC_PROTO((void)) DC_DECLSEP + int ibase DC_DECLSEP + int *readahead DC_DECLEND +{ + bc_num base; + bc_num result; + bc_num build; + bc_num tmp; + bc_num divisor; + dc_data full_result; + int negative = 0; + int digit; + int decimal; + int c; + + init_num(&tmp); + init_num(&build); + init_num(&base); + result = copy_num(_zero_); + int2num(&base, ibase); + c = (*input)(); + while (isspace(c)) + c = (*input)(); + if (c == '_' || c == '-'){ + negative = c; + c = (*input)(); + }else if (c == '+'){ + c = (*input)(); + } + while (isspace(c)) + c = (*input)(); + for (;;){ + if (isdigit(c)) + digit = c - '0'; + else if ('A' <= c && c <= 'F') + digit = 10 + c - 'A'; + else + break; + c = (*input)(); + int2num(&tmp, digit); + bc_multiply(result, base, &result, 0); + bc_add(result, tmp, &result); + } + if (c == '.'){ + free_num(&build); + free_num(&tmp); + divisor = copy_num(_one_); + build = copy_num(_zero_); + decimal = 0; + for (;;){ + c = (*input)(); + if (isdigit(c)) + digit = c - '0'; + else if ('A' <= c && c <= 'F') + digit = 10 + c - 'A'; + else + break; + int2num(&tmp, digit); + bc_multiply(build, base, &build, 0); + bc_add(build, tmp, &build); + bc_multiply(divisor, base, &divisor, 0); + ++decimal; + } + bc_divide(build, divisor, &build, decimal); + bc_add(result, build, &result); + } + /* Final work. */ + if (negative) + bc_sub(_zero_, result, &result); + + free_num(&tmp); + free_num(&build); + free_num(&base); + if (readahead) + *readahead = c; + full_result.v.number = (dc_num)result; + full_result.dc_type = DC_NUMBER; + return full_result; +} + + +/* return the "length" of the number */ +int +dc_numlen DC_DECLARG((value)) + dc_num value DC_DECLEND +{ + bc_num num = CastNum(value); + + /* is this right??? */ + return num->n_len + num->n_scale; +} + +/* return the scale factor of the passed dc_num + * If discard_flag is true then deallocate the value after use. + */ +int +dc_tell_scale DC_DECLARG((value, discard_flag)) + dc_num value DC_DECLSEP + dc_boolean discard_flag DC_DECLEND +{ + int kscale; + + kscale = CastNum(value)->n_scale; + if (discard_flag) + dc_free_num(&value); + return kscale; +} + + +/* initialize the math subsystem */ +void +dc_math_init DC_DECLVOID() +{ + init_numbers(); +} + +/* print out a dc_num in output base obase to stdout; + * if newline is true, terminate output with a '\n'; + * if discard_flag is true then deallocate the value after use + */ +void +dc_out_num DC_DECLARG((value, obase, newline, discard_flag)) + dc_num value DC_DECLSEP + int obase DC_DECLSEP + dc_boolean newline DC_DECLSEP + dc_boolean discard_flag DC_DECLEND +{ + out_num(CastNum(value), obase, out_char); + if (newline) + out_char('\n'); + if (discard_flag) + dc_free_num(&value); +} + + +/* deallocate an instance of a dc_num */ +void +dc_free_num DC_DECLARG((value)) + dc_num *value DC_DECLEND +{ + free_num((bc_num *)value); +} + +/* return a duplicate of the number in the passed value */ +/* The mismatched data types forces the caller to deal with + * bad dc_type'd dc_data values, and makes it more convenient + * for the caller to not have to do the grunge work of setting + * up a dc_type result. + */ +dc_data +dc_dup_num DC_DECLARG((value)) + dc_num value DC_DECLEND +{ + dc_data result; + + ++CastNum(value)->n_refs; + result.v.number = value; + result.dc_type = DC_NUMBER; + return result; +} + + + +/*---------------------------------------------------------------------------\ +| The rest of this file consists of stubs for bc routines called by number.c | +| so as to minimize the amount of bc code needed to build dc. | +| The bulk of the code was just lifted straight out of the bc source. | +\---------------------------------------------------------------------------*/ + +#include <stdlib.h> + +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + + +int out_col = 0; + +/* Output routines: Write a character CH to the standard output. + It keeps track of the number of characters output and may + break the output with a "\<cr>". */ + +void +out_char (ch) + char ch; +{ + + if (ch == '\n') + { + out_col = 0; + putchar ('\n'); + } + else + { + out_col++; + if (out_col == 70) + { + putchar ('\\'); + putchar ('\n'); + out_col = 1; + } + putchar (ch); + } +} + +/* Malloc could not get enought memory. */ + +void +out_of_memory() +{ + dc_memfail(); +} + +/* Runtime error will print a message and stop the machine. */ + +#if __STDC__ +void +rt_error (char *mesg, ...) +#else +void +rt_error (mesg, va_alist) + char *mesg; + va_dcl +#endif +{ + va_list args; + char error_mesg [255]; + +#if __STDC__ + va_start (args, mesg); +#else + va_start (args); +#endif + vsprintf (error_mesg, mesg, args); + va_end (args); + + fprintf (stderr, "Runtime error: %s\n", error_mesg); +} + + +/* A runtime warning tells of some action taken by the processor that + may change the program execution but was not enough of a problem + to stop the execution. */ + +#if __STDC__ +void +rt_warn (char *mesg, ...) +#else +void +rt_warn (mesg, va_alist) + char *mesg; + va_dcl +#endif +{ + va_list args; + char error_mesg [255]; + +#if __STDC__ + va_start (args, mesg); +#else + va_start (args); +#endif + vsprintf (error_mesg, mesg, args); + va_end (args); + + fprintf (stderr, "Runtime warning: %s\n", error_mesg); +} diff --git a/gnu/usr.bin/dc/dc-proto.h b/gnu/usr.bin/dc/dc-proto.h new file mode 100644 index 0000000..97d7aff --- /dev/null +++ b/gnu/usr.bin/dc/dc-proto.h @@ -0,0 +1,76 @@ +/* + * prototypes of all externally visible dc functions + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +extern const char *dc_str2charp DC_PROTO((dc_str)); +extern const char *dc_system DC_PROTO((const char *)); +extern void *dc_malloc DC_PROTO((size_t)); + +extern void dc_array_set DC_PROTO((int, int, dc_data)); +extern void dc_array_init DC_PROTO((void)); +extern void dc_binop DC_PROTO((int (*)(dc_num, dc_num, int, dc_num *), int)); +extern void dc_clear_stack DC_PROTO((void)); +extern void dc_free_num DC_PROTO((dc_num *)); +extern void dc_free_str DC_PROTO((dc_str *)); +extern void dc_garbage DC_PROTO((const char *, int)); +extern void dc_math_init DC_PROTO((void)); +extern void dc_memfail DC_PROTO((void)); +extern void dc_out_num DC_PROTO((dc_num, int, dc_boolean, dc_boolean)); +extern void dc_out_str DC_PROTO((dc_str, dc_boolean, dc_boolean)); +extern void dc_print DC_PROTO((dc_data, int)); +extern void dc_printall DC_PROTO((int)); +extern void dc_push DC_PROTO((dc_data)); +extern void dc_register_init DC_PROTO((void)); +extern void dc_register_push DC_PROTO((int, dc_data)); +extern void dc_register_set DC_PROTO((int, dc_data)); +extern void dc_show_id DC_PROTO((FILE *, int, const char *)); +extern void dc_string_init DC_PROTO((void)); + +extern int dc_cmpop DC_PROTO((void)); +extern int dc_compare DC_PROTO((dc_num, dc_num)); +extern int dc_evalfile DC_PROTO((FILE *)); +extern int dc_num2int DC_PROTO((dc_num, dc_boolean)); +extern int dc_numlen DC_PROTO((dc_num)); +extern int dc_pop DC_PROTO((dc_data *)); +extern int dc_register_get DC_PROTO((int, dc_data *)); +extern int dc_register_pop DC_PROTO((int, dc_data *)); +extern int dc_tell_length DC_PROTO((dc_data, dc_boolean)); +extern int dc_tell_scale DC_PROTO((dc_num, dc_boolean)); +extern int dc_tell_stackdepth DC_PROTO((void)); +extern int dc_top_of_stack DC_PROTO((dc_data *)); + +extern size_t dc_strlen DC_PROTO((dc_str)); + +extern dc_data dc_array_get DC_PROTO((int, int)); +extern dc_data dc_dup DC_PROTO((dc_data)); +extern dc_data dc_dup_num DC_PROTO((dc_num)); +extern dc_data dc_dup_str DC_PROTO((dc_str)); +extern dc_data dc_getnum DC_PROTO((int (*)(void), int, int *)); +extern dc_data dc_int2data DC_PROTO((int)); +extern dc_data dc_makestring DC_PROTO((const char *, size_t)); +extern dc_data dc_readstring DC_PROTO((FILE *, int , int)); + +extern int dc_add DC_PROTO((dc_num, dc_num, int, dc_num *)); +extern int dc_div DC_PROTO((dc_num, dc_num, int, dc_num *)); +extern int dc_exp DC_PROTO((dc_num, dc_num, int, dc_num *)); +extern int dc_mul DC_PROTO((dc_num, dc_num, int, dc_num *)); +extern int dc_rem DC_PROTO((dc_num, dc_num, int, dc_num *)); +extern int dc_sub DC_PROTO((dc_num, dc_num, int, dc_num *)); +extern int dc_sqrt DC_PROTO((dc_num, int, dc_num *)); diff --git a/gnu/usr.bin/dc/dc-regdef.h b/gnu/usr.bin/dc/dc-regdef.h new file mode 100644 index 0000000..4142205 --- /dev/null +++ b/gnu/usr.bin/dc/dc-regdef.h @@ -0,0 +1,38 @@ +/* + * definitions for dc's "register" declarations + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +#include <limits.h> + +/* determine how many register stacks there are */ +#ifndef DC_REGCOUNT +# ifndef UCHAR_MAX +# define DC_REGCOUNT 256 +# else +# define DC_REGCOUNT (UCHAR_MAX+1) +# endif +#endif /* not DC_REGCOUNT */ + +/* efficiency hack for masking arbritrary integers to 0..(DC_REGCOUNT-1) */ +#if (DC_REGCOUNT & (DC_REGCOUNT-1)) == 0 /* DC_REGCOUNT is power of 2 */ +# define regmap(r) ((r) & (DC_REGCOUNT-1)) +#else +# define regmap(r) ((r) % DC_REGCOUNT) +#endif diff --git a/gnu/usr.bin/dc/dc-stack.c b/gnu/usr.bin/dc/dc-stack.c new file mode 100644 index 0000000..76e61b3 --- /dev/null +++ b/gnu/usr.bin/dc/dc-stack.c @@ -0,0 +1,367 @@ +/* + * implement stack functions for dc + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +/* This module is the only one that knows what stacks (both the + * regular evaluation stack and the named register stacks) + * look like. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include "dc.h" +#include "dc-proto.h" +#include "dc-regdef.h" + +/* an oft-used error message: */ +#define Empty_Stack fprintf(stderr, "%s: stack empty\n", progname) + + +/* simple linked-list implementaion suffices: */ +struct dc_list { + dc_data value; + struct dc_list *link; +}; +typedef struct dc_list dc_list; + +/* the anonymous evaluation stack */ +static dc_list *dc_stack=NULL; + +/* the named register stacks */ +static dc_list *dc_register[DC_REGCOUNT]; + + +/* allocate a new dc_list item */ +static dc_list * +dc_alloc DC_DECLVOID() +{ + dc_list *result; + + result = dc_malloc(sizeof *result); + result->value.dc_type = DC_UNINITIALIZED; + result->link = NULL; + return result; +} + + +/* check that there are two numbers on top of the stack, + * then call op with the popped numbers. Construct a dc_data + * value from the dc_num returned by op and push it + * on the stack. + * If the op call doesn't return DC_SUCCESS, then leave the stack + * unmodified. + */ +void +dc_binop DC_DECLARG((op, kscale)) + int (*op)DC_PROTO((dc_num, dc_num, int, dc_num *)) DC_DECLSEP + int kscale DC_DECLEND +{ + dc_data a; + dc_data b; + dc_data r; + + if (!dc_stack || !dc_stack->link){ + Empty_Stack; + return; + } + if (dc_stack->value.dc_type!=DC_NUMBER + || dc_stack->link->value.dc_type!=DC_NUMBER){ + fprintf(stderr, "%s: non-numeric value\n", progname); + return; + } + (void)dc_pop(&b); + (void)dc_pop(&a); + if ((*op)(a.v.number, b.v.number, kscale, &r.v.number) == DC_SUCCESS){ + r.dc_type = DC_NUMBER; + dc_push(r); + dc_free_num(&a.v.number); + dc_free_num(&b.v.number); + }else{ + /* op failed; restore the stack */ + dc_push(a); + dc_push(b); + } +} + +/* check that there are two numbers on top of the stack, + * then call dc_compare with the popped numbers. + * Return negative, zero, or positive based on the ordering + * of the two numbers. + */ +int +dc_cmpop DC_DECLVOID() +{ + int result; + dc_data a; + dc_data b; + + if (!dc_stack || !dc_stack->link){ + Empty_Stack; + return 0; + } + if (dc_stack->value.dc_type!=DC_NUMBER + || dc_stack->link->value.dc_type!=DC_NUMBER){ + fprintf(stderr, "%s: non-numeric value\n", progname); + return 0; + } + (void)dc_pop(&b); + (void)dc_pop(&a); + result = dc_compare(b.v.number, a.v.number); + dc_free_num(&a.v.number); + dc_free_num(&b.v.number); + return result; +} + + +/* initialize the register stacks to their initial values */ +void +dc_register_init DC_DECLVOID() +{ + int i; + + for (i=0; i<DC_REGCOUNT; ++i) + dc_register[i] = NULL; +} + +/* clear the evaluation stack */ +void +dc_clear_stack DC_DECLVOID() +{ + dc_list *n; + dc_list *t; + + for (n=dc_stack; n; n=t){ + t = n->link; + if (n->value.dc_type == DC_NUMBER) + dc_free_num(&n->value.v.number); + else if (n->value.dc_type == DC_STRING) + dc_free_str(&n->value.v.string); + else + dc_garbage("in stack", -1); + free(n); + } + dc_stack = NULL; +} + +/* push a value onto the evaluation stack */ +void +dc_push DC_DECLARG((value)) + dc_data value DC_DECLEND +{ + dc_list *n = dc_alloc(); + + if (value.dc_type!=DC_NUMBER && value.dc_type!=DC_STRING) + dc_garbage("in data being pushed", -1); + n->value = value; + n->link = dc_stack; + dc_stack = n; +} + +/* push a value onto the named register stack */ +void +dc_register_push DC_DECLARG((stackid, value)) + int stackid DC_DECLSEP + dc_data value DC_DECLEND +{ + dc_list *n = dc_alloc(); + + stackid = regmap(stackid); + n->value = value; + n->link = dc_register[stackid]; + dc_register[stackid] = n; +} + +/* set *result to the value on the top of the evaluation stack */ +/* The caller is responsible for duplicating the value if it + * is to be maintained as anything more than a transient identity. + * + * DC_FAIL is returned if the stack is empty (and *result unchanged), + * DC_SUCCESS is returned otherwise + */ +int +dc_top_of_stack DC_DECLARG((result)) + dc_data *result DC_DECLEND +{ + if (!dc_stack){ + Empty_Stack; + return DC_FAIL; + } + if (dc_stack->value.dc_type!=DC_NUMBER + && dc_stack->value.dc_type!=DC_STRING) + dc_garbage("at top of stack", -1); + *result = dc_stack->value; + return DC_SUCCESS; +} + +/* set *result to a dup of the value on the top of the named register stack */ +/* + * DC_FAIL is returned if the named stack is empty (and *result unchanged), + * DC_SUCCESS is returned otherwise + */ +int +dc_register_get DC_DECLARG((regid, result)) + int regid DC_DECLSEP + dc_data *result DC_DECLEND +{ + dc_list *r; + + regid = regmap(regid); + r = dc_register[regid]; + if ( ! r ){ + fprintf(stderr, "%s: register ", progname); + dc_show_id(stderr, regid, " is empty\n"); + return DC_FAIL; + } + *result = dc_dup(r->value); + return DC_SUCCESS; +} + +/* set the top of the named register stack to the indicated value */ +/* If the named stack is empty, craft a stack entry to enter the + * value into. + */ +void +dc_register_set DC_DECLARG((regid, value)) + int regid DC_DECLSEP + dc_data value DC_DECLEND +{ + dc_list *r; + + regid = regmap(regid); + r = dc_register[regid]; + if ( ! r ) + dc_register[regid] = dc_alloc(); + else if (r->value.dc_type == DC_NUMBER) + dc_free_num(&r->value.v.number); + else if (r->value.dc_type == DC_STRING) + dc_free_str(&r->value.v.string); + else + dc_garbage("", regid); + dc_register[regid]->value = value; +} + +/* pop from the evaluation stack + * + * DC_FAIL is returned if the stack is empty (and *result unchanged), + * DC_SUCCESS is returned otherwise + */ +int +dc_pop DC_DECLARG((result)) + dc_data *result DC_DECLEND +{ + dc_list *r; + + r = dc_stack; + if (!r){ + Empty_Stack; + return DC_FAIL; + } + if (r->value.dc_type!=DC_NUMBER && r->value.dc_type!=DC_STRING) + dc_garbage("at top of stack", -1); + *result = r->value; + dc_stack = r->link; + free(r); + return DC_SUCCESS; +} + +/* pop from the named register stack + * + * DC_FAIL is returned if the named stack is empty (and *result unchanged), + * DC_SUCCESS is returned otherwise + */ +int +dc_register_pop DC_DECLARG((stackid, result)) + int stackid DC_DECLSEP + dc_data *result DC_DECLEND +{ + dc_list *r; + + stackid = regmap(stackid); + r = dc_register[stackid]; + if (!r){ + fprintf(stderr, "%s: stack register ", progname); + dc_show_id(stderr, stackid, " is empty\n"); + return DC_FAIL; + } + if (r->value.dc_type!=DC_NUMBER && r->value.dc_type!=DC_STRING) + dc_garbage(" stack", stackid); + *result = r->value; + dc_register[stackid] = r->link; + free(r); + return DC_SUCCESS; +} + + +/* tell how many entries are currently on the evaluation stack */ +int +dc_tell_stackdepth DC_DECLVOID() +{ + dc_list *n; + int depth=0; + + for (n=dc_stack; n; n=n->link) + ++depth; + return depth; +} + + +/* return the length of the indicated data value; + * if discard_flag is true, the deallocate the value when done + * + * The definition of a datum's length is deligated to the + * appropriate module. + */ +int +dc_tell_length DC_DECLARG((value, discard_flag)) + dc_data value DC_DECLSEP + dc_boolean discard_flag DC_DECLEND +{ + int length; + + if (value.dc_type == DC_NUMBER){ + length = dc_numlen(value.v.number); + if (discard_flag == DC_TRUE) + dc_free_num(&value.v.number); + } else if (value.dc_type == DC_STRING) { + length = dc_strlen(value.v.string); + if (discard_flag == DC_TRUE) + dc_free_str(&value.v.string); + } else { + dc_garbage("in tell_length", -1); + /*NOTREACHED*/ + length = 0; /*just to suppress spurious compiler warnings*/ + } + return length; +} + + + +/* print out all of the values on the evaluation stack */ +void +dc_printall DC_DECLARG((obase)) + int obase DC_DECLEND +{ + dc_list *n; + + for (n=dc_stack; n; n=n->link) + dc_print(n->value, obase); +} diff --git a/gnu/usr.bin/dc/dc-string.c b/gnu/usr.bin/dc/dc-string.c new file mode 100644 index 0000000..262cb03 --- /dev/null +++ b/gnu/usr.bin/dc/dc-string.c @@ -0,0 +1,193 @@ +/* + * implement string functions for dc + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +/* This should be the only module that knows the internals of type dc_string */ + +#include "config.h" + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include "dc.h" +#include "dc-proto.h" + +struct dc_string { + char *s_ptr; /* pointer to base of string */ + size_t s_len; /* length of counted string */ + int s_refs; /* reference count to cut down on memory use by duplicates */ +}; + + +/* return a duplicate of the string in the passed value */ +/* The mismatched data types forces the caller to deal with + * bad dc_type'd dc_data values, and makes it more convenient + * for the caller to not have to do the grunge work of setting + * up a dc_type result. + */ +dc_data +dc_dup_str DC_DECLARG((value)) + dc_str value DC_DECLEND +{ + dc_data result; + + ++((struct dc_string *)value)->s_refs; + result.v.string = value; + result.dc_type = DC_STRING; + return result; +} + +/* free an instance of a dc_str value */ +void +dc_free_str DC_DECLARG((value)) + dc_str *value DC_DECLEND +{ + struct dc_string *string = *value; + + if (--string->s_refs < 1){ + free(string->s_ptr); + free(string); + } +} + +/* Output a dc_str value. + * Add a trailing newline if "newline" is set. + * Free the value after use if discard_flag is set. + */ +void +dc_out_str DC_DECLARG((value, newline, discard_flag)) + dc_str value DC_DECLSEP + dc_boolean newline DC_DECLSEP + dc_boolean discard_flag DC_DECLEND +{ + struct dc_string *string = value; + + printf("%s", string->s_ptr); + if (newline == DC_TRUE) + printf("\n"); + if (discard_flag == DC_TRUE) + dc_free_str(&value); +} + +/* make a copy of a string (base s, length len) + * into a dc_str value; return a dc_data result + * with this value + */ +dc_data +dc_makestring DC_DECLARG((s, len)) + const char *s DC_DECLSEP + size_t len DC_DECLEND +{ + dc_data result; + struct dc_string *string; + + string = dc_malloc(sizeof *string); + string->s_ptr = dc_malloc(len+1); + memcpy(string->s_ptr, s, len); + string->s_ptr[len] = '\0'; /* nul terminated for those who need it */ + string->s_len = len; + string->s_refs = 1; + result.v.string = string; + result.dc_type = DC_STRING; + return result; +} + +/* read a dc_str value from FILE *fp; + * if ldelim == rdelim, then read until a ldelim char or EOF is reached; + * if ldelim != rdelim, then read until a matching rdelim for the + * (already eaten) first ldelim is read. + * Return a dc_data result with the dc_str value as its contents. + */ +dc_data +dc_readstring DC_DECLARG((fp, ldelim, rdelim)) + FILE *fp DC_DECLSEP + int ldelim DC_DECLSEP + int rdelim DC_DECLEND +{ + static char *line_buf = NULL; /* a buffer to build the string in */ + static size_t buflen = 0; /* the current size of line_buf */ + int depth=1; + int c; + char *p; + const char *end; + + if (!line_buf){ + /* initial buflen should be large enough to handle most cases */ + buflen = 2016; + line_buf = dc_malloc(buflen); + } + p = line_buf; + end = line_buf + buflen; + for (;;){ + c = getc(fp); + if (c == EOF) + break; + else if (c == rdelim && --depth < 1) + break; + else if (c == ldelim) + ++depth; + if (p >= end){ + ptrdiff_t offset = p - line_buf; + /* buflen increment should be big enough + * to avoid execessive reallocs: + */ + buflen += 2048; + line_buf = realloc(line_buf, buflen); + if (!line_buf) + dc_memfail(); + p = line_buf + offset; + end = line_buf + buflen; + } + *p++ = c; + } + return dc_makestring(line_buf, (size_t)(p-line_buf)); +} + +/* return the base pointer of the dc_str value; + * This function is needed because no one else knows what dc_str + * looks like. + */ +const char * +dc_str2charp DC_DECLARG((value)) + dc_str value DC_DECLEND +{ + return ((struct dc_string *)value)->s_ptr; +} + +/* return the length of the dc_str value; + * This function is needed because no one else knows what dc_str + * looks like, and strlen(dc_str2charp(value)) won't work + * if there's an embedded '\0'. + */ +size_t +dc_strlen DC_DECLARG((value)) + dc_str value DC_DECLEND +{ + return ((struct dc_string *)value)->s_len; +} + + +/* initialize the strings subsystem */ +void +dc_string_init DC_DECLVOID() +{ + /* nothing to do for this implementation */ +} diff --git a/gnu/usr.bin/dc/dc-version.h b/gnu/usr.bin/dc/dc-version.h new file mode 100644 index 0000000..917be94 --- /dev/null +++ b/gnu/usr.bin/dc/dc-version.h @@ -0,0 +1,22 @@ +/* + * dc version number + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +#define Version "dc 1.0" diff --git a/gnu/usr.bin/dc/dc.1 b/gnu/usr.bin/dc/dc.1 index 065baf6..6d8baf5 100644 --- a/gnu/usr.bin/dc/dc.1 +++ b/gnu/usr.bin/dc/dc.1 @@ -1,278 +1,389 @@ -.\" $Id$ -.TH DC 1 "03 Aug 1993" "GNU Project" -.SH NAME -dc, An Arbitrary Precision Calculator -.SH SYNOPSIS -.B dc -.SH DESCRIPTION -.PP -DC is a reverse-polish desk calculator which supports unlimited -precision arithmetic. It also allows you to define and call macros. -Normally DC reads from the standard input; if any command arguments -are given to it, they are filenames, and DC reads and executes the -contents of the files before reading from standard input. All output -is to standard output. - -A reverse-polish calculator stores numbers on a stack. Entering a -number pushes it on the stack. Arithmetic operations pop arguments off -the stack and push the results. - -To enter a number in DC, type the digits, with an optional decimal -point. Exponential notation is not supported. To enter a negative -number, begin the number with `_'. `-' cannot be used for this, as it -is a binary operator for subtraction instead. To enter two numbers in -succession, separate them with spaces or newlines. These have no -meaning as commands. +.TH DC 1 "07 Apr 1994" "GNU Project" +.ds dc \fIdc\fP +.ds Dc \fIDc\fP +.SH +NAME +dc \- an arbitrary precision calculator +.SH +SYNOPSIS +dc +.SH +DESCRIPTION +.PP +\*(Dc is a reverse-polish desk calculator which supports +unlimited precision arithmetic. +It also allows you to define and call macros. +Normally \*(dc reads from the standard input; +if any command arguments are given to it, they are filenames, +and \*(dc reads and executes the contents of the files before reading +from standard input. +All normal output is to standard output; +all error output is to standard error. +.PP +A reverse-polish calculator stores numbers on a stack. +Entering a number pushes it on the stack. +Arithmetic operations pop arguments off the stack and push the results. +.PP +To enter a number in +.IR dc , +type the digits with an optional decimal point. +Exponential notation is not supported. +To enter a negative number, +begin the number with ``_''. +``-'' cannot be used for this, +as it is a binary operator for subtraction instead. +To enter two numbers in succession, +separate them with spaces or newlines. +These have no meaning as commands. .PD -.SH "Printing Commands" -.PP +.SH +Printing Commands +.TP .B p Prints the value on the top of the stack, -without altering the stack. A newline is printed -after the value. -.PP +without altering the stack. +A newline is printed after the value. +.TP .B P -Prints the value on the top of the stack, -popping it off, and does not print a newline after. -.PP +Prints the value on the top of the stack, popping it off, +and does not print a newline after. +.TP .B f Prints the entire contents of the stack +.ig and the contents of all of the registers, -without altering anything. This is a good command -to use if you are lost or want to figure out -what the effect of some command has been. +.. +without altering anything. +This is a good command to use if you are lost or want +to figure out what the effect of some command has been. .PD -.SH "Arithmetic" -.PP +.SH +Arithmetic +.TP .B + Pops two values off the stack, adds them, -and pushes the result. The precision of the result -is determined only by the values of the arguments, +and pushes the result. +The precision of the result is determined only +by the values of the arguments, and is enough to be exact. -.PP +.TP .B - -Pops two values, subtracts the first one popped -from the second one popped, and pushes the result. -.PP +Pops two values, +subtracts the first one popped from the second one popped, +and pushes the result. +.TP .B * Pops two values, multiplies them, and pushes the result. The number of fraction digits in the result is controlled -by the current precision flag (see below) and does not +by the current precision value (see below) and does not depend on the values being multiplied. -.PP +.TP .B / -Pops two values, divides the second one popped from -the first one popped, and pushes the result. -The number of fraction digits is specified by the precision flag. -.PP +Pops two values, +divides the second one popped from the first one popped, +and pushes the result. +The number of fraction digits is specified by the precision value. +.TP .B % -Pops two values, computes the remainder of the division -that the \fB/\fR command would do, and pushes that. +Pops two values, +computes the remainder of the division that the +.B / +command would do, +and pushes that. The division is done with as many fraction digits -as the precision flag specifies, and the remainder -is also computed with that many fraction digits. -.PP +as the precision value specifies, +and the remainder is also computed with that many fraction digits. +.TP .B ^ -Pops two values and exponentiates, using the first -value popped as the exponent and the second popped as the base. +Pops two values and exponentiates, +using the first value popped as the exponent +and the second popped as the base. The fraction part of the exponent is ignored. -The precision flag specifies the number of fraction +The precision value specifies the number of fraction digits in the result. -.PP +.TP .B v -Pops one value, computes its square root, and pushes that. -The precision flag specifies the number of fraction digits -in the result. +Pops one value, +computes its square root, +and pushes that. +The precision value specifies the number of fraction digits in the result. .PP -Most arithmetic operations are affected by the "precision flag", +Most arithmetic operations are affected by the ``precision value'', which you can set with the -.BR k -command. The default precision -value is zero, which means that all arithmetic except for +.B k +command. +The default precision value is zero, +which means that all arithmetic except for addition and subtraction produces integer results. .PP The remainder operation -.BR % -requires some explanation: applied to -arguments `a' and `b' it produces `a - (b * (a / b))', -where `a / b' is computed in the current precision. -.PP -.SH "Stack Control" -.PP +.B % +requires some explanation: +applied to arguments ``a'' and ``b'' it produces ``a - (b * (a / b))'', +where ``a / b'' is computed in the current precision. +.SH +Stack Control +.TP .B c Clears the stack, rendering it empty. -.PP +.TP .B d Duplicates the value on the top of the stack, -pushing another copy of it. Thus, -`4d*p' computes 4 squared and prints it. -.SH "Registers" -.PP -DC provides 128 memory registers, each named by a single -ASCII character. You can store a number in a register -and retrieve it later. -.PP -.B s\fIr\fR +pushing another copy of it. +Thus, ``4d*p'' computes 4 squared and prints it. +.SH +Registers +.PP +\*(Dc provides 256 memory registers, +each named by a single character. +You can store a number or a string in a register and retrieve it later. +.TP +.BI s r Pop the value off the top of the stack and store -it into register \fIr\fR. -.PP -.B l\fIr\fR -Copy the value in register \fIr\fR and push it onto the stack. This -does not alter the contents of \fIr\fR. -.PP -Each register also contains its own stack. The current -register value is the top of the register's stack. -.PP -.B S\fIr\fR +it into register +.IR r . +.TP +.BI l r +Copy the value in register +.I r +and push it onto the stack. +This does not alter the contents of +.IR r . +.PP +Each register also contains its own stack. +The current register value is the top of the register's stack. +.TP +.BI S r Pop the value off the top of the (main) stack and -push it onto the stack of register \fIr\fR. +push it onto the stack of register +.IR r . The previous value of the register becomes inaccessible. -.PP -.B L\fIr\fR -Pop the value off the top of register \fIr\fR's stack -and push it onto the main stack. The previous value -in register \fIr\fR's stack, if any, is now accessible -via the -.BR Ir +.TP +.BI L r +Pop the value off the top of register +.IR r 's +stack and push it onto the main stack. +The previous value +in register +.IR r 's +stack, if any, +is now accessible via the +.BI l r command. +.ig .PP The -.BR f -command prints a list of all registers that have contents -stored in them, together with their contents. Only the -current contents of each register (the top of its stack) +.B f +command prints a list of all registers that have contents stored in them, +together with their contents. +Only the current contents of each register +(the top of its stack) is printed. +.. +.SH +Parameters .PP -.SH "Parameters" -.PP -DC has three parameters that control its operation: the precision, the -input radix, and the output radix. The precision specifies the number +\*(Dc has three parameters that control its operation: +the precision, the input radix, and the output radix. +The precision specifies the number of fraction digits to keep in the result of most arithmetic operations. The input radix controls the interpretation of numbers typed in; -allnumbers typed in use this radix. The output radix is used -for printing numbers. -.PP -The input and output radices are separate parameters; you can make them -unequal, which can be useful or confusing. Each radix must be between 2 -and 36 inclusive. The precision must be zero or greater. The precision -is always measured in decimal digits, regardless of the current input or -output radix. -.PP +all numbers typed in use this radix. +The output radix is used for printing numbers. +.PP +The input and output radices are separate parameters; +you can make them unequal, +which can be useful or confusing. +The input radix must be between 2 and 36 inclusive. +The output radix must be at least 2. +The precision must be zero or greater. +The precision is always measured in decimal digits, +regardless of the current input or output radix. +.TP .B i Pops the value off the top of the stack and uses it to set the input radix. -.PP +.TP .B o -.PP +Pops the value off the top of the stack +and uses it to set the output radix. +.TP .B k -Similarly set the output radix and the precision. -.PP +Pops the value off the top of the stack +and uses it to set the precision. +.TP .B I Pushes the current input radix on the stack. -.PP +.TP .B O -.PP +Pushes the current output radix on the stack. +.TP .B K -Similarly push the current output radix and the current precision. -.PP -.SH "Strings" -.PP -DC can operate on strings as well as on numbers. The only things you -can do with strings are print them and execute them as macros (which -means that the contents of the string are processed as DC commands). -Both registers and the stack can hold strings, and DC always knows -whether any given object is a string or a number. Some commands such as -arithmetic operations demand numbers as arguments and print errors if -given strings. Other commands can accept either a number or a string; +Pushes the current precision on the stack. +.SH +Strings +.PP +\*(Dc can operate on strings as well as on numbers. +The only things you can do with strings are +print them and execute them as macros +(which means that the contents of the string are processed as +\*(dc commands). +All registers and the stack can hold strings, +and \*(dc always knows whether any given object is a string or a number. +Some commands such as arithmetic operations demand numbers +as arguments and print errors if given strings. +Other commands can accept either a number or a string; for example, the -.BR p +.B p command can accept either and prints the object according to its type. -.PP -.B [characters] +.TP +.BI [ characters ] Makes a string containing -.BR characters -and pushes it -on the stack. For example, -.BR [foo]p -prints the -characters \fBfoo\fR (with no newline). -.PP +.I characters +(contained between balanced +.B [ +and +.B ] +characters), +and pushes it on the stack. +For example, +.B [foo]P +prints the characters +.B foo +(with no newline). +.TP .B x Pops a value off the stack and executes it as a macro. -Normally it should be a string; if it is a number, +Normally it should be a string; +if it is a number, it is simply pushed back onto the stack. For example, -.BR [1p]x +.B [1p]x executes the macro -.BR 1p -which pushes \fB1\fR on the stack and prints \fB1\fR +.B 1p +which pushes +.B 1 +on the stack and prints +.B 1 on a separate line. .PP Macros are most often stored in registers; -\fB[1p]sa\fR stores a macro to print \fB1\fR into register \fBa\fR, -and \fBlax\fR invokes the macro. -.PP -.B >\fIr\fR +.B [1p]sa +stores a macro to print +.B 1 +into register +.BR a , +and +.B lax +invokes this macro. +.TP +.BI > r Pops two values off the stack and compares them -assuming they are numbers, executing the contents -of register \fIr\fR as a macro if the original top-of-stack -is greater. Thus, \fB1 2>a\fR will invoke register \fBa\fR's contents -and \fB2 1>a\fR will not. -.PP -.B <\fIr\fB -Similar but invokes the macro if the original top-of-stack -is less. -.PP -.B =\fIr\fR -Similar but invokes the macro if the two numbers popped -are equal. This can also be validly used to compare two -strings for equality. -.PP +assuming they are numbers, +executing the contents of register +.I r +as a macro if the original top-of-stack +is greater. +Thus, +.B 1 2>a +will invoke register +.BR a 's +contents and +.B 2 1>a +will not. +.TP +.BI < r +Similar but invokes the macro if the original top-of-stack is less. +.TP +.BI = r +Similar but invokes the macro if the two numbers popped are equal. +.ig +This can also be validly used to compare two strings for equality. +.. +.TP .B ? Reads a line from the terminal and executes it. This command allows a macro to request input from the user. -.PP +.TP .B q -During the execution of a macro, this comand -does not exit DC. Instead, it exits from that -macro and also from the macro which invoked it (if any). -.PP +exits from a macro and also from the macro which invoked it. +If called from the top level, +or from a macro which was called directly from the top level, +the +.B q +command will cause \*(dc to exit. +.TP .B Q Pops a value off the stack and uses it as a count -of levels of macro execution to be exited. Thus, -\fB3Q\fR exits three levels. -.SH "Status Inquiry" -.PP +of levels of macro execution to be exited. +Thus, +.B 3Q +exits three levels. +The +.B Q +command will never cause \*(dc to exit. +.SH +Status Inquiry +.TP .B Z -Pops a value off the stack, calculates the number of -digits it has (or number of characters, if it is a string) +Pops a value off the stack, +calculates the number of digits it has +(or number of characters, if it is a string) and pushes that number. -.PP +.TP .B X -Pops a value off the stack, calculates the number of -fraction digits it has, and pushes that number. -For a string, the value pushed is -1. -.PP +Pops a value off the stack, +calculates the number of fraction digits it has, +and pushes that number. +For a string, +the value pushed is +.\" -1. +0. +.TP .B z -Pushes the current stack depth; the number of -objects on the stack before the execution of the \fBz\fR command. -.PP -.B I -Pushes the current value of the input radix. -.PP -.B O -Pushes the current value of the output radix. -.PP -.B K -Pushes the current value of the precision. -.SH "Notes" -.PP -The \fB:\fR and \fB;\fR commands of the Unix DC program are -not supported, as the documentation does not say what they do. -The \fB!\fR command is not supported, but will be supported -as soon as a library for executing a line as a command exists. -.SH BUGS +Pushes the current stack depth; +the number of objects on the stack before the execution of the +.B z +command. +.SH +Miscellaneous +.TP +.B ! +Will run the rest of the line as a system command. +.TP +.B # +Will interpret the rest of the line as a comment. +.TP +.BI : r +Will pop the top two values off of the stack. +The old second-to-top value will be stored in the array +.IR r , +indexed by the old top-of-stack value. +.TP +.BI ; r +Pops the top-of-stack and uses it as an index into +the array +.IR r . +The selected value is then pushed onto the stack. +.SH +NOTES +.PP +The array operations +.B : +and +.B ; +are usually only used by traditional implementations of +.IR bc . +(The GNU +.I bc +is self contained and does not need \*(dc to run.) +The comment operator +.B # +is a new command not found in traditional implementations of +.IR dc . +.SH +BUGS .PP Email bug reports to .BR bug-gnu-utils@prep.ai.mit.edu . diff --git a/gnu/usr.bin/dc/dc.c b/gnu/usr.bin/dc/dc.c deleted file mode 100644 index 6dd3083..0000000 --- a/gnu/usr.bin/dc/dc.c +++ /dev/null @@ -1,925 +0,0 @@ -/* - * `dc' desk calculator utility. - * - * Copyright (C) 1984, 1993 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 - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. - */ - -#include <stdio.h> -#include "decimal.h" /* definitions for our decimal arithmetic package */ - -FILE *open_file; /* input file now open */ -int file_count; /* Number of input files not yet opened */ -char **next_file; /* Pointer to vector of names of input files left */ - -struct regstack - { - decimal value; /* Saved value of register */ - struct regstack *rest; /* Tail of list */ - }; - -typedef struct regstack *regstack; - -regstack freeregstacks; /* Chain of free regstack structures for fast realloc */ - -#define DC_MAX_REG 127 -decimal regs[DC_MAX_REG + 1]; /* "registers", with single-character names */ -regstack regstacks[DC_MAX_REG + 1]; /* For each register, a stack of previous values */ - -int stacktop; /* index of last used element in stack */ -int stacksize; /* Current allocates size of stack */ -decimal *stack; /* Pointer to computation stack */ - -/* A decimal number can be regarded as a string by - treating its contents as characters and ignoring the - position of its decimal point. - Decimal numbers are marked as strings by having an `after' field of -1 - One use of strings is to execute them as macros. -*/ - -#define STRING -1 - -int macrolevel; /* Current macro nesting; 0 if taking keyboard input */ -int macrostacksize; /* Current allocated size of macrostack and macroindex */ -decimal *macrostack; /* Pointer to macro stack array */ -int *macroindex; /* Pointer to index-within-macro stack array */ - /* Note that an empty macro is popped from the stack - only when an trying to read a character from it - or trying to push another macro. */ - -int ibase; /* Radix for numeric input. */ -int obase; /* Radix for numeric output. */ -int precision; /* Number of digits to keep in multiply and divide. */ - -char *buffer; /* Address of buffer used for reading numbers */ -int bufsize; /* Current size of buffer (made bigger when nec) */ - -decimal dec_read (); -regstack get_regstack (); -int fetch (); -int fgetchar (); -char *concat (); -void pushsqrt (); -void condop (); -void setibase (); -void setobase (); -void setprecision (); -void pushmacro (); -decimal read_string (); -void pushlength (); -void pushscale (); -void unfetch (); -void popmacros (); -void popmacro (); -void popstack (); -void print_obj (); -void print_string (); -void free_regstack (); -void pushreg (); -void execute (); -void fputchar (); -void push (); -void incref (); -void decref (); -void binop (); - -main (argc, argv, env) - int argc; - char **argv, **env; -{ - - ibase = 10; - obase = 10; - precision = 0; - - freeregstacks = 0; - - bzero (regs, sizeof regs); - bzero (regstacks, sizeof regstacks); - - bufsize = 40; - buffer = (char *) xmalloc (40); - - stacksize = 40; - stack = (decimal *) xmalloc (stacksize * sizeof (decimal)); - stacktop = -1; - - macrostacksize = 40; - macrostack = (decimal *) xmalloc (macrostacksize * sizeof (decimal)); - macroindex = (int *) xmalloc (macrostacksize * sizeof (int)); - macrolevel = 0; - /* Initialize for reading input files if any */ - - open_file = 0; - - file_count = argc - 1; - next_file = argv + 1; - - - while (1) - { - execute (); - } -} - -/* Read and execute one command from the current source of input */ - -void -execute () -{ - int c = fetch (); - - if (c < 0) exit (0); - - { - switch (c) - { - case '+': /* Arithmetic operators... */ - binop (decimal_add); - break; - - case '-': - binop (decimal_sub); - break; - - case '*': - binop (decimal_mul_dc); /* Like decimal_mul but hairy - way of deciding precision to keep */ - break; - - case '/': - binop (decimal_div); - break; - - case '%': - binop (decimal_rem); - break; - - case '^': - binop (decimal_expt); - break; - - case '_': /* Begin a negative decimal constant */ - { - decimal tem = dec_read (stdin); - tem->sign = !tem->sign; - push (tem); - } - break; - - case '.': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': /* All these begin decimal constants */ - unfetch (c); - push (dec_read (stdin)); - break; - - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - unfetch (c); - push (dec_read (stdin)); - break; - - case 'c': /* Clear the stack */ - while (stacktop >= 0) - decref (stack[stacktop--]); - break; - - case 'd': /* Duplicate top of stack */ - if (stacktop < 0) - error ("stack empty", 0); - else push (stack[stacktop]); - break; - - case 'f': /* Describe all registers and stack contents */ - { - int regno; - int somereg = 0; /* set to 1 if we print any registers */ - for (regno = 0; regno <= DC_MAX_REG; regno++) - { - if (regs[regno]) - { - printf ("register %c: ", regno); - print_obj (regs[regno]); - somereg = 1; - printf ("\n"); - } - } - if (somereg) - printf ("\n"); - if (stacktop < 0) - printf ("stack empty\n"); - else - { - int i; - printf ("stack:\n"); - for (i = 0; i <= stacktop; i++) - { - print_obj (stack[stacktop - i]); - printf ("\n"); - } - } - } - break; - - case 'i': /* ibase <- top of stack */ - popstack (setibase); - break; - - case 'I': /* Push current ibase */ - push (decimal_from_int (ibase)); - break; - - case 'k': /* like i, I but for precision instead of ibase */ - popstack (setprecision); - break; - - case 'K': - push (decimal_from_int (precision)); - break; - - case 'l': /* l<x> load register <x> onto stack */ - { - int c1 = fetch (); - if (c1 < 0) exit (0); - if (c1 > DC_MAX_REG) - error ("invalid register %c", c1); - else if (!regs[c1]) - error ("register %c empty", c1); - else - push (regs[c1]); - } - break; - - case 'L': /* L<x> load register <x> to stack, pop <x>'s own stack */ - { - int c1 = fetch (); - if (c1 < 0) exit (0); - if (c1 > DC_MAX_REG) - error ("invalid register %c", c1); - else if (!regstacks[c1]) - error ("nothing pushed on register %c", c1); - else - { - regstack r = regstacks[c1]; - if (!regs[c1]) - error ("register %c empty after pop", c1); - else - push (regs[c1]); - regs[c1] = r->value; - regstacks[c1] = r->rest; - free_regstack (r); - } - } - break; - - case 'o': /* o, O like i, I but for obase instead of ibase */ - popstack (setobase); - break; - - case 'O': - push (decimal_from_int (obase)); - break; - - case 'p': /* Print tos, don't pop, do print newline afterward */ - if (stacktop < 0) - error ("stack empty", 0); - else - { - print_obj (stack[stacktop]); - printf ("\n"); - } - break; - - case 'P': /* Print tos, do pop, no newline afterward */ - popstack (print_obj); - break; - - case 'q': /* Exit */ - if (macrolevel) - { popmacro (); popmacro (); } /* decrease recursion level by 2 */ - else - exit (0); /* If not in a macro, exit the program. */ - - break; - - case 'Q': /* Tos says how many levels to exit */ - popstack (popmacros); - break; - - case 's': /* s<x> -- Pop stack and set register <x> */ - if (stacktop < 0) - empty (); - else - { - int c1 = fetch (); - if (c1 < 0) exit (0); - if (c1 > DC_MAX_REG) - error("invalid register %c", c1); - else - { - if (regs[c1]) decref (regs[c1]); - regs[c1] = stack[stacktop--]; - } - } - break; - - case 'S': /* S<x> -- pop stack and push as new value of register <x> */ - if (stacktop < 0) - empty (); - else - { - int c1 = fetch (); - if (c1 < 0) exit (0); - if (c1 > DC_MAX_REG) - error("invalid register %c", c1); - else - { - pushreg (c1); - regs[c1] = stack[stacktop--]; - } - } - break; - - case 'v': /* tos gets square root of tos */ - popstack (pushsqrt); - break; - - case 'x': /* pop stack , call as macro */ - popstack (pushmacro); - break; - - case 'X': /* Pop stack, get # fraction digits, push that */ - popstack (pushscale); - break; - - case 'z': /* Compute depth of stack, push that */ - push (decimal_from_int (stacktop + 1)); - break; - - case 'Z': /* Pop stack, get # digits, push that */ - popstack (pushlength); - break; - - case '<': /* Conditional: pop two numbers, compare, maybe execute register */ - /* Note: for no obvious reason, the standard Unix `dc' - considers < to be true if the top of stack is less - than the next-to-top of stack, - and vice versa for >. - This seems backwards to me, but I am preserving compatibility. */ - condop (1); - break; - - case '>': - condop (-1); - break; - - case '=': - condop (0); - break; - - case '?': /* Read expression from terminal and execute it */ - /* First ignore any leading newlines */ - { - int c1; - while ((c1 = getchar ()) == '\n'); - ungetc (c1, stdin); - } - /* Read a line from the terminal and execute it. */ - pushmacro (read_string ('\n', fgetchar, 0)); - break; - - case '[': /* Begin string constant */ - push (read_string (']', fetch, '[')); - break; - - case ' ': - case '\n': - break; - - default: - error ("undefined command %c", c); - } - } -} - -/* Functionals for performing arithmetic, etc */ - -/* Call the function `op', with the top of stack value as argument, - and then pop the stack. - If the stack is empty, print a message and do not call `op'. */ - -void -popstack (op) - void (*op) (); -{ - if (stacktop < 0) - empty (); - else - { - decimal value = stack[stacktop--]; - op (value); - decref (value); - } -} - -/* Call the function `op' with two arguments taken from the stack top, - then pop those arguments and push the value returned by `op'. - `op' is assumed to return a decimal number. - If there are not two values on the stack, print a message - and do not call `op'. */ - -void -binop (op) - decimal (*op) (); -{ - if (stacktop < 1) - error ("stack empty", 0); - else if (stack[stacktop]->after == STRING || stack[stacktop - 1]->after == STRING) - error ("operands not both numeric"); - else - { - decimal arg2 = stack [stacktop--]; - decimal arg1 = stack [stacktop--]; - - push (op (arg1, arg2, precision)); - - decref (arg1); - decref (arg2); - } -} - -void -condop (cond) - int cond; -{ - int regno = fetch (); - if (regno > DC_MAX_REG) - error ("invalid register %c", regno); - else if (!regs[regno]) - error ("register %c is empty", regno); - else if (stacktop < 1) - empty (); - else - { - decimal arg2 = stack[stacktop--]; - decimal arg1 = stack[stacktop--]; - int relation = decimal_compare (arg1, arg2); - decref (arg1); - decref (arg2); - if (cond == relation - || (cond < 0 && relation < 0) - || (cond > 0 && relation > 0)) - pushmacro (regs[regno]); - } -} - -/* Handle the command input source */ - -/* Fetch the next command character from a macro or from the terminal */ - -int -fetch() -{ - int c = -1; - - while (macrolevel && - LENGTH (macrostack[macrolevel-1]) == macroindex[macrolevel-1]) - popmacro(); - if (macrolevel) - return macrostack[macrolevel - 1]->contents[macroindex[macrolevel-1]++]; - while (1) - { - if (open_file) - { - c = getc (open_file); - if (c >= 0) break; - fclose (open_file); - open_file = 0; - } - else if (file_count) - { - open_file = fopen (*next_file++, "r"); - file_count--; - if (!open_file) - perror_with_name (*(next_file - 1)); - } - else break; - } - if (c >= 0) return c; - return getc (stdin); -} - -/* Unread character c on command input stream, whatever it is */ - -void -unfetch (c) - char c; -{ - if (macrolevel) - macroindex[macrolevel-1]--; - else if (open_file) - ungetc (c, open_file); - else - ungetc (c, stdin); -} - -/* Begin execution of macro m. */ - -void -pushmacro (m) - decimal m; -{ - while (macrolevel && - LENGTH (macrostack[macrolevel-1]) == macroindex[macrolevel-1]) - popmacro(); - if (m->after == STRING) - { - if (macrolevel == macrostacksize) - { - macrostacksize *= 2; - macrostack = (decimal *) xrealloc (macrostack, macrostacksize * sizeof (decimal)); - macroindex = (int *) xrealloc (macroindex, macrostacksize * sizeof (int)); - } - macroindex[macrolevel] = 0; - macrostack[macrolevel++] = m; - incref (m); - } - else - { /* Number supplied as a macro! */ - push (m); /* Its effect wouyld be to push the number. */ - } -} - -/* Pop a specified number of levels of macro execution. - The number of levels is specified by a decimal number d. */ - -void -popmacros (d) - decimal d; -{ - int num_pops = decimal_to_int (d); - int i; - for (i = 0; i < num_pops; i++) - popmacro (); -} -/* Exit one level of macro execution. */ - -void -popmacro () -{ - if (!macrolevel) - exit (0); - else - { - decref (macrostack[--macrolevel]); - } -} - -void -push (d) - decimal d; -{ - if (stacktop == stacksize - 1) - stack = (decimal *) xrealloc (stack, (stacksize *= 2) * sizeof (decimal)); - - incref (d); - - stack[++stacktop] = d; -} - -/* Reference counting and storage freeing */ - -void -decref (d) - decimal d; -{ - if (!--d->refcnt) - free (d); -} - -void -incref (d) - decimal d; -{ - d->refcnt++; -} - -empty () -{ - error ("stack empty", 0); -} - -regstack -get_regstack () -{ - if (freeregstacks) - { - regstack r = freeregstacks; - freeregstacks = r ->rest; - return r; - } - else - return (regstack) xmalloc (sizeof (struct regstack)); -} - -void -free_regstack (r) - regstack r; -{ - r->rest = freeregstacks; - freeregstacks = r; -} - -void -pushreg (c) - char c; -{ - regstack r = get_regstack (); - - r->rest = regstacks[c]; - r->value = regs[c]; - regstacks[c] = r; - regs[c] = 0; -} - -/* Input of numbers and strings */ - -/* Return a character read from the terminal. */ - -fgetchar () -{ - return getchar (); -} - -void -fputchar (c) - char (c); -{ - putchar (c); -} - -/* Read text from command input source up to a close-bracket, - make a string out of it, and return it. - If STARTC is nonzero, then it and STOPC must balance when nested. */ - -decimal -read_string (stopc, inputfn, startc) - char stopc; - int (*inputfn) (); - int startc; -{ - int c; - decimal result; - int i = 0; - int count = 0; - - while (1) - { - c = inputfn (); - if (c < 0 || (c == stopc && count == 0)) - { - if (count != 0) - error ("Unmatched `%c'", startc); - break; - } - if (c == stopc) - count--; - if (c == startc) - count++; - if (i + 1 >= bufsize) - buffer = (char *) xrealloc (buffer, bufsize *= 2); - buffer[i++] = c; - } - result = make_decimal (i, 0); - result->after = -1; /* Mark it as a string */ - result->before++; /* but keep the length unchanged */ - bcopy (buffer, result->contents, i); - return result; -} - -/* Read a number from the current input source */ - -decimal -dec_read () -{ - int c; - int i = 0; - - while (1) - { - c = fetch (); - if (! ((c >= '0' && c <= '9') - || (c >= 'A' && c <= 'F') - || c == '.')) - break; - if (i + 1 >= bufsize) - buffer = (char *) xrealloc (buffer, bufsize *= 2); - buffer[i++] = c; - } - buffer[i++] = 0; - unfetch (c); - - return decimal_parse (buffer, ibase); -} - -/* Output of numbers and strings */ - -/* Print the contents of obj, either numerically or as a string, - according to what obj says it is. */ - -void -print_obj (obj) - decimal obj; -{ - if (obj->after == STRING) - print_string (obj); - else - decimal_print (obj, fputchar, obase); -} - -/* Print the contents of the decimal number `string', treated as a string. */ - -void -print_string (string) - decimal string; -{ - char *p = string->contents; - int len = LENGTH (string); - int i; - - for (i = 0; i < len; i++) - { - putchar (*p++); - } -} - -/* Set the input radix from the value of the decimal number d, if valid. */ - -void -setibase (d) - decimal d; -{ - int value = decimal_to_int (d); - if (value < 2 || value > 36) - error ("input radix must be from 2 to 36", 0); - else - ibase = value; -} - -/* Set the output radix from the value of the decimal number d, if valid. */ - -void -setobase (d) - decimal d; -{ - int value = decimal_to_int (d); - if (value < 2 || value > 36) - error ("output radix must be from 2 to 36", 0); - else - obase = value; -} - -/* Set the precision for mul and div from the value of the decimal number d, if valid. */ - -void -setprecision (d) - decimal d; -{ - int value = decimal_to_int (d); - if (value < 0 || value > 30000) - error ("precision must be nonnegative and < 30000", 0); - else - precision = value; -} - -/* Push the number of digits in decimal number d, as a decimal number. */ - -void -pushlength (d) - decimal d; -{ - push (decimal_from_int (LENGTH (d))); -} - -/* Push the number of fraction digits in d. */ - -void -pushscale (d) - decimal d; -{ - push (decimal_from_int (d->after)); -} - -/* Push the square root of decimal number d. */ - -void -pushsqrt (d) - decimal d; -{ - push (decimal_sqrt (d, precision)); -} - -/* Print error message and exit. */ - -fatal (s1, s2) - char *s1, *s2; -{ - error (s1, s2); - exit (1); -} - -/* Print error message. `s1' is printf control string, `s2' is arg for it. */ - -error (s1, s2) - char *s1, *s2; -{ - printf ("dc: "); - printf (s1, s2); - printf ("\n"); -} - -decimal_error (s1, s2) - char *s1, *s2; -{ - error (s1, s2); -} - -perror_with_name (name) - char *name; -{ - extern int errno, sys_nerr; - char *s; - - if (errno < sys_nerr) - s = concat ("", sys_errlist[errno], " for %s"); - else - s = "cannot open %s"; - error (s, name); -} - -/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */ - -char * -concat (s1, s2, s3) - char *s1, *s2, *s3; -{ - int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); - char *result = (char *) xmalloc (len1 + len2 + len3 + 1); - - strcpy (result, s1); - strcpy (result + len1, s2); - strcpy (result + len1 + len2, s3); - *(result + len1 + len2 + len3) = 0; - - return result; -} - -/* Like malloc but get fatal error if memory is exhausted. */ - -int -xmalloc (size) - int size; -{ - int result = malloc (size); - if (!result) - fatal ("virtual memory exhausted", 0); - return result; -} - -int -xrealloc (ptr, size) - char *ptr; - int size; -{ - int result = realloc (ptr, size); - if (!result) - fatal ("virtual memory exhausted"); - return result; -} diff --git a/gnu/usr.bin/dc/dc.h b/gnu/usr.bin/dc/dc.h new file mode 100644 index 0000000..7193aea --- /dev/null +++ b/gnu/usr.bin/dc/dc.h @@ -0,0 +1,81 @@ +/* + * Header file for dc routines + * + * Copyright (C) 1994 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. + */ + +#ifndef DC_DEFS_H +#define DC_DEFS_H + +/* 'I' is a command, and bases 17 and 18 are quite + * unusual, so we limit ourselves to bases 2 to 16 + */ +#define DC_IBASE_MAX 16 + +#define DC_SUCCESS 0 +#define DC_DOMAIN_ERROR 1 +#define DC_FAIL 2 /* generic failure */ + + +#ifndef __STDC__ +# define DC_PROTO(x) () +# define DC_DECLVOID() () +# define DC_DECLARG(arglist) arglist +# define DC_DECLSEP ; +# define DC_DECLEND ; +#else /* __STDC__ */ +# define DC_PROTO(x) x +# define DC_DECLVOID() (void) +# define DC_DECLARG(arglist) ( +# define DC_DECLSEP , +# define DC_DECLEND ) +#endif /* __STDC__ */ + + +typedef enum {DC_FALSE, DC_TRUE} dc_boolean; + + +/* type discriminant for dc_data */ +typedef enum {DC_UNINITIALIZED, DC_NUMBER, DC_STRING} dc_value_type; + +/* generic pointer for information hiding */ +typedef void *Opaque; + +/* only dc-math.c knows what dc_num's *really* look like */ +typedef Opaque dc_num; + +/* only dc-string.c knows what dc_str's *really* look like */ +typedef Opaque dc_str; + + +/* except for the two implementation-specific modules, all + * dc functions only know of this one generic type of object + */ +typedef struct { + dc_value_type dc_type; /* discriminant for union */ + union { + dc_num number; + dc_str string; + } v; +} dc_data; + + +/* This is dc's only global variable: */ +extern const char *progname; /* basename of program invocation */ + +#endif /* not DC_DEFS_H */ diff --git a/gnu/usr.bin/dc/decimal.c b/gnu/usr.bin/dc/decimal.c deleted file mode 100644 index 780de29..0000000 --- a/gnu/usr.bin/dc/decimal.c +++ /dev/null @@ -1,1235 +0,0 @@ -/* - * Arbitrary precision decimal arithmetic. - * - * Copyright (C) 1984 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 - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. - */ - -/* Some known problems: - - Another problem with decimal_div is found when you try to - divide a number with > scale fraction digits by 1. The - expected result is simply truncation, but all sorts of things - happen instead. An example is that the result of .99999998/1 - with scale set to 6 is .000001 - - There are some problems in the behavior of the decimal package - related to printing and parsing. The - printer is weird about very large output radices, tending to want - to output single ASCII characters for any and all digits (even - in radices > 127). The UNIX bc approach is to print digit groups - separated by spaces. There is a rather overwrought workaround in - the function decputc() in bcmisc.c, but it would be better if - decimal.c got a fix for this. */ - -/* For stand-alone testing, compile with -DTEST. - This DTESTable feature defines a `main' function - which is a simple loop that accepts input of the form - number space op space number newline - where op is +, -, *, /, %, p or r, - and performs the operation and prints the operands and result. - `p' means print the first number in the radix spec'd by the second. - `r' means read the first one in the radix specified by the second - (and print the result in decimal). - Divide in this test keeps three fraction digits. */ - -#include "decimal.h" - -#define MAX(a, b) (((a) > (b) ? (a) : (b))) - -/* Some constant decimal numbers */ - -struct decimal decimal_zero = {0, 0, 0, 0, 0}; - -struct decimal decimal_one = {0, 0, 1, 0, 1}; - -/*** Assumes RADIX is even ***/ -struct decimal decimal_half = {0, 1, 0, 0, RADIX / 2}; - -decimal static decimal_add1 (), decimal_sub1 (); -static void add_scaled (); -static int subtract_scaled (); - -/* Create and return a decimal number that has `before' digits before - the decimal point and `after' digits after. The digits themselves are - initialized to zero. */ - -decimal -make_decimal (before, after) - int before, after; -{ - decimal result; - if (before >= 1<<16) - { - decimal_error ("%d too many decimal digits", before); - return 0; - } - if (after >= 1<<15) - { - decimal_error ("%d too many decimal digits", after); - return 0; - } - result = (decimal) malloc (sizeof (struct decimal) + before + after - 1); - result->sign = 0; - result->before = before; - result->after = after; - result->refcnt = 0; - bzero (result->contents, before + after); - return result; -} - -/* Create a copy of the decimal number `b' and return it. */ - -decimal -decimal_copy (b) - decimal b; -{ - decimal result = make_decimal (b->before, b->after); - bcopy (b->contents, result->contents, LENGTH(b)); - result->sign = b->sign; - return result; -} - -/* Copy a decimal number `b' but extend or truncate to exactly - `digits' fraction digits. */ - -static decimal -decimal_copy_1 (b, digits) - decimal b; - int digits; -{ - if (digits > b->after) - { - decimal result = make_decimal (b->before, digits); - bcopy (b->contents, result->contents + (digits - (int) b->after), LENGTH(b)); - return result; - } - else - return decimal_trunc_digits (b, digits); -} - -/* flush specified number `digits' of trailing fraction digits, - and flush any trailing fraction zero digits exposed after they are gone. - The number `b' is actually modified; no new storage is allocated. - That is why this is not global. */ - -static void -flush_trailing_digits (b, digits) - decimal b; - int digits; -{ - int flush = digits; - int maxdig = b->after; - - while (flush < maxdig && !b->contents [flush]) - flush++; - - if (flush) - { - int i; - - b->after -= flush; - for (i = 0; i < LENGTH (b); i++) - b->contents[i] = b->contents[flush + i]; - } - -} - -/* Return nonzero integer if the value of decimal number `b' is zero. */ - -int -decimal_zerop (b) - decimal b; -{ - return !LENGTH(b); -} - -/* Compare two decimal numbers arithmetically. - The value is < 0 if b1 < b2, > 0 if b1 > b2, 0 if b1 = b2. - This is the same way that `strcmp' reports the result of comparing - strings. */ - -int -decimal_compare (b1, b2) - decimal b1, b2; -{ - int l1, l2; - char *p1, *p2, *s1, *s2; - int i; - - /* If signs differ, deduce result from the signs */ - - if (b2->sign && !b1->sign) return 1; - if (b1->sign && !b2->sign) return -1; - - /* If same sign but number of nonfraction digits differs, - the one with more of them is farther from zero. */ - - if (b1->before != b2->before) - if (b1->sign) - return (int) (b2->before - b1->before); - else - return (int) (b1->before - b2->before); - - /* Else compare the numbers digit by digit from high end */ - l1 = LENGTH(b1); - l2 = LENGTH(b2); - s1 = b1->contents; /* Start of number -- don't back up digit pointer past here */ - s2 = b2->contents; - p1 = b1->contents + l1; /* Scanning pointer, for fetching digits. */ - p2 = b2->contents + l2; - for (i = MAX(l1, l2); i >= 0; i--) - { - int r = ((p1 != s1) ? *--p1 : 0) - ((p2 != s2) ? *--p2 : 0); - if (r) - return b1->sign ? -r : r; - } - return 0; -} - -/* Return the number of digits stored in decimal number `b' */ - -int -decimal_length (b) - decimal b; -{ - return LENGTH(b); -} - -/* Return the number of fraction digits stored in decimal number `b'. */ - -int -decimal_after (b) - decimal b; -{ - return b->after; -} - -/* Round decimal number `b' to have only `digits' fraction digits. - Result is rounded to nearest unit in the last remaining digit. - Return the result, another decimal number. */ - -decimal -decimal_round_digits (b, digits) - decimal b; - int digits; -{ - decimal result; - int old; - - if (b->after <= digits) return decimal_copy (b); - - if (digits < 0) - { - decimal_error ("request to keep negative number of digits %d", digits); - return decimal_copy (b); - } - - result = make_decimal (b->before + 1, b->after); - result->sign = b->sign; - bcopy (b->contents, result->contents, LENGTH(b)); - - old = result->after; - - /* Add .5 * last place to keep, so that we round rather than truncate */ - /* Note this ignores sign of result, so if result is negative - it is subtracting */ - - add_scaled (result, DECIMAL_HALF, 1, old - digits - 1); - - /* Flush desired digits, and any trailing zeros exposed by them. */ - - flush_trailing_digits (result, old - digits); - - /* Flush leading digits -- always is one, unless was a carry into it */ - - while (result->before > 0 - && result->contents[LENGTH(result) - 1] == 0) - result->before--; - - return result; -} - -/* Truncate decimal number `b' to have only `digits' fraction digits. - Any fraction digits in `b' beyond that are dropped and ignored. - Truncation is toward zero. - Return the result, another decimal number. */ - -decimal -decimal_trunc_digits (b, digits) - decimal b; - int digits; -{ - decimal result = decimal_copy (b); - int old = result->after; - - if (old <= digits) return result; - - if (digits < 0) - { - decimal_error ("request to keep negative number of digits %d", digits); - return result; - } - - flush_trailing_digits (result, old - digits); - - return result; -} - -/* Return the fractional part of decimal number `b': - that is, `b' - decimal_trunc_digits (`b') */ - -decimal -decimal_fraction (b) - decimal b; -{ - decimal result = make_decimal (0, b->after); - bcopy (b->contents, result->contents, b->after); - return result; -} - -/* return an integer whose value is that of decimal `b', sans its fraction. */ - -int -decimal_to_int (b) - decimal b; -{ - int result = 0; - int i; - int end = b->after; - - for (i = LENGTH(b) - 1; i >= end; i--) - { - result *= RADIX; - result += b->contents[i]; - } - return result; -} - -/* return a decimal whose value is the integer i. */ - -decimal -decimal_from_int (i) - int i; -{ - int log, tem; - decimal result; - - for (log = 0, tem = (i > 0 ? i : - i); tem; log++, tem /= RADIX); - - result = make_decimal (log, 0); - - for (log = 0, tem = (i > 0 ? i : - i); tem; log++, tem /= RADIX) - result->contents[log] = tem % RADIX; - - if (i < 0) result->sign = 1; - return result; -} - -/* Return (as an integer) the result of dividing decimal number `b' by - integer `divisor'. - This is used in printing decimal numbers in other radices. */ - -int -decimal_int_rem (b, divisor) - decimal b; - int divisor; -{ - int len = LENGTH(b); - int end = b->after; - int accum = 0; - int i; - - for (i = len - 1; i >= end; i--) - { - accum %= divisor; - accum *= RADIX; - accum += b->contents[i]; - } - return accum % divisor; -} - -/* Convert digit `digit' to a character and output it by calling - `charout' with it as arg. */ - -static void -print_digit (digit, charout) - int digit; - void (*charout) (); -{ - if (digit < 10) - charout ('0' + digit); - else - charout ('A' + digit - 10); -} - -/* print decimal number `b' in radix `radix', assuming it is an integer. - `r' is `radix' expressed as a decimal number. */ - -static -decimal_print_1 (b, r, radix, charout) - decimal b, r; - int radix; - void (*charout) (); -{ - int digit = decimal_int_rem (b, radix); - decimal rest = decimal_div (b, r, 0); - - if (!decimal_zerop (rest)) - decimal_print_1 (rest, r, radix, charout); - - print_digit (digit, charout); - - free (rest); -} - -/* User entry: print decimal number `b' in radix `radix' (an integer), - outputting characters by calling `charout'. */ - -void -decimal_print (b, charout, radix) - decimal b; - void (*charout) (); - int radix; -{ - if (b->sign) charout ('-'); - - if (radix == RADIX) - { - /* decimal output => just print the digits, inserting a point in - the proper place. */ - int i; - int before = b->before; - int len = before + b->after; - for (i = 0; i < len; i++) - { - if (i == before) charout ('.'); - /* Broken if RADIX /= 10 - charout ('0' + b->contents [len - 1 - i]); */ - print_digit (b->contents [len - 1 - i], charout); - } - if (!len) - charout ('0'); - } - else - { - /* nonstandard radix: must use multiply and divide to determine the - digits of the number in that radix. */ - - int i; - extern double log10 (); - /* Compute the number of fraction digits we want to have in the - new radix. They should contain the same amount of - information as the decimal digits we have. */ - int nfrac = (b->after / log10 ((double) radix) + .99); - decimal r = decimal_from_int (radix); - decimal intpart = decimal_trunc_digits (b, 0); - - /* print integer part */ - decimal_print_1 (intpart, r, radix, charout); - free (intpart); - - /* print fraction part */ - if (nfrac) - { - decimal tem1, tem2; - tem1 = decimal_fraction (b); - charout ('.'); - /* repeatedly multiply by `radix', print integer part as one digit, - and flush the integer part. */ - for (i = 0; i < nfrac; i++) - { - tem2 = decimal_mul (tem1, r); - free (tem1); - print_digit (decimal_to_int (tem2), charout); - tem1 = decimal_fraction (tem2); - free (tem2); - } - free (tem1); - } - free (r); - } -} - -static int -decode_digit (digitchar) - char digitchar; -{ - if ('0' <= digitchar && digitchar <= '9') - return digitchar - '0'; - if ('a' <= digitchar && digitchar <= 'z') - return digitchar - 'a' + 10; - if ('A' <= digitchar && digitchar <= 'Z') - return digitchar - 'A' + 10; - return -1; -} - -/* Parse string `s' into a number using radix `radix' - and return result as a decimal number. */ - -decimal -decimal_parse (s, radix) - char *s; - int radix; -{ - int i, len, before = -1; - char *p; - char c; - decimal result; - int negative = 0; - int excess_digit = 0; - - if (*s == '-') - { - s++; - negative = 1; - } - - /* First scan for valid characters. - Count total num digits, and count num before the decimal point. */ - - p = s; - i = 0; - while (c = *p++) - { - if (c == '.') - { - if (before >= 0) - decimal_error ("two decimal points in %s", s); - before = i; - } - else if (c == '0' && !i && before < 0) - s++; /* Discard leading zeros */ - else if (decode_digit (c) >= 0) - { - i++; - if (decode_digit (c) > RADIX) - excess_digit = 1; - } - else - decimal_error ("invalid number %s", s); - } - - len = i; - if (before < 0) before = i; - - p = s; - - /* Now parse those digits */ - - if (radix != RADIX || excess_digit) - { - decimal r = decimal_from_int (radix); - extern double log10 (); - int digits = (len - before) * log10 ((double) radix) + .99; - result = decimal_copy (DECIMAL_ZERO); - - /* Parse all the digits into an integer, ignoring decimal point, - by multiplying by `radix'. */ - - while (i > 0 && (c = *p++)) - { - if (c != '.') - { - decimal newdig = decimal_from_int (decode_digit (c)); - decimal prod = decimal_mul (result, r); - decimal newresult = decimal_add (newdig, prod); - - free (newdig); free (prod); free (result); - result = newresult; - i--; - } - } - - /* Now put decimal point in right place - by dividing by `radix' once for each digit - that really should have followed the decimal point. */ - - for (i = before; i < len; i++) - { - decimal newresult = decimal_div (result, r, digits); - free (result); - result = newresult; - } - free (r); - } - else - { - /* radix is standard - just copy the digits into a decimal number. */ - - int tem; - result = make_decimal (before, len - before); - - while (i > 0 && (c = *p++)) - { - if ((c != '.') && - ((tem = decode_digit (c)) >= 0)) - result->contents [--i] = tem; - } - } - - if (negative) result->sign = 1; - flush_trailing_digits (result, 0); - return result; -} - -/* Add b1 and b2, considering their signs */ - -decimal -decimal_add (b1, b2) - decimal b1, b2; -{ - decimal v; - - if (b1->sign != b2->sign) - v = decimal_sub1 (b1, b2); - else - v = decimal_add1 (b1, b2); - if (b1->sign && !decimal_zerop (v)) - v->sign = !v->sign; - return v; -} - -/* Add b1 and minus b2, considering their signs */ - -decimal -decimal_sub (b1, b2) - decimal b1, b2; -{ - decimal v; - - if (b1->sign != b2->sign) - v = decimal_add1 (b1, b2); - else - v = decimal_sub1 (b1, b2); - if (b1->sign && !decimal_zerop (v)) - v->sign = !v->sign; - return v; -} - -/* Return the negation of b2. */ - -decimal -decimal_neg (b2) - decimal b2; -{ - decimal v = decimal_copy (b2); - - if (!decimal_zerop (v)) - v->sign = !v->sign; - return v; -} - -/* add magnitudes of b1 and b2, ignoring their signs. */ - -static decimal -decimal_add1 (b1, b2) - decimal b1, b2; -{ - int before = MAX (b1->before, b2->before); - int after = MAX (b1->after, b2->after); - - int len = before+after+1; - decimal result = make_decimal (before+1, after); - - int i; - char *s1 = b1->contents; - char *s2 = b2->contents; - char *p1 = s1 + b1->after - after; - char *p2 = s2 + b2->after - after; - char *e1 = s1 + b1->before + b1->after; - char *e2 = s2 + b2->before + b2->after; - char *pr = result->contents; - int accum = 0; - - for (i = 0; i < len; i++, p1++, p2++) - { - accum /= RADIX; - if (p1 >= s1 && p1 < e1) accum += *p1; - if (p2 >= s2 && p2 < e2) accum += *p2; - *pr++ = accum % RADIX; - } - if (!accum) - (result->before)--; - - flush_trailing_digits (result, 0); - - return result; -} - -/* subtract magnitude of b2 from that or b1, returning signed decimal - number. */ - -static decimal -decimal_sub1 (b1, b2) - decimal b1, b2; -{ - int before = MAX (b1->before, b2->before); - int after = MAX (b1->after, b2->after); - - int len = before+after; - decimal result = make_decimal (before, after); - - int i; - char *s1 = b1->contents; - char *s2 = b2->contents; - char *p1 = s1 + b1->after - after; - char *p2 = s2 + b2->after - after; - char *e1 = s1 + b1->before + b1->after; - char *e2 = s2 + b2->before + b2->after; - char *pr = result->contents; - int accum = 0; - - for (i = 0; i < len; i++, p1++, p2++) - { - if (p1 >= s1 && p1 < e1) accum += *p1; - if (p2 >= s2 && p2 < e2) accum -= *p2; - if (accum < 0 && accum % RADIX) - *pr = RADIX - (- accum) % RADIX; - else - *pr = accum % RADIX; - accum -= *pr++; - accum /= RADIX; - } - - /* If result is negative, subtract it from RADIX**length - so that we get the right digits for sign-magnitude - rather than RADIX-complement */ - - if (accum) - { - result->sign = 1; - pr = result->contents; - accum = 0; - for (i = 0; i < len; i++) - { - accum -= *pr; - if (accum) - *pr = accum + RADIX; - else - *pr = 0; - accum -= *pr++; - accum /= RADIX; - } - } - - /* flush leading nonfraction zero digits */ - - while (result->before && *--pr == 0) - (result->before)--; - - flush_trailing_digits (result, 0); - - return result; -} - -/* multiply b1 and b2 keeping `digits' fraction digits */ - -decimal -decimal_mul_rounded (b1, b2, digits) - decimal b1, b2; - int digits; -{ - decimal tem = decimal_mul (b1, b2); - decimal result = decimal_round_digits (tem, digits); - free (tem); - return result; -} - -/* multiply b1 and b2 keeping the right number of fraction digits - for the `dc' program with precision = `digits'. */ - -decimal -decimal_mul_dc (b1, b2, digits) - decimal b1, b2; - int digits; -{ - decimal tem = decimal_mul (b1, b2); - decimal result - = decimal_round_digits (tem, MAX (digits, MAX (b1->after, b2->after))); - free (tem); - return result; -} - -/* multiply b1 and b2 as decimal error-free values; - keep LENGTH(b1) plus LENGTH(b2) significant figures. */ - -decimal -decimal_mul (b1, b2) - decimal b1, b2; -{ - decimal result = make_decimal (b1->before + b2->before, b1->after + b2->after); - int i; - int length2 = LENGTH(b2); - char *pr; - - for (i = 0; i < length2; i++) - add_scaled (result, b1, b2->contents[i], i); - - /* flush leading nonfraction zero digits */ - - pr = result->contents + LENGTH(result); - while (result->before && *--pr == 0) - (result->before)--; - - flush_trailing_digits (result, 0); /* flush trailing zeros */ - - /* Set sign properly */ - - if (b1->sign != b2->sign && LENGTH(result)) - result->sign = 1; - - return result; -} - -/* Modify decimal number `into' by adding `from', - multiplied by `factor' (which should be nonnegative and less than RADIX) - and shifted left `scale' digits at the least significant end. */ - -static void -add_scaled (into, from, factor, scale) - decimal into, from; - int factor, scale; -{ - char *pf = from->contents; - char *pi = into->contents + scale; - int lengthf = LENGTH(from); - int lengthi = LENGTH(into) - scale; - - int accum = 0; - int i; - - for (i = 0; i < lengthi; i++) - { - accum /= RADIX; - if (i < lengthf) - accum += *pf++ * factor; - accum += *pi; - *pi++ = accum % RADIX; - } -} - -/* Divide decimal number `b1' by `b2', keeping at most `digits' - fraction digits. - Returns the result as a decimal number. - - When division is not exact, the quotient is truncated toward zero. */ - -decimal -decimal_div (b1, b2, digits) - decimal b1, b2; - int digits; -{ - decimal result = make_decimal (MAX(1, (int) (1 + b1->before - b2->before)), digits); - - /* b1copy holds what is left of the dividend, - that is not accounted for by the quotient digits already known */ - - decimal b1copy = decimal_copy_1 (b1, b2->after + digits); - int length1 = LENGTH(b1copy); - int length2 = LENGTH(b2); - int lengthr = LENGTH(result); - int i; - - /* leading_divisor_digits contains the first two divisor digits, as - an integer */ - - int leading_divisor_digits = b2->contents[length2-1]*RADIX; - if (length2 > 1) - leading_divisor_digits += b2->contents[length2-2]; - - if (decimal_zerop (b2)) - { - decimal_error ("divisor is zero", 0); - return decimal_copy (DECIMAL_ZERO); - } - -/* if (lengthr <= (length1 - length2)) - abort(); */ /* My reasoning says this cannot happen, I hope */ - - for (i = length1 - length2; i >= 0; i--) - { - /* Guess the next quotient digit (in order of decreasing significance) - using integer division */ - - int guess; - int trial_dividend = b1copy->contents[length2+i-1]*RADIX; - if (i != length1 - length2) - trial_dividend += b1copy->contents[length2+i]*RADIX*RADIX; - if (length2 + i > 1) - trial_dividend += b1copy->contents[length2+i-2]; - - guess = trial_dividend / leading_divisor_digits; - - /* Remove the quotient times this digit from the dividend left */ - /* We may find that the quotient digit is too large, - when we consider the entire divisor. - Then we decrement the quotient digit and add the divisor back in */ - - if (guess && 0 > subtract_scaled (b1copy, b2, guess, i)) - { - guess--; - add_scaled (b1copy, b2, 1, i); - } - - if (guess >= RADIX) - { - result->contents[i + 1] += guess / RADIX; - guess %= RADIX; - } - result->contents[i] = guess; - } - - free (b1copy); - - result->sign = (b1->sign != b2->sign); - - /* flush leading nonfraction zero digits */ - - { - char *pr = result->contents + lengthr; - while (result->before && *--pr == 0) - (result->before)--; - } - - flush_trailing_digits (result, 0); /* Flush trailing zero fraction digits */ - - return result; -} - -/* The remainder for the above division. - Same as `b1' - (`b1' / `b2') * 'b2'. - Note that the value depends on the number of fraction digits - that were kept in computing `b1' / `b2'; - the argument `digits' specifies this. - - The remainder has the same sign as the dividend. - The divisor's sign is ignored. */ - -decimal -decimal_rem (b1, b2, digits) - decimal b1, b2; - int digits; -{ - decimal b1copy = decimal_copy_1 (b1, b2->after + digits); - int length1 = LENGTH(b1copy); - int length2 = LENGTH(b2); - int i; - - int leading_divisor_digits = b2->contents[length2-1]*RADIX; - - if (length2 > 1) - leading_divisor_digits += b2->contents[length2-2]; - - if (decimal_zerop (b2)) - { - decimal_error ("divisor is zero", 0); - return decimal_copy (DECIMAL_ZERO); - } - - /* Do like division, above, but throw away the quotient. - Keep only the final `rest of dividend', which becomes the remainder. */ - - for (i = length1 - length2; i >= 0; i--) - { - int guess; - int trial_dividend = b1copy->contents[length2+i-1]*RADIX; - if (i != length1 - length2) - trial_dividend += b1copy->contents[length2+i]*RADIX*RADIX; - if (length2 + i > 1) - trial_dividend += b1copy->contents[length2+i-2]; - - guess = trial_dividend / leading_divisor_digits; - - if (guess && 0 > subtract_scaled (b1copy, b2, guess, i)) - { - guess--; - add_scaled (b1copy, b2, 1, i); - } - /* No need to check whether guess exceeds RADIX - since we are not saving guess. */ - } - - /* flush leading nonfraction zero digits */ - - { - char *pr = b1copy->contents + length1; - while (b1copy->before && *--pr == 0) - (b1copy->before)--; - } - - flush_trailing_digits (b1copy, 0); - return b1copy; -} - -/* returns negative number if we chose factor too large */ - -static int -subtract_scaled (into, from, factor, scale) - decimal into, from; - int factor, scale; -{ - char *pf = from->contents; - char *pi = into->contents + scale; - int lengthf = LENGTH(from); - int lengthi = LENGTH(into) - scale; - int accum = 0; - int i; - - for (i = 0; i < lengthi && i <= lengthf; i++) - { - if (i < lengthf) - accum -= *pf++ * factor; - accum += *pi; - if (accum < 0 && accum % RADIX) - *pi = RADIX - (- accum) % RADIX; - else - *pi = accum % RADIX; - accum -= *pi++; - accum /= RADIX; - } - return accum; -} - -/* Return the square root of decimal number D, using Newton's method. - Number of fraction digits returned is max of FRAC_DIGITS - and D's number of fraction digits. */ - -decimal -decimal_sqrt (d, frac_digits) - decimal d; - int frac_digits; -{ - decimal guess; - int notdone = 1; - - if (decimal_zerop (d)) return d; - if (d->sign) - { - decimal_error ("square root argument negative", 0); - return decimal_copy (DECIMAL_ZERO); - } - - frac_digits = MAX (frac_digits, d->after); - - /* Compute an initial guess by taking the square root - of a nearby power of RADIX. */ - - if (d->before) - { - guess = make_decimal ((d->before + 1) / 2, 0); - guess->contents[guess->before - 1] = 1; - } - else - { - /* Arg is less than 1; compute nearest power of RADIX */ - char *p = d->contents + LENGTH(d); - char *sp = p; - - while (!*--p); /* Find most significant nonzero digit */ - if (sp - p == 1) - { - /* Arg is bigger than 1/RADIX; use 1 as a guess */ - guess = decimal_copy (DECIMAL_ONE); - } - else - { - guess = make_decimal (0, (sp - p) / 2); - guess->contents[0] = 1; - } - } - - /* Iterate doing guess = (guess + d/guess) / 2 */ - - while (notdone) - { - decimal tem1 = decimal_div (d, guess, frac_digits + 1); - decimal tem2 = decimal_add (guess, tem1); - decimal tem3 = decimal_mul_rounded (tem2, DECIMAL_HALF, frac_digits); - notdone = decimal_compare (guess, tem3); - free (tem1); - free (tem2); - free (guess); - guess = tem3; - if (decimal_zerop (guess)) return guess; /* Avoid divide-by-zero */ - } - - return guess; -} - -/* Raise decimal number `base' to power of integer part of decimal - number `expt'. - This function depends on using radix 10. - It is too hard to write it to work for any value of RADIX, - so instead it is simply not available if RADIX is not ten. */ - -#if !(RADIX - 10) - -decimal -decimal_expt (base, expt, frac_digits) - decimal base, expt; - int frac_digits; -{ - decimal accum = decimal_copy (DECIMAL_ONE); - decimal basis1 = base; - int digits = expt->before; - int dig = 0; /* Expt digit being processed */ - - if (expt->sign) - /* If negative power, take reciprocal first thing - so that fraction digit truncation won't destroy - what will ultimately be nonfraction digits. */ - basis1 = decimal_div (DECIMAL_ONE, base, frac_digits); - while (dig < digits) - { - decimal basis2, basis4, basis8, basis10; - int thisdigit = expt->contents[expt->after + dig]; - - /* Compute factors to multiply in for each bit of this digit */ - - basis2 = decimal_mul_rounded (basis1, basis1, frac_digits); - basis4 = decimal_mul_rounded (basis2, basis2, frac_digits); - basis8 = decimal_mul_rounded (basis4, basis4, frac_digits); - - /* Now accumulate the factors this digit value selects */ - - if (thisdigit & 1) - { - decimal accum1 = decimal_mul_rounded (accum, basis1, frac_digits); - free (accum); - accum = accum1; - } - - if (thisdigit & 2) - { - decimal accum1 = decimal_mul_rounded (accum, basis2, frac_digits); - free (accum); - accum = accum1; - } - - if (thisdigit & 4) - { - decimal accum1 = decimal_mul_rounded (accum, basis4, frac_digits); - free (accum); - accum = accum1; - } - - if (thisdigit & 8) - { - decimal accum1 = decimal_mul_rounded (accum, basis8, frac_digits); - free (accum); - accum = accum1; - } - - /* If there are further digits, compute the basis1 for the next digit */ - - if (++dig < digits) - basis10 = decimal_mul_rounded (basis2, basis8, frac_digits); - - /* Free intermediate results */ - - if (basis1 != base) free (basis1); - free (basis2); - free (basis4); - free (basis8); - basis1 = basis10; - } - return accum; -} -#endif - -#ifdef TEST - -fputchar (c) - char c; -{ - putchar (c); -} - -/* Top level that can be used to test the arithmetic functions */ - -main () -{ - char s1[40], s2[40]; - decimal b1, b2, b3; - char c; - - while (1) - { - scanf ("%s %c %s", s1, &c, s2); - b1 = decimal_parse (s1, RADIX); - b2 = decimal_parse (s2, RADIX); - switch (c) - { - default: - c = '+'; - case '+': - b3 = decimal_add (b1, b2); - break; - case '*': - b3 = decimal_mul (b1, b2); - break; - case '/': - b3 = decimal_div (b1, b2, 3); - break; - case '%': - b3 = decimal_rem (b1, b2, 3); - break; - case 'p': - decimal_print (b1, fputchar, RADIX); - printf (" printed in base %d is ", decimal_to_int (b2)); - decimal_print (b1, fputchar, decimal_to_int (b2)); - printf ("\n"); - continue; - case 'r': - printf ("%s read in base %d is ", s1, decimal_to_int (b2)); - decimal_print (decimal_parse (s1, decimal_to_int (b2)), fputchar, RADIX); - printf ("\n"); - continue; - } - decimal_print (b1, fputchar, RADIX); - printf (" %c ", c); - decimal_print (b2, fputchar, RADIX); - printf (" = "); - decimal_print (b3, fputchar, RADIX); - printf ("\n"); - } -} - -decimal_error (s1, s2) - char *s1, *s2; -{ - printf ("\n"); - printf (s1, s2); - printf ("\n"); -} - -static void -pbi (b) - int b; -{ - decimal_print ((decimal) b, fputchar, RADIX); -} - -static void -pb (b) - decimal b; -{ - decimal_print (b, fputchar, RADIX); -} - -#endif diff --git a/gnu/usr.bin/dc/decimal.h b/gnu/usr.bin/dc/decimal.h deleted file mode 100644 index d2cab4d..0000000 --- a/gnu/usr.bin/dc/decimal.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Header file for decimal.c (arbitrary precision decimal arithmetic) - * - * Copyright (C) 1984 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 - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * 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.; 675 Mass Ave. Cambridge, MA 02139, USA. - */ - -/* Autoconf stuff */ -#ifndef HAVE_BCOPY -#undef bcopy -#define bcopy(s2, s1, n) memcpy (s1, s2, n) -#endif - -#ifndef HAVE_BZERO -#undef bzero -#define bzero(b, l) memset (b, 0, l) -#endif - -/* Define the radix to use by default, and for representing the - numbers internally. This does not need to be decimal; that is just - the default for it. */ - -/* Currently, this is required to be even for this program to work. */ - -#ifndef RADIX -#define RADIX 10 -#endif - -/* The user must define the external function `decimal_error' - which is called with two arguments to report errors in this package. - The two arguments may be passed to `printf' to print a message. */ - -/* Structure that represents a decimal number */ - -struct decimal -{ - unsigned int sign: 1; /* One for negative number */ - /* The sign should always be zero for the number 0 */ - int after: 15; /* number of fraction digits */ - unsigned short before; /* number of non-fraction digits */ - unsigned short refcnt; /* number of pointers to this number */ - /* (used by calling program) */ - char contents[1]; /* the digits themselves, least significant first. */ - /* digits are just numbers 0 .. RADIX-1 */ -}; - -/* There may never be leading nonfraction zeros or trailing fraction - zeros in a number. They must be removed by all the arithmetic - functions. Therefore, the number zero always has no digits stored. */ - -typedef struct decimal *decimal; - -/* Decimal numbers are always passed around as pointers. - All the external entries in this file allocate new numbers - using `malloc' to store values in. - They never modify their arguments or any existing numbers. */ - -/* Return the total number of digits stored in the number `b' */ -#define LENGTH(b) ((b)->before + (b)->after) - -/* Some constant decimal numbers */ - - -#define DECIMAL_ZERO &decimal_zero - - -#define DECIMAL_ONE &decimal_one - -#define DECIMAL_HALF &decimal_half - -decimal decimal_add (), decimal_sub (), decimal_mul (), decimal_div (); -decimal decimal_mul_dc (), decimal_mul_rounded (), decimal_rem (); -decimal decimal_round_digits (), decimal_trunc_digits (); -decimal make_decimal (), decimal_copy (), decimal_parse (); -decimal decimal_sqrt (), decimal_expt (); - -void decimal_print (); - -/* End of decimal.h */ diff --git a/gnu/usr.bin/dc/doc/dc.texinfo b/gnu/usr.bin/dc/doc/dc.texinfo index 15b285f..73f687b 100644 --- a/gnu/usr.bin/dc/doc/dc.texinfo +++ b/gnu/usr.bin/dc/doc/dc.texinfo @@ -1,7 +1,7 @@ \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename dc.info -@settitle DC, An Arbitrary Precision Calculator +@settitle dc, an arbitrary precision calculator @c %**end of header @c This file has the new style title page commands. @@ -21,7 +21,7 @@ @syncodeindex tp fn @ifinfo -This file documents DC, an arbitrary precision calculator. +This file documents @sc{dc}, an arbitrary precision calculator. Published by the Free Software Foundation, 675 Massachusetts Avenue, @@ -51,15 +51,16 @@ except that this permission notice may be stated in a translation approved by the Foundation. @end ifinfo -@setchapternewpage odd +@setchapternewpage off @titlepage -@title DC, An Arbitrary Precision Calculator +@title dc, an arbitrary precision calculator -@author by Richard Stallman +@author by Ken Pizzini +@author manual by Richard Stallman @page @vskip 0pt plus 1filll -Copyright @copyright{} 1984 Free Software Foundation, Inc. +Copyright @copyright{} 1994 Free Software Foundation, Inc. @sp 2 Published by the Free Software Foundation, @* @@ -94,6 +95,7 @@ by the Foundation. * Parameters:: Parameters * Strings:: Strings * Status Inquiry:: Status Inquiry +* Miscellaneous:: Other commands * Notes:: Notes @end menu @@ -101,27 +103,34 @@ by the Foundation. @comment node-name, next, previous, up @chapter Introduction -DC is a reverse-polish desk calculator which supports unlimited -precision arithmetic. It also allows you to define and call macros. -Normally DC reads from the standard input; if any command arguments -are given to it, they are filenames, and DC reads and executes the -contents of the files before reading from standard input. All output -is to standard output. - -To exit, use @samp{q}. @kbd{C-c} does not exit; it is used to abort -macros that are looping, etc. (Currently this is not true; @kbd{C-c} -does exit.) - -A reverse-polish calculator stores numbers on a stack. Entering a -number pushes it on the stack. Arithmetic operations pop arguments off -the stack and push the results. - -To enter a number in DC, type the digits, with an optional decimal -point. Exponential notation is not supported. To enter a negative -number, begin the number with @samp{_}. @samp{-} cannot be used for -this, as it is a binary operator for subtraction instead. -To enter two numbers in succession, separate them with spaces or -newlines. These have no meaning as commands. +@sc{dc} is a reverse-polish desk calculator +which supports unlimited precision arithmetic. +It also allows you to define and call macros. +Normally @sc{dc} reads from the standard input; +if any command arguments are given to it, they are filenames, +and @sc{dc} reads and executes the contents of the files +before reading from standard input. +All normal output is to standard output; +all error messages are written to standard error. + +To exit, use @samp{q}. +@kbd{C-c} does not exit; +it is used to abort macros that are looping, etc. +(Currently this is not true; @kbd{C-c} does exit.) + +A reverse-polish calculator stores numbers on a stack. +Entering a number pushes it on the stack. +Arithmetic operations pop arguments off the stack and push the results. + +To enter a number in @sc{dc}, type the digits, +with an optional decimal point. +Exponential notation is not supported. +To enter a negative number, begin the number with @samp{_}. +@samp{-} cannot be used for this, as it is a binary operator +for subtraction instead. +To enter two numbers in succession, +separate them with spaces or newlines. +These have no meaning as commands. @node Printing Commands, Arithmetic, Introduction, Top @chapter Printing Commands @@ -129,19 +138,19 @@ newlines. These have no meaning as commands. @table @samp @item p Prints the value on the top of the stack, -without altering the stack. A newline is printed -after the value. +without altering the stack. +A newline is printed after the value. @item P -Prints the value on the top of the stack, -popping it off, and does not print a newline after. +Prints the value on the top of the stack, popping it off, +and does not print a newline after. @item f Prints the entire contents of the stack -and the contents of all of the registers, -without altering anything. This is a good command -to use if you are lost or want to figure out -what the effect of some command has been. +@c and the contents of all of the registers, +without altering anything. +This is a good command to use if you are lost or want +to figure out what the effect of some command has been. @end table @node Arithmetic, Stack Control, Printing Commands, Top @@ -149,10 +158,9 @@ what the effect of some command has been. @table @samp @item + -Pops two values off the stack, adds them, -and pushes the result. The precision of the result -is determined only by the values of the arguments, -and is enough to be exact. +Pops two values off the stack, adds them, and pushes the result. +The precision of the result is determined only +by the values of the arguments, and is enough to be exact. @item - Pops two values, subtracts the first one popped @@ -161,41 +169,46 @@ from the second one popped, and pushes the result. @item * Pops two values, multiplies them, and pushes the result. The number of fraction digits in the result is controlled -by the current precision flag (see below) and does not +by the current precision value (see below) and does not depend on the values being multiplied. @item / -Pops two values, divides the second one popped from -the first one popped, and pushes the result. -The number of fraction digits is specified by the precision flag. +Pops two values, divides the second one popped +from the first one popped, and pushes the result. +The number of fraction digits is specified by the precision value. @item % -Pops two values, computes the remainder of the division -that the @samp{/} command would do, and pushes that. +Pops two values, +computes the remainder of the division that +the @samp{/} command would do, +and pushes that. The division is done with as many fraction digits -as the precision flag specifies, and the remainder -is also computed with that many fraction digits. +as the precision value specifies, +and the remainder is also computed with that many fraction digits. @item ^ -Pops two values and exponentiates, using the first -value popped as the exponent and the second popped as the base. +Pops two values and exponentiates, +using the first value popped as the exponent +and the second popped as the base. The fraction part of the exponent is ignored. -The precision flag specifies the number of fraction +The precision value specifies the number of fraction digits in the result. @item v Pops one value, computes its square root, and pushes that. -The precision flag specifies the number of fraction digits +The precision value specifies the number of fraction digits in the result. @end table -Most arithmetic operations are affected by the "precision flag", -which you can set with the @samp{k} command. The default precision -value is zero, which means that all arithmetic except for +Most arithmetic operations are affected by the @emph{precision value}, +which you can set with the @samp{k} command. +The default precision value is zero, +which means that all arithmetic except for addition and subtraction produces integer results. -The remainder operation (@samp{%}) requires some explanation: applied to -arguments @samp{a} and @samp{b} it produces @samp{a - (b * (a / b))}, +The remainder operation (@samp{%}) requires some explanation: +applied to arguments @samp{a} and @samp{b} +it produces @samp{a - (b * (a / b))}, where @samp{a / b} is computed in the current precision. @node Stack Control, Registers, Arithmetic, Top @@ -207,28 +220,28 @@ Clears the stack, rendering it empty. @item d Duplicates the value on the top of the stack, -pushing another copy of it. Thus, -`4d*p' computes 4 squared and prints it. +pushing another copy of it. +Thus, @samp{4d*p} computes 4 squared and prints it. @end table @node Registers, Parameters, Stack Control, Top @chapter Registers -DC provides 128 memory registers, each named by a single -ASCII character. You can store a number in a register -and retrieve it later. +@sc{dc} provides 256 memory registers, each named by a single character. +You can store a number in a register and retrieve it later. @table @samp @item s@var{r} -Pop the value off the top of the stack and store -it into register @var{r}. +Pop the value off the top of the stack and +store it into register @var{r}. @item l@var{r} -Copy the value in register @var{r}, and push it onto -the stack. This does not alter the contents of @var{r}. +Copy the value in register @var{r}, +and push it onto the stack. +This does not alter the contents of @var{r}. -Each register also contains its own stack. The current -register value is the top of the register's stack. +Each register also contains its own stack. +The current register value is the top of the register's stack. @item S@var{r} Pop the value off the top of the (main) stack and @@ -237,31 +250,34 @@ The previous value of the register becomes inaccessible. @item L@var{r} Pop the value off the top of register @var{r}'s stack -and push it onto the main stack. The previous value -in register @var{r}'s stack, if any, is now accessible -via the `l@var{r}' command. +and push it onto the main stack. +The previous value in register @var{r}'s stack, if any, +is now accessible via the @samp{l@var{r}} command. @end table - -The @samp{f} command prints a list of all registers that have contents -stored in them, together with their contents. Only the -current contents of each register (the top of its stack) -is printed. +@c +@c The @samp{f} command prints a list of all registers that have contents +@c stored in them, together with their contents. +@c Only the current contents of each register (the top of its stack) +@c is printed. @node Parameters, Strings, Registers, Top @chapter Parameters -DC has three parameters that control its operation: the precision, the -input radix, and the output radix. The precision specifies the number -of fraction digits to keep in the result of most arithmetic operations. +@sc{dc} has three parameters that control its operation: +the precision, the input radix, and the output radix. +The precision specifies the number of fraction digits +to keep in the result of most arithmetic operations. The input radix controls the interpretation of numbers typed in; -@emph{all} numbers typed in use this radix. The output radix is used -for printing numbers. +@emph{all} numbers typed in use this radix. +The output radix is used for printing numbers. -The input and output radices are separate parameters; you can make them -unequal, which can be useful or confusing. Each radix must be between 2 -and 36 inclusive. The precision must be zero or greater. The precision -is always measured in decimal digits, regardless of the current input or -output radix. +The input and output radices are separate parameters; +you can make them unequal, which can be useful or confusing. +The input radix must be between 2 and 36 inclusive. +The output radix must be at least 2. +The precision must be zero or greater. +The precision is always measured in decimal digits, +regardless of the current input or output radix. @table @samp @item i @@ -269,42 +285,51 @@ Pops the value off the top of the stack and uses it to set the input radix. @item o -@itemx k -Similarly set the output radix and the precision. +Pops the value off the top of the stack +and uses it to set the output radix. + +@item k +Pops the value off the top of the stack +and uses it to set the precision. @item I Pushes the current input radix on the stack. @item O -@itemx K -Similarly push the current output radix and the current precision. +Pushes the current output radix on the stack. + +@item K +Pushes the current precision on the stack. + @end table @node Strings, Status Inquiry, Parameters, Top @chapter Strings -DC can operate on strings as well as on numbers. The only things you -can do with strings are print them and execute them as macros (which -means that the contents of the string are processed as DC commands). -Both registers and the stack can hold strings, and DC always knows -whether any given object is a string or a number. Some commands such as -arithmetic operations demand numbers as arguments and print errors if -given strings. Other commands can accept either a number or a string; +@sc{dc} can operate on strings as well as on numbers. +The only things you can do with strings are print them +and execute them as macros +(which means that the contents of the string are processed as @sc{dc} commands). +Both registers and the stack can hold strings, +and @sc{dc} always knows whether any given object is a string or a number. +Some commands such as arithmetic operations demand numbers +as arguments and print errors if given strings. +Other commands can accept either a number or a string; for example, the @samp{p} command can accept either and prints the object according to its type. @table @samp @item [@var{characters}] -Makes a string containing @var{characters} and pushes it -on the stack. For example, @samp{[foo]P} prints the -characters @samp{foo} (with no newline). +Makes a string containing @var{characters} and pushes it on the stack. +For example, @samp{[foo]P} prints the characters @samp{foo} +(with no newline). @item x Pops a value off the stack and executes it as a macro. -Normally it should be a string; if it is a number, -it is simply pushed back onto the stack. -For example, @samp{[1p]x} executes the macro @samp{1p}, which -pushes 1 on the stack and prints @samp{1} on a separate line. +Normally it should be a string; +if it is a number, it is simply pushed back onto the stack. +For example, @samp{[1p]x} executes the macro @samp{1p}, +which pushes 1 on the stack and prints @samp{1} on a separate line. Macros are most often stored in registers; @samp{[1p]sa} stores a macro to print @samp{1} into register @samp{a}, @@ -312,70 +337,89 @@ and @samp{lax} invokes the macro. @item >@var{r} Pops two values off the stack and compares them -assuming they are numbers, executing the contents -of register @var{r} as a macro if the original top-of-stack -is greater. Thus, @samp{1 2>a} will invoke register @samp{a}'s contents +assuming they are numbers, +executing the contents of register @var{r} as a macro +if the original top-of-stack is greater. +Thus, @samp{1 2>a} will invoke register @samp{a}'s contents and @samp{2 1>a} will not. @item <@var{r} -Similar but invokes the macro if the original top-of-stack -is less. +Similar but invokes the macro if the original top-of-stack is less. @item =@var{r} -Similar but invokes the macro if the two numbers popped -are equal. This can also be validly used to compare two -strings for equality. +Similar but invokes the macro if the two numbers popped are equal. +@c This can also be validly used to compare two strings for equality. @item ? Reads a line from the terminal and executes it. This command allows a macro to request input from the user. @item q -During the execution of a macro, this comand -does not exit DC. Instead, it exits from that -macro and also from the macro which invoked it (if any). +During the execution of a macro, +this command exits from the macro and also from the macro which invoked it. +If called from the top level, +or from a macro which was called directly from the top level, +the @samp{q} command will cause @sc{dc} to exit. @item Q Pops a value off the stack and uses it as a count -of levels of macro execution to be exited. Thus, -@samp{3Q} exits three levels. +of levels of macro execution to be exited. +Thus, @samp{3Q} exits three levels. @end table -@node Status Inquiry, Notes, Strings, Top +@node Status Inquiry, Miscellaneous, Strings, Top @chapter Status Inquiry @table @samp @item Z -Pops a value off the stack, calculates the number of -digits it has (or number of characters, if it is a string) +Pops a value off the stack, +calculates the number of digits it has +(or number of characters, if it is a string) and pushes that number. @item X -Pops a value off the stack, calculates the number of -fraction digits it has, and pushes that number. -For a string, the value pushed is -1. +Pops a value off the stack, +calculates the number of fraction digits it has, +and pushes that number. +For a string, the value pushed is +@c -1. +0. @item z -Pushes the current stack depth; the number of -objects on the stack before the execution of the @samp{z} command. +Pushes the current stack depth; +the number of objects on the stack +before the execution of the @samp{z} command. +@end table -@item I -Pushes the current value of the input radix. +@node Miscellaneous, Notes, Status Inquiry, Top +@chapter Miscellaneous -@item O -Pushes the current value of the output radix. +@table @samp +@item ! +Will run the rest of the line as a system command. -@item K -Pushes the current value of the precision. +@item # +Will interpret the rest of the line as a comment. + +@item :@var{r} +Will pop the top two values off of the stack. +The old second-to-top value will be stored in the array @var{r}, +indexed by the old top-of-stack value. + +@item ;@var{r} +Pops the top-of-stack and uses it as an index into +the array @var{r}. +The selected value is then pushed onto the stack. @end table -@node Notes, , Status Inquiry, Top +@node Notes, , Miscellaneous, Top @chapter Notes -The @samp{:} and @samp{;} commands of the Unix DC program are -not supported, as the documentation does not say what they do. -The @samp{!} command is not supported, but will be supported -as soon as a library for executing a line as a command exists. +The array operations @samp{:} and @samp{;} are usually +only used by traditional implementations of BC. +(The GNU BC is self contained and does not need @sc{dc} to run.) +The comment operator @samp{#} is a new command +not found in traditional implementations of @sc{dc}. @contents @bye |