[BACK]Return to undo.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mg

Annotation of src/usr.bin/mg/undo.c, Revision 1.51

1.51    ! florian     1: /* $OpenBSD: undo.c,v 1.50 2010/06/30 19:12:54 oga Exp $ */
1.1       vincent     2: /*
1.45      kjell       3:  * This file is in the public domain
1.1       vincent     4:  */
                      5:
                      6: #include "def.h"
                      7: #include "kbd.h"
                      8:
                      9: #define MAX_FREE_RECORDS       32
                     10:
                     11: /*
                     12:  * Local variables
                     13:  */
1.50      oga        14: static struct undoq             undo_free;
1.7       vincent    15: static int                      undo_free_num;
1.42      kjell      16: static int                      boundary_flag = TRUE;
1.43      deraadt    17: static int                      undo_enable_flag = TRUE;
1.1       vincent    18:
                     19: /*
                     20:  * Local functions
                     21:  */
1.35      deraadt    22: static int find_dot(struct line *, int);
1.39      kjell      23: static int find_lo(int, struct line **, int *, int *);
1.1       vincent    24: static struct undo_rec *new_undo_record(void);
                     25: static int drop_oldest_undo_record(void);
                     26:
1.9       vincent    27: /*
1.24      vincent    28:  * find_dot, find_lo()
1.9       vincent    29:  *
                     30:  * Find an absolute dot in the buffer from a line/offset pair, and vice-versa.
                     31:  *
1.13      deraadt    32:  * Since lines can be deleted while they are referenced by undo record, we
1.9       vincent    33:  * need to have an absolute dot to have something reliable.
                     34:  */
1.1       vincent    35: static int
1.35      deraadt    36: find_dot(struct line *lp, int off)
1.1       vincent    37: {
1.25      db         38:        int      count = 0;
1.35      deraadt    39:        struct line     *p;
1.1       vincent    40:
1.41      kjell      41:        for (p = curbp->b_headp; p != lp; p = lforw(p)) {
1.1       vincent    42:                if (count != 0) {
1.41      kjell      43:                        if (p == curbp->b_headp) {
1.1       vincent    44:                                ewprintf("Error: Undo stuff called with a"
1.9       vincent    45:                                    "nonexistent line");
1.11      vincent    46:                                return (FALSE);
1.1       vincent    47:                        }
                     48:                }
                     49:                count += llength(p) + 1;
                     50:        }
                     51:        count += off;
                     52:
1.11      vincent    53:        return (count);
1.1       vincent    54: }
                     55:
                     56: static int
1.39      kjell      57: find_lo(int pos, struct line **olp, int *offset, int *lnum)
1.1       vincent    58: {
1.35      deraadt    59:        struct line *p;
1.39      kjell      60:        int lineno;
1.1       vincent    61:
1.41      kjell      62:        p = curbp->b_headp;
1.39      kjell      63:        lineno = 0;
1.9       vincent    64:        while (pos > llength(p)) {
1.1       vincent    65:                pos -= llength(p) + 1;
1.41      kjell      66:                if ((p = lforw(p)) == curbp->b_headp) {
1.1       vincent    67:                        *olp = NULL;
                     68:                        *offset = 0;
1.11      vincent    69:                        return (FALSE);
1.1       vincent    70:                }
1.39      kjell      71:                lineno++;
1.1       vincent    72:        }
                     73:        *olp = p;
                     74:        *offset = pos;
1.39      kjell      75:        *lnum = lineno;
1.1       vincent    76:
1.11      vincent    77:        return (TRUE);
1.1       vincent    78: }
                     79:
                     80: static struct undo_rec *
                     81: new_undo_record(void)
                     82: {
                     83:        struct undo_rec *rec;
                     84:
1.50      oga        85:        rec = TAILQ_FIRST(&undo_free);
1.9       vincent    86:        if (rec != NULL) {
1.50      oga        87:                /* Remove it from the free-list */
                     88:                TAILQ_REMOVE(&undo_free, rec, next);
1.9       vincent    89:                undo_free_num--;
                     90:        } else {
1.25      db         91:                if ((rec = malloc(sizeof(*rec))) == NULL)
1.1       vincent    92:                        panic("Out of memory in undo code (record)");
                     93:        }
                     94:        memset(rec, 0, sizeof(struct undo_rec));
1.9       vincent    95:
1.11      vincent    96:        return (rec);
1.1       vincent    97: }
                     98:
