Logo Search packages:      
Sourcecode: kbuild version File versions  Download package

parser.c

/*    $NetBSD: parser.c,v 1.59 2005/03/21 20:10:29 dsl Exp $      */

/*-
 * Copyright (c) 1991, 1993
 *    The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Kenneth Almquist.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#if 0
#ifndef lint
static char sccsid[] = "@(#)parser.c      8.7 (Berkeley) 5/16/95";
#else
__RCSID("$NetBSD: parser.c,v 1.59 2005/03/21 20:10:29 dsl Exp $");
#endif /* not lint */
#endif

#include <stdlib.h>

#include "shell.h"
#include "parser.h"
#include "nodes.h"
#include "expand.h"     /* defines rmescapes() */
#include "eval.h" /* defines commandname */
#include "redir.h"      /* defines copyfd() */
#include "syntax.h"
#include "options.h"
#include "input.h"
#include "output.h"
#include "var.h"
#include "error.h"
#include "memalloc.h"
#include "mystring.h"
#include "alias.h"
#include "show.h"
#ifndef SMALL
# include "myhistedit.h"
#endif
#include "cd.h"
#include "shinstance.h"

/*
 * Shell command parser.
 */

#define EOFMARKLEN 79

/* values returned by readtoken */
#include "token.h"

#define OPENBRACE '{'
#define CLOSEBRACE '}'


struct heredoc {
      struct heredoc *next;   /* next here document in list */
      union node *here;       /* redirection node */
      char *eofmark;          /* string indicating end of input */
      int striptabs;          /* if set, strip leading tabs */
};



//static int noalias = 0;           /* when set, don't handle aliases */
//struct heredoc *heredoclist;      /* list of here documents to read */
//int parsebackquote;         /* nonzero if we are inside backquotes */
//int doprompt;               /* if set, prompt the user */
//int needprompt;             /* true if interactive and at start of line */
//int lasttoken;              /* last token read */
//MKINIT int tokpushback;           /* last token pushed back */
//char *wordtext;             /* text of last word returned by readtoken */
//MKINIT int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */
//struct nodelist *backquotelist;
//union node *redirnode;
//struct heredoc *heredoc;
//int quoteflag;              /* set if (part of) last token was quoted */
//int startlinno;             /* line # where last token started */


STATIC union node *list(shinstance *, int);
STATIC union node *andor(shinstance *);
STATIC union node *pipeline(shinstance *);
STATIC union node *command(shinstance *);
STATIC union node *simplecmd(shinstance *, union node **, union node *);
STATIC union node *makename(shinstance *);
STATIC void parsefname(shinstance *);
STATIC void parseheredoc(shinstance *);
STATIC int peektoken(shinstance *);
STATIC int readtoken(shinstance *);
STATIC int xxreadtoken(shinstance *);
STATIC int readtoken1(shinstance *, int, char const *, char *, int);
STATIC int noexpand(shinstance *, char *);
STATIC void synexpect(shinstance *, int) __attribute__((__noreturn__));
STATIC void synerror(shinstance *, const char *) __attribute__((__noreturn__));
STATIC void setprompt(shinstance *, int);


/*
 * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
 * valid parse tree indicating a blank line.)
 */

union node *
parsecmd(shinstance *psh, int interact)
{
      int t;

      psh->tokpushback = 0;
      psh->doprompt = interact;
      if (psh->doprompt)
            setprompt(psh, 1);
      else
            setprompt(psh, 0);
      psh->needprompt = 0;
      t = readtoken(psh);
      if (t == TEOF)
            return NEOF;
      if (t == TNL)
            return NULL;
      psh->tokpushback++;
      return list(psh, 1);
}


STATIC union node *
list(shinstance *psh, int nlflag)
{
      union node *n1, *n2, *n3;
      int tok;

      psh->checkkwd = 2;
      if (nlflag == 0 && tokendlist[peektoken(psh)])
            return NULL;
      n1 = NULL;
      for (;;) {
            n2 = andor(psh);
            tok = readtoken(psh);
            if (tok == TBACKGND) {
                  if (n2->type == NCMD || n2->type == NPIPE) {
                        n2->ncmd.backgnd = 1;
                  } else if (n2->type == NREDIR) {
                        n2->type = NBACKGND;
                  } else {
                        n3 = (union node *)stalloc(psh, sizeof (struct nredir));
                        n3->type = NBACKGND;
                        n3->nredir.n = n2;
                        n3->nredir.redirect = NULL;
                        n2 = n3;
                  }
            }
            if (n1 == NULL) {
                  n1 = n2;
            }
            else {
                  n3 = (union node *)stalloc(psh, sizeof (struct nbinary));
                  n3->type = NSEMI;
                  n3->nbinary.ch1 = n1;
                  n3->nbinary.ch2 = n2;
                  n1 = n3;
            }
            switch (tok) {
            case TBACKGND:
            case TSEMI:
                  tok = readtoken(psh);
                  /* fall through */
            case TNL:
                  if (tok == TNL) {
                        parseheredoc(psh);
                        if (nlflag)
                              return n1;
                  } else {
                        psh->tokpushback++;
                  }
                  psh->checkkwd = 2;
                  if (tokendlist[peektoken(psh)])
                        return n1;
                  break;
            case TEOF:
                  if (psh->heredoclist)
                        parseheredoc(psh);
                  else
                        pungetc(psh);           /* push back EOF on input */
                  return n1;
            default:
                  if (nlflag)
                        synexpect(psh, -1);
                  psh->tokpushback++;
                  return n1;
            }
      }
}



STATIC union node *
andor(shinstance *psh)
{
      union node *n1, *n2, *n3;
      int t;

      n1 = pipeline(psh);
      for (;;) {
            if ((t = readtoken(psh)) == TAND) {
                  t = NAND;
            } else if (t == TOR) {
                  t = NOR;
            } else {
                  psh->tokpushback++;
                  return n1;
            }
            n2 = pipeline(psh);
            n3 = (union node *)stalloc(psh, sizeof (struct nbinary));
            n3->type = t;
            n3->nbinary.ch1 = n1;
            n3->nbinary.ch2 = n2;
            n1 = n3;
      }
}



