home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / insert.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  17KB  |  902 lines

  1. /************************************************************************
  2.  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
  3.  * provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is *
  5.  * included in all the files.                                           *
  6.  ************************************************************************/
  7.  
  8. #include "jove.h"
  9. #include "jctype.h"
  10. #include "list.h"
  11. #include "chars.h"
  12. #include "disp.h"
  13. #include "abbrev.h"
  14. #include "ask.h"
  15. #include "c.h"
  16. #include "delete.h"
  17. #include "insert.h"
  18. #include "fmt.h"
  19. #include "macros.h"
  20. #include "marks.h"
  21. #include "misc.h"
  22. #include "move.h"
  23. #include "paragraph.h"
  24. #include "screen.h"    /* for tabstop */
  25. #include "sysprocs.h"
  26. #include "proc.h"
  27. #include "wind.h"
  28.  
  29. private void
  30.     DoNewline proto((bool indentp));
  31.  
  32. #ifdef LISP
  33. private Bufpos
  34.     *lisp_indent proto((void));
  35. #endif
  36.  
  37. /* Make a new line after "after" in buffer "buf", unless "after" is NULL,
  38.    in which case we insert the new line before first line. */
  39.  
  40. LinePtr
  41. listput(buf, after)
  42. register Buffer    *buf;
  43. register LinePtr    after;
  44. {
  45.     register LinePtr    newline = nbufline();
  46.  
  47.     newline->l_prev = after;
  48.     if (after == NULL) {    /* Before the first line */
  49.         newline->l_next = buf->b_first;
  50.         buf->b_first = newline;
  51.     } else {
  52.         newline->l_next = after->l_next;
  53.         after->l_next = newline;
  54.     }
  55.     if (newline->l_next != NULL)
  56.         newline->l_next->l_prev = newline;
  57.     else if (buf != NULL)
  58.         buf->b_last = newline;
  59.     if (buf && buf->b_dot == NULL)
  60.         buf->b_dot = newline;
  61.     return newline;
  62. }
  63.  
  64. /* Divide the current line and move the current line to the next one */
  65.  
  66. void
  67. LineInsert(num)
  68. register int    num;
  69. {
  70.     char    newline[LBSIZE];
  71.     register LinePtr    newdot,
  72.             olddot;
  73.     int    oldchar;
  74.  
  75.     olddot = curline;
  76.     oldchar = curchar;
  77.  
  78.     newdot = curline;
  79.     while (--num >= 0) {
  80.         newdot = listput(curbuf, newdot);
  81.         SavLine(newdot, NullStr);
  82.     }
  83.  
  84.     modify();
  85.     if (curchar != 0) {
  86.         strcpy(newline, &linebuf[curchar]);
  87.         linebuf[curchar] = '\0';    /* Shorten this line */
  88.         SavLine(curline, linebuf);
  89.         strcpy(linebuf, newline);
  90.     } else {    /* Redisplay optimization */
  91.         newdot->l_dline = curline->l_dline;
  92.         SavLine(curline, NullStr);
  93.     }
  94.  
  95.     makedirty(curline);
  96.     curline = newdot;
  97.     curchar = 0;
  98.     makedirty(curline);
  99.     IFixMarks(olddot, oldchar, curline, curchar);
  100. }
  101.  
  102. /* Inserts tabs and spaces to move the cursor to column GOAL.  It
  103.    Uses the most optimal number of tabs and spaces no matter what
  104.    was there before hand. */
  105.  
  106. void
  107. n_indent(goal)
  108. register int    goal;
  109. {
  110.     int    dotcol;
  111.  
  112.     DelWtSpace();
  113.     dotcol = calc_pos(linebuf, curchar);
  114.  
  115.     if (tabstop != 0) {
  116.         for (;;) {
  117.             int    incrmt = TABDIST(dotcol);
  118.  
  119.             if (dotcol + incrmt > goal)
  120.                 break;
  121.             insert_c('\t', 1);
  122.             dotcol += incrmt;
  123.         }
  124.     }
  125.     if (dotcol != goal)
  126.         insert_c(' ', (goal - dotcol));
  127. }
  128.  
  129. #ifdef ABBREV
  130. void
  131. MaybeAbbrevExpand()
  132. {
  133.     if (MinorMode(Abbrev) && !jisident(LastKeyStruck)
  134.     && !bolp() && jisident(linebuf[curchar - 1]))
  135.         AbbrevExpand();
  136. }
  137. #endif
  138.  
  139. private void
  140. Insert(c)
  141. char    c;
  142. {
  143.     if (c == CTL('J'))
  144.         LineInsert(arg_value());
  145.     else
  146.         insert_c(c, arg_value());
  147. }
  148.  
  149. void
  150. overwrite(c, n)
  151. char    c;
  152. int    n;
  153. {
  154.     register int    i;
  155.  
  156.     for (i = 0; i < n; i++) {
  157.         /* Delete one *column* forward (except that we don't
  158.          * notice that control characters take two columns).
  159.          */
  160.         if (!eolp() && (linebuf[curchar] != '\t' || tabstop == 0
  161.           || TABDIST(calc_pos(linebuf, curchar)) == 1))
  162.         {
  163.             del_char(FORWARD, 1, NO);
  164.         }
  165.         insert_c(c, 1);
  166.     }
  167. }
  168.  
  169. void
  170. SelfInsert()
  171. {
  172. #ifdef ABBREV
  173.     MaybeAbbrevExpand();
  174. #endif
  175.     if (LastKeyStruck != CTL('J') && MinorMode(OverWrite))
  176.         overwrite(LastKeyStruck, arg_value());
  177.     else
  178.         Insert(LastKeyStruck);
  179.  
  180.     /* If we are in fill mode and at or beyond the right margin,
  181.      * we break the line.  However, we won't do this if the new
  182.      * character is whitespace and we are at the end of the line:
  183.      * DoJustify would discard the trailing space.
  184.      */
  185.     if (MinorMode(Fill) && calc_pos(linebuf, curchar) >= RMargin
  186.     && !(jiswhite(LastKeyStruck) && eolp()))
  187.     {
  188.         int margin;
  189.         Bufpos save;
  190.  
  191.         if (MinorMode(Indent)) {
  192.             DOTsave(&save);
  193.             ToIndent();
  194.             margin = calc_pos(linebuf, curchar);
  195.             SetDot(&save);
  196.         } else {
  197.             margin = LMargin;
  198.         }
  199.         DoJustify(curline, 0, curline,
  200.               curchar + (int)strlen(&linebuf[curchar]), YES, margin);
  201.     }
  202. }
  203.  
  204. /* insert character C N times at point */
  205. void
  206. insert_c(c, n)
  207. char    c;
  208. int    n;
  209. {
  210.     if (n > 0) {
  211.         modify();
  212.         makedirty(curline);
  213.         ins_c(c, linebuf, curchar, n, LBSIZE);
  214.         IFixMarks(curline, curchar, curline, curchar + n);
  215.         curchar += n;
  216.     }
  217. }
  218.  
  219. /* Tab in to the right place for C mode */
  220.  
  221. void
  222. Tab()
  223. {
  224. #ifdef LISP
  225.     if (MajorMode(LISPMODE) && (bolp() || !eolp())) {
  226.         int    dotchar = curchar;
  227.  
  228.         ToIndent();
  229.         if (dotchar > curchar) {
  230.             Mark    *m = MakeMark(curline, dotchar);
  231.  
  232.             (void) lisp_indent();
  233.             ToMark(m);
  234.             DelMark(m);
  235.         } else {
  236.             (void) lisp_indent();
  237.             ToIndent();
  238.         }
  239.         return;
  240.     }
  241. #endif
  242.     if (MajorMode(CMODE)) {
  243.         if (within_indent())
  244.             (void) c_indent(NO);
  245.         else {
  246.             int    curpos,
  247.                 tabbed_pos;
  248.  
  249.             skip_wht_space();
  250.             curpos = calc_pos(linebuf, curchar);
  251.             tabbed_pos = curpos + (CIndIncrmt - (curpos % CIndIncrmt));
  252.             n_indent(tabbed_pos);
  253.         }
  254.     } else
  255.         SelfInsert();
  256. }
  257.  
  258. void
  259. QuotChar()
  260. {
  261.     ZXchar    c = ask_ks();
  262.  
  263.     if (c == '\0') {
  264.         int    n = arg_value();
  265.  
  266.         while (n-- > 0)
  267.             ins_str("^@");
  268.     } else {
  269.         Insert(c);
  270. #ifdef PCNONASCII
  271.         if (c == PCNONASCII) {
  272.             c = waitchar();
  273.             if (c == '\0') {
  274.                 int    n = arg_value();
  275.  
  276.                 while (n-- > 0)
  277.                     ins_str("^@");
  278.             } else {
  279.                 Insert(c);
  280.             }
  281.         }
  282. #endif
  283.     }
  284. }
  285.  
  286. /* Insert the paren.  If in C mode and c is a '}' then insert the
  287.    '}' in the "right" place for C indentation; that is indented
  288.    the same amount as the matching '{' is indented. */
  289.  
  290. int    PDelay = 5,        /* VAR: paren flash delay in tenths of a second */
  291.     CIndIncrmt = 8;    /* VAR: how much each indentation level pushes over in C mode */
  292.  
  293. void
  294. DoParen()
  295. {
  296.     Bufpos    *bp = NULL;    /* avoid uninitialized complaint from gcc -W */
  297.     ZXchar    c = LastKeyStruck;
  298.     bool    tried = NO;
  299.  
  300.     if (!jisclosep(c)) {
  301.         SelfInsert();
  302.         return;
  303.     }
  304.  
  305.     if (MajorMode(CMODE) && c == '}' && within_indent()) {
  306.         bp = c_indent(YES);
  307.         tried = YES;
  308.     }
  309. #ifdef LISP
  310.     if (MajorMode(LISPMODE) && c == ')' && blnkp(linebuf)) {
  311.         bp = lisp_indent();
  312.         tried = YES;
  313.     }
  314. #endif
  315.     SelfInsert();
  316.  
  317.     if (MinorMode(ShowMatch)
  318. #ifndef MAC
  319.     && !PreEmptOutput()
  320. #endif
  321.     && !in_macro())
  322.     {
  323.         b_char(1);    /* Back onto the ')' */
  324.         if (!tried)
  325.             bp = m_paren(c, BACKWARD, NO, YES);
  326.         f_char(1);
  327.         if (bp != NULL) {
  328.             int    nx = in_window(curwind, bp->p_line);
  329.  
  330.             if (nx != -1) {        /* is visible */
  331.                 Bufpos    b;
  332.  
  333.                 DOTsave(&b);
  334.                 SetDot(bp);
  335.                 SitFor(PDelay);
  336.                 SetDot(&b);
  337.             } else
  338.                 s_mess("%s", lcontents(bp->p_line));
  339.         }
  340.         mp_error();    /* display error message */
  341.     }
  342. }
  343.  
  344. void
  345. LineAI()
  346. {
  347.     DoNewline(YES);
  348. }
  349.  
  350. void
  351. Newline()
  352. {
  353.     DoNewline(MinorMode(Indent));
  354. }
  355.  
  356. private void
  357. DoNewline(indentp)
  358. bool    indentp;
  359. {
  360.     Bufpos    save;
  361.     int    indent;
  362.  
  363.     /* first we calculate the indent of the current line */
  364.     DOTsave(&save);
  365.     ToIndent();
  366.     indent = calc_pos(linebuf, curchar);
  367.     SetDot(&save);
  368.  
  369. #ifdef ABBREV
  370.     MaybeAbbrevExpand();
  371. #endif
  372.     if (
  373. #ifdef LISP
  374.         MajorMode(LISPMODE) ||
  375. #endif
  376.         indentp)
  377.     {
  378.         DelWtSpace();
  379.     }
  380.  
  381.     /* If there is more than 2 blank lines in a row then don't make
  382.        a newline, just move down one. */
  383.     if (arg_value() == 1 && eolp() && TwoBlank())
  384.         SetLine(curline->l_next);
  385.     else
  386.         LineInsert(arg_value());
  387.  
  388.     if (indentp) {
  389. #ifdef LISP
  390.         if (MajorMode(LISPMODE))
  391.         (void) lisp_indent();
  392.         else
  393. #endif
  394.         {
  395.         Bol();
  396.         n_indent((LMargin == 0) ? indent : LMargin);
  397.         }
  398.     }
  399. }
  400.  
  401. void
  402. ins_str(str)
  403. const char *str;
  404. {
  405.     ins_str_wrap(str, NO, LBSIZE-1);
  406. }
  407.  
  408. void
  409. ins_str_wrap(str, ok_nl, wrap_off)
  410. const char *str;
  411. bool ok_nl;
  412. int wrap_off;
  413. {
  414.     register char c;
  415.     Bufpos save;
  416.     int llen;
  417.  
  418.     if (*str == '\0')
  419.         return;        /* ain't nothing to insert! */
  420.     if (wrap_off > LBSIZE-1)
  421.         wrap_off = LBSIZE-1;
  422.     DOTsave(&save);
  423.     llen = strlen(linebuf);
  424.     while ((c = *str++) != '\0') {
  425.         if (c == '\n' || (ok_nl && llen >= wrap_off)) {
  426.             IFixMarks(save.p_line, save.p_char, curline, curchar);
  427.             modify();
  428.             makedirty(curline);
  429.             LineInsert(1);
  430.             DOTsave(&save);
  431.             llen = strlen(linebuf);
  432.         }
  433.         if (c != '\n') {
  434.             ins_c(c, linebuf, curchar++, 1, LBSIZE);
  435.             llen += 1;
  436.         }
  437.     }
  438.     IFixMarks(save.p_line, save.p_char, curline, curchar);
  439.     modify();
  440.     makedirty(curline);
  441. }
  442.  
  443. void
  444. open_lines(n)
  445. int    n;
  446. {
  447.     Bufpos    dot;
  448.  
  449.     DOTsave(&dot);
  450.     LineInsert(n);    /* Open the lines... */
  451.     SetDot(&dot);
  452. }
  453.  
  454. void
  455. OpenLine()
  456. {
  457.     open_lines(arg_value());
  458. }
  459.  
  460. /* Take the region FLINE/FCHAR to TLINE/TCHAR and insert it at
  461.    ATLINE/ATCHAR in WHATBUF. */
  462.  
  463. Bufpos *
  464. DoYank(fline, fchar, tline, tchar, atline, atchar, whatbuf)
  465. LinePtr    fline,
  466.     tline,
  467.     atline;
  468. int    fchar,
  469.     tchar,
  470.     atchar;
  471. Buffer    *whatbuf;
  472. {
  473.     register LinePtr    newline;
  474.     static Bufpos    bp;
  475.     char    save[LBSIZE],
  476.         buf[LBSIZE];
  477.     LinePtr    startline = atline;
  478.     int    startchar = atchar;
  479.  
  480.     lsave();
  481.     if (whatbuf != NULL)
  482.         modify();
  483.     (void) ltobuf(atline, genbuf);
  484.     strcpy(save, &genbuf[atchar]);
  485.  
  486.     (void) ltobuf(fline, buf);
  487.     if (fline == tline)
  488.         buf[tchar] = '\0';
  489.  
  490.     linecopy(genbuf, atchar, &buf[fchar]);
  491.     atline->l_dline = putline(genbuf);
  492.     makedirty(atline);
  493.  
  494.     fline = fline->l_next;
  495.     while (fline != tline->l_next) {
  496.         newline = listput(whatbuf, atline);
  497.         newline->l_dline = fline->l_dline;
  498.         makedirty(newline);
  499.         fline = fline->l_next;
  500.         atline = newline;
  501.         atchar = 0;
  502.     }
  503.  
  504.     getline(atline->l_dline, genbuf);
  505.     atchar += tchar;
  506.     linecopy(genbuf, atchar, save);
  507.     atline->l_dline = putline(genbuf);
  508.     makedirty(atline);
  509.     IFixMarks(startline, startchar, atline, atchar);
  510.     bp.p_line = atline;
  511.     bp.p_char = atchar;
  512.     if (whatbuf != NULL)
  513.         this_cmd = YANKCMD;
  514.     getDOT();            /* Whatever used to be in linebuf */
  515.     return &bp;
  516. }
  517.  
  518. void
  519. YankPop()
  520. {
  521.     Mark    *mp = CurMark();
  522.     LinePtr    line,
  523.         last;
  524.     Bufpos    *dot;
  525.  
  526.     switch (last_cmd) {
  527.     case YANKCMD:
  528.         {
  529.             /* Direction to rotate the ring */
  530.             int    dir = arg_value() < 0? 1 : NUMKILLS - 1;
  531.  
  532.             /* Now must find a recently killed region. */
  533.             do {
  534.                 killptr = (killptr+dir) % NUMKILLS;
  535.             } while (killbuf[killptr] == NULL);
  536.         }
  537.         break;
  538.     case UNDOABLECMD:
  539.         break;
  540.     default:
  541.         complain("Yank something first!");
  542.         /*NOTREACHED*/
  543.     }
  544.     lfreelist(reg_delete(mp->m_line, mp->m_char, curline, curchar));
  545.     line = killbuf[killptr];
  546.     last = lastline(line);
  547.     dot = DoYank(line, 0, last, length(last), curline, curchar, curbuf);
  548.     MarkSet(CurMark(), curline, curchar);
  549.     SetDot(dot);
  550.     if (last_cmd == UNDOABLECMD)
  551.         this_cmd = OTHER_CMD;
  552. }
  553.  
  554. /* This is an attempt to reduce the amount of memory taken up by each line.
  555.    Without this each malloc of a line uses sizeof (line) + sizeof(HEADER)
  556.    where line is 3 words and HEADER is 1 word.
  557.    This is going to allocate memory in chucks of CHUNKSIZE * sizeof (line)
  558.    and divide each chuck into Lines.  A line is free in a chunk when its
  559.    line->l_dline == NULL_DADDR, so freeline sets l_dline to NULL_DADDR. */
  560.  
  561. #define CHUNKSIZE    300
  562.  
  563. #ifdef FAR_LINES
  564. # ifdef __BORLANDC__
  565. #  include <alloc.h>    /* Borland farmalloc() */
  566. # else
  567. #  ifdef __WATCOMC__
  568. #   include <malloc.h>
  569. #   define farmalloc(sz)    _fmalloc(sz)
  570. #   define farfree(x)    _ffree(x)
  571. #  else
  572. #   include <dos.h>    /* Zortech farmalloc(), MSC (?) */
  573. #  endif
  574. # endif
  575. typedef struct chunk _far    *ChunkPtr;
  576. # define CHUNKMALLOC(s)    ((ChunkPtr) farmalloc(s))
  577. # define CHUNKFREE(c)    farfree((void _far *) (c))
  578. #else
  579. typedef struct chunk    *ChunkPtr;
  580. # define CHUNKMALLOC(s)    ((ChunkPtr) malloc(s))
  581. # define CHUNKFREE(c)    free((UnivPtr) (c))
  582. #endif
  583.  
  584. struct chunk {
  585.     ChunkPtr    c_nextchunk;    /* Next chunk of lines */
  586.     int    c_nlines;    /* Number of lines in this chunk (so they
  587.                    don't all have to be CHUNKSIZE long). */
  588.     struct line    c_block[1 /* or larger */];    /* Chunk of memory */
  589. };
  590.  
  591. private ChunkPtr    fchunk = NULL;    /* first chunk */
  592. private LinePtr    ffline = NULL;    /* First free line */
  593. private LinePtr    faline = NULL;    /* First available line */
  594.  
  595. private void
  596. freeline(line)
  597. register LinePtr    line;
  598. {
  599.     line->l_dline = NULL_DADDR;
  600.     line->l_next = ffline;
  601.     if (ffline)
  602.         ffline->l_prev = line;
  603.     line->l_prev = NULL;
  604.     ffline = line;
  605. }
  606.  
  607. /* Make sure that there are no dangling references to lines in the free list,
  608.  * then move them to the end of the avail list.
  609.  */
  610.  
  611. private void
  612. RecycleLines()
  613. {
  614.     if (ffline == NULL)
  615.         return;    /* nothing to do */
  616.  
  617.     ChkErrorLines();
  618.     /* ChkWindowLines(); -- nothing needs attention */
  619.     /* ChkBufLines(); -- nothing needs attention */
  620.  
  621.     if (faline == NULL) {
  622.         faline = ffline;
  623.     } else {
  624.         LinePtr    laline = lastline(faline);
  625.  
  626.         laline->l_next = ffline;
  627.         ffline->l_prev = laline;
  628.     }
  629.     ffline = NULL;
  630. }
  631.  
  632. void
  633. lfreelist(first)
  634. register LinePtr    first;
  635. {
  636.     if (first != NULL)
  637.         lfreereg(first, lastline(first));
  638. }
  639.  
  640. /* Append region from line1 to line2 onto the free list of lines */
  641.  
  642. void
  643. lfreereg(line1, line2)
  644. register LinePtr    line1,
  645.         line2;
  646. {
  647.     register LinePtr    next,
  648.             last = line2->l_next;
  649.  
  650.     while (line1 != last) {
  651.         next = line1->l_next;
  652.         freeline(line1);
  653.         line1 = next;
  654.     }
  655. }
  656.  
  657. private bool
  658. newchunk()
  659. {
  660.     register LinePtr    newline;
  661.     register int    i;
  662.     ChunkPtr    f;
  663.     int    nlines = CHUNKSIZE;
  664.     bool    done_gc = NO;
  665.  
  666.     for (;;) {
  667.         f = CHUNKMALLOC(sizeof(struct chunk) + sizeof(struct line) * (nlines-1));
  668.         if (f != NULL)
  669.             break;
  670.         if (!done_gc) {
  671.             GCchunks();
  672.             done_gc = YES;
  673.         } else {
  674.             nlines /= 2;
  675.             if (nlines <= 0)
  676.                 return NO;
  677.         }
  678.     }
  679.  
  680.     f->c_nlines = nlines;
  681.     for (i = 0, newline = f->c_block; i < nlines; newline++, i++) {
  682.         newline->l_dline = NULL_DADDR;
  683.         newline->l_next = faline;
  684.         if (faline)
  685.             faline->l_prev = newline;
  686.         newline->l_prev = NULL;
  687.         faline = newline;
  688.     }
  689.     f->c_nextchunk = fchunk;
  690.     fchunk = f;
  691.     return YES;
  692. }
  693.  
  694. /* New BUFfer LINE */
  695.  
  696. LinePtr
  697. nbufline()
  698. {
  699.     register LinePtr    newline;
  700.  
  701.     if (faline == NULL) {
  702.         RecycleLines();
  703.         if (faline == NULL) {
  704.             if (!newchunk())
  705.                 complain("[Out of lines] ");
  706.         }
  707.     }
  708.     newline = faline;
  709.     faline = newline->l_next;
  710.     if (faline)
  711.         faline->l_prev = NULL;
  712.     return newline;
  713. }
  714.  
  715. /* Remove the free lines, in chunk c, from the free list because they are
  716.    no longer free. */
  717.  
  718. private void
  719. remfreelines(c)
  720. register ChunkPtr    c;
  721. {
  722.     register LinePtr    lp;
  723.     register int    i;
  724.  
  725.     for (lp = c->c_block, i = c->c_nlines; i != 0 ; lp++, i--) {
  726.         if (lp->l_prev == NULL)
  727.             faline = lp->l_next;
  728.         else
  729.             lp->l_prev->l_next = lp->l_next;
  730.         if (lp->l_next != NULL)
  731.             lp->l_next->l_prev = lp->l_prev;
  732.     }
  733. }
  734.  
  735. /* This is used to garbage collect the chunks of lines when malloc fails
  736.    and we are NOT looking for a new buffer line.  This goes through each
  737.    chunk, and if every line in a given chunk is not allocated, the entire
  738.    chunk is `free'd by "free()". */
  739.  
  740. /* ??? I think that this WILL be called when we are looking for a new
  741.  * buffer line: nbufline() => newchunk() => GCchunks() -- DHR
  742.  */
  743.  
  744. void
  745. GCchunks()
  746. {
  747.     register ChunkPtr    cp;
  748.     ChunkPtr    prev = NULL,
  749.             next;
  750.     register int    i;
  751.     register LinePtr    newline;
  752.  
  753.     RecycleLines();
  754.     for (cp = fchunk; cp != NULL; cp = next) {
  755.         next = cp->c_nextchunk;
  756.         for (i = cp->c_nlines, newline = cp->c_block; ; newline++, i--) {
  757.             if (i == 0) {
  758.                 /* Empty: unlink and free it!!! */
  759.                 if (prev == NULL)
  760.                     fchunk = cp->c_nextchunk;
  761.                 else
  762.                     prev->c_nextchunk = cp->c_nextchunk;
  763.                 remfreelines(cp);
  764.                 CHUNKFREE(cp);
  765.                 break;
  766.             }
  767.             if (newline->l_dline != NULL_DADDR) {
  768.                 /* it's a keeper */
  769.                 prev = cp;
  770.                 break;
  771.             }
  772.         }
  773.     }
  774. }
  775.  
  776. #ifdef LISP
  777.  
  778. #include "re.h"
  779.  
  780. /* Grind S-Expr */
  781.  
  782. void
  783. GSexpr()
  784. {
  785.     Bufpos    dot,
  786.         end;
  787.  
  788.     if (linebuf[curchar] != '(')
  789.         complain((char *)NULL);
  790.     DOTsave(&dot);
  791.     FSexpr();
  792.     DOTsave(&end);
  793.     SetDot(&dot);
  794.     for (;;) {
  795.         if (curline == end.p_line)
  796.             break;
  797.         line_move(FORWARD, 1, NO);
  798.         if (!blnkp(linebuf))
  799.             (void) lisp_indent();
  800.     }
  801.     SetDot(&dot);
  802. }
  803.  
  804. /* lisp_indent() indents a new line in Lisp Mode, according to where
  805.    the matching close-paren would go if we typed that (sort of). */
  806.  
  807. private List    *specials = NULL;
  808.  
  809. private void
  810. init_specials()
  811. {
  812.     static char *const words[] = {
  813.         "case",
  814.         "def",
  815.         "dolist",
  816.         "fluid-let",
  817.         "lambda",
  818.         "let",
  819.         "lexpr",
  820.         "macro",
  821.         "named-l",    /* named-let and named-lambda */
  822.         "nlambda",
  823.         "prog",
  824.         "selectq",
  825.         NULL
  826.     };
  827.     char    *const *wordp = words;
  828.  
  829.     while (*wordp != NULL)
  830.         list_push(&specials, (UnivPtr) *wordp++);
  831. }
  832.  
  833. void
  834. AddSpecial()
  835. {
  836.     char    *word;
  837.     register List    *lp;
  838.  
  839.     if (specials == NULL)
  840.         init_specials();
  841.     word = ask((char *)NULL, ProcFmt);
  842.     for (lp = specials; lp != NULL; lp = list_next(lp))
  843.         if (strcmp((char *) list_data(lp), word) == 0)
  844.             return;        /* already in list */
  845.     (void) list_push(&specials, (UnivPtr) copystr(word));
  846. }
  847.  
  848. private Bufpos *
  849. lisp_indent()
  850. {
  851.     Bufpos    *bp,
  852.         savedot;
  853.     int    goal;
  854.  
  855.     bp = m_paren(')', BACKWARD, NO, YES);
  856.  
  857.     if (bp == NULL)
  858.         return NULL;
  859.  
  860.     /* We want to end up
  861.  
  862.         (atom atom atom ...
  863.               ^ here.
  864.      */
  865.  
  866.     DOTsave(&savedot);
  867.     SetDot(bp);
  868.     f_char(1);
  869.     if (linebuf[curchar] != '(') {
  870.         register List    *lp;
  871.  
  872.         if (specials == NULL)
  873.             init_specials();
  874.         for (lp = specials; lp != NULL; lp = list_next(lp))
  875.             if (caseeqn((char *) list_data(lp),
  876.                      &linebuf[curchar],
  877.                      strlen((char *) list_data(lp))))
  878.                 break;
  879.         if (lp == NULL) {    /* not special */
  880.             int    c_char = curchar;
  881.  
  882.             while (jisident(linebuf[curchar]))
  883.                 curchar += 1;
  884.             if (LookingAt("[ \t]*;\\|[ \t]*$", linebuf, curchar)) {
  885.                 curchar = c_char;
  886.             } else {
  887.                 while (linebuf[curchar] == ' ')
  888.                     curchar += 1;
  889.             }
  890.         } else {
  891.             curchar += 1;
  892.         }
  893.     }
  894.     goal = calc_pos(linebuf, curchar);
  895.     SetDot(&savedot);
  896.     Bol();
  897.     n_indent(goal);
  898.  
  899.     return bp;
  900. }
  901. #endif /* LISP */
  902.