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

Annotation of src/usr.bin/mg/buffer.c, Revision 1.96

1.96    ! bcallah     1: /*     $OpenBSD: buffer.c,v 1.95 2015/03/16 13:47:48 bcallah Exp $     */
1.43      kjell       2:
                      3: /* This file is in the public domain. */
1.5       niklas      4:
1.1       deraadt     5: /*
                      6:  *             Buffer handling.
                      7:  */
1.4       millert     8:
1.96    ! bcallah     9: #include <sys/queue.h>
        !            10: #include <errno.h>
        !            11: #include <libgen.h>
        !            12: #include <signal.h>
        !            13: #include <stdarg.h>
        !            14: #include <stdio.h>
        !            15: #include <stdlib.h>
        !            16: #include <string.h>
        !            17: #include <unistd.h>
        !            18:
1.76      kjell      19: #include "def.h"
                     20: #include "kbd.h"               /* needed for modes */
1.1       deraadt    21:
1.88      florian    22: #ifndef DIFFTOOL
                     23: #define DIFFTOOL "/usr/bin/diff"
                     24: #endif /* !DIFFTOOL */
                     25:
1.52      deraadt    26: static struct buffer  *makelist(void);
1.65      kjell      27: static struct buffer *bnew(const char *);
1.75      lum        28:
                     29: static int usebufname(const char *);
1.1       deraadt    30:
1.67      kjell      31: /* Flag for global working dir */
                     32: extern int globalwd;
                     33:
1.50      kjell      34: /* ARGSUSED */
1.27      vincent    35: int
1.31      vincent    36: togglereadonly(int f, int n)
1.27      vincent    37: {
1.68      kjell      38:        int s;
                     39:
                     40:        if ((s = checkdirty(curbp)) != TRUE)
                     41:                return (s);
1.47      kjell      42:        if (!(curbp->b_flag & BFREADONLY))
1.27      vincent    43:                curbp->b_flag |= BFREADONLY;
1.47      kjell      44:        else {
1.68      kjell      45:                curbp->b_flag &= ~BFREADONLY;
1.27      vincent    46:                if (curbp->b_flag & BFCHG)
                     47:                        ewprintf("Warning: Buffer was modified");
                     48:        }
1.70      kjell      49:        curwp->w_rflag |= WFMODE;
1.27      vincent    50:
1.47      kjell      51:        return (TRUE);
1.27      vincent    52: }
                     53:
1.73      deraadt    54: /* Switch to the named buffer.
                     55:  * If no name supplied, switch to the default (alternate) buffer.
                     56:  */
                     57: int
                     58: usebufname(const char *bufp)
                     59: {
                     60:        struct buffer *bp;
                     61:
                     62:        if (bufp == NULL)
                     63:                return (ABORT);
                     64:        if (bufp[0] == '\0' && curbp->b_altb != NULL)
                     65:                bp = curbp->b_altb;
                     66:        else if ((bp = bfind(bufp, TRUE)) == NULL)
                     67:                return (FALSE);
                     68:
                     69:        /* and put it in current window */
                     70:        curbp = bp;
                     71:        return (showbuffer(bp, curwp, WFFRAME | WFFULL));
                     72: }
                     73:
1.1       deraadt    74: /*
                     75:  * Attach a buffer to a window. The values of dot and mark come
                     76:  * from the buffer if the use count is 0. Otherwise, they come
                     77:  * from some other window.  *scratch* is the default alternate
                     78:  * buffer.
                     79:  */
1.3       millert    80: /* ARGSUSED */
                     81: int
