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

  1. Article 87 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 10 of 16)
  6. Message-ID: <5686@ncoast.UUCP>
  7. Date: 17 Nov 87 02:33:24 GMT
  8. Sender: allbery@ncoast.UUCP
  9. Lines: 1785
  10. Approved: allbery@ncoast.UUCP
  11. X-Archive: comp.sources.misc/microemacs-3.9/9
  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.10')
  17. # If you do not see the message
  18. #    `mes.10 completed!'
  19. # then the file was incomplete.
  20. echo extracting - region.c
  21. sed 's/^X//' > region.c << 'FRIDAY_NIGHT'
  22. X/*
  23. X * The routines in this file
  24. X * deal with the region, that magic space
  25. X * between "." and mark. Some functions are
  26. X * commands. Some functions are just for
  27. X * internal use.
  28. X */
  29. X#include        <stdio.h>
  30. X#include    "estruct.h"
  31. X#include        "edef.h"
  32. X
  33. X#if    MEGAMAX & ST520
  34. Xoverlay    "region"
  35. X#endif
  36. X
  37. X/*
  38. X * Kill the region. Ask "getregion"
  39. X * to figure out the bounds of the region.
  40. X * Move "." to the start, and kill the characters.
  41. X * Bound to "C-W".
  42. X */
  43. Xkillregion(f, n)
  44. X{
  45. X        register int    s;
  46. X        REGION          region;
  47. X
  48. X    if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  49. X        return(rdonly());    /* we are in read only mode    */
  50. X        if ((s=getregion(®ion)) != TRUE)
  51. X                return (s);
  52. X        if ((lastflag&CFKILL) == 0)             /* This is a kill type  */
  53. X                kdelete();                      /* command, so do magic */
  54. X        thisflag |= CFKILL;                     /* kill buffer stuff.   */
  55. X        curwp->w_dotp = region.r_linep;
  56. X        curwp->w_doto = region.r_offset;
  57. X        return (ldelete(region.r_size, TRUE));
  58. X}
  59. X
  60. X/*
  61. X * Copy all of the characters in the
  62. X * region to the kill buffer. Don't move dot
  63. X * at all. This is a bit like a kill region followed
  64. X * by a yank. Bound to "M-W".
  65. X */
  66. Xcopyregion(f, n)
  67. X{
  68. X        register LINE   *linep;
  69. X        register int    loffs;
  70. X        register int    s;
  71. X        REGION          region;
  72. X
  73. X        if ((s=getregion(®ion)) != TRUE)
  74. X                return (s);
  75. X        if ((lastflag&CFKILL) == 0)             /* Kill type command.   */
  76. X                kdelete();
  77. X        thisflag |= CFKILL;
  78. X        linep = region.r_linep;                 /* Current line.        */
  79. X        loffs = region.r_offset;                /* Current offset.      */
  80. X        while (region.r_size--) {
  81. X                if (loffs == llength(linep)) {  /* End of line.         */
  82. X                        if ((s=kinsert('\n')) != TRUE)
  83. X                                return (s);
  84. X                        linep = lforw(linep);
  85. X                        loffs = 0;
  86. X                } else {                        /* Middle of line.      */
  87. X                        if ((s=kinsert(lgetc(linep, loffs))) != TRUE)
  88. X                                return (s);
  89. X                        ++loffs;
  90. X                }
  91. X        }
  92. X    mlwrite("[region copied]");
  93. X        return (TRUE);
  94. X}
  95. X
  96. X/*
  97. X * Lower case region. Zap all of the upper
  98. X * case characters in the region to lower case. Use
  99. X * the region code to set the limits. Scan the buffer,
  100. X * doing the changes. Call "lchange" to ensure that
  101. X * redisplay is done in all buffers. Bound to
  102. X * "C-X C-L".
  103. X */
  104. Xlowerregion(f, n)
  105. X{
  106. X        register LINE   *linep;
  107. X        register int    loffs;
  108. X        register int    c;
  109. X        register int    s;
  110. X        REGION          region;
  111. X
  112. X    if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  113. X        return(rdonly());    /* we are in read only mode    */
  114. X        if ((s=getregion(®ion)) != TRUE)
  115. X                return (s);
  116. X        lchange(WFHARD);
  117. X        linep = region.r_linep;
  118. X        loffs = region.r_offset;
  119. X        while (region.r_size--) {
  120. X                if (loffs == llength(linep)) {
  121. X                        linep = lforw(linep);
  122. X                        loffs = 0;
  123. X                } else {
  124. X                        c = lgetc(linep, loffs);
  125. X                        if (c>='A' && c<='Z')
  126. X                                lputc(linep, loffs, c+'a'-'A');
  127. X                        ++loffs;
  128. X                }
  129. X        }
  130. X        return (TRUE);
  131. X}
  132. X
  133. X/*
  134. X * Upper case region. Zap all of the lower
  135. X * case characters in the region to upper case. Use
  136. X * the region code to set the limits. Scan the buffer,
  137. X * doing the changes. Call "lchange" to ensure that
  138. X * redisplay is done in all buffers. Bound to
  139. X * "C-X C-L".
  140. X */
  141. Xupperregion(f, n)
  142. X{
  143. X        register LINE   *linep;
  144. X        register int    loffs;
  145. X        register int    c;
  146. X        register int    s;
  147. X        REGION          region;
  148. X
  149. X    if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  150. X        return(rdonly());    /* we are in read only mode    */
  151. X        if ((s=getregion(®ion)) != TRUE)
  152. X                return (s);
  153. X        lchange(WFHARD);
  154. X        linep = region.r_linep;
  155. X        loffs = region.r_offset;
  156. X        while (region.r_size--) {
  157. X                if (loffs == llength(linep)) {
  158. X                        linep = lforw(linep);
  159. X                        loffs = 0;
  160. X                } else {
  161. X                        c = lgetc(linep, loffs);
  162. X                        if (c>='a' && c<='z')
  163. X                                lputc(linep, loffs, c-'a'+'A');
  164. X                        ++loffs;
  165. X                }
  166. X        }
  167. X        return (TRUE);
  168. X}
  169. X
  170. X/*
  171. X * This routine figures out the
  172. X * bounds of the region in the current window, and
  173. X * fills in the fields of the "REGION" structure pointed
  174. X * to by "rp". Because the dot and mark are usually very
  175. X * close together, we scan outward from dot looking for
  176. X * mark. This should save time. Return a standard code.
  177. X * Callers of this routine should be prepared to get
  178. X * an "ABORT" status; we might make this have the
  179. X * conform thing later.
  180. X */
  181. Xgetregion(rp)
  182. Xregister REGION *rp;
  183. X{
  184. X        register LINE   *flp;
  185. X        register LINE   *blp;
  186. X        long fsize;
  187. X        long bsize;
  188. X
  189. X        if (curwp->w_markp == NULL) {
  190. X                mlwrite("No mark set in this window");
  191. X                return (FALSE);
  192. X        }
  193. X        if (curwp->w_dotp == curwp->w_markp) {
  194. X                rp->r_linep = curwp->w_dotp;
  195. X                if (curwp->w_doto < curwp->w_marko) {
  196. X                        rp->r_offset = curwp->w_doto;
  197. X                        rp->r_size = (long)(curwp->w_marko-curwp->w_doto);
  198. X                } else {
  199. X                        rp->r_offset = curwp->w_marko;
  200. X                        rp->r_size = (long)(curwp->w_doto-curwp->w_marko);
  201. X                }
  202. X                return (TRUE);
  203. X        }
  204. X        blp = curwp->w_dotp;
  205. X        bsize = (long)curwp->w_doto;
  206. X        flp = curwp->w_dotp;
  207. X        fsize = (long)(llength(flp)-curwp->w_doto+1);
  208. X        while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
  209. X                if (flp != curbp->b_linep) {
  210. X                        flp = lforw(flp);
  211. X                        if (flp == curwp->w_markp) {
  212. X                                rp->r_linep = curwp->w_dotp;
  213. X                                rp->r_offset = curwp->w_doto;
  214. X                                rp->r_size = fsize+curwp->w_marko;
  215. X                                return (TRUE);
  216. X                        }
  217. X                        fsize += llength(flp)+1;
  218. X                }
  219. X                if (lback(blp) != curbp->b_linep) {
  220. X                        blp = lback(blp);
  221. X                        bsize += llength(blp)+1;
  222. X                        if (blp == curwp->w_markp) {
  223. X                                rp->r_linep = blp;
  224. X                                rp->r_offset = curwp->w_marko;
  225. X                                rp->r_size = bsize - curwp->w_marko;
  226. X                                return (TRUE);
  227. X                        }
  228. X                }
  229. X        }
  230. X        mlwrite("Bug: lost mark");
  231. X        return (FALSE);
  232. X}
  233. X
  234. FRIDAY_NIGHT
  235. echo extracting - search.c
  236. sed 's/^X//' > search.c << 'FRIDAY_NIGHT'
  237. X/*
  238. X * The functions in this file implement commands that search in the forward
  239. X * and backward directions.  There are no special characters in the search
  240. X * strings.  Probably should have a regular expression search, or something
  241. X * like that.
  242. X *
  243. X * Aug. 1986 John M. Gamble:
  244. X *    Made forward and reverse search use the same scan routine.
  245. X *
  246. X *    Added a limited number of regular expressions - 'any',
  247. X *    'character class', 'closure', 'beginning of line', and
  248. X *    'end of line'.
  249. X *
  250. X *    Replacement metacharacters will have to wait for a re-write of
  251. X *    the replaces function, and a new variation of ldelete().
  252. X *
  253. X *    For those curious as to my references, i made use of
  254. X *    Kernighan & Plauger's "Software Tools."
  255. X *    I deliberately did not look at any published grep or editor
  256. X *    source (aside from this one) for inspiration.  I did make use of
  257. X *    Allen Hollub's bitmap routines as published in Doctor Dobb's Journal,
  258. X *    June, 1985 and modified them for the limited needs of character class
  259. X *    matching.  Any inefficiences, bugs, stupid coding examples, etc.,
  260. X *    are therefore my own responsibility.
  261. X *
  262. X * April 1987: John M. Gamble
  263. X *    Deleted the "if (n == 0) n = 1;" statements in front of the
  264. X *    search/hunt routines.  Since we now use a do loop, these
  265. X *    checks are unnecessary.  Consolidated common code into the
  266. X *    function delins().  Renamed global mclen matchlen,
  267. X *    and added the globals matchline, matchoff, patmatch, and
  268. X *    mlenold.
  269. X *    This gave us the ability to unreplace regular expression searches,
  270. X *    and to put the matched string into an evironment variable.
  271. X *    SOON TO COME: Meta-replacement characters!
  272. X *
  273. X *    25-apr-87    DML
  274. X *    - cleaned up an unneccessary if/else in forwsearch() and
  275. X *      backsearch()
  276. X *    - savematch() failed to malloc room for the terminating byte
  277. X *      of the match string (stomp...stomp...). It does now. Also
  278. X *      it now returns gracefully if malloc fails
  279. X *
  280. X *    July 1987: John M. Gamble
  281. X *    Set the variables matchlen and matchoff in the 'unreplace'
  282. X *    section of replaces().  The function savematch() would
  283. X *    get confused if you replaced, unreplaced, then replaced
  284. X *    again (serves you right for being so wishy-washy...)
  285. X *
  286. X *    August 1987: John M. Gamble
  287. X *    Put in new function rmcstr() to create the replacement
  288. X *    meta-character array.  Modified delins() so that it knows
  289. X *    whether or not to make use of the array.  And, put in the
  290. X *    appropriate new structures and variables.
  291. X */
  292. X
  293. X#include        <stdio.h>
  294. X#include    "estruct.h"
  295. X#include        "edef.h"
  296. X
  297. X#if    LATTICE
  298. X#define    void    int
  299. X#endif
  300. X
  301. Xstatic int    readpattern();
  302. Xstatic int    replaces();
  303. Xstatic int    nextch();
  304. X#if    MAGIC
  305. Xstatic int    cclmake();
  306. Xstatic int    mcstr();
  307. Xstatic int    rmcstr();
  308. Xstatic int    mceq();
  309. Xstatic void   setbit();
  310. Xstatic int    amatch();
  311. Xstatic int    biteq();
  312. Xstatic BITMAP clearbits();
  313. X#endif
  314. X
  315. X/*
  316. X * forwsearch -- Search forward.  Get a search string from the user, and
  317. X *    search for the string.  If found, reset the "." to be just after
  318. X *    the match string, and (perhaps) repaint the display.
  319. X */
  320. Xforwsearch(f, n)
  321. Xint f, n;    /* default flag / numeric argument */
  322. X{
  323. X    register int status = TRUE;
  324. X
  325. X    /* If n is negative, search backwards.
  326. X     * Otherwise proceed by asking for the search string.
  327. X     */
  328. X    if (n < 0)
  329. X        return(backsearch(f, -n));
  330. X
  331. X    /* Ask the user for the text of a pattern.  If the
  332. X     * response is TRUE (responses other than FALSE are
  333. X     * possible), search for the pattern for as long as
  334. X     * n is positive (n == 0 will go through once, which
  335. X     * is just fine).
  336. X     */
  337. X    if ((status = readpattern("Search", &pat[0], TRUE)) == TRUE) {
  338. X        do {
  339. X#if    MAGIC
  340. X            if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  341. X                status = mcscanner(&mcpat[0], FORWARD, PTEND);
  342. X            else
  343. X#endif
  344. X                status = scanner(&pat[0], FORWARD, PTEND);
  345. X        } while ((--n > 0) && status);
  346. X
  347. X        /* Save away the match, or complain
  348. X         * if not there.
  349. X         */
  350. X        if (status == TRUE)
  351. X            savematch();
  352. X        else
  353. X            mlwrite("Not found");
  354. X    }
  355. X    return(status);
  356. X}
  357. X
  358. X/*
  359. X * forwhunt -- Search forward for a previously acquired search string.
  360. X *    If found, reset the "." to be just after the match string,
  361. X *    and (perhaps) repaint the display.
  362. X */
  363. X
  364. Xforwhunt(f, n)
  365. Xint f, n;    /* default flag / numeric argument */
  366. X{
  367. X    register int status = TRUE;
  368. X
  369. X    if (n < 0)        /* search backwards */
  370. X        return(backhunt(f, -n));
  371. X
  372. X    /* Make sure a pattern exists, or that we didn't switch
  373. X     * into MAGIC mode until after we entered the pattern.
  374. X     */
  375. X    if (pat[0] == '\0')
  376. X    {
  377. X        mlwrite("No pattern set");
  378. X        return FALSE;
  379. X    }
  380. X#if    MAGIC
  381. X    if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  382. X         mcpat[0].mc_type == MCNIL)
  383. X    {
  384. X        if (!mcstr())
  385. X            return FALSE;
  386. X    }
  387. X#endif
  388. X
  389. X    /* Search for the pattern for as long as
  390. X     * n is positive (n == 0 will go through once, which
  391. X     * is just fine).
  392. X     */
  393. X    do
  394. X    {
  395. X#if    MAGIC
  396. X        if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  397. X            status = mcscanner(&mcpat[0], FORWARD, PTEND);
  398. X        else
  399. X#endif
  400. X            status = scanner(&pat[0], FORWARD, PTEND);
  401. X    } while ((--n > 0) && status);
  402. X
  403. X    /* Save away the match, or complain
  404. X     * if not there.
  405. X     */
  406. X    if (status == TRUE)
  407. X        savematch();
  408. X    else
  409. X        mlwrite("Not found");
  410. X
  411. X    return(status);
  412. X}
  413. X
  414. X/*
  415. X * backsearch -- Reverse search.  Get a search string from the user, and
  416. X *    search, starting at "." and proceeding toward the front of the buffer.
  417. X *    If found "." is left pointing at the first character of the pattern
  418. X *    (the last character that was matched).
  419. X */
  420. Xbacksearch(f, n)
  421. Xint f, n;    /* default flag / numeric argument */
  422. X{
  423. X    register int status = TRUE;
  424. X
  425. X    /* If n is negative, search forwards.
  426. X     * Otherwise proceed by asking for the search string.
  427. X     */
  428. X    if (n < 0)
  429. X        return(forwsearch(f, -n));
  430. X
  431. X    /* Ask the user for the text of a pattern.  If the
  432. X     * response is TRUE (responses other than FALSE are
  433. X     * possible), search for the pattern for as long as
  434. X     * n is positive (n == 0 will go through once, which
  435. X     * is just fine).
  436. X     */
  437. X    if ((status = readpattern("Reverse search", &pat[0], TRUE)) == TRUE) {
  438. X        do {
  439. X#if    MAGIC
  440. X            if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  441. X                status = mcscanner(&tapcm[0], REVERSE, PTBEG);
  442. X            else
  443. X#endif
  444. X                status = scanner(&tap[0], REVERSE, PTBEG);
  445. X        } while ((--n > 0) && status);
  446. X
  447. X        /* Save away the match, or complain
  448. X         * if not there.
  449. X         */
  450. X        if (status == TRUE)
  451. X            savematch();
  452. X        else
  453. X            mlwrite("Not found");
  454. X    }
  455. X    return(status);
  456. X}
  457. X
  458. X/*
  459. X * backhunt -- Reverse search for a previously acquired search string,
  460. X *    starting at "." and proceeding toward the front of the buffer.
  461. X *    If found "." is left pointing at the first character of the pattern
  462. X *    (the last character that was matched).
  463. X */
  464. Xbackhunt(f, n)
  465. Xint f, n;    /* default flag / numeric argument */
  466. X{
  467. X    register int    status = TRUE;
  468. X
  469. X    if (n < 0)
  470. X        return(forwhunt(f, -n));
  471. X
  472. X    /* Make sure a pattern exists, or that we didn't switch
  473. X     * into MAGIC mode until after we entered the pattern.
  474. X     */
  475. X    if (tap[0] == '\0')
  476. X    {
  477. X        mlwrite("No pattern set");
  478. X        return FALSE;
  479. X    }
  480. X#if    MAGIC
  481. X    if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  482. X         tapcm[0].mc_type == MCNIL)
  483. X    {
  484. X        if (!mcstr())
  485. X            return FALSE;
  486. X    }
  487. X#endif
  488. X
  489. X    /* Go search for it for as long as
  490. X     * n is positive (n == 0 will go through once, which
  491. X     * is just fine).
  492. X     */
  493. X    do
  494. X    {
  495. X#if    MAGIC
  496. X        if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  497. X            status = mcscanner(&tapcm[0], REVERSE, PTBEG);
  498. X        else
  499. X#endif
  500. X            status = scanner(&tap[0], REVERSE, PTBEG);
  501. X    } while ((--n > 0) && status);
  502. X
  503. X    /* Save away the match, or complain
  504. X     * if not there.
  505. X     */
  506. X    if (status == TRUE)
  507. X        savematch();
  508. X    else
  509. X        mlwrite("Not found");
  510. X
  511. X    return(status);
  512. X}
  513. X
  514. X#if    MAGIC
  515. X/*
  516. X * mcscanner -- Search for a meta-pattern in either direction.  If found,
  517. X *    reset the "." to be at the start or just after the match string,
  518. X *    and (perhaps) repaint the display.
  519. X */
  520. Xint    mcscanner(mcpatrn, direct, beg_or_end)
  521. XMC    *mcpatrn;    /* pointer into pattern */
  522. Xint    direct;        /* which way to go.*/
  523. Xint    beg_or_end;    /* put point at beginning or end of pattern.*/
  524. X{
  525. X    LINE *curline;            /* current line during scan */
  526. X    int curoff;            /* position within current line */
  527. X
  528. X    /* If we are going in reverse, then the 'end' is actually
  529. X     * the beginning of the pattern.  Toggle it.
  530. X     */
  531. X    beg_or_end ^= direct;
  532. X
  533. X    /*
  534. X     * Save the old matchlen length, in case it is
  535. X     * very different (closure) from the old length.
  536. X     * This is important for query-replace undo
  537. X     * command.
  538. X     */
  539. X    mlenold = matchlen;
  540. X
  541. X    /* Setup local scan pointers to global ".".
  542. X     */
  543. X    curline = curwp->w_dotp;
  544. X    curoff  = curwp->w_doto;
  545. X
  546. X    /* Scan each character until we hit the head link record.
  547. X     */
  548. X    while (!boundry(curline, curoff, direct))
  549. X    {
  550. X        /* Save the current position in case we need to
  551. X         * restore it on a match, and initialize matchlen to
  552. X         * zero in case we are doing a search for replacement.
  553. X         */
  554. X        matchline = curline;
  555. X        matchoff = curoff;
  556. X        matchlen = 0;
  557. X
  558. X        if (amatch(mcpatrn, direct, &curline, &curoff))
  559. X        {
  560. X            /* A SUCCESSFULL MATCH!!!
  561. X             * reset the global "." pointers.
  562. X             */
  563. X            if (beg_or_end == PTEND)    /* at end of string */
  564. X            {
  565. X                curwp->w_dotp = curline;
  566. X                curwp->w_doto = curoff;
  567. X            }
  568. X            else        /* at beginning of string */
  569. X            {
  570. X                curwp->w_dotp = matchline;
  571. X                curwp->w_doto = matchoff;
  572. X            }
  573. X
  574. X            curwp->w_flag |= WFMOVE; /* flag that we have moved */
  575. X            return TRUE;
  576. X        }
  577. X
  578. X        /* Advance the cursor.
  579. X         */
  580. X        nextch(&curline, &curoff, direct);
  581. X    }
  582. X
  583. X    return FALSE;    /* We could not find a match.*/
  584. X}
  585. X
  586. X/*
  587. X * amatch -- Search for a meta-pattern in either direction.  Based on the
  588. X *    recursive routine amatch() (for "anchored match") in
  589. X *    Kernighan & Plauger's "Software Tools".
  590. X */
  591. Xstatic int    amatch(mcptr, direct, pcwline, pcwoff)
  592. Xregister MC    *mcptr;    /* string to scan for */
  593. Xint        direct;        /* which way to go.*/
  594. XLINE        **pcwline;    /* current line during scan */
  595. Xint        *pcwoff;    /* position within current line */
  596. X{
  597. X    register int c;            /* character at current position */
  598. X    LINE *curline;            /* current line during scan */
  599. X    int curoff;            /* position within current line */
  600. X    int nchars;
  601. X
  602. X    /* Set up local scan pointers to ".", and get
  603. X     * the current character.  Then loop around
  604. X     * the pattern pointer until success or failure.
  605. X     */
  606. X    curline = *pcwline;
  607. X    curoff = *pcwoff;
  608. X
  609. X    /* The beginning-of-line and end-of-line metacharacters
  610. X     * do not compare against characters, they compare
  611. X     * against positions.
  612. X     * BOL is guaranteed to be at the start of the pattern
  613. X     * for forward searches, and at the end of the pattern
  614. X     * for reverse searches.  The reverse is true for EOL.
  615. X     * So, for a start, we check for them on entry.
  616. X     */
  617. X    if (mcptr->mc_type == BOL)
  618. X    {
  619. X        if (curoff != 0)
  620. X            return FALSE;
  621. X        mcptr++;
  622. X    }
  623. X
  624. X    if (mcptr->mc_type == EOL)
  625. X    {
  626. X        if (curoff != llength(curline))
  627. X            return FALSE;
  628. X        mcptr++;
  629. X    }
  630. X
  631. X    while (mcptr->mc_type != MCNIL)
  632. X    {
  633. X        c = nextch(&curline, &curoff, direct);
  634. X
  635. X        if (mcptr->mc_type & CLOSURE)
  636. X        {
  637. X            /* Try to match as many characters as possible
  638. X             * against the current meta-character.  A
  639. X             * newline never matches a closure.
  640. X             */
  641. X            nchars = 0;
  642. X            while (c != '\n' && mceq(c, mcptr))
  643. X            {
  644. X                c = nextch(&curline, &curoff, direct);
  645. X                nchars++;
  646. X            }
  647. X
  648. X            /* We are now at the character that made us
  649. X             * fail.  Try to match the rest of the pattern.
  650. X             * Shrink the closure by one for each failure.
  651. X             * Since closure matches *zero* or more occurences
  652. X             * of a pattern, a match may start even if the
  653. X             * previous loop matched no characters.
  654. X             */
  655. X            mcptr++;
  656. X
  657. X            for (;;)
  658. X            {
  659. X                c = nextch(&curline, &curoff, direct ^ REVERSE);
  660. X
  661. X                if (amatch(mcptr, direct, &curline, &curoff))
  662. X                {
  663. X                    matchlen += nchars;
  664. X                    goto success;
  665. X                }
  666. X
  667. X                if (nchars-- == 0)
  668. X                    return FALSE;
  669. X            }
  670. X        }
  671. X        else            /* Not closure.*/
  672. X        {
  673. X            /* The only way we'd get a BOL metacharacter
  674. X             * at this point is at the end of the reversed pattern.
  675. X             * The only way we'd get an EOL metacharacter
  676. X             * here is at the end of a regular pattern.
  677. X             * So if we match one or the other, and are at
  678. X             * the appropriate position, we are guaranteed success
  679. X             * (since the next pattern character has to be MCNIL).
  680. X             * Before we report success, however, we back up by
  681. X             * one character, so as to leave the cursor in the
  682. X             * correct position.  For example, a search for ")$"
  683. X             * will leave the cursor at the end of the line, while
  684. X             * a search for ")<NL>" will leave the cursor at the
  685. X             * beginning of the next line.  This follows the
  686. X             * notion that the meta-character '$' (and likewise
  687. X             * '^') match positions, not characters.
  688. X             */
  689. X            if (mcptr->mc_type == BOL)
  690. X                if (curoff == llength(curline))
  691. X                {
  692. X                    c = nextch(&curline, &curoff,
  693. X                           direct ^ REVERSE);
  694. X                    goto success;
  695. X                }
  696. X                else
  697. X                    return FALSE;
  698. X
  699. X            if (mcptr->mc_type == EOL)
  700. X                if (curoff == 0)
  701. X                {
  702. X                    c = nextch(&curline, &curoff,
  703. X                           direct ^ REVERSE);
  704. X                    goto success;
  705. X                }
  706. X                else
  707. X                    return FALSE;
  708. X
  709. X            /* Neither BOL nor EOL, so go through
  710. X             * the meta-character equal function.
  711. X             */
  712. X            if (!mceq(c, mcptr))
  713. X                return FALSE;
  714. X        }
  715. X
  716. X        /* Increment the length counter and
  717. X         * advance the pattern pointer.
  718. X         */
  719. X        matchlen++;
  720. X        mcptr++;
  721. X    }            /* End of mcptr loop.*/
  722. X
  723. X    /* A SUCCESSFULL MATCH!!!
  724. X     * Reset the "." pointers.
  725. X     */
  726. Xsuccess:
  727. X    *pcwline = curline;
  728. X    *pcwoff  = curoff;
  729. X
  730. X    return TRUE;
  731. X}
  732. X#endif
  733. X
  734. X/*
  735. X * scanner -- Search for a pattern in either direction.  If found,
  736. X *    reset the "." to be at the start or just after the match string,
  737. X *    and (perhaps) repaint the display.
  738. X */
  739. Xint    scanner(patrn, direct, beg_or_end)
  740. Xunsigned char *patrn;    /* string to scan for */
  741. Xint    direct;        /* which way to go.*/
  742. Xint    beg_or_end;    /* put point at beginning or end of pattern.*/
  743. X{
  744. X    register int    c;        /* character at current position */
  745. X    register unsigned char *patptr;    /* pointer into pattern */
  746. X    LINE    *curline;        /* current line during scan */
  747. X    int    curoff;            /* position within current line */
  748. X    LINE    *scanline;        /* current line during scanning */
  749. X    int    scanoff;        /* position in scanned line */
  750. X
  751. X    /* If we are going in reverse, then the 'end' is actually
  752. X     * the beginning of the pattern.  Toggle it.
  753. X     */
  754. X    beg_or_end ^= direct;
  755. X
  756. X    /* Set up local pointers to global ".".
  757. X     */
  758. X    curline = curwp->w_dotp;
  759. X    curoff = curwp->w_doto;
  760. X
  761. X    /* Scan each character until we hit the head link record.
  762. X     */
  763. X    while (!boundry(curline, curoff, direct))
  764. X    {
  765. X        /* Save the current position in case we match
  766. X         * the search string at this point.
  767. X         */
  768. X        matchline = curline;
  769. X        matchoff = curoff;
  770. X
  771. X        /* Get the character resolving newlines, and
  772. X         * test it against first char in pattern.
  773. X         */
  774. X        c = nextch(&curline, &curoff, direct);
  775. X
  776. X        if (eq(c, patrn[0]))    /* if we find it..*/
  777. X        {
  778. X            /* Setup scanning pointers.
  779. X             */
  780. X            scanline = curline;
  781. X            scanoff = curoff;
  782. X            patptr = &patrn[0];
  783. X
  784. X            /* Scan through the pattern for a match.
  785. X             */
  786. X            while (*++patptr != '\0')
  787. X            {
  788. X                c = nextch(&scanline, &scanoff, direct);
  789. X
  790. X                if (!eq(c, *patptr))
  791. X                    goto fail;
  792. X            }
  793. X
  794. X            /* A SUCCESSFULL MATCH!!!
  795. X             * reset the global "." pointers
  796. X             */
  797. X            if (beg_or_end == PTEND)    /* at end of string */
  798. X            {
  799. X                curwp->w_dotp = scanline;
  800. X                curwp->w_doto = scanoff;
  801. X            }
  802. X            else        /* at beginning of string */
  803. X            {
  804. X                curwp->w_dotp = matchline;
  805. X                curwp->w_doto = matchoff;
  806. X            }
  807. X
  808. X            curwp->w_flag |= WFMOVE; /* Flag that we have moved.*/
  809. X            return TRUE;
  810. X
  811. X        }
  812. Xfail:;            /* continue to search */
  813. X    }
  814. X
  815. X    return FALSE;    /* We could not find a match */
  816. X}
  817. X
  818. X/*
  819. X * eq -- Compare two characters.  The "bc" comes from the buffer, "pc"
  820. X *    from the pattern.  If we are not in EXACT mode, fold out the case.
  821. X */
  822. Xint    eq(bc, pc)
  823. Xregister int    bc;
  824. Xregister int    pc;
  825. X{
  826. X    if ((curwp->w_bufp->b_mode & MDEXACT) == 0)
  827. X    {
  828. X        if (islower(bc))
  829. X            bc ^= DIFCASE;
  830. X
  831. X        if (islower(pc))
  832. X            pc ^= DIFCASE;
  833. X    }
  834. X
  835. X    return (bc == pc);
  836. X}
  837. X
  838. X/*
  839. X * readpattern -- Read a pattern.  Stash it in apat.  If it is the
  840. X *    search string, create the reverse pattern and the magic
  841. X *    pattern, assuming we are in MAGIC mode (and defined that way).
  842. X *    Apat is not updated if the user types in an empty line.  If
  843. X *    the user typed an empty line, and there is no old pattern, it is
  844. X *    an error.  Display the old pattern, in the style of Jeff Lomicka.
  845. X *    There is some do-it-yourself control expansion.  Change to using
  846. X *    <META> to delimit the end-of-pattern to allow <NL>s in the search
  847. X *    string. 
  848. X */
  849. Xstatic int    readpattern(prompt, apat, srch)
  850. Xchar    *prompt;
  851. Xchar    apat[];
  852. Xint    srch;
  853. X{
  854. X    int status;
  855. X    char tpat[NPAT+20];
  856. X
  857. X    strcpy(tpat, prompt);    /* copy prompt to output string */
  858. X    strcat(tpat, " [");    /* build new prompt string */
  859. X    expandp(&apat[0], &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  860. X    strcat(tpat, "]<META>: ");
  861. X
  862. X    /* Read a pattern.  Either we get one,
  863. X     * or we just get the META charater, and use the previous pattern.
  864. X     * Then, if it's the search string, make a reversed pattern.
  865. X     * *Then*, make the meta-pattern, if we are defined that way.
  866. X     */
  867. X    if ((status = mlreplyt(tpat, tpat, NPAT, metac)) == TRUE)
  868. X    {
  869. X        strcpy(apat, tpat);
  870. X        if (srch)    /* If we are doing the search string.*/
  871. X        {
  872. X            /* Reverse string copy, and remember
  873. X             * the length for substitution purposes.
  874. X             */
  875. X            rvstrcpy(tap, apat);
  876. X            mlenold = matchlen = strlen(apat);
  877. X        }
  878. X#if    MAGIC
  879. X        /* Only make the meta-pattern if in magic mode,
  880. X         * since the pattern in question might have an
  881. X         * invalid meta combination.
  882. X         */
  883. X        if ((curwp->w_bufp->b_mode & MDMAGIC) == 0)
  884. X        {
  885. X            mcclear();
  886. X            rmcclear();
  887. X        }
  888. X        else
  889. X            status = srch? mcstr(): rmcstr();
  890. X#endif
  891. X    }
  892. X    else if (status == FALSE && apat[0] != 0)    /* Old one */
  893. X        status = TRUE;
  894. X
  895. X    return(status);
  896. X}
  897. X
  898. X/*
  899. X * savematch -- We found the pattern?  Let's save it away.
  900. X */
  901. Xsavematch()
  902. X{
  903. X    register char    *ptr;        /* pointer to last match string */
  904. X    register int    j;
  905. X    LINE        *curline;    /* line of last match */
  906. X    int        curoff;        /* offset "      "    */
  907. X
  908. X    /* Free any existing match string, then
  909. X     * attempt to allocate a new one.
  910. X     */
  911. X    if (patmatch != NULL)
  912. X        free(patmatch);
  913. X
  914. X    ptr = patmatch = malloc(matchlen + 1);
  915. X
  916. X    if (ptr != NULL)
  917. X    {
  918. X        curoff = matchoff;
  919. X        curline = matchline;
  920. X
  921. X        for (j = 0; j < matchlen; j++)
  922. X            *ptr++ = nextch(&curline, &curoff, FORWARD);
  923. X
  924. X        *ptr = '\0';
  925. X    }
  926. X}
  927. X
  928. X/*
  929. X * rvstrcpy -- Reverse string copy.
  930. X */
  931. Xrvstrcpy(rvstr, str)
  932. Xregister char    *rvstr, *str;
  933. X{
  934. X    register int i;
  935. X
  936. X    str += (i = strlen(str));
  937. X
  938. X    while (i-- > 0)
  939. X        *rvstr++ = *--str;
  940. X
  941. X    *rvstr = '\0';
  942. X}
  943. X
  944. X/*
  945. X * sreplace -- Search and replace.
  946. X */
  947. Xsreplace(f, n)
  948. Xint f;        /* default flag */
  949. Xint n;        /* # of repetitions wanted */
  950. X{
  951. X    return(replaces(FALSE, f, n));
  952. X}
  953. X
  954. X/*
  955. X * qreplace -- search and replace with query.
  956. X */
  957. Xqreplace(f, n)
  958. Xint f;        /* default flag */
  959. Xint n;        /* # of repetitions wanted */
  960. X{
  961. X    return(replaces(TRUE, f, n));
  962. X}
  963. X
  964. X/*
  965. X * replaces -- Search for a string and replace it with another
  966. X *    string.  Query might be enabled (according to kind).
  967. X */
  968. Xstatic int    replaces(kind, f, n)
  969. Xint    kind;    /* Query enabled flag */
  970. Xint    f;    /* default flag */
  971. Xint    n;    /* # of repetitions wanted */
  972. X{
  973. X    register int status;    /* success flag on pattern inputs */
  974. X    register int rlength;    /* length of replacement string */
  975. X    register int numsub;    /* number of substitutions */
  976. X    register int nummatch;    /* number of found matches */
  977. X    int nlflag;        /* last char of search string a <NL>? */
  978. X    int nlrepl;        /* was a replace done on the last line? */
  979. X    char c;            /* input char for query */
  980. X    char tpat[NPAT];    /* temporary to hold search pattern */
  981. X    LINE *origline;        /* original "." position */
  982. X    int origoff;        /* and offset (for . query option) */
  983. X    LINE *lastline;        /* position of last replace and */
  984. X    int lastoff;        /* offset (for 'u' query option) */
  985. X
  986. X    if (curbp->b_mode & MDVIEW)    /* don't allow this command if    */
  987. X        return(rdonly());    /* we are in read only mode    */
  988. X
  989. X    /* Check for negative repetitions.
  990. X     */
  991. X    if (f && n < 0)
  992. X        return(FALSE);
  993. X
  994. X    /* Ask the user for the text of a pattern.
  995. X     */
  996. X    if ((status = readpattern(
  997. X        (kind == FALSE ? "Replace" : "Query replace"), &pat[0], TRUE))
  998. X                                != TRUE)
  999. X        return(status);
  1000. X
  1001. X    /* Ask for the replacement string.
  1002. X     */
  1003. X    if ((status = readpattern("with", &rpat[0], FALSE)) == ABORT)
  1004. X        return(status);
  1005. X
  1006. X    /* Find the length of the replacement string.
  1007. X     */
  1008. X    rlength = strlen(&rpat[0]);
  1009. X
  1010. X    /* Set up flags so we can make sure not to do a recursive
  1011. X     * replace on the last line.
  1012. X     */
  1013. X    nlflag = (pat[matchlen - 1] == '\n');
  1014. X    nlrepl = FALSE;
  1015. X
  1016. X    if (kind)
  1017. X    {
  1018. X        /* Build query replace question string.
  1019. X         */
  1020. X        strcpy(tpat, "Replace '");
  1021. X        expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3);
  1022. X        strcat(tpat, "' with '");
  1023. X        expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3);
  1024. X        strcat(tpat, "'? ");
  1025. X
  1026. X        /* Initialize last replaced pointers.
  1027. X         */
  1028. X        lastline = NULL;
  1029. X        lastoff = 0;
  1030. X    }
  1031. X
  1032. X    /* Save original . position, init the number of matches and
  1033. X     * substitutions, and scan through the file.
  1034. X     */
  1035. X    origline = curwp->w_dotp;
  1036. X    origoff = curwp->w_doto;
  1037. X    numsub = 0;
  1038. X    nummatch = 0;
  1039. X
  1040. X    while ( (f == FALSE || n > nummatch) &&
  1041. X        (nlflag == FALSE || nlrepl == FALSE) )
  1042. X    {
  1043. X        /* Search for the pattern.
  1044. X         * If we search with a regular expression,
  1045. X         * matchlen is reset to the true length of
  1046. X         * the matched string.
  1047. X         */
  1048. X#if    MAGIC
  1049. X        if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  1050. X        {
  1051. X            if (!mcscanner(&mcpat[0], FORWARD, PTBEG))
  1052. X                break;
  1053. X        }
  1054. X        else
  1055. X#endif
  1056. X            if (!scanner(&pat[0], FORWARD, PTBEG))
  1057. X                break;        /* all done */
  1058. X
  1059. X        ++nummatch;    /* Increment # of matches */
  1060. X
  1061. X        /* Check if we are on the last line.
  1062. X         */
  1063. X        nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
  1064. X
  1065. X        /* Check for query.
  1066. X         */
  1067. X        if (kind)
  1068. X        {
  1069. X            /* Get the query.
  1070. X             */
  1071. Xpprompt:        mlwrite(&tpat[0], &pat[0], &rpat[0]);
  1072. Xqprompt:
  1073. X            update(TRUE);  /* show the proposed place to change */
  1074. X            c = tgetc();            /* and input */
  1075. X            mlwrite("");            /* and clear it */
  1076. X
  1077. X            /* And respond appropriately.
  1078. X             */
  1079. X            switch (c)
  1080. X            {
  1081. X                case 'y':    /* yes, substitute */
  1082. X                case ' ':
  1083. X                    savematch();
  1084. X                    break;
  1085. X
  1086. X                case 'n':    /* no, onword */
  1087. X                    forwchar(FALSE, 1);
  1088. X                    continue;
  1089. X
  1090. X                case '!':    /* yes/stop asking */
  1091. X                    kind = FALSE;
  1092. X                    break;
  1093. X
  1094. X                case 'u':    /* undo last and re-prompt */
  1095. X
  1096. X                    /* Restore old position.
  1097. X                     */
  1098. X                    if (lastline == NULL)
  1099. X                    {
  1100. X                        /* There is nothing to undo.
  1101. X                         */
  1102. X                        TTbeep();
  1103. X                        goto pprompt;
  1104. X                    }
  1105. X                    curwp->w_dotp = lastline;
  1106. X                    curwp->w_doto = lastoff;
  1107. X                    lastline = NULL;
  1108. X                    lastoff = 0;
  1109. X
  1110. X                    /* Delete the new string.
  1111. X                     */
  1112. X                    backchar(FALSE, rlength);
  1113. X                    status = delins(rlength, patmatch, FALSE);
  1114. X                    if (status != TRUE)
  1115. X                        return (status);
  1116. X
  1117. X                    /* Record one less substitution,
  1118. X                     * backup, save our place, and
  1119. X                     * reprompt.
  1120. X                     */
  1121. X                    --numsub;
  1122. X                    backchar(FALSE, mlenold);
  1123. X                    matchline = curwp->w_dotp;
  1124. X                    matchoff  = curwp->w_doto;
  1125. X                    goto pprompt;
  1126. X
  1127. X                case '.':    /* abort! and return */
  1128. X                    /* restore old position */
  1129. X                    curwp->w_dotp = origline;
  1130. X                    curwp->w_doto = origoff;
  1131. X                    curwp->w_flag |= WFMOVE;
  1132. X
  1133. X                case BELL:    /* abort! and stay */
  1134. X                    mlwrite("Aborted!");
  1135. X                    return(FALSE);
  1136. X
  1137. X                default:    /* bitch and beep */
  1138. X                    TTbeep();
  1139. X
  1140. X                case '?':    /* help me */
  1141. X                    mlwrite(
  1142. X"(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: ");
  1143. X                    goto qprompt;
  1144. X
  1145. X            }    /* end of switch */
  1146. X        }    /* end of "if kind" */
  1147. X
  1148. X        /*
  1149. X         * Delete the sucker, and insert its
  1150. X         * replacement.
  1151. X         */
  1152. X        status = delins(matchlen, &rpat[0], TRUE);
  1153. X        if (status != TRUE)
  1154. X            return (status);
  1155. X
  1156. X        /* Save our position, since we may
  1157. X         * undo this.
  1158. X         */
  1159. X        if (kind)
  1160. X        {
  1161. X            lastline = curwp->w_dotp;
  1162. X            lastoff = curwp->w_doto;
  1163. X        }
  1164. X
  1165. X        numsub++;    /* increment # of substitutions */
  1166. X    }
  1167. X
  1168. X    /* And report the results.
  1169. X     */
  1170. X    mlwrite("%d substitutions", numsub);
  1171. X    return(TRUE);
  1172. X}
  1173. X
  1174. X/*
  1175. X * delins -- Delete a specified length from the current point
  1176. X *    then either insert the string directly, or make use of
  1177. X *    replacement meta-array.
  1178. X */
  1179. Xdelins(dlength, instr, use_meta)
  1180. Xint    dlength;
  1181. Xchar    *instr;
  1182. Xint    use_meta;
  1183. X{
  1184. X    int    status;
  1185. X#if    MAGIC
  1186. X    RMC    *rmcptr;
  1187. X#endif
  1188. X
  1189. X    /* Zap what we gotta,
  1190. X     * and insert its replacement.
  1191. X     */
  1192. X    if ((status = ldelete((long) dlength, FALSE)) != TRUE)
  1193. X        mlwrite("%%ERROR while deleting");
  1194. X    else
  1195. X#if    MAGIC
  1196. X        if ((rmagical && use_meta) &&
  1197. X             (curwp->w_bufp->b_mode & MDMAGIC) != 0) {
  1198. X            rmcptr = &rmcpat[0];
  1199. X            while (rmcptr->mc_type != MCNIL && status == TRUE) {
  1200. X                if (rmcptr->mc_type == LITCHAR)
  1201. X                    status = linstr(rmcptr->rstr);
  1202. X                else
  1203. X                    status = linstr(patmatch);
  1204. X                rmcptr++;
  1205. X            }
  1206. X        } else
  1207. X#endif
  1208. X            status = linstr(instr);
  1209. X
  1210. X    return(status);
  1211. X}
  1212. X
  1213. X/*
  1214. X * expandp -- Expand control key sequences for output.
  1215. X */
  1216. Xexpandp(srcstr, deststr, maxlength)
  1217. Xchar    *srcstr;    /* string to expand */
  1218. Xchar    *deststr;    /* destination of expanded string */
  1219. Xint    maxlength;    /* maximum chars in destination */
  1220. X{
  1221. X    unsigned char c;    /* current char to translate */
  1222. X
  1223. X    /* Scan through the string.
  1224. X     */
  1225. X    while ((c = *srcstr++) != 0)
  1226. X    {
  1227. X        if (c == '\n')        /* it's a newline */
  1228. X        {
  1229. X            *deststr++ = '<';
  1230. X            *deststr++ = 'N';
  1231. X            *deststr++ = 'L';
  1232. X            *deststr++ = '>';
  1233. X            maxlength -= 4;
  1234. X        }
  1235. X        else if (c < 0x20 || c == 0x7f)    /* control character */
  1236. X        {
  1237. X            *deststr++ = '^';
  1238. X            *deststr++ = c ^ 0x40;
  1239. X            maxlength -= 2;
  1240. X        }
  1241. X        else if (c == '%')
  1242. X        {
  1243. X            *deststr++ = '%';
  1244. X            *deststr++ = '%';
  1245. X            maxlength -= 2;
  1246. X        }
  1247. X        else            /* any other character */
  1248. X        {
  1249. X            *deststr++ = c;
  1250. X            maxlength--;
  1251. X        }
  1252. X
  1253. X        /* check for maxlength */
  1254. X        if (maxlength < 4)
  1255. X        {
  1256. X            *deststr++ = '$';
  1257. X            *deststr = '\0';
  1258. X            return(FALSE);
  1259. X        }
  1260. X    }
  1261. X    *deststr = '\0';
  1262. X    return(TRUE);
  1263. X}
  1264. X
  1265. X/*
  1266. X * boundry -- Return information depending on whether we may search no
  1267. X *    further.  Beginning of file and end of file are the obvious
  1268. X *    cases, but we may want to add further optional boundry restrictions
  1269. X *    in future, a' la VMS EDT.  At the moment, just return TRUE or
  1270. X *    FALSE depending on if a boundry is hit (ouch).
  1271. X */
  1272. Xint    boundry(curline, curoff, dir)
  1273. XLINE    *curline;
  1274. Xint    curoff, dir;
  1275. X{
  1276. X    register int    border;
  1277. X
  1278. X    if (dir == FORWARD)
  1279. X    {
  1280. X        border = (curoff == llength(curline)) &&
  1281. X             (lforw(curline) == curbp->b_linep);
  1282. X    }
  1283. X    else
  1284. X    {
  1285. X        border = (curoff == 0) &&
  1286. X             (lback(curline) == curbp->b_linep);
  1287. X    }
  1288. X    return (border);
  1289. X}
  1290. X
  1291. X/*
  1292. X * nextch -- retrieve the next/previous character in the buffer,
  1293. X *    and advance/retreat the point.
  1294. X *    The order in which this is done is significant, and depends
  1295. X *    upon the direction of the search.  Forward searches look at
  1296. X *    the current character and move, reverse searches move and
  1297. X *    look at the character.
  1298. X */
  1299. Xstatic int nextch(pcurline, pcuroff, dir)
  1300. XLINE    **pcurline;
  1301. Xint    *pcuroff;
  1302. Xint    dir;
  1303. X{
  1304. X    register LINE    *curline;
  1305. X    register int    curoff;
  1306. X    register int    c;
  1307. X
  1308. X    curline = *pcurline;
  1309. X    curoff = *pcuroff;
  1310. X
  1311. X    if (dir == FORWARD)
  1312. X    {
  1313. X        if (curoff == llength(curline))        /* if at EOL */
  1314. X        {
  1315. X            curline = lforw(curline);    /* skip to next line */
  1316. X            curoff = 0;
  1317. X            c = '\n';            /* and return a <NL> */
  1318. X        }
  1319. X        else
  1320. X            c = lgetc(curline, curoff++);    /* get the char */
  1321. X    }
  1322. X    else            /* Reverse.*/
  1323. X    {
  1324. X        if (curoff == 0)
  1325. X        {
  1326. X            curline = lback(curline);
  1327. X            curoff = llength(curline);
  1328. X            c = '\n';
  1329. X        }
  1330. X        else
  1331. X            c = lgetc(curline, --curoff);
  1332. X
  1333. X    }
  1334. X    *pcurline = curline;
  1335. X    *pcuroff = curoff;
  1336. X
  1337. X    return (c);
  1338. X}
  1339. X
  1340. X#if    MAGIC
  1341. X/*
  1342. X * mcstr -- Set up the 'magic' array.  The closure symbol is taken as
  1343. X *    a literal character when (1) it is the first character in the
  1344. X *    pattern, and (2) when preceded by a symbol that does not allow
  1345. X *    closure, such as a newline, beginning of line symbol, or another
  1346. X *    closure symbol.
  1347. X *
  1348. X *    Coding comment (jmg):  yes, i know i have gotos that are, strictly
  1349. X *    speaking, unnecessary.  But right now we are so cramped for
  1350. X *    code space that i will grab what i can in order to remain
  1351. X *    within the 64K limit.  C compilers actually do very little
  1352. X *    in the way of optimizing - they expect you to do that.
  1353. X */
  1354. Xstatic int mcstr()
  1355. X{
  1356. X    MC    *mcptr, *rtpcm;
  1357. X    char    *patptr;
  1358. X     int    mj;
  1359. X     int    pchr;
  1360. X     int    status = TRUE;
  1361. X     int    does_closure = FALSE;
  1362. X
  1363. X    /* If we had metacharacters in the MC array previously,
  1364. X     * free up any bitmaps that may have been allocated.
  1365. X     */
  1366. X    if (magical)
  1367. X        mcclear();
  1368. X
  1369. X    magical = FALSE;
  1370. X    mj = 0;
  1371. X    mcptr = &mcpat[0];
  1372. X    patptr = &pat[0];
  1373. X
  1374. X    while ((pchr = *patptr) && status)
  1375. X    {
  1376. X        switch (pchr)
  1377. X        {
  1378. X            case MC_CCL:
  1379. X                status = cclmake(&patptr, mcptr);
  1380. X                magical = TRUE;
  1381. X                does_closure = TRUE;
  1382. X                break;
  1383. X            case MC_BOL:
  1384. X                if (mj != 0)
  1385. X                    goto litcase;
  1386. X
  1387. X                mcptr->mc_type = BOL;
  1388. X                magical = TRUE;
  1389. X                does_closure = FALSE;
  1390. X                break;
  1391. X            case MC_EOL:
  1392. X                if (*(patptr + 1) != '\0')
  1393. X                    goto litcase;
  1394. X
  1395. X                mcptr->mc_type = EOL;
  1396. X                magical = TRUE;
  1397. X                does_closure = FALSE;
  1398. X                break;
  1399. X            case MC_ANY:
  1400. X                mcptr->mc_type = ANY;
  1401. X                magical = TRUE;
  1402. X                does_closure = TRUE;
  1403. X                break;
  1404. X            case MC_CLOSURE:
  1405. X                /* Does the closure symbol mean closure here?
  1406. X                 * If so, back up to the previous element
  1407. X                 * and indicate it is enclosed.
  1408. X                 */
  1409. X                if (!does_closure)
  1410. X                    goto litcase;
  1411. X                mj--;
  1412. X                mcptr--;
  1413. X                mcptr->mc_type |= CLOSURE;
  1414. X                magical = TRUE;
  1415. X                does_closure = FALSE;
  1416. X                break;
  1417. X
  1418. X            /* Note: no break between MC_ESC case and the default.
  1419. X             */
  1420. X            case MC_ESC:
  1421. X                if (*(patptr + 1) != '\0')
  1422. X                {
  1423. X                    pchr = *++patptr;
  1424. X                    magical = TRUE;
  1425. X                }
  1426. X            default:
  1427. Xlitcase:            mcptr->mc_type = LITCHAR;
  1428. X                mcptr->u.lchar = pchr;
  1429. X                does_closure = (pchr != '\n');
  1430. X                break;
  1431. X        }        /* End of switch.*/
  1432. X        mcptr++;
  1433. X        patptr++;
  1434. X        mj++;
  1435. X    }        /* End of while.*/
  1436. X
  1437. X    /* Close off the meta-string.
  1438. X     */
  1439. X    mcptr->mc_type = MCNIL;
  1440. X
  1441. X    /* Set up the reverse array, if the status is good.  Please note the
  1442. X     * structure assignment - your compiler may not like that.
  1443. X     * If the status is not good, nil out the meta-pattern.
  1444. X     * The only way the status would be bad is from the cclmake()
  1445. X     * routine, and the bitmap for that member is guarenteed to be
  1446. X     * freed.  So we stomp a MCNIL value there, and call mcclear()
  1447. X     * to free any other bitmaps.
  1448. X     */
  1449. X    if (status)
  1450. X    {
  1451. X        rtpcm = &tapcm[0];
  1452. X        while (--mj >= 0)
  1453. X        {
  1454. X#if    LATTICE
  1455. X            movmem(--mcptr, rtpcm++, sizeof (MC));
  1456. X#endif
  1457. X
  1458. X#if    MWC86 | AZTEC | MSC | TURBO | VMS | USG | BSD | V7
  1459. X            *rtpcm++ = *--mcptr;
  1460. X#endif
  1461. X        }
  1462. X        rtpcm->mc_type = MCNIL;
  1463. X    }
  1464. X    else
  1465. X    {
  1466. X        (--mcptr)->mc_type = MCNIL;
  1467. X        mcclear();
  1468. X    }
  1469. X
  1470. X    return(status);
  1471. X}
  1472. X
  1473. X/*
  1474. X * rmcstr -- Set up the replacement 'magic' array.  Note that if there
  1475. X *    are no meta-characters encountered in the replacement string,
  1476. X *    the array is never actually created - we will just use the
  1477. X *    character array rpat[] as the replacement string.
  1478. X */
  1479. Xstatic int rmcstr()
  1480. X{
  1481. X    RMC    *rmcptr;
  1482. X    char    *patptr;
  1483. X    int    status = TRUE;
  1484. X    int    mj;
  1485. X
  1486. X    patptr = &rpat[0];
  1487. X    rmcptr = &rmcpat[0];
  1488. X    mj = 0;
  1489. X    rmagical = FALSE;
  1490. X
  1491. X    while (*patptr && status == TRUE)
  1492. X    {
  1493. X        switch (*patptr)
  1494. X        {
  1495. X            case MC_DITTO:
  1496. X
  1497. X                /* If there were non-magical characters
  1498. X                 * in the string before reaching this
  1499. X                 * character, plunk it in the replacement
  1500. X                 * array before processing the current
  1501. X                 * meta-character.
  1502. X                 */
  1503. X                if (mj != 0)
  1504. X                {
  1505. X                    rmcptr->mc_type = LITCHAR;
  1506. X                    if ((rmcptr->rstr = malloc(mj + 1)) == NULL)
  1507. X                    {
  1508. X                        mlwrite("%%Out of memory");
  1509. X                        status = FALSE;
  1510. X                        break;
  1511. X                    }
  1512. X                    strncpy(rmcptr->rstr, patptr - mj, mj);
  1513. X                    rmcptr++;
  1514. X                    mj = 0;
  1515. X                }
  1516. X                rmcptr->mc_type = DITTO;
  1517. X                rmcptr++;
  1518. X                rmagical = TRUE;
  1519. X                break;
  1520. X
  1521. X            case MC_ESC:
  1522. X                rmcptr->mc_type = LITCHAR;
  1523. X
  1524. X                /* We malloc mj plus two here, instead
  1525. X                 * of one, because we have to count the
  1526. X                 * current character.
  1527. X                 */
  1528. X                if ((rmcptr->rstr = malloc(mj + 2)) == NULL)
  1529. X                {
  1530. X                    mlwrite("%%Out of memory");
  1531. X                    status = FALSE;
  1532. X                    break;
  1533. X                }
  1534. X
  1535. X                strncpy(rmcptr->rstr, patptr - mj, mj + 1);
  1536. X
  1537. X                /* If MC_ESC is not the last character
  1538. X                 * in the string, find out what it is
  1539. X                 * escaping, and overwrite the last
  1540. X                 * character with it.
  1541. X                 */
  1542. X                if (*(patptr + 1) != '\0')
  1543. X                    *((rmcptr->rstr) + mj) = *++patptr;
  1544. X
  1545. X                rmcptr++;
  1546. X                mj = 0;
  1547. X                rmagical = TRUE;
  1548. X                break;
  1549. X
  1550. X            default:
  1551. X                mj++;
  1552. X        }
  1553. X        patptr++;
  1554. X    }
  1555. X
  1556. X    if (rmagical && mj > 0)
  1557. X    {
  1558. X        rmcptr->mc_type = LITCHAR;
  1559. X        if ((rmcptr->rstr = malloc(mj + 1)) == NULL)
  1560. X        {
  1561. X            mlwrite("%%Out of memory.");
  1562. X            status = FALSE;
  1563. X        }
  1564. X        strncpy(rmcptr->rstr, patptr - mj, mj);
  1565. X        rmcptr++;
  1566. X    }
  1567. X
  1568. X    rmcptr->mc_type = MCNIL;
  1569. X}
  1570. X
  1571. X/*
  1572. X * mcclear -- Free up any CCL bitmaps, and MCNIL the MC search arrays.
  1573. X */
  1574. Xmcclear()
  1575. X{
  1576. X    register MC    *mcptr;
  1577. X
  1578. X    mcptr = &mcpat[0];
  1579. X
  1580. X    while (mcptr->mc_type != MCNIL)
  1581. X    {
  1582. X        if ((mcptr->mc_type & MASKCL) == CCL ||
  1583. X            (mcptr->mc_type & MASKCL) == NCCL)
  1584. X            free(mcptr->u.cclmap);
  1585. X        mcptr++;
  1586. X    }
  1587. X    mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
  1588. X}
  1589. X
  1590. X/*
  1591. X * rmcclear -- Free up any strings, and MCNIL the RMC array.
  1592. X */
  1593. Xrmcclear()
  1594. X{
  1595. X    register RMC    *rmcptr;
  1596. X
  1597. X    rmcptr = &rmcpat[0];
  1598. X
  1599. X    while (rmcptr->mc_type != MCNIL)
  1600. X    {
  1601. X        if (rmcptr->mc_type == LITCHAR)
  1602. X            free(rmcptr->rstr);
  1603. X        rmcptr++;
  1604. X    }
  1605. X
  1606. X    rmcpat[0].mc_type = MCNIL;
  1607. X}
  1608. X
  1609. X/*
  1610. X * mceq -- meta-character equality with a character.  In Kernighan & Plauger's
  1611. X *    Software Tools, this is the function omatch(), but i felt there
  1612. X *    were too many functions with the 'match' name already.
  1613. X */
  1614. Xstatic int    mceq(bc, mt)
  1615. Xint    bc;
  1616. XMC    *mt;
  1617. X{
  1618. X    register int result;
  1619. X
  1620. X    switch (mt->mc_type & MASKCL)
  1621. X    {
  1622. X        case LITCHAR:
  1623. X            result = eq(bc, mt->u.lchar);
  1624. X            break;
  1625. X
  1626. X        case ANY:
  1627. X            result = (bc != '\n');
  1628. X            break;
  1629. X
  1630. X        case CCL:
  1631. X            if (!(result = biteq(bc, mt->u.cclmap)))
  1632. X            {
  1633. X                if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1634. X                    (isletter(bc)))
  1635. X                {
  1636. X                    result = biteq(CHCASE(bc), mt->u.cclmap);
  1637. X                }
  1638. X            }
  1639. X            break;
  1640. X
  1641. X        case NCCL:
  1642. X            result = !biteq(bc, mt->u.cclmap);
  1643. X
  1644. X            if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1645. X                (isletter(bc)))
  1646. X            {
  1647. X                result &= !biteq(CHCASE(bc), mt->u.cclmap);
  1648. X            }
  1649. X            break;
  1650. X
  1651. X        default:
  1652. X            mlwrite("mceq: what is %d?", mt->mc_type);
  1653. X            result = FALSE;
  1654. X            break;
  1655. X
  1656. X    }    /* End of switch.*/
  1657. X
  1658. X    return (result);
  1659. X}
  1660. X
  1661. X/*
  1662. X * cclmake -- create the bitmap for the character class.
  1663. X *    ppatptr is left pointing to the end-of-character-class character,
  1664. X *    so that a loop may automatically increment with safety.
  1665. X */
  1666. Xstatic int    cclmake(ppatptr, mcptr)
  1667. Xchar    **ppatptr;
  1668. XMC    *mcptr;
  1669. X{
  1670. X    BITMAP        clearbits();
  1671. X    BITMAP        bmap;
  1672. X    register char    *patptr;
  1673. X    register int    pchr, ochr;
  1674. X
  1675. X    if ((bmap = clearbits()) == NULL)
  1676. X    {
  1677. X        mlwrite("%%Out of memory");
  1678. X        return FALSE;
  1679. X    }
  1680. X
  1681. X    mcptr->u.cclmap = bmap;
  1682. X    patptr = *ppatptr;
  1683. X
  1684. X    /*
  1685. X     * Test the initial character(s) in ccl for
  1686. X     * special cases - negate ccl, or an end ccl
  1687. X     * character as a first character.  Anything
  1688. X     * else gets set in the bitmap.
  1689. X     */
  1690. X    if (*++patptr == MC_NCCL)
  1691. X    {
  1692. X        patptr++;
  1693. X        mcptr->mc_type = NCCL;
  1694. X    }
  1695. X    else
  1696. X        mcptr->mc_type = CCL;
  1697. X
  1698. X    if ((ochr = *patptr) == MC_ECCL)
  1699. X    {
  1700. X        mlwrite("%%No characters in character class");
  1701. X        return (FALSE);
  1702. X    }
  1703. X    else
  1704. X    {
  1705. X        if (ochr == MC_ESC)
  1706. X            ochr = *++patptr;
  1707. X
  1708. X        setbit(ochr, bmap);
  1709. X        patptr++;
  1710. X    }
  1711. X
  1712. X    while (ochr != '\0' && (pchr = *patptr) != MC_ECCL)
  1713. X    {
  1714. X        switch (pchr)
  1715. X        {
  1716. X            /* Range character loses its meaning
  1717. X             * if it is the last character in
  1718. X             * the class.
  1719. X             */
  1720. X            case MC_RCCL:
  1721. X                if (*(patptr + 1) == MC_ECCL)
  1722. X                    setbit(pchr, bmap);
  1723. X                else
  1724. X                {
  1725. X                    pchr = *++patptr;
  1726. X                    while (++ochr <= pchr)
  1727. X                        setbit(ochr, bmap);
  1728. X                }
  1729. X                break;
  1730. X
  1731. X            /* Note: no break between case MC_ESC and the default.
  1732. X             */
  1733. X            case MC_ESC:
  1734. X                pchr = *++patptr;
  1735. X            default:
  1736. X                setbit(pchr, bmap);
  1737. X                break;
  1738. X        }
  1739. X        patptr++;
  1740. X        ochr = pchr;
  1741. X    }
  1742. X
  1743. X    *ppatptr = patptr;
  1744. X
  1745. X    if (ochr == '\0')
  1746. X    {
  1747. X        mlwrite("%%Character class not ended");
  1748. X        free(bmap);
  1749. X        return FALSE;
  1750. X    }
  1751. X    return TRUE;
  1752. X}
  1753. X
  1754. X/*
  1755. X * biteq -- is the character in the bitmap?
  1756. X */
  1757. Xstatic int    biteq(bc, cclmap)
  1758. Xint    bc;
  1759. XBITMAP    cclmap;
  1760. X{
  1761. X    if (bc >= HICHAR)
  1762. X        return FALSE;
  1763. X
  1764. X    return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
  1765. X}
  1766. X
  1767. X/*
  1768. X * clearbits -- Allocate and zero out a CCL bitmap.
  1769. X */
  1770. Xstatic    BITMAP clearbits()
  1771. X{
  1772. X    char        *malloc();
  1773. X
  1774. X    BITMAP        cclstart, cclmap;
  1775. X    register int    j;
  1776. X
  1777. X    if ((cclmap = cclstart = (BITMAP) malloc(HIBYTE)) != NULL)
  1778. X        for (j = 0; j < HIBYTE; j++)
  1779. X            *cclmap++ = 0;
  1780. X
  1781. X    return (cclstart);
  1782. X}
  1783. X
  1784. X/*
  1785. X * setbit -- Set a bit (ON only) in the bitmap.
  1786. X */
  1787. Xstatic void setbit(bc, cclmap)
  1788. Xint    bc;
  1789. XBITMAP    cclmap;
  1790. X{
  1791. X    if (bc < HICHAR)
  1792. X        *(cclmap + (bc >> 3)) |= BIT(bc & 7);
  1793. X}
  1794. X#endif
  1795. FRIDAY_NIGHT
  1796. echo mes.10 completed!
  1797. # That's all folks!
  1798.  
  1799.  
  1800.