STATIC union node *
pipeline(shinstance *psh)
{
      union node *n1, *n2, *pipenode;
      struct nodelist *lp, *prev;
      int negate;

      negate = 0;
      TRACE((psh, "pipeline: entered\n"));
      while (readtoken(psh) == TNOT)
            negate = !negate;
      psh->tokpushback++;
      n1 = command(psh);
      if (readtoken(psh) == TPIPE) {
            pipenode = (union node *)stalloc(psh, sizeof (struct npipe));
            pipenode->type = NPIPE;
            pipenode->npipe.backgnd = 0;
            lp = (struct nodelist *)stalloc(psh, sizeof (struct nodelist));
            pipenode->npipe.cmdlist = lp;
            lp->n = n1;
            do {
                  prev = lp;
                  lp = (struct nodelist *)stalloc(psh, sizeof (struct nodelist));
                  lp->n = command(psh);
                  prev->next = lp;
            } while (readtoken(psh) == TPIPE);
            lp->next = NULL;
            n1 = pipenode;
      }
      psh->tokpushback++;
      if (negate) {
            n2 = (union node *)stalloc(psh, sizeof (struct nnot));
            n2->type = NNOT;
            n2->nnot.com = n1;
            return n2;
      } else
            return n1;
}



STATIC union node *
command(shinstance *psh)
{
      union node *n1, *n2;
      union node *ap, **app;
      union node *cp, **cpp;
      union node *redir, **rpp;
      int t, negate = 0;

      psh->checkkwd = 2;
      redir = NULL;
      n1 = NULL;
      rpp = &redir;

      /* Check for redirection which may precede command */
      while (readtoken(psh) == TREDIR) {
            *rpp = n2 = psh->redirnode;
            rpp = &n2->nfile.next;
            parsefname(psh);
      }
      psh->tokpushback++;

      while (readtoken(psh) == TNOT) {
            TRACE((psh, "command: TNOT recognized\n"));
            negate = !negate;
      }
      psh->tokpushback++;

      switch (readtoken(psh)) {
      case TIF:
            n1 = (union node *)stalloc(psh, sizeof (struct nif));
            n1->type = NIF;
            n1->nif.test = list(psh, 0);
            if (readtoken(psh) != TTHEN)
                  synexpect(psh, TTHEN);
            n1->nif.ifpart = list(psh, 0);
            n2 = n1;
            while (readtoken(psh) == TELIF) {
                  n2->nif.elsepart = (union node *)stalloc(psh, sizeof (struct nif));
                  n2 = n2->nif.elsepart;
                  n2->type = NIF;
                  n2->nif.test = list(psh, 0);
                  if (readtoken(psh) != TTHEN)
                        synexpect(psh, TTHEN);
                  n2->nif.ifpart = list(psh, 0);
            }
            if (psh->lasttoken == TELSE)
                  n2->nif.elsepart = list(psh, 0);
            else {
                  n2->nif.elsepart = NULL;
                  psh->tokpushback++;
            }
            if (readtoken(psh) != TFI)
                  synexpect(psh, TFI);
            psh->checkkwd = 1;
            break;
      case TWHILE:
      case TUNTIL: {
            int got;
            n1 = (union node *)stalloc(psh, sizeof (struct nbinary));
            n1->type = (psh->lasttoken == TWHILE)? NWHILE : NUNTIL;
            n1->nbinary.ch1 = list(psh, 0);
            if ((got=readtoken(psh)) != TDO) {
TRACE((psh, "expecting DO got %s %s\n", tokname[got], got == TWORD ? psh->wordtext : ""));
                  synexpect(psh, TDO);
            }
            n1->nbinary.ch2 = list(psh, 0);
            if (readtoken(psh) != TDONE)
                  synexpect(psh, TDONE);
            psh->checkkwd = 1;
            break;
      }
      case TFOR:
            if (readtoken(psh) != TWORD || psh->quoteflag || ! goodname(psh->wordtext))
                  synerror(psh, "Bad for loop variable");
            n1 = (union node *)stalloc(psh, sizeof (struct nfor));
            n1->type = NFOR;
            n1->nfor.var = psh->wordtext;
            if (readtoken(psh) == TWORD && ! psh->quoteflag && equal(psh->wordtext, "in")) {
                  app = &ap;
                  while (readtoken(psh) == TWORD) {
                        n2 = (union node *)stalloc(psh, sizeof (struct narg));
                        n2->type = NARG;
                        n2->narg.text = psh->wordtext;
                        n2->narg.backquote = psh->backquotelist;
                        *app = n2;
                        app = &n2->narg.next;
                  }
                  *app = NULL;
                  n1->nfor.args = ap;
                  if (psh->lasttoken != TNL && psh->lasttoken != TSEMI)
                        synexpect(psh, -1);
            } else {
                  static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
                                                   '@', '=', '\0'};
                  n2 = (union node *)stalloc(psh, sizeof (struct narg));
                  n2->type = NARG;
                  n2->narg.text = argvars;
                  n2->narg.backquote = NULL;
                  n2->narg.next = NULL;
                  n1->nfor.args = n2;
                  /*
                   * Newline or semicolon here is optional (but note
                   * that the original Bourne shell only allowed NL).
                   */
                  if (psh->lasttoken != TNL && psh->lasttoken != TSEMI)
                        psh->tokpushback++;
            }
            psh->checkkwd = 2;
            if ((t = readtoken(psh)) == TDO)
                  t = TDONE;
            else if (t == TBEGIN)
                  t = TEND;
            else
                  synexpect(psh, -1);
            n1->nfor.body = list(psh, 0);
            if (readtoken(psh) != t)
                  synexpect(psh, t);
            psh->checkkwd = 1;
            break;
      case TCASE:
            n1 = (union node *)stalloc(psh, sizeof (struct ncase));
            n1->type = NCASE;
            if (readtoken(psh) != TWORD)
                  synexpect(psh, TWORD);
            n1->ncase.expr = n2 = (union node *)stalloc(psh, sizeof (struct narg));
            n2->type = NARG;
            n2->narg.text = psh->wordtext;
            n2->narg.backquote = psh->backquotelist;
            n2->narg.next = NULL;
            while (readtoken(psh) == TNL);
            if (psh->lasttoken != TWORD || ! equal(psh->wordtext, "in"))
                  synerror(psh, "expecting \"in\"");
            cpp = &n1->ncase.cases;
            psh->noalias = 1;
            psh->checkkwd = 2, readtoken(psh);
            do {
                  *cpp = cp = (union node *)stalloc(psh, sizeof (struct nclist));
                  cp->type = NCLIST;
                  app = &cp->nclist.pattern;
                  for (;;) {
                        *app = ap = (union node *)stalloc(psh, sizeof (struct narg));
                        ap->type = NARG;
                        ap->narg.text = psh->wordtext;
                        ap->narg.backquote = psh->backquotelist;
                        if (psh->checkkwd = 2, readtoken(psh) != TPIPE)
                              break;
                        app = &ap->narg.next;
                        readtoken(psh);
                  }
                  ap->narg.next = NULL;
                  psh->noalias = 0;
                  if (psh->lasttoken != TRP) {
                        synexpect(psh, TRP);
                  }
                  cp->nclist.body = list(psh, 0);

                  psh->checkkwd = 2;
                  if ((t = readtoken(psh)) != TESAC) {
                        if (t != TENDCASE) {
                              psh->noalias = 0;
                              synexpect(psh, TENDCASE);
                        } else {
                              psh->noalias = 1;
                              psh->checkkwd = 2;
                              readtoken(psh);
                        }
                  }
                  cpp = &cp->nclist.next;
            } while(psh->lasttoken != TESAC);
            psh->noalias = 0;
            *cpp = NULL;
            psh->checkkwd = 1;
            break;
      case TLP:
            n1 = (union node *)stalloc(psh, sizeof (struct nredir));
            n1->type = NSUBSHELL;
            n1->nredir.n = list(psh, 0);
            n1->nredir.redirect = NULL;
            if (readtoken(psh) != TRP)
                  synexpect(psh, TRP);
            psh->checkkwd = 1;
            break;
      case TBEGIN:
            n1 = list(psh, 0);
            if (readtoken(psh) != TEND)
                  synexpect(psh, TEND);
            psh->checkkwd = 1;
            break;
      /* Handle an empty command like other simple commands.  */
      case TSEMI:
            /*
             * An empty command before a ; doesn't make much sense, and
             * should certainly be disallowed in the case of `if ;'.
             */
            if (!redir)
                  synexpect(psh, -1);
      case TAND:
      case TOR:
      case TNL:
      case TEOF:
      case TWORD:
      case TRP:
            psh->tokpushback++;
            n1 = simplecmd(psh, rpp, redir);
            goto checkneg;
      default:
            synexpect(psh, -1);
            /* NOTREACHED */
      }

      /* Now check for redirection which may follow command */
      while (readtoken(psh) == TREDIR) {
            *rpp = n2 = psh->redirnode;
            rpp = &n2->nfile.next;
            parsefname(psh);
      }
      psh->tokpushback++;
      *rpp = NULL;
      if (redir) {
            if (n1->type != NSUBSHELL) {
                  n2 = (union node *)stalloc(psh, sizeof (struct nredir));
                  n2->type = NREDIR;
                  n2->nredir.n = n1;
                  n1 = n2;
            }
            n1->nredir.redirect = redir;
      }

