input.c revision 1.7
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#include "myhistedit.h" 59 60#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ 61 62MKINIT 63struct strpush { 64 struct strpush *prev; /* preceding string on stack */ 65 char *prevstring; 66 int prevnleft; 67 struct alias *ap; /* if push was associated with an alias */ 68}; 69 70/* 71 * The parsefile structure pointed to by the global variable parsefile 72 * contains information about the current file being read. 73 */ 74 75MKINIT 76struct parsefile { 77 struct parsefile *prev; /* preceding file on stack */ 78 int linno; /* current line */ 79 int fd; /* file descriptor (or -1 if string) */ 80 int nleft; /* number of chars left in buffer */ 81 char *nextc; /* next char in buffer */ 82 char *buf; /* input buffer */ 83 struct strpush *strpush; /* for pushing strings at this level */ 84 struct strpush basestrpush; /* so pushing one is fast */ 85}; 86 87 88int plinno = 1; /* input line number */ 89MKINIT int parsenleft; /* copy of parsefile->nleft */ 90char *parsenextc; /* copy of parsefile->nextc */ 91MKINIT struct parsefile basepf; /* top level input file */ 92char basebuf[BUFSIZ]; /* buffer for top level input file */ 93struct parsefile *parsefile = &basepf; /* current input file */ 94char *pushedstring; /* copy of parsenextc when text pushed back */ 95int pushednleft; /* copy of parsenleft when text pushed back */ 96int init_editline = 0; /* editline library initialized? */ 97int whichprompt; /* 1 == PS1, 2 == PS2 */ 98 99EditLine *el; /* cookie for editline package */ 100 101#ifdef __STDC__ 102STATIC void pushfile(void); 103#else 104STATIC void pushfile(); 105#endif 106 107 108 109#ifdef mkinit 110INCLUDE "input.h" 111INCLUDE "error.h" 112 113INIT { 114 extern char basebuf[]; 115 116 basepf.nextc = basepf.buf = basebuf; 117} 118 119RESET { 120 if (exception != EXSHELLPROC) 121 parsenleft = 0; /* clear input buffer */ 122 popallfiles(); 123} 124 125SHELLPROC { 126 popallfiles(); 127} 128#endif 129 130 131/* 132 * Read a line from the script. 133 */ 134 135char * 136pfgets(line, len) 137 char *line; 138 { 139 register char *p = line; 140 int nleft = len; 141 int c; 142 143 while (--nleft > 0) { 144 c = pgetc_macro(); 145 if (c == PEOF) { 146 if (p == line) 147 return NULL; 148 break; 149 } 150 *p++ = c; 151 if (c == '\n') 152 break; 153 } 154 *p = '\0'; 155 return line; 156} 157 158 159 160/* 161 * Read a character from the script, returning PEOF on end of file. 162 * Nul characters in the input are silently discarded. 163 */ 164 165int 166pgetc() { 167 return pgetc_macro(); 168} 169 170 171/* 172 * Refill the input buffer and return the next input character: 173 * 174 * 1) If a string was pushed back on the input, pop it; 175 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading 176 * from a string so we can't refill the buffer, return EOF. 177 * 3) Call read to read in the characters. 178 * 4) Delete all nul characters from the buffer. 179 */ 180 181int 182preadbuffer() { 183 register char *p, *q; 184 register int i; 185 register int something; 186#ifndef NO_HISTORY 187 extern EditLine *el; 188#endif 189 190 if (parsefile->strpush) { 191 popstring(); 192 if (--parsenleft >= 0) 193 return (*parsenextc++); 194 } 195 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) 196 return PEOF; 197 flushout(&output); 198 flushout(&errout); 199retry: 200#ifndef NO_HISTORY 201 p = parsenextc = parsefile->buf; 202 if (parsefile->fd == 0 && el) { 203 const char *rl_cp; 204 int len; 205 206 rl_cp = el_gets(el, &len); 207 if (rl_cp == NULL) { 208 i = 0; 209 goto eof; 210 } 211 strcpy(p, rl_cp); /* XXX - BUFSIZE should redesign so not necessary */ 212 i = len; 213 214 } else { 215#endif 216regular_read: 217 i = read(parsefile->fd, p, BUFSIZ - 1); 218#ifndef NO_HISTORY 219 } 220#endif 221eof: 222 if (i <= 0) { 223 if (i < 0) { 224 if (errno == EINTR) 225 goto retry; 226 if (parsefile->fd == 0 && errno == EWOULDBLOCK) { 227 int flags = fcntl(0, F_GETFL, 0); 228 if (flags >= 0 && flags & O_NONBLOCK) { 229 flags &=~ O_NONBLOCK; 230 if (fcntl(0, F_SETFL, flags) >= 0) { 231 out2str("sh: turning off NDELAY mode\n"); 232 goto retry; 233 } 234 } 235 } 236 } 237 parsenleft = EOF_NLEFT; 238 return PEOF; 239 } 240 parsenleft = i - 1; /* we're returning one char in this call */ 241 242 /* delete nul characters */ 243 something = 0; 244 for (;;) { 245 if (*p == '\0') 246 break; 247 if (*p != ' ' && *p != '\t' && *p != '\n') 248 something = 1; 249 p++; 250 if (--i <= 0) { 251 *p = '\0'; 252 goto done; /* no nul characters */ 253 } 254 } 255 /* 256 * remove nuls 257 */ 258 q = p++; 259 while (--i > 0) { 260 if (*p != '\0') 261 *q++ = *p; 262 p++; 263 } 264 *q = '\0'; 265 if (q == parsefile->buf) 266 goto retry; /* buffer contained nothing but nuls */ 267 parsenleft = q - parsefile->buf - 1; 268 269done: 270#ifndef NO_HISTORY 271 if (parsefile->fd == 0 && hist && something) { 272 INTOFF; 273 history(hist, whichprompt == 1 ? H_ENTER : H_ADD, 274 parsefile->buf); 275 INTON; 276 } 277#endif 278 if (vflag) { 279 /* 280 * This isn't right. Most shells coordinate it with 281 * reading a line at a time. I honestly don't know if its 282 * worth it. 283 */ 284 i = parsenleft + 1; 285 p = parsefile->buf; 286 for (; i--; p++) 287 out2c(*p) 288 flushout(out2); 289 } 290 return *parsenextc++; 291} 292 293/* 294 * Undo the last call to pgetc. Only one character may be pushed back. 295 * PEOF may be pushed back. 296 */ 297 298void 299pungetc() { 300 parsenleft++; 301 parsenextc--; 302} 303 304/* 305 * Push a string back onto the input at this current parsefile level. 306 * We handle aliases this way. 307 */ 308void 309pushstring(s, len, ap) 310 char *s; 311 int len; 312 void *ap; 313 { 314 struct strpush *sp; 315 316 INTOFF; 317/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ 318 if (parsefile->strpush) { 319 sp = ckmalloc(sizeof (struct strpush)); 320 sp->prev = parsefile->strpush; 321 parsefile->strpush = sp; 322 } else 323 sp = parsefile->strpush = &(parsefile->basestrpush); 324 sp->prevstring = parsenextc; 325 sp->prevnleft = parsenleft; 326 sp->ap = (struct alias *)ap; 327 if (ap) 328 ((struct alias *)ap)->flag |= ALIASINUSE; 329 parsenextc = s; 330 parsenleft = len; 331 INTON; 332} 333 334popstring() 335{ 336 struct strpush *sp = parsefile->strpush; 337 338 INTOFF; 339 parsenextc = sp->prevstring; 340 parsenleft = sp->prevnleft; 341/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ 342 if (sp->ap) 343 sp->ap->flag &= ~ALIASINUSE; 344 parsefile->strpush = sp->prev; 345 if (sp != &(parsefile->basestrpush)) 346 ckfree(sp); 347 INTON; 348} 349 350/* 351 * Set the input to take input from a file. If push is set, push the 352 * old input onto the stack first. 353 */ 354 355void 356setinputfile(fname, push) 357 char *fname; 358 { 359 int fd; 360 int fd2; 361 362 INTOFF; 363 if ((fd = open(fname, O_RDONLY)) < 0) 364 error("Can't open %s", fname); 365 if (fd < 10) { 366 fd2 = copyfd(fd, 10); 367 close(fd); 368 if (fd2 < 0) 369 error("Out of file descriptors"); 370 fd = fd2; 371 } 372 setinputfd(fd, push); 373 INTON; 374} 375 376 377/* 378 * Like setinputfile, but takes an open file descriptor. Call this with 379 * interrupts off. 380 */ 381 382void 383setinputfd(fd, push) { 384 if (push) { 385 pushfile(); 386 parsefile->buf = ckmalloc(BUFSIZ); 387 } 388 if (parsefile->fd > 0) 389 close(parsefile->fd); 390 parsefile->fd = fd; 391 if (parsefile->buf == NULL) 392 parsefile->buf = ckmalloc(BUFSIZ); 393 parsenleft = 0; 394 plinno = 1; 395} 396 397 398/* 399 * Like setinputfile, but takes input from a string. 400 */ 401 402void 403setinputstring(string, push) 404 char *string; 405 { 406 INTOFF; 407 if (push) 408 pushfile(); 409 parsenextc = string; 410 parsenleft = strlen(string); 411 parsefile->buf = NULL; 412 plinno = 1; 413 INTON; 414} 415 416 417 418/* 419 * To handle the "." command, a stack of input files is used. Pushfile 420 * adds a new entry to the stack and popfile restores the previous level. 421 */ 422 423STATIC void 424pushfile() { 425 struct parsefile *pf; 426 427 parsefile->nleft = parsenleft; 428 parsefile->nextc = parsenextc; 429 parsefile->linno = plinno; 430 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); 431 pf->prev = parsefile; 432 pf->fd = -1; 433 pf->strpush = NULL; 434 pf->basestrpush.prev = NULL; 435 parsefile = pf; 436} 437 438 439void 440popfile() { 441 struct parsefile *pf = parsefile; 442 443 INTOFF; 444 if (pf->fd >= 0) 445 close(pf->fd); 446 if (pf->buf) 447 ckfree(pf->buf); 448 while (pf->strpush) 449 popstring(); 450 parsefile = pf->prev; 451 ckfree(pf); 452 parsenleft = parsefile->nleft; 453 parsenextc = parsefile->nextc; 454 plinno = parsefile->linno; 455 INTON; 456} 457 458 459/* 460 * Return to top level. 461 */ 462 463void 464popallfiles() { 465 while (parsefile != &basepf) 466 popfile(); 467} 468 469 470 471/* 472 * Close the file(s) that the shell is reading commands from. Called 473 * after a fork is done. 474 */ 475 476void 477closescript() { 478 popallfiles(); 479 if (parsefile->fd > 0) { 480 close(parsefile->fd); 481 parsefile->fd = 0; 482 } 483} 484