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