
/****************************************************************************/
/*                                                                          */
/*      NNstat -- Internet Statistics Collection Package                    */
/*                                                                          */
/*            Written by: Bob Braden & Annette DeSchon                      */
/*            USC Information Sciences Institute                            */
/*            Marina del Rey, California                                    */
/*                                                                          */
/*      Copyright (c) 1991 University of Southern California.               */
/*      All rights reserved.                                                */
/*                                                                          */
/*      Redistribution and use in source and binary forms are permitted     */
/*      provided that the above copyright notice and this paragraph are     */
/*      duplicated in all such forms and that any documentation,            */
/*      advertising materials, and other materials related to such          */
/*      distribution and use acknowledge that the software was              */
/*      developed by the University of Southern California, Information     */
/*      Sciences Institute.  The name of the University may not be used     */
/*      to endorse or promote products derived from this software           */
/*      without specific prior written permission.                          */
/*      THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR        */
/*      IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      */
/*      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR          */
/*      PURPOSE.                                                            */
/*                                                                          */
/****************************************************************************/

static char rcsid[]=
  "$Header: /tmp_mnt/r/jove_staff3/mogul/alpha/code/NNstat/RCS/attach.c,v 1.3 1993/08/31 22:52:22 mogul Exp $";
 
 
/* CHANGES:
 *     15Aug88 ISI: Order GenOpPtr entries wrong
 *     17Nov88 ISI: Add eqf (alias EQ) object
 *     19Nov88 ISI: Add field reference count, ComputeCuminv().
 *     21Nov88 ISI: On attach with same obj name, verify parm list.
 *     22Nov88 ISI: Attach appends rather than prepends to field chains
 *     22Nov88 ISI: Add SetEtherType()
 *     28Nov88 ISI: Generalize pseudo-program for case construction
 *     11Mar89 Merit: Added omitted initialization statement to Get_SOBJ()
 *     25Oct89 ISI: Change names of some routines
 *       Rel 2.4:
 *     01Nov89 ISI: Add new -byte objects
 *     29Nov89 ISI: Fix init bug in Get_SOBJ().
 *       Rel 3.0:
 *     ISI: Support language extensions: Boolean expressions, symif, and select.
 *     ISI: Use improved compilation algorithm.
 *     ISI: Handle indirect reference count OK (detach bug)
 *     ISI: Move GenOpPtr[] to scan.c
 *     Cisco: Error to attach record object for filter invocation,
 *     Cisco: Plug memory leaks in Get_SOBJ() & Unget_SOBJ().
 *     Aug93/DECWRL: Alpha/OSF port
 */
 
/*
 *                          attach.c
 *
 *     This file contains the routines used for performing an attach
 *     command in statspy.  Specifically, the following action routines
 *     are called from the command parser in cmds.c:
 * 
 *            doInvoke() (calls doIfInvoke(), doSelectInv() locally)
 *            doIf(), doSymIf(), doCase(), doSelect()
 *            doAppend(), doNull()
 *            doAnd(), doOr()
 *            Init_attach()
 *            Install_it()
 *            CleanUp()
 *
 */
 
#include <stdio.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include "stat.h" 
#include "sobj.h"

extern struct FI_info Fields[];  /* Everything there is to know about fields*/
extern SOBJ *SOBJ_list ; /* Head of list of SOBJ's */
extern char *savestring() ;
extern char *SOBJ_error ;
extern char  Error_msg[256];
extern int   Xdebug;
extern GENERICOP *GenOpPtr[];  /* In scan.c */
extern char *class_nick[];
SOPC *Attach_SOBJ();
             
SOPC *inv_stack[100];   /* Stack for building invocation structure */
int   inv_stkptr = 0;   /* Stack pointer for invocation structure */
#define PUSH  inv_stack[inv_stkptr++]   
#define POP   inv_stack[--inv_stkptr] 
#define TOS   inv_stack[inv_stkptr-1]
#define TLIST condp->sopc_sublist[WRRET_TRUE]
#define FLIST condp->sopc_sublist[WRRET_FALSE]

boolean LinkBoolIsRepl;  /* global switch used by LinkBool() */      
       
    
    /***********************************************************
     * 
     *    ROUTINES TO COMPILE INTERPRETIVE CODE FOR CONFIGURATION
     *
     **********************************************************/

/*
 *   boolean doInvoke( int code,  struct invokes *invokesp)
 *
 *     'code' may be one of 'IfInvoke', 'NotIfInv', 'Record', or 'SelInvoke'.
 *
 *   Call Attach_SOBJ to invoke object characterized in *invokesp, and
 *   push SOPC ptr onto top of stack.  If object previously existed and if
 *   new call specifies parameters, verify that parameters match (up to
 *   the number saved); for a new object, save up to MAXSVNPARM words with
 *   it, for later checking.  Finally, do code-specific processing.
 */ 
