home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume1 / 8711 / microemacs-3.9 / 7 < prev    next >
Text File  |  1987-11-17  |  31KB  |  1,052 lines

  1. Article 83 of comp.sources.misc:
  2. Path: tut!osu-cis!cbosgd!mandrill!hal!ncoast!allbery
  3. From: nwd@j.cc.purdue.edu (Daniel Lawrence)
  4. Newsgroups: comp.sources.misc
  5. Subject: MicroEmacs 3.9 (Part 7 of 16)
  6. Message-ID: <5654@ncoast.UUCP>
  7. Date: 14 Nov 87 21:10:12 GMT
  8. Sender: allbery@ncoast.UUCP
  9. Lines: 1037
  10. Approved: allbery@ncoast.UUCP
  11. X-Archive: comp.sources.misc/microemacs-3.9/6
  12.  
  13. # This is a shar archive.
  14. # Remove everything above this line.
  15. # Run the file through sh, not csh.
  16. # (type `sh mes.7')
  17. # If you do not see the message
  18. #    `mes.7 completed!'
  19. # then the file was incomplete.
  20. echo extracting - input.c
  21. sed 's/^X//' > input.c << 'FRIDAY_NIGHT'
  22. X/*    INPUT:    Various input routines for MicroEMACS
  23. X        written by Daniel Lawrence
  24. X        5/9/86                        */
  25. X
  26. X#include    <stdio.h>
  27. X#include    "estruct.h"
  28. X#include    "edef.h"
  29. X
  30. X/*
  31. X * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  32. X * ABORT. The ABORT status is returned if the user bumps out of the question
  33. X * with a ^G. Used any time a confirmation is required.
  34. X */
  35. X
  36. Xmlyesno(prompt)
  37. X
  38. Xchar *prompt;
  39. X
  40. X{
  41. X    char c;            /* input character */
  42. X    char buf[NPAT];        /* prompt to user */
  43. X
  44. X    for (;;) {
  45. X        /* build and prompt the user */
  46. X        strcpy(buf, prompt);
  47. X        strcat(buf, " [y/n]? ");
  48. X        mlwrite(buf);
  49. X
  50. X        /* get the responce */
  51. X        c = tgetc();
  52. X
  53. X        if (c == ectoc(abortc))        /* Bail out! */
  54. X            return(ABORT);
  55. X
  56. X        if (c=='y' || c=='Y')
  57. X            return(TRUE);
  58. X
  59. X        if (c=='n' || c=='N')
  60. X            return(FALSE);
  61. X    }
  62. X}
  63. X
  64. X/*
  65. X * Write a prompt into the message line, then read back a response. Keep
  66. X * track of the physical position of the cursor. If we are in a keyboard
  67. X * macro throw the prompt away, and return the remembered response. This
  68. X * lets macros run at full speed. The reply is always terminated by a carriage
  69. X * return. Handle erase, kill, and abort keys.
  70. X */
  71. X
  72. Xmlreply(prompt, buf, nbuf)
  73. X    char *prompt;
  74. X    char *buf;
  75. X{
  76. X    return(nextarg(prompt, buf, nbuf, ctoec('\n')));
  77. X}
  78. X
  79. Xmlreplyt(prompt, buf, nbuf, eolchar)
  80. X
  81. Xchar *prompt;
  82. Xchar *buf;
  83. Xint eolchar;
  84. X
  85. X{
  86. X    return(nextarg(prompt, buf, nbuf, eolchar));
  87. X}
  88. X
  89. X/*    ectoc:    expanded character to character
  90. X        colapse the CTRL and SPEC flags back into an ascii code   */
  91. X
  92. Xectoc(c)
  93. X
  94. Xint c;
  95. X
  96. X{
  97. X    if (c & CTRL)
  98. X        c = c & ~(CTRL | 0x40);
  99. X    if (c & SPEC)
  100. X        c= c & 255;
  101. X    return(c);
  102. X}
  103. X
  104. X/*    ctoec:    character to extended character
  105. X        pull out the CTRL and SPEC prefixes (if possible)    */
  106. X
  107. Xctoec(c)
  108. X
  109. Xint c;
  110. X
  111. X{
  112. X        if (c>=0x00 && c<=0x1F)
  113. X                c = CTRL | (c+'@');
  114. X        return (c);
  115. X}
  116. X/* get a command name from the command line. Command completion means
  117. X   that pressing a <SPACE> will attempt to complete an unfinished command
  118. X   name if it is unique.
  119. X*/
  120. X
  121. Xint (*getname())()
  122. X
  123. X{
  124. X#if    ST520 & LATTICE
  125. X#define register        
  126. X#endif
  127. X    register int cpos;    /* current column on screen output */
  128. X    register int c;
  129. X    register char *sp;    /* pointer to string for output */
  130. X    register NBIND *ffp;    /* first ptr to entry in name binding table */
  131. X    register NBIND *cffp;    /* current ptr to entry in name binding table */
  132. X    register NBIND *lffp;    /* last ptr to entry in name binding table */
  133. X    char buf[NSTRING];    /* buffer to hold tentative command name */
  134. X    int (*fncmatch())();
  135. X
  136. X    /* starting at the beginning of the string buffer */
  137. X    cpos = 0;
  138. X
  139. X    /* if we are executing a command line get the next arg and match it */
  140. X    if (clexec) {
  141. X        if (macarg(buf) != TRUE)
  142. X            return(FALSE);
  143. X        return(fncmatch(&buf[0]));
  144. X    }
  145. X
  146. X    /* build a name string from the keyboard */
  147. X    while (TRUE) {
  148. X        c = tgetc();
  149. X
  150. X        /* if we are at the end, just match it */
  151. X        if (c == 0x0d) {
  152. X            buf[cpos] = 0;
  153. X
  154. X            /* and match it off */
  155. X            return(fncmatch(&buf[0]));
  156. X
  157. X        } else if (c == ectoc(abortc)) {    /* Bell, abort */
  158. X            ctrlg(FALSE, 0);
  159. X            TTflush();
  160. X            return( (int (*)()) NULL);
  161. X
  162. X        } else if (c == 0x7F || c == 0x08) {    /* rubout/erase */
  163. X            if (cpos != 0) {
  164. X                TTputc('\b');
  165. X                TTputc(' ');
  166. X                TTputc('\b');
  167. X                --ttcol;
  168. X                --cpos;
  169. X                TTflush();
  170. X            }
  171. X
  172. X        } else if (c == 0x15) {    /* C-U, kill */
  173. X            while (cpos != 0) {
  174. X                TTputc('\b');
  175. X                TTputc(' ');
  176. X                TTputc('\b');
  177. X                --cpos;
  178. X                --ttcol;
  179. X            }
  180. X
  181. X            TTflush();
  182. X
  183. X        } else if (c == ' ') {
  184. X/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  185. X    /* attempt a completion */
  186. X    buf[cpos] = 0;        /* terminate it for us */
  187. X    ffp = &names[0];    /* scan for matches */
  188. X    while (ffp->n_func != NULL) {
  189. X        if (strncmp(buf, ffp->n_name, strlen(buf)) == 0) {
  190. X            /* a possible match! More than one? */
  191. X            if ((ffp + 1)->n_func == NULL ||
  192. X               (strncmp(buf, (ffp+1)->n_name, strlen(buf)) != 0)) {
  193. X                /* no...we match, print it */
  194. X                sp = ffp->n_name + cpos;
  195. X                while (*sp)
  196. X                    TTputc(*sp++);
  197. X                TTflush();
  198. X                return(ffp->n_func);
  199. X            } else {
  200. X/* << << << << << << << << << << << << << << << << << */
  201. X    /* try for a partial match against the list */
  202. X
  203. X    /* first scan down until we no longer match the current input */
  204. X    lffp = (ffp + 1);
  205. X    while ((lffp+1)->n_func != NULL) {
  206. X        if (strncmp(buf, (lffp+1)->n_name, strlen(buf)) != 0)
  207. X            break;
  208. X        ++lffp;
  209. X    }
  210. X
  211. X    /* and now, attempt to partial complete the string, char at a time */
  212. X    while (TRUE) {
  213. X        /* add the next char in */
  214. X        buf[cpos] = ffp->n_name[cpos];
  215. X
  216. X        /* scan through the candidates */
  217. X        cffp = ffp + 1;
  218. X        while (cffp <= lffp) {
  219. X            if (cffp->n_name[cpos] != buf[cpos])
  220. X                goto onward;
  221. X            ++cffp;
  222. X        }
  223. X
  224. X        /* add the character */
  225. X        TTputc(buf[cpos++]);
  226. X    }
  227. X/* << << << << << << << << << << << << << << << << << */
  228. X            }
  229. X        }
  230. X        ++ffp;
  231. X    }
  232. X
  233. X    /* no match.....beep and onward */
  234. X    TTbeep();
  235. Xonward:;
  236. X    TTflush();
  237. X/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  238. X        } else {
  239. X            if (cpos < NSTRING-1 && c > ' ') {
  240. X                buf[cpos++] = c;
  241. X                TTputc(c);
  242. X            }
  243. X
  244. X            ++ttcol;
  245. X            TTflush();
  246. X        }
  247. X    }
  248. X}
  249. X
  250. X/*    tgetc:    Get a key from the terminal driver, resolve any keyboard
  251. X        macro action                    */
  252. X
  253. Xint tgetc()
  254. X
  255. X{
  256. X    int c;    /* fetched character */
  257. X
  258. X    /* if we are playing a keyboard macro back, */
  259. X    if (kbdmode == PLAY) {
  260. X
  261. X        /* if there is some left... */
  262. X        if (kbdptr < kbdend)
  263. X            return((int)*kbdptr++);
  264. X
  265. X        /* at the end of last repitition? */
  266. X        if (--kbdrep < 1) {
  267. X            kbdmode = STOP;
  268. X#if    VISMAC == 0
  269. X            /* force a screen update after all is done */
  270. X            update(FALSE);
  271. X#endif
  272. X        } else {
  273. X
  274. X            /* reset the macro to the begining for the next rep */
  275. X            kbdptr = &kbdm[0];
  276. X            return((int)*kbdptr++);
  277. X        }
  278. X    }
  279. X
  280. X    /* fetch a character from the terminal driver */
  281. X    c = TTgetc();
  282. X
  283. X    /* record it for $lastkey */
  284. X    lastkey = c;
  285. X
  286. X    /* save it if we need to */
  287. X    if (kbdmode == RECORD) {
  288. X        *kbdptr++ = c;
  289. X        kbdend = kbdptr;
  290. X
  291. X        /* don't overrun the buffer */
  292. X        if (kbdptr == &kbdm[NKBDM - 1]) {
  293. X            kbdmode = STOP;
  294. X            TTbeep();
  295. X        }
  296. X    }
  297. X
  298. X    /* and finally give the char back */
  299. X    return(c);
  300. X}
  301. X
  302. X/*    GET1KEY:    Get one keystroke. The only prefixs legal here
  303. X            are the SPEC and CTRL prefixes.
  304. X                                */
  305. X
  306. Xget1key()
  307. X
  308. X{
  309. X    int    c;
  310. X#if    AMIGA
  311. X    int    d;
  312. X#endif
  313. X
  314. X    /* get a keystroke */
  315. X        c = tgetc();
  316. X
  317. X#if    MSDOS | ST520
  318. X    if (c == 0) {                /* Apply SPEC prefix    */
  319. X            c = tgetc();
  320. X            if (c>=0x00 && c<=0x1F)        /* control key? */
  321. X                    c = CTRL | (c+'@');
  322. X        return(SPEC | c);
  323. X    }
  324. X#endif
  325. X
  326. X#if    AMIGA
  327. X    /* apply SPEC prefix */
  328. X    if ((unsigned)c == 155) {
  329. X        c = tgetc();
  330. X
  331. X        /* first try to see if it is a cursor key */
  332. X        if ((c >= 'A' && c <= 'D') || c == 'S' || c == 'T')
  333. X            return(SPEC | c);
  334. X
  335. X        /* next, a 2 char sequence */
  336. X        d = tgetc();
  337. X        if (d == '~')
  338. X            return(SPEC | c);
  339. X
  340. X        /* decode a 3 char sequence */
  341. X        c = d + 32;
  342. X        /* if a shifted function key, eat the tilde */
  343. X        if (d >= '0' && d <= '9')
  344. X            d = tgetc();
  345. X        return(SPEC | c);
  346. X    }
  347. X#endif
  348. X
  349. X#if  WANGPC
  350. X    if (c == 0x1F) {            /* Apply SPEC prefix    */
  351. X            c = tgetc();
  352. X        return(SPEC | c);
  353. X    }
  354. X#endif
  355. X
  356. X        if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  357. X                c = CTRL | (c+'@');
  358. X        return (c);
  359. X}
  360. X
  361. X/*    GETCMD:    Get a command from the keyboard. Process all applicable
  362. X        prefix keys
  363. X                            */
  364. Xgetcmd()
  365. X
  366. X{
  367. X    int c;        /* fetched keystroke */
  368. X
  369. X    /* get initial character */
  370. X    c = get1key();
  371. X
  372. X    /* process META prefix */
  373. X#if    VT100
  374. X    /* if ESC must be recognized.... change this to a 1 */
  375. X    if (c == metac || c == (CTRL | '['))
  376. X#else
  377. X    if (c == metac) {
  378. X#endif
  379. X        c = get1key();
  380. X            if (islower(c))        /* Force to upper */
  381. X                    c ^= DIFCASE;
  382. X            if (c>=0x00 && c<=0x1F)        /* control key */
  383. X                c = CTRL | (c+'@');
  384. X#if    VT100
  385. X        if (c == '[' || c == 'O') {
  386. X            c = get1key();
  387. X            return(SPEC | c);
  388. X        }
  389. X#endif
  390. X        return(META | c);
  391. X    }
  392. X
  393. X    /* process CTLX prefix */
  394. X    if (c == ctlxc) {
  395. X        c = get1key();
  396. X            if (c>='a' && c<='z')        /* Force to upper */
  397. X                    c -= 0x20;
  398. X            if (c>=0x00 && c<=0x1F)        /* control key */
  399. X                c = CTRL | (c+'@');
  400. X        return(CTLX | c);
  401. X    }
  402. X
  403. X    /* otherwise, just return it */
  404. X    return(c);
  405. X}
  406. X
  407. X/*    A more generalized prompt/reply function allowing the caller
  408. X    to specify the proper terminator. If the terminator is not
  409. X    a return ('\n') it will echo as "<NL>"
  410. X                            */
  411. Xgetstring(prompt, buf, nbuf, eolchar)
  412. X
  413. Xchar *prompt;
  414. Xchar *buf;
  415. Xint eolchar;
  416. X
  417. X{
  418. X    register int cpos;    /* current character position in string */
  419. X    register int c;
  420. X    register int quotef;    /* are we quoting the next char? */
  421. X
  422. X    cpos = 0;
  423. X    quotef = FALSE;
  424. X
  425. X    /* prompt the user for the input string */
  426. X    mlwrite(prompt);
  427. X
  428. X    for (;;) {
  429. X        /* get a character from the user */
  430. X        c = get1key();
  431. X
  432. X        /* If it is a <ret>, change it to a <NL> */
  433. X        if (c == (CTRL | 0x4d))
  434. X            c = CTRL | 0x40 | '\n';
  435. X
  436. X        /* if they hit the line terminate, wrap it up */
  437. X        if (c == eolchar && quotef == FALSE) {
  438. X            buf[cpos++] = 0;
  439. X
  440. X            /* clear the message line */
  441. X            mlwrite("");
  442. X            TTflush();
  443. X
  444. X            /* if we default the buffer, return FALSE */
  445. X            if (buf[0] == 0)
  446. X                return(FALSE);
  447. X
  448. X            return(TRUE);
  449. X        }
  450. X
  451. X        /* change from command form back to character form */
  452. X        c = ectoc(c);
  453. X
  454. X        if (c == ectoc(abortc) && quotef == FALSE) {
  455. X            /* Abort the input? */
  456. X            ctrlg(FALSE, 0);
  457. X            TTflush();
  458. X            return(ABORT);
  459. X        } else if ((c==0x7F || c==0x08) && quotef==FALSE) {
  460. X            /* rubout/erase */
  461. X            if (cpos != 0) {
  462. X                outstring("\b \b");
  463. X                --ttcol;
  464. X
  465. X                if (buf[--cpos] < 0x20) {
  466. X                    outstring("\b \b");
  467. X                    --ttcol;
  468. X                }
  469. X
  470. X                if (buf[cpos] == '\n') {
  471. X                    outstring("\b\b  \b\b");
  472. X                    ttcol -= 2;
  473. X                }
  474. X                TTflush();
  475. X            }
  476. X
  477. X        } else if (c == 0x15 && quotef == FALSE) {
  478. X            /* C-U, kill */
  479. X            while (cpos != 0) {
  480. X                outstring("\b \b");
  481. X                --ttcol;
  482. X
  483. X                if (buf[--cpos] < 0x20) {
  484. X                    outstring("\b \b");
  485. X                    --ttcol;
  486. X                }
  487. X            }
  488. X            TTflush();
  489. X
  490. X        } else if (c == quotec && quotef == FALSE) {
  491. X            quotef = TRUE;
  492. X        } else {
  493. X            quotef = FALSE;
  494. X            if (cpos < nbuf-1) {
  495. X                buf[cpos++] = c;
  496. X
  497. X                if ((c < ' ') && (c != '\n')) {
  498. X                    outstring("^");
  499. X                    ++ttcol;
  500. X                    c ^= 0x40;
  501. X                }
  502. X
  503. X                if (c != '\n') {
  504. X                    if (disinp)
  505. X                        TTputc(c);
  506. X                } else {    /* put out <NL> for <ret> */
  507. X                    outstring("<NL>");
  508. X                    ttcol += 3;
  509. X                }
  510. X                ++ttcol;
  511. X                TTflush();
  512. X            }
  513. X        }
  514. X    }
  515. X}
  516. X
  517. Xoutstring(s)    /* output a string of input characters */
  518. X
  519. Xchar *s;    /* string to output */
  520. X
  521. X{
  522. X    if (disinp)
  523. X        while (*s)
  524. X            TTputc(*s++);
  525. X}
  526. X
  527. Xostring(s)    /* output a string of output characters */
  528. X
  529. Xchar *s;    /* string to output */
  530. X
  531. X{
  532. X    if (discmd)
  533. X        while (*s)
  534. X            TTputc(*s++);
  535. X}
  536. X
  537. FRIDAY_NIGHT
  538. echo extracting - isearch.c
  539. sed 's/^X//' > isearch.c << 'FRIDAY_NIGHT'
  540. X/*
  541. X * The functions in this file implement commands that perform incremental
  542. X * searches in the forward and backward directions.  This "ISearch" command
  543. X * is intended to emulate the same command from the original EMACS 
  544. X * implementation (ITS).  Contains references to routines internal to
  545. X * SEARCH.C.
  546. X *
  547. X * REVISION HISTORY:
  548. X *
  549. X *    D. R. Banks 9-May-86
  550. X *    - added ITS EMACSlike ISearch
  551. X *
  552. X *    John M. Gamble 5-Oct-86
  553. X *    - Made iterative search use search.c's scanner() routine.
  554. X *      This allowed the elimination of bakscan().
  555. X *    - Put isearch constants into estruct.h
  556. X *    - Eliminated the passing of 'status' to scanmore() and
  557. X *      checknext(), since there were no circumstances where
  558. X *      it ever equalled FALSE.
  559. X */
  560. X
  561. X#include        <stdio.h>
  562. X#include    "estruct.h"
  563. X#include        "edef.h"
  564. X
  565. X#if    ISRCH
  566. X
  567. Xextern int scanner();            /* Handy search routine */
  568. Xextern int eq();            /* Compare chars, match case */
  569. X
  570. X/* A couple of "own" variables for re-eat */
  571. X
  572. Xint    (*saved_get_char)();        /* Get character routine */
  573. Xint    eaten_char = -1;        /* Re-eaten char */
  574. X
  575. X/* A couple more "own" variables for the command string */
  576. X
  577. Xint    cmd_buff[CMDBUFLEN];        /* Save the command args here */
  578. Xint    cmd_offset;            /* Current offset into command buff */
  579. Xint    cmd_reexecute = -1;        /* > 0 if re-executing command */
  580. X
  581. X
  582. X/*
  583. X * Subroutine to do incremental reverse search.  It actually uses the
  584. X * same code as the normal incremental search, as both can go both ways.
  585. X */
  586. Xint risearch(f, n)
  587. X{
  588. X    LINE *curline;            /* Current line on entry          */
  589. X    int  curoff;            /* Current offset on entry          */
  590. X
  591. X    /* remember the initial . on entry: */
  592. X
  593. X    curline = curwp->w_dotp;        /* Save the current line pointer      */
  594. X    curoff  = curwp->w_doto;        /* Save the current offset          */
  595. X
  596. X    /* Make sure the search doesn't match where we already are:              */
  597. X
  598. X    backchar(TRUE, 1);            /* Back up a character              */
  599. X
  600. X    if (!(isearch(f, -n)))        /* Call ISearch backwards          */
  601. X    {                    /* If error in search:              */
  602. X    curwp->w_dotp = curline;    /* Reset the line pointer          */
  603. X    curwp->w_doto = curoff;        /*  and the offset to original value  */
  604. X    curwp->w_flag |= WFMOVE;    /* Say we've moved              */
  605. X    update(FALSE);            /* And force an update              */
  606. X    mlwrite ("[search failed]");    /* Say we died                  */
  607. X    } else mlerase ();            /* If happy, just erase the cmd line  */
  608. X}
  609. X
  610. X/* Again, but for the forward direction */
  611. X
  612. Xint fisearch(f, n)
  613. X{
  614. X    LINE *curline;            /* Current line on entry          */
  615. X    int  curoff;            /* Current offset on entry          */
  616. X
  617. X    /* remember the initial . on entry: */
  618. X
  619. X    curline = curwp->w_dotp;        /* Save the current line pointer      */
  620. X    curoff  = curwp->w_doto;        /* Save the current offset          */
  621. X
  622. X    /* do the search */
  623. X
  624. X    if (!(isearch(f, n)))        /* Call ISearch forwards          */
  625. X    {                    /* If error in search:              */
  626. X    curwp->w_dotp = curline;    /* Reset the line pointer          */
  627. X    curwp->w_doto = curoff;        /*  and the offset to original value  */
  628. X    curwp->w_flag |= WFMOVE;    /* Say we've moved              */
  629. X    update(FALSE);            /* And force an update              */
  630. X    mlwrite ("[search failed]");    /* Say we died                  */
  631. X    } else mlerase ();            /* If happy, just erase the cmd line  */
  632. X}
  633. X
  634. X/*
  635. X * Subroutine to do an incremental search.  In general, this works similarly
  636. X * to the older micro-emacs search function, except that the search happens
  637. X * as each character is typed, with the screen and cursor updated with each
  638. X * new search character.
  639. X *
  640. X * While searching forward, each successive character will leave the cursor
  641. X * at the end of the entire matched string.  Typing a Control-S or Control-X
  642. X * will cause the next occurrence of the string to be searched for (where the
  643. X * next occurrence does NOT overlap the current occurrence).  A Control-R will
  644. X * change to a backwards search, META will terminate the search and Control-G
  645. X * will abort the search.  Rubout will back up to the previous match of the
  646. X * string, or if the starting point is reached first, it will delete the
  647. X * last character from the search string.
  648. X *
  649. X * While searching backward, each successive character will leave the cursor
  650. X * at the beginning of the matched string.  Typing a Control-R will search
  651. X * backward for the next occurrence of the string.  Control-S or Control-X
  652. X * will revert the search to the forward direction.  In general, the reverse
  653. X * incremental search is just like the forward incremental search inverted.
  654. X *
  655. X * In all cases, if the search fails, the user will be feeped, and the search
  656. X * will stall until the pattern string is edited back into something that
  657. X * exists (or until the search is aborted).
  658. X */
  659. Xisearch(f, n)
  660. X{
  661. X    int            status;        /* Search status */
  662. X    int            col;        /* prompt column */
  663. X    register int    cpos;        /* character number in search string  */
  664. X    register int    c;        /* current input character */
  665. X    register int    expc;        /* function expanded input char          */
  666. X    char        pat_save[NPAT];    /* Saved copy of the old pattern str  */
  667. X    LINE        *curline;    /* Current line on entry          */
  668. X    int            curoff;        /* Current offset on entry          */
  669. X    int            init_direction;    /* The initial search direction          */
  670. X
  671. X    /* Initialize starting conditions */
  672. X
  673. X    cmd_reexecute = -1;        /* We're not re-executing (yet?)      */
  674. X    cmd_offset = 0;            /* Start at the beginning of the buff */
  675. X    cmd_buff[0] = '\0';        /* Init the command buffer          */
  676. X    strncpy (pat_save, pat, NPAT);    /* Save the old pattern string          */
  677. X    curline = curwp->w_dotp;        /* Save the current line pointer      */
  678. X    curoff  = curwp->w_doto;        /* Save the current offset          */
  679. X    init_direction = n;            /* Save the initial search direction  */
  680. X
  681. X    /* This is a good place to start a re-execution: */
  682. X
  683. Xstart_over:
  684. X
  685. X    /* ask the user for the text of a pattern */
  686. X    col = promptpattern("ISearch: ");        /* Prompt, remember the col   */
  687. X
  688. X    cpos = 0;                    /* Start afresh              */
  689. X    status = TRUE;                /* Assume everything's cool   */
  690. X
  691. X    /*
  692. X       Get the first character in the pattern.  If we get an initial Control-S
  693. X       or Control-R, re-use the old search string and find the first occurrence
  694. X     */
  695. X
  696. X    c = ectoc(expc = get_char());        /* Get the first character    */
  697. X    if ((c == IS_FORWARD) ||
  698. X        (c == IS_REVERSE) ||
  699. X        (c == IS_VMSFORW))            /* Reuse old search string?   */
  700. X    {
  701. X        for (cpos = 0; pat[cpos] != 0; cpos++)    /* Yup, find the length          */
  702. X            col = echochar(pat[cpos],col);    /*  and re-echo the string    */
  703. X    if (c == IS_REVERSE) {            /* forward search?          */
  704. X        n = -1;                /* No, search in reverse      */
  705. X        backchar (TRUE, 1);            /* Be defensive about EOB     */
  706. X    } else
  707. X        n = 1;                /* Yes, search forward          */
  708. X    status = scanmore(pat, n);        /* Do the search          */
  709. X    c = ectoc(expc = get_char());        /* Get another character      */
  710. X    }
  711. X
  712. X    /* Top of the per character loop */
  713. X            
  714. X    for (;;)                    /* ISearch per character loop */
  715. X    {
  716. X    /* Check for special characters first: */
  717. X    /* Most cases here change the search */
  718. X
  719. X    if (expc == metac)            /* Want to quit searching?    */
  720. X        return (TRUE);            /* Quit searching now          */
  721. X
  722. X    switch (c)                /* dispatch on the input char */
  723. X    {
  724. X      case IS_ABORT:            /* If abort search request    */
  725. X        return(FALSE);            /* Quit searching again          */
  726. X
  727. X      case IS_REVERSE:            /* If backward search          */
  728. X      case IS_FORWARD:            /* If forward search          */
  729. X      case IS_VMSFORW:            /*  of either flavor          */
  730. X        if (c == IS_REVERSE)        /* If reverse search          */
  731. X        n = -1;                /* Set the reverse direction  */
  732. X        else                /* Otherwise,               */
  733. X        n = 1;                /*  go forward              */
  734. X        status = scanmore(pat, n);        /* Start the search again     */
  735. X        c = ectoc(expc = get_char());    /* Get the next char          */
  736. X        continue;                /* Go continue with the search*/
  737. X
  738. X      case IS_NEWLINE:            /* Carriage return          */
  739. X        c = '\n';                /* Make it a new line          */
  740. X        break;                /* Make sure we use it          */
  741. X
  742. X      case IS_QUOTE:            /* Quote character          */
  743. X      case IS_VMSQUOTE:            /*  of either variety          */
  744. X        c = ectoc(expc = get_char());    /* Get the next char          */
  745. X
  746. X      case IS_TAB:                /* Generically allowed          */
  747. X      case '\n':                /*  controlled characters     */
  748. X        break;                /* Make sure we use it          */
  749. X
  750. X      case IS_BACKSP:            /* If a backspace:            */
  751. X      case IS_RUBOUT:            /*  or if a Rubout:          */
  752. X        if (cmd_offset <= 1)        /* Anything to delete?          */
  753. X        return (TRUE);            /* No, just exit          */
  754. X        --cmd_offset;            /* Back up over the Rubout    */
  755. X        cmd_buff[--cmd_offset] = '\0';    /* Yes, delete last char   */
  756. X        curwp->w_dotp = curline;        /* Reset the line pointer     */
  757. X        curwp->w_doto = curoff;        /*  and the offset          */
  758. X        n = init_direction;            /* Reset the search direction */
  759. X        strncpy (pat, pat_save, NPAT);    /* Restore the old search str */
  760. X        cmd_reexecute = 0;            /* Start the whole mess over  */
  761. X        goto start_over;            /* Let it take care of itself */
  762. X
  763. X      /* Presumably a quasi-normal character comes here */
  764. X
  765. X      default:                /* All other chars              */
  766. X        if (c < ' ')            /* Is it printable?          */
  767. X        {                    /* Nope.              */
  768. X        reeat (c);            /* Re-eat the char          */
  769. X        return (TRUE);            /* And return the last status */
  770. X        }
  771. X    }  /* Switch */
  772. X
  773. X    /* I guess we got something to search for, so search for it          */
  774. X
  775. X    pat[cpos++] = c;            /* put the char in the buffer */
  776. X    if (cpos >= NPAT)            /* too many chars in string?  */
  777. X    {                    /* Yup.  Complain about it    */
  778. X        mlwrite("? Search string too long");
  779. X        return(TRUE);            /* Return an error          */
  780. X    }
  781. X    pat[cpos] = 0;                /* null terminate the buffer  */
  782. X    col = echochar(c,col);            /* Echo the character          */
  783. X    if (!status) {                /* If we lost last time          */
  784. X        TTputc(BELL);        /* Feep again              */
  785. X        TTflush();            /* see that the feep feeps    */
  786. X    } else                    /* Otherwise, we must have won*/
  787. X        if (!(status = checknext(c, pat, n))) /* See if match          */
  788. X        status = scanmore(pat, n);    /*  or find the next match    */
  789. X    c = ectoc(expc = get_char());        /* Get the next char          */
  790. X    } /* for {;;} */
  791. X}
  792. X
  793. X/*
  794. X * Trivial routine to insure that the next character in the search string is
  795. X * still true to whatever we're pointing to in the buffer.  This routine will
  796. X * not attempt to move the "point" if the match fails, although it will 
  797. X * implicitly move the "point" if we're forward searching, and find a match,
  798. X * since that's the way forward isearch works.
  799. X *
  800. X * If the compare fails, we return FALSE and assume the caller will call
  801. X * scanmore or something.
  802. X */
  803. X
  804. Xint checknext (chr, patrn, dir)    /* Check next character in search string */
  805. Xchar    chr;            /* Next char to look for         */
  806. Xchar    *patrn;            /* The entire search string (incl chr)   */
  807. Xint    dir;            /* Search direction             */
  808. X{
  809. X    register LINE *curline;        /* current line during scan          */
  810. X    register int curoff;        /* position within current line          */
  811. X    register int buffchar;        /* character at current position      */
  812. X    int status;                /* how well things go              */
  813. X
  814. X
  815. X    /* setup the local scan pointer to current "." */
  816. X
  817. X    curline = curwp->w_dotp;        /* Get the current line structure     */
  818. X    curoff  = curwp->w_doto;        /* Get the offset within that line    */
  819. X
  820. X    if (dir > 0)            /* If searching forward              */
  821. X    {
  822. X        if (curoff == llength(curline)) /* If at end of line              */
  823. X        {
  824. X        curline = lforw(curline);    /* Skip to the next line          */
  825. X        if (curline == curbp->b_linep)
  826. X        return (FALSE);        /* Abort if at end of buffer          */
  827. X        curoff = 0;            /* Start at the beginning of the line */
  828. X        buffchar = '\n';        /* And say the next char is NL          */
  829. X    } else
  830. X        buffchar = lgetc(curline, curoff++); /* Get the next char          */
  831. X    if (status = eq(buffchar, chr))    /* Is it what we're looking for?      */
  832. X    {
  833. X        curwp->w_dotp = curline;    /* Yes, set the buffer's point          */
  834. X        curwp->w_doto = curoff;    /*  to the matched character          */
  835. X        curwp->w_flag |= WFMOVE;    /* Say that we've moved              */
  836. X    }
  837. X    return (status);        /* And return the status          */
  838. X    } else                /* Else, if reverse search:          */
  839. X    return (match_pat (patrn));    /* See if we're in the right place    */
  840. X}
  841. X
  842. X/*
  843. X * This hack will search for the next occurrence of <pat> in the buffer, either
  844. X * forward or backward.  It is called with the status of the prior search
  845. X * attempt, so that it knows not to bother if it didn't work last time.  If
  846. X * we can't find any more matches, "point" is left where it was before.  If
  847. X * we do find a match, "point" will be at the end of the matched string for
  848. X * forward searches and at the beginning of the matched string for reverse
  849. X * searches.
  850. X */
  851. Xint scanmore(patrn, dir)    /* search forward or back for a pattern          */
  852. Xchar    *patrn;            /* string to scan for                  */
  853. Xint    dir;            /* direction to search                  */
  854. X{
  855. X    int    sts;            /* search status              */
  856. X
  857. X        if (dir < 0)                /* reverse search?          */
  858. X        {
  859. X        rvstrcpy(tap, patrn);        /* Put reversed string in tap */
  860. X        sts = scanner(tap, REVERSE, PTBEG);
  861. X    }
  862. X    else
  863. X        sts = scanner(patrn, FORWARD, PTEND);    /* Nope. Go forward   */
  864. X
  865. X    if (!sts)
  866. X    {
  867. X        TTputc(BELL);    /* Feep if search fails       */
  868. X        TTflush();        /* see that the feep feeps    */
  869. X    }
  870. X
  871. X    return(sts);                /* else, don't even try          */
  872. X}
  873. X
  874. X/*
  875. X * The following is a worker subroutine used by the reverse search.  It
  876. X * compares the pattern string with the characters at "." for equality. If
  877. X * any characters mismatch, it will return FALSE.
  878. X *
  879. X * This isn't used for forward searches, because forward searches leave "."
  880. X * at the end of the search string (instead of in front), so all that needs to
  881. X * be done is match the last char input.
  882. X */
  883. X
  884. Xint match_pat (patrn)    /* See if the pattern string matches string at "."   */
  885. Xchar    *patrn;        /* String to match to buffer                 */
  886. X{
  887. X    register int  i;            /* Generic loop index/offset          */
  888. X    register int buffchar;        /* character at current position      */
  889. X    register LINE *curline;        /* current line during scan          */
  890. X    register int curoff;        /* position within current line          */
  891. X
  892. X    /* setup the local scan pointer to current "." */
  893. X
  894. X    curline = curwp->w_dotp;        /* Get the current line structure     */
  895. X    curoff  = curwp->w_doto;        /* Get the offset within that line    */
  896. X
  897. X    /* top of per character compare loop: */
  898. X
  899. X    for (i = 0; i < strlen(patrn); i++)    /* Loop for all characters in patrn   */
  900. X    {
  901. X        if (curoff == llength(curline)) /* If at end of line              */
  902. X        {
  903. X        curline = lforw(curline);    /* Skip to the next line          */
  904. X        curoff = 0;            /* Start at the beginning of the line */
  905. X        if (curline == curbp->b_linep)
  906. X        return (FALSE);        /* Abort if at end of buffer          */
  907. X        buffchar = '\n';        /* And say the next char is NL          */
  908. X    } else
  909. X        buffchar = lgetc(curline, curoff++); /* Get the next char          */
  910. X    if (!eq(buffchar, patrn[i]))    /* Is it what we're looking for?      */
  911. X        return (FALSE);        /* Nope, just punt it then          */
  912. X    }
  913. X    return (TRUE);            /* Everything matched? Let's celebrate*/
  914. X}
  915. X
  916. X/* Routine to prompt for I-Search string. */
  917. X
  918. Xint promptpattern(prompt)
  919. Xchar *prompt;
  920. X{
  921. X    char tpat[NPAT+20];
  922. X
  923. X    strcpy(tpat, prompt);        /* copy prompt to output string */
  924. X    strcat(tpat, " [");            /* build new prompt string */
  925. X    expandp(pat, &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  926. X    strcat(tpat, "]<META>: ");
  927. X
  928. X    /* check to see if we are executing a command line */
  929. X    if (!clexec) {
  930. X    mlwrite(tpat);
  931. X    }
  932. X    return(strlen(tpat));
  933. X}
  934. X
  935. X/* routine to echo i-search characters */
  936. X
  937. Xint echochar(c,col)
  938. Xint    c;    /* character to be echoed */
  939. Xint    col;    /* column to be echoed in */
  940. X{
  941. X    movecursor(term.t_nrow,col);        /* Position the cursor          */
  942. X    if ((c < ' ') || (c == 0x7F))        /* Control character?          */
  943. X    {
  944. X    switch (c)                /* Yes, dispatch special cases*/
  945. X    {
  946. X      case '\n':                /* Newline              */
  947. X        TTputc('<');
  948. X        TTputc('N');
  949. X        TTputc('L');
  950. X        TTputc('>');
  951. X        col += 3;
  952. X        break;
  953. X
  954. X      case '\t':                /* Tab                  */
  955. X        TTputc('<');
  956. X        TTputc('T');
  957. X        TTputc('A');
  958. X        TTputc('B');
  959. X        TTputc('>');
  960. X        col += 4;
  961. X        break;
  962. X
  963. X      case 0x7F:                /* Rubout:              */
  964. X        TTputc('^');        /* Output a funny looking     */
  965. X        TTputc('?');        /*  indication of Rubout      */
  966. X        col++;                /* Count the extra char       */
  967. X        break;
  968. X
  969. X      default:                /* Vanilla control char       */
  970. X        TTputc('^');        /* Yes, output prefix          */
  971. X            TTputc(c+0x40);        /* Make it "^X"              */
  972. X        col++;                /* Count this char          */
  973. X    }
  974. X    } else
  975. X    TTputc(c);            /* Otherwise, output raw char */
  976. X    TTflush();                /* Flush the output          */
  977. X    return(++col);                /* return the new column no   */
  978. X}
  979. X
  980. X/*
  981. X * Routine to get the next character from the input stream.  If we're reading
  982. X * from the real terminal, force a screen update before we get the char. 
  983. X * Otherwise, we must be re-executing the command string, so just return the
  984. X * next character.
  985. X */
  986. X
  987. Xint get_char ()
  988. X{
  989. X    int    c;                /* A place to get a character          */
  990. X
  991. X    /* See if we're re-executing: */
  992. X
  993. X    if (cmd_reexecute >= 0)        /* Is there an offset?              */
  994. X    if ((c = cmd_buff[cmd_reexecute++]) != 0)
  995. X        return (c);            /* Yes, return any character          */
  996. X
  997. X    /* We're not re-executing (or aren't any more).  Try for a real char      */
  998. X
  999. X    cmd_reexecute = -1;        /* Say we're in real mode again          */
  1000. X    update(FALSE);            /* Pretty up the screen              */
  1001. X    if (cmd_offset >= CMDBUFLEN-1)    /* If we're getting too big ...          */
  1002. X    {
  1003. X    mlwrite ("? command too long");    /* Complain loudly and bitterly          */
  1004. X    return (metac);            /* And force a quit              */
  1005. X    }
  1006. X    c = get1key();        /* Get the next character          */
  1007. X    cmd_buff[cmd_offset++] = c; /* Save the char for next time        */
  1008. X    cmd_buff[cmd_offset] = '\0';/* And terminate the buffer          */
  1009. X    return (c);                /* Return the character              */
  1010. X}
  1011. X
  1012. X/*
  1013. X * Hacky routine to re-eat a character.  This will save the character to be
  1014. X * re-eaten by redirecting the input call to a routine here.  Hack, etc.
  1015. X */
  1016. X
  1017. X/* Come here on the next term.t_getchar call: */
  1018. X
  1019. Xint uneat()
  1020. X{
  1021. X    int c;
  1022. X
  1023. X    term.t_getchar = saved_get_char;    /* restore the routine address          */
  1024. X    c = eaten_char;            /* Get the re-eaten char          */
  1025. X    eaten_char = -1;            /* Clear the old char              */
  1026. X    return(c);                /* and return the last char          */
  1027. X}
  1028. X
  1029. Xint reeat(c)
  1030. Xint    c;
  1031. X{
  1032. X    if (eaten_char != -1)        /* If we've already been here          */
  1033. X    return/*(NULL)*/;        /* Don't do it again              */
  1034. X    eaten_char = c;            /* Else, save the char for later      */
  1035. X    saved_get_char = term.t_getchar;    /* Save the char get routine          */
  1036. X    term.t_getchar = uneat;        /* Replace it with ours              */
  1037. X}
  1038. X#else
  1039. Xisearch()
  1040. X{
  1041. X}
  1042. X#endif
  1043. FRIDAY_NIGHT
  1044. echo mes.7 completed!
  1045. # That's all folks!
  1046.  
  1047.  
  1048.