checkneg:
      if (negate) {
            n2 = (union node *)stalloc(psh, sizeof (struct nnot));
            n2->type = NNOT;
            n2->nnot.com = n1;
            return n2;
      }
      else
            return n1;
}


STATIC union node *
simplecmd(shinstance *psh, union node **rpp, union node *redir)
{
      union node *args, **app;
      union node **orig_rpp = rpp;
      union node *n = NULL, *n2;
      int negate = 0;

      /* If we don't have any redirections already, then we must reset */
      /* rpp to be the address of the local redir variable.  */
      if (redir == 0)
            rpp = &redir;

      args = NULL;
      app = &args;
      /*
       * We save the incoming value, because we need this for shell
       * functions.  There can not be a redirect or an argument between
       * the function name and the open parenthesis.
       */
      orig_rpp = rpp;

      while (readtoken(psh) == TNOT) {
            TRACE((psh, "command: TNOT recognized\n"));
            negate = !negate;
      }
      psh->tokpushback++;

      for (;;) {
            if (readtoken(psh) == TWORD) {
                  n = (union node *)stalloc(psh, sizeof (struct narg));
                  n->type = NARG;
                  n->narg.text = psh->wordtext;
                  n->narg.backquote = psh->backquotelist;
                  *app = n;
                  app = &n->narg.next;
            } else if (psh->lasttoken == TREDIR) {
                  *rpp = n = psh->redirnode;
                  rpp = &n->nfile.next;
                  parsefname(psh);  /* read name of redirection file */
            } else if (psh->lasttoken == TLP && app == &args->narg.next
                                  && rpp == orig_rpp) {
                  /* We have a function */
                  if (readtoken(psh) != TRP)
                        synexpect(psh, TRP);
#ifdef notdef
                  if (! goodname(n->narg.text))
                        synerror(psh, "Bad function name");
#endif
                  n->type = NDEFUN;
                  n->narg.next = command(psh);
                  goto checkneg;
            } else {
                  psh->tokpushback++;
                  break;
            }
      }
      *app = NULL;
      *rpp = NULL;
      n = (union node *)stalloc(psh, sizeof (struct ncmd));
      n->type = NCMD;
      n->ncmd.backgnd = 0;
      n->ncmd.args = args;
      n->ncmd.redirect = redir;

checkneg:
      if (negate) {
            n2 = (union node *)stalloc(psh, sizeof (struct nnot));
            n2->type = NNOT;
            n2->nnot.com = n;
            return n2;
      }
      else
            return n;
}

STATIC union node *
makename(shinstance *psh)
{
      union node *n;

      n = (union node *)stalloc(psh, sizeof (struct narg));
      n->type = NARG;
      n->narg.next = NULL;
      n->narg.text = psh->wordtext;
      n->narg.backquote = psh->backquotelist;
      return n;
}

void fixredir(shinstance *psh, union node *n, const char *text, int err)
      {
      TRACE((psh, "Fix redir %s %d\n", text, err));
      if (!err)
            n->ndup.vname = NULL;

      if (is_digit(text[0]) && text[1] == '\0')
            n->ndup.dupfd = digit_val(text[0]);
      else if (text[0] == '-' && text[1] == '\0')
            n->ndup.dupfd = -1;
      else {

            if (err)
                  synerror(psh, "Bad fd number");
            else
                  n->ndup.vname = makename(psh);
      }
}