boolean doInvoke(code, invokesp)
    register int code;
    register struct invokes *invokesp;
    {
    SOPC *sp;
    int  ncases = (code == SelInvoke)? MAX_NCASES: (code == Record)? 0 : 2;
    extern int is_filter[];
    
    if (code != Record && 
        invokesp->inv_classno && 
        !is_filter[invokesp->inv_classno]) {
            SOBJ_error = "Filter object required";
            return FALSE;
        }

    if ((sp = Attach_SOBJ(ncases, invokesp)) == NULL) {
        if (invokesp->inv_nparms)
            free(invokesp->inv_parmp);
            return(FALSE);
    }
   
    PUSH = sp;  /* push ptr onto top of stack */
    if (code == SelInvoke)
        return(doSelectInv());
    else if (code == IfInvoke)
        return(doIfInv(FALSE));
    else if (code == NotIfInv)
        return(doIfInv(TRUE));
    else
        return(TRUE);
}  /* doInvoke() */

    
/*
 *     boolean doIfInv(boolean isnot)
 *
 *     Postfix operator for invocation of If condition, which is top of
 *     stack (TOS).
 */
boolean doIfInv(isnot)
    boolean isnot;
    {
    SOPC *condp = TOS;
         /* Mark TRUE and FALSE stubs */
    condp->sopc_sublist[0] = (SOPC *) ((isnot)? T_STUB: F_STUB);
    condp->sopc_sublist[1] = (SOPC *) ((isnot)? F_STUB: T_STUB);
    return(TRUE);
}  /* doIfInv() */
 
 
/*
 *       Construct structure for an IF statement (filter)
 *
 *   There are three implicit operands, all on the stack: IF invocation, 
 *   TRUE alternative list, and FALSE alternative list.  They are 
 *   in the wrong order (unless isnot is true); reverse the order, and call
 *   the common routine.
 */
boolean doIf(isnot) 
    boolean isnot;  /* Note: This parm included for compatibility with earlier
                       versions */
    {
    register SOPC *Tp, *Fp, *condp = inv_stack[inv_stkptr-3];
    
    if (!isnot) {
        Fp = POP;
        Tp = POP;
    }
    else {
        Tp = POP;
        Fp = POP;
    }
    condp->sopc_rc = 0;
    LinkBool(condp, T_STUB, Tp);
    LinkBool(condp, F_STUB, Fp);
    NormForm(condp);  
        /* Convert to normal form (all chained from sublist[0])*/
    if (Xdebug) {
        printf("\nBoolean ==>\n");
        printForest(condp);
    }
    return(TRUE);
} /* doIf() */

    
/*
 *   doSymIf()
 *
 *    Same call environment as doIf().  Use identity:
 *       symif Cond {TList} else {Flist}  ===
 *
 *         if Cond  {Tlist} 
 *         else {
 *              if fs[Cond] {fs[Tlist]} 
 *              else {Flist} 
 *              }
 *   Where fs[] is field symmetry mapping.  In postfix, this is:
 *
 *         Cond {Tlist} fs[Cond] fs[Tlist] Flist (if) (if)
 */
boolean doSymIf()
    {
    SOPC *Flist, *Tlist, *newcopy;
    SOPC *CpyTree();
    
    Flist = POP;
    Tlist = TOS;
    
        /* Make copy of condition, but with SYM mapping of fields */
    newcopy = CpyTree(inv_stack[inv_stkptr-2], TRUE);
    if ((PUSH = newcopy) == NULL) {  /* Error... put Flist back into stack */
        TOS = Flist;
        return(FALSE);
    }
        /* Make copy of True list, but with SYM mapping of fields */
    PUSH = CpyTree(Tlist, TRUE);
    PUSH = Flist;
    
    if (!doIf(FALSE) || !doIf(FALSE))  /* sic! */
        return(FALSE);
    return(TRUE);
}
    

/*
 *  Push a null entry onto the stack, to begin new list.
 */
boolean doNull()
    {
    PUSH = NULL;
    return(TRUE);
}

    
/* 
 *   Pop list in TOS and append it to list in TOS-1 
 */
boolean doAppend()
    {
    SOPC *L = POP;
    appendto(L, &TOS);    
    return(TRUE);
} /* doAppend() */

   
/* 
 *        appendto(sp1, sp2p): Append list sp1 to list *sp2p 
 * 
 * Does linear scan of orig chain... this is a bit inefficient, but lists 
 * are typically short, and result is to keep lists in source-program order,
 * which is nice for users and for debugging.
 */
