home *** CD-ROM | disk | FTP | other *** search
- /*
- * The functions in this file implement commands that perform incremental
- * searches in the forward and backward directions. This "ISearch" command
- * is intended to emulate the same command from the original EMACS
- * implementation (ITS). Contains references to routines internal to
- * SEARCH.C.
- *
- * REVISION HISTORY:
- *
- * D. R. Banks 9-May-86
- * - added ITS EMACSlike ISearch
- *
- * John M. Gamble 5-Oct-86
- * - Made iterative search use search.c's scanner() routine.
- * This allowed the elimination of bakscan().
- * - Put isearch constants into estruct.h
- * - Eliminated the passing of 'status' to scanmore() and
- * checknext(), since there were no circumstances where
- * it ever equalled FALSE.
- * Dan Corkill 6-Oct-87
- * - Changed character loop to terminate with extended characters
- * (thus arrow keys, and most commands behave intuitively).
- * - Changed META to be reread rather than simply aborting.
- * - Conditionalized VMS alternates for ^S and ^Q to only apply
- * to VMS ports. (Allowing ^X as a synonym for ^S defeats some
- * of the benefits of the first change above.)
- *
- * (Further comments are in history.c)
- */
-
- #include <stdio.h>
- #include "estruct.h"
- #include "eproto.h"
- #include "edef.h"
- #include "elang.h"
-
- #if ISRCH
-
- #define CMDBUFLEN 256 /* Length of our command buffer */
-
- /* A couple of "own" variables for re-eat */
- /* Hey, BLISS user, these were "GLOBAL", I made them "OWN". */
- static int (PASCAL NEAR *saved_get_char)();/* Get character routine */
- static int eaten_char = -1; /* Re-eaten char */
-
- /* A couple more "own" variables for the command string */
-
- static int cmd_buff[CMDBUFLEN]; /* Save the command args here */
- static int cmd_offset; /* Current offset into command buff */
- static int cmd_reexecute = -1; /* > 0 if re-executing command */
-
- /*
- * Subroutine to do incremental reverse search. It actually uses the
- * same code as the normal incremental search, as both can go both ways.
- */
- int PASCAL NEAR risearch(f, n)
- int f,n; /* prefix flag and argument */
- {
- register int status;
-
- /* Make sure the search doesn't match where we already
- * are by backing up a character.
- */
- backchar(TRUE, 1);
-
- if (status = isearch(REVERSE))
- mlerase(); /* If happy, just erase the cmd line */
- else
- mlwrite(TEXT164);
- /* "[search failed]" */
- return (status);
- }
-
- /* Again, but for the forward direction */
-
- int PASCAL NEAR fisearch(f, n)
- int f,n;
- {
- register int status;
-
- if (status = isearch(FORWARD))
- mlerase(); /* If happy, just erase the cmd line */
- else
- mlwrite(TEXT164);
- /* "[search failed]" */
- return (status);
- }
-
- /*
- * Subroutine to do an incremental search. In general, this works similarly
- * to the older micro-emacs search function, except that the search happens
- * as each character is typed, with the screen and cursor updated with each
- * new search character.
- *
- * While searching forward, each successive character will leave the cursor
- * at the end of the entire matched string. Typing a Control-S
- * will cause the next occurrence of the string to be searched for (where the
- * next occurrence does NOT overlap the current occurrence). A Control-R will
- * change to a backwards search, META will terminate the search and Control-G
- * will abort the search. Rubout will back up to the previous match of the
- * string, or if the starting point is reached first, it will delete the
- * last character from the search string.
- *
- * While searching backward, each successive character will leave the cursor
- * at the beginning of the matched string. Typing a Control-R will search
- * backward for the next occurrence of the string. Control-S
- * will revert the search to the forward direction. In general, the reverse
- * incremental search is just like the forward incremental search inverted.
- *
- * In all cases, if the search fails, the user will be feeped, and the search
- * will stall until the pattern string is edited back into something that
- * exists (or until the search is aborted).
- */
- int PASCAL NEAR isearch(dir)
-
- int dir;
-
- {
- int status; /* Search status */
- int col; /* prompt column */
- register int cpos; /* character number in search string */
- register int c; /* current input character */
- register int expc; /* function expanded input char */
- char pat_save[NPAT]; /* Saved copy of the old pattern str */
- LINE *curline; /* Current line on entry */
- int curoff; /* Current offset on entry */
- int init_direction; /* The initial search direction */
- KEYTAB *ktp; /* The command bound to the key */
- register int (PASCAL NEAR *kfunc)();/* ptr to the requested function to bind to */
-
- /* Set up the starting conditions */
-
- cmd_reexecute = -1; /* We're not re-executing (yet?) */
- cmd_offset = 0; /* Start at the beginning of cmd_buff */
- cmd_buff[0] = '\0'; /* Reset the command buffer */
- bytecopy(pat_save, pat, NPAT); /* Save the old pattern string */
- curline = curwp->w_dotp; /* Save the current line pointer */
- curoff = curwp->w_doto; /* Save the current offset */
- init_direction = dir; /* Save the initial search direction */
- mmove_flag = FALSE; /* disable mouse move events */
-
-
- start_over: /* This is a good place to start a re-execution: */
-
- /*
- * Ask the user for the text of a pattern,
- * and remember the column.
- */
- col = (clexec)? 0: mlprompt(TEXT165, pat, isterm);
- /* "ISearch: " */
-
- cpos = 0; /* Start afresh */
- status = TRUE; /* Assume everything's cool */
-
- for (;;) { /* ISearch per character loop */
-
- /* Check for special characters first.
- That is, a control or ^X or FN or mouse function.
- Most cases here change the search.
- */
- c = ectoc(expc = get_char());
-
- if (expc == isterm) { /* Want to quit searching? */
- setjtable(); /* Update jump tables... */
- mmove_flag = TRUE;
- return(TRUE); /* Quit searching now */
- }
-
- if (expc == abortc) /* If abort search request */
- break; /* Quit searching */
-
- if (expc == quotec) { /* Quote character? */
- c = ectoc(expc = get_char()); /* Get the next char */
- } else if ((expc > 255 || expc == 0) &&
- (c != '\t' && c != '\r')) {
-
- kfunc = ((ktp = getbind(expc)) == NULL)? NULL: ktp->k_ptr.fp;
-
- if (kfunc == forwsearch || kfunc == forwhunt ||
- kfunc == fisearch || kfunc == backsearch ||
- kfunc == backhunt || kfunc == risearch) {
- dir = (kfunc == backsearch ||
- kfunc == backhunt ||
- kfunc == risearch) ? REVERSE: FORWARD;
-
- /*
- * if cpos == 0 then we are either just starting
- * or starting over. Use the original pattern
- * in pat, which has either not been changed or
- * has just been restored. Find the length and
- * re-echo the string.
- */
- if (cpos == 0) {
- cpos = strlen(pat);
- col = echostring(pat, col, NPAT/2);
- }
-
- status = scanmore(dir);
- continue;
- } else if (kfunc == backdel) {
- if (cmd_offset <= 1) { /* Anything to delete? */
- mmove_flag = TRUE;
- return(TRUE); /* No, just exit */
- }
-
- /* Back up over the Rubout and the character
- * it's rubbing out. If it is a quoted
- * char, rub the quote char out too.
- */
- cmd_offset -= 2;
- if (cmd_offset > 0 && cmd_buff[cmd_offset - 1] == quotec)
- cmd_offset--;
- cmd_buff[cmd_offset] = '\0';
-
- curwp->w_dotp = curline; /* Reset the line pointer */
- curwp->w_doto = curoff; /* and the offset */
- dir = init_direction; /* Reset the search direction */
- bytecopy(pat, pat_save, NPAT); /* Restore the old search str */
- setjtable(); /* and its jump tables. */
- cmd_reexecute = 0; /* Start the whole mess over */
- goto start_over; /* Let it take care of itself */
- }
-
- /* Presumably the key was uninteresting...*/
-
- reeat(expc); /* Re-eat the char */
- mmove_flag = TRUE;
- return(TRUE); /* And return the last status */
- }
-
- /* I guess we got something to search for, so search for it */
- pat[cpos++] = c; /* put the char in the buffer */
-
- if (cpos >= NPAT) { /* too many chars in string? */
- /* Yup. Complain about it */
- mlwrite(TEXT166);
- /* "? Search string too long" */
- bytecopy(pat, pat_save, NPAT); /* Restore the old search str */
- setjtable(); /* and its jump tables. */
- mmove_flag = TRUE;
- return(FALSE); /* Return an error, but stay. */
- }
-
- pat[cpos] = 0; /* null terminate the buffer */
- #if COLOR
- /* set up the proper colors for the command line */
- TTforg(gfcolor);
- TTbacg(gbcolor);
- #endif
- col = echochar(c, col); /* Echo the character */
- if (!status) /* If we lost last time */
- TTbeep(); /* Feep again */
- else /* Otherwise, we must have won*/
- status = checknext(c, dir); /* See if still matches or find next */
-
- }
-
- curwp->w_dotp = curline;/* Reset the line pointer */
- curwp->w_doto = curoff; /* and the offset to original value */
- curwp->w_flag |= WFMOVE;/* Say we've moved */
- update(FALSE); /* And force an update */
- mmove_flag = TRUE;
- return(FALSE);
- }
-
- /*
- * This hack will search for the next occurrence of <pat> in the buffer,
- * either forward or backward. If we can't find any more matches, "point"
- * is left where it was before. If we do find a match, "point" will be at
- * the end of the matched string for forward searches and at the beginning
- * of the matched string for reverse searches.
- */
- int PASCAL NEAR scanmore(dir)
- int dir; /* direction to search */
- {
- register int sts; /* search status */
-
- setjtable(); /* Set up fast search arrays */
-
- sts = scanner(dir, (dir == REVERSE)? PTBEG: PTEND, 1);
-
- if (!sts)
- TTbeep(); /* Feep if search fails */
-
- return(sts);
- }
-
- /*
- * Trivial routine to insure that the next character in the search
- * string is still true to whatever we're pointing to in the buffer.
- * This routine will not attempt to move the "point" if the match
- * fails, although it will implicitly move the "point" if we're
- * forward searching, and find a match, since that's the way forward
- * isearch works. If we are reverse searching we compare all
- * characters in the pattern string from "point" to the new end.
- *
- * If the compare fails, we return FALSE and call scanmore or something.
- */
- int PASCAL NEAR checknext(chr, dir)
- int chr; /* Next char to look for */
- int dir; /* Search direction */
- {
- LINE *curline; /* current line during scan */
- int curoff; /* position within current line */
- register char *patrn; /* The entire search string (incl chr) */
- register int sts; /* how well things go */
-
- /* setup the local scan pointer to current "." */
-
- curline = curwp->w_dotp; /* Get the current line structure */
- curoff = curwp->w_doto; /* Get the offset within that line */
-
- if (dir == FORWARD) { /* If searching forward */
- if (sts = !boundry(curline, curoff, FORWARD)) {
- /* If it's what we're looking for, set the point
- * and say that we've moved.
- */
- if (sts = eq(nextch(&curline, &curoff, FORWARD), chr)) {
- curwp->w_dotp = curline;
- curwp->w_doto = curoff;
- curwp->w_flag |= WFMOVE;
- }
- }
- } else { /* Else, reverse search check. */
- patrn = (char *)pat;
- while (*patrn) { /* Loop for all characters in patrn */
- if ((sts = !boundry(curline, curoff, FORWARD)) == FALSE ||
- (sts = eq(nextch(&curline, &curoff, FORWARD), *patrn)) == FALSE)
- break; /* Nope, just punt it then */
- patrn++;
- }
- }
-
- /*
- * If the 'next' character didn't fit in the pattern,
- * let's go search for it somewhere else.
- */
- if (sts == FALSE)
- sts = scanmore(dir);
-
- return(sts); /* And return the status */
- }
-
- /*
- * Routine to get the next character or extended character from the input
- * stream. If we're reading from the real terminal, force a screen update
- * before we get the char. Otherwise, we must be re-executing the command
- * string, so just return the next character.
- */
- int PASCAL NEAR get_char()
- {
- int c;
- KEYTAB *key;
-
- /* See if we're re-executing: */
-
- if (cmd_reexecute >= 0) /* Is there an offset? */
- if ((c = cmd_buff[cmd_reexecute++]) != 0)
- return(c); /* Yes, return any character */
-
- /* We're not re-executing (or aren't any more). Try for a real char
- */
- cmd_reexecute = -1; /* Say we're in real mode again */
- update(FALSE); /* Pretty up the screen */
- if (posflag) upmode(); /* and the modeline, if need be.*/
- if (cmd_offset >= CMDBUFLEN-1) {/* If we're getting too big ... */
- mlwrite (TEXT167); /* Complain loudly and bitterly */
- /* "? command too long" */
- return(isterm); /* And force a quit */
- }
-
- /*
- * If $isterm != meta character, then create the extended
- * character as getcmd() does.
- */
- if ((c = getkey()) == isterm) return (isterm);
- if ((key = getbind(c)) != NULL) {
- if (key->k_ptr.fp == cex || key->k_ptr.fp == meta) {
- c = getkey();
- #if SMOS
- c = upperc(c&255) | (c & ~255); /* Force to upper */
- #else
- c = upperc(c) | (c & ~255); /* Force to upper */
- #endif
- c |= (key->k_ptr.fp == cex)? CTLX: META;
- }
- }
-
- cmd_buff[cmd_offset++] = c; /* Save the char for next time */
- cmd_buff[cmd_offset] = '\0'; /* And terminate the buffer */
- return(c); /* Return the character */
- }
-
- /*
- * Hacky routine to re-eat a character. This will save the character to be
- * re-eaten by redirecting the input call to a routine here. Hack, etc.
- *
- * Come here on the next term.t_getchar call:
- */
- int PASCAL NEAR uneat()
- {
- int c;
-
- term.t_getchar = saved_get_char;/* restore the routine address */
- c = eaten_char; /* Get the re-eaten char */
- eaten_char = -1; /* Clear the old char */
- return(c); /* and return the last char */
- }
-
- VOID PASCAL NEAR reeat(c)
- int c;
- {
- if (eaten_char != -1) /* If we've already been here */
- return; /* Don't do it again */
- eaten_char = c; /* Else, save the char for later*/
- saved_get_char = term.t_getchar;/* Save the char get routine */
- term.t_getchar = uneat; /* Replace it with ours */
- }
- #else
- int PASCAL NEAR isearch(dir)
- int dir;
- {
- }
- #endif
-