1.7       vincent    99: void
1.1       vincent   100: free_undo_record(struct undo_rec *rec)
                    101: {
1.9       vincent   102:        static int initialised = 0;
                    103:
                    104:        /*
                    105:         * On the first run, do initialisation of the free list.
                    106:         */
                    107:        if (initialised == 0) {
1.50      oga       108:                TAILQ_INIT(&undo_free);
1.9       vincent   109:                initialised = 1;
                    110:        }
1.1       vincent   111:        if (rec->content != NULL) {
                    112:                free(rec->content);
                    113:                rec->content = NULL;
                    114:        }
                    115:        if (undo_free_num >= MAX_FREE_RECORDS) {
                    116:                free(rec);
                    117:                return;
                    118:        }
                    119:        undo_free_num++;
1.9       vincent   120:
1.50      oga       121:        TAILQ_INSERT_HEAD(&undo_free, rec, next);
1.1       vincent   122: }
                    123:
                    124: /*
                    125:  * Drop the oldest undo record in our list. Return 1 if we could remove it,
1.25      db        126:  * 0 if the undo list was empty.
1.1       vincent   127:  */
                    128: static int
                    129: drop_oldest_undo_record(void)
                    130: {
                    131:        struct undo_rec *rec;
                    132:
1.50      oga       133:        rec = TAILQ_LAST(&curbp->b_undo, undoq);
1.1       vincent   134:        if (rec != NULL) {
                    135:                undo_free_num--;
1.50      oga       136:                TAILQ_REMOVE(&curbp->b_undo, rec, next);
1.1       vincent   137:                free_undo_record(rec);
1.11      vincent   138:                return (1);
1.1       vincent   139:        }
1.11      vincent   140:        return (0);
1.1       vincent   141: }
1.9       vincent   142:
1.44      kjell     143: static int
1.22      vincent   144: lastrectype(void)
1.1       vincent   145: {
1.9       vincent   146:        struct undo_rec *rec;
                    147:
1.50      oga       148:        if ((rec = TAILQ_FIRST(&curbp->b_undo)) != NULL)
1.22      vincent   149:                return (rec->type);
1.11      vincent   150:        return (0);
1.1       vincent   151: }
                    152:
1.30      kjell     153: /*
1.42      kjell     154:  * Returns TRUE if undo is enabled, FALSE otherwise.
                    155:  */
                    156: int
                    157: undo_enabled(void)
                    158: {
                    159:        return (undo_enable_flag);
                    160: }
                    161:
                    162: /*
1.47      kjell     163:  * undo_enable: toggle undo_enable.
                    164:  * Returns the previous value of the flag.
1.30      kjell     165:  */
1.1       vincent   166: int
1.47      kjell     167: undo_enable(int f, int n)
1.1       vincent   168: {
1.42      kjell     169:        int pon = undo_enable_flag;
1.22      vincent   170:
1.47      kjell     171:        if (f & (FFARG | FFRAND))
                    172:                undo_enable_flag = n > 0;
                    173:        else
                    174:                undo_enable_flag = !undo_enable_flag;
                    175:
                    176:        if (!(f & FFRAND))
                    177:                ewprintf("Undo %sabled", undo_enable_flag ? "en" : "dis");
                    178:
1.42      kjell     179:        return (pon);
1.5       vincent   180: }
                    181:
1.30      kjell     182: /*
                    183:  * If undo is enabled, then:
1.47      kjell     184:  *  Toggle undo boundary recording.
                    185:  *  If called with an argument, (n > 0) => enable. Otherwise disable.
                    186:  * In either case, add an undo boundary
1.30      kjell     187:  * If undo is disabled, this function has no effect.
                    188:  */
1.47      kjell     189: int
                    190: undo_boundary_enable(int f, int n)
                    191: {
                    192:        int bon = boundary_flag;
1.42      kjell     193:
1.47      kjell     194:        if (!undo_enable_flag)
                    195:                return (FALSE);
                    196:
                    197:        undo_add_boundary(FFRAND, 1);
                    198:
                    199:        if (f & (FFARG | FFRAND))
                    200:                boundary_flag = n > 0;
                    201:        else
                    202:                boundary_flag = !boundary_flag;
                    203:
                    204:        if (!(f & FFRAND))
                    205:                ewprintf("Undo boundaries %sabled",
                    206:                    boundary_flag ? "en" : "dis");
                    207:
                    208:        return (bon);
1.28      kjell     209: }
                    210:
