v19i002: A reimplementation of the System V shell, Part02/08

Rich Salz rsalz at uunet.uu.net
Wed May 31 04:44:03 AEST 1989


Submitted-by: ka at june.cs.washington.edu (Kenneth Almquist)
Posting-number: Volume 19, Issue 2
Archive-name: ash/part02

# This is part 2 of ash.  To unpack, feed it into the shell (not csh).
# The ash distribution consists of eight pieces.  Be sure you get them all.
# After you unpack everything, read the file README.

echo extracting adjind.c
cat > adjind.c <<\EOF
/*
 * Adjust indentation.  The first arg is the old indentation amout,
 * the second arg is the new indentation amount, and the remaining
 * args are files to update.  Files are updated in place.  The original
 * version of a file named file.c is moved fo file.c~.
 *
 * Copyright (C) 1989 by Kenneth Almquist.
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose without fee is hereby granted,
 * provided that this copyright and permission notice is retained.
 */


#include <stdio.h>
#include <errno.h>

char tempfile[64];

FILE *ckfopen();

#define is_digit(c)	((unsigned)((c) - '0') <= 9)


main(argc, argv)
      char **argv;
      {
      int oldind, newind;
      double ratio;
      char **ap;

      if (argc < 3)
	    error("Usage: adjind old_indent new_indent file...");
      oldind = number(argv[1]);
      newind = number(argv[2]);
      if (oldind == 0)
	    error("Old indent cannot be zero");
      ratio = (double)newind / oldind;
      sprintf(tempfile, "/tmp/adjind%d", getpid());
      for (ap = argv + 3 ; *ap ; ap++) {
	    dofile(*ap, ratio);
      }
      done(0);
}



done(status) {
      exit(status);
}



dofile(fname, ratio)
      char *fname;
      double ratio;
      {
      register FILE *fp;
      register FILE *temp;
      register int c;
      register indent;
      double findent;
      char buf[1024];

      sprintf(tempfile, "%s.new", fname);
      fp = ckfopen(fname, "r");
      temp = ckfopen(tempfile, "w");
      for (;;) {	/* for each line of input, until EOF */
	    indent = 0;
	    for (;;) {
		  if ((c = getc(fp)) == ' ')
			indent++;
		  else if (c == '\t')
			indent = indent + 8 &~ 07;
		  else
			break;
	    }
	    findent = (double)indent * ratio;
	    indent = findent;
	    if (findent - indent > 0.5)
		  indent++;
	    while (indent >= 8) {
		  putc('\t', temp);
		  indent -= 8;
	    }
	    while (indent > 0) {
		  putc(' ', temp);
		  indent--;
	    }
	    if (c == EOF)
		  break;
	    putc(c, temp);
	    if (c != '\n') {
		  if (fgets(buf, 1024, fp) == NULL)
			break;
		  fputs(buf, temp);
	    }
      }
      fclose(fp);
      if (ferror(temp) || fclose(temp) == EOF)
	    error("Write error");
      sprintf(buf, "%s~", fname);
      movefile(fname, buf);
      movefile(tempfile, fname);
}



/*
 * Rename a file.  We do it with links since the rename system call is
 * not universal.
 */

movefile(old, new)
      char *old, *new;
      {
      int status;

      if ((status = link(old, new)) < 0 && errno == EEXIST) {
	    unlink(new);
	    status = link(old, new);
      }
      if (status < 0)
	    error("link failed");
      if (unlink(old) < 0)
	    perror("unlink failed");
}



FILE *
ckfopen(file, mode)
      char *file;
      char *mode;
      {
      FILE *fp;

      if ((fp = fopen(file, mode)) == NULL) {
	    fprintf(stderr, "Can't open %s\n", file);
	    done(2);
      }
      return fp;
}


int
number(s)
      char *s;
      {
      register char *p;

      for (p = s ; is_digit(*p) ; p++);
      if (p == s || *p != '\0') {
	    fprintf(stderr, "Illegal number: %s\n", s);
	    done(2);
      }
      return atoi(s);
}



error(msg)
      char *msg;
      {
      fprintf(stderr, "%s\n", msg);
      done(2);
}
EOF
if test `wc -c < adjind.c` -ne 3265
then	echo 'adjind.c is the wrong size'
fi
echo extracting builtins
cat > builtins <<\EOF
# This file lists all the builtin commands.  The first column is the name
# of a C routine.  The -j flag, if present, specifies that this command
# is to be excluded from systems without job control.  The rest of the line
# specifies the command name or names used to run the command.  The entry
# for nullcmd, which is run when the user does not specify a command, must
# come first.
#
# Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
# This file is part of ash, which is distributed under the terms specified
# by the Ash General Public License.  See the file named LICENSE.

