1 /* input.c -- read input from files or strings ($Revision: 1.2 $) */
2 /* stdgetenv is based on the FreeBSD getenv */
3
4 #include "es.h"
5 #include "input.h"
6
7
8 /*
9 * constants
10 */
11
12 #define BUFSIZE ((size_t) 1024) /* buffer size to fill reads into */
13
14
15 /*
16 * macros
17 */
18
19 #define ISEOF(in) ((in)->fill == eoffill)
20
21
22 /*
23 * globals
24 */
25
26 Input *input;
27 char *prompt, *prompt2;
28
29 Boolean disablehistory = FALSE;
30 Boolean resetterminal = FALSE;
31 static char *history;
32 static int historyfd = -1;
33
34 #if READLINE
35 int rl_meta_chars; /* for editline; ignored for gnu readline */
36 extern char *readline(char *);
37 extern void add_history(char *);
38 extern void rl_reset_terminal(char *);
39 extern char *rl_basic_word_break_characters;
40 extern char *rl_completer_quote_characters;
41
42 #if ABUSED_GETENV
43 static char *stdgetenv(const char *);
44 static char *esgetenv(const char *);
45 static char *(*realgetenv)(const char *) = stdgetenv;
46 #endif
47 #endif
48
49
50 /*
51 * errors and warnings
52 */
53
54 /* locate -- identify where an error came from */
locate(Input * in,char * s)55 static char *locate(Input *in, char *s) {
56 return (in->runflags & run_interactive)
57 ? s
58 : str("%s:%d: %s", in->name, in->lineno, s);
59 }
60
61 static char *error = NULL;
62
63 /* yyerror -- yacc error entry point */
yyerror(char * s)64 extern void yyerror(char *s) {
65 #if sgi
66 /* this is so that trip.es works */
67 if (streq(s, "Syntax error"))
68 s = "syntax error";
69 #endif
70 if (error == NULL) /* first error is generally the most informative */
71 error = locate(input, s);
72 }
73
74 /* warn -- print a warning */
warn(char * s)75 static void warn(char *s) {
76 eprint("warning: %s\n", locate(input, s));
77 }
78
79
80 /*
81 * history
82 */
83
84 /* loghistory -- write the last command out to a file */
loghistory(const char * cmd,size_t len)85 static void loghistory(const char *cmd, size_t len) {
86 const char *s, *end;
87 if (history == NULL || disablehistory)
88 return;
89 if (historyfd == -1) {
90 historyfd = eopen(history, oAppend);
91 if (historyfd == -1) {
92 eprint("history(%s): %s\n", history, esstrerror(errno));
93 vardef("history", NULL, NULL);
94 return;
95 }
96 }
97 /* skip empty lines and comments in history */
98 for (s = cmd, end = s + len; s < end; s++)
99 switch (*s) {
100 case '#': case '\n': return;
101 case ' ': case '\t': break;
102 default: goto writeit;
103 }
104 writeit:
105 ;
106 /*
107 * Small unix hack: since read() reads only up to a newline
108 * from a terminal, then presumably this write() will write at
109 * most only one input line at a time.
110 */
111 ewrite(historyfd, cmd, len);
112 }
113
114 /* sethistory -- change the file for the history log */
sethistory(char * file)115 extern void sethistory(char *file) {
116 if (historyfd != -1) {
117 close(historyfd);
118 historyfd = -1;
119 }
120 history = file;
121 }
122
123
124 /*
125 * unget -- character pushback
126 */
127
128 /* ungetfill -- input->fill routine for ungotten characters */
ungetfill(Input * in)129 static int ungetfill(Input *in) {
130 int c;
131 assert(in->ungot > 0);
132 c = in->unget[--in->ungot];
133 if (in->ungot == 0) {
134 assert(in->rfill != NULL);
135 in->fill = in->rfill;
136 in->rfill = NULL;
137 assert(in->rbuf != NULL);
138 in->buf = in->rbuf;
139 in->rbuf = NULL;
140 }
141 return c;
142 }
143
144 /* unget -- push back one character */
unget(Input * in,int c)145 extern void unget(Input *in, int c) {
146 if (in->ungot > 0) {
147 assert(in->ungot < MAXUNGET);
148 in->unget[in->ungot++] = c;
149 } else if (in->bufbegin < in->buf && in->buf[-1] == c && (input->runflags & run_echoinput) == 0)
150 --in->buf;
151 else {
152 assert(in->rfill == NULL);
153 in->rfill = in->fill;
154 in->fill = ungetfill;
155 assert(in->rbuf == NULL);
156 in->rbuf = in->buf;
157 in->buf = in->bufend;
158 assert(in->ungot == 0);
159 in->ungot = 1;
160 in->unget[0] = c;
161 }
162 }
163
164
165 /*
166 * getting characters
167 */
168
169 /* get -- get a character, filter out nulls */
get(Input * in)170 static int get(Input *in) {
171 int c;
172 while ((c = (in->buf < in->bufend ? *in->buf++ : (*in->fill)(in))) == '\0')
173 warn("null character ignored");
174 return c;
175 }
176
177 /* getverbose -- get a character, print it to standard error */
getverbose(Input * in)178 static int getverbose(Input *in) {
179 if (in->fill == ungetfill)
180 return get(in);
181 else {
182 int c = get(in);
183 if (c != EOF) {
184 char buf = c;
185 ewrite(2, &buf, 1);
186 }
187 return c;
188 }
189 }
190
191 /* eoffill -- report eof when called to fill input buffer */
eoffill(Input * in)192 static int eoffill(Input *in) {
193 assert(in->fd == -1);
194 return EOF;
195 }
196
197 #if READLINE
198 /* callreadline -- readline wrapper */
callreadline(char * prompt)199 static char *callreadline(char *prompt) {
200 char *r;
201 if (prompt == NULL)
202 prompt = ""; /* bug fix for readline 2.0 */
203 if (resetterminal) {
204 rl_reset_terminal(NULL);
205 resetterminal = FALSE;
206 }
207 interrupted = FALSE;
208 if (!setjmp(slowlabel)) {
209 slow = TRUE;
210 r = interrupted ? NULL : readline(prompt);
211 } else
212 r = NULL;
213 slow = FALSE;
214 if (r == NULL)
215 errno = EINTR;
216 SIGCHK();
217 return r;
218 }
219
220 /* getenv -- fake version of getenv for readline (or other libraries) */
esgetenv(const char * name)221 static char *esgetenv(const char *name) {
222 List *value = varlookup(name, NULL);
223 if (value == NULL)
224 return NULL;
225 else {
226 char *export;
227 static Dict *envdict;
228 static Boolean initialized = FALSE;
229 Ref(char *, string, NULL);
230
231 gcdisable();
232 if (!initialized) {
233 initialized = TRUE;
234 envdict = mkdict();
235 globalroot(&envdict);
236 }
237
238 string = dictget(envdict, name);
239 if (string != NULL)
240 efree(string);
241
242 export = str("%W", value);
243 string = ealloc(strlen(export) + 1);
244 strcpy(string, export);
245 envdict = dictput(envdict, (char *) name, string);
246
247 gcenable();
248 RefReturn(string);
249 }
250 }
251
252 #if ABUSED_GETENV
253
254 static char *
stdgetenv(name)255 stdgetenv(name)
256 register const char *name;
257 {
258 extern char **environ;
259 register int len;
260 register const char *np;
261 register char **p, *c;
262
263 if (name == NULL || environ == NULL)
264 return (NULL);
265 for (np = name; *np && *np != '='; ++np)
266 continue;
267 len = np - name;
268 for (p = environ; (c = *p) != NULL; ++p)
269 if (strncmp(c, name, len) == 0 && c[len] == '=') {
270 return (c + len + 1);
271 }
272 return (NULL);
273 }
274
275 char *
getenv(char * name)276 getenv(char *name)
277 {
278 return realgetenv(name);
279 }
280
281 extern void
initgetenv(void)282 initgetenv(void)
283 {
284 realgetenv = esgetenv;
285 }
286
287 #endif /* ABUSED_GETENV */
288
289 #endif /* READLINE */
290
291 /* fdfill -- fill input buffer by reading from a file descriptor */
fdfill(Input * in)292 static int fdfill(Input *in) {
293 long nread;
294 assert(in->buf == in->bufend);
295 assert(in->fd >= 0);
296
297 #if READLINE
298 if (in->runflags & run_interactive && in->fd == 0) {
299 char *rlinebuf = callreadline(prompt);
300 if (rlinebuf == NULL)
301
302 nread = 0;
303 else {
304 if (*rlinebuf != '\0')
305 add_history(rlinebuf);
306 nread = strlen(rlinebuf) + 1;
307 if (in->buflen < nread) {
308 while (in->buflen < nread)
309 in->buflen *= 2;
310 in->bufbegin = erealloc(in->bufbegin, in->buflen);
311 }
312 memcpy(in->bufbegin, rlinebuf, nread - 1);
313 in->bufbegin[nread - 1] = '\n';
314 }
315 } else
316 #endif
317 do {
318 nread = eread(in->fd, (char *) in->bufbegin, in->buflen);
319 SIGCHK();
320 } while (nread == -1 && errno == EINTR);
321
322 if (nread <= 0) {
323 close(in->fd);
324 in->fd = -1;
325 in->fill = eoffill;
326 in->runflags &= ~run_interactive;
327 if (nread == -1)
328 fail("$&parse", "%s: %s", in->name == NULL ? "es" : in->name, esstrerror(errno));
329 return EOF;
330 }
331
332 if (in->runflags & run_interactive)
333 loghistory((char *) in->bufbegin, nread);
334
335 in->buf = in->bufbegin;
336 in->bufend = &in->buf[nread];
337 return *in->buf++;
338 }
339
340
341 /*
342 * the input loop
343 */
344
345 /* parse -- call yyparse(), but disable garbage collection and catch errors */
parse(char * pr1,char * pr2)346 extern Tree *parse(char *pr1, char *pr2) {
347 int result;
348 assert(error == NULL);
349
350 inityy();
351 emptyherequeue();
352
353 if (ISEOF(input))
354 throw(mklist(mkstr("eof"), NULL));
355
356 #if READLINE
357 prompt = (pr1 == NULL) ? "" : pr1;
358 #else
359 if (pr1 != NULL)
360 eprint("%s", pr1);
361 #endif
362 prompt2 = pr2;
363
364 gcreserve(300 * sizeof (Tree));
365 gcdisable();
366 result = yyparse();
367 gcenable();
368
369 if (result || error != NULL) {
370 char *e;
371 assert(error != NULL);
372 e = error;
373 error = NULL;
374 fail("$&parse", "%s", e);
375 }
376 #if LISPTREES
377 if (input->runflags & run_lisptrees)
378 eprint("%B\n", parsetree);
379 #endif
380 return parsetree;
381 }
382
383 /* resetparser -- clear parser errors in the signal handler */
resetparser(void)384 extern void resetparser(void) {
385 error = NULL;
386 }
387
388 /* runinput -- run from an input source */
runinput(Input * in,int runflags)389 extern List *runinput(Input *in, int runflags) {
390 volatile int flags = runflags;
391 List * volatile result;
392 List *repl, *dispatch;
393 Push push;
394 const char *dispatcher[] = {
395 "fn-%eval-noprint",
396 "fn-%eval-print",
397 "fn-%noeval-noprint",
398 "fn-%noeval-print",
399 };
400
401 flags &= ~eval_inchild;
402 in->runflags = flags;
403 in->get = (flags & run_echoinput) ? getverbose : get;
404 in->prev = input;
405 input = in;
406
407 ExceptionHandler
408
409 dispatch
410 = varlookup(dispatcher[((flags & run_printcmds) ? 1 : 0)
411 + ((flags & run_noexec) ? 2 : 0)],
412 NULL);
413 if (flags & eval_exitonfalse)
414 dispatch = mklist(mkstr("%exit-on-false"), dispatch);
415 varpush(&push, "fn-%dispatch", dispatch);
416
417 repl = varlookup((flags & run_interactive)
418 ? "fn-%interactive-loop"
419 : "fn-%batch-loop",
420 NULL);
421 result = (repl == NULL)
422 ? prim("batchloop", NULL, NULL, flags)
423 : eval(repl, NULL, flags);
424
425 varpop(&push);
426
427 CatchException (e)
428
429 (*input->cleanup)(input);
430 input = input->prev;
431 throw(e);
432
433 EndExceptionHandler
434
435 input = in->prev;
436 (*in->cleanup)(in);
437 return result;
438 }
439
440
441 /*
442 * pushing new input sources
443 */
444
445 /* fdcleanup -- cleanup after running from a file descriptor */
fdcleanup(Input * in)446 static void fdcleanup(Input *in) {
447 unregisterfd(&in->fd);
448 if (in->fd != -1)
449 close(in->fd);
450 efree(in->bufbegin);
451 }
452
453 /* runfd -- run commands from a file descriptor */
runfd(int fd,const char * name,int flags)454 extern List *runfd(int fd, const char *name, int flags) {
455 Input in;
456 List *result;
457
458 memzero(&in, sizeof (Input));
459 in.lineno = 1;
460 in.fill = fdfill;
461 in.cleanup = fdcleanup;
462 in.fd = fd;
463 registerfd(&in.fd, TRUE);
464 in.buflen = BUFSIZE;
465 in.bufbegin = in.buf = ealloc(in.buflen);
466 in.bufend = in.bufbegin;
467 in.name = (name == NULL) ? str("fd %d", fd) : name;
468
469 RefAdd(in.name);
470 result = runinput(&in, flags);
471 RefRemove(in.name);
472
473 return result;
474 }
475
476 /* stringcleanup -- cleanup after running from a string */
stringcleanup(Input * in)477 static void stringcleanup(Input *in) {
478 efree(in->bufbegin);
479 }
480
481 /* stringfill -- placeholder than turns into EOF right away */
stringfill(Input * in)482 static int stringfill(Input *in) {
483 in->fill = eoffill;
484 return EOF;
485 }
486
487 /* runstring -- run commands from a string */
runstring(const char * str,const char * name,int flags)488 extern List *runstring(const char *str, const char *name, int flags) {
489 Input in;
490 List *result;
491 unsigned char *buf;
492
493 assert(str != NULL);
494
495 memzero(&in, sizeof (Input));
496 in.fd = -1;
497 in.lineno = 1;
498 in.name = (name == NULL) ? str : name;
499 in.fill = stringfill;
500 in.buflen = strlen(str);
501 buf = ealloc(in.buflen + 1);
502 memcpy(buf, str, in.buflen);
503 in.bufbegin = in.buf = buf;
504 in.bufend = in.buf + in.buflen;
505 in.cleanup = stringcleanup;
506
507 RefAdd(in.name);
508 result = runinput(&in, flags);
509 RefRemove(in.name);
510 return result;
511 }
512
513 /* parseinput -- turn an input source into a tree */
parseinput(Input * in)514 extern Tree *parseinput(Input *in) {
515 Tree * volatile result;
516
517 in->prev = input;
518 in->runflags = 0;
519 in->get = get;
520 input = in;
521
522 ExceptionHandler
523 result = parse(NULL, NULL);
524 if (get(in) != EOF)
525 fail("$&parse", "more than one value in term");
526 CatchException (e)
527 (*input->cleanup)(input);
528 input = input->prev;
529 throw(e);
530 EndExceptionHandler
531
532 input = in->prev;
533 (*in->cleanup)(in);
534 return result;
535 }
536
537 /* parsestring -- turn a string into a tree; must be exactly one tree */
parsestring(const char * str)538 extern Tree *parsestring(const char *str) {
539 Input in;
540 Tree *result;
541 unsigned char *buf;
542
543 assert(str != NULL);
544
545 /* TODO: abstract out common code with runstring */
546
547 memzero(&in, sizeof (Input));
548 in.fd = -1;
549 in.lineno = 1;
550 in.name = str;
551 in.fill = stringfill;
552 in.buflen = strlen(str);
553 buf = ealloc(in.buflen + 1);
554 memcpy(buf, str, in.buflen);
555 in.bufbegin = in.buf = buf;
556 in.bufend = in.buf + in.buflen;
557 in.cleanup = stringcleanup;
558
559 RefAdd(in.name);
560 result = parseinput(&in);
561 RefRemove(in.name);
562 return result;
563 }
564
565 /* isinteractive -- is the innermost input source interactive? */
isinteractive(void)566 extern Boolean isinteractive(void) {
567 return input == NULL ? FALSE : ((input->runflags & run_interactive) != 0);
568 }
569
570
571 /*
572 * initialization
573 */
574
575 /* initinput -- called at dawn of time from main() */
initinput(void)576 extern void initinput(void) {
577 input = NULL;
578
579 /* declare the global roots */
580 globalroot(&history); /* history file */
581 globalroot(&error); /* parse errors */
582 globalroot(&prompt); /* main prompt */
583 globalroot(&prompt2); /* secondary prompt */
584
585 /* mark the historyfd as a file descriptor to hold back from forked children */
586 registerfd(&historyfd, TRUE);
587
588 /* call the parser's initialization */
589 initparse();
590
591 #if READLINE
592 rl_meta_chars = 0;
593 rl_basic_word_break_characters=" \t\n\\'`$><=;|&{()}";
594 rl_completer_quote_characters="'";
595 #endif
596 }
597