STATIC void
parsefname(shinstance *psh)
{
      union node *n = psh->redirnode;

      if (readtoken(psh) != TWORD)
            synexpect(psh, -1);
      if (n->type == NHERE) {
            struct heredoc *here = psh->heredoc;
            struct heredoc *p;
            size_t i;

            if (psh->quoteflag == 0)
                  n->type = NXHERE;
            TRACE((psh, "Here document %d\n", n->type));
            if (here->striptabs) {
                  while (*psh->wordtext == '\t')
                        psh->wordtext++;
            }
            if (! noexpand(psh, psh->wordtext) || (i = strlen(psh->wordtext)) == 0 || i > EOFMARKLEN)
                  synerror(psh, "Illegal eof marker for << redirection");
            rmescapes(psh, psh->wordtext);
            here->eofmark = psh->wordtext;
            here->next = NULL;
            if (psh->heredoclist == NULL)
                  psh->heredoclist = here;
            else {
                  for (p = psh->heredoclist ; p->next ; p = p->next);
                  p->next = here;
            }
      } else if (n->type == NTOFD || n->type == NFROMFD) {
            fixredir(psh, n, psh->wordtext, 0);
      } else {
            n->nfile.fname = makename(psh);
      }
}


/*
 * Input any here documents.
 */

STATIC void
parseheredoc(shinstance *psh)
{
      struct heredoc *here;
      union node *n;

      while (psh->heredoclist) {
            here = psh->heredoclist;
            psh->heredoclist = here->next;
            if (psh->needprompt) {
                  setprompt(psh, 2);
                  psh->needprompt = 0;
            }
            readtoken1(psh, pgetc(psh), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
                        here->eofmark, here->striptabs);
            n = (union node *)stalloc(psh, sizeof (struct narg));
            n->narg.type = NARG;
            n->narg.next = NULL;
            n->narg.text = psh->wordtext;
            n->narg.backquote = psh->backquotelist;
            here->here->nhere.doc = n;
      }
}

STATIC int
peektoken(shinstance *psh)
{
      int t;

      t = readtoken(psh);
      psh->tokpushback++;
      return (t);
}

STATIC int
readtoken(shinstance *psh)
{
      int t;
      int savecheckkwd = psh->checkkwd;
#ifdef DEBUG
      int alreadyseen = psh->tokpushback;
#endif
      struct alias *ap;

      top:
      t = xxreadtoken(psh);

      if (psh->checkkwd) {
            /*
             * eat newlines
             */
            if (psh->checkkwd == 2) {
                  psh->checkkwd = 0;
                  while (t == TNL) {
                        parseheredoc(psh);
                        t = xxreadtoken(psh);
                  }
            } else
                  psh->checkkwd = 0;
            /*
             * check for keywords and aliases
             */
            if (t == TWORD && !psh->quoteflag)
            {
                  const char *const *pp;

                  for (pp = parsekwd; *pp; pp++) {
                        if (**pp == *psh->wordtext && equal(*pp, psh->wordtext))
                        {
                              psh->lasttoken = t = (int)(pp -
                                  parsekwd + KWDOFFSET);
                              TRACE((psh, "keyword %s recognized\n", tokname[t]));
                              goto out;
                        }
                  }
                  if(!psh->noalias &&
                      (ap = lookupalias(psh, psh->wordtext, 1)) != NULL) {
                        pushstring(psh, ap->val, strlen(ap->val), ap);
                        psh->checkkwd = savecheckkwd;
                        goto top;
                  }
            }
out:
            psh->checkkwd = (t == TNOT) ? savecheckkwd : 0;
      }
#ifdef DEBUG
      if (!alreadyseen)
          TRACE((psh, "token %s %s\n", tokname[t], t == TWORD ? psh->wordtext : ""));
      else
          TRACE((psh, "reread token %s %s\n", tokname[t], t == TWORD ? psh->wordtext : ""));
#endif
      return (t);
}


/*
 * Read the next input token.
 * If the token is a word, we set psh->backquotelist to the list of cmds in
 *    backquotes.  We set psh->quoteflag to true if any part of the word was
 *    quoted.
 * If the token is TREDIR, then we set psh->redirnode to a structure containing
 *    the redirection.
 * In all cases, the variable psh->startlinno is set to the number of the line
 *    on which the token starts.
 *
 * [Change comment:  here documents and internal procedures]
 * [Readtoken shouldn't have any arguments.  Perhaps we should make the
 *  word parsing code into a separate routine.  In this case, readtoken
 *  doesn't need to have any internal procedures, but parseword does.
 *  We could also make parseoperator in essence the main routine, and
 *  have parseword (readtoken1?) handle both words and redirection.]
 */

#define RETURN(token)   return psh->lasttoken = token

STATIC int
xxreadtoken(shinstance *psh)
{
      int c;

      if (psh->tokpushback) {
            psh->tokpushback = 0;
            return psh->lasttoken;
      }
      if (psh->needprompt) {
            setprompt(psh, 2);
            psh->needprompt = 0;
      }
      psh->startlinno = psh->plinno;
      for (;;) {  /* until token or start of word found */
            c = pgetc_macro(psh);
            if (c == ' ' || c == '\t')
                  continue;         /* quick check for white space first */
            switch (c) {
            case ' ': case '\t':
                  continue;
            case '#':
                  while ((c = pgetc(psh)) != '\n' && c != PEOF);
                  pungetc(psh);
                  continue;
            case '\\':
                  if (pgetc(psh) == '\n') {
                        psh->startlinno = ++psh->plinno;
                        if (psh->doprompt)
                              setprompt(psh, 2);
                        else
                              setprompt(psh, 0);
                        continue;
                  }
                  pungetc(psh);
                  goto breakloop;
            case '\n':
                  psh->plinno++;
                  psh->needprompt = psh->doprompt;
                  RETURN(TNL);
            case PEOF:
                  RETURN(TEOF);
            case '&':
                  if (pgetc(psh) == '&')
                        RETURN(TAND);
                  pungetc(psh);
                  RETURN(TBACKGND);
            case '|':
                  if (pgetc(psh) == '|')
                        RETURN(TOR);
                  pungetc(psh);
                  RETURN(TPIPE);
            case ';':
                  if (pgetc(psh) == ';')
                        RETURN(TENDCASE);
                  pungetc(psh);
                  RETURN(TSEMI);
            case '(':
                  RETURN(TLP);
            case ')':
                  RETURN(TRP);
            default:
                  goto breakloop;
            }
      }
breakloop:
      return readtoken1(psh, c, BASESYNTAX, (char *)NULL, 0);
#undef RETURN
}



/*
 * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
 * is not NULL, read a here document.  In the latter case, eofmark is the
 * word which marks the end of the document and striptabs is true if
 * leading tabs should be stripped from the document.  The argument firstc
 * is the first character of the input token or document.
 *
 * Because C does not have internal subroutines, I have simulated them
 * using goto's to implement the subroutine linkage.  The following macros
 * will run code that appears at the end of readtoken1.
 */

