/*
** The functions in this file implement commands that search in the forward
** and backward directions.
**
 ****************************************************************************/

#include "Include.h"

/*
** The variables matchline and matchoff hold the line
** and offset position of the *start* of the match.
 *********************************************************/

static int  matchlen;
static int  matchoff;
static int  patlenadd;
static int  lastchfjump;
static int  lastchbjump;
static int  deltaf[HICHAR];
static int  deltab[HICHAR];
static int  replen;     /* length of replacement string */

static LINE *matchline;

// Local functions

int   STD backhunt(int, int);
int   STD boundry(LINE *, int, int);
int   STD delins(int, char *, int);
int   STD expandp(char *, char *, int);
int   STD fbound(int, LINE **, int *, int);
int   STD forwhunt(int, int);
int   STD nextch(LINE **, int *, int);
int   STD readpattern(char *, char *, int);
int   STD replaces(int, int, int);
int   STD savematch(void);
void  STD rvstrcpy(char *, char *);
unsigned int   REG chcase(register unsigned int);


/// scanner
/*
** scanner -- Search for a pattern in either direction.  If found,
** reset the "." to be at the start or just after the match string,
** and (perhaps) repaint the display.
** Fast version using simplified version of Boyer and Moore
** Software-Practice and Experience, vol 10, 501-506 (1980)
**
** int   direct;        which way to go.
** int   beg_or_end;    put point at beginning or end of pattern.
** int   repeats;       search repetitions.
**********************/
int STD scanner(int direct, int beg_or_end, int repeats)
{
   register int   c;    /* character at current position */
   register char  *patptr; /* pointer into pattern */
   char  *patrn;        /* string to scan for */
   LINE  *curline;      /* current line during scan */
   int   curoff;        /* position within current line */
   LINE  *scanline;     /* current line during scanning */
   int   scanoff;    /* position in scanned line */
   int   jump;       /* next offset */

   /* If we are going in reverse, then the 'end' is actually
    * the beginning of the pattern.  Toggle it.
    */
   beg_or_end ^= direct;

   /* Another directional problem: if we are searching
    * forwards, use the pattern in pat[], and the jump
    * size in lastchfjump.  Otherwise, use the reversed
    * pattern in tap[], and the jump size of lastchbjump.
    */
   if (direct == FORWARD) {
      patrn = &pat[0];
      jump = lastchfjump;
   }
   else
   {
      patrn = &tap[0];
      jump = lastchbjump;
   }

   /* Set up local pointers to global ".".
    */
   curline = curwp->w_dotp;
   curoff = curwp->w_doto;

   /* Scan each character until we hit the head link record.
    * Get the character resolving newlines, offset
    * by the pattern length, i.e. the last character of the
    * potential match.
    */
   if (!fbound(patlenadd, &curline, &curoff, direct)) {
      do
      {
         /* Set up the scanning pointers, and save
          * the current position in case we match
          * the search string at this point.
          */
         scanline = matchline = curline;
         scanoff  = matchoff  = curoff;

         patptr = patrn;

         /* Scan through the pattern for a match.
          */
         while ((c = *patptr++) != '\0')
            if (!eq(c, nextch(&scanline, &scanoff, direct)))
               goto fail;

         /* A SUCCESSFULL MATCH!!!
          * Flag that we have moved and reset the
          * global "." pointers.
          */
         curwp->w_flag |= WFMOVE;
         if (beg_or_end == PTEND)   /* at end of string */
         {
            curwp->w_dotp = scanline;
            curwp->w_doto = scanoff;
         }
         else     /* at beginning of string */
         {
            curwp->w_dotp = matchline;
            curwp->w_doto = matchoff;
         }

         /* If we're heading in reverse, set the "match"
          * pointers to the start of the string, for savematch().
          */
         if (direct == REVERSE) {
            matchline = scanline;
            matchoff = scanoff;
         }

         if (savematch() == ABORT)
            return ABORT;

         /* Continue scanning if we haven't found
          * the nth match.
          */
         if (--repeats <= 0)
            return (TRUE);
         else
         {
            curline = scanline;
            curoff = scanoff;
         }

fail:;         /* continue to search */
      } while (!fbound(jump, &curline, &curoff, direct));
   }

   return FALSE;  /* We could not find a match */
}
//-

/// setjtable
/*
** setjtable -- Settting up search delta forward and delta backward
** tables.  The reverse search string and string lengths are
** set here, for table initialization and for substitution
** purposes.  The default for any character to jump is the
** pattern length.
 ***********************************************************************/