1.30      kjell     211: /*
1.42      kjell     212:  * Record an undo boundary, unless boundary_flag == FALSE.
1.40      kjell     213:  * Does nothing if previous undo entry is already a boundary or 'modified' flag.
1.30      kjell     214:  */
1.47      kjell     215: int
                    216: undo_add_boundary(int f, int n)
1.5       vincent   217: {
                    218:        struct undo_rec *rec;
1.40      kjell     219:        int last;
1.5       vincent   220:
1.42      kjell     221:        if (boundary_flag == FALSE)
1.47      kjell     222:                return (FALSE);
1.12      vincent   223:
1.40      kjell     224:        last = lastrectype();
                    225:        if (last == BOUNDARY || last == MODIFIED)
1.47      kjell     226:                return (TRUE);
1.31      deraadt   227:
1.5       vincent   228:        rec = new_undo_record();
1.9       vincent   229:        rec->type = BOUNDARY;
1.5       vincent   230:
1.50      oga       231:        TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next);
1.9       vincent   232:
1.47      kjell     233:        return (TRUE);
1.1       vincent   234: }
                    235:
1.40      kjell     236: /*
                    237:  * Record an undo "modified" boundary
                    238:  */
                    239: void
                    240: undo_add_modified(void)
                    241: {
                    242:        struct undo_rec *rec;
                    243:
                    244:        rec = new_undo_record();
                    245:        rec->type = MODIFIED;
                    246:
1.50      oga       247:        TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next);
1.40      kjell     248:
                    249:        return;
                    250: }
                    251:
1.1       vincent   252: int
1.35      deraadt   253: undo_add_insert(struct line *lp, int offset, int size)
1.1       vincent   254: {
1.35      deraadt   255:        struct region   reg;
1.25      db        256:        struct  undo_rec *rec;
                    257:        int     pos;
1.9       vincent   258:
1.47      kjell     259:        if (!undo_enable_flag)
1.11      vincent   260:                return (TRUE);
1.1       vincent   261:        reg.r_linep = lp;
                    262:        reg.r_offset = offset;
                    263:        reg.r_size = size;
1.9       vincent   264:
1.24      vincent   265:        pos = find_dot(lp, offset);
1.9       vincent   266:
1.1       vincent   267:        /*
                    268:         * We try to reuse the last undo record to `compress' things.
1.13      deraadt   269:         */
1.50      oga       270:        rec = TAILQ_FIRST(&curbp->b_undo);
1.23      vincent   271:        if (rec != NULL && rec->type == INSERT) {
                    272:                if (rec->pos + rec->region.r_size == pos) {
                    273:                        rec->region.r_size += reg.r_size;
                    274:                        return (TRUE);
1.1       vincent   275:                }
                    276:        }
1.9       vincent   277:
1.1       vincent   278:        /*
1.25      db        279:         * We couldn't reuse the last undo record, so prepare a new one.
1.1       vincent   280:         */
                    281:        rec = new_undo_record();
1.9       vincent   282:        rec->pos = pos;
1.1       vincent   283:        rec->type = INSERT;
1.35      deraadt   284:        memmove(&rec->region, &reg, sizeof(struct region));
1.1       vincent   285:        rec->content = NULL;
1.9       vincent   286:
1.47      kjell     287:        undo_add_boundary(FFRAND, 1);
1.9       vincent   288:
1.50      oga       289:        TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next);
1.1       vincent   290:
1.11      vincent   291:        return (TRUE);
1.1       vincent   292: }
                    293:
                    294: /*
1.25      db        295:  * This of course must be done _before_ the actual deletion is done.
1.1       vincent   296:  */
                    297: int