#define CHECKEND()      {goto checkend; checkend_return:;}
#define PARSEREDIR()    {goto parseredir; parseredir_return:;}
#define PARSESUB()      {goto parsesub; parsesub_return:;}
#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
#define     PARSEARITH()      {goto parsearith; parsearith_return:;}

/*
 * Keep track of nested doublequotes in dblquote and doublequotep.
 * We use dblquote for the first 32 levels, and we expand to a malloc'ed
 * region for levels above that. Usually we never need to malloc.
 * This code assumes that an int is 32 bits. We don't use uint32_t,
 * because the rest of the code does not.
 */
#define ISDBLQUOTE() ((varnest < 32) ? (dblquote & (1 << varnest)) : \
    (dblquotep[(varnest / 32) - 1] & (1 << (varnest % 32))))

#define SETDBLQUOTE() \
    if (varnest < 32) \
      dblquote |= (1 << varnest); \
    else \
      dblquotep[(varnest / 32) - 1] |= (1 << (varnest % 32))

#define CLRDBLQUOTE() \
    if (varnest < 32) \
      dblquote &= ~(1 << varnest); \
    else \
      dblquotep[(varnest / 32) - 1] &= ~(1 << (varnest % 32))

STATIC int
readtoken1(shinstance *psh, int firstc, char const *syntax, char *eofmark, int striptabs)
{
      int c = firstc;
      char *out;
      int len;
      char line[EOFMARKLEN + 1];
      struct nodelist *bqlist;
      int quotef;
      int *dblquotep = NULL;
      size_t maxnest = 32;
      int dblquote;
      int varnest;      /* levels of variables expansion */
      int arinest;      /* levels of arithmetic expansion */
      int parenlevel;   /* levels of parens in arithmetic */
      int oldstyle;
      char const *prevsyntax; /* syntax before arithmetic */
#if __GNUC__
      /* Avoid longjmp clobbering */
      (void) &maxnest;
      (void) &dblquotep;
      (void) &out;
      (void) &quotef;
      (void) &dblquote;
      (void) &varnest;
      (void) &arinest;
      (void) &parenlevel;
      (void) &oldstyle;
      (void) &prevsyntax;
      (void) &syntax;
#endif

      psh->startlinno = psh->plinno;
      dblquote = 0;
      varnest = 0;
      if (syntax == DQSYNTAX) {
            SETDBLQUOTE();
      }
      quotef = 0;
      bqlist = NULL;
      arinest = 0;
      parenlevel = 0;

      STARTSTACKSTR(psh, out);
      loop: {     /* for each line, until end of word */
#if ATTY
            if (c == '\034' && psh->doprompt
             && attyset() && ! equal(termval(), "emacs")) {
                  attyline();
                  if (syntax == BASESYNTAX)
                        return readtoken(psh);
                  c = pgetc(psh);
                  goto loop;
            }
#endif
            CHECKEND(); /* set c to PEOF if at end of here document */
            for (;;) {  /* until end of line or end of word */
                  CHECKSTRSPACE(psh, 4, out);   /* permit 4 calls to USTPUTC */
                  switch(syntax[c]) {
                  case CNL:   /* '\n' */
                        if (syntax == BASESYNTAX)
                              goto endword;     /* exit outer loop */
                        USTPUTC(psh, c, out);
                        psh->plinno++;
                        if (psh->doprompt)
                              setprompt(psh, 2);
                        else
                              setprompt(psh, 0);
                        c = pgetc(psh);
                        goto loop;        /* continue outer loop */
                  case CWORD:
                        USTPUTC(psh, c, out);
                        break;
                  case CCTL:
                        if (eofmark == NULL || ISDBLQUOTE())
                              USTPUTC(psh, CTLESC, out);
                        USTPUTC(psh, c, out);
                        break;
                  case CBACK: /* backslash */
                        c = pgetc(psh);
                        if (c == PEOF) {
                              USTPUTC(psh, '\\', out);
                              pungetc(psh);
                              break;
                        }
                        if (c == '\n') {
                              if (psh->doprompt)
                                    setprompt(psh, 2);
                              else
                                    setprompt(psh, 0);
                              break;
                        }
                        quotef = 1;
                        if (ISDBLQUOTE() && c != '\\' &&
                            c != '`' && c != '$' &&
                            (c != '"' || eofmark != NULL))
                              USTPUTC(psh, '\\', out);
                        if (SQSYNTAX[c] == CCTL)
                              USTPUTC(psh, CTLESC, out);
                        else if (eofmark == NULL) {
                              USTPUTC(psh, CTLQUOTEMARK, out);
                              USTPUTC(psh, c, out);
                              if (varnest != 0)
                                    USTPUTC(psh, CTLQUOTEEND, out);
                              break;
                        }
                        USTPUTC(psh, c, out);
                        break;
                  case CSQUOTE:
                        if (syntax != SQSYNTAX) {
                              if (eofmark == NULL)
                                    USTPUTC(psh, CTLQUOTEMARK, out);
                              quotef = 1;
                              syntax = SQSYNTAX;
                              break;
                        }
                        if (eofmark != NULL && arinest == 0 &&
                            varnest == 0) {
                              /* Ignore inside quoted here document */
                              USTPUTC(psh, c, out);
                              break;
                        }
                        /* End of single quotes... */
                        if (arinest)
                              syntax = ARISYNTAX;
                        else {
                              syntax = BASESYNTAX;
                              if (varnest != 0)
                                    USTPUTC(psh, CTLQUOTEEND, out);
                        }
                        break;
                  case CDQUOTE:
                        if (eofmark != NULL && arinest == 0 &&
                            varnest == 0) {
                              /* Ignore inside here document */
                              USTPUTC(psh, c, out);
                              break;
                        }
                        quotef = 1;
                        if (arinest) {
                              if (ISDBLQUOTE()) {
                                    syntax = ARISYNTAX;
                                    CLRDBLQUOTE();
                              } else {
                                    syntax = DQSYNTAX;
                                    SETDBLQUOTE();
                                    USTPUTC(psh, CTLQUOTEMARK, out);
                              }
                              break;
                        }
                        if (eofmark != NULL)
                              break;
                        if (ISDBLQUOTE()) {
                              if (varnest != 0)
                                    USTPUTC(psh, CTLQUOTEEND, out);
                              syntax = BASESYNTAX;
                              CLRDBLQUOTE();
                        } else {
                              syntax = DQSYNTAX;
                              SETDBLQUOTE();
                              USTPUTC(psh, CTLQUOTEMARK, out);
                        }
                        break;
                  case CVAR:  /* '$' */
                        PARSESUB();       /* parse substitution */
                        break;
                  case CENDVAR:     /* CLOSEBRACE */
                        if (varnest > 0 && !ISDBLQUOTE()) {
                              varnest--;
                              USTPUTC(psh, CTLENDVAR, out);
                        } else {
                              USTPUTC(psh, c, out);
                        }
                        break;
                  case CLP:   /* '(' in arithmetic */
                        parenlevel++;
                        USTPUTC(psh, c, out);
                        break;
                  case CRP:   /* ')' in arithmetic */
                        if (parenlevel > 0) {
                              USTPUTC(psh, c, out);
                              --parenlevel;
                        } else {
                              if (pgetc(psh) == ')') {
                                    if (--arinest == 0) {
                                          USTPUTC(psh, CTLENDARI, out);
                                          syntax = prevsyntax;
                                          if (syntax == DQSYNTAX)
                                                SETDBLQUOTE();
                                          else
                                                CLRDBLQUOTE();
                                    } else
                                          USTPUTC(psh, ')', out);
                              } else {
                                    /*
                                     * unbalanced parens
                                     *  (don't 2nd guess - no error)
                                     */
                                    pungetc(psh);
                                    USTPUTC(psh, ')', out);
                              }
                        }
                        break;
                  case CBQUOTE:     /* '`' */
                        PARSEBACKQOLD();
                        break;
                  case CSHEOF:
                        goto endword;           /* exit outer loop */
                  default:
                        if (varnest == 0)
                              goto endword;     /* exit outer loop */
                        USTPUTC(psh, c, out);
                  }
                  c = pgetc_macro(psh);
            }
      }
endword:
      if (syntax == ARISYNTAX)
            synerror(psh, "Missing '))'");
      if (syntax != BASESYNTAX && ! psh->parsebackquote && eofmark == NULL)
            synerror(psh, "Unterminated quoted string");
      if (varnest != 0) {
            psh->startlinno = psh->plinno;
            /* { */
            synerror(psh, "Missing '}'");
      }
      USTPUTC(psh, '\0', out);
      len = (int)(out - stackblock(psh));
      out = stackblock(psh);
      if (eofmark == NULL) {
            if ((c == '>' || c == '<')
             && quotef == 0
             && len <= 2
             && (*out == '\0' || is_digit(*out))) {
                  PARSEREDIR();
                  return psh->lasttoken = TREDIR;
            } else {
                  pungetc(psh);
            }
      }
      psh->quoteflag = quotef;
      psh->backquotelist = bqlist;
      grabstackblock(psh, len);
      psh->wordtext = out;
      if (dblquotep != NULL)
          ckfree(dblquotep);
      return psh->lasttoken = TWORD;
/* end of readtoken routine */



/*
 * Check to see whether we are at the end of the here document.  When this
 * is called, c is set to the first character of the next input line.  If
 * we are at the end of the here document, this routine sets the c to PEOF.
 */

checkend: {
      if (eofmark) {
            if (striptabs) {
                  while (c == '\t')
                        c = pgetc(psh);
            }
            if (c == *eofmark) {
                  if (pfgets(psh, line, sizeof line) != NULL) {
                        char *p, *q;

                        p = line;
                        for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
                        if (*p == '\n' && *q == '\0') {
                              c = PEOF;
                              psh->plinno++;
                              psh->needprompt = psh->doprompt;
                        } else {
                              pushstring(psh, line, strlen(line), NULL);
                        }
                  }
            }
      }
      goto checkend_return;
}


/*
 * Parse a redirection operator.  The variable "out" points to a string
 * specifying the fd to be redirected.  The variable "c" contains the
 * first character of the redirection operator.
 */

parseredir: {
      char fd = *out;
      union node *np;

      np = (union node *)stalloc(psh, sizeof (struct nfile));
      if (c == '>') {
            np->nfile.fd = 1;
            c = pgetc(psh);
            if (c == '>')
                  np->type = NAPPEND;
            else if (c == '|')
                  np->type = NCLOBBER;
            else if (c == '&')
                  np->type = NTOFD;
            else {
                  np->type = NTO;
                  pungetc(psh);
            }
      } else {    /* c == '<' */
            np->nfile.fd = 0;
            switch (c = pgetc(psh)) {
            case '<':
                  if (sizeof (struct nfile) != sizeof (struct nhere)) {
                        np = (union node *)stalloc(psh, sizeof (struct nhere));
                        np->nfile.fd = 0;
                  }
                  np->type = NHERE;
                  psh->heredoc = (struct heredoc *)stalloc(psh, sizeof (struct heredoc));
                  psh->heredoc->here = np;
                  if ((c = pgetc(psh)) == '-') {
                        psh->heredoc->striptabs = 1;
                  } else {
                        psh->heredoc->striptabs = 0;
                        pungetc(psh);
                  }
                  break;

            case '&':
                  np->type = NFROMFD;
                  break;

            case '>':
                  np->type = NFROMTO;
                  break;

            default:
                  np->type = NFROM;
                  pungetc(psh);
                  break;
            }
      }
      if (fd != '\0')
            np->nfile.fd = digit_val(fd);
      psh->redirnode = np;
      goto parseredir_return;
}


/*
 * Parse a substitution.  At this point, we have read the dollar sign
 * and nothing else.
 */

parsesub: {
      int subtype;
      int typeloc;
      int flags;
      char *p;
      static const char types[] = "}-+?=";

      c = pgetc(psh);
      if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) {
            USTPUTC(psh, '$', out);
            pungetc(psh);
      } else if (c == '(') {  /* $(command) or $((arith)) */
            if (pgetc(psh) == '(') {
                  PARSEARITH();
            } else {
                  pungetc(psh);
                  PARSEBACKQNEW();
            }
      } else {
            USTPUTC(psh, CTLVAR, out);
            typeloc = (int)(out - stackblock(psh));
            USTPUTC(psh, VSNORMAL, out);
            subtype = VSNORMAL;
            if (c == OPENBRACE) {
                  c = pgetc(psh);
                  if (c == '#') {
                        if ((c = pgetc(psh)) == CLOSEBRACE)
                              c = '#';
                        else
                              subtype = VSLENGTH;
                  }
                  else
                        subtype = 0;
            }
            if (is_name(c)) {
                  do {
                        STPUTC(psh, c, out);
                        c = pgetc(psh);
                  } while (is_in_name(c));
            } else if (is_digit(c)) {
                  do {
                        USTPUTC(psh, c, out);
                        c = pgetc(psh);
                  } while (is_digit(c));
            }
            else if (is_special(c)) {
                  USTPUTC(psh, c, out);
                  c = pgetc(psh);
            }
            else
badsub:                 synerror(psh, "Bad substitution");

            STPUTC(psh, '=', out);
            flags = 0;
            if (subtype == 0) {
                  switch (c) {
                  case ':':
                        flags = VSNUL;
                        c = pgetc(psh);
                        /*FALLTHROUGH*/
                  default:
                        p = strchr(types, c);
                        if (p == NULL)
                              goto badsub;
                        subtype = (int)(p - types + VSNORMAL);
                        break;
                  case '%':
                  case '#':
                        {
                              int cc = c;
                              subtype = c == '#' ? VSTRIMLEFT :
                                               VSTRIMRIGHT;
                              c = pgetc(psh);
                              if (c == cc)
                                    subtype++;
                              else
                                    pungetc(psh);
                              break;
                        }
                  }
            } else {
                  pungetc(psh);
            }
            if (ISDBLQUOTE() || arinest)
                  flags |= VSQUOTE;
            *(stackblock(psh) + typeloc) = subtype | flags;
            if (subtype != VSNORMAL) {
                  varnest++;
                  if (varnest >= maxnest) {
                        dblquotep = ckrealloc(dblquotep, maxnest / 8);
                        dblquotep[(maxnest / 32) - 1] = 0;
                        maxnest += 32;
                  }
            }
      }
      goto parsesub_return;
}


