home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / undo.c < prev    next >
C/C++ Source or Header  |  1998-04-28  |  23KB  |  911 lines

  1. /* these routines take care of undo operations
  2.  * code by Paul Fox, original algorithm mostly by Julia Harper May, 89
  3.  *
  4.  * written for vile: Copyright (c) 1990, 1995 by Paul Fox
  5.  *
  6.  * $Header: /usr/build/vile/vile/RCS/undo.c,v 1.69 1998/04/28 10:19:14 tom Exp $
  7.  *
  8.  */
  9.  
  10. #include "estruct.h"
  11. #include "edef.h"
  12.  
  13. #ifndef NULL
  14. #define NULL 0
  15. #endif
  16.  
  17. /*
  18.  * There are two stacks and two "dots" saved for undo operations.
  19.  *
  20.  * The first stack, the "backstack", or the "undo stack", is used to move
  21.  * back in history -- that is, as changes are made, they go on this stack,
  22.  * and as we undo those changes, they come off of this stack.  The second
  23.  * stack, the "forward stack" or "redo stack", is used when going forward
  24.  * in history, i.e., when undoing an undo.
  25.  *
  26.  * The "backdot" is the last value of DOT _before_ a change occurred, and
  27.  * therefore must be restored _after_ undoing that change.  The "forwdot"
  28.  * is the first value of DOT _after_ a change occurred, and therefore must
  29.  * be restored _after_ re-doing that change.
  30.  *
  31.  * Distinct sets of changes (i.e.  single user-operations) are separated on
  32.  * the stacks by "stack separator" (natch) entries.  The lforw and lback
  33.  * pointers of these entries are used to save the values of the forward and
  34.  * backward dots.
  35.  *
  36.  * The undo strategy is this:
  37.  *
  38.  * 1) For any deleted line, push it onto the undo stack.
  39.  *
  40.  * 2) On any change to a line, make a copy of it, push the copy to the undo
  41.  * stack, and mark the original as having been copied.  Do not copy/push
  42.  * lines that are already marked as having been copied.  Push a tag
  43.  * matching up the copy with the original.  Later, when undoing, and the
  44.  * copy is being put into the file, we can go down through the undo stack,
  45.  * find all references to the original line, and make them point at the
  46.  * copy instead.  We could do this immediately, instead of pushing the
  47.  * "patch" and waiting for the undo, but it seems better to pay the price
  48.  * of that stack walk at undo time, rather than at the time of the change.
  49.  * This patching wouldn't be necessary at all if we used line no's as the
  50.  * pointers, instead of real pointers.
  51.  *
  52.  * On the actual undo, we pop these things (lines or tags) one by one.
  53.  * There should be either a) no lines where it goes (it was a deleted line)
  54.  * and we can just put it back, or b) exactly one line where it goes (it
  55.  * was a changed/copied line) and it can be replaced, or if this is a tag
  56.  * we're popping (not a real line), then the line it points at was a
  57.  * fresh insert (and should be deleted now).  That makes it easy to undo
  58.  * the changes one by one.  Of course, we need to build a different,
  59.  * inverse stack (the "forward", or "redo" stack) as we go, so that undo
  60.  * can itself be undone.
  61.  *
  62.  * The "copied" cookie in the LINE structure is unioned with the stack link
  63.  * pointer on the undo stack, since they aren't both needed at once.
  64.  *
  65.  * There are basically three interface routines for code making buffer changes:
  66.  *  toss_to_undo() -- called when deleting a whole line
  67.  *  tag_for_undo() -- called when inserting a whole line
  68.  *  copy_for_undo() -- called when modifying a line
  69.  * These routines should be called _before_ calling chg_buff to mark the
  70.  * buffer as modified, since they want to record the current
  71.  * modified/unmodified state in the undo stack, so it can be restored later.
  72.  *
  73.  * In addition:
  74.  *  freeundostacks() cleans up any undo data structures for a buffer,
  75.  *  nounmodifiable() is called if a change is happening to a
  76.  *    buffer that is not undoable.
  77.  *  mayneedundo() is called before starting an operation that might call
  78.  *    one of the toss/copy/tag routines above.
  79.  *  dumpuline() is called if the whole-line undo (the 'U' command) line
  80.  *    may need to be flushed due to the current change.
  81.  *
  82.  * The command functions are:
  83.  *  backundo() -- undo changes going back in history  (^X-u)
  84.  *  forwredo() -- redo changes going forward in history  (^X-r)
  85.  *  undo() -- undo changes, toggling backwards and forwards  (u)
  86.  *  lineundo() -- undo all changes to currently-being-modified line (U)
  87.  *    (The lineundo() command is kind of separate, and acts independently
  88.  *     of the normal undo stacks -- an extra copy of the line is simply
  89.  *     made when the first change is made.)
  90.  *
  91.  *
  92.  * Notes on how the "copied" marks work:
  93.  *
  94.  * Say you do a change to a line, like you go into insertmode, and type
  95.  * three characters.  The first character causes linsert to be called,
  96.  * which calls copy_for_undo(), which copies the line, and marks it as
  97.  * "copied".  Then, the second and third characters also call
  98.  * copy_for_undo(), but since the line is marked, nothing happens.  Now you
  99.  * hit ESC.  Now enter insertmode again.  So far, nothing has happened,
  100.  * except that the "needundocleanup" flag has been set (by execute()s call
  101.  * to mayneedundo()), since we're in an undo-able command.  Type a
  102.  * character.  linsert() calls copy_for_undo() which calls preundocleanup()
  103.  * (based on the "needundocleanup" flag.  In previous versions of vile,
  104.  * this cleanup required walking the entire buffer, to reset the "copied"
  105.  * flag.  Now, the "copied" flag is actually a word-sized "cookie", which
  106.  * matches the global "current_undo_cookie" when the line has bee copied.
  107.  * By incrementing the global "current_undo_cookie" in preundocleanup(), we
  108.  * are effectively resetting all of the lines' "marks", since the cookie is
  109.  * now _guaranteed_ to not match against any of them.  Which is why, when
  110.  * the cookie wraps around to 0, we _do_ need to clean the buffer, because
  111.  * now there's a chance that the current_undo_cookie might match a very old
  112.  * marked line.
  113.  */
  114.  
  115. #define FORW 0
  116. #define BACK 1
  117.  
  118. /* shorthand for the two stacks and the two dots */
  119. #define BACKSTK(bp) (&(bp->b_udstks[BACK]))
  120. #define FORWSTK(bp) (&(bp->b_udstks[FORW]))
  121. #define BACKDOT(bp) (bp->b_uddot[BACK])
  122. #define FORWDOT(bp) (bp->b_uddot[FORW])
  123.  
  124. /* these let us refer to the current and other stack in relative terms */
  125. #define STACK(i)    (&(curbp->b_udstks[i]))
  126. #define OTHERSTACK(i)    (&(curbp->b_udstks[1^i]))
  127.  
  128. static    LINEPTR    copyline(LINE *lp);
  129. static    void    make_undo_patch(LINEPTR olp, LINEPTR nlp);
  130. static    void    freshstack(int stkindx);
  131. static    void    pushline(LINEPTR lp, LINEPTR *stk);
  132. static    LINE *    popline(LINEPTR *stkp, int force);
  133. static    int    undoworker(int stkindx);
  134. static    void    preundocleanup (void);
  135. static    void    repointstuff(register LINEPTR nlp, register LINEPTR olp);
  136. static    int    linesmatch(LINE *lp1, register LINE *lp2);
  137. static    void    setupuline(LINEPTR lp);
  138.  
  139. static    short    needundocleanup;
  140.  
  141. /* this could be per-buffer, but i don't think it matters in practice */
  142. static unsigned short current_undo_cookie = 1;
  143.  
  144. /* #define UNDOLOG 1 */
  145. #if UNDOLOG
  146. static void
  147. undolog(char *s, LINEPTR lp)
  148. {
  149.     char *t;
  150.     if (lisreal(lp))    t = "real";
  151.     else if (lispurestacksep(lp))t= "purestacksep";
  152.     else if (lisstacksep(lp))t= "stacksep";
  153.     else if (lispatch(lp))    t = "patch";
  154.     else             t = "unknown";
  155.  
  156.     dbgwrite("%s %s lp 0x%x",s,t,lp);
  157. }
  158. #else
  159. # define undolog(s,l)
  160. #endif
  161.  
  162. /*
  163.  * Test if the buffer is modifiable
  164.  */
  165. static int
  166. OkUndo(void)
  167. {
  168.     if (curbp == bminip)
  169.         return FALSE;
  170. #define SCRATCH 1
  171. #if SCRATCH
  172.     if (b_is_scratch(curbp))
  173. #else
  174.     if (b_val(curbp, MDVIEW))
  175. #endif
  176.         return FALSE;
  177.     return TRUE;
  178. }
  179.  
  180. /* push a deleted line onto the undo stack. */
  181. void
  182. toss_to_undo(LINEPTR lp)
  183. {
  184.     register LINEPTR next;
  185.     register LINEPTR prev;
  186.     int fc;
  187.  
  188.     if (!OkUndo())
  189.         return;
  190.  
  191.     if (needundocleanup)
  192.         preundocleanup();
  193.  
  194.     pushline(lp, BACKSTK(curbp));
  195.  
  196.     next = lforw(lp);
  197.  
  198.     /* need to save a dot -- either the next line or
  199.         the previous one */
  200.     if (next == buf_head(curbp)) {
  201.         prev = lback(lp);
  202.         FORWDOT(curbp).l = prev;
  203.         fc =  firstchar(prev);
  204.         if (fc < 0) /* all white */
  205.             FORWDOT(curbp).o = llength(prev) - 1;
  206.         else
  207.             FORWDOT(curbp).o = fc;
  208.     } else {
  209.         FORWDOT(curbp).l = next;
  210.         fc =  firstchar(next);
  211.         if (fc < 0) /* all white */
  212.             FORWDOT(curbp).o = b_left_margin(curbp);
  213.         else
  214.             FORWDOT(curbp).o = fc;
  215.     }
  216.  
  217.     dumpuline(lp);
  218. }
  219.  
  220. /*
  221.  * Push a copy of a line onto the undo stack.  Push a patch so we can
  222.  * later fix up any references to this line that might already be in the
  223.  * stack.  When the undo happens, the later pops (i.e. those lines still
  224.  * in the stack) will point at the _original_ (which will by then be on the
  225.  * redo stack) instead of at the copy, which will have just been popped,
  226.  * unless we fix them by popping and using the patch.
  227.  */
  228.  
  229. void
  230. copy_for_undo(LINEPTR lp)
  231. {
  232.     register LINEPTR nlp;
  233.  
  234.     if (!OkUndo())
  235.         return;
  236.  
  237.     if (needundocleanup)
  238.         preundocleanup();
  239.  
  240.     if (liscopied(lp))
  241.         return;
  242.  
  243.     /* take care of the normal undo stack */
  244.     nlp = copyline(lp);
  245.     if (nlp == null_ptr)
  246.         return;
  247.  
  248.     pushline(nlp, BACKSTK(curbp));
  249.  
  250.     make_undo_patch(lp, nlp);
  251.  
  252.     lsetcopied(lp);
  253.  
  254.     setupuline(lp);
  255.  
  256.     FORWDOT(curbp).l = lp;
  257.     FORWDOT(curbp).o = DOT.o;
  258. }
  259.  
  260. /* push an unreal line onto the undo stack
  261.  * lp should be the new line, _after_ insertion, so
  262.  *    lforw() and lback() are right
  263.  */
  264. void
  265. tag_for_undo(LINEPTR lp)
  266. {
  267.     register LINEPTR nlp;
  268.  
  269.     if (!OkUndo())
  270.         return;
  271.  
  272.     if (needundocleanup)
  273.         preundocleanup();
  274.  
  275.     if (liscopied(lp))
  276.         return;
  277.  
  278.     nlp = lalloc(LINENOTREAL, curbp);
  279.     if (nlp == null_ptr)
  280.         return;
  281.     set_lforw(nlp, lforw(lp));
  282.     set_lback(nlp, lback(lp));
  283.  
  284.     pushline(nlp, BACKSTK(curbp));
  285.  
  286.     lsetcopied(lp);
  287.     FORWDOT(curbp).l = lp;
  288.     FORWDOT(curbp).o = DOT.o;
  289. }
  290.  
  291. /* Change all PURESTACKSEPS on the stacks to STACKSEPS, so that undo won't
  292.  * reset the BFCHG bit.  This should be called anytime a non-undable change is
  293.  * made to a buffer.
  294.  */
  295. void
  296. nounmodifiable(BUFFER *bp)
  297. {
  298.     register LINE *tlp;
  299.     for (tlp = *BACKSTK(bp); tlp != NULL;
  300.                 tlp = tlp->l_nxtundo) {
  301.         if (lispurestacksep(tlp))
  302.             tlp->l_used = STACKSEP;
  303.     }
  304.     for (tlp = *FORWSTK(bp); tlp != NULL;
  305.                 tlp = tlp->l_nxtundo) {
  306.         if (lispurestacksep(tlp))
  307.             tlp->l_used = STACKSEP;
  308.     }
  309. }
  310.  
  311. /* before any undoable command (except undo itself), clean the undo list */
  312. /* clean the copied flag on the line we're the copy of */
  313. void
  314. freeundostacks(register BUFFER *bp, int both)
  315. {
  316.     register LINE *lp;
  317.  
  318.     while ((lp = popline(FORWSTK(bp),TRUE)) != NULL) {
  319.         lfree(lp,bp);
  320.     }
  321.     if (both) {
  322.         while ((lp = popline(BACKSTK(bp),TRUE)) != NULL)
  323.             lfree(lp,bp);
  324.         bp->b_udtail = null_ptr;
  325.         bp->b_udlastsep = null_ptr;
  326.         bp->b_udcount = 0;
  327.     }
  328.  
  329. }
  330.  
  331. /* ARGSUSED */
  332. int
  333. undo(int f GCC_UNUSED, int n GCC_UNUSED)
  334. {
  335.     int s;
  336.     L_NUM    before;
  337.  
  338.     if (b_val(curbp, MDVIEW))
  339.         return(rdonly());
  340.  
  341.     before = line_count(curbp);
  342.     if ((s = undoworker(curbp->b_udstkindx)) == TRUE) {
  343.         if (!line_report(before)) {
  344.             mlwrite("[change %sdone]",
  345.                 curbp->b_udstkindx == BACK ? "un" : "re");
  346.         }
  347.         curbp->b_udstkindx ^= 1;  /* flip to other stack */
  348.     } else {
  349.         mlwarn("[No changes to undo]");
  350.     }
  351.     return s;
  352. }
  353.  
  354. int
  355. inf_undo(int f, int n)
  356. {
  357.     int s = TRUE;
  358.  
  359.     if (!f || n < 1) n = 1;
  360.  
  361.     if (b_val(curbp, MDVIEW))
  362.         return(rdonly());
  363.  
  364.     curbp->b_udstkindx ^= 1;  /* flip to other stack */
  365.     while (s && n--) {
  366.         if ((s = undoworker(curbp->b_udstkindx)) == TRUE) {
  367.             mlwrite("[change %sdone]",
  368.                 curbp->b_udstkindx == BACK ? "un" : "re");
  369.         } else {
  370.             mlwarn("[No more changes to %s]",
  371.                 curbp->b_udstkindx == BACK ? "undo" : "redo");
  372.         }
  373.     }
  374.     curbp->b_udstkindx ^= 1;  /* flip to other stack */
  375.     return s;
  376. }
  377.  
  378. int
  379. backundo(int f, int n)
  380. {
  381.     int s = TRUE;
  382.  
  383.     if (b_val(curbp, MDVIEW))
  384.         return(rdonly());
  385.  
  386.     if (!f || n < 1) n = 1;
  387.  
  388.     while (s && n--) {
  389.         s = undoworker(BACK);
  390.         if (s) {
  391.             mlwrite("[change undone]");
  392.         } else {
  393.             mlwarn("[No more changes to undo]");
  394.         }
  395.     }
  396.  
  397.     curbp->b_udstkindx = FORW;  /* flip to other stack */
  398.  
  399.     return s;
  400. }
  401.  
  402. int
  403. forwredo(int f, int n)
  404. {
  405.     int s = TRUE;
  406.  
  407.     if (b_val(curbp, MDVIEW))
  408.         return(rdonly());
  409.  
  410.     if (!f || n < 1) n = 1;
  411.  
  412.     while (s && n--) {
  413.         s = undoworker(FORW);
  414.         if (s) {
  415.             mlwrite("[change redone]");
  416.         } else {
  417.             mlwarn("[No more changes to redo]");
  418.         }
  419.     }
  420.  
  421.     curbp->b_udstkindx = BACK;  /* flip to other stack */
  422.  
  423.     return s;
  424. }
  425.  
  426. void
  427. mayneedundo(void)
  428. {
  429.     needundocleanup = TRUE;
  430. }
  431.  
  432. static void
  433. preundocleanup(void)
  434. {
  435.     register LINE *lp;
  436.  
  437.     freeundostacks(curbp,FALSE);
  438.  
  439.     /* clear the flags in the buffer */
  440.     /* there may be a way to clean these less drastically, by
  441.         using the information on the stacks above, but I
  442.         couldn't figure it out.  -pgf  */
  443.     if (++current_undo_cookie == 0) {
  444.         current_undo_cookie++;    /* never let it be zero */
  445.         for_each_line(lp, curbp) {  /* once in while, feel the pain */
  446.             lsetnotcopied(lp);
  447.         }
  448.     }
  449.  
  450.     curbp->b_udstkindx = BACK;
  451.  
  452.     if (doingopcmd)
  453.         BACKDOT(curbp) = pre_op_dot;
  454.     else
  455.         BACKDOT(curbp) = DOT;
  456.  
  457.     /* be sure FORWDOT has _some_ value (may be null the first time)
  458.     if (sameline(FORWDOT(curbp), nullmark))
  459.         FORWDOT(curbp) = BACKDOT(curbp);
  460.     */
  461.     freshstack(BACK);
  462.     FORWDOT(curbp) = BACKDOT(curbp);
  463.  
  464.     needundocleanup = FALSE;
  465. }
  466.  
  467. static void
  468. pushline(LINEPTR lp, LINEPTR *stk)
  469. {
  470.     lp->l_nxtundo = *stk;
  471.     *stk = lp;
  472.     undolog("pushing", lp);
  473. }
  474.  
  475. /* get a line from the specified stack.  unless force'ing, don't
  476.     go past a false bottom stack-separator */
  477. static LINE *
  478. popline(LINEPTR *stkp, int force)
  479. {
  480.     LINE *lp;
  481.  
  482.     lp = *stkp;
  483.  
  484.     if (lp == NULL || (!force && lisstacksep(lp))) {
  485.         undolog("popping null",lp);
  486.         return NULL;
  487.     }
  488.  
  489.     *stkp = lp->l_nxtundo;
  490.     lp->l_nxtundo = null_ptr;
  491.     undolog("popped", lp);
  492.     return (lp);
  493. }
  494.  
  495. static LINE *
  496. peekline(LINEPTR *stkp)
  497. {
  498.     return *stkp;
  499. }
  500.  
  501. static void
  502. freshstack(int stkindx)
  503. {
  504.     register LINEPTR plp;
  505.     /* push on a stack delimiter, so we know where this undo ends */
  506.     if (b_is_changed(curbp)) {
  507.         plp = lalloc(STACKSEP, curbp);
  508.     } else { /* if the buffer is unmodified, use special separator */
  509.         plp = lalloc(PURESTACKSEP, curbp);
  510.  
  511.         /* and make sure there are no _other_ special separators */
  512.         nounmodifiable(curbp);
  513.     }
  514.     if (plp == null_ptr)
  515.         return;
  516.     set_lback(plp, BACKDOT(curbp).l);
  517.     plp->l_back_offs = BACKDOT(curbp).o;
  518.     set_lforw(plp, FORWDOT(curbp).l);
  519.     plp->l_forw_offs = FORWDOT(curbp).o;
  520.     pushline(plp, STACK(stkindx));
  521.     if (stkindx == BACK) {
  522.         plp->l_nextsep = null_ptr;
  523.         if (curbp->b_udtail == null_ptr) {
  524.             if (curbp->b_udcount != 0) {
  525.                 mlwrite("BUG: null tail with non-0 undo count");
  526.                 curbp->b_udcount = 0;
  527.             }
  528.             curbp->b_udtail = plp;
  529.             curbp->b_udlastsep = plp;
  530.         } else {
  531.             if (curbp->b_udlastsep == null_ptr) {
  532.                 /* then we need to find lastsep */
  533.                 int i;
  534.                 curbp->b_udlastsep = curbp->b_udtail;
  535.                 for (i = curbp->b_udcount-1; i > 0; i-- ) {
  536.                     curbp->b_udlastsep =
  537.                      curbp->b_udlastsep->l_nextsep;
  538.                 }
  539.             }
  540.             curbp->b_udlastsep->l_nextsep = plp;
  541.             curbp->b_udlastsep = plp;
  542.         }
  543.         /* enforce stack growth limit */
  544.         curbp->b_udcount++;
  545.         /* dbgwrite("bumped undocount %d", curbp->b_udcount); */
  546.         if ( b_val(curbp, VAL_UNDOLIM) != 0 &&
  547.                 curbp->b_udcount > b_val(curbp, VAL_UNDOLIM) ) {
  548.             LINEPTR newtail;
  549.             LINEPTR lp;
  550.  
  551.             newtail = curbp->b_udtail;
  552.             while (curbp->b_udcount > b_val(curbp, VAL_UNDOLIM)) {
  553.                 newtail = newtail->l_nextsep;
  554.                 curbp->b_udcount--;
  555.             }
  556.  
  557.             curbp->b_udtail = newtail;
  558.             newtail = newtail->l_nxtundo;
  559.             if (newtail != null_ptr) {
  560.                 do {
  561.                     lp = newtail;
  562.                     if (newtail == curbp->b_udlastsep)
  563.                         mlwrite("BUG: tail passed lastsep");
  564.                     newtail = newtail->l_nxtundo;
  565.                     lfree(lp,curbp);
  566.                 } while (newtail != null_ptr);
  567.             }
  568.             curbp->b_udtail->l_nxtundo = null_ptr;
  569.  
  570.         }
  571.     }
  572. }
  573.  
  574. static void
  575. make_undo_patch(LINEPTR olp, LINEPTR nlp)
  576. {
  577.     register LINEPTR plp;
  578.     /* push on a tag that matches up the copy with the original */
  579.     plp = lalloc(LINEUNDOPATCH, curbp);
  580.     if (plp == null_ptr)
  581.         return;
  582.     set_lforw(plp, olp);    /* lforw() is the original line */
  583.     set_lback(plp, nlp);    /* lback() is the copy */
  584.     pushline(plp, BACKSTK(curbp));
  585. }
  586.  
  587. static void
  588. applypatch(LINEPTR newlp, LINEPTR oldlp)
  589. {
  590.     register LINE *tlp;
  591.     for (tlp = *BACKSTK(curbp); tlp != NULL;
  592.                     tlp = tlp->l_nxtundo) {
  593.         if (!lispatch(tlp)) {
  594.             if (lforw(tlp) == oldlp)
  595.                 set_lforw(tlp, newlp);
  596.             if (lback(tlp) == oldlp)
  597.                 set_lback(tlp, newlp);
  598.         } else { /* it's a patch */
  599.             if (lforw(tlp) == oldlp) {
  600.                 set_lforw(tlp, newlp);
  601.             }
  602.             if (lback(tlp) == oldlp) {
  603.                 mlforce("BUG? copy is an old line");
  604.                 break;
  605.             }
  606.         }
  607.     }
  608. }
  609.  
  610. static LINEPTR
  611. copyline(register LINE *lp)
  612. {
  613.     register LINE *nlp;
  614.  
  615.     nlp = lalloc(lp->l_used,curbp);
  616.     if (nlp == NULL)
  617.         return null_ptr;
  618.     /* copy the text and forward and back pointers.  everything else
  619.         matches already */
  620.     set_lforw(nlp, lforw(lp));
  621.     set_lback(nlp, lback(lp));
  622.     /* copy the rest */
  623.     if (lp->l_text && nlp->l_text)
  624.         (void)memcpy(nlp->l_text, lp->l_text, (SIZE_T)lp->l_used);
  625.     return nlp;
  626. }
  627.  
  628. static int
  629. undoworker(int stkindx)
  630. {
  631.     register LINEPTR lp;
  632.     register LINEPTR alp;
  633.     int nopops = TRUE;
  634.  
  635.     while ((lp = popline(STACK(stkindx), FALSE)) != 0) {
  636.         if (nopops)  /* first pop -- establish a new stack base */
  637.             freshstack(1^stkindx);
  638.         nopops = FALSE;
  639.         if (lislinepatch(lp)) {
  640.             applypatch(lback(lp), lforw(lp));
  641.             lfree(lp,curbp);
  642.             continue;
  643.         }
  644.         if (lforw(lback(lp)) != lforw(lp)) { /* theres something there */
  645.             if (lforw(lforw(lback(lp))) == lforw(lp)) {
  646.                 /* then there is exactly one line there */
  647.                 /* alp is the line to remove */
  648.                 /* lp is the line we're putting in */
  649.                 alp = lforw(lback(lp));
  650.                 repointstuff(lp,alp);
  651.                 /* remove it */
  652.                 set_lforw(lback(lp), lforw(alp));
  653.                 set_lback(lforw(alp), lback(alp));
  654.             } else { /* there is more than one line there */
  655.                 mlforce("BUG: no stacked line for an insert");
  656.                 /* cleanup ? naw, a bugs a bug */
  657.                 return(FALSE);
  658.             }
  659.         } else { /* there is no line where we're going */
  660.             /* create an "unreal" tag line to push */
  661.             alp = lalloc(LINENOTREAL, curbp);
  662.             if (alp == null_ptr)
  663.                 return(FALSE);
  664.             set_lforw(alp, lforw(lp));
  665.             set_lback(alp, lback(lp));
  666.         }
  667.  
  668.         /* insert real lines into the buffer
  669.             throw away the markers */
  670.         if (lisreal(lp)) {
  671.             set_lforw(lback(lp), lp);
  672.             set_lback(lforw(lp), lp);
  673.         } else {
  674.             lfree(lp, curbp);
  675.         }
  676.  
  677.         pushline(alp, OTHERSTACK(stkindx));
  678.     }
  679.  
  680.     if (nopops) {
  681.         if (stkindx == BACK && curbp->b_udcount != 0) {
  682.             mlwrite("BUG: nopop, non-0 undo count");
  683.         }
  684.         return (FALSE);
  685.     }
  686.  
  687. #define bug_checks 1
  688. #ifdef bug_checks
  689.     if ((lp = peekline(STACK(stkindx))) == 0) {
  690.         mlforce("BUG: found null after undo/redo");
  691.         return FALSE;
  692.     }
  693.  
  694.     if (!lisstacksep(lp)) {
  695.         mlforce("BUG: found non-sep after undo/redo");
  696.         return FALSE;
  697.     }
  698. #endif
  699.  
  700.     lp = popline(STACK(stkindx),TRUE);
  701.     FORWDOT(curbp).l = lforw(lp);
  702.     FORWDOT(curbp).o = lp->l_forw_offs;
  703.     BACKDOT(curbp).l = lback(lp);
  704.     BACKDOT(curbp).o = lp->l_back_offs;
  705.     if (stkindx == FORW) {
  706.         /* if we moved, update the "last dot" mark */
  707.         if (!sameline(DOT, FORWDOT(curbp)))
  708.             curwp->w_lastdot = DOT;
  709.         DOT = FORWDOT(curbp);
  710.     } else {
  711.         /* if we moved, update the "last dot" mark */
  712.         if (!sameline(DOT, BACKDOT(curbp)))
  713.             curwp->w_lastdot = DOT;
  714.         DOT = BACKDOT(curbp);
  715.         /* dbgwrite("about to decr undocount %d", curbp->b_udcount); */
  716.         curbp->b_udcount--;
  717.         curbp->b_udlastsep = null_ptr;  /* it's only a hint */
  718.         if (curbp->b_udtail == lp) {
  719.             if (curbp->b_udcount != 0) {
  720.                 mlwrite("BUG: popped tail; non-0 undo count");
  721.                 curbp->b_udcount = 0;
  722.             }
  723.             /* dbgwrite("clearing tail 0x%x and lastsep 0x%x", curbp->b_udtail,
  724.                         curbp->b_udlastsep); */
  725.             curbp->b_udtail = null_ptr;
  726.             curbp->b_udlastsep = null_ptr;
  727.         }
  728.     }
  729.  
  730.     b_clr_counted(curbp);    /* don't know the size! */
  731.     if (lispurestacksep(lp))
  732.         unchg_buff(curbp, 0);
  733.     else
  734.         chg_buff(curbp, WFHARD|WFINS|WFKILLS);
  735.  
  736.     lfree(lp,curbp);
  737.  
  738.     return TRUE;
  739. }
  740.  
  741. static void
  742. setupuline(LINEPTR lp)
  743. {
  744.     register LINE *  ulp;
  745.     /* take care of the U line */
  746.     if ((ulp = curbp->b_ulinep) == NULL
  747.      || (ulp->l_nxtundo != lp)) {
  748.         if (ulp != NULL)
  749.             lfree(curbp->b_ulinep, curbp);
  750.         ulp = curbp->b_ulinep = copyline(lp);
  751.         if (ulp != NULL)
  752.             ulp->l_nxtundo = lp;
  753.     }
  754. }
  755.  
  756. void
  757. dumpuline(LINEPTR lp)
  758. {
  759.     register LINE *ulp = curbp->b_ulinep;
  760.  
  761.     if ((ulp != NULL) && (ulp->l_nxtundo == lp)) {
  762.         lfree(curbp->b_ulinep, curbp);
  763.         curbp->b_ulinep = null_ptr;
  764.     }
  765. }
  766.  
  767. /* ARGSUSED */
  768. int
  769. lineundo(int f GCC_UNUSED, int n GCC_UNUSED)
  770. {
  771.     register LINE *ulp;    /* the Undo line */
  772.     register LINE *lp;    /* the line we may replace */
  773.     register WINDOW *wp;
  774.     register char *ntext;
  775.  
  776.     ulp = curbp->b_ulinep;
  777.     if (ulp == NULL) {
  778.         kbd_alarm();
  779.         return FALSE;
  780.     }
  781.  
  782.     lp = ulp->l_nxtundo;
  783.  
  784.     if (lforw(ulp) != lforw(lp) ||
  785.         lback(ulp) != lback(lp)) {
  786.             /* then the last change affected more than one line,
  787.             and we can't use the saved U-line */
  788.         dumpuline(curbp->b_ulinep);
  789.         kbd_alarm();
  790.         return FALSE;
  791.     }
  792.  
  793.     /* avoid losing our undo stacks needlessly */
  794.     if (linesmatch(ulp,lp) == TRUE)
  795.         return TRUE;
  796.  
  797.     DOT.l = lp;
  798.  
  799.     preundocleanup();
  800.  
  801.     ntext = NULL;
  802.     if (ulp->l_size && (ntext = typeallocn(char,ulp->l_size)) == NULL)
  803.         return (FALSE);
  804.  
  805.     copy_for_undo(lp);
  806.  
  807.     if (ntext && lp->l_text) {
  808.         (void)memcpy(ntext, ulp->l_text, (SIZE_T)llength(ulp));
  809.         ltextfree(lp,curbp);
  810.     }
  811.  
  812.     lp->l_text = ntext;
  813.     lp->l_used = ulp->l_used;
  814.     lp->l_size = ulp->l_size;
  815.  
  816. #if ! WINMARK
  817.     if (MK.l == lp)
  818.         MK.o = b_left_margin(curbp);
  819. #endif
  820.     /* let's be defensive about this */
  821.     for_each_window(wp) {
  822.         if (wp->w_dot.l == lp)
  823.             wp->w_dot.o = b_left_margin(curbp);
  824. #if WINMARK
  825.         if (wp->w_mark.l == lp)
  826.             wp->w_mark.o = b_left_margin(curbp);
  827. #endif
  828.         if (wp->w_lastdot.l == lp)
  829.             wp->w_lastdot.o = b_left_margin(curbp);
  830.     }
  831.     do_mark_iterate(mp,
  832.             if (mp->l == lp)
  833.                 mp->o = b_left_margin(curbp);
  834.     );
  835.  
  836.     chg_buff(curbp, WFEDIT|WFKILLS|WFINS);
  837.  
  838.     return TRUE;
  839.  
  840. }
  841.  
  842.  
  843. #define _min(a,b) ((a) < (b)) ? (a) : (b)
  844.  
  845. static void
  846. repointstuff(register LINEPTR nlp, register LINEPTR olp)
  847. {
  848.     register WINDOW *wp;
  849.     int    usenew = lisreal(nlp);
  850.     register LINEPTR point;
  851.     register LINE *  ulp;
  852.  
  853.     point = usenew ? nlp : lforw(olp);
  854. #if ! WINMARK
  855.     if (MK.l == olp) {
  856.         MK.l = point;
  857.         MK.o = _min(MK.o, llength(point));
  858.     }
  859. #endif
  860.     /* fix anything important that points to it */
  861.     for_each_window(wp) {
  862.         if (wp->w_dot.l == olp) {
  863.             wp->w_dot.l = point;
  864.             wp->w_dot.o = _min(wp->w_dot.o, llength(point));
  865.         }
  866.         if (wp->w_line.l == olp)
  867.             wp->w_line.l = point;
  868. #if WINMARK
  869.         if (wp->w_mark.l == olp) {
  870.             wp->w_mark.l = point;
  871.             wp->w_mark.o = _min(wp->w_mark.o, llength(point));
  872.         }
  873. #endif
  874.         if (wp->w_lastdot.l == olp) {
  875.             wp->w_lastdot.l = point;
  876.             wp->w_lastdot.o = _min(wp->w_lastdot.o, llength(point));
  877.         }
  878.     }
  879.     do_mark_iterate(mp,
  880.             if (mp->l == olp) {
  881.                     mp->l = point;
  882.                     mp->o = _min(mp->o, llength(point));
  883.             }
  884.     );
  885.  
  886.     /* reset the uline */
  887.     if ((ulp = curbp->b_ulinep) != NULL
  888.      && (ulp->l_nxtundo == olp)) {
  889.         if (usenew) {
  890.             ulp->l_nxtundo = point;
  891.         } else {
  892.             /* we lose the ability to undo all changes
  893.                 to this line, since it's going away */
  894.             curbp->b_ulinep = null_ptr;
  895.         }
  896.     }
  897.  
  898. }
  899.  
  900.  
  901. static int
  902. linesmatch(register LINE *lp1, register LINE *lp2)
  903. {
  904.     if (llength(lp1) != llength(lp2))
  905.         return FALSE;
  906.     if (llength(lp1) == 0)
  907.         return TRUE;
  908.     return !memcmp(lp1->l_text, lp2->l_text, (SIZE_T)llength(lp1));
  909. }
  910.  
  911.