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