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