input.c (7155B)
1 /* input.c: i/o routines for files and pseudo-files (strings) */ 2 3 #include "rc.h" 4 5 #include <errno.h> 6 7 #include "develop.h" 8 #include "edit.h" 9 #include "input.h" 10 #include "jbwrap.h" 11 12 /* How many characters can we unget? */ 13 enum { UNGETSIZE = 2 }; 14 15 typedef enum inputtype { 16 iFd, iString, iEdit 17 } inputtype; 18 19 typedef struct Input { 20 bool saved; 21 inputtype t; 22 int fd, index, read, ungetcount, lineno, last; 23 char *ibuf; 24 void *cookie; 25 int ungetbuf[UNGETSIZE]; 26 int (*gchar)(void); 27 } Input; 28 29 #define BUFSIZE ((size_t) 256) 30 31 static char *inbuf; 32 static size_t istacksize, chars_out, chars_in; 33 static bool save_lineno = TRUE; 34 static Input *istack, *itop; 35 36 int lastchar; 37 38 static char *prompt, *prompt2; 39 40 extern void ugchar(int c) { 41 assert(istack->ungetcount < UNGETSIZE); 42 istack->ungetbuf[istack->ungetcount++] = c; 43 } 44 45 extern int gchar() { 46 int c; 47 48 if (istack->ungetcount) 49 return lastchar = istack->ungetbuf[--istack->ungetcount]; 50 51 while ((c = (*istack->gchar)()) == '\0') 52 pr_error("warning: null character ignored", 0); 53 54 return c; 55 } 56 57 58 /* get the next character from a string. */ 59 60 static int stringgchar() { 61 return lastchar = (inbuf[chars_out] == '\0' ? EOF : inbuf[chars_out++]); 62 } 63 64 65 /* write last command out to a file if interactive && $history is set */ 66 67 static void history() { 68 List *hist; 69 size_t a; 70 71 if (!interactive || (hist = varlookup("history")) == NULL) 72 return; 73 74 for (a = 0; a < chars_in; a++) { 75 char c = inbuf[a]; 76 77 /* skip empty lines and comments */ 78 if (c == '#' || c == '\n') 79 break; 80 81 /* line matches [ \t]*[^#\n] so it's ok to write out */ 82 if (c != ' ' && c != '\t') { 83 char *name = hist->w; 84 int fd = rc_open(name, rAppend); 85 if (fd < 0) 86 uerror(name); 87 else { 88 writeall(fd, inbuf, chars_in); 89 close(fd); 90 } 91 break; 92 } 93 } 94 } 95 96 97 /* read a character from a file descriptor */ 98 99 static int fdgchar() { 100 if (chars_out >= chars_in) { /* replenish empty buffer */ 101 ssize_t r; 102 do { 103 r = rc_read(istack->fd, inbuf, BUFSIZE); 104 sigchk(); 105 if (r == -1) 106 switch (errno) { 107 case EAGAIN: 108 if (!makeblocking(istack->fd)) 109 panic("not O_NONBLOCK"); 110 errno = EINTR; 111 break; 112 case EIO: 113 if (makesamepgrp(istack->fd)) 114 errno = EINTR; 115 else 116 errno = EIO; 117 break; 118 } 119 } while (r < 0 && errno == EINTR); 120 if (r < 0) { 121 uerror("read"); 122 rc_raise(eError); 123 } 124 chars_in = (size_t) r; 125 if (chars_in == 0) 126 return lastchar = EOF; 127 chars_out = 0; 128 if (dashvee) 129 writeall(2, inbuf, chars_in); 130 history(); 131 } 132 133 return lastchar = inbuf[chars_out++]; 134 } 135 136 /* read a character from a line-editing file descriptor */ 137 138 static int editgchar() { 139 if (chars_out >= chars_in) { /* replenish empty buffer */ 140 edit_free(istack->cookie); 141 inbuf = edit_alloc(istack->cookie, &chars_in); 142 if (inbuf == NULL) { 143 chars_in = 0; 144 fprint(2, "exit\n"); 145 return lastchar = EOF; 146 } 147 148 chars_out = 0; 149 if (dashvee) 150 writeall(2, inbuf, chars_in); 151 history(); 152 } 153 154 return lastchar = inbuf[chars_out++]; 155 } 156 157 void termchange(void) { 158 if (istack->t == iEdit) 159 edit_reset(istack->cookie); 160 } 161 162 163 /* set up the input stack, and put a "dead" input at the bottom, so that yyparse will always read eof */ 164 165 extern void initinput() { 166 istack = itop = ealloc(istacksize = 256 * sizeof (Input)); 167 istack->ungetcount = 0; 168 ugchar(EOF); 169 } 170 171 /* push an input source onto the stack. set up a new input buffer, and set gchar() */ 172 173 static void pushcommon() { 174 size_t idiff; 175 istack->index = chars_out; 176 istack->read = chars_in; 177 istack->ibuf = inbuf; 178 istack->lineno = lineno; 179 istack->saved = save_lineno; 180 istack->last = lastchar; 181 istack++; 182 idiff = istack - itop; 183 if (idiff >= istacksize / sizeof (Input)) { 184 itop = erealloc(itop, istacksize *= 2); 185 istack = itop + idiff; 186 } 187 chars_out = 0; 188 chars_in = 0; 189 istack->ungetcount = 0; 190 } 191 192 extern void pushfd(int fd) { 193 pushcommon(); 194 save_lineno = TRUE; 195 istack->fd = fd; 196 lineno = 1; 197 if (editing && interactive && isatty(fd)) { 198 istack->t = iEdit; 199 istack->gchar = editgchar; 200 istack->cookie = edit_begin(fd); 201 } else { 202 istack->t = iFd; 203 istack->gchar = fdgchar; 204 inbuf = ealloc(BUFSIZE); 205 } 206 } 207 208 extern void pushstring(char **a, bool save) { 209 pushcommon(); 210 istack->t = iString; 211 save_lineno = save; 212 inbuf = mprint("%A", a); 213 istack->gchar = stringgchar; 214 if (save_lineno) 215 lineno = 1; 216 else 217 --lineno; 218 } 219 220 221 /* remove an input source from the stack. restore associated variables etc. */ 222 223 extern void popinput() { 224 if (istack->t == iEdit) 225 edit_end(istack->cookie); 226 if (istack->t == iFd || istack->t == iEdit) 227 close(istack->fd); 228 efree(inbuf); 229 --istack; 230 lastchar = istack->last; 231 inbuf = istack->ibuf; 232 chars_out = istack->index; 233 chars_in = istack->read; 234 if (save_lineno) 235 lineno = istack->lineno; 236 else 237 lineno++; 238 save_lineno = istack->saved; 239 } 240 241 242 /* flush input characters up to newline. Used by scanerror() */ 243 244 extern void skiptonl() { 245 int c; 246 if (lastchar == '\n' || lastchar == EOF) 247 return; 248 while ((c = gchar()) != '\n' && c != EOF) 249 ; /* skip to newline */ 250 if (c == EOF) 251 ugchar(c); 252 } 253 254 255 /* the wrapper loop in rc: prompt for commands until EOF, calling yyparse and walk() */ 256 257 extern Node *doit(bool clobberexecit) { 258 bool eof; 259 bool execit; 260 Jbwrap j; 261 Estack e1; 262 Edata jerror; 263 264 if (dashen) 265 clobberexecit = FALSE; 266 execit = clobberexecit; 267 sigsetjmp(j.j, 1); 268 jerror.jb = &j; 269 except(eError, jerror, &e1); 270 for (eof = FALSE; !eof;) { 271 Edata block; 272 Estack e2; 273 274 block.b = newblock(); 275 except(eArena, block, &e2); 276 sigchk(); 277 278 if (interactive) { 279 List *s; 280 if (!dashen && fnlookup("prompt") != NULL) { 281 static bool died = FALSE; 282 static char *arglist[] = { "prompt", NULL }; 283 284 if (!died) { 285 died = TRUE; 286 funcall(arglist); 287 } 288 died = FALSE; 289 } 290 s = varlookup("prompt"); 291 if (s != NULL) { 292 prompt = s->w; 293 if (s->n != NULL) 294 prompt2 = s->n->w; 295 else 296 prompt2 = ""; 297 } else { 298 prompt = prompt2 = ""; 299 } 300 if (istack->t == iFd) 301 fprint(2, "%s", prompt); 302 else if (istack->t == iEdit) 303 edit_prompt(istack->cookie, prompt); 304 } 305 inityy(); 306 if (yyparse() == 1 && execit) 307 rc_raise(eError); 308 eof = (lastchar == EOF); /* "lastchar" can be clobbered during a walk() */ 309 if (parsetree != NULL) { 310 if (RC_DEVELOP) 311 tree_dump(parsetree); 312 if (execit) 313 walk(parsetree, TRUE); 314 else if (dashex && dashen) 315 fprint(2, "%T\n", parsetree); 316 } 317 unexcept(eArena); 318 } 319 popinput(); 320 unexcept(eError); 321 return parsetree; 322 } 323 324 /* parse a function imported from the environment */ 325 326 extern Node *parseline(char *extdef) { 327 bool i = interactive; 328 char *in[2]; 329 Node *fun; 330 in[0] = extdef; 331 in[1] = NULL; 332 interactive = FALSE; 333 pushstring(in, TRUE); 334 fun = doit(FALSE); 335 interactive = i; 336 return fun; 337 } 338 339 /* close file descriptors after a fork() */ 340 341 extern void closefds() { 342 Input *i; 343 for (i = istack; i != itop; --i) /* close open scripts */ 344 if (i->t == iFd && i->fd > 2) { 345 close(i->fd); 346 i->fd = -1; 347 } 348 } 349 350 /* print (or set) prompt(2) */ 351 352 extern void nextline() { 353 lineno++; 354 if (interactive) { 355 if (istack->t == iFd) 356 fprint(2, "%s", prompt2); 357 else if (istack->t == iEdit) 358 edit_prompt(istack->cookie, prompt2); 359 } 360 }