1 /************************************************************************
2  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
3  * provided to you without charge, and with no warranty.  You may give  *
4  * away copies of JOVE, including sources, provided that this notice is *
5  * included in all the files.                                           *
6  ************************************************************************/
7 
8 /* Contains commands that deal with creating, selecting, killing and
9    listing buffers, and buffer modes, and find-file, etc. */
10 
11 #include "jove.h"
12 #include "jctype.h"
13 #include "disp.h"
14 #include "ask.h"
15 #include "extend.h"
16 #include "fmt.h"
17 #include "insert.h"
18 #include "macros.h"	/* only for mac_getc(), used by do_find fudge */
19 #include "marks.h"
20 #include "move.h"
21 #include "sysprocs.h"
22 #include "proc.h"
23 #include "wind.h"
24 
25 #ifdef IPROCS
26 # include "fp.h"
27 # include "iproc.h"
28 #endif
29 
30 #ifdef MAC
31 # include "mac.h"
32 #else
33 # include <sys/stat.h>
34 #endif
35 
36 #ifdef AUTO_BUFS
37 char
38 	*iobuff,
39 	*genbuf,
40 	*linebuf;
41 #else
42 char
43 	iobuff[LBSIZE],
44 	genbuf[LBSIZE],
45 	linebuf[LBSIZE];
46 #endif
47 
48 private void
49 	setbname proto((Buffer *, char *));
50 
51 private char	*Mainbuf = "Main",
52 	*NoName = "Sans un nom!";
53 
54 Buffer
55 	*world = NULL,		/* First in the list */
56 	*curbuf = NULL,		/* pointer into world for current buffer */
57 	*lastbuf = NULL,	/* Last buffer we were in so we have a default
58 				   buffer during a select buffer. */
59 	*perr_buf = NULL;	/* Buffer with error messages */
60 
61 /* Toggle BIT in the current buffer's minor mode flags.  If argument is
62    supplied, a positive one always turns on the mode and zero argument
63    always turns it off. */
64 
65 void
TogMinor(bit)66 TogMinor(bit)
67 int	bit;
68 {
69 	if (is_an_arg()) {
70 		if (arg_value() == 0)
71 			curbuf->b_minor &= ~bit;
72 		else
73 			curbuf->b_minor |= bit;
74 	} else
75 		curbuf->b_minor ^= bit;
76 	UpdModLine = YES;
77 }
78 
79 /* Creates a new buffer, links it at the end of the buffer chain, and
80    returns it. */
81 
82 private Buffer	*free_bufs = NULL;
83 
84 private Buffer *
buf_alloc()85 buf_alloc()
86 {
87 	register Buffer	*b,
88 			*lastbp;
89 
90 	lastbp = NULL;
91 	for (b = world; b != NULL; b = b->b_next)
92 		lastbp = b;
93 
94 	if (free_bufs != NULL) {
95 		b = free_bufs;
96 		free_bufs = b->b_next;
97 	} else {
98 		b = (Buffer *) emalloc(sizeof (Buffer));
99 	}
100 	if (lastbp)
101 		lastbp->b_next = b;
102 	else
103 		world = b;
104 	b->b_first = NULL;
105 	b->b_next = NULL;
106 #ifdef MAC
107 	b->Type = BUFFER;	/* kludge, but simplifies menu handlers */
108 	b->Name = NULL;
109 #endif
110 	return b;
111 }
112 
113 /* Make a buffer and initialize it. */
114 
115 private Buffer *
mak_buf()116 mak_buf()
117 {
118 	register Buffer	*newb;
119 	register int	i;
120 
121 	newb = buf_alloc();
122 	newb->b_fname = NULL;
123 	newb->b_name = NoName;
124 	newb->b_marks = NULL;
125 	newb->b_themark = 0;		/* Index into markring */
126 	/* No marks yet */
127 	for (i = 0; i < NMARKS; i++)
128 		newb->b_markring[i] = NULL;
129 	newb->b_modified = newb->b_diverged = NO;
130 	newb->b_type = B_FILE;  /* File until proven SCRATCH */
131 	newb->b_minor = 0;
132 	newb->b_major = TEXTMODE;
133 	newb->b_first = NULL;
134 	newb->b_map = NULL;
135 #ifdef IPROCS
136 	newb->b_process = NULL;
137 #endif
138 	buf_clear(newb);
139 #ifdef MAC
140 	Bufchange = YES;
141 #endif
142 	return newb;
143 }
144 
145 void
ReNamBuf()146 ReNamBuf()
147 {
148 	setbname(curbuf, ask_buf((Buffer *)NULL, ALLOW_NEW));
149 }
150 
151 void
FindFile()152 FindFile()
153 {
154 	char	fnamebuf[FILESIZE];
155 
156 	(void) ask_file((char *)NULL, curbuf->b_fname, fnamebuf);
157 	SetABuf(curbuf);
158 	SetBuf(do_find(curwind, fnamebuf, YES, NO));
159 	if (curbuf->b_diverged)
160 		rbell();	/* slight hint of divergence */
161 }
162 
163 private void
mkbuflist(bnamp,ebnamp)164 mkbuflist(bnamp, ebnamp)
165 register char	**bnamp;
166 char		**ebnamp;
167 {
168 	register Buffer	*b;
169 
170 	for (b = world; b != NULL; b = b->b_next) {
171 		if (b->b_name != NULL) {
172 			*bnamp++ = b->b_name;
173 			if (bnamp >= ebnamp)
174 				complain("too many buffers to list");
175 		}
176 	}
177 	*bnamp = NULL;
178 }
179 
180 char *
ask_buf(def,flags)181 ask_buf(def, flags)
182 Buffer	*def;
183 int	flags;
184 {
185 	char	*defname = def != NULL? def->b_name : (char *)NULL;
186 	char	*bnames[200];
187 	register char	*bname;
188 	register int	offset;
189 	char	prompt[100];
190 
191 	/* The test for % in the next definition is a kludge to prevent
192 	 * the default buffer name in the prompt string from provoking
193 	 * unintended formatting.  Ugh!  The name will still be the default.
194 	 */
195 	if (defname != NULL && strchr(defname, '%') == NULL)
196 		swritef(prompt, sizeof(prompt), ": %%f (default %s) ", defname);
197 	else
198 		strcpy(prompt, ProcFmt);
199 	mkbuflist(bnames, &bnames[elemsof(bnames)]);
200 	/* Secret bonus: if there is no default, ^R will insert curbuf's name. */
201 	offset = complete(bnames, defname==NULL? curbuf->b_name : defname, prompt,
202 		flags | (defname == NULL? 0 : ALLOW_EMPTY));
203 	if (offset < 0)
204 		bname = *Minibuf == '\0' && defname != NULL? defname : Minibuf;
205 	else
206 		bname = bnames[offset];
207 	return bname;
208 }
209 
210 void
BufSelect()211 BufSelect()
212 {
213 	register char	*bname;
214 
215 	bname = ask_buf(lastbuf, ALLOW_OLD | ALLOW_INDEX | ALLOW_NEW);
216 	SetABuf(curbuf);
217 	SetBuf(do_select(curwind, bname));
218 }
219 
220 private void
BufNSelect(n)221 BufNSelect(n)
222 int	n;
223 {
224 	register Buffer	*b;
225 
226 	for (b = world; b != NULL; b = b->b_next) {
227 		if (b->b_name != NULL) {
228 			n -= 1;
229 			if (n == 0) {
230 				SetABuf(curbuf);
231 				SetBuf(do_select(curwind, b->b_name));
232 				return;
233 			}
234 		}
235 	}
236 	complain("[No such buffer]");
237 }
238 
Buf1Select()239 void Buf1Select() { BufNSelect(1); }
Buf2Select()240 void Buf2Select() { BufNSelect(2); }
Buf3Select()241 void Buf3Select() { BufNSelect(3); }
Buf4Select()242 void Buf4Select() { BufNSelect(4); }
Buf5Select()243 void Buf5Select() { BufNSelect(5); }
Buf6Select()244 void Buf6Select() { BufNSelect(6); }
Buf7Select()245 void Buf7Select() { BufNSelect(7); }
Buf8Select()246 void Buf8Select() { BufNSelect(8); }
Buf9Select()247 void Buf9Select() { BufNSelect(9); }
Buf10Select()248 void Buf10Select() { BufNSelect(10); }
249 
250 private void
delb_wind(b)251 delb_wind(b)
252 register Buffer *b;
253 {
254 	register Window	*w = fwind;
255 	char	*alt = lastbuf != NULL && lastbuf != b? lastbuf->b_name
256 		: b->b_next != NULL? b->b_next->b_name
257 		: Mainbuf;
258 
259 	do {
260 		Window	*next = w->w_next;
261 
262 		if (w->w_bufp == b) {
263 			if (one_windp() || alt != Mainbuf)
264 				(void) do_select(w, alt);
265 			else
266 				del_wind(w);
267 		}
268 		w = next;
269 	} while (w != fwind || w->w_bufp == b);
270 }
271 
272 private Buffer *
getNMbuf()273 getNMbuf()
274 {
275 	register Buffer	*delbuf = buf_exists(ask_buf(curbuf,
276 		ALLOW_OLD | ALLOW_INDEX));
277 
278 	if (delbuf->b_modified)
279 		confirm("%s modified, are you sure? ", delbuf->b_name);
280 	return delbuf;
281 }
282 
283 void
BufErase()284 BufErase()
285 {
286 	register Buffer	*delbuf = getNMbuf();
287 
288 	buf_clear(delbuf);
289 }
290 
291 /* Free a buffer structure.
292  * The actual struct is preserved to reduce the damage
293  * from dangling references to it.  They seem to be pervasive.
294  * We try to reset enough that a dangling reference will be useless.
295  */
296 
297 private void
kill_buf(delbuf)298 kill_buf(delbuf)
299 register Buffer	*delbuf;
300 {
301 #ifdef IPROCS
302 	untieDeadProcess(delbuf);	/* check for lingering processes */
303 #endif
304 
305 	/* Clean up windows associated with this buffer
306 	 * before it becomes invalid.
307 	 */
308 	delb_wind(delbuf);
309 
310 	/* Resetting curbuf must be done after delb_wind since it can
311 	 * have a side-effect of setting curbuf to delbuf.  For the same
312 	 * reason, delbuf must not be unlinked until after delb_wind.
313 	 */
314 	if (curbuf == delbuf)
315 		curbuf = NULL;
316 	if (lastbuf == delbuf)
317 		lastbuf = curbuf;	/* even if NULL */
318 	if (curbuf == NULL)
319 		SetBuf(curwind->w_bufp);
320 
321 	/* unlink the buffer */
322 	if (world == delbuf) {
323 		world = delbuf->b_next;
324 	} else {
325 		register Buffer	*b;
326 
327 		for (b = world; b->b_next != delbuf; b = b->b_next)
328 			;
329 		b->b_next = delbuf->b_next;
330 	}
331 
332 	if (perr_buf == delbuf) {
333 		ErrFree();
334 		perr_buf = NULL;
335 	}
336 
337 	lfreelist(delbuf->b_first);
338 	delbuf->b_first = delbuf->b_dot = delbuf->b_last = NULL;
339 	if (delbuf->b_name != NULL) {
340 		free((UnivPtr) delbuf->b_name);
341 		delbuf->b_name = NULL;
342 	}
343 	if (delbuf->b_fname != NULL) {
344 		free((UnivPtr) delbuf->b_fname);
345 		delbuf->b_fname = NULL;
346 	}
347 	flush_marks(delbuf);
348 	delbuf->b_marks = NULL;
349 	DelObjRef((data_obj *)delbuf->b_map);
350 	delbuf->b_map = NULL;
351 
352 	delbuf->b_next = free_bufs;
353 	free_bufs = delbuf;
354 #ifdef MAC
355 	Bufchange = YES;
356 	delbuf->Name = NULL;	/* ??? this cannot legitimately matter */
357 #endif
358 }
359 
360 /* offer to kill some buffers */
361 
362 void
KillSome()363 KillSome()
364 {
365 	register Buffer	*b,
366 			*next;
367 
368 	for (b = world; b != NULL; b = next) {
369 		next = b->b_next;
370 		if (!yes_or_no_p(IsModified(b)? "Kill %s [modified]? " : "Kill %s? ", b->b_name))
371 			continue;
372 		if (IsModified(b)
373 		&& yes_or_no_p("%s modified; should I save it? ", b->b_name))
374 		{
375 			Buffer	*oldb = curbuf;
376 
377 			SetBuf(b);
378 			SaveFile();
379 			SetBuf(oldb);
380 		}
381 		kill_buf(b);
382 	}
383 }
384 
385 void
BufKill()386 BufKill()
387 {
388 	kill_buf(getNMbuf());
389 }
390 
391 private const char	*const TypeNames[] = {
392 	NULL,
393 	"Scratch",
394 	"File",
395 	"Process",
396 };
397 
398 void
BufList()399 BufList()
400 {
401 	register char	*fmt = "%2s %5s %-8s %-1s%-1s %-*s  %-s";
402 	register Buffer	*b;
403 	int	bcount = 1,		/* To give each buffer a number */
404 		buf_width = 11;
405 	bool
406 		any_modified = NO,
407 		any_ntbf = NO,
408 		any_diverged = NO;
409 
410 	for (b = world; b != NULL; b = b->b_next) {
411 		buf_width = max(buf_width, (int)strlen(b->b_name));
412 		any_modified |= IsModified(b);
413 		any_ntbf |= b->b_ntbf;
414 		any_diverged |= b->b_diverged;
415 	}
416 
417 	TOstart("Buffer list");
418 
419 	if (any_modified)
420 		Typeout("(* means buffer needs saving)");
421 	if (any_ntbf)
422 		Typeout("(+ means file hasn't been read yet)");
423 	if (any_diverged)
424 		Typeout("(# means file has been changed since buffer was read or written)");
425 	if (any_modified | any_ntbf | any_diverged)
426 		Typeout(NullStr);
427 	Typeout(fmt, "NO", "Lines", "Type", NullStr, NullStr, buf_width, "Name", "File");
428 	Typeout(fmt, "--", "-----", "----", NullStr, NullStr, buf_width, "----", "----");
429 	for (b = world; b != NULL; b = b->b_next) {
430 		char
431 			bnostr[10],
432 			linestr[10];
433 
434 		swritef(bnostr, sizeof(bnostr), "%d", bcount++);
435 		if (b->b_ntbf)
436 			strcpy(linestr, "?");
437 		else
438 			swritef(linestr, sizeof(linestr), "%d",
439 				LinesTo(b->b_first, (LinePtr)NULL));
440 		Typeout(fmt, bnostr, linestr, TypeNames[b->b_type],
441 				b->b_diverged ? "#" : NullStr,
442 				IsModified(b) ? "*" :
443 					 b->b_ntbf ? "+" : NullStr,
444 				buf_width,	/* For the * (variable length field) */
445 				b->b_name,
446 				filename(b));
447 
448 		if (TOabort)
449 			break;
450 	}
451 	TOstop();
452 }
453 
454 private void
bufname(b)455 bufname(b)
456 register Buffer	*b;
457 {
458 	char	tmp[100],
459 		*cp;
460 	int	try = 1;
461 
462 	if (b->b_fname == NULL)
463 		complain("[No file name]");
464 	cp = basename(b->b_fname);
465 	strcpy(tmp, cp);
466 	while (buf_exists(tmp)) {
467 		swritef(tmp, sizeof(tmp), "%s.%d", cp, try);
468 		try += 1;
469 	}
470 	setbname(b, tmp);
471 }
472 
473 void
buf_clear(b)474 buf_clear(b)
475 register Buffer	*b;
476 {
477 	lfreelist(b->b_first);
478 	b->b_first = b->b_dot = b->b_last = NULL;
479 	(void) listput(b, b->b_first);
480 
481 	SavLine(b->b_dot, NullStr);
482 	b->b_char = 0;
483 	AllMarkReset(b, b->b_dot);
484 	if (b == curbuf)
485 		getDOT();
486 
487 	diverge(b, NO);
488 	if (b->b_modified)
489 		UpdModLine = YES;
490 	b->b_ntbf = b->b_modified = NO;
491 
492 #ifdef USE_INO
493 	b->b_dev = 0;
494 	b->b_ino = 0;
495 #endif
496 	b->b_mtime = 0;
497 }
498 
499 /* Returns pointer to buffer with name NAME, or if NAME is a string of digits
500    returns the buffer whose number equals those digits.  Otherwise, returns
501    NULL. */
502 
503 Buffer *
buf_exists(name)504 buf_exists(name)
505 register char	*name;
506 {
507 	register Buffer	*bp;
508 
509 	if (name == NULL)
510 		return NULL;
511 
512 	for (bp = world; bp != NULL; bp = bp->b_next)
513 		if (strcmp(bp->b_name, name) == 0)
514 			return bp;
515 
516 	return NULL;
517 }
518 
519 /* Returns buffer pointer with a file name NAME, if one is found.
520  * If target is NULL, all buffers are searched; otherwise, only
521  * target is examined.  Match is by inode (to catch links) or
522  * canonical name.
523  *
524  * Divergence is diligently searched for.  In particular, all
525  * buffers are examined to see if they match the specified file,
526  * in name or inode.  If they do, divergence is checked (i.e. loss
527  * of underlying file, change in inode number, or modification
528  * time different from last visit).
529  *
530  * DS_REUSE can be used to avoid redundant stat calls.  Use it
531  * with care!
532  */
533 
534 bool
535 	was_dir,	/* do_stat found a directory */
536 	was_file;	/* do_stat found a (plain) file */
537 
538 Buffer *
do_stat(name,target,flags)539 do_stat(name, target, flags)
540 register char	*name;
541 Buffer	*target;
542 int	flags;
543 {
544 	register Buffer	*b = NULL;
545 	Buffer	*result = NULL;
546 	char	fnamebuf[FILESIZE];
547 	static struct stat	stbuf;
548 
549 	PathParse(name, fnamebuf);
550 
551 	if ((flags & DS_REUSE) == 0) {
552 		stbuf.st_mode = S_IFREG;	/* default if stat fails */
553 #ifdef USE_INO
554 		stbuf.st_ino = 0;	/* impossible number */
555 		stbuf.st_dev = 0;
556 #endif
557 		stbuf.st_mtime = 0;
558 		(void) stat(fnamebuf, &stbuf);
559 		was_dir = (stbuf.st_mode & S_IFMT) == S_IFDIR;
560 		was_file = stbuf.st_ino != 0 && (stbuf.st_mode & S_IFMT) == S_IFREG;
561 	}
562 	if ((flags & DS_DIR) == 0 && was_dir)
563 		complain("[%s is a directory]", fnamebuf);
564 	if (flags & DS_SET) {
565 		if ((stbuf.st_mode & S_IFMT) == S_IFREG) {
566 #ifdef USE_INO
567 			target->b_dev = stbuf.st_dev;
568 			target->b_ino = stbuf.st_ino;
569 #endif
570 			target->b_mtime = stbuf.st_mtime;
571 		} else {
572 #ifdef USE_INO
573 			target->b_dev = 0;
574 			target->b_ino = 0;
575 #endif
576 			target->b_mtime = 0;
577 		}
578 		diverge(target, NO);
579 	}
580 
581 	for (b = world; b != NULL; b = b->b_next) {
582 #ifdef USE_INO
583 		if(b->b_ino != 0 && b->b_ino == stbuf.st_ino
584 		&& b->b_dev != 0 && b->b_dev == stbuf.st_dev)
585 		{
586 			/* A buffer's inode got a match; check divergence:
587 			 * - name is different & can't stat buffer's fname
588 			 * - name is different & stat yields a different inode
589 			 * - inode now refers to a non-file
590 			 */
591 			struct stat	stbchk;
592 
593 			if ((strcmp(b->b_fname, fnamebuf) != 0
594 			   && (stat(b->b_fname, &stbchk) == -1
595 			      || b->b_ino != stbchk.st_ino || b->b_dev != stbchk.st_dev))
596 			|| (stbuf.st_mode & S_IFMT) != S_IFREG)
597 			{
598 				/* false match: buffer's file has lost its inode */
599 				b->b_ino = 0;
600 				b->b_dev = 0;
601 				diverge(b, YES);
602 				continue;	/* try again */
603 			}
604 			/* true match -- check times */
605 			if (b->b_mtime != stbuf.st_mtime)
606 				diverge(b, YES);
607 			if (target == NULL || target == b)
608 				result = b;
609 		} else if (b->b_fname != NULL && strcmp(b->b_fname, fnamebuf) == 0) {
610 			/* a name (but not inode) match */
611 			if (b->b_ino != stbuf.st_ino
612 			|| (stbuf.st_mode & S_IFMT) != S_IFREG)
613 			{
614 				/* one or the other has an inode */
615 				b->b_ino = 0;
616 				b->b_dev = 0;
617 				diverge(b, YES);
618 			}
619 			if (target == NULL || target == b)
620 				result = b;
621 		}
622 #else
623 		if (b->b_fname != NULL && strcmp(b->b_fname, fnamebuf) == 0) {
624 			/* a name match -- check times */
625 			if (stbuf.st_mtime != b->b_mtime)
626 				diverge(b, YES);
627 			if (target == NULL || target == b)
628 				result = b;
629 		}
630 #endif
631 	}
632 	return result;
633 }
634 
635 private void
setbname(b,name)636 setbname(b, name)
637 register Buffer	*b;
638 register char	*name;
639 {
640 	UpdModLine = YES;	/* Kludge ... but speeds things up considerably */
641 	if (name != NULL) {
642 		if (b->b_name == NoName)
643 			b->b_name = NULL;
644 		b->b_name = freealloc((UnivPtr) b->b_name, strlen(name) + 1);
645 		strcpy(b->b_name, name);
646 	} else {
647 		b->b_name = NULL;
648 	}
649 #ifdef MAC
650 	Bufchange = YES;
651 #endif
652 }
653 
654 void
setfname(b,name)655 setfname(b, name)
656 register Buffer	*b;
657 register char	*name;
658 {
659 	char	wholename[FILESIZE],
660 		oldname[FILESIZE],
661 		*oldptr = NULL;
662 	Buffer	*save = curbuf;
663 
664 	SetBuf(b);
665 	UpdModLine = YES;	/* Kludge ... but speeds things up considerably */
666 	if (b->b_fname != NULL) {
667 		oldptr = strcpy(oldname, b->b_fname);
668 		free((UnivPtr) b->b_fname);
669 		b->b_fname = NULL;
670 	}
671 	if (name != NULL) {
672 		PathParse(name, wholename);
673 		curbuf->b_fname = strcpy(
674 			(char *)emalloc(strlen(wholename) + 1), wholename);
675 	}
676 	DoAutoExec(curbuf->b_fname, oldptr);
677 
678 	/* until they're known, zero these */
679 #ifdef USE_INO
680 	curbuf->b_dev = 0;
681 	curbuf->b_ino = 0;
682 #endif
683 	curbuf->b_mtime = 0;
684 	diverge(curbuf, NO);
685 
686 	SetBuf(save);
687 #ifdef MAC
688 	Bufchange = YES;
689 #endif
690 }
691 
692 /* Find the file `fname' into buf and put in in window `w' */
693 
694 Buffer *
do_find(w,fname,force,do_macros)695 do_find(w, fname, force, do_macros)
696 register Window	*w;
697 register char	*fname;
698 bool	force;
699 bool	do_macros;
700 {
701 	register Buffer *b;
702 	Buffer	*oldb = curbuf;
703 
704 	b = do_stat(fname, (Buffer *)NULL, DS_NONE);
705 	if (b == NULL) {
706 		b = mak_buf();
707 		setfname(b, fname);
708 		bufname(b);
709 		(void) do_stat(b->b_fname, b, DS_SET | DS_REUSE);
710 
711 		b->b_ntbf = force;
712 		SetBuf(b);	/* force => load the file */
713 		if (w)
714 			tiewind(w, b);
715 
716 		/* We now execute all pending macro text, on the
717 		 * unwarranted presumption that it must have come
718 		 * from an auto-execute-macro.  If we didn't do this
719 		 * here, it would get delayed until the current
720 		 * command was finished, which may be too late in the
721 		 * case of UNIX_cmdline, watch_input, find_tag, or
722 		 * ErrParse.
723 		 *
724 		 * There are some SetBuf(b) calls within the dispatch,
725 		 * but they do not cause reading of the file
726 		 * because b->b_ntbf will be false at this point.
727 		 * One consequence will be that the macro will be executed
728 		 * on the unfilled buffer -- somewhat surprising!
729 		 *
730 		 * ??? This code is a fudge, and should be replaced
731 		 * by a more elegant solution.
732 		 */
733 		if (do_macros) {
734 			ZXchar	c;
735 			int	saved_tcmd = this_cmd;
736 			int	saved_lcmd = last_cmd;
737 			int	saved_as, saved_ac;
738 
739 			save_arg(saved_as, saved_ac);
740 			last_cmd = this_cmd = OTHER_CMD;
741 			for (;;) {
742 				cmd_sync();
743 				if ((c = peekchar) != EOF) {
744 					/* double ugh! */
745 					peekchar = EOF;
746 				} else if ((c = mac_getc()) != EOF) {
747 					add_stroke(c);
748 				} else {
749 					break;
750 				}
751 				dispatch(LastKeyStruck=c);
752 			}
753 			last_cmd = saved_lcmd;
754 			this_cmd = saved_tcmd;
755 			restore_arg(saved_as, saved_ac);
756 		}
757 		/* ??? At this point, we make the rash assumption
758 		 * that buffer oldb still exists, even though
759 		 * the macro text could have deleted it.
760 		 */
761 		b->b_ntbf = !force;
762 		/*
763 		 * the file will be read when the user next mentions Buffer b
764 		 */
765 		SetBuf(oldb);
766 	} else {
767 		if (force) {
768 			SetBuf(b);	/* b->b_ntbf => load the file  */
769 			SetBuf(oldb);
770 		}
771 		if (w)
772 			tiewind(w, b);
773 	}
774 	return b;
775 }
776 
777 /* set alternate buffer */
778 
779 void
SetABuf(b)780 SetABuf(b)
781 Buffer	*b;
782 {
783 	if (b != NULL)
784 		lastbuf = b;
785 }
786 
787 
788 /* check to see if BP is a valid buffer pointer */
789 bool
valid_bp(bp)790 valid_bp(bp)
791 register Buffer	*bp;
792 {
793 	register Buffer	*b;
794 
795 	for (b = world; b != NULL; b = b->b_next)
796 		if (b == bp)
797 			return YES;
798 	return NO;
799 }
800 
801 void
SetBuf(newbuf)802 SetBuf(newbuf)
803 register Buffer	*newbuf;
804 {
805 	if (newbuf == curbuf || newbuf == NULL)
806 		return;
807 
808 	if (!valid_bp(newbuf))
809 		complain("Internal error: (0x%x) is not a valid buffer pointer!", newbuf);
810 	lsave();
811 	curbuf = newbuf;
812 	getDOT();
813 	/* do the read now ... */
814 	if (curbuf->b_ntbf)
815 		read_file(curbuf->b_fname, NO);
816 #ifdef MAC
817 	Modechange = YES;
818 #endif
819 }
820 
821 Buffer *
do_select(w,name)822 do_select(w, name)
823 register Window	*w;
824 register char	*name;
825 {
826 	register Buffer	*new;
827 
828 	if ((new = buf_exists(name)) == NULL) {
829 		new = mak_buf();
830 		setfname(new, (char *)NULL);
831 		setbname(new, name);
832 	}
833 	if (w != NULL)
834 		tiewind(w, new);
835 	return new;
836 }
837 
838 void
buf_init()839 buf_init()
840 {
841 	SetBuf(do_select(curwind, Mainbuf));
842 }
843 
844 LinePtr
lastline(lp)845 lastline(lp)
846 register LinePtr	lp;
847 {
848 	register LinePtr	next;
849 
850 	while ((next = lp->l_next) != NULL)
851 		lp = next;
852 	return lp;
853 }
854 
855 LinePtr
next_line(line,num)856 next_line(line, num)
857 register LinePtr	line;
858 register int	num;
859 {
860 	if (num < 0)
861 		return prev_line(line, -num);
862 	if (line != NULL)
863 		while (--num >= 0 && line->l_next != NULL)
864 			line = line->l_next;
865 	return line;
866 }
867 
868 LinePtr
prev_line(line,num)869 prev_line(line, num)
870 register LinePtr	line;
871 register int	num;
872 {
873 	if (num < 0)
874 		return next_line(line, -num);
875 	if (line != NULL)
876 		while (--num >= 0 && line->l_prev != NULL)
877 			line = line->l_prev;
878 	return line;
879 }
880