void STD setjtable(void)
{
   int   i;

   rvstrcpy(tap, pat);
   patlenadd = (matchlen = strlen(pat)) - 1;

   for (i = 0; i < HICHAR; i++) {
      deltaf[i] = matchlen;
      deltab[i] = matchlen;
   }

   /* Now put in the characters contained
    * in the pattern, duplicating the CASE.
    */
   for (i = 0; i < patlenadd; i++) {
      if (isletter(pat[i]))
         deltaf[(unsigned int) chcase((unsigned int) pat[i])]
          = patlenadd - i;
      deltaf[(unsigned int) pat[i]] = patlenadd - i;

      if (isletter(tap[i]))
         deltab[(unsigned int) chcase((unsigned int) tap[i])]
          = patlenadd - i;
      deltab[(unsigned int) tap[i]] = patlenadd - i;
   }

   /* The last character will have the pattern length
    * unless there are duplicates of it.  Get the number to
    * jump from the arrays delta, and overwrite with zeroes
    * in delta duplicating the CASE.
    *
    * The last character is, of course, the first character
    * of the reversed string.
    */
   lastchfjump = patlenadd + deltaf[(unsigned int) tap[0]];
   lastchbjump = patlenadd + deltab[(unsigned int) pat[0]];

   if (isletter(tap[0]))
      deltaf[(unsigned int) chcase(tap[0])] = 0;
   deltaf[(unsigned int) tap[0]] = 0;

   if (isletter(pat[0]))
      deltab[(unsigned int) chcase(pat[0])] = 0;
   deltab[(unsigned int) pat[0]] = 0;
}
//-

/// eq()
/*
** eq -- Compare two characters.  The "bc" comes from the buffer, "pc"
** from the pattern.  If we are not in EXACT mode, fold out the case.
*************************************************************************/
int STD eq(register int bc, register int pc)
{
   if ((curwp->w_bufp->b_mode & MDEXACT) == 0) {
      if (islower(bc))
         bc = chcase(bc);

      if (islower(pc))
         pc = chcase(pc);
   }

   return (bc == pc);
}
//-

/// sreplace()
/*
** sreplace -- Search and replace.
** int f;   default flag
** int n;   # of repetitions wanted
**********/
int STD sreplace(int f, int n)
{
   return (replaces(FALSE, f, n));
}
//-

/// qreplace()
/*
** qreplace -- search and replace with query.
**
** int f;         default flag
** int n;         # of repetitions wanted
****************/
int STD qreplace(int f, int n)
{
   return (replaces(TRUE, f, n));
}
//-

/// forwsearch()
/*
** forwsearch -- Search forward.  Get a search string from the user, and
** search for the string.  If found, reset the "." to be just after
** the match string, and (perhaps) repaint the display.
**
** int f, n;      default flag / numeric argument
****************/
int STD forwsearch(int f, int n)
{
   register int   status;

   /* If n is negative, search backwards.
    * Otherwise proceed by asking for the search string.
    */
   if (n < 0)
      return (backsearch(f, -n));

   /* Ask the user for the text of a pattern.  If the response is
    * TRUE (responses other than FALSE are possible), search for
    * pattern for up to n times, as long as the pattern is there
    * to be found.
    */
   if ((status = readpattern(TEXT78, &pat[0], TRUE)) == TRUE)
      status = forwhunt(f, n);
/*          "Search" */

   return (status);
}
//-

/// backsearch()
/*
** backsearch -- Reverse search.  Get a search string from the user, and
** search, starting at "." and proceeding toward the front of the buffer.
** If found "." is left pointing at the first character of the pattern
** (the last character that was matched).
**
** int f, n;      default flag / numeric argument
****************/
int STD backsearch(f, n)
{
   register int   status;

   /* If n is negative, search forwards.
    * Otherwise proceed by asking for the search string.
    */
   if (n < 0)
      return (forwsearch(f, -n));

   /* Ask the user for the text of a pattern.  If the response is
    * TRUE (responses other than FALSE are possible), search for
    * pattern for up to n times, as long as the pattern is there
    * to be found.
    */
   if ((status = readpattern(TEXT81, &pat[0], TRUE)) == TRUE)
      status = backhunt(f, n);
/*          "Reverse search" */

   return (status);
}
//-

// Local functions

