Annotation of src/usr.bin/mg/buffer.c, Revision 1.102
1.102 ! lum 1: /* $OpenBSD: buffer.c,v 1.101 2016/08/31 12:22:28 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;
31:
1.50 kjell 32: /* ARGSUSED */
1.27 vincent 33: int
1.31 vincent 34: togglereadonly(int f, int n)
1.27 vincent 35: {
1.68 kjell 36: int s;
37:
38: if ((s = checkdirty(curbp)) != TRUE)
39: return (s);
1.47 kjell 40: if (!(curbp->b_flag & BFREADONLY))
1.27 vincent 41: curbp->b_flag |= BFREADONLY;
1.47 kjell 42: else {
1.68 kjell 43: curbp->b_flag &= ~BFREADONLY;
1.27 vincent 44: if (curbp->b_flag & BFCHG)
45: ewprintf("Warning: Buffer was modified");
46: }
1.70 kjell 47: curwp->w_rflag |= WFMODE;
1.27 vincent 48:
1.47 kjell 49: return (TRUE);
1.27 vincent 50: }
51:
1.73 deraadt 52: /* Switch to the named buffer.
53: * If no name supplied, switch to the default (alternate) buffer.
54: */
55: int
56: usebufname(const char *bufp)
57: {
1.101 lum 58: struct buffer *bp = NULL;
1.73 deraadt 59:
1.101 lum 60: if (bufp == NULL) {
61: if ((bp = bfind("*scratch*", TRUE)) == NULL)
62: return(FALSE);
63: } else if (bufp[0] == '\0' && curbp->b_altb != NULL)
64: bp = curbp->b_altb;
1.73 deraadt 65: else if ((bp = bfind(bufp, TRUE)) == NULL)
66: return (FALSE);
67:
68: /* and put it in current window */
69: curbp = bp;
70: return (showbuffer(bp, curwp, WFFRAME | WFFULL));
71: }
72:
1.1 deraadt 73: /*
74: * Attach a buffer to a window. The values of dot and mark come
75: * from the buffer if the use count is 0. Otherwise, they come
76: * from some other window. *scratch* is the default alternate
77: * buffer.
78: */
1.3 millert 79: /* ARGSUSED */
80: int
1.26 vincent 81: usebuffer(int f, int n)
1.1 deraadt 82: {
1.34 vincent 83: char bufn[NBUFN], *bufp;
1.1 deraadt 84:
85: /* Get buffer to use from user */
1.101 lum 86: if (curbp->b_altb == NULL)
1.34 vincent 87: bufp = eread("Switch to buffer: ", bufn, NBUFN, EFNEW | EFBUF);
1.1 deraadt 88: else
1.34 vincent 89: bufp = eread("Switch to buffer: (default %s) ", bufn, NBUFN,
1.55 deraadt 90: EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
1.1 deraadt 91:
1.72 kjell 92: return (usebufname(bufp));
1.1 deraadt 93: }
94:
95: /*
96: * pop to buffer asked for by the user.
97: */
1.3 millert 98: /* ARGSUSED */
99: int
1.26 vincent 100: poptobuffer(int f, int n)
1.1 deraadt 101: {
1.52 deraadt 102: struct buffer *bp;
103: struct mgwin *wp;
1.34 vincent 104: char bufn[NBUFN], *bufp;
1.1 deraadt 105:
106: /* Get buffer to use from user */
1.21 deraadt 107: if ((curbp->b_altb == NULL) &&
108: ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
1.34 vincent 109: bufp = eread("Switch to buffer in other window: ", bufn, NBUFN,
1.55 deraadt 110: EFNEW | EFBUF);
1.1 deraadt 111: else
1.34 vincent 112: bufp = eread("Switch to buffer in other window: (default %s) ",
1.55 deraadt 113: bufn, NBUFN, EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
1.34 vincent 114: if (bufp == NULL)
1.37 db 115: return (ABORT);
1.44 kjell 116: if (bufp[0] == '\0' && curbp->b_altb != NULL)
1.3 millert 117: bp = curbp->b_altb;
118: else if ((bp = bfind(bufn, TRUE)) == NULL)
1.37 db 119: return (FALSE);
1.59 kjell 120: if (bp == curbp)
121: return (splitwind(f, n));
1.71 kjell 122: /* and put it in a new, non-ephemeral window */
123: if ((wp = popbuf(bp, WNONE)) == NULL)
1.37 db 124: return (FALSE);
1.1 deraadt 125: curbp = bp;
126: curwp = wp;
1.37 db 127: return (TRUE);
1.1 deraadt 128: }
129:
130: /*
131: * Dispose of a buffer, by name.
1.91 lum 132: * Ask for the name (unless called by dired mode). Look it up (don't
133: * get too upset if it isn't there at all!). Clear the buffer (ask
1.1 deraadt 134: * if the buffer has been changed). Then free the header
1.91 lum 135: * line and the buffer header. Bound to "C-x k".
1.1 deraadt 136: */
1.3 millert 137: /* ARGSUSED */
138: int
1.35 jfb 139: killbuffer_cmd(int f, int n)
1.1 deraadt 140: {
1.52 deraadt 141: struct buffer *bp;
1.34 vincent 142: char bufn[NBUFN], *bufp;
1.3 millert 143:
1.91 lum 144: if (f & FFRAND) /* dired mode 'q' */
145: bp = curbp;
146: else if ((bufp = eread("Kill buffer: (default %s) ", bufn, NBUFN,
1.40 kjell 147: EFNUL | EFNEW | EFBUF, curbp->b_bname)) == NULL)
1.37 db 148: return (ABORT);
1.44 kjell 149: else if (bufp[0] == '\0')
1.3 millert 150: bp = curbp;
151: else if ((bp = bfind(bufn, FALSE)) == NULL)
1.37 db 152: return (FALSE);
153: return (killbuffer(bp));
1.35 jfb 154: }
155:
156: int
1.52 deraadt 157: killbuffer(struct buffer *bp)
1.35 jfb 158: {
1.52 deraadt 159: struct buffer *bp1;
160: struct buffer *bp2;
161: struct mgwin *wp;
1.41 kjell 162: int s;
1.90 florian 163: struct undo_rec *rec;
1.3 millert 164:
165: /*
1.37 db 166: * Find some other buffer to display. Try the alternate buffer,
1.3 millert 167: * then the first different buffer in the buffer list. If there's
168: * only one buffer, create buffer *scratch* and make it the alternate
169: * buffer. Return if *scratch* is only buffer...
1.1 deraadt 170: */
171: if ((bp1 = bp->b_altb) == NULL) {
1.101 lum 172: /* only one buffer. see if it's *scratch* */
173: if (bp == bfind("*scratch*", FALSE))
174: return (TRUE);
175: /* create *scratch* for alternate buffer */
176: if ((bp1 = bfind("*scratch*", TRUE)) == NULL)
177: return (FALSE);
1.1 deraadt 178: }
1.41 kjell 179: if ((s = bclear(bp)) != TRUE)
180: return (s);
1.1 deraadt 181: for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) {
1.3 millert 182: if (wp->w_bufp == bp) {
183: bp2 = bp1->b_altb; /* save alternate buffer */
1.58 kjell 184: if (showbuffer(bp1, wp, WFMODE | WFFRAME | WFFULL))
1.3 millert 185: bp1->b_altb = bp2;
186: else
187: bp1 = bp2;
188: }
189: }
190: if (bp == curbp)
191: curbp = bp1;
1.62 kjell 192: free(bp->b_headp); /* Release header line. */
1.3 millert 193: bp2 = NULL; /* Find the header. */
1.1 deraadt 194: bp1 = bheadp;
195: while (bp1 != bp) {
196: if (bp1->b_altb == bp)
197: bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
198: bp2 = bp1;
199: bp1 = bp1->b_bufp;
200: }
1.3 millert 201: bp1 = bp1->b_bufp; /* Next one in chain. */
202: if (bp2 == NULL) /* Unlink it. */
1.1 deraadt 203: bheadp = bp1;
204: else
205: bp2->b_bufp = bp1;
1.3 millert 206: while (bp1 != NULL) { /* Finish with altb's */
1.1 deraadt 207: if (bp1->b_altb == bp)
208: bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
209: bp1 = bp1->b_bufp;
210: }
1.74 oga 211:
1.90 florian 212: while ((rec = TAILQ_FIRST(&bp->b_undo))) {
213: TAILQ_REMOVE(&bp->b_undo, rec, next);
1.46 kjell 214: free_undo_record(rec);
215: }
216:
1.54 kjell 217: free(bp->b_bname); /* Release name block */
1.3 millert 218: free(bp); /* Release buffer block */
1.37 db 219: return (TRUE);
1.1 deraadt 220: }
221:
222: /*
223: * Save some buffers - just call anycb with the arg flag.
224: */
1.3 millert 225: /* ARGSUSED */
226: int
1.26 vincent 227: savebuffers(int f, int n)
1.1 deraadt 228: {
1.3 millert 229: if (anycb(f) == ABORT)
1.37 db 230: return (ABORT);
231: return (TRUE);
1.1 deraadt 232: }
233:
234: /*
1.19 art 235: * Listing buffers.
236: */
237: static int listbuf_ncol;
238:
239: static int listbuf_goto_buffer(int f, int n);
1.39 jason 240: static int listbuf_goto_buffer_one(int f, int n);
241: static int listbuf_goto_buffer_helper(int f, int n, int only);
1.19 art 242:
243: static PF listbuf_pf[] = {
1.37 db 244: listbuf_goto_buffer
1.19 art 245: };
1.39 jason 246: static PF listbuf_one[] = {
247: listbuf_goto_buffer_one
248: };
249:
1.19 art 250:
1.97 bcallah 251: static struct KEYMAPE (2) listbufmap = {
252: 2,
1.39 jason 253: 2,
1.19 art 254: rescan,
255: {
1.39 jason 256: {
1.45 deraadt 257: CCHR('M'), CCHR('M'), listbuf_pf, NULL
1.39 jason 258: },
259: {
1.45 deraadt 260: '1', '1', listbuf_one, NULL
1.39 jason 261: }
1.19 art 262: }
263: };
264:
265: /*
1.1 deraadt 266: * Display the buffer list. This is done
267: * in two parts. The "makelist" routine figures out
268: * the text, and puts it in a buffer. "popbuf"
269: * then pops the data onto the screen. Bound to
270: * "C-X C-B".
271: */
1.3 millert 272: /* ARGSUSED */
273: int
1.26 vincent 274: listbuffers(int f, int n)
1.1 deraadt 275: {
1.52 deraadt 276: static int initialized = 0;
277: struct buffer *bp;
278: struct mgwin *wp;
1.1 deraadt 279:
1.19 art 280: if (!initialized) {
281: maps_add((KEYMAP *)&listbufmap, "listbufmap");
282: initialized = 1;
283: }
284:
1.71 kjell 285: if ((bp = makelist()) == NULL || (wp = popbuf(bp, WNONE)) == NULL)
1.37 db 286: return (FALSE);
287: wp->w_dotp = bp->b_dotp; /* fix up if window already on screen */
1.1 deraadt 288: wp->w_doto = bp->b_doto;
1.19 art 289: bp->b_modes[0] = name_mode("fundamental");
290: bp->b_modes[1] = name_mode("listbufmap");
291: bp->b_nmodes = 1;
292:
1.37 db 293: return (TRUE);
1.1 deraadt 294: }
295:
296: /*
297: * This routine rebuilds the text for the
1.37 db 298: * list buffers command. Return pointer
299: * to new list if everything works.
300: * Return NULL if there is an error (if
301: * there is no memory).
1.1 deraadt 302: */
1.52 deraadt 303: static struct buffer *
1.26 vincent 304: makelist(void)
1.3 millert 305: {
1.52 deraadt 306: int w = ncol / 2;
307: struct buffer *bp, *blp;
308: struct line *lp;
1.19 art 309:
1.3 millert 310: if ((blp = bfind("*Buffer List*", TRUE)) == NULL)
1.37 db 311: return (NULL);
1.3 millert 312: if (bclear(blp) != TRUE)
1.37 db 313: return (NULL);
1.3 millert 314: blp->b_flag &= ~BFCHG; /* Blow away old. */
1.28 vincent 315: blp->b_flag |= BFREADONLY;
1.1 deraadt 316:
1.19 art 317: listbuf_ncol = ncol; /* cache ncol for listbuf_goto_buffer */
318:
1.11 art 319: if (addlinef(blp, "%-*s%s", w, " MR Buffer", "Size File") == FALSE ||
320: addlinef(blp, "%-*s%s", w, " -- ------", "---- ----") == FALSE)
1.37 db 321: return (NULL);
1.11 art 322:
323: for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
324: RSIZE nbytes;
325:
1.3 millert 326: nbytes = 0; /* Count bytes in buf. */
1.1 deraadt 327: if (bp != blp) {
1.63 kjell 328: lp = bfirstlp(bp);
1.62 kjell 329: while (lp != bp->b_headp) {
1.3 millert 330: nbytes += llength(lp) + 1;
1.1 deraadt 331: lp = lforw(lp);
332: }
1.3 millert 333: if (nbytes)
334: nbytes--; /* no bonus newline */
1.1 deraadt 335: }
1.11 art 336:
1.19 art 337: if (addlinef(blp, "%c%c%c %-*.*s%c%-6d %-*s",
1.11 art 338: (bp == curbp) ? '.' : ' ', /* current buffer ? */
339: ((bp->b_flag & BFCHG) != 0) ? '*' : ' ', /* changed ? */
1.27 vincent 340: ((bp->b_flag & BFREADONLY) != 0) ? ' ' : '*',
1.19 art 341: w - 5, /* four chars already written */
342: w - 5, /* four chars already written */
1.11 art 343: bp->b_bname, /* buffer name */
1.19 art 344: strlen(bp->b_bname) < w - 5 ? ' ' : '$', /* truncated? */
1.11 art 345: nbytes, /* buffer size */
346: w - 7, /* seven chars already written */
347: bp->b_fname) == FALSE)
1.37 db 348: return (NULL);
1.1 deraadt 349: }
1.63 kjell 350: blp->b_dotp = bfirstlp(blp); /* put dot at beginning of
1.3 millert 351: * buffer */
1.1 deraadt 352: blp->b_doto = 0;
1.37 db 353: return (blp); /* All done */
1.19 art 354: }
355:
356: static int
357: listbuf_goto_buffer(int f, int n)
358: {
1.39 jason 359: return (listbuf_goto_buffer_helper(f, n, 0));
360: }
361:
362: static int
363: listbuf_goto_buffer_one(int f, int n)
364: {
365: return (listbuf_goto_buffer_helper(f, n, 1));
366: }
367:
368: static int
369: listbuf_goto_buffer_helper(int f, int n, int only)
370: {
1.52 deraadt 371: struct buffer *bp;
372: struct mgwin *wp;
373: char *line = NULL;
374: int i, ret = FALSE;
1.19 art 375:
376: if (curwp->w_dotp->l_text[listbuf_ncol/2 - 1] == '$') {
1.93 lum 377: dobeep();
1.19 art 378: ewprintf("buffer name truncated");
1.37 db 379: return (FALSE);
1.19 art 380: }
381:
382: if ((line = malloc(listbuf_ncol/2)) == NULL)
1.37 db 383: return (FALSE);
1.19 art 384:
385: memcpy(line, curwp->w_dotp->l_text + 4, listbuf_ncol/2 - 5);
386: for (i = listbuf_ncol/2 - 6; i > 0; i--) {
387: if (line[i] != ' ') {
388: line[i + 1] = '\0';
389: break;
390: }
391: }
1.37 db 392: if (i == 0)
1.42 cloder 393: goto cleanup;
1.19 art 394:
395: for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
396: if (strcmp(bp->b_bname, line) == 0)
397: break;
398: }
1.37 db 399: if (bp == NULL)
1.42 cloder 400: goto cleanup;
1.37 db 401:
1.71 kjell 402: if ((wp = popbuf(bp, WNONE)) == NULL)
1.42 cloder 403: goto cleanup;
1.19 art 404: curbp = bp;
405: curwp = wp;
1.39 jason 406:
407: if (only)
1.100 lum 408: ret = (onlywind(FFRAND, 1));
1.42 cloder 409: else
410: ret = TRUE;
411:
412: cleanup:
413: free(line);
1.19 art 414:
1.42 cloder 415: return (ret);
1.1 deraadt 416: }
417:
418: /*
1.47 kjell 419: * The argument "fmt" points to a format string. Append this line to the
1.3 millert 420: * buffer. Handcraft the EOL on the end. Return TRUE if it worked and
1.1 deraadt 421: * FALSE if you ran out of room.
422: */
1.3 millert 423: int
1.52 deraadt 424: addlinef(struct buffer *bp, char *fmt, ...)
1.3 millert 425: {
1.52 deraadt 426: va_list ap;
427: struct line *lp;
1.1 deraadt 428:
1.30 vincent 429: if ((lp = lalloc(0)) == NULL)
430: return (FALSE);
1.8 art 431: va_start(ap, fmt);
1.30 vincent 432: if (vasprintf(&lp->l_text, fmt, ap) == -1) {
433: lfree(lp);
1.16 deraadt 434: va_end(ap);
1.30 vincent 435: return (FALSE);
1.16 deraadt 436: }
1.30 vincent 437: lp->l_used = strlen(lp->l_text);
1.8 art 438: va_end(ap);
439:
1.62 kjell 440: bp->b_headp->l_bp->l_fp = lp; /* Hook onto the end */
441: lp->l_bp = bp->b_headp->l_bp;
442: bp->b_headp->l_bp = lp;
443: lp->l_fp = bp->b_headp;
1.60 kjell 444: bp->b_lines++;
1.8 art 445:
1.37 db 446: return (TRUE);
1.1 deraadt 447: }
448:
449: /*
1.3 millert 450: * Look through the list of buffers, giving the user a chance to save them.
1.51 kjell 451: * Return TRUE if there are any changed buffers afterwards. Buffers that don't
452: * have an associated file don't count. Return FALSE if there are no changed
453: * buffers. Return ABORT if an error occurs or if the user presses c-g.
1.3 millert 454: */
455: int
1.26 vincent 456: anycb(int f)
1.3 millert 457: {
1.52 deraadt 458: struct buffer *bp;
1.79 lum 459: int s = FALSE, save = FALSE, save2 = FALSE, ret;
1.53 kjell 460: char pbuf[NFILEN + 11];
1.1 deraadt 461:
462: for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
1.94 bcallah 463: if (*(bp->b_fname) != '\0' && (bp->b_flag & BFCHG) != 0) {
1.53 kjell 464: ret = snprintf(pbuf, sizeof(pbuf), "Save file %s",
1.17 deraadt 465: bp->b_fname);
1.53 kjell 466: if (ret < 0 || ret >= sizeof(pbuf)) {
1.93 lum 467: dobeep();
1.51 kjell 468: ewprintf("Error: filename too long!");
1.81 lum 469: return (UERROR);
1.51 kjell 470: }
1.53 kjell 471: if ((f == TRUE || (save = eyorn(pbuf)) == TRUE) &&
1.79 lum 472: (save2 = buffsave(bp)) == TRUE) {
1.1 deraadt 473: bp->b_flag &= ~BFCHG;
474: upmodes(bp);
1.79 lum 475: } else {
476: if (save2 == FIOERR)
477: return (save2);
1.3 millert 478: s = TRUE;
1.79 lum 479: }
1.3 millert 480: if (save == ABORT)
481: return (save);
1.1 deraadt 482: save = TRUE;
483: }
484: }
485: if (save == FALSE /* && kbdmop == NULL */ ) /* experimental */
486: ewprintf("(No files need saving)");
1.37 db 487: return (s);
1.1 deraadt 488: }
489:
490: /*
491: * Search for a buffer, by name.
492: * If not found, and the "cflag" is TRUE,
1.57 kjell 493: * create a new buffer. Return pointer to the found
494: * (or new) buffer.
1.1 deraadt 495: */
1.52 deraadt 496: struct buffer *
1.26 vincent 497: bfind(const char *bname, int cflag)
1.3 millert 498: {
1.52 deraadt 499: struct buffer *bp;
1.1 deraadt 500:
501: bp = bheadp;
502: while (bp != NULL) {
1.7 art 503: if (strcmp(bname, bp->b_bname) == 0)
1.37 db 504: return (bp);
1.1 deraadt 505: bp = bp->b_bufp;
506: }
1.3 millert 507: if (cflag != TRUE)
1.37 db 508: return (NULL);
1.29 vincent 509:
1.65 kjell 510: bp = bnew(bname);
1.57 kjell 511:
512: return (bp);
513: }
514:
515: /*
516: * Create a new buffer and put it in the list of
1.66 deraadt 517: * all buffers.
1.57 kjell 518: */
519: static struct buffer *
1.65 kjell 520: bnew(const char *bname)
1.57 kjell 521: {
1.66 deraadt 522: struct buffer *bp;
1.57 kjell 523: struct line *lp;
524: int i;
1.72 kjell 525: size_t len;
1.57 kjell 526:
1.52 deraadt 527: bp = calloc(1, sizeof(struct buffer));
1.32 vincent 528: if (bp == NULL) {
1.93 lum 529: dobeep();
1.52 deraadt 530: ewprintf("Can't get %d bytes", sizeof(struct buffer));
1.37 db 531: return (NULL);
1.1 deraadt 532: }
533: if ((lp = lalloc(0)) == NULL) {
1.29 vincent 534: free(bp);
1.37 db 535: return (NULL);
1.1 deraadt 536: }
1.3 millert 537: bp->b_altb = bp->b_bufp = NULL;
538: bp->b_dotp = lp;
539: bp->b_doto = 0;
1.1 deraadt 540: bp->b_markp = NULL;
541: bp->b_marko = 0;
1.3 millert 542: bp->b_flag = defb_flag;
1.72 kjell 543: /* if buffer name starts and ends with '*', we ignore changes */
544: len = strlen(bname);
545: if (len) {
546: if (bname[0] == '*' && bname[len - 1] == '*')
547: bp->b_flag |= BFIGNDIRTY;
548: }
1.3 millert 549: bp->b_nwnd = 0;
1.62 kjell 550: bp->b_headp = lp;
1.1 deraadt 551: bp->b_nmodes = defb_nmodes;
1.74 oga 552: TAILQ_INIT(&bp->b_undo);
1.46 kjell 553: bp->b_undoptr = NULL;
1.1 deraadt 554: i = 0;
555: do {
1.3 millert 556: bp->b_modes[i] = defb_modes[i];
557: } while (i++ < defb_nmodes);
1.1 deraadt 558: bp->b_fname[0] = '\0';
1.57 kjell 559: bp->b_cwd[0] = '\0';
1.1 deraadt 560: bzero(&bp->b_fi, sizeof(bp->b_fi));
561: lp->l_fp = lp;
562: lp->l_bp = lp;
563: bp->b_bufp = bheadp;
564: bheadp = bp;
1.60 kjell 565: bp->b_dotline = bp->b_markline = 1;
1.61 kjell 566: bp->b_lines = 1;
1.65 kjell 567: if ((bp->b_bname = strdup(bname)) == NULL) {
1.93 lum 568: dobeep();
1.65 kjell 569: ewprintf("Can't get %d bytes", strlen(bname) + 1);
570: return (NULL);
571: }
1.57 kjell 572:
1.37 db 573: return (bp);
1.1 deraadt 574: }
575:
576: /*
577: * This routine blows away all of the text
578: * in a buffer. If the buffer is marked as changed
579: * then we ask if it is ok to blow it away; this is
580: * to save the user the grief of losing text. The
581: * window chain is nearly always wrong if this gets
582: * called; the caller must arrange for the updates
583: * that are required. Return TRUE if everything
584: * looks good.
585: */
1.3 millert 586: int
1.52 deraadt 587: bclear(struct buffer *bp)
1.3 millert 588: {
1.52 deraadt 589: struct line *lp;
590: int s;
1.1 deraadt 591:
1.72 kjell 592: /* Has buffer changed, and do we care? */
593: if (!(bp->b_flag & BFIGNDIRTY) && (bp->b_flag & BFCHG) != 0 &&
1.21 deraadt 594: (s = eyesno("Buffer modified; kill anyway")) != TRUE)
1.1 deraadt 595: return (s);
1.3 millert 596: bp->b_flag &= ~BFCHG; /* Not changed */
1.62 kjell 597: while ((lp = lforw(bp->b_headp)) != bp->b_headp)
1.1 deraadt 598: lfree(lp);
1.62 kjell 599: bp->b_dotp = bp->b_headp; /* Fix dot */
1.3 millert 600: bp->b_doto = 0;
601: bp->b_markp = NULL; /* Invalidate "mark" */
1.1 deraadt 602: bp->b_marko = 0;
1.60 kjell 603: bp->b_dotline = bp->b_markline = 1;
1.61 kjell 604: bp->b_lines = 1;
1.60 kjell 605:
1.37 db 606: return (TRUE);
1.1 deraadt 607: }
608:
609: /*
610: * Display the given buffer in the given window. Flags indicated
1.68 kjell 611: * action on redisplay. Update modified flag so insert loop can check it.
1.1 deraadt 612: */
1.3 millert 613: int
1.52 deraadt 614: showbuffer(struct buffer *bp, struct mgwin *wp, int flags)
1.3 millert 615: {
1.52 deraadt 616: struct buffer *obp;
617: struct mgwin *owp;
1.1 deraadt 618:
1.68 kjell 619: /* Ensure file has not been modified elsewhere */
620: if (fchecktime(bp) != TRUE)
621: bp->b_flag |= BFDIRTY;
622:
1.32 vincent 623: if (wp->w_bufp == bp) { /* Easy case! */
1.70 kjell 624: wp->w_rflag |= flags;
1.37 db 625: return (TRUE);
1.1 deraadt 626: }
1.37 db 627: /* First, detach the old buffer from the window */
1.1 deraadt 628: if ((bp->b_altb = obp = wp->w_bufp) != NULL) {
629: if (--obp->b_nwnd == 0) {
1.3 millert 630: obp->b_dotp = wp->w_dotp;
631: obp->b_doto = wp->w_doto;
1.1 deraadt 632: obp->b_markp = wp->w_markp;
633: obp->b_marko = wp->w_marko;
1.60 kjell 634: obp->b_dotline = wp->w_dotline;
635: obp->b_markline = wp->w_markline;
1.1 deraadt 636: }
637: }
638: /* Now, attach the new buffer to the window */
639: wp->w_bufp = bp;
640:
1.3 millert 641: if (bp->b_nwnd++ == 0) { /* First use. */
642: wp->w_dotp = bp->b_dotp;
643: wp->w_doto = bp->b_doto;
1.1 deraadt 644: wp->w_markp = bp->b_markp;
645: wp->w_marko = bp->b_marko;
1.60 kjell 646: wp->w_dotline = bp->b_dotline;
647: wp->w_markline = bp->b_markline;
1.1 deraadt 648: } else
1.3 millert 649: /* already on screen, steal values from other window */
1.1 deraadt 650: for (owp = wheadp; owp != NULL; owp = wp->w_wndp)
651: if (wp->w_bufp == bp && owp != wp) {
1.3 millert 652: wp->w_dotp = owp->w_dotp;
653: wp->w_doto = owp->w_doto;
1.1 deraadt 654: wp->w_markp = owp->w_markp;
655: wp->w_marko = owp->w_marko;
1.60 kjell 656: wp->w_dotline = owp->w_dotline;
1.64 kjell 657: wp->w_markline = owp->w_markline;
1.1 deraadt 658: break;
659: }
1.70 kjell 660: wp->w_rflag |= WFMODE | flags;
1.56 kjell 661: return (TRUE);
662: }
663:
664: /*
665: * Augment a buffer name with a number, if necessary
666: *
667: * If more than one file of the same basename() is open,
668: * the additional buffers are named "file<2>", "file<3>", and
669: * so forth. This function adjusts a buffer name to
670: * include the number, if necessary.
671: */
672: int
1.57 kjell 673: augbname(char *bn, const char *fn, size_t bs)
1.56 kjell 674: {
1.66 deraadt 675: int count;
1.56 kjell 676: size_t remain, len;
677:
1.77 kjell 678: if ((len = xbasename(bn, fn, bs)) >= bs)
1.56 kjell 679: return (FALSE);
680:
681: remain = bs - len;
682: for (count = 2; bfind(bn, FALSE) != NULL; count++)
683: snprintf(bn + len, remain, "<%d>", count);
684:
1.37 db 685: return (TRUE);
1.1 deraadt 686: }
687:
688: /*
689: * Pop the buffer we got passed onto the screen.
690: * Returns a status.
691: */
1.52 deraadt 692: struct mgwin *
1.71 kjell 693: popbuf(struct buffer *bp, int flags)
1.3 millert 694: {
1.52 deraadt 695: struct mgwin *wp;
1.1 deraadt 696:
1.3 millert 697: if (bp->b_nwnd == 0) { /* Not on screen yet. */
1.99 jasper 698: /*
1.71 kjell 699: * Pick a window for a pop-up.
700: * If only one window, split the screen.
701: * Flag the new window as ephemeral
702: */
703: if (wheadp->w_wndp == NULL &&
704: splitwind(FFOTHARG, flags) == FALSE)
705: return (NULL);
706:
707: /*
708: * Pick the uppermost window that isn't
709: * the current window. An LRU algorithm
1.99 jasper 710: * might be better. Return a pointer, or NULL on error.
1.71 kjell 711: */
712: wp = wheadp;
713:
714: while (wp != NULL && wp == curwp)
715: wp = wp->w_wndp;
1.102 ! lum 716: } else {
! 717: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.1 deraadt 718: if (wp->w_bufp == bp) {
1.70 kjell 719: wp->w_rflag |= WFFULL | WFFRAME;
1.37 db 720: return (wp);
1.1 deraadt 721: }
1.102 ! lum 722: }
! 723: }
! 724: if (!wp)
! 725: return (NULL);
! 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: }