/*
 * Called to parse command substitutions.  Newstyle is set if the command
 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
 * list of commands (passed by reference), and savelen is the number of
 * characters on the top of the stack which must be preserved.
 */

parsebackq: {
      struct nodelist **nlpp;
      int savepbq;
      union node *n;
      char *volatile str;
      struct jmploc jmploc;
      struct jmploc *volatile savehandler;
      int savelen;
      int saveprompt;
#ifdef __GNUC__
      (void) &saveprompt;
#endif

      savepbq = psh->parsebackquote;
      if (setjmp(jmploc.loc)) {
            if (str)
                  ckfree(str);
            psh->parsebackquote = 0;
            psh->handler = savehandler;
            longjmp(psh->handler->loc, 1);
      }
      INTOFF;
      str = NULL;
      savelen = (int)(out - stackblock(psh));
      if (savelen > 0) {
            str = ckmalloc(savelen);
            memcpy(str, stackblock(psh), savelen);
      }
      savehandler = psh->handler;
      psh->handler = &jmploc;
      INTON;
        if (oldstyle) {
                /* We must read until the closing backquote, giving special
                   treatment to some slashes, and then push the string and
                   reread it as input, interpreting it normally.  */
                char *pout;
                int pc;
                int psavelen;
                char *pstr;


                STARTSTACKSTR(psh, pout);
            for (;;) {
                  if (psh->needprompt) {
                        setprompt(psh, 2);
                        psh->needprompt = 0;
                  }
                  switch (pc = pgetc(psh)) {
                  case '`':
                        goto done;

                  case '\\':
                                if ((pc = pgetc(psh)) == '\n') {
                              psh->plinno++;
                              if (psh->doprompt)
                                    setprompt(psh, 2);
                              else
                                    setprompt(psh, 0);
                              /*
                               * If eating a newline, avoid putting
                               * the newline into the new character
                               * stream (via the STPUTC after the
                               * switch).
                               */
                              continue;
                        }
                                if (pc != '\\' && pc != '`' && pc != '$'
                                    && (!ISDBLQUOTE() || pc != '"'))
                                        STPUTC(psh, '\\', pout);
                        break;

                  case '\n':
                        psh->plinno++;
                        psh->needprompt = psh->doprompt;
                        break;

                  case PEOF:
                          psh->startlinno = psh->plinno;
                        synerror(psh, "EOF in backquote substitution");
                        break;

                  default:
                        break;
                  }
                  STPUTC(psh, pc, pout);
                }
done:
                STPUTC(psh, '\0', pout);
                psavelen = (int)(pout - stackblock(psh));
                if (psavelen > 0) {
                  pstr = grabstackstr(psh, pout);
                  setinputstring(psh, pstr, 1);
                }
        }
      nlpp = &bqlist;
      while (*nlpp)
            nlpp = &(*nlpp)->next;
      *nlpp = (struct nodelist *)stalloc(psh, sizeof (struct nodelist));
      (*nlpp)->next = NULL;
      psh->parsebackquote = oldstyle;

      if (oldstyle) {
            saveprompt = psh->doprompt;
            psh->doprompt = 0;
      }

      n = list(psh, 0);

      if (oldstyle)
            psh->doprompt = saveprompt;
      else {
            if (readtoken(psh) != TRP)
                  synexpect(psh, TRP);
      }

      (*nlpp)->n = n;
        if (oldstyle) {
            /*
             * Start reading from old file again, ignoring any pushed back
             * tokens left from the backquote parsing
             */
                popfile(psh);
            psh->tokpushback = 0;
      }
      while (stackblocksize(psh) <= savelen)
            growstackblock(psh);
      STARTSTACKSTR(psh, out);
      if (str) {
            memcpy(out, str, savelen);
            STADJUST(psh, savelen, out);
            INTOFF;
            ckfree(str);
            str = NULL;
            INTON;
      }
      psh->parsebackquote = savepbq;
      psh->handler = savehandler;
      if (arinest || ISDBLQUOTE())
            USTPUTC(psh, CTLBACKQ | CTLQUOTE, out);
      else
            USTPUTC(psh, CTLBACKQ, out);
      if (oldstyle)
            goto parsebackq_oldreturn;
      else
            goto parsebackq_newreturn;
}

