/* National Institute of Standards and Technology (NIST)
/* National Computer System Laboratory (NCSL)
/* Office Systems Engineering (OSE) Group
/* ********************************************************************
/*                            D I S C L A I M E R
/*                              (March 8, 1989)
/*  
/* There is no warranty for the NIST NCSL OSE SGML parser and/or the NIST
/* NCSL OSE SGML parser validation suite.  If the SGML parser and/or
/* validation suite is modified by someone else and passed on, NIST wants
/* the parser's recipients to know that what they have is not what NIST
/* distributed, so that any problems introduced by others will not
/* reflect on our reputation.
/* 
/* Policies
/* 
/* 1. Anyone may copy and distribute verbatim copies of the SGML source
/* code as received in any medium.
/* 
/* 2. Anyone may modify your copy or copies of SGML parser source code or
/* any portion of it, and copy and distribute such modifications provided
/* that all modifications are clearly associated with the entity that
/* performs the modifications.
/* 
/* NO WARRANTY
/* ===========
/* 
/* NIST PROVIDES ABSOLUTELY NO WARRANTY.  THE SGML PARSER AND VALIDATION
/* SUITE ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
/* EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
/* THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS
/* WITH YOU.  SHOULD THE SGML PARSER OR VALIDATION SUITE PROVE DEFECTIVE,
/* YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
/* 
/* IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL NIST BE LIABLE FOR
/* DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL,
/* INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
/* INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
/* BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A
/* FAILURE OF THE PROGRAM TO OPERATE WITH PROGRAMS NOT DISTRIBUTED BY
/* NIST) THE PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF
/* SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
*/

/************************************************************************/
/*   TITLE:          SGML PARSER                                        */
/*   SYSTEM:         DOCUMENT PROCESSOR                                 */
/*   SUBSYSTEM:                                                         */
/*   SOURCE FILE:    DIINIT.C                                           */
/*   AUTHOR:         Steven Lindeman, Fred Maples                       */
/*                                                                      */
/*   DATE CREATED:                                                      */
/*   LAST MODIFIED:                                                     */
/*                                                                      */
/*                  REVISIONS                                           */
/*   WHEN      WHO            WHY                                       */
/************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include "didefs.h"
#include "diglobal.h"

/*------------------------------------------------------*/
/*         C H E C K O P T       */
/* Reads the command line for document name to  */
/* be parsed and options to be applied.      */
/*------------------------------------------------------*/
void checkopt(argc,argv,path,delete_temps,bld_ctr)
int argc;
char *argv[],path[];
BOOLEAN *delete_temps,*bld_ctr;
{
   FILE *fopen();
   int thisarg,fprintf(),fputc();
   long seekpos,atol();
   void semantic();
   char *cptr;

   if (argc < 3) {
      printf("INVALID COMMAND LINE PARAMETERS. Valid parameters are:\n");
      printf("PARSE3 <input-doc> -P<###> -T<path-name> -3<#> -N -F");
      exit(99);
   }
   if ((indoc=fopen(argv[1],"rb")) == NULL) {  /* machine dependent */
      printf("Unknown source document file '%s'\n",argv[1]);
      exit(99);
   }
#ifdef FRED
   ctrfile = get_char_mem(strlen(argv[1])+4);
   strcpy(ctrfile,argv[1]);

   for (cptr=ctrfile+strlen(ctrfile); *cptr!='.'&& *cptr!=DIR_SEPERATOR; cptr--);
   if (*cptr == '.')
      strcpy(cptr,".CTR");
   else
      strcat(ctrfile,".CTR");
#else
   ctrfile = "CTRFILE.CTR";
#endif
   fpstack[fpindx=0] = indoc;
   seekpos = atol(argv[2]+2);
   thisarg = 3;  
   *bld_ctr = TRUE;
   applic = nullfnc;
   our_fprintf = print_ctr = fprintf;
   our_fputc = put_ctr = fputc;
   path[0] = '\0';  
   *delete_temps = TRUE;

   while(argc > thisarg) {
      switch(*(argv[thisarg]+1)) {  
      case 'T':
         strcpy(path,argv[thisarg]+2);
         break;
      case 'N':
         *bld_ctr = FALSE;
         print_ctr = put_ctr = nullfnc;
         break;
      case 'F':
         *delete_temps = FALSE;
         break;
      case 'S':
         applic = semantics;
         break;
      case 'H':
         printf("\n                      SGML Document Element Parser\n");
         printf("         Parses document elements in conformance with ISO 8879\n");
         printf("\n          ------------ Version 0.16  08-Jul-87 -------------\n");
         printf("                    Assuming Core Concrete Syntax\n");
         printf("          --------------------------------------------------\n\n");
         printf("Parsing document... %s\n",argv[1]);
         break;
      }
      thisarg++;
   }
   fseek(indoc,seekpos,0);   /* seekpos is offset from file beginning */

   if (*bld_ctr && (ctrfp=fopen(ctrfile,"w")) == NULL) {
      printf("Unable to create CTR output file '%s'.\n",ctrfile);
      exit(99);
   }
   (*print_ctr)(ctrfp,"Canonical Test Result:");
   return;
}