/// backhunt()
/*
** backhunt -- Reverse search for a previously acquired search string,
** starting at "." and proceeding toward the front of the buffer.
** If found "." is left pointing at the first character of the pattern
** (the last character that was matched).
**
** int f, n;      default flag / numeric argument
**************/
int STD backhunt(int f, int n)
{
   register int   spoint = PTBEG;
   register int   status;

   if (n < 0)
      return (forwhunt(f, -n));

   /* Make sure a pattern exists, or that we didn't switch
    * into MAGIC mode after we entered the pattern.
    */
   if (tap[0] == '\0') {
      mlwrite(TEXT80);
/*       "No pattern set" */
      return FALSE;
   }

   /*
    * Do one extra search to get us past our current
    * match, if the search type has us at the start
    * of a match, instead of after a match.
    */
   if (searchtype == SREND) {
      spoint = PTEND;
      if (lastflag & CFSRCH)
         n = (n > 2)? (n + 1): 2;
   }

      status = scanner(REVERSE, spoint, n);

   /* Complain if not there.
    */
   if (status == FALSE)
      mlwrite(TEXT79);
/*       "Not found" */

   thisflag |= CFSRCH;
   return (status);
}
//-

/// boundry()
/*
** boundry -- Return information depending on whether we may search no
** further.  Beginning of file and end of file are the obvious
** cases, but we may want to add further optional boundry restrictions
** in future, a' la EDT.  At the moment, just return (TRUE) or
** FALSE depending on if a boundry is hit (ouch).
*************************************************************************/
int STD boundry(LINE *curline, int curoff, int dir)
{
   register int   border;

   if (dir == FORWARD) {
      border = (curoff == llength(curline)) &&
          (lforw(curline) == curbp->b_linep);
   }
   else
   {
      border = (curoff == 0) &&
          (lback(curline) == curbp->b_linep);
   }
   return (border);
}
//-

/// delins()
/*
** delins -- Delete a specified length from the current point
** then either insert the string directly, or make use of
** replacement meta-array.
*****************************************************************/
int STD delins(int dlength, char *instr, int use_rmc)
{
   register int   status;
// register char  *rstr;

   replen = 0;

   /* Zap what we gotta,
    * and insert its replacement.
    */
   if ((status = ldelete((long) dlength, FALSE)) != TRUE)
      mlwrite(TEXT93);
/*       "%%ERROR while deleting" */
   else
      {
         status = linstr(instr);
         replen = strlen(instr);
      }

   return (status);
}
//-

/// expandp()
/*
** expandp -- Expand control key sequences for output.
**
** char  *srcstr;       string to expand
** char  *deststr;      destination of expanded string
** int   maxlength;     maximum chars in destination
**********************/
int STD expandp(char *srcstr, char *deststr, int maxlength)
{
   unsigned char c;  /* current char to translate */

   /* Scan through the string.
    */
   while ((c = *srcstr++) != 0) {
      if (c == '\r')    /* it's a newline */
      {
         *deststr++ = '<';
         *deststr++ = 'N';
         *deststr++ = 'L';
         *deststr++ = '>';
         maxlength -= 4;
      }
      else if (c < 0x20 || c == 0x7f)  /* control character */
      {
         *deststr++ = '^';
         *deststr++ = c ^ 0x40;
         maxlength -= 2;
      }
      else if (c == '%') {
         *deststr++ = '%';
         *deststr++ = '%';
         maxlength -= 2;
      }
      else        /* any other character */
      {
         *deststr++ = c;
         maxlength--;
      }

      /* check for maxlength */
      if (maxlength < 4) {
         *deststr++ = '$';
         *deststr = '\0';
         return FALSE;
      }
   }
   *deststr = '\0';
   return (TRUE);
}
//-

