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