input.c revision 1.1
1/*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * 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 5.4 (Berkeley) 7/1/91"; 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 "shell.h" 47#include <fcntl.h> 48#include <errno.h> 49#include "syntax.h" 50#include "input.h" 51#include "output.h" 52#include "memalloc.h" 53#include "error.h" 54 55#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ 56 57 58/* 59 * The parsefile structure pointed to by the global variable parsefile 60 * contains information about the current file being read. 61 */ 62 63MKINIT 64struct parsefile { 65 int linno; /* current line */ 66 int fd; /* file descriptor (or -1 if string) */ 67 int nleft; /* number of chars left in buffer */ 68 char *nextc; /* next char in buffer */ 69 struct parsefile *prev; /* preceding file on stack */ 70 char *buf; /* input buffer */ 71}; 72 73 74int plinno = 1; /* input line number */ 75MKINIT int parsenleft; /* copy of parsefile->nleft */ 76char *parsenextc; /* copy of parsefile->nextc */ 77MKINIT struct parsefile basepf; /* top level input file */ 78char basebuf[BUFSIZ]; /* buffer for top level input file */ 79struct parsefile *parsefile = &basepf; /* current input file */ 80char *pushedstring; /* copy of parsenextc when text pushed back */ 81int pushednleft; /* copy of parsenleft when text pushed back */ 82 83#ifdef __STDC__ 84STATIC void pushfile(void); 85#else 86STATIC void pushfile(); 87#endif 88 89 90 91#ifdef mkinit 92INCLUDE "input.h" 93INCLUDE "error.h" 94 95INIT { 96 extern char basebuf[]; 97 98 basepf.nextc = basepf.buf = basebuf; 99} 100 101RESET { 102 if (exception != EXSHELLPROC) 103 parsenleft = 0; /* clear input buffer */ 104 popallfiles(); 105} 106 107SHELLPROC { 108 popallfiles(); 109} 110#endif 111 112 113/* 114 * Read a line from the script. 115 */ 116 117char * 118pfgets(line, len) 119 char *line; 120 { 121 register char *p = line; 122 int nleft = len; 123 int c; 124 125 while (--nleft > 0) { 126 c = pgetc_macro(); 127 if (c == PEOF) { 128 if (p == line) 129 return NULL; 130 break; 131 } 132 *p++ = c; 133 if (c == '\n') 134 break; 135 } 136 *p = '\0'; 137 return line; 138} 139 140 141 142/* 143 * Read a character from the script, returning PEOF on end of file. 144 * Nul characters in the input are silently discarded. 145 */ 146 147int 148pgetc() { 149 return pgetc_macro(); 150} 151 152 153/* 154 * Refill the input buffer and return the next input character: 155 * 156 * 1) If a string was pushed back on the input, switch back to the regular 157 * buffer. 158 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading 159 * from a string so we can't refill the buffer, return EOF. 160 * 3) Call read to read in the characters. 161 * 4) Delete all nul characters from the buffer. 162 */ 163 164int 165preadbuffer() { 166 register char *p, *q; 167 register int i; 168 169 if (pushedstring) { 170 parsenextc = pushedstring; 171 pushedstring = NULL; 172 parsenleft = pushednleft; 173 if (--parsenleft >= 0) 174 return *parsenextc++; 175 } 176 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) 177 return PEOF; 178 flushout(&output); 179 flushout(&errout); 180retry: 181 p = parsenextc = parsefile->buf; 182 i = read(parsefile->fd, p, BUFSIZ); 183 if (i <= 0) { 184 if (i < 0) { 185 if (errno == EINTR) 186 goto retry; 187 if (parsefile->fd == 0 && errno == EWOULDBLOCK) { 188 int flags = fcntl(0, F_GETFL, 0); 189 if (flags >= 0 && flags & O_NONBLOCK) { 190 flags &=~ O_NONBLOCK; 191 if (fcntl(0, F_SETFL, flags) >= 0) { 192 out2str("sh: turning off NDELAY mode\n"); 193 goto retry; 194 } 195 } 196 } 197 } 198 parsenleft = EOF_NLEFT; 199 return PEOF; 200 } 201 parsenleft = i - 1; 202 203 /* delete nul characters */ 204 for (;;) { 205 if (*p++ == '\0') 206 break; 207 if (--i <= 0) 208 return *parsenextc++; /* no nul characters */ 209 } 210 q = p - 1; 211 while (--i > 0) { 212 if (*p != '\0') 213 *q++ = *p; 214 p++; 215 } 216 if (q == parsefile->buf) 217 goto retry; /* buffer contained nothing but nuls */ 218 parsenleft = q - parsefile->buf - 1; 219 return *parsenextc++; 220} 221 222 223/* 224 * Undo the last call to pgetc. Only one character may be pushed back. 225 * PEOF may be pushed back. 226 */ 227 228void 229pungetc() { 230 parsenleft++; 231 parsenextc--; 232} 233 234 235/* 236 * Push a string back onto the input. This code doesn't work if the user 237 * tries to push back more than one string at once. 238 */ 239 240void 241ppushback(string, length) 242 char *string; 243 { 244 pushedstring = parsenextc; 245 pushednleft = parsenleft; 246 parsenextc = string; 247 parsenleft = length; 248} 249 250 251 252/* 253 * Set the input to take input from a file. If push is set, push the 254 * old input onto the stack first. 255 */ 256 257void 258setinputfile(fname, push) 259 char *fname; 260 { 261 int fd; 262 int fd2; 263 264 INTOFF; 265 if ((fd = open(fname, O_RDONLY)) < 0) 266 error("Can't open %s", fname); 267 if (fd < 10) { 268 fd2 = copyfd(fd, 10); 269 close(fd); 270 if (fd2 < 0) 271 error("Out of file descriptors"); 272 fd = fd2; 273 } 274 setinputfd(fd, push); 275 INTON; 276} 277 278 279/* 280 * Like setinputfile, but takes an open file descriptor. Call this with 281 * interrupts off. 282 */ 283 284void 285setinputfd(fd, push) { 286 if (push) { 287 pushfile(); 288 parsefile->buf = ckmalloc(BUFSIZ); 289 } 290 if (parsefile->fd > 0) 291 close(parsefile->fd); 292 parsefile->fd = fd; 293 if (parsefile->buf == NULL) 294 parsefile->buf = ckmalloc(BUFSIZ); 295 parsenleft = 0; 296 plinno = 1; 297} 298 299 300/* 301 * Like setinputfile, but takes input from a string. 302 */ 303 304void 305setinputstring(string, push) 306 char *string; 307 { 308 INTOFF; 309 if (push) 310 pushfile(); 311 parsenextc = string; 312 parsenleft = strlen(string); 313 parsefile->buf = NULL; 314 plinno = 1; 315 INTON; 316} 317 318 319 320/* 321 * To handle the "." command, a stack of input files is used. Pushfile 322 * adds a new entry to the stack and popfile restores the previous level. 323 */ 324 325STATIC void 326pushfile() { 327 struct parsefile *pf; 328 329 parsefile->nleft = parsenleft; 330 parsefile->nextc = parsenextc; 331 parsefile->linno = plinno; 332 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); 333 pf->prev = parsefile; 334 pf->fd = -1; 335 parsefile = pf; 336} 337 338 339void 340popfile() { 341 struct parsefile *pf = parsefile; 342 343 INTOFF; 344 if (pf->fd >= 0) 345 close(pf->fd); 346 if (pf->buf) 347 ckfree(pf->buf); 348 parsefile = pf->prev; 349 ckfree(pf); 350 parsenleft = parsefile->nleft; 351 parsenextc = parsefile->nextc; 352 plinno = parsefile->linno; 353 INTON; 354} 355 356 357/* 358 * Return to top level. 359 */ 360 361void 362popallfiles() { 363 while (parsefile != &basepf) 364 popfile(); 365} 366 367 368 369/* 370 * Close the file(s) that the shell is reading commands from. Called 371 * after a fork is done. 372 */ 373 374void 375closescript() { 376 popallfiles(); 377 if (parsefile->fd > 0) { 378 close(parsefile->fd); 379 parsefile->fd = 0; 380 } 381} 382