/*------------------------------------------------------*/
/*               C H A N G E _ T R E E S                */
/*     This routine is used to ensure that 'token' is   */
/*     is a valid index into the symbol table array.    */
/*     All of the trees and AND lists are traversed     */
/*     and the 'nodeid's are replaced with the corr-  */
/*     esponding new token indexes.       */
/*------------------------------------------------------*/
void change_trees(ptr,numsym)
TNODE *ptr;
int numsym;
{
   TNODE *currp;
   register short i;

   if (ptr != NULL)
      if (ptr->nodeid == AND) {
         currp = ptr->u.llptr;
         while(currp->next != ptr->u.llptr) {
            change_trees(currp,numsym);
            currp = currp->next;
         }
         change_trees(currp,numsym);
      }
      else {
         change_trees(ptr->left,numsym);
         change_trees(ptr->u.right,numsym);
         for (i=0; i<numsym && !ptr->changed; i++)
            if (ptr->nodeid == symtable[i].tokenid) {
               ptr->nodeid = i;
               ptr->changed = TRUE;
            }
      }
   return;
}

/*------------------------------------------------------*/
/*               C H A N G E _ E X C E P T S            */
/*     This routine is used to ensure that 'token' is   */
/*     is a valid index into the symbol table array.    */
/*     All of the entries in the exception lists are    */
/*     replaced with the corresponding new token        */
/*     indexes.                                 */
/*------------------------------------------------------*/
void change_excepts(headexcptr,numsym)
EXCEPTDESC *headexcptr;
int numsym;
{
   register int i;
   EXCEPTDESC *excptr;

   /*  This code assumes that an exception cannot include or exclude itself */
   for (i=0; i < numsym; i++) {
      for (excptr=headexcptr; excptr!=NULL; excptr=excptr->nextlocal)
         if (excptr->tokenid==symtable[i].tokenid && !excptr->changed) {
            excptr->changed = TRUE;
            excptr->tokenid = i;
         }
   }
   return;
}

/*------------------------------------------------------*/
/*        B U I L D A T T R         */
/* Builds a multi-linked list of attribute def- */
/* initions from the input file 'attrfile.dat'. */
/* A pointer to the newly built list is returned   */
/* or NULL if there were no attributes found in */
/* the file.               */
/*------------------------------------------------------*/
ATTRDESC *buildattr(attrfd)
int attrfd;
{
   ATTRDESC *retptr,*thisptr;
   unsigned numattr;
   register int i,j;
   char buffer[LITLEN];

   retptr = thisptr = adalloc();  /* first one */
   read(attrfd,&numattr,sizeof(int));
   for (i=1; i<=numattr; i++) {
      memset(thisptr->attrname,'\0',NAMELEN+1);
      read(attrfd,thisptr->attrname,NAMELEN);
      NULLTERM(thisptr->attrname);
      read(attrfd,&(thisptr->dvcode),sizeof(DECLVAL));
      read(attrfd,&(thisptr->defcode),sizeof(ADFLT));
      j = 0;
      read(attrfd,buffer,sizeof(char));
      while(buffer[j] != '\0')
         read(attrfd,&(buffer[++j]),sizeof(char));
      switch(thisptr->defcode) {
      case A_UNFIXED:  
      case A_FIXED:
         thisptr->u2.currdef = get_char_mem(j+1);
         strncpy(thisptr->u2.currdef,buffer,j+1);
         break;
      default:
         thisptr->u2.currgrp = NULL; /* could set currgrp or currdef */
         break;
      }
      thisptr->groupp = buildgroup(attrfd);
      thisptr->next = (i == numattr) ? NULL : adalloc();
      thisptr = thisptr->next;
   }
   return(retptr);
}

