]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD csh(1) 04/3204/2
authorDavid van Moolenbroek <david@minix3.org>
Mon, 28 Sep 2015 11:36:43 +0000 (11:36 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Tue, 29 Sep 2015 18:15:52 +0000 (18:15 +0000)
Jobctl warning commented out.  Largely untested.

Change-Id: I4dffe23a2855a374628c820703b51591633aed64

41 files changed:
bin/Makefile
bin/csh/Makefile [new file with mode: 0644]
bin/csh/USD.doc/Makefile [new file with mode: 0644]
bin/csh/USD.doc/csh.1 [new file with mode: 0644]
bin/csh/USD.doc/csh.2 [new file with mode: 0644]
bin/csh/USD.doc/csh.3 [new file with mode: 0644]
bin/csh/USD.doc/csh.4 [new file with mode: 0644]
bin/csh/USD.doc/csh.ap [new file with mode: 0644]
bin/csh/USD.doc/csh.g [new file with mode: 0644]
bin/csh/USD.doc/tabs [new file with mode: 0644]
bin/csh/alloc.c [new file with mode: 0644]
bin/csh/char.c [new file with mode: 0644]
bin/csh/char.h [new file with mode: 0644]
bin/csh/const.c [new file with mode: 0644]
bin/csh/csh.1 [new file with mode: 0644]
bin/csh/csh.c [new file with mode: 0644]
bin/csh/csh.h [new file with mode: 0644]
bin/csh/dir.c [new file with mode: 0644]
bin/csh/dir.h [new file with mode: 0644]
bin/csh/dol.c [new file with mode: 0644]
bin/csh/err.c [new file with mode: 0644]
bin/csh/exec.c [new file with mode: 0644]
bin/csh/exp.c [new file with mode: 0644]
bin/csh/extern.h [new file with mode: 0644]
bin/csh/file.c [new file with mode: 0644]
bin/csh/func.c [new file with mode: 0644]
bin/csh/glob.c [new file with mode: 0644]
bin/csh/hist.c [new file with mode: 0644]
bin/csh/init.c [new file with mode: 0644]
bin/csh/lex.c [new file with mode: 0644]
bin/csh/misc.c [new file with mode: 0644]
bin/csh/parse.c [new file with mode: 0644]
bin/csh/pathnames.h [new file with mode: 0644]
bin/csh/proc.c [new file with mode: 0644]
bin/csh/proc.h [new file with mode: 0644]
bin/csh/sem.c [new file with mode: 0644]
bin/csh/set.c [new file with mode: 0644]
bin/csh/str.c [new file with mode: 0644]
bin/csh/time.c [new file with mode: 0644]
distrib/sets/lists/minix/mi
etc/mtree/NetBSD.dist.base

index ce4ea6c79a46153aa939642905cd925ee67cced4..42421dc42b0ff659fa2ed53ef41fbe47fc8a5ff4 100644 (file)
@@ -1,7 +1,7 @@
 #      $NetBSD: Makefile,v 1.22 2007/12/31 15:31:24 ad Exp $
 #      @(#)Makefile    8.1 (Berkeley) 5/31/93
 
-SUBDIR=        cat chmod cp date dd df domainname echo ed expr hostname \
+SUBDIR=        cat chmod cp csh date dd df domainname echo ed expr hostname \
        kill ksh ln ls mkdir mv pax pwd rcp rcmd rm rmdir sh \
        sleep stty sync test
 
diff --git a/bin/csh/Makefile b/bin/csh/Makefile
new file mode 100644 (file)
index 0000000..493a87b
--- /dev/null
@@ -0,0 +1,76 @@
+#      $NetBSD: Makefile,v 1.39 2013/07/16 17:47:43 christos Exp $
+#      @(#)Makefile    8.1 (Berkeley) 5/31/93
+#
+# C Shell with process control; VM/UNIX VAX Makefile
+# Bill Joy UC Berkeley; Jim Kulp IIASA, Austria
+#
+# To profile, put -DPROF in DFLAGS and -pg in COPTS, and recompile.
+
+.include <bsd.own.mk>
+WARNS=6
+
+PROG=  csh
+DFLAGS=-DBUILTIN -DFILEC -DNLS -DSHORT_STRINGS
+# - Editor history not always aligned with shell history,
+#   should implement internally
+# - Does not handle escaped prompts.
+# - Does not do completion
+.ifndef SMALLPROG
+DFLAGS+=-DEDIT
+.endif
+CPPFLAGS+=-I${.CURDIR} -I. ${DFLAGS}
+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 printf.c proc.c \
+       sem.c set.c str.c time.c
+.PATH: ${NETBSDSRCDIR}/usr.bin/printf
+
+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
+
+DPSRCS+=       errnum.h const.h
+CLEANFILES+=   errnum.h const.h
+
+errnum.h: err.c
+       ${_MKTARGET_CREATE}
+       rm -f ${.TARGET}
+       (\
+       echo '/* Do not edit this file, make creates it. */' ;\
+       echo '#ifndef _h_sh_errnum' ;\
+       echo '#define _h_sh_errnum' ;\
+       egrep 'ERR_' ${.ALLSRC} | egrep '^#define' ;\
+       echo '#endif /* _h_sh_errnum */' ;\
+       ) > ${.TARGET}
+
+const.c: errnum.h
+const.h: const.c
+       ${_MKTARGET_CREATE}
+       rm -f ${.TARGET}
+       echo '/* Do not edit this file, make creates it. */' > ${.TARGET}
+       ${CC} -E ${CPPFLAGS} ${.ALLSRC} | egrep 'Char STR' | \
+           ${TOOL_SED} -e 's/Char \([a-zA-Z0-9_]*\)\(.*\)/extern Char \1[];/' | \
+           sort >> ${.TARGET}
+
+.if make(install)
+SUBDIR+=USD.doc
+.endif
+
+# XXX Only GCC 4.1 problem
+.if defined(HAVE_GCC) && ${HAVE_GCC} == 4 && ${MACHINE_ARCH} == "vax"
+COPTS.parse.c+=        -O0
+.endif
+COPTS.err.c = -Wno-format-nonliteral
+COPTS.printf.c = -Wno-format-nonliteral
+COPTS.proc.c = -Wno-format-nonliteral
+
+.if !empty(DFLAGS:M*EDIT)
+LDADD+=-ledit -lterminfo -lutil
+DPADD+=${LIBEDIT} ${LIBUTIL}
+.else
+LDADD+=-lutil
+DPADD+=${LIBUTIL}
+.endif
+
+.include <bsd.prog.mk>
+.include <bsd.subdir.mk>
diff --git a/bin/csh/USD.doc/Makefile b/bin/csh/USD.doc/Makefile
new file mode 100644 (file)
index 0000000..fd3fff5
--- /dev/null
@@ -0,0 +1,12 @@
+#      $NetBSD: Makefile,v 1.7 2007/10/18 18:26:31 tls Exp $
+#      @(#)Makefile    8.1 (Berkeley) 8/14/93
+
+DIR=   usd/04.csh
+SRCS=  tabs csh.1 csh.2 csh.3 csh.4 csh.ap csh.g
+MACROS= -ms
+
+paper.ps: ${SRCS}
+       ${TOOL_SOELIM} -I${.CURDIR} ${.ALLSRC} | \
+           ${TOOL_ROFF_PS} ${MACROS} > ${.TARGET}                  
+
+.include <bsd.doc.mk>
diff --git a/bin/csh/USD.doc/csh.1 b/bin/csh/USD.doc/csh.1
new file mode 100644 (file)
index 0000000..9f44af7
--- /dev/null
@@ -0,0 +1,1010 @@
+.\"    $NetBSD: csh.1,v 1.5 2003/08/07 09:05:08 agc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\"    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. 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       8.1 (Berkeley) 6/8/93
+.\"
+.EH 'USD:4-%''An Introduction to the C shell'
+.OH 'An Introduction to the C shell''USD:4-%'
+.\".RP
+.TL
+An Introduction to the C shell
+.AU
+William Joy
+(revised for 4.3BSD by Mark Seiden)
+.AI
+Computer Science Division
+.br
+Department of Electrical Engineering and Computer Science
+.br
+University of California, Berkeley
+.br
+Berkeley, California 94720
+.AB
+.I Csh
+is a new command language interpreter for
+.UX
+systems.
+It incorporates good features of other shells and a
+.I history
+mechanism similar to the
+.I redo
+of \s-2INTERLISP\s0.
+While incorporating many features of other shells which make
+writing shell programs (shell scripts) easier,
+most of the features unique to
+.I csh
+are designed more for the interactive \s-2UNIX\s0 user.
+.PP
+\s-2UNIX\s0
+users who have read a general introduction to the system
+will find a valuable basic explanation of the shell here.
+Simple terminal interaction with
+.I csh
+is possible after reading just the first section of this document.
+The second section describes the shell's capabilities which you can
+explore after you have begun to become acquainted with the shell.
+Later sections introduce features which are useful, but not necessary
+for all users of the shell.
+.PP
+Additional information includes an appendix listing special characters of the shell
+and a glossary of terms and commands introduced in this manual.
+.AE
+.SH
+.if n .ND
+Introduction
+.PP
+A
+.I shell
+is a command language interpreter.
+.I Csh
+is the name of one particular command interpreter on
+\s-2UNIX\s0.
+The primary purpose of
+.I csh
+is to translate command lines typed at a terminal into
+system actions, such as invocation of other programs.
+.I Csh
+is a user program just like any you might write.
+Hopefully,
+.I csh
+will be a very useful program for you
+in interacting with the \s-2UNIX\s0 system.
+.PP
+In addition to this document, you will want to refer to a copy
+of the \s-2UNIX\s0 User Reference Manual.
+The
+.I csh
+documentation in section 1 of the manual provides a full description of all
+features of the shell and is the definitive reference for questions
+about the shell.
+.PP
+Many words in this document are shown in
+.I italics.
+These are important words;
+names of commands, and words which have special meaning in discussing
+the shell and \s-2UNIX\s0.
+Many of the words are defined in a glossary at the end of this document.
+If you don't know what is meant by a word, you should look
+for it in the glossary.
+.SH
+Acknowledgements
+.PP
+Numerous people have provided good input about previous versions
+of
+.I csh
+and aided in its debugging and in the debugging of its documentation.
+I would especially like to thank Michael Ubell
+who made the crucial observation that history commands could be
+done well over the word structure of input text, and implemented
+a prototype history mechanism in an older version of the shell.
+Eric Allman has also provided a large number of useful comments on the
+shell, helping to unify those concepts which are present and to identify
+and eliminate useless and marginally useful features.
+Mike O'Brien suggested the pathname hashing
+mechanism which speeds command execution.
+Jim Kulp added the job control and directory stack primitives and
+added their documentation to this introduction.
+.br
+.bp
+.NH
+Terminal usage of the shell
+.NH 2
+The basic notion of commands
+.PP
+A
+.I shell
+in
+\s-2UNIX\s0
+acts mostly as a medium through which other
+.I programs
+are invoked.
+While it has a set of
+.I builtin
+functions which it performs directly,
+most commands cause execution of programs that are, in fact,
+external to the shell.
+The shell is thus distinguished from the command interpreters of other
+systems both by the fact that it is just a user program, and by the fact
+that it is used almost exclusively as a mechanism for invoking other programs.
+.PP
+.I Commands
+in the \s-2UNIX\s0 system consist of a list of strings or
+.I words
+interpreted as a
+.I "command name"
+followed by
+.I arguments.
+Thus the command
+.DS
+mail bill
+.DE
+consists of two words.
+The first word
+.I mail
+names the command to be executed, in this case the
+mail program which sends messages to other users.
+The shell uses the name of the command in attempting to execute it for you.
+It will look in a number of
+.I directories
+for a file with the name
+.I mail
+which is expected to contain the mail program.
+.PP
+The rest of the words of the command are given as
+.I arguments
+to the command itself when it is executed.
+In this case we specified also the argument
+.I bill
+which is interpreted by the
+.I mail
+program to be the name of a user to whom mail is to be sent.
+In normal terminal usage we might use the
+.I mail
+command as follows.
+.DS
+% mail bill
+I have a question about the csh documentation.
+My document seems to be missing page 5.
+Does a page five exist?
+       Bill
+EOT
+%
+.DE
+.PP
+Here we typed a message to send to
+.I bill
+and ended this message with a ^D which sent an end-of-file to
+the mail program.
+(Here and throughout this document, the notation ``^\fIx\fR''
+is to be read ``control-\fIx\fR'' and represents the striking of the \fIx\fR
+key while the control key is held down.)
+The mail program
+then echoed the characters `EOT' and transmitted our message.
+The characters `% ' were printed before and after the mail command
+by the shell to indicate that input was needed.
+.PP
+After typing the `% ' prompt the shell was reading command input from
+our terminal.
+We typed a complete command `mail bill'.
+The shell then executed the
+.I mail
+program with argument
+.I bill
+and went dormant waiting for it to complete.
+The mail program then read input from our terminal until we signalled
+an end-of-file via typing a ^D after which the shell noticed
+that mail had completed
+and signaled us that it was ready to read from the terminal again by
+printing another `% ' prompt.
+.PP
+This is the essential pattern of all interaction with \s-2UNIX\s0
+through the shell.
+A complete command is typed at the terminal, the shell executes
+the command and when this execution completes, it prompts for a new command.
+If you run the editor for an hour, the shell will patiently wait for
+you to finish editing and obediently prompt you again whenever you finish
+editing.
+.PP
+An example of a useful command you can execute now is the
+.I tset
+command, which sets the default
+.I erase
+and
+.I kill
+characters on your terminal \- the erase character erases the last
+character you typed and the kill character erases the entire line you
+have entered so far.
+By default, the erase character is the delete key (equivalent to `^?')
+and the kill character is `^U'.  Some people prefer to make the erase character
+the backspace key (equivalent to `^H').
+You can make this be true by typing
+.DS
+tset \-e
+.DE
+which tells the program
+.I tset
+to set the erase character to tset's default setting for this character
+(a backspace).
+.NH 2
+Flag arguments
+.PP
+A useful notion in \s-2UNIX\s0 is that of a
+.I flag
+argument.
+While many arguments to commands specify file names or user names,
+some arguments rather specify an optional capability of the command
+which you wish to invoke.
+By convention, such arguments begin with the character `\-' (hyphen).
+Thus the command
+.DS
+ls
+.DE
+will produce a list of the files in the current
+.I "working directory" .
+The option
+.I \-s
+is the size option, and
+.DS
+ls \-s
+.DE
+causes
+.I ls
+to also give, for each file the size of the file in blocks of 512
+characters.
+The manual section for each command in the \s-2UNIX\s0 reference manual
+gives the available options for each command.
+The
+.I ls
+command has a large number of useful and interesting options.
+Most other commands have either no options or only one or two options.
+It is hard to remember options of commands which are not used very
+frequently, so most \s-2UNIX\s0 utilities perform only one or two functions
+rather than having a large number of hard to remember options.
+.NH 2
+Output to files
+.PP
+Commands that normally read input or write output on the terminal
+can also be executed with this input and/or output done to
+a file.
+.PP
+Thus suppose we wish to save the current date in a file called `now'.
+The command
+.DS
+date
+.DE
+will print the current date on our terminal.
+This is because our terminal is the default
+.I "standard output"
+for the date command and the date command prints the date on its
+standard output.
+The shell lets us
+.I redirect
+the
+.I "standard output"
+of a command through a
+notation using the
+.I metacharacter
+`>' and the name of the file where output is to be placed.
+Thus the command
+.DS
+date > now
+.DE
+runs the
+.I date
+command such that its standard output is
+the file `now' rather than the terminal.
+Thus this command places the current date and time into the file `now'.
+It is important to know that the
+.I date
+command was unaware that its output was going to a file rather than
+to the terminal.
+The shell performed this
+.I redirection
+before the command began executing.
+.PP
+One other thing to note here is that the file `now'
+need not have existed before the
+.I date
+command was executed; the shell would have created the file if it did
+not exist.
+And if the file did exist?
+If it had existed previously these previous contents would have been discarded!
+A shell option
+.I noclobber
+exists to prevent this from happening accidentally;
+it is discussed in section 2.2.
+.PP
+The system normally keeps files which you create with `>' and all other files.
+Thus the default is for files to be permanent.  If you wish to create a file
+which will be removed automatically, you can begin its name with a `#'
+character, this `scratch' character denotes the fact that the file will
+be a scratch file.*
+.FS
+*Note that if your erase character is a `#', you will have to precede the
+`#' with a `\e'.  The fact that the `#' character is the old (pre-\s-2CRT\s0)
+standard erase character means that it seldom appears in a file name, and
+allows this convention to be used for scratch files.  If you are using a
+\s-2CRT\s0, your erase character should be a ^H, as we demonstrated
+in section 1.1 how this could be set up.
+.FE
+The system will remove such files after a couple of days,
+or sooner if file space becomes very tight.
+Thus, in running the
+.I date
+command above, we don't really want to save the output forever, so we
+would more likely do
+.DS
+date > #now
+.DE
+.NH 2
+Metacharacters in the shell
+.PP
+The shell has a large number of
+special characters (like `>')
+which indicate special functions.
+We say that these notations have
+.I syntactic
+and
+.I semantic
+meaning to the shell.
+In general, most characters which are neither letters nor digits
+have special meaning to the shell.
+We shall shortly learn a means of
+.I quotation
+which allows us to use
+.I metacharacters
+without the shell treating them in any special way.
+.PP
+Metacharacters normally have effect only when the shell is reading
+our input.
+We need not worry about placing shell metacharacters in a letter
+we are sending via
+.I mail,
+or when we are typing in text or data to some other program.
+Note that the shell is only reading input when it has prompted with
+`% ' (although we can type our input even before it prompts).
+.NH 2
+Input from files; pipelines
+.PP
+We learned above how to
+.I redirect
+the
+.I "standard output"
+of a command
+to a file.
+It is also possible to redirect the
+.I "standard input"
+of a command from a file.
+This is not often necessary since most commands will read from
+a file whose name is given as an argument.
+We can give the command
+.DS
+sort < data
+.DE
+to run the
+.I sort
+command with standard input, where the command normally
+reads its input, from the file
+`data'.
+We would more likely say
+.DS
+sort data
+.DE
+letting the
+.I sort
+command open the file
+`data'
+for input itself since this is less to type.
+.PP
+We should note that if we just typed
+.DS
+sort
+.DE
+then the sort program would sort lines from its
+.I "standard input."
+Since we did not
+.I redirect
+the standard input, it would sort lines as we typed them on the terminal
+until we typed a ^D to indicate an end-of-file.
+.PP
+A most useful capability is the ability to combine the standard output
+of one command with the standard input of another, i.e. to run the
+commands in a sequence known as a
+.I pipeline.
+For instance the command
+.DS
+ls \-s
+.DE
+normally produces a list of the files in our directory with the size
+of each in blocks of 512 characters.
+If we are interested in learning which of our files is largest we
+may wish to have this sorted by size rather than by name, which is
+the default way in which
+.I ls
+sorts.
+We could look at the many options of
+.I ls
+to see if there was an option to do this but would eventually discover
+that there is not.
+Instead we can use a couple of simple options of the
+.I sort
+command, combining it with
+.I ls
+to get what we want.
+.PP
+The
+.I \-n
+option of sort specifies a numeric sort rather than an alphabetic sort.
+Thus
+.DS
+ls \-s | sort \-n
+.DE
+specifies that the output of the
+.I ls
+command run with the option
+.I \-s
+is to be
+.I piped
+to the command
+.I sort
+run with the numeric sort option.
+This would give us a sorted list of our files by size, but with the
+smallest first.
+We could then use the
+.I \-r
+reverse sort option and the
+.I head
+command in combination with the previous command doing
+.DS
+ls \-s | sort \-n \-r | head \-5
+.DE
+Here we have taken a list of our files sorted alphabetically,
+each with the size in blocks.
+We have run this to the standard input of the
+.I sort
+command asking it to sort numerically in reverse order (largest first).
+This output has then been run into the command
+.I head
+which gives us the first few lines.
+In this case we have asked
+.I head
+for the first 5 lines.
+Thus this command gives us the names and sizes of our 5 largest files.
+.PP
+The notation introduced above is called the
+.I pipe
+mechanism.
+Commands separated by `\||\|' characters are connected together by the
+shell and the standard output of each is run into the standard input of the
+next.
+The leftmost command in a pipeline will normally take its standard
+input from the terminal and the rightmost will place its standard
+output on the terminal.
+Other examples of pipelines will be given later when we discuss the
+history mechanism;
+one important use of pipes which is illustrated there is in the
+routing of information to the line printer.
+.NH 2
+Filenames
+.PP
+Many commands to be executed will need the names of files as arguments.
+\s-2UNIX\s0
+.I pathnames
+consist of a number of
+.I components
+separated by `/'.
+Each component except the last names a directory in which the next
+component resides, in effect specifying the
+.I path
+of directories to follow to reach the file.
+Thus the pathname
+.DS
+/etc/motd
+.DE
+specifies a file in the directory
+`etc'
+which is a subdirectory of the
+.I root
+directory `/'.
+Within this directory the file named is `motd' which stands
+for `message of the day'.
+A
+.I pathname
+that begins with a slash is said to be an
+.I absolute
+pathname since it is specified from the absolute top of the entire
+directory hierarchy of the system (the
+.I root ).
+.I Pathnames
+which do not begin with `/' are interpreted as starting in the current
+.I "working directory" ,
+which is, by default, your
+.I home
+directory and can be changed dynamically by the
+.I cd
+change directory command.
+Such pathnames are said to be
+.I relative
+to the working directory since they are found by starting
+in the working directory and descending to lower levels of directories
+for each
+.I component
+of the pathname.  If the pathname contains no slashes at all then the
+file is contained in the working directory itself and the pathname is merely
+the name of the file in this directory.
+Absolute pathnames have no relation
+to the working directory.
+.PP
+Most filenames consist of a number of alphanumeric characters and
+`.'s (periods).
+In fact, all printing characters except `/' (slash) may appear in filenames.
+It is inconvenient to have most non-alphabetic characters in filenames
+because many of these have special meaning to the shell.
+The character `.' (period) is not a shell-metacharacter and is often used
+to separate the
+.I extension
+of a file name from the base of the name.
+Thus
+.DS
+prog.c prog.o prog.errs prog.output
+.DE
+are four related files.
+They share a
+.I base
+portion of a name
+(a base portion being that part of the name that is left when a trailing
+`.' and following characters which are not `.' are stripped off).
+The file
+`prog.c'
+might be the source for a C program,
+the file `prog.o' the corresponding object file,
+the file
+`prog.errs' the errors resulting from a compilation of the program
+and the file
+`prog.output' the output of a run of the program.
+.PP
+If we wished to refer to all four of these files in a command, we could
+use the notation
+.DS
+prog.*
+.DE
+This expression is expanded by the shell, before the command to which it is
+an argument is executed, into a list of names which begin with `prog.'.
+The character `*' here matches any sequence (including the empty sequence)
+of characters in a file name.
+The names which match are alphabetically sorted and placed in the
+.I "argument list"
+of the command.
+Thus the command
+.DS
+echo prog.*
+.DE
+will echo the names
+.DS
+prog.c prog.errs prog.o prog.output
+.DE
+Note that the names are in sorted order here, and a different
+order than we listed them above.
+The
+.I echo
+command receives four words as arguments, even though we only typed
+one word as as argument directly.
+The four words were generated by
+.I "filename expansion"
+of the one input word.
+.PP
+Other notations for
+.I "filename expansion"
+are also available.
+The character `?' matches any single character in a filename.
+Thus
+.DS
+echo ? \|?? \|???
+.DE
+will echo a line of filenames; first those with one character names,
+then those with two character names, and finally those with three
+character names.
+The names of each length will be independently sorted.
+.PP
+Another mechanism consists of a sequence of characters between `[' and `]'.
+This metasequence matches any single character from the enclosed set.
+Thus
+.DS
+prog.[co]
+.DE
+will match
+.DS
+prog.c prog.o
+.DE
+in the example above.
+We can also place two characters around a `\-' in this notation to denote
+a range.
+Thus
+.DS
+chap.[1\-5]
+.DE
+might match files
+.DS
+chap.1 chap.2 chap.3 chap.4 chap.5
+.DE
+if they existed.
+This is shorthand for
+.DS
+chap.[12345]
+.DE
+and otherwise equivalent.
+.PP
+An important point to note is that if a list of argument words to
+a command (an
+.I "argument list)"
+contains filename expansion syntax, and if this filename expansion syntax
+fails to match any existing file names, then the shell considers this
+to be an error and prints a diagnostic
+.DS
+No match.
+.DE
+and does not execute the command.
+.PP
+Another very important point is that files with the character `.' at the
+beginning are treated specially.
+Neither `*' or `?' or the `[' `]' mechanism will match it.
+This prevents accidental matching of the filenames `.' and `..'
+in the working directory which have special meaning to the system,
+as well as other files such as
+.I \&.cshrc
+which are not normally
+visible.
+We will discuss the special role of the file
+.I \&.cshrc
+later.
+.PP
+Another filename expansion mechanism gives access to the pathname of
+the
+.I home
+directory of other users.
+This notation consists of the character `~' (tilde) followed by another user's
+login name.
+For instance the word `~bill' would map to the pathname `/usr/bill'
+if the home directory for `bill' was `/usr/bill'.
+Since, on large systems, users may have login directories scattered over
+many different disk volumes with different prefix directory names,
+this notation provides a convenient way of accessing the files
+of other users.
+.PP
+A special case of this notation consists of a `~' alone, e.g. `~/mbox'.
+This notation is expanded by the shell into the file `mbox' in your
+.I home
+directory, i.e. into `/usr/bill/mbox' for me on Ernie Co-vax, the UCB
+Computer Science Department VAX machine, where this document was prepared.
+This can be very useful if you have used
+.I cd
+to change to another directory and have found a file you wish to
+copy using
+.I cp.
+If I give the command
+.DS
+cp thatfile ~
+.DE
+the shell will expand this command to
+.DS
+cp thatfile /usr/bill
+.DE
+since my home directory is /usr/bill.
+.PP
+There also exists a mechanism using the characters `{' and `}' for
+abbreviating a set of words which have common parts but cannot
+be abbreviated by the above mechanisms because they are not files,
+are the names of files which do not yet exist,
+are not thus conveniently described.
+This mechanism will be described much later,
+in section 4.2,
+as it is used less frequently.
+.NH 2
+Quotation
+.PP
+We have already seen a number of metacharacters used by the shell.
+These metacharacters pose a problem in that we cannot use them directly
+as parts of words.
+Thus the command
+.DS
+echo *
+.DE
+will not echo the character `*'.
+It will either echo an sorted list of filenames in the
+current
+.I "working directory,"
+or print the message `No match' if there are
+no files in the working directory.
+.PP
+The recommended mechanism for placing characters which are neither numbers,
+digits, `/', `.' or `\-' in an argument word to a command is to enclose
+it with single quotation characters `\'', i.e.
+.DS
+echo \'*\'
+.DE
+There is one special character `!' which is used by the
+.I history
+mechanism of the shell and which cannot be
+.I escaped
+by placing it within `\'' characters.
+It and the character `\'' itself can be preceded by a single `\e'
+to prevent their special meaning.
+Thus
+.DS
+echo \e\'\e!
+.DE
+prints
+.DS
+\'!
+.DE
+These two mechanisms suffice to place any printing character into a word
+which is an argument to a shell command.  They can be combined, as in
+.DS
+echo \e\'\'*\'
+.DE
+which prints
+.DS
+\'*
+.DE
+since the first `\e' escaped the first `\'' and the `*' was enclosed
+between `\'' characters.
+.NH 2
+Terminating commands
+.PP
+When you are executing a command and the shell is
+waiting for it to complete there are several ways
+to force it to stop.
+For instance if you type the command
+.DS
+cat /etc/passwd
+.DE
+the system will print a copy of a list of all users of the system
+on your terminal.
+This is likely to continue for several minutes unless you stop it.
+You can send an
+\s-2INTERRUPT\s0
+.I signal
+to the
+.I cat
+command by typing ^C on your terminal.*
+.FS
+*On some older Unix systems the \s-2DEL\s0 or \s-2RUBOUT\s0 key
+has the same effect. "stty all" will tell you the INTR key value.
+.FE
+Since
+.I cat
+does not take any precautions to avoid or otherwise handle this signal
+the
+\s-2INTERRUPT\s0
+will cause it to terminate.
+The shell notices that
+.I cat
+has terminated and prompts you again with `% '.
+If you hit \s-2INTERRUPT\s0 again, the shell will just
+repeat its prompt since it handles \s-2INTERRUPT\s0 signals
+and chooses to continue to execute commands rather than terminating
+like
+.I cat
+did, which would have the effect of logging you out.
+.PP
+Another way in which many programs terminate is when they get an end-of-file
+from their standard input.
+Thus the
+.I mail
+program in the first example above was terminated when we typed a ^D
+which generates an end-of-file from the standard input.
+The shell also terminates when it gets an end-of-file printing `logout';
+\s-2UNIX\s0 then logs you off the system.
+Since this means that typing too many ^D's can accidentally log us off,
+the shell has a mechanism for preventing this.
+This
+.I ignoreeof
+option will be discussed in section 2.2.
+.PP
+If a command has its standard input redirected from a file, then it will
+normally terminate when it reaches the end of this file.
+Thus if we execute
+.DS
+mail bill < prepared.text
+.DE
+the mail command will terminate without our typing a ^D.
+This is because it read to the end-of-file of our file
+`prepared.text' in which we placed a message for `bill' with an editor program.
+We could also have done
+.DS
+cat prepared.text \||\| mail bill
+.DE
+since the
+.I cat
+command would then have written the text through the pipe to the
+standard input of the mail command.
+When the
+.I cat
+command completed it would have terminated,
+closing down the pipeline
+and the
+.I mail
+command would have received an end-of-file from it and terminated.
+Using a pipe here is more complicated than redirecting input
+so we would more likely use the first form.
+These commands could also have been stopped by sending an \s-2INTERRUPT\s0.
+.PP
+Another possibility for stopping a command is to suspend its execution
+temporarily, with the possibility of continuing execution later.  This is
+done by sending a \s-2STOP\s0 signal via typing a ^Z.
+This signal causes all commands running on the terminal
+(usually one but more if a pipeline is executing) to become suspended.
+The shell notices that the command(s) have been suspended, types
+`Stopped' and then prompts for a new command.
+The previously executing command has been suspended, but otherwise
+unaffected by the \s-2STOP\s0 signal.  Any other commands can be executed
+while the original command remains suspended.  The suspended command can
+be continued using the
+.I fg
+command with no arguments.  The shell will then retype the command
+to remind you which command is being continued, and cause the command
+to resume execution.  Unless any input files in use by the suspended
+command have been changed in the meantime, the suspension has no effect
+whatsoever on the execution of the command.  This feature can be very useful
+during editing, when you need to look at another file before continuing. An
+example of command suspension follows.
+.DS
+% mail harold
+Someone just copied a big file into my directory and its name is
+^Z
+Stopped
+% ls
+funnyfile
+prog.c
+prog.o
+% jobs
+.ta 1.75i
+[1]  + Stopped mail harold
+% fg
+mail harold
+funnyfile. Do you know who did it?
+EOT
+%
+.so tabs
+.DE
+In this example someone was sending a message to Harold and forgot the
+name of the file he wanted to mention.  The mail command was suspended
+by typing ^Z.  When the shell noticed that the mail program was
+suspended, it typed `Stopped' and prompted for a new command.  Then the
+.I ls
+command was typed to find out the name of the file.  The
+.I jobs
+command was run to find out which command was suspended. At this time the
+.I fg
+command was typed to continue execution of the mail program.  Input
+to the mail program was then continued and ended with a ^D
+which indicated the end of the message at which time the mail
+program typed EOT.  The
+.I jobs
+command will show which commands are suspended.
+The ^Z should only be typed at the beginning of a line since
+everything typed on the current line is discarded when a signal is sent
+from the keyboard.  This also happens on \s-2INTERRUPT\s0, and \s-2QUIT\s0
+signals.  More information on
+suspending jobs and controlling them is given in
+section 2.6.
+.PP
+If you write or run programs which are not fully debugged then it may
+be necessary to stop them somewhat ungracefully.
+This can be done by sending them a \s-2QUIT\s0
+signal, sent by typing a ^\e.
+This will usually provoke the shell to produce a message like:
+.DS
+Quit (Core dumped)
+.DE
+indicating that a file
+`core' has been created containing information about the running program's
+state when it terminated due to the \s-2QUIT\s0 signal.
+You can examine this file yourself, or forward information to the
+maintainer of the program telling him/her where the
+.I "core file"
+is.
+.PP
+If you run background commands (as explained in section 2.6) then these
+commands will ignore \s-2INTERRUPT\s0 and \s-2QUIT\s0 signals at the
+terminal.  To stop them you must use the
+.I kill
+command.  See section 2.6 for an example.
+.PP
+If you want to examine the output of a command without having it move
+off the screen as the output of the
+.DS
+cat /etc/passwd
+.DE
+command will, you can use the command
+.DS
+more /etc/passwd
+.DE
+The
+.I more
+program pauses after each complete screenful and types `\-\-More\-\-'
+at which point you can hit a space to get another screenful, a return
+to get another line, a `?' to get some help on other commands, or a `q' to end the
+.I more
+program.  You can also use more as a filter, i.e.
+.DS
+cat /etc/passwd | more
+.DE
+works just like the more simple more command above.
+.PP
+For stopping output of commands not involving
+.I more
+you can use the
+^S key to stop the typeout.  The typeout will resume when you
+hit ^Q or any other key, but ^Q is normally used because
+it only restarts the output and does not become input to the program
+which is running.  This works well on low-speed terminals, but at 9600
+baud it is hard to type ^S and ^Q fast enough to paginate
+the output nicely, and a program like
+.I more
+is usually used.
+.PP
+An additional possibility is to use the ^O flush output
+character; when this character is typed, all output from the current
+command is thrown away (quickly) until the next input read occurs
+or until the next shell prompt.  This can be used to allow a command
+to complete without having to suffer through the output on a slow
+terminal; ^O is a toggle, so flushing can be turned off by
+typing ^O again while output is being flushed.
+.NH 2
+What now?
+.PP
+We have so far seen a number of mechanisms of the shell and learned a lot
+about the way in which it operates.
+The remaining sections will go yet further into the internals of the
+shell, but you will surely want to try using the
+shell before you go any further.
+To try it you can log in to \s-2UNIX\s0 and type the following
+command to the system:
+.DS
+chsh myname /bin/csh
+.DE
+Here `myname' should be replaced by the name you typed to
+the system prompt of `login:' to get onto the system.
+Thus I would use `chsh bill /bin/csh'.
+.B
+You only have to do this once; it takes effect at next login.
+.R
+You are now ready to try using
+.I csh.
+.PP
+Before you do the `chsh' command, the shell you are using when
+you log into the system is `/bin/sh'.
+In fact, much of the above discussion is applicable to `/bin/sh'.
+The next section will introduce many features particular to
+.I csh
+so you should change your shell to
+.I csh
+before you begin reading it.
+.bp
diff --git a/bin/csh/USD.doc/csh.2 b/bin/csh/USD.doc/csh.2
new file mode 100644 (file)
index 0000000..803434b
--- /dev/null
@@ -0,0 +1,1303 @@
+.\"    $NetBSD: csh.2,v 1.7 2003/08/07 09:05:08 agc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\"    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. 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.2       8.1 (Berkeley) 6/8/93
+.\"
+.nr H1 1
+.NH
+Details on the shell for terminal users
+.NH 2
+Shell startup and termination
+.PP
+When you login, the shell is started by the system in your
+.I home
+directory and begins by reading commands from a file
+.I \&.cshrc
+in this directory.
+All shells which you may start during your terminal session will
+read from this file.
+We will later see what kinds of commands are usefully placed there.
+For now we need not have this file and the shell does not complain about
+its absence.
+.PP
+A
+.I "login shell" ,
+executed after you login to the system,
+will, after it reads commands from
+.I \&.cshrc,
+read commands from a file
+.I \&.login
+also in your home directory.
+This file contains commands which you wish to do each time you login
+to the \s-2UNIX\s0 system.
+My
+.I \&.login
+file looks something like:
+.DS
+set ignoreeof
+set mail=(/var/mail/bill)
+echo "${prompt}users" ; users
+alias ts \e
+       \'set noglob ; eval \`tset \-s \-m dialup:c100rv4pna \-m plugboard:?hp2621nl \!*\`\';
+ts; stty intr ^C kill ^U crt
+set time=15 history=10
+msgs \-f
+if (\-e $mail) then
+       echo "${prompt}mail"
+       mail
+endif
+.DE
+.PP
+This file contains several commands to be executed by \s-2UNIX\s0
+each time I login.
+The first is a
+.I set
+command which is interpreted directly by the shell.  It sets the shell
+variable
+.I ignoreeof
+which causes the shell to not log me off if I hit ^D.  Rather,
+I use the
+.I logout
+command to log off of the system.
+By setting the
+.I mail
+variable, I ask the shell to watch for incoming mail to me.  Every 5 minutes
+the shell looks for this file and tells me if more mail has arrived there.
+An alternative to this is to put the command
+.DS
+biff y
+.DE
+in place of this
+.I set;
+this will cause me to be notified immediately when mail arrives, and to
+be shown the first few lines of the new message.
+.PP
+Next I set the shell variable `time' to `15' causing the shell to automatically
+print out statistics lines for commands which execute for at least 15 seconds
+of \s-2CPU\s+2 time.  The variable `history' is set to 10 indicating that
+I want the shell to remember the last 10 commands I type in its
+.I "history list" ,
+(described later).
+.PP
+I create an
+.I alias
+``ts'' which executes a
+\fItset\fR\|(1) command setting up the modes of the terminal.
+The parameters to
+.I tset
+indicate the kinds of terminal which I usually use when not on a hardwired
+port.  I then execute ``ts'' and also use the
+.I stty
+command to change the interrupt character to ^C and the line kill
+character to ^U.
+.PP
+I then run the `msgs' program, which provides me with any
+system messages which I have not seen before; the `\-f' option here prevents
+it from telling me anything if there are no new messages.
+Finally, if my mailbox file exists, then I run the `mail' program to
+process my mail.
+.PP
+When the `mail' and `msgs' programs finish, the shell will finish
+processing my
+.I \&.login
+file and begin reading commands from the terminal, prompting for each with
+`% '.
+When I log off (by giving the
+.I logout
+command) the shell
+will print `logout' and execute commands from the file `.logout'
+if it exists in my home directory.
+After that the shell will terminate and \s-2UNIX\s0 will log
+me off the system.
+If the system is not going down, I will receive a new login message.
+In any case, after the `logout' message the shell is committed to terminating
+and will take no further input from my terminal.
+.NH 2
+Shell variables
+.PP
+The shell maintains a set of
+.I variables.
+We saw above the variables
+.I history
+and
+.I time
+which had values `10' and `15'.
+In fact, each shell variable has as value an array of
+zero or more
+.I strings.
+Shell variables may be assigned values by the set command.  It has
+several forms, the most useful of which was given above and is
+.DS
+set name=value
+.DE
+.PP
+Shell variables may be used to store values which are to
+be used in commands later through a substitution mechanism.
+The shell variables most commonly referenced are, however, those which the
+shell itself refers to.
+By changing the values of these variables one can directly affect the
+behavior of the shell.
+.PP
+One of the most important variables is the variable
+.I path.
+This variable contains a sequence of directory names where the shell
+searches for commands.
+The
+.I set
+command with no arguments
+shows the value of all variables currently defined (we usually say
+.I set)
+in the shell.
+The default value for path will be shown by
+.I set
+to be
+.DS
+% set
+.ta .75i
+argv   ()
+cwd    /usr/bill
+home   /usr/bill
+path   (. /usr/ucb /bin /usr/bin)
+prompt %
+shell  /bin/csh
+status 0
+term   c100rv4pna
+user   bill
+%
+.so tabs
+.DE
+This output indicates that the variable path points to the current
+directory `.' and then `/usr/ucb', `/bin' and `/usr/bin'.
+Commands which you may write might be in `.' (usually one of
+your directories).
+Commands developed at Berkeley, live in `/usr/ucb'
+while commands developed at Bell Laboratories live in `/bin' and `/usr/bin'.
+.PP
+A number of locally developed programs on the system live in the directory
+`/usr/local'.
+If we wish that all shells which we invoke to have
+access to these new programs we can place the command
+.DS
+set path=(. /usr/ucb /bin /usr/bin /usr/local)
+.DE
+in our file
+.I \&.cshrc
+in our home directory.
+Try doing this and then logging out and back in and do
+.DS
+set
+.DE
+again to see that the value assigned to
+.I path
+has changed.
+.FS \(dg
+Another directory that might interest you is /usr/new, which contains
+many useful user-contributed programs provided with Berkeley Unix.
+.FE
+.PP
+One thing you should be aware of is that the shell examines each directory
+which you insert into your path and determines which commands are contained
+there.  Except for the current directory `.', which the shell treats specially,
+this means that if commands are added to a directory in your search path after
+you have started the shell, they will not necessarily be found by the shell.
+If you wish to use a command which has been added in this way, you should
+give the command
+.DS
+rehash
+.DE
+to the shell, which will cause it to recompute its internal table of command
+locations, so that it will find the newly added command.
+Since the shell has to look in the current directory `.' on each command,
+placing it at the end of the path specification usually works equivalently
+and reduces overhead.
+.PP
+Other useful built in variables are the variable
+.I home
+which shows your home directory,
+.I cwd
+which contains your current working directory,
+the variable
+.I ignoreeof
+which can be set in your
+.I \&.login
+file to tell the shell not to exit when it receives an end-of-file from
+a terminal (as described above).
+The variable `ignoreeof'
+is one of several variables which the shell does not care about the
+value of, only whether they are
+.I set
+or
+.I unset.
+Thus to set this variable you simply do
+.DS
+set ignoreeof
+.DE
+and to unset it do
+.DS
+unset ignoreeof
+.DE
+These give the variable `ignoreeof' no value, but none is desired or required.
+.PP
+Finally, some other built-in shell variables of use are the
+variables
+.I noclobber
+and
+.I mail.
+The metasyntax
+.DS
+> filename
+.DE
+which redirects the standard output of a command
+will overwrite and destroy the previous contents of the named file.
+In this way you may accidentally overwrite a file which is valuable.
+If you would prefer that the shell not overwrite files in this
+way you can
+.DS
+set noclobber
+.DE
+in your
+.I \&.login
+file.
+Then trying to do
+.DS
+date > now
+.DE
+would cause a diagnostic if `now' existed already.
+You could type
+.DS
+date >!  now
+.DE
+if you really wanted to overwrite the contents of `now'.
+The `>!' is a special metasyntax indicating that clobbering the
+file is ok.\(dg
+.FS
+\(dgThe space between the `!' and the word `now' is critical here, as `!now'
+would be an invocation of the
+.I history
+mechanism, and have a totally different effect.
+.FE
+.NH 2
+The shell's history list
+.PP
+The shell can maintain a
+.I "history list"
+into which it places the words
+of previous commands.
+It is possible to use a notation to reuse commands or words
+from commands in forming new commands.
+This mechanism can be used to repeat previous commands or to
+correct minor typing mistakes in commands.
+.PP
+The following figure gives a sample session involving typical usage of the
+history mechanism of the shell.
+.KF
+.DS
+% cat bug.c
+main()
+
+{
+       printf("hello);
+}
+% cc !$
+cc bug.c
+"bug.c", line 4: newline in string or char constant
+"bug.c", line 5: syntax error
+% ed !$
+ed bug.c
+29
+4s/);/"&/p
+        printf("hello");
+w
+30
+q
+% !c
+cc bug.c
+% a.out
+hello% !e
+ed bug.c
+30
+4s/lo/lo\e\en/p
+        printf("hello\en");
+w
+32
+q
+% !c \-o bug
+cc bug.c \-o bug
+% size a.out bug
+a.out: 2784+364+1028 = 4176b = 0x1050b
+bug: 2784+364+1028 = 4176b = 0x1050b
+% ls \-l !*
+ls \-l a.out bug
+\(mirwxr\(mixr\(mix 1 bill       3932 Dec 19 09:41 a.out
+\(mirwxr\(mixr\(mix 1 bill       3932 Dec 19 09:42 bug
+% bug
+hello
+% num bug.c | spp
+spp: Command not found.
+% ^spp^ssp
+num bug.c | ssp
+    1  main()
+    3  {
+    4          printf("hello\en");
+    5  }
+% !! | lpr
+num bug.c | ssp | lpr
+%
+.DE
+.KE
+In this example we have a very simple C program which has a bug (or two)
+in it in the file `bug.c', which we `cat' out on our terminal.  We then
+try to run the C compiler on it, referring to the file again as `!$',
+meaning the last argument to the previous command.  Here the `!' is the
+history mechanism invocation metacharacter, and the `$' stands for the last
+argument, by analogy to `$' in the editor which stands for the end of the line.
+The shell echoed the command, as it would have been typed without use of
+the history mechanism, and then executed it.
+The compilation yielded error diagnostics so we now run the editor on the
+file we were trying to compile, fix the bug, and run the C compiler again,
+this time referring to this command simply as `!c', which repeats the last
+command which started with the letter `c'.  If there were other
+commands starting with `c' done recently we could have said `!cc' or even
+`!cc:p' which would have printed the last command starting with `cc'
+without executing it.
+.PP
+After this recompilation, we ran the resulting `a.out' file, and then
+noting that there still was a bug, ran the editor again.  After fixing
+the program we ran the C compiler again, but tacked onto the command
+an extra `\-o bug' telling the compiler to place the resultant binary in
+the file `bug' rather than `a.out'.  In general, the history mechanisms
+may be used anywhere in the formation of new commands and other characters
+may be placed before and after the substituted commands.
+.PP
+We then ran the `size' command to see how large the binary program images
+we have created were, and then an `ls \-l' command with the same argument
+list, denoting the argument list `\!*'.
+Finally we ran the program `bug' to see that its output is indeed correct.
+.PP
+To make a numbered listing of the program we ran the `num' command on the file `bug.c'.
+In order to compress out blank lines in the output of `num' we ran the
+output through the filter `ssp', but misspelled it as spp.  To correct this
+we used a shell substitute, placing the old text and new text between `^'
+characters.  This is similar to the substitute command in the editor.
+Finally, we repeated the same command with `!!', but sent its output to the
+line printer.
+.PP
+There are other mechanisms available for repeating commands.  The
+.I history
+command prints out a number of previous commands with numbers by which
+they can be referenced.  There is a way to refer to a previous command
+by searching for a string which appeared in it, and there are other,
+less useful, ways to select arguments to include in a new command.
+A complete description of all these mechanisms
+is given in the C shell manual pages in the \s-2UNIX\s0 Programmer's Manual.
+.NH 2
+Aliases
+.PP
+The shell has an
+.I alias
+mechanism which can be used to make transformations on input commands.
+This mechanism can be used to simplify the commands you type,
+to supply default arguments to commands,
+or to perform transformations on commands and their arguments.
+The alias facility is similar to a macro facility.
+Some of the features obtained by aliasing can be obtained also
+using shell command files, but these take place in another instance
+of the shell and cannot directly affect the current shells environment
+or involve commands such as
+.I cd
+which must be done in the current shell.
+.PP
+As an example, suppose that there is a new version of the mail program
+on the system called `newmail'
+you wish to use, rather than the standard mail program which is called
+`mail'.
+If you place the shell command
+.DS
+alias mail newmail
+.DE
+in your
+.I \&.cshrc
+file, the shell will transform an input line of the form
+.DS
+mail bill
+.DE
+into a call on `newmail'.
+More generally, suppose we wish the command `ls' to always show
+sizes of files, that is to always do `\-s'.
+We can do
+.DS
+alias ls ls \-s
+.DE
+or even
+.DS
+alias dir ls \-s
+.DE
+creating a new command syntax `dir'
+which does an `ls \-s'.
+If we say
+.DS
+dir ~bill
+.DE
+then the shell will translate this to
+.DS
+ls \-s /mnt/bill
+.DE
+.PP
+Thus the
+.I alias
+mechanism can be used to provide short names for commands,
+to provide default arguments,
+and to define new short commands in terms of other commands.
+It is also possible to define aliases which contain multiple
+commands or pipelines, showing where the arguments to the original
+command are to be substituted using the facilities of the
+history mechanism.
+Thus the definition
+.DS
+alias cd \'cd \e!* ; ls \'
+.DE
+would do an
+.I ls
+command after each change directory
+.I cd
+command.
+We enclosed the entire alias definition in `\'' characters to prevent
+most substitutions from occurring and the character `;' from being
+recognized as a metacharacter.
+The `!' here is escaped with a `\e' to prevent it from being interpreted
+when the alias command is typed in.
+The `\e!*' here substitutes the entire argument list to the pre-aliasing
+.I cd
+command, without giving an error if there were no arguments.
+The `;' separating commands is used here
+to indicate that one command is to be done and then the next.
+Similarly the definition
+.DS
+alias whois \'grep \e!^ /etc/passwd\'
+.DE
+defines a command which looks up its first argument in the password file.
+.PP
+.B Warning:
+The shell currently reads the
+.I \&.cshrc
+file each time it starts up.  If you place a large number of commands
+there, shells will tend to start slowly.  A mechanism for saving the shell
+environment after reading the \fI\&.cshrc\fR file and quickly restoring it is
+under development, but for now you should try to limit the number of
+aliases you have to a reasonable number... 10 or 15 is reasonable,
+50 or 60 will cause a noticeable delay in starting up shells, and make
+the system seem sluggish when you execute commands from within the editor
+and other programs.
+.NH 2
+More redirection; >> and >&
+.PP
+There are a few more notations useful to the terminal user
+which have not been introduced yet.
+.PP
+In addition to the standard output, commands also have a
+.I "diagnostic output"
+which is normally directed to the terminal even when the standard output
+is redirected to a file or a pipe.
+It is occasionally desirable to direct the diagnostic output along with
+the standard output.
+For instance if you want to redirect the output of a long running command
+into a file and wish to have a record of any error diagnostic it produces
+you can do
+.DS
+command >& file
+.DE
+The `>&' here tells the shell to route both the diagnostic output and the
+standard output into `file'.
+Similarly you can give the command
+.DS
+command |\|& lpr
+.DE
+to route both standard and diagnostic output through the pipe
+to the line printer daemon
+.I lpr.\(dd
+.FS
+\(dd A command of the form
+.br
+.ti +5
+command >&! file
+.br
+exists, and is used when
+.I noclobber
+is set and
+.I file
+already exists.
+.FE
+.PP
+Finally, it is possible to use the form
+.DS
+command >> file
+.DE
+to place output at the end of an existing file.\(dg
+.FS
+\(dg If
+.I noclobber
+is set, then an error will result if
+.I file
+does not exist, otherwise the shell will create
+.I file
+if it doesn't exist.
+A form
+.br
+.ti +5
+command >>! file
+.br
+makes it not be an error for file to not exist when
+.I noclobber
+is set.
+.FE
+.NH 2
+Jobs; Background, Foreground, or Suspended
+.PP
+When one or more commands
+are typed together as a pipeline or as a sequence of commands separated by
+semicolons, a single
+.I job
+is created by the shell consisting of these commands together as a unit.
+Single commands without pipes or semicolons create the simplest jobs.
+Usually, every line typed to the shell creates a job.
+Some lines that create jobs (one per line) are
+.DS
+sort < data
+ls \-s | sort \-n | head \-5
+mail harold
+.DE
+.PP
+If the metacharacter `&' is typed
+at the end of the commands, then the job is started as a
+.I background
+job.  This means that the shell does not wait for it to complete but
+immediately prompts and is ready for another command.  The job runs
+.I "in the background"
+at the same time that normal jobs, called
+.I foreground
+jobs, continue to be read and executed by the shell one at a time.
+Thus
+.DS
+du > usage &
+.DE
+would run the
+.I du
+program, which reports on the disk usage of your working directory (as well as
+any directories below it), put the output into the file `usage' and return
+immediately with a prompt for the next command without out waiting for
+.I du
+to finish.  The
+.I du
+program would continue executing in the background
+until it finished, even though you can type and execute more commands in the
+mean time.
+When a background
+job terminates, a message is typed by the shell just before the next prompt
+telling you that the job has completed.
+In the following example the
+.I du
+job finishes sometime during the
+execution of the
+.I mail
+command and its completion is reported just before
+the prompt after the
+.I mail
+job is finished.
+.DS
+% du > usage &
+[1] 503
+% mail bill
+How do you know when a background job is finished?
+EOT
+.ta 1.75i
+[1] \- Done    du > usage
+%
+.so tabs
+.DE
+If the job did not terminate normally the `Done' message might say
+something else like `Killed'.
+If you want the
+terminations of background jobs to be reported at the time they occur
+(possibly interrupting the output of other foreground jobs), you can set
+the
+.I notify
+variable.  In the previous example this would mean that the
+`Done' message might have come right in the middle of the message to
+Bill.
+Background jobs are unaffected by any signals from the keyboard like
+the \s-2STOP\s0, \s-2INTERRUPT\s0, or \s-2QUIT\s0 signals mentioned earlier.
+.PP
+Jobs are recorded in a table inside the shell until they terminate.
+In this table, the shell remembers the command names, arguments and the
+.I "process numbers"
+of all commands in the job as well as the working directory where the job was
+started.
+Each job in the table is either running
+.I "in the foreground"
+with the shell waiting for it to terminate, running
+.I "in the background,"
+or
+.I suspended.
+Only one job can be running in the foreground at one time, but several
+jobs can be suspended or running in the background at once.  As each job
+is started, it is assigned a small identifying
+number called the
+.I "job number"
+which can be used later to refer to the job in the commands described below.
+Job numbers remain
+the same until the job terminates and then are re-used.
+.PP
+When a job is started in the backgound using `&', its number, as well
+as the process numbers of all its (top level) commands, is typed by the shell
+before prompting you for another command. For example,
+.DS
+% ls \-s | sort \-n > usage &
+[2] 2034 2035
+%
+.DE
+runs the `ls' program with the `\-s' options, pipes this output into
+the `sort' program with the `\-n' option which puts its output into the
+file `usage'.
+Since the `&' was at the end of the line, these two programs were started
+together as a background job.  After starting the job, the shell prints
+the job number in brackets (2 in this case) followed by the process number
+of each program started in the job.  Then the shell immediates prompts for
+a new command, leaving the job running simultaneously.
+.PP
+As mentioned in section 1.8, foreground jobs become
+.I suspended
+by typing ^Z
+which sends a \s-2STOP\s0 signal to the currently running
+foreground job.  A background job can become suspended by using the
+.I stop
+command described below.  When jobs are suspended they merely stop
+any further progress until started again, either in the foreground
+or the backgound.  The shell notices when a job becomes stopped and
+reports this fact, much like it reports the termination of background jobs.
+For foreground jobs this looks like
+.DS
+% du > usage
+^Z
+Stopped
+%
+.DE
+`Stopped' message is typed by the shell when it notices that the
+.I du
+program stopped.
+For background jobs, using the
+.I stop
+command, it is
+.DS
+% sort usage &
+[1] 2345
+% stop %1
+.ta 1.75i
+[1] + Stopped (signal) sort usage
+%
+.so tabs
+.DE
+Suspending foreground jobs can be very useful when you need to temporarily
+change what you are doing (execute other commands) and then return to
+the suspended job.  Also, foreground jobs can be suspended and then
+continued as background jobs using the
+.I bg
+command, allowing you to continue other work and
+stop waiting for the foreground job to finish.  Thus
+.DS
+% du > usage
+^Z
+Stopped
+% bg
+[1] du > usage &
+%
+.DE
+starts `du' in the foreground, stops it before it finishes, then continues
+it in the background allowing more foreground commands to be executed.
+This is especially helpful
+when a foreground job ends up taking longer than you expected and you
+wish you had started it in the backgound in the beginning.
+.PP
+All
+.I "job control"
+commands can take an argument that identifies a particular
+job.
+All job name arguments begin with the character `%', since some of the
+job control commands also accept process numbers (printed by the
+.I ps
+command.)
+The default job (when no argument is given) is called the
+.I current
+job and is identified by a `+' in the output of the
+.I jobs
+command, which shows you which jobs you have.
+When only one job is stopped or running in the background (the usual case)
+it is always the current job thus no argument is needed.
+If a job is stopped while running in the foreground it becomes the
+.I current
+job and the existing current job becomes the
+.I previous
+job \- identified by a `\-' in the output of
+.I jobs.
+When the current job terminates, the previous job becomes the current job.
+When given, the argument is either `%\-' (indicating
+the previous job); `%#', where # is the job number;
+`%pref' where pref is some unique prefix of the command name
+and arguments of one of the jobs; or `%?' followed by some string found
+in only one of the jobs.
+.PP
+The
+.I jobs
+command types the table of jobs, giving the job number,
+commands and status (`Stopped' or `Running') of each backgound or
+suspended job.  With the `\-l' option the process numbers are also
+typed.
+.DS
+% du > usage &
+[1] 3398
+% ls \-s | sort \-n > myfile &
+[2] 3405
+% mail bill
+^Z
+Stopped
+% jobs
+.ta 1.75i
+[1] \(mi Running       du > usage
+[2]    Running ls \-s | sort \-n > myfile
+[3] \(pl Stopped       mail bill
+% fg %ls
+ls \-s | sort \-n > myfile
+% more myfile
+.so tabs
+.DE
+.PP
+The
+.I fg
+command runs a suspended or background job in the foreground.  It is
+used to restart a previously suspended job or change a background job
+to run in the foreground (allowing signals or input from the terminal).
+In the above example we used
+.I fg
+to change the `ls' job from the
+background to the foreground since we wanted to wait for it to
+finish before looking at its output file.
+The
+.I bg
+command runs a suspended job in the background.  It is usually used
+after stopping the currently running foreground job with the
+\s-2STOP\s0 signal.  The combination of the \s-2STOP\s0 signal and the
+.I bg
+command changes a foreground job into a background job.
+The
+.I stop
+command suspends a background job.
+.PP
+The
+.I kill
+command terminates a background or suspended job immediately.
+In addition to jobs, it may be given process numbers as arguments,
+as printed by
+.I ps.
+Thus, in the example above, the running
+.I du
+command could have been terminated by the command
+.DS
+% kill %1
+.ta 1.75i
+[1]  Terminated        du > usage
+%
+.so tabs
+.DE
+.PP
+The
+.I notify
+command (not the variable mentioned earlier) indicates that the termination
+of a specific job should be
+reported at the time it finishes instead of waiting for the next prompt.
+.PP
+If a job running in the background tries to read input from the terminal
+it is automatically stopped.  When such a job is then run in the
+foreground, input can be given to the job.  If desired, the job can
+be run in the background again until it requests input again.
+This is illustrated in the following sequence where the `s' command in the
+text editor might take a long time.
+.ID
+.nf
+% ed bigfile
+120000
+1,$s/thisword/thatword/
+^Z
+Stopped
+% bg
+[1] ed bigfile &
+%
+ . . .  some foreground commands
+.ta 1.75i
+[1] Stopped (tty input)        ed bigfile
+% fg
+ed bigfile
+w
+120000
+q
+%
+.so tabs
+.DE
+So after the `s' command was issued, the `ed' job was stopped with ^Z
+and then put in the background using
+.I bg.
+Some time later when the `s' command was finished,
+.I ed
+tried to read another command and was stopped because jobs
+in the backgound cannot read from the terminal.  The
+.I fg
+command returned the `ed' job to the foreground where it could once again
+accept commands from the terminal.
+.PP
+The command
+.DS
+stty tostop
+.DE
+causes all background jobs run on your terminal to stop
+when they are about to
+write output to the terminal.  This prevents messages from background
+jobs from interrupting foreground job output and allows you to run
+a job in the background without losing terminal output.  It also
+can be used for interactive programs that sometimes have long
+periods without interaction.  Thus each time it outputs a prompt for more
+input it will stop before the prompt.  It can then be run in the
+foreground using
+.I fg,
+more input can be given and, if necessary stopped and returned to
+the background.  This
+.I stty
+command might be a good thing to put in your
+.I \&.login
+file if you do not like output from background jobs interrupting
+your work.  It also can reduce the need for redirecting the output
+of background jobs if the output is not very big:
+.DS
+% stty tostop
+% wc hugefile &
+[1] 10387
+% ed text
+\&. . . some time later
+q
+.ta 1.75i
+[1] Stopped (tty output)       wc hugefile
+% fg wc
+wc hugefile
+   13371   30123   302577
+% stty \-tostop
+.so tabs
+.DE
+Thus after some time the `wc' command, which counts the lines, words
+and characters in a file, had one line of output.  When it tried to
+write this to the terminal it stopped.  By restarting it in the
+foreground we allowed it to write on the terminal exactly when we were
+ready to look at its output.
+Programs which attempt to change the mode of the terminal will also
+block, whether or not
+.I tostop
+is set, when they are not in the foreground, as
+it would be very unpleasant to have a background job change the state
+of the terminal.
+.PP
+Since the
+.I jobs
+command only prints jobs started in the currently executing shell,
+it knows nothing about background jobs started in other login sessions
+or within shell files.  The
+.I ps
+can be used in this case to find out about background jobs not started
+in the current shell.
+.NH 2
+Working Directories
+.PP
+As mentioned in section 1.6, the shell is always in a particular
+.I "working directory."
+The `change directory' command
+.I chdir
+(its
+short form
+.I cd
+may also be used)
+changes the working directory of the shell,
+that is, changes the directory you
+are located in.
+.PP
+It is useful to make a directory for each project you wish to work on
+and to place all files related to that project in that directory.
+The `make directory' command,
+.I mkdir,
+creates a new directory.
+The
+.I pwd
+(`print working directory') command
+reports the absolute pathname of the working directory of the shell,
+that is, the directory you are
+located in.
+Thus in the example below:
+.DS
+% pwd
+/usr/bill
+% mkdir newpaper
+% chdir newpaper
+% pwd
+/usr/bill/newpaper
+%
+.DE
+the user has created and moved to the
+directory
+.I newpaper.
+where, for example, he might
+place a group of related files.
+.PP
+No matter where you have moved to in a directory hierarchy,
+you can return to your `home' login directory by doing just
+.DS
+cd
+.DE
+with no arguments.
+The name `..' always means the directory above the current one in
+the hierarchy, thus
+.DS
+cd ..
+.DE
+changes the shell's working directory to the one directly above the
+current one.
+The name `..' can be used in any
+pathname, thus,
+.DS
+cd ../programs
+.DE
+means
+change to the directory `programs' contained in the directory
+above the current one.
+If you have several directories for different
+projects under, say, your home directory,
+this shorthand notation
+permits you to switch easily between them.
+.PP
+The shell always remembers the pathname of its current working directory in
+the variable
+.I cwd.
+The shell can also be requested to remember the previous directory when
+you change to a new working directory.  If the `push directory' command
+.I pushd
+is used in place of the
+.I cd
+command, the shell saves the name of the current working directory
+on a
+.I "directory stack"
+before changing to the new one.
+You can see this list at any time by typing the `directories'
+command
+.I dirs.
+.ID
+.nf
+% pushd newpaper/references
+~/newpaper/references  ~
+% pushd /usr/lib/tmac
+/usr/lib/tmac  ~/newpaper/references  ~
+% dirs
+/usr/lib/tmac  ~/newpaper/references  ~
+% popd
+~/newpaper/references  ~
+% popd
+~
+%
+.DE
+The list is printed in a horizontal line, reading left to right,
+with a tilde (~) as
+shorthand for your home directory\(emin this case `/usr/bill'.
+The directory stack is printed whenever there is more than one
+entry on it and it changes.
+It is also printed by a
+.I dirs
+command.
+.I Dirs
+is usually faster and more informative than
+.I pwd
+since it shows the current working directory as well as any
+other directories remembered in the stack.
+.PP
+The
+.I pushd
+command with no argument
+alternates the current directory with the first directory in the
+list.
+The `pop directory'
+.I popd
+command without an argument returns you to the directory you were in prior to
+the current one, discarding the previous current directory from the
+stack (forgetting it).
+Typing
+.I popd
+several times in a series takes you backward through the directories
+you had been in (changed to) by
+.I pushd
+command.
+There are other options to
+.I pushd
+and
+.I popd
+to manipulate the contents of the directory stack and to change
+to directories not at the top of the stack; see the
+.I csh
+manual page for details.
+.PP
+Since the shell remembers the working directory in which each job
+was started, it warns you when you might be confused by restarting
+a job in the foreground which has a different working directory than the
+current working directory of the shell.  Thus if you start a background
+job, then change the shell's working directory and then cause the
+background job to run in the foreground, the shell warns you that the
+working directory of the currently running foreground job is different
+from that of the shell.
+.DS
+% dirs \-l
+/mnt/bill
+% cd myproject
+% dirs
+~/myproject
+% ed prog.c
+1143
+^Z
+Stopped
+% cd ..
+% ls
+myproject
+textfile
+% fg
+ed prog.c (wd: ~/myproject)
+.DE
+This way the shell warns you when there
+is an implied change of working directory, even though no cd command was
+issued.  In the above example the `ed' job was still in `/mnt/bill/project'
+even though the shell had changed to `/mnt/bill'.
+A similar warning is given when such a foreground job
+terminates or is suspended (using the \s-2STOP\s0 signal) since
+the return to the shell again implies a change of working directory.
+.DS
+% fg
+ed prog.c (wd: ~/myproject)
+ . . . after some editing
+q
+(wd now: ~)
+%
+.DE
+These messages are sometimes confusing if you use programs that change
+their own working directories, since the shell only remembers which
+directory a job is started in, and assumes it stays there.
+The `\-l' option of
+.I jobs
+will type the working directory
+of suspended or background jobs when it is different
+from the current working directory of the shell.
+.NH 2
+Useful built-in commands
+.PP
+We now give a few of the useful built-in commands of the shell describing
+how they are used.
+.PP
+The
+.I alias
+command described above is used to assign new aliases and to show the
+existing aliases.
+With no arguments it prints the current aliases.
+It may also be given only one argument such as
+.DS
+alias ls
+.DE
+to show the current alias for, e.g., `ls'.
+.PP
+The
+.I echo
+command prints its arguments.
+It is often used in
+.I "shell scripts"
+or as an interactive command
+to see what filename expansions will produce.
+.PP
+The
+.I history
+command will show the contents of the history list.
+The numbers given with the history events can be used to reference
+previous events which are difficult to reference using the
+contextual mechanisms introduced above.
+There is also a shell variable called
+.I prompt.
+By placing a `!' character in its value the shell will there substitute
+the number of the current command in the history list.
+You can use this number to refer to this command in a history substitution.
+Thus you could
+.DS
+set prompt=\'\e! % \'
+.DE
+Note that the `!' character had to be
+.I escaped
+here even within `\'' characters.
+.PP
+The
+.I limit
+command is used to restrict use of resources.
+With no arguments it prints the current limitations:
+.DS
+.ta 1i
+cputime        unlimited
+filesize       unlimited
+datasize       5616 kbytes
+stacksize      512 kbytes
+coredumpsize   unlimited
+.so tabs
+.DE
+Limits can be set, e.g.:
+.DS
+limit coredumpsize 128k
+.DE
+Most reasonable units abbreviations will work; see the
+.I csh
+manual page for more details.
+.PP
+The
+.I logout
+command can be used to terminate a login shell which has
+.I ignoreeof
+set.
+.PP
+The
+.I rehash
+command causes the shell to recompute a table of where commands are
+located.  This is necessary if you add a command to a directory
+in the current shell's search path and wish the shell to find it,
+since otherwise the hashing algorithm may tell the shell that the
+command wasn't in that directory when the hash table was computed.
+.PP
+The
+.I repeat
+command can be used to repeat a command several times.
+Thus to make 5 copies of the file
+.I one
+in the file
+.I five
+you could do
+.DS
+repeat 5 cat one >> five
+.DE
+.PP
+The
+.I setenv
+command can be used
+to set variables in the environment.
+Thus
+.DS
+setenv TERM adm3a
+.DE
+will set the value of the environment variable \s-2TERM\s0
+to
+`adm3a'.
+A user program
+.I printenv
+exists which will print out the environment.
+It might then show:
+.DS
+% printenv
+HOME=/usr/bill
+SHELL=/bin/csh
+PATH=:/usr/ucb:/bin:/usr/bin:/usr/local
+TERM=adm3a
+USER=bill
+%
+.DE
+.PP
+The
+.I source
+command can be used to force the current shell to read commands from
+a file.
+Thus
+.DS
+source .cshrc
+.DE
+can be used after editing in a change to the
+.I \&.cshrc
+file which you wish to take effect right away.
+.PP
+The
+.I time
+command can be used to cause a command to be timed no matter how much
+\s-2CPU\s0 time it takes.
+Thus
+.DS
+% time cp /etc/rc /usr/bill/rc
+0.0u 0.1s 0:01 8% 2+1k 3+2io 1pf+0w
+% time wc /etc/rc /usr/bill/rc
+     52    178   1347 /etc/rc
+     52    178   1347 /usr/bill/rc
+    104    356   2694 total
+0.1u 0.1s 0:00 13% 3+3k 5+3io 7pf+0w
+%
+.DE
+indicates that the
+.I cp
+command used a negligible amount of user time (u)
+and about 1/10th of a system time (s); the elapsed time was 1 second (0:01),
+there was an average memory usage of 2k bytes of program space and 1k
+bytes of data space over the cpu time involved (2+1k); the program
+did three disk reads and two disk writes (3+2io), and took one page fault
+and was not swapped (1pf+0w).
+The word count command
+.I wc
+on the other hand used 0.1 seconds of user time and 0.1 seconds of system
+time in less than a second of elapsed time.
+The percentage `13%' indicates that over the period when it was active
+the command `wc' used an average of 13 percent of the available \s-2CPU\s0
+cycles of the machine.
+.PP
+The
+.I unalias
+and
+.I unset
+commands can be used
+to remove aliases and variable definitions from the shell, and
+.I unsetenv
+removes variables from the environment.
+.NH 2
+What else?
+.PP
+This concludes the basic discussion of the shell for terminal users.
+There are more features of the shell to be discussed here, and all
+features of the shell are discussed in its manual pages.
+One useful feature which is discussed later is the
+.I foreach
+built-in command which can be used to run the same command
+sequence with a number of different arguments.
+.PP
+If you intend to use \s-2UNIX\s0 a lot you you should look through
+the rest of this document and the csh manual pages (section1) to become familiar
+with the other facilities which are available to you.
+.bp
diff --git a/bin/csh/USD.doc/csh.3 b/bin/csh/USD.doc/csh.3
new file mode 100644 (file)
index 0000000..a6a6f66
--- /dev/null
@@ -0,0 +1,648 @@
+.\"    $NetBSD: csh.3,v 1.5 2003/08/07 09:05:08 agc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\"    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. 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.3       8.1 (Berkeley) 6/8/93
+.\"
+.nr H1 2
+.NH
+Shell control structures and command scripts
+.NH 2
+Introduction
+.PP
+It is possible to place commands in files and to cause shells to be
+invoked to read and execute commands from these files,
+which are called
+.I "shell scripts."
+We here detail those features of the shell useful to the writers of such
+scripts.
+.NH 2
+Make
+.PP
+It is important to first note what shell scripts are
+.I not
+useful for.
+There is a program called
+.I make
+which is very useful for maintaining a group of related files
+or performing sets of operations on related files.
+For instance a large program consisting of one or more files
+can have its dependencies described in a
+.I makefile
+which contains definitions of the commands used to create these
+different files when changes occur.
+Definitions of the means for printing listings, cleaning up the directory
+in which the files reside, and installing the resultant programs
+are easily, and most appropriately placed in this
+.I makefile.
+This format is superior and preferable to maintaining a group of shell
+procedures to maintain these files.
+.PP
+Similarly when working on a document a
+.I makefile
+may be created which defines how different versions of the document
+are to be created and which options of
+.I nroff
+or
+.I troff
+are appropriate.
+.NH 2
+Invocation and the argv variable
+.PP
+A
+.I csh
+command script may be interpreted by saying
+.DS
+% csh script ...
+.DE
+where
+.I script
+is the name of the file containing a group of
+.I csh
+commands and
+`\&...' is replaced by a sequence of arguments.
+The shell places these arguments in the variable
+.I argv
+and then begins to read commands from the script.
+These parameters are then available through the same mechanisms
+which are used to reference any other shell variables.
+.PP
+If you make the file
+`script'
+executable by doing
+.DS
+chmod 755 script
+.DE
+and place a shell comment at the beginning of the shell script
+(i.e. begin the file with a `#' character)
+then a `/bin/csh' will automatically be invoked to execute `script' when
+you type
+.DS
+script
+.DE
+If the file does not begin with a `#' then the standard shell
+`/bin/sh' will be used to execute it.
+This allows you to convert your older shell scripts to use
+.I csh
+at your convenience.
+.NH 2
+Variable substitution
+.PP
+After each input line is broken into words and history substitutions
+are done on it, the input line is parsed into distinct commands.
+Before each command is executed a mechanism know as
+.I "variable substitution"
+is done on these words.
+Keyed by the character `$' this substitution replaces the names
+of variables by their values.
+Thus
+.DS
+echo $argv
+.DE
+when placed in a command script would cause the current value of the
+variable
+.I argv
+to be echoed to the output of the shell script.
+It is an error for
+.I argv
+to be unset at this point.
+.PP
+A number of notations are provided for accessing components and attributes
+of variables.
+The notation
+.DS
+$?name
+.DE
+expands to `1' if name is
+.I set
+or to `0'
+if name is not
+.I set.
+It is the fundamental mechanism used for checking whether particular
+variables have been assigned values.
+All other forms of reference to undefined variables cause errors.
+.PP
+The notation
+.DS
+$#name
+.DE
+expands to the number of elements in the variable
+.I name.
+Thus
+.DS
+% set argv=(a b c)
+% echo $?argv
+1
+% echo $#argv
+3
+% unset argv
+% echo $?argv
+0
+% echo $argv
+Undefined variable: argv.
+%
+.DE
+.PP
+It is also possible to access the components of a variable
+which has several values.
+Thus
+.DS
+$argv[1]
+.DE
+gives the first component of
+.I argv
+or in the example above `a'.
+Similarly
+.DS
+$argv[$#argv]
+.DE
+would give `c',
+and
+.DS
+$argv[1\-2]
+.DE
+would give `a b'. Other notations useful in shell scripts are
+.DS
+$\fIn\fR
+.DE
+where
+.I n
+is an integer as a shorthand for
+.DS
+$argv[\fIn\fR\|]
+.DE
+the
+.I n\|th
+parameter and
+.DS
+$*
+.DE
+which is a shorthand for
+.DS
+$argv
+.DE
+The form
+.DS
+$$
+.DE
+expands to the process number of the current shell.
+Since this process number is unique in the system it can
+be used in generation of unique temporary file names.
+The form
+.DS
+$<
+.DE
+is quite special and is replaced by the next line of input read from
+the shell's standard input (not the script it is reading).  This is
+useful for writing shell scripts that are interactive, reading
+commands from the terminal, or even writing a shell script that
+acts as a filter, reading lines from its input file. Thus the sequence
+.DS
+echo 'yes or no?\ec'
+set a=($<)
+.DE
+would write out the prompt `yes or no?' without a newline and then
+read the answer into the variable `a'.  In this case `$#a' would be
+`0' if either a blank line or end-of-file (^D) was typed.
+.PP
+One minor difference between `$\fIn\fR\|' and `$argv[\fIn\fR\|]'
+should be noted here.
+The form
+`$argv[\fIn\fR\|]'
+will yield an error if
+.I n
+is not in the range
+`1\-$#argv'
+while `$n'
+will never yield an out of range subscript error.
+This is for compatibility with the way older shells handled parameters.
+.PP
+Another important point is that it is never an error to give a subrange
+of the form `n\-'; if there are less than
+.I n
+components of the given variable then no words are substituted.
+A range of the form `m\-n' likewise returns an empty vector without giving
+an error when \fIm\fR exceeds the number of elements of the given variable,
+provided the subscript \fIn\fR is in range.
+.NH 2
+Expressions
+.PP
+In order for interesting shell scripts to be constructed it
+must be possible to evaluate expressions in the shell based on the
+values of variables.
+In fact, all the arithmetic operations of the language C are available
+in the shell
+with the same precedence that they have in C.
+In particular, the operations `==' and `!=' compare strings
+and the operators `&&' and `|\|\||' implement the boolean and/or operations.
+The special operators `=~' and `!~' are similar to `==' and `!=' except
+that the string on the right side can have pattern matching characters
+(like *, ? or []) and the test is whether the string on the left matches
+the pattern on the right.
+.PP
+The shell also allows file enquiries of the form
+.DS
+\-? filename
+.DE
+where `?' is replace by a number of single characters.
+For instance the expression primitive
+.DS
+\-e filename
+.DE
+tell whether the file
+`filename'
+exists.
+Other primitives test for read, write and execute access to the file,
+whether it is a directory, or has non-zero length.
+.PP
+It is possible to test whether a command terminates normally,
+by a primitive of the
+form `{ command }' which returns true, i.e. `1' if the command
+succeeds exiting normally with exit status 0, or `0' if the command
+terminates abnormally or with exit status non-zero.
+If more detailed information about the execution status of a command
+is required, it can be executed and the variable `$status' examined
+in the next command.
+Since `$status' is set by every command, it is very transient.
+It can be saved if it is inconvenient to use it only in the single
+immediately following command.
+.PP
+For a full list of expression components available see the manual
+section for the shell.
+.NH 2
+Sample shell script
+.PP
+A sample shell script which makes use of the expression mechanism
+of the shell and some of its control structure follows:
+.DS
+% cat copyc
+#
+# Copyc copies those C programs in the specified list
+# to the directory ~/backup if they differ from the files
+# already in ~/backup
+#
+set noglob
+foreach i ($argv)
+
+        if ($i !~ *.c) continue  # not a .c file so do nothing
+
+        if (! \-r ~/backup/$i:t) then
+                echo $i:t not in backup... not cp\e\'ed
+                continue
+        endif
+
+        cmp \-s $i ~/backup/$i:t # to set $status
+
+        if ($status != 0) then
+                echo new backup of $i
+                cp $i ~/backup/$i:t
+        endif
+end
+.DE
+.PP
+This script makes use of the
+.I foreach
+command, which causes the shell to execute the commands between the
+.I foreach
+and the matching
+.I end
+for each of the values given between `(' and `)' with the named
+variable, in this case `i' set to successive values in the list.
+Within this loop we may use the command
+.I break
+to stop executing the loop
+and
+.I continue
+to prematurely terminate one iteration
+and begin the next.
+After the
+.I foreach
+loop the iteration variable
+(\fIi\fR in this case)
+has the value at the last iteration.
+.PP
+We set the variable
+.I noglob
+here to prevent filename expansion of the members of
+.I argv.
+This is a good idea, in general, if the arguments to a shell script
+are filenames which have already been expanded or if the arguments
+may contain filename expansion metacharacters.
+It is also possible to quote each use of a `$' variable expansion,
+but this is harder and less reliable.
+.PP
+The other control construct used here is a statement of the form
+.DS
+\fBif\fR ( expression ) \fBthen\fR
+       command
+       ...
+\fBendif\fR
+.DE
+The placement of the keywords here is
+.B not
+flexible due to the current implementation of the shell.\(dg
+.FS
+\(dgThe following two formats are not currently acceptable to the shell:
+.sp
+.in +5
+.nf
+\fBif\fR ( expression )                # \fBWon't work!\fR
+\fBthen\fR
+       command
+       ...
+\fBendif\fR
+.fi
+.in -5
+.sp
+and
+.sp
+.in +5
+.nf
+\fBif\fR ( expression ) \fBthen\fR command \fBendif\fR         # \fBWon't work\fR
+.in -5
+.fi
+.FE
+.PP
+The shell does have another form of the if statement of the form
+.DS
+\fBif\fR ( expression ) \fBcommand\fR
+.DE
+which can be written
+.DS
+\fBif\fR ( expression ) \e
+       command
+.DE
+Here we have escaped the newline for the sake of appearance.
+The command must not involve `\||\|', `&' or `;'
+and must not be another control command.
+The second form requires the final `\e' to
+.B immediately
+precede the end-of-line.
+.PP
+The more general
+.I if
+statements above also admit a sequence of
+.I else\-if
+pairs followed by a single
+.I else
+and an
+.I endif,
+e.g.:
+.DS
+\fBif\fR ( expression ) \fBthen\fR
+       commands
+\fBelse\fR \fBif\fR (expression ) \fBthen\fR
+       commands
+\&...
+
+\fBelse\fR
+       commands
+\fBendif\fR
+.DE
+.PP
+Another important mechanism used in shell scripts is the `:' modifier.
+We can use the modifier `:r' here to extract a root of a filename or
+`:e' to extract the
+.I extension.
+Thus if the variable
+.I i
+has the value
+`/mnt/foo.bar'
+then
+.sp
+.in +5
+.nf
+% echo $i $i:r $i:e
+/mnt/foo.bar /mnt/foo bar
+%
+.sp
+.in -5
+.fi
+shows how the `:r' modifier strips off the trailing `.bar' and the
+the `:e' modifier leaves only the `bar'.
+Other modifiers will take off the last component of a pathname leaving
+the head `:h' or all but the last component of a pathname leaving the
+tail `:t'.
+These modifiers are fully described in the
+.I csh
+manual pages in the User's Reference Manual.
+It is also possible to use the
+.I "command substitution"
+mechanism described in the next major section to perform modifications
+on strings to then reenter the shell's environment.
+Since each usage of this mechanism involves the creation of a new process,
+it is much more expensive to use than the `:' modification mechanism.\(dd
+.FS
+\(dd It is also important to note that
+the current implementation of the shell limits the number of `:' modifiers
+on a `$' substitution to 1.
+Thus
+.sp
+.nf
+.in +5
+% echo $i $i:h:t
+/a/b/c /a/b:t
+%
+.in -5
+.fi
+.sp
+does not do what one would expect.
+.FE
+Finally, we note that the character `#' lexically introduces a shell
+comment in shell scripts (but not from the terminal).
+All subsequent characters on the input line after a `#' are discarded
+by the shell.
+This character can be quoted using `\'' or `\e' to place it in
+an argument word.
+.NH 2
+Other control structures
+.PP
+The shell also has control structures
+.I while
+and
+.I switch
+similar to those of C.
+These take the forms
+.DS
+\fBwhile\fR ( expression )
+       commands
+\fBend\fR
+.DE
+and
+.DS
+\fBswitch\fR ( word )
+
+\fBcase\fR str1:
+       commands
+       \fBbreaksw\fR
+
+\& ...
+
+\fBcase\fR strn:
+       commands
+       \fBbreaksw\fR
+
+\fBdefault:\fR
+       commands
+       \fBbreaksw\fR
+
+\fBendsw\fR
+.DE
+For details see the manual section for
+.I csh.
+C programmers should note that we use
+.I breaksw
+to exit from a
+.I switch
+while
+.I break
+exits a
+.I while
+or
+.I foreach
+loop.
+A common mistake to make in
+.I csh
+scripts is to use
+.I break
+rather than
+.I breaksw
+in switches.
+.PP
+Finally,
+.I csh
+allows a
+.I goto
+statement, with labels looking like they do in C, i.e.:
+.DS
+loop:
+       commands
+       \fBgoto\fR loop
+.DE
+.NH 2
+Supplying input to commands
+.PP
+Commands run from shell scripts receive by default the standard
+input of the shell which is running the script.
+This is different from previous shells running
+under \s-2UNIX\s0.  It allows shell scripts to fully participate
+in pipelines, but mandates extra notation for commands which are to take
+inline data.
+.PP
+Thus we need a metanotation for supplying inline data to commands in
+shell scripts.
+As an example, consider this script which runs the editor to
+delete leading blanks from the lines in each argument file:
+.DS
+% cat deblank
+# deblank \-\- remove leading blanks
+foreach i ($argv)
+ed \- $i << \'EOF\'
+1,$s/^[ ]*//
+w
+q
+\&\'EOF\'
+end
+%
+.DE
+The notation `<< \'EOF\''
+means that the standard input for the
+.I ed
+command is to come from the text in the shell script file
+up to the next line consisting of exactly `\'EOF\''.
+The fact that the `EOF' is enclosed in `\'' characters, i.e. quoted,
+causes the shell to not perform variable substitution on the
+intervening lines.
+In general, if any part of the word following the `<<' which the
+shell uses to terminate the text to be given to the command is quoted
+then these substitutions will not be performed.
+In this case since we used the form `1,$' in our editor script
+we needed to insure that this `$' was not variable substituted.
+We could also have insured this by preceding the `$' here with a `\e',
+i.e.:
+.DS
+1,\e$s/^[ ]*//
+.DE
+but quoting the `EOF' terminator is a more reliable way of achieving the
+same thing.
+.NH 2
+Catching interrupts
+.PP
+If our shell script creates temporary files, we may wish to catch
+interruptions of the shell script so that we can clean up
+these files.
+We can then do
+.DS
+onintr label
+.DE
+where
+.I label
+is a label in our program.
+If an interrupt is received the shell will do a
+`goto label'
+and we can remove the temporary files and then do an
+.I exit
+command (which is built in to the shell)
+to exit from the shell script.
+If we wish to exit with a non-zero status we can do
+.DS
+exit(1)
+.DE
+e.g. to exit with status `1'.
+.NH 2
+What else?
+.PP
+There are other features of the shell useful to writers of shell
+procedures.
+The
+.I verbose
+and
+.I echo
+options and the related
+.I \-v
+and
+.I \-x
+command line options can be used to help trace the actions of the shell.
+The
+.I \-n
+option causes the shell only to read commands and not to execute
+them and may sometimes be of use.
+.PP
+One other thing to note is that
+.I csh
+will not execute shell scripts which do not begin with the
+character `#', that is shell scripts that do not begin with a comment.
+Similarly, the `/bin/sh' on your system may well defer to `csh'
+to interpret shell scripts which begin with `#'.
+This allows shell scripts for both shells to live in harmony.
+.PP
+There is also another quotation mechanism using `"' which allows
+only some of the expansion mechanisms we have so far discussed to occur
+on the quoted string and serves to make this string into a single word
+as `\'' does.
+.bp
diff --git a/bin/csh/USD.doc/csh.4 b/bin/csh/USD.doc/csh.4
new file mode 100644 (file)
index 0000000..d4760ce
--- /dev/null
@@ -0,0 +1,176 @@
+.\"    $NetBSD: csh.4,v 1.4 2003/08/07 09:05:08 agc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\"    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. 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.4       8.1 (Berkeley) 6/8/93
+.\"
+.nr H1 3
+.NH
+Other, less commonly used, shell features
+.NH 2
+Loops at the terminal; variables as vectors
+.PP
+It is occasionally useful to use the
+.I foreach
+control structure at the terminal to aid in performing a number
+of similar commands.
+For instance, there were at one point three shells in use on the Cory \s-2UNIX\s0
+system at Cory Hall,
+`/bin/sh',
+`/bin/nsh',
+and
+`/bin/csh'.
+To count the number of persons using each shell one could have issued
+the commands
+.DS
+% grep \-c csh$ /etc/passwd
+27
+% grep \-c nsh$ /etc/passwd
+128
+% grep \-c \-v sh$ /etc/passwd
+430
+%
+.DE
+Since these commands are very similar we can use
+.I foreach
+to do this more easily.
+.DS
+% foreach i (\'sh$\' \'csh$\' \'\-v sh$\')
+? grep \-c $i /etc/passwd
+? end
+27
+128
+430
+%
+.DE
+Note here that the shell prompts for
+input with `? ' when reading the body of the loop.
+.PP
+Very useful with loops are variables which contain lists of filenames
+or other words.
+You can, for example, do
+.DS
+% set a=(\`ls\`)
+% echo $a
+csh.n csh.rm
+% ls
+csh.n
+csh.rm
+% echo $#a
+2
+%
+.DE
+The
+.I set
+command here gave the variable
+.I a
+a list of all the filenames in the current directory as value.
+We can then iterate over these names to perform any chosen function.
+.PP
+The output of a command within `\`' characters is converted by
+the shell to a list of words.
+You can also place the `\`' quoted string within `"' characters
+to take each (non-empty) line as a component of the variable;
+preventing the lines from being split into words at blanks and tabs.
+A modifier `:x' exists which can be used later to expand each component
+of the variable into another variable splitting it into separate words
+at embedded blanks and tabs.
+.NH 2
+Braces { ... } in argument expansion
+.PP
+Another form of filename expansion, alluded
+to before involves the characters `{' and `}'.
+These characters specify that the contained strings, separated by `,'
+are to be consecutively substituted into the containing characters
+and the results expanded left to right.
+Thus
+.DS
+A{str1,str2,...strn}B
+.DE
+expands to
+.DS
+Astr1B Astr2B ... AstrnB
+.DE
+This expansion occurs before the other filename expansions, and may
+be applied recursively (i.e. nested).
+The results of each expanded string are sorted separately, left
+to right order being preserved.
+The resulting filenames are not required to exist if no other expansion
+mechanisms are used.
+This means that this mechanism can be used to generate arguments which are
+not filenames, but which have common parts.
+.PP
+A typical use of this would be
+.DS
+mkdir ~/{hdrs,retrofit,csh}
+.DE
+to make subdirectories `hdrs', `retrofit' and `csh'
+in your home directory.
+This mechanism is most useful when the common prefix is longer
+than in this example, i.e.
+.DS
+chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}
+.DE
+.NH 2
+Command substitution
+.PP
+A command enclosed in `\`' characters is replaced, just before
+filenames are expanded, by the output from that command.
+Thus it is possible to do
+.DS
+set pwd=\`pwd\`
+.DE
+to save the current directory in the variable
+.I pwd
+or to do
+.DS
+ex \`grep \-l TRACE *.c\`
+.DE
+to run the editor
+.I ex
+supplying as arguments those files whose names end in `.c'
+which have the string `TRACE' in them.*
+.FS
+*Command expansion also occurs in input redirected with `<<'
+and within `"' quotations.
+Refer to the shell manual section for full details.
+.FE
+.NH 2
+Other details not covered here
+.PP
+In particular circumstances it may be necessary to know the exact
+nature and order of different substitutions performed by the shell.
+The exact meaning of certain combinations of quotations is also
+occasionally important.
+These are detailed fully in its manual section.
+.PP
+The shell has a number of command line option flags mostly of use
+in writing \s-2UNIX\s0 programs,
+and debugging shell scripts.
+See the csh(1) manual section for a list of these options.
+.bp
diff --git a/bin/csh/USD.doc/csh.ap b/bin/csh/USD.doc/csh.ap
new file mode 100644 (file)
index 0000000..7e71070
--- /dev/null
@@ -0,0 +1,93 @@
+.\"    $NetBSD: csh.ap,v 1.1 2007/10/18 18:26:31 tls Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\"    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. 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.a       8.1 (Berkeley) 6/8/93
+.\"
+.SH
+Appendix \- Special characters
+.LP
+The following table lists the special characters of
+.I csh
+and the \s-2UNIX\s0 system, giving for each the section(s) in which it
+is discussed.
+A number of these characters also have special meaning in expressions.
+See the
+.I csh
+manual section
+for a complete list.
+.ta .75i 1.5i 2.25i
+.LP
+Syntactic metacharacters
+.DS
+;      2.4     separates commands to be executed sequentially
+|      1.5     separates commands in a pipeline
+( )    2.2,3.6 brackets expressions and variable values
+&      2.5     follows commands to be executed without waiting for completion
+.DE
+.LP
+Filename metacharacters
+.DS
+/      1.6     separates components of a file's pathname
+\&.    1.6     separates root parts of a file name from extensions
+?      1.6     expansion character matching any single character
+*      1.6     expansion character matching any sequence of characters
+[ ]    1.6     expansion sequence matching any single character from a set
+~      1.6     used at the beginning of a filename to indicate home directories
+{ }    4.2     used to specify groups of arguments with common parts
+.DE
+.LP
+Quotation metacharacters
+.DS
+\e     1.7     prevents meta-meaning of following single character
+\'     1.7     prevents meta-meaning of a group of characters
+"      4.3     like \', but allows variable and command expansion
+.DE
+.LP
+Input/output metacharacters
+.DS
+<      1.5     indicates redirected input
+>      1.3     indicates redirected output
+.DE
+.LP
+Expansion/substitution metacharacters
+.DS
+$      3.4     indicates variable substitution
+!      2.3     indicates history substitution
+:      3.6     precedes substitution modifiers
+^      2.3     used in special forms of history substitution
+\`     4.3     indicates command substitution
+.DE
+.LP
+Other metacharacters
+.DS
+#      1.3,3.6 begins scratch file names; indicates shell comments
+\-     1.2     prefixes option (flag) arguments to commands
+%      2.6     prefixes job name specifications
+.DE
+.bp
diff --git a/bin/csh/USD.doc/csh.g b/bin/csh/USD.doc/csh.g
new file mode 100644 (file)
index 0000000..09f2baf
--- /dev/null
@@ -0,0 +1,1719 @@
+.\"    $NetBSD: csh.g,v 1.5 2003/08/07 09:05:08 agc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\"    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. 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.g       8.1 (Berkeley) 6/8/93
+.\"
+.SH
+Glossary
+.PP
+This glossary lists the most important terms introduced in the
+introduction to the
+shell and gives references to sections of the shell
+document for further information about them.
+References of the form
+`pr (1)'
+indicate that the command
+.I pr
+is in the \s-2UNIX\s0 User Reference manual in section 1.
+You can look at an online copy of its manual page by doing
+.DS
+man 1 pr
+.DE
+References of the form (2.5)
+indicate that more information can be found in section 2.5 of this
+manual.
+.IP \&\fB.\fR 15n
+Your current directory has the name `.' as well as the name printed
+by the command
+.I pwd;
+see also
+.I dirs.
+The current directory `.' is usually the first 
+.I component 
+of the search path contained in the variable
+.I path ,
+thus commands which are in `.' are found first (2.2).
+The character `.' is also used in separating 
+.I components 
+of filenames
+(1.6).
+The character `.' at the beginning of a 
+.I component 
+of a 
+.I pathname 
+is treated specially and not matched by the 
+.I "filename expansion"
+metacharacters `?', `*', and `[' `]' pairs (1.6).
+.IP \&\fB..\fR
+Each directory has a file `..' in it which is a reference to its
+parent directory.
+After changing into the directory with
+.I chdir ,
+i.e.
+.DS
+chdir paper
+.DE
+you can return to the parent directory by doing
+.DS
+chdir ..
+.DE
+The current directory is printed by
+.I pwd
+(2.7).
+.IP a.out
+Compilers which create executable images create them, by default, in the
+file 
+.I a.out.
+for historical reasons (2.3).
+.IP "absolute pathname"
+.br
+A 
+.I pathname 
+which begins with a `/' is
+.I absolute
+since it specifies the 
+.I path 
+of directories from the beginning
+of the entire directory system \- called the
+.I root
+directory.  
+.I Pathname s 
+which are not
+.I absolute
+are called
+.I relative
+(see definition of
+.I "relative pathname" )
+(1.6).
+.IP alias
+An
+.I alias
+specifies a shorter or different name for a \s-2UNIX\s0
+command, or a transformation on a command to be performed in
+the shell.
+The shell has a command
+.I alias
+which establishes 
+.I aliases 
+and can print their current values.
+The command
+.I unalias
+is used to remove 
+.I aliases 
+(2.4).
+.IP argument
+Commands in \s-2UNIX\s0 receive a list of 
+.I argument 
+words.
+Thus the command
+.DS
+echo a b c
+.DE
+consists of the
+.I "command name"
+`echo' and three 
+.I argument 
+words `a', `b' and `c'.
+The set of 
+.I arguments 
+after the
+.I "command name"
+is said to be the
+.I "argument list"
+of the command (1.1).
+.IP argv
+The list of arguments to a command written in the shell language
+(a shell script or shell procedure) is stored in a variable called
+.I argv
+within the shell.
+This name is taken from the conventional name in the
+C programming language (3.4).
+.IP background
+Commands started without waiting for them to complete are called
+.I background
+commands (2.6).
+.IP base
+A filename is sometimes thought of as consisting of a
+.I base
+part, before any `.' character, and an 
+.I extension 
+\- the part after
+the `.'.  See
+.I filename
+and
+.I extension
+(1.6) and basename (1).
+.IP bg
+The
+.I bg
+command causes a
+.I suspended 
+job to continue execution in the
+.I background
+(2.6).
+.IP bin
+A directory containing binaries of programs and shell scripts to be
+executed is typically called a 
+.I bin 
+directory.
+The standard system 
+.I bin 
+directories are `/bin' containing the most
+heavily used commands and `/usr/bin' which contains most other user
+programs.
+Programs developed at UC Berkeley live in `/usr/ucb', while locally
+written programs live in `/usr/local'.  Games are kept in the directory
+`/usr/games'.
+You can place binaries in any directory.
+If you wish to execute them often, the name of the directories
+should be a 
+.I component 
+of the variable
+.I path .
+.IP break
+.I Break
+is a builtin command used to exit from loops within the control
+structure of the shell (3.7).
+.IP breaksw
+The
+.I breaksw
+builtin command is used to exit from a
+.I switch
+control structure, like a
+.I break
+exits from loops (3.7).
+.IP builtin
+A command executed directly by the shell is called a
+.I builtin
+command.
+Most commands in \s-2UNIX\s0 are not built into the shell,
+but rather exist as files in 
+.I bin 
+directories.
+These commands are accessible because the directories in which
+they reside are named in the
+.I path
+variable.
+.IP case
+A
+.I case
+command is used as a label in a
+.I switch
+statement in the shell's control structure, similar to that of the
+language C.
+Details are given in the shell documentation `csh (1)' (3.7).
+.IP cat
+The
+.I cat
+program catenates a list of specified files on the 
+.I "standard output" .
+It is usually used to look at the contents of a single file on the terminal,
+to `cat a file' (1.8, 2.3).
+.IP cd
+The
+.I cd
+command is used to change the 
+.I "working directory" .
+With no arguments,
+.I cd
+changes your 
+.I "working directory" 
+to be your
+.I home
+directory (2.4, 2.7).
+.IP chdir
+The
+.I chdir
+command is a synonym for
+.I cd .
+.I Cd
+is usually used because it is easier to type.
+.IP chsh
+The
+.I chsh
+command is used to change the shell which you use on \s-2UNIX\s0.
+By default, you use an different version of the shell
+which resides in `/bin/sh'.
+You can change your shell to `/bin/csh' by doing
+.DS
+chsh your-login-name /bin/csh
+.DE
+Thus I would do
+.DS
+chsh bill /bin/csh
+.DE
+It is only necessary to do this once.
+The next time you log in to \s-2UNIX\s0 after doing this command,
+you will be using
+.I csh
+rather than the shell in `/bin/sh' (1.9).
+.IP cmp
+.I Cmp
+is a program which compares files.
+It is usually used on binary files, or to see if two files are identical (3.6).
+For comparing text files the program
+.I diff ,
+described in `diff (1)' is used.
+.IP command
+A function performed by the system, either by the shell
+(a builtin 
+.I command )
+or by a program residing in a file in
+a directory within the \s-2UNIX\s0 system, is called a
+.I command
+(1.1).
+.IP "command name"
+.br
+When a command is issued, it consists of a
+.I "command name" ,
+which is the first word of the command,
+followed by arguments.
+The convention on \s-2UNIX\s0 is that the first word of a
+command names the function to be performed (1.1).
+.IP "command substitution"
+.br
+The replacement of a command enclosed in `\`' characters
+by the text output by that command 
+is called
+.I "command substitution"
+(4.3).
+.IP component
+A part of a
+.I pathname
+between `/' characters is called a
+.I component
+of that 
+.I pathname .
+A variable
+which has multiple strings as value is said to have
+several
+.I component s;
+each string is a
+.I component
+of the variable.
+.IP continue
+A builtin command which causes execution of the enclosing
+.I foreach
+or
+.I while
+loop to cycle prematurely.
+Similar to the
+.I continue
+command in the programming language C (3.6).
+.IP control-
+Certain special characters, called
+.I control
+characters, are produced by holding down the \s-2CONTROL\s0 key
+on your terminal and simultaneously pressing another character, much like
+the \s-2SHIFT\s0 key is used to produce upper case characters. Thus
+.I control- c
+is produced by holding down the \s-2CONTROL\s0 key while pressing the
+`c' key.  Usually \s-2UNIX\s0 prints an caret (^) followed by the
+corresponding letter when you type a
+.I control
+character (e.g. `^C' for
+.I control- c
+(1.8).
+.IP "core\ dump"
+When a program terminates abnormally, the system places an image
+of its current state in a file named `core'.
+This 
+.I "core dump"
+can be examined with the system debugger `adb (1)'
+or `sdb (1)' in order to determine what went wrong with the program (1.8).
+If the shell produces a message of the form
+.DS
+Illegal instruction (core dumped)
+.DE
+(where `Illegal instruction' is only one of several possible
+messages), you should report this to the author of the program
+or a system administrator, 
+saving the `core' file.
+.IP cp
+The
+.I cp
+(copy) program is used to copy the contents of one file into another
+file.
+It is one of the most commonly used \s-2UNIX\s0 commands (1.6).
+.IP csh
+The name of the shell
+program that this document describes.
+.IP \&.cshrc
+The file
+.I \&.cshrc
+in your
+.I home
+directory is read by each shell as it begins execution.
+It is usually used to change the setting of the variable
+.I path
+and to set
+.I alias
+parameters which are to take effect globally (2.1).
+.IP cwd
+The
+.I cwd
+variable in the shell holds the
+.I "absolute pathname"
+of the current
+.I "working directory" \&.
+It is changed by the shell whenever your current
+.I "working directory"
+changes and should not be changed otherwise (2.2).
+.IP date
+The
+.I date
+command prints the current date and time (1.3).
+.IP debugging
+.I Debugging
+is the process of correcting mistakes in programs and shell scripts.
+The shell has several options and variables which may be used
+to aid in shell 
+.I debugging 
+(4.4).
+.IP default:
+The label
+.I default:
+is used within shell
+.I switch
+statements, as it is in the C language
+to label the code to be executed if none of the
+.I case
+labels matches the value switched on (3.7).
+.IP \s-2DELETE\s0
+The
+\s-2DELETE\s0
+or
+\s-2RUBOUT\s0
+key on the terminal normally causes an interrupt to be sent to the current job.
+Many users change the interrupt character to be ^C.
+.IP detached
+A command that continues running in the 
+.I background 
+after you logout is said to be 
+.I detached .
+.IP diagnostic
+An error message produced by a program is often referred to as a
+.I diagnostic .
+Most error messages are not written to the 
+.I "standard output" ,
+since that is often directed away from the terminal (1.3, 1.5).
+Error messsages are instead written to the
+.I "diagnostic output"
+which may be directed away from the terminal, but usually is not.
+Thus 
+.I diagnostics 
+will usually appear on the terminal (2.5).
+.IP directory
+A structure which contains files.
+At any time you are in one particular 
+.I directory 
+whose names can be printed by the command 
+.I pwd .
+The
+.I chdir
+command will change you to another 
+.I directory , 
+and make the files
+in that 
+.I directory 
+visible. The 
+.I directory 
+in which you are when you first login is your
+.I home
+directory (1.1, 2.7).
+.IP "directory\ stack"
+The shell saves the names of previous
+.I "working directories"
+in the
+.I "directory stack"
+when you change your current
+.I "working directory"
+via the
+.I pushd
+command.  The
+.I "directory stack"
+can be printed by using the
+.I dirs
+command, which includes your current
+.I "working directory"
+as the first directory name on the left (2.7).
+.IP dirs
+The
+.I dirs
+command prints the shell's
+.I "directory stack" 
+(2.7).
+.IP du
+The
+.I du
+command is a program (described in `du (1)') which
+prints the number of disk blocks is all directories below
+and including your current
+.I "working directory"
+(2.6).
+.IP echo
+The
+.I echo
+command prints its arguments (1.6, 3.6).
+.IP else
+The
+.I else
+command is part of the `if-then-else-endif' control
+command construct (3.6).
+.IP endif
+If an
+.I if
+statement is ended with the word
+.I then ,
+all lines following the
+.I if
+up to a line starting with the word
+.I endif
+or
+.I else
+are executed if the condition between parentheses after the
+.I if
+is true (3.6).
+.IP \s-2EOF\s0
+An
+.I "end\f1-\fPof\f1-\fPfile"
+is generated by the terminal by a control-d,
+and whenever a command reads to the end of a file which
+it has been given as input.
+Commands receiving input from a
+.I pipe
+receive an 
+.I "end\f1-\fPof\f1-\fPfile" 
+when the command sending them input completes.
+Most commands terminate when they receive an 
+.I "end\f1-\fPof\f1-\fPfile" .
+The shell has an option to ignore 
+.I "end\f1-\fPof\f1-\fPfile" 
+from a terminal
+input which may help you keep from logging out accidentally
+by typing too many control-d's (1.1, 1.8, 3.8).
+.IP escape
+A character `\e' used to prevent the special meaning of a metacharacter
+is said to
+.I escape
+the character from its special meaning.
+Thus
+.DS
+echo \e*
+.DE
+will echo the character `*' while just
+.DS
+echo *
+.DE
+will echo the names of the file in the current directory.
+In this example, \e
+.I escape s
+`*' (1.7).
+There is also a non-printing character called
+.I escape , 
+usually labelled
+\s-2ESC\s0
+or
+\s-2ALTMODE\s0
+on terminal keyboards.
+Some older \s-2UNIX\s0 systems use this character to indicate that
+output is to be 
+.I suspended . 
+Most systems use control-s to stop the output and control-q to start it.
+.IP /etc/passwd
+This file contains information about the accounts currently on the
+system.
+It consists of a line for each account with fields separated by
+`:' characters (1.8).
+You can look at this file by saying
+.DS
+cat /etc/passwd
+.DE
+The commands
+.I finger
+and
+.I grep
+are often used to search for information in this file.
+See `finger (1)', `passwd(5)', and `grep (1)' for more details.
+.IP exit
+The
+.I exit
+command is used to force termination of a shell script,
+and is built into the shell (3.9).
+.IP "exit\ status"
+A command which discovers a problem may reflect this back to the command
+(such as a shell) which invoked (executed) it.
+It does this by returning a non-zero number as its
+.I "exit status" ,
+a status of zero being considered
+`normal termination'.
+The
+.I exit
+command can be used to force a shell command script to give a non-zero
+.I "exit status" 
+(3.6).
+.IP expansion
+The replacement of strings in the shell input which contain metacharacters
+by other strings is referred to as the process of
+.I expansion .
+Thus the replacement of the word `*' by a sorted list of files
+in the current directory is a `filename expansion'.
+Similarly the replacement of the characters `!!' by the text of
+the last command is a `history expansion'.
+.I Expansions 
+are also referred to as
+.I substitutions
+(1.6, 3.4, 4.2).
+.IP expressions
+.I Expressions 
+are used in the shell
+to control the conditional structures used in the writing of shell
+scripts and in calculating values for these scripts.
+The operators available in shell 
+.I expressions 
+are those of the language
+C (3.5).
+.IP extension
+Filenames often consist of a
+.I base
+name and an
+.I extension
+separated by the character `.'.
+By convention, groups of related files often share the same 
+.I root 
+name.
+Thus if `prog.c' were a C program, then the object file for this
+program would be stored in `prog.o'.
+Similarly a paper written with the
+`\-me'
+nroff macro package might be stored in
+`paper.me'
+while a formatted version of this paper might be kept in
+`paper.out' and a list of spelling errors in
+`paper.errs' (1.6).
+.IP fg
+The
+.I "job control"
+command
+.I fg
+is used to run a
+.I background
+or
+.I suspended
+job in the
+.I foreground
+(1.8, 2.6).
+.IP filename
+Each file in \s-2UNIX\s0 has a name consisting of up to 14 characters
+and not including the character `/' which is used in
+.I pathname
+building.  Most 
+.I filenames
+do not begin with the character `.', and contain 
+only letters and digits with perhaps a `.' separating the
+.I base
+portion of the 
+.I filename 
+from an 
+.I extension 
+(1.6).
+.IP "filename expansion"
+.br
+.I "Filename expansion" 
+uses the metacharacters `*', `?' and `[' and `]'
+to provide a convenient mechanism for naming files.
+Using 
+.I "filename expansion" 
+it is easy to name all the files in
+the current directory, or all files which have a common 
+.I root 
+name. Other 
+.I "filename expansion" 
+mechanisms use the metacharacter `~' and allow
+files in other users' directories to be named easily (1.6, 4.2).
+.IP flag
+Many \s-2UNIX\s0 commands accept arguments which are not the names
+of files or other users but are used to modify the action of the commands.
+These are referred to as
+.I flag
+options, and by convention consist of one or more letters preceded by
+the character `\-' (1.2).
+Thus the
+.I ls
+(list files) command has an option
+`\-s' to list the sizes of files.
+This is specified
+.DS
+ls \-s
+.DE
+.IP foreach
+The
+.I foreach
+command is used in shell scripts and at the terminal to specify
+repetition of a sequence of commands while the value of a certain
+shell variable ranges through a specified list (3.6, 4.1).
+.IP foreground
+When commands are executing in the normal way such that the
+shell is waiting for them to finish before prompting for another
+command they are said to be
+.I "foreground jobs"
+or
+.I "running in the foreground" \&.  
+This is as opposed to
+.I background .
+.I Foreground
+jobs can be stopped by signals
+from the terminal caused by typing different
+control characters at the keyboard (1.8, 2.6).
+.IP goto
+The shell has a command
+.I goto
+used in shell scripts to transfer control to a given label (3.7).
+.IP grep
+The
+.I grep
+command searches through a list of argument files for a specified string.
+Thus
+.DS
+grep bill /etc/passwd
+.DE
+will print each line in the file
+.I "/etc/passwd"
+which contains the string `bill'.
+Actually,
+.I grep
+scans for 
+.I "regular expressions"
+in the sense of the editors
+`ed (1)' and `ex (1)'.
+.I Grep
+stands for
+`globally find 
+.I "regular expression" 
+and print' (2.4).
+.IP head
+The
+.I head
+command prints the first few lines of one or more files.
+If you have a bunch of files containing text which you are wondering
+about it is sometimes useful to run
+.I head
+with these files as arguments.
+This will usually show enough of what is in these files to let you decide
+which you are interested in (1.5).
+.br
+.I Head
+is also used to describe the part of a
+.I pathname
+before and including the last `/' character.  The
+.I tail
+of a
+.I pathname
+is the part after the last `/'.  The `:h' and `:t' modifiers allow the
+.I head
+or
+.I tail
+of a 
+.I pathname 
+stored in a shell variable to be used (3.6).
+.IP history
+The
+.I history
+mechanism of the shell allows previous commands to be repeated,
+possibly after modification to correct typing mistakes or to change
+the meaning of the command.
+The shell has a
+.I "history list"
+where these commands are kept, and a
+.I history
+variable which controls how large this list is (2.3).
+.IP "home\ directory"
+.br
+Each user has a 
+.I "home directory" ,
+which is given in your entry
+in the password file,
+.I /etc/passwd .
+This is the directory which you are placed in when you first login.
+The
+.I cd
+or
+.I chdir
+command with no arguments takes you back to this directory, whose
+name is recorded in the shell variable
+.I home .
+You can also access the 
+.I "home directories" 
+of other users in forming
+filenames using a 
+.I "filename expansion" 
+notation and the character `~' (1.6).
+.IP if
+A conditional command within the shell, the
+.I if
+command is used in shell command scripts to make decisions
+about what course of action to take next (3.6).
+.IP ignoreeof
+Normally, your shell will exit, printing
+`logout'
+if you type a control-d at a prompt of `% '.
+This is the way you usually log off the system.
+You can
+.I set
+the
+.I ignoreeof
+variable if you wish in your
+.I \&.login
+file and then use the command
+.I logout
+to logout.
+This is useful if you sometimes accidentally type too many control-d
+characters, logging yourself off
+(2.2).
+.IP input
+Many commands on \s-2UNIX\s0 take information from the terminal or from
+files which they then act on.
+This information is called
+.I input .
+Commands normally read for 
+.I input 
+from their
+.I "standard input"
+which is, by default, the terminal.
+This 
+.I "standard input" 
+can be redirected from a file using a shell metanotation
+with the character `<'.
+Many commands will also read from a file specified as argument.
+Commands placed in 
+.I pipelines
+will read from the output of the previous
+command in the 
+.I pipeline .
+The leftmost command in a 
+.I pipeline 
+reads from the terminal if
+you neither redirect its 
+.I input 
+nor give it a filename to use as
+.I "standard input" .
+Special mechanisms exist for supplying input to commands in shell
+scripts (1.5, 3.8).
+.IP interrupt
+An
+.I interrupt
+is a signal to a program that is generated by typing ^C. (On older versions
+of UNIX the \s-2RUBOUT\s0 or \s-2DELETE\s0 key were used for this purpose.)
+It causes most programs to stop execution.
+Certain programs, such as the shell and the editors,
+handle an 
+.I interrupt 
+in special ways, usually by stopping what they
+are doing and prompting for another command.
+While the shell is executing another command and waiting for it
+to finish, the shell does not listen to 
+.I interrupts.
+The shell often wakes up when you hit 
+.I interrupt 
+because many commands
+die when they receive an 
+.I interrupt 
+(1.8, 3.9).
+.IP job
+One or more commands
+typed on the same input line separated by `|' or `;' characters
+are run together and are called a
+.I job \&.
+Simple commands run by themselves without any `|' or `;' characters
+are the simplest 
+.I jobs.
+.I Jobs
+are classified as
+.I foreground ,
+.I background ,
+or
+.I suspended
+(2.6).
+.IP "job\ control"
+The builtin functions that control the execution of
+jobs are called
+.I "job control"
+commands.  These are
+.I "bg, fg, stop, kill"
+(2.6).
+.IP "job\ number"
+When each job
+is started it is assigned a small number called a
+.I "job number"
+which is printed next to the job in the output of the
+.I jobs
+command.  This number, preceded by a `%' character, can be used as an argument
+to
+.I "job control"
+commands to indicate
+a specific job (2.6).
+.IP jobs
+The
+.I jobs
+command prints a table showing
+jobs that are either running in the
+.I background
+or are
+.I suspended
+(2.6).
+.IP kill
+A command which sends a
+signal
+to a job causing it to terminate (2.6).
+.IP \&.login
+The file
+.I \&.login
+in your
+.I home
+directory is read by the shell each time you login to \s-2UNIX\s0
+and the commands there are executed.
+There are a number of commands which are usefully placed here,
+especially
+.I set
+commands to the shell itself (2.1).
+.IP "login\ shell"
+The shell that is started on your terminal when you login is called
+your
+.I "login shell" .
+It is different from other shells which you may run (e.g. on
+shell scripts)
+in that it reads the
+.I \&.login
+file before reading commands from the terminal and it reads the
+.I \&.logout
+file after you logout
+(2.1).
+.IP logout
+The
+.I logout
+command causes a login shell to exit.
+Normally, a login shell will exit when you hit control-d
+generating an 
+.I end\f1-\fPof\f1-\fPfile, 
+but if you have set
+.I ignoreeof
+in you
+.I \&.login
+file then this will not work and you must use
+.I logout
+to log off the \s-2UNIX\s0 system (2.8).
+.IP \&.logout
+When you log off of \s-2UNIX\s0 the shell will execute commands from
+the file
+.I \&.logout
+in your
+.I home
+directory after it prints `logout'.
+.IP lpr
+The command
+.I lpr
+is the line printer daemon.
+The standard input of
+.I lpr
+spooled and printed on the \s-2UNIX\s0 line printer.
+You can also give
+.I lpr
+a list of filenames as arguments to be printed.
+It is most common to use
+.I lpr
+as the last component of a
+.I pipeline
+(2.3).
+.IP ls
+The
+.I ls
+(list files) command is one of the most commonly used \s-2UNIX\s0
+commands.
+With no argument filenames it prints the names of the files in the
+current directory.
+It has a number of useful
+.I flag
+arguments, and can also be given the names of directories
+as arguments, in which case it lists the names of the files in these
+directories (1.2).
+.IP mail
+The
+.I mail
+program is used to send and receive messages from other \s-2UNIX\s0
+users (1.1, 2.1), whether they are logged on or not.
+.IP make
+The
+.I make
+command is used to maintain one or more related files and to
+organize functions to be performed on these files. 
+In many ways
+.I make
+is easier to use, and more helpful than
+shell command scripts (3.2).
+.IP makefile
+The file containing commands for
+.I make
+is called
+.I makefile 
+or 
+.I Makefile
+(3.2).
+.IP manual
+The 
+.I manual 
+often referred to is the
+`\s-2UNIX\s0 manual'.
+It contains 8 numbered sections with a description of each \s-2UNIX\s0
+program (section 1), system call (section 2), subroutine (section 3),
+device (section 4), special data structure (section 5), game (section 6),
+miscellaneous item (section 7) and system administration program (section 8).
+There are also supplementary documents (tutorials and reference guides)
+for individual programs which require explanation in more detail.
+An online version of the 
+.I manual 
+is accessible through the
+.I man
+command.
+Its documentation can be obtained online via
+.DS
+man man
+.DE
+If you can't decide what manual page to look in, try the 
+.I apropos (1)
+command.
+The supplementary documents are in subdirectories of /usr/doc.
+.IP metacharacter
+.br
+Many characters which are neither letters nor digits have special meaning
+either to the shell or to \s-2UNIX\s0.
+These characters are called
+.I metacharacters .
+If it is necessary to place these characters in arguments to commands
+without them having their special meaning then they must be
+.I quoted .
+An example of a 
+.I metacharacter 
+is the character `>' which is used
+to indicate placement of output into a file.
+For the purposes of the
+.I history
+mechanism,
+most unquoted 
+.I metacharacters
+form separate words (1.4).
+The appendix to this user's manual lists the 
+.I metacharacters
+in groups by their function.
+.IP mkdir
+The
+.I mkdir
+command is used to create a new directory.
+.IP modifier
+Substitutions with the 
+.I history 
+mechanism, keyed by the character `!'
+or of variables using the metacharacter `$', are often subjected
+to modifications, indicated by placing the character `:' after the
+substitution and following this with the 
+.I modifier 
+itself.
+The
+.I "command substitution"
+mechanism can also be used to perform modification in a similar way,
+but this notation is less clear (3.6).
+.IP more
+The program
+.I more
+writes a file on your terminal allowing you to control how much text
+is displayed at a time.
+.I More
+can move through the file screenful by screenful, line by line,
+search forward for a string, or start again at the beginning of the file.
+It is generally the easiest way of viewing a file (1.8).
+.IP noclobber
+The shell has a variable
+.I noclobber
+which may be set in the file
+.I \&.login
+to prevent accidental destruction of files by the `>' output redirection
+metasyntax of the shell (2.2, 2.5).
+.IP noglob
+The shell variable
+.I noglob
+is set to suppress the
+.I "filename expansion"
+of arguments containing the metacharacters `~', `*', `?', `[' and `]' (3.6).
+.IP notify
+The
+.I notify
+command tells the shell to report on the termination of a specific
+.I "background job"
+at the exact time it occurs as opposed to waiting
+until just before the next prompt to report the termination.
+The
+.I notify
+variable, if set, causes the shell to always report the termination
+of
+.I background 
+jobs exactly when they occur (2.6).
+.IP onintr
+The
+.I onintr
+command is built into the shell and is used to control the action
+of a shell command script when an 
+.I interrupt 
+signal is received (3.9).
+.IP output
+Many commands in \s-2UNIX\s0 result in some lines of text which are
+called their
+.I output.
+This 
+.I output 
+is usually placed on what is known as the
+.I "standard output"
+which is normally connected to the user's terminal.
+The shell has a syntax using the metacharacter `>' for redirecting
+the 
+.I "standard output" 
+of a command to a file (1.3).
+Using the
+.I pipe
+mechanism and the metacharacter `|' it is also possible for
+the 
+.I "standard output" 
+of one command to become the 
+.I "standard input" 
+of another command (1.5).
+Certain commands such as the line printer daemon
+.I p
+do not place their results on the 
+.I "standard output" 
+but rather in more
+useful places such as on the line printer (2.3).
+Similarly the
+.I write
+command places its output on another user's terminal rather than its
+.I "standard output" 
+(2.3).
+Commands also have a
+.I "diagnostic output"
+where they write their error messages.
+Normally these go to the terminal even if the 
+.I "standard output" 
+has been sent to a file or another command, but it is possible
+to direct error diagnostics along with 
+.I "standard output" 
+using a special metanotation (2.5).
+.IP path
+The shell has a variable
+.I path
+which gives the names of the directories in which it searches for
+the commands which it is given.
+It always checks first to see if the command it is given is
+built into the shell.
+If it is, then it need not search for the command as it can do it internally.
+If the command is not builtin, then the shell searches for a file
+with the name given in each of the directories in the
+.I path
+variable, left to right.
+Since the normal definition of the
+.I path
+variable is
+.DS
+path   (. /usr/ucb /bin /usr/bin)
+.DE
+the shell normally looks in the current directory, and then in
+the standard system directories `/usr/ucb', `/bin' and `/usr/bin' for the named
+command (2.2).
+If the command cannot be found the shell will print an error diagnostic.
+Scripts of shell commands will be executed using another shell to interpret
+them if they have `execute' permission set.
+This is normally true because a command of the form
+.DS
+chmod 755 script
+.DE
+was executed to turn this execute permission on (3.3).
+If you add new commands to a directory in the 
+.I path , 
+you should issue
+the command 
+.I rehash 
+(2.2).
+.IP pathname
+A list of names, separated by `/' characters, forms a
+.I pathname.
+Each
+.I component,
+between successive `/' characters, names a directory
+in which the next 
+.I component 
+file resides.
+.I Pathnames 
+which begin with the character `/' are interpreted relative
+to the
+.I root
+directory in the filesystem.
+Other 
+.I pathnames 
+are interpreted relative to the current directory
+as reported by
+.I pwd.
+The last component of a 
+.I pathname 
+may name a directory, but
+usually names a file.
+.IP pipeline
+A group of commands which are connected together, the 
+.I "standard output"
+of each connected to the 
+.I "standard input" 
+of the next,
+is called a
+.I pipeline.
+The
+.I pipe
+mechanism used to connect these commands is indicated by
+the shell metacharacter `|' (1.5, 2.3).
+.IP popd
+The
+.I popd
+command changes the shell's
+.I "working directory"
+to the directory you most recently left using the
+.I pushd
+command.  It returns to the directory without having to type its name,
+forgetting the name of the current
+.I "working directory"
+before doing so (2.7).
+.IP port
+The part of a computer system to which each terminal is
+connected is called a
+.I port .
+Usually the system has a fixed number of
+.I ports ,
+some of which are connected to telephone lines
+for dial-up access, and some of which are permanently
+wired directly to specific terminals.
+.IP pr
+The
+.I pr
+command is used to prepare listings of the contents of files
+with headers giving the name of the file and the date and
+time at which the file was last modified (2.3).
+.IP printenv
+The
+.I printenv
+command is used
+to print the current setting of variables in the environment
+(2.8).
+.IP process
+An instance of a running program is called a 
+.I process 
+(2.6).
+\s-2UNIX\s0 assigns each 
+.I process 
+a unique number when it is 
+started \- called the
+.I "process number" .
+.I "Process numbers" 
+can be used to stop individual 
+.I processes
+using the 
+.I kill
+or
+.I stop
+commands when the
+.I processes
+are part of a detached
+.I background
+job.
+.IP program
+Usually synonymous with
+.I command ;
+a binary file or shell command script
+which performs a useful function is often
+called a 
+.I program .
+.IP prompt
+Many programs will print a 
+.I prompt 
+on the terminal when they expect input.
+Thus the editor
+`ex (1)' will print a `:' when it expects input.
+The shell 
+.I prompts
+for input with `% ' and occasionally with `? ' when
+reading commands from the terminal (1.1).
+The shell has a variable
+.I prompt
+which may be set to a different value to change the shell's main 
+.I prompt .
+This is mostly used when debugging the shell (2.8).
+.IP pushd
+The
+.I pushd
+command, which means `push directory', changes the shell's
+.I "working directory"
+and also remembers the current
+.I "working directory"
+before the change is made, allowing you to return to the same
+directory via the
+.I popd
+command later without retyping its name (2.7).
+.IP ps
+The
+.I ps
+command is used to show the processes you are currently running.
+Each process is shown with its unique process number,
+an indication of the terminal name it is attached to,
+an indication of the state of the process (whether it is running,
+stopped, awaiting some event (sleeping), and whether it is swapped out),
+and the amount of \s-2CPU\s0 time it has used so far.
+The command is identified by printing some of the words used
+when it was invoked (2.6).
+Shells, such as the
+.I csh
+you use to run the 
+.I ps 
+command, are not normally shown in the output.
+.IP pwd
+The
+.I pwd
+command prints the full 
+.I pathname 
+of the current
+.I "working directory" \&.
+The
+.I dirs
+builtin command is usually a better and faster choice.
+.IP quit
+The
+.I quit
+signal, generated by a control-\e,
+is used to terminate programs which are behaving unreasonably.
+It normally produces a core image file (1.8).
+.IP quotation
+The process by which metacharacters are prevented their special
+meaning, usually by using the character `\' in pairs, or by
+using the character `\e', is referred to as
+.I quotation
+(1.7).
+.IP redirection
+The routing of input or output from or to a file is known
+as
+.I redirection
+of input or output (1.3).
+.IP rehash
+The
+.I rehash
+command tells the shell to rebuild its internal table of which commands
+are found in which directories in your
+.I path .
+This is necessary when a new program is installed in one of these
+directories (2.8).
+.IP "relative pathname"
+.br
+A
+.I pathname
+which does not begin with a `/' is called a
+.I "relative pathname"
+since it is interpreted
+.I relative
+to the current 
+.I "working directory" .
+The first
+.I component
+of such a
+.I pathname
+refers to some file or directory in the
+.I "working directory" ,
+and subsequent
+.I components
+between `/' characters refer to directories below the
+.I "working directory" .
+.I Pathnames
+that are not
+.I relative
+are called
+.I "absolute pathnames"
+(1.6).
+.IP repeat
+The
+.I repeat
+command iterates another command a specified number of times.
+.IP root
+The directory
+that is at the top of the entire directory structure is called the
+.I root
+directory since it is the `root' of the entire tree structure of
+directories.  The name used in
+.I pathnames
+to indicate the
+.I root
+is `/'.  
+.I Pathnames 
+starting with `/' are said to be
+.I absolute
+since they start at the
+.I root
+directory.
+.I Root
+is also used as the part of a 
+.I pathname 
+that is left after removing
+the 
+.I extension .
+See
+.I filename
+for a further explanation (1.6).
+.IP \s-2RUBOUT\s0
+The \s-2RUBOUT\s0 or \s-2DELETE\s0
+key is often used to erase the previously typed character; some users
+prefer the \s-2BACKSPACE\s0 for this purpose.  On older versions of \s-2UNIX\s0
+this key served as the \s-2INTR\s0 character.
+.IP "scratch file"
+Files whose names begin with a `#' are referred to as 
+.I "scratch files" ,
+since they are automatically removed by the system after a couple of
+days of non-use, or more frequently if disk space becomes tight (1.3).
+.IP script
+Sequences of shell commands placed in a file are called shell command 
+.I scripts .
+It is often possible to perform simple tasks using these 
+.I scripts 
+without writing a program in a language such as C, by
+using the shell to selectively run other programs (3.3, 3.10).
+.IP set
+The builtin
+.I set
+command is used to assign new values to shell variables
+and to show the values of the current variables.
+Many shell variables have special meaning to the shell itself.
+Thus by using the 
+.I set 
+command the behavior of the shell can be affected (2.1).
+.IP setenv
+Variables in the environment `environ (5)'
+can be changed by using the
+.I setenv
+builtin command (2.8).
+The
+.I printenv
+command can be used to print the value of the variables in the environment.
+.IP shell
+A 
+.I shell 
+is a command language interpreter.
+It is possible to write and run your own 
+.I shell ,
+as 
+.I shells 
+are no different than any other programs as far as the
+system is concerned.
+This manual deals with the details of one particular 
+.I shell ,
+called
+.I csh.
+.IP "shell script"
+See
+.I script
+(3.3, 3.10).
+.IP signal
+A
+.I signal
+in \s-2UNIX\s0 is a short message that is sent to a running program
+which causes something to happen to that process.
+.I Signals
+are sent either by typing special
+.I control
+characters on the keyboard or by using the
+.I kill
+or
+.I stop
+commands (1.8, 2.6).
+.IP sort
+The
+.I sort
+program sorts a sequence of lines in ways that can be controlled
+by argument 
+.I flags 
+(1.5).
+.IP source
+The
+.I source
+command causes the shell to read commands from a specified file.
+It is most useful for reading files such as
+.I \&.cshrc
+after changing them (2.8).
+.IP "special character"
+.br
+See
+.I metacharacters
+and the
+appendix to this manual.
+.IP standard
+We refer often to the
+.I "standard input"
+and
+.I "standard output"
+of commands.
+See
+.I input
+and
+.I output
+(1.3, 3.8).
+.IP status
+A command normally returns a
+.I status
+when it finishes.
+By convention a
+.I status
+of zero indicates that the command succeeded.
+Commands may return non-zero 
+.I status 
+to indicate that some abnormal event has occurred.
+The shell variable
+.I status
+is set to the 
+.I status 
+returned by the last command.
+It is most useful in shell commmand scripts (3.6).
+.IP stop
+The
+.I stop
+command causes a
+.I background 
+job to become 
+.I suspended 
+(2.6).
+.IP string
+A sequential group of characters taken together is called a
+.I string \&.
+.I Strings
+can contain any printable characters (2.2).
+.IP stty
+The
+.I stty
+program changes certain parameters inside \s-2UNIX\s0 which determine
+how your terminal is handled.  See `stty (1)' for a complete description (2.6).
+.IP substitution
+The shell implements a number of
+.I substitutions
+where sequences indicated by metacharacters are replaced by other sequences.
+Notable examples of this are history 
+.I substitution 
+keyed by the
+metacharacter `!' and variable 
+.I substitution 
+indicated by `$'.
+We also refer to 
+.I substitutions 
+as
+.I expansions
+(3.4).
+.IP suspended
+A job becomes
+.I suspended
+after a \s-2STOP\s0 signal is sent to it, either by typing a
+.I control -z
+at the terminal (for
+.I foreground
+jobs) or by using the
+.I stop
+command (for
+.I background
+jobs).  When
+.I suspended ,
+a job temporarily stops running until it is restarted by either the
+.I fg
+or
+.I bg
+command (2.6).
+.IP switch
+The
+.I switch
+command of the shell allows the shell
+to select one of a number of sequences of commands based on an
+argument string.
+It is similar to the
+.I switch
+statement in the language C (3.7).
+.IP termination
+When a command which is being executed finishes we say it undergoes
+.I termination
+or
+.I terminates.
+Commands normally terminate when they read an 
+.I end\f1-\fPof\f1-\fPfile
+from their 
+.I "standard input" .
+It is also possible to terminate commands by sending them
+an
+.I interrupt
+or
+.I quit
+signal (1.8).
+The
+.I kill
+program terminates specified jobs (2.6).
+.IP then
+The
+.I then
+command is part of the shell's
+`if-then-else-endif' control construct used in command scripts (3.6).
+.IP time
+The
+.I time
+command can be used to measure the amount of \s-2CPU\s0
+and real time consumed by a specified command as well
+as the amount of disk i/o, memory used, and number
+of page faults and swaps taken by the command (2.1, 2.8).
+.IP tset
+The
+.I tset
+program is used to set standard erase and kill characters
+and to tell the system what kind of terminal you are using.
+It is often invoked in a
+.I \&.login
+file (2.1).
+.IP tty
+The word
+.I tty
+is a historical abbreviation for `teletype' which is frequently used
+in \s-2UNIX\s0 to indicate the
+.I port
+to which a given terminal is connected.  The
+.I tty
+command will print the name of the
+.I tty
+or
+.I port
+to which your terminal is presently connected.
+.IP unalias
+The
+.I unalias
+command removes aliases (2.8).
+.IP \s-2UNIX\s0
+\s-2UNIX\s0 is an operating system on which
+.I csh
+runs.
+\s-2UNIX\s0 provides facilities which allow
+.I csh
+to invoke other programs such as editors and text formatters which
+you may wish to use.
+.IP unset
+The
+.I unset
+command removes the definitions of shell variables (2.2, 2.8).
+.IP "variable expansion"
+.br
+See
+.I variables
+and
+.I expansion
+(2.2, 3.4).
+.IP variables
+.I Variables 
+in
+.I csh
+hold one or more strings as value.
+The most common use of 
+.I variables 
+is in controlling the behavior
+of the shell.
+See
+.I path ,
+.I noclobber ,
+and
+.I ignoreeof
+for examples.
+.I Variables 
+such as
+.I argv
+are also used in writing shell programs (shell command scripts)
+(2.2).
+.IP verbose
+The
+.I verbose
+shell variable can be set to cause commands to be echoed
+after they are history expanded.
+This is often useful in debugging shell scripts.
+The
+.I verbose
+variable is set by the shell's
+.I \-v
+command line option (3.10).
+.IP wc
+The
+.I wc
+program calculates the number of characters, words, and lines in the
+files whose names are given as arguments (2.6).
+.IP while
+The
+.I while
+builtin control construct is used in shell command scripts (3.7).
+.IP word
+A sequence of characters which forms an argument to a command is called
+a
+.I word .
+Many characters which are neither letters, digits, `\-', `.' nor `/'
+form 
+.I words 
+all by themselves even if they are not surrounded
+by blanks.
+Any sequence of characters may be made into a 
+.I word 
+by surrounding it
+with `\'' characters
+except for the characters `\'' and `!' which require special treatment
+(1.1).
+This process of placing special characters in 
+.I words 
+without their special meaning is called
+.I quoting .
+.IP "working directory"
+.br
+At any given time you are in one particular directory, called
+your 
+.I "working directory" .
+This directory's name is printed by the
+.I pwd
+command and the files listed by
+.I ls
+are the ones in this directory.
+You can change 
+.I "working directories" 
+using
+.I chdir .
+.IP write
+The
+.I write
+command is an obsolete way of communicating with other users who are logged in to
+\s-2UNIX\s0 (you have to take turns typing).  If you are both using display 
+terminals, use \fItalk\fP(1), which is much more pleasant.
diff --git a/bin/csh/USD.doc/tabs b/bin/csh/USD.doc/tabs
new file mode 100644 (file)
index 0000000..77aa8da
--- /dev/null
@@ -0,0 +1,32 @@
+.\"    $NetBSD: tabs,v 1.4 2003/08/07 09:05:09 agc Exp $
+.\"
+.\" Copyright (c) 1980, 1993
+.\"    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. 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.
+.\"
+.\"    @(#)tabs        8.1 (Berkeley) 6/8/93
+.\"
+.ta 5n 10n 15n 20n 25n 30n 35n 40n 45n 50n 55n 60n 65n 70n 75n 80n
diff --git a/bin/csh/alloc.c b/bin/csh/alloc.c
new file mode 100644 (file)
index 0000000..9d158c6
--- /dev/null
@@ -0,0 +1,91 @@
+/* $NetBSD: alloc.c,v 1.13 2013/01/22 19:28:00 christos Exp $ */
+
+/*-
+ * Copyright (c) 1983, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)alloc.c    8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: alloc.c,v 1.13 2013/01/22 19:28:00 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "csh.h"
+#include "extern.h"
+
+ptr_t
+Malloc(size_t n)
+{
+    ptr_t ptr;
+
+    if ((ptr = malloc(n)) == (ptr_t) 0) {
+       child++;
+       stderror(ERR_NOMEM);
+    }
+    return (ptr);
+}
+
+ptr_t
+Realloc(ptr_t p, size_t n)
+{
+    ptr_t ptr;
+
+    if ((ptr = realloc(p, n)) == (ptr_t) 0) {
+       child++;
+       stderror(ERR_NOMEM);
+    }
+    return (ptr);
+}
+
+ptr_t
+Calloc(size_t s, size_t n)
+{
+    ptr_t ptr;
+
+    if ((ptr = calloc(s, n)) == (ptr_t) 0) {
+       child++;
+       stderror(ERR_NOMEM);
+    }
+    return (ptr);
+}
+
+void
+Free(ptr_t p)
+{
+    if (p)
+       free(p);
+}
diff --git a/bin/csh/char.c b/bin/csh/char.c
new file mode 100644 (file)
index 0000000..dd1160c
--- /dev/null
@@ -0,0 +1,315 @@
+/* $NetBSD: char.c,v 1.10 2012/01/19 02:42:53 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)char.c     8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: char.c,v 1.10 2012/01/19 02:42:53 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include "char.h"
+
+/* on default same as original map */
+unsigned short _cmap[256] = {
+/*       0 nul           1 soh           2 stx           3 etx */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*       4 eot           5 enq           6 ack           7 bel */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*       8 bs            9 ht           10 nl           11 vt  */
+       _CTR,           _CTR|_SP|_META, _CTR|_NL|_META, _CTR,
+
+/*      12 np           13 cr           14 so           15 si  */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*      16 dle          17 dc1          18 dc2          19 dc3 */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*      20 dc4          21 nak          22 syn          23 etb */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*      24 can          25 em           26 sub          27 esc */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*      28 fs           29 gs           30 rs           31 us  */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*      32 sp           33 !            34 "            35 #   */
+       _SP|_META,      _PUN,           _QF|_PUN,       _META|_PUN,
+
+/*      36 $            37 %            38 &            39 '   */
+       _DOL|_PUN,      _PUN,           _META|_CMD|_PUN,_QF|_PUN,
+
+/*      40 (            41 )            42 *            43 +   */
+       _META|_CMD|_PUN,_META|_PUN,     _GLOB|_PUN,     _PUN,
+
+/*      44 ,            45 -            46 .            47 /   */
+       _PUN,           _PUN,           _PUN,           _PUN,
+
+/*      48 0            49 1            50 2            51 3   */
+       _DIG|_XD,       _DIG|_XD,       _DIG|_XD,       _DIG|_XD,
+
+/*      52 4            53 5            54 6            55 7   */
+       _DIG|_XD,       _DIG|_XD,       _DIG|_XD,       _DIG|_XD,
+
+/*      56 8            57 9            58 :            59 ;   */
+       _DIG|_XD,       _DIG|_XD,       _PUN,           _META|_CMD|_PUN,
+
+/*      60 <            61 =            62 >            63 ?   */
+       _META|_PUN,     _PUN,           _META|_PUN,     _GLOB|_PUN,
+
+/*      64 @            65 A            66 B            67 C   */
+       _PUN,           _LET|_UP|_XD,   _LET|_UP|_XD,   _LET|_UP|_XD,
+
+/*      68 D            69 E            70 F            71 G   */
+       _LET|_UP|_XD,   _LET|_UP|_XD,   _LET|_UP|_XD,   _LET|_UP,
+
+/*      72 H            73 I            74 J            75 K   */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*      76 L            77 M            78 N            79 O   */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*      80 P            81 Q            82 R            83 S   */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*      84 T            85 U            86 V            87 W   */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*      88 X            89 Y            90 Z            91 [   */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _GLOB|_PUN,
+
+/*      92 \            93 ]            94 ^            95 _   */
+       _ESC|_PUN,      _PUN,           _PUN,           _PUN,
+
+/*      96 `            97 a            98 b            99 c   */
+  _QB|_GLOB|_META|_PUN,        _LET|_LOW|_XD,  _LET|_LOW|_XD,  _LET|_LOW|_XD,
+
+/*     100 d           101 e           102 f           103 g   */
+       _LET|_LOW|_XD,  _LET|_LOW|_XD,  _LET|_LOW|_XD,  _LET|_LOW,
+
+/*     104 h           105 i           106 j           107 k   */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     108 l           109 m           110 n           111 o   */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     112 p           113 q           114 r           115 s   */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     116 t           117 u           118 v           119 w   */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     120 x           121 y           122 z           123 {   */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _GLOB|_PUN,
+
+/*     124 |           125 }           126 ~           127 del */
+       _META|_CMD|_PUN,_PUN,           _PUN,           _CTR,
+
+#ifdef SHORT_STRINGS
+/****************************************************************/
+/* 128 - 255 The below is supposedly ISO 8859/1                        */
+/****************************************************************/
+/*     128 (undef)     129 (undef)     130 (undef)     131 (undef)     */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*     132 (undef)     133 (undef)     134 (undef)     135 (undef)     */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*     136 (undef)     137 (undef)     138 (undef)     139 (undef)     */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*     140 (undef)     141 (undef)     142 (undef)     143 (undef)     */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*     144 (undef)     145 (undef)     146 (undef)     147 (undef)     */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*     148 (undef)     149 (undef)     150 (undef)     151 (undef)     */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*     152 (undef)     153 (undef)     154 (undef)     155 (undef)     */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*     156 (undef)     157 (undef)     158 (undef)     159 (undef)     */
+       _CTR,           _CTR,           _CTR,           _CTR,
+
+/*     160 nobreakspace 161 exclamdown 162 cent        163 sterling    */
+       _PUN, /* XXX */ _PUN,           _PUN,           _PUN,
+
+/*     164 currency    165 yen         166 brokenbar   167 section     */
+       _PUN,           _PUN,           _PUN,           _PUN,
+
+/*     168 diaeresis   169 copyright   170 ordfeminine 171 guillemotleft*/
+       _PUN,           _PUN,           _PUN,           _PUN,
+
+/*     172 notsign     173 hyphen      174 registered  175 macron      */
+       _PUN,           _PUN,           _PUN,           _PUN,
+
+/*     176 degree      177 plusminus   178 twosuperior 179 threesuperior*/
+       _PUN,           _PUN,           _PUN,           _PUN,
+
+/*     180 acute       181 mu          182 paragraph   183 periodcentered*/
+       _PUN,           _PUN, /*XXX*/   _PUN,           _PUN,
+
+/*     184 cedilla     185 onesuperior 186 masculine   187 guillemotright*/
+       _PUN,           _PUN,           _PUN,           _PUN,
+
+/*     188 onequarter  189 onehalf     190 threequarters 191 questiondown*/
+       _PUN,           _PUN,           _PUN,           _PUN,
+
+/*     192 Agrave      193 Aacute      194 Acircumflex 195 Atilde      */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*     196 Adiaeresis  197 Aring       198 AE          199 Ccedilla    */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*     200 Egrave      201 Eacute      202 Ecircumflex 203 Ediaeresis  */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*     204 Igrave      205 Iacute      206 Icircumflex 207 Idiaeresis  */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*     208 ETH         209 Ntilde      210 Ograve      211 Oacute      */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*     212 Ocircumflex 213 Otilde      214 Odiaeresis  215 multiply    */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _PUN,
+
+/*     216 Ooblique    217 Ugrave      218 Uacute      219 Ucircumflex */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_UP,
+
+/*     220 Udiaeresis  221 Yacute      222 THORN       223 ssharp      */
+       _LET|_UP,       _LET|_UP,       _LET|_UP,       _LET|_LOW,
+
+/*     224 agrave      225 aacute      226 acircumflex 227 atilde      */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     228 adiaeresis  229 aring       230 ae          231 ccedilla    */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     232 egrave      233 eacute      234 ecircumflex 235 ediaeresis  */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     236 igrave      237 iacute      238 icircumflex 239 idiaeresis  */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     240 eth         241 ntilde      242 ograve      243 oacute      */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     244 ocircumflex 245 otilde      246 odiaeresis  247 division    */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _PUN,
+
+/*     248 oslash      249 ugrave      250 uacute      251 ucircumflex */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+
+/*     252 udiaeresis  253 yacute      254 thorn       255 ydiaeresis  */
+       _LET|_LOW,      _LET|_LOW,      _LET|_LOW,      _LET|_LOW,
+#endif /* SHORT_STRINGS */
+};
+
+#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 (file)
index 0000000..966d461
--- /dev/null
@@ -0,0 +1,100 @@
+/* $NetBSD: char.h,v 1.9 2012/01/19 02:42:53 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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      8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _CHAR_H_
+#define _CHAR_H_
+
+#include <ctype.h>
+
+extern unsigned short _cmap[];
+
+#ifndef NLS
+extern unsigned char _cmap_lower[], _cmap_upper[];
+
+#endif
+
+#define        _QF     0x0001          /* '" (Forward quotes) */
+#define        _QB     0x0002          /* ` (Backquote) */
+#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 _PUN   0x8000          /* punctuation */
+
+#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
+
+#endif /* !_CHAR_H_ */
diff --git a/bin/csh/const.c b/bin/csh/const.c
new file mode 100644 (file)
index 0000000..7693580
--- /dev/null
@@ -0,0 +1,164 @@
+/* $NetBSD: const.c,v 1.10 2013/01/22 20:35:29 christos Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)const.c    8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: const.c,v 1.10 2013/01/22 20:35:29 christos Exp $");
+#endif
+#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 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 STRedit[]          = { 'e', 'd', 'i', 't', '\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 STRtildothist[]    = { '~', '/', '.', 'h', 'i', 's', 't', 'o', 'r', 
+                           'y', '\0' };
+Char STRhistfile[]     = { 'h', 'i', 's', 't', 'f', 'i', 'l', 'e', '\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 STRnoambiguous[]  = { 'n', 'o', 'a', 'm', 'b', 'i', 'g', 'u', 'o', 'u', 
+                           's', '\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 STRret[]          = { '\n', '\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 (file)
index 0000000..d4efebc
--- /dev/null
@@ -0,0 +1,2293 @@
+.\"    $NetBSD: csh.1,v 1.52 2013/01/22 21:20:26 wiz Exp $
+.\"
+.\" Copyright (c) 1980, 1990, 1993
+.\"    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. 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       8.2 (Berkeley) 1/21/94
+.\"
+.Dd January 22, 2013
+.Dt CSH 1
+.Os
+.Sh NAME
+.Nm csh
+.Nd a shell (command interpreter) with C-like syntax
+.Sh SYNOPSIS
+.Nm
+.Op Fl bcefinstvVxX
+.Op arg ...
+.Nm
+.Op Fl l
+.Sh DESCRIPTION
+The
+.Nm
+is 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.
+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.
+A login shell also can be specified by invoking the shell with the
+.Ql Fl l
+flag as the only argument.
+.Pp
+The rest of 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 not to be a terminal.
+Shells are interactive without this option if their inputs
+and outputs are terminals.
+.It Fl l
+The shell is a login shell (only applicable if
+.Fl l
+is the only flag specified).
+.It Fl m
+Read
+.Pa \&.cshrc
+even if not owned by the user.
+This flag is normally given only by
+.Xr su 1 .
+.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
+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
+`\*[Am]' `\&|' `;' `\*[Lt]' `\*[Gt]' `(' `)'
+form separate words.
+If doubled in `\*[Am]\*[Am]',
+`\&|\&|', `\*[Lt]\*[Lt]' or `\*[Gt]\*[Gt]' 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,
+`'\|', `\*(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 later.
+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 that 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 `\*[Am]'.
+.Pp
+Any of the above may be placed in `(' `)' to form a simple command (that
+may be a component of a pipeline, etc.).
+It is also possible to separate pipelines with `\&|\&|'
+or `\*[Am]\*[Am]' showing, as in the C language,
+that the second is to be executed only if the first fails or succeeds
+respectively.
+(See
+.Sx 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 `\*[Am]',
+the shell prints a line that looks like:
+.Bd -filled -offset indent
+.Op 1
+1234
+.Ed
+.Pp
+showing 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
+.Ic ^Z
+(control-Z) which sends a STOP signal to the current job.
+The shell will then normally show 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 eventually bring the job back into the foreground with
+the
+.Em foreground
+command
+.Ar fg  .
+A
+.Ic ^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
+.Ic ^Y
+that does not generate a STOP signal until a program attempts to
+.Xr read 2
+it.
+This request can usefully be typed ahead when you have prepared some commands
+for a job that 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 number 1 back into the foreground.
+Similarly saying `%1 \*[Am]' resumes job number 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 about 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 that allows generation of interrupt characters from
+the keyboard to tell jobs to stop.
+See
+.Xr stty 1
+for details on setting options in the new tty driver.
+.Ss Status reporting
+The 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
+that 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 try to exit again immediately,
+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
+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\*[Lt]escape\*[Gt]
+.Pp
+.Nm
+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\*[Lt]escape\*[Gt]
+.Pp
+.Nm
+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
+will list all file names matching the prefix.
+For example,
+the input
+.Pp
+.Dl % vi D\*[Lt]control-D\*[Gt]
+.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\*[Lt]escape\*[Gt]
+.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\*[Lt]escape\*[Gt]
+.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
+.Em do not
+nest.)
+This `!' may be preceded by a `\e' to prevent its special meaning; for
+convenience, an `!' 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 that contains history substitution is echoed on the terminal
+before it is executed as it would have been typed without history substitution.
+.Pp
+Commands input from the terminal that 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 the history list is controlled by the
+.Ar history
+variable; the previous command is always retained,
+regardless of the value of the history variable.
+Commands are numbered sequentially from 1.
+.Pp
+For example, 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 change, 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 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 \&\*[Am]
+Repeat the previous substitution.
+.It g
+Apply the change once on each word, prefixing the above, e.g., `g\*[Am]'.
+.It a
+Apply the change as many times as possible on a single word, prefixing
+the above.
+It can be used together with `g' to apply a substitution
+globally.
+.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 change 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 instead 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 `\*[Am]' in the right hand side is replaced by the text from
+the left.
+A `\e' also quotes `\*[Am]'.
+A null
+.Ar l
+(`//')
+uses the previous string either from an
+.Ar l
+or from a
+contextual scan string
+.Ar s
+in
+.No \&`!? Ns Ar s Ns \e?' .
+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., `!$'.
+Here, 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 that follow.
+Thus, after `ls \-ld ~paul' we might do `!{l}a' to do `ls \-ld ~paula',
+while `!la' would look for a command starting with `la'.
+.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 Substitution
+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 that 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 that 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 that
+.Ar pr Ns '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 that 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 additional 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
+.Sx 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 variable's 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 that 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 that 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.
+If
+.Ar name
+is not a shell variable, but is set in the environment, then
+that value is returned (but `:' modifiers and the other forms
+given below are not available here).
+.It $name Ns Op selector
+.It ${name Ns [ 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 variable's value is numbered `1'.
+If the first number of a range is omitted it defaults to `1'.
+If the last number 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[*]'.
+.El
+.Pp
+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.
+.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 $!
+Substitute the (decimal) process number of the last background process
+started by this shell.
+.It $\*[Lt]
+Substitutes a line from the standard
+input, with no further interpretation.
+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.
+By selectively, we mean that portions of expressions which are
+not evaluated are not subjected to these expansions.
+For commands that 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 shown 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 replaces 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 that 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 (inclusive).
+.Pp
+The character `~' at the beginning of a filename refers to home
+directories.
+Standing alone, i.e., `~' it expands to the invoker's 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 does not appear 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'
+without 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 the match to `*box'.)
+As a special case `{', `}' and `{}' are passed undisturbed.
+.Ss Input/output
+The standard input and the standard output of a command may be redirected
+with the following syntax:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It \*[Lt] name
+Open file
+.Ar name
+(which is first variable, command and filename expanded) as the standard
+input.
+.It \*[Lt]\*[Lt] word
+Read the shell input up to a line that 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 the 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 that 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 that
+is given to the command as its standard input.
+.It \*[Gt] name
+.It \*[Gt]! name
+.It \*[Gt]\*[Am] name
+.It \*[Gt]\*[Am]! name
+The file
+.Ar name
+is used as the standard output.
+If the file does not exist then it is created;
+if the file exists, it is truncated; its previous contents are 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.
+Here, the `!' forms can be used to suppress this check.
+.Pp
+The forms involving `\*[Am]' route the standard error output into the specified
+file as well as the standard output.
+.Ar Name
+is expanded in the same way as `\*[Lt]' input filenames are.
+.It \*[Gt]\*[Gt] name
+.It \*[Gt]\*[Gt]\*[Am] name
+.It \*[Gt]\*[Gt]! name
+.It \*[Gt]\*[Gt]\*[Am]! name
+Uses file
+.Ar name
+as the standard output;
+like `\*[Gt]' 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 `\*[Gt]'.
+.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;
+instead they receive the original standard input of the shell.
+The `\*[Lt]\*[Lt]' 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 ;
+instead 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
+The standard error output may be directed through
+a pipe with the standard output.
+Simply use the form `\&|\*[Am]' instead of just `\&|'.
+.Ss Expressions
+Several of the builtin commands (to be described later)
+take expressions, in which the operators are similar to those of C, with
+the same precedence, but with the
+.Em opposite grouping :
+right to left.
+These expressions appear in the
+.Ar @ ,
+.Ar exit ,
+.Ar if ,
+and
+.Ar while
+commands.
+The following operators are available:
+.Bd -ragged -offset indent
+\&|\&|  \*[Am]\*[Am]  \&| \*(ua  \*[Am]  ==  !=  =~  !~  \*[Le]  \*[Ge]
+\*[Lt]  \*[Gt] \*[Lt]\*[Lt]  \*[Gt]\*[Gt]  +  \-  *  /  %  !  ~  (  )
+.Ed
+.Pp
+Here the precedence increases to the right,
+`==' `!=' `=~' and `!~', `\*[Le]' `\*[Ge]' `\*[Lt]'
+and `\*[Gt]', `\*[Lt]\*[Lt]' and `\*[Gt]\*[Gt]', `+' 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 that 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 that
+are syntactically significant to the parser
+(`\*[Am]' `\&|' `\*[Lt]' `\*[Gt]' `(' `)'),
+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 an expression and the variable
+.Ar status
+examined.
+.Ss Control flow
+The shell contains several commands that 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, because of 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 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 shell's 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 % Ns 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 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 that 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 showing 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
+that 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
+instead of oldest first.
+The
+.Fl h
+option causes the history list to be printed without leading numbers.
+This format produces files suitable for sourcing using the \-h
+option to
+.Ic source  .
+.Pp
+.It Ic if 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, i.e., when command is
+.Em 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 up to the first
+.Ic else
+are executed; otherwise if
+.Ar expr2
+is true then the commands up 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; the
+.Fl l
+option lists process id's in addition to the normal information.
+.Pp
+.It Ic kill % Ns Ar job
+.It Ic kill Ar pid ...
+.It Ic kill Fl l Op Ar exit_status
+.It Ic kill Fl s Ar signal_name pid ...
+.It Ic kill Fl Ar signal_name Ar pid ...
+.It Ic kill Fl Ar signal_number Ar pid ...
+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
+.In signal.h ,
+stripped of the prefix ``SIG'').
+The signal names are listed by ``kill \-l'';
+if an
+.Ar exit_status
+is specified, only the corresponding signal name will be written.
+There is no default, just saying `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
+Manipulates per-process system resource limits via the
+.Xr getrlimit 2
+and
+.Xr setrlimit 2
+system calls; this
+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.
+.Pp
+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:
+.Bl -tag -width coredumpsize
+.It Ar cputime
+The maximum number of CPU-seconds to be used by each process.
+.It Ar filesize
+The largest single file (in bytes) that can be created.
+.It Ar datasize
+The maximum growth of the data+stack region via
+.Xr sbrk 2
+beyond the end of the program text.
+.It Ar stacksize
+The maximum
+size of the automatically-extended stack region.
+.It Ar coredumpsize
+The size of the largest core dump (in bytes) that will be created.
+.It Ar memoryuse
+The maximum size (in bytes) to which a process's resident set
+size (RSS) may grow.
+.It Ar memorylocked
+The maximum size (in bytes) which a process may lock into memory using the
+.Xr mlock 2
+function.
+.It Ar maxproc
+The maximum number of simultaneous processes for this user id.
+.It Ar openfiles
+The maximum number of simultaneous open files for this user id.
+.It Ar sbsize
+The maximum socket buffer size of a process (in bytes).
+.It Ar vmemoryuse
+The maximum size (in bytes) which a process can obtain.
+.El
+.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 scale is `seconds';
+a scale factor of `m' for minutes
+or `h' for hours, or a time of the form `mm:ss' giving minutes
+and seconds also may be used.
+.Pp
+For both
+.Ar resource
+names and scale factors, unambiguous prefixes
+of the names suffice.
+.Pp
+Limits of an arbitrary process can be displayed or set using the
+.Xr sysctl 8
+utility.
+See the
+.Xr getrlimit 2
+and
+.Xr setrlimit 2
+man pages for an additional description of system resource limits.
+.Pp
+.It Ic login
+Terminate a login shell, replacing it with an instance of
+.Pa /usr/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 ...'.
+.Ar 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 `\*[Am]' are effectively
+.Ic nohup Ns \'ed .
+.Pp
+.It Ic notify
+.It Ic notify % Ns Ar job ...
+Causes the shell to notify the user asynchronously when the status of the
+current or specified jobs change; 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.
+Finally
+.Ic onintr
+statements are ignored in the system startup files where interrupts
+are disabled (/etc/csh.cshrc, /etc/csh.login).
+.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 members of the directory stack are numbered from the top starting at 0.
+.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 cwd )
+onto the directory stack.
+With a numeric argument,
+.Ic pushd
+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 a system directory.
+.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 that have other than a single word as their
+value print as a parenthesized word list.
+The second form sets
+.Ar name
+to the null string.
+The third form sets
+.Ar name
+to the single
+.Ar word .
+The fourth form sets
+the
+.Ar index Ns 'th
+component of
+.Ar name
+to
+.Ar word ;
+this component must already exist.
+The final form sets
+.Ar name
+to the list of words in
+.Ar wordlist .
+The value is always 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
+.It Ic setenv Ar name value
+The first form lists all current environment variables.
+It is equivalent to
+.Xr printenv 1 .
+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 variables
+.Ev USER ,
+.Ev TERM ,
+and
+.Ev PATH
+are automatically imported to and exported from the
+.Nm
+variables
+.Ar user ,
+.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 Ns 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
+.Ar 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 on the
+history list without being executed.
+.Pp
+.It Ic stop
+.It Ic stop % Ns Ar job ...
+Stops the current or specified jobs that are 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 the `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 the default label 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 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
+Wait for all background jobs.
+If the shell is interactive, then an interrupt can disrupt the wait.
+After the interrupt, the shell prints names and job numbers of all jobs
+known to be outstanding.
+.Pp
+.It Ic which Ar command
+Displays the resolved command that will be executed by the shell.
+.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
+.Ic 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 % Ns Ar job
+Brings the specified job into the foreground.
+.Pp
+.It Ic % Ns Ar job Ic \*[Am]
+Continues the specified job in the background.
+.Pp
+.It Ic @
+.It Ic @ Ar name Ns = expr
+.It Ic @ 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 `\*[Lt]', `\*[Gt]', `\*[Am]' 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 done
+explicitly by the user.
+.Pp
+The 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
+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 alternative 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 `^' in
+quick substitutions.
+.It Ic histfile
+Can be set to the pathname where history is going to be saved/restored.
+.It Ic history
+Can be given a numeric value to control the size of the history list.
+Any command that 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 checking is done after each command completion that 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 ensure that
+files are not accidentally destroyed, and that `\*[Gt]\*[Gt]' redirections
+refer to existing files.
+.It Ic noglob
+If set, filename expansion is inhibited.
+This inhibition is most useful in shell scripts that
+ 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; instead 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 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 that 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 that 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 that 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.
+If
+.Ar savehist
+is just set, the shell will use the value of
+.Ar history .
+.It Ic shell
+The file in which the shell resides.
+This variable is used in forking shells to interpret files that 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 that fail return exit status `1',
+all other builtin commands set status to `0'.
+.It Ic time
+Controls automatic timing of commands.
+This setting allows two parameters.
+The first specifies the CPU time threshold at which reporting should be done
+for a process, and the optional second specifies the output format.
+The following format strings are available:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Li \&%c
+Number of involuntary context switches.
+.It Li \&%D
+Average unshared data size.
+.It Li \&%E
+Elapsed (wall\-clock) time.
+.It Li \&%F
+Page faults.
+.It Li \&%I
+Filesystem blocks in.
+.It Li \&%K
+Average total data memory used.
+.It Li \&%k
+Number of signals received.
+.It Li \&%M
+Maximum Resident Set Size.
+.It Li \&%O
+Filesystem blocks out.
+.It Li \&%P
+Total percent time spent running.
+.It Li \&%R
+Page reclaims.
+.It Li \&%r
+Socket messages received.
+.It Li \&%S
+Total system CPU time used.
+.It Li \&%s
+Socket messages sent.
+.It Li \&%U
+Total user CPU time used.
+.It Li \&%W
+Number of swaps.
+.It Li \&%w
+Number of voluntary context switches (waits).
+.It Li \&%X
+Average shared text size.
+.El
+.Pp
+The default summary is "%Uu %Ss %E %P %X+%Dk %I+%Oio %Fpf+%Ww"
+.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 shortcut greatly speeds command location when many 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
+that 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 change.
+.Ss Signal handling
+The shell normally ignores
+.Ar quit
+signals.
+Jobs running detached (either by
+.Ic \&\*[Am]
+or the
+.Ic bg
+or
+.Ic %... \*[Am]
+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.
+Interrupts are not allowed when a login shell is reading the file
+.Pa \&.logout .
+.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 `\*[Lt]\*[Lt]'.
+.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 that 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 pipe 2 ,
+.Xr setrlimit 2 ,
+.Xr sigaction 2 ,
+.Xr umask 2 ,
+.Xr wait 2 ,
+.Xr killpg 3 ,
+.Xr tty 4 ,
+.Xr a.out 5 ,
+.Xr environ 7 ,
+.Xr sysctl 8
+.Rs
+.%T "An introduction to the C shell"
+.Re
+.Sh HISTORY
+.Nm
+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 that also have these mechanisms, plus
+a few more (and maybe some bugs too), which are available through the
+usenet.
+.Sh AUTHORS
+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 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 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 instead of aliases.
+.Pp
+Commands within loops, prompted for by `?', are not placed on the
+.Ic history
+list.
+Control structure should be parsed instead of being recognized as built-in
+commands.
+This would allow control commands to be placed anywhere,
+to be combined with `\&|', and to be used with `\*[Am]' and `;' metasyntax.
+.Pp
+It should be possible to use the `:' modifiers on the output of command
+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 (file)
index 0000000..f03f78a
--- /dev/null
@@ -0,0 +1,1401 @@
+/* $NetBSD: csh.c,v 1.46 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993\
+ The Regents of the University of California.  All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)csh.c      8.2 (Berkeley) 10/12/93";
+#else
+__RCSID("$NetBSD: csh.c,v 1.46 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <paths.h>     /* should this be included in pathnames.h instead? */
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <vis.h>
+
+#include "csh.h"
+#include "extern.h"
+#include "pathnames.h"
+#include "proc.h"
+
+/*
+ * 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 *tildehist[] = {STRsource, STRmh, STRtildothist, 0};
+
+int nofile = 0;
+int batch = 0;
+int enterhist = 0;
+int fast = 0;
+int mflag = 0;
+int nexececho = 0;
+int nverbose = 0;
+int prompt = 1;
+int quitit = 0;
+int reenter = 0;
+
+extern char **environ;
+
+static ssize_t readf(void *, void *, size_t);
+static off_t seekf(void *, off_t, int);
+static ssize_t writef(void *, const void *, size_t);
+static int closef(void *);
+static int srccat(Char *, Char *);
+static int srcfile(const char *, int, int);
+__dead static void phup(int);
+static void srcunit(int, int, int);
+static void mailchk(void);
+#ifndef _PATH_DEFPATH
+static Char **defaultpath(void);
+#endif
+#ifdef EDITING
+int    editing = 0;
+#endif
+
+int
+main(int argc, char *argv[])
+{
+    struct sigaction oact;
+    Char *cp;
+    char *tcp, **tempv;
+    const char *ecp;
+    sigset_t nsigset;
+    int f;
+
+    cshin = stdin;
+    cshout = stdout;
+    csherr = stderr;
+
+    setprogname(argv[0]);
+    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();
+    euid = geteuid();
+    egid = getegid();
+    /*
+     * 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++;)
+           continue;
+       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++)
+           continue;
+       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();
+    /*
+     * XXX: This is to keep programs that use stdio happy.
+     *     what we really want is freunopen() ....
+     *     Closing cshin cshout and csherr (which are really stdin stdout
+     *     and stderr at this point and then reopening them in the same order
+     *     gives us again stdin == cshin stdout == cshout and stderr == csherr.
+     *     If that was not the case builtins like printf that use stdio
+     *     would break. But in any case we could fix that with memcpy and
+     *     a bit of pointer manipulation...
+     *     Fortunately this is not needed under the current implementation
+     *     of stdio.
+     */
+    (void)fclose(cshin);
+    (void)fclose(cshout);
+    (void)fclose(csherr);
+    if (!(cshin  = funopen2((void *) &SHIN,  readf, writef, seekf, NULL,
+       closef)))
+       exit(1);
+    if (!(cshout = funopen2((void *) &SHOUT, readf, writef, seekf, NULL,
+       closef)))
+       exit(1);
+    if (!(csherr = funopen2((void *) &SHERR, readf, writef, seekf, NULL,
+       closef)))
+       exit(1);
+    (void)setvbuf(cshin,  NULL, _IOLBF, 0);
+    (void)setvbuf(cshout, NULL, _IOLBF, 0);
+    (void)setvbuf(csherr, NULL, _IOLBF, 0);
+
+    /*
+     * 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 ((ecp = getenv("HOME")) != NULL)
+       cp = quote(SAVE(ecp));
+    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 ((ecp = getenv("LOGNAME")) != NULL ||
+       (ecp = getenv("USER")) != NULL)
+       set(STRuser, quote(SAVE(ecp)));
+    if ((ecp = getenv("TERM")) != NULL)
+       set(STRterm, quote(SAVE(ecp)));
+
+    /*
+     * Re-initialize path if set in environment
+     */
+    if ((ecp = getenv("PATH")) == NULL) {
+#ifdef _PATH_DEFPATH
+       importpath(str2short(_PATH_DEFPATH));
+#else
+       setq(STRpath, defaultpath(), &shvhed);
+#endif
+    } else {
+       importpath(str2short(ecp));
+    }
+
+    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)sigaction(SIGINT, NULL, &oact);
+    parintr = oact.sa_handler;
+    (void)sigaction(SIGTERM, NULL, &oact);
+    parterm = oact.sa_handler;
+
+    /* catch these all, login shell or not */
+    (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 = FSHERR;
+               break;
+           default:
+               stderror(ERR_SYSTEM, tempv[0], strerror(errno));
+               /* NOTREACHED */
+           }
+       (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 != euid || gid != egid)) {
+           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);
+       sigemptyset(&nsigset);
+       (void)sigaddset(&nsigset, SIGINT);
+       (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
+       (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(FSHERR))
+               f = FSHERR;
+           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:
+#ifndef __minix /* MINIX3 has no job control.  No need to report this. */
+               (void)fprintf(csherr, "Warning: no access to tty (%s).\n",
+                              strerror(errno));
+               (void)fprintf(csherr, "Thus no job control in this shell.\n");
+#else /* __minix */
+               /* nothing */;
+#endif /* __minix */
+           }
+       }
+    }
+    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 */
+       {
+           sig_t oparintr;
+           sigset_t osigset;
+           int osetintr;
+
+           oparintr = parintr;
+           osetintr = setintr;
+           sigemptyset(&nsigset);
+           (void)sigaddset(&nsigset, SIGINT);
+           (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+
+           setintr = 0;
+           parintr = SIG_IGN;  /* Disable onintr */
+#ifdef _PATH_DOTCSHRC
+           (void)srcfile(_PATH_DOTCSHRC, 0, 0);
+#endif
+           if (!fast && !arginp && !onelflg)
+               dohash(NULL, NULL);
+#ifdef _PATH_DOTLOGIN
+           if (loginsh)
+               (void)srcfile(_PATH_DOTLOGIN, 0, 0);
+#endif
+           (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+           setintr = osetintr;
+           parintr = oparintr;
+       }
+       (void)srccat(value(STRhome), STRsldotcshrc);
+
+       if (!fast && !arginp && !onelflg && !havhash)
+           dohash(NULL, NULL);
+       /*
+        * Source history before .login so that it is available in .login
+        */
+       if ((cp = value(STRhistfile)) != STRNULL)
+           tildehist[2] = cp;
+       dosource(tildehist, NULL);
+        if (loginsh)
+             (void)srccat(value(STRhome), STRsldotlogin);
+    }
+
+    /*
+     * 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) {
+           (void)fprintf(cshout, "logout\n");
+           (void)close(SHIN);
+           child = 1;
+           goodbye();
+       }
+       else {
+           (void)fprintf(cshout, "exit\n");
+       }
+    }
+    rechist();
+    exitstat();
+    /* NOTREACHED */
+}
+
+void
+untty(void)
+{
+    if (tpgrp > 0) {
+       (void)setpgid(0, opgrp);
+       (void)tcsetpgrp(FSHTTY, opgrp);
+    }
+}
+
+void
+importpath(Char *cp)
+{
+    Char *dp, **pv;
+    int c, i;
+
+    i = 0;
+    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;
+    setq(STRpath, pv, &shvhed);
+}
+
+/*
+ * Source to the file which is the catenation of the argument names.
+ */
+static int
+srccat(Char *cp, Char *dp)
+{
+    Char *ep;
+    char *ptr;
+
+    ep = Strspl(cp, dp);
+    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(const char *f, int onlyown, int flag)
+{
+    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(int unit, int onlyown, int hflg)
+{
+    /* We have to push down a lot of state here */
+    /* All this could go into a structure */
+    struct whyle *oldwhyl;
+    struct Bin saveB;
+    sigset_t nsigset, osigset;
+    jmp_buf oldexit;
+    Char *oarginp, *oevalp, **oevalvec, *ogointr;
+    Char OHIST;
+    int oSHIN, oinsource, oldintty, oonelflg; 
+    int oenterhist, otell;      
+    /* The (few) real local variables */
+    int my_reenter;
+
+    oSHIN = -1;
+    oldintty = intty;
+    oinsource = insource;
+    oldwhyl = whyles;
+    ogointr = gointr;
+    oarginp = arginp;
+    oevalp = evalp;
+    oevalvec = evalvec;
+    oonelflg = onelflg;
+    oenterhist = enterhist;
+    OHIST = HIST;
+    otell = cantell;
+
+    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);
+
+    if (setintr) {
+       sigemptyset(&nsigset);
+       (void)sigaddset(&nsigset, SIGINT);
+       (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+    }
+    /* Setup the new values of the state stuff saved above */
+    (void)memcpy(&saveB, &B, 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)sigprocmask(SIG_SETMASK, &osigset, NULL);
+    settell();
+
+    if ((my_reenter = setexit()) == 0)
+       process(0);                             /* 0 -> blow away on errors */
+
+    if (setintr)
+       (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+    if (oSHIN >= 0) {
+       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 */
+       (void)memcpy(&B, &saveB, 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(void)
+{
+    Char buf[BUFSIZE], hbuf[BUFSIZE], *hfile;
+    int fp, ftmp, oldidfds;
+    struct varent *shist;
+
+    if (!fast) {
+       /*
+        * If $savehist is just set, we use the value of $history
+        * else we use the value in $savehist
+        */
+       if ((shist = adrof(STRsavehist)) != NULL) {
+           if (shist->vec[0][0] != '\0')
+               (void)Strcpy(hbuf, shist->vec[0]);
+           else if ((shist = adrof(STRhistory)) && shist->vec[0][0] != '\0')
+               (void)Strcpy(hbuf, shist->vec[0]);
+           else
+               return;
+       }
+       else
+           return;
+
+       if ((hfile = value(STRhistfile)) == STRNULL) {
+           hfile = Strcpy(buf, value(STRhome));
+           (void) Strcat(buf, STRsldthist);
+       }
+
+       if ((fp = open(short2str(hfile), O_WRONLY | O_CREAT | O_TRUNC,
+           0600)) == -1) 
+           return;
+
+       oldidfds = didfds;
+       didfds = 0;
+       ftmp = SHOUT;
+       SHOUT = fp;
+       dumphist[2] = hbuf;
+       dohist(dumphist, NULL);
+       SHOUT = ftmp;
+       (void)close(fp);
+       didfds = oldidfds;
+    }
+}
+
+void
+goodbye(void)
+{
+    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();
+    /* NOTREACHED */
+}
+
+__dead void
+exitstat(void)
+{
+    Char *s;
+#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;
+    s = value(STRstatus);
+    xexit(s ? getn(s) : 0);
+    /* NOTREACHED */
+}
+
+/*
+ * in the event of a HUP we want to save the history
+ */
+static void
+phup(int sig)
+{
+    rechist();
+
+    /*
+     * We kill the last foreground process group. It then becomes
+     * responsible to propagate the SIGHUP to its progeny. 
+     */
+    {
+       struct process *pp, *np;
+
+       for (pp = proclist.p_next; pp; pp = pp->p_next) {
+           np = pp;
+           /* 
+            * Find if this job is in the foreground. It could be that
+            * the process leader has exited and the foreground flag
+            * is cleared for it.
+            */
+           do
+               /*
+                * If a process is in the foreground; we try to kill
+                * its process group. If we succeed, then the 
+                * whole job is gone. Otherwise we keep going...
+                * But avoid sending HUP to the shell again.
+                */
+               if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp &&
+                   kill(-np->p_jobid, SIGHUP) != -1) {
+                   /* In case the job was suspended... */
+                   (void)kill(-np->p_jobid, SIGCONT);
+                   break;
+               }
+           while ((np = np->p_friends) != pp);
+       }
+    }
+    xexit(sig);
+    /* NOTREACHED */
+}
+
+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(int notused)
+{
+    pintr1(1);
+    /* NOTREACHED */
+}
+
+void
+pintr1(int wantnl)
+{
+    Char **v;
+    sigset_t nsigset, osigset;
+
+    sigemptyset(&nsigset);
+    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+    if (setintr) {
+       nsigset = osigset;
+       (void)sigdelset(&nsigset, SIGINT);
+       (void)sigprocmask(SIG_SETMASK, &nsigset, NULL);
+       if (pjobs) {
+           pjobs = 0;
+           (void)fprintf(cshout, "\n");
+           dojobs(jobargv, NULL);
+           stderror(ERR_NAME | ERR_INTR);
+       }
+    }
+    (void)sigdelset(&osigset, SIGCHLD);
+    (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+    (void)fpurge(cshout);
+    (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) {
+       gotolab(gointr);
+       timflg = 0;
+       if ((v = pargv) != NULL)
+           pargv = 0, blkfree(v);
+       if ((v = gargv) != NULL)
+           gargv = 0, blkfree(v);
+       reset();
+    }
+    else if (intty && wantnl) {
+       (void)fputc('\r', cshout);
+       (void)fputc('\n', cshout);
+    }
+    stderror(ERR_SILENT);
+    /* NOTREACHED */
+}
+
+/*
+ * 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.
+ */
+static struct command *savet = NULL;
+
+void
+process(int catch)
+{
+    struct command *t;
+    jmp_buf osetexit;
+    sigset_t nsigset;
+
+    t = savet;    
+    savet = NULL;
+    getexit(osetexit);
+    for (;;) {
+       pendjob();
+       paraml.next = paraml.prev = &paraml;
+       paraml.word = STRNULL;
+       (void)setexit();
+       justpr = enterhist;     /* execute if not entering history */
+
+       /*
+        * Interruptible during interactive reads
+        */
+       if (setintr) {
+           sigemptyset(&nsigset);
+           (void)sigaddset(&nsigset, SIGINT);
+           (void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
+       }
+
+       /*
+        * For the sake of reset()
+        */
+       freelex(&paraml);
+       if (savet)
+           freesyn(savet), savet = NULL;
+
+       if (haderr) {
+           if (!catch) {
+               /* unwind */
+               doneinp = 0;
+               resexit(osetexit);
+               savet = t;
+               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 (aret == F_SEEK && fseekp == feobp)
+               printprompt();
+           (void)fflush(cshout);
+       }
+       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)) {
+           int odidfds = didfds;
+           fflush(csherr);
+           didfds = 0;
+           prlex(csherr, &paraml);
+           fflush(csherr);
+           didfds = odidfds;
+       }
+
+       /*
+        * The parser may lose space if interrupted.
+        */
+       if (setintr)
+           (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
+
+       /*
+        * 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.
+        */
+       savet = syntax(paraml.next, &paraml, 0);
+       if (seterr)
+           stderror(ERR_OLD);
+
+       execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
+
+       /*
+        * Made it!
+        */
+       freelex(&paraml);
+       freesyn((struct command *) savet), savet = NULL;
+    }
+    resexit(osetexit);
+    savet = t;
+}
+
+void
+/*ARGSUSED*/
+dosource(Char **v, struct command *t)
+{
+    Char buf[BUFSIZE], *f;
+    int hflg;
+
+    hflg = 0;
+    v++;
+    if (*v && eq(*v, STRmh)) {
+       if (*++v == NULL)
+           stderror(ERR_NAME | ERR_HFLAG);
+       hflg++;
+    }
+    (void)Strcpy(buf, *v);
+    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(void)
+{
+    struct stat stb;
+    struct varent *v;
+    Char **vp;
+    time_t t;
+    int cnt, intvl;
+    int 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)
+           (void)fprintf(cshout, "You have %smail.\n", new ? "new " : "");
+       else
+           (void)fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail",
+                          vis_str(*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(Char *home)
+{
+    struct passwd *pw;
+    Char *h;
+
+    /*
+     * Is it us?
+     */
+    if (*home == '\0') {
+       if ((h = value(STRhome)) != NULL) {
+           (void)Strcpy(home, h);
+           return 0;
+       }
+       else
+           return 1;
+    }
+
+    if ((pw = getpwnam(short2str(home))) != NULL) {
+       (void)Strcpy(home, str2short(pw->pw_dir));
+       return 0;
+    }
+    else
+       return 1;
+}
+
+/*
+ * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17
+ * We also check if the shell has already changed the descriptor to point to
+ * 0, 1, 2 when didfds is set.
+ */
+#define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0))
+
+static ssize_t
+readf(void *oreo, void *buf, size_t siz)
+{
+    return read(DESC(oreo), buf, siz);
+}
+
+
+static ssize_t
+writef(void *oreo, const void *buf, size_t siz)
+{
+    return write(DESC(oreo), buf, siz);
+}
+
+static off_t
+seekf(void *oreo, off_t off, int whence)
+{
+    return lseek(DESC(oreo), off, whence);
+}
+
+
+static int
+closef(void *oreo)
+{
+    return close(DESC(oreo));
+}
+
+
+/*
+ * Print the visible version of a string.
+ */
+int
+vis_fputc(int ch, FILE *fp)
+{
+    char uenc[5];      /* 4 + NULL */
+
+    if (ch & QUOTE) 
+       return fputc(ch & TRIM, fp);
+    /* 
+     * XXX: When we are in AsciiOnly we want all characters >= 0200 to
+     * be encoded, but currently there is no way in vis to do that.
+     */
+    (void)vis(uenc, ch & TRIM, VIS_NOSLASH, 0);
+    return (fputs(uenc, fp));
+}
+
+/*
+ * Move the initial descriptors to their eventual
+ * resting places, closing all other units.
+ */
+void
+initdesc(void)
+{
+    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(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL);
+    (void)ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL);
+    closem();
+}
+
+
+__dead void
+#ifdef PROF
+done(int i)
+#else
+xexit(int i)
+#endif
+{
+    untty();
+    _exit(i);
+    /* NOTREACHED */
+}
+
+#ifndef _PATH_DEFPATH
+static Char **
+defaultpath(void)
+{
+    struct stat stb;
+    Char **blk, **blkp;
+    char *ptr;
+
+    blkp = blk = xmalloc((size_t) sizeof(Char *) * 10);
+
+#define DIRAPPEND(a)  \
+       if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
+               *blkp++ = SAVE(ptr)
+#ifdef RESCUEDIR
+    DIRAPPEND(RESCUEDIR);
+#endif
+    DIRAPPEND(_PATH_BIN);
+    DIRAPPEND(_PATH_USRBIN);
+
+#undef DIRAPPEND
+
+#if 0
+    if (euid != 0 && uid != 0)
+       *blkp++ = Strsave(STRdot);
+#endif
+
+    *blkp = NULL;
+    return (blk);
+}
+#endif /* _PATH_DEFPATH */
+
+void
+printprompt(void)
+{
+    Char *cp;
+
+    if (editing)
+       return;
+
+    if (!whyles) {
+       for (cp = value(STRprompt); *cp; cp++)
+           if (*cp == HIST)
+               (void)fprintf(cshout, "%d", eventno + 1);
+           else {
+               if (*cp == '\\' && cp[1] == HIST)
+                   cp++;
+               (void)vis_fputc(*cp | QUOTE, cshout);
+           }
+    }
+    else
+       /*
+        * Prompt for forward reading loop body content.
+        */
+       (void)fprintf(cshout, "? ");
+    (void)fflush(cshout);
+}
+
+#ifdef EDIT
+char *
+printpromptstr(EditLine *elx) {
+    static char pbuf[1024];
+    static char qspace[] = "? ";
+    Char *cp;
+    size_t i;
+
+    if (whyles)
+       return qspace;
+
+    i = 0;
+    for (cp = value(STRprompt); *cp; cp++) {
+       if (i >= sizeof(pbuf))
+           break;
+       if (*cp == HIST) {
+           int r;
+           r = snprintf(pbuf + i, sizeof(pbuf) - i, "%d", eventno + 1);
+           if (r > 0)
+               i += (size_t)r;
+       } else
+           pbuf[i++] = (char)*cp;
+    }
+    if (i >= sizeof(pbuf))
+       i = sizeof(pbuf) - 1;
+    pbuf[i] = '\0';
+    return pbuf;
+}
+#endif
diff --git a/bin/csh/csh.h b/bin/csh/csh.h
new file mode 100644 (file)
index 0000000..6a186d7
--- /dev/null
@@ -0,0 +1,559 @@
+/* $NetBSD: csh.h,v 1.26 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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       8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _CSH_H_
+#define _CSH_H_
+
+/*
+ * Fundamental definitions which may vary from system to system.
+ *
+ *     BUFSIZE         The i/o buffering size; also limits word size
+ *     MAILINTVL       How often to mailcheck; more often is more expensive
+ */
+#ifndef BUFSIZE
+#define        BUFSIZE 4096            /* default buffer size */
+#endif                         /* BUFSIZE */
+
+#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        FSHERR 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
+
+/*
+ * Make sure a variable is not stored in a register by taking its address
+ * This is used where variables might be clobbered by longjmp.
+ */
+#define UNREGISTER(a)  (void) &a
+
+typedef void *ioctl_t;         /* Third arg of ioctl */
+
+typedef void *ptr_t;
+
+#include "const.h"
+#include "char.h"
+#include "errnum.h"
+
+#define xmalloc(i) Malloc(i)
+#define xrealloc(p, i) Realloc(p, i)
+#define xcalloc(n, s) Calloc(n, s)
+#define xfree(p) Free(p)
+
+#include <stdio.h>
+FILE *cshin, *cshout, *csherr;
+
+#define        isdir(d) (S_ISDIR(d.st_mode))
+
+#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
+ */
+int child;                     /* Child shell ... errors cause exit */
+int chkstop;                   /* Warned of stopped jobs... allow exit */
+int didfds;                    /* Have setup i/o fd's for child */
+int doneinp;                   /* EOF indicator after reset from readc */
+int exiterr;                   /* Exit if error or non-zero exit status */
+int haderr;                    /* Reset was because of an error */
+int havhash;                   /* path hashing is available */
+int intact;                    /* We are interactive... therefore prompt */
+int intty;                     /* Input is a tty */
+int justpr;                    /* Just print because of :p hist mod */
+int loginsh;                   /* We are a loginsh -> .login/.logout */
+int neednote;                  /* Need to pnotify() */
+int noexec;                    /* Don't execute, just syntax check */
+int pjobs;                     /* want to print jobs if interrupted */
+int setintr;                   /* Set interrupts on/off -> Wait intr... */
+int timflg;                    /* Time the next waited for command */
+
+#ifdef FILEC
+extern int filec;              /* doing filename expansion */
+#endif
+
+/*
+ * Global i/o info
+ */
+Char *arginp;                  /* Argument input for sh -c and internal `xx` */
+Char *ffile;                   /* Name of shell file for $0 */
+int onelflg;                   /* 2 -> need line for -t, 1 -> exit on read */
+
+extern char *seterr;           /* Error message from scanner/parser */
+Char *shtemp;                  /* Temp name for << shell files in /tmp */
+
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+struct timespec time0;         /* Time at which the shell started */
+struct rusage ru0;
+
+/*
+ * Miscellany
+ */
+time_t chktim;                 /* Time mail last checked */
+Char *doldol;                  /* Character pid for $$ */
+pid_t backpid;                 /* Pid of the last background process */
+gid_t egid, gid;               /* Invokers gid */
+uid_t euid, uid;               /* Invokers uid */
+int shpgrp;                    /* Pgrp of shell */
+int tpgrp;                     /* Terminal process group */
+
+/* If tpgrp is -1, leave tty alone! */
+int opgrp;                     /* Initial pgrp and tty pgrp */
+
+
+/*
+ * 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 F{SHIN,...}.
+ */
+int SHIN;                      /* Current shell input (script) */
+int SHOUT;                     /* Shell output */
+int SHERR;                     /* Diagnostic output... shell errs go here */
+int 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) (void)memcpy((a), reslab, sizeof reslab)
+#define        resexit(a) (void)memcpy(reslab, (a), 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/sixteenth bit of characters is used to prevent recognition,
+ * and eventually stripped.
+ */
+#define        META 0x80
+#define        ASCII 0x7f
+#ifdef SHORT_STRINGS
+#define        CHAR ((Char)0xff)
+#define        QUOTE ((Char)0x8000)    /* 16nth char bit used for 'ing */
+#define        TRIM ((Char)0x7fff)     /* Mask to strip quote bit */
+#else
+#define        CHAR ((Char)0x7f)
+#define        QUOTE ((Char)0x80)      /* Eighth char bit used for 'ing */
+#define        TRIM ((Char)0x7f)       /* 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 */
+    int Bfblocks;              /* Number of buffer blocks */
+    Char **Bfbuf;              /* The array of buffer blocks */
+}       B;
+
+/*
+ * This structure allows us to seek inside aliases
+ */
+struct Ain {
+    int type;
+#define I_SEEK -1              /* Invalid seek */
+#define A_SEEK 0               /* Alias seek */
+#define F_SEEK 1               /* File seek */
+#define E_SEEK 2               /* Eval seek */
+    union {
+       off_t _f_seek;
+       Char* _c_seek;
+    } fc;
+#define f_seek fc._f_seek
+#define c_seek fc._c_seek
+    Char **a_seek;
+} ;
+extern int aret;               /* What was the last character returned */
+#define SEEKEQ(a, b) ((a)->type == (b)->type && \
+                     (a)->f_seek == (b)->f_seek && \
+                     (a)->a_seek == (b)->a_seek)
+
+#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.
+ */
+struct Ain lineloc;
+
+int 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/execution pass (sh.sem.c).
+ */
+struct command {
+    int 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              */
+    int 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       */
+    int t_nice;
+};
+
+
+/*
+ * These are declared here because they want to be
+ * initialized in sh.init.c (to allow them to be made readonly)
+ */
+
+extern struct biltins {
+    const char *bname;
+    void (*bfunct)(Char **, struct command *);
+    short minargs, maxargs;
+} bfunc[];
+
+extern int nbfunc;
+extern int nsrchn;
+
+extern struct srch {
+    const char *s_name;
+    short s_value;
+} srchn[];
+
+/*
+ * 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 {
+    struct Ain w_start;                /* Point to restart loop */
+    struct Ain 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]
+
+#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 */
+extern Char **alvec, *alvecp;  /* The (remnants of) alias vector */
+
+/*
+ * Filename/command name expansion variables
+ */
+int gflag;                     /* After tglob -> is globbing needed? */
+
+#define MAXVARLEN 30           /* Maximum number of char in a variable name */
+
+/*
+ * Variables for filename expansion
+ */
+extern long gargc;             /* Number args in gargv */
+extern Char **gargv;           /* Pointer to the (stack) arglist */
+
+/*
+ * Variables for command expansion.
+ */
+extern Char **pargv;           /* Pointer to the argv list space */
+extern long pargc;             /* Count of arguments in pargv */
+long pnleft;                   /* Number of chars left in pargs */
+Char *pargs;                   /* Pointer to start current word */
+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;
+    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)           strip(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)
+ */
+const char *bname;
+
+#define        setname(a) (bname = (a))
+
+Char *Vsav;
+Char *Vdp;
+Char *Vexpath;
+char **Vt;
+
+Char **evalvec;
+Char *evalp;
+
+/* 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;
+
+#ifdef EDIT
+#include <histedit.h>
+EditLine *el;
+History *hi;
+#endif
+int editing;
+
+#endif /* !_CSH_H_ */
diff --git a/bin/csh/dir.c b/bin/csh/dir.c
new file mode 100644 (file)
index 0000000..418d302
--- /dev/null
@@ -0,0 +1,901 @@
+/* $NetBSD: dir.c,v 1.30 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dir.c      8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: dir.c,v 1.30 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "csh.h"
+#include "dir.h"
+#include "extern.h"
+
+/* Directory management. */
+
+static struct directory *dfind(Char *);
+static Char *dfollow(Char *);
+static void printdirs(void);
+static Char *dgoto(Char *);
+static void skipargs(Char ***, const char *);
+static void dnewcwd(struct directory *);
+static void dset(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(Char *hp)
+{
+    static const char emsg[] = "csh: Trying to start from \"%s\"\n";
+    char path[MAXPATHLEN];
+    struct directory *dp;
+    const char *ecp;
+    Char *cp;
+
+    /* Don't believe the login shell home, because it may be a symlink */
+    ecp = getcwd(path, MAXPATHLEN);
+    if (ecp == NULL || *ecp == '\0') {
+       (void)fprintf(csherr, "csh: %s\n", strerror(errno));
+       if (hp && *hp) {
+           ecp = short2str(hp);
+           if (chdir(ecp) == -1)
+               cp = NULL;
+           else
+               cp = Strsave(hp);
+           (void)fprintf(csherr, emsg, vis_str(hp));
+       }
+       else
+           cp = NULL;
+       if (cp == NULL) {
+           (void)fprintf(csherr, 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(ecp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
+           swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino)
+           cp = Strsave(hp);
+       else {
+           const char *cwd;
+
+           /*
+            * use PWD if we have it (for subshells)
+            */
+           if ((cwd = getenv("PWD")) != NULL) {
+               if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev &&
+                   swd.st_ino == shp.st_ino)
+                   ecp = cwd;
+           }
+           cp = dcanon(SAVE(ecp), STRNULL);
+       }
+    }
+
+    dp = (struct directory *)xcalloc(1, sizeof(struct directory));
+    dp->di_name = 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(Char *dp)
+{
+    Char **vec;
+
+    /*
+     * Don't call set() directly cause if the directory contains ` or
+     * other junk characters glob will fail.
+     */
+
+    vec = 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(Char ***v, const char *str)
+{
+    Char  **n, *s;
+
+    n = *v;
+    dirflag = 0;
+    for (n++; *n != NULL && (*n)[0] == '-'; n++)
+       for (s = &((*n)[1]); *s; s++)
+           switch (*s) {
+           case 'l':
+               dirflag |= DIR_LONG;
+               break;
+           case 'n':
+               dirflag |= DIR_LINE;
+               break;
+           case 'v':
+               dirflag |= DIR_VERT;
+               break;
+           default:
+               stderror(ERR_DIRUS, vis_str(**v), str);
+               /* NOTREACHED */
+           }
+    *v = n;
+}
+
+/*
+ * dodirs - list all directories in directory loop
+ */
+void
+/*ARGSUSED*/
+dodirs(Char **v, struct command *t)
+{
+    skipargs(&v, "");
+
+    if (*v != NULL)
+       stderror(ERR_DIRUS, "dirs", "");
+    printdirs();
+}
+
+static void
+printdirs(void)
+{
+    struct directory *dp;
+    Char *hp, *s;
+    size_t cur, idx, len;
+
+    hp = value(STRhome);
+    if (*hp == '\0')
+       hp = NULL;
+    dp = dcwd;
+    idx = 0;
+    cur = 0;
+    do {
+       if (dp == &dhead)
+           continue;
+       if (dirflag & DIR_VERT) {
+           (void)fprintf(cshout, "%zu\t", idx++);
+           cur = 0;
+       }
+       if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) &&
+           (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) &&
+           (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) 
+           len = Strlen(s = (dp->di_name + len)) + 2;
+       else
+           len = Strlen(s = dp->di_name) + 1;
+
+       cur += len;
+       if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) {
+           (void)fprintf(cshout, "\n");
+           cur = len;
+       }
+       (void) fprintf(cshout, "%s%s%c", (s != dp->di_name)? "~" : "",
+           vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' ');
+    } while ((dp = dp->di_prev) != dcwd);
+    if (!(dirflag & DIR_VERT))
+       (void)fprintf(cshout, "\n");
+}
+
+void
+dtildepr(Char *home, Char *dir)
+{
+    if (!eq(home, STRslash) && prefix(home, dir))
+       (void)fprintf(cshout, "~%s", vis_str(dir + Strlen(home)));
+    else
+       (void)fprintf(cshout, "%s", vis_str(dir));
+}
+
+void
+dtilde(void)
+{
+    struct directory *d;
+
+    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(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)) {
+       size_t  dotdot = 0;
+       Char   *dp, *cwd;
+
+       cwd = 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) {
+           dp = Strrchr(cwd, '/');
+           if (dp) {
+               *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
+/*ARGSUSED*/
+dochngd(Char **v, struct command *t)
+{
+    struct directory *dp;
+    Char *cp;
+
+    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);
+    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(1, sizeof(struct directory));
+    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(Char *cp)
+{
+    Char *dp;
+
+    if (*cp != '/') {
+       Char *p, *q;
+       size_t cwdlen;
+
+       for (p = dcwd->di_name; *p++;)
+           continue;
+       if ((cwdlen = (size_t)(p - dcwd->di_name - 1)) == 1)    /* root */
+           cwdlen = 0;
+       for (p = cp; *p++;)
+           continue;
+       dp = xmalloc((size_t)(cwdlen + (size_t)(p - cp) + 1) * sizeof(Char));
+       for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
+           continue;
+       if (cwdlen)
+           p[-1] = '/';
+       else
+           p--;                /* don't add a / after root */
+       for (q = cp; (*p++ = *q++) != '\0';)
+           continue;
+       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(Char *cp)
+{
+    char ebuf[MAXPATHLEN];
+    struct varent *c;
+    Char *dp;
+    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;
+       Char *p;
+       Char    buf[MAXPATHLEN];
+
+       for (cdp = c->vec; *cdp; cdp++) {
+           for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';)
+               continue;
+           dp[-1] = '/';
+           for (p = cp; (*dp++ = *p++) != '\0';)
+               continue;
+           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));
+    /* NOTREACHED */
+}
+
+/*
+ * dopushd - push new directory onto directory stack.
+ *     with no arguments exchange top and second.
+ *     with numeric argument (+n) bring it to top.
+ */
+void
+/*ARGSUSED*/
+dopushd(Char **v, struct command *t)
+{
+    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);
+    else if ((dp = dfind(*v)) != NULL) {
+       char   *tmp;
+
+       if (chdir(tmp = short2str(dp->di_name)) < 0)
+           stderror(ERR_SYSTEM, tmp, strerror(errno));
+    }
+    else {
+       Char *ccp;
+
+       ccp = dfollow(*v);
+       dp = (struct directory *)xcalloc(1, sizeof(struct directory));
+       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(Char *cp)
+{
+    struct directory *dp;
+    Char *ep;
+    int i;
+
+    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
+/*ARGSUSED*/
+dopopd(Char **v, struct command *t)
+{
+    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);
+    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(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(Char *cp, Char *p)
+{
+    Char slink[MAXPATHLEN];
+    char tlink[MAXPATHLEN];
+    Char *newcp, *sp;
+    Char *p1, *p2;     /* general purpose */
+    ssize_t cc;
+    size_t len;
+    int slash;
+
+    /*
+     * 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 */
+           continue;
+       if (p != ++sp)
+           for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
+               continue;
+       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++) != '\0';)
+                   continue;
+               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) - 1)) >= 0) {
+               tlink[cc] = '\0';
+               (void)Strcpy(slink, str2short(tlink));
+
+               if (slash)
+                   *p = '/';
+               /*
+                * Point p to the '/' in "/..", and restore the '/'.
+                */
+               *(p = sp) = '/';
+               /*
+                * find length of p
+                */
+               for (p1 = p; *p1++;)
+                   continue;
+               if (*slink != '/') {
+                   /*
+                    * Relative path, expand it between the "yyy/" and the
+                    * "/..". First, back sp up to the character past "yyy/".
+                    */
+                   while (*--sp != '/')
+                       continue;
+                   sp++;
+                   *sp = 0;
+                   /*
+                    * New length is "yyy/" + slink + "/.." and rest
+                    */
+                   p1 = newcp = xmalloc(
+                       (size_t)((sp - cp) + cc + (p1 - p)) * sizeof(Char));
+                   /*
+                    * Copy new path into newcp
+                    */
+                   for (p2 = cp; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   for (p1--, p2 = slink; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   /*
+                    * Restart canonicalization at expanded "/xxx".
+                    */
+                   p = sp - cp - 1 + newcp;
+               }
+               else {
+                   /*
+                    * New length is slink + "/.." and rest
+                    */
+                   p1 = newcp = xmalloc(
+                       (size_t)(cc + (p1 - p)) * sizeof(Char));
+                   /*
+                    * Copy new path into newcp
+                    */
+                   for (p2 = slink; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   /*
+                    * Restart canonicalization at beginning
+                    */
+                   p = newcp;
+               }
+               xfree((ptr_t) cp);
+               cp = newcp;
+               continue;       /* canonicalize the link */
+           }
+           *sp = '/';
+           if (sp != cp)
+               while (*--sp != '/')
+                   continue;
+           if (slash) {
+               for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
+                   continue;
+               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)-1)) >= 0) {
+               tlink[cc] = '\0';
+               (void)Strcpy(slink, str2short(tlink));
+
+               /*
+                * restore the '/'.
+                */
+               if (slash)
+                   *p = '/';
+
+               /*
+                * point sp to p (rather than backing up).
+                */
+               sp = p;
+
+               /*
+                * find length of p
+                */
+               for (p1 = p; *p1++;)
+                   continue;
+               if (*slink != '/') {
+                   /*
+                    * Relative path, expand it between the "yyy/" and the
+                    * remainder. First, back sp up to the character past
+                    * "yyy/".
+                    */
+                   while (*--sp != '/')
+                       continue;
+                   sp++;
+                   *sp = 0;
+                   /*
+                    * New length is "yyy/" + slink + "/.." and rest
+                    */
+                   p1 = newcp = xmalloc(
+                       (size_t)((sp - cp) + cc + (p1 - p)) * sizeof(Char));
+                   /*
+                    * Copy new path into newcp
+                    */
+                   for (p2 = cp; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   for (p1--, p2 = slink; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   /*
+                    * Restart canonicalization at expanded "/xxx".
+                    */
+                   p = sp - cp - 1 + newcp;
+               }
+               else {
+                   /*
+                    * New length is slink + the rest
+                    */
+                   p1 = newcp = xmalloc(
+                       (size_t)(cc + (p1 - p)) * sizeof(Char));
+                   /*
+                    * Copy new path into newcp
+                    */
+                   for (p2 = slink; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
+                       continue;
+                   /*
+                    * Restart canonicalization at beginning
+                    */
+                   p = newcp;
+               }
+               xfree((ptr_t) cp);
+               cp = newcp;
+               continue;       /* canonicalize the link */
+           }
+           if (slash)
+               *p = '/';
+       }
+    }
+
+    /*
+     * fix home...
+     */
+    p1 = value(STRhome);
+    len = Strlen(p1);
+    /*
+     * See if we're not in a subdir of STRhome
+     */
+    if (p1 && *p1 == '/' &&
+       (Strncmp(p1, cp, len) != 0 || (cp[len] != '/' && cp[len] != '\0'))) {
+       static ino_t home_ino;
+       static dev_t home_dev = NODEV;
+       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(slink, 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, '/')) != NULL)
+               *sp = '\0';
+       }
+       /*
+        * See if we found it
+        */
+       if (*p2 && sp == (Char *) -1) {
+           /*
+            * Use STRhome to make '~' work
+            */
+           newcp = Strspl(p1, cp + Strlen(p2));
+           xfree((ptr_t) cp);
+           cp = newcp;
+       }
+    }
+    return cp;
+}
+
+
+/*
+ * dnewcwd - make a new directory in the loop the current one
+ */
+static void
+dnewcwd(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 (file)
index 0000000..16b9bab
--- /dev/null
@@ -0,0 +1,48 @@
+/* $NetBSD: dir.h,v 1.8 2003/08/07 09:05:04 agc Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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       8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _DIR_H_
+#define _DIR_H_
+
+/*
+ * 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 */
+
+#endif /* !_DIR_H_ */
diff --git a/bin/csh/dol.c b/bin/csh/dol.c
new file mode 100644 (file)
index 0000000..308203e
--- /dev/null
@@ -0,0 +1,968 @@
+/* $NetBSD: dol.c,v 1.29 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dol.c      8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: dol.c,v 1.29 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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 (_QF|_QB|_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.
+ */
+#define MAXWLEN (BUFSIZE - 4)
+#define MAXMOD MAXWLEN         /* This cannot overflow */
+static Char dolmod[MAXMOD];    /* : modifier character */
+static Char *dolp;             /* Remaining chars from this word */
+static Char **dolnxt;          /* Further words */
+static int dolcnt;             /* Count of further words */
+static int dolnmod;            /* Number of modifiers */
+static int dolmcnt;            /* :gx -> 10000, else 1 */
+static int dolwcnt;            /* :wx -> 10000, else 1 */
+
+static void Dfix2(Char **);
+static Char *Dpack(Char *, Char *);
+static int Dword(void);
+__dead static void dolerror(Char *);
+static int DgetC(int);
+static void Dgetdol(void);
+static void fixDolMod(void);
+static void setDolp(Char *);
+static void unDredc(int);
+static int Dredc(void);
+static void Dtestq(int);
+
+
+/*
+ * Fix up the $ expansions and quotations in the
+ * argument list to command t.
+ */
+void
+Dfix(struct command *t)
+{
+    Char *p, **pp;
+
+    if (noexec)
+       return;
+    /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
+    for (pp = t->t_dcom; (p = *pp++) != NULL;)
+       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(Char *cp)
+{
+    Char *Dv[2];
+
+    if (noexec)
+       return (0);
+    Dv[0] = cp;
+    Dv[1] = NULL;
+    Dfix2(Dv);
+    if (gargc != 1) {
+       setname(vis_str(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(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;
+}
+
+/*
+ * Pack up more characters in this word
+ */
+static Char *
+Dpack(Char *wbuf, Char *wp)
+{
+    int c;
+    ptrdiff_t i;
+    
+    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 | _QF | _QB)) {   /* 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++ = (Char)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(void)
+{
+    Char wbuf[BUFSIZE], *wp;
+    int c, c1;
+    ptrdiff_t i;
+    int dolflg, done, sofar;
+    
+    done = 0;
+    i = MAXWLEN;
+    sofar = 0;
+    wp = wbuf;
+
+    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);
+           /* FALLTHROUGH */
+       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++ = (Char)c, --i;
+           /* FALLTHROUGH */
+       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++ = (Char)(c == '`' ? '`' : (c | QUOTE));
+                   break;
+               case '\'':
+                   /* Prevent all further interpretation */
+                   *wp++ = (Char)(c | QUOTE);
+                   break;
+               case '`':
+                   /* Leave all text alone for later */
+                   *wp++ = (Char)c;
+                   break;
+               default:
+                   break;
+               }
+           }
+           if (c1 == '`')
+               *wp++ = '`' /* i--; eliminated */;
+           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;
+       default:
+           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(int flag)
+{
+    int c;
+top:
+    if ((c = Dpeekc) != '\0') {
+       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)) != '\0')
+           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, { NULL, NULL, NULL }, 0};
+
+static void
+dolerror(Char *s)
+{
+    setname(vis_str(s));
+    stderror(ERR_NAME | ERR_RANGE);
+    /* NOTREACHED */
+}
+
+/*
+ * Handle the multitudinous $ expansion forms.
+ * Ugh.
+ */
+static void
+Dgetdol(void)
+{
+    static Char *dolbang = NULL;
+    Char name[4*MAXVARLEN+1];
+    Char wbuf[BUFSIZE];
+    struct varent *vp;
+    Char *np;
+    int c, lwb, sc, subscr, upb;
+    int dimen, bitset;
+    char tnp;
+    
+    bitset = 0;
+    dimen = 0;
+    lwb = 1;
+    upb = 0;
+    subscr = 0;
+    vp = NULL;
+
+    dolnmod = dolmcnt = dolwcnt = 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);
+       if (backpid != 0) {
+           if (dolbang) 
+               xfree((ptr_t)dolbang);
+           setDolp(dolbang = putn(backpid));
+       }
+       goto eatbrac;
+    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 = (unsigned char)tnp;
+           if (np >= &wbuf[BUFSIZE - 1])
+               stderror(ERR_LTOOLONG);
+           if (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[dolnmod++] = 'q';
+       dolmcnt = 10000;
+       setDolp(wbuf);
+       goto eatbrac;
+    case DEOF:
+    case '\n':
+       stderror(ERR_SYNTAX);
+       /* NOTREACHED */
+    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)
+               stderror(ERR_RANGE);
+           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++ = (Char)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);
+    }
+    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++ = (Char)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')
+               continue;
+           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 {
+           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(void)
+{
+    int c;
+
+    c = DgetC(0);
+    if (c == ':') {
+       do {
+           c = DgetC(0), dolmcnt = 1, dolwcnt = 1;
+           if (c == 'g' || c == 'a') {
+               if (c == 'g')
+                   dolmcnt = 10000;
+               else
+                   dolwcnt = 10000;
+               c = DgetC(0);
+           }
+           if ((c == 'g' && dolmcnt != 10000) || 
+               (c == 'a' && dolwcnt != 10000)) {
+               if (c == 'g')
+                   dolmcnt = 10000;
+               else
+                   dolwcnt = 10000;
+               c = DgetC(0); 
+           }
+
+           if (c == 's') {     /* [eichin:19910926.0755EST] */
+               int delimcnt = 2;
+               int delim = DgetC(0);
+               dolmod[dolnmod++] = (Char)c;
+               dolmod[dolnmod++] = (Char)delim;
+               
+               if (!delim || letter(delim)
+                   || Isdigit(delim) || any(" \t\n", delim)) {
+                   seterror(ERR_BADSUBST);
+                   break;
+               }       
+               while ((c = DgetC(0)) != (-1)) {
+                   dolmod[dolnmod++] = (Char)c;
+                   if(c == delim) delimcnt--;
+                   if(!delimcnt) break;
+               }
+               if(delimcnt) {
+                   seterror(ERR_BADSUBST);
+                   break;
+               }
+               continue;
+           }
+           if (!any("htrqxes", c))
+               stderror(ERR_BADMOD, c);
+           dolmod[dolnmod++] = (Char)c;
+           if (c == 'q')
+               dolmcnt = 10000;
+       }
+       while ((c = DgetC(0)) == ':');
+       unDredc(c);
+    }
+    else
+       unDredc(c);
+}
+
+static void
+setDolp(Char *cp)
+{
+    Char *dp;
+    int i;
+
+    if (dolnmod == 0 || dolmcnt == 0) {
+       dolp = cp;
+       return;
+    }
+    dp = cp = Strsave(cp);
+    for (i = 0; i < dolnmod; i++) {
+       /* handle s// [eichin:19910926.0510EST] */
+       if(dolmod[i] == 's') {
+           int delim;
+           Char *lhsub, *rhsub, *np;
+           size_t lhlen = 0, rhlen = 0;
+           int didmod = 0;
+               
+           delim = dolmod[++i];
+           if (!delim || letter(delim)
+               || Isdigit(delim) || any(" \t\n", delim)) {
+               seterror(ERR_BADSUBST);
+               break;
+           }
+           lhsub = &dolmod[++i];
+           while(dolmod[i] != delim && dolmod[++i]) {
+               lhlen++;
+           }
+           dolmod[i] = 0;
+           rhsub = &dolmod[++i];
+           while(dolmod[i] != delim && dolmod[++i]) {
+               rhlen++;
+           }
+           dolmod[i] = 0;
+
+           do {
+               dp = Strstr(cp, lhsub);
+               if (dp) {
+                   np = xmalloc(
+                       (size_t)((Strlen(cp) + 1 - lhlen + rhlen) *
+                       sizeof(*np)));
+                   (void)Strncpy(np, cp, (size_t)(dp - cp));
+                   (void)Strcpy(np + (dp - cp), rhsub);
+                   (void)Strcpy(np + (dp - cp) + rhlen, dp + lhlen);
+
+                   xfree((ptr_t) cp);
+                   dp = cp = np;
+                   didmod = 1;
+               } else {
+                   /* should this do a seterror? */
+                   break;
+               }
+           }
+           while (dolwcnt == 10000);
+           /*
+            * restore dolmod for additional words
+            */
+           dolmod[i] = rhsub[-1] = (Char)delim;
+           if (didmod)
+               dolmcnt--;
+           else
+               break;
+        } else {
+           int didmod = 0;
+
+           do {
+               if ((dp = domod(cp, dolmod[i]))) {
+                   didmod = 1;
+                   if (Strcmp(cp, dp) == 0) {
+                       xfree((ptr_t) cp);
+                       cp = dp;
+                       break;
+                   }
+                   else {
+                       xfree((ptr_t) cp);
+                       cp = dp;
+                   }
+               }
+               else
+                   break;
+           }
+           while (dolwcnt == 10000);
+           dp = cp;
+           if (didmod)
+               dolmcnt--;
+           else
+               break;
+       }
+    }
+
+    if (dp) {
+       addla(dp);
+       xfree((ptr_t) dp);
+    }
+    else {
+       addla(cp);
+       xfree((ptr_t) cp);
+    }
+
+    dolp = STRNULL;
+    if (seterr)
+       stderror(ERR_OLD);
+}
+
+static void
+unDredc(int c)
+{
+    Dpeekrd = c;
+}
+
+static int
+Dredc(void)
+{
+    int c;
+
+    if ((c = Dpeekrd) != '\0') {
+       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(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
+/*ARGSUSED*/
+heredoc(Char *term)
+{
+    Char obuf[BUFSIZE], lbuf[BUFSIZE], mbuf[BUFSIZE];
+    struct timespec tv;
+    Char *Dv[2], *lbp, *obp, *mbp, **vp;
+    char *tmp;
+    int c, ocnt, lcnt, mcnt;
+    int quoted;
+
+again:
+    tmp = short2str(shtemp);
+    if (open(tmp, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600) < 0) {
+       if (errno == EEXIST) {
+           if (unlink(tmp) == -1) {
+               (void)clock_gettime(CLOCK_MONOTONIC, &tv);
+               mbp = putn((((int)tv.tv_sec) ^ 
+                   ((int)tv.tv_nsec) ^ ((int)getpid())) & 0x00ffffff);
+               shtemp = Strspl(STRtmpsh, mbp);
+               xfree((ptr_t)mbp);
+           }
+           goto again;
+       }
+       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 = BUFSIZE;
+    obp = obuf;
+    for (;;) {
+       /*
+        * Read up a line
+        */
+       lbp = lbuf;
+       lcnt = BUFSIZE - 4;
+       for (;;) {
+           c = readc(1);       /* 1 -> Want EOF returns */
+           if (c < 0 || c == '\n')
+               break;
+           if ((c &= TRIM) != '\0') {
+               *lbp++ = (Char)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)(BUFSIZE - ocnt));
+           (void)lseek(0, (off_t)0, SEEK_SET);
+           return;
+       }
+
+       /*
+        * If term was quoted or -n just pass it on
+        */
+       if (quoted || noexec) {
+           *lbp++ = '\n';
+           *lbp = 0;
+           for (lbp = lbuf; (c = *lbp++) != '\0';) {
+               *obp++ = (Char)c;
+               if (--ocnt == 0) {
+                   (void) write(0, short2str(obuf), BUFSIZE);
+                   obp = obuf;
+                   ocnt = BUFSIZE;
+               }
+           }
+           continue;
+       }
+
+       /*
+        * Term wasn't quoted so variable and then command expand the input
+        * line
+        */
+       Dcp = lbuf;
+       Dvp = Dv + 1;
+       mbp = mbuf;
+       mcnt = BUFSIZE - 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++ = (Char)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), BUFSIZE);
+                   obp = obuf;
+                   ocnt = BUFSIZE;
+               }
+           }
+           *obp++ = '\n';
+           if (--ocnt == 0) {
+               (void)write(0, short2str(obuf), BUFSIZE);
+               obp = obuf;
+               ocnt = BUFSIZE;
+           }
+       }
+       if (pargv)
+           blkfree(pargv), pargv = 0;
+    }
+}
diff --git a/bin/csh/err.c b/bin/csh/err.c
new file mode 100644 (file)
index 0000000..030aaf7
--- /dev/null
@@ -0,0 +1,388 @@
+/* $NetBSD: err.c,v 1.21 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)err.c      8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: err.c,v 1.21 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "csh.h"
+#include "extern.h"
+
+char *seterr = NULL;   /* Holds last error if there was one */
+
+#define ERR_FLAGS      ((int)0xf0000000)
+#define ERR_NAME       0x10000000
+#define ERR_SILENT     0x20000000
+#define ERR_OLD                0x40000000
+
+static const 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 [-rh] [# 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
+seterror(int id, ...)
+{
+    if (seterr == 0) {
+       char berr[BUFSIZE];
+       va_list va;
+
+       va_start(va, id);
+       if (id < 0 || id >= (int)(sizeof(errorlist) / sizeof(errorlist[0])) - 1)
+           id = ERR_INVALID;
+       (void)vsnprintf(berr, sizeof(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
+stderror(int id, ...)
+{
+    va_list va;
+    Char **v;
+    int flags;
+
+    flags = id & ERR_FLAGS;
+    id &= ~ERR_FLAGS;
+
+    if ((flags & ERR_OLD) && seterr == NULL)
+       abort();
+
+    if (id < 0 || id > (int)(sizeof(errorlist) / sizeof(errorlist[0])))
+       id = ERR_INVALID;
+
+    (void)fflush(cshout);
+    (void)fflush(csherr);
+    haderr = 1;                        /* Now to diagnostic output */
+    timflg = 0;                        /* This isn't otherwise reset */
+
+
+    if (!(flags & ERR_SILENT)) {
+       if (flags & ERR_NAME)
+           (void)fprintf(csherr, "%s: ", bname);
+       if ((flags & ERR_OLD))
+           /* Old error. */
+           (void)fprintf(csherr, "%s.\n", seterr);
+       else {
+           va_start(va, id);
+           (void)vfprintf(csherr, errorlist[id], va);
+           va_end(va);
+           (void)fprintf(csherr, ".\n");
+       }
+    }
+
+    if (seterr) {
+       xfree((ptr_t) seterr);
+       seterr = NULL;
+    }
+
+    if ((v = pargv) != NULL)
+       pargv = 0, blkfree(v);
+    if ((v = gargv) != NULL)
+       gargv = 0, blkfree(v);
+
+    (void)fflush(cshout);
+    (void)fflush(csherr);
+    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 (file)
index 0000000..bd2b343
--- /dev/null
@@ -0,0 +1,746 @@
+/* $NetBSD: exec.c,v 1.29 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)exec.c     8.3 (Berkeley) 5/23/95";
+#else
+__RCSID("$NetBSD: exec.c,v 1.29 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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 const 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 unsigned 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] |= (unsigned char)(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(void) __dead;
+static void texec(Char *, Char **);
+static int hashname(Char *);
+static int tellmewhat(struct wordent *, Char *);
+static int executable(Char *, Char *, int);
+static int iscommand(Char *);
+
+void
+/*ARGSUSED*/
+doexec(Char **v, struct command *t)
+{
+    struct varent *pathv;
+    Char *blk[2], **av, *dp, **pv, *sav;
+    int i, hashval, hashval1;
+    sigset_t nsigset;
+    int slash;
+
+    hashval = 0;
+    /*
+     * 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(vis_str(blk[0]));
+           stderror(ERR_NAME | ERR_NOMATCH);
+       }
+       gargv = 0;
+    }
+    else
+       pv = saveblk(blk);
+
+    trim(pv);
+
+    exerr = 0;
+    expath = Strsave(pv[0]);
+    Vexpath = expath;
+
+    pathv = adrof(STRpath);
+    if (pathv == 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(vis_str(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;
+    SHERR = 2;
+    OLDSTD = 0;
+    /*
+     * We must do this AFTER any possible forking (like `foo` in glob) so that
+     * this shell can still do subprocesses.
+     */
+    sigemptyset(&nsigset);
+    (void)sigprocmask(SIG_SETMASK, &nsigset, NULL);
+    /*
+     * If no path, no words in path, or a / in the filename then restrict the
+     * command search.
+     */
+    if (pathv == 0 || pathv->vec[0] == 0 || slash)
+       pv = justabs;
+    else
+       pv = pathv->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();
+    /* NOTREACHED */
+}
+
+static void
+pexerr(void)
+{
+    /* Couldn't find the damn thing */
+    if (expath) {
+       setname(vis_str(expath));
+       Vexpath = 0;
+       xfree((ptr_t)expath);
+       expath = 0;
+    }
+    else
+       setname("");
+    if (exerr)
+       stderror(ERR_NAME | ERR_STRING, exerr);
+    else
+       stderror(ERR_NAME | ERR_COMMAND);
+    /* NOTREACHED */
+}
+
+/*
+ * Execute command f, arg list t.
+ * Record error message if not found.
+ * Also do shell scripts here.
+ */
+static void
+texec(Char *sf, Char **st)
+{
+    struct varent *v;
+    Char *lastsh[2], **vp, *st0, **ost;
+    char *f, **t;
+    int fd;
+    unsigned char c = '\0';
+
+    /* 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);
+       /* FALLTHROUGH */
+
+    case ENOMEM:
+       stderror(ERR_SYSTEM, f, strerror(errno));
+       /* NOTREACHED */
+
+    case ENOENT:
+       break;
+
+    default:
+       if (exerr == 0) {
+           exerr = strerror(errno);
+           if (expath)
+               xfree((ptr_t) expath);
+           expath = Strsave(sf);
+           Vexpath = expath;
+       }
+    }
+}
+
+/*ARGSUSED*/
+void
+execash(Char **t, struct command *kp)
+{
+    jmp_buf osetexit;
+    sig_t osigint, osigquit, osigterm;
+    int my_reenter, odidfds, oOLDSTD, oSHERR, oSHIN, oSHOUT;
+    int saveDIAG, saveIN, saveOUT, saveSTD;
+
+    if (chkstop == 0 && setintr)
+       panystop(0);
+    /*
+     * Hmm, we don't really want to do that now because we might
+     * fail, but what is the choice
+     */
+    rechist();
+
+    osigint  = signal(SIGINT, parintr);
+    osigquit = signal(SIGQUIT, parintr);
+    osigterm = signal(SIGTERM, parterm);
+
+    odidfds = didfds;
+    oSHIN = SHIN;
+    oSHOUT = SHOUT;
+    oSHERR = SHERR;
+    oOLDSTD = OLDSTD;
+
+    saveIN = dcopy(SHIN, -1);
+    saveOUT = dcopy(SHOUT, -1);
+    saveDIAG = dcopy(SHERR, -1);
+    saveSTD = dcopy(OLDSTD, -1);
+
+    lshift(kp->t_dcom, 1);
+
+    getexit(osetexit);
+
+    if ((my_reenter = setexit()) == 0) {
+       SHIN = dcopy(0, -1);
+       SHOUT = dcopy(1, -1);
+       SHERR = dcopy(2, -1);
+       didfds = 0;
+       doexec(t, kp);
+    }
+
+    (void)signal(SIGINT, osigint);
+    (void)signal(SIGQUIT, osigquit);
+    (void)signal(SIGTERM, osigterm);
+
+    doneinp = 0;
+    didfds = odidfds;
+    (void)close(SHIN);
+    (void)close(SHOUT);
+    (void)close(SHERR);
+    (void)close(OLDSTD);
+    SHIN = dmove(saveIN, oSHIN);
+    SHOUT = dmove(saveOUT, oSHOUT);
+    SHERR = dmove(saveDIAG, oSHERR);
+    OLDSTD = dmove(saveSTD, oOLDSTD);
+
+    resexit(osetexit);
+    if (my_reenter)
+       stderror(ERR_SILENT);
+}
+
+void
+xechoit(Char **t)
+{
+    if (adrof(STRecho)) {
+       int odidfds = didfds;
+       (void)fflush(csherr);
+       odidfds = didfds;
+       didfds = 0;
+       blkpr(csherr, t);
+       (void)fputc('\n', csherr);
+       (void)fflush(csherr);
+       didfds = odidfds;
+    }
+}
+
+void
+/*ARGSUSED*/
+dohash(Char **v, struct command *t)
+{
+    struct dirent *dp;
+    struct varent *pathv;
+    DIR *dirp;
+    Char **pv;
+    size_t cnt;
+    int hashval, i;
+
+    i = 0;
+    havhash = 1;
+    pathv = adrof(STRpath);
+
+    for (cnt = 0; cnt < sizeof xhash; cnt++)
+       xhash[cnt] = 0;
+    if (pathv == 0)
+       return;
+    for (pv = pathv->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
+/*ARGSUSED*/
+dounhash(Char **v, struct command *t)
+{
+    havhash = 0;
+}
+
+void
+/*ARGSUSED*/
+hashstat(Char **v, struct command *t)
+{
+    if (hits + misses)
+       (void)fprintf(cshout, "%d hits, %d misses, %d%%\n",
+           hits, misses, 100 * hits / (hits + misses));
+}
+
+/*
+ * Hash a command name.
+ */
+static int
+hashname(Char *cp)
+{
+    long h = 0;
+
+    while (*cp)
+       h = hash(h, *cp++);
+    return ((int) h);
+}
+
+static int
+iscommand(Char *name)
+{
+    struct varent *v;
+    Char **pv, *sav;
+    int hashval, hashval1, i;
+    int slash;
+
+    hashval = 0;
+    slash = any(short2str(name), '/');
+    v = adrof(STRpath);
+    
+    if (v == 0 || v->vec[0] == 0 || slash)
+       pv = justabs;
+    else
+       pv = v->vec;
+    sav = Strspl(STRslash, name);      /* / command name for postpending */
+    if (havhash)
+       hashval = hashname(name);
+    i = 0;
+    do {
+       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 */
+           if (executable(NULL, name, 0)) {
+               xfree((ptr_t) sav);
+               return i + 1;
+           }
+       }
+       else {
+           if (executable(*pv, sav, 0)) {
+               xfree((ptr_t) sav);
+               return i + 1;
+           }
+       }
+cont:
+       pv++;
+       i++;
+    } while (*pv);
+    xfree((ptr_t) sav);
+    return 0;
+}
+
+/* Also by:
+ *  Andreas Luik <luik@isaak.isa.de>
+ *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
+ *  Azenberstr. 35
+ *  D-7000 Stuttgart 1
+ *  West-Germany
+ * is the executable() routine below and changes to iscommand().
+ * Thanks again!!
+ */
+
+/*
+ * executable() examines the pathname obtained by concatenating dir and name
+ * (dir may be NULL), and returns 1 either if it is executable by us, or
+ * if dir_ok is set and the pathname refers to a directory.
+ * This is a bit kludgy, but in the name of optimization...
+ */
+static int
+executable(Char *dir, Char *name, int dir_ok)
+{
+    struct stat stbuf;
+    Char path[MAXPATHLEN + 1], *dp, *sp;
+    char *strname;
+
+    if (dir && *dir) {
+       for (dp = path, sp = dir; *sp; *dp++ = *sp++)
+           if (dp == &path[MAXPATHLEN + 1]) {
+               *--dp = '\0';
+               break;
+           }
+       for (sp = name; *sp; *dp++ = *sp++)
+           if (dp == &path[MAXPATHLEN + 1]) {
+               *--dp = '\0';
+               break;
+           }
+       *dp = '\0';
+       strname = short2str(path);
+    }
+    else
+       strname = short2str(name);
+    return (stat(strname, &stbuf) != -1 && ((S_ISREG(stbuf.st_mode) &&
+        /* save time by not calling access() in the hopeless case */
+       (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
+       access(strname, X_OK) == 0) || (dir_ok && S_ISDIR(stbuf.st_mode))));
+}
+
+/* The dowhich() is by:
+ *  Andreas Luik <luik@isaak.isa.de>
+ *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
+ *  Azenberstr. 35
+ *  D-7000 Stuttgart 1
+ *  West-Germany
+ * Thanks!!
+ */
+/*ARGSUSED*/
+void
+dowhich(Char **v, struct command *c)
+{
+    struct wordent lexw[3];
+    struct varent *vp;
+
+    lexw[0].next = &lexw[1];
+    lexw[1].next = &lexw[2];
+    lexw[2].next = &lexw[0];
+
+    lexw[0].prev = &lexw[2];
+    lexw[1].prev = &lexw[0];
+    lexw[2].prev = &lexw[1];
+
+    lexw[0].word = STRNULL;
+    lexw[2].word = STRret;
+
+    while (*++v) {
+       if ((vp = adrof1(*v, &aliases)) != NULL) {
+           (void)fprintf(cshout, "%s: \t aliased to ", vis_str(*v));
+           blkpr(cshout, vp->vec);
+           (void)fputc('\n', cshout);
+           set(STRstatus, Strsave(STR0));
+       }
+       else {
+           lexw[1].word = *v;
+           set(STRstatus, Strsave(tellmewhat(lexw, NULL) ? STR0 : STR1));
+       }
+    }
+}
+
+static int
+tellmewhat(struct wordent *lexp, Char *str)
+{
+    struct biltins *bptr;
+    struct wordent *sp;
+    Char *cmd, *s0, *s1, *s2;
+    int i;
+    int aliased, found;
+    Char qc;
+
+    aliased = 0;
+    sp = lexp->next;
+
+    if (adrof1(sp->word, &aliases)) {
+       alias(lexp);
+       sp = lexp->next;
+       aliased = 1;
+    }
+
+    s0 = sp->word;             /* to get the memory freeing right... */
+
+    /* handle quoted alias hack */
+    if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
+       (sp->word)++;
+
+    /* do quoting, if it hasn't been done */
+    s1 = s2 = sp->word;
+    while (*s2)
+       switch (*s2) {
+       case '\'':
+       case '"':
+           qc = *s2++;
+           while (*s2 && *s2 != qc)
+               *s1++ = (Char)(*s2++ | QUOTE);
+           if (*s2)
+               s2++;
+           break;
+       case '\\':
+           if (*++s2)
+               *s1++ = (Char)(*s2++ | QUOTE);
+           break;
+       default:
+           *s1++ = *s2++;
+       }
+    *s1 = '\0';
+
+    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
+       if (eq(sp->word, str2short(bptr->bname))) {
+           if (str == NULL) {
+               if (aliased)
+                   prlex(cshout, lexp);
+               (void)fprintf(cshout, "%s: shell built-in command.\n", 
+                              vis_str(sp->word));
+           }
+           else
+               (void)Strcpy(str, sp->word);
+           sp->word = s0;      /* we save and then restore this */
+           return 1;
+       }
+    }
+
+    sp->word = cmd = globone(sp->word, G_IGNORE);
+
+    if ((i = iscommand(sp->word)) != 0) {
+       Char **pv;
+       struct varent *v;
+       int    slash = any(short2str(sp->word), '/');
+
+       v = adrof(STRpath);
+       if (v == 0 || v->vec[0] == 0 || slash)
+           pv = justabs;
+       else
+           pv = v->vec;
+
+       while (--i)
+           pv++;
+       if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
+           if (!slash) {
+               sp->word = Strspl(STRdotsl, sp->word);
+               prlex(cshout, lexp);
+               xfree((ptr_t) sp->word);
+           }
+           else
+               prlex(cshout, lexp);
+       }
+       else {
+           s1 = Strspl(*pv, STRslash);
+           sp->word = Strspl(s1, sp->word);
+           xfree((ptr_t) s1);
+           if (str == NULL)
+               prlex(cshout, lexp);
+           else
+               (void)Strcpy(str, sp->word);
+           xfree((ptr_t) sp->word);
+       }
+       found = 1;
+    }
+    else {
+       if (str == NULL) {
+           if (aliased)
+               prlex(cshout, lexp);
+           (void)fprintf(csherr,
+                          "%s: Command not found.\n", vis_str(sp->word));
+       }
+       else
+           (void)Strcpy(str, sp->word);
+       found = 0;
+    }
+    sp->word = s0;             /* we save and then restore this */
+    xfree((ptr_t) cmd);
+    return found;
+}
diff --git a/bin/csh/exp.c b/bin/csh/exp.c
new file mode 100644 (file)
index 0000000..a4be1fc
--- /dev/null
@@ -0,0 +1,661 @@
+/* $NetBSD: exp.c,v 1.20 2009/02/14 07:12:29 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)exp.c      8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: exp.c,v 1.20 2009/02/14 07:12:29 lukem Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifndef SHORT_STRINGS
+#include <string.h>
+#endif /* SHORT_STRINGS */
+
+#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(Char ***, int);
+static int csh_exp2(Char ***, int);
+static int exp2a(Char ***, int);
+static int exp2b(Char ***, int);
+static int exp2c(Char ***, int);
+static Char *exp3(Char ***, int);
+static Char *exp3a(Char ***, int);
+static Char *exp4(Char ***, int);
+static Char *exp5(Char ***, int);
+static Char *exp6(Char ***, int);
+static void evalav(Char **);
+static int isa(Char *, int);
+static int egetn(Char *);
+
+#ifdef EDEBUG
+static void etracc(char *, Char *, Char ***);
+static void etraci(char *, int, Char ***);
+#endif
+
+int
+expr(Char ***vp)
+{
+    return (exp0(vp, 0));
+}
+
+int
+exp0(Char ***vp, int ignore)
+{
+    int p1;
+
+    p1 = exp1(vp, ignore);
+#ifdef EDEBUG
+    etraci("exp0 p1", p1, vp);
+#endif
+    if (**vp && eq(**vp, STRor2)) {
+       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(Char ***vp, int ignore)
+{
+    int p1;
+
+    p1 = csh_exp2(vp, ignore);
+#ifdef EDEBUG
+    etraci("exp1 p1", p1, vp);
+#endif
+    if (**vp && eq(**vp, STRand2)) {
+       int p2;
+
+       (*vp)++;
+       p2 = exp1(vp, (ignore & IGNORE) || !p1);
+#ifdef EDEBUG
+       etraci("exp1 p2", p2, vp);
+#endif
+       return (p1 && p2);
+    }
+    return (p1);
+}
+
+static int
+csh_exp2(Char ***vp, int ignore)
+{
+    int p1;
+
+    p1 = exp2a(vp, ignore);
+#ifdef EDEBUG
+    etraci("exp3 p1", p1, vp);
+#endif
+    if (**vp && eq(**vp, STRor)) {
+       int p2;
+
+       (*vp)++;
+       p2 = csh_exp2(vp, ignore);
+#ifdef EDEBUG
+       etraci("exp3 p2", p2, vp);
+#endif
+       return (p1 | p2);
+    }
+    return (p1);
+}
+
+static int
+exp2a(Char ***vp, int ignore)
+{
+    int p1;
+
+    p1 = exp2b(vp, ignore);
+#ifdef EDEBUG
+    etraci("exp2a p1", p1, vp);
+#endif
+    if (**vp && eq(**vp, STRcaret)) {
+       int p2;
+
+       (*vp)++;
+       p2 = exp2a(vp, ignore);
+#ifdef EDEBUG
+       etraci("exp2a p2", p2, vp);
+#endif
+       return (p1 ^ p2);
+    }
+    return (p1);
+}
+
+static int
+exp2b(Char ***vp, int ignore)
+{
+    int p1;
+
+    p1 = exp2c(vp, ignore);
+#ifdef EDEBUG
+    etraci("exp2b p1", p1, vp);
+#endif
+    if (**vp && eq(**vp, STRand)) {
+       int p2;
+
+       (*vp)++;
+       p2 = exp2b(vp, ignore);
+#ifdef EDEBUG
+       etraci("exp2b p2", p2, vp);
+#endif
+       return (p1 & p2);
+    }
+    return (p1);
+}
+
+static int
+exp2c(Char ***vp, int ignore)
+{
+    Char *p1, *p2;
+    int i;
+
+    p1 = exp3(vp, ignore);
+#ifdef EDEBUG
+    etracc("exp2c p1", p1, vp);
+#endif
+    if ((i = isa(**vp, EQOP)) != 0) {
+       (*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 EQMATCH:
+               i = Gmatch(p1, p2);
+               break;
+           case NOTEQ:
+               i = !eq(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(Char ***vp, int ignore)
+{
+    Char *p1, *p2;
+    int i;
+
+    p1 = exp3a(vp, ignore);
+#ifdef EDEBUG
+    etracc("exp3 p1", p1, vp);
+#endif
+    if ((i = isa(**vp, RELOP)) != 0) {
+       (*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(Char ***vp, int ignore)
+{
+    Char *op, *p1, *p2;
+    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(Char ***vp, int ignore)
+{
+    Char *p1, *p2;
+    int i;
+
+    i = 0;
+    p1 = exp5(vp, ignore);
+#ifdef EDEBUG
+    etracc("exp4 p1", p1, vp);
+#endif
+    if (isa(**vp, ADDOP)) {
+       Char *op;
+
+       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(Char ***vp, int ignore)
+{
+    Char *p1, *p2;
+    int i;
+
+    i = 0;
+    p1 = exp6(vp, ignore);
+#ifdef EDEBUG
+    etracc("exp5 p1", p1, vp);
+#endif
+    if (isa(**vp, MULOP)) {
+       Char *op;
+
+       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(Char ***vp, int ignore)
+{
+    Char *cp, *dp, *ep;
+    int ccode, i;
+
+    i = 0;
+    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 != ')')
+           stderror(ERR_NAME | ERR_EXPRESSION);
+       (*vp)++;
+       return (putn(ccode));
+    }
+    if (eq(**vp, STRLbrace)) {
+       struct command faket;
+       Char *fakecom[2];
+       Char **v;
+
+       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 (cp[1] == 'l' ?
+               lstat(short2str(ep), &stb) : stat(short2str(ep), &stb)) {
+               xfree((ptr_t) ep);
+               return (Strsave(STR0));
+           }
+           switch (cp[1]) {
+           case 'd':
+               i = S_ISDIR(stb.st_mode);
+               break;
+           case 'e':
+               i = 1;
+               break;
+           case 'f':
+               i = S_ISREG(stb.st_mode);
+               break;
+           case 'l':
+#ifdef S_ISLNK
+               i = S_ISLNK(stb.st_mode);
+#else
+               i = 0;
+#endif
+               break;
+           case 'o':
+               i = stb.st_uid == (uid_t)uid;
+               break;
+           case 'p':
+#ifdef S_ISFIFO
+               i = S_ISFIFO(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;
+           }
+       }
+#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(Char **v)
+{
+    struct wordent *hp, paraml1, *wdp;
+    struct command *t;
+
+    hp = &paraml1;
+    wdp = hp;
+    set(STRstatus, Strsave(STR0));
+    hp->prev = hp->next = hp;
+    hp->word = STRNULL;
+    while (*v) {
+       struct wordent *new;
+
+       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(Char *cp, 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(Char *cp)
+{
+    if (*cp && *cp != '-' && !Isdigit(*cp))
+       stderror(ERR_NAME | ERR_EXPRESSION);
+    return (getn(cp));
+}
+
+/* Phew! */
+
+#ifdef EDEBUG
+static void
+etraci(char *str, int i, Char ***vp)
+{
+    (void)fprintf(csherr, "%s=%d\t", str, i);
+    blkpr(csherr, *vp);
+    (void)fprintf(csherr, "\n");
+}
+static void
+etracc(char *str, Char *cp, Char ***vp)
+{
+    (void)fprintf(csherr, "%s=%s\t", str, vis_str(cp));
+    blkpr(csherr, *vp);
+    (void)fprintf(csherr, "\n");
+}
+#endif
diff --git a/bin/csh/extern.h b/bin/csh/extern.h
new file mode 100644 (file)
index 0000000..0d882f9
--- /dev/null
@@ -0,0 +1,341 @@
+/* $NetBSD: extern.h,v 1.29 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. 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    8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _EXTERN_H_
+#define _EXTERN_H_
+
+#include <sys/cdefs.h>
+
+/*
+ * csh.c
+ */
+int gethdir(Char *);
+void dosource(Char **, struct command *);
+__dead void exitstat(void);
+__dead void goodbye(void);
+void importpath(Char *);
+void initdesc(void);
+__dead void pintr(int);
+__dead void pintr1(int);
+void printprompt(void);
+#ifdef EDIT
+char *printpromptstr(EditLine *);
+#endif
+void process(int);
+void rechist(void);
+void untty(void);
+int vis_fputc(int, FILE *);
+
+#ifdef PROF
+__dead void done(int);
+#else
+__dead void xexit(int);
+#endif
+
+/*
+ * dir.c
+ */
+void dinit(Char *);
+void dodirs(Char **, struct command *);
+Char *dcanon(Char *, Char *);
+void dtildepr(Char *, Char *);
+void dtilde(void);
+void dochngd(Char **, struct command *);
+Char *dnormalize(Char *);
+void dopushd(Char **, struct command *);
+void dopopd(Char **, struct command *);
+struct directory;
+void dfree(struct directory *);
+
+/*
+ * dol.c
+ */
+void Dfix(struct command *);
+Char *Dfix1(Char *);
+void heredoc(Char *);
+
+/*
+ * err.c
+ */
+void seterror(int, ...);
+__dead void stderror(int, ...);
+
+/*
+ * exec.c
+ */
+__dead void doexec(Char **, struct command *);
+void dohash(Char **, struct command *);
+void dounhash(Char **, struct command *);
+void dowhich(Char **, struct command *);
+void execash(Char **, struct command *);
+void hashstat(Char **, struct command *);
+void xechoit(Char **);
+
+/*
+ * exp.c
+ */
+int expr(Char ***);
+int exp0(Char ***, int);
+
+/*
+ * file.c
+ */
+#ifdef FILEC
+ssize_t tenex(Char *, size_t);
+#endif
+
+/*
+ * func.c
+ */
+void Setenv(Char *, Char *);
+void doalias(Char **, struct command *);
+void dobreak(Char **, struct command *);
+void docontin(Char **, struct command *);
+void doecho(Char **, struct command *);
+void doelse(Char **, struct command *);
+void doend(Char **, struct command *);
+void doeval(Char **, struct command *);
+void doexit(Char **, struct command *);
+void doforeach(Char **, struct command *);
+void doglob(Char **, struct command *);
+void dogoto(Char **, struct command *);
+void doif(Char **, struct command *);
+void dolimit(Char **, struct command *);
+__dead void dologin(Char **, struct command *);
+__dead void dologout(Char **, struct command *);
+void donohup(Char **, struct command *);
+void doonintr(Char **, struct command *);
+void doprintf(Char **, struct command *);
+void dorepeat(Char **, struct command *);
+void dosetenv(Char **, struct command *);
+void dosuspend(Char **, struct command *);
+void doswbrk(Char **, struct command *);
+void doswitch(Char **, struct command *);
+void doumask(Char **, struct command *);
+void dounlimit(Char **, struct command *);
+void dounsetenv(Char **, struct command *);
+void dowhile(Char **, struct command *);
+void dozip(Char **, struct command *);
+void func(struct command *, struct biltins *);
+struct biltins *isbfunc(struct command *);
+void prvars(void);
+void gotolab(Char *);
+int srchx(Char *);
+void unalias(Char **, struct command *);
+void wfree(void);
+
+/*
+ * glob.c
+ */
+Char **dobackp(Char *, int);
+void Gcat(Char *, Char *);
+Char *globone(Char *, int);
+int  Gmatch(Char *, Char *);
+void ginit(void);
+Char **globall(Char **);
+void rscan(Char **, void (*)(int));
+void tglob(Char **);
+void trim(Char **);
+#ifdef FILEC
+int sortscmp(const ptr_t, const ptr_t);
+#endif /* FILEC */
+
+/*
+ * hist.c
+ */
+void dohist(Char **, struct command *);
+struct Hist *enthist(int, struct wordent *, int);
+#ifdef EDIT
+void loadhist(struct Hist *);
+#endif
+void savehist(struct wordent *);
+
+/*
+ * lex.c
+ */
+void addla(Char *);
+void bseek(struct Ain *);
+void btell(struct Ain *);
+void btoeof(void);
+void copylex(struct wordent *, struct wordent *);
+Char *domod(Char *, int);
+void freelex(struct wordent *);
+int lex(struct wordent *);
+void prlex(FILE *, struct wordent *);
+#ifdef EDIT
+int sprlex(char **, struct wordent *);
+#endif
+int readc(int);
+void settell(void);
+void unreadc(int);
+
+/*
+ * misc.c
+ */
+int any(const char *, int);
+Char **blkcat(Char **, Char **);
+Char **blkcpy(Char **, Char **);
+Char **blkend(Char **);
+void blkfree(Char **);
+int blklen(Char **);
+void blkpr(FILE *, Char **);
+Char **blkspl(Char **, Char **);
+void closem(void);
+Char **copyblk(Char **);
+int dcopy(int, int);
+int dmove(int, int);
+void donefds(void);
+Char lastchr(Char *);
+void lshift(Char **, size_t);
+int number(Char *);
+int prefix(Char *, Char *);
+Char **saveblk(Char **);
+Char *strip(Char *);
+Char *quote(Char *);
+char *strsave(const char *);
+char *strspl(char *, char *);
+__dead void udvar(Char *);
+
+#ifndef        SHORT_STRINGS
+# ifdef NOTUSED
+char *strstr(const char *, const char *);
+# endif /* NOTUSED */
+char *strend(char *);
+#endif
+
+/*
+ * parse.c
+ */
+void alias(struct wordent *);
+void freesyn(struct command *);
+struct command *syntax(struct wordent *, struct wordent *, int);
+
+
+/*
+ * proc.c
+ */
+void dobg(Char **, struct command *);
+void dobg1(Char **, struct command *);
+void dofg(Char **, struct command *);
+void dofg1(Char **, struct command *);
+void dojobs(Char **, struct command *);
+void dokill(Char **, struct command *);
+void donotify(Char **, struct command *);
+void dostop(Char **, struct command *);
+void dowait(Char **, struct command *);
+void palloc(int, struct command *);
+void panystop(int);
+void pchild(int);
+void pendjob(void);
+struct process *pfind(Char *);
+int pfork(struct command *, int);
+void pgetty(int, int);
+void pjwait(struct process *);
+void pnote(void);
+void prestjob(void);
+void psavejob(void);
+void pstart(struct process *, int);
+void pwait(void);
+
+/*
+ * sem.c
+ */
+void execute(struct command *, int, int *, int *);
+void mypipe(int *);
+
+/*
+ * set.c
+ */
+struct varent*adrof1(Char *, struct varent *);
+void doset(Char **, struct command *);
+void dolet(Char **, struct command *);
+Char *putn(int);
+int getn(Char *);
+Char *value1(Char *, struct varent *);
+void set(Char *, Char *);
+void set1(Char *, Char **, struct varent *);
+void setq(Char *, Char **, struct varent *);
+void unset(Char **, struct command *);
+void unset1(Char *[], struct varent *);
+void unsetv(Char *);
+void setNS(Char *);
+void shift(Char **, struct command *);
+void plist(struct varent *);
+
+/*
+ * time.c
+ */
+void donice(Char **, struct command *);
+void dotime(Char **, struct command *);
+void prusage(FILE *, struct rusage *, struct rusage *, struct timespec *,
+             struct timespec *);
+void ruadd(struct rusage *, struct rusage *);
+void settimes(void);
+void psecs(long);
+
+/*
+ * alloc.c
+ */
+void Free(ptr_t);
+ptr_t Malloc(size_t);
+ptr_t Realloc(ptr_t, size_t);
+ptr_t Calloc(size_t, size_t);
+
+/*
+ * str.c:
+ */
+#ifdef SHORT_STRINGS
+Char *s_strchr(const Char *, int);
+Char *s_strrchr(const Char *, int);
+Char *s_strcat(Char *, const Char *);
+#ifdef NOTUSED
+Char *s_strncat(Char *, const Char *, size_t);
+#endif
+Char *s_strcpy(Char *, const Char *);
+Char *s_strncpy(Char *, const Char *, size_t);
+Char *s_strspl(const Char *, const Char *);
+size_t s_strlen(const Char *);
+int s_strcmp(const Char *, const Char *);
+int s_strncmp(const Char *, const Char *, size_t);
+Char *s_strsave(const Char *);
+Char *s_strend(const Char *);
+Char *s_strstr(const Char *, const Char *);
+Char *str2short(const char *);
+Char **blk2short(char **);
+char *short2str(const Char *);
+char **short2blk(Char * const *);
+#endif
+char *short2qstr(const Char *);
+char *vis_str(const Char *);
+
+#endif /* !_EXTERN_H_ */
diff --git a/bin/csh/file.c b/bin/csh/file.c
new file mode 100644 (file)
index 0000000..0933b1a
--- /dev/null
@@ -0,0 +1,729 @@
+/* $NetBSD: file.c,v 1.30 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)file.c     8.2 (Berkeley) 3/19/94";
+#else
+__RCSID("$NetBSD: file.c,v 1.30 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#ifdef FILEC
+
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/tty.h>
+
+#include <dirent.h>
+#include <pwd.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifndef SHORT_STRINGS
+#include <string.h>
+#endif /* SHORT_STRINGS */
+
+#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(int);
+static void back_to_col_1(void);
+static int pushback(Char *);
+static void catn(Char *, Char *, size_t);
+static void copyn(Char *, Char *, size_t);
+static Char filetype(Char *, Char *);
+static void print_by_column(Char *, Char *[], size_t);
+static Char *tilde(Char *, Char *);
+static void retype(void);
+static void beep(void);
+static void print_recognized_stuff(Char *);
+static void extract_dir_and_name(Char *, Char *, Char *);
+static Char *getentry(DIR *, int);
+static void free_items(Char **, size_t);
+static size_t tsearch(Char *, COMMAND, size_t);
+static int recognize(Char *, Char *, size_t, size_t);
+static int is_prefix(Char *, Char *);
+static int is_suffix(Char *, Char *);
+static int ignored(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.
+ */
+int filec = 0;
+
+static void
+setup_tty(int on)
+{
+    struct termios tchars;
+
+    (void)tcgetattr(SHIN, &tchars);
+
+    if (on) {
+       tchars.c_cc[VEOL] = ESC;
+       if (tchars.c_lflag & ICANON)
+           on = TCSADRAIN;
+       else {
+           tchars.c_lflag |= ICANON;
+           on = TCSAFLUSH;
+       }
+    }
+    else {
+       tchars.c_cc[VEOL] = _POSIX_VDISABLE;
+       on = TCSADRAIN;
+    }
+
+    (void)tcsetattr(SHIN, on, &tchars);
+}
+
+/*
+ * Move back to beginning of current line
+ */
+static void
+back_to_col_1(void)
+{
+    struct termios tty, tty_normal;
+    sigset_t nsigset, osigset;
+
+    sigemptyset(&nsigset);
+    (void)sigaddset(&nsigset, SIGINT);
+    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+    (void)tcgetattr(SHOUT, &tty);
+    tty_normal = tty;
+    tty.c_iflag &= ~INLCR;
+    tty.c_oflag &= ~ONLCR;
+    (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
+    (void)write(SHOUT, "\r", 1);
+    (void)tcsetattr(SHOUT, TCSADRAIN, &tty_normal);
+    (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+}
+
+/*
+ * Push string contents back into tty queue
+ */
+static int
+pushback(Char *string)
+{
+    struct termios tty, tty_normal;
+    char buf[64], svchars[sizeof(buf)];
+    sigset_t nsigset, osigset;
+    Char *p;
+    size_t bufidx, i, len_str, nbuf, nsv, onsv, retrycnt;
+    char c;
+
+    nsv = 0;
+    sigemptyset(&nsigset);
+    (void)sigaddset(&nsigset, SIGINT);
+    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+    (void)tcgetattr(SHOUT, &tty);
+    tty_normal = tty;
+    tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
+    /* FIONREAD works only in noncanonical mode. */
+    tty.c_lflag &= ~ICANON;
+    tty.c_cc[VMIN] = 0;
+    (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
+
+    for (retrycnt = 5; ; retrycnt--) {
+       /*
+        * Push back characters.
+        */
+       for (p = string; (c = (char)*p) != '\0'; p++)
+           (void)ioctl(SHOUT, TIOCSTI, (ioctl_t) &c);
+       for (i = 0; i < nsv; i++)
+           (void)ioctl(SHOUT, TIOCSTI, (ioctl_t) &svchars[i]);
+
+       if (retrycnt == 0)
+           break;              /* give up salvaging characters */
+
+       len_str = (size_t)(p - string);
+
+       if (ioctl(SHOUT, FIONREAD, (ioctl_t) &nbuf) ||
+           nbuf <= len_str + nsv ||    /* The string fit. */
+           nbuf > sizeof(buf))         /* For future binary compatibility
+                                          (and safety). */
+           break;
+
+       /*
+        * User has typed characters before the pushback finished.
+        * Salvage the characters.
+        */
+
+       /* This read() should be in noncanonical mode. */
+       if (read(SHOUT, &buf, nbuf) != (ssize_t)nbuf)
+           continue;           /* hangup? */
+
+       onsv = nsv;
+       for (bufidx = 0, i = 0; bufidx < nbuf; bufidx++, i++) {
+           c = buf[bufidx];
+           if ((i < len_str) ? c != (char)string[i] :
+                       (i < len_str + onsv) ? c != svchars[i - len_str] : 1) {
+               /* Salvage a character. */
+               if (nsv < (int)(sizeof svchars / sizeof svchars[0])) {
+                   svchars[nsv++] = c;
+                   i--;        /* try this comparison with the next char */
+               } else
+                   break;      /* too many */
+           }
+       }
+    }
+
+#if 1
+    /*
+     * XXX  Is this a bug or a feature of kernel tty driver?
+     *
+     * FIONREAD in canonical mode does not return correct byte count
+     * in tty input queue, but this is required to avoid unwanted echo.
+     */
+    tty.c_lflag |= ICANON;
+    (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
+    (void)ioctl(SHOUT, FIONREAD, (ioctl_t) &i);
+#endif
+    (void)tcsetattr(SHOUT, TCSADRAIN, &tty_normal);
+    (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+
+    return (int)nsv;
+}
+
+/*
+ * Concatenate src onto tail of des.
+ * Des is a string whose maximum length is count.
+ * Always null terminate.
+ */
+static void
+catn(Char *des, Char *src, size_t 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(Char *des, Char *src, size_t count)
+{
+    while (count-- > 0)
+       if ((*des++ = *src++) == 0)
+           return;
+    *des = '\0';
+}
+
+static Char
+filetype(Char *dir, Char *file)
+{
+    struct stat statb;
+    Char path[MAXPATHLEN];
+
+    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(Char *dir, Char *items[], size_t count)
+{
+    size_t c, columns, i, maxwidth, r, rows;
+
+    maxwidth = 0;
+
+    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) {
+               size_t w;
+
+               (void)fprintf(cshout, "%s", vis_str(items[i]));
+               (void)fputc(dir ? filetype(dir, items[i]) : ' ', cshout);
+               if (c < columns - 1) {  /* last column? */
+                   w = Strlen(items[i]) + 1;
+                   for (; w < maxwidth; w++)
+                       (void) fputc(' ', cshout);
+               }
+           }
+       }
+       (void)fputc('\r', cshout);
+       (void)fputc('\n', cshout);
+    }
+}
+
+/*
+ * Expand file name with possible tilde usage
+ *     ~person/mumble
+ * expands to
+ *     home_directory_of_person/mumble
+ */
+static Char *
+tilde(Char *new, Char *old)
+{
+    static Char person[40];
+    struct passwd *pw;
+    Char *o, *p;
+
+    if (old[0] != '~')
+       return (Strcpy(new, old));
+
+    for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
+       continue;
+    *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(void)
+{
+    struct termios tty;
+
+    (void)tcgetattr(SHOUT, &tty);
+    tty.c_lflag |= PENDIN;
+    (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
+}
+
+static void
+beep(void)
+{
+    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(Char *recognized_part)
+{
+    /* An optimized erasing of that silly ^[ */
+    (void)fputc('\b', cshout);
+    (void)fputc('\b', cshout);
+    switch (Strlen(recognized_part)) {
+    case 0:                    /* erase two Characters: ^[ */
+       (void)fputc(' ', cshout);
+       (void)fputc(' ', cshout);
+       (void)fputc('\b', cshout);
+       (void)fputc('\b', cshout);
+       break;
+    case 1:                    /* overstrike the ^, erase the [ */
+       (void)fprintf(cshout, "%s", vis_str(recognized_part));
+       (void)fputc(' ', cshout);
+       (void)fputc('\b', cshout);
+       break;
+    default:                   /* overstrike both Characters ^[ */
+       (void)fprintf(cshout, "%s", vis_str(recognized_part));
+       break;
+    }
+    (void)fflush(cshout);
+}
+
+/*
+ * 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(Char *path, Char *dir, Char *name)
+{
+    Char *p;
+
+    p = Strrchr(path, '/');
+    if (p == NULL) {
+       copyn(name, path, MAXNAMLEN);
+       dir[0] = '\0';
+    }
+    else {
+       copyn(name, ++p, MAXNAMLEN);
+       copyn(dir, path, (size_t)(p - path));
+    }
+}
+
+static Char *
+getentry(DIR *dir_fd, int looking_for_lognames)
+{
+    struct dirent *dirp;
+    struct passwd *pw;
+
+    if (looking_for_lognames) {
+       if ((pw = getpwent()) == NULL)
+           return (NULL);
+       return (str2short(pw->pw_name));
+    }
+    if ((dirp = readdir(dir_fd)) != NULL)
+       return (str2short(dirp->d_name));
+    return (NULL);
+}
+
+static void
+free_items(Char **items, size_t numitems)
+{
+    size_t i;
+
+    for (i = 0; i < numitems; i++)
+       xfree((ptr_t) items[i]);
+    xfree((ptr_t) items);
+}
+
+#define FREE_ITEMS(items, numitems) { \
+       sigset_t nsigset, osigset;\
+\
+       sigemptyset(&nsigset);\
+       (void) sigaddset(&nsigset, SIGINT);\
+       (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);\
+       free_items(items, numitems);\
+       (void) sigprocmask(SIG_SETMASK, &osigset, NULL);\
+}
+
+/*
+ * Perform a RECOGNIZE or LIST command on string "word".
+ */
+static size_t
+tsearch(Char *word, COMMAND command, size_t max_word_length)
+{
+    Char dir[MAXPATHLEN + 1], extended_name[MAXNAMLEN + 1];
+    Char name[MAXNAMLEN + 1], tilded_dir[MAXPATHLEN + 1];
+    DIR *dir_fd;
+    Char *entry;
+    int ignoring, looking_for_lognames;
+    size_t name_length, nignored, numitems;
+    Char **items = NULL;
+    size_t maxitems = 0;
+
+    numitems = 0;
+    ignoring = TRUE;
+    nignored = 0;
+
+    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)) != NULL;) {
+       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 ((size_t)numitems >= maxitems) {
+               maxitems += 1024;
+               if (items == NULL)
+                       items = xmalloc(sizeof(*items) * maxitems);
+               else
+                       items = xrealloc((ptr_t) items,
+                           sizeof(*items) * maxitems);
+           }
+           items[numitems] = 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, numitems);
+    }
+    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(Char *extended_name, Char *entry, size_t name_length, size_t numitems)
+{
+    if (numitems == 1)         /* 1st match */
+       copyn(extended_name, entry, MAXNAMLEN);
+    else {                     /* 2nd & subsequent matches */
+       Char *ent, *x;
+       size_t len = 0;
+
+       x = extended_name;
+       for (ent = entry; *x && *x == *ent++; x++, len++)
+           continue;
+       *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(Char *check, Char *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 its suffix.
+ */
+static int
+is_suffix(Char *check, Char *template)
+{
+    Char *c, *t;
+
+    for (c = check; *c++;)
+       continue;
+    for (t = template; *t++;)
+       continue;
+    for (;;) {
+       if (t == template)
+           return 1;
+       if (c == check || *--t != *--c)
+           return 0;
+    }
+}
+
+ssize_t
+tenex(Char *inputline, size_t inputline_size)
+{
+    char tinputline[BUFSIZE];
+    ssize_t num_read;
+    size_t numitems;
+
+    setup_tty(ON);
+
+    while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) {
+       size_t i, nr = (size_t) num_read;
+
+
+       static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
+       '>', '(', ')', '|', '^', '%', '\0'};
+       Char *str_end, *word_start, last_Char, should_retype;
+       size_t space_left;
+       COMMAND command;
+
+       for (i = 0; i < nr; i++)
+           inputline[i] = (unsigned char) tinputline[i];
+       last_Char = inputline[nr - 1] & ASCII;
+
+       if (last_Char == '\n' || nr == inputline_size)
+           break;
+       command = (last_Char == ESC) ? RECOGNIZE : LIST;
+       if (command == LIST)
+           (void)fputc('\n', cshout);
+       str_end = &inputline[nr];
+       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 - (size_t)(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 (pushback(inputline))
+           should_retype = TRUE;
+       if (should_retype) {
+           if (command == RECOGNIZE)
+               (void) fputc('\n', cshout);
+           printprompt();
+       }
+       if (should_retype)
+           retype();
+    }
+    setup_tty(OFF);
+    return num_read;
+}
+
+static int
+ignored(Char *entry)
+{
+    struct varent *vp;
+    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 (file)
index 0000000..2501a82
--- /dev/null
@@ -0,0 +1,1456 @@
+/* $NetBSD: func.c,v 1.40 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)func.c     8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: func.c,v 1.40 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <locale.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "csh.h"
+#include "extern.h"
+#include "pathnames.h"
+
+extern char **environ;
+extern int progprintf(int, char **);
+
+static void islogin(void);
+static void reexecute(struct command *);
+static void preread(void);
+static void doagain(void);
+static void search(int, int, Char *);
+static int getword(Char *);
+static int keyword(Char *);
+static void toend(void);
+static void xecho(int, Char **);
+static void Unsetenv(Char *);
+static void wpfree(struct whyle *);
+
+struct biltins *
+isbfunc(struct command *t)
+{
+    static struct biltins label = {"", dozip, 0, 0};
+    static struct biltins foregnd = {"%job", dofg1, 0, 0};
+    static struct biltins backgnd = {"%job &", dobg1, 0, 0};
+    struct biltins *bp, *bp1, *bp2;
+    Char *cp;
+
+    cp = t->t_dcom[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;) {
+       int 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(struct command *t, 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
+/*ARGSUSED*/
+doonintr(Char **v, struct command *t)
+{
+    Char *cp, *vv;
+    sigset_t nsigset;
+
+    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) {
+           sigemptyset(&nsigset);
+           (void)sigaddset(&nsigset, SIGINT);
+           (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
+       } 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
+/*ARGSUSED*/
+donohup(Char **v, struct command *t)
+{
+    if (intty)
+       stderror(ERR_NAME | ERR_TERMINAL);
+    if (setintr == 0) {
+       (void) signal(SIGHUP, SIG_IGN);
+    }
+}
+
+void
+/*ARGSUSED*/
+dozip(Char **v, struct command *t)
+{
+    ;
+}
+
+void
+prvars(void)
+{
+    plist(&shvhed);
+}
+
+void
+/*ARGSUSED*/
+doalias(Char **v, struct command *t)
+{
+    struct varent *vp;
+    Char *p;
+
+    v++;
+    p = *v++;
+    if (p == 0)
+       plist(&aliases);
+    else if (*v == 0) {
+       vp = adrof1(strip(p), &aliases);
+       if (vp) {
+           blkpr(cshout, vp->vec);
+           (void) fputc('\n', cshout);
+       }
+    }
+    else {
+       if (eq(p, STRalias) || eq(p, STRunalias)) {
+           setname(vis_str(p));
+           stderror(ERR_NAME | ERR_DANGER);
+       }
+       set1(strip(p), saveblk(v), &aliases);
+    }
+}
+
+void
+/*ARGSUSED*/
+unalias(Char **v, struct command *t)
+{
+    unset1(v, &aliases);
+}
+
+void
+/*ARGSUSED*/
+dologout(Char **v, struct command *t)
+{
+    islogin();
+    goodbye();
+}
+
+void
+/*ARGSUSED*/
+dologin(Char **v, struct command *t)
+{
+    islogin();
+    rechist();
+    (void)signal(SIGTERM, parterm);
+    (void)execl(_PATH_LOGIN, "login", short2str(v[1]), NULL);
+    untty();
+    xexit(1);
+    /* NOTREACHED */
+}
+
+static void
+islogin(void)
+{
+    if (chkstop == 0 && setintr)
+       panystop(0);
+    if (loginsh)
+       return;
+    stderror(ERR_NOTLOGIN);
+    /* NOTREACHED */
+}
+
+void
+doif(Char **v, struct command *kp)
+{
+    Char **vv;
+    int i;
+
+    v++;
+    i = expr(&v);
+    vv = v;
+    if (*vv == NULL)
+       stderror(ERR_NAME | ERR_EMPTYIF);
+    if (eq(*vv, STRthen)) {
+       if (*++vv)
+           stderror(ERR_NAME | ERR_IMPRTHEN);
+       setname(vis_str(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, (size_t)(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(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
+/*ARGSUSED*/
+doelse(Char **v, struct command *t)
+{
+    search(T_ELSE, 0, NULL);
+}
+
+void
+/*ARGSUSED*/
+dogoto(Char **v, struct command *t)
+{
+    Char *lp;
+
+    gotolab(lp = globone(v[1], G_ERROR));
+    xfree((ptr_t) lp);
+}
+
+void
+gotolab(Char *lab)
+{
+    struct whyle *wp;
+    /*
+     * 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.
+     */
+    for (wp = whyles; wp; wp = wp->w_next)
+       if (wp->w_end.type == F_SEEK && wp->w_end.f_seek == 0) {
+           search(T_BREAK, 0, NULL);
+           btell(&wp->w_end);
+       }
+       else
+           bseek(&wp->w_end);
+    search(T_GOTO, 0, lab);
+    /*
+     * Eliminate loops which were exited.
+     */
+    wfree();
+}
+
+void
+/*ARGSUSED*/
+doswitch(Char **v, struct command *t)
+{
+    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
+/*ARGSUSED*/
+dobreak(Char **v, struct command *t)
+{
+    if (whyles)
+       toend();
+    else
+       stderror(ERR_NAME | ERR_NOTWHILE);
+}
+
+void
+/*ARGSUSED*/
+doexit(Char **v, struct command *t)
+{
+    if (chkstop == 0 && (intty || intact) && evalvec == 0)
+       panystop(0);
+    /*
+     * Don't DEMAND parentheses here either.
+     */
+    v++;
+    if (*v) {
+       set(STRstatus, putn(expr(&v)));
+       if (*v)
+           stderror(ERR_NAME | ERR_EXPRESSION);
+    }
+    btoeof();
+    if (intty)
+       (void) close(SHIN);
+}
+
+void
+/*ARGSUSED*/
+doforeach(Char **v, struct command *t)
+{
+    struct whyle *nwp;
+    Char *cp, *sp;
+
+    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;
+    btell(&nwp->w_start);
+    nwp->w_fename = Strsave(cp);
+    nwp->w_next = whyles;
+    nwp->w_end.type = F_SEEK;
+    whyles = nwp;
+    /*
+     * Pre-read the loop so as to be more comprehensible to a terminal user.
+     */
+    if (intty)
+       preread();
+    doagain();
+}
+
+void
+/*ARGSUSED*/
+dowhile(Char **v, struct command *t)
+{
+    int status;
+    int again;
+
+    again = whyles != 0 && SEEKEQ(&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 = !expr(&v);
+    if (*v)
+       stderror(ERR_NAME | ERR_EXPRESSION);
+    if (!again) {
+       struct whyle *nwp =
+       (struct whyle *)xcalloc(1, sizeof(*nwp));
+
+       nwp->w_start = lineloc;
+       nwp->w_end.type = F_SEEK;
+       nwp->w_end.f_seek = 0;
+       nwp->w_next = whyles;
+       whyles = nwp;
+       if (intty) {
+           /*
+            * The tty preread
+            */
+           preread();
+           doagain();
+           return;
+       }
+    }
+    if (status)
+       /* We ain't gonna loop no more, no more! */
+       toend();
+}
+
+static void
+preread(void)
+{
+    sigset_t nsigset;
+
+    whyles->w_end.type = I_SEEK;
+    if (setintr) {
+       sigemptyset(&nsigset);
+       (void) sigaddset(&nsigset, SIGINT);
+       (void) sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
+    }
+
+    search(T_BREAK, 0, NULL);          /* read the expression in */
+    if (setintr)
+       (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
+    btell(&whyles->w_end);
+}
+
+void
+/*ARGSUSED*/
+doend(Char **v, struct command *t)
+{
+    if (!whyles)
+       stderror(ERR_NAME | ERR_NOTWHILE);
+    btell(&whyles->w_end);
+    doagain();
+}
+
+void
+/*ARGSUSED*/
+docontin(Char **v, struct command *t)
+{
+    if (!whyles)
+       stderror(ERR_NAME | ERR_NOTWHILE);
+    doagain();
+}
+
+static void
+doagain(void)
+{
+    /* 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(NULL, NULL);
+       return;
+    }
+    set(whyles->w_fename, Strsave(*whyles->w_fe++));
+    bseek(&whyles->w_start);
+}
+
+void
+dorepeat(Char **v, struct command *kp)
+{
+    int i;
+    sigset_t nsigset;
+
+    i = getn(v[1]);
+    if (setintr) {
+       sigemptyset(&nsigset);
+       (void)sigaddset(&nsigset, SIGINT);
+       (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
+    }
+    lshift(v, 2);
+    while (i > 0) {
+       if (setintr)
+           (void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
+       reexecute(kp);
+       --i;
+    }
+    donefds();
+    if (setintr)
+       (void) sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
+}
+
+void
+/*ARGSUSED*/
+doswbrk(Char **v, struct command *t)
+{
+    search(T_BRKSW, 0, NULL);
+}
+
+int
+srchx(Char *cp)
+{
+    struct srch *sp, *sp1, *sp2;
+    int 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*/
+static void
+search(int type, int level, Char *goal)
+{
+    Char wordbuf[BUFSIZE];
+    Char *aword, *cp;
+    struct whyle *wp;
+    int wlevel = 0;
+
+    aword = wordbuf;
+    Stype = (Char)type;
+    Sgoal = goal;
+    if (type == T_GOTO) {
+       struct Ain a;
+       a.type = F_SEEK;
+       a.f_seek = 0;
+       bseek(&a);
+    }
+    do {
+       if (intty && fseekp == feobp && aret == F_SEEK)
+           (void)fprintf(cshout, "? "), (void)fflush(cshout);
+       aword[0] = 0;
+       (void)getword(aword);
+       switch (srchx(aword)) {
+       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;
+       case T_ELSE:
+           if (level == 0 && type == T_IF)
+               return;
+           break;
+       case T_END:
+           if (type == T_BRKSW) {
+               if (wlevel == 0) {
+                   wp = whyles;
+                   if (wp) {
+                       whyles = wp->w_next;
+                       wpfree(wp);
+                   }
+               }
+           }
+           if (type == T_BREAK)
+               level--;
+           wlevel--;
+           break;
+       case T_ENDIF:
+           if (type == T_IF || type == T_ELSE)
+               level--;
+           break;
+       case T_ENDSW:
+           if (type == T_SWITCH || type == T_BRKSW)
+               level--;
+           break;
+       case T_IF:
+           while (getword(aword))
+               continue;
+           if ((type == T_IF || type == T_ELSE) &&
+               eq(aword, STRthen))
+               level++;
+           break;
+       case T_LABEL:
+           if (type == T_GOTO && getword(aword) && eq(aword, goal))
+               level = -1;
+           break;
+       case T_SWITCH:
+           if (type == T_SWITCH || type == T_BRKSW)
+               level++;
+           break;
+       case T_FOREACH: 
+       case T_WHILE:
+           wlevel++;
+           if (type == T_BREAK)
+               level++;
+           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;
+       }
+       (void) getword(NULL);
+    } while (level >= 0);
+}
+
+static void
+wpfree(struct whyle *wp)
+{ 
+    if (wp->w_fe0)
+       blkfree(wp->w_fe0); 
+    if (wp->w_fename)
+       xfree((ptr_t) wp->w_fename);
+    xfree((ptr_t) wp);
+}
+
+static int
+getword(Char *wp)
+{
+    int c, d, found, kwd;
+    Char *owp;
+
+    c = readc(1);
+    d = 0;
+    found = 0;
+    kwd = 0;
+    owp = wp;
+    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++ = (Char)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_BREAK:
+       stderror(ERR_NAME | ERR_NOTFOUND, "end");
+       /* NOTREACHED */
+    case T_ELSE:
+       stderror(ERR_NAME | ERR_NOTFOUND, "endif");
+       /* NOTREACHED */
+    case T_GOTO:
+       setname(vis_str(Sgoal));
+       stderror(ERR_NAME | ERR_NOTFOUND, "label");
+       /* NOTREACHED */
+    case T_IF:
+       stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
+       /* NOTREACHED */
+    case T_BRKSW:
+    case T_SWITCH:
+       stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
+       /* 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(Char *wp)
+{
+    static Char STRswitch[] = {'s', 'w', 'i', 't', 'c', 'h', '\0'};
+    static Char STRwhile[] = {'w', 'h', 'i', 'l', 'e', '\0'};
+    static Char STRif[] = {'i', 'f', '\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(void)
+{
+    if (whyles->w_end.type == F_SEEK && whyles->w_end.f_seek == 0) {
+       search(T_BREAK, 0, NULL);
+       btell(&whyles->w_end);
+       whyles->w_end.f_seek--;
+    }
+    else
+       bseek(&whyles->w_end);
+    wfree();
+}
+
+void
+wfree(void)
+{
+    struct Ain o;
+    struct whyle *nwp;
+
+    btell(&o);
+
+    for (; whyles; whyles = nwp) {
+       struct whyle *wp = whyles;
+       nwp = wp->w_next;
+
+       /*
+        * We free loops that have different seek types.
+        */
+       if (wp->w_end.type != I_SEEK && wp->w_start.type == wp->w_end.type &&
+           wp->w_start.type == o.type) {
+           if (wp->w_end.type == F_SEEK) {
+               if (o.f_seek >= wp->w_start.f_seek && 
+                   (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
+                   break;
+           }
+           else {
+               if (o.a_seek >= wp->w_start.a_seek && 
+                   (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
+                   break;
+           }
+       }
+
+       wpfree(wp);
+    }
+}
+
+void
+/*ARGSUSED*/
+doecho(Char **v, struct command *t)
+{
+    xecho(' ', v);
+}
+
+void
+/*ARGSUSED*/
+doglob(Char **v, struct command *t)
+{
+    xecho(0, v);
+    (void)fflush(cshout);
+}
+
+static void
+xecho(int sep, Char **v)
+{
+    Char *cp;
+    sigset_t nsigset;
+    int nonl;
+
+    nonl = 0;
+    if (setintr) {
+       sigemptyset(&nsigset);
+       (void)sigaddset(&nsigset, SIGINT);
+       (void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
+    }
+    v++;
+    if (*v == 0)
+       goto done;
+    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++) != NULL) {
+       int c;
+
+       while ((c = *cp++) != '\0')
+           (void)vis_fputc(c | QUOTE, cshout);
+
+       if (*v)
+           (void)vis_fputc(sep | QUOTE, cshout);
+    }
+done:
+    if (sep && nonl == 0)
+       (void)fputc('\n', cshout);
+    else
+       (void)fflush(cshout);
+    if (setintr)
+       (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
+    if (gargv)
+       blkfree(gargv), gargv = 0;
+}
+
+void
+/*ARGSUSED*/
+dosetenv(Char **v, struct command *t)
+{
+    Char *lp, *vp;
+    sigset_t nsigset;
+
+    v++;
+    if ((vp = *v++) == 0) {
+       Char **ep;
+
+       if (setintr) {
+           sigemptyset(&nsigset);
+           (void)sigaddset(&nsigset, SIGINT);
+           (void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
+       }
+       for (ep = STR_environ; *ep; ep++)
+           (void)fprintf(cshout, "%s\n", vis_str(*ep));
+       return;
+    }
+    if ((lp = *v++) == 0)
+       lp = STRNULL;
+    Setenv(vp, lp = globone(lp, G_APPEND));
+    if (eq(vp, STRPATH)) {
+       importpath(lp);
+       dohash(NULL, NULL);
+    }
+    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++)
+               continue;
+       AsciiOnly = k > 0377;
+#else
+       AsciiOnly = 0;
+#endif                         /* NLS */
+    }
+    xfree((ptr_t) lp);
+}
+
+void
+/*ARGSUSED*/
+dounsetenv(Char **v, struct command *t)
+{
+    static Char *name = NULL;
+    Char **ep, *p, *n;
+    int i, maxi;
+
+    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++)
+           continue;
+       if (i > maxi)
+           maxi = i;
+    }
+
+    name = 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++)
+                   continue;
+               *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++)
+                       continue;
+                   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(Char *name, Char *val)
+{
+    Char *blk[2], *cp, *dp, **ep, **oep;
+
+    ep = STR_environ;
+    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(Char *name)
+{
+    Char *cp, *dp, **ep, **oep;
+
+    ep = STR_environ;
+    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
+/*ARGSUSED*/
+doumask(Char **v, struct command *t)
+{
+    Char *cp;
+    mode_t i;
+
+    cp = v[1];
+    if (cp == 0) {
+       i = umask(0);
+       (void)umask(i);
+       (void)fprintf(cshout, "%o\n", i);
+       return;
+    }
+    i = 0;
+    while (Isdigit(*cp) && *cp != '8' && *cp != '9')
+       i = i * 8 + (mode_t)(*cp++ - '0');
+    if (*cp || i > 0777)
+       stderror(ERR_NAME | ERR_MASK);
+    (void)umask(i);
+}
+
+typedef rlim_t RLIM_TYPE;
+
+static const struct limits {
+    int     limconst;
+    const   char *limname;
+    int     limdiv;
+    const   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_NTHR,     "maxthread",    1,      "" },
+    { RLIMIT_NOFILE,   "openfiles",    1,      "" },
+    { RLIMIT_SBSIZE,   "sbsize",       1,      "bytes" },
+    { RLIMIT_AS,       "vmemoryuse",   1024,   "kbytes" },
+    { -1,              NULL,           0,      NULL }
+};
+
+static const struct limits *findlim(Char *);
+static RLIM_TYPE getval(const struct limits *, Char **);
+static void limtail(Char *, const char *);
+static void plim(const struct limits *, Char);
+static int setlim(const struct limits *, Char, RLIM_TYPE);
+
+static const struct limits *
+findlim(Char *cp)
+{
+    const struct limits *lp, *res;
+
+    res = 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 */
+}
+
+void
+/*ARGSUSED*/
+dolimit(Char **v, struct command *t)
+{
+    const struct limits *lp;
+    RLIM_TYPE limit;
+    char hard;
+
+    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(const struct limits *lp, Char **v)
+{
+    Char *cp;
+    double d;
+
+    cp = *v++;
+    d = atof(short2str(cp));
+
+    while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
+       cp++;
+    if (*cp == 0) {
+       if (*v == 0)
+           return ((RLIM_TYPE)((d + 0.5) * lp->limdiv));
+       cp = *v;
+    }
+    switch (*cp) {
+    case ':':
+       if (lp->limconst != RLIMIT_CPU)
+           goto badscal;
+       return ((RLIM_TYPE)(d * 60.0 + atof(short2str(cp + 1))));
+    case 'M':
+       if (lp->limconst == RLIMIT_CPU)
+           goto badscal;
+       *cp = 'm';
+       limtail(cp, "megabytes");
+       d *= 1024.0 * 1024.0;
+       break;
+    case 'h':
+       if (lp->limconst != RLIMIT_CPU)
+           goto badscal;
+       limtail(cp, "hours");
+       d *= 3600.0;
+       break;
+    case 'k':
+       if (lp->limconst == RLIMIT_CPU)
+           goto badscal;
+       limtail(cp, "kbytes");
+       d *= 1024.0;
+       break;
+    case 'm':
+       if (lp->limconst == RLIMIT_CPU) {
+           limtail(cp, "minutes");
+           d *= 60.0;
+           break;
+       }
+       *cp = 'm';
+       limtail(cp, "megabytes");
+       d *= 1024.0 * 1024.0;
+       break;
+    case 's':
+       if (lp->limconst != RLIMIT_CPU)
+           goto badscal;
+       limtail(cp, "seconds");
+       break;
+    case 'u':
+       limtail(cp, "unlimited");
+       return (RLIM_INFINITY);
+    default:
+    badscal:
+       stderror(ERR_NAME | ERR_SCALEF);
+       /* NOTREACHED */
+    }
+    d += 0.5;
+    if (d > (double) RLIM_INFINITY)
+       return RLIM_INFINITY;
+    else
+       return ((RLIM_TYPE)d);
+}
+
+static void
+limtail(Char *cp, const char *str)
+{
+    while (*cp && *cp == *str)
+       cp++, str++;
+    if (*cp)
+       stderror(ERR_BADSCALE, str);
+}
+
+
+/*ARGSUSED*/
+static void
+plim(const struct limits *lp, Char hard)
+{
+    struct rlimit rlim;
+    RLIM_TYPE limit;
+
+    (void)fprintf(cshout, "%-13.13s", lp->limname);
+
+    (void)getrlimit(lp->limconst, &rlim);
+    limit = hard ? rlim.rlim_max : rlim.rlim_cur;
+
+    if (limit == RLIM_INFINITY)
+       (void)fprintf(cshout, "unlimited");
+    else if (lp->limconst == RLIMIT_CPU)
+       psecs((long) limit);
+    else
+       (void)fprintf(cshout, "%jd %s",
+           (intmax_t) (limit / (RLIM_TYPE)lp->limdiv), lp->limscale);
+    (void)fputc('\n', cshout);
+}
+
+void
+/*ARGSUSED*/
+dounlimit(Char **v, struct command *t)
+{
+    const struct limits *lp;
+    int lerr;
+    Char hard;
+
+    lerr = 0;
+    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(const 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 (rlim.rlim_max < rlim.rlim_cur)
+       rlim.rlim_max = rlim.rlim_cur;
+
+    if (setrlimit(lp->limconst, &rlim) < 0) {
+       (void)fprintf(csherr, "%s: %s: Can't %s%s limit (%s)\n", bname,
+           lp->limname, limit == RLIM_INFINITY ? "remove" : "set",
+           hard ? " hard" : "", strerror(errno));
+       return (-1);
+    }
+    return (0);
+}
+
+void
+/*ARGSUSED*/
+dosuspend(Char **v, struct command *t)
+{
+    int     ctpgrp;
+    void    (*old)(int);
+
+    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 arguments, 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!
+ */
+static Char **gv = NULL;
+
+void
+/*ARGSUSED*/
+doeval(Char **v, struct command *t)
+{
+    jmp_buf osetexit;
+    Char *oevalp, **oevalvec, **savegv;
+    int my_reenter, odidfds, oSHERR, oSHIN, oSHOUT, saveERR, saveIN, saveOUT;
+
+    savegv = gv;
+    UNREGISTER(v);
+
+    oevalvec = evalvec;
+    oevalp = evalp;
+    odidfds = didfds;
+    oSHIN = SHIN;
+    oSHOUT = SHOUT;
+    oSHERR = SHERR;
+
+    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);
+    saveERR = dcopy(SHERR, -1);
+
+    getexit(osetexit);
+
+    if ((my_reenter = setexit()) == 0) {
+       evalvec = v;
+       evalp = 0;
+       SHIN = dcopy(0, -1);
+       SHOUT = dcopy(1, -1);
+       SHERR = dcopy(2, -1);
+       didfds = 0;
+       process(0);
+    }
+
+    evalvec = oevalvec;
+    evalp = oevalp;
+    doneinp = 0;
+    didfds = odidfds;
+    if (SHIN != -1)
+       (void)close(SHIN);
+    if (SHOUT != -1)
+       (void)close(SHOUT);
+    if (SHERR != -1)
+       (void)close(SHERR);
+    SHIN = dmove(saveIN, oSHIN);
+    SHOUT = dmove(saveOUT, oSHOUT);
+    SHERR = dmove(saveERR, oSHERR);
+    if (gv)
+       blkfree(gv), gv = NULL;
+    resexit(osetexit);
+    gv = savegv;
+    if (my_reenter)
+       stderror(ERR_SILENT);
+}
+
+void
+/*ARGSUSED*/
+doprintf(Char **v, struct command *t)
+{
+    char **c;
+    int ret;
+
+    ret = progprintf(blklen(v), c = short2blk(v));
+    (void)fflush(cshout);
+    (void)fflush(csherr);
+
+    blkfree((Char **) c);
+    if (ret)
+       stderror(ERR_SILENT);
+}
diff --git a/bin/csh/glob.c b/bin/csh/glob.c
new file mode 100644 (file)
index 0000000..7f01c9c
--- /dev/null
@@ -0,0 +1,923 @@
+/* $NetBSD: glob.c,v 1.27 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)glob.c     8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: glob.c,v 1.27 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <glob.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "csh.h"
+#include "extern.h"
+
+static int noglob;
+static int gargsiz, pargsiz;
+
+/*
+ * 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;
+Char **pargv = NULL;
+long gargc = 0;
+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(Char **, Char *);
+static Char *handleone(Char *, Char **, int);
+static Char **libglob(Char **);
+static Char **globexpand(Char **);
+static int globbrace(Char *, Char *, Char ***);
+static void expbrace(Char ***, Char ***, size_t);
+static int pmatch(Char *, Char *);
+static void pword(void);
+static void psave(int);
+static void backeval(Char *, int);
+
+static Char *
+globtilde(Char **nv, Char *s)
+{
+    Char gbuf[MAXPATHLEN], *b, *e, *gstart, *u;
+
+    gstart = gbuf;
+    *gstart++ = *s++;
+    u = s;
+    for (b = gstart, e = &gbuf[MAXPATHLEN - 1];
+        *s && *s != '/' && *s != ':' && b < e;
+        *b++ = *s++)
+        continue;
+    *b = EOS;
+    if (gethdir(gstart)) {
+       blkfree(nv);
+       if (*gstart)
+           stderror(ERR_UNKUSER, vis_str(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(Char *s, Char *p, Char ***bl)
+{
+    Char gbuf[MAXPATHLEN];
+    Char *lm, *pe, *pl, *pm, **nv, **vl;
+    int i, len, size;
+
+    size = GLOBSPACE;
+    nv = vl = xmalloc(sizeof(Char *) * (size_t)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 (-RBRK);
+           }
+       }
+       else if (*pe == LBRC)
+           i++;
+       else if (*pe == RBRC) {
+           if (i == 0)
+               break;
+           i--;
+       }
+
+    if (i != 0 || *pe == '\0') {
+       blkfree(nv);
+       return (-RBRC);
+    }
+
+    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;
+       default:
+           break;
+       }
+    *vl = NULL;
+    *bl = nv;
+    return (len);
+}
+
+static void
+expbrace(Char ***nvp, Char ***elp, size_t size)
+{
+    Char **ex, **nv, *s, **vl;
+
+    vl = nv = *nvp;
+    if (elp != NULL)
+       ex = *elp;
+    else
+       for (ex = vl; *ex; ex++)
+           continue;
+
+    for (s = *vl; s; s = *++vl) {
+       Char *b, **bp, **vp;
+
+       /* leave {} untouched for find */
+       if (s[0] == '{' && (s[1] == '\0' || (s[1] == '}' && s[2] == '\0')))
+           continue;
+       if ((b = Strchr(s, '{')) != NULL) {
+           Char **bl;
+           int len;
+
+           if ((len = globbrace(s, b, &bl)) < 0) {
+               xfree((ptr_t)nv);
+               stderror(ERR_MISSING, -len);
+           }
+           xfree((ptr_t) s);
+           if (len == 1) {
+               *vl-- = *bl;
+               xfree((ptr_t) bl);
+               continue;
+           }
+           len = blklen(bl);
+           if (&ex[len] >= &nv[size]) {
+               ptrdiff_t l, e;
+
+               l = &ex[len] - &nv[size];
+               size += (size_t)(GLOBSPACE > l ? GLOBSPACE : l);
+               l = vl - nv;
+               e = ex - nv;
+               nv = (Char **)xrealloc((ptr_t)nv,
+                   (size_t)size * sizeof(Char *));
+               vl = nv + l;
+               ex = nv + e;
+           }
+           vp = vl--;
+           *vp = *bl;
+           len--;
+           for (bp = ex; bp != vp; bp--)
+               bp[len] = *bp;
+           ex += len;
+           vp++;
+           for (bp = bl + 1; *bp; *vp++ = *bp++)
+               continue;
+           xfree((ptr_t)bl);
+       }
+
+    }
+    if (elp != NULL)
+       *elp = ex;
+    *nvp = nv;
+}
+
+static Char **
+globexpand(Char **v)
+{
+    Char **ex, **nv, *s, **vl;
+    size_t size;
+
+    size = GLOBSPACE;
+    nv = vl = xmalloc(sizeof(Char *) * size);
+    *vl = NULL;
+
+    /*
+     * Step 1: expand backquotes.
+     */
+    while ((s = *v++) != NULL) {
+       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 * sizeof(Char *));
+               vl = &nv[size - GLOBSPACE];
+           }
+       }
+    }
+    *vl = NULL;
+
+    if (noglob)
+       return (nv);
+
+    /*
+     * Step 2: expand braces
+     */
+    ex = vl;
+    expbrace(&nv, &ex, size);
+
+    /*
+     * Step 3: expand ~
+     */
+    vl = nv;
+    for (s = *vl; s; s = *++vl)
+       if (*s == '~')
+           *vl = globtilde(nv, s);
+    vl = nv;
+    return (vl);
+}
+
+static Char *
+handleone(Char *str, Char **vl, int action)
+{
+    Char *cp, **vlp;
+
+    vlp = vl;
+    switch (action) {
+    case G_ERROR:
+       setname(vis_str(str));
+       blkfree(vl);
+       stderror(ERR_NAME | ERR_AMBIG);
+       /* NOTREACHED */
+    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;
+    default:
+       break;
+    }
+    return (str);
+}
+
+static Char **
+libglob(Char **vl)
+{
+    glob_t globv;
+    char *ptr;
+    int gflgs, magic, match, nonomatch;
+
+    gflgs = GLOB_NOMAGIC;
+    magic = 0;
+    match = 0;
+    nonomatch = adrof(STRnonomatch) != 0;
+
+    if (!vl || !vl[0])
+       return (vl);
+
+    globv.gl_offs = 0;
+    globv.gl_pathv = 0;
+    globv.gl_pathc = 0;
+
+    if (nonomatch)
+       gflgs |= GLOB_NOCHECK;
+
+    do {
+       ptr = short2qstr(*vl);
+       switch (glob(ptr, gflgs, 0, &globv)) {
+       case GLOB_ABORTED:
+           setname(vis_str(*vl));
+           stderror(ERR_NAME | ERR_GLOB);
+           /* NOTREACHED */
+       case GLOB_NOSPACE:
+           stderror(ERR_NOMEM);
+           /* NOTREACHED */
+       default:
+           break;
+       }
+       if (globv.gl_flags & GLOB_MAGCHAR) {
+           match |= (globv.gl_matchc != 0);
+           magic = 1;
+       }
+       gflgs |= GLOB_APPEND;
+    }
+    while (*++vl);
+    vl = (globv.gl_pathc == 0 || (magic && !match && !nonomatch)) ?
+       NULL : blk2short(globv.gl_pathv);
+    globfree(&globv);
+    return (vl);
+}
+
+Char *
+globone(Char *str, int action)
+{
+    Char *v[2], **vl, **vo;
+    int gflg;
+
+    noglob = adrof(STRnoglob) != 0;
+    gflag = 0;
+    v[0] = str;
+    v[1] = 0;
+    tglob(v);
+    gflg = gflag;
+    if (gflg == G_NONE)
+       return (strip(Strsave(str)));
+
+    if (gflg & G_CSH) {
+       /*
+        * Expand back-quote, tilde and brace
+        */
+       vo = globexpand(v);
+       if (noglob || (gflg & 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 || (gflg & G_GLOB) == 0)
+       return (strip(Strsave(str)));
+    else
+       vo = v;
+
+    vl = libglob(vo);
+    if ((gflg & G_CSH) && vl != vo)
+       blkfree(vo);
+    if (vl == NULL) {
+       setname(vis_str(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(Char **v)
+{
+    Char **vl, **vo;
+    int gflg;
+
+    gflg = gflag;
+    if (!v || !v[0]) {
+       gargv = saveblk(v);
+       gargc = blklen(gargv);
+       return (gargv);
+    }
+
+    noglob = adrof(STRnoglob) != 0;
+
+    if (gflg & G_CSH)
+       /*
+        * Expand back-quote, tilde and brace
+        */
+       vl = vo = globexpand(v);
+    else
+       vl = vo = saveblk(v);
+
+    if (!noglob && (gflg & G_GLOB)) {
+       vl = libglob(vo);
+       if ((gflg & G_CSH) && vl != vo)
+           blkfree(vo);
+    }
+    else
+       trim(vl);
+
+    gargc = vl ? blklen(vl) : 0;
+    return (gargv = vl);
+}
+
+void
+ginit(void)
+{
+    gargsiz = GLOBSPACE;
+    gargv = xmalloc(sizeof(Char *) * (size_t)gargsiz);
+    gargv[0] = 0;
+    gargc = 0;
+}
+
+void
+rscan(Char **t, void (*f)(int))
+{
+    Char *p;
+
+    while ((p = *t++) != NULL)
+       while (*p)
+           (*f) (*p++);
+}
+
+void
+trim(Char **t)
+{
+    Char *p;
+
+    while ((p = *t++) != NULL)
+       while (*p)
+           *p++ &= TRIM;
+}
+
+void
+tglob(Char **t)
+{
+    Char *p, c;
+
+    while ((p = *t++) != NULL) {
+       if (*p == '~' || *p == '=')
+           gflag |= G_CSH;
+       else if (*p == '{' &&
+                (p[1] == '\0' || (p[1] == '}' && p[2] == '\0')))
+           continue;
+       while ((c = *p++) != '\0') {
+           /*
+            * eat everything inside the matching backquotes
+            */
+           if (c == '`') {
+               gflag |= G_CSH;
+               while (*p && *p != '`') 
+                   if (*p++ == '\\') {
+                       if (*p)         /* Quoted chars */
+                           p++;
+                       else
+                           break;
+                   }
+               if (*p)                 /* The matching ` */
+                   p++;
+               else
+                   break;
+           }
+           else if (c == '{')
+               gflag |= G_CSH;
+           else if (isglob(c))
+               gflag |= 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(Char *cp, int literal)
+{
+    Char word[MAXPATHLEN], *ep, *lp, *rp;
+
+    if (pargv) {
+#ifdef notdef
+       abort();
+#endif
+       blkfree(pargv);
+    }
+    pargsiz = GLOBSPACE;
+    pargv = xmalloc(sizeof(Char *) * (size_t)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(Char *cp, int literal)
+{
+    struct command faket;
+    char tibuf[BUFSIZE];
+    Char ibuf[BUFSIZE], *fakecom[2], *ip;
+    int pvec[2], c, quoted;
+    ssize_t icnt;
+    int hadnl;
+
+    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 fparaml;
+       struct command *t;
+
+       (void)close(pvec[0]);
+       (void)dmove(pvec[1], 1);
+       (void)dmove(SHERR, 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;
+       for (arginp = cp; *cp; cp++) {
+           *cp &= TRIM;
+           if (*cp == '\n' || *cp == '\r')
+               *cp = ';';
+       }
+
+        /*
+        * In the child ``forget'' everything about current aliases or
+        * eval vectors.
+        */
+       alvec = NULL;
+       evalvec = NULL;
+       alvecp = NULL;
+       evalp = NULL;
+       (void) lex(&fparaml);
+       if (seterr)
+           stderror(ERR_OLD);
+       alias(&fparaml);
+       t = syntax(fparaml.next, &fparaml, 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;
+
+       cnt = 0;
+
+       for (;;) {
+           if (icnt == 0) {
+               int i;
+
+               ip = ibuf;
+               do
+                   icnt = read(pvec[0], tibuf, BUFSIZE);
+               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(int c)
+{
+    if (--pnleft <= 0)
+       stderror(ERR_WTOOLONG);
+    *pargcp++ = (Char)c;
+}
+
+static void
+pword(void)
+{
+    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(Char *string, Char *pattern)
+{
+    Char **blk, **p;
+    int gpol, gres;
+
+    gpol = 1;
+    gres = 0;
+
+    if (*pattern == '^') {
+       gpol = 0;
+       pattern++;
+    }
+
+    blk = xmalloc(GLOBSPACE * sizeof(Char *));
+    blk[0] = Strsave(pattern);
+    blk[1] = NULL;
+
+    expbrace(&blk, NULL, GLOBSPACE);
+
+    for (p = blk; *p; p++)
+       gres |= pmatch(string, *p);
+
+    blkfree(blk);
+    return(gres == gpol);
+} 
+
+static int
+pmatch(Char *string, Char *pattern)
+{
+    int match, negate_range;
+    Char patternc, rangec, stringc;
+
+    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;
+           if ((negate_range = (*pattern == '^')) != 0)
+               pattern++;
+           while ((rangec = *pattern++) != '\0') {
+               if (rangec == ']')
+                   break;
+               if (match)
+                   continue;
+               if (rangec == '-' && *(pattern-2) != '[' && *pattern  != ']') {
+                   match = (stringc <= (*pattern & TRIM) &&
+                             (*(pattern-2) & TRIM) <= stringc);
+                   pattern++;
+               }
+               else 
+                   match = (stringc == (rangec & TRIM));
+           }
+           if (rangec == 0)
+               stderror(ERR_NAME | ERR_MISSING, ']');
+           if (match == negate_range)
+               return (0);
+           break;
+       default:
+           if ((patternc & TRIM) != stringc)
+               return (0);
+           break;
+
+       }
+    }
+}
+
+void
+Gcat(Char *s1, Char *s2)
+{
+    Char *p, *q;
+    ptrdiff_t n;
+
+    for (p = s1; *p++;)
+       continue;
+    for (q = s2; *q++;)
+       continue;
+    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] = xmalloc((size_t)n * sizeof(Char));
+    for (q = s1; (*p++ = *q++) != '\0';)
+       continue;
+    for (p--, q = s2; (*p++ = *q++) != '\0';)
+       continue;
+}
+
+#ifdef FILEC
+int
+sortscmp(const ptr_t a, const ptr_t b)
+{
+#if defined(NLS) && !defined(NOSTRCOLL)
+    char buf[2048];
+#endif
+
+    if (!a)                    /* check for NULL */
+       return (b ? 1 : 0);
+    if (!b)
+       return (-1);
+
+    if (!*(Char **)a)                  /* check for NULL */
+       return (*(Char **)b ? 1 : 0);
+    if (!*(Char **)b)
+       return (-1);
+
+#if defined(NLS) && !defined(NOSTRCOLL)
+    (void)strcpy(buf, short2str(*(Char **)a));
+    return ((int)strcoll(buf, short2str(*(Char **)b)));
+#else
+    return ((int)Strcmp(*(Char **)a, *(Char **)b));
+#endif
+}
+#endif /* FILEC */
diff --git a/bin/csh/hist.c b/bin/csh/hist.c
new file mode 100644 (file)
index 0000000..1c5bf9d
--- /dev/null
@@ -0,0 +1,207 @@
+/* $NetBSD: hist.c,v 1.20 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)hist.c     8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: hist.c,v 1.20 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "csh.h"
+#include "extern.h"
+
+static void hfree(struct Hist *);
+static void dohist1(struct Hist *, int *, int, int);
+static void phist(struct Hist *, int);
+
+void
+savehist(struct wordent *sp)
+{
+    struct Hist *hp, *np;
+    Char *cp;
+    int histlen;
+
+    histlen = 0;
+
+    /* throw away null lines */
+    if (sp->next->word[0] == '\n')
+       return;
+    cp = value(STRhistory);
+    if (*cp) {
+       Char *p = cp;
+
+       while (*p) {
+           if (!Isdigit(*p)) {
+               histlen = 0;
+               break;
+           }
+           histlen = histlen * 10 + *p++ - '0';
+       }
+    }
+    for (hp = &Histlist; (np = hp->Hnext) != NULL;)
+       if (eventno - np->Href >= histlen || histlen == 0)
+           hp->Hnext = np->Hnext, hfree(np);
+       else
+           hp = np;
+    (void) enthist(++eventno, sp, 1);
+}
+
+#ifdef EDIT
+void
+loadhist(struct Hist *hp) {
+    char *h = NULL;
+
+    if (hi == NULL || hp == NULL)
+       return;
+    loadhist(hp->Hnext);
+    if (sprlex(&h, &hp->Hlex) != -1) {
+       HistEvent ev;
+       history(hi, &ev, H_ENTER, h);
+    }
+}
+#endif
+
+struct Hist *
+enthist(int event, struct wordent *lp, int docopy)
+{
+    struct Hist *np;
+
+#ifdef EDIT
+    if (hi) {
+       char *h = NULL;
+       if (sprlex(&h, lp) != -1) {
+           HistEvent ev;
+           history(hi, &ev, H_ENTER, h);
+       }
+    }
+#endif
+    np = xmalloc(sizeof(*np));
+    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(struct Hist *hp)
+{
+    freelex(&hp->Hlex);
+    xfree((ptr_t) hp);
+}
+
+void
+/*ARGSUSED*/
+dohist(Char **v, struct command *t)
+{
+    sigset_t nsigset;
+    int hflg, n, rflg;
+
+    hflg = 0;
+    rflg = 0;
+
+    if (getn(value(STRhistory)) == 0)
+       return;
+    if (setintr) {
+       sigemptyset(&nsigset);
+       (void)sigaddset(&nsigset, SIGINT);
+       (void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
+    }
+    while (*++v && **v == '-') {
+       Char *vp = *v;
+
+       while (*++vp)
+           switch (*vp) {
+           case 'h':
+               hflg++;
+               break;
+           case 'r':
+               rflg++;
+               break;
+           case '-':           /* ignore multiple '-'s */
+               break;
+           default:
+               stderror(ERR_HISTUS);
+               /* NOTREACHED */
+           }
+    }
+    if (*v)
+       n = getn(*v);
+    else {
+       n = getn(value(STRhistory));
+    }
+    dohist1(Histlist.Hnext, &n, rflg, hflg);
+}
+
+static void
+dohist1(struct Hist *hp, int *np, int rflg, int hflg)
+{
+    int print;
+
+    print = (*np) > 0;
+
+    for (; hp != 0; hp = hp->Hnext) {
+       (*np)--;
+       hp->Href++;
+       if (rflg == 0) {
+           dohist1(hp->Hnext, np, rflg, hflg);
+           if (print)
+               phist(hp, hflg);
+           return;
+       }
+       if (*np >= 0)
+           phist(hp, hflg);
+    }
+}
+
+static void
+phist(struct Hist *hp, int hflg)
+{
+    if (hflg == 0)
+       (void)fprintf(cshout, "%6d\t", hp->Hnum);
+    prlex(cshout, &hp->Hlex);
+}
diff --git a/bin/csh/init.c b/bin/csh/init.c
new file mode 100644 (file)
index 0000000..06a3427
--- /dev/null
@@ -0,0 +1,131 @@
+/* $NetBSD: init.c,v 1.11 2013/01/22 19:28:00 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)init.c     8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: init.c,v 1.11 2013/01/22 19:28:00 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdarg.h>
+
+#include "csh.h"
+#include "extern.h"
+
+#define        INF     1000
+
+struct biltins bfunc[] =
+{
+    { "@",             dolet,          0, INF  },
+    { "alias",                 doalias,        0, INF  },
+    { "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  },
+    { "printf",                doprintf,       1, 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    },
+    { "which",         dowhich,        1, INF  },
+    { "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);
diff --git a/bin/csh/lex.c b/bin/csh/lex.c
new file mode 100644 (file)
index 0000000..8c1bca8
--- /dev/null
@@ -0,0 +1,1631 @@
+/* $NetBSD: lex.c,v 1.31 2013/08/06 05:42:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)lex.c      8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: lex.c,v 1.31 2013/08/06 05:42:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#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(void);
+static int getC1(int);
+static void getdol(void);
+static void getexcl(int);
+static struct Hist *findev(Char *, int);
+static void setexclp(Char *);
+static int bgetc(void);
+static void bfree(void);
+static struct wordent *gethent(int);
+static int matchs(Char *, Char *);
+static int getsel(int *, int *, int);
+static struct wordent *getsub(struct wordent *);
+static Char *subword(Char *, int, int *);
+static struct wordent *dosub(int, struct wordent *, int);
+
+/*
+ * 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 int peekc = 0, peekd = 0;
+static int 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 */
+Char **alvec, *alvecp;
+int aret = F_SEEK;
+
+/*
+ * 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[BUFSIZE];
+
+/*
+ * 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 int hadhist = 0;
+
+/*
+ * Avoid alias expansion recursion via \!#
+ */
+int     hleft;
+
+static int getCtmp;
+
+#define getC(f) ((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f))
+#define        ungetC(c) peekc = c
+#define        ungetD(c) peekd = c
+
+int
+lex(struct wordent *hp)
+{
+    struct wordent *wdp;
+    int c;
+
+    btell(&lineloc);
+    hp->next = hp->prev = hp;
+    hp->word = STRNULL;
+    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 {
+       struct wordent *new;
+
+       new = xmalloc(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(FILE *fp, struct wordent *sp0)
+{
+    struct wordent *sp;
+
+    sp = sp0->next;
+    for (;;) {
+       (void)fprintf(fp, "%s", vis_str(sp->word));
+       sp = sp->next;
+       if (sp == sp0)
+           break;
+       if (sp->word[0] != '\n')
+           (void) fputc(' ', fp);
+    }
+}
+
+#ifdef EDIT
+int
+sprlex(char **s, struct wordent *sp0)
+{
+    struct wordent *sp;
+
+    sp = sp0->next;
+    char *os = *s;
+    for (;;) {
+       char *w = vis_str(sp->word);
+       if (os == NULL) {
+           if (asprintf(s, "%s", w) < 0)
+               return -1;
+           os = *s;
+       } else if (*os != '\n') {
+           if (asprintf(s, "%s %s", os, w) < 0) {
+               free(os);
+               return 1;
+           }
+           free(os);
+           os = *s;
+       }
+       sp = sp->next;
+       if (sp == sp0)
+           break;
+    }
+    return 0;
+}
+#endif
+
+void
+copylex(struct wordent *hp, struct wordent *fp)
+{
+    struct wordent *wdp;
+
+    wdp = hp;
+    fp = fp->next;
+    do {
+       struct wordent *new;
+
+       new = xmalloc(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(struct wordent *vp)
+{
+    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(void)
+{
+    Char wbuf[BUFSIZE], *wp;
+    int i, c, c1;
+    int dolflg;
+
+    wp = wbuf;
+    i = BUFSIZE - 4;
+loop:
+    while ((c = getC(DOALL)) == ' ' || c == '\t')
+       continue;
+    if (cmap(c, _META | _ESC))
+       switch (c) {
+       case '&':
+       case '|':
+       case '<':
+       case '>':
+           *wp++ = (Char)c;
+           c1 = getC(DOALL);
+           if (c1 == c)
+               *wp++ = (Char)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;
+           /* FALLTHROUGH */
+
+       case ';':
+       case '(':
+       case ')':
+       case '\n':
+           *wp++ = (Char)c;
+           goto ret;
+
+       case '\\':
+           c = getC(0);
+           if (c == '\n') {
+               if (onelflg == 1)
+                   onelflg = 2;
+               goto loop;
+           }
+           if (c != HIST)
+               *wp++ = '\\', --i;
+           c |= QUOTE;
+           break;
+       }
+    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 | _QF | _QB | _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, _QF | _QB)) {      /* '"` */
+               c1 = c;
+               dolflg = c == '"' ? DOALL : DOEXCL;
+           }
+           else if (c != '#' || !intty) {
+               ungetC(c);
+               break;
+           }
+       }
+       if (--i > 0) {
+           *wp++ = (Char)c;
+           c = getC(dolflg);
+       }
+       else {
+           seterror(ERR_WTOOLONG);
+           wp = &wbuf[1];
+           break;
+       }
+    }
+ret:
+    *wp = 0;
+    return (Strsave(wbuf));
+}
+
+static int
+getC1(int flag)
+{
+    int c;
+
+    for (;;) {
+       if ((c = peekc) != '\0') {
+           peekc = 0;
+           return (c);
+       }
+       if (lap) {
+           if ((c = *lap++) == 0)
+               lap = 0;
+           else {
+               if (cmap(c, _META | _QF | _QB))
+                   c |= QUOTE;
+               return (c);
+           }
+       }
+       if ((c = peekd) != '\0') {
+           peekd = 0;
+           return (c);
+       }
+       if (exclp) {
+           if ((c = *exclp++) != '\0')
+               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(void)
+{
+    Char name[4*MAXVARLEN+1], *ep, *np;
+    int c, sc;
+    int special, toolong;
+
+    special = 0;
+    np = name, *np++ = '$';
+    c = sc = getC(DOEXCL);
+    if (any("\t \n", c)) {
+       ungetD(c);
+       ungetC('$' | QUOTE);
+       return;
+    }
+    if (c == '{')
+       *np++ = (Char)c, c = getC(DOEXCL);
+    if (c == '#' || c == '?')
+       special++, *np++ = (Char)c, c = getC(DOEXCL);
+    *np++ = (Char)c;
+    switch (c) {
+    case '<':
+    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)) != '\0'){
+               if (!Isdigit(c))
+                   break;
+               if (np < ep)
+                   *np++ = (Char)c;
+               else
+                   toolong = 1;
+           }
+       }
+       else if (letter(c)) {
+           /* we know that np < &name[4] */
+           ep = &np[MAXVARLEN];
+           toolong = 0;
+           while ((c = getC(DOEXCL)) != '\0') {
+               /* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */
+               if (!letter(c) && !Isdigit(c))
+                   break;
+               if (np < ep)
+                   *np++ = (Char)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++ = (Char)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++ = (Char)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 amodflag, gmodflag;
+
+       amodflag = 0;
+       gmodflag = 0;
+       do {
+           *np++ = (Char)c, c = getC(DOEXCL);
+           if (c == 'g' || c == 'a') {
+               if (c == 'g')
+                   gmodflag++;
+               else
+                   amodflag++;
+               *np++ = (Char)c; c = getC(DOEXCL);
+           }
+           if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) {
+               if (c == 'g')
+                   gmodflag++;
+               else
+                   amodflag++;
+               *np++ = (Char)c, c = getC(DOEXCL);
+           }
+           *np++ = (Char)c;
+           /* scan s// [eichin:19910926.0512EST] */
+           if (c == 's') {
+               int delimcnt = 2;
+               int delim = getC(0);
+               *np++ = (Char)delim;
+               
+               if (!delim || letter(delim)
+                   || Isdigit(delim) || any(" \t\n", delim)) {
+                   seterror(ERR_BADSUBST);
+                   break;
+               }       
+               while ((c = getC(0)) != -1) {
+                   *np++ = (Char)c;
+                   if(c == delim) delimcnt--;
+                   if(!delimcnt) break;
+               }
+               if(delimcnt) {
+                   seterror(ERR_BADSUBST);
+                   break;
+               }
+               c = 's';
+           }
+           if (!any("htrqxes", c)) {
+               if ((amodflag || gmodflag) && c == '\n')
+                   stderror(ERR_VARSYN);       /* strike */
+               seterror(ERR_VARMOD, c);
+               *np = 0;
+               addla(name);
+               return;
+           }
+       }
+       while ((c = getC(DOEXCL)) == ':');
+       ungetD(c);
+    }
+    else
+       ungetD(c);
+    if (sc == '{') {
+       c = getC(DOEXCL);
+       if (c != '}') {
+           ungetD(c);
+           seterror(ERR_MISSING, '}');
+           *np = 0;
+           addla(name);
+           return;
+       }
+       *np++ = (Char)c;
+    }
+    *np = 0;
+    addla(name);
+    return;
+}
+
+void
+addla(Char *cp)
+{
+    Char buf[BUFSIZE];
+
+    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(int sc)
+{
+    struct wordent *hp, *ip;
+    int c, dol, left, right;
+
+    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(struct wordent *en)
+{
+    Char orhsb[sizeof(rhsb) / sizeof(Char)];
+    Char *cp;
+    int c, delim, sc;
+    int global;
+
+    do {
+       exclnxt = 0;
+       global = 0;
+       sc = c = getC(0);
+       if (c == 'g' || c == 'a') {
+           global |= (c == 'g') ? 1 : 2;
+           sc = c = getC(0);
+       }
+       if (((c =='g') && !(global & 1)) || ((c == 'a') && !(global & 2))) {
+           global |= (c == 'g') ? 1 : 2;
+           sc = c = getC(0);
+       }
+
+       switch (c) {
+       case 'p':
+           justpr++;
+           return (en);
+       case 'x':
+       case 'q':
+           global |= 1;
+           /* FALLTHROUGH */
+       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++ = (Char)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++ = (Char)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);
+    }
+    while ((c = getC(0)) == ':');
+    unreadc(c);
+    return (en);
+}
+
+static struct wordent *
+dosub(int sc, struct wordent *en, int global)
+{
+    struct wordent lexi, *hp, *wdp;
+    int i;
+    int didone, didsub;
+
+    didone = 0;
+    didsub = 0;
+    i = exclc;
+    hp = &lexi;
+
+    wdp = hp;
+    while (--i >= 0) {
+       struct wordent *new = (struct wordent *)xcalloc(1, sizeof *wdp);
+
+       new->word = 0;
+       new->prev = wdp;
+       new->next = hp;
+       wdp->next = new;
+       wdp = new;
+       en = en->next;
+       if (en->word) {
+           Char *tword, *otword;
+
+           if ((global & 1) || didsub == 0) {
+               tword = subword(en->word, sc, &didone);
+               if (didone)
+                   didsub = 1;
+               if (global & 2) {
+                   while (didone && tword != STRNULL) {
+                       otword = tword;
+                       tword = subword(otword, sc, &didone);
+                       if (Strcmp(tword, otword) == 0) {
+                           xfree((ptr_t) otword);
+                           break;
+                       }
+                       else
+                           xfree((ptr_t)otword);
+                   }
+               }
+           }
+           else
+               tword = Strsave(en->word);
+           wdp->word = tword;
+       }
+    }
+    if (didsub == 0)
+       seterror(ERR_MODFAIL);
+    hp->prev = wdp;
+    return (&enthist(-1000, &lexi, 0)->Hlex);
+}
+
+static Char *
+subword(Char *cp, int type, int *adid)
+{
+    Char wbuf[BUFSIZE];
+    Char *mp, *np, *wp;
+    ssize_t i;
+
+    *adid = 0;
+    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 = BUFSIZE - 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++;
+                       /* FALLTHROUGH */
+                   default:
+                       if (--i < 0) {
+                           seterror(ERR_SUBOVFL);
+                           return (STRNULL);
+                       }
+                       *wp++ = *np;
+                       continue;
+                   case '&':
+                       i -= (ssize_t)Strlen(lhsb);
+                       if (i < 0) {
+                           seterror(ERR_SUBOVFL);
+                           return (STRNULL);
+                       }
+                       *wp = 0;
+                       (void) Strcat(wp, lhsb);
+                       wp = Strend(wp);
+                       continue;
+                   }
+               mp += Strlen(lhsb);
+               i -= (ssize_t)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(Char *cp, int type)
+{
+    Char *wp, *xp;
+    int c;
+
+    switch (type) {
+    case 'x':
+    case 'q':
+       wp = Strsave(cp);
+       for (xp = wp; (c = *xp) != '\0'; 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));
+    default:
+       break;
+    }
+    return (0);
+}
+
+static int
+matchs(Char *str, Char *pat)
+{
+    while (*str && *pat && *str == *pat)
+       str++, pat++;
+    return (*pat == 0);
+}
+
+static int
+getsel(int *al, int *ar, int dol)
+{
+    int c, i;
+    int first;
+
+    c = getC(0);
+    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(int sc)
+{
+    struct Hist *hp;
+    Char *np;
+    char *str;
+    int c, event;
+    int back;
+
+    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);
+           /* FALLTHROUGH */
+       default:
+           if (any("(=~", c)) {
+               unreadc(c);
+               ungetC(HIST);
+               return (0);
+           }
+           np = lhsb;
+           event = 0;
+           while (!cmap(c, _ESC | _META | _QF | _QB) && !any("${}:", c)) {
+               if (event != -1 && Isdigit(c))
+                   event = event * 10 + c - '0';
+               else
+                   event = -1;
+               if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
+                   *np++ = (Char)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++ = (Char)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);
+    str = vis_str(np);
+    xfree((ptr_t) np);
+    seterror(ERR_NOEVENT, str);
+    return (0);
+}
+
+static struct Hist *
+findev(Char *cp, int anyarg)
+{
+    struct Hist *hp;
+
+    for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
+       Char *dp, *p, *q;
+       struct wordent *lp;
+       int argno;
+
+       lp = hp->Hlex.next;
+       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, vis_str(cp));
+    return (0);
+}
+
+
+static void
+setexclp(Char *cp)
+{
+    if (cp && cp[0] == '\n')
+       return;
+    exclp = cp;
+}
+
+void
+unreadc(int c)
+{
+    peekread = c;
+}
+
+int
+readc(int wanteof)
+{
+    static int sincereal;
+    int c;
+
+    aret = F_SEEK;
+    if ((c = peekread) != '\0') {
+       peekread = 0;
+       return (c);
+    }
+top:
+    aret = F_SEEK;
+    if (alvecp) {
+       aret = A_SEEK;
+       if ((c = *alvecp++) != '\0')
+           return (c);
+       if (alvec && *alvec) {
+               alvecp = *alvec++;
+               return (' ');
+       }
+       else {
+           aret = F_SEEK;
+           alvecp = NULL;
+           return('\n');
+       }
+    }
+    if (alvec) {
+       if ((alvecp = *alvec) != '\0') {
+           alvec++;
+           goto top;
+       }
+       /* Infinite source! */
+       return ('\n');
+    }
+    if (evalp) {
+       aret = E_SEEK;
+       if ((c = *evalp++) != '\0')
+           return (c);
+       if (evalvec && *evalvec) {
+           evalp = *evalvec++;
+           return (' ');
+       }
+       aret = F_SEEK;
+       evalp = 0;
+    }
+    if (evalvec) {
+       if (evalvec == (Char **) 1) {
+           doneinp = 1;
+           reset();
+       }
+       if ((evalp = *evalvec) != '\0') {
+           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 */
+               pid_t     ctpgrp;
+
+               if (++sincereal > 25)
+                   goto oops;
+               if (tpgrp != -1 &&
+                   (ctpgrp = tcgetpgrp(FSHTTY)) != -1 &&
+                   tpgrp != ctpgrp) {
+                   (void)tcsetpgrp(FSHTTY, tpgrp);
+                   (void)kill(-ctpgrp, SIGHUP);
+                   (void)fprintf(csherr, "Reset tty pgrp from %ld to %ld\n",
+                                  (long)ctpgrp, (long)tpgrp);
+                   goto reread;
+               }
+               if (adrof(STRignoreeof)) {
+                   if (loginsh)
+                       (void)fprintf(csherr,"\nUse \"logout\" to logout.\n");
+                   else
+                       (void)fprintf(csherr,"\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(void)
+{
+#ifdef FILEC
+    char tbuf[BUFSIZE + 1];
+    Char ttyline[BUFSIZE];
+    int buf, off;
+    ssize_t c, numleft, roomleft;
+
+    numleft = 0; 
+#else /* FILEC */
+    char tbuf[BUFSIZE + 1];
+    int c, buf, off;        
+#endif /* !FILEC */
+
+    if (cantell) {
+       if (fseekp < fbobp || fseekp > feobp) {
+           fbobp = feobp = fseekp;
+           (void)lseek(SHIN, fseekp, SEEK_SET);
+       }
+       if (fseekp == feobp) {
+           int i;
+
+           fbobp = feobp;
+           do
+               c = read(SHIN, tbuf, BUFSIZE);
+           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 (int)(c);
+    }
+
+again:
+    buf = (int) fseekp / BUFSIZE;
+    if (buf >= fblocks) {
+       Char **nfbuf;
+
+       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(BUFSIZE, sizeof(Char));
+       fblocks++;
+       if (!intty)
+           goto again;
+    }
+    if (fseekp >= feobp) {
+       buf = (int) feobp / BUFSIZE;
+       off = (int) feobp % BUFSIZE;
+       roomleft = BUFSIZE - off;
+
+#ifdef FILEC
+       for (;;) {
+           if ((editing || filec) && intty) {
+#ifdef EDIT
+               if (editing) {
+                       const char *p;
+                       int d;
+                       if ((p = el_gets(el, &d)) != NULL) {
+                               size_t i;
+                               /* XXX: Truncation */
+                               numleft = d > BUFSIZE ? BUFSIZE : d;
+                               for (i = 0; *p && i < BUFSIZE; i++, p++)
+                                       ttyline[i] = *p;
+                               ttyline[i - (i == BUFSIZE)] = '\0';
+                       }
+               }
+#endif
+               c = numleft ? numleft : tenex(ttyline, BUFSIZE);
+               if (c > roomleft) {
+                   /* start with fresh buffer */
+                   feobp = fseekp = fblocks * BUFSIZE;
+                   numleft = c;
+                   goto again;
+               }
+               if (c > 0)
+                   (void)memcpy(fbuf[buf] + off, ttyline,
+                       (size_t)c * sizeof(**fbuf));
+               numleft = 0;
+           }
+           else {
+#endif
+               c = read(SHIN, tbuf, (size_t)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     iooff = 0;
+
+               (void)ioctl(SHIN, FIONBIO, (ioctl_t) & iooff);
+           }
+           else if (errno != EINTR)
+               break;
+#ifdef FILEC
+       }
+#endif
+       if (c <= 0)
+           return (-1);
+       feobp += c;
+#ifndef FILEC
+       goto again;
+#else
+       if (filec && !intty)
+           goto again;
+#endif
+    }
+    c = fbuf[buf][(int)fseekp % BUFSIZE];
+    fseekp++;
+    return (int)(c);
+}
+
+static void
+bfree(void)
+{
+    int i, sb;
+
+    if (cantell)
+       return;
+    if (whyles)
+       return;
+    sb = (int)(fseekp - 1) / BUFSIZE;
+    if (sb > 0) {
+       for (i = 0; i < sb; i++)
+           xfree((ptr_t) fbuf[i]);
+       (void)blkcpy(fbuf, &fbuf[sb]);
+       fseekp -= BUFSIZE * sb;
+       feobp -= BUFSIZE * sb;
+       fblocks -= sb;
+    }
+}
+
+void
+bseek(struct Ain *l)
+{
+    switch (aret = l->type) {
+    case A_SEEK:
+       alvec = l->a_seek;
+       alvecp = l->c_seek;
+       return;
+    case E_SEEK:
+       evalvec = l->a_seek;
+       evalp = l->c_seek;
+       return;
+    case F_SEEK:
+       fseekp = l->f_seek;
+       return;
+    default:
+       (void)fprintf(csherr, "Bad seek type %d\n", aret);
+       abort();
+    }
+}
+
+void
+btell(struct Ain *l)
+{
+    switch (l->type = aret) {
+    case A_SEEK:
+       l->a_seek = alvec;
+       l->c_seek = alvecp;
+       return;
+    case E_SEEK:
+       l->a_seek = evalvec;
+       l->c_seek = evalp;
+       return;
+    case F_SEEK:
+       l->f_seek = fseekp;
+       l->a_seek = NULL;
+       return;
+    default:
+       (void)fprintf(csherr, "Bad seek type %d\n", aret);
+       abort();
+    }
+}
+
+void
+btoeof(void)
+{
+    (void)lseek(SHIN, (off_t) 0, SEEK_END);
+    aret = F_SEEK;
+    fseekp = feobp;
+    alvec = NULL;
+    alvecp = NULL;
+    evalvec = NULL;
+    evalp = NULL;
+    wfree();
+    bfree();
+}
+
+void
+settell(void)
+{
+    cantell = 0;
+    if (arginp || onelflg || intty)
+       return;
+    if (lseek(SHIN, (off_t) 0, SEEK_CUR) < 0 || errno == ESPIPE)
+       return;
+    fbuf = (Char **)xcalloc(2, sizeof(Char **));
+    fblocks = 1;
+    fbuf[0] = (Char *)xcalloc(BUFSIZE, sizeof(Char));
+    fseekp = fbobp = feobp = lseek(SHIN, (off_t) 0, SEEK_CUR);
+    cantell = 1;
+}
diff --git a/bin/csh/misc.c b/bin/csh/misc.c
new file mode 100644 (file)
index 0000000..88d2f8e
--- /dev/null
@@ -0,0 +1,407 @@
+/* $NetBSD: misc.c,v 1.20 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)misc.c     8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: misc.c,v 1.20 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "csh.h"
+#include "extern.h"
+
+static int renum(int, int);
+
+int
+any(const char *s, int c)
+{
+    if (!s)
+       return (0);             /* Check for nil pointer */
+    while (*s)
+       if (*s++ == c)
+           return (1);
+    return (0);
+}
+
+char *
+strsave(const char *s)
+{
+    const char *n;
+    char *p, *r;
+
+    if (s == NULL)
+       s = "";
+    for (n = s; *n++;)
+       continue;
+    r = p = xmalloc((size_t)(n - s) * sizeof(*p));
+    while ((*p++ = *s++) != '\0')
+       continue;
+    return (r);
+}
+
+Char **
+blkend(Char **up)
+{
+    while (*up)
+       up++;
+    return (up);
+}
+
+
+void
+blkpr(FILE *fp, Char **av)
+{
+    for (; *av; av++) {
+       (void)fprintf(fp, "%s", vis_str(*av));
+       if (av[1])
+           (void)fprintf(fp, " ");
+    }
+}
+
+int
+blklen(Char **av)
+{
+    int i;
+
+    i = 0;
+    while (*av++)
+       i++;
+    return (i);
+}
+
+Char **
+blkcpy(Char **oav, Char **bv)
+{
+    Char **av;
+
+    av = oav;
+    while ((*av++ = *bv++) != NULL)
+       continue;
+    return (oav);
+}
+
+Char **
+blkcat(Char **up, Char **vp)
+{
+    (void)blkcpy(blkend(up), vp);
+    return (up);
+}
+
+void
+blkfree(Char **av0)
+{
+    Char **av;
+
+    av = av0;
+    if (!av0)
+       return;
+    for (; *av; av++)
+       xfree((ptr_t) * av);
+    xfree((ptr_t) av0);
+}
+
+Char **
+saveblk(Char **v)
+{
+    Char **newv, **onewv;
+
+    if (v == NULL)
+       return NULL;
+
+    newv = xcalloc((size_t)(blklen(v) + 1), sizeof(*newv));
+    onewv = newv;
+    while (*v)
+       *newv++ = Strsave(*v++);
+    return (onewv);
+}
+
+#ifdef NOTUSED
+char *
+strstr(char *s, char *t)
+{
+    do {
+       char *ss;
+       char *tt;
+
+       ss = s;
+       tt = t;
+
+       do
+           if (*tt == '\0')
+               return (s);
+       while (*ss++ == *tt++);
+    } while (*s++ != '\0');
+    return (NULL);
+}
+
+#endif /* NOTUSED */
+
+#ifndef SHORT_STRINGS
+char *
+strspl(char *cp, char *dp)
+{
+    char *ep, *p, *q;
+
+    if (!cp)
+       cp = "";
+    if (!dp)
+       dp = "";
+    for (p = cp; *p++;)
+       continue;
+    for (q = dp; *q++;)
+       continue;
+    ep = xmalloc((size_t)(((p - cp) + (q - dp) - 1) * sizeof(*ep)));
+    for (p = ep, q = cp; *p++ = *q++;)
+       continue;
+    for (p--, q = dp; *p++ = *q++;)
+       continue;
+    return (ep);
+}
+
+#endif
+
+Char **
+blkspl(Char **up, Char **vp)
+{
+    Char **wp;
+
+    wp = xcalloc((size_t)(blklen(up) + blklen(vp) + 1), sizeof(*wp));
+    (void)blkcpy(wp, up);
+    return (blkcat(wp, vp));
+}
+
+Char
+lastchr(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(void)
+{
+    int f;
+    int nofile;
+
+#ifdef F_CLOSEM
+    nofile = FOLDSTD + 1;
+    if (fcntl(nofile, F_CLOSEM, 0) == -1)
+#endif
+       nofile = NOFILE;
+
+    for (f = 0; f < nofile; f++)
+       if (f != SHIN && f != SHOUT && f != SHERR && f != OLDSTD &&
+           f != FSHTTY)
+           (void) close(f);
+}
+
+void
+donefds(void)
+{
+    (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(int i, int 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(int i, int j)
+{
+    if (i == j || i < 0 || (j < 0 && i > 2))
+       return (i);
+    if (j >= 0) {
+       (void)dup2(i, j);
+       return (j);
+    }
+    return (renum(i, j));
+}
+
+static int
+renum(int i, int j)
+{
+    int k;
+
+    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(Char **v, size_t c)
+{
+    Char **u;
+
+    for (u = v; *u && c-- > 0; u++)
+       xfree((ptr_t) *u);
+    (void)blkcpy(v, u);
+}
+
+int
+number(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(Char **v)
+{
+    Char **nv;
+
+    nv = xcalloc((size_t)(blklen(v) + 1), sizeof(*nv));
+
+    return (blkcpy(nv, v));
+}
+
+#ifndef SHORT_STRINGS
+char *
+strend(char *cp)
+{
+    if (!cp)
+       return (cp);
+    while (*cp)
+       cp++;
+    return (cp);
+}
+
+#endif /* SHORT_STRINGS */
+
+Char *
+strip(Char *cp)
+{
+    Char *dp;
+
+    dp = cp;
+    if (!cp)
+       return (cp);
+    while ((*dp++ &= TRIM) != '\0')
+       continue;
+    return (cp);
+}
+
+Char *
+quote(Char *cp)
+{
+    Char *dp;
+
+    dp = cp;
+    if (!cp)
+       return (cp);
+    while (*dp != '\0')
+       *dp++ |= QUOTE;
+    return (cp);
+}
+
+void
+udvar(Char *name)
+{
+    setname(vis_str(name));
+    stderror(ERR_NAME | ERR_UNDVAR);
+    /* NOTREACHED */
+}
+
+int
+prefix(Char *sub, Char *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 (file)
index 0000000..25bc438
--- /dev/null
@@ -0,0 +1,640 @@
+/* $NetBSD: parse.c,v 1.17 2007/07/16 18:26:10 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)parse.c    8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: parse.c,v 1.17 2007/07/16 18:26:10 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "csh.h"
+#include "extern.h"
+
+static void asyntax(struct wordent *, struct wordent *);
+static void asyn0(struct wordent *, struct wordent *);
+static void asyn3(struct wordent *, struct wordent *);
+static struct wordent *freenod(struct wordent *, struct wordent *);
+static struct command *syn0(struct wordent *, struct wordent *, int);
+static struct command *syn1(struct wordent *, struct wordent *, int);
+static struct command *syn1a(struct wordent *, struct wordent *, int);
+static struct command *syn1b(struct wordent *, struct wordent *, int);
+static struct command *syn2(struct wordent *, struct wordent *, int);
+static struct command *syn3(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(struct wordent *lexp)
+{
+    jmp_buf osetexit;
+
+    aleft = ALEFT;
+    hleft = HLEFT;
+    getexit(osetexit);
+    (void)setexit();
+    if (haderr) {
+       resexit(osetexit);
+       reset();
+    }
+    if (--aleft == 0)
+       stderror(ERR_ALIASLOOP);
+    asyntax(lexp->next, lexp);
+    resexit(osetexit);
+}
+
+static void
+asyntax(struct wordent *p1, struct wordent *p2)
+{
+    while (p1 != p2)
+       if (any(";&\n", p1->word[0]))
+           p1 = p1->next;
+       else {
+           asyn0(p1, p2);
+           return;
+       }
+}
+
+static void
+asyn0(struct wordent *p1, struct wordent *p2)
+{
+    struct wordent *p;
+    int l;
+
+    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(struct wordent *p1, struct wordent *p2)
+{
+    struct varent *ap;
+    struct wordent alout;
+    int 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;
+
+       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(struct wordent *p1, struct wordent *p2)
+{
+    struct wordent *retp;
+
+    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        PERR    8
+
+/*
+ * syntax
+ *     empty
+ *     syn0
+ */
+struct command *
+syntax(struct wordent *p1, struct wordent *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(struct wordent *p1, struct wordent *p2, int flags)
+{
+    struct wordent *p;
+    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;
+           /* FALLTHROUGH */
+       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(struct wordent *p1, struct wordent *p2, int flags)
+{
+    struct wordent *p;
+    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(struct wordent *p1, struct wordent *p2, int flags)
+{
+    struct wordent *p;
+    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 '|':
+           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(struct wordent *p1, struct wordent *p2, int flags)
+{
+    struct wordent *p;
+    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 '&':
+           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(struct wordent *p1, struct wordent *p2, int flags)
+{
+    struct wordent *p, *pn;
+    struct command *t;
+    int f, l;
+
+    l = 0;
+    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 |= PERR;
+               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(struct wordent *p1, struct wordent *p2, int flags)
+{
+    struct wordent *lp, *p, *rp;
+    struct command *t;
+    Char **av;
+    int c, l, n;
+    int specp;
+
+    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 | PERR)) {
+                   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 & PERR) == 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;
+           /* FALLTHROUGH */
+       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(struct command *t)
+{
+    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 (file)
index 0000000..db2e373
--- /dev/null
@@ -0,0 +1,44 @@
+/* $NetBSD: pathnames.h,v 1.8 2003/08/07 09:05:06 agc Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ *     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. 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 8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _PATHNAMES_H_
+#define _PATHNAMES_H_
+
+#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"
+
+#endif /* !_PATHNAMES_H_ */
diff --git a/bin/csh/proc.c b/bin/csh/proc.c
new file mode 100644 (file)
index 0000000..5c53d98
--- /dev/null
@@ -0,0 +1,1359 @@
+/* $NetBSD: proc.c,v 1.36 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)proc.c     8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: proc.c,v 1.36 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "csh.h"
+#include "dir.h"
+#include "extern.h"
+#include "proc.h"
+
+#define BIGINDEX 9 /* largest desirable job index */
+
+extern int insource;
+
+static void pflushall(void);
+static void pflush(struct process *);
+static void pclrcurr(struct process *);
+static void padd(struct command *);
+static int pprint(struct process *, int);
+static void ptprint(struct process *);
+static void pads(Char *);
+static void pkill(Char **v, int);
+static struct process *pgetcurr(struct process *);
+static void okpcntl(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!
+ */
+/* ARGSUSED */
+void
+pchild(int notused)
+{
+    struct rusage ru;
+    struct process *fp, *pp;
+    int jobflags, pid, w;
+
+loop:
+    errno = 0;                 /* reset, just in case */
+    pid = wait3(&w,
+       (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 = WSTOPSIG(w);
+    }
+    else {
+       if (pp->p_flags & (PTIME | PPTIME) || adrof(STRtime))
+           (void)clock_gettime(CLOCK_MONOTONIC, &pp->p_etime);
+
+       pp->p_rusage = ru;
+       if (WIFSIGNALED(w)) {
+           if (WTERMSIG(w) == SIGINT)
+               pp->p_flags |= PINTERRUPTED;
+           else
+               pp->p_flags |= PSIGNALED;
+           if (WCOREDUMP(w))
+               pp->p_flags |= PDUMPED;
+           pp->p_reason = WTERMSIG(w);
+       }
+       else {
+           pp->p_reason = WEXITSTATUS(w);
+           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) {
+               (void)vis_fputc('\r' | QUOTE, cshout);
+               (void)fputc('\n', cshout);
+           }
+       }
+       else {
+           if (jobflags & PNOTIFY || adrof(STRnotify)) {
+               (void)vis_fputc('\r' | QUOTE, cshout);
+               (void)fputc('\n', cshout);
+               (void)pprint(pp, NUMBER | NAME | REASON);
+               if ((jobflags & PSTOPPED) == 0)
+                   pflush(pp);
+           }
+           else {
+               fp->p_flags |= PNEEDNOTE;
+               neednote++;
+           }
+       }
+    }
+    goto loop;
+}
+
+void
+pnote(void)
+{
+    struct process *pp;
+    sigset_t osigset, nsigset;
+    int flags;
+
+    neednote = 0;
+    sigemptyset(&nsigset);
+    (void)sigaddset(&nsigset, SIGCHLD);
+    for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) {
+       if (pp->p_flags & PNEEDNOTE) {
+           (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+           pp->p_flags &= ~PNEEDNOTE;
+           flags = pprint(pp, NUMBER | NAME | REASON);
+           if ((flags & (PRUNNING | PSTOPPED)) == 0)
+               pflush(pp);
+           (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+       }
+    }
+}
+
+/*
+ * pwait - wait for current job to terminate, maintaining integrity
+ *     of current and previous job indicators.
+ */
+void
+pwait(void)
+{
+    struct process *fp, *pp;
+    sigset_t osigset, nsigset;
+
+    /*
+     * Here's where dead procs get flushed.
+     */
+    sigemptyset(&nsigset);
+    (void)sigaddset(&nsigset, SIGCHLD);
+    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+    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)sigprocmask(SIG_SETMASK, &osigset, NULL);
+    pjwait(pcurrjob);
+}
+
+
+/*
+ * pjwait - wait for a job to finish or become stopped
+ *     It is assumed to be in the foreground state (PFOREGND)
+ */
+void
+pjwait(struct process *pp)
+{
+    struct process *fp;
+    sigset_t osigset, nsigset;
+    int jobflags, reason;
+
+    while (pp->p_pid != pp->p_jobid)
+       pp = pp->p_friends;
+    fp = pp;
+
+    do {
+       if ((fp->p_flags & (PFOREGND | PRUNNING)) == PRUNNING)
+           (void)fprintf(csherr, "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;
+    sigemptyset(&nsigset);
+    (void)sigaddset(&nsigset, SIGCHLD);
+    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+    for (;;) {
+       sigemptyset(&nsigset);
+       (void)sigaddset(&nsigset, SIGCHLD);
+       (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
+       jobflags = 0;
+       do
+           jobflags |= fp->p_flags;
+       while ((fp = (fp->p_friends)) != pp);
+       if ((jobflags & PRUNNING) == 0)
+           break;
+#ifdef JOBDEBUG
+       (void)fprintf(csherr, "starting to sigsuspend for  SIGCHLD on %d\n",
+                      fp->p_pid);
+#endif                         /* JOBDEBUG */
+       nsigset = osigset;
+       (void)sigdelset(&nsigset, SIGCHLD);
+       (void)sigsuspend(&nsigset);
+    }
+    (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+    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) {
+           (void) fputc('\n', cshout);
+           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, NULL);
+               (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);
+    }
+    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))) {
+       (void)fprintf(cshout, "Exit %d\n", reason);
+    }
+    set(STRstatus, putn(reason));
+    if (reason && exiterr)
+       exitstat();
+    pflush(pp);
+}
+
+/*
+ * dowait - wait for all processes to finish
+ */
+void
+/*ARGSUSED*/
+dowait(Char **v, struct command *t)
+{
+    struct process *pp;
+    sigset_t osigset, nsigset;
+
+    pjobs++;
+    sigemptyset(&nsigset);
+    (void)sigaddset(&nsigset, SIGCHLD);
+    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+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) {
+           sigemptyset(&nsigset);
+           (void)sigsuspend(&nsigset);
+           goto loop;
+       }
+    (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+    pjobs = 0;
+}
+
+/*
+ * pflushall - flush all jobs from list (e.g. at fork())
+ */
+static void
+pflushall(void)
+{
+    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(struct process *pp)
+{
+    struct process *np;
+    int idx;
+
+    if (pp->p_pid == 0) {
+       (void)fprintf(csherr, "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(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 size_t 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(int pid, struct command *t)
+{
+    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 |= PERR;
+    }
+    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)
+           continue;
+       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)clock_gettime(CLOCK_MONOTONIC, &pp->p_btime);
+}
+
+static void
+padd(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(Char *cp)
+{
+    size_t 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(void)
+{
+    pholdjob = pcurrjob;
+    pcurrjob = NULL;
+}
+
+/*
+ * prestjob - opposite of psavejob.  This may be missed if we are interrupted
+ *     somewhere, but pendjob cleans up anyway.
+ */
+void
+prestjob(void)
+{
+    pcurrjob = pholdjob;
+    pholdjob = NULL;
+}
+
+/*
+ * pendjob - indicate that a job (set of commands) has been completed
+ *     or is about to begin.
+ */
+void
+pendjob(void)
+{
+    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;
+       (void)fprintf(cshout, "[%d]", pp->p_index);
+       tp = pp;
+       do {
+           (void)fprintf(cshout, " %ld", (long)pp->p_pid);
+           pp = pp->p_friends;
+       } while (pp != tp);
+       (void)fputc('\n', cshout);
+    }
+    pholdjob = pcurrjob = 0;
+}
+
+/*
+ * pprint - print a job
+ */
+static int
+pprint(struct process *pp, int flag)
+{
+    static struct rusage zru;
+    struct process *tp;
+    const char *format;
+    int jobflags, pstatus, reason, status;
+    int hadnl;
+
+    hadnl = 1; /* did we just have a newline */
+    (void)fpurge(cshout);
+
+    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 && !hadnl && !(flag & FANCY) &&
+           ((pstatus == status && pp->p_reason == reason) ||
+            !(flag & REASON))) {
+           (void)fputc(' ', cshout);
+           hadnl = 0;
+       }
+       else {
+           if (tp != pp && !hadnl) {
+               (void)fputc('\n', cshout);
+               hadnl = 1;
+           }
+           if (flag & NUMBER) {
+               if (pp == tp)
+                   (void)fprintf(cshout, "[%d]%s %c ", pp->p_index,
+                           pp->p_index < 10 ? " " : "",
+                           pp == pcurrent ? '+' :
+                           (pp == pprevious ? '-' : ' '));
+               else
+                   (void)fprintf(cshout, "       ");
+               hadnl = 0;
+           }
+           if (flag & FANCY) {
+               (void)fprintf(cshout, "%5ld ", (long)pp->p_pid);
+               hadnl = 0;
+           }
+           if (flag & (REASON | AREASON)) {
+               if (flag & NAME)
+                   format = "%-23s";
+               else
+                   format = "%s";
+               if (pstatus == status) {
+                   if (pp->p_reason == reason) {
+                       (void)fprintf(cshout, format, "");
+                       hadnl = 0;
+                       goto prcomd;
+                   }
+                   else
+                       reason = pp->p_reason;
+               } else {
+                   status = pstatus;
+                   reason = pp->p_reason;
+               }
+               switch (status) {
+               case PRUNNING:
+                   (void)fprintf(cshout, format, "Running ");
+                   hadnl = 0;
+                   break;
+               case PINTERRUPTED:
+               case PSTOPPED:
+               case PSIGNALED:
+                    /*
+                     * tell what happened to the background job
+                     * From: Michael Schroeder
+                     * <mlschroe@immd4.informatik.uni-erlangen.de>
+                     */
+                    if ((flag & REASON)
+                        || ((flag & AREASON)
+                            && reason != SIGINT
+                            && (reason != SIGPIPE
+                                || (pp->p_flags & PPOU) == 0))) {
+                       (void)fprintf(cshout, format,
+                                      sys_siglist[(unsigned char)
+                                                  pp->p_reason]);
+                       hadnl = 0;
+                   }
+                   break;
+               case PNEXITED:
+               case PAEXITED:
+                   if (flag & REASON) {
+                       if (pp->p_reason)
+                           (void)fprintf(cshout, "Exit %-18d", pp->p_reason);
+                       else
+                           (void)fprintf(cshout, format, "Done");
+                       hadnl = 0;
+                   }
+                   break;
+               default:
+                   (void)fprintf(csherr, "BUG: status=%-9o", status);
+               }
+           }
+       }
+prcomd:
+       if (flag & NAME) {
+           (void)fprintf(cshout, "%s", vis_str(pp->p_command));
+           if (pp->p_flags & PPOU)
+               (void)fprintf(cshout, " |");
+           if (pp->p_flags & PERR)
+               (void)fputc('&', cshout);
+           hadnl = 0;
+       }
+       if (flag & (REASON | AREASON) && pp->p_flags & PDUMPED) {
+           (void)fprintf(cshout, " (core dumped)");
+           hadnl = 0;
+       }
+       if (tp == pp->p_friends) {
+           if (flag & AMPERSAND) {
+               (void)fprintf(cshout, " &");
+               hadnl = 0;
+           }
+           if (flag & JOBDIR &&
+               !eq(tp->p_cwd->di_name, dcwd->di_name)) {
+               (void)fprintf(cshout, " (wd: ");
+               dtildepr(value(STRhome), tp->p_cwd->di_name);
+               (void)fputc(')', cshout);
+               hadnl = 0;
+           }
+       }
+       if (pp->p_flags & PPTIME && !(status & (PSTOPPED | PRUNNING))) {
+           if (!hadnl)
+               (void)fprintf(cshout, "\n\t");
+           prusage(cshout, &zru, &pp->p_rusage, &pp->p_etime,
+                   &pp->p_btime);
+           hadnl = 1;
+       }
+       if (tp == pp->p_friends) {
+           if (!hadnl) {
+               (void)fputc('\n', cshout);
+               hadnl = 1;
+           }
+           if (flag & SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) {
+               (void)fprintf(cshout, "(wd now: ");
+               dtildepr(value(STRhome), dcwd->di_name);
+               (void)fprintf(cshout, ")\n");
+               hadnl = 1;
+           }
+       }
+    } while ((pp = pp->p_friends) != tp);
+    if (jobflags & PTIME && (jobflags & (PSTOPPED | PRUNNING)) == 0) {
+       if (jobflags & NUMBER)
+           (void)fprintf(cshout, "       ");
+       ptprint(tp);
+       hadnl = 1;
+    }
+    (void)fflush(cshout);
+    return (jobflags);
+}
+
+static void
+ptprint(struct process *tp)
+{
+    static struct rusage zru;
+    static struct timespec ztime;
+    struct rusage ru;
+    struct timespec tetime, diff;
+    struct process *pp;
+
+    pp = tp;
+    ru = zru;
+    tetime = ztime;
+    do {
+       ruadd(&ru, &pp->p_rusage);
+       timespecsub(&pp->p_etime, &pp->p_btime, &diff);
+       if (timespeccmp(&diff, &tetime, >))
+           tetime = diff;
+    } while ((pp = pp->p_friends) != tp);
+    prusage(cshout, &zru, &ru, &tetime, &ztime);
+}
+
+/*
+ * dojobs - print all jobs
+ */
+void
+/*ARGSUSED*/
+dojobs(Char **v, struct command *t)
+{
+    struct process *pp;
+    int flag, i;
+
+    flag = NUMBER | NAME | REASON;
+    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
+/*ARGSUSED*/
+dofg(Char **v, struct command *t)
+{
+    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
+/*ARGSUSED*/
+dofg1(Char **v, struct command *t)
+{
+    struct process *pp;
+
+    okpcntl();
+    pp = pfind(v[0]);
+    pstart(pp, 1);
+    pjwait(pp);
+}
+
+/*
+ * dobg - builtin - put the job into the background
+ */
+void
+/*ARGSUSED*/
+dobg(Char **v, struct command *t)
+{
+    struct process *pp;
+
+    okpcntl();
+    ++v;
+    do {
+       pp = pfind(*v);
+       pstart(pp, 0);
+    } while (*v && *++v);
+}
+
+/*
+ * %... & - builtin - put the job into the background
+ */
+void
+/*ARGSUSED*/
+dobg1(Char **v, struct command *t)
+{
+    struct process *pp;
+
+    pp = pfind(v[0]);
+    pstart(pp, 0);
+}
+
+/*
+ * dostop - builtin - stop the job
+ */
+void
+/*ARGSUSED*/
+dostop(Char **v, struct command *t)
+{
+    pkill(++v, SIGSTOP);
+}
+
+/*
+ * dokill - builtin - superset of kill (1)
+ */
+void
+/*ARGSUSED*/
+dokill(Char **v, struct command *t)
+{
+    Char *signame;
+    char *name;
+    long signum;
+    char *ep;
+
+    signum = SIGTERM;
+    v++;
+    if (v[0] && v[0][0] == '-') {
+       if (v[0][1] == 'l') {
+           if (v[1]) {
+               if (!Isdigit(v[1][0]))
+                   stderror(ERR_NAME | ERR_BADSIG);
+
+               signum = strtol(short2str(v[1]), &ep, 10);
+               if (signum < 0 || signum >= NSIG)
+                   stderror(ERR_NAME | ERR_BADSIG);
+               else if (signum == 0)
+                   (void)fputc('0', cshout); /* 0's symbolic name is '0' */
+               else
+                   (void)fprintf(cshout, "%s ", sys_signame[signum]);
+           } else {
+               for (signum = 1; signum < NSIG; signum++) {
+                   (void)fprintf(cshout, "%s ", sys_signame[signum]);
+                   if (signum == NSIG / 2)
+                       (void)fputc('\n', cshout);
+               }
+           }
+           (void)fputc('\n', cshout);
+           return;
+       }
+       if (Isdigit(v[0][1])) {
+           signum = strtol(short2str(v[0] + 1), &ep, 10);
+           if (signum < 0 || signum >= NSIG || *ep)
+               stderror(ERR_NAME | ERR_BADSIG);
+       }
+       else {
+           if (v[0][1] == 's' && v[0][2] == '\0')
+               signame = *(++v);
+           else
+               signame = &v[0][1];
+
+           if (signame == NULL || v[1] == NULL)
+               stderror(ERR_NAME | ERR_TOOFEW);
+
+           name = short2str(signame);
+           for (signum = 1; signum < NSIG; signum++)
+               if (!strcasecmp(sys_signame[signum], name) ||
+                   (!strncasecmp("SIG", name, 3) &&    /* skip "SIG" prefix */
+                    !strcasecmp(sys_signame[signum], name + 3)))
+                   break;
+
+           if (signum == NSIG) {
+               if (signame[0] == '0')
+                   signum = 0;
+               else {
+                   setname(vis_str(signame));
+                   stderror(ERR_NAME | ERR_UNKSIG);
+               }
+           }
+       }
+       v++;
+    }
+    pkill(v, (int)signum);
+}
+
+static void
+pkill(Char **v, int signum)
+{
+    struct process *pp, *np;
+    Char *cp;
+    sigset_t nsigset;
+    int err1, jobflags, pid;
+    char *ep;
+
+    jobflags = 0;
+    err1 = 0;    
+    sigemptyset(&nsigset);
+    (void)sigaddset(&nsigset, SIGCHLD);
+    if (setintr)
+       (void)sigaddset(&nsigset, SIGINT);
+    (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
+    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) {
+                   (void)fprintf(csherr, "%s: Already suspended\n",
+                                  vis_str(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 (kill(-pp->p_jobid, signum) < 0) {
+               (void)fprintf(csherr, "%s: %s\n", vis_str(cp),
+                              strerror(errno));
+               err1++;
+           }
+           if (signum == SIGTERM || signum == SIGHUP)
+               (void)kill(-pp->p_jobid, SIGCONT);
+       }
+       else if (!(Isdigit(*cp) || *cp == '-'))
+           stderror(ERR_NAME | ERR_JOBARGS);
+       else {
+           pid = (pid_t)strtoul(short2str(cp), &ep, 0);
+           if (*ep) {
+               (void)fprintf(csherr, "%s: Badly formed number\n",
+                   short2str(cp));
+               err1++;
+               goto cont;
+           } else if (kill(pid, signum) < 0) {
+               (void)fprintf(csherr, "%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)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
+    if (err1)
+       stderror(ERR_SILENT);
+}
+
+/*
+ * pstart - start the job in foreground/background
+ */
+void
+pstart(struct process *pp, int foregnd)
+{
+    struct process *np;
+    sigset_t osigset, nsigset;
+    long jobflags;
+
+    jobflags = 0;
+    sigemptyset(&nsigset);
+    (void)sigaddset(&nsigset, SIGCHLD);
+    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+    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)kill(-pp->p_jobid, SIGCONT);
+    (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+}
+
+void
+panystop(int neednl)
+{
+    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(Char *cp)
+{
+    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] == '?') {
+               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 */
+}
+
+/*
+ * pgetcurr - find most recent job that is not pp, preferably stopped
+ */
+static struct process *
+pgetcurr(struct process *pp)
+{
+    struct process *np, *xp;
+
+    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
+/*ARGSUSED*/
+donotify(Char **v, struct command *t)
+{
+    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(struct command *t /* command we are forking for */, int wanttty)
+{
+    int pgrp, pid;
+    sigset_t osigset, nsigset;
+    int ignint;
+
+    ignint = 0;
+    /*
+     * 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.
+     */
+    sigemptyset(&nsigset);
+    (void)sigaddset(&nsigset, SIGCHLD);
+    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+    while ((pid = fork()) < 0)
+       if (setintr == 0)
+           (void)sleep(FORKSLEEP);
+       else {
+           (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+           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)sigprocmask(SIG_SETMASK, &osigset, NULL);
+    }
+
+    return (pid);
+}
+
+static void
+okpcntl(void)
+{
+    if (tpgrp == -1)
+       stderror(ERR_JOBCONTROL);
+    if (tpgrp == 0)
+       stderror(ERR_JOBCTRLSUB);
+    /* NOTREACHED */
+}
+
+/*
+ * 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(int wanttty, int pgrp)
+{
+    sigset_t osigset, nsigset;
+
+    /*
+     * christos: I am blocking the tty signals till I've set things
+     * correctly....
+     */
+    if (wanttty > 0) {
+       sigemptyset(&nsigset);
+       (void)sigaddset(&nsigset, SIGTSTP);
+       (void)sigaddset(&nsigset, SIGTTIN);
+       (void)sigaddset(&nsigset, SIGTTOU);
+       (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+    }
+    /*
+     * 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) {
+           (void)fprintf(csherr, "csh: setpgid error.\n");
+           xexit(0);
+       }
+
+    if (wanttty > 0) {
+       (void)tcsetpgrp(FSHTTY, pgrp);
+       (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+    }
+
+    if (tpgrp > 0)
+       tpgrp = 0;              /* gave tty away */
+}
diff --git a/bin/csh/proc.h b/bin/csh/proc.h
new file mode 100644 (file)
index 0000000..783d292
--- /dev/null
@@ -0,0 +1,104 @@
+/* $NetBSD: proc.h,v 1.14 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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      8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _PROC_H_
+#define _PROC_H_
+
+/*
+ * 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) */
+    int p_flags;               /* various job status flags */
+    int p_reason;              /* reason for entering this state */
+    int p_index;               /* shorthand job index */
+    pid_t p_pid;
+    pid_t p_jobid;             /* pid of job leader */
+    /* if a job is stopped/background p_jobid gives its pgrp */
+    struct timespec p_btime;   /* begin time */
+    struct timespec 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        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        PERR            (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        PALLSTATES      (PRUNNING|PSTOPPED|PNEXITED|PAEXITED|PSIGNALED|PINTERRUPTED)
+
+#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 */
+int 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 */
+
+#endif /* !_PROC_H_ */
diff --git a/bin/csh/sem.c b/bin/csh/sem.c
new file mode 100644 (file)
index 0000000..acda703
--- /dev/null
@@ -0,0 +1,646 @@
+/* $NetBSD: sem.c,v 1.29 2011/08/29 14:51:17 joerg Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)sem.c      8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: sem.c,v 1.29 2011/08/29 14:51:17 joerg Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "csh.h"
+#include "extern.h"
+#include "proc.h"
+
+__dead static void vffree(int);
+static Char *splicepipe(struct command *t, Char *);
+static void doio(struct command *t, int *, int *);
+static void chkclob(char *);
+
+void
+execute(struct command *t, int wtty, int *pipein, int *pipeout)
+{
+    static sigset_t csigset, ocsigset; 
+    static int nosigchld = 0, onosigchld = 0;
+    volatile int wanttty = wtty;
+    struct biltins * volatile bifunc;
+    int pv[2], pid;
+    sigset_t nsigset;
+    int forked;
+
+    UNREGISTER(forked);
+    UNREGISTER(bifunc);
+    UNREGISTER(wanttty);
+
+    forked = 0;
+    pid = 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;
+       /* FALLTHROUGH */
+    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);
+       }
+
+       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 it a command */
+       if (t->t_dtyp == NODE_COMMAND) {
+           /*
+            * Check if we have a builtin function and remember which one.
+            */
+           bifunc = isbfunc(t);
+           if (noexec && bifunc != NULL) {
+               /*
+                * Continue for builtins that are part of the scripting language
+                */
+               if (bifunc->bfunct != dobreak   && bifunc->bfunct != docontin &&
+                   bifunc->bfunct != doelse    && bifunc->bfunct != doend    &&
+                   bifunc->bfunct != doforeach && bifunc->bfunct != dogoto   &&
+                   bifunc->bfunct != doif      && bifunc->bfunct != dorepeat &&
+                   bifunc->bfunct != doswbrk   && bifunc->bfunct != doswitch &&
+                   bifunc->bfunct != dowhile   && bifunc->bfunct != dozip)
+                   break;
+           }
+       }
+       else {                  /* not a command */
+           bifunc = NULL;
+           if (noexec)
+               break;
+       }
+
+       /*
+        * 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 | F_PIPEOUT)) != 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) {
+                   sigemptyset(&nsigset);
+                   (void)sigaddset(&nsigset, SIGCHLD);
+                   (void)sigprocmask(SIG_BLOCK, &nsigset, &csigset);
+                   nosigchld = 1;
+               }
+
+               pid = pfork(t, wanttty);
+               if (pid == 0 && nosigchld) {
+                   (void)sigprocmask(SIG_SETMASK, &csigset, NULL);
+                   nosigchld = 0;
+               }
+               else if (pid != 0 && (t->t_dflg & F_AMPERSAND))
+                   backpid = pid;
+
+           }
+           else {
+               int ochild, osetintr, ohaderr, odidfds;
+               int oSHIN, oSHOUT, oSHERR, oOLDSTD, otpgrp;
+               sigset_t osigset;
+
+               /*
+                * 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 sigaction's for the signals the child touches
+                * before it exec's.
+                */
+               if (wanttty >= 0 && !nosigchld && !noexec) {
+                   sigemptyset(&nsigset);
+                   (void)sigaddset(&nsigset, SIGCHLD);
+                   (void)sigprocmask(SIG_BLOCK, &nsigset, &csigset);
+                   nosigchld = 1;
+               }
+               sigemptyset(&nsigset);
+               (void)sigaddset(&nsigset, SIGCHLD);
+               (void)sigaddset(&nsigset, SIGINT);
+               (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+               ochild = child;
+               osetintr = setintr;
+               ohaderr = haderr;
+               odidfds = didfds;
+               oSHIN = SHIN;
+               oSHOUT = SHOUT;
+               oSHERR = SHERR;
+               oOLDSTD = OLDSTD;
+               otpgrp = tpgrp;
+               ocsigset = csigset;
+               onosigchld = nosigchld;
+               Vsav = Vdp = 0;
+               Vexpath = 0;
+               Vt = 0;
+               pid = vfork();
+
+               if (pid < 0) {
+                   (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
+                   stderror(ERR_NOPROC);
+               }
+               forked++;
+               if (pid) {      /* parent */
+                   child = ochild;
+                   setintr = osetintr;
+                   haderr = ohaderr;
+                   didfds = odidfds;
+                   SHIN = oSHIN;
+                   SHOUT = oSHOUT;
+                   SHERR = oSHERR;
+                   OLDSTD = oOLDSTD;
+                   tpgrp = otpgrp;
+                   csigset = ocsigset;
+                   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)sigprocmask(SIG_SETMASK, &osigset, NULL);
+               }
+               else {          /* child */
+                   /* this is from pfork() */
+                   int pgrp;
+                   int ignint = 0;
+
+                   if (nosigchld) {
+                       (void)sigprocmask(SIG_SETMASK, &csigset, NULL);
+                       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)sigprocmask(SIG_SETMASK, &csigset, NULL);
+                   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(NULL, t);
+       /*
+        * For () commands must put new 0,1,2 in FSH* and recurse
+        */
+       (void) ioctl(OLDSTD = dcopy(0, FOLDSTD), FIOCLEX, NULL);
+       (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL);
+       (void) ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL);
+       (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();
+       /* NOTREACHED */
+    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(int i)
+{
+    Char **v;
+
+    if ((v = gargv) != NULL) {
+       gargv = 0;
+       xfree((ptr_t) v);
+    }
+    if ((v = pargv) != NULL) {
+       pargv = 0;
+       xfree((ptr_t) v);
+    }
+    _exit(i);
+    /* NOTREACHED */
+}
+
+/*
+ * Expand and glob the words after an i/o redirection.
+ * If more than one word is generated, then update the command vector.
+ *
+ * This is done differently in all the shells:
+ * 1. in the bourne shell and ksh globbing is not performed
+ * 2. Bash/csh say ambiguous
+ * 3. zsh does i/o to/from all the files
+ * 4. itcsh concatenates the words.
+ *
+ * I don't know what is best to do. I think that Ambiguous is better
+ * than restructuring the command vector, because the user can get
+ * unexpected results. In any case, the command vector restructuring 
+ * code is present and the user can choose it by setting noambiguous
+ */
+static Char *
+splicepipe(struct command *t, Char *cp /* word after < or > */)
+{
+    Char *blk[2];
+
+    if (adrof(STRnoambiguous)) {
+       Char **pv;
+
+       blk[0] = Dfix1(cp); /* expand $ */
+       blk[1] = NULL;
+
+       gflag = 0, tglob(blk);
+       if (gflag) {
+           pv = globall(blk);
+           if (pv == NULL) {
+               setname(vis_str(blk[0]));
+               xfree((ptr_t) blk[0]);
+               stderror(ERR_NAME | ERR_NOMATCH);
+               /* NOTREACHED */
+           }
+           gargv = NULL;
+           if (pv[1] != NULL) { /* we need to fix the command vector */
+               Char **av = blkspl(t->t_dcom, &pv[1]);
+               xfree((ptr_t) t->t_dcom);
+               t->t_dcom = av;
+           }
+           xfree((ptr_t) blk[0]);
+           blk[0] = pv[0];
+           xfree((ptr_t) pv);
+       }
+    }
+    else {
+       blk[0] = globone(blk[1] = Dfix1(cp), G_ERROR);
+       xfree((ptr_t) blk[1]);
+    }
+    return(blk[0]);
+}
+
+/*
+ * Perform io redirection.
+ * We may or maynot be forked here.
+ */
+static void
+doio(struct command *t, int *pipein, int *pipeout)
+{
+    Char *cp;
+    int fd, flags;
+
+    flags = t->t_dflg;
+    if (didfds || (flags & F_REPEAT))
+       return;
+    if ((flags & F_READ) == 0) {/* F_READ already done */
+       if (t->t_dlef) {
+           char tmp[MAXPATHLEN+1];
+
+           /*
+            * so < /dev/std{in,out,err} work
+            */
+           (void)dcopy(SHIN, 0);
+           (void)dcopy(SHOUT, 1);
+           (void)dcopy(SHERR, 2);
+           cp = splicepipe(t, t->t_dlef);
+           (void)strlcpy(tmp, short2str(cp), sizeof(tmp));
+           xfree((ptr_t) cp);
+           if ((fd = open(tmp, O_RDONLY)) < 0) {
+               stderror(ERR_SYSTEM, tmp, strerror(errno));
+               /* NOTREACHED */
+           }
+           (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 (t->t_drit) {
+       char    tmp[MAXPATHLEN+1];
+
+       cp = splicepipe(t, t->t_drit);
+       (void)strlcpy(tmp, short2str(cp), sizeof(tmp));
+       xfree((ptr_t) cp);
+       /*
+        * so > /dev/std{out,err} work
+        */
+       (void)dcopy(SHOUT, 1);
+       (void)dcopy(SHERR, 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, SEEK_END);
+#endif
+       else {
+           if (!(flags & F_OVERWRITE) && adrof(STRnoclobber)) {
+               if (flags & F_APPEND) {
+                   stderror(ERR_SYSTEM, tmp, strerror(errno));
+                   /* NOTREACHED */
+               }
+               chkclob(tmp);
+           }
+           if ((fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
+               stderror(ERR_SYSTEM, tmp, strerror(errno));
+               /* NOTREACHED */
+           }
+       }
+       (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(SHERR);
+       (void)ioctl(2, FIONCLEX, NULL);
+    }
+    didfds = 1;
+}
+
+void
+mypipe(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);
+    /* NOTREACHED */
+}
+
+static void
+chkclob(char *cp)
+{
+    struct stat stb;
+
+    if (stat(cp, &stb) < 0)
+       return;
+    if (S_ISCHR(stb.st_mode))
+       return;
+    stderror(ERR_EXISTS, cp);
+    /* NOTREACHED */
+}
diff --git a/bin/csh/set.c b/bin/csh/set.c
new file mode 100644 (file)
index 0000000..9b73abd
--- /dev/null
@@ -0,0 +1,820 @@
+/* $NetBSD: set.c,v 1.33 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)set.c      8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: set.c,v 1.33 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#ifndef SHORT_STRINGS
+#include <string.h>
+#endif /* SHORT_STRINGS */
+
+#include "csh.h"
+#include "extern.h"
+
+static Char *getinx(Char *, int *);
+static void asx(Char *, int, Char *);
+static struct varent *getvx(Char *, int);
+static Char *xset(Char *, Char ***);
+static Char *operate(int, Char *, Char *);
+static void putn1(int);
+static struct varent *madrof(Char *, struct varent *);
+static void unsetv1(struct varent *);
+static void exportpath(Char **);
+static void balance(struct varent *, int, int);
+
+/*
+ * C Shell
+ */
+
+static void
+update_vars(Char *vp)
+{
+    if (eq(vp, STRpath)) {
+       struct varent *pt = adrof(STRpath); 
+       if (pt == NULL)
+           stderror(ERR_NAME | ERR_UNDVAR);
+       else {
+           exportpath(pt->vec);
+           dohash(NULL, NULL);
+       }
+    }
+    else if (eq(vp, STRhistchars)) {
+       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)) {
+       Char *cp;
+
+       cp = Strsave(value(vp));        /* get the old value back */
+
+       /*
+        * convert to canonical 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
+#ifdef EDIT
+    else if (eq(vp, STRedit)) {
+       HistEvent ev;
+       editing = 1;
+       el = el_init_fd(getprogname(), cshin, cshout, csherr,
+           SHIN, SHOUT, SHERR);
+       el_set(el, EL_EDITOR, "emacs");
+       el_set(el, EL_PROMPT, printpromptstr);
+       hi = history_init();
+       history(hi, &ev, H_SETSIZE, getn(value(STRhistory)));
+       loadhist(Histlist.Hnext);
+       el_set(el, EL_HIST, history, hi);
+    }
+#endif
+}
+
+void
+/*ARGSUSED*/
+doset(Char **v, struct command *t)
+{
+    Char op, *p, **vecp, *vp;
+    int subscr = 0;    /* XXX: GCC */
+    int hadsub;
+
+    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 ((op = *p) != '\0') {
+           *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)) {
+           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));
+       update_vars(vp);
+    } while ((p = *v++) != NULL);
+}
+
+static Char *
+getinx(Char *cp, 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(Char *vp, int subscr, Char *p)
+{
+    struct varent *v;
+
+    v = getvx(vp, subscr);
+    xfree((ptr_t) v->vec[subscr - 1]);
+    v->vec[subscr - 1] = globone(p, G_APPEND);
+}
+
+static struct varent *
+getvx(Char *vp, int subscr)
+{
+    struct varent *v;
+
+    v = adrof(vp);
+    if (v == 0)
+       udvar(vp);
+    if (subscr < 1 || subscr > blklen(v->vec))
+       stderror(ERR_NAME | ERR_RANGE);
+    return (v);
+}
+
+void
+/*ARGSUSED*/
+dolet(Char **v, struct command *t)
+{
+    Char c, op, *p, *vp;
+    int subscr = 0;    /* XXX: GCC */
+    int hadsub;
+
+    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) != '\0')
+           *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)) {
+           struct varent *pt = adrof(STRpath); 
+           if (pt == NULL)
+               stderror(ERR_NAME | ERR_UNDVAR);
+           else {
+               exportpath(pt->vec);
+               dohash(NULL, NULL);
+           }
+       }
+       xfree((ptr_t) vp);
+       if (c != '=')
+           xfree((ptr_t) p);
+    } while ((p = *v++) != NULL);
+}
+
+static Char *
+xset(Char *cp, Char ***vp)
+{
+    Char *dp;
+
+    if (*cp) {
+       dp = Strsave(cp);
+       --(*vp);
+       xfree((ptr_t) ** vp);
+       **vp = dp;
+    }
+    return (putn(expr(vp)));
+}
+
+static Char *
+operate(int op, Char *vp, Char *p)
+{
+    Char opr[2], **v, *vec[5], **vecp;
+    int i;
+
+    v = vec;
+    vecp = v;
+    if (op != '=') {
+       if (*vp)
+           *v++ = vp;
+       opr[0] = (Char)op;
+       opr[1] = 0;
+       *v++ = opr;
+       if (op == '<' || op == '>')
+           *v++ = opr;
+    }
+    *v++ = p;
+    *v++ = 0;
+    i = expr(&vecp);
+    if (*vecp)
+       stderror(ERR_NAME | ERR_EXPRESSION);
+    return (putn(i));
+}
+
+static Char *putp;
+
+Char *
+putn(int n)
+{
+    static Char numbers[15];
+
+    putp = numbers;
+    if (n < 0) {
+       n = -n;
+       *putp++ = '-';
+    }
+    if ((unsigned int)n == 0x80000000U) {
+       *putp++ = '2';
+       n = 147483648;
+    }
+    putn1(n);
+    *putp = 0;
+    return (Strsave(numbers));
+}
+
+static void
+putn1(int n)
+{
+    if (n > 9)
+       putn1(n / 10);
+    *putp++ = (Char)(n % 10 + '0');
+}
+
+int
+getn(Char *cp)
+{
+    int n, 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(Char *var, struct varent *head)
+{
+    struct varent *vp;
+
+    vp = adrof1(var, head);
+    return (vp == 0 || vp->vec[0] == 0 ? STRNULL : vp->vec[0]);
+}
+
+static struct varent *
+madrof(Char *pat, struct varent *vp)
+{
+    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(Char *name, struct varent *v)
+{
+    int 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(Char *var, Char *val)
+{
+    Char **vec;
+
+    vec = xmalloc(2 * sizeof(*vec));
+    vec[0] = val;
+    vec[1] = 0;
+    set1(var, vec, &shvhed);
+}
+
+void
+set1(Char *var, Char **vec, struct varent *head)
+{
+    Char **oldv;
+
+    oldv = vec;
+    gflag = 0;
+    tglob(oldv);
+    if (gflag) {
+       vec = globall(oldv);
+       if (vec == 0) {
+           blkfree(oldv);
+           stderror(ERR_NAME | ERR_NOMATCH);
+       }
+       blkfree(oldv);
+       gargv = 0;
+    }
+    setq(var, vec, head);
+}
+
+void
+setq(Char *name, Char **vec, struct varent *p)
+{
+    struct varent *c;
+    int f;
+
+    f = 0;                     /* tree hangs off the header's left link */
+    while ((c = p->v_link[f]) != NULL) {
+       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 = xmalloc(sizeof(*c));
+    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
+/*ARGSUSED*/
+unset(Char **v, struct command *t)
+{
+    unset1(v, &shvhed);
+    if (adrof(STRhistchars) == 0) {
+       HIST = '!';
+       HISTSUB = '^';
+    }
+    else if (adrof(STRwordchars) == 0)
+       word_chars = STR_WORD_CHARS;
+#ifdef FILEC
+    else if (adrof(STRfilec) == 0)
+       filec = 0;
+#endif
+#ifdef EDIT
+    else if (adrof(STRedit) == 0) {
+       el_end(el);
+       history_end(hi);
+       el = NULL;
+       hi = NULL;
+       editing = 0;
+    }
+#endif
+}
+
+void
+unset1(Char *v[], struct varent *head)
+{
+    struct varent *vp;
+    int cnt;
+
+    while (*++v) {
+       cnt = 0;
+       while ((vp = madrof(*v, head->v_left)) != NULL)
+           unsetv1(vp), cnt++;
+       if (cnt == 0)
+           setname(vis_str(*v));
+    }
+}
+
+void
+unsetv(Char *var)
+{
+    struct varent *vp;
+
+    if ((vp = adrof1(var, &shvhed)) == 0)
+       udvar(var);
+    unsetv1(vp);
+}
+
+static void
+unsetv1(struct varent *p)
+{
+    struct varent *c, *pp;
+    int 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 its 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)
+           continue;
+       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) != NULL)
+       c->v_parent = pp;
+    /*
+     * Free the deleted node, and rebalance.
+     */
+    xfree((ptr_t) p);
+    balance(pp, f, 1);
+}
+
+void
+setNS(Char *cp)
+{
+    set(cp, Strsave(STRNULL));
+}
+
+void
+/*ARGSUSED*/
+shift(Char **v, struct command *t)
+{
+    struct varent *argv;
+    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);
+    update_vars(name);
+}
+
+static void
+exportpath(Char **val)
+{
+    Char exppath[BUFSIZE];
+
+    exppath[0] = 0;
+    if (val)
+       while (*val) {
+           if (Strlen(*val) + Strlen(exppath) + 2 > BUFSIZE) {
+               (void)fprintf(csherr,
+                              "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(struct varent *p)
+{
+    return (p);
+}
+struct varent *
+rright(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(struct varent *p, int f, int d)
+{
+    struct varent *pp;
+
+#ifndef lint
+    struct varent *t;  /* used by the rotate macros */
+
+#endif
+    int ff;
+
+    /*
+     * Ok, from here on, p is the node we're operating on; pp is its 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) != NULL; 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: /* single 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: /* single 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(struct varent *p)
+{
+    struct varent *c;
+    sigset_t nsigset;
+    int len;
+
+    if (setintr) {
+       sigemptyset(&nsigset);
+       (void)sigaddset(&nsigset, SIGINT);
+       (void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
+    }
+
+    for (;;) {
+       while (p->v_left)
+           p = p->v_left;
+x:
+       if (p->v_parent == 0)   /* is it the header? */
+           return;
+       len = blklen(p->vec);
+       (void)fprintf(cshout, "%s\t", short2str(p->v_name));
+       if (len != 1)
+           (void)fputc('(', cshout);
+       blkpr(cshout, p->vec);
+       if (len != 1)
+           (void)fputc(')', cshout);
+       (void)fputc('\n', cshout);
+       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 (file)
index 0000000..e828cc4
--- /dev/null
@@ -0,0 +1,441 @@
+/* $NetBSD: str.c,v 1.15 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)str.c      8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: str.c,v 1.15 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#define MALLOC_INCR 128
+
+/*
+ * tc.str.c: Short string package
+ *          This has been a lesson of how to write buggy code!
+ */
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <vis.h>
+
+#include "csh.h"
+#include "extern.h"
+
+#ifdef SHORT_STRINGS
+
+Char **
+blk2short(char **src)
+{
+    Char **dst, **sdst;
+    size_t n;
+
+    /*
+     * Count
+     */
+    for (n = 0; src[n] != NULL; n++)
+       continue;
+    sdst = dst = xmalloc((size_t)((n + 1) * sizeof(*dst)));
+
+    for (; *src != NULL; src++)
+       *dst++ = SAVE(*src);
+    *dst = NULL;
+    return (sdst);
+}
+
+char **
+short2blk(Char *const *src)
+{
+    char **dst, **sdst;
+    size_t n;
+
+    /*
+     * Count
+     */
+    for (n = 0; src[n] != NULL; n++)
+       continue;
+    sdst = dst = xmalloc((size_t)((n + 1) * sizeof(*dst)));
+
+    for (; *src != NULL; src++)
+       *dst++ = strsave(short2str(*src));
+    *dst = NULL;
+    return (sdst);
+}
+
+Char *
+str2short(const char *src)
+{
+    static Char *sdst;
+    Char *dst, *edst;
+    static size_t dstsize = 0;
+
+    if (src == NULL)
+       return (NULL);
+
+    if (sdst == (NULL)) {
+       dstsize = MALLOC_INCR;
+       sdst = xmalloc((size_t)dstsize * sizeof(*sdst));
+    }
+
+    dst = sdst;
+    edst = &dst[dstsize];
+    while (*src) {
+       *dst++ = (Char) ((unsigned char) *src++);
+       if (dst == edst) {
+           dstsize += MALLOC_INCR;
+           sdst = xrealloc((ptr_t)sdst,
+               (size_t)dstsize * sizeof(*sdst));
+           edst = &sdst[dstsize];
+           dst = &edst[-MALLOC_INCR];
+       }
+    }
+    *dst = 0;
+    return (sdst);
+}
+
+char *
+short2str(const Char *src)
+{
+    static char *sdst = NULL;
+    static size_t dstsize = 0;
+    char *dst, *edst;
+
+    if (src == NULL)
+       return (NULL);
+
+    if (sdst == NULL) {
+       dstsize = MALLOC_INCR;
+       sdst = xmalloc((size_t)dstsize * sizeof(*sdst));
+    }
+    dst = sdst;
+    edst = &dst[dstsize];
+    while (*src) {
+       *dst++ = (char) *src++;
+       if (dst == edst) {
+           dstsize += MALLOC_INCR;
+           sdst = xrealloc((ptr_t)sdst,
+               (size_t)dstsize * sizeof(*sdst));
+           edst = &sdst[dstsize];
+           dst = &edst[-MALLOC_INCR];
+       }
+    }
+    *dst = 0;
+    return (sdst);
+}
+
+Char *
+s_strcpy(Char *dst, const Char *src)
+{
+    Char *sdst;
+
+    sdst = dst;
+    while ((*dst++ = *src++) != '\0')
+       continue;
+    return (sdst);
+}
+
+Char *
+s_strncpy(Char *dst, const Char *src, size_t n)
+{
+    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(Char *dst, const Char *src)
+{
+    short *sdst;
+
+    sdst = dst;
+    while (*dst++)
+       continue;
+    --dst;
+    while ((*dst++ = *src++) != '\0')
+       continue;
+    return (sdst);
+}
+
+#ifdef NOTUSED
+Char *
+s_strncat(Char *dst, Char *src, size_t n)
+{
+    Char *sdst;
+
+    if (n == 0)
+       return (dst);
+
+    sdst = dst;
+
+    while (*dst++)
+       continue;
+    --dst;
+
+    do
+       if ((*dst++ = *src++) == '\0')
+           return(sdst);
+    while (--n != 0)
+       continue;
+
+    *dst = '\0';
+    return (sdst);
+}
+
+#endif
+
+Char *
+s_strchr(const Char *str, int ch)
+{
+    do
+       if (*str == ch)
+           return __UNCONST(str);
+    while (*str++);
+    return (NULL);
+}
+
+Char *
+s_strrchr(const Char *str, int ch)
+{
+    const Char *rstr;
+
+    rstr = NULL;
+    do
+       if (*str == ch)
+           rstr = str;
+    while (*str++);
+    return __UNCONST(rstr);
+}
+
+size_t
+s_strlen(const Char *str)
+{
+    size_t n;
+
+    for (n = 0; *str++; n++)
+       continue;
+    return (n);
+}
+
+int
+s_strcmp(const Char *str1, const Char *str2)
+{
+    for (; *str1 && *str1 == *str2; str1++, str2++)
+       continue;
+    /*
+     * 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(const Char *str1, const Char *str2, size_t n)
+{
+    if (n == 0)
+       return (0);
+    do {
+       if (*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')
+               return (-1);
+           else if (*str2 == '\0')
+               return (1);
+           else
+               return (*str1 - *str2);
+       }
+        if (*str1 == '\0')
+           return(0);
+       str1++, str2++;
+    } while (--n != 0);
+    return(0);
+}
+
+Char *
+s_strsave(const Char *s)
+{
+    const Char *p;
+    Char *n;
+
+    if (s == 0)
+       s = STRNULL;
+    for (p = s; *p++;)
+       continue;
+    p = n = xmalloc((size_t)(p - s) * sizeof(*n));
+    while ((*n++ = *s++) != '\0')
+       continue;
+    return __UNCONST(p);
+}
+
+Char *
+s_strspl(const Char *cp, const Char *dp)
+{
+    Char *ep, *d;
+    const Char *p, *q;
+
+    if (!cp)
+       cp = STRNULL;
+    if (!dp)
+       dp = STRNULL;
+    for (p = cp; *p++;)
+       continue;
+    for (q = dp; *q++;)
+       continue;
+    ep = xmalloc((size_t)((p - cp) + (q - dp) - 1) * sizeof(*ep));
+    for (d = ep, q = cp; (*d++ = *q++) != '\0';)
+       continue;
+    for (d--, q = dp; (*d++ = *q++) != '\0';)
+       continue;
+    return (ep);
+}
+
+Char *
+s_strend(const Char *cp)
+{
+    if (!cp)
+       return __UNCONST(cp);
+    while (*cp)
+       cp++;
+    return __UNCONST(cp);
+}
+
+Char *
+s_strstr(const Char *s, const Char *t)
+{
+    do {
+       const Char *ss = s;
+       const Char *tt = t;
+
+       do
+           if (*tt == '\0')
+               return __UNCONST(s);
+       while (*ss++ == *tt++);
+    } while (*s++ != '\0');
+    return (NULL);
+}
+#endif                         /* SHORT_STRINGS */
+
+char *
+short2qstr(const Char *src)
+{
+    static char *sdst = NULL;
+    static size_t dstsize = 0;
+    char *dst, *edst;
+
+    if (src == NULL)
+       return (NULL);
+
+    if (sdst == NULL) {
+       dstsize = MALLOC_INCR;
+       sdst = xmalloc((size_t)dstsize * sizeof(*sdst));
+    }
+    dst = sdst;
+    edst = &dst[dstsize];
+    while (*src) {
+
+       if (*src & QUOTE) {
+           *dst++ = '\\';
+           if (dst == edst) {
+               dstsize += MALLOC_INCR;
+               sdst = xrealloc((ptr_t) sdst, 
+                   (size_t)dstsize * sizeof(*sdst));
+               edst = &sdst[dstsize];
+               dst = &edst[-MALLOC_INCR];
+           }
+       }
+       *dst++ = (char) *src++;
+       if (dst == edst) {
+           dstsize += MALLOC_INCR;
+           sdst = xrealloc((ptr_t) sdst,
+               (size_t)dstsize * sizeof(*sdst));
+           edst = &sdst[dstsize];
+           dst = &edst[-MALLOC_INCR];
+       }
+    }
+    *dst = 0;
+    return (sdst);
+}
+
+/*
+ * XXX: Should we worry about QUOTE'd chars?
+ */
+char *
+vis_str(const Char *cp)
+{
+    static char *sdst = NULL;
+    static size_t dstsize = 0;
+    const Char *dp;
+    size_t n;
+
+    if (cp == NULL)
+       return (NULL);
+    
+    for (dp = cp; *dp++;)
+       continue;
+    n = ((size_t)(dp - cp) << 2) + 1; /* 4 times + NULL */
+    if (dstsize < n) {
+       sdst = (dstsize ? 
+           xrealloc(sdst, (size_t)n * sizeof(*sdst)) :
+           xmalloc((size_t)n * sizeof(*sdst)));
+       dstsize = n;
+    }
+    /* 
+     * XXX: When we are in AsciiOnly we want all characters >= 0200 to
+     * be encoded, but currently there is no way in vis to do that.
+     */
+    (void)strvis(sdst, short2str(cp), VIS_NOSLASH);
+    return (sdst);
+}
diff --git a/bin/csh/time.c b/bin/csh/time.c
new file mode 100644 (file)
index 0000000..bd8c9ad
--- /dev/null
@@ -0,0 +1,285 @@
+/* $NetBSD: time.c,v 1.20 2013/07/16 17:47:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ *     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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)time.c     8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: time.c,v 1.20 2013/07/16 17:47:43 christos Exp $");
+#endif
+#endif /* not lint */
+
+#ifndef NOT_CSH
+#include <sys/types.h>
+#include <stdarg.h>
+#include "csh.h"
+#include "extern.h"
+#endif
+#include <util.h>
+
+/*
+ * C Shell - routines handling process timing and niceing
+ */
+static void pdeltat(FILE *, struct timeval *, struct timeval *);
+static void pcsecs(FILE *, long);
+
+#ifndef NOT_CSH
+void
+settimes(void)
+{
+    struct rusage ruch;
+
+    (void)clock_gettime(CLOCK_MONOTONIC, &time0);
+    (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
+/*ARGSUSED*/
+dotime(Char **v, struct command *t)
+{
+    struct rusage ru1, ruch;
+    struct timespec timedol;
+
+    (void)getrusage(RUSAGE_SELF, &ru1);
+    (void)getrusage(RUSAGE_CHILDREN, &ruch);
+    ruadd(&ru1, &ruch);
+    (void)clock_gettime(CLOCK_MONOTONIC, &timedol);
+    prusage(cshout, &ru0, &ru1, &timedol, &time0);
+}
+
+/*
+ * donice is only called when it on the line by itself or with a +- value
+ */
+void
+/*ARGSUSED*/
+donice(Char **v, struct command *t)
+{
+    Char *cp;
+    int nval;
+
+    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(struct rusage *ru, struct rusage *ru2)
+{
+    timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime);
+    timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->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;
+}
+#endif /* NOT_CSH */
+
+void
+prusage(FILE *fp, struct rusage *r0, struct rusage *r1, struct timespec *e,
+        struct timespec *b)
+{
+#ifndef NOT_CSH
+    struct varent *vp;
+#endif
+    const char *cp;
+    long i;
+    time_t t;
+    time_t ms;
+
+    cp = "%Uu %Ss %E %P %X+%Dk %I+%Oio %Fpf+%Ww";
+    ms = (e->tv_sec - b->tv_sec) * 100 + (e->tv_nsec - b->tv_nsec) / 10000000;
+    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;
+#ifndef NOT_CSH
+    vp = adrof(STRtime);
+
+    if (vp && vp->vec[0] && vp->vec[1])
+       cp = short2str(vp->vec[1]);
+#endif
+
+    for (; *cp; cp++)
+       if (*cp != '%')
+           (void) fputc(*cp, fp);
+       else if (cp[1])
+           switch (*++cp) {
+           case 'D':           /* (average) unshared data size */
+               (void)fprintf(fp, "%ld", t == 0 ? 0L :
+                       (long)((r1->ru_idrss + r1->ru_isrss -
+                        (r0->ru_idrss + r0->ru_isrss)) / t));
+               break;
+           case 'E':           /* elapsed (wall-clock) time */
+               pcsecs(fp, (long) ms);
+               break;
+           case 'F':           /* page faults */
+               (void)fprintf(fp, "%ld", r1->ru_majflt - r0->ru_majflt);
+               break;
+           case 'I':           /* FS blocks in */
+               (void)fprintf(fp, "%ld", r1->ru_inblock - r0->ru_inblock);
+               break;
+           case 'K':           /* (average) total data memory used  */
+               (void)fprintf(fp, "%ld", t == 0 ? 0L :
+                       (long)(((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 */
+               (void)fprintf(fp, "%ld", r1->ru_maxrss / 2L);
+               break;
+           case 'O':           /* FS blocks out */
+               (void)fprintf(fp, "%ld", r1->ru_oublock - r0->ru_oublock);
+               break;
+           case 'P':           /* percent time spent running */
+               /* check if it did not run at all */
+               if (ms == 0) {
+                       (void)fputs("0.0%", fp);
+               } else {
+                       char pb[32];
+                       (void)fputs(strpct(pb, sizeof(pb),
+                           (uintmax_t)t, (uintmax_t)ms, 1), fp);
+                       (void)fputc('%', fp);
+               }
+               break;
+           case 'R':           /* page reclaims */
+               (void)fprintf(fp, "%ld", r1->ru_minflt - r0->ru_minflt);
+               break;
+           case 'S':           /* system CPU time used */
+               pdeltat(fp, &r1->ru_stime, &r0->ru_stime);
+               break;
+           case 'U':           /* user CPU time used */
+               pdeltat(fp, &r1->ru_utime, &r0->ru_utime);
+               break;
+           case 'W':           /* number of swaps */
+               i = r1->ru_nswap - r0->ru_nswap;
+               (void)fprintf(fp, "%ld", i);
+               break;
+           case 'X':           /* (average) shared text size */
+               (void)fprintf(fp, "%ld", t == 0 ? 0L : 
+                              (long)((r1->ru_ixrss - r0->ru_ixrss) / t));
+               break;
+           case 'c':           /* num. involuntary context switches */
+               (void)fprintf(fp, "%ld", r1->ru_nivcsw - r0->ru_nivcsw);
+               break;
+           case 'k':           /* number of signals received */
+               (void)fprintf(fp, "%ld", r1->ru_nsignals-r0->ru_nsignals);
+               break;
+           case 'r':           /* socket messages received */
+               (void)fprintf(fp, "%ld", r1->ru_msgrcv - r0->ru_msgrcv);
+               break;
+           case 's':           /* socket messages sent */
+               (void)fprintf(fp, "%ld", r1->ru_msgsnd - r0->ru_msgsnd);
+               break;
+           case 'w':           /* num. voluntary context switches (waits) */
+               (void)fprintf(fp, "%ld", r1->ru_nvcsw - r0->ru_nvcsw);
+               break;
+           }
+    (void)fputc('\n', fp);
+}
+
+static void
+pdeltat(FILE *fp, struct timeval *t1, struct timeval *t0)
+{
+    struct timeval td;
+
+    timersub(t1, t0, &td);
+    (void)fprintf(fp, "%ld.%01ld", (long)td.tv_sec,
+       (long)(td.tv_usec / 100000));
+}
+
+#define  P2DIG(fp, i) (void)fprintf(fp, "%ld%ld", (i) / 10, (i) % 10)
+
+#ifndef NOT_CSH
+void
+psecs(long l)
+{
+    long i;
+
+    i = l / 3600;
+    if (i) {
+       (void)fprintf(cshout, "%ld:", i);
+       i = l % 3600;
+       P2DIG(cshout, i / 60);
+       goto minsec;
+    }
+    i = l;
+    (void)fprintf(cshout, "%ld", i / 60);
+minsec:
+    i %= 60;
+    (void)fputc(':', cshout);
+    P2DIG(cshout, i);
+}
+#endif
+
+static void
+pcsecs(FILE *fp, long l)       /* PWP: print mm:ss.dd, l is in sec*100 */
+{
+    long i;
+
+    i = l / 360000;
+    if (i) {
+       (void)fprintf(fp, "%ld:", i);
+       i = (l % 360000) / 100;
+       P2DIG(fp, i / 60);
+       goto minsec;
+    }
+    i = l / 100;
+    (void)fprintf(fp, "%ld", i / 60);
+minsec:
+    i %= 60;
+    (void)fputc(':', fp);
+    P2DIG(fp, i);
+    (void)fputc('.', fp);
+    P2DIG(fp, (l % 100));
+}
index 6ba620154df84efdd24cbfa5d9c520cad44ab530..1c74b3f64bb82116b830d32080f030e6dc058398 100644 (file)
@@ -16,6 +16,7 @@
 ./bin/command                          minix-sys       obsolete
 ./bin/cp                               minix-sys
 ./bin/cpio                             minix-sys
+./bin/csh                              minix-sys
 ./bin/date                             minix-sys
 ./bin/dd                               minix-sys
 ./bin/df                               minix-sys
 ./usr/man/man1/[.1                     minix-sys
 ./usr/man/man1/..1                     minix-sys       obsolete
 ./usr/man/man1/addr2line.1             minix-sys       binutils
+./usr/man/man1/alias.1                 minix-sys
 ./usr/man/man1/apropos.1               minix-sys
 ./usr/man/man1/ar.1                    minix-sys       binutils
 ./usr/man/man1/as.1                    minix-sys       binutils
 ./usr/man/man1/banner.1                        minix-sys
 ./usr/man/man1/basename.1              minix-sys
 ./usr/man/man1/bdes.1                  minix-sys
+./usr/man/man1/bg.1                    minix-sys
 ./usr/man/man1/break.1                 minix-sys       obsolete
 ./usr/man/man1/bsdtar.1                        minix-sys
 ./usr/man/man1/bsfilt.1                        minix-sys
 ./usr/man/man1/cpp.1                   minix-sys       gcccmds
 ./usr/man/man1/crc.1                   minix-sys
 ./usr/man/man1/crontab.1               minix-sys
+./usr/man/man1/csh.1                   minix-sys
 ./usr/man/man1/csplit.1                        minix-sys
 ./usr/man/man1/ctags.1                 minix-sys
 ./usr/man/man1/cut.1                   minix-sys
 ./usr/man/man1/dhrystone.1             minix-sys
 ./usr/man/man1/diff.1                  minix-sys
 ./usr/man/man1/dirname.1               minix-sys
+./usr/man/man1/dirs.1                  minix-sys
 ./usr/man/man1/domainname.1            minix-sys
 ./usr/man/man1/dosdir.1                        minix-sys
 ./usr/man/man1/dosread.1               minix-sys
 ./usr/man/man1/expr.1                  minix-sys
 ./usr/man/man1/false.1                 minix-sys
 ./usr/man/man1/fetch.1                 minix-sys
+./usr/man/man1/fg.1                    minix-sys
 ./usr/man/man1/fgrep.1                 minix-sys
 ./usr/man/man1/file.1                  minix-sys
 ./usr/man/man1/find.1                  minix-sys
 ./usr/man/man1/flock.1                 minix-sys
 ./usr/man/man1/fold.1                  minix-sys
 ./usr/man/man1/for.1                   minix-sys       obsolete
+./usr/man/man1/foreach.1               minix-sys
 ./usr/man/man1/format.1                        minix-sys
 ./usr/man/man1/fpr.1                   minix-sys
 ./usr/man/man1/from.1                  minix-sys
 ./usr/man/man1/hash.1                  minix-sys       obsolete
 ./usr/man/man1/head.1                  minix-sys
 ./usr/man/man1/hexdump.1               minix-sys
+./usr/man/man1/history.1               minix-sys
 ./usr/man/man1/host.1                  minix-sys
 ./usr/man/man1/hostaddr.1              minix-sys
 ./usr/man/man1/hostname.1              minix-sys
 ./usr/man/man1/isodir.1                        minix-sys
 ./usr/man/man1/isoinfo.1               minix-sys
 ./usr/man/man1/isoread.1               minix-sys
-./usr/man/man1/jobs.1                  minix-sys       obsolete
+./usr/man/man1/jobs.1                  minix-sys
 ./usr/man/man1/join.1                  minix-sys
 ./usr/man/man1/jot.1                   minix-sys
 ./usr/man/man1/kill.1                  minix-sys
 ./usr/man/man1/lessecho.1              minix-sys
 ./usr/man/man1/lesskey.1               minix-sys
 ./usr/man/man1/lex.1                   minix-sys
+./usr/man/man1/limit.1                 minix-sys
 ./usr/man/man1/linkfarm.1              minix-sys       obsolete
 ./usr/man/man1/ln.1                    minix-sys
 ./usr/man/man1/loadfont.1              minix-sys
 ./usr/man/man1/ping.1                   minix-sys      obsolete
 ./usr/man/man1/pkg_view.1              minix-sys       obsolete
 ./usr/man/man1/playwave.1              minix-sys
+./usr/man/man1/popd.1                  minix-sys
 ./usr/man/man1/pr.1                    minix-sys
 ./usr/man/man1/prep.1                  minix-sys
 ./usr/man/man1/printenv.1              minix-sys
 ./usr/man/man1/printf.1                        minix-sys
 ./usr/man/man1/profile.1               minix-sys
 ./usr/man/man1/ps.1                    minix-sys
+./usr/man/man1/pushd.1                 minix-sys
 ./usr/man/man1/pwd.1                   minix-sys
 ./usr/man/man1/pwhash.1                        minix-sys
 ./usr/man/man1/ranlib.1                        minix-sys       binutils
 ./usr/man/man1/readlink.1              minix-sys
 ./usr/man/man1/readonly.1              minix-sys       obsolete
 ./usr/man/man1/recwave.1               minix-sys
+./usr/man/man1/rehash.1                        minix-sys
 ./usr/man/man1/remsync.1               minix-sys
+./usr/man/man1/repeat.1                        minix-sys
 ./usr/man/man1/return.1                        minix-sys       obsolete
 ./usr/man/man1/rev.1                   minix-sys
 ./usr/man/man1/rget.1                  minix-sys
 ./usr/man/man1/sleep.1                 minix-sys
 ./usr/man/man1/soelim.1                        minix-sys
 ./usr/man/man1/sort.1                  minix-sys
+./usr/man/man1/source.1                        minix-sys
 ./usr/man/man1/spell.1                 minix-sys
 ./usr/man/man1/split.1                 minix-sys
 ./usr/man/man1/sqlite3.1               minix-sys
 ./usr/man/man1/stat.1                  minix-sys
+./usr/man/man1/stop.1                  minix-sys
 ./usr/man/man1/strings.1               minix-sys       binutils
 ./usr/man/man1/strip.1                 minix-sys       binutils
 ./usr/man/man1/stty.1                  minix-sys
 ./usr/man/man1/su.1                    minix-sys
 ./usr/man/man1/sum.1                   minix-sys
+./usr/man/man1/suspend.1               minix-sys
 ./usr/man/man1/svc.1                   minix-sys       obsolete
 ./usr/man/man1/svrctl.1                        minix-sys
 ./usr/man/man1/synctree.1              minix-sys
 ./usr/share/doc/usd/03.shell/t3                minix-sys
 ./usr/share/doc/usd/03.shell/t4                minix-sys
 ./usr/share/doc/usd/03.shell/t.mac     minix-sys
+./usr/share/doc/usd/04.csh             minix-sys
+./usr/share/doc/usd/04.csh/Makefile    minix-sys
+./usr/share/doc/usd/04.csh/csh.1       minix-sys
+./usr/share/doc/usd/04.csh/csh.2       minix-sys
+./usr/share/doc/usd/04.csh/csh.3       minix-sys
+./usr/share/doc/usd/04.csh/csh.4       minix-sys
+./usr/share/doc/usd/04.csh/csh.ap      minix-sys
+./usr/share/doc/usd/04.csh/csh.g       minix-sys
+./usr/share/doc/usd/04.csh/tabs                minix-sys
 ./usr/share/doc/usd/30.rogue           minix-sys
 ./usr/share/doc/usd/30.rogue/Makefile  minix-sys
 ./usr/share/doc/usd/30.rogue/rogue.me  minix-sys
index 830b63d85d3fb89aae9d39627bb02f4bb52520ba..2e5c2723b0313a9a37791dad5ce17cd65c4c1700 100644 (file)
 ./usr/share/doc/psd/19.curses
 ./usr/share/doc/usd
 ./usr/share/doc/usd/03.shell
+./usr/share/doc/usd/04.csh
 ./usr/share/doc/usd/30.rogue
 ./usr/share/examples
 ./usr/share/examples/tmux