bltincmd	bltin
#alloccmd	alloc
bgcmd -j	bg
breakcmd	break continue
catfcmd		catf
cdcmd		cd
dotcmd		.
echocmd		echo
evalcmd		eval
execcmd		exec
exitcmd		exit
exportcmd	export readonly
exprcmd		expr test [
fgcmd -j	fg
getoptscmd	getopts
hashcmd		hash
jobidcmd	jobid
jobscmd		jobs
lccmd		lc
linecmd		line
localcmd	local
nlechocmd	nlecho
pwdcmd		pwd
readcmd		read
returncmd	return
setcmd		set
setvarcmd	setvar
shiftcmd	shift
trapcmd		trap
truecmd		: true
umaskcmd	umask
unsetcmd	unset
waitcmd		wait
EOF
if test `wc -c < builtins` -ne 1088
then	echo 'builtins is the wrong size'
fi
echo extracting cd.c
cat > cd.c <<\EOF
/*
 * The cd and pwd commands.
 *
 * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
 * This file is part of ash, which is distributed under the terms specified
 * by the Ash General Public License.  See the file named LICENSE.
 */

#include "shell.h"
#include "var.h"
#include "nodes.h"	/* for jobs.h */
#include "jobs.h"
#include "options.h"
#include "output.h"
#include "memalloc.h"
#include "error.h"
#include "mystring.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "myerrno.h"


#ifdef __STDC__
STATIC int docd(char *, int);
STATIC void updatepwd(char *);
STATIC void getpwd(void);
STATIC char *getcomponent(void);
#else
STATIC int docd();
STATIC void updatepwd();
STATIC void getpwd();
STATIC char *getcomponent();
#endif


char *curdir;			/* current working directory */
STATIC char *cdcomppath;

#if UDIR
extern int didudir;		/* set if /u/logname expanded */
#endif


int
cdcmd(argc, argv)  char **argv; {
      char *dest;
      char *path;
      char *p;
      struct stat statb;
      char *padvance();

      nextopt(nullstr);
      if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
	    error("HOME not set");
      if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
	    path = nullstr;
      while ((p = padvance(&path, dest)) != NULL) {
	    if (stat(p, &statb) >= 0
	     && (statb.st_mode & S_IFMT) == S_IFDIR
	     && docd(p, strcmp(p, dest)) >= 0)
		  return 0;
      }
      error("can't cd to %s", dest);
}


/*
 * Actually do the chdir.  If the name refers to symbolic links, we
 * compute the actual directory name before doing the cd.  In an
 * interactive shell, print the directory name if "print" is nonzero
 * or if the name refers to a symbolic link.  We also print the name
 * if "/u/logname" was expanded in it, since this is similar to a
 * symbolic link.  (The check for this breaks if the user gives the
 * cd command some additional, unused arguments.)
 */

#if SYMLINKS == 0
STATIC int
docd(dest, print)
      char *dest;
      {
#if UDIR
      if (didudir)
	    print = 1;
#endif
      INTOFF;
      if (chdir(dest) < 0) {
	    INTON;
	    return -1;
      }
      updatepwd(dest);
      INTON;
      if (print && iflag)
	    out1fmt("%s\n", stackblock());
      return 0;
}

#else



STATIC int
docd(dest, print)
      char *dest;
      {
      register char *p;
      register char *q;
      char *symlink;
      char *component;
      struct stat statb;
      int first;
      int i;

      TRACE(("docd(\"%s\", %d) called\n", dest, print));
#if UDIR
      if (didudir)
	    print = 1;
#endif

top:
      cdcomppath = dest;
      STARTSTACKSTR(p);
      if (*dest == '/') {
	    STPUTC('/', p);
	    cdcomppath++;
      }
      first = 1;
      while ((q = getcomponent()) != NULL) {
	    if (q[0] == '\0' || q[0] == '.' && q[1] == '\0')
		  continue;
	    if (! first)
		  STPUTC('/', p);
	    first = 0;
	    component = q;
	    while (*q)
		  STPUTC(*q++, p);
	    if (equal(component, ".."))
		  continue;
	    STACKSTRNUL(p);
	    if (lstat(stackblock(), &statb) < 0)
		  error("lstat %s failed", stackblock());
	    if ((statb.st_mode & S_IFMT) != S_IFLNK)
		  continue;

	    /* Hit a symbolic link.  We have to start all over again. */
	    print = 1;
	    STPUTC('\0', p);
	    symlink = grabstackstr(p);
	    i = (int)statb.st_size + 2;		/* 2 for '/' and '\0' */
	    if (cdcomppath != NULL)
		  i += strlen(cdcomppath);
	    p = stalloc(i);
	    if (readlink(symlink, p, (int)statb.st_size) < 0) {
		  error("readlink %s failed", stackblock());
	    }
	    if (cdcomppath != NULL) {
		  p[(int)statb.st_size] = '/';
		  scopy(cdcomppath, p + (int)statb.st_size + 1);
	    } else {
		  p[(int)statb.st_size] = '\0';
	    }
	    if (p[0] != '/') {	/* relative path name */
		  char *r;
		  q = r = symlink;
		  while (*q) {
			if (*q++ == '/')
			      r = q;
		  }
		  *r = '\0';
		  dest = stalloc(strlen(symlink) + strlen(p) + 1);
		  scopy(symlink, dest);
		  strcat(dest, p);
	    } else {
		  dest = p;
	    }
	    goto top;
      }
      STPUTC('\0', p);
      p = grabstackstr(p);
      INTOFF;
      if (chdir(p) < 0) {
	    INTON;
	    return -1;
      }
      updatepwd(p);
      INTON;
      if (print && iflag)
	    out1fmt("%s\n", p);
      return 0;
}
#endif /* SYMLINKS */



/*
 * Get the next component of the path name pointed to by cdcomppath.
 * This routine overwrites the string pointed to by cdcomppath.
 */

STATIC char *
getcomponent() {
      register char *p;
      char *start;

      if ((p = cdcomppath) == NULL)
	    return NULL;
      start = cdcomppath;
      while (*p != '/' && *p != '\0')
	    p++;
      if (*p == '\0') {
	    cdcomppath = NULL;
      } else {
	    *p++ = '\0';
	    cdcomppath = p;
      }
      return start;
}



/*
 * Update curdir (the name of the current directory) in response to a
 * cd command.  We also call hashcd to let the routines in exec.c know
 * that the current directory has changed.
 */

void hashcd();

STATIC void
updatepwd(dir)
      char *dir;
      {
      char *new;
      char *p;

      hashcd();				/* update command hash table */
      cdcomppath = stalloc(strlen(dir) + 1);
      scopy(dir, cdcomppath);
      STARTSTACKSTR(new);
      if (*dir != '/') {
	    if (curdir == NULL)
		  return;
	    p = curdir;
	    while (*p)
		  STPUTC(*p++, new);
	    if (p[-1] == '/')
		  STUNPUTC(new);
      }
      while ((p = getcomponent()) != NULL) {
	    if (equal(p, "..")) {
		  while (new > stackblock() && (STUNPUTC(new), *new) != '/');
	    } else if (*p != '\0' && ! equal(p, ".")) {
		  STPUTC('/', new);
		  while (*p)
			STPUTC(*p++, new);
	    }
      }
      if (new == stackblock())
	    STPUTC('/', new);
      STACKSTRNUL(new);
      if (curdir)
	    ckfree(curdir);
      curdir = savestr(stackblock());
}



int
pwdcmd(argc, argv)  char **argv; {
      getpwd();
      out1str(curdir);
      out1c('\n');
      return 0;
}



/*
 * Run /bin/pwd to find out what the current directory is.  We suppress
 * interrupts throughout most of this, but the user can still break out
 * of it by killing the pwd program.  If we already know the current
 * directory, this routine returns immediately.
 */

#define MAXPWD 256

STATIC void
getpwd() {
      char buf[MAXPWD];
      char *p;
      int i;
      int status;
      struct job *jp;
      int pip[2];

      if (curdir)
	    return;
      INTOFF;
      if (pipe(pip) < 0)
	    error("Pipe call failed");
      jp = makejob((union node *)NULL, 1);
      if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
	    close(pip[0]);
	    if (pip[1] != 1) {
		  close(1);
		  copyfd(pip[1], 1);
		  close(pip[1]);
	    }
	    execl("/bin/pwd", "pwd", (char *)0);
	    error("Cannot exec /bin/pwd");
      }
      close(pip[1]);
      pip[1] = -1;
      p = buf;
      while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
	  || i == -1 && errno == EINTR) {
	    if (i > 0)
		  p += i;
      }
      close(pip[0]);
      pip[0] = -1;
      status = waitforjob(jp);
      if (status != 0)
	    error((char *)0);
      if (i < 0 || p == buf || p[-1] != '\n')
	    error("pwd command failed");
      p[-1] = '\0';
      curdir = savestr(buf);
      INTON;
}
EOF
if test `wc -c < cd.c` -ne 7200
then	echo 'cd.c is the wrong size'
fi
echo extracting mydirent.h
cat > mydirent.h <<\EOF
/*
 * System V directory routines.  The BSD ones are almost the same except
 * that the structure tag "direct" is used instead of "dirent" and opendir
 * is replaced with myopendir (which checks that the file being opened is
 * a directory).  If we don't have the BSD ones, we use our own code which
 * assumes an old style directory format.  This file requires sys/types.h.
 *
 * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
 * This file is part of ash, which is distributed under the terms specified
 * by the Ash General Public License.  See the file named LICENSE.
 */

