home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / jove414s.zip / insert.c < prev    next >
C/C++ Source or Header  |  1991-07-06  |  16KB  |  779 lines

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