Annotation of src/usr.bin/mg/buffer.c, Revision 1.98
1.98 ! bcallah 1: /* $OpenBSD: buffer.c,v 1.97 2015/03/19 21:48:05 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: #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: {
58: struct buffer *bp;
59:
60: if (bufp == NULL)
61: return (ABORT);
62: if (bufp[0] == '\0' && curbp->b_altb != NULL)
63: bp = curbp->b_altb;
64: else if ((bp = bfind(bufp, TRUE)) == NULL)
65: return (FALSE);
66:
67: /* and put it in current window */
68: curbp = bp;
69: return (showbuffer(bp, curwp, WFFRAME | WFFULL));
70: }
71:
1.1 deraadt 72: /*
73: * Attach a buffer to a window. The values of dot and mark come
74: * from the buffer if the use count is 0. Otherwise, they come
75: * from some other window. *scratch* is the default alternate
76: * buffer.
77: */
1.3 millert 78: /* ARGSUSED */
79: int
1.26 vincent 80: usebuffer(int f, int n)
1.1 deraadt 81: {
1.34 vincent 82: char bufn[NBUFN], *bufp;
1.1 deraadt 83:
84: /* Get buffer to use from user */
1.21 deraadt 85: if ((curbp->b_altb == NULL) &&
86: ((curbp->b_altb = bfind("*scratch*", TRUE)) == 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) {
172: bp1 = (bp == bheadp) ? bp->b_bufp : bheadp;
173: if (bp1 == NULL) {
174: /* only one buffer. see if it's *scratch* */
1.3 millert 175: if (bp == bfind("*scratch*", FALSE))
1.49 kjell 176: return (TRUE);
1.1 deraadt 177: /* create *scratch* for alternate buffer */
1.3 millert 178: if ((bp1 = bfind("*scratch*", TRUE)) == NULL)
1.37 db 179: return (FALSE);
1.1 deraadt 180: }
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.42 cloder 411: ret = (onlywind(f, n));
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.71 kjell 701: /*
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
713: * might be better. Return a pointer, or NULL on error.
714: */
715: wp = wheadp;
716:
717: while (wp != NULL && wp == curwp)
718: wp = wp->w_wndp;
1.1 deraadt 719: } else
720: for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
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.58 kjell 725: if (showbuffer(bp, wp, WFFULL) != TRUE)
1.37 db 726: return (NULL);
727: return (wp);
1.1 deraadt 728: }
729:
730: /*
731: * Insert another buffer at dot. Very useful.
732: */
1.3 millert 733: /* ARGSUSED */
734: int
1.26 vincent 735: bufferinsert(int f, int n)
1.1 deraadt 736: {
1.52 deraadt 737: struct buffer *bp;
738: struct line *clp;
1.36 deraadt 739: int clo, nline;
740: char bufn[NBUFN], *bufp;
1.1 deraadt 741:
742: /* Get buffer to use from user */
743: if (curbp->b_altb != NULL)
1.34 vincent 744: bufp = eread("Insert buffer: (default %s) ", bufn, NBUFN,
1.40 kjell 745: EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
1.1 deraadt 746: else
1.38 cloder 747: bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF);
1.34 vincent 748: if (bufp == NULL)
1.37 db 749: return (ABORT);
1.34 vincent 750: if (bufp[0] == '\0' && curbp->b_altb != NULL)
1.3 millert 751: bp = curbp->b_altb;
752: else if ((bp = bfind(bufn, FALSE)) == NULL)
1.37 db 753: return (FALSE);
1.1 deraadt 754:
1.3 millert 755: if (bp == curbp) {
1.93 lum 756: dobeep();
1.1 deraadt 757: ewprintf("Cannot insert buffer into self");
1.37 db 758: return (FALSE);
1.1 deraadt 759: }
760: /* insert the buffer */
761: nline = 0;
1.63 kjell 762: clp = bfirstlp(bp);
1.3 millert 763: for (;;) {
1.1 deraadt 764: for (clo = 0; clo < llength(clp); clo++)
765: if (linsert(1, lgetc(clp, clo)) == FALSE)
1.37 db 766: return (FALSE);
1.62 kjell 767: if ((clp = lforw(clp)) == bp->b_headp)
1.3 millert 768: break;
1.95 bcallah 769: if (enewline(FFRAND, 1) == FALSE) /* fake newline */
1.37 db 770: return (FALSE);
1.1 deraadt 771: nline++;
772: }
1.3 millert 773: if (nline == 1)
774: ewprintf("[Inserted 1 line]");
775: else
776: ewprintf("[Inserted %d lines]", nline);
1.1 deraadt 777:
1.37 db 778: clp = curwp->w_linep; /* cosmetic adjustment */
1.3 millert 779: if (curwp->w_dotp == clp) { /* for offscreen insert */
1.62 kjell 780: while (nline-- && lback(clp) != curbp->b_headp)
1.1 deraadt 781: clp = lback(clp);
1.37 db 782: curwp->w_linep = clp; /* adjust framing. */
1.70 kjell 783: curwp->w_rflag |= WFFULL;
1.1 deraadt 784: }
785: return (TRUE);
786: }
787:
788: /*
789: * Turn off the dirty bit on this buffer.
790: */
1.3 millert 791: /* ARGSUSED */
792: int
1.26 vincent 793: notmodified(int f, int n)
1.1 deraadt 794: {
1.52 deraadt 795: struct mgwin *wp;
1.1 deraadt 796:
797: curbp->b_flag &= ~BFCHG;
1.3 millert 798: wp = wheadp; /* Update mode lines. */
1.1 deraadt 799: while (wp != NULL) {
800: if (wp->w_bufp == curbp)
1.70 kjell 801: wp->w_rflag |= WFMODE;
1.1 deraadt 802: wp = wp->w_wndp;
803: }
804: ewprintf("Modification-flag cleared");
1.37 db 805: return (TRUE);
1.1 deraadt 806: }
807:
808: /*
1.78 lum 809: * Popbuf and set all windows to top of buffer.
1.1 deraadt 810: */
1.3 millert 811: int
1.71 kjell 812: popbuftop(struct buffer *bp, int flags)
1.1 deraadt 813: {
1.52 deraadt 814: struct mgwin *wp;
1.1 deraadt 815:
1.63 kjell 816: bp->b_dotp = bfirstlp(bp);
1.3 millert 817: bp->b_doto = 0;
818: if (bp->b_nwnd != 0) {
819: for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
820: if (wp->w_bufp == bp) {
821: wp->w_dotp = bp->b_dotp;
822: wp->w_doto = 0;
1.70 kjell 823: wp->w_rflag |= WFFULL;
1.3 millert 824: }
825: }
1.71 kjell 826: return (popbuf(bp, flags) != NULL);
1.1 deraadt 827: }
1.57 kjell 828:
829: /*
830: * Return the working directory for the current buffer, terminated
831: * with a '/'. First, try to extract it from the current buffer's
832: * filename. If that fails, use global cwd.
833: */
834: int
835: getbufcwd(char *path, size_t plen)
836: {
837: char cwd[NFILEN];
838:
839: if (plen == 0)
840: return (FALSE);
841:
1.67 kjell 842: if (globalwd == FALSE && curbp->b_cwd[0] != '\0') {
1.57 kjell 843: (void)strlcpy(path, curbp->b_cwd, plen);
844: } else {
845: if (getcwdir(cwd, sizeof(cwd)) == FALSE)
846: goto error;
847: (void)strlcpy(path, cwd, plen);
848: }
849: return (TRUE);
850: error:
851: path[0] = '\0';
852: return (FALSE);
853: }
854:
1.68 kjell 855: /*
1.69 kjell 856: * Ensures a buffer has not been modified elsewhere; e.g. on disk.
857: * Prompt the user if it has.
858: * Returns TRUE if it has NOT (i.e. buffer is ok to edit).
859: * FALSE or ABORT otherwise
1.68 kjell 860: */
861: int
862: checkdirty(struct buffer *bp)
863: {
864: int s;
1.83 florian 865:
866: if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0)
867: if (fchecktime(bp) != TRUE)
868: bp->b_flag |= BFDIRTY;
869:
1.68 kjell 870: if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) {
1.84 florian 871: s = eynorr("File changed on disk; really edit the buffer");
872: switch (s) {
873: case TRUE:
874: bp->b_flag &= ~BFDIRTY;
875: bp->b_flag |= BFIGNDIRTY;
876: return (TRUE);
877: case REVERT:
878: dorevert();
879: return (FALSE);
880: default:
1.68 kjell 881: return (s);
1.84 florian 882: }
1.68 kjell 883: }
884:
885: return (TRUE);
886: }
1.82 jasper 887:
888: /*
889: * Revert the current buffer to whatever is on disk.
890: */
891: /* ARGSUSED */
892: int
893: revertbuffer(int f, int n)
894: {
895: char fbuf[NFILEN + 32];
896:
1.85 florian 897: if (curbp->b_fname[0] == 0) {
1.93 lum 898: dobeep();
1.82 jasper 899: ewprintf("Cannot revert buffer not associated with any files.");
900: return (FALSE);
901: }
902:
1.85 florian 903: snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s",
904: curbp->b_fname);
1.82 jasper 905:
1.85 florian 906: if (eyorn(fbuf) == TRUE)
1.84 florian 907: return dorevert();
1.82 jasper 908:
1.84 florian 909: return (FALSE);
910: }
1.82 jasper 911:
1.84 florian 912: int
1.86 haesbaer 913: dorevert(void)
1.84 florian 914: {
915: int lineno;
1.89 florian 916: struct undo_rec *rec;
1.82 jasper 917:
1.85 florian 918: if (access(curbp->b_fname, F_OK|R_OK) != 0) {
1.93 lum 919: dobeep();
1.84 florian 920: if (errno == ENOENT)
921: ewprintf("File %s no longer exists!",
1.85 florian 922: curbp->b_fname);
1.84 florian 923: else
924: ewprintf("File %s is no longer readable!",
1.85 florian 925: curbp->b_fname);
1.84 florian 926: return (FALSE);
1.82 jasper 927: }
928:
1.84 florian 929: /* Save our current line, so we can go back after reloading. */
1.85 florian 930: lineno = curwp->w_dotline;
1.84 florian 931:
932: /* Prevent readin from asking if we want to kill the buffer. */
933: curbp->b_flag &= ~BFCHG;
1.89 florian 934:
935: /* Clean up undo memory */
936: while ((rec = TAILQ_FIRST(&curbp->b_undo))) {
937: TAILQ_REMOVE(&curbp->b_undo, rec, next);
938: free_undo_record(rec);
939: }
1.84 florian 940:
1.85 florian 941: if (readin(curbp->b_fname))
1.84 florian 942: return(setlineno(lineno));
1.82 jasper 943: return (FALSE);
1.88 florian 944: }
945:
946: /*
947: * Diff the current buffer to what is on disk.
948: */
949: /*ARGSUSED */
950: int
951: diffbuffer(int f, int n)
952: {
953: struct buffer *bp;
954: struct line *lp, *lpend;
955: size_t len;
956: int ret;
957: char *text, *ttext;
958: char * const argv[] =
959: {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL};
960:
961: len = 0;
962:
963: /* C-u is not supported */
964: if (n > 1)
965: return (ABORT);
966:
967: if (access(DIFFTOOL, X_OK) != 0) {
1.93 lum 968: dobeep();
1.88 florian 969: ewprintf("%s not found or not executable.", DIFFTOOL);
970: return (FALSE);
971: }
972:
973: if (curbp->b_fname[0] == 0) {
1.93 lum 974: dobeep();
1.88 florian 975: ewprintf("Cannot diff buffer not associated with any files.");
976: return (FALSE);
977: }
978:
979: lpend = curbp->b_headp;
980: for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
981: len+=llength(lp);
982: if (lforw(lp) != lpend) /* no implied \n on last line */
983: len++;
984: }
985: if ((text = calloc(len + 1, sizeof(char))) == NULL) {
1.93 lum 986: dobeep();
1.88 florian 987: ewprintf("Cannot allocate memory.");
988: return (FALSE);
989: }
990: ttext = text;
991:
992: for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
993: if (llength(lp) != 0) {
994: memcpy(ttext, ltext(lp), llength(lp));
995: ttext += llength(lp);
996: }
997: if (lforw(lp) != lpend) /* no implied \n on last line */
998: *ttext++ = '\n';
999: }
1000:
1001: bp = bfind("*Diff*", TRUE);
1002: bp->b_flag |= BFREADONLY;
1003: if (bclear(bp) != TRUE) {
1004: free(text);
1005: return (FALSE);
1006: }
1007:
1008: ret = pipeio(DIFFTOOL, argv, text, len, bp);
1009:
1010: if (ret == TRUE) {
1011: eerase();
1012: if (lforw(bp->b_headp) == bp->b_headp)
1013: addline(bp, "Diff finished (no differences).");
1014: }
1015:
1016: free(text);
1017: return (ret);
1.92 lum 1018: }
1019:
1020: /*
1021: * Given a file name, either find the buffer it uses, or create a new
1022: * empty buffer to put it in.
1023: */
1024: struct buffer *
1025: findbuffer(char *fn)
1026: {
1027: struct buffer *bp;
1028: char bname[NBUFN], fname[NBUFN];
1029:
1030: if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) {
1.93 lum 1031: dobeep();
1.92 lum 1032: ewprintf("filename too long");
1033: return (NULL);
1034: }
1035:
1036: for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
1037: if (strcmp(bp->b_fname, fname) == 0)
1038: return (bp);
1039: }
1040: /* Not found. Create a new one, adjusting name first */
1041: if (augbname(bname, fname, sizeof(bname)) == FALSE)
1042: return (NULL);
1043:
1044: bp = bfind(bname, TRUE);
1045: return (bp);
1.82 jasper 1046: }