#if DIRENT		/* System V directory routines available */
#include <dirent.h>
#else
#ifdef BSD		/* 4.2 BSD directory routines available */
#include <sys/dir.h>
#ifdef __STDC__
DIR *myopendir(char *);
#else
DIR *myopendir();
#endif
#define dirent direct
#define opendir myopendir
#else			/* Use our own directory access routines */
#include <sys/dir.h>

struct dirent {				/* data from readdir */
      long d_ino;			/* inode number of entry */
      char d_name[DIRSIZ+1];		/* name of file */	/* non-POSIX */
};

#define DIRBUFENT 64

typedef struct {
      struct dirent dd_entry;		/* directory entry */
      int	    dd_fd;		/* file descriptor */
      int	    dd_nleft;		/* amount of valid data */
      struct direct *dd_loc;		/* location in block */
      struct direct dd_buf[DIRBUFENT];	/* -> directory block */
} DIR;					/* stream data from opendir() */

#ifdef __STDC__
DIR *opendir(char *);
struct dirent *readdir(DIR *);
int closedir(DIR *);
#else
DIR *opendir();
struct dirent *readdir();
int closedir();
#endif

#endif /* BSD */
#endif /* DIRENT */
EOF
if test `wc -c < mydirent.h` -ne 1652
then	echo 'mydirent.h is the wrong size'
fi
echo extracting dirent.c
cat > dirent.c <<\EOF
/*
 * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
 * This file is part of ash, which is distributed under the terms specified
 * by the Ash General Public License.  See the file named LICENSE.
 */

#include "shell.h"	/* definitions for pointer, NULL, DIRENT, and BSD */

#if ! DIRENT

#include "myerrno.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "mydirent.h"

#ifndef S_ISDIR				/* macro to test for directory file */
#define	S_ISDIR(mode)		(((mode) & S_IFMT) == S_IFDIR)
#endif

#ifdef BSD

#ifdef __STDC__
int stat(char *, struct stat *);
#else
int stat();
#endif


/*
 * The BSD opendir routine doesn't check that what is being opened is a
 * directory, so we have to include the check in a wrapper routine.
 */

#undef opendir

DIR *
myopendir(dirname)
      char *dirname;			/* name of directory */
      {
      struct stat statb;

      if (stat(dirname, &statb) != 0 || ! S_ISDIR(statb.st_mode)) {
	    errno = ENOTDIR;
	    return NULL;		/* not a directory */
      }
      return opendir(dirname);
}

#else /* not BSD */

/*
 * Dirent routines for old style file systems.
 */

#ifdef __STDC__
pointer malloc(unsigned);
void free(pointer);
int open(char *, int, ...);
int close(int);
int fstat(int, struct stat *);
#else
pointer malloc();
void free();
int open();
int close();
int fstat();
#endif


DIR *
opendir(dirname)
      char		*dirname;	/* name of directory */
      {
      register DIR	*dirp;		/* -> malloc'ed storage */
      register int	fd;		/* file descriptor for read */
      struct stat	statb;		/* result of fstat() */

#ifdef O_NDELAY
      fd = open(dirname, O_RDONLY|O_NDELAY);
#else
      fd = open(dirname, O_RDONLY);
#endif
      if (fd < 0)
	    return NULL;		/* errno set by open() */

      if (fstat(fd, &statb) != 0 || !S_ISDIR(statb.st_mode)) {
	    (void)close(fd);
	    errno = ENOTDIR;
	    return NULL;		/* not a directory */
      }

      if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
	    (void)close(fd);
	    errno = ENOMEM;
	    return NULL;		/* not enough memory */
      }

      dirp->dd_fd = fd;
      dirp->dd_nleft = 0;		/* refill needed */

      return dirp;
}



int
closedir(dirp)
      register DIR *dirp;		/* stream from opendir() */
      {
      register int fd;

      if (dirp == NULL) {
	    errno = EFAULT;
	    return -1;			/* invalid pointer */
      }

      fd = dirp->dd_fd;
      free((pointer)dirp);
      return close(fd);
}



struct dirent *
readdir(dirp)
      register DIR *dirp;		/* stream from opendir() */
      {
      register struct direct *dp;
      register char *p, *q;
      register int i;

      do {
	    if ((dirp->dd_nleft -= sizeof (struct direct)) < 0) {
		  if ((i = read(dirp->dd_fd,
				(char *)dirp->dd_buf,
				DIRBUFENT*sizeof(struct direct))) <= 0) {
			if (i == 0)
			      errno = 0;	/* unnecessary */
			return NULL;		/* EOF or error */
		  }
		  dirp->dd_loc = dirp->dd_buf;
		  dirp->dd_nleft = i - sizeof (struct direct);
	    }
	    dp = dirp->dd_loc++;
      } while (dp->d_ino == 0);
      dirp->dd_entry.d_ino = dp->d_ino;

      /* now copy the name, nul terminating it */
      p = dp->d_name;
      q = dirp->dd_entry.d_name;
      i = DIRSIZ;
      while (--i >= 0 && *p != '\0')
	    *q++ = *p++;
      *q = '\0';
      return &dirp->dd_entry;
}

#endif /* BSD */
#endif /* DIRENT */
EOF
if test `wc -c < dirent.c` -ne 3356
then	echo 'dirent.c is the wrong size'
fi
echo extracting errmsg.h
cat > errmsg.h <<\EOF
#define E_OPEN 01
#define E_CREAT 02
#define E_EXEC 04

#ifdef __STDC__
char *errmsg(int, int);
#else
char *errmsg();
#endif
EOF
if test `wc -c < errmsg.h` -ne 125
then	echo 'errmsg.h is the wrong size'
fi
echo extracting errmsg.c
cat > errmsg.c <<\EOF
#include "shell.h"
#include "output.h"
#include "errmsg.h"
#include "myerrno.h"


#define ALL (E_OPEN|E_CREAT|E_EXEC)


struct errname {
      short errcode;		/* error number */
      short action;		/* operation which encountered the error */
      char *msg;		/* text describing the error */
};


