Main Page | Class Hierarchy | Alphabetical List | Compound List | File List | Compound Members | File Members

md_preprocessor.c

Go to the documentation of this file.
00001 /*****************************************************************************\
00002  *  Copyright  1994  The Board of Trustees of the University of Illinois.
00003  *  All rights reserved.
00004  *
00005  *  md_preprocessor.c, part of the preprocessor for the IMPACT 
00006  *  Meta-Description Language 
00007  *  John C. Gyllenhaal, Wen-mei Hwu, and The IMPACT Research Group
00008  *  Coordinated Science Laboratory
00009  *  University of Illinois at Urbana-Champaign
00010  *
00011  *  The IMPACT Research Group may be contacted at impact@crhc.uiuc.edu.
00012  *
00013  *  md_preprocessor.c (part of the preprocessor for the IMPACT 
00014  *  Meta-Description Language software), including both binary and source 
00015  *  (hereafter, Software) is copyrighted by The Board of Trustees of 
00016  *  the University of Illinois (UI), and ownership remains with the UI.
00017  *
00018  *  The UI grants you (hereafter, Licensee) a license to use the Software for
00019  *  academic, research and internal business purposes only, without a fee.
00020  *  Licensee may distribute the Software to third parties provided that the
00021  *  copyright notice and this statement appears on all copies and that no
00022  *  charge is associated with such copies.
00023  *
00024  *  Licensee may make derivative works.  However, if Licensee distributes
00025  *  any derivative work based on or derived from the Software, then Licensee
00026  *  will clearly notify users that such derivative work is a modified
00027  *  version and not the original Software distributed by the UI.
00028  *
00029  *  Any Licensee wishing to make commercial use of the Software should contact
00030  *  the UI, c/o the Research and Technology Management Office [rtmo@uiuc.edu],
00031  *  to negotiate an appropriate license for such commercial use.  Commercial
00032  *  use includes (1) integration of all or part of the source code into a
00033  *  product for sale or license by or on behalf of Licensee to third parties,
00034  *  or (2) distribution of the Software to third parties that need it to
00035  *  utilize a commercial product sold or licensed by or on behalf of Licensee.
00036  *
00037  *  UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
00038  *  ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
00039  *  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE USERS OF THIS
00040  *  SOFTWARE.
00041  *
00042  *  By using or copying this Software, Licensee agrees to abide by the
00043  *  copyright law and all other applicable laws of the U.S. including, but
00044  *  not limited to, export control laws, and the terms of this license.
00045  *  UI shall have the right to terminate this license immediately by written
00046  *  notice upon Licensee's breach of, or non-compliance with, any of its terms.
00047  *  Licensee may be held legally responsible for any copyright infringement
00048  *  that is caused or encouraged by Licensee's failure to abide by the terms
00049  *  of this license.
00050  *
00051  *  Form approved by University Counsel, M.A.R., 05/10/93
00052 \*****************************************************************************/
00053 /*****************************************************************************\
00054  *      File:   md_preprocessor.c
00055  * 
00056  *      Description: The preprocessor for the IMPACT Meta-Description Language
00057  * 
00058  *      Creation Date:  October 1994
00059  * 
00060  *      Authors: John C. Gyllenhaal and Wen-mei Hwu
00061  *
00062  *      Revisions:
00063  *          John C. Gyllenhaal August 1995
00064  *          Enhanced expression evaluation routines
00065  *  
00066  *          John C. Gyllenhaal November 1996
00067  *          Modified $def semantics so that braces are not allowed in
00068  *          the '$def def_name def_value' form.  This allows a more helpful
00069  *          error message to be given for the subtly buggy text below:
00070  *             $if (1 == 1) {$def foo 1}
00071  *             $else {$def foo 2}
00072  *          The problem with this code is that foo is defined to '1}', so
00073  *          the $else inadvertently appears in the body of the $if.
00074  *
00075  *          John C. Gyllenhaal January 1997
00076  *          1) Added support to allow recursive text replacement.  
00077  *             For example, ${array_${index}} is now supported.
00078  * 
00079  *          2) Added new $for form which allows multiple value lists to be
00080  *             be specified.  The lists must be the same length and they
00081  *             are stepped through in parallel.
00082  *             For example: "$for ((I in 1 2)(J in 3 4)) {${I} ${J} }"
00083  *             Yields: "1 3 2 4 "
00084  *                          
00085  *          3) Added support for floating-point expression evaluation.
00086  *             Form: $.={floating-point expression}
00087  * 
00088  *          4) Now, by default, implicit text replacement does not occur.
00089  *             For example: "$def I {10}  I = ${I}"
00090  *             Now yields: "  I = 10" instead of "  10 = 10"
00091  * 
00092  *          5) Enhanced $def and $for to allow user to enable implicit text
00093  *             replacement by placing a '!' in front of the def_name(s).
00094  *             For example: "$def !I {10}  I = ${I}"
00095  *             Now yields: "  10 = 10"
00096  *      
00097  *          Many thanks to Shail Aditya and Bruce Childers for their 
00098  *          suggestions which led to the above enhancements!
00099  *  
00100  *  
00101 \*****************************************************************************/
00102 
00103 #ifndef lint
00104 #define lint
00105 static char copyright[] =
00106 "@(#) Copyright (c) 1994 The Board of Trustees of the University of Illinois.\
00107 \nAll rights reserved.\n";
00108 #endif
00109 
00110 #include "md_preprocessor.h"
00111 
00112 /* Write to out in many places */
00113 FILE *out;
00114 
00115 
00116 Mfile   *current_file;
00117 
00118 /* Symbol tables */
00119 Psymbol_Table *Pdef_table = NULL;
00120 Psymbol_Table *Mfile_table = NULL;
00121 
00122 /* Allow disabling of text replacement during processing of text replacement
00123  * directives.  
00124  */
00125 int allow_text_replacement = 1;
00126 
00127 /* Setting to a non-zero value disables implicit text replacement.
00128  * This may be done during the processing of a text replacement directive
00129  * or by a user directive (soon to be implemented).
00130  *
00131  * This variable will be incremented to disable implict text replacement
00132  * and decremented to possibly turn it back on (allows multiple nested
00133  * sets and resets (only the last one should really take effect)
00134  * to be properly modeled).
00135  */
00136 int disable_implicit_text_replacement = 0;
00137 
00138 /* A temp buffer for the preprocessor's use */
00139 Mbuf *temp_mbuf = NULL;
00140 Mbuf *pptr_mbuf = NULL;
00141 
00142 /* Placemarks for preprocessor's use */
00143 Pptr *temp_placemark = NULL;
00144 Mptr *expand_placemark = NULL;
00145 
00146 /* Alloc pools */
00147 L_Alloc_Pool *Pptr_pool = NULL;
00148 L_Alloc_Pool *Pdef_pool = NULL;
00149 L_Alloc_Pool *String_Node_pool = NULL;
00150 L_Alloc_Pool *Value_List_pool = NULL;
00151 
00152 char *program_name = NULL;
00153 
00154 /* Command line parameters */
00155 char *input_file_name = NULL;
00156 char *output_file_name = NULL;
00157 int using_stdin = 0;
00158 int print_line_directives = 1;
00159 int print_alloc_usage = 0;
00160 
00161 
00162 void print_usage (char *fmt, ...)
00163 {
00164     va_list     args;
00165 
00166     /* Print out error message if fmt is not NULL*/
00167     if (fmt != NULL)
00168     {
00169         fprintf (stderr, "\nError: ");
00170         va_start (args, fmt);
00171         vfprintf (stderr, fmt, args);
00172         va_end (args);
00173         fprintf (stderr, "\n");
00174     }
00175 
00176     fprintf (stderr, "\n");
00177     fprintf(stderr, "usage: %s [options] [input_file]\n\n",
00178             program_name);
00179     fprintf (stderr, "options:\n");
00180     fprintf (stderr, "  -o output_file    outputs to output_file instead of to standard output.\n");
00181     fprintf (stderr, "  -stdin            reads input from standard input instead of input_file.\n");
00182     fprintf (stderr, "  -no_directives    suppresses output of line directives.\n");
00183     fprintf (stderr, "\n");
00184     fprintf (stderr, "  -Ddef_name=value  overrides $def directives in input (until hit $undef.).\n");
00185     fprintf (stderr, "\n");
00186     fprintf (stderr, "debug options:\n");
00187     fprintf (stderr, "  -bypass_alloc     use malloc instead of alloc routines.\n");
00188     fprintf (stderr, "  -print_usage      prints alloc routine stats.\n");
00189     fprintf (stderr, "\n");
00190     exit (1);
00191 }
00192 
00193 void read_command_line_parameters (int argc, char **arg_ptr)
00194 {
00195     char *name, *value;
00196 
00197     /* Get program name for error messages */
00198     program_name = strdup (*arg_ptr);
00199 
00200     /* Start parsing arguments at first argument */
00201     arg_ptr++;
00202     for (;*arg_ptr != NULL; arg_ptr++)
00203     {
00204         /* If first character is a '-', must be an option */
00205         if ((*arg_ptr)[0] == '-')
00206         {
00207             /* Get output file */
00208             if (strcmp (*arg_ptr, "-o") == 0)
00209             {
00210                 /* Make sure -o not specified twice */
00211                 if (output_file_name != NULL)
00212                 {
00213                     print_usage("-o option specified twice.");
00214                 }
00215 
00216                 /* Get output file name, expect it to be non-NULL */
00217                 arg_ptr++;
00218                 if (*arg_ptr == NULL)
00219                 {
00220                     print_usage("expected '-o output_file'.\n");
00221                 }
00222                 output_file_name = strdup (*arg_ptr);
00223             }
00224             /* Get -Ddef_name=value directives */
00225             else if ((*arg_ptr)[1] == 'D')
00226             {
00227                 /* Malloc copy and parse into name and value */
00228                 name = strdup (&(*arg_ptr)[2]);
00229 
00230                 /* Make sure first character is a valid */
00231                 if ((name[0] != '_') && !isalpha (name[0]))
00232                 {
00233                     print_usage ("invalid def_name in '%s'.\n",
00234                                  *arg_ptr);
00235                 }
00236 
00237                 /* Find equals */
00238                 for (value = name; *value != 0; value ++)
00239                 {
00240                     /* Stop when hit equals */
00241                     if (*value == '=')
00242                     {
00243                         break;
00244                     }
00245 
00246                     /* Make sure name before it is a valid identifier */
00247                     if ((*value != '_') && !isalnum (*value))
00248                     {
00249                         print_usage ("invalid def_name in '%s'.\n",
00250                                      *arg_ptr);
00251                     }
00252                 }
00253                 /* If equals doesn't exist, set value to "" */
00254                 if (*value == 0)
00255                 {
00256                     value = "";
00257                 }
00258                 else
00259                 {
00260                     /* Terminate name at equals */
00261                     *value = 0;
00262 
00263                     /* Set value string to rest of argument */
00264                     value++;
00265                 }
00266 
00267                 /* Command line definitions are at level 2 and 
00268                  * do not allow implicit text replacement.
00269                  */
00270                 add_Pdef (name, value, 0, 2);
00271 
00272                 /* Free malloced string */
00273                 free (name);
00274             }
00275 
00276             /* Get -stdin flag */
00277             else if (strcmp (*arg_ptr, "-stdin") == 0)
00278             {
00279                 using_stdin = 1;
00280             }
00281 
00282             /* Get -no_directives flag */
00283             else if (strcmp (*arg_ptr, "-no_directives") == 0)
00284             {
00285                 print_line_directives = 0;
00286             }
00287 
00288             /* Get -bypass_alloc flag */
00289             else if (strcmp (*arg_ptr, "-bypass_alloc") == 0)
00290             {
00291                 bypass_alloc_routines = 1;
00292             }
00293 
00294             /* Get -print_usage flag */
00295             else if (strcmp (*arg_ptr, "-print_usage") == 0)
00296             {
00297                 print_alloc_usage = 1;
00298             }
00299             else
00300             {
00301                 print_usage("unknown command line option '%s'.\n", *arg_ptr);
00302             }
00303         }
00304         
00305         /* Otherwise, must be file name (can only be one of command line) */
00306         else 
00307         {
00308             if (input_file_name != NULL)
00309             {
00310                 print_usage("only one input file may be specified.\n");
00311                         
00312             }
00313 
00314             /* Get input file name */
00315             input_file_name = strdup (*arg_ptr);
00316         }
00317     }
00318 
00319     /* Make sure input file has been specified */
00320     if ((input_file_name == NULL) && !using_stdin)
00321     {
00322         /* Print error only if they specified something */
00323         if (argc > 1)
00324         {
00325             print_usage("input_file or -stdin must be specified.\n");
00326         }
00327         else
00328         {
00329             print_usage(NULL);
00330         }
00331     }
00332 
00333     /* Make sure input_file and -stdin have not both been specified */
00334     if ((input_file_name != NULL) && using_stdin)
00335     {
00336         print_usage("may not specify both input_file and -stdin.\n");
00337     }
00338     
00339     /* Make sure input and output file are not the same */
00340     if ((input_file_name != NULL) && (output_file_name != NULL) &&
00341         (strcmp (input_file_name, output_file_name) == 0))
00342     {
00343         print_usage("may not specify input file (%s) that is the same as output file.",
00344                     input_file_name);
00345     }
00346 
00347 }
00348 
00349 
00350 /*
00351  * Overrides lcode library's version.
00352  */
00353 void L_punt (char *fmt, ...)
00354 {
00355     va_list     args;
00356 
00357     va_start (args, fmt);
00358     vfprintf (stderr, fmt, args);
00359     va_end(args);
00360     fprintf (stderr,"\n");
00361 
00362     exit (-1);
00363 }
00364 
00365 
00366 /*
00367  * Parse error handler.
00368  * First argument is a pptr that points to where the error occured.
00369  * The second argument is a printf format string, and the rest
00370  * are the arguments for the format string.
00371  */
00372 void Perror (Pptr *pptr, char *fmt, ...)
00373 {
00374     va_list     args;
00375     Mptr        *mptr;
00376     int         i;
00377     int         text_replaced;
00378     int         error_pos;
00379 
00380 
00381     /* Get mptr for ease of use */
00382     mptr = pptr->mptr;
00383 
00384     /* Backup one character if file (if possible) to point at
00385      * character or identifier just preprocessed.  This is vital
00386      * for errors that occur at end of a line, since mptr and
00387      * pptr point to different lines.
00388      *
00389      * Test to make sure have a least one character in file (Mbackupc will
00390      * punt if this is not the case).  (Do Mbackupc if at EOF or not
00391      * at the first character in the file.)
00392      */
00393     if ((mptr->mline == NULL) || (mptr->pos != 0) || 
00394         (mptr->mline->line_no != 0))
00395         Mbackupc (mptr);
00396 
00397     /* 
00398      * Print out text line of text where error occured and put arrow to
00399      * location.  (pos starts at 0, so add 1 to get to column, but
00400      * subtract one since Pgetc reads one or more characters ahead)
00401      */
00402     fprintf (stderr, "\n");
00403     /* If not at EOF */
00404     if (mptr->mline != NULL)
00405     {
00406         fprintf (stderr, 
00407                  "Error during preprocessing (line %i char %i of %s):\n",
00408                  mptr->mline->line_no, 
00409                  mptr->pos+1, mptr->mfile->name);
00410     }
00411 
00412     /* otherwise, at EOF */
00413     else
00414     {
00415         fprintf (stderr, 
00416                  "Error during preprocessing (%s at EOF):\n",
00417                  mptr->mfile->name);
00418 
00419     }
00420 
00421     /*Print error message */
00422     va_start (args, fmt);
00423     vfprintf (stderr, fmt, args);
00424     va_end(args);
00425     fprintf (stderr,"\n");
00426     fprintf (stderr, "\n");
00427 
00428 
00429     /* If at EOF, do not print out file text */
00430     if (mptr->mline != NULL)
00431     {
00432         /* Detect text replacement, assume none */
00433         text_replaced = 0;
00434         for (i=0; i <pptr->expanded->cur_len; i++)
00435         {
00436             if (mptr->mline->buf[i] != pptr->expanded->buf[i])
00437             {
00438                 text_replaced = 1;
00439                 break;
00440             }
00441         }
00442         
00443     }
00444 
00445     /* If at EOF, print out replacement text if exists */
00446     else
00447     {
00448         if (pptr->expanded_pos > 0)
00449             text_replaced = 1;
00450         else
00451             text_replaced = 0;
00452     }
00453         
00454     /* If text has been replaced, show point (after scanning for text
00455      * replacement and show line after text replacement 
00456      */
00457     if (text_replaced)
00458     {
00459         /* Print out file text where error occurred, if not at EOF */
00460         fprintf (stderr, "File text where error occurred:\n");
00461             if (mptr->mline != NULL)
00462             {
00463                 print_buf_with_arrow (stderr, mptr->mline->buf, mptr->pos);
00464             }
00465             else
00466             {
00467                 fprintf(stderr, "At EOF.\n\n");
00468             }
00469 
00470 
00471         fprintf (stderr, "After incremental text replacement:\n");
00472         print_buf_with_arrow (stderr, pptr->expanded->buf, pptr->expanded_pos);
00473     }
00474 
00475     /* 
00476      * Otherwise, use expanded_pos instead of mptr->mos to give true
00477      * location of error (instead of where we scanned for text replacement).
00478      * Should have expanded_pos = (mptr->pos -1);
00479      */
00480     else
00481     {
00482         /* Print out file text where error occurred */
00483         fprintf (stderr, "File text where error occurred:\n");
00484 
00485         /* Just to be safe, only use pptr->expanded_pos if it is where
00486          * we think it should be.
00487          */
00488         if (pptr->expanded_pos == (mptr->pos -1))
00489             error_pos = pptr->expanded_pos;
00490         else
00491             error_pos = mptr->pos;
00492         print_buf_with_arrow (stderr, mptr->mline->buf, error_pos);
00493     }
00494 
00495     fprintf (stderr,"Fatal error, cannot continue.\n");
00496     fprintf (stderr,"\n");
00497 
00498     exit (-1);
00499 }
00500 
00501 
00502 void Pputc (Pptr *pptr, int ch)
00503 {
00504     static char *file_name = NULL;
00505     static int file_line = -1, file_pos = 0;
00506     char *input_name;
00507     int input_line;
00508     int name_changed, line_changed;
00509 
00510 
00511     /* Update line no, file if necessary at beginning of every line */
00512     if (file_pos == 0)
00513     {
00514         /* Assume nothing changed */
00515         line_changed = 0;
00516         name_changed = 0;
00517 
00518         /* Get pointer to input file name */
00519         input_name = pptr->mptr->mfile->name;
00520 
00521         /* Get input line number.
00522          * If at EOF, just assume on predicted line.
00523          * If ch is a newline, then mptr is pointing to the next
00524          * line, so adjust input line number back by 1.
00525          */
00526         if (pptr->mptr->mline == NULL)
00527         {
00528             input_line = file_line;
00529         }
00530         else
00531         {
00532             input_line = pptr->mptr->mline->line_no;
00533             if (ch == '\n')
00534                 input_line --;
00535         }
00536 
00537         if ((file_name == NULL) ||
00538             (strcmp (file_name, input_name) != 0))
00539         {
00540             if (file_name != NULL)
00541                 free (file_name);
00542             file_name = strdup (input_name);
00543 
00544             /* Mark that file name changed */
00545             name_changed = 1;
00546         }
00547 
00548         if ((pptr->mptr->mline != NULL) &&
00549             (file_line != input_line))
00550         {
00551             file_line = input_line;
00552 
00553             /* Mark that line changed */
00554             line_changed = 1;
00555         }
00556 
00557         /* Print out line control specifiers if desired */
00558         if (print_line_directives)
00559         {
00560             if (name_changed && line_changed)
00561                 fprintf (out, "$line %i \"%s\"\n",file_line, file_name);
00562             
00563             else if (name_changed)
00564                 fprintf (out, "$line \"%s\"\n", file_name);
00565             
00566             else if (line_changed)
00567                 fprintf (out, "$line %i\n", file_line);
00568         }
00569     }
00570 
00571     /* Output the character to the file */
00572     putc (ch, out);
00573 
00574     /* Update line_no and pos */
00575     if (ch == '\n')
00576     {
00577         file_line++;
00578         file_pos = 0;
00579     }
00580     else
00581     {
00582         file_pos ++;
00583     }
00584 }
00585 
00586 main (int argc, char **argv, char **envp)
00587 {
00588     FILE *in;
00589     Pptr *pptr;
00590     char ch;
00591     int i;
00592     char *name, *value;
00593 
00594     /* Initialize alloc pools */
00595     Pptr_pool = L_create_alloc_pool ("Pptr", sizeof (Pptr), 16);
00596     Pdef_pool = L_create_alloc_pool ("Pdef", sizeof (Pdef), 16);
00597     String_Node_pool = L_create_alloc_pool ("String_Node", 
00598                                             sizeof (String_Node), 16);
00599     Value_List_pool = L_create_alloc_pool ("Value_List", 
00600                                            sizeof (Value_List), 16);
00601     
00602     /* Create the temporary buffer for the preprocessor's use */
00603     temp_mbuf = create_Mbuf();
00604     pptr_mbuf = create_Mbuf();
00605 
00606 
00607     /* Create symbol tables */
00608     Pdef_table = create_Psymbol_Table ("def");
00609     Mfile_table = create_Psymbol_Table ("open file");
00610 
00611 
00612     /* Read command line arguments and input file name */
00613     read_command_line_parameters(argc, argv);
00614 
00615     /* Parse environment variables and place them in the Pdef table */
00616     for (i = 0; envp[i] != NULL; i++)
00617     {
00618         /* Malloc own copy so can mess with contents */
00619         name = strdup (envp[i]);
00620 
00621         /* Find end of name/start of value */
00622         for (value = name; *value != 0; value++)
00623         {
00624             /* If hit equals, terminate name and move value to next
00625              * position.
00626              */
00627             if (*value == '=')
00628             {
00629                 /* Terminate name */
00630                 *value = 0;
00631                 
00632                 /* The rest of the string is the value */
00633                 value++;
00634                 break;
00635             }
00636         }
00637 
00638         /* Define name at value with level 0 and require explicit replacement
00639          * directives in order to use value.
00640          */
00641         add_Pdef (name, value, 0, 0);
00642 
00643         /* Free name (and implicitly value) */
00644         free (name);
00645     }
00646 
00647     /* Open input file */
00648     if (using_stdin)
00649     {
00650         input_file_name = strdup ("(stdin)");
00651         in = stdin;
00652     }
00653     else
00654     {
00655         if ((in = fopen (input_file_name, "r")) == NULL)
00656             L_punt ("Unable to open input file '%s'.", input_file_name);
00657     }
00658 
00659     /* Open output file */
00660     if (output_file_name == NULL)
00661     {
00662         output_file_name = strdup ("(stdout)");
00663         out = stdout;
00664     }
00665     else
00666     {
00667         if ((out = fopen (output_file_name, "w")) == NULL)
00668             L_punt ("Unable to open output file '%s'.", output_file_name);
00669     }
00670 
00671 
00672     /* Load input file into memory, and add file to symbol table */
00673     current_file = create_Mfile (in, input_file_name, "preprocessing");
00674     add_Psymbol (Mfile_table, current_file->name, current_file);
00675 
00676     /* Get pointer to start of file */
00677     pptr = create_Pptr (current_file);
00678 
00679     /* Create placemarks for preprocessor's use */
00680     temp_placemark = create_Pptr (current_file);
00681     expand_placemark = create_Mptr (current_file);
00682 
00683     while ((ch = Ppeekc (pptr)) != EOF)
00684     {
00685         /* Handle unmatched end curly brackets.  Process body ends when
00686          * encounters this to allow loop constructs to use process_body.
00687          */
00688         if (ch == '}')
00689         {
00690             ch = Pgetc (pptr);
00691             Pputc (pptr, ch);
00692         }
00693         else
00694         {
00695             pptr = process_body (pptr);
00696         }
00697     }
00698 
00699     /* Free up placemarks */
00700     free_Pptr (temp_placemark);
00701     free_Mptr (expand_placemark);
00702 
00703     /* Free up Pptr */
00704     free_Pptr (pptr);
00705 
00706     /* Free symbol tables */
00707     free_Psymbol_Table (Pdef_table, free_Pdef);
00708     free_Psymbol_Table (Mfile_table, (void (*)(void *))free_Mfile);
00709 
00710     /* Free up temp_mbuf */
00711     free_Mbuf (temp_mbuf);
00712     free_Mbuf (pptr_mbuf);
00713 
00714     /* Print  Lalloc usage if flagged on command line */
00715     if (print_alloc_usage)
00716     {
00717         if (Mfile_pool != NULL)
00718             L_print_alloc_info (stdout, Mfile_pool, 1);
00719         if (Mline_pool != NULL)
00720             L_print_alloc_info (stdout, Mline_pool, 1);
00721         if (Mbuf_pool != NULL)
00722             L_print_alloc_info (stdout, Mbuf_pool, 1);
00723         if (Mptr_pool != NULL)
00724             L_print_alloc_info (stdout, Mptr_pool, 1);
00725         if (Psymbol_Table_pool != NULL)
00726             L_print_alloc_info (stdout, Psymbol_Table_pool, 1);
00727         if (Psymbol_pool != NULL)
00728             L_print_alloc_info (stdout, Psymbol_pool, 1);
00729         L_print_alloc_info (stdout, Pptr_pool, 1);
00730         L_print_alloc_info (stdout, Pdef_pool, 1);
00731         L_print_alloc_info (stdout, String_Node_pool, 1);
00732         L_print_alloc_info (stdout, Value_List_pool, 1);
00733     }
00734 
00735     /* Free alloc pool */
00736     if (Mfile_pool != NULL)
00737         L_free_alloc_pool (Mfile_pool);
00738     if (Mline_pool != NULL)
00739         L_free_alloc_pool (Mline_pool);
00740     if (Mbuf_pool != NULL)
00741         L_free_alloc_pool (Mbuf_pool);
00742     if (Mptr_pool != NULL)
00743         L_free_alloc_pool (Mptr_pool);
00744     if (Psymbol_Table_pool != NULL)
00745         L_free_alloc_pool (Psymbol_Table_pool);
00746     if (Psymbol_pool != NULL)
00747         L_free_alloc_pool (Psymbol_pool);
00748     L_free_alloc_pool (Pptr_pool);
00749     L_free_alloc_pool (Pdef_pool);
00750     L_free_alloc_pool (String_Node_pool);
00751     L_free_alloc_pool (Value_List_pool);
00752 
00753     /* close input/output file.  May be stdin/stdout */
00754     fclose(in);
00755     fclose(out);
00756     
00757     return (0);
00758 }
00759 
00760 
00761 /* Process body and put into mbuf */
00762 Pptr *process_body (Pptr *pptr)
00763 {
00764     int ch;
00765     int depth;
00766 
00767     /* Start at no { depth */
00768     depth = 0;
00769 
00770     /* Process body until hit } that takes depth <= 0 */
00771     while ((ch = Ppeekc (pptr)) != EOF)
00772     {
00773         /* Handle recursively if preprocessor directive */
00774         if (ch == '$')
00775         {
00776             pptr = process_directive (pptr);
00777         }
00778 
00779         else if (ch == '{')
00780         {
00781             /* Get character, put in file, increase depth */
00782             ch = Pgetc (pptr);
00783             Pputc (pptr, ch);
00784 
00785             depth++;
00786 
00787         }
00788         
00789         else if (ch == '}')
00790         {
00791             /* If don't have matching curly bracket, stop before put in
00792              * file.
00793              */
00794             if (depth <= 0)
00795                 break;
00796             
00797             /* Get character, put in file, decrease depth */
00798             ch = Pgetc (pptr);
00799             Pputc (pptr, ch);
00800 
00801             depth--;
00802         }
00803         
00804 
00805         /* If not a preprocessing directive */
00806         else 
00807         {
00808             /* Get character and put in file */
00809             ch = Pgetc (pptr);
00810             Pputc (pptr, ch);
00811             
00812             /* Handle backslashes, get the next character if there */
00813             if (ch == '\\')
00814             {
00815                 if ((ch = Pgetc (pptr)) != EOF)
00816                     Pputc (pptr, ch);
00817             }
00818 
00819             /* Handle signel quoted strings.  Dump string to file
00820              * until end-quote or end of line.
00821              */
00822             else if (ch == '\'')
00823             {
00824                 while ((ch = Pgetc (pptr)) != EOF)
00825                 {
00826                     Pputc (pptr, ch);
00827 
00828                     /* Stop at end-quote or newline */
00829                     if ((ch == '\'') || (ch == '\n'))
00830                         break;
00831 
00832                     /* Handle backslashes, get next character if there 
00833                      * unless newline.
00834                      */
00835                     if (ch == '\\')
00836                     {
00837                         if ((ch = Ppeekc (pptr)) != EOF)
00838                         {
00839                             if (ch != '\n')
00840                             {
00841                                 ch = Pgetc (pptr);
00842                                 Pputc (pptr, ch);
00843                             }
00844 
00845                         }
00846                     }
00847                 }
00848             }
00849         }
00850     }
00851 
00852     return (pptr);
00853 }
00854 
00855 /* Skip the body's contents (don't print out or process any directives, etc) */
00856 Pptr *skip_body (Pptr *pptr)
00857 {
00858     int ch;
00859     int depth;
00860 
00861     /* Disable all text processing until hit end of body */
00862     allow_text_replacement = 0;
00863 
00864     /* Start at no { depth */
00865     depth = 0;
00866 
00867     /* Skip body until hit } that takes depth <= 0 */
00868     while ((ch = Ppeekc (pptr)) != EOF)
00869     {
00870         /* Handle curley braces */
00871         if (ch == '{')
00872         {
00873             /* Get character, increase depth */
00874             ch = Pgetc (pptr);
00875 
00876             depth++;
00877 
00878         }
00879         
00880         else if (ch == '}')
00881         {
00882             /* If don't have matching curly bracket, stop before getting char.
00883              */
00884             if (depth <= 0)
00885                 break;
00886             
00887             /* Get character,  and decrease depth */
00888             ch = Pgetc (pptr);
00889 
00890             depth--;
00891         }
00892         
00893 
00894         /* If not a preprocessing directive */
00895         else 
00896         {
00897             /* Get character and throw it away */
00898             ch = Pgetc (pptr);
00899             
00900             /* Handle backslashes, get the next character if there */
00901             if (ch == '\\')
00902             {
00903                 ch = Pgetc (pptr);
00904             }
00905 
00906             /* Handle signel quoted strings.  Skip string 
00907              * until end-quote or end of line.
00908              */
00909             else if (ch == '\'')
00910             {
00911                 while ((ch = Pgetc (pptr)) != EOF)
00912                 {
00913                     /* Stop at end-quote or newline */
00914                     if ((ch == '\'') || (ch == '\n'))
00915                         break;
00916 
00917                     /* Handle backslashes, get next character if there 
00918                      * unless newline.
00919                      */
00920                     if (ch == '\\')
00921                     {
00922                         if ((ch = Ppeekc (pptr)) != EOF)
00923                         {
00924                             if (ch != '\n')
00925                             {
00926                                 ch = Pgetc (pptr);
00927                             }
00928                         }
00929                     }
00930                 }
00931             }
00932         }
00933     }
00934 
00935     /* Turn back on text processing */
00936     allow_text_replacement = 1;
00937 
00938     return (pptr);
00939 }
00940 
00941 Pptr *process_def_directive (Pptr *pptr)
00942 {
00943     char *name, *val, ch, *ptr;
00944     int allow_implicit_replacement;
00945 
00946 
00947 
00948     /* Get name being defined, disable implicit text replacement so that the
00949      * name being defined will not be replaced by a previous directive 
00950      * unless explicitly told to.
00951      * This allows names to be easily to be redefined (otherwise a
00952      * $\ is needed before the name being defined the second time)
00953      */
00954     disable_implicit_text_replacement++;
00955 
00956     Pskip_whitespace_no_nl (pptr);
00957 
00958     /* Allow implicit replacement if '!' placed before name */
00959     if (Ppeekc (pptr) == '!')
00960     {
00961         /* Get the '!' */
00962         Pgetc (pptr);
00963 
00964         /* Mark as allowing implicit text replacement */
00965         allow_implicit_replacement = 1;
00966     }
00967     /* Otherwise, by default, don't allow implicit text replacement */
00968     else
00969     {
00970         allow_implicit_replacement = 0;
00971     }
00972     name = Pget_identifier (pptr);
00973 
00974     /* Restore original value of disable_implicit_text_replacement */
00975     disable_implicit_text_replacement--;
00976 
00977     Pskip_whitespace_no_nl (pptr);
00978     
00979     ch = Ppeekc (pptr);
00980     
00981     /* If at EOF or newline, assume just defining name, give no value */
00982     if ((ch == EOF) || (ch == '\n'))
00983     {
00984         val = strdup ("");
00985     }
00986     
00987     /* If start with '(', read in bounded string */
00988     else if (ch == '{')
00989     {
00990         val = Pget_bounded_string (pptr);
00991     }
00992     
00993     /* Otherwise, read to end of line and strip off trailing whitespace 
00994      * 11/26/96 No longer allow '{' and '}' symbols in unbounded 
00995      * $def statements.  Their accidential inclusion causes very
00996      * confusing error messages
00997      */
00998     else
00999     {
01000         /* Get placemark of error messages */
01001         move_Pptr (temp_placemark, pptr);
01002         val = Pget_stripped_line (pptr);
01003 
01004         /* Scan string for illegal '{' or '}' characters */
01005         for (ptr = val; *ptr != NULL; ptr++)
01006         {
01007             /* Handle backslash characters properly */
01008             if (*ptr == '\\')
01009             {
01010                 /* Goto next character */
01011                 ptr++;
01012 
01013                 /* Make sure there is a next character */
01014                 if (*ptr == NULL)
01015                 {
01016                     Perror (temp_placemark, 
01017                             "Character missing after backslash in "
01018                             "$def of '%s' to '%s'.\n\n",
01019                             name, val);
01020                 }
01021             }
01022             else if ((*ptr == '{') || (*ptr == '}'))
01023             {
01024                 Perror (temp_placemark, 
01025                         "Unexpected '%c' in $def of '%s' to '%s'.\n"
01026                         "  Braces are not allowed in this form of $def.\n\n"
01027                         "Possible solutions:\n"
01028                         "  1) Place '%c' on next line to exclude from $def\n"
01029                         "  2) Place a backslash before '%c'\n"
01030                         "  3) Use '$def def_name {bounded_def_value}' form.\n",
01031                         *ptr, name, val, *ptr, *ptr);
01032             }
01033 
01034             /* Error to have '$' without a backslash */
01035             else if (*ptr == '$' )
01036             {
01037                 Perror (temp_placemark, 
01038                         "Expecting a backslash before '$' in "
01039                         "$def of '%s' to '%s'.\n\n"
01040                         "Note: $def, $undef, $for, $if, and $include directives may not\n"
01041                         "      be embedded in replacement value strings.",
01042                         name, val);
01043             }
01044         }
01045     }
01046     
01047     /* Add definition to symbol table */
01048     add_Pdef (name, val, allow_implicit_replacement, 1);
01049     
01050     /* Free name and value buffers */
01051     free (name);
01052     free (val);
01053     
01054 
01055     return (pptr);
01056 }
01057 
01058 Pptr *process_undef_directive (Pptr *pptr)
01059 {
01060     char *name;
01061 
01062 
01063     /* Get name being undefined, disable implicit text replacement so that the
01064      * name being undefined will not be text replaced unless explicitly
01065      * told to do so.
01066      * This allows names to be easily to be undefined (otherwise a
01067      * $\ is needed before the name being undefined).
01068      */
01069     disable_implicit_text_replacement++;
01070 
01071     Pskip_whitespace_no_nl (pptr);
01072     name = Pget_identifier (pptr);
01073 
01074     /* Restore original value of disable_implicit_text_replacement */
01075     disable_implicit_text_replacement--;
01076 
01077     /* Remove definition from symbol table */
01078     delete_Pdef (name);
01079     
01080     /* Free name and value buffers */
01081     free (name);
01082 
01083     return (pptr);
01084 }
01085 
01086 Value_List *get_for_value_list (Pptr *pptr, Pptr *placemark)
01087 {
01088     Value_List *value_list;
01089     String_Node *value_node;
01090     char *val;
01091     int ch;
01092 
01093     /* Allocate value list */
01094     value_list = (Value_List *) L_alloc (Value_List_pool);
01095     
01096     /* Skip any whitespace before opening '(' */
01097     Pskip_whitespace (pptr);
01098 
01099     /* Move placemark to beginning of value list definition */
01100     move_Pptr (placemark, pptr);
01101     
01102     /* Get ( */
01103     if ((ch = Ppeekc (pptr)) != '(')
01104         Perror (pptr,
01105                 "Error parsing $for directive, '(' expected not '%c'.",
01106                 ch);
01107     Pgetc (pptr);
01108     
01109     /* Get variable name, disable implicit text replacement so that the
01110      * variable name will not be replaced by a previous directive unless
01111      * explicitly told to do so.
01112      * This allows names to be easily to be redefined (otherwise a
01113      * $\ is needed before the variable name)
01114      */
01115     disable_implicit_text_replacement++;
01116 
01117     Pskip_whitespace (pptr);
01118 
01119     /* If '!' appears before name, allow implicit text replacement of
01120      * this variable.
01121      */
01122     if (Ppeekc (pptr) == '!')
01123     {
01124         /* Get the '!' and mark that this variable allows implicit text
01125          * replacement.
01126          */
01127         Pgetc (pptr);
01128         value_list->allow_implicit_replacement = 1;
01129     }
01130     else
01131     {
01132         /* Otherwise, do not allow implicit text replacement. */
01133         value_list->allow_implicit_replacement = 0;
01134 
01135     }
01136     value_list->name = Pget_identifier (pptr);
01137 
01138     /* Restore original value of disable_implicit_text_replacement */
01139     disable_implicit_text_replacement--;
01140 
01141     Pskip_whitespace (pptr);
01142     
01143     /* Get 'in' followed by whitespace */
01144     if (Ppeekc (pptr) != 'i')
01145         Perror (pptr, "Error parsing $for directive, 'in' expected.");
01146     Pgetc (pptr);
01147     if (Ppeekc (pptr) != 'n')
01148         Perror (pptr, "Error parsing $for directive, 'in' expected.");
01149     Pgetc (pptr);
01150     if (!isspace(Ppeekc (pptr)))
01151         Perror (pptr, "Error parsing $for directive, 'in' expected.");
01152     Pgetc (pptr);
01153     Pskip_whitespace (pptr);
01154     
01155     
01156     /* Build a list of values until ending ')' */
01157     value_list->first_value = NULL;
01158     value_list->last_value = NULL;
01159     value_list->value_count = 0;
01160     while ((ch = Ppeekc (pptr)) != ')')
01161     {
01162         /* Make sure not at EOF */
01163         if (ch == EOF)
01164         {
01165             Perror (placemark, 
01166                     "Expect matching ')' to terminate $for directive's value list.");
01167         }
01168         
01169         /* Get each value in $for */
01170         val = Pget_for_string (pptr);
01171         Pskip_whitespace (pptr);
01172         
01173         /* Add to linked list */
01174         value_node = (String_Node *) L_alloc (String_Node_pool);
01175         
01176         value_node->string = val;
01177         value_node->next = NULL;
01178         
01179         if (value_list->last_value != NULL)
01180             value_list->last_value->next = value_node;
01181         else
01182             value_list->first_value = value_node;
01183         value_list->last_value = value_node;
01184 
01185         /* Update count of values */
01186         value_list->value_count ++;
01187     }
01188     
01189     /* Get ending parenthesis */
01190     ch = Pgetc (pptr); 
01191     
01192     /* Make sure there are some values defined */
01193     if (value_list->value_count == 0)
01194         Perror (pptr, "Values expected in $for."); 
01195 
01196     /* Return newly created value list for $for */
01197     return (value_list);
01198 }
01199 
01200 Pptr *process_for_directive (Pptr *pptr, Pptr *placemark)
01201 {
01202     Value_List *first_list, *last_list, *list, *next_list;
01203     String_Node *value_node, *next_value_node; 
01204     Pptr *loop_start;
01205     int ch, multi_list;
01206 
01207     /* Initialize list pointers */
01208     first_list = NULL;
01209     last_list = NULL;
01210 
01211     Pskip_whitespace (pptr);
01212     
01213     /* Move placemark to beginning of $for value list */
01214     move_Pptr (placemark, pptr);
01215 
01216     /* Get ( */
01217     if ((ch = Ppeekc (pptr)) != '(')
01218     {
01219         Perror (pptr,
01220                 "Error parsing $for directive, '(' expected not '%c'.",
01221                 ch);
01222     }
01223 
01224     /* Determine if multiple value lists are expected */
01225     Pgetc (pptr);
01226     Pskip_whitespace (pptr);
01227 
01228     /* Only multilist of have second '(', i.e., $for ((i in ...)(j in ...)) */
01229     if (Ppeekc (pptr) != '(')
01230     {
01231         /* Single list form, move back to initial state */
01232         move_Pptr (pptr, placemark);
01233     
01234         /* Get the value list for the $for directive */
01235         list = get_for_value_list (pptr, placemark);
01236 
01237         /* This is the only value list */
01238         first_list = list;
01239         last_list = list;
01240         list->next_list = NULL;
01241     }
01242     else
01243     {
01244         /* Multi list form, get value lists until hit ending ')' */
01245         while ((ch = Ppeekc (pptr)) != ')')
01246         {
01247             /* If not ')', it better be '(' */
01248             if (ch != '(')
01249             {
01250                 Perror (pptr, 
01251                         "Error parsing $for directive of the form:\n"
01252                         "   $for ((name1 in ...) (name2 in ...) ...) {body}\n\n"
01253                         "Expecting '(' or ')', not '%c' at this point.",
01254                         ch);
01255             }
01256 
01257             /* Get the value list for the $for directive */
01258             list = get_for_value_list (pptr, placemark);
01259 
01260             /* Add this list to the end of the value list list */
01261             if (last_list == NULL)
01262                 first_list = list;
01263             else
01264                 last_list->next_list = list;
01265             last_list = list;
01266             list->next_list = NULL;
01267 
01268             /* Make sure this list has the same number of items as
01269              * the first list 
01270              */
01271             if (list->value_count != first_list->value_count)
01272             {
01273                 Perror (placemark, 
01274                         "Value lists must be the same length for $for directive of this form:\n"
01275                         "   $for ((name1 in ...) (name2 in ...) ...) {body}\n\n"
01276                         "List for '%s' has length %i but list of '%s' has length %i!",
01277                         first_list->name, first_list->value_count,
01278                         list->name, list->value_count);
01279             }
01280 
01281             /* Make sure the same name is not defined by multiple lists */
01282             for (list = first_list; list != last_list; list = list->next_list)
01283             {
01284                 if (strcmp (list->name, last_list->name) == 0)
01285                 {
01286                 Perror (placemark, 
01287                         "A list for '%s' has already been defined in $for directive of form:\n"
01288                         "   $for ((name1 in ...) (name2 in ...) ...) {body}\n",
01289                         last_list->name);
01290                 }
01291             }
01292             
01293             /* Skip whitespace until next value list or ending ')' */
01294             Pskip_whitespace (pptr);
01295         }
01296         
01297         /* Get ending ')' */
01298         Pgetc (pptr);
01299 
01300     }
01301 
01302     /* Skip whitespace until start of loop */
01303     Pskip_whitespace (pptr);
01304     
01305     /* Move placemark to beginning of $for body */
01306     move_Pptr (placemark, pptr);
01307 
01308     /* Get starting '{' */
01309     if ((ch = Ppeekc (pptr)) != '{')
01310         Perror (pptr, "Expect '{' before $for's body.");
01311     Pgetc(pptr);
01312     
01313 
01314     /* Get pointer to start of loop */
01315     loop_start = copy_Pptr (pptr);
01316 
01317     /* Loop once for each value in first list 
01318      * (for multiple lists, the values are traversed in parallel)
01319      */
01320     while (first_list->value_count > 0)
01321     {
01322         /* Move pptr to beginning of loop body */
01323         move_Pptr (pptr, loop_start);
01324         
01325         /* For each list, set the variable to the current value */
01326         for (list = first_list; list != NULL; list = list->next_list)
01327         {
01328             /* Give loop variables heighest priority since it doesn't
01329              * make sense to allow the command line to override them.
01330              */
01331             add_Pdef (list->name, list->first_value->string, 
01332                       list->allow_implicit_replacement, 3);
01333         }
01334         
01335         /* Process the loop body */
01336         pptr = process_body (pptr);
01337         
01338         /* For each list, undefine variable after processing body */
01339         for (list = first_list; list != NULL; list = list->next_list)
01340         {
01341             delete_Pdef (list->name);
01342         }
01343         
01344         /* Get end '}' */
01345         if ((ch = Pgetc (pptr)) != '}')
01346         {
01347             Perror (placemark, 
01348                     "Matching '}' for $for's body expected before EOF.");
01349         }
01350 
01351         /* For each list, delete the first value  */
01352         for (list = first_list; list != NULL; list = list->next_list)
01353         {
01354             /* Get the first value node */
01355             value_node = list->first_value;
01356 
01357             /* Repair the value list and count */
01358             list->first_value = value_node->next;
01359             if (list->first_value == NULL)
01360                 list->last_value = NULL;
01361             list->value_count--;
01362 
01363             /* Delete value node */
01364             free (value_node->string);
01365             L_free (String_Node_pool, value_node);
01366         }
01367     }
01368 
01369     /* Free each list */
01370     for (list = first_list; list != NULL; list = next_list)
01371     {
01372         /* Get the next list before deleting this one */
01373         next_list = list->next_list;
01374 
01375         free (list->name);
01376         L_free (Value_List_pool, list);
01377     }
01378     
01379     /* Free copy to loop start */
01380     free_Pptr (loop_start);
01381     
01382 
01383     return (pptr);
01384 }
01385 
01386 Pptr *process_if_directive (Pptr *pptr, Pptr *placemark)
01387 {
01388     int ch;
01389     int condition, skip_elses;
01390     char *directive_type;
01391 
01392     Pskip_whitespace (pptr);
01393     
01394     /* Get ( */
01395     if ((ch = Ppeekc (pptr)) != '(')
01396         Perror (pptr,
01397                 "Error parsing $if directive, '(' expected not '%c'.",
01398                 ch);
01399     Pgetc (pptr);
01400 
01401     /* Get expression's value */
01402     condition = Pcalc_C_int_expr (pptr, 0); 
01403 
01404     Pskip_whitespace (pptr);
01405     
01406     /* Get ) */
01407     if ((ch = Ppeekc (pptr)) != ')')
01408         Perror (pptr,
01409                 "Error parsing $if directive, ')' expected not '%c'.",
01410                 ch);
01411     Pgetc (pptr);
01412     
01413     /* Skip whitespace until start of body */
01414     Pskip_whitespace (pptr);
01415     
01416     /* Get starting '{' */
01417     if ((ch = Ppeekc (pptr)) != '{')
01418         Perror (pptr, "Expect '{' before $if's body.");
01419     Pgetc(pptr);
01420     
01421     /* If the condition is met, process the if body, otherwise skip it*/
01422     if (condition)
01423     {
01424         /* Process the if body */
01425         pptr = process_body (pptr);
01426 
01427         /* Mark the elses should be skipped */
01428         skip_elses = 1;
01429     }
01430     else
01431     {
01432         pptr = skip_body (pptr);
01433 
01434         /* Mark the elses should be not be skipped */
01435         skip_elses = 0;
01436     }
01437         
01438     /* Get end '}' */
01439     if ((ch = Pgetc (pptr)) != '}')
01440         Perror (placemark, "End '}' of $if expected before EOF.");
01441 
01442 
01443     /* Process $elses or $elifs after $if */
01444     while (1)
01445     {
01446         /* Move placemark to end of last if/else/elif processed */
01447         move_Pptr (placemark, pptr);
01448 
01449         /* Stop if next token is not $else or $elif */
01450         Pskip_whitespace (pptr);
01451 
01452         if (Pgetc (pptr) != '$')
01453             break;
01454 
01455         /* Get the directive type */
01456         directive_type = Pget_alnum_string (pptr);
01457 
01458         if (strcmp (directive_type, "else") == 0)
01459         {
01460             /* Stop if next token is not $else or $elif */
01461             Pskip_whitespace (pptr); 
01462 
01463             /* Get starting '{' */
01464             if ((ch = Ppeekc (pptr)) != '{')
01465                 Perror (pptr, "Expect '{' before $else's body.");
01466             Pgetc(pptr);
01467     
01468             if (!skip_elses)
01469             {
01470                 /* Process the else body */
01471                 pptr = process_body (pptr);
01472             }
01473             else
01474             {
01475                 pptr = skip_body (pptr);
01476             }
01477 
01478             /* Get end '}' */
01479             if ((ch = Pgetc (pptr)) != '}')
01480                 Perror (placemark, "End '}' of $else expected before EOF.");
01481 
01482             /* Move placemark to end of else */
01483             move_Pptr (placemark, pptr);
01484 
01485             /* Free directive type before existing */
01486             free (directive_type);
01487             break;
01488         }
01489         else if (strcmp (directive_type, "elif") == 0)
01490         {
01491             Pskip_whitespace (pptr);
01492     
01493             /* Get ( */
01494             if ((ch = Ppeekc (pptr)) != '(')
01495                 Perror (pptr,
01496                         "Error parsing $elif directive, '(' expected not '%c'.",
01497                         ch);
01498             Pgetc (pptr);
01499             
01500             /* Get expression's value */
01501             condition = Pcalc_C_int_expr (pptr, 0); 
01502             
01503             Pskip_whitespace (pptr);
01504             
01505             /* Get ) */
01506             if ((ch = Ppeekc (pptr)) != ')')
01507                 Perror (pptr,
01508                         "Error parsing $elif directive, ')' expected not '%c'.",
01509                         ch);
01510             Pgetc (pptr);
01511             
01512             /* Skip whitespace until start of body */
01513             Pskip_whitespace (pptr);
01514             
01515             /* Get starting '{' */
01516             if ((ch = Ppeekc (pptr)) != '{')
01517                 Perror (pptr, "Expect '{' before $elif's body.");
01518             Pgetc(pptr);
01519             
01520             /* If the condition is met (and not skipping elses), 
01521              * process the elif body, otherwise skip it.
01522              */
01523             if (condition && !skip_elses)
01524             {
01525                 /* Process the if body */
01526                 pptr = process_body (pptr);
01527                 
01528                 /* Mark the elses should be skipped */
01529                 skip_elses = 1;
01530             }
01531             else
01532             {
01533                 pptr = skip_body (pptr);
01534             }
01535             
01536             /* Get end '}' */
01537             if ((ch = Pgetc (pptr)) != '}')
01538                 Perror (placemark, "End '}' of $elif expected before EOF.");
01539         }
01540         else
01541         {
01542             /* Free directive type before existing */
01543             free (directive_type);
01544             break;
01545         }
01546 
01547         /* Free directive type before going to next directive */
01548         free (directive_type);
01549     }
01550 
01551     /* Move pptr back to end of last if/else/elif processed */
01552     move_Pptr (pptr, placemark);
01553         
01554     return (pptr);
01555 }
01556 Pptr *process_include_directive (Pptr *pptr, Pptr *placemark)
01557 {
01558     char *file_name;
01559     Psymbol *psymbol;
01560     Mfile *include_file;
01561     Pptr *include_pptr;
01562     int ch;
01563     FILE *in;
01564 
01565     /* Require that $include be the first thing on the line
01566      * and the only thing on the line.
01567      *
01568      * These restrictions are required if the preprocessor
01569      * line directives are going to work properly.
01570      *
01571      * Placemark must be 1 character past $.  If change mptr library,
01572      * this test may not work.
01573      */
01574     if (placemark->mptr->pos != 1)
01575     {
01576         Perror (placemark, 
01577                 "$include directive must be the first and only thing on the line.");
01578     }
01579     
01580     /* Get quoted string, stripping quotes from string */
01581     file_name = Pget_quoted_string (pptr, 1);
01582 
01583     /* Make sure only other thing on line is a newline */
01584     Pskip_whitespace_no_nl (pptr);
01585     ch = Pgetc (pptr);
01586     if ((ch != '\n') && (ch != EOF))
01587     {
01588         Perror (placemark, 
01589                 "$include directive must be the first and only thing on the line.");
01590     }
01591 
01592 
01593     /* Search to see if include file already open, if not
01594      * open it and add to open file table.
01595      */
01596     if ((psymbol = find_Psymbol (Mfile_table, file_name)) != NULL)
01597     {
01598         include_file = (Mfile *) psymbol->data;
01599     }
01600     else
01601     {
01602         if ((in = fopen (file_name, "r")) == NULL)
01603         {
01604             Perror (placemark, "Unable to open include file %s.",
01605                     file_name);
01606         }
01607 
01608         include_file = create_Mfile (in, file_name, "preprocessing");
01609         add_Psymbol (Mfile_table, include_file->name, include_file);
01610     }
01611     
01612     /* Create pointto to start of include file for preprocessing */
01613     include_pptr = create_Pptr (include_file);
01614 
01615     /* Include whole file */
01616     while ((ch = Ppeekc (include_pptr)) != EOF)
01617     {
01618         /* Handle unmatched end curly brackets.  Process body ends when
01619          * encounters this to allow loop constructs to use process_body.
01620          */
01621         if (ch == '}')
01622         {
01623             ch = Pgetc (include_pptr);
01624             Pputc (include_pptr, ch);
01625         }
01626         else
01627         {
01628             include_pptr = process_body (include_pptr);
01629         }
01630     }
01631 
01632     /* Free up include_pptr */
01633     free_Pptr (include_pptr);
01634 
01635     /* Free file_name */
01636     free (file_name);
01637     
01638     return (pptr);
01639 }
01640 
01641 
01642 Pptr *process_directive(Pptr *pptr)
01643 {
01644     int ch;
01645     char *directive_type;
01646     Pptr *placemark;
01647 
01648     /* Allocate placemark for error messages */
01649     placemark = copy_Pptr (pptr);
01650 
01651     /* Get '$' */
01652     if ((ch = Pgetc (pptr)) != '$')
01653         L_punt ("process_directive: $ expected.");
01654 
01655     /* Get the directive type */
01656     directive_type = Pget_alnum_string (pptr);
01657 
01658     /* Handle whitespace/wierd character after $ */
01659     if (directive_type[0] == 0)
01660     {
01661         Perror (placemark, 
01662                 "Poorly formed preprocessor directive, expecting either:\n"
01663                 " 1) A backslash before the '$' or\n"
01664                 " 2) one of the currently supported directives:\n"
01665                 "  $include \"name_of_file_to_include\"\n"
01666                 "  $def def_name def_value   /* Note: def_value ends at newline */\n"
01667                 "  $def def_name {bounded_def_value}\n"
01668                 "  ${name}                   /* Forces replacement of name */\n"
01669                 "  $\\name                    /* Prevents replacement of name */\n"
01670                 "  $?{name}                  /* Replaced with 1 if name defined, 0 otherwise */\n"
01671                 "  $={integer C expression}\n"
01672                 "  $.={floating-point C expression}\n"
01673                 "  $if (cond) {body}   $elif (cond) {body}   $else {body}\n"
01674                 "  $x..y  or  $(int C expr)..(int C expr)\n"
01675                 "  $for (def_name in value1 value2 ... value N) {for_body}\n"
01676                 "  $for ((def_name1 in ...)(def_name2 in...)...){for_body}");
01677     }
01678 
01679     /* Handle the different types of directives */
01680     if (strcmp (directive_type, "def") == 0)
01681     {
01682         pptr = process_def_directive (pptr);
01683     }
01684 
01685     else if (strcmp (directive_type, "undef") == 0)
01686     {
01687         pptr = process_undef_directive (pptr);
01688     }
01689 
01690     else if (strcmp (directive_type, "for") == 0)
01691     {
01692         pptr = process_for_directive (pptr, placemark);
01693     }
01694 
01695     else if (strcmp (directive_type, "if") == 0)
01696     {
01697         pptr = process_if_directive (pptr, placemark);
01698     }
01699 
01700     else if (strcmp (directive_type, "else") == 0)
01701     {
01702         Perror (placemark, "$else without associated $if.");
01703     }
01704 
01705     else if (strcmp (directive_type, "elif") == 0)
01706     {
01707         Perror (placemark, "$elif without associated $if.");
01708     }
01709 
01710     else if (strcmp (directive_type, "include") == 0)
01711     {
01712         pptr = process_include_directive (pptr, placemark);
01713     }
01714 
01715 
01716     /* Otherwise, unsupported directive type */
01717     else
01718     {
01719         Perror (placemark,
01720                 "Unsupported preprocessing directive '%s'.\n"
01721                 "The currently supported directives are:\n"
01722                 "  $include \"name_of_file_to_include\"\n"
01723                 "  $def def_name def_value   /* Note: def_value ends at newline */\n"
01724                 "  $def def_name {bounded_def_value}\n"
01725                 "  ${name}                   /* Forces replacement of name */\n"
01726                 "  $\\name                    /* Prevents replacement of name */\n"
01727                 "  $?{name}                  /* Replaced with 1 if name defined, 0 otherwise */\n"
01728                 "  $={integer C expression}\n"
01729                 "  $.={floating-point C expression}\n"
01730                 "  $if (cond) {body}   $elif (cond) {body}   $else {body}\n"
01731                 "  $x..y  or  $(int C expr)..(int C expr)\n"
01732                 "  $for (def_name in value1 value2 ... value N) {for_body}\n"
01733                 "  $for ((def_name1 in ...)(def_name2 in...)...){for_body}",
01734                 directive_type);
01735     }
01736 
01737 
01738     /* Free directive type */
01739     free (directive_type);
01740 
01741     /* Free placemark */
01742     free_Pptr (placemark);
01743     
01744     return (pptr);
01745 }
01746 
01747 char *Pget_alnum_string (Pptr *pptr)
01748 {
01749     int ch;
01750     char *string;
01751 
01752     /* Use temp_mbuf to build string */
01753     clear_Mbuf (temp_mbuf);
01754 
01755     while ((ch = Ppeekc (pptr)) != EOF)
01756     {
01757         if ((ch == '_') || isalnum (ch))
01758         {
01759             ch = Pgetc (pptr);
01760             addc_to_Mbuf (temp_mbuf, ch);
01761         }
01762         else
01763             break;
01764     }
01765 
01766     /* Alloc copy of string in temp_buf */
01767     string = copy_Mbuf_buf (temp_mbuf);
01768     
01769     return (string);
01770 }
01771 
01772 char *Pget_identifier (Pptr *pptr)
01773 {
01774     int ch;
01775     char *string;
01776 
01777     /* Use temp_mbuf to build string */
01778     clear_Mbuf (temp_mbuf);
01779 
01780     /* Make sure first character is a character or an _ */
01781     ch = Ppeekc (pptr);
01782 
01783     if ((ch != '_') && !isalpha(ch))
01784         Perror (pptr, "Error in identifier at '%c'.", ch);
01785 
01786     while ((ch = Ppeekc (pptr)) != EOF)
01787     {
01788         /* Stop at whitespace only */
01789         if (isspace (ch))
01790             break;
01791 
01792         if ((ch == '_') || isalnum (ch))
01793         {
01794             ch = Pgetc (pptr);
01795             addc_to_Mbuf (temp_mbuf, ch);
01796         }
01797         else
01798             Perror (pptr, "Error in identifier at '%c'.", ch);
01799     }
01800 
01801     /* Alloc copy of string in temp_buf */
01802     string = copy_Mbuf_buf (temp_mbuf);
01803     
01804     return (string);
01805 }
01806 
01807 /* Gets the rest of the line with leading and trailing whitespace stripped */
01808 char *Pget_stripped_line (Pptr *pptr)
01809 {
01810     int ch;
01811     char *string;
01812 
01813     /* Use temp_mbuf to build string */
01814     clear_Mbuf (temp_mbuf);
01815 
01816     /* Skip leading whitespace */
01817     Pskip_whitespace_no_nl (pptr);
01818 
01819     /* Stop at EOF or newline */
01820     while ((ch = Ppeekc (pptr)) != EOF)
01821     {
01822         if (ch == '\n')
01823             break;
01824 
01825         ch = Pgetc (pptr);
01826         addc_to_Mbuf (temp_mbuf, ch);
01827     }
01828 
01829     /* Strip trailing space */
01830     strip_Mbuf (temp_mbuf);
01831 
01832     /* Alloc copy of string in temp_buf */
01833     string = copy_Mbuf_buf (temp_mbuf);
01834     
01835     return (string);
01836 }
01837 
01838 char *Pget_quoted_string (Pptr *pptr, int strip_quotes)
01839 {
01840     int ch, quote_ch;
01841     char *string;
01842 
01843     /* Use temp_mbuf to build string */
01844     clear_Mbuf (temp_mbuf);
01845 
01846     /* Skip leading whitespace */
01847     Pskip_whitespace_no_nl (pptr);
01848 
01849     /* Get placemark for error messages */
01850     move_Pptr (temp_placemark, pptr);
01851 
01852     /* Expect opening quote */
01853     ch = Ppeekc (pptr);
01854     if ((ch != '\'') && (ch != '\"'))
01855     {
01856         Perror (pptr, "Open quote (\' or \") expected.");
01857     }
01858     /* Save opening quote to know what to look for in closing quote */
01859     ch = Pgetc (pptr);
01860     quote_ch = ch;
01861 
01862     /* Add quotes to buffer if not stripping quotes from string */
01863     if (!strip_quotes)
01864         addc_to_Mbuf (temp_mbuf, ch);
01865 
01866     while (1)
01867     {
01868         /* Error to have '$' without a backslash */
01869         ch = Ppeekc (pptr);
01870         if (ch == '$' )
01871         {
01872             Perror (pptr, 
01873                     "Expecting a backslash before '$' in quoted string.\n"
01874                     "Note: $def, $undef, $for, $if, and $include directives may not\n"
01875                     "      be embedded in replacement value strings.");
01876         }
01877 
01878         /* Get next character */
01879         ch = Pgetc (pptr);
01880 
01881         /* Error if hit end of line before endquote */
01882         if ((ch == EOF) || (ch == '\n'))
01883         {
01884             Perror (temp_placemark,
01885                     "Expecting close quote (%c) before end of line.",
01886                     quote_ch);
01887         }
01888 
01889         /* Detect end quote */
01890         if (ch == quote_ch)
01891         {
01892             /* Add quotes to buffer if not stripping quotes */
01893             if (!strip_quotes)
01894                 addc_to_Mbuf (temp_mbuf, ch);
01895             break;
01896         }
01897 
01898         /* Add character to buffer */
01899         addc_to_Mbuf (temp_mbuf, ch);
01900 
01901         /* Handle backslash character */
01902         if (ch == '\\')
01903         {
01904             ch = Ppeekc (pptr);
01905             /* Add anything except end of line to buffer */
01906             if ((ch != EOF) && (ch != '\n'))
01907             {
01908                 ch = Pgetc (pptr);
01909                 addc_to_Mbuf (temp_mbuf, ch);
01910             }
01911 
01912         }
01913     }
01914     
01915     string = copy_Mbuf_buf (temp_mbuf);
01916     
01917     return (string);
01918 }
01919 
01920 
01921 
01922 char *Pget_for_string (Pptr *pptr)
01923 {
01924     int ch, next_ch;
01925     char *string;
01926 
01927     /* Use temp_mbuf to build string */
01928     clear_Mbuf (temp_mbuf);
01929 
01930     /* Skip leading whitespace */
01931     Pskip_whitespace_no_nl (pptr);
01932 
01933     /* Get placemark for error messages */
01934     move_Pptr (temp_placemark, pptr);
01935 
01936     /* If first character is a '{', get hit matching '}' */
01937     if ((ch = Ppeekc (pptr)) == EOF)
01938         Perror (temp_placemark, "String expected, not EOF.");
01939     if (ch == '{')
01940     {
01941         string = Pget_bounded_string (pptr);
01942     }
01943     
01944     /* If quoted string, get quoted string (don't strip quotes)*/
01945     else if ((ch == '"') || (ch == '\''))
01946     {
01947         string = Pget_quoted_string (pptr, 0);
01948     }
01949 
01950     /* Get string until next whitespace or ')', 
01951      * Punt if hit quotes in middle of string.
01952      */
01953     else
01954     {
01955         while (1)
01956         {
01957             ch = Ppeekc (pptr);
01958 
01959             if ((ch == EOF) || (ch == ')') || isspace (ch))
01960                 break;
01961 
01962             if (ch == '\\')
01963             {
01964                 /* Get backslash and next character */
01965                 ch = Pgetc (pptr);
01966                 next_ch = Ppeekc (pptr);
01967                 
01968                 if ((next_ch == EOF) || (next_ch == '\n'))
01969                     Perror (pptr, "Character missing after backslash.");
01970 
01971                 next_ch = Pgetc (pptr);
01972 
01973                 addc_to_Mbuf (temp_mbuf, ch);
01974                 addc_to_Mbuf (temp_mbuf, next_ch);
01975                     
01976 
01977             }
01978 
01979             /* Error to have quote character in middle of unquoted string */
01980             else if ((ch == '"' ) || (ch == '\''))
01981             {
01982                 Perror (pptr, 
01983                         "Unexpected quote.  Expecting one of the following:\n 1) Whitespace before the quote\n 2) or a backslash before the quote.");
01984 
01985             }
01986 
01987             /* Error to have '(' in middle of unquoted string */
01988             else if (ch == '(' )
01989             {
01990                 Perror (pptr, "Unexpected '(' in unquoted string.");
01991             }
01992 
01993             /* Error to have '$' without a backslash */
01994             else if (ch == '$' )
01995             {
01996                 Perror (pptr, 
01997                         "Expecting a backslash before '$' in unquoted string.\n"
01998                         "Note: $def, $undef, $for, $if, and $include directives may not\n"
01999                         "      be embedded in replacement value strings.");
02000             }
02001 
02002             else
02003             {
02004                 ch = Pgetc (pptr);
02005                 addc_to_Mbuf (temp_mbuf, ch);
02006             }
02007         }
02008 
02009         /* Alloc copy of string in temp_buf */
02010         string = copy_Mbuf_buf (temp_mbuf);
02011     }
02012     
02013     return (string);
02014 }
02015 
02016 /* Gets string bounded by { }  */
02017 char *Pget_bounded_string (Pptr *pptr)
02018 {
02019     int ch;
02020     char *string;
02021     int nesting_level;
02022 
02023     /* Use temp_mbuf to build string */
02024     clear_Mbuf (temp_mbuf);
02025 
02026     /* Skip leading whitespace */
02027     Pskip_whitespace_no_nl (pptr);
02028 
02029     /* Get placemark for error messages */
02030     move_Pptr (temp_placemark, pptr);
02031 
02032     /* If first character is a '{', get hit matching '}' */
02033     if ((ch = Ppeekc (pptr)) == EOF)
02034         Perror (temp_placemark, "String expected, not EOF.");
02035 
02036     if (ch != '{')
02037         L_punt ("Pget_bounded_string: '{' expected not '%c'.", ch);
02038 
02039     /* Skip leading { */
02040     Pgetc (pptr);
02041     
02042     /* Start at nesting depth 1 */
02043     nesting_level = 1;
02044     
02045     while (1)
02046     {
02047         /* Error to have '$' without a backslash */
02048         ch = Ppeekc (pptr);
02049         if (ch == '$' )
02050         {
02051             Perror (pptr, 
02052                     "Expecting a backslash before '$' in bounded string.\n"
02053                     "Note: $def, $undef, $for, $if, and $include directives may not\n"
02054                     "      be embedded in replacement value strings.");
02055         }
02056 
02057         if ((ch = Pgetc (pptr)) == EOF)
02058             Perror (temp_placemark, 
02059                     "End '}' of bounded string not found.");
02060         
02061         /* Handle backslashed characters, just add both backslash
02062          * and character to bounded string 
02063          */
02064         if (ch == '\\')
02065         {
02066             addc_to_Mbuf (temp_mbuf, ch);
02067 
02068             if ((ch = Pgetc (pptr)) == EOF)
02069                 Perror (temp_placemark, 
02070                         "End '}' of bounded string not found.");
02071             addc_to_Mbuf (temp_mbuf, ch);
02072 
02073             continue;
02074         }
02075         
02076 
02077         if (ch == '{')
02078             nesting_level ++;
02079         else if (ch == '}')
02080             nesting_level --;
02081         
02082         /* Do not add trailing '}' */
02083         if (nesting_level <= 0)
02084             break;
02085         
02086         addc_to_Mbuf (temp_mbuf, ch);
02087     }
02088     
02089     /* Alloc copy of string in temp_buf */
02090     string = copy_Mbuf_buf (temp_mbuf);
02091     
02092     return (string);
02093 }
02094 
02095 void Pskip_whitespace (Pptr *pptr)
02096 {
02097     int ch;
02098     
02099     while ((ch = Ppeekc (pptr)) != EOF)
02100     {
02101         if (isspace (ch))
02102             Pgetc (pptr);
02103         else
02104             break;
02105     }
02106 }
02107 
02108 void Pskip_whitespace_no_nl (Pptr *pptr)
02109 {
02110     int ch;
02111     
02112     while ((ch = Ppeekc (pptr)) != EOF)
02113     {
02114         if ((ch != '\n') && isspace (ch))
02115             Pgetc (pptr);
02116         else
02117             break;
02118     }
02119 }
02120 
02121 
02122 /* Create preprocessed file ptr (pptr) to start of mfile */
02123 Pptr *create_Pptr (Mfile *mfile)
02124 {
02125     Pptr *pptr;
02126 
02127     pptr = (Pptr *) L_alloc (Pptr_pool);
02128 
02129     pptr->mptr = create_Mptr (mfile);
02130     pptr->expanded = create_Mbuf();
02131     pptr->expanded_pos = 0;
02132     pptr->quoted = 0;
02133     pptr->scanned = 0;
02134     
02135     return (pptr);
02136 }
02137 
02138 /* Duplicates a Pptr exactly
02139  * Returns the duplicated Pptr (dup must be freed)
02140  */
02141 Pptr *copy_Pptr (Pptr *orig_pptr)
02142 {
02143     Pptr *new_pptr;
02144     char *orig_expanded;
02145     
02146     /* alloc and copy over fields */
02147     new_pptr = (Pptr *) L_alloc (Pptr_pool);
02148 
02149     new_pptr->mptr = copy_Mptr(orig_pptr->mptr);
02150 
02151     /* Allocate new expanded buffer, and copy over contents */
02152     new_pptr->expanded = create_Mbuf();
02153     orig_expanded = get_Mbuf_buf (orig_pptr->expanded);
02154     adds_to_Mbuf (new_pptr->expanded, orig_expanded);
02155     new_pptr->expanded_pos = orig_pptr->expanded_pos;
02156     new_pptr->quoted = orig_pptr->quoted;
02157     new_pptr->scanned = orig_pptr->scanned;
02158 
02159     return (new_pptr);
02160 }
02161 
02162 /*
02163  * Moves old pptr to new position.
02164  * Used to jump preprocessed file around.
02165  */
02166 void move_Pptr (Pptr *old_pptr, Pptr *new_pptr)
02167 {
02168     char *new_expanded;
02169 
02170     /* Sanity check, may not be the same pointers */
02171     if (old_pptr == new_pptr)
02172         L_punt ("move_Pptr: old_pptr and new_pptr the same.");
02173     
02174     move_Mptr (old_pptr->mptr, new_pptr->mptr);
02175 
02176     /* Copy over contents of expanded buffer */
02177     clear_Mbuf (old_pptr->expanded);
02178     new_expanded = get_Mbuf_buf (new_pptr->expanded);
02179     adds_to_Mbuf (old_pptr->expanded, new_expanded);
02180 
02181     /* Move expanded pointer and state */
02182     old_pptr->expanded_pos = new_pptr->expanded_pos;
02183     old_pptr->quoted = new_pptr->quoted;
02184     old_pptr->scanned = new_pptr->scanned;
02185 }
02186 
02187 void free_Pptr (Pptr *pptr)
02188 {
02189     /* Free Mptr */
02190     free_Mptr (pptr->mptr);
02191 
02192     /* Free expanded buffer */
02193     free_Mbuf (pptr->expanded);
02194 
02195     /* L_free structure */
02196     L_free (Pptr_pool, pptr);
02197 }
02198 
02199 /* Expands a preprocessor directive (if applicable), or just puts 
02200  * $ in expanded buffer.
02201  */
02202 void Pexpand_directive (Pptr *source_pptr)
02203 {
02204     int ch, next_ch, end_ch;
02205     Pptr *expand_pptr;
02206     Mbuf *expand_mbuf;
02207     int range_start, range_end;
02208     int int_result;
02209     double double_result;
02210     char rbuf[100];
02211     char *recurse_buf;
02212     char *ident, *replacement;
02213     int i;
02214 
02215     /* Get a copy of the source_pptr for recursion to do calculation */
02216     expand_pptr = copy_Pptr (source_pptr);
02217 
02218     /* Add the entry $ to buffer and consume it by incrementing pos */
02219     addc_to_Mbuf (expand_pptr->expanded, '$');
02220     expand_pptr->expanded_pos += 1;
02221 
02222     /* Peek at next character to determine type of directive (if next
02223      * character is not a backslash (otherwise will cause infinite
02224      * recursion))
02225      */
02226     if ((next_ch = Mpeekc (expand_pptr->mptr)) != '\\')
02227     {
02228         next_ch = Ppeekc (expand_pptr);
02229     }
02230 
02231     /* Handle evaluate integer expression preprocessor directive */
02232     if ((next_ch == '=') && allow_text_replacement)
02233 
02234     {
02235         /* Get the '=' */
02236         Pgetc (expand_pptr);
02237         
02238         /* Expect '{' after '=' */
02239         if (Ppeekc (expand_pptr) != '{')
02240         {
02241             Perror (expand_pptr, 
02242                     "Expecting '{' after '$=' in 'evaluate integer expression' directive.\nForm expected: $={integer expression}.");
02243         }
02244         /* Get the '{' */
02245         Pgetc (expand_pptr);
02246 
02247         /* Get value of expression */
02248         int_result = Pcalc_C_int_expr (expand_pptr, 0); 
02249 
02250         /* Expect '}' at end of expression */
02251         Pskip_whitespace (expand_pptr);
02252 
02253         if ((end_ch = Ppeekc (expand_pptr)) != '}')
02254         {
02255             if ((end_ch == 'e') || (end_ch == 'E') || (end_ch == '.'))
02256             {
02257                 Perror (expand_pptr, 
02258                         "Expression error or missing '}' in 'evaulate int expression' directive.\n"
02259                         "Form expected: $={integer expression}.\n\n"
02260                         "For floating-point expressions use: "
02261                         "$.={floating-point expression}.");
02262             }
02263             else
02264             {
02265                 Perror (expand_pptr, 
02266                         "Expression error or missing '}' in 'evaulate int expression' directive.\n"
02267                         "Form expected: $={integer expression}.");
02268             }
02269         }
02270         /* Get the '{' */
02271         Pgetc (expand_pptr);
02272         
02273         /* Move source_pptr's mptr to after calculation using expand_pptr */
02274         move_Mptr (source_pptr->mptr, expand_pptr->mptr);
02275         
02276         /* Write result to source_pptr's expanded buffer */
02277         sprintf (rbuf, "%i", int_result);
02278         
02279         adds_to_Mbuf (source_pptr->expanded, rbuf);
02280         
02281         /* Copy rest of expansion text to expand buffer */
02282         recurse_buf = get_Mbuf_buf (expand_pptr->expanded);
02283         adds_to_Mbuf (source_pptr->expanded, 
02284                       &recurse_buf[expand_pptr->expanded_pos]);
02285         
02286     }
02287 
02288     /* Handle evaluate floating-point expression preprocessor directive */
02289     else if ((next_ch == '.') && allow_text_replacement)
02290 
02291     {
02292         /* Get the '.' */
02293         Pgetc (expand_pptr);
02294 
02295         /* Expect '=' after '.' */
02296         if (Ppeekc (expand_pptr) != '=')
02297         {
02298             Perror (expand_pptr, 
02299                     "Expecting '=' after '$.' in 'evaluate float expression' directive.\nForm expected: $.={floating-point expression}.");
02300         }
02301         /* Get the '=' */
02302         Pgetc (expand_pptr);
02303         
02304         /* Expect '{' after '.=' */
02305         if (Ppeekc (expand_pptr) != '{')
02306         {
02307             Perror (expand_pptr, 
02308                     "Expecting '{' after '$.=' in 'evaluate float expression' directive.\nForm expected: $.={floating-point expression}.");
02309         }
02310         /* Get the '{' */
02311         Pgetc (expand_pptr);
02312 
02313         /* Get value of expression */
02314         double_result = Pcalc_C_float_expr (expand_pptr, 0); 
02315 
02316         /* Expect '}' at end of expression */
02317         Pskip_whitespace (expand_pptr);
02318 
02319         if (Ppeekc (expand_pptr) != '}')
02320         {
02321             Perror (expand_pptr, 
02322                     "Expression error or missing '}' in 'evaulate float expression' directive.\nForm expected: $.={floating-point expression}.");
02323         }
02324         /* Get the '{' */
02325         Pgetc (expand_pptr);
02326         
02327         /* Move source_pptr's mptr to after calculation using expand_pptr */
02328         move_Mptr (source_pptr->mptr, expand_pptr->mptr);
02329         
02330         /* Write result to source_pptr's expanded buffer */
02331         sprintf (rbuf, "%.16g", double_result);
02332         
02333         adds_to_Mbuf (source_pptr->expanded, rbuf);
02334         
02335         /* Copy rest of expansion text to expand buffer */
02336         recurse_buf = get_Mbuf_buf (expand_pptr->expanded);
02337         adds_to_Mbuf (source_pptr->expanded, 
02338                       &recurse_buf[expand_pptr->expanded_pos]);
02339         
02340     }
02341 
02342     /* Handle 'is defined' directive I.e., $?{name} */
02343     else if ((next_ch == '?') && allow_text_replacement)
02344 
02345     {
02346         /* Disable implicit text replacement so that the identifier between
02347          * ${} will not be replaced with its value unless explicitly told
02348          * to do so.
02349          */
02350         disable_implicit_text_replacement++;
02351 
02352         /* Get the '?' */
02353         Pgetc (expand_pptr);
02354         
02355         /* Expect '{' after '?' */
02356         if (Ppeekc (expand_pptr) != '{')
02357         {
02358             Perror (expand_pptr, 
02359                     "Expecting '{' after '$?' in 'is defined?' directive.\nForm expected: $?{name}.");
02360         }
02361 
02362         /* Get the '{' */
02363         Pgetc (expand_pptr);
02364 
02365         /* Allocate mbuf to build identifier */
02366         expand_mbuf = create_Mbuf();
02367         
02368         /* Get the string until the matching right curly bracket */
02369         while ((ch = Ppeekc (expand_pptr)) != '}')
02370         {
02371             if ((ch == EOF) || (ch == '\n'))
02372                 Merror (source_pptr->mptr, 
02373                         "Text replacement directive's end '}' not found.");
02374             
02375             /* Make reasonably sure valid identifier */
02376             if ((ch == '_') || isalnum (ch))
02377             {
02378                 /* Get the character and place in buffer */
02379                 ch = Pgetc (expand_pptr);
02380                 addc_to_Mbuf (expand_mbuf, ch);
02381             }
02382             else
02383             {
02384                 Perror (expand_pptr, 
02385                         "Invalid character '%c' in 'is defined?' directive.", 
02386                         ch);
02387             }
02388         }
02389         
02390         /* Get pointer to identifier buffer */
02391         ident = get_Mbuf_buf (expand_mbuf);
02392 
02393         /* If text replacement exists, replace directive with '1' */
02394         if ((replacement = Plookup (ident, 0)) != NULL)
02395             addc_to_Mbuf (source_pptr->expanded, '1');
02396 
02397         /* Otherwise, '0' */
02398         else
02399         {
02400             addc_to_Mbuf (source_pptr->expanded, '0');
02401         }
02402         
02403         /* Free expand mbuf */
02404         free_Mbuf (expand_mbuf);
02405 
02406         /* Restore original value of disable_implicit_text_replacement */
02407         disable_implicit_text_replacement--;
02408 
02409         /* Get the right curly bracket */
02410         Pgetc (expand_pptr);
02411 
02412         /* Move source_mptr to where expand_pptr->mptr is now */
02413         move_Mptr (source_pptr->mptr, expand_pptr->mptr);
02414     }
02415 
02416     /* Handle range expansion.  Ie. $0..10 */
02417     else if ((isdigit (next_ch) || (next_ch == '-') || (next_ch == '+') 
02418               || (next_ch == '(')) &&  allow_text_replacement)
02419     {
02420         /* Get the start of the range.
02421          * Use Pcalc_C_int_factor instead of Pcalc_C_int_expr so that only 
02422          * integers and parenthetical expressions are allowed.
02423          * Using Pcalc_C_int_expr cause it to consume trailing negative 
02424          * numbers.
02425          */
02426         range_start = Pcalc_C_int_factor (expand_pptr); 
02427 
02428         /* Skip whitespace and make sure next two characters are '..' */
02429         Pskip_whitespace (expand_pptr);
02430 
02431         /* Peek at each '.' in range expression so error message goes
02432          * in correct place.
02433          */
02434         if ((Ppeekc (expand_pptr) != '.') || (Pgetc (expand_pptr) != '.') ||
02435             (Ppeekc (expand_pptr) != '.') || (Pgetc (expand_pptr) != '.'))
02436             Perror (expand_pptr, 
02437                     "Error in range expression: '..' expected between start and end range.");
02438         
02439         /* Skip whitespace before end range */
02440         Pskip_whitespace (expand_pptr);
02441         
02442         /* Use Pcalc_C_int_factor instead of Pcalc_C_int_expr so that only 
02443          * integers and parenthetical expressions are allowed.
02444          * Using Pcalc_C_int_expr cause it to consume trailing negative 
02445          * numbers.
02446          */
02447         range_end = Pcalc_C_int_factor (expand_pptr);
02448 
02449         /* For now */
02450         if (range_start <= range_end)
02451         {
02452             for (i = range_start; i <= range_end; i++)
02453             {
02454                 sprintf (rbuf, "%i", i);
02455                 adds_to_Mbuf (source_pptr->expanded, rbuf);
02456                 
02457                 if (i < range_end)
02458                     addc_to_Mbuf (source_pptr->expanded, ' ');
02459             }
02460         }
02461         else
02462         {
02463             for (i = range_start; i >= range_end; i--)
02464             {
02465                 sprintf (rbuf, "%i", i);
02466                 adds_to_Mbuf (source_pptr->expanded, rbuf);
02467                 
02468                 if (i > range_end)
02469                     addc_to_Mbuf (source_pptr->expanded, ' ');
02470             }
02471         }
02472         
02473         /* Move source_pptr's mptr to after calculation using expand_pptr */
02474         move_Mptr (source_pptr->mptr, expand_pptr->mptr);
02475 
02476         /* Copy rest of expansion text to expand buffer */
02477         recurse_buf = get_Mbuf_buf (expand_pptr->expanded);
02478         adds_to_Mbuf (source_pptr->expanded, 
02479                       &recurse_buf[expand_pptr->expanded_pos]);
02480     }
02481     
02482     /* Handle substitution directives */
02483     else if ((next_ch == '{') && allow_text_replacement)
02484     {
02485         /* Disable implicit text replacement so that the identifier between
02486          * ${} will not be replaced with its value unless explicitly told
02487          * to do so.
02488          */
02489         disable_implicit_text_replacement++;
02490 
02491         /* Get the left curly bracket */
02492         Pgetc (expand_pptr);
02493         
02494         /* Allocate mbuf to build identifier */
02495         expand_mbuf = create_Mbuf();
02496         
02497         /* Get the string until the matching right curly bracket */
02498         while ((ch = Ppeekc (expand_pptr)) != '}')
02499         {
02500             if ((ch == EOF) || (ch == '\n'))
02501                 Merror (source_pptr->mptr, 
02502                         "Text replacement directive's end '}' not found.");
02503             
02504             /* Make reasonably sure valid identifier */
02505             if ((ch == '_') || isalnum (ch))
02506             {
02507                 /* Get the character and place in buffer */
02508                 ch = Pgetc (expand_pptr);
02509                 addc_to_Mbuf (expand_mbuf, ch);
02510             }
02511             
02512             else
02513             {
02514                 Perror (expand_pptr, 
02515                         "Invalid character '%c' in text replacement directive.", 
02516                         ch);
02517             }
02518         }
02519         
02520         
02521         /* Get pointer to identifier buffer */
02522         ident = get_Mbuf_buf (expand_mbuf);
02523 
02524         /* Replace text if exists otherwise punt 
02525          * (in future, allow environment search also)
02526          */
02527         if ((replacement = Plookup (ident, 0)) != NULL)
02528             adds_to_Mbuf (source_pptr->expanded, replacement);
02529         else
02530         {
02531             Perror (expand_pptr, 
02532                     "Undefined name '%s' in text replacement directive.", 
02533                     ident);
02534         }
02535         
02536         /* Free expand mbuf */
02537         free_Mbuf (expand_mbuf);
02538 
02539         /* Restore original value of disable_implicit_text_replacement */
02540         disable_implicit_text_replacement--;
02541 
02542         /* Get the right curly bracket */
02543         Pgetc (expand_pptr);
02544 
02545         /* Move source_mptr to where expand_pptr->mptr is now */
02546         move_Mptr (source_pptr->mptr, expand_pptr->mptr);
02547     }
02548     
02549     /* Handle $\ , to prevent preprocessing of a name 
02550      * Special case, work directly with mptr because do not
02551      * want any preprocessing of next character
02552      */
02553     else if (next_ch == '\\') /* Handle even when not doing text replacement */
02554     {
02555         /* Get backslash */
02556         Mgetc (source_pptr->mptr);
02557         
02558         /* Peek at next character, if newline, consume newline,
02559          * otherwise, flag that next character has been scanned
02560          * (prevents text replacement of next identifier).
02561          */
02562         if (Mpeekc (source_pptr->mptr) == '\n')
02563             Mgetc (source_pptr->mptr);
02564         else
02565         {
02566             /* Flag that remaining text has already been scanned */
02567             source_pptr->scanned = 1;
02568         }
02569     }
02570     /* Otherwise, just put $ in expanded buffer */
02571     else
02572     {
02573         addc_to_Mbuf (source_pptr->expanded, '$');
02574     }
02575 
02576     /* Free expansion Pptr, not needed after expansion */
02577     free_Pptr (expand_pptr);
02578 
02579 }
02580 
02581 /* Expands the file text, as necessary to allow processing of the next
02582  * preprocessed character in the file.
02583  * 
02584  * Returns 1 if successful (may have done nothing), 0 if at EOF.
02585  */
02586 int Pexpand_text (Pptr *pptr)
02587 {
02588     char ch, next_ch, first_ch;
02589     Mptr *mptr;
02590     char *ident, *replacement;
02591     char nesting_level;
02592 
02593 
02594     /* Get mptr from pptr for ease of use */
02595     mptr = pptr->mptr;
02596 
02597     /* Loop until there is something for the caller to process
02598      * at pptr->expanded_pos.
02599      * Do nothing if already something there on entry.
02600      */
02601     while (pptr->expanded->cur_len <= pptr->expanded_pos)
02602     {
02603 
02604         /* If at start of new line, clear expanded buf */
02605         if (Mptr_pos(mptr) == 0)
02606         {
02607             /* Clear expanded text, starting processing of new line */
02608             clear_Mbuf (pptr->expanded);
02609             pptr->expanded_pos = 0;
02610             
02611             /* End processing of quoted text if hit end of line */
02612             pptr->quoted = 0;
02613         }
02614 
02615         /* Get the next character from the file, return 0 at EOF */
02616         if ((ch = Mgetc (mptr)) == NULL)
02617             return (0);
02618 
02619         /* If it is not a alnum or _ then we have not scanned it yet */
02620         if ((ch != '_') && !isalnum(ch))
02621         {
02622             pptr->scanned = 0;
02623         }
02624         
02625         /* If not in quoted text or text we have already scanned then: 
02626          *   if start of identifier, read in identifier and do
02627          *   text replacement if necessary.
02628          */
02629         if ((!pptr->quoted) && (!pptr->scanned) &&
02630             ((ch == '_') || isalpha(ch)))
02631         {
02632             /* Set placemark so can rewind if not do not do text replacement */
02633             move_Mptr (expand_placemark, mptr);
02634             first_ch = ch;
02635 
02636             /* Use pptr_mbuf to build identifier */
02637             clear_Mbuf (pptr_mbuf);
02638             addc_to_Mbuf (pptr_mbuf, ch);
02639 
02640             /* Get rest of identifier */
02641             while ((ch = Mpeekc (mptr)) != EOF)
02642             {
02643                 if ((ch == '_') || isalnum(ch))
02644                 {
02645                     ch = Mgetc (mptr);
02646                     addc_to_Mbuf (pptr_mbuf, ch);
02647                 }
02648                 else
02649                     break;
02650             }
02651             
02652             /* Get pointer to identifier buffer */
02653             ident = get_Mbuf_buf (pptr_mbuf);
02654             
02655             /* Use replacement text if exists, 
02656              * otherwise rewind and put first character in expand text
02657              * and set scanned flag.
02658              */
02659             if (allow_text_replacement && 
02660                 (disable_implicit_text_replacement == 0) &&
02661                 (replacement = Plookup (ident, 1)) != NULL)
02662                 adds_to_Mbuf (pptr->expanded, replacement);
02663             else
02664             {
02665                 move_Mptr (mptr, expand_placemark);
02666                 pptr->scanned = 1;
02667                 addc_to_Mbuf (pptr->expanded, first_ch);
02668             }
02669         }
02670 
02671         /* If not in quoted text,
02672          * Handle calculation directives .
02673          * (later substitution directives)
02674          */
02675         else if ((!pptr->quoted) && (ch == '$'))
02676         {
02677             Pexpand_directive (pptr);
02678         }
02679 
02680         /* Handle slashes and comments */
02681         else if (ch == '/')
02682         {
02683             /* Look at the next character to see what to do
02684              * will be EOF if at end of file
02685              */
02686             ch = Mpeekc (mptr);
02687 
02688             /* Handle C++ line comments */
02689             if (ch == '/')
02690             {
02691                 /* Consume file until hit newline (leaving it there)
02692                  * or hit EOF.
02693                  */
02694                 while ((ch = Mpeekc (mptr)) != EOF)
02695                 {
02696                     if (ch == '\n')
02697                         break;
02698                     Mgetc (mptr);
02699                 }
02700             }
02701             /* Handle C (possibly nested) comments */
02702             else if (ch == '*')
02703             {
02704                 /* Set placemark  for error messages */
02705                 move_Mptr (expand_placemark, mptr);
02706 
02707                 /* Consume '*' */
02708                 Mgetc(mptr);
02709 
02710                 /* Consume the comment until hit end comment */
02711                 nesting_level = 1;
02712                 
02713                 while (nesting_level > 0)
02714                 {
02715                     if ((ch = Mgetc (mptr)) == EOF)
02716                         Merror (expand_placemark, 
02717                                 "End of comment not found.");
02718                     
02719                     /* Peek at next character to detect start/end comment
02720                      * delimiters.
02721                      */
02722                     next_ch = Mpeekc (mptr);
02723                     
02724                     /* Handle backslashes */
02725                     if (ch == '\\')
02726                     {
02727                         /* Consume next character without looking at it */
02728                         Mgetc (mptr);
02729                     }
02730                         
02731 
02732                     /* Detect start commment delimiter */
02733                     if ((ch == '/') && (next_ch == '*'))
02734                     {
02735                         /* Consume '*' */
02736                         Mgetc (mptr);
02737 
02738                         nesting_level++;
02739                     }
02740                     
02741                     /* Detect end comment delimiter */
02742                     if ((ch == '*') && (next_ch == '/'))
02743                     {
02744                         /* Consume '/' */
02745                         Mgetc (mptr);
02746 
02747                         nesting_level--;
02748                     }
02749                 }
02750             }
02751 
02752             /* Otherwise, just add / */
02753             else
02754             {
02755                 addc_to_Mbuf (pptr->expanded, '/');
02756             }
02757         }
02758 
02759         /* Otherwise, add character to expanded buffer */
02760         else
02761         {
02762             addc_to_Mbuf (pptr->expanded, ch);
02763 
02764             /* Do not preprocess single quoted strings,
02765              * Maintain quoted flag state (to prevent text processing above)
02766              */
02767             if (ch == '\'')
02768             {
02769                 if (pptr->quoted)
02770                     pptr->quoted = 0;
02771                 else
02772                     pptr->quoted = 1;
02773             }
02774             
02775             /* If have backslash, add next character newline or at EOF */
02776             else if (ch == '\\')
02777             {
02778                 ch = Mpeekc (mptr);
02779                 if ((ch != '\n') && (ch != EOF))
02780                 {
02781                     ch = Mgetc(mptr);
02782                     addc_to_Mbuf (pptr->expanded, ch);
02783                 }
02784             }
02785         }
02786     }       
02787 
02788     /* Success, return 1 */
02789     return (1);
02790 }
02791 
02792 /* Gets the next preprocessed character from the file. */
02793 int Pgetc (Pptr *pptr)
02794 {
02795     int ch;
02796     char *expanded_buf;
02797 
02798     /* If not at end of expanded text, get next character */
02799     if (Pexpand_text (pptr))
02800     {
02801         /* Get next character from the expanded buffer */
02802         expanded_buf = get_Mbuf_buf (pptr->expanded);
02803         ch = expanded_buf[pptr->expanded_pos];
02804         pptr->expanded_pos++;
02805     }
02806     
02807     /* Otherwise, at EOF return EOF */
02808     else
02809     {
02810         ch = EOF;
02811     }
02812     return (ch);
02813 }
02814 
02815 /* Peeks at the next character to be returned */
02816 int Ppeekc (Pptr *pptr)
02817 {
02818     int ch;
02819     char *expanded_buf;
02820 
02821     /* If not at end of expanded text, get next character */
02822     if (Pexpand_text (pptr))
02823     {
02824         /* Peek at Get next character from the expanded buffer */
02825         expanded_buf = get_Mbuf_buf (pptr->expanded);
02826         ch = expanded_buf[pptr->expanded_pos];
02827     }
02828     
02829     /* Otherwise, at EOF return EOF */
02830     else
02831         ch = EOF;
02832 
02833     return (ch);
02834 }
02835 
02836 /* Calculates the value of an integer C expression using any non-assignment 
02837  * operation in C.  Start with a current_precedence of 0.
02838  */
02839 int Pcalc_C_int_expr (Pptr *pptr, int current_precedence)
02840 {
02841     int result;
02842     int ch, ch2;
02843     int unary_operator;
02844     int temp_result;
02845     Pptr *placemark;
02846 
02847     /* Detect unary operators before first factor */
02848     Pskip_whitespace (pptr);
02849     ch = Ppeekc (pptr);
02850 
02851     if ((ch == '!') || (ch == '~'))
02852     {
02853         /* Get unary operator and skip whitespace until next character */
02854         unary_operator = ch;
02855         Pgetc(pptr);
02856         Pskip_whitespace (pptr);
02857         ch = Ppeekc (pptr);
02858     }
02859     else
02860         unary_operator = 0;
02861 
02862     /* Get the first factor */
02863     result = Pcalc_C_int_factor (pptr);
02864 
02865     /* Processor unary operator (if any) */
02866     if (unary_operator != 0)
02867     {
02868         switch (unary_operator)
02869         {
02870           case '!':
02871             result = !result;
02872             break;
02873 
02874           case '~':
02875             result = ~result;
02876             break;
02877 
02878           default:
02879             Perror (pptr, "Algorithm error: undefined unary operator '%c'.",
02880                     unary_operator);
02881         }
02882     }
02883 
02884     /* Allocate placemark because can be recursively called in 
02885      * inner loop.
02886      */
02887     placemark = copy_Pptr (pptr);
02888 
02889     /* Add/Sub trace remaining terms */
02890     while (1)
02891     {
02892         /* Move placemark to current pos so can rewind if at end of expr */
02893         move_Pptr (placemark, pptr);
02894         Pskip_whitespace (pptr);
02895 
02896         /* Get next potential token and peek at character after it for
02897          * two character tokens 
02898          */
02899         ch = Pgetc (pptr);
02900         ch2 = Ppeekc (pptr);
02901 
02902         /* Process token if token matches and current level of predecence
02903          * is lower than the tokens precedence.
02904          * 
02905          * Check two character tokens first before one character tokens
02906          */
02907 
02908         /* 
02909          * Check two character tokens
02910          */
02911         if ((ch == '<') && (ch2 == '<') && (current_precedence < 11))
02912         {
02913             /* Consume second character of token */
02914             Pgetc(pptr);
02915             result = result << Pcalc_C_int_expr (pptr, 11);
02916         }
02917 
02918         else if ((ch == '>') && (ch2 == '>') && (current_precedence < 11))
02919         {
02920             /* Consume second character of token */
02921             Pgetc(pptr);
02922             result = result >> Pcalc_C_int_expr (pptr, 11);
02923         }
02924 
02925         else if ((ch == '<') && (ch2 == '=') && (current_precedence < 10))
02926         {
02927             /* Consume second character of token */
02928             Pgetc(pptr);
02929             result = result <= Pcalc_C_int_expr (pptr, 10);
02930         }
02931 
02932         else if ((ch == '>') && (ch2 == '=') && (current_precedence < 10))
02933         {
02934             /* Consume second character of token */
02935             Pgetc(pptr);
02936             result = result >= Pcalc_C_int_expr (pptr, 10);
02937         }
02938 
02939         else if ((ch == '=') && (ch2 == '=') && (current_precedence < 9))
02940         {
02941             /* Consume second character of token */
02942             Pgetc(pptr);
02943             result = result == Pcalc_C_int_expr (pptr, 9);
02944         }
02945 
02946         else if ((ch == '!') && (ch2 == '=') && (current_precedence < 9))
02947         {
02948             /* Consume second character of token */
02949             Pgetc(pptr);
02950             result = result != Pcalc_C_int_expr (pptr, 9);
02951         }
02952 
02953         else if ((ch == '&') && (ch2 == '&') && (current_precedence < 5))
02954         {
02955             /* Consume second character of token */
02956             Pgetc(pptr);
02957 
02958             /* Get RHS of && into temp, because C short circutes evaluation */
02959             temp_result = Pcalc_C_int_expr (pptr, 5);
02960             result = result && temp_result;
02961         }
02962 
02963         else if ((ch == '|') && (ch2 == '|') && (current_precedence < 4))
02964         {
02965             /* Consume second character of token */
02966             Pgetc(pptr);
02967 
02968             /* Get RHS of || into temp, because C short circutes evaluation */
02969             temp_result = Pcalc_C_int_expr (pptr, 4);
02970             result = result || temp_result;
02971         }
02972 
02973         /* 
02974          * Now check one character tokens 
02975          */
02976 
02977         else if ((ch == '*') && (current_precedence < 13))
02978             result = result * Pcalc_C_int_expr (pptr, 13);
02979 
02980         else if ((ch == '/') && (current_precedence < 13))
02981         {
02982             /* Detect divide by zero */
02983             temp_result = Pcalc_C_int_expr (pptr, 13);
02984             
02985             if (temp_result == 0)
02986                 Perror (pptr, "Expression attempts to divide by zero.");
02987 
02988             result = result / temp_result;
02989         }
02990 
02991         else if ((ch == '%') && (current_precedence < 13))
02992             result = result % Pcalc_C_int_expr (pptr, 13);
02993 
02994         else if ((ch == '+') && (current_precedence < 12))
02995             result = result + Pcalc_C_int_expr (pptr, 12);
02996 
02997         else if ((ch == '-') && (current_precedence < 12))
02998             result = result - Pcalc_C_int_expr (pptr, 12);
02999 
03000         else if ((ch == '<') && (current_precedence < 10))
03001             result = result < Pcalc_C_int_expr (pptr, 10);
03002 
03003         else if ((ch == '>') && (current_precedence < 10))
03004             result = result > Pcalc_C_int_expr (pptr, 10);
03005 
03006         /* Must check second char since first char alone higher prededence 
03007          * than &&
03008          */
03009         else if ((ch == '&') && (ch2 != '&') && (current_precedence < 8))
03010             result = result & Pcalc_C_int_expr (pptr, 8);
03011 
03012         else if ((ch == '^') && (current_precedence < 7))
03013             result = result ^ Pcalc_C_int_expr (pptr, 7);
03014 
03015         /* Must check second char since first char alone higher prededence 
03016          * than ||
03017          */
03018         else if ((ch == '|') && (ch2 != '|') && (current_precedence < 6))
03019             result = result | Pcalc_C_int_expr (pptr, 6);
03020 
03021         else
03022         {
03023             /* Go back to placemark position before returning */
03024             move_Pptr (pptr, placemark);
03025             break;
03026         }
03027     }
03028 
03029     /* Free placemark */
03030     free_Pptr (placemark);
03031     
03032     return (result);
03033 }
03034 
03035 int Pcalc_C_int_factor (Pptr *pptr)
03036 {
03037     int result;
03038     int ch, sign_operator;
03039     Pptr *placemark;
03040 
03041     Pskip_whitespace (pptr);
03042     ch = Ppeekc (pptr);
03043 
03044     /* Detect sign operators */
03045     if ((ch == '-') || (ch == '+'))
03046     {
03047         /* Get sign operator and skip whitespace until next character */
03048         sign_operator = ch;
03049         Pgetc(pptr);
03050         Pskip_whitespace (pptr);
03051         ch = Ppeekc (pptr);
03052     }
03053     else
03054         sign_operator = 0;
03055 
03056     /* Handle numbers */
03057     if (isdigit (ch))
03058     {
03059         /* Get first number */
03060         ch = Pgetc (pptr);
03061         result = ch - '0';
03062 
03063 
03064         /* Detect Octal and Hex numbers (not currently supported) */
03065         if (ch == '0')
03066         {
03067             ch = Ppeekc (pptr);
03068             if (isdigit (ch))
03069             {
03070                 Perror (pptr, "Octal representation (numbers with 0 prefix) not supported in expressions.");
03071             }
03072             else if ((ch == 'x') || (ch == 'X'))
03073             {
03074                 Perror (pptr, "Hex representation (numbers with 0x prefix) not supported in expressions.");
03075             }
03076         }
03077 
03078         /* Allocate placemark, since can be recursively called 
03079          * inside loop.
03080          */
03081         placemark = copy_Pptr (pptr);
03082 
03083         /* Get remaining digits */
03084         while (1)
03085         {
03086             /* Get placemark so can restore if not a character */
03087             move_Pptr (placemark, pptr);
03088 
03089             /* Get and process character */
03090             ch = Ppeekc (pptr);
03091 
03092             if (isdigit (ch))
03093             {
03094                 ch = Pgetc (pptr);
03095                 result = (result * 10) + (ch - '0');
03096             }
03097             else 
03098             {
03099                 /* Restore to right after number */
03100                 move_Pptr (pptr, placemark);
03101                 break;
03102             }
03103         }
03104         
03105         free_Pptr (placemark);
03106     }
03107 
03108     /* Handle parenthesis */
03109     else if (ch == '(')
03110     {
03111         /* Get '(', get internal expression value, and ending ')' */
03112         Pgetc (pptr);
03113         result = Pcalc_C_int_expr (pptr, 0);
03114         Pskip_whitespace (pptr);
03115         if ((ch = Ppeekc (pptr)) == ')')
03116             Pgetc (pptr);
03117         else
03118         {
03119             if ((ch == 'e') || (ch == 'E') || (ch == '.'))
03120             {
03121                 Perror (pptr, 
03122                         "Integer expression error: ')' expected.\n\n"
03123                         "For floating-point expressions use: "
03124                         "$.={floating-point expression}.");
03125             }
03126             else
03127             {
03128                 Perror (pptr, "Integer expression error: ')' expected.");
03129             }
03130         }
03131     }
03132 
03133     else
03134     {
03135         if (sign_operator != 0)
03136         {
03137             if (ch == '.')
03138             {
03139                 Perror (pptr, 
03140                         "Integer expression error:\n"
03141                         "   integer or '(' expected.\n\n"
03142                         "For floating-point expressions use the following form:\n"
03143                         "   $.={floating-point expression}.");
03144             }
03145             else
03146             {
03147                 Perror (pptr, 
03148                         "Integer expression error:\n"
03149                         "   integer or '(' expected.");
03150             }
03151         }
03152         else
03153         {
03154             if (ch == '.')
03155             {
03156                 Perror (pptr, 
03157                         "Integer expression error:\n"
03158                         "    integer , '(', or sign (-, +) expected.\n\n"
03159                         "For floating-point expressions use the following form:\n"
03160                         "   $.={floating-point expression}.");
03161             }
03162             else
03163             {
03164                 Perror (pptr, 
03165                         "Integer expression error:\n"
03166                         "    integer , '(', or sign (-, +) expected.");
03167             }
03168         }
03169 
03170     }
03171     
03172     if (sign_operator != 0)
03173     {
03174         switch (sign_operator)
03175         {
03176           case '-':
03177             result = -result;
03178             break;
03179 
03180           case '+':
03181             break;
03182 
03183 
03184           default:
03185             Perror (pptr, "Algorithm error: undefined sign operator '%c'.",
03186                     sign_operator);
03187         }
03188     }
03189         
03190 
03191     return (result);
03192 }
03193 
03194 
03195 
03196 /* Calculates the value of an floating-point C expression using any 
03197  * non-assignment operation in C.  Start with a current_precedence of 0.
03198  */
03199 double Pcalc_C_float_expr (Pptr *pptr, int current_precedence)
03200 {
03201     double result;
03202     int ch, ch2;
03203     int unary_operator;
03204     double temp_result;
03205     Pptr *placemark;
03206 
03207     /* Detect unary operators before first factor */
03208     Pskip_whitespace (pptr);
03209     ch = Ppeekc (pptr);
03210 
03211     if (ch == '!') 
03212     {
03213         /* Get unary operator and skip whitespace until next character */
03214         unary_operator = ch;
03215         Pgetc(pptr);
03216         Pskip_whitespace (pptr);
03217         ch = Ppeekc (pptr);
03218     }
03219     else if (ch == '~')
03220     {
03221         Perror (pptr, "'~' not supported in floating point expressions.");
03222     }
03223     else
03224         unary_operator = 0;
03225 
03226     /* Get the first factor */
03227     result = Pcalc_C_float_factor (pptr);
03228 
03229     /* Processor unary operator (if any) */
03230     if (unary_operator != 0)
03231     {
03232         switch (unary_operator)
03233         {
03234           case '!':
03235             result = !result;
03236             break;
03237 
03238           default:
03239             Perror (pptr, "Algorithm error: undefined unary operator '%c'.",
03240                     unary_operator);
03241         }
03242     }
03243 
03244     /* Allocate placemark because can be recursively called in 
03245      * inner loop.
03246      */
03247     placemark = copy_Pptr (pptr);
03248 
03249     /* Add/Sub trace remaining terms */
03250     while (1)
03251     {
03252         /* Move placemark to current pos so can rewind if at end of expr */
03253         move_Pptr (placemark, pptr);
03254         Pskip_whitespace (pptr);
03255 
03256         /* Get next potential token and peek at character after it for
03257          * two character tokens 
03258          */
03259         ch = Pgetc (pptr);
03260         ch2 = Ppeekc (pptr);
03261 
03262         /* Process token if token matches and current level of predecence
03263          * is lower than the tokens precedence.
03264          * 
03265          * Check two character tokens first before one character tokens
03266          */
03267 
03268         /* 
03269          * Check two character tokens
03270          */
03271         if ((ch == '<') && (ch2 == '<') && (current_precedence < 11))
03272         {
03273             Perror (pptr, "'<<' not supported in floating point expressions.");
03274         }
03275 
03276         else if ((ch == '>') && (ch2 == '>') && (current_precedence < 11))
03277         {
03278             Perror (pptr, "'>>' not supported in floating point expressions.");
03279         }
03280 
03281         else if ((ch == '<') && (ch2 == '=') && (current_precedence < 10))
03282         {
03283             /* Consume second character of token */
03284             Pgetc(pptr);
03285             result = result <= Pcalc_C_float_expr (pptr, 10);
03286         }
03287 
03288         else if ((ch == '>') && (ch2 == '=') && (current_precedence < 10))
03289         {
03290             /* Consume second character of token */
03291             Pgetc(pptr);
03292             result = result >= Pcalc_C_float_expr (pptr, 10);
03293         }
03294 
03295         else if ((ch == '=') && (ch2 == '=') && (current_precedence < 9))
03296         {
03297             /* Consume second character of token */
03298             Pgetc(pptr);
03299             result = result == Pcalc_C_float_expr (pptr, 9);
03300         }
03301 
03302         else if ((ch == '!') && (ch2 == '=') && (current_precedence < 9))
03303         {
03304             /* Consume second character of token */
03305             Pgetc(pptr);
03306             result = result != Pcalc_C_float_expr (pptr, 9);
03307         }
03308 
03309         else if ((ch == '&') && (ch2 == '&') && (current_precedence < 5))
03310         {
03311             /* Consume second character of token */
03312             Pgetc(pptr);
03313 
03314             /* Get RHS of && into temp, because C short circutes evaluation */
03315             temp_result = Pcalc_C_float_expr (pptr, 5);
03316             result = result && temp_result;
03317         }
03318 
03319         else if ((ch == '|') && (ch2 == '|') && (current_precedence < 4))
03320         {
03321             /* Consume second character of token */
03322             Pgetc(pptr);
03323 
03324             /* Get RHS of || into temp, because C short circutes evaluation */
03325             temp_result = Pcalc_C_float_expr (pptr, 4);
03326             result = result || temp_result;
03327         }
03328 
03329         /* 
03330          * Now check one character tokens 
03331          */
03332 
03333         else if ((ch == '*') && (current_precedence < 13))
03334             result = result * Pcalc_C_float_expr (pptr, 13);
03335 
03336         else if ((ch == '/') && (current_precedence < 13))
03337         {
03338             /* Detect divide by zero */
03339             temp_result = Pcalc_C_float_expr (pptr, 13);
03340             
03341             if (temp_result == 0.0)
03342                 Perror (pptr, "Float expression attempts to divide by zero.");
03343 
03344             result = result / temp_result;
03345         }
03346 
03347         else if ((ch == '%') && (current_precedence < 13))
03348             Perror (pptr, "'%' not supported in floating point expressions.");
03349 
03350         else if ((ch == '+') && (current_precedence < 12))
03351             result = result + Pcalc_C_float_expr (pptr, 12);
03352 
03353         else if ((ch == '-') && (current_precedence < 12))
03354             result = result - Pcalc_C_float_expr (pptr, 12);
03355 
03356         else if ((ch == '<') && (current_precedence < 10))
03357             result = result < Pcalc_C_float_expr (pptr, 10);
03358 
03359         else if ((ch == '>') && (current_precedence < 10))
03360             result = result > Pcalc_C_float_expr (pptr, 10);
03361 
03362         /* Must check second char since first char alone higher prededence 
03363          * than &&
03364          */
03365         else if ((ch == '&') && (ch2 != '&') && (current_precedence < 8))
03366             Perror (pptr, "'&' not supported in floating point expressions.");
03367 
03368         else if ((ch == '^') && (current_precedence < 7))
03369             Perror (pptr, "'^' not supported in floating point expressions.");
03370 
03371         /* Must check second char since first char alone higher prededence 
03372          * than ||
03373          */
03374         else if ((ch == '|') && (ch2 != '|') && (current_precedence < 6))
03375             Perror (pptr, "'|' not supported in floating point expressions.");
03376 
03377         else
03378         {
03379             /* Go back to placemark position before returning */
03380             move_Pptr (pptr, placemark);
03381             break;
03382         }
03383     }
03384 
03385     /* Free placemark */
03386     free_Pptr (placemark);
03387     
03388     return (result);
03389 }
03390 
03391 double Pcalc_C_float_factor (Pptr *pptr)
03392 {
03393     double result;
03394     int ch, sign_operator;
03395     Pptr *placemark;
03396     Mbuf *double_mbuf;
03397     int after_decimal_point, in_exponent;
03398     char *double_string, *end_ptr;
03399 
03400     Pskip_whitespace (pptr);
03401     ch = Ppeekc (pptr);
03402 
03403     /* Detect sign operators */
03404     if ((ch == '-') || (ch == '+'))
03405     {
03406         /* Get sign operator and skip whitespace until next character */
03407         sign_operator = ch;
03408         Pgetc(pptr);
03409         Pskip_whitespace (pptr);
03410         ch = Ppeekc (pptr);
03411     }
03412     else
03413         sign_operator = 0;
03414 
03415     /* Handle numbers */
03416     if (isdigit (ch) || (ch == '.'))
03417     {
03418         /* Initialize state to allow only one decimal point or
03419          * exponent.
03420          */
03421         after_decimal_point = 0;
03422         in_exponent = 0;
03423 
03424         /* Create a mbuf to hold number being built */
03425         double_mbuf = create_Mbuf();
03426 
03427         /* Add the first character to the mbuf */
03428         ch = Pgetc (pptr);
03429         addc_to_Mbuf (double_mbuf, ch);
03430 
03431         /* If decimal point, require the next character to be
03432          * a digit.
03433          */
03434         if (ch == '.')
03435         {
03436             ch = Ppeekc (pptr);
03437             if (!isdigit(ch))
03438             {
03439                 Perror (pptr, 
03440                         "Digit required after '.' in floating-point expression.");
03441             }
03442 
03443             /* Flag that have seen the only decimal point allowed */
03444             after_decimal_point = 1;
03445         }
03446 
03447         /* Allocate placemark, since can be recursively called 
03448          * inside loop.
03449          */
03450         placemark = copy_Pptr (pptr);
03451 
03452         /* Get remaining digits */
03453         while (1)
03454         {
03455             /* Get placemark so can restore if not a character */
03456             move_Pptr (placemark, pptr);
03457 
03458             /* Get and process character */
03459             ch = Ppeekc (pptr);
03460 
03461             /* If digit, just place in string */
03462             if (isdigit (ch))
03463             {
03464                 ch = Pgetc (pptr);
03465                 addc_to_Mbuf (double_mbuf, ch);
03466             }
03467 
03468             /* If decimal point, make sure in valid spot */
03469             else if (ch == '.')
03470             {
03471                 /* Cannot have two decimal points */
03472                 if (after_decimal_point)
03473                 {
03474                     Perror (pptr, 
03475                             "Unexpected second decimal point in floating-point number.");
03476                 }
03477 
03478                 /* Cannot have decimal point in exponent */
03479                 if (in_exponent)
03480                 {
03481                     Perror (pptr, 
03482                             "Unexpected decimal point in exponent.");
03483                 }
03484 
03485                 /* Add decimal point to string */
03486                 ch = Pgetc (pptr);
03487                 addc_to_Mbuf (double_mbuf, ch);
03488 
03489                 /* Flag that after decimal point */
03490                 after_decimal_point = 1;
03491             }
03492 
03493             /* Handle exponent specifier */
03494             else if ((ch == 'e') || (ch == 'E'))
03495             {
03496                 /* Make don't already in exponent */
03497                 if (in_exponent)
03498                 {
03499                     Perror (pptr, "Unexpected second exponent specified.");
03500                 }
03501 
03502                 /* Flag that in exponent now */
03503                 in_exponent = 1;
03504 
03505                 /* Add exponent specifier to string */
03506                 ch = Pgetc (pptr);
03507                 addc_to_Mbuf (double_mbuf, ch);
03508 
03509                 /* Get optional + or - */
03510                 ch = Ppeekc (pptr);
03511                 if ((ch == '+') || (ch == '-'))
03512                 {
03513                     ch = Pgetc (pptr);
03514                     addc_to_Mbuf (double_mbuf, ch);
03515                 }
03516 
03517                 /* Make sure followed by a digit (with or without sign)*/
03518                 ch = Ppeekc (pptr);
03519                 if (!isdigit (ch))
03520                 {
03521                     Perror (pptr, "Exponent expected not '%c'.", ch);
03522                 }
03523             }
03524             else 
03525             {
03526                 /* Restore to right after number */
03527                 move_Pptr (pptr, placemark);
03528                 break;
03529             }
03530         }
03531         
03532         /* Convert double string to double */
03533         double_string = get_Mbuf_buf (double_mbuf);
03534         result = strtod (double_string, &end_ptr);
03535 
03536 
03537         /* Make sure have legal double */
03538         if (*end_ptr != 0)
03539         {
03540             Perror (pptr, "Invalid float number '%s' in expression.",
03541                     double_string);
03542         }
03543 
03544         /* Free mbuf (this also frees double_string!) */
03545         free_Mbuf (double_mbuf);
03546         
03547         free_Pptr (placemark);
03548     }
03549 
03550     /* Handle parenthesis */
03551     else if (ch == '(')
03552     {
03553         /* Get '(', get internal expression value, and ending ')' */
03554         Pgetc (pptr);
03555         result = Pcalc_C_float_expr (pptr, 0);
03556         Pskip_whitespace (pptr);
03557         if ((ch = Ppeekc (pptr)) == ')')
03558             Pgetc (pptr);
03559         else
03560         {
03561             Perror (pptr, "Floating-point expression error: ')' expected.");
03562 
03563         }
03564     }
03565 
03566     else
03567     {
03568         if (sign_operator != 0)
03569         {
03570             Perror (pptr, 
03571                     "Floating-point expression error: integer, '.', or '(' expected.");
03572         }
03573         else
03574         {
03575             Perror (pptr, 
03576                     "Floating-point expression error: integer , '(', '.', or sign (-, +) expected.");
03577         }
03578 
03579     }
03580     
03581     if (sign_operator != 0)
03582     {
03583         switch (sign_operator)
03584         {
03585           case '-':
03586             result = -result;
03587             break;
03588 
03589           case '+':
03590             break;
03591 
03592 
03593           default:
03594             Perror (pptr, "Algorithm error: undefined sign operator '%c'.",
03595                     sign_operator);
03596         }
03597     }
03598         
03599 
03600     return (result);
03601 }
03602 
03603 
03604 
03605 /* Create Pdef structure */
03606 Pdef *create_Pdef (char *name, char *val, int allow_implicit_replacement,
03607                    int level)
03608 {
03609     Pdef *def;
03610 
03611     def = (Pdef *) L_alloc (Pdef_pool);
03612 
03613     def->name = strdup (name);
03614     def->val = strdup (val);
03615     def->allow_implicit_replacement = allow_implicit_replacement;
03616     def->level = level;
03617 
03618     return (def);
03619 }
03620 
03621 void free_Pdef (void *def_v)
03622 {
03623     Pdef *def;
03624 
03625     def = (Pdef *) def_v;
03626 
03627     free (def->name);
03628     free (def->val);
03629     
03630     L_free (Pdef_pool, def);
03631 }
03632 
03633 void add_Pdef (char *name, char *val, int allow_implicit_replacement, 
03634                int level)
03635 {
03636     Psymbol *psymbol;
03637     Pdef *pdef;
03638 
03639     /* If symbol already in table, replace val, otherwise make new
03640      * entry.
03641      */
03642     psymbol = find_Psymbol (Pdef_table, name);
03643     
03644     if (psymbol == NULL)
03645     {
03646         pdef = create_Pdef (name, val, allow_implicit_replacement, level);
03647         add_Psymbol (Pdef_table, name, (void *)pdef);
03648     }
03649     else
03650     {
03651         pdef = (Pdef *) psymbol->data;
03652         /* Ignore new definition if existing defintion is at a 
03653          * higher level.
03654          */
03655         if (pdef->level > level)
03656             return;
03657 
03658         free (pdef->val);
03659         pdef->val = strdup (val);
03660         pdef->allow_implicit_replacement = allow_implicit_replacement;
03661         pdef->level = level;
03662     }
03663 }
03664 
03665 void delete_Pdef (char *name)
03666 {
03667     Psymbol *psymbol;
03668 
03669     /* Delete only if in symbol table */
03670     psymbol = find_Psymbol (Pdef_table, name);
03671     
03672     if (psymbol != NULL)
03673         delete_Psymbol (psymbol, free_Pdef);
03674 }
03675 
03676 /* Looks up name in Pdef_table and returns value if found, NULL otherwise 
03677  * If implicit replacement is set to 1, allow only definitions with
03678  * allow_implicit_replacement set to be found.
03679  */
03680 char *Plookup (char *name, int implicit_replacement)
03681 {
03682     Psymbol *symbol;
03683     Pdef *def;
03684 
03685     if ((symbol = find_Psymbol (Pdef_table, name)) != NULL)
03686     {
03687         def = (Pdef *) symbol->data;
03688 
03689         /* If an implicit replacement, allow only defs with 
03690          * allow_implicit_replacement set to be found.
03691          */
03692         if (implicit_replacement && !def->allow_implicit_replacement)
03693             return (NULL);
03694 
03695         return (def->val);
03696     }
03697 
03698     return (NULL);
03699 }

Generated on Mon Jul 21 20:28:03 2003 for TINKER LEGO DOC by doxygen 1.3.2