xref: /386bsd/usr/src/usr.bin/bc/util.c (revision a2142627)
1 /* util.c: Utility routines for bc. */
2 
3 /*  This file is part of bc written for MINIX.
4     Copyright (C) 1991, 1992 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 of the License , or
9     (at your option) 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; see the file COPYING.  If not, write to
18     the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20     You may contact the author by:
21        e-mail:  phil@cs.wwu.edu
22       us-mail:  Philip A. Nelson
23                 Computer Science Department, 9062
24                 Western Washington University
25                 Bellingham, WA 98226-9062
26 
27 *************************************************************************/
28 
29 
30 #include "bcdefs.h"
31 #ifndef VARARGS
32 #include <stdarg.h>
33 #else
34 #include <varargs.h>
35 #endif
36 #include "global.h"
37 #include "proto.h"
38 
39 
40 /* strcopyof mallocs new memory and copies a string to to the new
41    memory. */
42 
43 char *
strcopyof(str)44 strcopyof (str)
45      char *str;
46 {
47   char *temp;
48 
49   temp = (char *) bc_malloc (strlen (str)+1);
50   return (strcpy (temp,str));
51 }
52 
53 
54 /* nextarg adds another value to the list of arguments. */
55 
56 arg_list *
nextarg(args,val)57 nextarg (args, val)
58      arg_list *args;
59      char val;
60 { arg_list *temp;
61 
62   temp = (arg_list *) bc_malloc (sizeof (arg_list));
63   temp->av_name = val;
64   temp->next = args;
65 
66   return (temp);
67 }
68 
69 
70 /* For generate, we must produce a string in the form
71     "val,val,...,val".  We also need a couple of static variables
72    for retaining old generated strings.  It also uses a recursive
73    function that builds the string. */
74 
75 static char *arglist1 = NULL, *arglist2 = NULL;
76 
77 
78 /* make_arg_str does the actual construction of the argument string.
79    ARGS is the pointer to the list and LEN is the maximum number of
80    characters needed.  1 char is the minimum needed. COMMAS tells
81    if each number should be seperated by commas.*/
82 
83 _PROTOTYPE (static char *make_arg_str, (arg_list *args, int len, int commas));
84 
85 static char *
make_arg_str(args,len,commas)86 make_arg_str (args, len, commas)
87       arg_list *args;
88       int len;
89       int commas;
90 {
91   char *temp;
92   char sval[20];
93 
94   /* Recursive call. */
95   if (args != NULL)
96     temp = make_arg_str (args->next, len+11, commas);
97   else
98     {
99       temp = (char *) bc_malloc (len);
100       *temp = 0;
101       return temp;
102     }
103 
104   /* Add the current number to the end of the string. */
105   if (len != 1 && commas)
106     sprintf (sval, "%d,", args->av_name);
107   else
108     sprintf (sval, "%d", args->av_name);
109   temp = strcat (temp, sval);
110   return (temp);
111 }
112 
113 char *
arg_str(args,commas)114 arg_str (args, commas)
115      arg_list *args;
116      int commas;
117 {
118   if (arglist2 != NULL)
119     free (arglist2);
120   arglist2 = arglist1;
121   arglist1 = make_arg_str (args, 1, commas);
122   return (arglist1);
123 }
124 
125 
126 /* free_args frees an argument list ARGS. */
127 
128 void
free_args(args)129 free_args (args)
130       arg_list *args;
131 {
132   arg_list *temp;
133 
134   temp = args;
135   while (temp != NULL)
136     {
137       args = args->next;
138       free (temp);
139       temp = args;
140     }
141 }
142 
143 
144 /* Check for valid parameter (PARAMS) and auto (AUTOS) lists.
145    There must be no duplicates any where.  Also, this is where
146    warnings are generated for array parameters. */
147 
148 void
check_params(params,autos)149 check_params ( params, autos )
150      arg_list *params, *autos;
151 {
152   arg_list *tmp1, *tmp2;
153 
154   /* Check for duplicate parameters. */
155   if (params != NULL)
156     {
157       tmp1 = params;
158       while (tmp1 != NULL)
159 	{
160 	  tmp2 = tmp1->next;
161 	  while (tmp2 != NULL)
162 	    {
163 	      if (tmp2->av_name == tmp1->av_name)
164 		yyerror ("duplicate parameter names");
165 	      tmp2 = tmp2->next;
166 	    }
167 	  if (tmp1->av_name < 0)
168 	    warn ("Array parameter");
169 	  tmp1 = tmp1->next;
170 	}
171     }
172 
173   /* Check for duplicate autos. */
174   if (autos != NULL)
175     {
176       tmp1 = autos;
177       while (tmp1 != NULL)
178 	{
179 	  tmp2 = tmp1->next;
180 	  while (tmp2 != NULL)
181 	    {
182 	      if (tmp2->av_name == tmp1->av_name)
183 		yyerror ("duplicate auto variable names");
184 	      tmp2 = tmp2->next;
185 	    }
186 	  tmp1 = tmp1->next;
187 	}
188     }
189 
190   /* Check for duplicate between parameters and autos. */
191   if ((params != NULL) && (autos != NULL))
192     {
193       tmp1 = params;
194       while (tmp1 != NULL)
195 	{
196 	  tmp2 = autos;
197 	  while (tmp2 != NULL)
198 	    {
199 	      if (tmp2->av_name == tmp1->av_name)
200 		yyerror ("variable in both parameter and auto lists");
201 	      tmp2 = tmp2->next;
202 	    }
203 	  tmp1 = tmp1->next;
204 	}
205     }
206 }
207 
208 
209 /* Initialize the code generator the parser. */
210 
211 void
init_gen()212 init_gen ()
213 {
214   /* Get things ready. */
215   break_label = 0;
216   continue_label = 0;
217   next_label  = 1;
218   out_count = 2;
219   if (compile_only)
220     printf ("@i");
221   else
222     init_load ();
223   had_error = FALSE;
224   did_gen = FALSE;
225 }
226 
227 
228 /* generate code STR for the machine. */
229 
230 void
generate(str)231 generate (str)
232       char *str;
233 {
234   did_gen = TRUE;
235   if (compile_only)
236     {
237       printf ("%s",str);
238       out_count += strlen(str);
239       if (out_count > 60)
240 	{
241 	  printf ("\n");
242 	  out_count = 0;
243 	}
244     }
245   else
246     load_code (str);
247 }
248 
249 
250 /* Execute the current code as loaded. */
251 
252 void
run_code()253 run_code()
254 {
255   /* If no compile errors run the current code. */
256   if (!had_error && did_gen)
257     {
258       if (compile_only)
259 	{
260 	  printf ("@r\n");
261 	  out_count = 0;
262 	}
263       else
264 	execute ();
265     }
266 
267   /* Reinitialize the code generation and machine. */
268   if (did_gen)
269     init_gen();
270   else
271     had_error = FALSE;
272 }
273 
274 
275 /* Output routines: Write a character CH to the standard output.
276    It keeps track of the number of characters output and may
277    break the output with a "\<cr>". */
278 
279 void
out_char(ch)280 out_char (ch)
281      char ch;
282 {
283   if (ch == '\n')
284     {
285       out_col = 0;
286       putchar ('\n');
287     }
288   else
289     {
290       out_col++;
291       if (out_col == 70)
292 	{
293 	  putchar ('\\');
294 	  putchar ('\n');
295 	  out_col = 1;
296 	}
297       putchar (ch);
298     }
299 }
300 
301 
302 /* The following are "Symbol Table" routines for the parser. */
303 
304 /*  find_id returns a pointer to node in TREE that has the correct
305     ID.  If there is no node in TREE with ID, NULL is returned. */
306 
307 id_rec *
find_id(tree,id)308 find_id (tree, id)
309      id_rec *tree;
310      char   *id;
311 {
312   int cmp_result;
313 
314   /* Check for an empty tree. */
315   if (tree == NULL)
316     return NULL;
317 
318   /* Recursively search the tree. */
319   cmp_result = strcmp (id, tree->id);
320   if (cmp_result == 0)
321     return tree;  /* This is the item. */
322   else if (cmp_result < 0)
323     return find_id (tree->left, id);
324   else
325     return find_id (tree->right, id);
326 }
327 
328 
329 /* insert_id_rec inserts a NEW_ID rec into the tree whose ROOT is
330    provided.  insert_id_rec returns TRUE if the tree height from
331    ROOT down is increased otherwise it returns FALSE.  This is a
332    recursive balanced binary tree insertion algorithm. */
333 
insert_id_rec(root,new_id)334 int insert_id_rec (root, new_id)
335      id_rec **root;
336      id_rec *new_id;
337 {
338   id_rec *A, *B;
339 
340   /* If root is NULL, this where it is to be inserted. */
341   if (*root == NULL)
342     {
343       *root = new_id;
344       new_id->left = NULL;
345       new_id->right = NULL;
346       new_id->balance = 0;
347       return (TRUE);
348     }
349 
350   /* We need to search for a leaf. */
351   if (strcmp (new_id->id, (*root)->id) < 0)
352     {
353       /* Insert it on the left. */
354       if (insert_id_rec (&((*root)->left), new_id))
355 	{
356 	  /* The height increased. */
357 	  (*root)->balance --;
358 
359       switch ((*root)->balance)
360 	{
361 	case  0:  /* no height increase. */
362 	  return (FALSE);
363 	case -1:  /* height increase. */
364 	  return (FALSE);
365 	case -2:  /* we need to do a rebalancing act. */
366 	  A = *root;
367 	  B = (*root)->left;
368 	  if (B->balance <= 0)
369 	    {
370 	      /* Single Rotate. */
371 	      A->left = B->right;
372 	      B->right = A;
373 	      *root = B;
374 	      A->balance = 0;
375 	      B->balance = 0;
376 	    }
377 	  else
378 	    {
379 	      /* Double Rotate. */
380 	      *root = B->right;
381 	      B->right = (*root)->left;
382 	      A->left = (*root)->right;
383 	      (*root)->left = B;
384 	      (*root)->right = A;
385 	      switch ((*root)->balance)
386 		{
387 		case -1:
388 		  A->balance = 1;
389 		  B->balance = 0;
390 		  break;
391 		case  0:
392 		  A->balance = 0;
393 		  B->balance = 0;
394 		  break;
395 		case  1:
396 		  A->balance = 0;
397 		  B->balance = -1;
398 		  break;
399 		}
400 	      (*root)->balance = 0;
401 	    }
402 	}
403 	}
404     }
405   else
406     {
407       /* Insert it on the right. */
408       if (insert_id_rec (&((*root)->right), new_id))
409 	{
410 	  /* The height increased. */
411 	  (*root)->balance ++;
412 	  switch ((*root)->balance)
413 	    {
414 	    case 0:  /* no height increase. */
415 	      return (FALSE);
416 	    case 1:  /* height increase. */
417 	      return (FALSE);
418 	    case 2:  /* we need to do a rebalancing act. */
419 	      A = *root;
420 	      B = (*root)->right;
421 	      if (B->balance >= 0)
422 		{
423 		  /* Single Rotate. */
424 		  A->right = B->left;
425 		  B->left = A;
426 		  *root = B;
427 		  A->balance = 0;
428 		  B->balance = 0;
429 		}
430 	      else
431 		{
432 		  /* Double Rotate. */
433 		  *root = B->left;
434 		  B->left = (*root)->right;
435 		  A->right = (*root)->left;
436 		  (*root)->left = A;
437 		  (*root)->right = B;
438 		  switch ((*root)->balance)
439 		    {
440 		    case -1:
441 		      A->balance = 0;
442 		      B->balance = 1;
443 		      break;
444 		    case  0:
445 		      A->balance = 0;
446 		      B->balance = 0;
447 		      break;
448 		    case  1:
449 		      A->balance = -1;
450 		      B->balance = 0;
451 		      break;
452 		    }
453 		  (*root)->balance = 0;
454 		}
455 	    }
456 	}
457     }
458 
459   /* If we fall through to here, the tree did not grow in height. */
460   return (FALSE);
461 }
462 
463 
464 /* Initialize variables for the symbol table tree. */
465 
466 void
init_tree()467 init_tree()
468 {
469   name_tree  = NULL;
470   next_array = 1;
471   next_func  = 1;
472   next_var   = 4;  /* 0 => ibase, 1 => obase, 2 => scale, 3 => last. */
473 }
474 
475 
476 /* Lookup routines for symbol table names. */
477 
478 int
lookup(name,namekind)479 lookup (name, namekind)
480      char *name;
481      int  namekind;
482 {
483   id_rec *id;
484 
485   /* Warn about non-standard name. */
486   if (strlen(name) != 1)
487     warn ("multiple letter name - %s", name);
488 
489   /* Look for the id. */
490   id = find_id (name_tree, name);
491   if (id == NULL)
492     {
493       /* We need to make a new item. */
494       id = (id_rec *) bc_malloc (sizeof (id_rec));
495       id->id = strcopyof (name);
496       id->a_name = 0;
497       id->f_name = 0;
498       id->v_name = 0;
499       insert_id_rec (&name_tree, id);
500     }
501 
502   /* Return the correct value. */
503   switch (namekind)
504     {
505 
506     case ARRAY:
507       /* ARRAY variable numbers are returned as negative numbers. */
508       if (id->a_name != 0)
509 	{
510 	  free (name);
511 	  return (-id->a_name);
512 	}
513       id->a_name = next_array++;
514       a_names[id->a_name] = name;
515       if (id->a_name < MAX_STORE)
516 	{
517 	  if (id->a_name >= a_count)
518 	    more_arrays ();
519 	  return (-id->a_name);
520 	}
521       yyerror ("Too many array variables");
522       exit (1);
523 
524     case FUNCT:
525       if (id->f_name != 0)
526 	{
527 	  free(name);
528 	  return (id->f_name);
529 	}
530       id->f_name = next_func++;
531       f_names[id->f_name] = name;
532       if (id->f_name < MAX_STORE)
533 	{
534 	  if (id->f_name >= f_count)
535 	    more_functions ();
536 	  return (id->f_name);
537 	}
538       yyerror ("Too many functions");
539       exit (1);
540 
541     case SIMPLE:
542       if (id->v_name != 0)
543 	{
544 	  free(name);
545 	  return (id->v_name);
546 	}
547       id->v_name = next_var++;
548       v_names[id->v_name - 1] = name;
549       if (id->v_name <= MAX_STORE)
550 	{
551 	  if (id->v_name >= v_count)
552 	    more_variables ();
553 	  return (id->v_name);
554 	}
555       yyerror ("Too many variables");
556       exit (1);
557     }
558 }
559 
560 
561 /* Print the welcome banner. */
562 
563 void
welcome()564 welcome()
565 {
566   printf ("This is free software with ABSOLUTELY NO WARRANTY.\n");
567   printf ("For details type `warranty'. \n");
568 }
569 
570 
571 /* Print out the warranty information. */
572 
573 void
warranty(prefix)574 warranty(prefix)
575      char *prefix;
576 {
577   printf ("\n%s%s\n\n", prefix, BC_VERSION);
578   printf ("%s%s%s%s%s%s%s%s%s%s%s",
579 "    This program is free software; you can redistribute it and/or modify\n",
580 "    it under the terms of the GNU General Public License as published by\n",
581 "    the Free Software Foundation; either version 2 of the License , or\n",
582 "    (at your option) any later version.\n\n",
583 "    This program is distributed in the hope that it will be useful,\n",
584 "    but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
585 "    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n",
586 "    GNU General Public License for more details.\n\n",
587 "    You should have received a copy of the GNU General Public License\n",
588 "    along with this program. If not, write to the Free Software\n",
589 "    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
590 }
591 
592 /* Print out the limits of this program. */
593 
594 void
limits()595 limits()
596 {
597   printf ("BC_BASE_MAX     = %d\n",  BC_BASE_MAX);
598   printf ("BC_DIM_MAX      = %ld\n", (long) BC_DIM_MAX);
599   printf ("BC_SCALE_MAX    = %d\n",  BC_SCALE_MAX);
600   printf ("BC_STRING_MAX   = %d\n",  BC_STRING_MAX);
601   printf ("MAX Exponent    = %ld\n", (long) LONG_MAX);
602   printf ("MAX code        = %ld\n", (long) BC_MAX_SEGS * (long) BC_SEG_SIZE);
603   printf ("multiply digits = %ld\n", (long) LONG_MAX / (long) 90);
604   printf ("Number of vars  = %ld\n", (long) MAX_STORE);
605 #ifdef OLD_EQ_OP
606   printf ("Old assignment operatiors are valid. (=-, =+, ...)\n");
607 #endif
608 }
609 
610 /* bc_malloc will check the return value so all other places do not
611    have to do it!  SIZE is the number of types to allocate. */
612 
613 char *
bc_malloc(size)614 bc_malloc (size)
615      int size;
616 {
617   char *ptr;
618 
619   ptr = (char *) malloc (size);
620   if (ptr == NULL)
621     out_of_memory ();
622 
623   return ptr;
624 }
625 
626 
627 /* The following routines are error routines for various problems. */
628 
629 /* Malloc could not get enought memory. */
630 
631 void
out_of_memory()632 out_of_memory()
633 {
634   fprintf (stderr, "Fatal error: Out of memory for malloc.\n");
635   exit (1);
636 }
637 
638 
639 
640 /* The standard yyerror routine.  Built with variable number of argumnets. */
641 
642 #ifndef VARARGS
643 #ifdef __STDC__
644 void
yyerror(char * str,...)645 yyerror (char *str, ...)
646 #else
647 void
648 yyerror (str)
649      char *str;
650 #endif
651 #else
652 void
653 yyerror (str, va_alist)
654      char *str;
655 #endif
656 {
657   char *name;
658   va_list args;
659 
660 #ifndef VARARGS
661    va_start (args, str);
662 #else
663    va_start (args);
664 #endif
665   if (is_std_in)
666     name = "(standard_in)";
667   else
668     name = g_argv[optind-1];
669   fprintf (stderr,"%s %d: ",name,line_no);
670   vfprintf (stderr, str, args);
671   fprintf (stderr, "\n");
672   had_error = TRUE;
673   va_end (args);
674 }
675 
676 
677 /* The routine to produce warnings about non-standard features
678    found during parsing. */
679 
680 #ifndef VARARGS
681 #ifdef __STDC__
682 void
warn(char * mesg,...)683 warn (char *mesg, ...)
684 #else
685 void
686 warn (mesg)
687      char *mesg;
688 #endif
689 #else
690 void
691 warn (mesg, va_alist)
692      char *mesg;
693 #endif
694 {
695   char *name;
696   va_list args;
697 
698 #ifndef VARARGS
699   va_start (args, mesg);
700 #else
701   va_start (args);
702 #endif
703   if (std_only)
704     {
705       if (is_std_in)
706 	name = "(standard_in)";
707       else
708 	name = g_argv[optind-1];
709       fprintf (stderr,"%s %d: ",name,line_no);
710       vfprintf (stderr, mesg, args);
711       fprintf (stderr, "\n");
712       had_error = TRUE;
713     }
714   else
715     if (warn_not_std)
716       {
717 	if (is_std_in)
718 	  name = "(standard_in)";
719 	else
720 	  name = g_argv[optind-1];
721 	fprintf (stderr,"%s %d: (Warning) ",name,line_no);
722 	vfprintf (stderr, mesg, args);
723 	fprintf (stderr, "\n");
724       }
725   va_end (args);
726 }
727 
728 /* Runtime error will  print a message and stop the machine. */
729 
730 #ifndef VARARGS
731 #ifdef __STDC__
732 void
rt_error(char * mesg,...)733 rt_error (char *mesg, ...)
734 #else
735 void
736 rt_error (mesg)
737      char *mesg;
738 #endif
739 #else
740 void
741 rt_error (mesg, va_alist)
742      char *mesg;
743 #endif
744 {
745   va_list args;
746   char error_mesg [255];
747 
748 #ifndef VARARGS
749   va_start (args, mesg);
750 #else
751   va_start (args);
752 #endif
753   vsprintf (error_mesg, mesg, args);
754   va_end (args);
755 
756   fprintf (stderr, "Runtime error (func=%s, adr=%d): %s\n",
757 	   f_names[pc.pc_func], pc.pc_addr, error_mesg);
758   runtime_error = TRUE;
759 }
760 
761 
762 /* A runtime warning tells of some action taken by the processor that
763    may change the program execution but was not enough of a problem
764    to stop the execution. */
765 
766 #ifndef VARARGS
767 #ifdef __STDC__
768 void
rt_warn(char * mesg,...)769 rt_warn (char *mesg, ...)
770 #else
771 void
772 rt_warn (mesg)
773      char *mesg;
774 #endif
775 #else
776 void
777 rt_warn (mesg, va_alist)
778      char *mesg;
779 #endif
780 {
781   va_list args;
782   char error_mesg [255];
783 
784 #ifndef VARARGS
785   va_start (args, mesg);
786 #else
787   va_start (args);
788 #endif
789   vsprintf (error_mesg, mesg, args);
790   va_end (args);
791 
792   fprintf (stderr, "Runtime warning (func=%s, adr=%d): %s\n",
793 	   f_names[pc.pc_func], pc.pc_addr, error_mesg);
794 }
795