1.26      vincent    82: usebuffer(int f, int n)
1.1       deraadt    83: {
1.34      vincent    84:        char    bufn[NBUFN], *bufp;
1.1       deraadt    85:
                     86:        /* Get buffer to use from user */
1.21      deraadt    87:        if ((curbp->b_altb == NULL) &&
                     88:            ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
1.34      vincent    89:                bufp = eread("Switch to buffer: ", bufn, NBUFN, EFNEW | EFBUF);
1.1       deraadt    90:        else
1.34      vincent    91:                bufp = eread("Switch to buffer: (default %s) ", bufn, NBUFN,
1.55      deraadt    92:                    EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
1.1       deraadt    93:
1.72      kjell      94:        return (usebufname(bufp));
1.1       deraadt    95: }
                     96:
                     97: /*
                     98:  * pop to buffer asked for by the user.
                     99:  */
1.3       millert   100: /* ARGSUSED */
                    101: int
1.26      vincent   102: poptobuffer(int f, int n)
1.1       deraadt   103: {
1.52      deraadt   104:        struct buffer *bp;
                    105:        struct mgwin  *wp;
1.34      vincent   106:        char    bufn[NBUFN], *bufp;
1.1       deraadt   107:
                    108:        /* Get buffer to use from user */
1.21      deraadt   109:        if ((curbp->b_altb == NULL) &&
                    110:            ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
1.34      vincent   111:                bufp = eread("Switch to buffer in other window: ", bufn, NBUFN,
1.55      deraadt   112:                    EFNEW | EFBUF);
1.1       deraadt   113:        else
1.34      vincent   114:                bufp = eread("Switch to buffer in other window: (default %s) ",
1.55      deraadt   115:                    bufn, NBUFN, EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
1.34      vincent   116:        if (bufp == NULL)
1.37      db        117:                return (ABORT);
1.44      kjell     118:        if (bufp[0] == '\0' && curbp->b_altb != NULL)
1.3       millert   119:                bp = curbp->b_altb;
                    120:        else if ((bp = bfind(bufn, TRUE)) == NULL)
1.37      db        121:                return (FALSE);
1.59      kjell     122:        if (bp == curbp)
                    123:                return (splitwind(f, n));
1.71      kjell     124:        /* and put it in a new, non-ephemeral window */
                    125:        if ((wp = popbuf(bp, WNONE)) == NULL)
1.37      db        126:                return (FALSE);
1.1       deraadt   127:        curbp = bp;
                    128:        curwp = wp;
1.37      db        129:        return (TRUE);
1.1       deraadt   130: }
                    131:
                    132: /*
                    133:  * Dispose of a buffer, by name.
1.91      lum       134:  * Ask for the name (unless called by dired mode). Look it up (don't
                    135:  * get too upset if it isn't there at all!). Clear the buffer (ask
1.1       deraadt   136:  * if the buffer has been changed). Then free the header
1.91      lum       137:  * line and the buffer header. Bound to "C-x k".
1.1       deraadt   138:  */
1.3       millert   139: /* ARGSUSED */
                    140: int
1.35      jfb       141: killbuffer_cmd(int f, int n)
1.1       deraadt   142: {
1.52      deraadt   143:        struct buffer *bp;
1.34      vincent   144:        char    bufn[NBUFN], *bufp;
1.3       millert   145:
1.91      lum       146:        if (f & FFRAND) /* dired mode 'q' */
                    147:                bp = curbp;
                    148:        else if ((bufp = eread("Kill buffer: (default %s) ", bufn, NBUFN,
1.40      kjell     149:            EFNUL | EFNEW | EFBUF, curbp->b_bname)) == NULL)
1.37      db        150:                return (ABORT);
1.44      kjell     151:        else if (bufp[0] == '\0')
1.3       millert   152:                bp = curbp;
                    153:        else if ((bp = bfind(bufn, FALSE)) == NULL)
1.37      db        154:                return (FALSE);
                    155:        return (killbuffer(bp));
1.35      jfb       156: }
                    157:
                    158: int
1.52      deraadt   159: killbuffer(struct buffer *bp)
1.35      jfb       160: {
1.52      deraadt   161:        struct buffer *bp1;
                    162:        struct buffer *bp2;
                    163:        struct mgwin  *wp;
1.41      kjell     164:        int s;
1.90      florian   165:        struct undo_rec *rec;
1.3       millert   166:
                    167:        /*
1.37      db        168:         * Find some other buffer to display. Try the alternate buffer,
1.3       millert   169:         * then the first different buffer in the buffer list.  If there's
                    170:         * only one buffer, create buffer *scratch* and make it the alternate
                    171:         * buffer.  Return if *scratch* is only buffer...
1.1       deraadt   172:         */
                    173:        if ((bp1 = bp->b_altb) == NULL) {
                    174:                bp1 = (bp == bheadp) ? bp->b_bufp : bheadp;
                    175:                if (bp1 == NULL) {
                    176:                        /* only one buffer. see if it's *scratch* */
1.3       millert   177:                        if (bp == bfind("*scratch*", FALSE))
1.49      kjell     178:                                return (TRUE);
1.1       deraadt   179:                        /* create *scratch* for alternate buffer */
1.3       millert   180:                        if ((bp1 = bfind("*scratch*", TRUE)) == NULL)
1.37      db        181:                                return (FALSE);
1.1       deraadt   182:                }
                    183:        }
1.41      kjell     184:        if ((s = bclear(bp)) != TRUE)
                    185:                return (s);
1.1       deraadt   186:        for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) {
1.3       millert   187:                if (wp->w_bufp == bp) {
                    188:                        bp2 = bp1->b_altb;      /* save alternate buffer */
1.58      kjell     189:                        if (showbuffer(bp1, wp, WFMODE | WFFRAME | WFFULL))
1.3       millert   190:                                bp1->b_altb = bp2;
                    191:                        else
                    192:                                bp1 = bp2;
                    193:                }
                    194:        }
                    195:        if (bp == curbp)
                    196:                curbp = bp1;
1.62      kjell     197:        free(bp->b_headp);                      /* Release header line.  */
1.3       millert   198:        bp2 = NULL;                             /* Find the header.      */
1.1       deraadt   199:        bp1 = bheadp;
                    200:        while (bp1 != bp) {
                    201:                if (bp1->b_altb == bp)
                    202:                        bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
                    203:                bp2 = bp1;
                    204:                bp1 = bp1->b_bufp;
                    205:        }
1.3       millert   206:        bp1 = bp1->b_bufp;                      /* Next one in chain.    */
                    207:        if (bp2 == NULL)                        /* Unlink it.            */
1.1       deraadt   208:                bheadp = bp1;
                    209:        else
                    210:                bp2->b_bufp = bp1;
1.3       millert   211:        while (bp1 != NULL) {                   /* Finish with altb's    */
1.1       deraadt   212:                if (bp1->b_altb == bp)
                    213:                        bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
                    214:                bp1 = bp1->b_bufp;
                    215:        }
1.74      oga       216:
1.90      florian   217:        while ((rec = TAILQ_FIRST(&bp->b_undo))) {
                    218:                TAILQ_REMOVE(&bp->b_undo, rec, next);
1.46      kjell     219:                free_undo_record(rec);
                    220:        }
                    221:
1.54      kjell     222:        free(bp->b_bname);                      /* Release name block    */
1.3       millert   223:        free(bp);                               /* Release buffer block */
1.37      db        224:        return (TRUE);
1.1       deraadt   225: }
                    226:
                    227: /*
                    228:  * Save some buffers - just call anycb with the arg flag.
                    229:  */
1.3       millert   230: /* ARGSUSED */
                    231: int
1.26      vincent   232: savebuffers(int f, int n)
1.1       deraadt   233: {
1.3       millert   234:        if (anycb(f) == ABORT)
1.37      db        235:                return (ABORT);
                    236:        return (TRUE);
1.1       deraadt   237: }
                    238:
                    239: /*
1.19      art       240:  * Listing buffers.
                    241:  */
                    242: static int listbuf_ncol;
                    243:
                    244: static int     listbuf_goto_buffer(int f, int n);
1.39      jason     245: static int     listbuf_goto_buffer_one(int f, int n);
                    246: static int     listbuf_goto_buffer_helper(int f, int n, int only);
1.19      art       247:
                    248: static PF listbuf_pf[] = {
1.37      db        249:        listbuf_goto_buffer
1.19      art       250: };
1.39      jason     251: static PF listbuf_one[] = {
                    252:        listbuf_goto_buffer_one
                    253: };
                    254:
1.19      art       255:
1.39      jason     256: static struct KEYMAPE (2 + IMAPEXT) listbufmap = {
                    257:        2,
                    258:        2 + IMAPEXT,
1.19      art       259:        rescan,
                    260:        {
1.39      jason     261:                {
1.45      deraadt   262:                        CCHR('M'), CCHR('M'), listbuf_pf, NULL
1.39      jason     263:                },
                    264:                {
1.45      deraadt   265:                        '1', '1', listbuf_one, NULL
1.39      jason     266:                }
1.19      art       267:        }
                    268: };
                    269:
                    270: /*
1.1       deraadt   271:  * Display the buffer list. This is done
                    272:  * in two parts. The "makelist" routine figures out
                    273:  * the text, and puts it in a buffer. "popbuf"
                    274:  * then pops the data onto the screen. Bound to
                    275:  * "C-X C-B".
                    276:  */
1.3       millert   277: /* ARGSUSED */
                    278: int
1.26      vincent   279: listbuffers(int f, int n)
1.1       deraadt   280: {
1.52      deraadt   281:        static int               initialized = 0;
                    282:        struct buffer           *bp;
                    283:        struct mgwin            *wp;
1.1       deraadt   284:
1.19      art       285:        if (!initialized) {
                    286:                maps_add((KEYMAP *)&listbufmap, "listbufmap");
                    287:                initialized = 1;
                    288:        }
                    289:
1.71      kjell     290:        if ((bp = makelist()) == NULL || (wp = popbuf(bp, WNONE)) == NULL)
1.37      db        291:                return (FALSE);
                    292:        wp->w_dotp = bp->b_dotp; /* fix up if window already on screen */
1.1       deraadt   293:        wp->w_doto = bp->b_doto;
1.19      art       294:        bp->b_modes[0] = name_mode("fundamental");
                    295:        bp->b_modes[1] = name_mode("listbufmap");
                    296:        bp->b_nmodes = 1;
                    297:
1.37      db        298:        return (TRUE);
1.1       deraadt   299: }
                    300:
                    301: /*
                    302:  * This routine rebuilds the text for the
1.37      db        303:  * list buffers command. Return pointer
                    304:  * to new list if everything works.
                    305:  * Return NULL if there is an error (if
                    306:  * there is no memory).
1.1       deraadt   307:  */
1.52      deraadt   308: static struct buffer *
1.26      vincent   309: makelist(void)
1.3       millert   310: {
1.52      deraadt   311:        int             w = ncol / 2;
                    312:        struct buffer   *bp, *blp;
                    313:        struct line     *lp;
1.19      art       314:
1.3       millert   315:        if ((blp = bfind("*Buffer List*", TRUE)) == NULL)
1.37      db        316:                return (NULL);
1.3       millert   317:        if (bclear(blp) != TRUE)
1.37      db        318:                return (NULL);
1.3       millert   319:        blp->b_flag &= ~BFCHG;          /* Blow away old.        */
1.28      vincent   320:        blp->b_flag |= BFREADONLY;
1.1       deraadt   321:
1.19      art       322:        listbuf_ncol = ncol;            /* cache ncol for listbuf_goto_buffer */
                    323:
1.11      art       324:        if (addlinef(blp, "%-*s%s", w, " MR Buffer", "Size   File") == FALSE ||
                    325:            addlinef(blp, "%-*s%s", w, " -- ------", "----   ----") == FALSE)
1.37      db        326:                return (NULL);
1.11      art       327:
                    328:        for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
                    329:                RSIZE nbytes;
                    330:
1.3       millert   331:                nbytes = 0;                     /* Count bytes in buf.   */
1.1       deraadt   332:                if (bp != blp) {
1.63      kjell     333:                        lp = bfirstlp(bp);
1.62      kjell     334:                        while (lp != bp->b_headp) {
1.3       millert   335:                                nbytes += llength(lp) + 1;
1.1       deraadt   336:                                lp = lforw(lp);
                    337:                        }
1.3       millert   338:                        if (nbytes)
                    339:                                nbytes--;       /* no bonus newline      */
1.1       deraadt   340:                }
1.11      art       341:
1.19      art       342:                if (addlinef(blp, "%c%c%c %-*.*s%c%-6d %-*s",
1.11      art       343:                    (bp == curbp) ? '.' : ' ',  /* current buffer ? */
                    344:                    ((bp->b_flag & BFCHG) != 0) ? '*' : ' ',    /* changed ? */
1.27      vincent   345:                    ((bp->b_flag & BFREADONLY) != 0) ? ' ' : '*',
1.19      art       346:                    w - 5,              /* four chars already written */
                    347:                    w - 5,              /* four chars already written */
1.11      art       348:                    bp->b_bname,        /* buffer name */
1.19      art       349:                    strlen(bp->b_bname) < w - 5 ? ' ' : '$', /* truncated? */
1.11      art       350:                    nbytes,             /* buffer size */
                    351:                    w - 7,              /* seven chars already written */
                    352:                    bp->b_fname) == FALSE)
1.37      db        353:                        return (NULL);
1.1       deraadt   354:        }
1.63      kjell     355:        blp->b_dotp = bfirstlp(blp);            /* put dot at beginning of
1.3       millert   356:                                                 * buffer */
1.1       deraadt   357:        blp->b_doto = 0;
1.37      db        358:        return (blp);                           /* All done              */
1.19      art       359: }
                    360:
                    361: static int
                    362: listbuf_goto_buffer(int f, int n)
                    363: {
1.39      jason     364:        return (listbuf_goto_buffer_helper(f, n, 0));
                    365: }
                    366:
                    367: static int
                    368: listbuf_goto_buffer_one(int f, int n)
                    369: {
                    370:        return (listbuf_goto_buffer_helper(f, n, 1));
                    371: }
                    372:
                    373: static int
                    374: listbuf_goto_buffer_helper(int f, int n, int only)
                    375: {
1.52      deraadt   376:        struct buffer   *bp;
                    377:        struct mgwin    *wp;
                    378:        char            *line = NULL;
                    379:        int              i, ret = FALSE;
1.19      art       380:
                    381:        if (curwp->w_dotp->l_text[listbuf_ncol/2 - 1] == '$') {
1.93      lum       382:                dobeep();
1.19      art       383:                ewprintf("buffer name truncated");
1.37      db        384:                return (FALSE);
1.19      art       385:        }
                    386:
                    387:        if ((line = malloc(listbuf_ncol/2)) == NULL)
1.37      db        388:                return (FALSE);
1.19      art       389:
                    390:        memcpy(line, curwp->w_dotp->l_text + 4, listbuf_ncol/2 - 5);
                    391:        for (i = listbuf_ncol/2 - 6; i > 0; i--) {
                    392:                if (line[i] != ' ') {
                    393:                        line[i + 1] = '\0';
                    394:                        break;
                    395:                }
                    396:        }
1.37      db        397:        if (i == 0)
1.42      cloder    398:                goto cleanup;
1.19      art       399:
                    400:        for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
                    401:                if (strcmp(bp->b_bname, line) == 0)
                    402:                        break;
                    403:        }
1.37      db        404:        if (bp == NULL)
1.42      cloder    405:                goto cleanup;
1.37      db        406:
1.71      kjell     407:        if ((wp = popbuf(bp, WNONE)) == NULL)
1.42      cloder    408:                goto cleanup;
1.19      art       409:        curbp = bp;
                    410:        curwp = wp;
1.39      jason     411:
                    412:        if (only)
1.42      cloder    413:                ret = (onlywind(f, n));
                    414:        else
                    415:                ret = TRUE;
                    416:
                    417: cleanup:
                    418:        free(line);
1.19      art       419:
1.42      cloder    420:        return (ret);
1.1       deraadt   421: }
                    422:
                    423: /*
1.47      kjell     424:  * The argument "fmt" points to a format string.  Append this line to the
1.3       millert   425:  * buffer. Handcraft the EOL on the end.  Return TRUE if it worked and
1.1       deraadt   426:  * FALSE if you ran out of room.
                    427:  */
1.3       millert   428: int
1.52      deraadt   429: addlinef(struct buffer *bp, char *fmt, ...)
1.3       millert   430: {
1.52      deraadt   431:        va_list          ap;
                    432:        struct line     *lp;
1.1       deraadt   433:
1.30      vincent   434:        if ((lp = lalloc(0)) == NULL)
                    435:                return (FALSE);
1.8       art       436:        va_start(ap, fmt);
1.30      vincent   437:        if (vasprintf(&lp->l_text, fmt, ap) == -1) {
                    438:                lfree(lp);
1.16      deraadt   439:                va_end(ap);
1.30      vincent   440:                return (FALSE);
1.16      deraadt   441:        }
1.30      vincent   442:        lp->l_used = strlen(lp->l_text);
1.8       art       443:        va_end(ap);
                    444:
1.62      kjell     445:        bp->b_headp->l_bp->l_fp = lp;           /* Hook onto the end     */
                    446:        lp->l_bp = bp->b_headp->l_bp;
                    447:        bp->b_headp->l_bp = lp;
                    448:        lp->l_fp = bp->b_headp;
1.60      kjell     449:        bp->b_lines++;
1.8       art       450:
1.37      db        451:        return (TRUE);
1.1       deraadt   452: }
                    453:
                    454: /*
1.3       millert   455:  * Look through the list of buffers, giving the user a chance to save them.
1.51      kjell     456:  * Return TRUE if there are any changed buffers afterwards.  Buffers that don't
                    457:  * have an associated file don't count.  Return FALSE if there are no changed
                    458:  * buffers.  Return ABORT if an error occurs or if the user presses c-g.
1.3       millert   459:  */
                    460: int
1.26      vincent   461: anycb(int f)
1.3       millert   462: {
1.52      deraadt   463:        struct buffer   *bp;
1.79      lum       464:        int              s = FALSE, save = FALSE, save2 = FALSE, ret;
1.53      kjell     465:        char             pbuf[NFILEN + 11];
1.1       deraadt   466:
                    467:        for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
1.94      bcallah   468:                if (*(bp->b_fname) != '\0' && (bp->b_flag & BFCHG) != 0) {
1.53      kjell     469:                        ret = snprintf(pbuf, sizeof(pbuf), "Save file %s",
1.17      deraadt   470:                            bp->b_fname);
1.53      kjell     471:                        if (ret < 0 || ret >= sizeof(pbuf)) {
1.93      lum       472:                                dobeep();
1.51      kjell     473:                                ewprintf("Error: filename too long!");
1.81      lum       474:                                return (UERROR);
1.51      kjell     475:                        }
1.53      kjell     476:                        if ((f == TRUE || (save = eyorn(pbuf)) == TRUE) &&
1.79      lum       477:                            (save2 = buffsave(bp)) == TRUE) {
1.1       deraadt   478:                                bp->b_flag &= ~BFCHG;
                    479:                                upmodes(bp);
1.79      lum       480:                        } else {
                    481:                                if (save2 == FIOERR)
                    482:                                        return (save2);
1.3       millert   483:                                s = TRUE;
1.79      lum       484:                        }
1.3       millert   485:                        if (save == ABORT)
                    486:                                return (save);
1.1       deraadt   487:                        save = TRUE;
                    488:                }
                    489:        }
                    490:        if (save == FALSE /* && kbdmop == NULL */ )     /* experimental */
                    491:                ewprintf("(No files need saving)");
1.37      db        492:        return (s);
1.1       deraadt   493: }
                    494:
                    495: /*
                    496:  * Search for a buffer, by name.
                    497:  * If not found, and the "cflag" is TRUE,
1.57      kjell     498:  * create a new buffer. Return pointer to the found
                    499:  * (or new) buffer.
1.1       deraadt   500:  */
1.52      deraadt   501: struct buffer *
1.26      vincent   502: bfind(const char *bname, int cflag)
1.3       millert   503: {
1.52      deraadt   504:        struct buffer   *bp;
1.1       deraadt   505:
                    506:        bp = bheadp;
                    507:        while (bp != NULL) {
1.7       art       508:                if (strcmp(bname, bp->b_bname) == 0)
1.37      db        509:                        return (bp);
1.1       deraadt   510:                bp = bp->b_bufp;
                    511:        }
1.3       millert   512:        if (cflag != TRUE)
1.37      db        513:                return (NULL);
1.29      vincent   514:
1.65      kjell     515:        bp = bnew(bname);
1.57      kjell     516:
                    517:        return (bp);
                    518: }
                    519:
                    520: /*
                    521:  * Create a new buffer and put it in the list of
1.66      deraadt   522:  * all buffers.
1.57      kjell     523:  */
                    524: static struct buffer *