/*------------------------------------------------------*/
/*                B U I L D E X C E P T         */
/* This routine builds a linked list containing */
/* the inclusion or exclusion exceptions for a     */
/* token.  A pointer to the newly built list is */
/* returned or NULL is returned if there were no   */
/* exceptions found in the file.       */
/*------------------------------------------------------*/
EXCEPTDESC *build_except(exceptfd)
int exceptfd;
{
   EXCEPTDESC *curr,*retptr;
   register int i;
   unsigned num_except;

   retptr = NULL;
   read(exceptfd,&num_except,sizeof(int));
   for (i=0; i<num_except; i++) {
      if (i == 0)
         curr = retptr = exalloc();
      else
         curr = curr->nextlocal = curr->nextglobal = exalloc();
      read(exceptfd,&(curr->tokenid),sizeof(int));
      curr->changed = FALSE;   /* not been changed yet for new indexes */
   }
   if (retptr != NULL)
      curr->nextlocal = curr->nextglobal = NULL;
   return(retptr);
}

/*------------------------------------------------------*/
/*                B U I L D E N T I T Y         */
/* This routine builds a linked list containing */
/* the general entity names and values.  A ptr     */
/* to the newly built list is returned or NULL  */
/* if there were no entities found in the file. */
/*------------------------------------------------------*/
ENTITYDESC *build_entity(entityfd)
int entityfd;
{
   ENTITYDESC *thisptr,*retptr;
   int length;

   retptr = NULL;             /* length of reference */
   while(read(entityfd,&length,sizeof(int)) > 0) { /* including ending null */
      if (retptr == NULL)
         thisptr = retptr = gealloc();
      else
         thisptr = thisptr->next = gealloc();
      memset(thisptr->entityname,'\0',NAMELEN+1);
      read(entityfd,thisptr->entityname,NAMELEN);  /* name of entity */
      NULLTERM(thisptr->entityname);
      read(entityfd,&(thisptr->entitytype),sizeof(int));
      thisptr->entityvalue = get_char_mem(length);
      read(entityfd,thisptr->entityvalue,length);  /* entity reference that */
   }                    /* null terminated       */
   if (retptr != NULL)
      thisptr->next = NULL;
   return(retptr);
}

/*------------------------------------------------------*/
/*       B U I L D G R O U P        */
/* Builds a singly linked list, to be part of the  */
/* multi-linked list described in BUILDATTR, which */
/* contains the members of the name token group.   */
/* If the number in the group equals zero, that is */
/* there is no group, BUILDGROUP returns NULL   */
/* else a pointer to newly build list is returned. */
/*------------------------------------------------------*/
GROUPDESC *buildgroup(attrfd)
int attrfd;
{
   GROUPDESC *retptr,*thisptr;
   unsigned numgroup;
   register int i;

   read(attrfd,&numgroup,sizeof(int));
   if (numgroup == 0)
      retptr = NULL;
   else
      retptr = thisptr = gralloc();  /* get first one */
   for (i=1; i<=numgroup; i++) {
      memset(thisptr->groupname,'\0',NAMELEN+1);
      read(attrfd,thisptr->groupname,NAMELEN);
      NULLTERM(thisptr->groupname);
      thisptr->next = (i == numgroup) ? NULL : gralloc();
      thisptr = thisptr->next;
   }
   return(retptr);
}

