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