home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / x / xvisrc.zoo / undo.c < prev    next >
C/C++ Source or Header  |  1992-07-28  |  21KB  |  879 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)undo.c    2.2 (Chris & John Downey) 8/28/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     undo.c
  14. * module function:
  15.     Code to implement "undo" command.
  16.  
  17. * usage:
  18.     We provide several primitive functions for "do"ing things,
  19.     and an "undo" function to restore the previous state.
  20.  
  21.     Normally, a primitive function is simply called, and will
  22.     automatically throw away the old previous state before
  23.     saving the current one and then making the change.
  24.  
  25.     Alternatively, it is possible to bracket lots of changes
  26.     between calls to the start_command() and end_command()
  27.     functions; more global changes can then be effected,
  28.     and undo still works as it should.  This is used for the
  29.     "global" command, for multi-line substitutes, and for
  30.     insert mode.
  31.  
  32. * history:
  33.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  34.     Originally by Tim Thompson (twitch!tjt)
  35.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  36.     Heavily modified by Chris & John Downey
  37.  
  38. ***/
  39.  
  40. #include "xvi.h"
  41.  
  42. static    void    save_position P((Xviwin *));
  43. static    void    free_changes P((Change *));
  44. static    void    report P((Xviwin *));
  45.  
  46. /*
  47.  * This variable holds the total number of added/deleted lines
  48.  * for a change; it is used for reporting (the "report" parameter).
  49.  */
  50. static    long    total_lines;
  51.  
  52. void
  53. init_undo(buffer)
  54. Buffer    *buffer;
  55. {
  56.     /*
  57.      * Initialise the undo-related variables in the Buffer.
  58.      */
  59.     buffer->b_nlevels = 0;
  60.     buffer->b_change = NULL;
  61.  
  62.     /*
  63.      * Set line numbers.
  64.      */
  65.     buffer->b_line0->l_number = 0;
  66.     buffer->b_file->l_number = 1;
  67.     buffer->b_lastline->l_number = MAX_LINENO;
  68. }
  69.  
  70. /*
  71.  * Start a command. This routine may be called many times;
  72.  * what it does is increase the variable which shows how
  73.  * many times it has been called.  The end_command() then
  74.  * decrements the variable - so it is vital that calls
  75.  * of the two routines are matched.
  76.  *
  77.  * The effect of this is quite simple; if the nlevels variable
  78.  * is >0, the "do*" routines will not throw away the previous
  79.  * state before making a change; and thus we are able to undo
  80.  * multiple changes.
  81.  *
  82.  * All the do* routines, and start_command(), will throw away
  83.  * the previous saved state if the b_nlevels variable is 0.
  84.  */
  85. bool_t
  86. start_command(window)
  87. Xviwin    *window;
  88. {
  89.     Buffer    *buffer;
  90.  
  91.     buffer = window->w_buffer;
  92.  
  93.     if (buffer->b_nlevels == 0) {
  94.     if (not_editable(buffer)) {
  95.         show_error(window, "Edit not allowed!");
  96.         return(FALSE);
  97.     }
  98.     free_changes(buffer->b_change);
  99.     buffer->b_change = NULL;
  100.     }
  101.  
  102.     buffer->b_nlevels += 1;
  103.  
  104.     total_lines = 0;
  105.  
  106.     save_position(window);
  107.  
  108.     return(TRUE);
  109. }
  110.  
  111. /*
  112.  * Save the cursor position.
  113.  *
  114.  * This is called at the start of each change, so that
  115.  * the cursor will return to the right place after an undo.
  116.  */
  117. static void
  118. save_position(window)
  119. Xviwin    *window;
  120. {
  121.     Buffer    *buffer;
  122.     Change    *change;
  123.  
  124.     buffer = window->w_buffer;
  125.  
  126.     change = challoc();
  127.     if (change == NULL)
  128.     return;
  129.  
  130.     change->c_type = C_POSITION;
  131.     change->c_pline = lineno(buffer, window->w_cursor->p_line);
  132.     change->c_pindex = window->w_cursor->p_index;
  133.  
  134.     change->c_next = buffer->b_change;
  135.     buffer->b_change = change;
  136. }
  137.  
  138. void
  139. end_command(window)
  140. Xviwin    *window;
  141. {
  142.     Buffer    *buffer;
  143.  
  144.     buffer = window->w_buffer;
  145.  
  146.     if (buffer->b_nlevels > 0) {
  147.     buffer->b_nlevels -= 1;
  148.     if (buffer->b_nlevels == 0) {
  149.         report(window);
  150.     }
  151.     } else {
  152.     show_error(window, "Internal error: too many \"end_command\"s");
  153.     }
  154. }
  155.  
  156. /*
  157.  * Replace the given section of the given line with the
  158.  * new (null-terminated) string. nchars may be zero for
  159.  * insertions of text; newstring may point to a 0-length
  160.  * string to delete text. start is the first character
  161.  * of the section which is to be replaced.
  162.  *
  163.  * Note that we don't call strcpy() to copy text here, for
  164.  * two reasons: firstly, we are likely to be copying small
  165.  * numbers of characters and it is therefore faster to do
  166.  * the copying ourselves, and secondly, strcpy() doesn't
  167.  * necessarily work for copying overlapping text towards
  168.  * higher locations in memory.
  169.  */
  170. void
  171. replchars(window, line, start, nchars, newstring)
  172. Xviwin    *window;
  173. Line    *line;
  174. int    start;
  175. int    nchars;
  176. char    *newstring;
  177. {
  178.     register char    *from;        /* where to copy from */
  179.     register char    *to;        /* where to copy to */
  180.     register int    nlen;        /* length of newstring */
  181.     register int    olen;        /* length of old line */
  182.     register int    offset;        /* how much to move text by */
  183.     Buffer        *buffer;
  184.     Change        *change;
  185.  
  186.     buffer = window->w_buffer;
  187.  
  188.     /*
  189.      * If this is a singleton command, make sure we
  190.      * destroy the changes made as part of the last
  191.      * command before we start the new one.
  192.      */
  193.     if (buffer->b_nlevels == 0) {
  194.     if (not_editable(buffer)) {
  195.         show_error(window, "Edit not allowed!");
  196.         return;
  197.     }
  198.     free_changes(buffer->b_change);
  199.     buffer->b_change = NULL;
  200.     save_position(window);
  201.     }
  202.  
  203.     /*
  204.      * First thing we have to do is to obtain a change
  205.      * structure to record the change so we can be sure
  206.      * that we can undo it. If this fails, we must not
  207.      * make any change at all.
  208.      */
  209.     change = challoc();
  210.     if (change == NULL)
  211.     return;
  212.  
  213.     nlen = strlen(newstring);
  214.     olen = strlen(line->l_text + start);
  215.     if (olen < nchars)
  216.     nchars = olen;
  217.     offset = nlen - nchars;
  218.  
  219.     /*
  220.      * Record the opposite change so we can undo it.
  221.      */
  222.     if (nchars == 0) {
  223.     change->c_type = C_DEL_CHAR;
  224.     } else {
  225.     change->c_type = C_CHAR;
  226.     change->c_chars = alloc((unsigned) nchars + 1);
  227.     if (change->c_chars == NULL) {
  228.         chfree(change);
  229.         State = NORMAL;
  230.         return;
  231.     }
  232.     (void) strncpy(change->c_chars, line->l_text + start, nchars);
  233.     change->c_chars[nchars] = '\0';
  234.     }
  235.     change->c_lineno = lineno(buffer, line);
  236.     change->c_index = start;
  237.     change->c_nchars = nlen;
  238.  
  239.     if (offset > 0) {
  240.     register char    *s;
  241.  
  242.     /*
  243.      * Move existing text along by offset to the right.
  244.      * First make some room in the line.
  245.      */
  246.     if (grow_line(line, offset) == FALSE) {
  247.         free(change->c_chars);
  248.         chfree(change);
  249.         State = NORMAL;
  250.         return;
  251.     }
  252.  
  253.     /*
  254.      * Copy characters backwards, i.e. start
  255.      * at the end of the line rather than at
  256.      * the start.
  257.      */
  258.     from = line->l_text + start;
  259.     to = from + offset + olen + 1;
  260.     s = from + olen + 1;
  261.     while (s > from) {
  262.         *--to = *--s;
  263.     }
  264.  
  265.     } else if (offset < 0) {
  266.  
  267.     /*
  268.      * Move existing text along to the left.
  269.      */
  270.     offset = - offset;
  271.     to = line->l_text + start;
  272.     from = to + offset;
  273.  
  274.     /*
  275.      * Do classic K&R strcpy().
  276.      */
  277.     while ((*to++ = *from++) != '\0') {
  278.         ;
  279.     }
  280.     }
  281.  
  282.     /*
  283.      * Finally, copy the new text into position.
  284.      * Note that we are careful not to copy the
  285.      * null terminator.
  286.      */
  287.     from = newstring;
  288.     to = line->l_text + start;
  289.     while (*from != '\0') {
  290.     *to++ = *from++;
  291.     }
  292.  
  293.     buffer->b_flags |= FL_MODIFIED;
  294.  
  295.     window->w_curs_new = TRUE;
  296.     
  297.     /*
  298.      * Push this change onto the LIFO of changes
  299.      * that form the current command.
  300.      */
  301.     change->c_next = buffer->b_change;
  302.     buffer->b_change = change;
  303. }
  304.  
  305. /*
  306.  * Replace the specified set of lines with the replacement set.
  307.  * The number of lines to be replaced may be 0; the replacement
  308.  * list may be a NULL pointer.
  309.  */
  310. void
  311. repllines(window, line, nolines, newlines)
  312. register Xviwin    *window;
  313. Line        *line;
  314. long        nolines;
  315. Line        *newlines;
  316. {
  317.     register Buffer    *buffer;    /* buffer window is mapped onto */
  318.     Line        *firstp;    /* line before first to delete */
  319.     Line        *lastp;        /* line after last to delete */
  320.     Line        *lastline;    /* last line to delete */
  321.     Line        *new_start;    /* start of lines to be inserted */
  322.     Line        *new_end;    /* last line to be inserted */
  323.     long        nnlines;    /* no. new logical lines */
  324.     long        oplines;    /* no. physical lines to be replaced */
  325.     long        nplines;    /* no. new physical lines */
  326.     long        n;        /* lines done so far */
  327.     register Xviwin    *wp;        /* loop variable */
  328.     Change        *change;
  329.  
  330.     buffer = window->w_buffer;
  331.  
  332.     /*
  333.      * If this is a singleton command, make sure we
  334.      * destroy the changes made as part of the last
  335.      * command before we start the new one.
  336.      */
  337.     if (buffer->b_nlevels == 0) {
  338.     if (not_editable(buffer)) {
  339.         show_error(window, "Edit not allowed!");
  340.         return;
  341.     }
  342.     free_changes(buffer->b_change);
  343.     buffer->b_change = NULL;
  344.     total_lines = 0;
  345.     save_position(window);
  346.     }
  347.  
  348.     /*
  349.      * First thing we have to do is to obtain a change
  350.      * structure to record the change so we can be sure
  351.      * that we can undo it. If this fails, we must not
  352.      * make any change at all.
  353.      */
  354.     change = challoc();
  355.     if (change == NULL)
  356.     return;
  357.     change->c_type = C_LINE;
  358.  
  359.     /*
  360.      * Work out how many lines are in the new set, and set up
  361.      * pointers to the beginning and end of the list.
  362.      */
  363.     if (newlines != NULL) {
  364.     /*
  365.      * We have a new set of lines to replace the old.
  366.      */
  367.     new_start = newlines;
  368.     nnlines = 1;
  369.     nplines = 0;
  370.     for (new_end = newlines; new_end->l_next != NULL;
  371.                         new_end = new_end->l_next) {
  372.         nnlines++;
  373.         nplines += plines(window, new_end);
  374.     }
  375.     nplines += plines(window, new_end);
  376.     } else {
  377.     /*
  378.      * No new lines; we are just deleting some.
  379.      */
  380.     new_start = new_end = NULL;
  381.     nnlines = 0;
  382.     nplines = 0;
  383.     }
  384.  
  385.     /*
  386.      * Point "firstp" at the line before the first to be deleted,
  387.      * "lastline" at the last line to be deleted, and "lastp" at
  388.      * the line after the last to be deleted. To do the latter,
  389.      * we must loop through the buffer from the specified start
  390.      * line "nolines" times. We also use this loop to set up the
  391.      * "oplines" variable.
  392.      *
  393.      * The line number recorded for the change is the number of the
  394.      * line immediately before it, plus 1. This copes with line being
  395.      * equal to the lastline pointer, which is sometimes necessary
  396.      * for deleting lines at the end of the file.
  397.      */
  398.     firstp = line->l_prev;
  399.     lastline = line;
  400.     change->c_lineno = firstp->l_number + 1;
  401.  
  402.     oplines = 0;
  403.     for (lastp = line, n = 0; lastp != buffer->b_lastline && n < nolines;
  404.                         n++, lastp = lastp->l_next) {
  405.  
  406.     lastline = lastp;
  407.  
  408.     /*
  409.      * Clear any marks that may have been set
  410.      * on the lines to be deleted.
  411.      */
  412.     clrmark(lastp, buffer);
  413.  
  414.     oplines += plines(window, lastp);
  415.  
  416.     /*
  417.      * Scan through all windows which are mapped
  418.      * onto the buffer we are modifying, to see
  419.      * if we need to update their w_topline and/or
  420.      * w_cursor elements as a result of the change.
  421.      *
  422.      * There is a disgusting hack here. If the number
  423.      * of lines being added/deleted is such that the
  424.      * cursor could remain at the same logical line
  425.      * in the file after the change, then it should.
  426.      * Since the cursor could be in different places
  427.      * in each window, we use the Posn.p_index field
  428.      * to store the offset from the start of the
  429.      * changed section. This is only used if the
  430.      * Posn.p_line field has been set to NULL.
  431.      */
  432.     wp = window;
  433.     do {
  434.         if (wp->w_buffer != buffer)
  435.         continue;
  436.  
  437.         if (lastp == wp->w_cursor->p_line) {
  438.         long    offset;
  439.  
  440.         /*
  441.          * Mark the cursor line as NULL
  442.          * so we will know to re-assign
  443.          * it later.
  444.          */
  445.         wp->w_cursor->p_line = NULL;
  446.         offset = cntllines(line, lastp);
  447.         if (offset > INT_MAX) {
  448.             offset = 0;
  449.         }
  450.         wp->w_cursor->p_index = offset;
  451.         }
  452.     } while ((wp = next_window(wp)) != window);
  453.     }
  454.  
  455.     /*
  456.      * Hack.
  457.      *
  458.      * If we are replacing the entire buffer with no lines, we must
  459.      * insert a blank line to avoid the buffer becoming totally empty.
  460.      */
  461.     if (nnlines == 0 && firstp == buffer->b_line0 &&
  462.                     lastp == buffer->b_lastline) {
  463.     /*
  464.      * We are going to delete all the lines - so we have
  465.      * to insert a new blank one in their place.
  466.      */
  467.     new_start = newline(1);
  468.     if (new_start == NULL) {
  469.         show_error(window, "Can't get memory to delete lines");
  470.         chfree(change);
  471.         return;
  472.     }
  473.     new_end = new_start;
  474.     nnlines = 1;
  475.     nplines = 1;
  476.     }
  477.  
  478.     /*
  479.      * Scan through all of the windows which are mapped onto
  480.      * the buffer being changed, and do any screen updates
  481.      * that seem like a good idea.
  482.      */
  483.     wp = window;
  484.     do {
  485.     /*
  486.      * Only do windows onto the right buffer.
  487.      */
  488.     if (wp->w_buffer != buffer)
  489.         continue;
  490.  
  491.     /*
  492.      * Redraw part of the screen if necessary.
  493.      */
  494.     if (!earlier(line, wp->w_topline) &&
  495.         earlier(lastline, wp->w_botline)) {
  496.  
  497.         int    start_row;
  498.  
  499.         start_row = cntplines(wp, wp->w_topline, line);
  500.         if (nplines > oplines && start_row > 0 &&
  501.         (start_row + nplines - oplines) < wp->w_nrows - 2) {
  502.  
  503.         s_ins(wp, start_row, (int) (nplines - oplines));
  504.  
  505.         } else if (nplines < oplines &&
  506.             (start_row + oplines - nplines) < (wp->w_nrows - 2)) {
  507.  
  508.         s_del(wp, start_row, (int) (oplines - nplines));
  509.         }
  510.     }
  511.     } while ((wp = next_window(wp)) != window);
  512.  
  513.     /*
  514.      * Record the old set of lines as the replacement
  515.      * set for undo, if there were any.
  516.      */
  517.     if (nolines > 0) {
  518.     lastp->l_prev->l_next = NULL;
  519.     line->l_prev = NULL;
  520.     change->c_lines = line;
  521.     } else {
  522.     change->c_lines = NULL;
  523.     }
  524.     change->c_nlines = nnlines;
  525.  
  526.     /*
  527.      * Link the buffer back together, using the new
  528.      * lines if there are any, otherwise just linking
  529.      * around the deleted lines.
  530.      */
  531.     if (new_start != NULL) {
  532.     firstp->l_next = new_start;
  533.     lastp->l_prev = new_end;
  534.     new_end->l_next = lastp;
  535.     new_start->l_prev = firstp;
  536.     } else {
  537.     firstp->l_next = lastp;
  538.     lastp->l_prev = firstp;
  539.     }
  540.  
  541.     buffer->b_flags |= FL_MODIFIED;
  542.  
  543.     window->w_curs_new = TRUE;
  544.  
  545.     /*
  546.      * Re-link the buffer file pointer
  547.      * in case we deleted line 1.
  548.      */
  549.     buffer->b_file = buffer->b_line0->l_next;
  550.  
  551.     /*
  552.      * Push this change onto the LIFO of changes
  553.      * that form the current command.
  554.      */
  555.     change->c_next = buffer->b_change;
  556.     buffer->b_change = change;
  557.  
  558.     /*
  559.      * Update the w_cursor and w_topline fields in any Xviwins
  560.      * for which the lines to which they were pointing have
  561.      * been deleted.
  562.      */
  563.     wp = window;
  564.     do {
  565.     /*
  566.      * Only do windows onto the right buffer.
  567.      */
  568.     if (wp->w_buffer != buffer)
  569.         continue;
  570.  
  571.     /*
  572.      * Don't need to update the w_cursor or w_topline
  573.      * elements of this window if no lines are being
  574.      * deleted.
  575.      */
  576.     if (nolines == 0)
  577.         continue;
  578.  
  579.     /*
  580.      * Need a new cursor line value.
  581.      */
  582.     if (wp->w_cursor->p_line == NULL) {
  583.         if (wp->w_cursor->p_index == 0) {
  584.         wp->w_cursor->p_line = (lastp != buffer->b_lastline) ?
  585.                         lastp : lastp->l_prev;
  586.         } else {
  587.         wp->w_cursor->p_line = firstp;
  588.         (void) onedown(wp, (long) wp->w_cursor->p_index);
  589.         }
  590.         wp->w_cursor->p_index = 0;
  591.         begin_line(wp, TRUE);
  592.     }
  593.  
  594.     /*
  595.      * Update the "topline" element of the Xviwin structure
  596.      * if the current topline is one of those being replaced.
  597.      */
  598.     if (line->l_number <= wp->w_topline->l_number &&
  599.             lastline->l_number >= wp->w_topline->l_number) {
  600.         wp->w_topline = wp->w_cursor->p_line;
  601.     }
  602.     } while ((wp = next_window(wp)) != window);
  603.  
  604.     /*
  605.      * Renumber the buffer - but not until all the pointers,
  606.      * especially the b_file pointer, have been re-linked.
  607.      */
  608.     {
  609.     Line        *p;
  610.     unsigned long    l;
  611.  
  612.     p = firstp;
  613.     l = p->l_number;
  614.     for (p = p->l_next; p != buffer->b_lastline; p = p->l_next) {
  615.         p->l_number = ++l;
  616.     }
  617.     buffer->b_lastline->l_number = MAX_LINENO;
  618.     }
  619.  
  620.     total_lines += nnlines - nolines;
  621.     if (buffer->b_nlevels == 0)
  622.     report(window);
  623. }
  624.  
  625. /*
  626.  * Replace the entire buffer with the specified list of lines.
  627.  * This is only used for the :edit command, and we assume that
  628.  * checking has already been done that we are not losing data.
  629.  * The buffer is marked as unmodified. No screen updating is
  630.  * performed. This is the only way to make a non-undoable change
  631.  * to a buffer.
  632.  */
  633. void
  634. replbuffer(window, newlines)
  635. register Xviwin    *window;
  636. Line        *newlines;
  637. {
  638.     register Buffer    *buffer;    /* buffer window is mapped onto */
  639.     Line        *new_end;    /* last line to be inserted */
  640.     Line        *p;
  641.     unsigned long    l;
  642.     Xviwin        *wp;
  643.  
  644.     buffer = window->w_buffer;
  645.  
  646.     if (newlines == NULL) {
  647.     show_error(window,
  648.         "Internal error: replbuffer called with no lines");
  649.     return;
  650.     }
  651.  
  652.     if (buffer->b_nlevels != 0) {
  653.     show_error(window,
  654.         "Internal error: replbuffer called with nlevels != 0");
  655.     return;
  656.     }
  657.     free_changes(buffer->b_change);
  658.     buffer->b_change = NULL;
  659.     buffer->b_nlevels = 0;
  660.  
  661.     /*
  662.      * Point new_end at the last line of newlines.
  663.      */
  664.     for (new_end = newlines; new_end->l_next != NULL;
  665.                         new_end = new_end->l_next) {
  666.     ;
  667.     }
  668.  
  669.     /*
  670.      * Free up the old list of lines.
  671.      */
  672.     buffer->b_lastline->l_prev->l_next = NULL;
  673.     throw(buffer->b_file);
  674.  
  675.     /*
  676.      * Link the buffer back together with the new lines.
  677.      */
  678.     buffer->b_line0->l_next = buffer->b_file = newlines;
  679.     newlines->l_prev = buffer->b_line0;
  680.     buffer->b_lastline->l_prev = new_end;
  681.     new_end->l_next = buffer->b_lastline;
  682.  
  683.     /*
  684.      * Update the w_cursor and w_topline fields in all Xviwins
  685.      * mapped onto the current Buffer.
  686.      */
  687.     wp = window;
  688.     do {
  689.     if (wp->w_buffer != buffer)
  690.         continue;
  691.  
  692.     move_cursor(wp, buffer->b_file, 0);
  693.     wp->w_topline = wp->w_cursor->p_line;
  694.  
  695.     } while ((wp = next_window(wp)) != window);
  696.  
  697.     /*
  698.      * Renumber the buffer.
  699.      */
  700.     l = 0L;
  701.     for (p = buffer->b_line0; p != buffer->b_lastline; p = p->l_next) {
  702.     p->l_number = l++;
  703.     }
  704.     buffer->b_lastline->l_number = MAX_LINENO;
  705.  
  706.     /*
  707.      * Mark buffer as unmodified, and clear any marks it has.
  708.      */
  709.     buffer->b_flags &= ~FL_MODIFIED;
  710.     init_marks(buffer);
  711. }
  712.  
  713. /*
  714.  * Undo the last change in the buffer mapped by the given window.
  715.  */
  716. void
  717. undo(window)
  718. Xviwin    *window;
  719. {
  720.     Buffer    *buffer;
  721.     Change    *chp;
  722.  
  723.     buffer = window->w_buffer;
  724.  
  725.     if (buffer->b_nlevels != 0) {
  726.     show_error(window, "Internal error: undo called with nlevels != 0");
  727.     return;
  728.     }
  729.  
  730.     /*
  731.      * Grab the list of transactions which formed the command
  732.      * that has to be undone, and then call start_command() so
  733.      * we start with a clean slate.
  734.      */
  735.     chp = buffer->b_change;
  736.     buffer->b_change = NULL;
  737.  
  738.     if (start_command(window) == FALSE) {
  739.     return;
  740.     }
  741.  
  742.     while (chp != NULL) {
  743.     Change    *tmp;
  744.     Line    *lp;
  745.  
  746.     tmp = chp;
  747.     chp = chp->c_next;
  748.  
  749.     lp = gotoline(buffer, tmp->c_lineno);
  750.  
  751.     switch (tmp->c_type) {
  752.     case C_LINE:
  753.         /*
  754.          * If the line corresponding to the given number
  755.          * isn't the one we went to, then we must have
  756.          * deleted it in the original change. If the change
  757.          * structure says to insert lines, we must set the
  758.          * line pointer to the lastline marker so that the
  759.          * insert happens in the right place; otherwise,
  760.          * we should not, because repllines won't handle
  761.          * deleting lines from the lastline pointer.
  762.          */
  763.         if (lp->l_number < tmp->c_lineno && tmp->c_lines != NULL) {
  764.         lp = buffer->b_lastline;
  765.         }
  766.         /*
  767.          * Put the lines back as they were.
  768.          * Note that we don't free the lines
  769.          * here; that is only ever done by
  770.          * free_changes, when the next line
  771.          * change happens.
  772.          */
  773.         repllines(window, lp, tmp->c_nlines, tmp->c_lines);
  774.         break;
  775.  
  776.     case C_DEL_CHAR:
  777.         replchars(window, lp, tmp->c_index, tmp->c_nchars, "");
  778.         break;
  779.  
  780.     case C_CHAR:
  781.         replchars(window, lp, tmp->c_index, tmp->c_nchars, tmp->c_chars);
  782.  
  783.         /*
  784.          * Free up the string, since it was strsave'd
  785.          * by replchars at the time the change was made.
  786.          */
  787.         free(tmp->c_chars);
  788.         break;
  789.  
  790.     case C_POSITION:
  791.         move_cursor(window, gotoline(buffer, tmp->c_pline), tmp->c_pindex);
  792.         break;
  793.  
  794.     default:
  795.         show_error(window,
  796.          "Internal error in undo: invalid change type. This is serious.");
  797.         break;
  798.     }
  799.     chfree(tmp);
  800.     }
  801.  
  802.     end_command(window);
  803.     
  804.     update_buffer(buffer);
  805. }
  806.  
  807. static void
  808. free_changes(chp)
  809. Change    *chp;
  810. {
  811.     while (chp != NULL) {
  812.     Change    *tmp;
  813.  
  814.     tmp = chp;
  815.     chp = chp->c_next;
  816.  
  817.     switch (tmp->c_type) {
  818.     case C_LINE:
  819.         throw(tmp->c_lines);
  820.         break;
  821.     case C_CHAR:
  822.         free(tmp->c_chars);
  823.         break;
  824.     case C_DEL_CHAR:
  825.     case C_POSITION:
  826.         break;
  827.     }
  828.     chfree(tmp);
  829.     }
  830. }
  831.  
  832. static void
  833. report(window)
  834. Xviwin    *window;
  835. {
  836.     if (echo & e_REPORT) {
  837.     if (total_lines > Pn(P_report)) {
  838.         show_message(window, "%ld more lines", total_lines);
  839.     } else if (-total_lines > Pn(P_report)) {
  840.         show_message(window, "%ld fewer lines", -total_lines);
  841.     }
  842.     }
  843. }
  844.  
  845. bool_t
  846. set_edit(window, new_value, interactive)
  847. Xviwin        *window;
  848. Paramval    new_value;
  849. bool_t        interactive;
  850. {
  851.     Xviwin    *wp;
  852.  
  853.     /*
  854.      * Disallow setting of "edit" parameter to TRUE if it is FALSE.
  855.      * Hence, this parameter can only ever be set to FALSE.
  856.      */
  857.     if (new_value.pv_b == TRUE && !Pb(P_edit)) {
  858.     if (interactive) {
  859.         show_error(window, "Can't set edit once it has been unset");
  860.     }
  861.     return(FALSE);
  862.     } else {
  863.     /*
  864.      * Set the "noedit" flag on all current buffers,
  865.      * but only if we are in interactive mode
  866.      * (otherwise the window pointer is unreliable).
  867.      * This may set the flag several times on split
  868.      * buffers, but it's no great problem so why not.
  869.      */
  870.     if (interactive) {
  871.         wp = window;
  872.         do {
  873.         wp->w_buffer->b_flags |= FL_NOEDIT;
  874.         } while ((wp = next_window(wp)) != window);
  875.     }
  876.     return(TRUE);
  877.     }
  878. }
  879.