* `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)
* 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 "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 */
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 */
decimal regs
[128]; /* "registers", with single-character names */
regstack regstacks
[128]; /* 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.
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) */
regstack
get_regstack ();
bzero (regs
, sizeof regs
);
bzero (regstacks
, sizeof regstacks
);
buffer
= (char *) xmalloc (40);
stack
= (decimal
*) xmalloc (stacksize
* sizeof (decimal
));
macrostack
= (decimal
*) xmalloc (macrostacksize
* sizeof (decimal
));
macroindex
= (int *) xmalloc (macrostacksize
* sizeof (int));
/* Initialize for reading input files if any */
/* Read and execute one command from the current source of input */
case '+': /* Arithmetic operators... */
binop (decimal_mul_dc
); /* Like decimal_mul but hairy
way of deciding precision to keep */
case '_': /* Begin a negative decimal constant */
decimal tem
= dec_read (stdin
);
case '9': /* All these begin decimal constants */
case 'c': /* Clear the stack */
decref (stack
[stacktop
--]);
case 'd': /* Duplicate top of stack */
error ("stack empty", 0);
else push (stack
[stacktop
]);
case 'f': /* Describe all registers and stack contents */
int somereg
= 0; /* set to 1 if we print any registers */
for (regno
= 0; regno
< 128; regno
++)
printf ("register %c: ", regno
);
printf ("stack empty\n");
for (i
= 0; i
<= stacktop
; i
++)
print_obj (stack
[stacktop
- i
]);
case 'i': /* ibase <- top of stack */
case 'I': /* Push current ibase */
push (decimal_from_int (ibase
));
case 'k': /* like i, I but for precision instead of ibase */
push (decimal_from_int (precision
));
case 'l': /* l<x> load register <x> onto stack */
error ("register %c empty", c1
);
case 'L': /* L<x> load register <x> to stack, pop <x>'s own stack */
error ("nothing pushed on register %c", c1
);
regstack r
= regstacks
[c1
];
error ("register %c empty after pop", c1
);
case 'o': /* o, O like i, I but for obase instead of ibase */
push (decimal_from_int (obase
));
case 'p': /* Print tos, don't pop, do print newline afterward */
error ("stack empty", 0);
print_obj (stack
[stacktop
]);
case 'P': /* Print tos, do pop, no newline afterward */
{ popmacro (); popmacro (); } /* decrease recursion level by 2 */
exit (0); /* If not in a macro, exit the program. */
case 'Q': /* Tos says how many levels to exit */
case 's': /* s<x> -- Pop stack and set register <x> */
if (regs
[c1
]) decref (regs
[c1
]);
regs
[c1
] = stack
[stacktop
--];
case 'S': /* S<x> -- pop stack and push as new value of register <x> */
regs
[c1
] = stack
[stacktop
--];
case 'v': /* tos gets square root of tos */
case 'x': /* pop stack , call as macro */
case 'X': /* Pop stack, get # fraction digits, push that */
case 'z': /* Compute depth of stack, push that */
push (decimal_from_int (stacktop
+ 1));
case 'Z': /* Pop stack, get # digits, push that */
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,
This seems backwards to me, but I am preserving compatibility. */
case '?': /* Read expression from terminal and execute it */
/* First ignore any leading newlines */
while ((c1
= getchar ()) == '\n');
/* Read a line from the terminal and execute it. */
pushmacro (read_string ('\n', fgetchar
, 0));
case '[': /* Begin string constant */
push (read_string (']', fetch
, '['));
error ("undefined command %c", c
);
/* Functionals for performing arithmetic, etc */
/* Call the function `op', with the top of stack value as argument,
If the stack is empty, print a message and do not call `op'. */
decimal value
= stack
[stacktop
--];
/* 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
error ("stack empty", 0);
else if (stack
[stacktop
]->after
== STRING
|| stack
[stacktop
- 1]->after
== STRING
)
error ("operands not both numeric");
decimal arg2
= stack
[stacktop
--];
decimal arg1
= stack
[stacktop
--];
push (op (arg1
, arg2
, precision
));
error ("register %c is empty", regno
);
decimal arg2
= stack
[stacktop
--];
decimal arg1
= stack
[stacktop
--];
int relation
= decimal_compare (arg1
, arg2
);
|| (cond
< 0 && relation
< 0)
|| (cond
> 0 && relation
> 0))
/* Handle the command input source */
/* Fetch the next command character from a macro or from the terminal */
LENGTH (macrostack
[macrolevel
-1]) == macroindex
[macrolevel
-1])
return macrostack
[macrolevel
- 1]->contents
[macroindex
[macrolevel
-1]++];
open_file
= fopen (*next_file
++, "r");
perror_with_name (*(next_file
- 1));
/* Unread character c on command input stream, whatever it is */
macroindex
[macrolevel
-1]--;
/* Begin execution of macro m. */
LENGTH (macrostack
[macrolevel
-1]) == macroindex
[macrolevel
-1])
if (macrolevel
== macrostacksize
)
macrostack
= (decimal
*) xrealloc (macrostack
, macrostacksize
* sizeof (decimal
));
macroindex
= (int *) xrealloc (macroindex
, macrostacksize
* sizeof (int));
macroindex
[macrolevel
] = 0;
macrostack
[macrolevel
++] = m
;
{ /* 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. */
int num_pops
= decimal_to_int (d
);
for (i
= 0; i
< num_pops
; i
++)
/* Exit one level of macro execution. */
decref (macrostack
[--macrolevel
]);
if (stacktop
== stacksize
- 1)
stack
= (decimal
*) xrealloc (stack
, (stacksize
*= 2) * sizeof (decimal
));
/* Reference counting and storage freeing */
error ("stack empty", 0);
regstack r
= freeregstacks
;
freeregstacks
= r
->rest
;
return (regstack
) xmalloc (sizeof (struct regstack
));
regstack r
= get_regstack ();
/* Input of numbers and strings */
/* Return a character read from the terminal. */
/* 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. */
read_string (stopc
, inputfn
, startc
)
if (c
< 0 || (c
== stopc
&& count
== 0))
error ("Unmatched `%c'", startc
);
buffer
= (char *) xrealloc (buffer
, bufsize
*= 2);
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
);
/* Read a number from the current input source */
if (! ((c
>= '0' && c
<= '9')
|| (c
>= 'A' && c
<= 'F')
buffer
= (char *) xrealloc (buffer
, bufsize
*= 2);
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. */
if (obj
->after
== STRING
)
decimal_print (obj
, fputchar
, obase
);
/* Print the contents of the decimal number `string', treated as a string. */
char *p
= string
->contents
;
int len
= LENGTH (string
);
for (i
= 0; i
< len
; i
++)
/* Set the input radix from the value of the decimal number d, if valid. */
int value
= decimal_to_int (d
);
if (value
< 2 || value
> 36)
error ("input radix must be from 2 to 36", 0);
/* Set the output radix from the value of the decimal number d, if valid. */
int value
= decimal_to_int (d
);
if (value
< 2 || value
> 36)
error ("output radix must be from 2 to 36", 0);
/* Set the precision for mul and div from the value of the decimal number d, if valid. */
int value
= decimal_to_int (d
);
if (value
< 0 || value
> 30000)
error ("precision must be nonnegative and < 30000", 0);
/* Push the number of digits in decimal number d, as a decimal number. */
push (decimal_from_int (LENGTH (d
)));
/* Push the number of fraction digits in d. */
push (decimal_from_int (d
->after
));
/* Push the square root of decimal number d. */
push (decimal_sqrt (d
, precision
));
/* Print error message and exit. */
/* Print error message. `s1' is printf control string, `s2' is arg for it. */
extern int errno
, sys_nerr
;
extern char *sys_errlist
[];
s
= concat ("", sys_errlist
[errno
], " for %s");
/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
int len1
= strlen (s1
), len2
= strlen (s2
), len3
= strlen (s3
);
char *result
= (char *) xmalloc (len1
+ len2
+ len3
+ 1);
strcpy (result
+ len1
, s2
);
strcpy (result
+ len1
+ len2
, s3
);
*(result
+ len1
+ len2
+ len3
) = 0;
/* Like malloc but get fatal error if memory is exhausted. */
int result
= malloc (size
);
fatal ("virtual memory exhausted", 0);
int result
= realloc (ptr
, size
);
fatal ("virtual memory exhausted");