appendto(sp1, sp2p)
    register SOPC *sp1;
    SOPC **sp2p;
    {
    register SOPC *sp2 = *sp2p;
    if (sp2) {
        while (sp2->sopc_next) sp2 = sp2->sopc_next;  /* Find end of first list */
        sp2->sopc_next = sp1;                         /* and append to it */
    }
    else 
        *sp2p = sp1;  /* Orig list was null */
}

 
/*
 *  boolean doOr()
 */
boolean doOr()
    {
    SOPC *X1, *X2;
    int Fno;
    
    X2 = POP;
    X1 = TOS;
    Fno = MaxT(X1->sopc_AtFIno, X2->sopc_AtFIno);
    if (Fno < 0)
        appendto(X2, &TOS);
    else {
        LinkBool(X1, F_STUB, X2);
        X1->sopc_AtFIno = Fno;
    }
    return(TRUE);
} /* doOr() */

/*
 *  boolean doAnd()
 */
boolean doAnd()
    {
    register SOPC *X1, *X2;
    int Fno, F1, F2;
    
    X2 = POP;
    X1 = TOS;
    Fno = MaxT(F1 = X1->sopc_AtFIno, F2 = X2->sopc_AtFIno);
    if (Fno <0) {
        sprintf(SOBJ_error = Error_msg,
            "Inconsistent fields for AND: %s, %s",
            Fields[F1].field_name,
            Fields[F2].field_name);
        return(FALSE);
    }
    
    LinkBool(X1, T_STUB, X2);
    X1->sopc_AtFIno = Fno;
    return(TRUE);
} /* doAnd() */

 

/*
 *           NormForm(sopcp)
 *
 *   Convert to normal form, suitable for compilation.  For each conditional
 *   invocation in subtree headed by sopcp: chain all sublist[k]  k>0 into the
 *   sublist[0] chain, after marking the topmost entry from sublist[k]
 *   with sopc_rc = k.  Note that sublist[0] and its rc values are unchanged,
 *   since subtree may already be in normal form.
 *
 *   Also, we chain sublists in the order 1, ... ncases-1, 0, to make final
 *   program come out in logical order (as displayed by 'show' command).
 *
 */
NormForm(condp)
    SOPC *condp;
    {
    register SOPC *sp, *lastsp;
    register int i;
    SOPC dsopc;
    
    while (condp) {
        if (condp->sopc_ncases)
            for (sp = condp->sopc_sublist[0]; sp; sp = sp->sopc_next)
                NormForm(sp); /* Normalize sublist[0] first */
             
        lastsp = &dsopc;  /* point to dummy SOPC */
        for (i=1; i<condp->sopc_ncases; i++) {
            if (sp = condp->sopc_sublist[i]) {
                NormForm(sp);
                lastsp->sopc_next = sp;  /* append to chain */
                condp->sopc_sublist[i] = NULL;
                do {  /* mark all topmost entries of sublist */
                    lastsp = lastsp->sopc_next;
                    lastsp->sopc_rc = i;
                }  while(lastsp->sopc_next);
            }
        }
        if (lastsp != &dsopc) {
               /* Now prepend new chain to sublist[0] */
            lastsp->sopc_next = condp->sopc_sublist[0];
            condp->sopc_sublist[0] = dsopc.sopc_next;
        }
        condp = condp->sopc_next;
    }
} /* NormForm() */


/*
 *        doSelectInv()
 *
 *   Starting a select statement.  Push one empty frame onto stack
 *   for default case.
 */
boolean doSelectInv()
    {
    (TOS)->sopc_ncases = 1;
    (TOS)->sopc_flags |= FLAG_SELECT;  /* mark temporarily (for find_slct) */
    PUSH = NULL;
    return(TRUE); 
}


/*
 *      doCase(case-number, struct invokes *)
 * 
 *   Top of stack is statement list belonging to SELECT case, whose values
 *   are passed as parameters.  If word-count == 0, is default case: pop TOS
 *   and put into default frame; otherwise, add value to vector in (dummy)
 *   SELECT SOPC and increment ncases.  Note that parm list must be
 *   dynamically allocated.
 *   
 */
boolean doCase(caseno, invokesp)
    int caseno;
    struct invokes *invokesp;
    {
    int i, N;
    SOPC *sopcp;
    extern printForest();
    
    if (Xdebug) {
        printf("Case %d (", caseno);
        for (i=0;i<invokesp->inv_nparms;i++)
           printf("%d ", invokesp->inv_parmp[i]);
        printf("):\n");
        printForest(TOS);
    }
    N = find_slct();
    if (N<1) return(FALSE);
    if (caseno == 0)  {  /* default case... */
        if (inv_stack[inv_stkptr-N]) {
            SOBJ_error = ">1 default in select";
            return(FALSE);
        }
        sopcp = POP;  /* Move statement list to slot 0 */
        inv_stack[inv_stkptr-(--N)] = sopcp;
    }
    else
        inv_stack[inv_stkptr-N-1]->sopc_ncases++;
    if (!Make_Case(inv_stack[inv_stkptr-N-1]->sopc_sobjp, caseno, 
                              invokesp->inv_parmp, invokesp->inv_nparms))
        return(FALSE);
                                                  
    return(TRUE);
} /* doCase() */


