home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / bc-1.03-src.tgz / tar.out / fsf / bc / util.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  17KB  |  819 lines

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