home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / memacs400_src.lzh / MEMACS400 / SRC / isearch.c < prev    next >
Text File  |  1996-04-25  |  15KB  |  490 lines

  1. /*
  2.  * The functions in this file implement commands that perform incremental
  3.  * searches in the forward and backward directions.  This "ISearch" command
  4.  * is intended to emulate the same command from the original EMACS
  5.  * implementation (ITS).  Contains references to routines internal to
  6.  * SEARCH.C.
  7.  *
  8.  * REVISION HISTORY:
  9.  *
  10.  *    D. R. Banks 9-May-86
  11.  *    - added ITS EMACSlike ISearch
  12.  *
  13.  *    John M. Gamble 5-Oct-86
  14.  *    - Made iterative search use search.c's scanner() routine.
  15.  *      This allowed the elimination of bakscan().
  16.  *    - Put isearch constants into estruct.h
  17.  *    - Eliminated the passing of 'status' to scanmore() and
  18.  *      checknext(), since there were no circumstances where
  19.  *      it ever equalled FALSE.
  20.  *    Dan Corkill 6-Oct-87
  21.  *      - Changed character loop to terminate with extended characters
  22.  *        (thus arrow keys, and most commands behave intuitively).
  23.  *      - Changed META to be reread rather than simply aborting.
  24.  *      - Conditionalized VMS alternates for ^S and ^Q to only apply
  25.  *        to VMS ports.  (Allowing ^X as a synonym for ^S defeats some
  26.  *        of the benefits of the first change above.)
  27.  *
  28.  *    (Further comments are in history.c)
  29.  */
  30.  
  31. #include <stdio.h>
  32. #include "estruct.h"
  33. #include "eproto.h"
  34. #include "edef.h"
  35. #include "elang.h"
  36.  
  37. #if    ISRCH
  38.  
  39. #define CMDBUFLEN    256    /* Length of our command buffer */
  40.  
  41. /* A couple of "own" variables for re-eat */
  42. /* Hey, BLISS user, these were "GLOBAL", I made them "OWN". */
  43. static int    (PASCAL NEAR *saved_get_char)();    /* Get character routine */
  44. static int    eaten_char = -1;    /* Re-eaten char */
  45.  
  46. /* A couple more "own" variables for the command string */
  47.  
  48. static int cmd_buff[CMDBUFLEN];    /* Save the command args here */
  49. static int cmd_offset;    /* Current offset into command buff */
  50. static int cmd_reexecute = -1;    /* > 0 if re-executing command */
  51.  
  52. /*
  53.  * Subroutine to do incremental reverse search.  It actually uses the
  54.  * same code as the normal incremental search, as both can go both ways.
  55.  */
  56.  
  57. int PASCAL NEAR risearch(f, n)
  58. int f, n;                /* prefix flag and argument */
  59. {
  60.     register int    status;
  61.  
  62.     /* Make sure the search doesn't match where we already
  63.      * are by backing up a character.
  64.      */
  65.     backchar(TRUE, 1);
  66.  
  67.     if (status = isearch(REVERSE))
  68.         mlerase();        /* If happy, just erase the cmd line  */
  69.     else
  70.         mlwrite(TEXT164);
  71. /*               "[search failed]" */
  72.     return (status);
  73. }
  74.  
  75. /* Again, but for the forward direction */
  76.  
  77. int PASCAL NEAR fisearch(f, n)
  78. int f, n;
  79. {
  80.     register int     status;
  81.  
  82.     if (status = isearch(FORWARD))
  83.         mlerase();        /* If happy, just erase the cmd line  */
  84.     else
  85.         mlwrite(TEXT164);
  86. /*               "[search failed]" */
  87.     return (status);
  88. }
  89.  
  90. /*
  91.  * Subroutine to do an incremental search.  In general, this works similarly
  92.  * to the older micro-emacs search function, except that the search happens
  93.  * as each character is typed, with the screen and cursor updated with each
  94.  * new search character.
  95.  *
  96.  * While searching forward, each successive character will leave the cursor
  97.  * at the end of the entire matched string.  Typing a Control-S
  98.  * will cause the next occurrence of the string to be searched for (where the
  99.  * next occurrence does NOT overlap the current occurrence).  A Control-R will
  100.  * change to a backwards search, META will terminate the search and Control-G
  101.  * will abort the search.  Rubout will back up to the previous match of the
  102.  * string, or if the starting point is reached first, it will delete the
  103.  * last character from the search string.
  104.  *
  105.  * While searching backward, each successive character will leave the cursor
  106.  * at the beginning of the matched string.  Typing a Control-R will search
  107.  * backward for the next occurrence of the string.  Control-S
  108.  * will revert the search to the forward direction.  In general, the reverse
  109.  * incremental search is just like the forward incremental search inverted.
  110.  *
  111.  * In all cases, if the search fails, the user will be feeped, and the search
  112.  * will stall until the pattern string is edited back into something that
  113.  * exists (or until the search is aborted).
  114.  */
  115.  
  116. int PASCAL NEAR isearch(dir)
  117.  
  118. int dir;
  119.  
  120. {
  121.     int         status;    /* Search status */
  122.     int         col;    /* prompt column */
  123.     register int    cpos;    /* character number in search string  */
  124.     register int    c;    /* current input character */
  125.     register int    expc;    /* function expanded input char       */
  126.     char        pat_save[NPAT];    /* Saved copy of the old pattern str  */
  127.     LINE        *curline;    /* Current line on entry          */
  128.     int         curoff;    /* Current offset on entry          */
  129.     int         init_direction;    /* The initial search direction       */
  130.     KEYTAB        *ktp;    /* The command bound to the key       */
  131.     register int (PASCAL NEAR *kfunc)();    /* ptr to the requested function to bind to */
  132.  
  133.     /* Set up the starting conditions */
  134.  
  135.     cmd_reexecute = -1;    /* We're not re-executing (yet?) */
  136.     cmd_offset = 0;        /* Start at the beginning of cmd_buff */
  137.     cmd_buff[0] = '\0';    /* Reset the command buffer */
  138.     init_direction = dir;    /* Save the initial search direction */
  139.     mmove_flag = FALSE;    /* disable mouse move events */
  140.  
  141.     /*
  142.      * Save the current search string and line position, in case
  143.      * we bounce out if isearch.  Unmake the meta-character array,
  144.      * so that it will get re-made automatically with the (maybe)
  145.      * new search string on a MAGIC mode search.
  146.      */
  147.     bytecopy(pat_save, (char *) pat, NPAT);    /* Save the old pattern string */
  148.     curline = curwp->w_dotp;    /* Save the current line pointer */
  149.     curoff = curwp->w_doto;    /* Save the current offset */
  150. #if MAGIC
  151.     mcclear();
  152. #endif
  153.  
  154.  
  155. start_over:                /* This is a good place to start a re-execution: */
  156.  
  157.     /*
  158.      * Ask the user for the text of a pattern,
  159.      * and remember the column.
  160.      */
  161.     col = (clexec) ? 0 : mlprompt(TEXT165, (char *) pat, isterm);
  162. /*                "ISearch: " */
  163.  
  164.     cpos = 0;            /* Start afresh           */
  165.     status = TRUE;        /* Assume everything's cool   */
  166.  
  167.     for (;;)
  168.     {                /* ISearch per character loop */
  169.  
  170.         /*
  171.          * Check for special characters first.
  172.          * That is, a control or ^X or FN or mouse function.
  173.          * Most cases here change the search.
  174.          */
  175.         c = ectoc(expc = get_char());
  176.  
  177.         if (expc == isterm)
  178.         {            /* Want to quit searching?      */
  179.             setjtable();    /* Update jump tables...      */
  180.             mmove_flag = TRUE;
  181.             return (TRUE);    /* Quit searching now          */
  182.         }
  183.  
  184.         if (expc == abortc)    /* If abort search request      */
  185.             break;        /* Quit searching          */
  186.  
  187.         if (expc == quotec)            /* Quote character?       */
  188.             c = ectoc(expc = get_char());    /* Get the next char          */
  189.         else if ((expc > 255 || expc == 0) &&
  190.             (c != '\t' && c != '\r'))
  191.         {
  192.  
  193.             kfunc = ((ktp = getbind(expc)) == NULL) ? NULL : ktp->k_ptr.fp;
  194.  
  195.             if (kfunc == forwsearch || kfunc == forwhunt ||
  196.                 kfunc == fisearch || kfunc == backsearch ||
  197.                 kfunc == backhunt || kfunc == risearch)
  198.             {
  199.                 dir = (kfunc == backsearch ||
  200.                     kfunc == backhunt ||
  201.                     kfunc == risearch) ? REVERSE : FORWARD;
  202.  
  203.                 /*
  204.                  * if cpos == 0 then we are either just starting
  205.                  * or starting over.  Use the original pattern
  206.                  * in pat, which has either not been changed or
  207.                  * has just been restored. Find the length and
  208.                  * re-echo the string.
  209.                  */
  210.                 if (cpos == 0)
  211.                     {
  212.                     cpos = strlen((char *) pat);
  213.                     col = echostring((char *) pat, col, NPAT / 2);
  214.                     }
  215.  
  216.                 status = scanmore(dir);
  217.                 continue;
  218.             }
  219.             else if (kfunc == backdel)
  220.             {
  221.                 /*
  222.                  * If there's nothing to delete, just exit.
  223.                  */
  224.                 if (cmd_offset <= 1)
  225.                 {
  226.                     mmove_flag = TRUE;
  227.                     return (TRUE);
  228.                 }
  229.  
  230.                 /* Back up over the Rubout and the character
  231.                  * it's rubbing out.  If it is a quoted
  232.                  * char, rub the quote char out too.
  233.                  */
  234.                 cmd_offset -= 2;
  235.                 if (cmd_offset > 0 &&
  236.                     cmd_buff[cmd_offset - 1] == quotec)
  237.                     cmd_offset--;
  238.                 cmd_buff[cmd_offset] = '\0';
  239.  
  240.                 /*
  241.                  * Reset everything - line and offset,
  242.                  * search direction and pattern and
  243.                  * jump tables, start the whole mess
  244.                  * over (cmd_reexecute = 0) and
  245.                  * let it take care of itself.
  246.                  */
  247.                 curwp->w_dotp = curline;
  248.                 curwp->w_doto = curoff;
  249.                 dir = init_direction;
  250.                 bytecopy((char *) pat, pat_save, NPAT);
  251.                 setjtable();
  252.                 cmd_reexecute = 0;
  253.                 goto start_over;
  254.             }
  255.  
  256.             /*
  257.              * Presumably the key was a command key,
  258.              * yet strangely uninteresting...
  259.              */
  260.             reeat(expc);    /* Re-eat the char          */
  261.             setjtable();
  262.             mmove_flag = TRUE;
  263.             return (TRUE);    /* And return the last status */
  264.         }
  265.  
  266.         /*
  267.          * I guess we got something to search for, so put the
  268.          * character in the buffer and search for it.
  269.          */
  270.         pat[cpos++] = c;
  271.  
  272.         /*
  273.          * Too many characters in the string?  Yup.  Complain
  274.          * about it, and restore the old search string and
  275.          * its jump tables.
  276.          */
  277.         if (cpos >= NPAT)
  278.         {
  279.             mlwrite(TEXT166);
  280. /*                "? Search string too long" */
  281.             bytecopy((char *) pat, pat_save, NPAT);
  282.             setjtable();
  283.             mmove_flag = TRUE;
  284.             return (FALSE);    /* Return an error, but stay. */
  285.         }
  286.  
  287.         pat[cpos] = 0;    /* null terminate the buffer  */
  288. #if    COLOR
  289.         /* set up the proper colors for the command line */
  290.         TTforg(gfcolor);
  291.         TTbacg(gbcolor);
  292. #endif
  293.         movecursor(term.t_nrow, col);    /* Position the cursor    */
  294.         col += echochar(c);    /* Echo the character          */
  295.         if (!status)    /* If we lost last time       */
  296.             TTbeep();    /* Feep again        */
  297.         else             /* Otherwise, we must have won*/
  298.             status = checknext(c, dir);    /* See if still matches or find next */
  299.     }
  300.  
  301.     curwp->w_dotp = curline;    /* Reset the line pointer        */
  302.     curwp->w_doto = curoff;    /*   and the offset to original value    */
  303.     curwp->w_flag |= WFMOVE;    /* Say we've moved            */
  304.     update(FALSE);        /* And force an update            */
  305.     mmove_flag = TRUE;
  306.     return (FALSE);
  307. }
  308.  
  309. /*
  310.  * This hack will search for the next occurrence of <pat> in the buffer,
  311.  * either forward or backward.  If we can't find any more matches, "point"
  312.  * is left where it was before.  If we do find a match, "point" will be at
  313.  * the end of the matched string for forward searches and at the beginning
  314.  * of the matched string for reverse searches.
  315.  */
  316.  
  317. int PASCAL NEAR scanmore(dir)
  318. int dir;                /* direction to search        */
  319. {
  320.     register int    status;    /* search status        */
  321.  
  322.     setjtable();        /* Set up fast search arrays    */
  323.  
  324. #if MAGIC
  325.     if (dir == FORWARD)
  326.         status = mcscanner(&mcdeltapat[0], dir, PTEND, 1);
  327.     else
  328.         status = mcscanner(&tapatledcm[0], dir, PTBEG, 1);
  329. #else
  330.     status = scanner(dir, (dir == REVERSE) ? PTBEG : PTEND, 1);
  331. #endif
  332.  
  333.     if (!status)
  334.         TTbeep();        /* Feep if search fails       */
  335.  
  336.     return (status);
  337. }
  338.  
  339. /*
  340.  * Trivial routine to insure that the next character in the search
  341.  * string is still true to whatever we're pointing to in the buffer.
  342.  * This routine will not attempt to move the "point" if the match
  343.  * fails, although it will implicitly move the "point" if we're
  344.  * forward searching, and find a match, since that's the way forward
  345.  * isearch works.  If we are reverse searching we compare all
  346.  * characters in the pattern string from "point" to the new end.
  347.  *
  348.  * If the compare fails, we return FALSE and call scanmore or something.
  349.  */
  350.  
  351. int PASCAL NEAR checknext(chr, dir)
  352. int chr;                /* Next char to look for    */
  353. int dir;                /* Search direction         */
  354. {
  355.     LINE *curline;        /* current line during scan    */
  356.     int curoff;            /* position within current line    */
  357.     register char *patrn;    /* The entire search string (incl chr) */
  358.     register int sts;    /* how well things go        */
  359.  
  360.     /* setup the local scan pointer to current "." */
  361.  
  362.     curline = curwp->w_dotp;    /* Get the current line structure */
  363.     curoff = curwp->w_doto;    /* Get the offset within that line */
  364.  
  365.     if (dir == FORWARD)
  366.         {                /* If searching forward        */
  367.         if (sts = !boundry(curline, curoff, FORWARD))
  368.             {
  369.             /* If it's what we're looking for, set the point
  370.              * and say that we've moved.
  371.              */
  372.             if (sts = eq(nextch(&curline, &curoff, FORWARD), chr))
  373.                 {
  374.                 curwp->w_dotp = curline;
  375.                 curwp->w_doto = curoff;
  376.                 curwp->w_flag |= WFMOVE;
  377.                 }
  378.             }
  379.         }
  380.     else {                /* Else, reverse search check. */
  381.         patrn = (char *) pat;
  382.         while (*patrn)
  383.             {            /* Loop for all characters in patrn   */
  384.             if ((sts = !boundry(curline, curoff, FORWARD)) == FALSE ||
  385.                 (sts = eq(nextch(&curline, &curoff, FORWARD), *patrn)) == FALSE)
  386.                 break;    /* Nope, just punt it then */
  387.             patrn++;
  388.             }
  389.         }
  390.  
  391.     /*
  392.      * If the 'next' character didn't fit in the pattern,
  393.      * let's go search for it somewhere else.
  394.      */
  395.     if (sts == FALSE)
  396.         sts = scanmore(dir);
  397.  
  398.     return (sts);        /* And return the status */
  399. }
  400.  
  401. /*
  402.  * Routine to get the next character or extended character from the input
  403.  * stream.  If we're reading from the real terminal, force a screen update
  404.  * before we get the char.  Otherwise, we must be re-executing the command
  405.  * string, so just return the next character.
  406.  */
  407.  
  408. int PASCAL NEAR get_char()
  409. {
  410.     int    c;
  411.     KEYTAB    *key;
  412.  
  413.     /* See if we're re-executing: */
  414.  
  415.     if (cmd_reexecute >= 0)    /* Is there an offset?        */
  416.         if ((c = cmd_buff[cmd_reexecute++]) != 0)
  417.             return (c);    /* Yes, return any character    */
  418.  
  419.     /* We're not re-executing (or aren't any more).  Try for a real char
  420.      */
  421.     cmd_reexecute = -1;    /* Say we're in real mode again    */
  422.     update(FALSE);        /* Pretty up the screen        */
  423.     if (posflag)
  424.         upmode();        /* and the modeline, if need be.*/
  425.     if (cmd_offset >= CMDBUFLEN - 1)
  426.         {                /* If we're getting too big ...    */
  427.         mlwrite(TEXT167);    /* Complain loudly and bitterly    */
  428. /*               "? command too long" */
  429.         return (isterm);    /* And force a quit        */
  430.         }
  431.  
  432.     /*
  433.      * If $isterm != meta character, then create the extended
  434.      * character as getcmd() does.
  435.      */
  436.     if ((c = get_key()) == isterm)
  437.         return (isterm);
  438.     if ((key = getbind(c)) != NULL)
  439.         {
  440.         if (key->k_ptr.fp == cex || key->k_ptr.fp == meta)
  441.             {
  442.             c = get_key();
  443. #if    SMOS
  444.             c = upperc(c&255) | (c & ~255); /* Force to upper */
  445. #else
  446.             c = upperc(c) | (c & ~255);    /* Force to upper */
  447. #endif
  448.             c |= (key->k_ptr.fp == cex) ? CTLX : META;
  449.             }
  450.         }
  451.  
  452.     cmd_buff[cmd_offset++] = c;    /* Save the char for next time    */
  453.     cmd_buff[cmd_offset] = '\0';    /* And terminate the buffer    */
  454.     return (c);            /* Return the character        */
  455. }
  456.  
  457. /*
  458.  * Hacky routine to re-eat a character.  This will save the character to be
  459.  * re-eaten by redirecting the input call to a routine here.  Hack, etc.
  460.  *
  461.  * Come here on the next term.t_getchar call:
  462.  */
  463.  
  464. int PASCAL NEAR uneat()
  465. {
  466.     int c;
  467.  
  468.     term.t_getchar = saved_get_char;    /* restore the routine address    */
  469.     c = eaten_char;        /* Get the re-eaten char    */
  470.     eaten_char = -1;    /* Clear the old char        */
  471.     return (c);            /* and return the last char    */
  472. }
  473.  
  474. VOID PASCAL NEAR reeat(c)
  475. int    c;
  476. {
  477.     if (eaten_char != -1)    /* If we've already been here    */
  478.         return;            /* Don't do it again        */
  479.     eaten_char = c;        /* Else, save the char for later*/
  480.     saved_get_char = term.t_getchar;    /* Save the char get routine    */
  481.     term.t_getchar = uneat;    /* Replace it with ours        */
  482. }
  483.  
  484. #else
  485. int PASCAL NEAR isearch(dir)
  486. int dir;
  487. {
  488. }
  489. #endif
  490.