/*
 *      doSelect()
 *
 *   Stack contains N cases, on top of SELECT invocation.  Scan for
 *   SELECT invocation, to learn N, and then pop all N cases from stack and
 *   chain them to select invocation.  Finally, call NormForm() to convert to
 *   normal form (all queued off sublist[0]).
 */
boolean doSelect()
    {
    int count = find_slct();
    SOPC *condp = inv_stack[inv_stkptr-count-1];
    register int i;
      

    condp->sopc_flags &= ~FLAG_SELECT;  /* Turn off special flag */
    for (i = count-1; i >= 0; i--)
        condp->sopc_sublist[i] = POP;
    NormForm(condp);
    return(TRUE);
}

    
/*
 *  Initialize for new ATTACH
 */
boolean Init_attach()
    {
    inv_stkptr = 0;
    deinitdevice();  /* Turn off packet filter */
    return(TRUE);
}
   

/*
 *   Recursive-descent compilation routine: CompileT()
 *
 *   Input: pointer to forest of trees, and MaxT over superior nodes.
 */
boolean CompileT(condp, MaxFIno)
    SOPC *condp;
    int MaxFIno;
    {
    int Fno;
    register SOPC *sp, *tp;
    SOPC *origcondp, *nextcondp, *lastcondp, *nextp;
    register int i;
    SOPC *Get_SOPC();
    
    Fno = MaxT(MaxFIno, condp->sopc_AtFIno);
    if (Fno < 0) {  /* inconsistent fields */
        sprintf(SOBJ_error = Error_msg,
            "Config Error: impossible combo of fields: %s %s",
            Fields[MaxFIno].field_name,
            Fields[condp->sopc_AtFIno].field_name);
        return(FALSE);
    }
    
    if (condp->sopc_ncases == 0) {  /* Leaf of tree (RECORD statement) */
        condp->sopc_AtFIno = Fno;
        return(TRUE);
    }
    
    sp = condp->sopc_sublist[0];  /* Subordinates: forest of trees */   
    while (sp) {  /* walk through the forest... */
        nextp = sp->sopc_next;        
                /* Recursively compile descendant.  When return, tree *sp 
                 * may have been expanded into a forest of trees; each SOPC
                 * in this list will contain MaxT down to leaf in AtFIno.
                 */
        if (!CompileT(sp, Fno))
            return(FALSE);
        sp = nextp;
    }
        
        /* Now process all subordinate SOPCs, putting together under a
         *   common invocation all those whose invocation fields are
         *   in the same equivalence set.
         */        
    sp = condp->sopc_sublist[0];  /* Subordinates: forest of trees */
    if (sp == NULL) {
        SOBJ_error = "CompileT: LOGIC ERROR\n";
        return(FALSE);
    }
    for (i=0; i < condp->sopc_ncases; i++)
        condp->sopc_sublist[i] = NULL;   
    origcondp = lastcondp = condp; /* head & tail of chain of filter invocations */
    nextcondp = condp->sopc_next;  /* original successor */
    Fno = condp->sopc_AtFIno = sp->sopc_AtFIno;  /* prime the pump */
    
    while (sp) {  /* Walk through the (expanded) forest... */
        nextp = sp->sopc_next;
        sp->sopc_next = NULL;
        
        for (tp = condp; tp != nextcondp; tp = tp->sopc_next) {
            if (Fields[tp->sopc_AtFIno].field_equivno == 
                          Fields[sp->sopc_AtFIno].field_equivno)
                break;
        }
        if (tp != nextcondp) { /* Previous cond. invocation for same equiv set */
            tp->sopc_AtFIno = MaxT(tp->sopc_AtFIno, sp->sopc_AtFIno);
        }
        else {  /* No previous cond. invocation; make copy */
            if ((tp = Get_SOPC(condp->sopc_FIptr1, condp->sopc_FIptr2,
                           condp->sopc_sobjp, "", condp->sopc_ncases)) == NULL){
                /* recover */
                return(FALSE);
            }
            tp->sopc_next = nextcondp;
            lastcondp->sopc_next = tp;
            lastcondp = tp;
            tp->sopc_AtFIno = sp->sopc_AtFIno;
            tp->sopc_rc = condp->sopc_rc;
        } 
        if (tp->sopc_AtFIno < Fno) {
            Fno = tp->sopc_AtFIno;
            origcondp = tp;
        }                  
            /* Finally, chain subordinate onto appropriate chain
             * of invocation which we have selected/constructed,
             * and go on to next subordinate in original list.
             */
        appendto(sp, &tp->sopc_sublist[sp->sopc_rc]);
        sp->sopc_rc = 0;  /* for cleanliness... */ 
        sp = nextp;
    }
    
        /* If there is >1 invocation, make later ones indirect from first.
         */
    for (tp = condp; tp != nextcondp; tp = tp->sopc_next) {
        if (tp != origcondp) {
            tp->sopc_indir = origcondp;
            origcondp->sopc_irefs++;
        }
    }
    return(TRUE);
}  /* CompileT() */