1.49      kjell     298: undo_add_delete(struct line *lp, int offset, int size, int isreg)
1.1       vincent   299: {
1.35      deraadt   300:        struct region   reg;
1.25      db        301:        struct  undo_rec *rec;
                    302:        int     pos;
1.1       vincent   303:
1.47      kjell     304:        if (!undo_enable_flag)
1.11      vincent   305:                return (TRUE);
1.1       vincent   306:
                    307:        reg.r_linep = lp;
                    308:        reg.r_offset = offset;
                    309:        reg.r_size = size;
                    310:
1.24      vincent   311:        pos = find_dot(lp, offset);
1.3       vincent   312:
1.10      vincent   313:        if (offset == llength(lp))      /* if it's a newline... */
1.47      kjell     314:                undo_add_boundary(FFRAND, 1);
1.50      oga       315:        else if ((rec = TAILQ_FIRST(&curbp->b_undo)) != NULL) {
1.9       vincent   316:                /*
                    317:                 * Separate this command from the previous one if we're not
                    318:                 * just before the previous record...
                    319:                 */
1.49      kjell     320:                if (!isreg && rec->type == DELETE) {
1.9       vincent   321:                        if (rec->pos - rec->region.r_size != pos)
1.47      kjell     322:                                undo_add_boundary(FFRAND, 1);
1.23      vincent   323:                }
1.3       vincent   324:        }
1.1       vincent   325:        rec = new_undo_record();
                    326:        rec->pos = pos;
1.49      kjell     327:        if (isreg)
                    328:                rec->type = DELREG;
                    329:        else
                    330:                rec->type = DELETE;
1.35      deraadt   331:        memmove(&rec->region, &reg, sizeof(struct region));
1.1       vincent   332:        do {
                    333:                rec->content = malloc(reg.r_size + 1);
                    334:        } while ((rec->content == NULL) && drop_oldest_undo_record());
1.9       vincent   335:
1.1       vincent   336:        if (rec->content == NULL)
                    337:                panic("Out of memory");
1.9       vincent   338:
1.1       vincent   339:        region_get_data(&reg, rec->content, reg.r_size);
                    340:
1.49      kjell     341:        if (isreg || lastrectype() != DELETE)
1.47      kjell     342:                undo_add_boundary(FFRAND, 1);
1.22      vincent   343:
1.50      oga       344:        TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next);
1.1       vincent   345:
1.11      vincent   346:        return (TRUE);
1.1       vincent   347: }
                    348:
                    349: /*
1.25      db        350:  * This of course must be called before the change takes place.
1.1       vincent   351:  */
                    352: int
1.35      deraadt   353: undo_add_change(struct line *lp, int offset, int size)
1.1       vincent   354: {
1.47      kjell     355:        if (!undo_enable_flag)
1.11      vincent   356:                return (TRUE);
1.47      kjell     357:        undo_add_boundary(FFRAND, 1);
1.42      kjell     358:        boundary_flag = FALSE;
1.49      kjell     359:        undo_add_delete(lp, offset, size, 0);
1.12      vincent   360:        undo_add_insert(lp, offset, size);
1.42      kjell     361:        boundary_flag = TRUE;
1.47      kjell     362:        undo_add_boundary(FFRAND, 1);
1.9       vincent   363:
1.11      vincent   364:        return (TRUE);
1.8       vincent   365: }
                    366:
                    367: /*
                    368:  * Show the undo records for the current buffer in a new buffer.
                    369:  */
1.32      kjell     370: /* ARGSUSED */
1.8       vincent   371: int
1.18      vincent   372: undo_dump(int f, int n)
1.8       vincent   373: {
1.25      db        374:        struct   undo_rec *rec;
1.35      deraadt   375:        struct buffer   *bp;
                    376:        struct mgwin    *wp;
1.25      db        377:        char     buf[4096], tmp[1024];
                    378:        int      num;
1.8       vincent   379:
                    380:        /*
                    381:         * Prepare the buffer for insertion.
                    382:         */
                    383:        if ((bp = bfind("*undo*", TRUE)) == NULL)
1.11      vincent   384:                return (FALSE);
1.9       vincent   385:        bp->b_flag |= BFREADONLY;
1.8       vincent   386:        bclear(bp);
1.48      kjell     387:        popbuf(bp, WNONE);
1.8       vincent   388:
1.10      vincent   389:        for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.8       vincent   390:                if (wp->w_bufp == bp) {
1.41      kjell     391:                        wp->w_dotp = bp->b_headp;
1.8       vincent   392:                        wp->w_doto = 0;
                    393:                }
1.10      vincent   394:        }
1.8       vincent   395:
                    396:        num = 0;
