/*
 * File: lscan.c
 *  Contents: bscan, escan
 */

#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"


/*
 * bscan - set &subject and &pos upon entry to a scanning expression.
 *
 *  Arguments are:
 *	Arg0 - new value for &subject
 *	Arg1 - saved value of &subject
 *	Arg2 - saved value of &pos
 *
 * A variable pointing to the saved &subject and &pos is returned to be
 *  used by escan.
 */

LibDcl(bscan,2,"?")
   {
   char sbuf[MaxCvtLen];
   int rc;
   struct pf_marker *cur_pfp;

#if MACINTOSH
#if MPW
/* #pragma unused(nargs) */
#endif					/* MPW */
#endif					/* MACINTOSH */

   /*
    * Convert the new value for &subject to a string.
    */
   if (DeRef(Arg0) == Error) 
      RunErr(0, NULL);

   switch (cvstr(&Arg0, sbuf)) {
      case Cvt:
	 /*
	  * The new value for &subject wasn't a string.  Allocate the
	  *  new value and fall through.
	  */
         if (strreq(StrLen(Arg0)) == Error) 
            RunErr(0, NULL);
	 StrLoc(Arg0) = alcstr(StrLoc(Arg0), StrLen(Arg0));

      case NoCvt:
	 /*
	  * Establish a new &subject value and set &pos to 1.
	  */
	 k_subject = Arg0;
	 k_pos = 1;
         break;

      default:
         RunErr(103, &Arg0);
      }

   /* If the saved scanning environment belongs to the current procedure
    *  call, put a reference to it in the procedure frame.
    */
   if (pfp->pf_scan == NULL)
      pfp->pf_scan = &Arg1;
   cur_pfp = pfp;

   /*
    * Suspend with a variable pointing to the saved &subject and &pos.
    */
   ArgType(0) = D_Var;
   VarLoc(Arg0) = &Arg1;

   rc = interp(G_Csusp,cargp);

   if (pfp != cur_pfp)
      return rc;

   /*
    * Leaving scanning environment. Restore the old &subject and &pos values.
    */
   k_subject = Arg1;
   k_pos = IntVal(Arg2);
   if (pfp->pf_scan == &Arg1)
      pfp->pf_scan = NULL;

   if (rc == A_Resumption)
      return A_Failure;
   else
      return rc;

   }


/*
 * escan - restore &subject and &pos at the end of a scanning expression.
 *
 *  Arguments:
 *    Arg0 - variable pointing to old values of &subject and &pos
 *    Arg1 - result of the scanning expression
 *
 * The two arguments are reversed, so that the result of the scanning
 *  expression becomes the result of escan. This result is dereferenced
 *  if it refers to &subject or &pos. Then the saved values of &subject
 *  and &pos are exchanged with the current ones.
 *
 * Escan suspends once it has restored the old &subject; on failure
 *  the new &subject and &pos are "unrestored", and the failure is
 *  propagated into the using clause.
 */

LibDcl(escan,1,"escan")
   {
   struct descrip tmp;
   int rc;
   struct pf_marker *cur_pfp;

#if MACINTOSH
#if MPW
/* #pragma unused(nargs) */
#endif					/* MPW */
#endif					/* MACINTOSH */

   /*
    * Copy the result of the scanning expression into Arg0, which will
    *  be the result of the scan.
    */
   tmp = Arg0;
   Arg0 = Arg1;
   Arg1 = tmp;

   /*
    * If the result of the scanning expression is &subject or &pos,
    *  it is dereferenced.
    */
   if (((char *)BlkLoc(Arg0) == (char *)&tvky_sub) ||
      ((char *)BlkLoc(Arg0) == (char *)&tvky_pos))
         if (DeRef(Arg0) == Error) 
            RunErr(0, NULL);

   /*
    * Swap new and old values of &subject
    */
   tmp = k_subject;
   k_subject = *VarLoc(Arg1);
   *VarLoc(Arg1) = tmp;

   /*
    * Swap new and old values of &pos
    */
   tmp = *(VarLoc(Arg1) + 1);
   IntVal(*(VarLoc(Arg1) + 1)) = k_pos;
   k_pos = IntVal(tmp);

   /*
    * If we are returning to the scanning environment of the current 
    *  procedure call, indicate that it is no longed in a saved state.
    */
   if (pfp->pf_scan == VarLoc(Arg1))
      pfp->pf_scan = NULL;
   cur_pfp = pfp;

   /*
    * Suspend the value of the scanning expression.
    */

   rc = interp(G_Csusp,cargp);

   if (pfp != cur_pfp)
      return rc;

   /*
    * Re-entering scanning environment, exchange the values of &subject
    *  and &pos again
    */
   tmp = k_subject;
   k_subject = *VarLoc(Arg1);
   *VarLoc(Arg1) = tmp;

   tmp = *(VarLoc(Arg1) + 1);
   IntVal(*(VarLoc(Arg1) +1)) = k_pos;
   k_pos = IntVal(tmp);

   if (pfp->pf_scan == NULL)
      pfp->pf_scan = VarLoc(Arg1);

   if (rc == A_Resumption)
      return A_Failure;
   else
      return rc;
   }
