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