From d1e4d7ce7de96b58a7e34cb41f3fd9aa036d9692 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Mon, 28 Sep 2015 11:36:43 +0000 Subject: [PATCH] Import NetBSD csh(1) Jobctl warning commented out. Largely untested. Change-Id: I4dffe23a2855a374628c820703b51591633aed64 --- bin/Makefile | 2 +- bin/csh/Makefile | 76 ++ bin/csh/USD.doc/Makefile | 12 + bin/csh/USD.doc/csh.1 | 1010 +++++++++++++++ bin/csh/USD.doc/csh.2 | 1303 ++++++++++++++++++++ bin/csh/USD.doc/csh.3 | 648 ++++++++++ bin/csh/USD.doc/csh.4 | 176 +++ bin/csh/USD.doc/csh.ap | 93 ++ bin/csh/USD.doc/csh.g | 1719 ++++++++++++++++++++++++++ bin/csh/USD.doc/tabs | 32 + bin/csh/alloc.c | 91 ++ bin/csh/char.c | 315 +++++ bin/csh/char.h | 100 ++ bin/csh/const.c | 164 +++ bin/csh/csh.1 | 2293 +++++++++++++++++++++++++++++++++++ bin/csh/csh.c | 1401 +++++++++++++++++++++ bin/csh/csh.h | 559 +++++++++ bin/csh/dir.c | 901 ++++++++++++++ bin/csh/dir.h | 48 + bin/csh/dol.c | 968 +++++++++++++++ bin/csh/err.c | 388 ++++++ bin/csh/exec.c | 746 ++++++++++++ bin/csh/exp.c | 661 ++++++++++ bin/csh/extern.h | 341 ++++++ bin/csh/file.c | 729 +++++++++++ bin/csh/func.c | 1456 ++++++++++++++++++++++ bin/csh/glob.c | 923 ++++++++++++++ bin/csh/hist.c | 207 ++++ bin/csh/init.c | 131 ++ bin/csh/lex.c | 1631 +++++++++++++++++++++++++ bin/csh/misc.c | 407 +++++++ bin/csh/parse.c | 640 ++++++++++ bin/csh/pathnames.h | 44 + bin/csh/proc.c | 1359 +++++++++++++++++++++ bin/csh/proc.h | 104 ++ bin/csh/sem.c | 646 ++++++++++ bin/csh/set.c | 820 +++++++++++++ bin/csh/str.c | 441 +++++++ bin/csh/time.c | 285 +++++ distrib/sets/lists/minix/mi | 27 +- etc/mtree/NetBSD.dist.base | 1 + 41 files changed, 23896 insertions(+), 2 deletions(-) create mode 100644 bin/csh/Makefile create mode 100644 bin/csh/USD.doc/Makefile create mode 100644 bin/csh/USD.doc/csh.1 create mode 100644 bin/csh/USD.doc/csh.2 create mode 100644 bin/csh/USD.doc/csh.3 create mode 100644 bin/csh/USD.doc/csh.4 create mode 100644 bin/csh/USD.doc/csh.ap create mode 100644 bin/csh/USD.doc/csh.g create mode 100644 bin/csh/USD.doc/tabs create mode 100644 bin/csh/alloc.c create mode 100644 bin/csh/char.c create mode 100644 bin/csh/char.h create mode 100644 bin/csh/const.c create mode 100644 bin/csh/csh.1 create mode 100644 bin/csh/csh.c create mode 100644 bin/csh/csh.h create mode 100644 bin/csh/dir.c create mode 100644 bin/csh/dir.h create mode 100644 bin/csh/dol.c create mode 100644 bin/csh/err.c create mode 100644 bin/csh/exec.c create mode 100644 bin/csh/exp.c create mode 100644 bin/csh/extern.h create mode 100644 bin/csh/file.c create mode 100644 bin/csh/func.c create mode 100644 bin/csh/glob.c create mode 100644 bin/csh/hist.c create mode 100644 bin/csh/init.c create mode 100644 bin/csh/lex.c create mode 100644 bin/csh/misc.c create mode 100644 bin/csh/parse.c create mode 100644 bin/csh/pathnames.h create mode 100644 bin/csh/proc.c create mode 100644 bin/csh/proc.h create mode 100644 bin/csh/sem.c create mode 100644 bin/csh/set.c create mode 100644 bin/csh/str.c create mode 100644 bin/csh/time.c diff --git a/bin/Makefile b/bin/Makefile index ce4ea6c79..42421dc42 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -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 index 000000000..493a87bea --- /dev/null +++ b/bin/csh/Makefile @@ -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 +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 +.include diff --git a/bin/csh/USD.doc/Makefile b/bin/csh/USD.doc/Makefile new file mode 100644 index 000000000..fd3fff523 --- /dev/null +++ b/bin/csh/USD.doc/Makefile @@ -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 diff --git a/bin/csh/USD.doc/csh.1 b/bin/csh/USD.doc/csh.1 new file mode 100644 index 000000000..9f44af796 --- /dev/null +++ b/bin/csh/USD.doc/csh.1 @@ -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 index 000000000..803434b19 --- /dev/null +++ b/bin/csh/USD.doc/csh.2 @@ -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 index 000000000..a6a6f66e8 --- /dev/null +++ b/bin/csh/USD.doc/csh.3 @@ -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 index 000000000..d4760ce80 --- /dev/null +++ b/bin/csh/USD.doc/csh.4 @@ -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 index 000000000..7e71070ad --- /dev/null +++ b/bin/csh/USD.doc/csh.ap @@ -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 index 000000000..09f2baf84 --- /dev/null +++ b/bin/csh/USD.doc/csh.g @@ -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 index 000000000..77aa8da7e --- /dev/null +++ b/bin/csh/USD.doc/tabs @@ -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 index 000000000..9d158c601 --- /dev/null +++ b/bin/csh/alloc.c @@ -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 +#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 + +#include +#include +#include + +#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 index 000000000..dd1160cb9 --- /dev/null +++ b/bin/csh/char.c @@ -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 +#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 index 000000000..966d46142 --- /dev/null +++ b/bin/csh/char.h @@ -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 + +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 index 000000000..7693580f0 --- /dev/null +++ b/bin/csh/const.c @@ -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 +#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 index 000000000..d4efebcc4 --- /dev/null +++ b/bin/csh/csh.1 @@ -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 index 000000000..f03f78a93 --- /dev/null +++ b/bin/csh/csh.c @@ -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 +#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 +#include +#include + +#include +#include +#include +#include /* should this be included in pathnames.h instead? */ +#include +#include +#include +#include +#include +#include +#include + +#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 - 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") + * 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 = ¶ml; + 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(¶ml); + 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(¶ml) && !seterr && intty) || adrof(STRverbose)) { + int odidfds = didfds; + fflush(csherr); + didfds = 0; + prlex(csherr, ¶ml); + 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(¶ml); + + /* + * 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(¶ml); + + /* + * Parse the words of the input into a parse tree. + */ + savet = syntax(paraml.next, ¶ml, 0); + if (seterr) + stderror(ERR_OLD); + + execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); + + /* + * Made it! + */ + freelex(¶ml); + 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 index 000000000..6a186d7ec --- /dev/null +++ b/bin/csh/csh.h @@ -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 +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 +#include +#include + +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 +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 +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_drit */ +#define NODE_PAREN 2 /* ( t_dspr ) 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 +#ifdef _PATH_BSHELL +Char *STR_BSHELL; +#endif +Char *STR_WORD_CHARS; +Char **STR_environ; + +#ifdef EDIT +#include +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 index 000000000..418d30236 --- /dev/null +++ b/bin/csh/dir.c @@ -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 +#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 +#include + +#include +#include +#include +#include +#include + +#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, " []"); + 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, " [|+]"); + 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, " [+]"); + 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 index 000000000..16b9babff --- /dev/null +++ b/bin/csh/dir.h @@ -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 index 000000000..308203ee3 --- /dev/null +++ b/bin/csh/dol.c @@ -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 +#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 + +#include +#include +#include +#include +#include +#include +#include + +#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, "$#"); + 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 index 000000000..030aaf724 --- /dev/null +++ b/bin/csh/err.c @@ -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 +#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 + +#include +#include +#include + +#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 + "$? or $# 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 index 000000000..bd2b343b4 --- /dev/null +++ b/bin/csh/exec.c @@ -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 +#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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 + * 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 + * 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 index 000000000..a4be1fc14 --- /dev/null +++ b/bin/csh/exp.c @@ -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 +#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 +#include + +#include +#include +#include + +#ifndef SHORT_STRINGS +#include +#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 = ¶ml1; + 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(¶ml1); + t = syntax(paraml1.next, ¶ml1, 0); + if (seterr) + stderror(ERR_OLD); + execute(t, -1, NULL, NULL); + freelex(¶ml1), 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 index 000000000..0d882f93c --- /dev/null +++ b/bin/csh/extern.h @@ -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 + +/* + * 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 index 000000000..0933b1ac6 --- /dev/null +++ b/bin/csh/file.c @@ -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 +#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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef SHORT_STRINGS +#include +#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 index 000000000..2501a8290 --- /dev/null +++ b/bin/csh/func.c @@ -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 +#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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..7f01c9c24 --- /dev/null +++ b/bin/csh/glob.c @@ -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 +#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 + +#include +#include +#include +#include +#include +#include +#include + +#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 , + * 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 index 000000000..1c5bf9d21 --- /dev/null +++ b/bin/csh/hist.c @@ -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 +#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 + +#include +#include + +#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 index 000000000..06a34270b --- /dev/null +++ b/bin/csh/init.c @@ -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 +#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 + +#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 index 000000000..8c1bca818 --- /dev/null +++ b/bin/csh/lex.c @@ -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 +#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 +#include + +#include +#include +#include +#include +#include +#include + +#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 (¶ml); + /* 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 index 000000000..88d2f8e94 --- /dev/null +++ b/bin/csh/misc.c @@ -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 +#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 + +#include +#include +#include +#include +#include + +#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 index 000000000..25bc43867 --- /dev/null +++ b/bin/csh/parse.c @@ -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 +#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 + +#include +#include +#include + +#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 index 000000000..db2e37358 --- /dev/null +++ b/bin/csh/pathnames.h @@ -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 index 000000000..5c53d98f7 --- /dev/null +++ b/bin/csh/proc.c @@ -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 +#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 +#include + +#include +#include +#include +#include +#include + +#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 + * + */ + 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 + * 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 index 000000000..783d29251 --- /dev/null +++ b/bin/csh/proc.h @@ -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 index 000000000..acda703d6 --- /dev/null +++ b/bin/csh/sem.c @@ -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 +#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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 index 000000000..9b73abd3e --- /dev/null +++ b/bin/csh/set.c @@ -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 +#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 + +#include +#include + +#ifndef SHORT_STRINGS +#include +#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 index 000000000..e828cc42b --- /dev/null +++ b/bin/csh/str.c @@ -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 +#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 + +#include +#include + +#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 index 000000000..bd8c9ad3c --- /dev/null +++ b/bin/csh/time.c @@ -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 +#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 +#include +#include "csh.h" +#include "extern.h" +#endif +#include + +/* + * 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)); +} diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 6ba620154..1c74b3f64 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -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 @@ -2310,6 +2311,7 @@ ./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 @@ -2328,6 +2330,7 @@ ./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 @@ -2369,6 +2372,7 @@ ./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 @@ -2380,6 +2384,7 @@ ./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 @@ -2400,6 +2405,7 @@ ./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 @@ -2409,6 +2415,7 @@ ./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 @@ -2432,6 +2439,7 @@ ./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 @@ -2447,7 +2455,7 @@ ./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 @@ -2476,6 +2484,7 @@ ./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 @@ -2541,12 +2550,14 @@ ./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 @@ -2557,7 +2568,9 @@ ./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 @@ -2582,15 +2595,18 @@ ./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 @@ -5420,6 +5436,15 @@ ./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 diff --git a/etc/mtree/NetBSD.dist.base b/etc/mtree/NetBSD.dist.base index 830b63d85..2e5c2723b 100644 --- a/etc/mtree/NetBSD.dist.base +++ b/etc/mtree/NetBSD.dist.base @@ -131,6 +131,7 @@ ./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