input.c revision 1.15
1/* $NetBSD: input.c,v 1.15 1995/06/07 16:28:03 cgd 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.2 (Berkeley) 5/4/95"; 42#else 43static char rcsid[] = "$NetBSD: input.c,v 1.15 1995/06/07 16:28:03 cgd 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 struct alias *ap; /* if push was associated with an alias */ 78}; 79 80/* 81 * The parsefile structure pointed to by the global variable parsefile 82 * contains information about the current file being read. 83 */ 84 85MKINIT 86struct parsefile { 87 struct parsefile *prev; /* preceding file on stack */ 88 int linno; /* current line */ 89 int fd; /* file descriptor (or -1 if string) */ 90 int nleft; /* number of chars left in buffer */ 91 char *nextc; /* next char in buffer */ 92 char *buf; /* input buffer */ 93 struct strpush *strpush; /* for pushing strings at this level */ 94 struct strpush basestrpush; /* so pushing one is fast */ 95}; 96 97 98int plinno = 1; /* input line number */ 99MKINIT int parsenleft; /* copy of parsefile->nleft */ 100char *parsenextc; /* copy of parsefile->nextc */ 101MKINIT struct parsefile basepf; /* top level input file */ 102char basebuf[BUFSIZ]; /* buffer for top level input file */ 103struct parsefile *parsefile = &basepf; /* current input file */ 104char *pushedstring; /* copy of parsenextc when text pushed back */ 105int pushednleft; /* copy of parsenleft when text pushed back */ 106int init_editline = 0; /* editline library initialized? */ 107int whichprompt; /* 1 == PS1, 2 == PS2 */ 108 109EditLine *el; /* cookie for editline package */ 110 111STATIC void pushfile __P((void)); 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 int len; 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 extern EditLine *el; 192 193 if (parsefile->strpush) { 194 popstring(); 195 if (--parsenleft >= 0) 196 return (*parsenextc++); 197 } 198 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) 199 return PEOF; 200 flushout(&output); 201 flushout(&errout); 202retry: 203 p = parsenextc = parsefile->buf; 204 if (parsefile->fd == 0 && el) { 205 const char *rl_cp; 206 int len; 207 208 rl_cp = el_gets(el, &len); 209 if (rl_cp == NULL) { 210 i = 0; 211 goto eof; 212 } 213 strcpy(p, rl_cp); /* XXX - BUFSIZE should redesign so not necessary */ 214 i = len; 215 216 } else { 217 i = read(parsefile->fd, p, BUFSIZ - 1); 218 } 219eof: 220 if (i <= 0) { 221 if (i < 0) { 222 if (errno == EINTR) 223 goto retry; 224 if (parsefile->fd == 0 && errno == EWOULDBLOCK) { 225 int flags = fcntl(0, F_GETFL, 0); 226 if (flags >= 0 && flags & O_NONBLOCK) { 227 flags &=~ O_NONBLOCK; 228 if (fcntl(0, F_SETFL, flags) >= 0) { 229 out2str("sh: turning off NDELAY mode\n"); 230 goto retry; 231 } 232 } 233 } 234 } 235 parsenleft = EOF_NLEFT; 236 return PEOF; 237 } 238 parsenleft = i - 1; /* we're returning one char in this call */ 239 240 /* delete nul characters */ 241 something = 0; 242 for (;;) { 243 if (*p == '\0') 244 break; 245 if (*p != ' ' && *p != '\t' && *p != '\n') 246 something = 1; 247 p++; 248 if (--i <= 0) { 249 *p = '\0'; 250 goto done; /* no nul characters */ 251 } 252 } 253 /* 254 * remove nuls 255 */ 256 q = p++; 257 while (--i > 0) { 258 if (*p != '\0') 259 *q++ = *p; 260 p++; 261 } 262 *q = '\0'; 263 if (q == parsefile->buf) 264 goto retry; /* buffer contained nothing but nuls */ 265 parsenleft = q - parsefile->buf - 1; 266 267done: 268 if (parsefile->fd == 0 && hist && something) { 269 INTOFF; 270 history(hist, whichprompt == 1 ? H_ENTER : H_ADD, 271 parsefile->buf); 272 INTON; 273 } 274 if (vflag) { 275 /* 276 * This isn't right. Most shells coordinate it with 277 * reading a line at a time. I honestly don't know if its 278 * worth it. 279 */ 280 i = parsenleft + 1; 281 p = parsefile->buf; 282 for (; i--; p++) 283 out2c(*p) 284 flushout(out2); 285 } 286 return *parsenextc++; 287} 288 289/* 290 * Undo the last call to pgetc. Only one character may be pushed back. 291 * PEOF may be pushed back. 292 */ 293 294void 295pungetc() { 296 parsenleft++; 297 parsenextc--; 298} 299 300/* 301 * Push a string back onto the input at this current parsefile level. 302 * We handle aliases this way. 303 */ 304void 305pushstring(s, len, ap) 306 char *s; 307 int len; 308 void *ap; 309 { 310 struct strpush *sp; 311 312 INTOFF; 313/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ 314 if (parsefile->strpush) { 315 sp = ckmalloc(sizeof (struct strpush)); 316 sp->prev = parsefile->strpush; 317 parsefile->strpush = sp; 318 } else 319 sp = parsefile->strpush = &(parsefile->basestrpush); 320 sp->prevstring = parsenextc; 321 sp->prevnleft = parsenleft; 322 sp->ap = (struct alias *)ap; 323 if (ap) 324 ((struct alias *)ap)->flag |= ALIASINUSE; 325 parsenextc = s; 326 parsenleft = len; 327 INTON; 328} 329 330void 331popstring() 332{ 333 struct strpush *sp = parsefile->strpush; 334 335 INTOFF; 336 parsenextc = sp->prevstring; 337 parsenleft = sp->prevnleft; 338/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ 339 if (sp->ap) 340 sp->ap->flag &= ~ALIASINUSE; 341 parsefile->strpush = sp->prev; 342 if (sp != &(parsefile->basestrpush)) 343 ckfree(sp); 344 INTON; 345} 346 347/* 348 * Set the input to take input from a file. If push is set, push the 349 * old input onto the stack first. 350 */ 351 352void 353setinputfile(fname, push) 354 char *fname; 355 int push; 356{ 357 int fd; 358 int fd2; 359 360 INTOFF; 361 if ((fd = open(fname, O_RDONLY)) < 0) 362 error("Can't open %s", fname); 363 if (fd < 10) { 364 fd2 = copyfd(fd, 10); 365 close(fd); 366 if (fd2 < 0) 367 error("Out of file descriptors"); 368 fd = fd2; 369 } 370 setinputfd(fd, push); 371 INTON; 372} 373 374 375/* 376 * Like setinputfile, but takes an open file descriptor. Call this with 377 * interrupts off. 378 */ 379 380void 381setinputfd(fd, push) 382 int fd, push; 383{ 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 int push; 406 { 407 INTOFF; 408 if (push) 409 pushfile(); 410 parsenextc = string; 411 parsenleft = strlen(string); 412 parsefile->buf = NULL; 413 plinno = 1; 414 INTON; 415} 416 417 418 419/* 420 * To handle the "." command, a stack of input files is used. Pushfile 421 * adds a new entry to the stack and popfile restores the previous level. 422 */ 423 424STATIC void 425pushfile() { 426 struct parsefile *pf; 427 428 parsefile->nleft = parsenleft; 429 parsefile->nextc = parsenextc; 430 parsefile->linno = plinno; 431 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); 432 pf->prev = parsefile; 433 pf->fd = -1; 434 pf->strpush = NULL; 435 pf->basestrpush.prev = NULL; 436 parsefile = pf; 437} 438 439 440void 441popfile() { 442 struct parsefile *pf = parsefile; 443 444 INTOFF; 445 if (pf->fd >= 0) 446 close(pf->fd); 447 if (pf->buf) 448 ckfree(pf->buf); 449 while (pf->strpush) 450 popstring(); 451 parsefile = pf->prev; 452 ckfree(pf); 453 parsenleft = parsefile->nleft; 454 parsenextc = parsefile->nextc; 455 plinno = parsefile->linno; 456 INTON; 457} 458 459 460/* 461 * Return to top level. 462 */ 463 464void 465popallfiles() { 466 while (parsefile != &basepf) 467 popfile(); 468} 469 470 471 472/* 473 * Close the file(s) that the shell is reading commands from. Called 474 * after a fork is done. 475 */ 476 477void 478closescript() { 479 popallfiles(); 480 if (parsefile->fd > 0) { 481 close(parsefile->fd); 482 parsefile->fd = 0; 483 } 484} 485