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