/*--------------------------------------------------------------*/
/*                   B U I L D _ T A B L E                      */
/* This routine is done as part of the initialization */
/* process.  It sets up memory for the symbol table of   */
/* generic identifiers and goes off to build the content */
/* model for each one.              */
/*--------------------------------------------------------------*/
build_table(path)
char path[];
{
   char filename[PATHLEN];
   register int i;
   int tablefd,count;
   SYMBREC srec;

   sprintf(filename,"%s%s",path,"dtdfile.sgm");
   if ((tablefd=open(filename,O_RDONLY)) == -1)   /* open file */
      ourexit(2,"Unknown document type definition file\n");

   read(tablefd,&count,sizeof(int));      /* read number of symbols in table */
   read(tablefd,&rootid,sizeof(int));      /* read the root symbol id */

   if ((symtable=(STPTR) malloc((count)*sizeof(STENTRY))) == NULL)
      ourexit(2,"\nInsufficient memory in parse3.\n");

   for (i=0; i < count; i++) {          /* read each entry into symtable */
      read(tablefd,&srec,sizeof(SYMBREC));
      memset(symtable[i].nametoken,'\0',NAMELEN+1);
      strncpy(symtable[i].nametoken,srec.Sname,NAMELEN);
      NULLTERM(symtable[i].nametoken);
      symtable[i].tokenid = srec.Sid;
      /* minimization, NONE, START, END, BOTH */
      symtable[i].miniexcept = srec.Smin;
      symtable[i].adptr = NULL;      /* assumes there are no attributes */
      symtable[i].inclusion = symtable[i].exclusion = NULL;
      symtable[i].content_type = ELEMENT_CONTENT;  /* just an assumption */
      symtable[i].num_open = 0;
   }

   for (i=0; i < count; i++)                 /* build the content model for each element */
      symtable[i].cmptr = buildtree(tablefd,&dontcare,&(symtable[i].content_type));
   if (close(tablefd) != 0) {
      printf("Unable to close 'dtdfile.sgm'.\n");
      exit(99);
   }
   return(count);
}

/*------------------------------------------------------*/
/*                 B U I L D T R E E                     */
/*                                                 */
/*    Called by   :INIT, BUILDTREE                 */
/*                                                 */
/*    Returns     :ptr to the root node in         */
/*                 the tree.                       */
/*                                                 */
/*    Builds the content models as binary          */
/*      trees for the given symbol table           */
/*      entry.                                     */
/*------------------------------------------------------*/
TNODE *buildtree(tablefd,oiopt,content_type)
int tablefd;
BOOLEAN *oiopt;
int *content_type;
{
   TNODE *ptr,*currp;
   DTDREC drec;
   unsigned numands;
   register int i;
   BOOLEAN leftoiopt,rightoiopt,listoiopt;

   /* If a given node has an optional occurrence indicator,
         then everything below is essentially optional.  */

   read(tablefd,&drec,sizeof(DTDREC));    /* reads nodeid & occurind */

   ptr = talloc();          /* build a node */
   ptr->nodeid = drec.Dtoken;
   ptr->copyoi = ptr->occurind = drec.Doi;
   ptr->contreq = ptr->copycontreq = drec.Dcontreq;
   ptr->changed = ptr->contref_attr = FALSE;
   switch(ptr->nodeid) {
   case COMMA:
      ptr->left = buildtree(tablefd,&leftoiopt,content_type);
      ptr->u.right = buildtree(tablefd,&rightoiopt,content_type);
      if ((leftoiopt&&rightoiopt) && (testoi(ptr)==PLUS||testoi(ptr)==ONE)) {
         *oiopt = TRUE;
         reduceoi(ptr);
      }
      else
         *oiopt = FALSE;
      break;
   case OR:
      ptr->left = buildtree(tablefd,&leftoiopt,content_type);
      ptr->u.right = buildtree(tablefd,&rightoiopt,content_type);
      if ((leftoiopt||rightoiopt) && (testoi(ptr)==PLUS||testoi(ptr)==ONE)) {
         *oiopt = TRUE;
         reduceoi(ptr);
      }
      else
         *oiopt = FALSE;
      break;
   case AND:
      read(tablefd,&numands,sizeof(int));  /* number of elem in & group */
      currp = ptr->u.llptr = buildtree(tablefd,&listoiopt,content_type);
      *oiopt = listoiopt;  /* first node in list */
      for (i=1; i<numands; i++) {
         currp->next = buildtree(tablefd,&listoiopt,content_type);
         if (listoiopt == FALSE)  /* once false, can never change */
            *oiopt = FALSE;
         currp = currp->next;
      }
      currp->next = ptr->u.llptr;    /* make it circular */
      if ((*oiopt==TRUE) && (testoi(ptr)==PLUS||testoi(ptr)==ONE))
         reduceoi(ptr);
      break;
   default:
      if (ptr->nodeid < 0)     /* ANY, CDATA, RCDATA, PCDATA, or EMPTY */
         *content_type = OTHER_CONTENT;
      *oiopt = (testoi(ptr) == OPT) ? TRUE : FALSE;
      ptr->left = NULL;   /* null left pointer */
      ptr->u.right = NULL;  /* null right pointer */
      break;
   }
   return(ptr);
}