1.65      kjell     525: bnew(const char *bname)
1.57      kjell     526: {
1.66      deraadt   527:        struct buffer   *bp;
1.57      kjell     528:        struct line     *lp;
                    529:        int              i;
1.72      kjell     530:        size_t          len;
1.57      kjell     531:
1.52      deraadt   532:        bp = calloc(1, sizeof(struct buffer));
1.32      vincent   533:        if (bp == NULL) {
1.93      lum       534:                dobeep();
1.52      deraadt   535:                ewprintf("Can't get %d bytes", sizeof(struct buffer));
1.37      db        536:                return (NULL);
1.1       deraadt   537:        }
                    538:        if ((lp = lalloc(0)) == NULL) {
1.29      vincent   539:                free(bp);
1.37      db        540:                return (NULL);
1.1       deraadt   541:        }
1.3       millert   542:        bp->b_altb = bp->b_bufp = NULL;
                    543:        bp->b_dotp = lp;
                    544:        bp->b_doto = 0;
1.1       deraadt   545:        bp->b_markp = NULL;
                    546:        bp->b_marko = 0;
1.3       millert   547:        bp->b_flag = defb_flag;
1.72      kjell     548:        /* if buffer name starts and ends with '*', we ignore changes */
                    549:        len = strlen(bname);
                    550:        if (len) {
                    551:                if (bname[0] == '*' && bname[len - 1] == '*')
                    552:                        bp->b_flag |= BFIGNDIRTY;
                    553:        }
1.3       millert   554:        bp->b_nwnd = 0;
1.62      kjell     555:        bp->b_headp = lp;
1.1       deraadt   556:        bp->b_nmodes = defb_nmodes;
1.74      oga       557:        TAILQ_INIT(&bp->b_undo);
1.46      kjell     558:        bp->b_undoptr = NULL;
1.1       deraadt   559:        i = 0;
                    560:        do {
1.3       millert   561:                bp->b_modes[i] = defb_modes[i];
                    562:        } while (i++ < defb_nmodes);
1.1       deraadt   563:        bp->b_fname[0] = '\0';
1.57      kjell     564:        bp->b_cwd[0] = '\0';
1.1       deraadt   565:        bzero(&bp->b_fi, sizeof(bp->b_fi));
                    566:        lp->l_fp = lp;
                    567:        lp->l_bp = lp;
                    568:        bp->b_bufp = bheadp;
                    569:        bheadp = bp;
1.60      kjell     570:        bp->b_dotline = bp->b_markline = 1;
1.61      kjell     571:        bp->b_lines = 1;
1.65      kjell     572:        if ((bp->b_bname = strdup(bname)) == NULL) {
1.93      lum       573:                dobeep();
1.65      kjell     574:                ewprintf("Can't get %d bytes", strlen(bname) + 1);
                    575:                return (NULL);
                    576:        }
1.57      kjell     577:
1.37      db        578:        return (bp);
1.1       deraadt   579: }
                    580:
                    581: /*
                    582:  * This routine blows away all of the text
                    583:  * in a buffer. If the buffer is marked as changed
                    584:  * then we ask if it is ok to blow it away; this is
                    585:  * to save the user the grief of losing text. The
                    586:  * window chain is nearly always wrong if this gets
                    587:  * called; the caller must arrange for the updates
                    588:  * that are required. Return TRUE if everything
                    589:  * looks good.
                    590:  */
