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: }