/* 
 *  Top-level of ATTACH.  
 *     (Bottom of) STACK contains ptr to beginning of chain of SOPCs.
 *     Perform Phase II of compilation to get final forest of trees for
 *     configuration.  Then append each top-level SOPC to end of appropriate 
 *     field chain.
 */
boolean Install_it()
    {
    register SOPC *sopcp, *tsopcp, **tpp;
    
    if (inv_stkptr != 1) {
        fprintf(stderr, "Stack Synch Error!\n");
        return(FALSE);
    }
    sopcp = TOS;
    if (Xdebug) {
        printf("COMPILE:\n");
        printForest(sopcp);
    }
    while (sopcp) {
        tsopcp = sopcp->sopc_next;
        if (!CompileT(sopcp, sopcp->sopc_AtFIno)) return(FALSE);
        sopcp = tsopcp;
    }
 
    if (Xdebug) {
        printf("COMPILED:\n");
        printForest(sopcp);
    }
    sopcp = POP;
    while (tsopcp = sopcp) {
        sopcp = sopcp->sopc_next ;
        tpp = &Fields[tsopcp->sopc_AtFIno].field_opcs ;
        while (*tpp)  tpp = &(*tpp)->sopc_next;
        *tpp = tsopcp ;
        tsopcp->sopc_next = NULL;
    }
    ComputeCuminv();  /* Compute cumulative invocation count */
    SetEtherType();   /* Try to narrow NIT3.x interface to specific
                       *   Ethernet type.
                       */
    return(TRUE);
}  /* Install_it() */

           
/*
 *  Routine to cleanup after Attach error.
 *
 *  Detach all invocations in each of
 *     n lists popped from top of stack.  If n is zero, clear entire stack.
 */
CleanUp(n)
    int n;
    {   
    SOPC *sopcp;
    struct ODV CUodv;
    
    CUodv.odv_cp = NULL;
    CUodv.odv_file = (Xdebug)?0:1;
    
    while (inv_stkptr) {
        sopcp = POP;
        tree_prune(&CUodv, &sopcp, "*");
        if (n == 0) continue;
        else if (n == 1) return;
        else n--;
    }
} /* CleanUp() */


/*
 *  int MaxT(FIno1, FIno2)
 *
 *  Compute MaxT function of two fields:
 *     The "deeper" of the two fields if one is ancestor of the other (or is
 *     the same; -1 if they are cousins or are pseudo-fields that cannot be
 *     compared.
 *
 */
int MaxT(FIno1, FIno2)
    register int FIno1, FIno2;
    {
    register Fno;
    
    if (FIno1 < 0 || FIno2 < 0) 
        return(-1); /* Funny pseudo-field ... cannot be compared */

    if (FIno1 == FIno2) return(FIno1);
    if (FIno1 < FIno2) {
        Fno = FIno2;
        FIno2 = FIno1;
        FIno1 = Fno;
    }
        /* Now FIno1 is larger of two.  Starting at FIno1, search up parse 
         * tree for FIno2. */
    for (Fno = FIno1; Fno > FIno2;)
        Fno = Fields[Fno].field_parent;
        
    return((Fno == FIno2)? FIno1 : -1);
} /* MaxT() */


    
 
/* Copy Tree: CpyTree(sp, issym)
 *   sp is pointer to head of tree to be copied.  issym if true causes
 *   symmetry mapping to be applied to the fields in copy.  Returns
 *   ptr to top to new tree, or NULL if it fails.  
 *
 */
SOPC *CpyTree(sp, issym)
    SOPC *sp;
    boolean issym;
    {
    int i;
    SOPC *newsp, *Get_SOPC();
        
    if ((newsp = Get_SOPC(
        (issym)? &Fields[sp->sopc_FIptr1->field_symother] : sp->sopc_FIptr1,
        (issym&&sp->sopc_FIptr2)? 
                    &Fields[sp->sopc_FIptr2->field_symother] : sp->sopc_FIptr2,
        sp->sopc_sobjp, sp->sopc_sobjp->sob_name, sp->sopc_ncases)) == NULL)
            return(NULL);
    
    newsp->sopc_rc = sp->sopc_rc;
    
    for (i = 0; i<sp->sopc_ncases; i++)
        newsp->sopc_sublist[i] = ((int) sp->sopc_sublist[i] > MAX_STUB) ?
                                    CpyTree(sp->sopc_sublist[i], issym) :   
                                    sp->sopc_sublist[i];     
    if (sp->sopc_next)   
        newsp->sopc_next = CpyTree(sp->sopc_next, issym);
    return(newsp);
}  /* CpyTree() */ 


