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