STATIC const struct errname errormsg[] = {
      EINTR, ALL,	"interrupted",
      EACCES, ALL,	"permission denied",
      EIO, ALL,		"I/O error",
      ENOENT, E_OPEN,	"no such file",
      ENOENT, E_CREAT,	"directory nonexistent",
      ENOENT, E_EXEC,	"not found",
      ENOTDIR, E_OPEN,	"no such file",
      ENOTDIR, E_CREAT,	"directory nonexistent",
      ENOTDIR, E_EXEC,	"not found",
      EISDIR, ALL,	"is a directory",
/*    EMFILE, ALL,	"too many open files", */
      ENFILE, ALL,	"file table overflow",
      ENOSPC, ALL,	"file system full",
#ifdef EDQUOT
      EDQUOT, ALL,	"disk quota exceeded",
#endif
#ifdef ENOSR
      ENOSR, ALL,	"no streams resources",
#endif
      ENXIO, ALL,	"no such device or address",
      EROFS, ALL,	"read-only file system",
      ETXTBSY, ALL,	"text busy",
#ifdef SYSV
      EAGAIN, E_EXEC,	"not enough memory",
#endif
      ENOMEM, ALL,	"not enough memory",
#ifdef ENOLINK
      ENOLINK, ALL,	"remote access failed"
#endif
#ifdef EMULTIHOP
      EMULTIHOP, ALL,	"remote access failed",
#endif
#ifdef ECOMM
      ECOMM, ALL,	"remote access failed",
#endif
#ifdef ESTALE
      ESTALE, ALL,	"remote access failed",
#endif
#ifdef ETIMEDOUT
      ETIMEDOUT, ALL,	"remote access failed",
#endif
#ifdef ELOOP
      ELOOP, ALL,	"symbolic link loop",
#endif
      E2BIG, E_EXEC,	"argument list too long",
#ifdef ELIBACC
      ELIBACC, E_EXEC,	"shared library missing",
#endif
      0, 0,		NULL
};


/*
 * Return a string describing an error.  The returned string may be a
 * pointer to a static buffer that will be overwritten on the next call.
 * Action describes the operation that got the error.
 */

char *
errmsg(e, action) {
      struct errname const *ep;
      static char buf[12];

      for (ep = errormsg ; ep->errcode ; ep++) {
	    if (ep->errcode == e && (ep->action & action) != 0)
		  return ep->msg;
      }
      fmtstr(buf, sizeof buf, "error %d", e);
      return buf;
}
EOF
if test `wc -c < errmsg.c` -ne 2226
then	echo 'errmsg.c is the wrong size'
fi
echo extracting eval.h
cat > eval.h <<\EOF
extern char *commandname;	/* currently executing command */
extern int exitstatus;		/* exit status of last command */
extern struct strlist *cmdenviron;  /* environment for builtin command */


struct backcmd {		/* result of evalbackcmd */
      int fd;			/* file descriptor to read from */
      char *buf;		/* buffer */
      int nleft;		/* number of chars in buffer */
      struct job *jp;		/* job structure for command */
};


#ifdef __STDC__
void evalstring(char *);
void evaltree(union node *, int);
void evalbackcmd(union node *, struct backcmd *);
#else
void evalstring();
void evaltree();
void evalbackcmd();
#endif

/* in_function returns nonzero if we are currently evaluating a function */
#define in_function()	funcnest
extern int funcnest;
EOF
if test `wc -c < eval.h` -ne 755
then	echo 'eval.h is the wrong size'
fi
echo extracting eval.c
cat > eval.c <<\EOF
/*
 * Evaluate a command.
 *
 * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
 * This file is part of ash, which is distributed under the terms specified
 * by the Ash General Public License.  See the file named LICENSE.
 */

#include "shell.h"
#include "nodes.h"
#include "syntax.h"
#include "expand.h"
#include "parser.h"
#include "jobs.h"
#include "eval.h"
#include "builtins.h"
#include "options.h"
#include "exec.h"
#include "redir.h"
#include "input.h"
#include "output.h"
#include "trap.h"
#include "var.h"
#include "memalloc.h"
#include "error.h"
#include "mystring.h"
#include <signal.h>


/* flags in argument to evaltree */
#define EV_EXIT 01		/* exit after evaluating tree */
#define EV_TESTED 02		/* exit status is checked; ignore -e flag */
#define EV_BACKCMD 04		/* command executing within back quotes */


/* reasons for skipping commands (see comment on breakcmd routine) */
#define SKIPBREAK 1
#define SKIPCONT 2
#define SKIPFUNC 3

MKINIT int evalskip;		/* set if we are skipping commands */
STATIC int skipcount;		/* number of levels to skip */
MKINIT int loopnest;		/* current loop nesting level */
int funcnest;			/* depth of function calls */


char *commandname;
struct strlist *cmdenviron;
int exitstatus;			/* exit status of last command */


#ifdef __STDC__
STATIC void evalloop(union node *);
STATIC void evalfor(union node *);
STATIC void evalcase(union node *, int);
STATIC void evalsubshell(union node *, int);
STATIC void expredir(union node *);
STATIC void evalpipe(union node *);
STATIC void evalcommand(union node *, int, struct backcmd *);
STATIC void prehash(union node *);
#else
STATIC void evalloop();
STATIC void evalfor();
STATIC void evalcase();
STATIC void evalsubshell();
STATIC void expredir();
STATIC void evalpipe();
STATIC void evalcommand();
STATIC void prehash();
#endif



/*
 * Called to reset things after an exception.
 */

#ifdef mkinit
INCLUDE "eval.h"

RESET {
      evalskip = 0;
      loopnest = 0;
      funcnest = 0;
}

SHELLPROC {
      exitstatus = 0;
}
#endif



/*
 * The eval builtin.  Do you want clean, straight-forward semantics for
 * your eval command?  If so, read on....
 */

#ifdef ELIGANT
evalcmd(argc, argv)  char **argv; {
      char **ap;

      for (ap = argv + 1 ; *ap ; ap++) {
	    evalstring(*ap);
      }
      return exitstatus;
}
#else

/*
 * If, on the other hand, you prefer downright bogus semantics in the
 * name of compatibility, here it is...
 */

evalcmd(argc, argv)  char **argv; {
      char *p;
      char *concat;
      char **ap;

      if (argc > 1) {
	    p = argv[1];
	    if (argc > 2) {
		  STARTSTACKSTR(concat);
		  ap = argv + 2;
		  for (;;) {
			while (*p)
			      STPUTC(*p++, concat);
			if ((p = *ap++) == NULL)
			      break;
			STPUTC(' ', concat);
		  }
		  STPUTC('\0', concat);
		  p = grabstackstr(concat);
	    }
	    evalstring(p);
      }
      return exitstatus;
}
#endif



/*
 * Execute a command or commands contained in a string.
 */

void
evalstring(s)
      char *s;
      {
      union node *n;
      struct stackmark smark;

      setstackmark(&smark);
      setinputstring(s, 1);
      while ((n = parsecmd(0)) != NEOF) {
	    evaltree(n, 0);
	    popstackmark(&smark);
      }
      popfile();
      popstackmark(&smark);
}



/*
 * Evaluate a parse tree.  The value is left in the global variable
 * exitstatus.
 */

