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

Click here to go back to the directory listing.
Click here to download this file.
last modified: 1997-04-10 21:37:50