Upgrade to dc version 1.0 which comes bundled with
bc version 1.03.
This commit is contained in:
parent
defb499d08
commit
485f6985a7
@ -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'.
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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.
|
105
gnu/usr.bin/dc/dc-array.c
Normal file
105
gnu/usr.bin/dc/dc-array.c
Normal file
@ -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);
|
||||
}
|
569
gnu/usr.bin/dc/dc-eval.c
Normal file
569
gnu/usr.bin/dc/dc-eval.c
Normal file
@ -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;
|
||||
}
|
224
gnu/usr.bin/dc/dc-misc.c
Normal file
224
gnu/usr.bin/dc/dc-misc.c
Normal file
@ -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);
|
||||
}
|
478
gnu/usr.bin/dc/dc-number.c
Normal file
478
gnu/usr.bin/dc/dc-number.c
Normal file
@ -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);
|
||||
}
|
76
gnu/usr.bin/dc/dc-proto.h
Normal file
76
gnu/usr.bin/dc/dc-proto.h
Normal file
@ -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 *));
|
38
gnu/usr.bin/dc/dc-regdef.h
Normal file
38
gnu/usr.bin/dc/dc-regdef.h
Normal file
@ -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
|
367
gnu/usr.bin/dc/dc-stack.c
Normal file
367
gnu/usr.bin/dc/dc-stack.c
Normal file
@ -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);
|
||||
}
|
193
gnu/usr.bin/dc/dc-string.c
Normal file
193
gnu/usr.bin/dc/dc-string.c
Normal file
@ -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 */
|
||||
}
|
22
gnu/usr.bin/dc/dc-version.h
Normal file
22
gnu/usr.bin/dc/dc-version.h
Normal file
@ -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"
|
@ -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
|
||||
.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 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.
|
||||
\*(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"
|
||||
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
|
||||
\*(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.
|
||||
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
|
||||
.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
|
||||
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.
|
||||
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.
|
||||
Pushes the current precision on the stack.
|
||||
.SH
|
||||
Strings
|
||||
.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;
|
||||
\*(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.
|
||||
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
|
||||
.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
|
||||
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 .
|
||||
|
@ -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;
|
||||
}
|
81
gnu/usr.bin/dc/dc.h
Normal file
81
gnu/usr.bin/dc/dc.h
Normal file
@ -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 */
|
File diff suppressed because it is too large
Load Diff
@ -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 */
|
@ -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.
|
||||
@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.)
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
@item I
|
||||
Pushes the current value of the input radix.
|
||||
|
||||
@item O
|
||||
Pushes the current value of the output radix.
|
||||
|
||||
@item K
|
||||
Pushes the current value of the precision.
|
||||
Pushes the current stack depth;
|
||||
the number of objects on the stack
|
||||
before the execution of the @samp{z} command.
|
||||
@end table
|
||||
|
||||
@node Notes, , Status Inquiry, Top
|
||||
@node Miscellaneous, Notes, Status Inquiry, Top
|
||||
@chapter Miscellaneous
|
||||
|
||||
@table @samp
|
||||
@item !
|
||||
Will run the rest of the line as a system command.
|
||||
|
||||
@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, , 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
|
||||
|
Loading…
Reference in New Issue
Block a user