void
evaltree(n, flags)
      union node *n;
      {
      if (n == NULL) {
	    TRACE(("evaltree(NULL) called\n"));
	    return;
      }
      TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type));
      switch (n->type) {
      case NSEMI:
	    evaltree(n->nbinary.ch1, 0);
	    if (evalskip)
		  goto out;
	    evaltree(n->nbinary.ch2, flags);
	    break;
      case NAND:
	    evaltree(n->nbinary.ch1, EV_TESTED);
	    if (evalskip || exitstatus != 0)
		  goto out;
	    evaltree(n->nbinary.ch2, flags);
	    break;
      case NOR:
	    evaltree(n->nbinary.ch1, EV_TESTED);
	    if (evalskip || exitstatus == 0)
		  goto out;
	    evaltree(n->nbinary.ch2, flags);
	    break;
      case NREDIR:
	    expredir(n->nredir.redirect);
	    redirect(n->nredir.redirect, REDIR_PUSH);
	    evaltree(n->nredir.n, flags);
	    popredir();
	    break;
      case NSUBSHELL:
	    evalsubshell(n, flags);
	    break;
      case NBACKGND:
	    evalsubshell(n, flags);
	    break;
      case NIF:
	    evaltree(n->nif.test, EV_TESTED);
	    if (evalskip)
		  goto out;
	    if (exitstatus == 0) {
		  evaltree(n->nif.ifpart, flags);
	    } else if (n->nif.elsepart) {
		  evaltree(n->nif.elsepart, flags);
	    }
	    break;
      case NWHILE:
      case NUNTIL:
	    evalloop(n);
	    break;
      case NFOR:
	    evalfor(n);
	    break;
      case NCASE:
	    evalcase(n, flags);
	    break;
      case NDEFUN:
	    defun(n->narg.text, n->narg.next);
	    exitstatus = 0;
	    break;
      case NPIPE:
	    evalpipe(n);
	    break;
      case NCMD:
	    evalcommand(n, flags, (struct backcmd *)NULL);
	    break;
      default:
	    out1fmt("Node type = %d\n", n->type);
	    flushout(&output);
	    break;
      }
out:
      if (sigpending)
	    dotrap();
      if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
	    exitshell(exitstatus);
}


STATIC void
evalloop(n)
      union node *n;
      {
      int status;

      loopnest++;
      status = 0;
      for (;;) {
	    evaltree(n->nbinary.ch1, EV_TESTED);
	    if (evalskip) {
skipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
			evalskip = 0;
			continue;
		  }
		  if (evalskip == SKIPBREAK && --skipcount <= 0)
			evalskip = 0;
		  break;
	    }
	    if (n->type == NWHILE) {
		  if (exitstatus != 0)
			break;
	    } else {
		  if (exitstatus == 0)
			break;
	    }
	    evaltree(n->nbinary.ch2, 0);
	    status = exitstatus;
	    if (evalskip)
		  goto skipping;
      }
      loopnest--;
      exitstatus = status;
}



STATIC void
evalfor(n)
      union node *n;
      {
      struct arglist arglist;
      union node *argp;
      struct strlist *sp;
      struct stackmark smark;

      setstackmark(&smark);
      arglist.lastp = &arglist.list;
      for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
	    expandarg(argp, &arglist, 1);
	    if (evalskip)
		  goto out;
      }
      *arglist.lastp = NULL;

      exitstatus = 0;
      loopnest++;
      for (sp = arglist.list ; sp ; sp = sp->next) {
	    setvar(n->nfor.var, sp->text, 0);
	    evaltree(n->nfor.body, 0);
	    if (evalskip) {
		  if (evalskip == SKIPCONT && --skipcount <= 0) {
			evalskip = 0;
			continue;
		  }
		  if (evalskip == SKIPBREAK && --skipcount <= 0)
			evalskip = 0;
		  break;
	    }
      }
      loopnest--;
out:
      popstackmark(&smark);
}



STATIC void
evalcase(n, flags)
      union node *n;
      {
      union node *cp;
      union node *patp;
      struct arglist arglist;
      struct stackmark smark;

      setstackmark(&smark);
      arglist.lastp = &arglist.list;
      expandarg(n->ncase.expr, &arglist, 0);
      for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
	    for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
		  if (casematch(patp, arglist.list->text)) {
			if (evalskip == 0) {
			      evaltree(cp->nclist.body, flags);
			}
			goto out;
		  }
	    }
      }
out:
      popstackmark(&smark);
}



/*
 * Kick off a subshell to evaluate a tree.
 */

STATIC void
evalsubshell(n, flags)
      union node *n;
      {
      struct job *jp;
      int backgnd = (n->type == NBACKGND);

      expredir(n->nredir.redirect);
      jp = makejob(n, 1);
      if (forkshell(jp, n, backgnd) == 0) {
	    if (backgnd)
		  flags &=~ EV_TESTED;
	    redirect(n->nredir.redirect, 0);
	    evaltree(n->nredir.n, flags | EV_EXIT);	/* never returns */
      }
      if (! backgnd) {
	    INTOFF;
	    exitstatus = waitforjob(jp);
	    INTON;
      }
}



/*
 * Compute the names of the files in a redirection list.
 */

STATIC void
expredir(n)
      union node *n;
      {
      register union node *redir;

      for (redir = n ; redir ; redir = redir->nfile.next) {
	    if (redir->type == NFROM
	     || redir->type == NTO
	     || redir->type == NAPPEND) {
		  struct arglist fn;
		  fn.lastp = &fn.list;
		  expandarg(redir->nfile.fname, &fn, 0);
		  redir->nfile.expfname = fn.list->text;
	    }
      }
}



/*
 * Evaluate a pipeline.  All the processes in the pipeline are children
 * of the process creating the pipeline.  (This differs from some versions
 * of the shell, which make the last process in a pipeline the parent
 * of all the rest.)
 */

STATIC void
evalpipe(n)
      union node *n;
      {
      struct job *jp;
      struct nodelist *lp;
      int pipelen;
      int prevfd;
      int pip[2];

      TRACE(("evalpipe(0x%x) called\n", (int)n));
      pipelen = 0;
      for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
	    pipelen++;
      INTOFF;
      jp = makejob(n, pipelen);
      prevfd = -1;
      for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
	    prehash(lp->n);
	    pip[1] = -1;
	    if (lp->next) {
		  if (pipe(pip) < 0) {
			close(prevfd);
			error("Pipe call failed");
		  }
	    }
	    if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
		  INTON;
		  if (prevfd > 0) {
			close(0);
			copyfd(prevfd, 0);
			close(prevfd);
		  }
		  if (pip[1] >= 0) {
			close(pip[0]);
			if (pip[1] != 1) {
			      close(1);
			      copyfd(pip[1], 1);
			      close(pip[1]);
			}
		  }
		  evaltree(lp->n, EV_EXIT);
	    }
	    if (prevfd >= 0)
		  close(prevfd);
	    prevfd = pip[0];
	    close(pip[1]);
      }
      INTON;
      if (n->npipe.backgnd == 0) {
	    INTOFF;
	    exitstatus = waitforjob(jp);
	    TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
	    INTON;
      }
}



