1 /* $Id: buffer.c,v 1.16 2001/05/25 15:36:52 amura Exp $ */
2 /*
3  *		Buffer handling.
4  */
5 
6 /*
7  * $Log: buffer.c,v $
8  * Revision 1.16  2001/05/25 15:36:52  amura
9  * now buffers have only one mark (before windows have one mark)
10  *
11  * Revision 1.15  2001/05/08 17:52:43  amura
12  * display buffer size in bufferlist
13  *
14  * Revision 1.14  2001/03/02 08:49:04  amura
15  * now AUTOSAVE feature implemented almost all (except for WIN32
16  *
17  * Revision 1.13  2001/02/28 17:06:05  amura
18  * buffer size to use eread() is more secure
19  *
20  * Revision 1.12  2001/02/18 19:26:41  amura
21  * remove malloc() prototype
22  *
23  * Revision 1.11  2001/02/18 17:07:23  amura
24  * append AUTOSAVE feature (but NOW not work)
25  *
26  * Revision 1.10  2001/01/05 14:06:59  amura
27  * first implementation of Hojo Kanji support
28  *
29  * -- snip --
30  *
31  * Revision 1.1  1999/05/19  03:47:59  amura
32  * Initial revision
33  *
34  */
35 /* 90.01.29	Modified for Ng 1.0 by S.Yoshida */
36 
37 #include	"config.h"	/* 90.12.20  by S.Yoshida */
38 #include	"def.h"
39 #include	"kbd.h"			/* needed for modes */
40 #ifdef	UNDO
41 #include	"undo.h"
42 #endif
43 
44 #ifdef  VARIABLE_TAB
45 int defb_tab = 8;
46 int cmode_tab = 0;
47 #endif  /* VARIABLE_TAB */
48 #define	GETNUMLEN	6
49 static BUFFER *makelist pro((void));
50 static long buffersize pro((BUFFER*));
51 
52 #ifdef	MOVE_BUFFER		/* 95.08.29 by M.Suzuki	*/
53 /* Move to the next buffer	*/
nextbuffer(f,n)54 nextbuffer(f,n)
55 {
56 	register BUFFER *bp;
57 
58 	/* Get buffer to use from user */
59 	if ((curbp->b_altb == NULL)
60 	    && ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
61 		 return FALSE;
62 
63 	bp = bheadp;
64 	while (bp != NULL) {
65 		if (fncmp(curbp->b_bname, bp->b_bname) == 0){
66 			bp = bp->b_bufp;
67 			break;
68 		}
69 		bp = bp->b_bufp;
70 	}
71 	if ( bp == NULL){
72 		bp = bheadp;
73 	}
74 
75 	/* and put it in current window */
76 	curbp = bp;
77 	return showbuffer(bp, curwp, WFFORCE|WFHARD);
78 }
79 
80 /* Move to the previous buffer	*/
prevbuffer(f,n)81 prevbuffer(f,n)
82 {
83 	register BUFFER *bp,*bp1;
84 
85 	if ((curbp->b_altb == NULL)
86 	    && ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
87 		return FALSE;
88 
89 	bp1 = bp = bheadp;
90 	while (bp != NULL) {
91 		if (fncmp(curbp->b_bname, bp->b_bname) == 0){
92 			if( bp == bp1 ){		/* abnomal found */
93 				while( bp != NULL ){	/* last search */
94 					bp1 = bp;
95 					bp = bp->b_bufp;
96 				}
97 			}
98 			break;
99 		}
100 		bp1 = bp;
101 		bp = bp->b_bufp;
102 	}
103 
104 	/* and put it in current window */
105 	curbp = bp1;
106 	return showbuffer(bp1, curwp, WFFORCE|WFHARD);
107 }
108 #endif	/* MOVE_BUFFER */
109 
110 /*
111  * Attach a buffer to a window. The values of dot and mark come
112  * from the buffer if the use count is 0. Otherwise, they come
113  * from some other window.  *scratch* is the default alternate
114  * buffer.
115  */
116 /*ARGSUSED*/
usebuffer(f,n)117 usebuffer(f, n)
118 {
119 	register BUFFER *bp;
120 	register int	s;
121 	char		bufn[NBUFN];
122 
123 	/* Get buffer to use from user */
124 	if ((curbp->b_altb == NULL)
125 	    && ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
126 		s=eread("Switch to buffer: ", bufn, sizeof(bufn), EFNEW|EFBUF);
127 	else
128 		s=eread("Switch to buffer: (default %s) ", bufn, sizeof(bufn),
129 			 EFNEW|EFBUF, curbp->b_altb->b_bname);
130 
131 	if (s == ABORT) return s;
132 	if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb ;
133 	else if ((bp=bfind(bufn, TRUE)) == NULL) return FALSE;
134 
135 	/* and put it in current window */
136 	curbp = bp;
137 	return showbuffer(bp, curwp, WFFORCE|WFHARD);
138 }
139 
140 /*
141  * pop to buffer asked for by the user.
142  */
143 /*ARGSUSED*/
poptobuffer(f,n)144 poptobuffer(f, n)
145 {
146 	register BUFFER *bp;
147 	register WINDOW *wp;
148 	register int	s;
149 	char		bufn[NBUFN];
150 	WINDOW	*popbuf();
151 
152 	/* Get buffer to use from user */
153 	if ((curbp->b_altb == NULL)
154 	    && ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
155 		s=eread("Switch to buffer in other window: ",
156 			bufn, sizeof(bufn), EFNEW|EFBUF);
157 	else
158 		s=eread("Switch to buffer in other window: (default %s) ",
159 			 bufn, sizeof(bufn), EFNEW|EFBUF, curbp->b_altb->b_bname);
160 	if (s == ABORT) return s;
161 	if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb ;
162 	else if ((bp=bfind(bufn, TRUE)) == NULL) return FALSE;
163 
164 	/* and put it in a new window */
165 	if ((wp = popbuf(bp)) == NULL) return FALSE;
166 	curbp = bp;
167 	curwp = wp;
168 	return TRUE;
169 }
170 
171 /*
172  * Dispose of a buffer, by name.
173  * Ask for the name. Look it up (don't get too
174  * upset if it isn't there at all!). Clear the buffer (ask
175  * if the buffer has been changed). Then free the header
176  * line and the buffer header. Bound to "C-X K".
177  */
178 /*ARGSUSED*/
killbuffer(f,n)179 killbuffer(f, n)
180 {
181 	register BUFFER *bp;
182 	register BUFFER *bp1;
183 	register BUFFER *bp2;
184 	WINDOW		*wp;
185 	register int	s;
186 	char		bufn[NBUFN];
187 
188 	if ((s=eread("Kill buffer: (default %s) ", bufn, sizeof(bufn),
189 		     EFNEW|EFBUF, curbp->b_bname)) == ABORT) return (s);
190 	else if (s == FALSE) bp = curbp;
191 	else if ((bp=bfind(bufn, FALSE)) == NULL) return FALSE;
192 
193 	/* find some other buffer to display. try the alternate buffer,
194 	 * then the first different buffer in the buffer list.	if
195 	 * there's only one buffer, create buffer *scratch* and make
196 	 * it the alternate buffer.  return if *scratch* is only buffer
197 	 */
198 #ifdef	BUGFIX	/* 90.02.22  by S.Yoshida */
199 	if ((bp1 = bp->b_altb) == NULL || bp1 == bp) {
200 #else	/* ORIGINAL */
201 	if ((bp1 = bp->b_altb) == NULL) {
202 #endif	/* BUGFIX */
203 		bp1 = (bp == bheadp) ? bp->b_bufp : bheadp;
204 		if (bp1 == NULL) {
205 			/* only one buffer. see if it's *scratch* */
206 			if (bp == bfind("*scratch*",FALSE))
207 				return FALSE;
208 			/* create *scratch* for alternate buffer */
209 			if ((bp1 = bfind("*scratch*",TRUE)) == NULL)
210 				return FALSE;
211 		}
212 	}
213 	if (bp->b_fname == NULL) {
214 	  /* Do not ask for saving
215 	     if the buffer is not associated with a file.
216 	     by Tillanosoft */
217 	  bp->b_flag  &= ~BFCHG;
218 	}
219 	if (bclear(bp) != TRUE) return FALSE;
220 	for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) {
221 	    if (wp->w_bufp == bp) {
222 		bp2 = bp1->b_altb;		/* save alternate buffer */
223 		if(showbuffer(bp1, wp, WFMODE|WFFORCE|WFHARD) != FALSE)
224 			bp1->b_altb = bp2;
225 		else	bp1 = bp2;
226 	    }
227 	}
228 	if (bp == curbp) curbp = bp1;
229 	free((char *) bp->b_linep);		/* Release header line. */
230 	bp2 = NULL;				/* Find the header.	*/
231 	bp1 = bheadp;
232 	while (bp1 != bp) {
233 		if (bp1->b_altb == bp)
234 			bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
235 		bp2 = bp1;
236 		bp1 = bp1->b_bufp;
237 	}
238 	bp1 = bp1->b_bufp;			/* Next one in chain.	*/
239 	if (bp2 == NULL)			/* Unlink it.		*/
240 		bheadp = bp1;
241 	else
242 		bp2->b_bufp = bp1;
243 	while (bp1 != NULL) {			/* Finish with altb's	*/
244 		if (bp1->b_altb == bp)
245 			bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
246 		bp1 = bp1->b_bufp;
247 	}
248 	if (bp->b_fname != NULL)		/* Release filename block */
249 		free(bp->b_fname);
250 #ifdef	EXTD_DIR
251 	if (bp->b_cwd != NULL)			/* Release pathname block */
252 		free(bp->b_cwd);
253 #endif
254 	free(bp->b_bname);			/* Release name block	*/
255 #ifdef	UNDO
256 	undo_clean(bp);				/* Release undo data	*/
257 #endif
258 	free((char *) bp);			/* Release buffer block */
259 	return TRUE;
260 }
261 
262 /*
263  * Save some buffers - just call anycb with the arg flag.
264  */
265 /*ARGSUSED*/
266 savebuffers(f, n)
267 {
268 	if (anycb(f) == ABORT) return ABORT;
269 	return TRUE;
270 }
271 
272 /*
273  * Display the buffer list. This is done
274  * in two parts. The "makelist" routine figures out
275  * the text, and puts it in a buffer. "popbuf"
276  * then pops the data onto the screen. Bound to
277  * "C-X C-B".
278  */
279 /*ARGSUSED*/
280 listbuffers(f, n)
281 {
282 	register BUFFER *bp;
283 	register WINDOW *wp;
284 	BUFFER		*makelist();
285 	WINDOW		*popbuf();
286 
287 	if ((bp=makelist()) == NULL || (wp=popbuf(bp)) == NULL)
288 		return FALSE;
289 	wp->w_dotp = bp->b_dotp;	/* fix up if window already on screen */
290 	wp->w_doto = bp->b_doto;
291 #ifdef BUFFER_MODE
292 	bp->b_modes[0] = name_mode("Buffer Menu");
293 	if (bp->b_modes[0] == NULL) {
294 	  bp->b_modes[0] = &map_table[0];
295 	  ewprintf("Could not find \"Buffer Menu\" mode");
296 	}
297 	else {
298 	  bp->b_nmodes = 0;
299 	}
300 #endif
301 	return TRUE;
302 }
303 
304 /*
305  * This routine rebuilds the text for the
306  * list buffers command. Return TRUE if
307  * everything works. Return FALSE if there
308  * is an error (if there is no memory).
309  */
310 BUFFER *
311 makelist() {
312 	register char	*cp1;
313 	register char	*cp2;
314 	register int	c;
315 	register BUFFER *bp;
316 	LINE		*lp;
317 	BUFFER		*blp;
318 	char		b[6+1];
319 	char		line[4+NBUFN+7+NFILEN+4];
320 #ifdef HANKANA
321 	int nhankana;
322 #endif
323 
324 	if ((blp = bfind("*Buffer List*", TRUE)) == NULL) return NULL;
325 	if (bclear(blp) != TRUE) return NULL;
326 #ifdef	AUTOSAVE	/* 96.12.24 by M.Suzuki	*/
327 	blp->b_flag &= ~(BFCHG|BFACHG);		/* Blow away old.	*/
328 #else
329   	blp->b_flag &= ~BFCHG;			/* Blow away old.	*/
330 #endif	/* AUTOSAVE	*/
331 
332 	(VOID) strcpy(line, " MR Buffer");
333 	cp1 = line + 10;
334 	while(cp1 < line + 4 + NBUFN + 1) *cp1++ = ' ';
335 	(VOID) strcpy(cp1, "Size   File");
336 	if (addline(blp, line) == FALSE) return NULL;
337 	(VOID) strcpy(line, " -- ------");
338 	cp1 = line + 10;
339 	while(cp1 < line + 4 + NBUFN + 1) *cp1++ = ' ';
340 	(VOID) strcpy(cp1, "----   ----");
341 	if (addline(blp, line) == FALSE) return NULL;
342 	bp = bheadp;				/* For all buffers	*/
343 	while (bp != NULL) {
344 		sprintf(line,"%c%c%c %-30.30s %7ld %s",
345 				(bp == curbp) ? '.' : ' ',
346 				(bp->b_flag & BFCHG) ? '*' : ' ',
347 #ifdef	READONLY	/* 91.01.05  by S.Yoshida */
348 				(bp->b_flag & BFRONLY) ? '%' : ' ',
349 #else
350 				' ',
351 #endif
352 				bp->b_bname, buffersize(bp),
353 				(bp->b_fname != NULL) ? bp->b_fname : "" );
354 		if (addline(blp, line) == FALSE)
355 			return NULL;
356 		bp = bp->b_bufp;
357 	}
358 	blp->b_dotp = lforw(blp->b_linep);	/* put dot at beginning of buffer */
359 	blp->b_doto = 0;
360 #ifdef	READONLY	/* 91.02.06  by N.Kamei */
361 	blp->b_flag |= BFRONLY;
362 #endif	/* READONLY */
363 	return blp;				/* All done		*/
364 }
365 
366 /*
367  * Calculate buffer size
368  */
369 static long
370 buffersize(bp)
371     BUFFER *bp;
372 {
373     long size = 0;
374     LINE *line;
375     line = bp->b_linep;
376     line = lforw(line);
377     while (line != bp->b_linep) {
378 	size += llength(line);
379 	size++;
380 	line = lforw(line);
381     }
382     return size;
383 }
384 
385 /*
386  * The argument "text" points to
387  * a string. Append this line to the
388  * buffer. Handcraft the EOL
389  * on the end. Return TRUE if it worked and
390  * FALSE if you ran out of room.
391  */
392 addline(bp, text) register BUFFER *bp; char *text; {
393 	register LINE	*lp;
394 	register int	i;
395 	register int	ntext;
396 
397 	ntext = strlen(text);
398 	if ((lp=lalloc(ntext)) == NULL)
399 		return FALSE;
400 	for (i=0; i<ntext; ++i)
401 		lputc(lp, i, text[i]);
402 	bp->b_linep->l_bp->l_fp = lp;		/* Hook onto the end	*/
403 	lp->l_bp = bp->b_linep->l_bp;
404 	bp->b_linep->l_bp = lp;
405 	lp->l_fp = bp->b_linep;
406 #ifdef CANTHAPPEN
407 	if (bp->b_dotp == bp->b_linep)		/* If "." is at the end	*/
408 		bp->b_dotp = lp;		/* move it to new line	*/
409 	if (bp->b_markp == bp->b_linep)		/* ditto for mark	*/
410 		bp->b_markp = lp;
411 #endif
412 	return TRUE;
413 }
414 
415 /*
416  * Look through the list of buffers, giving the user
417  * a chance to save them.  Return TRUE if there are
418  * any changed buffers afterwards. Buffers that don't
419  * have an associated file don't count. Return FALSE
420  * if there are no changed buffers.
421  */
422 anycb(f) {
423 	register BUFFER *bp;
424 	register int	s = FALSE, save = FALSE;
425 	char		*prompt;
426 	VOID		upmodes();
427 
428 	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
429 		if (bp->b_fname != NULL
430 		&&  (bp->b_flag&BFCHG) != 0) {
431 		    	prompt = alloca(10 + strlen(bp->b_fname) + 1);
432 			if (prompt != NULL) {
433 				(VOID) strcpy(prompt, "Save file ");
434 				(VOID) strcpy(prompt + 10, bp->b_fname);
435 			}
436 			else {
437 				prompt = "Save file <Somethings>";
438 			}
439 			if ((f == TRUE || (save = eyorn(prompt)) == TRUE)
440 			&&  buffsave(bp) == TRUE) {
441 #ifdef	AUTOSAVE	/* 96.12.24 by M.Suzuki	*/
442 				bp->b_flag &= ~(BFCHG|BFACHG);
443 #else
444 				bp->b_flag &= ~BFCHG;
445 #endif	/* AUTOSAVE	*/
446 				upmodes(bp);
447 			} else s = TRUE;
448 			if (save == ABORT) return (save);
449 			save = TRUE;
450 		}
451 	}
452 	if (save == FALSE /* && kbdmop == NULL */ )	/* experimental */
453 		ewprintf("(No files need saving)");
454 	return s;
455 }
456 
457 /*
458  * Search for a buffer, by name.
459  * If not found, and the "cflag" is TRUE,
460  * create a buffer and put it in the list of
461  * all buffers. Return pointer to the BUFFER
462  * block for the buffer.
463  */
464 BUFFER	*
465 bfind(bname, cflag) register char *bname; {
466 	register BUFFER *bp;
467 	register LINE	*lp;
468 	int i;
469 	extern int defb_nmodes;
470 	extern MAPS *defb_modes[PBMODES];
471 	extern int defb_flag;
472 	extern int defb_tab;
473 
474 	bp = bheadp;
475 	while (bp != NULL) {
476 		if (fncmp(bname, bp->b_bname) == 0)
477 			return bp;
478 		bp = bp->b_bufp;
479 	}
480 	if (cflag!=TRUE) return NULL;
481 	/*NOSTRICT*/
482 	if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL) {
483 		ewprintf("Can't get %d bytes", sizeof(BUFFER));
484 		return NULL;
485 	}
486 	if ((bp->b_bname=malloc((unsigned)(strlen(bname)+1))) == NULL) {
487 		ewprintf("Can't get %d bytes", strlen(bname)+1);
488 		free((char *) bp);
489 		return NULL;
490 	}
491 	if ((lp = lalloc(0)) == NULL) {
492 		free(bp->b_bname);
493 		free((char *) bp);
494 		return NULL;
495 	}
496 #ifdef	UNDO
497 	bzero(bp->b_ustack, sizeof(UNDO_DATA *)*(UNDOSIZE+1));
498 	undo_reset(bp);
499 #endif
500 	bp->b_altb = bp->b_bufp	 = NULL;
501 	bp->b_dotp  = lp;
502 	bp->b_doto  = 0;
503 	bp->b_markp = NULL;
504 	bp->b_marko = 0;
505 	bp->b_flag  = defb_flag;
506 	bp->b_nwnd  = 0;
507 	bp->b_linep = lp;
508 	bp->b_nmodes = defb_nmodes;
509 #ifdef	KANJI	/* 90.01.29  by S.Yoshida */
510 	bp->b_kfio = NIL;	/* changed by amura */
511 				/* set in fileio.c  */
512 #endif	/* KANJI */
513 #ifdef  VARIABLE_TAB
514 	bp->b_tabwidth = defb_tab;
515 #endif  /* VARIABLE_TAB */
516 	i = 0;
517 	do {
518 	    bp->b_modes[i] = defb_modes[i];
519 	} while(i++ < defb_nmodes);
520 	bp->b_fname = NULL;
521 #ifdef	EXTD_DIR
522 	bp->b_cwd = NULL;
523 	if (curbp) {
524 	  if (curbp->b_cwd == NULL) {
525 	    extern void storecwd pro((BUFFER *bp));
526 	    storecwd(curbp);
527 	  }
528 	  if (curbp->b_cwd != NULL &&
529 	      (bp->b_cwd=malloc(strlen(curbp->b_cwd)+1)) != NULL)
530 	    strcpy(bp->b_cwd, curbp->b_cwd);
531 	}
532 #endif
533 	(VOID) strcpy(bp->b_bname, bname);
534 	lp->l_fp = lp;
535 	lp->l_bp = lp;
536 	bp->b_bufp = bheadp;
537 	bheadp = bp;
538 	return bp;
539 }
540 
541 /*
542  * This routine blows away all of the text
543  * in a buffer. If the buffer is marked as changed
544  * then we ask if it is ok to blow it away; this is
545  * to save the user the grief of losing text. The
546  * window chain is nearly always wrong if this gets
547  * called; the caller must arrange for the updates
548  * that are required. Return TRUE if everything
549  * looks good.
550  */
551 bclear(bp) register BUFFER *bp; {
552 	register LINE	*lp;
553 	register int	s;
554 	VOID		lfree();
555 
556 	if ((bp->b_flag&BFCHG) != 0		/* Changed.		*/
557 	&& (s=eyesno("Buffer modified; kill anyway")) != TRUE)
558 		return (s);
559 #ifdef	AUTOSAVE	/* 96.12.25 by M.Suzuki	*/
560 	if (bp->b_bname && bp->b_bname[0] != '*' ){/* file buffer only	*/
561 		del_autosave_file(bp->b_fname);
562 	}
563 	bp->b_flag  &= ~(BFCHG|BFACHG);		/* Not changed		*/
564 #else
565 	bp->b_flag  &= ~BFCHG;			/* Not changed		*/
566 #endif	/* AUTOSAVE	*/
567 	while ((lp=lforw(bp->b_linep)) != bp->b_linep)
568 		lfree(lp);
569 	bp->b_dotp  = bp->b_linep;		/* Fix "."		*/
570 	bp->b_doto  = 0;
571 	bp->b_markp = NULL;			/* Invalidate "mark"	*/
572 	bp->b_marko = 0;
573 	return TRUE;
574 }
575 
576 /*
577  * Display the given buffer in the given window. Flags indicated
578  * action on redisplay.
579  */
580 showbuffer(bp, wp, flags) register BUFFER *bp; register WINDOW *wp; {
581 	register BUFFER *obp;
582 	WINDOW		*owp;
583 
584 	if (wp->w_bufp == bp) {			/* Easy case!	*/
585 		wp->w_flag |= flags;
586 		return TRUE;
587 	}
588 
589 	/* First, dettach the old buffer from the window */
590 	if ((bp->b_altb = obp = wp->w_bufp) != NULL) {
591 		if (--obp->b_nwnd == 0) {
592 			obp->b_dotp  = wp->w_dotp;
593 			obp->b_doto  = wp->w_doto;
594 }
595 	}
596 
597 	/* Now, attach the new buffer to the window */
598 	wp->w_bufp = bp;
599 
600 	if (bp->b_nwnd++ == 0) {		/* First use.		*/
601 		wp->w_dotp  = bp->b_dotp;
602 		wp->w_doto  = bp->b_doto;
603 	} else
604 	/* already on screen, steal values from other window */
605 #ifdef	BUGFIX	/* ? 90.12.08    Sawayanagi Yosirou */
606 		for (owp = wheadp; owp != NULL; owp = owp->w_wndp)
607 			if (owp->w_bufp == bp && owp != wp) {
608 #else	/* ORIGINAL */
609 		for (owp = wheadp; owp != NULL; owp = wp->w_wndp)
610 			if (wp->w_bufp == bp && owp != wp) {
611 #endif	/* BUGFIX */
612 				wp->w_dotp  = owp->w_dotp;
613 				wp->w_doto  = owp->w_doto;
614 				break;
615 			}
616 	wp->w_flag |= WFMODE|flags;
617 	return TRUE;
618 }
619 
620 /*
621  * Pop the buffer we got passed onto the screen.
622  * Returns a status.
623  */
624 WINDOW *
625 popbuf(bp) register BUFFER *bp; {
626 	register WINDOW *wp;
627 
628 	if (bp->b_nwnd == 0) {		/* Not on screen yet.	*/
629 		if ((wp=wpopup()) == NULL) return NULL;
630 	} else
631 		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
632 			if (wp->w_bufp == bp) {
633 				wp->w_flag |= WFHARD|WFFORCE;
634 				return wp ;
635 			}
636 	if (showbuffer(bp, wp, WFHARD) != TRUE) return NULL;
637 	return wp;
638 }
639 
640 /*
641  * Insert another buffer at dot.  Very useful.
642  */
643 /*ARGSUSED*/
644 bufferinsert(f, n)
645 {
646 	register BUFFER *bp;
647 	register LINE	*clp;
648 	register int	clo;
649 	register int	nline;
650 	int		s;
651 	char		bufn[NBUFN];
652 
653 #ifdef	READONLY	/* 91.01.05  by S.Yoshida */
654 	if (curbp->b_flag & BFRONLY) {	/* If this buffer is read-only, */
655 		warnreadonly();		/* do only displaying warning.	*/
656 		return TRUE;
657 	}
658 #endif	/* READONLY */
659 
660 	/* Get buffer to use from user */
661 	if (curbp->b_altb != NULL)
662 		s=eread("Insert buffer: (default %s) ", bufn, sizeof(bufn),
663 #ifdef	BUGFIX	/* 91.01.04  by S.Yoshida */
664 			 EFNEW|EFBUF, curbp->b_altb->b_bname);
665 	else
666 		s=eread("Insert buffer: ", bufn, sizeof(bufn), EFNEW|EFBUF);
667 #else	/* ORIGINAL */
668 			 EFNEW|EFBUF, &(curbp->b_altb->b_bname),
669 			 (char *) NULL) ;
670 	else
671 		s=eread("Insert buffer: ", bufn, sizeof(bufn), EFNEW|EFBUF,
672 			 (char *) NULL) ;
673 #endif	/* BUGFIX */
674 	if (s == ABORT) return (s);
675 	if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb;
676 	else if ((bp=bfind(bufn, FALSE)) == NULL) return FALSE;
677 
678 	if (bp==curbp) {
679 		ewprintf("Cannot insert buffer into self");
680 		return FALSE;
681 	}
682 
683 	/* insert the buffer */
684 	nline = 0;
685 	clp = lforw(bp->b_linep);
686 	for(;;) {
687 		for (clo = 0; clo < llength(clp); clo++)
688 			if (linsert(1, lgetc(clp, clo)) == FALSE)
689 				return FALSE;
690 		if((clp = lforw(clp)) == bp->b_linep) break;
691 		if (newline(FFRAND, 1) == FALSE) /* fake newline */
692 			return FALSE;
693 		nline++;
694 	}
695 	if (nline == 1) ewprintf("[Inserted 1 line]");
696 	else	ewprintf("[Inserted %d lines]", nline);
697 
698 	clp = curwp->w_linep;			/* cosmetic adjustment */
699 	if (curwp->w_dotp == clp) {		/* for offscreen insert */
700 		while (nline > 0 && lback(clp)!=curbp->b_linep) {
701 			clp = lback(clp);
702 			nline --;
703 		}
704 		curwp->w_linep = clp;		/* adjust framing.	*/
705 		curwp->w_lines = 0;
706 		curwp->w_flag |= WFHARD;
707 	}
708 	return (TRUE);
709 }
710 
711 /*
712  * Turn off the dirty bit on this buffer.
713  */
714 /*ARGSUSED*/
715 notmodified(f, n)
716 {
717 	register WINDOW *wp;
718 
719 #ifdef	AUTOSAVE	/* 96.12.24 by M.Suzuki	*/
720 	curbp->b_flag &= ~(BFCHG|BFACHG);
721 #else
722 	curbp->b_flag &= ~BFCHG;
723 #endif	/* AUTOSAVE	*/
724 	wp = wheadp;				/* Update mode lines.	*/
725 	while (wp != NULL) {
726 		if (wp->w_bufp == curbp)
727 			wp->w_flag |= WFMODE;
728 		wp = wp->w_wndp;
729 	}
730 	ewprintf("Modification-flag cleared");
731 	return TRUE;
732 }
733 
734 #ifdef	READONLY	/* 91.01.05  by S.Yoshida */
735 /*
736  * Toggle the read-only bit on this buffer.
737  */
738 /*ARGSUSED*/
739 togglereadonly(f, n)
740 {
741 	register WINDOW *wp;
742 
743 	curbp->b_flag ^= BFRONLY;		/* Toggle read-only bit. */
744 	wp = wheadp;				/* Update mode lines.	*/
745 	while (wp != NULL) {
746 		if (wp->w_bufp == curbp)
747 			wp->w_flag |= WFMODE;
748 		wp = wp->w_wndp;
749 	}
750 	return TRUE;
751 }
752 
753 /*
754  * Display warning message.
755  */
756 VOID
757 warnreadonly()
758 {
759 	ewprintf("Buffer is read-only: #<buffer %s>", curbp->b_bname);
760 	ttbeep();	/* 91.02.06  Add beep. by S.Yoshida */
761 }
762 #endif	/* READONLY */
763 
764 #ifndef NO_HELP
765 /*
766  * Popbuf and set all windows to top of buffer.	 Currently only used by
767  * help functions.
768  */
769 
770 popbuftop(bp)
771 register BUFFER *bp;
772 {
773     register WINDOW *wp;
774 
775     bp->b_dotp = lforw(bp->b_linep);
776     bp->b_doto = 0;
777     if(bp->b_nwnd != 0) {
778 	for(wp = wheadp; wp!=NULL; wp = wp->w_wndp)
779 	    if(wp->w_bufp == bp) {
780 		wp->w_dotp = bp->b_dotp;
781 		wp->w_doto = 0;
782 		wp->w_flag |= WFHARD;
783 	    }
784     }
785     return popbuf(bp) != NULL;
786 }
787 #endif
788 
789 #if defined(CMODE)||defined(VARIABLE_TAB)||defined(AUTOSAVE)
790 #define		USING_GETNUM	1
791 #endif
792 
793 #ifdef USING_GETNUM
794 getnum(prompt, num)
795 char *prompt;
796 int *num;
797 {
798 	char numstr[GETNUMLEN];
799 
800 	if (ereply("%s : ", numstr, GETNUMLEN, prompt) == FALSE)
801 		return (FALSE);
802 	*num = atoi(numstr);
803 	return (TRUE);
804 }
805 #undef USING_GETNUM
806 #endif  /* USING_GETNUM */
807 
808 #ifdef  VARIABLE_TAB
809 set_default_tabwidth(f, n)
810 int f, n;
811 {
812     if ((f & FFARG) == 0)
813 	{
814 	if (getnum("default-tab-width", &n) == FALSE)
815 	    return (FALSE);
816 	}
817     if (n>64 || n<=2)
818     	return (FALSE);
819     defb_tab = n;
820     return (TRUE);
821 }
822 
823 set_tabwidth(f, n)
824 int f, n;
825 {
826     extern int refresh();
827     if ((f & FFARG) == 0)
828 	{
829 	if (getnum("tab-width", &n) == FALSE)
830 	    return (FALSE);
831 	}
832 	if (n == 0)
833 		n = defb_tab;
834     else if (n>64 || n<=2)
835     	return (FALSE);
836     curbp->b_tabwidth = n;
837     if (f >= 0)
838 	    refresh(0, 0);
839     return (TRUE);
840 }
841 
842 set_cmode_tabwidth(f, n)
843 int f, n;
844 {
845     if ((f & FFARG) == 0)
846 	{
847 	if (getnum("c-tab-width", &n) == FALSE)
848 	    return (FALSE);
849 	}
850     if (n>64 || n<=2)
851     	return (FALSE);
852     cmode_tab = n;
853     return (TRUE);
854 }
855 #endif  /* VARIABLE_TAB */
856 
857 #ifdef BUFFER_MODE
858 
859 #define BUFNAME_START_COL 4
860 
861 static int
862 b_makename(lp, buf, len)
863 LINE *lp;
864 char *buf;
865 int len;
866 {
867   if (BUFNAME_START_COL < llength(lp)) {
868     char *p = lp->l_text + BUFNAME_START_COL, *q = buf, *ep = p + NBUFN;
869 
870     while (*ep == ' ') {
871       ep--;
872     }
873     ep++;
874     while (p < ep) {
875       *q++ = *p++;
876     }
877     *q = '\0';
878     return TRUE;
879   }
880   return FALSE;
881 }
882 
883 /*ARGSUSED*/
884 b_thiswin(f, n)
885 int f, n;
886 {
887   char bufname[NBUFN];
888   register BUFFER *bp;
889   LINE *lp = curwp->w_dotp;
890   int s;
891 
892   s = b_makename(lp, bufname, NBUFN);
893   if (s) {
894     bp = bfind(bufname, FALSE);
895     if (bp != NULL) {
896       /* put it in current window */
897 	curbp = bp;
898 	return showbuffer(bp, curwp, WFFORCE|WFHARD);
899       }
900     else {
901       ewprintf("No buffer named \"%s\"", bufname);
902     }
903   }
904   return FALSE;
905 }
906 
907 /*ARGSUSED*/
908 static
909 b_delundel(ch)
910 int ch;
911 {
912   LINE *lp = curwp->w_dotp;
913 
914   if (lback(lp) != curbp->b_linep &&
915       lback(lback(lp)) != curbp->b_linep) {
916     if (llength(lp) > 0) {
917       lputc(lp, 0, ch);
918     }
919     if (lforw(lp) != curbp->b_linep) {
920       curwp->w_dotp = lforw(lp);
921     }
922     curwp->w_flag |= WFEDIT | WFMOVE;
923     curwp->w_doto = 0;
924     return TRUE;
925   }
926   return FALSE;
927 }
928 
929 /*ARGSUSED*/
930 b_del(f, n)
931 int f, n;
932 {
933   return b_delundel((int)'D');
934 }
935 
936 /*ARGSUSED*/
937 b_undel(f, n)
938 int f, n;
939 {
940   return b_delundel((int)' ');
941 }
942 
943 /*ARGSUSED*/
944 b_expunge(f, n)
945 int f, n;
946 {
947   char bufname[NBUFN];
948   register LINE *lp, *nlp;
949   VOID lfree();
950 
951   for (lp = lforw(curbp->b_linep) ; lp != curbp->b_linep ; lp = nlp) {
952     nlp = lforw(lp);
953     if (0 < llength(lp) && lgetc(lp, 0) == 'D') {
954       switch (b_makename(lp, bufname, NBUFN)) {
955       case FALSE:
956 	break;
957 
958       case TRUE:
959 	eargset(bufname);
960 	if (killbuffer(f, n)) {
961 	  lfree(lp);
962 	  curwp->w_flag |= WFHARD;
963 	}
964 	break;
965       }
966     }
967   }
968   return TRUE;
969 }
970 #endif /* BUFFER_MODE */
971