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