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 #include "jove.h"
9 #include "fp.h"
10 #include "jctype.h"
11 #include "chars.h"
12 #include "commands.h"
13 #include "disp.h"
14 #include "re.h"
15 #include "ask.h"
16 #include "extend.h"
17 #include "fmt.h"
18 #include "insert.h"
19 #include "move.h"
20 #include "sysprocs.h"
21 #include "proc.h"
22 /* #include "util.h" */
23 #include "vars.h"
24
25 #ifdef SUBSHELL
26 # include <errno.h>
27 #endif
28
29 #ifdef MAC
30 # include "mac.h"
31 #endif
32
33 #ifdef MSDOS_PROCS
34 # include <process.h>
35 #endif
36
37 private void
38 DefAutoExec proto((data_obj *(*proc) ptrproto((const char *))));
39
40 int InJoverc = 0;
41
42 /* Auto execute code */
43
44 #define NEXECS 20
45
46 private struct AutoExec {
47 char *a_pattern;
48 data_obj *a_cmd;
49 int a_arg_state,
50 a_arg_count;
51 } AutoExecs[NEXECS]; /* must be initialized by system to 0 */
52
53 private int ExecIndex = 0;
54
55 /* Command auto-execute. */
56
57 void
CAutoExec()58 CAutoExec()
59 {
60 DefAutoExec(findcom);
61 }
62
63 /* Macro auto-execute. */
64
65 void
MAutoExec()66 MAutoExec()
67 {
68 DefAutoExec(findmac);
69 }
70
71 private void
72 DefAutoExec(proc)
73 data_obj *(*proc) ptrproto((const char *));
74 {
75 data_obj *d = (*proc)(ProcFmt);
76 char *pattern;
77 register struct AutoExec *p;
78
79 pattern = do_ask("\r\n", NULL_ASK_EXT, (char *) NULL, ": %f %s ", d->Name);
80 for (p = AutoExecs; p != &AutoExecs[ExecIndex]; p++)
81 if (p->a_cmd == d
82 && ((pattern == NULL || p->a_pattern == NULL)?
83 (pattern == p->a_pattern) : (strcmp(pattern, p->a_pattern) == 0))
84 && p->a_arg_state == arg_state
85 && p->a_arg_count == arg_count)
86 return; /* eliminate duplicates */
87 if (ExecIndex >= NEXECS)
88 complain("Too many auto-executes, max %d.", NEXECS);
89 p->a_pattern = copystr(pattern);
90 p->a_cmd = d;
91 p->a_arg_state = arg_state;
92 p->a_arg_count = arg_count;
93 ExecIndex += 1;
94 }
95
96 /* DoAutoExec: NEW and OLD are file names, and if NEW and OLD aren't the
97 same kind of file (i.e., match the same pattern) or OLD is NULL and it
98 matches, OR if the pattern is NULL (none was specified) then, we execute
99 the command associated with that kind of file. */
100
101 void
DoAutoExec(new,old)102 DoAutoExec(new, old)
103 register char *new,
104 *old;
105 {
106 register struct AutoExec *p;
107
108 for (p = AutoExecs; p != &AutoExecs[ExecIndex]; p++) {
109 if (p->a_pattern == NULL
110 || ((new != NULL && LookingAt(p->a_pattern, new, 0))
111 && !(old != NULL && LookingAt(p->a_pattern, old, 0))))
112 {
113 arg_state = p->a_arg_state;
114 arg_count = p->a_arg_count;
115 ExecCmd(p->a_cmd);
116 clr_arg_value();
117 }
118 }
119 }
120
121 ZXchar
addgetc()122 addgetc() /* NOTE: can return EOF */
123 {
124 ZXchar c;
125
126 if (!InJoverc) {
127 Asking = YES;
128 AskingWidth = strlen(mesgbuf);
129 c = getch();
130 Asking = NO;
131 add_mess("%p ", c);
132 } else {
133 c = getch();
134 switch (c) {
135 case '\n':
136 c = EOF; /* this isn't part of the sequence */
137 break;
138 case '\\':
139 case '^':
140 c = DecodePair(c, getch());
141 break;
142 }
143 }
144 return c;
145 }
146
147 void
Extend()148 Extend()
149 {
150 ExecCmd(findcom(": "));
151 }
152
153 /* Read a positive integer from CP. It must be in base BASE, and
154 complains if it isn't. If allints, all the characters
155 in the string must be integers or we return NO (failure); otherwise
156 we stop reading at the first nondigit and return YES (success). */
157
158 bool
chr_to_int(cp,base,allints,result)159 chr_to_int(cp, base, allints, result)
160 register char *cp;
161 int base;
162 bool allints;
163 register int *result;
164 {
165 register char c;
166 int value = 0,
167 sign;
168
169 if ((c = *cp) == '-') {
170 sign = -1;
171 cp += 1;
172 } else
173 sign = 1;
174 while ((c = *cp++) != '\0') {
175 if (!jisdigit(c)) {
176 if (allints)
177 return NO;
178 break;
179 }
180 c = c - '0';
181 if (c >= base)
182 complain("You must specify in base %d.", base);
183 value = value * base + c;
184 }
185 *result = value * sign;
186 return YES;
187 }
188
189 int
ask_int(def,prompt,base)190 ask_int(def, prompt, base)
191 char *def;
192 char *prompt;
193 int base;
194 {
195 char *val = ask(def, prompt);
196 int value;
197
198 if (!chr_to_int(val, base, YES, &value))
199 complain("That's not a number!");
200 return value;
201 }
202
203 void
vpr_aux(vp,buf,size)204 vpr_aux(vp, buf, size)
205 register const struct variable *vp;
206 char *buf;
207 size_t size;
208 {
209 switch (vp->v_flags & V_TYPEMASK) {
210 case V_INT:
211 case V_WHOLEX:
212 case V_WHOLE:
213 case V_NAT:
214 swritef(buf, size, (vp->v_flags & V_FMODE)? "%03o" : "%d",
215 *((int *) vp->v_value));
216 break;
217
218 case V_BOOL:
219 swritef(buf, size, (*((int *) vp->v_value)) ? "on" : "off");
220 break;
221
222 case V_STRING:
223 case V_FILENAME:
224 swritef(buf, size, "%s", vp->v_value);
225 break;
226
227 case V_CHAR:
228 swritef(buf, size, "%p", *((ZXchar *) vp->v_value));
229 break;
230 }
231 }
232
233 void
PrVar()234 PrVar()
235 {
236 struct variable *vp = (struct variable *) findvar(ProcFmt);
237 char prbuf[MAXCOLS];
238
239 vpr_aux(vp, prbuf, sizeof(prbuf));
240 f_mess(": %f %s => %s", vp->Name, prbuf);
241 stickymsg = YES;
242 }
243
244 void
vset_aux(vp,prompt)245 vset_aux(vp, prompt)
246 const struct variable *vp;
247 char *prompt;
248 {
249 switch (vp->v_flags & V_TYPEMASK) {
250 case V_INT:
251 case V_WHOLEX:
252 case V_WHOLE:
253 case V_NAT:
254 {
255 char def[30];
256 static const int lwbt[] = {
257 ~0, /* V_INT -- I hope we are two's complement */
258 -1, /* V_WHOLEX */
259 0, /* V_WHOLE */
260 1, /* V_NAT */
261 };
262 int
263 val,
264 lwb = lwbt[(vp->v_flags & V_TYPEMASK) - V_INT];
265
266 vpr_aux(vp, def, sizeof(def));
267 val = ask_int(def, prompt, (vp->v_flags & V_FMODE)? 8 : 10);
268 if (val < lwb)
269 complain("[%s must not be less than %d]", vp->Name, lwb);
270 *((int *) vp->v_value) = val;
271 break;
272 }
273
274 case V_BOOL:
275 {
276 static char *possible[/*bool*/] = {"off", "on", NULL };
277 bool *valp = (bool *) vp->v_value;
278 int newval = complete(possible, possible[!*valp], prompt,
279 CASEIND | ALLOW_OLD | ALLOW_EMPTY);
280
281 if (newval < 0)
282 newval = !*valp;
283 *valp = newval;
284 #ifdef MAC
285 MarkVar(vp, -1, 0); /* mark the menu item */
286 #endif
287 s_mess("%s%s", prompt, possible[newval]);
288 break;
289 }
290
291 case V_FILENAME:
292 {
293 char fbuf[FILESIZE];
294
295 strcpy((char *) vp->v_value,
296 ask_file(prompt, (char *) vp->v_value, fbuf));
297 break;
298 }
299
300 case V_STRING:
301 {
302 char *str;
303
304 /* Do_ask() so you can set string to "" if you so desire. */
305 str = do_ask("\r\n", NULL_ASK_EXT, (char *) vp->v_value, prompt);
306 if (str == NULL)
307 str = NullStr;
308 if (strlen(str) >= vp->v_size)
309 complain("string too long");
310 strcpy((char *) vp->v_value, str);
311 break;
312 }
313
314 case V_CHAR:
315 s_mess(prompt);
316 *((ZXchar *) vp->v_value) = addgetc();
317 break;
318 }
319
320 if (vp->v_flags & V_MODELINE)
321 UpdModLine = YES;
322 if (vp->v_flags & V_CLRSCREEN)
323 ClAndRedraw();
324 if (vp->v_flags & V_TTY_RESET)
325 tty_adjust();
326 #ifdef UNIX
327 if (vp->v_flags & V_UPDFREQ)
328 SetClockAlarm(YES);
329 #endif
330 #if defined(USE_CTYPE) && !defined(NO_SETLOCALE)
331 if (vp->v_flags & V_LOCALE) {
332 locale_adjust();
333 ClAndRedraw();
334 }
335 #endif
336 }
337
338 void
SetVar()339 SetVar()
340 {
341 struct variable *vp = (struct variable *) findvar(ProcFmt);
342 char prompt[128];
343
344 swritef(prompt, sizeof(prompt), ": %f %s ", vp->Name);
345 vset_aux(vp, prompt);
346 }
347
348 /* complete: buffer/command/keymap/macro/variable name completion.
349 *
350 * possible: an array of strings
351 * prompt: the prompt to use
352 * flags: a set of flags:
353 * CASEIND: ignore case
354 * ALLOW_OLD: allow answer listed in possible
355 * ALLOW_NEW: allow answer not listed in possible
356 * ALLOW_EMPTY: allow empty answer
357 *
358 * complete returns an index into possible, or -1 if there is no
359 * right answer there, in which case what the user typed is in Minibuf.
360 *
361 * aux_complete is called by real_ask, on behalf of complete, to handle:
362 *
363 * ? Typeout possible completions (does not change the answer)
364 *
365 * TAB Extend answer as much as possible (this might actually
366 * involve shrinking the answer until it is the prefix of
367 * at least one Possible; if so, error).
368 *
369 * SP If answer is complete (i.e. matches a whole entry in Possible),
370 * accept it. Otherwise, extend answer as much as possible;
371 * if the result is unique, and we didn't shrink the answer,
372 * accept it.
373 *
374 * CR or NL If ALLOW_NEW and answer non-empty: accept the answer.
375 * If ALLOW_EMPTY and answer empty: accept the answer.
376 * If ALLOW_OLD and answer is complete: accept it.
377 * If ALLOW_OLD and answer is prefix of a unique Possible: accept that Possible.
378 * If ALLOW_INDEX and answer is a numeral that is a legitimate
379 * index into possible, accept as that possible.
380 * Otherwise: error
381 *
382 * If we are InJoverc, we cannot be interactive, so ? is forbidden
383 * and all the others are treated as CR; an error is fatal.
384 *
385 * The following file-static variables smuggle values from complete
386 * to aux_complete and vice versa, behind the back of do_ask etc.
387 * This could be nicer if C supported nested procedures (closures);
388 * perhaps we should simulate them.
389 */
390
391 private char **Possible; /* possible arg of complete */
392 private int
393 comp_flags, /* flags arg of complete */
394 comp_value; /* return value for complete; set by aux_complete */
395
396 private bool aux_complete proto((ZXchar c)); /* needed to comfort dumb MS Visual C */
397
398 private bool
aux_complete(c)399 aux_complete(c)
400 ZXchar c;
401 {
402 if (comp_flags & CASEIND) {
403 char lc;
404 char *lp;
405
406 for (lp = linebuf; (lc = *lp) != '\0'; lp++)
407 *lp = CharDowncase(lc);
408 }
409 if (c == '?') {
410 int i;
411 size_t len = strlen(linebuf);
412
413 if (InJoverc)
414 complain("[invalid `?']");
415 /* kludge: in case we're using UseBuffers, in which case
416 linebuf gets written all over (but restored by TOstop/TOabort) */
417 strcpy(Minibuf, linebuf);
418 TOstart("Completion"); /* for now ... */
419 for (i = 0; Possible[i] != NULL && !TOabort; i++) {
420 if ((comp_flags & ALLOW_INDEX) && len == 0)
421 Typeout("%2d %s", i+1, Possible[i]);
422 else if (strncmp(Possible[i], Minibuf, len) == 0)
423 Typeout("%s", Possible[i]);
424 }
425
426 TOstop();
427 } else {
428 /* let's do some completion! */
429 int
430 i,
431 numeral,
432 len = strlen(linebuf),
433 minmatch = 1000, /* init with dummy to placate picky compilers */
434 maxmatch = 0,
435 numfound = 0,
436 lastmatch = -1; /* init with dummy to placate picky compilers */
437
438 if (InJoverc || c == '\n')
439 c = '\r'; /* NL is synonym for CR; InJoverc, all are treated as CR */
440 for (i = 0; Possible[i] != NULL; i++) {
441 int this_len = *Possible[i] == *linebuf
442 ? numcomp(Possible[i], linebuf) : 0;
443
444 if (maxmatch < this_len)
445 maxmatch = this_len;
446 if (this_len >= len) {
447 if (Possible[i][len] == '\0' && c != '\t') {
448 /* an exact match */
449 if (comp_flags & ALLOW_OLD) {
450 comp_value = i; /* good: done */
451 return NO;
452 } else {
453 if (InJoverc)
454 complain("[%s already exists]");
455 add_mess(" [already exists]");
456 SitFor(7);
457 return YES;
458 }
459 }
460 minmatch = numfound == 0
461 ? strlen(Possible[i])
462 : min(minmatch, numcomp(Possible[lastmatch], Possible[i]));
463 numfound += 1;
464 lastmatch = i;
465 }
466 }
467
468 if (c == '\r' && len > 0
469 && (comp_flags & ALLOW_INDEX)
470 && chr_to_int(linebuf, 10, YES, &numeral)
471 && 0 < numeral && numeral <= i
472 ) {
473 comp_value = numeral - 1; /* accept as an index into possible */
474 return NO;
475 }
476
477 if (c == '\r' && (comp_flags & (len == 0? ALLOW_EMPTY : ALLOW_NEW))) {
478 comp_value = -1; /* accept as new value (perhaps empty) */
479 return NO;
480 }
481 if (numfound == 1 && c != '\t' && (comp_flags & ALLOW_OLD)) {
482 comp_value = lastmatch;
483 return NO;
484 }
485 if (InJoverc)
486 complain("[\"%s\" %s]", linebuf,
487 numfound == 0? "unknown" : "ambiguous");
488 if (numfound == 0) {
489 /* Unknown: either not ALLOW_NEW (bad) or not CR (not good enough) */
490 add_mess(" [unknown]");
491 SitFor(7);
492 if (maxmatch < len && (comp_flags & ALLOW_NEW) == 0) {
493 linebuf[maxmatch] = '\0';
494 Eol();
495 }
496 } else {
497 /* Ambiguous (or unique, but TAB): extend as much as
498 * possible without precluding any of the ambiguous matches.
499 * Explain if we were expected to be done (CR)
500 * or if we made no progress.
501 */
502 null_ncpy(linebuf, Possible[lastmatch], (size_t) minmatch);
503 Eol();
504 if (c == '\r' || minmatch == len) {
505 add_mess(numfound == 1? " [complete]" : " [ambiguous]");
506 SitFor(7);
507 }
508 }
509 }
510 return YES;
511 }
512
513 int
complete(possible,def,prompt,flags)514 complete(possible, def, prompt, flags)
515 register char *possible[];
516 const char *def;
517 const char *prompt;
518 int flags;
519 {
520 /* protect static "Possible" etc. from being overwritten due to recursion */
521 if (InRealAsk)
522 complain((char *) NULL);
523
524 Possible = possible;
525 comp_flags = flags;
526 (void) do_ask("\r\n \t?", aux_complete, def, prompt);
527 return comp_value;
528 }
529
530 void
Source()531 Source()
532 {
533 char
534 fnamebuf[FILESIZE];
535 bool silence = is_an_arg();
536
537 swritef(fnamebuf, sizeof(fnamebuf),
538 #ifdef MSFILESYSTEM
539 "%s/jove.rc",
540 #else
541 "%s/.joverc",
542 #endif
543 HomeDir);
544 (void) ask_file((char *)NULL, fnamebuf, fnamebuf);
545 if (!joverc(fnamebuf) && !silence) {
546 message(IOerr("read", fnamebuf));
547 complain((char *)NULL);
548 }
549 }
550
551 void
BufPos()552 BufPos()
553 {
554 register LinePtr lp = curbuf->b_first;
555 register int
556 i,
557 dotline = 0; /* avoid uninitialized complaint from gcc -W */
558 long dotchar = 0; /* avoid uninitialized complaint from gcc -W */
559 long nchars;
560
561 for (i = nchars = 0; lp != NULL; i++, lp = lp->l_next) {
562 if (lp == curline) {
563 dotchar = nchars + curchar;
564 dotline = i + 1;
565 }
566 nchars += length(lp) + (lp->l_next != NULL); /* include the NL */
567 }
568
569 f_mess("[\"%s\" line %d/%d, char %D/%D (%d%%), cursor = %d/%d]",
570 filename(curbuf), dotline, i, dotchar, nchars,
571 (nchars == 0) ? 100 : (int) (((long) dotchar * 100) / nchars),
572 calc_pos(linebuf, curchar),
573 calc_pos(linebuf, (int)strlen(linebuf)));
574 stickymsg = YES;
575 }
576
577 #ifdef SUBSHELL
578
579 private bool
do_if(cmd)580 do_if(cmd)
581 char *cmd;
582 {
583 char *args[12];
584
585 /* Parse cmd into an argv. Handle various quotes
586 * but not environment variable references.
587 */
588 {
589 char
590 *ip = cmd,
591 *op = cmd,
592 **ap = args;
593
594 for (;;) {
595 while (jiswhite(*ip))
596 ip++;
597 if (*ip == '\0')
598 break;
599 if (ap == &args[elemsof(args)])
600 complain("Too many args for IF shell command");
601 *ap++ = op;
602 for (;;) {
603 char
604 c = *ip++,
605 c2;
606
607 switch (c) {
608 case '\0':
609 ip -= 1;
610 /*FALLTHROUGH*/
611 case ' ':
612 case '\t':
613 break;
614 case '"':
615 case '\'':
616 while ((c2 = *ip++) != c) {
617 switch (c2) {
618 case '\0':
619 complain("Unpaired quote in IF command");
620 /*NOTREACHED*/
621 case '\\':
622 if (c == '"') {
623 c2 = *ip++;
624 if (c2 == '\0')
625 complain("Misplaced \\ in IF command");
626 }
627 /*FALLTHROUGH*/
628 default:
629 *op++ = c2;
630 break;
631 }
632 }
633 continue;
634 case '\\':
635 c = *ip++;
636 if (c == '\0')
637 complain("Misplaced \\ in IF command");
638 /*FALLTHROUGH*/
639 default:
640 *op++ = c;
641 continue;
642 }
643 break;
644 }
645 *op++ = '\0';
646 }
647 *ap = NULL;
648 }
649
650 /* Exec the parsed command */
651 # ifdef UNIX
652 {
653 wait_status_t status;
654
655 switch (ChildPid = fork()) {
656 case -1:
657 complain("[Fork failed for IF: %s]", strerror(errno));
658 /*NOTREACHED*/
659
660 case 0:
661 close(0); /* we want reads to fail */
662 /* close(1); but not writes or ioctl's */
663 /* close(2); */
664 (void) execvp(args[0], args);
665 _exit(errno);
666 /*NOTREACHED*/
667 }
668 dowait(&status);
669 if (!WIFEXITED(status))
670 complain("[no status returned from child in IF test]");
671 if (WIFSIGNALED(status))
672 complain("[IF test terminated by signal %d]", WTERMSIG(status));
673 return WEXITSTATUS(status)==0;
674 }
675 # else
676 # ifdef MSDOS_PROCS
677 {
678 int status;
679
680 if ((status = spawnvp(0, args[0], args)) < 0)
681 complain("[Spawn failed: IF]");
682 return (status == 0); /* 0 means successful */
683 }
684 # else /* !MSDOS_PROCS */
685 I do not know how to do this
686 # endif /* !MSDOS_PROCS */
687 # endif /* !UNIX */
688 }
689 #endif /* SUBSHELL */
690
691 private bool
cmdmatch(inp,verb,oppat)692 cmdmatch(inp, verb, oppat)
693 char *inp;
694 char *verb;
695 char *oppat;
696 {
697 int len = strlen(verb);
698
699 if (caseeqn(inp, verb, (size_t)len) && LookingAt("[ \\t]\\|$", inp, len)) {
700 if (!LookingAt(oppat, inp, len))
701 complain("[malformed %s]", verb);
702 return YES;
703 }
704 return NO;
705 }
706
707 bool
joverc(file)708 joverc(file)
709 char *file;
710 {
711 char buf[LBSIZE],
712 lbuf[LBSIZE];
713
714 jmp_buf savejmp;
715 volatile int lnum = 0;
716 File *volatile fp;
717 volatile bool eof = NO;
718 volatile unsigned int /* bitstrings */
719 finger = 1,
720 skipping = 0,
721 inelse = 0;
722
723 fp = open_file(file, buf, F_READ, NO);
724 if (fp == NULL)
725 return NO;
726
727 /* Catch any errors, here, and do the right thing with them,
728 and then restore the error handle to whoever did a setjmp
729 last. */
730
731 InJoverc += 1;
732 push_env(savejmp);
733 if (setjmp(mainjmp)) {
734 Buffer *savebuf = curbuf;
735
736 SetBuf(do_select((Window *)NULL, "RC errors"));
737 ins_str(sprint("%s:%d:%s", pr_name(file, YES), lnum, lbuf));
738 ins_str(sprint("\t%s\n", mesgbuf));
739 unmodify();
740 SetBuf(savebuf);
741 Asking = NO;
742 }
743 while (!eof) {
744 /* This peculiar delayed EOF testing allows the last line to
745 * end without a NL. We add NL later, so we leave room for it.
746 */
747 eof = f_gets(fp, lbuf, sizeof(lbuf)-1);
748 lnum += 1;
749 Inputp = lbuf;
750 while (jiswhite(*Inputp))
751 Inputp += 1; /* skip white space */
752 if (*Inputp == '#' || *Inputp == '\0') {
753 /* a comment */
754 #ifdef SUBSHELL
755 } else if (cmdmatch(Inputp, "if", "[ \t]*\\(.*\\)$")) {
756 char cmd[128];
757
758 finger <<= 1;
759 if (finger == 0)
760 complain("[`if' nested too deeply]");
761
762 putmatch(1, cmd, sizeof cmd);
763 if (skipping == 0 && !do_if(cmd))
764 skipping |= finger;
765 #endif /* SUBSHELL */
766 #ifndef MAC /* no environment in MacOS */
767 } else if (cmdmatch(Inputp, "ifenv", "\\>[ \t]*\\<\\([^ \t][^ \t]*\\)\\>[ \t]\\(.*\\)$")) {
768 finger <<= 1;
769 if (finger == 0)
770 complain("[`ifenv' nested too deeply]");
771 if (skipping == 0) {
772 char envname[128],
773 envpat[128],
774 *envval;
775
776 putmatch(1, envname, sizeof envname);
777 putmatch(2, envpat, sizeof envpat);
778 envval = getenv(envname);
779 if (envval==NULL || !LookingAt(envpat, envval, 0))
780 skipping |= finger;
781 }
782 #endif
783 } else if (cmdmatch(Inputp, "else", "[ \\t]*$")) {
784 if (finger == 1 || (inelse & finger))
785 complain("[Unexpected `else']");
786 inelse |= finger;
787 skipping ^= finger;
788 } else if (cmdmatch(Inputp, "endif", "[ \\t]*$")) {
789 if (finger == 1)
790 complain("[Unexpected `endif']");
791 inelse &= ~finger;
792 skipping &= ~finger;
793 finger >>= 1;
794 } else if (skipping == 0) {
795 (void) strcat(Inputp, "\n");
796 for (;;) {
797 ZXchar c;
798
799 cmd_sync();
800 this_cmd = OTHER_CMD;
801
802 if ((c = ZXC(*Inputp)) == '-' || jisdigit(c)) {
803 LastKeyStruck = c;
804 Inputp += 1;
805 Digit();
806 } else {
807 Extend();
808 }
809
810 /* get any pending input hiding in the peek buffer */
811
812 if ((c = peekchar) != EOF) {
813 peekchar = EOF;
814 if (Inputp != NULL && ZXRC(Inputp[-1]) == c)
815 Inputp -= 1;
816 else if (!jiswhite(c) && c != '\n' && c != '\0')
817 complain("[junk at end of line]");
818 }
819
820 if (Inputp == NULL)
821 break;
822 while (jiswhite(*Inputp))
823 Inputp += 1; /* skip white space */
824 if (*Inputp == '\0' || *Inputp == '\n')
825 break;
826 if (this_cmd != ARG_CMD)
827 complain("[junk at end of line]");
828 }
829 }
830 }
831
832 #ifdef SUBSHELL
833 if (finger != 1) {
834 finger = 1;
835 complain("[Missing endif]");
836 }
837 #endif
838 f_close(fp);
839 pop_env(savejmp);
840 Inputp = NULL;
841 Asking = NO;
842 InJoverc -= 1;
843 return YES;
844 }
845