/*
 * Execute a command inside back quotes.  If it's a builtin command, we
 * want to save its output in a block obtained from malloc.  Otherwise
 * we fork off a subprocess and get the output of the command via a pipe.
 * Should be called with interrupts off.
 */

void
evalbackcmd(n, result)
      union node *n;
      struct backcmd *result;
      {
      int pip[2];
      struct job *jp;
      struct stackmark smark;		/* unnecessary */

      setstackmark(&smark);
      result->fd = -1;
      result->buf = NULL;
      result->nleft = 0;
      result->jp = NULL;
      if (n->type == NCMD) {
	    evalcommand(n, EV_BACKCMD, result);
      } else {
	    if (pipe(pip) < 0)
		  error("Pipe call failed");
	    jp = makejob(n, 1);
	    if (forkshell(jp, n, FORK_NOJOB) == 0) {
		  FORCEINTON;
		  close(pip[0]);
		  if (pip[1] != 1) {
			close(1);
			copyfd(pip[1], 1);
			close(pip[1]);
		  }
		  evaltree(n, EV_EXIT);
	    }
	    close(pip[1]);
	    result->fd = pip[0];
	    result->jp = jp;
      }
      popstackmark(&smark);
      TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
	    result->fd, result->buf, result->nleft, result->jp));
}



/*
 * Execute a simple command.
 */

STATIC void
evalcommand(cmd, flags, backcmd)
      union node *cmd;
      struct backcmd *backcmd;
      {
      struct stackmark smark;
      union node *argp;
      struct arglist arglist;
      struct arglist varlist;
      char **argv;
      int argc;
      char **envp;
      int varflag;
      struct strlist *sp;
      register char *p;
      int mode;
      int pip[2];
      struct cmdentry cmdentry;
      struct job *jp;
      struct jmploc jmploc;
      struct jmploc *volatile savehandler;
      char *volatile savecmdname;
      volatile struct shparam saveparam;
      struct localvar *volatile savelocalvars;
      volatile int e;
      char *lastarg;

      /* First expand the arguments. */
      TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags));
      setstackmark(&smark);
      arglist.lastp = &arglist.list;
      varlist.lastp = &varlist.list;
      varflag = 1;
      for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
	    p = argp->narg.text;
	    if (varflag && is_name(*p)) {
		  do {
			p++;
		  } while (is_in_name(*p));
		  if (*p == '=') {
			expandarg(argp, &varlist, 0);
			continue;
		  }
	    }
	    expandarg(argp, &arglist, 1);
	    varflag = 0;
      }
      *arglist.lastp = NULL;
      *varlist.lastp = NULL;
      expredir(cmd->ncmd.redirect);
      argc = 0;
      for (sp = arglist.list ; sp ; sp = sp->next)
	    argc++;
      argv = stalloc(sizeof (char *) * (argc + 1));
      for (sp = arglist.list ; sp ; sp = sp->next)
	    *argv++ = sp->text;
      *argv = NULL;
      lastarg = NULL;
      if (iflag && funcnest == 0 && argc > 0)
	    lastarg = argv[-1];
      argv -= argc;

      /* Print the command if xflag is set. */
      if (xflag) {
	    outc('+', &errout);
	    for (sp = varlist.list ; sp ; sp = sp->next) {
		  outc(' ', &errout);
		  out2str(sp->text);
	    }
	    for (sp = arglist.list ; sp ; sp = sp->next) {
		  outc(' ', &errout);
		  out2str(sp->text);
	    }
	    outc('\n', &errout);
	    flushout(&errout);
      }

      /* Now locate the command. */
      if (argc == 0) {
	    cmdentry.cmdtype = CMDBUILTIN;
	    cmdentry.u.index = BLTINCMD;
      } else {
	    find_command(argv[0], &cmdentry, 1);
	    if (cmdentry.cmdtype == CMDUNKNOWN) {	/* command not found */
		  exitstatus = 2;
		  flushout(&errout);
		  return;
	    }
	    /* implement the bltin builtin here */
	    if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
		  for (;;) {
			argv++;
			if (--argc == 0)
			      break;
			if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
			      outfmt(&errout, "%s: not found\n", *argv);
			      exitstatus = 2;
			      flushout(&errout);
			      return;
			}
			if (cmdentry.u.index != BLTINCMD)
			      break;
		  }
	    }
      }

      /* Fork off a child process if necessary. */
      if (cmd->ncmd.backgnd
       || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0
       || (flags & EV_BACKCMD) != 0
	 && (cmdentry.cmdtype != CMDBUILTIN
	     || cmdentry.u.index == DOTCMD
	     || cmdentry.u.index == EVALCMD)) {
	    jp = makejob(cmd, 1);
	    mode = cmd->ncmd.backgnd;
	    if (flags & EV_BACKCMD) {
		  mode = FORK_NOJOB;
		  if (pipe(pip) < 0)
			error("Pipe call failed");
	    }
	    if (forkshell(jp, cmd, mode) != 0)
		  goto parent;	/* at end of routine */
	    if (flags & EV_BACKCMD) {
		  FORCEINTON;
		  close(pip[0]);
		  if (pip[1] != 1) {
			close(1);
			copyfd(pip[1], 1);
			close(pip[1]);
		  }
	    }
	    flags |= EV_EXIT;
      }

      /* This is the child process if a fork occurred. */
      /* Execute the command. */
      if (cmdentry.cmdtype == CMDFUNCTION) {
	    trputs("Shell function:  ");  trargs(argv);
	    redirect(cmd->ncmd.redirect, REDIR_PUSH);
	    saveparam = shellparam;
	    shellparam.malloc = 0;
	    shellparam.nparam = argc - 1;
	    shellparam.p = argv + 1;
	    shellparam.optnext = NULL;
	    INTOFF;
	    savelocalvars = localvars;
	    localvars = NULL;
	    INTON;
	    if (setjmp(jmploc.loc)) {
		  if (exception == EXSHELLPROC)
			freeparam((struct shparam *)&saveparam);
		  else {
			freeparam(&shellparam);
			shellparam = saveparam;
		  }
		  poplocalvars();
		  localvars = savelocalvars;
		  handler = savehandler;
		  longjmp(handler->loc, 1);
	    }
	    savehandler = handler;
	    handler = &jmploc;
	    for (sp = varlist.list ; sp ; sp = sp->next)
		  mklocal(sp->text);
	    funcnest++;
	    evaltree(cmdentry.u.func, 0);
	    funcnest--;
	    INTOFF;
	    poplocalvars();
	    localvars = savelocalvars;
	    freeparam(&shellparam);
	    shellparam = saveparam;
	    handler = savehandler;
	    popredir();
	    INTON;
	    if (evalskip == SKIPFUNC) {
		  evalskip = 0;
		  skipcount = 0;
	    }
	    if (flags & EV_EXIT)
		  exitshell(exitstatus);
      } else if (cmdentry.cmdtype == CMDBUILTIN) {
	    trputs("builtin command:  ");  trargs(argv);
	    mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
	    if (flags == EV_BACKCMD) {
		  memout.nleft = 0;
		  memout.nextc = memout.buf;
		  memout.bufsize = 64;
		  mode |= REDIR_BACKQ;
	    }
	    redirect(cmd->ncmd.redirect, mode);
	    savecmdname = commandname;
	    cmdenviron = varlist.list;
	    e = -1;
	    if (setjmp(jmploc.loc)) {
		  e = exception;
		  exitstatus = (e == EXINT)? SIGINT+128 : 2;
		  goto cmddone;
	    }
	    savehandler = handler;
	    handler = &jmploc;
	    commandname = argv[0];
	    argptr = argv + 1;
	    optptr = NULL;			/* initialize nextopt */
	    exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
	    flushall();
cmddone:
	    out1 = &output;
	    out2 = &errout;
	    freestdout();
	    if (e != EXSHELLPROC) {
		  commandname = savecmdname;
		  if (flags & EV_EXIT) {
			exitshell(exitstatus);
		  }
	    }
	    handler = savehandler;
	    if (e != -1) {
		  if (e != EXERROR || cmdentry.u.index == BLTINCMD
				   || cmdentry.u.index == DOTCMD
				   || cmdentry.u.index == EVALCMD
				   || cmdentry.u.index == EXECCMD)
			raise(e);
		  FORCEINTON;
	    }
	    if (cmdentry.u.index != EXECCMD)
		  popredir();
	    if (flags == EV_BACKCMD) {
		  backcmd->buf = memout.buf;
		  backcmd->nleft = memout.nextc - memout.buf;
		  memout.buf = NULL;
	    }
      } else {
	    trputs("normal command:  ");  trargs(argv);
	    clearredir();
	    redirect(cmd->ncmd.redirect, 0);
	    if (varlist.list) {
		  p = stalloc(strlen(pathval()) + 1);
		  scopy(pathval(), p);
	    } else {
		  p = pathval();
	    }
	    for (sp = varlist.list ; sp ; sp = sp->next)
		  setvareq(sp->text, VEXPORT|VSTACK);
	    envp = environment();
	    shellexec(argv, envp, p, cmdentry.u.index);
	    /*NOTREACHED*/
      }
      goto out;