/*--------------------------------------------------------------*/
/*                 E X P A N D _ A N Y                      */
/* This routine expands the declaration for content of   */
/* ANY to mixed content in which parsed character data   */
/* and any elements defined in the same DTD are allowed. */
/* A pointer to the newly built tree is returned.     */
/*--------------------------------------------------------------*/
TNODE *expand_any(numsym)
int numsym;
{
   register TNODE *retptr;
   static int thissym = -1;

   retptr = talloc();
   retptr->nodeid = OR;
   retptr->contreq = retptr->copycontreq = C_NEVERO;   /* not contextually required */
   retptr->changed = FALSE;
   retptr->contref_attr = FALSE;

   retptr->left = talloc();
   retptr->left->changed = retptr->left->contref_attr = FALSE;
   retptr->left->contreq =  retptr->left->copycontreq = C_NEVERO;

   switch(thissym) {
   case -1:
      retptr->left->nodeid = PCDATA;
      retptr->occurind = retptr->copyoi = '*';
      retptr->left->occurind = '*';
      break;
   default:
      retptr->left->nodeid = symtable[thissym].tokenid;
      retptr->occurind = retptr->copyoi = '?';
      retptr->left->occurind = retptr->left->copyoi = '?';
      break;
   }
   retptr->left->left = retptr->left->u.right = NULL;
   if (++thissym < numsym-1)
      retptr->u.right = expand_any(numsym);
   else {
      retptr->u.right = talloc();
      retptr->u.right->nodeid = symtable[thissym].tokenid;
      retptr->u.right->left = retptr->u.right->u.right = NULL;
      retptr->u.right->changed = retptr->u.right->contref_attr = FALSE;
      retptr->u.right->contreq = retptr->u.right->copycontreq = C_NEVERO;
      retptr->u.right->occurind = retptr->u.right->occurind = '?';
      thissym = -1;
   }
   return(retptr);
}

