diff --git a/bin/Makefile b/bin/Makefile index 7066ea3b7..2215fdb5b 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -2,7 +2,7 @@ # @(#)Makefile 8.1 (Berkeley) 5/31/93 SUBDIR= cat chmod cp date df echo ed expr hostname \ - kill ksh ln ls mkdir mv pax pwd rm rmdir \ + kill ksh ln ls mkdir mv pax pwd rm rmdir sh \ sleep stty sync test .include diff --git a/bin/sh/Makefile b/bin/sh/Makefile new file mode 100644 index 000000000..28890a263 --- /dev/null +++ b/bin/sh/Makefile @@ -0,0 +1,88 @@ +# $NetBSD: Makefile,v 1.99 2012/12/02 12:55:27 apb Exp $ +# @(#)Makefile 8.4 (Berkeley) 5/5/95 + +.include + +YHEADER=1 +PROG= sh +SHSRCS= alias.c cd.c echo.c error.c eval.c exec.c expand.c \ + histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \ + mystring.c options.c parser.c redir.c show.c trap.c output.c var.c \ + test.c kill.c syntax.c +GENSRCS=arith.c arith_lex.c builtins.c init.c nodes.c +GENHDRS=arith.h builtins.h nodes.h token.h +SRCS= ${SHSRCS} ${GENSRCS} + +DPSRCS+=${GENHDRS} + +LDADD+= -ll -ledit -lterminfo +DPADD+= ${LIBL} ${LIBEDIT} ${LIBTERMINFO} + +LFLAGS= -8 # 8-bit lex scanner for arithmetic + +# Environment for scripts executed during build. +SCRIPT_ENV= \ + AWK=${TOOL_AWK:Q} \ + SED=${TOOL_SED:Q} + +# The .depend file can get references to these temporary files +.OPTIONAL: lex.yy.c y.tab.c + +.ifdef CRUNCHEDPROG +LFLAGS+=-L +YFLAGS+=-l +.endif + +CPPFLAGS+=-DSHELL -I. -I${.CURDIR} +#XXX: For testing only. +#CPPFLAGS+=-DDEBUG=2 +#COPTS+=-g +#CFLAGS+=-funsigned-char +#TARGET_CHARFLAG?= -DTARGET_CHAR="unsigned char" -funsigned-char + +.ifdef SMALLPROG +CPPFLAGS+=-DSMALL +.else +SRCS+=printf.c +.endif + +.PATH: ${.CURDIR}/bltin ${NETBSDSRCDIR}/bin/test \ + ${NETBSDSRCDIR}/usr.bin/printf \ + ${NETBSDSRCDIR}/bin/kill + +CLEANFILES+= ${GENSRCS} ${GENHDRS} y.tab.h +CLEANFILES+= trace + +token.h: mktokens + ${_MKTARGET_CREATE} + ${SCRIPT_ENV} ${HOST_SH} ${.ALLSRC} + +.ORDER: builtins.h builtins.c +builtins.h builtins.c: mkbuiltins shell.h builtins.def + ${_MKTARGET_CREATE} + ${SCRIPT_ENV} ${HOST_SH} ${.ALLSRC} ${.OBJDIR} + [ -f builtins.h ] + +init.c: mkinit.sh ${SHSRCS} + ${_MKTARGET_CREATE} + ${SCRIPT_ENV} ${HOST_SH} ${.ALLSRC} + +.ORDER: nodes.h nodes.c +nodes.c nodes.h: mknodes.sh nodetypes nodes.c.pat + ${_MKTARGET_CREATE} + ${SCRIPT_ENV} ${HOST_SH} ${.ALLSRC} ${.OBJDIR} + [ -f nodes.h ] + +.if ${USETOOLS} == "yes" +NBCOMPATLIB= -L${TOOLDIR}/lib -lnbcompat +.endif + +.if make(install) +SUBDIR+=USD.doc +.endif + +COPTS.printf.c = -Wno-format-nonliteral +COPTS.jobs.c = -Wno-format-nonliteral + +.include +.include diff --git a/minix/commands/ash/TOUR b/bin/sh/TOUR similarity index 97% rename from minix/commands/ash/TOUR rename to bin/sh/TOUR index afd0c8dd2..681f9866d 100644 --- a/minix/commands/ash/TOUR +++ b/bin/sh/TOUR @@ -1,5 +1,5 @@ +# $NetBSD: TOUR,v 1.10 2008/11/15 17:01:38 snj Exp $ # @(#)TOUR 8.1 (Berkeley) 5/31/93 -# $FreeBSD: src/bin/sh/TOUR,v 1.6 1999/08/27 23:15:07 peter Exp $ NOTE -- This is the original TOUR paper distributed with ash and does not represent the current state of the shell. It is provided anyway @@ -22,7 +22,7 @@ SOURCE CODE GENERATORS: Files whose names begin with "mk" are programs that generate source code. A complete list of these programs is: - program intput files generates + program input files generates ------- ------------ --------- mkbuiltins builtins builtins.h builtins.c mkinit *.c init.c @@ -91,7 +91,7 @@ INTERRUPTS: In an interactive shell, an interrupt will cause an EXINT exception to return to the main command loop. (Exception: EXINT is not raised if the user traps interrupts using the trap command.) The INTOFF and INTON macros (defined in exception.h) -provide uninterruptable critical sections. Between the execution +provide uninterruptible critical sections. Between the execution of INTOFF and the execution of INTON, interrupt signals will be held for later delivery. INTOFF and INTON can be nested. @@ -110,7 +110,7 @@ string was going to be: p = stackptr; *p++ = c; /* repeated as many times as needed */ stackptr = p; -The folloing three macros (defined in memalloc.h) perform these +The following three macros (defined in memalloc.h) perform these operations, but grow the stack if you run off the end: STARTSTACKSTR(p); STPUTC(c, p); /* repeated as many times as needed */ @@ -232,7 +232,7 @@ control is defined. REDIR.C: Ash allows file descriptors to be redirected and then restored without forking off a child process. This is accom- plished by duplicating the original file descriptors. The redir- -tab structure records where the file descriptors have be dupli- +tab structure records where the file descriptors have been dupli- cated to. EXEC.C: The routine find_command locates a command, and enters @@ -327,7 +327,7 @@ is called at appropriate points to actually handle the signal. When an interrupt is caught and no trap has been set for that signal, the routine "onint" in error.c is called. -OUTPUT: Ash uses it's own output routines. There are three out- +OUTPUT: Ash uses its own output routines. There are three out- put structures allocated. "Output" represents the standard out- put, "errout" the standard error, and "memout" contains output which is to be stored in memory. This last is used when a buil- @@ -355,6 +355,3 @@ cause the preprocessor can't handle functions with a variable number of arguments. Defining DEBUG also causes the shell to generate a core dump if it is sent a quit signal. The tracing code is in show.c. - -# -# $PchId: TOUR,v 1.3 2006/03/31 11:33:46 philip Exp $ diff --git a/bin/sh/USD.doc/Makefile b/bin/sh/USD.doc/Makefile new file mode 100644 index 000000000..b12590eef --- /dev/null +++ b/bin/sh/USD.doc/Makefile @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.1 2010/08/22 01:58:16 perry Exp $ +# @(#)Makefile 8.1 (Berkeley) 8/14/93 + +DIR= usd/03.shell +SRCS= Rv7man t.mac t1 t2 t3 t4 +MACROS= -ms + +paper.ps: ${SRCS} + ${TOOL_REFER} -e -p ${SRCS} | \ + ${TOOL_ROFF_PS} ${MACROS} > ${.TARGET} + +.include diff --git a/bin/sh/USD.doc/Rv7man b/bin/sh/USD.doc/Rv7man new file mode 100644 index 000000000..e5da45212 --- /dev/null +++ b/bin/sh/USD.doc/Rv7man @@ -0,0 +1,405 @@ +%A L. P. Deutsch +%A B. W. Lampson +%T An online editor +%J Comm. Assoc. Comp. Mach. +%V 10 +%N 12 +%D December 1967 +%P 793-799, 803 +%K qed + +.[ +%r 17 +%K cstr +%R Comp. Sci. Tech. Rep. No. 17 +%I Bell Laboratories +%C Murray Hill, New Jersey +%A B. W. Kernighan +%A L. L. Cherry +%T A System for Typesetting Mathematics +%d May 1974, revised April 1977 +%J Comm. Assoc. Comp. Mach. +%K acm cacm +%V 18 +%P 151-157 +%D March 1975 +.] + +%T U\s-2NIX\s0 Time-Sharing System: Document Preparation +%K unix bstj +%A B. W. Kernighan +%A M. E. Lesk +%A J. F. Ossanna +%J Bell Sys. Tech. J. +%V 57 +%N 6 +%P 2115-2135 +%D 1978 + +%A T. A. Dolotta +%A J. R. Mashey +%T An Introduction to the Programmer's Workbench +%J Proc. 2nd Int. Conf. on Software Engineering +%D October 13-15, 1976 +%P 164-168 + +%T U\s-2NIX\s0 Time-Sharing System: The Programmer's Workbench +%A T. A. Dolotta +%A R. C. Haight +%A J. R. Mashey +%J Bell Sys. Tech. J. +%V 57 +%N 6 +%P 2177-2200 +%D 1978 +%K unix bstj + +%T U\s-2NIX\s0 Time-Sharing System: U\s-2NIX\s0 on a Microprocessor +%K unix bstj +%A H. Lycklama +%J Bell Sys. Tech. J. +%V 57 +%N 6 +%P 2087-2101 +%D 1978 + +%T The C Programming Language +%A B. W. Kernighan +%A D. M. Ritchie +%I Prentice-Hall +%C Englewood Cliffs, New Jersey +%D 1978 + +%T Computer Recreations +%A Aleph-null +%J Software Practice and Experience +%V 1 +%N 2 +%D April-June 1971 +%P 201-204 + +%T U\s-2NIX\s0 Time-Sharing System: The U\s-2NIX\s0 Shell +%A S. R. Bourne +%K unix bstj +%J Bell Sys. Tech. J. +%V 57 +%N 6 +%P 1971-1990 +%D 1978 + +%A L. P. Deutsch +%A B. W. Lampson +%T \*sSDS\*n 930 time-sharing system preliminary reference manual +%R Doc. 30.10.10, Project \*sGENIE\*n +%C Univ. Cal. at Berkeley +%D April 1965 + +%A R. J. Feiertag +%A E. I. Organick +%T The Multics input-output system +%J Proc. Third Symposium on Operating Systems Principles +%D October 18-20, 1971 +%P 35-41 + +%A D. G. Bobrow +%A J. D. Burchfiel +%A D. L. Murphy +%A R. S. Tomlinson +%T \*sTENEX\*n, a Paged Time Sharing System for the \*sPDP\*n-10 +%J Comm. Assoc. Comp. Mach. +%V 15 +%N 3 +%D March 1972 +%K tenex +%P 135-143 + +%A R. E. Griswold +%A D. R. Hanson +%T An Overview of SL5 +%J SIGPLAN Notices +%V 12 +%N 4 +%D April 1977 +%P 40-50 + +%A E. W. Dijkstra +%T Cooperating Sequential Processes +%B Programming Languages +%E F. Genuys +%I Academic Press +%C New York +%D 1968 +%P 43-112 + +%A J. A. Hawley +%A W. B. Meyer +%T M\s-2UNIX\s0, A Multiprocessing Version of U\s-2NIX\s0 +%K munix unix +%R M.S. Thesis +%I Naval Postgraduate School +%C Monterey, Cal. +%D 1975 + +%T The U\s-2NIX\s0 Time-Sharing System +%K unix bstj +%A D. M. Ritchie +%A K. Thompson +%J Bell Sys. Tech. J. +%V 57 +%N 6 +%P 1905-1929 +%D 1978 + +%A E. I. Organick +%T The M\s-2ULTICS\s0 System +%K multics +%I M.I.T. Press +%C Cambridge, Mass. +%D 1972 + +%T UNIX for Beginners +%A B. W. Kernighan +%D 1978 + +%T U\s-2NIX\s0 Programmer's Man\&ual +%A K. Thompson +%A D. M. Ritchie +%K unix +%I Bell Laboratories +%O Seventh Edition. +%D 1978 + +%A K. Thompson +%T The U\s-2NIX\s0 Command Language +%B Structured Programming\(emInfotech State of the Art Report +%I Infotech International Ltd. +%C Nicholson House, Maidenhead, Berkshire, England +%D March 1975 +%P 375-384 +%K unix +%X pwb +Brief description of shell syntax and semantics, without much +detail on implementation. +Much on pipes and convenience of hooking programs together. +Includes SERMONETTE: +"Many familiar computing `concepts' are missing from UNIX. +Files have no records. There are no access methods. +There are no file types. These concepts fill a much-needed gap. +I sincerely hope that when future systems are designed by +manufacturers the value of some of these ingrained notions is re-examined. +Like the politician and his `common man', manufacturers have +their `average user'. + +%A J. R. Mashey +%T PWB/UNIX Shell Tutorial +%D September 30, 1977 + +%A D. F. Hartley (Ed.) +%T The Cambridge Multiple Access System \- Users Reference Manual +%I University Mathematical Laboratory +%C Cambridge, England +%D 1968 + +%A P. A. Crisman (Ed.) +%T The Compatible Time-Sharing System +%I M.I.T. Press +%K whole ctss book +%C Cambridge, Mass. +%D 1965 + +%T LR Parsing +%A A. V. Aho +%A S. C. Johnson +%J Comp. Surveys +%V 6 +%N 2 +%P 99-124 +%D June 1974 + +%T Deterministic Parsing of Ambiguous Grammars +%A A. V. Aho +%A S. C. Johnson +%A J. D. Ullman +%J Comm. Assoc. Comp. Mach. +%K acm cacm +%V 18 +%N 8 +%P 441-452 +%D August 1975 + +%A A. V. Aho +%A J. D. Ullman +%T Principles of Compiler Design +%I Addison-Wesley +%C Reading, Mass. +%D 1977 + +.[ +%r 65 +%R Comp. Sci. Tech. Rep. No. 65 +%K CSTR +%A S. C. Johnson +%T Lint, a C Program Checker +%D December 1977 +%O updated version TM 78-1273-3 +%D 1978 +.] + +%T A Portable Compiler: Theory and Practice +%A S. C. Johnson +%J Proc. 5th ACM Symp. on Principles of Programming Languages +%P 97-104 +%D January 1978 + +.[ +%r 39 +%K CSTR +%R Comp. Sci. Tech. Rep. No. 39 +%I Bell Laboratories +%C Murray Hill, New Jersey +%A M. E. Lesk +%T Lex \(em A Lexical Analyzer Generator +%D October 1975 +.] + +.[ +%r 32 +%K CSTR +%R Comp. Sci. Tech. Rep. No. 32 +%I Bell Laboratories +%C Murray Hill, New Jersey +%A S. C. Johnson +%T Yacc \(em Yet Another Compiler-Compiler +%D July 1975 +.] + +%T U\s-2NIX\s0 Time-Sharing System: Portability of C Programs and the U\s-2NIX\s0 System +%K unix bstj +%A S. C. Johnson +%A D. M. Ritchie +%J Bell Sys. Tech. J. +%V 57 +%N 6 +%P 2021-2048 +%D 1978 + +%T Typing Documents on UNIX and GCOS: The -ms Macros for Troff +%A M. E. Lesk +%D 1977 + +%A K. Thompson +%A D. M. Ritchie +%T U\s-2NIX\s0 Programmer's Manual +%K unix +%I Bell Laboratories +%O Sixth Edition +%D May 1975 + +%T The Network U\s-2NIX\s0 System +%K unix +%A G. L. Chesson +%J Operating Systems Review +%V 9 +%N 5 +%P 60-66 +%D 1975 +%O Also in \f2Proc. 5th Symp. on Operating Systems Principles.\f1 + +%T Spider \(em An Experimental Data Communications System +%Z ctr127 +%A A. G. Fraser +%J Proc. IEEE Conf. on Communications +%P 21F +%O IEEE Cat. No. 74CH0859-9-CSCB. +%D June 1974 + +%T A Virtual Channel Network +%A A. G. Fraser +%J Datamation +%P 51-56 +%D February 1975 + +.[ +%r 41 +%K CSTR +%R Comp. Sci. Tech. Rep. No. 41 +%I Bell Laboratories +%C Murray Hill, New Jersey +%A J. W. Hunt +%A M. D. McIlroy +%T An Algorithm for Differential File Comparison +%D June 1976 +.] + +%A F. P. Brooks, Jr. +%T The Mythical Man-Month +%I Addison-Wesley +%C Reading, Mass. +%D 1975 +%X pwb +Readable, classic reference on software engineering and +problems of large projects, from someone with experience in them. +Required reading for any software engineer, even if conclusions may not +always be agreed with. +%br +"The second is the most dangerous system a man every designs." p.55. +%br +"Hence plan to throw one away; you will, anyhow." p.116. +%br +"Cosgrove has perceptively pointed out that the programmer delivers +satisfaction of a user need rather than any tangible product. +And both the actual need and the user's perception of that need +will change as programs are built, tested, and used." p.117. +%br +"The total cost of maintaining a widely used program is typically 40 percent +or more of the cost of developing it." p.121. +%br +"As shown above, amalgamating prose and program reduces the total +number of characters to be stored." p.175. + +%T A Portable Compiler for the Language C +%A A. Snyder +%I Master's Thesis, M.I.T. +%C Cambridge, Mass. +%D 1974 + +%T The C Language Calling Sequence +%A M. E. Lesk +%A S. C. Johnson +%A D. M. Ritchie +%D 1977 + +%T Optimal Code Generation for Expression Trees +%A A. V. Aho +%A S. C. Johnson +%D 1975 +%J J. Assoc. Comp. Mach. +%K acm jacm +%V 23 +%N 3 +%P 488-501 +%O Also in \f2Proc. ACM Symp. on Theory of Computing,\f1 pp. 207-217, 1975. + +%A R. Sethi +%A J. D. Ullman +%T The Generation of Optimal Code for Arithmetic Expressions +%J J. Assoc. Comp. Mach. +%K acm jacm +%V 17 +%N 4 +%D October 1970 +%P 715-728 +%O Reprinted as pp. 229-247 in \fICompiler Techniques\fR, ed. B. W. Pollack, Auerbach, Princeton NJ (1972). +%X pwb +Optimal approach for straight-line, fixed +number of regs. + +%T Code Generation for Machines with Multiregister +Operations +%A A. V. Aho +%A S. C. Johnson +%A J. D. Ullman +%J Proc. 4th ACM Symp. on Principles of Programming Languages +%P 21-28 +%D January 1977 + diff --git a/bin/sh/USD.doc/t.mac b/bin/sh/USD.doc/t.mac new file mode 100644 index 000000000..9bf65c8fe --- /dev/null +++ b/bin/sh/USD.doc/t.mac @@ -0,0 +1,69 @@ +.\" $NetBSD: t.mac,v 1.2 2010/08/22 02:19:07 perry Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are +.\" met: +.\" +.\" Redistributions of source code and documentation must retain the above +.\" copyright notice, this list of conditions and the following +.\" disclaimer. +.\" +.\" 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. +.\" +.\" All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" +.\" This product includes software developed or owned by Caldera +.\" International, Inc. Neither the name of Caldera International, Inc. +.\" nor the names of other contributors may be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" @(#)t.mac 8.1 (Berkeley) 8/14/93 +.\" +.ds ZZ \fB.\|.\|.\fP +.ds ST \v'.3m'\s+2*\s0\v'-.3m' +.ds DO \h'\w'do 'u' +.ds Ca \h'\w'case 'u' +.ds WH \h'\w'while 'u' +.ds VT \|\fB\(or\fP\| +.ds TH \h'\w'then 'u' +.ds DC \*(DO\*(Ca +.ds AP >\h'-.2m'> +.ds HE <\h'-.2m'< +. \" macros for algol 68c reference manual +.ds DA 1977 November 1 +.ds md \v'.25m' +.ds mu \v'-.25m' +.ds U \*(mu\s-3 +.ds V \s0\*(md +.ds L \*(md\s-3 +.ds M \s0\*(mu +.ds S \s-1 +.ds T \s0 +. \" small 1 +.ds O \*S1\*T +.ds h \| +.ds s \|\| +. \" ellipsis +.ds e .\|.\|. +. \" subscripts +.ds 1 \*(md\s-41\s0\*(mu +.ds 2 \*(md\s-42\s0\*(mu diff --git a/bin/sh/USD.doc/t1 b/bin/sh/USD.doc/t1 new file mode 100644 index 000000000..075511fe8 --- /dev/null +++ b/bin/sh/USD.doc/t1 @@ -0,0 +1,553 @@ +.\" $NetBSD: t1,v 1.3 2010/08/22 02:19:07 perry Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are +.\" met: +.\" +.\" Redistributions of source code and documentation must retain the above +.\" copyright notice, this list of conditions and the following +.\" disclaimer. +.\" +.\" 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. +.\" +.\" All advertising materials mentioning features or use of this software +.\" must display the following acknowledgment: +.\" +.\" This product includes software developed or owned by Caldera +.\" International, Inc. Neither the name of Caldera International, Inc. +.\" nor the names of other contributors may be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" @(#)t1 8.1 (Berkeley) 8/14/93 +.\" +.EH 'USD:3-%''An Introduction to the UNIX Shell' +.OH 'An Introduction to the UNIX Shell''USD:3-%' +.\".RP +.TL +An Introduction to the UNIX Shell +.AU +S. R. Bourne +.AI +Murray Hill, NJ +.AU +(Updated for 4.3BSD by Mark Seiden) +.AU +(Further updated by Perry E. Metzger)\(dg +.AB +.FS +\(dg This paper was updated in 2010 to reflect most features of modern +POSIX shells, which all follow the design of S.R. Bourne's original v7 +Unix shell. +Among these are ash, bash, ksh and others. +Typically one of these will be installed as /bin/sh on a modern system. +It does not describe the behavior of the c shell (csh). +If it's the c shell (csh) you're interested in, a good place to +begin is William Joy's paper "An Introduction to the C shell" (USD:4). +.FE +.LP +The +.ul +shell +is a command programming language that provides an interface +to the +.UX +operating system. +Its features include +control-flow primitives, parameter passing, variables and +string substitution. +Constructs such as +.ul +while, if then else, case +and +.ul +for +are available. +Two-way communication is possible between the +.ul +shell +and commands. +String-valued parameters, typically file names or flags, may be +passed to a command. +A return code is set by commands that may be used to determine control-flow, +and the standard output from a command may be used +as shell input. +.LP +The +.ul +shell +can modify the environment +in which commands run. +Input and output can be redirected +to files, and processes that communicate through `pipes' +can be invoked. +Commands are found by +searching directories +in the file system in a +sequence that can be defined by the user. +Commands can be read either from the terminal or from a file, +which allows command procedures to be +stored for later use. +.AE +.ds ST \v'.3m'\s+2*\s0\v'-.3m' +.SH +1.0\ Introduction +.LP +The shell is both a command language +and a programming language +that provides an interface to the UNIX +operating system. +This memorandum describes, with +examples, the UNIX shell. +The first section covers most of the +everyday requirements +of terminal users. +Some familiarity with UNIX +is an advantage when reading this section; +see, for example, +"UNIX for beginners". +.[ +unix beginn kernigh 1978 +.] +Section 2 describes those features +of the shell primarily intended +for use within shell procedures. +These include the control-flow +primitives and string-valued variables +provided by the shell. +A knowledge of a programming language +would be a help when reading this section. +The last section describes the more +advanced features of the shell. +References of the form "see \fIpipe\fP (2)" +are to a section of the UNIX manual. +.[ +seventh 1978 ritchie thompson +.] +.SH +1.1\ Simple\ commands +.LP +Simple commands consist of one or more words +separated by blanks. +The first word is the name of the command +to be executed; any remaining words +are passed as arguments to the command. +For example, +.DS + who +.DE +is a command that prints the names +of users logged in. +The command +.DS + ls \(mil +.DE +prints a list of files in the current +directory. +The argument \fI\(mil\fP tells \fIls\fP +to print status information, size and +the creation date for each file. +.SH +1.2\ Input\ output\ redirection +.LP +Most commands produce output on the standard output +that is initially connected to the terminal. +This output may be sent to a file +by writing, for example, +.DS + ls \(mil >file +.DE +The notation \fI>file\fP +is interpreted by the shell and is not passed +as an argument to \fIls.\fP +If \fIfile\fP does not exist then the +shell creates it; +otherwise the original contents of +\fIfile\fP are replaced with the output +from \fIls.\fP +Output may be appended to a file +using the notation +.DS + ls \(mil \*(APfile +.DE +In this case \fIfile\fP is also created if it does not already +exist. +.LP +The standard input of a command may be taken +from a file instead of the terminal by +writing, for example, +.DS + wc \|. +.\" For example +.\" .DS +.\" command some args >out 2>errors +.\" .DE +.\" will redirect standard output to the file `out' but standard error +.\" (and thus all error messages) to `errors'. +.\" The notation 2>&1 sets standard error pointing to the same +.\" place as standard out. +.\" Thus: +.\" .DS +.\" command some args 2>&1 >everything +.\" .DE +.\" will put both standard out and standard error into the file `everything'. +.\" See section 3.7 below for more details. +.SH +1.3\ Pipelines\ and\ filters +.LP +The standard output of one command may be +connected to the standard input of another +by writing +the `pipe' operator, +indicated by \*(VT, +as in, +.DS + ls \(mil \*(VT wc +.DE +Two commands connected in this way constitute +a \fIpipeline\fP and +the overall effect is the same as +.DS + ls \(mil >file; wc \*(ST ? \*(VT &\|,\fR +are called metacharacters. +A complete list of metacharacters is given +in appendix B. +Any character preceded by a \fB\\\fR is \fIquoted\fP +and loses its special meaning, if any. +The \fB\\\fP is elided so that +.DS + echo \\? +.DE +will echo a single \fB?\|,\fP +and +.DS + echo \\\\ +.DE +will echo a single \fB\\\|.\fR +To allow long strings to be continued over +more than one line +the sequence \fB\\newline\fP +is ignored. +.LP +\fB\\\fP is convenient for quoting +single characters. +When more than one character needs +quoting the above mechanism is clumsy and +error prone. +A string of characters may be quoted +by enclosing the string between single quotes. +For example, +.DS + echo xx\'\*(ST\*(ST\*(ST\*(ST\'xx +.DE +will echo +.DS + xx\*(ST\*(ST\*(ST\*(STxx +.DE +The quoted string may not contain +a single quote +but may contain newlines, which are preserved. +This quoting mechanism is the most +simple and is recommended +for casual use. +.LP +A third quoting mechanism using double quotes +is also available +that prevents interpretation of some but not all +metacharacters. +Discussion of the +details is deferred +to section 3.5\|. +.SH +1.7\ Prompting +.LP +When the shell is used from a terminal it will +issue a prompt before reading a command. +By default this prompt is `\fB$\ \fR'\|. +It may be changed by saying, +for example, +.DS + \s-1PS1\s0="yesdear$ " +.DE +that sets the prompt to be the string \fIyesdear$\|.\fP +If a newline is typed and further input is needed +then the shell will issue the prompt `\fB>\ \fR'\|. +Sometimes this can be caused by mistyping +a quote mark. +If it is unexpected then entering the interrupt character +(typically \s-1CONTROL-C\s0) +will return the shell to read another command. +This prompt may be changed by saying, for example, +.DS + \s-1PS2\s0=more +.DE +Entering the interrupt character may also be used to terminate most +programs running as the current foreground job. +.LP +(\s-1PS1\s0 and \s-1PS2\s0 are \fIshell variables\fP, which will be +described in section 2.4 below.) +.SH +1.8\ The\ shell\ and\ login +.LP +Following \fIlogin\fP(1) +the shell is called to read and execute +commands typed at the terminal. +If the user's login directory +contains the file \fB.profile\fP +then it is assumed to contain commands +and is read by the shell before reading +any commands from the terminal. +.LP +(Most versions of the shell also specify a file that is read and +executed on start-up whether or not the shell is invoked by login. +The \s-1ENV\s0 shell variable, described in section 2.4 below, can be +used to override the name of this file. +See the shell manual page for further information.) +.SH +1.9\ Summary +.sp +.RS +.IP \(bu +\fBls\fP +.br +Print the names of files in the current directory. +.IP \(bu +\fBls >file\fP +.br +Put the output from \fIls\fP into \fIfile.\fP +.IP \(bu +\fBls \*(VT wc \(mil\fR +.br +Print the number of files in the current directory. +.IP \(bu +\fBls \*(VT grep old\fR +.br +Print those file names containing the string \fIold.\fP +.IP \(bu +\fBls \*(VT grep old \*(VT wc \(mil\fR +.br +Print the number of files whose name contains the string \fIold.\fP +.IP \(bu +\fBcc pgm.c &\fR +.br +Run \fIcc\fP in the background. +.RE diff --git a/bin/sh/USD.doc/t2 b/bin/sh/USD.doc/t2 new file mode 100644 index 000000000..d49747e90 --- /dev/null +++ b/bin/sh/USD.doc/t2 @@ -0,0 +1,971 @@ +.\" $NetBSD: t2,v 1.3 2010/08/22 02:19:07 perry Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are +.\" met: +.\" +.\" Redistributions of source code and documentation must retain the above +.\" copyright notice, this list of conditions and the following +.\" disclaimer. +.\" +.\" 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. +.\" +.\" All advertising materials mentioning features or use of this software +.\" must display the following acknowledgment: +.\" +.\" This product includes software developed or owned by Caldera +.\" International, Inc. Neither the name of Caldera International, Inc. +.\" nor the names of other contributors may be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" @(#)t2 8.1 (Berkeley) 6/8/93 +.\" +.SH +2.0\ Shell\ scripts +.LP +The shell may be used to read and execute commands +contained in a file. +For example, +.DS + sh file [ args \*(ZZ ] +.DE +calls the shell to read commands from \fIfile.\fP +Such a file is called a \fIshell script.\fP +Arguments may be supplied with the call +and are referred to in \fIfile\fP +using the positional parameters +\fB$1, $2, \*(ZZ\|.\fR +.LP +For example, if the file \fIwg\fP contains +.DS + who \*(VT grep $1 +.DE +then +.DS + sh wg fred +.DE +is equivalent to +.DS + who \*(VT grep fred +.DE +.LP +UNIX files have three independent attributes, +\fIread,\fP \fIwrite\fP and \fIexecute.\fP +The UNIX command \fIchmod\fP(1) may be used +to make a file executable. +For example, +.DS + chmod +x wg +.DE +will ensure that the file \fIwg\fP has execute status. +Following this, the command +.DS + wg fred +.DE +is equivalent to +.DS + sh wg fred +.DE +This allows shell scripts and other programs +to be used interchangeably. +In either case a new process is created to +run the command. +.LP +The `\fB#\fP' character is used as a comment character by the shell. +All characters following the `#' on a line are ignored. +.LP +A typical modern system has several different shells, some with differing +command syntax, and it is desirable to specify which one should be +invoked when an executable script is invoked. +If the special comment +.DS + #!/\fIpath\fP/\fIto\fP/\fIinterpreter\fP +.DE +appears as the first line in a script, it is used to specify the +absolute pathname of the shell (or other interpreter) that should be +used to execute the file. +(Without such a line, \fB/bin/sh\fP is assumed.) +It is best if a script explicitly states +what shell it is intended for in this manner. +.LP +As well as providing names for the positional +parameters, +the number of positional parameters to a script +is available as \fB$#\|.\fP +The name of the file being executed +is available as \fB$0\|.\fP +.LP +A special shell parameter \fB$\*(ST\fP +is used to substitute for all positional parameters +except \fB$0\|.\fP +A typical use of this is to provide +some default arguments, +as in, +.DS + nroff \(miT450 \(mims $\*(ST +.DE +which simply prepends some arguments +to those already given. +(The variable \fB$@\fP also expands to ``all positional +parameters'', but is subtly different when expanded inside quotes. +See section 3.5, below.) +.SH +2.1\ Control\ flow\ -\ for +.LP +A frequent use of shell scripts is to loop +through the arguments (\fB$1, $2, \*(ZZ\fR) +executing commands once for each argument. +An example of such a script is +\fItel\fP that searches the file +\fB/usr/share/telnos\fR +that contains lines of the form +.DS + \*(ZZ + fred mh0123 + bert mh0789 + \*(ZZ +.DE +The text of \fItel\fP is +.DS + #!/bin/sh + + for i + do + grep $i /usr/share/telnos + done +.DE +The command +.DS + tel fred +.DE +prints those lines in \fB/usr/share/telnos\fR +that contain the string \fIfred\|.\fP +.DS + tel fred bert +.DE +prints those lines containing \fIfred\fP +followed by those for \fIbert.\fP +.LP +The \fBfor\fP loop notation is recognized by the shell +and has the general form +.DS + \fBfor\fR \fIname\fR \fBin\fR \fIw1 w2 \*(ZZ\fR + \fBdo\fR \fIcommand-list\fR + \fBdone\fR +.DE +A \fIcommand-list\fP is a sequence of one or more +simple commands separated or terminated by a newline or semicolon. +Furthermore, reserved words +like \fBdo\fP and \fBdone\fP are only +recognized following a newline or +semicolon. +\fIname\fP is a shell variable that is set +to the words \fIw1 w2 \*(ZZ\fR in turn each time the \fIcommand-list\fP +following \fBdo\fP +is executed. +If \fBin\fR \fIw1 w2 \*(ZZ\fR +is omitted then the loop +is executed once for each positional parameter; +that is, \fBin\fR \fI$\*(ST\fR is assumed. +.LP +Another example of the use of the \fBfor\fP +loop is the \fIcreate\fP command +whose text is +.DS + for i do >$i; done +.DE +The command +.DS + create alpha beta +.DE +ensures that two empty files +\fIalpha\fP and \fIbeta\fP exist +and are empty. +The notation \fI>file\fP may be used on its +own to create or clear the contents of a file. +Notice also that a semicolon (or newline) is required before \fBdone.\fP +.SH +2.2\ Control\ flow\ -\ case +.LP +A multiple way branch is provided for by the +\fBcase\fP notation. +For example, +.DS + case $# in + \*(Ca1) cat \*(AP$1 ;; + \*(Ca2) cat \*(AP$2 <$1 ;; + \*(Ca\*(ST) echo \'usage: append [ from ] to\' ;; + esac +.DE +is an \fIappend\fP command. +When called +with one argument as +.DS + append file +.DE +\fB$#\fP is the string \fI1\fP and +the standard input is copied onto the +end of \fIfile\fP +using the \fIcat\fP command. +.DS + append file1 file2 +.DE +appends the contents of \fIfile1\fP +onto \fIfile2.\fP +If the number of arguments supplied to +\fIappend\fP is other than 1 or 2 +then a message is printed indicating +proper usage. +.LP +The general form of the \fBcase\fP command +is +.DS + \fBcase \fIword \fBin + \*(Ca\fIpattern\|\fB)\ \fIcommand-list\fB\|;; + \*(Ca\*(ZZ + \fBesac\fR +.DE +The shell attempts to match +\fIword\fR with each \fIpattern,\fR +in the order in which the patterns +appear. +If a match is found the +associated \fIcommand-list\fP is +executed and execution +of the \fBcase\fP is complete. +Since \*(ST is the pattern that matches any +string it can be used for the default case. +.LP +A word of caution: +no check is made to ensure that only +one pattern matches +the case argument. +The first match found defines the set of commands +to be executed. +In the example below the commands following +the second \*(ST will never be executed. +.DS + case $# in + \*(Ca\*(ST) \*(ZZ ;; + \*(Ca\*(ST) \*(ZZ ;; + esac +.DE +.LP +Another example of the use of the \fBcase\fP +construction is to distinguish +between different forms +of an argument. +The following example is a fragment of a \fIcc\fP command. +.DS + for i + do case $i in + \*(DC\(mi[ocs]) \*(ZZ ;; + \*(DC\(mi\*(ST) echo "unknown flag $i" ;; + \*(DC\*(ST.c) /lib/c0 $i \*(ZZ ;; + \*(DC\*(ST) echo "unexpected argument $i" ;; + \*(DOesac + done +.DE +.LP +To allow the same commands to be associated +with more than one pattern +the \fBcase\fP command provides +for alternative patterns +separated by a \*(VT\|. +For example, +.DS + case $i in + \*(Ca\(mix\*(VT\(miy) \*(ZZ + esac +.DE +is equivalent to +.DS + case $i in + \*(Ca\(mi[xy]) \*(ZZ + esac +.DE +.LP +The usual quoting conventions apply +so that +.DS + case $i in + \*(Ca\\?) \*(ZZ +.DE +will match the character \fB?\|.\fP +.SH +2.3\ Here\ documents +.LP +The shell script \fItel\fP +in section 2.1 uses the file \fB/usr/share/telnos\fR +to supply the data +for \fIgrep.\fP +An alternative is to include this +data +within the shell script as a \fIhere\fP document, as in, +.DS + for i + do grep $i \*(HE! + \*(DO\*(ZZ + \*(DOfred mh0123 + \*(DObert mh0789 + \*(DO\*(ZZ + ! + done +.DE +In this example +the shell takes the lines between \fB\*(HE!\fR and \fB!\fR +as the standard input for \fIgrep.\fP +The string \fB!\fR is arbitrary, the document +being terminated by a line that consists +of the string following \*(HE\|. +.LP +Parameters are substituted in the document +before it is made available to \fIgrep\fP +as illustrated by the following script +called \fIedg\|.\fP +.DS + ed $3 \*(HE% + g/$1/s//$2/g + w + % +.DE +The call +.DS + edg string1 string2 file +.DE +is then equivalent to the command +.DS + ed file \*(HE% + g/string1/s//string2/g + w + % +.DE +and changes all occurrences of \fIstring1\fP +in \fIfile\fP to \fIstring2\|.\fP +Substitution can be prevented using \\ +to quote the special character \fB$\fP +as in +.DS + ed $3 \*(HE+ + 1,\\$s/$1/$2/g + w + + +.DE +(This version of \fIedg\fP is equivalent to +the first except that \fIed\fP will print +a \fB?\fR if there are no occurrences of +the string \fB$1\|.\fP) +Substitution within a \fIhere\fP document +may be prevented entirely by quoting +the terminating string, +for example, +.DS + grep $i \*(HE'end' + \*(ZZ + end +.DE +The document is presented +without modification to \fIgrep.\fP +If parameter substitution is not required +in a \fIhere\fP document this latter form +is more efficient. +.SH +2.4\ Shell\ variables\(dg +.LP +.FS +Also known as \fIenvironment variables\fB, see \fIenvironment\fB(7). +.FE +The shell +provides string-valued variables. +Variable names begin with a letter +and consist of letters, digits and +underscores. +Variables may be given values by writing, for example, +.DS + user=fred\ box=m000\ acct=mh0000 +.DE +which assigns values to the variables +\fBuser, box\fP and \fBacct.\fP +A variable may be set to the null string +by saying, for example, +.DS + null= +.DE +The value of a variable is substituted +by preceding its name with \fB$\|\fP; +for example, +.DS + echo $user +.DE +will echo \fIfred.\fP +.LP +Variables may be used interactively +to provide abbreviations for frequently +used strings. +For example, +.DS + b=/usr/fred/bin + mv pgm $b +.DE +will move the file \fIpgm\fP +from the current directory to the directory \fB/usr/fred/bin\|.\fR +A more general notation is available for parameter +(or variable) +substitution, as in, +.DS + echo ${user} +.DE +which is equivalent to +.DS + echo $user +.DE +and is used when the parameter name is +followed by a letter or digit. +For example, +.DS + tmp=/tmp/ps + ps a >${tmp}a +.DE +will direct the output of \fIps\fR +to the file \fB/tmp/psa,\fR +whereas, +.DS + ps a >$tmpa +.DE +would cause the value of the variable \fBtmpa\fP +to be substituted. +.LP +Except for \fB$?\fP the following +are set initially by the shell. +\fB$?\fP is set after executing each command. +.RS +.IP \fB$?\fP 8 +The exit status (return code) +of the last command executed +as a decimal string. +Most commands return a zero exit status +if they complete successfully, +otherwise a non-zero exit status is returned. +Testing the value of return codes is dealt with +later under \fBif\fP and \fBwhile\fP commands. +.IP \fB$#\fP 8 +The number of positional parameters +(in decimal). +Used, for example, in the \fIappend\fP command +to check the number of parameters. +.IP \fB$$\fP 8 +The process number of this shell (in decimal). +Since process numbers are unique among +all existing processes, this string is +frequently used to generate +unique +temporary file names. +For example, +.DS + ps a >/tmp/ps$$ + \*(ZZ + rm /tmp/ps$$ +.DE +.IP \fB$\|!\fP 8 +The process number of the last process +run in the background (in decimal). +.IP \fB$\(mi\fP 8 +The current shell flags, such as +\fB\(mix\fR and \fB\(miv\|.\fR +.RE +.LP +Some variables have a special meaning to the +shell and should be avoided for general +use. +.RS +.IP \fB$\s-1MAIL\s0\fP 8 +When used interactively +the shell looks at the file +specified by this variable +before it issues a prompt. +If the specified file has been modified +since it +was last looked at the shell +prints the message +\fIyou have mail\fP before prompting +for the next command. +This variable is typically set +in the file \fB.profile,\fP +in the user's login directory. +For example, +.DS + \s-1MAIL\s0=/usr/spool/mail/fred +.DE +.IP \fB$\s-1HOME\s0\fP 8 +The default argument +for the \fIcd\fP command. +The current directory is used to resolve +file name references that do not begin with +a \fB/\|,\fR +and is changed using the \fIcd\fP command. +For example, +.DS + cd /usr/fred/bin +.DE +makes the current directory \fB/usr/fred/bin\|.\fR +.DS + cat wn +.DE +will print on the terminal the file \fIwn\fP +in this directory. +The command +\fIcd\fP with no argument +is equivalent to +.DS + cd $\s-1HOME\s0 +.DE +This variable is also typically set in the +the user's login profile. +.IP \fB$\s-1PWD\s0\fP 8 +The current working directory. Set by the \fIcd\fB command. +.IP \fB$\s-1PATH\s0\fP 8 +A list of directories that contain commands (the \fIsearch path\fR\|). +Each time a command is executed by the shell +a list of directories is searched +for an executable file. +.ne 5 +If \fB$\s-1PATH\s0\fP is not set +then the current directory, +\fB/bin\fP, and \fB/usr/bin\fP are searched by default. +.ne 5 +Otherwise \fB$\s-1PATH\s0\fP consists of directory +names separated by \fB:\|.\fP +For example, +.DS + \s-1PATH\s0=\fB:\fP/usr/fred/bin\fB:\fP/bin\fB:\fP/usr/bin +.DE +specifies that the current directory +(the null string before the first \fB:\fP\|), +\fB/usr/fred/bin, /bin \fRand\fP /usr/bin\fR +are to be searched in that order. +In this way individual users +can have their own `private' commands +that are accessible independently +of the current directory. +If the command name contains a \fB/\fR then this directory search +is not used; a single attempt +is made to execute the command. +.IP \fB$\s-1PS1\s0\fP 8 +The primary shell prompt string, by default, `\fB$\ \fR'. +.IP \fB$\s-1PS2\s0\fP 8 +The shell prompt when further input is needed, +by default, `\fB>\ \fR'. +.IP \fB$\s-1IFS\s0\fP 8 +The set of characters used by \fIblank +interpretation\fR (see section 3.5). +.IP \fB$\s-1ENV\s0\fP 8 +The shell reads and executes the commands in the file +specified by this variable when it is initially started. +Unlike the \fB.profile\fP file, these commands are executed by all +shells, not just the one started at login. +(Most versions of the shell specify a filename that is used if +\s-1ENV\s0 is not explicitly set. See the manual page for your shell.) +.RE +.SH +2.5\ The\ test\ command +.LP +The \fItest\fP command, although not part of the shell, +is intended for use by shell programs. +For example, +.DS + test \(mif file +.DE +returns zero exit status if \fIfile\fP +exists and non-zero exit status otherwise. +In general \fItest\fP evaluates a predicate +and returns the result as its exit status. +Some of the more frequently used \fItest\fP +arguments are given here, see \fItest\fP(1) +for a complete specification. +.DS + test s true if the argument \fIs\fP is not the null string + test \(mif file true if \fIfile\fP exists + test \(mir file true if \fIfile\fP is readable + test \(miw file true if \fIfile\fP is writable + test \(mid file true if \fIfile\fP is a directory +.DE +The \fItest\fP command is known as `\fB[\fP' and may be invoked as +such. +For aesthetic reasons, the command ignores a close bracket `\fB]\fP' given +at the end of a command so +.DS + [ -f filename ] +.DE +and +.DS + test -f filename +.DE +are completely equivalent. +Typically, the bracket notation is used when \fItest\fP is invoked inside +shell control constructs. +.SH +2.6\ Control\ flow\ -\ while +.LP +The actions of +the \fBfor\fP loop and the \fBcase\fP +branch are determined by data available to the shell. +A \fBwhile\fP or \fBuntil\fP loop +and an \fBif then else\fP branch +are also provided whose +actions are determined by the exit status +returned by commands. +A \fBwhile\fP loop has the general form +.DS + \fBwhile\fP \fIcommand-list\*1\fP + \fBdo\fP \fIcommand-list\*2\fP + \fBdone\fP +.DE +.LP +The value tested by the \fBwhile\fP command +is the exit status of the last simple command +following \fBwhile.\fP +Each time round the loop +\fIcommand-list\*1\fP is executed; +if a zero exit status is returned then +\fIcommand-list\*2\fP +is executed; +otherwise, the loop terminates. +For example, +.DS + while [ $1 ] + do \*(ZZ + \*(DOshift + done +.DE +is equivalent to +.DS + for i + do \*(ZZ + done +.DE +\fIshift\fP is a shell command that +renames the positional parameters +\fB$2, $3, \*(ZZ\fR as \fB$1, $2, \*(ZZ\fR +and loses \fB$1\|.\fP +.LP +Another kind of use for the \fBwhile/until\fP +loop is to wait until some +external event occurs and then run +some commands. +In an \fBuntil\fP loop +the termination condition is reversed. +For example, +.DS + until [ \(mif file ] + do sleep 300; done + \fIcommands\fP +.DE +will loop until \fIfile\fP exists. +Each time round the loop it waits for +5 minutes before trying again. +(Presumably another process +will eventually create the file.) +.LP +The most recent enclosing loop may be exited with the \fBbreak\fP +command, or the rest of the body skipped and the next iteration begun +with the \fBcontinue\fP command. +.LP +The commands \fItrue\fP(1) and \fIfalse\fP(1) return 0 and non-zero +exit statuses respectively. They are sometimes of use in control flow, +e.g.: +.DS + while true + do date; sleep 5 + done +.DE +is an infinite loop that prints the date and time every five seconds. +.SH +2.7\ Control\ flow\ -\ if +.LP +Also available is a +general conditional branch +of the form, +.DS + \fBif\fP \fIcommand-list + \fBthen \fIcommand-list + \fBelse \fIcommand-list + \fBfi\fR +.DE +that tests the value returned by the last simple command +following \fBif.\fP +.LP +The \fBif\fP command may be used +in conjunction with the \fItest\fP command +to test for the existence of a file as in +.DS + if [ \(mif file ] + then \fIprocess file\fP + else \fIdo something else\fP + fi +.DE +.LP +An example of the use of \fBif, case\fP +and \fBfor\fP constructions is given in +section 2.10\|. +.LP +A multiple test \fBif\fP command +of the form +.DS + if \*(ZZ + then \*(ZZ + else if \*(ZZ + then \*(ZZ + else if \*(ZZ + \*(ZZ + fi + fi + fi +.DE +may be written using an extension of the \fBif\fP +notation as, +.DS + if \*(ZZ + then \*(ZZ + elif \*(ZZ + then \*(ZZ + elif \*(ZZ + \*(ZZ + fi +.DE +.LP +The following example is an implementation of the \fItouch\fP command +which changes the `last modified' time for a list +of files. +The command may be used in conjunction +with \fImake\fP(1) to force recompilation of a list +of files. +.DS + #!/bin/sh + + flag= + for i + do case $i in + \*(DC\(mic) flag=N ;; + \*(DC\*(ST) if [ \(mif $i ] + \*(DC then cp $i junk$$; mv junk$$ $i + \*(DC elif [ $flag ] + \*(DC then echo file \\'$i\\' does not exist + \*(DC else >$i + \*(DC fi + \*(DO esac + done +.DE +The \fB\(mic\fP flag is used in this command to +force subsequent files to be created if they do not already exist. +Otherwise, if the file does not exist, an error message is printed. +The shell variable \fIflag\fP +is set to some non-null string if the \fB\(mic\fP +argument is encountered. +The commands +.DS + cp \*(ZZ; mv \*(ZZ +.DE +copy the file and then overwrite it with the copy, +thus causing the last modified date to be updated. +.LP +The sequence +.DS + if command1 + then command2 + fi +.DE +may be written +.DS + command1 && command2 +.DE +Conversely, +.DS + command1 \*(VT\*(VT command2 +.DE +executes \fIcommand2\fP only if \fIcommand1\fP +fails. +In each case the value returned +is that of the last simple command executed. +.LP +Placing a `\fB!\fP' in front of a pipeline inverts its exit +status, almost in the manner of the C operator of the same name. +Thus: +.DS + if ! [ -d $1 ] + then + echo $1 is not a directory + fi +.DE +will print a message only if $1 is not a directory. +.SH +2.8\ Command\ grouping +.LP +Commands may be grouped in two ways, +.DS + \fB{\fI command-list\fB ; }\fR +.DE +and +.DS + \fB(\fI command-list\fB )\fR +.DE +.LP +In the first \fIcommand-list\fP is simply executed. +The second form executes \fIcommand-list\fP +as a separate process. +For example, +.DS + (cd x; rm junk ) +.DE +executes \fIrm junk\fP in the directory +\fBx\fP without changing the current +directory of the invoking shell. +.LP +The commands +.DS + cd x; rm junk +.DE +have the same effect but leave the invoking +shell in the directory \fBx.\fP +.SH +2.9\ Shell\ Functions +.LP +A function may be defined by the syntax +.DS + \fIfuncname\fP() \fB{\fI command-list\fB ; }\fR +.DE +Functions are invoked within a script as though they were separate +commands of the same name. +While they are executed, the +positional parameters \fB$1, $2, \*(ZZ\fR are temporarily set to the +arguments passed to the function. For example: +.DS + count() { + echo $2 : $# + } + + count a b c +.DE +would print `b : 3'. +.SH +2.10\ Debugging\ shell\ scripts +.LP +The shell provides two tracing mechanisms +to help when debugging shell scripts. +The first is invoked within the script +as +.DS + set \(miv +.DE +(\fBv\fP for verbose) and causes lines of the +script to be printed as they are read. +It is useful to help isolate syntax errors. +It may be invoked without modifying the script +by saying +.DS + sh \(miv \fIscript\fP \*(ZZ +.DE +where \fIscript\fP is the name of the shell script. +This flag may be used in conjunction +with the \fB\(min\fP flag which prevents +execution of subsequent commands. +(Note that saying \fIset \(min\fP at a terminal +will render the terminal useless +until an end-of-file is typed.) +.LP +The command +.DS + set \(mix +.DE +will produce an execution +trace. +Following parameter substitution +each command is printed as it is executed. +(Try these at the terminal to see +what effect they have.) +Both flags may be turned off by saying +.DS + set \(mi +.DE +and the current setting of the shell flags is available as \fB$\(mi\|\fR. +.SH +2.11\ The\ man\ command +.LP +The following is a simple implementation of the \fIman\fP command, +which is used to display sections of the UNIX manual on your terminal. +It is called, for example, as +.DS + man sh + man \(mit ed + man 2 fork +.DE +In the first the manual section for \fIsh\fP +is displayed.. +Since no section is specified, section 1 is used. +The second example will typeset (\fB\(mit\fP option) +the manual section for \fIed.\fP +The last prints the \fIfork\fP manual page +from section 2, which covers system calls. +.sp 2 +.DS + #!/bin/sh + + cd /usr/share/man + + # "#" is the comment character + # default is nroff ($N), section 1 ($s) + N=n\ s=1 + + for i + do case $i in +.sp .5 + \*(DC[1\(mi9]\*(ST) s=$i ;; +.sp .5 + \*(DC\(mit) N=t ;; +.sp .5 + \*(DC\(min) N=n ;; +.sp .5 + \*(DC\(mi\*(ST) echo unknown flag \\'$i\\' ;; +.sp .5 + \*(DC\*(ST) if [ \(mif man$s/$i.$s ] + \*(DC then + \*(DC ${N}roff \(miman man$s/$i.$s + \*(DC else # look through all manual sections + \*(DC found=no + \*(DC for j in 1 2 3 4 5 6 7 8 9 + \*(DC do + \*(DC \*(DOif [ \(mif man$j/$i.$j ] + \*(DC \*(DOthen + \*(DC \*(DO\*(THman $j $i + \*(DC \*(DO\*(THfound=yes + \*(DC \*(DO\*(THbreak + \*(DC \*(DOfi + \*(DC done + \*(DC case $found in + \*(DC \*(Cano) echo \\'$i: manual page not found\\' + \*(DC esac + \*(DC fi + \*(DOesac + done +.DE +.ce +.ft B +Figure 1. A version of the man command +.ft R diff --git a/bin/sh/USD.doc/t3 b/bin/sh/USD.doc/t3 new file mode 100644 index 000000000..aab53ee11 --- /dev/null +++ b/bin/sh/USD.doc/t3 @@ -0,0 +1,976 @@ +.\" $NetBSD: t3,v 1.3 2010/08/22 02:19:07 perry Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are +.\" met: +.\" +.\" Redistributions of source code and documentation must retain the above +.\" copyright notice, this list of conditions and the following +.\" disclaimer. +.\" +.\" 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. +.\" +.\" All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" +.\" This product includes software developed or owned by Caldera +.\" International, Inc. Neither the name of Caldera International, Inc. +.\" nor the names of other contributors may be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" @(#)t3 8.1 (Berkeley) 6/8/93 +.\" +.SH +3.0\ Keyword\ parameters +.LP +Shell variables may be given values +by assignment +or when a shell script is invoked. +An argument to a command of the form +\fIname=value\fP +that precedes the command name +causes \fIvalue\fP +to be assigned to \fIname\fP +before execution of the command begins. +The value of \fIname\fP in the invoking +shell is not affected. +For example, +.DS + user=fred\ command +.DE +will execute \fIcommand\fP with +\fBuser\fP set to \fIfred\fP. +.\" Removed by Perry Metzger because -k is not in POSIX +.\" +.\" The \fB\(mik\fR flag causes arguments of the form +.\" \fIname=value\fP to be interpreted in this way +.\" anywhere in the argument list. +.\" Such \fInames\fP are sometimes +.\" called keyword parameters. +.\" If any arguments remain they +.\" are available as positional +.\" parameters \fB$1, $2, \*(ZZ\|.\fP +.LP +The \fIset\fP command +may also be used to set positional parameters +from within a script. +For example, +.DS + set\ \(mi\(mi\ \*(ST +.DE +will set \fB$1\fP to the first file name +in the current directory, \fB$2\fP to the next, +and so on. +Note that the first argument, \(mi\(mi, ensures correct treatment +when the first file name begins with a \(mi\|. +.LP +.SH +3.1\ Parameter\ transmission +.LP +When a command is executed both positional parameters +and shell variables may be set on invocation. +Variables are also made available implicitly +to a command +by specifying in advance that such parameters +are to be exported from the invoking shell. +For example, +.DS + export\ user\ box=red +.DE +marks the variables \fBuser\fP and \fBbox\fP +for export (setting \fBbox\fP to ``red'' in the process). +When a command is invoked +copies are made of all exportable variables +(also known as \fIenvironment variables\fP) +for use within the invoked program. +Modification of such variables +within an invoked command does not +affect the values in the invoking shell. +It is generally true of +a shell script or other program +that it +cannot modify the state +of its caller without explicit +actions on the part of the caller. +.\" Removed by Perry Metzger because this is confusing to beginners. +.\" +.\" (Shared file descriptors are an +.\" exception to this rule.) +.LP +Names whose value is intended to remain +constant may be declared \fIreadonly\|.\fP +The form of this command is the same as that of the \fIexport\fP +command, +.DS + readonly name[=value] \*(ZZ +.DE +Subsequent attempts to set readonly variables +are illegal. +.SH +3.2\ Parameter\ substitution +.LP +If a shell parameter is not set +then the null string is substituted for it. +For example, if the variable \fBd\fP +is not set +.DS + echo $d +.DE +or +.DS + echo ${d} +.DE +will echo nothing. +A default string may be given +as in +.DS + echo ${d:\(mi\fB.\fR} +.DE +which will echo +the value of the variable \fBd\fP +if it is set and not null and `\fB.\fP' otherwise. +The default string is evaluated using the usual +quoting conventions so that +.DS + echo ${d:\(mi\'\*(ST\'} +.DE +will echo \fB\*(ST\fP if the variable \fBd\fP +is not set or null. +Similarly +.DS + echo ${d:\(mi$1} +.DE +will echo the value of \fBd\fP if it is set and not null +and the value (if any) of \fB$1\fP otherwise. +.LP +The notation ${d:+\fB.\fR} performs the inverse operation. It +substitutes `\fB.\fP' if \fBd\fP is set or not null, and otherwise +substitutes null. +.LP +A variable may be assigned a default value +using +the notation +.DS + echo ${d:=\fB.\fR} +.DE +which substitutes the same string as +.DS + echo ${d:\(mi\fB.\fR} +.DE +and if \fBd\fP were not previously set or null +then it will be set to the string `\fB.\fP'\|. +.LP +If there is no sensible default then +the notation +.DS + echo ${d:?\fImessage\fP} +.DE +will echo the value of the variable \fBd\fP if it is set and not null, +otherwise \fImessage\fP is printed by the shell and +execution of the shell script is abandoned. +If \fImessage\fP is absent then a standard message +is printed. +A shell script that requires some variables +to be set might start as follows: +.DS + :\ ${user:?}\ ${acct:?}\ ${bin:?} + \*(ZZ +.DE +Colon (\fB:\fP) is a command +that is +built in to the shell and does nothing +once its arguments have been evaluated. +If any of the variables \fBuser, acct\fP +or \fBbin\fP are not set then the shell +will abandon execution of the script. +.SH +3.3\ Command\ substitution +.LP +The standard output from a command can be +substituted in a similar way to parameters. +The command \fIpwd\fP prints on its standard +output the name of the current directory. +For example, if the current directory is +\fB/usr/fred/bin\fR +then the commands +.DS + d=$(pwd) +.DE +(or the older notation d=\`pwd\`) +is equivalent to +.DS + d=/usr/fred/bin +.DE +.LP +The entire string inside $(\*(ZZ)\| (or between grave accents \`\*(ZZ\`) +is taken as the command +to be executed +and is replaced with the output from +the command. +(The difference between the $(\*(ZZ) and \`\*(ZZ\` notations is that +the former may be nested, while the latter cannot be.) +.LP +The command is written using the usual quoting conventions, +except that inside \`\*(ZZ\` +a \fB\`\fR must be escaped using +a \fB\\\|\fR. +For example, +.DS + ls $(echo "$HOME") +.DE +is equivalent to +.DS + ls $HOME +.DE +Command substitution occurs in all contexts +where parameter substitution occurs (including \fIhere\fP documents) and the +treatment of the resulting text is the same +in both cases. +This mechanism allows string +processing commands to be used within +shell scripts. +An example of such a command is \fIbasename\fP +which removes a specified suffix from a string. +For example, +.DS + basename main\fB.\fPc \fB.\fPc +.DE +will print the string \fImain\|.\fP +Its use is illustrated by the following +fragment from a \fIcc\fP command. +.DS + case $A in + \*(Ca\*(ZZ + \*(Ca\*(ST\fB.\fPc) B=$(basename $A \fB.\fPc) + \*(Ca\*(ZZ + esac +.DE +that sets \fBB\fP to the part of \fB$A\fP +with the suffix \fB.c\fP stripped. +.LP +Here are some composite examples. +.RS +.IP \(bu +.ft B +for i in \`ls \(mit\`; do \*(ZZ +.ft R +.br +The variable \fBi\fP is set +to the names of files in time order, +most recent first. +.IP \(bu +.ft B +set \(mi\(mi\| \`date\`; echo $6 $2 $3, $4 +.ft R +.br +will print, e.g., +.ft I +1977 Nov 1, 23:59:59 +.ft R +.RE +.SH +3.4\ Arithmetic\ Expansion +.LP +Within a $((\*(ZZ)) construct, integer arithmetic operations are +evaluated. +(The $ in front of variable names is optional within $((\*(ZZ)). +For example: +.DS + x=5; y=1 + echo $(($x+3*2)) + echo $((y+=x)) + echo $y +.DE +will print `11', then `6', then `6' again. +Most of the constructs permitted in C arithmetic operations are +permitted though some (like `++') are not universally supported \(em +see the shell manual page for details. +.SH +3.5\ Evaluation\ and\ quoting +.LP +The shell is a macro processor that +provides parameter substitution, command substitution and file +name generation for the arguments to commands. +This section discusses the order in which +these evaluations occur and the +effects of the various quoting mechanisms. +.LP +Commands are parsed initially according to the grammar +given in appendix A. +Before a command is executed +the following +substitutions occur. +.RS +.IP \(bu +parameter substitution, e.g. \fB$user\fP +.IP \(bu +command substitution, e.g. \fB$(pwd)\fP or \fB\`pwd\`\fP +.IP \(bu +arithmetic expansion, e.g. \fB$(($count+1))\fP +.RS +.LP +Only one evaluation occurs so that if, for example, the value of the variable +\fBX\fP +is the string \fI$y\fP +then +.DS + echo $X +.DE +will echo \fI$y\|.\fP +.RE +.IP \(bu +blank interpretation +.RS +.LP +Following the above substitutions +the resulting characters +are broken into non-blank words (\fIblank interpretation\fP). +For this purpose `blanks' are the characters of the string +\fB$\s-1IFS\s0\fP. +By default, this string consists of blank, tab and newline. +The null string +is not regarded as a word unless it is quoted. +For example, +.DS + echo \'\' +.DE +will pass on the null string as the first argument to \fIecho\fP, +whereas +.DS + echo $null +.DE +will call \fIecho\fR with no arguments +if the variable \fBnull\fP is not set +or set to the null string. +.RE +.IP \(bu +file name generation +.RS +.LP +Each word +is then scanned for the file pattern characters +\fB\*(ST, ?\fR and \fB[\*(ZZ]\fR +and an alphabetical list of file names +is generated to replace the word. +Each such file name is a separate argument. +.RE +.RE +.LP +The evaluations just described also occur +in the list of words associated with a \fBfor\fP +loop. +Only substitution occurs +in the \fIword\fP used +for a \fBcase\fP branch. +.LP +As well as the quoting mechanisms described +earlier using \fB\\\fR and \fB\'\*(ZZ\'\fR +a third quoting mechanism is provided using double quotes. +Within double quotes parameter and command substitution +occurs but file name generation and the interpretation +of blanks does not. +The following characters +have a special meaning within double quotes +and may be quoted using \fB\\\|.\fP +.DS + \fB$ \fPparameter substitution + \fB$()\fP command substitution + \fB\`\fP command substitution + \fB"\fP ends the quoted string + \fB\e\fP quotes the special characters \fB$ \` " \e\fP +.DE +For example, +.DS + echo "$x" +.DE +will pass the value of the variable \fBx\fP as a +single argument to \fIecho.\fP +Similarly, +.DS + echo "$\*(ST" +.DE +will pass the positional parameters as a single +argument and is equivalent to +.DS + echo "$1 $2 \*(ZZ" +.DE +The notation \fB$@\fP +is the same as \fB$\*(ST\fR +except when it is quoted. +.DS + echo "$@" +.DE +will pass the positional parameters, unevaluated, to \fIecho\fR +and is equivalent to +.DS + echo "$1" "$2" \*(ZZ +.DE +.LP +The following table gives, for each quoting mechanism, +the shell metacharacters that are evaluated. +.DS +.ce +.ft I +metacharacter +.ft +.in 1.5i + \e $ * \` " \' +\' n n n n n t +\` y n n t n n +" y y n y t n + + t terminator + y interpreted + n not interpreted + +.in +.ft B +.ce +Figure 2. Quoting mechanisms +.ft +.DE +.LP +In cases where more than one evaluation of a string +is required the built-in command \fIeval\fP +may be used. +For example, +if the variable \fBX\fP has the value +\fI$y\fP, and if \fBy\fP has the value \fIpqr\fP +then +.DS + eval echo $X +.DE +will echo the string \fIpqr\|.\fP +.LP +In general the \fIeval\fP command +evaluates its arguments (as do all commands) +and treats the result as input to the shell. +The input is read and the resulting command(s) +executed. +For example, +.DS + wg=\'eval who\*(VTgrep\' + $wg fred +.DE +is equivalent to +.DS + who\*(VTgrep fred +.DE +In this example, +\fIeval\fP is required +since there is no interpretation +of metacharacters, such as \fB\*(VT\|\fR, following +substitution. +.SH +3.6\ Error\ handling +.LP +The treatment of errors detected by +the shell depends on the type of error +and on whether the shell is being +used interactively. +An interactive shell is one whose +input and output are connected +to a terminal. +.\" Removed by Perry Metzger, obsolete and excess detail +.\" +.\" (as determined by +.\" \fIgtty\fP (2)). +A shell invoked with the \fB\(mii\fP +flag is also interactive. +.LP +Execution of a command (see also 3.7) may fail +for any of the following reasons. +.IP \(bu +Input output redirection may fail. +For example, if a file does not exist +or cannot be created. +.IP \(bu +The command itself does not exist +or cannot be executed. +.IP \(bu +The command terminates abnormally, +for example, with a "bus error" +or "memory fault". +See Figure 2 below for a complete list +of UNIX signals. +.IP \(bu +The command terminates normally +but returns a non-zero exit status. +.LP +In all of these cases the shell +will go on to execute the next command. +Except for the last case an error +message will be printed by the shell. +All remaining errors cause the shell +to exit from a script. +An interactive shell will return +to read another command from the terminal. +Such errors include the following. +.IP \(bu +Syntax errors. +e.g., if \*(ZZ then \*(ZZ done +.IP \(bu +A signal such as interrupt. +The shell waits for the current +command, if any, to finish execution and +then either exits or returns to the terminal. +.IP \(bu +Failure of any of the built-in commands +such as \fIcd.\fP +.LP +The shell flag \fB\(mie\fP +causes the shell to terminate +if any error is detected. +.DS +1 hangup +2 interrupt +3* quit +4* illegal instruction +5* trace trap +6* IOT instruction +7* EMT instruction +8* floating point exception +9 kill (cannot be caught or ignored) +10* bus error +11* segmentation violation +12* bad argument to system call +13 write on a pipe with no one to read it +14 alarm clock +15 software termination (from \fIkill\fP (1)) + +.DE +.ft B +.ce +Figure 3. UNIX signals\(dg +.ft +.FS +\(dg Additional signals have been added in modern Unix. +See \fIsigvec\fP(2) or \fIsignal\fP(3) for an up-to-date list. +.FE +Those signals marked with an asterisk +produce a core dump +if not caught. +However, +the shell itself ignores quit which is the only +external signal that can cause a dump. +The signals in this list of potential interest +to shell programs are 1, 2, 3, 14 and 15. +.SH +3.7\ Fault\ handling +.LP +shell scripts normally terminate +when an interrupt is received from the +terminal. +The \fItrap\fP command is used +if some cleaning up is required, such +as removing temporary files. +For example, +.DS + trap\ \'rm\ /tmp/ps$$; exit\'\ 2 +.DE +sets a trap for signal 2 (terminal +interrupt), and if this signal is received +will execute the commands +.DS + rm /tmp/ps$$; exit +.DE +\fIexit\fP is +another built-in command +that terminates execution of a shell script. +The \fIexit\fP is required; otherwise, +after the trap has been taken, +the shell will resume executing +the script +at the place where it was interrupted. +.LP +UNIX signals can be handled in one of three ways. +They can be ignored, in which case +the signal is never sent to the process. +They can be caught, in which case the process +must decide what action to take when the +signal is received. +Lastly, they can be left to cause +termination of the process without +it having to take any further action. +If a signal is being ignored +on entry to the shell script, for example, +by invoking it in the background (see 3.7) then \fItrap\fP +commands (and the signal) are ignored. +.LP +The use of \fItrap\fP is illustrated +by this modified version of the \fItouch\fP +command (Figure 4). +The cleanup action is to remove the file \fBjunk$$\fR\|. +.DS + #!/bin/sh + + flag= + trap\ \'rm\ \(mif\ junk$$;\ exit\'\ 1 2 3 15 + for i + do\ case\ $i\ in + \*(DC\(mic) flag=N ;; + \*(DC\*(ST) if\ test\ \(mif\ $i + \*(DC then cp\ $i\ junk$$;\ mv\ junk$$ $i + \*(DC elif\ test\ $flag + \*(DC then echo\ file\ \\'$i\\'\ does\ not\ exist + \*(DC else >$i + \*(DC fi + \*(DOesac + done +.DE +.sp +.ft B +.ce +Figure 4. The touch command +.ft +.sp +The \fItrap\fP command +appears before the creation +of the temporary file; +otherwise it would be +possible for the process +to die without removing +the file. +.LP +Since there is no signal 0 in UNIX +it is used by the shell to indicate the +commands to be executed on exit from the +shell script. +.LP +A script may, itself, elect to +ignore signals by specifying the null +string as the argument to trap. +The following fragment is taken from the +\fInohup\fP command. +.DS + trap \'\' 1 2 3 15 +.DE +which causes \fIhangup, interrupt, quit \fRand\fI kill\fR +to be ignored both by the +script and by invoked commands. +.LP +Traps may be reset by saying +.DS + trap 2 3 +.DE +which resets the traps for signals 2 and 3 to their default values. +A list of the current values of traps may be obtained +by writing +.DS + trap +.DE +.LP +The script \fIscan\fP (Figure 5) is an example +of the use of \fItrap\fP where there is no exit +in the trap command. +\fIscan\fP takes each directory +in the current directory, prompts +with its name, and then executes +commands typed at the terminal +until an end of file or an interrupt is received. +Interrupts are ignored while executing +the requested commands but cause +termination when \fIscan\fP is +waiting for input. +.DS + d=\`pwd\` + for\ i\ in\ \*(ST + do\ if\ test\ \(mid\ $d/$i + \*(DOthen\ cd\ $d/$i + \*(DO\*(THwhile\ echo\ "$i:" + \*(DO\*(TH\*(WHtrap\ exit\ 2 + \*(DO\*(TH\*(WHread\ x + \*(DO\*(THdo\ trap\ :\ 2;\ eval\ $x;\ done + \*(DOfi + done +.DE +.sp +.ft B +.ce +Figure 5. The scan command +.ft +.sp +\fIread x\fR is a built-in command that reads one line from the +standard input +and places the result in the variable \fBx\|.\fP +It returns a non-zero exit status if either +an end-of-file is read or an interrupt +is received. +.SH +3.8\ Command\ execution +.LP +To run a command (other than a built-in) the shell first creates +a new process using the system call \fIfork.\fP +The execution environment for the command +includes input, output and the states of signals, and +is established in the child process +before the command is executed. +The built-in command \fIexec\fP +is used in the rare cases when no fork +is required +and simply replaces the shell with a new command. +For example, a simple version of the \fInohup\fP +command looks like +.DS + trap \\'\\' 1 2 3 15 + exec $\*(ST +.DE +The \fItrap\fP turns off the signals specified +so that they are ignored by subsequently created commands +and \fIexec\fP replaces the shell by the command +specified. +.LP +Most forms of input output redirection have already been +described. +In the following \fIword\fP is only subject +to parameter and command substitution. +No file name generation or blank interpretation +takes place so that, for example, +.DS + echo \*(ZZ >\*(ST.c +.DE +will write its output into a file whose name is \fB\*(ST.c\|.\fP +Input output specifications are evaluated left to right +as they appear in the command. +.IP >\ \fIword\fP 12 +The standard output (file descriptor 1) +is sent to the file \fIword\fP which is +created if it does not already exist. +.IP \*(AP\ \fIword\fP 12 +The standard output is sent to file \fIword.\fP +If the file exists then output is appended +(by seeking to the end); +otherwise the file is created. +.IP <\ \fIword\fP 12 +The standard input (file descriptor 0) +is taken from the file \fIword.\fP +.IP \*(HE\ \fIword\fP 12 +The standard input is taken from the lines +of shell input that follow up to but not +including a line consisting only of \fIword.\fP +If \fIword\fP is quoted then no interpretation +of the document occurs. +If \fIword\fP is not quoted +then parameter and command substitution +occur and \fB\\\fP is used to quote +the characters \fB\\\fP \fB$\fP \fB\`\fP and the first character +of \fIword.\fP +In the latter case \fB\\newline\fP is ignored (c.f. quoted strings). +.IP >&\ \fIdigit\fP 12 +The file descriptor \fIdigit\fP is duplicated using the system +call \fIdup\fP (2) +and the result is used as the standard output. +.IP <&\ \fIdigit\fP 12 +The standard input is duplicated from file descriptor \fIdigit.\fP +.IP <&\(mi 12 +The standard input is closed. +.IP >&\(mi 12 +The standard output is closed. +.LP +Any of the above may be preceded by a digit in which +case the file descriptor created is that specified by the digit +instead of the default 0 or 1. +For example, +.DS + \*(ZZ 2>file +.DE +runs a command with message output (file descriptor 2) +directed to \fIfile.\fP +.DS + \*(ZZ 2>&1 +.DE +runs a command with its standard output and message output +merged. +(Strictly speaking file descriptor 2 is created +by duplicating file descriptor 1 but the effect is usually to +merge the two streams.) +.\" Removed by Perry Metzger, most of this is now obsolete +.\" +.\" .LP +.\" The environment for a command run in the background such as +.\" .DS +.\" list \*(ST.c \*(VT lpr & +.\" .DE +.\" is modified in two ways. +.\" Firstly, the default standard input +.\" for such a command is the empty file \fB/dev/null\|.\fR +.\" This prevents two processes (the shell and the command), +.\" which are running in parallel, from trying to +.\" read the same input. +.\" Chaos would ensue +.\" if this were not the case. +.\" For example, +.\" .DS +.\" ed file & +.\" .DE +.\" would allow both the editor and the shell +.\" to read from the same input at the same time. +.\" .LP +.\" The other modification to the environment of a background +.\" command is to turn off the QUIT and INTERRUPT signals +.\" so that they are ignored by the command. +.\" This allows these signals to be used +.\" at the terminal without causing background +.\" commands to terminate. +.\" For this reason the UNIX convention +.\" for a signal is that if it is set to 1 +.\" (ignored) then it is never changed +.\" even for a short time. +.\" Note that the shell command \fItrap\fP +.\" has no effect for an ignored signal. +.SH +3.9\ Invoking\ the\ shell +.LP +The following flags are interpreted by the shell +when it is invoked. +If the first character of argument zero is a minus, +then commands are read from the file \fB.profile\|.\fP +.IP \fB\(mic\fP\ \fIstring\fP +.br +If the \fB\(mic\fP flag is present then +commands are read from \fIstring\|.\fP +.IP \fB\(mis\fP +If the \fB\(mis\fP flag is present or if no +arguments remain +then commands are read from the standard input. +Shell output is written to +file descriptor 2. +.IP \fB\(mii\fP +If the \fB\(mii\fP flag is present or +if the shell input and output are attached to a terminal (as told by \fIgtty\fP) +then this shell is \fIinteractive.\fP +In this case TERMINATE is ignored (so that \fBkill 0\fP +does not kill an interactive shell) and INTERRUPT is caught and ignored +(so that \fBwait\fP is interruptable). +In all cases QUIT is ignored by the shell. +.SH +3.10\ Job\ Control +.LP +When a command or pipeline (also known as a \fIjob\fP) is running in +the foreground, entering the stop character (typically +\s-1CONTROL-Z\s0 but user settable with the \fIstty\fP(1) command) +will usually cause the job to stop. +.LP +The jobs associated with the current shell may be listed by entering +the \fIjobs\fP command. +Each job has an associated \fIjob number\fP. +Jobs that are stopped may be continued by entering +.DS + bg %\fIjobnumber\fP +.DE +and jobs may be moved to the foreground by entering +.DS + fg %\fIjobnumber\fP +.DE +If there is a sole job with a particular name (say only one instance +of \fIcc\fP running), \fIfg\fP and \fIbg\fP may also use name of the +command in place of the number, as in: +.DS + bg %cc +.DE +If no `\fB%\fP' clause is entered, most recently stopped job +(indicated with a `+' by the \fIjobs\fP command) will be assumed. +See the manual page for the shell for more details. +.SH +3.11\ Aliases +.LP +The \fIalias\fP command creates a so-called shell alias, which is an +abbreviation that macro-expands at run time into some other command. +For example: +.DS + alias ls="ls -F" +.DE +would cause the command sequence \fBls -F\fP to be executed whenever +the user types \fBls\fP into the shell. +Note that if the user types \fBls -a\fP, the shell will in fact +execute \fBls -F -a\fP. +The command \fBalias\fP on its own prints out all current aliases. +The \fIunalias\fP command, as in: +.DS + unalias ls +.DE +will remove an existing alias. +Aliases can shadow pre-existing commands, as in the example above. +They are typically used to override the interactive behavior of +commands in small ways, for example to always invoke some program with +a favorite option, and are almost never found in scripts. +.SH +3.12\ Command\ Line\ Editing\ and\ Recall +.LP +When working interactively with the shell, it is often tedious to +retype previously entered commands, especially if they are complicated. +The shell therefore maintains a so-called \fIhistory\fP, which is +stored in the file specified by the \fB\s-1HISTFILE\s0\fP environment +variable if it is set. +Users may view, edit, and re-enter previous lines of input using +a small subset of the commands of the \fIvi\fP(1) or +\fIemacs\fP(1)\(dg editors. +.FS +Technically, vi command editing is standardized by POSIX while emacs +is not. +However, all modern shells support both styles. +.FE +Emacs style editing may be selected by entering +.DS + set -o emacs +.DE +and vi style editing may be selected with +.DS + set -o vi +.DE +The details of how command line editing works are beyond the scope of +this document. +See the shell manual page for details. +.SH +Acknowledgements +.LP +The design of the shell is +based in part on the original UNIX shell +.[ +unix command language thompson +.] +and the PWB/UNIX shell, +.[ +pwb shell mashey unix +.] +some +features having been taken from both. +Similarities also exist with the +command interpreters +of the Cambridge Multiple Access System +.[ +cambridge multiple access system hartley +.] +and of CTSS. +.[ +ctss +.] +.LP +I would like to thank Dennis Ritchie +and John Mashey for many +discussions during the design of the shell. +I am also grateful to the members of the Computing Science Research Center +and to Joe Maranzano for their +comments on drafts of this document. +.SH +.[ +$LIST$ +.] diff --git a/bin/sh/USD.doc/t4 b/bin/sh/USD.doc/t4 new file mode 100644 index 000000000..7719d6c40 --- /dev/null +++ b/bin/sh/USD.doc/t4 @@ -0,0 +1,180 @@ +.\" $NetBSD: t4,v 1.3 2010/08/22 02:19:07 perry Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are +.\" met: +.\" +.\" Redistributions of source code and documentation must retain the above +.\" copyright notice, this list of conditions and the following +.\" disclaimer. +.\" +.\" 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. +.\" +.\" All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" +.\" This product includes software developed or owned by Caldera +.\" International, Inc. Neither the name of Caldera International, Inc. +.\" nor the names of other contributors may be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" @(#)t4 8.1 (Berkeley) 8/14/93 +.\" +.bp +.SH +Appendix\ A\ -\ Grammar +.LP +Note: This grammar needs updating, it is obsolete. +.LP +.LD +\fIitem: word + input-output + name = value +.sp 0.7 +simple-command: item + simple-command item +.sp 0.7 +command: simple-command + \fB( \fIcommand-list \fB) + \fB{ \fIcommand-list \fB} + \fBfor \fIname \fBdo \fIcommand-list \fBdone + \fBfor \fIname \fBin \fIword \*(ZZ \fBdo \fIcommand-list \fBdone + \fBwhile \fIcommand-list \fBdo \fIcommand-list \fBdone + \fBuntil \fIcommand-list \fBdo \fIcommand-list \fBdone + \fBcase \fIword \fBin \fIcase-part \*(ZZ \fBesac + \fBif \fIcommand-list \fBthen \fIcommand-list \fIelse-part \fBfi +.sp 0.7 +\fIpipeline: command + pipeline \fB\*(VT\fI command +.sp 0.7 +andor: pipeline + andor \fB&&\fI pipeline + andor \fB\*(VT\*(VT\fI pipeline +.sp 0.7 +command-list: andor + command-list \fB;\fI + command-list \fB&\fI + command-list \fB;\fI andor + command-list \fB&\fI andor +.sp 0.7 +input-output: \fB> \fIfile + \fB< \fIfile + \fB\*(AP \fIword + \fB\*(HE \fIword +.sp 0.7 +file: word + \fB&\fI digit + \fB&\fI \(mi +.sp 0.7 +case-part: pattern\fB ) \fIcommand-list\fB ;; +.sp 0.7 +\fIpattern: word + pattern \fB\*(VT\fI word +.sp 0.7 +\fIelse-part: \fBelif \fIcommand-list\fB then\fI command-list else-part\fP + \fBelse \fIcommand-list\fI + empty +.sp 0.7 +empty: +.sp 0.7 +word: \fRa sequence of non-blank characters\fI +.sp 0.7 +name: \fRa sequence of letters, digits or underscores starting with a letter\fI +.sp 0.7 +digit: \fB0 1 2 3 4 5 6 7 8 9\fP +.DE +.LP +.bp +.SH +Appendix\ B\ -\ Meta-characters\ and\ Reserved\ Words +.LP +a) syntactic +.RS +.IP \fB\*(VT\fR 6 +pipe symbol +.IP \fB&&\fR 6 +`andf' symbol +.IP \fB\*(VT\*(VT\fR 6 +`orf' symbol +.IP \fB;\fP 8 +command separator +.IP \fB;;\fP 8 +case delimiter +.IP \fB&\fP 8 +background commands +.IP \fB(\ )\fP 8 +command grouping +.IP \fB<\fP 8 +input redirection +.IP \fB\*(HE\fP 8 +input from a here document +.IP \fB>\fP 8 +output creation +.IP \fB\*(AP\fP 8 +output append +.sp 2 +.RE +.LP +b) patterns +.RS +.IP \fB\*(ST\fP 8 +match any character(s) including none +.IP \fB?\fP 8 +match any single character +.IP \fB[...]\fP 8 +match any of the enclosed characters +.sp 2 +.RE +.LP +c) substitution +.RS +.IP \fB${...}\fP 8 +substitute shell variable +.IP \fB$(...)\fP 8 +substitute command output +.IP \fB\`...\`\fP 8 +substitute command output +.IP \fB$((...))\fP 8 +substitute arithmetic expression +.sp 2 +.RE +.LP +d) quoting +.RS +.IP \fB\e\fP 8 +quote the next character +.IP \fB\'...\'\fP 8 +quote the enclosed characters except for \' +.IP \fB"\&..."\fP 8 +quote the enclosed characters except +for \fB$ \` \e "\fP +.sp 2 +.RE +.LP +e) reserved words +.DS +.ft B +if then else elif fi +case in esac +for while until do done +! { } +.ft +.DE diff --git a/minix/commands/ash/alias.c b/bin/sh/alias.c similarity index 86% rename from minix/commands/ash/alias.c rename to bin/sh/alias.c index e584ebcf3..8b638c6ff 100644 --- a/minix/commands/ash/alias.c +++ b/bin/sh/alias.c @@ -1,3 +1,5 @@ +/* $NetBSD: alias.c,v 1.14 2011/06/18 21:18:46 christos Exp $ */ + /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,15 +32,14 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)alias.c 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: alias.c,v 1.14 2011/06/18 21:18:46 christos Exp $"); #endif #endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/alias.c,v 1.18 2004/04/06 20:06:51 markm Exp $"); -*/ #include #include "shell.h" @@ -49,18 +50,20 @@ __FBSDID("$FreeBSD: src/bin/sh/alias.c,v 1.18 2004/04/06 20:06:51 markm Exp $"); #include "mystring.h" #include "alias.h" #include "options.h" /* XXX for argptr (should remove?) */ +#include "builtins.h" +#include "var.h" #define ATABSIZE 39 -STATIC struct alias *atab[ATABSIZE]; +struct alias *atab[ATABSIZE]; -STATIC void setalias(const char *, const char *); -STATIC int unalias(const char *); -STATIC struct alias **hashalias(const char *); +STATIC void setalias(char *, char *); +STATIC int unalias(char *); +STATIC struct alias **hashalias(char *); STATIC void -setalias(const char *name, const char *val) +setalias(char *name, char *val) { struct alias *ap, **app; @@ -78,6 +81,7 @@ setalias(const char *name, const char *val) INTOFF; ap = ckmalloc(sizeof (struct alias)); ap->name = savestr(name); + ap->flag = 0; /* * XXX - HACK: in order that the parser will not finish reading the * alias value off the input before processing the next alias, we @@ -87,7 +91,7 @@ setalias(const char *name, const char *val) * expanding an alias, the value of the alias is pushed back on the * input as a string and a pointer to the alias is stored with the * string. The alias is marked as being in use. When the input - * routine finishes reading the string, it marks the alias not + * routine finishes reading the string, it markes the alias not * in use. The problem is synchronization with the parser. Since * it reads ahead, the alias is marked not in use before the * resulting token(s) is next checked for further alias sub. The @@ -106,14 +110,13 @@ setalias(const char *name, const char *val) ap->val[len+1] = '\0'; } #endif - ap->flag = 0; ap->next = *app; *app = ap; INTON; } STATIC int -unalias(const char *name) +unalias(char *name) { struct alias *ap, **app; @@ -146,7 +149,8 @@ unalias(const char *name) } #ifdef mkinit -INCLUDE "alias.h" +MKINIT void rmaliases(void); + SHELLPROC { rmaliases(); } @@ -174,7 +178,7 @@ rmaliases(void) } struct alias * -lookupalias(const char *name, int check) +lookupalias(char *name, int check) { struct alias *ap = *hashalias(name); @@ -189,6 +193,17 @@ lookupalias(const char *name, int check) return (NULL); } +char * +get_alias_text(char *name) +{ + struct alias *ap; + + ap = lookupalias(name, 0); + if (ap == NULL) + return NULL; + return ap->val; +} + /* * TODO - sort output */ @@ -206,23 +221,23 @@ aliascmd(int argc, char **argv) for (ap = atab[i]; ap; ap = ap->next) { if (*ap->name != '\0') { out1fmt("alias %s=", ap->name); - out1qstr(ap->val); + print_quoted(ap->val); out1c('\n'); } } return (0); } while ((n = *++argv) != NULL) { - if ((v = strchr(n+1, '=')) == NULL) /* n+1: funny ksh stuff */ + if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */ if ((ap = lookupalias(n, 0)) == NULL) { outfmt(out2, "alias: %s not found\n", n); ret = 1; } else { out1fmt("alias %s=", n); - out1qstr(ap->val); + print_quoted(ap->val); out1c('\n'); } - else { + } else { *v++ = '\0'; setalias(n, v); } @@ -232,7 +247,7 @@ aliascmd(int argc, char **argv) } int -unaliascmd(int argc __unused, char **argv __unused) +unaliascmd(int argc, char **argv) { int i; @@ -249,7 +264,7 @@ unaliascmd(int argc __unused, char **argv __unused) } STATIC struct alias ** -hashalias(const char *p) +hashalias(char *p) { unsigned int hashval; @@ -258,7 +273,3 @@ hashalias(const char *p) hashval+= *p++; return &atab[hashval % ATABSIZE]; } - -/* - * $PchId: alias.c,v 1.5 2006/05/22 12:41:12 philip Exp $ - */ diff --git a/minix/commands/ash/alias.h b/bin/sh/alias.h similarity index 85% rename from minix/commands/ash/alias.h rename to bin/sh/alias.h index 5b4fb8fae..2f14dd713 100644 --- a/minix/commands/ash/alias.h +++ b/bin/sh/alias.h @@ -1,3 +1,5 @@ +/* $NetBSD: alias.h,v 1.7 2011/06/18 21:18:46 christos Exp $ */ + /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,7 +32,6 @@ * SUCH DAMAGE. * * @(#)alias.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/alias.h,v 1.8 2004/04/06 20:06:51 markm Exp $ */ #define ALIASINUSE 1 @@ -42,11 +43,6 @@ struct alias { int flag; }; -struct alias *lookupalias(const char *, int); -int aliascmd(int, char **); -int unaliascmd(int, char **); +struct alias *lookupalias(char *, int); +char *get_alias_text(char *); void rmaliases(void); - -/* - * $PchId: alias.h,v 1.4 2006/03/31 11:30:54 philip Exp $ - */ diff --git a/bin/sh/arith.y b/bin/sh/arith.y new file mode 100644 index 000000000..4fbfa1837 --- /dev/null +++ b/bin/sh/arith.y @@ -0,0 +1,206 @@ +%{ +/* $NetBSD: arith.y,v 1.22 2012/03/20 18:42:29 matt Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: arith.y,v 1.22 2012/03/20 18:42:29 matt Exp $"); +#endif +#endif /* not lint */ + +#include +#include "expand.h" +#include "builtins.h" +#include "shell.h" +#include "error.h" +#include "output.h" +#include "memalloc.h" + +typedef intmax_t YYSTYPE; +#define YYSTYPE YYSTYPE + +intmax_t arith_result; +const char *arith_buf, *arith_startbuf; + +__dead static void yyerror(const char *); +#ifdef TESTARITH +int main(int , char *[]); +int error(char *); +#endif + +%} +%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN + +%left ARITH_OR +%left ARITH_AND +%left ARITH_BOR +%left ARITH_BXOR +%left ARITH_BAND +%left ARITH_EQ ARITH_NE +%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE +%left ARITH_LSHIFT ARITH_RSHIFT +%left ARITH_ADD ARITH_SUB +%left ARITH_MUL ARITH_DIV ARITH_REM +%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT +%% + +exp: expr { + /* + * yyparse() returns int, so we have to save + * the desired result elsewhere. + */ + arith_result = $1; + } + ; + + +expr: ARITH_LPAREN expr ARITH_RPAREN { $$ = $2; } + | expr ARITH_OR expr { $$ = $1 ? $1 : $3 ? $3 : 0; } + | expr ARITH_AND expr { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; } + | expr ARITH_BOR expr { $$ = $1 | $3; } + | expr ARITH_BXOR expr { $$ = $1 ^ $3; } + | expr ARITH_BAND expr { $$ = $1 & $3; } + | expr ARITH_EQ expr { $$ = $1 == $3; } + | expr ARITH_GT expr { $$ = $1 > $3; } + | expr ARITH_GE expr { $$ = $1 >= $3; } + | expr ARITH_LT expr { $$ = $1 < $3; } + | expr ARITH_LE expr { $$ = $1 <= $3; } + | expr ARITH_NE expr { $$ = $1 != $3; } + | expr ARITH_LSHIFT expr { $$ = $1 << $3; } + | expr ARITH_RSHIFT expr { $$ = $1 >> $3; } + | expr ARITH_ADD expr { $$ = $1 + $3; } + | expr ARITH_SUB expr { $$ = $1 - $3; } + | expr ARITH_MUL expr { $$ = $1 * $3; } + | expr ARITH_DIV expr { + if ($3 == 0) + yyerror("division by zero"); + $$ = $1 / $3; + } + | expr ARITH_REM expr { + if ($3 == 0) + yyerror("division by zero"); + $$ = $1 % $3; + } + | ARITH_NOT expr { $$ = !($2); } + | ARITH_BNOT expr { $$ = ~($2); } + | ARITH_SUB expr %prec ARITH_UNARYMINUS { $$ = -($2); } + | ARITH_ADD expr %prec ARITH_UNARYPLUS { $$ = $2; } + | ARITH_NUM + ; +%% +intmax_t +arith(const char *s) +{ + intmax_t result; + + arith_buf = arith_startbuf = s; + + INTOFF; + (void) yyparse(); + result = arith_result; + arith_lex_reset(); /* reprime lex */ + INTON; + + return (result); +} + + +/* + * The exp(1) builtin. + */ +int +expcmd(int argc, char **argv) +{ + const char *p; + char *concat; + char **ap; + intmax_t i; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + /* + * concatenate arguments + */ + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + } else + p = ""; + + (void)arith(p); + i = arith_result; + + out1fmt("%"PRIdMAX"\n", i); + return (! i); +} + +/*************************/ +#ifdef TEST_ARITH +#include +main(argc, argv) + char *argv[]; +{ + printf("%"PRIdMAX"\n", exp(argv[1])); +} +error(s) + char *s; +{ + fprintf(stderr, "exp: %s\n", s); + exit(1); +} +#endif + +static void +yyerror(const char *s) +{ + + yyerrok; + yyclearin; + arith_lex_reset(); /* reprime lex */ + error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); + /* NOTREACHED */ +} diff --git a/minix/commands/ash/arith_lex.l b/bin/sh/arith_lex.l similarity index 50% rename from minix/commands/ash/arith_lex.l rename to bin/sh/arith_lex.l index 57d71e966..6f7a933ab 100644 --- a/minix/commands/ash/arith_lex.l +++ b/bin/sh/arith_lex.l @@ -1,4 +1,6 @@ %{ +/* $NetBSD: arith_lex.l,v 1.16 2012/03/20 18:42:29 matt Exp $ */ + /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -14,7 +16,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -31,103 +33,71 @@ * SUCH DAMAGE. */ -#if 0 +#include #ifndef lint +#if 0 static char sccsid[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: arith_lex.l,v 1.16 2012/03/20 18:42:29 matt Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/arith_lex.l,v 1.22 2004/04/06 20:06:51 markm Exp $"); -*/ -#include - -#include "shell.h" +#include #include "arith.h" #include "error.h" -#include "memalloc.h" +#include "expand.h" #include "var.h" -extern char *arith_buf, *arith_startbuf; +extern intmax_t yylval; +extern const char *arith_buf, *arith_startbuf; #undef YY_INPUT #define YY_INPUT(buf,result,max) \ result = (*buf = *arith_buf++) ? 1 : YY_NULL; -#define YY_NO_UNPUT %} +%option nounput noinput + %% [ \t\n] { ; } - -0x[a-fA-F0-9]+ { - yylval.l_value = strtoarith_t(yytext, NULL, 16); - return ARITH_NUM; - } - -0[0-7]+ { - yylval.l_value = strtoarith_t(yytext, NULL, 8); - return ARITH_NUM; - } - -[0-9]+ { - yylval.l_value = strtoarith_t(yytext, NULL, 10); - return ARITH_NUM; - } - -[A-Za-z][A-Za-z0-9_]* { - /* - * If variable doesn't exist, we should initialize - * it to zero. - */ - char *temp; - if (lookupvar(yytext) == NULL) - setvarsafe(yytext, "0", 0); - temp = (char *)ckmalloc(strlen(yytext) + 1); - yylval.s_value = strcpy(temp, yytext); - - return ARITH_VAR; - } - -"(" { return ARITH_LPAREN; } -")" { return ARITH_RPAREN; } -"||" { return ARITH_OR; } -"&&" { return ARITH_AND; } -"|" { return ARITH_BOR; } -"^" { return ARITH_BXOR; } -"&" { return ARITH_BAND; } -"==" { return ARITH_EQ; } -"!=" { return ARITH_NE; } -">" { return ARITH_GT; } -">=" { return ARITH_GE; } -"<" { return ARITH_LT; } -"<=" { return ARITH_LE; } -"<<" { return ARITH_LSHIFT; } -">>" { return ARITH_RSHIFT; } -"*" { return ARITH_MUL; } -"/" { return ARITH_DIV; } -"%" { return ARITH_REM; } -"+" { return ARITH_ADD; } -"-" { return ARITH_SUB; } -"~" { return ARITH_BNOT; } -"!" { return ARITH_NOT; } -"=" { return ARITH_ASSIGN; } -"+=" { return ARITH_ADDASSIGN; } -"-=" { return ARITH_SUBASSIGN; } -"*=" { return ARITH_MULASSIGN; } -"/=" { return ARITH_DIVASSIGN; } -"%=" { return ARITH_REMASSIGN; } -">>=" { return ARITH_RSHASSIGN; } -"<<=" { return ARITH_LSHASSIGN; } -"&=" { return ARITH_BANDASSIGN; } -"^=" { return ARITH_BXORASSIGN; } -"|=" { return ARITH_BORASSIGN; } -. { - error("arith: syntax error: \"%s\"\n", arith_startbuf); +0x[0-9a-fA-F]+ { yylval = strtoimax(yytext, 0, 0); return(ARITH_NUM); } +0[0-7]* { yylval = strtoimax(yytext, 0, 0); return(ARITH_NUM); } +[1-9][0-9]* { yylval = strtoimax(yytext, 0, 0); return(ARITH_NUM); } +[A-Za-z_][A-Za-z_0-9]* { char *v = lookupvar(yytext); + if (v) { + yylval = strtoimax(v, &v, 0); + if (*v == 0) + return ARITH_NUM; + } + error("arith: syntax error: \"%s\"", arith_startbuf); } +"(" { return(ARITH_LPAREN); } +")" { return(ARITH_RPAREN); } +"||" { return(ARITH_OR); } +"&&" { return(ARITH_AND); } +"|" { return(ARITH_BOR); } +"^" { return(ARITH_BXOR); } +"&" { return(ARITH_BAND); } +"==" { return(ARITH_EQ); } +"!=" { return(ARITH_NE); } +">" { return(ARITH_GT); } +">=" { return(ARITH_GE); } +"<" { return(ARITH_LT); } +"<=" { return(ARITH_LE); } +"<<" { return(ARITH_LSHIFT); } +">>" { return(ARITH_RSHIFT); } +"*" { return(ARITH_MUL); } +"/" { return(ARITH_DIV); } +"%" { return(ARITH_REM); } +"+" { return(ARITH_ADD); } +"-" { return(ARITH_SUB); } +"~" { return(ARITH_BNOT); } +"!" { return(ARITH_NOT); } +. { error("arith: syntax error: \"%s\"", arith_startbuf); } %% void -arith_lex_reset(void) -{ +arith_lex_reset(void) { +#ifdef YY_NEW_FILE YY_NEW_FILE; +#endif } diff --git a/minix/commands/ash/bltin/bltin.h b/bin/sh/bltin/bltin.h similarity index 60% rename from minix/commands/ash/bltin/bltin.h rename to bin/sh/bltin/bltin.h index 4dc41fac6..5cd90f5ac 100644 --- a/minix/commands/ash/bltin/bltin.h +++ b/bin/sh/bltin/bltin.h @@ -1,3 +1,5 @@ +/* $NetBSD: bltin.h,v 1.13 2008/10/12 01:40:37 dholland Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -29,63 +31,70 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)bltin.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/bltin/bltin.h,v 1.13 2004/04/06 20:06:53 markm Exp $ + * @(#)bltin.h 8.1 (Berkeley) 5/31/93 */ /* * This file is included by programs which are optionally built into the - * shell. If SHELL is defined, we try to map the standard UNIX library - * routines to ash routines using defines. + * shell. + * + * We always define SHELL_BUILTIN, to allow other included headers to + * hide some of their symbols if appropriate. + * + * If SHELL is defined, we try to map the standard UNIX library routines + * to ash routines using defines. */ +#define SHELL_BUILTIN #include "../shell.h" #include "../mystring.h" #ifdef SHELL -#include "builtins.h" #include "../output.h" +#include "../error.h" #undef stdout -#define stdout out1 #undef stderr -#define stderr out2 -#define printf out1fmt #undef putc -#define putc(c, file) outc(c, file) #undef putchar -#define putchar(c) out1c(c) -#define fprintf outfmt -#define fputs outstr -#define fflush flushout +#undef fileno +#undef ferror +#define FILE struct output +#define stdout out1 +#define stderr out2 +#define _RETURN_INT(x) ((x), 0) /* map from void foo() to int bar() */ +#define fprintf(...) _RETURN_INT(outfmt(__VA_ARGS__)) +#define printf(...) _RETURN_INT(out1fmt(__VA_ARGS__)) +#define putc(c, file) _RETURN_INT(outc(c, file)) +#define putchar(c) _RETURN_INT(out1c(c)) +#define fputs(...) _RETURN_INT(outstr(__VA_ARGS__)) +#define fflush(f) _RETURN_INT(flushout(f)) +#define fileno(f) ((f)->fd) +#define ferror(f) ((f)->flags & OUTPUT_ERR) #define INITARGS(argv) -#define warnx1(a, b, c) { \ - char buf[64]; \ - (void)snprintf(buf, sizeof(buf), a); \ - error("%s", buf); \ -} -#define warnx2(a, b, c) { \ - char buf[64]; \ - (void)snprintf(buf, sizeof(buf), a, b); \ - error("%s", buf); \ -} -#define warnx3(a, b, c) { \ - char buf[64]; \ - (void)snprintf(buf, sizeof(buf), a, b, c); \ - error("%s", buf); \ -} +#define err sh_err +#define verr sh_verr +#define errx sh_errx +#define verrx sh_verrx +#define warn sh_warn +#define vwarn sh_vwarn +#define warnx sh_warnx +#define vwarnx sh_vwarnx +#define exit sh_exit +#define setprogname(s) +#define getprogname() commandname +#define setlocate(l,s) 0 -#else +#define getenv(p) bltinlookup((p),0) + +#else /* ! SHELL */ #undef NULL #include #undef main #define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else -#endif +#endif /* ! SHELL */ pointer stalloc(int); -void error(const char *, ...); + +int echocmd(int, char **); -extern char *commandname; - -/* - * $PchId: bltin.h,v 1.4 2006/03/29 11:39:00 philip Exp $ - */ +extern const char *commandname; diff --git a/bin/sh/bltin/echo.1 b/bin/sh/bltin/echo.1 new file mode 100644 index 000000000..7e71fa336 --- /dev/null +++ b/bin/sh/bltin/echo.1 @@ -0,0 +1,109 @@ +.\" $NetBSD: echo.1,v 1.13 2003/08/07 09:05:40 agc Exp $ +.\" +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" Copyright 1989 by Kenneth Almquist +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)echo.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt ECHO 1 +.Os +.Sh NAME +.Nm echo +.Nd produce message in a shell script +.Sh SYNOPSIS +.Nm +.Op Fl n | Fl e +.Ar args ... +.Sh DESCRIPTION +.Nm +prints its arguments on the standard output, separated by spaces. +Unless the +.Fl n +option is present, a newline is output following the arguments. +The +.Fl e +option causes +.Nm +to treat the escape sequences specially, as described in the following +paragraph. +The +.Fl e +option is the default, and is provided solely for compatibility with +other systems. +Only one of the options +.Fl n +and +.Fl e +may be given. +.Pp +If any of the following sequences of characters is encountered during +output, the sequence is not output. Instead, the specified action is +performed: +.Bl -tag -width indent +.It Li \eb +A backspace character is output. +.It Li \ec +Subsequent output is suppressed. This is normally used at the end of the +last argument to suppress the trailing newline that +.Nm +would otherwise output. +.It Li \ef +Output a form feed. +.It Li \en +Output a newline character. +.It Li \er +Output a carriage return. +.It Li \et +Output a (horizontal) tab character. +.It Li \ev +Output a vertical tab. +.It Li \e0 Ns Ar digits +Output the character whose value is given by zero to three digits. +If there are zero digits, a nul character is output. +.It Li \e\e +Output a backslash. +.El +.Sh HINTS +Remember that backslash is special to the shell and needs to be escaped. +To output a message to standard error, say +.Pp +.D1 echo message \*[Gt]\*[Am]2 +.Sh BUGS +The octal character escape mechanism +.Pq Li \e0 Ns Ar digits +differs from the +C language mechanism. +.Pp +There is no way to force +.Nm +to treat its arguments literally, rather than interpreting them as +options and escape sequences. diff --git a/minix/commands/ash/bltin/echo.c b/bin/sh/bltin/echo.c similarity index 69% rename from minix/commands/ash/bltin/echo.c rename to bin/sh/bltin/echo.c index 72bb60e52..440e01bcd 100644 --- a/minix/commands/ash/bltin/echo.c +++ b/bin/sh/bltin/echo.c @@ -1,3 +1,5 @@ +/* $NetBSD: echo.c,v 1.14 2008/10/12 01:40:37 dholland Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -29,61 +31,65 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)echo.c 8.2 (Berkeley) 5/4/95 + * @(#)echo.c 8.1 (Berkeley) 5/31/93 */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/bltin/echo.c,v 1.14 2004/04/06 20:06:53 markm Exp $"); -*/ - /* * Echo command. + * + * echo is steeped in tradition - several of them! + * netbsd has supported 'echo [-n | -e] args' in spite of -e not being + * documented anywhere. + * Posix requires that -n be supported, output from strings containing + * \ is implementation defined + * The Single Unix Spec requires that \ escapes be treated as if -e + * were set, but that -n not be treated as an option. + * (ksh supports 'echo [-eEn] args', but not -- so that it is actually + * impossible to actually output '-n') + * + * It is suggested that 'printf "%b" "string"' be used to get \ sequences + * expanded. printf is now a builtin of netbsd's sh and csh. */ +#include +__RCSID("$NetBSD: echo.c,v 1.14 2008/10/12 01:40:37 dholland Exp $"); + +#define main echocmd + #include "bltin.h" int -echocmd(argc, argv) - int argc; - char **argv; +main(int argc, char **argv) { char **ap; char *p; char c; int count; int nflag = 0; -#ifdef __minix int eflag = 0; -#endif ap = argv; if (argc) ap++; + if ((p = *ap) != NULL) { -#ifdef __minix - if (equal(p, "--")) { - ap++; - } -#endif if (equal(p, "-n")) { - nflag++; + nflag = 1; ap++; } else if (equal(p, "-e")) { -#ifdef __minix - eflag++; -#endif + eflag = 1; ap++; } } + while ((p = *ap++) != NULL) { while ((c = *p++) != '\0') { if (c == '\\' && eflag) { switch (*p++) { - case 'a': c = '\a'; break; + case 'a': c = '\a'; break; /* bell */ case 'b': c = '\b'; break; case 'c': return 0; /* exit */ - case 'e': c = '\033'; break; + case 'e': c = 033; break; /* escape */ case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; @@ -97,6 +103,7 @@ echocmd(argc, argv) c = (c << 3) + (*p++ - '0'); break; default: + /* Output the '/' and char following */ p--; break; } @@ -108,9 +115,8 @@ echocmd(argc, argv) } if (! nflag) putchar('\n'); + fflush(stdout); + if (ferror(stdout)) + return 1; return 0; } - -/* - * $PchId: echo.c,v 1.5 2006/05/23 12:05:56 philip Exp $ - */ diff --git a/minix/commands/ash/builtins.def b/bin/sh/builtins.def similarity index 59% rename from minix/commands/ash/builtins.def rename to bin/sh/builtins.def index b55ba83eb..1f09d4306 100644 --- a/minix/commands/ash/builtins.def +++ b/bin/sh/builtins.def @@ -1,4 +1,5 @@ #!/bin/sh - +# $NetBSD: builtins.def,v 1.22 2012/12/31 14:10:15 dsl Exp $ # # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. @@ -14,7 +15,7 @@ # 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. -# 4. Neither the name of the University nor the names of its contributors +# 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. # @@ -31,63 +32,62 @@ # SUCH DAMAGE. # # @(#)builtins.def 8.4 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/builtins.def,v 1.14 2004/04/06 20:06:51 markm Exp $ # # This file lists all the builtin commands. The first column is the name -# of a C routine. The -j flag, if present, specifies that this command -# is to be excluded from systems without job control, and the -h flag, -# if present specifies that this command is to be excluded from systems -# based on the NO_HISTORY compile-time symbol. The rest of the line -# specifies the command name or names used to run the command. The entry -# for bltincmd, which is run when the user does not specify a command, must -# come first. -# -# NOTE: bltincmd must come first! +# of a C routine. +# The -j flag specifies that this command is to be excluded from systems +# without job control. +# The -h flag specifies that this command is to be excluded from systems +# based on the SMALL compile-time symbol. +# The -s flag specifies that this is a posix 'special builtin' command. +# The -u flag specifies that this is a posix 'standard utility'. +# The rest of the line specifies the command name or names used to run +# the command. -bltincmd builtin -commandcmd command -#if JOBS -bgcmd -j bg -#endif -breakcmd break continue -#catfcmd catf -cdcmd cd chdir -dotcmd . +bltincmd -u command +bgcmd -j -u bg +breakcmd -s break -s continue +cdcmd -u cd chdir +dotcmd -s . echocmd echo -evalcmd eval -execcmd exec -exitcmd exit +evalcmd -s eval +execcmd -s exec +exitcmd -s exit expcmd exp let -exportcmd export readonly -exprcmd expr test [ -falsecmd false -histcmd -h fc -#if JOBS -fgcmd -j fg -#endif -getoptscmd getopts +exportcmd -s export -s readonly +falsecmd -u false +histcmd -h -u fc +inputrc inputrc +fgcmd -j -u fg +fgcmd_percent -j -u % +getoptscmd -u getopts hashcmd hash jobidcmd jobid -jobscmd jobs +jobscmd -u jobs localcmd local #ifndef SMALL -#printfcmd printf +printfcmd printf #endif -pwdcmd pwd -readcmd read -returncmd return -setcmd set +pwdcmd -u pwd +readcmd -u read +returncmd -s return +setcmd -s set setvarcmd setvar -shiftcmd shift -trapcmd trap -truecmd : true +shiftcmd -s shift +timescmd -s times +trapcmd -s trap +truecmd -s : -u true typecmd type -umaskcmd umask -unaliascmd unalias -unsetcmd unset -waitcmd wait -aliascmd alias +umaskcmd -u umask +unaliascmd -u unalias +unsetcmd -s unset +waitcmd -u wait +aliascmd -u alias ulimitcmd ulimit -bindcmd bind +testcmd test [ +killcmd -u kill # mandated by posix for 'kill %job' wordexpcmd wordexp +#newgrp -u newgrp # optional command in posix + +#exprcmd expr diff --git a/minix/commands/ash/cd.c b/bin/sh/cd.c similarity index 55% rename from minix/commands/ash/cd.c rename to bin/sh/cd.c index 8513128ed..829570825 100644 --- a/minix/commands/ash/cd.c +++ b/bin/sh/cd.c @@ -1,3 +1,5 @@ +/* $NetBSD: cd.c,v 1.44 2011/08/31 16:24:54 plunky Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,15 +32,14 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: cd.c,v 1.44 2011/08/31 16:24:54 plunky Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/cd.c,v 1.34 2004/04/06 20:06:51 markm Exp $"); -*/ #include #include @@ -46,7 +47,6 @@ __FBSDID("$FreeBSD: src/bin/sh/cd.c,v 1.34 2004/04/06 20:06:51 markm Exp $"); #include #include #include -#include /* * The cd and pwd commands. @@ -57,68 +57,74 @@ __FBSDID("$FreeBSD: src/bin/sh/cd.c,v 1.34 2004/04/06 20:06:51 markm Exp $"); #include "nodes.h" /* for jobs.h */ #include "jobs.h" #include "options.h" +#include "builtins.h" #include "output.h" #include "memalloc.h" #include "error.h" #include "exec.h" #include "redir.h" #include "mystring.h" -#include "builtins.h" #include "show.h" #include "cd.h" -STATIC int cdlogical(const char *); -STATIC int cdphysical(const char *); -STATIC int docd(const char *, int, int); +STATIC int docd(const char *, int); STATIC char *getcomponent(void); -STATIC int updatepwd(const char *); +STATIC void updatepwd(const char *); +STATIC void find_curdir(int noerror); -STATIC char *curdir = NULL; /* current working directory */ -STATIC char *prevdir; /* previous working directory */ +char *curdir = NULL; /* current working directory */ +char *prevdir; /* previous working directory */ STATIC char *cdcomppath; int cdcmd(int argc, char **argv) { - char *dest; - char *path; - char *p; + const char *dest; + const char *path, *p; + char *d; struct stat statb; - int ch, phys, print = 0; + int print = cdprint; /* set -cdprint to enable */ - optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ - phys = Pflag; - while ((ch = getopt(argc, argv, "LP")) != -1) { - switch (ch) { - case 'L': - phys = 0; - break; - case 'P': - phys = 1; - break; - default: - error("unknown option: -%c", optopt); - break; + while (nextopt("P") != '\0') + ; + + /* + * Try (quite hard) to have 'curdir' defined, nothing has set + * it on entry to the shell, but we want 'cd fred; cd -' to work. + */ + getpwd(1); + dest = *argptr; + if (dest == NULL) { + dest = bltinlookup("HOME", 1); + if (dest == NULL) + error("HOME not set"); + } else { + if (argptr[1]) { + /* Do 'ksh' style substitution */ + if (!curdir) + error("PWD not set"); + p = strstr(curdir, dest); + if (!p) + error("bad substitution"); + d = stalloc(strlen(curdir) + strlen(argptr[1]) + 1); + memcpy(d, curdir, p - curdir); + strcpy(d + (p - curdir), argptr[1]); + strcat(d, p + strlen(dest)); + dest = d; + print = 1; } } - argc -= optind; - argv += optind; - if (argc > 1) - error("too many arguments"); - - if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) - error("HOME not set"); - if (*dest == '\0') - dest = "."; if (dest[0] == '-' && dest[1] == '\0') { dest = prevdir ? prevdir : curdir; - if (dest) - print = 1; - else - dest = "."; + print = 1; } - if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) + if (*dest == '\0') + dest = "."; + p = dest; + if (*p == '.' && *++p == '.') + p++; + if (*p == 0 || *p == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) path = nullstr; while ((p = padvance(&path, dest)) != NULL) { if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { @@ -127,41 +133,27 @@ cdcmd(int argc, char **argv) * XXX - rethink */ if (p[0] == '.' && p[1] == '/' && p[2] != '\0') - p += 2; - print = strcmp(p, dest); + print = strcmp(p + 2, dest); + else + print = strcmp(p, dest); } - if (docd(p, print, phys) >= 0) + if (docd(p, print) >= 0) return 0; + } } error("can't cd to %s", dest); - /*NOTREACHED*/ - return 0; + /* NOTREACHED */ } /* - * Actually change the directory. In an interactive shell, print the + * Actually do the chdir. In an interactive shell, print the * directory name if "print" is nonzero. */ -STATIC int -docd(const char *dest, int print, int phys) -{ - - TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys)); - - /* If logical cd fails, fall back to physical. */ - if ((phys || cdlogical(dest) < 0) && cdphysical(dest) < 0) - return (-1); - - if (print && iflag && curdir) - out1fmt("%s\n", curdir); - - return 0; -} STATIC int -cdlogical(const char *dest) +docd(const char *dest, int print) { char *p; char *q; @@ -170,6 +162,8 @@ cdlogical(const char *dest) int first; int badstat; + TRACE(("docd(\"%s\", %d) called\n", dest, print)); + /* * Check each component of the path. If we find a symlink or * something we can't stat, clear curdir to force a getcwd() @@ -196,38 +190,32 @@ cdlogical(const char *dest) if (equal(component, "..")) continue; STACKSTRNUL(p); - if (lstat(stackblock(), &statb) < 0) { + if ((lstat(stackblock(), &statb) < 0) + || (S_ISLNK(statb.st_mode))) { + /* print = 1; */ badstat = 1; break; } } INTOFF; - if (updatepwd(badstat ? NULL : dest) < 0 || chdir(curdir) < 0) { + if (chdir(dest) < 0) { INTON; - return (-1); + return -1; } + updatepwd(badstat ? NULL : dest); INTON; - return (0); + if (print && iflag == 1 && curdir) + out1fmt("%s\n", curdir); + return 0; } -STATIC int -cdphysical(const char *dest) -{ - - INTOFF; - if (chdir(dest) < 0 || updatepwd(NULL) < 0) { - INTON; - return (-1); - } - INTON; - return (0); -} /* * Get the next component of the path name pointed to by cdcomppath. * This routine overwrites the string pointed to by cdcomppath. */ + STATIC char * getcomponent(void) { @@ -249,12 +237,14 @@ getcomponent(void) } + /* * Update curdir (the name of the current directory) in response to a * cd command. We also call hashcd to let the routines in exec.c know * that the current directory has changed. */ -STATIC int + +STATIC void updatepwd(const char *dir) { char *new; @@ -273,14 +263,14 @@ updatepwd(const char *dir) INTOFF; prevdir = curdir; curdir = NULL; - if (getpwd() == NULL) { - INTON; - return (-1); - } - setvar("PWD", curdir, VEXPORT); - setvar("OLDPWD", prevdir, VEXPORT); + getpwd(1); INTON; - return (0); + if (curdir) { + setvar("OLDPWD", prevdir, VEXPORT); + setvar("PWD", curdir, VEXPORT); + } else + unsetvar("PWD", 0); + return; } cdcomppath = stalloc(strlen(dir) + 1); scopy(dir, cdcomppath); @@ -309,82 +299,166 @@ updatepwd(const char *dir) ckfree(prevdir); prevdir = curdir; curdir = savestr(stackblock()); - setvar("PWD", curdir, VEXPORT); setvar("OLDPWD", prevdir, VEXPORT); + setvar("PWD", curdir, VEXPORT); INTON; - - return (0); } +/* + * Posix says the default should be 'pwd -L' (as below), however + * the 'cd' command (above) does something much nearer to the + * posix 'cd -P' (not the posix default of 'cd -L'). + * If 'cd' is changed to support -P/L then the default here + * needs to be revisited if the historic behaviour is to be kept. + */ + int pwdcmd(int argc, char **argv) { - char buf[PATH_MAX]; - int ch, phys; + int i; + char opt = 'L'; - optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ - phys = Pflag; - while ((ch = getopt(argc, argv, "LP")) != -1) { - switch (ch) { - case 'L': - phys = 0; - break; - case 'P': - phys = 1; - break; - default: - error("unknown option: -%c", optopt); - break; - } - } - argc -= optind; - argv += optind; + while ((i = nextopt("LP")) != '\0') + opt = i; + if (*argptr) + error("unexpected argument"); - if (argc != 0) - error("too many arguments"); - - if (!phys && getpwd()) { - out1str(curdir); - out1c('\n'); - } else { - if (getcwd(buf, sizeof(buf)) == NULL) - error(".: %s", strerror(errno)); - out1str(buf); - out1c('\n'); - } + if (opt == 'L') + getpwd(0); + else + find_curdir(0); + setvar("OLDPWD", prevdir, VEXPORT); + setvar("PWD", curdir, VEXPORT); + out1str(curdir); + out1c('\n'); return 0; } + + +void +initpwd(void) +{ + getpwd(1); + if (curdir) + setvar("PWD", curdir, VEXPORT); + else + sh_warnx("Cannot determine current working directory"); +} + +#define MAXPWD 256 + /* * Find out what the current directory is. If we already know the current * directory, this routine returns immediately. */ -char * -getpwd(void) +void +getpwd(int noerror) { - char buf[PATH_MAX]; + char *pwd; + struct stat stdot, stpwd; + static int first = 1; if (curdir) - return curdir; - if (getcwd(buf, sizeof(buf)) == NULL) { - char *pwd = getenv("PWD"); - struct stat stdot, stpwd; + return; + if (first) { + first = 0; + pwd = getenv("PWD"); if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && stat(pwd, &stpwd) != -1 && stdot.st_dev == stpwd.st_dev && stdot.st_ino == stpwd.st_ino) { curdir = savestr(pwd); - return curdir; + return; } - return NULL; } - curdir = savestr(buf); - return curdir; + find_curdir(noerror); + + return; } -/* - * $PchId: cd.c,v 1.6 2006/05/22 12:42:03 philip Exp $ - */ +STATIC void +find_curdir(int noerror) +{ + int i; + char *pwd; + + /* + * Things are a bit complicated here; we could have just used + * getcwd, but traditionally getcwd is implemented using popen + * to /bin/pwd. This creates a problem for us, since we cannot + * keep track of the job if it is being ran behind our backs. + * So we re-implement getcwd(), and we suppress interrupts + * throughout the process. This is not completely safe, since + * the user can still break out of it by killing the pwd program. + * We still try to use getcwd for systems that we know have a + * c implementation of getcwd, that does not open a pipe to + * /bin/pwd. + */ +#if defined(__NetBSD__) || defined(__SVR4) || defined(__minix) + + for (i = MAXPWD;; i *= 2) { + pwd = stalloc(i); + if (getcwd(pwd, i) != NULL) { + curdir = savestr(pwd); + return; + } + stunalloc(pwd); + if (errno == ERANGE) + continue; + if (!noerror) + error("getcwd() failed: %s", strerror(errno)); + return; + } +#else + { + char *p; + int status; + struct job *jp; + int pip[2]; + + pwd = stalloc(MAXPWD); + INTOFF; + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob(NULL, 1); + if (forkshell(jp, NULL, FORK_NOJOB) == 0) { + (void) close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1, 1); + close(pip[1]); + } + (void) execl("/bin/pwd", "pwd", (char *)0); + error("Cannot exec /bin/pwd"); + } + (void) close(pip[1]); + pip[1] = -1; + p = pwd; + while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0 + || (i == -1 && errno == EINTR)) { + if (i > 0) + p += i; + } + (void) close(pip[0]); + pip[0] = -1; + status = waitforjob(jp); + if (status != 0) + error((char *)0); + if (i < 0 || p == pwd || p[-1] != '\n') { + if (noerror) { + INTON; + return; + } + error("pwd command failed"); + } + p[-1] = '\0'; + INTON; + curdir = savestr(pwd); + return; + } +#endif +} diff --git a/minix/commands/ash/cd.h b/bin/sh/cd.h similarity index 85% rename from minix/commands/ash/cd.h rename to bin/sh/cd.h index f4546f206..7600955da 100644 --- a/minix/commands/ash/cd.h +++ b/bin/sh/cd.h @@ -1,3 +1,5 @@ +/* $NetBSD: cd.h,v 1.6 2011/06/18 21:18:46 christos Exp $ */ + /*- * Copyright (c) 1995 * The Regents of the University of California. All rights reserved. @@ -10,7 +12,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -26,13 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/bin/sh/cd.h,v 1.7 2004/04/06 20:06:51 markm Exp $ */ -char *getpwd(void); -int cdcmd (int, char **); -int pwdcmd(int, char **); - -/* - * $PchId: cd.h,v 1.3 2006/03/31 09:59:04 philip Exp $ - */ +void initpwd(void); +void getpwd(int); diff --git a/bin/sh/error.c b/bin/sh/error.c new file mode 100644 index 000000000..2ac98b69a --- /dev/null +++ b/bin/sh/error.c @@ -0,0 +1,370 @@ +/* $NetBSD: error.c,v 1.38 2012/03/15 02:02:20 joerg Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)error.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: error.c,v 1.38 2012/03/15 02:02:20 joerg Exp $"); +#endif +#endif /* not lint */ + +/* + * Errors and exceptions. + */ + +#include +#include +#include +#include +#include +#include + +#include "shell.h" +#include "eval.h" /* for commandname */ +#include "main.h" +#include "options.h" +#include "output.h" +#include "error.h" +#include "show.h" + + +/* + * Code to handle exceptions in C. + */ + +struct jmploc *handler; +int exception; +volatile int suppressint; +volatile int intpending; + + +static void exverror(int, const char *, va_list) __dead; + +/* + * Called to raise an exception. Since C doesn't include exceptions, we + * just do a longjmp to the exception handler. The type of exception is + * stored in the global variable "exception". + */ + +void +exraise(int e) +{ + if (handler == NULL) + abort(); + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received. (If the user specifies + * that SIGINT is to be trapped or ignored using the trap builtin, then + * this routine is not called.) Suppressint is nonzero when interrupts + * are held using the INTOFF macro. The call to _exit is necessary because + * there is a short period after a fork before the signal handlers are + * set to the appropriate value for the child. (The test for iflag is + * just defensive programming.) + */ + +void +onint(void) +{ + sigset_t nsigset; + + if (suppressint) { + intpending = 1; + return; + } + intpending = 0; + sigemptyset(&nsigset); + sigprocmask(SIG_SETMASK, &nsigset, NULL); + if (rootshell && iflag) + exraise(EXINT); + else { + signal(SIGINT, SIG_DFL); + raise(SIGINT); + } + /* NOTREACHED */ +} + +static __printflike(2, 0) void +exvwarning(int sv_errno, const char *msg, va_list ap) +{ + /* Partially emulate line buffered output so that: + * printf '%d\n' 1 a 2 + * and + * printf '%d %d %d\n' 1 a 2 + * both generate sensible text when stdout and stderr are merged. + */ + if (output.nextc != output.buf && output.nextc[-1] == '\n') + flushout(&output); + if (commandname) + outfmt(&errout, "%s: ", commandname); + else + outfmt(&errout, "%s: ", getprogname()); + if (msg != NULL) { + doformat(&errout, msg, ap); + if (sv_errno >= 0) + outfmt(&errout, ": "); + } + if (sv_errno >= 0) + outfmt(&errout, "%s", strerror(sv_errno)); + out2c('\n'); + flushout(&errout); +} + +/* + * Exverror is called to raise the error exception. If the second argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ +static __printflike(2, 0) void +exverror(int cond, const char *msg, va_list ap) +{ + CLEAR_PENDING_INT; + INTOFF; + +#ifdef DEBUG + if (msg) { + TRACE(("exverror(%d, \"", cond)); + TRACEV((msg, ap)); + TRACE(("\") pid=%d\n", getpid())); + } else + TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); +#endif + if (msg) + exvwarning(-1, msg, ap); + + flushall(); + exraise(cond); + /* NOTREACHED */ +} + + +void +error(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + exverror(EXERROR, msg, ap); + /* NOTREACHED */ + va_end(ap); +} + + +void +exerror(int cond, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + exverror(cond, msg, ap); + /* NOTREACHED */ + va_end(ap); +} + +/* + * error/warning routines for external builtins + */ + +void +sh_exit(int rval) +{ + exerrno = rval & 255; + exraise(EXEXEC); +} + +void +sh_err(int status, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(errno, fmt, ap); + va_end(ap); + sh_exit(status); +} + +void +sh_verr(int status, const char *fmt, va_list ap) +{ + exvwarning(errno, fmt, ap); + sh_exit(status); +} + +void +sh_errx(int status, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(-1, fmt, ap); + va_end(ap); + sh_exit(status); +} + +void +sh_verrx(int status, const char *fmt, va_list ap) +{ + exvwarning(-1, fmt, ap); + sh_exit(status); +} + +void +sh_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(errno, fmt, ap); + va_end(ap); +} + +void +sh_vwarn(const char *fmt, va_list ap) +{ + exvwarning(errno, fmt, ap); +} + +void +sh_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(-1, fmt, ap); + va_end(ap); +} + +void +sh_vwarnx(const char *fmt, va_list ap) +{ + exvwarning(-1, fmt, ap); +} + + +/* + * Table of error messages. + */ + +struct errname { + short errcode; /* error number */ + short action; /* operation which encountered the error */ + const char *msg; /* text describing the error */ +}; + + +#define ALL (E_OPEN|E_CREAT|E_EXEC) + +STATIC const struct errname errormsg[] = { + { EINTR, ALL, "interrupted" }, + { EACCES, ALL, "permission denied" }, + { EIO, ALL, "I/O error" }, + { EEXIST, ALL, "file exists" }, + { ENOENT, E_OPEN, "no such file" }, + { ENOENT, E_CREAT,"directory nonexistent" }, + { ENOENT, E_EXEC, "not found" }, + { ENOTDIR, E_OPEN, "no such file" }, + { ENOTDIR, E_CREAT,"directory nonexistent" }, + { ENOTDIR, E_EXEC, "not found" }, + { EISDIR, ALL, "is a directory" }, +#ifdef EMFILE + { EMFILE, ALL, "too many open files" }, +#endif + { ENFILE, ALL, "file table overflow" }, + { ENOSPC, ALL, "file system full" }, +#ifdef EDQUOT + { EDQUOT, ALL, "disk quota exceeded" }, +#endif +#ifdef ENOSR + { ENOSR, ALL, "no streams resources" }, +#endif + { ENXIO, ALL, "no such device or address" }, + { EROFS, ALL, "read-only file system" }, + { ETXTBSY, ALL, "text busy" }, +#ifdef EAGAIN + { EAGAIN, E_EXEC, "not enough memory" }, +#endif + { ENOMEM, ALL, "not enough memory" }, +#ifdef ENOLINK + { ENOLINK, ALL, "remote access failed" }, +#endif +#ifdef EMULTIHOP + { EMULTIHOP, ALL, "remote access failed" }, +#endif +#ifdef ECOMM + { ECOMM, ALL, "remote access failed" }, +#endif +#ifdef ESTALE + { ESTALE, ALL, "remote access failed" }, +#endif +#ifdef ETIMEDOUT + { ETIMEDOUT, ALL, "remote access failed" }, +#endif +#ifdef ELOOP + { ELOOP, ALL, "symbolic link loop" }, +#endif +#ifdef ENAMETOOLONG + { ENAMETOOLONG, ALL, "file name too long" }, +#endif + { E2BIG, E_EXEC, "argument list too long" }, +#ifdef ELIBACC + { ELIBACC, E_EXEC, "shared library missing" }, +#endif + { 0, 0, NULL }, +}; + + +/* + * Return a string describing an error. The returned string may be a + * pointer to a static buffer that will be overwritten on the next call. + * Action describes the operation that got the error. + */ + +const char * +errmsg(int e, int action) +{ + struct errname const *ep; + static char buf[12]; + + for (ep = errormsg ; ep->errcode ; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return ep->msg; + } + fmtstr(buf, sizeof buf, "error %d", e); + return buf; +} diff --git a/minix/commands/ash/error.h b/bin/sh/error.h similarity index 70% rename from minix/commands/ash/error.h rename to bin/sh/error.h index 6597d9038..b4121b8da 100644 --- a/minix/commands/ash/error.h +++ b/bin/sh/error.h @@ -1,3 +1,5 @@ +/* $NetBSD: error.h,v 1.19 2012/03/15 02:02:20 joerg Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,28 +32,38 @@ * SUCH DAMAGE. * * @(#)error.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/error.h,v 1.17 2004/04/06 20:06:51 markm Exp $ */ +#include + +/* + * Types of operations (passed to the errmsg routine). + */ + +#define E_OPEN 01 /* opening a file */ +#define E_CREAT 02 /* creating a file */ +#define E_EXEC 04 /* executing a program */ + + /* * We enclose jmp_buf in a structure so that we can declare pointers to * jump locations. The global variable handler contains the location to * jump to when an exception occurs, and the global variable exception - * contains a code identifying the exception. To implement nested + * contains a code identifying the exeception. To implement nested * exception handlers, the user should save the value of handler on entry * to an inner scope, set handler to point to a jmploc structure for the * inner scope, and restore handler on exit from the scope. */ #include -#include struct jmploc { jmp_buf loc; }; extern struct jmploc *handler; -extern volatile sig_atomic_t exception; +extern int exception; +extern int exerrno; /* error for EXEXEC */ /* exceptions */ #define EXINT 0 /* SIGINT received */ @@ -67,8 +79,8 @@ extern volatile sig_atomic_t exception; * more fun than worrying about efficiency and portability. :-)) */ -extern volatile sig_atomic_t suppressint; -extern volatile sig_atomic_t intpending; +extern volatile int suppressint; +extern volatile int intpending; #define INTOFF suppressint++ #define INTON { if (--suppressint == 0 && intpending) onint(); } @@ -76,12 +88,24 @@ extern volatile sig_atomic_t intpending; #define CLEAR_PENDING_INT intpending = 0 #define int_pending() intpending -#define __printf0like(a,b) - -void exraise(int); +#if ! defined(SHELL_BUILTIN) +void exraise(int) __dead; void onint(void); -void error(const char *, ...) __printf0like(1, 2); -void exerror(int, const char *, ...) __printf0like(2, 3); +void error(const char *, ...) __dead __printflike(1, 2); +void exerror(int, const char *, ...) __dead __printflike(2, 3); +const char *errmsg(int, int); +#endif /* ! SHELL_BUILTIN */ + +void sh_err(int, const char *, ...) __dead __printflike(2, 3); +void sh_verr(int, const char *, va_list) __dead __printflike(2, 0); +void sh_errx(int, const char *, ...) __dead __printflike(2, 3); +void sh_verrx(int, const char *, va_list) __dead __printflike(2, 0); +void sh_warn(const char *, ...) __printflike(1, 2); +void sh_vwarn(const char *, va_list) __printflike(1, 0); +void sh_warnx(const char *, ...) __printflike(1, 2); +void sh_vwarnx(const char *, va_list) __printflike(1, 0); + +void sh_exit(int) __dead; /* @@ -89,13 +113,7 @@ void exerror(int, const char *, ...) __printf0like(2, 3); * so we use _setjmp instead. */ -#ifdef BSD -#ifndef __minix +#if defined(BSD) && !defined(__SVR4) #define setjmp(jmploc) _setjmp(jmploc) #define longjmp(jmploc, val) _longjmp(jmploc, val) #endif -#endif - -/* - * $PchId: error.h,v 1.5 2006/04/10 14:36:43 philip Exp $ - */ diff --git a/minix/commands/ash/eval.c b/bin/sh/eval.c similarity index 58% rename from minix/commands/ash/eval.c rename to bin/sh/eval.c index 4c1df9c55..a22819b52 100644 --- a/minix/commands/ash/eval.c +++ b/bin/sh/eval.c @@ -1,3 +1,5 @@ +/* $NetBSD: eval.c,v 1.107 2013/06/27 23:22:04 yamt Exp $ */ + /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,24 +32,28 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; +#else +__RCSID("$NetBSD: eval.c,v 1.107 2013/06/27 23:22:04 yamt Exp $"); #endif #endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/eval.c,v 1.42 2004/04/06 20:06:51 markm Exp $"); -*/ -#ifndef NO_PATHS_H -#include -#endif -#include +#include #include -#include -#include /* For WIFSIGNALED(status) */ +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include /* * Evaluate a command. @@ -72,33 +78,32 @@ __FBSDID("$FreeBSD: src/bin/sh/eval.c,v 1.42 2004/04/06 20:06:51 markm Exp $"); #include "error.h" #include "show.h" #include "mystring.h" -#if !defined(NO_HISTORY) +#include "main.h" +#ifndef SMALL #include "myhistedit.h" #endif -#ifndef _PATH_STDPATH -#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin:" -#endif /* flags in argument to evaltree */ #define EV_EXIT 01 /* exit after evaluating tree */ #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ #define EV_BACKCMD 04 /* command executing within back quotes */ -MKINIT int evalskip; /* set if we are skipping commands */ +int evalskip; /* set if we are skipping commands */ STATIC int skipcount; /* number of levels to skip */ MKINIT int loopnest; /* current loop nesting level */ int funcnest; /* depth of function calls */ +STATIC int builtin_flags; /* evalcommand flags for builtins */ -char *commandname; +const char *commandname; struct strlist *cmdenviron; int exitstatus; /* exit status of last command */ -int oexitstatus; /* saved exit status */ +int back_exitstatus; /* exit status of backquoted command */ -STATIC void evalloop(union node *); -STATIC void evalfor(union node *); +STATIC void evalloop(union node *, int); +STATIC void evalfor(union node *, int); STATIC void evalcase(union node *, int); STATIC void evalsubshell(union node *, int); STATIC void expredir(union node *); @@ -125,10 +130,35 @@ SHELLPROC { } #endif +static int +sh_pipe(int fds[2]) +{ + int nfd; + + if (pipe(fds)) + return -1; + + if (fds[0] < 3) { + nfd = fcntl(fds[0], F_DUPFD, 3); + if (nfd != -1) { + close(fds[0]); + fds[0] = nfd; + } + } + + if (fds[1] < 3) { + nfd = fcntl(fds[1], F_DUPFD, 3); + if (nfd != -1) { + close(fds[1]); + fds[1] = nfd; + } + } + return 0; +} /* - * The eval command. + * The eval commmand. */ int @@ -153,7 +183,7 @@ evalcmd(int argc, char **argv) STPUTC('\0', concat); p = grabstackstr(concat); } - evalstring(p); + evalstring(p, builtin_flags & EV_TESTED); } return exitstatus; } @@ -164,15 +194,16 @@ evalcmd(int argc, char **argv) */ void -evalstring(char *s) +evalstring(char *s, int flag) { union node *n; struct stackmark smark; setstackmark(&smark); setinputstring(s, 1); + while ((n = parsecmd(0)) != NEOF) { - evaltree(n, 0); + evaltree(n, flag); popstackmark(&smark); } popfile(); @@ -189,27 +220,30 @@ evalstring(char *s) void evaltree(union node *n, int flags) { + bool do_etest; + + do_etest = false; if (n == NULL) { TRACE(("evaltree(NULL) called\n")); exitstatus = 0; goto out; } -#if !defined(NO_HISTORY) +#ifndef SMALL displayhist = 1; /* show history substitutions done with fc */ #endif - TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type)); + TRACE(("pid %d, evaltree(%p: %d, %d) called\n", + getpid(), n, n->type, flags)); switch (n->type) { case NSEMI: - evaltree(n->nbinary.ch1, 0); + evaltree(n->nbinary.ch1, flags & EV_TESTED); if (evalskip) goto out; evaltree(n->nbinary.ch2, flags); break; case NAND: evaltree(n->nbinary.ch1, EV_TESTED); - if (evalskip || exitstatus != 0) { + if (evalskip || exitstatus != 0) goto out; - } evaltree(n->nbinary.ch2, flags); break; case NOR: @@ -226,6 +260,7 @@ evaltree(union node *n, int flags) break; case NSUBSHELL: evalsubshell(n, flags); + do_etest = !(flags & EV_TESTED); break; case NBACKGND: evalsubshell(n, flags); @@ -244,10 +279,10 @@ evaltree(union node *n, int flags) } case NWHILE: case NUNTIL: - evalloop(n); + evalloop(n, flags); break; case NFOR: - evalfor(n); + evalfor(n, flags); break; case NCASE: evalcase(n, flags); @@ -260,12 +295,13 @@ evaltree(union node *n, int flags) evaltree(n->nnot.com, EV_TESTED); exitstatus = !exitstatus; break; - case NPIPE: evalpipe(n); + do_etest = !(flags & EV_TESTED); break; case NCMD: - evalcommand(n, flags, (struct backcmd *)NULL); + evalcommand(n, flags, NULL); + do_etest = !(flags & EV_TESTED); break; default: out1fmt("Node type = %d\n", n->type); @@ -275,15 +311,13 @@ evaltree(union node *n, int flags) out: if (pendingsigs) dotrap(); - if ((flags & EV_EXIT) || (eflag && exitstatus - && !(flags & EV_TESTED) && (n->type == NCMD || - n->type == NSUBSHELL))) + if ((flags & EV_EXIT) != 0 || (eflag && exitstatus != 0 && do_etest)) exitshell(exitstatus); } STATIC void -evalloop(union node *n) +evalloop(union node *n, int flags) { int status; @@ -307,7 +341,7 @@ skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { if (exitstatus == 0) break; } - evaltree(n->nbinary.ch2, 0); + evaltree(n->nbinary.ch2, flags & EV_TESTED); status = exitstatus; if (evalskip) goto skipping; @@ -319,28 +353,28 @@ skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { STATIC void -evalfor(union node *n) +evalfor(union node *n, int flags) { struct arglist arglist; union node *argp; struct strlist *sp; struct stackmark smark; + int status = 0; setstackmark(&smark); arglist.lastp = &arglist.list; for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { - oexitstatus = exitstatus; expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); if (evalskip) goto out; } *arglist.lastp = NULL; - exitstatus = 0; loopnest++; for (sp = arglist.list ; sp ; sp = sp->next) { setvar(n->nfor.var, sp->text, 0); - evaltree(n->nfor.body, 0); + evaltree(n->nfor.body, flags & EV_TESTED); + status = exitstatus; if (evalskip) { if (evalskip == SKIPCONT && --skipcount <= 0) { evalskip = 0; @@ -352,6 +386,7 @@ evalfor(union node *n) } } loopnest--; + exitstatus = status; out: popstackmark(&smark); } @@ -365,22 +400,24 @@ evalcase(union node *n, int flags) union node *patp; struct arglist arglist; struct stackmark smark; + int status = 0; setstackmark(&smark); arglist.lastp = &arglist.list; - oexitstatus = exitstatus; expandarg(n->ncase.expr, &arglist, EXP_TILDE); for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { if (casematch(patp, arglist.list->text)) { if (evalskip == 0) { evaltree(cp->nclist.body, flags); + status = exitstatus; } goto out; } } } out: + exitstatus = status; popstackmark(&smark); } @@ -397,18 +434,19 @@ evalsubshell(union node *n, int flags) int backgnd = (n->type == NBACKGND); expredir(n->nredir.redirect); + INTOFF; jp = makejob(n, 1); - if (forkshell(jp, n, backgnd) == 0) { + if (forkshell(jp, n, backgnd ? FORK_BG : FORK_FG) == 0) { + INTON; if (backgnd) flags &=~ EV_TESTED; redirect(n->nredir.redirect, 0); - evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ - } - if (! backgnd) { - INTOFF; - exitstatus = waitforjob(jp, (int *)NULL); - INTON; + /* never returns */ + evaltree(n->nredir.n, flags | EV_EXIT); } + if (! backgnd) + exitstatus = waitforjob(jp); + INTON; } @@ -425,13 +463,12 @@ expredir(union node *n) for (redir = n ; redir ; redir = redir->nfile.next) { struct arglist fn; fn.lastp = &fn.list; - oexitstatus = exitstatus; switch (redir->type) { + case NFROMTO: case NFROM: case NTO: - case NFROMTO: - case NAPPEND: case NCLOBBER: + case NAPPEND: expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); redir->nfile.expfname = fn.list->text; break; @@ -475,22 +512,24 @@ evalpipe(union node *n) prehash(lp->n); pip[1] = -1; if (lp->next) { - if (pipe(pip) < 0) { - close(prevfd); - error("Pipe call failed: %s", strerror(errno)); + if (sh_pipe(pip) < 0) { + if (prevfd >= 0) + close(prevfd); + error("Pipe call failed"); } } - if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + if (forkshell(jp, lp->n, n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) { INTON; if (prevfd > 0) { - dup2(prevfd, 0); + close(0); + copyfd(prevfd, 0, 1); close(prevfd); } if (pip[1] >= 0) { - if (!(prevfd >= 0 && pip[0] == 0)) - close(pip[0]); + close(pip[0]); if (pip[1] != 1) { - dup2(pip[1], 1); + close(1); + copyfd(pip[1], 1, 1); close(pip[1]); } } @@ -501,13 +540,11 @@ evalpipe(union node *n) prevfd = pip[0]; close(pip[1]); } - INTON; if (n->npipe.backgnd == 0) { - INTOFF; - exitstatus = waitforjob(jp, (int *)NULL); + exitstatus = waitforjob(jp); TRACE(("evalpipe: job done exit status %d\n", exitstatus)); - INTON; } + INTON; } @@ -532,102 +569,185 @@ evalbackcmd(union node *n, struct backcmd *result) result->nleft = 0; result->jp = NULL; if (n == NULL) { - exitstatus = 0; goto out; } +#ifdef notyet + /* + * For now we disable executing builtins in the same + * context as the shell, because we are not keeping + * enough state to recover from changes that are + * supposed only to affect subshells. eg. echo "`cd /`" + */ if (n->type == NCMD) { exitstatus = oexitstatus; evalcommand(n, EV_BACKCMD, result); - } else { - exitstatus = 0; - if (pipe(pip) < 0) - error("Pipe call failed: %s", strerror(errno)); + } else +#endif + { + INTOFF; + if (sh_pipe(pip) < 0) + error("Pipe call failed"); jp = makejob(n, 1); if (forkshell(jp, n, FORK_NOJOB) == 0) { FORCEINTON; close(pip[0]); if (pip[1] != 1) { - dup2(pip[1], 1); + close(1); + copyfd(pip[1], 1, 1); close(pip[1]); } + eflag = 0; evaltree(n, EV_EXIT); + /* NOTREACHED */ } close(pip[1]); result->fd = pip[0]; result->jp = jp; + INTON; } out: popstackmark(&smark); - TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n", + TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", result->fd, result->buf, result->nleft, result->jp)); } +static const char * +syspath(void) +{ +#if !defined(__minix) + static char *sys_path = NULL; + static int mib[] = {CTL_USER, USER_CS_PATH}; +#endif /* !defined(__minix) */ + static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin"; +#if !defined(__minix) + size_t len; + if (sys_path == NULL) { + if (sysctl(mib, 2, 0, &len, 0, 0) != -1 && + (sys_path = ckmalloc(len + 5)) != NULL && + sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) { + memcpy(sys_path, "PATH=", 5); + } else { + ckfree(sys_path); + /* something to keep things happy */ + sys_path = def_path; + } + } + return sys_path; +#else + /* On Minix no support for CTL_USER. */ + return def_path; +#endif /* !defined(__minix) */ +} + +static int +parse_command_args(int argc, char **argv, int *use_syspath) +{ + int sv_argc = argc; + char *cp, c; + + *use_syspath = 0; + + for (;;) { + argv++; + if (--argc == 0) + break; + cp = *argv; + if (*cp++ != '-') + break; + if (*cp == '-' && cp[1] == 0) { + argv++; + argc--; + break; + } + while ((c = *cp++)) { + switch (c) { + case 'p': + *use_syspath = 1; + break; + default: + /* run 'typecmd' for other options */ + return 0; + } + } + } + return sv_argc - argc; +} + +int vforked = 0; +extern char *trap[]; /* * Execute a simple command. */ STATIC void -evalcommand(union node *cmd, int flags, struct backcmd *backcmd) +evalcommand(union node *cmd, int flgs, struct backcmd *backcmd) { struct stackmark smark; union node *argp; struct arglist arglist; struct arglist varlist; - char **argv; - int argc; + volatile int flags = flgs; + char ** volatile argv; + volatile int argc; char **envp; int varflag; struct strlist *sp; - int mode; + volatile int mode; int pip[2]; struct cmdentry cmdentry; - struct job *jp; + struct job * volatile jp; struct jmploc jmploc; - struct jmploc *volatile savehandler; - char *volatile savecmdname; + struct jmploc *volatile savehandler = NULL; + const char *volatile savecmdname; volatile struct shparam saveparam; struct localvar *volatile savelocalvars; volatile int e; - char *lastarg; - int realstatus; - int do_clearcmdentry; -#if __GNUC__ - /* Avoid longjmp clobbering */ - (void) &argv; - (void) &argc; - (void) &lastarg; - (void) &flags; - (void) &do_clearcmdentry; -#endif + char * volatile lastarg; + const char * volatile path = pathval(); + volatile int temp_path; + vforked = 0; /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(&smark); + back_exitstatus = 0; + arglist.lastp = &arglist.list; - varlist.lastp = &varlist.list; varflag = 1; - do_clearcmdentry = 0; - oexitstatus = exitstatus; - exitstatus = 0; + /* Expand arguments, ignoring the initial 'name=value' ones */ for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { char *p = argp->narg.text; if (varflag && is_name(*p)) { do { p++; } while (is_in_name(*p)); - if (*p == '=') { - expandarg(argp, &varlist, EXP_VARTILDE); + if (*p == '=') continue; - } } expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); varflag = 0; } *arglist.lastp = NULL; - *varlist.lastp = NULL; + expredir(cmd->ncmd.redirect); + + /* Now do the initial 'name=value' ones we skipped above */ + varlist.lastp = &varlist.list; + for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { + char *p = argp->narg.text; + if (!is_name(*p)) + break; + do + p++; + while (is_in_name(*p)); + if (*p != '=') + break; + expandarg(argp, &varlist, EXP_VARTILDE); + } + *varlist.lastp = NULL; + argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; @@ -645,14 +765,19 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) /* Print the command if xflag is set. */ if (xflag) { - outc('+', &errout); + char sep = 0; + out2str(ps4val()); for (sp = varlist.list ; sp ; sp = sp->next) { - outc(' ', &errout); - out2str(sp->text); + if (sep != 0) + outc(sep, &errout); + out2shstr(sp->text); + sep = ' '; } for (sp = arglist.list ; sp ; sp = sp->next) { - outc(' ', &errout); - out2str(sp->text); + if (sep != 0) + outc(sep, &errout); + out2shstr(sp->text); + sep = ' '; } outc('\n', &errout); flushout(&errout); @@ -660,90 +785,136 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) /* Now locate the command. */ if (argc == 0) { - cmdentry.cmdtype = CMDBUILTIN; - cmdentry.u.index = BLTINCMD; + cmdentry.cmdtype = CMDSPLBLTIN; + cmdentry.u.bltin = bltincmd; } else { static const char PATH[] = "PATH="; - char *path = pathval(); + int cmd_flags = DO_ERR; /* * Modify the command lookup path, if a PATH= assignment * is present */ - for (sp = varlist.list ; sp ; sp = sp->next) - if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) { + for (sp = varlist.list; sp; sp = sp->next) + if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) path = sp->text + sizeof(PATH) - 1; - /* - * On `PATH=... command`, we need to make - * sure that the command isn't using the - * non-updated hash table of the outer PATH - * setting and we need to make sure that - * the hash table isn't filled with items - * from the temporary setting. - * - * It would be better to forbit using and - * updating the table while this command - * runs, by the command finding mechanism - * is heavily integrated with hash handling, - * so we just delete the hash before and after - * the command runs. Partly deleting like - * changepatch() does doesn't seem worth the - * bookinging effort, since most such runs add - * directories in front of the new PATH. - */ - clearcmdentry(0); - do_clearcmdentry = 1; + + do { + int argsused, use_syspath; + find_command(argv[0], &cmdentry, cmd_flags, path); + if (cmdentry.cmdtype == CMDUNKNOWN) { + exitstatus = 127; + flushout(&errout); + goto out; } - find_command(argv[0], &cmdentry, 1, path); - if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ - exitstatus = 127; - flushout(&errout); - return; - } - /* implement the bltin builtin here */ - if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { - for (;;) { - argv++; - if (--argc == 0) - break; - if ((cmdentry.u.index = find_builtin(*argv)) < 0) { - outfmt(&errout, "%s: not found\n", *argv); - exitstatus = 127; - flushout(&errout); - return; - } - if (cmdentry.u.index != BLTINCMD) - break; + /* implement the 'command' builtin here */ + if (cmdentry.cmdtype != CMDBUILTIN || + cmdentry.u.bltin != bltincmd) + break; + cmd_flags |= DO_NOFUNC; + argsused = parse_command_args(argc, argv, &use_syspath); + if (argsused == 0) { + /* use 'type' builting to display info */ + cmdentry.u.bltin = typecmd; + break; } - } + argc -= argsused; + argv += argsused; + if (use_syspath) + path = syspath() + 5; + } while (argc != 0); + if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) + /* posix mandates that 'command ' act as if + was a normal builtin */ + cmdentry.cmdtype = CMDBUILTIN; } /* Fork off a child process if necessary. */ - if (cmd->ncmd.backgnd - || (cmdentry.cmdtype == CMDNORMAL - && ((flags & EV_EXIT) == 0 || Tflag)) + if (cmd->ncmd.backgnd || (trap[0] && (flags & EV_EXIT) != 0) + || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) || ((flags & EV_BACKCMD) != 0 - && (cmdentry.cmdtype != CMDBUILTIN - || cmdentry.u.index == CDCMD - || cmdentry.u.index == DOTCMD - || cmdentry.u.index == EVALCMD)) - || (cmdentry.cmdtype == CMDBUILTIN && - cmdentry.u.index == COMMANDCMD)) { + && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN) + || cmdentry.u.bltin == dotcmd + || cmdentry.u.bltin == evalcmd))) { + INTOFF; jp = makejob(cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; - if (pipe(pip) < 0) - error("Pipe call failed: %s", strerror(errno)); + if (sh_pipe(pip) < 0) + error("Pipe call failed"); } - if (forkshell(jp, cmd, mode) != 0) - goto parent; /* at end of routine */ - if (flags & EV_BACKCMD) { +#ifdef DO_SHAREDVFORK + /* It is essential that if DO_SHAREDVFORK is defined that the + * child's address space is actually shared with the parent as + * we rely on this. + */ + if (cmdentry.cmdtype == CMDNORMAL) { + pid_t pid; + + savelocalvars = localvars; + localvars = NULL; + vforked = 1; + switch (pid = vfork()) { + case -1: + TRACE(("Vfork failed, errno=%d\n", errno)); + INTON; + error("Cannot vfork"); + break; + case 0: + /* Make sure that exceptions only unwind to + * after the vfork(2) + */ + if (setjmp(jmploc.loc)) { + if (exception == EXSHELLPROC) { + /* We can't progress with the vfork, + * so, set vforked = 2 so the parent + * knows, and _exit(); + */ + vforked = 2; + _exit(0); + } else { + _exit(exerrno); + } + } + savehandler = handler; + handler = &jmploc; + listmklocal(varlist.list, VEXPORT | VNOFUNC); + forkchild(jp, cmd, mode, vforked); + break; + default: + handler = savehandler; /* restore from vfork(2) */ + poplocalvars(); + localvars = savelocalvars; + if (vforked == 2) { + vforked = 0; + + (void)waitpid(pid, NULL, 0); + /* We need to progress in a normal fork fashion */ + goto normal_fork; + } + vforked = 0; + forkparent(jp, cmd, mode, pid); + goto parent; + } + } else { +normal_fork: +#endif + if (forkshell(jp, cmd, mode) != 0) + goto parent; /* at end of routine */ FORCEINTON; +#ifdef DO_SHAREDVFORK + } +#endif + if (flags & EV_BACKCMD) { + if (!vforked) { + FORCEINTON; + } close(pip[0]); if (pip[1] != 1) { - dup2(pip[1], 1); + close(1); + copyfd(pip[1], 1, 1); close(pip[1]); } } @@ -752,8 +923,9 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) /* This is the child process if a fork occurred. */ /* Execute the command. */ - if (cmdentry.cmdtype == CMDFUNCTION) { -#if DEBUG + switch (cmdentry.cmdtype) { + case CMDFUNCTION: +#ifdef DEBUG trputs("Shell function: "); trargs(argv); #endif redirect(cmd->ncmd.redirect, REDIR_PUSH); @@ -768,9 +940,10 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) localvars = NULL; INTON; if (setjmp(jmploc.loc)) { - if (exception == EXSHELLPROC) - freeparam((struct shparam *)&saveparam); - else { + if (exception == EXSHELLPROC) { + freeparam((volatile struct shparam *) + &saveparam); + } else { freeparam(&shellparam); shellparam = saveparam; } @@ -781,13 +954,11 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) } savehandler = handler; handler = &jmploc; - for (sp = varlist.list ; sp ; sp = sp->next) - mklocal(sp->text); - funcnest++; - if (flags & EV_TESTED) - evaltree(cmdentry.u.func, EV_TESTED); - else - evaltree(cmdentry.u.func, 0); + listmklocal(varlist.list, VEXPORT); + /* stop shell blowing its stack */ + if (++funcnest > 1000) + error("too many nested function calls"); + evaltree(cmdentry.u.func, flags & EV_TESTED); funcnest--; INTOFF; poplocalvars(); @@ -803,104 +974,123 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) } if (flags & EV_EXIT) exitshell(exitstatus); - } else if (cmdentry.cmdtype == CMDBUILTIN) { -#if DEBUG + break; + + case CMDBUILTIN: + case CMDSPLBLTIN: +#ifdef DEBUG trputs("builtin command: "); trargs(argv); #endif - mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; + mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { memout.nleft = 0; memout.nextc = memout.buf; memout.bufsize = 64; mode |= REDIR_BACKQ; } - redirect(cmd->ncmd.redirect, mode); - savecmdname = commandname; - cmdenviron = varlist.list; e = -1; - if (setjmp(jmploc.loc)) { - e = exception; - exitstatus = (e == EXINT)? SIGINT+128 : 2; - goto cmddone; - } savehandler = handler; + savecmdname = commandname; handler = &jmploc; - commandname = argv[0]; - argptr = argv + 1; - optptr = NULL; /* initialize nextopt */ - exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); + temp_path = 0; + if (!setjmp(jmploc.loc)) { + /* We need to ensure the command hash table isn't + * corruped by temporary PATH assignments. + * However we must ensure the 'local' command works! + */ + if (path != pathval() && (cmdentry.u.bltin == hashcmd || + cmdentry.u.bltin == typecmd)) { + savelocalvars = localvars; + localvars = 0; + temp_path = 1; + mklocal(path - 5 /* PATH= */, 0); + } + redirect(cmd->ncmd.redirect, mode); + + /* exec is a special builtin, but needs this list... */ + cmdenviron = varlist.list; + /* we must check 'readonly' flag for all builtins */ + listsetvar(varlist.list, + cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); + commandname = argv[0]; + /* initialize nextopt */ + argptr = argv + 1; + optptr = NULL; + /* and getopt */ + optreset = 1; + optind = 1; + builtin_flags = flags; + exitstatus = cmdentry.u.bltin(argc, argv); + } else { + e = exception; + exitstatus = e == EXINT ? SIGINT + 128 : + e == EXEXEC ? exerrno : 2; + } + handler = savehandler; flushall(); -cmddone: - cmdenviron = NULL; out1 = &output; out2 = &errout; freestdout(); + if (temp_path) { + poplocalvars(); + localvars = savelocalvars; + } + cmdenviron = NULL; if (e != EXSHELLPROC) { commandname = savecmdname; - if (flags & EV_EXIT) { + if (flags & EV_EXIT) exitshell(exitstatus); - } } - handler = savehandler; if (e != -1) { if ((e != EXERROR && e != EXEXEC) - || cmdentry.u.index == BLTINCMD - || cmdentry.u.index == DOTCMD - || cmdentry.u.index == EVALCMD -#ifndef NO_HISTORY - || cmdentry.u.index == HISTCMD -#endif - || cmdentry.u.index == EXECCMD - || cmdentry.u.index == COMMANDCMD) + || cmdentry.cmdtype == CMDSPLBLTIN) exraise(e); FORCEINTON; } - if (cmdentry.u.index != EXECCMD) + if (cmdentry.u.bltin != execcmd) popredir(); if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; memout.buf = NULL; } - } else { -#if DEBUG + break; + + default: +#ifdef DEBUG trputs("normal command: "); trargs(argv); #endif - clearredir(); - redirect(cmd->ncmd.redirect, 0); - for (sp = varlist.list ; sp ; sp = sp->next) - setvareq(sp->text, VEXPORT|VSTACK); + redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0); + if (!vforked) + for (sp = varlist.list ; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); envp = environment(); - shellexec(argv, envp, pathval(), cmdentry.u.index); - /*NOTREACHED*/ + shellexec(argv, envp, path, cmdentry.u.index, vforked); + break; } goto out; parent: /* parent process gets here (if we forked) */ - if (mode == 0) { /* argument to fork */ - INTOFF; - exitstatus = waitforjob(jp, &realstatus); - INTON; - if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { - evalskip = SKIPBREAK; - skipcount = loopnest; - } - } else if (mode == 2) { + if (mode == FORK_FG) { /* argument to fork */ + exitstatus = waitforjob(jp); + } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; close(pip[1]); backcmd->jp = jp; } + FORCEINTON; out: if (lastarg) + /* dsl: I think this is intended to be used to support + * '_' in 'vi' command mode during line editing... + * However I implemented that within libedit itself. + */ setvar("_", lastarg, 0); - if (do_clearcmdentry) - clearcmdentry(0); popstackmark(&smark); } - /* * Search for a command. This is called before we fork so that the * location of the command will be available in the parent as well as @@ -913,7 +1103,7 @@ prehash(union node *n) { struct cmdentry entry; - if (n->type == NCMD && n->ncmd.args) + if (n && n->type == NCMD && n->ncmd.args) if (goodname(n->ncmd.args->narg.text)) find_command(n->ncmd.args->narg.text, &entry, 0, pathval()); @@ -927,19 +1117,17 @@ prehash(union node *n) */ /* - * No command given, or a bltin command with no arguments. Set the - * specified variables. + * No command given. */ int -bltincmd(int argc __unused, char **argv __unused) +bltincmd(int argc, char **argv) { - listsetvar(cmdenviron); /* * Preserve exitstatus of a previous possible redirection * as POSIX mandates */ - return exitstatus; + return back_exitstatus; } @@ -968,55 +1156,6 @@ breakcmd(int argc, char **argv) return 0; } -/* - * The `command' command. - */ -int -commandcmd(int argc, char **argv) -{ - static char stdpath[] = _PATH_STDPATH; - struct jmploc loc, *old; - struct strlist *sp; - char *path; - int ch; - - for (sp = cmdenviron; sp ; sp = sp->next) - setvareq(sp->text, VEXPORT|VSTACK); - path = pathval(); - - optind = optreset = 1; - opterr = 0; - while ((ch = getopt(argc, argv, "p")) != -1) { - switch (ch) { - case 'p': - path = stdpath; - break; - case '?': - default: - error("unknown option: -%c", optopt); - } - } - argc -= optind; - argv += optind; - - if (argc != 0) { - old = handler; - handler = &loc; - if (setjmp(handler->loc) == 0) - shellexec(argv, environment(), path, 0); - handler = old; - if (exception == EXEXEC) - exit(exerrno); - exraise(exception); - } - - /* - * Do nothing successfully if no command was specified; - * ksh also does this. - */ - exit(0); -} - /* * The return command. @@ -1025,29 +1164,31 @@ commandcmd(int argc, char **argv) int returncmd(int argc, char **argv) { - int ret = argc > 1 ? number(argv[1]) : oexitstatus; + int ret = argc > 1 ? number(argv[1]) : exitstatus; if (funcnest) { evalskip = SKIPFUNC; skipcount = 1; - } else { - /* skip the rest of the file */ + return ret; + } + else { + /* Do what ksh does; skip the rest of the file */ evalskip = SKIPFILE; skipcount = 1; + return ret; } - return ret; } int -falsecmd(int argc __unused, char **argv __unused) +falsecmd(int argc, char **argv) { return 1; } int -truecmd(int argc __unused, char **argv __unused) +truecmd(int argc, char **argv) { return 0; } @@ -1062,14 +1203,58 @@ execcmd(int argc, char **argv) iflag = 0; /* exit on error */ mflag = 0; optschanged(); - for (sp = cmdenviron; sp ; sp = sp->next) + for (sp = cmdenviron; sp; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); - shellexec(argv + 1, environment(), pathval(), 0); - + shellexec(argv + 1, environment(), pathval(), 0, 0); } return 0; } -/* - * $PchId: eval.c,v 1.7 2006/04/10 14:46:14 philip Exp $ - */ +static int +conv_time(clock_t ticks, char *seconds, size_t l) +{ + static clock_t tpm = 0; + clock_t mins; + int i; + + if (!tpm) + tpm = sysconf(_SC_CLK_TCK) * 60; + + mins = ticks / tpm; + snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm ); + + if (seconds[0] == '6' && seconds[1] == '0') { + /* 59.99995 got rounded up... */ + mins++; + strlcpy(seconds, "0.0", l); + return mins; + } + + /* suppress trailing zeros */ + i = strlen(seconds) - 1; + for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--) + seconds[i] = 0; + return mins; +} + +int +timescmd(int argc, char **argv) +{ + struct tms tms; + int u, s, cu, cs; + char us[8], ss[8], cus[8], css[8]; + + nextopt(""); + + times(&tms); + + u = conv_time(tms.tms_utime, us, sizeof(us)); + s = conv_time(tms.tms_stime, ss, sizeof(ss)); + cu = conv_time(tms.tms_cutime, cus, sizeof(cus)); + cs = conv_time(tms.tms_cstime, css, sizeof(css)); + + outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n", + u, us, s, ss, cu, cus, cs, css); + + return 0; +} diff --git a/minix/commands/ash/eval.h b/bin/sh/eval.h similarity index 82% rename from minix/commands/ash/eval.h rename to bin/sh/eval.h index b8b9415b9..7c0652af1 100644 --- a/minix/commands/ash/eval.h +++ b/bin/sh/eval.h @@ -1,3 +1,5 @@ +/* $NetBSD: eval.h,v 1.15 2008/02/15 17:26:06 matt Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,11 +32,11 @@ * SUCH DAMAGE. * * @(#)eval.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/eval.h,v 1.10 2004/04/06 20:06:51 markm Exp $ */ -extern char *commandname; /* currently executing command */ +extern const char *commandname; /* currently executing command */ extern int exitstatus; /* exit status of last command */ +extern int back_exitstatus; /* exit status of backquoted command */ extern struct strlist *cmdenviron; /* environment for builtin command */ @@ -45,18 +47,10 @@ struct backcmd { /* result of evalbackcmd */ struct job *jp; /* job structure for command */ }; -int evalcmd(int, char **); -void evalstring(char *); +void evalstring(char *, int); union node; /* BLETCH for ansi C */ void evaltree(union node *, int); void evalbackcmd(union node *, struct backcmd *); -int bltincmd(int, char **); -int breakcmd(int, char **); -int returncmd(int, char **); -int falsecmd(int, char **); -int truecmd(int, char **); -int execcmd(int, char **); -int commandcmd(int, char **); /* in_function returns nonzero if we are currently evaluating a function */ #define in_function() funcnest @@ -68,7 +62,3 @@ extern int evalskip; #define SKIPCONT 2 #define SKIPFUNC 3 #define SKIPFILE 4 - -/* - * $PchId: eval.h,v 1.3 2006/03/30 15:39:25 philip Exp $ - */ diff --git a/minix/commands/ash/exec.c b/bin/sh/exec.c similarity index 57% rename from minix/commands/ash/exec.c rename to bin/sh/exec.c index e6a12e9ad..739f51b7a 100644 --- a/minix/commands/ash/exec.c +++ b/bin/sh/exec.c @@ -1,3 +1,5 @@ +/* $NetBSD: exec.c,v 1.45 2013/11/01 16:49:02 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,21 +32,22 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; +#else +__RCSID("$NetBSD: exec.c,v 1.45 2013/11/01 16:49:02 christos Exp $"); #endif #endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/exec.c,v 1.24.2.1 2004/09/30 04:41:55 des Exp $"); -*/ #include #include +#include #include #include #include +#include #include /* @@ -97,13 +100,18 @@ STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ int exerrno = 0; /* Last exec error */ -STATIC void tryexec(char *, char **, char **); +STATIC void tryexec(char *, char **, char **, int); STATIC void printentry(struct tblentry *, int); -STATIC struct tblentry *cmdlookup(char *, int); +STATIC void clearcmdentry(int); +STATIC struct tblentry *cmdlookup(const char *, int); STATIC void delete_cmd_entry(void); -STATIC void addcmdentry(char *, struct cmdentry *); + +#ifndef BSD +STATIC void execinterp(char **, char **); +#endif +extern const char *const parsekwd[]; /* * Exec a program. Never returns. If you change this routine, you may @@ -111,19 +119,19 @@ STATIC void addcmdentry(char *, struct cmdentry *); */ void -shellexec(char **argv, char **envp, char *path, int index) +shellexec(char **argv, char **envp, const char *path, int idx, int vforked) { char *cmdname; int e; if (strchr(argv[0], '/') != NULL) { - tryexec(argv[0], argv, envp); + tryexec(argv[0], argv, envp, vforked); e = errno; } else { e = ENOENT; while ((cmdname = padvance(&path, argv[0])) != NULL) { - if (--index < 0 && pathopt == NULL) { - tryexec(cmdname, argv, envp); + if (--idx < 0 && pathopt == NULL) { + tryexec(cmdname, argv, envp, vforked); if (errno != ENOENT && errno != ENOTDIR) e = errno; } @@ -143,31 +151,137 @@ shellexec(char **argv, char **envp, char *path, int index) exerrno = 2; break; } - if (e == ENOENT || e == ENOTDIR) - exerror(EXEXEC, "%s: not found", argv[0]); - exerror(EXEXEC, "%s: %s", argv[0], strerror(e)); + TRACE(("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n", + argv[0], e, vforked, suppressint )); + exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); + /* NOTREACHED */ } STATIC void -tryexec(char *cmd, char **argv, char **envp) +tryexec(char *cmd, char **argv, char **envp, int vforked) { int e; +#ifndef BSD + char *p; +#endif +#ifdef SYSV + do { + execve(cmd, argv, envp); + } while (errno == EINTR); +#else execve(cmd, argv, envp); - +#endif e = errno; if (e == ENOEXEC) { + if (vforked) { + /* We are currently vfork(2)ed, so raise an + * exception, and evalcommand will try again + * with a normal fork(2). + */ + exraise(EXSHELLPROC); + } +#ifdef DEBUG + TRACE(("execve(cmd=%s) returned ENOEXEC\n", cmd)); +#endif initshellproc(); setinputfile(cmd, 0); commandname = arg0 = savestr(argv[0]); +#ifndef BSD + pgetc(); pungetc(); /* fill up input buffer */ + p = parsenextc; + if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { + argv[0] = cmd; + execinterp(argv, envp); + } +#endif setparam(argv + 1); exraise(EXSHELLPROC); - /*NOTREACHED*/ } errno = e; } + +#ifndef BSD +/* + * Execute an interpreter introduced by "#!", for systems where this + * feature has not been built into the kernel. If the interpreter is + * the shell, return (effectively ignoring the "#!"). If the execution + * of the interpreter fails, exit. + * + * This code peeks inside the input buffer in order to avoid actually + * reading any input. It would benefit from a rewrite. + */ + +#define NEWARGS 5 + +STATIC void +execinterp(char **argv, char **envp) +{ + int n; + char *inp; + char *outp; + char c; + char *p; + char **ap; + char *newargs[NEWARGS]; + int i; + char **ap2; + char **new; + + n = parsenleft - 2; + inp = parsenextc + 2; + ap = newargs; + for (;;) { + while (--n >= 0 && (*inp == ' ' || *inp == '\t')) + inp++; + if (n < 0) + goto bad; + if ((c = *inp++) == '\n') + break; + if (ap == &newargs[NEWARGS]) +bad: error("Bad #! line"); + STARTSTACKSTR(outp); + do { + STPUTC(c, outp); + } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); + STPUTC('\0', outp); + n++, inp--; + *ap++ = grabstackstr(outp); + } + if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ + p = newargs[0]; + for (;;) { + if (equal(p, "sh") || equal(p, "ash")) { + return; + } + while (*p != '/') { + if (*p == '\0') + goto break2; + p++; + } + p++; + } +break2:; + } + i = (char *)ap - (char *)newargs; /* size in bytes */ + if (i == 0) + error("Bad #! line"); + for (ap2 = argv ; *ap2++ != NULL ; ); + new = ckmalloc(i + ((char *)ap2 - (char *)argv)); + ap = newargs, ap2 = new; + while ((i -= sizeof (char **)) >= 0) + *ap2++ = *ap++; + ap = argv; + while (*ap2++ = *ap++); + shellexec(new, envp, pathval(), 0); + /* NOTREACHED */ +} +#endif + + + /* * Do a path search. The variable path (passed by reference) should be * set to the start of the path before the first call; padvance will update @@ -178,13 +292,14 @@ tryexec(char *cmd, char **argv, char **envp) * NULL. */ -char *pathopt; +const char *pathopt; char * -padvance(char **path, char *name) +padvance(const char **path, const char *name) { - char *p, *q; - char *start; + const char *p; + char *q; + const char *start; int len; if (*path == NULL) @@ -219,7 +334,7 @@ padvance(char **path, char *name) int -hashcmd(int argc __unused, char **argv __unused) +hashcmd(int argc, char **argv) { struct tblentry **pp; struct tblentry *cmdp; @@ -239,7 +354,7 @@ hashcmd(int argc __unused, char **argv __unused) if (*argptr == NULL) { for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { - if (cmdp->cmdtype == CMDNORMAL) + if (verbose || cmdp->cmdtype == CMDNORMAL) printentry(cmdp, verbose); } } @@ -250,14 +365,12 @@ hashcmd(int argc __unused, char **argv __unused) && (cmdp->cmdtype == CMDNORMAL || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) delete_cmd_entry(); - find_command(name, &entry, 1, pathval()); + find_command(name, &entry, DO_ERR, pathval()); if (verbose) { if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ cmdp = cmdlookup(name, 0); if (cmdp != NULL) printentry(cmdp, verbose); - else - outfmt(&errout, "%s: not found\n", name); } flushall(); } @@ -270,34 +383,40 @@ hashcmd(int argc __unused, char **argv __unused) STATIC void printentry(struct tblentry *cmdp, int verbose) { - int index; - char *path; + int idx; + const char *path; char *name; - if (cmdp->cmdtype == CMDNORMAL) { - index = cmdp->param.index; + switch (cmdp->cmdtype) { + case CMDNORMAL: + idx = cmdp->param.index; path = pathval(); do { name = padvance(&path, cmdp->cmdname); stunalloc(name); - } while (--index >= 0); + } while (--idx >= 0); out1str(name); - } else if (cmdp->cmdtype == CMDBUILTIN) { + break; + case CMDSPLBLTIN: + out1fmt("special builtin %s", cmdp->cmdname); + break; + case CMDBUILTIN: out1fmt("builtin %s", cmdp->cmdname); - } else if (cmdp->cmdtype == CMDFUNCTION) { + break; + case CMDFUNCTION: out1fmt("function %s", cmdp->cmdname); if (verbose) { + struct procstat ps; INTOFF; - name = commandtext(cmdp->param.func); - out1c(' '); - out1str(name); - ckfree(name); + commandtext(&ps, cmdp->param.func); INTON; + out1str("() { "); + out1str(ps.cmd); + out1str("; }"); } -#if DEBUG - } else { - error("internal error: cmdtype %d", cmdp->cmdtype); -#endif + break; + default: + error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype); } if (cmdp->rehash) out1c('*'); @@ -312,36 +431,78 @@ printentry(struct tblentry *cmdp, int verbose) */ void -find_command(char *name, struct cmdentry *entry, int printerr, char *path) +find_command(char *name, struct cmdentry *entry, int act, const char *path) { - struct tblentry *cmdp; - int index; + struct tblentry *cmdp, loc_cmd; + int idx; int prev; char *fullname; struct stat statb; int e; - int i; + int (*bltin)(int,char **); - /* If name contains a slash, don't use the hash table */ + /* If name contains a slash, don't use PATH or hash table */ if (strchr(name, '/') != NULL) { + if (act & DO_ABS) { + while (stat(name, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + entry->cmdtype = CMDUNKNOWN; + entry->u.index = -1; + return; + } + entry->cmdtype = CMDNORMAL; + entry->u.index = -1; + return; + } entry->cmdtype = CMDNORMAL; entry->u.index = 0; return; } - /* If name is in the table, and not invalidated by cd, we're done */ - if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) - goto success; + if (path != pathval()) + act |= DO_ALTPATH; + + if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL) + act |= DO_ALTBLTIN; + + /* If name is in the table, check answer will be ok */ + if ((cmdp = cmdlookup(name, 0)) != NULL) { + do { + switch (cmdp->cmdtype) { + case CMDNORMAL: + if (act & DO_ALTPATH) { + cmdp = NULL; + continue; + } + break; + case CMDFUNCTION: + if (act & DO_NOFUNC) { + cmdp = NULL; + continue; + } + break; + case CMDBUILTIN: + if ((act & DO_ALTBLTIN) || builtinloc >= 0) { + cmdp = NULL; + continue; + } + break; + } + /* if not invalidated by cd, we're done */ + if (cmdp->rehash == 0) + goto success; + } while (0); + } /* If %builtin not in path, check for builtin next */ - if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { - INTOFF; - cmdp = cmdlookup(name, 1); - cmdp->cmdtype = CMDBUILTIN; - cmdp->param.index = i; - INTON; - goto success; - } + if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) && + (bltin = find_builtin(name)) != 0) + goto builtin_success; /* We have to search path. */ prev = -1; /* where to start */ @@ -353,35 +514,35 @@ find_command(char *name, struct cmdentry *entry, int printerr, char *path) } e = ENOENT; - index = -1; + idx = -1; loop: while ((fullname = padvance(&path, name)) != NULL) { stunalloc(fullname); - index++; + idx++; if (pathopt) { if (prefix("builtin", pathopt)) { - if ((i = find_builtin(name)) < 0) + if ((bltin = find_builtin(name)) == 0) goto loop; - INTOFF; - cmdp = cmdlookup(name, 1); - cmdp->cmdtype = CMDBUILTIN; - cmdp->param.index = i; - INTON; - goto success; + goto builtin_success; } else if (prefix("func", pathopt)) { /* handled below */ } else { - goto loop; /* ignore unimplemented options */ + /* ignore unimplemented options */ + goto loop; } } /* if rehash, don't redo absolute path names */ - if (fullname[0] == '/' && index <= prev) { - if (index < prev) + if (fullname[0] == '/' && idx <= prev) { + if (idx < prev) goto loop; TRACE(("searchexec \"%s\": no change\n", name)); goto success; } - if (stat(fullname, &statb) < 0) { + while (stat(fullname, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif if (errno != ENOENT && errno != ENOTDIR) e = errno; goto loop; @@ -390,14 +551,19 @@ loop: if (!S_ISREG(statb.st_mode)) goto loop; if (pathopt) { /* this is a %func directory */ + if (act & DO_NOFUNC) + goto loop; stalloc(strlen(fullname) + 1); readcmdfile(fullname); - if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) + if ((cmdp = cmdlookup(name, 0)) == NULL || + cmdp->cmdtype != CMDFUNCTION) error("%s not defined in %s", name, fullname); stunalloc(fullname); goto success; } #ifdef notdef + /* XXX this code stops root executing stuff, and is buggy + if you need a group from the group list. */ if (statb.st_uid == geteuid()) { if ((statb.st_mode & 0100) == 0) goto loop; @@ -411,9 +577,13 @@ loop: #endif TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); INTOFF; - cmdp = cmdlookup(name, 1); + if (act & DO_ALTPATH) { + stalloc(strlen(fullname) + 1); + cmdp = &loc_cmd; + } else + cmdp = cmdlookup(name, 1); cmdp->cmdtype = CMDNORMAL; - cmdp->param.index = index; + cmdp->param.index = idx; INTON; goto success; } @@ -421,19 +591,30 @@ loop: /* We failed. If there was an entry for this command, delete it */ if (cmdp) delete_cmd_entry(); - if (printerr) { - if (e == ENOENT || e == ENOTDIR) - outfmt(out2, "%s: not found\n", name); - else - outfmt(out2, "%s: %s\n", name, strerror(e)); - } + if (act & DO_ERR) + outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); entry->cmdtype = CMDUNKNOWN; return; +builtin_success: + INTOFF; + if (act & DO_ALTPATH) + cmdp = &loc_cmd; + else + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + /* DO_NOFUNC must have been set */ + cmdp = &loc_cmd; + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.bltin = bltin; + INTON; success: - cmdp->rehash = 0; - entry->cmdtype = cmdp->cmdtype; - entry->u = cmdp->param; + if (cmdp) { + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; + } else + entry->cmdtype = CMDUNKNOWN; } @@ -443,15 +624,48 @@ success: */ int -find_builtin(char *name) +(*find_builtin(char *name))(int, char **) { const struct builtincmd *bp; for (bp = builtincmd ; bp->name ; bp++) { - if (*bp->name == *name && equal(bp->name, name)) - return bp->code; + if (*bp->name == *name + && (*name == '%' || equal(bp->name, name))) + return bp->builtin; + } + return 0; +} + +int +(*find_splbltin(char *name))(int, char **) +{ + const struct builtincmd *bp; + + for (bp = splbltincmd ; bp->name ; bp++) { + if (*bp->name == *name && equal(bp->name, name)) + return bp->builtin; + } + return 0; +} + +/* + * At shell startup put special builtins into hash table. + * ensures they are executed first (see posix). + * We stop functions being added with the same name + * (as they are impossible to call) + */ + +void +hash_special_builtins(void) +{ + const struct builtincmd *bp; + struct tblentry *cmdp; + + for (bp = splbltincmd ; bp->name ; bp++) { + cmdp = cmdlookup(bp->name, 1); + cmdp->cmdtype = CMDSPLBLTIN; + cmdp->param.bltin = bp->builtin; } - return -1; } @@ -479,27 +693,28 @@ hashcd(void) /* + * Fix command hash table when PATH changed. * Called before PATH is changed. The argument is the new value of PATH; - * pathval() still returns the old value at this point. Called with - * interrupts off. + * pathval() still returns the old value at this point. + * Called with interrupts off. */ void changepath(const char *newval) { const char *old, *new; - int index; + int idx; int firstchange; int bltin; old = pathval(); new = newval; firstchange = 9999; /* assume no change */ - index = 0; + idx = 0; bltin = -1; for (;;) { if (*old != *new) { - firstchange = index; + firstchange = idx; if ((*old == '\0' && *new == ':') || (*old == ':' && *new == '\0')) firstchange++; @@ -508,9 +723,9 @@ changepath(const char *newval) if (*new == '\0') break; if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) - bltin = index; + bltin = idx; if (*new == ':') { - index++; + idx++; } new++, old++; } @@ -528,7 +743,7 @@ changepath(const char *newval) * PATH which has changed. */ -void +STATIC void clearcmdentry(int firstchange) { struct tblentry **tblp; @@ -559,7 +774,13 @@ clearcmdentry(int firstchange) */ #ifdef mkinit -INCLUDE "exec.h" +MKINIT void deletefuncs(void); +MKINIT void hash_special_builtins(void); + +INIT { + hash_special_builtins(); +} + SHELLPROC { deletefuncs(); } @@ -598,14 +819,14 @@ deletefuncs(void) * entry. */ -STATIC struct tblentry **lastcmdentry; +struct tblentry **lastcmdentry; STATIC struct tblentry * -cmdlookup(char *name, int add) +cmdlookup(const char *name, int add) { int hashval; - char *p; + const char *p; struct tblentry *cmdp; struct tblentry **pp; @@ -652,23 +873,42 @@ delete_cmd_entry(void) +#ifdef notdef +void +getcmdentry(char *name, struct cmdentry *entry) +{ + struct tblentry *cmdp = cmdlookup(name, 0); + + if (cmdp) { + entry->u = cmdp->param; + entry->cmdtype = cmdp->cmdtype; + } else { + entry->cmdtype = CMDUNKNOWN; + entry->u.index = 0; + } +} +#endif + + /* * Add a new command entry, replacing any existing command entry for - * the same name. + * the same name - except special builtins. */ -static void +STATIC void addcmdentry(char *name, struct cmdentry *entry) { struct tblentry *cmdp; INTOFF; cmdp = cmdlookup(name, 1); - if (cmdp->cmdtype == CMDFUNCTION) { - freefunc(cmdp->param.func); + if (cmdp->cmdtype != CMDSPLBLTIN) { + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; } - cmdp->cmdtype = entry->cmdtype; - cmdp->param = entry->u; INTON; } @@ -699,16 +939,17 @@ unsetfunc(char *name) { struct tblentry *cmdp; - if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { + if ((cmdp = cmdlookup(name, 0)) != NULL && + cmdp->cmdtype == CMDFUNCTION) { freefunc(cmdp->param.func); delete_cmd_entry(); - return (0); } - return (0); + return 0; } /* * Locate and print what a word is... + * also used for 'command -[v|V]' */ int @@ -716,76 +957,115 @@ typecmd(int argc, char **argv) { struct cmdentry entry; struct tblentry *cmdp; - char **pp; + const char * const *pp; struct alias *ap; - int i; - int error = 0; - extern char *const parsekwd[]; + int err = 0; + char *arg; + int c; + int V_flag = 0; + int v_flag = 0; + int p_flag = 0; - for (i = 1; i < argc; i++) { - out1str(argv[i]); + while ((c = nextopt("vVp")) != 0) { + switch (c) { + case 'v': v_flag = 1; break; + case 'V': V_flag = 1; break; + case 'p': p_flag = 1; break; + } + } + + if (p_flag && (v_flag || V_flag)) + error("cannot specify -p with -v or -V"); + + while ((arg = *argptr++)) { + if (!v_flag) + out1str(arg); /* First look at the keywords */ - for (pp = (char **)parsekwd; *pp; pp++) - if (**pp == *argv[i] && equal(*pp, argv[i])) + for (pp = parsekwd; *pp; pp++) + if (**pp == *arg && equal(*pp, arg)) break; if (*pp) { - out1str(" is a shell keyword\n"); + if (v_flag) + err = 1; + else + out1str(" is a shell keyword\n"); continue; } /* Then look at the aliases */ - if ((ap = lookupalias(argv[i], 1)) != NULL) { - out1fmt(" is an alias for %s\n", ap->val); + if ((ap = lookupalias(arg, 1)) != NULL) { + if (!v_flag) + out1fmt(" is an alias for \n"); + out1fmt("%s\n", ap->val); continue; } /* Then check if it is a tracked alias */ - if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { + if ((cmdp = cmdlookup(arg, 0)) != NULL) { entry.cmdtype = cmdp->cmdtype; entry.u = cmdp->param; - } - else { + } else { /* Finally use brute force */ - find_command(argv[i], &entry, 0, pathval()); + find_command(arg, &entry, DO_ABS, pathval()); } switch (entry.cmdtype) { case CMDNORMAL: { - if (strchr(argv[i], '/') == NULL) { - char *path = pathval(), *name; + if (strchr(arg, '/') == NULL) { + const char *path = pathval(); + char *name; int j = entry.u.index; do { - name = padvance(&path, argv[i]); + name = padvance(&path, arg); stunalloc(name); } while (--j >= 0); - out1fmt(" is%s %s\n", - cmdp ? " a tracked alias for" : "", name); + if (!v_flag) + out1fmt(" is%s ", + cmdp ? " a tracked alias for" : ""); + out1fmt("%s\n", name); } else { - if (access(argv[i], X_OK) == 0) - out1fmt(" is %s\n", argv[i]); - else - out1fmt(": %s\n", strerror(errno)); + if (access(arg, X_OK) == 0) { + if (!v_flag) + out1fmt(" is "); + out1fmt("%s\n", arg); + } else { + if (!v_flag) + out1fmt(": %s\n", + strerror(errno)); + else + err = 126; + } } - break; + break; } case CMDFUNCTION: - out1str(" is a shell function\n"); + if (!v_flag) + out1str(" is a shell function\n"); + else + out1fmt("%s\n", arg); break; case CMDBUILTIN: - out1str(" is a shell builtin\n"); + if (!v_flag) + out1str(" is a shell builtin\n"); + else + out1fmt("%s\n", arg); + break; + + case CMDSPLBLTIN: + if (!v_flag) + out1str(" is a special shell builtin\n"); + else + out1fmt("%s\n", arg); break; default: - out1str(": not found\n"); - error |= 127; + if (!v_flag) + out1str(": not found\n"); + err = 127; break; } } - return error; + return err; } - -/* - * $PchId: exec.c,v 1.6 2006/04/10 14:47:03 philip Exp $ - */ diff --git a/minix/commands/ash/exec.h b/bin/sh/exec.h similarity index 61% rename from minix/commands/ash/exec.h rename to bin/sh/exec.h index eef4c9533..c0fbd51ba 100644 --- a/minix/commands/ash/exec.h +++ b/bin/sh/exec.h @@ -1,3 +1,5 @@ +/* $NetBSD: exec.h,v 1.23 2012/03/15 02:02:20 joerg Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,41 +32,45 @@ * SUCH DAMAGE. * * @(#)exec.h 8.3 (Berkeley) 6/8/95 - * $FreeBSD: src/bin/sh/exec.h,v 1.12 2004/04/06 20:06:51 markm Exp $ */ /* values of cmdtype */ -#define CMDUNKNOWN -1 /* no entry in table for command */ -#define CMDNORMAL 0 /* command is an executable program */ -#define CMDBUILTIN 1 /* command is a shell builtin */ -#define CMDFUNCTION 2 /* command is a shell function */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDFUNCTION 1 /* command is a shell function */ +#define CMDBUILTIN 2 /* command is a shell builtin */ +#define CMDSPLBLTIN 3 /* command is a special shell builtin */ struct cmdentry { int cmdtype; union param { int index; + int (*bltin)(int, char**); union node *func; } u; }; -extern char *pathopt; /* set by padvance */ -extern int exerrno; /* last exec error */ +/* action to find_command() */ +#define DO_ERR 0x01 /* prints errors */ +#define DO_ABS 0x02 /* checks absolute paths */ +#define DO_NOFUNC 0x04 /* don't return shell functions, for command */ +#define DO_ALTPATH 0x08 /* using alternate path */ +#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ -void shellexec(char **, char **, char *, int); -char *padvance(char **, char *); -int hashcmd(int, char **); -void find_command(char *, struct cmdentry *, int, char *); -int find_builtin(char *); +extern const char *pathopt; /* set by padvance */ + +void shellexec(char **, char **, const char *, int, int) __dead; +char *padvance(const char **, const char *); +void find_command(char *, struct cmdentry *, int, const char *); +int (*find_builtin(char *))(int, char **); +int (*find_splbltin(char *))(int, char **); void hashcd(void); void changepath(const char *); void deletefuncs(void); +void getcmdentry(char *, struct cmdentry *); +void addcmdentry(char *, struct cmdentry *); void defun(char *, union node *); int unsetfunc(char *); -int typecmd(int, char **); -void clearcmdentry(int); - -/* - * $PchId: exec.h,v 1.5 2006/04/10 14:47:34 philip Exp $ - */ +void hash_special_builtins(void); diff --git a/minix/commands/ash/expand.c b/bin/sh/expand.c similarity index 77% rename from minix/commands/ash/expand.c rename to bin/sh/expand.c index 74309c4c9..aea3cf190 100644 --- a/minix/commands/ash/expand.c +++ b/bin/sh/expand.c @@ -1,3 +1,5 @@ +/* $NetBSD: expand.c,v 1.90 2013/10/06 21:05:50 ast Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,26 +32,25 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; +#else +__RCSID("$NetBSD: expand.c,v 1.90 2013/10/06 21:05:50 ast Exp $"); #endif #endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/expand.c,v 1.46 2004/04/06 20:06:51 markm Exp $"); -*/ #include +#include #include #include #include #include #include -#include #include +#include #include -#include /* * Routines to expand arguments to commands. We have to deal with @@ -65,13 +66,13 @@ __FBSDID("$FreeBSD: src/bin/sh/expand.c,v 1.46 2004/04/06 20:06:51 markm Exp $") #include "parser.h" #include "jobs.h" #include "options.h" +#include "builtins.h" #include "var.h" #include "input.h" #include "output.h" #include "memalloc.h" #include "error.h" #include "mystring.h" -#include "arith.h" #include "show.h" /* @@ -83,15 +84,15 @@ struct ifsregion { struct ifsregion *next; /* next region in list */ int begoff; /* offset of start of region */ int endoff; /* offset of end of region */ - int nulonly; /* search for nul bytes only */ + int inquotes; /* search for nul bytes only */ }; -STATIC char *expdest; /* output of current string */ -STATIC struct nodelist *argbackq; /* list of back quote expressions */ -STATIC struct ifsregion ifsfirst; /* first struct in list of ifs regions */ -STATIC struct ifsregion *ifslastp; /* last struct in list */ -STATIC struct arglist exparg; /* holds expanded arg list */ +char *expdest; /* output of current string */ +struct nodelist *argbackq; /* list of back quote expressions */ +struct ifsregion ifsfirst; /* first struct in list of ifs regions */ +struct ifsregion *ifslastp; /* last struct in list */ +struct arglist exparg; /* holds expanded arg list */ STATIC void argstr(char *, int); STATIC char *exptilde(char *, int); @@ -99,10 +100,11 @@ STATIC void expbackq(union node *, int, int); STATIC int subevalvar(char *, char *, int, int, int, int); STATIC char *evalvar(char *, int); STATIC int varisset(char *, int); -STATIC void varvalue(char *, int, int); +STATIC void varvalue(char *, int, int, int); STATIC void recordregion(int, int, int); STATIC void removerecordregions(int); STATIC void ifsbreakup(char *, struct arglist *); +STATIC void ifsfree(void); STATIC void expandmeta(struct strlist *, int); STATIC void expmeta(char *, char *); STATIC void addfname(char *); @@ -110,30 +112,16 @@ STATIC struct strlist *expsort(struct strlist *); STATIC struct strlist *msort(struct strlist *, int); STATIC int pmatch(char *, char *, int); STATIC char *cvtnum(int, char *); -STATIC int collate_range_cmp(int, int); -STATIC void expari(int); - -STATIC int -collate_range_cmp(int c1, int c2) -{ - static char s1[2], s2[2]; - - s1[0] = c1; - s2[0] = c2; - return (strcoll(s1, s2)); -} /* * Expand shell variables and backquotes inside a here document. - * union node *arg the document - * int fd; where to write the expanded version */ void expandhere(union node *arg, int fd) { herefd = fd; - expandarg(arg, (struct arglist *)NULL, 0); + expandarg(arg, NULL, 0); xwrite(fd, stackblock(), expdest - stackblock()); } @@ -178,14 +166,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag) *exparg.lastp = sp; exparg.lastp = &sp->next; } - while (ifsfirst.next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifsfirst.next->next; - ckfree(ifsfirst.next); - ifsfirst.next = ifsp; - INTON; - } + ifsfree(); *exparg.lastp = NULL; if (exparg.list) { *arglist->lastp = exparg.list; @@ -196,31 +177,40 @@ expandarg(union node *arg, struct arglist *arglist, int flag) /* - * Perform variable and command substitution. If EXP_FULL is set, output CTLESC - * characters to allow for further processing. Otherwise treat - * $@ like $* since no splitting will be performed. + * Perform variable and command substitution. + * If EXP_FULL is set, output CTLESC characters to allow for further processing. + * Otherwise treat $@ like $* since no splitting will be performed. */ STATIC void argstr(char *p, int flag) { char c; - int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ + int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ int firsteq = 1; + const char *ifs = NULL; + int ifs_split = EXP_IFS_SPLIT; + + if (flag & EXP_IFS_SPLIT) + ifs = ifsset() ? ifsval() : " \t\n"; if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(p, flag); for (;;) { switch (c = *p++) { case '\0': - case CTLENDVAR: /* ??? */ - goto breakloop; + case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ + return; case CTLQUOTEMARK: /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_FULL) != 0) STPUTC(c, expdest); + ifs_split = 0; + break; + case CTLQUOTEEND: + ifs_split = EXP_IFS_SPLIT; break; case CTLESC: if (quotes) @@ -229,7 +219,7 @@ argstr(char *p, int flag) STPUTC(c, expdest); break; case CTLVAR: - p = evalvar(p, flag); + p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split)); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: @@ -258,9 +248,14 @@ argstr(char *p, int flag) break; default: STPUTC(c, expdest); + if (flag & ifs_split && strchr(ifs, c) != NULL) { + /* We need to get the output split here... */ + recordregion(expdest - stackblock() - 1, + expdest - stackblock(), 0); + } + break; } } -breakloop:; } STATIC char * @@ -268,8 +263,8 @@ exptilde(char *p, int flag) { char c, *startp = p; struct passwd *pw; - char *home; - int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); + const char *home; + int quotes = flag & (EXP_FULL | EXP_CASE); while ((c = *p) != '\0') { switch(c) { @@ -350,42 +345,44 @@ removerecordregions(int endoff) ifslastp->endoff = endoff; } + /* * Expand arithmetic expression. Backup to start of expression, * evaluate, place result in (backed up) result, adjust string position. */ -STATIC void +void expari(int flag) { char *p, *start; - int result; + intmax_t result; + int adjustment; int begoff; - int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); + int quotes = flag & (EXP_FULL | EXP_CASE); int quoted; + /* ifsfree(); */ /* * This routine is slightly over-complicated for * efficiency. First we make sure there is * enough space for the result, which may be bigger - * than the expression if we add exponentiation. Next we + * than the expression if we add exponentation. Next we * scan backwards looking for the start of arithmetic. If the * next previous character is a CTLESC character, then we * have to rescan starting from the beginning since CTLESC * characters have to be processed left to right. */ -#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10 -#error "integers with more than 10 digits are not supported" -#endif - CHECKSTRSPACE(12 - 2, expdest); +/* SPACE_NEEDED is enough for all digits, plus possible "-", plus 2 (why?) */ +#define SPACE_NEEDED ((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 1 + 2) + CHECKSTRSPACE((int)(SPACE_NEEDED - 2), expdest); USTPUTC('\0', expdest); start = stackblock(); - p = expdest - 2; - while (p >= start && *p != CTLARI) + p = expdest - 1; + while (*p != CTLARI && p >= start) --p; - if (p < start || *p != CTLARI) + if (*p != CTLARI) error("missing CTLARI (shouldn't happen)"); - if (p > start && *(p - 1) == CTLESC) + if (p > start && *(p-1) == CTLESC) for (p = start; *p != CTLARI; p++) if (*p == CTLESC) p++; @@ -399,13 +396,15 @@ expari(int flag) if (quotes) rmescapes(p+2); result = arith(p+2); - fmtstr(p, 12, "%d", result); + fmtstr(p, SPACE_NEEDED, "%"PRIdMAX, result); + while (*p++) ; + if (quoted == 0) recordregion(begoff, p - 1 - start, 0); - result = expdest - p + 1; - STADJUST(-result, expdest); + adjustment = expdest - p + 1; + STADJUST(-adjustment, expdest); } @@ -427,7 +426,7 @@ expbackq(union node *cmd, int quoted, int flag) int startloc = dest - stackblock(); char const *syntax = quoted? DQSYNTAX : BASESYNTAX; int saveherefd; - int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); + int quotes = flag & (EXP_FULL | EXP_CASE); int nnl; INTOFF; @@ -447,7 +446,6 @@ expbackq(union node *cmd, int quoted, int flag) p = in.buf; lastc = '\0'; nnl = 0; - /* Don't copy trailing newlines */ for (;;) { if (--in.nleft < 0) { if (in.fd < 0) @@ -461,16 +459,17 @@ expbackq(union node *cmd, int quoted, int flag) } lastc = *p++; if (lastc != '\0') { - if (quotes && syntax[(int)lastc] == CCTL) - STPUTC(CTLESC, dest); - if (lastc == '\n') { + if (lastc == '\n') nnl++; - } else { + else { + CHECKSTRSPACE(nnl + 2, dest); while (nnl > 0) { nnl--; - STPUTC('\n', dest); + USTPUTC('\n', dest); } - STPUTC(lastc, dest); + if (quotes && syntax[(int)lastc] == CCTL) + USTPUTC(CTLESC, dest); + USTPUTC(lastc, dest); } } } @@ -480,12 +479,12 @@ expbackq(union node *cmd, int quoted, int flag) if (in.buf) ckfree(in.buf); if (in.jp) - exitstatus = waitforjob(in.jp, (int *)NULL); + back_exitstatus = waitforjob(in.jp); if (quoted == 0) recordregion(startloc, dest - stackblock(), 0); TRACE(("evalbackq: size=%d: \"%.*s\"\n", - (dest - stackblock()) - startloc, - (dest - stackblock()) - startloc, + (int)((dest - stackblock()) - startloc), + (int)((dest - stackblock()) - startloc), stackblock() + startloc)); expdest = dest; INTON; @@ -494,8 +493,7 @@ expbackq(union node *cmd, int quoted, int flag) STATIC int -subevalvar(char *p, char *str, int strloc, int subtype, int startloc, - int varflags) +subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags) { char *startp; char *loc = NULL; @@ -503,10 +501,21 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int c = 0; int saveherefd = herefd; struct nodelist *saveargbackq = argbackq; - int amount; + int amount, how; herefd = -1; - argstr(p, 0); + switch (subtype) { + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + how = (varflags & VSQUOTE) ? 0 : EXP_CASE; + break; + default: + how = 0; + break; + } + argstr(p, how); STACKSTRNUL(expdest); herefd = saveherefd; argbackq = saveargbackq; @@ -520,31 +529,28 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, amount = startp - expdest; STADJUST(amount, expdest); varflags &= ~VSNUL; - if (c != 0) - *loc = c; return 1; case VSQUESTION: if (*p != CTLENDVAR) { outfmt(&errout, "%s\n", startp); - error((char *)NULL); + error(NULL); } - error("%.*s: parameter %snot set", (int)(p - str - 1), + error("%.*s: parameter %snot set", + (int)(p - str - 1), str, (varflags & VSNUL) ? "null or " : nullstr); - return 0; + /* NOTREACHED */ case VSTRIMLEFT: for (loc = startp; loc < str; loc++) { c = *loc; *loc = '\0'; - if (patmatch(str, startp, varflags & VSQUOTE)) { - *loc = c; + if (patmatch(str, startp, varflags & VSQUOTE)) goto recordleft; - } *loc = c; if ((varflags & VSQUOTE) && *loc == CTLESC) - loc++; + loc++; } return 0; @@ -552,10 +558,8 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, for (loc = str - 1; loc >= startp;) { c = *loc; *loc = '\0'; - if (patmatch(str, startp, varflags & VSQUOTE)) { - *loc = c; + if (patmatch(str, startp, varflags & VSQUOTE)) goto recordleft; - } *loc = c; loc--; if ((varflags & VSQUOTE) && loc > startp && @@ -570,12 +574,9 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, return 0; case VSTRIMRIGHT: - for (loc = str - 1; loc >= startp;) { - if (patmatch(str, loc, varflags & VSQUOTE)) { - amount = loc - expdest; - STADJUST(amount, expdest); - return 1; - } + for (loc = str - 1; loc >= startp;) { + if (patmatch(str, loc, varflags & VSQUOTE)) + goto recordright; loc--; if ((varflags & VSQUOTE) && loc > startp && *(loc - 1) == CTLESC) { @@ -590,27 +591,31 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, case VSTRIMRIGHTMAX: for (loc = startp; loc < str - 1; loc++) { - if (patmatch(str, loc, varflags & VSQUOTE)) { - amount = loc - expdest; - STADJUST(amount, expdest); - return 1; - } + if (patmatch(str, loc, varflags & VSQUOTE)) + goto recordright; if ((varflags & VSQUOTE) && *loc == CTLESC) - loc++; + loc++; } return 0; - default: abort(); } recordleft: + *loc = c; amount = ((str - 1) - (loc - startp)) - expdest; STADJUST(amount, expdest); while (loc != str - 1) *startp++ = *loc++; return 1; + +recordright: + amount = loc - expdest; + STADJUST(amount, expdest); + STPUTC('\0', expdest); + STADJUST(-1, expdest); + return 1; } @@ -632,31 +637,37 @@ evalvar(char *p, int flag) int special; int startloc; int varlen; - int easy; - int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); + int apply_ifs; + int quotes = flag & (EXP_FULL | EXP_CASE); - varflags = *p++; + varflags = (unsigned char)*p++; subtype = varflags & VSTYPE; var = p; - special = 0; - if (! is_name(*p)) - special = 1; + special = !is_name(*p); p = strchr(p, '=') + 1; + again: /* jump here after setting a variable with ${var=text} */ - if (special) { + if (varflags & VSLINENO) { + set = 1; + special = 0; + val = var; + p[-1] = '\0'; + } else if (special) { set = varisset(var, varflags & VSNUL); val = NULL; } else { - val = bltinlookup(var, 1); + val = lookupvar(var); if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { val = NULL; set = 0; } else set = 1; } + varlen = 0; startloc = expdest - stackblock(); - if (!set && uflag) { + + if (!set && uflag && *var != '@' && *var != '*') { switch (subtype) { case VSNORMAL: case VSTRIMLEFT: @@ -664,14 +675,16 @@ again: /* jump here after setting a variable with ${var=text} */ case VSTRIMRIGHT: case VSTRIMRIGHTMAX: case VSLENGTH: - error("%.*s: parameter not set", (int)(p - var - 1), - var); + error("%.*s: parameter not set", + (int)(p - var - 1), var); + /* NOTREACHED */ } } + if (set && subtype != VSPLUS) { /* insert the value of the variable */ if (special) { - varvalue(var, varflags & VSQUOTE, flag & EXP_FULL); + varvalue(var, varflags & VSQUOTE, subtype, flag); if (subtype == VSLENGTH) { varlen = expdest - stackblock() - startloc; STADJUST(-varlen, expdest); @@ -683,11 +696,9 @@ again: /* jump here after setting a variable with ${var=text} */ if (subtype == VSLENGTH) { for (;*val; val++) varlen++; - } - else { + } else { while (*val) { - if (quotes && - syntax[(int)*val] == CCTL) + if (quotes && syntax[(int)*val] == CCTL) STPUTC(CTLESC, expdest); STPUTC(*val++, expdest); } @@ -696,34 +707,45 @@ again: /* jump here after setting a variable with ${var=text} */ } } - if (subtype == VSPLUS) - set = ! set; - - easy = ((varflags & VSQUOTE) == 0 || - (*var == '@' && shellparam.nparam != 1)); + if (flag & EXP_IN_QUOTES) + apply_ifs = 0; + else if (varflags & VSQUOTE) { + if (*var == '@' && shellparam.nparam != 1) + apply_ifs = 1; + else { + /* + * Mark so that we don't apply IFS if we recurse through + * here expanding $bar from "${foo-$bar}". + */ + flag |= EXP_IN_QUOTES; + apply_ifs = 0; + } + } else + apply_ifs = 1; switch (subtype) { case VSLENGTH: expdest = cvtnum(varlen, expdest); - goto record; + break; case VSNORMAL: - if (!easy) - break; -record: - recordregion(startloc, expdest - stackblock(), - varflags & VSQUOTE); break; case VSPLUS: + set = !set; + /* FALLTHROUGH */ case VSMINUS: if (!set) { - argstr(p, flag); - break; + argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0)); + /* + * ${x-a b c} doesn't get split, but removing the + * 'apply_ifs = 0' apparently breaks ${1+"$@"}.. + * ${x-'a b' c} should generate 2 args. + */ + /* We should have marked stuff already */ + apply_ifs = 0; } - if (easy) - goto record; break; case VSTRIMLEFT: @@ -745,29 +767,33 @@ record: } /* Remove any recorded regions beyond start of variable */ removerecordregions(startloc); - goto record; + apply_ifs = 1; + break; case VSASSIGN: case VSQUESTION: - if (!set) { - if (subevalvar(p, var, 0, subtype, startloc, varflags)) { - varflags &= ~VSNUL; - /* - * Remove any recorded regions beyond - * start of variable - */ - removerecordregions(startloc); - goto again; - } + if (set) break; + if (subevalvar(p, var, 0, subtype, startloc, varflags)) { + varflags &= ~VSNUL; + /* + * Remove any recorded regions beyond + * start of variable + */ + removerecordregions(startloc); + goto again; } - if (easy) - goto record; + apply_ifs = 0; break; default: abort(); } + p[-1] = '='; /* recover overwritten '=' */ + + if (apply_ifs) + recordregion(startloc, expdest - stackblock(), + varflags & VSQUOTE); if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; @@ -798,7 +824,6 @@ record: STATIC int varisset(char *name, int nulok) { - if (*name == '!') return backgndpid != -1; else if (*name == '@' || *name == '*') { @@ -838,19 +863,18 @@ varisset(char *name, int nulok) */ STATIC void -varvalue(char *name, int quoted, int allow_split) +varvalue(char *name, int quoted, int subtype, int flag) { int num; char *p; int i; - extern int oexitstatus; char sep; char **ap; char const *syntax; #define STRTODEST(p) \ do {\ - if (allow_split) { \ + if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \ syntax = quoted? DQSYNTAX : BASESYNTAX; \ while (*p) { \ if (syntax[(int)*p] == CCTL) \ @@ -868,7 +892,7 @@ varvalue(char *name, int quoted, int allow_split) num = rootpid; goto numvar; case '?': - num = oexitstatus; + num = exitstatus; goto numvar; case '#': num = shellparam.nparam; @@ -879,21 +903,22 @@ numvar: expdest = cvtnum(num, expdest); break; case '-': - for (i = 0 ; i < NOPTS ; i++) { - if (optlist[i].val) + for (i = 0; optlist[i].name; i++) { + if (optlist[i].val && optlist[i].letter) STPUTC(optlist[i].letter, expdest); } break; case '@': - if (allow_split && quoted) { + if (flag & EXP_FULL && quoted) { for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { STRTODEST(p); if (*ap) + /* A NUL separates args inside "" */ STPUTC('\0', expdest); } break; } - /* FALLTHROUGH */ + /* fall through */ case '*': if (ifsset() != 0) sep = ifsval()[0]; @@ -924,18 +949,24 @@ numvar: /* - * Record the the fact that we have to scan this region of the + * Record the fact that we have to scan this region of the * string for IFS characters. */ STATIC void -recordregion(int start, int end, int nulonly) +recordregion(int start, int end, int inquotes) { struct ifsregion *ifsp; if (ifslastp == NULL) { ifsp = &ifsfirst; } else { + if (ifslastp->endoff == start + && ifslastp->inquotes == inquotes) { + /* extend previous area */ + ifslastp->endoff = end; + return; + } ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); ifslastp->next = ifsp; } @@ -943,7 +974,7 @@ recordregion(int start, int end, int nulonly) ifslastp->next = NULL; ifslastp->begoff = start; ifslastp->endoff = end; - ifslastp->nulonly = nulonly; + ifslastp->inquotes = inquotes; } @@ -961,77 +992,89 @@ ifsbreakup(char *string, struct arglist *arglist) char *start; char *p; char *q; - char *ifs; - int ifsspc; - int nulonly; - + const char *ifs; + const char *ifsspc; + int had_param_ch = 0; start = string; - ifsspc = 0; - nulonly = 0; - if (ifslastp != NULL) { - ifsp = &ifsfirst; - do { - p = string + ifsp->begoff; - nulonly = ifsp->nulonly; - ifs = nulonly ? nullstr : - ( ifsset() ? ifsval() : " \t\n" ); - ifsspc = 0; - while (p < string + ifsp->endoff) { - q = p; - if (*p == CTLESC) + + if (ifslastp == NULL) { + /* Return entire argument, IFS doesn't apply to any of it */ + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + return; + } + + ifs = ifsset() ? ifsval() : " \t\n"; + + for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { + p = string + ifsp->begoff; + while (p < string + ifsp->endoff) { + had_param_ch = 1; + q = p; + if (*p == CTLESC) + p++; + if (ifsp->inquotes) { + /* Only NULs (should be from "$@") end args */ + if (*p != 0) { p++; - if (strchr(ifs, *p)) { - if (!nulonly) - ifsspc = (strchr(" \t\n", *p) != NULL); - /* Ignore IFS whitespace at start */ - if (q == start && ifsspc) { - p++; - start = p; - continue; - } - *q = '\0'; - sp = (struct strlist *)stalloc(sizeof *sp); - sp->text = start; - *arglist->lastp = sp; - arglist->lastp = &sp->next; + continue; + } + ifsspc = NULL; + } else { + if (!strchr(ifs, *p)) { + p++; + continue; + } + had_param_ch = 0; + ifsspc = strchr(" \t\n", *p); + + /* Ignore IFS whitespace at start */ + if (q == start && ifsspc != NULL) { p++; - if (!nulonly) { - for (;;) { - if (p >= string + ifsp->endoff) { - break; - } - q = p; - if (*p == CTLESC) - p++; - if (strchr(ifs, *p) == NULL ) { - p = q; - break; - } else if (strchr(" \t\n",*p) == NULL) { - if (ifsspc) { - p++; - ifsspc = 0; - } else { - p = q; - break; - } - } else - p++; - } - } start = p; - } else - p++; + continue; + } } - } while ((ifsp = ifsp->next) != NULL); - if (*start || (!ifsspc && start > string && - (nulonly || 1))) { + + /* Save this argument... */ + *q = '\0'; sp = (struct strlist *)stalloc(sizeof *sp); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; + p++; + + if (ifsspc != NULL) { + /* Ignore further trailing IFS whitespace */ + for (; p < string + ifsp->endoff; p++) { + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p) == NULL) { + p = q; + break; + } + if (strchr(" \t\n", *p) == NULL) { + p++; + break; + } + } + } + start = p; } - } else { + } + + /* + * Save anything left as an argument. + * Traditionally we have treated 'IFS=':'; set -- x$IFS' as + * generating 2 arguments, the second of which is empty. + * Some recent clarification of the Posix spec say that it + * should only generate one.... + */ + if (had_param_ch || *start != 0) { sp = (struct strlist *)stalloc(sizeof *sp); sp->text = start; *arglist->lastp = sp; @@ -1039,6 +1082,21 @@ ifsbreakup(char *string, struct arglist *arglist) } } +STATIC void +ifsfree(void) +{ + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + ifslastp = NULL; + ifsfirst.next = NULL; +} + /* @@ -1046,11 +1104,11 @@ ifsbreakup(char *string, struct arglist *arglist) * should be escapes. The results are stored in the list exparg. */ -STATIC char *expdir; +char *expdir; STATIC void -expandmeta(struct strlist *str, int flag __unused) +expandmeta(struct strlist *str, int flag) { char *p; struct strlist **savelastp; @@ -1107,6 +1165,7 @@ STATIC void expmeta(char *enddir, char *name) { char *p; + const char *cp; char *q; char *start; char *endname; @@ -1124,7 +1183,7 @@ expmeta(char *enddir, char *name) metaflag = 1; else if (*p == '[') { q = p + 1; - if (*q == '!' || *q == '^') + if (*q == '!') q++; for (;;) { while (*q == CTLQUOTEMARK) @@ -1164,7 +1223,7 @@ expmeta(char *enddir, char *name) if (*p == '\0') break; } - if (metaflag == 0 || stat(expdir, &statb) >= 0) + if (metaflag == 0 || lstat(expdir, &statb) >= 0) addfname(expdir); return; } @@ -1180,14 +1239,14 @@ expmeta(char *enddir, char *name) } } if (enddir == expdir) { - p = "."; + cp = "."; } else if (enddir == expdir + 1 && *expdir == '/') { - p = "/"; + cp = "/"; } else { - p = expdir; + cp = expdir; enddir[-1] = '\0'; } - if ((dirp = opendir(p)) == NULL) + if ((dirp = opendir(cp)) == NULL) return; if (enddir != expdir) enddir[-1] = '/'; @@ -1213,9 +1272,8 @@ expmeta(char *enddir, char *name) scopy(dp->d_name, enddir); addfname(expdir); } else { - char *q; - for (p = enddir, q = dp->d_name; - (*p++ = *q++) != '\0';) + for (p = enddir, cp = dp->d_name; + (*p++ = *cp++) != '\0';) continue; p[-1] = '/'; expmeta(p, endname); @@ -1380,7 +1438,7 @@ pmatch(char *pattern, char *string, int squoted) char chr; endp = p; - if (*endp == '!' || *endp == '^') + if (*endp == '!') endp++; for (;;) { while (*endp == CTLQUOTEMARK) @@ -1393,7 +1451,7 @@ pmatch(char *pattern, char *string, int squoted) break; } invert = 0; - if (*p == '!' || *p == '^') { + if (*p == '!') { invert++; p++; } @@ -1415,9 +1473,7 @@ pmatch(char *pattern, char *string, int squoted) p++; if (*p == CTLESC) p++; - if ( collate_range_cmp(chr, c) >= 0 - && collate_range_cmp(chr, *p) <= 0 - ) + if (chr >= c && chr <= *p) found = 1; p++; } else { @@ -1532,17 +1588,15 @@ wordexpcmd(int argc, char **argv) size_t len; int i; - out1fmt("%08x", argc - 1); + out1fmt("%d", argc - 1); + out1c('\0'); for (i = 1, len = 0; i < argc; i++) len += strlen(argv[i]); - out1fmt("%08x", (int)len); + out1fmt("%zu", len); + out1c('\0'); for (i = 1; i < argc; i++) { out1str(argv[i]); out1c('\0'); } - return (0); + return (0); } - -/* - * $PchId: expand.c,v 1.6 2006/04/10 14:52:06 philip Exp $ - */ diff --git a/minix/commands/ash/expand.h b/bin/sh/expand.h similarity index 87% rename from minix/commands/ash/expand.h rename to bin/sh/expand.h index f53b61c9d..76a012aa9 100644 --- a/minix/commands/ash/expand.h +++ b/bin/sh/expand.h @@ -1,3 +1,5 @@ +/* $NetBSD: expand.h,v 1.19 2012/12/22 20:15:22 dsl Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,9 +32,10 @@ * SUCH DAMAGE. * * @(#)expand.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/expand.h,v 1.12 2004/04/06 20:06:51 markm Exp $ */ +#include + struct strlist { struct strlist *next; char *text; @@ -52,23 +55,19 @@ struct arglist { #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ +#define EXP_IFS_SPLIT 0x20 /* need to record arguments for ifs breakup */ +#define EXP_IN_QUOTES 0x40 /* don't set EXP_IFS_SPLIT again */ union node; void expandhere(union node *, int); void expandarg(union node *, struct arglist *, int); +void expari(int); int patmatch(char *, char *, int); void rmescapes(char *); int casematch(union node *, char *); -int wordexpcmd(int, char **); /* From arith.y */ -int arith(char *); -int arith_assign(char *, arith_t); -int expcmd(int , char **); +intmax_t arith(const char *); void arith_lex_reset(void); - - -/* - * $PchId: expand.h,v 1.4 2006/03/30 14:50:52 philip Exp $ - */ +int yylex(void); diff --git a/minix/commands/ash/funcs/cmv b/bin/sh/funcs/cmv old mode 100755 new mode 100644 similarity index 86% rename from minix/commands/ash/funcs/cmv rename to bin/sh/funcs/cmv index 0e701e5cb..667f846e0 --- a/minix/commands/ash/funcs/cmv +++ b/bin/sh/funcs/cmv @@ -1,3 +1,4 @@ +# $NetBSD: cmv,v 1.7 1995/05/11 21:31:05 christos Exp $ # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # @@ -12,6 +13,10 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. @@ -29,7 +34,6 @@ # SUCH DAMAGE. # # @(#)cmv 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/funcs/cmv,v 1.7 2004/04/06 20:06:53 markm Exp $ # Conditional move--don't replace an existing file. @@ -44,6 +48,3 @@ cmv() { fi /bin/mv "$1" "$2" } - -# -# $PchId: cmv,v 1.2 2006/03/29 10:43:18 philip Exp $ diff --git a/minix/commands/ash/funcs/dirs b/bin/sh/funcs/dirs old mode 100755 new mode 100644 similarity index 88% rename from minix/commands/ash/funcs/dirs rename to bin/sh/funcs/dirs index 337c70945..68bb317bb --- a/minix/commands/ash/funcs/dirs +++ b/bin/sh/funcs/dirs @@ -1,3 +1,4 @@ +# $NetBSD: dirs,v 1.7 1995/05/11 21:31:08 christos Exp $ # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # @@ -12,6 +13,10 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. @@ -29,7 +34,6 @@ # SUCH DAMAGE. # # @(#)dirs 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/funcs/dirs,v 1.7 2004/04/06 20:06:53 markm Exp $ # pushd, popd, and dirs --- written by Chris Bertin # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris @@ -68,6 +72,3 @@ dirs () { echo "`pwd` $DSTACK" return 0 } - -# -# $PchId: dirs,v 1.2 2006/03/29 10:43:18 philip Exp $ diff --git a/minix/commands/ash/funcs/kill b/bin/sh/funcs/kill old mode 100755 new mode 100644 similarity index 86% rename from minix/commands/ash/funcs/kill rename to bin/sh/funcs/kill index 3c858ca20..75b0180bf --- a/minix/commands/ash/funcs/kill +++ b/bin/sh/funcs/kill @@ -1,3 +1,4 @@ +# $NetBSD: kill,v 1.7 1995/05/11 21:31:10 christos Exp $ # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # @@ -12,6 +13,10 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. @@ -29,7 +34,6 @@ # SUCH DAMAGE. # # @(#)kill 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/funcs/kill,v 1.7 2004/04/06 20:06:53 markm Exp $ # Convert job names to process ids and then run /bin/kill. @@ -44,6 +48,3 @@ kill() { done /bin/kill $args } - -# -# $PchId: kill,v 1.2 2006/03/29 10:43:18 philip Exp $ diff --git a/minix/commands/ash/funcs/login b/bin/sh/funcs/login old mode 100755 new mode 100644 similarity index 85% rename from minix/commands/ash/funcs/login rename to bin/sh/funcs/login index d4720ed1b..7ae08b2b0 --- a/minix/commands/ash/funcs/login +++ b/bin/sh/funcs/login @@ -1,3 +1,4 @@ +# $NetBSD: login,v 1.7 1995/05/11 21:31:11 christos Exp $ # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # @@ -12,6 +13,10 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. @@ -29,10 +34,6 @@ # SUCH DAMAGE. # # @(#)login 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/funcs/login,v 1.7 2004/04/06 20:06:53 markm Exp $ # replaces the login builtin in the BSD shell login () exec login "$@" - -# -# $PchId: login,v 1.2 2006/03/29 10:43:18 philip Exp $ diff --git a/minix/commands/ash/funcs/newgrp b/bin/sh/funcs/newgrp old mode 100755 new mode 100644 similarity index 85% rename from minix/commands/ash/funcs/newgrp rename to bin/sh/funcs/newgrp index c2ab4fd18..796a4f184 --- a/minix/commands/ash/funcs/newgrp +++ b/bin/sh/funcs/newgrp @@ -1,3 +1,4 @@ +# $NetBSD: newgrp,v 1.7 1995/05/11 21:31:12 christos Exp $ # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # @@ -12,6 +13,10 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. @@ -29,9 +34,5 @@ # SUCH DAMAGE. # # @(#)newgrp 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/funcs/newgrp,v 1.7 2004/04/06 20:06:53 markm Exp $ newgrp() exec newgrp "$@" - -# -# $PchId: newgrp,v 1.2 2006/03/29 10:43:18 philip Exp $ diff --git a/minix/commands/ash/funcs/popd b/bin/sh/funcs/popd old mode 100755 new mode 100644 similarity index 88% rename from minix/commands/ash/funcs/popd rename to bin/sh/funcs/popd index a2654e573..b2b65d5c2 --- a/minix/commands/ash/funcs/popd +++ b/bin/sh/funcs/popd @@ -1,3 +1,4 @@ +# $NetBSD: popd,v 1.7 1995/05/11 21:31:13 christos Exp $ # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # @@ -12,6 +13,10 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. @@ -29,7 +34,6 @@ # SUCH DAMAGE. # # @(#)popd 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/funcs/popd,v 1.7 2004/04/06 20:06:53 markm Exp $ # pushd, popd, and dirs --- written by Chris Bertin # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris @@ -68,6 +72,3 @@ dirs () { echo "`pwd` $DSTACK" return 0 } - -# -# $PchId: popd,v 1.2 2006/03/29 10:43:18 philip Exp $ diff --git a/minix/commands/ash/funcs/pushd b/bin/sh/funcs/pushd old mode 100755 new mode 100644 similarity index 88% rename from minix/commands/ash/funcs/pushd rename to bin/sh/funcs/pushd index 799bef4ee..b3930380c --- a/minix/commands/ash/funcs/pushd +++ b/bin/sh/funcs/pushd @@ -1,3 +1,4 @@ +# $NetBSD: pushd,v 1.7 1995/05/11 21:31:15 christos Exp $ # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # @@ -12,6 +13,10 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. @@ -29,7 +34,6 @@ # SUCH DAMAGE. # # @(#)pushd 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/funcs/pushd,v 1.7 2004/04/06 20:06:53 markm Exp $ # pushd, popd, and dirs --- written by Chris Bertin # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris @@ -68,6 +72,3 @@ dirs () { echo "`pwd` $DSTACK" return 0 } - -# -# $PchId: pushd,v 1.2 2006/03/29 10:43:18 philip Exp $ diff --git a/minix/commands/ash/funcs/suspend b/bin/sh/funcs/suspend old mode 100755 new mode 100644 similarity index 85% rename from minix/commands/ash/funcs/suspend rename to bin/sh/funcs/suspend index 3d354e1e4..8a4197dfe --- a/minix/commands/ash/funcs/suspend +++ b/bin/sh/funcs/suspend @@ -1,3 +1,4 @@ +# $NetBSD: suspend,v 1.7 1995/05/11 21:31:17 christos Exp $ # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # @@ -12,6 +13,10 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. @@ -29,13 +34,9 @@ # SUCH DAMAGE. # # @(#)suspend 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/funcs/suspend,v 1.7 2004/04/06 20:06:53 markm Exp $ suspend() { local - set +j kill -TSTP 0 } - -# -# $PchId: suspend,v 1.2 2006/03/29 10:43:18 philip Exp $ diff --git a/minix/commands/ash/histedit.c b/bin/sh/histedit.c similarity index 76% rename from minix/commands/ash/histedit.c rename to bin/sh/histedit.c index e341c56d8..8a77ebfba 100644 --- a/minix/commands/ash/histedit.c +++ b/bin/sh/histedit.c @@ -1,3 +1,5 @@ +/* $NetBSD: histedit.c,v 1.45 2012/03/20 18:42:29 matt Exp $ */ + /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,20 +32,17 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: histedit.c,v 1.45 2012/03/20 18:42:29 matt Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/histedit.c,v 1.26 2004/04/06 20:06:51 markm Exp $"); -*/ -#include -#ifndef NO_PATHS_H +#include #include -#endif #include #include #include @@ -54,14 +53,13 @@ __FBSDID("$FreeBSD: src/bin/sh/histedit.c,v 1.26 2004/04/06 20:06:51 markm Exp $ #include "parser.h" #include "var.h" #include "options.h" +#include "builtins.h" #include "main.h" #include "output.h" #include "mystring.h" -#include "builtins.h" -#if !defined(NO_HISTORY) #include "myhistedit.h" -#include "complete.h" #include "error.h" +#ifndef SMALL #include "eval.h" #include "memalloc.h" @@ -71,11 +69,14 @@ __FBSDID("$FreeBSD: src/bin/sh/histedit.c,v 1.26 2004/04/06 20:06:51 markm Exp $ History *hist; /* history cookie */ EditLine *el; /* editline cookie */ int displayhist; -static FILE *el_in, *el_out, *el_err; +static FILE *el_in, *el_out; +unsigned char _el_fn_complete(EditLine *, int); -STATIC char *fc_replace(const char *, char *, char *); -STATIC int not_fcnumber(char *); -STATIC int str_to_event(char *, int); +STATIC const char *fc_replace(const char *, char *, char *); + +#ifdef DEBUG +extern FILE *tracefile; +#endif /* * Set history and editing status. Called whenever the status may @@ -84,10 +85,11 @@ STATIC int str_to_event(char *, int); void histedit(void) { + FILE *el_err; #define editing (Eflag || Vflag) - if (iflag) { + if (iflag == 1) { if (!hist) { /* * turn history on @@ -105,20 +107,37 @@ histedit(void) /* * turn editing on */ + char *term, *shname; + INTOFF; if (el_in == NULL) el_in = fdopen(0, "r"); - if (el_err == NULL) - el_err = fdopen(1, "w"); if (el_out == NULL) el_out = fdopen(2, "w"); - if (el_in == NULL || el_err == NULL || el_out == NULL) + if (el_in == NULL || el_out == NULL) goto bad; - el = el_init(arg0, el_in, el_out, el_err); + el_err = el_out; +#if DEBUG + if (tracefile) + el_err = tracefile; +#endif + term = lookupvar("TERM"); + if (term) + setenv("TERM", term, 1); + else + unsetenv("TERM"); + shname = arg0; + if (shname[0] == '-') + shname++; + el = el_init(shname, el_in, el_out, el_err); if (el != NULL) { if (hist) el_set(el, EL_HIST, history, hist); el_set(el, EL_PROMPT, getprompt); + el_set(el, EL_SIGNAL, 1); + el_set(el, EL_ADDFN, "rl-complete", + "ReadLine compatible completion function", + _el_fn_complete); } else { bad: out2str("sh: can't initialize editing\n"); @@ -131,27 +150,13 @@ bad: INTON; } if (el) { + el_source(el, NULL); if (Vflag) el_set(el, EL_EDITOR, "vi"); else if (Eflag) el_set(el, EL_EDITOR, "emacs"); - el_set(el, EL_ADDFN, "ed-do-complete", - "Complete Argument", complete); - el_set(el, EL_ADDFN, "ed-list-complete", - "List Argument Completions", complete_list); - el_set(el, EL_ADDFN, "ed-maybe-complete", - "Complete Argument Or List Completions", - complete_or_list); - el_set(el, EL_ADDFN, "ed-expand", - "Expand Completions", complete_expand); - el_set(el, EL_BIND, "^I", "ed-maybe-complete", NULL); - el_set(el, EL_BIND, "-a", "=", "ed-list-complete", - NULL); - el_set(el, EL_BIND, "-a", "\\\\", "ed-do-complete", - NULL); - el_set(el, EL_BIND, "-a", "*", "ed-expand", - NULL); - el_source(el, NULL); + el_set(el, EL_BIND, "^I", + tabcomplete ? "rl-complete" : "ed-insert", NULL); } } else { INTOFF; @@ -169,8 +174,7 @@ bad: void -sethistsize(hs) - const char *hs; +sethistsize(const char *hs) { int histsize; HistEvent he; @@ -180,40 +184,63 @@ sethistsize(hs) (histsize = atoi(hs)) < 0) histsize = 100; history(hist, &he, H_SETSIZE, histsize); + history(hist, &he, H_SETUNIQUE, 1); } } +void +setterm(const char *term) +{ + if (el != NULL && term != NULL) + if (el_set(el, EL_TERMINAL, term) != 0) { + outfmt(out2, "sh: Can't set terminal type %s\n", term); + outfmt(out2, "sh: Using dumb terminal settings.\n"); + } +} + +int +inputrc(int argc, char **argv) +{ + if (argc != 2) { + out2str("usage: inputrc file\n"); + return 1; + } + if (el != NULL) { + if (el_source(el, argv[1])) { + out2str("inputrc: failed\n"); + return 1; + } else + return 0; + } else { + out2str("sh: inputrc ignored, not editing\n"); + return 1; + } +} + +/* + * This command is provided since POSIX decided to standardize + * the Korn shell fc command. Oh well... + */ int histcmd(int argc, char **argv) { int ch; - char *editor = NULL; + const char * volatile editor = NULL; HistEvent he; - int lflg = 0, nflg = 0, rflg = 0, sflg = 0; + int lflg = 0; + volatile int nflg = 0, rflg = 0, sflg = 0; int i, retval; - char *firststr, *laststr; + const char *firststr, *laststr; int first, last, direction; - char *pat = NULL, *repl; + char *pat = NULL, *repl; /* ksh "fc old=new" crap */ static int active = 0; struct jmploc jmploc; struct jmploc *volatile savehandler; - char editfile[PATH_MAX]; + char editfile[MAXPATHLEN + 1]; FILE *efp; - int oldhistnum; #ifdef __GNUC__ - /* Avoid longjmp clobbering */ - (void) &editor; - (void) &lflg; - (void) &nflg; - (void) &rflg; - (void) &sflg; - (void) &firststr; - (void) &laststr; - (void) &pat; - (void) &repl; - (void) &efp; - (void) &argc; - (void) &argv; + repl = NULL; /* XXX gcc4 */ + efp = NULL; /* XXX gcc4 */ #endif if (hist == NULL) @@ -223,12 +250,11 @@ histcmd(int argc, char **argv) error("missing history argument"); optreset = 1; optind = 1; /* initialize getopt */ - opterr = 0; while (not_fcnumber(argv[optind]) && (ch = getopt(argc, argv, ":e:lnrs")) != -1) switch ((char)ch) { case 'e': - editor = optarg; + editor = optionarg; break; case 'l': lflg = 1; @@ -244,9 +270,11 @@ histcmd(int argc, char **argv) break; case ':': error("option -%c expects argument", optopt); + /* NOTREACHED */ case '?': default: error("unknown option: -%c", optopt); + /* NOTREACHED */ } argc -= optind, argv += optind; @@ -260,6 +288,7 @@ histcmd(int argc, char **argv) * Catch interrupts to reset active counter and * cleanup temp files. */ + savehandler = handler; if (setjmp(jmploc.loc)) { active = 0; if (*editfile) @@ -267,7 +296,6 @@ histcmd(int argc, char **argv) handler = savehandler; longjmp(handler->loc, 1); } - savehandler = handler; handler = &jmploc; if (++active > MAXHISTLOOPS) { active = 0; @@ -298,6 +326,13 @@ histcmd(int argc, char **argv) *repl++ = '\0'; argc--, argv++; } + + /* + * If -s is specified, accept only one operand + */ + if (sflg && argc >= 2) + error("too many args"); + /* * determine [first] and [last] */ @@ -316,6 +351,7 @@ histcmd(int argc, char **argv) break; default: error("too many args"); + /* NOTREACHED */ } /* * Turn into event numbers. @@ -341,7 +377,7 @@ histcmd(int argc, char **argv) if (editor) { int fd; INTOFF; /* easier */ - sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP); + snprintf(editfile, sizeof(editfile), "%s_shXXXXXX", _PATH_TMP); if ((fd = mkstemp(editfile)) < 0) error("can't create temporary file %s", editfile); if ((efp = fdopen(fd, "w")) == NULL) { @@ -366,34 +402,29 @@ histcmd(int argc, char **argv) out1fmt("%5d ", he.num); out1str(he.str); } else { - char *s = pat ? - fc_replace(he.str, pat, repl) : (char *)he.str; + const char *s = pat ? + fc_replace(he.str, pat, repl) : he.str; if (sflg) { if (displayhist) { out2str(s); } - evalstring(s); + + evalstring(strcpy(stalloc(strlen(s) + 1), s), 0); if (displayhist && hist) { /* * XXX what about recursive and * relative histnums. */ - oldhistnum = he.num; history(hist, &he, H_ENTER, s); - /* - * XXX H_ENTER moves the internal - * cursor, set it back to the current - * entry. - */ - retval = history(hist, &he, - H_NEXT_EVENT, oldhistnum); } + + break; } else fputs(s, efp); } /* - * At end? (if we were to loose last, we'd sure be + * At end? (if we were to lose last, we'd sure be * messed up). */ if (he.num == last) @@ -405,7 +436,7 @@ histcmd(int argc, char **argv) fclose(efp); editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); sprintf(editcmd, "%s %s", editor, editfile); - evalstring(editcmd); /* XXX - should use no JC command */ + evalstring(editcmd, 0); /* XXX - should use no JC command */ INTON; readcmdfile(editfile); /* XXX - should read back - quick tst */ unlink(editfile); @@ -418,7 +449,7 @@ histcmd(int argc, char **argv) return 0; } -STATIC char * +STATIC const char * fc_replace(const char *s, char *p, char *r) { char *dest; @@ -440,21 +471,21 @@ fc_replace(const char *s, char *p, char *r) return (dest); } -STATIC int +int not_fcnumber(char *s) { if (s == NULL) - return (0); - if (*s == '-') - s++; + return 0; + if (*s == '-') + s++; return (!is_number(s)); } -STATIC int -str_to_event(char *str, int last) +int +str_to_event(const char *str, int last) { HistEvent he; - char *s = str; + const char *s = str; int relative = 0; int i, retval; @@ -481,7 +512,8 @@ str_to_event(char *str, int last) * the notion of first and last is * backwards to that of the history package */ - retval = history(hist, &he, last ? H_FIRST : H_LAST); + retval = history(hist, &he, + last ? H_FIRST : H_LAST); } } if (retval == -1) @@ -497,37 +529,17 @@ str_to_event(char *str, int last) } return (he.num); } - -int -bindcmd(int argc, char **argv) -{ - - if (el == NULL) - error("line editing is disabled"); - return (el_parse(el, argc, argv)); -} - #else -#include "error.h" - int histcmd(int argc, char **argv) { - error("not compiled with history support"); - /*NOTREACHED*/ - return (0); + /* NOTREACHED */ } - int -bindcmd(int argc, char **argv) +inputrc(int argc, char **argv) { - - error("not compiled with line editing support"); - return (0); + error("not compiled with history support"); + /* NOTREACHED */ } -#endif /* !NO_HISTORY */ - -/* - * $PchId: histedit.c,v 1.6 2006/04/10 14:52:58 philip Exp $ - */ +#endif diff --git a/minix/commands/ash/init.h b/bin/sh/init.h similarity index 89% rename from minix/commands/ash/init.h rename to bin/sh/init.h index 0cfb9c4ca..60d924e99 100644 --- a/minix/commands/ash/init.h +++ b/bin/sh/init.h @@ -1,3 +1,5 @@ +/* $NetBSD: init.h,v 1.10 2003/08/07 09:05:32 agc Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,13 +32,8 @@ * SUCH DAMAGE. * * @(#)init.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/init.h,v 1.8 2004/04/06 20:06:51 markm Exp $ */ void init(void); void reset(void); void initshellproc(void); - -/* - * $PchId: init.h,v 1.3 2006/03/30 14:31:06 philip Exp $ - */ diff --git a/minix/commands/ash/input.c b/bin/sh/input.c similarity index 80% rename from minix/commands/ash/input.c rename to bin/sh/input.c index 15fa04ccd..50e5cfdaf 100644 --- a/minix/commands/ash/input.c +++ b/bin/sh/input.c @@ -1,3 +1,5 @@ +/* $NetBSD: input.c,v 1.46 2013/10/30 08:38:40 mrg Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,24 +32,22 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; +#else +__RCSID("$NetBSD: input.c,v 1.46 2013/10/30 08:38:40 mrg Exp $"); #endif #endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/input.c,v 1.22 2004/04/06 20:06:51 markm Exp $"); -*/ -#include #include /* defines BUFSIZ */ #include #include #include +#include #include #include -#include /* * This file implements the input routines used by the parser. @@ -64,10 +64,6 @@ __FBSDID("$FreeBSD: src/bin/sh/input.c,v 1.22 2004/04/06 20:06:51 markm Exp $"); #include "alias.h" #include "parser.h" #include "myhistedit.h" -#include "redir.h" -#include "trap.h" - -static void popstring(void); #define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ @@ -91,7 +87,7 @@ struct parsefile { int linno; /* current line */ int fd; /* file descriptor (or -1 if string) */ int nleft; /* number of chars left in this line */ - int lleft; /* number of lines left in this buffer */ + int lleft; /* number of chars left in this buffer */ char *nextc; /* next char in buffer */ char *buf; /* input buffer */ struct strpush *strpush; /* for pushing strings at this level */ @@ -100,27 +96,24 @@ struct parsefile { int plinno = 1; /* input line number */ -MKINIT int parsenleft; /* copy of parsefile->nleft */ +int parsenleft; /* copy of parsefile->nleft */ MKINIT int parselleft; /* copy of parsefile->lleft */ char *parsenextc; /* copy of parsefile->nextc */ MKINIT struct parsefile basepf; /* top level input file */ -char basebuf[BUFSIZ]; /* buffer for top level input file */ -STATIC struct parsefile *parsefile = &basepf; /* current input file */ +MKINIT char basebuf[BUFSIZ]; /* buffer for top level input file */ +struct parsefile *parsefile = &basepf; /* current input file */ int init_editline = 0; /* editline library initialized? */ -int whichprompt; /* -1 == PSE, 1 == PS1, 2 == PS2 */ - -EditLine *el; /* cookie for editline package */ +int whichprompt; /* 1 == PS1, 2 == PS2 */ STATIC void pushfile(void); static int preadfd(void); #ifdef mkinit +INCLUDE INCLUDE "input.h" INCLUDE "error.h" INIT { - extern char basebuf[]; - basepf.nextc = basepf.buf = basebuf; } @@ -180,35 +173,40 @@ static int preadfd(void) { int nr; - parsenextc = parsefile->buf; + char *buf = parsefile->buf; + parsenextc = buf; -#if !defined(NO_HISTORY) - if (el != NULL && gotwinch) { - gotwinch = 0; - el_resize(el); - } -#endif retry: -#ifndef NO_HISTORY +#ifndef SMALL if (parsefile->fd == 0 && el) { - const char *rl_cp; + static const char *rl_cp; + static int el_len; - rl_cp = el_gets(el, &nr); if (rl_cp == NULL) - nr = 0; + rl_cp = el_gets(el, &el_len); + if (rl_cp == NULL) + nr = el_len == 0 ? 0 : -1; else { - /* XXX - BUFSIZE should redesign so not necessary */ - (void) strcpy(parsenextc, rl_cp); + nr = el_len; + if (nr > BUFSIZ - 8) + nr = BUFSIZ - 8; + memcpy(buf, rl_cp, nr); + if (nr != el_len) { + el_len -= nr; + rl_cp += nr; + } else + rl_cp = 0; } + } else #endif - nr = read(parsefile->fd, parsenextc, BUFSIZ - 1); + nr = read(parsefile->fd, buf, BUFSIZ - 8); + if (nr <= 0) { if (nr < 0) { if (errno == EINTR) goto retry; -#ifdef EWOULDBLOCK if (parsefile->fd == 0 && errno == EWOULDBLOCK) { int flags = fcntl(0, F_GETFL, 0); if (flags >= 0 && flags & O_NONBLOCK) { @@ -219,7 +217,6 @@ retry: } } } -#endif /* EWOULDBLOCK */ } nr = -1; } @@ -232,7 +229,7 @@ retry: * 1) If a string was pushed back on the input, pop it; * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading * from a string so we can't refill the buffer, return EOF. - * 3) If there is more in this buffer, use it else call read to fill it. + * 3) If the is more stuff in this buffer, use it else call read to fill it. * 4) Process input up to the next newline, deleting nul characters. */ @@ -241,7 +238,9 @@ preadbuffer(void) { char *p, *q; int more; +#ifndef SMALL int something; +#endif char savec; if (parsefile->strpush) { @@ -265,7 +264,9 @@ again: q = p = parsenextc; /* delete nul characters */ +#ifndef SMALL something = 0; +#endif for (more = 1; more;) { switch (*p) { case '\0': @@ -282,7 +283,9 @@ again: break; default: +#ifndef SMALL something = 1; +#endif break; } @@ -300,11 +303,11 @@ check: savec = *q; *q = '\0'; -#if !defined(NO_HISTORY) +#ifndef SMALL if (parsefile->fd == 0 && hist && something) { HistEvent he; INTOFF; - history(hist, &he, whichprompt == 1 ? H_ENTER : H_ADD, + history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, parsenextc); INTON; } @@ -342,7 +345,7 @@ pushstring(char *s, int len, void *ap) struct strpush *sp; INTOFF; -/*dbgprintf("*** calling pushstring: %s, %d\n", s, len);*/ +/*debugprintf("*** calling pushstring: %s, %d\n", s, len);*/ if (parsefile->strpush) { sp = ckmalloc(sizeof (struct strpush)); sp->prev = parsefile->strpush; @@ -360,7 +363,7 @@ pushstring(char *s, int len, void *ap) INTON; } -static void +void popstring(void) { struct strpush *sp = parsefile->strpush; @@ -369,7 +372,7 @@ popstring(void) parsenextc = sp->prevstring; parsenleft = sp->prevnleft; parselleft = sp->prevlleft; -/*dbgprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ +/*debugprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ if (sp->ap) sp->ap->flag &= ~ALIASINUSE; parsefile->strpush = sp->prev; @@ -384,27 +387,32 @@ popstring(void) */ void -setinputfile(char *fname, int push) +setinputfile(const char *fname, int push) { + unsigned char magic[4]; int fd; int fd2; - struct stat statbuf; - int saved_errno; INTOFF; if ((fd = open(fname, O_RDONLY)) < 0) - error("Can't open %s: %s", fname, strerror(errno)); - if (fstat(fd, &statbuf) < 0) { - saved_errno = errno; - close(fd); - error("Can't stat %s: %s", fname, strerror(saved_errno)); - } - if (!S_ISREG(statbuf.st_mode)) { - close(fd); - error("Can't open %s: %s", fname, strerror(ENOEXEC)); + error("Can't open %s", fname); + + /* Since the message "Syntax error: "(" unexpected" is not very + * helpful, we check if the file starts with the ELF magic to + * avoid that message. The first lseek tries to make sure that + * we can later rewind the file. + */ + if (lseek(fd, 0, SEEK_SET) == 0) { + if (read(fd, magic, 4) == 4) { + if (memcmp(magic, "\177ELF", 4) == 0) + error("Cannot execute ELF binary %s", fname); + } + if (lseek(fd, 0, SEEK_SET) != 0) + error("Cannot rewind the file %s", fname); } + if (fd < 10) { - fd2 = fcntl(fd, F_DUPFD, 10); + fd2 = copyfd(fd, 10, 0); close(fd); if (fd2 < 0) error("Out of file descriptors"); @@ -423,7 +431,7 @@ setinputfile(char *fname, int push) void setinputfd(int fd, int push) { - (void)fcntl(fd, F_SETFD, FD_CLOEXEC); + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); if (push) { pushfile(); parsefile->buf = ckmalloc(BUFSIZ); @@ -518,18 +526,25 @@ popallfiles(void) /* * Close the file(s) that the shell is reading commands from. Called * after a fork is done. + * + * Takes one arg, vfork, which tells it to not modify its global vars + * as it is still running in the parent. + * + * This code is (probably) unnecessary as the 'close on exec' flag is + * set and should be enough. In the vfork case it is definitely wrong + * to close the fds as another fork() may be done later to feed data + * from a 'here' document into a pipe and we don't want to close the + * pipe! */ void -closescript(void) +closescript(int vforked) { + if (vforked) + return; popallfiles(); if (parsefile->fd > 0) { close(parsefile->fd); parsefile->fd = 0; } } - -/* - * $PchId: input.c,v 1.7 2006/05/29 13:09:38 philip Exp $ - */ diff --git a/minix/commands/ash/input.h b/bin/sh/input.h similarity index 90% rename from minix/commands/ash/input.h rename to bin/sh/input.h index 97267b513..a9d3a12b4 100644 --- a/minix/commands/ash/input.h +++ b/bin/sh/input.h @@ -1,3 +1,5 @@ +/* $NetBSD: input.h,v 1.15 2003/08/07 09:05:33 agc Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,7 +32,6 @@ * SUCH DAMAGE. * * @(#)input.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/input.h,v 1.9 2004/04/06 20:06:51 markm Exp $ */ /* PEOF (the end of file marker) is defined in syntax.h */ @@ -50,15 +51,12 @@ int pgetc(void); int preadbuffer(void); void pungetc(void); void pushstring(char *, int, void *); -void setinputfile(char *, int); +void popstring(void); +void setinputfile(const char *, int); void setinputfd(int, int); void setinputstring(char *, int); void popfile(void); void popallfiles(void); -void closescript(void); +void closescript(int); #define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) - -/* - * $PchId: input.h,v 1.3 2006/03/30 13:49:37 philip Exp $ - */ diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c new file mode 100644 index 000000000..4505b18fa --- /dev/null +++ b/bin/sh/jobs.c @@ -0,0 +1,1527 @@ +/* $NetBSD: jobs.c,v 1.71 2012/12/31 14:10:15 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: jobs.c,v 1.71 2012/12/31 14:10:15 dsl Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef BSD +#include +#include +#include +#endif +#include + +#include "shell.h" +#if JOBS +#if OLD_TTY_DRIVER +#include "sgtty.h" +#else +#include +#endif +#undef CEOF /* syntax.h redefines this */ +#endif +#include "redir.h" +#include "show.h" +#include "main.h" +#include "parser.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "builtins.h" +#include "trap.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + + +static struct job *jobtab; /* array of jobs */ +static int njobs; /* size of array */ +static int jobs_invalid; /* set in child */ +MKINIT pid_t backgndpid = -1; /* pid of last background process */ +#if JOBS +int initialpgrp; /* pgrp of shell on invocation */ +static int curjob = -1; /* current job */ +#endif +#if JOBS && defined(__minix) +static int ttyfd = -1; +#endif /* JOBS && defined(__minix) */ + +STATIC void restartjob(struct job *); +STATIC void freejob(struct job *); +STATIC struct job *getjob(const char *, int); +STATIC int dowait(int, struct job *); +#define WBLOCK 1 +#define WNOFREE 2 +STATIC int waitproc(int, struct job *, int *); +STATIC void cmdtxt(union node *); +STATIC void cmdlist(union node *, int); +STATIC void cmdputs(const char *); + +#ifdef SYSV +STATIC int onsigchild(void); +#endif + +#ifdef OLD_TTY_DRIVER +static pid_t tcgetpgrp(int fd); +static int tcsetpgrp(int fd, pid_t pgrp); + +static pid_t +tcgetpgrp(int fd) +{ + pid_t pgrp; + if (ioctl(fd, TIOCGPGRP, (char *)&pgrp) == -1) + return -1; + else + return pgrp; +} + +static int +tcsetpgrp(int fd, pid_tpgrp) +{ + return ioctl(fd, TIOCSPGRP, (char *)&pgrp); +} +#endif + +/* + * Turn job control on and off. + * + * Note: This code assumes that the third arg to ioctl is a character + * pointer, which is true on Berkeley systems but not System V. Since + * System V doesn't have job control yet, this isn't a problem now. + */ + +MKINIT int jobctl; + +#if JOBS /* Minix setjobctl is defined to empty, compilation error */ +void +setjobctl(int on) +{ +#ifdef OLD_TTY_DRIVER + int ldisc; +#endif + + if (on == jobctl || rootshell == 0) + return; + if (on) { +#if defined(FIOCLEX) || defined(FD_CLOEXEC) + int err; + int i; + if (ttyfd != -1) + close(ttyfd); + if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) { + for (i = 0; i < 3; i++) { + if (isatty(i) && (ttyfd = dup(i)) != -1) + break; + } + if (i == 3) + goto out; + } + /* Move to a high fd */ + for (i = 10; i > 2; i--) { + if ((err = fcntl(ttyfd, F_DUPFD, (1 << i) - 1)) != -1) + break; + } + if (err != -1) { + close(ttyfd); + ttyfd = err; + } +#ifdef FIOCLEX + err = ioctl(ttyfd, FIOCLEX, 0); +#elif FD_CLOEXEC + err = fcntl(ttyfd, F_SETFD, + fcntl(ttyfd, F_GETFD, 0) | FD_CLOEXEC); +#endif + if (err == -1) { + close(ttyfd); + ttyfd = -1; + goto out; + } +#else + out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control"); + goto out; +#endif + do { /* while we are in the background */ + if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) { +out: + out2str("sh: can't access tty; job control turned off\n"); + mflag = 0; + return; + } + if (initialpgrp == -1) + initialpgrp = getpgrp(); + else if (initialpgrp != getpgrp()) { + killpg(0, SIGTTIN); + continue; + } + } while (0); + +#ifdef OLD_TTY_DRIVER + if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0 + || ldisc != NTTYDISC) { + out2str("sh: need new tty driver to run job control; job control turned off\n"); + mflag = 0; + return; + } +#endif + setsignal(SIGTSTP, 0); + setsignal(SIGTTOU, 0); + setsignal(SIGTTIN, 0); + if (getpgrp() != rootpid && setpgid(0, rootpid) == -1) + error("Cannot set process group (%s) at %d", + strerror(errno), __LINE__); + if (tcsetpgrp(ttyfd, rootpid) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + } else { /* turning job control off */ + if (getpgrp() != initialpgrp && setpgid(0, initialpgrp) == -1) + error("Cannot set process group (%s) at %d", + strerror(errno), __LINE__); + if (tcsetpgrp(ttyfd, initialpgrp) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + close(ttyfd); + ttyfd = -1; + setsignal(SIGTSTP, 0); + setsignal(SIGTTOU, 0); + setsignal(SIGTTIN, 0); + } + jobctl = on; +} +#endif /* JOBS */ + + +#ifdef mkinit +INCLUDE + +SHELLPROC { + backgndpid = -1; +#if JOBS + jobctl = 0; +#endif +} + +#endif + + + +#if JOBS +static int +do_fgcmd(const char *arg_ptr) +{ + struct job *jp; + int i; + int status; + + jp = getjob(arg_ptr, 0); + if (jp->jobctl == 0) + error("job not created under job control"); + out1fmt("%s", jp->ps[0].cmd); + for (i = 1; i < jp->nprocs; i++) + out1fmt(" | %s", jp->ps[i].cmd ); + out1c('\n'); + flushall(); + + for (i = 0; i < jp->nprocs; i++) + if (tcsetpgrp(ttyfd, jp->ps[i].pid) != -1) + break; + + if (i >= jp->nprocs) { + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + } + restartjob(jp); + INTOFF; + status = waitforjob(jp); + INTON; + return status; +} + +int +fgcmd(int argc, char **argv) +{ + nextopt(""); + return do_fgcmd(*argptr); +} + +int +fgcmd_percent(int argc, char **argv) +{ + nextopt(""); + return do_fgcmd(*argv); +} + +static void +set_curjob(struct job *jp, int mode) +{ + struct job *jp1, *jp2; + int i, ji; + + ji = jp - jobtab; + + /* first remove from list */ + if (ji == curjob) + curjob = jp->prev_job; + else { + for (i = 0; i < njobs; i++) { + if (jobtab[i].prev_job != ji) + continue; + jobtab[i].prev_job = jp->prev_job; + break; + } + } + + /* Then re-insert in correct position */ + switch (mode) { + case 0: /* job being deleted */ + jp->prev_job = -1; + break; + case 1: /* newly created job or backgrounded job, + put after all stopped jobs. */ + if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) { + for (jp1 = jobtab + curjob; ; jp1 = jp2) { + if (jp1->prev_job == -1) + break; + jp2 = jobtab + jp1->prev_job; + if (jp2->state != JOBSTOPPED) + break; + } + jp->prev_job = jp1->prev_job; + jp1->prev_job = ji; + break; + } + /* FALLTHROUGH */ + case 2: /* newly stopped job - becomes curjob */ + jp->prev_job = curjob; + curjob = ji; + break; + } +} + +int +bgcmd(int argc, char **argv) +{ + struct job *jp; + int i; + + nextopt(""); + do { + jp = getjob(*argptr, 0); + if (jp->jobctl == 0) + error("job not created under job control"); + set_curjob(jp, 1); + out1fmt("[%ld] %s", (long)(jp - jobtab + 1), jp->ps[0].cmd); + for (i = 1; i < jp->nprocs; i++) + out1fmt(" | %s", jp->ps[i].cmd ); + out1c('\n'); + flushall(); + restartjob(jp); + } while (*argptr && *++argptr); + return 0; +} + + +STATIC void +restartjob(struct job *jp) +{ + struct procstat *ps; + int i; + + if (jp->state == JOBDONE) + return; + INTOFF; + for (i = 0; i < jp->nprocs; i++) + if (killpg(jp->ps[i].pid, SIGCONT) != -1) + break; + if (i >= jp->nprocs) + error("Cannot continue job (%s)", strerror(errno)); + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + if (WIFSTOPPED(ps->status)) { + ps->status = -1; + jp->state = JOBRUNNING; + } + } + INTON; +} +#else +/* LSC: For Minix add dummy functions. */ +int +fgcmd(int argc, char **argv) +{ + error("no job control in this shell."); + return 1; +} + +int +fgcmd_percent(int argc, char **argv) +{ + error("no job control in this shell."); + return 1; +} + + +int +bgcmd(int argc, char **argv) +{ + error("no job control in this shell."); + return 1; +} +#endif + +static void +showjob(struct output *out, struct job *jp, int mode) +{ + int procno; + int st; + struct procstat *ps; + int col; + char s[64]; + +#if JOBS + if (mode & SHOW_PGID) { + /* just output process (group) id of pipeline */ + outfmt(out, "%ld\n", (long)jp->ps->pid); + return; + } +#endif + + procno = jp->nprocs; + if (!procno) + return; + + if (mode & SHOW_PID) + mode |= SHOW_MULTILINE; + + if ((procno > 1 && !(mode & SHOW_MULTILINE)) + || (mode & SHOW_SIGNALLED)) { + /* See if we have more than one status to report */ + ps = jp->ps; + st = ps->status; + do { + int st1 = ps->status; + if (st1 != st) + /* yes - need multi-line output */ + mode |= SHOW_MULTILINE; + if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1)) + continue; + if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f) + && st1 != SIGINT && st1 != SIGPIPE)) + mode |= SHOW_ISSIG; + + } while (ps++, --procno); + procno = jp->nprocs; + } + + if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) { + if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) { + TRACE(("showjob: freeing job %d\n", jp - jobtab + 1)); + freejob(jp); + } + return; + } + + for (ps = jp->ps; --procno >= 0; ps++) { /* for each process */ + if (ps == jp->ps) + fmtstr(s, 16, "[%ld] %c ", + (long)(jp - jobtab + 1), +#if JOBS + jp == jobtab + curjob ? '+' : + curjob != -1 && jp == jobtab + + jobtab[curjob].prev_job ? '-' : +#endif + ' '); + else + fmtstr(s, 16, " " ); + col = strlen(s); + if (mode & SHOW_PID) { + fmtstr(s + col, 16, "%ld ", (long)ps->pid); + col += strlen(s + col); + } + if (ps->status == -1) { + scopy("Running", s + col); + } else if (WIFEXITED(ps->status)) { + st = WEXITSTATUS(ps->status); + if (st) + fmtstr(s + col, 16, "Done(%d)", st); + else + fmtstr(s + col, 16, "Done"); + } else { +#if JOBS + if (WIFSTOPPED(ps->status)) + st = WSTOPSIG(ps->status); + else /* WIFSIGNALED(ps->status) */ +#endif + st = WTERMSIG(ps->status); + st &= 0x7f; + if (st < NSIG && sys_siglist[st]) + scopyn(sys_siglist[st], s + col, 32); + else + fmtstr(s + col, 16, "Signal %d", st); + if (WCOREDUMP(ps->status)) { + col += strlen(s + col); + scopyn(" (core dumped)", s + col, 64 - col); + } + } + col += strlen(s + col); + outstr(s, out); + do { + outc(' ', out); + col++; + } while (col < 30); + outstr(ps->cmd, out); + if (mode & SHOW_MULTILINE) { + if (procno > 0) { + outc(' ', out); + outc('|', out); + } + } else { + while (--procno >= 0) + outfmt(out, " | %s", (++ps)->cmd ); + } + outc('\n', out); + } + flushout(out); + jp->changed = 0; + if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) + freejob(jp); +} + + +int +jobscmd(int argc, char **argv) +{ + int mode, m; + int sv = jobs_invalid; + + jobs_invalid = 0; + mode = 0; + while ((m = nextopt("lp"))) + if (m == 'l') + mode = SHOW_PID; + else + mode = SHOW_PGID; + if (*argptr) + do + showjob(out1, getjob(*argptr,0), mode); + while (*++argptr); + else + showjobs(out1, mode); + jobs_invalid = sv; + return 0; +} + + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + * + * If the shell is interrupted in the process of creating a job, the + * result may be a job structure containing zero processes. Such structures + * will be freed here. + */ + +void +showjobs(struct output *out, int mode) +{ + int jobno; + struct job *jp; + int silent = 0, gotpid; + + TRACE(("showjobs(%x) called\n", mode)); + + /* If not even one one job changed, there is nothing to do */ + gotpid = dowait(0, NULL); + while (dowait(0, NULL) > 0) + continue; +#if JOBS /* MINIX: #ifdef fails when JOBS = 0 */ + /* + * Check if we are not in our foreground group, and if not + * put us in it. + */ + if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) { + if (tcsetpgrp(ttyfd, getpid()) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + TRACE(("repaired tty process group\n")); + silent = 1; + } +#endif + if (jobs_invalid) + return; + + for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { + if (!jp->used) + continue; + if (jp->nprocs == 0) { + freejob(jp); + continue; + } + if ((mode & SHOW_CHANGED) && !jp->changed) + continue; + if (silent && jp->changed) { + jp->changed = 0; + continue; + } + showjob(out, jp, mode); + } +} + +/* + * Mark a job structure as unused. + */ + +STATIC void +freejob(struct job *jp) +{ + INTOFF; + if (jp->ps != &jp->ps0) { + ckfree(jp->ps); + jp->ps = &jp->ps0; + } + jp->nprocs = 0; + jp->used = 0; +#if JOBS + set_curjob(jp, 0); +#endif + INTON; +} + + + +int +waitcmd(int argc, char **argv) +{ + struct job *job; + int status, retval; + struct job *jp; + + nextopt(""); + + if (!*argptr) { + /* wait for all jobs */ + jp = jobtab; + if (jobs_invalid) + return 0; + for (;;) { + if (jp >= jobtab + njobs) { + /* no running procs */ + return 0; + } + if (!jp->used || jp->state != JOBRUNNING) { + jp++; + continue; + } + if (dowait(WBLOCK, NULL) == -1) + return 128 + SIGINT; + jp = jobtab; + } + } + + retval = 127; /* XXXGCC: -Wuninitialized */ + for (; *argptr; argptr++) { + job = getjob(*argptr, 1); + if (!job) { + retval = 127; + continue; + } + /* loop until process terminated or stopped */ + while (job->state == JOBRUNNING) { + if (dowait(WBLOCK|WNOFREE, job) == -1) + return 128 + SIGINT; + } + status = job->ps[job->nprocs - 1].status; + if (WIFEXITED(status)) + retval = WEXITSTATUS(status); +#if JOBS + else if (WIFSTOPPED(status)) + retval = WSTOPSIG(status) + 128; +#endif + else { + /* XXX: limits number of signals */ + retval = WTERMSIG(status) + 128; + } + if (!iflag) + freejob(job); + } + return retval; +} + + + +int +jobidcmd(int argc, char **argv) +{ + struct job *jp; + int i; + + nextopt(""); + jp = getjob(*argptr, 0); + for (i = 0 ; i < jp->nprocs ; ) { + out1fmt("%ld", (long)jp->ps[i].pid); + out1c(++i < jp->nprocs ? ' ' : '\n'); + } + return 0; +} + +int +getjobpgrp(const char *name) +{ + struct job *jp; + + jp = getjob(name, 1); + if (jp == 0) + return 0; + return -jp->ps[0].pid; +} + +/* + * Convert a job name to a job structure. + */ + +STATIC struct job * +getjob(const char *name, int noerror) +{ + int jobno = -1; + struct job *jp; + int pid; + int i; + const char *err_msg = "No such job: %s"; + + if (name == NULL) { +#if JOBS + jobno = curjob; +#endif + err_msg = "No current job"; + } else if (name[0] == '%') { + if (is_number(name + 1)) { + jobno = number(name + 1) - 1; + } else if (!name[2]) { + switch (name[1]) { +#if JOBS + case 0: + case '+': + case '%': + jobno = curjob; + err_msg = "No current job"; + break; + case '-': + jobno = curjob; + if (jobno != -1) + jobno = jobtab[jobno].prev_job; + err_msg = "No previous job"; + break; +#endif + default: + goto check_pattern; + } + } else { + struct job *found; + check_pattern: + found = NULL; + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (!jp->used || jp->nprocs <= 0) + continue; + if ((name[1] == '?' + && strstr(jp->ps[0].cmd, name + 2)) + || prefix(name + 1, jp->ps[0].cmd)) { + if (found) { + err_msg = "%s: ambiguous"; + found = 0; + break; + } + found = jp; + } + } + if (found) + return found; + } + + } else if (is_number(name)) { + pid = number(name); + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && jp->ps[jp->nprocs - 1].pid == pid) + return jp; + } + } + + if (!jobs_invalid && jobno >= 0 && jobno < njobs) { + jp = jobtab + jobno; + if (jp->used) + return jp; + } + if (!noerror) + error(err_msg, name); + return 0; +} + + + +/* + * Return a new job structure, + */ + +struct job * +makejob(union node *node, int nprocs) +{ + int i; + struct job *jp; + + if (jobs_invalid) { + for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) { + if (jp->used) + freejob(jp); + } + jobs_invalid = 0; + } + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + INTOFF; + if (njobs == 0) { + jobtab = ckmalloc(4 * sizeof jobtab[0]); + } else { + jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); + memcpy(jp, jobtab, njobs * sizeof jp[0]); + /* Relocate `ps' pointers */ + for (i = 0; i < njobs; i++) + if (jp[i].ps == &jobtab[i].ps0) + jp[i].ps = &jp[i].ps0; + ckfree(jobtab); + jobtab = jp; + } + jp = jobtab + njobs; + for (i = 4 ; --i >= 0 ; ) + jobtab[njobs++].used = 0; + INTON; + break; + } + if (jp->used == 0) + break; + } + INTOFF; + jp->state = JOBRUNNING; + jp->used = 1; + jp->changed = 0; + jp->nprocs = 0; +#if JOBS + jp->jobctl = jobctl; + set_curjob(jp, 1); +#endif + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } else { + jp->ps = &jp->ps0; + } + INTON; + TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, + jp - jobtab + 1)); + return jp; +} + + +/* + * Fork off a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + */ + +int +forkshell(struct job *jp, union node *n, int mode) +{ + int pid; + + TRACE(("forkshell(%%%d, %p, %d) called\n", jp - jobtab, n, mode)); + switch ((pid = fork())) { + case -1: + TRACE(("Fork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork"); + break; + case 0: + forkchild(jp, n, mode, 0); + return 0; + default: + return forkparent(jp, n, mode, pid); + } +} + +int +forkparent(struct job *jp, union node *n, int mode, pid_t pid) +{ + int pgrp; + + if (rootshell && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; +#if JOBS /* LSC: not available under MINIX. */ + /* This can fail because we are doing it in the child also */ + (void)setpgid(pid, pgrp); +#endif + } + if (mode == FORK_BG) + backgndpid = pid; /* set $! */ + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd[0] = 0; + if (/* iflag && rootshell && */ n) + commandtext(ps, n); + } + TRACE(("In parent shell: child = %d\n", pid)); + return pid; +} + +void +forkchild(struct job *jp, union node *n, int mode, int vforked) +{ + int wasroot; +#if JOBS /* LSC: for proper compilation with JOBS == 0 */ + int pgrp; +#endif + const char *devnull = _PATH_DEVNULL; + const char *nullerr = "Can't open %s"; + + wasroot = rootshell; + TRACE(("Child shell %d\n", getpid())); + if (!vforked) + rootshell = 0; + + closescript(vforked); + clear_traps(vforked); +#if JOBS + if (!vforked) + jobctl = 0; /* do job control only in root shell */ + if (wasroot && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; + /* This can fail because we are doing it in the parent also */ + (void)setpgid(0, pgrp); + if (mode == FORK_FG) { + if (tcsetpgrp(ttyfd, pgrp) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + } + setsignal(SIGTSTP, vforked); + setsignal(SIGTTOU, vforked); + } else if (mode == FORK_BG) { + ignoresig(SIGINT, vforked); + ignoresig(SIGQUIT, vforked); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#else + if (mode == FORK_BG) { + ignoresig(SIGINT, vforked); + ignoresig(SIGQUIT, vforked); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#endif + if (wasroot && iflag) { + setsignal(SIGINT, vforked); + setsignal(SIGQUIT, vforked); + setsignal(SIGTERM, vforked); + } + + if (!vforked) + jobs_invalid = 1; +} + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * forground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + */ + +int +waitforjob(struct job *jp) +{ +#if JOBS + int mypgrp = getpgrp(); +#endif + int status; + int st; + + INTOFF; + TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); + while (jp->state == JOBRUNNING) { + dowait(WBLOCK, jp); + } +#if JOBS + if (jp->jobctl) { + if (tcsetpgrp(ttyfd, mypgrp) == -1) + error("Cannot set tty process group (%s) at %d", + strerror(errno), __LINE__); + } + if (jp->state == JOBSTOPPED && curjob != jp - jobtab) + set_curjob(jp, 2); +#endif + status = jp->ps[jp->nprocs - 1].status; + /* convert to 8 bits */ + if (WIFEXITED(status)) + st = WEXITSTATUS(status); +#if JOBS + else if (WIFSTOPPED(status)) + st = WSTOPSIG(status) + 128; +#endif + else + st = WTERMSIG(status) + 128; + TRACE(("waitforjob: job %d, nproc %d, status %x, st %x\n", + jp - jobtab + 1, jp->nprocs, status, st )); +#if JOBS + if (jp->jobctl) { + /* + * This is truly gross. + * If we're doing job control, then we did a TIOCSPGRP which + * caused us (the shell) to no longer be in the controlling + * session -- so we wouldn't have seen any ^C/SIGINT. So, we + * intuit from the subprocess exit status whether a SIGINT + * occurred, and if so interrupt ourselves. Yuck. - mycroft + */ + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) + raise(SIGINT); + } +#endif + if (! JOBS || jp->state == JOBDONE) + freejob(jp); + INTON; + return st; +} + + + +/* + * Wait for a process to terminate. + */ + +STATIC int +dowait(int flags, struct job *job) +{ + int pid; + int status; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + int done; + int stopped; + extern volatile char gotsig[]; + + TRACE(("dowait(%x) called\n", flags)); + do { + pid = waitproc(flags & WBLOCK, job, &status); + TRACE(("wait returns pid %d, status %d\n", pid, status)); + } while (pid == -1 && errno == EINTR && gotsig[SIGINT - 1] == 0); + if (pid <= 0) + return pid; + INTOFF; + thisjob = NULL; + for (jp = jobtab ; jp < jobtab + njobs ; jp++) { + if (jp->used) { + done = 1; + stopped = 1; + for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { + if (sp->pid == -1) + continue; + if (sp->pid == pid) { + TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jp - jobtab + 1, pid, sp->status, status)); + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + stopped = 0; + else if (WIFSTOPPED(sp->status)) + done = 0; + } + if (stopped) { /* stopped or done */ + int state = done ? JOBDONE : JOBSTOPPED; + if (jp->state != state) { + TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); + jp->state = state; +#if JOBS + if (done) + set_curjob(jp, 0); +#endif + } + } + } + } + + if (thisjob && thisjob->state != JOBRUNNING) { + int mode = 0; + if (!rootshell || !iflag) + mode = SHOW_SIGNALLED; + if ((job == thisjob && (flags & WNOFREE) == 0) || + (job != thisjob && (flags & WNOFREE) != 0)) + mode = SHOW_SIGNALLED | SHOW_NO_FREE; + if (mode) + showjob(out2, thisjob, mode); + else { + TRACE(("Not printing status, rootshell=%d, job=%p\n", + rootshell, job)); + thisjob->changed = 1; + } + } + + INTON; + return pid; +} + + + +/* + * Do a wait system call. If job control is compiled in, we accept + * stopped processes. If block is zero, we return a value of zero + * rather than blocking. + * + * System V doesn't have a non-blocking wait system call. It does + * have a SIGCLD signal that is sent to a process when one of its + * children dies. The obvious way to use SIGCLD would be to install + * a handler for SIGCLD which simply bumped a counter when a SIGCLD + * was received, and have waitproc bump another counter when it got + * the status of a process. Waitproc would then know that a wait + * system call would not block if the two counters were different. + * This approach doesn't work because if a process has children that + * have not been waited for, System V will send it a SIGCLD when it + * installs a signal handler for SIGCLD. What this means is that when + * a child exits, the shell will be sent SIGCLD signals continuously + * until is runs out of stack space, unless it does a wait call before + * restoring the signal handler. The code below takes advantage of + * this (mis)feature by installing a signal handler for SIGCLD and + * then checking to see whether it was called. If there are any + * children to be waited for, it will be. + * + * If neither SYSV nor BSD is defined, we don't implement nonblocking + * waits at all. In this case, the user will not be informed when + * a background process until the next time she runs a real program + * (as opposed to running a builtin command or just typing return), + * and the jobs command may give out of date information. + */ + +#ifdef SYSV +STATIC int gotsigchild; + +STATIC int onsigchild() { + gotsigchild = 1; +} +#endif + + +STATIC int +waitproc(int block, struct job *jp, int *status) +{ +#ifdef BSD + int flags = 0; + +#if JOBS + if (jp != NULL && jp->jobctl) + flags |= WUNTRACED; +#endif + if (block == 0) + flags |= WNOHANG; + return waitpid(-1, status, flags); +#else +#ifdef SYSV + int (*save)(); + + if (block == 0) { + gotsigchild = 0; + save = signal(SIGCLD, onsigchild); + signal(SIGCLD, save); + if (gotsigchild == 0) + return 0; + } + return wait(status); +#else + if (block == 0) + return 0; + return wait(status); +#endif +#endif +} + +/* + * return 1 if there are stopped jobs, otherwise 0 + */ +int job_warning = 0; +int +stoppedjobs(void) +{ + int jobno; + struct job *jp; + + if (job_warning || jobs_invalid) + return (0); + for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { + if (jp->used == 0) + continue; + if (jp->state == JOBSTOPPED) { + out2str("You have stopped jobs.\n"); + job_warning = 2; + return (1); + } + } + + return (0); +} + +/* + * Return a string identifying a command (to be printed by the + * jobs command). + */ + +STATIC char *cmdnextc; +STATIC int cmdnleft; + +void +commandtext(struct procstat *ps, union node *n) +{ + int len; + + cmdnextc = ps->cmd; + if (iflag || mflag || sizeof ps->cmd < 100) + len = sizeof(ps->cmd); + else + len = sizeof(ps->cmd) / 10; + cmdnleft = len; + cmdtxt(n); + if (cmdnleft <= 0) { + char *p = ps->cmd + len - 4; + p[0] = '.'; + p[1] = '.'; + p[2] = '.'; + p[3] = 0; + } else + *cmdnextc = '\0'; + TRACE(("commandtext: ps->cmd %x, end %x, left %d\n\t\"%s\"\n", + ps->cmd, cmdnextc, cmdnleft, ps->cmd)); +} + + +STATIC void +cmdtxt(union node *n) +{ + union node *np; + struct nodelist *lp; + const char *p; + int i; + char s[2]; + + if (n == NULL || cmdnleft <= 0) + return; + switch (n->type) { + case NSEMI: + cmdtxt(n->nbinary.ch1); + cmdputs("; "); + cmdtxt(n->nbinary.ch2); + break; + case NAND: + cmdtxt(n->nbinary.ch1); + cmdputs(" && "); + cmdtxt(n->nbinary.ch2); + break; + case NOR: + cmdtxt(n->nbinary.ch1); + cmdputs(" || "); + cmdtxt(n->nbinary.ch2); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + cmdtxt(lp->n); + if (lp->next) + cmdputs(" | "); + } + break; + case NSUBSHELL: + cmdputs("("); + cmdtxt(n->nredir.n); + cmdputs(")"); + break; + case NREDIR: + case NBACKGND: + cmdtxt(n->nredir.n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + cmdtxt(n->nif.ifpart); + if (n->nif.elsepart) { + cmdputs("; else "); + cmdtxt(n->nif.elsepart); + } + cmdputs("; fi"); + break; + case NWHILE: + cmdputs("while "); + goto until; + case NUNTIL: + cmdputs("until "); +until: + cmdtxt(n->nbinary.ch1); + cmdputs("; do "); + cmdtxt(n->nbinary.ch2); + cmdputs("; done"); + break; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in "); + cmdlist(n->nfor.args, 1); + cmdputs("; do "); + cmdtxt(n->nfor.body); + cmdputs("; done"); + break; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in "); + for (np = n->ncase.cases; np; np = np->nclist.next) { + cmdtxt(np->nclist.pattern); + cmdputs(") "); + cmdtxt(np->nclist.body); + cmdputs(";; "); + } + cmdputs("esac"); + break; + case NDEFUN: + cmdputs(n->narg.text); + cmdputs("() { ... }"); + break; + case NCMD: + cmdlist(n->ncmd.args, 1); + cmdlist(n->ncmd.redirect, 0); + break; + case NARG: + cmdputs(n->narg.text); + break; + case NTO: + p = ">"; i = 1; goto redir; + case NCLOBBER: + p = ">|"; i = 1; goto redir; + case NAPPEND: + p = ">>"; i = 1; goto redir; + case NTOFD: + p = ">&"; i = 1; goto redir; + case NFROM: + p = "<"; i = 0; goto redir; + case NFROMFD: + p = "<&"; i = 0; goto redir; + case NFROMTO: + p = "<>"; i = 0; goto redir; +redir: + if (n->nfile.fd != i) { + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + } + cmdputs(p); + if (n->type == NTOFD || n->type == NFROMFD) { + s[0] = n->ndup.dupfd + '0'; + s[1] = '\0'; + cmdputs(s); + } else { + cmdtxt(n->nfile.fname); + } + break; + case NHERE: + case NXHERE: + cmdputs("<<..."); + break; + default: + cmdputs("???"); + break; + } +} + +STATIC void +cmdlist(union node *np, int sep) +{ + for (; np; np = np->narg.next) { + if (!sep) + cmdputs(" "); + cmdtxt(np); + if (sep && np->narg.next) + cmdputs(" "); + } +} + + +STATIC void +cmdputs(const char *s) +{ + const char *p, *str = 0; + char c, cc[2] = " "; + char *nextc; + int nleft; + int subtype = 0; + int quoted = 0; + static char vstype[16][4] = { "", "}", "-", "+", "?", "=", + "#", "##", "%", "%%" }; + + p = s; + nextc = cmdnextc; + nleft = cmdnleft; + while (nleft > 0 && (c = *p++) != 0) { + switch (c) { + case CTLESC: + c = *p++; + break; + case CTLVAR: + subtype = *p++; + if ((subtype & VSTYPE) == VSLENGTH) + str = "${#"; + else + str = "${"; + if (!(subtype & VSQUOTE) != !(quoted & 1)) { + quoted ^= 1; + c = '"'; + } else + c = *str++; + break; + case CTLENDVAR: + if (quoted & 1) { + c = '"'; + str = "}"; + } else + c = '}'; + quoted >>= 1; + subtype = 0; + break; + case CTLBACKQ: + c = '$'; + str = "(...)"; + break; + case CTLBACKQ+CTLQUOTE: + c = '"'; + str = "$(...)\""; + break; + case CTLARI: + c = '$'; + str = "(("; + break; + case CTLENDARI: + c = ')'; + str = ")"; + break; + case CTLQUOTEMARK: + quoted ^= 1; + c = '"'; + break; + case '=': + if (subtype == 0) + break; + str = vstype[subtype & VSTYPE]; + if (subtype & VSNUL) + c = ':'; + else + c = *str++; + if (c != '}') + quoted <<= 1; + break; + case '\'': + case '\\': + case '"': + case '$': + /* These can only happen inside quotes */ + cc[0] = c; + str = cc; + c = '\\'; + break; + default: + break; + } + do { + *nextc++ = c; + } while (--nleft > 0 && str && (c = *str++)); + str = 0; + } + if ((quoted & 1) && nleft) { + *nextc++ = '"'; + nleft--; + } + cmdnleft = nleft; + cmdnextc = nextc; +} diff --git a/minix/commands/ash/jobs.h b/bin/sh/jobs.h similarity index 62% rename from minix/commands/ash/jobs.h rename to bin/sh/jobs.h index db436aa8b..927373619 100644 --- a/minix/commands/ash/jobs.h +++ b/bin/sh/jobs.h @@ -1,3 +1,5 @@ +/* $NetBSD: jobs.h,v 1.20 2011/06/18 21:18:46 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,15 +32,24 @@ * SUCH DAMAGE. * * @(#)jobs.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/jobs.h,v 1.18 2004/04/06 20:06:51 markm Exp $ */ +#include "output.h" + /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ #define FORK_FG 0 #define FORK_BG 1 #define FORK_NOJOB 2 -#include /* for sig_atomic_t */ +/* mode flags for showjob(s) */ +#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ +#define SHOW_MULTILINE 0x02 /* one line per process */ +#define SHOW_PID 0x04 /* include process pid */ +#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ +#define SHOW_SIGNALLED 0x10 /* only if stopped/exited on signal */ +#define SHOW_ISSIG 0x20 /* job was signalled */ +#define SHOW_NO_FREE 0x40 /* do not free job */ + /* * A job structure contains information about a job. A job is either a @@ -46,57 +57,45 @@ * latter case, pidlist will be non-NULL, and will point to a -1 terminated * array of pids. */ +#define MAXCMDTEXT 200 struct procstat { - pid_t pid; /* process id */ - int status; /* status flags (defined above) */ - char *cmd; /* text of command being run */ + pid_t pid; /* process id */ + int status; /* last process status from wait() */ + char cmd[MAXCMDTEXT];/* text of command being run */ }; - -/* states */ -#define JOBSTOPPED 1 /* all procs are stopped */ -#define JOBDONE 2 /* all procs are completed */ - - struct job { struct procstat ps0; /* status of process */ struct procstat *ps; /* status or processes when more than one */ - short nprocs; /* number of processes */ - pid_t pgrp; /* process group of this job */ - char state; /* true if job is finished */ - char used; /* true if this entry is in used */ - char changed; /* true if status has changed */ - char foreground; /* true if running in the foreground */ + int nprocs; /* number of processes */ + pid_t pgrp; /* process group of this job */ + char state; +#define JOBRUNNING 0 /* at least one proc running */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ + char used; /* true if this entry is in used */ + char changed; /* true if status has changed */ #if JOBS - char jobctl; /* job running under job control */ - struct job *next; /* job used after this one */ + char jobctl; /* job running under job control */ + int prev_job; /* previous job index */ #endif }; extern pid_t backgndpid; /* pid of last background process */ extern int job_warning; /* user was warned about stopped jobs */ -extern int in_waitcmd; /* are we in waitcmd()? */ -extern int in_dowait; /* are we in dowait()? */ -extern volatile sig_atomic_t breakwaitcmd; /* break wait to process traps? */ void setjobctl(int); -int fgcmd(int, char **); -int bgcmd(int, char **); -int jobscmd(int, char **); -void showjobs(int, int, int); -int waitcmd(int, char **); -int jobidcmd(int, char **); +void showjobs(struct output *, int); struct job *makejob(union node *, int); -pid_t forkshell(struct job *, union node *, int); -int waitforjob(struct job *, int *); +int forkshell(struct job *, union node *, int); +void forkchild(struct job *, union node *, int, int); +int forkparent(struct job *, union node *, int, pid_t); +int waitforjob(struct job *); int stoppedjobs(void); -char *commandtext(union node *); +void commandtext(struct procstat *, union node *); +int getjobpgrp(const char *); #if ! JOBS #define setjobctl(on) /* do nothing */ #endif - -/* - * $PchId: jobs.h,v 1.4 2006/03/30 12:07:24 philip Exp $ - */ diff --git a/minix/commands/ash/machdep.h b/bin/sh/machdep.h similarity index 78% rename from minix/commands/ash/machdep.h rename to bin/sh/machdep.h index 4de7acd21..14e803bf7 100644 --- a/minix/commands/ash/machdep.h +++ b/bin/sh/machdep.h @@ -1,3 +1,5 @@ +/* $NetBSD: machdep.h,v 1.11 2003/08/07 09:05:33 agc Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,11 +15,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -41,15 +39,9 @@ * in some way. The following macro will get this right on many machines. */ -#ifndef ALIGN -union align { - int i; - char *cp; -}; - -#define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1)) -#endif - +#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1) /* - * $PchId: machdep.h,v 1.2 2001/05/15 16:36:26 philip Exp $ + * It appears that grabstackstr() will barf with such alignments + * because stalloc() will return a string allocated in a new stackblock. */ +#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) diff --git a/minix/commands/ash/mail.c b/bin/sh/mail.c similarity index 89% rename from minix/commands/ash/mail.c rename to bin/sh/mail.c index 14bc1d91e..4ddd7c074 100644 --- a/minix/commands/ash/mail.c +++ b/bin/sh/mail.c @@ -1,3 +1,5 @@ +/* $NetBSD: mail.c,v 1.16 2003/08/07 09:05:33 agc Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,19 +32,21 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)mail.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: mail.c,v 1.16 2003/08/07 09:05:33 agc Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/mail.c,v 1.13 2004/04/06 20:06:51 markm Exp $"); -*/ /* * Routines to check for mail. (Perhaps make part of main.c?) */ +#include +#include +#include #include "shell.h" #include "exec.h" /* defines padvance() */ @@ -51,9 +55,6 @@ __FBSDID("$FreeBSD: src/bin/sh/mail.c,v 1.13 2004/04/06 20:06:51 markm Exp $"); #include "memalloc.h" #include "error.h" #include "mail.h" -#include -#include -#include #define MAXMBOXES 10 @@ -74,7 +75,7 @@ void chkmail(int silent) { int i; - char *mpath; + const char *mpath; char *p; char *q; struct stackmark smark; @@ -85,7 +86,7 @@ chkmail(int silent) if (nmboxes == 0) return; setstackmark(&smark); - mpath = mpathset()? mpathval() : mailval(); + mpath = mpathset() ? mpathval() : mailval(); for (i = 0 ; i < nmboxes ; i++) { p = padvance(&mpath, nullstr); if (p == NULL) @@ -100,7 +101,7 @@ chkmail(int silent) if (stat(p, &statb) < 0) statb.st_mtime = 0; if (statb.st_mtime > mailtime[i] && ! silent) { - out2str(pathopt? pathopt : "you have mail"); + out2str(pathopt ? pathopt : "you have mail"); out2c('\n'); } mailtime[i] = statb.st_mtime; @@ -108,7 +109,7 @@ chkmail(int silent) if (stat(p, &statb) < 0) statb.st_size = 0; if (statb.st_size > mailtime[i] && ! silent) { - out2str(pathopt? pathopt : "you have mail"); + out2str(pathopt ? pathopt : "you have mail"); out2c('\n'); } mailtime[i] = statb.st_size; @@ -117,7 +118,3 @@ chkmail(int silent) nmboxes = i; popstackmark(&smark); } - -/* - * $PchId: mail.c,v 1.5 2006/05/22 12:02:37 philip Exp $ - */ diff --git a/minix/commands/ash/mail.h b/bin/sh/mail.h similarity index 89% rename from minix/commands/ash/mail.h rename to bin/sh/mail.h index 4d67b0ae9..9ea7c218b 100644 --- a/minix/commands/ash/mail.h +++ b/bin/sh/mail.h @@ -1,3 +1,5 @@ +/* $NetBSD: mail.h,v 1.10 2003/08/07 09:05:34 agc Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,11 +32,6 @@ * SUCH DAMAGE. * * @(#)mail.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/mail.h,v 1.8 2004/04/06 20:06:51 markm Exp $ */ void chkmail(int); - -/* - * $PchId: mail.h,v 1.3 2006/03/30 11:53:44 philip Exp $ - */ diff --git a/minix/commands/ash/main.c b/bin/sh/main.c similarity index 72% rename from minix/commands/ash/main.c rename to bin/sh/main.c index 262756db4..35082a955 100644 --- a/minix/commands/ash/main.c +++ b/bin/sh/main.c @@ -1,3 +1,5 @@ +/* $NetBSD: main.c,v 1.57 2011/06/18 21:18:46 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,34 +32,35 @@ * SUCH DAMAGE. */ +#include #ifndef lint -static char const copyright[] = -"@(#) Copyright (c) 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; +__COPYRIGHT("@(#) Copyright (c) 1991, 1993\ + The Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint #if 0 -static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/28/95"; +static char sccsid[] = "@(#)main.c 8.7 (Berkeley) 7/19/95"; +#else +__RCSID("$NetBSD: main.c,v 1.57 2011/06/18 21:18:46 christos Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/main.c,v 1.26 2004/04/06 20:06:51 markm Exp $"); -*/ +#include #include #include #include #include -#include +#include #include -#include +#include + #include "shell.h" #include "main.h" #include "mail.h" #include "options.h" +#include "builtins.h" #include "output.h" #include "parser.h" #include "nodes.h" @@ -74,13 +77,20 @@ __FBSDID("$FreeBSD: src/bin/sh/main.c,v 1.26 2004/04/06 20:06:51 markm Exp $"); #include "mystring.h" #include "exec.h" #include "cd.h" -#include "builtins.h" + +#define PROFILE 0 int rootpid; int rootshell; +int posix; +#if PROFILE +short profile_buf[16384]; +extern int etext(); +#endif -STATIC void read_profile(char *); +STATIC void read_profile(const char *); STATIC char *find_dot_file(char *); +int main(int, char **); /* * Main routine. We initialize things, parse the arguments, execute @@ -91,14 +101,19 @@ STATIC char *find_dot_file(char *); */ int -main(int argc, char *argv[]) +main(int argc, char **argv) { struct jmploc jmploc; struct stackmark smark; volatile int state; char *shinit; - (void) setlocale(LC_ALL, ""); + setlocale(LC_ALL, ""); + + posix = getenv("POSIXLY_CORRECT") != NULL; +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif state = 0; if (setjmp(jmploc.loc)) { /* @@ -127,11 +142,15 @@ main(int argc, char *argv[]) } if (exception != EXSHELLPROC) { - if (state == 0 || iflag == 0 || ! rootshell) - exitshell(exitstatus); + if (state == 0 || iflag == 0 || ! rootshell) + exitshell(exitstatus); } reset(); - if (exception == EXINT) { + if (exception == EXINT +#if ATTY + && (! attyset() || equal(termval(), "emacs")) +#endif + ) { out2c('\n'); flushout(&errout); } @@ -147,30 +166,30 @@ main(int argc, char *argv[]) goto state4; } handler = &jmploc; -#if DEBUG +#ifdef DEBUG +#if DEBUG == 2 + debug = 1; +#endif opentrace(); trputs("Shell args: "); trargs(argv); #endif rootpid = getpid(); rootshell = 1; init(); + initpwd(); setstackmark(&smark); procargs(argc, argv); - if (getpwd() == NULL && iflag) - out2str("sh: cannot determine working directory\n"); if (argv[0] && argv[0][0] == '-') { state = 1; read_profile("/etc/profile"); state1: state = 2; - if (privileged == 0) - read_profile(".profile"); - else - read_profile("/etc/suid_profile"); + read_profile(".profile"); } state2: state = 3; - if (!privileged && iflag) { + if ((iflag || !posix) && + getuid() == geteuid() && getgid() == getegid()) { if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { state = 3; read_profile(shinit); @@ -178,16 +197,33 @@ state2: } state3: state = 4; - if (minusc) { - evalstring(minusc); + if (sflag == 0 || minusc) { + static int sigs[] = { + SIGINT, SIGQUIT, SIGHUP, +#ifdef SIGTSTP + SIGTSTP, +#endif + SIGPIPE + }; +#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) + size_t i; + + for (i = 0; i < SIGSSIZE; i++) + setsignal(sigs[i], 0); } + + if (minusc) + evalstring(minusc, 0); + if (sflag || minusc == NULL) { state4: /* XXX ??? - why isn't this before the "if" statement */ cmdloop(1); } +#if PROFILE + monitor(0); +#endif exitshell(exitstatus); - /*NOTREACHED*/ - return 0; + /* NOTREACHED */ } @@ -210,11 +246,11 @@ cmdloop(int top) if (pendingsigs) dotrap(); inter = 0; - if (iflag && top) { - inter++; - showjobs(1, 0, 0); + if (iflag == 1 && top) { + inter = 1; + showjobs(out2, SHOW_CHANGED); chkmail(0); - flushout(&output); + flushout(&errout); } n = parsecmd(inter); /* showtree(n); DEBUG */ @@ -249,9 +285,11 @@ cmdloop(int top) */ STATIC void -read_profile(char *name) +read_profile(const char *name) { int fd; + int xflag_set = 0; + int vflag_set = 0; INTOFF; if ((fd = open(name, O_RDONLY)) >= 0) @@ -259,7 +297,20 @@ read_profile(char *name) INTON; if (fd < 0) return; + /* -q turns off -x and -v just when executing init files */ + if (qflag) { + if (xflag) + xflag = 0, xflag_set = 1; + if (vflag) + vflag = 0, vflag_set = 1; + } cmdloop(0); + if (qflag) { + if (xflag_set) + xflag = 1; + if (vflag_set) + vflag = 1; + } popfile(); } @@ -278,7 +329,7 @@ readcmdfile(char *name) if ((fd = open(name, O_RDONLY)) >= 0) setinputfd(fd, 1); else - error("Can't open %s: %s", name, strerror(errno)); + error("Can't open %s", name); INTON; cmdloop(0); popfile(); @@ -295,40 +346,46 @@ readcmdfile(char *name) STATIC char * find_dot_file(char *basename) { - static char localname[FILENAME_MAX+1]; char *fullname; - char *path = pathval(); + const char *path = pathval(); struct stat statb; /* don't try this for absolute or relative paths */ - if( strchr(basename, '/')) + if (strchr(basename, '/')) return basename; while ((fullname = padvance(&path, basename)) != NULL) { - strcpy(localname, fullname); + if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { + /* + * Don't bother freeing here, since it will + * be freed by the caller. + */ + return fullname; + } stunalloc(fullname); - if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) - return localname; } - return basename; + + /* not found in the PATH */ + error("%s: not found", basename); + /* NOTREACHED */ } int dotcmd(int argc, char **argv) { - struct strlist *sp; exitstatus = 0; - for (sp = cmdenviron; sp ; sp = sp->next) - setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED); - if (argc >= 2) { /* That's what SVR2 does */ - char *fullname = find_dot_file(argv[1]); + char *fullname; + struct stackmark smark; + setstackmark(&smark); + fullname = find_dot_file(argv[1]); setinputfile(fullname, 1); commandname = fullname; cmdloop(0); popfile(); + popstackmark(&smark); } return exitstatus; } @@ -337,19 +394,10 @@ dotcmd(int argc, char **argv) int exitcmd(int argc, char **argv) { - extern int oexitstatus; - if (stoppedjobs()) return 0; if (argc > 1) exitstatus = number(argv[1]); - else - exitstatus = oexitstatus; exitshell(exitstatus); - /*NOTREACHED*/ - return 0; + /* NOTREACHED */ } - -/* - * $PchId: main.c,v 1.5 2006/05/22 12:03:02 philip Exp $ - */ diff --git a/minix/commands/ash/main.h b/bin/sh/main.h similarity index 87% rename from minix/commands/ash/main.h rename to bin/sh/main.h index 55ab91b7a..9caa218f1 100644 --- a/minix/commands/ash/main.h +++ b/bin/sh/main.h @@ -1,3 +1,5 @@ +/* $NetBSD: main.h,v 1.11 2011/06/18 21:18:46 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,7 +32,6 @@ * SUCH DAMAGE. * * @(#)main.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/main.h,v 1.8 2004/04/06 20:06:51 markm Exp $ */ extern int rootpid; /* pid of main shell */ @@ -38,9 +39,3 @@ extern int rootshell; /* true if we aren't a child of the main shell */ void readcmdfile(char *); void cmdloop(int); -int dotcmd(int, char **); -int exitcmd(int, char **); - -/* - * $PchId: main.h,v 1.3 2006/03/30 11:43:59 philip Exp $ - */ diff --git a/minix/commands/ash/memalloc.c b/bin/sh/memalloc.c similarity index 74% rename from minix/commands/ash/memalloc.c rename to bin/sh/memalloc.c index ab4900f46..a26348e5e 100644 --- a/minix/commands/ash/memalloc.c +++ b/bin/sh/memalloc.c @@ -1,3 +1,5 @@ +/* $NetBSD: memalloc.c,v 1.29 2008/02/15 17:26:06 matt Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,36 +32,36 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: memalloc.c,v 1.29 2008/02/15 17:26:06 matt Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/memalloc.c,v 1.26 2004/04/06 20:06:51 markm Exp $"); -*/ + +#include +#include #include "shell.h" #include "output.h" -#include "machdep.h" #include "memalloc.h" #include "error.h" +#include "machdep.h" #include "mystring.h" -#include "expand.h" -#include -#include /* * Like malloc, but returns an error when out of space. */ pointer -ckmalloc(int nbytes) +ckmalloc(size_t nbytes) { pointer p; - if ((p = malloc(nbytes)) == NULL) + p = malloc(nbytes); + if (p == NULL) error("Out of space"); return p; } @@ -72,7 +74,8 @@ ckmalloc(int nbytes) pointer ckrealloc(pointer p, int nbytes) { - if ((p = realloc(p, nbytes)) == NULL) + p = realloc(p, nbytes); + if (p == NULL) error("Out of space"); return p; } @@ -98,56 +101,46 @@ savestr(const char *s) * to make this more efficient, and also to avoid all sorts of exception * handling code to handle interrupts in the middle of a parse. * - * The size 496 was chosen because with 16-byte alignment the total size - * for the allocated block is 512. + * The size 504 was chosen because the Ultrix malloc handles that size + * well. */ -#define MINSIZE 496 /* minimum size of a block. */ - +#define MINSIZE 504 /* minimum size of a block */ struct stack_block { struct stack_block *prev; - /* Data follows */ + char space[MINSIZE]; }; -#define SPACE(sp) ((char*)(sp) + ALIGN(sizeof(struct stack_block))) -STATIC struct stack_block *stackp; -STATIC struct stackmark *markp; -char *stacknxt; -int stacknleft; +struct stack_block stackbase; +struct stack_block *stackp = &stackbase; +struct stackmark *markp; +char *stacknxt = stackbase.space; +int stacknleft = MINSIZE; int sstrnleft; int herefd = -1; - -static void -stnewblock(int nbytes) -{ - struct stack_block *sp; - int allocsize; - - if (nbytes < MINSIZE) - nbytes = MINSIZE; - - allocsize = ALIGN(sizeof(struct stack_block)) + ALIGN(nbytes); - - INTOFF; - sp = ckmalloc(allocsize); - sp->prev = stackp; - stacknxt = SPACE(sp); - stacknleft = allocsize - (stacknxt - (char*)sp); - stackp = sp; - INTON; -} - - pointer stalloc(int nbytes) { char *p; - nbytes = ALIGN(nbytes); - if (nbytes > stacknleft) - stnewblock(nbytes); + nbytes = SHELL_ALIGN(nbytes); + if (nbytes > stacknleft) { + int blocksize; + struct stack_block *sp; + + blocksize = nbytes; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + INTOFF; + sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + stackp = sp; + INTON; + } p = stacknxt; stacknxt += nbytes; stacknleft -= nbytes; @@ -159,7 +152,7 @@ void stunalloc(pointer p) { if (p == NULL) { /*DEBUG */ - write(STDERR_FILENO, "stunalloc\n", 10); + write(2, "stunalloc\n", 10); abort(); } stacknleft += stacknxt - (char *)p; @@ -210,32 +203,27 @@ popstackmark(struct stackmark *mark) void growstackblock(void) { - char *p; - int newlen; - char *oldspace; - int oldlen; - struct stack_block *sp; - struct stack_block *oldstackp; - struct stackmark *xmark; + int newlen = SHELL_ALIGN(stacknleft * 2 + 100); - newlen = (stacknleft == 0) ? MINSIZE : stacknleft * 2 + 100; - newlen = ALIGN(newlen); - oldspace = stacknxt; - oldlen = stacknleft; + if (stacknxt == stackp->space && stackp != &stackbase) { + struct stack_block *oldstackp; + struct stackmark *xmark; + struct stack_block *sp; - if (stackp != NULL && stacknxt == SPACE(stackp)) { INTOFF; oldstackp = stackp; - stackp = oldstackp->prev; - sp = ckrealloc((pointer)oldstackp, newlen); + sp = stackp; + stackp = sp->prev; + sp = ckrealloc((pointer)sp, + sizeof(struct stack_block) - MINSIZE + newlen); sp->prev = stackp; stackp = sp; - stacknxt = SPACE(sp); - stacknleft = newlen - (stacknxt - (char*)sp); + stacknxt = sp->space; + stacknleft = newlen; /* * Stack marks pointing to the start of the old block - * must be relocated to point to the new block + * must be relocated to point to the new block */ xmark = markp; while (xmark != NULL && xmark->stackp == oldstackp) { @@ -246,27 +234,26 @@ growstackblock(void) } INTON; } else { - p = stalloc(newlen); - if (oldlen != 0) - memcpy(p, oldspace, oldlen); - stunalloc(p); + char *oldspace = stacknxt; + int oldlen = stacknleft; + char *p = stalloc(newlen); + + (void)memcpy(p, oldspace, oldlen); + stacknxt = p; /* free the space */ + stacknleft += newlen; /* we just allocated */ } } - - void grabstackblock(int len) { - len = ALIGN(len); + len = SHELL_ALIGN(len); stacknxt += len; stacknleft -= len; } - - /* - * The following routines are somewhat easier to use that the above. + * The following routines are somewhat easier to use than the above. * The user declares a variable of type STACKSTR, which may be declared * to be a register. The macro STARTSTACKSTR initializes things. Then * the user uses the macro STPUTC to add characters to the string. In @@ -283,13 +270,10 @@ grabstackblock(int len) * is space for at least one character. */ - char * growstackstr(void) { - int len; - - len = stackblocksize(); + int len = stackblocksize(); if (herefd >= 0 && len >= 1024) { xwrite(herefd, stackblock(), len); sstrnleft = len - 1; @@ -300,7 +284,6 @@ growstackstr(void) return stackblock() + len; } - /* * Called from CHECKSTRSPACE. */ @@ -308,24 +291,17 @@ growstackstr(void) char * makestrspace(void) { - int len; - - len = stackblocksize() - sstrnleft; + int len = stackblocksize() - sstrnleft; growstackblock(); sstrnleft = stackblocksize() - len; return stackblock() + len; } - - void ungrabstackstr(char *s, char *p) { stacknleft += stacknxt - s; stacknxt = s; sstrnleft = stacknleft - (p - s); -} -/* - * $PchId: memalloc.c,v 1.5 2006/05/22 12:03:26 philip Exp $ - */ +} diff --git a/minix/commands/ash/memalloc.h b/bin/sh/memalloc.h similarity index 91% rename from minix/commands/ash/memalloc.h rename to bin/sh/memalloc.h index 68b89a207..8e5b07615 100644 --- a/minix/commands/ash/memalloc.h +++ b/bin/sh/memalloc.h @@ -1,3 +1,5 @@ +/* $NetBSD: memalloc.h,v 1.15 2008/02/15 17:26:06 matt Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,14 +32,13 @@ * SUCH DAMAGE. * * @(#)memalloc.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/memalloc.h,v 1.9 2004/04/06 20:06:51 markm Exp $ */ struct stackmark { struct stack_block *stackp; char *stacknxt; int stacknleft; - struct stackmark *marknext; + struct stackmark *marknext; }; @@ -46,7 +47,7 @@ extern int stacknleft; extern int sstrnleft; extern int herefd; -pointer ckmalloc(int); +pointer ckmalloc(size_t); pointer ckrealloc(pointer, int); char *savestr(const char *); pointer stalloc(int); @@ -74,7 +75,3 @@ void ungrabstackstr(char *, char *); #define grabstackstr(p) stalloc(stackblocksize() - sstrnleft) #define ckfree(p) free((pointer)(p)) - -/* - * $PchId: memalloc.h,v 1.3 2006/03/30 11:39:41 philip Exp $ - */ diff --git a/minix/commands/ash/miscbltin.c b/bin/sh/miscbltin.c similarity index 57% rename from minix/commands/ash/miscbltin.c rename to bin/sh/miscbltin.c index d63b1e27d..b7c1c5ad8 100644 --- a/minix/commands/ash/miscbltin.c +++ b/bin/sh/miscbltin.c @@ -1,3 +1,5 @@ +/* $NetBSD: miscbltin.c,v 1.42 2012/06/11 18:28:10 njoly Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,32 +32,28 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: miscbltin.c,v 1.42 2012/06/11 18:28:10 njoly Exp $"); #endif #endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/miscbltin.c,v 1.30 2004/04/06 20:06:51 markm Exp $"); -*/ /* - * Miscellaneous builtins. + * Miscelaneous builtins. */ -#include +#include /* quad_t */ +#include /* BSD4_4 */ #include #include -#include -#include +#include #include +#include #include #include -#include -#include -#include -#include #include "shell.h" #include "options.h" @@ -63,159 +61,148 @@ __FBSDID("$FreeBSD: src/bin/sh/miscbltin.c,v 1.30 2004/04/06 20:06:51 markm Exp #include "output.h" #include "memalloc.h" #include "error.h" -#include "mystring.h" #include "builtins.h" +#include "mystring.h" + +#undef rflag + -#undef eflag /* - * The read builtin. The -r option causes backslashes to be treated like - * ordinary characters. + * The read builtin. + * Backslahes escape the next char unless -r is specified. * * This uses unbuffered input, which may be avoidable in some cases. + * + * Note that if IFS=' :' then read x y should work so that: + * 'a b' x='a', y='b' + * ' a b ' x='a', y='b' + * ':b' x='', y='b' + * ':' x='', y='' + * '::' x='', y='' + * ': :' x='', y='' + * ':::' x='', y='::' + * ':b c:' x='', y='b c:' */ int -readcmd(int argc __unused, char **argv __unused) +readcmd(int argc, char **argv) { char **ap; - int backslash; char c; int rflag; char *prompt; - char *ifs; + const char *ifs; char *p; int startword; int status; int i; - struct timeval tv; - char *tvptr; -#ifndef __minix - fd_set ifds; - struct termios told, tnew; - int tsaved; -#endif + int is_ifs; + int saveall = 0; rflag = 0; prompt = NULL; - tv.tv_sec = -1; - tv.tv_usec = 0; - while ((i = nextopt("erp:t:")) != '\0') { - switch(i) { - case 'p': - prompt = shoptarg; - break; - case 'e': - break; - case 'r': + while ((i = nextopt("p:r")) != '\0') { + if (i == 'p') + prompt = optionarg; + else rflag = 1; - break; - case 't': - tv.tv_sec = strtol(shoptarg, &tvptr, 0); - if (tvptr == shoptarg) - error("timeout value"); - switch(*tvptr) { - case 0: - case 's': - break; - case 'h': - tv.tv_sec *= 60; - /* FALLTHROUGH */ - case 'm': - tv.tv_sec *= 60; - break; - default: - error("timeout unit"); - } - break; - } } + if (prompt && isatty(0)) { out2str(prompt); flushall(); } + if (*(ap = argptr) == NULL) error("arg count"); - if ((ifs = bltinlookup("IFS", 1)) == NULL) - ifs = nullstr; - if (tv.tv_sec >= 0) { -#ifdef __minix - abort(); -#else - /* - * See if we can disable input processing; this will - * not give the desired result if we are in a pipeline - * and someone upstream is still in line-by-line mode. - */ - tsaved = 0; - if (tcgetattr(0, &told) == 0) { - memcpy(&tnew, &told, sizeof(told)); - cfmakeraw(&tnew); - tcsetattr(0, TCSANOW, &tnew); - tsaved = 1; - } - /* - * Wait for something to become available. - */ - FD_ZERO(&ifds); - FD_SET(0, &ifds); - status = select(1, &ifds, NULL, NULL, &tv); - if (tsaved) - tcsetattr(0, TCSANOW, &told); - /* - * If there's nothing ready, return an error. - */ - if (status <= 0) - return(1); -#endif - } + if ((ifs = bltinlookup("IFS", 1)) == NULL) + ifs = " \t\n"; status = 0; - startword = 1; - backslash = 0; + startword = 2; STARTSTACKSTR(p); for (;;) { - if (read(STDIN_FILENO, &c, 1) != 1) { + if (read(0, &c, 1) != 1) { status = 1; break; } if (c == '\0') continue; - if (backslash) { - backslash = 0; + if (c == '\\' && !rflag) { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } if (c != '\n') STPUTC(c, p); continue; } - if (!rflag && c == '\\') { - backslash++; - continue; - } if (c == '\n') break; - if (startword && *ifs == ' ' && strchr(ifs, c)) { + if (strchr(ifs, c)) + is_ifs = strchr(" \t\n", c) ? 1 : 2; + else + is_ifs = 0; + + if (startword != 0) { + if (is_ifs == 1) { + /* Ignore leading IFS whitespace */ + if (saveall) + STPUTC(c, p); + continue; + } + if (is_ifs == 2 && startword == 1) { + /* Only one non-whitespace IFS per word */ + startword = 2; + if (saveall) + STPUTC(c, p); + continue; + } + } + + if (is_ifs == 0) { + /* append this character to the current variable */ + startword = 0; + if (saveall) + /* Not just a spare terminator */ + saveall++; + STPUTC(c, p); continue; } - startword = 0; - if (backslash && c == '\\') { - if (read(STDIN_FILENO, &c, 1) != 1) { - status = 1; - break; - } - STPUTC(c, p); - } else if (ap[1] != NULL && strchr(ifs, c) != NULL) { - STACKSTRNUL(p); - setvar(*ap, stackblock(), 0); - ap++; - startword = 1; - STARTSTACKSTR(p); - } else { + + /* end of variable... */ + startword = is_ifs; + + if (ap[1] == NULL) { + /* Last variable needs all IFS chars */ + saveall++; STPUTC(c, p); + continue; } + + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + STARTSTACKSTR(p); } STACKSTRNUL(p); + + /* Remove trailing IFS chars */ + for (; stackblock() <= --p; *p = 0) { + if (!strchr(ifs, *p)) + break; + if (strchr(" \t\n", *p)) + /* Always remove whitespace */ + continue; + if (saveall > 1) + /* Don't remove non-whitespace unless it was naked */ + break; + } setvar(*ap, stackblock(), 0); + + /* Set any remaining args to "" */ while (*++ap != NULL) setvar(*ap, nullstr, 0); return status; @@ -224,7 +211,7 @@ readcmd(int argc __unused, char **argv __unused) int -umaskcmd(int argc __unused, char **argv) +umaskcmd(int argc, char **argv) { char *ap; int mask; @@ -276,7 +263,7 @@ umaskcmd(int argc __unused, char **argv) out1fmt("%.4o\n", mask); } } else { - if (isdigit(*ap)) { + if (isdigit((unsigned char)*ap)) { mask = 0; do { if (*ap >= '8' || *ap < '0') @@ -286,44 +273,23 @@ umaskcmd(int argc __unused, char **argv) umask(mask); } else { void *set; - if ((set = setmode (ap)) == 0) - error("Illegal number: %s", ap); - mask = getmode (set, ~mask & 0777); + INTOFF; + if ((set = setmode(ap)) != 0) { + mask = getmode(set, ~mask & 0777); + ckfree(set); + } + INTON; + if (!set) + error("Cannot set mode `%s' (%s)", ap, + strerror(errno)); + umask(~mask & 0777); - free(set); } } return 0; } -#ifdef __minix -struct rlimit -{ - unsigned long rlim_cur; /* current (soft) limit */ - unsigned long rlim_max; /* maximum value for rlim_cur */ -}; -#define RLIM_INFINITY (((unsigned long)1 << 31) - 1) - -int getrlimit (int, struct rlimit *); -int setrlimit (int, const struct rlimit *); - -int getrlimit(resource, rlp) -int resource; -struct rlimit *rlp; -{ - errno= ENOSYS; - return -1; -} -int setrlimit(resource, rlp) -int resource; -const struct rlimit *rlp; -{ - errno= ENOSYS; - return -1; -} -#endif - /* * ulimit builtin * @@ -336,7 +302,7 @@ const struct rlimit *rlp; struct limits { const char *name; - const char *units; + const char *unit; int cmd; int factor; /* multiply by to get rlim_{cur,max} values */ char option; @@ -344,49 +310,52 @@ struct limits { static const struct limits limits[] = { #ifdef RLIMIT_CPU - { "cpu time", "seconds", RLIMIT_CPU, 1, 't' }, + { "time", "seconds", RLIMIT_CPU, 1, 't' }, #endif #ifdef RLIMIT_FSIZE - { "file size", "512-blocks", RLIMIT_FSIZE, 512, 'f' }, + { "file", "blocks", RLIMIT_FSIZE, 512, 'f' }, #endif #ifdef RLIMIT_DATA - { "data seg size", "kbytes", RLIMIT_DATA, 1024, 'd' }, + { "data", "kbytes", RLIMIT_DATA, 1024, 'd' }, #endif #ifdef RLIMIT_STACK - { "stack size", "kbytes", RLIMIT_STACK, 1024, 's' }, + { "stack", "kbytes", RLIMIT_STACK, 1024, 's' }, #endif #ifdef RLIMIT_CORE - { "core file size", "512-blocks", RLIMIT_CORE, 512, 'c' }, + { "coredump", "blocks", RLIMIT_CORE, 512, 'c' }, #endif #ifdef RLIMIT_RSS - { "max memory size", "kbytes", RLIMIT_RSS, 1024, 'm' }, + { "memory", "kbytes", RLIMIT_RSS, 1024, 'm' }, #endif #ifdef RLIMIT_MEMLOCK - { "locked memory", "kbytes", RLIMIT_MEMLOCK, 1024, 'l' }, + { "locked memory","kbytes", RLIMIT_MEMLOCK, 1024, 'l' }, +#endif +#ifdef RLIMIT_NTHR + { "thread", "threads", RLIMIT_NTHR, 1, 'r' }, #endif #ifdef RLIMIT_NPROC - { "max user processes", (char *)0, RLIMIT_NPROC, 1, 'u' }, + { "process", "processes", RLIMIT_NPROC, 1, 'p' }, #endif #ifdef RLIMIT_NOFILE - { "open files", (char *)0, RLIMIT_NOFILE, 1, 'n' }, + { "nofiles", "descriptors", RLIMIT_NOFILE, 1, 'n' }, #endif #ifdef RLIMIT_VMEM - { "virtual mem size", "kbytes", RLIMIT_VMEM, 1024, 'v' }, + { "vmemory", "kbytes", RLIMIT_VMEM, 1024, 'v' }, #endif #ifdef RLIMIT_SWAP - { "swap limit", "kbytes", RLIMIT_SWAP, 1024, 'w' }, + { "swap", "kbytes", RLIMIT_SWAP, 1024, 'w' }, #endif #ifdef RLIMIT_SBSIZE - { "sbsize", "bytes", RLIMIT_SBSIZE, 1, 'b' }, + { "sbsize", "bytes", RLIMIT_SBSIZE, 1, 'b' }, #endif - { (char *) 0, (char *)0, 0, 0, '\0' } + { NULL, NULL, 0, 0, '\0' } }; int -ulimitcmd(int argc __unused, char **argv __unused) +ulimitcmd(int argc, char **argv) { int c; - intmax_t val = 0; + rlim_t val = 0; enum { SOFT = 0x1, HARD = 0x2 } how = SOFT | HARD; const struct limits *l; @@ -395,7 +364,7 @@ ulimitcmd(int argc __unused, char **argv __unused) struct rlimit limit; what = 'f'; - while ((optc = nextopt("HSatfdsmcnuvlb")) != '\0') + while ((optc = nextopt("HSabtfdscmlrpnv")) != '\0') switch (optc) { case 'H': how = HARD; @@ -424,56 +393,48 @@ ulimitcmd(int argc __unused, char **argv __unused) if (strcmp(p, "unlimited") == 0) val = RLIM_INFINITY; else { - val = 0; + val = (rlim_t) 0; while ((c = *p++) >= '0' && c <= '9') - { val = (val * 10) + (long)(c - '0'); - if (val < 0) - break; - } if (c) error("bad number"); val *= l->factor; } } if (all) { - for (l = limits; l->name; l++) { - char optbuf[40]; - if (getrlimit(l->cmd, &limit) < 0) - error("can't get limit: %s", strerror(errno)); + for (l = limits; l->name; l++) { + getrlimit(l->cmd, &limit); if (how & SOFT) val = limit.rlim_cur; else if (how & HARD) val = limit.rlim_max; - if (l->units) - snprintf(optbuf, sizeof(optbuf), - "(%s, -%c) ", l->units, l->option); - else - snprintf(optbuf, sizeof(optbuf), - "(-%c) ", l->option); - out1fmt("%-18s %18s ", l->name, optbuf); + out1fmt("%-13s (-%c %-11s) ", l->name, l->option, + l->unit); if (val == RLIM_INFINITY) out1fmt("unlimited\n"); else { val /= l->factor; - out1fmt("%jd\n", (intmax_t)val); +#ifdef BSD4_4 + out1fmt("%lld\n", (long long) val); +#else + out1fmt("%ld\n", (long) val); +#endif } } return 0; } - if (getrlimit(l->cmd, &limit) < 0) - error("can't get limit: %s", strerror(errno)); + getrlimit(l->cmd, &limit); if (set) { - if (how & SOFT) - limit.rlim_cur = val; if (how & HARD) limit.rlim_max = val; + if (how & SOFT) + limit.rlim_cur = val; if (setrlimit(l->cmd, &limit) < 0) - error("bad limit: %s", strerror(errno)); + error("error setting limit (%s)", strerror(errno)); } else { if (how & SOFT) val = limit.rlim_cur; @@ -485,12 +446,12 @@ ulimitcmd(int argc __unused, char **argv __unused) else { val /= l->factor; - out1fmt("%jd\n", (intmax_t)val); +#ifdef BSD4_4 + out1fmt("%lld\n", (long long) val); +#else + out1fmt("%ld\n", (long) val); +#endif } } return 0; } - -/* - * $PchId: miscbltin.c,v 1.7 2006/05/23 11:59:08 philip Exp $ - */ diff --git a/bin/sh/miscbltin.h b/bin/sh/miscbltin.h new file mode 100644 index 000000000..4c12c8249 --- /dev/null +++ b/bin/sh/miscbltin.h @@ -0,0 +1,31 @@ +/* $NetBSD: miscbltin.h,v 1.3 2003/08/21 17:57:53 christos Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +int readcmd(int, char **); +int umaskcmd(int, char **); +int ulimitcmd(int, char **); diff --git a/minix/commands/ash/mkbuiltins.sh b/bin/sh/mkbuiltins old mode 100755 new mode 100644 similarity index 57% rename from minix/commands/ash/mkbuiltins.sh rename to bin/sh/mkbuiltins index 73109ad70..2ebf7ac77 --- a/minix/commands/ash/mkbuiltins.sh +++ b/bin/sh/mkbuiltins @@ -1,4 +1,5 @@ #!/bin/sh - +# $NetBSD: mkbuiltins,v 1.22 2009/10/06 19:56:58 apb Exp $ # # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. @@ -14,7 +15,7 @@ # 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. -# 4. Neither the name of the University nor the names of its contributors +# 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. # @@ -31,68 +32,105 @@ # SUCH DAMAGE. # # @(#)mkbuiltins 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/mkbuiltins,v 1.13 2004/04/06 20:06:51 markm Exp $ -#temp=`/usr/bin/mktemp -t ka` -temp=/tmp/mkb$$ havehist=1 -if [ "X$1" = "X-h" ]; then +if [ x"$1" = x"-h" ]; then havehist=0 shift fi + +shell=$1 +builtins=$2 +objdir=$3 + havejobs=0 -if [ "X$1" = "X-j" ]; then - havejobs=0 - shift -elif grep '^#define[ ]*JOBS[ ]*1' $2 > /dev/null -then havejobs=1 +if grep '^#define JOBS[ ]*1' ${shell} > /dev/null +then + havejobs=1 fi -objdir=$1 -exec > ${objdir}/builtins.c -cat <<\! -/* + +exec <$builtins 3> ${objdir}/builtins.c 4> ${objdir}/builtins.h + +echo '/* * This file was generated by the mkbuiltins program. */ -#include #include "shell.h" #include "builtins.h" -! -awk '/^[^#]/ {if(('$havejobs' || $2 != "-j") && ('$havehist' || $2 != "-h")) \ - print $0}' $3 | sed 's/-[hj]//' > $temp -#awk '{ printf "int %s();\n", $1}' $temp -echo ' -int (*const builtinfunc[]) (int, char **) = {' -awk '/^[^#]/ { printf "\t%s,\n", $1}' $temp -echo '}; +const struct builtincmd builtincmd[] = { +' >&3 -const struct builtincmd builtincmd[] = {' -awk '{ for (i = 2 ; i <= NF ; i++) { - printf "\t{ \"%s\", %d },\n", $i, NR-1 - }}' $temp -echo ' { NULL, 0 } -};' - -exec > ${objdir}/builtins.h -cat <<\! -/* +echo '/* * This file was generated by the mkbuiltins program. */ -! -tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp | - awk '{ printf "#define %s %d\n", $1, NR-1}' -echo ' +#include + struct builtincmd { - char *name; - int code; + const char *name; + int (*builtin)(int, char **); }; -extern int (*const builtinfunc[]) (int, char **); -extern const struct builtincmd builtincmd[];' -awk '{ printf "int %s (int, char **);\n", $1 }' < $temp -rm -f $temp +extern const struct builtincmd builtincmd[]; +extern const struct builtincmd splbltincmd[]; -# -# $PchId: mkbuiltins,v 1.6 2006/05/22 12:42:58 philip Exp $ +' >&4 + +specials= + +while read line +do + set -- $line + [ -z "$1" ] && continue + case "$1" in + \#if*|\#def*|\#end*) + echo $line >&3 + echo $line >&4 + continue + ;; + \#*) + continue + ;; + esac + + func=$1 + shift + [ x"$1" = x'-j' ] && { + [ $havejobs = 0 ] && continue + shift + } + [ x"$1" = x'-h' ] && { + [ $havehist = 0 ] && continue + shift + } + echo 'int '"$func"'(int, char **);' >&4 + while + [ $# != 0 ] && [ x"$1" != x'#' ] + do + [ x"$1" = x'-s' ] && { + specials="$specials $2 $func" + shift 2 + continue; + } + [ x"$1" = x'-u' ] && shift + echo ' { "'$1'", '"$func"' },' >&3 + shift + done +done + +echo ' { 0, 0 },' >&3 +echo '};' >&3 +echo >&3 +echo 'const struct builtincmd splbltincmd[] = {' >&3 + +set -- $specials +while + [ $# != 0 ] +do + echo ' { "'$1'", '"$2"' },' >&3 + shift 2 +done + +echo ' { 0, 0 },' >&3 +echo "};" >&3 diff --git a/bin/sh/mkinit.sh b/bin/sh/mkinit.sh new file mode 100755 index 000000000..a7dcb91f2 --- /dev/null +++ b/bin/sh/mkinit.sh @@ -0,0 +1,170 @@ +#! /bin/sh +# $NetBSD: mkinit.sh,v 1.5 2008/10/23 20:21:57 apb Exp $ + +# Copyright (c) 2003 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by David Laight. +# +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + +srcs="$*" + +nl=' +' +openparen='(' +backslash='\' + +includes=' "shell.h" "mystring.h" "init.h" ' +defines= +decles= +event_init= +event_reset= +event_shellproc= + +for src in $srcs; do + exec <$src + decnl="$nl" + while IFS=; read -r line; do + [ "$line" = x ] + case "$line " in + INIT["{ "]* ) event=init;; + RESET["{ "]* ) event=reset;; + SHELLPROC["{ "]* ) event=shellproc;; + INCLUDE[\ \ ]* ) + IFS=' ' + set -- $line + # ignore duplicates + [ "${includes}" != "${includes%* $2 }" ] && continue + includes="$includes$2 " + continue + ;; + MKINIT\ ) + # struct declaration + decles="$decles$nl" + while + read -r line + decles="${decles}${line}${nl}" + [ "$line" != "};" ] + do + : + done + decnl="$nl" + continue + ;; + MKINIT["{ "]* ) + # strip initialiser + def=${line#MKINIT} + comment="${def#*;}" + def="${def%;$comment}" + def="${def%%=*}" + def="${def% }" + decles="${decles}${decnl}extern${def};${comment}${nl}" + decnl= + continue + ;; + \#define[\ \ ]* ) + IFS=' ' + set -- $line + # Ignore those with arguments + [ "$2" = "${2##*$openparen}" ] || continue + # and multiline definitions + [ "$line" = "${line%$backslash}" ] || continue + defines="${defines}#undef $2${nl}${line}${nl}" + continue + ;; + * ) continue;; + esac + # code for events + ev="${nl} /* from $src: */${nl} {${nl}" + # Indent the text by an extra + while + read -r line + [ "$line" != "}" ] + do + [ -n "$line" -a "$line" = "${line###}" ] && + line=" $line" + ev="${ev}${line}${nl}" + done + ev="${ev} }${nl}" + eval event_$event=\"\$event_$event\$ev\" + done +done + +exec >init.c.tmp + +echo "/*" +echo " * This file was generated by the mkinit program." +echo " */" +echo + +IFS=' ' +for f in $includes; do + echo "#include $f" +done + +echo +echo +echo +echo "$defines" +echo +echo "$decles" +echo +echo +echo "/*" +echo " * Initialization code." +echo " */" +echo +echo "void" +echo "init(void)" +echo "{" +echo "${event_init}" +echo "}" +echo +echo +echo +echo "/*" +echo " * This routine is called when an error or an interrupt occurs in an" +echo " * interactive shell and control is returned to the main command loop." +echo " */" +echo +echo "void" +echo "reset(void)" +echo "{" +echo "${event_reset}" +echo "}" +echo +echo +echo +echo "/*" +echo " * This routine is called to initialize the shell to run a shell procedure." +echo " */" +echo +echo "void" +echo "initshellproc(void)" +echo "{" +echo "${event_shellproc}" +echo "}" + +exec >&- +mv init.c.tmp init.c diff --git a/bin/sh/mknodes.sh b/bin/sh/mknodes.sh new file mode 100755 index 000000000..2208fd846 --- /dev/null +++ b/bin/sh/mknodes.sh @@ -0,0 +1,214 @@ +#! /bin/sh +# $NetBSD: mknodes.sh,v 1.2 2008/04/29 06:53:00 martin Exp $ + +# Copyright (c) 2003 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by David Laight. +# +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + +nodetypes=$1 +nodes_pat=$2 +objdir="$3" + +exec <$nodetypes +exec >$objdir/nodes.h.tmp + +echo "/*" +echo " * This file was generated by mknodes.sh" +echo " */" +echo + +tagno=0 +while IFS=; read -r line; do + line="${line%%#*}" + IFS=' ' + set -- $line + IFS= + [ -z "$2" ] && continue + case "$line" in + [" "]* ) + IFS=' ' + [ $field = 0 ] && struct_list="$struct_list $struct" + eval field_${struct}_$field=\"\$*\" + eval numfld_$struct=\$field + field=$(($field + 1)) + ;; + * ) + define=$1 + struct=$2 + echo "#define $define $tagno" + tagno=$(($tagno + 1)) + eval define_$struct=\"\$define_$struct \$define\" + struct_define="$struct_define $struct" + field=0 + ;; + esac +done + +echo + +IFS=' ' +for struct in $struct_list; do + echo + echo + echo "struct $struct {" + field=0 + while + eval line=\"\$field_${struct}_$field\" + field=$(($field + 1)) + [ -n "$line" ] + do + IFS=' ' + set -- $line + name=$1 + case $2 in + nodeptr ) type="union node *";; + nodelist ) type="struct nodelist *";; + string ) type="char *";; + int ) type="int ";; + * ) name=; shift 2; type="$*";; + esac + echo " $type$name;" + done + echo "};" +done + +echo +echo +echo "union node {" +echo " int type;" +for struct in $struct_list; do + echo " struct $struct $struct;" +done +echo "};" +echo +echo +echo "struct nodelist {" +echo " struct nodelist *next;" +echo " union node *n;" +echo "};" +echo +echo +echo "union node *copyfunc(union node *);" +echo "void freefunc(union node *);" + +mv $objdir/nodes.h.tmp $objdir/nodes.h || exit 1 + +exec <$nodes_pat +exec >$objdir/nodes.c.tmp + +echo "/*" +echo " * This file was generated by mknodes.sh" +echo " */" +echo + +while IFS=; read -r line; do + IFS=' ' + set -- $line + IFS= + case "$1" in + '%SIZES' ) + echo "static const short nodesize[$tagno] = {" + IFS=' ' + for struct in $struct_define; do + echo " SHELL_ALIGN(sizeof (struct $struct))," + done + echo "};" + ;; + '%CALCSIZE' ) + echo " if (n == NULL)" + echo " return;" + echo " funcblocksize += nodesize[n->type];" + echo " switch (n->type) {" + IFS=' ' + for struct in $struct_list; do + eval defines=\"\$define_$struct\" + for define in $defines; do + echo " case $define:" + done + eval field=\$numfld_$struct + while + [ $field != 0 ] + do + eval line=\"\$field_${struct}_$field\" + field=$(($field - 1)) + IFS=' ' + set -- $line + name=$1 + cl=")" + case $2 in + nodeptr ) fn=calcsize;; + nodelist ) fn=sizenodelist;; + string ) fn="funcstringsize += strlen" + cl=") + 1";; + * ) continue;; + esac + echo " ${fn}(n->$struct.$name${cl};" + done + echo " break;" + done + echo " };" + ;; + '%COPY' ) + echo " if (n == NULL)" + echo " return NULL;" + echo " new = funcblock;" + echo " funcblock = (char *) funcblock + nodesize[n->type];" + echo " switch (n->type) {" + IFS=' ' + for struct in $struct_list; do + eval defines=\"\$define_$struct\" + for define in $defines; do + echo " case $define:" + done + eval field=\$numfld_$struct + while + [ $field != 0 ] + do + eval line=\"\$field_${struct}_$field\" + field=$(($field - 1)) + IFS=' ' + set -- $line + name=$1 + case $2 in + nodeptr ) fn="copynode(";; + nodelist ) fn="copynodelist(";; + string ) fn="nodesavestr(";; + int ) fn=;; + * ) continue;; + esac + f="$struct.$name" + echo " new->$f = ${fn}n->$f${fn:+)};" + done + echo " break;" + done + echo " };" + echo " new->type = n->type;" + ;; + * ) echo "$line";; + esac +done + +mv $objdir/nodes.c.tmp $objdir/nodes.c || exit 1 diff --git a/minix/commands/ash/mktokens.sh b/bin/sh/mktokens old mode 100755 new mode 100644 similarity index 75% rename from minix/commands/ash/mktokens.sh rename to bin/sh/mktokens index e529902b4..d12bdee5b --- a/minix/commands/ash/mktokens.sh +++ b/bin/sh/mktokens @@ -1,4 +1,5 @@ #!/bin/sh - +# $NetBSD: mktokens,v 1.12 2008/10/25 22:18:15 apb Exp $ # # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. @@ -14,7 +15,7 @@ # 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. -# 4. Neither the name of the University nor the names of its contributors +# 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. # @@ -31,17 +32,15 @@ # SUCH DAMAGE. # # @(#)mktokens 8.1 (Berkeley) 5/31/93 -# $FreeBSD: src/bin/sh/mktokens,v 1.9 2004/04/06 20:06:51 markm Exp $ -# All calls to awk removed, because Minix bawk is deficient. (kjb) +: ${AWK:=awk} +: ${SED:=sed} # The following is a list of tokens. The second column is nonzero if the # token marks the end of a list. The third column is the name to print in # error messages. -#temp=`/usr/bin/mktemp -t ka` -temp=/tmp/mkt$$ -cat > $temp <<\! +cat > /tmp/ka$$ <<\! TEOF 1 end of file TNL 0 newline TSEMI 0 ";" @@ -71,54 +70,26 @@ TCASE 0 "case" TESAC 1 "esac" TNOT 0 "!" ! -nl=`wc -l $temp` +nl=`wc -l /tmp/ka$$` exec > token.h -i=0 -while read line -do - set -$- $line - echo "#define $1 $i" - i=`expr $i + 1` -done <$temp +${AWK} '{print "#define " $1 " " NR-1}' /tmp/ka$$ echo ' /* Array indicating which tokens mark the end of a list */ const char tokendlist[] = {' -while read line -do - set -$- $line - echo " $2," -done <$temp +${AWK} '{print "\t" $2 ","}' /tmp/ka$$ echo '}; const char *const tokname[] = {' -sed -e 's/"/\\"/g' \ +${SED} -e 's/"/\\"/g' \ -e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \ - $temp + /tmp/ka$$ echo '}; ' -i=0 -go= -sed 's/"//g' $temp | - while read line - do - set -$- $line - if [ "$1" = TIF ] - then - echo "#define KWDOFFSET $i" - echo - echo "const char *const parsekwd[] = {" - go=true - fi - if [ "$go" ] - then - echo " \"$3\"," - fi - i=`expr $i + 1` - done +${SED} 's/"//g' /tmp/ka$$ | ${AWK} ' +/TIF/{print "#define KWDOFFSET " NR-1; print ""; + print "const char *const parsekwd[] = {"} +/TIF/,/neverfound/{print " \"" $3 "\","}' echo ' 0 };' -rm $temp - -# -# $PchId: mktokens,v 1.5 2006/05/22 12:43:35 philip Exp $ +rm /tmp/ka$$ diff --git a/minix/commands/ash/myhistedit.h b/bin/sh/myhistedit.h similarity index 84% rename from minix/commands/ash/myhistedit.h rename to bin/sh/myhistedit.h index f6e295bda..be66df04d 100644 --- a/minix/commands/ash/myhistedit.h +++ b/bin/sh/myhistedit.h @@ -1,3 +1,5 @@ +/* $NetBSD: myhistedit.h,v 1.11 2011/06/18 21:18:46 christos Exp $ */ + /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -10,7 +12,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -27,7 +29,6 @@ * SUCH DAMAGE. * * @(#)myhistedit.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/myhistedit.h,v 1.10 2004/04/06 20:06:51 markm Exp $ */ #include @@ -38,13 +39,8 @@ extern int displayhist; void histedit(void); void sethistsize(const char *); -int histcmd(int, char **); -int bindcmd(int, char **); - -/* From libedit */ -void re_goto_bottom(EditLine *); - -/* - * $PchId: myhistedit.h,v 1.5 2006/03/29 15:55:18 philip Exp $ - */ +void setterm(const char *); +int inputrc(int, char **); +int not_fcnumber(char *); +int str_to_event(const char *, int); diff --git a/minix/commands/ash/mystring.c b/bin/sh/mystring.c similarity index 90% rename from minix/commands/ash/mystring.c rename to bin/sh/mystring.c index 7957c333a..d2d582552 100644 --- a/minix/commands/ash/mystring.c +++ b/bin/sh/mystring.c @@ -1,3 +1,5 @@ +/* $NetBSD: mystring.c,v 1.17 2013/04/28 17:01:28 dholland Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,15 +32,14 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)mystring.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: mystring.c,v 1.17 2013/04/28 17:01:28 dholland Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/mystring.c,v 1.13 2004/04/06 20:06:51 markm Exp $"); -*/ /* * String functions. @@ -57,7 +58,7 @@ __FBSDID("$FreeBSD: src/bin/sh/mystring.c,v 1.13 2004/04/06 20:06:51 markm Exp $ #include "mystring.h" -char nullstr[1]; /* zero length string */ +const char nullstr[1]; /* zero length string */ /* * equal - #defined in mystring.h @@ -109,8 +110,9 @@ prefix(const char *pfx, const char *string) int number(const char *s) { + if (! is_number(s)) - error("Illegal number: %s", (char *)s); + error("Illegal number: %s", s); return atoi(s); } @@ -129,7 +131,3 @@ is_number(const char *p) } while (*++p != '\0'); return 1; } - -/* - * $PchId: mystring.c,v 1.4 2006/05/22 12:21:53 philip Exp $ - */ diff --git a/minix/commands/ash/mystring.h b/bin/sh/mystring.h similarity index 90% rename from minix/commands/ash/mystring.h rename to bin/sh/mystring.h index 9e9939cd0..08a73e9e5 100644 --- a/minix/commands/ash/mystring.h +++ b/bin/sh/mystring.h @@ -1,3 +1,5 @@ +/* $NetBSD: mystring.h,v 1.11 2003/08/07 09:05:35 agc Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,7 +32,6 @@ * SUCH DAMAGE. * * @(#)mystring.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/mystring.h,v 1.8 2004/04/06 20:06:51 markm Exp $ */ #include @@ -42,7 +43,3 @@ int is_number(const char *); #define equal(s1, s2) (strcmp(s1, s2) == 0) #define scopy(s1, s2) ((void)strcpy(s2, s1)) - -/* - * $PchId: mystring.h,v 1.3 2006/03/29 15:49:08 philip Exp $ - */ diff --git a/minix/commands/ash/nodes.c.pat b/bin/sh/nodes.c.pat similarity index 80% rename from minix/commands/ash/nodes.c.pat rename to bin/sh/nodes.c.pat index fb867053e..979e6b00e 100644 --- a/minix/commands/ash/nodes.c.pat +++ b/bin/sh/nodes.c.pat @@ -1,3 +1,5 @@ +/* $NetBSD: nodes.c.pat,v 1.13 2012/03/20 18:42:29 matt Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,7 +32,6 @@ * SUCH DAMAGE. * * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/nodes.c.pat,v 1.15 2004/04/06 20:06:51 markm Exp $ */ #include @@ -41,17 +42,14 @@ #include "shell.h" #include "nodes.h" #include "memalloc.h" +#include "machdep.h" #include "mystring.h" -#ifndef __minix -#include -#endif -#include "machdep.h" -STATIC int funcblocksize; /* size of structures in function */ -STATIC int funcstringsize; /* size of strings in node */ -STATIC pointer funcblock; /* block to allocate function from */ -STATIC char *funcstring; /* block to allocate strings from */ +int funcblocksize; /* size of structures in function */ +int funcstringsize; /* size of strings in node */ +pointer funcblock; /* block to allocate function from */ +char *funcstring; /* block to allocate strings from */ %SIZES @@ -77,7 +75,7 @@ copyfunc(union node *n) funcstringsize = 0; calcsize(n); funcblock = ckmalloc(funcblocksize + funcstringsize); - funcstring = (char *)funcblock + funcblocksize; + funcstring = (char *) funcblock + funcblocksize; return copynode(n); } @@ -95,7 +93,7 @@ STATIC void sizenodelist(struct nodelist *lp) { while (lp) { - funcblocksize += ALIGN(sizeof(struct nodelist)); + funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); calcsize(lp->n); lp = lp->next; } @@ -122,7 +120,8 @@ copynodelist(struct nodelist *lp) lpp = &start; while (lp) { *lpp = funcblock; - funcblock = (char *)funcblock + ALIGN(sizeof(struct nodelist)); + funcblock = (char *) funcblock + + SHELL_ALIGN(sizeof(struct nodelist)); (*lpp)->n = copynode(lp->n); lp = lp->next; lpp = &(*lpp)->next; @@ -136,11 +135,11 @@ copynodelist(struct nodelist *lp) STATIC char * nodesavestr(char *s) { - char *p = s; - char *q = funcstring; + register char *p = s; + register char *q = funcstring; char *rtn = funcstring; - while ((*q++ = *p++) != '\0') + while ((*q++ = *p++) != 0) continue; funcstring = q; return rtn; @@ -158,7 +157,3 @@ freefunc(union node *n) if (n) ckfree(n); } - -/* - * $PchId: nodes.c.pat,v 1.5 2006/05/22 12:43:57 philip Exp $ - */ diff --git a/minix/commands/ash/nodetypes b/bin/sh/nodetypes similarity index 94% rename from minix/commands/ash/nodetypes rename to bin/sh/nodetypes index 2c25c1d33..4bf4ae0b0 100644 --- a/minix/commands/ash/nodetypes +++ b/bin/sh/nodetypes @@ -1,4 +1,4 @@ -# +# $NetBSD: nodetypes,v 1.13 2009/05/26 07:30:51 joerg Exp $ # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # @@ -13,7 +13,7 @@ # 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. -# 4. Neither the name of the University nor the names of its contributors +# 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. # @@ -30,7 +30,6 @@ # SUCH DAMAGE. # # @(#)nodetypes 8.2 (Berkeley) 5/4/95 -# $FreeBSD: src/bin/sh/nodetypes,v 1.9 2004/04/06 20:06:51 markm Exp $ # This file describes the nodes used in parse trees. Unindented lines # contain a node type followed by a structure tag. Subsequent indented @@ -65,7 +64,7 @@ NPIPE npipe # a pipeline backgnd int # set to run pipeline in background cmdlist nodelist # the commands in the pipeline -NREDIR nredir # redirection (of a compex command) +NREDIR nredir # redirection (of a complex command) type int n nodeptr # the command redirect nodeptr # list of file redirections @@ -113,10 +112,10 @@ NARG narg # represents a word backquote nodelist # list of commands in back quotes NTO nfile # fd> fname +NCLOBBER nfile # fd>| fname NFROM nfile # fd< fname NFROMTO nfile # fd<> fname NAPPEND nfile # fd>> fname -NCLOBBER nfile # fd>| fname type int next nodeptr # next redirection in list fd int # file descriptor being redirected @@ -140,8 +139,5 @@ NXHERE nhere # fd< #ifndef lint #if 0 static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: options.c,v 1.43 2012/03/20 18:42:29 matt Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/options.c,v 1.21 2004/04/06 20:06:51 markm Exp $"); -*/ #include #include @@ -48,6 +49,7 @@ __FBSDID("$FreeBSD: src/bin/sh/options.c,v 1.21 2004/04/06 20:06:51 markm Exp $" #define DEFINE_OPTIONS #include "options.h" #undef DEFINE_OPTIONS +#include "builtins.h" #include "nodes.h" /* for other header files */ #include "eval.h" #include "jobs.h" @@ -58,17 +60,16 @@ __FBSDID("$FreeBSD: src/bin/sh/options.c,v 1.21 2004/04/06 20:06:51 markm Exp $" #include "memalloc.h" #include "error.h" #include "mystring.h" -#include "builtins.h" -#if !defined(NO_HISTORY) +#ifndef SMALL #include "myhistedit.h" #endif +#include "show.h" char *arg0; /* value of $0 */ struct shparam shellparam; /* current positional parameters */ char **argptr; /* argument list for builtin commands */ -char *shoptarg; /* set by nextopt (like getopt) */ +char *optionarg; /* set by nextopt (like getopt) */ char *optptr; /* used by nextopt */ -int editable; /* isatty(0) && isatty(1) */ char *minusc; /* argument to -c option */ @@ -86,33 +87,43 @@ STATIC int getopts(char *, char *, char **, char ***, char **); void procargs(int argc, char **argv) { - int i; + size_t i; argptr = argv; if (argc > 0) argptr++; for (i = 0; i < NOPTS; i++) optlist[i].val = 2; - privileged = (getuid() != geteuid() || getgid() != getegid()); options(1); if (*argptr == NULL && minusc == NULL) sflag = 1; - editable = (isatty(0) && isatty(1)); - if (iflag == 2 && sflag == 1 && editable) + if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) iflag = 1; + if (iflag == 1 && sflag == 2) + iflag = 2; if (mflag == 2) mflag = iflag; for (i = 0; i < NOPTS; i++) if (optlist[i].val == 2) optlist[i].val = 0; +#if DEBUG == 2 + debug = 1; +#endif arg0 = argv[0]; if (sflag == 0 && minusc == NULL) { - commandname = arg0 = *argptr++; - setinputfile(commandname, 0); + commandname = argv[0]; + arg0 = *argptr++; + setinputfile(arg0, 0); + commandname = arg0; } /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ - if (argptr && minusc && *argptr) - arg0 = *argptr++; + if (minusc != NULL) { + if (argptr == NULL || *argptr == NULL) + error("Bad -c option"); + minusc = *argptr++; + if (*argptr != 0) + arg0 = *argptr++; + } shellparam.p = argptr; shellparam.reset = 1; @@ -121,9 +132,6 @@ procargs(int argc, char **argv) shellparam.nparam++; argptr++; } -#ifdef __minix - if(!Eflag && !Vflag) Eflag = 1; -#endif optschanged(); } @@ -132,7 +140,7 @@ void optschanged(void) { setinteractive(iflag); -#if !defined(NO_HISTORY) +#ifndef SMALL histedit(); #endif setjobctl(mflag); @@ -146,6 +154,7 @@ optschanged(void) STATIC void options(int cmdline) { + static char empty[] = ""; char *p; int val; int c; @@ -175,65 +184,62 @@ options(int cmdline) } while ((c = *p++) != '\0') { if (c == 'c' && cmdline) { - char *q; -#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ - if (*p == '\0') -#endif - q = *argptr++; - if (q == NULL || minusc != NULL) - error("Bad -c option"); - minusc = q; -#ifdef NOHACK - break; -#endif + /* command is after shell args*/ + minusc = empty; } else if (c == 'o') { minus_o(*argptr, val); if (*argptr) argptr++; } else { - if (c == 'p' && !val && privileged) { - (void) setuid(getuid()); - (void) setgid(getgid()); - } setoption(c, val); } } } } +static void +set_opt_val(size_t i, int val) +{ + size_t j; + int flag; + + if (val && (flag = optlist[i].opt_set)) { + /* some options (eg vi/emacs) are mutually exclusive */ + for (j = 0; j < NOPTS; j++) + if (optlist[j].opt_set == flag) + optlist[j].val = 0; + } + optlist[i].val = val; +#ifdef DEBUG + if (&optlist[i].val == &debug) + opentrace(); +#endif +} + STATIC void minus_o(char *name, int val) { - int doneset, i; + size_t i; if (name == NULL) { if (val) { - /* "Pretty" output. */ out1str("Current option settings\n"); - for (i = 0; i < NOPTS; i++) + for (i = 0; i < NOPTS; i++) { out1fmt("%-16s%s\n", optlist[i].name, optlist[i].val ? "on" : "off"); + } } else { - /* Output suitable for re-input to shell. */ - for (doneset = i = 0; i < NOPTS; i++) - if (optlist[i].val) { - if (!doneset) { - out1str("set"); - doneset = 1; - } - out1fmt(" -o %s", optlist[i].name); - } - if (doneset) - out1c('\n'); + out1str("set"); + for (i = 0; i < NOPTS; i++) { + out1fmt(" %co %s", + "+-"[optlist[i].val], optlist[i].name); + } + out1str("\n"); } } else { for (i = 0; i < NOPTS; i++) if (equal(name, optlist[i].name)) { - if (!val && privileged && equal(name, "privileged")) { - (void) setuid(getuid()); - (void) setgid(getgid()); - } - setoption(optlist[i].letter, val); + set_opt_val(i, val); return; } error("Illegal option -o %s", name); @@ -244,21 +250,15 @@ minus_o(char *name, int val) STATIC void setoption(int flag, int val) { - int i; + size_t i; for (i = 0; i < NOPTS; i++) if (optlist[i].letter == flag) { - optlist[i].val = val; - if (val) { - /* #%$ hack for ksh semantics */ - if (flag == 'V') - Eflag = 0; - else if (flag == 'E') - Vflag = 0; - } + set_opt_val( i, val ); return; } error("Illegal option -%c", flag); + /* NOTREACHED */ } @@ -269,7 +269,7 @@ INCLUDE "options.h" SHELLPROC { int i; - for (i = 0; i < NOPTS; i++) + for (i = 0; optlist[i].name; i++) optlist[i].val = 0; optschanged(); @@ -288,7 +288,8 @@ setparam(char **argv) char **ap; int nparam; - for (nparam = 0 ; argv[nparam] ; nparam++); + for (nparam = 0 ; argv[nparam] ; nparam++) + continue; ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); while (*argv) { *ap++ = savestr(*argv++); @@ -307,7 +308,7 @@ setparam(char **argv) */ void -freeparam(struct shparam *param) +freeparam(volatile struct shparam *param) { char **ap; @@ -358,7 +359,7 @@ int setcmd(int argc, char **argv) { if (argc == 1) - return showvarscmd(argc, argv); + return showvars(0, 0, 1); INTOFF; options(0); optschanged(); @@ -389,7 +390,7 @@ getoptsreset(const char *value) int getoptscmd(int argc, char **argv) { - char **optbase = NULL; + char **optbase; if (argc < 3) error("usage: getopts optstring var [arg]"); @@ -409,17 +410,16 @@ getoptscmd(int argc, char **argv) } STATIC int -getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, - char **optptr) +getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr) { char *p, *q; char c = '?'; int done = 0; int ind = 0; int err = 0; - char s[10]; + char s[12]; - if ((p = *optptr) == NULL || *p == '\0') { + if ((p = *optpptr) == NULL || *p == '\0') { /* Current word is done, advance */ if (*optnext == NULL) return 1; @@ -444,10 +444,9 @@ atend: s[0] = c; s[1] = '\0'; err |= setvarsafe("OPTARG", s, 0); - } - else { - out1fmt("Illegal option -%c\n", c); - (void) unsetvar("OPTARG"); + } else { + outfmt(&errout, "Illegal option -%c\n", c); + (void) unsetvar("OPTARG", 0); } c = '?'; goto bad; @@ -463,10 +462,9 @@ atend: s[1] = '\0'; err |= setvarsafe("OPTARG", s, 0); c = ':'; - } - else { - out1fmt("No arg for -%c option\n", c); - (void) unsetvar("OPTARG"); + } else { + outfmt(&errout, "No arg for -%c option\n", c); + (void) unsetvar("OPTARG", 0); c = '?'; } goto bad; @@ -474,11 +472,10 @@ atend: if (p == **optnext) (*optnext)++; - setvarsafe("OPTARG", p, 0); + err |= setvarsafe("OPTARG", p, 0); p = NULL; - } - else - setvarsafe("OPTARG", "", 0); + } else + err |= setvarsafe("OPTARG", "", 0); ind = *optnext - optfirst + 1; goto out; @@ -487,7 +484,7 @@ bad: *optnext = NULL; p = NULL; out: - *optptr = p; + *optpptr = p; fmtstr(s, sizeof(s), "%d", ind); err |= setvarsafe("OPTIND", s, VNOFUNC); s[0] = c; @@ -495,7 +492,7 @@ out: err |= setvarsafe(optvar, s, 0); if (err) { *optnext = NULL; - *optptr = NULL; + *optpptr = NULL; flushall(); exraise(EXERROR); } @@ -514,9 +511,10 @@ out: */ int -nextopt(char *optstring) +nextopt(const char *optstring) { - char *p, *q; + char *p; + const char *q; char c; if ((p = optptr) == NULL || *p == '\0') { @@ -537,13 +535,9 @@ nextopt(char *optstring) if (*++q == ':') { if (*p == '\0' && (p = *argptr++) == NULL) error("No arg for -%c option", c); - shoptarg = p; + optionarg = p; p = NULL; } optptr = p; return c; } - -/* - * $PchId: options.c,v 1.6 2006/05/29 13:09:12 philip Exp $ - */ diff --git a/minix/commands/ash/options.h b/bin/sh/options.h similarity index 56% rename from minix/commands/ash/options.h rename to bin/sh/options.h index de1a55eba..0bf4a2a5d 100644 --- a/minix/commands/ash/options.h +++ b/bin/sh/options.h @@ -1,3 +1,5 @@ +/* $NetBSD: options.h,v 1.20 2011/06/18 21:18:46 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,7 +32,6 @@ * SUCH DAMAGE. * * @(#)options.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/options.h,v 1.13 2004/04/06 20:06:51 markm Exp $ */ struct shparam { @@ -43,57 +44,74 @@ struct shparam { }; - -#define eflag optlist[0].val -#define fflag optlist[1].val -#define Iflag optlist[2].val -#define iflag optlist[3].val -#define mflag optlist[4].val -#define nflag optlist[5].val -#define sflag optlist[6].val -#define xflag optlist[7].val -#define vflag optlist[8].val -#define Vflag optlist[9].val -#define Eflag optlist[10].val -#define Cflag optlist[11].val -#define aflag optlist[12].val -#define bflag optlist[13].val -#define uflag optlist[14].val -#define privileged optlist[15].val -#define Tflag optlist[16].val -#define Pflag optlist[17].val - -#define NOPTS 18 - struct optent { - const char *name; - const char letter; - char val; + const char *name; /* for set -o */ + const char letter; /* set [+/-] and $- */ + const char opt_set; /* mutually exclusive option set */ + unsigned char val; /* value of flag */ }; +/* Those marked [U] are required by posix, but have no effect! */ + #ifdef DEFINE_OPTIONS -struct optent optlist[NOPTS] = { - { "errexit", 'e', 0 }, - { "noglob", 'f', 0 }, - { "ignoreeof", 'I', 0 }, - { "interactive",'i', 0 }, - { "monitor", 'm', 0 }, - { "noexec", 'n', 0 }, - { "stdin", 's', 0 }, - { "xtrace", 'x', 0 }, - { "verbose", 'v', 0 }, - { "vi", 'V', 0 }, - { "emacs", 'E', 0 }, - { "noclobber", 'C', 0 }, - { "allexport", 'a', 0 }, - { "notify", 'b', 0 }, - { "nounset", 'u', 0 }, - { "privileged", 'p', 0 }, - { "trapsasync", 'T', 0 }, - { "physical", 'P', 0 }, -}; +#define DEF_OPTS(name, letter, opt_set) {name, letter, opt_set, 0}, +struct optent optlist[] = { #else -extern struct optent optlist[NOPTS]; +#define DEF_OPTS(name, letter, opt_set) +#endif +#define DEF_OPT(name,letter) DEF_OPTS(name, letter, 0) + +DEF_OPT( "errexit", 'e' ) /* exit on error */ +#define eflag optlist[0].val +DEF_OPT( "noglob", 'f' ) /* no pathname expansion */ +#define fflag optlist[1].val +DEF_OPT( "ignoreeof", 'I' ) /* do not exit on EOF */ +#define Iflag optlist[2].val +DEF_OPT( "interactive",'i' ) /* interactive shell */ +#define iflag optlist[3].val +DEF_OPT( "monitor", 'm' ) /* job control */ +#define mflag optlist[4].val +DEF_OPT( "noexec", 'n' ) /* [U] do not exec commands */ +#define nflag optlist[5].val +DEF_OPT( "stdin", 's' ) /* read from stdin */ +#define sflag optlist[6].val +DEF_OPT( "xtrace", 'x' ) /* trace after expansion */ +#define xflag optlist[7].val +DEF_OPT( "verbose", 'v' ) /* trace read input */ +#define vflag optlist[8].val +DEF_OPTS( "vi", 'V', 'V' ) /* vi style editing */ +#define Vflag optlist[9].val +DEF_OPTS( "emacs", 'E', 'V' ) /* emacs style editing */ +#define Eflag optlist[10].val +DEF_OPT( "noclobber", 'C' ) /* do not overwrite files with > */ +#define Cflag optlist[11].val +DEF_OPT( "allexport", 'a' ) /* export all variables */ +#define aflag optlist[12].val +DEF_OPT( "notify", 'b' ) /* [U] report completion of background jobs */ +#define bflag optlist[13].val +DEF_OPT( "nounset", 'u' ) /* error expansion of unset variables */ +#define uflag optlist[14].val +DEF_OPT( "quietprofile", 'q' ) +#define qflag optlist[15].val +DEF_OPT( "nolog", 0 ) /* [U] no functon defs in command history */ +#define nolog optlist[16].val +DEF_OPT( "cdprint", 0 ) /* always print result of cd */ +#define cdprint optlist[17].val +DEF_OPT( "tabcomplete", 0 ) /* causes filename expansion */ +#define tabcomplete optlist[18].val +#ifdef DEBUG +DEF_OPT( "debug", 0 ) /* enable debug prints */ +#define debug optlist[19].val +#endif + +#ifdef DEFINE_OPTIONS + { 0, 0, 0, 0 }, +}; +#define NOPTS (sizeof optlist / sizeof optlist[0] - 1) +int sizeof_optlist = sizeof optlist; +#else +extern struct optent optlist[]; +extern int sizeof_optlist; #endif @@ -101,20 +119,12 @@ extern char *minusc; /* argument to -c option */ extern char *arg0; /* $0 */ extern struct shparam shellparam; /* $@ */ extern char **argptr; /* argument list for builtin commands */ -extern char *shoptarg; /* set by nextopt */ +extern char *optionarg; /* set by nextopt */ extern char *optptr; /* used by nextopt */ -extern int editable; /* isatty(0) && isatty(1) */ void procargs(int, char **); void optschanged(void); void setparam(char **); -void freeparam(struct shparam *); -int shiftcmd(int, char **); -int setcmd(int, char **); -int getoptscmd(int, char **); -int nextopt(char *); +void freeparam(volatile struct shparam *); +int nextopt(const char *); void getoptsreset(const char *); - -/* - * $PchId: options.h,v 1.5 2006/05/29 13:08:45 philip Exp $ - */ diff --git a/minix/commands/ash/output.c b/bin/sh/output.c similarity index 76% rename from minix/commands/ash/output.c rename to bin/sh/output.c index 5af44ae84..cd33e8e0f 100644 --- a/minix/commands/ash/output.c +++ b/bin/sh/output.c @@ -1,3 +1,5 @@ +/* $NetBSD: output.c,v 1.33 2010/08/30 06:27:14 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,9 +32,12 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 -static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 5/31/93"; +static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: output.c,v 1.33 2010/08/30 06:27:14 christos Exp $"); #endif #endif /* not lint */ @@ -47,28 +52,26 @@ static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 5/31/93"; * Our output routines may be smaller than the stdio routines. */ +#include /* quad_t */ +#include /* BSD4_4 */ +#include + #include /* defines BUFSIZ */ +#include +#include +#include +#include + #include "shell.h" #include "syntax.h" #include "output.h" #include "memalloc.h" #include "error.h" -#include "var.h" -#ifdef __STDC__ -#include "stdarg.h" -#else -#include -#endif -#include -#include -#include -#include #define OUTBUFSIZ BUFSIZ #define BLOCK_OUT -2 /* output to a fixed block of memory */ #define MEM_OUT -3 /* output to dynamically allocated memory */ -#define OUTPUT_ERR 01 /* error occurred on output */ struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; @@ -102,11 +105,8 @@ RESET { */ void -open_mem(block, length, file) - char *block; - int length; - struct output *file; - { +open_mem(char *block, int length, struct output *file) +{ file->nextc = block; file->nleft = --length; file->fd = BLOCK_OUT; @@ -116,16 +116,9 @@ open_mem(block, length, file) void -out1str(p) - const char *p; - { - outstr(p, out1); -} - -void -out1qstr(const char *p) +out1str(const char *p) { - outqstr(p, out1); + outstr(p, out1); } @@ -137,43 +130,49 @@ out2str(const char *p) void -outstr(p, file) - register const char *p; - register struct output *file; - { +outstr(const char *p, struct output *file) +{ while (*p) outc(*p++, file); if (file == out2) flushout(file); } -/* Like outstr(), but quote for re-input into the shell. */ + void -outqstr(const char *p, struct output *file) +out2shstr(const char *p) { - char ch; + outshstr(p, out2); +} - if (p[strcspn(p, "|&;<>()$`\\\"'")] == '\0' && (!ifsset() || - p[strcspn(p, ifsval())] == '\0')) { - outstr(p, file); - return; - } - out1c('\''); - while ((ch = *p++) != '\0') { - switch (ch) { - case '\'': - /* - * Can't quote single quotes inside single quotes; - * close them, write escaped single quote, open again. - */ - outstr("'\\''", file); - break; - default: - outc(ch, file); +void +outshstr(const char *p, struct output *file) +{ + static const char norm_chars [] \ + = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/-="; + int need_q = p[0] == 0 || p[strspn(p, norm_chars)] != 0; + char c; + + if (need_q) + outc('\'', file); + + while (c = *p++, c != 0){ + if (c != '\''){ + outc(c, file); + }else{ + outc('\'', file); + outc('\\', file); + outc(c, file); + outc('\'', file); } } - out1c('\''); + + if (need_q) + outc('\'', file); + + if (file == out2) + flushout(file); } @@ -181,9 +180,8 @@ char out_junk[16]; void -emptyoutbuf(dest) - struct output *dest; - { +emptyoutbuf(struct output *dest) +{ int offset; if (dest->fd == BLOCK_OUT) { @@ -212,16 +210,16 @@ emptyoutbuf(dest) void -flushall() { +flushall(void) +{ flushout(&output); flushout(&errout); } void -flushout(dest) - struct output *dest; - { +flushout(struct output *dest) +{ if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) return; @@ -233,7 +231,8 @@ flushout(dest) void -freestdout() { +freestdout(void) +{ INTOFF; if (output.buf) { ckfree(output.buf); @@ -244,7 +243,6 @@ freestdout() { } -#ifdef __STDC__ void outfmt(struct output *file, const char *fmt, ...) { @@ -266,8 +264,9 @@ out1fmt(const char *fmt, ...) va_end(ap); } +#ifdef DEBUG void -dbgprintf(const char *fmt, ...) +debugprintf(const char *fmt, ...) { va_list ap; @@ -276,9 +275,10 @@ dbgprintf(const char *fmt, ...) va_end(ap); flushout(out2); } +#endif void -fmtstr(char *outbuf, int length, const char *fmt, ...) +fmtstr(char *outbuf, size_t length, const char *fmt, ...) { va_list ap; struct output strout; @@ -295,82 +295,11 @@ fmtstr(char *outbuf, int length, const char *fmt, ...) va_end(ap); } -#else /* not __STDC__ */ - -void -outfmt(va_alist) - va_dcl - { - va_list ap; - struct output *file; - char *fmt; - - va_start(ap); - file = va_arg(ap, struct output *); - fmt = va_arg(ap, char *); - doformat(file, fmt, ap); - va_end(ap); -} - - -void -out1fmt(va_alist) - va_dcl - { - va_list ap; - char *fmt; - - va_start(ap); - fmt = va_arg(ap, char *); - doformat(out1, fmt, ap); - va_end(ap); -} - -void -dbgprintf(va_alist) - va_dcl - { - va_list ap; - char *fmt; - - va_start(ap); - fmt = va_arg(ap, char *); - doformat(out2, fmt, ap); - va_end(ap); - flushout(out2); -} - -void -fmtstr(va_alist) - va_dcl - { - va_list ap; - struct output strout; - char *outbuf; - int length; - char *fmt; - - va_start(ap); - outbuf = va_arg(ap, char *); - length = va_arg(ap, int); - fmt = va_arg(ap, char *); - strout.nextc = outbuf; - strout.nleft = length; - strout.fd = BLOCK_OUT; - strout.flags = 0; - doformat(&strout, fmt, ap); - outc('\0', &strout); - if (strout.flags & OUTPUT_ERR) - outbuf[length - 1] = '\0'; -} -#endif /* __STDC__ */ - - /* * Formatted output. This routine handles a subset of the printf formats: - * - Formats supported: d, u, o, X, s, and c. + * - Formats supported: d, u, o, p, X, s, and c. * - The x format is also accepted but is treated like X. - * - The l modifier is accepted. + * - The l, ll and q modifiers are accepted. * - The - and # flags are accepted; # only works with the o format. * - Width and precision may be specified with any format except c. * - An * may be given for the width or precision. @@ -381,27 +310,40 @@ fmtstr(va_alist) #define TEMPSIZE 24 -#ifdef __STDC__ -static const char digit[16] = "0123456789ABCDEF"; -#else -static const char digit[17] = "0123456789ABCDEF"; +#ifdef BSD4_4 +#define HAVE_VASPRINTF 1 #endif - void doformat(struct output *dest, const char *f, va_list ap) { - register char c; +#if HAVE_VASPRINTF + char *s; + + vasprintf(&s, f, ap); + if (s == NULL) + error("Could not allocate formatted output buffer"); + outstr(s, dest); + free(s); +#else /* !HAVE_VASPRINTF */ + static const char digit[] = "0123456789ABCDEF"; + char c; char temp[TEMPSIZE]; int flushleft; int sharp; int width; int prec; int islong; + int isquad; char *p; int sign; +#ifdef BSD4_4 + quad_t l; + u_quad_t num; +#else long l; - unsigned long num; + u_long num; +#endif unsigned base; int len; int size; @@ -417,6 +359,7 @@ doformat(struct output *dest, const char *f, va_list ap) width = 0; prec = -1; islong = 0; + isquad = 0; for (;;) { if (*f == '-') flushleft++; @@ -446,11 +389,23 @@ doformat(struct output *dest, const char *f, va_list ap) } } if (*f == 'l') { - islong++; + f++; + if (*f == 'l') { + isquad++; + f++; + } else + islong++; + } else if (*f == 'q') { + isquad++; f++; } switch (*f) { case 'd': +#ifdef BSD4_4 + if (isquad) + l = va_arg(ap, quad_t); + else +#endif if (islong) l = va_arg(ap, long); else @@ -469,12 +424,21 @@ doformat(struct output *dest, const char *f, va_list ap) case 'o': base = 8; goto uns_number; + case 'p': + outc('0', dest); + outc('x', dest); + /*FALLTHROUGH*/ case 'x': /* we don't implement 'x'; treat like 'X' */ case 'X': base = 16; uns_number: /* an unsigned number */ sign = 0; +#ifdef BSD4_4 + if (isquad) + num = va_arg(ap, u_quad_t); + else +#endif if (islong) num = va_arg(ap, unsigned long); else @@ -542,6 +506,7 @@ number: /* process a number */ } f++; } +#endif /* !HAVE_VASPRINTF */ } @@ -551,11 +516,8 @@ number: /* process a number */ */ int -xwrite(fd, buf, nbytes) - int fd; - char *buf; - int nbytes; - { +xwrite(int fd, char *buf, int nbytes) +{ int ntry; int i; int n; @@ -578,6 +540,17 @@ xwrite(fd, buf, nbytes) } } + /* - * $PchId: output.c,v 1.6 2006/05/22 12:46:03 philip Exp $ + * Version of ioctl that retries after a signal is caught. + * XXX unused function */ + +int +xioctl(int fd, unsigned long request, char *arg) +{ + int i; + + while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); + return i; +} diff --git a/minix/commands/ash/output.h b/bin/sh/output.h similarity index 82% rename from minix/commands/ash/output.h rename to bin/sh/output.h index 59c36b3a5..618a09533 100644 --- a/minix/commands/ash/output.h +++ b/bin/sh/output.h @@ -1,3 +1,5 @@ +/* $NetBSD: output.h,v 1.24 2012/03/15 02:02:20 joerg Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,7 +32,6 @@ * SUCH DAMAGE. * * @(#)output.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/output.h,v 1.13 2004/04/06 20:06:51 markm Exp $ */ #ifndef OUTPUT_INCL @@ -46,41 +47,38 @@ struct output { short flags; }; +/* flags for ->flags */ +#define OUTPUT_ERR 01 /* error occurred on output */ + extern struct output output; extern struct output errout; extern struct output memout; extern struct output *out1; extern struct output *out2; -#ifndef __printflike -#define __printflike(a,b) -#endif - void open_mem(char *, int, struct output *); void out1str(const char *); -void out1qstr(const char *); void out2str(const char *); -void out2qstr(const char *); void outstr(const char *, struct output *); -void outqstr(const char *, struct output *); +void out2shstr(const char *); +void outshstr(const char *, struct output *); void emptyoutbuf(struct output *); void flushall(void); void flushout(struct output *); void freestdout(void); void outfmt(struct output *, const char *, ...) __printflike(2, 3); void out1fmt(const char *, ...) __printflike(1, 2); -void dbgprintf(const char *, ...) __printflike(1, 2); -void fmtstr(char *, int, const char *, ...) __printflike(3, 4); +#ifdef DEBUG +void debugprintf(const char *, ...) __printflike(1, 2); +#endif +void fmtstr(char *, size_t, const char *, ...) __printflike(3, 4); void doformat(struct output *, const char *, va_list) __printflike(2, 0); int xwrite(int, char *, int); +int xioctl(int, unsigned long, char *); #define outc(c, file) (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c))) -#define out1c(c) outc(c, out1); -#define out2c(c) outc(c, out2); +#define out1c(c) outc(c, out1) +#define out2c(c) outc(c, out2) #define OUTPUT_INCL #endif - -/* - * $PchId: output.h,v 1.5 2006/05/23 12:04:54 philip Exp $ - */ diff --git a/minix/commands/ash/parser.c b/bin/sh/parser.c similarity index 77% rename from minix/commands/ash/parser.c rename to bin/sh/parser.c index e06521400..2304e9304 100644 --- a/minix/commands/ash/parser.c +++ b/bin/sh/parser.c @@ -1,3 +1,5 @@ +/* $NetBSD: parser.c,v 1.85 2013/10/02 21:48:55 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,23 +32,25 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; +#else +__RCSID("$NetBSD: parser.c,v 1.85 2013/10/02 21:48:55 christos Exp $"); #endif #endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/parser.c,v 1.51.2.1 2005/03/03 03:43:20 obrien Exp $"); -*/ +#include #include -#include +#include #include "shell.h" #include "parser.h" #include "nodes.h" #include "expand.h" /* defines rmescapes() */ +#include "eval.h" /* defines commandname */ +#include "redir.h" /* defines copyfd() */ #include "syntax.h" #include "options.h" #include "input.h" @@ -57,8 +61,7 @@ __FBSDID("$FreeBSD: src/bin/sh/parser.c,v 1.51.2.1 2005/03/03 03:43:20 obrien Ex #include "mystring.h" #include "alias.h" #include "show.h" -#include "eval.h" -#if !defined(NO_HISTORY) +#ifndef SMALL #include "myhistedit.h" #endif @@ -66,12 +69,13 @@ __FBSDID("$FreeBSD: src/bin/sh/parser.c,v 1.51.2.1 2005/03/03 03:43:20 obrien Ex * Shell command parser. */ -#define EOFMARKLEN 79 -#define PROMPTLEN 128 +#define EOFMARKLEN 79 /* values returned by readtoken */ #include "token.h" +#define OPENBRACE '{' +#define CLOSEBRACE '}' struct heredoc { @@ -83,31 +87,24 @@ struct heredoc { -STATIC struct heredoc *heredoclist; /* list of here documents to read */ -STATIC int parsebackquote; /* nonzero if we are inside backquotes */ -STATIC int doprompt; /* if set, prompt the user */ -STATIC int needprompt; /* true if interactive and at start of line */ -STATIC int lasttoken; /* last token read */ +static int noalias = 0; /* when set, don't handle aliases */ +struct heredoc *heredoclist; /* list of here documents to read */ +int parsebackquote; /* nonzero if we are inside backquotes */ +int doprompt; /* if set, prompt the user */ +int needprompt; /* true if interactive and at start of line */ +int lasttoken; /* last token read */ MKINIT int tokpushback; /* last token pushed back */ -STATIC char *wordtext; /* text of last word returned by readtoken */ -MKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ -STATIC struct nodelist *backquotelist; -STATIC union node *redirnode; -STATIC struct heredoc *heredoc; -STATIC int quoteflag; /* set if (part of) last token was quoted */ -STATIC int startlinno; /* line # where last token started */ - -/* XXX When 'noaliases' is set to one, no alias expansion takes place. */ -static int noaliases = 0; - -#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */ -#ifdef GDB_HACK -static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'}; -static const char types[] = "}-+?="; -#endif +char *wordtext; /* text of last word returned by readtoken */ +MKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ +struct nodelist *backquotelist; +union node *redirnode; +struct heredoc *heredoc; +int quoteflag; /* set if (part of) last token was quoted */ +int startlinno; /* line # where last token started */ +int funclinno; /* line # where the current function started */ -STATIC union node *list(int); +STATIC union node *list(int, int); STATIC union node *andor(void); STATIC union node *pipeline(void); STATIC union node *command(void); @@ -120,8 +117,8 @@ STATIC int readtoken(void); STATIC int xxreadtoken(void); STATIC int readtoken1(int, char const *, char *, int); STATIC int noexpand(char *); -STATIC void synexpect(int); -STATIC void synerror(char *); +STATIC void synexpect(int) __dead; +STATIC void synerror(const char *) __dead; STATIC void setprompt(int); @@ -134,13 +131,11 @@ union node * parsecmd(int interact) { int t; - extern int exitstatus; tokpushback = 0; doprompt = interact; if (doprompt) - setprompt(exitstatus == 0 || (vpse.flags & VUNSET) - ? 1 : -1); + setprompt(1); else setprompt(0); needprompt = 0; @@ -150,15 +145,16 @@ parsecmd(int interact) if (t == TNL) return NULL; tokpushback++; - return list(1); + return list(1, 0); } STATIC union node * -list(int nlflag) +list(int nlflag, int erflag) { union node *n1, *n2, *n3; int tok; + TRACE(("list: entered\n")); checkkwd = 2; if (nlflag == 0 && tokendlist[peektoken()]) @@ -194,7 +190,7 @@ list(int nlflag) case TBACKGND: case TSEMI: tok = readtoken(); - /* FALLTHROUGH */ + /* fall through */ case TNL: if (tok == TNL) { parseheredoc(); @@ -214,7 +210,7 @@ list(int nlflag) pungetc(); /* push back EOF on input */ return n1; default: - if (nlflag) + if (nlflag || erflag) synexpect(-1); tokpushback++; return n1; @@ -230,6 +226,7 @@ andor(void) union node *n1, *n2, *n3; int t; + TRACE(("andor: entered\n")); n1 = pipeline(); for (;;) { if ((t = readtoken()) == TAND) { @@ -258,10 +255,14 @@ pipeline(void) struct nodelist *lp, *prev; int negate; - negate = 0; TRACE(("pipeline: entered\n")); - while (readtoken() == TNOT) + + negate = 0; + checkkwd = 2; + while (readtoken() == TNOT) { + TRACE(("pipeline: TNOT recognized\n")); negate = !negate; + } tokpushback++; n1 = command(); if (readtoken() == TPIPE) { @@ -282,6 +283,7 @@ pipeline(void) } tokpushback++; if (negate) { + TRACE(("negate pipeline\n")); n2 = (union node *)stalloc(sizeof (struct nnot)); n2->type = NNOT; n2->nnot.com = n1; @@ -301,6 +303,8 @@ command(void) union node *redir, **rpp; int t, negate = 0; + TRACE(("command: entered\n")); + checkkwd = 2; redir = NULL; n1 = NULL; @@ -324,24 +328,22 @@ command(void) case TIF: n1 = (union node *)stalloc(sizeof (struct nif)); n1->type = NIF; - if ((n1->nif.test = list(0)) == NULL) - synexpect(-1); + n1->nif.test = list(0, 0); if (readtoken() != TTHEN) synexpect(TTHEN); - n1->nif.ifpart = list(0); + n1->nif.ifpart = list(0, 0); n2 = n1; while (readtoken() == TELIF) { n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); n2 = n2->nif.elsepart; n2->type = NIF; - if ((n2->nif.test = list(0)) == NULL) - synexpect(-1); + n2->nif.test = list(0, 0); if (readtoken() != TTHEN) synexpect(TTHEN); - n2->nif.ifpart = list(0); + n2->nif.ifpart = list(0, 0); } if (lasttoken == TELSE) - n2->nif.elsepart = list(0); + n2->nif.elsepart = list(0, 0); else { n2->nif.elsepart = NULL; tokpushback++; @@ -355,13 +357,12 @@ command(void) int got; n1 = (union node *)stalloc(sizeof (struct nbinary)); n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; - if ((n1->nbinary.ch1 = list(0)) == NULL) - synexpect(-1); + n1->nbinary.ch1 = list(0, 0); if ((got=readtoken()) != TDO) { TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); synexpect(TDO); } - n1->nbinary.ch2 = list(0); + n1->nbinary.ch2 = list(0, 0); if (readtoken() != TDONE) synexpect(TDONE); checkkwd = 1; @@ -388,13 +389,11 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); if (lasttoken != TNL && lasttoken != TSEMI) synexpect(-1); } else { -#ifndef GDB_HACK - static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, + static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'}; -#endif n2 = (union node *)stalloc(sizeof (struct narg)); n2->type = NARG; - n2->narg.text = (char *)argvars; + n2->narg.text = argvars; n2->narg.backquote = NULL; n2->narg.next = NULL; n1->nfor.args = n2; @@ -412,7 +411,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); t = TEND; else synexpect(-1); - n1->nfor.body = list(0); + n1->nfor.body = list(0, 0); if (readtoken() != t) synexpect(t); checkkwd = 1; @@ -431,14 +430,14 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); if (lasttoken != TWORD || ! equal(wordtext, "in")) synerror("expecting \"in\""); cpp = &n1->ncase.cases; - noaliases = 1; /* turn off alias expansion */ + noalias = 1; checkkwd = 2, readtoken(); - while (lasttoken != TESAC) { + do { *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); - cp->type = NCLIST; - app = &cp->nclist.pattern; if (lasttoken == TLP) readtoken(); + cp->type = NCLIST; + app = &cp->nclist.pattern; for (;;) { *app = ap = (union node *)stalloc(sizeof (struct narg)); ap->type = NARG; @@ -450,48 +449,54 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); readtoken(); } ap->narg.next = NULL; - if (lasttoken != TRP) - noaliases = 0, synexpect(TRP); - cp->nclist.body = list(0); + noalias = 0; + if (lasttoken != TRP) { + synexpect(TRP); + } + cp->nclist.body = list(0, 0); checkkwd = 2; if ((t = readtoken()) != TESAC) { - if (t != TENDCASE) - noaliases = 0, synexpect(TENDCASE); - else - checkkwd = 2, readtoken(); + if (t != TENDCASE) { + noalias = 0; + synexpect(TENDCASE); + } else { + noalias = 1; + checkkwd = 2; + readtoken(); + } } cpp = &cp->nclist.next; - } - noaliases = 0; /* reset alias expansion */ + } while(lasttoken != TESAC); + noalias = 0; *cpp = NULL; checkkwd = 1; break; case TLP: n1 = (union node *)stalloc(sizeof (struct nredir)); n1->type = NSUBSHELL; - n1->nredir.n = list(0); + n1->nredir.n = list(0, 0); n1->nredir.redirect = NULL; if (readtoken() != TRP) synexpect(TRP); checkkwd = 1; break; case TBEGIN: - n1 = list(0); + n1 = list(0, 0); if (readtoken() != TEND) synexpect(TEND); checkkwd = 1; break; /* Handle an empty command like other simple commands. */ case TSEMI: - case TAND: - case TOR: /* * An empty command before a ; doesn't make much sense, and * should certainly be disallowed in the case of `if ;'. */ if (!redir) synexpect(-1); + case TAND: + case TOR: case TNL: case TEOF: case TWORD: @@ -501,6 +506,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); goto checkneg; default: synexpect(-1); + /* NOTREACHED */ } /* Now check for redirection which may follow command */ @@ -523,6 +529,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); checkneg: if (negate) { + TRACE(("negate command\n")); n2 = (union node *)stalloc(sizeof (struct nnot)); n2->type = NNOT; n2->nnot.com = n1; @@ -556,7 +563,7 @@ simplecmd(union node **rpp, union node *redir) orig_rpp = rpp; while (readtoken() == TNOT) { - TRACE(("command: TNOT recognized\n")); + TRACE(("simplcmd: TNOT recognized\n")); negate = !negate; } tokpushback++; @@ -578,12 +585,13 @@ simplecmd(union node **rpp, union node *redir) /* We have a function */ if (readtoken() != TRP) synexpect(TRP); -#ifdef notdef - if (! goodname(n->narg.text)) + funclinno = plinno; + rmescapes(n->narg.text); + if (!goodname(n->narg.text)) synerror("Bad function name"); -#endif n->type = NDEFUN; n->narg.next = command(); + funclinno = 0; goto checkneg; } else { tokpushback++; @@ -600,6 +608,7 @@ simplecmd(union node **rpp, union node *redir) checkneg: if (negate) { + TRACE(("negate simplecmd\n")); n2 = (union node *)stalloc(sizeof (struct nnot)); n2->type = NNOT; n2->nnot.com = n; @@ -623,7 +632,7 @@ makename(void) } void fixredir(union node *n, const char *text, int err) -{ + { TRACE(("Fix redir %s %d\n", text, err)); if (!err) n->ndup.vname = NULL; @@ -723,10 +732,10 @@ readtoken(void) { int t; int savecheckkwd = checkkwd; - struct alias *ap; -#if DEBUG +#ifdef DEBUG int alreadyseen = tokpushback; #endif + struct alias *ap; top: t = xxreadtoken(); @@ -748,17 +757,18 @@ readtoken(void) */ if (t == TWORD && !quoteflag) { - const char * const *pp; + const char *const *pp; for (pp = parsekwd; *pp; pp++) { if (**pp == *wordtext && equal(*pp, wordtext)) { - lasttoken = t = pp - parsekwd + KWDOFFSET; + lasttoken = t = pp - + parsekwd + KWDOFFSET; TRACE(("keyword %s recognized\n", tokname[t])); goto out; } } - if (noaliases == 0 && + if(!noalias && (ap = lookupalias(wordtext, 1)) != NULL) { pushstring(ap->val, strlen(ap->val), ap); checkkwd = savecheckkwd; @@ -768,12 +778,7 @@ readtoken(void) out: checkkwd = (t == TNOT) ? savecheckkwd : 0; } -#if DEBUG - if (!alreadyseen) - TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); - else - TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); -#endif + TRACE(("%stoken %s %s\n", alreadyseen ? "reread " : "", tokname[t], t == TWORD ? wordtext : "")); return (t); } @@ -814,8 +819,6 @@ xxreadtoken(void) startlinno = plinno; for (;;) { /* until token or start of word found */ c = pgetc_macro(); - if (c == ' ' || c == '\t') - continue; /* quick check for white space first */ switch (c) { case ' ': case '\t': continue; @@ -864,7 +867,7 @@ xxreadtoken(void) } } breakloop: - return readtoken1(c, BASESYNTAX, (char *)NULL, 0); + return readtoken1(c, BASESYNTAX, NULL, 0); #undef RETURN } @@ -889,55 +892,77 @@ breakloop: #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} #define PARSEARITH() {goto parsearith; parsearith_return:;} +/* + * Keep track of nested doublequotes in dblquote and doublequotep. + * We use dblquote for the first 32 levels, and we expand to a malloc'ed + * region for levels above that. Usually we never need to malloc. + * This code assumes that an int is 32 bits. We don't use uint32_t, + * because the rest of the code does not. + */ +#define ISDBLQUOTE() ((varnest < 32) ? (dblquote & (1 << varnest)) : \ + (dblquotep[(varnest / 32) - 1] & (1 << (varnest % 32)))) + +#define SETDBLQUOTE() \ + if (varnest < 32) \ + dblquote |= (1 << varnest); \ + else \ + dblquotep[(varnest / 32) - 1] |= (1 << (varnest % 32)) + +#define CLRDBLQUOTE() \ + if (varnest < 32) \ + dblquote &= ~(1 << varnest); \ + else \ + dblquotep[(varnest / 32) - 1] &= ~(1 << (varnest % 32)) + STATIC int -readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) +readtoken1(int firstc, char const *syn, char *eofmark, int striptabs) { + char const * volatile syntax = syn; int c = firstc; - char *out; + char * volatile out; int len; char line[EOFMARKLEN + 1]; struct nodelist *bqlist; - int quotef; - int dblquote; - int varnest; /* levels of variables expansion */ - int arinest; /* levels of arithmetic expansion */ - int parenlevel; /* levels of parens in arithmetic */ - int oldstyle; - char const *prevsyntax; /* syntax before arithmetic */ - int synentry; -#if __GNUC__ - /* Avoid longjmp clobbering */ - (void) &out; - (void) "ef; - (void) &dblquote; - (void) &varnest; - (void) &arinest; - (void) &parenlevel; - (void) &oldstyle; - (void) &prevsyntax; - (void) &syntax; - (void) &synentry; + volatile int quotef; + int * volatile dblquotep = NULL; + volatile size_t maxnest = 32; + volatile int dblquote; + volatile size_t varnest; /* levels of variables expansion */ + volatile int arinest; /* levels of arithmetic expansion */ + volatile int parenlevel; /* levels of parens in arithmetic */ + volatile int oldstyle; + char const * volatile prevsyntax; /* syntax before arithmetic */ +#ifdef __GNUC__ + prevsyntax = NULL; /* XXX gcc4 */ #endif startlinno = plinno; dblquote = 0; - if (syntax == DQSYNTAX) - dblquote = 1; + varnest = 0; + if (syntax == DQSYNTAX) { + SETDBLQUOTE(); + } quotef = 0; bqlist = NULL; - varnest = 0; arinest = 0; parenlevel = 0; STARTSTACKSTR(out); loop: { /* for each line, until end of word */ +#if ATTY + if (c == '\034' && doprompt + && attyset() && ! equal(termval(), "emacs")) { + attyline(); + if (syntax == BASESYNTAX) + return readtoken(); + c = pgetc(); + goto loop; + } +#endif CHECKEND(); /* set c to PEOF if at end of here document */ for (;;) { /* until end of line or end of word */ - CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ - - synentry = syntax[c]; - - switch(synentry) { + CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ + switch(syntax[c]) { case CNL: /* '\n' */ if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ @@ -953,7 +978,7 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) USTPUTC(c, out); break; case CCTL: - if (eofmark == NULL || dblquote) + if (eofmark == NULL || ISDBLQUOTE()) USTPUTC(CTLESC, out); USTPUTC(c, out); break; @@ -962,55 +987,91 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) if (c == PEOF) { USTPUTC('\\', out); pungetc(); - } else if (c == '\n') { + break; + } + if (c == '\n') { if (doprompt) setprompt(2); else setprompt(0); - } else { - if (dblquote && c != '\\' && - c != '`' && c != '$' && - (c != '"' || eofmark != NULL)) - USTPUTC('\\', out); - if (SQSYNTAX[c] == CCTL) - USTPUTC(CTLESC, out); - else if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - USTPUTC(c, out); - quotef++; + break; } + quotef = 1; + if (ISDBLQUOTE() && c != '\\' && + c != '`' && c != '$' && + (c != '"' || eofmark != NULL)) + USTPUTC('\\', out); + if (SQSYNTAX[c] == CCTL) + USTPUTC(CTLESC, out); + else if (eofmark == NULL) { + USTPUTC(CTLQUOTEMARK, out); + USTPUTC(c, out); + if (varnest != 0) + USTPUTC(CTLQUOTEEND, out); + break; + } + USTPUTC(c, out); break; case CSQUOTE: - if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - syntax = SQSYNTAX; - break; - case CDQUOTE: - if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - syntax = DQSYNTAX; - dblquote = 1; - break; - case CENDQUOTE: + if (syntax != SQSYNTAX) { + if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + quotef = 1; + syntax = SQSYNTAX; + break; + } if (eofmark != NULL && arinest == 0 && varnest == 0) { + /* Ignore inside quoted here document */ USTPUTC(c, out); - } else { - if (arinest) { + break; + } + /* End of single quotes... */ + if (arinest) + syntax = ARISYNTAX; + else { + syntax = BASESYNTAX; + if (varnest != 0) + USTPUTC(CTLQUOTEEND, out); + } + break; + case CDQUOTE: + if (eofmark != NULL && arinest == 0 && + varnest == 0) { + /* Ignore inside here document */ + USTPUTC(c, out); + break; + } + quotef = 1; + if (arinest) { + if (ISDBLQUOTE()) { syntax = ARISYNTAX; - dblquote = 0; - } else if (eofmark == NULL) { - syntax = BASESYNTAX; - dblquote = 0; + CLRDBLQUOTE(); + } else { + syntax = DQSYNTAX; + SETDBLQUOTE(); + USTPUTC(CTLQUOTEMARK, out); } - quotef++; + break; + } + if (eofmark != NULL) + break; + if (ISDBLQUOTE()) { + if (varnest != 0) + USTPUTC(CTLQUOTEEND, out); + syntax = BASESYNTAX; + CLRDBLQUOTE(); + } else { + syntax = DQSYNTAX; + SETDBLQUOTE(); + USTPUTC(CTLQUOTEMARK, out); } break; case CVAR: /* '$' */ PARSESUB(); /* parse substitution */ break; - case CENDVAR: /* '}' */ - if (varnest > 0) { + case CENDVAR: /* CLOSEBRACE */ + if (varnest > 0 && !ISDBLQUOTE()) { varnest--; USTPUTC(CTLENDVAR, out); } else { @@ -1031,9 +1092,9 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) USTPUTC(CTLENDARI, out); syntax = prevsyntax; if (syntax == DQSYNTAX) - dblquote = 1; + SETDBLQUOTE(); else - dblquote = 0; + CLRDBLQUOTE(); } else USTPUTC(')', out); } else { @@ -1052,7 +1113,7 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) case CEOF: goto endword; /* exit outer loop */ default: - if (varnest == 0) + if (varnest == 0 && !ISDBLQUOTE()) goto endword; /* exit outer loop */ USTPUTC(c, out); } @@ -1062,10 +1123,11 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) endword: if (syntax == ARISYNTAX) synerror("Missing '))'"); - if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) + if (syntax != BASESYNTAX && /* ! parsebackquote && */ eofmark == NULL) synerror("Unterminated quoted string"); if (varnest != 0) { startlinno = plinno; + /* { */ synerror("Missing '}'"); } USTPUTC('\0', out); @@ -1086,6 +1148,8 @@ endword: backquotelist = bqlist; grabstackblock(len); wordtext = out; + if (dblquotep != NULL) + ckfree(dblquotep); return lasttoken = TWORD; /* end of readtoken routine */ @@ -1109,7 +1173,7 @@ checkend: { p = line; for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); - if (*p == '\n' && *q == '\0') { + if ((*p == '\0' || *p == '\n') && *q == '\0') { c = PEOF; plinno++; needprompt = doprompt; @@ -1139,18 +1203,18 @@ parseredir: { c = pgetc(); if (c == '>') np->type = NAPPEND; - else if (c == '&') - np->type = NTOFD; else if (c == '|') np->type = NCLOBBER; + else if (c == '&') + np->type = NTOFD; else { np->type = NTO; pungetc(); } } else { /* c == '<' */ np->nfile.fd = 0; - c = pgetc(); - if (c == '<') { + switch (c = pgetc()) { + case '<': if (sizeof (struct nfile) != sizeof (struct nhere)) { np = (union node *)stalloc(sizeof (struct nhere)); np->nfile.fd = 0; @@ -1164,13 +1228,20 @@ parseredir: { heredoc->striptabs = 0; pungetc(); } - } else if (c == '&') + break; + + case '&': np->type = NFROMFD; - else if (c == '>') + break; + + case '>': np->type = NFROMTO; - else { + break; + + default: np->type = NFROM; pungetc(); + break; } } if (fd != '\0') @@ -1186,17 +1257,17 @@ parseredir: { */ parsesub: { + char buf[10]; int subtype; int typeloc; int flags; char *p; -#ifndef GDB_HACK static const char types[] = "}-+?="; -#endif - int bracketed_name = 0; /* used to handle ${[0-9]*} variables */ + int i; + int linno; c = pgetc(); - if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { + if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) { USTPUTC('$', out); pungetc(); } else if (c == '(') { /* $(command) or $((arith)) */ @@ -1211,11 +1282,11 @@ parsesub: { typeloc = out - stackblock(); USTPUTC(VSNORMAL, out); subtype = VSNORMAL; - if (c == '{') { - bracketed_name = 1; + flags = 0; + if (c == OPENBRACE) { c = pgetc(); if (c == '#') { - if ((c = pgetc()) == '}') + if ((c = pgetc()) == CLOSEBRACE) c = '#'; else subtype = VSLENGTH; @@ -1224,32 +1295,41 @@ parsesub: { subtype = 0; } if (is_name(c)) { + p = out; do { STPUTC(c, out); c = pgetc(); } while (is_in_name(c)); - } else if (is_digit(c)) { - if (bracketed_name) { - do { - STPUTC(c, out); - c = pgetc(); - } while (is_digit(c)); - } else { - STPUTC(c, out); - c = pgetc(); + if (out - p == 6 && strncmp(p, "LINENO", 6) == 0) { + /* Replace the variable name with the + * current line number. */ + linno = plinno; + if (funclinno != 0) + linno -= funclinno - 1; + snprintf(buf, sizeof(buf), "%d", linno); + STADJUST(-6, out); + for (i = 0; buf[i] != '\0'; i++) + STPUTC(buf[i], out); + flags |= VSLINENO; } - } else { - if (! is_special(c)) -badsub: synerror("Bad substitution"); + } else if (is_digit(c)) { + do { + USTPUTC(c, out); + c = pgetc(); + } while (is_digit(c)); + } + else if (is_special(c)) { USTPUTC(c, out); c = pgetc(); } + else +badsub: synerror("Bad substitution"); + STPUTC('=', out); - flags = 0; if (subtype == 0) { switch (c) { case ':': - flags = VSNUL; + flags |= VSNUL; c = pgetc(); /*FALLTHROUGH*/ default: @@ -1275,11 +1355,17 @@ badsub: synerror("Bad substitution"); } else { pungetc(); } - if (subtype != VSLENGTH && (dblquote || arinest)) + if (ISDBLQUOTE() || arinest) flags |= VSQUOTE; *(stackblock() + typeloc) = subtype | flags; - if (subtype != VSNORMAL) + if (subtype != VSNORMAL) { varnest++; + if (varnest >= maxnest) { + dblquotep = ckrealloc(dblquotep, maxnest / 8); + dblquotep[(maxnest / 32) - 1] = 0; + maxnest += 32; + } + } } goto parsesub_return; } @@ -1296,15 +1382,11 @@ parsebackq: { struct nodelist **nlpp; int savepbq; union node *n; - char *volatile str; + char *volatile str = NULL; struct jmploc jmploc; - struct jmploc *volatile savehandler; + struct jmploc *volatile savehandler = NULL; int savelen; int saveprompt; -#if __GNUC__ - /* Avoid longjmp clobbering */ - (void) &saveprompt; -#endif savepbq = parsebackquote; if (setjmp(jmploc.loc)) { @@ -1328,24 +1410,24 @@ parsebackq: { /* We must read until the closing backquote, giving special treatment to some slashes, and then push the string and reread it as input, interpreting it normally. */ - char *out; - int c; - int savelen; - char *str; + char *pout; + int pc; + int psavelen; + char *pstr; - STARTSTACKSTR(out); + STARTSTACKSTR(pout); for (;;) { if (needprompt) { setprompt(2); needprompt = 0; } - switch (c = pgetc()) { + switch (pc = pgetc()) { case '`': goto done; case '\\': - if ((c = pgetc()) == '\n') { + if ((pc = pgetc()) == '\n') { plinno++; if (doprompt) setprompt(2); @@ -1359,9 +1441,9 @@ parsebackq: { */ continue; } - if (c != '\\' && c != '`' && c != '$' - && (!dblquote || c != '"')) - STPUTC('\\', out); + if (pc != '\\' && pc != '`' && pc != '$' + && (!ISDBLQUOTE() || pc != '"')) + STPUTC('\\', pout); break; case '\n': @@ -1377,15 +1459,14 @@ parsebackq: { default: break; } - STPUTC(c, out); + STPUTC(pc, pout); } done: - STPUTC('\0', out); - savelen = out - stackblock(); - if (savelen > 0) { - str = ckmalloc(savelen); - memcpy(str, stackblock(), savelen); - setinputstring(str, 1); + STPUTC('\0', pout); + psavelen = pout - stackblock(); + if (psavelen > 0) { + pstr = grabstackstr(pout); + setinputstring(pstr, 1); } } nlpp = &bqlist; @@ -1398,9 +1479,10 @@ done: if (oldstyle) { saveprompt = doprompt; doprompt = 0; - } + } else + saveprompt = 0; - n = list(0); + n = list(0, oldstyle); if (oldstyle) doprompt = saveprompt; @@ -1431,7 +1513,7 @@ done: } parsebackquote = savepbq; handler = savehandler; - if (arinest || dblquote) + if (arinest || ISDBLQUOTE()) USTPUTC(CTLBACKQ | CTLQUOTE, out); else USTPUTC(CTLBACKQ, out); @@ -1450,7 +1532,7 @@ parsearith: { prevsyntax = syntax; syntax = ARISYNTAX; USTPUTC(CTLARI, out); - if (dblquote) + if (ISDBLQUOTE()) USTPUTC('"',out); else USTPUTC(' ',out); @@ -1488,7 +1570,7 @@ noexpand(char *text) p = text; while ((c = *p++) != '\0') { - if ( c == CTLQUOTEMARK) + if (c == CTLQUOTEMARK) continue; if (c == CTLESC) p++; @@ -1506,7 +1588,7 @@ noexpand(char *text) int goodname(char *name) -{ + { char *p; p = name; @@ -1538,16 +1620,20 @@ synexpect(int token) fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); } synerror(msg); + /* NOTREACHED */ } STATIC void -synerror(char *msg) +synerror(const char *msg) { if (commandname) outfmt(&errout, "%s: %d: ", commandname, startlinno); + else + outfmt(&errout, "%s: ", getprogname()); outfmt(&errout, "Syntax error: %s\n", msg); - error((char *)NULL); + error(NULL); + /* NOTREACHED */ } STATIC void @@ -1555,9 +1641,9 @@ setprompt(int which) { whichprompt = which; -#ifndef NO_HISTORY +#ifndef SMALL if (!el) -#endif /* !NO_HISTORY */ +#endif out2str(getprompt(NULL)); } @@ -1565,110 +1651,17 @@ setprompt(int which) * called by editline -- any expansions to the prompt * should be added here. */ -char * -getprompt(void *unused __unused) -{ - static char ps[PROMPTLEN]; - char *fmt; - int i, j, trim; - - /* - * Select prompt format. - */ +const char * +getprompt(void *unused) + { switch (whichprompt) { - case -1: - fmt = pseval(); - break; case 0: - fmt = ""; - break; + return ""; case 1: - fmt = ps1val(); - break; + return ps1val(); case 2: - fmt = ps2val(); - break; + return ps2val(); default: return ""; } - - /* - * Format prompt string. - */ - for (i = 0; (i < 127) && (*fmt != '\0'); i++, fmt++) - if (*fmt == '\\') - switch (*++fmt) { - - /* - * Hostname. - * - * \h specifies just the local hostname, - * \H specifies fully-qualified hostname. - */ - case 'h': - case 'H': - ps[i] == '\0'; - gethostname(&ps[i], PROMPTLEN - i); - /* Skip to end of hostname. */ - trim = (*fmt == 'h') ? '.' : '\0'; - while ((ps[i+1] != '\0') && (ps[i+1] != trim)) - i++; - break; - - /* - * Working directory. - * - * \W specifies just the final component, - * \w specifies the entire path. - */ - case 'W': - case 'w': - ps[i] == '\0'; - getcwd(&ps[i], PROMPTLEN - i); - if (*fmt == 'W') { - /* Final path component only. */ - trim = 1; - for (j = i; ps[j] != '\0'; j++) - if (ps[j] == '/') - trim = j + 1; - memmove(&ps[i], &ps[trim], - j - trim + 1); - } - /* Skip to end of path. */ - while (ps[i + 1] != '\0') - i++; - break; - - /* - * Superuser status. - * - * '$' for normal users, '#' for root. - */ - case '$': - ps[i] = (geteuid() != 0) ? '$' : '#'; - break; - - /* - * A literal \. - */ - case '\\': - ps[i] = '\\'; - break; - - /* - * Emit unrecognized formats verbatim. - */ - default: - ps[i++] = '\\'; - ps[i] = *fmt; - break; - } - else - ps[i] = *fmt; - ps[i] = '\0'; - return (ps); } - -/* - * $PchId: parser.c,v 1.6 2006/05/29 13:08:11 philip Exp $ - */ diff --git a/minix/commands/ash/parser.h b/bin/sh/parser.h similarity index 78% rename from minix/commands/ash/parser.h rename to bin/sh/parser.h index 522902a4b..cc4910f84 100644 --- a/minix/commands/ash/parser.h +++ b/bin/sh/parser.h @@ -1,3 +1,5 @@ +/* $NetBSD: parser.h,v 1.18 2013/10/02 19:52:58 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,24 +32,28 @@ * SUCH DAMAGE. * * @(#)parser.h 8.3 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/parser.h,v 1.10 2004/04/06 20:06:51 markm Exp $ */ /* control characters in argument strings */ -#define CTLESC '\201' -#define CTLVAR '\202' +#define CTL_FIRST '\201' /* first 'special' character */ +#define CTLESC '\201' /* escape next character */ +#define CTLVAR '\202' /* variable defn */ #define CTLENDVAR '\203' #define CTLBACKQ '\204' #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ /* CTLBACKQ | CTLQUOTE == '\205' */ -#define CTLARI '\206' +#define CTLARI '\206' /* arithmetic expression */ #define CTLENDARI '\207' #define CTLQUOTEMARK '\210' +#define CTLQUOTEEND '\211' /* only inside ${...} */ +#define CTL_LAST '\211' /* last 'special' character */ /* variable substitution byte (follows CTLVAR) */ -#define VSTYPE 0x0f /* type of variable substitution */ -#define VSNUL 0x10 /* colon--treat the empty string as unset */ -#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ +#define VSTYPE 0x0f /* type of variable substitution */ +#define VSNUL 0x10 /* colon--treat the empty string as unset */ +#define VSLINENO 0x20 /* expansion of $LINENO, the line number + follows immediately */ +#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ /* values of VSTYPE field */ #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ @@ -75,8 +81,4 @@ extern int whichprompt; /* 1 == PS1, 2 == PS2 */ union node *parsecmd(int); void fixredir(union node *, const char *, int); int goodname(char *); -char *getprompt(void *); - -/* - * $PchId: parser.h,v 1.3 2006/03/29 14:33:35 philip Exp $ - */ +const char *getprompt(void *); diff --git a/minix/commands/ash/redir.c b/bin/sh/redir.c similarity index 71% rename from minix/commands/ash/redir.c rename to bin/sh/redir.c index 9fa35de48..a2ab37983 100644 --- a/minix/commands/ash/redir.c +++ b/bin/sh/redir.c @@ -1,3 +1,5 @@ +/* $NetBSD: redir.c,v 1.35 2013/06/27 23:22:04 yamt Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,18 +32,17 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: redir.c,v 1.35 2013/06/27 23:22:04 yamt Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/redir.c,v 1.26 2004/04/06 20:06:51 markm Exp $"); -*/ #include -#include +#include /* PIPE_BUF */ #include #include #include @@ -53,25 +54,31 @@ __FBSDID("$FreeBSD: src/bin/sh/redir.c,v 1.26 2004/04/06 20:06:51 markm Exp $"); * Code for dealing with input/output redirection. */ +#include "main.h" #include "shell.h" #include "nodes.h" #include "jobs.h" +#include "options.h" #include "expand.h" #include "redir.h" #include "output.h" #include "memalloc.h" #include "error.h" -#include "options.h" #define EMPTY -2 /* marks an unused slot in redirtab */ -#define PIPESIZE 4096 /* amount of buffering in a pipe */ +#define CLOSED -1 /* fd was not open before redir */ +#ifndef PIPE_BUF +# define PIPESIZE 4096 /* amount of buffering in a pipe */ +#else +# define PIPESIZE PIPE_BUF +#endif MKINIT struct redirtab { struct redirtab *next; - int renamed[10]; + short renamed[10]; }; @@ -82,10 +89,10 @@ MKINIT struct redirtab *redirlist; * background commands, where we want to redirect fd0 to /dev/null only * if it hasn't already been redirected. */ -STATIC int fd0_redirected = 0; +int fd0_redirected = 0; -STATIC void openredirect(union node *, char[10 ]); -STATIC int openhere(union node *); +STATIC void openredirect(union node *, char[10], int); +STATIC int openhere(const union node *); /* @@ -103,13 +110,15 @@ redirect(union node *redir, int flags) struct redirtab *sv = NULL; int i; int fd; - int try; char memory[10]; /* file descriptors to write to memory */ for (i = 10 ; --i >= 0 ; ) memory[i] = 0; memory[1] = flags & REDIR_BACKQ; if (flags & REDIR_PUSH) { + /* We don't have to worry about REDIR_VFORK here, as + * flags & REDIR_PUSH is never true if REDIR_VFORK is set. + */ sv = ckmalloc(sizeof (struct redirtab)); for (i = 0 ; i < 10 ; i++) sv->renamed[i] = EMPTY; @@ -118,38 +127,32 @@ redirect(union node *redir, int flags) } for (n = redir ; n ; n = n->nfile.next) { fd = n->nfile.fd; - try = 0; if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && n->ndup.dupfd == fd) continue; /* redirect from/to same file descriptor */ if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { INTOFF; -again: if ((i = fcntl(fd, F_DUPFD, 10)) == -1) { switch (errno) { case EBADF: - if (!try) { - openredirect(n, memory); - try++; - goto again; - } - /* FALLTHROUGH*/ + i = CLOSED; + break; default: INTON; error("%d: %s", fd, strerror(errno)); - break; + /* NOTREACHED */ } - } - if (!try) { - sv->renamed[fd] = i; - } + } else + (void)fcntl(i, F_SETFD, FD_CLOEXEC); + sv->renamed[fd] = i; INTON; + } else { + close(fd); } - if (fd == 0) - fd0_redirected++; - if (!try) - openredirect(n, memory); + if (fd == 0) + fd0_redirected++; + openredirect(n, memory, flags); } if (memory[1]) out1 = &memout; @@ -159,15 +162,12 @@ again: STATIC void -openredirect(union node *redir, char memory[10]) +openredirect(union node *redir, char memory[10], int flags) { - struct stat sb; int fd = redir->nfile.fd; char *fname; int f; - - /* Assume redirection succeeds. */ - { extern int exitstatus; exitstatus = 0; } + int oflags = O_WRONLY|O_CREAT|O_TRUNC, eflags; /* * We suppress interrupts so that we won't leave open file @@ -179,56 +179,64 @@ openredirect(union node *redir, char memory[10]) switch (redir->nfile.type) { case NFROM: fname = redir->nfile.expfname; - if ((f = open(fname, O_RDONLY)) < 0) - error("cannot open %s: %s", fname, strerror(errno)); -movefd: - if (f != fd) { - dup2(f, fd); - close(f); - } + if (flags & REDIR_VFORK) + eflags = O_NONBLOCK; + else + eflags = 0; + if ((f = open(fname, O_RDONLY|eflags)) < 0) + goto eopen; + if (eflags) + (void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags); break; case NFROMTO: fname = redir->nfile.expfname; - if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0) - error("cannot create %s: %s", fname, strerror(errno)); - goto movefd; + if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) + goto ecreate; + break; case NTO: - fname = redir->nfile.expfname; - if (Cflag && stat(fname, &sb) != -1 && S_ISREG(sb.st_mode)) - error("cannot create %s: %s", fname, - strerror(EEXIST)); - if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) - error("cannot create %s: %s", fname, strerror(errno)); - goto movefd; + if (Cflag) + oflags |= O_EXCL; + /* FALLTHROUGH */ case NCLOBBER: fname = redir->nfile.expfname; - if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) - error("cannot create %s: %s", fname, strerror(errno)); - goto movefd; + if ((f = open(fname, oflags, 0666)) < 0) + goto ecreate; + break; case NAPPEND: fname = redir->nfile.expfname; if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) - error("cannot create %s: %s", fname, strerror(errno)); - goto movefd; + goto ecreate; + break; case NTOFD: case NFROMFD: if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ if (memory[redir->ndup.dupfd]) memory[fd] = 1; else - dup2(redir->ndup.dupfd, fd); - } else { - close(fd); + copyfd(redir->ndup.dupfd, fd, 1); } - break; + INTON; + return; case NHERE: case NXHERE: f = openhere(redir); - goto movefd; + break; default: abort(); } + + if (f != fd) { + copyfd(f, fd, 1); + close(f); + } INTON; + return; +ecreate: + exerrno = 1; + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +eopen: + exerrno = 1; + error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); } @@ -239,13 +247,13 @@ movefd: */ STATIC int -openhere(union node *redir) +openhere(const union node *redir) { int pip[2]; int len = 0; if (pipe(pip) < 0) - error("Pipe call failed: %s", strerror(errno)); + error("Pipe call failed"); if (redir->type == NHERE) { len = strlen(redir->nhere.doc->narg.text); if (len <= PIPESIZE) { @@ -253,12 +261,14 @@ openhere(union node *redir) goto out; } } - if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + if (forkshell(NULL, NULL, FORK_NOJOB) == 0) { close(pip[0]); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); +#endif signal(SIGPIPE, SIG_DFL); if (redir->type == NHERE) xwrite(pip[1], redir->nhere.doc->narg.text, len); @@ -287,11 +297,10 @@ popredir(void) if (rp->renamed[i] != EMPTY) { if (i == 0) fd0_redirected--; + close(i); if (rp->renamed[i] >= 0) { - dup2(rp->renamed[i], i); + copyfd(rp->renamed[i], i, 1); close(rp->renamed[i]); - } else { - close(i); } } } @@ -315,15 +324,14 @@ RESET { } SHELLPROC { - clearredir(); + clearredir(0); } #endif /* Return true if fd 0 has already been redirected at least once. */ int -fd0_redirected_p(void) -{ +fd0_redirected_p (void) { return fd0_redirected != 0; } @@ -332,7 +340,7 @@ fd0_redirected_p(void) */ void -clearredir(void) +clearredir(int vforked) { struct redirtab *rp; int i; @@ -342,11 +350,34 @@ clearredir(void) if (rp->renamed[i] >= 0) { close(rp->renamed[i]); } - rp->renamed[i] = EMPTY; + if (!vforked) + rp->renamed[i] = EMPTY; } } } + + /* - * $PchId: redir.c,v 1.5 2006/05/22 12:27:37 philip Exp $ + * Copy a file descriptor to be >= to. Returns -1 + * if the source file descriptor is closed, EMPTY if there are no unused + * file descriptors left. */ + +int +copyfd(int from, int to, int equal) +{ + int newfd; + + if (equal) + newfd = dup2(from, to); + else + newfd = fcntl(from, F_DUPFD, to); + if (newfd < 0) { + if (errno == EMFILE) + return EMPTY; + else + error("%d: %s", from, strerror(errno)); + } + return newfd; +} diff --git a/minix/commands/ash/redir.h b/bin/sh/redir.h similarity index 88% rename from minix/commands/ash/redir.h rename to bin/sh/redir.h index 9b665d06f..923619fbd 100644 --- a/minix/commands/ash/redir.h +++ b/bin/sh/redir.h @@ -1,3 +1,5 @@ +/* $NetBSD: redir.h,v 1.16 2011/02/17 15:13:49 pooka Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,20 +32,17 @@ * SUCH DAMAGE. * * @(#)redir.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/redir.h,v 1.10 2004/04/06 20:06:51 markm Exp $ */ /* flags passed to redirect */ #define REDIR_PUSH 01 /* save previous values of file descriptors */ #define REDIR_BACKQ 02 /* save the command output in memory */ +#define REDIR_VFORK 04 /* running under vfork(2), be careful */ union node; void redirect(union node *, int); void popredir(void); int fd0_redirected_p(void); -void clearredir(void); +void clearredir(int); +int copyfd(int, int, int); - -/* - * $PchId: redir.h,v 1.3 2006/03/29 14:13:34 philip Exp $ - */ diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 new file mode 100644 index 000000000..05e8d74bf --- /dev/null +++ b/bin/sh/sh.1 @@ -0,0 +1,2002 @@ +.\" $NetBSD: sh.1,v 1.111 2013/10/02 20:42:56 christos Exp $ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sh.1 8.6 (Berkeley) 5/4/95 +.\" +.Dd October 2, 2013 +.Dt SH 1 +.Os +.Sh NAME +.Nm sh +.Nd command interpreter (shell) +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl aCefnuvxIimqVEb +.Op Cm +aCefnuvxIimqVEb +.Ek +.Bk -words +.Op Fl o Ar option_name +.Op Cm +o Ar option_name +.Ek +.Bk -words +.Op Ar command_file Oo Ar argument ... Oc +.Ek +.Nm +.Fl c +.Bk -words +.Op Fl aCefnuvxIimqVEb +.Op Cm +aCefnuvxIimqVEb +.Ek +.Bk -words +.Op Fl o Ar option_name +.Op Cm +o Ar option_name +.Ek +.Bk -words +.Ar command_string +.Op Ar command_name Oo Ar argument ... Oc +.Ek +.Nm +.Fl s +.Bk -words +.Op Fl aCefnuvxIimqVEb +.Op Cm +aCefnuvxIimqVEb +.Ek +.Bk -words +.Op Fl o Ar option_name +.Op Cm +o Ar option_name +.Ek +.Bk -words +.Op Ar argument ... +.Ek +.Sh DESCRIPTION +.Nm +is the standard command interpreter for the system. +The current version of +.Nm +is in the process of being changed to conform with the +.Tn POSIX +1003.2 and 1003.2a specifications for the shell. +This version has many +features which make it appear similar in some respects to the Korn shell, +but it is not a Korn shell clone (see +.Xr ksh 1 ) . +Only features designated by +.Tn POSIX , +plus a few Berkeley extensions, are being incorporated into this shell. +.\" We expect +.\" .Tn POSIX +.\" conformance by the time 4.4 BSD is released. +This man page is not intended +to be a tutorial or a complete specification of the shell. +.Ss Overview +The shell is a command that reads lines from either a file or the +terminal, interprets them, and generally executes other commands. +It is the program that is running when a user logs into the system +(although a user can select a different shell with the +.Xr chsh 1 +command). +The shell implements a language that has flow control +constructs, a macro facility that provides a variety of features in +addition to data storage, along with built in history and line editing +capabilities. +It incorporates many features to aid interactive use and +has the advantage that the interpretative language is common to both +interactive and non-interactive use (shell scripts). +That is, commands +can be typed directly to the running shell or can be put into a file and +the file can be executed directly by the shell. +.Ss Invocation +If no arguments are present and if the standard input of the shell +is connected to a terminal (or if the +.Fl i +flag is set), +and the +.Fl c +option is not present, the shell is considered an interactive shell. +An interactive shell generally prompts before each command and handles +programming and command errors differently (as described below). +When first starting, +the shell inspects argument 0, and if it begins with a dash +.Sq - , +the shell is also considered +a login shell. +This is normally done automatically by the system +when the user first logs in. +A login shell first reads commands +from the files +.Pa /etc/profile +and +.Pa .profile +if they exist. +If the environment variable +.Ev ENV +is set on entry to a shell, or is set in the +.Pa .profile +of a login shell, the shell next reads +commands from the file named in +.Ev ENV . +Therefore, a user should place commands that are to be executed only at +login time in the +.Pa .profile +file, and commands that are executed for every shell inside the +.Ev ENV +file. +To set the +.Ev ENV +variable to some file, place the following line in your +.Pa .profile +of your home directory +.Pp +.Dl ENV=$HOME/.shinit; export ENV +.Pp +substituting for +.Dq .shinit +any filename you wish. +Since the +.Ev ENV +file is read for every invocation of the shell, including shell scripts +and non-interactive shells, the following paradigm is useful for +restricting commands in the +.Ev ENV +file to interactive invocations. +Place commands within the +.Dq case +and +.Dq esac +below (these commands are described later): +.Pp +.Bl -item -compact -offset indent +.It +.Li case $- in *i*) +.Bl -item -compact -offset indent +.It +.Li # commands for interactive use only +.It +.Li ... +.El +.It +.Li esac +.El +.Pp +If command line arguments besides the options have been specified, then +the shell treats the first argument as the name of a file from which to +read commands (a shell script), and the remaining arguments are set as the +positional parameters of the shell ($1, $2, etc). +Otherwise, the shell +reads commands from its standard input. +.Ss Argument List Processing +All of the single letter options have a corresponding name that can be +used as an argument to the +.Fl o +option. +The set +.Fl o +name is provided next to the single letter option in +the description below. +Specifying a dash +.Dq - +turns the option on, while using a plus +.Dq + +disables the option. +The following options can be set from the command line or +with the +.Ic set +built-in (described later). +.Bl -tag -width aaaallexportfoo -offset indent +.It Fl a Em allexport +Export all variables assigned to. +.It Fl c +Read commands from the +.Ar command_string +operand instead of from the standard input. +Special parameter 0 will be set from the +.Ar command_name +operand and the positional parameters ($1, $2, etc.) +set from the remaining argument operands. +.It Fl C Em noclobber +Don't overwrite existing files with +.Dq \*[Gt] . +.It Fl e Em errexit +If not interactive, exit immediately if any untested command fails. +The exit status of a command is considered to be +explicitly tested if the command is used to control an +.Ic if , +.Ic elif , +.Ic while , +or +.Ic until , +or if the command is the left hand operand of an +.Dq \*[Am]\*[Am] +or +.Dq || +operator. +.It Fl f Em noglob +Disable pathname expansion. +.It Fl n Em noexec +If not interactive, read commands but do not execute them. +This is useful for checking the syntax of shell scripts. +.It Fl u Em nounset +Write a message to standard error when attempting to expand a variable +that is not set, and if the shell is not interactive, exit immediately. +.It Fl v Em verbose +The shell writes its input to standard error as it is read. +Useful for debugging. +.It Fl x Em xtrace +Write each command to standard error (preceded by a +.Sq +\ ) +before it is executed. +Useful for debugging. +.It Fl q Em quietprofile +If the +.Fl v +or +.Fl x +options have been set, do not apply them when reading +initialization files, these being +.Pa /etc/profile , +.Pa .profile , +and the file specified by the +.Ev ENV +environment variable. +.It Fl I Em ignoreeof +Ignore EOFs from input when interactive. +.It Fl i Em interactive +Force the shell to behave interactively. +.It Fl m Em monitor +Turn on job control (set automatically when interactive). +.It Fl s Em stdin +Read commands from standard input (set automatically if no file arguments +are present). +This option has no effect when set after the shell has +already started running (i.e. with +.Ic set ) . +.It Fl V Em vi +Enable the built-in +.Xr vi 1 +command line editor (disables +.Fl E +if it has been set). +(See the +.Sx Command Line Editing +section below.) +.It Fl E Em emacs +Enable the built-in emacs style +command line editor (disables +.Fl V +if it has been set). +(See the +.Sx Command Line Editing +section below.) +.It Fl b Em notify +Enable asynchronous notification of background job completion. +(Not implemented.) +.It "\ \ " Em cdprint +Make an interactive shell always print the new directory name when +changed by the +.Ic cd +command. +.It "\ \ " Em tabcomplete +Enables filename completion in the command line editor. +Typing a tab character will extend the current input word to match a +filename. +If more than one filename matches it is only extended to be the common prefix. +Typing a second tab character will list all the matching names. +One of the editing modes, either +.Fl E +or +.Fl V , +must be enabled for this to work. +.El +.Ss Lexical Structure +The shell reads input in terms of lines from a file and breaks it up into +words at whitespace (blanks and tabs), and at certain sequences of +characters that are special to the shell called +.Dq operators . +There are two types of operators: control operators and redirection +operators (their meaning is discussed later). +Following is a list of operators: +.Bl -ohang -offset indent +.It "Control operators:" +.Dl \*[Am] \*[Am]\*[Am] \&( \&) \&; ;; | || \*[Lt]newline\*[Gt] +.It "Redirection operators:" +.Dl \*[Lt] \*[Gt] \*[Gt]| \*[Lt]\*[Lt] \*[Gt]\*[Gt] \*[Lt]\*[Am] \*[Gt]\*[Am] \*[Lt]\*[Lt]- \*[Lt]\*[Gt] +.El +.Ss Quoting +Quoting is used to remove the special meaning of certain characters or +words to the shell, such as operators, whitespace, or keywords. +There are three types of quoting: matched single quotes, +matched double quotes, and backslash. +.Ss Backslash +A backslash preserves the literal meaning of the following +character, with the exception of +.Aq newline . +A backslash preceding a +.Aq newline +is treated as a line continuation. +.Ss Single Quotes +Enclosing characters in single quotes preserves the literal meaning of all +the characters (except single quotes, making it impossible to put +single quotes in a single-quoted string). +.Ss Double Quotes +Enclosing characters within double quotes preserves the literal +meaning of all characters except dollar sign +.Pq $ , +backquote +.Pq ` , +and backslash +.Pq \e . +The backslash inside double quotes is historically weird, and serves to +quote only the following characters: +.Dl $ ` \*q \e \*[Lt]newline\*[Gt] . +Otherwise it remains literal. +.Ss Reserved Words +Reserved words are words that have special meaning to the +shell and are recognized at the beginning of a line and +after a control operator. +The following are reserved words: +.Bl -column while while while while while -offset indent +.It ! Ta elif Ta fi Ta while Ta case +.It else Ta for Ta then Ta { Ta } +.It do Ta done Ta until Ta if Ta esac +.El +.Pp +Their meaning is discussed later. +.Ss Aliases +An alias is a name and corresponding value set using the +.Ic alias +built-in command. +Whenever a reserved word may occur (see above), +and after checking for reserved words, the shell +checks the word to see if it matches an alias. +If it does, it replaces it in the input stream with its value. +For example, if there is an alias called +.Dq lf +with the value +.Dq "ls -F" , +then the input: +.Pp +.Dl lf foobar Aq return +.Pp +would become +.Pp +.Dl ls -F foobar Aq return +.Pp +Aliases provide a convenient way for naive users to create shorthands for +commands without having to learn how to create functions with arguments. +They can also be used to create lexically obscure code. +This use is discouraged. +.Ss Commands +The shell interprets the words it reads according to a language, the +specification of which is outside the scope of this man page (refer to the +BNF in the +.Tn POSIX +1003.2 document). +Essentially though, a line is read and if the first +word of the line (or after a control operator) is not a reserved word, +then the shell has recognized a simple command. +Otherwise, a complex +command or some other special construct may have been recognized. +.Ss Simple Commands +If a simple command has been recognized, the shell performs +the following actions: +.Bl -enum -offset indent +.It +Leading words of the form +.Dq name=value +are stripped off and assigned to the environment of the simple command. +Redirection operators and their arguments (as described below) are +stripped off and saved for processing. +.It +The remaining words are expanded as described in the +.Sx Word Expansions +section below, +and the first remaining word is considered the command name and the +command is located. +The remaining words are considered the arguments of the command. +If no command name resulted, then the +.Dq name=value +variable assignments recognized in item 1 affect the current shell. +.It +Redirections are performed as described in the next section. +.El +.Ss Redirections +Redirections are used to change where a command reads its input or sends +its output. +In general, redirections open, close, or duplicate an +existing reference to a file. +The overall format used for redirection is: +.Pp +.Dl [n] Va redir-op Ar file +.Pp +where +.Va redir-op +is one of the redirection operators mentioned previously. +Following is a list of the possible redirections. +The +.Bq n +is an optional number, as in +.Sq 3 +(not +.Sq Bq 3 ) , +that refers to a file descriptor. +.Bl -tag -width aaabsfiles -offset indent +.It [n] Ns \*[Gt] file +Redirect standard output (or n) to file. +.It [n] Ns \*[Gt]| file +Same, but override the +.Fl C +option. +.It [n] Ns \*[Gt]\*[Gt] file +Append standard output (or n) to file. +.It [n] Ns \*[Lt] file +Redirect standard input (or n) from file. +.It [n1] Ns \*[Lt]\*[Am] Ns n2 +Duplicate standard input (or n1) from file descriptor n2. +.It [n] Ns \*[Lt]\*[Am]- +Close standard input (or n). +.It [n1] Ns \*[Gt]\*[Am] Ns n2 +Duplicate standard output (or n1) to n2. +.It [n] Ns \*[Gt]\*[Am]- +Close standard output (or n). +.It [n] Ns \*[Lt]\*[Gt] file +Open file for reading and writing on standard input (or n). +.El +.Pp +The following redirection is often called a +.Dq here-document . +.Bl -item -offset indent +.It +.Li [n]\*[Lt]\*[Lt] delimiter +.Dl here-doc-text ... +.Li delimiter +.El +.Pp +All the text on successive lines up to the delimiter, or to an EOF, is +saved away and made available to the command on standard input, or file +descriptor n if it is specified. +If the delimiter as specified on the initial line is +quoted, then the here-doc-text is treated literally; otherwise, the text is +subjected to parameter expansion, command substitution, and arithmetic +expansion as described in the +.Sx Word Expansions +section below. +If the operator is +.Dq \*[Lt]\*[Lt]- +instead of +.Dq \*[Lt]\*[Lt] , +then leading tabs in the here-doc-text are stripped. +.Ss Search and Execution +There are three types of commands: shell functions, built-in commands, and +normal programs -- and the command is searched for (by name) in that order. +They each are executed in a different way. +.Pp +When a shell function is executed, all of the shell positional parameters +(except $0, which remains unchanged) are set to the arguments of the shell +function. +The variables which are explicitly placed in the environment of +the command (by placing assignments to them before the function name) are +made local to the function and are set to the values given. +Then the command given in the function definition is executed. +The positional parameters are restored to their original values +when the command completes. +This all occurs within the current shell. +.Pp +Shell built-ins are executed internally to the shell, without spawning a +new process. +.Pp +Otherwise, if the command name doesn't match a function or built-in, the +command is searched for as a normal program in the file system (as +described in the next section). +When a normal program is executed, the shell runs the program, +passing the arguments and the environment to the program. +If the program is not a normal executable file (i.e., if it does +not begin with the "magic number" whose +.Tn ASCII +representation is "#!", so +.Xr execve 2 +returns +.Er ENOEXEC +then) the shell will interpret the program in a subshell. +The child shell will reinitialize itself in this case, +so that the effect will be as if a +new shell had been invoked to handle the ad-hoc shell script, except that +the location of hashed commands located in the parent shell will be +remembered by the child. +.Pp +Note that previous versions of this document and the source code itself +misleadingly and sporadically refer to a shell script without a magic +number as a "shell procedure". +.Ss Path Search +When locating a command, the shell first looks to see if it has a shell +function by that name. +Then it looks for a built-in command by that name. +If a built-in command is not found, one of two things happen: +.Bl -enum +.It +Command names containing a slash are simply executed without performing +any searches. +.It +The shell searches each entry in +.Ev PATH +in turn for the command. +The value of the +.Ev PATH +variable should be a series of entries separated by colons. +Each entry consists of a directory name. +The current directory may be indicated +implicitly by an empty directory name, or explicitly by a single period. +.El +.Ss Command Exit Status +Each command has an exit status that can influence the behavior +of other shell commands. +The paradigm is that a command exits +with zero for normal or success, and non-zero for failure, +error, or a false indication. +The man page for each command +should indicate the various exit codes and what they mean. +Additionally, the built-in commands return exit codes, as does +an executed shell function. +.Pp +If a command consists entirely of variable assignments then the +exit status of the command is that of the last command substitution +if any, otherwise 0. +.Ss Complex Commands +Complex commands are combinations of simple commands with control +operators or reserved words, together creating a larger complex command. +More generally, a command is one of the following: +.Bl -bullet +.It +simple command +.It +pipeline +.It +list or compound-list +.It +compound command +.It +function definition +.El +.Pp +Unless otherwise stated, the exit status of a command is that of the last +simple command executed by the command. +.Ss Pipelines +A pipeline is a sequence of one or more commands separated +by the control operator |. +The standard output of all but +the last command is connected to the standard input +of the next command. +The standard output of the last +command is inherited from the shell, as usual. +.Pp +The format for a pipeline is: +.Pp +.Dl [!] command1 [ | command2 ...] +.Pp +The standard output of command1 is connected to the standard input of +command2. +The standard input, standard output, or both of a command is +considered to be assigned by the pipeline before any redirection specified +by redirection operators that are part of the command. +.Pp +If the pipeline is not in the background (discussed later), the shell +waits for all commands to complete. +.Pp +If the reserved word ! does not precede the pipeline, the exit status is +the exit status of the last command specified in the pipeline. +Otherwise, the exit status is the logical NOT of the exit status of the +last command. +That is, if the last command returns zero, the exit status +is 1; if the last command returns greater than zero, the exit status is +zero. +.Pp +Because pipeline assignment of standard input or standard output or both +takes place before redirection, it can be modified by redirection. +For example: +.Pp +.Dl $ command1 2\*[Gt]\*[Am]1 | command2 +.Pp +sends both the standard output and standard error of command1 +to the standard input of command2. +.Pp +A ; or +.Aq newline +terminator causes the preceding AND-OR-list (described +next) to be executed sequentially; a \*[Am] causes asynchronous execution of +the preceding AND-OR-list. +.Pp +Note that unlike some other shells, each process in the pipeline is a +child of the invoking shell (unless it is a shell built-in, in which case +it executes in the current shell -- but any effect it has on the +environment is wiped). +.Ss Background Commands -- \*[Am] +If a command is terminated by the control operator ampersand (\*[Am]), the +shell executes the command asynchronously -- that is, the shell does not +wait for the command to finish before executing the next command. +.Pp +The format for running a command in background is: +.Pp +.Dl command1 \*[Am] [command2 \*[Am] ...] +.Pp +If the shell is not interactive, the standard input of an asynchronous +command is set to +.Pa /dev/null . +.Ss Lists -- Generally Speaking +A list is a sequence of zero or more commands separated by newlines, +semicolons, or ampersands, and optionally terminated by one of these three +characters. +The commands in a list are executed in the order they are written. +If command is followed by an ampersand, the shell starts the +command and immediately proceed onto the next command; otherwise it waits +for the command to terminate before proceeding to the next one. +.Ss Short-Circuit List Operators +.Dq \*[Am]\*[Am] +and +.Dq || +are AND-OR list operators. +.Dq \*[Am]\*[Am] +executes the first command, and then executes the second command if and only +if the exit status of the first command is zero. +.Dq || +is similar, but executes the second command if and only if the exit status +of the first command is nonzero. +.Dq \*[Am]\*[Am] +and +.Dq || +both have the same priority. +Note that these operators are left-associative, so +.Dq true || echo bar \*[Am]\*[Am] echo baz +writes +.Dq baz +and nothing else. +This is not the way it works in C. +Also, if you forget the left-hand side (for example when continuing lines but +forgetting to use a backslash) it defaults to a true statement. +This behavior is not useful and should not be relied upon. +.Ss Flow-Control Constructs -- if, while, for, case +The syntax of the if command is +.Bd -literal -offset indent +if list +then list +[ elif list +then list ] ... +[ else list ] +fi +.Ed +.Pp +The syntax of the while command is +.Bd -literal -offset indent +while list +do list +done +.Ed +.Pp +The two lists are executed repeatedly while the exit status of the +first list is zero. +The until command is similar, but has the word +until in place of while, which causes it to +repeat until the exit status of the first list is zero. +.Pp +The syntax of the for command is +.Bd -literal -offset indent +for variable in word ... +do list +done +.Ed +.Pp +The words are expanded, and then the list is executed repeatedly with the +variable set to each word in turn. +do and done may be replaced with +.Dq { +and +.Dq } . +.Pp +The syntax of the break and continue command is +.Bd -literal -offset indent +break [ num ] +continue [ num ] +.Ed +.Pp +Break terminates the num innermost for or while loops. +Continue continues with the next iteration of the innermost loop. +These are implemented as built-in commands. +.Pp +The syntax of the case command is +.Bd -literal -offset indent +case word in +pattern) list ;; +\&... +esac +.Ed +.Pp +The pattern can actually be one or more patterns (see +.Sx Shell Patterns +described later), separated by +.Dq \*(Ba +characters. +.Ss Grouping Commands Together +Commands may be grouped by writing either +.Pp +.Dl (list) +.Pp +or +.Pp +.Dl { list; } +.Pp +The first of these executes the commands in a subshell. +Built-in commands grouped into a (list) will not affect the current shell. +The second form does not fork another shell so is slightly more efficient. +Grouping commands together this way allows you to redirect +their output as though they were one program: +.Bd -literal -offset indent +{ echo -n \*q hello \*q ; echo \*q world" ; } \*[Gt] greeting +.Ed +.Pp +Note that +.Dq } +must follow a control operator (here, +.Dq \&; ) +so that it is recognized as a reserved word and not as another command argument. +.Ss Functions +The syntax of a function definition is +.Pp +.Dl name ( ) command +.Pp +A function definition is an executable statement; when executed it +installs a function named name and returns an exit status of zero. +The command is normally a list enclosed between +.Dq { +and +.Dq } . +.Pp +Variables may be declared to be local to a function by using a local +command. +This should appear as the first statement of a function, and the syntax is +.Pp +.Dl local [ variable | - ] ... +.Pp +.Dq Local +is implemented as a built-in command. +.Pp +When a variable is made local, it inherits the initial value and exported +and read-only flags from the variable with the same name in the surrounding +scope, if there is one. +Otherwise, the variable is initially unset. +The shell uses dynamic scoping, so that if you make the variable x local to +function f, which then calls function g, references to the variable x made +inside g will refer to the variable x declared inside f, not to the global +variable named x. +.Pp +The only special parameter that can be made local is +.Dq - . +Making +.Dq - +local causes any shell options that are changed via the set command inside the +function to be restored to their original values when the function +returns. +.Pp +The syntax of the return command is +.Pp +.Dl return [ exitstatus ] +.Pp +It terminates the currently executing function. +Return is implemented as a built-in command. +.Ss Variables and Parameters +The shell maintains a set of parameters. +A parameter denoted by a name is called a variable. +When starting up, the shell turns all the environment +variables into shell variables. +New variables can be set using the form +.Pp +.Dl name=value +.Pp +Variables set by the user must have a name consisting solely of +alphabetics, numerics, and underscores - the first of which must not be +numeric. +A parameter can also be denoted by a number or a special +character as explained below. +.Ss Positional Parameters +A positional parameter is a parameter denoted by a number (n \*[Gt] 0). +The shell sets these initially to the values of its command line arguments +that follow the name of the shell script. +The +.Ic set +built-in can also be used to set or reset them. +.Ss Special Parameters +A special parameter is a parameter denoted by one of the following special +characters. +The value of the parameter is listed next to its character. +.Bl -tag -width thinhyphena +.It * +Expands to the positional parameters, starting from one. +When the +expansion occurs within a double-quoted string it expands to a single +field with the value of each parameter separated by the first character of +the +.Ev IFS +variable, or by a +.Aq space +if +.Ev IFS +is unset. +.It @ +Expands to the positional parameters, starting from one. +When the expansion occurs within double quotes, each positional +parameter expands as a separate argument. +If there are no positional parameters, the +expansion of @ generates zero arguments, even when @ is +double-quoted. +What this basically means, for example, is +if $1 is +.Dq abc +and $2 is +.Dq def ghi , +then +.Qq $@ +expands to +the two arguments: +.Pp +.Sm off +.Dl \*q abc \*q \ \*q def\ ghi \*q +.Sm on +.It # +Expands to the number of positional parameters. +.It \&? +Expands to the exit status of the most recent pipeline. +.It - (Hyphen.) +Expands to the current option flags (the single-letter +option names concatenated into a string) as specified on +invocation, by the set built-in command, or implicitly +by the shell. +.It $ +Expands to the process ID of the invoked shell. +A subshell retains the same value of $ as its parent. +.It \&! +Expands to the process ID of the most recent background +command executed from the current shell. +For a pipeline, the process ID is that of the last command in the pipeline. +.It 0 (Zero.) +Expands to the name of the shell or shell script. +.El +.Ss Word Expansions +This section describes the various expansions that are performed on words. +Not all expansions are performed on every word, as explained later. +.Pp +Tilde expansions, parameter expansions, command substitutions, arithmetic +expansions, and quote removals that occur within a single word expand to a +single field. +It is only field splitting or pathname expansion that can +create multiple fields from a single word. +The single exception to this +rule is the expansion of the special parameter @ within double quotes, as +was described above. +.Pp +The order of word expansion is: +.Bl -enum +.It +Tilde Expansion, Parameter Expansion, Command Substitution, +Arithmetic Expansion (these all occur at the same time). +.It +Field Splitting is performed on fields +generated by step (1) unless the +.Ev IFS +variable is null. +.It +Pathname Expansion (unless set +.Fl f +is in effect). +.It +Quote Removal. +.El +.Pp +The $ character is used to introduce parameter expansion, command +substitution, or arithmetic evaluation. +.Ss Tilde Expansion (substituting a user's home directory) +A word beginning with an unquoted tilde character (~) is +subjected to tilde expansion. +All the characters up to +a slash (/) or the end of the word are treated as a username +and are replaced with the user's home directory. +If the username is missing (as in +.Pa ~/foobar ) , +the tilde is replaced with the value of the +.Va HOME +variable (the current user's home directory). +.Ss Parameter Expansion +The format for parameter expansion is as follows: +.Pp +.Dl ${expression} +.Pp +where expression consists of all characters until the matching +.Dq } . +Any +.Dq } +escaped by a backslash or within a quoted string, and characters in +embedded arithmetic expansions, command substitutions, and variable +expansions, are not examined in determining the matching +.Dq } . +.Pp +The simplest form for parameter expansion is: +.Pp +.Dl ${parameter} +.Pp +The value, if any, of parameter is substituted. +.Pp +The parameter name or symbol can be enclosed in braces, which are +optional except for positional parameters with more than one digit or +when parameter is followed by a character that could be interpreted as +part of the name. +If a parameter expansion occurs inside double quotes: +.Bl -enum +.It +Pathname expansion is not performed on the results of the expansion. +.It +Field splitting is not performed on the results of the +expansion, with the exception of the special rules for @. +.El +.Pp +In addition, a parameter expansion can be modified by using one of the +following formats. +If the +.Dq Dv \&: +is omitted in the following modifiers, then the expansion is applied only +to unset parameters, not null ones. +.Bl -tag -width aaparameterwordaaaaa +.It ${parameter:-word} +Use Default Values. +If parameter is unset or null, the expansion of word +is substituted; otherwise, the value of parameter is substituted. +.It ${parameter:=word} +Assign Default Values. +If parameter is unset or null, the expansion of +word is assigned to parameter. +In all cases, the final value of parameter is substituted. +Only variables, not positional parameters or special +parameters, can be assigned in this way. +.It ${parameter:?[word]} +Indicate Error if Null or Unset. +If parameter is unset or null, the +expansion of word (or a message indicating it is unset if word is omitted) +is written to standard error and the shell exits with a nonzero exit status. +Otherwise, the value of parameter is substituted. +An interactive shell need not exit. +.It ${parameter:+word} +Use Alternative Value. +If parameter is unset or null, null is +substituted; otherwise, the expansion of word is substituted. +.It ${#parameter} +String Length. +The length in characters of the value of parameter. +.El +.Pp +The following four varieties of parameter expansion provide for substring +processing. +In each case, pattern matching notation (see +.Sx Shell Patterns ) , +rather than regular expression notation, is used to evaluate the patterns. +If parameter is * or @, the result of the expansion is unspecified. +Enclosing the full parameter expansion string in double quotes does not +cause the following four varieties of pattern characters to be quoted, +whereas quoting characters within the braces has this effect. +.Bl -tag -width aaparameterwordaaaaa +.It ${parameter%word} +Remove Smallest Suffix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the +smallest portion of the suffix matched by the pattern deleted. +.It ${parameter%%word} +Remove Largest Suffix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the largest +portion of the suffix matched by the pattern deleted. +.It ${parameter#word} +Remove Smallest Prefix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the +smallest portion of the prefix matched by the pattern deleted. +.It ${parameter##word} +Remove Largest Prefix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the largest +portion of the prefix matched by the pattern deleted. +.El +.Ss Command Substitution +Command substitution allows the output of a command to be substituted in +place of the command name itself. +Command substitution occurs when the command is enclosed as follows: +.Pp +.Dl $(command) +.Pp +or +.Po +.Dq backquoted +version +.Pc : +.Pp +.Dl `command` +.Pp +The shell expands the command substitution by executing command in a +subshell environment and replacing the command substitution with the +standard output of the command, removing sequences of one or more +.Ao newline Ac Ns s +at the end of the substitution. +(Embedded +.Ao newline Ac Ns s +before +the end of the output are not removed; however, during field splitting, +they may be translated into +.Ao space Ac Ns s , +depending on the value of +.Ev IFS +and quoting that is in effect.) +.Ss Arithmetic Expansion +Arithmetic expansion provides a mechanism for evaluating an arithmetic +expression and substituting its value. +The format for arithmetic expansion is as follows: +.Pp +.Dl $((expression)) +.Pp +The expression is treated as if it were in double quotes, except +that a double quote inside the expression is not treated specially. +The shell expands all tokens in the expression for parameter expansion, +command substitution, and quote removal. +.Pp +Next, the shell treats this as an arithmetic expression and +substitutes the value of the expression. +.Pp +Arithmetic expressions use a syntax similar to that +of the C language, and are evaluated using the +.Ql intmax_t +data type (this is an extension to +.Tn POSIX , +which requires only +.Ql long +arithmetic). +Shell variables may be referenced by name inside an arithmetic +expression, without needing a +.Dq \&$ +sign. +.Ss White Space Splitting (Field Splitting) +After parameter expansion, command substitution, and +arithmetic expansion the shell scans the results of +expansions and substitutions that did not occur in double quotes for +field splitting and multiple fields can result. +.Pp +The shell treats each character of the +.Ev IFS +as a delimiter and use the delimiters to split the results of parameter +expansion and command substitution into fields. +.Pp +Non-whitespace characters in +.Ev IFS +are treated strictly as parameter terminators. +So adjacent non-whitespace +.Ev IFS +characters will produce empty parameters. +.Pp +If +.Ev IFS +is unset it is assumed to contain space, tab, and newline. +.Ss Pathname Expansion (File Name Generation) +Unless the +.Fl f +flag is set, file name generation is performed after word splitting is +complete. +Each word is viewed as a series of patterns, separated by slashes. +The process of expansion replaces the word with the names of all +existing files whose names can be formed by replacing each pattern with a +string that matches the specified pattern. +There are two restrictions on +this: first, a pattern cannot match a string containing a slash, and +second, a pattern cannot match a string starting with a period unless the +first character of the pattern is a period. +The next section describes the +patterns used for both Pathname Expansion and the +.Ic case +command. +.Ss Shell Patterns +A pattern consists of normal characters, which match themselves, +and meta-characters. +The meta-characters are +.Dq \&! , +.Dq * , +.Dq \&? , +and +.Dq \&[ . +These characters lose their special meanings if they are quoted. +When command or variable substitution is performed +and the dollar sign or backquotes are not double-quoted, +the value of the variable or the output of +the command is scanned for these characters and they are turned into +meta-characters. +.Pp +An asterisk +.Pq Dq * +matches any string of characters. +A question mark +.Pq Dq \&? +matches any single character. +A left bracket +.Pq Dq \&[ +introduces a character class. +The end of the character class is indicated by a right bracket +.Pq Dq \&] ; +if this +.Dq \&] +is missing then the +.Dq \&[ +matches a +.Dq \&[ +rather than introducing a character class. +A character class matches any of the characters between the square brackets. +A range of characters may be specified using a minus sign +.Pq Dq - . +The character class may be complemented +by making an exclamation mark +.Pq Dq \&! +the first character of the character class. +.Pp +To include a +.Dq \&] +in a character class, make it the first character listed (after the +.Dq \&! , +if any). +To include a +.Dq - , +make it the first or last character listed. +.Ss Built-ins +This section lists the built-in commands which are built-in because they +need to perform some operation that can't be performed by a separate +process. +In addition to these, there are several other commands that may +be built in for efficiency (e.g. +.Xr printf 1 , +.Xr echo 1 , +.Xr test 1 , +etc). +.Bl -tag -width 5n +.It : +A null command that returns a 0 (true) exit value. +.It \&. file +The commands in the specified file are read and executed by the shell. +.It alias Op Ar name Ns Op Ar "=string ..." +If +.Ar name=string +is specified, the shell defines the alias +.Ar name +with value +.Ar string . +If just +.Ar name +is specified, the value of the alias +.Ar name +is printed. +With no arguments, the +.Ic alias +built-in prints the +names and values of all defined aliases (see +.Ic unalias ) . +.It bg [ Ar job ] ... +Continue the specified jobs (or the current job if no +jobs are given) in the background. +.It command Oo Fl p Oc Oo Fl v Oc Oo Fl V Oc Ar command Oo Ar arg ... Oc +Execute the specified command but ignore shell functions when searching +for it. +(This is useful when you +have a shell function with the same name as a built-in command.) +.Bl -tag -width 5n +.It Fl p +search for command using a +.Ev PATH +that guarantees to find all the standard utilities. +.It Fl V +Do not execute the command but +search for the command and print the resolution of the +command search. +This is the same as the +.Ic type +built-in. +.It Fl v +Do not execute the command but +search for the command and print the absolute pathname +of utilities, the name for built-ins or the expansion of aliases. +.El +.It cd Oo Fl P Oc Op Ar directory Op Ar replace +Switch to the specified directory (default +.Ev $HOME ) . +If +.Ar replace +is specified, then the new directory name is generated by replacing +the first occurrence of +.Ar directory +in the current directory name with +.Ar replace . +If +.Ar directory +is +.Sq - , +then the current working directory is changed to the previous current +working directory as set in +.Ev OLDPWD . +Otherwise if an entry for +.Ev CDPATH +appears in the environment of the +.Ic cd +command or the shell variable +.Ev CDPATH +is set and the directory name does not begin with a slash, +or its first (or only) component isn't dot or dot dot, +then the directories listed in +.Ev CDPATH +will be searched for the specified directory. +The format of +.Ev CDPATH +is the same as that of +.Ev PATH . +.Pp +The +.Fl P +option instructs the shell to update +.Ev PWD +with the specified physical directory path and change to that directory. +This is the default. +.Pp +When the directory changes, the variable +.Ev OLDPWD +is set to the working directory before the change. +.Pp +Some shells also support a +.Fl L +option, which instructs the shell to update +.Ev PWD +with the logical path and to change the current directory +accordingly. +This is not supported. +.Pp +In an interactive shell, the +.Ic cd +command will print out the name of the +directory that it actually switched to if this is different from the name +that the user gave. +These may be different either because the +.Ev CDPATH +mechanism was used or because a symbolic link was crossed. +.It eval Ar string ... +Concatenate all the arguments with spaces. +Then re-parse and execute the command. +.It exec Op Ar command arg ... +Unless command is omitted, the shell process is replaced with the +specified program (which must be a real program, not a shell built-in or +function). +Any redirections on the +.Ic exec +command are marked as permanent, so that they are not undone when the +.Ic exec +command finishes. +.It exit Op Ar exitstatus +Terminate the shell process. +If +.Ar exitstatus +is given it is used as the exit status of the shell; otherwise the +exit status of the preceding command is used. +.It export Ar name ... +.It export Fl p +The specified names are exported so that they will appear in the +environment of subsequent commands. +The only way to un-export a variable is to unset it. +The shell allows the value of a variable to be set at the +same time it is exported by writing +.Pp +.Dl export name=value +.Pp +With no arguments the export command lists the names of all exported variables. +With the +.Fl p +option specified the output will be formatted suitably for non-interactive use. +.It fc Oo Fl e Ar editor Oc Oo Ar first Oo Ar last Oc Oc +.It fc Fl l Oo Fl nr Oc Oo Ar first Oo Ar last Oc Oc +.It fc Fl s Oo Ar old=new Oc Oo Ar first Oc +The +.Ic fc +built-in lists, or edits and re-executes, commands previously entered +to an interactive shell. +.Bl -tag -width 5n +.It Fl e No editor +Use the editor named by editor to edit the commands. +The editor string is a command name, subject to search via the +.Ev PATH +variable. +The value in the +.Ev FCEDIT +variable is used as a default when +.Fl e +is not specified. +If +.Ev FCEDIT +is null or unset, the value of the +.Ev EDITOR +variable is used. +If +.Ev EDITOR +is null or unset, +.Xr ed 1 +is used as the editor. +.It Fl l No (ell) +List the commands rather than invoking an editor on them. +The commands are written in the sequence indicated by +the first and last operands, as affected by +.Fl r , +with each command preceded by the command number. +.It Fl n +Suppress command numbers when listing with -l. +.It Fl r +Reverse the order of the commands listed (with +.Fl l ) +or edited (with neither +.Fl l +nor +.Fl s ) . +.It Fl s +Re-execute the command without invoking an editor. +.It first +.It last +Select the commands to list or edit. +The number of previous commands that +can be accessed are determined by the value of the +.Ev HISTSIZE +variable. +The value of first or last or both are one of the following: +.Bl -tag -width 5n +.It [+]number +A positive number representing a command number; command numbers can be +displayed with the +.Fl l +option. +.It Fl number +A negative decimal number representing the command that was executed +number of commands previously. +For example, \-1 is the immediately previous command. +.El +.It string +A string indicating the most recently entered command that begins with +that string. +If the old=new operand is not also specified with +.Fl s , +the string form of the first operand cannot contain an embedded equal sign. +.El +.Pp +The following environment variables affect the execution of fc: +.Bl -tag -width HISTSIZE +.It Ev FCEDIT +Name of the editor to use. +.It Ev HISTSIZE +The number of previous commands that are accessible. +.El +.It fg Op Ar job +Move the specified job or the current job to the foreground. +.It getopts Ar optstring var +The +.Tn POSIX +.Ic getopts +command, not to be confused with the +.Em Bell Labs +-derived +.Xr getopt 1 . +.Pp +The first argument should be a series of letters, each of which may be +optionally followed by a colon to indicate that the option requires an +argument. +The variable specified is set to the parsed option. +.Pp +The +.Ic getopts +command deprecates the older +.Xr getopt 1 +utility due to its handling of arguments containing whitespace. +.Pp +The +.Ic getopts +built-in may be used to obtain options and their arguments +from a list of parameters. +When invoked, +.Ic getopts +places the value of the next option from the option string in the list in +the shell variable specified by +.Va var +and its index in the shell variable +.Ev OPTIND . +When the shell is invoked, +.Ev OPTIND +is initialized to 1. +For each option that requires an argument, the +.Ic getopts +built-in will place it in the shell variable +.Ev OPTARG . +If an option is not allowed for in the +.Va optstring , +then +.Ev OPTARG +will be unset. +.Pp +.Va optstring +is a string of recognized option letters (see +.Xr getopt 3 ) . +If a letter is followed by a colon, the option is expected to have an +argument which may or may not be separated from it by whitespace. +If an option character is not found where expected, +.Ic getopts +will set the variable +.Va var +to a +.Dq \&? ; +.Ic getopts +will then unset +.Ev OPTARG +and write output to standard error. +By specifying a colon as the first character of +.Va optstring +all errors will be ignored. +.Pp +A nonzero value is returned when the last option is reached. +If there are no remaining arguments, +.Ic getopts +will set +.Va var +to the special option, +.Dq -- , +otherwise, it will set +.Va var +to +.Dq \&? . +.Pp +The following code fragment shows how one might process the arguments +for a command that can take the options +.Op a +and +.Op b , +and the option +.Op c , +which requires an argument. +.Bd -literal -offset indent +while getopts abc: f +do + case $f in + a | b) flag=$f;; + c) carg=$OPTARG;; + \e?) echo $USAGE; exit 1;; + esac +done +shift $(expr $OPTIND - 1) +.Ed +.Pp +This code will accept any of the following as equivalent: +.Bd -literal -offset indent +cmd \-acarg file file +cmd \-a \-c arg file file +cmd \-carg -a file file +cmd \-a \-carg \-\- file file +.Ed +.It hash Fl rv Ar command ... +The shell maintains a hash table which remembers the +locations of commands. +With no arguments whatsoever, +the +.Ic hash +command prints out the contents of this table. +Entries which have not been looked at since the last +.Ic cd +command are marked with an asterisk; it is possible for these entries +to be invalid. +.Pp +With arguments, the +.Ic hash +command removes the specified commands from the hash table (unless +they are functions) and then locates them. +With the +.Fl v +option, hash prints the locations of the commands as it finds them. +The +.Fl r +option causes the hash command to delete all the entries in the hash table +except for functions. +.It inputrc Ar file +Read the +.Va file +to set keybindings as defined by +.Xr editrc 5 . +.It jobid Op Ar job +Print the process id's of the processes in the job. +If the +.Ar job +argument is omitted, the current job is used. +.It jobs +This command lists out all the background processes +which are children of the current shell process. +.It pwd Op Fl \&LP +Print the current directory. +If +.Fl L +is specified the cached value (initially set from +.Ev PWD ) +is checked to see if it refers to the current directory; if it does +the value is printed. +Otherwise the current directory name is found using +.Xr getcwd 3 . +The environment variable +.Ev PWD +is set to the printed value. +.Pp +The default is +.Ic pwd +.Fl L , +but note that the built-in +.Ic cd +command doesn't currently support the +.Fl L +option and will cache (almost) the absolute path. +If +.Ic cd +is changed, +.Ic pwd +may be changed to default to +.Ic pwd +.Fl P . +.Pp +If the current directory is renamed and replaced by a symlink to the +same directory, or the initial +.Ev PWD +value followed a symbolic link, then the cached value may not +be the absolute path. +.Pp +The built-in command may differ from the program of the same name because +the program will use +.Ev PWD +and the built-in uses a separately cached value. +.It read Oo Fl p Ar prompt Oc Oo Fl r Oc Ar variable Oo Ar ... Oc +The prompt is printed if the +.Fl p +option is specified and the standard input is a terminal. +Then a line is read from the standard input. +The trailing newline is deleted from the +line and the line is split as described in the +.Sx Word Expansions +section above, and the pieces are assigned to the variables in order. +If there are more pieces than variables, the remaining pieces +(along with the characters in +.Ev IFS +that separated them) are assigned to the last variable. +If there are more variables than pieces, +the remaining variables are assigned the null string. +The +.Ic read +built-in will indicate success unless EOF is encountered on input, in +which case failure is returned. +.Pp +By default, unless the +.Fl r +option is specified, the backslash +.Dq \e +acts as an escape character, causing the following character to be treated +literally. +If a backslash is followed by a newline, the backslash and the +newline will be deleted. +.It readonly Ar name ... +.It readonly Fl p +The specified names are marked as read only, so that they cannot be +subsequently modified or unset. +The shell allows the value of a variable +to be set at the same time it is marked read only by writing +.Pp +.Dl readonly name=value +.Pp +With no arguments the readonly command lists the names of all read only +variables. +With the +.Fl p +option specified the output will be formatted suitably for non-interactive use. +.Pp +.It set Oo { Fl options | Cm +options | Cm \-- } Oc Ar arg ... +The +.Ic set +command performs three different functions. +.Pp +With no arguments, it lists the values of all shell variables. +.Pp +If options are given, it sets the specified option +flags, or clears them as described in the +.Sx Argument List Processing +section. +.Pp +The third use of the set command is to set the values of the shell's +positional parameters to the specified arguments. +To change the positional +parameters without changing any options, use +.Dq -- +as the first argument to set. +If no arguments are present, the set command +will clear all the positional parameters (equivalent to executing +.Dq shift $# . ) +.It setvar Ar variable Ar value +Assigns value to variable. +(In general it is better to write +variable=value rather than using +.Ic setvar . +.Ic setvar +is intended to be used in +functions that assign values to variables whose names are passed as +parameters.) +.It shift Op Ar n +Shift the positional parameters n times. +A +.Ic shift +sets the value of +.Va $1 +to the value of +.Va $2 , +the value of +.Va $2 +to the value of +.Va $3 , +and so on, decreasing +the value of +.Va $# +by one. +If there are zero positional parameters, +.Ic shift +does nothing. +.It trap Oo Fl l Oc +.It trap Oo Ar action Oc Ar signal ... +Cause the shell to parse and execute action when any of the specified +signals are received. +The signals are specified by signal number or as the name of the signal. +If +.Ar signal +is +.Li 0 +or its equivalent, EXIT, +the action is executed when the shell exits. +.Ar action +may be null, which cause the specified signals to be ignored. +With +.Ar action +omitted or set to +.Sq - +the specified signals are set to their default action. +When the shell forks off a subshell, it resets trapped (but not ignored) +signals to the default action. +On non-interactive shells, the +.Ic trap +command has no effect on signals that were +ignored on entry to the shell. +On interactive shells, the +.Ic trap +command will catch or reset signals ignored on entry. +Issuing +.Ic trap +with option +.Ar -l +will print a list of valid signal names. +.Ic trap +without any arguments cause it to write a list of signals and their +associated action to the standard output in a format that is suitable +as an input to the shell that achieves the same trapping results. +.Pp +Examples: +.Pp +.Dl trap +.Pp +List trapped signals and their corresponding action +.Pp +.Dl trap -l +.Pp +Print a list of valid signals +.Pp +.Dl trap '' INT QUIT tstp 30 +.Pp +Ignore signals INT QUIT TSTP USR1 +.Pp +.Dl trap date INT +.Pp +Print date upon receiving signal INT +.It type Op Ar name ... +Interpret each name as a command and print the resolution of the command +search. +Possible resolutions are: +shell keyword, alias, shell built-in, +command, tracked alias and not found. +For aliases the alias expansion is +printed; for commands and tracked aliases the complete pathname of the +command is printed. +.It ulimit Oo Fl H \*(Ba Fl S Oc Oo Fl a \*(Ba Fl btfdscmlrpnv Oo Ar value Oc Oc +Inquire about or set the hard or soft limits on processes or set new +limits. +The choice between hard limit (which no process is allowed to +violate, and which may not be raised once it has been lowered) and soft +limit (which causes processes to be signaled but not necessarily killed, +and which may be raised) is made with these flags: +.Bl -tag -width Fl +.It Fl H +set or inquire about hard limits +.It Fl S +set or inquire about soft limits. +If neither +.Fl H +nor +.Fl S +is specified, the soft limit is displayed or both limits are set. +If both are specified, the last one wins. +.El +.Pp +The limit to be interrogated or set, then, is chosen by specifying +any one of these flags: +.Bl -tag -width Fl +.It Fl a +show all the current limits +.It Fl b +show or set the limit on the socket buffer size of a process (in bytes) +.It Fl t +show or set the limit on CPU time (in seconds) +.It Fl f +show or set the limit on the largest file that can be created +(in 512-byte blocks) +.It Fl d +show or set the limit on the data segment size of a process (in kilobytes) +.It Fl s +show or set the limit on the stack size of a process (in kilobytes) +.It Fl c +show or set the limit on the largest core dump size that can be produced +(in 512-byte blocks) +.It Fl m +show or set the limit on the total physical memory that can be +in use by a process (in kilobytes) +.It Fl l +show or set the limit on how much memory a process can lock with +.Xr mlock 2 +(in kilobytes) +.It Fl r +show or set the limit on the number of threads this user can +have at one time +.It Fl p +show or set the limit on the number of processes this user can +have at one time +.It Fl n +show or set the limit on the number of files a process can have open at once +.It Fl v +show or set the limit on how large a process address space can be +.El +.Pp +If none of these is specified, it is the limit on file size that is shown +or set. +If value is specified, the limit is set to that number; otherwise +the current limit is displayed. +.Pp +Limits of an arbitrary process can be displayed or set using the +.Xr sysctl 8 +utility. +.Pp +.It umask Op Ar mask +Set the value of umask (see +.Xr umask 2 ) +to the specified octal value. +If the argument is omitted, the umask value is printed. +.It unalias Oo Fl a Oc Oo Ar name Oc +If +.Ar name +is specified, the shell removes that alias. +If +.Fl a +is specified, all aliases are removed. +.It unset Ar name ... +The specified variables and functions are unset and unexported. +If a given name corresponds to both a variable and a function, both +the variable and the function are unset. +.It wait Op Ar job +Wait for the specified job to complete and return the exit status of the +last process in the job. +If the argument is omitted, wait for all jobs to +complete and then return an exit status of zero. +.El +.Ss Command Line Editing +When +.Nm +is being used interactively from a terminal, the current command +and the command history (see +.Ic fc +in the +.Sx Built-ins +section) +can be edited using emacs-mode or vi-mode command-line editing. +The command +.Ql set -o emacs +enables emacs-mode editing. +The command +.Ql set -o vi +enables vi-mode editing and places the current shell process into +.Ar vi +insert mode. +(See the +.Sx Argument List Processing +section above.) +.Pp +The +.Ar vi +mode uses commands similar to a subset of those described in the +.Xr vi 1 +man page. +With vi-mode +enabled, +.Nm sh +can be switched between insert mode and command mode. +It's similar to +.Xr vi 1 : +pressing the +.Aq ESC +key will throw you into command VI command mode. +Pressing the +.Aq return +key while in command mode will pass the line to the shell. +.Pp +The +.Ar emacs +mode uses commands similar to a subset available in +the +.Xr emacs 1 +editor. +With emacs-mode enabled, special keys can be used to modify the text +in the buffer using the control key. +.Pp +.Nm +uses the +.Xr editline 3 +library. +.Sh ENVIRONMENT +.Bl -tag -width MAILCHECK +.It Ev HOME +Set automatically by +.Xr login 1 +from the user's login directory in the password file +.Pq Xr passwd 5 . +This environment variable also functions as the default argument for the +.Ic cd +built-in. +.It Ev PATH +The default search path for executables. +See the +.Sx Path Search +section above. +.It Ev CDPATH +The search path used with the +.Ic cd +built-in. +.It Ev LINENO +The current line number in the script or function. +.It Ev LANG +The string used to specify localization information that allows users +to work with different culture-specific and language conventions. +See +.Xr nls 7 . +.It Ev MAIL +The name of a mail file, that will be checked for the arrival of new mail. +Overridden by +.Ev MAILPATH . +.It Ev MAILCHECK +The frequency in seconds that the shell checks for the arrival of mail +in the files specified by the +.Ev MAILPATH +or the +.Ev MAIL +file. +If set to 0, the check will occur at each prompt. +.It Ev MAILPATH +A colon +.Dq \&: +separated list of file names, for the shell to check for incoming mail. +This environment setting overrides the +.Ev MAIL +setting. +There is a maximum of 10 mailboxes that can be monitored at once. +.It Ev PS1 +The primary prompt string, which defaults to +.Dq $ \ , +unless you are the superuser, in which case it defaults to +.Dq # \ . +.It Ev PS2 +The secondary prompt string, which defaults to +.Dq \*[Gt] \ . +.It Ev PS4 +Output before each line when execution trace (set -x) is enabled, +defaults to +.Dq + \ . +.It Ev IFS +Input Field Separators. +This is normally set to +.Aq space , +.Aq tab , +and +.Aq newline . +See the +.Sx White Space Splitting +section for more details. +.It Ev TERM +The default terminal setting for the shell. +This is inherited by +children of the shell, and is used in the history editing modes. +.It Ev HISTSIZE +The number of lines in the history buffer for the shell. +.El +.Sh FILES +.Bl -item +.It +.Pa $HOME/.profile +.It +.Pa /etc/profile +.El +.Sh EXIT STATUS +Errors that are detected by the shell, such as a syntax error, will cause the +shell to exit with a non-zero exit status. +If the shell is not an +interactive shell, the execution of the shell file will be aborted. +Otherwise +the shell will return the exit status of the last command executed, or +if the exit built-in is used with a numeric argument, it will return the +argument. +.Sh SEE ALSO +.Xr csh 1 , +.Xr echo 1 , +.Xr getopt 1 , +.Xr ksh 1 , +.Xr login 1 , +.Xr printf 1 , +.Xr test 1 , +.Xr editline 3 , +.Xr getopt 3 , +.\" .Xr profile 4 , +.Xr editrc 5 , +.Xr passwd 5 , +.Xr environ 7 , +.Xr nls 7 , +.Xr sysctl 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . +It was, however, unmaintainable so we wrote this one. +.Sh BUGS +Setuid shell scripts should be avoided at all costs, as they are a +significant security risk. +.Pp +PS1, PS2, and PS4 should be subject to parameter expansion before +being displayed. +.Pp +The characters generated by filename completion should probably be quoted +to ensure that the filename is still valid after the input line has been +processed. diff --git a/minix/commands/ash/shell.h b/bin/sh/shell.h similarity index 66% rename from minix/commands/ash/shell.h rename to bin/sh/shell.h index 9d76acf0d..4df721a80 100644 --- a/minix/commands/ash/shell.h +++ b/bin/sh/shell.h @@ -1,3 +1,5 @@ +/* $NetBSD: shell.h,v 1.18 2013/04/28 17:01:28 dholland Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,57 +32,58 @@ * SUCH DAMAGE. * * @(#)shell.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/shell.h,v 1.17 2004/04/06 20:06:51 markm Exp $ */ -#include /* for mode_t */ - /* * The follow should be set to reflect the type of system you have: * JOBS -> 1 if you have Berkeley job control, 0 otherwise. - * TILDE -> 1 if you want the shell to expand ~logname. - * USEGETPW -> 1 if getpwnam() must be used to look up a name. - * READLINE -> 1 if line editing by readline() should be enabled. + * SHORTNAMES -> 1 if your linker cannot handle long names. * define BSD if you are running 4.2 BSD or later. * define SYSV if you are running under System V. - * define DEBUG=1 to compile in debugging (set global "debug" to turn on) + * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) * define DEBUG=2 to compile in and turn on debugging. + * define DO_SHAREDVFORK to indicate that vfork(2) shares its address + * with its parent. * - * When debugging is on, debugging info will be written to $HOME/trace and + * When debugging is on, debugging info will be written to ./trace and * a quit signal will generate a core dump. */ -#ifndef JOBS -#define JOBS 1 -#endif +#include + +#if defined(__minix) +#define JOBS 0 +#else +#define JOBS 1 +#endif /* defined(__minix) */ #ifndef BSD #define BSD 1 #endif -#ifndef DEBUG -#define DEBUG 0 -#endif -#define POSIX 1 -/* - * Type of used arithmetics. SUSv3 requires us to have at least signed long. - */ -typedef long arith_t; -#define ARITH_FORMAT_STR "%ld" -#define atoarith_t(arg) strtol(arg, NULL, 0) -#define strtoarith_t(nptr, endptr, base) strtol(nptr, endptr, base) +#ifndef DO_SHAREDVFORK +#if !defined(__minix) +#if __NetBSD_Version__ >= 104000000 +#define DO_SHAREDVFORK +#endif +#endif /* !defined(__minix) */ +#endif typedef void *pointer; -#define STATIC static -#define MKINIT /* empty */ +#ifndef NULL +#define NULL (void *)0 +#endif +#define STATIC /* empty */ +#define MKINIT /* empty */ -extern char nullstr[1]; /* null string */ +#include -#if DEBUG -#define TRACE(param) sh_trace param +extern const char nullstr[1]; /* null string */ + + +#ifdef DEBUG +#define TRACE(param) trace param +#define TRACEV(param) tracev param #else #define TRACE(param) +#define TRACEV(param) #endif - -/* - * $PchId: shell.h,v 1.7 2006/05/22 12:47:00 philip Exp $ - */ diff --git a/minix/commands/ash/show.c b/bin/sh/show.c similarity index 83% rename from minix/commands/ash/show.c rename to bin/sh/show.c index 1b848927e..9c008715d 100644 --- a/minix/commands/ash/show.c +++ b/bin/sh/show.c @@ -1,3 +1,5 @@ +/* $NetBSD: show.c,v 1.28 2011/08/23 10:01:32 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,39 +32,37 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: show.c,v 1.28 2011/08/23 10:01:32 christos Exp $"); #endif #endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/show.c,v 1.21 2004/04/06 20:06:51 markm Exp $"); -*/ -#include #include -#include #include -#include +#include +#include #include "shell.h" #include "parser.h" #include "nodes.h" #include "mystring.h" #include "show.h" +#include "options.h" -#if DEBUG -static void trputc(int c); + +#ifdef DEBUG static void shtree(union node *, int, char *, FILE*); static void shcmd(union node *, FILE *); static void sharg(union node *, FILE *); static void indent(int, char *, FILE *); static void trstring(char *); -static void showtree(union node *n); -static void +void showtree(union node *n) { trputs("showtree called\n"); @@ -74,7 +74,7 @@ static void shtree(union node *n, int ind, char *pfx, FILE *fp) { struct nodelist *lp; - char *s; + const char *s; if (n == NULL) return; @@ -126,7 +126,7 @@ shcmd(union node *cmd, FILE *fp) { union node *np; int first; - char *s; + const char *s; int dftfd; first = 1; @@ -141,22 +141,19 @@ shcmd(union node *cmd, FILE *fp) putchar(' '); switch (np->nfile.type) { case NTO: s = ">"; dftfd = 1; break; + case NCLOBBER: s = ">|"; dftfd = 1; break; case NAPPEND: s = ">>"; dftfd = 1; break; case NTOFD: s = ">&"; dftfd = 1; break; - case NCLOBBER: s = ">|"; dftfd = 1; break; case NFROM: s = "<"; dftfd = 0; break; - case NFROMTO: s = "<>"; dftfd = 0; break; case NFROMFD: s = "<&"; dftfd = 0; break; + case NFROMTO: s = "<>"; dftfd = 0; break; default: s = "*error*"; dftfd = 0; break; } if (np->nfile.fd != dftfd) fprintf(fp, "%d", np->nfile.fd); fputs(s, fp); if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { - if (np->ndup.dupfd >= 0) - fprintf(fp, "%d", np->ndup.dupfd); - else - fprintf(fp, "-"); + fprintf(fp, "%d", np->ndup.dupfd); } else { sharg(np->nfile.fname, fp); } @@ -175,7 +172,6 @@ sharg(union node *arg, FILE *fp) if (arg->type != NARG) { printf("\n", arg->type); - fflush(stdout); abort(); } bqlist = arg->narg.backquote; @@ -262,6 +258,8 @@ indent(int amount, char *pfx, FILE *fp) putc('\t', fp); } } +#endif + /* @@ -271,46 +269,52 @@ indent(int amount, char *pfx, FILE *fp) FILE *tracefile; -#if DEBUG == 2 -int debug = 1; -#else -int debug = 0; -#endif - -static void +#ifdef DEBUG +void trputc(int c) { - if (tracefile == NULL) + if (debug != 1 || !tracefile) return; putc(c, tracefile); - if (c == '\n') - fflush(tracefile); } - +#endif void -sh_trace(const char *fmt, ...) +trace(const char *fmt, ...) { +#ifdef DEBUG va_list va; + + if (debug != 1 || !tracefile) + return; va_start(va, fmt); - if (tracefile != NULL) { - (void) vfprintf(tracefile, fmt, va); - if (strchr(fmt, '\n')) - (void) fflush(tracefile); - } + (void) vfprintf(tracefile, fmt, va); va_end(va); +#endif +} + +void +tracev(const char *fmt, va_list va) +{ +#ifdef DEBUG + va_list ap; + if (debug != 1 || !tracefile) + return; + va_copy(ap, va); + (void) vfprintf(tracefile, fmt, ap); + va_end(ap); +#endif } +#ifdef DEBUG void -trputs(char *s) +trputs(const char *s) { - if (tracefile == NULL) + if (debug != 1 || !tracefile) return; fputs(s, tracefile); - if (strchr(s, '\n')) - fflush(tracefile); } @@ -320,7 +324,7 @@ trstring(char *s) char *p; char c; - if (tracefile == NULL) + if (debug != 1 || !tracefile) return; putc('"', tracefile); for (p = s ; *p ; p++) { @@ -352,12 +356,14 @@ backslash: putc('\\', tracefile); } putc('"', tracefile); } +#endif void trargs(char **ap) { - if (tracefile == NULL) +#ifdef DEBUG + if (debug != 1 || !tracefile) return; while (*ap) { trstring(*ap++); @@ -366,18 +372,25 @@ trargs(char **ap) else putc('\n', tracefile); } - fflush(tracefile); +#endif } +#ifdef DEBUG void opentrace(void) { char s[100]; +#ifdef O_APPEND int flags; +#endif - if (!debug) + if (debug != 1) { + if (tracefile) + fflush(tracefile); + /* leave open because libedit might be using it */ return; + } #ifdef not_this_way { char *p; @@ -391,19 +404,27 @@ opentrace(void) strcat(s, "/trace"); } #else - scopy("./trace", s); + snprintf(s, sizeof(s), "./trace.%d", (int)getpid()); #endif /* not_this_way */ - if ((tracefile = fopen(s, "a")) == NULL) { - fprintf(stderr, "Can't open %s: %s\n", s, strerror(errno)); - return; + if (tracefile) { + if (!freopen(s, "a", tracefile)) { + fprintf(stderr, "Can't re-open %s\n", s); + tracefile = NULL; + debug = 0; + return; + } + } else { + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + debug = 0; + return; + } } +#ifdef O_APPEND if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif + setlinebuf(tracefile); fputs("\nTracing started.\n", tracefile); - fflush(tracefile); } #endif /* DEBUG */ - -/* - * $PchId: show.c,v 1.6 2006/05/22 12:27:51 philip Exp $ - */ diff --git a/minix/commands/ash/show.h b/bin/sh/show.h similarity index 84% rename from minix/commands/ash/show.h rename to bin/sh/show.h index 59cb3e752..3152ff277 100644 --- a/minix/commands/ash/show.h +++ b/bin/sh/show.h @@ -1,3 +1,5 @@ +/* $NetBSD: show.h,v 1.7 2003/08/07 09:05:38 agc Exp $ */ + /*- * Copyright (c) 1995 * The Regents of the University of California. All rights reserved. @@ -10,7 +12,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -27,16 +29,17 @@ * SUCH DAMAGE. * * @(#)show.h 1.1 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/show.h,v 1.11 2004/04/06 20:06:51 markm Exp $ */ -#ifdef DEBUG -void sh_trace(const char *, ...); +#include + +union node; +void showtree(union node *); +void trace(const char *, ...); +void tracev(const char *, va_list); void trargs(char **); -void trputs(char *); +#ifdef DEBUG +void trputc(int); +void trputs(const char *); void opentrace(void); #endif - -/* - * $PchId: show.h,v 1.4 2006/03/29 13:28:45 philip Exp $ - */ diff --git a/bin/sh/syntax.c b/bin/sh/syntax.c new file mode 100644 index 000000000..e0647294f --- /dev/null +++ b/bin/sh/syntax.c @@ -0,0 +1,105 @@ +/* $NetBSD: syntax.c,v 1.3 2012/03/28 20:11:25 christos Exp $ */ + +#include +__RCSID("$NetBSD: syntax.c,v 1.3 2012/03/28 20:11:25 christos Exp $"); + +#include +#include "shell.h" +#include "syntax.h" +#include "parser.h" + +#if CWORD != 0 +#error initialisation assumes 'CWORD' is zero +#endif + +#define ndx(ch) (ch + 1 - CHAR_MIN) +#define set(ch, val) [ndx(ch)] = val, +#define set_range(s, e, val) [ndx(s) ... ndx(e)] = val, + +/* syntax table used when not in quotes */ +const char basesyntax[257] = { CEOF, + set_range(CTL_FIRST, CTL_LAST, CCTL) + set('\n', CNL) + set('\\', CBACK) + set('\'', CSQUOTE) + set('"', CDQUOTE) + set('`', CBQUOTE) + set('$', CVAR) + set('}', CENDVAR) + set('<', CSPCL) + set('>', CSPCL) + set('(', CSPCL) + set(')', CSPCL) + set(';', CSPCL) + set('&', CSPCL) + set('|', CSPCL) + set(' ', CSPCL) + set('\t', CSPCL) +}; + +/* syntax table used when in double quotes */ +const char dqsyntax[257] = { CEOF, + set_range(CTL_FIRST, CTL_LAST, CCTL) + set('\n', CNL) + set('\\', CBACK) + set('"', CDQUOTE) + set('`', CBQUOTE) + set('$', CVAR) + set('}', CENDVAR) + /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ + set('!', CCTL) + set('*', CCTL) + set('?', CCTL) + set('[', CCTL) + set('=', CCTL) + set('~', CCTL) + set(':', CCTL) + set('/', CCTL) + set('-', CCTL) +}; + +/* syntax table used when in single quotes */ +const char sqsyntax[257] = { CEOF, + set_range(CTL_FIRST, CTL_LAST, CCTL) + set('\n', CNL) + set('\'', CSQUOTE) + /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ + set('!', CCTL) + set('*', CCTL) + set('?', CCTL) + set('[', CCTL) + set('=', CCTL) + set('~', CCTL) + set(':', CCTL) + set('/', CCTL) + set('-', CCTL) +}; + +/* syntax table used when in arithmetic */ +const char arisyntax[257] = { CEOF, + set_range(CTL_FIRST, CTL_LAST, CCTL) + set('\n', CNL) + set('\\', CBACK) + set('`', CBQUOTE) + set('\'', CSQUOTE) + set('"', CDQUOTE) + set('$', CVAR) + set('}', CENDVAR) + set('(', CLP) + set(')', CRP) +}; + +/* character classification table */ +const char is_type[257] = { 0, + set_range('0', '9', ISDIGIT) + set_range('a', 'z', ISLOWER) + set_range('A', 'Z', ISUPPER) + set('_', ISUNDER) + set('#', ISSPECL) + set('?', ISSPECL) + set('$', ISSPECL) + set('!', ISSPECL) + set('-', ISSPECL) + set('*', ISSPECL) + set('@', ISSPECL) +}; diff --git a/bin/sh/syntax.h b/bin/sh/syntax.h new file mode 100644 index 000000000..89a32dcdc --- /dev/null +++ b/bin/sh/syntax.h @@ -0,0 +1,83 @@ +/* $NetBSD: syntax.h,v 1.2 2004/01/17 17:38:12 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +/* Syntax classes */ +#define CWORD 0 /* character is nothing special */ +#define CNL 1 /* newline character */ +#define CBACK 2 /* a backslash character */ +#define CSQUOTE 3 /* single quote */ +#define CDQUOTE 4 /* double quote */ +#define CBQUOTE 5 /* backwards single quote */ +#define CVAR 6 /* a dollar sign */ +#define CENDVAR 7 /* a '}' character */ +#define CLP 8 /* a left paren in arithmetic */ +#define CRP 9 /* a right paren in arithmetic */ +#define CEOF 10 /* end of file */ +#define CCTL 11 /* like CWORD, except it must be escaped */ +#define CSPCL 12 /* these terminate a word */ + +/* Syntax classes for is_ functions */ +#define ISDIGIT 01 /* a digit */ +#define ISUPPER 02 /* an upper case letter */ +#define ISLOWER 04 /* a lower case letter */ +#define ISUNDER 010 /* an underscore */ +#define ISSPECL 020 /* the name of a special parameter */ + +#define PEOF (CHAR_MIN - 1) +#define SYNBASE (-PEOF) +/* XXX UPEOF is CHAR_MAX, so is a valid 'char' value... */ +#define UPEOF ((char)PEOF) + + +#define BASESYNTAX (basesyntax + SYNBASE) +#define DQSYNTAX (dqsyntax + SYNBASE) +#define SQSYNTAX (sqsyntax + SYNBASE) +#define ARISYNTAX (arisyntax + SYNBASE) + +/* These defines assume that the digits are contiguous */ +#define is_digit(c) ((unsigned)((c) - '0') <= 9) +#define is_alpha(c) (((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && isalpha((unsigned char)(c))) +#define is_name(c) (((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalpha((unsigned char)(c)))) +#define is_in_name(c) (((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalnum((unsigned char)(c)))) +#define is_special(c) ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT)) +#define digit_val(c) ((c) - '0') + +extern const char basesyntax[]; +extern const char dqsyntax[]; +extern const char sqsyntax[]; +extern const char arisyntax[]; +extern const char is_type[]; diff --git a/bin/sh/trap.c b/bin/sh/trap.c new file mode 100644 index 000000000..d48b94be0 --- /dev/null +++ b/bin/sh/trap.c @@ -0,0 +1,475 @@ +/* $NetBSD: trap.c,v 1.35 2011/06/18 21:18:46 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; +#else +__RCSID("$NetBSD: trap.c,v 1.35 2011/06/18 21:18:46 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include "shell.h" +#include "main.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" +#include "jobs.h" +#include "show.h" +#include "options.h" +#include "builtins.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "trap.h" +#include "mystring.h" +#include "var.h" + + +/* + * Sigmode records the current value of the signal handlers for the various + * modes. A value of zero means that the current handler is not known. + * S_HARD_IGN indicates that the signal was ignored on entry to the shell, + */ + +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ + + +char *trap[NSIG+1]; /* trap handler commands */ +MKINIT char sigmode[NSIG]; /* current value of signal */ +volatile char gotsig[NSIG]; /* indicates specified signal received */ +int pendingsigs; /* indicates some signal received */ + +static int getsigaction(int, sig_t *); + +/* + * return the signal number described by `p' (as a number or a name) + * or -1 if it isn't one + */ + +static int +signame_to_signum(const char *p) +{ + int i; + + if (is_number(p)) + return number(p); + + if (strcasecmp(p, "exit") == 0 ) + return 0; + + if (strncasecmp(p, "sig", 3) == 0) + p += 3; + + for (i = 0; i < NSIG; ++i) + if (strcasecmp (p, sys_signame[i]) == 0) + return i; + return -1; +} + +/* + * Print a list of valid signal names + */ +static void +printsignals(void) +{ + int n; + + out1str("EXIT "); + + for (n = 1; n < NSIG; n++) { + out1fmt("%s", sys_signame[n]); + if ((n == NSIG/2) || n == (NSIG - 1)) + out1str("\n"); + else + out1c(' '); + } +} + +/* + * The trap builtin. + */ + +int +trapcmd(int argc, char **argv) +{ + char *action; + char **ap; + int signo; + + if (argc <= 1) { + for (signo = 0 ; signo <= NSIG ; signo++) + if (trap[signo] != NULL) { + out1fmt("trap -- "); + print_quoted(trap[signo]); + out1fmt(" %s\n", + (signo) ? sys_signame[signo] : "EXIT"); + } + return 0; + } + ap = argv + 1; + + action = NULL; + + if (strcmp(*ap, "--") == 0) + if (*++ap == NULL) + return 0; + + if (signame_to_signum(*ap) == -1) { + if ((*ap)[0] == '-') { + if ((*ap)[1] == '\0') + ap++; + else if ((*ap)[1] == 'l' && (*ap)[2] == '\0') { + printsignals(); + return 0; + } + else + error("bad option %s\n", *ap); + } + else + action = *ap++; + } + + while (*ap) { + if (is_number(*ap)) + signo = number(*ap); + else + signo = signame_to_signum(*ap); + + if (signo < 0 || signo > NSIG) + error("%s: bad trap", *ap); + + INTOFF; + if (action) + action = savestr(action); + + if (trap[signo]) + ckfree(trap[signo]); + + trap[signo] = action; + + if (signo != 0) + setsignal(signo, 0); + INTON; + ap++; + } + return 0; +} + + + +/* + * Clear traps on a fork or vfork. + * Takes one arg vfork, to tell it to not be destructive of + * the parents variables. + */ + +void +clear_traps(int vforked) +{ + char **tp; + + for (tp = trap ; tp <= &trap[NSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + if (!vforked) { + ckfree(*tp); + *tp = NULL; + } + if (tp != &trap[0]) + setsignal(tp - trap, vforked); + INTON; + } + } +} + + + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ + +sig_t +setsignal(int signo, int vforked) +{ + int action; + sig_t sigact = SIG_DFL, sig; + char *t, tsig; + + if ((t = trap[signo]) == NULL) + action = S_DFL; + else if (*t != '\0') + action = S_CATCH; + else + action = S_IGN; + if (rootshell && !vforked && action == S_DFL) { + switch (signo) { + case SIGINT: + if (iflag || minusc || sflag == 0) + action = S_CATCH; + break; + case SIGQUIT: +#ifdef DEBUG + if (debug) + break; +#endif + /* FALLTHROUGH */ + case SIGTERM: + if (iflag) + action = S_IGN; + break; +#if JOBS + case SIGTSTP: + case SIGTTOU: + if (mflag) + action = S_IGN; + break; +#endif + } + } + + t = &sigmode[signo - 1]; + tsig = *t; + if (tsig == 0) { + /* + * current setting unknown + */ + if (!getsigaction(signo, &sigact)) { + /* + * Pretend it worked; maybe we should give a warning + * here, but other shells don't. We don't alter + * sigmode, so that we retry every time. + */ + return 0; + } + if (sigact == SIG_IGN) { + /* + * POSIX 3.14.13 states that non-interactive shells + * should ignore trap commands for signals that were + * ignored upon entry, and leaves the behavior + * unspecified for interactive shells. On interactive + * shells, or if job control is on, and we have a job + * control related signal, we allow the trap to work. + * + * This change allows us to be POSIX compliant, and + * at the same time override the default behavior if + * we need to by setting the interactive flag. + */ + if ((mflag && (signo == SIGTSTP || + signo == SIGTTIN || signo == SIGTTOU)) || iflag) { + tsig = S_IGN; + } else + tsig = S_HARD_IGN; + } else { + tsig = S_RESET; /* force to be set */ + } + } + if (tsig == S_HARD_IGN || tsig == action) + return 0; + switch (action) { + case S_DFL: sigact = SIG_DFL; break; + case S_CATCH: sigact = onsig; break; + case S_IGN: sigact = SIG_IGN; break; + } + sig = signal(signo, sigact); + if (sig != SIG_ERR) { + sigset_t ss; + if (!vforked) + *t = action; + if (action == S_CATCH) + (void)siginterrupt(signo, 1); + /* + * If our parent accidentally blocked signals for + * us make sure we unblock them + */ + (void)sigemptyset(&ss); + (void)sigaddset(&ss, signo); + (void)sigprocmask(SIG_UNBLOCK, &ss, NULL); + } + return sig; +} + +/* + * Return the current setting for sig w/o changing it. + */ +static int +getsigaction(int signo, sig_t *sigact) +{ + struct sigaction sa; + + if (sigaction(signo, (struct sigaction *)0, &sa) == -1) + return 0; + *sigact = (sig_t) sa.sa_handler; + return 1; +} + +/* + * Ignore a signal. + */ + +void +ignoresig(int signo, int vforked) +{ + if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { + signal(signo, SIG_IGN); + } + if (!vforked) + sigmode[signo - 1] = S_HARD_IGN; +} + + +#ifdef mkinit +INCLUDE +INCLUDE "trap.h" + +SHELLPROC { + char *sm; + + clear_traps(0); + for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } +} +#endif + + + +/* + * Signal handler. + */ + +void +onsig(int signo) +{ + signal(signo, onsig); + if (signo == SIGINT && trap[SIGINT] == NULL) { + onint(); + return; + } + gotsig[signo - 1] = 1; + pendingsigs++; +} + + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ + +void +dotrap(void) +{ + int i; + int savestatus; + + for (;;) { + for (i = 1 ; ; i++) { + if (gotsig[i - 1]) + break; + if (i >= NSIG) + goto done; + } + gotsig[i - 1] = 0; + savestatus=exitstatus; + evalstring(trap[i], 0); + exitstatus=savestatus; + } +done: + pendingsigs = 0; +} + + + +/* + * Controls whether the shell is interactive or not. + */ + + +void +setinteractive(int on) +{ + static int is_interactive; + + if (on == is_interactive) + return; + setsignal(SIGINT, 0); + setsignal(SIGQUIT, 0); + setsignal(SIGTERM, 0); + is_interactive = on; +} + + + +/* + * Called to exit the shell. + */ + +void +exitshell(int status) +{ + struct jmploc loc1, loc2; + char *p; + + TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); + if (setjmp(loc1.loc)) { + goto l1; + } + if (setjmp(loc2.loc)) { + goto l2; + } + handler = &loc1; + if ((p = trap[0]) != NULL && *p != '\0') { + trap[0] = NULL; + evalstring(p, 0); + } +l1: handler = &loc2; /* probably unnecessary */ + flushall(); +#if JOBS + setjobctl(0); +#endif +l2: _exit(status); + /* NOTREACHED */ +} diff --git a/minix/commands/ash/trap.h b/bin/sh/trap.h similarity index 80% rename from minix/commands/ash/trap.h rename to bin/sh/trap.h index a61672ab6..0a1d69875 100644 --- a/minix/commands/ash/trap.h +++ b/bin/sh/trap.h @@ -1,3 +1,5 @@ +/* $NetBSD: trap.h,v 1.20 2012/03/15 02:02:20 joerg Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,23 +32,14 @@ * SUCH DAMAGE. * * @(#)trap.h 8.3 (Berkeley) 6/5/95 - * $FreeBSD: src/bin/sh/trap.h,v 1.12 2004/04/06 20:06:51 markm Exp $ */ extern int pendingsigs; -extern int in_dotrap; -extern int is_interactive; -extern volatile sig_atomic_t gotwinch; -int trapcmd(int, char **); -void clear_traps(void); -void setsignal(int); -void ignoresig(int); +void clear_traps(int); +sig_t setsignal(int, int); +void ignoresig(int, int); +void onsig(int); void dotrap(void); void setinteractive(int); -void exitshell(int); -char *strsiglist(int); - -/* - * $PchId: trap.h,v 1.6 2006/05/22 12:48:30 philip Exp $ - */ +void exitshell(int) __dead; diff --git a/minix/commands/ash/var.c b/bin/sh/var.c similarity index 60% rename from minix/commands/ash/var.c rename to bin/sh/var.c index 061d3a5a0..d2ff93a15 100644 --- a/minix/commands/ash/var.c +++ b/bin/sh/var.c @@ -1,3 +1,5 @@ +/* $NetBSD: var.c,v 1.43 2013/11/01 16:49:02 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,28 +32,25 @@ * SUCH DAMAGE. */ +#include #ifndef lint #if 0 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: var.c,v 1.43 2013/11/01 16:49:02 christos Exp $"); #endif #endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/var.c,v 1.26.2.1 2004/09/30 04:41:55 des Exp $"); -*/ #include #include -#ifndef NO_PATHS_H +#include #include -#endif +#include /* * Shell variables. */ -#include - #include "shell.h" #include "output.h" #include "expand.h" @@ -60,49 +59,57 @@ __FBSDID("$FreeBSD: src/bin/sh/var.c,v 1.26.2.1 2004/09/30 04:41:55 des Exp $"); #include "exec.h" #include "syntax.h" #include "options.h" +#include "builtins.h" #include "mail.h" #include "var.h" #include "memalloc.h" #include "error.h" #include "mystring.h" #include "parser.h" -#if !defined(NO_HISTORY) +#include "show.h" +#ifndef SMALL #include "myhistedit.h" #endif -#include "builtins.h" - -#ifndef _PATH_DEFPATH -#define _PATH_DEFPATH "/usr/bin:/bin" -#endif - +#ifdef SMALL #define VTABSIZE 39 +#else +#define VTABSIZE 517 +#endif struct varinit { struct var *var; int flags; - char *text; + const char *text; void (*func)(const char *); }; +struct localvar *localvars; -#ifndef NO_HISTORY +#if ATTY +struct var vatty; +#endif +#ifndef SMALL struct var vhistsize; +struct var vterm; #endif struct var vifs; struct var vmail; struct var vmpath; struct var vpath; -struct var vppid; struct var vps1; struct var vps2; -struct var vpse; +struct var vps4; struct var vvers; -STATIC struct var voptind; +struct var voptind; -STATIC const struct varinit varinit[] = { -#if !defined(NO_HISTORY) +const struct varinit varinit[] = { +#if ATTY + { &vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=", + NULL }, +#endif +#ifndef SMALL { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", sethistsize }, #endif @@ -114,26 +121,27 @@ STATIC const struct varinit varinit[] = { NULL }, { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH, changepath }, - { &vppid, VSTRFIXED|VTEXTFIXED|VUNSET, "PPID=", - NULL }, /* * vps1 depends on uid */ { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL }, - { &vpse, VSTRFIXED|VTEXTFIXED|VUNSET, "PSE=", + { &vps4, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL }, - { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", +#ifndef SMALL + { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=", + setterm }, +#endif + { &voptind, VSTRFIXED|VTEXTFIXED|VNOFUNC, "OPTIND=1", getoptsreset }, { NULL, 0, NULL, NULL } }; -STATIC struct var *vartab[VTABSIZE]; +struct var *vartab[VTABSIZE]; -STATIC struct var **hashvar(char *); -STATIC int varequal(char *, char *); -STATIC int localevar(char *); +STATIC int strequal(const char *, const char *); +STATIC struct var *find_var(const char *, struct var ***, int *); /* * Initialize the varable symbol tables and import the environment @@ -141,9 +149,9 @@ STATIC int localevar(char *); #ifdef mkinit INCLUDE "var.h" +MKINIT char **environ; INIT { char **envp; - extern char **environ; initvar(); for (envp = environ ; *envp ; envp++) { @@ -163,35 +171,28 @@ INIT { void initvar(void) { - char ppid[20]; const struct varinit *ip; struct var *vp; struct var **vpp; for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { - if ((vp->flags & VEXPORT) == 0) { - vpp = hashvar(ip->text); - vp->next = *vpp; - *vpp = vp; - vp->text = ip->text; - vp->flags = ip->flags; - vp->func = ip->func; - } + if (find_var(ip->text, &vpp, &vp->name_len) != NULL) + continue; + vp->next = *vpp; + *vpp = vp; + vp->text = strdup(ip->text); + vp->flags = ip->flags; + vp->func = ip->func; } /* * PS1 depends on uid */ - if ((vps1.flags & VEXPORT) == 0) { - vpp = hashvar("PS1="); + if (find_var("PS1", &vpp, &vps1.name_len) == NULL) { vps1.next = *vpp; *vpp = &vps1; - vps1.text = geteuid() ? "PS1=$ " : "PS1=# "; + vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# "); vps1.flags = VSTRFIXED|VTEXTFIXED; } - if ((vppid.flags & VEXPORT) == 0) { - fmtstr(ppid, sizeof(ppid), "%d", (int)getppid()); - setvarsafe("PPID", ppid, 0); - } } /* @@ -199,15 +200,11 @@ initvar(void) */ int -setvarsafe(char *name, char *val, int flags) +setvarsafe(const char *name, const char *val, int flags) { struct jmploc jmploc; struct jmploc *volatile savehandler = handler; - int err = 0; -#if __GNUC__ - /* Avoid longjmp clobbering */ - (void) &err; -#endif + int volatile err = 0; if (setjmp(jmploc.loc)) err = 1; @@ -220,14 +217,16 @@ setvarsafe(char *name, char *val, int flags) } /* - * Set the value of a variable. The flags argument is tored with the + * Set the value of a variable. The flags argument is ored with the * flags of the variable. If val is NULL, the variable is unset. */ void -setvar(char *name, char *val, int flags) +setvar(const char *name, const char *val, int flags) { - char *p, *q; + const char *p; + const char *q; + char *d; int len; int namelen; char *nameeq; @@ -255,37 +254,18 @@ setvar(char *name, char *val, int flags) } else { len += strlen(val); } - p = nameeq = ckmalloc(len); + d = nameeq = ckmalloc(len); q = name; while (--namelen >= 0) - *p++ = *q++; - *p++ = '='; - *p = '\0'; + *d++ = *q++; + *d++ = '='; + *d = '\0'; if (val) - scopy(val, p); + scopy(val, d); setvareq(nameeq, flags); } -STATIC int -localevar(char *s) -{ - static char *lnames[7] = { - "ALL", "COLLATE", "CTYPE", "MONETARY", - "NUMERIC", "TIME", NULL - }; - char **ss; - if (*s != 'L') - return 0; - if (varequal(s + 1, "ANG")) - return 1; - if (strncmp(s + 1, "C_", 2) != 0) - return 0; - for (ss = lnames; *ss ; ss++) - if (varequal(s + 3, *ss)) - return 1; - return 0; -} /* * Same as setvar except that the variable and value are passed in @@ -298,56 +278,47 @@ void setvareq(char *s, int flags) { struct var *vp, **vpp; - int len; + int nlen; if (aflag) flags |= VEXPORT; - vpp = hashvar(s); - for (vp = *vpp ; vp ; vp = vp->next) { - if (varequal(s, vp->text)) { - if (vp->flags & VREADONLY) { - len = strchr(s, '=') - s; - error("%.*s: is read only", len, s); - } - INTOFF; - - if (vp->func && (flags & VNOFUNC) == 0) - (*vp->func)(strchr(s, '=') + 1); - - if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) - ckfree(vp->text); - - vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); - vp->flags |= flags; - vp->text = s; - - /* - * We could roll this to a function, to handle it as - * a regular variable function callback, but why bother? - */ - if (vp == &vmpath || (vp == &vmail && ! mpathset())) - chkmail(1); - if ((vp->flags & VEXPORT) && localevar(s)) { - putenv(s); - (void) setlocale(LC_ALL, ""); - } - INTON; + vp = find_var(s, &vpp, &nlen); + if (vp != NULL) { + if (vp->flags & VREADONLY) + error("%.*s: is read only", vp->name_len, s); + if (flags & VNOSET) return; - } + INTOFF; + + if (vp->func && (flags & VNOFUNC) == 0) + (*vp->func)(s + vp->name_len + 1); + + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + + vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags & ~VNOFUNC; + vp->text = s; + + /* + * We could roll this to a function, to handle it as + * a regular variable function callback, but why bother? + */ + if (vp == &vmpath || (vp == &vmail && ! mpathset())) + chkmail(1); + INTON; + return; } /* not found */ + if (flags & VNOSET) + return; vp = ckmalloc(sizeof (*vp)); - vp->flags = flags; + vp->flags = flags & ~VNOFUNC; vp->text = s; + vp->name_len = nlen; vp->next = *vpp; vp->func = NULL; - INTOFF; *vpp = vp; - if ((vp->flags & VEXPORT) && localevar(s)) { - putenv(s); - (void) setlocale(LC_ALL, ""); - } - INTON; } @@ -357,17 +328,25 @@ setvareq(char *s, int flags) */ void -listsetvar(struct strlist *list) +listsetvar(struct strlist *list, int flags) { struct strlist *lp; INTOFF; for (lp = list ; lp ; lp = lp->next) { - setvareq(savestr(lp->text), 0); + setvareq(savestr(lp->text), flags); } INTON; } +void +listmklocal(struct strlist *list, int flags) +{ + struct strlist *lp; + + for (lp = list ; lp ; lp = lp->next) + mklocal(lp->text, flags); +} /* @@ -375,18 +354,14 @@ listsetvar(struct strlist *list) */ char * -lookupvar(char *name) +lookupvar(const char *name) { struct var *v; - for (v = *hashvar(name) ; v ; v = v->next) { - if (varequal(v->text, name)) { - if (v->flags & VUNSET) - return NULL; - return strchr(v->text, '=') + 1; - } - } - return NULL; + v = find_var(name, NULL, NULL); + if (v == NULL || v->flags & VUNSET) + return NULL; + return v->text + v->name_len + 1; } @@ -398,24 +373,21 @@ lookupvar(char *name) */ char * -bltinlookup(char *name, int doall) +bltinlookup(const char *name, int doall) { struct strlist *sp; struct var *v; for (sp = cmdenviron ; sp ; sp = sp->next) { - if (varequal(sp->text, name)) + if (strequal(sp->text, name)) return strchr(sp->text, '=') + 1; } - for (v = *hashvar(name) ; v ; v = v->next) { - if (varequal(v->text, name)) { - if ((v->flags & VUNSET) - || (!doall && (v->flags & VEXPORT) == 0)) - return NULL; - return strchr(v->text, '=') + 1; - } - } - return NULL; + + v = find_var(name, NULL, NULL); + + if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT))) + return NULL; + return v->text + v->name_len + 1; } @@ -431,7 +403,8 @@ environment(void) int nenv; struct var **vpp; struct var *vp; - char **env, **ep; + char **env; + char **ep; nenv = 0; for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { @@ -457,6 +430,8 @@ environment(void) */ #ifdef mkinit +void shprocvar(void); + SHELLPROC { shprocvar(); } @@ -496,24 +471,94 @@ shprocvar(void) * any variables. */ +void +print_quoted(const char *p) +{ + const char *q; + + if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) { + out1fmt("%s", p); + return; + } + while (*p) { + if (*p == '\'') { + out1fmt("\\'"); + p++; + continue; + } + q = strchr(p, '\''); + if (!q) { + out1fmt("'%s'", p ); + return; + } + out1fmt("'%.*s'", (int)(q - p), p ); + p = q; + } +} + +static int +sort_var(const void *v_v1, const void *v_v2) +{ + const struct var * const *v1 = v_v1; + const struct var * const *v2 = v_v2; + + /* XXX Will anyone notice we include the '=' of the shorter name? */ + return strcoll((*v1)->text, (*v2)->text); +} + +/* + * POSIX requires that 'set' (but not export or readonly) output the + * variables in lexicographic order - by the locale's collating order (sigh). + * Maybe we could keep them in an ordered balanced binary tree + * instead of hashed lists. + * For now just roll 'em through qsort for printing... + */ + int -showvarscmd(int argc __unused, char **argv __unused) +showvars(const char *name, int flag, int show_value) { struct var **vpp; struct var *vp; - const char *s; + const char *p; + + static struct var **list; /* static in case we are interrupted */ + static int list_len; + int count = 0; + + if (!list) { + list_len = 32; + list = ckmalloc(list_len * sizeof *list); + } for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { for (vp = *vpp ; vp ; vp = vp->next) { - if (vp->flags & VUNSET) + if (flag && !(vp->flags & flag)) continue; - for (s = vp->text; *s != '='; s++) - out1c(*s); - out1c('='); - out1qstr(s + 1); - out1c('\n'); + if (vp->flags & VUNSET && !(show_value & 2)) + continue; + if (count >= list_len) { + list = ckrealloc(list, + (list_len << 1) * sizeof *list); + list_len <<= 1; + } + list[count++] = vp; } } + + qsort(list, count, sizeof *list, sort_var); + + for (vpp = list; count--; vpp++) { + vp = *vpp; + if (name) + out1fmt("%s ", name); + for (p = vp->text ; *p != '=' ; p++) + out1c(*p); + if (!(vp->flags & VUNSET) && show_value) { + out1fmt("="); + print_quoted(++p); + } + out1c('\n'); + } return 0; } @@ -526,71 +571,29 @@ showvarscmd(int argc __unused, char **argv __unused) int exportcmd(int argc, char **argv) { - struct var **vpp; struct var *vp; char *name; - char *p; - char *cmdname; - int ch, values; + const char *p; int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + int pflag; - cmdname = argv[0]; - optreset = optind = 1; - opterr = 0; - values = 0; - while ((ch = getopt(argc, argv, "p")) != -1) { - switch (ch) { - case 'p': - values = 1; - break; - case '?': - default: - error("unknown option: -%c", optopt); - } + pflag = nextopt("p") == 'p' ? 3 : 0; + if (argc <= 1 || pflag) { + showvars( pflag ? argv[0] : 0, flag, pflag ); + return 0; } - argc -= optind; - argv += optind; - listsetvar(cmdenviron); - if (argc != 0) { - while ((name = *argptr++) != NULL) { - if ((p = strchr(name, '=')) != NULL) { - p++; - } else { - vpp = hashvar(name); - for (vp = *vpp ; vp ; vp = vp->next) { - if (varequal(vp->text, name)) { - - vp->flags |= flag; - if ((vp->flags & VEXPORT) && localevar(vp->text)) { - putenv(vp->text); - (void) setlocale(LC_ALL, ""); - } - goto found; - } - } - } - setvar(name, p, flag); -found:; - } - } else { - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) { - if (vp->flags & flag) { - if (values) { - out1str(cmdname); - out1c(' '); - } - for (p = vp->text ; *p != '=' ; p++) - out1c(*p); - if (values && !(vp->flags & VUNSET)) { - out1c('='); - out1qstr(p + 1); - } - out1c('\n'); - } + while ((name = *argptr++) != NULL) { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + vp = find_var(name, NULL, NULL); + if (vp != NULL) { + vp->flags |= flag; + continue; } } + setvar(name, p, flag); } return 0; } @@ -601,28 +604,28 @@ found:; */ int -localcmd(int argc __unused, char **argv __unused) +localcmd(int argc, char **argv) { char *name; if (! in_function()) error("Not in a function"); while ((name = *argptr++) != NULL) { - mklocal(name); + mklocal(name, 0); } return 0; } /* - * Make a variable a local variable. When a variable is made local, it's + * Make a variable a local variable. When a variable is made local, its * value and flags are saved in a localvar structure. The saved values * will be restored when the shell function returns. We handle the name * "-" as a special case. */ void -mklocal(char *name) +mklocal(const char *name, int flags) { struct localvar *lvp; struct var **vpp; @@ -631,17 +634,17 @@ mklocal(char *name) INTOFF; lvp = ckmalloc(sizeof (struct localvar)); if (name[0] == '-' && name[1] == '\0') { - lvp->text = ckmalloc(sizeof optlist); - memcpy(lvp->text, optlist, sizeof optlist); + char *p; + p = ckmalloc(sizeof_optlist); + lvp->text = memcpy(p, optlist, sizeof_optlist); vp = NULL; } else { - vpp = hashvar(name); - for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next); + vp = find_var(name, &vpp, NULL); if (vp == NULL) { if (strchr(name, '=')) - setvareq(savestr(name), VSTRFIXED); + setvareq(savestr(name), VSTRFIXED|flags); else - setvar(name, NULL, VSTRFIXED); + setvar(name, NULL, VSTRFIXED|flags); vp = *vpp; /* the new variable */ lvp->text = NULL; lvp->flags = VUNSET; @@ -649,8 +652,8 @@ mklocal(char *name) lvp->text = vp->text; lvp->flags = vp->flags; vp->flags |= VSTRFIXED|VTEXTFIXED; - if (strchr(name, '=')) - setvareq(savestr(name), 0); + if (name[vp->name_len] == '=') + setvareq(savestr(name), flags); } } lvp->vp = vp; @@ -673,12 +676,15 @@ poplocalvars(void) while ((lvp = localvars) != NULL) { localvars = lvp->next; vp = lvp->vp; + TRACE(("poplocalvar %s", vp ? vp->text : "-")); if (vp == NULL) { /* $- saved */ - memcpy(optlist, lvp->text, sizeof optlist); + memcpy(optlist, lvp->text, sizeof_optlist); ckfree(lvp->text); } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { - (void)unsetvar(vp->text); + (void)unsetvar(vp->text, 0); } else { + if (vp->func && (vp->flags & VNOFUNC) == 0) + (*vp->func)(lvp->text + vp->name_len + 1); if ((vp->flags & VTEXTFIXED) == 0) ckfree(vp->text); vp->flags = lvp->flags; @@ -709,7 +715,7 @@ setvarcmd(int argc, char **argv) */ int -unsetcmd(int argc __unused, char **argv __unused) +unsetcmd(int argc, char **argv) { char **ap; int i; @@ -717,11 +723,11 @@ unsetcmd(int argc __unused, char **argv __unused) int flg_var = 0; int ret = 0; - while ((i = nextopt("vf")) != '\0') { + while ((i = nextopt("evf")) != '\0') { if (i == 'f') flg_func = 1; else - flg_var = 1; + flg_var = i; } if (flg_func == 0 && flg_var == 0) flg_var = 1; @@ -730,7 +736,7 @@ unsetcmd(int argc __unused, char **argv __unused) if (flg_func) ret |= unsetfunc(*ap); if (flg_var) - ret |= unsetvar(*ap); + ret |= unsetvar(*ap, flg_var == 'e'); } return ret; } @@ -741,58 +747,38 @@ unsetcmd(int argc __unused, char **argv __unused) */ int -unsetvar(char *s) +unsetvar(const char *s, int unexport) { struct var **vpp; struct var *vp; - vpp = hashvar(s); - for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { - if (varequal(vp->text, s)) { - if (vp->flags & VREADONLY) - return (1); - INTOFF; - if (*(strchr(vp->text, '=') + 1) != '\0') - setvar(s, nullstr, 0); - if ((vp->flags & VEXPORT) && localevar(vp->text)) { - unsetenv(s); - setlocale(LC_ALL, ""); - } - vp->flags &= ~VEXPORT; - vp->flags |= VUNSET; - if ((vp->flags & VSTRFIXED) == 0) { - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - *vpp = vp->next; - ckfree(vp); - } - INTON; - return (0); + vp = find_var(s, &vpp, NULL); + if (vp == NULL) + return 0; + + if (vp->flags & VREADONLY) + return 1; + + INTOFF; + if (unexport) { + vp->flags &= ~VEXPORT; + } else { + if (vp->text[vp->name_len + 1] != '\0') + setvar(s, nullstr, 0); + vp->flags &= ~VEXPORT; + vp->flags |= VUNSET; + if ((vp->flags & VSTRFIXED) == 0) { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); } } - - return (0); + INTON; + return 0; } - -/* - * Find the appropriate entry in the hash table from the name. - */ - -STATIC struct var ** -hashvar(char *p) -{ - unsigned int hashval; - - hashval = ((unsigned char) *p) << 4; - while (*p && *p != '=') - hashval += (unsigned char) *p++; - return &vartab[hashval % VTABSIZE]; -} - - - /* * Returns true if the two strings specify the same varable. The first * variable name is terminated by '='; the second may be terminated by @@ -800,7 +786,7 @@ hashvar(char *p) */ STATIC int -varequal(char *p, char *q) +strequal(const char *p, const char *q) { while (*p == *q++) { if (*p++ == '=') @@ -812,5 +798,39 @@ varequal(char *p, char *q) } /* - * $PchId: var.c,v 1.5 2006/05/22 12:28:49 philip Exp $ + * Search for a variable. + * 'name' may be terminated by '=' or a NUL. + * vppp is set to the pointer to vp, or the list head if vp isn't found + * lenp is set to the number of charactets in 'name' */ + +STATIC struct var * +find_var(const char *name, struct var ***vppp, int *lenp) +{ + unsigned int hashval; + int len; + struct var *vp, **vpp; + const char *p = name; + + hashval = 0; + while (*p && *p != '=') + hashval = 2 * hashval + (unsigned char)*p++; + len = p - name; + + if (lenp) + *lenp = len; + vpp = &vartab[hashval % VTABSIZE]; + if (vppp) + *vppp = vpp; + + for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { + if (vp->name_len != len) + continue; + if (memcmp(vp->text, name, len) != 0) + continue; + if (vppp) + *vppp = vpp; + return vp; + } + return NULL; +} diff --git a/minix/commands/ash/var.h b/bin/sh/var.h similarity index 75% rename from minix/commands/ash/var.h rename to bin/sh/var.h index afa62565a..c5b692769 100644 --- a/minix/commands/ash/var.h +++ b/bin/sh/var.h @@ -1,3 +1,5 @@ +/* $NetBSD: var.h,v 1.25 2011/06/18 21:18:46 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -13,7 +15,7 @@ * 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. - * 4. Neither the name of the University nor the names of its contributors + * 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. * @@ -30,7 +32,6 @@ * SUCH DAMAGE. * * @(#)var.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/var.h,v 1.12 2004/04/06 20:06:51 markm Exp $ */ /* @@ -40,17 +41,19 @@ /* flags */ #define VEXPORT 0x01 /* variable is exported */ #define VREADONLY 0x02 /* variable cannot be modified */ -#define VSTRFIXED 0x04 /* variable struct is staticly allocated */ -#define VTEXTFIXED 0x08 /* text is staticly allocated */ +#define VSTRFIXED 0x04 /* variable struct is statically allocated */ +#define VTEXTFIXED 0x08 /* text is statically allocated */ #define VSTACK 0x10 /* text is allocated on the stack */ #define VUNSET 0x20 /* the variable is not set */ #define VNOFUNC 0x40 /* don't call the callback function */ +#define VNOSET 0x80 /* do not set variable - just readonly test */ struct var { struct var *next; /* next entry in hash list */ int flags; /* flags are defined above */ char *text; /* name=value */ + int name_len; /* length of name */ void (*func)(const char *); /* function to be called when */ /* the variable gets set/unset */ @@ -65,17 +68,21 @@ struct localvar { }; -struct localvar *localvars; +extern struct localvar *localvars; +#if ATTY +extern struct var vatty; +#endif extern struct var vifs; extern struct var vmail; extern struct var vmpath; extern struct var vpath; -extern struct var vppid; extern struct var vps1; extern struct var vps2; -extern struct var vpse; -#ifndef NO_HISTORY +extern struct var vps4; +#ifndef SMALL +extern struct var vterm; +extern struct var vtermcap; extern struct var vhistsize; #endif @@ -92,33 +99,31 @@ extern struct var vhistsize; #define pathval() (vpath.text + 5) #define ps1val() (vps1.text + 4) #define ps2val() (vps2.text + 4) -#define pseval() (vpse.text + 4) +#define ps4val() (vps4.text + 4) #define optindval() (voptind.text + 7) -#ifndef NO_HISTORY +#ifndef SMALL #define histsizeval() (vhistsize.text + 9) +#define termval() (vterm.text + 5) #endif +#if ATTY +#define attyset() ((vatty.flags & VUNSET) == 0) +#endif #define mpathset() ((vmpath.flags & VUNSET) == 0) void initvar(void); -void setvar(char *, char *, int); +void setvar(const char *, const char *, int); void setvareq(char *, int); struct strlist; -void listsetvar(struct strlist *); -char *lookupvar(char *); -char *bltinlookup(char *, int); +void listsetvar(struct strlist *, int); +char *lookupvar(const char *); +char *bltinlookup(const char *, int); char **environment(void); void shprocvar(void); -int showvarscmd(int, char **); -int exportcmd(int, char **); -int localcmd(int, char **); -void mklocal(char *); +int showvars(const char *, int, int); +void mklocal(const char *, int); +void listmklocal(struct strlist *, int); void poplocalvars(void); -int setvarcmd(int, char **); -int unsetcmd(int, char **); -int unsetvar(char *); -int setvarsafe(char *, char *, int); - -/* - * $PchId: var.h,v 1.4 2006/03/29 12:04:45 philip Exp $ - */ +int unsetvar(const char *, int); +int setvarsafe(const char *, const char *, int); +void print_quoted(const char *); diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 8546028f2..1d3a96165 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -2214,14 +2214,14 @@ ./usr/Makefile minix-sys ./usr/man minix-sys ./usr/man/man1 minix-sys -./usr/man/man1/..1 minix-sys +./usr/man/man1/..1 minix-sys obsolete ./usr/man/man1/[.1 minix-sys ./usr/man/man1/addr2line.1 minix-sys binutils ./usr/man/man1/apropos.1 minix-sys ./usr/man/man1/ar.1 minix-sys binutils ./usr/man/man1/as.1 minix-sys binutils ./usr/man/man1/asa.1 minix-sys -./usr/man/man1/ash.1 minix-sys +./usr/man/man1/ash.1 minix-sys obsolete ./usr/man/man1/at.1 minix-sys ./usr/man/man1/atf2kyua.1 minix-sys kyua ./usr/man/man1/atf-check.1 minix-sys atf @@ -2235,7 +2235,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/break.1 minix-sys +./usr/man/man1/break.1 minix-sys obsolete ./usr/man/man1/bsdtar.1 minix-sys ./usr/man/man1/bsfilt.1 minix-sys ./usr/man/man1/bunzip2.1 minix-sys @@ -2245,12 +2245,12 @@ ./usr/man/man1/c++.1 minix-sys gcccmds ./usr/man/man1/cal.1 minix-sys ./usr/man/man1/calendar.1 minix-sys -./usr/man/man1/case.1 minix-sys +./usr/man/man1/case.1 minix-sys obsolete ./usr/man/man1/cat.1 minix-sys ./usr/man/man1/cawf.1 minix-sys ./usr/man/man1/cc.1 minix-sys gcccmds ./usr/man/man1/cccp.1 minix-sys gcccmds -./usr/man/man1/cd.1 minix-sys +./usr/man/man1/cd.1 minix-sys obsolete ./usr/man/man1/c++filt.1 minix-sys binutils ./usr/man/man1/checknr.1 minix-sys ./usr/man/man1/chfn.1 minix-sys @@ -2268,9 +2268,9 @@ ./usr/man/man1/colrm.1 minix-sys ./usr/man/man1/column.1 minix-sys ./usr/man/man1/comm.1 minix-sys -./usr/man/man1/command.1 minix-sys +./usr/man/man1/command.1 minix-sys obsolete ./usr/man/man1/compress.1 minix-sys -./usr/man/man1/continue.1 minix-sys +./usr/man/man1/continue.1 minix-sys obsolete ./usr/man/man1/cp.1 minix-sys ./usr/man/man1/cpio.1 minix-sys ./usr/man/man1/cpp.1 minix-sys gcccmds @@ -2296,12 +2296,12 @@ ./usr/man/man1/eject.1 minix-sys ./usr/man/man1/elfedit.1 minix-sys binutils ./usr/man/man1/env.1 minix-sys -./usr/man/man1/eval.1 minix-sys +./usr/man/man1/eval.1 minix-sys obsolete ./usr/man/man1/ex.1 minix-sys -./usr/man/man1/exec.1 minix-sys -./usr/man/man1/exit.1 minix-sys +./usr/man/man1/exec.1 minix-sys obsolete +./usr/man/man1/exit.1 minix-sys obsolete ./usr/man/man1/expand.1 minix-sys -./usr/man/man1/export.1 minix-sys +./usr/man/man1/export.1 minix-sys obsolete ./usr/man/man1/expr.1 minix-sys ./usr/man/man1/false.1 minix-sys ./usr/man/man1/fetch.1 minix-sys @@ -2312,7 +2312,7 @@ ./usr/man/man1/flex.1 minix-sys ./usr/man/man1/flexdoc.1 minix-sys ./usr/man/man1/fold.1 minix-sys -./usr/man/man1/for.1 minix-sys +./usr/man/man1/for.1 minix-sys obsolete ./usr/man/man1/format.1 minix-sys ./usr/man/man1/fpr.1 minix-sys ./usr/man/man1/from.1 minix-sys @@ -2325,7 +2325,7 @@ ./usr/man/man1/gcpp.1 minix-sys gcccmds ./usr/man/man1/genassym.1 minix-sys ./usr/man/man1/getopt.1 minix-sys -./usr/man/man1/getopts.1 minix-sys +./usr/man/man1/getopts.1 minix-sys obsolete ./usr/man/man1/gprof.1 minix-sys binutils ./usr/man/man1/grep.1 minix-sys ./usr/man/man1/groups.1 minix-sys @@ -2333,14 +2333,14 @@ ./usr/man/man1/gzcat.1 minix-sys ./usr/man/man1/gzexe.1 minix-sys ./usr/man/man1/gzip.1 minix-sys -./usr/man/man1/hash.1 minix-sys +./usr/man/man1/hash.1 minix-sys obsolete ./usr/man/man1/head.1 minix-sys ./usr/man/man1/hexdump.1 minix-sys ./usr/man/man1/host.1 minix-sys ./usr/man/man1/hostaddr.1 minix-sys ./usr/man/man1/hostname.1 minix-sys ./usr/man/man1/id.1 minix-sys -./usr/man/man1/if.1 minix-sys +./usr/man/man1/if.1 minix-sys obsolete ./usr/man/man1/ifdef.1 minix-sys ./usr/man/man1/indent.1 minix-sys ./usr/man/man1/info.1 minix-sys @@ -2351,7 +2351,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 +./usr/man/man1/jobs.1 minix-sys obsolete ./usr/man/man1/join.1 minix-sys ./usr/man/man1/jot.1 minix-sys ./usr/man/man1/kill.1 minix-sys @@ -2384,7 +2384,7 @@ ./usr/man/man1/ln.1 minix-sys ./usr/man/man1/loadfont.1 minix-sys ./usr/man/man1/loadkeys.1 minix-sys -./usr/man/man1/local.1 minix-sys +./usr/man/man1/local.1 minix-sys obsolete ./usr/man/man1/lock.1 minix-sys ./usr/man/man1/logger.1 minix-sys ./usr/man/man1/login.1 minix-sys @@ -2454,13 +2454,13 @@ ./usr/man/man1/pwhash.1 minix-sys ./usr/man/man1/ranlib.1 minix-sys binutils ./usr/man/man1/rcp.1 minix-sys -./usr/man/man1/read.1 minix-sys +./usr/man/man1/read.1 minix-sys obsolete ./usr/man/man1/readelf.1 minix-sys binutils ./usr/man/man1/readlink.1 minix-sys -./usr/man/man1/readonly.1 minix-sys +./usr/man/man1/readonly.1 minix-sys obsolete ./usr/man/man1/recwave.1 minix-sys ./usr/man/man1/remsync.1 minix-sys -./usr/man/man1/return.1 minix-sys +./usr/man/man1/return.1 minix-sys obsolete ./usr/man/man1/rev.1 minix-sys ./usr/man/man1/rget.1 minix-sys ./usr/man/man1/rlogin.1 minix-sys @@ -2472,12 +2472,12 @@ ./usr/man/man1/sdiff.1 minix-sys ./usr/man/man1/sed.1 minix-sys ./usr/man/man1/seq.1 minix-sys -./usr/man/man1/set.1 minix-sys -./usr/man/man1/setvar.1 minix-sys +./usr/man/man1/set.1 minix-sys obsolete +./usr/man/man1/setvar.1 minix-sys obsolete ./usr/man/man1/sh.1 minix-sys ./usr/man/man1/sha1.1 minix-sys ./usr/man/man1/shar.1 minix-sys -./usr/man/man1/shift.1 minix-sys +./usr/man/man1/shift.1 minix-sys obsolete ./usr/man/man1/shlock.1 minix-sys ./usr/man/man1/shuffle.1 minix-sys ./usr/man/man1/size.1 minix-sys binutils @@ -2514,13 +2514,13 @@ ./usr/man/man1/touch.1 minix-sys ./usr/man/man1/tput.1 minix-sys ./usr/man/man1/tr.1 minix-sys -./usr/man/man1/trap.1 minix-sys +./usr/man/man1/trap.1 minix-sys obsolete ./usr/man/man1/true.1 minix-sys ./usr/man/man1/truncate.1 minix-sys ./usr/man/man1/tsort.1 minix-sys ./usr/man/man1/tty.1 minix-sys ./usr/man/man1/ul.1 minix-sys -./usr/man/man1/umask.1 minix-sys +./usr/man/man1/umask.1 minix-sys obsolete ./usr/man/man1/umount.1 minix-sys ./usr/man/man1/uname.1 minix-sys ./usr/man/man1/uncompress.1 minix-sys @@ -2530,7 +2530,7 @@ ./usr/man/man1/uniq.1 minix-sys ./usr/man/man1/units.1 minix-sys ./usr/man/man1/unlzma.1 minix-sys -./usr/man/man1/unset.1 minix-sys +./usr/man/man1/unset.1 minix-sys obsolete ./usr/man/man1/unvis.1 minix-sys ./usr/man/man1/unxz.1 minix-sys ./usr/man/man1/unzip.1 minix-sys @@ -2544,7 +2544,7 @@ ./usr/man/man1/vis.1 minix-sys ./usr/man/man1/vol.1 minix-sys ./usr/man/man1/w.1 minix-sys -./usr/man/man1/wait.1 minix-sys +./usr/man/man1/wait.1 minix-sys obsolete ./usr/man/man1/wall.1 minix-sys ./usr/man/man1/wc.1 minix-sys ./usr/man/man1/what.1 minix-sys @@ -5249,6 +5249,15 @@ ./usr/share/doc/psd/19.curses/twinkle1.c minix-sys ./usr/share/doc/psd/19.curses/twinkle2.c minix-sys ./usr/share/doc/psd/19.curses/win_st.c minix-sys +./usr/share/doc/usd minix-sys +./usr/share/doc/usd/03.shell minix-sys +./usr/share/doc/usd/03.shell/Makefile minix-sys +./usr/share/doc/usd/03.shell/Rv7man minix-sys +./usr/share/doc/usd/03.shell/t1 minix-sys +./usr/share/doc/usd/03.shell/t2 minix-sys +./usr/share/doc/usd/03.shell/t3 minix-sys +./usr/share/doc/usd/03.shell/t4 minix-sys +./usr/share/doc/usd/03.shell/t.mac minix-sys ./usr/share/examples minix-sys atf ./usr/share/examples/atf minix-sys atf ./usr/share/examples/atf/atf-run.hooks minix-sys atf,!kyua diff --git a/etc/mtree/NetBSD.dist.base b/etc/mtree/NetBSD.dist.base index 7d68a01b0..a1f56ae3d 100644 --- a/etc/mtree/NetBSD.dist.base +++ b/etc/mtree/NetBSD.dist.base @@ -114,6 +114,8 @@ ./usr/share/doc/html/bzip2 ./usr/share/doc/psd ./usr/share/doc/psd/19.curses +./usr/share/doc/usd +./usr/share/doc/usd/03.shell ./usr/share/info ./usr/share/games ./usr/share/games/fortune diff --git a/etc/profile b/etc/profile index 6daff7af1..57e791d1b 100755 --- a/etc/profile +++ b/etc/profile @@ -2,6 +2,7 @@ # Activate emacs keybindings and command line history support set -o emacs +set -o tabcomplete # Set the default path PATH=/usr/local/bin:/usr/pkg/bin:/usr/bin:/bin:/usr/games diff --git a/minix/commands/Makefile b/minix/commands/Makefile index 854f7634a..00bb9d82a 100644 --- a/minix/commands/Makefile +++ b/minix/commands/Makefile @@ -2,7 +2,7 @@ .include -SUBDIR= add_route arp ash at backup btrace \ +SUBDIR= add_route arp at backup btrace \ cawf cdprobe \ ci cleantmp cmp co \ compress crc cron crontab \ diff --git a/minix/commands/ash/Makefile b/minix/commands/ash/Makefile deleted file mode 100644 index bcf56e683..000000000 --- a/minix/commands/ash/Makefile +++ /dev/null @@ -1,101 +0,0 @@ -# Makefile for ash. - -.include - -PROG= sh -BINDIR= /bin -MAN= - -# Enable this line to disable command line editing -#EDIT=-DNO_HISTORY - -# Enable this line if your system does not have a -#NO_PATHS_H=-DNO_PATHS_H - -# Enable this if you don't want job control -NO_JOBS=-DJOBS=0 -MKB_NO_JOBS=-j - -SRCS= alias.c arith.y arith_lex.l cd.c complete.c eval.c exec.c expand.c \ - histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \ - mystring.c options.c output.c parser.c redir.c show.c \ - trap.c var.c setmode.c - -.include "${.CURDIR}/bltin/Makefile.inc" - -GENSRCS= builtins.c init.c nodes.c syntax.c operators.c signames.c -GENHDRS= builtins.h nodes.h syntax.h token.h operators.h signames.h - -SRCS+= ${GENSRCS} - -CLEANFILES+=${GENSRCS} ${GENHDRS} - -DPADD+= ${LIBL} ${LIBEDIT} -LDADD+= -ll -ledit - -CPPFLAGS+= -DSHELL -CPPFLAGS+=${EDIT} ${NO_PATHS_H} ${NO_JOBS} - -CPPFLAGS+= -I. -I${.CURDIR} - -# A. Generate C tools used to build ash -.for tool in init nodes signames syntax -${.OBJDIR}/mk${tool}: ${.CURDIR}/mk${tool}.c - ${HOST_CC} ${HOST_CFLAGS} ${HOST_CPPFLAGS} ${.ALLSRC} -o ${.TARGET} - -CLEANFILES+= ${.OBJDIR}/mk${tool} -.endfor - -# B. Generates C sources from C tools -NODES_ARGS:= ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat -INIT_ARGS:= alias.c eval.c exec.c input.c jobs.c options.c parser.c \ - redir.c trap.c var.c - -.for tool in nodes signames syntax -${tool}.c ${tool}.h: ${.OBJDIR}/mk${tool} - ${.OBJDIR}/mk${tool} ${${tool:tu}_ARGS} -.endfor - -init.c: ${.OBJDIR}/mkinit \ - alias.c eval.c exec.c input.c jobs.c options.c parser.c \ - redir.c trap.c var.c - ${.OBJDIR}/mkinit ${.ALLSRC:S,^${.OBJDIR}/mkinit$,,} - -# C. Generates C sources from shell scripts -token.h: - ${.CURDIR}/mktokens.sh - -builtins.c builtins.h: - ${.CURDIR}/mkbuiltins.sh ${MKB_NO_JOBS} . ${.CURDIR}/shell.h ${.CURDIR}/builtins.def - -operators.c operators.h: - ${.CURDIR}/bltin/mkexpr.sh ${.CURDIR}/bltin/unary_op ${.CURDIR}/bltin/binary_op - -# D. Generates sources from yacc/lex -LFLAGS= -8 # 8-bit lex scanner for arithmetic - -YFLAGS:= -d -CLEANFILES+= arith.h arith.y.o - -parser.c: token.h -y.tab.h: arith.y -arith.h: y.tab.h -arith_lex.l: arith.h - -# Explicit dependencies to ensure creation when needed -# LSC FIXME Under MINIX, the build system curiously needs more help. -# is it because of the missing order tools? -expand.c: arith.h -trap.c: signames.h -cd.c complete.c eval.c exec.c expand.c jobs.c main.c options.c parser.c redir.c show.c trap.c var.c: nodes.h -eval.c exec.c expand.c input.c input.h jobs.c mystring.c output.c parser.c trap.c var.c: syntax.h -cd.c eval.c exec.c histedit.cjobs.c main.c miscbltin.c options.c trap.c var.c: builtins.h - -# LSC: Seems that this file is implicitly taken into account by NetBSD's make, -# still seems to be ignored / not found currently. -# It's a sad story, as it has default rules to manage yacc / lex files. So for -# a happy ending here it is explicitly included: -.include - -.include - diff --git a/minix/commands/ash/arith.y b/minix/commands/ash/arith.y deleted file mode 100644 index 50dfd5e29..000000000 --- a/minix/commands/ash/arith.y +++ /dev/null @@ -1,360 +0,0 @@ -%{ -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -#ifndef lint -static char sccsid[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95"; -#endif -#endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/arith.y,v 1.19 2004/05/24 10:11:31 stefanf Exp $"); -*/ - -#include -#include - - -#include "shell.h" -#include "expand.h" -#include "var.h" -%} -%union { - arith_t l_value; - char* s_value; -} -%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN -%token ARITH_VAR - -%type expr -%right ARITH_ASSIGN -%right ARITH_ADDASSIGN ARITH_SUBASSIGN -%right ARITH_MULASSIGN ARITH_DIVASSIGN ARITH_REMASSIGN -%right ARITH_RSHASSIGN ARITH_LSHASSIGN -%right ARITH_BANDASSIGN ARITH_BXORASSIGN ARITH_BORASSIGN -%left ARITH_OR -%left ARITH_AND -%left ARITH_BOR -%left ARITH_BXOR -%left ARITH_BAND -%left ARITH_EQ ARITH_NE -%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE -%left ARITH_LSHIFT ARITH_RSHIFT -%left ARITH_ADD ARITH_SUB -%left ARITH_MUL ARITH_DIV ARITH_REM -%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT -%% - -exp: - expr - { return ($1); } - ; - -expr: - ARITH_LPAREN expr ARITH_RPAREN - { $$ = $2; } | - expr ARITH_OR expr - { $$ = $1 ? $1 : $3 ? $3 : 0; } | - expr ARITH_AND expr - { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; } | - expr ARITH_BOR expr - { $$ = $1 | $3; } | - expr ARITH_BXOR expr - { $$ = $1 ^ $3; } | - expr ARITH_BAND expr - { $$ = $1 & $3; } | - expr ARITH_EQ expr - { $$ = $1 == $3; } | - expr ARITH_GT expr - { $$ = $1 > $3; } | - expr ARITH_GE expr - { $$ = $1 >= $3; } | - expr ARITH_LT expr - { $$ = $1 < $3; } | - expr ARITH_LE expr - { $$ = $1 <= $3; } | - expr ARITH_NE expr - { $$ = $1 != $3; } | - expr ARITH_LSHIFT expr - { $$ = $1 << $3; } | - expr ARITH_RSHIFT expr - { $$ = $1 >> $3; } | - expr ARITH_ADD expr - { $$ = $1 + $3; } | - expr ARITH_SUB expr - { $$ = $1 - $3; } | - expr ARITH_MUL expr - { $$ = $1 * $3; } | - expr ARITH_DIV expr - { - if ($3 == 0) - yyerror("division by zero"); - $$ = $1 / $3; - } | - expr ARITH_REM expr - { - if ($3 == 0) - yyerror("division by zero"); - $$ = $1 % $3; - } | - ARITH_NOT expr - { $$ = !($2); } | - ARITH_BNOT expr - { $$ = ~($2); } | - ARITH_SUB expr %prec ARITH_UNARYMINUS - { $$ = -($2); } | - ARITH_ADD expr %prec ARITH_UNARYPLUS - { $$ = $2; } | - ARITH_NUM | - ARITH_VAR - { - char *p; - arith_t arith_val; - char *str_val; - - if (lookupvar($1) == NULL) - setvarsafe($1, "0", 0); - str_val = lookupvar($1); - arith_val = strtoarith_t(str_val, &p, 0); - /* - * Conversion is successful only in case - * we've converted _all_ characters. - */ - if (*p != '\0') - yyerror("variable conversion error"); - $$ = arith_val; - } | - ARITH_VAR ARITH_ASSIGN expr - { - if (arith_assign($1, $3) != 0) - yyerror("variable assignment error"); - $$ = $3; - } | - ARITH_VAR ARITH_ADDASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) + $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_SUBASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) - $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_MULASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) * $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_DIVASSIGN expr - { - arith_t value; - - if ($3 == 0) - yyerror("division by zero"); - - value = atoarith_t(lookupvar($1)) / $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_REMASSIGN expr - { - arith_t value; - - if ($3 == 0) - yyerror("division by zero"); - - value = atoarith_t(lookupvar($1)) % $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_RSHASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) >> $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_LSHASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) << $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_BANDASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) & $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_BXORASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) ^ $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } | - ARITH_VAR ARITH_BORASSIGN expr - { - arith_t value; - - value = atoarith_t(lookupvar($1)) | $3; - if (arith_assign($1, value) != 0) - yyerror("variable assignment error"); - $$ = value; - } ; -%% -#include "error.h" -#include "output.h" -#include "memalloc.h" - -#define lstrlen(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3) - -char *arith_buf, *arith_startbuf; - -int yylex(void); -int yyparse(void); - -int -arith_assign(char *name, arith_t value) -{ - char *str; - int ret; - - str = (char *)ckmalloc(lstrlen(value)); - sprintf(str, ARITH_FORMAT_STR, value); - ret = setvarsafe(name, str, 0); - free(str); - return ret; -} - -int -arith(char *s) -{ - long result; - - arith_buf = arith_startbuf = s; - - INTOFF; - result = yyparse(); - arith_lex_reset(); /* Reprime lex. */ - INTON; - - return result; -} - -void -yyerror(char *s) -{ - - yyerrok; - yyclearin; - arith_lex_reset(); /* Reprime lex. */ - error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); -} - -/* - * The exp(1) builtin. - */ -int -expcmd(int argc, char **argv) -{ - char *p; - char *concat; - char **ap; - long i; - - if (argc > 1) { - p = argv[1]; - if (argc > 2) { - /* - * Concatenate arguments. - */ - STARTSTACKSTR(concat); - ap = argv + 2; - for (;;) { - while (*p) - STPUTC(*p++, concat); - if ((p = *ap++) == NULL) - break; - STPUTC(' ', concat); - } - STPUTC('\0', concat); - p = grabstackstr(concat); - } - } else - p = ""; - - i = arith(p); - - out1fmt("%ld\n", i); - return !i; -} - -/*************************/ -#ifdef TEST_ARITH -#include -main(int argc, char *argv[]) -{ - printf("%d\n", exp(argv[1])); -} - -error(char *s) -{ - fprintf(stderr, "exp: %s\n", s); - exit(1); -} -#endif diff --git a/minix/commands/ash/arith_lex.h b/minix/commands/ash/arith_lex.h deleted file mode 100644 index 301befacf..000000000 --- a/minix/commands/ash/arith_lex.h +++ /dev/null @@ -1,12 +0,0 @@ -/* -arith_lex.h - -Created: July 1995 by Philip Homburg -*/ - -int yylex(void); -void arith_lex_reset(void); - -/* - * $PchId: arith_lex.h,v 1.1 2001/05/18 19:57:55 philip Exp $ - */ diff --git a/minix/commands/ash/bltin/LICENSE b/minix/commands/ash/bltin/LICENSE deleted file mode 100644 index 83685c506..000000000 --- a/minix/commands/ash/bltin/LICENSE +++ /dev/null @@ -1,40 +0,0 @@ - ASH GENERAL PUBLIC LICENSE - - 1. You may copy and distribute ash code or code derived from it in -source or object form, provided that you conspicuously and appropriately -publish on each copy a valid copyright notice "Copyright 1989 by Kenneth -Almquist." (or with whatever year is appropriate); keep intact the -notices on all files that refer to this License Agreement and to the -absence of any warranty; and give any other recipients of the ash program -a copy of this License Agreement along with the program. - - 2. You may not copy, sublicense, distribute or transfer ash except as -expressly provided under this License Agreement. Any attempt otherwise -to copy, sublicense, distribute or transfer ash is void and your rights -to use ash under this License agreement shall be automatically terminated. -However, parties who have received computer software programs from you -with this License Agreement will not have their licenses terminated so -long as such parties remain in full compliance. - - - NO WARRANTY - - Because ash is licensed free of charge, I provide absolutely no -warranty, to the extent permitted by applicable state law. Except -when otherwise stated in writing, Kenneth Almquist and/or other -parties provide ash "as is" without warranty of any kind, either -expressed or implied, including, but not limited to, the implied -warranties of merchantability and fitness for a particular purpose. -The entire risk as to the quality and performance of the program is -with you. Should the ash program prove defective, you assume the cost -of all necessary servicing, repair or correction. - - In no event unless required by applicable law will Kenneth Almquist -and/or any other party who may modify and redistribute ash as permitted -above, be liable to you for damages, including any lost profits, lost -monies, or other special, incidental or consequential damages arising -out of the use or inability to use (including but not limited to loss -of data or data being rendered inaccurate or losses sustained by third -parties or a failure of the program to operate with programs provided -by other parties) the program, even if you have been advised of the -possibility of such damages, or for any claim by any other party. diff --git a/minix/commands/ash/bltin/Makefile.inc b/minix/commands/ash/bltin/Makefile.inc deleted file mode 100644 index 5e13a4d40..000000000 --- a/minix/commands/ash/bltin/Makefile.inc +++ /dev/null @@ -1,7 +0,0 @@ - -.PATH: ${.CURDIR}/bltin - -SRCS+= echo.c error.c expr.c regexp.c - -# LSC Again nbmake seems dumber on MINIX... -expr.c: operators.h diff --git a/minix/commands/ash/bltin/binary_op b/minix/commands/ash/bltin/binary_op deleted file mode 100644 index 985fe4077..000000000 --- a/minix/commands/ash/bltin/binary_op +++ /dev/null @@ -1,27 +0,0 @@ -# List of binary operators used by test/expr. -# -# Copyright 1989 by Kenneth Almquist. All rights reserved. -# This file is part of ash, which is distributed under the terms specified -# by the Ash General Public License. See the file named LICENSE. - -OR1 -o 1 -OR2 | 1 -AND1 -a 2 -AND2 & 2 -STREQ = 4 OP_STRING -STRNE != 4 OP_STRING -NEWER -newer 4 OP_STRING -NEWER -nt 4 OP_STRING -OLDER -ot 4 OP_STRING -EQ -eq 4 OP_INT -NE -ne 4 OP_INT -GT -gt 4 OP_INT -LT -lt 4 OP_INT -LE -le 4 OP_INT -GE -ge 4 OP_INT -PLUS + 5 OP_INT -MINUS - 5 OP_INT -TIMES * 6 OP_INT -DIVIDE / 6 OP_INT -REM % 6 OP_INT -MATCHPAT : 7 OP_STRING diff --git a/minix/commands/ash/bltin/catf.c b/minix/commands/ash/bltin/catf.c deleted file mode 100644 index c7aee99c8..000000000 --- a/minix/commands/ash/bltin/catf.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copy the files given as arguments to the standard output. The file - * name "-" refers to the standard input. - * - * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. - * This file is part of ash, which is distributed under the terms specified - * by the Ash General Public License. See the file named LICENSE. - */ - -#define main catfcmd - -#include "bltin.h" -#include "../error.h" -#include -#include - - -#ifdef SBUFSIZE -#define BUFSIZE() SBUFSIZE -#else -#ifdef MAXBSIZE -#define BUFSIZE() MAXBSIZE -#else -#define BUFSIZE() BSIZE -#endif -#endif - - -main(argc, argv) char **argv; { - char *filename; - char *buf = stalloc(BUFSIZE()); - int fd; - int i; -#ifdef SHELL - volatile int input; - struct jmploc jmploc; - struct jmploc *volatile savehandler; -#endif - - INITARGS(argv); -#ifdef SHELL - input = -1; - if (setjmp(jmploc.loc)) { - close(input); - handler = savehandler; - longjmp(handler, 1); - } - savehandler = handler; - handler = &jmploc; -#endif - while ((filename = *++argv) != NULL) { - if (filename[0] == '-' && filename[1] == '\0') { - fd = 0; - } else { -#ifdef SHELL - INTOFF; - if ((fd = open(filename, O_RDONLY)) < 0) - error("Can't open %s", filename); - input = fd; - INTON; -#else - if ((fd = open(filename, O_RDONLY)) < 0) { - fprintf(stderr, "catf: Can't open %s\n", filename); - exit(2); - } -#endif - } - while ((i = read(fd, buf, BUFSIZE())) > 0) { -#ifdef SHELL - if (out1 == &memout) { - register char *p; - for (p = buf ; --i >= 0 ; p++) { - outc(*p, &memout); - } - } else { - write(1, buf, i); - } -#else - write(1, buf, i); -#endif - } - if (fd != 0) - close(fd); - } -#ifdef SHELL - handler = savehandler; -#endif -} diff --git a/minix/commands/ash/bltin/error.c b/minix/commands/ash/bltin/error.c deleted file mode 100644 index 7af94dd61..000000000 --- a/minix/commands/ash/bltin/error.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. - * This file is part of ash, which is distributed under the terms specified - * by the Ash General Public License. See the file named LICENSE. - */ - -#include - -char *commandname; - - -void -#ifdef __STDC__ -error(char *msg, ...) { -#else -error(msg) - char *msg; - { -#endif - - fprintf(stderr, "%s: %s\n", commandname, msg); - exit(2); -} diff --git a/minix/commands/ash/bltin/expr.c b/minix/commands/ash/bltin/expr.c deleted file mode 100644 index 3c5ca51e8..000000000 --- a/minix/commands/ash/bltin/expr.c +++ /dev/null @@ -1,482 +0,0 @@ -/* - * The expr and test commands. - * - * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. - * This file is part of ash, which is distributed under the terms specified - * by the Ash General Public License. See the file named LICENSE. - */ - - -#include "bltin.h" -#include "operators.h" -#include -#include -#include -#include -#include - - -#define STACKSIZE 12 -#define NESTINCR 16 - -/* data types */ -#define STRING 0 -#define INTEGER 1 -#define BOOLEAN 2 - - -/* - * This structure hold a value. The type keyword specifies the type of - * the value, and the union u holds the value. The value of a boolean - * is stored in u.num (1 = TRUE, 0 = FALSE). - */ - -struct value { - int type; - union { - char *string; - long num; - } u; -}; - - -struct operator { - short op; /* which operator */ - short pri; /* priority of operator */ -}; - - -struct filestat { - char *name; /* name of file */ - int rcode; /* return code from stat */ - struct stat stat; /* status info on file */ -}; - - -extern char *match_begin[10]; /* matched string */ -extern short match_length[10]; /* defined in regexp.c */ -extern short number_parens; /* number of \( \) pairs */ - - -#ifdef __STDC__ -static int expr_is_false(struct value *); -static void expr_operator(int, struct value *, struct filestat *); -static int lookup_op(char *, char *const*); -#else -static int expr_is_false(); -static void expr_operator(); -static int lookup_op(); -#endif - - - -int exprcmd(argc, argv) int argc; char **argv; { - char **ap; - char *opname; - char c; - char *p; - int print; - int nest; /* parenthises nesting */ - int op; - int pri; - int skipping; - int binary; - struct operator opstack[STACKSIZE]; - struct operator *opsp; - struct value valstack[STACKSIZE + 1]; - struct value *valsp; - struct filestat fs; - - INITARGS(argv); - c = **argv; - print = 1; - if (c == 't') - print = 0; - else if (c == '[') { - if (! equal(argv[argc - 1], "]")) - error("missing ]"); - argv[argc - 1] = NULL; - print = 0; - } - ap = argv + 1; - fs.name = NULL; - - /* - * We use operator precedence parsing, evaluating the expression - * as we parse it. Parentheses are handled by bumping up the - * priority of operators using the variable "nest." We use the - * variable "skipping" to turn off evaluation temporarily for the - * short circuit boolean operators. (It is important do the short - * circuit evaluation because under NFS a stat operation can take - * infinitely long.) - */ - - nest = 0; - skipping = 0; - opsp = opstack + STACKSIZE; - valsp = valstack; - if (*ap == NULL) { - valstack[0].type = BOOLEAN; - valstack[0].u.num = 0; - goto done; - } - for (;;) { - opname = *ap++; - if (opname == NULL) -syntax: error("syntax error"); - if (opname[0] == '(' && opname[1] == '\0') { - nest += NESTINCR; - continue; - } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { - if (opsp == &opstack[0]) -overflow: error("Expression too complex"); - --opsp; - opsp->op = op; - opsp->pri = op_priority[op] + nest; - continue; - - } else { - if (opname[0] == '\'') { - for (p = opname ; *++p != '\0' ; ); - if (--p > opname && *p == '\'') { - *p = '\0'; - opname++; - } - } - valsp->type = STRING; - valsp->u.string = opname; - valsp++; - } - for (;;) { - opname = *ap++; - if (opname == NULL) { - if (nest != 0) - goto syntax; - pri = 0; - break; - } - if (opname[0] != ')' || opname[1] != '\0') { - if ((op = lookup_op(opname, binary_op)) < 0) - goto syntax; - op += FIRST_BINARY_OP; - pri = op_priority[op] + nest; - break; - } - if ((nest -= NESTINCR) < 0) - goto syntax; - } - while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { - binary = opsp->op; - for (;;) { - valsp--; - c = op_argflag[opsp->op]; - if (c == OP_INT) { - if (valsp->type == STRING) - valsp->u.num = atol(valsp->u.string); - valsp->type = INTEGER; - } else if (c >= OP_STRING) { /* OP_STRING or OP_FILE */ - if (valsp->type == INTEGER) { - p = stalloc(32); -#ifdef SHELL - fmtstr(p, 32, "%ld", valsp->u.num); -#else - sprintf(p, "%d", valsp->u.num); -#endif - valsp->u.string = p; - } else if (valsp->type == BOOLEAN) { - if (valsp->u.num) - valsp->u.string = "true"; - else - valsp->u.string = ""; - } - valsp->type = STRING; - if (c == OP_FILE - && (fs.name == NULL - || ! equal(fs.name, valsp->u.string))) { - fs.name = valsp->u.string; - fs.rcode = stat(valsp->u.string, &fs.stat); - } - } - if (binary < FIRST_BINARY_OP) - break; - binary = 0; - } - if (! skipping) - expr_operator(opsp->op, valsp, &fs); - else if (opsp->op == AND1 || opsp->op == OR1) - skipping--; - valsp++; /* push value */ - opsp++; /* pop operator */ - } - if (opname == NULL) - break; - if (opsp == &opstack[0]) - goto overflow; - if (op == AND1 || op == AND2) { - op = AND1; - if (skipping || expr_is_false(valsp - 1)) - skipping++; - } - if (op == OR1 || op == OR2) { - op = OR1; - if (skipping || ! expr_is_false(valsp - 1)) - skipping++; - } - opsp--; - opsp->op = op; - opsp->pri = pri; - } -done: - if (print) { - if (valstack[0].type == STRING) - printf("%s\n", valstack[0].u.string); - else if (valstack[0].type == INTEGER) - printf("%ld\n", valstack[0].u.num); - else if (valstack[0].u.num != 0) - printf("true\n"); - } - return expr_is_false(&valstack[0]); -} - - -static int -expr_is_false(val) - struct value *val; - { - if (val->type == STRING) { - if (val->u.string[0] == '\0') - return 1; - } else { /* INTEGER or BOOLEAN */ - if (val->u.num == 0) - return 1; - } - return 0; -} - - -/* - * Execute an operator. Op is the operator. Sp is the stack pointer; - * sp[0] refers to the first operand, sp[1] refers to the second operand - * (if any), and the result is placed in sp[0]. The operands are converted - * to the type expected by the operator before expr_operator is called. - * Fs is a pointer to a structure which holds the value of the last call - * to stat, to avoid repeated stat calls on the same file. - */ - -static void -expr_operator(op, sp, fs) - int op; - struct value *sp; - struct filestat *fs; - { - int i, r; - struct stat st1, st2; - regex_t pat; - regmatch_t rm[2]; - - switch (op) { - case NOT: - sp->u.num = expr_is_false(sp); - sp->type = BOOLEAN; - break; - case EXISTS: - if (fs->rcode >= 0) goto true; - goto false; - case ISREAD: - i = 04; - goto permission; - case ISWRITE: - i = 02; - goto permission; - case ISEXEC: - i = 01; -permission: - if (fs->stat.st_uid == geteuid()) - i <<= 6; - else if (fs->stat.st_gid == getegid()) - i <<= 3; - goto filebit; /* true if (stat.st_mode & i) != 0 */ - case ISFILE: - i = S_IFREG; - goto filetype; - case ISDIR: - i = S_IFDIR; - goto filetype; - case ISCHAR: - i = S_IFCHR; - goto filetype; - case ISBLOCK: - i = S_IFBLK; - goto filetype; - case ISFIFO: -#ifdef S_IFIFO - i = S_IFIFO; - goto filetype; -#else - goto false; -#endif -filetype: - if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) { -true: - sp->u.num = 1; - } else { -false: - sp->u.num = 0; - } - sp->type = BOOLEAN; - break; - case ISSETUID: - i = S_ISUID; - goto filebit; - case ISSETGID: - i = S_ISGID; - goto filebit; - case ISSTICKY: - i = S_ISVTX; -filebit: - if (fs->stat.st_mode & i && fs->rcode >= 0) - goto true; - goto false; - case ISSLINK: - if (lstat(fs->name, &st1) == -1) - goto false; - if (S_ISLNK(st1.st_mode)) - goto true; - goto false; - case ISSIZE: - sp->u.num = fs->rcode >= 0? fs->stat.st_size : 0L; - sp->type = INTEGER; - break; - case OLDER: - case NEWER: - if (stat(sp->u.string, &st1) != 0) { - sp->u.num = 0; - } else if (stat((sp + 1)->u.string, &st2) != 0) { - sp->u.num = 1; - } else { - int isnewer = st1.st_mtime >= st2.st_mtime; - if(op == NEWER) - sp->u.num = isnewer; - else - sp->u.num = !isnewer; - } - sp->type = INTEGER; - break; - case ISTTY: - sp->u.num = isatty(sp->u.num); - sp->type = BOOLEAN; - break; - case NULSTR: - if (sp->u.string[0] == '\0') - goto true; - goto false; - case STRLEN: - sp->u.num = strlen(sp->u.string); - sp->type = INTEGER; - break; - case OR1: - case AND1: - /* - * These operators are mostly handled by the parser. If we - * get here it means that both operands were evaluated, so - * the value is the value of the second operand. - */ - *sp = *(sp + 1); - break; - case STREQ: - case STRNE: - i = 0; - if (equal(sp->u.string, (sp + 1)->u.string)) - i++; - if (op == STRNE) - i = 1 - i; - sp->u.num = i; - sp->type = BOOLEAN; - break; - case EQ: - if (sp->u.num == (sp + 1)->u.num) - goto true; - goto false; - case NE: - if (sp->u.num != (sp + 1)->u.num) - goto true; - goto false; - case GT: - if (sp->u.num > (sp + 1)->u.num) - goto true; - goto false; - case LT: - if (sp->u.num < (sp + 1)->u.num) - goto true; - goto false; - case LE: - if (sp->u.num <= (sp + 1)->u.num) - goto true; - goto false; - case GE: - if (sp->u.num >= (sp + 1)->u.num) - goto true; - goto false; - case PLUS: - sp->u.num += (sp + 1)->u.num; - break; - case MINUS: - sp->u.num -= (sp + 1)->u.num; - break; - case TIMES: - sp->u.num *= (sp + 1)->u.num; - break; - case DIVIDE: - if ((sp + 1)->u.num == 0) - error("Division by zero"); - sp->u.num /= (sp + 1)->u.num; - break; - case REM: - if ((sp + 1)->u.num == 0) - error("Division by zero"); - sp->u.num %= (sp + 1)->u.num; - break; - case MATCHPAT: - { - r = regcomp(&pat, (sp + 1)->u.string, 0); - if (r) - error("Bad regular expression"); - if (regexec(&pat, sp->u.string, 2, rm, 0) == 0 && - rm[0].rm_so == 0) - { - if (pat.re_nsub > 0) { - sp->u.string[rm[1].rm_eo] = '\0'; - sp->u.string = sp->u.string+rm[1].rm_so; - } else { - sp->u.num = rm[0].rm_eo; - sp->type = INTEGER; - } - } else { - if (pat.re_nsub > 0) { - sp->u.string[0] = '\0'; - } else { - sp->u.num = 0; - sp->type = INTEGER; - } - } - } - break; - } -} - - -static int -lookup_op(name, table) - char *name; - char *const*table; - { - register char *const*tp; - register char const *p; - char c = name[1]; - - for (tp = table ; (p = *tp) != NULL ; tp++) { - if (p[1] == c && equal(p, name)) - return tp - table; - } - return -1; -} diff --git a/minix/commands/ash/bltin/line.c b/minix/commands/ash/bltin/line.c deleted file mode 100644 index aa6970eed..000000000 --- a/minix/commands/ash/bltin/line.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * The line command. Reads one line from the standard input and writes it - * to the standard output. - * - * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. - * This file is part of ash, which is distributed under the terms specified - * by the Ash General Public License. See the file named LICENSE. - */ - -#define main linecmd - -#include "bltin.h" - - -main(argc, argv) char **argv; { - char c; - - for (;;) { - if (read(0, &c, 1) != 1) { - putchar('\n'); - return 1; - } - putchar(c); - if (c == '\n') - return 0; - } -} diff --git a/minix/commands/ash/bltin/makefile.not b/minix/commands/ash/bltin/makefile.not deleted file mode 100644 index ed36c73f5..000000000 --- a/minix/commands/ash/bltin/makefile.not +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. -# This file is part of ash, which is distributed under the terms specified -# by the Ash General Public License. See the file named LICENSE. - -LIBFILES=catfcmd.o echocmd.o exprcmd.o linecmd.o nlechocmd.o\ - operators.o regexp.o -DEBUG=-g -CFLAGS=$(DEBUG) -#CC=gcc - -all:$P bltinlib.a catf echo expr line nlecho true umask - -bltinlib.a:$P $(LIBFILES) - ar rc $@ $(LIBFILES) - -catf: catf.c bltin.h ../shell.h ../error.h error.o stalloc.o - $(CC) $(CFLAGS) -o $@ catf.c error.o stalloc.o - -catfcmd.o: catf.c bltin.h ../shell.h ../error.h - $(CC) -DSHELL $(CFLAGS) -c catf.c - mv catf.o $@ - -expr: expr.c bltin.h ../shell.h operators.h operators.o regexp.o error.o stalloc.o - $(CC) $(CFLAGS) -o $@ expr.c operators.o regexp.o error.o stalloc.o - -rm -f test '[' - ln expr test - ln expr '[' - -exprcmd.o: expr.c bltin.h ../shell.h operators.h - $(CC) -DSHELL $(CFLAGS) -c expr.c - mv expr.o $@ - -operators.c operators.h: unary_op binary_op mkexpr - ./mkexpr - -operators.o: ../shell.h operators.h - -regexp.o: bltin.h ../shell.h - -echo: echo.c bltin.h ../shell.h - $(CC) $(CFLAGS) -o $@ echo.c - -echocmd.o: echo.c bltin.h ../shell.h - $(CC) -DSHELL $(CFLAGS) -c echo.c - mv echo.o $@ - -line: line.c bltin.h ../shell.h - $(CC) $(CFLAGS) -o $@ line.c - -linecmd.o: line.c bltin.h ../shell.h - $(CC) -DSHELL $(CFLAGS) -c line.c - mv line.o $@ - -nlecho: nlecho.c bltin.h ../shell.h - $(CC) $(CFLAGS) -o $@ nlecho.c - -nlechocmd.o: nlecho.c bltin.h ../shell.h - $(CC) -DSHELL $(CFLAGS) -c nlecho.c - mv nlecho.o $@ - -umask: umask.c bltin.h - $(CC) $(CFLAGS) -o $@ umask.c - -true: - > : - chmod 755 : - rm -f true - ln : true - -stalloc.o: ../shell.h - diff --git a/minix/commands/ash/bltin/mkexpr.sh b/minix/commands/ash/bltin/mkexpr.sh deleted file mode 100755 index c01483b50..000000000 --- a/minix/commands/ash/bltin/mkexpr.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -# Copyright 1989 by Kenneth Almquist. All rights reserved. -# -# This file is part of ash. Ash is distributed under the terms specified -# by the Ash General Public License. See the file named LICENSE. - -# All calls to awk removed, because Minix bawk is deficient. (kjb) - -if [ $# -ne 2 ] -then - echo "Usage: $0 " >&2 - exit 1 -fi -unary_op="$1" -binary_op="$2" - -exec > operators.h -i=0 -sed -e '/^[^#]/!d' "$unary_op" "$binary_op" | while read line -do - set -$- $line - echo "#define $1 $i" - i=`expr $i + 1` -done -echo -echo "#define FIRST_BINARY_OP" `sed -e '/^[^#]/!d' "$unary_op" | wc -l` -echo ' -#define OP_INT 1 /* arguments to operator are integer */ -#define OP_STRING 2 /* arguments to operator are string */ -#define OP_FILE 3 /* argument is a file name */ - -extern char *const unary_op[]; -extern char *const binary_op[]; -extern const char op_priority[]; -extern const char op_argflag[];' - -exec > operators.c -echo '/* - * Operators used in the expr/test command. - */ - -#include -#include "shell.h" -#include "operators.h" - -char *const unary_op[] = {' -sed -e '/^[^#]/!d - s/[ ][ ]*/ /g - s/^[^ ][^ ]* \([^ ][^ ]*\).*/ "\1",/ - ' "$unary_op" -echo ' NULL -}; - -char *const binary_op[] = {' -sed -e '/^[^#]/!d - s/[ ][ ]*/ /g - s/^[^ ][^ ]* \([^ ][^ ]*\).*/ "\1",/ - ' "$binary_op" -echo ' NULL -}; - -const char op_priority[] = {' -sed -e '/^[^#]/!d - s/[ ][ ]*/ /g - s/^[^ ][^ ]* [^ ][^ ]* \([^ ][^ ]*\).*/ \1,/ - ' "$unary_op" "$binary_op" -echo '}; - -const char op_argflag[] = {' -sed -e '/^[^#]/!d - s/[ ][ ]*/ /g - s/^[^ ][^ ]* [^ ][^ ]* [^ ][^ ]*$/& 0/ - s/^[^ ][^ ]* [^ ][^ ]* [^ ][^ ]* \([^ ][^ ]*\)/ \1,/ - ' "$unary_op" "$binary_op" -echo '};' diff --git a/minix/commands/ash/bltin/myregexp.h b/minix/commands/ash/bltin/myregexp.h deleted file mode 100644 index 83006a18a..000000000 --- a/minix/commands/ash/bltin/myregexp.h +++ /dev/null @@ -1,8 +0,0 @@ -/* -myregexp.h - -Created: July 1995 by Philip Homburg -*/ - -char *re_compile(char *pattern); -int re_match(char *pattern, char *string); diff --git a/minix/commands/ash/bltin/nlecho.c b/minix/commands/ash/bltin/nlecho.c deleted file mode 100644 index ccfb79288..000000000 --- a/minix/commands/ash/bltin/nlecho.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Echo the command argument to the standard output, one line at a time. - * This command is useful for debugging th shell and whenever you what - * to output strings literally. - * - * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. - * This file is part of ash, which is distributed under the terms specified - * by the Ash General Public License. See the file named LICENSE. - */ - - -#define main nlechocmd - -#include "bltin.h" - - -main(argc, argv) char **argv; { - register char **ap; - - for (ap = argv + 1 ; *ap ; ap++) { - fputs(*ap, stdout); - putchar('\n'); - } - return 0; -} diff --git a/minix/commands/ash/bltin/regexp.c b/minix/commands/ash/bltin/regexp.c deleted file mode 100644 index 7ef68ba1f..000000000 --- a/minix/commands/ash/bltin/regexp.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Regular expression matching for expr(1). Bugs: The upper bound of - * a range specified by the \{ feature cannot be zero. - * - * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. - * This file is part of ash, which is distributed under the terms specified - * by the Ash General Public License. See the file named LICENSE. - */ - -#include "bltin.h" -#include "myregexp.h" - -#include - -#define RE_END 0 /* end of regular expression */ -#define RE_LITERAL 1 /* normal character follows */ -#define RE_DOT 2 /* "." */ -#define RE_CCL 3 /* "[...]" */ -#define RE_NCCL 4 /* "[^...]" */ -#define RE_LP 5 /* "\(" */ -#define RE_RP 6 /* "\)" */ -#define RE_MATCHED 7 /* "\digit" */ -#define RE_EOS 8 /* "$" matches end of string */ -#define RE_STAR 9 /* "*" */ -#define RE_RANGE 10 /* "\{num,num\}" */ - - - -char *match_begin[10]; -short match_length[10]; -short number_parens; -static int match(char *pattern, char *string); - - - -char * -re_compile(pattern) - char *pattern; - { - register char *p; - register char c; - char *comp; - register char *q; - char *begin; - char *endp; - register int len; - int first; - int type; - char *stackp; - char stack[10]; - int paren_num; - int i; - - p = pattern; - if (*p == '^') - p++; - comp = q = malloc(2 * strlen(p) + 1); - begin = q; - stackp = stack; - paren_num = 0; - for (;;) { - switch (c = *p++) { - case '\0': - *q = '\0'; - goto out; - case '.': - *q++ = RE_DOT; - len = 1; - break; - case '[': - begin = q; - *q = RE_CCL; - if (*p == '^') { - *q = RE_NCCL; - p++; - } - q++; - first = 1; - while (*p != ']' || first == 1) { - if (p[1] == '-' && p[2] != ']') { - *q++ = '-'; - *q++ = p[0]; - *q++ = p[2]; - p += 3; - } else if (*p == '-') { - *q++ = '-'; - *q++ = '-'; - *q++ = '-'; - p++; - } else { - *q++ = *p++; - } - first = 0; - } - p++; - *q++ = '\0'; - len = q - begin; - break; - case '$': - if (*p != '\0') - goto dft; - *q++ = RE_EOS; - break; - case '*': - if (len == 0) - goto dft; - type = RE_STAR; -range: - i = (type == RE_RANGE)? 3 : 1; - endp = q + i; - begin = q - len; - do { - --q; - *(q + i) = *q; - } while (--len > 0); - q = begin; - *q++ = type; - if (type == RE_RANGE) { - i = 0; - while ((unsigned)(*p - '0') <= 9) - i = 10 * i + (*p++ - '0'); - *q++ = i; - if (*p != ',') { - *q++ = i; - } else { - p++; - i = 0; - while ((unsigned)(*p - '0') <= 9) - i = 10 * i + (*p++ - '0'); - *q++ = i; - } - if (*p != '\\' || *++p != '}') - error("RE error"); - p++; - } - q = endp; - break; - case '\\': - if ((c = *p++) == '(') { - if (++paren_num > 9) - error("RE error"); - *q++ = RE_LP; - *q++ = paren_num; - *stackp++ = paren_num; - len = 0; - } else if (c == ')') { - if (stackp == stack) - error("RE error"); - *q++ = RE_RP; - *q++ = *--stackp; - len = 0; - } else if (c == '{') { - type = RE_RANGE; - goto range; - } else if ((unsigned)(c - '1') < 9) { - /* should check validity here */ - *q++ = RE_MATCHED; - *q++ = c - '0'; - len = 2; - } else { - goto dft; - } - break; - default: -dft: *q++ = RE_LITERAL; - *q++ = c; - len = 2; - break; - } - } -out: - if (stackp != stack) - error("RE error"); - number_parens = paren_num; - return comp; -} - - - -int -re_match(pattern, string) - char *pattern; - char *string; - { - char **pp; - - match_begin[0] = string; - for (pp = &match_begin[1] ; pp <= &match_begin[9] ; pp++) - *pp = 0; - return match(pattern, string); -} - - - -static -match(pattern, string) - char *pattern; - char *string; - { - register char *p, *q; - int counting; - int low, high, count; - char *curpat; - char *start_count; - int negate; - int found; - char *r; - int len; - char c; - - p = pattern; - q = string; - counting = 0; - for (;;) { - if (counting) { - if (++count > high) - goto bad; - p = curpat; - } - switch (*p++) { - case RE_END: - match_length[0] = q - match_begin[0]; - return 1; - case RE_LITERAL: - if (*q++ != *p++) - goto bad; - break; - case RE_DOT: - if (*q++ == '\0') - goto bad; - break; - case RE_CCL: - negate = 0; - goto ccl; - case RE_NCCL: - negate = 1; -ccl: - found = 0; - c = *q++; - while (*p) { - if (*p == '-') { - if (c >= *++p && c <= *++p) - found = 1; - } else { - if (c == *p) - found = 1; - } - p++; - } - p++; - if (found == negate) - goto bad; - break; - case RE_LP: - match_begin[*p++] = q; - break; - case RE_RP: - match_length[*p] = q - match_begin[*p]; - p++; - break; - case RE_MATCHED: - r = match_begin[*p]; - len = match_length[*p++]; - while (--len >= 0) { - if (*q++ != *r++) - goto bad; - } - break; - case RE_EOS: - if (*q != '\0') - goto bad; - break; - case RE_STAR: - low = 0; - high = 32767; - goto range; - case RE_RANGE: - low = *p++; - high = *p++; - if (high == 0) - high = 32767; -range: - curpat = p; - start_count = q; - count = 0; - counting++; - break; - } - } -bad: - if (! counting) - return 0; - len = 1; - if (*curpat == RE_MATCHED) - len = match_length[curpat[1]]; - while (--count >= low) { - if (match(p, start_count + count * len)) - return 1; - } - return 0; -} diff --git a/minix/commands/ash/bltin/stalloc.c b/minix/commands/ash/bltin/stalloc.c deleted file mode 100644 index 381271ac5..000000000 --- a/minix/commands/ash/bltin/stalloc.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. - * This file is part of ash, which is distributed under the terms specified - * by the Ash General Public License. See the file named LICENSE. - */ - -#include "../shell.h" - - -void error(); -pointer malloc(); - - -pointer -stalloc(nbytes) { - register pointer p; - - if ((p = malloc(nbytes)) == NULL) - error("Out of space"); - return p; -} diff --git a/minix/commands/ash/bltin/umask.c b/minix/commands/ash/bltin/umask.c deleted file mode 100644 index 5c1f54bf2..000000000 --- a/minix/commands/ash/bltin/umask.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. - * This file is part of ash, which is distributed under the terms specified - * by the Ash General Public License. See the file named LICENSE. - */ - -#include - - -main(argc, argv) char **argv; { - int mask; - - if (argc > 1) { - fprintf(stderr, "umask: only builtin version of umask can set value\n"); - exit(2); - } - printf("%.4o\n", umask(0)); - return 0; -} diff --git a/minix/commands/ash/bltin/unary_op b/minix/commands/ash/bltin/unary_op deleted file mode 100644 index e7a0f3cb6..000000000 --- a/minix/commands/ash/bltin/unary_op +++ /dev/null @@ -1,24 +0,0 @@ -# List of unary operators used by test/expr. -# -# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. -# This file is part of ash, which is distributed under the terms specified -# by the Ash General Public License. See the file named LICENSE. - -NOT ! 3 -EXISTS -e 12 OP_FILE -ISREAD -r 12 OP_FILE -ISWRITE -w 12 OP_FILE -ISEXEC -x 12 OP_FILE -ISFILE -f 12 OP_FILE -ISDIR -d 12 OP_FILE -ISCHAR -c 12 OP_FILE -ISBLOCK -b 12 OP_FILE -ISFIFO -p 12 OP_FILE -ISSETUID -u 12 OP_FILE -ISSETGID -g 12 OP_FILE -ISSTICKY -k 12 OP_FILE -ISSLINK -h 12 OP_FILE -ISSIZE -s 12 OP_FILE -ISTTY -t 12 OP_INT -NULSTR -z 12 OP_STRING -STRLEN -n 12 OP_STRING diff --git a/minix/commands/ash/builtins b/minix/commands/ash/builtins deleted file mode 100644 index 57409b1fc..000000000 --- a/minix/commands/ash/builtins +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)builtins 8.1 (Berkeley) 5/31/93 - -# -# This file lists all the builtin commands. The first column is the name -# of a C routine. The -j flag, if present, specifies that this command -# is to be excluded from systems without job control. The rest of the line -# specifies the command name or names used to run the command. The entry -# for bltincmd, which is run when the user does not specify a command, must -# come first. -# -# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. -# This file is part of ash, which is distributed under the terms specified -# by the Ash General Public License. See the file named LICENSE. -# -# NOTE: bltincmd must come first! - -bltincmd command -#alloccmd alloc -bgcmd -j bg -breakcmd break continue -#catfcmd catf -cdcmd cd chdir -dotcmd . -echocmd echo -evalcmd eval -execcmd exec -exitcmd exit -expcmd exp let -exportcmd export readonly -#exprcmd expr test [ -histcmd fc -fgcmd -j fg -getoptscmd getopts -hashcmd hash -jobidcmd jobid -jobscmd jobs -#linecmd line -localcmd local -#nlechocmd nlecho -printfcmd printf -pwdcmd pwd -readcmd read -returncmd return -setcmd set -setvarcmd setvar -shiftcmd shift -trapcmd trap -truecmd : true -umaskcmd umask -unaliascmd unalias -unsetcmd unset -waitcmd wait -#foocmd foo -aliascmd alias diff --git a/minix/commands/ash/complete.c b/minix/commands/ash/complete.c deleted file mode 100644 index b7ebd30a8..000000000 --- a/minix/commands/ash/complete.c +++ /dev/null @@ -1,354 +0,0 @@ -/* -complete.c - -Created: July 1995 by Philip Homburg -*/ - -#include -#include -#include -#include -#include -#include -#include "myhistedit.h" -#include "shell.h" - -#include "complete.h" -#include "error.h" -#include "expand.h" -#include "nodes.h" -#include "memalloc.h" - -static char **getlist(EditLine *el, int *baselen, int *isdir); -static char **getlist_tilde(char *prefix); -static int vstrcmp(const void *v1, const void *v2); -static void print_list(char **list); -static int install_extra(EditLine *el, char **list, int baselen, int isdir); - -unsigned char complete(EditLine *el, int ch) -{ - struct stackmark mark; - const LineInfo *lf; - char **list; - int baselen, prefix, isdir; - - /* Direct the cursor the the end of the word. */ - for(;;) - { - lf = el_line(el); - if (lf->cursor < lf->lastchar && - !isspace((unsigned char)*lf->cursor)) - { - (*(char **)&lf->cursor)++; /* XXX */ - } - else - break; - } - - setstackmark(&mark); - list= getlist(el, &baselen, &isdir); - if (list) - { - prefix= install_extra(el, list, baselen, isdir); - el_push(el, "i"); - } - popstackmark(&mark); - if (list) - return CC_REFRESH; - else - return CC_ERROR; -} - -unsigned char complete_list(EditLine *el, int ch) -{ - struct stackmark mark; - char **list; - - setstackmark(&mark); - list= getlist(el, NULL, NULL); - if (list) - { - print_list(list); - re_goto_bottom(el); - } - popstackmark(&mark); - if (list) - { - return CC_REFRESH; - } - else - return CC_ERROR; -} - -unsigned char complete_or_list(EditLine *el, int ch) -{ - struct stackmark mark; - const LineInfo *lf; - char **list; - int baselen, prefix, isdir; - - setstackmark(&mark); - list= getlist(el, &baselen, &isdir); - if (list) - { - prefix= install_extra(el, list, baselen, isdir); - if (prefix == baselen) - { - print_list(list); - re_goto_bottom(el); - } - } - popstackmark(&mark); - if (list) - return CC_REFRESH; - else - return CC_ERROR; -} - -unsigned char complete_expand(EditLine *el, int ch) -{ - printf("complete_expand\n"); - return CC_ERROR; -} - -static char **getlist(EditLine *el, int *baselen, int *isdir) -{ - const LineInfo *lf; - const char *begin, *end; - char *dirnam, *basenam; - union node arg; - struct arglist arglist; - DIR *dir; - struct dirent *dirent; - int i, l, n; - char *p, **list; - struct strlist *slp, *nslp; - struct stat sb; - - lf = el_line(el); - - /* Try to find to begin and end of the word that we have to comple. */ - begin= lf->cursor; - while (begin > lf->buffer && !isspace((unsigned char)begin[-1])) - begin--; - end= lf->cursor; - while (end < lf->lastchar && !isspace((unsigned char)end[0])) - end++; - - *(const char **)&lf->cursor= end; /* XXX */ - - /* Copy the word to a string */ - dirnam= stalloc(end-begin+1); - strncpy(dirnam, begin, end-begin); - dirnam[end-begin]= '\0'; - - /* Cut the word in two pieces: a path and a (partial) component. */ - basenam= strrchr(dirnam, '/'); - if (basenam) - { - basenam++; - p= stalloc(strlen(basenam) + 1); - strcpy(p, basenam); - *basenam= '\0'; - basenam= p; - } - else - { - if (dirnam[0] == '~') - return getlist_tilde(dirnam); - basenam= dirnam; - dirnam= "./"; - } - if (baselen) - *baselen= strlen(basenam); - - arg.type= NARG; - arg.narg.next= NULL; - arg.narg.text= dirnam; - arg.narg.backquote= NULL; - arglist.list= NULL; - arglist.lastp= &arglist.list; - expandarg(&arg, &arglist, EXP_TILDE); - - INTOFF; - list= NULL; - dir= opendir(arglist.list->text); - if (dir) - { - slp= NULL; - n= 0; - l= strlen(basenam); - while(dirent= readdir(dir)) - { - if (strncmp(dirent->d_name, basenam, l) != 0) - continue; - if (l == 0 && dirent->d_name[0] == '.') - continue; - nslp= stalloc(sizeof(*nslp)); - nslp->next= slp; - slp= nslp; - slp->text= stalloc(strlen(dirent->d_name)+1); - strcpy(slp->text, dirent->d_name); - n++; - if (n == 1 && isdir != NULL) - { - /* Try to findout whether this entry is a - * file or a directory. - */ - p= stalloc(strlen(arglist.list->text) + - strlen(dirent->d_name) + 1); - strcpy(p, arglist.list->text); - strcat(p, dirent->d_name); - if (stat(p, &sb) == -1) - printf("stat '%s' failed: %s\n", - p, strerror(errno)); - if (stat(p, &sb) == 0 && S_ISDIR(sb.st_mode)) - *isdir= 1; - else - *isdir= 0; - } - } - closedir(dir); - if (n != 0) - { - list= stalloc((n+1)*sizeof(*list)); - for(i= 0; slp; i++, slp= slp->next) - list[i]= slp->text; - if (i != n) - error("complete'make_list: i != n"); - list[i]= NULL; - qsort(list, n, sizeof(*list), vstrcmp); - } - } - INTON; - return list; -} - -static char **getlist_tilde(char *prefix) -{ - printf("should ~-complete '%s'\n", prefix); - return NULL; -} - -static int vstrcmp(const void *v1, const void *v2) -{ - return strcmp(*(char **)v1, *(char **)v2); -} - -#define MAXCOLS 40 -#define SEPWIDTH 4 - -static void print_list(char **list) -{ - struct - { - int cols; - int start[MAXCOLS+1]; - int width[MAXCOLS]; - } best, next; - int e, i, j, l, n, o, cols, maxw, width; - int linewidth= 80; - - /* Count the number of entries. */ - for (n= 0; list[n]; n++) - ; /* do nothing */ - if (n == 0) - error("complete'print_list: n= 0"); - - /* Try to maximize the number of columns */ - for (cols= 1; cols<= MAXCOLS; cols++) - { - next.cols= cols; - - o= 0; - width= 0; - for(j= 0; j maxw) - maxw= l; - } - next.width[j]= maxw; - width += maxw; - o += e; - } - next.start[j]= o; - if (cols > 1 && width-SEPWIDTH>linewidth) - break; - best= next; - } - cols= best.cols; - e= best.start[1]; - printf("\n"); - for(i= 0; i0) - { - if (strncmp(list[0], *lp, l) != 0) - l--; - else - break; - } - } - if (l > baselen || list[1] == NULL) - { - p= stalloc(l-baselen+2); - strncpy(p, list[0]+baselen, l-baselen); - if (list[1] == NULL) - { - p[l-baselen]= isdir ? '/' : ' '; - p[l-baselen+1]= '\0'; - l++; - } - else - p[l-baselen]= '\0'; - if (el_insertstr(el, p) == -1) - return -1; - } - return l; -} - -/* - * $PchId: complete.c,v 1.2 2006/04/10 14:35:53 philip Exp $ - */ diff --git a/minix/commands/ash/complete.h b/minix/commands/ash/complete.h deleted file mode 100644 index af859f9eb..000000000 --- a/minix/commands/ash/complete.h +++ /dev/null @@ -1,14 +0,0 @@ -/* -complete.h - -Created: July 1995 by Philip Homburg -*/ - -unsigned char complete(EditLine *el, int ch); -unsigned char complete_list(EditLine *el, int ch); -unsigned char complete_or_list(EditLine *el, int ch); -unsigned char complete_expand(EditLine *el, int ch); - -/* - * $PchId: complete.h,v 1.1 2001/05/17 07:12:05 philip Exp $ - */ diff --git a/minix/commands/ash/errmsg.c b/minix/commands/ash/errmsg.c deleted file mode 100644 index 2a0303b66..000000000 --- a/minix/commands/ash/errmsg.c +++ /dev/null @@ -1,127 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)errmsg.c 8.1 (Berkeley) 5/31/93"; -#endif /* not lint */ - -#include "shell.h" -#include "output.h" -#include "errmsg.h" -#include - - -#define ALL (E_OPEN|E_CREAT|E_EXEC) - - -struct errname { - short errcode; /* error number */ - short action; /* operation which encountered the error */ - char *msg; /* text describing the error */ -}; - - -STATIC const struct errname errormsg[] = { - EINTR, ALL, "interrupted", - EACCES, ALL, "permission denied", - EIO, ALL, "I/O error", - ENOENT, E_OPEN, "no such file", - ENOENT, E_CREAT, "directory nonexistent", - ENOENT, E_EXEC, "not found", - ENOTDIR, E_OPEN, "no such file", - ENOTDIR, E_CREAT, "directory nonexistent", - ENOTDIR, E_EXEC, "not found", - EISDIR, ALL, "is a directory", -/* EMFILE, ALL, "too many open files", */ - ENFILE, ALL, "file table overflow", - ENOSPC, ALL, "file system full", -#ifdef EDQUOT - EDQUOT, ALL, "disk quota exceeded", -#endif -#ifdef ENOSR - ENOSR, ALL, "no streams resources", -#endif - ENXIO, ALL, "no such device or address", - EROFS, ALL, "read-only file system", - ETXTBSY, ALL, "text busy", -#ifdef SYSV - EAGAIN, E_EXEC, "not enough memory", -#endif - ENOMEM, ALL, "not enough memory", -#ifdef ENOLINK - ENOLINK, ALL, "remote access failed" -#endif -#ifdef EMULTIHOP - EMULTIHOP, ALL, "remote access failed", -#endif -#ifdef ECOMM - ECOMM, ALL, "remote access failed", -#endif -#ifdef ESTALE - ESTALE, ALL, "remote access failed", -#endif -#ifdef ETIMEDOUT - ETIMEDOUT, ALL, "remote access failed", -#endif -#ifdef ELOOP - ELOOP, ALL, "symbolic link loop", -#endif - E2BIG, E_EXEC, "argument list too long", -#ifdef ELIBACC - ELIBACC, E_EXEC, "shared library missing", -#endif - 0, 0, NULL -}; - - -/* - * Return a string describing an error. The returned string may be a - * pointer to a static buffer that will be overwritten on the next call. - * Action describes the operation that got the error. - */ - -char * -errmsg(e, action) { - struct errname const *ep; - static char buf[12]; - - for (ep = errormsg ; ep->errcode ; ep++) { - if (ep->errcode == e && (ep->action & action) != 0) - return ep->msg; - } - fmtstr(buf, sizeof buf, "error %d", e); - return buf; -} diff --git a/minix/commands/ash/errmsg.h b/minix/commands/ash/errmsg.h deleted file mode 100644 index 03cb605ec..000000000 --- a/minix/commands/ash/errmsg.h +++ /dev/null @@ -1,47 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)errmsg.h 8.1 (Berkeley) 5/31/93 - */ - -#define E_OPEN 01 -#define E_CREAT 02 -#define E_EXEC 04 - -#ifdef __STDC__ -char *errmsg(int, int); -#else -char *errmsg(); -#endif diff --git a/minix/commands/ash/error.c b/minix/commands/ash/error.c deleted file mode 100644 index a56d0f228..000000000 --- a/minix/commands/ash/error.c +++ /dev/null @@ -1,182 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)error.c 8.2 (Berkeley) 5/4/95"; -#endif -#endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/error.c,v 1.25 2004/04/06 20:06:51 markm Exp $"); -*/ - -/* - * Errors and exceptions. - */ - -#include "shell.h" -#include "main.h" -#include "options.h" -#include "output.h" -#include "error.h" -#include "trap.h" -#include -#include -#include -#include - - -/* - * Code to handle exceptions in C. - */ - -struct jmploc *handler; -volatile sig_atomic_t exception; -volatile sig_atomic_t suppressint; -volatile sig_atomic_t intpending; -char *commandname; - - -static void exverror(int, const char *, va_list) __printf0like(2, 0); - -/* - * Called to raise an exception. Since C doesn't include exceptions, we - * just do a longjmp to the exception handler. The type of exception is - * stored in the global variable "exception". - */ - -void -exraise(int e) -{ - if (handler == NULL) - abort(); - exception = e; - longjmp(handler->loc, 1); -} - - -/* - * Called from trap.c when a SIGINT is received. (If the user specifies - * that SIGINT is to be trapped or ignored using the trap builtin, then - * this routine is not called.) Suppressint is nonzero when interrupts - * are held using the INTOFF macro. If SIGINTs are not suppressed and - * the shell is not a root shell, then we want to be terminated if we - * get here, as if we were terminated directly by a SIGINT. Arrange for - * this here. - */ - -void -onint(void) -{ - sigset_t sigset; - - /* - * The !in_dotrap here is safe. The only way we can arrive here - * with in_dotrap set is that a trap handler set SIGINT to SIG_DFL - * and killed itself. - */ - - if (suppressint && !in_dotrap) { - intpending++; - return; - } - intpending = 0; - sigemptyset(&sigset); - sigprocmask(SIG_SETMASK, &sigset, NULL); - - /* - * This doesn't seem to be needed, since main() emits a newline. - */ -#if 0 - if (tcgetpgrp(0) == getpid()) - write(STDERR_FILENO, "\n", 1); -#endif - if (rootshell && iflag) - exraise(EXINT); - else { - signal(SIGINT, SIG_DFL); - kill(getpid(), SIGINT); - } -} - - -/* - * Exverror is called to raise the error exception. If the first argument - * is not NULL then error prints an error message using printf style - * formatting. It then raises the error exception. - */ -static void -exverror(int cond, const char *msg, va_list ap) -{ - CLEAR_PENDING_INT; - INTOFF; - -#if DEBUG - if (msg) - TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid())); - else - TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); -#endif - if (msg) { - if (commandname) - outfmt(&errout, "%s: ", commandname); - doformat(&errout, msg, ap); - out2c('\n'); - } - flushall(); - exraise(cond); -} - - -void -error(const char *msg, ...) -{ - va_list ap; - va_start(ap, msg); - exverror(EXERROR, msg, ap); - va_end(ap); -} - - -void -exerror(int cond, const char *msg, ...) -{ - va_list ap; - va_start(ap, msg); - exverror(cond, msg, ap); - va_end(ap); -} - -/* - * $PchId: error.c,v 1.5 2006/04/10 14:36:23 philip Exp $ - */ diff --git a/minix/commands/ash/jobs.c b/minix/commands/ash/jobs.c deleted file mode 100644 index 8333f8b90..000000000 --- a/minix/commands/ash/jobs.c +++ /dev/null @@ -1,1300 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; -#endif -#endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/jobs.c,v 1.67 2004/04/06 20:06:51 markm Exp $"); -*/ - -#include "shell.h" - -#include -#include -#include -#include -#ifndef NO_PATHS_H -#include -#endif -#include -#include -#ifdef POSIX -#include -#include -#elif defined(BSD) -#include -#include -#include -#include -#endif -#include - -#if JOBS -#include -#undef CEOF /* syntax.h redefines this */ -#endif -#include "redir.h" -#include "show.h" -#include "main.h" -#include "parser.h" -#include "nodes.h" -#include "jobs.h" -#include "options.h" -#include "trap.h" -#include "syntax.h" -#include "input.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "mystring.h" -#include "builtins.h" - -#ifdef __minix -/* #define NO_KILLPG */ -#endif - -#ifndef _PATH_TTY -#define _PATH_TTY "/dev/tty" -#endif -#ifndef _PATH_DEVNULL -#define _PATH_DEVNULL "/dev/null" -#endif - -STATIC struct job *jobtab; /* array of jobs */ -STATIC int njobs; /* size of array */ -MKINIT pid_t backgndpid = -1; /* pid of last background process */ -#if JOBS -STATIC struct job *jobmru; /* most recently used job list */ -STATIC pid_t initialpgrp; /* pgrp of shell on invocation */ -#endif -int in_waitcmd = 0; /* are we in waitcmd()? */ -int in_dowait = 0; /* are we in dowait()? */ -volatile sig_atomic_t breakwaitcmd = 0; /* should wait be terminated? */ -#if JOBS -static int ttyfd = -1; -#endif - -#ifndef WCOREDUMP -#define WCOREDUMP(s) ((s) & 0x80) -#endif - -#if JOBS -STATIC void restartjob(struct job *); -#endif -STATIC void freejob(struct job *); -STATIC struct job *getjob(char *); -STATIC pid_t dowait(int, struct job *); -STATIC pid_t waitproc(int, int *); -STATIC void cmdtxt(union node *); -STATIC void cmdputs(char *); -#if JOBS -STATIC void setcurjob(struct job *); -STATIC void deljob(struct job *); -STATIC struct job *getcurjob(struct job *); -#endif -STATIC void showjob(struct job *, pid_t, int, int); - -#ifdef NO_KILLPG -static int killpg(pid_t,int); -#endif - -/* - * Turn job control on and off. - */ - -MKINIT int jobctl; - -#if JOBS -void -setjobctl(int on) -{ - int i; - - if (on == jobctl || rootshell == 0) - return; - if (on) { - if (ttyfd != -1) - close(ttyfd); - if ((ttyfd = open(_PATH_TTY, O_RDWR)) < 0) { - i = 0; - while (i <= 2 && !isatty(i)) - i++; - if (i > 2 || (ttyfd = fcntl(i, F_DUPFD, 10)) < 0) - goto out; - } - if (ttyfd < 10) { - /* - * Keep our TTY file descriptor out of the way of - * the user's redirections. - */ - if ((i = fcntl(ttyfd, F_DUPFD, 10)) < 0) { - close(ttyfd); - ttyfd = -1; - goto out; - } - close(ttyfd); - ttyfd = i; - } - if (fcntl(ttyfd, F_SETFD, FD_CLOEXEC) < 0) { - close(ttyfd); - ttyfd = -1; - goto out; - } - do { /* while we are in the background */ - initialpgrp = tcgetpgrp(ttyfd); - if (initialpgrp < 0) { -out: out2str("sh: can't access tty; job control turned off\n"); - mflag = 0; - return; - } - if (initialpgrp == -1) - initialpgrp = getpgrp(); - else if (initialpgrp != getpgrp()) { - killpg(0, SIGTTIN); - continue; - } - } while (0); - setsignal(SIGTSTP); - setsignal(SIGTTOU); - setsignal(SIGTTIN); - setpgid(0, rootpid); - tcsetpgrp(ttyfd, rootpid); - } else { /* turning job control off */ - setpgid(0, initialpgrp); - tcsetpgrp(ttyfd, initialpgrp); - close(ttyfd); - ttyfd = -1; - setsignal(SIGTSTP); - setsignal(SIGTTOU); - setsignal(SIGTTIN); - } - jobctl = on; -} -#endif - - -#ifdef mkinit -INCLUDE -INCLUDE - -SHELLPROC { - backgndpid = -1; -#if JOBS - jobctl = 0; -#endif -} - -#endif - - - -#if JOBS -int -fgcmd(int argc __unused, char **argv) -{ - struct job *jp; - pid_t pgrp; - int status; - - jp = getjob(argv[1]); - if (jp->jobctl == 0) - error("job not created under job control"); - out1str(jp->ps[0].cmd); - out1c('\n'); - flushout(&output); - pgrp = jp->ps[0].pid; - tcsetpgrp(ttyfd, pgrp); - restartjob(jp); - jp->foreground = 1; - INTOFF; - status = waitforjob(jp, (int *)NULL); - INTON; - return status; -} - - -int -bgcmd(int argc, char **argv) -{ - char s[64]; - struct job *jp; - - do { - jp = getjob(*++argv); - if (jp->jobctl == 0) - error("job not created under job control"); - if (jp->state == JOBDONE) - continue; - restartjob(jp); - jp->foreground = 0; - fmtstr(s, 64, "[%td] ", jp - jobtab + 1); - out1str(s); - out1str(jp->ps[0].cmd); - out1c('\n'); - } while (--argc > 1); - return 0; -} - - -STATIC void -restartjob(struct job *jp) -{ - struct procstat *ps; - int i; - - if (jp->state == JOBDONE) - return; - setcurjob(jp); - INTOFF; - killpg(jp->ps[0].pid, SIGCONT); - for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { - if (WIFSTOPPED(ps->status)) { - ps->status = -1; - jp->state = 0; - } - } - INTON; -} -#endif - - -int -jobscmd(int argc, char *argv[]) -{ - char *id; - int ch, sformat, lformat; - - optind = optreset = 1; - opterr = 0; - sformat = lformat = 0; - while ((ch = getopt(argc, argv, "ls")) != -1) { - switch (ch) { - case 'l': - lformat = 1; - break; - case 's': - sformat = 1; - break; - case '?': - default: - error("unknown option: -%c", optopt); - } - } - argc -= optind; - argv += optind; - - if (argc == 0) - showjobs(0, sformat, lformat); - else - while ((id = *argv++) != NULL) - showjob(getjob(id), 0, sformat, lformat); - - return (0); -} - -STATIC void -showjob(struct job *jp, pid_t pid, int sformat, int lformat) -{ - char s[64]; - struct procstat *ps; -#if JOBS - struct job *j; -#endif - int col, curr, i, jobno, prev, procno; - char c; - - procno = jp->nprocs; - jobno = jp - jobtab + 1; - curr = prev = 0; -#if JOBS - if ((j = getcurjob(NULL)) != NULL) { - curr = j - jobtab + 1; - if ((j = getcurjob(j)) != NULL) - prev = j - jobtab + 1; - } -#endif - for (ps = jp->ps ; ; ps++) { /* for each process */ - if (sformat) { - out1fmt("%d\n", (int)ps->pid); - goto skip; - } - if (!lformat && ps != jp->ps && pid == 0) - goto skip; - if (pid != 0 && pid != ps->pid) - goto skip; - if (jobno == curr && ps == jp->ps) - c = '+'; - else if (jobno == prev && ps == jp->ps) - c = '-'; - else - c = ' '; - if (ps == jp->ps) - fmtstr(s, 64, "[%d] %c ", jobno, c); - else - fmtstr(s, 64, " %c ", c); - out1str(s); - col = strlen(s); - if (lformat) { - fmtstr(s, 64, "%d ", (int)ps->pid); - out1str(s); - col += strlen(s); - } - s[0] = '\0'; - if (ps != jp->ps) { - *s = '\0'; - } else if (ps->status == -1) { - strcpy(s, "Running"); - } else if (WIFEXITED(ps->status)) { - if (WEXITSTATUS(ps->status) == 0) - strcpy(s, "Done"); - else - fmtstr(s, 64, "Done (%d)", - WEXITSTATUS(ps->status)); - } else { -#if JOBS - if (WIFSTOPPED(ps->status)) - i = WSTOPSIG(ps->status); - else -#endif - i = WTERMSIG(ps->status); - if ((i & 0x7F) < _NSIG && strsiglist(i & 0x7F)) - scopy(strsiglist(i & 0x7F), s); - else - fmtstr(s, 64, "Signal %d", i & 0x7F); - if (WCOREDUMP(ps->status)) - strcat(s, " (core dumped)"); - } - out1str(s); - col += strlen(s); - do { - out1c(' '); - col++; - } while (col < 30); - out1str(ps->cmd); - out1c('\n'); -skip: if (--procno <= 0) - break; - } -} - -/* - * Print a list of jobs. If "change" is nonzero, only print jobs whose - * statuses have changed since the last call to showjobs. - * - * If the shell is interrupted in the process of creating a job, the - * result may be a job structure containing zero processes. Such structures - * will be freed here. - */ - -void -showjobs(int change, int sformat, int lformat) -{ - int jobno; - struct job *jp; - - TRACE(("showjobs(%d) called\n", change)); - while (dowait(0, (struct job *)NULL) > 0); - for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { - if (! jp->used) - continue; - if (jp->nprocs == 0) { - freejob(jp); - continue; - } - if (change && ! jp->changed) - continue; - showjob(jp, 0, sformat, lformat); - jp->changed = 0; - if (jp->state == JOBDONE) { - freejob(jp); - } - } -} - - -/* - * Mark a job structure as unused. - */ - -STATIC void -freejob(struct job *jp) -{ - struct procstat *ps; - int i; - - INTOFF; - for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { - if (ps->cmd != nullstr) - ckfree(ps->cmd); - } - if (jp->ps != &jp->ps0) - ckfree(jp->ps); - jp->used = 0; -#if JOBS - deljob(jp); -#endif - INTON; -} - - - -int -waitcmd(int argc, char **argv) -{ - struct job *job; - int status, retval; - struct job *jp; - - if (argc > 1) { - job = getjob(argv[1]); - } else { - job = NULL; - } - - /* - * Loop until a process is terminated or stopped, or a SIGINT is - * received. - */ - - in_waitcmd++; - do { - if (job != NULL) { - if (job->state) { - status = job->ps[job->nprocs - 1].status; - if (WIFEXITED(status)) - retval = WEXITSTATUS(status); -#if JOBS - else if (WIFSTOPPED(status)) - retval = WSTOPSIG(status) + 128; -#endif - else - retval = WTERMSIG(status) + 128; - if (! iflag) - freejob(job); - in_waitcmd--; - return retval; - } - } else { - for (jp = jobtab ; ; jp++) { - if (jp >= jobtab + njobs) { /* no running procs */ - in_waitcmd--; - return 0; - } - if (jp->used && jp->state == 0) - break; - } - } - } while (dowait(1, (struct job *)NULL) != -1); - in_waitcmd--; - - return 0; -} - - - -int -jobidcmd(int argc __unused, char **argv) -{ - struct job *jp; - int i; - - jp = getjob(argv[1]); - for (i = 0 ; i < jp->nprocs ; ) { - out1fmt("%d", (int)jp->ps[i].pid); - out1c(++i < jp->nprocs? ' ' : '\n'); - } - return 0; -} - - - -/* - * Convert a job name to a job structure. - */ - -STATIC struct job * -getjob(char *name) -{ - int jobno; - struct job *found, *jp; - pid_t pid; - int i; - - if (name == NULL) { -#if JOBS -currentjob: if ((jp = getcurjob(NULL)) == NULL) - error("No current job"); - return (jp); -#else - error("No current job"); -#endif - } else if (name[0] == '%') { - if (is_digit(name[1])) { - jobno = number(name + 1); - if (jobno > 0 && jobno <= njobs - && jobtab[jobno - 1].used != 0) - return &jobtab[jobno - 1]; -#if JOBS - } else if (name[1] == '%' && name[2] == '\0') { - goto currentjob; - } else if (name[1] == '+' && name[2] == '\0') { - goto currentjob; - } else if (name[1] == '-' && name[2] == '\0') { - if ((jp = getcurjob(NULL)) == NULL || - (jp = getcurjob(jp)) == NULL) - error("No previous job"); - return (jp); -#endif - } else if (name[1] == '?') { - found = NULL; - for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { - if (jp->used && jp->nprocs > 0 - && strstr(jp->ps[0].cmd, name + 2) != NULL) { - if (found) - error("%s: ambiguous", name); - found = jp; - } - } - if (found != NULL) - return (found); - } else { - found = NULL; - for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { - if (jp->used && jp->nprocs > 0 - && prefix(name + 1, jp->ps[0].cmd)) { - if (found) - error("%s: ambiguous", name); - found = jp; - } - } - if (found) - return found; - } - } else if (is_number(name)) { - pid = (pid_t)number(name); - for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { - if (jp->used && jp->nprocs > 0 - && jp->ps[jp->nprocs - 1].pid == pid) - return jp; - } - } - error("No such job: %s", name); - /*NOTREACHED*/ - return NULL; -} - - - -/* - * Return a new job structure, - */ - -struct job * -makejob(union node *node __unused, int nprocs) -{ - int i; - struct job *jp; - - for (i = njobs, jp = jobtab ; ; jp++) { - if (--i < 0) { - INTOFF; - if (njobs == 0) { - jobtab = ckmalloc(4 * sizeof jobtab[0]); -#if JOBS - jobmru = NULL; -#endif - } else { - jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); - memcpy(jp, jobtab, njobs * sizeof jp[0]); -#if JOBS - /* Relocate `next' pointers and list head */ - if (jobmru != NULL) - jobmru = &jp[jobmru - jobtab]; - for (i = 0; i < njobs; i++) - if (jp[i].next != NULL) - jp[i].next = &jp[jp[i].next - - jobtab]; -#endif - /* Relocate `ps' pointers */ - for (i = 0; i < njobs; i++) - if (jp[i].ps == &jobtab[i].ps0) - jp[i].ps = &jp[i].ps0; - ckfree(jobtab); - jobtab = jp; - } - jp = jobtab + njobs; - for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); - INTON; - break; - } - if (jp->used == 0) - break; - } - INTOFF; - jp->state = 0; - jp->used = 1; - jp->changed = 0; - jp->nprocs = 0; - jp->foreground = 0; -#if JOBS - jp->jobctl = jobctl; - jp->next = NULL; -#endif - if (nprocs > 1) { - jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); - } else { - jp->ps = &jp->ps0; - } - INTON; - TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, - jp - jobtab + 1)); - return jp; -} - -#if JOBS -STATIC void -setcurjob(struct job *cj) -{ - struct job *jp, *prev; - - for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) { - if (jp == cj) { - if (prev != NULL) - prev->next = jp->next; - else - jobmru = jp->next; - jp->next = jobmru; - jobmru = cj; - return; - } - } - cj->next = jobmru; - jobmru = cj; -} - -STATIC void -deljob(struct job *j) -{ - struct job *jp, *prev; - - for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) { - if (jp == j) { - if (prev != NULL) - prev->next = jp->next; - else - jobmru = jp->next; - return; - } - } -} - -/* - * Return the most recently used job that isn't `nj', and preferably one - * that is stopped. - */ -STATIC struct job * -getcurjob(struct job *nj) -{ - struct job *jp; - - /* Try to find a stopped one.. */ - for (jp = jobmru; jp != NULL; jp = jp->next) - if (jp->used && jp != nj && jp->state == JOBSTOPPED) - return (jp); - /* Otherwise the most recently used job that isn't `nj' */ - for (jp = jobmru; jp != NULL; jp = jp->next) - if (jp->used && jp != nj) - return (jp); - - return (NULL); -} - -#endif - -/* - * Fork of a subshell. If we are doing job control, give the subshell its - * own process group. Jp is a job structure that the job is to be added to. - * N is the command that will be evaluated by the child. Both jp and n may - * be NULL. The mode parameter can be one of the following: - * FORK_FG - Fork off a foreground process. - * FORK_BG - Fork off a background process. - * FORK_NOJOB - Like FORK_FG, but don't give the process its own - * process group even if job control is on. - * - * When job control is turned off, background processes have their standard - * input redirected to /dev/null (except for the second and later processes - * in a pipeline). - */ - -pid_t -forkshell(struct job *jp, union node *n, int mode) -{ - pid_t pid; - pid_t pgrp; - - TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n, - mode)); - INTOFF; - flushall(); - pid = fork(); - if (pid == -1) { - TRACE(("Fork failed, errno=%d\n", errno)); - INTON; - error("Cannot fork: %s", strerror(errno)); - } - if (pid == 0) { - struct job *p; - int wasroot; - int i; - - TRACE(("Child shell %d\n", (int)getpid())); - wasroot = rootshell; - rootshell = 0; - closescript(); - INTON; - clear_traps(); -#if JOBS - jobctl = 0; /* do job control only in root shell */ - if (wasroot && mode != FORK_NOJOB && mflag) { - if (jp == NULL || jp->nprocs == 0) - pgrp = getpid(); - else - pgrp = jp->ps[0].pid; - if (setpgid(0, pgrp) == 0 && mode == FORK_FG) { - /*** this causes superfluous TIOCSPGRPS ***/ - if (tcsetpgrp(ttyfd, pgrp) < 0) - error("tcsetpgrp failed, errno=%d", errno); - } - setsignal(SIGTSTP); - setsignal(SIGTTOU); - } else if (mode == FORK_BG) { - ignoresig(SIGINT); - ignoresig(SIGQUIT); - if ((jp == NULL || jp->nprocs == 0) && - ! fd0_redirected_p ()) { - close(0); - if (open(_PATH_DEVNULL, O_RDONLY) != 0) - error("Can't open %s: %s", - _PATH_DEVNULL, strerror(errno)); - } - } -#else - if (mode == FORK_BG) { - ignoresig(SIGINT); - ignoresig(SIGQUIT); - if ((jp == NULL || jp->nprocs == 0) && - ! fd0_redirected_p ()) { - close(0); - if (open(_PATH_DEVNULL, O_RDONLY) != 0) - error("Can't open %s: %s", - _PATH_DEVNULL, strerror(errno)); - } - } -#endif - INTOFF; - for (i = njobs, p = jobtab ; --i >= 0 ; p++) - if (p->used) - freejob(p); - INTON; - if (wasroot && iflag) { - setsignal(SIGINT); - setsignal(SIGQUIT); - setsignal(SIGTERM); - } - return pid; - } - if (rootshell && mode != FORK_NOJOB && mflag) { - if (jp == NULL || jp->nprocs == 0) - pgrp = pid; - else - pgrp = jp->ps[0].pid; -#if JOBS - setpgid(pid, pgrp); -#endif - } - if (mode == FORK_BG) - backgndpid = pid; /* set $! */ - if (jp) { - struct procstat *ps = &jp->ps[jp->nprocs++]; - ps->pid = pid; - ps->status = -1; - ps->cmd = nullstr; - if (iflag && rootshell && n) - ps->cmd = commandtext(n); - jp->foreground = mode == FORK_FG; -#if JOBS - setcurjob(jp); -#endif - } - INTON; - TRACE(("In parent shell: child = %d\n", (int)pid)); - return pid; -} - - - -/* - * Wait for job to finish. - * - * Under job control we have the problem that while a child process is - * running interrupts generated by the user are sent to the child but not - * to the shell. This means that an infinite loop started by an inter- - * active user may be hard to kill. With job control turned off, an - * interactive user may place an interactive program inside a loop. If - * the interactive program catches interrupts, the user doesn't want - * these interrupts to also abort the loop. The approach we take here - * is to have the shell ignore interrupt signals while waiting for a - * foreground process to terminate, and then send itself an interrupt - * signal if the child process was terminated by an interrupt signal. - * Unfortunately, some programs want to do a bit of cleanup and then - * exit on interrupt; unless these processes terminate themselves by - * sending a signal to themselves (instead of calling exit) they will - * confuse this approach. - */ - -int -waitforjob(struct job *jp, int *origstatus) -{ -#if JOBS - pid_t mypgrp = getpgrp(); -#endif - int status; - int st; - - INTOFF; - TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); - while (jp->state == 0) - if (dowait(1, jp) == -1) - dotrap(); -#if JOBS - if (jp->jobctl) { - if (tcsetpgrp(ttyfd, mypgrp) < 0) - error("tcsetpgrp failed, errno=%d\n", errno); - } - if (jp->state == JOBSTOPPED) - setcurjob(jp); -#endif - status = jp->ps[jp->nprocs - 1].status; - if (origstatus != NULL) - *origstatus = status; - /* convert to 8 bits */ - if (WIFEXITED(status)) - st = WEXITSTATUS(status); -#if JOBS - else if (WIFSTOPPED(status)) - st = WSTOPSIG(status) + 128; -#endif - else - st = WTERMSIG(status) + 128; - if (! JOBS || jp->state == JOBDONE) - freejob(jp); - if (int_pending()) { - if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) - kill(getpid(), SIGINT); - else - CLEAR_PENDING_INT; - } - INTON; - return st; -} - - - -/* - * Wait for a process to terminate. - */ - -STATIC pid_t -dowait(int block, struct job *job) -{ - pid_t pid; - int status, core; - struct procstat *sp; - struct job *jp; - struct job *thisjob; - int done; - int stopped; - int sig; - int i; - - in_dowait++; - TRACE(("dowait(%d) called\n", block)); - do { - pid = waitproc(block, &status); - TRACE(("wait returns %d, status=%d\n", (int)pid, status)); - } while ((pid == -1 && errno == EINTR && breakwaitcmd == 0) || - (pid > 0 && WIFSTOPPED(status) && !iflag)); - in_dowait--; - if (breakwaitcmd != 0) { - breakwaitcmd = 0; - return -1; - } - if (pid <= 0) - return pid; - INTOFF; - thisjob = NULL; - for (jp = jobtab ; jp < jobtab + njobs ; jp++) { - if (jp->used) { - done = 1; - stopped = 1; - for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { - if (sp->pid == -1) - continue; - if (sp->pid == pid) { - TRACE(("Changing status of proc %d from 0x%x to 0x%x\n", - (int)pid, sp->status, - status)); - sp->status = status; - thisjob = jp; - } - if (sp->status == -1) - stopped = 0; - else if (WIFSTOPPED(sp->status)) - done = 0; - } - if (stopped) { /* stopped or done */ - int state = done? JOBDONE : JOBSTOPPED; - if (jp->state != state) { - TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); - jp->state = state; -#if JOBS - if (done) - deljob(jp); -#endif - } - } - } - } - INTON; - if (! rootshell || ! iflag || (job && thisjob == job)) { - core = WCOREDUMP(status); -#if JOBS - if (WIFSTOPPED(status)) - sig = WSTOPSIG(status); - else -#endif - { - if (WIFEXITED(status)) - sig = 0; - else - sig = WTERMSIG(status); - } - if (sig != 0 && sig != SIGINT && sig != SIGPIPE) { - if (!mflag || - (thisjob->foreground && !WIFSTOPPED(status))) { - i = WTERMSIG(status); - if ((i & 0x7F) < _NSIG && strsiglist(i & 0x7F)) - out1str(strsiglist(i & 0x7F)); - else - out1fmt("Signal %d", i & 0x7F); - if (core) - out1str(" (core dumped)"); - out1c('\n'); - } else - showjob(thisjob, pid, 0, 0); - } - } else { - TRACE(("Not printing status, rootshell=%d, job=%p\n", rootshell, job)); - if (thisjob) - thisjob->changed = 1; - } - return pid; -} - - - -/* - * Do a wait system call. If job control is compiled in, we accept - * stopped processes. If block is zero, we return a value of zero - * rather than blocking. - */ -STATIC pid_t -waitproc(int block, int *status) -{ -#if POSIX - int flags; - -#if JOBS - flags = ((rootshell && is_interactive) ? WUNTRACED : 0); -#else - flags = 0; -#endif /* JOBS */ - return waitpid(-1, status, (block == 0 ? WNOHANG : 0) | flags); -#else /* !POSIX */ - /* Assume BSD */ - int flags; - -#if JOBS - flags = WUNTRACED; -#else - flags = 0; -#endif /* JOBS */ - if (block == 0) - flags |= WNOHANG; - return wait3(status, flags, (struct rusage *)NULL); -#endif /* POSIX */ -} - -/* - * return 1 if there are stopped jobs, otherwise 0 - */ -int job_warning = 0; -int -stoppedjobs(void) -{ - int jobno; - struct job *jp; - - if (job_warning) - return (0); - for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { - if (jp->used == 0) - continue; - if (jp->state == JOBSTOPPED) { - out2str("You have stopped jobs.\n"); - job_warning = 2; - return (1); - } - } - - return (0); -} - -/* - * Return a string identifying a command (to be printed by the - * jobs command. - */ - -STATIC char *cmdnextc; -STATIC int cmdnleft; -#define MAXCMDTEXT 200 - -char * -commandtext(union node *n) -{ - char *name; - - cmdnextc = name = ckmalloc(MAXCMDTEXT); - cmdnleft = MAXCMDTEXT - 4; - cmdtxt(n); - *cmdnextc = '\0'; - return name; -} - - -STATIC void -cmdtxt(union node *n) -{ - union node *np; - struct nodelist *lp; - char *p; - int i; - char s[2]; - - if (n == NULL) - return; - switch (n->type) { - case NSEMI: - cmdtxt(n->nbinary.ch1); - cmdputs("; "); - cmdtxt(n->nbinary.ch2); - break; - case NAND: - cmdtxt(n->nbinary.ch1); - cmdputs(" && "); - cmdtxt(n->nbinary.ch2); - break; - case NOR: - cmdtxt(n->nbinary.ch1); - cmdputs(" || "); - cmdtxt(n->nbinary.ch2); - break; - case NPIPE: - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - cmdtxt(lp->n); - if (lp->next) - cmdputs(" | "); - } - break; - case NSUBSHELL: - cmdputs("("); - cmdtxt(n->nredir.n); - cmdputs(")"); - break; - case NREDIR: - case NBACKGND: - cmdtxt(n->nredir.n); - break; - case NIF: - cmdputs("if "); - cmdtxt(n->nif.test); - cmdputs("; then "); - cmdtxt(n->nif.ifpart); - cmdputs("..."); - break; - case NWHILE: - cmdputs("while "); - goto until; - case NUNTIL: - cmdputs("until "); -until: - cmdtxt(n->nbinary.ch1); - cmdputs("; do "); - cmdtxt(n->nbinary.ch2); - cmdputs("; done"); - break; - case NFOR: - cmdputs("for "); - cmdputs(n->nfor.var); - cmdputs(" in ..."); - break; - case NCASE: - cmdputs("case "); - cmdputs(n->ncase.expr->narg.text); - cmdputs(" in ..."); - break; - case NDEFUN: - cmdputs(n->narg.text); - cmdputs("() ..."); - break; - case NCMD: - for (np = n->ncmd.args ; np ; np = np->narg.next) { - cmdtxt(np); - if (np->narg.next) - cmdputs(" "); - } - for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { - cmdputs(" "); - cmdtxt(np); - } - break; - case NARG: - cmdputs(n->narg.text); - break; - case NTO: - p = ">"; i = 1; goto redir; - case NAPPEND: - p = ">>"; i = 1; goto redir; - case NTOFD: - p = ">&"; i = 1; goto redir; - case NCLOBBER: - p = ">|"; i = 1; goto redir; - case NFROM: - p = "<"; i = 0; goto redir; - case NFROMTO: - p = "<>"; i = 0; goto redir; - case NFROMFD: - p = "<&"; i = 0; goto redir; -redir: - if (n->nfile.fd != i) { - s[0] = n->nfile.fd + '0'; - s[1] = '\0'; - cmdputs(s); - } - cmdputs(p); - if (n->type == NTOFD || n->type == NFROMFD) { - if (n->ndup.dupfd >= 0) - s[0] = n->ndup.dupfd + '0'; - else - s[0] = '-'; - s[1] = '\0'; - cmdputs(s); - } else { - cmdtxt(n->nfile.fname); - } - break; - case NHERE: - case NXHERE: - cmdputs("<<..."); - break; - default: - cmdputs("???"); - break; - } -} - - - -STATIC void -cmdputs(char *s) -{ - char *p, *q; - char c; - int subtype = 0; - - if (cmdnleft <= 0) - return; - p = s; - q = cmdnextc; - while ((c = *p++) != '\0') { - if (c == CTLESC) - *q++ = *p++; - else if (c == CTLVAR) { - *q++ = '$'; - if (--cmdnleft > 0) - *q++ = '{'; - subtype = *p++; - } else if (c == '=' && subtype != 0) { - *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL]; - subtype = 0; - } else if (c == CTLENDVAR) { - *q++ = '}'; - } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE) - cmdnleft++; /* ignore it */ - else - *q++ = c; - if (--cmdnleft <= 0) { - *q++ = '.'; - *q++ = '.'; - *q++ = '.'; - break; - } - } - cmdnextc = q; -} - -#ifdef NO_KILLPG -static int killpg(grp, sig) -pid_t grp; -int sig; -{ - return kill(-grp, sig); -} -#endif - -/* - * $PchId: jobs.c,v 1.7 2006/05/22 12:02:13 philip Exp $ - */ diff --git a/minix/commands/ash/mkinit.c b/minix/commands/ash/mkinit.c deleted file mode 100644 index 962631fc8..000000000 --- a/minix/commands/ash/mkinit.c +++ /dev/null @@ -1,500 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char const copyright[] = -"@(#) Copyright (c) 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)mkinit.c 8.2 (Berkeley) 5/4/95"; -#endif -#endif /* not lint */ -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/mkinit.c,v 1.17 2004/04/06 20:06:51 markm Exp $"); -*/ - -/* - * This program scans all the source files for code to handle various - * special events and combines this code into one file. This (allegedly) - * improves the structure of the program since there is no need for - * anyone outside of a module to know that that module performs special - * operations on particular events. - * - * Usage: mkinit sourcefile... - */ - - -#include -#include -#include -#include -#include -#include -#include - -/* - * OUTFILE is the name of the output file. Output is initially written - * to the file OUTTEMP, which is then moved to OUTFILE. - */ - -#define OUTFILE "init.c" -#define OUTTEMP "init.c.new" - - -/* - * A text structure is basicly just a string that grows as more characters - * are added onto the end of it. It is implemented as a linked list of - * blocks of characters. The routines addstr and addchar append a string - * or a single character, respectively, to a text structure. Writetext - * writes the contents of a text structure to a file. - */ - -#define BLOCKSIZE 512 - -struct text { - char *nextc; - int nleft; - struct block *start; - struct block *last; -}; - -struct block { - struct block *next; - char text[BLOCKSIZE]; -}; - - -/* - * There is one event structure for each event that mkinit handles. - */ - -struct event { - char *name; /* name of event (e.g. INIT) */ - char *routine; /* name of routine called on event */ - char *comment; /* comment describing routine */ - struct text code; /* code for handling event */ -}; - - -char writer[] = "\ -/*\n\ - * This file was generated by the mkinit program.\n\ - */\n\ -\n"; - -char init[] = "\ -/*\n\ - * Initialization code.\n\ - */\n"; - -char reset[] = "\ -/*\n\ - * This routine is called when an error or an interrupt occurs in an\n\ - * interactive shell and control is returned to the main command loop.\n\ - */\n"; - -char shellproc[] = "\ -/*\n\ - * This routine is called to initialize the shell to run a shell procedure.\n\ - */\n"; - - -struct event event[] = { - {"INIT", "init", init}, - {"RESET", "reset", reset}, - {"SHELLPROC", "initshellproc", shellproc}, - {NULL, NULL} -}; - - -char *curfile; /* current file */ -int linno; /* current line */ -char *header_files[200]; /* list of header files */ -struct text defines; /* #define statements */ -struct text decls; /* declarations */ -int amiddecls; /* for formatting */ - - -static void readfile(char *); -static int match(char *, char *); -static int gooddefine(char *); -static void doevent(struct event *, FILE *, char *); -static void doinclude(char *); -static void dodecl(char *, FILE *); -static void output(void); -static void addstr(char *, struct text *); -static void addchar(int, struct text *); -static void writetext(struct text *, FILE *); -static FILE *ckfopen(char *, char *); -static void *ckmalloc(int); -static char *savestr(char *); -static void error(char *); - -#define equal(s1, s2) (strcmp(s1, s2) == 0) - -#ifndef __unused -#define __unused __attribute__((__unused__)) -#endif - -int -main(int argc __unused, char *argv[]) -{ - char **ap; - - header_files[0] = "\"shell.h\""; - header_files[1] = "\"mystring.h\""; - for (ap = argv + 1 ; *ap ; ap++) - readfile(*ap); - output(); - rename(OUTTEMP, OUTFILE); - exit(0); -} - - -/* - * Parse an input file. - */ - -static void -readfile(char *fname) -{ - FILE *fp; - char line[1024]; - struct event *ep; - - fp = ckfopen(fname, "r"); - curfile = fname; - linno = 0; - amiddecls = 0; - while (fgets(line, sizeof line, fp) != NULL) { - linno++; - for (ep = event ; ep->name ; ep++) { - if (line[0] == ep->name[0] && match(ep->name, line)) { - doevent(ep, fp, fname); - break; - } - } - if (line[0] == 'I' && match("INCLUDE", line)) - doinclude(line); - if (line[0] == 'M' && match("MKINIT", line)) - dodecl(line, fp); - if (line[0] == '#' && gooddefine(line)) { - char *cp; - char line2[1024]; - static const char undef[] = "#undef "; - - strcpy(line2, line); - memcpy(line2, undef, sizeof(undef) - 1); - cp = line2 + sizeof(undef) - 1; - while(*cp && (*cp == ' ' || *cp == '\t')) - cp++; - while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n') - cp++; - *cp++ = '\n'; *cp = '\0'; - addstr(line2, &defines); - addstr(line, &defines); - } - } - fclose(fp); -} - - -static int -match(char *name, char *line) -{ - char *p, *q; - - p = name, q = line; - while (*p) { - if (*p++ != *q++) - return 0; - } - if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n') - return 0; - return 1; -} - - -static int -gooddefine(char *line) -{ - char *p; - - if (! match("#define", line)) - return 0; /* not a define */ - p = line + 7; - while (*p == ' ' || *p == '\t') - p++; - while (*p != ' ' && *p != '\t') { - if (*p == '(') - return 0; /* macro definition */ - p++; - } - while (*p != '\n' && *p != '\0') - p++; - if (p[-1] == '\\') - return 0; /* multi-line definition */ - return 1; -} - - -static void -doevent(struct event *ep, FILE *fp, char *fname) -{ - char line[1024]; - int indent; - char *p; - - sprintf(line, "\n /* from %s: */\n", fname); - addstr(line, &ep->code); - addstr(" {\n", &ep->code); - for (;;) { - linno++; - if (fgets(line, sizeof line, fp) == NULL) - error("Unexpected EOF"); - if (equal(line, "}\n")) - break; - indent = 6; - for (p = line ; *p == '\t' ; p++) - indent += 8; - for ( ; *p == ' ' ; p++) - indent++; - if (*p == '\n' || *p == '#') - indent = 0; - while (indent >= 8) { - addchar('\t', &ep->code); - indent -= 8; - } - while (indent > 0) { - addchar(' ', &ep->code); - indent--; - } - addstr(p, &ep->code); - } - addstr(" }\n", &ep->code); -} - - -static void -doinclude(char *line) -{ - char *p; - char *name; - char **pp; - - for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++); - if (*p == '\0') - error("Expecting '\"' or '<'"); - name = p; - while (*p != ' ' && *p != '\t' && *p != '\n') - p++; - if (p[-1] != '"' && p[-1] != '>') - error("Missing terminator"); - *p = '\0'; - - /* name now contains the name of the include file */ - for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++); - if (*pp == NULL) - *pp = savestr(name); -} - - -static void -dodecl(char *line1, FILE *fp) -{ - char line[1024]; - char *p, *q; - - if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */ - addchar('\n', &decls); - do { - linno++; - if (fgets(line, sizeof line, fp) == NULL) - error("Unterminated structure declaration"); - addstr(line, &decls); - } while (line[0] != '}'); - amiddecls = 0; - } else { - if (! amiddecls) - addchar('\n', &decls); - q = NULL; - for (p = line1 + 6 ; *p && strchr("=/\n", *p) == NULL; p++) - continue; - if (*p == '=') { /* eliminate initialization */ - for (q = p ; *q && *q != ';' ; q++); - if (*q == '\0') - q = NULL; - else { - while (p[-1] == ' ') - p--; - *p = '\0'; - } - } - addstr("extern", &decls); - addstr(line1 + 6, &decls); - if (q != NULL) - addstr(q, &decls); - amiddecls = 1; - } -} - - - -/* - * Write the output to the file OUTTEMP. - */ - -static void -output(void) -{ - FILE *fp; - char **pp; - struct event *ep; - - fp = ckfopen(OUTTEMP, "w"); - fputs(writer, fp); - for (pp = header_files ; *pp ; pp++) - fprintf(fp, "#include %s\n", *pp); - fputs("\n\n\n", fp); - writetext(&defines, fp); - fputs("\n\n", fp); - writetext(&decls, fp); - for (ep = event ; ep->name ; ep++) { - fputs("\n\n\n", fp); - fputs(ep->comment, fp); - fprintf(fp, "\nvoid\n%s(void) {\n", ep->routine); - writetext(&ep->code, fp); - fprintf(fp, "}\n"); - } - fclose(fp); -} - - -/* - * A text structure is simply a block of text that is kept in memory. - * Addstr appends a string to the text struct, and addchar appends a single - * character. - */ - -static void -addstr(char *s, struct text *text) -{ - while (*s) { - if (--text->nleft < 0) - addchar(*s++, text); - else - *text->nextc++ = *s++; - } -} - - -static void -addchar(int c, struct text *text) -{ - struct block *bp; - - if (--text->nleft < 0) { - bp = ckmalloc(sizeof *bp); - if (text->start == NULL) - text->start = bp; - else - text->last->next = bp; - text->last = bp; - text->nextc = bp->text; - text->nleft = BLOCKSIZE - 1; - } - *text->nextc++ = c; -} - -/* - * Write the contents of a text structure to a file. - */ -static void -writetext(struct text *text, FILE *fp) -{ - struct block *bp; - - if (text->start != NULL) { - for (bp = text->start ; bp != text->last ; bp = bp->next) - fwrite(bp->text, sizeof (char), BLOCKSIZE, fp); - fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp); - } -} - -static FILE * -ckfopen(char *file, char *mode) -{ - FILE *fp; - - if ((fp = fopen(file, mode)) == NULL) { - fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno)); - exit(2); - } - return fp; -} - -static void * -ckmalloc(int nbytes) -{ - char *p; - - if ((p = malloc(nbytes)) == NULL) - error("Out of space"); - return p; -} - -static char * -savestr(char *s) -{ - char *p; - - p = ckmalloc(strlen(s) + 1); - strcpy(p, s); - return p; -} - -static void -error(char *msg) -{ - if (curfile != NULL) - fprintf(stderr, "%s:%d: ", curfile, linno); - fprintf(stderr, "%s\n", msg); - exit(2); -} - -/* - * $PchId: mkinit.c,v 1.6 2006/05/22 12:16:50 philip Exp $ - */ diff --git a/minix/commands/ash/mknodes.c b/minix/commands/ash/mknodes.c deleted file mode 100644 index d9582d010..000000000 --- a/minix/commands/ash/mknodes.c +++ /dev/null @@ -1,455 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -#ifndef lint -static char const copyright[] = -"@(#) Copyright (c) 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)mknodes.c 8.2 (Berkeley) 5/4/95"; -#endif /* not lint */ -#endif -/* -__FBSDID("$FreeBSD: src/bin/sh/mknodes.c,v 1.17 2004/04/06 20:06:51 markm Exp $"); -*/ - -/* - * This program reads the nodetypes file and nodes.c.pat file. It generates - * the files nodes.h and nodes.c. - */ - -#include -#include -#include -#include -#include - -#define MAXTYPES 50 /* max number of node types */ -#define MAXFIELDS 20 /* max fields in a structure */ -#define BUFLEN 100 /* size of character buffers */ - -/* field types */ -#define T_NODE 1 /* union node *field */ -#define T_NODELIST 2 /* struct nodelist *field */ -#define T_STRING 3 -#define T_INT 4 /* int field */ -#define T_OTHER 5 /* other */ -#define T_TEMP 6 /* don't copy this field */ - - -struct field { /* a structure field */ - char *name; /* name of field */ - int type; /* type of field */ - char *decl; /* declaration of field */ -}; - - -struct str { /* struct representing a node structure */ - char *tag; /* structure tag */ - int nfields; /* number of fields in the structure */ - struct field field[MAXFIELDS]; /* the fields of the structure */ - int done; /* set if fully parsed */ -}; - - -static int ntypes; /* number of node types */ -static char *nodename[MAXTYPES]; /* names of the nodes */ -static struct str *nodestr[MAXTYPES]; /* type of structure used by the node */ -static int nstr; /* number of structures */ -static struct str str[MAXTYPES]; /* the structures */ -static struct str *curstr; /* current structure */ -static FILE *infp; -static char line[1024]; -static int linno; -static char *linep; - -#ifndef __printf0like -#define __printf0like(a,b) -#endif - -static void parsenode(void); -static void parsefield(void); -static void output(char *); -static void outsizes(FILE *); -static void outfunc(FILE *, int); -static void indent(int, FILE *); -static int nextfield(char *); -static void skipbl(void); -static int readline(void); -static void error(const char *, ...) __printf0like(1, 2); -static char *savestr(const char *); - - -int -main(int argc, char *argv[]) -{ - if (argc != 3) - error("usage: mknodes file"); - infp = stdin; - if ((infp = fopen(argv[1], "r")) == NULL) - error("Can't open %s: %s", argv[1], strerror(errno)); - while (readline()) { - if (line[0] == ' ' || line[0] == '\t') - parsefield(); - else if (line[0] != '\0') - parsenode(); - } - output(argv[2]); - exit(0); -} - - - -static void -parsenode(void) -{ - char name[BUFLEN]; - char tag[BUFLEN]; - struct str *sp; - - if (curstr && curstr->nfields > 0) - curstr->done = 1; - nextfield(name); - if (! nextfield(tag)) - error("Tag expected"); - if (*linep != '\0') - error("Garbage at end of line"); - nodename[ntypes] = savestr(name); - for (sp = str ; sp < str + nstr ; sp++) { - if (strcmp(sp->tag, tag) == 0) - break; - } - if (sp >= str + nstr) { - sp->tag = savestr(tag); - sp->nfields = 0; - curstr = sp; - nstr++; - } - nodestr[ntypes] = sp; - ntypes++; -} - - -static void -parsefield(void) -{ - char name[BUFLEN]; - char type[BUFLEN]; - char decl[2 * BUFLEN]; - struct field *fp; - - if (curstr == NULL || curstr->done) - error("No current structure to add field to"); - if (! nextfield(name)) - error("No field name"); - if (! nextfield(type)) - error("No field type"); - fp = &curstr->field[curstr->nfields]; - fp->name = savestr(name); - if (strcmp(type, "nodeptr") == 0) { - fp->type = T_NODE; - sprintf(decl, "union node *%s", name); - } else if (strcmp(type, "nodelist") == 0) { - fp->type = T_NODELIST; - sprintf(decl, "struct nodelist *%s", name); - } else if (strcmp(type, "string") == 0) { - fp->type = T_STRING; - sprintf(decl, "char *%s", name); - } else if (strcmp(type, "int") == 0) { - fp->type = T_INT; - sprintf(decl, "int %s", name); - } else if (strcmp(type, "other") == 0) { - fp->type = T_OTHER; - } else if (strcmp(type, "temp") == 0) { - fp->type = T_TEMP; - } else { - error("Unknown type %s", type); - } - if (fp->type == T_OTHER || fp->type == T_TEMP) { - skipbl(); - fp->decl = savestr(linep); - } else { - if (*linep) - error("Garbage at end of line"); - fp->decl = savestr(decl); - } - curstr->nfields++; -} - - -char writer[] = "\ -/*\n\ - * This file was generated by the mknodes program.\n\ - */\n\ -\n"; - -static void -output(char *file) -{ - FILE *hfile; - FILE *cfile; - FILE *patfile; - int i; - struct str *sp; - struct field *fp; - char *p; - - if ((patfile = fopen(file, "r")) == NULL) - error("Can't open %s: %s", file, strerror(errno)); - if ((hfile = fopen("nodes.h", "w")) == NULL) - error("Can't create nodes.h: %s", strerror(errno)); - if ((cfile = fopen("nodes.c", "w")) == NULL) - error("Can't create nodes.c"); - fputs(writer, hfile); - for (i = 0 ; i < ntypes ; i++) - fprintf(hfile, "#define %s %d\n", nodename[i], i); - fputs("\n\n\n", hfile); - for (sp = str ; sp < &str[nstr] ; sp++) { - fprintf(hfile, "struct %s {\n", sp->tag); - for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) { - fprintf(hfile, " %s;\n", fp->decl); - } - fputs("};\n\n\n", hfile); - } - fputs("union node {\n", hfile); - fprintf(hfile, " int type;\n"); - for (sp = str ; sp < &str[nstr] ; sp++) { - fprintf(hfile, " struct %s %s;\n", sp->tag, sp->tag); - } - fputs("};\n\n\n", hfile); - fputs("struct nodelist {\n", hfile); - fputs("\tstruct nodelist *next;\n", hfile); - fputs("\tunion node *n;\n", hfile); - fputs("};\n\n\n", hfile); - fputs("union node *copyfunc(union node *);\n", hfile); - fputs("void freefunc(union node *);\n", hfile); - - fputs(writer, cfile); - while (fgets(line, sizeof line, patfile) != NULL) { - for (p = line ; *p == ' ' || *p == '\t' ; p++); - if (strcmp(p, "%SIZES\n") == 0) - outsizes(cfile); - else if (strcmp(p, "%CALCSIZE\n") == 0) - outfunc(cfile, 1); - else if (strcmp(p, "%COPY\n") == 0) - outfunc(cfile, 0); - else - fputs(line, cfile); - } -} - - - -static void -outsizes(FILE *cfile) -{ - int i; - - fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes); - for (i = 0 ; i < ntypes ; i++) { - fprintf(cfile, " ALIGN(sizeof (struct %s)),\n", nodestr[i]->tag); - } - fprintf(cfile, "};\n"); -} - - -static void -outfunc(FILE *cfile, int calcsize) -{ - struct str *sp; - struct field *fp; - int i; - - fputs(" if (n == NULL)\n", cfile); - if (calcsize) - fputs(" return;\n", cfile); - else - fputs(" return NULL;\n", cfile); - if (calcsize) - fputs(" funcblocksize += nodesize[n->type];\n", cfile); - else { - fputs(" new = funcblock;\n", cfile); - fputs(" funcblock = (char *)funcblock + nodesize[n->type];\n", cfile); - } - fputs(" switch (n->type) {\n", cfile); - for (sp = str ; sp < &str[nstr] ; sp++) { - for (i = 0 ; i < ntypes ; i++) { - if (nodestr[i] == sp) - fprintf(cfile, " case %s:\n", nodename[i]); - } - for (i = sp->nfields ; --i >= 1 ; ) { - fp = &sp->field[i]; - switch (fp->type) { - case T_NODE: - if (calcsize) { - indent(12, cfile); - fprintf(cfile, "calcsize(n->%s.%s);\n", - sp->tag, fp->name); - } else { - indent(12, cfile); - fprintf(cfile, "new->%s.%s = copynode(n->%s.%s);\n", - sp->tag, fp->name, sp->tag, fp->name); - } - break; - case T_NODELIST: - if (calcsize) { - indent(12, cfile); - fprintf(cfile, "sizenodelist(n->%s.%s);\n", - sp->tag, fp->name); - } else { - indent(12, cfile); - fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s);\n", - sp->tag, fp->name, sp->tag, fp->name); - } - break; - case T_STRING: - if (calcsize) { - indent(12, cfile); - fprintf(cfile, "funcstringsize += strlen(n->%s.%s) + 1;\n", - sp->tag, fp->name); - } else { - indent(12, cfile); - fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s);\n", - sp->tag, fp->name, sp->tag, fp->name); - } - break; - case T_INT: - case T_OTHER: - if (! calcsize) { - indent(12, cfile); - fprintf(cfile, "new->%s.%s = n->%s.%s;\n", - sp->tag, fp->name, sp->tag, fp->name); - } - break; - } - } - indent(12, cfile); - fputs("break;\n", cfile); - } - fputs(" };\n", cfile); - if (! calcsize) - fputs(" new->type = n->type;\n", cfile); -} - - -static void -indent(int amount, FILE *fp) -{ - while (amount >= 8) { - putc('\t', fp); - amount -= 8; - } - while (--amount >= 0) { - putc(' ', fp); - } -} - - -static int -nextfield(char *buf) -{ - char *p, *q; - - p = linep; - while (*p == ' ' || *p == '\t') - p++; - q = buf; - while (*p != ' ' && *p != '\t' && *p != '\0') - *q++ = *p++; - *q = '\0'; - linep = p; - return (q > buf); -} - - -static void -skipbl(void) -{ - while (*linep == ' ' || *linep == '\t') - linep++; -} - - -static int -readline(void) -{ - char *p; - - if (fgets(line, 1024, infp) == NULL) - return 0; - for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++); - while (p > line && (p[-1] == ' ' || p[-1] == '\t')) - p--; - *p = '\0'; - linep = line; - linno++; - if (p - line > BUFLEN) - error("Line too long"); - return 1; -} - - - -static void -error(const char *msg, ...) -{ - va_list va; - va_start(va, msg); - - (void) fprintf(stderr, "line %d: ", linno); - (void) vfprintf(stderr, msg, va); - (void) fputc('\n', stderr); - - va_end(va); - - exit(2); -} - - - -static char * -savestr(const char *s) -{ - char *p; - - if ((p = malloc(strlen(s) + 1)) == NULL) - error("Out of space"); - (void) strcpy(p, s); - return p; -} - -/* - * $PchId: mknodes.c,v 1.6 2006/05/23 12:05:14 philip Exp $ - */ diff --git a/minix/commands/ash/mksignames.c b/minix/commands/ash/mksignames.c deleted file mode 100644 index b78786133..000000000 --- a/minix/commands/ash/mksignames.c +++ /dev/null @@ -1,204 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char copyright[] = -"@(#) Copyright (c) 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)mksignames.c 8.1 (Berkeley) 5/31/93"; -#endif /* not lint */ - -/* - * This program generates the signames.h and signames.c files. - */ -#include -#include -#include - -int main(int argc, char *argv[]); - -struct sig { - int signo; /* signal number */ - char *name; /* signal name (without leading "SIG") */ - char *mesg; /* description */ -}; - - -struct sig sigtab[] = { - SIGHUP, "HUP", "Hangup", - SIGINT, "INT", "Interrupt", /* normally don't print message */ - SIGQUIT, "QUIT", "Quit", - SIGILL, "ILL", "Illegal instruction", - SIGTRAP, "TRAP", "Trace/BPT trap", -#ifdef SIGABRT - SIGABRT, "ABRT", "abort", -#endif -#if defined(SIGIOT) && (! defined(SIGABRT) || SIGABRT != SIGIOT) - SIGIOT, "IOT", "abort", -#endif -#ifdef SIGEMT - SIGEMT, "EMT", "EMT trap", -#endif - SIGFPE, "FPE", "Floating exception", - SIGKILL, "KILL", "Killed", - SIGBUS, "BUS", "Bus error", - SIGSEGV, "SEGV", "Memory fault", -#ifdef SIGSYS - SIGSYS, "SYS", "Bad system call", -#endif - SIGPIPE, "PIPE", "Broken pipe", /* normally don't print message */ - SIGALRM, "ALRM", "Alarm call", - SIGTERM, "TERM", "Terminated", -#ifdef SIGUSR1 - SIGUSR1, "USR1", "User signal 1", -#endif -#ifdef SIGUSR2 - SIGUSR2, "USR2", "User signal 2", -#endif -#ifdef SIGCLD - SIGCLD, "CLD", NULL, -#endif -#if defined(SIGCHLD) && ! defined(SIGCLD) - SIGCHLD, "CLD", NULL, -#endif -#ifdef SIGPWR - SIGPWR, "PWR", "Power fail", -#endif -#ifdef SIGPOLL - SIGPOLL, "POLL", "Poll", -#endif - /* Now for the BSD signals */ -#ifdef SIGURG - SIGURG, "URG", NULL, -#endif -#ifdef SIGSTOP - SIGSTOP, "STOP", "Stopped", -#endif -#ifdef SIGTSTP - SIGTSTP, "TSTP", "Stopped", -#endif -#ifdef SIGCONT - SIGCONT, "CONT", NULL, -#endif -#ifdef SIGTTIN - SIGTTIN, "TTIN", "Stopped (input)", -#endif -#ifdef SIGTTOU - SIGTTOU, "TTOU", "Stopped (output)", -#endif -#ifdef SIGIO - SIGIO, "IO", NULL, -#endif -#ifdef SIGXCPU - SIGXCPU, "XCPU", "Time limit exceeded", -#endif -#ifdef SIGXFSZ - SIGXFSZ, "XFSZ", NULL, -#endif -#ifdef SIGVTALRM - SIGVTALRM, "VTALARM", "Virtual alarm", -#endif -#ifdef SIGPROF - SIGPROF, "PROF", "Profiling alarm", -#endif -#ifdef SIGWINCH - SIGWINCH, "WINCH", NULL, -#endif - 0, NULL, NULL -}; - - -#define MAXSIG 64 - - -char *sigmesg[MAXSIG + 1]; - - -char writer[] = "\ -/*\n\ - * This file was generated by the mksignames program.\n\ - */\n\ -\n"; - - - -main(argc, argv) char **argv; { - FILE *cfile, *hfile; - struct sig *sigp; - int maxsig; - int i; - - if ((cfile = fopen("signames.c", "w")) == NULL) { - fputs("Can't create signames.c\n", stderr); - exit(2); - } - if ((hfile = fopen("signames.h", "w")) == NULL) { - fputs("Can't create signames.h\n", stderr); - exit(2); - } - maxsig = 0; - for (sigp = sigtab ; sigp->signo != 0 ; sigp++) { - if (sigp->signo < 0 || sigp->signo > MAXSIG) - continue; - sigmesg[sigp->signo] = sigp->mesg; - if (maxsig < sigp->signo) - maxsig = sigp->signo; - } - - fputs(writer, hfile); - fprintf(hfile, "#define MAXSIG %d\n\n", maxsig); - fprintf(hfile, "extern char *const sigmesg[MAXSIG+1];\n"); - - fputs(writer, cfile); - fprintf(cfile, "#include \"shell.h\"\n\n"); - fprintf(cfile, "char *const sigmesg[%d] = {\n", maxsig + 1); - for (i = 0 ; i <= maxsig ; i++) { - if (sigmesg[i] == NULL) { - fprintf(cfile, " 0,\n"); - } else { - fprintf(cfile, " \"%s\",\n", sigmesg[i]); - } - } - fprintf(cfile, "};\n"); - exit(0); -} - -/* - * $PchId: mksignames.c,v 1.2 2001/05/14 19:22:26 philip Exp $ - */ diff --git a/minix/commands/ash/mksyntax.c b/minix/commands/ash/mksyntax.c deleted file mode 100644 index 40b825052..000000000 --- a/minix/commands/ash/mksyntax.c +++ /dev/null @@ -1,403 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -#ifndef lint -static char const copyright[] = -"@(#) Copyright (c) 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)mksyntax.c 8.2 (Berkeley) 5/4/95"; -#endif /* not lint */ -#endif -#include -/* -__FBSDID("$FreeBSD: src/bin/sh/mksyntax.c,v 1.23 2004/04/06 20:06:51 markm Exp $"); -*/ - -/* - * This program creates syntax.h and syntax.c. - */ - -#include -#include -#include -#include "parser.h" - -struct synclass { - char *name; - char *comment; -}; - -/* Syntax classes */ -struct synclass synclass[] = { - { "CWORD", "character is nothing special" }, - { "CNL", "newline character" }, - { "CBACK", "a backslash character" }, - { "CSQUOTE", "single quote" }, - { "CDQUOTE", "double quote" }, - { "CENDQUOTE", "a terminating quote" }, - { "CBQUOTE", "backwards single quote" }, - { "CVAR", "a dollar sign" }, - { "CENDVAR", "a '}' character" }, - { "CLP", "a left paren in arithmetic" }, - { "CRP", "a right paren in arithmetic" }, - { "CEOF", "end of file" }, - { "CCTL", "like CWORD, except it must be escaped" }, - { "CSPCL", "these terminate a word" }, - { NULL, NULL } -}; - - -/* - * Syntax classes for is_ functions. Warning: if you add new classes - * you may have to change the definition of the is_in_name macro. - */ -struct synclass is_entry[] = { - { "ISDIGIT", "a digit" }, - { "ISUPPER", "an upper case letter" }, - { "ISLOWER", "a lower case letter" }, - { "ISUNDER", "an underscore" }, - { "ISSPECL", "the name of a special parameter" }, - { NULL, NULL } -}; - -static char writer[] = "\ -/*\n\ - * This file was generated by the mksyntax program.\n\ - */\n\ -\n"; - - -static FILE *cfile; -static FILE *hfile; -static char *syntax[513]; -static int base; -static int size; /* number of values which a char variable can have */ -static int nbits; /* number of bits in a character */ -static int digit_contig;/* true if digits are contiguous */ - -static void filltable(char *); -static void init(void); -static void add(char *, char *); -static void print(char *); -static void output_type_macros(void); -static void digit_convert(void); - -#ifndef __unused -#define __unused __attribute__((__unused__)) -#endif - -int -main(int argc __unused, char **argv __unused) -{ - char c; - char d; - int sign; - int i; - char buf[80]; - int pos; - static char digit[] = "0123456789"; - - /* Create output files */ - if ((cfile = fopen("syntax.c", "w")) == NULL) { - perror("syntax.c"); - exit(2); - } - if ((hfile = fopen("syntax.h", "w")) == NULL) { - perror("syntax.h"); - exit(2); - } - fputs(writer, hfile); - fputs(writer, cfile); - - /* Determine the characteristics of chars. */ - c = -1; - if (c < 0) - sign = 1; - else - sign = 0; - for (nbits = 1 ; ; nbits++) { - d = (1 << nbits) - 1; - if (d == c) - break; - } -#if 0 - printf("%s %d bit chars\n", sign? "signed" : "unsigned", nbits); -#endif - if (nbits > 9) { - fputs("Characters can't have more than 9 bits\n", stderr); - exit(2); - } - size = (1 << nbits) + 1; - base = 1; - if (sign) - base += 1 << (nbits - 1); - digit_contig = 1; - for (i = 0 ; i < 10 ; i++) { - if (digit[i] != '0' + i) - digit_contig = 0; - } - - fputs("#include \n", hfile); - - /* Generate the #define statements in the header file */ - fputs("/* Syntax classes */\n", hfile); - for (i = 0 ; synclass[i].name ; i++) { - sprintf(buf, "#define %s %d", synclass[i].name, i); - fputs(buf, hfile); - for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07) - putc('\t', hfile); - fprintf(hfile, "/* %s */\n", synclass[i].comment); - } - putc('\n', hfile); - fputs("/* Syntax classes for is_ functions */\n", hfile); - for (i = 0 ; is_entry[i].name ; i++) { - sprintf(buf, "#define %s %#o", is_entry[i].name, 1 << i); - fputs(buf, hfile); - for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07) - putc('\t', hfile); - fprintf(hfile, "/* %s */\n", is_entry[i].comment); - } - putc('\n', hfile); - fprintf(hfile, "#define SYNBASE %d\n", base); - fprintf(hfile, "#define PEOF %d\n\n", -base); - putc('\n', hfile); - fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile); - fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile); - fputs("#define SQSYNTAX (sqsyntax + SYNBASE)\n", hfile); - fputs("#define ARISYNTAX (arisyntax + SYNBASE)\n", hfile); - putc('\n', hfile); - output_type_macros(); /* is_digit, etc. */ - putc('\n', hfile); - - /* Generate the syntax tables. */ - fputs("#include \"shell.h\"\n", cfile); - fputs("#include \"syntax.h\"\n\n", cfile); - init(); - fputs("/* syntax table used when not in quotes */\n", cfile); - add("\n", "CNL"); - add("\\", "CBACK"); - add("'", "CSQUOTE"); - add("\"", "CDQUOTE"); - add("`", "CBQUOTE"); - add("$", "CVAR"); - add("}", "CENDVAR"); - add("<>();&| \t", "CSPCL"); - print("basesyntax"); - init(); - fputs("\n/* syntax table used when in double quotes */\n", cfile); - add("\n", "CNL"); - add("\\", "CBACK"); - add("\"", "CENDQUOTE"); - add("`", "CBQUOTE"); - add("$", "CVAR"); - add("}", "CENDVAR"); - /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ - add("!*?[=~:/-", "CCTL"); - print("dqsyntax"); - init(); - fputs("\n/* syntax table used when in single quotes */\n", cfile); - add("\n", "CNL"); - add("'", "CENDQUOTE"); - /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ - add("!*?[=~:/-", "CCTL"); - print("sqsyntax"); - init(); - fputs("\n/* syntax table used when in arithmetic */\n", cfile); - add("\n", "CNL"); - add("\\", "CBACK"); - add("`", "CBQUOTE"); - add("'", "CSQUOTE"); - add("\"", "CDQUOTE"); - add("$", "CVAR"); - add("}", "CENDVAR"); - add("(", "CLP"); - add(")", "CRP"); - print("arisyntax"); - filltable("0"); - fputs("\n/* character classification table */\n", cfile); - add("0123456789", "ISDIGIT"); - add("abcdefghijklmnopqrstucvwxyz", "ISLOWER"); - add("ABCDEFGHIJKLMNOPQRSTUCVWXYZ", "ISUPPER"); - add("_", "ISUNDER"); - add("#?$!-*@", "ISSPECL"); - print("is_type"); - if (! digit_contig) - digit_convert(); - exit(0); -} - - - -/* - * Clear the syntax table. - */ - -static void -filltable(char *dftval) -{ - int i; - - for (i = 0 ; i < size ; i++) - syntax[i] = dftval; -} - - -/* - * Initialize the syntax table with default values. - */ - -static void -init(void) -{ - filltable("CWORD"); - syntax[0] = "CEOF"; - syntax[base + CTLESC] = "CCTL"; - syntax[base + CTLVAR] = "CCTL"; - syntax[base + CTLENDVAR] = "CCTL"; - syntax[base + CTLBACKQ] = "CCTL"; - syntax[base + CTLBACKQ + CTLQUOTE] = "CCTL"; - syntax[base + CTLARI] = "CCTL"; - syntax[base + CTLENDARI] = "CCTL"; - syntax[base + CTLQUOTEMARK] = "CCTL"; -} - - -/* - * Add entries to the syntax table. - */ - -static void -add(char *p, char *type) -{ - while (*p) - syntax[*p++ + base] = type; -} - - - -/* - * Output the syntax table. - */ - -static void -print(char *name) -{ - int i; - int col; - - fprintf(hfile, "extern const char %s[];\n", name); - fprintf(cfile, "const char %s[%d] = {\n", name, size); - col = 0; - for (i = 0 ; i < size ; i++) { - if (i == 0) { - fputs(" ", cfile); - } else if ((i & 03) == 0) { - fputs(",\n ", cfile); - col = 0; - } else { - putc(',', cfile); - while (++col < 9 * (i & 03)) - putc(' ', cfile); - } - fputs(syntax[i], cfile); - col += strlen(syntax[i]); - } - fputs("\n};\n", cfile); -} - - - -/* - * Output character classification macros (e.g. is_digit). If digits are - * contiguous, we can test for them quickly. - */ - -static char *macro[] = { - "#define is_digit(c)\t((is_type+SYNBASE)[c] & ISDIGIT)", - "#define is_alpha(c)\t((c) != PEOF && ((c) < CTLESC || (c) > CTLQUOTEMARK) && isalpha((unsigned char) (c)))", - "#define is_name(c)\t((c) != PEOF && ((c) < CTLESC || (c) > CTLQUOTEMARK) && ((c) == '_' || isalpha((unsigned char) (c))))", - "#define is_in_name(c)\t((c) != PEOF && ((c) < CTLESC || (c) > CTLQUOTEMARK) && ((c) == '_' || isalnum((unsigned char) (c))))", - "#define is_special(c)\t((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))", - NULL -}; - -static void -output_type_macros(void) -{ - char **pp; - - if (digit_contig) - macro[0] = "#define is_digit(c)\t((unsigned)((c) - '0') <= 9)"; - for (pp = macro ; *pp ; pp++) - fprintf(hfile, "%s\n", *pp); - if (digit_contig) - fputs("#define digit_val(c)\t((c) - '0')\n", hfile); - else - fputs("#define digit_val(c)\t(digit_value[c])\n", hfile); -} - - - -/* - * Output digit conversion table (if digits are not contiguous). - */ - -static void -digit_convert(void) -{ - int maxdigit; - static char digit[] = "0123456789"; - char *p; - int i; - - maxdigit = 0; - for (p = digit ; *p ; p++) - if (*p > maxdigit) - maxdigit = *p; - fputs("extern const char digit_value[];\n", hfile); - fputs("\n\nconst char digit_value[] = {\n", cfile); - for (i = 0 ; i <= maxdigit ; i++) { - for (p = digit ; *p && *p != i ; p++); - if (*p == '\0') - p = digit; - fprintf(cfile, " %d,\n", (int)(p - digit)); - } - fputs("};\n", cfile); -} - -/* - * $PchId: mksyntax.c,v 1.7 2006/05/23 12:04:27 philip Exp $ - */ diff --git a/minix/commands/ash/setmode.c b/minix/commands/ash/setmode.c deleted file mode 100644 index 80f66ae6b..000000000 --- a/minix/commands/ash/setmode.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (c) 1989, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Dave Borman at Cray Research, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)setmode.c 8.2 (Berkeley) 3/25/94"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include - -#include -#include -#include -#include -#include - -#ifdef SETMODE_DEBUG -#include -#endif - -#include "shell.h" - -#ifndef S_ISTXT -#define S_ISTXT S_ISVTX -#endif - -#define SET_LEN 6 /* initial # of bitcmd struct to malloc */ -#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ - -typedef struct bitcmd { - char cmd; - char cmd2; - mode_t bits; -} BITCMD; - -#define CMD2_CLR 0x01 -#define CMD2_SET 0x02 -#define CMD2_GBITS 0x04 -#define CMD2_OBITS 0x08 -#define CMD2_UBITS 0x10 - -static BITCMD *addcmd (BITCMD *, int, int, int, unsigned int); -static void compress_mode (BITCMD *); -#ifdef SETMODE_DEBUG -static void dumpmode __P((BITCMD *)); -#endif - -/* - * Given the old mode and an array of bitcmd structures, apply the operations - * described in the bitcmd structures to the old mode, and return the new mode. - * Note that there is no '=' command; a strict assignment is just a '-' (clear - * bits) followed by a '+' (set bits). - */ -mode_t -getmode(bbox, omode) - void *bbox; - mode_t omode; -{ - register BITCMD *set; - register mode_t clrval, newmode, value; - - set = (BITCMD *)bbox; - newmode = omode; - for (value = 0;; set++) - switch(set->cmd) { - /* - * When copying the user, group or other bits around, we "know" - * where the bits are in the mode so that we can do shifts to - * copy them around. If we don't use shifts, it gets real - * grundgy with lots of single bit checks and bit sets. - */ - case 'u': - value = (newmode & S_IRWXU) >> 6; - goto common; - - case 'g': - value = (newmode & S_IRWXG) >> 3; - goto common; - - case 'o': - value = newmode & S_IRWXO; -common: if (set->cmd2 & CMD2_CLR) { - clrval = - (set->cmd2 & CMD2_SET) ? S_IRWXO : value; - if (set->cmd2 & CMD2_UBITS) - newmode &= ~((clrval<<6) & set->bits); - if (set->cmd2 & CMD2_GBITS) - newmode &= ~((clrval<<3) & set->bits); - if (set->cmd2 & CMD2_OBITS) - newmode &= ~(clrval & set->bits); - } - if (set->cmd2 & CMD2_SET) { - if (set->cmd2 & CMD2_UBITS) - newmode |= (value<<6) & set->bits; - if (set->cmd2 & CMD2_GBITS) - newmode |= (value<<3) & set->bits; - if (set->cmd2 & CMD2_OBITS) - newmode |= value & set->bits; - } - break; - - case '+': - newmode |= set->bits; - break; - - case '-': - newmode &= ~set->bits; - break; - - case 'X': - if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) - newmode |= set->bits; - break; - - case '\0': - default: -#ifdef SETMODE_DEBUG - (void)printf("getmode:%04o -> %04o\n", omode, newmode); -#endif - return (newmode); - } -} - -#define ADDCMD(a, b, c, d) \ - if (set >= endset) { \ - register BITCMD *newset; \ - setlen += SET_LEN_INCR; \ - newset = realloc(saveset, sizeof(BITCMD) * setlen); \ - if (!saveset) \ - return (NULL); \ - set = newset + (set - saveset); \ - saveset = newset; \ - endset = newset + (setlen - 2); \ - } \ - set = addcmd(set, (a), (b), (c), (d)) - -#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) - -void * -setmode(p) - register char *p; -{ - register int perm, who; - register char op; - BITCMD *set, *saveset, *endset; - sigset_t sigset, sigoset; - mode_t mask; - int equalopdone, permXbits, setlen; - - if (!*p) - return (NULL); - - /* - * Get a copy of the mask for the permissions that are mask relative. - * Flip the bits, we want what's not set. Since it's possible that - * the caller is opening files inside a signal handler, protect them - * as best we can. - */ - sigfillset(&sigset); - (void)sigprocmask(SIG_BLOCK, &sigset, &sigoset); - (void)umask(mask = umask(0)); - mask = ~mask; - (void)sigprocmask(SIG_SETMASK, &sigoset, NULL); - - setlen = SET_LEN + 2; - - if ((set = malloc((unsigned int)(sizeof(BITCMD) * setlen))) == NULL) - return (NULL); - saveset = set; - endset = set + (setlen - 2); - - /* - * If an absolute number, get it and return; disallow non-octal digits - * or illegal bits. - */ - if (isdigit(*p)) { - perm = (mode_t)strtol(p, NULL, 8); - if (perm & ~(STANDARD_BITS|S_ISTXT)) { - free(saveset); - return (NULL); - } - while (*++p) - if (*p < '0' || *p > '7') { - free(saveset); - return (NULL); - } - ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); - return (saveset); - } - - /* - * Build list of structures to set/clear/copy bits as described by - * each clause of the symbolic mode. - */ - for (;;) { - /* First, find out which bits might be modified. */ - for (who = 0;; ++p) { - switch (*p) { - case 'a': - who |= STANDARD_BITS; - break; - case 'u': - who |= S_ISUID|S_IRWXU; - break; - case 'g': - who |= S_ISGID|S_IRWXG; - break; - case 'o': - who |= S_IRWXO; - break; - default: - goto getop; - } - } - -getop: if ((op = *p++) != '+' && op != '-' && op != '=') { - free(saveset); - return (NULL); - } - if (op == '=') - equalopdone = 0; - - who &= ~S_ISTXT; - for (perm = 0, permXbits = 0;; ++p) { - switch (*p) { - case 'r': - perm |= S_IRUSR|S_IRGRP|S_IROTH; - break; - case 's': - /* If only "other" bits ignore set-id. */ - if (who & ~S_IRWXO) - perm |= S_ISUID|S_ISGID; - break; - case 't': - /* If only "other" bits ignore sticky. */ - if (who & ~S_IRWXO) { - who |= S_ISTXT; - perm |= S_ISTXT; - } - break; - case 'w': - perm |= S_IWUSR|S_IWGRP|S_IWOTH; - break; - case 'X': - permXbits = S_IXUSR|S_IXGRP|S_IXOTH; - break; - case 'x': - perm |= S_IXUSR|S_IXGRP|S_IXOTH; - break; - case 'u': - case 'g': - case 'o': - /* - * When ever we hit 'u', 'g', or 'o', we have - * to flush out any partial mode that we have, - * and then do the copying of the mode bits. - */ - if (perm) { - ADDCMD(op, who, perm, mask); - perm = 0; - } - if (op == '=') - equalopdone = 1; - if (op == '+' && permXbits) { - ADDCMD('X', who, permXbits, mask); - permXbits = 0; - } - ADDCMD(*p, who, op, mask); - break; - - default: - /* - * Add any permissions that we haven't already - * done. - */ - if (perm || (op == '=' && !equalopdone)) { - if (op == '=') - equalopdone = 1; - ADDCMD(op, who, perm, mask); - perm = 0; - } - if (permXbits) { - ADDCMD('X', who, permXbits, mask); - permXbits = 0; - } - goto apply; - } - } - -apply: if (!*p) - break; - if (*p != ',') - goto getop; - ++p; - } - set->cmd = 0; -#ifdef SETMODE_DEBUG - (void)printf("Before compress_mode()\n"); - dumpmode(saveset); -#endif - compress_mode(saveset); -#ifdef SETMODE_DEBUG - (void)printf("After compress_mode()\n"); - dumpmode(saveset); -#endif - return (saveset); -} - -static BITCMD * -addcmd(set, op, who, oparg, mask) - BITCMD *set; - register int oparg, who; - register int op; - unsigned int mask; -{ - switch (op) { - case '=': - set->cmd = '-'; - set->bits = who ? who : STANDARD_BITS; - set++; - - op = '+'; - /* FALLTHROUGH */ - case '+': - case '-': - case 'X': - set->cmd = op; - set->bits = (who ? who : mask) & oparg; - break; - - case 'u': - case 'g': - case 'o': - set->cmd = op; - if (who) { - set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | - ((who & S_IRGRP) ? CMD2_GBITS : 0) | - ((who & S_IROTH) ? CMD2_OBITS : 0); - set->bits = ~0; - } else { - set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; - set->bits = mask; - } - - if (oparg == '+') - set->cmd2 |= CMD2_SET; - else if (oparg == '-') - set->cmd2 |= CMD2_CLR; - else if (oparg == '=') - set->cmd2 |= CMD2_SET|CMD2_CLR; - break; - } - return (set + 1); -} - -#ifdef SETMODE_DEBUG -static void -dumpmode(set) - register BITCMD *set; -{ - for (; set->cmd; ++set) - (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", - set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", - set->cmd2 & CMD2_CLR ? " CLR" : "", - set->cmd2 & CMD2_SET ? " SET" : "", - set->cmd2 & CMD2_UBITS ? " UBITS" : "", - set->cmd2 & CMD2_GBITS ? " GBITS" : "", - set->cmd2 & CMD2_OBITS ? " OBITS" : ""); -} -#endif - -/* - * Given an array of bitcmd structures, compress by compacting consecutive - * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', - * 'g' and 'o' commands continue to be separate. They could probably be - * compacted, but it's not worth the effort. - */ -static void -compress_mode(set) - register BITCMD *set; -{ - register BITCMD *nset; - register int setbits, clrbits, Xbits, op; - - for (nset = set;;) { - /* Copy over any 'u', 'g' and 'o' commands. */ - while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { - *set++ = *nset++; - if (!op) - return; - } - - for (setbits = clrbits = Xbits = 0;; nset++) { - if ((op = nset->cmd) == '-') { - clrbits |= nset->bits; - setbits &= ~nset->bits; - Xbits &= ~nset->bits; - } else if (op == '+') { - setbits |= nset->bits; - clrbits &= ~nset->bits; - Xbits &= ~nset->bits; - } else if (op == 'X') - Xbits |= nset->bits & ~setbits; - else - break; - } - if (clrbits) { - set->cmd = '-'; - set->cmd2 = 0; - set->bits = clrbits; - set++; - } - if (setbits) { - set->cmd = '+'; - set->cmd2 = 0; - set->bits = setbits; - set++; - } - if (Xbits) { - set->cmd = 'X'; - set->cmd2 = 0; - set->bits = Xbits; - set++; - } - } -} - -/* - * $PchId: setmode.c,v 1.3 2006/05/23 11:57:34 philip Exp $ - */ diff --git a/minix/commands/ash/trap.c b/minix/commands/ash/trap.c deleted file mode 100644 index 56dc8572f..000000000 --- a/minix/commands/ash/trap.c +++ /dev/null @@ -1,568 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; -#endif -#endif /* not lint */ -/* -#include -__FBSDID("$FreeBSD: src/bin/sh/trap.c,v 1.29 2004/04/06 20:06:51 markm Exp $"); -*/ - -#include -#include -#include - -#include "shell.h" -#include "main.h" -#include "nodes.h" /* for other headers */ -#include "eval.h" -#include "jobs.h" -#include "show.h" -#include "options.h" -#include "syntax.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "trap.h" -#include "mystring.h" -#if !defined(NO_HISTORY) -#include "myhistedit.h" -#endif -#include "builtins.h" - -typedef void (*sig_T)(int); - -/* - * Sigmode records the current value of the signal handlers for the various - * modes. A value of zero means that the current handler is not known. - * S_HARD_IGN indicates that the signal was ignored on entry to the shell, - */ - -#define S_DFL 1 /* default signal handling (SIG_DFL) */ -#define S_CATCH 2 /* signal is caught */ -#define S_IGN 3 /* signal is ignored (SIG_IGN) */ -#define S_HARD_IGN 4 /* signal is ignored permanently */ -#define S_RESET 5 /* temporary - to reset a hard ignored sig */ - - -MKINIT char sigmode[_NSIG]; /* current value of signal */ -int pendingsigs; /* indicates some signal received */ -int is_interactive= -1; /* Shell is interactive */ -int in_dotrap; /* do we execute in a trap handler? */ -static char *volatile trap[_NSIG]; /* trap handler commands */ -static volatile sig_atomic_t gotsig[_NSIG]; - /* indicates specified signal received */ -static int ignore_sigchld; /* Used while handling SIGCHLD traps. */ -volatile sig_atomic_t gotwinch; - -static int sigstring_to_signum (char *); -static void printsignals (void); -static int getsigaction(int, sig_T *); -static void onsig (int); -#ifdef NO_SIGINTERRUPT -static int siginterrupt (int,int); -#endif -static char *strsigname (int); - - -/* - * Map a string to a signal number. - */ -static int -sigstring_to_signum(char *sig) -{ - - if (is_number(sig)) { - int signo; - - signo = atoi(sig); - return ((signo >= 0 && signo < _NSIG) ? signo : (-1)); - } else if (strcasecmp(sig, "exit") == 0) { - return (0); - } else { - int n; - - if (strncasecmp(sig, "sig", 3) == 0) - sig += 3; - for (n = 1; n < _NSIG; n++) - if (strcasecmp(strsigname(n), sig) == 0) - return (n); - } - return (-1); -} - - -/* - * Print a list of valid signal names. - */ -static void -printsignals(void) -{ - int n, outlen; - - outlen = 0; - for (n = 1; n < _NSIG; n++) { - if (strsigname(n)) { - out1fmt("%s", strsigname(n)); - outlen += strlen(strsigname(n)); - } else { - out1fmt("%d", n); - outlen += 3; /* good enough */ - } - ++outlen; - if (outlen > 70 || n == _NSIG - 1) { - out1str("\n"); - outlen = 0; - } else { - out1c(' '); - } - } -} - - -/* - * The trap builtin. - */ -int -trapcmd(int argc, char **argv) -{ - char *action; - int signo; - - if (argc <= 1) { - for (signo = 0 ; signo < _NSIG ; signo++) { - if (trap[signo] != NULL) { - if (signo == 0) { - out1fmt("trap -- '%s' %s\n", - trap[signo], "exit"); - } else if (strsigname(signo)) { - out1fmt("trap -- '%s' %s\n", - trap[signo], strsigname(signo)); - } else { - out1fmt("trap -- '%s' %d\n", - trap[signo], signo); - } - } - } - return 0; - } - action = NULL; - if (*++argv && strcmp(*argv, "--") == 0) - argv++; - if (*argv && sigstring_to_signum(*argv) == -1) { - if ((*argv)[0] != '-') { - action = *argv; - argv++; - } else if ((*argv)[1] == '\0') { - argv++; - } else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') { - printsignals(); - return 0; - } else { - error("bad option %s", *argv); - } - } - while (*argv) { - if ((signo = sigstring_to_signum(*argv)) == -1) - error("bad signal %s", *argv); - INTOFF; - if (action) - action = savestr(action); - if (trap[signo]) - ckfree(trap[signo]); - trap[signo] = action; - if (signo != 0) - setsignal(signo); - INTON; - argv++; - } - return 0; -} - - -/* - * Clear traps on a fork. - */ -void -clear_traps(void) -{ - char *volatile *tp; - - for (tp = trap ; tp <= &trap[_NSIG - 1] ; tp++) { - if (*tp && **tp) { /* trap not NULL or SIG_IGN */ - INTOFF; - ckfree(*tp); - *tp = NULL; - if (tp != &trap[0]) - setsignal(tp - trap); - INTON; - } - } -} - - -/* - * Set the signal handler for the specified signal. The routine figures - * out what it should be set to. - */ -void -setsignal(int signo) -{ - int action; - sig_T sig, sigact = SIG_DFL; - char *t; - - if ((t = trap[signo]) == NULL) - action = S_DFL; - else if (*t != '\0') - action = S_CATCH; - else - action = S_IGN; - if (action == S_DFL) { - switch (signo) { - case SIGINT: - action = S_CATCH; - break; - case SIGQUIT: -#if DEBUG - { - extern int debug; - - if (debug) - break; - } -#endif - action = S_CATCH; - break; - case SIGTERM: - if (rootshell && iflag) - action = S_IGN; - break; -#if JOBS - case SIGTSTP: - case SIGTTOU: - if (rootshell && mflag) - action = S_IGN; - break; -#endif -#ifndef NO_HISTORY - case SIGWINCH: - if (rootshell && iflag) - action = S_CATCH; - break; -#endif - } - } - - t = &sigmode[signo]; - if (*t == 0) { - /* - * current setting unknown - */ - if (!getsigaction(signo, &sigact)) { - /* - * Pretend it worked; maybe we should give a warning - * here, but other shells don't. We don't alter - * sigmode, so that we retry every time. - */ - return; - } - if (sigact == SIG_IGN) { - if (mflag && (signo == SIGTSTP || - signo == SIGTTIN || signo == SIGTTOU)) { - *t = S_IGN; /* don't hard ignore these */ - } else - *t = S_HARD_IGN; - } else { - *t = S_RESET; /* force to be set */ - } - } - if (*t == S_HARD_IGN || *t == action) - return; - switch (action) { - case S_DFL: sigact = SIG_DFL; break; - case S_CATCH: sigact = onsig; break; - case S_IGN: sigact = SIG_IGN; break; - } - *t = action; - sig = signal(signo, sigact); - if (sig != SIG_ERR && action == S_CATCH) - siginterrupt(signo, 1); -} - - -/* - * Return the current setting for sig w/o changing it. - */ -static int -getsigaction(int signo, sig_T *sigact) -{ - struct sigaction sa; - - if (sigaction(signo, (struct sigaction *)0, &sa) == -1) - return 0; - *sigact = (sig_T) sa.sa_handler; - return 1; -} - - -/* - * Ignore a signal. - */ -void -ignoresig(int signo) -{ - - if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { - signal(signo, SIG_IGN); - } - sigmode[signo] = S_HARD_IGN; -} - - -#ifdef mkinit -INCLUDE -INCLUDE "trap.h" - -SHELLPROC { - char *sm; - - clear_traps(); - for (sm = sigmode ; sm < sigmode + _NSIG ; sm++) { - if (*sm == S_IGN) - *sm = S_HARD_IGN; - } -} -#endif - - -/* - * Signal handler. - */ -static void -onsig(int signo) -{ - -#ifndef BSD - signal(signo, onsig); -#endif - if (signo == SIGINT && trap[SIGINT] == NULL) { - onint(); - return; - } - - if (signo != SIGCHLD || !ignore_sigchld) - gotsig[signo] = 1; - pendingsigs++; - - /* If we are currently in a wait builtin, prepare to break it */ - if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0) - breakwaitcmd = 1; - /* - * If a trap is set, not ignored and not the null command, we need - * to make sure traps are executed even when a child blocks signals. - */ - if (Tflag && - trap[signo] != NULL && - ! trap[signo][0] == '\0' && - ! (trap[signo][0] == ':' && trap[signo][1] == '\0')) - breakwaitcmd = 1; - -#ifndef NO_HISTORY - if (signo == SIGWINCH) - gotwinch = 1; -#endif -} - - -/* - * Called to execute a trap. Perhaps we should avoid entering new trap - * handlers while we are executing a trap handler. - */ -void -dotrap(void) -{ - int i; - int savestatus; - - in_dotrap++; - for (;;) { - for (i = 1; i < _NSIG; i++) { - if (gotsig[i]) { - gotsig[i] = 0; - if (trap[i]) { - /* - * Ignore SIGCHLD to avoid infinite - * recursion if the trap action does - * a fork. - */ - if (i == SIGCHLD) - ignore_sigchld++; - savestatus = exitstatus; - evalstring(trap[i]); - exitstatus = savestatus; - if (i == SIGCHLD) - ignore_sigchld--; - } - break; - } - } - if (i >= _NSIG) - break; - } - in_dotrap--; - pendingsigs = 0; -} - - -/* - * Controls whether the shell is interactive or not. - */ -void -setinteractive(int on) -{ - if (on == is_interactive) - return; - setsignal(SIGINT); - setsignal(SIGQUIT); - setsignal(SIGTERM); -#ifndef NO_HISTORY - setsignal(SIGWINCH); -#endif - is_interactive = on; -} - - -/* - * Called to exit the shell. - */ -void -exitshell(int status) -{ - struct jmploc loc1, loc2; - char *p; - - TRACE(("exitshell(%d) pid=%d\n", status, getpid())); - if (setjmp(loc1.loc)) { - goto l1; - } - if (setjmp(loc2.loc)) { - goto l2; - } - handler = &loc1; - if ((p = trap[0]) != NULL && *p != '\0') { - trap[0] = NULL; - evalstring(p); - } -l1: handler = &loc2; /* probably unnecessary */ - flushall(); -#if JOBS - setjobctl(0); -#endif -l2: _exit(status); -} - -#ifdef NO_SIGINTERRUPT -static int siginterrupt(sig, flag) -int sig; -int flag; -{ - return 0; -} -#endif - -#ifdef __minix -static char *strsigname(sig) -int sig; -{ - switch(sig) - { - case 0: return "Signal 0"; /* 0 */ - case SIGHUP: return "hup"; /* 1 */ - case SIGINT: return "int"; /* 2 */ - case SIGQUIT: return "quit"; /* 3 */ - case SIGILL: return "ill"; /* 4 */ - case SIGTRAP: return "trap"; /* 5 */ - case SIGABRT: return "abrt"; /* 6 */ - case SIGBUS: return "bus"; /* 7 */ - case SIGFPE: return "fpe"; /* 8 */ - case SIGKILL: return "kill"; /* 9 */ - case SIGUSR1: return "usr1"; /* 10 */ - case SIGSEGV: return "segv"; /* 11 */ - case SIGUSR2: return "usr2"; /* 12 */ - case SIGPIPE: return "pipe"; /* 13 */ - case SIGALRM: return "alrm"; /* 14 */ - case SIGTERM: return "term"; /* 15 */ - case SIGEMT: return "emt"; /* 16 */ - case SIGCHLD: return "chld"; /* 17 */ - case SIGCONT: return "cont"; /* 18 */ - case SIGSTOP: return "stop"; /* 19 */ - case SIGTSTP: return "tstp"; /* 20 */ - case SIGTTIN: return "ttin"; /* 21 */ - case SIGTTOU: return "ttou"; /* 22 */ - case SIGWINCH: return "winch"; /* 23 */ - case SIGVTALRM: return "vtalrm"; /* 24 */ - case SIGPROF: return "prof"; /* 25 */ - default: return "Signal n"; - } -} -#else -static char *strsigname(sig) -int sig; -{ - return (char *)sys_signame[sig]; -} -#endif - -#ifdef __minix -#include "signames.h" -char *strsiglist(sig) -int sig; -{ - if (sig > MAXSIG) - return NULL; - return sigmesg[sig]; -} -#else -char *strsiglist(sig) -int sig; -{ - return (char *)sys_siglist[sig]; -} -#endif - -/* - * $PchId: trap.c,v 1.7 2006/05/23 11:56:21 philip Exp $ - */ diff --git a/minix/drivers/storage/ramdisk/Makefile b/minix/drivers/storage/ramdisk/Makefile index 3e2a7f448..84a732a8b 100644 --- a/minix/drivers/storage/ramdisk/Makefile +++ b/minix/drivers/storage/ramdisk/Makefile @@ -48,7 +48,7 @@ dir.procfs:= minix/fs/procfs PROGRAMS+= service dir.service:= minix/commands/service PROGRAMS+= sh -dir.sh:= minix/commands/ash +dir.sh:= bin/sh PROGRAMS+= sysenv dir.sysenv:= minix/commands/sysenv PROGRAMS+= umount diff --git a/minix/man/man1/Makefile b/minix/man/man1/Makefile index 8f8b8e14c..d2fb8f240 100644 --- a/minix/man/man1/Makefile +++ b/minix/man/man1/Makefile @@ -1,4 +1,4 @@ -MAN= ash.1 at.1 \ +MAN= at.1 \ bsfilt.1 cawf.1 chgrp.1 \ cmp.1 compress.1 \ crc.1 crontab.1 dd.1 \ @@ -21,33 +21,6 @@ MAN= ash.1 at.1 \ uud.1 uue.1 vol.1 \ yap.1 linkfarm.1 pkg_view.1 -MLINKS += ash.1 sh.1 -MLINKS += ash.1 ..1 -MLINKS += ash.1 break.1 -MLINKS += ash.1 case.1 -MLINKS += ash.1 cd.1 -MLINKS += ash.1 command.1 -MLINKS += ash.1 continue.1 -MLINKS += ash.1 eval.1 -MLINKS += ash.1 exec.1 -MLINKS += ash.1 exit.1 -MLINKS += ash.1 export.1 -MLINKS += ash.1 for.1 -MLINKS += ash.1 getopts.1 -MLINKS += ash.1 hash.1 -MLINKS += ash.1 if.1 -MLINKS += ash.1 jobs.1 -MLINKS += ash.1 local.1 -MLINKS += ash.1 read.1 -MLINKS += ash.1 readonly.1 -MLINKS += ash.1 return.1 -MLINKS += ash.1 set.1 -MLINKS += ash.1 setvar.1 -MLINKS += ash.1 shift.1 -MLINKS += ash.1 trap.1 -MLINKS += ash.1 umask.1 -MLINKS += ash.1 unset.1 -MLINKS += ash.1 wait.1 MLINKS += compress.1 uncompress.1 MLINKS += svc.1 ci.1 MLINKS += svc.1 co.1 diff --git a/minix/man/man1/ash.1 b/minix/man/man1/ash.1 deleted file mode 100644 index 2f237ace8..000000000 --- a/minix/man/man1/ash.1 +++ /dev/null @@ -1,1131 +0,0 @@ -.\" Copyright (c) 1991 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" This code is derived from software contributed to Berkeley by -.\" Kenneth Almquist. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)sh.1 5.1 (Berkeley) 3/7/91 -.\" -.TH SH 1 "March 7, 1991" -.UC 7 -.de h \" subheading -.sp -.ti -0.3i -.B "\\$1" -.PP -.. -.de d \" begin display -.sp -.in +4 -.nf -.. -.de e \" end display -.in -4 -.fi -.sp -.. -.de c \" command, etc. -.br -.HP 3 -\fB\\$1\fR -.br -.. -.de b \" begin builtin command -.HP 3 -.B \\$1 -.. -.SH NAME -ash, sh, bigsh, ., break, case, cd, command, continue, eval, exec, exit, export, for, getopts, hash, if, jobs, local, read, readonly, return, set, setvar, shift, trap, umask, unset, wait, while \- a shell -.SH SYNOPSIS -.B ash -[ -.B -efIijnsxz -] [ -.B +efIijnsxz -] [ -.B -c -.I command -] [ -.I arg -] ... -.SH COPYRIGHT -Copyright 1989 by Kenneth Almquist. -.SH DESCRIPTION -.I Ash -is a version of -.I sh -with features similar to those of the System V shell. -This manual page lists all the features of -.I ash -but concentrates on the ones not in other shells. -.h "Invocation" -If the -.B -c -options is given, then the shell executes the specified shell command. -The -.B -s -flag cause the shell to read commands from the standard input (after -executing any command specified with the -.B -c -option. -If neither the -.B -s -or -.B -c -options are set, then the first -.I arg -is taken as the name of a file to read commands from. -If this is impossible because there are no arguments following -the options, then -.I ash -will set the -.B -s -flag and will read commands from the standard input. -.PP -The shell sets the initial value of the positional parameters from the -.IR arg s -remaining after any -.I arg -used as the name of a file of commands is deleted. -.PP -The flags (other than -.BR -c ) -are set by preceding them with ``-'' and cleared by preceding them -with ``+''; see the -.I set -builtin command for a list of flags. -If no value is specified for the -.B -i -flag, the -.B -s -flag is set, and the standard input and output of the shell -are connected to terminals, then the -.B -i -flag will be set. -If no value is specified for the -.B -j -flag, then the -.B -j -flag will be set if the -.B -i -flag is set. -.PP -When the shell is invoked with the -.B -c -option, it is good practice to include the -.I -i -flag if the command was entered interactively by a user. -For compatibility with the System V shell, the -.I -i -option should come after the -.B -c -option. -.PP -If the first character of argument zero to the shell is ``-'', -the shell is assumed to be a login shell, and the files -.B /etc/profile -and -.B .profile -are read if they exist. -If the environment variable SHINIT is set on entry to the shell, -the commands in SHINIT are normally parsed and executed. SHINIT is -not examined if the shell is a login shell, or if it the shell is running a -shell procedure. (A shell is considered to be running a shell -procedure if neither the -.B -s -nor the -.B -c -options are set.) -.PP -In older versions of MINIX that did not have virtual memory, it was -important for executables to have enough memory assigned to them. The -.B bigsh -binary was provided for shells that need much memory. This command is -retained for backward compatibility and need not be used on MINIX 3.1.4 -and later. -.h "Control Structures" -A -.I list -is a sequence of zero or more commands separated by newlines, -semicolons, or ampersands, and optionally terminated by one of these -three characters. (This differs from the System V shell, which -requires a list to contain at least one command in most cases.) The -commands in a list are executed in the order they are written. -If command is followed by an ampersand, the shell starts the command -and immediately proceed onto the next command; otherwise it waits -for the command to terminate before proceeding to the next one. -.PP -``&&'' and ``||'' are binary operators. -``&&'' executes the first command, and then executes the second command -iff the exit status of the first command is zero. ``||'' is similar, -but executes the second command iff the exit status of the first command -is nonzero. ``&&'' and ``||'' both have the same priority. -.PP -The ``|'' operator is a binary operator which feeds the standard output -of the first command into the standard input of the second command. -The exit status of the ``|'' operator is the exit status of the second -command. ``|'' has a higher priority than ``||'' or ``&&''. -.PP -An -.I if -command looks like -.d -\fBif\fR list -\fBthen\fR list -.ti -\w'[ 'u -[ \fBelif\fR list - \fBthen\fR list ] ... -.ti -\w'[ 'u -[ \fBelse\fR list ] -\fBfi\fR -.e -.PP -A -.I while -command looks like -.d -\fBwhile\fR list -\fBdo\fR list -\fBdone\fR -.e -The two lists are executed repeatedly while the exit status of the first -list is zero. The -.I until -command is similar, but has the word -.B until -in place of -.B while - repeats until the exit status of the first list -is zero. -.PP -The -.I for -command looks like -.d -\fBfor\fR variable \fBin\fR word... -\fBdo\fR list -\fBdone\fR -.e -The words are expanded, and then the list is executed repeatedly with -the variable set to each word in turn. -.B do -and -.B done -may be replaced with -``{'' and ``}''. -.PP -The -.I break -and -.I continue -commands look like -.d -\fBbreak\fR [ num ] -\fBcontinue\fR [ num ] -.e -.I Break -terminates the -.I num -innermost -.I for -or -.I while -loops. -.I Continue -continues with the next iteration of the -.IR num'th -innermost loop. -These are implemented as builtin commands. -.PP -The -.I case -command looks like -.d -\fBcase\fR word \fBin\fR -pattern\fB)\fR list \fB;;\fR -\&... -\fBesac\fR -.e -The pattern can actually be one or more patterns (see -.I Patterns -below), separated by ``|'' characters. -.PP -Commands may be grouped by writing either -.d -\fB(\fRlist\fB)\fR -.e -or -.d -\fB{\fR list; \fB}\fR -.e -The first of these executes the commands in a subshell. -.PP -A function definition looks like -.d -name \fB( )\fR command -.e -A function definition is an executable statement; when executed it installs -a function named -.B name -and returns an exit status of zero. -The command is normally a list enclosed between ``{'' and ``}''. -.PP -Variables may be declared to be local to a function by using a -.I local -command. This should appear as the first staement of a function, -and looks like -.d -\fBlocal\fR [ variable | \fB-\fR ] ... -.e -.I Local -is implemented as a builtin command. -.PP -When a variable is made local, it inherits the initial value and -exported and readonly flags from the variable with the same name in the -surrounding scope, if there is one. Otherwise, the variable is -initially unset. -.I Ash -uses dynamic scoping, so that if you make the variable -.B x -local to function -.IR f , -which then calls function -.IR g , -references to the variable -.B x -made inside -.I g -will refer to the variable -.B x -declared inside -.IR f , -not to the global variable named -.BR x . -.PP -The only special parameter that can be made local is ``\fB-\fR''. -Making ``\fB-\fR'' local any shell options that are changed via the -.I set -command inside the function to be restored to their original values -when the function returns. -.PP -The -.I return -command looks like -.d -\fBreturn\fR [ exitstatus ] -.e -It terminates the currently executing function. -.I Return -is implemented as a builtin command. -.h "Simple Commands" -A simple command is a sequence of words. The execution of a simple -command proceeds as follows. First, the leading words of the form -``name=value'' are stripped off and assigned to the environment of -the command. Second, the words are expanded. Third, the first -remaining word is taken as the command name that command is located. -Fourth, any redirections are performed. Fifth, the command is -executed. We look at these operations in reverse order. -.PP -The execution of the command varies with the type of command. -There are three types of commands: shell functions, builtin commands, -and normal programs. -.PP -When a shell function is executed, all of the shell positional parameters -(except $0, which remains unchanged) are set to the parameters to the shell -function. The variables which are explicitly placed in the environment -of the command (by placing assignments to them before the function name) -are made local to the function and are set to values given. -Then the command given in the function definition is executed. -The positional parameters are restored to their original values when -the command completes. -.PP -Shell builtins are executed internally to the shell, without spawning -a new process. -.PP -When a normal program is executed, the shell runs the program, passing -the parameters and the environment to the program. If the program is -a shell procedure, the shell will interpret the program in a subshell. -The shell will reinitialize itself in this case, so that the effect -will be as if a new shell had been invoked to handle the shell procedure, -except that the location of commands located in the parent shell will -be remembered by the child. If the program is a file beginning with -``#!'', the remainder of the first line specifies an interpreter for -the program. The shell (or the operating system, under Berkeley UNIX) -will run the interpreter in this case. The arguments to the interpreter -will consist of any arguments given on the first line of the program, -followed by the name of the program, followed by the arguments passed -to the program. -.h "Redirection" -Input/output redirections can be intermixed with the words in a simple -command and can be placed following any of the other commands. When -redirection occurs, the shell saves the old values of the file descriptors -and restores them when the command completes. The ``<'', ``>'', and ``>>'' -redirections open a file for input, output, and appending, respectively. -The ``<&digit'' and ``>&digit'' makes the input or output a duplicate -of the file descriptor numbered by the digit. If a minus sign is used -in place of a digit, the standard input or standard output are closed. -.PP -The ``<<\ word'' redirection -takes input from a -.I here -document. -As the shell encounters ``<<'' redirections, it collects them. The -next time it encounters an unescaped newline, it reads the documents -in turn. The word following the ``<<'' specifies the contents of the -line that terminates the document. If none of the quoting methods -('', "", or \e) are used to enter the word, then the document is treated -like a word inside double quotes: ``$'' and backquote are expanded -and backslash can be used to escape these and to continue long lines. -The word cannot contain any variable or command substitutions, and -its length (after quoting) must be in the range of 1 to 79 characters. -If ``<<-'' is used in place of ``<<'', then leading tabs are deleted -from the lines of the document. (This is to allow you do indent shell -procedures containing here documents in a natural fashion.) -.PP -Any of the preceding redirection operators may be preceded by a single -digit specifying the file descriptor to be redirected. There cannot -be any white space between the digit and the redirection operator. -.h "Path Search" -When locating a command, the shell first looks to see if it has a -shell function by that name. Then, if PATH does not contain an -entry for "%builtin", it looks for a builtin command by that name. -Finally, it searches each entry in PATH in turn for the command. -.PP -The value of the PATH variable should be a series of entries separated -by colons. -Each entry consists of a directory name, or a directory name followed -by a flag beginning with a percent sign. -The current directory should be indicated by an empty directory name. -.PP -If no percent sign is present, then the entry causes the shell to -search for the command in the specified directory. If the flag is -``%builtin'' then the list of shell builtin commands is searched. -If the flag is ``%func'' then the directory is searched for a file which -is read as input to the shell. This file should define a function -whose name is the name of the command being searched for. -.PP -Command names containing a slash are simply executed without performing -any of the above searches. -.h "The Environment" -The environment of a command is a set of name/value pairs. When the -shell is invoked, it reads these names and values, sets the shell -variables with these names to the corresponding values, and marks -the variables as exported. The -.I export -command can be used to mark additional variables as exported. -.PP -The environment of a command is constructed by constructing name/value -pairs from all the exported shell variables, and then modifying this -set by the assignments which precede the command, if any. -.h "Expansion" -The process of evaluating words when a shell procedure is executed is -called -.IR expansion . -Expansion consists of four steps: variable substitution, command -substitution, word splitting, and file name generation. If a word -is the expression following the word -.B case -in a case statement, the file name -which follows a redirection symbol, or an assignment to the environment -of a command, then the word cannot be split into multiple words. In -these cases, the last two steps of the expansion process are omitted. -.h "Variable Substitution" -To be written. -.h "Command Substitution" -.I Ash -accepts two syntaxes for command substitution: -.d -`\fIlist\fR` -.e -and -.d -$(\fIlist\fR) -.e -Either of these may be included in a word. -During the command substitution process, the command (syntactly a -.IR list ) -will be executed and anything that the command writes to the standard -output will be captured by the shell. The final newline (if any) of -the output will be deleted; the rest of the output will be substituted -for the command in the word. -.h "Word Splitting" -When the value of a variable or the output of a command is substituted, -the resulting text is subject to word splitting, unless the dollar sign -introducing the variable or backquotes containing the text were enclosed -in double quotes. In addition, ``$@'' is subject to a special type of -splitting, even in the presence of double quotes. -.PP -Ash uses two different splitting algorithms. The normal approach, which -is intended for splitting text separated by which space, is used if the -first character of the shell variable IFS is a space. Otherwise an alternative -experimental algorithm, which is useful for splitting (possibly empty) -fields separated by a separator character, is used. -.PP -When performing splitting, the shell scans the replacement text looking -for a character (when IFS does not begin with a space) or a sequence of -characters (when IFS does begin with a space), deletes the character or -sequence of characters, and spits the word into two strings at that -point. When IFS begins with a space, the shell deletes either of the -strings if they are null. As a special case, if the word containing -the replacement text is the null string, the word is deleted. -.PP -The variable ``$@'' is special in two ways. First, splitting takes -place between the positional parameters, even if the text is enclosed -in double quotes. Second, if the word containing the replacement -text is the null string and there are no positional parameters, then -the word is deleted. The result of these rules is that "$@" is -equivalent to "$1" "$2" ... "$\fIn\fR", where \fIn\fR is the number of -positional parameters. (Note that this differs from the System V shell. -The System V documentation claims that "$@" behaves this way; in fact -on the System V shell "$@" is equivalent to "" when there are no -positional paramteters.) -.h "File Name Generation" -Unless the -.B -f -flag is set, file name generation is performed after word splitting is -complete. Each word is viewed as a series of patterns, separated by -slashes. The process of expansion replaces the word with the names of -all existing files whose names can be formed by replacing each pattern -with a string that matches the specified pattern. There are two -restrictions on this: first, a pattern cannot match a string containing -a slash, and second, a pattern cannot match a string starting with a -period unless the first character of the pattern is a period. -.PP -If a word fails to match any files and the -.B -z -flag is not set, then the word will be left unchanged (except that the -meta-characters will be converted to normal characters). If the -.B -z -flag is set, then the word is only left unchanged if none -of the patterns contain a character that can match anything besides -itself. Otherwise the -.B -z -flag forces the word to be replaced with the names of the files that it -matches, even if there are zero names. -.h "Patterns" -A -.I pattern -consists of normal characters, which match themselves, and meta-characters. -The meta-characters are ``!'', ``*'', ``?'', and ``[''. These characters lose -there special meanings if they are quoted. When command or variable -substitution is performed and the dollar sign or back quotes are not -double quoted, the value of the variable or the output of the command -is scanned for these characters and they are turned into meta-characters. -.PP -Two exclamation points at the beginning of a pattern function as a ``not'' -operator, causing the pattern to match any string that the remainder of -the pattern does -.I not -match. Other occurances of exclamation points in a pattern match -exclamation points. Two exclamation points are required rather than one -to decrease the incompatibility with the System V shell (which does not -treat exclamation points specially). -.PP -An asterisk (``*'') matches any string of characters. -A question mark matches any single character. -A left bracket (``['') introduces a character class. The end of the -character class is indicated by a ``]''; if the ``]'' is missing then -the ``['' matches a ``['' rather than introducing a character class. -A character class matches any of the characters between the square -brackets. A range of characters may be specified using a minus sign. -The character class may be complemented by making an exclamation point -the first character of the character class. -.PP -To include a ``]'' in a character class, make it the first character listed -(after the ``!'', if any). -To include a minus sign, make it the first or last character listed. -.h "The /u Directory" -By convention, the name ``/u/user'' refers to the home directory of the -specified user. There are good reasons why this feature should be supported -by the file system (using a feature such as symbolic links) rather than -by the shell, but -.I ash -is capable of performing this mapping if the file system doesn't. -If the mapping is done by -.IR ash , -setting the -.B -f -flag will turn it off. -.h "Character Set" -.I Ash -silently discards nul characters. Any other character will be handled -correctly by -.IR ash , -including characters with the high order bit set. -.h "Job Names and Job Control" -The term -.I job -refers to a process created by a shell command, or in the case of a -pipeline, to the set of processes in the pipeline. The ways to refer -to a job are: -.d -%\fInumber\fR -%\fIstring\fR -%% -\fIprocess_id\fR -.e -The first form identifies a job by job number. -When a command is run, -.I ash -assigns it a job number -(the lowest unused number is assigned). -The second form identifies a job by giving a prefix of the command used -to create the job. The prefix must be unique. If there is only one job, -then the null prefix will identify the job, so you can refer to the job -by writing ``%''. The third form refers to the \fIcurrent job\fR. The -current job is the last job to be stopped while it was in the foreground. -(See the next paragraph.) The last form identifies a job by giving the -process id of the last process in the job. -.PP -If the operating system that -.I ash -is running on supports job control, -.I ash -will allow you to use it. -In this case, typing the suspend character (typically ^Z) while running -a command will return you to -.I ash -and will make the suspended command the current job. You can then continue -the job in the background by typing -.IR bg , -or you can continue it in the foreground by typing -.IR fg . -.h "Atty" -If the shell variable ATTY is set, and the shell variable TERM is not -set to ``emacs'', then \fIash\fR generates appropriate escape sequences -to talk to -.IR atty (1). -.h "Exit Statuses" -By tradition, an exit status of zero means that a command has succeeded -and a nonzero exit status indicates that the command failed. This is -better than no convention at all, but in practice it is extremely useful -to allow commands that succeed to use the exit status to return information -to the caller. A variety of better conventions have been proposed, but -none of them has met with universal approval. The convention used by -\fIash\fR and all the programs included in the \fIash\fR distribution is -as follows: -.ta 1i 2i -.nf - 0 Success. - 1 Alternate success. - 2 Failure. - 129-... Command terminated by a signal. -.fi -The \fIalternate success\fR return is used by commands to indicate various -conditions which are not errors but which can, with a little imagination, -be conceived of as less successful than plain success. For example, -.I test -returns 1 when the tested condition is false and -.I getopts -returns 1 when there are no more options. -Because this convention is not used universally, the -.B -e -option of -.I ash -causes the shell to exit when a command returns 1 even though that -contradicts the convention described here. -.PP -When a command is terminated by a signal, the uses 128 plus the signal -number as the exit code for the command. -.h "Builtin Commands" -This concluding section lists the builtin commands which are builtin -because they need to perform some operation that can't be performed by a -separate process. In addition to these, there are several other commands -.RI ( catf , -.IR echo , -.IR expr , -.IR line , -.IR nlecho , -.IR test , -.RI `` : '', -and -.IR true ) -which can optionally be compiled into the shell. The builtin -commands described below that accept options use the System V Release 2 -.IR getopt (3) -syntax. -.sp -.b bg -[ -.I job -] ... -.br -Continue the specified jobs (or the current job if no jobs are given) -in the background. -This command is only available on systems with Bekeley job control. -.b command -.IR "command arg" ... -.br -Execute the specified builtin command. (This is useful when you have a -shell function with the same name as a builtin command.) -.b cd -[ -.I directory -] -.br -Switch to the specified directory (default $HOME). -If the an entry for CDPATH appears in the environment of the cd command -or the shell variable CDPATH is set and the directory name does not -begin with a slash, then the directories listed in CDPATH will be -searched for the specified directory. The format of CDPATH is the -same as that of PATH. -In an interactive shell, the cd command will print out the name of the -directory that it actually switched to if this is different from the -name that the user gave. These may be different either because -the CDPATH mechanism was used or because a symbolic link was crossed. -.\" .b ".\fI\h'0.1i'file" -.\" Cawf can't do \h'0.1i' -.b . -.I file -.br -The commands in the specified file are read and executed by the shell. -A path search is not done to find the file because the directories in -PATH generally contain files that are intended to be executed, not read. -.b eval -.IR string ... -.br -The strings are parsed as shell commands and executed. -(This differs from the System V shell, which concatenates the arguments -(separated by spaces) and parses the result as a single command.) -.b exec -[ -.IR "command arg" ... -] -.br -Unless -.I command -is omitted, -the shell process is replaced with the specified program (which must be a real -program, not a shell builtin or function). -Any redirections on the exec command are marked as permanent, so that they -are not undone when the exec command finishes. -If the command is not found, the exec command causes the shell to exit. -.b exit -[ -.I exitstatus -] -.br -Terminate the shell process. If -.I exitstatus -is given it is used as the -exit status of the shell; otherwise the exit status of the preceding -command is used. -.b export -.IR name ... -.br -The specified names are exported so that they will appear in the environment -of subsequent commands. The only way to un-export a variable is to unset it. -.I Ash -allows the value of a variable to be set at the same time it is exported -by writing -.d -\fBexport\fR name=value -.e -With no arguments the export command lists the names of all exported variables. -.b fg -[ -.I job -] -.br -Move the specified job or the current job to the foreground. -This command is only available on systems with Bekeley job control. -.b getopts -.I optstring -.I var -.br -The System V -.I getopts -command. -.b hash -.B -rv -.IR command ... -.br -The shell maintains a hash table which remembers the locations of -commands. With no arguments whatsoever, the hash command prints -out the contents of this table. Entries which have not been looked -at since the last -.I cd -command are marked with an asterisk; it is possible for these entries -to be invalid. -.sp -With arguments, the hash command removes the specified commands from -the hash table (unless they are functions) and then locates them. -With the -.B -v -option, -.I hash -prints the locations of the commands as it finds them. -The -.B -r -option causes the -.I hash -command to delete all the entries in the hash table except for -functions. -.b jobid -[ -.I job -] -.br -Print the process id's of the processes in the job. If the job argument -is omitted, use the current job. -.b jobs -.br -This command lists out all the background processes which are children -of the current shell process. -.b pwd -.br -Print the current directory. The builtin command may differ from the -program of the same name because the builtin command remembers what -the current directory is rather than recomputing it each time. This -makes it faster. However, if the current directory is renamed, the -builtin version of pwd will continue to print the old name for the -directory. -.b read -[ -.B -p -.I prompt -] -[ -.B -e -] -.IR variable ... -.br -The prompt is printed if the -.B -p -option is specified and the standard input is a terminal. Then a -line is read from the standard input. The trailing newline is deleted -from the line and the line is split as described -in the section on word splitting above, and the pieces are assigned to -the variables in order. If there are more pieces than variables, the -remaining pieces (along with the characters in IFS that separated them) -are assigned to the last variable. If there are more variables than -pieces, the remaining variables are assigned the null string. -.sp -The -.B -e -option causes any backslashes in the input to be treated specially. -If a backslash is followed by a newline, the backslash and the newline -will be deleted. If a backslash is followed by any other character, -the backslash will be deleted and the following character will be treated -as though it were not in IFS, even if it is. -.b readonly -.IR name ... -.br -The specified names are marked as read only, so that they cannot be -subsequently modified or unset. -.I Ash -allows the value of a variable to be set at the same time it is marked -read only by writing -.d -\fBreadonly\fR name=value -.e -With no arguments the readonly command lists the names of all -read only variables. -.b set -[ -{ -.BI - options -| -.BI + options -| -.B -- -} -] -.IR arg ... -.br -The -.I set -command performs three different functions. -.sp -With no arguments, it lists the values of all shell variables. -.sp -If options are given, it sets the specified option flags, or clears -them if the option flags are introduced with a -.B + -rather than a -.BR - . -Only the first argument to -.I set -can contain options. -The possible options are: -.sp -.ta 0.4i -.in +0.4i -.ti -0.4i -\fB-e\fR Causes the shell to exit when a command terminates with -a nonzero exit status, except when the exit status of the command is -explicitly tested. The exit status of a command is considered to be -explicitly tested if the command is used to control an -.IR if , -.IR elif , -.IR while , -or -.IR until ; -or if the command is the left hand operand of an ``&&'' or ``||'' -operator. -.sp -.ti -0.4i -\fB-f\fR Turn off file name generation. -.sp -.ti -0.4i -\fB-I\fR Cause the shell to ignore end of file conditions. -(This doesn't apply when the shell a script sourced using the ``.'' -command.) The shell will in fact exit if it gets 50 eof's in a -row. -.sp -.ti -0.4i -\fB-i\fR Make the shell interactive. This causes the shell to -prompt for input, to trap interrupts, to ignore quit and terminate signals, -and to return to the main command loop rather than exiting on error. -.sp -.ti -0.4i -\fB-j\fR Turns on Berkeley job control, on systems that support it. -When the shell starts up, the -.B -j -is set by default if the -.B -i -flag is set. -.sp -.ti -0.4i -\fB-n\fR Causes the shell to read commands but not execute them. -(This is marginally useful for checking the syntax of scripts.) -.sp -.ti -0.4i -\fB-s\fR If this flag is set when the shell starts up, the shell -reads commands from its standard input. The shell doesn't examine the -value of this flag any other time. -.sp -.ti -0.4i -\fB-x\fR If this flag is set, the shell will print out each -command before executing it. -.sp -.ti -0.4i -\fB-z\fR If this flag is set, the file name generation process -may generate zero files. If it is not set, then a pattern which does -not match any files will be replaced by a quoted version of the pattern. -.in -0.4i -.sp -The third use of the set command is to set the values of the shell's -positional parameters to the specified -.IR args . -To change the positional parameters without changing any options, -use ``\fB--\fR'' as the first argument to -.IR set . -If no args are present, the set command will leave the value of the -positional parameters unchanged, so to set the positional parameters -to set of values that may be empty, execute the command -.d -shift $# -.e -first to clear out the old values of the positional parameters. -.b setvar -.I variable -.I value -.br -Assigns -.I value -to -.IR variable . -(In general it is better to write -.I variable=value -rather than using -.IR setvar . -.I Setvar -is intended to be used in functions that assign values to variables whose -names are passed as parameters.) -.b shift -[ -.I n -] -.br -Shift the positional parameters -.I n -times. -A shift sets the value of $1 to the value of $2, the value of $2 to -the value of $3, and so on, decreasing the value of $# by one. -If there are zero positional parameters, shifting doesn't do anything. -.b trap -[ -.I action -] -.IR signal ... -.br -Cause the shell to parse and execute -.I action -when any of the specified signals are received. -The signals are specified by signal number. -.I Action -may be null or omitted; -the former causes the specified signal to be ignored and the latter -causes the default action to be taken. -When the shell forks off a subshell, it resets trapped (but not ignored) -signals to the default action. -The trap command has no effect on signals that were ignored on entry -to the shell. -.b umask -[ -.I mask -] -.br -Set the value of umask (see -.IR umask (2)) -to the specified octal value. If the argument is omitted, the umask -value is printed. -.b unset -.IR name ... -.br -The specified variables and functions are unset and unexported. -If a given name corresponds to both a variable and a function, both the -variable and the function are unset. -.b wait -[ -.I job -] -.br -Wait for the specified job to complete and return the exit status of the -last process in the job. If the argument is omitted, wait for all jobs -to complete and the return an exit status of zero. -.SH EXAMPLES -The following function redefines the \fIcd\fR command: -.d -cd() { - if command cd "$@" - then if test -f .enter - then . .enter - else return 0 - fi - fi -} -.e -This function causes the file ``.enter'' to be read when you enter a -directory, if it exists. The \fIcommand\fR command is used to access the -real \fIcd\fR command. The ``return 0'' ensures that the function will -return an exit status of zero if it successfully changes to a directory -that does not contain a ``.enter'' file. Redefining existing commands -is not always a good idea, but this example shows that you can do it if -you want to. -.PP -The suspend function distributed with -.I ash -looks like -.d -# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. -# This file is part of ash, which is distributed under the terms -# specified by the Ash General Public License. - -suspend() { - local - - set +j - kill -TSTP 0 -} -.e -This turns off job control and then sends a stop signal to the current -process group, which suspends the shell. (When job control is turned -on, the shell ignores the TSTP signal.) Job control will be turned back -on when the function returns because ``-'' is local to the function. -As an example of what \fInot\fR to do, consider an earlier version of -\fIsuspend\fR: -.d -suspend() { - suspend_flag=$- - set +j - kill -TSTP 0 - set -$suspend_flag -} -.e -There are two problems with this. First, \fBsuspend_flag\fR is a global -variable rather than a local one, which will cause problems in the -(unlikely) circumstance that the user is using that variable for some -other purpose. Second, consider what happens if shell received an interrupt -signal after it executes the first \fIset\fR command but before it executes -the second one. The interrupt signal will abort the shell function, so -that the second \fIset\fR command will never be executed and job control -will be left off. The first version of \fIsuspend\fR avoids this problem -by turning job control off only in a local copy of the shell options. The -local copy of the shell options is discarded when the function is terminated, -no matter how it is terminated. -.SH HINTS -Shell variables can be used to provide abbreviations for things which -you type frequently. For example, I set -.br -.\" \h'1i'export h=$HOME -.\" Cawf can't do \h'1i' -.in +1i -export h=$HOME -.in -1i -.br -in my .profile so that I can type the name of my home directory simply -by typing ``$h''. -.PP -When writing shell procedures, try not to make assumptions about what is -imported from the environment. Explicitly unset or initialize all variables, -rather than assuming they will be unset. If you use cd, it is a good idea -to unset CDPATH. -.PP -People sometimes use ``<&-'' or ``>&-'' to provide no input to a command -or to discard the output of a command. A better way to do this is -to redirect the input or output of the command to -.BR /dev/null . -.PP -Word splitting and file name generation are performed by default, -and you have to explicitly use double quotes to suppress it. This is -backwards, but you can learn to live with it. Just get in the habit of -writing double quotes around variable and command substitutions, and -omit them only when you really want word splitting and file name generation. -If you want word splitting but not file name generation, use the -.B -f -option. -.SH AUTHORS -Kenneth Almquist -.SH "SEE ALSO" -echo(1), expr(1), line(1), pwd(1), true(1). -.SH BUGS -When command substitution occurs inside a here document, the commands inside -the here document are run with their standard input closed. For example, -the following will not work because the standard input of the -.I line -command will be closed when the command is run: -.d -cat <<-! -Line 1: $(line) -Line 2: $(line) -! -.e -.PP -Unsetting a function which is currently being executed may cause strange -behavior. -.PP -The shell syntax allows a here document to be terminated by an end of file -as well as by a line containing the terminator word which follows the ``<<''. -What this means is that if you mistype the terminator line, the shell -will silently swallow up the rest of your shell script and stick it -in the here document. -.\" several minor typos corrected -- ASW 2005-01-15 diff --git a/minix/tests/testvnd.sh b/minix/tests/testvnd.sh index 96fdf1833..cbc13451e 100755 --- a/minix/tests/testvnd.sh +++ b/minix/tests/testvnd.sh @@ -142,7 +142,7 @@ vndconfig vnd0 image0 || bomb "unable to configure vnd0" # Invoke the blocktest test set to test various other aspects. vndconfig -S vnd0 image0 || bomb "unable to configure vnd0" cd ../blocktest -. support.sh +. ./support.sh block_test /dev/vnd0 \ "rw,min_read=1,min_write=1,element=1,max=16777216,nocontig,silent" || \ bomb "blocktest test set failed"