parent:	/* parent process gets here (if we forked) */
      if (mode == 0) {	/* argument to fork */
	    INTOFF;
	    exitstatus = waitforjob(jp);
	    INTON;
      } else if (mode == 2) {
	    backcmd->fd = pip[0];
	    close(pip[1]);
	    backcmd->jp = jp;
      }

out:
      if (lastarg)
	    setvar("_", lastarg, 0);
      popstackmark(&smark);
}



/*
 * Search for a command.  This is called before we fork so that the
 * location of the command will be available in the parent as well as
 * the child.  The check for "goodname" is an overly conservative
 * check that the name will not be subject to expansion.
 */

STATIC void
prehash(n)
      union node *n;
      {
      struct cmdentry entry;

      if (n->type == NCMD && goodname(n->ncmd.args->narg.text))
	    find_command(n->ncmd.args->narg.text, &entry, 0);
}



/*
 * Builtin commands.  Builtin commands whose functions are closely
 * tied to evaluation are implemented here.
 */

/*
 * No command given, or a bltin command with no arguments.  Set the
 * specified variables.
 */

bltincmd(argc, argv)  char **argv; {
      listsetvar(cmdenviron);
      return exitstatus;
}


/*
 * Handle break and continue commands.  Break, continue, and return are
 * all handled by setting the evalskip flag.  The evaluation routines
 * above all check this flag, and if it is set they start skipping
 * commands rather than executing them.  The variable skipcount is
 * the number of loops to break/continue, or the number of function
 * levels to return.  (The latter is always 1.)  It should probably
 * be an error to break out of more loops than exist, but it isn't
 * in the standard shell so we don't make it one here.
 */

breakcmd(argc, argv)  char **argv; {
      int n;

      n = 1;
      if (argc > 1)
	    n = number(argv[1]);
      if (n > loopnest)
	    n = loopnest;
      if (n > 0) {
	    evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
	    skipcount = n;
      }
      return 0;
}


/*
 * The return command.
 */

returncmd(argc, argv)  char **argv; {
      int ret;

      ret = exitstatus;
      if (argc > 1)
	    ret = number(argv[1]);
      if (funcnest) {
	    evalskip = SKIPFUNC;
	    skipcount = 1;
      }
      return ret;
}


truecmd(argc, argv)  char **argv; {
      return 0;
}


execcmd(argc, argv)  char **argv; {
      if (argc > 1) {
	    iflag = 0;		/* exit on error */
	    setinteractive(0);
#if JOBS
	    jflag = 0;
	    setjobctl(0);
#endif
	    shellexec(argv + 1, environment(), pathval(), 0);

      }
      return 0;
}
EOF
if test `wc -c < eval.c` -ne 20104
then	echo 'eval.c is the wrong size'
fi
echo extracting error.h
cat > error.h <<\EOF
/*
 * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
 * This file is part of ash, which is distributed under the terms specified
 * by the Ash General Public License.  See the file named LICENSE.
 */

/*
 * Types of operations (passed to the errmsg routine).
 */

#define E_OPEN 01	/* opening a file */
#define E_CREAT 02	/* creating a file */
#define E_EXEC 04	/* executing a program */


/*
 * We enclose jmp_buf in a structure so that we can declare pointers to
 * jump locations.  The global variable handler contains the location to
 * jump to when an exception occurs, and the global variable exception
 * contains a code identifying the exeception.  To implement nested
 * exception handlers, the user should save the value of handler on entry
 * to an inner scope, set handler to point to a jmploc structure for the
 * inner scope, and restore handler on exit from the scope.
 */

#include <setjmp.h>

struct jmploc {
      jmp_buf loc;
};

extern struct jmploc *handler;
extern int exception;

/* exceptions */
#define EXINT 0		/* SIGINT received */
#define EXERROR 1	/* a generic error */
#define EXSHELLPROC 2	/* execute a shell procedure */


/*
 * These macros allow the user to suspend the handling of interrupt signals
 * over a period of time.  This is similar to SIGHOLD to or sigblock, but
 * much more efficient and portable.  (But hacking the kernel is so much
 * more fun than worrying about efficiency and portability. :-))
 */

extern volatile int suppressint;
extern volatile int intpending;
extern char *commandname;	/* name of command--printed on error */

#define INTOFF suppressint++
#define INTON if (--suppressint == 0 && intpending) onint(); else
#define FORCEINTON {suppressint = 0; if (intpending) onint();}
#define CLEAR_PENDING_INT intpending = 0
#define int_pending() intpending

#ifdef __STDC__
void raise(int);
void onint(void);
void error2(char *, char *);
void error(char *, ...);
char *errmsg(int, int);
#else
void raise();
void onint();
void error2();
void error();
char *errmsg();
#endif


