xref: /openbsd-src/usr.bin/mg/buffer.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: buffer.c,v 1.68 2008/09/15 16:11:35 kjell Exp $	*/
2 
3 /* This file is in the public domain. */
4 
5 /*
6  *		Buffer handling.
7  */
8 
9 #include <libgen.h>
10 #include <stdarg.h>
11 
12 #include "def.h"
13 #include "kbd.h"		/* needed for modes */
14 
15 static struct buffer  *makelist(void);
16 static struct buffer *bnew(const char *);
17 
18 /* Flag for global working dir */
19 extern int globalwd;
20 
21 /* ARGSUSED */
22 int
23 togglereadonly(int f, int n)
24 {
25 	int s;
26 
27 	if ((s = checkdirty(curbp)) != TRUE)
28 		return (s);
29 	if (!(curbp->b_flag & BFREADONLY))
30 		curbp->b_flag |= BFREADONLY;
31 	else {
32 		curbp->b_flag &= ~BFREADONLY;
33 		if (curbp->b_flag & BFCHG)
34 			ewprintf("Warning: Buffer was modified");
35 	}
36 	curwp->w_flag |= WFMODE;
37 
38 	return (TRUE);
39 }
40 
41 /*
42  * Attach a buffer to a window. The values of dot and mark come
43  * from the buffer if the use count is 0. Otherwise, they come
44  * from some other window.  *scratch* is the default alternate
45  * buffer.
46  */
47 /* ARGSUSED */
48 int
49 usebuffer(int f, int n)
50 {
51 	struct buffer *bp;
52 	char    bufn[NBUFN], *bufp;
53 
54 	/* Get buffer to use from user */
55 	if ((curbp->b_altb == NULL) &&
56 	    ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
57 		bufp = eread("Switch to buffer: ", bufn, NBUFN, EFNEW | EFBUF);
58 	else
59 		bufp = eread("Switch to buffer: (default %s) ", bufn, NBUFN,
60 		    EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
61 
62 	if (bufp == NULL)
63 		return (ABORT);
64 	if (bufp[0] == '\0' && curbp->b_altb != NULL)
65 		bp = curbp->b_altb;
66 	else if ((bp = bfind(bufn, TRUE)) == NULL)
67 		return (FALSE);
68 
69 	/* and put it in current window */
70 	curbp = bp;
71 	return (showbuffer(bp, curwp, WFFRAME | WFFULL));
72 }
73 
74 /*
75  * pop to buffer asked for by the user.
76  */
77 /* ARGSUSED */
78 int
79 poptobuffer(int f, int n)
80 {
81 	struct buffer *bp;
82 	struct mgwin  *wp;
83 	char    bufn[NBUFN], *bufp;
84 
85 	/* Get buffer to use from user */
86 	if ((curbp->b_altb == NULL) &&
87 	    ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
88 		bufp = eread("Switch to buffer in other window: ", bufn, NBUFN,
89 		    EFNEW | EFBUF);
90 	else
91 		bufp = eread("Switch to buffer in other window: (default %s) ",
92 		    bufn, NBUFN, EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
93 	if (bufp == NULL)
94 		return (ABORT);
95 	if (bufp[0] == '\0' && curbp->b_altb != NULL)
96 		bp = curbp->b_altb;
97 	else if ((bp = bfind(bufn, TRUE)) == NULL)
98 		return (FALSE);
99 	if (bp == curbp)
100 		return (splitwind(f, n));
101 	/* and put it in a new window */
102 	if ((wp = popbuf(bp)) == NULL)
103 		return (FALSE);
104 	curbp = bp;
105 	curwp = wp;
106 	return (TRUE);
107 }
108 
109 /*
110  * Dispose of a buffer, by name.
111  * Ask for the name. Look it up (don't get too
112  * upset if it isn't there at all!). Clear the buffer (ask
113  * if the buffer has been changed). Then free the header
114  * line and the buffer header. Bound to "C-X K".
115  */
116 /* ARGSUSED */
117 int
118 killbuffer_cmd(int f, int n)
119 {
120 	struct buffer *bp;
121 	char    bufn[NBUFN], *bufp;
122 
123 	if ((bufp = eread("Kill buffer: (default %s) ", bufn, NBUFN,
124 	    EFNUL | EFNEW | EFBUF, curbp->b_bname)) == NULL)
125 		return (ABORT);
126 	else if (bufp[0] == '\0')
127 		bp = curbp;
128 	else if ((bp = bfind(bufn, FALSE)) == NULL)
129 		return (FALSE);
130 	return (killbuffer(bp));
131 }
132 
133 int
134 killbuffer(struct buffer *bp)
135 {
136 	struct buffer *bp1;
137 	struct buffer *bp2;
138 	struct mgwin  *wp;
139 	int s;
140 	struct undo_rec *rec, *next;
141 
142 	/*
143 	 * Find some other buffer to display. Try the alternate buffer,
144 	 * then the first different buffer in the buffer list.  If there's
145 	 * only one buffer, create buffer *scratch* and make it the alternate
146 	 * buffer.  Return if *scratch* is only buffer...
147 	 */
148 	if ((bp1 = bp->b_altb) == NULL) {
149 		bp1 = (bp == bheadp) ? bp->b_bufp : bheadp;
150 		if (bp1 == NULL) {
151 			/* only one buffer. see if it's *scratch* */
152 			if (bp == bfind("*scratch*", FALSE))
153 				return (TRUE);
154 			/* create *scratch* for alternate buffer */
155 			if ((bp1 = bfind("*scratch*", TRUE)) == NULL)
156 				return (FALSE);
157 		}
158 	}
159 	if ((s = bclear(bp)) != TRUE)
160 		return (s);
161 	for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) {
162 		if (wp->w_bufp == bp) {
163 			bp2 = bp1->b_altb;	/* save alternate buffer */
164 			if (showbuffer(bp1, wp, WFMODE | WFFRAME | WFFULL))
165 				bp1->b_altb = bp2;
166 			else
167 				bp1 = bp2;
168 		}
169 	}
170 	if (bp == curbp)
171 		curbp = bp1;
172 	free(bp->b_headp);			/* Release header line.  */
173 	bp2 = NULL;				/* Find the header.	 */
174 	bp1 = bheadp;
175 	while (bp1 != bp) {
176 		if (bp1->b_altb == bp)
177 			bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
178 		bp2 = bp1;
179 		bp1 = bp1->b_bufp;
180 	}
181 	bp1 = bp1->b_bufp;			/* Next one in chain.	 */
182 	if (bp2 == NULL)			/* Unlink it.		 */
183 		bheadp = bp1;
184 	else
185 		bp2->b_bufp = bp1;
186 	while (bp1 != NULL) {			/* Finish with altb's	 */
187 		if (bp1->b_altb == bp)
188 			bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
189 		bp1 = bp1->b_bufp;
190 	}
191 	rec = LIST_FIRST(&bp->b_undo);
192 	while (rec != NULL) {
193 		next = LIST_NEXT(rec, next);
194 		free_undo_record(rec);
195 		rec = next;
196 	}
197 
198 	free(bp->b_bname);			/* Release name block	 */
199 	free(bp);				/* Release buffer block */
200 	return (TRUE);
201 }
202 
203 /*
204  * Save some buffers - just call anycb with the arg flag.
205  */
206 /* ARGSUSED */
207 int
208 savebuffers(int f, int n)
209 {
210 	if (anycb(f) == ABORT)
211 		return (ABORT);
212 	return (TRUE);
213 }
214 
215 /*
216  * Listing buffers.
217  */
218 static int listbuf_ncol;
219 
220 static int	listbuf_goto_buffer(int f, int n);
221 static int	listbuf_goto_buffer_one(int f, int n);
222 static int	listbuf_goto_buffer_helper(int f, int n, int only);
223 
224 static PF listbuf_pf[] = {
225 	listbuf_goto_buffer
226 };
227 static PF listbuf_one[] = {
228 	listbuf_goto_buffer_one
229 };
230 
231 
232 static struct KEYMAPE (2 + IMAPEXT) listbufmap = {
233 	2,
234 	2 + IMAPEXT,
235 	rescan,
236 	{
237 		{
238 			CCHR('M'), CCHR('M'), listbuf_pf, NULL
239 		},
240 		{
241 			'1', '1', listbuf_one, NULL
242 		}
243 	}
244 };
245 
246 /*
247  * Display the buffer list. This is done
248  * in two parts. The "makelist" routine figures out
249  * the text, and puts it in a buffer. "popbuf"
250  * then pops the data onto the screen. Bound to
251  * "C-X C-B".
252  */
253 /* ARGSUSED */
254 int
255 listbuffers(int f, int n)
256 {
257 	static int		 initialized = 0;
258 	struct buffer		*bp;
259 	struct mgwin		*wp;
260 
261 	if (!initialized) {
262 		maps_add((KEYMAP *)&listbufmap, "listbufmap");
263 		initialized = 1;
264 	}
265 
266 	if ((bp = makelist()) == NULL || (wp = popbuf(bp)) == NULL)
267 		return (FALSE);
268 	wp->w_dotp = bp->b_dotp; /* fix up if window already on screen */
269 	wp->w_doto = bp->b_doto;
270 	bp->b_modes[0] = name_mode("fundamental");
271 	bp->b_modes[1] = name_mode("listbufmap");
272 	bp->b_nmodes = 1;
273 
274 	return (TRUE);
275 }
276 
277 /*
278  * This routine rebuilds the text for the
279  * list buffers command. Return pointer
280  * to new list if everything works.
281  * Return NULL if there is an error (if
282  * there is no memory).
283  */
284 static struct buffer *
285 makelist(void)
286 {
287 	int		w = ncol / 2;
288 	struct buffer	*bp, *blp;
289 	struct line	*lp;
290 
291 	if ((blp = bfind("*Buffer List*", TRUE)) == NULL)
292 		return (NULL);
293 	if (bclear(blp) != TRUE)
294 		return (NULL);
295 	blp->b_flag &= ~BFCHG;		/* Blow away old.	 */
296 	blp->b_flag |= BFREADONLY;
297 
298 	listbuf_ncol = ncol;		/* cache ncol for listbuf_goto_buffer */
299 
300 	if (addlinef(blp, "%-*s%s", w, " MR Buffer", "Size   File") == FALSE ||
301 	    addlinef(blp, "%-*s%s", w, " -- ------", "----   ----") == FALSE)
302 		return (NULL);
303 
304 	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
305 		RSIZE nbytes;
306 
307 		nbytes = 0;			/* Count bytes in buf.	 */
308 		if (bp != blp) {
309 			lp = bfirstlp(bp);
310 			while (lp != bp->b_headp) {
311 				nbytes += llength(lp) + 1;
312 				lp = lforw(lp);
313 			}
314 			if (nbytes)
315 				nbytes--;	/* no bonus newline	 */
316 		}
317 
318 		if (addlinef(blp, "%c%c%c %-*.*s%c%-6d %-*s",
319 		    (bp == curbp) ? '.' : ' ',	/* current buffer ? */
320 		    ((bp->b_flag & BFCHG) != 0) ? '*' : ' ',	/* changed ? */
321 		    ((bp->b_flag & BFREADONLY) != 0) ? ' ' : '*',
322 		    w - 5,		/* four chars already written */
323 		    w - 5,		/* four chars already written */
324 		    bp->b_bname,	/* buffer name */
325 		    strlen(bp->b_bname) < w - 5 ? ' ' : '$', /* truncated? */
326 		    nbytes,		/* buffer size */
327 		    w - 7,		/* seven chars already written */
328 		    bp->b_fname) == FALSE)
329 			return (NULL);
330 	}
331 	blp->b_dotp = bfirstlp(blp);		/* put dot at beginning of
332 						 * buffer */
333 	blp->b_doto = 0;
334 	return (blp);				/* All done		 */
335 }
336 
337 static int
338 listbuf_goto_buffer(int f, int n)
339 {
340 	return (listbuf_goto_buffer_helper(f, n, 0));
341 }
342 
343 static int
344 listbuf_goto_buffer_one(int f, int n)
345 {
346 	return (listbuf_goto_buffer_helper(f, n, 1));
347 }
348 
349 static int
350 listbuf_goto_buffer_helper(int f, int n, int only)
351 {
352 	struct buffer	*bp;
353 	struct mgwin	*wp;
354 	char		*line = NULL;
355 	int		 i, ret = FALSE;
356 
357 	if (curwp->w_dotp->l_text[listbuf_ncol/2 - 1] == '$') {
358 		ewprintf("buffer name truncated");
359 		return (FALSE);
360 	}
361 
362 	if ((line = malloc(listbuf_ncol/2)) == NULL)
363 		return (FALSE);
364 
365 	memcpy(line, curwp->w_dotp->l_text + 4, listbuf_ncol/2 - 5);
366 	for (i = listbuf_ncol/2 - 6; i > 0; i--) {
367 		if (line[i] != ' ') {
368 			line[i + 1] = '\0';
369 			break;
370 		}
371 	}
372 	if (i == 0)
373 		goto cleanup;
374 
375 	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
376 		if (strcmp(bp->b_bname, line) == 0)
377 			break;
378 	}
379 	if (bp == NULL)
380 		goto cleanup;
381 
382 	if ((wp = popbuf(bp)) == NULL)
383 		goto cleanup;
384 	curbp = bp;
385 	curwp = wp;
386 
387 	if (only)
388 		ret = (onlywind(f, n));
389 	else
390 		ret = TRUE;
391 
392 cleanup:
393 	free(line);
394 
395 	return (ret);
396 }
397 
398 /*
399  * The argument "fmt" points to a format string.  Append this line to the
400  * buffer. Handcraft the EOL on the end.  Return TRUE if it worked and
401  * FALSE if you ran out of room.
402  */
403 int
404 addlinef(struct buffer *bp, char *fmt, ...)
405 {
406 	va_list		 ap;
407 	struct line	*lp;
408 
409 	if ((lp = lalloc(0)) == NULL)
410 		return (FALSE);
411 	va_start(ap, fmt);
412 	if (vasprintf(&lp->l_text, fmt, ap) == -1) {
413 		lfree(lp);
414 		va_end(ap);
415 		return (FALSE);
416 	}
417 	lp->l_used = strlen(lp->l_text);
418 	va_end(ap);
419 
420 	bp->b_headp->l_bp->l_fp = lp;		/* Hook onto the end	 */
421 	lp->l_bp = bp->b_headp->l_bp;
422 	bp->b_headp->l_bp = lp;
423 	lp->l_fp = bp->b_headp;
424 	bp->b_lines++;
425 
426 	return (TRUE);
427 }
428 
429 /*
430  * Look through the list of buffers, giving the user a chance to save them.
431  * Return TRUE if there are any changed buffers afterwards.  Buffers that don't
432  * have an associated file don't count.  Return FALSE if there are no changed
433  * buffers.  Return ABORT if an error occurs or if the user presses c-g.
434  */
435 int
436 anycb(int f)
437 {
438 	struct buffer	*bp;
439 	int		 s = FALSE, save = FALSE, ret;
440 	char		 pbuf[NFILEN + 11];
441 
442 	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
443 		if (bp->b_fname != NULL && *(bp->b_fname) != '\0' &&
444 		    (bp->b_flag & BFCHG) != 0) {
445 			ret = snprintf(pbuf, sizeof(pbuf), "Save file %s",
446 			    bp->b_fname);
447 			if (ret < 0 || ret >= sizeof(pbuf)) {
448 				ewprintf("Error: filename too long!");
449 				return (ABORT);
450 			}
451 			if ((f == TRUE || (save = eyorn(pbuf)) == TRUE) &&
452 			    buffsave(bp) == TRUE) {
453 				bp->b_flag &= ~BFCHG;
454 				upmodes(bp);
455 			} else
456 				s = TRUE;
457 			if (save == ABORT)
458 				return (save);
459 			save = TRUE;
460 		}
461 	}
462 	if (save == FALSE /* && kbdmop == NULL */ )	/* experimental */
463 		ewprintf("(No files need saving)");
464 	return (s);
465 }
466 
467 /*
468  * Search for a buffer, by name.
469  * If not found, and the "cflag" is TRUE,
470  * create a new buffer. Return pointer to the found
471  * (or new) buffer.
472  */
473 struct buffer *
474 bfind(const char *bname, int cflag)
475 {
476 	struct buffer	*bp;
477 
478 	bp = bheadp;
479 	while (bp != NULL) {
480 		if (strcmp(bname, bp->b_bname) == 0)
481 			return (bp);
482 		bp = bp->b_bufp;
483 	}
484 	if (cflag != TRUE)
485 		return (NULL);
486 
487 	bp = bnew(bname);
488 
489 	return (bp);
490 }
491 
492 /*
493  * Create a new buffer and put it in the list of
494  * all buffers.
495  */
496 static struct buffer *
497 bnew(const char *bname)
498 {
499 	struct buffer	*bp;
500 	struct line	*lp;
501 	int		 i;
502 
503 	bp = calloc(1, sizeof(struct buffer));
504 	if (bp == NULL) {
505 		ewprintf("Can't get %d bytes", sizeof(struct buffer));
506 		return (NULL);
507 	}
508 	if ((lp = lalloc(0)) == NULL) {
509 		free(bp);
510 		return (NULL);
511 	}
512 	bp->b_altb = bp->b_bufp = NULL;
513 	bp->b_dotp = lp;
514 	bp->b_doto = 0;
515 	bp->b_markp = NULL;
516 	bp->b_marko = 0;
517 	bp->b_flag = defb_flag;
518 	bp->b_nwnd = 0;
519 	bp->b_headp = lp;
520 	bp->b_nmodes = defb_nmodes;
521 	LIST_INIT(&bp->b_undo);
522 	bp->b_undoptr = NULL;
523 	memset(&bp->b_undopos, 0, sizeof(bp->b_undopos));
524 	i = 0;
525 	do {
526 		bp->b_modes[i] = defb_modes[i];
527 	} while (i++ < defb_nmodes);
528 	bp->b_fname[0] = '\0';
529 	bp->b_cwd[0] = '\0';
530 	bzero(&bp->b_fi, sizeof(bp->b_fi));
531 	lp->l_fp = lp;
532 	lp->l_bp = lp;
533 	bp->b_bufp = bheadp;
534 	bheadp = bp;
535 	bp->b_dotline = bp->b_markline = 1;
536 	bp->b_lines = 1;
537 	if ((bp->b_bname = strdup(bname)) == NULL) {
538 		ewprintf("Can't get %d bytes", strlen(bname) + 1);
539 		return (NULL);
540 	}
541 
542 	return (bp);
543 }
544 
545 /*
546  * This routine blows away all of the text
547  * in a buffer. If the buffer is marked as changed
548  * then we ask if it is ok to blow it away; this is
549  * to save the user the grief of losing text. The
550  * window chain is nearly always wrong if this gets
551  * called; the caller must arrange for the updates
552  * that are required. Return TRUE if everything
553  * looks good.
554  */
555 int
556 bclear(struct buffer *bp)
557 {
558 	struct line	*lp;
559 	int		 s;
560 
561 	if ((bp->b_flag & BFCHG) != 0 &&	/* Changed. */
562 	    (s = eyesno("Buffer modified; kill anyway")) != TRUE)
563 		return (s);
564 	bp->b_flag &= ~BFCHG;	/* Not changed		 */
565 	while ((lp = lforw(bp->b_headp)) != bp->b_headp)
566 		lfree(lp);
567 	bp->b_dotp = bp->b_headp;	/* Fix dot */
568 	bp->b_doto = 0;
569 	bp->b_markp = NULL;	/* Invalidate "mark"	 */
570 	bp->b_marko = 0;
571 	bp->b_dotline = bp->b_markline = 1;
572 	bp->b_lines = 1;
573 
574 	return (TRUE);
575 }
576 
577 /*
578  * Display the given buffer in the given window. Flags indicated
579  * action on redisplay. Update modified flag so insert loop can check it.
580  */
581 int
582 showbuffer(struct buffer *bp, struct mgwin *wp, int flags)
583 {
584 	struct buffer	*obp;
585 	struct mgwin	*owp;
586 
587 	/* Ensure file has not been modified elsewhere */
588 	if (fchecktime(bp) != TRUE)
589 		bp->b_flag |= BFDIRTY;
590 
591 	if (wp->w_bufp == bp) {	/* Easy case! */
592 		wp->w_flag |= flags;
593 		wp->w_dotp = bp->b_dotp;
594 		wp->w_doto = bp->b_doto;
595 		return (TRUE);
596 	}
597 	/* First, detach the old buffer from the window */
598 	if ((bp->b_altb = obp = wp->w_bufp) != NULL) {
599 		if (--obp->b_nwnd == 0) {
600 			obp->b_dotp = wp->w_dotp;
601 			obp->b_doto = wp->w_doto;
602 			obp->b_markp = wp->w_markp;
603 			obp->b_marko = wp->w_marko;
604 			obp->b_dotline = wp->w_dotline;
605 			obp->b_markline = wp->w_markline;
606 		}
607 	}
608 	/* Now, attach the new buffer to the window */
609 	wp->w_bufp = bp;
610 
611 	if (bp->b_nwnd++ == 0) {	/* First use.		 */
612 		wp->w_dotp = bp->b_dotp;
613 		wp->w_doto = bp->b_doto;
614 		wp->w_markp = bp->b_markp;
615 		wp->w_marko = bp->b_marko;
616 		wp->w_dotline = bp->b_dotline;
617 		wp->w_markline = bp->b_markline;
618 	} else
619 		/* already on screen, steal values from other window */
620 		for (owp = wheadp; owp != NULL; owp = wp->w_wndp)
621 			if (wp->w_bufp == bp && owp != wp) {
622 				wp->w_dotp = owp->w_dotp;
623 				wp->w_doto = owp->w_doto;
624 				wp->w_markp = owp->w_markp;
625 				wp->w_marko = owp->w_marko;
626 				wp->w_dotline = owp->w_dotline;
627 				wp->w_markline = owp->w_markline;
628 				break;
629 			}
630 	wp->w_flag |= WFMODE | flags;
631 	return (TRUE);
632 }
633 
634 /*
635  * Augment a buffer name with a number, if necessary
636  *
637  * If more than one file of the same basename() is open,
638  * the additional buffers are named "file<2>", "file<3>", and
639  * so forth.  This function adjusts a buffer name to
640  * include the number, if necessary.
641  */
642 int
643 augbname(char *bn, const char *fn, size_t bs)
644 {
645 	int	 count;
646 	size_t	 remain, len;
647 
648 	len = strlcpy(bn, basename(fn), bs);
649 	if (len >= bs)
650 		return (FALSE);
651 
652 	remain = bs - len;
653 	for (count = 2; bfind(bn, FALSE) != NULL; count++)
654 		snprintf(bn + len, remain, "<%d>", count);
655 
656 	return (TRUE);
657 }
658 
659 /*
660  * Pop the buffer we got passed onto the screen.
661  * Returns a status.
662  */
663 struct mgwin *
664 popbuf(struct buffer *bp)
665 {
666 	struct mgwin	*wp;
667 
668 	if (bp->b_nwnd == 0) {	/* Not on screen yet.	 */
669 		if ((wp = wpopup()) == NULL)
670 			return (NULL);
671 	} else
672 		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
673 			if (wp->w_bufp == bp) {
674 				wp->w_flag |= WFFULL | WFFRAME;
675 				return (wp);
676 			}
677 	if (showbuffer(bp, wp, WFFULL) != TRUE)
678 		return (NULL);
679 	return (wp);
680 }
681 
682 /*
683  * Insert another buffer at dot.  Very useful.
684  */
685 /* ARGSUSED */
686 int
687 bufferinsert(int f, int n)
688 {
689 	struct buffer *bp;
690 	struct line   *clp;
691 	int	clo, nline;
692 	char	bufn[NBUFN], *bufp;
693 
694 	/* Get buffer to use from user */
695 	if (curbp->b_altb != NULL)
696 		bufp = eread("Insert buffer: (default %s) ", bufn, NBUFN,
697 		    EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
698 	else
699 		bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF);
700 	if (bufp == NULL)
701 		return (ABORT);
702 	if (bufp[0] == '\0' && curbp->b_altb != NULL)
703 		bp = curbp->b_altb;
704 	else if ((bp = bfind(bufn, FALSE)) == NULL)
705 		return (FALSE);
706 
707 	if (bp == curbp) {
708 		ewprintf("Cannot insert buffer into self");
709 		return (FALSE);
710 	}
711 	/* insert the buffer */
712 	nline = 0;
713 	clp = bfirstlp(bp);
714 	for (;;) {
715 		for (clo = 0; clo < llength(clp); clo++)
716 			if (linsert(1, lgetc(clp, clo)) == FALSE)
717 				return (FALSE);
718 		if ((clp = lforw(clp)) == bp->b_headp)
719 			break;
720 		if (newline(FFRAND, 1) == FALSE)	/* fake newline */
721 			return (FALSE);
722 		nline++;
723 	}
724 	if (nline == 1)
725 		ewprintf("[Inserted 1 line]");
726 	else
727 		ewprintf("[Inserted %d lines]", nline);
728 
729 	clp = curwp->w_linep;		/* cosmetic adjustment	*/
730 	if (curwp->w_dotp == clp) {	/* for offscreen insert */
731 		while (nline-- && lback(clp) != curbp->b_headp)
732 			clp = lback(clp);
733 		curwp->w_linep = clp;	/* adjust framing.	*/
734 		curwp->w_flag |= WFFULL;
735 	}
736 	return (TRUE);
737 }
738 
739 /*
740  * Turn off the dirty bit on this buffer.
741  */
742 /* ARGSUSED */
743 int
744 notmodified(int f, int n)
745 {
746 	struct mgwin *wp;
747 
748 	curbp->b_flag &= ~BFCHG;
749 	wp = wheadp;		/* Update mode lines.	 */
750 	while (wp != NULL) {
751 		if (wp->w_bufp == curbp)
752 			wp->w_flag |= WFMODE;
753 		wp = wp->w_wndp;
754 	}
755 	ewprintf("Modification-flag cleared");
756 	return (TRUE);
757 }
758 
759 #ifndef NO_HELP
760 /*
761  * Popbuf and set all windows to top of buffer.	 Currently only used by
762  * help functions.
763  */
764 int
765 popbuftop(struct buffer *bp)
766 {
767 	struct mgwin *wp;
768 
769 	bp->b_dotp = bfirstlp(bp);
770 	bp->b_doto = 0;
771 	if (bp->b_nwnd != 0) {
772 		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
773 			if (wp->w_bufp == bp) {
774 				wp->w_dotp = bp->b_dotp;
775 				wp->w_doto = 0;
776 				wp->w_flag |= WFFULL;
777 			}
778 	}
779 	return (popbuf(bp) != NULL);
780 }
781 #endif
782 
783 /*
784  * Return the working directory for the current buffer, terminated
785  * with a '/'. First, try to extract it from the current buffer's
786  * filename. If that fails, use global cwd.
787  */
788 int
789 getbufcwd(char *path, size_t plen)
790 {
791 	char cwd[NFILEN];
792 
793 	if (plen == 0)
794 		return (FALSE);
795 
796 	if (globalwd == FALSE && curbp->b_cwd[0] != '\0') {
797 		(void)strlcpy(path, curbp->b_cwd, plen);
798 	} else {
799 		if (getcwdir(cwd, sizeof(cwd)) == FALSE)
800 			goto error;
801 		(void)strlcpy(path, cwd, plen);
802 	}
803 	return (TRUE);
804 error:
805 	path[0] = '\0';
806 	return (FALSE);
807 }
808 
809 /*
810  * Ensures a buffer has not been modified elsewhere.
811  * Returns TRUE if it has NOT. FALSE or ABORT otherwise
812  */
813 int
814 checkdirty(struct buffer *bp)
815 {
816 	int s;
817 
818 	if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) {
819 		if ((s = eyorn("File changed on disk; really edit the buffer"))
820 		    != TRUE)
821 			return (s);
822 		bp->b_flag &= ~BFDIRTY;
823 		bp->b_flag |= BFIGNDIRTY;
824 	}
825 
826 	return (TRUE);
827 }
828 
829