/*-------------------------------------------------------------------*/
/*                          I N I T                                  */
/*     Called by   :MAIN                                             */
/*     Returns     :VOID                                             */
/*                                                                   */      
/*     Initializes :symbol table, binary tree, number of symbols     */
/*                    rootid                                         */
/*                                                                   */
/*     The symbol table is an array of structures.  The structure    */
/*       has three members; nametoken, tokenid, and cmptr.           */
/*     The nametoken is a character array which contains the name    */
/*       of a token.                                                 */
/*     The tokenid is an integer used to uniquely represent the      */
/*       nametoken.                                                  */
/*     The cmptr (content model pointer) is an integer pointer that  */
/*       points to the root node of a binary tree.  The binary tree  */
/*       is used the represent the content model associated with     */
/*       each nametoken.                                             */
/*-------------------------------------------------------------------*/
void init(numsym,genthead,penthead,path)
int *numsym;
ENTITYDESC **genthead,**penthead;
char path[];
{
   BOOLEAN root_changed=FALSE;
   int gentityfd,attrfd,tokenids[GRPCNT],exceptfd,except_elt,pentityfd;
   unsigned num_elem;
   register int i;
   char filename[PATHLEN];

   sprintf(filename,"%s%s",path,"attrfile.sgm");
   if ((attrfd=open(filename,O_RDONLY)) == -1)
      ourexit(2,"Unknown attribute file\n");

   sprintf(filename,"%s%s",path,"greffile.sgm");
   if ((gentityfd=open(filename,O_RDONLY)) == -1)
      ourexit(2,"Unknown general entity file\n");

   sprintf(filename,"%s%s",path,"preffile.sgm");
   if ((pentityfd=open(filename,O_RDONLY)) == -1)
      ourexit(2,"Unknown parameter entity file\n");

   sprintf(filename,"%s%s",path,"except.sgm");
   if ((exceptfd=open(filename,O_RDONLY)) == -1)
      ourexit(2,"Unknown exception file\n");

   idhead = idrefhead = NULL;   /* linked list of ID and IDREF values  */
   /* in all the attribute specifications */
   currexcl = currincl = NULL;  /* linked list of current exclusion */
   /* and inclusion exceptions       */
   stptr = NULL;  /* head pointer of input stack */

   state = GETNEW;   
   sp = entitylevel = num_open_ms = 0;
   open_cdata_ms = open_rcdata_ms = FALSE;
   head = NULL;

   /* creates lists of general and parameter entities */
   *genthead = build_entity(gentityfd);
   *penthead = build_entity(pentityfd);

   *numsym = build_table(path);

   read(exceptfd,&num_elem,sizeof(int));
   for (i=0; i<num_elem; i++) {        /* build list of inclusion     */
      read(exceptfd,&except_elt,sizeof(int));   /* exceptions for each element */
      symtable[except_elt].inclusion = build_except(exceptfd);
   }
   read(exceptfd,&num_elem,sizeof(int));
   for (i=0; i<num_elem; i++) {         /* build list of exclusion     */
      read(exceptfd,&except_elt,sizeof(int));    /* exceptions for each element */
      symtable[except_elt].exclusion = build_except(exceptfd);
   }

   read(attrfd,&num_elem,sizeof(int));
   while(num_elem != -1) {
      for (i=0; i < num_elem; i++)
         read(attrfd,tokenids+i,sizeof(int));
      symtable[tokenids[0]].adptr = buildattr(attrfd);
      for (i=1; i < num_elem; i++)
         symtable[tokenids[i]].adptr = symtable[tokenids[0]].adptr;
      read(attrfd,&num_elem,sizeof(int));
   }
   /*  get rid of content model defined as ANY and  */
   /*  build a new model, expanded out with PCDATA  */
   /*  and every element in the symbol table     */
   for (i=0; i < *numsym; i++)
      if (symtable[i].cmptr->nodeid == ANY) {
         free((char *)symtable[i].cmptr);
         symtable[i].cmptr = expand_any(*numsym);
      }

   qsort(symtable,*numsym,sizeof(STENTRY),compare);   /* sort the table */

   for (i=0; i < *numsym; i++) {
      change_trees(symtable[i].cmptr,*numsym);
      change_excepts(symtable[i].inclusion,*numsym);
      change_excepts(symtable[i].exclusion,*numsym);
      if (rootid==symtable[i].tokenid && !root_changed) {
         root_changed = TRUE;
         rootid = i;
      }
   }
   for (i=0; i < *numsym; i++)
      symtable[i].tokenid = i;

   if (close(attrfd)!=0 || close(gentityfd)!=0 ||
       close(exceptfd)!=0 || close(pentityfd)!=0) {
      printf("Unable to close intermediate files.\n");
      exit(99);
   }
   return;
}

/*------------------------------------------------------*/
/*                C H A N G E _ R I G H T               */
/* This routine is used to keep up with the  */
/* flag called 'contreq' which is found on each */
/* node of the content model tree.  If you are  */
/* in a sequence group, and the left side wasn't   */
/* found, the tag for the token found on the right */
/* side should become never omissable.  If it   */
/* was found on the left, the right side then   */
/* becomes omissable.            */
/*------------------------------------------------------*/
void change_right(ptr,leftretval)
TNODE *ptr;
int leftretval;
{
   if (ptr != NULL)
      if (leftretval==FOUND && ptr->contreq==C_SOMETIMESO)
         if (testoi(ptr) == PLUS)
            ptr->contreq = C_FTO;
         else
            ptr->contreq = C_ALWAYSO;
      else
         if (leftretval==NFDHT && ptr->contreq==C_SOMETIMESO)
            ptr->contreq = C_NEVERO;
   return;
}