1.3       millert   591: int
1.52      deraadt   592: bclear(struct buffer *bp)
1.3       millert   593: {
1.52      deraadt   594:        struct line     *lp;
                    595:        int              s;
1.1       deraadt   596:
1.72      kjell     597:        /* Has buffer changed, and do we care? */
                    598:        if (!(bp->b_flag & BFIGNDIRTY) && (bp->b_flag & BFCHG) != 0 &&
1.21      deraadt   599:            (s = eyesno("Buffer modified; kill anyway")) != TRUE)
1.1       deraadt   600:                return (s);
1.3       millert   601:        bp->b_flag &= ~BFCHG;   /* Not changed           */
1.62      kjell     602:        while ((lp = lforw(bp->b_headp)) != bp->b_headp)
1.1       deraadt   603:                lfree(lp);
1.62      kjell     604:        bp->b_dotp = bp->b_headp;       /* Fix dot */
1.3       millert   605:        bp->b_doto = 0;
                    606:        bp->b_markp = NULL;     /* Invalidate "mark"     */
1.1       deraadt   607:        bp->b_marko = 0;
1.60      kjell     608:        bp->b_dotline = bp->b_markline = 1;
1.61      kjell     609:        bp->b_lines = 1;
1.60      kjell     610:
1.37      db        611:        return (TRUE);
1.1       deraadt   612: }
                    613:
                    614: /*
                    615:  * Display the given buffer in the given window. Flags indicated
1.68      kjell     616:  * action on redisplay. Update modified flag so insert loop can check it.
1.1       deraadt   617:  */
1.3       millert   618: int
1.52      deraadt   619: showbuffer(struct buffer *bp, struct mgwin *wp, int flags)
1.3       millert   620: {
1.52      deraadt   621:        struct buffer   *obp;
                    622:        struct mgwin    *owp;
1.1       deraadt   623:
1.68      kjell     624:        /* Ensure file has not been modified elsewhere */
                    625:        if (fchecktime(bp) != TRUE)
                    626:                bp->b_flag |= BFDIRTY;
                    627:
1.32      vincent   628:        if (wp->w_bufp == bp) { /* Easy case! */
1.70      kjell     629:                wp->w_rflag |= flags;
1.37      db        630:                return (TRUE);
1.1       deraadt   631:        }
1.37      db        632:        /* First, detach the old buffer from the window */
1.1       deraadt   633:        if ((bp->b_altb = obp = wp->w_bufp) != NULL) {
                    634:                if (--obp->b_nwnd == 0) {
1.3       millert   635:                        obp->b_dotp = wp->w_dotp;
                    636:                        obp->b_doto = wp->w_doto;
1.1       deraadt   637:                        obp->b_markp = wp->w_markp;
                    638:                        obp->b_marko = wp->w_marko;
1.60      kjell     639:                        obp->b_dotline = wp->w_dotline;
                    640:                        obp->b_markline = wp->w_markline;
1.1       deraadt   641:                }
                    642:        }
                    643:        /* Now, attach the new buffer to the window */
                    644:        wp->w_bufp = bp;
                    645:
1.3       millert   646:        if (bp->b_nwnd++ == 0) {        /* First use.            */
                    647:                wp->w_dotp = bp->b_dotp;
                    648:                wp->w_doto = bp->b_doto;
1.1       deraadt   649:                wp->w_markp = bp->b_markp;
                    650:                wp->w_marko = bp->b_marko;
1.60      kjell     651:                wp->w_dotline = bp->b_dotline;
                    652:                wp->w_markline = bp->b_markline;
1.1       deraadt   653:        } else
1.3       millert   654:                /* already on screen, steal values from other window */
1.1       deraadt   655:                for (owp = wheadp; owp != NULL; owp = wp->w_wndp)
                    656:                        if (wp->w_bufp == bp && owp != wp) {
1.3       millert   657:                                wp->w_dotp = owp->w_dotp;
                    658:                                wp->w_doto = owp->w_doto;
1.1       deraadt   659:                                wp->w_markp = owp->w_markp;
                    660:                                wp->w_marko = owp->w_marko;
1.60      kjell     661:                                wp->w_dotline = owp->w_dotline;
1.64      kjell     662:                                wp->w_markline = owp->w_markline;
1.1       deraadt   663:                                break;
                    664:                        }
1.70      kjell     665:        wp->w_rflag |= WFMODE | flags;
1.56      kjell     666:        return (TRUE);
                    667: }
                    668:
                    669: /*
                    670:  * Augment a buffer name with a number, if necessary
                    671:  *
                    672:  * If more than one file of the same basename() is open,
                    673:  * the additional buffers are named "file<2>", "file<3>", and
                    674:  * so forth.  This function adjusts a buffer name to
                    675:  * include the number, if necessary.
                    676:  */
                    677: int
