/*
 * File: fstranl.c
 *  Contents: any, bal, find, many, match, upto
 */

#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"


/*
 * any(c,s,i,j) - test if first character of s[i:j] is in c.
 */

FncDcl(any,4)
   {
   register word i, j;
   long l1, l2;
   int *cs, csbuf[CsetSize];
   char sbuf[MaxCvtLen];

   /*
    * Arg1 must be a cset.  Arg2 defaults to &subject; Arg3 defaults to &pos
    * if Arg2 defaulted, 1 otherwise.  Arg4 defaults to 0.
    */
   if (cvcset(&Arg1, &cs, csbuf) == CvtFail) 
      RunErr(104, &Arg1);
   switch (defstr(&Arg2, sbuf, &k_subject)) {
      case Error:
         RunErr(0, NULL);
      case Defaulted:
         if (defint(&Arg3, &l1, k_pos) == Error) 
            RunErr(0, NULL);
         break;
      default:
         if (defint(&Arg3, &l1, (word)1) == Error) 
            RunErr(0, NULL);
      }
   if (defint(&Arg4, &l2, (word)0) == Error) 
      RunErr(0, NULL);

   /*
    * Convert Arg3 and Arg4 to positions in Arg2. If Arg3 == Arg4 then the
    *  specified substring of Arg2 is empty and any fails. Otherwise make
    *  Arg3 the smaller of the two.  (Arg4 is of no further use.)
    */
   i = cvpos(l1, StrLen(Arg2));
   if (i == CvtFail)
      Fail;
   j = cvpos(l2, StrLen(Arg2));
   if (j == CvtFail)
      Fail;
   if (i == j)
      Fail;
   if (i > j)
      i = j;

   /*
    * If Arg2[Arg3] is not in the cset Arg1, fail.
    */
   j = (word)ToAscii(StrLoc(Arg2)[i-1]);
   if (!Testb(j, cs))
      Fail;

   /*
    * Return pos(s[i+1]).
    */
   Arg0.dword = D_Integer;
   IntVal(Arg0) = i + 1;
   Return;
   }


/*
 * bal(c1,c2,c3,s,i,j) - find end of a balanced substring of s[i:j].
 *  Generates successive positions.
 */

