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