1.57      kjell     678: augbname(char *bn, const char *fn, size_t bs)
1.56      kjell     679: {
1.66      deraadt   680:        int      count;
1.56      kjell     681:        size_t   remain, len;
                    682:
1.77      kjell     683:        if ((len = xbasename(bn, fn, bs)) >= bs)
1.56      kjell     684:                return (FALSE);
                    685:
                    686:        remain = bs - len;
                    687:        for (count = 2; bfind(bn, FALSE) != NULL; count++)
                    688:                snprintf(bn + len, remain, "<%d>", count);
                    689:
1.37      db        690:        return (TRUE);
1.1       deraadt   691: }
                    692:
                    693: /*
                    694:  * Pop the buffer we got passed onto the screen.
                    695:  * Returns a status.
                    696:  */
1.52      deraadt   697: struct mgwin *
1.71      kjell     698: popbuf(struct buffer *bp, int flags)
1.3       millert   699: {
1.52      deraadt   700:        struct mgwin    *wp;
1.1       deraadt   701:
1.3       millert   702:        if (bp->b_nwnd == 0) {  /* Not on screen yet.    */
1.71      kjell     703:                /*
                    704:                 * Pick a window for a pop-up.
                    705:                 * If only one window, split the screen.
                    706:                 * Flag the new window as ephemeral
                    707:                 */
                    708:                if (wheadp->w_wndp == NULL &&
                    709:                    splitwind(FFOTHARG, flags) == FALSE)
                    710:                        return (NULL);
                    711:
                    712:                /*
                    713:                 * Pick the uppermost window that isn't
                    714:                 * the current window. An LRU algorithm
                    715:                 * might be better. Return a pointer, or NULL on error.
                    716:                 */
                    717:                wp = wheadp;
                    718:
                    719:                while (wp != NULL && wp == curwp)
                    720:                        wp = wp->w_wndp;
1.1       deraadt   721:        } else
                    722:                for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
                    723:                        if (wp->w_bufp == bp) {
1.70      kjell     724:                                wp->w_rflag |= WFFULL | WFFRAME;
1.37      db        725:                                return (wp);
1.1       deraadt   726:                        }
1.58      kjell     727:        if (showbuffer(bp, wp, WFFULL) != TRUE)
1.37      db        728:                return (NULL);
                    729:        return (wp);
1.1       deraadt   730: }
                    731:
                    732: /*
                    733:  * Insert another buffer at dot.  Very useful.
                    734:  */