/// fbound()
/*
** fbound -- Return information depending on whether we have hit a boundry
** (and may therefore search no further) or if a trailing character
** of the search string has been found.  See boundry() for search
** restrictions.
****************************************************************************/
int STD  fbound(int jump, LINE **pcurline, int *pcuroff, int dir)
{
   register int   spare, curoff;
   register LINE  *curline;

   curline = *pcurline;
   curoff = *pcuroff;

   if (dir == FORWARD) {
      while (jump != 0) {
         curoff += jump;
         spare = curoff - llength(curline);

         if (curline == curbp->b_linep)
            return (TRUE); /* hit end of buffer */

         while (spare > 0) {
            curline = lforw(curline);/* skip to next line */
            curoff = spare - 1;
            spare = curoff - llength(curline);
            if (curline == curbp->b_linep)
               return (TRUE); /* hit end of buffer */
         }

         if (spare == 0)
            jump = deltaf[(int) '\r'];
         else
            jump = deltaf[(int) lgetc(curline, curoff)];
      }

      /* The last character matches, so back up to start
       * of possible match.
       */
      curoff -= patlenadd;

      while (curoff < 0) {
         curline = lback(curline);  /* skip back a line */
         curoff += llength(curline) + 1;
      }

   }
   else        /* Reverse.*/
   {
      jump++;     /* allow for offset in reverse */
      while (jump != 0) {
         curoff -= jump;
         while (curoff < 0) {
            curline = lback(curline);  /* skip back a line */
            curoff += llength(curline) + 1;
            if (curline == curbp->b_linep)
               return (TRUE); /* hit end of buffer */
         }

         if (curoff == llength(curline))
            jump = deltab[(int) '\r'];
         else
            jump = deltab[(int) lgetc(curline, curoff)];
      }

      /* The last character matches, so back up to start
       * of possible match.
       */
      curoff += patlenadd + 1;
      spare = curoff - llength(curline);
      while (spare > 0) {
         curline = lforw(curline);  /* skip back a line */
         curoff = spare - 1;
         spare = curoff - llength(curline);
      }
   }

   *pcurline = curline;
   *pcuroff = curoff;
   return FALSE;
}
//-

/// forwhunt()
/*
** forwhunt -- Search forward for a previously acquired search string.
** If found, reset the "." to be just after the match string,
** and (perhaps) repaint the display.
**
** int f, n;      default flag / numeric argument
****************/
int STD forwhunt(int f, int n)
{
   register int   spoint = PTEND;
   register int   status;

   if (n < 0)     /* search backwards */
      return (backhunt(f, -n));

   /* Make sure a pattern exists, or that we didn't switch
    * into MAGIC mode after we entered the pattern.
    */
   if (pat[0] == '\0') {
      mlwrite(TEXT80);
/*       "No pattern set" */
      return FALSE;
   }


   /*
    * Do one extra search to get us past our current
    * match, if the search type has us at the start
    * of a match, instead of after a match.
    */
   if (searchtype == SRBEGIN) {
      spoint = PTBEG;
      if (lastflag & CFSRCH)
         n = (n > 2)? (n + 1): 2;
   }

      status = scanner(FORWARD, spoint, n);

   /* Complain if not there.
    */
   if (status == FALSE)
      mlwrite(TEXT79);
/*       "Not found" */

   thisflag |= CFSRCH;
   return (status);
}
//-

/// nextch()
/*
** nextch -- retrieve the next/previous character in the buffer,
** and advance/retreat the point.
** The order in which this is done is significant, and depends
** upon the direction of the search.  Forward searches look at
** the current character and move, reverse searches move and
** look at the character.
******************************************************************/
int STD nextch(LINE **pcurline, int *pcuroff, int dir)
{
   register LINE  *curline;
   register int   curoff;
   register int   c;

   curline = *pcurline;
   curoff = *pcuroff;

   if (dir == FORWARD) {
      if (curoff == llength(curline))     /* if at EOL */
      {
         curline = lforw(curline);  /* skip to next line */
         curoff = 0;
         c = '\r';         /* and return a <NL> */
      }
      else
         c = lgetc(curline, curoff++); /* get the char */
   }
   else        /* Reverse.*/
   {
      if (curoff == 0) {
         curline = lback(curline);
         curoff = llength(curline);
         c = '\r';
      }
      else
         c = lgetc(curline, --curoff);

   }
   *pcurline = curline;
   *pcuroff = curoff;

   return (c);
}
//-

