text: /*
  **++
  **  FACILITY:	MMK
  **
  **  ABSTRACT:	Description file parser.
  **
  **  MODULE DESCRIPTION:
  **
  **  	This module contains routine PARSE_DESCRIP.
  **
  **
  **  AUTHOR: 	    M. Madison
  **
  **  Copyright (c) 2008, Matthew Madison.
  **  Copyright (c) 2014, Endless Software Solutions.
  **  
  **  All rights reserved.
  **  
  **  Redistribution and use in source and binary forms, with or without
  **  modification, are permitted provided that the following conditions
  **  are met:
  **  
  **      * Redistributions of source code must retain the above
  **        copyright notice, this list of conditions and the following
  **        disclaimer.
  **      * Redistributions in binary form must reproduce the above
  **        copyright notice, this list of conditions and the following
  **        disclaimer in the documentation and/or other materials provided
  **        with the distribution.
  **      * Neither the name of the copyright owner nor the names of any
  **        other contributors may be used to endorse or promote products
  **        derived from this software without specific prior written
  **        permission.
  **  
  **  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  **  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  **  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  **  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  **  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  **  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  **  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  **  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  **  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  **
  **  CREATION DATE:  20-AUG-1992
  **
  **  MODIFICATION HISTORY:
  **
  **  	20-AUG-1992 V1.0    Madison 	Initial coding.
  **  	29-SEP-1992 V1.1    Madison 	Support OBJREFs.
  **  	12-OCT-1992 V1.1-1  Madison 	Fix some dependency rule parsing problems.
  **  	02-APR-1993 V1.2    Madison 	Support '-' cmd pfx, comments in cmds.
  **  	29-APR-1993 V1.2-1  Madison 	Fix dependency target handling.
  **  	04-JUN-1993 V1.3    Madison 	Support for new directives.
  **  	28-OCT-1993 V1.3-1  Madison 	Fix behavior of .SUFFIXES directive,
  **  	    	    	    	    	    symbol definitions.
  **  	12-DEC-1993 V1.4    Madison 	Support multiple dependencies for
  **  	    	    	    	    	    single object.
  **  	15-DEC-1993 V1.4-1  Madison 	Fix @-, -@ in action lines.
  **  	03-MAR-1994 V1.4-2  Madison 	Resolve symbols on all lines.
  **  	04-APR-1994 V1.4-3  Madison 	Don't resolve symbols on _all_ lines (!)
  **  	08-APR-1994 V1.4-4  Madison 	Fix space-sep lists on lhs of dep rules.
  **  	28-JUN-1994 V1.4-5  Madison 	Link object module to file in target spec.
  **  	12-JUL-1994 V1.5    Madison 	Fix conditionals, add :: support.
  **  	14-JUL-1994 V1.6    Madison 	Add prefixing on inference rules.
  **  	22-AUG-1994 V1.6-1  Madison 	Remove LIBMOD check on dependency creation.
  **  	18-OCT-1994 V1.6-2  Madison 	Don't free line until after we signal error!
  **  	02-DEC-1994 V1.7    Madison 	Add line numbers to syntax error msgs.
  **  	28-DEC-1994 V1.8    Madison 	Allow multiple dependencies for a target
  **  	    	    	    	    	  as long as only one has an action list.
  **  	    	    	    	    	  Add deferred symbol references.
  **  	    	    	    	    	  Add .IF directive.
  **  	10-JAN-1995 V1.8-1  Madison 	"defined" means set to non-null value.
  **  	11-JAN-1995 V1.8-2  Madison 	Clear current_deplist on rule or .FIRST/.LAST.
  **  	21-JUN-1995 V1.8-3  Madison 	Change to Define_Symbol.
  **  	22-JUN-1995 V1.8-4  Madison 	Fix for CMD_TEXT ACCVIO.
  **  	09-OCT-1995 V1.8-5  Madison 	Fix erroneous free(), 0-length malloc()s.
  **  	29-MAY-1996 V1.8-6  Madison 	Fix ${} handling.
  **	22-DEC-1996 V1.9    Madison 	Add support for FIRST/LAST on rules.
  **  	27-DEC-1998 V2.0    Madison 	Cleanup, add support for .IFNDEF.
  **      30-MAR-2001 V2.0-1  Madison     Fix comma in SYM2DEP handling.
  **      03-MAY-2004 V2.1    Madison     Integrate IA64 changes.
  **	10-OCT-2008 V2.2    Sneddon	Added support for more MMS features
  **					  as well as .CASE_SENSITIVE. This feature
  **					  was part of another fork of MMK by
  **					  Kris Clippeleyr.
  **	01-JUL-2009 V2.3    Sneddon	Changed definition for tpa0.  Now
  **					 works better with older compilers.
  **	16-APR-2010 V2.3-1  Sneddon	Fix symnam to be length of MMK_S_SYMBOL.
  **	07-JUL-2012 V2.4    Sneddon	Added support for '|='.
  **	25-JUL-2012 V2.4-1  Sneddon	Add some comments to sym_do_actrtn.
  **	21-FEB-2013 V2.5    Sneddon	Change .IF handling to call
  **					 Resolve_Symbol so we catch function
  **					 calls.
  **	05-MAR-2013 V2.6    Sneddon	Add support for immediate evaluation
  **					 assignment.
  **	01-MAY-2013 V2.6-1  Sneddon	#68: Updated all mention of '!=' to
  **					 be '|='.
  **	08-JUN-2014 V2.7    Sneddon     #82: Add support for .SUFFIXES_*
  **  	13-JUN-2014 V2.8    Sneddon	Changes to Define_Symbol args.
  **--
  */
  #pragma module PARSE_DESCRIP "V2.7"
  #include "mmk.h"
  #include "globals.h"
  #include <tpadef.h>
  
  #pragma nostandard
      globalvalue unsigned int LIB$_SYNTAXERR;
  #pragma standard
  
  /*
  ** TPARSE context block.  A basic TPARSE block plus some
  ** extras of our own.
  */
  
  #define TPA_C_LENGTH	(TPA$C_LENGTH0+16)
  #define TPA_K_COUNT 	(TPA$K_COUNT0+4)
  
      struct TPABLK {
      	struct tpadef	   tpa0;
      	char    	  *tpa_l_stringbase;
      	char    	  *tpa_l_upbase;
      	FILEHANDLE  	  *tpa_l_unit;
      	int 	    	  *tpa_l_maxlen;
      };
  
  /*
  ** Forward declarations
  */
      void parse_descrip(char *, int, FILEHANDLE *, int *, int, char *);
      int parse_store(struct TPABLK *);
      static void make_objrefs(struct QUE *, struct QUE *);
      static void copy_objrefs(struct OBJREF *, struct QUE *);
      static void sym_do_actrtn(void *, struct dsc$descriptor *);
  
  /*
  ** Parse function codes.  Must match counterparts in PARSE_TABLE.MAR.
  */
  #define PRS_K_CHECK_COND     0
  #define PRS_K_CMD_INIT	     1
  #define PRS_K_SYM_INIT	     2
  #define PRS_K_DEP_INIT	     3
  #define PRS_K_DIR_SFX	     4
  #define PRS_K_DIR_FIRST	     5
  #define PRS_K_DIR_LAST	     6
  #define PRS_K_RULE_INIT	     7
  #define PRS_K_DIR_RHS	     8
  #define PRS_K_RULE_NEWSFX    9
  #define PRS_K_RULE_SFX	    10
  #define PRS_K_CMD_NOECHO    11
  #define PRS_K_CMD_TEXT	    12
  #define PRS_K_SYM2DEP	    13
  #define PRS_K_SYM_VALUE	    14
  #define PRS_K_DEP_TRGAPP    15
  #define PRS_K_DEP_RHS	    16
  #define PRS_K_RULE_END      17
  #define PRS_K_DIR_IFDEF     18
  #define PRS_K_DIR_ELSE      19
  #define PRS_K_DIR_ENDIF	    20
  #define PRS_K_SYM2DEP2	    21
  #define PRS_K_CMD_IGNERR    22
  #define	PRS_K_DIR_SILENT    23
  #define PRS_K_DIR_IGNORE    24
  #define PRS_K_DIR_DEFAULT   25
  #define PRS_K_DIR_INCLUDE   26
  #define PRS_K_CMD_FFORCED   27
  #define PRS_K_DEP_TRGAPP2   28
  #define PRS_K_DEP_DC	    29
  #define PRS_K_RULE_INIPFX   30
  #define PRS_K_RULE_NEWPFX   31
  #define PRS_K_DIR_IFLHS	    32
  #define PRS_K_DIR_IFEQL	    33
  #define PRS_K_DIR_IFNEQ	    34
  #define PRS_K_DIR_IFRHS	    35
  #define	PRS_K_CMD_LFORCED   36
  #define PRS_K_CMD_SETFLAGS  37
  #define PRS_K_DIR_IFNDEF    38
  #define PRS_K_DIR_IFGEQ     39
  #define PRS_K_DIR_IFLEQ     40
  #define PRS_K_DIR_IFGTR     41
  #define PRS_K_DIR_IFLSS     42
  #define PRS_K_DIR_NOT       43
  #define PRS_K_DIR_AND	    44
  #define PRS_K_DIR_OR	    45
  #define PRS_K_DIR_BUILTIN   46
  #define PRS_K_DIR_CASE	    47
  #define PRS_K_DIR_ELSIF	    48
  #define PRS_K_CHECK_GNU     49
  #define PRS_K_DIR_GNU	    50
  #define PRS_K_SYM_DEFINED   51
  #define PRS_K_SYM_APPEND    52
  #define PRS_K_SYM_DO	    53
  #define PRS_K_SYM_EVAL      54
  #define PRS_K_DIR_SFX_AFTER  55
  #define PRS_K_DIR_SFX_BEFORE 56
  #define PRS_K_DIR_SFX_DELETE 57
  
  /*
  ** .IFDEF context block.  Used for tracking when we're in and out
  ** of .IFDEF/.ELSE/.ENDIF constructs in description files.
  */
  
      static struct IF {
      	struct IF *flink, *blink;
      	int do_it, in_else, matched;
      } ifque = {&ifque,&ifque,0,0};
  
      static int just_did_rule = 0;
  
  /*
  ** External references
  */
  #if defined(__ALPHA) || defined(__ia64__)
      extern int parse_state, parse_key;
      unsigned int lib$table_parse();
  #define lib$tparse lib$table_parse
  #else
      globalref parse_state, parse_key;
  #endif
  
  /*
  **++
  **  ROUTINE:	parse_descrip
  **
  **  FUNCTIONAL DESCRIPTION:
  **
  **  RETURNS:	void (errors are signaled)
  **
  **  PROTOTYPE:
  **
  **  	parse_descrip(char *line, int linelen, FILEHANDLE *newu, int *newmaxl)
  **
  **  IMPLICIT INPUTS:	All the globals.
  **
  **  IMPLICIT OUTPUTS:	All the globals.
  **
  **  COMPLETION CODES:
  **
  **
  **  SIDE EFFECTS:   	None.
  **
  **--
  */
  void parse_descrip (char *xline, int xlinelen, FILEHANDLE *newu, int *newmaxl,
      	    	    	int current_line, char *current_file) {
      
      struct TPABLK tpablk;
      char *upline, *line;
      int i, linelen;
      unsigned int status;
  
  /*
  ** Resolve symbols, leaving untouched any symbols that cannot be
  ** resolved at this point.
  */
      if (just_did_rule && xlinelen > 0 && isspace(*xline)) {
      	line = xline;
      	linelen = xlinelen;
      } else {
      	Resolve_Symbols(xline, xlinelen, &line, &linelen, 1);
      }
  
  /*
  ** To be able to match the keywords in the parse table, we must be
  ** upper case.  For those items that are case sensitive, parse_store
  ** uses the token's offset from the beginning of the upcase string
  ** as the offset into the mixed-case string for extracting the token
  ** value.
  */
      upline = malloc(linelen+1);
      memcpy(upline,line,linelen);
      *(upline+linelen) = '\0';
      upcase(upline);
  
      memset(&tpablk, 0, TPA_C_LENGTH);
  
      tpablk.tpa0.tpa$l_count = TPA_K_COUNT;
      tpablk.tpa0.tpa$l_options = TPA$M_BLANKS;
      tpablk.tpa0.tpa$l_stringcnt = linelen;
      tpablk.tpa0.tpa$l_stringptr = (unsigned int)upline;
      tpablk.tpa_l_stringbase = line;
      tpablk.tpa_l_upbase = upline;
      tpablk.tpa_l_unit = newu;
      tpablk.tpa_l_maxlen = newmaxl;
  
      status = lib$tparse(&tpablk, &parse_state, &parse_key);
      free(upline);
      if (!OK(status) && status != MMK__CONDSKIP) {
  	lib$signal(MMK__PARSERR, 2, linelen, line,
      	    	   MMK__ERRLOC,  3, current_line, strlen(current_file),
  				    current_file,
  		   status);
      }
      if (line != xline) free(line);
  
  } /* parse_descrip */
  
  /*
  **++
  **  ROUTINE:	PARSE_STORE
  **
  **  FUNCTIONAL DESCRIPTION:
  **
  **  	Action routine for use with LIB$TPARSE.
  **
  **  RETURNS:	cond_value, longword (unsigned), write only, by value
  **
  **  PROTOTYPE:
  **
  **  	PARSE_STORE  flag, inplen, inp, toklen, tok, char, number,
  **  	    	    	usrarg
  **
  ** The first eight arguments are the LIB$TPARSE standard argument block.
  ** LIB$TABLE_PARSE just passes the address of the context block, rather
  ** than passing the context fields as the routine arguments.
  **
  ** This routine is non-reentrant.
  **
  **
  **  IMPLICIT INPUTS:	None.
  **
  **  IMPLICIT OUTPUTS:	None.
  **
  **  COMPLETION CODES:
  **
  **  	
  **
  **  SIDE EFFECTS:   	None.
  **
  **--
  */
  int parse_store (struct TPABLK *tpa) {
  
      int len, i, do_it;
      char *append = 0, *cp, *cp1, *vl_cp;
      int vl_sb, vl_tp, vl_ub;
      struct SYMBOL *s;
      unsigned int status;
      struct dsc$descriptor cmd, result;
      static struct CMD *current_cmd;
      static struct SYMBOL *current_sym;
      static struct QUE *current_cmdque, trgque, srcque, refque;
      static struct RULE *current_rule, *r, *xr;
      static struct DEPEND *current_deplist = 0;
      static int current_dirtype;
      static char *sfx_ptr;
      static char *trg_str, *src_str;
      static struct dsc$descriptor iflhs, ifrhs;
      static unsigned int iftype, tmp_cmdflags;
      static unsigned int and = 0, not = 0, or = 0, elsif = 0;
      static int trg_str_size, trg_str_len;
      static int double_colon;
      struct OBJECT *obj;
      struct DEPEND *dep;
      struct IF *ifent;
  
  /*
  ** We handle .IFDEF/.ELSE/.ENDIF sequences first, since we don't want
  ** to be interpreting stuff that's inside a dead block.
  */
  
      switch (tpa->tpa0.tpa$l_param) {
      	char symnam[MMK_S_SYMBOL];
  
      case PRS_K_CHECK_COND:
      	for (ifent = ifque.flink; ifent != &ifque; ifent = ifent->flink) {
      	    if (!ifent->do_it) return MMK__CONDSKIP;
      	}
      	return SS$_NORMAL;
      	break;
  
      case PRS_K_CHECK_GNU:
  	if (gnu_syntax) return SS$_NORMAL;
  	return 0;
  	break;
  
      case PRS_K_DIR_IFDEF:
      case PRS_K_DIR_IFNDEF:
  	if (elsif) {
  	    elsif = 0;
  	    ifent = ifque.flink;
  	    if (ifent->matched) return SS$_NORMAL;
  	} else {
  	    ifent = malloc(sizeof(struct IF));
  	    ifent->matched = 0;
  	    queue_insert(ifent, &ifque);
  	}
      	strncpy(symnam, (char *)tpa->tpa0.tpa$l_tokenptr,
  		    tpa->tpa0.tpa$l_tokencnt);
      	*(symnam+tpa->tpa0.tpa$l_tokencnt) = 0;
      	s = Lookup_Symbol(symnam);
      	ifent->do_it = (s != 0) && (s->value != 0) && (s->value[0] != '\0');
      	if (tpa->tpa0.tpa$l_param == PRS_K_DIR_IFNDEF)
      	    ifent->do_it = !ifent->do_it;
      	ifent->in_else = 0;
      	return SS$_NORMAL;
  
      case PRS_K_DIR_NOT:
          not = 1;
          return SS$_NORMAL;
  
      case PRS_K_DIR_IFLHS:
  	Resolve_Symbols(tpa->tpa_l_stringbase + (((char *)tpa->tpa0.tpa$l_tokenptr)-tpa->tpa_l_upbase),
  			tpa->tpa0.tpa$l_tokencnt, &cp, &len, 0);
  	iflhs.dsc$w_length = (unsigned short)len;
      	iflhs.dsc$b_dtype = DSC$K_DTYPE_T;
      	iflhs.dsc$b_class = DSC$K_CLASS_S;
  	iflhs.dsc$a_pointer = cp;
      	return SS$_NORMAL;
  
      case PRS_K_DIR_IFEQL:
      case PRS_K_DIR_IFNEQ:
      case PRS_K_DIR_IFGEQ:
      case PRS_K_DIR_IFLEQ:
      case PRS_K_DIR_IFGTR:
      case PRS_K_DIR_IFLSS:
          iftype = tpa->tpa0.tpa$l_param;
          return SS$_NORMAL;
  
      case PRS_K_DIR_IFRHS:
  	if (and || or || elsif) {
  	    ifent = ifque.flink;
  	} else {
  	    ifent = malloc(sizeof(struct IF));
  	    ifent->matched = 0;
              queue_insert(ifent, &ifque);
  	}
  	if (ifent->matched) {
  	    elsif = 0;
  	    return SS$_NORMAL;
  	}
          /*
  	** If the length of the rhs token is zero then this is the symbol form of
  	** .IF. MMS has obsoleted .IF[N]DEF and replaced it with '.IF [.NOT] symbol'.
  	** So, we do a lookup instead. If the symbol exists, then the test result
  	** is positive.
          */
  	if (tpa->tpa0.tpa$l_tokencnt == 0) {
              strncpy(symnam, iflhs.dsc$a_pointer, iflhs.dsc$w_length);
              *(symnam+iflhs.dsc$w_length) = 0;
              s = Lookup_Symbol(symnam);
              do_it = (s != 0) && (s->value != 0) && (s->value[0] != '\0');
          } else {
  	    Resolve_Symbols(tpa->tpa_l_stringbase + (((char *)tpa->tpa0.tpa$l_tokenptr)-tpa->tpa_l_upbase),
  			    tpa->tpa0.tpa$l_tokencnt, &cp, &len, 0);
              ifrhs.dsc$w_length = (unsigned short)len;
              ifrhs.dsc$a_pointer = cp;
              ifrhs.dsc$b_dtype = DSC$K_DTYPE_T;
              ifrhs.dsc$b_class = DSC$K_CLASS_S;
              i = str$case_blind_compare(&iflhs, &ifrhs);
              switch (iftype) {
  
              case PRS_K_DIR_IFEQL:
                  do_it = (i == 0) ? 1 : 0;
                  break;
              case PRS_K_DIR_IFNEQ:
                  do_it = (i != 0) ? 1 : 0;
                  break;
              case PRS_K_DIR_IFGEQ:
                  do_it = (i >= 0) ? 1 : 0;
                  break;
              case PRS_K_DIR_IFLEQ:
                  do_it = (i <= 0) ? 1 : 0;
                  break;
              case PRS_K_DIR_IFGTR:
                  do_it = (i >  0) ? 1 : 0;
                  break;
              case PRS_K_DIR_IFLSS:
                  do_it = (i <  0) ? 1 : 0;
                  break;
              }
  	    free(ifrhs.dsc$a_pointer);
          }
  	free(iflhs.dsc$a_pointer);
          do_it = (not) ? !do_it : do_it;
  	do_it = (and) ? ifent->do_it && do_it : do_it;
  	do_it = (or ) ? ifent->do_it || do_it : do_it;
  	ifent->do_it = do_it;
          ifent->in_else = 0;
  	or = not = and = elsif = 0;
          return SS$_NORMAL;
  
      case PRS_K_DIR_AND:
  	and = 1;
  	return SS$_NORMAL;
  
      case PRS_K_DIR_OR:
  	or = 1;
  	return SS$_NORMAL;
  
      case PRS_K_DIR_ELSE:
      	ifent = ifque.flink;
      	if (ifent == &ifque) return MMK__ELSENOIF;
      	if (ifent->in_else) return MMK__ELSENOIF;
  	if (!ifent->matched) ifent->do_it = !ifent->do_it;
      	ifent->in_else = 1;
      	return SS$_NORMAL;
  
      case PRS_K_DIR_ELSIF:
  	ifent = ifque.flink;
  	if (ifent == &ifque) return MMK__ELSIFNOIF;
  	if (ifent->in_else) return MMK__ELSIFAFTELSE;
  	elsif = 1;
  	if (ifent->do_it) {
  	     ifent->matched = 1;
  	     ifent->do_it = 0;
  	}
  	return SS$_NORMAL;
  
      case PRS_K_DIR_ENDIF:
      	if (!queue_remove(ifque.flink, &ifent)) return MMK__ENDIFNOIF;
      	free(ifent);
      	return SS$_NORMAL;
  
      default:
      	break;
      }
  
      switch (tpa->tpa0.tpa$l_param) {
  
      case PRS_K_CMD_INIT:
      	current_cmd = mem_get_cmd();
      	tmp_cmdflags = 0;
      	break;
  
      case PRS_K_SYM_INIT:
      	current_sym = mem_get_symbol();
      	Resolve_Symbols((char *)tpa->tpa0.tpa$l_tokenptr,
  			    tpa->tpa0.tpa$l_tokencnt, &cp, &len, 0);
      	if (len >= sizeof(current_sym->name)) len = sizeof(current_sym->name)-1;
      	strncpy(current_sym->name, cp, len);
      	*(current_sym->name+len) = '\0';
      	free(cp);
      	current_sym->value = (char *) 0;
      	break;
  
      case PRS_K_DEP_INIT:
      	trg_str = malloc(64);
      	trg_str_len = 0;
      	trg_str_size = 64;
      	double_colon = 0;
      	break;
  
      case PRS_K_DIR_SFX:
      case PRS_K_DIR_SFX_AFTER:
      case PRS_K_DIR_SFX_BEFORE:
      case PRS_K_DIR_SFX_DELETE:
      case PRS_K_DIR_FIRST:
      case PRS_K_DIR_LAST:
      	current_dirtype = tpa->tpa0.tpa$l_param;
      	break;
  
      case PRS_K_DIR_RHS:
      	switch (current_dirtype) {
  
      	case PRS_K_DIR_SFX:
      	    if (tpa->tpa0.tpa$l_stringcnt > 0) {
      	    	Resolve_Symbols((tpa->tpa_l_stringbase+
      	    	    (((char *)tpa->tpa0.tpa$l_stringptr)-tpa->tpa_l_upbase)),
      	    	    tpa->tpa0.tpa$l_stringcnt, &cp, &len, 0);
      	    	while (len > 0 && isspace(cp[len-1])) len--;
      	    	Build_Suffix_List(cp, len);
      	    	free(cp);
      	    } else Build_Suffix_List("", 0);
      	    current_dirtype = 0;
      	    break;
          case PRS_K_DIR_SFX_AFTER:
          case PRS_K_DIR_SFX_BEFORE:
              if (tpa->tpa0.tpa$l_stringcnt > 0) {
                  char *rhs, *rhsend;
                  int rhslen;
  
                  Resolve_Symbols((tpa->tpa_l_stringbase+
                      (((char *)tpa->tpa0.tpa$l_stringptr)-tpa->tpa_l_upbase)),
                      tpa->tpa0.tpa$l_stringcnt, &rhs, &rhslen, 0);
  
                  cp = rhs;
                  rhsend = rhs+(rhslen-1);
  
                  /*
                  ** Skip leading whitespace and then count the following
                  ** non-whitespace characters (the suffix).
                  */
                  while ((cp <= rhsend) && isspace(*cp)) cp++;
                  for (len = 0; (cp+len <= rhsend) && !isspace(cp[len]); len++)
      	    	    ;
  		if (len == 0) {
      	    	    lib$signal(MMK__NOSUFFLST, 1,
      	    	    	       (current_dirtype == PRS_K_DIR_SFX_AFTER) ?
      	    	    	    	   ".SUFFIXES_AFTER" : ".SUFFIXES_BEFORE");
      	    	} else {
      	    	    struct SFX *sfx;
  
      	    	    sfx = find_suffix(cp, len);
      	    	    if (sfx == 0) {
      	    	    	lib$signal(MMK__NOTINSUFFLST, 2, len, cp);
      	    	    	sfx = suffixes.blink;
      	    	    } else {
      	    	    	if (current_dirtype == PRS_K_DIR_SFX_BEFORE)
      	    	    	    sfx = sfx->blink;
      	    	    }
  
      	    	    cp += len;
                      while (cp <= rhsend) {
                      	while ((cp <= rhsend) && isspace(*cp)) cp++;
                      	for (len = 0; (cp+len <= rhsend) && !isspace(cp[len]);
      	    	    	    	len++)
      	    	    	    ;
      	    	    	if (len > 0) {
      	    	    	    if (create_suffix(cp, len, sfx) == 0)
      	    	    	    	lib$signal(MMK__ALRINSUFFLST, 2, len, cp);
      	    	    	}
  	    	    	cp += len;
      	    	    }
                  }
                  free(rhs);
      	    	set_mmssuffixes();
              } else {
      	    	lib$signal(MMK__NOSUFFLST, 1,
      	    	    	   (current_dirtype == PRS_K_DIR_SFX_AFTER) ?
      	    	    	       ".SUFFIXES_AFTER" : ".SUFFIXES_BEFORE");
      	    }
              current_dirtype = 0;
              break;
          case PRS_K_DIR_SFX_DELETE:
              if (tpa->tpa0.tpa$l_stringcnt > 0) {
                  char *rhs, *rhsend;
                  int rhslen;
  
                  Resolve_Symbols((tpa->tpa_l_stringbase+
                      (((char *)tpa->tpa0.tpa$l_stringptr)-tpa->tpa_l_upbase)),
                      tpa->tpa0.tpa$l_stringcnt, &rhs, &rhslen, 0);
  
                  cp = rhs;
                  rhsend = rhs+(rhslen-1);
                  while (cp <= rhsend) {
                      struct SFX *sfx;
  
                      /*
                      ** Skip leading whitespace and then count the following
                      ** non-whitespace characters (the suffix).
                      */
                      while ((cp <= rhsend) && isspace(*cp)) cp++;
                      for (len = 0; (cp+len <= rhsend) && !isspace(cp[len]);
      	    	    	    len++)
      	    	    	;
      	    	    if (len > 0) {
      	    	    	sfx = find_suffix(cp, len);
      	    	    	if (sfx != 0) {
      	    	    	    queue_remove(sfx, &sfx);
      	    	    	    mem_free_sfx(sfx);
      	    	    	}
      	    	    }
      	    	    cp += len;
                  }
                  free(rhs);
      	    	set_mmssuffixes();
              } else Build_Suffix_List("", 0);
              current_dirtype = 0;
              break;
      	case PRS_K_DIR_FIRST:
      	    current_cmdque = (struct QUE *) &do_first;
      	    current_dirtype = 0;
      	    current_deplist = 0;
      	    break;
      	case PRS_K_DIR_LAST:
      	    current_cmdque = (struct QUE *) &do_last;
      	    current_dirtype = 0;
      	    current_deplist = 0;
      	    break;
      	}
      	just_did_rule = 0;
      	break;
  
      case PRS_K_DIR_SILENT:
      	if (!override_silent) verify = 0;
      	just_did_rule = 0;
      	break;
  
      case PRS_K_DIR_IGNORE:
      	if (!override_ignore) ignore = 3;
      	just_did_rule = 0;
      	break;
  
      case PRS_K_DIR_CASE:
  	if (!override_case) case_sensitive = 1;
  	just_did_rule = 0;
  	break;
  
      case PRS_K_DIR_BUILTIN:
  	if (!override_builtins) builtins = 1;
  	just_did_rule = 0;
  	break;
  
      case PRS_K_DIR_GNU:
  	if (!override_gnu_syntax) gnu_syntax = 1;
  	just_did_rule = 0;
  	break;
  
      case PRS_K_DIR_DEFAULT:
      	default_rule = mem_get_rule();
      	current_cmdque = (struct QUE *) &default_rule->cmdque;
      	current_deplist = 0;
      	just_did_rule = 0;
      	break;
  
      case PRS_K_DIR_INCLUDE:
      	i = tpa->tpa0.tpa$l_stringcnt;
      	cp = (char *)tpa->tpa0.tpa$l_stringptr;
      	while (i > 0 && isspace(*cp)) {
      	    cp++; i--;
      	}
      	if (i > 0) {
      	    while (i > 1 && isspace(*(cp+(i-1)))) i--;
      	}
      	if (i > 0) {
      	    char tmp[256];
      	    status = file_open(cp, tpa->tpa_l_unit, "SYS$DISK:[].MMS",
      	    	    	    tmp, tpa->tpa_l_maxlen);
      	    if (OK(status)) {
      	    	if (do_log) lib$signal(MMK__OPENINCL, 1, tmp);
      	    } else {
      	    	lib$signal(MMK__NOOPNINCL, 1, cp, status);
      	    	*(tpa->tpa_l_unit) = 0;
      	    }
      	}
      	just_did_rule = 0;
      	break;
  
      case PRS_K_RULE_INIT:
      case PRS_K_RULE_INIPFX:
      	current_rule = mem_get_rule();
      	current_deplist = 0;
      	sfx_ptr = current_rule->src;
      	*sfx_ptr++ = '.';
      	*sfx_ptr = '\0';
      	current_cmdque = (struct QUE *) &current_rule->cmdque;
      	if (tpa->tpa0.tpa$l_param == PRS_K_RULE_INIPFX) {
      	    current_rule->srcpfxlen = tpa->tpa0.tpa$l_tokencnt-2;
      	    if (current_rule->srcpfxlen > sizeof(current_rule->srcpfx)-1)
      	    	current_rule->srcpfxlen = sizeof(current_rule->srcpfx)-1;
      	    memcpy(current_rule->srcpfx, (char *)tpa->tpa0.tpa$l_tokenptr+1,
      	    	    	current_rule->srcpfxlen);
      	    current_rule->srcpfx[current_rule->srcpfxlen] = '\0';
      	}
      	break;
  
      case PRS_K_RULE_SFX:
      	*sfx_ptr++ = tpa->tpa0.tpa$b_char;
      	*sfx_ptr   = '\0';
      	break;
  
      case PRS_K_RULE_NEWSFX:
      	sfx_ptr = current_rule->trg;
      	*sfx_ptr++ = '.';
      	*sfx_ptr = '\0';
      	break;
  
      case PRS_K_RULE_NEWPFX:
      	current_rule->trgpfxlen = tpa->tpa0.tpa$l_tokencnt-2;
      	if (current_rule->trgpfxlen > sizeof(current_rule->trgpfx)-1)
      	    current_rule->trgpfxlen = sizeof(current_rule->trgpfx)-1;
      	memcpy(current_rule->trgpfx, (char *)tpa->tpa0.tpa$l_tokenptr+1,
      	    	    	current_rule->trgpfxlen);
      	current_rule->trgpfx[current_rule->trgpfxlen] = '\0';
      	break;
  
      case PRS_K_RULE_END:
      	if (current_rule->trg[0] == '\0' ||
      	    current_rule->src[0] == '\0') return LIB$_SYNTAXERR;
      	xr = find_rule(current_rule->trg, current_rule->src);
      	if (xr != 0) {
      	    struct RULE *lastr;
      	    lastr = 0;
      	    current_rule->parent = xr;
      	    for (r = xr; r != 0; r = r->next) {
      	    	if (r->srcpfxlen == current_rule->srcpfxlen &&
      	    	    	r->trgpfxlen == current_rule->trgpfxlen &&
      	    	    	strneql_case_blind(r->srcpfx, current_rule->srcpfx,
      	    	    	    	r->srcpfxlen) &&
      	    	    	strneql_case_blind(r->trgpfx, current_rule->trgpfx,
      	    	    	    	r->trgpfxlen)) {
      	    	    break;
      	    	}
      	    	lastr = r;
      	    }
      	    if (r == 0) {
      	    	lastr->next = current_rule;
      	    	current_rule->next = 0;
      	    } else {
      	    	current_rule->next = r->next;
      	    	if (r == xr) {
      	    	    queue_insert(current_rule, r);
      	    	    queue_remove(r, &r);
      	    	} else {
      	    	    lastr->next = current_rule;
      	    	}
      	    }
      	} else {   /* ensure that a non-prefixed rule exists ahead of any prefixed rule in the list */
      	    if (current_rule->trgpfxlen > 0 || current_rule->srcpfxlen > 0) {
      	    	struct RULE *r;
      	    	struct CMD *c, *c2;
      	    	r = mem_get_rule();
      	    	memcpy(r, current_rule, sizeof(*r));
      	    	r->cmdque.flink = r->cmdque.blink = &r->cmdque;
      	    	r->srcpfxlen = r->trgpfxlen = 0;
      	    	for (c = current_rule->cmdque.flink; c != &current_rule->cmdque; c = c->flink) {
      	    	    c2 = mem_get_cmd();
      	    	    memcpy(c2, c, sizeof(*c));
      	    	    c2->cmd = malloc(strlen(c->cmd)+1);
      	    	    strcpy(c2->cmd, c->cmd);
      	    	    queue_insert(c2, r->cmdque.blink);
      	    	}
      	    	queue_insert(r, rules.blink);
      	    	r->next = current_rule;
      	    	current_rule->parent = r;
      	    } else {
      	    	queue_insert(current_rule, rules.blink);
      	    }
      	}
      	just_did_rule = 1;
      	break;
  
      case PRS_K_CMD_NOECHO:
      	tmp_cmdflags |= CMD_M_NOECHO;
      	break;
  
      case PRS_K_CMD_IGNERR:
      	tmp_cmdflags |= CMD_M_IGNERR;
      	break;
  
      case PRS_K_CMD_FFORCED:
      	tmp_cmdflags |= CMD_M_FORCED_FIRST;
      	break;
  
      case PRS_K_CMD_LFORCED:
      	tmp_cmdflags |= CMD_M_FORCED_LAST;
      	break;
  
      case PRS_K_CMD_SETFLAGS:
      	current_cmd->flags = tmp_cmdflags;
      	tmp_cmdflags = 0;
      	break;
  
      case PRS_K_CMD_TEXT:
      	if (current_cmdque == 0) return MMK__ACTNODEPRULE;
      	current_cmd->cmd = malloc(tpa->tpa0.tpa$l_stringcnt+1);
      	strncpy(current_cmd->cmd, (tpa->tpa_l_stringbase+
      	    	(((char *)tpa->tpa0.tpa$l_stringptr)-tpa->tpa_l_upbase)),
      	    	tpa->tpa0.tpa$l_stringcnt);
      	*(current_cmd->cmd+tpa->tpa0.tpa$l_stringcnt) = '\0';
      	queue_insert(current_cmd, current_cmdque->tail);
      	current_cmd = (struct CMD *) 0;
  /*
  **  If we just did a dependency line, set up the pointers to the command
  **  queue for each dependency processed, and make sure we don't have
  **  multiple sets of action lines for multiple single-colon dependencies
  **  for one target.
  */
      	while (current_deplist != 0) {
      	    dep = current_deplist;
      	    if (dep->cmdqptr != 0 && !dep->double_colon) {
      	    	char target[256];
      	    	i = make_object_name(target, dep->target);
      	    	lib$signal(MMK__MULACTION, 2, i, target);
      	    	return MMK__MULACTION;
      	    }
      	    dep->cmdqptr = (struct CMD *) current_cmdque;
      	    current_deplist = dep->deplist_flink;
      	}
      	break;
  
      case PRS_K_SYM2DEP:
      	trg_str_size = 64;
  	trg_str = malloc(64);
  	strcpy(trg_str, current_sym->name);
  	trg_str_len = strlen(current_sym->name);
      	*(trg_str+trg_str_len) = tpa->tpa0.tpa$b_char;
      	trg_str_len++;
      	double_colon = 0;
  	mem_free_symbol(current_sym);
      	current_sym = (struct SYMBOL *) 0;
  	break;
  
      case PRS_K_SYM2DEP2:
      	trg_str_size = 64;
  	trg_str = malloc(64);
  	strcpy(trg_str, current_sym->name);
  	trg_str_len = strlen(current_sym->name);
      	double_colon = 0;
  	mem_free_symbol(current_sym);
      	current_sym = (struct SYMBOL *) 0;
  	break;
  
      case PRS_K_SYM_DEFINED:
  	if (Lookup_Symbol(current_sym->name) != (struct SYMBOL *) 0) {
      	    mem_free_symbol(current_sym);
      	    current_sym = (struct SYMBOL *) 0;
      	    just_did_rule = 0;
      	    break;
  	}
      case PRS_K_SYM_APPEND:
  	if (tpa->tpa0.tpa$l_param == PRS_K_SYM_APPEND) append = "";
      case PRS_K_SYM_VALUE:
      	if (tpa->tpa0.tpa$l_stringcnt == 0) {
      	    Define_Symbol(MMK_K_SYM_DESCRIP, current_sym->name, "", 0, append);
      	} else {
      	    Resolve_Symbols((tpa->tpa_l_stringbase+
      	    	(((char *)tpa->tpa0.tpa$l_stringptr)-tpa->tpa_l_upbase)),
      	    	tpa->tpa0.tpa$l_stringcnt, &cp, &len, 2);
  /*
  **  Change deferred symbol references, ${xxx}, into normal symbol
  **  references, $(xxx).
  */
      	    cp1 = cp;
      	    i = len;
      	    while (i > 1) {
      	    	if (*cp1 == '$' && *(cp1+1) == '{') {
      	    	    int j;
      	    	    char *cp2;
      	    	    for (j = i-2, cp2 = cp1+2; j > 0; cp2++, j--) {
      	    	    	if (*cp2 == '}') {
      	    	    	    *(cp1+1) = '(';
      	    	    	    *cp2 = ')';
      	    	    	    i -= (cp2-cp1);
      	    	    	    cp1 = cp2;
      	    	    	    break;
      	    	    	}
      	    	    }
      	    	}
      	    	cp1++; i--;
      	    }
      	    Define_Symbol(MMK_K_SYM_DESCRIP, current_sym->name, cp, len, append);
      	    free(cp);
      	}
      	mem_free_symbol(current_sym);
      	current_sym = (struct SYMBOL *) 0;
      	just_did_rule = 0;
      	break;
  
      case PRS_K_SYM_DO:
  	Resolve_Symbols((tpa->tpa_l_stringbase+
      	    (((char *)tpa->tpa0.tpa$l_stringptr)-tpa->tpa_l_upbase)),
      	    tpa->tpa0.tpa$l_stringcnt, &cp, &len, 0);
  
  	INIT_DYNDESC(result);
  	INIT_SDESC(cmd, len, cp);
  
  	sp_once(&cmd, sym_do_actrtn, &result);
  
  	Define_Symbol(MMK_K_SYM_DESCRIP, current_sym->name,
  			result.dsc$a_pointer, result.dsc$w_length);
  
  	str$free1_dx(&result);
  	free(cp);
  	mem_free_symbol(current_sym);
      	current_sym = (struct SYMBOL *) 0;
      	just_did_rule = 0;
      	break;
  
      case PRS_K_SYM_EVAL:
  	Resolve_Symbols((tpa->tpa_l_stringbase+
      	    (((char *)tpa->tpa0.tpa$l_stringptr)-tpa->tpa_l_upbase)),
      	    tpa->tpa0.tpa$l_stringcnt, &cp, &len, 0);
  	Define_Symbol(MMK_K_SYM_DESCRIP, current_sym->name, cp, len);
  	free(cp);
      	just_did_rule = 0;
      	break;
  
      case PRS_K_DEP_TRGAPP:
      	if (trg_str_len == trg_str_size) {
      	    trg_str_size += 64;
      	    trg_str = realloc(trg_str, trg_str_size);
      	}
  	if (case_sensitive) {
  	    vl_sb = (int) tpa->tpa_l_stringbase;
  	    vl_tp = (int) tpa->tpa0.tpa$l_tokenptr;
  	    vl_ub = (int) tpa->tpa_l_upbase;
  	    vl_cp = (char *) (vl_sb + vl_tp - vl_ub);
  
  	    *(trg_str+trg_str_len) = *vl_cp;
  	} else {
      	    *(trg_str+trg_str_len) = tpa->tpa0.tpa$b_char;
  	}
      	trg_str_len++;
      	break;
  
      case PRS_K_DEP_TRGAPP2:
      	if (trg_str_len > trg_str_size-2) {
      	    trg_str_size += 64;
      	    trg_str = realloc(trg_str, trg_str_size);
      	}
      	*(trg_str+trg_str_len) = ',';
  	if (case_sensitive) {
  	    vl_sb = (int) tpa->tpa_l_stringbase;
  	    vl_tp = (int) tpa->tpa0.tpa$l_tokenptr;
  	    vl_ub = (int) tpa->tpa_l_upbase;
  	    vl_cp = (char *) (vl_sb - vl_tp - vl_ub);
  
  	    *(trg_str+trg_str_len) = *vl_cp;
  	} else {
      	    *(trg_str+trg_str_len+1) = tpa->tpa0.tpa$b_char;
  	}
      	trg_str_len += 2;
      	break;
  
      case PRS_K_DEP_DC:
      	double_colon = 1;
      	break;
  
      case PRS_K_DEP_RHS: {
      	struct OBJREF *o;
      	struct OBJECT *obj2;
  
      	src_str = malloc(tpa->tpa0.tpa$l_stringcnt+1);
      	strncpy(src_str, (tpa->tpa_l_stringbase+
      	    	(((char *)tpa->tpa0.tpa$l_stringptr)-tpa->tpa_l_upbase)),
      	    	tpa->tpa0.tpa$l_stringcnt);
      	*(src_str+tpa->tpa0.tpa$l_stringcnt) = '\0';
      	trgque.head = trgque.tail = &trgque;
      	srcque.head = srcque.tail = &srcque;
      	refque.head = refque.tail = &refque;
      	Resolve_Symbols(trg_str, trg_str_len, &cp, &len, 0);
      	Parse_Objects(cp, len, &trgque, 1);
      	free(cp);
      	if (*src_str != '\0') {
      	    Resolve_Symbols(src_str, strlen(src_str), &cp, &len, 0);
      	    Parse_Objects(cp, len, &srcque, 0);
      	    free(cp);
      	}
      	make_objrefs(&refque, &srcque);
      	current_cmdque = (struct QUE *) mem_get_cmd();
      	current_cmdque->head = current_cmdque->tail = current_cmdque;
      	current_deplist = 0;
      	while (queue_remove(trgque.head, &obj)) {
      	    if ((obj2 = Find_Object(obj)) == NULL) {
      	    	Insert_Object(obj);
      	    } else {
      	    	mem_free_object(obj);
      	    	obj = obj2;
      	    }
      	    dep = find_dependency(obj, 0);
      	    if (dep == 0) {
      	    	dep = mem_get_depend();
      	    	dep->target = obj;
      	    	dep->double_colon = double_colon;
      	    	queue_insert(dep, dependencies.blink);
      	    } else {
      	    	if ((double_colon && !dep->double_colon) ||
      	    	    	(!double_colon && dep->double_colon)) {
      	    	    lib$signal(MMK__SDCMIX, 1, obj->name);
      	    	    return MMK__SDCMIX;
      	    	}
      	    	if (double_colon) {
      	    	    struct DEPEND *dep2, *dep3;
      	    	    dep2 = mem_get_depend();
      	    	    dep2->target = obj;
      	    	    dep2->double_colon = 1;
      	    	    while (dep->dc_flink != 0) dep = dep->dc_flink;
      	    	    dep->dc_flink = dep2;
      	    	    dep = dep2;
      	    	}
      	    }
      	    copy_objrefs(&dep->sources, &refque);
      	    dep->deplist_flink = current_deplist;
      	    current_deplist = dep;
      	}
      	while (queue_remove(refque.head, &o)) mem_free_objref(o);
      	free(trg_str);
      	trg_str_len = trg_str_size = 0;
      	free(src_str);
      	just_did_rule = 0;
      	break;
      }
  
      default:
      	lib$signal(MMK__PRSTBLERR, 0);
  
      }
      return SS$_NORMAL;
  
  } /* parse_store */
  
  /*
  **++
  **  ROUTINE:	make_objrefs
  **
  **  FUNCTIONAL DESCRIPTION:
  **
  **  	Duplicates a queue of objects to another queue.
  **
  **  RETURNS:	cond_value, longword (unsigned), write only, by value
  **
  **  PROTOTYPE:
  **
  **  	make_objrefs(struct QUE *destq, struct QUE *srcq);
  **
  **  IMPLICIT INPUTS:	None.
  **
  **  IMPLICIT OUTPUTS:	None.
  **
  **  COMPLETION CODES:
  **
  **
  **  SIDE EFFECTS:   	None.
  **
  **--
  */
  static void make_objrefs (struct QUE *destq, struct QUE *srcq) {
  
      struct OBJECT *obj, *obj2;
      struct OBJREF *tmp;
  
      while (queue_remove(srcq->head, &obj)) {
      	obj2 = Find_Object(obj);
      	if (obj2 == NULL) {
      	    Insert_Object(obj);
      	} else {
      	    mem_free_object(obj);
      	    obj = obj2;
      	}
          for (tmp = destq->head;
                  tmp != (struct OBJREF *) destq && tmp->obj != obj;
                  tmp = tmp->flink);
          if (tmp == (struct OBJREF *) destq) {
      	    tmp = mem_get_objref();
      	    tmp->obj = obj;
      	    queue_insert(tmp, destq->tail);
          }
      }
  
  } /* make_objrefs */
  
  /*
  **++
  **  ROUTINE:	copy_objrefs
  **
  **  FUNCTIONAL DESCRIPTION:
  **
  **  	Duplicates a queue of object references to another queue.
  **
  **  RETURNS:	cond_value, longword (unsigned), write only, by value
  **
  **  PROTOTYPE:
  **
  **  	copy_objrefs(struct QUE *destq, struct QUE *srcq);
  **
  **  IMPLICIT INPUTS:	None.
  **
  **  IMPLICIT OUTPUTS:	None.
  **
  **  COMPLETION CODES:
  **
  **
  **  SIDE EFFECTS:   	None.
  **
  **--
  */
  static void copy_objrefs (struct OBJREF *destq, struct QUE *srcq) {
  
      struct OBJREF *tmp, *obj, *o;
  
      for (obj = srcq->head; obj != (struct OBJREF *) srcq; obj = obj->flink) {
      	for (o = destq->flink; o != destq; o = o->flink)
      	    if (o->obj == obj->obj) break;
      	if (o == destq) {
      	    tmp = mem_get_objref();
      	    tmp->obj = obj->obj;
      	    queue_insert(tmp, destq->blink);
      	}
      }
  
  } /* copy_objrefs */
  
  /*
  **++
  **  ROUTINE:	sym_do_actrtn
  **
  **  FUNCTIONAL DESCRIPTION:
  **
  **	Call back routine for sp_once to accumulate output
  **  for '|='.
  **
  **  RETURNS:	None.
  **
  **  PROTOTYPE:
  **
  **      sym_do_actrtn(struct dsc$descriptor *accumulator,
  **			  struct dsc$descriptor *in) {
  **
  **  IMPLICIT INPUTS:	None.
  **
  **  IMPLICIT OUTPUTS:	None.
  **
  **  COMPLETION CODES:	None.
  **
  **
  **  SIDE EFFECTS:   	None.
  **
  **--
  */
  static void sym_do_actrtn(void *ctx,
  			  struct dsc$descriptor *in) {
  
      static const struct dsc$descriptor space = SDESC(" ");
      struct dsc$descriptor *result = ctx;
  
      str$concat(result, &space, in);
  
  } /* sym_do_actrtn */
  