xref: /openbsd-src/usr.bin/mg/buffer.c (revision 3a3fbb3f2e2521ab7c4a56b7ff7462ebd9095ec5)
1 /*	$OpenBSD: buffer.c,v 1.17 2001/11/25 07:34:17 deraadt Exp $	*/
2 
3 /*
4  *		Buffer handling.
5  */
6 
7 #include "def.h"
8 #include "kbd.h"		/* needed for modes */
9 #include <stdarg.h>
10 
11 static BUFFER  *makelist	__P((void));
12 
13 /*
14  * Attach a buffer to a window. The values of dot and mark come
15  * from the buffer if the use count is 0. Otherwise, they come
16  * from some other window.  *scratch* is the default alternate
17  * buffer.
18  */
19 /* ARGSUSED */
20 int
21 usebuffer(f, n)
22 	int     f, n;
23 {
24 	BUFFER *bp;
25 	int     s;
26 	char    bufn[NBUFN];
27 
28 	/* Get buffer to use from user */
29 	if ((curbp->b_altb == NULL)
30 	    && ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
31 		s = eread("Switch to buffer: ", bufn, NBUFN, EFNEW | EFBUF);
32 	else
33 		s = eread("Switch to buffer: (default %s) ", bufn, NBUFN,
34 			  EFNEW | EFBUF, curbp->b_altb->b_bname);
35 
36 	if (s == ABORT)
37 		return s;
38 	if (s == FALSE && curbp->b_altb != NULL)
39 		bp = curbp->b_altb;
40 	else if ((bp = bfind(bufn, TRUE)) == NULL)
41 		return FALSE;
42 
43 	/* and put it in current window */
44 	curbp = bp;
45 	return showbuffer(bp, curwp, WFFORCE | WFHARD);
46 }
47 
48 /*
49  * pop to buffer asked for by the user.
50  */
51 /* ARGSUSED */
52 int
53 poptobuffer(f, n)
54 	int     f, n;
55 {
56 	BUFFER *bp;
57 	MGWIN  *wp;
58 	int     s;
59 	char    bufn[NBUFN];
60 
61 	/* Get buffer to use from user */
62 	if ((curbp->b_altb == NULL)
63 	    && ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
64 		s = eread("Switch to buffer in other window: ", bufn, NBUFN,
65 			  EFNEW | EFBUF);
66 	else
67 		s = eread("Switch to buffer in other window: (default %s) ",
68 			bufn, NBUFN, EFNEW | EFBUF, curbp->b_altb->b_bname);
69 	if (s == ABORT)
70 		return s;
71 	if (s == FALSE && curbp->b_altb != NULL)
72 		bp = curbp->b_altb;
73 	else if ((bp = bfind(bufn, TRUE)) == NULL)
74 		return FALSE;
75 
76 	/* and put it in a new window */
77 	if ((wp = popbuf(bp)) == NULL)
78 		return FALSE;
79 	curbp = bp;
80 	curwp = wp;
81 	return TRUE;
82 }
83 
84 /*
85  * Dispose of a buffer, by name.
86  * Ask for the name. Look it up (don't get too
87  * upset if it isn't there at all!). Clear the buffer (ask
88  * if the buffer has been changed). Then free the header
89  * line and the buffer header. Bound to "C-X K".
90  */
91 /* ARGSUSED */
92 int
93 killbuffer(f, n)
94 	int     f, n;
95 {
96 	BUFFER *bp;
97 	BUFFER *bp1;
98 	BUFFER *bp2;
99 	MGWIN  *wp;
100 	int     s;
101 	char    bufn[NBUFN];
102 
103 	if ((s = eread("Kill buffer: (default %s) ", bufn, NBUFN, EFNEW | EFBUF,
104 		       curbp->b_bname)) == ABORT)
105 		return (s);
106 	else if (s == FALSE)
107 		bp = curbp;
108 	else if ((bp = bfind(bufn, FALSE)) == NULL)
109 		return FALSE;
110 
111 	/*
112 	 * Find some other buffer to display. try the alternate buffer,
113 	 * then the first different buffer in the buffer list.  If there's
114 	 * only one buffer, create buffer *scratch* and make it the alternate
115 	 * buffer.  Return if *scratch* is only buffer...
116 	 */
117 	if ((bp1 = bp->b_altb) == NULL) {
118 		bp1 = (bp == bheadp) ? bp->b_bufp : bheadp;
119 		if (bp1 == NULL) {
120 			/* only one buffer. see if it's *scratch* */
121 			if (bp == bfind("*scratch*", FALSE))
122 				return FALSE;
123 			/* create *scratch* for alternate buffer */
124 			if ((bp1 = bfind("*scratch*", TRUE)) == NULL)
125 				return FALSE;
126 		}
127 	}
128 	if (bclear(bp) != TRUE)
129 		return TRUE;
130 	for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) {
131 		if (wp->w_bufp == bp) {
132 			bp2 = bp1->b_altb;	/* save alternate buffer */
133 			if (showbuffer(bp1, wp, WFMODE | WFFORCE | WFHARD))
134 				bp1->b_altb = bp2;
135 			else
136 				bp1 = bp2;
137 		}
138 	}
139 	if (bp == curbp)
140 		curbp = bp1;
141 	free(bp->b_linep);			/* Release header line.  */
142 	bp2 = NULL;				/* Find the header.	 */
143 	bp1 = bheadp;
144 	while (bp1 != bp) {
145 		if (bp1->b_altb == bp)
146 			bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
147 		bp2 = bp1;
148 		bp1 = bp1->b_bufp;
149 	}
150 	bp1 = bp1->b_bufp;			/* Next one in chain.	 */
151 	if (bp2 == NULL)			/* Unlink it.		 */
152 		bheadp = bp1;
153 	else
154 		bp2->b_bufp = bp1;
155 	while (bp1 != NULL) {			/* Finish with altb's	 */
156 		if (bp1->b_altb == bp)
157 			bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
158 		bp1 = bp1->b_bufp;
159 	}
160 	free(bp->b_bname);			/* Release name block	 */
161 	free(bp);				/* Release buffer block */
162 	return TRUE;
163 }
164 
165 /*
166  * Save some buffers - just call anycb with the arg flag.
167  */
168 /* ARGSUSED */
169 int
170 savebuffers(f, n)
171 	int f, n;
172 {
173 	if (anycb(f) == ABORT)
174 		return ABORT;
175 	return TRUE;
176 }
177 
178 /*
179  * Display the buffer list. This is done
180  * in two parts. The "makelist" routine figures out
181  * the text, and puts it in a buffer. "popbuf"
182  * then pops the data onto the screen. Bound to
183  * "C-X C-B".
184  */
185 /* ARGSUSED */
186 int
187 listbuffers(f, n)
188 	int f, n;
189 {
190 	BUFFER *bp;
191 	MGWIN  *wp;
192 
193 	if ((bp = makelist()) == NULL || (wp = popbuf(bp)) == NULL)
194 		return FALSE;
195 	wp->w_dotp = bp->b_dotp;/* fix up if window already on screen */
196 	wp->w_doto = bp->b_doto;
197 	return TRUE;
198 }
199 
200 /*
201  * This routine rebuilds the text for the
202  * list buffers command. Return TRUE if
203  * everything works. Return FALSE if there
204  * is an error (if there is no memory).
205  */
206 static BUFFER *
207 makelist()
208 {
209 	int	w = ncol / 2;
210 	BUFFER *bp, *blp;
211 	LINE   *lp;
212 
213 	if ((blp = bfind("*Buffer List*", TRUE)) == NULL)
214 		return NULL;
215 	if (bclear(blp) != TRUE)
216 		return NULL;
217 	blp->b_flag &= ~BFCHG;		/* Blow away old.	 */
218 
219 	if (addlinef(blp, "%-*s%s", w, " MR Buffer", "Size   File") == FALSE ||
220 	    addlinef(blp, "%-*s%s", w, " -- ------", "----   ----") == FALSE)
221 		return NULL;
222 
223 	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
224 		RSIZE nbytes;
225 
226 		nbytes = 0;			/* Count bytes in buf.	 */
227 		if (bp != blp) {
228 			lp = lforw(bp->b_linep);
229 			while (lp != bp->b_linep) {
230 				nbytes += llength(lp) + 1;
231 				lp = lforw(lp);
232 			}
233 			if (nbytes)
234 				nbytes--;	/* no bonus newline	 */
235 		}
236 
237 		if (addlinef(blp, "%c%c%c %-*s%-6d %-*s",
238 		    (bp == curbp) ? '.' : ' ',	/* current buffer ? */
239 		    ((bp->b_flag & BFCHG) != 0) ? '*' : ' ',	/* changed ? */
240 		    ' ',			/* no readonly buffers yet */
241 		    w - 4,		/* four chars already written */
242 		    bp->b_bname,	/* buffer name */
243 		    nbytes,		/* buffer size */
244 		    w - 7,		/* seven chars already written */
245 		    bp->b_fname) == FALSE)
246 			return NULL;
247 	}
248 	blp->b_dotp = lforw(blp->b_linep);	/* put dot at beginning of
249 						 * buffer */
250 	blp->b_doto = 0;
251 	return blp;				/* All done		 */
252 }
253 
254 /*
255  * The argument "text" points to a format string.  Append this line to the
256  * buffer. Handcraft the EOL on the end.  Return TRUE if it worked and
257  * FALSE if you ran out of room.
258  */
259 int
260 addlinef(BUFFER *bp, char *fmt, ...)
261 {
262 	va_list ap;
263 	LINE  *lp;
264 	int    ntext;
265 	char   dummy[1];
266 
267 	va_start(ap, fmt);
268 	ntext = vsnprintf(dummy, 1, fmt, ap) + 1;
269 	if (ntext == -1) {
270 		va_end(ap);
271 		return FALSE;
272 	}
273 	if ((lp = lalloc(ntext)) == NULL) {
274 		va_end(ap);
275 		return FALSE;
276 	}
277 	vsnprintf(lp->l_text, ntext, fmt, ap);
278 	lp->l_used--;
279 	va_end(ap);
280 
281 	bp->b_linep->l_bp->l_fp = lp;		/* Hook onto the end	 */
282 	lp->l_bp = bp->b_linep->l_bp;
283 	bp->b_linep->l_bp = lp;
284 	lp->l_fp = bp->b_linep;
285 
286 	return TRUE;
287 }
288 
289 /*
290  * Look through the list of buffers, giving the user a chance to save them.
291  * Return TRUE if there are any changed buffers afterwards.  Buffers that
292  * don't have an associated file don't count.  Return FALSE if there are
293  * no changed buffers.
294  */
295 int
296 anycb(f)
297 	int     f;
298 {
299 	BUFFER *bp;
300 	int     s = FALSE, save = FALSE;
301 	char    prompt[NFILEN + 11];
302 
303 	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
304 		if (*(bp->b_fname) != '\0'
305 		    && (bp->b_flag & BFCHG) != 0) {
306 			snprintf(prompt, sizeof prompt, "Save file %s",
307 			    bp->b_fname);
308 			if ((f == TRUE || (save = eyorn(prompt)) == TRUE)
309 			    && buffsave(bp) == TRUE) {
310 				bp->b_flag &= ~BFCHG;
311 				upmodes(bp);
312 			} else
313 				s = TRUE;
314 			if (save == ABORT)
315 				return (save);
316 			save = TRUE;
317 		}
318 	}
319 	if (save == FALSE /* && kbdmop == NULL */ )	/* experimental */
320 		ewprintf("(No files need saving)");
321 	return s;
322 }
323 
324 /*
325  * Search for a buffer, by name.
326  * If not found, and the "cflag" is TRUE,
327  * create a buffer and put it in the list of
328  * all buffers. Return pointer to the BUFFER
329  * block for the buffer.
330  */
331 BUFFER *
332 bfind(bname, cflag)
333 	char *bname;
334 	int   cflag;
335 {
336 	BUFFER	*bp;
337 	LINE	*lp;
338 	int	 i;
339 
340 	bp = bheadp;
341 	while (bp != NULL) {
342 		if (strcmp(bname, bp->b_bname) == 0)
343 			return bp;
344 		bp = bp->b_bufp;
345 	}
346 	if (cflag != TRUE)
347 		return NULL;
348 	/* NOSTRICT */
349 	if ((bp = (BUFFER *) malloc(sizeof(BUFFER))) == NULL) {
350 		ewprintf("Can't get %d bytes", sizeof(BUFFER));
351 		return NULL;
352 	}
353 	if ((bp->b_bname = malloc((strlen(bname) + 1))) == NULL) {
354 		ewprintf("Can't get %d bytes", strlen(bname) + 1);
355 		free((char *) bp);
356 		return NULL;
357 	}
358 	if ((lp = lalloc(0)) == NULL) {
359 		free(bp->b_bname);
360 		free((char *) bp);
361 		return NULL;
362 	}
363 	bp->b_altb = bp->b_bufp = NULL;
364 	bp->b_dotp = lp;
365 	bp->b_doto = 0;
366 	bp->b_markp = NULL;
367 	bp->b_marko = 0;
368 	bp->b_flag = defb_flag;
369 	bp->b_nwnd = 0;
370 	bp->b_linep = lp;
371 	bp->b_nmodes = defb_nmodes;
372 	i = 0;
373 	do {
374 		bp->b_modes[i] = defb_modes[i];
375 	} while (i++ < defb_nmodes);
376 	bp->b_fname[0] = '\0';
377 	bzero(&bp->b_fi, sizeof(bp->b_fi));
378 	(void) strcpy(bp->b_bname, bname);
379 	lp->l_fp = lp;
380 	lp->l_bp = lp;
381 	bp->b_bufp = bheadp;
382 	bheadp = bp;
383 	return bp;
384 }
385 
386 /*
387  * This routine blows away all of the text
388  * in a buffer. If the buffer is marked as changed
389  * then we ask if it is ok to blow it away; this is
390  * to save the user the grief of losing text. The
391  * window chain is nearly always wrong if this gets
392  * called; the caller must arrange for the updates
393  * that are required. Return TRUE if everything
394  * looks good.
395  */
396 int
397 bclear(bp)
398 	BUFFER *bp;
399 {
400 	LINE  *lp;
401 	int    s;
402 
403 	if ((bp->b_flag & BFCHG) != 0	/* Changed.		 */
404 	    && (s = eyesno("Buffer modified; kill anyway")) != TRUE)
405 		return (s);
406 	bp->b_flag &= ~BFCHG;	/* Not changed		 */
407 	while ((lp = lforw(bp->b_linep)) != bp->b_linep)
408 		lfree(lp);
409 	bp->b_dotp = bp->b_linep;	/* Fix "."		 */
410 	bp->b_doto = 0;
411 	bp->b_markp = NULL;	/* Invalidate "mark"	 */
412 	bp->b_marko = 0;
413 	return TRUE;
414 }
415 
416 /*
417  * Display the given buffer in the given window. Flags indicated
418  * action on redisplay.
419  */
420 int
421 showbuffer(bp, wp, flags)
422 	BUFFER *bp;
423 	MGWIN  *wp;
424 	int     flags;
425 {
426 	BUFFER *obp;
427 	MGWIN  *owp;
428 
429 	if (wp->w_bufp == bp) {	/* Easy case!	 */
430 		wp->w_flag |= flags;
431 		return TRUE;
432 	}
433 	/* First, dettach the old buffer from the window */
434 	if ((bp->b_altb = obp = wp->w_bufp) != NULL) {
435 		if (--obp->b_nwnd == 0) {
436 			obp->b_dotp = wp->w_dotp;
437 			obp->b_doto = wp->w_doto;
438 			obp->b_markp = wp->w_markp;
439 			obp->b_marko = wp->w_marko;
440 		}
441 	}
442 	/* Now, attach the new buffer to the window */
443 	wp->w_bufp = bp;
444 
445 	if (bp->b_nwnd++ == 0) {	/* First use.		 */
446 		wp->w_dotp = bp->b_dotp;
447 		wp->w_doto = bp->b_doto;
448 		wp->w_markp = bp->b_markp;
449 		wp->w_marko = bp->b_marko;
450 	} else
451 		/* already on screen, steal values from other window */
452 		for (owp = wheadp; owp != NULL; owp = wp->w_wndp)
453 			if (wp->w_bufp == bp && owp != wp) {
454 				wp->w_dotp = owp->w_dotp;
455 				wp->w_doto = owp->w_doto;
456 				wp->w_markp = owp->w_markp;
457 				wp->w_marko = owp->w_marko;
458 				break;
459 			}
460 	wp->w_flag |= WFMODE | flags;
461 	return TRUE;
462 }
463 
464 /*
465  * Pop the buffer we got passed onto the screen.
466  * Returns a status.
467  */
468 MGWIN *
469 popbuf(bp)
470 	BUFFER *bp;
471 {
472 	MGWIN  *wp;
473 
474 	if (bp->b_nwnd == 0) {	/* Not on screen yet.	 */
475 		if ((wp = wpopup()) == NULL)
476 			return NULL;
477 	} else
478 		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
479 			if (wp->w_bufp == bp) {
480 				wp->w_flag |= WFHARD | WFFORCE;
481 				return wp;
482 			}
483 	if (showbuffer(bp, wp, WFHARD) != TRUE)
484 		return NULL;
485 	return wp;
486 }
487 
488 /*
489  * Insert another buffer at dot.  Very useful.
490  */
491 /* ARGSUSED */
492 int
493 bufferinsert(f, n)
494 {
495 	BUFFER *bp;
496 	LINE   *clp;
497 	int     clo;
498 	int     nline;
499 	int     s;
500 	char    bufn[NBUFN];
501 
502 	/* Get buffer to use from user */
503 	if (curbp->b_altb != NULL)
504 		s = eread("Insert buffer: (default %s) ", bufn, NBUFN,
505 			  EFNEW | EFBUF, &(curbp->b_altb->b_bname), NULL);
506 	else
507 		s = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF, NULL);
508 	if (s == ABORT)
509 		return (s);
510 	if (s == FALSE && curbp->b_altb != NULL)
511 		bp = curbp->b_altb;
512 	else if ((bp = bfind(bufn, FALSE)) == NULL)
513 		return FALSE;
514 
515 	if (bp == curbp) {
516 		ewprintf("Cannot insert buffer into self");
517 		return FALSE;
518 	}
519 	/* insert the buffer */
520 	nline = 0;
521 	clp = lforw(bp->b_linep);
522 	for (;;) {
523 		for (clo = 0; clo < llength(clp); clo++)
524 			if (linsert(1, lgetc(clp, clo)) == FALSE)
525 				return FALSE;
526 		if ((clp = lforw(clp)) == bp->b_linep)
527 			break;
528 		if (newline(FFRAND, 1) == FALSE)	/* fake newline */
529 			return FALSE;
530 		nline++;
531 	}
532 	if (nline == 1)
533 		ewprintf("[Inserted 1 line]");
534 	else
535 		ewprintf("[Inserted %d lines]", nline);
536 
537 	clp = curwp->w_linep;	/* cosmetic adjustment */
538 	if (curwp->w_dotp == clp) {	/* for offscreen insert */
539 		while (nline-- && lback(clp) != curbp->b_linep)
540 			clp = lback(clp);
541 		curwp->w_linep = clp;	/* adjust framing.	 */
542 		curwp->w_flag |= WFHARD;
543 	}
544 	return (TRUE);
545 }
546 
547 /*
548  * Turn off the dirty bit on this buffer.
549  */
550 /* ARGSUSED */
551 int
552 notmodified(f, n)
553 {
554 	MGWIN *wp;
555 
556 	curbp->b_flag &= ~BFCHG;
557 	wp = wheadp;		/* Update mode lines.	 */
558 	while (wp != NULL) {
559 		if (wp->w_bufp == curbp)
560 			wp->w_flag |= WFMODE;
561 		wp = wp->w_wndp;
562 	}
563 	ewprintf("Modification-flag cleared");
564 	return TRUE;
565 }
566 
567 #ifndef NO_HELP
568 /*
569  * Popbuf and set all windows to top of buffer.	 Currently only used by
570  * help functions.
571  */
572 int
573 popbuftop(bp)
574 	BUFFER *bp;
575 {
576 	MGWIN *wp;
577 
578 	bp->b_dotp = lforw(bp->b_linep);
579 	bp->b_doto = 0;
580 	if (bp->b_nwnd != 0) {
581 		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
582 			if (wp->w_bufp == bp) {
583 				wp->w_dotp = bp->b_dotp;
584 				wp->w_doto = 0;
585 				wp->w_flag |= WFHARD;
586 			}
587 	}
588 	return popbuf(bp) != NULL;
589 }
590 #endif
591