/// readpattern()
/*
** readpattern -- Read a pattern.  Stash it in apat.  If it is the
** search string (which means that the global variable pat[]
** has been passed in), create the reverse pattern and the magic
** pattern, assuming we are in MAGIC mode (and #defined that way).
**
** Apat is not updated if the user types in an empty line.  If
** the user typed an empty line, and there is no old pattern, it is
** an error.  Display the old pattern, in the style of Jeff Lomicka.
** There is some do-it-yourself control expansion.  Change to using
** <META> to delimit the end-of-pattern to allow <NL>s in the search
** string.
**********************************************************************/
int STD readpattern(char *prompt, char apat[], int srch)
{
   register int   status;
   char     tpat[NPAT+20];

   strcpy(tpat, prompt);   /* copy prompt to output string */
   strcat(tpat, " [");  /* build new prompt string */
   expandp(&apat[0], &tpat[strlen(tpat)], NPAT/2); /* add old pattern */
   strcat(tpat, "]");

   /* display the proper current search terminator character */
   switch (sterm) {

      case CTRL | '[':  strcat(tpat, "<META>"); break;
      case CTRL | 'M':  strcat(tpat, "<NL>"); break;
      default:    strcat(tpat, "<");
               cmdstr(sterm, &(tpat[strlen(tpat)]));
               strcat(tpat, ">");
   }
   strcat(tpat, ": ");

   /* Read a pattern.  Either we get one,
    * or we just get the META charater, and use the previous pattern.
    * Then, if it's the search string, make a reversed pattern.
    * *Then*, make the meta-pattern, if we are defined that way.
    */
   if ((status = mltreply(tpat, tpat, NPAT, sterm)) == TRUE) {
      lastflag &= ~CFSRCH;
      strcpy(apat, tpat);

      /* If we are doing the search string,
       * set the delta tables.
       */
      if (srch)
         setjtable();

   }
   else if (status == FALSE && apat[0] != 0) /* Old one */
      status = TRUE;

   return (status);
}
//-

/// replaces()
/*
** replaces -- Search for a string and replace it with another
** string.  Query might be enabled (according to kind).
**
** int   kind;    Query enabled flag
** int   f;       default flag
** int   n;       # of repetitions wanted
**************/
int STD  replaces(int kind, int f, int n)
{
   register int status; /* success flag on pattern inputs */
   register int nummatch;  /* number of found matches */
   int numsub;    /* number of substitutions */
   int nlflag;    /* last char of search string a <NL>? */
   int nlrepl;    /* was a replace done on the last line? */
   char c;        /* input char for query */
   char tpat[NPAT];  /* temporary to hold search pattern */
   LINE *origline;      /* original "." position */
   int origoff;      /* and offset (for . query option) */
   LINE *lastline;      /* position of last replace and */
   int lastoff;      /* offset (for 'u' query option) */
   int oldmatchlen = 0; /* Closure may alter the match length.*/
   static char *oldpatmatch = NULL; /* allocated memory for un-do.*/

   if (curbp->b_mode & MDVIEW)   /* don't allow this command if   */
      return (rdonly());   /* we are in read only mode   */

   /* Check for negative repetitions.
    */
   if (f && n < 0)
      return FALSE;

   /* Ask the user for the text of a pattern.
    */
   if ((status = readpattern(
       (kind == FALSE ? TEXT84 : TEXT85), &pat[0], TRUE)) != TRUE)
/*          "Replace" */
/*             "Query replace" */
      return (status);

   /* Ask for the replacement string, and get its length.
    */
   if ((status = readpattern(TEXT86, &rpat[0], FALSE)) == ABORT)
/*          "with" */
      return (status);

   /* Set up flags so we can make sure not to do a recursive
    * replace on the last line.
    */
   nlflag = (pat[matchlen - 1] == '\r');
   nlrepl = FALSE;

   if (kind) {
      /* Build query replace question string.
       */
      strcpy(tpat, TEXT87);
/*          "Replace '" */
      expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3);
      strcat(tpat, TEXT88);
/*          "' with '" */
      expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3);
      strcat(tpat, "'? ");

      /* Initialize last replaced pointers.
       */
      lastline = NULL;
      lastoff = 0;
   }

   /* Save original . position, reset the number of matches and
    * substitutions, and scan through the file.
    */
   origline = curwp->w_dotp;
   origoff = curwp->w_doto;
   numsub = 0;
   nummatch = 0;

   while ( (f == FALSE || n > nummatch) &&
      (nlflag == FALSE || nlrepl == FALSE) ) {
      /* Search for the pattern.
       * If we search with a regular expression,
       * matchlen is reset to the true length of
       * the matched string.
       */
         if (!scanner(FORWARD, PTBEG, 1))
            break;      /* all done */

      ++nummatch; /* Increment # of matches */

      /* Check if we are on the last line.
       */
      nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);

      /* Check for query.
       */
      if (kind) {
         /* Get the query.
          */
pprompt:    mlwrite(&tpat[0]);
qprompt:
         update(TRUE);  /* show the proposed place to change */
         c = tgetc();         /* and input */
         mlerase();        /* and clear it */

         /* And respond appropriately.
          */
         switch (c) {
            case 'y':   /* yes, substitute */
            case 'Y':
            case ' ':
               break;

            case 'n':   /* no, onward */
            case 'N':
               forwchar(FALSE, 1);
               continue;

            case '!':   /* yes/stop asking */
               kind = FALSE;
               break;

            case 'u':   /* undo last and re-prompt */
            case 'U':
               /* Restore old position.
                */
               if (lastline == NULL) {
                  /* There is nothing to undo.
                   */
                  TTbeep();
                  goto pprompt;
               }
               curwp->w_dotp = lastline;
               curwp->w_doto = lastoff;
               lastline = NULL;
               lastoff = 0;

               /* Delete the new string,
                * restore the old match.
                */
               backchar(FALSE, replen);
               status = delins(replen, oldpatmatch, FALSE);
               if (status != TRUE)
                  return (status);

               /* Record one less substitution,
                * backup, save our place, and
                * reprompt.
                */
               --numsub;
               backchar(FALSE, oldmatchlen);
               matchlen = oldmatchlen;
               matchline = curwp->w_dotp;
               matchoff  = curwp->w_doto;
               continue;

            case '.':   /* abort! and return */
               /* restore old position */
               curwp->w_dotp = origline;
               curwp->w_doto = origoff;
               curwp->w_flag |= WFMOVE;

            case BELL:  /* abort! and stay */
               mlwrite(TEXT89);
/*                "Aborted!" */
               return FALSE;

            default: /* bitch and beep */
               TTbeep();

            case '?':   /* help me */
               mlwrite(TEXT90);
/*"(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: "*/
               goto qprompt;

         }  /* end of switch */
      }  /* end of "if kind" */

      /* if this is the point origin, flag so we a can reset it */
      if (curwp->w_dotp == origline) {
         origline = NULL;
         lastline = curwp->w_dotp->l_bp;
      }

      /* Delete the sucker, and insert its
       * replacement.
       */
      status = delins(matchlen, &rpat[0], FALSE);
      if (origline == NULL) {
         origline = lastline->l_fp;
         origoff = 0;
      }

      if (status != TRUE)
         return (status);

      numsub++;   /* increment # of substitutions */

      /* Save our position, the match length, and the string
       * matched if we are query-replacing, as we may undo
       * the replacement. If we are not querying, check to
       * make sure that we didn't replace an empty string
       * (possible in MAGIC mode), because we'll infinite loop.
       */
      if (kind) {
         lastline = curwp->w_dotp;
         lastoff = curwp->w_doto;
         oldmatchlen = matchlen; /* Save the length for un-do.*/

         if (oldpatmatch != NULL)
            free(oldpatmatch);

         if ((oldpatmatch = malloc(matchlen + 1)) == NULL) {
            mlwrite(TEXT94);
/*             "%%Out of memory" */
            return ABORT;
         }
         strcpy(oldpatmatch, patmatch);
      }
      else if (matchlen == 0) {
         mlwrite(TEXT91);
/*          "Empty string replaced, stopping." */
         return FALSE;
      }
   }

   /* And report the results.
    */
   mlwrite(TEXT92, numsub);