1.3       millert   735: /* ARGSUSED */
                    736: int
1.26      vincent   737: bufferinsert(int f, int n)
1.1       deraadt   738: {
1.52      deraadt   739:        struct buffer *bp;
                    740:        struct line   *clp;
1.36      deraadt   741:        int     clo, nline;
                    742:        char    bufn[NBUFN], *bufp;
1.1       deraadt   743:
                    744:        /* Get buffer to use from user */
                    745:        if (curbp->b_altb != NULL)
1.34      vincent   746:                bufp = eread("Insert buffer: (default %s) ", bufn, NBUFN,
1.40      kjell     747:                    EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
1.1       deraadt   748:        else
1.38      cloder    749:                bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF);
1.34      vincent   750:        if (bufp == NULL)
1.37      db        751:                return (ABORT);
1.34      vincent   752:        if (bufp[0] == '\0' && curbp->b_altb != NULL)
1.3       millert   753:                bp = curbp->b_altb;
                    754:        else if ((bp = bfind(bufn, FALSE)) == NULL)
1.37      db        755:                return (FALSE);
1.1       deraadt   756:
1.3       millert   757:        if (bp == curbp) {
1.93      lum       758:                dobeep();
1.1       deraadt   759:                ewprintf("Cannot insert buffer into self");
1.37      db        760:                return (FALSE);
1.1       deraadt   761:        }
                    762:        /* insert the buffer */
                    763:        nline = 0;
1.63      kjell     764:        clp = bfirstlp(bp);
1.3       millert   765:        for (;;) {
1.1       deraadt   766:                for (clo = 0; clo < llength(clp); clo++)
                    767:                        if (linsert(1, lgetc(clp, clo)) == FALSE)
1.37      db        768:                                return (FALSE);
1.62      kjell     769:                if ((clp = lforw(clp)) == bp->b_headp)
1.3       millert   770:                        break;
1.95      bcallah   771:                if (enewline(FFRAND, 1) == FALSE)       /* fake newline */
1.37      db        772:                        return (FALSE);
1.1       deraadt   773:                nline++;
                    774:        }
1.3       millert   775:        if (nline == 1)
                    776:                ewprintf("[Inserted 1 line]");
                    777:        else
                    778:                ewprintf("[Inserted %d lines]", nline);
1.1       deraadt   779:
1.37      db        780:        clp = curwp->w_linep;           /* cosmetic adjustment  */
1.3       millert   781:        if (curwp->w_dotp == clp) {     /* for offscreen insert */
1.62      kjell     782:                while (nline-- && lback(clp) != curbp->b_headp)
1.1       deraadt   783:                        clp = lback(clp);
1.37      db        784:                curwp->w_linep = clp;   /* adjust framing.      */
1.70      kjell     785:                curwp->w_rflag |= WFFULL;
1.1       deraadt   786:        }
                    787:        return (TRUE);
                    788: }
                    789:
                    790: /*
                    791:  * Turn off the dirty bit on this buffer.
                    792:  */