/*
 * LinkBool(sp1, val, sp2):
 *
 *    Scan entire subtree *sp1 for occurrences of exit "val" (TRUE/FALSE);
 *    fix each to point to unique occurrence of *sp2, replicating *sp2 as
 *    necessary so result is pure tree.
 */
LinkBool(sp1, val, sp2)
    SOPC *sp1, *sp2;
    boolean val;
    {
    LinkBoolIsRepl = FALSE;  /* initialize global switch */
    LB(sp1, val, sp2);       /* and call inner recursive routine */
}   /* LinkBool() */
    
/*
 *  Inner recursive routine for LinkBool()
 */
LB(sp1, val, sp2)
    SOPC *sp1, *sp2;
    boolean val;
    {
    int i;  
    while (sp1) {
        for (i=0; i<sp1->sopc_ncases; i++) {
            if ((int) sp1->sopc_sublist[i] == val) {
                /* Found an occurrence of 'val' */
                if (LinkBoolIsRepl&&((int)sp2>MAX_STUB))
                    sp2 = CpyTree(sp2, FALSE);
                sp1->sopc_sublist[i] = sp2;
                LinkBoolIsRepl = TRUE;
            }
            else if ((int) sp1->sopc_sublist[i] > MAX_STUB)
                LB(sp1->sopc_sublist[i], val, sp2);
        }
        sp1 = sp1->sopc_next;
    }
}  /* LB() */


/*
 *  Scan down stack for first uncompiled SELECT invocation; 
 *  return # of intervening stackframes, or -1 if not found.
 */
int find_slct()
    {
    register int i;
    register SOPC *sp;
    
    for (i=inv_stkptr; i>1; i--) {
        if ((sp = inv_stack[i-1]) && (sp->sopc_flags & FLAG_SELECT))
            return(inv_stkptr-i);
    } 
    return(-1);
} /* find_slct() */

    
    /***************************************************************
     * 
     *     ROUTINES TO CREATE, ATTACH, AND DELETE OBJECTS
     *
     *         SOPC  *Attach_SOBJ();  SOPC *Get_SOPC()
     *
     ***************************************************************/
/*
 *
 *    SOPC *Attach_SOBJ( ncases, &(struct invokes))
 *
 *  Find/create object and set up invocation of it from specified field.
 *  Return pointer to SOPC iff no error occurs.
 */
SOPC *Attach_SOBJ( ncases, invokesp)
    int ncases;
    register struct invokes *invokesp;
    {
    char  *Objname = invokesp->inv_objname;
    register FIELD *FIptr1, *FIptr2 = NULL ;
    register SOBJ *sobjp ;
    SOPC  *sopcp;
    FIELD *find_field();
    SOBJ  *Get_SOBJ();
    SOPC  *Get_SOPC();
   
    if ((FIptr1 = find_field(invokesp->inv_field1)) == NULL) { 
        attach_error("Bad field name", invokesp->inv_field1);
        return(NULL);
    }       
    if (*invokesp->inv_field2 && 
            (FIptr2 = find_field(invokesp->inv_field2)) == NULL) {
        attach_error("Bad field name", invokesp->inv_field2);
        return(NULL);
    }   
    if (!strcmp(Objname, NONAME)) {
        attach_error( "Illegal name", Objname);
        return(NULL);
    }

        /* Create new object */
    if ((sobjp = Get_SOBJ(invokesp->inv_classno, invokesp->inv_objname, 
                   invokesp->inv_parmp, invokesp->inv_nparms,
                   FIptr1->field_maxlen, 
                   (FIptr2)? FIptr2->field_maxlen: 0)) == NULL) {
        return(NULL);  
    }
        /* Construct Invocation of object */
    if ((sopcp = Get_SOPC( FIptr1, FIptr2, sobjp, Objname, ncases)) == NULL) {
        Unget_SOBJ(sobjp);
        free(sopcp);
        return(NULL);
    }    

    if (Xdebug) {
        printf("Attached: SOPC=0x%x SOBJ=", sopcp);
        print_OBJ(sopcp);
        printf("\n");
    }               
    return(sopcp); 
} /* Attach_SOBJ() */


/* 
 *   SOBJ  *Get_SOBJ(classname, object-name, parmlist, nparms,
 *                                                datalen1, datalen2 )
 *
 *     Find/Create specified object. parmlistp pts to dynamically-alloc'd
 *     area of 'nparms' words, containing parameter list.
 *
 *
 */