1.50      oga       397:        TAILQ_FOREACH(rec, &curbp->b_undo, next) {
1.8       vincent   398:                num++;
1.25      db        399:                snprintf(buf, sizeof(buf),
1.38      kjell     400:                    "%d:\t %s at %d ", num,
1.8       vincent   401:                    (rec->type == DELETE) ? "DELETE":
1.49      kjell     402:                    (rec->type == DELREG) ? "DELREGION":
1.8       vincent   403:                    (rec->type == INSERT) ? "INSERT":
1.40      kjell     404:                    (rec->type == BOUNDARY) ? "----" :
                    405:                    (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN",
1.8       vincent   406:                    rec->pos);
1.10      vincent   407:
1.12      vincent   408:                if (rec->content) {
1.38      kjell     409:                        (void)strlcat(buf, "\"", sizeof(buf));
1.25      db        410:                        snprintf(tmp, sizeof(tmp), "%.*s", rec->region.r_size,
1.8       vincent   411:                            rec->content);
1.38      kjell     412:                        (void)strlcat(buf, tmp, sizeof(buf));
                    413:                        (void)strlcat(buf, "\"", sizeof(buf));
1.8       vincent   414:                }
1.25      db        415:                snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size);
1.33      kjell     416:                if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) {
                    417:                        ewprintf("Undo record too large. Aborted.");
                    418:                        return (FALSE);
                    419:                }
1.17      deraadt   420:                addlinef(bp, "%s", buf);
1.8       vincent   421:        }
1.11      vincent   422:        return (TRUE);
1.1       vincent   423: }
                    424:
1.9       vincent   425: /*
1.25      db        426:  * After the user did action1, then action2, then action3:
1.9       vincent   427:  *
                    428:  *     [action3] <--- Undoptr
                    429:  *     [action2]
                    430:  *     [action1]
                    431:  *      ------
                    432:  *      [undo]
                    433:  *
                    434:  * After undo:
                    435:  *
                    436:  *     [undo of action3]
                    437:  *     [action2] <--- Undoptr
                    438:  *     [action1]
                    439:  *      ------
                    440:  *      [undo]
1.13      deraadt   441:  *
1.9       vincent   442:  * After another undo:
                    443:  *
                    444:  *
                    445:  *     [undo of action2]
                    446:  *     [undo of action3]
                    447:  *     [action1]  <--- Undoptr
                    448:  *      ------
                    449:  *      [undo]
1.13      deraadt   450:  *
1.25      db        451:  * Note that the "undo of actionX" have no special meaning. Only when
                    452:  * we undo a deletion, the insertion will be recorded just as if it
1.10      vincent   453:  * was typed on the keyboard. Resulting in the inverse operation being
1.9       vincent   454:  * saved in the list.
                    455:  *
                    456:  * If undoptr reaches the bottom of the list, or if we moved between
                    457:  * two undo actions, we make it point back at the topmost record. This is
                    458:  * how we handle redoing.
                    459:  */
