home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / VILE327.ZIP / VILE327.TAR / vile3.27 / undo.c < prev    next >
C/C++ Source or Header  |  1992-12-14  |  13KB  |  640 lines

  1.  
  2. /* these routines take care of undo operations
  3.  * code by Paul Fox, original algorithm mostly by Julia Harper May, 89
  4.  *
  5.  * $Log: undo.c,v $
  6.  * Revision 1.20  1992/12/04  09:14:36  foxharp
  7.  * deleted unused assigns
  8.  *
  9.  * Revision 1.19  1992/11/30  23:07:03  foxharp
  10.  * firstchar/lastchar now return -1 for no non-white chars on line
  11.  *
  12.  * Revision 1.18  1992/07/28  22:02:55  foxharp
  13.  * took out crufty ifdefs and commented-out code.  reworded some commentary.
  14.  *
  15.  * Revision 1.17  1992/05/16  12:00:31  pgf
  16.  * prototypes/ansi/void-int stuff/microsoftC
  17.  *
  18.  * Revision 1.16  1992/01/05  00:06:13  pgf
  19.  * split mlwrite into mlwrite/mlprompt/mlforce to make errors visible more
  20.  * often.  also normalized message appearance somewhat.
  21.  *
  22.  * Revision 1.15  1991/11/08  13:06:42  pgf
  23.  * moved firstchar() to basic.c
  24.  *
  25.  * Revision 1.14  1991/11/02  19:40:06  pgf
  26.  * fixed bad free in lineundo()
  27.  *
  28.  * Revision 1.13  1991/11/01  14:38:00  pgf
  29.  * saber cleanup
  30.  *
  31.  * Revision 1.12  1991/10/08  01:30:00  pgf
  32.  * added new bp arg to lfree and lalloc
  33.  *
  34.  * Revision 1.11  1991/09/30  01:47:24  pgf
  35.  * satisfy gcc's objection to ' in ifdefs
  36.  *
  37.  * Revision 1.10  1991/09/10  13:04:34  pgf
  38.  * don't let DOT.o go negative after an undo
  39.  *
  40.  * Revision 1.9  1991/08/07  12:35:07  pgf
  41.  * added RCS log messages
  42.  *
  43.  * revision 1.8
  44.  * date: 1991/08/06 15:26:42;
  45.  * no undo on scratch buffers, and
  46.  * allow for null l_text pointers
  47.  * 
  48.  * revision 1.7
  49.  * date: 1991/07/23 11:11:42;
  50.  * undo is an absolute motion -- maintain "lastdot" properly
  51.  * 
  52.  * revision 1.6
  53.  * date: 1991/06/25 19:53:36;
  54.  * massive data structure restructure
  55.  * 
  56.  * revision 1.5
  57.  * date: 1991/06/03 10:27:18;
  58.  * took out #if INLINE code
  59.  * 
  60.  * revision 1.4
  61.  * date: 1991/04/04 09:44:09;
  62.  * undo line makes use of the separated line text
  63.  * 
  64.  * revision 1.3
  65.  * date: 1991/04/04 09:28:41;
  66.  * line text is now separate from LINE struct
  67.  * 
  68.  * revision 1.2
  69.  * date: 1991/03/26 17:01:36;
  70.  * preserve offset across undo
  71.  * 
  72.  * revision 1.1
  73.  * date: 1990/09/21 10:26:14;
  74.  * initial vile RCS revision
  75.  */
  76.  
  77. #include "estruct.h"
  78. #include "edef.h"
  79.  
  80. #ifndef NULL
  81. #define NULL 0
  82. #endif
  83.  
  84.  
  85. /* the undo strategy is this:
  86.     1) For any deleted line, push it onto the undo list.
  87.     2) On any change to a line, make a copy of it, push the copy to
  88.         the undo list, and mark the original as having been copied.
  89.         Do not copy/push lines that are marked as having been copied.
  90.         Push a tag matching up the copy with the original.  Later,
  91.         when the copy has been put into the file, we can
  92.         go back through the undo stack, find lines there pointing
  93.         at the original, and make them point at the copy.  ugh.
  94.         This wouldn't be necessary if we used line no's as the pointers,
  95.         instead of real pointers.
  96.  
  97.     On the actual undo, we pop these things one by one.  There should
  98.     either be no lines where it goes (it was deleted), or exactly
  99.     one line where it goes (it was changed/copied).  That makes it
  100.     easy to undo the changes one by one.  Of course, we need to build
  101.     a different, inverse stack as we go, so that undo can be undone.
  102.  
  103.     The "copied" flag in the LINE structure is unioned with the stack
  104.     link pointer on the undo stack, since they aren't both needed at once.
  105.  
  106. */
  107.  
  108.  
  109. #define CURSTK(bp) (&(bp->b_udstks[bp->b_udstkindx]))
  110. #define ALTSTK(bp) (&(bp->b_udstks[1^(bp->b_udstkindx)]))
  111.  
  112. #define CURDOT(bp) bp->b_uddot[bp->b_udstkindx]
  113. #define ALTDOT(bp) bp->b_uddot[1^(bp->b_udstkindx)]
  114. #define SWITCHSTKS(bp) (bp->b_udstkindx = 1 ^ bp->b_udstkindx)
  115.  
  116. short needundocleanup;
  117. LINE *copyline();
  118. void make_undo_patch();
  119.  
  120. /* push the line onto the right undo stack. */
  121. void
  122. toss_to_undo(lp)
  123. LINE *lp;
  124. {
  125.     if (curbp->b_flag & BFSCRTCH)
  126.         return;
  127.     if (needundocleanup)
  128.         preundocleanup();
  129.     pushline(lp,CURSTK(curbp));
  130.     if ((ALTDOT(curbp).l == NULL) || (ALTDOT(curbp).l == lp)) {
  131.         int fc;
  132.         /* need to save a dot -- either the next line or 
  133.             the previous one */
  134.         if (lp->l_fp == curbp->b_line.l) {
  135.             ALTDOT(curbp).l = lp->l_bp;
  136.             fc =  firstchar(lp->l_bp);
  137.             if (fc < 0) /* all white */
  138.                 ALTDOT(curbp).o = llength(lp->l_bp) - 1;
  139.             else
  140.                 ALTDOT(curbp).o = fc;
  141.         } else {
  142.             ALTDOT(curbp).l = lp->l_fp;
  143.             fc =  firstchar(lp->l_fp);
  144.             if (fc < 0) /* all white */
  145.                 ALTDOT(curbp).o = 0;
  146.             else
  147.                 ALTDOT(curbp).o = fc;
  148.         }
  149.     }
  150.     dumpuline(lp);
  151. }
  152.  
  153. /* 
  154.  * Push a copy of a line onto the right undo stack.  Push a patch so we can
  155.  * later fix up any references to this line that might already be in the
  156.  * stack.  When the undo happens, the later pops (i.e. those lines still
  157.  * in the stack) will point at the _original_ (which will by then be on the
  158.  * other undo stack) instead of the copy, which will have just been popped. 
  159.  * unless we fix them by when popping the patch.
  160.  */
  161.  
  162. int
  163. copy_for_undo(lp)
  164. LINE *lp;
  165. {
  166.     register LINE *nlp;
  167.  
  168.     if (curbp->b_flag & BFSCRTCH)
  169.         return TRUE;
  170.     if (needundocleanup)
  171.         preundocleanup();
  172.  
  173.     if (liscopied(lp))
  174.         return(TRUE);
  175.  
  176.     /* take care of the normal undo stack */
  177.     nlp = copyline(lp);
  178.     if (nlp == NULL)
  179.         return(FALSE);
  180.     pushline(nlp,CURSTK(curbp));
  181.  
  182.     make_undo_patch(lp,nlp,LINEUNDOPATCH);
  183.  
  184.     lsetcopied(lp);
  185.  
  186.     setupuline(lp);
  187.  
  188.     if (ALTDOT(curbp).l == NULL) {
  189.         ALTDOT(curbp).l = lp;
  190.         ALTDOT(curbp).o = curwp->w_dot.o;
  191.     }
  192.     return (TRUE);
  193. }
  194.  
  195. /* push an unreal line onto the right undo stack */
  196. /* lp should be the new line, _after_ insertion, so l_fp and l_bp are right */
  197. int
  198. tag_for_undo(lp)
  199. LINE *lp;
  200. {
  201.     register LINE *nlp;
  202.  
  203.     if (curbp->b_flag & BFSCRTCH)
  204.         return TRUE;
  205.     if (needundocleanup)
  206.         preundocleanup();
  207.  
  208.     if (liscopied(lp))
  209.         return(TRUE);
  210.  
  211.     nlp = lalloc(-1,curbp);
  212.     if (nlp == NULL)
  213.         return(FALSE);
  214.     llength(nlp) = LINENOTREAL;
  215.     nlp->l_fp = lp->l_fp;
  216.     nlp->l_bp = lp->l_bp;
  217.     pushline(nlp,CURSTK(curbp));
  218.     lsetcopied(lp);
  219.     if (ALTDOT(curbp).l == NULL) {
  220.             ALTDOT(curbp).l = lp;
  221.             ALTDOT(curbp).o = curwp->w_dot.o;
  222.     }
  223.     return (TRUE);
  224. }
  225.  
  226. void
  227. pushline(lp,stk)
  228. LINE *lp,**stk;
  229. {
  230.     lp->l_nxtundo = *stk;
  231.     *stk = lp;
  232. }
  233.  
  234. LINE *
  235. popline(stk)
  236. LINE **stk;
  237. {
  238.     LINE *lp;
  239.     lp = *stk;
  240.     if (lp != NULL) {
  241.         *stk = lp->l_nxtundo;
  242.         lp->l_nxtundo = NULL;
  243.     }
  244.     return (lp);
  245. }
  246.  
  247. void
  248. make_undo_patch(olp,nlp,type)
  249. LINE *olp,*nlp;
  250. int type;
  251. {
  252.     register LINE *plp;
  253.     /* push on a tag that matches up the copy with the original */
  254.     plp = lalloc(-1,curbp);
  255.     if (plp == NULL)
  256.         return;
  257.     llength(plp) = type;
  258.     plp->l_fp = olp;    /* l_fp is the original line */
  259.     plp->l_bp = nlp;    /* l_bp is the copy */
  260.     pushline(plp,CURSTK(curbp));
  261. }
  262.  
  263. void
  264. applypatch(newlp,oldlp)
  265. LINE *newlp, *oldlp;
  266. {
  267.     register LINE *tlp;
  268.     for (tlp = *CURSTK(curbp); tlp != NULL ; tlp = tlp->l_nxtundo) {
  269.         if (!lispatch(tlp)) {
  270.             if (tlp->l_fp == oldlp)
  271.                 tlp->l_fp = newlp;
  272.             if (tlp->l_bp == oldlp)
  273.                 tlp->l_bp = newlp;
  274.         }
  275.     }
  276. }
  277.  
  278. LINE *
  279. copyline(lp)
  280. register LINE *lp;
  281. {
  282.     register LINE *nlp;
  283.     
  284.     nlp = lalloc(lp->l_used,curbp);
  285.     if (nlp == NULL)
  286.         return(NULL);
  287.     /* copy the text and forward and back pointers.  everything else 
  288.         matches already */
  289.     nlp->l_fp = lp->l_fp;
  290.     nlp->l_bp = lp->l_bp;
  291.     /* copy the rest */
  292.     if (lp->l_text && nlp->l_text)
  293.         memcpy(nlp->l_text, lp->l_text, lp->l_used);
  294.     return nlp;
  295. }
  296.  
  297.  
  298. /* before any undoable command (except undo itself), clean the undo list */
  299. /* clean the copied flag on the line we're the copy of */
  300. void
  301. freeundostacks(bp)
  302. register BUFFER *bp;
  303. {
  304.     register LINE *lp;
  305.     int i;
  306.  
  307.     for (i = 0; i <= 1; i++, SWITCHSTKS(bp)) {
  308.         while ((lp = popline(CURSTK(bp))) != NULL) {
  309.             lfree(lp,bp);
  310.         }
  311.     }
  312.  
  313.     /* clear the flags in the buffer */
  314.     /* there may be a way to clean these less drastically, by
  315.         using the information on the stacks above, but I
  316.         couldn't figure it out.  -pgf  */
  317.     lp = lforw(bp->b_line.l);
  318.     while (lp != bp->b_line.l) {
  319.         lsetnotcopied(lp);
  320.         lp = lforw(lp);
  321.     }
  322.  
  323. }
  324.  
  325. /* ARGSUSED */
  326. int
  327. undo(f,n)
  328. int f,n;
  329. {
  330.     LINE *lp, *alp;
  331.     int nopops = TRUE;
  332.     
  333.     if (b_val(curbp, MDVIEW))
  334.         return(rdonly());
  335.  
  336.     while ((lp = popline(CURSTK(curbp))) != NULL) {
  337.         nopops = FALSE;
  338.         if (lislinepatch(lp)) {
  339.             applypatch(lp->l_bp, lp->l_fp);
  340.             lfree(lp,curbp);
  341.             continue;
  342.         }
  343.         lchange(WFHARD|WFINS|WFKILLS);
  344.         if (lp->l_bp->l_fp != lp->l_fp) { /* theres something there */
  345.             if (lp->l_bp->l_fp->l_fp == lp->l_fp) {
  346.                 /* then there is exactly one line there */
  347.                 /* alp is the line to remove */
  348.                 /* lp is the line we're putting in */
  349.                 alp = lp->l_bp->l_fp;
  350.                 repointstuff(lp,alp);
  351.                 /* remove it */
  352.                 lp->l_bp->l_fp = alp->l_fp;
  353.                 alp->l_fp->l_bp = alp->l_bp;
  354.             } else { /* there is more than one line there */
  355.                 mlforce("BUG: no stacked line for an insert");
  356.                 /* cleanup ? naw, a bugs a bug */
  357.                 return(FALSE);
  358.             }
  359.         } else { /* there is no line where we're going */
  360.             /* create an "unreal" tag line to push */
  361.             alp = lalloc(-1,curbp);
  362.             if (alp == NULL)
  363.                 return(FALSE);
  364.             llength(alp) = LINENOTREAL;
  365.             alp->l_fp = lp->l_fp;
  366.             alp->l_bp = lp->l_bp;
  367.         }
  368.  
  369.         /* insert real lines into the buffer 
  370.             throw away the markers */
  371.         if (lisreal(lp)) {
  372.             lp->l_bp->l_fp = lp;
  373.             lp->l_fp->l_bp = lp;
  374.         } else {
  375.             lfree(lp,curbp);
  376.         }
  377.  
  378.         pushline(alp,ALTSTK(curbp));
  379.     }
  380.     
  381.     if (nopops) {
  382.         TTbeep();
  383.         return (FALSE);
  384.     }
  385.  
  386.  
  387.     {
  388.         /* it's an absolute move -- remember where we are */
  389.         MARK odot;
  390.         odot = DOT;
  391.  
  392.         DOT = CURDOT(curbp);
  393.         DOT.o = firstchar(DOT.l);
  394.         if (DOT.o < 0) {
  395.             DOT.o = llength(DOT.l) - 1;
  396.             if (DOT.o < 0)
  397.                 DOT.o = 0;
  398.         }
  399.  
  400.         /* if we moved, update the "last dot" mark */
  401.         if (!sameline(DOT, odot)) {
  402.             curwp->w_lastdot = odot;
  403.         }
  404.     }
  405.  
  406.     SWITCHSTKS(curbp);
  407.     
  408.     vverify("undo");
  409.     
  410.     return TRUE;
  411. }
  412.  
  413. void
  414. mayneedundo()
  415. {
  416.     needundocleanup = TRUE;
  417. }
  418.  
  419. void
  420. preundocleanup()
  421. {
  422.     freeundostacks(curbp);
  423.     CURDOT(curbp) = curwp->w_dot;
  424.     ALTDOT(curbp) = nullmark;
  425.     needundocleanup = FALSE;
  426. }
  427.  
  428. /* ARGSUSED */
  429. int
  430. lineundo(f,n)
  431. int f,n;
  432. {
  433.     register LINE *ulp;    /* the Undo line */
  434.     register LINE *lp;    /* the line we may replace */
  435.     register WINDOW *wp;
  436.     register char *ntext;
  437.  
  438.     ulp = curbp->b_ulinep;
  439.     if (ulp == NULL) {
  440.         TTbeep();
  441.         return FALSE;
  442.     }
  443.  
  444.     lp = ulp->l_nxtundo;
  445.  
  446.     if (ulp->l_fp != lp->l_fp ||
  447.         ulp->l_bp != lp->l_bp) {
  448.             /* then the change affected more than one line */
  449.         dumpuline(ulp);
  450.         return FALSE;
  451.     }
  452.  
  453.     /* avoid losing our undo stacks needlessly */
  454.     if (linesmatch(ulp,lp) == TRUE) 
  455.         return TRUE;
  456.  
  457.     curwp->w_dot.l = lp;
  458.     preundocleanup();
  459.  
  460.     ntext = NULL;
  461.     if (ulp->l_size && (ntext = malloc(ulp->l_size)) == NULL)
  462.         return (FALSE);
  463.  
  464.     copy_for_undo(lp);
  465.  
  466.     if (ntext && lp->l_text) {
  467.         memcpy(ntext, ulp->l_text, llength(ulp));
  468.         ltextfree(lp,curbp);
  469.     }
  470.  
  471.     lp->l_text = ntext;
  472.     lp->l_used = ulp->l_used;
  473.     lp->l_size = ulp->l_size;
  474.  
  475. #if ! WINMARK
  476.     if (MK.l == lp)
  477.         MK.o = 0;
  478. #endif
  479.     /* let's be defensive about this */
  480.     wp = wheadp;
  481.     while (wp != NULL) {
  482.         if (wp->w_dot.l == lp)
  483.             wp->w_dot.o = 0;
  484. #if WINMARK
  485.         if (wp->w_mark.l == lp)
  486.             wp->w_mark.o = 0;
  487. #endif
  488.         if (wp->w_lastdot.l == lp)
  489.             wp->w_lastdot.o = 0;
  490.         wp = wp->w_wndp;
  491.     }
  492.     if (CURDOT(curbp).l == lp)
  493.         CURDOT(curbp).o = 0;
  494.     if (curbp->b_nmmarks != NULL) {
  495.         /* fix the named marks */
  496.         int i;
  497.         struct MARK *mp;
  498.         for (i = 0; i < 26; i++) {
  499.             mp = &(curbp->b_nmmarks[i]);
  500.             if (mp->l == lp)
  501.                 mp->o = 0;
  502.         }
  503.     }
  504.  
  505.     curwp->w_flag |= WFEDIT;
  506.     
  507.     vverify("lineundo");
  508.     return TRUE;
  509.  
  510. }
  511.  
  512. void
  513. repointstuff(nlp,olp)
  514. register LINE *nlp,*olp;
  515. {
  516.     register WINDOW *wp;
  517.  
  518.     if (DOT.l == olp) {
  519.         if (lisreal(nlp)) {
  520.             DOT.l = nlp;
  521.         } else {
  522.             DOT.l = olp->l_fp;
  523.         }
  524.         DOT.o = 0;
  525.     }
  526. #if ! WINMARK
  527.     if (MK.l == olp) {
  528.         if (lisreal(nlp)) {
  529.             MK.l = nlp;
  530.         } else {
  531.             MK.l = olp->l_fp;
  532.         }
  533.         MK.o = 0;
  534.     }
  535. #endif
  536.     /* fix anything important that points to it */
  537.     wp = wheadp;
  538.     while (wp != NULL) {
  539.         if (wp->w_line.l == olp)
  540.             if (lisreal(nlp)) {
  541.                 wp->w_line.l = nlp;
  542.             } else {
  543.                 wp->w_line.l = olp->l_fp;
  544.             }
  545. #if WINMARK
  546.         if (wp->w_mark.l == olp) {
  547.             if (lisreal(nlp)) {
  548.                 wp->w_mark.l = nlp;
  549.             } else {
  550.                 wp->w_mark.l = olp->l_fp;
  551.             }
  552.             wp->w_mark.o = 0;
  553.         }
  554. #endif
  555.         if (wp->w_lastdot.l == olp) {
  556.             if (lisreal(nlp)) {
  557.                 wp->w_lastdot.l = nlp;
  558.             } else {
  559.                 wp->w_lastdot.l = olp->l_fp;
  560.             }
  561.             wp->w_lastdot.o = 0;
  562.         }
  563.         wp = wp->w_wndp;
  564.     }
  565.     if (CURDOT(curbp).l == olp) {
  566.         if (lisreal(nlp)) {
  567.             CURDOT(curbp).l = nlp;
  568.         } else {
  569.             mlforce("BUG: preundodot points at newly inserted line!");
  570.         }
  571.     }
  572.     if (curbp->b_nmmarks != NULL) {
  573.         /* fix the named marks */
  574.         int i;
  575.         struct MARK *mp;
  576.         for (i = 0; i < 26; i++) {
  577.             mp = &(curbp->b_nmmarks[i]);
  578.             if (mp->l == olp) {
  579.                 if (lisreal(nlp)) {
  580.                     mp->l = nlp;
  581.                     mp->o = 0;
  582.                 } else {
  583.                     mlforce("[Lost mark]");
  584.                 }
  585.             }
  586.         }
  587.     }
  588.     resetuline(olp,nlp);
  589. }
  590.  
  591. int
  592. linesmatch(lp1,lp2)
  593. register LINE *lp1,*lp2;
  594. {
  595.     if (llength(lp1) != llength(lp2))
  596.         return FALSE;
  597.     if (llength(lp1) == 0)
  598.         return TRUE;
  599.     return !memcmp(lp1->l_text, lp2->l_text, llength(lp1));
  600. }
  601.  
  602. void
  603. dumpuline(lp)
  604. LINE *lp;
  605. {
  606.     if ((curbp->b_ulinep != NULL) &&
  607.             (curbp->b_ulinep->l_nxtundo == lp)) {
  608.         lfree(curbp->b_ulinep,curbp);
  609.         curbp->b_ulinep = NULL;
  610.     }
  611. }
  612.  
  613. void
  614. setupuline(lp)
  615. LINE *lp;
  616. {
  617.     /* take care of the U line */
  618.     if ((curbp->b_ulinep == NULL) || (curbp->b_ulinep->l_nxtundo != lp)) {
  619.         if (curbp->b_ulinep != NULL)
  620.             lfree(curbp->b_ulinep,curbp);
  621.         curbp->b_ulinep = copyline(lp);
  622.         if (curbp->b_ulinep != NULL)
  623.             curbp->b_ulinep->l_nxtundo = lp;
  624.     }
  625. }
  626.  
  627. void
  628. resetuline(olp,nlp)
  629. register LINE *olp,*nlp;
  630. {
  631.     if (curbp->b_ulinep != NULL && curbp->b_ulinep->l_nxtundo == olp) {
  632.         if (lisreal(nlp)) {
  633.             curbp->b_ulinep->l_nxtundo = nlp;
  634.         } else {
  635.             mlforce("BUG: b_ulinep pointed at inserted line!");
  636.         }
  637.     }
  638. }
  639.  
  640.