SOBJ *Get_SOBJ(Classno, objname, parmlistp, nparms, datalen, datalen2)
    char *objname;
    u_int32 *parmlistp ;
    int Classno, datalen, datalen2, nparms;
    {
    register SOBJ *sobjp ;
    GENERICOP *genopp = GenOpPtr[Classno-1] ;
    int n  = (nparms < MAXSVNPARM)? nparms : MAXSVNPARM;
    u_int32 *savevec();
    
    if (objname[0] != '\0') {
           /* Look for existing object */
        for (sobjp = SOBJ_list; sobjp; sobjp = sobjp->sob_next ) {
            if (!strcmp(objname, sobjp->sob_name)) {
                    /* FOUND IT */
                if ((Classno)&& Classno != sobjp->sob_class) {
                    attach_error("Class Conflict for", objname);
                    return(NULL) ;
                } 
                if ((nparms) &&
                    /* New invocation has parm list. It must match parm
                     *   list of existing object!
                     */
                        ((n != sobjp->sob_nparms) || 0 !=
                    	    bcmp((char *) parmlistp, (char *) sobjp->sob_parmp, 
                    	                                n*sizeof(u_int32)))) {
                    attach_error("Parm list conflict for", objname);
                    return(NULL);
                }
                return(sobjp);  /* Use existing object */
            } 
        }
    }
    else objname = NONAME;
    
        /* New object. Must know its class... */
    if (Classno == 0) {
        attach_error("Unknown class for new object", objname) ;
        return(FALSE);
    } 

    if (n == 0) {
	parmlistp = NULL;
    }
    else
    if ((parmlistp = savevec(parmlistp,n*sizeof(u_int32))) == NULL) {
    	attach_error("Storage shortage for", objname);
    	return(NULL);
    }
       /* Call make entry point for class */
    sobjp = (*(genopp->make_xx))( objname, parmlistp, nparms,
                                           datalen, datalen2 ) ;
    if (sobjp == NULL) {
        free(parmlistp);
        return( NULL ) ;
    }
    sobjp->sob_name = savestring(objname) ;
    sobjp->sob_class = Classno ;
    sobjp->sob_nparms = nparms;
    sobjp->sob_parmp = parmlistp;
    sobjp->sob_next = SOBJ_list ;
    SOBJ_list = sobjp ;
    sobjp->sob_maketime = sobjp->sob_cleartime = CurrTime ;
    bcopy(genopp, &sobjp->sob_genop, sizeof(GENERICOP)) ;
    sobjp->sob_dleng = datalen;
    sobjp->sob_dleng2 = datalen2;
    sobjp->sob_totalno = sobjp->sob_orphans = 0;
    sobjp->sob_opccnt = sobjp->sob_lock = sobjp->sob_flags = 0;
    return(sobjp) ;
} /* Get_SOBJ() */


/*
 *      Unget_SOBJ( (SOBJ *) sobjp)
 */
Unget_SOBJ( sobjp)
    register SOBJ *sobjp;
    {
    register SOBJ **top = &SOBJ_list ;
    
            /* Must unchain from all-SOBJ list... */
    while (*top != sobjp) {
        if (*top == NULL) return;  /* SHOULD NEVER HAPPEN */
        top = &(*top)->sob_next ;
    }
    *top = (*top)->sob_next ;
    free(sobjp->sob_name) ; 
    if (sobjp->sob_parmp)
        free(sobjp->sob_parmp);
	(*(sobjp->sob_genop.delete_xx))(sobjp) ;
}

 /*  
  *   Get_SOPC(&field1, &field2, &SOBJ, objname, ncases)
  *
  *      Create invocation of object SOBJ on field(s) field1 [,field2].
  *      (objname included for error messages)
  */     