/*
 * Parse an arithmetic expansion (indicate start of one and set state)
 */
parsearith: {

      if (++arinest == 1) {
            prevsyntax = syntax;
            syntax = ARISYNTAX;
            USTPUTC(psh, CTLARI, out);
            if (ISDBLQUOTE())
                  USTPUTC(psh, '"',out);
            else
                  USTPUTC(psh, ' ',out);
      } else {
            /*
             * we collapse embedded arithmetic expansion to
             * parenthesis, which should be equivalent
             */
            USTPUTC(psh, '(', out);
      }
      goto parsearith_return;
}

} /* end of readtoken */



#ifdef mkinit
RESET {
      psh->tokpushback = 0;
      psh->checkkwd = 0;
}
#endif

/*
 * Returns true if the text contains nothing to expand (no dollar signs
 * or backquotes).
 */

STATIC int
noexpand(shinstance *psh, char *text)
{
      char *p;
      char c;

      p = text;
      while ((c = *p++) != '\0') {
            if (c == CTLQUOTEMARK)
                  continue;
            if (c == CTLESC)
                  p++;
            else if (BASESYNTAX[(int)c] == CCTL)
                  return 0;
      }
      return 1;
}


/*
 * Return true if the argument is a legal variable name (a letter or
 * underscore followed by zero or more letters, underscores, and digits).
 */

