/*
 * File: rdoasgn.c
 *  Contents: doasgn.c
 */

#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"

/*
 * doasgn - assign value of dp2 to variable dp1.
 *  Does the work for asgn, swap, rasgn, and rswap.
 */

int doasgn(dp1, dp2)
dptr dp1, dp2;
   {
   register word i1, i2;
   register union block *bp;
   register struct b_table *tp;
   register uword hn;
   int (*putf)();
   int ret_code;
   union block *hook, **slot;
   char sbuf1[MaxCvtLen];

   tended[1] = *dp1;
   tended[2] = *dp2;
   ntended = 2;

assign:

   if (!Tvar(tended[1]))
      *(dptr)((word *)VarLoc(tended[1]) + Offset(tended[1])) = tended[2];
   else switch (Type(tended[1])) {
         case T_Tvsubs:
            /*
             * An assignment is being made to a substring trapped
             *  variable.  The tended descriptors are used as
             *  follows:
             *
             *    tended[1] - the substring trapped variable
             *    tended[2] - the value to assign
             *    tended[3] - the string containing the substring
             *    tended[4] - the substring
             *    tended[5] - the result string
             */

            /*
             * Be sure that the value to be assigned is a string.
             */
            ntended = 5;
            if (DeRef(tended[2]) == Error) {
               ntended = 0;
               return Error;
               }
            if (cvstr(&tended[2], sbuf1) == CvtFail) {
               ntended = 0;
               RetError(103, tended[2]);
               }

            /*
             * Be sure that the variable in the trapped variable points
             *  to a string.
             */
            tended[3] = BlkLoc(tended[1])->tvsubs.ssvar;
            if (DeRef(tended[3]) == Error) {
               ntended = 0;
               return Error;
               }
            if (!Qual(tended[3])) {
               ntended = 0;
               RetError(103, tended[3]);
               }
            if (strreq(StrLen(tended[3]) + StrLen(tended[2])) == Error)
               return Error;

            /*
             * Get a pointer to the substring trapped-variable block and
             *  make i1 a C-style index to the character that begins the
             *  substring.
             */
            bp = BlkLoc(tended[1]);
            i1 = bp->tvsubs.sspos - 1;

            /*
             * Make tended[4] a descriptor for the substring.
             */
            StrLen(tended[4]) = bp->tvsubs.sslen;
            StrLoc(tended[4]) = StrLoc(tended[3]) + i1;

            /*
             * Make i2 a C-style index to the character after the
             *  substring. If i2 is greater than the length of the
             *  substring, it is an error because the string being
             *  assigned will not fit.
             */
            i2 = i1 + StrLen(tended[4]);
            if (i2 > StrLen(tended[3])) {
               ntended = 0;
               RetError(-205, nulldesc);
               }

            /*
             * Form the result string.	First, copy the portion of the
             *  substring string to the left of the substring into the
             *  string space.
             */
            StrLoc(tended[5]) = alcstr(StrLoc(tended[3]), i1);

            /*
             * Copy the string to be assigned into the string space,
             *  effectively concatenating it.
             */
            alcstr(StrLoc(tended[2]), StrLen(tended[2]));

            /*
             * Copy the portion of the substring to the right of
             *  the substring into the string space, completing the
             *  result.
             */
            alcstr(StrLoc(tended[3]) + i2, StrLen(tended[3]) - i2);

            /*
             * Calculate the length of the new string.
             */
            StrLen(tended[5]) = StrLen(tended[3]) - StrLen(tended[4]) +
               StrLen(tended[2]);
            bp->tvsubs.sslen = StrLen(tended[2]);
            tended[1] = bp->tvsubs.ssvar;
            tended[2] = tended[5];

            /*
             * Everything is set up for the actual assignment.  Go
             *  back to the beginning of the routine to do it.
             */
            goto assign;

         case T_Tvtbl:
            /*
             *
             * The tended descriptors are used as follows:
             *
             *    tended[1] - the table element trapped variable
             *    tended[2] - the value to be assigned
             *    tended[3] - subscripting value
             */

            /*
             * Point bp to the trapped-variable block, point tended[3]
             *  to the subscripting value, and point tp to the table-
             *  header block.
             */
            ntended = 3;
            bp = BlkLoc(tended[1]);

            if (bp->tvtbl.title == T_Telem) {
               /*
                * The trapped-variable block already has been
                *  converted to a table-element block.  Just assign
                *  to it and return.
                */
               bp->telem.tval = tended[2];
               ntended = 0;
               return Success;
               }
            tended[3] = bp->tvtbl.tref;
            tp = (struct b_table *)bp->tvtbl.clink;

            /*
             * Get the hash number for the subscripting value and
             *  locate the chain that contains the element to which
             *  assignment is to be made.
             */
            hn = bp->tvtbl.hashnum;
            slot = hchain((union block *)tp, hn);
            bp = *slot;

            /*
             * Traverse the chain to see if the value is already in the
             *  table.  If it is there, assign to it and return.
             */
            hook = bp;
            while (bp != NULL && bp->telem.hashnum <= hn) {
               if (bp->telem.hashnum == hn &&
                  equiv(&bp->telem.tref, &tended[3])) {
                     bp->telem.tval = tended[2];
                     ntended = 0;
                     return Success;
                     }
               hook = bp;
               bp = bp->telem.clink;
               }

            /*
             * The value being assigned is new.  Increment the table
             *  size, convert the table-element trapped-variable block
             *  to a table-element block, and link it into the chain.
             */
            tp->size++;
            if (hook == bp) {		/* it goes at front of chain */
               bp = BlkLoc(tended[1]);
               bp->telem.clink = *slot;
               *slot = bp;
               }

            else {			/* it follows hook */
               bp = BlkLoc(tended[1]);
               bp->telem.clink = hook->telem.clink;
               hook->telem.clink = bp;
               }

            bp->tvtbl.title = T_Telem;
            bp->telem.tval = tended[2];
            tended[1].dword = D_Telem;
            MMShow(&tended[1], "r");
            if (TooCrowded(tp)) {
               tended[1].dword = D_Table;
               BlkLoc(tended[1]) = (union block *)tp;
               hgrow(&tended[1]);
               }
            ntended = 0;
            return Success;

         case T_Tvkywd:
            ntended = 2;
            putf = BlkLoc(tended[1])->tvkywd.putval;
            ret_code = (*putf)(&tended[2], BlkLoc(tended[1]));
            ntended = 0;
            return ret_code;

         default:
            syserr("doasgn: illegal trapped variable");
         }

   ntended = 0;
   return Success;
   }