SOPC *Get_SOPC(FIptr1, FIptr2, sobjp, Objname, ncases)
    FIELD *FIptr1, *FIptr2;
    SOBJ *sobjp;
    char *Objname;
    int   ncases;
    {
    register SOPC *sopcp, **tp;
    int maxlen2 = (FIptr2)? FIptr2->field_maxlen: 0;
        
        /* Allocate space for an SOPC */
    if ((sopcp = (SOPC *) malloc(sizeof(SOPC)+(ncases-2)*sizeof(SOPC *))) ==
                                                                  NULL)  {
        SOBJ_error = "ATTACH: Severe memory shortage" ;
        return(NULL);
    }   
    sopcp->sopc_sobjp = sobjp ;
    sopcp->sopc_ncases =  ncases;
    tp = &sopcp->sopc_sublist[0];
    while (ncases-- > 0) *tp++ = NULL;
    sopcp->sopc_next = sopcp->sopc_indir = NULL;    

    if (sobjp->sob_opccnt > 0 && (
             sobjp->sob_dtype != FIptr1->field_dtype ||
             ((FIptr2) && sobjp->sob_dtype2 != FIptr2->field_dtype)))
        {
        attach_error( "Conflicting data type",  Objname) ;
    }
    else if (sobjp->sob_opccnt > 0 && (
             sobjp->sob_dleng != FIptr1->field_maxlen ||
             sobjp->sob_dleng2 != maxlen2 )) 
        {
        attach_error( "Conflicting field size",  Objname) ;
    }
    else {
        /* Everything is OK... */
        sopcp->sopc_FIptr1 =  FIptr1;  
        sopcp->sopc_FIptr2 =  FIptr2;  
        sopcp->sopc_AtFIno =
             (FIptr2 == NULL|| FIptr1->field_fino > FIptr2->field_fino) ? 
                  FIptr1->field_fino : FIptr2->field_fino ;
        sobjp->sob_dtype = FIptr1->field_dtype; 
        if (FIptr2) {
            sobjp->sob_dtype2 = FIptr2->field_dtype ;
        }
        sobjp->sob_opccnt++ ;  /* Count GETs on this object */
        sopcp->sopc_irefs = 0; /* No indirection yet... */
        sopcp->sopc_rc = 0;    /* default: chain 0 in compilation */
        return(sopcp);  /* OK */
    }
    free(sopcp);
    return(NULL); /* Failed */
} /* SOPC *Get_SOPC() */

         
/*
 *  Delete SOPC.
 *      If this was last call, delete SOBJ; 
 *      in any case, delete SOPC
 */     
DeleteSOPC(sopcp)
    register SOPC *sopcp;
    {
    SOBJ *sobjp = sopcp->sopc_sobjp;
    
    if (sobjp) { /* (not dummy SOPC) */
        if (--(sobjp->sob_opccnt) == 0) 
            Unget_SOBJ(sobjp); 
    }
    free(sopcp);
}   
    
u_int32 *savevec(cp, n)
    u_int32 *cp;
    int n;
    {
    u_int32 *sp = (u_int32 *) malloc(n);  
    /*  We are depending upon malloc to return addr on fullword boundary... */
    if (sp)
        bcopy((char *) cp, (char *) sp, n);
    return(sp);
}
          

#define MAXLINE 256
char PF[MAXLINE];   
/*
 *  printForest(SOPC *)  -- debugging print routine
 *
 */
printForest(sp)
    SOPC *sp;
    {
    PF[0] = '|';
        /* Enter recursion... */
    print_F(1, sp);
}

print_F(len, sp)
    int len;   /* Length used in prefix string PF[] */
    SOPC *sp;  /* ptr to forest to be formatted */
    {
    int i;
    
    while (sp) {
        PF[len] = '\0';
        printf("%s\n", PF);  /* print prefix alone */
        PF[len-1] = '\0';
        printf("%s[F%d-->", PF, sp->sopc_AtFIno);
        print_OBJ(sp);
        printf("]\n");
        PF[len-1] = (sp->sopc_next)?'|':' ';
        if (sp->sopc_sobjp->sob_class == SELECTOBJ) {
            printf("%s",PF);
            for (i=sp->sopc_ncases;i>0;i--)  printf("   %d", i-1);
            printf("\n");
        }
        else if (sp->sopc_ncases)
            printf("%s   T   F\n", PF);
        for (i=sp->sopc_ncases;i>0;i--){
            sprintf(&PF[len], "   %c", 
                 ((int) sp->sopc_sublist[i-1] > MAX_STUB)?'|':' ');
            len += 4;
        }
        PF[len] = '\0';
        for (i=0;i<sp->sopc_ncases;i++) {   
            if ((int) sp->sopc_sublist[i] > MAX_STUB) {
                /* Print sublist */
                sprintf(&PF[len], "   |");
                print_F(len, sp->sopc_sublist[i]);
            }
            len -= 4;
            PF[len] = '\0';
        }
        sp = sp->sopc_next;
    }
}
    
print_OBJ(sp)
    register SOPC *sp;
    {
    if (sp->sopc_rc)
        printf("RC=%d ", sp->sopc_rc);
    printf("%x %s %s(F%d:%s",
          sp->sopc_sobjp, sp->sopc_sobjp->sob_name,
            class_nick[sp->sopc_sobjp->sob_class],
            sp->sopc_FIptr1->field_fino, sp->sopc_FIptr1->field_name);
    if (sp->sopc_indir)
        printf("/*");
    if (sp->sopc_FIptr2)
        printf(",F%d:%s)", sp->sopc_FIptr2->field_fino, 
                           sp->sopc_FIptr2->field_name);
    else
        printf(")");
}  