/*
 * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
 * so we use _setjmp instead.
 */

#ifdef BSD
#define setjmp(jmploc)	_setjmp(jmploc)
#define longjmp(jmploc, val)	_longjmp(jmploc, val)
#endif
EOF
if test `wc -c < error.h` -ne 2269
then	echo 'error.h is the wrong size'
fi
echo extracting error.c
cat > error.c <<\EOF
/*
 * Errors and exceptions.
 *
 * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
 * This file is part of ash, which is distributed under the terms specified
 * by the Ash General Public License.  See the file named LICENSE.
 */

#include "shell.h"
#include "main.h"
#include "options.h"
#include "output.h"
#include "error.h"
#include <signal.h>
#ifdef __STDC__
#include "stdarg.h"
#else
#include <varargs.h>	
#endif
#include "myerrno.h"


/*
 * Code to handle exceptions in C.
 */

struct jmploc *handler;
int exception;
volatile int suppressint;
volatile int intpending;
char *commandname;


/*
 * Called to raise an exception.  Since C doesn't include exceptions, we
 * just do a longjmp to the exception handler.  The type of exception is
 * stored in the global variable "exception".
 */

void
raise(e) {
      if (handler == NULL)
	    abort();
      exception = e;
      longjmp(handler->loc, 1);
}


/*
 * Called from trap.c when a SIGINT is received.  (If the user specifies
 * that SIGINT is to be trapped or ignored using the trap builtin, then
 * this routine is not called.)  Suppressint is nonzero when interrupts
 * are held using the INTOFF macro.  The call to _exit is necessary because
 * there is a short period after a fork before the signal handlers are
 * set to the appropriate value for the child.  (The test for iflag is
 * just defensive programming.)
 */

void
onint() {
      if (suppressint) {
	    intpending++;
	    return;
      }
      intpending = 0;
#ifdef BSD
      sigsetmask(0);
#endif
      if (rootshell && iflag)
	    raise(EXINT);
      else
	    _exit(128 + SIGINT);
}



void
error2(a, b)
      char *a, *b;
      {
      error("%s: %s", a, b);
}


/*
 * Error is called to raise the error exception.  If the first argument
 * is not NULL then error prints an error message using printf style
 * formatting.  It then raises the error exception.
 */

#ifdef __STDC__
void
error(char *msg, ...) {
#else
void
error(va_alist)
      va_dcl
      {
      char *msg;
#endif
      va_list ap;

      CLEAR_PENDING_INT;
      INTOFF;
#ifdef __STDC__
      va_start(ap, msg);
#else
      va_start(ap);
      msg = va_arg(ap, char *);
#endif
#ifdef DEBUG
      if (msg)
	    TRACE(("error(\"%s\") pid=%d\n", msg, getpid()));
      else
	    TRACE(("error(NULL) pid=%d\n", getpid()));
#endif
      if (msg) {
	    if (commandname)
		  outfmt(&errout, "%s: ", commandname);
	    doformat(&errout, msg, ap);
	    out2c('\n');
      }
      va_end(ap);
      flushall();
      raise(EXERROR);
}



/*
 * Table of error messages.
 */

struct errname {
      short errcode;		/* error number */
      short action;		/* operation which encountered the error */
      char *msg;		/* text describing the error */
};


#define ALL (E_OPEN|E_CREAT|E_EXEC)

STATIC const struct errname errormsg[] = {
      EINTR, ALL,	"interrupted",
      EACCES, ALL,	"permission denied",
      EIO, ALL,		"I/O error",
      ENOENT, E_OPEN,	"no such file",
      ENOENT, E_CREAT,	"directory nonexistent",
      ENOENT, E_EXEC,	"not found",
      ENOTDIR, E_OPEN,	"no such file",
      ENOTDIR, E_CREAT,	"directory nonexistent",
      ENOTDIR, E_EXEC,	"not found",
      EISDIR, ALL,	"is a directory",
/*    EMFILE, ALL,	"too many open files", */
      ENFILE, ALL,	"file table overflow",
      ENOSPC, ALL,	"file system full",
#ifdef EDQUOT
      EDQUOT, ALL,	"disk quota exceeded",
#endif
#ifdef ENOSR
      ENOSR, ALL,	"no streams resources",
#endif
      ENXIO, ALL,	"no such device or address",
      EROFS, ALL,	"read-only file system",
      ETXTBSY, ALL,	"text busy",
#ifdef SYSV
      EAGAIN, E_EXEC,	"not enough memory",
#endif
      ENOMEM, ALL,	"not enough memory",
#ifdef ENOLINK
      ENOLINK, ALL,	"remote access failed"
#endif
#ifdef EMULTIHOP
      EMULTIHOP, ALL,	"remote access failed",
#endif
#ifdef ECOMM
      ECOMM, ALL,	"remote access failed",
#endif
#ifdef ESTALE
      ESTALE, ALL,	"remote access failed",
#endif
#ifdef ETIMEDOUT
      ETIMEDOUT, ALL,	"remote access failed",
#endif
#ifdef ELOOP
      ELOOP, ALL,	"symbolic link loop",
#endif
      E2BIG, E_EXEC,	"argument list too long",
#ifdef ELIBACC
      ELIBACC, E_EXEC,	"shared library missing",
#endif
      0, 0,		NULL
};


/*
 * Return a string describing an error.  The returned string may be a
 * pointer to a static buffer that will be overwritten on the next call.
 * Action describes the operation that got the error.
 */

char *
errmsg(e, action) {
      struct errname const *ep;
      static char buf[12];

      for (ep = errormsg ; ep->errcode ; ep++) {
	    if (ep->errcode == e && (ep->action & action) != 0)
		  return ep->msg;
      }
      fmtstr(buf, sizeof buf, "error %d", e);
      return buf;
}
EOF
if test `wc -c < error.c` -ne 4722
then	echo 'error.c is the wrong size'
fi
echo extracting exec.h
cat > exec.h <<\EOF
/* values of cmdtype */
#define CMDUNKNOWN -1		/* no entry in table for command */
#define CMDNORMAL 0		/* command is an executable program */
#define CMDBUILTIN 1		/* command is a shell builtin */
#define CMDFUNCTION 2		/* command is a shell function */


struct cmdentry {
      int cmdtype;
      union param {
	    int index;
	    union node *func;
      } u;
};


extern char *pathopt;		/* set by padvance */

#ifdef __STDC__
void shellexec(char **, char **, char *, int);
char *padvance(char **, char *);
void find_command(char *, struct cmdentry *, int);
int find_builtin(char *);
void hashcd(void);
void changepath(char *);
void defun(char *, union node *);
void unsetfunc(char *);
#else
void shellexec();
char *padvance();
void find_command();
int find_builtin();
void hashcd();
void changepath();
void defun();
void unsetfunc();
#endif
EOF
if test `wc -c < exec.h` -ne 846
then	echo 'exec.h is the wrong size'
fi
echo Archive 2 unpacked
exit

-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list