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