xref: /386bsd/usr/src/usr.bin/dc/dc.c (revision a2142627)
1 /*
2  * `dc' desk calculator utility.
3  *
4  * Copyright (C) 1984, 1993 Free Software Foundation, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you can either send email to this
18  * program's author (see below) or write to: The Free Software Foundation,
19  * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA.
20  */
21 
22 #include <stdio.h>
23 #include "decimal.h"  /* definitions for our decimal arithmetic package */
24 
25 FILE *open_file;	/* input file now open */
26 int file_count;		/* Number of input files not yet opened */
27 char **next_file;	/* Pointer to vector of names of input files left */
28 
29 struct regstack
30   {
31     decimal value;	/* Saved value of register */
32     struct regstack *rest;	/* Tail of list */
33   };
34 
35 typedef struct regstack *regstack;
36 
37 regstack freeregstacks;	/* Chain of free regstack structures for fast realloc */
38 
39 decimal regs[128];	/* "registers", with single-character names */
40 regstack regstacks[128]; /* For each register, a stack of previous values */
41 
42 int stacktop;		/* index of last used element in stack */
43 int stacksize;		/* Current allocates size of stack */
44 decimal *stack;	/* Pointer to computation stack */
45 
46 /* A decimal number can be regarded as a string by
47  treating its contents as characters and ignoring the
48  position of its decimal point.
49  Decimal numbers are marked as strings by having an `after' field of -1
50  One use of strings is to execute them as macros.
51 */
52 
53 #define STRING -1
54 
55 int macrolevel;	/* Current macro nesting; 0 if taking keyboard input */
56 int macrostacksize;	/* Current allocated size of macrostack and macroindex */
57 decimal *macrostack;	/* Pointer to macro stack array */
58 int *macroindex;	/* Pointer to index-within-macro stack array */
59 	/* Note that an empty macro is popped from the stack
60 	   only when an trying to read a character from it
61 	   or trying to push another macro.  */
62 
63 int ibase;	/* Radix for numeric input.  */
64 int obase;	/* Radix for numeric output.  */
65 int precision;	/* Number of digits to keep in multiply and divide.  */
66 
67 char *buffer;	/* Address of buffer used for reading numbers */
68 int bufsize;	/* Current size of buffer (made bigger when nec) */
69 
70 decimal dec_read ();
71 regstack get_regstack ();
72 int fetch ();
73 int fgetchar ();
74 char *concat ();
75 void pushsqrt ();
76 void condop ();
77 void setibase ();
78 void setobase ();
79 void setprecision ();
80 void pushmacro ();
81 decimal read_string ();
82 void pushlength ();
83 void pushscale ();
84 void unfetch ();
85 void popmacros ();
86 void popmacro ();
87 void popstack ();
88 void print_obj ();
89 void print_string ();
90 void free_regstack ();
91 void pushreg ();
92 void execute ();
93 void fputchar ();
94 void push ();
95 void incref ();
96 void decref ();
97 void binop ();
98 
main(argc,argv,env)99 main (argc, argv, env)
100      int argc;
101      char **argv, **env;
102 {
103 
104   ibase = 10;
105   obase = 10;
106   precision = 0;
107 
108   freeregstacks = 0;
109 
110   bzero (regs, sizeof regs);
111   bzero (regstacks, sizeof regstacks);
112 
113   bufsize = 40;
114   buffer = (char *) xmalloc (40);
115 
116   stacksize = 40;
117   stack = (decimal *) xmalloc (stacksize * sizeof (decimal));
118   stacktop = -1;
119 
120   macrostacksize = 40;
121   macrostack = (decimal *) xmalloc (macrostacksize * sizeof (decimal));
122   macroindex = (int *) xmalloc (macrostacksize * sizeof (int));
123   macrolevel = 0;
124   /* Initialize for reading input files if any */
125 
126   open_file = 0;
127 
128   file_count = argc - 1;
129   next_file = argv + 1;
130 
131 
132   while (1)
133     {
134       execute ();
135     }
136 }
137 
138 /* Read and execute one command from the current source of input */
139 
140 void
execute()141 execute ()
142 {
143   int c = fetch ();
144 
145   if (c < 0) exit (0);
146 
147     {
148       switch (c)
149 	{
150 	case '+':		/* Arithmetic operators... */
151 	  binop (decimal_add);
152 	  break;
153 
154 	case '-':
155 	  binop (decimal_sub);
156 	  break;
157 
158 	case '*':
159 	  binop (decimal_mul_dc);  /* Like decimal_mul but hairy
160 				      way of deciding precision to keep */
161 	  break;
162 
163 	case '/':
164 	  binop (decimal_div);
165 	  break;
166 
167 	case '%':
168 	  binop (decimal_rem);
169 	  break;
170 
171 	case '^':
172 	  binop (decimal_expt);
173 	  break;
174 
175 	case '_':		/* Begin a negative decimal constant */
176 	  {
177 	    decimal tem = dec_read (stdin);
178 	    tem->sign = !tem->sign;
179 	    push (tem);
180 	  }
181 	  break;
182 
183 	case '.':
184 	case '0':
185 	case '1':
186 	case '2':
187 	case '3':
188 	case '4':
189 	case '5':
190 	case '6':
191 	case '7':
192 	case '8':
193 	case '9':		/* All these begin decimal constants */
194 	  unfetch (c);
195 	  push (dec_read (stdin));
196 	  break;
197 
198 	case 'A':
199 	case 'B':
200 	case 'C':
201 	case 'D':
202 	case 'E':
203 	case 'F':
204 	  unfetch (c);
205 	  push (dec_read (stdin));
206 	  break;
207 
208 	case 'c':		/* Clear the stack */
209 	  while (stacktop >= 0)
210 	    decref (stack[stacktop--]);
211 	  break;
212 
213 	case 'd':		/* Duplicate top of stack */
214 	  if (stacktop < 0)
215 	    error ("stack empty", 0);
216 	  else push (stack[stacktop]);
217 	  break;
218 
219 	case 'f':		/* Describe all registers and stack contents */
220 	  {
221 	    int regno;
222 	    int somereg = 0;	/* set to 1 if we print any registers */
223 	    for (regno = 0; regno < 128; regno++)
224 	      {
225 		if (regs[regno])
226 		  {
227 		    printf ("register %c: ", regno);
228 		    print_obj (regs[regno]);
229 		    somereg = 1;
230 		    printf ("\n");
231 		  }
232 	      }
233 	    if (somereg)
234 	      printf ("\n");
235 	    if (stacktop < 0)
236 	      printf ("stack empty\n");
237 	    else
238 	      {
239 		int i;
240 		printf ("stack:\n");
241 		for (i = 0; i <= stacktop; i++)
242 		  {
243 		    print_obj (stack[stacktop - i]);
244 		    printf ("\n");
245 		  }
246 	      }
247 	  }
248 	  break;
249 
250 	case 'i':		/* ibase <- top of stack */
251 	  popstack (setibase);
252 	  break;
253 
254 	case 'I':		/* Push current ibase */
255 	  push (decimal_from_int (ibase));
256 	  break;
257 
258 	case 'k':		/* like i, I but for precision instead of ibase */
259 	  popstack (setprecision);
260 	  break;
261 
262 	case 'K':
263 	  push (decimal_from_int (precision));
264 	  break;
265 
266 	case 'l':		/* l<x> load register <x> onto stack */
267 	  {
268 	    char c1 = fetch ();
269 	    if (c1 < 0) exit (0);
270 	    if (!regs[c1])
271 	      error ("register %c empty", c1);
272 	    else
273 	      push (regs[c1]);
274 	  }
275 	  break;
276 
277 	case 'L':		/* L<x> load register <x> to stack, pop <x>'s own stack */
278 	  {
279 	    char c1 = fetch ();
280 	    if (c1 < 0) exit (0);
281 	    if (!regstacks[c1])
282 	      error ("nothing pushed on register %c", c1);
283 	    else
284 	      {
285 		regstack r = regstacks[c1];
286 		if (!regs[c1])
287 		  error ("register %c empty after pop", c1);
288 		else
289 		  push (regs[c1]);
290 		regs[c1] = r->value;
291 		regstacks[c1] = r->rest;
292 		free_regstack (r);
293 	      }
294 	  }
295 	  break;
296 
297 	case 'o':		/* o, O like i, I but for obase instead of ibase */
298 	  popstack (setobase);
299 	  break;
300 
301 	case 'O':
302 	  push (decimal_from_int (obase));
303 	  break;
304 
305 	case 'p':		/* Print tos, don't pop, do print newline afterward */
306 	  if (stacktop < 0)
307 	    error ("stack empty", 0);
308 	  else
309 	    {
310 	      print_obj (stack[stacktop]);
311 	      printf ("\n");
312 	    }
313 	  break;
314 
315 	case 'P':		/* Print tos, do pop, no newline afterward */
316 	  popstack (print_obj);
317 	  break;
318 
319 	case 'q':		/* Exit */
320 	  if (macrolevel)
321 	    { popmacro (); popmacro (); }  /* decrease recursion level by 2 */
322 	  else
323 	    exit (0);		/* If not in a macro, exit the program.  */
324 
325 	  break;
326 
327 	case 'Q':		/* Tos says how many levels to exit */
328 	  popstack (popmacros);
329 	  break;
330 
331 	case 's':		/* s<x> -- Pop stack and set register <x> */
332 	  if (stacktop < 0)
333 	    empty ();
334 	  else
335 	    {
336 	      int c1 = fetch ();
337 	      if (c1 < 0) exit (0);
338 	      if (regs[c1]) decref (regs[c1]);
339 	      regs[c1] = stack[stacktop--];
340 	    }
341 	  break;
342 
343 	case 'S':		/* S<x> -- pop stack and push as new value of register <x> */
344 	  if (stacktop < 0)
345 	    empty ();
346 	  else
347 	    {
348 	      int c1 = fetch ();
349 	      if (c1 < 0) exit (0);
350 	      pushreg (c1);
351 	      regs[c1] = stack[stacktop--];
352 	    }
353 	  break;
354 
355 	case 'v':		/* tos gets square root of tos */
356 	  popstack (pushsqrt);
357 	  break;
358 
359 	case 'x':		/* pop stack , call as macro */
360 	  popstack (pushmacro);
361 	  break;
362 
363 	case 'X':		/* Pop stack, get # fraction digits, push that */
364 	  popstack (pushscale);
365 	  break;
366 
367 	case 'z':		/* Compute depth of stack, push that */
368 	  push (decimal_from_int (stacktop + 1));
369 	  break;
370 
371 	case 'Z':		/* Pop stack, get # digits, push that */
372 	  popstack (pushlength);
373 	  break;
374 
375 	case '<':		/* Conditional: pop two numbers, compare, maybe execute register */
376 	  /* Note: for no obvious reason, the standard Unix `dc'
377 	     considers < to be true if the top of stack is less
378 	     than the next-to-top of stack,
379 	     and vice versa for >.
380 	     This seems backwards to me, but I am preserving compatibility.  */
381 	  condop (1);
382 	  break;
383 
384 	case '>':
385 	  condop (-1);
386 	  break;
387 
388 	case '=':
389 	  condop (0);
390 	  break;
391 
392 	case '?':		/* Read expression from terminal and execute it */
393 	  /* First ignore any leading newlines */
394 	  {
395 	    int c1;
396 	    while ((c1 = getchar ()) == '\n');
397 	    ungetc (c1, stdin);
398 	  }
399 	  /* Read a line from the terminal and execute it.  */
400 	  pushmacro (read_string ('\n', fgetchar, 0));
401 	  break;
402 
403 	case '[':		/* Begin string constant */
404 	  push (read_string (']', fetch, '['));
405 	  break;
406 
407         case ' ':
408 	case '\n':
409 	  break;
410 
411 	default:
412 	  error ("undefined command %c", c);
413 	}
414     }
415 }
416 
417 /* Functionals for performing arithmetic, etc */
418 
419 /* Call the function `op', with the top of stack value as argument,
420  and then pop the stack.
421  If the stack is empty, print a message and do not call `op'.  */
422 
423 void
424 popstack (op)
425      void (*op) ();
426 {
427   if (stacktop < 0)
428     empty ();
429   else
430     {
431       decimal value = stack[stacktop--];
432       op (value);
433       decref (value);
434     }
435 }
436 
437 /* Call the function `op' with two arguments taken from the stack top,
438  then pop those arguments and push the value returned by `op'.
439  `op' is assumed to return a decimal number.
440  If there are not two values on the stack, print a message
441  and do not call `op'.  */
442 
443 void
444 binop (op)
445      decimal (*op) ();
446 {
447   if (stacktop < 1)
448     error ("stack empty", 0);
449   else if (stack[stacktop]->after == STRING || stack[stacktop - 1]->after == STRING)
450     error ("operands not both numeric");
451   else
452     {
453       decimal arg2 = stack [stacktop--];
454       decimal arg1 = stack [stacktop--];
455 
456       push (op (arg1, arg2, precision));
457 
458       decref (arg1);
459       decref (arg2);
460     }
461 }
462 
463 void
condop(cond)464 condop (cond)
465      int cond;
466 {
467   int regno = fetch ();
468   if (!regs[regno])
469     error ("register %c is empty", regno);
470   else if (stacktop < 1)
471     empty ();
472   else
473     {
474       decimal arg2 = stack[stacktop--];
475       decimal arg1 = stack[stacktop--];
476       int relation = decimal_compare (arg1, arg2);
477       decref (arg1);
478       decref (arg2);
479       if (cond == relation
480 	  || (cond < 0 && relation < 0)
481 	  || (cond > 0 && relation > 0))
482 	pushmacro (regs[regno]);
483     }
484 }
485 
486 /* Handle the command input source */
487 
488 /* Fetch the next command character from a macro or from the terminal */
489 
490 int
fetch()491 fetch()
492 {
493   int c = -1;
494 
495   while (macrolevel &&
496 		LENGTH (macrostack[macrolevel-1]) == macroindex[macrolevel-1])
497     popmacro();
498   if (macrolevel)
499     return macrostack[macrolevel - 1]->contents[macroindex[macrolevel-1]++];
500   while (1)
501     {
502       if (open_file)
503 	{
504 	  c = getc (open_file);
505 	  if (c >= 0) break;
506 	  fclose (open_file);
507 	  open_file = 0;
508 	}
509       else if (file_count)
510 	{
511 	  open_file = fopen (*next_file++, "r");
512 	  file_count--;
513 	  if (!open_file)
514 	    perror_with_name (*(next_file - 1));
515 	}
516       else break;
517     }
518   if (c >= 0) return c;
519   return getc (stdin);
520 }
521 
522 /* Unread character c on command input stream, whatever it is */
523 
524 void
unfetch(c)525 unfetch (c)
526      char c;
527 {
528   if (macrolevel)
529     macroindex[macrolevel-1]--;
530   else if (open_file)
531     ungetc (c, open_file);
532   else
533     ungetc (c, stdin);
534 }
535 
536 /* Begin execution of macro m.  */
537 
538 void
pushmacro(m)539 pushmacro (m)
540      decimal m;
541 {
542   while (macrolevel &&
543 		LENGTH (macrostack[macrolevel-1]) == macroindex[macrolevel-1])
544     popmacro();
545   if (m->after == STRING)
546     {
547       if (macrolevel == macrostacksize)
548 	{
549 	  macrostacksize *= 2;
550 	  macrostack = (decimal *) xrealloc (macrostack, macrostacksize * sizeof (decimal));
551 	  macroindex = (int *) xrealloc (macroindex, macrostacksize * sizeof (int));
552 	}
553       macroindex[macrolevel] = 0;
554       macrostack[macrolevel++] = m;
555       incref (m);
556     }
557   else
558     {   /* Number supplied as a macro!  */
559       push (m);   /* Its effect wouyld be to push the number.  */
560     }
561 }
562 
563 /* Pop a specified number of levels of macro execution.
564  The number of levels is specified by a decimal number d.  */
565 
566 void
popmacros(d)567 popmacros (d)
568      decimal d;
569 {
570   int num_pops = decimal_to_int (d);
571   int i;
572   for (i = 0; i < num_pops; i++)
573     popmacro ();
574 }
575 /* Exit one level of macro execution.  */
576 
577 void
popmacro()578 popmacro ()
579 {
580   if (!macrolevel)
581     exit (0);
582   else
583     {
584       decref (macrostack[--macrolevel]);
585     }
586 }
587 
588 void
push(d)589 push (d)
590      decimal d;
591 {
592   if (stacktop == stacksize - 1)
593     stack = (decimal *) xrealloc (stack, (stacksize *= 2) * sizeof (decimal));
594 
595     incref (d);
596 
597     stack[++stacktop] = d;
598 }
599 
600 /* Reference counting and storage freeing */
601 
602 void
decref(d)603 decref (d)
604      decimal d;
605 {
606   if (!--d->refcnt)
607     free (d);
608 }
609 
610 void
incref(d)611 incref (d)
612      decimal d;
613 {
614   d->refcnt++;
615 }
616 
empty()617 empty ()
618 {
619   error ("stack empty", 0);
620 }
621 
622 regstack
get_regstack()623 get_regstack ()
624 {
625   if (freeregstacks)
626     {
627       regstack r = freeregstacks;
628       freeregstacks = r ->rest;
629       return r;
630     }
631   else
632     return (regstack) xmalloc (sizeof (struct regstack));
633 }
634 
635 void
free_regstack(r)636 free_regstack (r)
637      regstack r;
638 {
639   r->rest = freeregstacks;
640   freeregstacks = r;
641 }
642 
643 void
pushreg(c)644 pushreg (c)
645      char c;
646 {
647   regstack r = get_regstack ();
648 
649   r->rest = regstacks[c];
650   r->value = regs[c];
651   regstacks[c] = r;
652   regs[c] = 0;
653 }
654 
655 /* Input of numbers and strings */
656 
657 /* Return a character read from the terminal.  */
658 
fgetchar()659 fgetchar ()
660 {
661   return getchar ();
662 }
663 
664 void
665 fputchar (c)
666      char (c);
667 {
668   putchar (c);
669 }
670 
671 /* Read text from command input source up to a close-bracket,
672    make a string out of it, and return it.
673    If STARTC is nonzero, then it and STOPC must balance when nested.  */
674 
675 decimal
read_string(stopc,inputfn,startc)676 read_string (stopc, inputfn, startc)
677      char stopc;
678      int (*inputfn) ();
679      int startc;
680 {
681   int c;
682   decimal result;
683   int i = 0;
684   int count = 0;
685 
686   while (1)
687     {
688       c = inputfn ();
689       if (c < 0 || (c == stopc && count == 0))
690 	{
691 	  if (count != 0)
692 	    error ("Unmatched `%c'", startc);
693 	  break;
694 	}
695       if (c == stopc)
696 	count--;
697       if (c == startc)
698 	count++;
699       if (i + 1 >= bufsize)
700 	buffer = (char *) xrealloc (buffer, bufsize *= 2);
701       buffer[i++] = c;
702     }
703   result = make_decimal (i, 0);
704   result->after = -1;	/* Mark it as a string */
705   result->before++;	/* but keep the length unchanged */
706   bcopy (buffer, result->contents, i);
707   return result;
708 }
709 
710 /* Read a number from the current input source */
711 
712 decimal
dec_read()713 dec_read ()
714 {
715   int c;
716   int i = 0;
717 
718   while (1)
719     {
720       c = fetch ();
721       if (! ((c >= '0' && c <= '9')
722 	     || (c >= 'A' && c <= 'F')
723 	     || c == '.'))
724         break;
725       if (i + 1 >= bufsize)
726 	buffer = (char *) xrealloc (buffer, bufsize *= 2);
727       buffer[i++] = c;
728     }
729   buffer[i++] = 0;
730   unfetch (c);
731 
732   return decimal_parse (buffer, ibase);
733 }
734 
735 /* Output of numbers and strings */
736 
737 /* Print the contents of obj, either numerically or as a string,
738  according to what obj says it is.  */
739 
740 void
print_obj(obj)741 print_obj (obj)
742      decimal obj;
743 {
744   if (obj->after == STRING)
745     print_string (obj);
746   else
747     decimal_print (obj, fputchar, obase);
748 }
749 
750 /* Print the contents of the decimal number `string', treated as a string.  */
751 
752 void
print_string(string)753 print_string (string)
754      decimal string;
755 {
756   char *p = string->contents;
757   int len = LENGTH (string);
758   int i;
759 
760   for (i = 0; i < len; i++)
761     {
762       putchar (*p++);
763     }
764 }
765 
766 /* Set the input radix from the value of the decimal number d, if valid.  */
767 
768 void
setibase(d)769 setibase (d)
770      decimal d;
771 {
772   int value = decimal_to_int (d);
773   if (value < 2 || value > 36)
774     error ("input radix must be from 2 to 36", 0);
775   else
776     ibase = value;
777 }
778 
779 /* Set the output radix from the value of the decimal number d, if valid.  */
780 
781 void
setobase(d)782 setobase (d)
783      decimal d;
784 {
785   int value = decimal_to_int (d);
786   if (value < 2 || value > 36)
787     error ("output radix must be from 2 to 36", 0);
788   else
789     obase = value;
790 }
791 
792 /* Set the precision for mul and div from the value of the decimal number d, if valid.  */
793 
794 void
setprecision(d)795 setprecision (d)
796      decimal d;
797 {
798   int value = decimal_to_int (d);
799   if (value < 0 || value > 30000)
800     error ("precision must be nonnegative and < 30000", 0);
801   else
802     precision = value;
803 }
804 
805 /* Push the number of digits in decimal number d, as a decimal number.  */
806 
807 void
pushlength(d)808 pushlength (d)
809      decimal d;
810 {
811   push (decimal_from_int (LENGTH (d)));
812 }
813 
814 /* Push the number of fraction digits in d.  */
815 
816 void
pushscale(d)817 pushscale (d)
818      decimal d;
819 {
820   push (decimal_from_int (d->after));
821 }
822 
823 /* Push the square root of decimal number d.  */
824 
825 void
pushsqrt(d)826 pushsqrt (d)
827      decimal d;
828 {
829   push (decimal_sqrt (d, precision));
830 }
831 
832 /* Print error message and exit.  */
833 
fatal(s1,s2)834 fatal (s1, s2)
835      char *s1, *s2;
836 {
837   error (s1, s2);
838   exit (1);
839 }
840 
841 /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
842 
error(s1,s2)843 error (s1, s2)
844      char *s1, *s2;
845 {
846   printf ("dc: ");
847   printf (s1, s2);
848   printf ("\n");
849 }
850 
decimal_error(s1,s2)851 decimal_error (s1, s2)
852      char *s1, *s2;
853 {
854   error (s1, s2);
855 }
856 
perror_with_name(name)857 perror_with_name (name)
858      char *name;
859 {
860   extern int errno, sys_nerr;
861   extern char *sys_errlist[];
862   char *s;
863 
864   if (errno < sys_nerr)
865     s = concat ("", sys_errlist[errno], " for %s");
866   else
867     s = "cannot open %s";
868   error (s, name);
869 }
870 
871 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
872 
873 char *
concat(s1,s2,s3)874 concat (s1, s2, s3)
875      char *s1, *s2, *s3;
876 {
877   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
878   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
879 
880   strcpy (result, s1);
881   strcpy (result + len1, s2);
882   strcpy (result + len1 + len2, s3);
883   *(result + len1 + len2 + len3) = 0;
884 
885   return result;
886 }
887 
888 /* Like malloc but get fatal error if memory is exhausted.  */
889 
890 int
xmalloc(size)891 xmalloc (size)
892      int size;
893 {
894   int result = malloc (size);
895   if (!result)
896     fatal ("virtual memory exhausted", 0);
897   return result;
898 }
899 
900 int
xrealloc(ptr,size)901 xrealloc (ptr, size)
902      char *ptr;
903      int size;
904 {
905   int result = realloc (ptr, size);
906   if (!result)
907     fatal ("virtual memory exhausted");
908   return result;
909 }
910