1 /* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kenneth Almquist. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; 43 #else 44 __RCSID("$NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $"); 45 #endif 46 #endif /* not lint */ 47 48 #include <stdio.h> /* defines BUFSIZ */ 49 #include <fcntl.h> 50 #include <errno.h> 51 #include <unistd.h> 52 #include <stdlib.h> 53 #include <string.h> 54 55 /* 56 * This file implements the input routines used by the parser. 57 */ 58 59 #include "shell.h" 60 #include "redir.h" 61 #include "syntax.h" 62 #include "input.h" 63 #include "output.h" 64 #include "options.h" 65 #include "memalloc.h" 66 #include "error.h" 67 #include "alias.h" 68 #include "parser.h" 69 #include "myhistedit.h" 70 71 #define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ 72 73 MKINIT 74 struct strpush { 75 struct strpush *prev; /* preceding string on stack */ 76 char *prevstring; 77 int prevnleft; 78 int prevlleft; 79 struct alias *ap; /* if push was associated with an alias */ 80 }; 81 82 /* 83 * The parsefile structure pointed to by the global variable parsefile 84 * contains information about the current file being read. 85 */ 86 87 MKINIT 88 struct parsefile { 89 struct parsefile *prev; /* preceding file on stack */ 90 int linno; /* current line */ 91 int fd; /* file descriptor (or -1 if string) */ 92 int nleft; /* number of chars left in this line */ 93 int lleft; /* number of chars left in this buffer */ 94 char *nextc; /* next char in buffer */ 95 char *buf; /* input buffer */ 96 struct strpush *strpush; /* for pushing strings at this level */ 97 struct strpush basestrpush; /* so pushing one is fast */ 98 }; 99 100 101 int plinno = 1; /* input line number */ 102 int parsenleft; /* copy of parsefile->nleft */ 103 MKINIT int parselleft; /* copy of parsefile->lleft */ 104 char *parsenextc; /* copy of parsefile->nextc */ 105 MKINIT struct parsefile basepf; /* top level input file */ 106 MKINIT char basebuf[BUFSIZ]; /* buffer for top level input file */ 107 struct parsefile *parsefile = &basepf; /* current input file */ 108 int init_editline = 0; /* editline library initialized? */ 109 int whichprompt; /* 1 == PS1, 2 == PS2 */ 110 111 EditLine *el; /* cookie for editline package */ 112 113 STATIC void pushfile(void); 114 static int preadfd(void); 115 116 #ifdef mkinit 117 INCLUDE <stdio.h> 118 INCLUDE "input.h" 119 INCLUDE "error.h" 120 121 INIT { 122 basepf.nextc = basepf.buf = basebuf; 123 } 124 125 RESET { 126 if (exception != EXSHELLPROC) 127 parselleft = parsenleft = 0; /* clear input buffer */ 128 popallfiles(); 129 } 130 131 SHELLPROC { 132 popallfiles(); 133 } 134 #endif 135 136 137 /* 138 * Read a line from the script. 139 */ 140 141 char * 142 pfgets(char *line, int len) 143 { 144 char *p = line; 145 int nleft = len; 146 int c; 147 148 while (--nleft > 0) { 149 c = pgetc_macro(); 150 if (c == PEOF) { 151 if (p == line) 152 return NULL; 153 break; 154 } 155 *p++ = c; 156 if (c == '\n') 157 break; 158 } 159 *p = '\0'; 160 return line; 161 } 162 163 164 165 /* 166 * Read a character from the script, returning PEOF on end of file. 167 * Nul characters in the input are silently discarded. 168 */ 169 170 int 171 pgetc(void) 172 { 173 return pgetc_macro(); 174 } 175 176 177 static int 178 preadfd(void) 179 { 180 int nr; 181 char *buf = parsefile->buf; 182 parsenextc = buf; 183 184 retry: 185 #ifndef SMALL 186 if (parsefile->fd == 0 && el) { 187 static const char *rl_cp; 188 static int el_len; 189 190 if (rl_cp == NULL) 191 rl_cp = el_gets(el, &el_len); 192 if (rl_cp == NULL) 193 nr = 0; 194 else { 195 nr = el_len; 196 if (nr > BUFSIZ - 8) 197 nr = BUFSIZ - 8; 198 memcpy(buf, rl_cp, nr); 199 if (nr != el_len) { 200 el_len -= nr; 201 rl_cp += nr; 202 } else 203 rl_cp = 0; 204 } 205 206 } else 207 #endif 208 nr = read(parsefile->fd, buf, BUFSIZ - 8); 209 210 211 if (nr <= 0) { 212 if (nr < 0) { 213 if (errno == EINTR) 214 goto retry; 215 if (parsefile->fd == 0 && errno == EWOULDBLOCK) { 216 int flags = fcntl(0, F_GETFL, 0); 217 if (flags >= 0 && flags & O_NONBLOCK) { 218 flags &=~ O_NONBLOCK; 219 if (fcntl(0, F_SETFL, flags) >= 0) { 220 out2str("sh: turning off NDELAY mode\n"); 221 goto retry; 222 } 223 } 224 } 225 } 226 nr = -1; 227 } 228 return nr; 229 } 230 231 /* 232 * Refill the input buffer and return the next input character: 233 * 234 * 1) If a string was pushed back on the input, pop it; 235 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading 236 * from a string so we can't refill the buffer, return EOF. 237 * 3) If the is more stuff in this buffer, use it else call read to fill it. 238 * 4) Process input up to the next newline, deleting nul characters. 239 */ 240 241 int 242 preadbuffer(void) 243 { 244 char *p, *q; 245 int more; 246 int something; 247 char savec; 248 249 if (parsefile->strpush) { 250 popstring(); 251 if (--parsenleft >= 0) 252 return (*parsenextc++); 253 } 254 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) 255 return PEOF; 256 flushout(&output); 257 flushout(&errout); 258 259 again: 260 if (parselleft <= 0) { 261 if ((parselleft = preadfd()) == -1) { 262 parselleft = parsenleft = EOF_NLEFT; 263 return PEOF; 264 } 265 } 266 267 q = p = parsenextc; 268 269 /* delete nul characters */ 270 something = 0; 271 for (more = 1; more;) { 272 switch (*p) { 273 case '\0': 274 p++; /* Skip nul */ 275 goto check; 276 277 case '\t': 278 case ' ': 279 break; 280 281 case '\n': 282 parsenleft = q - parsenextc; 283 more = 0; /* Stop processing here */ 284 break; 285 286 default: 287 something = 1; 288 break; 289 } 290 291 *q++ = *p++; 292 check: 293 if (--parselleft <= 0) { 294 parsenleft = q - parsenextc - 1; 295 if (parsenleft < 0) 296 goto again; 297 *q = '\0'; 298 more = 0; 299 } 300 } 301 302 savec = *q; 303 *q = '\0'; 304 305 #ifndef SMALL 306 if (parsefile->fd == 0 && hist && something) { 307 HistEvent he; 308 INTOFF; 309 history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, 310 parsenextc); 311 INTON; 312 } 313 #endif 314 315 if (vflag) { 316 out2str(parsenextc); 317 flushout(out2); 318 } 319 320 *q = savec; 321 322 return *parsenextc++; 323 } 324 325 /* 326 * Undo the last call to pgetc. Only one character may be pushed back. 327 * PEOF may be pushed back. 328 */ 329 330 void 331 pungetc(void) 332 { 333 parsenleft++; 334 parsenextc--; 335 } 336 337 /* 338 * Push a string back onto the input at this current parsefile level. 339 * We handle aliases this way. 340 */ 341 void 342 pushstring(char *s, int len, void *ap) 343 { 344 struct strpush *sp; 345 346 INTOFF; 347 /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ 348 if (parsefile->strpush) { 349 sp = ckmalloc(sizeof (struct strpush)); 350 sp->prev = parsefile->strpush; 351 parsefile->strpush = sp; 352 } else 353 sp = parsefile->strpush = &(parsefile->basestrpush); 354 sp->prevstring = parsenextc; 355 sp->prevnleft = parsenleft; 356 sp->prevlleft = parselleft; 357 sp->ap = (struct alias *)ap; 358 if (ap) 359 ((struct alias *)ap)->flag |= ALIASINUSE; 360 parsenextc = s; 361 parsenleft = len; 362 INTON; 363 } 364 365 void 366 popstring(void) 367 { 368 struct strpush *sp = parsefile->strpush; 369 370 INTOFF; 371 parsenextc = sp->prevstring; 372 parsenleft = sp->prevnleft; 373 parselleft = sp->prevlleft; 374 /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ 375 if (sp->ap) 376 sp->ap->flag &= ~ALIASINUSE; 377 parsefile->strpush = sp->prev; 378 if (sp != &(parsefile->basestrpush)) 379 ckfree(sp); 380 INTON; 381 } 382 383 /* 384 * Set the input to take input from a file. If push is set, push the 385 * old input onto the stack first. 386 */ 387 388 void 389 setinputfile(const char *fname, int push) 390 { 391 int fd; 392 int fd2; 393 394 INTOFF; 395 if ((fd = open(fname, O_RDONLY)) < 0) 396 error("Can't open %s", fname); 397 if (fd < 10) { 398 fd2 = copyfd(fd, 10); 399 close(fd); 400 if (fd2 < 0) 401 error("Out of file descriptors"); 402 fd = fd2; 403 } 404 setinputfd(fd, push); 405 INTON; 406 } 407 408 409 /* 410 * Like setinputfile, but takes an open file descriptor. Call this with 411 * interrupts off. 412 */ 413 414 void 415 setinputfd(int fd, int push) 416 { 417 (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 418 if (push) { 419 pushfile(); 420 parsefile->buf = ckmalloc(BUFSIZ); 421 } 422 if (parsefile->fd > 0) 423 close(parsefile->fd); 424 parsefile->fd = fd; 425 if (parsefile->buf == NULL) 426 parsefile->buf = ckmalloc(BUFSIZ); 427 parselleft = parsenleft = 0; 428 plinno = 1; 429 } 430 431 432 /* 433 * Like setinputfile, but takes input from a string. 434 */ 435 436 void 437 setinputstring(char *string, int push) 438 { 439 INTOFF; 440 if (push) 441 pushfile(); 442 parsenextc = string; 443 parselleft = parsenleft = strlen(string); 444 parsefile->buf = NULL; 445 plinno = 1; 446 INTON; 447 } 448 449 450 451 /* 452 * To handle the "." command, a stack of input files is used. Pushfile 453 * adds a new entry to the stack and popfile restores the previous level. 454 */ 455 456 STATIC void 457 pushfile(void) 458 { 459 struct parsefile *pf; 460 461 parsefile->nleft = parsenleft; 462 parsefile->lleft = parselleft; 463 parsefile->nextc = parsenextc; 464 parsefile->linno = plinno; 465 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); 466 pf->prev = parsefile; 467 pf->fd = -1; 468 pf->strpush = NULL; 469 pf->basestrpush.prev = NULL; 470 parsefile = pf; 471 } 472 473 474 void 475 popfile(void) 476 { 477 struct parsefile *pf = parsefile; 478 479 INTOFF; 480 if (pf->fd >= 0) 481 close(pf->fd); 482 if (pf->buf) 483 ckfree(pf->buf); 484 while (pf->strpush) 485 popstring(); 486 parsefile = pf->prev; 487 ckfree(pf); 488 parsenleft = parsefile->nleft; 489 parselleft = parsefile->lleft; 490 parsenextc = parsefile->nextc; 491 plinno = parsefile->linno; 492 INTON; 493 } 494 495 496 /* 497 * Return to top level. 498 */ 499 500 void 501 popallfiles(void) 502 { 503 while (parsefile != &basepf) 504 popfile(); 505 } 506 507 508 509 /* 510 * Close the file(s) that the shell is reading commands from. Called 511 * after a fork is done. 512 * 513 * Takes one arg, vfork, which tells it to not modify its global vars 514 * as it is still running in the parent. 515 */ 516 517 void 518 closescript(int vforked) 519 { 520 if (vforked) { 521 struct parsefile *pf; 522 523 for (pf=parsefile; pf != &basepf; pf=pf->prev) 524 close(pf->fd); 525 if (parsefile->fd > 0) 526 close(parsefile->fd); 527 return; 528 } 529 popallfiles(); 530 if (parsefile->fd > 0) { 531 close(parsefile->fd); 532 parsefile->fd = 0; 533 } 534 } 535