1.3       millert   793: /* ARGSUSED */
                    794: int
1.26      vincent   795: notmodified(int f, int n)
1.1       deraadt   796: {
1.52      deraadt   797:        struct mgwin *wp;
1.1       deraadt   798:
                    799:        curbp->b_flag &= ~BFCHG;
1.3       millert   800:        wp = wheadp;            /* Update mode lines.    */
1.1       deraadt   801:        while (wp != NULL) {
                    802:                if (wp->w_bufp == curbp)
1.70      kjell     803:                        wp->w_rflag |= WFMODE;
1.1       deraadt   804:                wp = wp->w_wndp;
                    805:        }
                    806:        ewprintf("Modification-flag cleared");
1.37      db        807:        return (TRUE);
1.1       deraadt   808: }
                    809:
                    810: /*
1.78      lum       811:  * Popbuf and set all windows to top of buffer.
1.1       deraadt   812:  */
1.3       millert   813: int
1.71      kjell     814: popbuftop(struct buffer *bp, int flags)
1.1       deraadt   815: {
1.52      deraadt   816:        struct mgwin *wp;
1.1       deraadt   817:
1.63      kjell     818:        bp->b_dotp = bfirstlp(bp);
1.3       millert   819:        bp->b_doto = 0;
                    820:        if (bp->b_nwnd != 0) {
                    821:                for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
                    822:                        if (wp->w_bufp == bp) {
                    823:                                wp->w_dotp = bp->b_dotp;
                    824:                                wp->w_doto = 0;
1.70      kjell     825:                                wp->w_rflag |= WFFULL;
1.3       millert   826:                        }
                    827:        }
1.71      kjell     828:        return (popbuf(bp, flags) != NULL);
1.1       deraadt   829: }
1.57      kjell     830:
                    831: /*
                    832:  * Return the working directory for the current buffer, terminated
                    833:  * with a '/'. First, try to extract it from the current buffer's
                    834:  * filename. If that fails, use global cwd.
                    835:  */
                    836: int
                    837: getbufcwd(char *path, size_t plen)
                    838: {
                    839:        char cwd[NFILEN];
                    840:
                    841:        if (plen == 0)
                    842:                return (FALSE);
                    843:
1.67      kjell     844:        if (globalwd == FALSE && curbp->b_cwd[0] != '\0') {
1.57      kjell     845:                (void)strlcpy(path, curbp->b_cwd, plen);
                    846:        } else {
                    847:                if (getcwdir(cwd, sizeof(cwd)) == FALSE)
                    848:                        goto error;
                    849:                (void)strlcpy(path, cwd, plen);
                    850:        }
                    851:        return (TRUE);
                    852: error:
                    853:        path[0] = '\0';
                    854:        return (FALSE);
                    855: }
                    856:
1.68      kjell     857: /*
1.69      kjell     858:  * Ensures a buffer has not been modified elsewhere; e.g. on disk.
                    859:  * Prompt the user if it has.
                    860:  * Returns TRUE if it has NOT (i.e. buffer is ok to edit).
                    861:  * FALSE or ABORT otherwise
1.68      kjell     862:  */
                    863: int
                    864: checkdirty(struct buffer *bp)
                    865: {
                    866:        int s;
1.83      florian   867:
                    868:        if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0)
                    869:                if (fchecktime(bp) != TRUE)
                    870:                        bp->b_flag |= BFDIRTY;
                    871:
1.68      kjell     872:        if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) {
1.84      florian   873:                s = eynorr("File changed on disk; really edit the buffer");
                    874:                switch (s) {
                    875:                case TRUE:
                    876:                        bp->b_flag &= ~BFDIRTY;
                    877:                        bp->b_flag |= BFIGNDIRTY;
                    878:                        return (TRUE);
                    879:                case REVERT:
                    880:                        dorevert();
                    881:                        return (FALSE);
                    882:                default:
1.68      kjell     883:                        return (s);
1.84      florian   884:                }
1.68      kjell     885:        }
                    886:
                    887:        return (TRUE);
                    888: }
