master xplshn/aruu / cmd / posix / sh / input.c
  1/*-
  2 * SPDX-License-Identifier: BSD-3-Clause
  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. Neither the name of the University nor the names of its contributors
 19 *    may be used to endorse or promote products derived from this software
 20 *    without specific prior written permission.
 21 *
 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 32 * SUCH DAMAGE.
 33 */
 34
 35#include <errno.h>
 36#include <fcntl.h>
 37#include <stdio.h> /* defines BUFSIZ */
 38#include <stdlib.h>
 39#include <string.h>
 40#include <unistd.h>
 41
 42/*
 43 * This file implements the input routines used by the parser.
 44 */
 45
 46#include "alias.h"
 47#include "error.h"
 48#include "input.h"
 49#include "memalloc.h"
 50#include "options.h"
 51#include "output.h"
 52#include "parser.h"
 53#include "redir.h"
 54#include "shell.h"
 55#include "syntax.h"
 56#ifndef NO_HISTORY
 57#include "lineedit.h"
 58#endif
 59#include "trap.h"
 60
 61#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
 62
 63struct strpush {
 64  struct strpush *prev; /* preceding string on stack */
 65  const char     *prevstring;
 66  int             prevnleft;
 67  int             prevlleft;
 68  struct alias   *ap; /* if push was associated with an alias */
 69};
 70
 71/*
 72 * The parsefile structure pointed to by the global variable parsefile
 73 * contains information about the current file being read.
 74 */
 75
 76struct parsefile {
 77  struct parsefile *prev;        /* preceding file on stack */
 78  int               linno;       /* current line */
 79  int               fd;          /* file descriptor (or -1 if string) */
 80  int               nleft;       /* number of chars left in this line */
 81  int               lleft;       /* number of lines left in this buffer */
 82  const char       *nextc;       /* next char in buffer */
 83  char             *buf;         /* input buffer */
 84  size_t            bufsize;     /* input buffer size */
 85  struct strpush   *strpush;     /* for pushing strings at this level */
 86  struct strpush    basestrpush; /* so pushing one is fast */
 87};
 88
 89int                     plinno = 1;          /* input line number */
 90int                     parsenleft;          /* copy of parsefile->nleft */
 91static int              parselleft;          /* copy of parsefile->lleft */
 92const char             *parsenextc;          /* copy of parsefile->nextc */
 93static char             basebuf[BUFSIZ + 1]; /* buffer for top level input file */
 94static struct parsefile basepf = {
 95    /* top level input file */
 96    .nextc   = basebuf,
 97    .buf     = basebuf,
 98    .bufsize = sizeof(basebuf),
 99};
