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.2 (Berkeley) 03/13/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 & FNDELAY) { 164 flags &=~ FNDELAY; 165 if (fcntl(0, F_SETFL, flags) >= 0) { 166 out2str("sh: turning off NDELAY 167 mode\n"); 168 goto retry; 169 } 170 } 171 } 172 } 173 parsenleft = EOF_NLEFT; 174 return PEOF; 175 } 176 parsenleft = i - 1; 177 178 /* delete nul characters */ 179 for (;;) { 180 if (*p++ == '\0') 181 break; 182 if (--i <= 0) 183 return *parsenextc++; /* no nul characters */ 184 } 185 q = p - 1; 186 while (--i > 0) { 187 if (*p != '\0') 188 *q++ = *p; 189 p++; 190 } 191 if (q == parsefile->buf) 192 goto retry; /* buffer contained nothing but nuls */ 193 parsenleft = q - parsefile->buf - 1; 194 return *parsenextc++; 195 } 196 197 198 /* 199 * Undo the last call to pgetc. Only one character may be pushed back. 200 * PEOF may be pushed back. 201 */ 202 203 void 204 pungetc() { 205 parsenleft++; 206 parsenextc--; 207 } 208 209 210 /* 211 * Push a string back onto the input. This code doesn't work if the user 212 * tries to push back more than one string at once. 213 */ 214 215 void 216 ppushback(string, length) 217 char *string; 218 { 219 pushedstring = parsenextc; 220 pushednleft = parsenleft; 221 parsenextc = string; 222 parsenleft = length; 223 } 224 225 226 227 /* 228 * Set the input to take input from a file. If push is set, push the 229 * old input onto the stack first. 230 */ 231 232 void 233 setinputfile(fname, push) 234 char *fname; 235 { 236 int fd; 237 int fd2; 238 239 INTOFF; 240 if ((fd = open(fname, O_RDONLY)) < 0) 241 error("Can't open %s", fname); 242 if (fd < 10) { 243 fd2 = copyfd(fd, 10); 244 close(fd); 245 if (fd2 < 0) 246 error("Out of file descriptors"); 247 fd = fd2; 248 } 249 setinputfd(fd, push); 250 INTON; 251 } 252 253 254 /* 255 * Like setinputfile, but takes an open file descriptor. Call this with 256 * interrupts off. 257 */ 258 259 void 260 setinputfd(fd, push) { 261 if (push) { 262 pushfile(); 263 parsefile->buf = ckmalloc(BUFSIZ); 264 } 265 if (parsefile->fd > 0) 266 close(parsefile->fd); 267 parsefile->fd = fd; 268 if (parsefile->buf == NULL) 269 parsefile->buf = ckmalloc(BUFSIZ); 270 parsenleft = 0; 271 plinno = 1; 272 } 273 274 275 /* 276 * Like setinputfile, but takes input from a string. 277 */ 278 279 void 280 setinputstring(string, push) 281 char *string; 282 { 283 INTOFF; 284 if (push) 285 pushfile(); 286 parsenextc = string; 287 parsenleft = strlen(string); 288 parsefile->buf = NULL; 289 plinno = 1; 290 INTON; 291 } 292 293 294 295 /* 296 * To handle the "." command, a stack of input files is used. Pushfile 297 * adds a new entry to the stack and popfile restores the previous level. 298 */ 299 300 STATIC void 301 pushfile() { 302 struct parsefile *pf; 303 304 parsefile->nleft = parsenleft; 305 parsefile->nextc = parsenextc; 306 parsefile->linno = plinno; 307 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); 308 pf->prev = parsefile; 309 pf->fd = -1; 310 parsefile = pf; 311 } 312 313 314 void 315 popfile() { 316 struct parsefile *pf = parsefile; 317 318 INTOFF; 319 if (pf->fd >= 0) 320 close(pf->fd); 321 if (pf->buf) 322 ckfree(pf->buf); 323 parsefile = pf->prev; 324 ckfree(pf); 325 parsenleft = parsefile->nleft; 326 parsenextc = parsefile->nextc; 327 plinno = parsefile->linno; 328 INTON; 329 } 330 331 332 /* 333 * Return to top level. 334 */ 335 336 void 337 popallfiles() { 338 while (parsefile != &basepf) 339 popfile(); 340 } 341 342 343 344 /* 345 * Close the file(s) that the shell is reading commands from. Called 346 * after a fork is done. 347 */ 348 349 void 350 closescript() { 351 popallfiles(); 352 if (parsefile->fd > 0) { 353 close(parsefile->fd); 354 parsefile->fd = 0; 355 } 356 } 357