1.82      jasper    889:
                    890: /*
                    891:  * Revert the current buffer to whatever is on disk.
                    892:  */
                    893: /* ARGSUSED */
                    894: int
                    895: revertbuffer(int f, int n)
                    896: {
                    897:        char fbuf[NFILEN + 32];
                    898:
1.85      florian   899:        if (curbp->b_fname[0] == 0) {
1.93      lum       900:                dobeep();
1.82      jasper    901:                ewprintf("Cannot revert buffer not associated with any files.");
                    902:                return (FALSE);
                    903:        }
                    904:
1.85      florian   905:        snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s",
                    906:            curbp->b_fname);
1.82      jasper    907:
1.85      florian   908:        if (eyorn(fbuf) == TRUE)
1.84      florian   909:                return dorevert();
1.82      jasper    910:
1.84      florian   911:        return (FALSE);
                    912: }
1.82      jasper    913:
1.84      florian   914: int
1.86      haesbaer  915: dorevert(void)
1.84      florian   916: {
                    917:        int lineno;
1.89      florian   918:        struct undo_rec *rec;
1.82      jasper    919:
1.85      florian   920:        if (access(curbp->b_fname, F_OK|R_OK) != 0) {
1.93      lum       921:                dobeep();
1.84      florian   922:                if (errno == ENOENT)
                    923:                        ewprintf("File %s no longer exists!",
1.85      florian   924:                            curbp->b_fname);
1.84      florian   925:                else
                    926:                        ewprintf("File %s is no longer readable!",
1.85      florian   927:                            curbp->b_fname);
1.84      florian   928:                return (FALSE);
1.82      jasper    929:        }
                    930:
1.84      florian   931:        /* Save our current line, so we can go back after reloading. */
1.85      florian   932:        lineno = curwp->w_dotline;
1.84      florian   933:
                    934:        /* Prevent readin from asking if we want to kill the buffer. */
                    935:        curbp->b_flag &= ~BFCHG;
1.89      florian   936:
                    937:        /* Clean up undo memory */
                    938:        while ((rec = TAILQ_FIRST(&curbp->b_undo))) {
                    939:                TAILQ_REMOVE(&curbp->b_undo, rec, next);
                    940:                free_undo_record(rec);
                    941:        }
1.84      florian   942:
1.85      florian   943:        if (readin(curbp->b_fname))
1.84      florian   944:                return(setlineno(lineno));
1.82      jasper    945:        return (FALSE);
1.88      florian   946: }
                    947:
                    948: /*
                    949:  * Diff the current buffer to what is on disk.
                    950:  */
                    951: /*ARGSUSED */
                    952: int
                    953: diffbuffer(int f, int n)
                    954: {
                    955:        struct buffer   *bp;
                    956:        struct line     *lp, *lpend;
                    957:        size_t           len;
                    958:        int              ret;
                    959:        char            *text, *ttext;
                    960:        char            * const argv[] =
                    961:            {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL};
                    962:
                    963:        len = 0;
                    964:
                    965:        /* C-u is not supported */
                    966:        if (n > 1)
                    967:                return (ABORT);
                    968:
                    969:        if (access(DIFFTOOL, X_OK) != 0) {
1.93      lum       970:                dobeep();
1.88      florian   971:                ewprintf("%s not found or not executable.", DIFFTOOL);
                    972:                return (FALSE);
                    973:        }
                    974:
                    975:        if (curbp->b_fname[0] == 0) {
1.93      lum       976:                dobeep();
1.88      florian   977:                ewprintf("Cannot diff buffer not associated with any files.");
                    978:                return (FALSE);
                    979:        }
                    980:
                    981:        lpend = curbp->b_headp;
                    982:        for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
                    983:                len+=llength(lp);
                    984:                if (lforw(lp) != lpend)         /* no implied \n on last line */
                    985:                        len++;
                    986:        }
                    987:        if ((text = calloc(len + 1, sizeof(char))) == NULL) {
1.93      lum       988:                dobeep();
1.88      florian   989:                ewprintf("Cannot allocate memory.");
                    990:                return (FALSE);
                    991:        }
                    992:        ttext = text;
                    993:
                    994:        for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
                    995:                if (llength(lp) != 0) {
                    996:                        memcpy(ttext, ltext(lp), llength(lp));
                    997:                        ttext += llength(lp);
                    998:                }
                    999:                if (lforw(lp) != lpend)         /* no implied \n on last line */
                   1000:                        *ttext++ = '\n';
                   1001:        }
                   1002:
                   1003:        bp = bfind("*Diff*", TRUE);
                   1004:        bp->b_flag |= BFREADONLY;
                   1005:        if (bclear(bp) != TRUE) {
                   1006:                free(text);
                   1007:                return (FALSE);
                   1008:        }
                   1009:
                   1010:        ret = pipeio(DIFFTOOL, argv, text, len, bp);
                   1011:
                   1012:        if (ret == TRUE) {
                   1013:                eerase();
                   1014:                if (lforw(bp->b_headp) == bp->b_headp)
                   1015:                        addline(bp, "Diff finished (no differences).");
                   1016:        }
                   1017:
                   1018:        free(text);
                   1019:        return (ret);
1.92      lum      1020: }
                   1021:
                   1022: /*
                   1023:  * Given a file name, either find the buffer it uses, or create a new
                   1024:  * empty buffer to put it in.
                   1025:  */
                   1026: struct buffer *
                   1027: findbuffer(char *fn)
                   1028: {
                   1029:        struct buffer   *bp;
                   1030:        char            bname[NBUFN], fname[NBUFN];
                   1031:
                   1032:        if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) {
1.93      lum      1033:                dobeep();
1.92      lum      1034:                ewprintf("filename too long");
                   1035:                return (NULL);
                   1036:        }
                   1037:
                   1038:        for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
                   1039:                if (strcmp(bp->b_fname, fname) == 0)
                   1040:                        return (bp);
                   1041:        }
                   1042:        /* Not found. Create a new one, adjusting name first */
                   1043:        if (augbname(bname, fname, sizeof(bname)) == FALSE)
                   1044:                return (NULL);
                   1045:
                   1046:        bp = bfind(bname, TRUE);
                   1047:        return (bp);
1.82      jasper   1048: }