100static struct parsefile *parsefile = &basepf; /* current input file */
101int                      whichprompt;         /* 1 == PS1, 2 == PS2 */
102
103static void pushfile(void);
104static int  preadfd(void);
105static void popstring(void);
106
107void
108resetinput(void)
109{
110  popallfiles();
111  parselleft = parsenleft = 0; /* clear input buffer */
112}
113
114/*
115 * Read a character from the script, returning PEOF on end of file.
116 * Nul characters in the input are silently discarded.
117 */
118
119int
120pgetc(void)
121{
122  return pgetc_macro();
123}
124
125static int
126preadfd(void)
127{
128  int nr;
129
130retry:
131#ifndef NO_HISTORY
132  if (parsefile->fd == 0 && sh_history_enabled) {
133    char *line;
134
135    line = redline(getprompt(NULL));
136    if (line != NULL) {
137      nr = strlen(line) + 1;
138      if (parsefile->bufsize < (size_t)nr + 1) {
139        size_t bufsize;
140
141        INTOFF;
142        if (parsefile->buf != basebuf) {
143          ckfree(parsefile->buf);
144          parsefile->buf     = NULL;
145          parsefile->bufsize = 0;
146        }
147        bufsize = (size_t)nr + BUFSIZ + 1;
148        bufsize -= bufsize % BUFSIZ;
149        parsefile->buf     = ckmalloc(bufsize);
150        parsefile->bufsize = bufsize;
151        INTON;
152      }
153      memcpy(parsefile->buf, line, nr - 1);
154      parsefile->buf[nr - 1] = '\n';
155      parsefile->buf[nr]     = '\0';
156      free(line);
157    } else {
158      nr = 0;
159    }
160  } else
161#endif
162    nr = read(parsefile->fd, parsefile->buf, parsefile->bufsize - 1);
163
164  if (nr < 0)
165    switch (errno) {
166      int flags;
167
168      case EINTR:
169        goto retry;
170      case EWOULDBLOCK:
171        if (parsefile->fd != 0)
172          break;
173        if ((flags = fcntl(0, F_GETFL, 0)) < 0)
174          break;
175        if (!(flags & O_NONBLOCK))
176          break;
177        if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) < 0)
178          break;
179        out2fmt_flush("sh: turning off NDELAY mode\n");
180        goto retry;
181    }
182  else if (nr > 0)
183    parsefile->buf[nr] = '\0';
184  else
185    nr = -1;
186
187  parsenextc = parsefile->buf;
188  return nr;
189}
190
191/*
192 * Refill the input buffer and return the next input character:
193 *
194 * 1) If a string was pushed back on the input, pop it;
195 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
196 *    from a string so we can't refill the buffer, return EOF.
197 * 3) If there is more in this buffer, use it else call read to fill it.
198 * 4) Process input up to the next newline, deleting nul characters.
199 */
200
201int
202preadbuffer(void)
203{
204  const char *end;
205  char       *q, *r;
206  char        savec;
207
208  while (parsefile->strpush) {
209    /*
210     * Add a space to the end of an alias to ensure that the
211     * alias remains in use while parsing its last word.
212     * This avoids alias recursions.
213     */
214    if (parsenleft == -1 && parsefile->strpush->ap != NULL)
215      return ' ';
216    popstring();
217    if (--parsenleft >= 0)
218      return (*parsenextc++);
219  }
220  if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
221    return PEOF;
222
223again:
224  if (parselleft <= 0 && (parselleft = preadfd()) == -1) {
225    parselleft = parsenleft = EOF_NLEFT;
226    return (PEOF);
227  }
228  end = parsenextc + parselleft;
229  q   = strchrnul(parsenextc, '\n');
230  if (*q == '\0' && q != end) {
231    /* delete nul characters */
232    for (r = q++; q != end; q++)
233      if (*q != '\0')
234        *r++ = *q;
235    *r         = '\0';
236    parselleft = r - parsenextc;
237    goto again;
238  }
239  if (*q == '\0') {
240    parsenleft = parselleft;
241    parselleft = 0;
242  } else /* *q == '\n' */ {
243    q++;
244    parsenleft = q - parsenextc;
245    parselleft -= parsenleft;
246  }
247  parsenleft--;
248
249  savec = *q;
250  *q    = '\0';
251
252#ifndef NO_HISTORY
253  if (parsefile->fd == 0 && sh_history_enabled && parsenextc[strspn(parsenextc, " \t\n")] != '\0') {
254    char *histline = strdup(parsenextc);
255    if (histline) {
256      char *nl = strchr(histline, '\n');
257      if (nl)
258        *nl = '\0';
259      INTOFF;
260      redlineHistoryAdd(histline);
261      INTON;
262      free(histline);
263    }
264  }
265#endif
266
267  if (vflag) {
268    out2str(parsenextc);
269    flushout(out2);
270  }
271
272  *q = savec;
273
274  return *parsenextc++;
275}
276
277/*
278 * Returns if we are certain we are at EOF. Does not cause any more input
279 * to be read from the outside world.
280 */
281
282int
283preadateof(void)
284{
285  if (parsenleft > 0)
286    return 0;
287  if (parsefile->strpush)
288    return 0;
289  if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
290    return 1;
291  return 0;
292}
293
294/*
295 * Undo the last call to pgetc.  Only one character may be pushed back.
296 * PEOF may be pushed back.
297 */
298
299void
300pungetc(void)
301{
302  parsenleft++;
303  parsenextc--;
304}
305
306/*
307 * Push a string back onto the input at this current parsefile level.
308 * We handle aliases this way.
309 */
310void
311pushstring(const char *s, int len, struct alias *ap)
312{
313  struct strpush *sp;
314
315  INTOFF;
316  /*out2fmt_flush("*** calling pushstring: %s, %d\n", s, len);*/
317  if (parsefile->strpush) {
318    sp                 = ckmalloc(sizeof(struct strpush));
319    sp->prev           = parsefile->strpush;
320    parsefile->strpush = sp;
321  } else
322    sp = parsefile->strpush = &(parsefile->basestrpush);
323  sp->prevstring = parsenextc;
324  sp->prevnleft  = parsenleft;
325  sp->prevlleft  = parselleft;
326  sp->ap         = ap;
327  if (ap)
328    ap->flag |= ALIASINUSE;
329  parsenextc = s;
330  parsenleft = len;
331  INTON;
332}
333
334static void
335popstring(void)
336{
337  struct strpush *sp = parsefile->strpush;
338
339  INTOFF;
340  if (sp->ap) {
341    if (parsenextc != sp->ap->val && (parsenextc[-1] == ' ' || parsenextc[-1] == '\t'))
342      forcealias();
343    sp->ap->flag &= ~ALIASINUSE;
344  }
345  parsenextc = sp->prevstring;
346  parsenleft = sp->prevnleft;
347  parselleft = sp->prevlleft;
348  /*out2fmt_flush("*** calling popstring: restoring to '%s'\n",
349   * parsenextc);*/
350  parsefile->strpush = sp->prev;
351  if (sp != &(parsefile->basestrpush))
352    ckfree(sp);
353  INTON;
354}
355
356/*
357 * Set the input to take input from a file.  If push is set, push the
358 * old input onto the stack first.
359 * About verify:
360 *   -1: Obey verifyflag
361 *    0: Do not verify
362 *    1: Do verify
363 */
364
365void
366setinputfile(const char *fname, int push, int verify)
367{
368  int e;
369  int fd;
370  int fd2;
371  int oflags = O_RDONLY | O_CLOEXEC;
372
373  if (verify == 1 || (verify == -1 && verifyflag))
374    oflags |= O_VERIFY;
375
376  INTOFF;
377  if ((fd = open(fname, oflags)) < 0) {
378    e = errno;
379    errorwithstatus(
380        e == ENOENT || e == ENOTDIR ? 127 : 126, "cannot open %s: %s", fname, strerror(e)
381    );
382  }
383  if (fd < 10) {
384    fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 10);
385    close(fd);
386    if (fd2 < 0)
387      error("Out of file descriptors");
388    fd = fd2;
389  }
390  setinputfd(fd, push);
391  INTON;
392}
393
394/*
395 * Like setinputfile, but takes an open file descriptor (which should have
396 * its FD_CLOEXEC flag already set).  Call this with interrupts off.
397 */
398
399void
400setinputfd(int fd, int push)
401{
402  if (push)
403    pushfile();
404  if (parsefile->fd > 0)
405    close(parsefile->fd);
406  parsefile->fd = fd;
407  if (parsefile->buf == NULL) {
408    parsefile->buf     = ckmalloc(BUFSIZ + 1);
409    parsefile->bufsize = BUFSIZ + 1;
410  }
411  parselleft = parsenleft = 0;
412  plinno                  = 1;
413}
414
415/*
416 * Like setinputfile, but takes input from a string.
417 */
418
419void
420setinputstring(const char *string)
421{
422  INTOFF;
423  pushfile();
424  parsenextc = string;
425  parselleft = parsenleft = strlen(string);
426  plinno                  = 1;
427  INTON;
428}
429
430/*
431 * To handle the "." command, a stack of input files is used.  Pushfile
432 * adds a new entry to the stack and popfile restores the previous level.
433 */
434
435static void
436pushfile(void)
437{
438  struct parsefile *pf;
439
440  pf               = (struct parsefile *)ckmalloc(sizeof(struct parsefile));
441  *pf              = (struct parsefile){.prev = parsefile, .fd = -1};
442  parsefile->nleft = parsenleft;
443  parsefile->lleft = parselleft;
444  parsefile->nextc = parsenextc;
445  parsefile->linno = plinno;
446  parsefile        = pf;
447}
448
449void
450popfile(void)
451{
452  struct parsefile *pf = parsefile;
453
454  INTOFF;
455  if (pf->fd >= 0)
456    close(pf->fd);
457  if (pf->buf)
458    ckfree(pf->buf);
459  while (pf->strpush)
460    popstring();
461  parsefile = pf->prev;
462  ckfree(pf);
463  parsenleft = parsefile->nleft;
464  parselleft = parsefile->lleft;
465  parsenextc = parsefile->nextc;
466  plinno     = parsefile->linno;
467  INTON;
468}
469
470/*
471 * Return current file (to go back to it later using popfilesupto()).
472 */
473
474struct parsefile *
475getcurrentfile(void)
476{
477  return parsefile;
478}
479
480/*
481 * Pop files until the given file is on top again. Useful for regular
482 * builtins that read shell commands from files or strings.
483 * If the given file is not an active file, an error is raised.
484 */
485
486void
487popfilesupto(struct parsefile *file)
488{
489  while (parsefile != file && parsefile != &basepf)
490    popfile();
491  if (parsefile != file)
492    error("popfilesupto() misused");
493}
494
495/*
496 * Return to top level.
497 */
498
499void
500popallfiles(void)
501{
502  while (parsefile != &basepf)
503    popfile();
504}
505
506/*
507 * Close the file(s) that the shell is reading commands from.  Called
508 * after a fork is done.
509 */
510
511void
512closescript(void)
513{
514  popallfiles();
515  if (parsefile->fd > 0) {
516    close(parsefile->fd);
517    parsefile->fd = 0;
518  }
519}