FncDcl(bal,6)
   {
   register word i, j;
   register int cnt, c;
   word t;
   long l1, l2;
   int *cs1, *cs2, *cs3;
   int csbuf1[CsetSize], csbuf2[CsetSize], csbuf3[CsetSize];
   char sbuf[MaxCvtLen];
   static int lpar[CsetSize] =	/* '(' */

#if EBCDIC != 1
      cset_display(0, 0, 0400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#else					/* EBCDIC != 1 */
      cset_display(0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#endif					/* EBCDIC != 1 */

   static int rpar[CsetSize] =	/* ')' */

#if EBCDIC != 1
      cset_display(0, 0, 01000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#else					/* EBCDIC != 1 */
      cset_display(0, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#endif					/* EBCDIC != 1 */

   /*
    *  Arg1 defaults to &cset; Arg2 defaults to '('; Arg3 defaults to
    *	')'; Arg4 to &subject; Arg5 to &pos if Arg4 defaulted, 1 otherwise;
    *	Arg6 defaults to 0.
    */
   if ((defcset(&Arg1, &cs1, csbuf1, k_cset.bits) == Error) ||
         (defcset(&Arg2, &cs2, csbuf2, lpar) == Error) ||
         (defcset(&Arg3, &cs3, csbuf3, rpar) == Error)) 
      RunErr(0, NULL);
   switch (defstr(&Arg4, sbuf, &k_subject)) {
      case Error:
         RunErr(0, NULL);
      case Defaulted:
         if (defint(&Arg5, &l1, k_pos) == Error) 
            RunErr(0, NULL);
         break;
      default:
         if (defint(&Arg5, &l1, (word)1) == Error) 
         RunErr(0, NULL);
      }
   if (defint(&Arg6, &l2, (word)0) == Error) 
      RunErr(0, NULL);

   /*
    * Convert Arg5 and Arg6 to positions in Arg4 and order them.
    */
   i = cvpos(l1, StrLen(Arg4));
   if (i == CvtFail)
      Fail;
   j = cvpos(l2, StrLen(Arg4));
   if (j == CvtFail)
      Fail;
   if (i > j) {
      t = i;
      i = j;
      j = t;
      }

   /*
    * Loop through characters in Arg4[Arg5:Arg6].  When a character in Arg2 is
    *  found, increment cnt; when a character in Arg3 is found, decrement
    *  cnt.  When cnt is 0 there have been an equal number of occurrences
    *  of characters in Arg2 and Arg3, i.e., the string to the left of
    *  i is balanced.  If the string is balanced and the current character
    *  (Arg4[i]) is in Arg1, suspend with i.  Note that if cnt drops below
    *  zero, bal fails.
    */
   cnt = 0;
   Arg0.dword = D_Integer;
   while (i < j) {
      c = ToAscii(StrLoc(Arg4)[i-1]);
      if (cnt == 0 && Testb(c, cs1)) {
         IntVal(Arg0) = i;
         Suspend;
         }
      if (Testb(c, cs2))
         cnt++;
      else if (Testb(c, cs3))
         cnt--;
      if (cnt < 0)
         Fail;
      i++;
      }
   /*
    * Eventually fail.
    */
   Fail;
   }


/*
 * find(s1,s2,i,j) - find string s1 in s2[i:j] and return position in
 *  s2 of beginning of s1.
 * Generates successive positions.
 */

FncDcl(find,4)
   {
   register word l;
   register char *s1, *s2;
   word i, j, t;
   long l1, l2;
   char sbuf1[MaxCvtLen], sbuf2[MaxCvtLen];

   /*
    * Arg1 must be a string.  Arg2 defaults to &subject; Arg3 defaults
    *  to &pos if Arg2 is defaulted, or to 1 otherwise; Arg4 defaults
    *  to 0.

    */
   if (cvstr(&Arg1, sbuf1) == CvtFail) 
      RunErr(103, &Arg1);
   switch (defstr(&Arg2, sbuf2, &k_subject)) {
      case Error:
         RunErr(0, NULL);
      case Defaulted:
         if (defint(&Arg3, &l1, k_pos) == Error) 
            RunErr(0, NULL);
         break;
      default:
         if (defint(&Arg3, &l1, (word)1) == Error) 
            RunErr(0, NULL);
      }
   if (defint(&Arg4, &l2, (word)0)== Error) 
      RunErr(0, NULL);

   /*
    * Convert Arg3 and Arg4 to absolute positions in Arg2 and order them.
    */
   i = cvpos(l1, StrLen(Arg2));
   if (i == CvtFail)
      Fail;
   j = cvpos(l2, StrLen(Arg2));
   if (j == CvtFail)
      Fail;
   if (i > j) {
      t = i;
      i = j;
      j = t;
      }

   /*
    * Loop through Arg2[i:j] trying to find Arg1 at each point, stopping
    *  when the remaining portion Arg2[i:j] is too short to contain Arg1.
    */
   Arg0.dword = D_Integer;
   while (i <= j - StrLen(Arg1)) {
      s1 = StrLoc(Arg1);
      s2 = StrLoc(Arg2) + i - 1;
      l = StrLen(Arg1);

      /*
       * Compare strings on a byte-wise basis; if the end is reached
       *  before inequality is found, suspend with the position of the
       *  string.
       */
      do {
         if (l-- <= 0) {
            IntVal(Arg0) = i;
            Suspend;
            break;
            }
         } while (*s1++ == *s2++);
      i++;
      }

   Fail;
   }

/*
 * many(c,s,i,j) - find longest prefix of s[i:j] of characters in c.
 */

FncDcl(many,4)
   {
   register word i, j, t;
   int *cs, csbuf[CsetSize];
   long l1, l2;
   char sbuf[MaxCvtLen];

   /*
    * Arg1 must be a cset.  Arg2 defaults to &subject;	Arg3 defaults to
    *  &pos if Arg2 defaulted, 1 otherwise;  Arg4 defaults to 0.
    */
   if (cvcset(&Arg1, &cs, csbuf) == CvtFail) 
      RunErr(104, &Arg1);
   switch (defstr(&Arg2, sbuf, &k_subject)) {
      case Error:
         RunErr(0, NULL);
      case Defaulted:
         if (defint(&Arg3, &l1, k_pos) == Error) 
            RunErr(0, NULL);
         break;
      default:
         if (defint(&Arg3, &l1, (word)1) == Error) 
            RunErr(0, NULL);
      }
   if (defint(&Arg4, &l2, (word)0) == Error) 
      RunErr(0, NULL);

   /*
    * Convert Arg3 and Arg4 to absolute positions and order them.
    */
   i = cvpos(l1, StrLen(Arg2));
   if (i == CvtFail)
      Fail;
   j = cvpos(l2, StrLen(Arg2));
   if (j == CvtFail)
      Fail;
   if (i == j)
      Fail;
   if (i > j) {
      t = i;
      i = j;
      j = t;
      }

   /*
    * Fail if first character of Arg2[i:j] is not in Arg1.
    */
   t = (word)ToAscii(StrLoc(Arg2)[i-1]);
   if (!Testb(t, cs))
      Fail;

   /*
    * Move i along Arg2[i:j] until a character that is not in Arg1 is found or
    *  the end of the string is reached.
    */
   i++;
   while (i < j) {
      t = (word)ToAscii(StrLoc(Arg2)[i-1]);
      if (!Testb(t, cs))
         break;
      i++;
      }

   /*
    * Return the position of the first character not in Arg1.
    */
   Arg0.dword = D_Integer;
   IntVal(Arg0) = i;
   Return;
   }

/*
 * match(s1,s2,i,j) - test if s1 is prefix of s2[i:j].
 */
FncDcl(match,4)
   {
   register word i;
   register char *s1, *s2;
   word j, t;
   long l1, l2;
   char sbuf1[MaxCvtLen], sbuf2[MaxCvtLen];

   /*
    * Arg1 must be a string.  Arg2 defaults to &subject;  Arg3 defaults
    *  to &pos if Arg2 defaulted, 1 otherwise; Arg4 defaults to 0.
    */
   if (cvstr(&Arg1, sbuf1) == CvtFail) 
      RunErr(103, &Arg1);
   switch (defstr(&Arg2, sbuf2, &k_subject)) {
      case Error:
         RunErr(0, NULL);
      case Defaulted:
         if (defint(&Arg3, &l1, k_pos) == Error) 
            RunErr(0, NULL);
         break;
      default:
         if (defint(&Arg3, &l1, (word)1) == Error) 
            RunErr(0, NULL);
      }
   if (defint(&Arg4, &l2, (word)0) == Error) 
      RunErr(0, NULL);

   /*
    * Convert Arg3 and Arg4 to absolute positions and order them.
    */
   i = cvpos(l1, StrLen(Arg2));
   if (i == CvtFail)
      Fail;
   j = cvpos(l2, StrLen(Arg2));
   if (j == CvtFail)
      Fail;
   if (i > j) {
      t = i;
      i = j;
      j = t - j;
      }
   else
      j = j - i;

   /*
    * Cannot match unless Arg1 is as long as Arg2[i:j].
    */
   if (j < StrLen(Arg1))
      Fail;

   /*
    * Compare Arg1 with Arg2[i:j] for *Arg1 characters; fail if an inequality
    *  if found.
    */
   s1 = StrLoc(Arg1);
   s2 = StrLoc(Arg2) + i - 1;
   for (j = StrLen(Arg1); j > 0; j--)
      if (*s1++ != *s2++)
         Fail;

   /*
    * Return position of end of matched string in Arg2.
    */
   Arg0.dword = D_Integer;
   IntVal(Arg0) = i + StrLen(Arg1);
   Return;
   }

/*
 * upto(c,s,i,j) - find each occurrence in s[i:j] of a character in c.
 * Generates successive positions.
 */

FncDcl(upto,4)
   {
   register word i, j, t;
   long l1, l2;
   int *cs, csbuf[CsetSize];
   char sbuf[MaxCvtLen];

   /*
    * Arg1 must be a cset.  Arg2 defaults to &subject; Arg3 defaults
    *  to &pos if Arg2 defaulted, 1 otherwise; Arg4 defaults to 0.
    */
   if (cvcset(&Arg1, &cs, csbuf) == CvtFail) 
      RunErr(104, &Arg1);
   switch (defstr(&Arg2, sbuf, &k_subject)) {
      case Error:
         RunErr(0, NULL);
      case Defaulted:
         if (defint(&Arg3, &l1, k_pos) == Error) 
            RunErr(0, NULL);
         break;
      default:
         if (defint(&Arg3, &l1, (word)1) == Error) 
            RunErr(0, NULL);
      }
   if (defint(&Arg4, &l2, (word)0) == Error)
      RunErr(0, NULL);

   /*
    * Convert Arg3 and Arg4 to positions in Arg2 and order them.
    */
   i = cvpos(l1, StrLen(Arg2));
   if (i == CvtFail)
      Fail;
   j = cvpos(l2, StrLen(Arg2));
   if (j == CvtFail)
      Fail;
   if (i > j) {
      t = i;
      i = j;
      j = t;
      }

   /*
    * Look through Arg2[i:j] and suspend position of each occurrence of
    *  of a character in Arg1.
    */
   while (i < j) {
      t = (word)ToAscii(StrLoc(Arg2)[i-1]);
      if (Testb(t, cs)) {
         Arg0.dword = D_Integer;
         IntVal(Arg0) = i;
         Suspend;
         }
      i++;
      }
   /*
    * Eventually fail.
    */
   Fail;
   }