int
goodname(const char *name)
{
      const char *p;

      p = name;
      if (! is_name(*p))
            return 0;
      while (*++p) {
            if (! is_in_name(*p))
                  return 0;
      }
      return 1;
}


/*
 * Called when an unexpected token is read during the parse.  The argument
 * is the token that is expected, or -1 if more than one type of token can
 * occur at this point.
 */

STATIC void
synexpect(shinstance *psh, int token)
{
      char msg[64];

      if (token >= 0) {
            fmtstr(msg, 64, "%s unexpected (expecting %s)",
                  tokname[psh->lasttoken], tokname[token]);
      } else {
            fmtstr(msg, 64, "%s unexpected", tokname[psh->lasttoken]);
      }
      synerror(psh, msg);
      /* NOTREACHED */
}


STATIC void
synerror(shinstance *psh, const char *msg)
{
      if (psh->commandname)
            outfmt(&psh->errout, "%s: %d: ", psh->commandname, psh->startlinno);
      outfmt(&psh->errout, "Syntax error: %s\n", msg);
      error(psh, (char *)NULL);
      /* NOTREACHED */
}

STATIC const char *
my_basename(const char *argv0, unsigned *lenp)
{
      const char *tmp;

    /* skip the path */
    for (tmp = strpbrk(argv0, "\\/:"); tmp; tmp = strpbrk(argv0, "\\/:"))
        argv0 = tmp + 1;

      if (lenp) {
            /* find the end, ignoring extenions */
            tmp = strrchr(argv0, '.');
            if (!tmp)
                  tmp = strchr(argv0, '\0');
            *lenp = (unsigned)(tmp - argv0);
      }
      return argv0;
}


STATIC void
setprompt(shinstance *psh, int which)
{
      psh->whichprompt = which;

#ifndef SMALL
      if (!el)
#endif
      {
            /* deal with bash prompts */
            const char *prompt = getprompt(psh, NULL);
            if (!strchr(prompt, '\\')) {
                  out2str(psh, prompt);
            } else {
                  while (*prompt) {
                        if (*prompt != '\\') {
                              out2c(psh, *prompt++);
                        } else {
                              prompt++;
                              switch (*prompt++)
                              {
                                    /* simple */
                                    case '$':   out2c(psh, sh_geteuid(psh) ? '$' : '#'); break;
                                    case '\\':  out2c(psh, '\\'); break;
                                    case 'a':   out2c(psh, '\a'); break;
                                    case 'e':   out2c(psh, 033); break;
                                    case 'n':   out2c(psh, '\n'); break;
                                    case 'r':   out2c(psh, '\r'); break;

                                    /* complicated */
                                    case 's': {
                                          unsigned len;
                                          const char *arg0 = my_basename(psh->arg0, &len);
                                          outfmt(psh->out2, "%.*s", len, arg0);
                                          break;
                                    }
                                    case 'v':
                                          outfmt(psh->out2, "%d.%d", KBUILD_VERSION_MAJOR,
                                                   KBUILD_VERSION_MINOR);
                                          break;
                                    case 'V':
                                          outfmt(psh->out2, "%d.%d.%d", KBUILD_VERSION_MAJOR,
                                                   KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
                                          break;
                                          out2str(psh, getpwd(psh, 1) ? getpwd(psh, 1) : "?");
                                          break;
                                    case 'w':
                                    case 'W': {
                                          const char *cwd = getpwd(psh, 1);
                                          const char *home = bltinlookup(psh, "HOME", 1);
                                          size_t home_len = home ? strlen(home) : 0;
                                          if (!cwd) cwd = "?";
                                          if (!strncmp(cwd, home, home_len)
                                            && (    cwd[home_len] == '\0'
                                                || (cwd[home_len] == '/' && prompt[-1] == 'w'))) {
                                                out2c(psh, '~');
                                                if (prompt[-1] == 'w' && cwd[home_len]) {
                                                      out2str(psh, cwd + home_len);
                                                }
                                          } else if (prompt[-1] == 'w') {
                                                out2str(psh, cwd);
                                          } else {
                                                out2str(psh, my_basename(cwd, NULL));
                                          }
                                          break;
                                    }
                                    case '0':
                                    case '1':
                                    case '2':
                                    case '3': {
                                          unsigned int ch = prompt[-1] - '0';
                                          if (isdigit(*prompt)) {
                                                ch *= 8;
                                                ch += *prompt++ - '0';
                                          }
                                          if (isdigit(*prompt)) {
                                                ch *= 8;
                                                ch += *prompt++ - '0';
                                          }
                                          out2c(psh, ch);
                                          break;
                                    }

                                    /* ignore */
                                          break;
                                    case '!':
                                    case '#':
                                    case '@':
                                    case 'A':
                                    case 'h':
                                    case 'H':
                                    case 'j':
                                    case 'l':
                                    case 't':
                                    case 'T':
                                    case 'u':
                                    case '[':
                                          if (strchr(prompt, ']')) {
                                                prompt = strchr(prompt, ']') + 1;
                                          }
                                          break;
                                    case 'D':
                                          if (*prompt == '{' && strchr(prompt, '}')) {
                                                prompt = strchr(prompt, '}') + 1;
                                          }
                                          break;
                              }

                        }
                  }
            }
      }
}

/*
 * called by editline -- any expansions to the prompt
 *    should be added here.
 */
const char *
getprompt(shinstance *psh, void *unused)
{
      switch (psh->whichprompt) {
      case 0:
            return "";
      case 1:
            return ps1val(psh);
      case 2:
            return ps2val(psh);
      default:
            return "<internal prompt error>";
      }
}

Generated by  Doxygen 1.6.0   Back to index