aboutsummaryrefslogtreecommitdiff
path: root/bin/csh
diff options
context:
space:
mode:
Diffstat (limited to 'bin/csh')
-rw-r--r--bin/csh/Makefile40
-rw-r--r--bin/csh/alloc.c542
-rw-r--r--bin/csh/char.c311
-rw-r--r--bin/csh/char.h96
-rw-r--r--bin/csh/const.c157
-rw-r--r--bin/csh/csh.12145
-rw-r--r--bin/csh/csh.c1184
-rw-r--r--bin/csh/csh.h537
-rw-r--r--bin/csh/dir.c912
-rw-r--r--bin/csh/dir.h45
-rw-r--r--bin/csh/dol.c842
-rw-r--r--bin/csh/err.c410
-rw-r--r--bin/csh/exec.c444
-rw-r--r--bin/csh/exp.c708
-rw-r--r--bin/csh/extern.h358
-rw-r--r--bin/csh/file.c675
-rw-r--r--bin/csh/func.c1376
-rw-r--r--bin/csh/glob.c837
-rw-r--r--bin/csh/hist.c206
-rw-r--r--bin/csh/init.c168
-rw-r--r--bin/csh/lex.c1527
-rw-r--r--bin/csh/misc.c413
-rw-r--r--bin/csh/parse.c698
-rw-r--r--bin/csh/pathnames.h41
-rw-r--r--bin/csh/print.c188
-rw-r--r--bin/csh/printf.c311
-rw-r--r--bin/csh/proc.c1295
-rw-r--r--bin/csh/proc.h101
-rw-r--r--bin/csh/sem.c571
-rw-r--r--bin/csh/set.c829
-rw-r--r--bin/csh/str.c416
-rw-r--r--bin/csh/time.c263
32 files changed, 18646 insertions, 0 deletions
diff --git a/bin/csh/Makefile b/bin/csh/Makefile
new file mode 100644
index 000000000000..dc7ee8075d4a
--- /dev/null
+++ b/bin/csh/Makefile
@@ -0,0 +1,40 @@
+# @(#)Makefile 5.24 (Berkeley) 7/1/91
+#
+# C Shell with process control; VM/UNIX VAX Makefile
+# Bill Joy UC Berkeley; Jim Kulp IIASA, Austria
+#
+# To profile, put -DPROF in DEFS and -pg in CFLAGS, and recompile.
+
+PROG= csh
+CFLAGS+=-fstrength-reduce -DFILEC -DNLS -DSHORT_STRINGS -I.
+SRCS= alloc.c char.c const.c csh.c dir.c dol.c err.c exec.c exp.c file.c \
+ func.c glob.c hist.c init.c lex.c misc.c parse.c print.c printf.c \
+ proc.c sem.c set.c str.c time.c
+
+MAN1= csh.1
+MLINKS= csh.1 limit.1 csh.1 alias.1 csh.1 bg.1 csh.1 dirs.1 csh.1 fg.1 \
+ csh.1 foreach.1 csh.1 history.1 csh.1 jobs.1 csh.1 popd.1 \
+ csh.1 pushd.1 csh.1 rehash.1 csh.1 repeat.1 csh.1 suspend.1 \
+ csh.1 stop.1 csh.1 source.1
+CLEANFILES+=err.h const.h
+
+const.h: err.h
+
+err.h: err.c
+ @rm -f $@
+ @echo '/* Do not edit this file, make creates it. */' > $@
+ @echo '#ifndef _h_sh_err' >> $@
+ @echo '#define _h_sh_err' >> $@
+ egrep 'ERR_' ${.CURDIR}/$*.c | egrep '^#define' >> $@
+ @echo '#endif /* _h_sh_err */' >> $@
+
+const.h: const.c
+ @rm -f $@
+ @echo '/* Do not edit this file, make creates it. */' > $@
+ ${CC} -E ${CFLAGS} ${.CURDIR}/$*.c | egrep 'Char STR' | \
+ sed -e 's/Char \([a-zA-Z0-9_]*\)\(.*\)/extern Char \1[];/' | \
+ sort >> $@
+
+.depend alloc.o: const.h err.h
+
+.include <bsd.prog.mk>
diff --git a/bin/csh/alloc.c b/bin/csh/alloc.c
new file mode 100644
index 000000000000..b7c541bc0696
--- /dev/null
+++ b/bin/csh/alloc.c
@@ -0,0 +1,542 @@
+/*-
+ * Copyright (c) 1983, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)alloc.c 5.8 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+/*
+ * tc.alloc.c from malloc.c (Caltech) 2/21/82
+ * Chris Kingsley, kingsley@cit-20.
+ *
+ * This is a very fast storage allocator. It allocates blocks of a small
+ * number of different sizes, and keeps free lists of each size. Blocks that
+ * don't exactly fit are passed up to the next larger size. In this
+ * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long.
+ * This is designed for use in a program that uses vast quantities of memory,
+ * but bombs when it runs out.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+char *memtop = NULL; /* PWP: top of current memory */
+char *membot = NULL; /* PWP: bottom of allocatable memory */
+
+#ifndef SYSMALLOC
+
+#undef RCHECK
+#undef DEBUG
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+
+/*
+ * The overhead on a block is at least 4 bytes. When free, this space
+ * contains a pointer to the next free block, and the bottom two bits must
+ * be zero. When in use, the first byte is set to MAGIC, and the second
+ * byte is the size index. The remaining bytes are for alignment.
+ * If range checking is enabled and the size of the block fits
+ * in two bytes, then the top two bytes hold the size of the requested block
+ * plus the range checking words, and the header word MINUS ONE.
+ */
+
+#define ROUNDUP 7
+
+#define ALIGN(a) (((a) + ROUNDUP) & ~ROUNDUP)
+
+union overhead {
+ union overhead *ov_next; /* when free */
+ struct {
+ u_char ovu_magic; /* magic number */
+ u_char ovu_index; /* bucket # */
+#ifdef RCHECK
+ u_short ovu_size; /* actual block size */
+ u_int ovu_rmagic; /* range magic number */
+#endif
+ } ovu;
+#define ov_magic ovu.ovu_magic
+#define ov_index ovu.ovu_index
+#define ov_size ovu.ovu_size
+#define ov_rmagic ovu.ovu_rmagic
+};
+
+#define MAGIC 0xfd /* magic # on accounting info */
+#define RMAGIC 0x55555555 /* magic # on range info */
+#ifdef RCHECK
+#define RSLOP sizeof (u_int)
+#else
+#define RSLOP 0
+#endif
+
+/*
+ * nextf[i] is the pointer to the next free block of size 2^(i+3). The
+ * smallest allocatable block is 8 bytes. The overhead information
+ * precedes the data area returned to the user.
+ */
+#define NBUCKETS 30
+static union overhead *nextf[NBUCKETS];
+
+static int findbucket __P((union overhead *, int));
+static void morecore __P((int));
+
+/*
+ * nmalloc[i] is the difference between the number of mallocs and frees
+ * for a given block size.
+ */
+static u_int nmalloc[NBUCKETS];
+
+
+#ifdef DEBUG
+#define CHECK(a, str, p) \
+ if (a) { \
+ xprintf(str, p); \
+ xprintf("memtop = %lx membot = %lx.\n", memtop, membot); \
+ abort(); \
+ } \
+ else
+#else
+#define CHECK(a, str, p) \
+ if (a) { \
+ xprintf(str, p); \
+ xprintf("memtop = %lx membot = %lx.\n", memtop, membot); \
+ return; \
+ } \
+ else
+#endif
+
+ptr_t
+malloc(nbytes)
+ register size_t nbytes;
+{
+#ifndef lint
+ register union overhead *p;
+ register int bucket = 0;
+ register unsigned shiftr;
+
+ /*
+ * Convert amount of memory requested into closest block size stored in
+ * hash buckets which satisfies request. Account for space used per block
+ * for accounting.
+ */
+ nbytes = ALIGN(ALIGN(sizeof(union overhead)) + nbytes + RSLOP);
+ shiftr = (nbytes - 1) >> 2;
+
+ /* apart from this loop, this is O(1) */
+ while (shiftr >>= 1)
+ bucket++;
+ /*
+ * If nothing in hash bucket right now, request more memory from the
+ * system.
+ */
+ if (nextf[bucket] == NULL)
+ morecore(bucket);
+ if ((p = (union overhead *) nextf[bucket]) == NULL) {
+ child++;
+#ifndef DEBUG
+ stderror(ERR_NOMEM);
+#else
+ showall();
+ xprintf("nbytes=%d: Out of memory\n", nbytes);
+ abort();
+#endif
+ /* fool lint */
+ return ((ptr_t) 0);
+ }
+ /* remove from linked list */
+ nextf[bucket] = nextf[bucket]->ov_next;
+ p->ov_magic = MAGIC;
+ p->ov_index = bucket;
+ nmalloc[bucket]++;
+#ifdef RCHECK
+ /*
+ * Record allocated size of block and bound space with magic numbers.
+ */
+ if (nbytes <= 0x10000)
+ p->ov_size = nbytes - 1;
+ p->ov_rmagic = RMAGIC;
+ *((u_int *) (((caddr_t) p) + nbytes - RSLOP)) = RMAGIC;
+#endif
+ return ((ptr_t) (((caddr_t) p) + ALIGN(sizeof(union overhead))));
+#else
+ if (nbytes)
+ return ((ptr_t) 0);
+ else
+ return ((ptr_t) 0);
+#endif /* !lint */
+}
+
+#ifndef lint
+/*
+ * Allocate more memory to the indicated bucket.
+ */
+static void
+morecore(bucket)
+ register int bucket;
+{
+ register union overhead *op;
+ register int rnu; /* 2^rnu bytes will be requested */
+ register int nblks; /* become nblks blocks of the desired size */
+ register int siz;
+
+ if (nextf[bucket])
+ return;
+ /*
+ * Insure memory is allocated on a page boundary. Should make getpageize
+ * call?
+ */
+ op = (union overhead *) sbrk(0);
+ memtop = (char *) op;
+ if (membot == NULL)
+ membot = memtop;
+ if ((int) op & 0x3ff) {
+ memtop = (char *) sbrk(1024 - ((int) op & 0x3ff));
+ memtop += 1024 - ((int) op & 0x3ff);
+ }
+
+ /* take 2k unless the block is bigger than that */
+ rnu = (bucket <= 8) ? 11 : bucket + 3;
+ nblks = 1 << (rnu - (bucket + 3)); /* how many blocks to get */
+ if (rnu < bucket)
+ rnu = bucket;
+ memtop = (char *) sbrk(1 << rnu); /* PWP */
+ op = (union overhead *) memtop;
+ memtop += 1 << rnu;
+ /* no more room! */
+ if ((int) op == -1)
+ return;
+ /*
+ * Round up to minimum allocation size boundary and deduct from block count
+ * to reflect.
+ */
+ if (((u_int) op) & ROUNDUP) {
+ op = (union overhead *) (((u_int) op + (ROUNDUP + 1)) & ~ROUNDUP);
+ nblks--;
+ }
+ /*
+ * Add new memory allocated to that on free list for this hash bucket.
+ */
+ nextf[bucket] = op;
+ siz = 1 << (bucket + 3);
+ while (--nblks > 0) {
+ op->ov_next = (union overhead *) (((caddr_t) op) + siz);
+ op = (union overhead *) (((caddr_t) op) + siz);
+ }
+}
+
+#endif
+
+#ifdef sun
+int
+#else
+void
+#endif
+free(cp)
+ ptr_t cp;
+{
+#ifndef lint
+ register int size;
+ register union overhead *op;
+
+ if (cp == NULL)
+ return;
+ CHECK(!memtop || !membot, "free(%lx) called before any allocations.", cp);
+ CHECK(cp > (ptr_t) memtop, "free(%lx) above top of memory.", cp);
+ CHECK(cp < (ptr_t) membot, "free(%lx) above top of memory.", cp);
+ op = (union overhead *) (((caddr_t) cp) - ALIGN(sizeof(union overhead)));
+ CHECK(op->ov_magic != MAGIC, "free(%lx) bad block.", cp);
+
+#ifdef RCHECK
+ if (op->ov_index <= 13)
+ CHECK(*(u_int *) ((caddr_t) op + op->ov_size + 1 - RSLOP) != RMAGIC,
+ "free(%lx) bad range check.", cp);
+#endif
+ CHECK(op->ov_index >= NBUCKETS, "free(%lx) bad block index.", cp);
+ size = op->ov_index;
+ op->ov_next = nextf[size];
+ nextf[size] = op;
+
+ nmalloc[size]--;
+
+#else
+ if (cp == NULL)
+ return;
+#endif
+}
+
+ptr_t
+calloc(i, j)
+ size_t i, j;
+{
+#ifndef lint
+ register char *cp, *scp;
+
+ i *= j;
+ scp = cp = (char *) xmalloc((size_t) i);
+ if (i != 0)
+ do
+ *cp++ = 0;
+ while (--i);
+
+ return (scp);
+#else
+ if (i && j)
+ return ((ptr_t) 0);
+ else
+ return ((ptr_t) 0);
+#endif
+}
+
+/*
+ * When a program attempts "storage compaction" as mentioned in the
+ * old malloc man page, it realloc's an already freed block. Usually
+ * this is the last block it freed; occasionally it might be farther
+ * back. We have to search all the free lists for the block in order
+ * to determine its bucket: 1st we make one pass thru the lists
+ * checking only the first block in each; if that fails we search
+ * ``realloc_srchlen'' blocks in each list for a match (the variable
+ * is extern so the caller can modify it). If that fails we just copy
+ * however many bytes was given to realloc() and hope it's not huge.
+ */
+#ifndef lint
+int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */
+
+#endif /* lint */
+
+ptr_t
+realloc(cp, nbytes)
+ ptr_t cp;
+ size_t nbytes;
+{
+#ifndef lint
+ register u_int onb;
+ union overhead *op;
+ char *res;
+ register int i;
+ int was_alloced = 0;
+
+ if (cp == NULL)
+ return (malloc(nbytes));
+ op = (union overhead *) (((caddr_t) cp) - ALIGN(sizeof(union overhead)));
+ if (op->ov_magic == MAGIC) {
+ was_alloced++;
+ i = op->ov_index;
+ }
+ else
+ /*
+ * Already free, doing "compaction".
+ *
+ * Search for the old block of memory on the free list. First, check the
+ * most common case (last element free'd), then (this failing) the last
+ * ``realloc_srchlen'' items free'd. If all lookups fail, then assume
+ * the size of the memory block being realloc'd is the smallest
+ * possible.
+ */
+ if ((i = findbucket(op, 1)) < 0 &&
+ (i = findbucket(op, realloc_srchlen)) < 0)
+ i = 0;
+
+ onb = ALIGN(nbytes + ALIGN(sizeof(union overhead)) + RSLOP);
+
+ /* avoid the copy if same size block */
+ if (was_alloced && (onb < (1 << (i + 3))) && (onb >= (1 << (i + 2))))
+ return ((ptr_t) cp);
+ if ((res = malloc(nbytes)) == NULL)
+ return ((ptr_t) 0);
+ if (cp != res) /* common optimization */
+ bcopy(cp, res, nbytes);
+ if (was_alloced)
+ free(cp);
+ return (res);
+#else
+ if (cp && nbytes)
+ return ((ptr_t) 0);
+ else
+ return ((ptr_t) 0);
+#endif /* !lint */
+}
+
+
+
+#ifndef lint
+/*
+ * Search ``srchlen'' elements of each free list for a block whose
+ * header starts at ``freep''. If srchlen is -1 search the whole list.
+ * Return bucket number, or -1 if not found.
+ */
+static int
+findbucket(freep, srchlen)
+ union overhead *freep;
+ int srchlen;
+{
+ register union overhead *p;
+ register int i, j;
+
+ for (i = 0; i < NBUCKETS; i++) {
+ j = 0;
+ for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
+ if (p == freep)
+ return (i);
+ j++;
+ }
+ }
+ return (-1);
+}
+
+#endif
+
+
+#else /* SYSMALLOC */
+
+/**
+ ** ``Protected versions'' of malloc, realloc, calloc, and free
+ **
+ ** On many systems:
+ **
+ ** 1. malloc(0) is bad
+ ** 2. free(0) is bad
+ ** 3. realloc(0, n) is bad
+ ** 4. realloc(n, 0) is bad
+ **
+ ** Also we call our error routine if we run out of memory.
+ **/
+char *
+Malloc(n)
+ size_t n;
+{
+ ptr_t ptr;
+
+ n = n ? n : 1;
+
+ if ((ptr = malloc(n)) == (ptr_t) 0) {
+ child++;
+ stderror(ERR_NOMEM);
+ }
+ return ((char *) ptr);
+}
+
+char *
+Realloc(p, n)
+ ptr_t p;
+ size_t n;
+{
+ ptr_t ptr;
+
+ n = n ? n : 1;
+ if ((ptr = (p ? realloc(p, n) : malloc(n))) == (ptr_t) 0) {
+ child++;
+ stderror(ERR_NOMEM);
+ }
+ return ((char *) ptr);
+}
+
+char *
+Calloc(s, n)
+ size_t s, n;
+{
+ char *sptr;
+ ptr_t ptr;
+
+ n *= s;
+ n = n ? n : 1;
+ if ((ptr = malloc(n)) == (ptr_t) 0) {
+ child++;
+ stderror(ERR_NOMEM);
+ }
+
+ sptr = (char *) ptr;
+ if (n != 0)
+ do
+ *sptr++ = 0;
+ while (--n);
+
+ return ((char *) ptr);
+}
+
+void
+Free(p)
+ ptr_t p;
+{
+ if (p)
+ free(p);
+}
+
+#endif /* SYSMALLOC */
+
+/*
+ * mstats - print out statistics about malloc
+ *
+ * Prints two lines of numbers, one showing the length of the free list
+ * for each size category, the second showing the number of mallocs -
+ * frees for each size category.
+ */
+void
+showall()
+{
+#ifndef SYSMALLOC
+ register int i, j;
+ register union overhead *p;
+ int totfree = 0, totused = 0;
+
+ xprintf("csh current memory allocation:\nfree:\t");
+ for (i = 0; i < NBUCKETS; i++) {
+ for (j = 0, p = nextf[i]; p; p = p->ov_next, j++);
+ xprintf(" %4d", j);
+ totfree += j * (1 << (i + 3));
+ }
+ xprintf("\nused:\t");
+ for (i = 0; i < NBUCKETS; i++) {
+ xprintf(" %4d", nmalloc[i]);
+ totused += nmalloc[i] * (1 << (i + 3));
+ }
+ xprintf("\n\tTotal in use: %d, total free: %d\n",
+ totused, totfree);
+ xprintf("\tAllocated memory from 0x%lx to 0x%lx. Real top at 0x%lx\n",
+ membot, memtop, (char *) sbrk(0));
+#else
+ xprintf("Allocated memory from 0x%lx to 0x%lx (%ld).\n",
+ membot, memtop = (char *) sbrk(0), memtop - membot);
+#endif /* SYSMALLOC */
+}
diff --git a/bin/csh/char.c b/bin/csh/char.c
new file mode 100644
index 000000000000..f552a28684a5
--- /dev/null
+++ b/bin/csh/char.c
@@ -0,0 +1,311 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)char.c 5.6 (Berkeley) 6/7/91";
+#endif /* not lint */
+
+#include "char.h"
+
+unsigned short _cmap[256] = {
+/* nul soh stx etx */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* eot enq ack bel */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* bs ht nl vt */
+ _CTR, _CTR|_SP|_META, _CTR|_NL|_META, _CTR,
+
+/* np cr so si */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* dle dc1 dc2 dc3 */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* dc4 nak syn etb */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* can em sub esc */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* fs gs rs us */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* sp ! " # */
+ _SP|_META, 0, _Q, _META,
+
+/* $ % & ' */
+ _DOL, 0, _META|_CMD, _Q,
+
+/* ( ) * + */
+ _META|_CMD, _META, _GLOB, 0,
+
+/* , - . / */
+ 0, 0, 0, 0,
+
+/* 0 1 2 3 */
+ _DIG|_XD, _DIG|_XD, _DIG|_XD, _DIG|_XD,
+
+/* 4 5 6 7 */
+ _DIG|_XD, _DIG|_XD, _DIG|_XD, _DIG|_XD,
+
+/* 8 9 : ; */
+ _DIG|_XD, _DIG|_XD, 0, _META|_CMD,
+
+/* < = > ? */
+ _META, 0, _META, _GLOB,
+
+/* @ A B C */
+ 0, _LET|_UP|_XD, _LET|_UP|_XD, _LET|_UP|_XD,
+
+/* D E F G */
+ _LET|_UP|_XD, _LET|_UP|_XD, _LET|_UP|_XD, _LET|_UP,
+
+/* H I J K */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* L M N O */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* P Q R S */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* T U V W */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* X Y Z [ */
+ _LET|_UP, _LET|_UP, _LET|_UP, _GLOB,
+
+/* \ ] ^ _ */
+ _ESC, 0, 0, 0,
+
+/* ` a b c */
+ _Q1|_GLOB|_CMD|_META, _LET|_LOW|_XD, _LET|_LOW|_XD, _LET|_LOW|_XD,
+
+/* d e f g */
+ _LET|_LOW|_XD, _LET|_LOW|_XD, _LET|_LOW|_XD, _LET|_LOW,
+
+/* h i j k */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* l m n o */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* p q r s */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* t u v w */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* x y z { */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _GLOB,
+
+/* | } ~ del */
+ _META|_CMD, 0, 0, _CTR,
+
+#if defined(SHORT_STRINGS) && !defined(KANJI)
+/****************************************************************/
+/* 128 - 255 The below is supposedly ISO 8859/1 */
+/****************************************************************/
+/* (undef) (undef) (undef) (undef) */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* (undef) (undef) (undef) (undef) */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* (undef) (undef) (undef) (undef) */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* (undef) (undef) (undef) (undef) */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* (undef) (undef) (undef) (undef) */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* (undef) (undef) (undef) (undef) */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* (undef) (undef) (undef) (undef) */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* (undef) (undef) (undef) (undef) */
+ _CTR, _CTR, _CTR, _CTR,
+
+/* nobreakspace exclamdown cent sterling */
+ _SP, 0, 0, 0,
+
+/* currency yen brokenbar section */
+ 0, 0, 0, 0,
+
+/* diaeresis copyright ordfeminine guillemotleft */
+ 0, 0, 0, 0,
+
+/* notsign hyphen registered macron */
+ 0, 0, 0, 0,
+
+/* degree plusminus twosuperior threesuperior */
+ 0, 0, 0, 0,
+
+/* acute mu paragraph periodcentered */
+ 0, 0, 0, 0,
+
+/* cedilla onesuperior masculine guillemotright */
+ 0, 0, 0, 0,
+
+/* onequarter onehalf threequarters questiondown */
+ 0, 0, 0, 0,
+
+/* Agrave Aacute Acircumflex Atilde */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* Adiaeresis Aring AE Ccedilla */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* Egrave Eacute Ecircumflex Ediaeresis */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* Igrave Iacute Icircumflex Idiaeresis */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* ETH Ntilde Ograve Oacute */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* Ocircumflex Otilde Odiaeresis multiply */
+ _LET|_UP, _LET|_UP, _LET|_UP, 0,
+
+/* Ooblique Ugrave Uacute Ucircumflex */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP,
+
+/* Udiaeresis Yacute THORN ssharp */
+ _LET|_UP, _LET|_UP, _LET|_UP, _LET|_LOW,
+
+/* agrave aacute acircumflex atilde */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* adiaeresis aring ae ccedilla */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* egrave eacute ecircumflex ediaeresis */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* igrave iacute icircumflex idiaeresis */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* eth ntilde ograve oacute */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* ocircumflex otilde odiaeresis division */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, 0,
+
+/* oslash ugrave uacute ucircumflex */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+
+/* udiaeresis yacute thorn ydiaeresis */
+ _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW,
+#endif /* SHORT_STRINGS && !KANJI */
+};
+
+#ifndef NLS
+/* _cmap_lower, _cmap_upper for ISO 8859/1 */
+
+unsigned char _cmap_lower[256] = {
+ 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
+ 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
+ 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
+ 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
+ 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
+ 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
+ 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
+ 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
+ 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
+ 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
+ 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
+ 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
+ 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
+ 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
+ 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0327,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
+};
+
+unsigned char _cmap_upper[256] = {
+ 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
+ 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
+ 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
+ 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
+ 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
+ 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
+ 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
+ 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
+ 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0367,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0377,
+};
+#endif /* NLS */
diff --git a/bin/csh/char.h b/bin/csh/char.h
new file mode 100644
index 000000000000..297a6ee8e1e2
--- /dev/null
+++ b/bin/csh/char.h
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * @(#)char.h 5.6 (Berkeley) 6/4/91
+ */
+
+#include <ctype.h>
+
+extern unsigned short _cmap[];
+
+#ifndef NLS
+extern unsigned char _cmap_lower[], _cmap_upper[];
+
+#endif
+
+#define _Q 0x0001 /* '" */
+#define _Q1 0x0002 /* ` */
+#define _SP 0x0004 /* space and tab */
+#define _NL 0x0008 /* \n */
+#define _META 0x0010 /* lex meta characters, sp #'`";&<>()|\t\n */
+#define _GLOB 0x0020 /* glob characters, *?{[` */
+#define _ESC 0x0040 /* \ */
+#define _DOL 0x0080 /* $ */
+#define _DIG 0x0100 /* 0-9 */
+#define _LET 0x0200 /* a-z, A-Z, _ */
+#define _UP 0x0400 /* A-Z */
+#define _LOW 0x0800 /* a-z */
+#define _XD 0x1000 /* 0-9, a-f, A-F */
+#define _CMD 0x2000 /* lex end of command chars, ;&(|` */
+#define _CTR 0x4000 /* control */
+
+#define cmap(c, bits) \
+ (((c) & QUOTE) ? 0 : (_cmap[(unsigned char)(c)] & (bits)))
+
+#define isglob(c) cmap(c, _GLOB)
+#define isspc(c) cmap(c, _SP)
+#define ismeta(c) cmap(c, _META)
+#define iscmdmeta(c) cmap(c, _CMD)
+#define letter(c) (((c) & QUOTE) ? 0 : \
+ (isalpha((unsigned char) (c)) || (c) == '_'))
+#define alnum(c) (((c) & QUOTE) ? 0 : \
+ (isalnum((unsigned char) (c)) || (c) == '_'))
+#ifdef NLS
+#define Isspace(c) (((c) & QUOTE) ? 0 : isspace((unsigned char) (c)))
+#define Isdigit(c) (((c) & QUOTE) ? 0 : isdigit((unsigned char) (c)))
+#define Isalpha(c) (((c) & QUOTE) ? 0 : isalpha((unsigned char) (c)))
+#define Islower(c) (((c) & QUOTE) ? 0 : islower((unsigned char) (c)))
+#define Isupper(c) (((c) & QUOTE) ? 0 : isupper((unsigned char) (c)))
+#define Tolower(c) (((c) & QUOTE) ? 0 : tolower((unsigned char) (c)))
+#define Toupper(c) (((c) & QUOTE) ? 0 : toupper((unsigned char) (c)))
+#define Isxdigit(c) (((c) & QUOTE) ? 0 : isxdigit((unsigned char) (c)))
+#define Isalnum(c) (((c) & QUOTE) ? 0 : isalnum((unsigned char) (c)))
+#define Iscntrl(c) (((c) & QUOTE) ? 0 : iscntrl((unsigned char) (c)))
+#define Isprint(c) (((c) & QUOTE) ? 0 : isprint((unsigned char) (c)))
+#else
+#define Isspace(c) cmap(c, _SP|_NL)
+#define Isdigit(c) cmap(c, _DIG)
+#define Isalpha(c) (cmap(c,_LET) && !(((c) & META) && AsciiOnly))
+#define Islower(c) (cmap(c,_LOW) && !(((c) & META) && AsciiOnly))
+#define Isupper(c) (cmap(c, _UP) && !(((c) & META) && AsciiOnly))
+#define Tolower(c) (_cmap_lower[(unsigned char)(c)])
+#define Toupper(c) (_cmap_upper[(unsigned char)(c)])
+#define Isxdigit(c) cmap(c, _XD)
+#define Isalnum(c) (cmap(c, _DIG|_LET) && !(((c) & META) && AsciiOnly))
+#define Iscntrl(c) (cmap(c,_CTR) && !(((c) & META) && AsciiOnly))
+#define Isprint(c) (!cmap(c,_CTR) && !(((c) & META) && AsciiOnly))
+#endif
diff --git a/bin/csh/const.c b/bin/csh/const.c
new file mode 100644
index 000000000000..66893e46b708
--- /dev/null
+++ b/bin/csh/const.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)const.c 5.2 (Berkeley) 6/7/91";
+#endif /* not lint */
+
+/*
+ * tc.const.c: String constants for csh.
+ */
+
+#include "csh.h"
+
+Char STR0[] = { '0', '\0' };
+Char STR1[] = { '1', '\0' };
+Char STRHOME[] = { 'H', 'O', 'M', 'E', '\0' };
+Char STRLANG[] = { 'L', 'A', 'N', 'G', '\0' };
+Char STRLC_CTYPE[] = { 'L', 'C', '_', 'C', 'T', 'Y', 'P', 'E' ,'\0' };
+Char STRLOGNAME[] = { 'L', 'O', 'G', 'N', 'A', 'M', 'E', '\0' };
+Char STRLbrace[] = { '{', '\0' };
+Char STRLparen[] = { '(', '\0' };
+Char STRLparensp[] = { '(', ' ', '\0' };
+Char STRNULL[] = { '\0' };
+Char STRPATH[] = { 'P', 'A', 'T', 'H', '\0' };
+Char STRPWD[] = { 'P', 'W', 'D', '\0' };
+Char STRQNULL[] = { '\0' | QUOTE, '\0' };
+Char STRRbrace[] = { '}', '\0' };
+Char STRspRparen[] = { ' ', ')', '\0' };
+Char STRTERM[] = { 'T', 'E', 'R', 'M', '\0' };
+Char STRUSER[] = { 'U', 'S', 'E', 'R', '\0' };
+Char STRalias[] = { 'a', 'l', 'i', 'a', 's', '\0' };
+Char STRampm[] = { 'a', 'm', 'p', 'm', '\0' };
+Char STRand[] = { '&', '\0' };
+Char STRand2[] = { '&', '&', '\0' };
+Char STRaout[] = { 'a', '.', 'o', 'u', 't', '\0' };
+Char STRargv[] = { 'a', 'r', 'g', 'v', '\0' };
+Char STRbang[] = { '!', '\0' };
+Char STRcaret[] = { '^', '\0' };
+Char STRcdpath[] = { 'c', 'd', 'p', 'a', 't', 'h', '\0' };
+Char STRcent2[] = { '%', '%', '\0' };
+Char STRcenthash[] = { '%', '#', '\0' };
+Char STRcentplus[] = { '%', '+', '\0' };
+Char STRcentminus[] = { '%', '-', '\0' };
+Char STRchase_symlinks[] = { 'c', 'h', 'a', 's', 'e', '_', 's', 'y', 'm', 'l',
+ 'i', 'n', 'k', 's', '\0' };
+Char STRchild[] = { 'c', 'h', 'i', 'l', 'd', '\0' };
+Char STRcolon[] = { ':', '\0' };
+Char STRcwd[] = { 'c', 'w', 'd', '\0' };
+Char STRdefault[] = { 'd', 'e', 'f', 'a', 'u', 'l', 't', '\0' };
+Char STRdot[] = { '.', '\0' };
+Char STRdotdotsl[] = { '.', '.', '/', '\0' };
+Char STRdotsl[] = { '.', '/', '\0' };
+Char STRecho[] = { 'e', 'c', 'h', 'o', '\0' };
+Char STRequal[] = { '=', '\0' };
+Char STRfakecom[] = { '{', ' ', '.', '.', '.', ' ', '}', '\0' };
+Char STRfakecom1[] = { '`', ' ', '.', '.', '.', ' ', '`', '\0' };
+Char STRfignore[] = { 'f', 'i', 'g', 'n', 'o', 'r', 'e', '\0' };
+#ifdef FILEC
+Char STRfilec[] = { 'f', 'i', 'l', 'e', 'c', '\0' };
+#endif /* FILEC */
+Char STRhistchars[] = { 'h', 'i', 's', 't', 'c', 'h', 'a', 'r', 's', '\0' };
+Char STRhistfile[] = { '~', '/', '.', 'h', 'i', 's', 't', 'o', 'r',
+ 'y', '\0' };
+Char STRhistory[] = { 'h', 'i', 's', 't', 'o', 'r', 'y', '\0' };
+Char STRhome[] = { 'h', 'o', 'm', 'e', '\0' };
+Char STRignore_symlinks[] = { 'i', 'g', 'n', 'o', 'r', 'e', '_', 's', 'y', 'm',
+ 'l', 'i', 'n', 'k', 's', '\0' };
+Char STRignoreeof[] = { 'i', 'g', 'n', 'o', 'r', 'e', 'e', 'o', 'f', '\0' };
+Char STRjobs[] = { 'j', 'o', 'b', 's', '\0' };
+Char STRlistjobs[] = { 'l', 'i', 's', 't', 'j', 'o', 'b', 's', '\0' };
+Char STRlogout[] = { 'l', 'o', 'g', 'o', 'u', 't', '\0' };
+Char STRlong[] = { 'l', 'o', 'n', 'g', '\0' };
+Char STRmail[] = { 'm', 'a', 'i', 'l', '\0' };
+Char STRmh[] = { '-', 'h', '\0' };
+Char STRminus[] = { '-', '\0' };
+Char STRml[] = { '-', 'l', '\0' };
+Char STRmn[] = { '-', 'n', '\0' };
+Char STRmquestion[] = { '?' | QUOTE, ' ', '\0' };
+Char STRnice[] = { 'n', 'i', 'c', 'e', '\0' };
+Char STRnobeep[] = { 'n', 'o', 'b', 'e', 'e', 'p', '\0' };
+Char STRnoclobber[] = { 'n', 'o', 'c', 'l', 'o', 'b', 'b', 'e', 'r', '\0' };
+Char STRnoglob[] = { 'n', 'o', 'g', 'l', 'o', 'b', '\0' };
+Char STRnohup[] = { 'n', 'o', 'h', 'u', 'p', '\0' };
+Char STRnonomatch[] = { 'n', 'o', 'n', 'o', 'm', 'a', 't', 'c', 'h', '\0' };
+Char STRnormal[] = { 'n', 'o', 'r', 'm', 'a', 'l', '\0' };
+Char STRnotify[] = { 'n', 'o', 't', 'i', 'f', 'y', '\0' };
+Char STRor[] = { '|', '\0' };
+Char STRor2[] = { '|', '|', '\0' };
+Char STRpath[] = { 'p', 'a', 't', 'h', '\0' };
+Char STRprintexitvalue[] = { 'p', 'r', 'i', 'n', 't', 'e', 'x', 'i', 't', 'v',
+ 'a', 'l', 'u', 'e', '\0' };
+Char STRprompt[] = { 'p', 'r', 'o', 'm', 'p', 't', '\0' };
+Char STRprompt2[] = { 'p', 'r', 'o', 'm', 'p', 't', '2', '\0' };
+Char STRpushdsilent[] = { 'p', 'u', 's', 'h', 'd', 's', 'i', 'l', 'e', 'n',
+ 't', '\0' };
+Char STRsavehist[] = { 's', 'a', 'v', 'e', 'h', 'i', 's', 't', '\0' };
+Char STRsemisp[] = { ';', ' ', '\0' };
+Char STRshell[] = { 's', 'h', 'e', 'l', 'l', '\0' };
+Char STRslash[] = { '/', '\0' };
+Char STRsldotcshrc[] = { '/', '.', 'c', 's', 'h', 'r', 'c', '\0' };
+Char STRsldotlogin[] = { '/', '.', 'l', 'o', 'g', 'i', 'n', '\0' };
+Char STRsldthist[] = { '/', '.', 'h', 'i', 's', 't', 'o', 'r', 'y', '\0' };
+Char STRsldtlogout[] = { '/', '.', 'l', 'o', 'g', 'o', 'u', 't', '\0' };
+Char STRsource[] = { 's', 'o', 'u', 'r', 'c', 'e', '\0' };
+Char STRsp3dots[] = { ' ', '.', '.', '.', '\0' };
+Char STRspLarrow2sp[] = { ' ', '<', '<', ' ', '\0' };
+Char STRspLarrowsp[] = { ' ', '<', ' ', '\0' };
+Char STRspRarrow[] = { ' ', '>', '\0' };
+Char STRspRarrow2[] = { ' ', '>', '>', '\0' };
+Char STRRparen[] = { ')', '\0' };
+Char STRspace[] = { ' ', '\0' };
+Char STRspand2sp[] = { ' ', '&', '&', ' ', '\0' };
+Char STRspor2sp[] = { ' ', '|', '|', ' ', '\0' };
+Char STRsporsp[] = { ' ', '|', ' ', '\0' };
+Char STRstar[] = { '*', '\0' };
+Char STRstatus[] = { 's', 't', 'a', 't', 'u', 's', '\0' };
+Char STRsymcent[] = { '%', ' ', '\0' };
+Char STRsymhash[] = { '#', ' ', '\0' };
+Char STRterm[] = { 't', 'e', 'r', 'm', '\0' };
+Char STRthen[] = { 't', 'h', 'e', 'n', '\0' };
+Char STRtilde[] = { '~', '\0' };
+Char STRtime[] = { 't', 'i', 'm', 'e', '\0' };
+Char STRtmpsh[] = { '/', 't', 'm', 'p', '/', 's', 'h', '\0' };
+Char STRunalias[] = { 'u', 'n', 'a', 'l', 'i', 'a', 's', '\0' };
+Char STRuser[] = { 'u', 's', 'e', 'r', '\0' };
+Char STRverbose[] = { 'v', 'e', 'r', 'b', 'o', 's', 'e', '\0' };
+Char STRwordchars[] = { 'w', 'o', 'r', 'd', 'c', 'h', 'a', 'r', 's', '\0' };
diff --git a/bin/csh/csh.1 b/bin/csh/csh.1
new file mode 100644
index 000000000000..38bd737d44a1
--- /dev/null
+++ b/bin/csh/csh.1
@@ -0,0 +1,2145 @@
+.\" Copyright (c) 1980, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. 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.
+.\"
+.\" @(#)csh.1 6.17 (Berkeley) 6/7/91
+.\"
+.\" PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+.\" -------------------- ----- ----------------------
+.\" CURRENT PATCH LEVEL: 1 00130
+.\" -------------------- ----- ----------------------
+.\"
+.\" 06 Apr 93 Sascha Wildner Misc small fixes
+.\"
+.Dd June 7, 1991
+.Dt CSH 1
+.Os BSD 4
+.Sh NAME
+.Nm csh
+.Nd a shell (command interpreter) with C-like syntax
+.Sh SYNOPSIS
+.Nm csh
+.Op Fl bcefinstvVxX
+.Op arg ...
+.Sh DESCRIPTION
+The
+.Nm Csh
+is a command language interpreter
+incorporating a history mechanism (see
+.Nm History Substitutions ) ,
+job control facilities (see
+.Nm Jobs ) ,
+interactive file name
+and user name completion (see
+.Nm File Name Completion ) ,
+and a C-like syntax. It is used both as an interactive
+login shell and a shell script command processor.
+.Ss Argument list processing
+If the first argument (argument 0) to the shell is
+.Ql Fl
+then this
+is a login shell.
+The flag arguments are interpreted as follows:
+.Bl -tag -width 5n
+.It Fl b
+This flag forces a ``break'' from option processing, causing any further
+shell arguments to be treated as non-option arguments.
+The remaining arguments will not be interpreted as shell options.
+This may be used to pass options to a shell script without confusion
+or possible subterfuge.
+The shell will not run a set-user ID script without this option.
+.It Fl c
+Commands are read from the (single) following argument which must
+be present.
+Any remaining arguments are placed in
+.Ar argv .
+.It Fl e
+The shell exits if any invoked command terminates abnormally
+or yields a non-zero exit status.
+.It Fl f
+The shell will start faster, because it will neither search for nor
+execute commands from the file
+.Pa \&.cshrc
+in the invoker's home directory.
+.It Fl i
+The shell is interactive and prompts for its top-level input,
+even if it appears to not be a terminal.
+Shells are interactive without this option if their inputs
+and outputs are terminals.
+.It Fl n
+Commands are parsed, but not executed.
+This aids in syntactic checking of shell scripts.
+.It Fl s
+Command input is taken from the standard input.
+.It Fl t
+A single line of input is read and executed.
+A
+.Ql \e
+may be used to escape the newline at the end of this
+line and continue onto another line.
+.It Fl v
+Causes the
+.Ar verbose
+variable to be set, with the effect
+that command input is echoed after history substitution.
+.It Fl x
+Causes the
+.Ar echo
+variable to be set, so that commands are echoed immediately before execution.
+.It Fl V
+Causes the
+.Ar verbose
+variable to be set even before
+.Pa .cshrc
+is executed.
+.It Fl X
+Is to
+.Fl x
+as
+.Fl V
+is to
+.Fl v .
+.El
+.Pp
+After processing of flag arguments, if arguments remain but none of the
+.Fl c ,
+.Fl i ,
+.Fl s ,
+or
+.Fl t
+options were given, the first argument is taken as the name of a file of
+commands to be executed.
+The shell opens this file, and saves its name for possible resubstitution
+by `$0'.
+Since many systems use either the standard version 6 or version 7 shells
+whose shell scripts are not compatible with this shell, the shell will
+execute such a `standard' shell if the first character of a script
+is not a `#', i.e. if the script does not start with a comment.
+Remaining arguments initialize the variable
+.Ar argv .
+.Pp
+An instance of
+.Nm csh
+begins by executing commands from the file
+.Pa /etc/csh.cshrc
+and,
+if this is a login shell,
+.Pa \&/etc/csh.login .
+It then executes
+commands from
+.Pa \&.cshrc
+in the
+.Ar home
+directory of the invoker, and, if this is a login shell, the file
+.Pa \&.login
+in the same location.
+It is typical for users on crt's to put the command ``stty crt''
+in their
+.Pa \&.login
+file, and to also invoke
+.Xr tset 1
+there.
+.Pp
+In the normal case, the shell will begin reading commands from the
+terminal, prompting with `% '.
+Processing of arguments and the use of the shell to process files
+containing command scripts will be described later.
+.Pp
+The shell repeatedly performs the following actions:
+a line of command input is read and broken into
+.Ar words .
+This sequence of words is placed on the command history list and parsed.
+Finally each command in the current line is executed.
+.Pp
+When a login shell terminates it executes commands from the files
+.Pa .logout
+in the user's
+.Ar home
+directory and
+.Pa /etc/csh.logout .
+.Ss Lexical structure
+The shell splits input lines into words at blanks and tabs with the
+following exceptions.
+The characters
+`&' `\&|' `;' `<' `>' `(' `)'
+form separate words.
+If doubled in `&&', `\&|\&|', `<<' or `>>' these pairs form single words.
+These parser metacharacters may be made part of other words, or prevented their
+special meaning, by preceding them with `\e'.
+A newline preceded by a `\e' is equivalent to a blank.
+.Pp
+Strings enclosed in matched pairs of quotations,
+`\*(aa', `\*(ga' or `"',
+form parts of a word; metacharacters in these strings, including blanks
+and tabs, do not form separate words.
+These quotations have semantics to be described subsequently.
+Within pairs of `\'' or `"' characters a newline preceded by a `\e' gives
+a true newline character.
+.Pp
+When the shell's input is not a terminal,
+the character `#' introduces a comment which continues to the end of the
+input line.
+It is prevented this special meaning when preceded by `\e'
+and in quotations using `\`', `\'', and `"'.
+.Ss Commands
+A simple command is a sequence of words, the first of which
+specifies the command to be executed.
+A simple command or
+a sequence of simple commands separated by `\&|' characters
+forms a pipeline.
+The output of each command in a pipeline is connected to the input of the next.
+Sequences of pipelines may be separated by `;', and are then executed
+sequentially.
+A sequence of pipelines may be executed without immediately
+waiting for it to terminate by following it with an `&'.
+.Pp
+Any of the above may be placed in `(' `)' to form a simple command (which
+may be a component of a pipeline, etc.)
+It is also possible to separate pipelines with `\&|\&|' or `&&' indicating,
+as in the C language,
+that the second is to be executed only if the first fails or succeeds
+respectively. (See
+.Em Expressions . )
+.Ss Jobs
+The shell associates a
+.Ar job
+with each pipeline. It keeps
+a table of current jobs, printed by the
+.Ar jobs
+command, and assigns them small integer numbers. When
+a job is started asynchronously with `&', the shell prints a line which looks
+like:
+.Bd -filled -offset indent
+.Op 1
+1234
+.Ed
+.Pp
+indicating that the job which was started asynchronously was job number
+1 and had one (top-level) process, whose process id was 1234.
+.Pp
+If you are running a job and wish to do something else you may hit the key
+.Nm ^Z
+(control-Z) which sends a STOP signal to the current job.
+The shell will then normally indicate that the job has been `Stopped',
+and print another prompt. You can then manipulate the state of this job,
+putting it in the
+.Em background
+with the
+.Ar bg
+command, or run some other
+commands and then eventually bring the job back into the foreground with
+the
+.Em foreground
+command
+.Ar fg .
+A
+.Nm ^Z
+takes effect immediately and
+is like an interrupt in that pending output and unread input are discarded
+when it is typed. There is another special key
+.Nm ^Y
+which does
+not generate a STOP signal until a program attempts to
+.Xr read 2
+it.
+This can usefully be typed ahead when you have prepared some commands
+for a job which you wish to stop after it has read them.
+.Pp
+A job being run in the background will stop if it tries to read
+from the terminal. Background jobs are normally allowed to produce output,
+but this can be disabled by giving the command ``stty tostop''.
+If you set this
+tty option, then background jobs will stop when they try to produce
+output like they do when they try to read input.
+.Pp
+There are several ways to refer to jobs in the shell. The character
+`%' introduces a job name. If you wish to refer to job number 1, you can
+name it as `%1'. Just naming a job brings it to the foreground; thus
+`%1' is a synonym for `fg %1', bringing job 1 back into the foreground.
+Similarly saying `%1 &' resumes job 1 in the background.
+Jobs can also be named by prefixes of the string typed in to start them,
+if these prefixes are unambiguous, thus `%ex' would normally restart
+a suspended
+.Xr ex 1
+job, if there were only one suspended job whose name began with
+the string `ex'. It is also possible to say `%?string'
+which specifies a job whose text contains
+.Ar string ,
+if there is only one such job.
+.Pp
+The shell maintains a notion of the current and previous jobs.
+In output pertaining to jobs, the current job is marked with a `+'
+and the previous job with a `\-'. The abbreviation `%+' refers
+to the current job and `%\-' refers to the previous job. For close
+analogy with the syntax of the
+.Ar history
+mechanism (described below),
+`%%' is also a synonym for the current job.
+.Pp
+The job control mechanism requires that the
+.Xr stty 1
+option
+.Ic new
+be set. It is an artifact from a
+.Em new
+implementation
+of the
+tty driver which allows generation of interrupt characters from
+the keyboard to tell jobs to stop. See stty(1) for details
+on setting options in the new tty driver.
+.Ss Status reporting
+This shell learns immediately whenever a process changes state.
+It normally informs you whenever a job becomes blocked so that
+no further progress is possible, but only just before it prints
+a prompt. This is done so that it does not otherwise disturb your work.
+If, however, you set the shell variable
+.Ar notify ,
+the shell will notify you immediately of changes of status in background
+jobs.
+There is also a shell command
+.Ar notify
+which marks a single process so that its status changes will be immediately
+reported. By default
+.Ar notify
+marks the current process;
+simply say `notify' after starting a background job to mark it.
+.Pp
+When you try to leave the shell while jobs are stopped, you will
+be warned that `You have stopped jobs.' You may use the
+.Ar jobs
+command to see what they are. If you do this or immediately try to
+exit again, the shell will not warn you a second time, and the suspended
+jobs will be terminated.
+.Ss File Name Completion
+When the file name completion feature is enabled by setting
+the shell variable
+.Ar filec
+(see
+.Ic set ) ,
+.Nm csh
+will
+interactively complete file names and user names from unique
+prefixes, when they are input from the terminal followed by
+the escape character (the escape key, or control-[)
+For example,
+if the current directory looks like
+.Bd -literal -offset indent
+DSC.OLD bin cmd lib xmpl.c
+DSC.NEW chaosnet cmtest mail xmpl.o
+bench class dev mbox xmpl.out
+.Ed
+.Pp
+and the input is
+.Pp
+.Dl % vi ch<escape>
+.Pp
+.Nm csh
+will complete the prefix ``ch''
+to the only matching file name ``chaosnet'', changing the input
+line to
+.Pp
+.Dl % vi chaosnet
+.Pp
+However, given
+.Pp
+.Dl % vi D<escape>
+.Pp
+.Nm csh
+will only expand the input to
+.Pp
+.Dl % vi DSC.
+.Pp
+and will sound the terminal bell to indicate that the expansion is
+incomplete, since there are two file names matching the prefix ``D''.
+.Pp
+If a partial file name is followed by the end-of-file character
+(usually control-D), then, instead of completing the name,
+.Nm csh
+will list all file names matching the prefix. For example,
+the input
+.Pp
+.Dl % vi D<control-D>
+.Pp
+causes all files beginning with ``D'' to be listed:
+.Pp
+.Dl DSC.NEW DSC.OLD
+.Pp
+while the input line remains unchanged.
+.Pp
+The same system of escape and end-of-file can also be used to
+expand partial user names, if the word to be completed
+(or listed) begins with the character ``~''. For example,
+typing
+.Pp
+.Dl cd ~ro<escape>
+.Pp
+may produce the expansion
+.Pp
+.Dl cd ~root
+.Pp
+The use of the terminal bell to signal errors or multiple matches
+can be inhibited by setting the variable
+.Ar nobeep .
+.Pp
+Normally, all files in the particular directory are candidates
+for name completion. Files with certain suffixes can be excluded
+from consideration by setting the variable
+.Ar fignore
+to the
+list of suffixes to be ignored. Thus, if
+.Ar fignore
+is set by
+the command
+.Pp
+.Dl % set fignore = (.o .out)
+.Pp
+then typing
+.Pp
+.Dl % vi x<escape>
+.Pp
+would result in the completion to
+.Pp
+.Dl % vi xmpl.c
+.Pp
+ignoring the files "xmpl.o" and "xmpl.out".
+However, if the only completion possible requires not ignoring these
+suffixes, then they are not ignored. In addition,
+.Ar fignore
+does not affect the listing of file names by control-D. All files
+are listed regardless of their suffixes.
+.Ss Substitutions
+We now describe the various transformations the shell performs on the
+input in the order in which they occur.
+.Ss History substitutions
+History substitutions place words from previous command input as portions
+of new commands, making it easy to repeat commands, repeat arguments
+of a previous command in the current command, or fix spelling mistakes
+in the previous command with little typing and a high degree of confidence.
+History substitutions begin with the character `!' and may begin
+.Ar anywhere
+in the input stream (with the proviso that they
+.Nm "do not"
+nest.)
+This `!' may be preceded by an `\e' to prevent its special meaning; for
+convenience, a `!' is passed unchanged when it is followed by a blank,
+tab, newline, `=' or `('.
+(History substitutions also occur when an input line begins with `\*(ua'.
+This special abbreviation will be described later.)
+Any input line which contains history substitution is echoed on the terminal
+before it is executed as it could have been typed without history substitution.
+.Pp
+Commands input from the terminal which consist of one or more words
+are saved on the history list.
+The history substitutions reintroduce sequences of words from these
+saved commands into the input stream.
+The size of which is controlled by the
+.Ar history
+variable; the previous command is always retained, regardless of its value.
+Commands are numbered sequentially from 1.
+.Pp
+For definiteness, consider the following output from the
+.Ar history
+command:
+.Bd -literal -offset indent
+\09 write michael
+10 ex write.c
+11 cat oldwrite.c
+12 diff *write.c
+.Ed
+.Pp
+The commands are shown with their event numbers.
+It is not usually necessary to use event numbers, but the current event
+number can be made part of the
+.Ar prompt
+by placing an `!' in the prompt string.
+.Pp
+With the current event 13 we can refer to previous events by event
+number `!11', relatively as in `!\-2' (referring to the same event),
+by a prefix of a command word
+as in `!d' for event 12 or `!wri' for event 9, or by a string contained in
+a word in the command as in `!?mic?' also referring to event 9.
+These forms, without further modification, simply reintroduce the words
+of the specified events, each separated by a single blank.
+As a special case `!!' refers to the previous command; thus `!!'
+alone is essentially a
+.Ar redo .
+.Pp
+To select words from an event we can follow the event specification by
+a `:' and a designator for the desired words.
+The words of an input line are numbered from 0,
+the first (usually command) word being 0, the second word (first argument)
+being 1, etc.
+The basic word designators are:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It \&0
+first (command) word
+.It Ar n
+.Ar n Ns \'th
+argument
+.It \*(ua
+first argument, i.e. `1'
+.It $
+last argument
+.It %
+word matched by (immediately preceding)
+.No \&? Ns Ar s Ns \&?
+search
+.It Ar \&x\-y
+range of words
+.It Ar \&\-y
+abbreviates
+.Ar `\&0\-y\'
+.It *
+abbreviates `\*(ua\-$', or nothing if only 1 word in event
+.It Ar x*
+abbreviates
+.Ar `x\-$\'
+.It Ar x\-
+like
+.Ar `x*\'
+but omitting word `$'
+.El
+.Pp
+The `:' separating the event specification from the word designator
+can be omitted if the argument selector begins with a `\*(ua', `$', `*'
+`\-' or `%'.
+After the optional word designator can be
+placed a sequence of modifiers, each preceded by a `:'.
+The following modifiers are defined:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It h
+Remove a trailing pathname component, leaving the head.
+.It r
+Remove a trailing `.xxx' component, leaving the root name.
+.It e
+Remove all but the extension `.xxx' part.
+.It s Ns Ar /l/r/
+Substitute
+.Ar l
+for
+.Ar r
+.It t
+Remove all leading pathname components, leaving the tail.
+.It \&&
+Repeat the previous substitution.
+.It g
+Apply the change globally, prefixing the above, e.g. `g&'.
+.It p
+Print the new command line but do not execute it.
+.It q
+Quote the substituted words, preventing further substitutions.
+.It x
+Like q, but break into words at blanks, tabs and newlines.
+.El
+.Pp
+Unless preceded by a `g' the modification is applied only to the first
+modifiable word. With substitutions, it is an error for no word to be
+applicable.
+.Pp
+The left hand side of substitutions are not regular expressions in the sense
+of the editors, but rather strings.
+Any character may be used as the delimiter in place of `/';
+a `\e' quotes the delimiter into the
+.Ar l " "
+and
+.Ar r " "
+strings.
+The character `&' in the right hand side is replaced by the text from
+the left.
+A `\e' quotes `&' also.
+A null
+.Ar l
+(" ")
+uses the previous string either from a
+.Ar l
+or from a
+contextual scan string
+.Ar s
+in
+.Ns `!? Ns Ar s Ns ?'.
+The trailing delimiter in the substitution may be omitted if a newline
+follows immediately as may the trailing `?' in a contextual scan.
+.Pp
+A history reference may be given without an event specification, e.g. `!$'.
+In this case the reference is to the previous command unless a previous
+history reference occurred on the same line in which case this form repeats
+the previous reference.
+Thus `!?foo?\*(ua !$' gives the first and last arguments
+from the command matching `?foo?'.
+.Pp
+A special abbreviation of a history reference occurs when the first
+non-blank character of an input line is a `\*(ua'.
+This is equivalent to `!:s\*(ua' providing a convenient shorthand for substitutions
+on the text of the previous line.
+Thus `\*(ualb\*(ualib' fixes the spelling of
+`lib'
+in the previous command.
+Finally, a history substitution may be surrounded with `{' and `}'
+if necessary to insulate it from the characters which follow.
+Thus, after `ls \-ld ~paul' we might do `!{l}a' to do `ls \-ld ~paula',
+while `!la' would look for a command starting `la'.
+.Pp
+.Ss Quotations with \' and \&"
+The quotation of strings by `\'' and `"' can be used
+to prevent all or some of the remaining substitutions.
+Strings enclosed in `\'' are prevented any further interpretation.
+Strings enclosed in `"' may be expanded as described below.
+.Pp
+In both cases the resulting text becomes (all or part of) a single word;
+only in one special case (see
+.Em Command Substitition
+below) does a `"' quoted string yield parts of more than one word;
+`\'' quoted strings never do.
+.Ss Alias substitution
+The shell maintains a list of aliases which can be established, displayed
+and modified by the
+.Ar alias
+and
+.Ar unalias
+commands.
+After a command line is scanned, it is parsed into distinct commands and
+the first word of each command, left-to-right, is checked to see if it
+has an alias.
+If it does, then the text which is the alias for that command is reread
+with the history mechanism available
+as though that command were the previous input line.
+The resulting words replace the
+command and argument list.
+If no reference is made to the history list, then the argument list is
+left unchanged.
+.Pp
+Thus if the alias for `ls' is `ls \-l' the command `ls /usr' would map to
+`ls \-l /usr', the argument list here being undisturbed.
+Similarly if the alias for `lookup' was `grep !\*(ua /etc/passwd' then
+`lookup bill' would map to `grep bill /etc/passwd'.
+.Pp
+If an alias is found, the word transformation of the input text
+is performed and the aliasing process begins again on the reformed input line.
+Looping is prevented if the first word of the new text is the same as the old
+by flagging it to prevent further aliasing.
+Other loops are detected and cause an error.
+.Pp
+Note that the mechanism allows aliases to introduce parser metasyntax.
+Thus we can `alias print \'pr \e!* \&| lpr\'' to make a command which
+.Ar pr \'s
+its arguments to the line printer.
+.Ss Variable substitution
+The shell maintains a set of variables, each of which has as value a list
+of zero or more words.
+Some of these variables are set by the shell or referred to by it.
+For instance, the
+.Ar argv
+variable is an image of the shell's argument list, and words of this
+variable's value are referred to in special ways.
+.Pp
+The values of variables may be displayed and changed by using the
+.Ar set
+and
+.Ar unset
+commands.
+Of the variables referred to by the shell a number are toggles;
+the shell does not care what their value is,
+only whether they are set or not.
+For instance, the
+.Ar verbose
+variable is a toggle which causes command input to be echoed.
+The setting of this variable results from the
+.Fl v
+command line option.
+.Pp
+Other operations treat variables numerically.
+The `@' command permits numeric calculations to be performed and the result
+assigned to a variable.
+Variable values are, however, always represented as (zero or more) strings.
+For the purposes of numeric operations, the null string is considered to be
+zero, and the second and subsequent words of multiword values are ignored.
+.Pp
+After the input line is aliased and parsed, and before each command
+is executed, variable substitution
+is performed keyed by `$' characters.
+This expansion can be prevented by preceding the `$' with a `\e' except
+within `"'s where it
+.Em always
+occurs, and within `\''s where it
+.Em never
+occurs.
+Strings quoted by `\*(ga' are interpreted later (see
+.Nm "Command substitution"
+below) so `$' substitution does not occur there until later, if at all.
+A `$' is passed unchanged if followed by a blank, tab, or end-of-line.
+.Pp
+Input/output redirections are recognized before variable expansion,
+and are variable expanded separately.
+Otherwise, the command name and entire argument list are expanded together.
+It is thus possible for the first (command) word to this point to generate
+more than one word, the first of which becomes the command name,
+and the rest of which become arguments.
+.Pp
+Unless enclosed in `"' or given the `:q' modifier the results of variable
+substitution may eventually be command and filename substituted.
+Within `"', a variable whose value consists of multiple words expands to a
+(portion of) a single word, with the words of the variables value
+separated by blanks.
+When the `:q' modifier is applied to a substitution
+the variable will expand to multiple words with each word separated
+by a blank and quoted to prevent later command or filename substitution.
+.Pp
+The following metasequences are provided for introducing variable values into
+the shell input.
+Except as noted, it is an error to reference a variable which is not set.
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It $name
+.It ${name}
+Are replaced by the words of the value of variable
+.Ar name ,
+each separated by a blank.
+Braces insulate
+.Ar name
+from following characters which would otherwise be part of it.
+Shell variables have names consisting of up to 20 letters and digits
+starting with a letter. The underscore character is considered a letter.
+.br
+If
+.Ar name
+is not a shell variable, but is set in the environment, then
+that value is returned (but
+.Nm :
+modifiers and the other forms
+given below are not available in this case).
+.It $name Ns Op selector
+.It ${name Ns Op selector Ns }
+May be used to select only some of the words from the value of
+.Ar name .
+The selector is subjected to `$' substitution and may consist of a single
+number or two numbers separated by a `\-'.
+The first word of a variables value is numbered `1'.
+If the first number of a range is omitted it defaults to `1'.
+If the last member of a range is omitted it defaults to `$#name'.
+The selector `*' selects all words.
+It is not an error for a range to be empty if the second argument is omitted
+or in range.
+.It $#name
+.It ${#name}
+Gives the number of words in the variable.
+This is useful for later use in a
+`$argv[selector]'.
+.It $0
+Substitutes the name of the file from which command input is being read.
+An error occurs if the name is not known.
+.It $number
+.It ${number}
+Equivalent to
+`$argv[number]'.
+.It $*
+Equivalent to
+`$argv[*]'.
+The modifiers `:e', `:h', `:t', `:r', `:q' and `:x' may be applied to
+the substitutions above as may `:gh', `:gt' and `:gr'.
+If braces `{' '}' appear in the command form then the modifiers
+must appear within the braces.
+The current implementation allows only one `:' modifier on each `$' expansion.
+.El
+.Pp
+The following substitutions may not be modified with `:' modifiers.
+.Bl -tag -width Ds -compact -offset indent
+.It $?name
+.It ${?name}
+Substitutes the string `1' if name is set, `0' if it is not.
+.It $?0
+Substitutes `1' if the current input filename is known, `0' if it is not.
+.It $$
+Substitute the (decimal) process number of the (parent) shell.
+.It $<
+Substitutes a line from the standard
+input, with no further interpretation thereafter. It can be used
+to read from the keyboard in a shell script.
+.El
+.Ss Command and filename substitution
+The remaining substitutions, command and filename substitution,
+are applied selectively to the arguments of builtin commands.
+This means that portions of expressions which are not evaluated are
+not subjected to these expansions.
+For commands which are not internal to the shell, the command
+name is substituted separately from the argument list.
+This occurs very late,
+after input-output redirection is performed, and in a child
+of the main shell.
+.Ss Command substitution
+Command substitution is indicated by a command enclosed in `\*(ga'.
+The output from such a command is normally broken into separate words
+at blanks, tabs and newlines, with null words being discarded,
+this text then replacing the original string.
+Within `"'s, only newlines force new words; blanks and tabs are preserved.
+.Pp
+In any case, the single final newline does not force a new word.
+Note that it is thus possible for a command substitution to yield
+only part of a word, even if the command outputs a complete line.
+.Ss Filename substitution
+If a word contains any of the characters `*', `?', `[' or `{'
+or begins with the character `~', then that word is a candidate for
+filename substitution, also known as `globbing'.
+This word is then regarded as a pattern, and replaced with an alphabetically
+sorted list of file names which match the pattern.
+In a list of words specifying filename substitution it is an error for
+no pattern to match an existing file name, but it is not required
+for each pattern to match.
+Only the metacharacters `*', `?' and `[' imply pattern matching,
+the characters `~' and `{' being more akin to abbreviations.
+.Pp
+In matching filenames, the character `.' at the beginning of a filename
+or immediately following a `/', as well as the character `/' must
+be matched explicitly.
+The character `*' matches any string of characters, including the null
+string.
+The character `?' matches any single character.
+The sequence
+.Sq Op ...
+matches any one of the characters enclosed.
+Within
+.Sq Op ... ,
+a pair of characters separated by `\-' matches any character lexically between
+the two.
+.Pp
+The character `~' at the beginning of a filename is used to refer to home
+directories.
+Standing alone, i.e. `~' it expands to the invokers home directory as reflected
+in the value of the variable
+.Ar home .
+When followed by a name consisting of letters, digits and `\-' characters
+the shell searches for a user with that name and substitutes their
+home directory; thus `~ken' might expand to `/usr/ken' and `~ken/chmach'
+to `/usr/ken/chmach'.
+If the character `~' is followed by a character other than a letter or `/'
+or appears not at the beginning of a word,
+it is left undisturbed.
+.Pp
+The metanotation `a{b,c,d}e' is a shorthand for `abe ace ade'.
+Left to right order is preserved, with results of matches being sorted
+separately at a low level to preserve this order.
+This construct may be nested.
+Thus `~source/s1/{oldls,ls}.c' expands to
+`/usr/source/s1/oldls.c /usr/source/s1/ls.c'
+whether or not these files exist without any chance of error
+if the home directory for `source' is `/usr/source'.
+Similarly `../{memo,*box}' might expand to `../memo ../box ../mbox'.
+(Note that `memo' was not sorted with the results of matching `*box'.)
+As a special case `{', `}' and `{}' are passed undisturbed.
+.Ss Input/output
+The standard input and standard output of a command may be redirected
+with the following syntax:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It < name
+Open file
+.Ar name
+(which is first variable, command and filename expanded) as the standard
+input.
+.It << word
+Read the shell input up to a line which is identical to
+.Ar word .
+.Ar Word
+is not subjected to variable, filename or command substitution,
+and each input line is compared to
+.Ar word
+before any substitutions are done on this input line.
+Unless a quoting `\e', `"', `\*(aa' or `\*(ga' appears in
+.Ar word
+variable and command substitution is performed on the intervening lines,
+allowing `\e' to quote `$', `\e' and `\*(ga'.
+Commands which are substituted have all blanks, tabs, and newlines
+preserved, except for the final newline which is dropped.
+The resultant text is placed in an anonymous temporary file which
+is given to the command as standard input.
+.It > name
+.It >! name
+.It >& name
+.It >&! name
+The file
+.Ar name
+is used as standard output.
+If the file does not exist then it is created;
+if the file exists, its is truncated, its previous contents being lost.
+.Pp
+If the variable
+.Ar noclobber
+is set, then the file must not exist or be a character special file (e.g. a
+terminal or `/dev/null') or an error results.
+This helps prevent accidental destruction of files.
+In this case the `!' forms can be used and suppress this check.
+.Pp
+The forms involving `&' route the diagnostic output into the specified
+file as well as the standard output.
+.Ar Name
+is expanded in the same way as `<' input filenames are.
+.It >> name
+.It >>& name
+.It >>! name
+.It >>&! name
+Uses file
+.Ar name
+as standard output like `>' but places output at the end of the file.
+If the variable
+.Ar noclobber
+is set, then it is an error for the file not to exist unless
+one of the `!' forms is given.
+Otherwise similar to `>'.
+.El
+.Pp
+A command receives the environment in which the shell was
+invoked as modified by the input-output parameters and
+the presence of the command in a pipeline.
+Thus, unlike some previous shells, commands run from a file of shell commands
+have no access to the text of the commands by default; rather
+they receive the original standard input of the shell.
+The `<<' mechanism should be used to present inline data.
+This permits shell command scripts to function as components of pipelines
+and allows the shell to block read its input.
+Note that the default standard input for a command run detached is
+.Ar not
+modified to be the empty file
+.Pa /dev/null ;
+rather the standard input
+remains as the original standard input of the shell. If this is a terminal
+and if the process attempts to read from the terminal, then the process
+will block and the user will be notified (see
+.Sx Jobs
+above).
+.Pp
+Diagnostic output may be directed through a pipe with the standard output.
+Simply use the form `\&|&' rather than just `\&|'.
+.Ss Expressions
+A number of the builtin commands (to be described subsequently)
+take expressions, in which the operators are similar to those of C, with
+the same precedence.
+These expressions appear in the
+.Nm @,
+.Ar exit ,
+.Ar if ,
+and
+.Ar while
+commands.
+The following operators are available:
+.Bd -ragged -offset indent
+\&|\&| && \&| \*(ua & == != =~ !~ <= >=
+< > << >> + \- * / % ! ~ ( )
+.Ed
+.Pp
+Here the precedence increases to the right,
+`==' `!=' `=~' and `!~', `<=' `>=' `<' and `>', `<<' and `>>', `+' and `\-',
+`*' `/' and `%' being, in groups, at the same level.
+The `==' `!=' `=~' and `!~' operators compare their arguments as strings;
+all others operate on numbers.
+The operators `=~' and `!~' are like `!=' and `==' except that the right
+hand side is a
+.Ar pattern
+(containing, e.g. `*'s, `?'s and instances of
+`[...]'
+against which the left hand operand is matched. This reduces the
+need for use of the
+.Ar switch
+statement in shell scripts when all that is really needed is pattern matching.
+.Pp
+Strings which begin with `0' are considered octal numbers.
+Null or missing arguments are considered `0'.
+The result of all expressions are strings,
+which represent decimal numbers.
+It is important to note that no two components of an expression can appear
+in the same word; except when adjacent to components of expressions which
+are syntactically significant to the parser (`&' `\&|' `<' `>' `(' `)')
+they should be surrounded by spaces.
+.Pp
+Also available in expressions as primitive operands are command executions
+enclosed in `{' and `}'
+and file enquiries of the form
+.Fl l
+.Ar name
+where
+.Ic l
+is one of:
+.Bd -literal -offset indent
+r read access
+w write access
+x execute access
+e existence
+o ownership
+z zero size
+f plain file
+d directory
+.Ed
+.Pp
+The specified name is command and filename expanded and then tested
+to see if it has the specified relationship to the real user.
+If the file does not exist or is inaccessible then all enquiries return
+false, i.e. `0'.
+Command executions succeed, returning true, i.e. `1',
+if the command exits with status 0, otherwise they fail, returning
+false, i.e. `0'.
+If more detailed status information is required then the command
+should be executed outside of an expression and the variable
+.Ar status
+examined.
+.Ss Control flow
+The shell contains a number of commands which can be used to regulate the
+flow of control in command files (shell scripts) and
+(in limited but useful ways) from terminal input.
+These commands all operate by forcing the shell to reread or skip in its
+input and, due to the implementation, restrict the placement of some
+of the commands.
+.Pp
+The
+.Ic foreach ,
+.Ic switch ,
+and
+.Ic while
+statements, as well as the
+.Ic if\-then\-else
+form of the
+.Ic if
+statement require that the major keywords appear in a single simple command
+on an input line as shown below.
+.Pp
+If the shell's input is not seekable,
+the shell buffers up input whenever a loop is being read
+and performs seeks in this internal buffer to accomplish the rereading
+implied by the loop.
+(To the extent that this allows, backward goto's will succeed on
+non-seekable inputs.)
+.Ss Builtin commands
+Builtin commands are executed within the shell.
+If a builtin command occurs as any component of a pipeline
+except the last then it is executed in a subshell.
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ic alias
+.It Ic alias Ar name
+.It Ic alias Ar name wordlist
+The first form prints all aliases.
+The second form prints the alias for name.
+The final form assigns the specified
+.Ar wordlist
+as the alias of
+.Ar name ;
+.Ar wordlist
+is command and filename substituted.
+.Ar Name
+is not allowed to be
+.Ar alias
+or
+.Ar unalias .
+.Pp
+.It Ic alloc
+Shows the amount of dynamic memory acquired, broken down into used and
+free memory.
+With an argument shows the number of free and used blocks in each size
+category. The categories start at size 8 and double at each step.
+This command's output may vary across system types, since
+systems other than the VAX may use a different memory allocator.
+.Pp
+.It Ic bg
+.It Ic bg \&% Ns Ar job ...
+Puts the current or specified jobs into the background, continuing them
+if they were stopped.
+.Pp
+.It Ic break
+Causes execution to resume after the
+.Ic end
+of the nearest enclosing
+.Ic foreach
+or
+.Ic while .
+The remaining commands on the current line are executed.
+Multi-level breaks are thus possible by writing them all on one line.
+.Pp
+.It Ic breaksw
+Causes a break from a
+.Ic switch ,
+resuming after the
+.Ic endsw .
+.Pp
+.It Ic case Ar label :
+A label in a
+.Ic switch
+statement as discussed below.
+.Pp
+.It Ic cd
+.It Ic cd Ar name
+.It Ic chdir
+.It Ic chdir Ar name
+Change the shell's working directory to directory
+.Ar name .
+If no argument is given then change to the home directory of the user.
+If
+.Ar name
+is not found as a subdirectory of the current directory (and does not begin
+with `/', `./' or `../'), then each
+component of the variable
+.Ic cdpath
+is checked to see if it has a subdirectory
+.Ar name .
+Finally, if all else fails but
+.Ar name
+is a shell variable whose value begins with `/', then this
+is tried to see if it is a directory.
+.Pp
+.It Ic continue
+Continue execution of the nearest enclosing
+.Ic while
+or
+.Ic foreach .
+The rest of the commands on the current line are executed.
+.Pp
+.It Ic default :
+Labels the default case in a
+.Ic switch
+statement.
+The default should come after all
+.Ic case
+labels.
+.Pp
+.It Ic dirs
+Prints the directory stack; the top of the stack is at the left,
+the first directory in the stack being the current directory.
+.Pp
+.It Ic echo Ar wordlist
+.It Ic echo Fl n Ar wordlist
+The specified words are written to the shells standard output, separated
+by spaces, and terminated with a newline unless the
+.Fl n
+option is specified.
+.Pp
+.It Ic else
+.It Ic end
+.It Ic endif
+.It Ic endsw
+See the description of the
+.Ic foreach ,
+.Ic if ,
+.Ic switch ,
+and
+.Ic while
+statements below.
+.Pp
+.It Ic eval Ar arg ...
+(As in
+.Xr sh 1 . )
+The arguments are read as input to the shell and the resulting
+command(s) executed in the context of the current shell.
+This is usually used to execute commands
+generated as the result of command or variable substitution, since
+parsing occurs before these substitutions. See
+.Xr tset 1
+for an example of using
+.Ic eval .
+.Pp
+.It Ic exec Ar command
+The specified command is executed in place of the current shell.
+.Pp
+.It Ic exit
+.It Ic exit Ar (expr )
+The shell exits either with the value of the
+.Ic status
+variable (first form) or with the value of the specified
+.Ic expr
+(second form).
+.Pp
+.It Ic fg
+.It Ic fg \&% Ar job ...
+Brings the current or specified jobs into the foreground, continuing them if
+they were stopped.
+.Pp
+.It Ic foreach Ar name (wordlist)
+.It ...
+.It Ic end
+The variable
+.Ic name
+is successively set to each member of
+.Ic wordlist
+and the sequence of commands between this command and the matching
+.Ic end
+are executed.
+(Both
+.Ic foreach
+and
+.Ic end
+must appear alone on separate lines.)
+The builtin command
+.Ic continue
+may be used to continue the loop prematurely and the builtin
+command
+.Ic break
+to terminate it prematurely.
+When this command is read from the terminal, the loop is read up once
+prompting with `?' before any statements in the loop are executed.
+If you make a mistake typing in a loop at the terminal you can rub it out.
+.Pp
+.It Ic glob Ar wordlist
+Like
+.Ic echo
+but no `\e' escapes are recognized and words are delimited
+by null characters in the output.
+Useful for programs which wish to use the shell to filename expand a list
+of words.
+.Pp
+.It Ic goto Ar word
+The specified
+.Ic word
+is filename and command expanded to yield a string of the form `label'.
+The shell rewinds its input as much as possible
+and searches for a line of the form `label:'
+possibly preceded by blanks or tabs.
+Execution continues after the specified line.
+.Pp
+.It Ic hashstat
+Print a statistics line indicating how effective the internal hash
+table has been at locating commands (and avoiding
+.Ic exec Ns \'s ) .
+An
+.Ic exec
+is attempted for each component of the
+.Em path
+where the hash function indicates a possible hit, and in each component
+which does not begin with a `/'.
+.Pp
+.It Ic history
+.It Ic history Ar n
+.It Ic history Fl r Ar n
+.It Ic history Fl h Ar n
+Displays the history event list; if
+.Ar n
+is given only the
+.Ar n
+most recent events are printed.
+The
+.Fl r
+option reverses the order of printout to be most recent first
+rather than oldest first.
+The
+.Fl h
+option causes the history list to be printed without leading numbers.
+This is used to produce files suitable for sourceing using the \-h
+option to
+.Ic source .
+.Pp
+.It Ic if Pq Ar expr No command
+If the specified expression evaluates true, then the single
+.Ar command
+with arguments is executed.
+Variable substitution on
+.Ar command
+happens early, at the same
+time it does for the rest of the
+.Ic if
+command.
+.Ar Command
+must be a simple command, not
+a pipeline, a command list, or a parenthesized command list.
+Input/output redirection occurs even if
+.Ar expr
+is false, when command is
+.Sy not
+executed (this is a bug).
+.Pp
+.It Ic if ( Ar expr ) Ic then
+.It ...
+.It Ic else if ( Ar expr2 ) Ic then
+.It ...
+.It Ic else
+.It ...
+.It Ic endif
+If the specified
+.Ar expr
+is true then the commands to the first
+.Ic else
+are executed; otherwise if
+.Ar expr2
+is true then the commands to the
+second
+.Ic else
+are executed, etc.
+Any number of
+.Ic else-if
+pairs are possible; only one
+.Ic endif
+is needed.
+The
+.Ic else
+part is likewise optional.
+(The words
+.Ic else
+and
+.Ic endif
+must appear at the beginning of input lines;
+the
+.Ic if
+must appear alone on its input line or after an
+.Ic else . )
+.Pp
+.It Ic jobs
+.It Ic jobs Fl l
+Lists the active jobs; given the
+.Fl l
+options lists process id's in addition to the normal information.
+.Pp
+.It Ic kill % Ar job
+.It Ic kill Ar pid
+.It Ic kill Fl sig Ar pid ...
+.It Ic kill Fl l
+Sends either the TERM (terminate) signal or the
+specified signal to the specified jobs or processes.
+Signals are either given by number or by names (as given in
+.Pa /usr/include/signal.h,
+stripped of the prefix ``SIG'').
+The signal names are listed by ``kill \-l''.
+There is no default, saying just `kill' does not
+send a signal to the current job.
+If the signal being sent is TERM (terminate) or HUP (hangup),
+then the job or process will be sent a CONT (continue) signal as well.
+.Pp
+.It Ic limit
+.It Ic limit Ar resource
+.It Ic limit Ar resource maximum-use
+.It Ic limit Fl h
+.It Ic limit Fl h Ar resource
+.It Ic limit Fl h Ar resource maximum-use
+Limits the consumption by the current process and each process
+it creates to not individually exceed
+.Ar maximum-use
+on the
+specified
+.Ar resource .
+If no
+.Ar maximum-use
+is given, then
+the current limit is printed; if no
+.Ar resource
+is given, then
+all limitations are given. If the
+.Fl h
+flag is given, the hard limits are used instead of the current
+limits. The hard limits impose a ceiling on the values of
+the current limits. Only the super-user may raise the hard limits,
+but a user may lower or raise the current limits within the legal range.
+.Pp
+Resources controllable currently include
+.Ar cputime
+(the maximum
+number of cpu-seconds to be used by each process),
+.Ar filesize
+(the largest single file which can be created),
+.Ar datasize
+(the maximum growth of the data+stack region via
+.Xr sbrk 2
+beyond the end of the program text),
+.Ar stacksize
+(the maximum
+size of the automatically-extended stack region), and
+.Ar coredumpsize
+(the size of the largest core dump that will be created).
+.Pp
+The
+.Ar maximum-use
+may be given as a (floating point or integer)
+number followed by a scale factor. For all limits other than
+.Ar cputime
+the default scale is `k' or `kilobytes' (1024 bytes);
+a scale factor of `m' or `megabytes' may also be used.
+For
+.Ar cputime
+the default scaling is `seconds', while `m' for minutes
+or `h' for hours, or a time of the form `mm:ss' giving minutes
+and seconds may be used.
+.Pp
+For both
+.Ar resource
+names and scale factors, unambiguous prefixes
+of the names suffice.
+.Pp
+.It Ic login
+Terminate a login shell, replacing it with an instance of
+.Pa /bin/login.
+This is one way to log off, included for compatibility with
+.Xr sh 1 .
+.Pp
+.It Ic logout
+Terminate a login shell.
+Especially useful if
+.Ic ignoreeof
+is set.
+.Pp
+.It Ic nice
+.It Ic nice Ar +number
+.It Ic nice Ar command
+.It Ic nice Ar +number command
+The first form sets the
+scheduling priority
+for this shell to 4.
+The second form sets the
+priority
+to the given
+.Ar number .
+The final two forms run command at priority 4 and
+.Ar number
+respectively.
+The greater the number, the less cpu the process will get.
+The super-user may specify negative priority by using `nice \-number ...'.
+Command is always executed in a sub-shell, and the restrictions
+placed on commands in simple
+.Ic if
+statements apply.
+.Pp
+.It Ic nohup
+.It Ic nohup Ar command
+The first form can be used in shell scripts to cause hangups to be
+ignored for the remainder of the script.
+The second form causes the specified command to be run with hangups
+ignored.
+All processes detached with `&' are effectively
+.Ic nohup Ns \'ed .
+.Pp
+.It Ic notify
+.It Ic notify % Ar job ...
+Causes the shell to notify the user asynchronously when the status of the
+current or specified jobs changes; normally notification is presented
+before a prompt. This is automatic if the shell variable
+.Ic notify
+is set.
+.Pp
+.It Ic onintr
+.It Ic onintr Fl
+.It Ic onintr Ar label
+Control the action of the shell on interrupts.
+The first form restores the default action of the shell on interrupts
+which is to terminate shell scripts or to return to the terminal command
+input level.
+The second form `onintr \-' causes all interrupts to be ignored.
+The final form causes the shell to execute a `goto label' when
+an interrupt is received or a child process terminates because
+it was interrupted.
+.Pp
+In any case, if the shell is running detached and interrupts are
+being ignored, all forms of
+.Ic onintr
+have no meaning and interrupts
+continue to be ignored by the shell and all invoked commands.
+.Pp
+.It Ic popd
+.It Ic popd Ar +n
+Pops the directory stack, returning to the new top directory.
+With an argument
+.Ns \`+ Ar n Ns \'
+discards the
+.Ar n Ns \'th
+entry in the stack.
+The elements of the directory stack are numbered from 0 starting at the top.
+.Pp
+.It Ic pushd
+.It Ic pushd Ar name
+.It Ic pushd Ar n
+With no arguments,
+.Ic pushd
+exchanges the top two elements of the directory stack.
+Given a
+.Ar name
+argument,
+.Ic pushd
+changes to the new directory (ala
+.Ic cd )
+and pushes the old current working directory
+(as in
+.Ic csw )
+onto the directory stack.
+With a numeric argument, rotates the
+.Ar n Ns \'th
+argument of the directory
+stack around to be the top element and changes to it. The members
+of the directory stack are numbered from the top starting at 0.
+.Pp
+.It Ic rehash
+Causes the internal hash table of the contents of the directories in
+the
+.Ic path
+variable to be recomputed. This is needed if new commands are added
+to directories in the
+.Ic path
+while you are logged in. This should only be necessary if you add
+commands to one of your own directories, or if a systems programmer
+changes the contents of one of the system directories.
+.Pp
+.It Ic repeat Ar count command
+The specified
+.Ar command
+which is subject to the same restrictions
+as the
+.Ar command
+in the one line
+.Ic if
+statement above,
+is executed
+.Ar count
+times.
+I/O redirections occur exactly once, even if
+.Ar count
+is 0.
+.Pp
+.It Ic set
+.It Ic set Ar name
+.It Ic set Ar name Ns =word
+.It Ic set Ar name[index] Ns =word
+.It Ic set Ar name Ns =(wordlist)
+The first form of the command shows the value of all shell variables.
+Variables which have other than a single word as value print as a parenthesized
+word list.
+The second form sets
+.Ic name
+to the null string.
+The third form sets
+.Ic name
+to the single
+.Ic word .
+The fourth form sets
+the
+.Ar index Ns 'th
+component of name to word;
+this component must already exist.
+The final form sets
+.Ar name
+to the list of words in
+.Ar wordlist .
+In all cases the value is command and filename expanded.
+.Pp
+These arguments may be repeated to set multiple values in a single set command.
+Note however, that variable expansion happens for all arguments before any
+setting occurs.
+.Pp
+.It Ic setenv
+.It Ic setenv Ar name value
+.It Ic setenv Ar name
+The first form lists all current environment variables.
+The last form sets the value of environment variable
+.Ar name
+to be
+.Ar value ,
+a single string. The second form sets
+.Ar name
+to an empty string.
+The most commonly used environment variable
+.Ev USER ,
+.Ev TERM ,
+and
+.Ev PATH
+are automatically imported to and exported from the
+.Nm csh
+variables
+.Ar user ,
+.Op Ar term ,
+and
+.Ar path ;
+there is no need to use
+.Ic setenv
+for these.
+.Pp
+.It Ic shift
+.It Ic shift Ar variable
+The members of
+.Ic argv
+are shifted to the left, discarding
+.Ic argv Bq 1 .
+It is an error for
+.Ic argv
+not to be set or to have less than one word as value.
+The second form performs the same function on the specified variable.
+.Pp
+.It Ic source Ar name
+.It Ic source Fl h Ar name
+The shell reads commands from
+.Ic name .
+.Ic Source
+commands may be nested; if they are nested too deeply the shell may
+run out of file descriptors.
+An error in a
+.Ic source
+at any level terminates all nested
+.Ic source
+commands.
+Normally input during
+.Ic source
+commands is not placed on the history list;
+the \-h option causes the commands to be placed in the
+history list without being executed.
+.Pp
+.It Ic stop
+.It Ic stop % Ns Ar job ...
+Stops the current or specified job which is executing in the background.
+.Pp
+.It Ic suspend
+Causes the shell to stop in its tracks, much as if it had been sent a stop
+signal with
+.Ic ^Z .
+This is most often used to stop shells started by
+.Xr su 1 .
+.Pp
+.It Ic switch Ar (string)
+.It Ic case Ar str1 :
+.It \ \ \ \ \&...
+.It Ic \ \ \ \ breaksw
+.It \ \ \ \ \&...
+.It Ic default :
+.It \ \ \ \ \&...
+.It Ic \ \ \ \ breaksw
+.It Ic endsw
+Each case label is successively matched, against the specified
+.Ar string
+which is first command and filename expanded.
+The file metacharacters `*', `?' and `[...]'
+may be used in the case labels,
+which are variable expanded.
+If none of the labels match before a `default' label is found, then
+the execution begins after the default label.
+Each case label and the default label must appear at the beginning of a line.
+The command
+.Ic breaksw
+causes execution to continue after the
+.Ic endsw .
+Otherwise control may fall through case labels and default labels as in C.
+If no label matches and there is no default, execution continues after
+the
+.Ic endsw .
+.Pp
+.It Ic time
+.It Ic time Ar command
+With no argument, a summary of time used by this shell and its children
+is printed.
+If arguments are given
+the specified simple command is timed and a time summary
+as described under the
+.Ic time
+variable is printed. If necessary, an extra shell is created to print the time
+statistic when the command completes.
+.Pp
+.It Ic umask
+.It Ic umask Ar value
+The file creation mask is displayed (first form) or set to the specified
+value (second form). The mask is given in octal. Common values for
+the mask are 002 giving all access to the group and read and execute
+access to others or 022 giving all access except no write access for
+users in the group or others.
+.Pp
+.It Ic unalias Ar pattern
+All aliases whose names match the specified pattern are discarded.
+Thus all aliases are removed by `unalias *'.
+It is not an error for nothing to be
+.Ic unaliased .
+.Pp
+.It Ic unhash
+Use of the internal hash table to speed location of executed programs
+is disabled.
+.Pp
+.It Ic unlimit
+.It Ic unlimit Ar resource
+.It Ic unlimit Fl h
+.It Ic unlimit Fl h Ar resource
+Removes the limitation on
+.Ar resource .
+If no
+.Ar resource
+is specified, then all
+.Ar resource
+limitations are removed. If
+.Fl h
+is given, the corresponding hard limits are removed. Only the
+super-user may do this.
+.Pp
+.It Ic unset Ar pattern
+All variables whose names match the specified pattern are removed.
+Thus all variables are removed by `unset *'; this has noticeably
+distasteful side-effects.
+It is not an error for nothing to be
+.Ic unset .
+.Pp
+.It Ic unsetenv Ar pattern
+Removes all variables whose name match the specified pattern from the
+environment. See also the
+.Ic setenv
+command above and
+.Xr printenv 1 .
+.Pp
+.It Ic wait
+All background jobs are waited for.
+It the shell is interactive, then an interrupt can disrupt the wait,
+at which time the shell prints names and job numbers of all jobs
+known to be outstanding.
+.Pp
+.It Ic while Ar (expr)
+.It \&...
+.It Ic end
+While the specified expression evaluates non-zero, the commands between
+the
+.Ic while
+and the matching end are evaluated.
+.Ic Break
+and
+.Ic continue
+may be used to terminate or continue the loop prematurely.
+(The
+.Ic while
+and
+.Ic end
+must appear alone on their input lines.)
+Prompting occurs here the first time through the loop as for the
+.Ic foreach
+statement if the input is a terminal.
+.Pp
+.It Ic % Ar job
+Brings the specified job into the foreground.
+.Pp
+.It Ic % Ar job Ic &
+Continues the specified job in the background.
+.Pp
+.It Ic @
+.It Ic @ Ns Ar name Ns = expr
+.It Ic @ Ns Ar name[index] Ns = expr
+The first form prints the values of all the shell variables.
+The second form sets the specified
+.Ar name
+to the value of
+.Ar expr .
+If the expression contains `<', `>', `&' or `' then at least
+this part of the expression must be placed within `(' `)'.
+The third form assigns the value of
+.Ar expr
+to the
+.Ar index Ns 'th
+argument of
+.Ar name .
+Both
+.Ar name
+and its
+.Ar index Ns 'th
+component must already exist.
+.El
+.Pp
+The operators `*=', `+=', etc are available as in C.
+The space separating the name from the assignment operator is optional.
+Spaces are, however, mandatory in separating components of
+.Ar expr
+which would otherwise be single words.
+.Pp
+Special postfix `++' and `\-\-' operators increment and decrement
+.Ar name
+respectively, i.e. `@ i++'.
+.Ss Pre-defined and environment variables
+The following variables have special meaning to the shell.
+Of these,
+.Ar argv ,
+.Ar cwd,
+.Ar home ,
+.Ar path,
+.Ar prompt ,
+.Ar shell
+and
+.Ar status
+are always set by the shell.
+Except for
+.Ar cwd
+and
+.Ar status
+this setting occurs only at initialization;
+these variables will not then be modified unless this is done
+explicitly by the user.
+.Pp
+This shell copies the environment variable
+.Ev USER
+into the variable
+.Ar user ,
+.Ev TERM
+into
+.Ar term ,
+and
+.Ev HOME
+into
+.Ar home ,
+and copies these back into the environment whenever the normal
+shell variables are reset.
+The environment variable
+.Ev PATH
+is likewise handled; it is not
+necessary to worry about its setting other than in the file
+.Ar \&.cshrc
+as inferior
+.Nm csh
+processes will import the definition of
+.Ar path
+from the environment, and re-export it if you then change it.
+.Bl -tag -width histchars
+.It Ic argv
+Set to the arguments to the shell, it is from this variable that
+positional parameters are substituted, i.e. `$1' is replaced by
+`$argv[1]',
+etc.
+.It Ic cdpath
+Gives a list of alternate directories searched to find subdirectories
+in
+.Ar chdir
+commands.
+.It Ic cwd
+The full pathname of the current directory.
+.It Ic echo
+Set when the
+.Fl x
+command line option is given.
+Causes each command and its arguments
+to be echoed just before it is executed.
+For non-builtin commands all expansions occur before echoing.
+Builtin commands are echoed before command and filename substitution,
+since these substitutions are then done selectively.
+.It Ic filec
+Enable file name completion.
+.It Ic histchars
+Can be given a string value to change the characters used in history
+substitution. The first character of its value is used as the
+history substitution character, replacing the default character `!'.
+The second character of its value replaces the character `\(ua' in
+quick substitutions.
+.It Ic history
+Can be given a numeric value to control the size of the history list.
+Any command which has been referenced in this many events will not be
+discarded.
+Too large values of
+.Ar history
+may run the shell out of memory.
+The last executed command is always saved on the history list.
+.It Ic home
+The home directory of the invoker, initialized from the environment.
+The filename expansion of
+.Sq Pa ~
+refers to this variable.
+.It Ic ignoreeof
+If set the shell ignores
+end-of-file from input devices which are terminals.
+This prevents shells from accidentally being killed by control-D's.
+.It Ic mail
+The files where the shell checks for mail.
+This is done after each command completion which will result in a prompt,
+if a specified interval has elapsed.
+The shell says `You have new mail.'
+if the file exists with an access time not greater than its modify time.
+.Pp
+If the first word of the value of
+.Ar mail
+is numeric it specifies a different mail checking interval, in seconds,
+than the default, which is 10 minutes.
+.Pp
+If multiple mail files are specified, then the shell says
+`New mail in
+.Ar name Ns '
+when there is mail in the file
+.Ar name .
+.It Ic noclobber
+As described in the section on
+.Sx Input/output ,
+restrictions are placed on output redirection to insure that
+files are not accidentally destroyed, and that `>>' redirections
+refer to existing files.
+.It Ic noglob
+If set, filename expansion is inhibited.
+This is most useful in shell scripts which are not dealing with filenames,
+or after a list of filenames has been obtained and further expansions
+are not desirable.
+.It Ic nonomatch
+If set, it is not an error for a filename expansion to not match any
+existing files; rather the primitive pattern is returned.
+It is still an error for the primitive pattern to be malformed, i.e.
+`echo ['
+still gives an error.
+.It Ic notify
+If set, the shell notifies asynchronously of job completions. The
+default is to rather present job completions just before printing
+a prompt.
+.It Ic path
+Each word of the path variable specifies a directory in which
+commands are to be sought for execution.
+A null word specifies the current directory.
+If there is no
+.Ar path
+variable then only full path names will execute.
+The usual search path is `.', `/bin' and `/usr/bin', but this
+may vary from system to system.
+For the super-user the default search path is `/etc', `/bin' and `/usr/bin'.
+A shell which is given neither the
+.Fl c
+nor the
+.Fl t
+option will normally hash the contents of the directories in the
+.Ar path
+variable after reading
+.Ar \&.cshrc ,
+and each time the
+.Ar path
+variable is reset. If new commands are added to these directories
+while the shell is active, it may be necessary to do a
+.Ic rehash
+or the commands may not be found.
+.It Ic prompt
+The string which is printed before each command is read from
+an interactive terminal input.
+If a `!' appears in the string it will be replaced by the current event number
+unless a preceding `\e' is given.
+Default is `% ', or `# ' for the super-user.
+.It Ic savehist
+Is given a numeric value to control the number of entries of the
+history list that are saved in ~/.history when the user logs out.
+Any command which has been referenced in this many events will be saved.
+During start up the shell sources ~/.history into the history list
+enabling history to be saved across logins.
+Too large values of
+.Ar savehist
+will slow down the shell during start up.
+.It Ic shell
+The file in which the shell resides.
+This is used in forking shells to interpret files which have execute
+bits set, but which are not executable by the system.
+(See the description of
+.Sx Non-builtin Command Execution
+below.)
+Initialized to the (system-dependent) home of the shell.
+.It Ic status
+The status returned by the last command.
+If it terminated abnormally, then 0200 is added to the status.
+Builtin commands which fail return exit status `1',
+all other builtin commands set status `0'.
+.It Ic time
+Controls automatic timing of commands.
+If set, then any command which takes more than this many cpu seconds
+will cause a line giving user, system, and real times and a utilization
+percentage which is the ratio of user plus system times to real time
+to be printed when it terminates.
+.It Ic verbose
+Set by the
+.Fl v
+command line option, causes the words of each command to be printed
+after history substitution.
+.El
+.Ss Non-builtin command execution
+When a command to be executed is found to not be a builtin command
+the shell attempts to execute the command via
+.Xr execve 2 .
+Each word in the variable
+.Ar path
+names a directory from which the shell will attempt to execute the command.
+If it is given neither a
+.Fl c
+nor a
+.Fl t
+option, the shell will hash the names in these directories into an internal
+table so that it will only try an
+.Ic exec
+in a directory if there is a possibility that the command resides there.
+This greatly speeds command location when a large number of directories
+are present in the search path.
+If this mechanism has been turned off (via
+.Ic unhash ) ,
+or if the shell was given a
+.Fl c
+or
+.Fl t
+argument, and in any case for each directory component of
+.Ar path
+which does not begin with a `/',
+the shell concatenates with the given command name to form a path name
+of a file which it then attempts to execute.
+.Pp
+Parenthesized commands are always executed in a subshell.
+Thus
+.Pp
+.Dl (cd ; pwd) ; pwd
+.Pp
+prints the
+.Ar home
+directory; leaving you where you were (printing this after the home directory),
+while
+.Pp
+.Dl cd ; pwd
+.Pp
+leaves you in the
+.Ar home
+directory.
+Parenthesized commands are most often used to prevent
+.Ic chdir
+from affecting the current shell.
+.Pp
+If the file has execute permissions but is not an
+executable binary to the system, then it is assumed to be a
+file containing shell commands and a new shell is spawned to read it.
+.Pp
+If there is an
+.Ic alias
+for
+.Ic shell
+then the words of the alias will be prepended to the argument list to form
+the shell command.
+The first word of the
+.Ic alias
+should be the full path name of the shell
+(e.g. `$shell').
+Note that this is a special, late occurring, case of
+.Ic alias
+substitution,
+and only allows words to be prepended to the argument list without modification.
+.Ss Signal handling
+The shell normally ignores
+.Ar quit
+signals.
+Jobs running detached (either by
+.Ic \&&
+or the
+.Ic bg
+or
+.Ic %... &
+commands) are immune to signals generated from the keyboard, including
+hangups.
+Other signals have the values which the shell inherited from its parent.
+The shell's handling of interrupts and terminate signals
+in shell scripts can be controlled by
+.Ic onintr .
+Login shells catch the
+.Ar terminate
+signal; otherwise this signal is passed on to children from the state in the
+shell's parent.
+In no case are interrupts allowed when a login shell is reading the file
+.Pa \&.logout .
+.Sh AUTHOR
+William Joy.
+Job control and directory stack features first implemented by J.E. Kulp of
+IIASA, Laxenburg, Austria,
+with different syntax than that used now.
+File name completion code written by Ken Greer, HP Labs.
+Eight-bit implementation Christos S. Zoulas, Cornell University.
+.Sh FILES
+.Bl -tag -width /etc/passwd -compact
+.It Pa ~/.cshrc
+Read at beginning of execution by each shell.
+.It Pa ~/.login
+Read by login shell, after `.cshrc' at login.
+.It Pa ~/.logout
+Read by login shell, at logout.
+.It Pa /bin/sh
+Standard shell, for shell scripts not starting with a `#'.
+.It Pa /tmp/sh*
+Temporary file for `<<'.
+.It Pa /etc/passwd
+Source of home directories for `~name'.
+.El
+.Sh LIMITATIONS
+Word lengths \-
+Words can be no longer than 1024 characters.
+The system limits argument lists to 10240 characters.
+The number of arguments to a command which involves filename expansion
+is limited to 1/6'th the number of characters allowed in an argument list.
+Command substitutions may substitute no more characters than are
+allowed in an argument list.
+To detect looping, the shell restricts the number of
+.Ic alias
+substitutions on a single line to 20.
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr access 2 ,
+.Xr execve 2 ,
+.Xr fork 2 ,
+.Xr killpg 2 ,
+.Xr pipe 2 ,
+.Xr sigvec 2 ,
+.Xr umask 2 ,
+.Xr setrlimit 2 ,
+.Xr wait 2 ,
+.Xr tty 4 ,
+.Xr a.out 5 ,
+.Xr environ 7 ,
+.br
+.Em An introduction to the C shell
+.Sh HISTORY
+.Nm Csh
+appeared in
+.Bx 3 .
+It
+was a first implementation of a command language interpreter
+incorporating a history mechanism (see
+.Sx History Substitutions ) ,
+job control facilities (see
+.Sx Jobs ) ,
+interactive file name
+and user name completion (see
+.Sx File Name Completion ) ,
+and a C-like syntax.
+There are now many shells which also have these mechanisms, plus
+a few more (and maybe some bugs too), which are available thru the
+usenet, or with
+.Bx
+as contributed software like the
+.Xr ksh korn\ shell .
+.Sh BUGS
+When a command is restarted from a stop,
+the shell prints the directory it started in if this is different
+from the current directory; this can be misleading (i.e. wrong)
+as the job may have changed directories internally.
+.Pp
+Shell builtin functions are not stoppable/restartable.
+Command sequences of the form `a ; b ; c' are also not handled gracefully
+when stopping is attempted. If you suspend `b', the shell will then
+immediately execute `c'. This is especially noticeable if this
+expansion results from an
+.Ar alias .
+It suffices to place the sequence of commands in ()'s to force it to
+a subshell, i.e. `( a ; b ; c )'.
+.Pp
+Control over tty output after processes are started is primitive;
+perhaps this will inspire someone to work on a good virtual
+terminal interface. In a virtual terminal interface much more
+interesting things could be done with output control.
+.Pp
+Alias substitution is most often used to clumsily simulate shell procedures;
+shell procedures should be provided rather than aliases.
+.Pp
+Commands within loops, prompted for by `?', are not placed in the
+.Ic history
+list.
+Control structure should be parsed rather than being recognized as built-in
+commands. This would allow control commands to be placed anywhere,
+to be combined with `\&|', and to be used with `&' and `;' metasyntax.
+.Pp
+It should be possible to use the `:' modifiers on the output of command
+substitutions.
+All and more than one `:' modifier should be allowed on `$' substitutions.
+.Pp
+The way the
+.Ic filec
+facility is implemented is ugly and expensive.
diff --git a/bin/csh/csh.c b/bin/csh/csh.c
new file mode 100644
index 000000000000..d35df2027d78
--- /dev/null
+++ b/bin/csh/csh.c
@@ -0,0 +1,1184 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)csh.c 5.26 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+#include "pathnames.h"
+
+extern bool MapsAreInited;
+extern bool NLSMapsAreInited;
+
+/*
+ * C Shell
+ *
+ * Bill Joy, UC Berkeley, California, USA
+ * October 1978, May 1980
+ *
+ * Jim Kulp, IIASA, Laxenburg, Austria
+ * April 1980
+ *
+ * Christos Zoulas, Cornell University
+ * June, 1991
+ */
+
+Char *dumphist[] = {STRhistory, STRmh, 0, 0};
+Char *loadhist[] = {STRsource, STRmh, STRhistfile, 0};
+
+int nofile = 0;
+bool reenter = 0;
+bool nverbose = 0;
+bool nexececho = 0;
+bool quitit = 0;
+bool fast = 0;
+bool batch = 0;
+bool mflag = 0;
+bool prompt = 1;
+bool enterhist = 0;
+bool tellwhat = 0;
+
+extern char **environ;
+
+static int srccat __P((Char *, Char *));
+static int srcfile __P((char *, bool, bool));
+static void phup __P((int));
+static void srcunit __P((int, bool, bool));
+static void mailchk __P((void));
+static Char **defaultpath __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register Char *cp;
+ register char *tcp;
+ register int f;
+ register char **tempv;
+ struct sigvec osv;
+
+
+ settimes(); /* Immed. estab. timing base */
+
+ /*
+ * Initialize non constant strings
+ */
+#ifdef _PATH_BSHELL
+ STR_BSHELL = SAVE(_PATH_BSHELL);
+#endif
+#ifdef _PATH_CSHELL
+ STR_SHELLPATH = SAVE(_PATH_CSHELL);
+#endif
+ STR_environ = blk2short(environ);
+ environ = short2blk(STR_environ); /* So that we can free it */
+ STR_WORD_CHARS = SAVE(WORD_CHARS);
+
+ HIST = '!';
+ HISTSUB = '^';
+ word_chars = STR_WORD_CHARS;
+
+ tempv = argv;
+ if (eq(str2short(tempv[0]), STRaout)) /* A.out's are quittable */
+ quitit = 1;
+ uid = getuid();
+ gid = getgid();
+ /*
+ * We are a login shell if: 1. we were invoked as -<something> and we had
+ * no arguments 2. or we were invoked only with the -l flag
+ */
+ loginsh = (**tempv == '-' && argc == 1) ||
+ (argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' &&
+ tempv[1][2] == '\0');
+
+ if (loginsh && **tempv != '-') {
+ /*
+ * Mangle the argv space
+ */
+ tempv[1][0] = '\0';
+ tempv[1][1] = '\0';
+ tempv[1] = NULL;
+ for (tcp = *tempv; *tcp++;);
+ for (tcp--; tcp >= *tempv; tcp--)
+ tcp[1] = tcp[0];
+ *++tcp = '-';
+ argc--;
+ }
+ if (loginsh)
+ (void) time(&chktim);
+
+ AsciiOnly = 1;
+#ifdef NLS
+ (void) setlocale(LC_ALL, "");
+ {
+ int k;
+
+ for (k = 0200; k <= 0377 && !Isprint(k); k++);
+ AsciiOnly = k > 0377;
+ }
+#else
+ AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL;
+#endif /* NLS */
+
+ /*
+ * Move the descriptors to safe places. The variable didfds is 0 while we
+ * have only FSH* to work with. When didfds is true, we have 0,1,2 and
+ * prefer to use these.
+ */
+ initdesc();
+
+ /*
+ * Initialize the shell variables. ARGV and PROMPT are initialized later.
+ * STATUS is also munged in several places. CHILD is munged when
+ * forking/waiting
+ */
+ set(STRstatus, Strsave(STR0));
+
+ if ((tcp = getenv("HOME")) != NULL)
+ cp = SAVE(tcp);
+ else
+ cp = NULL;
+
+ if (cp == NULL)
+ fast = 1; /* No home -> can't read scripts */
+ else
+ set(STRhome, cp);
+ dinit(cp); /* dinit thinks that HOME == cwd in a login
+ * shell */
+ /*
+ * Grab other useful things from the environment. Should we grab
+ * everything??
+ */
+ if ((tcp = getenv("LOGNAME")) != NULL ||
+ (tcp = getenv("USER")) != NULL)
+ set(STRuser, SAVE(tcp));
+ if ((tcp = getenv("TERM")) != NULL)
+ set(STRterm, SAVE(tcp));
+
+ /*
+ * Re-initialize path if set in environment
+ */
+ if ((tcp = getenv("PATH")) == NULL)
+ set1(STRpath, defaultpath(), &shvhed);
+ else
+ importpath(SAVE(tcp));
+
+ set(STRshell, Strsave(STR_SHELLPATH));
+
+ doldol = putn((int) getpid()); /* For $$ */
+ shtemp = Strspl(STRtmpsh, doldol); /* For << */
+
+ /*
+ * Record the interrupt states from the parent process. If the parent is
+ * non-interruptible our hand must be forced or we (and our children) won't
+ * be either. Our children inherit termination from our parent. We catch it
+ * only if we are the login shell.
+ */
+ /* parents interruptibility */
+ (void) sigvec(SIGINT, NULL, &osv);
+ parintr = (void (*) ()) osv.sv_handler;
+ (void) sigvec(SIGTERM, NULL, &osv);
+ parterm = (void (*) ()) osv.sv_handler;
+
+ if (loginsh) {
+ (void) signal(SIGHUP, phup); /* exit processing on HUP */
+ (void) signal(SIGXCPU, phup); /* ...and on XCPU */
+ (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */
+ }
+
+ /*
+ * Process the arguments.
+ *
+ * Note that processing of -v/-x is actually delayed till after script
+ * processing.
+ *
+ * We set the first character of our name to be '-' if we are a shell running
+ * interruptible commands. Many programs which examine ps'es use this to
+ * filter such shells out.
+ */
+ argc--, tempv++;
+ while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) {
+ do
+ switch (*tcp++) {
+
+ case 0: /* - Interruptible, no prompt */
+ prompt = 0;
+ setintr = 1;
+ nofile = 1;
+ break;
+
+ case 'b': /* -b Next arg is input file */
+ batch = 1;
+ break;
+
+ case 'c': /* -c Command input from arg */
+ if (argc == 1)
+ xexit(0);
+ argc--, tempv++;
+ arginp = SAVE(tempv[0]);
+ prompt = 0;
+ nofile = 1;
+ break;
+
+ case 'e': /* -e Exit on any error */
+ exiterr = 1;
+ break;
+
+ case 'f': /* -f Fast start */
+ fast = 1;
+ break;
+
+ case 'i': /* -i Interactive, even if !intty */
+ intact = 1;
+ nofile = 1;
+ break;
+
+ case 'm': /* -m read .cshrc (from su) */
+ mflag = 1;
+ break;
+
+ case 'n': /* -n Don't execute */
+ noexec = 1;
+ break;
+
+ case 'q': /* -q (Undoc'd) ... die on quit */
+ quitit = 1;
+ break;
+
+ case 's': /* -s Read from std input */
+ nofile = 1;
+ break;
+
+ case 't': /* -t Read one line from input */
+ onelflg = 2;
+ prompt = 0;
+ nofile = 1;
+ break;
+
+ case 'v': /* -v Echo hist expanded input */
+ nverbose = 1; /* ... later */
+ break;
+
+ case 'x': /* -x Echo just before execution */
+ nexececho = 1; /* ... later */
+ break;
+
+ case 'V': /* -V Echo hist expanded input */
+ setNS(STRverbose); /* NOW! */
+ break;
+
+ case 'X': /* -X Echo just before execution */
+ setNS(STRecho); /* NOW! */
+ break;
+
+ } while (*tcp);
+ tempv++, argc--;
+ }
+
+ if (quitit) /* With all due haste, for debugging */
+ (void) signal(SIGQUIT, SIG_DFL);
+
+ /*
+ * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
+ * arguments the first of them is the name of a shell file from which to
+ * read commands.
+ */
+ if (nofile == 0 && argc > 0) {
+ nofile = open(tempv[0], O_RDONLY);
+ if (nofile < 0) {
+ child = 1; /* So this doesn't return */
+ stderror(ERR_SYSTEM, tempv[0], strerror(errno));
+ }
+ ffile = SAVE(tempv[0]);
+ /*
+ * Replace FSHIN. Handle /dev/std{in,out,err} specially
+ * since once they are closed we cannot open them again.
+ * In that case we use our own saved descriptors
+ */
+ if ((SHIN = dmove(nofile, FSHIN)) < 0)
+ switch(nofile) {
+ case 0:
+ SHIN = FSHIN;
+ break;
+ case 1:
+ SHIN = FSHOUT;
+ break;
+ case 2:
+ SHIN = FSHDIAG;
+ break;
+ default:
+ stderror(ERR_SYSTEM, tempv[0], strerror(errno));
+ break;
+ }
+ (void) ioctl(SHIN, FIOCLEX, NULL);
+ prompt = 0;
+ /* argc not used any more */ tempv++;
+ }
+ intty = isatty(SHIN);
+ intty |= intact;
+ if (intty || (intact && isatty(SHOUT))) {
+ if (!batch && (uid != geteuid() || gid != getegid())) {
+ errno = EACCES;
+ child = 1; /* So this doesn't return */
+ stderror(ERR_SYSTEM, "csh", strerror(errno));
+ }
+ }
+ /*
+ * Decide whether we should play with signals or not. If we are explicitly
+ * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
+ * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
+ * Note that in only the login shell is it likely that parent may have set
+ * signals to be ignored
+ */
+ if (loginsh || intact || intty && isatty(SHOUT))
+ setintr = 1;
+ settell();
+ /*
+ * Save the remaining arguments in argv.
+ */
+ setq(STRargv, blk2short(tempv), &shvhed);
+
+ /*
+ * Set up the prompt.
+ */
+ if (prompt) {
+ set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent));
+ /* that's a meta-questionmark */
+ set(STRprompt2, Strsave(STRmquestion));
+ }
+
+ /*
+ * If we are an interactive shell, then start fiddling with the signals;
+ * this is a tricky game.
+ */
+ shpgrp = getpgrp();
+ opgrp = tpgrp = -1;
+ if (setintr) {
+ **argv = '-';
+ if (!quitit) /* Wary! */
+ (void) signal(SIGQUIT, SIG_IGN);
+ (void) signal(SIGINT, pintr);
+ (void) sigblock(sigmask(SIGINT));
+ (void) signal(SIGTERM, SIG_IGN);
+ if (quitit == 0 && arginp == 0) {
+ (void) signal(SIGTSTP, SIG_IGN);
+ (void) signal(SIGTTIN, SIG_IGN);
+ (void) signal(SIGTTOU, SIG_IGN);
+ /*
+ * Wait till in foreground, in case someone stupidly runs csh &
+ * dont want to try to grab away the tty.
+ */
+ if (isatty(FSHDIAG))
+ f = FSHDIAG;
+ else if (isatty(FSHOUT))
+ f = FSHOUT;
+ else if (isatty(OLDSTD))
+ f = OLDSTD;
+ else
+ f = -1;
+ retry:
+ if ((tpgrp = tcgetpgrp(f)) != -1) {
+ if (tpgrp != shpgrp) {
+ sig_t old = signal(SIGTTIN, SIG_DFL);
+ (void) kill(0, SIGTTIN);
+ (void) signal(SIGTTIN, old);
+ goto retry;
+ }
+ opgrp = shpgrp;
+ shpgrp = getpid();
+ tpgrp = shpgrp;
+ /*
+ * Setpgid will fail if we are a session leader and
+ * mypid == mypgrp (POSIX 4.3.3)
+ */
+ if (opgrp != shpgrp)
+ if (setpgid(0, shpgrp) == -1)
+ goto notty;
+ /*
+ * We do that after we set our process group, to make sure
+ * that the process group belongs to a process in the same
+ * session as the tty (our process and our group) (POSIX 7.2.4)
+ */
+ if (tcsetpgrp(f, shpgrp) == -1)
+ goto notty;
+ (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL);
+ }
+ if (tpgrp == -1) {
+notty:
+ xprintf("Warning: no access to tty (%s).\n", strerror(errno));
+ xprintf("Thus no job control in this shell.\n");
+ }
+ }
+ }
+ if ((setintr == 0) && (parintr == SIG_DFL))
+ setintr = 1;
+ (void) signal(SIGCHLD, pchild); /* while signals not ready */
+
+ /*
+ * Set an exit here in case of an interrupt or error reading the shell
+ * start-up scripts.
+ */
+ reenter = setexit(); /* PWP */
+ haderr = 0; /* In case second time through */
+ if (!fast && reenter == 0) {
+ /* Will have value(STRhome) here because set fast if don't */
+ {
+ int osetintr = setintr;
+ sigset_t omask = sigblock(sigmask(SIGINT));
+
+ setintr = 0;
+#ifdef _PATH_DOTCSHRC
+ (void) srcfile(_PATH_DOTCSHRC, 0, 0);
+#endif
+ if (!fast && !arginp && !onelflg)
+ dohash();
+#ifdef _PATH_DOTLOGIN
+ if (loginsh)
+ (void) srcfile(_PATH_DOTLOGIN, 0, 0);
+#endif
+ (void) sigsetmask(omask);
+ setintr = osetintr;
+ }
+ (void) srccat(value(STRhome), STRsldotcshrc);
+
+ if (!fast && !arginp && !onelflg && !havhash)
+ dohash();
+ if (loginsh)
+ (void) srccat(value(STRhome), STRsldotlogin);
+ dosource(loadhist);
+ }
+
+ /*
+ * Now are ready for the -v and -x flags
+ */
+ if (nverbose)
+ setNS(STRverbose);
+ if (nexececho)
+ setNS(STRecho);
+
+ /*
+ * All the rest of the world is inside this call. The argument to process
+ * indicates whether it should catch "error unwinds". Thus if we are a
+ * interactive shell our call here will never return by being blown past on
+ * an error.
+ */
+ process(setintr);
+
+ /*
+ * Mop-up.
+ */
+ if (intty) {
+ if (loginsh) {
+ xprintf("logout\n");
+ (void) close(SHIN);
+ child = 1;
+ goodbye();
+ }
+ else {
+ xprintf("exit\n");
+ }
+ }
+ rechist();
+ exitstat();
+ return (0);
+}
+
+void
+untty()
+{
+ if (tpgrp > 0) {
+ (void) setpgid(0, opgrp);
+ (void) tcsetpgrp(FSHTTY, opgrp);
+ }
+}
+
+void
+importpath(cp)
+ Char *cp;
+{
+ register int i = 0;
+ register Char *dp;
+ register Char **pv;
+ int c;
+
+ for (dp = cp; *dp; dp++)
+ if (*dp == ':')
+ i++;
+ /*
+ * i+2 where i is the number of colons in the path. There are i+1
+ * directories in the path plus we need room for a zero terminator.
+ */
+ pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char **));
+ dp = cp;
+ i = 0;
+ if (*dp)
+ for (;;) {
+ if ((c = *dp) == ':' || c == 0) {
+ *dp = 0;
+ pv[i++] = Strsave(*cp ? cp : STRdot);
+ if (c) {
+ cp = dp + 1;
+ *dp = ':';
+ }
+ else
+ break;
+ }
+ dp++;
+ }
+ pv[i] = 0;
+ set1(STRpath, pv, &shvhed);
+}
+
+/*
+ * Source to the file which is the catenation of the argument names.
+ */
+static int
+srccat(cp, dp)
+ Char *cp, *dp;
+{
+ register Char *ep = Strspl(cp, dp);
+ char *ptr = short2str(ep);
+
+ xfree((ptr_t) ep);
+ return srcfile(ptr, mflag ? 0 : 1, 0);
+}
+
+/*
+ * Source to a file putting the file descriptor in a safe place (> 2).
+ */
+static int
+srcfile(f, onlyown, flag)
+ char *f;
+ bool onlyown, flag;
+{
+ register int unit;
+
+ if ((unit = open(f, O_RDONLY)) == -1)
+ return 0;
+ unit = dmove(unit, -1);
+
+ (void) ioctl(unit, FIOCLEX, NULL);
+ srcunit(unit, onlyown, flag);
+ return 1;
+}
+
+/*
+ * Source to a unit. If onlyown it must be our file or our group or
+ * we don't chance it. This occurs on ".cshrc"s and the like.
+ */
+int insource;
+static void
+srcunit(unit, onlyown, hflg)
+ register int unit;
+ bool onlyown, hflg;
+{
+ /* We have to push down a lot of state here */
+ /* All this could go into a structure */
+ int oSHIN = -1, oldintty = intty, oinsource = insource;
+ struct whyle *oldwhyl = whyles;
+ Char *ogointr = gointr, *oarginp = arginp;
+ Char *oevalp = evalp, **oevalvec = evalvec;
+ int oonelflg = onelflg;
+ bool oenterhist = enterhist;
+ char OHIST = HIST;
+ bool otell = cantell;
+
+ struct Bin saveB;
+ sigset_t omask;
+ jmp_buf oldexit;
+
+ /* The (few) real local variables */
+ int my_reenter;
+
+ if (unit < 0)
+ return;
+ if (didfds)
+ donefds();
+ if (onlyown) {
+ struct stat stb;
+
+ if (fstat(unit, &stb) < 0) {
+ (void) close(unit);
+ return;
+ }
+ }
+
+ /*
+ * There is a critical section here while we are pushing down the input
+ * stream since we have stuff in different structures. If we weren't
+ * careful an interrupt could corrupt SHIN's Bin structure and kill the
+ * shell.
+ *
+ * We could avoid the critical region by grouping all the stuff in a single
+ * structure and pointing at it to move it all at once. This is less
+ * efficient globally on many variable references however.
+ */
+ insource = 1;
+ getexit(oldexit);
+ omask = 0;
+
+ if (setintr)
+ omask = sigblock(sigmask(SIGINT));
+ /* Setup the new values of the state stuff saved above */
+ bcopy((char *) &B, (char *) &(saveB), sizeof(B));
+ fbuf = NULL;
+ fseekp = feobp = fblocks = 0;
+ oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
+ intty = isatty(SHIN), whyles = 0, gointr = 0;
+ evalvec = 0;
+ evalp = 0;
+ enterhist = hflg;
+ if (enterhist)
+ HIST = '\0';
+
+ /*
+ * Now if we are allowing commands to be interrupted, we let ourselves be
+ * interrupted.
+ */
+ if (setintr)
+ (void) sigsetmask(omask);
+ settell();
+
+ if ((my_reenter = setexit()) == 0)
+ process(0); /* 0 -> blow away on errors */
+
+ if (setintr)
+ (void) sigsetmask(omask);
+ if (oSHIN >= 0) {
+ register int i;
+
+ /* We made it to the new state... free up its storage */
+ /* This code could get run twice but xfree doesn't care */
+ for (i = 0; i < fblocks; i++)
+ xfree((ptr_t) fbuf[i]);
+ xfree((ptr_t) fbuf);
+
+ /* Reset input arena */
+ bcopy((char *) &(saveB), (char *) &B, sizeof(B));
+
+ (void) close(SHIN), SHIN = oSHIN;
+ arginp = oarginp, onelflg = oonelflg;
+ evalp = oevalp, evalvec = oevalvec;
+ intty = oldintty, whyles = oldwhyl, gointr = ogointr;
+ if (enterhist)
+ HIST = OHIST;
+ enterhist = oenterhist;
+ cantell = otell;
+ }
+
+ resexit(oldexit);
+ /*
+ * If process reset() (effectively an unwind) then we must also unwind.
+ */
+ if (my_reenter)
+ stderror(ERR_SILENT);
+ insource = oinsource;
+}
+
+void
+rechist()
+{
+ Char buf[BUFSIZ];
+ int fp, ftmp, oldidfds;
+
+ if (!fast) {
+ if (value(STRsavehist)[0] == '\0')
+ return;
+ (void) Strcpy(buf, value(STRhome));
+ (void) Strcat(buf, STRsldthist);
+ fp = creat(short2str(buf), 0600);
+ if (fp == -1)
+ return;
+ oldidfds = didfds;
+ didfds = 0;
+ ftmp = SHOUT;
+ SHOUT = fp;
+ (void) Strcpy(buf, value(STRsavehist));
+ dumphist[2] = buf;
+ dohist(dumphist);
+ (void) close(fp);
+ SHOUT = ftmp;
+ didfds = oldidfds;
+ }
+}
+
+void
+goodbye()
+{
+ rechist();
+
+ if (loginsh) {
+ (void) signal(SIGQUIT, SIG_IGN);
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGTERM, SIG_IGN);
+ setintr = 0; /* No interrupts after "logout" */
+ if (!(adrof(STRlogout)))
+ set(STRlogout, STRnormal);
+#ifdef _PATH_DOTLOGOUT
+ (void) srcfile(_PATH_DOTLOGOUT, 0, 0);
+#endif
+ if (adrof(STRhome))
+ (void) srccat(value(STRhome), STRsldtlogout);
+ }
+ exitstat();
+}
+
+void
+exitstat()
+{
+
+#ifdef PROF
+ monitor(0);
+#endif
+ /*
+ * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
+ * directly because we poke child here. Otherwise we might continue
+ * unwarrantedly (sic).
+ */
+ child = 1;
+ xexit(getn(value(STRstatus)));
+}
+
+/*
+ * in the event of a HUP we want to save the history
+ */
+static void
+phup(sig)
+int sig;
+{
+ rechist();
+ xexit(sig);
+}
+
+Char *jobargv[2] = {STRjobs, 0};
+
+/*
+ * Catch an interrupt, e.g. during lexical input.
+ * If we are an interactive shell, we reset the interrupt catch
+ * immediately. In any case we drain the shell output,
+ * and finally go through the normal error mechanism, which
+ * gets a chance to make the shell go away.
+ */
+/* ARGSUSED */
+void
+pintr(notused)
+ int notused;
+{
+ pintr1(1);
+}
+
+void
+pintr1(wantnl)
+ bool wantnl;
+{
+ register Char **v;
+ sigset_t omask;
+
+ omask = sigblock((sigset_t) 0);
+ if (setintr) {
+ (void) sigsetmask(omask & ~sigmask(SIGINT));
+ if (pjobs) {
+ pjobs = 0;
+ xprintf("\n");
+ dojobs(jobargv);
+ stderror(ERR_NAME | ERR_INTR);
+ }
+ }
+ (void) sigsetmask(omask & ~sigmask(SIGCHLD));
+ draino();
+ (void) endpwent();
+
+ /*
+ * If we have an active "onintr" then we search for the label. Note that if
+ * one does "onintr -" then we shan't be interruptible so we needn't worry
+ * about that here.
+ */
+ if (gointr) {
+ search(T_GOTO, 0, gointr);
+ timflg = 0;
+ if (v = pargv)
+ pargv = 0, blkfree(v);
+ if (v = gargv)
+ gargv = 0, blkfree(v);
+ reset();
+ }
+ else if (intty && wantnl) {
+ (void) putraw('\r');
+ (void) putraw('\n');
+ }
+ stderror(ERR_SILENT);
+}
+
+/*
+ * Process is the main driving routine for the shell.
+ * It runs all command processing, except for those within { ... }
+ * in expressions (which is run by a routine evalav in sh.exp.c which
+ * is a stripped down process), and `...` evaluation which is run
+ * also by a subset of this code in sh.glob.c in the routine backeval.
+ *
+ * The code here is a little strange because part of it is interruptible
+ * and hence freeing of structures appears to occur when none is necessary
+ * if this is ignored.
+ *
+ * Note that if catch is not set then we will unwind on any error.
+ * If an end-of-file occurs, we return.
+ */
+void
+process(catch)
+ bool catch;
+{
+ jmp_buf osetexit;
+ struct command *t;
+
+ getexit(osetexit);
+ for (;;) {
+ pendjob();
+ paraml.next = paraml.prev = &paraml;
+ paraml.word = STRNULL;
+ t = 0;
+ (void) setexit();
+ justpr = enterhist; /* execute if not entering history */
+
+ /*
+ * Interruptible during interactive reads
+ */
+ if (setintr)
+ (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT));
+
+ /*
+ * For the sake of reset()
+ */
+ freelex(&paraml);
+ freesyn(t);
+ t = 0;
+
+ if (haderr) {
+ if (!catch) {
+ /* unwind */
+ doneinp = 0;
+ resexit(osetexit);
+ reset();
+ }
+ haderr = 0;
+ /*
+ * Every error is eventually caught here or the shell dies. It is
+ * at this point that we clean up any left-over open files, by
+ * closing all but a fixed number of pre-defined files. Thus
+ * routines don't have to worry about leaving files open due to
+ * deeper errors... they will get closed here.
+ */
+ closem();
+ continue;
+ }
+ if (doneinp) {
+ doneinp = 0;
+ break;
+ }
+ if (chkstop)
+ chkstop--;
+ if (neednote)
+ pnote();
+ if (intty && prompt && evalvec == 0) {
+ mailchk();
+ /*
+ * If we are at the end of the input buffer then we are going to
+ * read fresh stuff. Otherwise, we are rereading input and don't
+ * need or want to prompt.
+ */
+ if (fseekp == feobp)
+ printprompt();
+ flush();
+ }
+ if (seterr) {
+ xfree((ptr_t) seterr);
+ seterr = NULL;
+ }
+
+ /*
+ * Echo not only on VERBOSE, but also with history expansion. If there
+ * is a lexical error then we forego history echo.
+ */
+ if (lex(&paraml) && !seterr && intty || adrof(STRverbose)) {
+ haderr = 1;
+ prlex(&paraml);
+ haderr = 0;
+ }
+
+ /*
+ * The parser may lose space if interrupted.
+ */
+ if (setintr)
+ (void) sigblock(sigmask(SIGINT));
+
+ /*
+ * Save input text on the history list if reading in old history, or it
+ * is from the terminal at the top level and not in a loop.
+ *
+ * PWP: entry of items in the history list while in a while loop is done
+ * elsewhere...
+ */
+ if (enterhist || catch && intty && !whyles)
+ savehist(&paraml);
+
+ /*
+ * Print lexical error messages, except when sourcing history lists.
+ */
+ if (!enterhist && seterr)
+ stderror(ERR_OLD);
+
+ /*
+ * If had a history command :p modifier then this is as far as we
+ * should go
+ */
+ if (justpr)
+ reset();
+
+ alias(&paraml);
+
+ /*
+ * Parse the words of the input into a parse tree.
+ */
+ t = syntax(paraml.next, &paraml, 0);
+ if (seterr)
+ stderror(ERR_OLD);
+
+ execute(t, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
+
+ /*
+ * Made it!
+ */
+ freelex(&paraml);
+ freesyn(t);
+ }
+ resexit(osetexit);
+}
+
+void
+dosource(t)
+ register Char **t;
+{
+ register Char *f;
+ bool hflg = 0;
+ Char buf[BUFSIZ];
+
+ t++;
+ if (*t && eq(*t, STRmh)) {
+ if (*++t == NULL)
+ stderror(ERR_NAME | ERR_HFLAG);
+ hflg++;
+ }
+ (void) Strcpy(buf, *t);
+ f = globone(buf, G_ERROR);
+ (void) strcpy((char *) buf, short2str(f));
+ xfree((ptr_t) f);
+ if (!srcfile((char *) buf, 0, hflg) && !hflg)
+ stderror(ERR_SYSTEM, (char *) buf, strerror(errno));
+}
+
+/*
+ * Check for mail.
+ * If we are a login shell, then we don't want to tell
+ * about any mail file unless its been modified
+ * after the time we started.
+ * This prevents us from telling the user things he already
+ * knows, since the login program insists on saying
+ * "You have mail."
+ */
+static void
+mailchk()
+{
+ register struct varent *v;
+ register Char **vp;
+ time_t t;
+ int intvl, cnt;
+ struct stat stb;
+ bool new;
+
+ v = adrof(STRmail);
+ if (v == 0)
+ return;
+ (void) time(&t);
+ vp = v->vec;
+ cnt = blklen(vp);
+ intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
+ if (intvl < 1)
+ intvl = 1;
+ if (chktim + intvl > t)
+ return;
+ for (; *vp; vp++) {
+ if (stat(short2str(*vp), &stb) < 0)
+ continue;
+ new = stb.st_mtime > time0.tv_sec;
+ if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
+ (stb.st_atime < chktim && stb.st_mtime < chktim) ||
+ loginsh && !new)
+ continue;
+ if (cnt == 1)
+ xprintf("You have %smail.\n", new ? "new " : "");
+ else
+ xprintf("%s in %s.\n", new ? "New mail" : "Mail", short2str(*vp));
+ }
+ chktim = t;
+}
+
+/*
+ * Extract a home directory from the password file
+ * The argument points to a buffer where the name of the
+ * user whose home directory is sought is currently.
+ * We write the home directory of the user back there.
+ */
+int
+gethdir(home)
+ Char *home;
+{
+ Char *h;
+ struct passwd *pw;
+
+ /*
+ * Is it us?
+ */
+ if (*home == '\0') {
+ if (h = value(STRhome)) {
+ (void) Strcpy(home, h);
+ return 0;
+ }
+ else
+ return 1;
+ }
+
+ if (pw = getpwnam(short2str(home))) {
+ (void) Strcpy(home, str2short(pw->pw_dir));
+ return 0;
+ }
+ else
+ return 1;
+}
+
+/*
+ * Move the initial descriptors to their eventual
+ * resting places, closin all other units.
+ */
+void
+initdesc()
+{
+
+ didfds = 0; /* 0, 1, 2 aren't set up */
+ (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL);
+ (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL);
+ (void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, NULL);
+ (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL);
+ closem();
+}
+
+
+void
+#ifdef PROF
+done(i)
+#else
+xexit(i)
+#endif
+ int i;
+{
+ untty();
+ _exit(i);
+}
+
+static Char **
+defaultpath()
+{
+ char *ptr;
+ Char **blk, **blkp;
+ struct stat stb;
+
+ blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10);
+
+#define DIRAPPEND(a) \
+ if (stat(ptr = a, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) \
+ *blkp++ = SAVE(ptr)
+
+ DIRAPPEND(_PATH_BIN);
+ DIRAPPEND(_PATH_USRBIN);
+
+#undef DIRAPPEND
+
+ *blkp++ = Strsave(STRdot);
+ *blkp = NULL;
+ return (blk);
+}
+
+void
+printprompt()
+{
+ register Char *cp;
+
+ if (!whyles) {
+ for (cp = value(STRprompt); *cp; cp++)
+ if (*cp == HIST)
+ xprintf("%d", eventno + 1);
+ else {
+ if (*cp == '\\' && cp[1] == HIST)
+ cp++;
+ xputchar(*cp | QUOTE);
+ }
+ }
+ else
+ /*
+ * Prompt for forward reading loop body content.
+ */
+ xprintf("? ");
+ flush();
+}
diff --git a/bin/csh/csh.h b/bin/csh/csh.h
new file mode 100644
index 000000000000..056bbb070b89
--- /dev/null
+++ b/bin/csh/csh.h
@@ -0,0 +1,537 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * @(#)csh.h 5.15 (Berkeley) 6/8/91
+ */
+
+/*
+ * Fundamental definitions which may vary from system to system.
+ *
+ * BUFSIZ The i/o buffering size; also limits word size
+ * MAILINTVL How often to mailcheck; more often is more expensive
+ */
+#ifndef BUFSIZ
+#define BUFSIZ 1024 /* default buffer size */
+#endif /* BUFSIZ */
+
+#define FORKSLEEP 10 /* delay loop on non-interactive fork failure */
+#define MAILINTVL 600 /* 10 minutes */
+
+/*
+ * The shell moves std in/out/diag and the old std input away from units
+ * 0, 1, and 2 so that it is easy to set up these standards for invoked
+ * commands.
+ */
+#define FSHTTY 15 /* /dev/tty when manip pgrps */
+#define FSHIN 16 /* Preferred desc for shell input */
+#define FSHOUT 17 /* ... shell output */
+#define FSHDIAG 18 /* ... shell diagnostics */
+#define FOLDSTD 19 /* ... old std input */
+
+#ifdef PROF
+#define xexit(n) done(n)
+#endif
+
+#ifdef SHORT_STRINGS
+typedef short Char;
+
+#define SAVE(a) (Strsave(str2short(a)))
+#else
+typedef char Char;
+
+#define SAVE(a) (strsave(a))
+#endif
+
+typedef void *ioctl_t; /* Third arg of ioctl */
+
+typedef void *ptr_t;
+
+#include "const.h"
+#include "char.h"
+#include "err.h"
+
+#ifdef SYSMALLOC
+#define xmalloc(i) Malloc(i)
+#define xrealloc(p, i) Realloc(p, i)
+#define xcalloc(n, s) Calloc(n, s)
+#define xfree(p) Free(p)
+#else
+#define xmalloc(i) malloc(i)
+#define xrealloc(p, i) realloc(p, i)
+#define xcalloc(n, s) calloc(n, s)
+#define xfree(p) free(p)
+#endif /* SYSMALLOC */
+
+#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR)
+
+#define SIGN_EXTEND_CHAR(a) \
+ ((a) & 0x80 ? ((int) (a)) | 0xffffff00 : ((int) a) & 0x000000ff)
+
+
+typedef int bool;
+
+#define eq(a, b) (Strcmp(a, b) == 0)
+
+/* globone() flags */
+#define G_ERROR 0 /* default action: error if multiple words */
+#define G_IGNORE 1 /* ignore the rest of the words */
+#define G_APPEND 2 /* make a sentence by cat'ing the words */
+
+/*
+ * Global flags
+ */
+bool chkstop; /* Warned of stopped jobs... allow exit */
+bool didfds; /* Have setup i/o fd's for child */
+bool doneinp; /* EOF indicator after reset from readc */
+bool exiterr; /* Exit if error or non-zero exit status */
+bool child; /* Child shell ... errors cause exit */
+bool haderr; /* Reset was because of an error */
+bool intty; /* Input is a tty */
+bool intact; /* We are interactive... therefore prompt */
+bool justpr; /* Just print because of :p hist mod */
+bool loginsh; /* We are a loginsh -> .login/.logout */
+bool neednote; /* Need to pnotify() */
+bool noexec; /* Don't execute, just syntax check */
+bool pjobs; /* want to print jobs if interrupted */
+bool setintr; /* Set interrupts on/off -> Wait intr... */
+bool timflg; /* Time the next waited for command */
+bool havhash; /* path hashing is available */
+
+#ifdef FILEC
+bool filec; /* doing filename expansion */
+#endif
+
+/*
+ * Global i/o info
+ */
+Char *arginp; /* Argument input for sh -c and internal `xx` */
+int onelflg; /* 2 -> need line for -t, 1 -> exit on read */
+Char *ffile; /* Name of shell file for $0 */
+
+char *seterr; /* Error message from scanner/parser */
+Char *shtemp; /* Temp name for << shell files in /tmp */
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+struct timeval time0; /* Time at which the shell started */
+struct rusage ru0;
+
+/*
+ * Miscellany
+ */
+Char *doldol; /* Character pid for $$ */
+int uid; /* Invokers uid */
+int gid; /* Invokers gid */
+time_t chktim; /* Time mail last checked */
+int shpgrp; /* Pgrp of shell */
+int tpgrp; /* Terminal process group */
+
+/* If tpgrp is -1, leave tty alone! */
+int opgrp; /* Initial pgrp and tty pgrp */
+
+/*
+ * These are declared here because they want to be
+ * initialized in sh.init.c (to allow them to be made readonly)
+ */
+
+extern struct biltins {
+ char *bname;
+ void (*bfunct) ();
+ short minargs, maxargs;
+} bfunc[];
+extern int nbfunc;
+
+extern struct srch {
+ char *s_name;
+ short s_value;
+} srchn[];
+extern int nsrchn;
+
+/*
+ * To be able to redirect i/o for builtins easily, the shell moves the i/o
+ * descriptors it uses away from 0,1,2.
+ * Ideally these should be in units which are closed across exec's
+ * (this saves work) but for version 6, this is not usually possible.
+ * The desired initial values for these descriptors are defined in
+ * local.h.
+ */
+short SHIN; /* Current shell input (script) */
+short SHOUT; /* Shell output */
+short SHDIAG; /* Diagnostic output... shell errs go here */
+short OLDSTD; /* Old standard input (def for cmds) */
+
+/*
+ * Error control
+ *
+ * Errors in scanning and parsing set up an error message to be printed
+ * at the end and complete. Other errors always cause a reset.
+ * Because of source commands and .cshrc we need nested error catches.
+ */
+
+#include <setjmp.h>
+jmp_buf reslab;
+
+#define setexit() (setjmp(reslab))
+#define reset() longjmp(reslab, 1)
+ /* Should use structure assignment here */
+#define getexit(a) bcopy((char *)reslab, ((char *)(a)), sizeof reslab)
+#define resexit(a) bcopy((char *)(a), (char *)reslab, sizeof reslab)
+
+Char *gointr; /* Label for an onintr transfer */
+
+#include <signal.h>
+sig_t parintr; /* Parents interrupt catch */
+sig_t parterm; /* Parents terminate catch */
+
+/*
+ * Lexical definitions.
+ *
+ * All lexical space is allocated dynamically.
+ * The eighth/sizteenth bit of characters is used to prevent recognition,
+ * and eventually stripped.
+ */
+#define META 0200
+#define ASCII 0177
+#ifdef SHORT_STRINGS
+#define CHAR 0377
+#define QUOTE 0100000 /* 16nth char bit used for 'ing */
+#define TRIM 0077777 /* Mask to strip quote bit */
+#else
+#define CHAR 0177
+#define QUOTE 0200 /* Eighth char bit used for 'ing */
+#define TRIM 0177 /* Mask to strip quote bit */
+#endif
+
+int AsciiOnly; /* If set only 7 bits is expected in characters */
+
+/*
+ * Each level of input has a buffered input structure.
+ * There are one or more blocks of buffered input for each level,
+ * exactly one if the input is seekable and tell is available.
+ * In other cases, the shell buffers enough blocks to keep all loops
+ * in the buffer.
+ */
+struct Bin {
+ off_t Bfseekp; /* Seek pointer */
+ off_t Bfbobp; /* Seekp of beginning of buffers */
+ off_t Bfeobp; /* Seekp of end of buffers */
+ short Bfblocks; /* Number of buffer blocks */
+ Char **Bfbuf; /* The array of buffer blocks */
+} B;
+
+#define fseekp B.Bfseekp
+#define fbobp B.Bfbobp
+#define feobp B.Bfeobp
+#define fblocks B.Bfblocks
+#define fbuf B.Bfbuf
+
+/*
+ * The shell finds commands in loops by reseeking the input
+ * For whiles, in particular, it reseeks to the beginning of the
+ * line the while was on; hence the while placement restrictions.
+ */
+off_t lineloc;
+
+bool cantell; /* Is current source tellable ? */
+
+/*
+ * Input lines are parsed into doubly linked circular
+ * lists of words of the following form.
+ */
+struct wordent {
+ Char *word;
+ struct wordent *prev;
+ struct wordent *next;
+};
+
+/*
+ * During word building, both in the initial lexical phase and
+ * when expanding $ variable substitutions, expansion by `!' and `$'
+ * must be inhibited when reading ahead in routines which are themselves
+ * processing `!' and `$' expansion or after characters such as `\' or in
+ * quotations. The following flags are passed to the getC routines
+ * telling them which of these substitutions are appropriate for the
+ * next character to be returned.
+ */
+#define DODOL 1
+#define DOEXCL 2
+#define DOALL DODOL|DOEXCL
+
+/*
+ * Labuf implements a general buffer for lookahead during lexical operations.
+ * Text which is to be placed in the input stream can be stuck here.
+ * We stick parsed ahead $ constructs during initial input,
+ * process id's from `$$', and modified variable values (from qualifiers
+ * during expansion in sh.dol.c) here.
+ */
+Char *lap;
+
+/*
+ * Parser structure
+ *
+ * Each command is parsed to a tree of command structures and
+ * flags are set bottom up during this process, to be propagated down
+ * as needed during the semantics/exeuction pass (sh.sem.c).
+ */
+struct command {
+ short t_dtyp; /* Type of node */
+#define NODE_COMMAND 1 /* t_dcom <t_dlef >t_drit */
+#define NODE_PAREN 2 /* ( t_dspr ) <t_dlef >t_drit */
+#define NODE_PIPE 3 /* t_dlef | t_drit */
+#define NODE_LIST 4 /* t_dlef ; t_drit */
+#define NODE_OR 5 /* t_dlef || t_drit */
+#define NODE_AND 6 /* t_dlef && t_drit */
+ short t_dflg; /* Flags, e.g. F_AMPERSAND|... */
+#define F_SAVE (F_NICE|F_TIME|F_NOHUP) /* save these when re-doing */
+
+#define F_AMPERSAND (1<<0) /* executes in background */
+#define F_APPEND (1<<1) /* output is redirected >> */
+#define F_PIPEIN (1<<2) /* input is a pipe */
+#define F_PIPEOUT (1<<3) /* output is a pipe */
+#define F_NOFORK (1<<4) /* don't fork, last ()ized cmd */
+#define F_NOINTERRUPT (1<<5) /* should be immune from intr's */
+/* spare */
+#define F_STDERR (1<<7) /* redirect unit 2 with unit 1 */
+#define F_OVERWRITE (1<<8) /* output was ! */
+#define F_READ (1<<9) /* input redirection is << */
+#define F_REPEAT (1<<10) /* reexec aft if, repeat,... */
+#define F_NICE (1<<11) /* t_nice is meaningful */
+#define F_NOHUP (1<<12) /* nohup this command */
+#define F_TIME (1<<13) /* time this command */
+ union {
+ Char *T_dlef; /* Input redirect word */
+ struct command *T_dcar; /* Left part of list/pipe */
+ } L;
+ union {
+ Char *T_drit; /* Output redirect word */
+ struct command *T_dcdr; /* Right part of list/pipe */
+ } R;
+#define t_dlef L.T_dlef
+#define t_dcar L.T_dcar
+#define t_drit R.T_drit
+#define t_dcdr R.T_dcdr
+ Char **t_dcom; /* Command/argument vector */
+ struct command *t_dspr; /* Pointer to ()'d subtree */
+ short t_nice;
+};
+
+
+/*
+ * The keywords for the parser
+ */
+#define T_BREAK 0
+#define T_BRKSW 1
+#define T_CASE 2
+#define T_DEFAULT 3
+#define T_ELSE 4
+#define T_END 5
+#define T_ENDIF 6
+#define T_ENDSW 7
+#define T_EXIT 8
+#define T_FOREACH 9
+#define T_GOTO 10
+#define T_IF 11
+#define T_LABEL 12
+#define T_LET 13
+#define T_SET 14
+#define T_SWITCH 15
+#define T_TEST 16
+#define T_THEN 17
+#define T_WHILE 18
+
+/*
+ * Structure defining the existing while/foreach loops at this
+ * source level. Loops are implemented by seeking back in the
+ * input. For foreach (fe), the word list is attached here.
+ */
+struct whyle {
+ off_t w_start; /* Point to restart loop */
+ off_t w_end; /* End of loop (0 if unknown) */
+ Char **w_fe, **w_fe0; /* Current/initial wordlist for fe */
+ Char *w_fename; /* Name for fe */
+ struct whyle *w_next; /* Next (more outer) loop */
+} *whyles;
+
+/*
+ * Variable structure
+ *
+ * Aliases and variables are stored in AVL balanced binary trees.
+ */
+struct varent {
+ Char **vec; /* Array of words which is the value */
+ Char *v_name; /* Name of variable/alias */
+ struct varent *v_link[3]; /* The links, see below */
+ int v_bal; /* Balance factor */
+} shvhed, aliases;
+
+#define v_left v_link[0]
+#define v_right v_link[1]
+#define v_parent v_link[2]
+
+struct varent *adrof1();
+
+#define adrof(v) adrof1(v, &shvhed)
+#define value(v) value1(v, &shvhed)
+
+/*
+ * The following are for interfacing redo substitution in
+ * aliases to the lexical routines.
+ */
+struct wordent *alhistp; /* Argument list (first) */
+struct wordent *alhistt; /* Node after last in arg list */
+Char **alvec; /* The (remnants of) alias vector */
+
+/*
+ * Filename/command name expansion variables
+ */
+short gflag; /* After tglob -> is globbing needed? */
+
+#define MAXVARLEN 30 /* Maximum number of char in a variable name */
+
+/*
+ * Variables for filename expansion
+ */
+extern Char **gargv; /* Pointer to the (stack) arglist */
+extern long gargc; /* Number args in gargv */
+
+/*
+ * Variables for command expansion.
+ */
+extern Char **pargv; /* Pointer to the argv list space */
+extern long pargc; /* Count of arguments in pargv */
+Char *pargs; /* Pointer to start current word */
+long pnleft; /* Number of chars left in pargs */
+Char *pargcp; /* Current index into pargs */
+
+/*
+ * History list
+ *
+ * Each history list entry contains an embedded wordlist
+ * from the scanner, a number for the event, and a reference count
+ * to aid in discarding old entries.
+ *
+ * Essentially "invisible" entries are put on the history list
+ * when history substitution includes modifiers, and thrown away
+ * at the next discarding since their event numbers are very negative.
+ */
+struct Hist {
+ struct wordent Hlex;
+ int Hnum;
+ int Href;
+ long Htime;
+ struct Hist *Hnext;
+} Histlist;
+
+struct wordent paraml; /* Current lexical word list */
+int eventno; /* Next events number */
+int lastev; /* Last event reference (default) */
+
+Char HIST; /* history invocation character */
+Char HISTSUB; /* auto-substitute character */
+
+/*
+ * strings.h:
+ */
+#ifndef SHORT_STRINGS
+#define Strchr(a, b) strchr(a, b)
+#define Strrchr(a, b) strrchr(a, b)
+#define Strcat(a, b) strcat(a, b)
+#define Strncat(a, b, c) strncat(a, b, c)
+#define Strcpy(a, b) strcpy(a, b)
+#define Strncpy(a, b, c) strncpy(a, b, c)
+#define Strlen(a) strlen(a)
+#define Strcmp(a, b) strcmp(a, b)
+#define Strncmp(a, b, c) strncmp(a, b, c)
+
+#define Strspl(a, b) strspl(a, b)
+#define Strsave(a) strsave(a)
+#define Strend(a) strend(a)
+#define Strstr(a, b) strstr(a, b)
+
+#define str2short(a) (a)
+#define blk2short(a) saveblk(a)
+#define short2blk(a) saveblk(a)
+#define short2str(a) (a)
+#define short2qstr(a) (a)
+#else
+#define Strchr(a, b) s_strchr(a, b)
+#define Strrchr(a, b) s_strrchr(a, b)
+#define Strcat(a, b) s_strcat(a, b)
+#define Strncat(a, b, c) s_strncat(a, b, c)
+#define Strcpy(a, b) s_strcpy(a, b)
+#define Strncpy(a, b, c) s_strncpy(a, b, c)
+#define Strlen(a) s_strlen(a)
+#define Strcmp(a, b) s_strcmp(a, b)
+#define Strncmp(a, b, c) s_strncmp(a, b, c)
+
+#define Strspl(a, b) s_strspl(a, b)
+#define Strsave(a) s_strsave(a)
+#define Strend(a) s_strend(a)
+#define Strstr(a, b) s_strstr(a, b)
+#endif
+
+/*
+ * setname is a macro to save space (see sh.err.c)
+ */
+char *bname;
+
+#define setname(a) (bname = (a))
+
+Char *Vsav;
+Char *Vdp;
+Char *Vexpath;
+char **Vt;
+
+Char **evalvec;
+Char *evalp;
+
+extern struct mesg {
+ char *iname; /* name from /usr/include */
+ char *pname; /* print name */
+} mesg[];
+
+/* word_chars is set by default to WORD_CHARS but can be overridden by
+ the worchars variable--if unset, reverts to WORD_CHARS */
+
+Char *word_chars;
+
+#define WORD_CHARS "*?_-.[]~=" /* default chars besides alnums in words */
+
+Char *STR_SHELLPATH;
+
+#include <paths.h>
+#ifdef _PATH_BSHELL
+Char *STR_BSHELL;
+#endif
+Char *STR_WORD_CHARS;
+Char **STR_environ;
diff --git a/bin/csh/dir.c b/bin/csh/dir.c
new file mode 100644
index 000000000000..b9cad32746a8
--- /dev/null
+++ b/bin/csh/dir.c
@@ -0,0 +1,912 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00013
+ * -------------------- ----- ----------------------
+ *
+ * 19 Aug 92 Kevin Lahey Fixed csh segmentation violation
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dir.c 5.12 (Berkeley) 6/27/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "dir.h"
+#include "extern.h"
+
+/* Directory management. */
+
+static struct directory
+ *dfind __P((Char *));
+static Char *dfollow __P((Char *));
+static void printdirs __P((void));
+static Char *dgoto __P((Char *));
+static void dnewcwd __P((struct directory *));
+static void dset __P((Char *));
+
+struct directory dhead; /* "head" of loop */
+int printd; /* force name to be printed */
+
+static int dirflag = 0;
+
+/*
+ * dinit - initialize current working directory
+ */
+void
+dinit(hp)
+ Char *hp;
+{
+ register char *tcp;
+ register Char *cp;
+ register struct directory *dp;
+ char path[MAXPATHLEN];
+ static char *emsg = "csh: Trying to start from \"%s\"\n";
+
+ /* Don't believe the login shell home, because it may be a symlink */
+ tcp = getwd(path); /* see ngetwd.c for System V version */
+ if (tcp == NULL || *tcp == '\0') {
+ (void) xprintf("csh: %s\n", path);
+ if (hp && *hp) {
+ tcp = short2str(hp);
+ (void) xprintf(emsg, tcp);
+ if (chdir(tcp) == -1)
+ cp = NULL;
+ else
+ cp = hp;
+ }
+ else
+ cp = NULL;
+ if (cp == NULL) {
+ (void) xprintf(emsg, "/");
+ if (chdir("/") == -1)
+ /* I am not even try to print an error message! */
+ xexit(1);
+ cp = SAVE("/");
+ }
+ }
+ else {
+ struct stat swd, shp;
+
+ /*
+ * See if $HOME is the working directory we got and use that
+ */
+ if (hp && *hp &&
+ stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
+ swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino)
+ cp = hp;
+ else {
+ char *cwd;
+
+ /*
+ * use PWD if we have it (for subshells)
+ */
+ if (cwd = getenv("PWD")) {
+ if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev &&
+ swd.st_ino == shp.st_ino)
+ tcp = cwd;
+ }
+ /* EWS: dcanon frees its argument; can't just use str2short */
+ cp = dcanon(Strsave(str2short(tcp)), STRNULL);
+ }
+ }
+
+ dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
+ dp->di_name = Strsave(cp);
+ dp->di_count = 0;
+ dhead.di_next = dhead.di_prev = dp;
+ dp->di_next = dp->di_prev = &dhead;
+ printd = 0;
+ dnewcwd(dp);
+}
+
+static void
+dset(dp)
+Char *dp;
+{
+ /*
+ * Don't call set() directly cause if the directory contains ` or
+ * other junk characters glob will fail.
+ */
+ register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **)));
+
+ vec[0] = Strsave(dp);
+ vec[1] = 0;
+ setq(STRcwd, vec, &shvhed);
+ Setenv(STRPWD, dp);
+}
+
+#define DIR_LONG 1
+#define DIR_VERT 2
+#define DIR_LINE 4
+
+static void
+skipargs(v, str)
+ Char ***v;
+ char *str;
+{
+ Char **n = *v, *s;
+
+ dirflag = 0;
+ for (n++; *n != NULL && (*n)[0] == '-'; n++)
+ for (s = &((*n)[1]); *s; s++)
+ switch (*s) {
+ case 'l':
+ dirflag |= DIR_LONG;
+ break;
+ case 'v':
+ dirflag |= DIR_VERT;
+ break;
+ case 'n':
+ dirflag |= DIR_LINE;
+ break;
+ default:
+ stderror(ERR_DIRUS, short2str(**v), str);
+ break;
+ }
+ *v = n;
+}
+
+/*
+ * dodirs - list all directories in directory loop
+ */
+void
+dodirs(v)
+ Char **v;
+{
+ skipargs(&v, "");
+
+ if (*v != NULL)
+ stderror(ERR_DIRUS, "dirs", "");
+ printdirs();
+}
+
+static void
+printdirs()
+{
+ register struct directory *dp;
+ Char *s, *hp = value(STRhome);
+ int idx, len, cur;
+
+ if (*hp == '\0')
+ hp = NULL;
+ dp = dcwd;
+ idx = 0;
+ cur = 0;
+ do {
+ if (dp == &dhead)
+ continue;
+ if (dirflag & DIR_VERT) {
+ xprintf("%d\t", idx++);
+ cur = 0;
+ }
+ if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) &&
+ prefix(hp, dp->di_name))
+ len = Strlen(s = (dp->di_name + Strlen(hp))) + 2;
+ else
+ len = Strlen(s = dp->di_name) + 1;
+
+ cur += len;
+ if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) {
+ xprintf("\n");
+ cur = len;
+ }
+ xprintf(s != dp->di_name ? "~%s%c" : "%s%c",
+ short2str(s), (dirflag & DIR_VERT) ? '\n' : ' ');
+ } while ((dp = dp->di_prev) != dcwd);
+ if (!(dirflag & DIR_VERT))
+ xprintf("\n");
+}
+
+void
+dtildepr(home, dir)
+ register Char *home, *dir;
+{
+
+ if (!eq(home, STRslash) && prefix(home, dir))
+ xprintf("~%s", short2str(dir + Strlen(home)));
+ else
+ xprintf("%s", short2str(dir));
+}
+
+void
+dtilde()
+{
+ struct directory *d = dcwd;
+
+ do {
+ if (d == &dhead)
+ continue;
+ d->di_name = dcanon(d->di_name, STRNULL);
+ } while ((d = d->di_prev) != dcwd);
+
+ dset(dcwd->di_name);
+}
+
+
+/* dnormalize():
+ * If the name starts with . or .. then we might need to normalize
+ * it depending on the symbolic link flags
+ */
+Char *
+dnormalize(cp)
+ Char *cp;
+{
+
+#define UC (unsigned char)
+#define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/')))
+#define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1])))
+
+ if ((unsigned char) cp[0] == '/')
+ return (Strsave(cp));
+
+ if (adrof(STRignore_symlinks)) {
+ int dotdot = 0;
+ Char *dp, *cwd;
+
+ cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) *
+ sizeof(Char)));
+ (void) Strcpy(cwd, dcwd->di_name);
+
+ /*
+ * Ignore . and count ..'s
+ */
+ while (*cp) {
+ if (ISDOT(cp)) {
+ if (*++cp)
+ cp++;
+ }
+ else if (ISDOTDOT(cp)) {
+ dotdot++;
+ cp += 2;
+ if (*cp)
+ cp++;
+ }
+ else
+ break;
+ }
+ while (dotdot > 0)
+ if ((dp = Strrchr(cwd, '/'))) {
+ *dp = '\0';
+ dotdot--;
+ }
+ else
+ break;
+
+ if (*cp) {
+ cwd[dotdot = Strlen(cwd)] = '/';
+ cwd[dotdot + 1] = '\0';
+ dp = Strspl(cwd, cp);
+ xfree((ptr_t) cwd);
+ return dp;
+ }
+ else {
+ if (!*cwd) {
+ cwd[0] = '/';
+ cwd[1] = '\0';
+ }
+ return cwd;
+ }
+ }
+ return Strsave(cp);
+}
+
+/*
+ * dochngd - implement chdir command.
+ */
+void
+dochngd(v)
+ Char **v;
+{
+ register Char *cp;
+ register struct directory *dp;
+
+ skipargs(&v, " [<dir>]");
+ printd = 0;
+ if (*v == NULL) {
+ if ((cp = value(STRhome)) == NULL || *cp == 0)
+ stderror(ERR_NAME | ERR_NOHOMEDIR);
+ if (chdir(short2str(cp)) < 0)
+ stderror(ERR_NAME | ERR_CANTCHANGE);
+ cp = Strsave(cp);
+ }
+ else if (v[1] != NULL) {
+ stderror(ERR_NAME | ERR_TOOMANY);
+ /* NOTREACHED */
+ return;
+ }
+ else if ((dp = dfind(*v)) != 0) {
+ char *tmp;
+
+ printd = 1;
+ if (chdir(tmp = short2str(dp->di_name)) < 0)
+ stderror(ERR_SYSTEM, tmp, strerror(errno));
+ dcwd->di_prev->di_next = dcwd->di_next;
+ dcwd->di_next->di_prev = dcwd->di_prev;
+ dfree(dcwd);
+ dnewcwd(dp);
+ return;
+ }
+ else
+ cp = dfollow(*v);
+ dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
+ dp->di_name = cp;
+ dp->di_count = 0;
+ dp->di_next = dcwd->di_next;
+ dp->di_prev = dcwd->di_prev;
+ dp->di_prev->di_next = dp;
+ dp->di_next->di_prev = dp;
+ dfree(dcwd);
+ dnewcwd(dp);
+}
+
+static Char *
+dgoto(cp)
+ Char *cp;
+{
+ Char *dp;
+
+ if (*cp != '/') {
+ register Char *p, *q;
+ int cwdlen;
+
+ for (p = dcwd->di_name; *p++;);
+ if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */
+ cwdlen = 0;
+ for (p = cp; *p++;);
+ dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
+ for (p = dp, q = dcwd->di_name; *p++ = *q++;);
+ if (cwdlen)
+ p[-1] = '/';
+ else
+ p--; /* don't add a / after root */
+ for (q = cp; *p++ = *q++;);
+ xfree((ptr_t) cp);
+ cp = dp;
+ dp += cwdlen;
+ }
+ else
+ dp = cp;
+
+ cp = dcanon(cp, dp);
+ return cp;
+}
+
+/*
+ * dfollow - change to arg directory; fall back on cdpath if not valid
+ */
+static Char *
+dfollow(cp)
+ register Char *cp;
+{
+ register Char *dp;
+ struct varent *c;
+ char ebuf[MAXPATHLEN];
+ int serrno;
+
+ cp = globone(cp, G_ERROR);
+ /*
+ * if we are ignoring symlinks, try to fix relatives now.
+ */
+ dp = dnormalize(cp);
+ if (chdir(short2str(dp)) >= 0) {
+ xfree((ptr_t) cp);
+ return dgoto(dp);
+ }
+ else {
+ xfree((ptr_t) dp);
+ if (chdir(short2str(cp)) >= 0)
+ return dgoto(cp);
+ serrno = errno;
+ }
+
+ if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
+ && (c = adrof(STRcdpath))) {
+ Char **cdp;
+ register Char *p;
+ Char buf[MAXPATHLEN];
+
+ for (cdp = c->vec; *cdp; cdp++) {
+ for (dp = buf, p = *cdp; *dp++ = *p++;);
+ dp[-1] = '/';
+ for (p = cp; *dp++ = *p++;);
+ if (chdir(short2str(buf)) >= 0) {
+ printd = 1;
+ xfree((ptr_t) cp);
+ cp = Strsave(buf);
+ return dgoto(cp);
+ }
+ }
+ }
+ dp = value(cp);
+ if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
+ xfree((ptr_t) cp);
+ cp = Strsave(dp);
+ printd = 1;
+ return dgoto(cp);
+ }
+ (void) strcpy(ebuf, short2str(cp));
+ xfree((ptr_t) cp);
+ stderror(ERR_SYSTEM, ebuf, strerror(serrno));
+ return (NULL);
+}
+
+
+/*
+ * dopushd - push new directory onto directory stack.
+ * with no arguments exchange top and second.
+ * with numeric argument (+n) bring it to top.
+ */
+void
+dopushd(v)
+ Char **v;
+{
+ register struct directory *dp;
+
+ skipargs(&v, " [<dir>|+<n>]");
+ printd = 1;
+ if (*v == NULL) {
+ char *tmp;
+
+ if ((dp = dcwd->di_prev) == &dhead)
+ dp = dhead.di_prev;
+ if (dp == dcwd)
+ stderror(ERR_NAME | ERR_NODIR);
+ if (chdir(tmp = short2str(dp->di_name)) < 0)
+ stderror(ERR_SYSTEM, tmp, strerror(errno));
+ dp->di_prev->di_next = dp->di_next;
+ dp->di_next->di_prev = dp->di_prev;
+ dp->di_next = dcwd->di_next;
+ dp->di_prev = dcwd;
+ dcwd->di_next->di_prev = dp;
+ dcwd->di_next = dp;
+ }
+ else if (v[1] != NULL) {
+ stderror(ERR_NAME | ERR_TOOMANY);
+ /* NOTREACHED */
+ return;
+ }
+ else if (dp = dfind(*v)) {
+ char *tmp;
+
+ if (chdir(tmp = short2str(dp->di_name)) < 0)
+ stderror(ERR_SYSTEM, tmp, strerror(errno));
+ }
+ else {
+ register Char *ccp;
+
+ ccp = dfollow(*v);
+ dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
+ dp->di_name = ccp;
+ dp->di_count = 0;
+ dp->di_prev = dcwd;
+ dp->di_next = dcwd->di_next;
+ dcwd->di_next = dp;
+ dp->di_next->di_prev = dp;
+ }
+ dnewcwd(dp);
+}
+
+/*
+ * dfind - find a directory if specified by numeric (+n) argument
+ */
+static struct directory *
+dfind(cp)
+ register Char *cp;
+{
+ register struct directory *dp;
+ register int i;
+ register Char *ep;
+
+ if (*cp++ != '+')
+ return (0);
+ for (ep = cp; Isdigit(*ep); ep++)
+ continue;
+ if (*ep)
+ return (0);
+ i = getn(cp);
+ if (i <= 0)
+ return (0);
+ for (dp = dcwd; i != 0; i--) {
+ if ((dp = dp->di_prev) == &dhead)
+ dp = dp->di_prev;
+ if (dp == dcwd)
+ stderror(ERR_NAME | ERR_DEEP);
+ }
+ return (dp);
+}
+
+/*
+ * dopopd - pop a directory out of the directory stack
+ * with a numeric argument just discard it.
+ */
+void
+dopopd(v)
+ Char **v;
+{
+ register struct directory *dp, *p = NULL;
+
+ skipargs(&v, " [+<n>]");
+ printd = 1;
+ if (*v == NULL)
+ dp = dcwd;
+ else if (v[1] != NULL) {
+ stderror(ERR_NAME | ERR_TOOMANY);
+ /* NOTREACHED */
+ return;
+ }
+ else if ((dp = dfind(*v)) == 0)
+ stderror(ERR_NAME | ERR_BADDIR);
+ if (dp->di_prev == &dhead && dp->di_next == &dhead)
+ stderror(ERR_NAME | ERR_EMPTY);
+ if (dp == dcwd) {
+ char *tmp;
+
+ if ((p = dp->di_prev) == &dhead)
+ p = dhead.di_prev;
+ if (chdir(tmp = short2str(p->di_name)) < 0)
+ stderror(ERR_SYSTEM, tmp, strerror(errno));
+ }
+ dp->di_prev->di_next = dp->di_next;
+ dp->di_next->di_prev = dp->di_prev;
+ if (dp == dcwd)
+ dnewcwd(p);
+ else {
+ printdirs();
+ }
+ dfree(dp);
+}
+
+/*
+ * dfree - free the directory (or keep it if it still has ref count)
+ */
+void
+dfree(dp)
+ register struct directory *dp;
+{
+
+ if (dp->di_count != 0) {
+ dp->di_next = dp->di_prev = 0;
+ }
+ else {
+ xfree((char *) dp->di_name);
+ xfree((ptr_t) dp);
+ }
+}
+
+/*
+ * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
+ * we are of course assuming that the file system is standardly
+ * constructed (always have ..'s, directories have links)
+ */
+Char *
+dcanon(cp, p)
+ register Char *cp, *p;
+{
+ register Char *sp;
+ register Char *p1, *p2; /* general purpose */
+ bool slash;
+
+ Char link[MAXPATHLEN];
+ char tlink[MAXPATHLEN];
+ int cc;
+ Char *newcp;
+
+ /*
+ * christos: if the path given does not start with a slash prepend cwd. If
+ * cwd does not start with a path or the result would be too long abort().
+ */
+ if (*cp != '/') {
+ Char tmpdir[MAXPATHLEN];
+
+ p1 = value(STRcwd);
+ if (p1 == NULL || *p1 != '/')
+ abort();
+ if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN)
+ abort();
+ (void) Strcpy(tmpdir, p1);
+ (void) Strcat(tmpdir, STRslash);
+ (void) Strcat(tmpdir, cp);
+ xfree((ptr_t) cp);
+ cp = p = Strsave(tmpdir);
+ }
+
+ while (*p) { /* for each component */
+ sp = p; /* save slash address */
+ while (*++p == '/') /* flush extra slashes */
+ ;
+ if (p != ++sp)
+ for (p1 = sp, p2 = p; *p1++ = *p2++;);
+ p = sp; /* save start of component */
+ slash = 0;
+ while (*++p) /* find next slash or end of path */
+ if (*p == '/') {
+ slash = 1;
+ *p = 0;
+ break;
+ }
+
+ if (*sp == '\0') /* if component is null */
+ if (--sp == cp) /* if path is one char (i.e. /) */
+ break;
+ else
+ *sp = '\0';
+ else if (sp[0] == '.' && sp[1] == 0) {
+ if (slash) {
+ for (p1 = sp, p2 = p + 1; *p1++ = *p2++;);
+ p = --sp;
+ }
+ else if (--sp != cp)
+ *sp = '\0';
+ }
+ else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
+ /*
+ * We have something like "yyy/xxx/..", where "yyy" can be null or
+ * a path starting at /, and "xxx" is a single component. Before
+ * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
+ * symbolic link.
+ */
+ *--sp = 0; /* form the pathname for readlink */
+ if (sp != cp && !adrof(STRignore_symlinks) &&
+ (cc = readlink(short2str(cp), tlink,
+ sizeof tlink)) >= 0) {
+ (void) Strcpy(link, str2short(tlink));
+ link[cc] = '\0';
+
+ if (slash)
+ *p = '/';
+ /*
+ * Point p to the '/' in "/..", and restore the '/'.
+ */
+ *(p = sp) = '/';
+ /*
+ * find length of p
+ */
+ for (p1 = p; *p1++;);
+ if (*link != '/') {
+ /*
+ * Relative path, expand it between the "yyy/" and the
+ * "/..". First, back sp up to the character past "yyy/".
+ */
+ while (*--sp != '/');
+ sp++;
+ *sp = 0;
+ /*
+ * New length is "yyy/" + link + "/.." and rest
+ */
+ p1 = newcp = (Char *) xmalloc((size_t)
+ (((sp - cp) + cc + (p1 - p)) *
+ sizeof(Char)));
+ /*
+ * Copy new path into newcp
+ */
+ for (p2 = cp; *p1++ = *p2++;);
+ for (p1--, p2 = link; *p1++ = *p2++;);
+ for (p1--, p2 = p; *p1++ = *p2++;);
+ /*
+ * Restart canonicalization at expanded "/xxx".
+ */
+ p = sp - cp - 1 + newcp;
+ }
+ else {
+ /*
+ * New length is link + "/.." and rest
+ */
+ p1 = newcp = (Char *) xmalloc((size_t)
+ ((cc + (p1 - p)) * sizeof(Char)));
+ /*
+ * Copy new path into newcp
+ */
+ for (p2 = link; *p1++ = *p2++;);
+ for (p1--, p2 = p; *p1++ = *p2++;);
+ /*
+ * Restart canonicalization at beginning
+ */
+ p = newcp;
+ }
+ xfree((ptr_t) cp);
+ cp = newcp;
+ continue; /* canonicalize the link */
+ }
+ *sp = '/';
+ if (sp != cp)
+ while (*--sp != '/');
+ if (slash) {
+ for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;);
+ p = sp;
+ }
+ else if (cp == sp)
+ *++sp = '\0';
+ else
+ *sp = '\0';
+ }
+ else { /* normal dir name (not . or .. or nothing) */
+
+ if (sp != cp && adrof(STRchase_symlinks) &&
+ !adrof(STRignore_symlinks) &&
+ (cc = readlink(short2str(cp), tlink,
+ sizeof tlink)) >= 0) {
+ (void) Strcpy(link, str2short(tlink));
+ link[cc] = '\0';
+
+ /*
+ * restore the '/'.
+ */
+ if (slash)
+ *p = '/';
+
+ /*
+ * point sp to p (rather than backing up).
+ */
+ sp = p;
+
+ /*
+ * find length of p
+ */
+ for (p1 = p; *p1++;);
+ if (*link != '/') {
+ /*
+ * Relative path, expand it between the "yyy/" and the
+ * remainder. First, back sp up to the character past
+ * "yyy/".
+ */
+ while (*--sp != '/');
+ sp++;
+ *sp = 0;
+ /*
+ * New length is "yyy/" + link + "/.." and rest
+ */
+ p1 = newcp = (Char *) xmalloc((size_t)
+ (((sp - cp) + cc + (p1 - p))
+ * sizeof(Char)));
+ /*
+ * Copy new path into newcp
+ */
+ for (p2 = cp; *p1++ = *p2++;);
+ for (p1--, p2 = link; *p1++ = *p2++;);
+ for (p1--, p2 = p; *p1++ = *p2++;);
+ /*
+ * Restart canonicalization at expanded "/xxx".
+ */
+ p = sp - cp - 1 + newcp;
+ }
+ else {
+ /*
+ * New length is link + the rest
+ */
+ p1 = newcp = (Char *) xmalloc((size_t)
+ ((cc + (p1 - p)) * sizeof(Char)));
+ /*
+ * Copy new path into newcp
+ */
+ for (p2 = link; *p1++ = *p2++;);
+ for (p1--, p2 = p; *p1++ = *p2++;);
+ /*
+ * Restart canonicalization at beginning
+ */
+ p = newcp;
+ }
+ xfree((ptr_t) cp);
+ cp = newcp;
+ continue; /* canonicalize the link */
+ }
+ if (slash)
+ *p = '/';
+ }
+ }
+
+ /*
+ * fix home...
+ */
+ p1 = value(STRhome);
+ cc = Strlen(p1);
+ /*
+ * See if we're not in a subdir of STRhome
+ */
+ if (p1 && *p1 == '/' &&
+ (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) {
+ static ino_t home_ino = -1;
+ static dev_t home_dev = -1;
+ static Char *home_ptr = NULL;
+ struct stat statbuf;
+
+ /*
+ * Get dev and ino of STRhome
+ */
+ if (home_ptr != p1 &&
+ stat(short2str(p1), &statbuf) != -1) {
+ home_dev = statbuf.st_dev;
+ home_ino = statbuf.st_ino;
+ home_ptr = p1;
+ }
+ /*
+ * Start comparing dev & ino backwards
+ */
+ p2 = Strcpy(link, cp);
+ for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) {
+ if (statbuf.st_dev == home_dev &&
+ statbuf.st_ino == home_ino) {
+ sp = (Char *) - 1;
+ break;
+ }
+ if (sp = Strrchr(p2, '/'))
+ *sp = '\0';
+ }
+ /*
+ * See if we found it
+ */
+ if (*p2 && sp == (Char *) -1) {
+ /*
+ * Use STRhome to make '~' work
+ */
+ p2 = cp + Strlen(p2);
+ sp = newcp = (Char *) xmalloc((size_t)
+/* 19 Sep 92*/ ((cc + Strlen(p2) + 1) * sizeof(Char)));
+ while (*p1)
+ *sp++ = *p1++;
+ while (*p2)
+ *sp++ = *p2++;
+ *sp = '\0';
+ xfree((ptr_t) cp);
+ cp = newcp;
+ }
+ }
+ return cp;
+}
+
+
+/*
+ * dnewcwd - make a new directory in the loop the current one
+ */
+static void
+dnewcwd(dp)
+ register struct directory *dp;
+{
+ dcwd = dp;
+ dset(dcwd->di_name);
+ if (printd && !(adrof(STRpushdsilent)))
+ printdirs();
+}
diff --git a/bin/csh/dir.h b/bin/csh/dir.h
new file mode 100644
index 000000000000..8a8f006a8dfd
--- /dev/null
+++ b/bin/csh/dir.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * @(#)dir.h 5.4 (Berkeley) 6/4/91
+ */
+
+/*
+ * Structure for entries in directory stack.
+ */
+struct directory {
+ struct directory *di_next; /* next in loop */
+ struct directory *di_prev; /* prev in loop */
+ unsigned short *di_count; /* refcount of processes */
+ Char *di_name; /* actual name */
+};
+struct directory *dcwd; /* the one we are in now */
diff --git a/bin/csh/dol.c b/bin/csh/dol.c
new file mode 100644
index 000000000000..d2b28f5433b1
--- /dev/null
+++ b/bin/csh/dol.c
@@ -0,0 +1,842 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dol.c 5.13 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+/*
+ * These routines perform variable substitution and quoting via ' and ".
+ * To this point these constructs have been preserved in the divided
+ * input words. Here we expand variables and turn quoting via ' and " into
+ * QUOTE bits on characters (which prevent further interpretation).
+ * If the `:q' modifier was applied during history expansion, then
+ * some QUOTEing may have occurred already, so we dont "trim()" here.
+ */
+
+static int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */
+static Char *Dcp, **Dvp; /* Input vector for Dreadc */
+
+#define DEOF -1
+
+#define unDgetC(c) Dpeekc = c
+
+#define QUOTES (_Q|_Q1|_ESC) /* \ ' " ` */
+
+/*
+ * The following variables give the information about the current
+ * $ expansion, recording the current word position, the remaining
+ * words within this expansion, the count of remaining words, and the
+ * information about any : modifier which is being applied.
+ */
+static Char *dolp; /* Remaining chars from this word */
+static Char **dolnxt; /* Further words */
+static int dolcnt; /* Count of further words */
+static Char dolmod; /* : modifier character */
+static int dolmcnt; /* :gx -> 10000, else 1 */
+
+static void Dfix2 __P((Char **));
+static Char *Dpack __P((Char *, Char *));
+static int Dword __P((void));
+static void dolerror __P((Char *));
+static int DgetC __P((int));
+static void Dgetdol __P((void));
+static void fixDolMod __P((void));
+static void setDolp __P((Char *));
+static void unDredc __P((int));
+static int Dredc __P((void));
+static void Dtestq __P((int));
+
+
+/*
+ * Fix up the $ expansions and quotations in the
+ * argument list to command t.
+ */
+void
+Dfix(t)
+ register struct command *t;
+{
+ register Char **pp;
+ register Char *p;
+
+ if (noexec)
+ return;
+ /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
+ for (pp = t->t_dcom; p = *pp++;)
+ for (; *p; p++) {
+ if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */
+ Dfix2(t->t_dcom); /* found one */
+ blkfree(t->t_dcom);
+ t->t_dcom = gargv;
+ gargv = 0;
+ return;
+ }
+ }
+}
+
+/*
+ * $ substitute one word, for i/o redirection
+ */
+Char *
+Dfix1(cp)
+ register Char *cp;
+{
+ Char *Dv[2];
+
+ if (noexec)
+ return (0);
+ Dv[0] = cp;
+ Dv[1] = NULL;
+ Dfix2(Dv);
+ if (gargc != 1) {
+ setname(short2str(cp));
+ stderror(ERR_NAME | ERR_AMBIG);
+ }
+ cp = Strsave(gargv[0]);
+ blkfree(gargv), gargv = 0;
+ return (cp);
+}
+
+/*
+ * Subroutine to do actual fixing after state initialization.
+ */
+static void
+Dfix2(v)
+ Char **v;
+{
+ ginit(); /* Initialize glob's area pointers */
+ Dvp = v;
+ Dcp = STRNULL; /* Setup input vector for Dreadc */
+ unDgetC(0);
+ unDredc(0); /* Clear out any old peeks (at error) */
+ dolp = 0;
+ dolcnt = 0; /* Clear out residual $ expands (...) */
+ while (Dword())
+ continue;
+}
+
+#define MAXWLEN (BUFSIZ - 4)
+/*
+ * Pack up more characters in this word
+ */
+static Char *
+Dpack(wbuf, wp)
+ Char *wbuf, *wp;
+{
+ register int c;
+ register int i = MAXWLEN - (wp - wbuf);
+
+ for (;;) {
+ c = DgetC(DODOL);
+ if (c == '\\') {
+ c = DgetC(0);
+ if (c == DEOF) {
+ unDredc(c);
+ *wp = 0;
+ Gcat(STRNULL, wbuf);
+ return (NULL);
+ }
+ if (c == '\n')
+ c = ' ';
+ else
+ c |= QUOTE;
+ }
+ if (c == DEOF) {
+ unDredc(c);
+ *wp = 0;
+ Gcat(STRNULL, wbuf);
+ return (NULL);
+ }
+ if (cmap(c, _SP | _NL | _Q | _Q1)) { /* sp \t\n'"` */
+ unDgetC(c);
+ if (cmap(c, QUOTES))
+ return (wp);
+ *wp++ = 0;
+ Gcat(STRNULL, wbuf);
+ return (NULL);
+ }
+ if (--i <= 0)
+ stderror(ERR_WTOOLONG);
+ *wp++ = c;
+ }
+}
+
+/*
+ * Get a word. This routine is analogous to the routine
+ * word() in sh.lex.c for the main lexical input. One difference
+ * here is that we don't get a newline to terminate our expansion.
+ * Rather, DgetC will return a DEOF when we hit the end-of-input.
+ */
+static int
+Dword()
+{
+ register int c, c1;
+ Char wbuf[BUFSIZ];
+ register Char *wp = wbuf;
+ register int i = MAXWLEN;
+ register bool dolflg;
+ bool sofar = 0, done = 0;
+
+ while (!done) {
+ done = 1;
+ c = DgetC(DODOL);
+ switch (c) {
+
+ case DEOF:
+ if (sofar == 0)
+ return (0);
+ /* finish this word and catch the code above the next time */
+ unDredc(c);
+ /* fall into ... */
+
+ case '\n':
+ *wp = 0;
+ Gcat(STRNULL, wbuf);
+ return (1);
+
+ case ' ':
+ case '\t':
+ done = 0;
+ break;
+
+ case '`':
+ /* We preserve ` quotations which are done yet later */
+ *wp++ = c, --i;
+ case '\'':
+ case '"':
+ /*
+ * Note that DgetC never returns a QUOTES character from an
+ * expansion, so only true input quotes will get us here or out.
+ */
+ c1 = c;
+ dolflg = c1 == '"' ? DODOL : 0;
+ for (;;) {
+ c = DgetC(dolflg);
+ if (c == c1)
+ break;
+ if (c == '\n' || c == DEOF)
+ stderror(ERR_UNMATCHED, c1);
+ if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE))
+ --wp, ++i;
+ if (--i <= 0)
+ stderror(ERR_WTOOLONG);
+ switch (c1) {
+
+ case '"':
+ /*
+ * Leave any `s alone for later. Other chars are all
+ * quoted, thus `...` can tell it was within "...".
+ */
+ *wp++ = c == '`' ? '`' : c | QUOTE;
+ break;
+
+ case '\'':
+ /* Prevent all further interpretation */
+ *wp++ = c | QUOTE;
+ break;
+
+ case '`':
+ /* Leave all text alone for later */
+ *wp++ = c;
+ break;
+ }
+ }
+ if (c1 == '`')
+ *wp++ = '`', --i;
+ sofar = 1;
+ if ((wp = Dpack(wbuf, wp)) == NULL)
+ return (1);
+ else {
+ i = MAXWLEN - (wp - wbuf);
+ done = 0;
+ }
+ break;
+
+ case '\\':
+ c = DgetC(0); /* No $ subst! */
+ if (c == '\n' || c == DEOF) {
+ done = 0;
+ break;
+ }
+ c |= QUOTE;
+ break;
+ }
+ if (done) {
+ unDgetC(c);
+ sofar = 1;
+ if ((wp = Dpack(wbuf, wp)) == NULL)
+ return (1);
+ else {
+ i = MAXWLEN - (wp - wbuf);
+ done = 0;
+ }
+ }
+ }
+ /* Really NOTREACHED */
+ return (0);
+}
+
+
+/*
+ * Get a character, performing $ substitution unless flag is 0.
+ * Any QUOTES character which is returned from a $ expansion is
+ * QUOTEd so that it will not be recognized above.
+ */
+static int
+DgetC(flag)
+ register int flag;
+{
+ register int c;
+
+top:
+ if (c = Dpeekc) {
+ Dpeekc = 0;
+ return (c);
+ }
+ if (lap) {
+ c = *lap++ & (QUOTE | TRIM);
+ if (c == 0) {
+ lap = 0;
+ goto top;
+ }
+quotspec:
+ if (cmap(c, QUOTES))
+ return (c | QUOTE);
+ return (c);
+ }
+ if (dolp) {
+ if (c = *dolp++ & (QUOTE | TRIM))
+ goto quotspec;
+ if (dolcnt > 0) {
+ setDolp(*dolnxt++);
+ --dolcnt;
+ return (' ');
+ }
+ dolp = 0;
+ }
+ if (dolcnt > 0) {
+ setDolp(*dolnxt++);
+ --dolcnt;
+ goto top;
+ }
+ c = Dredc();
+ if (c == '$' && flag) {
+ Dgetdol();
+ goto top;
+ }
+ return (c);
+}
+
+static Char *nulvec[] = {0};
+static struct varent nulargv = {nulvec, STRargv, 0};
+
+static void
+dolerror(s)
+ Char *s;
+{
+ setname(short2str(s));
+ stderror(ERR_NAME | ERR_RANGE);
+}
+
+/*
+ * Handle the multitudinous $ expansion forms.
+ * Ugh.
+ */
+static void
+Dgetdol()
+{
+ register Char *np;
+ register struct varent *vp = NULL;
+ Char name[4 * MAXVARLEN + 1];
+ int c, sc;
+ int subscr = 0, lwb = 1, upb = 0;
+ bool dimen = 0, bitset = 0;
+ char tnp;
+ Char wbuf[BUFSIZ];
+
+ dolmod = dolmcnt = 0;
+ c = sc = DgetC(0);
+ if (c == '{')
+ c = DgetC(0); /* sc is { to take } later */
+ if ((c & TRIM) == '#')
+ dimen++, c = DgetC(0); /* $# takes dimension */
+ else if (c == '?')
+ bitset++, c = DgetC(0); /* $? tests existence */
+ switch (c) {
+
+ case '$':
+ if (dimen || bitset)
+ stderror(ERR_SYNTAX);
+ setDolp(doldol);
+ goto eatbrac;
+
+ case '<' | QUOTE:
+ if (bitset)
+ stderror(ERR_NOTALLOWED, "$?<");
+ if (dimen)
+ stderror(ERR_NOTALLOWED, "$?#");
+ for (np = wbuf; read(OLDSTD, &tnp, 1) == 1; np++) {
+ *np = tnp;
+ if (np >= &wbuf[BUFSIZ - 1])
+ stderror(ERR_LTOOLONG);
+ if (SIGN_EXTEND_CHAR(tnp) <= 0 || tnp == '\n')
+ break;
+ }
+ *np = 0;
+ /*
+ * KLUDGE: dolmod is set here because it will cause setDolp to call
+ * domod and thus to copy wbuf. Otherwise setDolp would use it
+ * directly. If we saved it ourselves, no one would know when to free
+ * it. The actual function of the 'q' causes filename expansion not to
+ * be done on the interpolated value.
+ */
+ dolmod = 'q';
+ dolmcnt = 10000;
+ setDolp(wbuf);
+ goto eatbrac;
+
+ case DEOF:
+ case '\n':
+ stderror(ERR_SYNTAX);
+ /* NOTREACHED */
+ break;
+
+ case '*':
+ (void) Strcpy(name, STRargv);
+ vp = adrof(STRargv);
+ subscr = -1; /* Prevent eating [...] */
+ break;
+
+ default:
+ np = name;
+ if (Isdigit(c)) {
+ if (dimen)
+ stderror(ERR_NOTALLOWED, "$#<num>");
+ subscr = 0;
+ do {
+ subscr = subscr * 10 + c - '0';
+ c = DgetC(0);
+ } while (Isdigit(c));
+ unDredc(c);
+ if (subscr < 0) {
+ dolerror(vp->v_name);
+ return;
+ }
+ if (subscr == 0) {
+ if (bitset) {
+ dolp = ffile ? STR1 : STR0;
+ goto eatbrac;
+ }
+ if (ffile == 0)
+ stderror(ERR_DOLZERO);
+ fixDolMod();
+ setDolp(ffile);
+ goto eatbrac;
+ }
+ if (bitset)
+ stderror(ERR_DOLQUEST);
+ vp = adrof(STRargv);
+ if (vp == 0) {
+ vp = &nulargv;
+ goto eatmod;
+ }
+ break;
+ }
+ if (!alnum(c))
+ stderror(ERR_VARALNUM);
+ for (;;) {
+ *np++ = c;
+ c = DgetC(0);
+ if (!alnum(c))
+ break;
+ if (np >= &name[MAXVARLEN])
+ stderror(ERR_VARTOOLONG);
+ }
+ *np++ = 0;
+ unDredc(c);
+ vp = adrof(name);
+ }
+ if (bitset) {
+ dolp = (vp || getenv(short2str(name))) ? STR1 : STR0;
+ goto eatbrac;
+ }
+ if (vp == 0) {
+ np = str2short(getenv(short2str(name)));
+ if (np) {
+ fixDolMod();
+ setDolp(np);
+ goto eatbrac;
+ }
+ udvar(name);
+ /* NOTREACHED */
+ }
+ c = DgetC(0);
+ upb = blklen(vp->vec);
+ if (dimen == 0 && subscr == 0 && c == '[') {
+ np = name;
+ for (;;) {
+ c = DgetC(DODOL); /* Allow $ expand within [ ] */
+ if (c == ']')
+ break;
+ if (c == '\n' || c == DEOF)
+ stderror(ERR_INCBR);
+ if (np >= &name[sizeof(name) / sizeof(Char) - 2])
+ stderror(ERR_VARTOOLONG);
+ *np++ = c;
+ }
+ *np = 0, np = name;
+ if (dolp || dolcnt) /* $ exp must end before ] */
+ stderror(ERR_EXPORD);
+ if (!*np)
+ stderror(ERR_SYNTAX);
+ if (Isdigit(*np)) {
+ int i;
+
+ for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0');
+ if ((i < 0 || i > upb) && !any("-*", *np)) {
+ dolerror(vp->v_name);
+ return;
+ }
+ lwb = i;
+ if (!*np)
+ upb = lwb, np = STRstar;
+ }
+ if (*np == '*')
+ np++;
+ else if (*np != '-')
+ stderror(ERR_MISSING, '-');
+ else {
+ register int i = upb;
+
+ np++;
+ if (Isdigit(*np)) {
+ i = 0;
+ while (Isdigit(*np))
+ i = i * 10 + *np++ - '0';
+ if (i < 0 || i > upb) {
+ dolerror(vp->v_name);
+ return;
+ }
+ }
+ if (i < lwb)
+ upb = lwb - 1;
+ else
+ upb = i;
+ }
+ if (lwb == 0) {
+ if (upb != 0) {
+ dolerror(vp->v_name);
+ return;
+ }
+ upb = -1;
+ }
+ if (*np)
+ stderror(ERR_SYNTAX);
+ }
+ else {
+ if (subscr > 0)
+ if (subscr > upb)
+ lwb = 1, upb = 0;
+ else
+ lwb = upb = subscr;
+ unDredc(c);
+ }
+ if (dimen) {
+ Char *cp = putn(upb - lwb + 1);
+
+ addla(cp);
+ xfree((ptr_t) cp);
+ }
+ else {
+eatmod:
+ fixDolMod();
+ dolnxt = &vp->vec[lwb - 1];
+ dolcnt = upb - lwb + 1;
+ }
+eatbrac:
+ if (sc == '{') {
+ c = Dredc();
+ if (c != '}')
+ stderror(ERR_MISSING, '}');
+ }
+}
+
+static void
+fixDolMod()
+{
+ register int c;
+
+ c = DgetC(0);
+ if (c == ':') {
+ c = DgetC(0), dolmcnt = 1;
+ if (c == 'g')
+ c = DgetC(0), dolmcnt = 10000;
+ if (!any("htrqxe", c))
+ stderror(ERR_BADMOD, c);
+ dolmod = c;
+ if (c == 'q')
+ dolmcnt = 10000;
+ }
+ else
+ unDredc(c);
+}
+
+static void
+setDolp(cp)
+ register Char *cp;
+{
+ register Char *dp;
+
+ if (dolmod == 0 || dolmcnt == 0) {
+ dolp = cp;
+ return;
+ }
+ dp = domod(cp, dolmod);
+ if (dp) {
+ dolmcnt--;
+ addla(dp);
+ xfree((ptr_t) dp);
+ }
+ else
+ addla(cp);
+ dolp = STRNULL;
+ if (seterr)
+ stderror(ERR_OLD);
+}
+
+static void
+unDredc(c)
+ int c;
+{
+
+ Dpeekrd = c;
+}
+
+static int
+Dredc()
+{
+ register int c;
+
+ if (c = Dpeekrd) {
+ Dpeekrd = 0;
+ return (c);
+ }
+ if (Dcp && (c = *Dcp++))
+ return (c & (QUOTE | TRIM));
+ if (*Dvp == 0) {
+ Dcp = 0;
+ return (DEOF);
+ }
+ Dcp = *Dvp++;
+ return (' ');
+}
+
+static void
+Dtestq(c)
+ register int c;
+{
+
+ if (cmap(c, QUOTES))
+ gflag = 1;
+}
+
+/*
+ * Form a shell temporary file (in unit 0) from the words
+ * of the shell input up to EOF or a line the same as "term".
+ * Unit 0 should have been closed before this call.
+ */
+void
+heredoc(term)
+ Char *term;
+{
+ register int c;
+ Char *Dv[2];
+ Char obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ];
+ int ocnt, lcnt, mcnt;
+ register Char *lbp, *obp, *mbp;
+ Char **vp;
+ bool quoted;
+ char *tmp;
+
+ if (creat(tmp = short2str(shtemp), 0600) < 0)
+ stderror(ERR_SYSTEM, tmp, strerror(errno));
+ (void) close(0);
+ if (open(tmp, O_RDWR) < 0) {
+ int oerrno = errno;
+
+ (void) unlink(tmp);
+ errno = oerrno;
+ stderror(ERR_SYSTEM, tmp, strerror(errno));
+ }
+ (void) unlink(tmp); /* 0 0 inode! */
+ Dv[0] = term;
+ Dv[1] = NULL;
+ gflag = 0;
+ trim(Dv);
+ rscan(Dv, Dtestq);
+ quoted = gflag;
+ ocnt = BUFSIZ;
+ obp = obuf;
+ for (;;) {
+ /*
+ * Read up a line
+ */
+ lbp = lbuf;
+ lcnt = BUFSIZ - 4;
+ for (;;) {
+ c = readc(1); /* 1 -> Want EOF returns */
+ if (c < 0 || c == '\n')
+ break;
+ if (c &= TRIM) {
+ *lbp++ = c;
+ if (--lcnt < 0) {
+ setname("<<");
+ stderror(ERR_NAME | ERR_OVERFLOW);
+ }
+ }
+ }
+ *lbp = 0;
+
+ /*
+ * Check for EOF or compare to terminator -- before expansion
+ */
+ if (c < 0 || eq(lbuf, term)) {
+ (void) write(0, short2str(obuf), (size_t) (BUFSIZ - ocnt));
+ (void) lseek(0, 0l, L_SET);
+ return;
+ }
+
+ /*
+ * If term was quoted or -n just pass it on
+ */
+ if (quoted || noexec) {
+ *lbp++ = '\n';
+ *lbp = 0;
+ for (lbp = lbuf; c = *lbp++;) {
+ *obp++ = c;
+ if (--ocnt == 0) {
+ (void) write(0, short2str(obuf), BUFSIZ);
+ obp = obuf;
+ ocnt = BUFSIZ;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * Term wasn't quoted so variable and then command expand the input
+ * line
+ */
+ Dcp = lbuf;
+ Dvp = Dv + 1;
+ mbp = mbuf;
+ mcnt = BUFSIZ - 4;
+ for (;;) {
+ c = DgetC(DODOL);
+ if (c == DEOF)
+ break;
+ if ((c &= TRIM) == 0)
+ continue;
+ /* \ quotes \ $ ` here */
+ if (c == '\\') {
+ c = DgetC(0);
+ if (!any("$\\`", c))
+ unDgetC(c | QUOTE), c = '\\';
+ else
+ c |= QUOTE;
+ }
+ *mbp++ = c;
+ if (--mcnt == 0) {
+ setname("<<");
+ stderror(ERR_NAME | ERR_OVERFLOW);
+ }
+ }
+ *mbp++ = 0;
+
+ /*
+ * If any ` in line do command substitution
+ */
+ mbp = mbuf;
+ if (any(short2str(mbp), '`')) {
+ /*
+ * 1 arg to dobackp causes substitution to be literal. Words are
+ * broken only at newlines so that all blanks and tabs are
+ * preserved. Blank lines (null words) are not discarded.
+ */
+ vp = dobackp(mbuf, 1);
+ }
+ else
+ /* Setup trivial vector similar to return of dobackp */
+ Dv[0] = mbp, Dv[1] = NULL, vp = Dv;
+
+ /*
+ * Resurrect the words from the command substitution each separated by
+ * a newline. Note that the last newline of a command substitution
+ * will have been discarded, but we put a newline after the last word
+ * because this represents the newline after the last input line!
+ */
+ for (; *vp; vp++) {
+ for (mbp = *vp; *mbp; mbp++) {
+ *obp++ = *mbp & TRIM;
+ if (--ocnt == 0) {
+ (void) write(0, short2str(obuf), BUFSIZ);
+ obp = obuf;
+ ocnt = BUFSIZ;
+ }
+ }
+ *obp++ = '\n';
+ if (--ocnt == 0) {
+ (void) write(0, short2str(obuf), BUFSIZ);
+ obp = obuf;
+ ocnt = BUFSIZ;
+ }
+ }
+ if (pargv)
+ blkfree(pargv), pargv = 0;
+ }
+}
diff --git a/bin/csh/err.c b/bin/csh/err.c
new file mode 100644
index 000000000000..060686158c08
--- /dev/null
+++ b/bin/csh/err.c
@@ -0,0 +1,410 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)err.c 5.10 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+char *seterr = NULL; /* Holds last error if there was one */
+
+#define ERR_FLAGS 0xf0000000
+#define ERR_NAME 0x10000000
+#define ERR_SILENT 0x20000000
+#define ERR_OLD 0x40000000
+
+static char *errorlist[] =
+{
+#define ERR_SYNTAX 0
+ "Syntax Error",
+#define ERR_NOTALLOWED 1
+ "%s is not allowed",
+#define ERR_WTOOLONG 2
+ "Word too long",
+#define ERR_LTOOLONG 3
+ "$< line too long",
+#define ERR_DOLZERO 4
+ "No file for $0",
+#define ERR_DOLQUEST 5
+ "$? not allowed here",
+#define ERR_INCBR 6
+ "Incomplete [] modifier",
+#define ERR_EXPORD 7
+ "$ expansion must end before ]",
+#define ERR_BADMOD 8
+ "Bad : modifier in $ (%c)",
+#define ERR_SUBSCRIPT 9
+ "Subscript error",
+#define ERR_BADNUM 10
+ "Badly formed number",
+#define ERR_NOMORE 11
+ "No more words",
+#define ERR_FILENAME 12
+ "Missing file name",
+#define ERR_GLOB 13
+ "Internal glob error",
+#define ERR_COMMAND 14
+ "Command not found",
+#define ERR_TOOFEW 15
+ "Too few arguments",
+#define ERR_TOOMANY 16
+ "Too many arguments",
+#define ERR_DANGER 17
+ "Too dangerous to alias that",
+#define ERR_EMPTYIF 18
+ "Empty if",
+#define ERR_IMPRTHEN 19
+ "Improper then",
+#define ERR_NOPAREN 20
+ "Words not parenthesized",
+#define ERR_NOTFOUND 21
+ "%s not found",
+#define ERR_MASK 22
+ "Improper mask",
+#define ERR_LIMIT 23
+ "No such limit",
+#define ERR_TOOLARGE 24
+ "Argument too large",
+#define ERR_SCALEF 25
+ "Improper or unknown scale factor",
+#define ERR_UNDVAR 26
+ "Undefined variable",
+#define ERR_DEEP 27
+ "Directory stack not that deep",
+#define ERR_BADSIG 28
+ "Bad signal number",
+#define ERR_UNKSIG 29
+ "Unknown signal; kill -l lists signals",
+#define ERR_VARBEGIN 30
+ "Variable name must begin with a letter",
+#define ERR_VARTOOLONG 31
+ "Variable name too long",
+#define ERR_VARALNUM 32
+ "Variable name must contain alphanumeric characters",
+#define ERR_JOBCONTROL 33
+ "No job control in this shell",
+#define ERR_EXPRESSION 34
+ "Expression Syntax",
+#define ERR_NOHOMEDIR 35
+ "No home directory",
+#define ERR_CANTCHANGE 36
+ "Can't change to home directory",
+#define ERR_NULLCOM 37
+ "Invalid null command",
+#define ERR_ASSIGN 38
+ "Assignment missing expression",
+#define ERR_UNKNOWNOP 39
+ "Unknown operator",
+#define ERR_AMBIG 40
+ "Ambiguous",
+#define ERR_EXISTS 41
+ "%s: File exists",
+#define ERR_INTR 42
+ "Interrupted",
+#define ERR_RANGE 43
+ "Subscript out of range",
+#define ERR_OVERFLOW 44
+ "Line overflow",
+#define ERR_VARMOD 45
+ "Unknown variable modifier",
+#define ERR_NOSUCHJOB 46
+ "No such job",
+#define ERR_TERMINAL 47
+ "Can't from terminal",
+#define ERR_NOTWHILE 48
+ "Not in while/foreach",
+#define ERR_NOPROC 49
+ "No more processes",
+#define ERR_NOMATCH 50
+ "No match",
+#define ERR_MISSING 51
+ "Missing %c",
+#define ERR_UNMATCHED 52
+ "Unmatched %c",
+#define ERR_NOMEM 53
+ "Out of memory",
+#define ERR_PIPE 54
+ "Can't make pipe",
+#define ERR_SYSTEM 55
+ "%s: %s",
+#define ERR_STRING 56
+ "%s",
+#define ERR_JOBS 57
+ "Usage: jobs [ -l ]",
+#define ERR_JOBARGS 58
+ "Arguments should be jobs or process id's",
+#define ERR_JOBCUR 59
+ "No current job",
+#define ERR_JOBPREV 60
+ "No previous job",
+#define ERR_JOBPAT 61
+ "No job matches pattern",
+#define ERR_NESTING 62
+ "Fork nesting > %d; maybe `...` loop",
+#define ERR_JOBCTRLSUB 63
+ "No job control in subshells",
+#define ERR_BADPLPS 64
+ "Badly placed ()'s",
+#define ERR_STOPPED 65
+ "%sThere are suspended jobs",
+#define ERR_NODIR 66
+ "No other directory",
+#define ERR_EMPTY 67
+ "Directory stack empty",
+#define ERR_BADDIR 68
+ "Bad directory",
+#define ERR_DIRUS 69
+ "Usage: %s [-lvn]%s",
+#define ERR_HFLAG 70
+ "No operand for -h flag",
+#define ERR_NOTLOGIN 71
+ "Not a login shell",
+#define ERR_DIV0 72
+ "Division by 0",
+#define ERR_MOD0 73
+ "Mod by 0",
+#define ERR_BADSCALE 74
+ "Bad scaling; did you mean \"%s\"?",
+#define ERR_SUSPLOG 75
+ "Can't suspend a login shell (yet)",
+#define ERR_UNKUSER 76
+ "Unknown user: %s",
+#define ERR_NOHOME 77
+ "No $home variable set",
+#define ERR_HISTUS 78
+ "Usage: history [-rht] [# number of events]",
+#define ERR_SPDOLLT 79
+ "$ or < not allowed with $# or $?",
+#define ERR_NEWLINE 80
+ "Newline in variable name",
+#define ERR_SPSTAR 81
+ "* not allowed with $# or $?",
+#define ERR_DIGIT 82
+ "$?<digit> or $#<digit> not allowed",
+#define ERR_VARILL 83
+ "Illegal variable name",
+#define ERR_NLINDEX 84
+ "Newline in variable index",
+#define ERR_EXPOVFL 85
+ "Expansion buffer overflow",
+#define ERR_VARSYN 86
+ "Variable syntax",
+#define ERR_BADBANG 87
+ "Bad ! form",
+#define ERR_NOSUBST 88
+ "No previous substitute",
+#define ERR_BADSUBST 89
+ "Bad substitute",
+#define ERR_LHS 90
+ "No previous left hand side",
+#define ERR_RHSLONG 91
+ "Right hand side too long",
+#define ERR_BADBANGMOD 92
+ "Bad ! modifier: %c",
+#define ERR_MODFAIL 93
+ "Modifier failed",
+#define ERR_SUBOVFL 94
+ "Substitution buffer overflow",
+#define ERR_BADBANGARG 95
+ "Bad ! arg selector",
+#define ERR_NOSEARCH 96
+ "No prev search",
+#define ERR_NOEVENT 97
+ "%s: Event not found",
+#define ERR_TOOMANYRP 98
+ "Too many )'s",
+#define ERR_TOOMANYLP 99
+ "Too many ('s",
+#define ERR_BADPLP 100
+ "Badly placed (",
+#define ERR_MISRED 101
+ "Missing name for redirect",
+#define ERR_OUTRED 102
+ "Ambiguous output redirect",
+#define ERR_REDPAR 103
+ "Can't << within ()'s",
+#define ERR_INRED 104
+ "Ambiguous input redirect",
+#define ERR_ALIASLOOP 105
+ "Alias loop",
+#define ERR_HISTLOOP 106
+ "!# History loop",
+#define ERR_ARCH 107
+ "%s: %s. Wrong Architecture",
+#define ERR_FILEINQ 108
+ "Malformed file inquiry",
+#define ERR_SELOVFL 109
+ "Selector overflow",
+#define ERR_INVALID 110
+ "Invalid Error"
+};
+
+/*
+ * The parser and scanner set up errors for later by calling seterr,
+ * which sets the variable err as a side effect; later to be tested,
+ * e.g. in process.
+ */
+void
+#if __STDC__
+seterror(int id, ...)
+#else
+seterror(id, va_alist)
+ int id;
+ va_dcl
+#endif
+{
+ if (seterr == 0) {
+ char berr[BUFSIZ];
+ va_list va;
+
+#if __STDC__
+ va_start(va, id);
+#else
+ va_start(va);
+#endif
+ if (id < 0 || id > sizeof(errorlist) / sizeof(errorlist[0]))
+ id = ERR_INVALID;
+ xvsprintf(berr, errorlist[id], va);
+ va_end(va);
+
+ seterr = strsave(berr);
+ }
+}
+
+/*
+ * Print the error with the given id.
+ *
+ * Special ids:
+ * ERR_SILENT: Print nothing.
+ * ERR_OLD: Print the previously set error if one was there.
+ * otherwise return.
+ * ERR_NAME: If this bit is set, print the name of the function
+ * in bname
+ *
+ * This routine always resets or exits. The flag haderr
+ * is set so the routine who catches the unwind can propogate
+ * it if they want.
+ *
+ * Note that any open files at the point of error will eventually
+ * be closed in the routine process in sh.c which is the only
+ * place error unwinds are ever caught.
+ */
+void
+#if __STDC__
+stderror(int id, ...)
+#else
+stderror(id, va_alist)
+ int id;
+ va_dcl
+#endif
+{
+ va_list va;
+ register Char **v;
+ int flags = id & ERR_FLAGS;
+
+ id &= ~ERR_FLAGS;
+
+ if ((flags & ERR_OLD) && seterr == NULL)
+ return;
+
+ if (id < 0 || id > sizeof(errorlist) / sizeof(errorlist[0]))
+ id = ERR_INVALID;
+
+ /*
+ * Must flush before we print as we wish output before the error to go on
+ * (some form of) standard output, while output after goes on (some form
+ * of) diagnostic output. If didfds then output will go to 1/2 else to
+ * FSHOUT/FSHDIAG. See flush in sh.print.c.
+ */
+ flush();
+ haderr = 1; /* Now to diagnostic output */
+ timflg = 0; /* This isn't otherwise reset */
+
+
+ if (!(flags & ERR_SILENT)) {
+ if (flags & ERR_NAME)
+ xprintf("%s: ", bname);
+ if ((flags & ERR_OLD))
+ /* Old error. */
+ xprintf("%s.\n", seterr);
+ else {
+#if __STDC__
+ va_start(va, id);
+#else
+ va_start(va);
+#endif
+ xvprintf(errorlist[id], va);
+ va_end(va);
+ xprintf(".\n");
+ }
+ }
+
+ if (seterr) {
+ xfree((ptr_t) seterr);
+ seterr = NULL;
+ }
+
+ if (v = pargv)
+ pargv = 0, blkfree(v);
+ if (v = gargv)
+ gargv = 0, blkfree(v);
+
+ didfds = 0; /* Forget about 0,1,2 */
+ /*
+ * Go away if -e or we are a child shell
+ */
+ if (exiterr || child)
+ xexit(1);
+
+ /*
+ * Reset the state of the input. This buffered seek to end of file will
+ * also clear the while/foreach stack.
+ */
+ btoeof();
+
+ set(STRstatus, Strsave(STR1));
+ if (tpgrp > 0)
+ (void) tcsetpgrp(FSHTTY, tpgrp);
+ reset(); /* Unwind */
+}
diff --git a/bin/csh/exec.c b/bin/csh/exec.c
new file mode 100644
index 000000000000..a2f177f7ca7e
--- /dev/null
+++ b/bin/csh/exec.c
@@ -0,0 +1,444 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)exec.c 5.17 (Berkeley) 6/17/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+/*
+ * System level search and execute of a command. We look in each directory
+ * for the specified command name. If the name contains a '/' then we
+ * execute only the full path name. If there is no search path then we
+ * execute only full path names.
+ */
+extern char **environ;
+
+/*
+ * As we search for the command we note the first non-trivial error
+ * message for presentation to the user. This allows us often
+ * to show that a file has the wrong mode/no access when the file
+ * is not in the last component of the search path, so we must
+ * go on after first detecting the error.
+ */
+static char *exerr; /* Execution error message */
+static Char *expath; /* Path for exerr */
+
+/*
+ * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
+ * to hash execs. If it is allocated (havhash true), then to tell
+ * whether ``name'' is (possibly) present in the i'th component
+ * of the variable path, you look at the bit in xhash indexed by
+ * hash(hashname("name"), i). This is setup automatically
+ * after .login is executed, and recomputed whenever ``path'' is
+ * changed.
+ * The two part hash function is designed to let texec() call the
+ * more expensive hashname() only once and the simple hash() several
+ * times (once for each path component checked).
+ * Byte size is assumed to be 8.
+ */
+#define HSHSIZ 8192 /* 1k bytes */
+#define HSHMASK (HSHSIZ - 1)
+#define HSHMUL 243
+static char xhash[HSHSIZ / 8];
+
+#define hash(a, b) ((a) * HSHMUL + (b) & HSHMASK)
+#define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */
+#define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */
+static int hits, misses;
+
+/* Dummy search path for just absolute search when no path */
+static Char *justabs[] = {STRNULL, 0};
+
+static void pexerr __P((void));
+static void texec __P((Char *, Char **));
+static int hashname __P((Char *));
+
+void
+doexec(t)
+ register struct command *t;
+{
+ register Char *dp, **pv, **av, *sav;
+ register struct varent *v;
+ register bool slash;
+ register int hashval = 0, hashval1, i;
+ Char *blk[2];
+
+ /*
+ * Glob the command name. We will search $path even if this does something,
+ * as in sh but not in csh. One special case: if there is no PATH, then we
+ * execute only commands which start with '/'.
+ */
+ blk[0] = t->t_dcom[0];
+ blk[1] = 0;
+ gflag = 0, tglob(blk);
+ if (gflag) {
+ pv = globall(blk);
+ if (pv == 0) {
+ setname(short2str(blk[0]));
+ stderror(ERR_NAME | ERR_NOMATCH);
+ }
+ gargv = 0;
+ }
+ else
+ pv = saveblk(blk);
+
+ trim(pv);
+
+ exerr = 0;
+ expath = Strsave(pv[0]);
+ Vexpath = expath;
+
+ v = adrof(STRpath);
+ if (v == 0 && expath[0] != '/') {
+ blkfree(pv);
+ pexerr();
+ }
+ slash = any(short2str(expath), '/');
+
+ /*
+ * Glob the argument list, if necessary. Otherwise trim off the quote bits.
+ */
+ gflag = 0;
+ av = &t->t_dcom[1];
+ tglob(av);
+ if (gflag) {
+ av = globall(av);
+ if (av == 0) {
+ blkfree(pv);
+ setname(short2str(expath));
+ stderror(ERR_NAME | ERR_NOMATCH);
+ }
+ gargv = 0;
+ }
+ else
+ av = saveblk(av);
+
+ blkfree(t->t_dcom);
+ t->t_dcom = blkspl(pv, av);
+ xfree((ptr_t) pv);
+ xfree((ptr_t) av);
+ av = t->t_dcom;
+ trim(av);
+
+ if (*av == NULL || **av == '\0')
+ pexerr();
+
+ xechoit(av); /* Echo command if -x */
+ /*
+ * Since all internal file descriptors are set to close on exec, we don't
+ * need to close them explicitly here. Just reorient ourselves for error
+ * messages.
+ */
+ SHIN = 0;
+ SHOUT = 1;
+ SHDIAG = 2;
+ OLDSTD = 0;
+ /*
+ * We must do this AFTER any possible forking (like `foo` in glob) so that
+ * this shell can still do subprocesses.
+ */
+ (void) sigsetmask((sigset_t) 0);
+ /*
+ * If no path, no words in path, or a / in the filename then restrict the
+ * command search.
+ */
+ if (v == 0 || v->vec[0] == 0 || slash)
+ pv = justabs;
+ else
+ pv = v->vec;
+ sav = Strspl(STRslash, *av);/* / command name for postpending */
+ Vsav = sav;
+ if (havhash)
+ hashval = hashname(*av);
+ i = 0;
+ hits++;
+ do {
+ /*
+ * Try to save time by looking at the hash table for where this command
+ * could be. If we are doing delayed hashing, then we put the names in
+ * one at a time, as the user enters them. This is kinda like Korn
+ * Shell's "tracked aliases".
+ */
+ if (!slash && pv[0][0] == '/' && havhash) {
+ hashval1 = hash(hashval, i);
+ if (!bit(xhash, hashval1))
+ goto cont;
+ }
+ if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */
+ texec(*av, av);
+ else {
+ dp = Strspl(*pv, sav);
+ Vdp = dp;
+ texec(dp, av);
+ Vdp = 0;
+ xfree((ptr_t) dp);
+ }
+ misses++;
+cont:
+ pv++;
+ i++;
+ } while (*pv);
+ hits--;
+ Vsav = 0;
+ xfree((ptr_t) sav);
+ pexerr();
+}
+
+static void
+pexerr()
+{
+ /* Couldn't find the damn thing */
+ if (expath) {
+ setname(short2str(expath));
+ Vexpath = 0;
+ xfree((ptr_t) expath);
+ expath = 0;
+ }
+ else
+ setname("");
+ if (exerr)
+ stderror(ERR_NAME | ERR_STRING, exerr);
+ stderror(ERR_NAME | ERR_COMMAND);
+}
+
+/*
+ * Execute command f, arg list t.
+ * Record error message if not found.
+ * Also do shell scripts here.
+ */
+static void
+texec(sf, st)
+ Char *sf;
+ register Char **st;
+{
+ register char **t;
+ register char *f;
+ register struct varent *v;
+ register Char **vp;
+ Char *lastsh[2];
+ int fd;
+ unsigned char c;
+ Char *st0, **ost;
+
+ /* The order for the conversions is significant */
+ t = short2blk(st);
+ f = short2str(sf);
+ Vt = t;
+ errno = 0; /* don't use a previous error */
+ (void) execve(f, t, environ);
+ Vt = 0;
+ blkfree((Char **) t);
+ switch (errno) {
+
+ case ENOEXEC:
+ /*
+ * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
+ * it, don't feed it to the shell if it looks like a binary!
+ */
+ if ((fd = open(f, O_RDONLY)) != -1) {
+ if (read(fd, (char *) &c, 1) == 1) {
+ if (!Isprint(c) && (c != '\n' && c != '\t')) {
+ (void) close(fd);
+ /*
+ * We *know* what ENOEXEC means.
+ */
+ stderror(ERR_ARCH, f, strerror(errno));
+ }
+ }
+#ifdef _PATH_BSHELL
+ else
+ c = '#';
+#endif
+ (void) close(fd);
+ }
+ /*
+ * If there is an alias for shell, then put the words of the alias in
+ * front of the argument list replacing the command name. Note no
+ * interpretation of the words at this point.
+ */
+ v = adrof1(STRshell, &aliases);
+ if (v == 0) {
+ vp = lastsh;
+ vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH;
+ vp[1] = NULL;
+#ifdef _PATH_BSHELL
+ if (fd != -1 && c != '#')
+ vp[0] = STR_BSHELL;
+#endif
+ }
+ else
+ vp = v->vec;
+ st0 = st[0];
+ st[0] = sf;
+ ost = st;
+ st = blkspl(vp, st); /* Splice up the new arglst */
+ ost[0] = st0;
+ sf = *st;
+ /* The order for the conversions is significant */
+ t = short2blk(st);
+ f = short2str(sf);
+ xfree((ptr_t) st);
+ Vt = t;
+ (void) execve(f, t, environ);
+ Vt = 0;
+ blkfree((Char **) t);
+ /* The sky is falling, the sky is falling! */
+
+ case ENOMEM:
+ stderror(ERR_SYSTEM, f, strerror(errno));
+
+ case ENOENT:
+ break;
+
+ default:
+ if (exerr == 0) {
+ exerr = strerror(errno);
+ if (expath)
+ xfree((ptr_t) expath);
+ expath = Strsave(sf);
+ Vexpath = expath;
+ }
+ }
+}
+
+/*ARGSUSED*/
+void
+execash(t, kp)
+ char **t;
+ register struct command *kp;
+{
+ if (chkstop == 0 && setintr)
+ panystop(0);
+ rechist();
+ (void) signal(SIGINT, parintr);
+ (void) signal(SIGQUIT, parintr);
+ (void) signal(SIGTERM, parterm); /* if doexec loses, screw */
+ lshift(kp->t_dcom, 1);
+ exiterr = 1;
+ doexec(kp);
+ /* NOTREACHED */
+}
+
+void
+xechoit(t)
+ Char **t;
+{
+ if (adrof(STRecho)) {
+ flush();
+ haderr = 1;
+ blkpr(t), xputchar('\n');
+ haderr = 0;
+ }
+}
+
+/*VARARGS0*/
+void
+dohash()
+{
+ DIR *dirp;
+ register struct dirent *dp;
+ register int cnt;
+ int i = 0;
+ struct varent *v = adrof(STRpath);
+ Char **pv;
+ int hashval;
+
+ havhash = 1;
+ for (cnt = 0; cnt < sizeof xhash; cnt++)
+ xhash[cnt] = 0;
+ if (v == 0)
+ return;
+ for (pv = v->vec; *pv; pv++, i++) {
+ if (pv[0][0] != '/')
+ continue;
+ dirp = opendir(short2str(*pv));
+ if (dirp == NULL)
+ continue;
+ while ((dp = readdir(dirp)) != NULL) {
+ if (dp->d_ino == 0)
+ continue;
+ if (dp->d_name[0] == '.' &&
+ (dp->d_name[1] == '\0' ||
+ dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
+ continue;
+ hashval = hash(hashname(str2short(dp->d_name)), i);
+ bis(xhash, hashval);
+ /* tw_add_comm_name (dp->d_name); */
+ }
+ (void) closedir(dirp);
+ }
+}
+
+void
+dounhash()
+{
+ havhash = 0;
+}
+
+void
+hashstat()
+{
+ if (hits + misses)
+ xprintf("%d hits, %d misses, %d%%\n",
+ hits, misses, 100 * hits / (hits + misses));
+}
+
+/*
+ * Hash a command name.
+ */
+static int
+hashname(cp)
+ register Char *cp;
+{
+ register long h = 0;
+
+ while (*cp)
+ h = hash(h, *cp++);
+ return ((int) h);
+}
diff --git a/bin/csh/exp.c b/bin/csh/exp.c
new file mode 100644
index 000000000000..16963a7975ce
--- /dev/null
+++ b/bin/csh/exp.c
@@ -0,0 +1,708 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)exp.c 5.11 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+#define IGNORE 1 /* in ignore, it means to ignore value, just parse */
+#define NOGLOB 2 /* in ignore, it means not to globone */
+
+#define ADDOP 1
+#define MULOP 2
+#define EQOP 4
+#define RELOP 8
+#define RESTOP 16
+#define ANYOP 31
+
+#define EQEQ 1
+#define GTR 2
+#define LSS 4
+#define NOTEQ 6
+#define EQMATCH 7
+#define NOTEQMATCH 8
+
+static int exp1 __P((Char ***, bool));
+static int exp2 __P((Char ***, bool));
+static int exp2a __P((Char ***, bool));
+static int exp2b __P((Char ***, bool));
+static int exp2c __P((Char ***, bool));
+static Char * exp3 __P((Char ***, bool));
+static Char * exp3a __P((Char ***, bool));
+static Char * exp4 __P((Char ***, bool));
+static Char * exp5 __P((Char ***, bool));
+static Char * exp6 __P((Char ***, bool));
+static void evalav __P((Char **));
+static int isa __P((Char *, int));
+static int egetn __P((Char *));
+
+#ifdef EDEBUG
+static void etracc __P((char *, Char *, Char ***));
+static void etraci __P((char *, int, Char ***));
+#endif
+
+int
+exp(vp)
+ register Char ***vp;
+{
+ return (exp0(vp, 0));
+}
+
+int
+exp0(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register int p1 = exp1(vp, ignore);
+
+#ifdef EDEBUG
+ etraci("exp0 p1", p1, vp);
+#endif
+ if (**vp && eq(**vp, STRor2)) {
+ register int p2;
+
+ (*vp)++;
+ p2 = exp0(vp, (ignore & IGNORE) || p1);
+#ifdef EDEBUG
+ etraci("exp0 p2", p2, vp);
+#endif
+ return (p1 || p2);
+ }
+ return (p1);
+}
+
+static int
+exp1(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register int p1 = exp2(vp, ignore);
+
+#ifdef EDEBUG
+ etraci("exp1 p1", p1, vp);
+#endif
+ if (**vp && eq(**vp, STRand2)) {
+ register int p2;
+
+ (*vp)++;
+ p2 = exp1(vp, (ignore & IGNORE) || !p1);
+#ifdef EDEBUG
+ etraci("exp1 p2", p2, vp);
+#endif
+ return (p1 && p2);
+ }
+ return (p1);
+}
+
+static int
+exp2(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register int p1 = exp2a(vp, ignore);
+
+#ifdef EDEBUG
+ etraci("exp3 p1", p1, vp);
+#endif
+ if (**vp && eq(**vp, STRor)) {
+ register int p2;
+
+ (*vp)++;
+ p2 = exp2(vp, ignore);
+#ifdef EDEBUG
+ etraci("exp3 p2", p2, vp);
+#endif
+ return (p1 | p2);
+ }
+ return (p1);
+}
+
+static int
+exp2a(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register int p1 = exp2b(vp, ignore);
+
+#ifdef EDEBUG
+ etraci("exp2a p1", p1, vp);
+#endif
+ if (**vp && eq(**vp, STRcaret)) {
+ register int p2;
+
+ (*vp)++;
+ p2 = exp2a(vp, ignore);
+#ifdef EDEBUG
+ etraci("exp2a p2", p2, vp);
+#endif
+ return (p1 ^ p2);
+ }
+ return (p1);
+}
+
+static int
+exp2b(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register int p1 = exp2c(vp, ignore);
+
+#ifdef EDEBUG
+ etraci("exp2b p1", p1, vp);
+#endif
+ if (**vp && eq(**vp, STRand)) {
+ register int p2;
+
+ (*vp)++;
+ p2 = exp2b(vp, ignore);
+#ifdef EDEBUG
+ etraci("exp2b p2", p2, vp);
+#endif
+ return (p1 & p2);
+ }
+ return (p1);
+}
+
+static int
+exp2c(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register Char *p1 = exp3(vp, ignore);
+ register Char *p2;
+ register int i;
+
+#ifdef EDEBUG
+ etracc("exp2c p1", p1, vp);
+#endif
+ if (i = isa(**vp, EQOP)) {
+ (*vp)++;
+ if (i == EQMATCH || i == NOTEQMATCH)
+ ignore |= NOGLOB;
+ p2 = exp3(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp2c p2", p2, vp);
+#endif
+ if (!(ignore & IGNORE))
+ switch (i) {
+
+ case EQEQ:
+ i = eq(p1, p2);
+ break;
+
+ case NOTEQ:
+ i = !eq(p1, p2);
+ break;
+
+ case EQMATCH:
+ i = Gmatch(p1, p2);
+ break;
+
+ case NOTEQMATCH:
+ i = !Gmatch(p1, p2);
+ break;
+ }
+ xfree((ptr_t) p1);
+ xfree((ptr_t) p2);
+ return (i);
+ }
+ i = egetn(p1);
+ xfree((ptr_t) p1);
+ return (i);
+}
+
+static Char *
+exp3(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register Char *p1, *p2;
+ register int i;
+
+ p1 = exp3a(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp3 p1", p1, vp);
+#endif
+ if (i = isa(**vp, RELOP)) {
+ (*vp)++;
+ if (**vp && eq(**vp, STRequal))
+ i |= 1, (*vp)++;
+ p2 = exp3(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp3 p2", p2, vp);
+#endif
+ if (!(ignore & IGNORE))
+ switch (i) {
+
+ case GTR:
+ i = egetn(p1) > egetn(p2);
+ break;
+
+ case GTR | 1:
+ i = egetn(p1) >= egetn(p2);
+ break;
+
+ case LSS:
+ i = egetn(p1) < egetn(p2);
+ break;
+
+ case LSS | 1:
+ i = egetn(p1) <= egetn(p2);
+ break;
+ }
+ xfree((ptr_t) p1);
+ xfree((ptr_t) p2);
+ return (putn(i));
+ }
+ return (p1);
+}
+
+static Char *
+exp3a(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register Char *p1, *p2, *op;
+ register int i;
+
+ p1 = exp4(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp3a p1", p1, vp);
+#endif
+ op = **vp;
+ if (op && any("<>", op[0]) && op[0] == op[1]) {
+ (*vp)++;
+ p2 = exp3a(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp3a p2", p2, vp);
+#endif
+ if (op[0] == '<')
+ i = egetn(p1) << egetn(p2);
+ else
+ i = egetn(p1) >> egetn(p2);
+ xfree((ptr_t) p1);
+ xfree((ptr_t) p2);
+ return (putn(i));
+ }
+ return (p1);
+}
+
+static Char *
+exp4(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register Char *p1, *p2;
+ register int i = 0;
+
+ p1 = exp5(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp4 p1", p1, vp);
+#endif
+ if (isa(**vp, ADDOP)) {
+ register Char *op = *(*vp)++;
+
+ p2 = exp4(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp4 p2", p2, vp);
+#endif
+ if (!(ignore & IGNORE))
+ switch (op[0]) {
+
+ case '+':
+ i = egetn(p1) + egetn(p2);
+ break;
+
+ case '-':
+ i = egetn(p1) - egetn(p2);
+ break;
+ }
+ xfree((ptr_t) p1);
+ xfree((ptr_t) p2);
+ return (putn(i));
+ }
+ return (p1);
+}
+
+static Char *
+exp5(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ register Char *p1, *p2;
+ register int i = 0;
+
+ p1 = exp6(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp5 p1", p1, vp);
+#endif
+ if (isa(**vp, MULOP)) {
+ register Char *op = *(*vp)++;
+
+ p2 = exp5(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp5 p2", p2, vp);
+#endif
+ if (!(ignore & IGNORE))
+ switch (op[0]) {
+
+ case '*':
+ i = egetn(p1) * egetn(p2);
+ break;
+
+ case '/':
+ i = egetn(p2);
+ if (i == 0)
+ stderror(ERR_DIV0);
+ i = egetn(p1) / i;
+ break;
+
+ case '%':
+ i = egetn(p2);
+ if (i == 0)
+ stderror(ERR_MOD0);
+ i = egetn(p1) % i;
+ break;
+ }
+ xfree((ptr_t) p1);
+ xfree((ptr_t) p2);
+ return (putn(i));
+ }
+ return (p1);
+}
+
+static Char *
+exp6(vp, ignore)
+ register Char ***vp;
+ bool ignore;
+{
+ int ccode, i = 0;
+ register Char *cp, *dp, *ep;
+
+ if (**vp == 0)
+ stderror(ERR_NAME | ERR_EXPRESSION);
+ if (eq(**vp, STRbang)) {
+ (*vp)++;
+ cp = exp6(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp6 ! cp", cp, vp);
+#endif
+ i = egetn(cp);
+ xfree((ptr_t) cp);
+ return (putn(!i));
+ }
+ if (eq(**vp, STRtilde)) {
+ (*vp)++;
+ cp = exp6(vp, ignore);
+#ifdef EDEBUG
+ etracc("exp6 ~ cp", cp, vp);
+#endif
+ i = egetn(cp);
+ xfree((ptr_t) cp);
+ return (putn(~i));
+ }
+ if (eq(**vp, STRLparen)) {
+ (*vp)++;
+ ccode = exp0(vp, ignore);
+#ifdef EDEBUG
+ etraci("exp6 () ccode", ccode, vp);
+#endif
+ if (*vp == 0 || **vp == 0 || ***vp != ')')
+ stderror(ERR_NAME | ERR_EXPRESSION);
+ (*vp)++;
+ return (putn(ccode));
+ }
+ if (eq(**vp, STRLbrace)) {
+ register Char **v;
+ struct command faket;
+ Char *fakecom[2];
+
+ faket.t_dtyp = NODE_COMMAND;
+ faket.t_dflg = 0;
+ faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL;
+ faket.t_dcom = fakecom;
+ fakecom[0] = STRfakecom;
+ fakecom[1] = NULL;
+ (*vp)++;
+ v = *vp;
+ for (;;) {
+ if (!**vp)
+ stderror(ERR_NAME | ERR_MISSING, '}');
+ if (eq(*(*vp)++, STRRbrace))
+ break;
+ }
+ if (ignore & IGNORE)
+ return (Strsave(STRNULL));
+ psavejob();
+ if (pfork(&faket, -1) == 0) {
+ *--(*vp) = 0;
+ evalav(v);
+ exitstat();
+ }
+ pwait();
+ prestjob();
+#ifdef EDEBUG
+ etraci("exp6 {} status", egetn(value(STRstatus)), vp);
+#endif
+ return (putn(egetn(value(STRstatus)) == 0));
+ }
+ if (isa(**vp, ANYOP))
+ return (Strsave(STRNULL));
+ cp = *(*vp)++;
+ if (*cp == '-' && any("erwxfdzopls", cp[1])) {
+ struct stat stb;
+
+ if (cp[2] != '\0')
+ stderror(ERR_NAME | ERR_FILEINQ);
+ /*
+ * Detect missing file names by checking for operator in the file name
+ * position. However, if an operator name appears there, we must make
+ * sure that there's no file by that name (e.g., "/") before announcing
+ * an error. Even this check isn't quite right, since it doesn't take
+ * globbing into account.
+ */
+ if (isa(**vp, ANYOP) && stat(short2str(**vp), &stb))
+ stderror(ERR_NAME | ERR_FILENAME);
+
+ dp = *(*vp)++;
+ if (ignore & IGNORE)
+ return (Strsave(STRNULL));
+ ep = globone(dp, G_ERROR);
+ switch (cp[1]) {
+
+ case 'r':
+ i = !access(short2str(ep), R_OK);
+ break;
+
+ case 'w':
+ i = !access(short2str(ep), W_OK);
+ break;
+
+ case 'x':
+ i = !access(short2str(ep), X_OK);
+ break;
+
+ default:
+ if (
+#ifdef S_IFLNK
+ cp[1] == 'l' ? lstat(short2str(ep), &stb) :
+#endif
+ stat(short2str(ep), &stb)) {
+ xfree((ptr_t) ep);
+ return (Strsave(STR0));
+ }
+ switch (cp[1]) {
+
+ case 'f':
+ i = S_ISREG(stb.st_mode);
+ break;
+
+ case 'd':
+ i = S_ISDIR(stb.st_mode);
+ break;
+
+ case 'p':
+#ifdef S_ISFIFO
+ i = S_ISFIFO(stb.st_mode);
+#else
+ i = 0;
+#endif
+ break;
+
+ case 'l':
+#ifdef S_ISLNK
+ i = S_ISLNK(stb.st_mode);
+#else
+ i = 0;
+#endif
+ break;
+
+ case 's':
+#ifdef S_ISSOCK
+ i = S_ISSOCK(stb.st_mode);
+#else
+ i = 0;
+#endif
+ break;
+
+ case 'z':
+ i = stb.st_size == 0;
+ break;
+
+ case 'e':
+ i = 1;
+ break;
+
+ case 'o':
+ i = stb.st_uid == uid;
+ break;
+ }
+ }
+#ifdef EDEBUG
+ etraci("exp6 -? i", i, vp);
+#endif
+ xfree((ptr_t) ep);
+ return (putn(i));
+ }
+#ifdef EDEBUG
+ etracc("exp6 default", cp, vp);
+#endif
+ return (ignore & NOGLOB ? Strsave(cp) : globone(cp, G_ERROR));
+}
+
+static void
+evalav(v)
+ register Char **v;
+{
+ struct wordent paraml1;
+ register struct wordent *hp = &paraml1;
+ struct command *t;
+ register struct wordent *wdp = hp;
+
+ set(STRstatus, Strsave(STR0));
+ hp->prev = hp->next = hp;
+ hp->word = STRNULL;
+ while (*v) {
+ register struct wordent *new =
+ (struct wordent *) xcalloc(1, sizeof *wdp);
+
+ new->prev = wdp;
+ new->next = hp;
+ wdp->next = new;
+ wdp = new;
+ wdp->word = Strsave(*v++);
+ }
+ hp->prev = wdp;
+ alias(&paraml1);
+ t = syntax(paraml1.next, &paraml1, 0);
+ if (seterr)
+ stderror(ERR_OLD);
+ execute(t, -1, NULL, NULL);
+ freelex(&paraml1), freesyn(t);
+}
+
+static int
+isa(cp, what)
+ register Char *cp;
+ register int what;
+{
+ if (cp == 0)
+ return ((what & RESTOP) != 0);
+ if (cp[1] == 0) {
+ if (what & ADDOP && (*cp == '+' || *cp == '-'))
+ return (1);
+ if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%'))
+ return (1);
+ if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' ||
+ *cp == '~' || *cp == '^' || *cp == '"'))
+ return (1);
+ }
+ else if (cp[2] == 0) {
+ if (what & RESTOP) {
+ if (cp[0] == '|' && cp[1] == '&')
+ return (1);
+ if (cp[0] == '<' && cp[1] == '<')
+ return (1);
+ if (cp[0] == '>' && cp[1] == '>')
+ return (1);
+ }
+ if (what & EQOP) {
+ if (cp[0] == '=') {
+ if (cp[1] == '=')
+ return (EQEQ);
+ if (cp[1] == '~')
+ return (EQMATCH);
+ }
+ else if (cp[0] == '!') {
+ if (cp[1] == '=')
+ return (NOTEQ);
+ if (cp[1] == '~')
+ return (NOTEQMATCH);
+ }
+ }
+ }
+ if (what & RELOP) {
+ if (*cp == '<')
+ return (LSS);
+ if (*cp == '>')
+ return (GTR);
+ }
+ return (0);
+}
+
+static int
+egetn(cp)
+ register Char *cp;
+{
+ if (*cp && *cp != '-' && !Isdigit(*cp))
+ stderror(ERR_NAME | ERR_EXPRESSION);
+ return (getn(cp));
+}
+
+/* Phew! */
+
+#ifdef EDEBUG
+static void
+etraci(str, i, vp)
+ char *str;
+ int i;
+ Char ***vp;
+{
+ xprintf("%s=%d\t", str, i);
+ blkpr(*vp);
+ xprintf("\n");
+}
+static void
+etracc(str, cp, vp)
+ char *str;
+ Char *cp;
+ Char ***vp;
+{
+ xprintf("%s=%s\t", str, cp);
+ blkpr(*vp);
+ xprintf("\n");
+}
+#endif
diff --git a/bin/csh/extern.h b/bin/csh/extern.h
new file mode 100644
index 000000000000..4bd1ff7b1306
--- /dev/null
+++ b/bin/csh/extern.h
@@ -0,0 +1,358 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * @(#)extern.h 5.4 (Berkeley) 6/8/91
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * csh.c
+ */
+int gethdir __P((Char *));
+void dosource __P((Char **));
+void exitstat __P((void));
+void goodbye __P((void));
+void importpath __P((Char *));
+void initdesc __P((void));
+void pintr __P((int));
+void pintr1 __P((bool));
+void printprompt __P((void));
+void process __P((bool));
+void rechist __P((void));
+void untty __P((void));
+
+#ifdef PROF
+void done __P((int));
+#else
+void xexit __P((int));
+#endif
+
+/*
+ * dir.c
+ */
+void dinit __P((Char *));
+void dodirs __P((Char **));
+Char *dcanon __P((Char *, Char *));
+void dtildepr __P((Char *, Char *));
+void dtilde __P((void));
+void dochngd __P((Char **));
+Char *dnormalize __P((Char *));
+void dopushd __P((Char **));
+void dopopd __P((Char **));
+struct directory;
+void dfree __P((struct directory *));
+
+/*
+ * dol.c
+ */
+void Dfix __P((struct command *));
+Char *Dfix1 __P((Char *));
+void heredoc __P((Char *));
+
+/*
+ * err.c
+ */
+void seterror __P((int, ...));
+void stderror __P((int, ...));
+
+/*
+ * exec.c
+ */
+void doexec __P((struct command *));
+void dohash __P((void));
+void dounhash __P((void));
+void execash __P((char **, struct command *));
+void hashstat __P((void));
+void xechoit __P((Char **));
+
+/*
+ * exp.c
+ */
+int exp __P((Char ***));
+int exp0 __P((Char ***, bool));
+
+/*
+ * file.c
+ */
+#ifdef FILEC
+int tenex __P((Char *, int));
+#endif
+
+/*
+ * func.c
+ */
+void Setenv __P((Char *, Char *));
+void doalias __P((Char **));
+void dobreak __P((void));
+void docontin __P((void));
+void doecho __P((Char **));
+void doelse __P((void));
+void doend __P((void));
+void doeval __P((Char **));
+void doexit __P((Char **));
+void doforeach __P((Char **));
+void doglob __P((Char **));
+void dogoto __P((Char **));
+void doif __P((Char **, struct command *));
+void dolimit __P((Char **));
+void dologin __P((Char **));
+void dologout __P((void));
+void donohup __P((void));
+void doonintr __P((Char **));
+void dorepeat __P((Char **, struct command *));
+void dosetenv __P((Char **));
+void dosuspend __P((void));
+void doswbrk __P((void));
+void doswitch __P((Char **));
+void doumask __P((Char **));
+void dounlimit __P((Char **));
+void dounsetenv __P((Char **));
+void dowhile __P((Char **));
+void dozip __P((void));
+void func __P((struct command *, struct biltins *));
+struct biltins *
+ isbfunc __P((struct command *));
+void prvars __P((void));
+void search __P((int, int, Char *));
+int srchx __P((Char *));
+void unalias __P((Char **));
+void wfree __P((void));
+
+/*
+ * glob.c
+ */
+Char **dobackp __P((Char *, bool));
+void Gcat __P((Char *, Char *));
+Char *globone __P((Char *, int));
+int Gmatch __P((Char *, Char *));
+void ginit __P((void));
+Char **globall __P((Char **));
+void rscan __P((Char **, void (*)()));
+void tglob __P((Char **));
+void trim __P((Char **));
+#ifdef FILEC
+int sortscmp __P((Char **, Char **));
+#endif /* FILEC */
+
+/*
+ * hist.c
+ */
+void dohist __P((Char **));
+struct Hist *
+ enthist __P((int, struct wordent *, bool));
+void savehist __P((struct wordent *));
+
+/*
+ * lex.c
+ */
+void addla __P((Char *));
+void bseek __P((off_t));
+void btoeof __P((void));
+void copylex __P((struct wordent *, struct wordent *));
+Char *domod __P((Char *, int));
+void freelex __P((struct wordent *));
+int lex __P((struct wordent *));
+void prlex __P((struct wordent *));
+int readc __P((bool));
+void settell __P((void));
+void unreadc __P((int));
+
+/*
+ * misc.c
+ */
+int any __P((char *, int));
+Char **blkcat __P((Char **, Char **));
+Char **blkcpy __P((Char **, Char **));
+Char **blkend __P((Char **));
+void blkfree __P((Char **));
+int blklen __P((Char **));
+void blkpr __P((Char **));
+Char **blkspl __P((Char **, Char **));
+void closem __P((void));
+Char **copyblk __P((Char **));
+int dcopy __P((int, int));
+int dmove __P((int, int));
+void donefds __P((void));
+Char lastchr __P((Char *));
+void lshift __P((Char **, int));
+int number __P((Char *));
+int prefix __P((Char *, Char *));
+Char **saveblk __P((Char **));
+void setzero __P((char *, int));
+Char *strip __P((Char *));
+char *strsave __P((char *));
+char *strspl __P((char *, char *));
+void udvar __P((Char *));
+
+#ifndef NOTUSED
+char *strstr __P((const char *, const char *));
+#endif
+#ifndef SHORT_STRINGS
+char *strend __P((char *));
+#endif
+
+/*
+ * parse.c
+ */
+void alias __P((struct wordent *));
+void freesyn __P((struct command *));
+struct command *
+ syntax __P((struct wordent *, struct wordent *, int));
+
+/*
+ * print.c
+ */
+void draino __P((void));
+void flush __P((void));
+void pcsecs __P((long));
+void psecs __P((long));
+int putpure __P((int));
+int putraw __P((int));
+void xputchar __P((int));
+
+/*
+ * proc.c
+ */
+void dobg __P((Char **));
+void dobg1 __P((Char **));
+void dofg __P((Char **));
+void dofg1 __P((Char **));
+void dojobs __P((Char **));
+void dokill __P((Char **));
+void donotify __P((Char **));
+void dostop __P((Char **));
+void dowait __P((void));
+void palloc __P((int, struct command *));
+void panystop __P((bool));
+void pchild __P((int));
+void pendjob __P((void));
+struct process *
+ pfind __P((Char *));
+int pfork __P((struct command *, int));
+void pgetty __P((int, int));
+void pjwait __P((struct process *));
+void pnote __P((void));
+void prestjob __P((void));
+void psavejob __P((void));
+void pstart __P((struct process *, int));
+void pwait __P((void));
+
+/*
+ * sem.c
+ */
+void execute __P((struct command *, int, int *, int *));
+void mypipe __P((int *));
+
+/*
+ * set.c
+ */
+struct varent
+ *adrof1 __P((Char *, struct varent *));
+void doset __P((Char **));
+void dolet __P((Char **));
+Char *putn __P((int));
+int getn __P((Char *));
+Char *value1 __P((Char *, struct varent *));
+void set __P((Char *, Char *));
+void set1 __P((Char *, Char **, struct varent *));
+void setq __P((Char *, Char **, struct varent *));
+void unset __P((Char *[]));
+void unset1 __P((Char *[], struct varent *));
+void unsetv __P((Char *));
+void setNS __P((Char *));
+void shift __P((Char **));
+void plist __P((struct varent *));
+
+/*
+ * time.c
+ */
+void donice __P((Char **));
+void dotime __P((void));
+void prusage __P((struct rusage *, struct rusage *,
+ struct timeval *, struct timeval *));
+void ruadd __P((struct rusage *, struct rusage *));
+void settimes __P((void));
+void tvadd __P((struct timeval *, struct timeval *));
+void tvsub __P((struct timeval *, struct timeval *, struct timeval *));
+
+/*
+ * alloc.c
+ */
+#ifndef SYSMALLOC
+void free __P((ptr_t));
+ptr_t malloc __P((size_t));
+ptr_t realloc __P((ptr_t, size_t));
+ptr_t calloc __P((size_t, size_t));
+#else
+void Free __P((ptr_t));
+ptr_t Malloc __P((size_t));
+ptr_t Realloc __P((ptr_t, size_t));
+ptr_t Calloc __P((size_t, size_t));
+#endif /* SYSMALLOC */
+void showall __P((void));
+
+/*
+ * printf.h
+ */
+void xprintf __P((char *, ...));
+void xsprintf __P((char *, char *, ...));
+void xvprintf __P((char *, va_list));
+void xvsprintf __P((char *, char *, va_list));
+
+/*
+ * str.c:
+ */
+#ifdef SHORT_STRINGS
+Char *s_strchr __P((Char *, int));
+Char *s_strrchr __P((Char *, int));
+Char *s_strcat __P((Char *, Char *));
+#ifdef NOTUSED
+Char *s_strncat __P((Char *, Char *, size_t));
+#endif
+Char *s_strcpy __P((Char *, Char *));
+Char *s_strncpy __P((Char *, Char *, size_t));
+Char *s_strspl __P((Char *, Char *));
+size_t s_strlen __P((Char *));
+int s_strcmp __P((Char *, Char *));
+int s_strncmp __P((Char *, Char *, size_t));
+Char *s_strsave __P((Char *));
+Char *s_strend __P((Char *));
+#ifdef NOTUSED
+Char *s_strstr __P((Char *, Char *));
+#endif
+Char *str2short __P((char *));
+Char **blk2short __P((char **));
+char *short2str __P((Char *));
+char *short2qstr __P((Char *));
+char **short2blk __P((Char **));
+#endif
diff --git a/bin/csh/file.c b/bin/csh/file.c
new file mode 100644
index 000000000000..d489ed1a1786
--- /dev/null
+++ b/bin/csh/file.c
@@ -0,0 +1,675 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)file.c 5.17 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#ifdef FILEC
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+/*
+ * Tenex style file name recognition, .. and more.
+ * History:
+ * Author: Ken Greer, Sept. 1975, CMU.
+ * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
+ */
+
+#define ON 1
+#define OFF 0
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define ESC '\033'
+
+typedef enum {
+ LIST, RECOGNIZE
+} COMMAND;
+
+static void setup_tty __P((int));
+static void back_to_col_1 __P((void));
+static void pushback __P((Char *));
+static void catn __P((Char *, Char *, int));
+static void copyn __P((Char *, Char *, int));
+static Char filetype __P((Char *, Char *));
+static void print_by_column __P((Char *, Char *[], int));
+static Char *tilde __P((Char *, Char *));
+static void retype __P((void));
+static void beep __P((void));
+static void print_recognized_stuff __P((Char *));
+static void extract_dir_and_name __P((Char *, Char *, Char *));
+static Char *getentry __P((DIR *, int));
+static void free_items __P((Char **));
+static int tsearch __P((Char *, COMMAND, int));
+static int recognize __P((Char *, Char *, int, int));
+static int is_prefix __P((Char *, Char *));
+static int is_suffix __P((Char *, Char *));
+static int ignored __P((Char *));
+
+/*
+ * Put this here so the binary can be patched with adb to enable file
+ * completion by default. Filec controls completion, nobeep controls
+ * ringing the terminal bell on incomplete expansions.
+ */
+bool filec = 0;
+
+static void
+setup_tty(on)
+ int on;
+{
+ static struct termios tchars;
+
+ if (on) {
+ (void) tcgetattr(SHIN, &tchars);
+ tchars.c_cc[VEOL] = ESC;
+ if (tchars.c_lflag & ICANON)
+ on = TCSANOW;
+ else {
+ on = TCSAFLUSH;
+ tchars.c_lflag |= ICANON;
+ }
+ (void) tcsetattr(SHIN, on, &tchars);
+ }
+ else {
+ tchars.c_cc[VEOL] = _POSIX_VDISABLE;
+ (void) tcsetattr(SHIN, TCSANOW, &tchars);
+ }
+}
+
+/*
+ * Move back to beginning of current line
+ */
+static void
+back_to_col_1()
+{
+ struct termios tty, tty_normal;
+ int omask;
+
+ omask = sigblock(sigmask(SIGINT));
+ (void) tcgetattr(SHOUT, &tty);
+ tty_normal = tty;
+ tty.c_iflag &= ~INLCR;
+ tty.c_oflag &= ~ONLCR;
+ (void) tcsetattr(SHOUT, TCSANOW, &tty);
+ (void) write(SHOUT, "\r", 1);
+ (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
+ (void) sigsetmask(omask);
+}
+
+/*
+ * Push string contents back into tty queue
+ */
+static void
+pushback(string)
+ Char *string;
+{
+ register Char *p;
+ struct termios tty, tty_normal;
+ int omask;
+ char c;
+
+ omask = sigblock(sigmask(SIGINT));
+ (void) tcgetattr(SHOUT, &tty);
+ tty_normal = tty;
+ tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
+ (void) tcsetattr(SHOUT, TCSANOW, &tty);
+
+ for (p = string; c = *p; p++)
+ (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
+ (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
+ (void) sigsetmask(omask);
+}
+
+/*
+ * Concatenate src onto tail of des.
+ * Des is a string whose maximum length is count.
+ * Always null terminate.
+ */
+static void
+catn(des, src, count)
+ register Char *des, *src;
+ register int count;
+{
+ while (--count >= 0 && *des)
+ des++;
+ while (--count >= 0)
+ if ((*des++ = *src++) == 0)
+ return;
+ *des = '\0';
+}
+
+/*
+ * Like strncpy but always leave room for trailing \0
+ * and always null terminate.
+ */
+static void
+copyn(des, src, count)
+ register Char *des, *src;
+ register int count;
+{
+ while (--count >= 0)
+ if ((*des++ = *src++) == 0)
+ return;
+ *des = '\0';
+}
+
+static Char
+filetype(dir, file)
+ Char *dir, *file;
+{
+ Char path[MAXPATHLEN];
+ struct stat statb;
+
+ catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
+ if (lstat(short2str(path), &statb) == 0) {
+ switch (statb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ return ('/');
+
+ case S_IFLNK:
+ if (stat(short2str(path), &statb) == 0 && /* follow it out */
+ S_ISDIR(statb.st_mode))
+ return ('>');
+ else
+ return ('@');
+
+ case S_IFSOCK:
+ return ('=');
+
+ default:
+ if (statb.st_mode & 0111)
+ return ('*');
+ }
+ }
+ return (' ');
+}
+
+static struct winsize win;
+
+/*
+ * Print sorted down columns
+ */
+static void
+print_by_column(dir, items, count)
+ Char *dir, *items[];
+ int count;
+{
+ register int i, rows, r, c, maxwidth = 0, columns;
+
+ if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
+ win.ws_col = 80;
+ for (i = 0; i < count; i++)
+ maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
+ maxwidth += 2; /* for the file tag and space */
+ columns = win.ws_col / maxwidth;
+ if (columns == 0)
+ columns = 1;
+ rows = (count + (columns - 1)) / columns;
+ for (r = 0; r < rows; r++) {
+ for (c = 0; c < columns; c++) {
+ i = c * rows + r;
+ if (i < count) {
+ register int w;
+
+ xprintf("%s", short2str(items[i]));
+ xputchar(dir ? filetype(dir, items[i]) : ' ');
+ if (c < columns - 1) { /* last column? */
+ w = Strlen(items[i]) + 1;
+ for (; w < maxwidth; w++)
+ xputchar(' ');
+ }
+ }
+ }
+ xputchar('\r');
+ xputchar('\n');
+ }
+}
+
+/*
+ * Expand file name with possible tilde usage
+ * ~person/mumble
+ * expands to
+ * home_directory_of_person/mumble
+ */
+static Char *
+tilde(new, old)
+ Char *new, *old;
+{
+ register Char *o, *p;
+ register struct passwd *pw;
+ static Char person[40];
+
+ if (old[0] != '~')
+ return (Strcpy(new, old));
+
+ for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++);
+ *p = '\0';
+ if (person[0] == '\0')
+ (void) Strcpy(new, value(STRhome));
+ else {
+ pw = getpwnam(short2str(person));
+ if (pw == NULL)
+ return (NULL);
+ (void) Strcpy(new, str2short(pw->pw_dir));
+ }
+ (void) Strcat(new, o);
+ return (new);
+}
+
+/*
+ * Cause pending line to be printed
+ */
+static void
+retype()
+{
+ struct termios tty;
+
+ (void) tcgetattr(SHOUT, &tty);
+ tty.c_lflag |= PENDIN;
+ (void) tcsetattr(SHOUT, TCSANOW, &tty);
+}
+
+static void
+beep()
+{
+ if (adrof(STRnobeep) == 0)
+ (void) write(SHOUT, "\007", 1);
+}
+
+/*
+ * Erase that silly ^[ and
+ * print the recognized part of the string
+ */
+static void
+print_recognized_stuff(recognized_part)
+ Char *recognized_part;
+{
+ /* An optimized erasing of that silly ^[ */
+ putraw('\b');
+ putraw('\b');
+ switch (Strlen(recognized_part)) {
+
+ case 0: /* erase two Characters: ^[ */
+ putraw(' ');
+ putraw(' ');
+ putraw('\b');
+ putraw('\b');
+ break;
+
+ case 1: /* overstrike the ^, erase the [ */
+ xprintf("%s", short2str(recognized_part));
+ putraw(' ');
+ putraw('\b');
+ break;
+
+ default: /* overstrike both Characters ^[ */
+ xprintf("%s", short2str(recognized_part));
+ break;
+ }
+ flush();
+}
+
+/*
+ * Parse full path in file into 2 parts: directory and file names
+ * Should leave final slash (/) at end of dir.
+ */
+static void
+extract_dir_and_name(path, dir, name)
+ Char *path, *dir, *name;
+{
+ register Char *p;
+
+ p = Strrchr(path, '/');
+ if (p == NULL) {
+ copyn(name, path, MAXNAMLEN);
+ dir[0] = '\0';
+ }
+ else {
+ copyn(name, ++p, MAXNAMLEN);
+ copyn(dir, path, p - path);
+ }
+}
+
+static Char *
+getentry(dir_fd, looking_for_lognames)
+ DIR *dir_fd;
+ int looking_for_lognames;
+{
+ register struct passwd *pw;
+ register struct dirent *dirp;
+
+ if (looking_for_lognames) {
+ if ((pw = getpwent()) == NULL)
+ return (NULL);
+ return (str2short(pw->pw_name));
+ }
+ if (dirp = readdir(dir_fd))
+ return (str2short(dirp->d_name));
+ return (NULL);
+}
+
+static void
+free_items(items)
+ register Char **items;
+{
+ register int i;
+
+ for (i = 0; items[i]; i++)
+ xfree((ptr_t) items[i]);
+ xfree((ptr_t) items);
+}
+
+#define FREE_ITEMS(items) { \
+ int omask;\
+\
+ omask = sigblock(sigmask(SIGINT));\
+ free_items(items);\
+ items = NULL;\
+ (void) sigsetmask(omask);\
+}
+
+/*
+ * Perform a RECOGNIZE or LIST command on string "word".
+ */
+static int
+tsearch(word, command, max_word_length)
+ Char *word;
+ COMMAND command;
+ int max_word_length;
+{
+ static Char **items = NULL;
+ register DIR *dir_fd;
+ register numitems = 0, ignoring = TRUE, nignored = 0;
+ register name_length, looking_for_lognames;
+ Char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
+ Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
+ Char *entry;
+
+#define MAXITEMS 1024
+
+ if (items != NULL)
+ FREE_ITEMS(items);
+
+ looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
+ if (looking_for_lognames) {
+ (void) setpwent();
+ copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */
+ dir_fd = NULL;
+ }
+ else {
+ extract_dir_and_name(word, dir, name);
+ if (tilde(tilded_dir, dir) == 0)
+ return (0);
+ dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
+ if (dir_fd == NULL)
+ return (0);
+ }
+
+again: /* search for matches */
+ name_length = Strlen(name);
+ for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames);) {
+ if (!is_prefix(name, entry))
+ continue;
+ /* Don't match . files on null prefix match */
+ if (name_length == 0 && entry[0] == '.' &&
+ !looking_for_lognames)
+ continue;
+ if (command == LIST) {
+ if (numitems >= MAXITEMS) {
+ xprintf("\nYikes!! Too many %s!!\n",
+ looking_for_lognames ?
+ "names in password file" : "files");
+ break;
+ }
+ if (items == NULL)
+ items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS);
+ items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) *
+ sizeof(Char));
+ copyn(items[numitems], entry, MAXNAMLEN);
+ numitems++;
+ }
+ else { /* RECOGNIZE command */
+ if (ignoring && ignored(entry))
+ nignored++;
+ else if (recognize(extended_name,
+ entry, name_length, ++numitems))
+ break;
+ }
+ }
+ if (ignoring && numitems == 0 && nignored > 0) {
+ ignoring = FALSE;
+ nignored = 0;
+ if (looking_for_lognames)
+ (void) setpwent();
+ else
+ rewinddir(dir_fd);
+ goto again;
+ }
+
+ if (looking_for_lognames)
+ (void) endpwent();
+ else
+ (void) closedir(dir_fd);
+ if (numitems == 0)
+ return (0);
+ if (command == RECOGNIZE) {
+ if (looking_for_lognames)
+ copyn(word, STRtilde, 1);
+ else
+ /* put back dir part */
+ copyn(word, dir, max_word_length);
+ /* add extended name */
+ catn(word, extended_name, max_word_length);
+ return (numitems);
+ }
+ else { /* LIST */
+ qsort((ptr_t) items, numitems, sizeof(items[0]),
+ (int (*)(const void *, const void *)) sortscmp);
+ print_by_column(looking_for_lognames ? NULL : tilded_dir,
+ items, numitems);
+ if (items != NULL)
+ FREE_ITEMS(items);
+ }
+ return (0);
+}
+
+/*
+ * Object: extend what user typed up to an ambiguity.
+ * Algorithm:
+ * On first match, copy full entry (assume it'll be the only match)
+ * On subsequent matches, shorten extended_name to the first
+ * Character mismatch between extended_name and entry.
+ * If we shorten it back to the prefix length, stop searching.
+ */
+static int
+recognize(extended_name, entry, name_length, numitems)
+ Char *extended_name, *entry;
+ int name_length, numitems;
+{
+ if (numitems == 1) /* 1st match */
+ copyn(extended_name, entry, MAXNAMLEN);
+ else { /* 2nd & subsequent matches */
+ register Char *x, *ent;
+ register int len = 0;
+
+ x = extended_name;
+ for (ent = entry; *x && *x == *ent++; x++, len++);
+ *x = '\0'; /* Shorten at 1st Char diff */
+ if (len == name_length) /* Ambiguous to prefix? */
+ return (-1); /* So stop now and save time */
+ }
+ return (0);
+}
+
+/*
+ * Return true if check matches initial Chars in template.
+ * This differs from PWB imatch in that if check is null
+ * it matches anything.
+ */
+static int
+is_prefix(check, template)
+ register Char *check, *template;
+{
+ do
+ if (*check == 0)
+ return (TRUE);
+ while (*check++ == *template++);
+ return (FALSE);
+}
+
+/*
+ * Return true if the Chars in template appear at the
+ * end of check, I.e., are it's suffix.
+ */
+static int
+is_suffix(check, template)
+ Char *check, *template;
+{
+ register Char *c, *t;
+
+ for (c = check; *c++;);
+ for (t = template; *t++;);
+ for (;;) {
+ if (t == template)
+ return 1;
+ if (c == check || *--t != *--c)
+ return 0;
+ }
+}
+
+int
+tenex(inputline, inputline_size)
+ Char *inputline;
+ int inputline_size;
+{
+ register int numitems, num_read;
+ char tinputline[BUFSIZ];
+
+
+ setup_tty(ON);
+
+ while ((num_read = read(SHIN, tinputline, BUFSIZ)) > 0) {
+ int i;
+ static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
+ '>', '(', ')', '|', '^', '%', '\0'};
+ register Char *str_end, *word_start, last_Char, should_retype;
+ register int space_left;
+ COMMAND command;
+
+ for (i = 0; i < num_read; i++)
+ inputline[i] = (unsigned char) tinputline[i];
+ last_Char = inputline[num_read - 1] & ASCII;
+
+ if (last_Char == '\n' || num_read == inputline_size)
+ break;
+ command = (last_Char == ESC) ? RECOGNIZE : LIST;
+ if (command == LIST)
+ xputchar('\n');
+ str_end = &inputline[num_read];
+ if (last_Char == ESC)
+ --str_end; /* wipeout trailing cmd Char */
+ *str_end = '\0';
+ /*
+ * Find LAST occurence of a delimiter in the inputline. The word start
+ * is one Character past it.
+ */
+ for (word_start = str_end; word_start > inputline; --word_start)
+ if (Strchr(delims, word_start[-1]))
+ break;
+ space_left = inputline_size - (word_start - inputline) - 1;
+ numitems = tsearch(word_start, command, space_left);
+
+ if (command == RECOGNIZE) {
+ /* print from str_end on */
+ print_recognized_stuff(str_end);
+ if (numitems != 1) /* Beep = No match/ambiguous */
+ beep();
+ }
+
+ /*
+ * Tabs in the input line cause trouble after a pushback. tty driver
+ * won't backspace over them because column positions are now
+ * incorrect. This is solved by retyping over current line.
+ */
+ should_retype = FALSE;
+ if (Strchr(inputline, '\t')) { /* tab Char in input line? */
+ back_to_col_1();
+ should_retype = TRUE;
+ }
+ if (command == LIST) /* Always retype after a LIST */
+ should_retype = TRUE;
+ if (should_retype)
+ printprompt();
+ pushback(inputline);
+ if (should_retype)
+ retype();
+ }
+ setup_tty(OFF);
+ return (num_read);
+}
+
+static int
+ignored(entry)
+ register Char *entry;
+{
+ struct varent *vp;
+ register Char **cp;
+
+ if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
+ return (FALSE);
+ for (; *cp != NULL; cp++)
+ if (is_suffix(entry, *cp))
+ return (TRUE);
+ return (FALSE);
+}
+#endif /* FILEC */
diff --git a/bin/csh/func.c b/bin/csh/func.c
new file mode 100644
index 000000000000..f3645f52c877
--- /dev/null
+++ b/bin/csh/func.c
@@ -0,0 +1,1376 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)func.c 5.20 (Berkeley) 6/27/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+#include "pathnames.h"
+
+extern char **environ;
+
+static int zlast = -1;
+static void islogin __P((void));
+static void reexecute __P((struct command *));
+static void preread __P((void));
+static void doagain __P((void));
+static int getword __P((Char *));
+static int keyword __P((Char *));
+static void Unsetenv __P((Char *));
+static void toend __P((void));
+static void xecho __P((int, Char **));
+
+struct biltins *
+isbfunc(t)
+ struct command *t;
+{
+ register Char *cp = t->t_dcom[0];
+ register struct biltins *bp, *bp1, *bp2;
+ static struct biltins label = {"", dozip, 0, 0};
+ static struct biltins foregnd = {"%job", dofg1, 0, 0};
+ static struct biltins backgnd = {"%job &", dobg1, 0, 0};
+
+ if (lastchr(cp) == ':') {
+ label.bname = short2str(cp);
+ return (&label);
+ }
+ if (*cp == '%') {
+ if (t->t_dflg & F_AMPERSAND) {
+ t->t_dflg &= ~F_AMPERSAND;
+ backgnd.bname = short2str(cp);
+ return (&backgnd);
+ }
+ foregnd.bname = short2str(cp);
+ return (&foregnd);
+ }
+ /*
+ * Binary search Bp1 is the beginning of the current search range. Bp2 is
+ * one past the end.
+ */
+ for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) {
+ register i;
+
+ bp = bp1 + ((bp2 - bp1) >> 1);
+ if ((i = *cp - *bp->bname) == 0 &&
+ (i = Strcmp(cp, str2short(bp->bname))) == 0)
+ return bp;
+ if (i < 0)
+ bp2 = bp;
+ else
+ bp1 = bp + 1;
+ }
+ return (0);
+}
+
+void
+func(t, bp)
+ register struct command *t;
+ register struct biltins *bp;
+{
+ int i;
+
+ xechoit(t->t_dcom);
+ setname(bp->bname);
+ i = blklen(t->t_dcom) - 1;
+ if (i < bp->minargs)
+ stderror(ERR_NAME | ERR_TOOFEW);
+ if (i > bp->maxargs)
+ stderror(ERR_NAME | ERR_TOOMANY);
+ (*bp->bfunct) (t->t_dcom, t);
+}
+
+void
+doonintr(v)
+ Char **v;
+{
+ register Char *cp;
+ register Char *vv = v[1];
+
+ if (parintr == SIG_IGN)
+ return;
+ if (setintr && intty)
+ stderror(ERR_NAME | ERR_TERMINAL);
+ cp = gointr;
+ gointr = 0;
+ xfree((ptr_t) cp);
+ if (vv == 0) {
+ if (setintr)
+ (void) sigblock(sigmask(SIGINT));
+ else
+ (void) signal(SIGINT, SIG_DFL);
+ gointr = 0;
+ }
+ else if (eq((vv = strip(vv)), STRminus)) {
+ (void) signal(SIGINT, SIG_IGN);
+ gointr = Strsave(STRminus);
+ }
+ else {
+ gointr = Strsave(vv);
+ (void) signal(SIGINT, pintr);
+ }
+}
+
+void
+donohup()
+{
+ if (intty)
+ stderror(ERR_NAME | ERR_TERMINAL);
+ if (setintr == 0) {
+ (void) signal(SIGHUP, SIG_IGN);
+ }
+}
+
+void
+dozip()
+{
+ ;
+}
+
+void
+prvars()
+{
+ plist(&shvhed);
+}
+
+void
+doalias(v)
+ register Char **v;
+{
+ register struct varent *vp;
+ register Char *p;
+
+ v++;
+ p = *v++;
+ if (p == 0)
+ plist(&aliases);
+ else if (*v == 0) {
+ vp = adrof1(strip(p), &aliases);
+ if (vp)
+ blkpr(vp->vec), xprintf("\n");
+ }
+ else {
+ if (eq(p, STRalias) || eq(p, STRunalias)) {
+ setname(short2str(p));
+ stderror(ERR_NAME | ERR_DANGER);
+ }
+ set1(strip(p), saveblk(v), &aliases);
+ }
+}
+
+void
+unalias(v)
+ Char **v;
+{
+ unset1(v, &aliases);
+}
+
+void
+dologout()
+{
+ islogin();
+ goodbye();
+}
+
+void
+dologin(v)
+ Char **v;
+{
+ islogin();
+ rechist();
+ (void) signal(SIGTERM, parterm);
+ (void) execl(_PATH_LOGIN, "login", short2str(v[1]), NULL);
+ untty();
+ xexit(1);
+}
+
+static void
+islogin()
+{
+ if (chkstop == 0 && setintr)
+ panystop(0);
+ if (loginsh)
+ return;
+ stderror(ERR_NOTLOGIN);
+}
+
+void
+doif(v, kp)
+ Char **v;
+ struct command *kp;
+{
+ register int i;
+ register Char **vv;
+
+ v++;
+ i = exp(&v);
+ vv = v;
+ if (*vv == NULL)
+ stderror(ERR_NAME | ERR_EMPTYIF);
+ if (eq(*vv, STRthen)) {
+ if (*++vv)
+ stderror(ERR_NAME | ERR_IMPRTHEN);
+ setname(short2str(STRthen));
+ /*
+ * If expression was zero, then scan to else, otherwise just fall into
+ * following code.
+ */
+ if (!i)
+ search(T_IF, 0, NULL);
+ return;
+ }
+ /*
+ * Simple command attached to this if. Left shift the node in this tree,
+ * munging it so we can reexecute it.
+ */
+ if (i) {
+ lshift(kp->t_dcom, vv - kp->t_dcom);
+ reexecute(kp);
+ donefds();
+ }
+}
+
+/*
+ * Reexecute a command, being careful not
+ * to redo i/o redirection, which is already set up.
+ */
+static void
+reexecute(kp)
+ register struct command *kp;
+{
+ kp->t_dflg &= F_SAVE;
+ kp->t_dflg |= F_REPEAT;
+ /*
+ * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set
+ * pgrp's as the jobs would then have no way to get the tty (we can't give
+ * it to them, and our parent wouldn't know their pgrp, etc.
+ */
+ execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
+}
+
+void
+doelse()
+{
+ search(T_ELSE, 0, NULL);
+}
+
+void
+dogoto(v)
+ Char **v;
+{
+ register struct whyle *wp;
+ Char *lp;
+
+ /*
+ * While we still can, locate any unknown ends of existing loops. This
+ * obscure code is the WORST result of the fact that we don't really parse.
+ */
+ zlast = T_GOTO;
+ for (wp = whyles; wp; wp = wp->w_next)
+ if (wp->w_end == 0) {
+ search(T_BREAK, 0, NULL);
+ wp->w_end = fseekp;
+ }
+ else
+ bseek(wp->w_end);
+ search(T_GOTO, 0, lp = globone(v[1], G_ERROR));
+ xfree((ptr_t) lp);
+ /*
+ * Eliminate loops which were exited.
+ */
+ wfree();
+}
+
+void
+doswitch(v)
+ register Char **v;
+{
+ register Char *cp, *lp;
+
+ v++;
+ if (!*v || *(*v++) != '(')
+ stderror(ERR_SYNTAX);
+ cp = **v == ')' ? STRNULL : *v++;
+ if (*(*v++) != ')')
+ v--;
+ if (*v)
+ stderror(ERR_SYNTAX);
+ search(T_SWITCH, 0, lp = globone(cp, G_ERROR));
+ xfree((ptr_t) lp);
+}
+
+void
+dobreak()
+{
+ if (whyles)
+ toend();
+ else
+ stderror(ERR_NAME | ERR_NOTWHILE);
+}
+
+void
+doexit(v)
+ Char **v;
+{
+ if (chkstop == 0 && (intty || intact) && evalvec == 0)
+ panystop(0);
+ /*
+ * Don't DEMAND parentheses here either.
+ */
+ v++;
+ if (*v) {
+ set(STRstatus, putn(exp(&v)));
+ if (*v)
+ stderror(ERR_NAME | ERR_EXPRESSION);
+ }
+ btoeof();
+ if (intty)
+ (void) close(SHIN);
+}
+
+void
+doforeach(v)
+ register Char **v;
+{
+ register Char *cp, *sp;
+ register struct whyle *nwp;
+
+ v++;
+ sp = cp = strip(*v);
+ if (!letter(*sp))
+ stderror(ERR_NAME | ERR_VARBEGIN);
+ while (*cp && alnum(*cp))
+ cp++;
+ if (*cp)
+ stderror(ERR_NAME | ERR_VARALNUM);
+ if ((cp - sp) > MAXVARLEN)
+ stderror(ERR_NAME | ERR_VARTOOLONG);
+ cp = *v++;
+ if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
+ stderror(ERR_NAME | ERR_NOPAREN);
+ v++;
+ gflag = 0, tglob(v);
+ v = globall(v);
+ if (v == 0)
+ stderror(ERR_NAME | ERR_NOMATCH);
+ nwp = (struct whyle *) xcalloc(1, sizeof *nwp);
+ nwp->w_fe = nwp->w_fe0 = v;
+ gargv = 0;
+ nwp->w_start = fseekp;
+ nwp->w_fename = Strsave(cp);
+ nwp->w_next = whyles;
+ whyles = nwp;
+ /*
+ * Pre-read the loop so as to be more comprehensible to a terminal user.
+ */
+ zlast = T_FOREACH;
+ if (intty)
+ preread();
+ doagain();
+}
+
+void
+dowhile(v)
+ Char **v;
+{
+ register int status;
+ register bool again = whyles != 0 && whyles->w_start == lineloc &&
+ whyles->w_fename == 0;
+
+ v++;
+ /*
+ * Implement prereading here also, taking care not to evaluate the
+ * expression before the loop has been read up from a terminal.
+ */
+ if (intty && !again)
+ status = !exp0(&v, 1);
+ else
+ status = !exp(&v);
+ if (*v)
+ stderror(ERR_NAME | ERR_EXPRESSION);
+ if (!again) {
+ register struct whyle *nwp =
+ (struct whyle *) xcalloc(1, sizeof(*nwp));
+
+ nwp->w_start = lineloc;
+ nwp->w_end = 0;
+ nwp->w_next = whyles;
+ whyles = nwp;
+ zlast = T_WHILE;
+ if (intty) {
+ /*
+ * The tty preread
+ */
+ preread();
+ doagain();
+ return;
+ }
+ }
+ if (status)
+ /* We ain't gonna loop no more, no more! */
+ toend();
+}
+
+static void
+preread()
+{
+ whyles->w_end = -1;
+ if (setintr)
+ (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT));
+
+ search(T_BREAK, 0, NULL); /* read the expression in */
+ if (setintr)
+ (void) sigblock(sigmask(SIGINT));
+ whyles->w_end = fseekp;
+}
+
+void
+doend()
+{
+ if (!whyles)
+ stderror(ERR_NAME | ERR_NOTWHILE);
+ whyles->w_end = fseekp;
+ doagain();
+}
+
+void
+docontin()
+{
+ if (!whyles)
+ stderror(ERR_NAME | ERR_NOTWHILE);
+ doagain();
+}
+
+static void
+doagain()
+{
+ /* Repeating a while is simple */
+ if (whyles->w_fename == 0) {
+ bseek(whyles->w_start);
+ return;
+ }
+ /*
+ * The foreach variable list actually has a spurious word ")" at the end of
+ * the w_fe list. Thus we are at the of the list if one word beyond this
+ * is 0.
+ */
+ if (!whyles->w_fe[1]) {
+ dobreak();
+ return;
+ }
+ set(whyles->w_fename, Strsave(*whyles->w_fe++));
+ bseek(whyles->w_start);
+}
+
+void
+dorepeat(v, kp)
+ Char **v;
+ struct command *kp;
+{
+ register int i;
+ register sigset_t omask = 0;
+
+ i = getn(v[1]);
+ if (setintr)
+ omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT);
+ lshift(v, 2);
+ while (i > 0) {
+ if (setintr)
+ (void) sigsetmask(omask);
+ reexecute(kp);
+ --i;
+ }
+ donefds();
+ if (setintr)
+ (void) sigsetmask(omask);
+}
+
+void
+doswbrk()
+{
+ search(T_BRKSW, 0, NULL);
+}
+
+int
+srchx(cp)
+ register Char *cp;
+{
+ register struct srch *sp, *sp1, *sp2;
+ register i;
+
+ /*
+ * Binary search Sp1 is the beginning of the current search range. Sp2 is
+ * one past the end.
+ */
+ for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) {
+ sp = sp1 + ((sp2 - sp1) >> 1);
+ if ((i = *cp - *sp->s_name) == 0 &&
+ (i = Strcmp(cp, str2short(sp->s_name))) == 0)
+ return sp->s_value;
+ if (i < 0)
+ sp2 = sp;
+ else
+ sp1 = sp + 1;
+ }
+ return (-1);
+}
+
+static Char Stype;
+static Char *Sgoal;
+
+/*VARARGS2*/
+void
+search(type, level, goal)
+ int type;
+ register int level;
+ Char *goal;
+{
+ Char wordbuf[BUFSIZ];
+ register Char *aword = wordbuf;
+ register Char *cp;
+
+ Stype = type;
+ Sgoal = goal;
+ if (type == T_GOTO)
+ bseek((off_t) 0);
+ do {
+ if (intty && fseekp == feobp)
+ xprintf("? "), flush();
+ aword[0] = 0;
+ (void) getword(aword);
+ switch (srchx(aword)) {
+
+ case T_ELSE:
+ if (level == 0 && type == T_IF)
+ return;
+ break;
+
+ case T_IF:
+ while (getword(aword))
+ continue;
+ if ((type == T_IF || type == T_ELSE) &&
+ eq(aword, STRthen))
+ level++;
+ break;
+
+ case T_ENDIF:
+ if (type == T_IF || type == T_ELSE)
+ level--;
+ break;
+
+ case T_FOREACH:
+ case T_WHILE:
+ if (type == T_BREAK)
+ level++;
+ break;
+
+ case T_END:
+ if (type == T_BREAK)
+ level--;
+ break;
+
+ case T_SWITCH:
+ if (type == T_SWITCH || type == T_BRKSW)
+ level++;
+ break;
+
+ case T_ENDSW:
+ if (type == T_SWITCH || type == T_BRKSW)
+ level--;
+ break;
+
+ case T_LABEL:
+ if (type == T_GOTO && getword(aword) && eq(aword, goal))
+ level = -1;
+ break;
+
+ default:
+ if (type != T_GOTO && (type != T_SWITCH || level != 0))
+ break;
+ if (lastchr(aword) != ':')
+ break;
+ aword[Strlen(aword) - 1] = 0;
+ if (type == T_GOTO && eq(aword, goal) ||
+ type == T_SWITCH && eq(aword, STRdefault))
+ level = -1;
+ break;
+
+ case T_CASE:
+ if (type != T_SWITCH || level != 0)
+ break;
+ (void) getword(aword);
+ if (lastchr(aword) == ':')
+ aword[Strlen(aword) - 1] = 0;
+ cp = strip(Dfix1(aword));
+ if (Gmatch(goal, cp))
+ level = -1;
+ xfree((ptr_t) cp);
+ break;
+
+ case T_DEFAULT:
+ if (type == T_SWITCH && level == 0)
+ level = -1;
+ break;
+ }
+ (void) getword(NULL);
+ } while (level >= 0);
+}
+
+static int
+getword(wp)
+ register Char *wp;
+{
+ register int found = 0;
+ register int c, d;
+ int kwd = 0;
+ Char *owp = wp;
+
+ c = readc(1);
+ d = 0;
+ do {
+ while (c == ' ' || c == '\t')
+ c = readc(1);
+ if (c == '#')
+ do
+ c = readc(1);
+ while (c >= 0 && c != '\n');
+ if (c < 0)
+ goto past;
+ if (c == '\n') {
+ if (wp)
+ break;
+ return (0);
+ }
+ unreadc(c);
+ found = 1;
+ do {
+ c = readc(1);
+ if (c == '\\' && (c = readc(1)) == '\n')
+ c = ' ';
+ if (c == '\'' || c == '"')
+ if (d == 0)
+ d = c;
+ else if (d == c)
+ d = 0;
+ if (c < 0)
+ goto past;
+ if (wp) {
+ *wp++ = c;
+ *wp = 0; /* end the string b4 test */
+ }
+ } while ((d || !(kwd = keyword(owp)) && c != ' '
+ && c != '\t') && c != '\n');
+ } while (wp == 0);
+
+ /*
+ * if we have read a keyword ( "if", "switch" or "while" ) then we do not
+ * need to unreadc the look-ahead char
+ */
+ if (!kwd) {
+ unreadc(c);
+ if (found)
+ *--wp = 0;
+ }
+
+ return (found);
+
+past:
+ switch (Stype) {
+
+ case T_IF:
+ stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
+
+ case T_ELSE:
+ stderror(ERR_NAME | ERR_NOTFOUND, "endif");
+
+ case T_BRKSW:
+ case T_SWITCH:
+ stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
+
+ case T_BREAK:
+ stderror(ERR_NAME | ERR_NOTFOUND, "end");
+
+ case T_GOTO:
+ setname(short2str(Sgoal));
+ stderror(ERR_NAME | ERR_NOTFOUND, "label");
+ }
+ /* NOTREACHED */
+ return (0);
+}
+
+/*
+ * keyword(wp) determines if wp is one of the built-n functions if,
+ * switch or while. It seems that when an if statement looks like
+ * "if(" then getword above sucks in the '(' and so the search routine
+ * never finds what it is scanning for. Rather than rewrite doword, I hack
+ * in a test to see if the string forms a keyword. Then doword stops
+ * and returns the word "if" -strike
+ */
+
+static int
+keyword(wp)
+ Char *wp;
+{
+ static Char STRif[] = {'i', 'f', '\0'};
+ static Char STRwhile[] = {'w', 'h', 'i', 'l', 'e', '\0'};
+ static Char STRswitch[] = {'s', 'w', 'i', 't', 'c', 'h', '\0'};
+
+ if (!wp)
+ return (0);
+
+ if ((Strcmp(wp, STRif) == 0) || (Strcmp(wp, STRwhile) == 0)
+ || (Strcmp(wp, STRswitch) == 0))
+ return (1);
+
+ return (0);
+}
+
+static void
+toend()
+{
+ if (whyles->w_end == 0) {
+ search(T_BREAK, 0, NULL);
+ whyles->w_end = fseekp - 1;
+ }
+ else
+ bseek(whyles->w_end);
+ wfree();
+}
+
+void
+wfree()
+{
+ long o = fseekp;
+
+ while (whyles) {
+ register struct whyle *wp = whyles;
+ register struct whyle *nwp = wp->w_next;
+
+ if (o >= wp->w_start && (wp->w_end == 0 || o < wp->w_end))
+ break;
+ if (wp->w_fe0)
+ blkfree(wp->w_fe0);
+ if (wp->w_fename)
+ xfree((ptr_t) wp->w_fename);
+ xfree((ptr_t) wp);
+ whyles = nwp;
+ }
+}
+
+void
+doecho(v)
+ Char **v;
+{
+ xecho(' ', v);
+}
+
+void
+doglob(v)
+ Char **v;
+{
+ xecho(0, v);
+ flush();
+}
+
+static void
+xecho(sep, v)
+ int sep;
+ register Char **v;
+{
+ register Char *cp;
+ int nonl = 0;
+
+ if (setintr)
+ (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT));
+ v++;
+ if (*v == 0)
+ return;
+ gflag = 0, tglob(v);
+ if (gflag) {
+ v = globall(v);
+ if (v == 0)
+ stderror(ERR_NAME | ERR_NOMATCH);
+ }
+ else {
+ v = gargv = saveblk(v);
+ trim(v);
+ }
+ if (sep == ' ' && *v && eq(*v, STRmn))
+ nonl++, v++;
+ while (cp = *v++) {
+ register int c;
+
+ while (c = *cp++)
+ xputchar(c | QUOTE);
+
+ if (*v)
+ xputchar(sep | QUOTE);
+ }
+ if (sep && nonl == 0)
+ xputchar('\n');
+ else
+ flush();
+ if (setintr)
+ (void) sigblock(sigmask(SIGINT));
+ if (gargv)
+ blkfree(gargv), gargv = 0;
+}
+
+/* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
+ (and anything else with a modern compiler) */
+
+void
+dosetenv(v)
+ register Char **v;
+{
+ Char *vp, *lp;
+
+ v++;
+ if ((vp = *v++) == 0) {
+ register Char **ep;
+
+ if (setintr)
+ (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT));
+ for (ep = STR_environ; *ep; ep++)
+ xprintf("%s\n", short2str(*ep));
+ return;
+ }
+ if ((lp = *v++) == 0)
+ lp = STRNULL;
+ Setenv(vp, lp = globone(lp, G_ERROR));
+ if (eq(vp, STRPATH)) {
+ importpath(lp);
+ dohash();
+ }
+ else if (eq(vp, STRLANG) || eq(vp, STRLC_CTYPE)) {
+#ifdef NLS
+ int k;
+
+ (void) setlocale(LC_ALL, "");
+ for (k = 0200; k <= 0377 && !Isprint(k); k++);
+ AsciiOnly = k > 0377;
+#else
+ AsciiOnly = 0;
+#endif /* NLS */
+ }
+ xfree((ptr_t) lp);
+}
+
+void
+dounsetenv(v)
+ register Char **v;
+{
+ Char **ep, *p, *n;
+ int i, maxi;
+ static Char *name = NULL;
+
+ if (name)
+ xfree((ptr_t) name);
+ /*
+ * Find the longest environment variable
+ */
+ for (maxi = 0, ep = STR_environ; *ep; ep++) {
+ for (i = 0, p = *ep; *p && *p != '='; p++, i++);
+ if (i > maxi)
+ maxi = i;
+ }
+
+ name = (Char *) xmalloc((size_t) (maxi + 1) * sizeof(Char));
+
+ while (++v && *v)
+ for (maxi = 1; maxi;)
+ for (maxi = 0, ep = STR_environ; *ep; ep++) {
+ for (n = name, p = *ep; *p && *p != '='; *n++ = *p++);
+ *n = '\0';
+ if (!Gmatch(name, *v))
+ continue;
+ maxi = 1;
+ if (eq(name, STRLANG) || eq(name, STRLC_CTYPE)) {
+#ifdef NLS
+ int k;
+
+ (void) setlocale(LC_ALL, "");
+ for (k = 0200; k <= 0377 && !Isprint(k); k++);
+ AsciiOnly = k > 0377;
+#else
+ AsciiOnly = getenv("LANG") == NULL &&
+ getenv("LC_CTYPE") == NULL;
+#endif /* NLS */
+ }
+ /*
+ * Delete name, and start again cause the environment changes
+ */
+ Unsetenv(name);
+ break;
+ }
+ xfree((ptr_t) name), name = NULL;
+}
+
+void
+Setenv(name, val)
+ Char *name, *val;
+{
+ register Char **ep = STR_environ;
+ register Char *cp, *dp;
+ Char *blk[2];
+ Char **oep = ep;
+
+
+ for (; *ep; ep++) {
+ for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
+ continue;
+ if (*cp != 0 || *dp != '=')
+ continue;
+ cp = Strspl(STRequal, val);
+ xfree((ptr_t) * ep);
+ *ep = strip(Strspl(name, cp));
+ xfree((ptr_t) cp);
+ blkfree((Char **) environ);
+ environ = short2blk(STR_environ);
+ return;
+ }
+ cp = Strspl(name, STRequal);
+ blk[0] = strip(Strspl(cp, val));
+ xfree((ptr_t) cp);
+ blk[1] = 0;
+ STR_environ = blkspl(STR_environ, blk);
+ blkfree((Char **) environ);
+ environ = short2blk(STR_environ);
+ xfree((ptr_t) oep);
+}
+
+static void
+Unsetenv(name)
+ Char *name;
+{
+ register Char **ep = STR_environ;
+ register Char *cp, *dp;
+ Char **oep = ep;
+
+ for (; *ep; ep++) {
+ for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
+ continue;
+ if (*cp != 0 || *dp != '=')
+ continue;
+ cp = *ep;
+ *ep = 0;
+ STR_environ = blkspl(STR_environ, ep + 1);
+ environ = short2blk(STR_environ);
+ *ep = cp;
+ xfree((ptr_t) cp);
+ xfree((ptr_t) oep);
+ return;
+ }
+}
+
+void
+doumask(v)
+ register Char **v;
+{
+ register Char *cp = v[1];
+ register int i;
+
+ if (cp == 0) {
+ i = umask(0);
+ (void) umask(i);
+ xprintf("%o\n", i);
+ return;
+ }
+ i = 0;
+ while (Isdigit(*cp) && *cp != '8' && *cp != '9')
+ i = i * 8 + *cp++ - '0';
+ if (*cp || i < 0 || i > 0777)
+ stderror(ERR_NAME | ERR_MASK);
+ (void) umask(i);
+}
+
+typedef int RLIM_TYPE;
+
+static struct limits {
+ int limconst;
+ char *limname;
+ int limdiv;
+ char *limscale;
+} limits[] = {
+ RLIMIT_CPU, "cputime", 1, "seconds",
+ RLIMIT_FSIZE, "filesize", 1024, "kbytes",
+ RLIMIT_DATA, "datasize", 1024, "kbytes",
+ RLIMIT_STACK, "stacksize", 1024, "kbytes",
+ RLIMIT_CORE, "coredumpsize", 1024, "kbytes",
+ RLIMIT_RSS, "memoryuse", 1024, "kbytes",
+ RLIMIT_MEMLOCK, "memorylocked", 1024, "kbytes",
+ RLIMIT_NPROC, "maxproc", 1, "",
+ RLIMIT_OFILE, "openfiles", 1, "",
+ -1, NULL, 0, NULL
+};
+
+static struct limits *findlim();
+static RLIM_TYPE getval();
+static void limtail();
+static void plim();
+static int setlim();
+
+static struct limits *
+findlim(cp)
+ Char *cp;
+{
+ register struct limits *lp, *res;
+
+ res = (struct limits *) NULL;
+ for (lp = limits; lp->limconst >= 0; lp++)
+ if (prefix(cp, str2short(lp->limname))) {
+ if (res)
+ stderror(ERR_NAME | ERR_AMBIG);
+ res = lp;
+ }
+ if (res)
+ return (res);
+ stderror(ERR_NAME | ERR_LIMIT);
+ /* NOTREACHED */
+ return (0);
+}
+
+void
+dolimit(v)
+ register Char **v;
+{
+ register struct limits *lp;
+ register RLIM_TYPE limit;
+ char hard = 0;
+
+ v++;
+ if (*v && eq(*v, STRmh)) {
+ hard = 1;
+ v++;
+ }
+ if (*v == 0) {
+ for (lp = limits; lp->limconst >= 0; lp++)
+ plim(lp, hard);
+ return;
+ }
+ lp = findlim(v[0]);
+ if (v[1] == 0) {
+ plim(lp, hard);
+ return;
+ }
+ limit = getval(lp, v + 1);
+ if (setlim(lp, hard, limit) < 0)
+ stderror(ERR_SILENT);
+}
+
+static RLIM_TYPE
+getval(lp, v)
+ register struct limits *lp;
+ Char **v;
+{
+ register float f;
+ double atof();
+ Char *cp = *v++;
+
+ f = atof(short2str(cp));
+
+ while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
+ cp++;
+ if (*cp == 0) {
+ if (*v == 0)
+ return ((RLIM_TYPE) ((f + 0.5) * lp->limdiv));
+ cp = *v;
+ }
+ switch (*cp) {
+ case ':':
+ if (lp->limconst != RLIMIT_CPU)
+ goto badscal;
+ return ((RLIM_TYPE) (f * 60.0 + atof(short2str(cp + 1))));
+ case 'h':
+ if (lp->limconst != RLIMIT_CPU)
+ goto badscal;
+ limtail(cp, "hours");
+ f *= 3600.0;
+ break;
+ case 'm':
+ if (lp->limconst == RLIMIT_CPU) {
+ limtail(cp, "minutes");
+ f *= 60.0;
+ break;
+ }
+ *cp = 'm';
+ limtail(cp, "megabytes");
+ f *= 1024.0 * 1024.0;
+ break;
+ case 's':
+ if (lp->limconst != RLIMIT_CPU)
+ goto badscal;
+ limtail(cp, "seconds");
+ break;
+ case 'M':
+ if (lp->limconst == RLIMIT_CPU)
+ goto badscal;
+ *cp = 'm';
+ limtail(cp, "megabytes");
+ f *= 1024.0 * 1024.0;
+ break;
+ case 'k':
+ if (lp->limconst == RLIMIT_CPU)
+ goto badscal;
+ limtail(cp, "kbytes");
+ f *= 1024.0;
+ break;
+ case 'u':
+ limtail(cp, "unlimited");
+ return (RLIM_INFINITY);
+ default:
+badscal:
+ stderror(ERR_NAME | ERR_SCALEF);
+ }
+ return ((RLIM_TYPE) (f + 0.5));
+}
+
+static void
+limtail(cp, str)
+ Char *cp;
+ char *str;
+{
+ while (*cp && *cp == *str)
+ cp++, str++;
+ if (*cp)
+ stderror(ERR_BADSCALE, str);
+}
+
+
+/*ARGSUSED*/
+static void
+plim(lp, hard)
+ register struct limits *lp;
+ Char hard;
+{
+ struct rlimit rlim;
+ RLIM_TYPE limit;
+
+ xprintf("%s \t", lp->limname);
+
+ (void) getrlimit(lp->limconst, &rlim);
+ limit = hard ? rlim.rlim_max : rlim.rlim_cur;
+
+ if (limit == RLIM_INFINITY)
+ xprintf("unlimited");
+ else if (lp->limconst == RLIMIT_CPU)
+ psecs((long) limit);
+ else
+ xprintf("%ld %s", (long) (limit / lp->limdiv), lp->limscale);
+ xprintf("\n");
+}
+
+void
+dounlimit(v)
+ register Char **v;
+{
+ register struct limits *lp;
+ int lerr = 0;
+ Char hard = 0;
+
+ v++;
+ if (*v && eq(*v, STRmh)) {
+ hard = 1;
+ v++;
+ }
+ if (*v == 0) {
+ for (lp = limits; lp->limconst >= 0; lp++)
+ if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
+ lerr++;
+ if (lerr)
+ stderror(ERR_SILENT);
+ return;
+ }
+ while (*v) {
+ lp = findlim(*v++);
+ if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
+ stderror(ERR_SILENT);
+ }
+}
+
+static int
+setlim(lp, hard, limit)
+ register struct limits *lp;
+ Char hard;
+ RLIM_TYPE limit;
+{
+ struct rlimit rlim;
+
+ (void) getrlimit(lp->limconst, &rlim);
+
+ if (hard)
+ rlim.rlim_max = limit;
+ else if (limit == RLIM_INFINITY && geteuid() != 0)
+ rlim.rlim_cur = rlim.rlim_max;
+ else
+ rlim.rlim_cur = limit;
+
+ if (setrlimit(lp->limconst, &rlim) < 0) {
+ xprintf("%s: %s: Can't %s%s limit\n", bname, lp->limname,
+ limit == RLIM_INFINITY ? "remove" : "set",
+ hard ? " hard" : "");
+ return (-1);
+ }
+ return (0);
+}
+
+void
+dosuspend()
+{
+ int ctpgrp;
+
+ void (*old) ();
+
+ if (loginsh)
+ stderror(ERR_SUSPLOG);
+ untty();
+
+ old = signal(SIGTSTP, SIG_DFL);
+ (void) kill(0, SIGTSTP);
+ /* the shell stops here */
+ (void) signal(SIGTSTP, old);
+
+ if (tpgrp != -1) {
+retry:
+ ctpgrp = tcgetpgrp(FSHTTY);
+ if (ctpgrp != opgrp) {
+ old = signal(SIGTTIN, SIG_DFL);
+ (void) kill(0, SIGTTIN);
+ (void) signal(SIGTTIN, old);
+ goto retry;
+ }
+ (void) setpgid(0, shpgrp);
+ (void) tcsetpgrp(FSHTTY, shpgrp);
+ }
+}
+
+/* This is the dreaded EVAL built-in.
+ * If you don't fiddle with file descriptors, and reset didfds,
+ * this command will either ignore redirection inside or outside
+ * its aguments, e.g. eval "date >x" vs. eval "date" >x
+ * The stuff here seems to work, but I did it by trial and error rather
+ * than really knowing what was going on. If tpgrp is zero, we are
+ * probably a background eval, e.g. "eval date &", and we want to
+ * make sure that any processes we start stay in our pgrp.
+ * This is also the case for "time eval date" -- stay in same pgrp.
+ * Otherwise, under stty tostop, processes will stop in the wrong
+ * pgrp, with no way for the shell to get them going again. -IAN!
+ */
+void
+doeval(v)
+ Char **v;
+{
+ Char **oevalvec;
+ Char *oevalp;
+ int odidfds;
+ jmp_buf osetexit;
+ int my_reenter;
+ Char **gv;
+ int saveIN;
+ int saveOUT;
+ int saveDIAG;
+ int oSHIN;
+ int oSHOUT;
+ int oSHDIAG;
+
+ oevalvec = evalvec;
+ oevalp = evalp;
+ odidfds = didfds;
+ oSHIN = SHIN;
+ oSHOUT = SHOUT;
+ oSHDIAG = SHDIAG;
+
+ v++;
+ if (*v == 0)
+ return;
+ gflag = 0, tglob(v);
+ if (gflag) {
+ gv = v = globall(v);
+ gargv = 0;
+ if (v == 0)
+ stderror(ERR_NOMATCH);
+ v = copyblk(v);
+ }
+ else {
+ gv = NULL;
+ v = copyblk(v);
+ trim(v);
+ }
+
+ saveIN = dcopy(SHIN, -1);
+ saveOUT = dcopy(SHOUT, -1);
+ saveDIAG = dcopy(SHDIAG, -1);
+
+ getexit(osetexit);
+
+ if ((my_reenter = setexit()) == 0) {
+ evalvec = v;
+ evalp = 0;
+ SHIN = dcopy(0, -1);
+ SHOUT = dcopy(1, -1);
+ SHDIAG = dcopy(2, -1);
+ didfds = 0;
+ process(0);
+ }
+
+ evalvec = oevalvec;
+ evalp = oevalp;
+ doneinp = 0;
+ didfds = odidfds;
+ (void) close(SHIN);
+ (void) close(SHOUT);
+ (void) close(SHDIAG);
+ SHIN = dmove(saveIN, oSHIN);
+ SHOUT = dmove(saveOUT, oSHOUT);
+ SHDIAG = dmove(saveDIAG, oSHDIAG);
+
+ if (gv)
+ blkfree(gv);
+ resexit(osetexit);
+ if (my_reenter)
+ stderror(ERR_SILENT);
+}
diff --git a/bin/csh/glob.c b/bin/csh/glob.c
new file mode 100644
index 000000000000..3947275c194d
--- /dev/null
+++ b/bin/csh/glob.c
@@ -0,0 +1,837 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)glob.c 5.21 (Berkeley) 6/25/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <glob.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+static int noglob, nonomatch;
+static int pargsiz, gargsiz;
+
+/*
+ * Values for gflag
+ */
+#define G_NONE 0 /* No globbing needed */
+#define G_GLOB 1 /* string contains *?[] characters */
+#define G_CSH 2 /* string contains ~`{ characters */
+
+#define GLOBSPACE 100 /* Alloc increment */
+
+#define LBRC '{'
+#define RBRC '}'
+#define LBRK '['
+#define RBRK ']'
+#define EOS '\0'
+
+Char **gargv = NULL;
+long gargc = 0;
+Char **pargv = NULL;
+long pargc = 0;
+
+/*
+ * globbing is now done in two stages. In the first pass we expand
+ * csh globbing idioms ~`{ and then we proceed doing the normal
+ * globbing if needed ?*[
+ *
+ * Csh type globbing is handled in globexpand() and the rest is
+ * handled in glob() which is part of the 4.4BSD libc.
+ *
+ */
+static Char *globtilde __P((Char **, Char *));
+static Char **libglob __P((Char **));
+static Char **globexpand __P((Char **));
+static int globbrace __P((Char *, Char *, Char ***));
+static void pword __P((void));
+static void psave __P((int));
+static void backeval __P((Char *, bool));
+
+
+static Char *
+globtilde(nv, s)
+ Char **nv, *s;
+{
+ Char gbuf[MAXPATHLEN], *gstart, *b, *u, *e;
+
+ gstart = gbuf;
+ *gstart++ = *s++;
+ u = s;
+ for (b = gstart, e = &gbuf[MAXPATHLEN - 1]; *s && *s != '/' && b < e;
+ *b++ = *s++);
+ *b = EOS;
+ if (gethdir(gstart)) {
+ blkfree(nv);
+ if (*gstart)
+ stderror(ERR_UNKUSER, short2str(gstart));
+ else
+ stderror(ERR_NOHOME);
+ }
+ b = &gstart[Strlen(gstart)];
+ while (*s)
+ *b++ = *s++;
+ *b = EOS;
+ --u;
+ xfree((ptr_t) u);
+ return (Strsave(gstart));
+}
+
+static int
+globbrace(s, p, bl)
+ Char *s, *p, ***bl;
+{
+ int i, len;
+ Char *pm, *pe, *lm, *pl;
+ Char **nv, **vl;
+ Char gbuf[MAXPATHLEN];
+ int size = GLOBSPACE;
+
+ nv = vl = (Char **) xmalloc((size_t) sizeof(Char *) * size);
+ *vl = NULL;
+
+ len = 0;
+ /* copy part up to the brace */
+ for (lm = gbuf, p = s; *p != LBRC; *lm++ = *p++)
+ continue;
+
+ /* check for balanced braces */
+ for (i = 0, pe = ++p; *pe; pe++)
+ if (*pe == LBRK) {
+ /* Ignore everything between [] */
+ for (++pe; *pe != RBRK && *pe != EOS; pe++)
+ continue;
+ if (*pe == EOS) {
+ blkfree(nv);
+ return (-LBRK);
+ }
+ }
+ else if (*pe == LBRC)
+ i++;
+ else if (*pe == RBRC) {
+ if (i == 0)
+ break;
+ i--;
+ }
+
+ if (i != 0) {
+ blkfree(nv);
+ return (-LBRC);
+ }
+
+ for (i = 0, pl = pm = p; pm <= pe; pm++)
+ switch (*pm) {
+ case LBRK:
+ for (++pm; *pm != RBRK && *pm != EOS; pm++)
+ continue;
+ if (*pm == EOS) {
+ *vl = NULL;
+ blkfree(nv);
+ return (-RBRK);
+ }
+ break;
+ case LBRC:
+ i++;
+ break;
+ case RBRC:
+ if (i) {
+ i--;
+ break;
+ }
+ /* FALLTHROUGH */
+ case ',':
+ if (i && *pm == ',')
+ break;
+ else {
+ Char savec = *pm;
+
+ *pm = EOS;
+ (void) Strcpy(lm, pl);
+ (void) Strcat(gbuf, pe + 1);
+ *pm = savec;
+ *vl++ = Strsave(gbuf);
+ len++;
+ pl = pm + 1;
+ if (vl == &nv[size]) {
+ size += GLOBSPACE;
+ nv = (Char **) xrealloc((ptr_t) nv, (size_t)
+ size * sizeof(Char *));
+ vl = &nv[size - GLOBSPACE];
+ }
+ }
+ break;
+ }
+ *vl = NULL;
+ *bl = nv;
+ return (len);
+}
+
+static Char **
+globexpand(v)
+ Char **v;
+{
+ Char *s;
+ Char **nv, **vl, **el;
+ int size = GLOBSPACE;
+
+
+ nv = vl = (Char **) xmalloc((size_t) sizeof(Char *) * size);
+ *vl = NULL;
+
+ /*
+ * Step 1: expand backquotes.
+ */
+ while (s = *v++) {
+ if (Strchr(s, '`')) {
+ int i;
+
+ (void) dobackp(s, 0);
+ for (i = 0; i < pargc; i++) {
+ *vl++ = pargv[i];
+ if (vl == &nv[size]) {
+ size += GLOBSPACE;
+ nv = (Char **) xrealloc((ptr_t) nv,
+ (size_t) size * sizeof(Char *));
+ vl = &nv[size - GLOBSPACE];
+ }
+ }
+ xfree((ptr_t) pargv);
+ pargv = NULL;
+ }
+ else {
+ *vl++ = Strsave(s);
+ if (vl == &nv[size]) {
+ size += GLOBSPACE;
+ nv = (Char **) xrealloc((ptr_t) nv, (size_t)
+ size * sizeof(Char *));
+ vl = &nv[size - GLOBSPACE];
+ }
+ }
+ }
+ *vl = NULL;
+
+ if (noglob)
+ return (nv);
+
+ /*
+ * Step 2: expand braces
+ */
+ el = vl;
+ vl = nv;
+ for (s = *vl; s; s = *++vl) {
+ Char *b;
+ Char **vp, **bp;
+
+ if (b = Strchr(s, LBRC)) {
+ Char **bl;
+ int len;
+
+ if ((len = globbrace(s, b, &bl)) < 0) {
+ blkfree(nv);
+ stderror(ERR_MISSING, -len);
+ }
+ xfree((ptr_t) s);
+ if (len == 1) {
+ *vl-- = *bl;
+ xfree((ptr_t) bl);
+ continue;
+ }
+ len = blklen(bl);
+ if (&el[len] >= &nv[size]) {
+ int l, e;
+
+ l = &el[len] - &nv[size];
+ size += GLOBSPACE > l ? GLOBSPACE : l;
+ l = vl - nv;
+ e = el - nv;
+ nv = (Char **) xrealloc((ptr_t) nv, (size_t)
+ size * sizeof(Char *));
+ vl = nv + l;
+ el = nv + e;
+ }
+ vp = vl--;
+ *vp = *bl;
+ len--;
+ for (bp = el; bp != vp; bp--)
+ bp[len] = *bp;
+ el += len;
+ vp++;
+ for (bp = bl + 1; *bp; *vp++ = *bp++)
+ continue;
+ xfree((ptr_t) bl);
+ }
+
+ }
+
+ /*
+ * Step 3: expand ~
+ */
+ vl = nv;
+ for (s = *vl; s; s = *++vl)
+ if (*s == '~')
+ *vl = globtilde(nv, s);
+ vl = nv;
+ return (vl);
+}
+
+static Char *
+handleone(str, vl, action)
+ Char *str, **vl;
+ int action;
+{
+
+ Char *cp, **vlp = vl;
+
+ switch (action) {
+ case G_ERROR:
+ setname(short2str(str));
+ blkfree(vl);
+ stderror(ERR_NAME | ERR_AMBIG);
+ break;
+ case G_APPEND:
+ trim(vlp);
+ str = Strsave(*vlp++);
+ do {
+ cp = Strspl(str, STRspace);
+ xfree((ptr_t) str);
+ str = Strspl(cp, *vlp);
+ xfree((ptr_t) cp);
+ }
+ while (*++vlp);
+ blkfree(vl);
+ break;
+ case G_IGNORE:
+ str = Strsave(strip(*vlp));
+ blkfree(vl);
+ break;
+ }
+ return (str);
+}
+
+static Char **
+libglob(vl)
+ Char **vl;
+{
+ int gflgs = GLOB_QUOTE | GLOB_NOCHECK;
+ glob_t globv;
+ char *ptr;
+
+ globv.gl_offs = 0;
+ globv.gl_pathv = 0;
+ globv.gl_pathc = 0;
+ nonomatch = adrof(STRnonomatch) != 0;
+ do {
+ ptr = short2qstr(*vl);
+ switch (glob(ptr, gflgs, 0, &globv)) {
+ case GLOB_ABEND:
+ setname(ptr);
+ stderror(ERR_NAME | ERR_GLOB);
+ /* NOTREACHED */
+ case GLOB_NOSPACE:
+ stderror(ERR_NOMEM);
+ /* NOTREACHED */
+ default:
+ break;
+ }
+ if (!nonomatch && (globv.gl_matchc == 0) &&
+ (globv.gl_flags & GLOB_MAGCHAR)) {
+ globfree(&globv);
+ return (NULL);
+ }
+ gflgs |= GLOB_APPEND;
+ }
+ while (*++vl);
+ vl = blk2short(globv.gl_pathv);
+ globfree(&globv);
+ return (vl);
+}
+
+Char *
+globone(str, action)
+ Char *str;
+ int action;
+{
+
+ Char *v[2], **vl, **vo;
+
+ noglob = adrof(STRnoglob) != 0;
+ gflag = 0;
+ v[0] = str;
+ v[1] = 0;
+ tglob(v);
+ if (gflag == G_NONE)
+ return (strip(Strsave(str)));
+
+ if (gflag & G_CSH) {
+ /*
+ * Expand back-quote, tilde and brace
+ */
+ vo = globexpand(v);
+ if (noglob || (gflag & G_GLOB) == 0) {
+ if (vo[0] == NULL) {
+ xfree((ptr_t) vo);
+ return (Strsave(STRNULL));
+ }
+ if (vo[1] != NULL)
+ return (handleone(str, vo, action));
+ else {
+ str = strip(vo[0]);
+ xfree((ptr_t) vo);
+ return (str);
+ }
+ }
+ }
+ else if (noglob || (gflag & G_GLOB) == 0)
+ return (strip(Strsave(str)));
+ else
+ vo = v;
+
+ vl = libglob(vo);
+ if (gflag & G_CSH)
+ blkfree(vo);
+ if (vl == NULL) {
+ setname(short2str(str));
+ stderror(ERR_NAME | ERR_NOMATCH);
+ }
+ if (vl[0] == NULL) {
+ xfree((ptr_t) vl);
+ return (Strsave(STRNULL));
+ }
+ if (vl[1] != NULL)
+ return (handleone(str, vl, action));
+ else {
+ str = strip(*vl);
+ xfree((ptr_t) vl);
+ return (str);
+ }
+}
+
+Char **
+globall(v)
+ Char **v;
+{
+ Char **vl, **vo;
+
+ if (!v || !v[0]) {
+ gargv = saveblk(v);
+ gargc = blklen(gargv);
+ return (gargv);
+ }
+
+ noglob = adrof(STRnoglob) != 0;
+
+ if (gflag & G_CSH)
+ /*
+ * Expand back-quote, tilde and brace
+ */
+ vl = vo = globexpand(v);
+ else
+ vl = vo = saveblk(v);
+
+ if (!noglob && (gflag & G_GLOB)) {
+ vl = libglob(vo);
+ if (gflag & G_CSH)
+ blkfree(vo);
+ }
+
+ gargc = vl ? blklen(vl) : 0;
+ return (gargv = vl);
+}
+
+void
+ginit()
+{
+ gargsiz = GLOBSPACE;
+ gargv = (Char **) xmalloc((size_t) sizeof(Char *) * gargsiz);
+ gargv[0] = 0;
+ gargc = 0;
+}
+
+void
+rscan(t, f)
+ register Char **t;
+ void (*f) ();
+{
+ register Char *p;
+
+ while (p = *t++)
+ while (*p)
+ (*f) (*p++);
+}
+
+void
+trim(t)
+ register Char **t;
+{
+ register Char *p;
+
+ while (p = *t++)
+ while (*p)
+ *p++ &= TRIM;
+}
+
+void
+tglob(t)
+ register Char **t;
+{
+ register Char *p, c;
+
+ while (p = *t++) {
+ if (*p == '~' || *p == '=')
+ gflag |= G_CSH;
+ else if (*p == '{' &&
+ (p[1] == '\0' || p[1] == '}' && p[2] == '\0'))
+ continue;
+ while (c = *p++)
+ if (isglob(c))
+ gflag |= (c == '{' || c == '`') ? G_CSH : G_GLOB;
+ }
+}
+
+/*
+ * Command substitute cp. If literal, then this is a substitution from a
+ * << redirection, and so we should not crunch blanks and tabs, separating
+ * words only at newlines.
+ */
+Char **
+dobackp(cp, literal)
+ Char *cp;
+ bool literal;
+{
+ register Char *lp, *rp;
+ Char *ep, word[MAXPATHLEN];
+
+ if (pargv) {
+ abort();
+ blkfree(pargv);
+ }
+ pargsiz = GLOBSPACE;
+ pargv = (Char **) xmalloc((size_t) sizeof(Char *) * pargsiz);
+ pargv[0] = NULL;
+ pargcp = pargs = word;
+ pargc = 0;
+ pnleft = MAXPATHLEN - 4;
+ for (;;) {
+ for (lp = cp; *lp != '`'; lp++) {
+ if (*lp == 0) {
+ if (pargcp != pargs)
+ pword();
+ return (pargv);
+ }
+ psave(*lp);
+ }
+ lp++;
+ for (rp = lp; *rp && *rp != '`'; rp++)
+ if (*rp == '\\') {
+ rp++;
+ if (!*rp)
+ goto oops;
+ }
+ if (!*rp)
+ oops: stderror(ERR_UNMATCHED, '`');
+ ep = Strsave(lp);
+ ep[rp - lp] = 0;
+ backeval(ep, literal);
+ cp = rp + 1;
+ }
+}
+
+static void
+backeval(cp, literal)
+ Char *cp;
+ bool literal;
+{
+ register int icnt, c;
+ register Char *ip;
+ struct command faket;
+ bool hadnl;
+ int pvec[2], quoted;
+ Char *fakecom[2], ibuf[BUFSIZ];
+ char tibuf[BUFSIZ];
+
+ hadnl = 0;
+ icnt = 0;
+ quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0;
+ faket.t_dtyp = NODE_COMMAND;
+ faket.t_dflg = 0;
+ faket.t_dlef = 0;
+ faket.t_drit = 0;
+ faket.t_dspr = 0;
+ faket.t_dcom = fakecom;
+ fakecom[0] = STRfakecom1;
+ fakecom[1] = 0;
+
+ /*
+ * We do the psave job to temporarily change the current job so that the
+ * following fork is considered a separate job. This is so that when
+ * backquotes are used in a builtin function that calls glob the "current
+ * job" is not corrupted. We only need one level of pushed jobs as long as
+ * we are sure to fork here.
+ */
+ psavejob();
+
+ /*
+ * It would be nicer if we could integrate this redirection more with the
+ * routines in sh.sem.c by doing a fake execute on a builtin function that
+ * was piped out.
+ */
+ mypipe(pvec);
+ if (pfork(&faket, -1) == 0) {
+ struct wordent paraml;
+ struct command *t;
+
+ (void) close(pvec[0]);
+ (void) dmove(pvec[1], 1);
+ (void) dmove(SHDIAG, 2);
+ initdesc();
+ /*
+ * Bugfix for nested backquotes by Michael Greim <greim@sbsvax.UUCP>,
+ * posted to comp.bugs.4bsd 12 Sep. 1989.
+ */
+ if (pargv) /* mg, 21.dec.88 */
+ blkfree(pargv), pargv = 0, pargsiz = 0;
+ /* mg, 21.dec.88 */
+ arginp = cp;
+ while (*cp)
+ *cp++ &= TRIM;
+ (void) lex(&paraml);
+ if (seterr)
+ stderror(ERR_OLD);
+ alias(&paraml);
+ t = syntax(paraml.next, &paraml, 0);
+ if (seterr)
+ stderror(ERR_OLD);
+ if (t)
+ t->t_dflg |= F_NOFORK;
+ (void) signal(SIGTSTP, SIG_IGN);
+ (void) signal(SIGTTIN, SIG_IGN);
+ (void) signal(SIGTTOU, SIG_IGN);
+ execute(t, -1, NULL, NULL);
+ exitstat();
+ }
+ xfree((ptr_t) cp);
+ (void) close(pvec[1]);
+ c = 0;
+ ip = NULL;
+ do {
+ int cnt = 0;
+
+ for (;;) {
+ if (icnt == 0) {
+ int i;
+
+ ip = ibuf;
+ do
+ icnt = read(pvec[0], tibuf, BUFSIZ);
+ while (icnt == -1 && errno == EINTR);
+ if (icnt <= 0) {
+ c = -1;
+ break;
+ }
+ for (i = 0; i < icnt; i++)
+ ip[i] = (unsigned char) tibuf[i];
+ }
+ if (hadnl)
+ break;
+ --icnt;
+ c = (*ip++ & TRIM);
+ if (c == 0)
+ break;
+ if (c == '\n') {
+ /*
+ * Continue around the loop one more time, so that we can eat
+ * the last newline without terminating this word.
+ */
+ hadnl = 1;
+ continue;
+ }
+ if (!quoted && (c == ' ' || c == '\t'))
+ break;
+ cnt++;
+ psave(c | quoted);
+ }
+ /*
+ * Unless at end-of-file, we will form a new word here if there were
+ * characters in the word, or in any case when we take text literally.
+ * If we didn't make empty words here when literal was set then we
+ * would lose blank lines.
+ */
+ if (c != -1 && (cnt || literal))
+ pword();
+ hadnl = 0;
+ } while (c >= 0);
+ (void) close(pvec[0]);
+ pwait();
+ prestjob();
+}
+
+static void
+psave(c)
+ int c;
+{
+ if (--pnleft <= 0)
+ stderror(ERR_WTOOLONG);
+ *pargcp++ = c;
+}
+
+static void
+pword()
+{
+ psave(0);
+ if (pargc == pargsiz - 1) {
+ pargsiz += GLOBSPACE;
+ pargv = (Char **) xrealloc((ptr_t) pargv,
+ (size_t) pargsiz * sizeof(Char *));
+ }
+ pargv[pargc++] = Strsave(pargs);
+ pargv[pargc] = NULL;
+ pargcp = pargs;
+ pnleft = MAXPATHLEN - 4;
+}
+
+int
+Gmatch(string, pattern)
+ register Char *string, *pattern;
+{
+ register Char stringc, patternc;
+ int match;
+ Char rangec;
+
+ for (;; ++string) {
+ stringc = *string & TRIM;
+ patternc = *pattern++;
+ switch (patternc) {
+ case 0:
+ return (stringc == 0);
+ case '?':
+ if (stringc == 0)
+ return (0);
+ break;
+ case '*':
+ if (!*pattern)
+ return (1);
+ while (*string)
+ if (Gmatch(string++, pattern))
+ return (1);
+ return (0);
+ case '[':
+ match = 0;
+ while (rangec = *pattern++) {
+ if (rangec == ']')
+ if (match)
+ break;
+ else
+ return (0);
+ if (match)
+ continue;
+ if (rangec == '-' && *(pattern - 2) != '[' && *pattern != ']') {
+ match = (stringc <= (*pattern & TRIM) &&
+ (*(pattern - 2) & TRIM) <= stringc);
+ pattern++;
+ }
+ else
+ match = (stringc == rangec);
+ }
+ if (rangec == 0)
+ stderror(ERR_NAME | ERR_MISSING, ']');
+ break;
+ default:
+ if ((patternc & TRIM) != stringc)
+ return (0);
+ break;
+
+ }
+ }
+}
+
+void
+Gcat(s1, s2)
+ Char *s1, *s2;
+{
+ register Char *p, *q;
+ int n;
+
+ for (p = s1; *p++;);
+ for (q = s2; *q++;);
+ n = (p - s1) + (q - s2) - 1;
+ if (++gargc >= gargsiz) {
+ gargsiz += GLOBSPACE;
+ gargv = (Char **) xrealloc((ptr_t) gargv,
+ (size_t) gargsiz * sizeof(Char *));
+ }
+ gargv[gargc] = 0;
+ p = gargv[gargc - 1] = (Char *) xmalloc((size_t) n * sizeof(Char));
+ for (q = s1; *p++ = *q++;);
+ for (p--, q = s2; *p++ = *q++;);
+}
+
+#ifdef FILEC
+int
+sortscmp(a, b)
+ register Char **a, **b;
+{
+#if defined(NLS) && !defined(NOSTRCOLL)
+ char buf[2048];
+
+#endif
+
+ if (!a) /* check for NULL */
+ return (b ? 1 : 0);
+ if (!b)
+ return (-1);
+
+ if (!*a) /* check for NULL */
+ return (*b ? 1 : 0);
+ if (!*b)
+ return (-1);
+
+#if defined(NLS) && !defined(NOSTRCOLL)
+ (void) strcpy(buf, short2str(*a));
+ return ((int) strcoll(buf, short2str(*b)));
+#else
+ return ((int) Strcmp(*a, *b));
+#endif
+}
+#endif /* FILEC */
diff --git a/bin/csh/hist.c b/bin/csh/hist.c
new file mode 100644
index 000000000000..745b248231dd
--- /dev/null
+++ b/bin/csh/hist.c
@@ -0,0 +1,206 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hist.c 5.9 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+static void hfree __P((struct Hist *));
+static void dohist1 __P((struct Hist *, int *, int, int, int));
+static void phist __P((struct Hist *, int, int));
+
+void
+savehist(sp)
+ struct wordent *sp;
+{
+ register struct Hist *hp, *np;
+ register int histlen = 0;
+ Char *cp;
+
+ /* throw away null lines */
+ if (sp->next->word[0] == '\n')
+ return;
+ cp = value(STRhistory);
+ if (*cp) {
+ register Char *p = cp;
+
+ while (*p) {
+ if (!Isdigit(*p)) {
+ histlen = 0;
+ break;
+ }
+ histlen = histlen * 10 + *p++ - '0';
+ }
+ }
+ for (hp = &Histlist; np = hp->Hnext;)
+ if (eventno - np->Href >= histlen || histlen == 0)
+ hp->Hnext = np->Hnext, hfree(np);
+ else
+ hp = np;
+ (void) enthist(++eventno, sp, 1);
+}
+
+struct Hist *
+enthist(event, lp, docopy)
+ int event;
+ register struct wordent *lp;
+ bool docopy;
+{
+ register struct Hist *np;
+
+ np = (struct Hist *) xmalloc((size_t) sizeof(*np));
+ (void) time(&(np->Htime));
+ np->Hnum = np->Href = event;
+ if (docopy) {
+ copylex(&np->Hlex, lp);
+ }
+ else {
+ np->Hlex.next = lp->next;
+ lp->next->prev = &np->Hlex;
+ np->Hlex.prev = lp->prev;
+ lp->prev->next = &np->Hlex;
+ }
+ np->Hnext = Histlist.Hnext;
+ Histlist.Hnext = np;
+ return (np);
+}
+
+static void
+hfree(hp)
+ register struct Hist *hp;
+{
+
+ freelex(&hp->Hlex);
+ xfree((ptr_t) hp);
+}
+
+void
+dohist(vp)
+ Char **vp;
+{
+ int n, rflg = 0, hflg = 0, tflg = 0;
+
+ if (getn(value(STRhistory)) == 0)
+ return;
+ if (setintr)
+ (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT));
+ while (*++vp && **vp == '-') {
+ Char *vp2 = *vp;
+
+ while (*++vp2)
+ switch (*vp2) {
+ case 'h':
+ hflg++;
+ break;
+ case 'r':
+ rflg++;
+ break;
+ case 't':
+ tflg++;
+ break;
+ case '-': /* ignore multiple '-'s */
+ break;
+ default:
+ stderror(ERR_HISTUS);
+ break;
+ }
+ }
+ if (*vp)
+ n = getn(*vp);
+ else {
+ n = getn(value(STRhistory));
+ }
+ dohist1(Histlist.Hnext, &n, rflg, hflg, tflg);
+}
+
+static void
+dohist1(hp, np, rflg, hflg, tflg)
+ struct Hist *hp;
+ int *np, rflg, hflg, tflg;
+{
+ bool print = (*np) > 0;
+
+ for (; hp != 0; hp = hp->Hnext) {
+ (*np)--;
+ hp->Href++;
+ if (rflg == 0) {
+ dohist1(hp->Hnext, np, rflg, hflg, tflg);
+ if (print)
+ phist(hp, hflg, tflg);
+ return;
+ }
+ if (*np >= 0)
+ phist(hp, hflg, tflg);
+ }
+}
+
+static void
+phist(hp, hflg, tflg)
+ register struct Hist *hp;
+ int hflg, tflg;
+{
+ struct tm *t;
+ char ampm = 'a';
+
+ if (hflg == 0) {
+ xprintf("%6d\t", hp->Hnum);
+ if (tflg == 0) {
+ t = localtime(&hp->Htime);
+ if (adrof(STRampm)) { /* addition by Hans J. Albertsson */
+ if (t->tm_hour >= 12) {
+ if (t->tm_hour > 12)
+ t->tm_hour -= 12;
+ ampm = 'p';
+ }
+ else if (t->tm_hour == 0)
+ t->tm_hour = 12;
+ xprintf("%2d:%02d%cm\t", t->tm_hour, t->tm_min, ampm);
+ }
+ else {
+ xprintf("%2d:%02d\t", t->tm_hour, t->tm_min);
+ }
+ }
+ }
+ prlex(&hp->Hlex);
+}
diff --git a/bin/csh/init.c b/bin/csh/init.c
new file mode 100644
index 000000000000..de076a4149e5
--- /dev/null
+++ b/bin/csh/init.c
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)init.c 5.12 (Berkeley) 6/27/91";
+#endif /* not lint */
+
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+#define INF 1000
+
+struct biltins bfunc[] =
+{
+ "@", dolet, 0, INF,
+ "alias", doalias, 0, INF,
+ "alloc", showall, 0, 1,
+ "bg", dobg, 0, INF,
+ "break", dobreak, 0, 0,
+ "breaksw", doswbrk, 0, 0,
+ "case", dozip, 0, 1,
+ "cd", dochngd, 0, INF,
+ "chdir", dochngd, 0, INF,
+ "continue", docontin, 0, 0,
+ "default", dozip, 0, 0,
+ "dirs", dodirs, 0, INF,
+ "echo", doecho, 0, INF,
+ "else", doelse, 0, INF,
+ "end", doend, 0, 0,
+ "endif", dozip, 0, 0,
+ "endsw", dozip, 0, 0,
+ "eval", doeval, 0, INF,
+ "exec", execash, 1, INF,
+ "exit", doexit, 0, INF,
+ "fg", dofg, 0, INF,
+ "foreach", doforeach, 3, INF,
+ "glob", doglob, 0, INF,
+ "goto", dogoto, 1, 1,
+ "hashstat", hashstat, 0, 0,
+ "history", dohist, 0, 2,
+ "if", doif, 1, INF,
+ "jobs", dojobs, 0, 1,
+ "kill", dokill, 1, INF,
+ "limit", dolimit, 0, 3,
+ "linedit", doecho, 0, INF,
+ "login", dologin, 0, 1,
+ "logout", dologout, 0, 0,
+ "nice", donice, 0, INF,
+ "nohup", donohup, 0, INF,
+ "notify", donotify, 0, INF,
+ "onintr", doonintr, 0, 2,
+ "popd", dopopd, 0, INF,
+ "pushd", dopushd, 0, INF,
+ "rehash", dohash, 0, 0,
+ "repeat", dorepeat, 2, INF,
+ "set", doset, 0, INF,
+ "setenv", dosetenv, 0, 2,
+ "shift", shift, 0, 1,
+ "source", dosource, 1, 2,
+ "stop", dostop, 1, INF,
+ "suspend", dosuspend, 0, 0,
+ "switch", doswitch, 1, INF,
+ "time", dotime, 0, INF,
+ "umask", doumask, 0, 1,
+ "unalias", unalias, 1, INF,
+ "unhash", dounhash, 0, 0,
+ "unlimit", dounlimit, 0, INF,
+ "unset", unset, 1, INF,
+ "unsetenv", dounsetenv, 1, INF,
+ "wait", dowait, 0, 0,
+ "while", dowhile, 1, INF,
+};
+int nbfunc = sizeof bfunc / sizeof *bfunc;
+
+struct srch srchn[] =
+{
+ "@", T_LET,
+ "break", T_BREAK,
+ "breaksw", T_BRKSW,
+ "case", T_CASE,
+ "default", T_DEFAULT,
+ "else", T_ELSE,
+ "end", T_END,
+ "endif", T_ENDIF,
+ "endsw", T_ENDSW,
+ "exit", T_EXIT,
+ "foreach", T_FOREACH,
+ "goto", T_GOTO,
+ "if", T_IF,
+ "label", T_LABEL,
+ "set", T_SET,
+ "switch", T_SWITCH,
+ "while", T_WHILE,
+};
+int nsrchn = sizeof srchn / sizeof *srchn;
+
+struct mesg mesg[] =
+{
+ /* 0 */ 0, "",
+ /* 1 */ "HUP", "Hangup",
+ /* 2 */ "INT", "Interrupt",
+ /* 3 */ "QUIT", "Quit",
+ /* 4 */ "ILL", "Illegal instruction",
+ /* 5 */ "TRAP", "Trace/BPT trap",
+ /* 6 */ "IOT", "IOT trap",
+ /* 7 */ "EMT", "EMT trap",
+ /* 8 */ "FPE", "Floating exception",
+ /* 9 */ "KILL", "Killed",
+ /* 10 */ "BUS", "Bus error",
+ /* 11 */ "SEGV", "Segmentation fault",
+ /* 12 */ "SYS", "Bad system call",
+ /* 13 */ "PIPE", "Broken pipe",
+ /* 14 */ "ALRM", "Alarm clock",
+ /* 15 */ "TERM", "Terminated",
+ /* 16 */ "URG", "Urgent condition on IO channel",
+ /* 17 */ "STOP", "Suspended (signal)",
+ /* 18 */ "TSTP", "Suspended",
+ /* 19 */ "CONT", "Continued",
+ /* 20 */ "CHLD", "Child exited",
+ /* 21 */ "TTIN", "Suspended (tty input)",
+ /* 22 */ "TTOU", "Suspended (tty output)",
+ /* 23 */ "IO", "IO possible interrupt",
+ /* 24 */ "XCPU", "Cputime limit exceeded",
+ /* 25 */ "XFSZ", "Filesize limit exceeded",
+ /* 26 */ "VTALRM", "Virtual time alarm",
+ /* 27 */ "PROF", "Profiling time alarm",
+ /* 28 */ "WINCH", "Window changed",
+ /* 29 */ "INFO", "Information request",
+ /* 30 */ "USR1", "User signal 1",
+ /* 31 */ "USR2", "User signal 2",
+ /* 32 */ 0, "Signal 32",
+};
diff --git a/bin/csh/lex.c b/bin/csh/lex.c
new file mode 100644
index 000000000000..63af5da774a4
--- /dev/null
+++ b/bin/csh/lex.c
@@ -0,0 +1,1527 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lex.c 5.16 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+/*
+ * These lexical routines read input and form lists of words.
+ * There is some involved processing here, because of the complications
+ * of input buffering, and especially because of history substitution.
+ */
+
+static Char *word __P((void));
+static int getC1 __P((int));
+static void getdol __P((void));
+static void getexcl __P((int));
+static struct Hist
+ *findev __P((Char *, bool));
+static void setexclp __P((Char *));
+static int bgetc __P((void));
+static void bfree __P((void));
+static struct wordent
+ *gethent __P((int));
+static int matchs __P((Char *, Char *));
+static int getsel __P((int *, int *, int));
+static struct wordent
+ *getsub __P((struct wordent *));
+static Char *subword __P((Char *, int, bool *));
+static struct wordent
+ *dosub __P((int, struct wordent *, bool));
+
+/*
+ * Peekc is a peek character for getC, peekread for readc.
+ * There is a subtlety here in many places... history routines
+ * will read ahead and then insert stuff into the input stream.
+ * If they push back a character then they must push it behind
+ * the text substituted by the history substitution. On the other
+ * hand in several places we need 2 peek characters. To make this
+ * all work, the history routines read with getC, and make use both
+ * of ungetC and unreadc. The key observation is that the state
+ * of getC at the call of a history reference is such that calls
+ * to getC from the history routines will always yield calls of
+ * readc, unless this peeking is involved. That is to say that during
+ * getexcl the variables lap, exclp, and exclnxt are all zero.
+ *
+ * Getdol invokes history substitution, hence the extra peek, peekd,
+ * which it can ungetD to be before history substitutions.
+ */
+static Char peekc = 0, peekd = 0;
+static Char peekread = 0;
+
+/* (Tail of) current word from ! subst */
+static Char *exclp = NULL;
+
+/* The rest of the ! subst words */
+static struct wordent *exclnxt = NULL;
+
+/* Count of remaining words in ! subst */
+static int exclc = 0;
+
+/* "Globp" for alias resubstitution */
+static Char *alvecp = NULL;
+
+/*
+ * Labuf implements a general buffer for lookahead during lexical operations.
+ * Text which is to be placed in the input stream can be stuck here.
+ * We stick parsed ahead $ constructs during initial input,
+ * process id's from `$$', and modified variable values (from qualifiers
+ * during expansion in sh.dol.c) here.
+ */
+static Char labuf[BUFSIZ];
+
+/*
+ * Lex returns to its caller not only a wordlist (as a "var" parameter)
+ * but also whether a history substitution occurred. This is used in
+ * the main (process) routine to determine whether to echo, and also
+ * when called by the alias routine to determine whether to keep the
+ * argument list.
+ */
+static bool hadhist = 0;
+
+/*
+ * Avoid alias expansion recursion via \!#
+ */
+int hleft;
+
+static Char getCtmp;
+
+#define getC(f) ((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f))
+#define ungetC(c) peekc = c
+#define ungetD(c) peekd = c
+
+int
+lex(hp)
+ register struct wordent *hp;
+{
+ register struct wordent *wdp;
+ int c;
+
+ lineloc = fseekp;
+ hp->next = hp->prev = hp;
+ hp->word = STRNULL;
+ alvecp = 0, hadhist = 0;
+ do
+ c = readc(0);
+ while (c == ' ' || c == '\t');
+ if (c == HISTSUB && intty)
+ /* ^lef^rit from tty is short !:s^lef^rit */
+ getexcl(c);
+ else
+ unreadc(c);
+ wdp = hp;
+ /*
+ * The following loop is written so that the links needed by freelex will
+ * be ready and rarin to go even if it is interrupted.
+ */
+ do {
+ register struct wordent *new;
+
+ new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
+ new->word = 0;
+ new->prev = wdp;
+ new->next = hp;
+ wdp->next = new;
+ wdp = new;
+ wdp->word = word();
+ } while (wdp->word[0] != '\n');
+ hp->prev = wdp;
+ return (hadhist);
+}
+
+void
+prlex(sp0)
+ struct wordent *sp0;
+{
+ register struct wordent *sp = sp0->next;
+
+ for (;;) {
+ xprintf("%s", short2str(sp->word));
+ sp = sp->next;
+ if (sp == sp0)
+ break;
+ if (sp->word[0] != '\n')
+ xputchar(' ');
+ }
+}
+
+void
+copylex(hp, fp)
+ register struct wordent *hp;
+ register struct wordent *fp;
+{
+ register struct wordent *wdp;
+
+ wdp = hp;
+ fp = fp->next;
+ do {
+ register struct wordent *new;
+
+ new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
+ new->prev = wdp;
+ new->next = hp;
+ wdp->next = new;
+ wdp = new;
+ wdp->word = Strsave(fp->word);
+ fp = fp->next;
+ } while (wdp->word[0] != '\n');
+ hp->prev = wdp;
+}
+
+void
+freelex(vp)
+ register struct wordent *vp;
+{
+ register struct wordent *fp;
+
+ while (vp->next != vp) {
+ fp = vp->next;
+ vp->next = fp->next;
+ xfree((ptr_t) fp->word);
+ xfree((ptr_t) fp);
+ }
+ vp->prev = vp;
+}
+
+static Char *
+word()
+{
+ register Char c, c1;
+ register Char *wp;
+ Char wbuf[BUFSIZ];
+ register bool dolflg;
+ register int i;
+
+ wp = wbuf;
+ i = BUFSIZ - 4;
+loop:
+ while ((c = getC(DOALL)) == ' ' || c == '\t');
+ if (cmap(c, _META | _ESC))
+ switch (c) {
+ case '&':
+ case '|':
+ case '<':
+ case '>':
+ *wp++ = c;
+ c1 = getC(DOALL);
+ if (c1 == c)
+ *wp++ = c1;
+ else
+ ungetC(c1);
+ goto ret;
+
+ case '#':
+ if (intty)
+ break;
+ c = 0;
+ do {
+ c1 = c;
+ c = getC(0);
+ } while (c != '\n');
+ if (c1 == '\\')
+ goto loop;
+ /* fall into ... */
+
+ case ';':
+ case '(':
+ case ')':
+ case '\n':
+ *wp++ = c;
+ goto ret;
+
+ case '\\':
+ c = getC(0);
+ if (c == '\n') {
+ if (onelflg == 1)
+ onelflg = 2;
+ goto loop;
+ }
+ if (c != HIST)
+ *wp++ = '\\', --i;
+ c |= QUOTE;
+ }
+ c1 = 0;
+ dolflg = DOALL;
+ for (;;) {
+ if (c1) {
+ if (c == c1) {
+ c1 = 0;
+ dolflg = DOALL;
+ }
+ else if (c == '\\') {
+ c = getC(0);
+ if (c == HIST)
+ c |= QUOTE;
+ else {
+ if (c == '\n')
+ /*
+ * if (c1 == '`') c = ' '; else
+ */
+ c |= QUOTE;
+ ungetC(c);
+ c = '\\';
+ }
+ }
+ else if (c == '\n') {
+ seterror(ERR_UNMATCHED, c1);
+ ungetC(c);
+ break;
+ }
+ }
+ else if (cmap(c, _META | _Q | _Q1 | _ESC)) {
+ if (c == '\\') {
+ c = getC(0);
+ if (c == '\n') {
+ if (onelflg == 1)
+ onelflg = 2;
+ break;
+ }
+ if (c != HIST)
+ *wp++ = '\\', --i;
+ c |= QUOTE;
+ }
+ else if (cmap(c, _Q | _Q1)) { /* '"` */
+ c1 = c;
+ dolflg = c == '"' ? DOALL : DOEXCL;
+ }
+ else if (c != '#' || !intty) {
+ ungetC(c);
+ break;
+ }
+ }
+ if (--i > 0) {
+ *wp++ = c;
+ c = getC(dolflg);
+ }
+ else {
+ seterror(ERR_WTOOLONG);
+ wp = &wbuf[1];
+ break;
+ }
+ }
+ret:
+ *wp = 0;
+ return (Strsave(wbuf));
+}
+
+static int
+getC1(flag)
+ register int flag;
+{
+ register Char c;
+
+ while (1) {
+ if (c = peekc) {
+ peekc = 0;
+ return (c);
+ }
+ if (lap) {
+ if ((c = *lap++) == 0)
+ lap = 0;
+ else {
+ if (cmap(c, _META | _Q | _Q1))
+ c |= QUOTE;
+ return (c);
+ }
+ }
+ if (c = peekd) {
+ peekd = 0;
+ return (c);
+ }
+ if (exclp) {
+ if (c = *exclp++)
+ return (c);
+ if (exclnxt && --exclc >= 0) {
+ exclnxt = exclnxt->next;
+ setexclp(exclnxt->word);
+ return (' ');
+ }
+ exclp = 0;
+ exclnxt = 0;
+ }
+ if (exclnxt) {
+ exclnxt = exclnxt->next;
+ if (--exclc < 0)
+ exclnxt = 0;
+ else
+ setexclp(exclnxt->word);
+ continue;
+ }
+ c = readc(0);
+ if (c == '$' && (flag & DODOL)) {
+ getdol();
+ continue;
+ }
+ if (c == HIST && (flag & DOEXCL)) {
+ getexcl(0);
+ continue;
+ }
+ break;
+ }
+ return (c);
+}
+
+static void
+getdol()
+{
+ register Char *np, *ep;
+ Char name[4 * MAXVARLEN + 1];
+ register int c;
+ int sc;
+ bool special = 0, toolong;
+
+ np = name, *np++ = '$';
+ c = sc = getC(DOEXCL);
+ if (any("\t \n", c)) {
+ ungetD(c);
+ ungetC('$' | QUOTE);
+ return;
+ }
+ if (c == '{')
+ *np++ = c, c = getC(DOEXCL);
+ if (c == '#' || c == '?')
+ special++, *np++ = c, c = getC(DOEXCL);
+ *np++ = c;
+ switch (c) {
+
+ case '<':
+ case '$':
+ if (special)
+ seterror(ERR_SPDOLLT);
+ *np = 0;
+ addla(name);
+ return;
+
+ case '\n':
+ ungetD(c);
+ np--;
+ seterror(ERR_NEWLINE);
+ *np = 0;
+ addla(name);
+ return;
+
+ case '*':
+ if (special)
+ seterror(ERR_SPSTAR);
+ *np = 0;
+ addla(name);
+ return;
+
+ default:
+ toolong = 0;
+ if (Isdigit(c)) {
+#ifdef notdef
+ /* let $?0 pass for now */
+ if (special) {
+ seterror(ERR_DIGIT);
+ *np = 0;
+ addla(name);
+ return;
+ }
+#endif
+ /* we know that np < &name[4] */
+ ep = &np[MAXVARLEN];
+ while (c = getC(DOEXCL)) {
+ if (!Isdigit(c))
+ break;
+ if (np < ep)
+ *np++ = c;
+ else
+ toolong = 1;
+ }
+ }
+ else if (letter(c)) {
+ /* we know that np < &name[4] */
+ ep = &np[MAXVARLEN];
+ toolong = 0;
+ while (c = getC(DOEXCL)) {
+ /* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */
+ if (!letter(c) && !Isdigit(c))
+ break;
+ if (np < ep)
+ *np++ = c;
+ else
+ toolong = 1;
+ }
+ }
+ else {
+ *np = 0;
+ seterror(ERR_VARILL);
+ addla(name);
+ return;
+ }
+ if (toolong) {
+ seterror(ERR_VARTOOLONG);
+ *np = 0;
+ addla(name);
+ return;
+ }
+ break;
+ }
+ if (c == '[') {
+ *np++ = c;
+ /*
+ * Name up to here is a max of MAXVARLEN + 8.
+ */
+ ep = &np[2 * MAXVARLEN + 8];
+ do {
+ /*
+ * Michael Greim: Allow $ expansion to take place in selector
+ * expressions. (limits the number of characters returned)
+ */
+ c = getC(DOEXCL | DODOL);
+ if (c == '\n') {
+ ungetD(c);
+ np--;
+ seterror(ERR_NLINDEX);
+ *np = 0;
+ addla(name);
+ return;
+ }
+ if (np < ep)
+ *np++ = c;
+ } while (c != ']');
+ *np = '\0';
+ if (np >= ep) {
+ seterror(ERR_SELOVFL);
+ addla(name);
+ return;
+ }
+ c = getC(DOEXCL);
+ }
+ /*
+ * Name up to here is a max of 2 * MAXVARLEN + 8.
+ */
+ if (c == ':') {
+ /*
+ * if the :g modifier is followed by a newline, then error right away!
+ * -strike
+ */
+
+ int gmodflag = 0;
+
+ *np++ = c, c = getC(DOEXCL);
+ if (c == 'g')
+ gmodflag++, *np++ = c, c = getC(DOEXCL);
+ *np++ = c;
+ if (!any("htrqxe", c)) {
+ if (gmodflag && c == '\n')
+ stderror(ERR_VARSYN); /* strike */
+ seterror(ERR_VARMOD, c);
+ *np = 0;
+ addla(name);
+ return;
+ }
+ }
+ else
+ ungetD(c);
+ if (sc == '{') {
+ c = getC(DOEXCL);
+ if (c != '}') {
+ ungetD(c);
+ seterror(ERR_MISSING, '}');
+ *np = 0;
+ addla(name);
+ return;
+ }
+ *np++ = c;
+ }
+ *np = 0;
+ addla(name);
+ return;
+}
+
+void
+addla(cp)
+ Char *cp;
+{
+ Char buf[BUFSIZ];
+
+ if (Strlen(cp) + (lap ? Strlen(lap) : 0) >=
+ (sizeof(labuf) - 4) / sizeof(Char)) {
+ seterror(ERR_EXPOVFL);
+ return;
+ }
+ if (lap)
+ (void) Strcpy(buf, lap);
+ (void) Strcpy(labuf, cp);
+ if (lap)
+ (void) Strcat(labuf, buf);
+ lap = labuf;
+}
+
+static Char lhsb[32];
+static Char slhs[32];
+static Char rhsb[64];
+static int quesarg;
+
+static void
+getexcl(sc)
+ int sc;
+{
+ register struct wordent *hp, *ip;
+ int left, right, dol;
+ register int c;
+
+ if (sc == 0) {
+ sc = getC(0);
+ if (sc != '{') {
+ ungetC(sc);
+ sc = 0;
+ }
+ }
+ quesarg = -1;
+ lastev = eventno;
+ hp = gethent(sc);
+ if (hp == 0)
+ return;
+ hadhist = 1;
+ dol = 0;
+ if (hp == alhistp)
+ for (ip = hp->next->next; ip != alhistt; ip = ip->next)
+ dol++;
+ else
+ for (ip = hp->next->next; ip != hp->prev; ip = ip->next)
+ dol++;
+ left = 0, right = dol;
+ if (sc == HISTSUB) {
+ ungetC('s'), unreadc(HISTSUB), c = ':';
+ goto subst;
+ }
+ c = getC(0);
+ if (!any(":^$*-%", c))
+ goto subst;
+ left = right = -1;
+ if (c == ':') {
+ c = getC(0);
+ unreadc(c);
+ if (letter(c) || c == '&') {
+ c = ':';
+ left = 0, right = dol;
+ goto subst;
+ }
+ }
+ else
+ ungetC(c);
+ if (!getsel(&left, &right, dol))
+ return;
+ c = getC(0);
+ if (c == '*')
+ ungetC(c), c = '-';
+ if (c == '-') {
+ if (!getsel(&left, &right, dol))
+ return;
+ c = getC(0);
+ }
+subst:
+ exclc = right - left + 1;
+ while (--left >= 0)
+ hp = hp->next;
+ if (sc == HISTSUB || c == ':') {
+ do {
+ hp = getsub(hp);
+ c = getC(0);
+ } while (c == ':');
+ }
+ unreadc(c);
+ if (sc == '{') {
+ c = getC(0);
+ if (c != '}')
+ seterror(ERR_BADBANG);
+ }
+ exclnxt = hp;
+}
+
+static struct wordent *
+getsub(en)
+ struct wordent *en;
+{
+ register Char *cp;
+ int delim;
+ register int c;
+ int sc;
+ bool global = 0;
+ Char orhsb[sizeof(rhsb) / sizeof(Char)];
+
+ exclnxt = 0;
+ sc = c = getC(0);
+ if (c == 'g')
+ global ++, sc = c = getC(0);
+
+ switch (c) {
+ case 'p':
+ justpr++;
+ return (en);
+
+ case 'x':
+ case 'q':
+ global ++;
+
+ /* fall into ... */
+
+ case 'h':
+ case 'r':
+ case 't':
+ case 'e':
+ break;
+
+ case '&':
+ if (slhs[0] == 0) {
+ seterror(ERR_NOSUBST);
+ return (en);
+ }
+ (void) Strcpy(lhsb, slhs);
+ break;
+
+#ifdef notdef
+ case '~':
+ if (lhsb[0] == 0)
+ goto badlhs;
+ break;
+#endif
+
+ case 's':
+ delim = getC(0);
+ if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) {
+ unreadc(delim);
+ lhsb[0] = 0;
+ seterror(ERR_BADSUBST);
+ return (en);
+ }
+ cp = lhsb;
+ for (;;) {
+ c = getC(0);
+ if (c == '\n') {
+ unreadc(c);
+ break;
+ }
+ if (c == delim)
+ break;
+ if (cp > &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) {
+ lhsb[0] = 0;
+ seterror(ERR_BADSUBST);
+ return (en);
+ }
+ if (c == '\\') {
+ c = getC(0);
+ if (c != delim && c != '\\')
+ *cp++ = '\\';
+ }
+ *cp++ = c;
+ }
+ if (cp != lhsb)
+ *cp++ = 0;
+ else if (lhsb[0] == 0) {
+ seterror(ERR_LHS);
+ return (en);
+ }
+ cp = rhsb;
+ (void) Strcpy(orhsb, cp);
+ for (;;) {
+ c = getC(0);
+ if (c == '\n') {
+ unreadc(c);
+ break;
+ }
+ if (c == delim)
+ break;
+#ifdef notdef
+ if (c == '~') {
+ if (&cp[Strlen(orhsb)] > &rhsb[sizeof(rhsb) / sizeof(Char) - 2])
+ goto toorhs;
+ (void) Strcpy(cp, orhsb);
+ cp = Strend(cp);
+ continue;
+ }
+#endif
+ if (cp > &rhsb[sizeof(rhsb) / sizeof(Char) - 2]) {
+ seterror(ERR_RHSLONG);
+ return (en);
+ }
+ if (c == '\\') {
+ c = getC(0);
+ if (c != delim /* && c != '~' */ )
+ *cp++ = '\\';
+ }
+ *cp++ = c;
+ }
+ *cp++ = 0;
+ break;
+
+ default:
+ if (c == '\n')
+ unreadc(c);
+ seterror(ERR_BADBANGMOD, c);
+ return (en);
+ }
+ (void) Strcpy(slhs, lhsb);
+ if (exclc)
+ en = dosub(sc, en, global);
+ return (en);
+}
+
+static struct wordent *
+dosub(sc, en, global)
+ int sc;
+ struct wordent *en;
+ bool global;
+{
+ struct wordent lexi;
+ bool didsub = 0;
+ struct wordent *hp = &lexi;
+ register struct wordent *wdp;
+ register int i = exclc;
+
+ wdp = hp;
+ while (--i >= 0) {
+ register struct wordent *new;
+
+ new = (struct wordent *) xcalloc(1, sizeof *wdp);
+ new->word = 0;
+ new->prev = wdp;
+ new->next = hp;
+ wdp->next = new;
+ wdp = new;
+ en = en->next;
+ wdp->word = (en->word && (global ||didsub == 0)) ?
+ subword(en->word, sc, &didsub) : Strsave(en->word);
+ }
+ if (didsub == 0)
+ seterror(ERR_MODFAIL);
+ hp->prev = wdp;
+ return (&enthist(-1000, &lexi, 0)->Hlex);
+}
+
+static Char *
+subword(cp, type, adid)
+ Char *cp;
+ int type;
+ bool *adid;
+{
+ Char wbuf[BUFSIZ];
+ register Char *wp, *mp, *np;
+ register int i;
+
+ switch (type) {
+
+ case 'r':
+ case 'e':
+ case 'h':
+ case 't':
+ case 'q':
+ case 'x':
+ wp = domod(cp, type);
+ if (wp == 0)
+ return (Strsave(cp));
+ *adid = 1;
+ return (wp);
+
+ default:
+ wp = wbuf;
+ i = BUFSIZ - 4;
+ for (mp = cp; *mp; mp++)
+ if (matchs(mp, lhsb)) {
+ for (np = cp; np < mp;)
+ *wp++ = *np++, --i;
+ for (np = rhsb; *np; np++)
+ switch (*np) {
+
+ case '\\':
+ if (np[1] == '&')
+ np++;
+ /* fall into ... */
+
+ default:
+ if (--i < 0) {
+ seterror(ERR_SUBOVFL);
+ return (STRNULL);
+ }
+ *wp++ = *np;
+ continue;
+
+ case '&':
+ i -= Strlen(lhsb);
+ if (i < 0) {
+ seterror(ERR_SUBOVFL);
+ return (STRNULL);
+ }
+ *wp = 0;
+ (void) Strcat(wp, lhsb);
+ wp = Strend(wp);
+ continue;
+ }
+ mp += Strlen(lhsb);
+ i -= Strlen(mp);
+ if (i < 0) {
+ seterror(ERR_SUBOVFL);
+ return (STRNULL);
+ }
+ *wp = 0;
+ (void) Strcat(wp, mp);
+ *adid = 1;
+ return (Strsave(wbuf));
+ }
+ return (Strsave(cp));
+ }
+}
+
+Char *
+domod(cp, type)
+ Char *cp;
+ int type;
+{
+ register Char *wp, *xp;
+ register int c;
+
+ switch (type) {
+
+ case 'x':
+ case 'q':
+ wp = Strsave(cp);
+ for (xp = wp; c = *xp; xp++)
+ if ((c != ' ' && c != '\t') || type == 'q')
+ *xp |= QUOTE;
+ return (wp);
+
+ case 'h':
+ case 't':
+ if (!any(short2str(cp), '/'))
+ return (type == 't' ? Strsave(cp) : 0);
+ wp = Strend(cp);
+ while (*--wp != '/')
+ continue;
+ if (type == 'h')
+ xp = Strsave(cp), xp[wp - cp] = 0;
+ else
+ xp = Strsave(wp + 1);
+ return (xp);
+
+ case 'e':
+ case 'r':
+ wp = Strend(cp);
+ for (wp--; wp >= cp && *wp != '/'; wp--)
+ if (*wp == '.') {
+ if (type == 'e')
+ xp = Strsave(wp + 1);
+ else
+ xp = Strsave(cp), xp[wp - cp] = 0;
+ return (xp);
+ }
+ return (Strsave(type == 'e' ? STRNULL : cp));
+ }
+ return (0);
+}
+
+static int
+matchs(str, pat)
+ register Char *str, *pat;
+{
+ while (*str && *pat && *str == *pat)
+ str++, pat++;
+ return (*pat == 0);
+}
+
+static int
+getsel(al, ar, dol)
+ register int *al, *ar;
+ int dol;
+{
+ register int c = getC(0);
+ register int i;
+ bool first = *al < 0;
+
+ switch (c) {
+
+ case '%':
+ if (quesarg == -1) {
+ seterror(ERR_BADBANGARG);
+ return (0);
+ }
+ if (*al < 0)
+ *al = quesarg;
+ *ar = quesarg;
+ break;
+
+ case '-':
+ if (*al < 0) {
+ *al = 0;
+ *ar = dol - 1;
+ unreadc(c);
+ }
+ return (1);
+
+ case '^':
+ if (*al < 0)
+ *al = 1;
+ *ar = 1;
+ break;
+
+ case '$':
+ if (*al < 0)
+ *al = dol;
+ *ar = dol;
+ break;
+
+ case '*':
+ if (*al < 0)
+ *al = 1;
+ *ar = dol;
+ if (*ar < *al) {
+ *ar = 0;
+ *al = 1;
+ return (1);
+ }
+ break;
+
+ default:
+ if (Isdigit(c)) {
+ i = 0;
+ while (Isdigit(c)) {
+ i = i * 10 + c - '0';
+ c = getC(0);
+ }
+ if (i < 0)
+ i = dol + 1;
+ if (*al < 0)
+ *al = i;
+ *ar = i;
+ }
+ else if (*al < 0)
+ *al = 0, *ar = dol;
+ else
+ *ar = dol - 1;
+ unreadc(c);
+ break;
+ }
+ if (first) {
+ c = getC(0);
+ unreadc(c);
+ if (any("-$*", c))
+ return (1);
+ }
+ if (*al > *ar || *ar > dol) {
+ seterror(ERR_BADBANGARG);
+ return (0);
+ }
+ return (1);
+
+}
+
+static struct wordent *
+gethent(sc)
+ int sc;
+{
+ register struct Hist *hp;
+ register Char *np;
+ register int c;
+ int event;
+ bool back = 0;
+
+ c = sc == HISTSUB ? HIST : getC(0);
+ if (c == HIST) {
+ if (alhistp)
+ return (alhistp);
+ event = eventno;
+ }
+ else
+ switch (c) {
+
+ case ':':
+ case '^':
+ case '$':
+ case '*':
+ case '%':
+ ungetC(c);
+ if (lastev == eventno && alhistp)
+ return (alhistp);
+ event = lastev;
+ break;
+
+ case '#': /* !# is command being typed in (mrh) */
+ if (--hleft == 0) {
+ seterror(ERR_HISTLOOP);
+ return (0);
+ }
+ else
+ return (&paraml);
+ /* NOTREACHED */
+
+ case '-':
+ back = 1;
+ c = getC(0);
+ /* FALLSTHROUGH */
+
+ default:
+ if (any("(=~", c)) {
+ unreadc(c);
+ ungetC(HIST);
+ return (0);
+ }
+ np = lhsb;
+ event = 0;
+ while (!any(": \t\\\n}", c)) {
+ if (event != -1 && Isdigit(c))
+ event = event * 10 + c - '0';
+ else
+ event = -1;
+ if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
+ *np++ = c;
+ c = getC(0);
+ }
+ unreadc(c);
+ if (np == lhsb) {
+ ungetC(HIST);
+ return (0);
+ }
+ *np++ = 0;
+ if (event != -1) {
+ /*
+ * History had only digits
+ */
+ if (back)
+ event = eventno + (alhistp == 0) - (event ? event : 0);
+ break;
+ }
+ hp = findev(lhsb, 0);
+ if (hp)
+ lastev = hp->Hnum;
+ return (&hp->Hlex);
+
+ case '?':
+ np = lhsb;
+ for (;;) {
+ c = getC(0);
+ if (c == '\n') {
+ unreadc(c);
+ break;
+ }
+ if (c == '?')
+ break;
+ if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
+ *np++ = c;
+ }
+ if (np == lhsb) {
+ if (lhsb[0] == 0) {
+ seterror(ERR_NOSEARCH);
+ return (0);
+ }
+ }
+ else
+ *np++ = 0;
+ hp = findev(lhsb, 1);
+ if (hp)
+ lastev = hp->Hnum;
+ return (&hp->Hlex);
+ }
+
+ for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
+ if (hp->Hnum == event) {
+ hp->Href = eventno;
+ lastev = hp->Hnum;
+ return (&hp->Hlex);
+ }
+ np = putn(event);
+ seterror(ERR_NOEVENT, short2str(np));
+ return (0);
+}
+
+static struct Hist *
+findev(cp, anyarg)
+ Char *cp;
+ bool anyarg;
+{
+ register struct Hist *hp;
+
+ for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
+ Char *dp;
+ register Char *p, *q;
+ register struct wordent *lp = hp->Hlex.next;
+ int argno = 0;
+
+ /*
+ * The entries added by alias substitution don't have a newline but do
+ * have a negative event number. Savehist() trims off these entries,
+ * but it happens before alias expansion, too early to delete those
+ * from the previous command.
+ */
+ if (hp->Hnum < 0)
+ continue;
+ if (lp->word[0] == '\n')
+ continue;
+ if (!anyarg) {
+ p = cp;
+ q = lp->word;
+ do
+ if (!*p)
+ return (hp);
+ while (*p++ == *q++);
+ continue;
+ }
+ do {
+ for (dp = lp->word; *dp; dp++) {
+ p = cp;
+ q = dp;
+ do
+ if (!*p) {
+ quesarg = argno;
+ return (hp);
+ }
+ while (*p++ == *q++);
+ }
+ lp = lp->next;
+ argno++;
+ } while (lp->word[0] != '\n');
+ }
+ seterror(ERR_NOEVENT, short2str(cp));
+ return (0);
+}
+
+
+static void
+setexclp(cp)
+ register Char *cp;
+{
+ if (cp && cp[0] == '\n')
+ return;
+ exclp = cp;
+}
+
+void
+unreadc(c)
+ int c;
+{
+ peekread = c;
+}
+
+int
+readc(wanteof)
+ bool wanteof;
+{
+ register int c;
+ static sincereal;
+
+ if (c = peekread) {
+ peekread = 0;
+ return (c);
+ }
+top:
+ if (alvecp) {
+ if (c = *alvecp++)
+ return (c);
+ if (*alvec) {
+ alvecp = *alvec++;
+ return (' ');
+ }
+ }
+ if (alvec) {
+ if (alvecp = *alvec) {
+ alvec++;
+ goto top;
+ }
+ /* Infinite source! */
+ return ('\n');
+ }
+ if (evalp) {
+ if (c = *evalp++)
+ return (c);
+ if (*evalvec) {
+ evalp = *evalvec++;
+ return (' ');
+ }
+ evalp = 0;
+ }
+ if (evalvec) {
+ if (evalvec == (Char **) 1) {
+ doneinp = 1;
+ reset();
+ }
+ if (evalp = *evalvec) {
+ evalvec++;
+ goto top;
+ }
+ evalvec = (Char **) 1;
+ return ('\n');
+ }
+ do {
+ if (arginp == (Char *) 1 || onelflg == 1) {
+ if (wanteof)
+ return (-1);
+ exitstat();
+ }
+ if (arginp) {
+ if ((c = *arginp++) == 0) {
+ arginp = (Char *) 1;
+ return ('\n');
+ }
+ return (c);
+ }
+reread:
+ c = bgetc();
+ if (c < 0) {
+ struct termios tty;
+ if (wanteof)
+ return (-1);
+ /* was isatty but raw with ignoreeof yields problems */
+ if (tcgetattr(SHIN, &tty) == 0 && (tty.c_lflag & ICANON))
+ {
+ /* was 'short' for FILEC */
+ int ctpgrp;
+
+ if (++sincereal > 25)
+ goto oops;
+ if (tpgrp != -1 &&
+ (ctpgrp = tcgetpgrp(FSHTTY)) != -1 &&
+ tpgrp != ctpgrp) {
+ (void) tcsetpgrp(FSHTTY, tpgrp);
+ (void) killpg((pid_t) ctpgrp, SIGHUP);
+ xprintf("Reset tty pgrp from %d to %d\n", ctpgrp, tpgrp);
+ goto reread;
+ }
+ if (adrof(STRignoreeof)) {
+ if (loginsh)
+ xprintf("\nUse \"logout\" to logout.\n");
+ else
+ xprintf("\nUse \"exit\" to leave csh.\n");
+ reset();
+ }
+ if (chkstop == 0)
+ panystop(1);
+ }
+ oops:
+ doneinp = 1;
+ reset();
+ }
+ sincereal = 0;
+ if (c == '\n' && onelflg)
+ onelflg--;
+ } while (c == 0);
+ return (c);
+}
+
+static int
+bgetc()
+{
+ register int buf, off, c;
+
+#ifdef FILEC
+ register int numleft = 0, roomleft;
+ Char ttyline[BUFSIZ];
+#endif
+ char tbuf[BUFSIZ + 1];
+
+ if (cantell) {
+ if (fseekp < fbobp || fseekp > feobp) {
+ fbobp = feobp = fseekp;
+ (void) lseek(SHIN, fseekp, L_SET);
+ }
+ if (fseekp == feobp) {
+ int i;
+
+ fbobp = feobp;
+ do
+ c = read(SHIN, tbuf, BUFSIZ);
+ while (c < 0 && errno == EINTR);
+ if (c <= 0)
+ return (-1);
+ for (i = 0; i < c; i++)
+ fbuf[0][i] = (unsigned char) tbuf[i];
+ feobp += c;
+ }
+ c = fbuf[0][fseekp - fbobp];
+ fseekp++;
+ return (c);
+ }
+
+again:
+ buf = (int) fseekp / BUFSIZ;
+ if (buf >= fblocks) {
+ register Char **nfbuf =
+ (Char **) xcalloc((size_t) (fblocks + 2),
+ sizeof(Char **));
+
+ if (fbuf) {
+ (void) blkcpy(nfbuf, fbuf);
+ xfree((ptr_t) fbuf);
+ }
+ fbuf = nfbuf;
+ fbuf[fblocks] = (Char *) xcalloc(BUFSIZ, sizeof(Char));
+ fblocks++;
+ if (!intty)
+ goto again;
+ }
+ if (fseekp >= feobp) {
+ buf = (int) feobp / BUFSIZ;
+ off = (int) feobp % BUFSIZ;
+ roomleft = BUFSIZ - off;
+
+#ifdef FILEC
+ roomleft = BUFSIZ - off;
+ for (;;) {
+ if (filec && intty) {
+ c = numleft ? numleft : tenex(ttyline, BUFSIZ);
+ if (c > roomleft) {
+ /* start with fresh buffer */
+ feobp = fseekp = fblocks * BUFSIZ;
+ numleft = c;
+ goto again;
+ }
+ if (c > 0)
+ bcopy(ttyline, fbuf[buf] + off, c * sizeof(Char));
+ numleft = 0;
+ }
+ else {
+#endif
+ c = read(SHIN, tbuf, roomleft);
+ if (c > 0) {
+ int i;
+ Char *ptr = fbuf[buf] + off;
+
+ for (i = 0; i < c; i++)
+ ptr[i] = (unsigned char) tbuf[i];
+ }
+#ifdef FILEC
+ }
+#endif
+ if (c >= 0)
+ break;
+ if (errno == EWOULDBLOCK) {
+ int off = 0;
+
+ (void) ioctl(SHIN, FIONBIO, (ioctl_t) & off);
+ }
+ else if (errno != EINTR)
+ break;
+ }
+ if (c <= 0)
+ return (-1);
+ feobp += c;
+#ifndef FILEC
+ goto again;
+#else
+ if (filec && !intty)
+ goto again;
+#endif
+ }
+ c = fbuf[buf][(int) fseekp % BUFSIZ];
+ fseekp++;
+ return (c);
+}
+
+static void
+bfree()
+{
+ register int sb, i;
+
+ if (cantell)
+ return;
+ if (whyles)
+ return;
+ sb = (int) (fseekp - 1) / BUFSIZ;
+ if (sb > 0) {
+ for (i = 0; i < sb; i++)
+ xfree((ptr_t) fbuf[i]);
+ (void) blkcpy(fbuf, &fbuf[sb]);
+ fseekp -= BUFSIZ * sb;
+ feobp -= BUFSIZ * sb;
+ fblocks -= sb;
+ }
+}
+
+void
+bseek(l)
+ off_t l;
+
+{
+
+ fseekp = l;
+ if (!cantell) {
+#ifdef notdef
+ register struct whyle *wp;
+#endif
+
+ if (!whyles)
+ return;
+#ifdef notdef
+ /*
+ * Christos: I don't understand this? both wp and l are local. What is
+ * this used for? I suspect the author meant fseek = wp->w_start
+ * This seek/tell stuff needs to be re-written...
+ */
+ for (wp = whyles; wp->w_next; wp = wp->w_next)
+ continue;
+ if (wp->w_start > l)
+ l = wp->w_start;
+#endif
+ }
+}
+
+void
+btoeof()
+{
+ (void) lseek(SHIN, (off_t) 0, L_XTND);
+ fseekp = feobp;
+ wfree();
+ bfree();
+}
+
+void
+settell()
+{
+ cantell = 0;
+ if (arginp || onelflg || intty)
+ return;
+ if (lseek(SHIN, (off_t) 0, L_INCR) < 0 || errno == ESPIPE)
+ return;
+ fbuf = (Char **) xcalloc(2, sizeof(Char **));
+ fblocks = 1;
+ fbuf[0] = (Char *) xcalloc(BUFSIZ, sizeof(Char));
+ fseekp = fbobp = feobp = lseek(SHIN, (off_t) 0, L_INCR);
+ cantell = 1;
+}
diff --git a/bin/csh/misc.c b/bin/csh/misc.c
new file mode 100644
index 000000000000..387ec0b268d8
--- /dev/null
+++ b/bin/csh/misc.c
@@ -0,0 +1,413 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 5.13 (Berkeley) 6/27/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+static int renum __P((int, int));
+
+int
+any(s, c)
+ register char *s;
+ register int c;
+{
+ if (!s)
+ return (0); /* Check for nil pointer */
+ while (*s)
+ if (*s++ == c)
+ return (1);
+ return (0);
+}
+
+void
+setzero(cp, i)
+ char *cp;
+ int i;
+{
+ if (i != 0)
+ do
+ *cp++ = 0;
+ while (--i);
+}
+
+char *
+strsave(s)
+ register char *s;
+{
+ char *n;
+ register char *p;
+
+ if (s == NULL)
+ s = "";
+ for (p = s; *p++;);
+ n = p = (char *) xmalloc((size_t) ((p - s) * sizeof(char)));
+ while (*p++ = *s++);
+ return (n);
+}
+
+Char **
+blkend(up)
+ register Char **up;
+{
+
+ while (*up)
+ up++;
+ return (up);
+}
+
+
+void
+blkpr(av)
+ register Char **av;
+{
+
+ for (; *av; av++) {
+ xprintf("%s", short2str(*av));
+ if (av[1])
+ xprintf(" ");
+ }
+}
+
+int
+blklen(av)
+ register Char **av;
+{
+ register int i = 0;
+
+ while (*av++)
+ i++;
+ return (i);
+}
+
+Char **
+blkcpy(oav, bv)
+ Char **oav;
+ register Char **bv;
+{
+ register Char **av = oav;
+
+ while (*av++ = *bv++)
+ continue;
+ return (oav);
+}
+
+Char **
+blkcat(up, vp)
+ Char **up, **vp;
+{
+
+ (void) blkcpy(blkend(up), vp);
+ return (up);
+}
+
+void
+blkfree(av0)
+ Char **av0;
+{
+ register Char **av = av0;
+
+ if (!av0)
+ return;
+ for (; *av; av++)
+ xfree((ptr_t) * av);
+ xfree((ptr_t) av0);
+}
+
+Char **
+saveblk(v)
+ register Char **v;
+{
+ register Char **newv =
+ (Char **) xcalloc((size_t) (blklen(v) + 1), sizeof(Char **));
+ Char **onewv = newv;
+
+ while (*v)
+ *newv++ = Strsave(*v++);
+ return (onewv);
+}
+
+#ifdef NOTUSED
+char *
+strstr(s, t)
+ register char *s, *t;
+{
+ do {
+ register char *ss = s;
+ register char *tt = t;
+
+ do
+ if (*tt == '\0')
+ return (s);
+ while (*ss++ == *tt++);
+ } while (*s++ != '\0');
+ return (NULL);
+}
+
+#endif /* NOTUSED */
+
+#ifndef SHORT_STRINGS
+char *
+strspl(cp, dp)
+ char *cp, *dp;
+{
+ char *ep;
+ register char *p, *q;
+
+ if (!cp)
+ cp = "";
+ if (!dp)
+ dp = "";
+ for (p = cp; *p++;);
+ for (q = dp; *q++;);
+ ep = (char *) xmalloc((size_t) (((p - cp) + (q - dp) - 1) * sizeof(char)));
+ for (p = ep, q = cp; *p++ = *q++;);
+ for (p--, q = dp; *p++ = *q++;);
+ return (ep);
+}
+
+#endif
+
+Char **
+blkspl(up, vp)
+ register Char **up, **vp;
+{
+ register Char **wp =
+ (Char **) xcalloc((size_t) (blklen(up) + blklen(vp) + 1),
+ sizeof(Char **));
+
+ (void) blkcpy(wp, up);
+ return (blkcat(wp, vp));
+}
+
+Char
+lastchr(cp)
+ register Char *cp;
+{
+
+ if (!cp)
+ return (0);
+ if (!*cp)
+ return (0);
+ while (cp[1])
+ cp++;
+ return (*cp);
+}
+
+/*
+ * This routine is called after an error to close up
+ * any units which may have been left open accidentally.
+ */
+void
+closem()
+{
+ register int f;
+
+ for (f = 0; f < NOFILE; f++)
+ if (f != SHIN && f != SHOUT && f != SHDIAG && f != OLDSTD &&
+ f != FSHTTY)
+ (void) close(f);
+}
+
+void
+donefds()
+{
+
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ didfds = 0;
+}
+
+/*
+ * Move descriptor i to j.
+ * If j is -1 then we just want to get i to a safe place,
+ * i.e. to a unit > 2. This also happens in dcopy.
+ */
+int
+dmove(i, j)
+ register int i, j;
+{
+
+ if (i == j || i < 0)
+ return (i);
+ if (j >= 0) {
+ (void) dup2(i, j);
+ if (j != i)
+ (void) close(i);
+ return (j);
+ }
+ j = dcopy(i, j);
+ if (j != i)
+ (void) close(i);
+ return (j);
+}
+
+int
+dcopy(i, j)
+ register int i, j;
+{
+
+ if (i == j || i < 0 || j < 0 && i > 2)
+ return (i);
+ if (j >= 0) {
+ (void) dup2(i, j);
+ return (j);
+ }
+ (void) close(j);
+ return (renum(i, j));
+}
+
+static int
+renum(i, j)
+ register int i, j;
+{
+ register int k = dup(i);
+
+ if (k < 0)
+ return (-1);
+ if (j == -1 && k > 2)
+ return (k);
+ if (k != j) {
+ j = renum(k, j);
+ (void) close(k);
+ return (j);
+ }
+ return (k);
+}
+
+/*
+ * Left shift a command argument list, discarding
+ * the first c arguments. Used in "shift" commands
+ * as well as by commands like "repeat".
+ */
+void
+lshift(v, c)
+ register Char **v;
+ register int c;
+{
+ register Char **u = v;
+
+ while (*u && --c >= 0)
+ xfree((ptr_t) * u++);
+ (void) blkcpy(v, u);
+}
+
+int
+number(cp)
+ Char *cp;
+{
+ if (!cp)
+ return(0);
+ if (*cp == '-') {
+ cp++;
+ if (!Isdigit(*cp))
+ return (0);
+ cp++;
+ }
+ while (*cp && Isdigit(*cp))
+ cp++;
+ return (*cp == 0);
+}
+
+Char **
+copyblk(v)
+ register Char **v;
+{
+ Char **nv = (Char **) xcalloc((size_t) (blklen(v) + 1), sizeof(Char **));
+
+ return (blkcpy(nv, v));
+}
+
+#ifndef SHORT_STRINGS
+char *
+strend(cp)
+ register char *cp;
+{
+ if (!cp)
+ return (cp);
+ while (*cp)
+ cp++;
+ return (cp);
+}
+
+#endif /* SHORT_STRINGS */
+
+Char *
+strip(cp)
+ Char *cp;
+{
+ register Char *dp = cp;
+
+ if (!cp)
+ return (cp);
+ while (*dp++ &= TRIM)
+ continue;
+ return (cp);
+}
+
+void
+udvar(name)
+ Char *name;
+{
+
+ setname(short2str(name));
+ stderror(ERR_NAME | ERR_UNDVAR);
+}
+
+int
+prefix(sub, str)
+ register Char *sub, *str;
+{
+
+ for (;;) {
+ if (*sub == 0)
+ return (1);
+ if (*str == 0)
+ return (0);
+ if (*sub++ != *str++)
+ return (0);
+ }
+}
diff --git a/bin/csh/parse.c b/bin/csh/parse.c
new file mode 100644
index 000000000000..429030108d25
--- /dev/null
+++ b/bin/csh/parse.c
@@ -0,0 +1,698 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parse.c 5.11 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+static void asyntax __P((struct wordent *, struct wordent *));
+static void asyn0 __P((struct wordent *, struct wordent *));
+static void asyn3 __P((struct wordent *, struct wordent *));
+static struct wordent
+ *freenod __P((struct wordent *, struct wordent *));
+static struct command
+ *syn0 __P((struct wordent *, struct wordent *, int));
+static struct command
+ *syn1 __P((struct wordent *, struct wordent *, int));
+static struct command
+ *syn1a __P((struct wordent *, struct wordent *, int));
+static struct command
+ *syn1b __P((struct wordent *, struct wordent *, int));
+static struct command
+ *syn2 __P((struct wordent *, struct wordent *, int));
+static struct command
+ *syn3 __P((struct wordent *, struct wordent *, int));
+
+#define ALEFT 21 /* max of 20 alias expansions */
+#define HLEFT 11 /* max of 10 history expansions */
+/*
+ * Perform aliasing on the word list lex
+ * Do a (very rudimentary) parse to separate into commands.
+ * If word 0 of a command has an alias, do it.
+ * Repeat a maximum of 20 times.
+ */
+static int aleft;
+extern int hleft;
+void
+alias(lex)
+ register struct wordent *lex;
+{
+ jmp_buf osetexit;
+
+ aleft = ALEFT;
+ hleft = HLEFT;
+ getexit(osetexit);
+ (void) setexit();
+ if (haderr) {
+ resexit(osetexit);
+ reset();
+ }
+ if (--aleft == 0)
+ stderror(ERR_ALIASLOOP);
+ asyntax(lex->next, lex);
+ resexit(osetexit);
+}
+
+static void
+asyntax(p1, p2)
+ register struct wordent *p1, *p2;
+{
+ while (p1 != p2)
+ if (any(";&\n", p1->word[0]))
+ p1 = p1->next;
+ else {
+ asyn0(p1, p2);
+ return;
+ }
+}
+
+static void
+asyn0(p1, p2)
+ struct wordent *p1;
+ register struct wordent *p2;
+{
+ register struct wordent *p;
+ register int l = 0;
+
+ for (p = p1; p != p2; p = p->next)
+ switch (p->word[0]) {
+
+ case '(':
+ l++;
+ continue;
+
+ case ')':
+ l--;
+ if (l < 0)
+ stderror(ERR_TOOMANYRP);
+ continue;
+
+ case '>':
+ if (p->next != p2 && eq(p->next->word, STRand))
+ p = p->next;
+ continue;
+
+ case '&':
+ case '|':
+ case ';':
+ case '\n':
+ if (l != 0)
+ continue;
+ asyn3(p1, p);
+ asyntax(p->next, p2);
+ return;
+ }
+ if (l == 0)
+ asyn3(p1, p2);
+}
+
+static void
+asyn3(p1, p2)
+ struct wordent *p1;
+ register struct wordent *p2;
+{
+ register struct varent *ap;
+ struct wordent alout;
+ register bool redid;
+
+ if (p1 == p2)
+ return;
+ if (p1->word[0] == '(') {
+ for (p2 = p2->prev; p2->word[0] != ')'; p2 = p2->prev)
+ if (p2 == p1)
+ return;
+ if (p2 == p1->next)
+ return;
+ asyn0(p1->next, p2);
+ return;
+ }
+ ap = adrof1(p1->word, &aliases);
+ if (ap == 0)
+ return;
+ alhistp = p1->prev;
+ alhistt = p2;
+ alvec = ap->vec;
+ redid = lex(&alout);
+ alhistp = alhistt = 0;
+ alvec = 0;
+ if (seterr) {
+ freelex(&alout);
+ stderror(ERR_OLD);
+ }
+ if (p1->word[0] && eq(p1->word, alout.next->word)) {
+ Char *cp = alout.next->word;
+
+ alout.next->word = Strspl(STRQNULL, cp);
+ xfree((ptr_t) cp);
+ }
+ p1 = freenod(p1, redid ? p2 : p1->next);
+ if (alout.next != &alout) {
+ p1->next->prev = alout.prev->prev;
+ alout.prev->prev->next = p1->next;
+ alout.next->prev = p1;
+ p1->next = alout.next;
+ xfree((ptr_t) alout.prev->word);
+ xfree((ptr_t) (alout.prev));
+ }
+ reset(); /* throw! */
+}
+
+static struct wordent *
+freenod(p1, p2)
+ register struct wordent *p1, *p2;
+{
+ register struct wordent *retp = p1->prev;
+
+ while (p1 != p2) {
+ xfree((ptr_t) p1->word);
+ p1 = p1->next;
+ xfree((ptr_t) (p1->prev));
+ }
+ retp->next = p2;
+ p2->prev = retp;
+ return (retp);
+}
+
+#define PHERE 1
+#define PIN 2
+#define POUT 4
+#define PDIAG 8
+
+/*
+ * syntax
+ * empty
+ * syn0
+ */
+struct command *
+syntax(p1, p2, flags)
+ register struct wordent *p1, *p2;
+ int flags;
+{
+
+ while (p1 != p2)
+ if (any(";&\n", p1->word[0]))
+ p1 = p1->next;
+ else
+ return (syn0(p1, p2, flags));
+ return (0);
+}
+
+/*
+ * syn0
+ * syn1
+ * syn1 & syntax
+ */
+static struct command *
+syn0(p1, p2, flags)
+ struct wordent *p1, *p2;
+ int flags;
+{
+ register struct wordent *p;
+ register struct command *t, *t1;
+ int l;
+
+ l = 0;
+ for (p = p1; p != p2; p = p->next)
+ switch (p->word[0]) {
+
+ case '(':
+ l++;
+ continue;
+
+ case ')':
+ l--;
+ if (l < 0)
+ seterror(ERR_TOOMANYRP);
+ continue;
+
+ case '|':
+ if (p->word[1] == '|')
+ continue;
+ /* fall into ... */
+
+ case '>':
+ if (p->next != p2 && eq(p->next->word, STRand))
+ p = p->next;
+ continue;
+
+ case '&':
+ if (l != 0)
+ break;
+ if (p->word[1] == '&')
+ continue;
+ t1 = syn1(p1, p, flags);
+ if (t1->t_dtyp == NODE_LIST ||
+ t1->t_dtyp == NODE_AND ||
+ t1->t_dtyp == NODE_OR) {
+ t = (struct command *) xcalloc(1, sizeof(*t));
+ t->t_dtyp = NODE_PAREN;
+ t->t_dflg = F_AMPERSAND | F_NOINTERRUPT;
+ t->t_dspr = t1;
+ t1 = t;
+ }
+ else
+ t1->t_dflg |= F_AMPERSAND | F_NOINTERRUPT;
+ t = (struct command *) xcalloc(1, sizeof(*t));
+ t->t_dtyp = NODE_LIST;
+ t->t_dflg = 0;
+ t->t_dcar = t1;
+ t->t_dcdr = syntax(p, p2, flags);
+ return (t);
+ }
+ if (l == 0)
+ return (syn1(p1, p2, flags));
+ seterror(ERR_TOOMANYLP);
+ return (0);
+}
+
+/*
+ * syn1
+ * syn1a
+ * syn1a ; syntax
+ */
+static struct command *
+syn1(p1, p2, flags)
+ struct wordent *p1, *p2;
+ int flags;
+{
+ register struct wordent *p;
+ register struct command *t;
+ int l;
+
+ l = 0;
+ for (p = p1; p != p2; p = p->next)
+ switch (p->word[0]) {
+
+ case '(':
+ l++;
+ continue;
+
+ case ')':
+ l--;
+ continue;
+
+ case ';':
+ case '\n':
+ if (l != 0)
+ break;
+ t = (struct command *) xcalloc(1, sizeof(*t));
+ t->t_dtyp = NODE_LIST;
+ t->t_dcar = syn1a(p1, p, flags);
+ t->t_dcdr = syntax(p->next, p2, flags);
+ if (t->t_dcdr == 0)
+ t->t_dcdr = t->t_dcar, t->t_dcar = 0;
+ return (t);
+ }
+ return (syn1a(p1, p2, flags));
+}
+
+/*
+ * syn1a
+ * syn1b
+ * syn1b || syn1a
+ */
+static struct command *
+syn1a(p1, p2, flags)
+ struct wordent *p1, *p2;
+ int flags;
+{
+ register struct wordent *p;
+ register struct command *t;
+ register int l = 0;
+
+ for (p = p1; p != p2; p = p->next)
+ switch (p->word[0]) {
+
+ case '(':
+ l++;
+ continue;
+
+ case ')':
+ l--;
+ continue;
+
+ case '|':
+ if (p->word[1] != '|')
+ continue;
+ if (l == 0) {
+ t = (struct command *) xcalloc(1, sizeof(*t));
+ t->t_dtyp = NODE_OR;
+ t->t_dcar = syn1b(p1, p, flags);
+ t->t_dcdr = syn1a(p->next, p2, flags);
+ t->t_dflg = 0;
+ return (t);
+ }
+ continue;
+ }
+ return (syn1b(p1, p2, flags));
+}
+
+/*
+ * syn1b
+ * syn2
+ * syn2 && syn1b
+ */
+static struct command *
+syn1b(p1, p2, flags)
+ struct wordent *p1, *p2;
+ int flags;
+{
+ register struct wordent *p;
+ register struct command *t;
+ register int l = 0;
+
+ for (p = p1; p != p2; p = p->next)
+ switch (p->word[0]) {
+
+ case '(':
+ l++;
+ continue;
+
+ case ')':
+ l--;
+ continue;
+
+ case '&':
+ if (p->word[1] == '&' && l == 0) {
+ t = (struct command *) xcalloc(1, sizeof(*t));
+ t->t_dtyp = NODE_AND;
+ t->t_dcar = syn2(p1, p, flags);
+ t->t_dcdr = syn1b(p->next, p2, flags);
+ t->t_dflg = 0;
+ return (t);
+ }
+ continue;
+ }
+ return (syn2(p1, p2, flags));
+}
+
+/*
+ * syn2
+ * syn3
+ * syn3 | syn2
+ * syn3 |& syn2
+ */
+static struct command *
+syn2(p1, p2, flags)
+ struct wordent *p1, *p2;
+ int flags;
+{
+ register struct wordent *p, *pn;
+ register struct command *t;
+ register int l = 0;
+ int f;
+
+ for (p = p1; p != p2; p = p->next)
+ switch (p->word[0]) {
+
+ case '(':
+ l++;
+ continue;
+
+ case ')':
+ l--;
+ continue;
+
+ case '|':
+ if (l != 0)
+ continue;
+ t = (struct command *) xcalloc(1, sizeof(*t));
+ f = flags | POUT;
+ pn = p->next;
+ if (pn != p2 && pn->word[0] == '&') {
+ f |= PDIAG;
+ t->t_dflg |= F_STDERR;
+ }
+ t->t_dtyp = NODE_PIPE;
+ t->t_dcar = syn3(p1, p, f);
+ if (pn != p2 && pn->word[0] == '&')
+ p = pn;
+ t->t_dcdr = syn2(p->next, p2, flags | PIN);
+ return (t);
+ }
+ return (syn3(p1, p2, flags));
+}
+
+static char RELPAR[] = {'<', '>', '(', ')', '\0'};
+
+/*
+ * syn3
+ * ( syn0 ) [ < in ] [ > out ]
+ * word word* [ < in ] [ > out ]
+ * KEYWORD ( word* ) word* [ < in ] [ > out ]
+ *
+ * KEYWORD = (@ exit foreach if set switch test while)
+ */
+static struct command *
+syn3(p1, p2, flags)
+ struct wordent *p1, *p2;
+ int flags;
+{
+ register struct wordent *p;
+ struct wordent *lp, *rp;
+ register struct command *t;
+ register int l;
+ Char **av;
+ int n, c;
+ bool specp = 0;
+
+ if (p1 != p2) {
+ p = p1;
+again:
+ switch (srchx(p->word)) {
+
+ case T_ELSE:
+ p = p->next;
+ if (p != p2)
+ goto again;
+ break;
+
+ case T_EXIT:
+ case T_FOREACH:
+ case T_IF:
+ case T_LET:
+ case T_SET:
+ case T_SWITCH:
+ case T_WHILE:
+ specp = 1;
+ break;
+ }
+ }
+ n = 0;
+ l = 0;
+ for (p = p1; p != p2; p = p->next)
+ switch (p->word[0]) {
+
+ case '(':
+ if (specp)
+ n++;
+ l++;
+ continue;
+
+ case ')':
+ if (specp)
+ n++;
+ l--;
+ continue;
+
+ case '>':
+ case '<':
+ if (l != 0) {
+ if (specp)
+ n++;
+ continue;
+ }
+ if (p->next == p2)
+ continue;
+ if (any(RELPAR, p->next->word[0]))
+ continue;
+ n--;
+ continue;
+
+ default:
+ if (!specp && l != 0)
+ continue;
+ n++;
+ continue;
+ }
+ if (n < 0)
+ n = 0;
+ t = (struct command *) xcalloc(1, sizeof(*t));
+ av = (Char **) xcalloc((size_t) (n + 1), sizeof(Char **));
+ t->t_dcom = av;
+ n = 0;
+ if (p2->word[0] == ')')
+ t->t_dflg = F_NOFORK;
+ lp = 0;
+ rp = 0;
+ l = 0;
+ for (p = p1; p != p2; p = p->next) {
+ c = p->word[0];
+ switch (c) {
+
+ case '(':
+ if (l == 0) {
+ if (lp != 0 && !specp)
+ seterror(ERR_BADPLP);
+ lp = p->next;
+ }
+ l++;
+ goto savep;
+
+ case ')':
+ l--;
+ if (l == 0)
+ rp = p;
+ goto savep;
+
+ case '>':
+ if (l != 0)
+ goto savep;
+ if (p->word[1] == '>')
+ t->t_dflg |= F_APPEND;
+ if (p->next != p2 && eq(p->next->word, STRand)) {
+ t->t_dflg |= F_STDERR, p = p->next;
+ if (flags & (POUT | PDIAG)) {
+ seterror(ERR_OUTRED);
+ continue;
+ }
+ }
+ if (p->next != p2 && eq(p->next->word, STRbang))
+ t->t_dflg |= F_OVERWRITE, p = p->next;
+ if (p->next == p2) {
+ seterror(ERR_MISRED);
+ continue;
+ }
+ p = p->next;
+ if (any(RELPAR, p->word[0])) {
+ seterror(ERR_MISRED);
+ continue;
+ }
+ if ((flags & POUT) && (flags & PDIAG) == 0 || t->t_drit)
+ seterror(ERR_OUTRED);
+ else
+ t->t_drit = Strsave(p->word);
+ continue;
+
+ case '<':
+ if (l != 0)
+ goto savep;
+ if (p->word[1] == '<')
+ t->t_dflg |= F_READ;
+ if (p->next == p2) {
+ seterror(ERR_MISRED);
+ continue;
+ }
+ p = p->next;
+ if (any(RELPAR, p->word[0])) {
+ seterror(ERR_MISRED);
+ continue;
+ }
+ if ((flags & PHERE) && (t->t_dflg & F_READ))
+ seterror(ERR_REDPAR);
+ else if ((flags & PIN) || t->t_dlef)
+ seterror(ERR_INRED);
+ else
+ t->t_dlef = Strsave(p->word);
+ continue;
+
+ savep:
+ if (!specp)
+ continue;
+ default:
+ if (l != 0 && !specp)
+ continue;
+ if (seterr == 0)
+ av[n] = Strsave(p->word);
+ n++;
+ continue;
+ }
+ }
+ if (lp != 0 && !specp) {
+ if (n != 0)
+ seterror(ERR_BADPLPS);
+ t->t_dtyp = NODE_PAREN;
+ t->t_dspr = syn0(lp, rp, PHERE);
+ }
+ else {
+ if (n == 0)
+ seterror(ERR_NULLCOM);
+ t->t_dtyp = NODE_COMMAND;
+ }
+ return (t);
+}
+
+void
+freesyn(t)
+ register struct command *t;
+{
+ register Char **v;
+
+ if (t == 0)
+ return;
+ switch (t->t_dtyp) {
+
+ case NODE_COMMAND:
+ for (v = t->t_dcom; *v; v++)
+ xfree((ptr_t) * v);
+ xfree((ptr_t) (t->t_dcom));
+ xfree((ptr_t) t->t_dlef);
+ xfree((ptr_t) t->t_drit);
+ break;
+ case NODE_PAREN:
+ freesyn(t->t_dspr);
+ xfree((ptr_t) t->t_dlef);
+ xfree((ptr_t) t->t_drit);
+ break;
+
+ case NODE_AND:
+ case NODE_OR:
+ case NODE_PIPE:
+ case NODE_LIST:
+ freesyn(t->t_dcar), freesyn(t->t_dcdr);
+ break;
+ }
+ xfree((ptr_t) t);
+}
diff --git a/bin/csh/pathnames.h b/bin/csh/pathnames.h
new file mode 100644
index 000000000000..f309210fc714
--- /dev/null
+++ b/bin/csh/pathnames.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * @(#)pathnames.h 5.6 (Berkeley) 6/8/91
+ */
+
+#define _PATH_BIN "/bin"
+#define _PATH_DOTCSHRC "/etc/csh.cshrc"
+#define _PATH_DOTLOGIN "/etc/csh.login"
+#define _PATH_DOTLOGOUT "/etc/csh.logout"
+#define _PATH_LOGIN "/usr/bin/login"
+#define _PATH_USRBIN "/usr/bin"
diff --git a/bin/csh/print.c b/bin/csh/print.c
new file mode 100644
index 000000000000..f25dcd4635ac
--- /dev/null
+++ b/bin/csh/print.c
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)print.c 5.11 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+extern int Tty_eight_bit;
+extern int Tty_raw_mode;
+extern Char GettingInput;
+
+int lbuffed = 1; /* true if line buffered */
+
+static void p2dig __P((int));
+
+void
+psecs(l)
+ long l;
+{
+ register int i;
+
+ i = l / 3600;
+ if (i) {
+ xprintf("%d:", i);
+ i = l % 3600;
+ p2dig(i / 60);
+ goto minsec;
+ }
+ i = l;
+ xprintf("%d", i / 60);
+minsec:
+ i %= 60;
+ xprintf(":");
+ p2dig(i);
+}
+
+void
+pcsecs(l) /* PWP: print mm:ss.dd, l is in sec*100 */
+ long l;
+{
+ register int i;
+
+ i = l / 360000;
+ if (i) {
+ xprintf("%d:", i);
+ i = (l % 360000) / 100;
+ p2dig(i / 60);
+ goto minsec;
+ }
+ i = l / 100;
+ xprintf("%d", i / 60);
+minsec:
+ i %= 60;
+ xprintf(":");
+ p2dig(i);
+ xprintf(".");
+ p2dig((int) (l % 100));
+}
+
+static void
+p2dig(i)
+ register int i;
+{
+
+ xprintf("%d%d", i / 10, i % 10);
+}
+
+char linbuf[2048];
+char *linp = linbuf;
+bool output_raw = 0; /* PWP */
+
+void
+xputchar(c)
+ register int c;
+{
+ c &= CHAR | QUOTE;
+ if (!output_raw && (c & QUOTE) == 0) {
+ if (Iscntrl(c)) {
+ if (c != '\t' && c != '\n' && c != '\r') {
+ xputchar('^');
+ if (c == ASCII)
+ c = '?';
+ else
+ c |= 0100;
+ }
+ }
+ else if (!Isprint(c)) {
+ xputchar('\\');
+ xputchar((((c >> 6) & 7) + '0'));
+ xputchar((((c >> 3) & 7) + '0'));
+ c = (c & 7) + '0';
+ }
+ (void) putraw(c);
+ }
+ else {
+ c &= TRIM;
+ (void) putpure(c);
+ }
+ if (lbuffed && (c & CHAR) == '\n')
+ flush();
+}
+
+int
+putraw(c)
+ register int c;
+{
+ return putpure(c);
+}
+
+int
+putpure(c)
+ register int c;
+{
+ c &= CHAR;
+
+ *linp++ = c;
+ if (linp >= &linbuf[sizeof linbuf - 10])
+ flush();
+ return (1);
+}
+
+void
+draino()
+{
+ linp = linbuf;
+}
+
+void
+flush()
+{
+ register int unit;
+
+ /* int lmode; */
+
+ if (linp == linbuf)
+ return;
+#ifdef notdef
+ if (linp < &linbuf[sizeof linbuf - 10])
+ return;
+#endif
+ if (haderr)
+ unit = didfds ? 2 : SHDIAG;
+ else
+ unit = didfds ? 1 : SHOUT;
+ (void) write(unit, linbuf, (size_t) (linp - linbuf));
+ linp = linbuf;
+}
diff --git a/bin/csh/printf.c b/bin/csh/printf.c
new file mode 100644
index 000000000000..f8dd5ab79489
--- /dev/null
+++ b/bin/csh/printf.c
@@ -0,0 +1,311 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)printf.c 5.4 (Berkeley) 6/16/91";
+#endif /* not lint */
+
+/*
+ * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints
+ * through the putchar() routine. Feel free to use for
+ * anything... -- 7/17/87 Paul Placeway
+ */
+#include <strings.h>
+#include <stdlib.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+#include "csh.h"
+#include "char.h"
+#include "extern.h"
+
+#ifdef lint
+#undef va_arg
+#define va_arg(a, b) (a ? (b) 0 : (b) 0)
+#endif
+
+#define INF 32766 /* should be bigger than any field to print */
+
+static unsigned char buf[128];
+
+static void
+doprnt(addchar, sfmt, ap)
+ void (*addchar) ();
+ char *sfmt;
+ va_list ap;
+{
+ register unsigned char *f, *bp;
+ register long l;
+ register unsigned long u;
+ register int i;
+ register int fmt;
+ register unsigned char pad = ' ';
+ int flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
+ int sign = 0;
+
+ f = (unsigned char *) sfmt;
+ for (; *f; f++) {
+ if (*f != '%') { /* then just out the char */
+ (*addchar) ((int) (*f));
+ }
+ else {
+ f++; /* skip the % */
+
+ if (*f == '-') { /* minus: flush left */
+ flush_left = 1;
+ f++;
+ }
+
+ if (*f == '0' || *f == '.') {
+ /* padding with 0 rather than blank */
+ pad = '0';
+ f++;
+ }
+ if (*f == '*') { /* field width */
+ f_width = va_arg(ap, int);
+ f++;
+ }
+ else if (Isdigit(*f)) {
+ f_width = atoi((char *) f);
+ while (Isdigit(*f))
+ f++; /* skip the digits */
+ }
+
+ if (*f == '.') { /* precision */
+ f++;
+ if (*f == '*') {
+ prec = va_arg(ap, int);
+ f++;
+ }
+ else if (Isdigit(*f)) {
+ prec = atoi((char *) f);
+ while (Isdigit(*f))
+ f++; /* skip the digits */
+ }
+ }
+
+ if (*f == '#') { /* alternate form */
+ hash = 1;
+ f++;
+ }
+
+ if (*f == 'l') { /* long format */
+ do_long = 1;
+ f++;
+ }
+
+ fmt = *f;
+ if (Isupper(fmt)) {
+ do_long = 1;
+ fmt = Tolower(fmt);
+ }
+ bp = buf;
+ switch (fmt) { /* do the format */
+ case 'd':
+ if (do_long)
+ l = va_arg(ap, long);
+ else
+ l = (long) (va_arg(ap, int));
+ if (l < 0) {
+ sign = 1;
+ l = -l;
+ }
+ do {
+ *bp++ = l % 10 + '0';
+ } while ((l /= 10) > 0);
+ if (sign)
+ *bp++ = '-';
+ f_width = f_width - (bp - buf);
+ if (!flush_left)
+ while (f_width-- > 0)
+ (*addchar) ((int) (pad));
+ for (bp--; bp >= buf; bp--)
+ (*addchar) ((int) (*bp));
+ if (flush_left)
+ while (f_width-- > 0)
+ (*addchar) ((int) (' '));
+ break;
+
+ case 'o':
+ case 'x':
+ case 'u':
+ if (do_long)
+ u = va_arg(ap, unsigned long);
+ else
+ u = (unsigned long) (va_arg(ap, unsigned));
+ if (fmt == 'u') { /* unsigned decimal */
+ do {
+ *bp++ = u % 10 + '0';
+ } while ((u /= 10) > 0);
+ }
+ else if (fmt == 'o') { /* octal */
+ do {
+ *bp++ = u % 8 + '0';
+ } while ((u /= 8) > 0);
+ if (hash)
+ *bp++ = '0';
+ }
+ else if (fmt == 'x') { /* hex */
+ do {
+ i = u % 16;
+ if (i < 10)
+ *bp++ = i + '0';
+ else
+ *bp++ = i - 10 + 'a';
+ } while ((u /= 16) > 0);
+ if (hash) {
+ *bp++ = 'x';
+ *bp++ = '0';
+ }
+ }
+ i = f_width - (bp - buf);
+ if (!flush_left)
+ while (i-- > 0)
+ (*addchar) ((int) (pad));
+ for (bp--; bp >= buf; bp--)
+ (*addchar) ((int) (*bp));
+ if (flush_left)
+ while (i-- > 0)
+ (*addchar) ((int) (' '));
+ break;
+
+
+ case 'c':
+ i = va_arg(ap, int);
+ (*addchar) ((int) (i));
+ break;
+
+ case 's':
+ bp = va_arg(ap, unsigned char *);
+ if (!bp)
+ bp = (unsigned char *) "(nil)";
+ f_width = f_width - strlen((char *) bp);
+ if (!flush_left)
+ while (f_width-- > 0)
+ (*addchar) ((int) (pad));
+ for (i = 0; *bp && i < prec; i++) {
+ (*addchar) ((int) (*bp));
+ bp++;
+ }
+ if (flush_left)
+ while (f_width-- > 0)
+ (*addchar) ((int) (' '));
+
+ break;
+
+ case '%':
+ (*addchar) ((int) ('%'));
+ break;
+ }
+ flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
+ sign = 0;
+ pad = ' ';
+ }
+ }
+}
+
+
+static unsigned char *xstring;
+static void
+xaddchar(c)
+ int c;
+{
+ *xstring++ = c;
+}
+
+
+void
+#if __STDC__
+xsprintf(char *str, char *fmt, ...)
+#else
+xsprintf(str, fmt, va_alist)
+ char *str;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list va;
+
+#if __STDC__
+ va_start(va, fmt);
+#else
+ va_start(va);
+#endif
+ xstring = (unsigned char *) str;
+ doprnt(xaddchar, fmt, va);
+ va_end(va);
+ *xstring++ = '\0';
+}
+
+
+void
+#if __STDC__
+xprintf(char *fmt, ...)
+#else
+xprintf(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list va;
+
+#if __STDC__
+ va_start(va, fmt);
+#else
+ va_start(va);
+#endif
+ doprnt(xputchar, fmt, va);
+ va_end(va);
+}
+
+
+void
+xvprintf(fmt, va)
+ char *fmt;
+ va_list va;
+{
+ doprnt(xputchar, fmt, va);
+}
+
+void
+xvsprintf(str, fmt, va)
+ char *str;
+ char *fmt;
+ va_list va;
+{
+ xstring = (unsigned char *) str;
+ doprnt(xaddchar, fmt, va);
+ *xstring++ = '\0';
+}
diff --git a/bin/csh/proc.c b/bin/csh/proc.c
new file mode 100644
index 000000000000..e8e8c0751f19
--- /dev/null
+++ b/bin/csh/proc.c
@@ -0,0 +1,1295 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)proc.c 5.22 (Berkeley) 6/14/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "dir.h"
+#include "proc.h"
+#include "extern.h"
+
+#define BIGINDEX 9 /* largest desirable job index */
+
+static struct rusage zru;
+
+static void pflushall __P((void));
+static void pflush __P((struct process *));
+static void pclrcurr __P((struct process *));
+static void padd __P((struct command *));
+static int pprint __P((struct process *, int));
+static void ptprint __P((struct process *));
+static void pads __P((Char *));
+static void pkill __P((Char **v, int));
+static struct process
+ *pgetcurr __P((struct process *));
+static void okpcntl __P((void));
+
+/*
+ * pchild - called at interrupt level by the SIGCHLD signal
+ * indicating that at least one child has terminated or stopped
+ * thus at least one wait system call will definitely return a
+ * childs status. Top level routines (like pwait) must be sure
+ * to mask interrupts when playing with the proclist data structures!
+ */
+/* ARGUSED */
+void
+pchild(notused)
+ int notused;
+{
+ register struct process *pp;
+ register struct process *fp;
+ register int pid;
+ extern int insource;
+ union wait w;
+ int jobflags;
+ struct rusage ru;
+
+loop:
+ errno = 0; /* reset, just in case */
+ pid = wait3(&w.w_status,
+ (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), &ru);
+
+ if (pid <= 0) {
+ if (errno == EINTR) {
+ errno = 0;
+ goto loop;
+ }
+ pnoprocesses = pid == -1;
+ return;
+ }
+ for (pp = proclist.p_next; pp != NULL; pp = pp->p_next)
+ if (pid == pp->p_pid)
+ goto found;
+ goto loop;
+found:
+ if (pid == atoi(short2str(value(STRchild))))
+ unsetv(STRchild);
+ pp->p_flags &= ~(PRUNNING | PSTOPPED | PREPORTED);
+ if (WIFSTOPPED(w)) {
+ pp->p_flags |= PSTOPPED;
+ pp->p_reason = w.w_stopsig;
+ }
+ else {
+ if (pp->p_flags & (PTIME | PPTIME) || adrof(STRtime))
+ (void) gettimeofday(&pp->p_etime, NULL);
+
+ pp->p_rusage = ru;
+ if (WIFSIGNALED(w)) {
+ if (w.w_termsig == SIGINT)
+ pp->p_flags |= PINTERRUPTED;
+ else
+ pp->p_flags |= PSIGNALED;
+ if (w.w_coredump)
+ pp->p_flags |= PDUMPED;
+ pp->p_reason = w.w_termsig;
+ }
+ else {
+ pp->p_reason = w.w_retcode;
+ if (pp->p_reason != 0)
+ pp->p_flags |= PAEXITED;
+ else
+ pp->p_flags |= PNEXITED;
+ }
+ }
+ jobflags = 0;
+ fp = pp;
+ do {
+ if ((fp->p_flags & (PPTIME | PRUNNING | PSTOPPED)) == 0 &&
+ !child && adrof(STRtime) &&
+ fp->p_rusage.ru_utime.tv_sec + fp->p_rusage.ru_stime.tv_sec
+ >= atoi(short2str(value(STRtime))))
+ fp->p_flags |= PTIME;
+ jobflags |= fp->p_flags;
+ } while ((fp = fp->p_friends) != pp);
+ pp->p_flags &= ~PFOREGND;
+ if (pp == pp->p_friends && (pp->p_flags & PPTIME)) {
+ pp->p_flags &= ~PPTIME;
+ pp->p_flags |= PTIME;
+ }
+ if ((jobflags & (PRUNNING | PREPORTED)) == 0) {
+ fp = pp;
+ do {
+ if (fp->p_flags & PSTOPPED)
+ fp->p_flags |= PREPORTED;
+ } while ((fp = fp->p_friends) != pp);
+ while (fp->p_pid != fp->p_jobid)
+ fp = fp->p_friends;
+ if (jobflags & PSTOPPED) {
+ if (pcurrent && pcurrent != fp)
+ pprevious = pcurrent;
+ pcurrent = fp;
+ }
+ else
+ pclrcurr(fp);
+ if (jobflags & PFOREGND) {
+ if (jobflags & (PSIGNALED | PSTOPPED | PPTIME) ||
+#ifdef IIASA
+ jobflags & PAEXITED ||
+#endif
+ !eq(dcwd->di_name, fp->p_cwd->di_name)) {
+ ; /* print in pjwait */
+ }
+ /* PWP: print a newline after ^C */
+ else if (jobflags & PINTERRUPTED)
+ xputchar('\r' | QUOTE), xputchar('\n');
+ }
+ else {
+ if (jobflags & PNOTIFY || adrof(STRnotify)) {
+ xputchar('\r' | QUOTE), xputchar('\n');
+ (void) pprint(pp, NUMBER | NAME | REASON);
+ if ((jobflags & PSTOPPED) == 0)
+ pflush(pp);
+ }
+ else {
+ fp->p_flags |= PNEEDNOTE;
+ neednote++;
+ }
+ }
+ }
+ goto loop;
+}
+
+void
+pnote()
+{
+ register struct process *pp;
+ int flags;
+ sigset_t omask;
+
+ neednote = 0;
+ for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) {
+ if (pp->p_flags & PNEEDNOTE) {
+ omask = sigblock(sigmask(SIGCHLD));
+ pp->p_flags &= ~PNEEDNOTE;
+ flags = pprint(pp, NUMBER | NAME | REASON);
+ if ((flags & (PRUNNING | PSTOPPED)) == 0)
+ pflush(pp);
+ (void) sigsetmask(omask);
+ }
+ }
+}
+
+/*
+ * pwait - wait for current job to terminate, maintaining integrity
+ * of current and previous job indicators.
+ */
+void
+pwait()
+{
+ register struct process *fp, *pp;
+ sigset_t omask;
+
+ /*
+ * Here's where dead procs get flushed.
+ */
+ omask = sigblock(sigmask(SIGCHLD));
+ for (pp = (fp = &proclist)->p_next; pp != NULL; pp = (fp = pp)->p_next)
+ if (pp->p_pid == 0) {
+ fp->p_next = pp->p_next;
+ xfree((ptr_t) pp->p_command);
+ if (pp->p_cwd && --pp->p_cwd->di_count == 0)
+ if (pp->p_cwd->di_next == 0)
+ dfree(pp->p_cwd);
+ xfree((ptr_t) pp);
+ pp = fp;
+ }
+ (void) sigsetmask(omask);
+ pjwait(pcurrjob);
+}
+
+
+/*
+ * pjwait - wait for a job to finish or become stopped
+ * It is assumed to be in the foreground state (PFOREGND)
+ */
+void
+pjwait(pp)
+ register struct process *pp;
+{
+ register struct process *fp;
+ int jobflags, reason;
+ sigset_t omask;
+
+ while (pp->p_pid != pp->p_jobid)
+ pp = pp->p_friends;
+ fp = pp;
+
+ do {
+ if ((fp->p_flags & (PFOREGND | PRUNNING)) == PRUNNING)
+ xprintf("BUG: waiting for background job!\n");
+ } while ((fp = fp->p_friends) != pp);
+ /*
+ * Now keep pausing as long as we are not interrupted (SIGINT), and the
+ * target process, or any of its friends, are running
+ */
+ fp = pp;
+ omask = sigblock(sigmask(SIGCHLD));
+ for (;;) {
+ (void) sigblock(sigmask(SIGCHLD));
+ jobflags = 0;
+ do
+ jobflags |= fp->p_flags;
+ while ((fp = (fp->p_friends)) != pp);
+ if ((jobflags & PRUNNING) == 0)
+ break;
+#ifdef JOBDEBUG
+ xprintf("starting to sigpause for SIGCHLD on %d\n", fp->p_pid);
+#endif /* JOBDEBUG */
+ (void) sigpause(omask & ~sigmask(SIGCHLD));
+ }
+ (void) sigsetmask(omask);
+ if (tpgrp > 0) /* get tty back */
+ (void) tcsetpgrp(FSHTTY, tpgrp);
+ if ((jobflags & (PSIGNALED | PSTOPPED | PTIME)) ||
+ !eq(dcwd->di_name, fp->p_cwd->di_name)) {
+ if (jobflags & PSTOPPED) {
+ xprintf("\n");
+ if (adrof(STRlistjobs)) {
+ Char *jobcommand[3];
+
+ jobcommand[0] = STRjobs;
+ if (eq(value(STRlistjobs), STRlong))
+ jobcommand[1] = STRml;
+ else
+ jobcommand[1] = NULL;
+ jobcommand[2] = NULL;
+
+ dojobs(jobcommand);
+ (void) pprint(pp, SHELLDIR);
+ }
+ else
+ (void) pprint(pp, AREASON | SHELLDIR);
+ }
+ else
+ (void) pprint(pp, AREASON | SHELLDIR);
+ }
+ if ((jobflags & (PINTERRUPTED | PSTOPPED)) && setintr &&
+ (!gointr || !eq(gointr, STRminus))) {
+ if ((jobflags & PSTOPPED) == 0)
+ pflush(pp);
+ pintr1(0);
+ /* NOTREACHED */
+ }
+ reason = 0;
+ fp = pp;
+ do {
+ if (fp->p_reason)
+ reason = fp->p_flags & (PSIGNALED | PINTERRUPTED) ?
+ fp->p_reason | META : fp->p_reason;
+ } while ((fp = fp->p_friends) != pp);
+ if ((reason != 0) && (adrof(STRprintexitvalue)))
+ xprintf("Exit %d\n", reason);
+ set(STRstatus, putn(reason));
+ if (reason && exiterr)
+ exitstat();
+ pflush(pp);
+}
+
+/*
+ * dowait - wait for all processes to finish
+ */
+void
+dowait()
+{
+ register struct process *pp;
+ sigset_t omask;
+
+ pjobs++;
+ omask = sigblock(sigmask(SIGCHLD));
+loop:
+ for (pp = proclist.p_next; pp; pp = pp->p_next)
+ if (pp->p_pid && /* pp->p_pid == pp->p_jobid && */
+ pp->p_flags & PRUNNING) {
+ (void) sigpause((sigset_t) 0);
+ goto loop;
+ }
+ (void) sigsetmask(omask);
+ pjobs = 0;
+}
+
+/*
+ * pflushall - flush all jobs from list (e.g. at fork())
+ */
+static void
+pflushall()
+{
+ register struct process *pp;
+
+ for (pp = proclist.p_next; pp != NULL; pp = pp->p_next)
+ if (pp->p_pid)
+ pflush(pp);
+}
+
+/*
+ * pflush - flag all process structures in the same job as the
+ * the argument process for deletion. The actual free of the
+ * space is not done here since pflush is called at interrupt level.
+ */
+static void
+pflush(pp)
+ register struct process *pp;
+{
+ register struct process *np;
+ register int idx;
+
+ if (pp->p_pid == 0) {
+ xprintf("BUG: process flushed twice");
+ return;
+ }
+ while (pp->p_pid != pp->p_jobid)
+ pp = pp->p_friends;
+ pclrcurr(pp);
+ if (pp == pcurrjob)
+ pcurrjob = 0;
+ idx = pp->p_index;
+ np = pp;
+ do {
+ np->p_index = np->p_pid = 0;
+ np->p_flags &= ~PNEEDNOTE;
+ } while ((np = np->p_friends) != pp);
+ if (idx == pmaxindex) {
+ for (np = proclist.p_next, idx = 0; np; np = np->p_next)
+ if (np->p_index > idx)
+ idx = np->p_index;
+ pmaxindex = idx;
+ }
+}
+
+/*
+ * pclrcurr - make sure the given job is not the current or previous job;
+ * pp MUST be the job leader
+ */
+static void
+pclrcurr(pp)
+ register struct process *pp;
+{
+
+ if (pp == pcurrent)
+ if (pprevious != NULL) {
+ pcurrent = pprevious;
+ pprevious = pgetcurr(pp);
+ }
+ else {
+ pcurrent = pgetcurr(pp);
+ pprevious = pgetcurr(pp);
+ }
+ else if (pp == pprevious)
+ pprevious = pgetcurr(pp);
+}
+
+/* +4 here is 1 for '\0', 1 ea for << >& >> */
+static Char command[PMAXLEN + 4];
+static int cmdlen;
+static Char *cmdp;
+
+/*
+ * palloc - allocate a process structure and fill it up.
+ * an important assumption is made that the process is running.
+ */
+void
+palloc(pid, t)
+ int pid;
+ register struct command *t;
+{
+ register struct process *pp;
+ int i;
+
+ pp = (struct process *) xcalloc(1, (size_t) sizeof(struct process));
+ pp->p_pid = pid;
+ pp->p_flags = t->t_dflg & F_AMPERSAND ? PRUNNING : PRUNNING | PFOREGND;
+ if (t->t_dflg & F_TIME)
+ pp->p_flags |= PPTIME;
+ cmdp = command;
+ cmdlen = 0;
+ padd(t);
+ *cmdp++ = 0;
+ if (t->t_dflg & F_PIPEOUT) {
+ pp->p_flags |= PPOU;
+ if (t->t_dflg & F_STDERR)
+ pp->p_flags |= PDIAG;
+ }
+ pp->p_command = Strsave(command);
+ if (pcurrjob) {
+ struct process *fp;
+
+ /* careful here with interrupt level */
+ pp->p_cwd = 0;
+ pp->p_index = pcurrjob->p_index;
+ pp->p_friends = pcurrjob;
+ pp->p_jobid = pcurrjob->p_pid;
+ for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends);
+ fp->p_friends = pp;
+ }
+ else {
+ pcurrjob = pp;
+ pp->p_jobid = pid;
+ pp->p_friends = pp;
+ pp->p_cwd = dcwd;
+ dcwd->di_count++;
+ if (pmaxindex < BIGINDEX)
+ pp->p_index = ++pmaxindex;
+ else {
+ struct process *np;
+
+ for (i = 1;; i++) {
+ for (np = proclist.p_next; np; np = np->p_next)
+ if (np->p_index == i)
+ goto tryagain;
+ pp->p_index = i;
+ if (i > pmaxindex)
+ pmaxindex = i;
+ break;
+ tryagain:;
+ }
+ }
+ if (pcurrent == NULL)
+ pcurrent = pp;
+ else if (pprevious == NULL)
+ pprevious = pp;
+ }
+ pp->p_next = proclist.p_next;
+ proclist.p_next = pp;
+ (void) gettimeofday(&pp->p_btime, NULL);
+}
+
+static void
+padd(t)
+ register struct command *t;
+{
+ Char **argp;
+
+ if (t == 0)
+ return;
+ switch (t->t_dtyp) {
+
+ case NODE_PAREN:
+ pads(STRLparensp);
+ padd(t->t_dspr);
+ pads(STRspRparen);
+ break;
+
+ case NODE_COMMAND:
+ for (argp = t->t_dcom; *argp; argp++) {
+ pads(*argp);
+ if (argp[1])
+ pads(STRspace);
+ }
+ break;
+
+ case NODE_OR:
+ case NODE_AND:
+ case NODE_PIPE:
+ case NODE_LIST:
+ padd(t->t_dcar);
+ switch (t->t_dtyp) {
+ case NODE_OR:
+ pads(STRspor2sp);
+ break;
+ case NODE_AND:
+ pads(STRspand2sp);
+ break;
+ case NODE_PIPE:
+ pads(STRsporsp);
+ break;
+ case NODE_LIST:
+ pads(STRsemisp);
+ break;
+ }
+ padd(t->t_dcdr);
+ return;
+ }
+ if ((t->t_dflg & F_PIPEIN) == 0 && t->t_dlef) {
+ pads((t->t_dflg & F_READ) ? STRspLarrow2sp : STRspLarrowsp);
+ pads(t->t_dlef);
+ }
+ if ((t->t_dflg & F_PIPEOUT) == 0 && t->t_drit) {
+ pads((t->t_dflg & F_APPEND) ? STRspRarrow2 : STRspRarrow);
+ if (t->t_dflg & F_STDERR)
+ pads(STRand);
+ pads(STRspace);
+ pads(t->t_drit);
+ }
+}
+
+static void
+pads(cp)
+ Char *cp;
+{
+ register int i;
+
+ /*
+ * Avoid the Quoted Space alias hack! Reported by:
+ * sam@john-bigboote.ICS.UCI.EDU (Sam Horrocks)
+ */
+ if (cp[0] == STRQNULL[0])
+ cp++;
+
+ i = Strlen(cp);
+
+ if (cmdlen >= PMAXLEN)
+ return;
+ if (cmdlen + i >= PMAXLEN) {
+ (void) Strcpy(cmdp, STRsp3dots);
+ cmdlen = PMAXLEN;
+ cmdp += 4;
+ return;
+ }
+ (void) Strcpy(cmdp, cp);
+ cmdp += i;
+ cmdlen += i;
+}
+
+/*
+ * psavejob - temporarily save the current job on a one level stack
+ * so another job can be created. Used for { } in exp6
+ * and `` in globbing.
+ */
+void
+psavejob()
+{
+
+ pholdjob = pcurrjob;
+ pcurrjob = NULL;
+}
+
+/*
+ * prestjob - opposite of psavejob. This may be missed if we are interrupted
+ * somewhere, but pendjob cleans up anyway.
+ */
+void
+prestjob()
+{
+
+ pcurrjob = pholdjob;
+ pholdjob = NULL;
+}
+
+/*
+ * pendjob - indicate that a job (set of commands) has been completed
+ * or is about to begin.
+ */
+void
+pendjob()
+{
+ register struct process *pp, *tp;
+
+ if (pcurrjob && (pcurrjob->p_flags & (PFOREGND | PSTOPPED)) == 0) {
+ pp = pcurrjob;
+ while (pp->p_pid != pp->p_jobid)
+ pp = pp->p_friends;
+ xprintf("[%d]", pp->p_index);
+ tp = pp;
+ do {
+ xprintf(" %d", pp->p_pid);
+ pp = pp->p_friends;
+ } while (pp != tp);
+ xprintf("\n");
+ }
+ pholdjob = pcurrjob = 0;
+}
+
+/*
+ * pprint - print a job
+ */
+static int
+pprint(pp, flag)
+ register struct process *pp;
+ bool flag;
+{
+ register status, reason;
+ struct process *tp;
+ extern char *linp, linbuf[];
+ int jobflags, pstatus;
+ char *format;
+
+ while (pp->p_pid != pp->p_jobid)
+ pp = pp->p_friends;
+ if (pp == pp->p_friends && (pp->p_flags & PPTIME)) {
+ pp->p_flags &= ~PPTIME;
+ pp->p_flags |= PTIME;
+ }
+ tp = pp;
+ status = reason = -1;
+ jobflags = 0;
+ do {
+ jobflags |= pp->p_flags;
+ pstatus = pp->p_flags & PALLSTATES;
+ if (tp != pp && linp != linbuf && !(flag & FANCY) &&
+ (pstatus == status && pp->p_reason == reason ||
+ !(flag & REASON)))
+ xprintf(" ");
+ else {
+ if (tp != pp && linp != linbuf)
+ xprintf("\n");
+ if (flag & NUMBER)
+ if (pp == tp)
+ xprintf("[%d]%s %c ", pp->p_index,
+ pp->p_index < 10 ? " " : "",
+ pp == pcurrent ? '+' :
+ (pp == pprevious ? '-' : ' '));
+ else
+ xprintf(" ");
+ if (flag & FANCY) {
+ xprintf("%5d ", pp->p_pid);
+ }
+ if (flag & (REASON | AREASON)) {
+ if (flag & NAME)
+ format = "%-23s";
+ else
+ format = "%s";
+ if (pstatus == status)
+ if (pp->p_reason == reason) {
+ xprintf(format, "");
+ goto prcomd;
+ }
+ else
+ reason = pp->p_reason;
+ else {
+ status = pstatus;
+ reason = pp->p_reason;
+ }
+ switch (status) {
+
+ case PRUNNING:
+ xprintf(format, "Running ");
+ break;
+
+ case PINTERRUPTED:
+ case PSTOPPED:
+ case PSIGNALED:
+ if ((flag & REASON) ||
+ ((flag & AREASON) && reason != SIGINT
+ && reason != SIGPIPE))
+ xprintf(format, mesg[pp->p_reason].pname);
+ break;
+
+ case PNEXITED:
+ case PAEXITED:
+ if (flag & REASON)
+ if (pp->p_reason)
+ xprintf("Exit %-18d", pp->p_reason);
+ else
+ xprintf(format, "Done");
+ break;
+
+ default:
+ xprintf("BUG: status=%-9o", status);
+ }
+ }
+ }
+prcomd:
+ if (flag & NAME) {
+ xprintf("%s", short2str(pp->p_command));
+ if (pp->p_flags & PPOU)
+ xprintf(" |");
+ if (pp->p_flags & PDIAG)
+ xprintf("&");
+ }
+ if (flag & (REASON | AREASON) && pp->p_flags & PDUMPED)
+ xprintf(" (core dumped)");
+ if (tp == pp->p_friends) {
+ if (flag & AMPERSAND)
+ xprintf(" &");
+ if (flag & JOBDIR &&
+ !eq(tp->p_cwd->di_name, dcwd->di_name)) {
+ xprintf(" (wd: ");
+ dtildepr(value(STRhome), tp->p_cwd->di_name);
+ xprintf(")");
+ }
+ }
+ if (pp->p_flags & PPTIME && !(status & (PSTOPPED | PRUNNING))) {
+ if (linp != linbuf)
+ xprintf("\n\t");
+ prusage(&zru, &pp->p_rusage, &pp->p_etime,
+ &pp->p_btime);
+ }
+ if (tp == pp->p_friends) {
+ if (linp != linbuf)
+ xprintf("\n");
+ if (flag & SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) {
+ xprintf("(wd now: ");
+ dtildepr(value(STRhome), dcwd->di_name);
+ xprintf(")\n");
+ }
+ }
+ } while ((pp = pp->p_friends) != tp);
+ if (jobflags & PTIME && (jobflags & (PSTOPPED | PRUNNING)) == 0) {
+ if (jobflags & NUMBER)
+ xprintf(" ");
+ ptprint(tp);
+ }
+ return (jobflags);
+}
+
+static void
+ptprint(tp)
+ register struct process *tp;
+{
+ struct timeval tetime, diff;
+ static struct timeval ztime;
+ struct rusage ru;
+ static struct rusage zru;
+ register struct process *pp = tp;
+
+ ru = zru;
+ tetime = ztime;
+ do {
+ ruadd(&ru, &pp->p_rusage);
+ tvsub(&diff, &pp->p_etime, &pp->p_btime);
+ if (timercmp(&diff, &tetime, >))
+ tetime = diff;
+ } while ((pp = pp->p_friends) != tp);
+ prusage(&zru, &ru, &tetime, &ztime);
+}
+
+/*
+ * dojobs - print all jobs
+ */
+void
+dojobs(v)
+ Char **v;
+{
+ register struct process *pp;
+ register int flag = NUMBER | NAME | REASON;
+ int i;
+
+ if (chkstop)
+ chkstop = 2;
+ if (*++v) {
+ if (v[1] || !eq(*v, STRml))
+ stderror(ERR_JOBS);
+ flag |= FANCY | JOBDIR;
+ }
+ for (i = 1; i <= pmaxindex; i++)
+ for (pp = proclist.p_next; pp; pp = pp->p_next)
+ if (pp->p_index == i && pp->p_pid == pp->p_jobid) {
+ pp->p_flags &= ~PNEEDNOTE;
+ if (!(pprint(pp, flag) & (PRUNNING | PSTOPPED)))
+ pflush(pp);
+ break;
+ }
+}
+
+/*
+ * dofg - builtin - put the job into the foreground
+ */
+void
+dofg(v)
+ Char **v;
+{
+ register struct process *pp;
+
+ okpcntl();
+ ++v;
+ do {
+ pp = pfind(*v);
+ pstart(pp, 1);
+ pjwait(pp);
+ } while (*v && *++v);
+}
+
+/*
+ * %... - builtin - put the job into the foreground
+ */
+void
+dofg1(v)
+ Char **v;
+{
+ register struct process *pp;
+
+ okpcntl();
+ pp = pfind(v[0]);
+ pstart(pp, 1);
+ pjwait(pp);
+}
+
+/*
+ * dobg - builtin - put the job into the background
+ */
+void
+dobg(v)
+ Char **v;
+{
+ register struct process *pp;
+
+ okpcntl();
+ ++v;
+ do {
+ pp = pfind(*v);
+ pstart(pp, 0);
+ } while (*v && *++v);
+}
+
+/*
+ * %... & - builtin - put the job into the background
+ */
+void
+dobg1(v)
+ Char **v;
+{
+ register struct process *pp;
+
+ pp = pfind(v[0]);
+ pstart(pp, 0);
+}
+
+/*
+ * dostop - builtin - stop the job
+ */
+void
+dostop(v)
+ Char **v;
+{
+ pkill(++v, SIGSTOP);
+}
+
+/*
+ * dokill - builtin - superset of kill (1)
+ */
+void
+dokill(v)
+ Char **v;
+{
+ register int signum, len = 0;
+ register char *name;
+
+ v++;
+ if (v[0] && v[0][0] == '-') {
+ if (v[0][1] == 'l') {
+ for (signum = 1; signum <= NSIG; signum++) {
+ if ((name = mesg[signum].iname) != NULL) {
+ len += strlen(name) + 1;
+ if (len >= 80 - 1) {
+ xprintf("\n");
+ len = strlen(name) + 1;
+ }
+ xprintf("%s ", name);
+ }
+ }
+ xprintf("\n");
+ return;
+ }
+ if (Isdigit(v[0][1])) {
+ signum = atoi(short2str(v[0] + 1));
+ if (signum < 0 || signum > NSIG)
+ stderror(ERR_NAME | ERR_BADSIG);
+ }
+ else {
+ for (signum = 1; signum <= NSIG; signum++)
+ if (mesg[signum].iname &&
+ eq(&v[0][1], str2short(mesg[signum].iname)))
+ goto gotsig;
+ setname(short2str(&v[0][1]));
+ stderror(ERR_NAME | ERR_UNKSIG);
+ }
+gotsig:
+ v++;
+ }
+ else
+ signum = SIGTERM;
+ pkill(v, signum);
+}
+
+static void
+pkill(v, signum)
+ Char **v;
+ int signum;
+{
+ register struct process *pp, *np;
+ register int jobflags = 0;
+ int pid, err1 = 0;
+ sigset_t omask;
+ Char *cp;
+
+ omask = sigmask(SIGCHLD);
+ if (setintr)
+ omask |= sigmask(SIGINT);
+ omask = sigblock(omask) & ~omask;
+ gflag = 0, tglob(v);
+ if (gflag) {
+ v = globall(v);
+ if (v == 0)
+ stderror(ERR_NAME | ERR_NOMATCH);
+ }
+ else {
+ v = gargv = saveblk(v);
+ trim(v);
+ }
+
+ while (v && (cp = *v)) {
+ if (*cp == '%') {
+ np = pp = pfind(cp);
+ do
+ jobflags |= np->p_flags;
+ while ((np = np->p_friends) != pp);
+ switch (signum) {
+
+ case SIGSTOP:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ if ((jobflags & PRUNNING) == 0) {
+ xprintf("%s: Already suspended\n", short2str(cp));
+ err1++;
+ goto cont;
+ }
+ break;
+ /*
+ * suspend a process, kill -CONT %, then type jobs; the shell
+ * says it is suspended, but it is running; thanks jaap..
+ */
+ case SIGCONT:
+ pstart(pp, 0);
+ goto cont;
+ }
+ if (killpg((pid_t) pp->p_jobid, signum) < 0) {
+ xprintf("%s: %s\n", short2str(cp), strerror(errno));
+ err1++;
+ }
+ if (signum == SIGTERM || signum == SIGHUP)
+ (void) killpg((pid_t) pp->p_jobid, SIGCONT);
+ }
+ else if (!(Isdigit(*cp) || *cp == '-'))
+ stderror(ERR_NAME | ERR_JOBARGS);
+ else {
+ pid = atoi(short2str(cp));
+ if (kill((pid_t) pid, signum) < 0) {
+ xprintf("%d: %s\n", pid, strerror(errno));
+ err1++;
+ goto cont;
+ }
+ if (signum == SIGTERM || signum == SIGHUP)
+ (void) kill((pid_t) pid, SIGCONT);
+ }
+cont:
+ v++;
+ }
+ if (gargv)
+ blkfree(gargv), gargv = 0;
+ (void) sigsetmask(omask);
+ if (err1)
+ stderror(ERR_SILENT);
+}
+
+/*
+ * pstart - start the job in foreground/background
+ */
+void
+pstart(pp, foregnd)
+ register struct process *pp;
+ int foregnd;
+{
+ register struct process *np;
+ sigset_t omask;
+ long jobflags = 0;
+
+ omask = sigblock(sigmask(SIGCHLD));
+ np = pp;
+ do {
+ jobflags |= np->p_flags;
+ if (np->p_flags & (PRUNNING | PSTOPPED)) {
+ np->p_flags |= PRUNNING;
+ np->p_flags &= ~PSTOPPED;
+ if (foregnd)
+ np->p_flags |= PFOREGND;
+ else
+ np->p_flags &= ~PFOREGND;
+ }
+ } while ((np = np->p_friends) != pp);
+ if (!foregnd)
+ pclrcurr(pp);
+ (void) pprint(pp, foregnd ? NAME | JOBDIR : NUMBER | NAME | AMPERSAND);
+ if (foregnd)
+ (void) tcsetpgrp(FSHTTY, pp->p_jobid);
+ if (jobflags & PSTOPPED)
+ (void) killpg((pid_t) pp->p_jobid, SIGCONT);
+ (void) sigsetmask(omask);
+}
+
+void
+panystop(neednl)
+ bool neednl;
+{
+ register struct process *pp;
+
+ chkstop = 2;
+ for (pp = proclist.p_next; pp; pp = pp->p_next)
+ if (pp->p_flags & PSTOPPED)
+ stderror(ERR_STOPPED, neednl ? "\n" : "");
+}
+
+struct process *
+pfind(cp)
+ Char *cp;
+{
+ register struct process *pp, *np;
+
+ if (cp == 0 || cp[1] == 0 || eq(cp, STRcent2) || eq(cp, STRcentplus)) {
+ if (pcurrent == NULL)
+ stderror(ERR_NAME | ERR_JOBCUR);
+ return (pcurrent);
+ }
+ if (eq(cp, STRcentminus) || eq(cp, STRcenthash)) {
+ if (pprevious == NULL)
+ stderror(ERR_NAME | ERR_JOBPREV);
+ return (pprevious);
+ }
+ if (Isdigit(cp[1])) {
+ int idx = atoi(short2str(cp + 1));
+
+ for (pp = proclist.p_next; pp; pp = pp->p_next)
+ if (pp->p_index == idx && pp->p_pid == pp->p_jobid)
+ return (pp);
+ stderror(ERR_NAME | ERR_NOSUCHJOB);
+ }
+ np = NULL;
+ for (pp = proclist.p_next; pp; pp = pp->p_next)
+ if (pp->p_pid == pp->p_jobid) {
+ if (cp[1] == '?') {
+ register Char *dp;
+
+ for (dp = pp->p_command; *dp; dp++) {
+ if (*dp != cp[2])
+ continue;
+ if (prefix(cp + 2, dp))
+ goto match;
+ }
+ }
+ else if (prefix(cp + 1, pp->p_command)) {
+ match:
+ if (np)
+ stderror(ERR_NAME | ERR_AMBIG);
+ np = pp;
+ }
+ }
+ if (np)
+ return (np);
+ stderror(ERR_NAME | cp[1] == '?' ? ERR_JOBPAT : ERR_NOSUCHJOB);
+ /* NOTREACHED */
+ return (0);
+}
+
+
+/*
+ * pgetcurr - find most recent job that is not pp, preferably stopped
+ */
+static struct process *
+pgetcurr(pp)
+ register struct process *pp;
+{
+ register struct process *np;
+ register struct process *xp = NULL;
+
+ for (np = proclist.p_next; np; np = np->p_next)
+ if (np != pcurrent && np != pp && np->p_pid &&
+ np->p_pid == np->p_jobid) {
+ if (np->p_flags & PSTOPPED)
+ return (np);
+ if (xp == NULL)
+ xp = np;
+ }
+ return (xp);
+}
+
+/*
+ * donotify - flag the job so as to report termination asynchronously
+ */
+void
+donotify(v)
+ Char **v;
+{
+ register struct process *pp;
+
+ pp = pfind(*++v);
+ pp->p_flags |= PNOTIFY;
+}
+
+/*
+ * Do the fork and whatever should be done in the child side that
+ * should not be done if we are not forking at all (like for simple builtin's)
+ * Also do everything that needs any signals fiddled with in the parent side
+ *
+ * Wanttty tells whether process and/or tty pgrps are to be manipulated:
+ * -1: leave tty alone; inherit pgrp from parent
+ * 0: already have tty; manipulate process pgrps only
+ * 1: want to claim tty; manipulate process and tty pgrps
+ * It is usually just the value of tpgrp.
+ */
+
+int
+pfork(t, wanttty)
+ struct command *t; /* command we are forking for */
+ int wanttty;
+{
+ register int pid;
+ bool ignint = 0;
+ int pgrp;
+ sigset_t omask;
+
+ /*
+ * A child will be uninterruptible only under very special conditions.
+ * Remember that the semantics of '&' is implemented by disconnecting the
+ * process from the tty so signals do not need to ignored just for '&'.
+ * Thus signals are set to default action for children unless: we have had
+ * an "onintr -" (then specifically ignored) we are not playing with
+ * signals (inherit action)
+ */
+ if (setintr)
+ ignint = (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT))
+ || (gointr && eq(gointr, STRminus));
+ /*
+ * Check for maximum nesting of 16 processes to avoid Forking loops
+ */
+ if (child == 16)
+ stderror(ERR_NESTING, 16);
+ /*
+ * Hold SIGCHLD until we have the process installed in our table.
+ */
+ omask = sigblock(sigmask(SIGCHLD));
+ while ((pid = fork()) < 0)
+ if (setintr == 0)
+ (void) sleep(FORKSLEEP);
+ else {
+ (void) sigsetmask(omask);
+ stderror(ERR_NOPROC);
+ }
+ if (pid == 0) {
+ settimes();
+ pgrp = pcurrjob ? pcurrjob->p_jobid : getpid();
+ pflushall();
+ pcurrjob = NULL;
+ child++;
+ if (setintr) {
+ setintr = 0; /* until I think otherwise */
+ /*
+ * Children just get blown away on SIGINT, SIGQUIT unless "onintr
+ * -" seen.
+ */
+ (void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL);
+ (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL);
+ if (wanttty >= 0) {
+ /* make stoppable */
+ (void) signal(SIGTSTP, SIG_DFL);
+ (void) signal(SIGTTIN, SIG_DFL);
+ (void) signal(SIGTTOU, SIG_DFL);
+ }
+ (void) signal(SIGTERM, parterm);
+ }
+ else if (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) {
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGQUIT, SIG_IGN);
+ }
+ pgetty(wanttty, pgrp);
+ /*
+ * Nohup and nice apply only to NODE_COMMAND's but it would be nice
+ * (?!?) if you could say "nohup (foo;bar)" Then the parser would have
+ * to know about nice/nohup/time
+ */
+ if (t->t_dflg & F_NOHUP)
+ (void) signal(SIGHUP, SIG_IGN);
+ if (t->t_dflg & F_NICE)
+ (void) setpriority(PRIO_PROCESS, 0, t->t_nice);
+ }
+ else {
+ if (wanttty >= 0)
+ (void) setpgid(pid, pcurrjob ? pcurrjob->p_jobid : pid);
+ palloc(pid, t);
+ (void) sigsetmask(omask);
+ }
+
+ return (pid);
+}
+
+static void
+okpcntl()
+{
+ if (tpgrp == -1)
+ stderror(ERR_JOBCONTROL);
+ if (tpgrp == 0)
+ stderror(ERR_JOBCTRLSUB);
+}
+
+/*
+ * if we don't have vfork(), things can still go in the wrong order
+ * resulting in the famous 'Stopped (tty output)'. But some systems
+ * don't permit the setpgid() call, (these are more recent secure
+ * systems such as ibm's aix). Then we'd rather print an error message
+ * than hang the shell!
+ * I am open to suggestions how to fix that.
+ */
+void
+pgetty(wanttty, pgrp)
+ int wanttty, pgrp;
+{
+ sigset_t omask = 0;
+
+ /*
+ * christos: I am blocking the tty signals till I've set things
+ * correctly....
+ */
+ if (wanttty > 0)
+ omask = sigblock(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU));
+ /*
+ * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
+ * Don't check for tpgrp >= 0 so even non-interactive shells give
+ * background jobs process groups Same for the comparison in the other part
+ * of the #ifdef
+ */
+ if (wanttty >= 0)
+ if (setpgid(0, pgrp) == -1) {
+ xprintf("csh: setpgid error.\n");
+ xexit(0);
+ }
+
+ if (wanttty > 0) {
+ (void) tcsetpgrp(FSHTTY, pgrp);
+ (void) sigsetmask(omask);
+ }
+
+ if (tpgrp > 0)
+ tpgrp = 0; /* gave tty away */
+}
diff --git a/bin/csh/proc.h b/bin/csh/proc.h
new file mode 100644
index 000000000000..82531f28d354
--- /dev/null
+++ b/bin/csh/proc.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * @(#)proc.h 5.6 (Berkeley) 6/25/91
+ */
+
+/*
+ * Structure for each process the shell knows about:
+ * allocated and filled by pcreate.
+ * flushed by pflush; freeing always happens at top level
+ * so the interrupt level has less to worry about.
+ * processes are related to "friends" when in a pipeline;
+ * p_friends links makes a circular list of such jobs
+ */
+struct process {
+ struct process *p_next; /* next in global "proclist" */
+ struct process *p_friends; /* next in job list (or self) */
+ struct directory *p_cwd; /* cwd of the job (only in head) */
+ short unsigned p_flags; /* various job status flags */
+ char p_reason; /* reason for entering this state */
+ int p_index; /* shorthand job index */
+ int p_pid;
+ int p_jobid; /* pid of job leader */
+ /* if a job is stopped/background p_jobid gives its pgrp */
+ struct timeval p_btime; /* begin time */
+ struct timeval p_etime; /* end time */
+ struct rusage p_rusage;
+ Char *p_command; /* first PMAXLEN chars of command */
+};
+
+/* flag values for p_flags */
+#define PRUNNING (1<<0) /* running */
+#define PSTOPPED (1<<1) /* stopped */
+#define PNEXITED (1<<2) /* normally exited */
+#define PAEXITED (1<<3) /* abnormally exited */
+#define PSIGNALED (1<<4) /* terminated by a signal != SIGINT */
+
+#define PALLSTATES (PRUNNING|PSTOPPED|PNEXITED|PAEXITED|PSIGNALED|PINTERRUPTED)
+#define PNOTIFY (1<<5) /* notify async when done */
+#define PTIME (1<<6) /* job times should be printed */
+#define PAWAITED (1<<7) /* top level is waiting for it */
+#define PFOREGND (1<<8) /* started in shells pgrp */
+#define PDUMPED (1<<9) /* process dumped core */
+#define PDIAG (1<<10) /* diagnostic output also piped out */
+#define PPOU (1<<11) /* piped output */
+#define PREPORTED (1<<12) /* status has been reported */
+#define PINTERRUPTED (1<<13) /* job stopped via interrupt signal */
+#define PPTIME (1<<14) /* time individual process */
+#define PNEEDNOTE (1<<15) /* notify as soon as practical */
+
+#define PMAXLEN 80
+
+/* defines for arguments to pprint */
+#define NUMBER 01
+#define NAME 02
+#define REASON 04
+#define AMPERSAND 010
+#define FANCY 020
+#define SHELLDIR 040 /* print shell's dir if not the same */
+#define JOBDIR 0100 /* print job's dir if not the same */
+#define AREASON 0200
+
+struct process proclist; /* list head of all processes */
+bool pnoprocesses; /* pchild found nothing to wait for */
+
+struct process *pholdjob; /* one level stack of current jobs */
+
+struct process *pcurrjob; /* current job */
+struct process *pcurrent; /* current job in table */
+struct process *pprevious; /* previous job in table */
+
+int pmaxindex; /* current maximum job index */
diff --git a/bin/csh/sem.c b/bin/csh/sem.c
new file mode 100644
index 000000000000..7eb145f5ce0e
--- /dev/null
+++ b/bin/csh/sem.c
@@ -0,0 +1,571 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sem.c 5.17 (Berkeley) 6/17/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "proc.h"
+#include "extern.h"
+
+static void vffree __P((int));
+static void doio __P((struct command *t, int *, int *));
+static void chkclob __P((char *));
+
+void
+execute(t, wanttty, pipein, pipeout)
+ register struct command *t;
+ int wanttty, *pipein, *pipeout;
+{
+ bool forked = 0;
+ struct biltins *bifunc;
+ int pid = 0;
+ int pv[2];
+
+ static sigset_t csigmask;
+
+ static sigset_t ocsigmask;
+ static int onosigchld = 0;
+ static int nosigchld = 0;
+
+ if (t == 0)
+ return;
+
+ if (t->t_dflg & F_AMPERSAND)
+ wanttty = 0;
+ switch (t->t_dtyp) {
+
+ case NODE_COMMAND:
+ if ((t->t_dcom[0][0] & (QUOTE | TRIM)) == QUOTE)
+ (void) Strcpy(t->t_dcom[0], t->t_dcom[0] + 1);
+ if ((t->t_dflg & F_REPEAT) == 0)
+ Dfix(t); /* $ " ' \ */
+ if (t->t_dcom[0] == 0)
+ return;
+ /* fall into... */
+
+ case NODE_PAREN:
+ if (t->t_dflg & F_PIPEOUT)
+ mypipe(pipeout);
+ /*
+ * Must do << early so parent will know where input pointer should be.
+ * If noexec then this is all we do.
+ */
+ if (t->t_dflg & F_READ) {
+ (void) close(0);
+ heredoc(t->t_dlef);
+ if (noexec)
+ (void) close(0);
+ }
+ if (noexec)
+ break;
+
+ set(STRstatus, Strsave(STR0));
+
+ /*
+ * This mess is the necessary kludge to handle the prefix builtins:
+ * nice, nohup, time. These commands can also be used by themselves,
+ * and this is not handled here. This will also work when loops are
+ * parsed.
+ */
+ while (t->t_dtyp == NODE_COMMAND)
+ if (eq(t->t_dcom[0], STRnice))
+ if (t->t_dcom[1])
+ if (strchr("+-", t->t_dcom[1][0]))
+ if (t->t_dcom[2]) {
+ setname("nice");
+ t->t_nice =
+ getn(t->t_dcom[1]);
+ lshift(t->t_dcom, 2);
+ t->t_dflg |= F_NICE;
+ }
+ else
+ break;
+ else {
+ t->t_nice = 4;
+ lshift(t->t_dcom, 1);
+ t->t_dflg |= F_NICE;
+ }
+ else
+ break;
+ else if (eq(t->t_dcom[0], STRnohup))
+ if (t->t_dcom[1]) {
+ t->t_dflg |= F_NOHUP;
+ lshift(t->t_dcom, 1);
+ }
+ else
+ break;
+ else if (eq(t->t_dcom[0], STRtime))
+ if (t->t_dcom[1]) {
+ t->t_dflg |= F_TIME;
+ lshift(t->t_dcom, 1);
+ }
+ else
+ break;
+ else
+ break;
+
+ /* is t a command */
+ if (t->t_dtyp == NODE_COMMAND) {
+ /*
+ * Check if we have a builtin function and remember which one.
+ */
+ bifunc = isbfunc(t);
+ }
+ else { /* not a command */
+ bifunc = NULL;
+ }
+
+ /*
+ * We fork only if we are timed, or are not the end of a parenthesized
+ * list and not a simple builtin function. Simple meaning one that is
+ * not pipedout, niced, nohupped, or &'d. It would be nice(?) to not
+ * fork in some of these cases.
+ */
+ /*
+ * Prevent forking cd, pushd, popd, chdir cause this will cause the
+ * shell not to change dir!
+ */
+ if (bifunc && (bifunc->bfunct == dochngd ||
+ bifunc->bfunct == dopushd ||
+ bifunc->bfunct == dopopd))
+ t->t_dflg &= ~(F_NICE);
+ if (((t->t_dflg & F_TIME) || (t->t_dflg & F_NOFORK) == 0 &&
+ (!bifunc || t->t_dflg &
+ (F_PIPEOUT | F_AMPERSAND | F_NICE | F_NOHUP))) ||
+ /*
+ * We have to fork for eval too.
+ */
+ (bifunc && (t->t_dflg & F_PIPEIN) != 0 &&
+ bifunc->bfunct == doeval))
+ if (t->t_dtyp == NODE_PAREN ||
+ t->t_dflg & (F_REPEAT | F_AMPERSAND) || bifunc) {
+ forked++;
+ /*
+ * We need to block SIGCHLD here, so that if the process does
+ * not die before we can set the process group
+ */
+ if (wanttty >= 0 && !nosigchld) {
+ csigmask = sigblock(sigmask(SIGCHLD));
+ nosigchld = 1;
+ }
+
+ pid = pfork(t, wanttty);
+ if (pid == 0 && nosigchld) {
+ (void) sigsetmask(csigmask);
+ nosigchld = 0;
+ }
+ }
+ else {
+ int ochild, osetintr, ohaderr, odidfds;
+ int oSHIN, oSHOUT, oSHDIAG, oOLDSTD, otpgrp;
+ sigset_t omask;
+
+ /*
+ * Prepare for the vfork by saving everything that the child
+ * corrupts before it exec's. Note that in some signal
+ * implementations which keep the signal info in user space
+ * (e.g. Sun's) it will also be necessary to save and restore
+ * the current sigvec's for the signals the child touches
+ * before it exec's.
+ */
+ if (wanttty >= 0 && !nosigchld && !noexec) {
+ csigmask = sigblock(sigmask(SIGCHLD));
+ nosigchld = 1;
+ }
+ omask = sigblock(sigmask(SIGCHLD) | sigmask(SIGINT));
+ ochild = child;
+ osetintr = setintr;
+ ohaderr = haderr;
+ odidfds = didfds;
+ oSHIN = SHIN;
+ oSHOUT = SHOUT;
+ oSHDIAG = SHDIAG;
+ oOLDSTD = OLDSTD;
+ otpgrp = tpgrp;
+ ocsigmask = csigmask;
+ onosigchld = nosigchld;
+ Vsav = Vdp = 0;
+ Vexpath = 0;
+ Vt = 0;
+ pid = vfork();
+
+ if (pid < 0) {
+ (void) sigsetmask(omask);
+ stderror(ERR_NOPROC);
+ }
+ forked++;
+ if (pid) { /* parent */
+ child = ochild;
+ setintr = osetintr;
+ haderr = ohaderr;
+ didfds = odidfds;
+ SHIN = oSHIN;
+ SHOUT = oSHOUT;
+ SHDIAG = oSHDIAG;
+ OLDSTD = oOLDSTD;
+ tpgrp = otpgrp;
+ csigmask = ocsigmask;
+ nosigchld = onosigchld;
+
+ xfree((ptr_t) Vsav);
+ Vsav = 0;
+ xfree((ptr_t) Vdp);
+ Vdp = 0;
+ xfree((ptr_t) Vexpath);
+ Vexpath = 0;
+ blkfree((Char **) Vt);
+ Vt = 0;
+ /* this is from pfork() */
+ palloc(pid, t);
+ (void) sigsetmask(omask);
+ }
+ else { /* child */
+ /* this is from pfork() */
+ int pgrp;
+ bool ignint = 0;
+
+ if (nosigchld) {
+ (void) sigsetmask(csigmask);
+ nosigchld = 0;
+ }
+
+ if (setintr)
+ ignint =
+ (tpgrp == -1 &&
+ (t->t_dflg & F_NOINTERRUPT))
+ || gointr && eq(gointr, STRminus);
+ pgrp = pcurrjob ? pcurrjob->p_jobid : getpid();
+ child++;
+ if (setintr) {
+ setintr = 0;
+ if (ignint) {
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGQUIT, SIG_IGN);
+ }
+ else {
+ (void) signal(SIGINT, vffree);
+ (void) signal(SIGQUIT, SIG_DFL);
+ }
+
+ if (wanttty >= 0) {
+ (void) signal(SIGTSTP, SIG_DFL);
+ (void) signal(SIGTTIN, SIG_DFL);
+ (void) signal(SIGTTOU, SIG_DFL);
+ }
+
+ (void) signal(SIGTERM, parterm);
+ }
+ else if (tpgrp == -1 &&
+ (t->t_dflg & F_NOINTERRUPT)) {
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGQUIT, SIG_IGN);
+ }
+
+ pgetty(wanttty, pgrp);
+ if (t->t_dflg & F_NOHUP)
+ (void) signal(SIGHUP, SIG_IGN);
+ if (t->t_dflg & F_NICE)
+ (void) setpriority(PRIO_PROCESS, 0, t->t_nice);
+ }
+
+ }
+ if (pid != 0) {
+ /*
+ * It would be better if we could wait for the whole job when we
+ * knew the last process had been started. Pwait, in fact, does
+ * wait for the whole job anyway, but this test doesn't really
+ * express our intentions.
+ */
+ if (didfds == 0 && t->t_dflg & F_PIPEIN) {
+ (void) close(pipein[0]);
+ (void) close(pipein[1]);
+ }
+ if ((t->t_dflg & F_PIPEOUT) == 0) {
+ if (nosigchld) {
+ (void) sigsetmask(csigmask);
+ nosigchld = 0;
+ }
+ if ((t->t_dflg & F_AMPERSAND) == 0)
+ pwait();
+ }
+ break;
+ }
+ doio(t, pipein, pipeout);
+ if (t->t_dflg & F_PIPEOUT) {
+ (void) close(pipeout[0]);
+ (void) close(pipeout[1]);
+ }
+ /*
+ * Perform a builtin function. If we are not forked, arrange for
+ * possible stopping
+ */
+ if (bifunc) {
+ func(t, bifunc);
+ if (forked)
+ exitstat();
+ break;
+ }
+ if (t->t_dtyp != NODE_PAREN) {
+ doexec(t);
+ /* NOTREACHED */
+ }
+ /*
+ * For () commands must put new 0,1,2 in FSH* and recurse
+ */
+ OLDSTD = dcopy(0, FOLDSTD);
+ SHOUT = dcopy(1, FSHOUT);
+ SHDIAG = dcopy(2, FSHDIAG);
+ (void) close(SHIN);
+ SHIN = -1;
+ didfds = 0;
+ wanttty = -1;
+ t->t_dspr->t_dflg |= t->t_dflg & F_NOINTERRUPT;
+ execute(t->t_dspr, wanttty, NULL, NULL);
+ exitstat();
+
+ case NODE_PIPE:
+ t->t_dcar->t_dflg |= F_PIPEOUT |
+ (t->t_dflg & (F_PIPEIN | F_AMPERSAND | F_STDERR | F_NOINTERRUPT));
+ execute(t->t_dcar, wanttty, pipein, pv);
+ t->t_dcdr->t_dflg |= F_PIPEIN | (t->t_dflg &
+ (F_PIPEOUT | F_AMPERSAND | F_NOFORK | F_NOINTERRUPT));
+ if (wanttty > 0)
+ wanttty = 0; /* got tty already */
+ execute(t->t_dcdr, wanttty, pv, pipeout);
+ break;
+
+ case NODE_LIST:
+ if (t->t_dcar) {
+ t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT;
+ execute(t->t_dcar, wanttty, NULL, NULL);
+ /*
+ * In strange case of A&B make a new job after A
+ */
+ if (t->t_dcar->t_dflg & F_AMPERSAND && t->t_dcdr &&
+ (t->t_dcdr->t_dflg & F_AMPERSAND) == 0)
+ pendjob();
+ }
+ if (t->t_dcdr) {
+ t->t_dcdr->t_dflg |= t->t_dflg &
+ (F_NOFORK | F_NOINTERRUPT);
+ execute(t->t_dcdr, wanttty, NULL, NULL);
+ }
+ break;
+
+ case NODE_OR:
+ case NODE_AND:
+ if (t->t_dcar) {
+ t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT;
+ execute(t->t_dcar, wanttty, NULL, NULL);
+ if ((getn(value(STRstatus)) == 0) !=
+ (t->t_dtyp == NODE_AND))
+ return;
+ }
+ if (t->t_dcdr) {
+ t->t_dcdr->t_dflg |= t->t_dflg &
+ (F_NOFORK | F_NOINTERRUPT);
+ execute(t->t_dcdr, wanttty, NULL, NULL);
+ }
+ break;
+ }
+ /*
+ * Fall through for all breaks from switch
+ *
+ * If there will be no more executions of this command, flush all file
+ * descriptors. Places that turn on the F_REPEAT bit are responsible for
+ * doing donefds after the last re-execution
+ */
+ if (didfds && !(t->t_dflg & F_REPEAT))
+ donefds();
+}
+
+static void
+vffree(i)
+int i;
+{
+ register Char **v;
+
+ if (v = gargv) {
+ gargv = 0;
+ xfree((ptr_t) v);
+ }
+ if (v = pargv) {
+ pargv = 0;
+ xfree((ptr_t) v);
+ }
+ _exit(i);
+}
+
+/*
+ * Perform io redirection.
+ * We may or maynot be forked here.
+ */
+static void
+doio(t, pipein, pipeout)
+ register struct command *t;
+ int *pipein, *pipeout;
+{
+ register int fd;
+ register Char *cp;
+ register int flags = t->t_dflg;
+
+ if (didfds || (flags & F_REPEAT))
+ return;
+ if ((flags & F_READ) == 0) {/* F_READ already done */
+ if (cp = t->t_dlef) {
+ char tmp[MAXPATHLEN+1];
+
+ /*
+ * so < /dev/std{in,out,err} work
+ */
+ (void) dcopy(SHIN, 0);
+ (void) dcopy(SHOUT, 1);
+ (void) dcopy(SHDIAG, 2);
+ cp = globone(Dfix1(cp), G_IGNORE);
+ (void) strncpy(tmp, short2str(cp), MAXPATHLEN);
+ tmp[MAXPATHLEN] = '\0';
+ xfree((ptr_t) cp);
+ if ((fd = open(tmp, O_RDONLY)) < 0)
+ stderror(ERR_SYSTEM, tmp, strerror(errno));
+ (void) dmove(fd, 0);
+ }
+ else if (flags & F_PIPEIN) {
+ (void) close(0);
+ (void) dup(pipein[0]);
+ (void) close(pipein[0]);
+ (void) close(pipein[1]);
+ }
+ else if ((flags & F_NOINTERRUPT) && tpgrp == -1) {
+ (void) close(0);
+ (void) open(_PATH_DEVNULL, O_RDONLY);
+ }
+ else {
+ (void) close(0);
+ (void) dup(OLDSTD);
+ (void) ioctl(0, FIONCLEX, NULL);
+ }
+ }
+ if (cp = t->t_drit) {
+ char tmp[MAXPATHLEN+1];
+
+ cp = globone(Dfix1(cp), G_IGNORE);
+ (void) strncpy(tmp, short2str(cp), MAXPATHLEN);
+ tmp[MAXPATHLEN] = '\0';
+ xfree((ptr_t) cp);
+ /*
+ * so > /dev/std{out,err} work
+ */
+ (void) dcopy(SHOUT, 1);
+ (void) dcopy(SHDIAG, 2);
+ if ((flags & F_APPEND) &&
+#ifdef O_APPEND
+ (fd = open(tmp, O_WRONLY | O_APPEND)) >= 0);
+#else
+ (fd = open(tmp, O_WRONLY)) >= 0)
+ (void) lseek(1, (off_t) 0, L_XTND);
+#endif
+ else {
+ if (!(flags & F_OVERWRITE) && adrof(STRnoclobber)) {
+ if (flags & F_APPEND)
+ stderror(ERR_SYSTEM, tmp, strerror(errno));
+ chkclob(tmp);
+ }
+ if ((fd = creat(tmp, 0666)) < 0)
+ stderror(ERR_SYSTEM, tmp, strerror(errno));
+ }
+ (void) dmove(fd, 1);
+ }
+ else if (flags & F_PIPEOUT) {
+ (void) close(1);
+ (void) dup(pipeout[1]);
+ }
+ else {
+ (void) close(1);
+ (void) dup(SHOUT);
+ (void) ioctl(1, FIONCLEX, NULL);
+ }
+
+ (void) close(2);
+ if (flags & F_STDERR) {
+ (void) dup(1);
+ }
+ else {
+ (void) dup(SHDIAG);
+ (void) ioctl(2, FIONCLEX, NULL);
+ }
+ didfds = 1;
+}
+
+void
+mypipe(pv)
+ register int *pv;
+{
+
+ if (pipe(pv) < 0)
+ goto oops;
+ pv[0] = dmove(pv[0], -1);
+ pv[1] = dmove(pv[1], -1);
+ if (pv[0] >= 0 && pv[1] >= 0)
+ return;
+oops:
+ stderror(ERR_PIPE);
+}
+
+static void
+chkclob(cp)
+ register char *cp;
+{
+ struct stat stb;
+
+ if (stat(cp, &stb) < 0)
+ return;
+ if ((stb.st_mode & S_IFMT) == S_IFCHR)
+ return;
+ stderror(ERR_EXISTS, cp);
+}
diff --git a/bin/csh/set.c b/bin/csh/set.c
new file mode 100644
index 000000000000..7f763aaa5760
--- /dev/null
+++ b/bin/csh/set.c
@@ -0,0 +1,829 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)set.c 5.12 (Berkeley) 6/14/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+static Char *getinx __P((Char *, int *));
+static void asx __P((Char *, int, Char *));
+static struct varent
+ *getvx __P((Char *, int));
+static Char *xset __P((Char *, Char ***));
+static Char *operate __P((int, Char *, Char *));
+static void putn1 __P((int));
+static struct varent
+ *madrof __P((Char *, struct varent *));
+static void unsetv1 __P((struct varent *));
+static void exportpath __P((Char **));
+static void balance __P((struct varent *, int, int));
+
+
+/*
+ * C Shell
+ */
+
+void
+doset(v)
+ register Char **v;
+{
+ register Char *p;
+ Char *vp, op;
+ Char **vecp;
+ bool hadsub;
+ int subscr;
+
+ v++;
+ p = *v++;
+ if (p == 0) {
+ prvars();
+ return;
+ }
+ do {
+ hadsub = 0;
+ vp = p;
+ if (letter(*p))
+ for (; alnum(*p); p++)
+ continue;
+ if (vp == p || !letter(*vp))
+ stderror(ERR_NAME | ERR_VARBEGIN);
+ if ((p - vp) > MAXVARLEN) {
+ stderror(ERR_NAME | ERR_VARTOOLONG);
+ return;
+ }
+ if (*p == '[') {
+ hadsub++;
+ p = getinx(p, &subscr);
+ }
+ if (op = *p) {
+ *p++ = 0;
+ if (*p == 0 && *v && **v == '(')
+ p = *v++;
+ }
+ else if (*v && eq(*v, STRequal)) {
+ op = '=', v++;
+ if (*v)
+ p = *v++;
+ }
+ if (op && op != '=')
+ stderror(ERR_NAME | ERR_SYNTAX);
+ if (eq(p, STRLparen)) {
+ register Char **e = v;
+
+ if (hadsub)
+ stderror(ERR_NAME | ERR_SYNTAX);
+ for (;;) {
+ if (!*e)
+ stderror(ERR_NAME | ERR_MISSING, ')');
+ if (**e == ')')
+ break;
+ e++;
+ }
+ p = *e;
+ *e = 0;
+ vecp = saveblk(v);
+ set1(vp, vecp, &shvhed);
+ *e = p;
+ v = e + 1;
+ }
+ else if (hadsub)
+ asx(vp, subscr, Strsave(p));
+ else
+ set(vp, Strsave(p));
+ if (eq(vp, STRpath)) {
+ exportpath(adrof(STRpath)->vec);
+ dohash();
+ }
+ else if (eq(vp, STRhistchars)) {
+ register Char *pn = value(STRhistchars);
+
+ HIST = *pn++;
+ HISTSUB = *pn;
+ }
+ else if (eq(vp, STRuser)) {
+ Setenv(STRUSER, value(vp));
+ Setenv(STRLOGNAME, value(vp));
+ }
+ else if (eq(vp, STRwordchars)) {
+ word_chars = value(vp);
+ }
+ else if (eq(vp, STRterm))
+ Setenv(STRTERM, value(vp));
+ else if (eq(vp, STRhome)) {
+ register Char *cp;
+
+ cp = Strsave(value(vp)); /* get the old value back */
+
+ /*
+ * convert to cononical pathname (possibly resolving symlinks)
+ */
+ cp = dcanon(cp, cp);
+
+ set(vp, Strsave(cp)); /* have to save the new val */
+
+ /* and now mirror home with HOME */
+ Setenv(STRHOME, cp);
+ /* fix directory stack for new tilde home */
+ dtilde();
+ xfree((ptr_t) cp);
+ }
+#ifdef FILEC
+ else if (eq(vp, STRfilec))
+ filec = 1;
+#endif
+ } while (p = *v++);
+}
+
+static Char *
+getinx(cp, ip)
+ register Char *cp;
+ register int *ip;
+{
+
+ *ip = 0;
+ *cp++ = 0;
+ while (*cp && Isdigit(*cp))
+ *ip = *ip * 10 + *cp++ - '0';
+ if (*cp++ != ']')
+ stderror(ERR_NAME | ERR_SUBSCRIPT);
+ return (cp);
+}
+
+static void
+asx(vp, subscr, p)
+ Char *vp;
+ int subscr;
+ Char *p;
+{
+ register struct varent *v = getvx(vp, subscr);
+
+ xfree((ptr_t) v->vec[subscr - 1]);
+ v->vec[subscr - 1] = globone(p, G_APPEND);
+}
+
+static struct varent *
+getvx(vp, subscr)
+ Char *vp;
+ int subscr;
+{
+ register struct varent *v = adrof(vp);
+
+ if (v == 0)
+ udvar(vp);
+ if (subscr < 1 || subscr > blklen(v->vec))
+ stderror(ERR_NAME | ERR_RANGE);
+ return (v);
+}
+
+void
+dolet(v)
+ Char **v;
+{
+ register Char *p;
+ Char *vp, c, op;
+ bool hadsub;
+ int subscr;
+
+ v++;
+ p = *v++;
+ if (p == 0) {
+ prvars();
+ return;
+ }
+ do {
+ hadsub = 0;
+ vp = p;
+ if (letter(*p))
+ for (; alnum(*p); p++)
+ continue;
+ if (vp == p || !letter(*vp))
+ stderror(ERR_NAME | ERR_VARBEGIN);
+ if ((p - vp) > MAXVARLEN)
+ stderror(ERR_NAME | ERR_VARTOOLONG);
+ if (*p == '[') {
+ hadsub++;
+ p = getinx(p, &subscr);
+ }
+ if (*p == 0 && *v)
+ p = *v++;
+ if (op = *p)
+ *p++ = 0;
+ else
+ stderror(ERR_NAME | ERR_ASSIGN);
+
+ if (*p == '\0' && *v == NULL)
+ stderror(ERR_NAME | ERR_ASSIGN);
+
+ vp = Strsave(vp);
+ if (op == '=') {
+ c = '=';
+ p = xset(p, &v);
+ }
+ else {
+ c = *p++;
+ if (any("+-", c)) {
+ if (c != op || *p)
+ stderror(ERR_NAME | ERR_UNKNOWNOP);
+ p = Strsave(STR1);
+ }
+ else {
+ if (any("<>", op)) {
+ if (c != op)
+ stderror(ERR_NAME | ERR_UNKNOWNOP);
+ c = *p++;
+ stderror(ERR_NAME | ERR_SYNTAX);
+ }
+ if (c != '=')
+ stderror(ERR_NAME | ERR_UNKNOWNOP);
+ p = xset(p, &v);
+ }
+ }
+ if (op == '=')
+ if (hadsub)
+ asx(vp, subscr, p);
+ else
+ set(vp, p);
+ else if (hadsub) {
+ struct varent *gv = getvx(vp, subscr);
+
+ asx(vp, subscr, operate(op, gv->vec[subscr - 1], p));
+ }
+ else
+ set(vp, operate(op, value(vp), p));
+ if (eq(vp, STRpath)) {
+ exportpath(adrof(STRpath)->vec);
+ dohash();
+ }
+ xfree((ptr_t) vp);
+ if (c != '=')
+ xfree((ptr_t) p);
+ } while (p = *v++);
+}
+
+static Char *
+xset(cp, vp)
+ Char *cp, ***vp;
+{
+ register Char *dp;
+
+ if (*cp) {
+ dp = Strsave(cp);
+ --(*vp);
+ xfree((ptr_t) ** vp);
+ **vp = dp;
+ }
+ return (putn(exp(vp)));
+}
+
+static Char *
+operate(op, vp, p)
+ int op;
+ Char *vp, *p;
+{
+ Char opr[2];
+ Char *vec[5];
+ register Char **v = vec;
+ Char **vecp = v;
+ register int i;
+
+ if (op != '=') {
+ if (*vp)
+ *v++ = vp;
+ opr[0] = op;
+ opr[1] = 0;
+ *v++ = opr;
+ if (op == '<' || op == '>')
+ *v++ = opr;
+ }
+ *v++ = p;
+ *v++ = 0;
+ i = exp(&vecp);
+ if (*vecp)
+ stderror(ERR_NAME | ERR_EXPRESSION);
+ return (putn(i));
+}
+
+static Char *putp;
+
+Char *
+putn(n)
+ register int n;
+{
+ int num;
+ static Char number[15];
+
+ putp = number;
+ if (n < 0) {
+ n = -n;
+ *putp++ = '-';
+ }
+ num = 2; /* confuse lint */
+ if (sizeof(int) == num && n == -32768) {
+ *putp++ = '3';
+ n = 2768;
+#ifdef pdp11
+ }
+#else
+ }
+ else {
+ num = 4; /* confuse lint */
+ if (sizeof(int) == num && n == -2147483648) {
+ *putp++ = '2';
+ n = 147483648;
+ }
+ }
+#endif
+ putn1(n);
+ *putp = 0;
+ return (Strsave(number));
+}
+
+static void
+putn1(n)
+ register int n;
+{
+ if (n > 9)
+ putn1(n / 10);
+ *putp++ = n % 10 + '0';
+}
+
+int
+getn(cp)
+ register Char *cp;
+{
+ register int n;
+ int sign;
+
+ sign = 0;
+ if (cp[0] == '+' && cp[1])
+ cp++;
+ if (*cp == '-') {
+ sign++;
+ cp++;
+ if (!Isdigit(*cp))
+ stderror(ERR_NAME | ERR_BADNUM);
+ }
+ n = 0;
+ while (Isdigit(*cp))
+ n = n * 10 + *cp++ - '0';
+ if (*cp)
+ stderror(ERR_NAME | ERR_BADNUM);
+ return (sign ? -n : n);
+}
+
+Char *
+value1(var, head)
+ Char *var;
+ struct varent *head;
+{
+ register struct varent *vp;
+
+ vp = adrof1(var, head);
+ return (vp == 0 || vp->vec[0] == 0 ? STRNULL : vp->vec[0]);
+}
+
+static struct varent *
+madrof(pat, vp)
+ Char *pat;
+ register struct varent *vp;
+{
+ register struct varent *vp1;
+
+ for (; vp; vp = vp->v_right) {
+ if (vp->v_left && (vp1 = madrof(pat, vp->v_left)))
+ return vp1;
+ if (Gmatch(vp->v_name, pat))
+ return vp;
+ }
+ return vp;
+}
+
+struct varent *
+adrof1(name, v)
+ register Char *name;
+ register struct varent *v;
+{
+ register cmp;
+
+ v = v->v_left;
+ while (v && ((cmp = *name - *v->v_name) ||
+ (cmp = Strcmp(name, v->v_name))))
+ if (cmp < 0)
+ v = v->v_left;
+ else
+ v = v->v_right;
+ return v;
+}
+
+/*
+ * The caller is responsible for putting value in a safe place
+ */
+void
+set(var, val)
+ Char *var, *val;
+{
+ register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **)));
+
+ vec[0] = val;
+ vec[1] = 0;
+ set1(var, vec, &shvhed);
+}
+
+void
+set1(var, vec, head)
+ Char *var, **vec;
+ struct varent *head;
+{
+ register Char **oldv = vec;
+
+ gflag = 0;
+ tglob(oldv);
+ if (gflag) {
+ vec = globall(oldv);
+ if (vec == 0) {
+ blkfree(oldv);
+ stderror(ERR_NAME | ERR_NOMATCH);
+ return;
+ }
+ blkfree(oldv);
+ gargv = 0;
+ }
+ setq(var, vec, head);
+}
+
+
+void
+setq(name, vec, p)
+ Char *name, **vec;
+ register struct varent *p;
+{
+ register struct varent *c;
+ register f;
+
+ f = 0; /* tree hangs off the header's left link */
+ while (c = p->v_link[f]) {
+ if ((f = *name - *c->v_name) == 0 &&
+ (f = Strcmp(name, c->v_name)) == 0) {
+ blkfree(c->vec);
+ goto found;
+ }
+ p = c;
+ f = f > 0;
+ }
+ p->v_link[f] = c = (struct varent *) xmalloc((size_t) sizeof(struct varent));
+ c->v_name = Strsave(name);
+ c->v_bal = 0;
+ c->v_left = c->v_right = 0;
+ c->v_parent = p;
+ balance(p, f, 0);
+found:
+ trim(c->vec = vec);
+}
+
+void
+unset(v)
+ Char *v[];
+{
+ unset1(v, &shvhed);
+#ifdef FILEC
+ if (adrof(STRfilec) == 0)
+ filec = 0;
+#endif
+ if (adrof(STRhistchars) == 0) {
+ HIST = '!';
+ HISTSUB = '^';
+ }
+ if (adrof(STRwordchars) == 0)
+ word_chars = STR_WORD_CHARS;
+}
+
+void
+unset1(v, head)
+ register Char *v[];
+ struct varent *head;
+{
+ register struct varent *vp;
+ register int cnt;
+
+ while (*++v) {
+ cnt = 0;
+ while (vp = madrof(*v, head->v_left))
+ unsetv1(vp), cnt++;
+ if (cnt == 0)
+ setname(short2str(*v));
+ }
+}
+
+void
+unsetv(var)
+ Char *var;
+{
+ register struct varent *vp;
+
+ if ((vp = adrof1(var, &shvhed)) == 0)
+ udvar(var);
+ unsetv1(vp);
+}
+
+static void
+unsetv1(p)
+ register struct varent *p;
+{
+ register struct varent *c, *pp;
+ register f;
+
+ /*
+ * Free associated memory first to avoid complications.
+ */
+ blkfree(p->vec);
+ xfree((ptr_t) p->v_name);
+ /*
+ * If p is missing one child, then we can move the other into where p is.
+ * Otherwise, we find the predecessor of p, which is guaranteed to have no
+ * right child, copy it into p, and move it's left child into it.
+ */
+ if (p->v_right == 0)
+ c = p->v_left;
+ else if (p->v_left == 0)
+ c = p->v_right;
+ else {
+ for (c = p->v_left; c->v_right; c = c->v_right);
+ p->v_name = c->v_name;
+ p->vec = c->vec;
+ p = c;
+ c = p->v_left;
+ }
+ /*
+ * Move c into where p is.
+ */
+ pp = p->v_parent;
+ f = pp->v_right == p;
+ if (pp->v_link[f] = c)
+ c->v_parent = pp;
+ /*
+ * Free the deleted node, and rebalance.
+ */
+ xfree((ptr_t) p);
+ balance(pp, f, 1);
+}
+
+void
+setNS(cp)
+ Char *cp;
+{
+ set(cp, Strsave(STRNULL));
+}
+
+void
+shift(v)
+ register Char **v;
+{
+ register struct varent *argv;
+ register Char *name;
+
+ v++;
+ name = *v;
+ if (name == 0)
+ name = STRargv;
+ else
+ (void) strip(name);
+ argv = adrof(name);
+ if (argv == 0)
+ udvar(name);
+ if (argv->vec[0] == 0)
+ stderror(ERR_NAME | ERR_NOMORE);
+ lshift(argv->vec, 1);
+}
+
+static void
+exportpath(val)
+ Char **val;
+{
+ Char exppath[BUFSIZ];
+
+ exppath[0] = 0;
+ if (val)
+ while (*val) {
+ if (Strlen(*val) + Strlen(exppath) + 2 > BUFSIZ) {
+ xprintf("Warning: ridiculously long PATH truncated\n");
+ break;
+ }
+ (void) Strcat(exppath, *val++);
+ if (*val == 0 || eq(*val, STRRparen))
+ break;
+ (void) Strcat(exppath, STRcolon);
+ }
+ Setenv(STRPATH, exppath);
+}
+
+#ifndef lint
+ /*
+ * Lint thinks these have null effect
+ */
+ /* macros to do single rotations on node p */
+#define rright(p) (\
+ t = (p)->v_left,\
+ (t)->v_parent = (p)->v_parent,\
+ ((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\
+ (t->v_right = (p))->v_parent = t,\
+ (p) = t)
+#define rleft(p) (\
+ t = (p)->v_right,\
+ (t)->v_parent = (p)->v_parent,\
+ ((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\
+ (t->v_left = (p))->v_parent = t,\
+ (p) = t)
+#else
+struct varent *
+rleft(p)
+ struct varent *p;
+{
+ return (p);
+}
+struct varent *
+rright(p)
+ struct varent *p;
+{
+ return (p);
+}
+
+#endif /* ! lint */
+
+
+/*
+ * Rebalance a tree, starting at p and up.
+ * F == 0 means we've come from p's left child.
+ * D == 1 means we've just done a delete, otherwise an insert.
+ */
+static void
+balance(p, f, d)
+ register struct varent *p;
+ register int f, d;
+{
+ register struct varent *pp;
+
+#ifndef lint
+ register struct varent *t; /* used by the rotate macros */
+
+#endif
+ register ff;
+
+ /*
+ * Ok, from here on, p is the node we're operating on; pp is it's parent; f
+ * is the branch of p from which we have come; ff is the branch of pp which
+ * is p.
+ */
+ for (; pp = p->v_parent; p = pp, f = ff) {
+ ff = pp->v_right == p;
+ if (f ^ d) { /* right heavy */
+ switch (p->v_bal) {
+ case -1: /* was left heavy */
+ p->v_bal = 0;
+ break;
+ case 0: /* was balanced */
+ p->v_bal = 1;
+ break;
+ case 1: /* was already right heavy */
+ switch (p->v_right->v_bal) {
+ case 1: /* sigle rotate */
+ pp->v_link[ff] = rleft(p);
+ p->v_left->v_bal = 0;
+ p->v_bal = 0;
+ break;
+ case 0: /* single rotate */
+ pp->v_link[ff] = rleft(p);
+ p->v_left->v_bal = 1;
+ p->v_bal = -1;
+ break;
+ case -1: /* double rotate */
+ (void) rright(p->v_right);
+ pp->v_link[ff] = rleft(p);
+ p->v_left->v_bal =
+ p->v_bal < 1 ? 0 : -1;
+ p->v_right->v_bal =
+ p->v_bal > -1 ? 0 : 1;
+ p->v_bal = 0;
+ break;
+ }
+ break;
+ }
+ }
+ else { /* left heavy */
+ switch (p->v_bal) {
+ case 1: /* was right heavy */
+ p->v_bal = 0;
+ break;
+ case 0: /* was balanced */
+ p->v_bal = -1;
+ break;
+ case -1: /* was already left heavy */
+ switch (p->v_left->v_bal) {
+ case -1: /* single rotate */
+ pp->v_link[ff] = rright(p);
+ p->v_right->v_bal = 0;
+ p->v_bal = 0;
+ break;
+ case 0: /* signle rotate */
+ pp->v_link[ff] = rright(p);
+ p->v_right->v_bal = -1;
+ p->v_bal = 1;
+ break;
+ case 1: /* double rotate */
+ (void) rleft(p->v_left);
+ pp->v_link[ff] = rright(p);
+ p->v_left->v_bal =
+ p->v_bal < 1 ? 0 : -1;
+ p->v_right->v_bal =
+ p->v_bal > -1 ? 0 : 1;
+ p->v_bal = 0;
+ break;
+ }
+ break;
+ }
+ }
+ /*
+ * If from insert, then we terminate when p is balanced. If from
+ * delete, then we terminate when p is unbalanced.
+ */
+ if ((p->v_bal == 0) ^ d)
+ break;
+ }
+}
+
+void
+plist(p)
+ register struct varent *p;
+{
+ register struct varent *c;
+ register len;
+
+ if (setintr)
+ (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT));
+
+ for (;;) {
+ while (p->v_left)
+ p = p->v_left;
+x:
+ if (p->v_parent == 0) /* is it the header? */
+ return;
+ len = blklen(p->vec);
+ xprintf(short2str(p->v_name));
+ xputchar('\t');
+ if (len != 1)
+ xputchar('(');
+ blkpr(p->vec);
+ if (len != 1)
+ xputchar(')');
+ xputchar('\n');
+ if (p->v_right) {
+ p = p->v_right;
+ continue;
+ }
+ do {
+ c = p;
+ p = p->v_parent;
+ } while (p->v_right == c);
+ goto x;
+ }
+}
diff --git a/bin/csh/str.c b/bin/csh/str.c
new file mode 100644
index 000000000000..f7ddcec60532
--- /dev/null
+++ b/bin/csh/str.c
@@ -0,0 +1,416 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)str.c 5.5 (Berkeley) 6/27/91";
+#endif /* not lint */
+
+/*
+ * tc.str.c: Short string package
+ * This has been a lesson of how to write buggy code!
+ */
+#ifdef SHORT_STRINGS
+
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+Char **
+blk2short(src)
+ register char **src;
+{
+ size_t n;
+ register Char **sdst, **dst;
+
+ /*
+ * Count
+ */
+ for (n = 0; src[n] != NULL; n++);
+ sdst = dst = (Char **) xmalloc((size_t) ((n + 1) * sizeof(Char *)));
+
+ for (; *src != NULL; src++)
+ *dst++ = SAVE(*src);
+ *dst = NULL;
+ return (sdst);
+}
+
+char **
+short2blk(src)
+ register Char **src;
+{
+ size_t n;
+ register char **sdst, **dst;
+
+ /*
+ * Count
+ */
+ for (n = 0; src[n] != NULL; n++);
+ sdst = dst = (char **) xmalloc((size_t) ((n + 1) * sizeof(char *)));
+
+ for (; *src != NULL; src++)
+ *dst++ = strsave(short2str(*src));
+ *dst = NULL;
+ return (sdst);
+}
+
+#define MALLOC_INCR 1024
+Char *
+str2short(src)
+ register char *src;
+{
+ static Char *sdst;
+ static size_t dstsize = 0;
+ register Char *dst, *edst;
+
+ if (src == NULL)
+ return (NULL);
+
+ if (sdst == (NULL)) {
+ dstsize = MALLOC_INCR;
+ sdst = (Char *) xmalloc((size_t) dstsize * sizeof(Char));
+ }
+
+ dst = sdst;
+ edst = &dst[dstsize];
+ while (*src) {
+ *dst++ = (Char) ((unsigned char) *src++);
+ if (dst == edst) {
+ dstsize += MALLOC_INCR;
+ sdst = (Char *) xrealloc((ptr_t) sdst,
+ (size_t) dstsize * sizeof(Char));
+ edst = &sdst[dstsize];
+ dst = &edst[-MALLOC_INCR];
+ }
+ }
+ *dst = 0;
+ return (sdst);
+}
+
+char *
+short2qstr(src)
+ register Char *src;
+{
+ static char *sdst = NULL;
+ static size_t dstsize = 0;
+ register char *dst, *edst;
+
+ if (src == NULL)
+ return (NULL);
+
+ if (sdst == NULL) {
+ dstsize = MALLOC_INCR;
+ sdst = (char *) xmalloc((size_t) dstsize * sizeof(char));
+ }
+ dst = sdst;
+ edst = &dst[dstsize];
+ while (*src) {
+ if (*src & QUOTE) {
+ *dst++ = '\\';
+ if (dst == edst) {
+ dstsize += MALLOC_INCR;
+ sdst = (char *) xrealloc((ptr_t) sdst,
+ (size_t) dstsize * sizeof(char));
+ edst = &sdst[dstsize];
+ dst = &edst[-MALLOC_INCR];
+ }
+ }
+ *dst++ = (char) *src++;
+ if (dst == edst) {
+ dstsize += MALLOC_INCR;
+ sdst = (char *) xrealloc((ptr_t) sdst,
+ (size_t) dstsize * sizeof(char));
+ edst = &sdst[dstsize];
+ dst = &edst[-MALLOC_INCR];
+ }
+ }
+ *dst = 0;
+ return (sdst);
+}
+char *
+short2str(src)
+ register Char *src;
+{
+ static char *sdst = NULL;
+ static size_t dstsize = 0;
+ register char *dst, *edst;
+
+ if (src == NULL)
+ return (NULL);
+
+ if (sdst == NULL) {
+ dstsize = MALLOC_INCR;
+ sdst = (char *) xmalloc((size_t) dstsize * sizeof(char));
+ }
+ dst = sdst;
+ edst = &dst[dstsize];
+ while (*src) {
+ *dst++ = (char) *src++;
+ if (dst == edst) {
+ dstsize += MALLOC_INCR;
+ sdst = (char *) xrealloc((ptr_t) sdst,
+ (size_t) dstsize * sizeof(char));
+ edst = &sdst[dstsize];
+ dst = &edst[-MALLOC_INCR];
+ }
+ }
+ *dst = 0;
+ return (sdst);
+}
+
+Char *
+s_strcpy(dst, src)
+ register Char *dst, *src;
+{
+ register Char *sdst;
+
+ sdst = dst;
+ while (*dst++ = *src++);
+ return (sdst);
+}
+
+Char *
+s_strncpy(dst, src, n)
+ register Char *dst, *src;
+ register size_t n;
+{
+ register Char *sdst;
+
+ if (n == 0)
+ return(dst);
+
+ sdst = dst;
+ do
+ if ((*dst++ = *src++) == '\0') {
+ while (--n != 0)
+ *dst++ = '\0';
+ return(sdst);
+ }
+ while (--n != 0);
+ return (sdst);
+}
+
+Char *
+s_strcat(dst, src)
+ register Char *dst, *src;
+{
+ register short *sdst;
+
+ sdst = dst;
+ while (*dst++);
+ --dst;
+ while (*dst++ = *src++);
+ return (sdst);
+}
+
+#ifdef NOTUSED
+Char *
+s_strncat(dst, src, n)
+ register Char *dst, *src;
+ register size_t n;
+{
+ register Char *sdst;
+
+ if (n == 0)
+ return (dst);
+
+ sdst = dst;
+
+ while (*dst++);
+ --dst;
+
+ do
+ if ((*dst++ = *src++) == '\0')
+ return(sdst);
+ while (--n != 0);
+
+ *dst = '\0';
+ return (sdst);
+}
+
+#endif
+
+Char *
+s_strchr(str, ch)
+ register Char *str;
+ int ch;
+{
+ do
+ if (*str == ch)
+ return (str);
+ while (*str++);
+ return (NULL);
+}
+
+Char *
+s_strrchr(str, ch)
+ register Char *str;
+ int ch;
+{
+ register Char *rstr;
+
+ rstr = NULL;
+ do
+ if (*str == ch)
+ rstr = str;
+ while (*str++);
+ return (rstr);
+}
+
+size_t
+s_strlen(str)
+ register Char *str;
+{
+ register size_t n;
+
+ for (n = 0; *str++; n++);
+ return (n);
+}
+
+int
+s_strcmp(str1, str2)
+ register Char *str1, *str2;
+{
+ for (; *str1 && *str1 == *str2; str1++, str2++);
+ /*
+ * The following case analysis is necessary so that characters which look
+ * negative collate low against normal characters but high against the
+ * end-of-string NUL.
+ */
+ if (*str1 == '\0' && *str2 == '\0')
+ return (0);
+ else if (*str1 == '\0')
+ return (-1);
+ else if (*str2 == '\0')
+ return (1);
+ else
+ return (*str1 - *str2);
+}
+
+int
+s_strncmp(str1, str2, n)
+ register Char *str1, *str2;
+ register size_t n;
+{
+ if (n == 0)
+ return (0);
+ do {
+ if (*str1 == '\0' || *str1 != *str2)
+ break;
+ str1++, str2++;
+ } while (--n != 0);
+ /*
+ * The following case analysis is necessary so that characters which look
+ * negative collate low against normal characters but high against the
+ * end-of-string NUL.
+ */
+ if (*str1 == '\0' && *str2 == '\0')
+ return (0);
+ else if (*str1 == '\0')
+ return (-1);
+ else if (*str2 == '\0')
+ return (1);
+ else
+ return (*str1 - *str2);
+ return(0);
+}
+
+Char *
+s_strsave(s)
+ register Char *s;
+{
+ Char *n;
+ register Char *p;
+
+ if (s == 0)
+ s = STRNULL;
+ for (p = s; *p++;);
+ n = p = (Char *) xmalloc((size_t) ((p - s) * sizeof(Char)));
+ while (*p++ = *s++);
+ return (n);
+}
+
+Char *
+s_strspl(cp, dp)
+ Char *cp, *dp;
+{
+ Char *ep;
+ register Char *p, *q;
+
+ if (!cp)
+ cp = STRNULL;
+ if (!dp)
+ dp = STRNULL;
+ for (p = cp; *p++;);
+ for (q = dp; *q++;);
+ ep = (Char *) xmalloc((size_t)
+ (((p - cp) + (q - dp) - 1) * sizeof(Char)));
+ for (p = ep, q = cp; *p++ = *q++;);
+ for (p--, q = dp; *p++ = *q++;);
+ return (ep);
+}
+
+Char *
+s_strend(cp)
+ register Char *cp;
+{
+ if (!cp)
+ return (cp);
+ while (*cp)
+ cp++;
+ return (cp);
+}
+
+#ifdef NOTUSED
+Char *
+s_strstr(s, t)
+ register Char *s, *t;
+{
+ do {
+ register Char *ss = s;
+ register Char *tt = t;
+
+ do
+ if (*tt == '\0')
+ return (s);
+ while (*ss++ == *tt++);
+ } while (*s++ != '\0');
+ return (NULL);
+}
+#endif
+
+#endif /* SHORT_STRINGS */
diff --git a/bin/csh/time.c b/bin/csh/time.c
new file mode 100644
index 000000000000..b36cda8ce662
--- /dev/null
+++ b/bin/csh/time.c
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)time.c 5.14 (Berkeley) 6/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include "csh.h"
+#include "extern.h"
+
+/*
+ * C Shell - routines handling process timing and niceing
+ */
+static void pdeltat __P((struct timeval *, struct timeval *));
+
+void
+settimes()
+{
+ struct rusage ruch;
+
+ (void) gettimeofday(&time0, NULL);
+ (void) getrusage(RUSAGE_SELF, &ru0);
+ (void) getrusage(RUSAGE_CHILDREN, &ruch);
+ ruadd(&ru0, &ruch);
+}
+
+/*
+ * dotime is only called if it is truly a builtin function and not a
+ * prefix to another command
+ */
+void
+dotime()
+{
+ struct timeval timedol;
+ struct rusage ru1, ruch;
+
+ (void) getrusage(RUSAGE_SELF, &ru1);
+ (void) getrusage(RUSAGE_CHILDREN, &ruch);
+ ruadd(&ru1, &ruch);
+ (void) gettimeofday(&timedol, NULL);
+ prusage(&ru0, &ru1, &timedol, &time0);
+}
+
+/*
+ * donice is only called when it on the line by itself or with a +- value
+ */
+void
+donice(v)
+ register Char **v;
+{
+ register Char *cp;
+ int nval = 0;
+
+ v++, cp = *v++;
+ if (cp == 0)
+ nval = 4;
+ else if (*v == 0 && any("+-", cp[0]))
+ nval = getn(cp);
+ (void) setpriority(PRIO_PROCESS, 0, nval);
+}
+
+void
+ruadd(ru, ru2)
+ register struct rusage *ru, *ru2;
+{
+ tvadd(&ru->ru_utime, &ru2->ru_utime);
+ tvadd(&ru->ru_stime, &ru2->ru_stime);
+ if (ru2->ru_maxrss > ru->ru_maxrss)
+ ru->ru_maxrss = ru2->ru_maxrss;
+
+ ru->ru_ixrss += ru2->ru_ixrss;
+ ru->ru_idrss += ru2->ru_idrss;
+ ru->ru_isrss += ru2->ru_isrss;
+ ru->ru_minflt += ru2->ru_minflt;
+ ru->ru_majflt += ru2->ru_majflt;
+ ru->ru_nswap += ru2->ru_nswap;
+ ru->ru_inblock += ru2->ru_inblock;
+ ru->ru_oublock += ru2->ru_oublock;
+ ru->ru_msgsnd += ru2->ru_msgsnd;
+ ru->ru_msgrcv += ru2->ru_msgrcv;
+ ru->ru_nsignals += ru2->ru_nsignals;
+ ru->ru_nvcsw += ru2->ru_nvcsw;
+ ru->ru_nivcsw += ru2->ru_nivcsw;
+}
+
+void
+prusage(r0, r1, e, b)
+ register struct rusage *r0, *r1;
+ struct timeval *e, *b;
+{
+ register time_t t =
+ (r1->ru_utime.tv_sec - r0->ru_utime.tv_sec) * 100 +
+ (r1->ru_utime.tv_usec - r0->ru_utime.tv_usec) / 10000 +
+ (r1->ru_stime.tv_sec - r0->ru_stime.tv_sec) * 100 +
+ (r1->ru_stime.tv_usec - r0->ru_stime.tv_usec) / 10000;
+ register char *cp;
+ register long i;
+ register struct varent *vp = adrof(STRtime);
+
+ int ms =
+ (e->tv_sec - b->tv_sec) * 100 + (e->tv_usec - b->tv_usec) / 10000;
+
+ cp = "%Uu %Ss %E %P %X+%Dk %I+%Oio %Fpf+%Ww";
+
+ if (vp && vp->vec[0] && vp->vec[1])
+ cp = short2str(vp->vec[1]);
+
+ for (; *cp; cp++)
+ if (*cp != '%')
+ xputchar(*cp);
+ else if (cp[1])
+ switch (*++cp) {
+
+ case 'U': /* user CPU time used */
+ pdeltat(&r1->ru_utime, &r0->ru_utime);
+ break;
+
+ case 'S': /* system CPU time used */
+ pdeltat(&r1->ru_stime, &r0->ru_stime);
+ break;
+
+ case 'E': /* elapsed (wall-clock) time */
+ pcsecs((long) ms);
+ break;
+
+ case 'P': /* percent time spent running */
+ /* check if it did not run at all */
+ i = (ms == 0) ? 0 : (t * 1000 / ms);
+ xprintf("%ld.%01ld%%", i / 10, i % 10); /* nn.n% */
+ break;
+
+ case 'W': /* number of swaps */
+ i = r1->ru_nswap - r0->ru_nswap;
+ xprintf("%ld", i);
+ break;
+
+ case 'X': /* (average) shared text size */
+ xprintf("%ld", t == 0 ? 0L : (r1->ru_ixrss - r0->ru_ixrss) / t);
+ break;
+
+ case 'D': /* (average) unshared data size */
+ xprintf("%ld", t == 0 ? 0L :
+ (r1->ru_idrss + r1->ru_isrss -
+ (r0->ru_idrss + r0->ru_isrss)) / t);
+ break;
+
+ case 'K': /* (average) total data memory used */
+ xprintf("%ld", t == 0 ? 0L :
+ ((r1->ru_ixrss + r1->ru_isrss + r1->ru_idrss) -
+ (r0->ru_ixrss + r0->ru_idrss + r0->ru_isrss)) / t);
+ break;
+
+ case 'M': /* max. Resident Set Size */
+ xprintf("%ld", r1->ru_maxrss / 2L);
+ break;
+
+ case 'F': /* page faults */
+ xprintf("%ld", r1->ru_majflt - r0->ru_majflt);
+ break;
+
+ case 'R': /* page reclaims */
+ xprintf("%ld", r1->ru_minflt - r0->ru_minflt);
+ break;
+
+ case 'I': /* FS blocks in */
+ xprintf("%ld", r1->ru_inblock - r0->ru_inblock);
+ break;
+
+ case 'O': /* FS blocks out */
+ xprintf("%ld", r1->ru_oublock - r0->ru_oublock);
+ break;
+
+ case 'r': /* socket messages recieved */
+ xprintf("%ld", r1->ru_msgrcv - r0->ru_msgrcv);
+ break;
+
+ case 's': /* socket messages sent */
+ xprintf("%ld", r1->ru_msgsnd - r0->ru_msgsnd);
+ break;
+
+ case 'k': /* number of signals recieved */
+ xprintf("%ld", r1->ru_nsignals - r0->ru_nsignals);
+ break;
+
+ case 'w': /* num. voluntary context switches (waits) */
+ xprintf("%ld", r1->ru_nvcsw - r0->ru_nvcsw);
+ break;
+
+ case 'c': /* num. involuntary context switches */
+ xprintf("%ld", r1->ru_nivcsw - r0->ru_nivcsw);
+ break;
+ }
+ xputchar('\n');
+}
+
+static void
+pdeltat(t1, t0)
+ struct timeval *t1, *t0;
+{
+ struct timeval td;
+
+ tvsub(&td, t1, t0);
+ xprintf("%d.%01d", td.tv_sec, td.tv_usec / 100000);
+}
+
+void
+tvadd(tsum, t0)
+ struct timeval *tsum, *t0;
+{
+
+ tsum->tv_sec += t0->tv_sec;
+ tsum->tv_usec += t0->tv_usec;
+ if (tsum->tv_usec > 1000000)
+ tsum->tv_sec++, tsum->tv_usec -= 1000000;
+}
+
+void
+tvsub(tdiff, t1, t0)
+ struct timeval *tdiff, *t1, *t0;
+{
+
+ tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
+ tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
+ if (tdiff->tv_usec < 0)
+ tdiff->tv_sec--, tdiff->tv_usec += 1000000;
+}