xref: /openbsd-src/usr.bin/mg/buffer.c (revision 2badd5e3f47d2d4252969cd98d7042b4e701b5ac)
1 /*	$OpenBSD: buffer.c,v 1.16 2001/08/18 21:36:11 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 			sprintf(prompt, "Save file %s", bp->b_fname);
307 			if ((f == TRUE || (save = eyorn(prompt)) == TRUE)
308 			    && buffsave(bp) == TRUE) {
309 				bp->b_flag &= ~BFCHG;
310 				upmodes(bp);
311 			} else
312 				s = TRUE;
313 			if (save == ABORT)
314 				return (save);
315 			save = TRUE;
316 		}
317 	}
318 	if (save == FALSE /* && kbdmop == NULL */ )	/* experimental */
319 		ewprintf("(No files need saving)");
320 	return s;
321 }
322 
323 /*
324  * Search for a buffer, by name.
325  * If not found, and the "cflag" is TRUE,
326  * create a buffer and put it in the list of
327  * all buffers. Return pointer to the BUFFER
328  * block for the buffer.
329  */
330 BUFFER *
331 bfind(bname, cflag)
332 	char *bname;
333 	int   cflag;
334 {
335 	BUFFER	*bp;
336 	LINE	*lp;
337 	int	 i;
338 
339 	bp = bheadp;
340 	while (bp != NULL) {
341 		if (strcmp(bname, bp->b_bname) == 0)
342 			return bp;
343 		bp = bp->b_bufp;
344 	}
345 	if (cflag != TRUE)
346 		return NULL;
347 	/* NOSTRICT */
348 	if ((bp = (BUFFER *) malloc(sizeof(BUFFER))) == NULL) {
349 		ewprintf("Can't get %d bytes", sizeof(BUFFER));
350 		return NULL;
351 	}
352 	if ((bp->b_bname = malloc((strlen(bname) + 1))) == NULL) {
353 		ewprintf("Can't get %d bytes", strlen(bname) + 1);
354 		free((char *) bp);
355 		return NULL;
356 	}
357 	if ((lp = lalloc(0)) == NULL) {
358 		free(bp->b_bname);
359 		free((char *) bp);
360 		return NULL;
361 	}
362 	bp->b_altb = bp->b_bufp = NULL;
363 	bp->b_dotp = lp;
364 	bp->b_doto = 0;
365 	bp->b_markp = NULL;
366 	bp->b_marko = 0;
367 	bp->b_flag = defb_flag;
368 	bp->b_nwnd = 0;
369 	bp->b_linep = lp;
370 	bp->b_nmodes = defb_nmodes;
371 	i = 0;
372 	do {
373 		bp->b_modes[i] = defb_modes[i];
374 	} while (i++ < defb_nmodes);
375 	bp->b_fname[0] = '\0';
376 	bzero(&bp->b_fi, sizeof(bp->b_fi));
377 	(void) strcpy(bp->b_bname, bname);
378 	lp->l_fp = lp;
379 	lp->l_bp = lp;
380 	bp->b_bufp = bheadp;
381 	bheadp = bp;
382 	return bp;
383 }
384 
385 /*
386  * This routine blows away all of the text
387  * in a buffer. If the buffer is marked as changed
388  * then we ask if it is ok to blow it away; this is
389  * to save the user the grief of losing text. The
390  * window chain is nearly always wrong if this gets
391  * called; the caller must arrange for the updates
392  * that are required. Return TRUE if everything
393  * looks good.
394  */
395 int
396 bclear(bp)
397 	BUFFER *bp;
398 {
399 	LINE  *lp;
400 	int    s;
401 
402 	if ((bp->b_flag & BFCHG) != 0	/* Changed.		 */
403 	    && (s = eyesno("Buffer modified; kill anyway")) != TRUE)
404 		return (s);
405 	bp->b_flag &= ~BFCHG;	/* Not changed		 */
406 	while ((lp = lforw(bp->b_linep)) != bp->b_linep)
407 		lfree(lp);
408 	bp->b_dotp = bp->b_linep;	/* Fix "."		 */
409 	bp->b_doto = 0;
410 	bp->b_markp = NULL;	/* Invalidate "mark"	 */
411 	bp->b_marko = 0;
412 	return TRUE;
413 }
414 
415 /*
416  * Display the given buffer in the given window. Flags indicated
417  * action on redisplay.
418  */
419 int
420 showbuffer(bp, wp, flags)
421 	BUFFER *bp;
422 	MGWIN  *wp;
423 	int     flags;
424 {
425 	BUFFER *obp;
426 	MGWIN  *owp;
427 
428 	if (wp->w_bufp == bp) {	/* Easy case!	 */
429 		wp->w_flag |= flags;
430 		return TRUE;
431 	}
432 	/* First, dettach the old buffer from the window */
433 	if ((bp->b_altb = obp = wp->w_bufp) != NULL) {
434 		if (--obp->b_nwnd == 0) {
435 			obp->b_dotp = wp->w_dotp;
436 			obp->b_doto = wp->w_doto;
437 			obp->b_markp = wp->w_markp;
438 			obp->b_marko = wp->w_marko;
439 		}
440 	}
441 	/* Now, attach the new buffer to the window */
442 	wp->w_bufp = bp;
443 
444 	if (bp->b_nwnd++ == 0) {	/* First use.		 */
445 		wp->w_dotp = bp->b_dotp;
446 		wp->w_doto = bp->b_doto;
447 		wp->w_markp = bp->b_markp;
448 		wp->w_marko = bp->b_marko;
449 	} else
450 		/* already on screen, steal values from other window */
451 		for (owp = wheadp; owp != NULL; owp = wp->w_wndp)
452 			if (wp->w_bufp == bp && owp != wp) {
453 				wp->w_dotp = owp->w_dotp;
454 				wp->w_doto = owp->w_doto;
455 				wp->w_markp = owp->w_markp;
456 				wp->w_marko = owp->w_marko;
457 				break;
458 			}
459 	wp->w_flag |= WFMODE | flags;
460 	return TRUE;
461 }
462 
463 /*
464  * Pop the buffer we got passed onto the screen.
465  * Returns a status.
466  */
467 MGWIN *
468 popbuf(bp)
469 	BUFFER *bp;
470 {
471 	MGWIN  *wp;
472 
473 	if (bp->b_nwnd == 0) {	/* Not on screen yet.	 */
474 		if ((wp = wpopup()) == NULL)
475 			return NULL;
476 	} else
477 		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
478 			if (wp->w_bufp == bp) {
479 				wp->w_flag |= WFHARD | WFFORCE;
480 				return wp;
481 			}
482 	if (showbuffer(bp, wp, WFHARD) != TRUE)
483 		return NULL;
484 	return wp;
485 }
486 
487 /*
488  * Insert another buffer at dot.  Very useful.
489  */
490 /* ARGSUSED */
491 int
492 bufferinsert(f, n)
493 {
494 	BUFFER *bp;
495 	LINE   *clp;
496 	int     clo;
497 	int     nline;
498 	int     s;
499 	char    bufn[NBUFN];
500 
501 	/* Get buffer to use from user */
502 	if (curbp->b_altb != NULL)
503 		s = eread("Insert buffer: (default %s) ", bufn, NBUFN,
504 			  EFNEW | EFBUF, &(curbp->b_altb->b_bname), NULL);
505 	else
506 		s = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF, NULL);
507 	if (s == ABORT)
508 		return (s);
509 	if (s == FALSE && curbp->b_altb != NULL)
510 		bp = curbp->b_altb;
511 	else if ((bp = bfind(bufn, FALSE)) == NULL)
512 		return FALSE;
513 
514 	if (bp == curbp) {
515 		ewprintf("Cannot insert buffer into self");
516 		return FALSE;
517 	}
518 	/* insert the buffer */
519 	nline = 0;
520 	clp = lforw(bp->b_linep);
521 	for (;;) {
522 		for (clo = 0; clo < llength(clp); clo++)
523 			if (linsert(1, lgetc(clp, clo)) == FALSE)
524 				return FALSE;
525 		if ((clp = lforw(clp)) == bp->b_linep)
526 			break;
527 		if (newline(FFRAND, 1) == FALSE)	/* fake newline */
528 			return FALSE;
529 		nline++;
530 	}
531 	if (nline == 1)
532 		ewprintf("[Inserted 1 line]");
533 	else
534 		ewprintf("[Inserted %d lines]", nline);
535 
536 	clp = curwp->w_linep;	/* cosmetic adjustment */
537 	if (curwp->w_dotp == clp) {	/* for offscreen insert */
538 		while (nline-- && lback(clp) != curbp->b_linep)
539 			clp = lback(clp);
540 		curwp->w_linep = clp;	/* adjust framing.	 */
541 		curwp->w_flag |= WFHARD;
542 	}
543 	return (TRUE);
544 }
545 
546 /*
547  * Turn off the dirty bit on this buffer.
548  */
549 /* ARGSUSED */
550 int
551 notmodified(f, n)
552 {
553 	MGWIN *wp;
554 
555 	curbp->b_flag &= ~BFCHG;
556 	wp = wheadp;		/* Update mode lines.	 */
557 	while (wp != NULL) {
558 		if (wp->w_bufp == curbp)
559 			wp->w_flag |= WFMODE;
560 		wp = wp->w_wndp;
561 	}
562 	ewprintf("Modification-flag cleared");
563 	return TRUE;
564 }
565 
566 #ifndef NO_HELP
567 /*
568  * Popbuf and set all windows to top of buffer.	 Currently only used by
569  * help functions.
570  */
571 int
572 popbuftop(bp)
573 	BUFFER *bp;
574 {
575 	MGWIN *wp;
576 
577 	bp->b_dotp = lforw(bp->b_linep);
578 	bp->b_doto = 0;
579 	if (bp->b_nwnd != 0) {
580 		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
581 			if (wp->w_bufp == bp) {
582 				wp->w_dotp = bp->b_dotp;
583 				wp->w_doto = 0;
584 				wp->w_flag |= WFHARD;
585 			}
586 	}
587 	return popbuf(bp) != NULL;
588 }
589 #endif
590