1.32      kjell     460: /* ARGSUSED */
1.1       vincent   461: int
1.4       vincent   462: undo(int f, int n)
1.1       vincent   463: {
1.25      db        464:        struct undo_rec *ptr, *nptr;
1.31      deraadt   465:        int              done, rval;
1.36      kjell     466:        struct line     *lp;
1.51    ! florian   467:        int              offset, save;
1.28      kjell     468:        static int       nulled = FALSE;
1.39      kjell     469:        int              lineno;
1.23      vincent   470:
1.46      kjell     471:        if (n < 0)
                    472:                return (FALSE);
                    473:
1.29      kjell     474:        ptr = curbp->b_undoptr;
1.9       vincent   475:
1.46      kjell     476:        /* first invocation, make ptr point back to the top of the list */
                    477:        if ((ptr == NULL && nulled == TRUE) ||  rptcount == 0) {
1.50      oga       478:                ptr = TAILQ_FIRST(&curbp->b_undo);
1.28      kjell     479:                nulled = TRUE;
                    480:        }
1.1       vincent   481:
1.9       vincent   482:        rval = TRUE;
1.10      vincent   483:        while (n--) {
1.9       vincent   484:                /* if we have a spurious boundary, free it and move on.... */
                    485:                while (ptr && ptr->type == BOUNDARY) {
1.50      oga       486:                        nptr = TAILQ_NEXT(ptr, next);
                    487:                        TAILQ_REMOVE(&curbp->b_undo, ptr, next);
1.9       vincent   488:                        free_undo_record(ptr);
                    489:                        ptr = nptr;
1.4       vincent   490:                }
                    491:                /*
1.9       vincent   492:                 * Ptr is NULL, but on the next run, it will point to the
                    493:                 * top again, redoing all stuff done in the buffer since
                    494:                 * its creation.
1.4       vincent   495:                 */
1.9       vincent   496:                if (ptr == NULL) {
1.11      vincent   497:                        ewprintf("No further undo information");
1.9       vincent   498:                        rval = FALSE;
1.28      kjell     499:                        nulled = TRUE;
1.4       vincent   500:                        break;
                    501:                }
1.28      kjell     502:                nulled = FALSE;
1.6       vincent   503:
1.13      deraadt   504:                /*
1.9       vincent   505:                 * Loop while we don't get a boundary specifying we've
                    506:                 * finished the current action...
                    507:                 */
1.22      vincent   508:
1.47      kjell     509:                undo_add_boundary(FFRAND, 1);
1.22      vincent   510:
1.42      kjell     511:                save = boundary_flag;
                    512:                boundary_flag = FALSE;
1.22      vincent   513:
1.9       vincent   514:                done = 0;
                    515:                do {
                    516:                        /*
                    517:                         * Move to where this has to apply
                    518:                         *
1.40      kjell     519:                         * Boundaries (and the modified flag)  are put as
                    520:                         * position 0 (to save lookup time in find_dot)
                    521:                         * so we must not move there...
1.9       vincent   522:                         */
1.40      kjell     523:                        if (ptr->type != BOUNDARY && ptr->type != MODIFIED) {
1.24      vincent   524:                                if (find_lo(ptr->pos, &lp,
1.39      kjell     525:                                    &offset, &lineno) == FALSE) {
1.9       vincent   526:                                        ewprintf("Internal error in Undo!");
                    527:                                        rval = FALSE;
                    528:                                        break;
                    529:                                }
                    530:                                curwp->w_dotp = lp;
                    531:                                curwp->w_doto = offset;
1.39      kjell     532:                                curwp->w_markline = curwp->w_dotline;
                    533:                                curwp->w_dotline = lineno;
1.9       vincent   534:                        }
                    535:
                    536:                        /*
                    537:                         * Do operation^-1
                    538:                         */
                    539:                        switch (ptr->type) {
                    540:                        case INSERT:
1.34      kjell     541:                                ldelete(ptr->region.r_size, KNONE);
1.9       vincent   542:                                break;
                    543:                        case DELETE:
1.46      kjell     544:                                lp = curwp->w_dotp;
                    545:                                offset = curwp->w_doto;
1.13      deraadt   546:                                region_put_data(ptr->content,
1.9       vincent   547:                                    ptr->region.r_size);
1.46      kjell     548:                                curwp->w_dotp = lp;
                    549:                                curwp->w_doto = offset;
1.49      kjell     550:                                break;
                    551:                        case DELREG:
                    552:                                region_put_data(ptr->content,
                    553:                                    ptr->region.r_size);
1.9       vincent   554:                                break;
                    555:                        case BOUNDARY:
                    556:                                done = 1;
1.40      kjell     557:                                break;
                    558:                        case MODIFIED:
                    559:                                curbp->b_flag &= ~BFCHG;
1.9       vincent   560:                                break;
                    561:                        default:
                    562:                                break;
                    563:                        }
                    564:
                    565:                        /* And move to next record */
1.50      oga       566:                        ptr = TAILQ_NEXT(ptr, next);
1.9       vincent   567:                } while (ptr != NULL && !done);
1.22      vincent   568:
1.42      kjell     569:                boundary_flag = save;
1.47      kjell     570:                undo_add_boundary(FFRAND, 1);
1.9       vincent   571:
                    572:                ewprintf("Undo!");
1.1       vincent   573:        }
1.51    ! florian   574:
1.29      kjell     575:        curbp->b_undoptr = ptr;
1.9       vincent   576:
1.13      deraadt   577:        return (rval);
1.1       vincent   578: }