BSD 4_3 development
[unix-history] / usr / contrib / jove / extend.c
CommitLineData
b346cbec
C
1/*************************************************************************
2 * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is *
3 * provided to you without charge for use only on a licensed Unix *
4 * system. You may copy JOVE provided that this notice is included with *
5 * the copy. You may not sell copies of this program or versions *
6 * modified for use on microcomputer systems, unless the copies are *
7 * included with a Unix system distribution and the source is provided. *
8 *************************************************************************/
9
10#include "jove.h"
11#include "io.h"
12#include "termcap.h"
13#include "ctype.h"
14#ifdef JOB_CONTROL
15# include <signal.h>
16#endif
17
18#include <varargs.h>
19
20int InJoverc = 0;
21
22extern int getch(),
23 getchar();
24
25/* Auto execute code */
26
27#define NEXECS 20
28
29private struct {
30 char *a_pattern;
31 data_obj *a_cmd;
32} AutoExecs[NEXECS] = {0};
33
34private int ExecIndex = 0;
35
36/* Command auto-execute. */
37
38CAutoExec()
39{
40 DefAutoExec(findcom);
41}
42
43/* Macro auto-execute. */
44
45MAutoExec()
46{
47 DefAutoExec(findmac);
48}
49
50/* VARARGS0 */
51
52DefAutoExec(proc)
53data_obj *(*proc)();
54{
55 data_obj *d;
56 char *pattern;
57 int i;
58
59 if (ExecIndex >= NEXECS)
60 complain("Too many auto-executes, max %d.", NEXECS);
61 if ((d = (*proc)(ProcFmt)) == 0)
62 return;
63 pattern = ask((char *) 0, ": %f %s ", d->Name);
64 for (i = 0; i < ExecIndex; i++)
65 if ((AutoExecs[i].a_cmd == d) &&
66 (strcmp(pattern, AutoExecs[i].a_pattern) == 0))
67 return; /* Eliminate duplicates. */
68 AutoExecs[ExecIndex].a_pattern = copystr(pattern);
69 AutoExecs[ExecIndex].a_cmd = d;
70 ExecIndex++;
71}
72
73/* DoAutoExec: NEW and OLD are file names, and if NEW and OLD aren't the
74 same kind of file (i.e., match the same pattern) or OLD is 0 and it
75 matches, we execute the command associated with that kind of file. */
76
77DoAutoExec(new, old)
78register char *new,
79 *old;
80{
81 register int i;
82
83 exp_p = 1;
84 exp = 1; /* So minor modes don't toggle. We always want
85 them on. */
86 if (new == 0)
87 return;
88 for (i = 0; i < ExecIndex; i++)
89 if ((LookingAt(AutoExecs[i].a_pattern, new, 0)) &&
90 (old == 0 || !LookingAt(AutoExecs[i].a_pattern, old, 0)))
91 ExecCmd(AutoExecs[i].a_cmd);
92}
93
94BindAKey()
95{
96 BindSomething(findcom);
97}
98
99BindMac()
100{
101 BindSomething(findmac);
102}
103
104extern int EscPrefix(),
105 CtlxPrefix(),
106 MiscPrefix();
107
108data_obj **
109IsPrefix(cp)
110data_obj *cp;
111{
112 int (*proc)();
113
114 if (cp == 0 || (cp->Type & TYPEMASK) != FUNCTION)
115 return 0;
116 proc = ((struct cmd *) cp)->c_proc;
117 if (proc == EscPrefix)
118 return pref1map;
119 if (proc == CtlxPrefix)
120 return pref2map;
121 if (proc == MiscPrefix)
122 return miscmap;
123 return 0;
124}
125
126unbind_aux(c)
127{
128 if (c == CR || c == LF)
129 return FALSE; /* tells do_ask to return */
130 Insert(c);
131 return !FALSE;
132}
133
134UnbindC()
135{
136 char *keys;
137 data_obj **map = mainmap;
138
139 keys = do_ask("\r\n\01\02\03\04\05\06\010\011\013\014\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037", unbind_aux, (char *) 0, ProcFmt);
140 if (keys == 0)
141 return;
142 for (;;) {
143 if (keys[1] == '\0')
144 break;
145 if ((map = IsPrefix(map[*keys])) == 0)
146 break;
147 keys++;
148 }
149 if (keys[1] != 0)
150 complain("That's not a legitimate key sequence.");
151 map[keys[0]] = 0;
152}
153
154addgetc()
155{
156 int c;
157
158 if (!InJoverc)
159 Asking = strlen(mesgbuf);
160 c = getch();
161 if (InJoverc) {
162 if (c == '\n')
163 return EOF; /* this isn't part of the sequence */
164 else if (c == '\\') {
165 if ((c = getch()) == LF)
166 complain("[Premature end of line]");
167 } else if (c == '^') {
168 if ((c = getch()) == '?')
169 c = RUBOUT;
170 else if (isalpha(c) || index("[\\]^_", c))
171 c = c - '@';
172 else
173 complain("[Unknown control character]");
174 }
175 }
176
177 Asking = 0;
178 add_mess("%p ", c);
179
180 return c;
181}
182
183BindWMap(map, lastkey, cmd)
184data_obj **map,
185 *cmd;
186{
187 data_obj **nextmap;
188 int c;
189
190 c = addgetc();
191 if (c == EOF) {
192 if (lastkey == EOF)
193 complain("[Empty key sequence]");
194 complain("[Premature end of key sequence]");
195 } else {
196 if (nextmap = IsPrefix(map[c]))
197 BindWMap(nextmap, c, cmd);
198 else
199 map[c] = cmd;
200 }
201}
202
203/* VARARGS0 */
204
205BindSomething(proc)
206data_obj *(*proc)();
207{
208 data_obj *d;
209
210 if ((d = (*proc)(ProcFmt)) == 0)
211 return;
212 s_mess(": %f %s ", d->Name);
213 BindWMap(mainmap, EOF, d);
214}
215
216/* Describe key */
217
218DescWMap(map, key)
219data_obj **map;
220{
221 data_obj *cp = map[key],
222 **prefp;
223
224 if (cp == 0)
225 add_mess("is unbound.");
226 else if (prefp = IsPrefix(cp))
227 DescWMap(prefp, addgetc());
228 else
229 add_mess("is bound to %s.", cp->Name);
230}
231
232KeyDesc()
233{
234 s_mess(ProcFmt);
235 DescWMap(mainmap, addgetc());
236}
237
238DescCom()
239{
240 data_obj *dp;
241 char pattern[100],
242 doc_type[40],
243 *file = CMD_DB;
244 File *fp;
245
246 if (!strcmp(LastCmd->Name, "describe-variable"))
247 dp = (data_obj *) findvar(ProcFmt);
248 else
249 dp = (data_obj *) findcom(ProcFmt);
250
251 if (dp == 0)
252 return;
253 fp = open_file(file, iobuff, F_READ, COMPLAIN, QUIET);
254 Placur(ILI, 0);
255 flusho();
256 sprintf(pattern, "^:entry \"%s\" \"\\([^\"]*\\)\"", dp->Name);
257 TOstart("Help", TRUE);
258 for (;;) {
259 if (f_gets(fp, genbuf, LBSIZE) == EOF) {
260 Typeout("There is no documentation for \"%s\".", dp->Name);
261 goto outahere;
262 }
263 if ((strncmp(genbuf, ":entry", 6) == 0) && LookingAt(pattern, genbuf, 0))
264 break;
265 }
266 /* found it ... let's print it */
267 putmatch(1, doc_type, sizeof doc_type);
268 if (strcmp("Variable", doc_type) == 0)
269 Typeout(dp->Name);
270 else if (strcmp("Command", doc_type) == 0) {
271 char binding[128];
272
273 find_binds((struct cmd *) dp, binding);
274 if (blnkp(binding))
275 Typeout("To invoke %s, type \"ESC X %s<cr>\".",
276 dp->Name,
277 dp->Name);
278 else
279 Typeout("Type \"%s\" to invoke %s.", binding, dp->Name);
280 }
281 Typeout("");
282 while (f_gets(fp, genbuf, LBSIZE) != EOF)
283 if (strncmp(genbuf, ":entry", 6) == 0)
284 goto outahere;
285 else
286 Typeout("%s", genbuf);
287outahere:
288 f_close(fp);
289 TOstop();
290}
291
292DescBindings()
293{
294 extern int Typeout();
295
296 TOstart("Key Bindings", TRUE);
297 DescMap(mainmap, NullStr);
298 TOstop();
299}
300
301DescMap(map, pref)
302data_obj **map;
303char *pref;
304{
305 int c1,
306 c2 = 0,
307 numbetween;
308 char keydescbuf[40];
309 data_obj **prefp;
310
311 for (c1 = 0; c1 < 0200 && c2 < 0200; c1 = c2 + 1) {
312 c2 = c1;
313 if (map[c1] == 0)
314 continue;
315 while (++c2 < 0200 && map[c1] == map[c2])
316 ;
317 c2--;
318 numbetween = c2 - c1;
319 if (numbetween == 1)
320 sprintf(keydescbuf, "%s {%p,%p}", pref, c1, c2);
321 else if (numbetween == 0)
322 sprintf(keydescbuf, "%s %p", pref, c1);
323 else
324 sprintf(keydescbuf, "%s [%p-%p]", pref, c1, c2);
325 if (prefp = IsPrefix(map[c1]))
326 DescMap(prefp, keydescbuf);
327 else
328 Typeout("%-14s%s", keydescbuf, map[c1]->Name);
329 }
330}
331
332private
333find_binds(cp, buf)
334struct cmd *cp;
335char *buf;
336{
337 char *endp;
338
339 buf[0] = '\0';
340 fb_aux(cp, mainmap, (char *) 0, buf);
341 endp = buf + strlen(buf) - 2;
342 if ((endp > buf) && (strcmp(endp, ", ") == 0))
343 *endp = '\0';
344}
345
346private
347fb_aux(cp, map, prefix, buf)
348register data_obj *cp,
349 **map;
350char *buf,
351 *prefix;
352{
353 int c1,
354 c2;
355 char *bufp = buf + strlen(buf),
356 prefbuf[20];
357 data_obj **prefp;
358
359 for (c1 = c2 = 0; c1 < 0200 && c2 < 0200; c1 = c2 + 1) {
360 c2 = c1;
361 if (map[c1] == cp) {
362 while (++c2 < 0200 && map[c1] == map[c2])
363 ;
364 c2--;
365 if (prefix)
366 sprintf(bufp, "%s ", prefix);
367 bufp += strlen(bufp);
368 switch (c2 - c1) {
369 case 0:
370 sprintf(bufp, "%p, ", c1);
371 break;
372
373 case 1:
374 sprintf(bufp, "{%p,%p}, ", c1, c2);
375 break;
376
377 default:
378 sprintf(bufp, "[%p-%p], ", c1, c2);
379 break;
380 }
381 }
382 if (prefp = IsPrefix(map[c1])) {
383 sprintf(prefbuf, "%p", c1);
384 fb_aux(cp, prefp, prefbuf, bufp);
385 }
386 bufp += strlen(bufp);
387 }
388}
389
390Apropos()
391{
392 register struct cmd *cp;
393 register struct macro *m;
394 register struct variable *v;
395 char *ans;
396 int anyfs = 0,
397 anyvs = 0,
398 anyms = 0;
399 char buf[256];
400
401 ans = ask((char *) 0, ": %f (keyword) ");
402 TOstart("Help", TRUE);
403 for (cp = commands; cp->Name != 0; cp++)
404 if (sindex(ans, cp->Name)) {
405 if (anyfs == 0) {
406 Typeout("Commands");
407 Typeout("--------");
408 }
409 find_binds(cp, buf);
410 if (buf[0])
411 Typeout(": %-30s(%s)", cp->Name, buf);
412 else
413 Typeout(": %s", cp->Name);
414 anyfs++;
415 }
416 if (anyfs)
417 Typeout(NullStr);
418 for (v = variables; v->Name != 0; v++)
419 if (sindex(ans, v->Name)) {
420 if (anyvs == 0) {
421 Typeout("Variables");
422 Typeout("---------");
423 }
424 anyvs++;
425 vpr_aux(v, buf);
426 Typeout(": set %-26s%s", v->Name, buf);
427 }
428 if (anyvs)
429 Typeout(NullStr);
430 for (m = macros; m != 0; m = m->m_nextm)
431 if (sindex(ans, m->Name)) {
432 if (anyms == 0) {
433 Typeout("Macros");
434 Typeout("------");
435 }
436 anyms++;
437 find_binds((data_obj *) m, buf);
438 if (buf[0])
439 Typeout(": %-30s(%s)", m->Name, buf);
440 else
441 Typeout(": %-30s%s", "execute-macro", m->Name);
442 }
443 TOstop();
444}
445
446Extend()
447{
448 data_obj *d;
449
450 if (d = findcom(": "))
451 ExecCmd(d);
452}
453
454/* Read a positive integer from CP. It must be in base BASE, and
455 complains if it isn't. If allints is nonzero, all the characters
456 in the string must be integers or we return -1; otherwise we stop
457 reading at the first nondigit. */
458
459chr_to_int(cp, base, allints)
460register char *cp;
461{
462 register int c;
463 int value = 0;
464
465 while (c = *cp++) {
466 if (!isdigit(c)) {
467 if (allints)
468 return -1;
469 break;
470 }
471 c = c - '0';
472 if (c >= base)
473 complain("You must specify in base %d.", base);
474 value = value * base + c;
475 }
476 return value;
477}
478
479ask_int(prompt, base)
480char *prompt;
481int base;
482{
483 char *val = ask((char *) 0, prompt);
484 int value = chr_to_int(val, base, 1);
485
486 if (value < 0)
487 complain("That's not a number!");
488 return value;
489}
490
491private
492vpr_aux(vp, buf)
493register struct variable *vp;
494char *buf;
495{
496 switch (vp->v_flags & V_TYPEMASK) {
497 case V_BASE10:
498 sprintf(buf, "%d", *(vp->v_value));
499 break;
500
501 case V_BASE8:
502 sprintf(buf, "%o", *(vp->v_value));
503 break;
504
505 case V_BOOL:
506 sprintf(buf, (*(vp->v_value)) ? "on" : "off");
507 break;
508
509 case V_STRING:
510 sprintf(buf, "%s", (char *) vp->v_value);
511 break;
512
513 case V_CHAR:
514 sprintf(buf, "%p", *(vp->v_value));
515 break;
516 }
517}
518
519PrVar()
520{
521 struct variable *vp;
522 char prbuf[256];
523
524 if ((vp = (struct variable *) findvar(ProcFmt)) == 0)
525 return;
526 vpr_aux(vp, prbuf);
527 s_mess(": %f %s => %s", vp->Name, prbuf);
528}
529
530SetVar()
531{
532 struct variable *vp;
533 char *prompt;
534
535 if ((vp = (struct variable *) findvar(ProcFmt)) == 0)
536 return;
537 prompt = sprint(": %f %s ", vp->Name);
538
539 switch (vp->v_flags & V_TYPEMASK) {
540 case V_BASE10:
541 case V_BASE8:
542 {
543 int value;
544
545 value = ask_int(prompt, ((vp->v_flags & V_TYPEMASK) == V_BASE10)
546 ? 10 : 8);
547 *(vp->v_value) = value;
548 break;
549 }
550
551 case V_BOOL:
552 {
553 char *def = *(vp->v_value) ? "off" : "on",
554 *on_off;
555 int value;
556
557 on_off = ask(def, prompt);
558 if (casecmp(on_off, "on") == 0)
559 value = ON;
560 else if (casecmp(on_off, "off") == 0)
561 value = OFF;
562 else
563 complain("Boolean variables must be ON or OFF.");
564 *(vp->v_value) = value;
565 s_mess("%s%s", prompt, value ? "on" : "off");
566 break;
567 }
568
569 case V_STRING:
570 {
571 char *str;
572
573 /* Do_ask() so if you want you can set string to
574 "" if you so desire. */
575 str = do_ask("\r\n", (int (*)()) 0, (char *) vp->v_value, prompt);
576 if (str == 0)
577 str = NullStr;
578 strcpy((char *) vp->v_value, str);
579 /* ... and hope there is enough room. */
580 break;
581 }
582 case V_CHAR:
583 f_mess(prompt);
584 *(vp->v_value) = addgetc();
585 break;
586
587 }
588 if (vp->v_flags & V_MODELINE)
589 UpdModLine++;
590 if (vp->v_flags & V_CLRSCREEN)
591 ClAndRedraw();
592 if (vp->v_flags & V_TTY_RESET)
593 tty_reset();
594}
595
596/* Command completion - possible is an array of strings, prompt is
597 the prompt to use, and flags are ... well read jove.h.
598
599 If flags are RET_STATE, and the user hits <return> what they typed
600 so far is in the Minibuf string. */
601
602private char **Possible;
603private int comp_value,
604 comp_flags;
605
606aux_complete(c)
607{
608 int command,
609 length,
610 i;
611
612 switch (c) {
613 case EOF:
614 comp_value = -1;
615 return 0;
616
617 case '\r':
618 case '\n':
619 command = match(Possible, linebuf);
620 if (command >= 0) {
621 comp_value = command;
622 return 0; /* tells ask to stop */
623 }
624 if (eolp() && bolp()) {
625 comp_value = NULLSTRING;
626 return 0;
627 }
628 if (comp_flags == RET_STATE) switch (command) {
629 case UNIQUE:
630 case ORIGINAL:
631 case NULLSTRING:
632 comp_value = command;
633 return 0;
634
635 default:
636 break;
637 }
638 if (InJoverc)
639 complain("[\"%s\" unknown]", linebuf);
640 rbell();
641 break;
642
643 case '\t':
644 case ' ':
645 {
646 int minmatch = 1000,
647 maxmatch = 0,
648 numfound = 0,
649 lastmatch = -1,
650 length = strlen(linebuf);
651
652 for (i = 0; Possible[i] != 0; i++) {
653 int this_len;
654
655 this_len = numcomp(Possible[i], linebuf);
656 maxmatch = max(maxmatch, this_len);
657 if (this_len >= length) {
658 if (numfound)
659 minmatch = min(minmatch, numcomp(Possible[lastmatch], Possible[i]));
660 else
661 minmatch = strlen(Possible[i]);
662 numfound++;
663 lastmatch = i;
664 if (strcmp(linebuf, Possible[i]) == 0)
665 break;
666 }
667 }
668
669 if (numfound == 0) {
670 rbell();
671 if (InJoverc)
672 complain("[\"%s\" unknown]", linebuf);
673 /* If we're not in the .joverc then
674 let's do something helpful for the
675 user. */
676 if (maxmatch < length) {
677 char *cp;
678
679 cp = linebuf + maxmatch;
680 *cp = 0;
681 Eol();
682 }
683 break;
684 }
685 if (c != '\t' && numfound == 1) {
686 comp_value = lastmatch;
687 return 0;
688 }
689 null_ncpy(linebuf, Possible[lastmatch], minmatch);
690 Eol();
691 if (minmatch == length) /* No difference */
692 rbell();
693 break;
694 }
695
696 case '?':
697 if (InJoverc)
698 complain((char *) 0);
699 /* kludge: in case we're using UseBuffers, in which case
700 linebuf gets written all over */
701 strcpy(Minibuf, linebuf);
702 length = strlen(Minibuf);
703 TOstart("Completion", TRUE); /* for now ... */
704 for (i = 0; Possible[i]; i++)
705 if (numcomp(Possible[i], Minibuf) >= length) {
706 Typeout(Possible[i]);
707 if (TOabort != 0)
708 break;
709 }
710
711 TOstop();
712 break;
713 }
714 return !FALSE;
715}
716
717complete(possible, prompt, flags)
718register char *possible[];
719char *prompt;
720{
721 Possible = possible;
722 comp_flags = flags;
723 (void) do_ask("\r\n \t?", aux_complete, NullStr, prompt);
724 return comp_value;
725}
726
727match(choices, what)
728register char **choices,
729 *what;
730{
731 register int len;
732 int i,
733 found = 0,
734 save,
735 exactmatch = -1;
736
737 len = strlen(what);
738 if (len == 0)
739 return NULLSTRING;
740 for (i = 0; choices[i]; i++) {
741 if (strncmp(what, choices[i], len) == 0) {
742 if (strcmp(what, choices[i]) == 0)
743 exactmatch = i;
744 save = i;
745 found++; /* Found one. */
746 }
747 }
748
749 if (found == 0)
750 save = ORIGINAL;
751 else if (found > 1) {
752 if (exactmatch != -1)
753 save = exactmatch;
754 else
755 save = AMBIGUOUS;
756 }
757
758 return save;
759}
760
761Source()
762{
763 char *com,
764 buf[FILESIZE];
765
766 sprintf(buf, "%s/.joverc", getenv("HOME"));
767 com = ask_file(buf, buf);
768 if (joverc(buf) == NIL)
769 complain(IOerr("read", com));
770}
771
772BufPos()
773{
774 register Line *lp = curbuf->b_first;
775 register int i,
776 dotline;
777
778 for (i = 0; lp != 0; i++, lp = lp->l_next)
779 if (lp == curline)
780 dotline = i + 1;
781
782 s_mess("\"%s\" line %d of %d --%d%%--, column %d of %d.",
783 filename(curbuf),
784 dotline,
785 i,
786 (int) (((long) dotline * 100) / i),
787 1 + calc_pos(linebuf, curchar),
788 1 + calc_pos(linebuf, strlen(linebuf)));
789}
790
791#define IF_UNBOUND -1
792#define IF_TRUE 1
793#define IF_FALSE !IF_TRUE
794
795do_if(cmd)
796char *cmd;
797{
798 int pid,
799 status;
800
801 switch (pid = fork()) {
802 case -1:
803 complain("[Fork failed: if]");
804
805 case 0:
806 {
807 char *args[12],
808 *cp = cmd,
809 **ap = args;
810
811 *ap++ = cmd;
812 for (;;) {
813 if ((cp = index(cp, ' ')) == 0)
814 break;
815 *cp++ = '\0';
816 *ap++ = cp;
817 }
818 *ap = 0;
819
820 close(0); /* we want reads to fail */
821 /* close(1); but not writes or ioctl's
822 close(2); */
823
824 (void) execvp(args[0], args);
825 _exit(-10); /* signals exec error (see below) */
826 }
827 }
828#ifdef IPROCS
829 sighold(SIGCHLD);
830#endif
831 dowait(pid, &status);
832#ifdef IPROCS
833 sigrelse(SIGCHLD);
834#endif
835 if (status == -10)
836 complain("[Exec failed]");
837 if (status < 0)
838 complain("[Exit %d]", status);
839 return (status == 0); /* 0 means successful */
840}
841
842joverc(file)
843char *file;
844{
845 char buf[LBSIZE],
846 lbuf[128];
847 int lnum = 0,
848 eof = FALSE;
849 jmp_buf savejmp;
850 int IfStatus = IF_UNBOUND;
851 File *fp;
852
853 fp = open_file(file, buf, F_READ, !COMPLAIN, QUIET);
854 if (fp == NIL)
855 return NIL;
856
857 /* Catch any errors, here, and do the right thing with them,
858 and then restore the error handle to whoever did a setjmp
859 last. */
860
861 push_env(savejmp);
862 if (setjmp(mainjmp)) {
863 Buffer *savebuf = curbuf;
864
865 SetBuf(do_select((Window *) 0, "RC errors"));
866 ins_str(sprint("%s:%d:%s\t%s\n", pr_name(file), lnum, lbuf, mesgbuf), NO);
867 unmodify();
868 SetBuf(savebuf);
869 Asking = 0;
870 }
871 InJoverc = 1;
872 if (!eof) do {
873 eof = (f_gets(fp, lbuf, sizeof lbuf) == EOF);
874 lnum++;
875 if (casencmp(lbuf, "if", 2) == 0) {
876 char cmd[128];
877
878 if (IfStatus != IF_UNBOUND)
879 complain("[Cannot have nested if's]");
880 if (LookingAt("if[ \t]*\\(.*\\)$", lbuf, 0) == 0)
881 complain("[If syntax error]");
882 putmatch(1, cmd, sizeof cmd);
883 IfStatus = do_if(cmd) ? IF_TRUE : IF_FALSE;
884 continue;
885 } else if (casencmp(lbuf, "else", 4) == 0) {
886 if (IfStatus == IF_UNBOUND)
887 complain("[Unexpected `else']");
888 IfStatus = !IfStatus;
889 continue;
890 } else if (casencmp(lbuf, "endif", 5) == 0) {
891 if (IfStatus == IF_UNBOUND)
892 complain("[Unexpected `endif']");
893 IfStatus = IF_UNBOUND;
894 continue;
895 }
896 if (IfStatus == IF_FALSE)
897 continue;
898 (void) strcat(lbuf, "\n");
899 Inputp = lbuf;
900 while (*Inputp == ' ' || *Inputp == '\t')
901 Inputp++; /* skip white space */
902 Extend();
903 } while (!eof);
904
905 f_close(fp);
906 pop_env(savejmp);
907 Inputp = 0;
908 Asking = 0;
909 InJoverc = 0;
910 if (IfStatus != IF_UNBOUND)
911 complain("[Missing endif]");
912 return !NIL;
913}