/*    "%d substitutions" */
   return (TRUE);
}
//-

/// savematch
/*
** savematch -- We found the pattern?  Let's save it away.
*************************************************************/
int STD savematch(void)
{
// register int   j;
   REGION      tmpreg;

   if (patmatch != NULL)
      free(patmatch);

   if ((patmatch = malloc(matchlen + 1)) == NULL) {
      mlwrite(TEXT94);
/*       "%%Out of memory" */
      return ABORT;
   }

   tmpreg.r_offset = matchoff;
   tmpreg.r_linep = matchline;
   tmpreg.r_size = matchlen;
   regtostr(patmatch, &tmpreg);

   return TRUE;
}
//-

/// rvstrcpy()
/*
** rvstrcpy -- Reverse string copy.
 ***********************************************************************/
void STD rvstrcpy(register char *rvstr, register char *str)
{
   register int i;

   str += (i = strlen(str));

   while (i-- > 0)
      *rvstr++ = *--str;

   *rvstr = '\0';
}
//-

/// chcase()
/*
** Change the case of the current character.
** First check lower and then upper.  If it is not a letter,
** it gets returned unchanged.
 ***********************************************************************/
unsigned int REG chcase(register unsigned int ch)
{
   /* translate lowercase */
   if (islower(ch))
      return((unsigned int) lowcase[ch]);

   /* translate uppercase */
   if (isupper(ch))
      return((unsigned int) upcase[ch]);

   /* let the rest pass */
   return(ch);
}
//-


