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