This commit was generated by cvs2svn to compensate for changes in r2, which

included commits to RCS files with non-trunk default branches.
This commit is contained in:
tg 2003-03-22 17:35:03 +00:00
parent 7fbdbe578d
commit 80609f6010
85 changed files with 51195 additions and 0 deletions

1398
BUG-REPORTS Normal file

File diff suppressed because it is too large Load Diff

129
CONTRIBUTORS Normal file
View File

@ -0,0 +1,129 @@
$OpenBSD: CONTRIBUTORS,v 1.9 1999/07/14 13:37:23 millert Exp $
This is a partial history of this shell gleened from old change logs and
readmes (most of which are still in the misc directory) and the source
code. Hopefully it is correct and no contributors have been left out
(file a bug report if you spot a problem :-)).
Release history:
* Eric Gisin (egisin@math.uwaterloo.ca), created pdksh, using
Charles Forsyth's public domain V7 shell as a base; also used parts
of the BRL shell (written by Doug A Gwyn, Doug Kingston, Ron Natalie,
Arnold Robbins, Lou Salkind, and others?, circa '87; the parts used in
pdksh included getopts, test builtin, ulimit, tty setting/getting, emacs
editing, and job control; the test builtin was based on code by Erik
Baalbergen).
'87..'89 ?
Released versions: .. 3.2
* John R MacMillan (@yonge.csri.toronto.edu:chance!john@sq.sq.com)
takes over as maintainer
dates?
Released versions: 3.3 (?)
* Simon J. Gerraty (sjg@zen.void.oz.au) takes over as maintainer
Nov '91..July '94 ?
Released versions: 4.0 .. 4.9
* Michael Rendell (michael@cs.mun.ca) takes over as maintainer
July, 1994
Released versions: 5.0 .. 5.2
Major contributions:
* John R MacMillan (@yonge.csri.toronto.edu:chance!john@sq.sq.com), ?:
cleaned up configuration, many bug fixes (see misc/Changes.jrm).
* Simon Gerraty, (sjg@zen.void.oz.au), Nov '91..?: much improved emacs mode
ala at&t ksh, 386bsd port, sigaction routines for non-POSIX systems
(see misc/ChangeLog.sjg and misc/ReadME.sjg).
* Peter Collinson (pc@hillside.co.uk), July '92: added select, at&t ksh
style history file, original csh-style {} globbing, BSD/386 port,
misc bug fixes.
* Larry Bouzane (larry@compusult.nf.ca), Mar '89..'93: re-wrote job control,
added async job notification, added CDPATH and other cd fixes, misc bug
fixes.
* John Rochester (jr@cs.mun.ca), '87: wrote vi command line editor; various
bug fixes/enhancements.
* Jeff Sparkes (jsparkes@bnr.ca), Mar '89..Mar '90: added arrays,
merged John Rochester's vi code into pdksh, misc bug fixes.
* Michael Haardt (u31b3hs@POOL.Informatik.RWTH-Aachen.DE), Sept '94:
organized man page, filled in many of its copious blank spots; added
KSH ifdefs.
* Dale DePriest (daled@cadence.com): ported to OS/2 (initially based on
port of pdksh4.9 to OS/2 by Kai Rommel (rommel@ars.muc.de)); maintains
OS/2 port; misc bug fixes.
Other contributors:
* Piercarlo Grandi (pcg@aber.ac.uk), Dec '93: fixes for linux port
* Neil Smithline (Neil.Smithline@eng.sun.com), Aug '92: emacs-style
filename completion.
* Mike Jetzer [mlj] (jetzer@studsys.mscs.mu.edu), ?;Nov '94: fixes for vi
mode (see misc/Changes.mlj), added v to vi, fixes for history; fixed
command redoing in vi; fixes to vi globbing.
* Robert J Gibson: mailbox checking code that was adapted for pdksh by
John R. MacMillan.
* ? (guy@demon.co.uk), ?: promptlen() function.
* J.T. Conklin (jtc@cygnus.com): POSIXized test builtin; miscellaneous
fixes/enhancements.
* Sean Hogan (sean@neweast.ca): fixes for ICS 3.0 Unix, found and helped
fix numerous problems.
* Gordan Larson (hoh@approve.se): fix to compile sans VI, ksh.1 typo.
* Thomas Gellekum (thomas@ghpc8.ihf.rwth-aachen.de): fixes for Makefile
typos, fixed CLK_TCK for FreeBSD, man page fixes.
* Ed Ferguson (Ed.Ferguson@dseg.ti.com): fix to compile sans VI.
* Brian Campbell (brianc@qnx.com): fixes to compile under QNX and
to compile with dmake.
* (guy@netapp.com), Oct '94: patch to use gmacs flag.
* Andrew Moore (alm@netcom.com): reported many bugs, fixes.
* William Bader (wbader@CSEE.Lehigh.Edu): fix to compile on SCO Unix
(strut winsize).
* Mike Long (mike.long@analog.com): makefile fix - use $manext, not 1.
* Art Mills (aem@hpbs9162.bio.hp.com): bug fix for vi file completion in
command mode.
* Tory Bollinger (tboll@authstin.ibm.com): allow ~ in vi mode to take
a count.
* Frank Edwards (<crash@azhrei.EEC.COM>): added macros to vi (@char).
* Fritz Heinrichmeyer (<Fritz.Heinrichmeyer@FernUni-Hagen.de>): fixes
to allow compile under Linux 1.4.3.
* Gabor Zahemszky (<zgabor@CoDe.hu>): SVR3_PGRP vs SYSV_PGRP, many
bug reports and man page fixes.
* Dave Kinchlea (<kinch@julian.uwo.ca>): DEFAULT_ENV patches.
* Paul Borman (<prb@bsdi.com>): j_exit: send HUP, then CONT; HUP fg process.
* DaviD W. Sanderson (<dws@ssec.wisc.edu>): patches to allow { .. } instead
of in .. esac in case statements.
* ? (<ra@rhi.hi.is>): partial patches to handle SIGWINCH for command line
editing.
* Jason Tyler (<jason@nc.bhpese.oz.au>): fixes for bugs in fc.
* Stefan Dalibor (<Stefan.Dalibor@informatik.uni-erlangen.de>): fix for
COLUMNS never being set in x_init().
* Arnon Kanfi (<arnon@gilly.datatools.com>): fix for prompt.
* Marc Olzheim (<marcolz@stack.nl>): patches to ifdef KSH the mail check
code and aliases; enum patches for old K&R compilers; handle missing dup2.
* Lars Hecking (<lhecking@nmrc.ucc.ie>): fixes so shell compiles as sh
again.
* Bill Kish (<kish@browncow.com>): added prompt delimiter hack for
hidden characters (eg, escape codes).
* Andrew S. Townley (<atownley@informix.com>): fixes for NeXT machines:
get a controlling if one needed, use correct profile.
* Eric J. Chet (<ejc@bazzle.com>): fix for core dump in . (quitenv() called
too soon).
* Greg A. Woods <woods@most.weird.com>: fix to make ^[_ in emacs work
as in at&t ksh.
* George Robbins <grr@shandakor.tharsis.com>: fix for sh mode to
keep exec'd file descriptors open.
* George White <gwhite@bodnext.bio.dfo.ca>: fix here-doc problem under OS/2
(memory allocated incorrectly).
* David E. Wexelblat <dwex@DataFocus.com>: fix to avoid memory overrun
in aresize(); fix to not print un-named options.
* Clifford Wolf (<clifford@clifford.at>): fix memory overrun in aresize();
fixed sys_siglist[] problem.
* Theo de Raadt (<deraadt@cvs.openbsd.org>): allow ". /dev/null".
* Eric Youngdale (<ericy@datafocus.com>): flag field incorrectly changed
in exec.c(flushcom).
* Todd. C Miller (Todd C. Miller <Todd.Miller@courtesan.com>): fix
for coredump in jobs.
* Kevin Schoedel <schoedel@kw.igs.net>: fix for word location in file
completion.
* Martin Lucina <mato@kotelna.sk>: fix for argument parsing in exit command,
fix for KSH_CHECK_H_TYPE.
* Mark Funkenhauser <mark@interix.com>: added $LINENO support.
* Corinna Vinschen <Corinna@Vinschen.de> and Steven Hein <ssh@sgi.com>:
port to cyngin environment on win95/winnt.
* Martin Dalecki <dalecki@cs.net.pl>: changes for 8 bit emacs mode.
* Dave Hillman <daveh@gte.net>: patch for bug in test -nt.

1600
ChangeLog Normal file

File diff suppressed because it is too large Load Diff

3589
ChangeLog.0 Normal file

File diff suppressed because it is too large Load Diff

18
IAFA-PACKAGE Normal file
View File

@ -0,0 +1,18 @@
$OpenBSD: IAFA-PACKAGE,v 1.7 1999/07/14 13:37:23 millert Exp $
Title: pdksh
Version: 5.2.14
Description: A public domain implementation of the Korn shell (ksh88),
a UNIX command line interpreter / scripting language; the few
missing ksh features are being added and the shell is being
POSIXized.
Author: (Eric Gisin), (Charles Forsyth), (John R MacMillan),
sjg@zen.void.oz.au (Simon J. Gerraty),
michael@cs.mun.ca (Michael Rendell), (plus many others)
Maintained-by: michael@cs.mun.ca (Michael Rendell)
Maintained-at: ftp://ftp.cs.mun.ca:/pub/pdksh/
Platforms: Written in C, runs on most UNIX boxes (uses GNU autoconf;
works best in a POSIX or BSD environment). Also runs on OS/2
and (using cygwin32 package) on win95/NT
Copying-Policy: Freely Redistributable (mostly public domain, some copyrighted)
Keywords: pdksh, ksh, Korn, shell, command line interpreter

151
INSTALL Normal file
View File

@ -0,0 +1,151 @@
$OpenBSD: INSTALL,v 1.1.1.1 1996/08/14 06:19:10 downsj Exp $
[This file is the generic GNU autoconf/configure installation description,
see the README for pdksh specific configuration/installation information]
This is a generic INSTALL file for utilities distributions.
If this package does not come with, e.g., installable documentation or
data files, please ignore the references to them below.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation, and
creates the Makefile(s) (one in each subdirectory of the source
directory). In some packages it creates a C header file containing
system-dependent definitions. It also creates a file `config.status'
that you can run in the future to recreate the current configuration.
To compile this package:
1. Configure the package for your system.
Normally, you just `cd' to the directory containing the package's
source code and type `./configure'. If you're using `csh' on an old
version of System V, you might need to type `sh configure' instead to
prevent `csh' from trying to execute `configure' itself.
Running `configure' takes awhile. While it is running, it
prints some messages that tell what it is doing. If you don't want to
see any messages, run `configure' with its standard output redirected
to `/dev/null'; for example, `./configure >/dev/null'.
To compile the package in a different directory from the one
containing the source code, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. If
for some reason `configure' is not in the source code directory that
you are configuring, then it will report that it can't find the source
code. In that case, run `configure' with the option `--srcdir=DIR',
where DIR is the directory that contains the source code.
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'. Alternately, you can do so by consistently
giving a value for the `prefix' variable when you run `make', e.g.,
make prefix=/usr/gnu
make prefix=/usr/gnu install
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH' or set the `make'
variable `exec_prefix' to PATH, the package will use PATH as the prefix
for installing programs and libraries. Data files and documentation
will still use the regular prefix. Normally, all files are installed
using the same prefix.
Some packages pay attention to `--with-PACKAGE' options to
`configure', where PACKAGE is something like `gnu-as' or `x' (for the
X Window System). They may also pay attention to `--enable-FEATURE'
options, where FEATURE indicates an optional part of the package. The
README should mention any `--with-' and `--enable-' options that the
package recognizes.
`configure' also recognizes the following options:
`--help'
Print a summary of the options to `configure', and exit.
`--quiet'
`--silent'
Do not print messages saying which checks are being made.
`--verbose'
Print the results of the checks.
`--version'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--x-includes=DIR'
X include files are in DIR.
`--x-libraries=DIR'
X library files are in DIR.
`configure' also accepts and ignores some other options.
On systems that require unusual options for compilation or linking
that the package's `configure' script does not know about, you can give
`configure' initial values for variables by setting them in the
environment. In Bourne-compatible shells, you can do that on the
command line like this:
CC='gcc -traditional' LIBS=-lposix ./configure
On systems that have the `env' program, you can do it like this:
env CC='gcc -traditional' LIBS=-lposix ./configure
Here are the `make' variables that you might want to override with
environment variables when running `configure'.
For these variables, any value given in the environment overrides the
value that `configure' would choose:
- Variable: CC
C compiler program. The default is `cc'.
- Variable: INSTALL
Program to use to install files. The default is `install' if you
have it, `cp' otherwise.
For these variables, any value given in the environment is added to
the value that `configure' chooses:
- Variable: DEFS
Configuration options, in the form `-Dfoo -Dbar...'. Do not use
this variable in packages that create a configuration header file.
- Variable: LIBS
Libraries to link with, in the form `-lfoo -lbar...'.
If you need to do unusual things to compile the package, we encourage
you to figure out how `configure' could check whether to do them, and
mail diffs or instructions to the address given in the README so we
can include them in the next release.
2. Type `make' to compile the package. If you want, you can override
the `make' variables CFLAGS and LDFLAGS like this:
make CFLAGS=-O2 LDFLAGS=-s
3. If the package comes with self-tests and you want to run them,
type `make check'. If you're not sure whether there are any, try it;
if `make' responds with something like
make: *** No way to make target `check'. Stop.
then the package does not come with self-tests.
4. Type `make install' to install programs, data files, and
documentation.
5. You can remove the program binaries and object files from the
source directory by typing `make clean'. To also remove the
Makefile(s), the header file containing system-dependent definitions
(if the package uses one), and `config.status' (all the files that
`configure' created), type `make distclean'.
The file `configure.in' is used to create `configure' by a program
called `autoconf'. You only need it if you want to regenerate
`configure' using a newer version of `autoconf'.

36
LEGAL Normal file
View File

@ -0,0 +1,36 @@
$OpenBSD: LEGAL,v 1.1.1.1 1996/08/14 06:19:10 downsj Exp $
pdksh is provided AS IS, with NO WARRANTY, either expressed or implied.
The vast majority of the code that makes pdksh is in the public domain.
The exceptions are:
sigact.c and sigact.h
These are covered by copyrighten by Simon J. Gerraty;
the copyright notice for these files is as follows:
This is free software. It comes with NO WARRANTY.
Permission to use, modify and distribute this source code
is granted subject to the following conditions.
1/ that that the above copyright notice and this notice
are preserved in all copies and that due credit be given
to the author.
2/ that any changes to this code are clearly commented
as such so that the author does get blamed for bugs
other than his own.
aclocal.m4
This is covered by the GNU General Public Licence (GPL)
as it contains modified versions of macros that come with
GNU autoconf. As this is used solely for configuration,
the pdksh code itself is not covered by the GPL.
The following is taken from autoconf 2.x documentation
(info autoconf questions distributing) concerning use
of autoconf in programs:
There are no restrictions on how the configuration
scripts that Autoconf produces may be distributed
or used. In Autoconf version 1, they were covered by
the GNU General Public License. We still encourage
software authors to distribute their work under terms
like those of the GPL, but doing so is not required
to use Autoconf.
That's it. Short and simple.

33
Makefile Normal file
View File

@ -0,0 +1,33 @@
# $OpenBSD: Makefile,v 1.13 2002/04/22 21:44:58 miod Exp $
PROG= ksh
SRCS= alloc.c c_ksh.c c_sh.c c_test.c c_ulimit.c edit.c emacs.c \
eval.c exec.c expr.c history.c io.c jobs.c lex.c mail.c \
main.c misc.c missing.c path.c shf.c syn.c table.c trap.c \
tree.c tty.c var.c version.c vi.c
DEFS= -DHAVE_CONFIG_H -Wall -Wno-unused
CFLAGS+=${DEFS} -I. -I${.CURDIR} -DKSH
MAN= ksh.1 sh.1
CLEANFILES+= siglist.out emacs.out
LINKS= ${BINDIR}/ksh ${BINDIR}/rksh
LINKS+= ${BINDIR}/ksh ${BINDIR}/sh
MLINKS= ksh.1 rksh.1 ksh.1 ulimit.1
.depend trap.o: siglist.out
.depend emacs.o: emacs.out
siglist.out: config.h sh.h siglist.in siglist.sh
/bin/sh ${.CURDIR}/siglist.sh \
"${CPP} ${CPPFLAGS} ${DEFS} -I${.CURDIR}" \
< ${.CURDIR}/siglist.in > siglist.out
emacs.out: emacs.c
/bin/sh ${.CURDIR}/emacs-gen.sh ${.CURDIR}/emacs.c > emacs.out
check test:
/bin/sh ${.CURDIR}/tests/th.sh ${.CURDIR}/tests/th -s ${.CURDIR}/tests -p ./ksh -C pdksh,sh,ksh,posix,posix-upu
.include <bsd.prog.mk>

662
NEWS Normal file
View File

@ -0,0 +1,662 @@
Version 5.2.14
* bug fixes
* test: -nt(-ot) now succeed if second (first) file doesn't exist and the
other does.
* time: now accepts the -p (posix) option.
* vi: failed redo (.) commands no longer return the line to the shell.
* emacs: bind commands in .profile/$ENV no longer overridden by tty
settings.
* heredocs: now saved in memory to avoid temp files disappear too soon.
* time: commands at the end of a pipeline can now be timed.
* on startup: MAILCHECK,TMOUT,SECONDS values from environment are honoured.
* trap: exit traps now executed in subshells (without explicit exit call).
* eval: if given empty command while in non-posix mode, exit status is
that of last command substitution (if any).
* trap: if first argument is "exit", it is taken as a command not a signal.
* pwd: config test & code to work around bug in hpux 10.x getcwd().
* RANDOM: seed based on both time and pid; different sequence in sub shells.
* typeset -f: now pretty-prints $(..) and $((..)) correctly.
* fixed bug in memory allocation which can lead to core dumps.
* set -o: no longer prints options that have no names.
* emacs: <esc>. in very fist command no longer dumps core.
* .: can now dot non-regular files (eg, /dev/null).
* parsing: bar(); no longer dumps core when function bar is run.
* variable substitution: ${#array[*]} now prints number of set elements
in the array (used to print largest index, not what ksh88 did).
* job control: resuming suspended gnu su no longer hoses su'd shell.
* job control: fixed possible core dump when waiting for jobs.
* LINENO variable now supported.
* port to cygwin environment on win95/winnt.
Version 5.2.13
* bug fixes
* functions: $0 in sh-style functions is now the same as the shell's $0.
* .: fixed possible core dump on clean up.
* test: a lone -t argument now does a isatty(1) test if not in posix mode.
* alias: PS2 no longer printed when expanding alias foo="cmd; ".
* set/typeset/getopts: can have options prefixed with both + and -.
* typeset -f: now reproduces functions correctly ("function F" vs "F()").
* alias: options can start with +.
* set -A: a -- marking end of options is now skipped.
* errexit option (-e) ignored when reading profile and $ENV files.
* test: '-x f' now fails for root if f is a file and has no x bits set.
* $_: set to last arg in interactive shells only.
* PATH: if $PATH not set on startup, it is set to the default path.
* extended globbing: allow (pat|pat) within @(...) and ${foo#...} patterns.
* emacs: ^[_ now behaves as it does in at&t ksh (word from _last_ command).
* MAIL/MAILCHECK: fixed bug that prevented the `new mail' messages.
* ${..%..} and ${..#..} now work if compiled as sh.
* sh: fd's greater than 2 are passed on to executed commands.
* syntax: accepts "if (( 1 )) then" (also [[ ]]): no ; required before then.
* substitution: accepts (and ignores) leading : in %, %%, #, ## substitions.
* .: doting directories no longer allowed.
* editing: completion after "cmd " now completes files (was command).
$OpenBSD: NEWS,v 1.12 2003/02/28 09:45:09 jmc Exp $
Version 5.2.12
* bug fixes
* editing: shell recognizes window resizes on Dec alphas (config problem).
* alias: no longer dumps core if alias is in a command substitution.
* alias: everything after ;\n or \n\n was ignored in aliases.
* exec: temp files used by here docs in functions now cleaned up on exec.
* possible core dump when cleaning up environment fixed.
* vi: set -o vi-show8 now does what it was supposed to do (cat -v like).
* job control: process group synchronization (needed on systems with
broken setpgrp()) now works when the pipeline contains built in commands.
* vi: if set -o vi-tabcomplete, tab works in command mode as well.
* set/typeset: unset parameters are only reported if they have attributes.
Version 5.2.11
* bug fixes
* aliases: expansion was reading an extra character (bug added in 5.2.10).
Version 5.2.10
* bug fixes
* parsing: handling of backslash-newline fixed (esp. in here documents).
* read: prints prompt if non-interactive and input is a tty.
Version 5.2.9
* bug fixes
* config: using LDSTATIC no longer generates config error.
* config: can compile as sh again (--enable-shell=sh).
* config: should compile on machines with broken "gcc -g"
* config: fixed test for broken S_IFIFO.
* config: fixed test for getwd() routine.
* config: better NeXT support (signal list generated correctly, clock_t
type detected, enable job control in rlogin sessions)
* parsing: assignments containing brackets ([]) not treated as commands.
* editing: terminal column width determined correctly on startup.
* vi: long prompts truncated (more or less) correctly.
* file completion: files of the form ~user (no /'s) expanded correctly.
* at&t ksh method for delimiting hidden characters in prompt added (i.e.,
start prompt with non-printing char and \r, use char to delimit esc codes).
Version 5.2.8
* bug fixes
* configuration: handle FreeBSD's strange S_ISSOCK.
* test: added == operator.
* configuration: fixed opendir/dirent usage.
* redirections before subshells handled correctly.
* COLUMNS/LINES are no longer exported when they are automatically set.
* mail checks and PS1/PS4 expansions removed if compiled as sh.
* subcommands in PS1 no longer generate bogus warning messages.
* environment variables not longer messed up on 16-bit machines.
* unset: now returns non-zero if variable/function isn't set.
* select: only prints menu first time, if REPLY is null or on blank line.
* check for `cannot execute' improved, error message says why.
* typeset: now reports variables with attributes but now value.
* vi/emacs file completion: does directory listing on zero length names.
* arithmetic: non-numeric parameters expanded recursively.
* arithmetic: identifiers in unevaluated part of ?:,&&,|| parsed correctly.
* functions: unsetting a function within itself is now safe.
* arrays: unsetting element 0 of an array no longer kills the whole array.
* co-processes now behave like ksh93 co-processes (and less like ksh88).
* functions declared with "function foo" are treated differently (from those
declared with "foo()"): $0 is (not) set to the function name, assignments
before function calls aren't (are) kept in the parent shell.
* vi: added vi-esccomplete option for people who want ESC-ESC completion.
* vi/emacs: now notice window size changes (but not while editing a line).
* emacs: <esc># now does the comment/uncomment thing.
* arithmetic: ++, -- and , added.
Version 5.2.7
* bug fixes
* vi: commands can be longer that 16 chars...
Version 5.2.6
* bug fixes
* break/continue: if too big a number is given, last enclosing loop is used.
* set: set +o now generates a set command that can be saved and executed.
* COLUMNS/LINES are now exported when they are automatically set.
* emacs: completion: space not added after directory names.
* vi: # command inserts # after each newline; # on commented line
undoes the commenting.
* some regression tests made less sensitive to their environment.
* should compile on os/2 again.
Version 5.2.5
* bug fixes
* configuration: if sig_setjmp() being used, use sigjmp_buf.
* configuration: test for times() fixed.
* configuration: ANSI usage of setjmp() and offsetof().
* echo/print: octal number in \ sequence must start with a 0.
* echo: don't treat a lone minus as an option.
* typeset -f: correctly prints functions with select statements.
* vi: / with no pattern repeats last search.
* vi: repeat counts no longer effect file completion/expansion.
* vi: tab-completion now also works in command mode.
* emacs/vi: ^O key now read as ^O on suns/alphas (was eaten by tty driver).
* emacs: now has file expansion (^[*).
* emacs: ^O goes to next command, not next next command.
* COLUMNS/LINES: environment variables now set on start up.
* variables: command line assignments can't change readonly variables.
* arithmetic: giving multiple bases (5#4#3) no longer allowed.
* arithmetic: when assigning a non-integer variables, base no longer shown.
* history: fixed replacement bug introduced in last release.
* history: -1 refers to the previous command, not current fc command.
* parsing: correctly handles command substitutions starting with a newline.
* full command completion added (both vi and emacs).
Version 5.2.4
* bug fixes
* PS1 imported from environment again.
* vi handles prompts with embedded newlines.
* errors redirecting stderr aren't lost.
* redirection errors for <&n no longer reported as to >&n.
* don't do globbing on re-direction targets if not interactive (POSIX).
* pattern matching in [[ foo = foo*bar ]] now works again.
* HUP signals are passed on to jobs running in the foreground.
* $? now valid (ie, not 0) in trap handlers, `...` expressions, etc.
* noclobber doesn't effect redirections to non-regular files (eg, /dev/null)
* \newline in here-document delimiters handled correctly.
* typeset -f now reports unloaded autoload functions properly.
* ~,~+,~- are not expanded if HOME,PWD,OLDPWD are unset.
* vi completion/expansion: * not appeded if word contains $.
* cd: error message contains correct directory string.
* vi expansion list: printed in column form ala at&t ksh.
* ^C while reading .profile/$ENV nolonger causes shell to exit.
* option errors for build-in commands now include command name.
* emacs completion/expansion: ' and " are treated as word delimiters.
* fc: replacements (a=b) no longer truncates the command.
* alias: alias -t -r now cleans out the tracked alias table.
* compile-time configuration changed: configure script --enable-XXX options
replace the old options.h file. Use "configure --help" for information
on what the options do (they are basicly the same as what was in the
options.h file). Shell can be configured as a (almost) plain bourne
shell using the --enable-shell=sh (also generates appropriate man page).
Installed name of program (ksh or sh) can be modified using configure's
--program-* options.
* ulimit: added -p (maxproc) option.
* case statements can use the old syntax of {,} instead of in,esac.
* extended file globbing added (eg, f*(bar|Bar) matches f, fbar fBarbar, etc).
* trim expressions can be of the form ${parameter#pattern1|pattern2|...}.
* if compiled as sh, $ENV included only if posix option is set.
* vi: U command added (undo all changes on line).
* the Bugs script has been replaced by a new regression testing system, kept
in the tests/ directory (contains a perl script which sets up a test
environment and runs tests, and a bunch of tests).
Version 5.2.3
* bug fixes
* arrays: set -A and unset now unset whole array.
* history(complex version): fixed core caused by uninitialized hist_source.
* getopts: will continue parsing options if called after error.
* getopts: doesn't print shell name twice in error message.
* posix: if posix option is set, $0 is always the name of the shell.
* history: "fc -s foo" now finds foo if it is the most recent command.
* let: expression errors no longer cause scripts to exit.
* PS1: does not go into infinite loop if there is an expansion error.
* configure: memmove/bcopy test has a change of working now.
* configure: check for flock(), undefine COMPLEX_HISTORY if not found.
* substitution: tilde substitution works in word part of ${var[-+=?]word}.
* history: "fc <number>" now edits <number>, not <number> to most recent.
* cd: two argument form works again.
* special commands taking assignments (alias,set,etc.): field splitting,
file globbing, etc. suppressed only for args that look like assignments.
* command: -V now finds reserved words.
* added support for Korn's /dev/fd tests
* new compile time option: DEFAULT_ENV - if defined, it names a file to
include if $ENV is not set.
* test -o option: if option starts with a !, the test is negated. The test
always fails if the option doesn't exist (so [ -o foo -o -o !foo ] is true
iff option foo exists).
* new option: set -o nohup (currently on by default) - if set, running jobs
are not kill -HUP'd when a login shell exits; if clear, they are. In
future, this will be clear by default (to act like at&t ksh) - if you don't
(won't) like this, add "[ -o !nohup ] && set -o nohup" to your .profile.
Version 5.2.2
* bug fixes
* included c_test.h in distribution (opps).
Version 5.2.1
* bug fixes
* emacs: buffer no longer overflowed when completing file names/commands.
* emacs: <ESC><tty-erase-char> now bound to delete-back-word (was ...-char).
* emacs: ignores a space char after ^V (version), as in at&t ksh.
* emacs: ^O bound to newline-and-next, ^X^Y bound to list-file.
* emacs: emacs words now include underscore.
* vi: set -o markdirs, directories and ^[= now get along.
* cd: -P no longer leaves .. and . in PWD.
* cd: if CDPATH set and can't cd, error doesn't contain any of CDPATH.
* cd: sets PWD properly, on machines without getwd().
* configuration: unistd.h test fixed (include sys/types before dirent.h).
* configuration: detects memmove/bcopy's that don't handle overlaps.
* [[ ... ]] does lazy evaluation (eg, [[ ! -f foo || $(<foo) = bar ]] does
not evaluate $(<foo) if foo doesn't exist).
Version 5.2.0
* bug fixes
* vi: completion now allows globbing characters.
* vi: can deal with very long prompts.
* vi: . (redo) works after j, k, return.
* vi: [dyc]% causing backwards motion now gets correct start/end position.
* vi: complete_word (<ESC>\) no longer rings bell on ambiguous matches.
* vi: globbing doesn't append * if last component of file has globbing chars.
* emacs: most commands now take arguments, arguments can be multi digit.
* emacs: newline-and-next command works more correctly.
* after set -u, trimming substitutions no longer automatically fail.
* set -i no longer reports an internal error.
* FPATH: no longer incorrectly complains about function not being defined.
by a file; when it connectly complains, shell name in error is correct.
* set -a; set -o allexport: these now do something.
* shell deals with non-blocking input (clears non-blocking flag).
* autoconf: fixed memmove/memcpy tests.
* ! translation in prompt now done before parameter substitution.
* siglist.sh works around bug in bash 1.4.3.
* correct positional parameters accessible in local assignments.
* (sleep 100&) no longer waits for sleep to complete.
* fc -s option added (same as -e -).
* vi: ^V command (version) added.
* vi: @<char> macros added (@X executes vi commands in alias _X).
* emacs: bind -l lists all command names.
* emacs: goto-history command added.
* emacs: search-char function changed to search-char-forward;
added search-char-backward (bound to <ESC>^]).
* cd and pwd take -L and -P options; added set -o physical option
(PWD,OLDPWD no longer readonly).
* new command line -l option tells shell it is a login session.
* os2 changes completed.
* uses autoconf 2.x (was using 1.x).
Version 5.1.3
* bug fixes
* fixed bug in arithmetic expression evaluation (||,&& caused core dump).
* ulimit code now uses rlim_t or quad_t, if appropriate.
* vi: file completion in command mode of single character filename works.
* vi: file completion with markdirs set resulted in two trailing /'s.
* vi: completion/expansion/listing acts like at&t ksh when expand fails.
* vi: ~ takes count.
* lines from history file are no longer negative (easy history).
* Makefile now uses manual extension consistently.
* fc now allows out of range relative (negative) numbers.
* functions with elif now printed correctly.
* FPATH now searched if PATH search fails, as in at&t ksh.
* typeset -f output is readable (and more correct)
* compiles under SCO unix
* more os/2 changes integrated
Version 5.1.2
* bug fixes
* for i; do ...; done now accepted.
* leading non-white-space IFS chars no longer ignored (now delimit fields).
* fixed globbing code so echo /usr/*/make works.
Version 5.1.1
* bug fixes
* { ..;} allowed instead of do ..;done in for/select loops
* EOF after ; or & no longer causes syntax error
* complex history: when shrinking history file, keeps inside buffer space.
* vi editing: `v' on modified line no longer changes command numbering.
* ^C in vi/emacs no longer prints two newlines.
* long arguments (> 255) with globbing characters don't cause core dumps.
* new (un)option, KSH, which compiles out ksh code (for producing minimal sh).
* os/2 changes partly merged.
Version 5.1.0
* bug fixes
* problem caused by _POSIX_VDISABLE on BSDI machines fixed
* exit status set to 127 if command file could not be opened
* profile files processed if basename argv[0] starts with (was $0)
* PWD now imported properly from environment.
* emacs code now either uses dynamic buffers or does overflow checking.
* emacs forward-word and delete-forward-word now work like other emacs's.
* ^C/^\ in vi/emacs work like at&t ksh (prompt reprinted, even if trapped).
* history number to command mapping now constant (numbers used to change).
* configuration: BSD tty now used on ultrix (avoids type ahead problem)
* eof in the middle of multiline commands now ignored if ignoreeof set.
* vi space command now works again.
* pointer mismatch compiler warning for waitpid() call dealt with.
* emacs internal memory error in command completion fixed.
* autoloaded functions now work first try.
* SECONDS parameter now acts like in at&t ksh.
* sense of vi-show8 option changed: 8-bit characters are printed as is by
default; turning on vi-show8 now causes chars with 8th bit set to be
prefixed with M-.
* missing sections in man page added (now basicly complete)
* emacs ^V command added: prints ksh version
* vi g command added: moves to most recent history
Version 5.0.10
* bug fixes
* [[ ]] construct unbroken.
* the newline after a here document marker is now read properly.
* blank lines no longer cause $? to be set to 0.
* mail checking now uses atime/mtime instead of size.
* changing attributes of exported parameters no longer causes core dump.
* the last command in a file does not have to end in a newline.
* empty expressions now treated as 0 (previously generated an error).
* nul bytes stripped from input.
* 0241 (M-!) in a command substitution no longer lost.
* when read used in startup file, line continuation no longer causes crash.
* very long commands in history no longer cause vi to overwrite memory.
* easy history: when saving history, avoid going past the end of history.
* emacs mode no longer entered if EDITOR/VISUAL set to null string.
* command -p disabled in restricted mode.
* closed file descriptors are re-closed after a redirection.
* lone [ (test command) no longer causes globbing code to search directory.
* if TIMES_BROKEN is defined, ksh_times no longer recurses infinitely.
* `r r' no longer repeats r command forever.
* make depend no longer generates backslash followed by a blank line.
* globbing code now deals with symlinks that point to non-existent files.
* if the ] is missing in a pattern, the [ matches the [ character.
* syntax errors in test no longer have two newlines.
* in vi, G now goes to the oldest history (was newest).
* configuration: test for sys_siglist now harder for optimizers to break.
* configuration: look for clock_t in sys/times.h.
* configuration: use _SIGMAX, if available, for # of signals.
* SIGHUP now causes builtin read command to exit.
* wait builtin now returns whenever a traped signal occurs as per POSIX.
* v command now works in vi; anchored searches now work in vi mode (/^ptrn);
multi-line commands displayed correctly by history.
* echo is now schizophrenic: accepts -n/-e/-E and backslash sequences.
* test -H file added (checks for context dependent files on HPs).
* set -o gmacs and markdirs honoured.
* ansi arrow keys in default emacs key bindings.
* ulimit now takes arithmetic expression (as per Korn book).
* co-processes changed to be more compatible with at&t ksh.
Version 5.0.9
* bug fixes
* FOO is put in the environment for FOO=bar exec blah.
* compiles under QNX and with dmake.
* the file pattern [!a--]* is now invalid (POSIX) (used to match everything)
* echo "${foo:-"a"}*" no longer the same as echo a*.
* alternation (brace expansion) fixes:
* brace expansion done after variable expansion, as in csh/at&t ksh.
* `echo a{b,c' no longer gives "Missing }" error (it echos a{b,c).
* expansion only done if there is a comma (ie, `echo {a}' prints {a}).
* globbing/expansion code passes 0x80 unharmed.
* "echo ${XX=a*b}" no longer sets XX to "a\200*b".
* "echo ${unset-a*b}" no longer has \200 in the error message.
* bad substitution error generated for things like ${x:a}, ${x^a}, etc.
* `x="a cdef"; echo ${x#a c}' now prints "def" instead of "a a cdef".
* on systems where /etc/passwd//// is a valid name, echo /etc/pass*/ no
longer matches /etc/passwd.
* trace output (set -x) flushed correctly, PS4 initialized.
* ulimit output ungarbled, code to use {set,get}ulimit (if available)
enabled.
* tilde expansion done in word part of ${foo-~/bar}
* when reading stdin (ie, ksh -s), no longer reads too much.
* shell handles i/o redirection and errors in builtin commands as per
POSIX (still have to sort out variable assignment errors).
* starting jobs that save/change/restore tty settings in the background
no longer messes up tty settings when job finishes.
* the pattern [a'-'z] now matches three characters, not 26, and
the pattern [ab']'] also matches three characters.
* a mostly complete man page! (work is still in progress)
* quoting inside $(..) mostly works.
* error reporting has been orthogonalized.
* brace expansion on by default (can be disabled with set +o braceexpand, or
set -o posix).
* output of "set -o" now fits on a normal screen.
* co-processes added (|&, read -p, print -p, etc.).
* restricted mode added (for what its worth).
* vi now prints meta characters with M- prefix, unless vi-show8 option is on.
Version 5.0.8
* bug fixes
* two problems in fc (introduced in 5.0.7)
* install target in Makefile missing a dollar
Version 5.0.7
* POSIX command command added
* a few bug fixes
* now compiles with various options undefined (eg, VI, EMACS, JOBS).
* fixed typos in Makefile.in (maxext -> manext) and ksh.1 (\f -> \fP).
* CLK_TCK defined to correct value for FreeBSD 1.1.5 (and earlier?).
* original process group restored when an exec is done.
* the exit value of set is that of the last $(...) on the command line.
* ditto for a command with no command (eg, x=`false`).
* command variable assignments done before path search (so PATH=... x works)
and are added as they are processed (so A=1 B=$A works).
* variable assignments infront of function calls are exported to programs
inside the function.
* aliases with trailing space are only honoured in command contexts
if in posix mode.
* make depend target added; install target warns if ksh not in /etc/shells.
* set -o bgnice now does something.
* vi mode: ESC is no longer a file completion command (too annoying).
Version 5.0.6
* most reported bugs/problems fixed (all but two).
* temporary files now created in $TMPDIR (if it is a sane path).
Version 5.0.5
* function parsing POSIXized (function bodies can be any compound command,
redirections after functions effect function invocation, not the
instantiation, the () in a function definition now parsed as two tokens).
* exit bultin now does stopped jobs check.
* set -p/-o priviliged supported.
* test builtin now believed to be completely posix.
* a default path is now used when PATH is not set (defined in options.h).
Version 5.0.4
* configuration checks for buggy opendir()s and setpgrp()s.
* autoloading functions now supported.
* functions can safely redefine themselves.
Version 5.0.3
* hash command changed to "alias -t"; whence -p added; print -s added
(all as in at&t ksh); unalias -a added (POSIX).
* test builtin POSIX complient
* TMOUT parameter supported (at&t ksh: timeout interactive shells)
Version 5.0.2
* trap/error handling changed to eliminate longjmp()s from signal handlers;
trap ERR added.
* ksh conditional expressions ([[ .. ]]) supported.
* arithmetic expressions (let, $((..)), etc.) now understand full C
integer expressions (except ++/-- and sizeof()).
* typeset -L -R -Z -u -l added (as in at&t ksh)
* at&t/posix $(( .. )) arithmetic expansions supported.
Version 5.0.1
* set -e no longer effects commands executed as part of if/while/until/&&/||/!
condition.
* posix ! keyword now recognized.
* posix getopts; if not in posix mode, getopts will accept options starting
with + (at&t kshism)
* syntax error messages improved (says what was unexpected/unmatched)
Version 4.9+mun.5
* all known bugs related to job control fixed:
* fg,bg,jobs,wait,kill commands fully POSIX complient
* signals are no longer reported for foreground jobs killed by SIGINT and
SIGPIPE
* pipeline process groups now created more reliablely (was a problem
if first process exited before second process exec'd).
* "(: ; cat /etc/termcap) | sleep" nolonger hangs
* save/restore tty mode if command succeeds/fails, respectively. Edit
mode (emacs,vi) no longer use old tty mode information
* test command: added -h
* alternations option renamed to braceexpand (eg, use set -o braceexpand).
Old usage (set -o alternations) still accepted (will disappear in next
version).
* trap/kill now accept upper and lower case signal names.
Version 4.9+mun.3
* here documents in functions now work properly
* read command: added -s option, use REPLY if no variable specified
* don't accept "while command; done" as a valid command
* fg,bg,jobs,wait,kill commands mostly POSIX complient.
* unset command: added POSIX -v option
* set command: added -A option
* handle ${array[@]} and ${array[*]}
* compiles with old bsd 4.2 compiler (pcc)
* new versions of etc/profile and etc/ksh.profile
Version 4.9+mun.2 (versus 4.9)
* directory/file structure has been re-arranged:
* moved files from sh directory up a level, deleted sh directory
* created misc directory, old ChangeLog,README,.. files moved to misc
* now uses GNU autoconf for compilation.
* no longer uses stdio FILE *'s for I/O redirection (most stdio
usage has been removed). Solves many porting problems caused by
dup'd file descriptors, forked processes and exiting.
* removed lint from code (compiles with very few warning with gcc -O -Wall
-Wno-comment)
* has array support (needs work but is pretty functional).
* ulimit command now more functional on more machines. Compatible with at&t ksh.
* command line and set option parsing cleaned up, POSIXized.
* POSIX IFS handling.
* many many small bug fixes (see ChangeLog)

545
NOTES Normal file
View File

@ -0,0 +1,545 @@
$OpenBSD: NOTES,v 1.8 2003/02/26 03:53:35 david Exp $
General features of at&t ksh88 that are not (yet) in pdksh:
- exported aliases and functions (not in ksh93).
- set -t.
- signals/traps not cleared during functions.
- trap DEBUG, local ERR and EXIT traps in functions.
- ERRNO parameter.
- doesn't have posix file globbing (eg, [[:alpha:]], etc.).
- use of an `agent' to execute unreadable/setuid/setgid shell scripts
(don't ask).
- read/select aren't hooked in to the command line editor
- the last command of a pipeline is not run in the parent shell
Known bugs (see also BUG-REPORTS and PROJECTS files):
Variable parsing, Expansion:
- some specials behave differently when unset (eg, IFS behaves like
" \t\n") others lose their special meaning. IFS/PATH taken care of,
still need to sort out some others (eg, TMOUT).
Parsing,Lexing:
- line numbers in errors are wrong for nested constructs. Need to
keep track of the line a command started on (can use for LINENO
parameter as well).
- a $(..) expression nested inside double quotes inside another $(..)
isn't parsed correctly (eg, $(echo "foo$(echo ")")") )
Commands,Execution:
- setting special parameters that have side effects when
changed/restored (ie, HISTFILE, OPTIND, RANDOM) in front
of a command (eg, HISTFILE=/foo/bar echo hi) effects the parent
shell. Note that setting other (not so special) parameters
does not effect the parent shell.
- `echo hi | exec cat -n' causes at&t to exit, `exec echo hi | cat -n'
does not. pdksh exits for neither. Don't think POSIX requires
an exit, but not sure.
- `echo foo | read bar; echo $bar' prints foo in at&t ksh, nothing
in pdksh (ie, the read is done in a separate process in pdksh).
Misc:
Known problems not caused by ksh:
- after stoping a job, emacs/vi is not re-entered. Hitting return
prints the prompt and everything is fine again. Problem (often
involving a pager like less) is related to order of process
scheduling (shell runs before `stop'ed (sub) processes have had a chance
to clean up the screen/terminal).
Known differences between pdksh & at&t ksh (that may change)
- vi:
- `^U': at&t: kills only what has been inserted, pdksh: kills to
start of line
- at&t ksh login shells say "Warning: you have running jobs" if you
try to exit when there are running jobs. An immediate second attempt
to exit will kill the jobs and exit. pdksh does not print a warning,
nor does it kill running jobs when it exits (it does warn/kill for
stopped jobs).
- TMOUT: at&t prints warning, then waits another 60 seconds. If on screwed
up serial line, the output could cause more input, so pdksh just
prints a message and exits. (Also, in at&t ksh, setting TMOUT has no
effect after the sequence "TMOUT=60; unset TMOUT", which could be
useful - pdksh may do this in the future).
- in pdksh, if the last command of a pipeline is a shell builtin, it is
not executed in the parent shell, so "echo a b | read foo bar" does not
set foo and bar in the parent shell (at&t ksh will).
This may get fixed in the future, but it may take a while.
- in pdksh, set +o lists the options that are currently set, in at&t ksh
it is the same as set -o.
- in pdksh emacs mode, ^T does what gnu emacs does, not what at&t ksh
does.
- in ksh93, `. name' calls a function (defined with function) with POSIX
semantics (instead of ksh semantics). in pdksh, . does not call
functions.
- test: "test -f foo bar blah" is the same as "test -f foo" (the extra
arguments, of which there must be at least 2, are ignored) - pdksh
generates an error message (unexpected operator/operand "bar") as it
should. Sometimes used to test file globs (e.g., if test -f *.o; ...).
- if the command 'sleep 5 && /bin/echo blah' is run interactively and
is the sleep is stopped (^Z), the echo is run immediately in pdksh.
In at&t ksh, the whole thing is stopped.
- LINENO:
- in ksh88 variable is always 1 (can't be changed) in interac mode;
in pdksh it changes.
- Value of LINENO after it has been set by the script in one file
is bizarre when used in another file.
Known differences between pdksh & at&t ksh (that are not likely to change)
- at&t ksh seems to catch or ignore SIGALRM - pdksh dies upon receipt
(unless it's traped of course)
- typeset:
- at&t ksh overloads -u/-l options: for integers, means unsigned/long,
for strings means uppercase/lowercase; pdksh just has the
upper/lower case (which can be useful for integers when base > 10).
unsigned/long really should have their own options.
- at&t ksh can't have justified integer variables
(eg, typeset -iR5 j=10), pdksh can.
- in pdksh, number arguments for -L/-R/-Z/-i must follow the option
character, at&t allows it at the end of the option group (eg,
at&t ksh likes "typeset -iu5 j", pdksh wants "typeset -i5 -u j"
or "typeset -ui5 j"). Also, pdksh allows "typeset -i 5 j" (same
as "typeset -i5 j"), at&t ksh does not allow this.
- typeset -R: pdksh strips trailing space type characters (ie,
uses isspace()), at&t ksh only skips blanks.
- at&t ksh allows attributes of read-only variables to be changed,
pdksh allows only the export attribute to be set.
- (some) at&t ksh allows set -A of readonly variables, pdksh does not.
- at&t ksh allows command assignments of readonly variables (eg, YY=2 cat),
pdksh does not.
- at&t ksh does not exit scripts when an implicit assignment to an integer
variable fails due to an expression error: eg,
echo 2+ > /tmp/x
unset x; typeset -i x
read x < /tmp/x
echo still here
prints an error and then prints "still here", similarly for
unset x; typeset -i x
set +A x 1 2+ 3
echo still here
and
unset x y; typeset -i x y; set +A y 10 20 30
set +A x 1 1+y[2+] 3
echo still here
pdksh exits a script in all the above cases. (note that both shells
exit for:
unset x; typeset -i x
for x in 1 2+ 3; do echo x=$x; done
echo still here
).
- at&t ksh seems to allow function calls inside expressions
(eg, typeset -i x='y(2)') but they do not seem to be regular functions
nor math functions (eg, pow, exp) - anyone known anything about this?
- `set -o nounset; unset foo; echo ${#foo}`: at&t ksh prints 0; pdksh
generates error. Same for ${#foo[*]} and ${#foo[@]}.
- . file: at&t ksh parses the whole file before executing anything,
pdksh executes as it parses. This means aliases defined in the file
will affect how pdksh parses the file, but won't affect how at&t ksh
parses the file. Also means pdksh will not parse statements occuring
after a (executed) return statement.
- a return in $ENV in at&t ksh will cause the shell to exit, while in
pdksh it will stop executing the script (this is consistent with
what a return in .profile does in both shells).
- at&t ksh does file globbing for `echo "${foo:-"*"}"`, pdksh does not
(POSIX would seem to indicate pdksh is right).
- at&t ksh thinks ${a:##foo} is ok, pdksh doesn't.
- at&t does tilde expansion on here-document delimiters, pdksh does
not. eg.
$ cat << ~michael
~michael
$
works for pdksh, not for at&t ksh (POSIX seems to agree with pdksh).
- in at&t ksh, tracked aliases have the export flag implicitly set
and tracked aliases and normal aliases live in the same name space
(eg, "alias" will list both tracked and normal aliases).
in pdksh, -t does not imply -x (since -x doesn't do anything yet), and
tracked/normal aliases live in separate name spaces.
in at&t ksh, alias accepts + options (eg, +x, +t) - pdksh does not.
in pdksh, alias has a -d option to allow examination/changing of
cached ~ entries, also unalias has -d and -t options (unalias -d
is useful if the ~ cache gets out of date - not sure how at&t deals
with this problem (it does cache ~ entries)).
- at&t ksh will stop a recursive function after about 60 calls; pdksh
will not since the limit is arbitrary and can't be controlled
by the user (hit ^C if you get in trouble).
- the wait command (with and without arguments) in at&t ksh will wait for
stopped jobs when job control is enabled. pdksh doesn't.
- at&t ksh automatically sets the bgnice option for interactive shells;
pdksh does not.
- in at&t ksh, "eval `false`; echo $?" prints 1, pdksh prints 0 (which
is what POSIX says it should). Same goes for "wait `false`; echo $?".
(same goes for "set `false`; echo $?" if posix option is set - some
scripts that use the old getopt depend on this, so be careful about
setting the posix option).
- in at&t ksh, print -uX and read -uX are interrperted as -u with no
argument (defaults to 1 and 0 respectively) and -X (which may or
may not be a valid flag). In pdksh, -uX is interpreted as file
descriptor X.
- in at&t ksh, some signals (HUP, INT, QUIT) cause the read to exit, others
(ie, everything else) do not. When it does cause exiting, anything read
to that point is used (usually an empty line) and read returns with 0
status. pdksh currently does similar things, but for TERM as well and
the exit status is 128+<signal-number> - in future, pdksh's read will
do this for all signals that are normally fatal as required by POSIX.
(POSIX does not require the setting of variables to null so applications
shouldn't rely on this).
- in pdksh, ! substitution done before variable substitution; in at&t ksh
it is done after substitution (and therefor may do ! substitutions on
the result of variable substitutions). POSIX doesn't say which is to be
done.
- pwd: in at&t ksh, it ignores arguments; in pdksh, it complains when given
arguments.
- the at&t ksh does not do command substition on PS1, pdksh does.
- ksh93 allows ". foo" to run the function foo if there is no file
called foo (go figure).
- field splitting (IFS): ksh88/ksh93 strip leading non-white space IFS
chars, pdksh (and POSIX, I think) leave them intact. e.g.
$ IFS="$IFS:"; read x; echo "<$x>"
::
prints "<>" in at&t ksh, "<::>" in pdksh.
- command completion: at&t ksh will do completion on a blank line (matching
all commands), pdksh does not (as this isn't very useful - use * if
you really want the list).
- co-processes: if ksh93, the write portion of the co-process output is
closed when the most recently started co-process exits. pdksh closes
it when all the co-processes using it have exited.
- pdksh accepts empty command lists for while and for statements, while
at&t ksh (and sh) don't. Eg., pdksh likes
while false ; do done
but ksh88 doesn't like it.
- pdksh bumps RANDOM in parent after a fork, at&t ksh bumps it in both
parent and child:
RANDOM=1
echo child: `echo $RANDOM`
echo parent: $RANDOM
will produce "child: 16838 parent: 5758" in pdksh, while at&t ksh
will produce "child: 5758 parent: 5758".
Oddities in ksh (pd & at&t):
- array references inside (())/$(()) are strange:
$(( x[2] )) does the expected, $(( $x[2] )) doesn't.
- `typeset -R3 X='x '; echo "($X)"` produces ( x) - trailing
spaces are stripped.
- typeset -R turns off Z flag.
- both shells have the following mis-feature:
$ x='function xx {
cat -n <<- EOF
here we are in xx
EOF
}'
$ (eval "$x"; (sleep 2; xx) & echo bye)
[1] 1234
bye
$ xx: /tmp/sh1234.1: cannot open
- bizarre special handling of alias/export/readonly/typeset arguments
$ touch a=a; typeset a=[ab]; echo "$a"
a=[ab]
$ x=typeset; $x a=[ab]; echo "$a"
a=a
$
- both ignore SIGTSTP,SIGTTIN,SIGTTOU in exec'd processes when talking
and not monitoring (at&t ksh kind of does this). Doesn't really make
sense.
(Note that ksh.att -ic 'set +m; check-sigs' shows TSTP et al aren't
ignored, while ksh.att -ic 'set +m^J check-sigs' does... very strange)
- when tracing (set -x), and a command's stderr is redirected, the trace
output is also redirected. so "set -x; echo foo 2> /tmp/O > /dev/null"
will create /tmp/foo with the lines "+ > /dev/null" and "+ echo foo".
- undocumented at&t ksh feature: FPATH is searched after PATH if no
executable is found, even if typeset -uf wasn't used.
at&t ksh bugs:
[various versions:
MIPS m120 RISC/os 5.0: Version 11/16/88d
Dec alpha osf/1 v1.3: OSF/1 Version 11/16/88d NLS
HP pa HP-UX 9.01: Version 11/16/88
]
- (only hpux)
$ _[2]=hi
Bus error (core dumped)
- (only riscos, hpux)
$ typeset x[
$
- (only osf/1)
$ A=B cat << EOF
.$A.
EOF
Segmentation fault(coredump)
$
- (only osf/1)
$ read "?foo "
foo Foo
$ set | grep Foo
=Foo
$
- (all)
$ typeset -i A
$ typeset -L3 A
$ typeset -l A
Illegal instruction (core dumped)
- (all)
$ for i in a b c ; do echo $i, ${i[2]}, ${i[10]} ; done
a, ,
a, , b
a, , c
$
- (all)
$ echo ${abc:-G { I } K }
G { I K }
$
$ abc=hi
$ echo ${abc:-G { I } K }
hi K }
$
The second echo should only have printed `hi'.
- (all)
$ echo ${abc:- > foo}
syntax error: > unexpected
$
- (all? hpux) read reads too much from pipe (when pipe isn't stdin)
print 'hi\nthere' | ksh 8<&0 0< /dev/tty
$ read -u8 x
$ print $x
hi
$ cat 0<&8
$ read -u8 y
$ print $y
there
$
- (all)
$ umask 0
$ umask
00
$
- (osf, mips, !hpux)
$ exec alias
alias: not found
(shell dead)
- (all) non-white space IFS in non-substitution not preserved
$ IFS="$IFS:"
$ echo : "$@" # this is ok
:
$ echo :"$@" # this should print : too (me thinks)
$
- (only osf/1)
$ set +m
$ sleep 1 & # wait for a sec or two
$ jobs
Memory fault (core dumped)
- (all)
$ (sleep 1 & echo hi) &
[1] 123
$ [1] 234
hi
- (osf/1, mips)
$ getopts abc optc -a -b -c
$ getopts abc optc -a -b -c
$ getopts abc optc -a
Memory fault (core dumped)
- (osf/1) POSIX says OPTIND shall be initialized to 1
$ echo $OPTIND
0
$
- (osf/1 + others?)
$ typeset -ri r=10
$ let r=12
$ echo $r
12
$
- (osf/1 + others?)
$ typeset -i a
$ typeset -L3 a
Memory fault (core dumped)
- (osf/1 + others?): -L strips leading \ \t\n\r, -R only strips trailing
spaces
$ typeset -L3 x
$ x=' ^I^J^M 2'
$ echo "($x)"
(2 )
$ typeset -R3 y
$ x='2^I^J^M '
$ echo "($x)"
(^I^J^M)
$
- (osf/1 + others?)
$ typeset +i RANDOM
Memory fault (core dumped)
- (osf/1 + others?): -L/-R/-Z clear -l/-u after assignment and vise versa
$ typeset -u x=ab
$ echo "($x)"
(AB)
$ typeset -L4 x=def
$ echo "($x)"
(DEF )
$ typeset | grep ' x$'
leftjust 4 x
$
$ typeset -L4 x=def
$ echo "($x)"
(def )
$ typeset -u x=ab
$ echo "($x)"
(AB )
$ typeset | grep ' x$'
uppercase x
$
$ typeset -i x
$ x='2()'
$ x='()'
$ x='2(4)'
- (osf/1, others?)
$ unset foo
$ echo "${foo:-"*"}"
<results of * expansion>
$
- (osf/1, others?)
$ alias blah
blah: alias not found
$ alias -x blah | grep blah
blah
$ type blah
Memory fault (core dumped)
- (osf/1, others?)
$ trap 'echo hi; false' ERR
$ false
hi
hi
....
Memory fault (core dumped)
- (osf/1, others?)
$ typeset +i ERRNO
Memory fault (core dumped)
- (osf/1, others?)
$ X=abcdef
$ echo ${X#a{b,c}e} # does not match {} inside word part of ${..#..}
abcdefe}
$
- (osf/1, others?)
$ x=f=abcdef
$ echo ${f#a|abc}
def
$ echo ${f#abc|a}
bcdef
$ echo ${f#abc|a|d}
abcdef
$
- (osf/1, hp-ux, others?)
$ i() echo hi
$ typeset -f
function i
{
hi
$
- (osf/1, others?)
$ function X {
echo start of X
function Y {
echo in Y
}
echo end of X
}
$ X
start of X
end of X
$ typeset -f
function X
{
echo start of X
function Y {
echo in Y
}
echo end of X
}
function Y
{
echo in Y
echo end of X
}
}
$
- (osf/1, others?)
$ while read x; do print -r "A $x"; done |&
[1] 18212
$ exec 8<&p
$ kill %1
Memory fault
- (osf/1, others?) Error only happens for builtin commands (/bin/echo works)
$ while read x; do print -r "A $x"; done |&
[1] 18212
$ echo hi <&p
hi
$ echo hi <&p
ksh: p: bad file unit number
$ while read x; do print -r "A $x"; done |&
ksh: process already exists
$
- (osf/1, others?) in restricted shells, command -p should not work.
$ PATH=/tmp ksh -r
$ print hi | command -p cat -n
1 hi
$
- (osf/1, others?) error message wrong for autoload files that don't define
functions
$ FPATH=/tmp
$ echo echo hi there > /tmp/aja
$ aja
hi there
ksh: echo: not found
$
- (SunOS M-12/28/93d):
$ cat -n << X $(
> echo foo
> )
> X
> echo bar
)
./ksh93: X: cannot open [No such file or directory]
Memory fault (core dumped)
POSIX sh questions (references are to POSIX 1003.2-1992)
- arithmetic expressions: how are empty expressions treated?
(eg, echo $(( ))). at&t ksh (and now pdksh) echo 0.
Same question goes for `test "" -eq 0' - does this generate an error
or, if not, what is the exit code?
- should tilde expansion occur after :'s in the word part of ${..=..}?
(me thinks it should)
- if a signal is received during the execution of a built-in,
does the builtin command exit or the whole shell?
- is it legal to execute last command of pipeline in current
execution environment (eg, can "echo foo | read bar" set
bar?)
- what action should be taken if there is an error doing a dup due
to system limits (eg, not enough feil destriptors): is this
a "redirection error" (in which case a script will exit iff the
error occured while executing a special built-in)?
IMHO, shell should exit script. Couldn't find a blanket statement
like "if shell encounters an unexpected system error, it shall
exit non-interactive scripts"...
POSIX sh bugs (references are to POSIX 1003.2-1992)
- in vi insert mode, ^W deletes to beginning of line or to the first
blank/punct character (para at line 9124, section 3). This means
"foo ^W" will do nothing. This is inconsistent with the vi
spec, which says delete preceding word including and interceding
blanks (para at line 5189, section 5).
- parameter expansion, section 3.6.2, line 391: `in each case that a
value of word is needed (..), word shall be subjected to tilde
expansion, parameter expansion, ...'. Various expansions should not
be performed if parameter is in double quotes.
- the getopts description says assigning OPTIND a value other than 1
produces undefined results, while the rationale for getopts suggests
saving/restoring the OPTIND value inside functions (since POSIX
functions don't do the save/restore automatically). Restoring
OPTIND is kind of dumb since getopts may have been in the middle
of parsing a group of flags (eg, -abc).
- unclear whether arithmetic expressions (eg, $((..))) should
understand C integer constants (ie, 0x123, 0177). at&t ksh doesn't
and neither does pdksh.
- `...` definition (3.6.3) says nothing about backslash followed by
a newline, which sh and at&t ksh strip out completely. e.g.,
$ show-args `echo 'X
Y'`
Number of args: 1
1: <XY>
$
POSIX would indicate the backslash-newline would be preserved.
- does not say how "cat << ''" is to be treated (illegal, read 'til
blank line, or read 'til eof). at&t ksh reads til eof, bourne shell
reads 'til blank line. pdksh reads 'til blank line.

111
PROJECTS Normal file
View File

@ -0,0 +1,111 @@
$OpenBSD: PROJECTS,v 1.5 1999/07/14 13:37:23 millert Exp $
Things to be done in pdksh (see also the NOTES file):
* builtin utilities:
pdksh has most if not all POSIX/at&t ksh builtins, but they need to
be checked that they conform to POSIX/at&t manual. Part of the
process is changing the builtins to use the ksh_getopt() routine.
The following builtins, which are defined by POSIX, haven't been
examined:
eval
The first pass has been done on the following commands:
. : alias bg break cd continue echo exec exit export false fc fg
getopts jobs kill pwd read readonly return set shift time trap true
umask unalias unset wait
The second pass (ie, believed to be completely POSIX) has been done on
the following commands:
test
(ulimit also needs to be examined to check that it fits the posix style)
* test suite
Ideally, as the builtin utilities are being POSIXized, short tests
should be written to be used in regression testing. The tests
directory contains some tests, but many more need to be written.
* internationalization
Need to handle with the LANG and LC_* environment variables. This
involves changes to ensure <ctype.h> macros are being used (currently
uses its own macros in many places), figuring out how to deal with
bases (for integer arithmetic, eg, 12#1A), and (the nasty one) doing
string look ups for error messages, etc.. It probably isn't worth
translating strings to other languages yet as the code is likely
to change a lot in the near future, but it would be good to have the
code set up so string tables can be used.
* trap code
* add the DEBUG trap.
* fix up signal handling code. In particular, fatal vs tty signals,
have signal routine to call to check for pending/fatal traps, etc.
* parsing
* the time keyword needs to be hacked to accept options (!) since
POSIX says it shall accept the -p option and must skip a -- argument
(end of options). Yuck.
* lexing
the lexing may need a re-write since it currently doesn't parse $( .. ),
$(( .. )), (( ... )) properly.
* need to ignore contents of quoted strings (and escaped chars?)
inside $( .. ) and $(( .. )) when counting parentheses.
* need to put bounds check on states[] array (if it still exists after
the re-write)
* variables
* The "struct tbl" that is currently used for variables needs work since
more information (eg, array stuff, fields) are needed for variables
but not for the other things that use "struct tbl".
* Arrays need to be implemented differently: currently does a linear
search of a linked list to find element i; the linked list is not
freed when a variable is unset.
* functions
finish the differences between function x and x(): trap EXIT, traps
in general, treatment of OPTIND/OPTARG,
* history
There are two versions of the history code, COMPLEX_HISTORY and
EASY_HISTORY, which need to be merged. COMPLEX does at&t style history
where the history file is written after each command and checked when
ever looking through the history (in case another shell has added
something). EASY simply reads the history file at startup and writes
it before exiting.
* re-write the COMPLEX_HISTORY code so mmap() not needed (currently
can't be used on machines without mmap()).
* Add multiline knowledge to COMPLEX_HISTORY (see EASY_HISTORY
stuff).
* change COMPLEX_HISTORY code so concurrent history files are
controlled by an option (set -o history-concurrent?). Delete
the EASY_HISTORY code.
* bring history code up to POSIX standards (see POSIX description
of fc, etc.).
* documentation
Some sort of tutorial with examples would be good. Texinfo is probably
the best medium for this. Also, the man page could be converted to
texinfo (if the tutorial and man page are put in the same texinfo
page, they should be somewhat distinct - i.e., the tutorial should
be a separate thread - but there should be cross references between the
two).
* miscellaneous
* POSIX specifies what happens when various kinds of errors occur
in special built-ins commands vs regular commands (builtin or
otherwise) (see POSIX.2:3.8.1). Some of this has been taken
care of, but more needs doing.
* remove static limits created by fixed sized arrays
(eg, ident[], heres[], PATH, buffer size in emacs/vi code)
* merge the emacs and vi code (should reduce the size of the shell and
make maintenance easier); handle SIGWINCH while editing a line.
[John Rochester is working on the merge]
* add POSIX globbing (eg, [[:alnum:]]), see POSIX.2:2.8.3.2.
* teach shf_vfprintf() about long long's (%lld); also make %p use
long longs if appropriate.

197
README Normal file
View File

@ -0,0 +1,197 @@
$OpenBSD: README,v 1.10 2003/03/10 03:48:16 david Exp $
Last updated Jul '99 for pdksh-5.2.14.
(check ftp://ftp.cs.mun.ca:/pub/pdksh/ or
http://www.cs.mun.ca/~michael/pdksh/ for new versions/patches)
PD-ksh is a mostly complete AT&T ksh look-alike (see NOTES file for a list
of things not supported). Work is mostly finished to make it fully
compatible with both POSIX and AT&T ksh (when the two don't conflict).
Since pdksh is free and compiles and runs on most common unix systems, it
is very useful in creating a consistent user interface across multiple
machines. For example, in the CS dept. of MUN, pdksh is installed on a
variety of machines including Suns, HPs, DecStations, pcs running Linux,
etc., and is the login shell of ~5200 users.
PDksh is currently being maintained by Michael Rendell (michael@cs.mun.ca),
who took over from Simon J. Gerraty (sjg@zen.void.oz.au) at the later's
suggestion. A short list of things that have been added since the last
public pdksh release (4.9) are auto-configuration, arrays, $(( .. )),
[[ .. ]], variable attributes, co-processes, extended file globbing,
many POSIXisms and many bug fixes. See the NEWS and ChangeLog files for
other features added and bugs fixed.
Note that pdksh is provided AS IS, with NO WARRANTY, either expressed or
implied. Also note that although the bulk of the code in pdksh is in the
public domain, some files are copyrighten (but freely distributable) and
subject to certain conditions (eg, don't remove copyright, document any
changes, etc.). See the LEGAL file for details.
If you would like to be notified via email of new releases as they become
available, send mail to pdksh-request@cs.mun.ca with subject
"send release notifications" (or "don't send release notifications" to stop
them).
Files of interest:
NEWS short list of noticeable changes in various versions.
CONTRIBUTORS short history of pdksh, people who contributed, etc.
NOTES lists of known bugs in pdksh, at&t ksh, and posix.
PROJECTS list of things that need to be done in pdksh.
BUG-REPORTS list of recently reported bugs that have been fixed
and all reported bugs that haven't been fixed.
LEGAL A file detailing legal issues concerning pdksh.
etc/* system profile and kshrc files used by Simon J. Gerraty.
misc/README* readme files from previous versions.
misc/Changes* changelog files from previous versions.
os2/* files and info needed to compile ksh on os/2.
tests/* pdksh's regression testing system.
Compiling/Installing:
The quick way:
./configure
make
make check # optional
make install # will install /usr/local/bin/ksh
# and /usr/local/man/man1/ksh.1
[add path-to-installed-pdksh to /etc/shells]
The more detailed description:
* run "configure --help | your-favorite-pager" and look at the
--enable-* and --disable-* options (they are at the end).
Select any you options you wish to enable/disable
(most people can skip this step).
* run configure: this is a GNU autoconf configure script that will generate
a Makefile and a config.h. Some of the useful options to configure are:
--prefix=PATH indicates the directory tree under which the binary
and man page are installed (ie, PATH/bin/ksh and
PATH/man/man1/ksh.1).
The default prefix is /usr/local.
--exec-prefix=PATH overrides --prefix for machine dependent files
(ie, the ksh binary)
--program-prefix=pd install binary and man page as pdksh and pdksh.1
--verbose show what is being defined as script runs
Note that you don't have to build in the source directory. To build
in a separate directory, do something like:
$ mkdir objs
$ cd objs
$ ../configure --verbose
....
$ make
See the file INSTALL for a more complete description of configure and its
generic options (ksh specific options are documented in the --help output)
* miscellaneous configuration notes:
* If your make doesn't understand VPATH, you must compile in
the source directory.
* On DecStations, MIPS and SONY machines with older C compilers that
can't handle "int * volatile x", you should use gcc or turn off
optimization. The problem is configure defines volatile to nothing
since the compiler can't handle it properly, but the compiler does
optimizations that the volatile is meant to prevent. So. Use gcc.
* On MIPS RISC/os 5.0 systems, sysv environment, <signal.h> is
messed up - it defines sigset_t, but not any of the rest of
the posix signals (the sigset_t typedef should be in the
ifdef KERNEL section) - also doesn't have waitpid() or wait3().
Things compile up ok in the svr4 environment, but it dumps core
in __start (perhaps our system doesn't have the full svr4
environ?). Try compiling in the bsd43 environ instead (still not
perfect - see BUG-REPORTS file), using gcc - cc has problems with
macro expansions in the argument of a macro (in this case, the ARGS
macro).
* On TitanOS (Stardent/Titan), use `CC="cc -43" configure ...'.
When configure finishes, edit config.h, undef HAVE_DIRENT_H and
define HAVE_SYS_DIR_H (the dirent.h header file is broken).
* On Linux (red hat distribution), check that /dev/tty has mode 0666
(not mode 0644). If it has the wrong permissions, ksh will print
warnings about not being able to do job control.
* on NeXT machines (3.2, probably other releases), the siglist.out file
won't be generated correctly if you try to use the system's compiler
(it has a broken cc -E and strange header files). There are two
ways to make it work:
1) if you have gcc, use it (for everything). Alternatively,
force configure to use it for CPP, i.e., use
CPP="gcc -E" configure ...
2) Force configure to use some extra CPPFLAGS, using
CPPFLAGS="XXX" configure ...
where XXX is obtained from running "cc -v YYY.c" on some
C file. Look at the options passed to cpp (there are lots
of them...) and replace the XXX above with them.
Make sure you do a "make distclean" (or "rm config.cache") if
you re-run configure with a difference CPP or CPPFLAGS.
Also note that if you are building multiple arch binaries, you
will have to specify both CC and CPP.
* run make: everything should compile and link without problems.
* run make check: this fires up a perl script that checks for some known
and some fixed bugs. The script prints pass/fail for tests it expected
to pass/fail, and PASS/FAIL for tests it expected to fail/pass. If you
don't have perl, or if your perl doesn't work (most common problem is
the .ph header files are missing or broken), you can run
ENV= path-to-pdksh-executable misc/Bugs path-to-pdksh-executable
instead.
* run make install: this installs ksh (in /usr/local/bin/ksh by default,
or where ever you told configure to put things).
* add path-to-installed-pdksh to /etc/shells if it's not already there.
This is only needed if you intend to use pdksh as a login shell (things
like ftp won't allow users to connect in if their shell isn't in this
file).
The following is a list of machines that pdksh is reported to work on:
-/PC Linux 1.x,2.x
-/PC NetBSD 0.9a
-/PC BSDI 1.1
-/PC FreeBSD 2.x, 3.x
-/PC OpenBSD
-/PC Interactive/Sunsoft 3.0.1 and 4.1 (note that problems have been
reported with isc3.2 - see the BUG-REPORTS file)
-/PC OS/2
Commodore/Amiga NetBSD 1.0
Dec/alpha OSF/1 v2.x, v3.x
Dec/alpha NetBSD 1.1B
Dec/pmax Ultrix 4.2
Dec/vax Ultrix 2.2 (not tested recently :-))
Dec/vax 4.3BSD+NFS (MtXinu) (not tested recently :-))
HP/pa HP-UX 9.01
IBM/RS/6000 AIX 3.2.5
MIPS/m120 RISC/os 5.0 (bsd43 environ)
NeXT NeXTStep 3.2
SGI/IRIX 6.2
Sun/sun4 SunOS 4.1.3, 4.1.4
Sun/sun4 Solaris 2.x
Sun/sun386i SunOS 4.0.2
Sun/sun3 SunOS 4.0.3, 4.1.1_U1
Stardent/TitanOS 4.2
Newer versions of pdksh may be available from
ftp://ftp.cs.mun.ca:/pub/pdksh/
you may want to check for one if you run into any problems, as the problem may
already be fixed (you can get new release notifications automatically - see
above). The file pdksh-unstable-XXX.tar.gz has the very latest version which
may not compile (it is generated automatically when changes are detected
in the main source repository) - it is for those who want to follow
changes as they are made.
You can send bug reports, fixes, and enhancements to pdksh@cs.mun.ca (please
don't assume I will see bug reports that are posted to some newsgroup or
mailing list - I probably won't).
If you are reporting a bug (with or without a fix), please include
* the version of pdksh you are using (see version.c, or, if you are
running pdksh, try echo $KSH_VERSION),
* the machine, operating system and compiler you are using,
* and a description of how to repeat the bug (a small shell
script that demonstrates the bug is best).
as well as the following, if relevant (if you aren't sure, include them)
* what options you are using (both configure options and set -o options)
* the output of configure, with the verbose flag
(eg, make distclean; ./configure --verbose)
* the contents of config.log (this is created by the configure script)
* if you are using gcc (the GNU C compiler), which version it is.
BTW, THE MOST FREQUENTLY REPORTED BUG IS
echo hi | read a; echo $a # Does not print hi
I'm aware of this and there is no need to report it.
Michael Rendell, michael@cs.mun.ca

119
alloc.c Normal file
View File

@ -0,0 +1,119 @@
/* $OpenBSD: alloc.c,v 1.5 2002/03/01 13:06:18 espie Exp $ */
/*
* Copyright (c) 2002 Marc Espie.
*
* 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 OPENBSD PROJECT 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 OPENBSD
* PROJECT 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.
*/
/*
* area-based allocation built on malloc/free
*/
#include "sh.h"
struct link {
struct link *prev;
struct link *next;
};
Area *
ainit(Area *ap)
{
ap->freelist = NULL;
return ap;
}
void
afreeall(Area *ap)
{
struct link *l, *l2;
for (l = ap->freelist; l != NULL; l = l2) {
l2 = l->next;
free(l);
}
ap->freelist = NULL;
}
#define L2P(l) ( (void *)(((char *)(l)) + sizeof(struct link)) )
#define P2L(p) ( (struct link *)(((char *)(p)) - sizeof(struct link)) )
void *
alloc(size_t size, Area *ap)
{
struct link *l;
l = malloc(size + sizeof(struct link));
if (!l)
return NULL;
l->next = ap->freelist;
l->prev = NULL;
if (ap->freelist)
ap->freelist->prev = l;
ap->freelist = l;
return L2P(l);
}
void *
aresize(void *ptr, size_t size, Area *ap)
{
struct link *l, *l2, *lprev, *lnext;
if (ptr == NULL)
return alloc(size, ap);
l = P2L(ptr);
lprev = l->prev;
lnext = l->next;
l2 = realloc(l, size+sizeof(struct link));
if (l2) {
if (lprev)
lprev->next = l2;
else
ap->freelist = l2;
if (lnext)
lnext->prev = l2;
}
return L2P(l2);
}
void
afree(void *ptr, Area *ap)
{
struct link *l;
if (!ptr)
return;
l = P2L(ptr);
if (l->prev)
l->prev->next = l->next;
else
ap->freelist = l->next;
if (l->next)
l->next->prev = l->prev;
free(l);
}

1475
c_ksh.c Normal file

File diff suppressed because it is too large Load Diff

906
c_sh.c Normal file
View File

@ -0,0 +1,906 @@
/* $OpenBSD: c_sh.c,v 1.17 2003/03/13 09:03:07 deraadt Exp $ */
/*
* built-in Bourne commands
*/
#include "sh.h"
#include "ksh_stat.h" /* umask() */
#include "ksh_time.h"
#include "ksh_times.h"
static char *clocktos ARGS((clock_t t));
/* :, false and true */
int
c_label(wp)
char **wp;
{
return wp[0][0] == 'f' ? 1 : 0;
}
int
c_shift(wp)
char **wp;
{
register struct block *l = e->loc;
register int n;
long val;
char *arg;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
arg = wp[builtin_opt.optind];
if (arg) {
evaluate(arg, &val, KSH_UNWIND_ERROR);
n = val;
} else
n = 1;
if (n < 0) {
bi_errorf("%s: bad number", arg);
return (1);
}
if (l->argc < n) {
bi_errorf("nothing to shift");
return (1);
}
l->argv[n] = l->argv[0];
l->argv += n;
l->argc -= n;
return 0;
}
int
c_umask(wp)
char **wp;
{
register int i;
register char *cp;
int symbolic = 0;
int old_umask;
int optc;
while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != EOF)
switch (optc) {
case 'S':
symbolic = 1;
break;
case '?':
return 1;
}
cp = wp[builtin_opt.optind];
if (cp == NULL) {
old_umask = umask(0);
umask(old_umask);
if (symbolic) {
char buf[18];
int j;
old_umask = ~old_umask;
cp = buf;
for (i = 0; i < 3; i++) {
*cp++ = "ugo"[i];
*cp++ = '=';
for (j = 0; j < 3; j++)
if (old_umask & (1 << (8 - (3*i + j))))
*cp++ = "rwx"[j];
*cp++ = ',';
}
cp[-1] = '\0';
shprintf("%s\n", buf);
} else
shprintf("%#3.3o\n", old_umask);
} else {
int new_umask;
if (digit(*cp)) {
for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
new_umask = new_umask * 8 + (*cp - '0');
if (*cp) {
bi_errorf("bad number");
return 1;
}
} else {
/* symbolic format */
int positions, new_val;
char op;
old_umask = umask(0);
umask(old_umask); /* in case of error */
old_umask = ~old_umask;
new_umask = old_umask;
positions = 0;
while (*cp) {
while (*cp && strchr("augo", *cp))
switch (*cp++) {
case 'a': positions |= 0111; break;
case 'u': positions |= 0100; break;
case 'g': positions |= 0010; break;
case 'o': positions |= 0001; break;
}
if (!positions)
positions = 0111; /* default is a */
if (!strchr("=+-", op = *cp))
break;
cp++;
new_val = 0;
while (*cp && strchr("rwxugoXs", *cp))
switch (*cp++) {
case 'r': new_val |= 04; break;
case 'w': new_val |= 02; break;
case 'x': new_val |= 01; break;
case 'u': new_val |= old_umask >> 6;
break;
case 'g': new_val |= old_umask >> 3;
break;
case 'o': new_val |= old_umask >> 0;
break;
case 'X': if (old_umask & 0111)
new_val |= 01;
break;
case 's': /* ignored */
break;
}
new_val = (new_val & 07) * positions;
switch (op) {
case '-':
new_umask &= ~new_val;
break;
case '=':
new_umask = new_val
| (new_umask & ~(positions * 07));
break;
case '+':
new_umask |= new_val;
}
if (*cp == ',') {
positions = 0;
cp++;
} else if (!strchr("=+-", *cp))
break;
}
if (*cp) {
bi_errorf("bad mask");
return 1;
}
new_umask = ~new_umask;
}
umask(new_umask);
}
return 0;
}
int
c_dot(wp)
char **wp;
{
char *file, *cp;
char **argv;
int argc;
int i;
int err;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
if ((cp = wp[builtin_opt.optind]) == NULL)
return 0;
file = search(cp, path, R_OK, &err);
if (file == NULL) {
bi_errorf("%s: %s", cp, err ? strerror(err) : "not found");
return 1;
}
/* Set positional parameters? */
if (wp[builtin_opt.optind + 1]) {
argv = wp + builtin_opt.optind;
argv[0] = e->loc->argv[0]; /* preserve $0 */
for (argc = 0; argv[argc + 1]; argc++)
;
} else {
argc = 0;
argv = (char **) 0;
}
i = include(file, argc, argv, 0);
if (i < 0) { /* should not happen */
bi_errorf("%s: %s", cp, strerror(errno));
return 1;
}
return i;
}
int
c_wait(wp)
char **wp;
{
int UNINITIALIZED(rv);
int sig;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
wp += builtin_opt.optind;
if (*wp == (char *) 0) {
while (waitfor((char *) 0, &sig) >= 0)
;
rv = sig;
} else {
for (; *wp; wp++)
rv = waitfor(*wp, &sig);
if (rv < 0)
rv = sig ? sig : 127; /* magic exit code: bad job-id */
}
return rv;
}
int
c_read(wp)
char **wp;
{
register int c = 0;
int expand = 1, history = 0;
int expanding;
int ecode = 0;
register char *cp;
int fd = 0;
struct shf *shf;
int optc;
const char *emsg;
XString cs, xs;
struct tbl *vp;
char UNINITIALIZED(*xp);
while ((optc = ksh_getopt(wp, &builtin_opt, "prsu,")) != EOF)
switch (optc) {
#ifdef KSH
case 'p':
if ((fd = coproc_getfd(R_OK, &emsg)) < 0) {
bi_errorf("-p: %s", emsg);
return 1;
}
break;
#endif /* KSH */
case 'r':
expand = 0;
break;
case 's':
history = 1;
break;
case 'u':
if (!*(cp = builtin_opt.optarg))
fd = 0;
else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) {
bi_errorf("-u: %s: %s", cp, emsg);
return 1;
}
break;
case '?':
return 1;
}
wp += builtin_opt.optind;
if (*wp == NULL)
*--wp = "REPLY";
/* Since we can't necessarily seek backwards on non-regular files,
* don't buffer them so we can't read too much.
*/
shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare);
if ((cp = strchr(*wp, '?')) != NULL) {
*cp = 0;
if (isatty(fd)) {
/* at&t ksh says it prints prompt on fd if it's open
* for writing and is a tty, but it doesn't do it
* (it also doesn't check the interactive flag,
* as is indicated in the Kornshell book).
*/
shellf("%s", cp+1);
}
}
#ifdef KSH
/* If we are reading from the co-process for the first time,
* make sure the other side of the pipe is closed first. This allows
* the detection of eof.
*
* This is not compatible with at&t ksh... the fd is kept so another
* coproc can be started with same output, however, this means eof
* can't be detected... This is why it is closed here.
* If this call is removed, remove the eof check below, too.
* coproc_readw_close(fd);
*/
#endif /* KSH */
if (history)
Xinit(xs, xp, 128, ATEMP);
expanding = 0;
Xinit(cs, cp, 128, ATEMP);
for (; *wp != NULL; wp++) {
for (cp = Xstring(cs, cp); ; ) {
if (c == '\n' || c == EOF)
break;
while (1) {
c = shf_getc(shf);
if (c == '\0'
#ifdef OS2
|| c == '\r'
#endif /* OS2 */
)
continue;
if (c == EOF && shf_error(shf)
&& shf_errno(shf) == EINTR)
{
/* Was the offending signal one that
* would normally kill a process?
* If so, pretend the read was killed.
*/
ecode = fatal_trap_check();
/* non fatal (eg, CHLD), carry on */
if (!ecode) {
shf_clearerr(shf);
continue;
}
}
break;
}
if (history) {
Xcheck(xs, xp);
Xput(xs, xp, c);
}
Xcheck(cs, cp);
if (expanding) {
expanding = 0;
if (c == '\n') {
c = 0;
if (Flag(FTALKING_I) && isatty(fd)) {
/* set prompt in case this is
* called from .profile or $ENV
*/
set_prompt(PS2, (Source *) 0);
pprompt(prompt, 0);
}
} else if (c != EOF)
Xput(cs, cp, c);
continue;
}
if (expand && c == '\\') {
expanding = 1;
continue;
}
if (c == '\n' || c == EOF)
break;
if (ctype(c, C_IFS)) {
if (Xlength(cs, cp) == 0 && ctype(c, C_IFSWS))
continue;
if (wp[1])
break;
}
Xput(cs, cp, c);
}
/* strip trailing IFS white space from last variable */
if (!wp[1])
while (Xlength(cs, cp) && ctype(cp[-1], C_IFS)
&& ctype(cp[-1], C_IFSWS))
cp--;
Xput(cs, cp, '\0');
vp = global(*wp);
/* Must be done before setting export. */
if (vp->flag & RDONLY) {
shf_flush(shf);
bi_errorf("%s is read only", *wp);
return 1;
}
if (Flag(FEXPORT))
typeset(*wp, EXPORT, 0, 0, 0);
if (!setstr(vp, Xstring(cs, cp), KSH_RETURN_ERROR)) {
shf_flush(shf);
return 1;
}
}
shf_flush(shf);
if (history) {
Xput(xs, xp, '\0');
source->line++;
histsave(source->line, Xstring(xs, xp), 1);
Xfree(xs, xp);
}
#ifdef KSH
/* if this is the co-process fd, close the file descriptor
* (can get eof if and only if all processes are have died, ie,
* coproc.njobs is 0 and the pipe is closed).
*/
if (c == EOF && !ecode)
coproc_read_close(fd);
#endif /* KSH */
return ecode ? ecode : c == EOF;
}
int
c_eval(wp)
char **wp;
{
register struct source *s;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
s = pushs(SWORDS, ATEMP);
s->u.strv = wp + builtin_opt.optind;
if (!Flag(FPOSIX)) {
/*
* Handle case where the command is empty due to failed
* command substitution, eg, eval "$(false)".
* In this case, shell() will not set/change exstat (because
* compiled tree is empty), so will use this value.
* subst_exstat is cleared in execute(), so should be 0 if
* there were no substitutions.
*
* A strict reading of POSIX says we don't do this (though
* it is traditionally done). [from 1003.2-1992]
* 3.9.1: Simple Commands
* ... If there is a command name, execution shall
* continue as described in 3.9.1.1. If there
* is no command name, but the command contained a command
* substitution, the command shall complete with the exit
* status of the last command substitution
* 3.9.1.1: Command Search and Execution
* ...(1)...(a) If the command name matches the name of
* a special built-in utility, that special built-in
* utility shall be invoked.
* 3.14.5: Eval
* ... If there are no arguments, or only null arguments,
* eval shall return an exit status of zero.
*/
exstat = subst_exstat;
}
return shell(s, FALSE);
}
int
c_trap(wp)
char **wp;
{
int i;
char *s;
register Trap *p;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
wp += builtin_opt.optind;
if (*wp == NULL) {
int anydfl = 0;
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) {
if (p->trap == NULL)
anydfl = 1;
else {
shprintf("trap -- ");
print_value_quoted(p->trap);
shprintf(" %s\n", p->name);
}
}
#if 0 /* this is ugly and not clear POSIX needs it */
/* POSIX may need this so output of trap can be saved and
* used to restore trap conditions
*/
if (anydfl) {
shprintf("trap -- -");
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->trap == NULL && p->name)
shprintf(" %s", p->name);
shprintf(newline);
}
#endif
return 0;
}
/*
* Use case sensitive lookup for first arg so the
* command 'exit' isn't confused with the pseudo-signal
* 'EXIT'.
*/
s = (gettrap(*wp, FALSE) == NULL) ? *wp++ : NULL; /* get command */
if (s != NULL && s[0] == '-' && s[1] == '\0')
s = NULL;
/* set/clear traps */
while (*wp != NULL) {
p = gettrap(*wp++, TRUE);
if (p == NULL) {
bi_errorf("bad signal %s", wp[-1]);
return 1;
}
settrap(p, s);
}
return 0;
}
int
c_exitreturn(wp)
char **wp;
{
int how = LEXIT;
int n;
char *arg;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
arg = wp[builtin_opt.optind];
if (arg) {
if (!getn(arg, &n)) {
exstat = 1;
warningf(TRUE, "%s: bad number", arg);
} else
exstat = n;
}
if (wp[0][0] == 'r') { /* return */
struct env *ep;
/* need to tell if this is exit or return so trap exit will
* work right (POSIX)
*/
for (ep = e; ep; ep = ep->oenv)
if (STOP_RETURN(ep->type)) {
how = LRETURN;
break;
}
}
if (how == LEXIT && !really_exit && j_stopped_running()) {
really_exit = 1;
how = LSHELL;
}
quitenv(); /* get rid of any i/o redirections */
unwind(how);
/*NOTREACHED*/
return 0;
}
int
c_brkcont(wp)
char **wp;
{
int n, quit;
struct env *ep, *last_ep = (struct env *) 0;
char *arg;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
arg = wp[builtin_opt.optind];
if (!arg)
n = 1;
else if (!bi_getn(arg, &n))
return 1;
quit = n;
if (quit <= 0) {
/* at&t ksh does this for non-interactive shells only - weird */
bi_errorf("%s: bad value", arg);
return 1;
}
/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
if (ep->type == E_LOOP) {
if (--quit == 0)
break;
ep->flags |= EF_BRKCONT_PASS;
last_ep = ep;
}
if (quit) {
/* at&t ksh doesn't print a message - just does what it
* can. We print a message 'cause it helps in debugging
* scripts, but don't generate an error (ie, keep going).
*/
if (n == quit) {
warningf(TRUE, "%s: cannot %s", wp[0], wp[0]);
return 0;
}
/* POSIX says if n is too big, the last enclosing loop
* shall be used. Doesn't say to print an error but we
* do anyway 'cause the user messed up.
*/
last_ep->flags &= ~EF_BRKCONT_PASS;
warningf(TRUE, "%s: can only %s %d level(s)",
wp[0], wp[0], n - quit);
}
unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
/*NOTREACHED*/
}
int
c_set(wp)
char **wp;
{
int argi, setargs;
struct block *l = e->loc;
register char **owp = wp;
if (wp[1] == NULL) {
static const char *const args [] = { "set", "-", NULL };
return c_typeset((char **) args);
}
argi = parse_args(wp, OF_SET, &setargs);
if (argi < 0)
return 1;
/* set $# and $* */
if (setargs) {
owp = wp += argi - 1;
wp[0] = l->argv[0]; /* save $0 */
while (*++wp != NULL)
*wp = str_save(*wp, &l->area);
l->argc = wp - owp - 1;
l->argv = (char **) alloc(sizeofN(char *, l->argc+2), &l->area);
for (wp = l->argv; (*wp++ = *owp++) != NULL; )
;
}
/* POSIX says set exit status is 0, but old scripts that use
* getopt(1), use the construct: set -- `getopt ab:c "$@"`
* which assumes the exit value set will be that of the ``
* (subst_exstat is cleared in execute() so that it will be 0
* if there are no command substitutions).
*/
return Flag(FPOSIX) ? 0 : subst_exstat;
}
int
c_unset(wp)
char **wp;
{
register char *id;
int optc, unset_var = 1;
int ret = 0;
while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != EOF)
switch (optc) {
case 'f':
unset_var = 0;
break;
case 'v':
unset_var = 1;
break;
case '?':
return 1;
}
wp += builtin_opt.optind;
for (; (id = *wp) != NULL; wp++)
if (unset_var) { /* unset variable */
struct tbl *vp = global(id);
if (!(vp->flag & ISSET))
ret = 1;
if ((vp->flag&RDONLY)) {
bi_errorf("%s is read only", vp->name);
return 1;
}
unset(vp, strchr(id, '[') ? 1 : 0);
} else { /* unset function */
if (define(id, (struct op *) NULL))
ret = 1;
}
return ret;
}
int
c_times(wp)
char **wp;
{
struct tms all;
(void) ksh_times(&all);
shprintf("Shell: %8ss user ", clocktos(all.tms_utime));
shprintf("%8ss system\n", clocktos(all.tms_stime));
shprintf("Kids: %8ss user ", clocktos(all.tms_cutime));
shprintf("%8ss system\n", clocktos(all.tms_cstime));
return 0;
}
/*
* time pipeline (really a statement, not a built-in command)
*/
int
timex(t, f)
struct op *t;
int f;
{
#define TF_NOARGS BIT(0)
#define TF_NOREAL BIT(1) /* don't report real time */
#define TF_POSIX BIT(2) /* report in posix format */
int rv = 0;
struct tms t0, t1, tms;
clock_t t0t, t1t = 0;
int tf = 0;
extern clock_t j_usrtime, j_systime; /* computed by j_wait */
char opts[1];
t0t = ksh_times(&t0);
if (t->left) {
/*
* Two ways of getting cpu usage of a command: just use t0
* and t1 (which will get cpu usage from other jobs that
* finish while we are executing t->left), or get the
* cpu usage of t->left. at&t ksh does the former, while
* pdksh tries to do the later (the j_usrtime hack doesn't
* really work as it only counts the last job).
*/
j_usrtime = j_systime = 0;
if (t->left->type == TCOM)
t->left->str = opts;
opts[0] = 0;
rv = execute(t->left, f | XTIME);
tf |= opts[0];
t1t = ksh_times(&t1);
} else
tf = TF_NOARGS;
if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */
tf |= TF_NOREAL;
tms.tms_utime = t0.tms_utime + t0.tms_cutime;
tms.tms_stime = t0.tms_stime + t0.tms_cstime;
} else {
tms.tms_utime = t1.tms_utime - t0.tms_utime + j_usrtime;
tms.tms_stime = t1.tms_stime - t0.tms_stime + j_systime;
}
if (!(tf & TF_NOREAL))
shf_fprintf(shl_out,
tf & TF_POSIX ? "real %8s\n" : "%8ss real ",
clocktos(t1t - t0t));
shf_fprintf(shl_out, tf & TF_POSIX ? "user %8s\n" : "%8ss user ",
clocktos(tms.tms_utime));
shf_fprintf(shl_out, tf & TF_POSIX ? "sys %8s\n" : "%8ss system\n",
clocktos(tms.tms_stime));
shf_flush(shl_out);
return rv;
}
void
timex_hook(t, app)
struct op *t;
char ** volatile *app;
{
char **wp = *app;
int optc;
int i, j;
Getopt opt;
ksh_getopt_reset(&opt, 0);
opt.optind = 0; /* start at the start */
while ((optc = ksh_getopt(wp, &opt, ":p")) != EOF)
switch (optc) {
case 'p':
t->str[0] |= TF_POSIX;
break;
case '?':
errorf("time: -%s unknown option", opt.optarg);
case ':':
errorf("time: -%s requires an argument",
opt.optarg);
}
/* Copy command words down over options. */
if (opt.optind != 0) {
for (i = 0; i < opt.optind; i++)
afree(wp[i], ATEMP);
for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++)
;
}
if (!wp[0])
t->str[0] |= TF_NOARGS;
*app = wp;
}
static char *
clocktos(t)
clock_t t;
{
static char temp[22]; /* enough for 64 bit clock_t */
register int i;
register char *cp = temp + sizeof(temp);
/* note: posix says must use max precision, ie, if clk_tck is
* 1000, must print 3 places after decimal (if non-zero, else 1).
*/
if (CLK_TCK != 100) /* convert to 1/100'ths */
t = (t < 1000000000/CLK_TCK) ?
(t * 100) / CLK_TCK : (t / CLK_TCK) * 100;
*--cp = '\0';
for (i = -2; i <= 0 || t > 0; i++) {
if (i == 0)
*--cp = '.';
*--cp = '0' + (char)(t%10);
t /= 10;
}
return cp;
}
/* exec with no args - args case is taken care of in comexec() */
int
c_exec(wp)
char ** wp;
{
int i;
/* make sure redirects stay in place */
if (e->savefd != NULL) {
for (i = 0; i < NUFILE; i++) {
if (e->savefd[i] > 0)
close(e->savefd[i]);
/*
* For ksh keep anything > 2 private,
* for sh, let them be (POSIX says what
* happens is unspecified and the bourne shell
* keeps them open).
*/
#ifdef KSH
if (!Flag(FSH) && i > 2 && e->savefd[i])
fd_clexec(i);
#endif /* KSH */
}
e->savefd = NULL;
}
return 0;
}
/* dummy function, special case in comexec() */
int
c_builtin(wp)
char ** wp;
{
return 0;
}
extern int c_test ARGS((char **wp)); /* in c_test.c */
extern int c_ulimit ARGS((char **wp)); /* in c_ulimit.c */
/* A leading = means assignments before command are kept;
* a leading * means a POSIX special builtin;
* a leading + means a POSIX regular builtin
* (* and + should not be combined).
*/
const struct builtin shbuiltins [] = {
{"*=.", c_dot},
{"*=:", c_label},
{"[", c_test},
{"*=break", c_brkcont},
{"=builtin", c_builtin},
{"*=continue", c_brkcont},
{"*=eval", c_eval},
{"*=exec", c_exec},
{"*=exit", c_exitreturn},
{"+false", c_label},
{"*=return", c_exitreturn},
{"*=set", c_set},
{"*=shift", c_shift},
{"=times", c_times},
{"*=trap", c_trap},
{"+=wait", c_wait},
{"+read", c_read},
{"test", c_test},
{"+true", c_label},
{"ulimit", c_ulimit},
{"+umask", c_umask},
{"*=unset", c_unset},
#ifdef OS2
/* In OS2, the first line of a file can be "extproc name", which
* tells the command interpreter (cmd.exe) to use name to execute
* the file. For this to be useful, ksh must ignore commands
* starting with extproc and this does the trick...
*/
{"extproc", c_label},
#endif /* OS2 */
{NULL, NULL}
};

664
c_test.c Normal file
View File

@ -0,0 +1,664 @@
/* $OpenBSD: c_test.c,v 1.9 2003/02/28 09:45:09 jmc Exp $ */
/*
* test(1); version 7-like -- author Erik Baalbergen
* modified by Eric Gisin to be used as built-in.
* modified by Arnold Robbins to add SVR3 compatibility
* (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
* modified by Michael Rendell to add Korn's [[ .. ]] expressions.
* modified by J.T. Conklin to add POSIX compatibility.
*/
#include "sh.h"
#include "ksh_stat.h"
#include "c_test.h"
/* test(1) accepts the following grammar:
oexpr ::= aexpr | aexpr "-o" oexpr ;
aexpr ::= nexpr | nexpr "-a" aexpr ;
nexpr ::= primary | "!" nexpr ;
primary ::= unary-operator operand
| operand binary-operator operand
| operand
| "(" oexpr ")"
;
unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
"-L"|"-h"|"-S"|"-H";
binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
"-nt"|"-ot"|"-ef"|
"<"|">" # rules used for [[ .. ]] expressions
;
operand ::= <any thing>
*/
#define T_ERR_EXIT 2 /* POSIX says > 1 for errors */
struct t_op {
char op_text[4];
Test_op op_num;
};
static const struct t_op u_ops [] = {
{"-a", TO_FILAXST },
{"-b", TO_FILBDEV },
{"-c", TO_FILCDEV },
{"-d", TO_FILID },
{"-e", TO_FILEXST },
{"-f", TO_FILREG },
{"-G", TO_FILGID },
{"-g", TO_FILSETG },
{"-h", TO_FILSYM },
{"-H", TO_FILCDF },
{"-k", TO_FILSTCK },
{"-L", TO_FILSYM },
{"-n", TO_STNZE },
{"-O", TO_FILUID },
{"-o", TO_OPTION },
{"-p", TO_FILFIFO },
{"-r", TO_FILRD },
{"-s", TO_FILGZ },
{"-S", TO_FILSOCK },
{"-t", TO_FILTT },
{"-u", TO_FILSETU },
{"-w", TO_FILWR },
{"-x", TO_FILEX },
{"-z", TO_STZER },
{"", TO_NONOP }
};
static const struct t_op b_ops [] = {
{"=", TO_STEQL },
#ifdef KSH
{"==", TO_STEQL },
#endif /* KSH */
{"!=", TO_STNEQ },
{"<", TO_STLT },
{">", TO_STGT },
{"-eq", TO_INTEQ },
{"-ne", TO_INTNE },
{"-gt", TO_INTGT },
{"-ge", TO_INTGE },
{"-lt", TO_INTLT },
{"-le", TO_INTLE },
{"-ef", TO_FILEQ },
{"-nt", TO_FILNT },
{"-ot", TO_FILOT },
{"", TO_NONOP }
};
static int test_stat ARGS((const char *path, struct stat *statb));
static int test_eaccess ARGS((const char *path, int mode));
static int test_oexpr ARGS((Test_env *te, int do_eval));
static int test_aexpr ARGS((Test_env *te, int do_eval));
static int test_nexpr ARGS((Test_env *te, int do_eval));
static int test_primary ARGS((Test_env *te, int do_eval));
static int ptest_isa ARGS((Test_env *te, Test_meta meta));
static const char *ptest_getopnd ARGS((Test_env *te, Test_op op, int do_eval));
static int ptest_eval ARGS((Test_env *te, Test_op op, const char *opnd1,
const char *opnd2, int do_eval));
static void ptest_error ARGS((Test_env *te, int offset, const char *msg));
int
c_test(wp)
char **wp;
{
int argc;
int res;
Test_env te;
te.flags = 0;
te.isa = ptest_isa;
te.getopnd = ptest_getopnd;
te.eval = ptest_eval;
te.error = ptest_error;
for (argc = 0; wp[argc]; argc++)
;
if (strcmp(wp[0], "[") == 0) {
if (strcmp(wp[--argc], "]") != 0) {
bi_errorf("missing ]");
return T_ERR_EXIT;
}
}
te.pos.wp = wp + 1;
te.wp_end = wp + argc;
/*
* Handle the special cases from POSIX.2, section 4.62.4.
* Implementation of all the rules isn't necessary since
* our parser does the right thing for the omitted steps.
*/
if (argc <= 5) {
char **owp = wp;
int invert = 0;
Test_op op;
const char *opnd1, *opnd2;
while (--argc >= 0) {
if ((*te.isa)(&te, TM_END))
return !0;
if (argc == 3) {
opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
if ((op = (Test_op) (*te.isa)(&te, TM_BINOP))) {
opnd2 = (*te.getopnd)(&te, op, 1);
res = (*te.eval)(&te, op, opnd1, opnd2,
1);
if (te.flags & TEF_ERROR)
return T_ERR_EXIT;
if (invert & 1)
res = !res;
return !res;
}
/* back up to opnd1 */
te.pos.wp--;
}
if (argc == 1) {
opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
/* Historically, -t by itself test if fd 1
* is a file descriptor, but POSIX says its
* a string test...
*/
if (!Flag(FPOSIX) && strcmp(opnd1, "-t") == 0)
break;
res = (*te.eval)(&te, TO_STNZE, opnd1,
(char *) 0, 1);
if (invert & 1)
res = !res;
return !res;
}
if ((*te.isa)(&te, TM_NOT)) {
invert++;
} else
break;
}
te.pos.wp = owp + 1;
}
return test_parse(&te);
}
/*
* Generic test routines.
*/
Test_op
test_isop(te, meta, s)
Test_env *te;
Test_meta meta;
const char *s;
{
char sc1;
const struct t_op *otab;
otab = meta == TM_UNOP ? u_ops : b_ops;
if (*s) {
sc1 = s[1];
for (; otab->op_text[0]; otab++)
if (sc1 == otab->op_text[1]
&& strcmp(s, otab->op_text) == 0
&& ((te->flags & TEF_DBRACKET)
|| (otab->op_num != TO_STLT
&& otab->op_num != TO_STGT)))
return otab->op_num;
}
return TO_NONOP;
}
int
test_eval(te, op, opnd1, opnd2, do_eval)
Test_env *te;
Test_op op;
const char *opnd1;
const char *opnd2;
int do_eval;
{
int res;
int not;
struct stat b1, b2;
if (!do_eval)
return 0;
switch ((int) op) {
/*
* Unary Operators
*/
case TO_STNZE: /* -n */
return *opnd1 != '\0';
case TO_STZER: /* -z */
return *opnd1 == '\0';
case TO_OPTION: /* -o */
if ((not = *opnd1 == '!'))
opnd1++;
if ((res = option(opnd1)) < 0)
res = 0;
else {
res = Flag(res);
if (not)
res = !res;
}
return res;
case TO_FILRD: /* -r */
return test_eaccess(opnd1, R_OK) == 0;
case TO_FILWR: /* -w */
return test_eaccess(opnd1, W_OK) == 0;
case TO_FILEX: /* -x */
return test_eaccess(opnd1, X_OK) == 0;
case TO_FILAXST: /* -a */
return test_stat(opnd1, &b1) == 0;
case TO_FILEXST: /* -e */
/* at&t ksh does not appear to do the /dev/fd/ thing for
* this (unless the os itself handles it)
*/
return stat(opnd1, &b1) == 0;
case TO_FILREG: /* -r */
return test_stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode);
case TO_FILID: /* -d */
return test_stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode);
case TO_FILCDEV: /* -c */
#ifdef S_ISCHR
return test_stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode);
#else
return 0;
#endif
case TO_FILBDEV: /* -b */
#ifdef S_ISBLK
return test_stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode);
#else
return 0;
#endif
case TO_FILFIFO: /* -p */
#ifdef S_ISFIFO
return test_stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode);
#else
return 0;
#endif
case TO_FILSYM: /* -h -L */
#ifdef S_ISLNK
return lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode);
#else
return 0;
#endif
case TO_FILSOCK: /* -S */
#ifdef S_ISSOCK
return test_stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode);
#else
return 0;
#endif
case TO_FILCDF:/* -H HP context dependent files (directories) */
#ifdef S_ISCDF
{
/* Append a + to filename and check to see if result is a
* setuid directory. CDF stuff in general is hookey, since
* it breaks for the following sequence: echo hi > foo+;
* mkdir foo; echo bye > foo/default; chmod u+s foo
* (foo+ refers to the file with hi in it, there is no way
* to get at the file with bye in it - please correct me if
* I'm wrong about this).
*/
int len = strlen(opnd1);
char *p = str_nsave(opnd1, len + 1, ATEMP);
p[len++] = '+';
p[len] = '\0';
return stat(p, &b1) == 0 && S_ISCDF(b1.st_mode);
}
#else
return 0;
#endif
case TO_FILSETU: /* -u */
#ifdef S_ISUID
return test_stat(opnd1, &b1) == 0
&& (b1.st_mode & S_ISUID) == S_ISUID;
#else
return 0;
#endif
case TO_FILSETG: /* -g */
#ifdef S_ISGID
return test_stat(opnd1, &b1) == 0
&& (b1.st_mode & S_ISGID) == S_ISGID;
#else
return 0;
#endif
case TO_FILSTCK: /* -k */
return test_stat(opnd1, &b1) == 0
&& (b1.st_mode & S_ISVTX) == S_ISVTX;
case TO_FILGZ: /* -s */
return test_stat(opnd1, &b1) == 0 && b1.st_size > 0L;
case TO_FILTT: /* -t */
if (opnd1 && !bi_getn(opnd1, &res)) {
te->flags |= TEF_ERROR;
res = 0;
} else {
/* generate error if in FPOSIX mode? */
res = isatty(opnd1 ? res : 0);
}
return res;
case TO_FILUID: /* -O */
return test_stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid;
case TO_FILGID: /* -G */
return test_stat(opnd1, &b1) == 0 && b1.st_gid == getegid();
/*
* Binary Operators
*/
case TO_STEQL: /* = */
if (te->flags & TEF_DBRACKET)
return gmatch(opnd1, opnd2, FALSE);
return strcmp(opnd1, opnd2) == 0;
case TO_STNEQ: /* != */
if (te->flags & TEF_DBRACKET)
return !gmatch(opnd1, opnd2, FALSE);
return strcmp(opnd1, opnd2) != 0;
case TO_STLT: /* < */
return strcmp(opnd1, opnd2) < 0;
case TO_STGT: /* > */
return strcmp(opnd1, opnd2) > 0;
case TO_INTEQ: /* -eq */
case TO_INTNE: /* -ne */
case TO_INTGE: /* -ge */
case TO_INTGT: /* -gt */
case TO_INTLE: /* -le */
case TO_INTLT: /* -lt */
{
long v1, v2;
if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR)
|| !evaluate(opnd2, &v2, KSH_RETURN_ERROR))
{
/* error already printed.. */
te->flags |= TEF_ERROR;
return 1;
}
switch ((int) op) {
case TO_INTEQ:
return v1 == v2;
case TO_INTNE:
return v1 != v2;
case TO_INTGE:
return v1 >= v2;
case TO_INTGT:
return v1 > v2;
case TO_INTLE:
return v1 <= v2;
case TO_INTLT:
return v1 < v2;
}
}
case TO_FILNT: /* -nt */
{
int s2;
/* ksh88/ksh93 succeed if file2 can't be stated
* (subtly different from `does not exist').
*/
return stat(opnd1, &b1) == 0
&& (((s2 = stat(opnd2, &b2)) == 0
&& b1.st_mtime > b2.st_mtime) || s2 < 0);
}
case TO_FILOT: /* -ot */
{
int s1;
/* ksh88/ksh93 succeed if file1 can't be stated
* (subtly different from `does not exist').
*/
return stat(opnd2, &b2) == 0
&& (((s1 = stat(opnd1, &b1)) == 0
&& b1.st_mtime < b2.st_mtime) || s1 < 0);
}
case TO_FILEQ: /* -ef */
return stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0
&& b1.st_dev == b2.st_dev
&& b1.st_ino == b2.st_ino;
}
(*te->error)(te, 0, "internal error: unknown op");
return 1;
}
/* Nasty kludge to handle Korn's bizarre /dev/fd hack */
static int
test_stat(path, statb)
const char *path;
struct stat *statb;
{
#if !defined(HAVE_DEV_FD)
int fd;
if (strncmp(path, "/dev/fd/", 8) == 0 && getn(path + 8, &fd))
return fstat(fd, statb);
#endif /* !HAVE_DEV_FD */
return stat(path, statb);
}
/* Routine to handle Korn's /dev/fd hack, and to deal with X_OK on
* non-directories when running as root.
*/
static int
test_eaccess(path, mode)
const char *path;
int mode;
{
int res;
#if !defined(HAVE_DEV_FD)
int fd;
/* Note: doesn't handle //dev/fd, etc.. (this is ok) */
if (strncmp(path, "/dev/fd/", 8) == 0 && getn(path + 8, &fd)) {
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) < 0
|| (mode & X_OK)
|| ((mode & W_OK) && (flags & O_ACCMODE) == O_RDONLY)
|| ((mode & R_OK) && (flags & O_ACCMODE) == O_WRONLY))
return -1;
return 0;
}
#endif /* !HAVE_DEV_FD */
/* On most (all?) unixes, access() says everything is executable for
* root - avoid this on files by using stat().
*/
if ((mode & X_OK) && ksheuid == 0) {
struct stat statb;
if (stat(path, &statb) < 0)
res = -1;
else if (S_ISDIR(statb.st_mode))
res = 0;
else
res = (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
? 0 : -1;
/* Need to check other permissions? If so, use access() as
* this will deal with root on NFS.
*/
if (res == 0 && (mode & (R_OK|W_OK)))
res = eaccess(path, mode);
} else
res = eaccess(path, mode);
return res;
}
int
test_parse(te)
Test_env *te;
{
int res;
res = test_oexpr(te, 1);
if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END))
(*te->error)(te, 0, "unexpected operator/operand");
return (te->flags & TEF_ERROR) ? T_ERR_EXIT : !res;
}
static int
test_oexpr(te, do_eval)
Test_env *te;
int do_eval;
{
int res;
res = test_aexpr(te, do_eval);
if (res)
do_eval = 0;
if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR))
return test_oexpr(te, do_eval) || res;
return res;
}
static int
test_aexpr(te, do_eval)
Test_env *te;
int do_eval;
{
int res;
res = test_nexpr(te, do_eval);
if (!res)
do_eval = 0;
if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND))
return test_aexpr(te, do_eval) && res;
return res;
}
static int
test_nexpr(te, do_eval)
Test_env *te;
int do_eval;
{
if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT))
return !test_nexpr(te, do_eval);
return test_primary(te, do_eval);
}
static int
test_primary(te, do_eval)
Test_env *te;
int do_eval;
{
const char *opnd1, *opnd2;
int res;
Test_op op;
if (te->flags & TEF_ERROR)
return 0;
if ((*te->isa)(te, TM_OPAREN)) {
res = test_oexpr(te, do_eval);
if (te->flags & TEF_ERROR)
return 0;
if (!(*te->isa)(te, TM_CPAREN)) {
(*te->error)(te, 0, "missing closing paren");
return 0;
}
return res;
}
if ((op = (Test_op) (*te->isa)(te, TM_UNOP))) {
/* unary expression */
opnd1 = (*te->getopnd)(te, op, do_eval);
if (!opnd1) {
(*te->error)(te, -1, "missing argument");
return 0;
}
return (*te->eval)(te, op, opnd1, (const char *) 0, do_eval);
}
opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval);
if (!opnd1) {
(*te->error)(te, 0, "expression expected");
return 0;
}
if ((op = (Test_op) (*te->isa)(te, TM_BINOP))) {
/* binary expression */
opnd2 = (*te->getopnd)(te, op, do_eval);
if (!opnd2) {
(*te->error)(te, -1, "missing second argument");
return 0;
}
return (*te->eval)(te, op, opnd1, opnd2, do_eval);
}
if (te->flags & TEF_DBRACKET) {
(*te->error)(te, -1, "missing expression operator");
return 0;
}
return (*te->eval)(te, TO_STNZE, opnd1, (const char *) 0, do_eval);
}
/*
* Plain test (test and [ .. ]) specific routines.
*/
/* Test if the current token is a whatever. Accepts the current token if
* it is. Returns 0 if it is not, non-zero if it is (in the case of
* TM_UNOP and TM_BINOP, the returned value is a Test_op).
*/
static int
ptest_isa(te, meta)
Test_env *te;
Test_meta meta;
{
/* Order important - indexed by Test_meta values */
static const char *const tokens[] = {
"-o", "-a", "!", "(", ")"
};
int ret;
if (te->pos.wp >= te->wp_end)
return meta == TM_END;
if (meta == TM_UNOP || meta == TM_BINOP)
ret = (int) test_isop(te, meta, *te->pos.wp);
else if (meta == TM_END)
ret = 0;
else
ret = strcmp(*te->pos.wp, tokens[(int) meta]) == 0;
/* Accept the token? */
if (ret)
te->pos.wp++;
return ret;
}
static const char *
ptest_getopnd(te, op, do_eval)
Test_env *te;
Test_op op;
int do_eval;
{
if (te->pos.wp >= te->wp_end)
return op == TO_FILTT ? "1" : (const char *) 0;
return *te->pos.wp++;
}
static int
ptest_eval(te, op, opnd1, opnd2, do_eval)
Test_env *te;
Test_op op;
const char *opnd1;
const char *opnd2;
int do_eval;
{
return test_eval(te, op, opnd1, opnd2, do_eval);
}
static void
ptest_error(te, offset, msg)
Test_env *te;
int offset;
const char *msg;
{
const char *op = te->pos.wp + offset >= te->wp_end ?
(const char *) 0 : te->pos.wp[offset];
te->flags |= TEF_ERROR;
if (op)
bi_errorf("%s: %s", op, msg);
else
bi_errorf("%s", msg);
}

55
c_test.h Normal file
View File

@ -0,0 +1,55 @@
/* $OpenBSD: c_test.h,v 1.1.1.1 1996/08/14 06:19:10 downsj Exp $ */
/* Various types of operations. Keeping things grouped nicely
* (unary,binary) makes switch() statements more efficeint.
*/
enum Test_op {
TO_NONOP = 0, /* non-operator */
/* unary operators */
TO_STNZE, TO_STZER, TO_OPTION,
TO_FILAXST,
TO_FILEXST,
TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK,
TO_FILCDF, TO_FILID, TO_FILGID, TO_FILSETG, TO_FILSTCK, TO_FILUID,
TO_FILRD, TO_FILGZ, TO_FILTT, TO_FILSETU, TO_FILWR, TO_FILEX,
/* binary operators */
TO_STEQL, TO_STNEQ, TO_STLT, TO_STGT, TO_INTEQ, TO_INTNE, TO_INTGT,
TO_INTGE, TO_INTLT, TO_INTLE, TO_FILEQ, TO_FILNT, TO_FILOT
};
typedef enum Test_op Test_op;
/* Used by Test_env.isa() (order important - used to index *_tokens[] arrays) */
enum Test_meta {
TM_OR, /* -o or || */
TM_AND, /* -a or && */
TM_NOT, /* ! */
TM_OPAREN, /* ( */
TM_CPAREN, /* ) */
TM_UNOP, /* unary operator */
TM_BINOP, /* binary operator */
TM_END /* end of input */
};
typedef enum Test_meta Test_meta;
#define TEF_ERROR BIT(0) /* set if we've hit an error */
#define TEF_DBRACKET BIT(1) /* set if [[ .. ]] test */
typedef struct test_env Test_env;
struct test_env {
int flags; /* TEF_* */
union {
char **wp; /* used by ptest_* */
XPtrV *av; /* used by dbtestp_* */
} pos;
char **wp_end; /* used by ptest_* */
int (*isa) ARGS((Test_env *te, Test_meta meta));
const char *(*getopnd) ARGS((Test_env *te, Test_op op, int do_eval));
int (*eval) ARGS((Test_env *te, Test_op op, const char *opnd1,
const char *opnd2, int do_eval));
void (*error) ARGS((Test_env *te, int offset, const char *msg));
};
Test_op test_isop ARGS((Test_env *te, Test_meta meta, const char *s));
int test_eval ARGS((Test_env *te, Test_op op, const char *opnd1,
const char *opnd2, int do_eval));
int test_parse ARGS((Test_env *te));

273
c_ulimit.c Normal file
View File

@ -0,0 +1,273 @@
/* $OpenBSD: c_ulimit.c,v 1.9 2002/06/09 05:47:27 todd Exp $ */
/*
ulimit -- handle "ulimit" builtin
Reworked to use getrusage() and ulimit() at once (as needed on
some schizophenic systems, eg, HP-UX 9.01), made argument parsing
conform to at&t ksh, added autoconf support. Michael Rendell, May, '94
Eric Gisin, September 1988
Adapted to PD KornShell. Removed AT&T code.
last edit: 06-Jun-1987 D A Gwyn
This started out as the BRL UNIX System V system call emulation
for 4.nBSD, and was later extended by Doug Kingston to handle
the extended 4.nBSD resource limits. It now includes the code
that was originally under case SYSULIMIT in source file "xec.c".
*/
#include "sh.h"
#include "ksh_time.h"
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif /* HAVE_SYS_RESOURCE_H */
#ifdef HAVE_ULIMIT_H
# include <ulimit.h>
#else /* HAVE_ULIMIT_H */
# ifdef HAVE_ULIMIT
extern long ulimit();
# endif /* HAVE_ULIMIT */
#endif /* HAVE_ULIMIT_H */
#define SOFT 0x1
#define HARD 0x2
#ifdef RLIM_INFINITY
# define KSH_RLIM_INFINITY RLIM_INFINITY
#else
# define KSH_RLIM_INFINITY ((rlim_t) 1 << (sizeof(rlim_t) * 8 - 1) - 1)
#endif /* RLIM_INFINITY */
int
c_ulimit(wp)
char **wp;
{
static const struct limits {
const char *name;
enum { RLIMIT, ULIMIT } which;
int gcmd; /* get command */
int scmd; /* set command (or -1, if no set command) */
int factor; /* multiply by to get rlim_{cur,max} values */
char option;
} limits[] = {
/* Do not use options -H, -S or -a */
#ifdef RLIMIT_CPU
{ "time(cpu-seconds)", RLIMIT, RLIMIT_CPU, RLIMIT_CPU, 1, 't' },
#endif
#ifdef RLIMIT_FSIZE
{ "file(blocks)", RLIMIT, RLIMIT_FSIZE, RLIMIT_FSIZE, 512, 'f' },
#else /* RLIMIT_FSIZE */
# ifdef UL_GETFSIZE /* x/open */
{ "file(blocks)", ULIMIT, UL_GETFSIZE, UL_SETFSIZE, 1, 'f' },
# else /* UL_GETFSIZE */
# ifdef UL_GFILLIM /* svr4/xenix */
{ "file(blocks)", ULIMIT, UL_GFILLIM, UL_SFILLIM, 1, 'f' },
# else /* UL_GFILLIM */
{ "file(blocks)", ULIMIT, 1, 2, 1, 'f' },
# endif /* UL_GFILLIM */
# endif /* UL_GETFSIZE */
#endif /* RLIMIT_FSIZE */
#ifdef RLIMIT_CORE
{ "coredump(blocks)", RLIMIT, RLIMIT_CORE, RLIMIT_CORE, 512, 'c' },
#endif
#ifdef RLIMIT_DATA
{ "data(kbytes)", RLIMIT, RLIMIT_DATA, RLIMIT_DATA, 1024, 'd' },
#endif
#ifdef RLIMIT_STACK
{ "stack(kbytes)", RLIMIT, RLIMIT_STACK, RLIMIT_STACK, 1024, 's' },
#endif
#ifdef RLIMIT_MEMLOCK
{ "lockedmem(kbytes)", RLIMIT, RLIMIT_MEMLOCK, RLIMIT_MEMLOCK, 1024, 'l' },
#endif
#ifdef RLIMIT_RSS
{ "memory(kbytes)", RLIMIT, RLIMIT_RSS, RLIMIT_RSS, 1024, 'm' },
#endif
#ifdef RLIMIT_NOFILE
{ "nofiles(descriptors)", RLIMIT, RLIMIT_NOFILE, RLIMIT_NOFILE, 1, 'n' },
#else /* RLIMIT_NOFILE */
# ifdef UL_GDESLIM /* svr4/xenix */
{ "nofiles(descriptors)", ULIMIT, UL_GDESLIM, -1, 1, 'n' },
# endif /* UL_GDESLIM */
#endif /* RLIMIT_NOFILE */
#ifdef RLIMIT_NPROC
{ "processes", RLIMIT, RLIMIT_NPROC, RLIMIT_NPROC, 1, 'p' },
#endif
#ifdef RLIMIT_VMEM
{ "vmemory(kbytes)", RLIMIT, RLIMIT_VMEM, RLIMIT_VMEM, 1024, 'v' },
#else /* RLIMIT_VMEM */
/* These are not quite right - really should subtract etext or something */
# ifdef UL_GMEMLIM /* svr4/xenix */
{ "vmemory(maxaddr)", ULIMIT, UL_GMEMLIM, -1, 1, 'v' },
# else /* UL_GMEMLIM */
# ifdef UL_GETBREAK /* osf/1 */
{ "vmemory(maxaddr)", ULIMIT, UL_GETBREAK, -1, 1, 'v' },
# else /* UL_GETBREAK */
# ifdef UL_GETMAXBRK /* hpux */
{ "vmemory(maxaddr)", ULIMIT, UL_GETMAXBRK, -1, 1, 'v' },
# endif /* UL_GETMAXBRK */
# endif /* UL_GETBREAK */
# endif /* UL_GMEMLIM */
#endif /* RLIMIT_VMEM */
#ifdef RLIMIT_SWAP
{ "swap(kbytes)", RLIMIT_SWAP, RLIMIT_SWAP, 1024, 'w' },
#endif
{ (char *) 0 }
};
static char options[3 + NELEM(limits)];
rlim_t UNINITIALIZED(val);
int how = SOFT | HARD;
const struct limits *l;
int set, all = 0;
int optc, what;
#ifdef HAVE_SETRLIMIT
struct rlimit limit;
#endif /* HAVE_SETRLIMIT */
if (!options[0]) {
/* build options string on first call - yuck */
char *p = options;
*p++ = 'H'; *p++ = 'S'; *p++ = 'a';
for (l = limits; l->name; l++)
*p++ = l->option;
*p = '\0';
}
what = 'f';
while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF)
switch (optc) {
case 'H':
how = HARD;
break;
case 'S':
how = SOFT;
break;
case 'a':
all = 1;
break;
case '?':
return 1;
default:
what = optc;
}
for (l = limits; l->name && l->option != what; l++)
;
if (!l->name) {
internal_errorf(0, "ulimit: %c", what);
return 1;
}
wp += builtin_opt.optind;
set = *wp ? 1 : 0;
if (set) {
if (all || wp[1]) {
bi_errorf("too many arguments");
return 1;
}
if (strcmp(wp[0], "unlimited") == 0)
val = KSH_RLIM_INFINITY;
else {
long rval;
if (!evaluate(wp[0], &rval, KSH_RETURN_ERROR))
return 1;
/* Avoid problems caused by typos that
* evaluate misses due to evaluating unset
* parameters to 0...
* If this causes problems, will have to
* add parameter to evaluate() to control
* if unset params are 0 or an error.
*/
if (!rval && !digit(wp[0][0])) {
bi_errorf("invalid limit: %s", wp[0]);
return 1;
}
val = rval * l->factor;
}
}
if (all) {
for (l = limits; l->name; l++) {
#ifdef HAVE_SETRLIMIT
if (l->which == RLIMIT) {
getrlimit(l->gcmd, &limit);
if (how & SOFT)
val = limit.rlim_cur;
else if (how & HARD)
val = limit.rlim_max;
} else
#endif /* HAVE_SETRLIMIT */
#ifdef HAVE_ULIMIT
{
val = ulimit(l->gcmd, (rlim_t) 0);
}
#else /* HAVE_ULIMIT */
;
#endif /* HAVE_ULIMIT */
shprintf("%-20s ", l->name);
#ifdef RLIM_INFINITY
if (val == RLIM_INFINITY)
shprintf("unlimited\n");
else
#endif /* RLIM_INFINITY */
{
val /= l->factor;
shprintf("%ld\n", (long) val);
}
}
return 0;
}
#ifdef HAVE_SETRLIMIT
if (l->which == RLIMIT) {
getrlimit(l->gcmd, &limit);
if (set) {
if (how & SOFT)
limit.rlim_cur = val;
if (how & HARD)
limit.rlim_max = val;
if (setrlimit(l->scmd, &limit) < 0) {
if (errno == EPERM)
bi_errorf("exceeds allowable limit");
else
bi_errorf("bad limit: %s",
strerror(errno));
return 1;
}
} else {
if (how & SOFT)
val = limit.rlim_cur;
else if (how & HARD)
val = limit.rlim_max;
}
} else
#endif /* HAVE_SETRLIMIT */
#ifdef HAVE_ULIMIT
{
if (set) {
if (l->scmd == -1) {
bi_errorf("can't change limit");
return 1;
} else if (ulimit(l->scmd, val) < 0) {
bi_errorf("bad limit: %s", strerror(errno));
return 1;
}
} else
val = ulimit(l->gcmd, (rlim_t) 0);
}
#else /* HAVE_ULIMIT */
;
#endif /* HAVE_ULIMIT */
if (!set) {
#ifdef RLIM_INFINITY
if (val == RLIM_INFINITY)
shprintf("unlimited\n");
else
#endif /* RLIM_INFINITY */
{
val /= l->factor;
shprintf("%ld\n", (long) val);
}
}
return 0;
}

62
conf-end.h Normal file
View File

@ -0,0 +1,62 @@
/* $OpenBSD: conf-end.h,v 1.2 1996/08/25 12:37:58 downsj Exp $ */
/*
* End of configuration stuff for PD ksh.
*/
#if defined(EMACS) || defined(VI)
# define EDIT
#else
# undef EDIT
#endif
/* Super small configuration-- no editing. */
#if defined(EDIT) && defined(NOEDIT)
# undef EDIT
# undef EMACS
# undef VI
#endif
/* Editing implies history */
#if defined(EDIT) && !defined(HISTORY)
# define HISTORY
#endif /* EDIT */
/*
* if you don't have mmap() you can't use Peter Collinson's history
* mechanism. If that is the case, then define EASY_HISTORY
*/
#if defined(HISTORY) && (!defined(COMPLEX_HISTORY) || !defined(HAVE_MMAP) || !defined(HAVE_FLOCK))
# undef COMPLEX_HISTORY
# define EASY_HISTORY /* sjg's trivial history file */
#endif
/* Can we safely catch sigchld and wait for processes? */
#if (defined(HAVE_WAITPID) || defined(HAVE_WAIT3)) \
&& (defined(POSIX_SIGNALS) || defined(BSD42_SIGNALS))
# define JOB_SIGS
#endif
#if !defined(JOB_SIGS) || !(defined(POSIX_PGRP) || defined(BSD_PGRP))
# undef JOBS /* if no JOB_SIGS, no job control support */
#endif
/* pdksh assumes system calls return EINTR if a signal happened (this so
* the signal handler doesn't have to longjmp()). I don't know if this
* happens (or can be made to happen) with sigset() et. al. (the bsd41 signal
* routines), so, the autoconf stuff checks what they do and defines
* SIGNALS_DONT_INTERRUPT if signals don't interrupt read().
* If SIGNALS_DONT_INTERRUPT isn't defined and your compiler chokes on this,
* delete the hash in front of the error (and file a bug report).
*/
#ifdef SIGNALS_DONT_INTERRUPT
# error pdksh needs interruptable system calls.
#endif /* SIGNALS_DONT_INTERRUPT */
#ifdef HAVE_GCC_FUNC_ATTR
# define GCC_FUNC_ATTR(x) __attribute__((x))
# define GCC_FUNC_ATTR2(x,y) __attribute__((x,y))
#else
# define GCC_FUNC_ATTR(x)
# define GCC_FUNC_ATTR2(x,y)
#endif /* HAVE_GCC_FUNC_ATTR */

364
config.h Normal file
View File

@ -0,0 +1,364 @@
/* $OpenBSD: config.h,v 1.7 2003/02/28 09:45:09 jmc Exp $ */
/* config.h. Generated automatically by configure. */
/* config.h.in. Generated automatically from configure.in by autoheader. */
/*
* This file, acconfig.h, which is a part of pdksh (the public domain ksh),
* is placed in the public domain. It comes with no licence, warranty
* or guarantee of any kind (i.e., at your own risk).
*/
#ifndef CONFIG_H
#define CONFIG_H
/* Define if on AIX 3.
System headers sometimes define this.
We just want to avoid a redefinition error message. */
#ifndef _ALL_SOURCE
/* #undef _ALL_SOURCE */
#endif
/* Define if the closedir function returns void instead of int. */
/* #undef CLOSEDIR_VOID */
/* Define to empty if the keyword does not work. */
/* #undef const */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef gid_t */
/* Define if you have a working `mmap' system call. */
#define HAVE_MMAP 1
/* Define if your struct stat has st_rdev. */
#define HAVE_ST_RDEV 1
/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
#define HAVE_SYS_WAIT_H 1
/* Define if you have <unistd.h>. */
#define HAVE_UNISTD_H 1
/* Define if on MINIX. */
/* #undef _MINIX */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef mode_t */
/* Define to `long' if <sys/types.h> doesn't define. */
/* #undef off_t */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef pid_t */
/* Define if the system does not provide POSIX.1 features except
with this defined. */
/* #undef _POSIX_1_SOURCE */
/* Define if you need to in order for stat and other things to work. */
/* #undef _POSIX_SOURCE */
/* Define as the return type of signal handlers (int or void). */
#define RETSIGTYPE void
/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */
/* #undef STAT_MACROS_BROKEN */
/* Define if `sys_siglist' is declared by <signal.h>. */
#define SYS_SIGLIST_DECLARED 1
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef uid_t */
/* Define if the closedir function returns void instead of int. */
/* #undef VOID_CLOSEDIR */
/* Define if your kernal doesn't handle scripts starting with #! */
/* #undef SHARPBANG */
/* Define if dup2() preserves the close-on-exec flag (ultrix does this) */
/* #undef DUP2_BROKEN */
/* Define as the return value of signal handlers (0 or ). */
#define RETSIGVAL
/* Define if you have posix signal routines (sigaction(), et. al.) */
#define POSIX_SIGNALS 1
/* Define if you have BSD4.2 signal routines (sigsetmask(), et. al.) */
/* #undef BSD42_SIGNALS */
/* Define if you have BSD4.1 signal routines (sigset(), et. al.) */
/* #undef BSD41_SIGNALS */
/* Define if you have v7 signal routines (signal(), signal reset on delivery) */
/* #undef V7_SIGNALS */
/* Define to use the fake posix signal routines (sigact.[ch]) */
/* #undef USE_FAKE_SIGACT */
/* Define if signals don't interrupt read() */
/* #undef SIGNALS_DONT_INTERRUPT */
/* Define if you have bsd versions of the setpgrp() and getpgrp() routines */
/* #undef BSD_PGRP */
/* Define if you have POSIX versions of the setpgid() and getpgrp() routines */
#define POSIX_PGRP 1
/* Define if you have sysV versions of the setpgrp() and getpgrp() routines */
/* #undef SYSV_PGRP */
/* Define if you don't have setpgrp(), setpgid() or getpgrp() routines */
/* #undef NO_PGRP */
/* Define to char if your compiler doesn't like the void keyword */
/* #undef void */
/* Define to nothing if compiler doesn't like the volatile keyword */
/* #undef volatile */
/* Define if C compiler groks function prototypes */
#define HAVE_PROTOTYPES 1
/* Define if C compiler groks __attribute__((...)) (const, noreturn, format) */
#define HAVE_GCC_FUNC_ATTR 1
/* Define to 32-bit signed integer type if <sys/types.h> doesn't define */
/* #undef clock_t */
/* Define to the type of struct rlimit fields if the rlim_t type is missing */
/* #undef rlim_t */
/* Define if time() is declared in <time.h> */
#define TIME_DECLARED 1
/* Define to `unsigned' if <signal.h> doesn't define */
/* #undef sigset_t */
/* Define if sys_errlist[] and sys_nerr are in the C library */
#define HAVE_SYS_ERRLIST 1
/* Define if sys_errlist[] and sys_nerr are defined in <errno.h> */
#define SYS_ERRLIST_DECLARED 1
/* Define if sys_siglist[] is in the C library */
#define HAVE_SYS_SIGLIST 1
/* Define if you have a sane <termios.h> header file */
#define HAVE_TERMIOS_H 1
/* Define if you have a memset() function in your C library */
#define HAVE_MEMSET 1
/* Define if you have a memmove() function in your C library */
#define HAVE_MEMMOVE 1
/* Define if you have a bcopy() function in your C library */
/* #undef HAVE_BCOPY */
/* Define if you have a lstat() function in your C library */
#define HAVE_LSTAT 1
/* Define if you have a sane <termio.h> header file */
/* #undef HAVE_TERMIO_H */
/* Define if you don't have times() or if it always returns 0 */
/* #undef TIMES_BROKEN */
/* Define if opendir() will open non-directory files */
/* #undef OPENDIR_DOES_NONDIR */
/* Define if the pgrp of setpgrp() can't be the pid of a zombie process */
/* #undef NEED_PGRP_SYNC */
/* Define if you arg running SCO unix */
/* #undef OS_SCO */
/* Define if you arg running ISC unix */
/* #undef OS_ISC */
/* Define if you arg running OS2 with the EMX library */
/* #undef OS2 */
/* Define if you have a POSIX.1 compatible <sys/wait.h> */
#define POSIX_SYS_WAIT 1
/* Define if your OS maps references to /dev/fd/n to file descriptor n */
#define HAVE_DEV_FD 1
/* Default PATH (see comments in configure.in for more details) */
#define DEFAULT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
/* Define if your C library's getwd/getcwd function dumps core in unreadable
* directories. */
/* #undef HPUX_GETWD_BUG */
/* Include ksh features? (see comments in configure.in for more details) */
/* #define KSH 1 */
/* Include emacs editing? (see comments in configure.in for more details) */
#define EMACS 1
/* Include vi editing? (see comments in configure.in for more details) */
#define VI 1
/* Include job control? (see comments in configure.in for more details) */
#define JOBS 1
/* Include brace-expansion? (see comments in configure.in for more details) */
#define BRACE_EXPAND 1
/* Include any history? (see comments in configure.in for more details) */
#define HISTORY 1
/* Include complex history? (see comments in configure.in for more details) */
#define COMPLEX_HISTORY
/* Strict POSIX behaviour? (see comments in configure.in for more details) */
/* #undef POSIXLY_CORRECT */
/* Specify default $ENV? (see comments in configure.in for more details) */
/* #undef DEFAULT_ENV */
/* Include shl(1) support? (see comments in configure.in for more details) */
/* #undef SWTCH */
/* Include game-of-life? (see comments in configure.in for more details) */
/* #undef SILLY */
/* The number of bytes in a int. */
#define SIZEOF_INT 4
/* The number of bytes in a long. */
#if defined(alpha)
#define SIZEOF_LONG 8
#else
#define SIZEOF_LONG 4
#endif
/* Define if you have the _setjmp function. */
/* #undef HAVE__SETJMP */
/* Define if you have the confstr function. */
#define HAVE_CONFSTR 1
/* Define if you have the flock function. */
#define HAVE_FLOCK 1
/* Define if you have the getcwd function. */
#define HAVE_GETCWD 1
/* Define if you have the getgroups function. */
/* #undef HAVE_GETGROUPS */
/* Define if you have the getpagesize function. */
#define HAVE_GETPAGESIZE 1
/* Define if you have the getrusage function. */
/* #undef HAVE_GETRUSAGE */
/* Define if you have the getwd function. */
#define HAVE_GETWD 1
/* Define if you have the killpg function. */
#define HAVE_KILLPG 1
/* Define if you have the nice function. */
#define HAVE_NICE 1
/* Define if you have the setrlimit function. */
#define HAVE_SETRLIMIT 1
/* Define if you have the sigsetjmp function. */
#define HAVE_SIGSETJMP 1
/* Define if you have the strcasecmp function. */
#define HAVE_STRCASECMP 1
/* Define if you have the strerror function. */
#define HAVE_STRERROR 1
/* Define if you have the strstr function. */
#define HAVE_STRSTR 1
/* Define if you have the sysconf function. */
#define HAVE_SYSCONF 1
/* Define if you have the tcsetpgrp function. */
#define HAVE_TCSETPGRP 1
/* Define if you have the ulimit function. */
/* #undef HAVE_ULIMIT */
/* Define if you have the valloc function. */
#define HAVE_VALLOC 1
/* Define if you have the wait3 function. */
#define HAVE_WAIT3 1
/* Define if you have the waitpid function. */
#define HAVE_WAITPID 1
/* Define if you have the <dirent.h> header file. */
#define HAVE_DIRENT_H 1
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define if you have the <ndir.h> header file. */
/* #undef HAVE_NDIR_H */
/* Define if you have the <paths.h> header file. */
#define HAVE_PATHS_H 1
/* Define if you have the <stddef.h> header file. */
#define HAVE_STDDEF_H 1
/* Define if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define if you have the <sys/dir.h> header file. */
/* #undef HAVE_SYS_DIR_H */
/* Define if you have the <sys/ndir.h> header file. */
/* #undef HAVE_SYS_NDIR_H */
/* Define if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
/* Define if you have the <sys/resource.h> header file. */
#define HAVE_SYS_RESOURCE_H 1
/* Define if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define if you have the <sys/wait.h> header file. */
#define HAVE_SYS_WAIT_H 1
/* Define if you have the <ulimit.h> header file. */
/* #undef HAVE_ULIMIT_H */
/* Define if you have the <values.h> header file. */
/* #undef HAVE_VALUES_H */
/* Need to use a separate file to keep the configure script from commenting
* out the undefs....
*/
#include "conf-end.h"
#endif /* CONFIG_H */

1081
edit.c Normal file

File diff suppressed because it is too large Load Diff

87
edit.h Normal file
View File

@ -0,0 +1,87 @@
/* $OpenBSD: edit.h,v 1.3 1999/11/14 22:04:02 d Exp $ */
/* NAME:
* edit.h - globals for edit modes
*
* DESCRIPTION:
* This header defines various global edit objects.
*
* SEE ALSO:
*
*
* RCSid:
* $From: edit.h,v 1.2 1994/05/19 18:32:40 michael Exp michael $
*
*/
/* some useful #defines */
#ifdef EXTERN
# define I__(i) = i
#else
# define I__(i)
# define EXTERN extern
# define EXTERN_DEFINED
#endif
#define BEL 0x07
/* tty driver characters we are interested in */
typedef struct {
int erase;
int kill;
int werase;
int intr;
int quit;
int eof;
} X_chars;
EXTERN X_chars edchars;
/* x_fc_glob() flags */
#define XCF_COMMAND BIT(0) /* Do command completion */
#define XCF_FILE BIT(1) /* Do file completion */
#define XCF_FULLPATH BIT(2) /* command completion: store full path */
#define XCF_COMMAND_FILE (XCF_COMMAND|XCF_FILE)
/* edit.c */
int x_getc ARGS((void));
void x_flush ARGS((void));
void x_putc ARGS((int c));
void x_puts ARGS((const char *s));
bool_t x_mode ARGS((bool_t onoff));
int promptlen ARGS((const char *cp, const char **spp));
int x_do_comment ARGS((char *buf, int bsize, int *lenp));
void x_print_expansions ARGS((int nwords, char *const *words, int is_command));
int x_cf_glob ARGS((int flags, const char *buf, int buflen, int pos, int *startp,
int *endp, char ***wordsp, int *is_commandp));
int x_longest_prefix ARGS((int nwords, char *const *words));
int x_basename ARGS((const char *s, const char *se));
void x_free_words ARGS((int nwords, char **words));
int x_escape ARGS((const char *, size_t, int (*)(const char *s, size_t len)));
/* emacs.c */
int x_emacs ARGS((char *buf, size_t len));
void x_init_emacs ARGS((void));
void x_emacs_keys ARGS((X_chars *ec));
/* vi.c */
int x_vi ARGS((char *buf, size_t len));
#ifdef DEBUG
# define D__(x) x
#else
# define D__(x)
#endif
/* This lot goes at the END */
/* be sure not to interfere with anyone else's idea about EXTERN */
#ifdef EXTERN_DEFINED
# undef EXTERN_DEFINED
# undef EXTERN
#endif
#undef I__
/*
* Local Variables:
* version-control:t
* comment-column:40
* End:
*/

44
emacs-gen.sh Normal file
View File

@ -0,0 +1,44 @@
#!/bin/sh
# $OpenBSD: emacs-gen.sh,v 1.1.1.1 1996/08/14 06:19:10 downsj Exp $
case $# in
1) file=$1;;
*)
echo "$0: Usage: $0 path-to-emacs.c" 1>&2
exit 1
esac;
if [ ! -r "$file" ] ;then
echo "$0: can't read $file" 1>&2
exit 1
fi
cat << E_O_F || exit 1
/*
* NOTE: THIS FILE WAS GENERATED AUTOMATICALLY FROM $file
*
* DO NOT BOTHER EDITING THIS FILE
*/
E_O_F
# Pass 1: print out lines before @START-FUNC-TAB@
# and generate defines and function declarations,
sed -e '1,/@START-FUNC-TAB@/d' -e '/@END-FUNC-TAB@/,$d' < $file |
awk 'BEGIN { nfunc = 0; }
/^[ ]*#/ {
print $0;
next;
}
{
fname = $2;
c = substr(fname, length(fname), 1);
if (c == ",")
fname = substr(fname, 1, length(fname) - 1);
if (fname != "0") {
printf "#define XFUNC_%s %d\n", substr(fname, 3, length(fname) - 2), nfunc;
printf "static int %s ARGS((int c));\n", fname;
nfunc++;
}
}' || exit 1
exit 0

2199
emacs.c Normal file

File diff suppressed because it is too large Load Diff

1378
eval.c Normal file

File diff suppressed because it is too large Load Diff

1729
exec.c Normal file

File diff suppressed because it is too large Load Diff

107
expand.h Normal file
View File

@ -0,0 +1,107 @@
/* $OpenBSD: expand.h,v 1.3 2001/03/26 16:19:45 todd Exp $ */
/*
* Expanding strings
*/
#define X_EXTRA 8 /* this many extra bytes in X string */
#if 0 /* Usage */
XString xs;
char *xp;
Xinit(xs, xp, 128, ATEMP); /* allocate initial string */
while ((c = generate()) {
Xcheck(xs, xp); /* expand string if necessary */
Xput(xs, xp, c); /* add character */
}
return Xclose(xs, xp); /* resize string */
/*
* NOTE:
* The Xcheck and Xinit macros have a magic + X_EXTRA in the lengths.
* This is so that you can put up to X_EXTRA characters in a XString
* before calling Xcheck. (See yylex in lex.c)
*/
#endif /* 0 */
typedef struct XString {
char *end, *beg; /* end, begin of string */
size_t len; /* length */
Area *areap; /* area to allocate/free from */
} XString;
typedef char * XStringP;
/* initialize expandable string */
#define Xinit(xs, xp, length, area) do { \
(xs).len = length; \
(xs).areap = (area); \
(xs).beg = alloc((xs).len + X_EXTRA, (xs).areap); \
(xs).end = (xs).beg + (xs).len; \
xp = (xs).beg; \
} while (0)
/* stuff char into string */
#define Xput(xs, xp, c) (*xp++ = (c))
/* check if there are at least n bytes left */
#define XcheckN(xs, xp, n) do { \
int more = ((xp) + (n)) - (xs).end; \
if (more > 0) \
xp = Xcheck_grow_(&xs, xp, more); \
} while (0)
/* check for overflow, expand string */
#define Xcheck(xs, xp) XcheckN(xs, xp, 1)
/* free string */
#define Xfree(xs, xp) afree((void*) (xs).beg, (xs).areap)
/* close, return string */
#define Xclose(xs, xp) (char*) aresize((void*)(xs).beg, \
(size_t)((xp) - (xs).beg), (xs).areap)
/* begin of string */
#define Xstring(xs, xp) ((xs).beg)
#define Xnleft(xs, xp) ((xs).end - (xp)) /* may be less than 0 */
#define Xlength(xs, xp) ((xp) - (xs).beg)
#define Xsize(xs, xp) ((xs).end - (xs).beg)
#define Xsavepos(xs, xp) ((xp) - (xs).beg)
#define Xrestpos(xs, xp, n) ((xs).beg + (n))
char * Xcheck_grow_ ARGS((XString *xsp, char *xp, int more));
/*
* expandable vector of generic pointers
*/
typedef struct XPtrV {
void **cur; /* next avail pointer */
void **beg, **end; /* begin, end of vector */
} XPtrV;
#define XPinit(x, n) do { \
register void **vp__; \
vp__ = (void**) alloc(sizeofN(void*, n), ATEMP); \
(x).cur = (x).beg = vp__; \
(x).end = vp__ + n; \
} while (0)
#define XPput(x, p) do { \
if ((x).cur >= (x).end) { \
int n = XPsize(x); \
(x).beg = (void**) aresize((void*) (x).beg, \
sizeofN(void*, n*2), ATEMP); \
(x).cur = (x).beg + n; \
(x).end = (x).cur + n; \
} \
*(x).cur++ = (p); \
} while (0)
#define XPptrv(x) ((x).beg)
#define XPsize(x) ((x).cur - (x).beg)
#define XPclose(x) (void**) aresize((void*)(x).beg, \
sizeofN(void*, XPsize(x)), ATEMP)
#define XPfree(x) afree((void*) (x).beg, ATEMP)

607
expr.c Normal file
View File

@ -0,0 +1,607 @@
/* $OpenBSD: expr.c,v 1.8 2003/02/28 09:45:09 jmc Exp $ */
/*
* Korn expression evaluation
*/
/*
* todo: better error handling: if in builtin, should be builtin error, etc.
*/
#include "sh.h"
#include <ctype.h>
/* The order of these enums is constrained by the order of opinfo[] */
enum token {
/* some (long) unary operators */
O_PLUSPLUS = 0, O_MINUSMINUS,
/* binary operators */
O_EQ, O_NE,
/* assignments are assumed to be in range O_ASN .. O_BORASN */
O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
O_LSHIFT, O_RSHIFT,
O_LE, O_GE, O_LT, O_GT,
O_LAND,
O_LOR,
O_TIMES, O_DIV, O_MOD,
O_PLUS, O_MINUS,
O_BAND,
O_BXOR,
O_BOR,
O_TERN,
O_COMMA,
/* things after this aren't used as binary operators */
/* unary that are not also binaries */
O_BNOT, O_LNOT,
/* misc */
OPEN_PAREN, CLOSE_PAREN, CTERN,
/* things that don't appear in the opinfo[] table */
VAR, LIT, END, BAD
};
#define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
enum prec {
P_PRIMARY = 0, /* VAR, LIT, (), ~ ! - + */
P_MULT, /* * / % */
P_ADD, /* + - */
P_SHIFT, /* << >> */
P_RELATION, /* < <= > >= */
P_EQUALITY, /* == != */
P_BAND, /* & */
P_BXOR, /* ^ */
P_BOR, /* | */
P_LAND, /* && */
P_LOR, /* || */
P_TERN, /* ?: */
P_ASSIGN, /* = *= /= %= += -= <<= >>= &= ^= |= */
P_COMMA /* , */
};
#define MAX_PREC P_COMMA
struct opinfo {
char name[4];
int len; /* name length */
enum prec prec; /* precedence: lower is higher */
};
/* Tokens in this table must be ordered so the longest are first
* (eg, += before +). If you change something, change the order
* of enum token too.
*/
static const struct opinfo opinfo[] = {
{ "++", 2, P_PRIMARY }, /* before + */
{ "--", 2, P_PRIMARY }, /* before - */
{ "==", 2, P_EQUALITY }, /* before = */
{ "!=", 2, P_EQUALITY }, /* before ! */
{ "=", 1, P_ASSIGN }, /* keep assigns in a block */
{ "*=", 2, P_ASSIGN },
{ "/=", 2, P_ASSIGN },
{ "%=", 2, P_ASSIGN },
{ "+=", 2, P_ASSIGN },
{ "-=", 2, P_ASSIGN },
{ "<<=", 3, P_ASSIGN },
{ ">>=", 3, P_ASSIGN },
{ "&=", 2, P_ASSIGN },
{ "^=", 2, P_ASSIGN },
{ "|=", 2, P_ASSIGN },
{ "<<", 2, P_SHIFT },
{ ">>", 2, P_SHIFT },
{ "<=", 2, P_RELATION },
{ ">=", 2, P_RELATION },
{ "<", 1, P_RELATION },
{ ">", 1, P_RELATION },
{ "&&", 2, P_LAND },
{ "||", 2, P_LOR },
{ "*", 1, P_MULT },
{ "/", 1, P_MULT },
{ "%", 1, P_MULT },
{ "+", 1, P_ADD },
{ "-", 1, P_ADD },
{ "&", 1, P_BAND },
{ "^", 1, P_BXOR },
{ "|", 1, P_BOR },
{ "?", 1, P_TERN },
{ ",", 1, P_COMMA },
{ "~", 1, P_PRIMARY },
{ "!", 1, P_PRIMARY },
{ "(", 1, P_PRIMARY },
{ ")", 1, P_PRIMARY },
{ ":", 1, P_PRIMARY },
{ "", 0, P_PRIMARY } /* end of table */
};
typedef struct expr_state Expr_state;
struct expr_state {
const char *expression; /* expression being evaluated */
const char *tokp; /* lexical position */
enum token tok; /* token from token() */
int noassign; /* don't do assigns (for ?:,&&,||) */
struct tbl *val; /* value from token() */
struct tbl *evaling; /* variable that is being recursively
* expanded (EXPRINEVAL flag set)
*/
};
enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
ET_LVALUE, ET_RDONLY, ET_STR };
static void evalerr ARGS((Expr_state *es, enum error_type type,
const char *str)) GCC_FUNC_ATTR(noreturn);
static struct tbl *evalexpr ARGS((Expr_state *es, enum prec prec));
static void token ARGS((Expr_state *es));
static struct tbl *do_ppmm ARGS((Expr_state *es, enum token op,
struct tbl *vasn, bool_t is_prefix));
static void assign_check ARGS((Expr_state *es, enum token op,
struct tbl *vasn));
static struct tbl *tempvar ARGS((void));
static struct tbl *intvar ARGS((Expr_state *es, struct tbl *vp));
/*
* parse and evalute expression
*/
int
evaluate(expr, rval, error_ok)
const char *expr;
long *rval;
int error_ok;
{
struct tbl v;
int ret;
v.flag = DEFINED|INTEGER;
v.type = 0;
ret = v_evaluate(&v, expr, error_ok);
*rval = v.val.i;
return ret;
}
/*
* parse and evalute expression, storing result in vp.
*/
int
v_evaluate(vp, expr, error_ok)
struct tbl *vp;
const char *expr;
volatile int error_ok;
{
struct tbl *v;
Expr_state curstate;
Expr_state * const es = &curstate;
int i;
/* save state to allow recursive calls */
curstate.expression = curstate.tokp = expr;
curstate.noassign = 0;
curstate.evaling = (struct tbl *) 0;
newenv(E_ERRH);
i = ksh_sigsetjmp(e->jbuf, 0);
if (i) {
/* Clear EXPRINEVAL in of any variables we were playing with */
if (curstate.evaling)
curstate.evaling->flag &= ~EXPRINEVAL;
quitenv();
if (i == LAEXPR) {
if (error_ok == KSH_RETURN_ERROR)
return 0;
errorf(null);
}
unwind(i);
/*NOTREACHED*/
}
token(es);
#if 1 /* ifdef-out to disallow empty expressions to be treated as 0 */
if (es->tok == END) {
es->tok = LIT;
es->val = tempvar();
}
#endif /* 0 */
v = intvar(es, evalexpr(es, MAX_PREC));
if (es->tok != END)
evalerr(es, ET_UNEXPECTED, (char *) 0);
if (vp->flag & INTEGER)
setint_v(vp, v);
else
/* can fail if readony */
setstr(vp, str_val(v), error_ok);
quitenv();
return 1;
}
static void
evalerr(es, type, str)
Expr_state *es;
enum error_type type;
const char *str;
{
char tbuf[2];
const char *s;
switch (type) {
case ET_UNEXPECTED:
switch (es->tok) {
case VAR:
s = es->val->name;
break;
case LIT:
s = str_val(es->val);
break;
case END:
s = "end of expression";
break;
case BAD:
tbuf[0] = *es->tokp;
tbuf[1] = '\0';
s = tbuf;
break;
default:
s = opinfo[(int)es->tok].name;
}
warningf(TRUE, "%s: unexpected `%s'", es->expression, s);
break;
case ET_BADLIT:
warningf(TRUE, "%s: bad number `%s'", es->expression, str);
break;
case ET_RECURSIVE:
warningf(TRUE, "%s: expression recurses on parameter `%s'",
es->expression, str);
break;
case ET_LVALUE:
warningf(TRUE, "%s: %s requires lvalue",
es->expression, str);
break;
case ET_RDONLY:
warningf(TRUE, "%s: %s applied to read only variable",
es->expression, str);
break;
default: /* keep gcc happy */
case ET_STR:
warningf(TRUE, "%s: %s", es->expression, str);
break;
}
unwind(LAEXPR);
}
static struct tbl *
evalexpr(es, prec)
Expr_state *es;
enum prec prec;
{
struct tbl *vl, UNINITIALIZED(*vr), *vasn;
enum token op;
long UNINITIALIZED(res);
if (prec == P_PRIMARY) {
op = es->tok;
if (op == O_BNOT || op == O_LNOT || op == O_MINUS
|| op == O_PLUS)
{
token(es);
vl = intvar(es, evalexpr(es, P_PRIMARY));
if (op == O_BNOT)
vl->val.i = ~vl->val.i;
else if (op == O_LNOT)
vl->val.i = !vl->val.i;
else if (op == O_MINUS)
vl->val.i = -vl->val.i;
/* op == O_PLUS is a no-op */
} else if (op == OPEN_PAREN) {
token(es);
vl = evalexpr(es, MAX_PREC);
if (es->tok != CLOSE_PAREN)
evalerr(es, ET_STR, "missing )");
token(es);
} else if (op == O_PLUSPLUS || op == O_MINUSMINUS) {
token(es);
vl = do_ppmm(es, op, es->val, TRUE);
token(es);
} else if (op == VAR || op == LIT) {
vl = es->val;
token(es);
} else {
evalerr(es, ET_UNEXPECTED, (char *) 0);
/*NOTREACHED*/
}
if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
vl = do_ppmm(es, es->tok, vl, FALSE);
token(es);
}
return vl;
}
vl = evalexpr(es, ((int) prec) - 1);
for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec;
op = es->tok)
{
token(es);
vasn = vl;
if (op != O_ASN) /* vl may not have a value yet */
vl = intvar(es, vl);
if (IS_ASSIGNOP(op)) {
assign_check(es, op, vasn);
vr = intvar(es, evalexpr(es, P_ASSIGN));
} else if (op != O_TERN && op != O_LAND && op != O_LOR)
vr = intvar(es, evalexpr(es, ((int) prec) - 1));
if ((op == O_DIV || op == O_MOD || op == O_DIVASN
|| op == O_MODASN) && vr->val.i == 0)
{
if (es->noassign)
vr->val.i = 1;
else
evalerr(es, ET_STR, "zero divisor");
}
switch ((int) op) {
case O_TIMES:
case O_TIMESASN:
res = vl->val.i * vr->val.i;
break;
case O_DIV:
case O_DIVASN:
res = vl->val.i / vr->val.i;
break;
case O_MOD:
case O_MODASN:
res = vl->val.i % vr->val.i;
break;
case O_PLUS:
case O_PLUSASN:
res = vl->val.i + vr->val.i;
break;
case O_MINUS:
case O_MINUSASN:
res = vl->val.i - vr->val.i;
break;
case O_LSHIFT:
case O_LSHIFTASN:
res = vl->val.i << vr->val.i;
break;
case O_RSHIFT:
case O_RSHIFTASN:
res = vl->val.i >> vr->val.i;
break;
case O_LT:
res = vl->val.i < vr->val.i;
break;
case O_LE:
res = vl->val.i <= vr->val.i;
break;
case O_GT:
res = vl->val.i > vr->val.i;
break;
case O_GE:
res = vl->val.i >= vr->val.i;
break;
case O_EQ:
res = vl->val.i == vr->val.i;
break;
case O_NE:
res = vl->val.i != vr->val.i;
break;
case O_BAND:
case O_BANDASN:
res = vl->val.i & vr->val.i;
break;
case O_BXOR:
case O_BXORASN:
res = vl->val.i ^ vr->val.i;
break;
case O_BOR:
case O_BORASN:
res = vl->val.i | vr->val.i;
break;
case O_LAND:
if (!vl->val.i)
es->noassign++;
vr = intvar(es, evalexpr(es, ((int) prec) - 1));
res = vl->val.i && vr->val.i;
if (!vl->val.i)
es->noassign--;
break;
case O_LOR:
if (vl->val.i)
es->noassign++;
vr = intvar(es, evalexpr(es, ((int) prec) - 1));
res = vl->val.i || vr->val.i;
if (vl->val.i)
es->noassign--;
break;
case O_TERN:
{
int e = vl->val.i != 0;
if (!e)
es->noassign++;
vl = evalexpr(es, MAX_PREC);
if (!e)
es->noassign--;
if (es->tok != CTERN)
evalerr(es, ET_STR, "missing :");
token(es);
if (e)
es->noassign++;
vr = evalexpr(es, P_TERN);
if (e)
es->noassign--;
vl = e ? vl : vr;
}
break;
case O_ASN:
res = vr->val.i;
break;
case O_COMMA:
res = vr->val.i;
break;
}
if (IS_ASSIGNOP(op)) {
vr->val.i = res;
if (vasn->flag & INTEGER)
setint_v(vasn, vr);
else
setint(vasn, res);
vl = vr;
} else if (op != O_TERN)
vl->val.i = res;
}
return vl;
}
static void
token(es)
Expr_state *es;
{
const char *cp;
int c;
char *tvar;
/* skip white space */
for (cp = es->tokp; (c = *cp), isspace(c); cp++)
;
es->tokp = cp;
if (c == '\0')
es->tok = END;
else if (letter(c)) {
for (; letnum(c); c = *cp)
cp++;
if (c == '[') {
int len;
len = array_ref_len(cp);
if (len == 0)
evalerr(es, ET_STR, "missing ]");
cp += len;
}
#ifdef KSH
else if (c == '(' /*)*/ ) {
/* todo: add math functions (all take single argument):
* abs acos asin atan cos cosh exp int log sin sinh sqrt
* tan tanh
*/
;
}
#endif /* KSH */
if (es->noassign) {
es->val = tempvar();
es->val->flag |= EXPRLVALUE;
} else {
tvar = str_nsave(es->tokp, cp - es->tokp, ATEMP);
es->val = global(tvar);
afree(tvar, ATEMP);
}
es->tok = VAR;
} else if (digit(c)) {
for (; c != '_' && (letnum(c) || c == '#'); c = *cp++)
;
tvar = str_nsave(es->tokp, --cp - es->tokp, ATEMP);
es->val = tempvar();
es->val->flag &= ~INTEGER;
es->val->type = 0;
es->val->val.s = tvar;
if (setint_v(es->val, es->val) == NULL)
evalerr(es, ET_BADLIT, tvar);
afree(tvar, ATEMP);
es->tok = LIT;
} else {
int i, n0;
for (i = 0; (n0 = opinfo[i].name[0]); i++)
if (c == n0
&& strncmp(cp, opinfo[i].name, opinfo[i].len) == 0)
{
es->tok = (enum token) i;
cp += opinfo[i].len;
break;
}
if (!n0)
es->tok = BAD;
}
es->tokp = cp;
}
/* Do a ++ or -- operation */
static struct tbl *
do_ppmm(es, op, vasn, is_prefix)
Expr_state *es;
enum token op;
struct tbl *vasn;
bool_t is_prefix;
{
struct tbl *vl;
int oval;
assign_check(es, op, vasn);
vl = intvar(es, vasn);
oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--;
if (vasn->flag & INTEGER)
setint_v(vasn, vl);
else
setint(vasn, vl->val.i);
if (!is_prefix) /* undo the inc/dec */
vl->val.i = oval;
return vl;
}
static void
assign_check(es, op, vasn)
Expr_state *es;
enum token op;
struct tbl *vasn;
{
if (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE))
evalerr(es, ET_LVALUE, opinfo[(int) op].name);
else if (vasn->flag & RDONLY)
evalerr(es, ET_RDONLY, opinfo[(int) op].name);
}
static struct tbl *
tempvar()
{
register struct tbl *vp;
vp = (struct tbl*) alloc(sizeof(struct tbl), ATEMP);
vp->flag = ISSET|INTEGER;
vp->type = 0;
vp->areap = ATEMP;
vp->val.i = 0;
vp->name[0] = '\0';
return vp;
}
/* cast (string) variable to temporary integer variable */
static struct tbl *
intvar(es, vp)
Expr_state *es;
struct tbl *vp;
{
struct tbl *vq;
/* try to avoid replacing a temp var with another temp var */
if (vp->name[0] == '\0'
&& (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
return vp;
vq = tempvar();
if (setint_v(vq, vp) == NULL) {
if (vp->flag & EXPRINEVAL)
evalerr(es, ET_RECURSIVE, vp->name);
es->evaling = vp;
vp->flag |= EXPRINEVAL;
v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR);
vp->flag &= ~EXPRINEVAL;
es->evaling = (struct tbl *) 0;
}
return vq;
}

1192
history.c Normal file

File diff suppressed because it is too large Load Diff

560
io.c Normal file
View File

@ -0,0 +1,560 @@
/* $OpenBSD: io.c,v 1.12 2003/03/10 03:48:16 david Exp $ */
/*
* shell buffered IO and formatted output
*/
#include <ctype.h>
#include "sh.h"
#include "ksh_stat.h"
static int initio_done;
/*
* formatted output functions
*/
/* A shell error occurred (eg, syntax error, etc.) */
void
#ifdef HAVE_PROTOTYPES
errorf(const char *fmt, ...)
#else
errorf(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list va;
shl_stdout_ok = 0; /* debugging: note that stdout not valid */
exstat = 1;
if (*fmt) {
error_prefix(TRUE);
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_putchar('\n', shl_out);
}
shf_flush(shl_out);
unwind(LERROR);
}
/* like errorf(), but no unwind is done */
void
#ifdef HAVE_PROTOTYPES
warningf(int fileline, const char *fmt, ...)
#else
warningf(fileline, fmt, va_alist)
int fileline;
const char *fmt;
va_dcl
#endif
{
va_list va;
error_prefix(fileline);
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_putchar('\n', shl_out);
shf_flush(shl_out);
}
/* Used by built-in utilities to prefix shell and utility name to message
* (also unwinds environments for special builtins).
*/
void
#ifdef HAVE_PROTOTYPES
bi_errorf(const char *fmt, ...)
#else
bi_errorf(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list va;
shl_stdout_ok = 0; /* debugging: note that stdout not valid */
exstat = 1;
if (*fmt) {
error_prefix(TRUE);
/* not set when main() calls parse_args() */
if (builtin_argv0)
shf_fprintf(shl_out, "%s: ", builtin_argv0);
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_putchar('\n', shl_out);
}
shf_flush(shl_out);
/* POSIX special builtins and ksh special builtins cause
* non-interactive shells to exit.
* XXX odd use of KEEPASN; also may not want LERROR here
*/
if ((builtin_flag & SPEC_BI)
|| (Flag(FPOSIX) && (builtin_flag & KEEPASN)))
{
builtin_argv0 = (char *) 0;
unwind(LERROR);
}
}
/* Called when something that shouldn't happen does */
void
#ifdef HAVE_PROTOTYPES
internal_errorf(int jump, const char *fmt, ...)
#else
internal_errorf(jump, fmt, va_alist)
int jump;
const char *fmt;
va_dcl
#endif
{
va_list va;
error_prefix(TRUE);
shf_fprintf(shl_out, "internal error: ");
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_putchar('\n', shl_out);
shf_flush(shl_out);
if (jump)
unwind(LERROR);
}
/* used by error reporting functions to print "ksh: .kshrc[25]: " */
void
error_prefix(fileline)
int fileline;
{
/* Avoid foo: foo[2]: ... */
if (!fileline || !source || !source->file
|| strcmp(source->file, kshname) != 0)
shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
if (fileline && source && source->file != NULL) {
shf_fprintf(shl_out, "%s[%d]: ", source->file,
source->errline > 0 ? source->errline : source->line);
source->errline = 0;
}
}
/* printf to shl_out (stderr) with flush */
void
#ifdef HAVE_PROTOTYPES
shellf(const char *fmt, ...)
#else
shellf(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list va;
if (!initio_done) /* shl_out may not be set up yet... */
return;
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_flush(shl_out);
}
/* printf to shl_stdout (stdout) */
void
#ifdef HAVE_PROTOTYPES
shprintf(const char *fmt, ...)
#else
shprintf(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list va;
if (!shl_stdout_ok)
internal_errorf(1, "shl_stdout not valid");
SH_VA_START(va, fmt);
shf_vfprintf(shl_stdout, fmt, va);
va_end(va);
}
#ifdef KSH_DEBUG
static struct shf *kshdebug_shf;
void
kshdebug_init_()
{
if (kshdebug_shf)
shf_close(kshdebug_shf);
kshdebug_shf = shf_open("/tmp/ksh-debug.log",
O_WRONLY|O_APPEND|O_CREAT, 0600,
SHF_WR|SHF_MAPHI);
if (kshdebug_shf) {
shf_fprintf(kshdebug_shf, "\nNew shell[pid %d]\n", getpid());
shf_flush(kshdebug_shf);
}
}
/* print to debugging log */
void
# ifdef HAVE_PROTOTYPES
kshdebug_printf_(const char *fmt, ...)
# else
kshdebug_printf_(fmt, va_alist)
const char *fmt;
va_dcl
# endif
{
va_list va;
if (!kshdebug_shf)
return;
SH_VA_START(va, fmt);
shf_fprintf(kshdebug_shf, "[%d] ", getpid());
shf_vfprintf(kshdebug_shf, fmt, va);
va_end(va);
shf_flush(kshdebug_shf);
}
void
kshdebug_dump_(str, mem, nbytes)
const char *str;
const void *mem;
int nbytes;
{
int i, j;
int nprow = 16;
if (!kshdebug_shf)
return;
shf_fprintf(kshdebug_shf, "[%d] %s:\n", getpid(), str);
for (i = 0; i < nbytes; i += nprow) {
char c = '\t';
for (j = 0; j < nprow && i + j < nbytes; j++) {
shf_fprintf(kshdebug_shf, "%c%02x",
c, ((const unsigned char *) mem)[i + j]);
c = ' ';
}
shf_fprintf(kshdebug_shf, "\n");
}
shf_flush(kshdebug_shf);
}
#endif /* KSH_DEBUG */
/* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */
int
can_seek(fd)
int fd;
{
struct stat statb;
return fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ?
SHF_UNBUF : 0;
}
struct shf shf_iob[3];
void
initio()
{
shf_fdopen(1, SHF_WR, shl_stdout); /* force buffer allocation */
shf_fdopen(2, SHF_WR, shl_out);
shf_fdopen(2, SHF_WR, shl_spare); /* force buffer allocation */
initio_done = 1;
kshdebug_init();
}
/* A dup2() with error checking */
int
ksh_dup2(ofd, nfd, errok)
int ofd;
int nfd;
int errok;
{
int ret = dup2(ofd, nfd);
if (ret < 0 && errno != EBADF && !errok)
errorf("too many files open in shell");
#ifdef DUP2_BROKEN
/* Ultrix systems like to preserve the close-on-exec flag */
if (ret >= 0)
(void) fcntl(nfd, F_SETFD, 0);
#endif /* DUP2_BROKEN */
return ret;
}
/*
* move fd from user space (0<=fd<10) to shell space (fd>=10),
* set close-on-exec flag.
*/
int
savefd(fd, noclose)
int fd;
int noclose;
{
int nfd;
if (fd < FDBASE) {
nfd = ksh_dupbase(fd, FDBASE);
if (nfd < 0) {
if (errno == EBADF)
return -1;
else
errorf("too many files open in shell");
}
if (!noclose)
close(fd);
} else
nfd = fd;
fd_clexec(nfd);
return nfd;
}
void
restfd(fd, ofd)
int fd, ofd;
{
if (fd == 2)
shf_flush(&shf_iob[fd]);
if (ofd < 0) /* original fd closed */
close(fd);
else {
ksh_dup2(ofd, fd, TRUE); /* XXX: what to do if this fails? */
close(ofd);
}
}
void
openpipe(pv)
register int *pv;
{
if (pipe(pv) < 0)
errorf("can't create pipe - try again");
pv[0] = savefd(pv[0], 0);
pv[1] = savefd(pv[1], 0);
}
void
closepipe(pv)
register int *pv;
{
close(pv[0]);
close(pv[1]);
}
/* Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn
* a string (the X in 2>&X, read -uX, print -uX) into a file descriptor.
*/
int
check_fd(name, mode, emsgp)
char *name;
int mode;
const char **emsgp;
{
int fd, fl;
if (isdigit(name[0]) && !name[1]) {
fd = name[0] - '0';
if ((fl = fcntl(fd = name[0] - '0', F_GETFL, 0)) < 0) {
if (emsgp)
*emsgp = "bad file descriptor";
return -1;
}
fl &= O_ACCMODE;
#ifdef OS2
if (mode == W_OK ) {
if (setmode(fd, O_TEXT) == -1) {
if (emsgp)
*emsgp = "couldn't set write mode";
return -1;
}
} else if (mode == R_OK)
if (setmode(fd, O_BINARY) == -1) {
if (emsgp)
*emsgp = "couldn't set read mode";
return -1;
}
#else /* OS2 */
/* X_OK is a kludge to disable this check for dups (x<&1):
* historical shells never did this check (XXX don't know what
* posix has to say).
*/
if (!(mode & X_OK) && fl != O_RDWR
&& (((mode & R_OK) && fl != O_RDONLY)
|| ((mode & W_OK) && fl != O_WRONLY)))
{
if (emsgp)
*emsgp = (fl == O_WRONLY) ?
"fd not open for reading"
: "fd not open for writing";
return -1;
}
#endif /* OS2 */
return fd;
}
#ifdef KSH
else if (name[0] == 'p' && !name[1])
return coproc_getfd(mode, emsgp);
#endif /* KSH */
if (emsgp)
*emsgp = "illegal file descriptor name";
return -1;
}
#ifdef KSH
/* Called once from main */
void
coproc_init()
{
coproc.read = coproc.readw = coproc.write = -1;
coproc.njobs = 0;
coproc.id = 0;
}
/* Called by c_read() when eof is read - close fd if it is the co-process fd */
void
coproc_read_close(fd)
int fd;
{
if (coproc.read >= 0 && fd == coproc.read) {
coproc_readw_close(fd);
close(coproc.read);
coproc.read = -1;
}
}
/* Called by c_read() and by iosetup() to close the other side of the
* read pipe, so reads will actually terminate.
*/
void
coproc_readw_close(fd)
int fd;
{
if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) {
close(coproc.readw);
coproc.readw = -1;
}
}
/* Called by c_print when a write to a fd fails with EPIPE and by iosetup
* when co-process input is dup'd
*/
void
coproc_write_close(fd)
int fd;
{
if (coproc.write >= 0 && fd == coproc.write) {
close(coproc.write);
coproc.write = -1;
}
}
/* Called to check for existence of/value of the co-process file descriptor.
* (Used by check_fd() and by c_read/c_print to deal with -p option).
*/
int
coproc_getfd(mode, emsgp)
int mode;
const char **emsgp;
{
int fd = (mode & R_OK) ? coproc.read : coproc.write;
if (fd >= 0)
return fd;
if (emsgp)
*emsgp = "no coprocess";
return -1;
}
/* called to close file descriptors related to the coprocess (if any)
* Should be called with SIGCHLD blocked.
*/
void
coproc_cleanup(reuse)
int reuse;
{
/* This to allow co-processes to share output pipe */
if (!reuse || coproc.readw < 0 || coproc.read < 0) {
if (coproc.read >= 0) {
close(coproc.read);
coproc.read = -1;
}
if (coproc.readw >= 0) {
close(coproc.readw);
coproc.readw = -1;
}
}
if (coproc.write >= 0) {
close(coproc.write);
coproc.write = -1;
}
}
#endif /* KSH */
/*
* temporary files
*/
struct temp *
maketemp(ap, type, tlist)
Area *ap;
Temp_type type;
struct temp **tlist;
{
static unsigned int inc;
struct temp *tp;
int len;
int fd;
char *path;
const char *dir;
dir = tmpdir ? tmpdir : "/tmp";
/* The 20 + 20 is a paranoid worst case for pid/inc */
len = strlen(dir) + 3 + 20 + 20 + 1;
tp = (struct temp *) alloc(sizeof(struct temp) + len, ap);
tp->name = path = (char *) &tp[1];
tp->shf = (struct shf *) 0;
tp->type = type;
#ifdef __OpenBSD__
shf_snprintf(path, len, "%s/shXXXXXXXX", dir);
fd = mkstemp(path);
if (fd >= 0)
tp->shf = shf_fdopen(fd, SHF_WR, (struct shf *) 0);
#else
while (1) {
/* Note that temp files need to fit 8.3 DOS limits */
shf_snprintf(path, len, "%s/sh%05u.%03x",
dir, (unsigned) procpid, inc++);
/* Mode 0600 to be paranoid, O_TRUNC in case O_EXCL isn't
* really there.
*/
fd = open(path, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0600);
if (fd >= 0) {
tp->shf = shf_fdopen(fd, SHF_WR, (struct shf *) 0);
break;
}
if (errno != EINTR
#ifdef EEXIST
&& errno != EEXIST
#endif /* EEXIST */
#ifdef EISDIR
&& errno != EISDIR
#endif /* EISDIR */
)
/* Error must be printed by caller: don't know here if
* errorf() or bi_errorf() should be used.
*/
break;
}
#endif /* __OpenBSD__ */
tp->pid = procpid;
tp->next = *tlist;
*tlist = tp;
return tp;
}

1851
jobs.c Normal file

File diff suppressed because it is too large Load Diff

5164
ksh.1tbl Normal file

File diff suppressed because it is too large Load Diff

26
ksh_dir.h Normal file
View File

@ -0,0 +1,26 @@
/* $OpenBSD: ksh_dir.h,v 1.1.1.1 1996/08/14 06:19:11 downsj Exp $ */
/* Wrapper around the ugly dir includes/ifdefs */
#if defined(HAVE_DIRENT_H)
# include <dirent.h>
# define NLENGTH(dirent) (strlen(dirent->d_name))
#else
# define dirent direct
# define NLENGTH(dirent) (dirent->d_namlen)
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif /* HAVE_SYS_NDIR_H */
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif /* HAVE_SYSDIR_H */
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif /* HAVE_NDIR_H */
#endif /* HAVE_DIRENT_H */
#ifdef OPENDIR_DOES_NONDIR
extern DIR *ksh_opendir ARGS((const char *d));
#else /* OPENDIR_DOES_NONDIR */
# define ksh_opendir(d) opendir(d)
#endif /* OPENDIR_DOES_NONDIR */

24
ksh_limval.h Normal file
View File

@ -0,0 +1,24 @@
/* $OpenBSD: ksh_limval.h,v 1.1.1.1 1996/08/14 06:19:11 downsj Exp $ */
/* Wrapper around the values.h/limits.h includes/ifdefs */
#ifdef HAVE_VALUES_H
# include <values.h>
#endif /* HAVE_VALUES_H */
/* limits.h is included in sh.h */
#ifndef DMAXEXP
# define DMAXEXP 128 /* should be big enough */
#endif
#ifndef BITSPERBYTE
# ifdef CHAR_BIT
# define BITSPERBYTE CHAR_BIT
# else
# define BITSPERBYTE 8 /* probably true.. */
# endif
#endif
#ifndef BITS
# define BITS(t) (BITSPERBYTE * sizeof(t))
#endif

59
ksh_stat.h Normal file
View File

@ -0,0 +1,59 @@
/* $OpenBSD: ksh_stat.h,v 1.3 1996/10/01 02:05:39 downsj Exp $ */
/* Wrapper around the ugly sys/stat includes/ifdefs */
/* assumes <sys/types.h> already included */
#include <sys/stat.h>
#ifndef HAVE_LSTAT
# define lstat(path, buf) stat(path, buf)
#endif /* HAVE_LSTAT */
#ifdef STAT_MACROS_BROKEN
# undef S_ISREG
# undef S_ISDIR
# undef S_ISCHR
# undef S_ISBLK
# undef S_ISFIFO
# undef S_ISSOCK
# undef S_ISLNK
#endif /* STAT_MACROS_BROKEN */
#if !defined(S_ISREG) && defined(S_IFREG)
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif /* S_ISREG */
#if !defined(S_ISDIR) && defined(S_IFDIR)
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif /* S_ISDIR */
#if !defined(S_ISCHR) && defined(S_IFCHR)
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#endif /* S_ISCHR */
#if !defined(S_ISBLK) && defined(S_IFBLK)
# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#endif /* S_ISBLK */
#if !defined(S_ISFIFO) && defined(S_IFIFO)
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#endif /* S_ISFIFO */
#if !defined(S_ISLNK) && defined(S_IFLNK)
# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#endif /* S_ISLNK */
#if !defined(S_ISSOCK) && defined(S_IFSOCK)
# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
#endif /* S_ISSOCK */
#if !defined(S_ISCDF) && defined(S_CDF)
# define S_ISCDF(m) (S_ISDIR(m) && ((m) & S_CDF))
#endif /* S_ISSOCK */
#ifndef S_ISVTX
# define S_ISVTX 01000 /* sticky bit */
#endif /* S_ISVTX */
#ifndef S_IXUSR
# define S_IXUSR 00100 /* user execute bit */
#endif /* S_IXUSR */
#ifndef S_IXGRP
# define S_IXGRP 00010 /* user execute bit */
#endif /* S_IXGRP */
#ifndef S_IXOTH
# define S_IXOTH 00001 /* user execute bit */
#endif /* S_IXOTH */

26
ksh_time.h Normal file
View File

@ -0,0 +1,26 @@
/* $OpenBSD: ksh_time.h,v 1.2 1996/10/01 02:05:40 downsj Exp $ */
#ifndef KSH_TIME_H
# define KSH_TIME_H
/* Wrapper around the ugly time.h,sys/time.h includes/ifdefs */
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else /* TIME_WITH_SYS_TIME */
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif /* TIME_WITH_SYS_TIME */
#ifndef TIME_DECLARED
extern time_t time ARGS((time_t *));
#endif
#ifndef CLK_TCK
# define CLK_TCK 60 /* 60HZ */
#endif
#endif /* KSH_TIME_H */

20
ksh_times.h Normal file
View File

@ -0,0 +1,20 @@
/* $OpenBSD: ksh_times.h,v 1.2 1996/10/01 02:05:41 downsj Exp $ */
#ifndef KSH_TIMES_H
# define KSH_TIMES_H
/* Needed for clock_t on some systems (ie, NeXT in non-posix mode) */
#include "ksh_time.h"
#include <sys/times.h>
#ifdef TIMES_BROKEN
extern clock_t ksh_times ARGS((struct tms *));
#else /* TIMES_BROKEN */
# define ksh_times times
#endif /* TIMES_BROKEN */
#ifdef HAVE_TIMES
extern clock_t times ARGS((struct tms *));
#endif /* HAVE_TIMES */
#endif /* KSH_TIMES_H */

51
ksh_wait.h Normal file
View File

@ -0,0 +1,51 @@
/* $OpenBSD: ksh_wait.h,v 1.3 1997/06/19 13:58:43 kstailey Exp $ */
/* Wrapper around the ugly sys/wait includes/ifdefs */
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifndef POSIX_SYS_WAIT
/* Get rid of system macros (which probably use union wait) */
# undef WIFCORED
# undef WIFEXITED
# undef WEXITSTATUS
# undef WIFSIGNALED
# undef WTERMSIG
# undef WIFSTOPPED
# undef WSTOPSIG
#endif /* POSIX_SYS_WAIT */
typedef int WAIT_T;
#ifndef WIFCORED
# define WIFCORED(s) ((s) & 0x80)
#endif
#define WSTATUS(s) (s)
#ifndef WIFEXITED
# define WIFEXITED(s) (((s) & 0xff) == 0)
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(s) (((s) >> 8) & 0xff)
#endif
#ifndef WIFSIGNALED
# define WIFSIGNALED(s) (((s) & 0xff) != 0 && ((s) & 0xff) != 0x7f)
#endif
#ifndef WTERMSIG
# define WTERMSIG(s) ((s) & 0x7f)
#endif
#ifndef WIFSTOPPED
# define WIFSTOPPED(s) (((s) & 0xff) == 0x7f)
#endif
#ifndef WSTOPSIG
# define WSTOPSIG(s) (((s) >> 8) & 0xff)
#endif
#if !defined(HAVE_WAITPID) && defined(HAVE_WAIT3)
/* always used with p == -1 */
# define ksh_waitpid(p, s, o) wait3((s), (o), (struct rusage *) 0)
#else /* !HAVE_WAITPID && HAVE_WAIT3 */
# define ksh_waitpid(p, s, o) waitpid((p), (s), (o))
#endif /* !HAVE_WAITPID && HAVE_WAIT3 */

1398
lex.c Normal file

File diff suppressed because it is too large Load Diff

132
lex.h Normal file
View File

@ -0,0 +1,132 @@
/* $OpenBSD: lex.h,v 1.7 2003/02/28 09:45:09 jmc Exp $ */
/*
* Source input, lexer and parser
*/
/* $From: lex.h,v 1.4 1994/05/31 13:34:34 michael Exp $ */
#define IDENT 64
typedef struct source Source;
struct source {
const char *str; /* input pointer */
int type; /* input type */
const char *start; /* start of current buffer */
union {
char **strv; /* string [] */
struct shf *shf; /* shell file */
struct tbl *tblp; /* alias (SALIAS) */
char *freeme; /* also for SREREAD */
} u;
char ugbuf[2]; /* buffer for ungetsc() (SREREAD) and
* alias (SALIAS) */
int line; /* line number */
int errline; /* line the error occurred on (0 if not set) */
const char *file; /* input file name */
int flags; /* SF_* */
Area *areap;
XString xs; /* input buffer */
Source *next; /* stacked source */
};
/* Source.type values */
#define SEOF 0 /* input EOF */
#define SFILE 1 /* file input */
#define SSTDIN 2 /* read stdin */
#define SSTRING 3 /* string */
#define SWSTR 4 /* string without \n */
#define SWORDS 5 /* string[] */
#define SWORDSEP 6 /* string[] separator */
#define SALIAS 7 /* alias expansion */
#define SREREAD 8 /* read ahead to be re-scanned */
/* Source.flags values */
#define SF_ECHO BIT(0) /* echo input to shlout */
#define SF_ALIAS BIT(1) /* faking space at end of alias */
#define SF_ALIASEND BIT(2) /* faking space at end of alias */
#define SF_TTY BIT(3) /* type == SSTDIN & it is a tty */
/*
* states while lexing word
*/
#define SBASE 0 /* outside any lexical constructs */
#define SWORD 1 /* implicit quoting for substitute() */
#ifdef KSH
#define SLETPAREN 2 /* inside (( )), implicit quoting */
#endif /* KSH */
#define SSQUOTE 3 /* inside '' */
#define SDQUOTE 4 /* inside "" */
#define SBRACE 5 /* inside ${} */
#define SCSPAREN 6 /* inside $() */
#define SBQUOTE 7 /* inside `` */
#define SASPAREN 8 /* inside $(( )) */
#define SHEREDELIM 9 /* parsing <<,<<- delimiter */
#define SHEREDQUOTE 10 /* parsing " in <<,<<- delimiter */
#define SPATTERN 11 /* parsing *(...|...) pattern (*+?@!) */
#define STBRACE 12 /* parsing ${..[#%]..} */
typedef union {
int i;
char *cp;
char **wp;
struct op *o;
struct ioword *iop;
} YYSTYPE;
/* If something is added here, add it to tokentab[] in syn.c as well */
#define LWORD 256
#define LOGAND 257 /* && */
#define LOGOR 258 /* || */
#define BREAK 259 /* ;; */
#define IF 260
#define THEN 261
#define ELSE 262
#define ELIF 263
#define FI 264
#define CASE 265
#define ESAC 266
#define FOR 267
#define SELECT 268
#define WHILE 269
#define UNTIL 270
#define DO 271
#define DONE 272
#define IN 273
#define FUNCTION 274
#define TIME 275
#define REDIR 276
#ifdef KSH
#define MDPAREN 277 /* (( )) */
#endif /* KSH */
#define BANG 278 /* ! */
#define DBRACKET 279 /* [[ .. ]] */
#define COPROC 280 /* |& */
#define YYERRCODE 300
/* flags to yylex */
#define CONTIN BIT(0) /* skip new lines to complete command */
#define ONEWORD BIT(1) /* single word for substitute() */
#define ALIAS BIT(2) /* recognize alias */
#define KEYWORD BIT(3) /* recognize keywords */
#define LETEXPR BIT(4) /* get expression inside (( )) */
#define VARASN BIT(5) /* check for var=word */
#define ARRAYVAR BIT(6) /* parse x[1 & 2] as one word */
#define ESACONLY BIT(7) /* only accept esac keyword */
#define CMDWORD BIT(8) /* parsing simple command (alias related) */
#define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */
#define HERES 10 /* max << in line */
EXTERN Source *source; /* yyparse/yylex source */
EXTERN YYSTYPE yylval; /* result from yylex */
EXTERN struct ioword *heres [HERES], **herep;
EXTERN char ident [IDENT+1];
#ifdef HISTORY
# define HISTORYSIZE 128 /* size of saved history */
EXTERN char **history; /* saved commands */
EXTERN char **histptr; /* last history item */
EXTERN int histsize; /* history size */
#endif /* HISTORY */

205
mail.c Normal file
View File

@ -0,0 +1,205 @@
/* $OpenBSD: mail.c,v 1.9 1999/06/15 01:18:35 millert Exp $ */
/*
* Mailbox checking code by Robert J. Gibson, adapted for PD ksh by
* John R. MacMillan
*/
#include "config.h"
#ifdef KSH
#include "sh.h"
#include "ksh_stat.h"
#include "ksh_time.h"
#define MBMESSAGE "you have mail in $_"
typedef struct mbox {
struct mbox *mb_next; /* next mbox in list */
char *mb_path; /* path to mail file */
char *mb_msg; /* to announce arrival of new mail */
time_t mb_mtime; /* mtime of mail file */
} mbox_t;
/*
* $MAILPATH is a linked list of mboxes. $MAIL is a treated as a
* special case of $MAILPATH, where the list has only one node. The
* same list is used for both since they are exclusive.
*/
static mbox_t *mplist;
static mbox_t mbox;
static time_t mlastchkd; /* when mail was last checked */
static time_t mailcheck_interval;
static void munset ARGS((mbox_t *mlist)); /* free mlist and mval */
static mbox_t * mballoc ARGS((char *p, char *m)); /* allocate a new mbox */
static void mprintit ARGS((mbox_t *mbp));
void
mcheck()
{
register mbox_t *mbp;
time_t now;
struct tbl *vp;
struct stat stbuf;
now = time((time_t *) 0);
if (mlastchkd == 0)
mlastchkd = now;
if (now - mlastchkd >= mailcheck_interval) {
mlastchkd = now;
if (mplist)
mbp = mplist;
else if ((vp = global("MAIL")) && (vp->flag & ISSET))
mbp = &mbox;
else
mbp = NULL;
while (mbp) {
if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0
&& S_ISREG(stbuf.st_mode))
{
if (stbuf.st_size
&& mbp->mb_mtime != stbuf.st_mtime
&& stbuf.st_atime <= stbuf.st_mtime)
mprintit(mbp);
mbp->mb_mtime = stbuf.st_mtime;
} else {
/*
* Some mail readers remove the mail
* file if all mail is read. If file
* does not exist, assume this is the
* case and set mtime to zero.
*/
mbp->mb_mtime = 0;
}
mbp = mbp->mb_next;
}
}
}
void
mcset(interval)
long interval;
{
mailcheck_interval = interval;
}
void
mbset(p)
register char *p;
{
struct stat stbuf;
if (mbox.mb_msg)
afree((void *)mbox.mb_msg, APERM);
if (mbox.mb_path)
afree((void *)mbox.mb_path, APERM);
/* Save a copy to protect from export (which munges the string) */
mbox.mb_path = str_save(p, APERM);
mbox.mb_msg = NULL;
if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
mbox.mb_mtime = stbuf.st_mtime;
else
mbox.mb_mtime = 0;
}
void
mpset(mptoparse)
register char *mptoparse;
{
register mbox_t *mbp;
register char *mpath, *mmsg, *mval;
char *p;
munset( mplist );
mplist = NULL;
mval = str_save(mptoparse, APERM);
while (mval) {
mpath = mval;
if ((mval = strchr(mval, PATHSEP)) != NULL) {
*mval = '\0', mval++;
}
/* POSIX/bourne-shell say file%message */
for (p = mpath; (mmsg = strchr(p, '%')); ) {
/* a literal percent? (POSIXism) */
if (mmsg[-1] == '\\') {
/* use memmove() to avoid overlap problems */
memmove(mmsg - 1, mmsg, strlen(mmsg) + 1);
p = mmsg + 1;
continue;
}
break;
}
/* at&t ksh says file?message */
if (!mmsg && !Flag(FPOSIX))
mmsg = strchr(mpath, '?');
if (mmsg) {
*mmsg = '\0';
mmsg++;
}
mbp = mballoc(mpath, mmsg);
mbp->mb_next = mplist;
mplist = mbp;
}
}
static void
munset(mlist)
register mbox_t *mlist;
{
register mbox_t *mbp;
while (mlist != NULL) {
mbp = mlist;
mlist = mbp->mb_next;
if (!mlist)
afree((void *)mbp->mb_path, APERM);
afree((void *)mbp, APERM);
}
}
static mbox_t *
mballoc(p, m)
char *p;
char *m;
{
struct stat stbuf;
register mbox_t *mbp;
mbp = (mbox_t *)alloc(sizeof(mbox_t), APERM);
mbp->mb_next = NULL;
mbp->mb_path = p;
mbp->mb_msg = m;
if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
mbp->mb_mtime = stbuf.st_mtime;
else
mbp->mb_mtime = 0;
return(mbp);
}
static void
mprintit( mbp )
mbox_t *mbp;
{
struct tbl *vp;
#if 0
/*
* I doubt this $_ overloading is bad in /bin/sh mode. Anyhow, we
* crash as the code looks now if we do not set vp. Now, this is
* easy to fix too, but I'd like to see what POSIX says before doing
* a change like that.
*/
if (!Flag(FSH))
#endif
/* Ignore setstr errors here (arbitrary) */
setstr((vp = local("_", FALSE)), mbp->mb_path, KSH_RETURN_ERROR);
shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0));
unset(vp, 0);
}
#endif /* KSH */

863
main.c Normal file
View File

@ -0,0 +1,863 @@
/* $OpenBSD: main.c,v 1.23 2003/03/10 03:48:16 david Exp $ */
/*
* startup, main loop, environments and error handling
*/
#define EXTERN /* define EXTERNs in sh.h */
#include "sh.h"
#include "ksh_stat.h"
#include "ksh_time.h"
extern char **environ;
/*
* global data
*/
static void reclaim ARGS((void));
static void remove_temps ARGS((struct temp *tp));
static int is_restricted ARGS((char *name));
/*
* shell initialization
*/
static const char initifs[] = "IFS= \t\n";
static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }";
static const char version_param[] =
#ifdef KSH
"KSH_VERSION"
#else /* KSH */
"SH_VERSION"
#endif /* KSH */
;
static const char *const initcoms [] = {
"typeset", "-x", "SHELL", "PATH", "HOME", NULL,
"typeset", "-r", version_param, NULL,
"typeset", "-i", "PPID", NULL,
"typeset", "-i", "OPTIND=1", NULL,
#ifdef KSH
"eval", "typeset -i RANDOM MAILCHECK=\"${MAILCHECK-600}\" SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"", NULL,
#endif /* KSH */
"alias",
/* Standard ksh aliases */
"hash=alias -t", /* not "alias -t --": hash -r needs to work */
"type=whence -v",
#ifdef JOBS
"stop=kill -STOP",
"suspend=kill -STOP $$",
#endif
#ifdef KSH
"autoload=typeset -fu",
"functions=typeset -f",
# ifdef HISTORY
"history=fc -l",
# endif /* HISTORY */
"integer=typeset -i",
"nohup=nohup ",
"local=typeset",
"r=fc -e -",
#endif /* KSH */
#ifdef KSH
/* Aliases that are builtin commands in at&t */
"login=exec login",
#ifndef __OpenBSD__
"newgrp=exec newgrp",
#endif /* __OpenBSD__ */
#endif /* KSH */
NULL,
/* this is what at&t ksh seems to track, with the addition of emacs */
"alias", "-tU",
"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
"mail", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who",
NULL,
#ifdef EXTRA_INITCOMS
EXTRA_INITCOMS, NULL,
#endif /* EXTRA_INITCOMS */
NULL
};
int
main(argc, argv)
int argc;
register char **argv;
{
register int i;
int argi;
Source *s;
struct block *l;
int restricted, errexit;
char **wp;
struct env env;
pid_t ppid;
#ifdef MEM_DEBUG
chmem_set_defaults("ct", 1);
/* chmem_push("+c", 1); */
#endif /* MEM_DEBUG */
#ifdef OS2
setmode (0, O_BINARY);
setmode (1, O_TEXT);
#endif
/* make sure argv[] is sane */
if (!*argv) {
static const char *empty_argv[] = {
"pdksh", (char *) 0
};
argv = (char **) empty_argv;
argc = 1;
}
kshname = *argv;
ainit(&aperm); /* initialize permanent Area */
/* set up base environment */
memset(&env, 0, sizeof(env));
env.type = E_NONE;
ainit(&env.area);
e = &env;
newblock(); /* set up global l->vars and l->funs */
/* Do this first so output routines (eg, errorf, shellf) can work */
initio();
initvar();
initctypes();
inittraps();
#ifdef KSH
coproc_init();
#endif /* KSH */
/* set up variable and command dictionaries */
tinit(&taliases, APERM, 0);
tinit(&aliases, APERM, 0);
tinit(&homedirs, APERM, 0);
/* define shell keywords */
initkeywords();
/* define built-in commands */
tinit(&builtins, APERM, 64); /* must be 2^n (currently 40 builtins) */
for (i = 0; shbuiltins[i].name != NULL; i++)
builtin(shbuiltins[i].name, shbuiltins[i].func);
for (i = 0; kshbuiltins[i].name != NULL; i++)
builtin(kshbuiltins[i].name, kshbuiltins[i].func);
init_histvec();
def_path = DEFAULT__PATH;
#if defined(HAVE_CONFSTR) && defined(_CS_PATH)
{
size_t len = confstr(_CS_PATH, (char *) 0, 0);
char *new;
if (len > 0) {
confstr(_CS_PATH, new = alloc(len + 1, APERM), len + 1);
def_path = new;
}
}
#endif /* HAVE_CONFSTR && _CS_PATH */
/* Set PATH to def_path (will set the path global variable).
* (import of environment below will probably change this setting).
*/
{
struct tbl *vp = global("PATH");
/* setstr can't fail here */
setstr(vp, def_path, KSH_RETURN_ERROR);
}
/* Turn on nohup by default for how - will change to off
* by default once people are aware of its existence
* (at&t ksh does not have a nohup option - it always sends
* the hup).
*/
Flag(FNOHUP) = 1;
/* Turn on brace expansion by default. At&t ksh's that have
* alternation always have it on. BUT, posix doesn't have
* brace expansion, so set this before setting up FPOSIX
* (change_flag() clears FBRACEEXPAND when FPOSIX is set).
*/
#ifdef BRACE_EXPAND
Flag(FBRACEEXPAND) = 1;
#endif /* BRACE_EXPAND */
/* set posix flag just before environment so that it will have
* exactly the same effect as the POSIXLY_CORRECT environment
* variable. If this needs to be done sooner to ensure correct posix
* operation, an initial scan of the environment will also have
* done sooner.
*/
#ifdef POSIXLY_CORRECT
change_flag(FPOSIX, OF_SPECIAL, 1);
#endif /* POSIXLY_CORRECT */
/* Check to see if we're /bin/sh. */
if (!strcmp(&kshname[strlen(kshname) - 3], "/sh")
|| !strcmp(kshname, "sh") || !strcmp(kshname, "-sh"))
Flag(FSH) = 1;
/* Set edit mode to emacs by default, may be overridden
* by the environment or the user. Also, we want tab completion
* on in vi by default. */
#if defined(EDIT) && defined(EMACS)
change_flag(FEMACS, OF_SPECIAL, 1);
#endif /* EDIT && EMACS */
#if defined(EDIT) && defined(VI)
Flag(FVITABCOMPLETE) = 1;
#endif /* EDIT && VI */
/* import environment */
if (environ != NULL)
for (wp = environ; *wp != NULL; wp++)
typeset(*wp, IMPORT|EXPORT, 0, 0, 0);
kshpid = procpid = getpid();
typeset(initifs, 0, 0, 0, 0); /* for security */
/* assign default shell variable values */
substitute(initsubs, 0);
/* Figure out the current working directory and set $PWD */
{
struct stat s_pwd, s_dot;
struct tbl *pwd_v = global("PWD");
char *pwd = str_val(pwd_v);
char *pwdx = pwd;
/* Try to use existing $PWD if it is valid */
if (!ISABSPATH(pwd)
|| stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0
|| s_pwd.st_dev != s_dot.st_dev
|| s_pwd.st_ino != s_dot.st_ino)
pwdx = (char *) 0;
set_current_wd(pwdx);
if (current_wd[0])
simplify_path(current_wd);
/* Only set pwd if we know where we are or if it had a
* bogus value
*/
if (current_wd[0] || pwd != null)
/* setstr can't fail here */
setstr(pwd_v, current_wd, KSH_RETURN_ERROR);
}
ppid = getppid();
setint(global("PPID"), (long) ppid);
#ifdef KSH
setint(global("RANDOM"), (long) (time((time_t *)0) * kshpid * ppid));
#endif /* KSH */
/* setstr can't fail here */
setstr(global(version_param), ksh_version, KSH_RETURN_ERROR);
/* execute initialization statements */
for (wp = (char**) initcoms; *wp != NULL; wp++) {
shcomexec(wp);
for (; *wp != NULL; wp++)
;
}
ksheuid = geteuid();
safe_prompt = ksheuid ? "$ " : "# ";
{
struct tbl *vp = global("PS1");
/* Set PS1 if it isn't set, or we are root and prompt doesn't
* contain a #.
*/
if (!(vp->flag & ISSET)
|| (!ksheuid && !strchr(str_val(vp), '#')))
/* setstr can't fail here */
setstr(vp, safe_prompt, KSH_RETURN_ERROR);
}
/* Set this before parsing arguments */
Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid();
/* this to note if monitor is set on command line (see below) */
Flag(FMONITOR) = 127;
argi = parse_args(argv, OF_CMDLINE, (int *) 0);
if (argi < 0)
exit(1);
if (Flag(FCOMMAND)) {
s = pushs(SSTRING, ATEMP);
if (!(s->start = s->str = argv[argi++]))
errorf("-c requires an argument");
if (argv[argi])
kshname = argv[argi++];
} else if (argi < argc && !Flag(FSTDIN)) {
s = pushs(SFILE, ATEMP);
#ifdef OS2
/* a bug in os2 extproc shell processing doesn't
* pass full pathnames so we have to search for it.
* This changes the behavior of 'ksh arg' to search
* the users search path but it can't be helped.
*/
s->file = search(argv[argi++], path, R_OK, (int *) 0);
if (!s->file || !*s->file)
s->file = argv[argi - 1];
#else
s->file = argv[argi++];
#endif /* OS2 */
s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
if (s->u.shf == NULL) {
exstat = 127; /* POSIX */
errorf("%s: %s", s->file, strerror(errno));
}
kshname = s->file;
} else {
Flag(FSTDIN) = 1;
s = pushs(SSTDIN, ATEMP);
s->file = "<stdin>";
s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),
(struct shf *) 0);
if (isatty(0) && isatty(2)) {
Flag(FTALKING) = Flag(FTALKING_I) = 1;
/* The following only if isatty(0) */
s->flags |= SF_TTY;
s->u.shf->flags |= SHF_INTERRUPT;
s->file = (char *) 0;
}
}
/* This bizarreness is mandated by POSIX */
{
struct stat s_stdin;
if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) &&
Flag(FTALKING))
reset_nonblock(0);
}
/* initialize job control */
i = Flag(FMONITOR) != 127;
Flag(FMONITOR) = 0;
j_init(i);
#ifdef EDIT
/* Do this after j_init(), as tty_fd is not initialized 'til then */
if (Flag(FTALKING))
x_init();
#endif
l = e->loc;
l->argv = &argv[argi - 1];
l->argc = argc - argi;
l->argv[0] = (char *) kshname;
getopts_reset(1);
/* Disable during .profile/ENV reading */
restricted = Flag(FRESTRICTED);
Flag(FRESTRICTED) = 0;
errexit = Flag(FERREXIT);
Flag(FERREXIT) = 0;
/* Do this before profile/$ENV so that if it causes problems in them,
* user will know why things broke.
*/
if (!current_wd[0] && Flag(FTALKING))
warningf(FALSE, "Cannot determine current working directory");
if (Flag(FLOGIN)) {
#ifdef OS2
char *profile;
/* Try to find a profile - first see if $INIT has a value,
* then try /etc/profile.ksh, then c:/usr/etc/profile.ksh.
*/
if (!Flag(FPRIVILEGED)
&& strcmp(profile = substitute("$INIT/profile.ksh", 0),
"/profile.ksh"))
include(profile, 0, (char **) 0, 1);
else if (include("/etc/profile.ksh", 0, (char **) 0, 1) < 0)
include("c:/usr/etc/profile.ksh", 0, (char **) 0, 1);
if (!Flag(FPRIVILEGED))
include(substitute("$HOME/profile.ksh", 0), 0,
(char **) 0, 1);
#else /* OS2 */
include(KSH_SYSTEM_PROFILE, 0, (char **) 0, 1);
if (!Flag(FPRIVILEGED))
include(substitute("$HOME/.profile", 0), 0,
(char **) 0, 1);
#endif /* OS2 */
}
if (Flag(FPRIVILEGED))
include("/etc/suid_profile", 0, (char **) 0, 1);
else {
char *env_file;
#ifndef KSH
if (!Flag(FPOSIX))
env_file = null;
else
#endif /* !KSH */
/* include $ENV */
env_file = str_val(global("ENV"));
#ifdef DEFAULT_ENV
/* If env isn't set, include default environment */
if (env_file == null)
env_file = DEFAULT_ENV;
#endif /* DEFAULT_ENV */
env_file = substitute(env_file, DOTILDE);
if (*env_file != '\0')
include(env_file, 0, (char **) 0, 1);
#ifdef OS2
else if (Flag(FTALKING))
include(substitute("$HOME/kshrc.ksh", 0), 0,
(char **) 0, 1);
#endif /* OS2 */
}
if (is_restricted(argv[0]) || is_restricted(str_val(global("SHELL"))))
restricted = 1;
if (restricted) {
static const char *const restr_com[] = {
"typeset", "-r", "PATH",
"ENV", "SHELL",
(char *) 0
};
shcomexec((char **) restr_com);
/* After typeset command... */
Flag(FRESTRICTED) = 1;
}
if (errexit)
Flag(FERREXIT) = 1;
if (Flag(FTALKING)) {
hist_init(s);
#ifdef KSH
alarm_init();
#endif /* KSH */
} else
Flag(FTRACKALL) = 1; /* set after ENV */
shell(s, TRUE); /* doesn't return */
return 0;
}
int
include(name, argc, argv, intr_ok)
const char *name;
int argc;
char **argv;
int intr_ok;
{
register Source *volatile s = NULL;
struct shf *shf;
char **volatile old_argv;
volatile int old_argc;
int i;
shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
if (shf == NULL)
return -1;
if (argv) {
old_argv = e->loc->argv;
old_argc = e->loc->argc;
} else {
old_argv = (char **) 0;
old_argc = 0;
}
newenv(E_INCL);
i = ksh_sigsetjmp(e->jbuf, 0);
if (i) {
if (s) /* Do this before quitenv(), which frees the memory */
shf_close(s->u.shf);
quitenv();
if (old_argv) {
e->loc->argv = old_argv;
e->loc->argc = old_argc;
}
switch (i) {
case LRETURN:
case LERROR:
return exstat & 0xff; /* see below */
case LINTR:
/* intr_ok is set if we are including .profile or $ENV.
* If user ^C's out, we don't want to kill the shell...
*/
if (intr_ok && (exstat - 128) != SIGTERM)
return 1;
/* fall through... */
case LEXIT:
case LLEAVE:
case LSHELL:
unwind(i);
/*NOREACHED*/
default:
internal_errorf(1, "include: %d", i);
/*NOREACHED*/
}
}
if (argv) {
e->loc->argv = argv;
e->loc->argc = argc;
}
s = pushs(SFILE, ATEMP);
s->u.shf = shf;
s->file = str_save(name, ATEMP);
i = shell(s, FALSE);
shf_close(s->u.shf);
quitenv();
if (old_argv) {
e->loc->argv = old_argv;
e->loc->argc = old_argc;
}
return i & 0xff; /* & 0xff to ensure value not -1 */
}
int
command(comm)
const char *comm;
{
register Source *s;
s = pushs(SSTRING, ATEMP);
s->start = s->str = comm;
return shell(s, FALSE);
}
/*
* run the commands from the input source, returning status.
*/
int
shell(s, toplevel)
Source *volatile s; /* input source */
int volatile toplevel;
{
struct op *t;
volatile int wastty = s->flags & SF_TTY;
volatile int attempts = 13;
volatile int interactive = Flag(FTALKING) && toplevel;
Source *volatile old_source = source;
int i;
newenv(E_PARSE);
if (interactive)
really_exit = 0;
i = ksh_sigsetjmp(e->jbuf, 0);
if (i) {
switch (i) {
case LINTR: /* we get here if SIGINT not caught or ignored */
case LERROR:
case LSHELL:
if (interactive) {
if (i == LINTR)
shellf(newline);
/* Reset any eof that was read as part of a
* multiline command.
*/
if (Flag(FIGNOREEOF) && s->type == SEOF
&& wastty)
s->type = SSTDIN;
/* Used by exit command to get back to
* top level shell. Kind of strange since
* interactive is set if we are reading from
* a tty, but to have stopped jobs, one only
* needs FMONITOR set (not FTALKING/SF_TTY)...
*/
/* toss any input we have so far */
s->start = s->str = null;
break;
}
/* fall through... */
case LEXIT:
case LLEAVE:
case LRETURN:
source = old_source;
quitenv();
unwind(i); /* keep on going */
/*NOREACHED*/
default:
source = old_source;
quitenv();
internal_errorf(1, "shell: %d", i);
/*NOREACHED*/
}
}
while (1) {
if (trap)
runtraps(0);
if (s->next == NULL) {
if (Flag(FVERBOSE))
s->flags |= SF_ECHO;
else
s->flags &= ~SF_ECHO;
}
if (interactive) {
j_notify();
#ifdef KSH
mcheck();
#endif /* KSH */
set_prompt(PS1, s);
}
t = compile(s);
if (t != NULL && t->type == TEOF) {
if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
shellf("Use `exit' to leave ksh\n");
s->type = SSTDIN;
} else if (wastty && !really_exit
&& j_stopped_running())
{
really_exit = 1;
s->type = SSTDIN;
} else {
/* this for POSIX, which says EXIT traps
* shall be taken in the environment
* immediately after the last command
* executed.
*/
if (toplevel)
unwind(LEXIT);
break;
}
}
if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY)))
exstat = execute(t, 0);
if (t != NULL && t->type != TEOF && interactive && really_exit)
really_exit = 0;
reclaim();
}
quitenv();
source = old_source;
return exstat;
}
/* return to closest error handler or shell(), exit if none found */
void
unwind(i)
int i;
{
/* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */
if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR)
&& sigtraps[SIGEXIT_].trap))
{
runtrap(&sigtraps[SIGEXIT_]);
i = LLEAVE;
} else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
runtrap(&sigtraps[SIGERR_]);
i = LLEAVE;
}
while (1) {
switch (e->type) {
case E_PARSE:
case E_FUNC:
case E_INCL:
case E_LOOP:
case E_ERRH:
ksh_siglongjmp(e->jbuf, i);
/*NOTREACHED*/
case E_NONE:
if (i == LINTR)
e->flags |= EF_FAKE_SIGDIE;
/* Fall through... */
default:
quitenv();
}
}
}
void
newenv(type)
int type;
{
register struct env *ep;
ep = (struct env *) alloc(sizeof(*ep), ATEMP);
ep->type = type;
ep->flags = 0;
ainit(&ep->area);
ep->loc = e->loc;
ep->savefd = NULL;
ep->oenv = e;
ep->temps = NULL;
e = ep;
}
void
quitenv()
{
register struct env *ep = e;
register int fd;
if (ep->oenv && ep->oenv->loc != ep->loc)
popblock();
if (ep->savefd != NULL) {
for (fd = 0; fd < NUFILE; fd++)
/* if ep->savefd[fd] < 0, means fd was closed */
if (ep->savefd[fd])
restfd(fd, ep->savefd[fd]);
if (ep->savefd[2]) /* Clear any write errors */
shf_reopen(2, SHF_WR, shl_out);
}
reclaim();
/* Bottom of the stack.
* Either main shell is exiting or cleanup_parents_env() was called.
*/
if (ep->oenv == NULL) {
if (ep->type == E_NONE) { /* Main shell exiting? */
if (Flag(FTALKING))
hist_finish();
j_exit();
if (ep->flags & EF_FAKE_SIGDIE) {
int sig = exstat - 128;
/* ham up our death a bit (at&t ksh
* only seems to do this for SIGTERM)
* Don't do it for SIGQUIT, since we'd
* dump a core..
*/
if (sig == SIGINT || sig == SIGTERM) {
setsig(&sigtraps[sig], SIG_DFL,
SS_RESTORE_CURR|SS_FORCE);
kill(0, sig);
}
}
#ifdef MEM_DEBUG
chmem_allfree();
#endif /* MEM_DEBUG */
}
exit(exstat);
}
e = e->oenv;
afree(ep, ATEMP);
}
/* Called after a fork to cleanup stuff left over from parents environment */
void
cleanup_parents_env()
{
struct env *ep;
int fd;
/* Don't clean up temporary files - parent will probably need them.
* Also, can't easily reclaim memory since variables, etc. could be
* anywhere.
*/
/* close all file descriptors hiding in savefd */
for (ep = e; ep; ep = ep->oenv) {
if (ep->savefd) {
for (fd = 0; fd < NUFILE; fd++)
if (ep->savefd[fd] > 0)
close(ep->savefd[fd]);
afree(ep->savefd, &ep->area);
ep->savefd = (short *) 0;
}
}
e->oenv = (struct env *) 0;
}
/* Called just before an execve cleanup stuff temporary files */
void
cleanup_proc_env()
{
struct env *ep;
for (ep = e; ep; ep = ep->oenv)
remove_temps(ep->temps);
}
/* remove temp files and free ATEMP Area */
static void
reclaim()
{
remove_temps(e->temps);
e->temps = NULL;
afreeall(&e->area);
}
static void
remove_temps(tp)
struct temp *tp;
{
#ifdef OS2
static struct temp *delayed_remove;
struct temp *t, **tprev;
if (delayed_remove) {
for (tprev = &delayed_remove, t = delayed_remove; t; t = *tprev)
/* No need to check t->pid here... */
if (unlink(t->name) >= 0 || errno == ENOENT) {
*tprev = t->next;
afree(t, APERM);
} else
tprev = &t->next;
}
#endif /* OS2 */
for (; tp != NULL; tp = tp->next)
if (tp->pid == procpid) {
#ifdef OS2
/* OS/2 (and dos) do not allow files that are currently
* open to be removed, so we cache it away for future
* removal.
* XXX should only do this if errno
* is Efile-still-open-can't-remove
* (but I don't know what that is...)
*/
if (unlink(tp->name) < 0 && errno != ENOENT) {
t = (struct temp *) alloc(
sizeof(struct temp) + strlen(tp->name) + 1,
APERM);
memset(t, 0, sizeof(struct temp));
t->name = (char *) &t[1];
strcpy(t->name, tp->name);
t->next = delayed_remove;
delayed_remove = t;
}
#else /* OS2 */
unlink(tp->name);
#endif /* OS2 */
}
}
/* Returns true if name refers to a restricted shell */
static int
is_restricted(name)
char *name;
{
char *p;
if ((p = ksh_strrchr_dirsep(name)))
name = p;
/* accepts rsh, rksh, rpdksh, pdrksh, etc. */
return (p = strchr(name, 'r')) && strstr(p, "sh");
}
void
aerror(ap, msg)
Area *ap;
const char *msg;
{
internal_errorf(1, "alloc: %s", msg);
errorf(null); /* this is never executed - keeps gcc quiet */
/*NOTREACHED*/
}

1350
misc.c Normal file

File diff suppressed because it is too large Load Diff

295
missing.c Normal file
View File

@ -0,0 +1,295 @@
/* $OpenBSD: missing.c,v 1.4 1999/06/15 01:18:35 millert Exp $ */
/*
* Routines which may be missing on some machines
*/
#include "sh.h"
#include "ksh_stat.h"
#include "ksh_dir.h"
#ifndef HAVE_MEMSET
void *
memset(d, c, n)
void *d;
int c;
size_t n;
{
unsigned char *p = (unsigned char *) d;
/* Not amazingly fast.. */
for (; n > 0; --n)
*p++ = c;
return d;
}
#endif /* !HAVE_MEMSET */
#if !defined(HAVE_MEMMOVE) && !defined(HAVE_BCOPY)
void *
memmove(d, s, n)
void *d;
const void *s;
size_t n;
{
char *dp = (char *) d, *sp = (char *) s;
if (n <= 0)
;
else if (dp < sp)
do
*dp++ = *sp++;
while (--n > 0);
else if (dp > sp) {
dp += n;
sp += n;
do
*--dp = *--sp;
while (--n > 0);
}
return d;
}
#endif /* !HAVE_MEMMOVE && !HAVE_BCOPY */
#ifndef HAVE_STRCASECMP
/*
* Case insensitive string compare routines, same semantics as str[n]cmp()
* (assumes ASCII..).
*/
static const char ichars[256] = {
0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
int
strcasecmp(s1, s2)
const char *s1;
const char *s2;
{
const unsigned char *us1 = (const unsigned char *) s1;
const unsigned char *us2 = (const unsigned char *) s2;
while (ichars[*us1] == ichars[*us2++])
if (!*us1++)
return 0;
return ichars[*us1] - ichars[*--us2];
}
int
strncasecmp(s1, s2, n)
const char *s1;
const char *s2;
int n;
{
const unsigned char *us1 = (const unsigned char *) s1;
const unsigned char *us2 = (const unsigned char *) s2;
while (--n >= 0 && ichars[*us1] == ichars[*us2++])
if (!*us1++)
return 0;
return n < 0 ? 0 : ichars[*us1] - ichars[*--us2];
}
#endif /* HAVE_STRCASECMP */
#ifndef HAVE_STRSTR
char *
strstr(s, p)
const char *s;
const char *p;
{
int len;
if (s && p)
for (len = strlen(p); *s; s++)
if (*s == *p && strncmp(s, p, len) == 0)
return (char *) s;
return 0;
}
#endif /* HAVE_STRSTR */
#ifndef HAVE_STRERROR
char *
strerror(err)
int err;
{
static char buf[64];
# ifdef HAVE_SYS_ERRLIST
# ifndef SYS_ERRLIST_DECLARED
extern int sys_nerr;
extern char *sys_errlist[];
# endif
char *p;
if (err < 0 || err >= sys_nerr)
shf_snprintf(p = buf, sizeof(buf), "Unknown system error %d",
err);
else
p = sys_errlist[err];
return p;
# else /* HAVE_SYS_ERRLIST */
switch (err) {
case EINVAL:
return "Invalid argument";
case EACCES:
return "Permission denied";
case ESRCH:
return "No such process";
case EPERM:
return "Not owner";
case ENOENT:
return "No such file or directory";
case ENOTDIR:
return "Not a directory";
case ENOEXEC:
return "Exec format error";
case ENOMEM:
return "Not enough memory";
case E2BIG:
return "Argument list too long";
default:
shf_snprintf(buf, sizeof(buf), "Unknown system error %d", err);
return buf;
}
# endif /* HAVE_SYS_ERRLIST */
}
#endif /* !HAVE_STRERROR */
#ifdef TIMES_BROKEN
# include "ksh_time.h"
# include "ksh_times.h"
# ifdef HAVE_GETRUSAGE
# include <sys/resource.h>
# else /* HAVE_GETRUSAGE */
# include <sys/timeb.h>
# endif /* HAVE_GETRUSAGE */
clock_t
ksh_times(tms)
struct tms *tms;
{
static clock_t base_sec;
clock_t rv;
# ifdef HAVE_GETRUSAGE
{
struct timeval tv;
struct rusage ru;
getrusage(RUSAGE_SELF, &ru);
tms->tms_utime = ru.ru_utime.tv_sec * CLK_TCK
+ ru.ru_utime.tv_usec * CLK_TCK / 1000000;
tms->tms_stime = ru.ru_stime.tv_sec * CLK_TCK
+ ru.ru_stime.tv_usec * CLK_TCK / 1000000;
getrusage(RUSAGE_CHILDREN, &ru);
tms->tms_cutime = ru.ru_utime.tv_sec * CLK_TCK
+ ru.ru_utime.tv_usec * CLK_TCK / 1000000;
tms->tms_cstime = ru.ru_stime.tv_sec * CLK_TCK
+ ru.ru_stime.tv_usec * CLK_TCK / 1000000;
gettimeofday(&tv, (struct timezone *) 0);
if (base_sec == 0)
base_sec = tv.tv_sec;
rv = (tv.tv_sec - base_sec) * CLK_TCK;
rv += tv.tv_usec * CLK_TCK / 1000000;
}
# else /* HAVE_GETRUSAGE */
/* Assume times() available, but always returns 0
* (also assumes ftime() available)
*/
{
struct timeb tb;
if (times(tms) == (clock_t) -1)
return (clock_t) -1;
ftime(&tb);
if (base_sec == 0)
base_sec = tb.time;
rv = (tb.time - base_sec) * CLK_TCK;
rv += tb.millitm * CLK_TCK / 1000;
}
# endif /* HAVE_GETRUSAGE */
return rv;
}
#endif /* TIMES_BROKEN */
#ifdef OPENDIR_DOES_NONDIR
/* Prevent opendir() from attempting to open non-directories. Such
* behavior can cause problems if it attempts to open special devices...
*/
DIR *
ksh_opendir(d)
const char *d;
{
struct stat statb;
if (stat(d, &statb) != 0)
return (DIR *) 0;
if (!S_ISDIR(statb.st_mode)) {
errno = ENOTDIR;
return (DIR *) 0;
}
return opendir(d);
}
#endif /* OPENDIR_DOES_NONDIR */
#ifndef HAVE_DUP2
int
dup2(oldd, newd)
int oldd;
int newd;
{
int old_errno;
if (fcntl(oldd, F_GETFL, 0) == -1)
return -1; /* errno == EBADF */
if (oldd == newd)
return newd;
old_errno = errno;
close(newd); /* in case its open */
errno = old_errno;
return fcntl(oldd, F_DUPFD, newd);
}
#endif /* !HAVE_MEMSET */

312
path.c Normal file
View File

@ -0,0 +1,312 @@
/* $OpenBSD: path.c,v 1.8 2003/02/28 09:45:09 jmc Exp $ */
#include "sh.h"
#include "ksh_stat.h"
/*
* Contains a routine to search a : separated list of
* paths (a la CDPATH) and make appropriate file names.
* Also contains a routine to simplify .'s and ..'s out of
* a path name.
*
* Larry Bouzane (larry@cs.mun.ca)
*/
#ifdef S_ISLNK
static char *do_phys_path ARGS((XString *xsp, char *xp, const char *path));
#endif /* S_ISLNK */
/*
* Makes a filename into result using the following algorithm.
* - make result NULL
* - if file starts with '/', append file to result & set cdpathp to NULL
* - if file starts with ./ or ../ append cwd and file to result
* and set cdpathp to NULL
* - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
* then cwd is appended to result.
* - the first element of cdpathp is appended to result
* - file is appended to result
* - cdpathp is set to the start of the next element in cdpathp (or NULL
* if there are no more elements.
* The return value indicates whether a non-null element from cdpathp
* was appened to result.
*/
int
make_path(cwd, file, cdpathp, xsp, phys_pathp)
const char *cwd;
const char *file;
char **cdpathp; /* & of : separated list */
XString *xsp;
int *phys_pathp;
{
int rval = 0;
int use_cdpath = 1;
char *plist;
int len;
int plen = 0;
char *xp = Xstring(*xsp, xp);
if (!file)
file = null;
if (!ISRELPATH(file)) {
*phys_pathp = 0;
use_cdpath = 0;
} else {
if (file[0] == '.') {
char c = file[1];
if (c == '.')
c = file[2];
if (ISDIRSEP(c) || c == '\0')
use_cdpath = 0;
}
plist = *cdpathp;
if (!plist)
use_cdpath = 0;
else if (use_cdpath) {
char *pend;
for (pend = plist; *pend && *pend != PATHSEP; pend++)
;
plen = pend - plist;
*cdpathp = *pend ? ++pend : (char *) 0;
}
if ((use_cdpath == 0 || !plen || ISRELPATH(plist))
&& (cwd && *cwd))
{
len = strlen(cwd);
XcheckN(*xsp, xp, len);
memcpy(xp, cwd, len);
xp += len;
if (!ISDIRSEP(cwd[len - 1]))
Xput(*xsp, xp, DIRSEP);
}
*phys_pathp = Xlength(*xsp, xp);
if (use_cdpath && plen) {
XcheckN(*xsp, xp, plen);
memcpy(xp, plist, plen);
xp += plen;
if (!ISDIRSEP(plist[plen - 1]))
Xput(*xsp, xp, DIRSEP);
rval = 1;
}
}
len = strlen(file) + 1;
XcheckN(*xsp, xp, len);
memcpy(xp, file, len);
if (!use_cdpath)
*cdpathp = (char *) 0;
return rval;
}
/*
* Simplify pathnames containing "." and ".." entries.
* ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
*/
void
simplify_path(path)
char *path;
{
char *cur;
char *t;
int isrooted;
char *very_start = path;
char *start;
if (!*path)
return;
if ((isrooted = ISROOTEDPATH(path)))
very_start++;
#if defined (OS2) || defined (__CYGWIN__)
if (path[0] && path[1] == ':') /* skip a: */
very_start += 2;
#endif /* OS2 || __CYGWIN__ */
/* Before After
* /foo/ /foo
* /foo/../../bar /bar
* /foo/./blah/.. /foo
* . .
* .. ..
* ./foo foo
* foo/../../../bar ../../bar
* OS2 and CYGWIN:
* a:/foo/../.. a:/
* a:. a:
* a:.. a:..
* a:foo/../../blah a:../blah
*/
#ifdef __CYGWIN__
/* preserve leading double-slash on pathnames (for UNC paths) */
if (path[0] && ISDIRSEP(path[0]) && path[1] && ISDIRSEP(path[1]))
very_start++;
#endif /* __CYGWIN__ */
for (cur = t = start = very_start; ; ) {
/* treat multiple '/'s as one '/' */
while (ISDIRSEP(*t))
t++;
if (*t == '\0') {
if (cur == path)
/* convert empty path to dot */
*cur++ = '.';
*cur = '\0';
break;
}
if (t[0] == '.') {
if (!t[1] || ISDIRSEP(t[1])) {
t += 1;
continue;
} else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) {
if (!isrooted && cur == start) {
if (cur != very_start)
*cur++ = DIRSEP;
*cur++ = '.';
*cur++ = '.';
start = cur;
} else if (cur != start)
while (--cur > start && !ISDIRSEP(*cur))
;
t += 2;
continue;
}
}
if (cur != very_start)
*cur++ = DIRSEP;
/* find/copy next component of pathname */
while (*t && !ISDIRSEP(*t))
*cur++ = *t++;
}
}
void
set_current_wd(path)
char *path;
{
int len;
char *p = path;
if (!p && !(p = ksh_get_wd((char *) 0, 0)))
p = null;
len = strlen(p) + 1;
if (len > current_wd_size)
current_wd = aresize(current_wd, current_wd_size = len, APERM);
memcpy(current_wd, p, len);
if (p != path && p != null)
afree(p, ATEMP);
}
#ifdef S_ISLNK
char *
get_phys_path(path)
const char *path;
{
XString xs;
char *xp;
Xinit(xs, xp, strlen(path) + 1, ATEMP);
xp = do_phys_path(&xs, xp, path);
if (!xp)
return (char *) 0;
if (Xlength(xs, xp) == 0)
Xput(xs, xp, DIRSEP);
Xput(xs, xp, '\0');
return Xclose(xs, xp);
}
static char *
do_phys_path(xsp, xp, path)
XString *xsp;
char *xp;
const char *path;
{
const char *p, *q;
int len, llen;
int savepos;
char lbuf[PATH];
Xcheck(*xsp, xp);
for (p = path; p; p = q) {
while (ISDIRSEP(*p))
p++;
if (!*p)
break;
len = (q = ksh_strchr_dirsep(p)) ? q - p : strlen(p);
if (len == 1 && p[0] == '.')
continue;
if (len == 2 && p[0] == '.' && p[1] == '.') {
while (xp > Xstring(*xsp, xp)) {
xp--;
if (ISDIRSEP(*xp))
break;
}
continue;
}
savepos = Xsavepos(*xsp, xp);
Xput(*xsp, xp, DIRSEP);
XcheckN(*xsp, xp, len + 1);
memcpy(xp, p, len);
xp += len;
*xp = '\0';
llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1);
if (llen < 0) {
/* EINVAL means it wasn't a symlink... */
if (errno != EINVAL)
return (char *) 0;
continue;
}
lbuf[llen] = '\0';
/* If absolute path, start from scratch.. */
xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp)
: Xrestpos(*xsp, xp, savepos);
if (!(xp = do_phys_path(xsp, xp, lbuf)))
return (char *) 0;
}
return xp;
}
#endif /* S_ISLNK */
#ifdef TEST
main(argc, argv)
{
int rv;
char *cp, cdpath[256], pwd[256], file[256], result[256];
printf("enter CDPATH: "); gets(cdpath);
printf("enter PWD: "); gets(pwd);
while (1) {
if (printf("Enter file: "), gets(file) == 0)
return 0;
cp = cdpath;
do {
rv = make_path(pwd, file, &cp, result, sizeof(result));
printf("make_path returns (%d), \"%s\" ", rv, result);
simplify_path(result);
printf("(simpifies to \"%s\")\n", result);
} while (cp);
}
}
#endif /* TEST */

303
proto.h Normal file
View File

@ -0,0 +1,303 @@
/* $OpenBSD: proto.h,v 1.9 2001/02/19 09:49:54 camield Exp $ */
/*
* prototypes for PD-KSH
* originally generated using "cproto.c 3.5 92/04/11 19:28:01 cthuang "
* $From: proto.h,v 1.3 1994/05/19 18:32:40 michael Exp michael $
*/
/* alloc.c */
Area * ainit ARGS((Area *ap));
void afreeall ARGS((Area *ap));
void * alloc ARGS((size_t size, Area *ap));
void * aresize ARGS((void *ptr, size_t size, Area *ap));
void afree ARGS((void *ptr, Area *ap));
/* c_ksh.c */
int c_hash ARGS((char **wp));
int c_cd ARGS((char **wp));
int c_pwd ARGS((char **wp));
int c_print ARGS((char **wp));
int c_whence ARGS((char **wp));
int c_command ARGS((char **wp));
int c_typeset ARGS((char **wp));
int c_alias ARGS((char **wp));
int c_unalias ARGS((char **wp));
int c_let ARGS((char **wp));
int c_jobs ARGS((char **wp));
int c_fgbg ARGS((char **wp));
int c_kill ARGS((char **wp));
void getopts_reset ARGS((int val));
int c_getopts ARGS((char **wp));
int c_bind ARGS((char **wp));
/* c_sh.c */
int c_label ARGS((char **wp));
int c_shift ARGS((char **wp));
int c_umask ARGS((char **wp));
int c_dot ARGS((char **wp));
int c_wait ARGS((char **wp));
int c_read ARGS((char **wp));
int c_eval ARGS((char **wp));
int c_trap ARGS((char **wp));
int c_brkcont ARGS((char **wp));
int c_exitreturn ARGS((char **wp));
int c_set ARGS((char **wp));
int c_unset ARGS((char **wp));
int c_ulimit ARGS((char **wp));
int c_times ARGS((char **wp));
int timex ARGS((struct op *t, int f));
void timex_hook ARGS((struct op *t, char ** volatile *app));
int c_exec ARGS((char **wp));
int c_builtin ARGS((char **wp));
/* c_test.c */
int c_test ARGS((char **wp));
/* edit.c: most prototypes in edit.h */
void x_init ARGS((void));
int x_read ARGS((char *buf, size_t len));
void set_editmode ARGS((const char *ed));
/* emacs.c: most prototypes in edit.h */
int x_bind ARGS((const char *a1, const char *a2, int macro,
int list));
/* eval.c */
char * substitute ARGS((const char *cp, int f));
char ** eval ARGS((char **ap, int f));
char * evalstr ARGS((char *cp, int f));
char * evalonestr ARGS((char *cp, int f));
char *debunk ARGS((char *dp, const char *sp));
void expand ARGS((char *cp, XPtrV *wp, int f));
int glob_str ARGS((char *cp, XPtrV *wp, int markdirs));
/* exec.c */
int fd_clexec ARGS((int fd));
int execute ARGS((struct op * volatile t, volatile int flags));
int shcomexec ARGS((char **wp));
struct tbl * findfunc ARGS((const char *name, unsigned int h, int create));
int define ARGS((const char *name, struct op *t));
void builtin ARGS((const char *name, int (*func)(char **)));
struct tbl * findcom ARGS((const char *name, int flags));
void flushcom ARGS((int all));
char * search ARGS((const char *name, const char *path, int mode,
int *errnop));
int search_access ARGS((const char *path, int mode, int *errnop));
int pr_menu ARGS((char *const *ap));
int pr_list ARGS((char *const *ap));
/* expr.c */
int evaluate ARGS((const char *expr, long *rval, int error_ok));
int v_evaluate ARGS((struct tbl *vp, const char *expr, volatile int error_ok));
/* history.c */
void init_histvec ARGS((void));
void hist_init ARGS((Source *s));
void hist_finish ARGS((void));
void histsave ARGS((int lno, const char *cmd, int dowrite));
#ifdef HISTORY
int c_fc ARGS((register char **wp));
void sethistsize ARGS((int n));
void sethistfile ARGS((const char *name));
# ifdef EASY_HISTORY
void histappend ARGS((const char *cmd, int nl_separate));
# endif
char ** histpos ARGS((void));
int histN ARGS((void));
int histnum ARGS((int n));
int findhist ARGS((int start, int fwd, const char *str,
int anchored));
#endif /* HISTORY */
/* io.c */
void errorf ARGS((const char *fmt, ...))
GCC_FUNC_ATTR2(noreturn, format(printf, 1, 2));
void warningf ARGS((int fileline, const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 2, 3));
void bi_errorf ARGS((const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 1, 2));
void internal_errorf ARGS((int jump, const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 2, 3));
void error_prefix ARGS((int fileline));
void shellf ARGS((const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 1, 2));
void shprintf ARGS((const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 1, 2));
#ifdef KSH_DEBUG
void kshdebug_init_ ARGS((void));
void kshdebug_printf_ ARGS((const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 1, 2));
void kshdebug_dump_ ARGS((const char *str, const void *mem, int nbytes));
#endif /* KSH_DEBUG */
int can_seek ARGS((int fd));
void initio ARGS((void));
int ksh_dup2 ARGS((int ofd, int nfd, int errok));
int savefd ARGS((int fd, int noclose));
void restfd ARGS((int fd, int ofd));
void openpipe ARGS((int *pv));
void closepipe ARGS((int *pv));
int check_fd ARGS((char *name, int mode, const char **emsgp));
#ifdef KSH
void coproc_init ARGS((void));
void coproc_read_close ARGS((int fd));
void coproc_readw_close ARGS((int fd));
void coproc_write_close ARGS((int fd));
int coproc_getfd ARGS((int mode, const char **emsgp));
void coproc_cleanup ARGS((int reuse));
#endif /* KSH */
struct temp *maketemp ARGS((Area *ap, Temp_type type, struct temp **tlist));
/* jobs.c */
void j_init ARGS((int mflagset));
void j_exit ARGS((void));
void j_change ARGS((void));
int exchild ARGS((struct op *t, int flags, int close_fd));
void startlast ARGS((void));
int waitlast ARGS((void));
int waitfor ARGS((const char *cp, int *sigp));
int j_kill ARGS((const char *cp, int sig));
int j_resume ARGS((const char *cp, int bg));
int j_jobs ARGS((const char *cp, int slp, int nflag));
void j_notify ARGS((void));
pid_t j_async ARGS((void));
int j_stopped_running ARGS((void));
/* lex.c */
int yylex ARGS((int cf));
void yyerror ARGS((const char *fmt, ...))
GCC_FUNC_ATTR2(noreturn, format(printf, 1, 2));
Source * pushs ARGS((int type, Area *areap));
void set_prompt ARGS((int to, Source *s));
void pprompt ARGS((const char *cp, int ntruncate));
/* mail.c */
#ifdef KSH
void mcheck ARGS((void));
void mcset ARGS((long interval));
void mbset ARGS((char *p));
void mpset ARGS((char *mptoparse));
#endif /* KSH */
/* main.c */
int include ARGS((const char *name, int argc, char **argv,
int intr_ok));
int command ARGS((const char *comm));
int shell ARGS((Source *volatile s, int volatile toplevel));
void unwind ARGS((int i)) GCC_FUNC_ATTR(noreturn);
void newenv ARGS((int type));
void quitenv ARGS((void));
void cleanup_parents_env ARGS((void));
void cleanup_proc_env ARGS((void));
void aerror ARGS((Area *ap, const char *msg))
GCC_FUNC_ATTR(noreturn);
/* misc.c */
void setctypes ARGS((const char *s, int t));
void initctypes ARGS((void));
char * ulton ARGS((unsigned long n, int base));
char * str_save ARGS((const char *s, Area *ap));
char * str_nsave ARGS((const char *s, int n, Area *ap));
int option ARGS((const char *n));
char * getoptions ARGS((void));
void change_flag ARGS((enum sh_flag f, int what, int newval));
int parse_args ARGS((char **argv, int what, int *setargsp));
int getn ARGS((const char *as, int *ai));
int bi_getn ARGS((const char *as, int *ai));
char * strerror ARGS((int i));
int gmatch ARGS((const char *s, const char *p, int isfile));
int has_globbing ARGS((const char *xp, const char *xpe));
const unsigned char *pat_scan ARGS((const unsigned char *p,
const unsigned char *pe, int match_sep));
void qsortp ARGS((void **base, size_t n, int (*f)(void *, void *)));
int xstrcmp ARGS((void *p1, void *p2));
void ksh_getopt_reset ARGS((Getopt *go, int));
int ksh_getopt ARGS((char **argv, Getopt *go, const char *options));
void print_value_quoted ARGS((const char *s));
void print_columns ARGS((struct shf *shf, int n,
char *(*func)(void *, int, char *, int),
void *arg, int max_width, int prefcol));
int strip_nuls ARGS((char *buf, int nbytes));
char *str_zcpy ARGS((char *dst, const char *src, int dsize));
int blocking_read ARGS((int fd, char *buf, int nbytes));
int reset_nonblock ARGS((int fd));
char *ksh_get_wd ARGS((char *buf, int bsize));
/* path.c */
int make_path ARGS((const char *cwd, const char *file,
char **pathlist, XString *xsp, int *phys_pathp));
void simplify_path ARGS((char *path));
char *get_phys_path ARGS((const char *path));
void set_current_wd ARGS((char *path));
/* syn.c */
void initkeywords ARGS((void));
struct op * compile ARGS((Source *s));
/* table.c */
unsigned int hash ARGS((const char *n));
void tinit ARGS((struct table *tp, Area *ap, int tsize));
struct tbl * tsearch ARGS((struct table *tp, const char *n, unsigned int h));
struct tbl * tenter ARGS((struct table *tp, const char *n, unsigned int h));
void tdelete ARGS((struct tbl *p));
void twalk ARGS((struct tstate *ts, struct table *tp));
struct tbl * tnext ARGS((struct tstate *ts));
struct tbl ** tsort ARGS((struct table *tp));
/* trace.c */
/* trap.c */
void inittraps ARGS((void));
#ifdef KSH
void alarm_init ARGS((void));
#endif /* KSH */
Trap * gettrap ARGS((const char *name, int igncase));
RETSIGTYPE trapsig ARGS((int i));
void intrcheck ARGS((void));
int fatal_trap_check ARGS((void));
int trap_pending ARGS((void));
void runtraps ARGS((int intr));
void runtrap ARGS((Trap *p));
void cleartraps ARGS((void));
void restoresigs ARGS((void));
void settrap ARGS((Trap *p, char *s));
int block_pipe ARGS((void));
void restore_pipe ARGS((int restore_dfl));
int setsig ARGS((Trap *p, handler_t f, int flags));
void setexecsig ARGS((Trap *p, int restore));
/* tree.c */
int fptreef ARGS((struct shf *f, int indent, const char *fmt, ...));
char * snptreef ARGS((char *s, int n, const char *fmt, ...));
struct op * tcopy ARGS((struct op *t, Area *ap));
char * wdcopy ARGS((const char *wp, Area *ap));
char * wdscan ARGS((const char *wp, int c));
char * wdstrip ARGS((const char *wp));
void tfree ARGS((struct op *t, Area *ap));
/* var.c */
void newblock ARGS((void));
void popblock ARGS((void));
void initvar ARGS((void));
struct tbl * global ARGS((const char *n));
struct tbl * local ARGS((const char *n, bool_t copy));
char * str_val ARGS((struct tbl *vp));
long intval ARGS((struct tbl *vp));
int setstr ARGS((struct tbl *vq, const char *s, int error_ok));
struct tbl *setint_v ARGS((struct tbl *vq, struct tbl *vp));
void setint ARGS((struct tbl *vq, long n));
int getint ARGS((struct tbl *vp, long *nump));
struct tbl * typeset ARGS((const char *var, Tflag set, Tflag clr, int field, int base));
void unset ARGS((struct tbl *vp, int array_ref));
char * skip_varname ARGS((const char *s, int aok));
char *skip_wdvarname ARGS((const char *s, int aok));
int is_wdvarname ARGS((const char *s, int aok));
int is_wdvarassign ARGS((const char *s));
char ** makenv ARGS((void));
void change_random ARGS((void));
int array_ref_len ARGS((const char *cp));
char * arrayname ARGS((const char *str));
void set_array ARGS((const char *var, int reset, char **vals));
/* version.c */
/* vi.c: see edit.h */
/* Hack to avoid billions of compile warnings on SunOS 4.1.x */
#if defined(MUN) && defined(sun) && !defined(__svr4__)
extern void bcopy ARGS((const void *src, void *dst, size_t size));
extern int fclose ARGS((FILE *fp));
extern int fprintf ARGS((FILE *fp, const char *fmt, ...));
extern int fread ARGS((void *buf, int size, int num, FILE *fp));
extern int ioctl ARGS((int fd, int request, void *arg));
extern int killpg ARGS((int pgrp, int sig));
extern int nice ARGS((int n));
extern int readlink ARGS((const char *path, char *buf, int bufsize));
extern int setpgrp ARGS((int pid, int pgrp));
extern int strcasecmp ARGS((const char *s1, const char *s2));
extern int tolower ARGS((int));
extern int toupper ARGS((int));
/* Include files aren't included yet */
extern int getrlimit ARGS(( /* int resource, struct rlimit *rpl */ ));
extern int getrusage ARGS(( /* int who, struct rusage *rusage */ ));
extern int gettimeofday ARGS(( /* struct timeval *tv, struct timezone *tz */ ));
extern int setrlimit ARGS(( /* int resource, struct rlimit *rlp */ ));
extern int lstat ARGS(( /* const char *path, struct stat *buf */ ));
#endif

3838
sh.1tbl Normal file

File diff suppressed because it is too large Load Diff

747
sh.h Normal file
View File

@ -0,0 +1,747 @@
/* $OpenBSD: sh.h,v 1.12 2002/10/07 23:09:32 vincent Exp $ */
/*
* Public Domain Bourne/Korn shell
*/
/* $From: sh.h,v 1.2 1994/05/19 18:32:40 michael Exp michael $ */
#include "config.h" /* system and option configuration info */
#ifdef HAVE_PROTOTYPES
# define ARGS(args) args /* prototype declaration */
#else
# define ARGS(args) () /* K&R declaration */
#endif
/* Start of common headers */
#include <stdio.h>
#include <sys/types.h>
#include <setjmp.h>
#ifdef HAVE_STDDEF_H
# include <stddef.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#else
/* just a useful subset of what stdlib.h would have */
extern char * getenv ARGS((const char *));
extern void * malloc ARGS((size_t));
extern void * realloc ARGS((void *, size_t));
extern int free ARGS((void *));
extern int exit ARGS((int));
extern int rand ARGS((void));
extern void srand ARGS((unsigned int));
extern int atoi ARGS((const char *));
#endif /* HAVE_STDLIB_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#else
/* just a useful subset of what unistd.h would have */
extern int access ARGS((const char *, int));
extern int open ARGS((const char *, int, ...));
extern int creat ARGS((const char *, mode_t));
extern int read ARGS((int, char *, unsigned));
extern int write ARGS((int, const char *, unsigned));
extern off_t lseek ARGS((int, off_t, int));
extern int close ARGS((int));
extern int pipe ARGS((int []));
extern int dup2 ARGS((int, int));
extern int unlink ARGS((const char *));
extern int fork ARGS((void));
extern int execve ARGS((const char *, char * const[], char * const[]));
extern int chdir ARGS((const char *));
extern int kill ARGS((pid_t, int));
extern char *getcwd(); /* no ARGS here - differs on different machines */
extern int geteuid ARGS((void));
extern int readlink ARGS((const char *, char *, int));
extern int getegid ARGS((void));
extern int getpid ARGS((void));
extern int getppid ARGS((void));
extern unsigned int sleep ARGS((unsigned int));
extern int isatty ARGS((int));
# ifdef POSIX_PGRP
extern int getpgrp ARGS((void));
extern int setpgid ARGS((pid_t, pid_t));
# endif /* POSIX_PGRP */
# ifdef BSD_PGRP
extern int getpgrp ARGS((pid_t));
extern int setpgrp ARGS((pid_t, pid_t));
# endif /* BSD_PGRP */
# ifdef SYSV_PGRP
extern int getpgrp ARGS((void));
extern int setpgrp ARGS((void));
# endif /* SYSV_PGRP */
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
# define strchr index
# define strrchr rindex
#endif /* HAVE_STRING_H */
#ifndef HAVE_STRSTR
char *strstr ARGS((const char *s, const char *p));
#endif /* HAVE_STRSTR */
#ifndef HAVE_STRCASECMP
int strcasecmp ARGS((const char *s1, const char *s2));
int strncasecmp ARGS((const char *s1, const char *s2, int n));
#endif /* HAVE_STRCASECMP */
#ifdef HAVE_MEMORY_H
# include <memory.h>
#endif
#ifndef HAVE_MEMSET
# define memcpy(d, s, n) bcopy(s, d, n)
# define memcmp(s1, s2, n) bcmp(s1, s2, n)
void *memset ARGS((void *d, int c, size_t n));
#endif /* HAVE_MEMSET */
#ifndef HAVE_MEMMOVE
# ifdef HAVE_BCOPY
# define memmove(d, s, n) bcopy(s, d, n)
# else
void *memmove ARGS((void *d, const void *s, size_t n));
# endif
#endif /* HAVE_MEMMOVE */
#ifdef HAVE_PROTOTYPES
# include <stdarg.h>
# define SH_VA_START(va, argn) va_start(va, argn)
#else
# include <varargs.h>
# define SH_VA_START(va, argn) va_start(va)
#endif /* HAVE_PROTOTYPES */
#include <errno.h>
extern int errno;
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#else
# include <sys/file.h>
#endif /* HAVE_FCNTL_H */
#ifndef O_ACCMODE
# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
#endif /* !O_ACCMODE */
#ifndef F_OK /* access() arguments */
# define F_OK 0
# define X_OK 1
# define W_OK 2
# define R_OK 4
#endif /* !F_OK */
#ifndef SEEK_SET
# ifdef L_SET
# define SEEK_SET L_SET
# define SEEK_CUR L_INCR
# define SEEK_END L_XTND
# else /* L_SET */
# define SEEK_SET 0
# define SEEK_CUR 1
# define SEEK_END 2
# endif /* L_SET */
#endif /* !SEEK_SET */
/* Some machines (eg, FreeBSD 1.1.5) define CLK_TCK in limits.h
* (ksh_limval.h assumes limits has been included, if available)
*/
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif /* HAVE_LIMITS_H */
#include <signal.h>
#ifdef NSIG
# define SIGNALS NSIG
#else
# ifdef _MINIX
# define SIGNALS (_NSIG+1) /* _NSIG is # of signals used, excluding 0. */
# else
# ifdef _SIGMAX /* QNX */
# define SIGNALS _SIGMAX
# else /* _SIGMAX */
# define SIGNALS 32
# endif /* _SIGMAX */
# endif /* _MINIX */
#endif /* NSIG */
#ifndef SIGCHLD
# define SIGCHLD SIGCLD
#endif
/* struct sigaction.sa_flags is set to KSH_SA_FLAGS. Used to ensure
* system calls are interrupted
*/
#ifdef SA_INTERRUPT
# define KSH_SA_FLAGS SA_INTERRUPT
#else /* SA_INTERRUPT */
# define KSH_SA_FLAGS 0
#endif /* SA_INTERRUPT */
typedef RETSIGTYPE (*handler_t) ARGS((int)); /* signal handler */
#ifdef USE_FAKE_SIGACT
# include "sigact.h" /* use sjg's fake sigaction() */
#endif
#ifdef HAVE_PATHS_H
# include <paths.h>
#endif /* HAVE_PATHS_H */
#ifdef _PATH_DEFPATH
# define DEFAULT__PATH _PATH_DEFPATH
#else /* _PATH_DEFPATH */
# define DEFAULT__PATH DEFAULT_PATH
#endif /* _PATH_DEFPATH */
#ifndef offsetof
# define offsetof(type,id) ((size_t)&((type*)NULL)->id)
#endif
#ifndef HAVE_KILLPG
# define killpg(p, s) kill(-(p), (s))
#endif /* !HAVE_KILLPG */
/* Special cases for execve(2) */
#ifdef OS2
extern int ksh_execve(char *cmd, char **args, char **env, int flags);
#else /* OS2 */
# if defined(OS_ISC) && defined(_POSIX_SOURCE)
/* Kludge for ISC 3.2 (and other versions?) so programs will run correctly. */
# define ksh_execve(p, av, ev, flags) \
do { \
__setostype(0); \
execve(p, av, ev); \
__setostype(1); \
} while (0)
# else /* OS_ISC && _POSIX */
# define ksh_execve(p, av, ev, flags) execve(p, av, ev)
# endif /* OS_ISC && _POSIX */
#endif /* OS2 */
/* this is a hang-over from older versions of the os2 port */
#define ksh_dupbase(fd, base) fcntl(fd, F_DUPFD, base)
#ifdef HAVE_SIGSETJMP
# define ksh_sigsetjmp(env,sm) sigsetjmp((env), (sm))
# define ksh_siglongjmp(env,v) siglongjmp((env), (v))
# define ksh_jmp_buf sigjmp_buf
#else /* HAVE_SIGSETJMP */
# ifdef HAVE__SETJMP
# define ksh_sigsetjmp(env,sm) _setjmp(env)
# define ksh_siglongjmp(env,v) _longjmp((env), (v))
# else /* HAVE__SETJMP */
# define ksh_sigsetjmp(env,sm) setjmp(env)
# define ksh_siglongjmp(env,v) longjmp((env), (v))
# endif /* HAVE__SETJMP */
# define ksh_jmp_buf jmp_buf
#endif /* HAVE_SIGSETJMP */
#ifndef HAVE_DUP2
extern int dup2 ARGS((int, int));
#endif /* !HAVE_DUP2 */
/* Find a integer type that is at least 32 bits (or die) - SIZEOF_* defined
* by autoconf (assumes an 8 bit byte, but I'm not concerned).
* NOTE: INT32 may end up being more than 32 bits.
*/
#if SIZEOF_INT >= 4
# define INT32 int
#else /* SIZEOF_INT */
# if SIZEOF_LONG >= 4
# define INT32 long
# else /* SIZEOF_LONG */
#error cannot find 32 bit type...
# endif /* SIZEOF_LONG */
#endif /* SIZEOF_INT */
/* end of common headers */
/* Stop gcc and lint from complaining about possibly uninitialized variables */
#if defined(__GNUC__) || defined(lint)
# define UNINITIALIZED(var) var = 0
#else
# define UNINITIALIZED(var) var
#endif /* GNUC || lint */
/* some useful #defines */
#ifdef EXTERN
# define I__(i) = i
#else
# define I__(i)
# define EXTERN extern
# define EXTERN_DEFINED
#endif
#ifdef OS2
# define inDOS() (!(_emx_env & 0x200))
#endif
#ifndef EXECSHELL
/* shell to exec scripts (see also $SHELL initialization in main.c) */
# ifdef OS2
# define EXECSHELL (inDOS() ? "c:\\command.com" : "c:\\os2\\cmd.exe")
# define EXECSHELL_STR (inDOS() ? "COMSPEC" : "OS2_SHELL")
# else /* OS2 */
# define EXECSHELL "/bin/sh"
# define EXECSHELL_STR "EXECSHELL"
# endif /* OS2 */
#endif
/* ISABSPATH() means path is fully and completely specified,
* ISROOTEDPATH() means a .. as the first component is a no-op,
* ISRELPATH() means $PWD can be tacked on to get an absolute path.
*
* OS Path ISABSPATH ISROOTEDPATH ISRELPATH
* unix /foo yes yes no
* unix foo no no yes
* unix ../foo no no yes
* os2+cyg a:/foo yes yes no
* os2+cyg a:foo no no no
* os2+cyg /foo no yes no
* os2+cyg foo no no yes
* os2+cyg ../foo no no yes
* cyg //foo yes yes no
*/
#ifdef OS2
# define PATHSEP ';'
# define DIRSEP '/' /* even though \ is native */
# define DIRSEPSTR "\\"
# define ISDIRSEP(c) ((c) == '\\' || (c) == '/')
# define ISABSPATH(s) (((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2])))
# define ISROOTEDPATH(s) (ISDIRSEP((s)[0]) || ISABSPATH(s))
# define ISRELPATH(s) (!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0])))
# define FILECHCONV(c) (isascii(c) && isupper(c) ? tolower(c) : c)
# define FILECMP(s1, s2) stricmp(s1, s2)
# define FILENCMP(s1, s2, n) strnicmp(s1, s2, n)
extern char *ksh_strchr_dirsep(const char *path);
extern char *ksh_strrchr_dirsep(const char *path);
# define chdir _chdir2
# define getcwd _getcwd2
#else
# define PATHSEP ':'
# define DIRSEP '/'
# define DIRSEPSTR "/"
# define ISDIRSEP(c) ((c) == '/')
#ifdef __CYGWIN__
# define ISABSPATH(s) \
(((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2])) || ISDIRSEP((s)[0]))
# define ISRELPATH(s) (!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0])))
#else /* __CYGWIN__ */
# define ISABSPATH(s) ISDIRSEP((s)[0])
# define ISRELPATH(s) (!ISABSPATH(s))
#endif /* __CYGWIN__ */
# define ISROOTEDPATH(s) ISABSPATH(s)
# define FILECHCONV(c) c
# define FILECMP(s1, s2) strcmp(s1, s2)
# define FILENCMP(s1, s2, n) strncmp(s1, s2, n)
# define ksh_strchr_dirsep(p) strchr(p, DIRSEP)
# define ksh_strrchr_dirsep(p) strrchr(p, DIRSEP)
#endif
typedef int bool_t;
#define FALSE 0
#define TRUE 1
#define NELEM(a) (sizeof(a) / sizeof((a)[0]))
#define sizeofN(type, n) (sizeof(type) * (n))
#define BIT(i) (1<<(i)) /* define bit in flag */
/* Table flag type - needs > 16 and < 32 bits */
typedef INT32 Tflag;
#define NUFILE 32 /* Number of user-accessible files */
#define FDBASE 10 /* First file usable by Shell */
/* you're not going to run setuid shell scripts, are you? */
#define eaccess(path, mode) access(path, mode)
/* Make MAGIC a char that might be printed to make bugs more obvious, but
* not a char that is used often. Also, can't use the high bit as it causes
* portability problems (calling strchr(x, 0x80|'x') is error prone).
*/
#define MAGIC (7)/* prefix for *?[!{,} during expand */
#define ISMAGIC(c) ((unsigned char)(c) == MAGIC)
#define NOT '!' /* might use ^ (ie, [!...] vs [^..]) */
#define LINE 1024 /* input line size */
#define PATH 1024 /* pathname size (todo: PATH_MAX/pathconf()) */
#define ARRAYMAX 1023 /* max array index */
EXTERN const char *kshname; /* $0 */
EXTERN pid_t kshpid; /* $$, shell pid */
EXTERN pid_t procpid; /* pid of executing process */
EXTERN uid_t ksheuid; /* effective uid of shell */
EXTERN int exstat; /* exit status */
EXTERN int subst_exstat; /* exit status of last $(..)/`..` */
EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */
/*
* Area-based allocation built on malloc/free
*/
typedef struct Area {
struct link *freelist; /* free list */
} Area;
EXTERN Area aperm; /* permanent object space */
#define APERM &aperm
#define ATEMP &e->area
#ifdef MEM_DEBUG
# include "chmem.h" /* a debugging front end for malloc et. al. */
#endif /* MEM_DEBUG */
#ifdef KSH_DEBUG
# define kshdebug_init() kshdebug_init_()
# define kshdebug_printf(a) kshdebug_printf_ a
# define kshdebug_dump(a) kshdebug_dump_ a
#else /* KSH_DEBUG */
# define kshdebug_init()
# define kshdebug_printf(a)
# define kshdebug_dump(a)
#endif /* KSH_DEBUG */
/*
* parsing & execution environment
*/
EXTERN struct env {
short type; /* environment type - see below */
short flags; /* EF_* */
Area area; /* temporary allocation area */
struct block *loc; /* local variables and functions */
short *savefd; /* original redirected fd's */
struct env *oenv; /* link to previous environment */
ksh_jmp_buf jbuf; /* long jump back to env creator */
struct temp *temps; /* temp files */
} *e;
/* struct env.type values */
#define E_NONE 0 /* dummy environment */
#define E_PARSE 1 /* parsing command # */
#define E_FUNC 2 /* executing function # */
#define E_INCL 3 /* including a file via . # */
#define E_EXEC 4 /* executing command tree */
#define E_LOOP 5 /* executing for/while # */
#define E_ERRH 6 /* general error handler # */
/* # indicates env has valid jbuf (see unwind()) */
/* struct env.flag values */
#define EF_FUNC_PARSE BIT(0) /* function being parsed */
#define EF_BRKCONT_PASS BIT(1) /* set if E_LOOP must pass break/continue on */
#define EF_FAKE_SIGDIE BIT(2) /* hack to get info from unwind to quitenv */
/* Do breaks/continues stop at env type e? */
#define STOP_BRKCONT(t) ((t) == E_NONE || (t) == E_PARSE \
|| (t) == E_FUNC || (t) == E_INCL)
/* Do returns stop at env type e? */
#define STOP_RETURN(t) ((t) == E_FUNC || (t) == E_INCL)
/* values for ksh_siglongjmp(e->jbuf, 0) */
#define LRETURN 1 /* return statement */
#define LEXIT 2 /* exit statement */
#define LERROR 3 /* errorf() called */
#define LLEAVE 4 /* untrappable exit/error */
#define LINTR 5 /* ^C noticed */
#define LBREAK 6 /* break statement */
#define LCONTIN 7 /* continue statement */
#define LSHELL 8 /* return to interactive shell() */
#define LAEXPR 9 /* error in arithmetic expression */
/* option processing */
#define OF_CMDLINE 0x01 /* command line */
#define OF_SET 0x02 /* set builtin */
#define OF_SPECIAL 0x04 /* a special variable changing */
#define OF_INTERNAL 0x08 /* set internally by shell */
#define OF_ANY (OF_CMDLINE | OF_SET | OF_SPECIAL | OF_INTERNAL)
struct option {
const char *name; /* long name of option */
char c; /* character flag (if any) */
short flags; /* OF_* */
};
extern const struct option options[];
/*
* flags (the order of these enums MUST match the order in misc.c(options[]))
*/
enum sh_flag {
FEXPORT = 0, /* -a: export all */
#ifdef BRACE_EXPAND
FBRACEEXPAND, /* enable {} globbing */
#endif
FBGNICE, /* bgnice */
FCOMMAND, /* -c: (invocation) execute specified command */
#ifdef EMACS
FEMACS, /* emacs command editing */
#endif
FERREXIT, /* -e: quit on error */
#ifdef EMACS
FGMACS, /* gmacs command editing */
#endif
FIGNOREEOF, /* eof does not exit */
FTALKING, /* -i: interactive */
FKEYWORD, /* -k: name=value anywere */
FLOGIN, /* -l: a login shell */
FMARKDIRS, /* mark dirs with / in file name completion */
FMONITOR, /* -m: job control monitoring */
FNOCLOBBER, /* -C: don't overwrite existing files */
FNOEXEC, /* -n: don't execute any commands */
FNOGLOB, /* -f: don't do file globbing */
FNOHUP, /* -H: don't kill running jobs when login shell exits */
FNOLOG, /* don't save functions in history (ignored) */
#ifdef JOBS
FNOTIFY, /* -b: asynchronous job completion notification */
#endif
FNOUNSET, /* -u: using an unset var is an error */
FPHYSICAL, /* -o physical: don't do logical cd's/pwd's */
FPOSIX, /* -o posix: be posixly correct */
FPRIVILEGED, /* -p: use suid_profile */
FRESTRICTED, /* -r: restricted shell */
FSH, /* -o sh: favor sh behavour */
FSTDIN, /* -s: (invocation) parse stdin */
FTRACKALL, /* -h: create tracked aliases for all commands */
FVERBOSE, /* -v: echo input */
#ifdef VI
FVI, /* vi command editing */
FVIRAW, /* always read in raw mode (ignored) */
FVISHOW8, /* display chars with 8th bit set as is (versus M-) */
FVITABCOMPLETE, /* enable tab as file name completion char */
FVIESCCOMPLETE, /* enable ESC as file name completion in command mode */
#endif
FXTRACE, /* -x: execution trace */
FTALKING_I, /* (internal): initial shell was interactive */
FNFLAGS /* (place holder: how many flags are there) */
};
#define Flag(f) (shell_flags[(int) (f)])
EXTERN char shell_flags [FNFLAGS];
EXTERN char null [] I__(""); /* null value for variable */
EXTERN char space [] I__(" ");
EXTERN char newline [] I__("\n");
EXTERN char slash [] I__("/");
enum temp_type {
TT_HEREDOC_EXP, /* expanded heredoc */
TT_HIST_EDIT /* temp file used for history editing (fc -e) */
};
typedef enum temp_type Temp_type;
/* temp/heredoc files. The file is removed when the struct is freed. */
struct temp {
struct temp *next;
struct shf *shf;
int pid; /* pid of process parsed here-doc */
Temp_type type;
char *name;
};
/*
* stdio and our IO routines
*/
#define shl_spare (&shf_iob[0]) /* for c_read()/c_print() */
#define shl_stdout (&shf_iob[1])
#define shl_out (&shf_iob[2])
EXTERN int shl_stdout_ok;
/*
* trap handlers
*/
typedef struct trap {
int signal; /* signal number */
const char *name; /* short name */
const char *mess; /* descriptive name */
char *trap; /* trap command */
int volatile set; /* trap pending */
int flags; /* TF_* */
handler_t cursig; /* current handler (valid if TF_ORIG_* set) */
handler_t shtrap; /* shell signal handler */
} Trap;
/* values for Trap.flags */
#define TF_SHELL_USES BIT(0) /* shell uses signal, user can't change */
#define TF_USER_SET BIT(1) /* user has (tried to) set trap */
#define TF_ORIG_IGN BIT(2) /* original action was SIG_IGN */
#define TF_ORIG_DFL BIT(3) /* original action was SIG_DFL */
#define TF_EXEC_IGN BIT(4) /* restore SIG_IGN just before exec */
#define TF_EXEC_DFL BIT(5) /* restore SIG_DFL just before exec */
#define TF_DFL_INTR BIT(6) /* when received, default action is LINTR */
#define TF_TTY_INTR BIT(7) /* tty generated signal (see j_waitj) */
#define TF_CHANGED BIT(8) /* used by runtrap() to detect trap changes */
#define TF_FATAL BIT(9) /* causes termination if not trapped */
/* values for setsig()/setexecsig() flags argument */
#define SS_RESTORE_MASK 0x3 /* how to restore a signal before an exec() */
#define SS_RESTORE_CURR 0 /* leave current handler in place */
#define SS_RESTORE_ORIG 1 /* restore original handler */
#define SS_RESTORE_DFL 2 /* restore to SIG_DFL */
#define SS_RESTORE_IGN 3 /* restore to SIG_IGN */
#define SS_FORCE BIT(3) /* set signal even if original signal ignored */
#define SS_USER BIT(4) /* user is doing the set (ie, trap command) */
#define SS_SHTRAP BIT(5) /* trap for internal use (CHLD,ALRM,WINCH) */
#define SIGEXIT_ 0 /* for trap EXIT */
#define SIGERR_ SIGNALS /* for trap ERR */
EXTERN int volatile trap; /* traps pending? */
EXTERN int volatile intrsig; /* pending trap interrupts executing command */
EXTERN int volatile fatal_trap;/* received a fatal signal */
#ifndef FROM_TRAP_C
/* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */
extern Trap sigtraps[SIGNALS+1];
#endif /* !FROM_TRAP_C */
#ifdef KSH
/*
* TMOUT support
*/
/* values for ksh_tmout_state */
enum tmout_enum {
TMOUT_EXECUTING = 0, /* executing commands */
TMOUT_READING, /* waiting for input */
TMOUT_LEAVING /* have timed out */
};
EXTERN unsigned int ksh_tmout;
EXTERN enum tmout_enum ksh_tmout_state I__(TMOUT_EXECUTING);
#endif /* KSH */
/* For "You have stopped jobs" message */
EXTERN int really_exit;
/*
* fast character classes
*/
#define C_ALPHA BIT(0) /* a-z_A-Z */
#define C_DIGIT BIT(1) /* 0-9 */
#define C_LEX1 BIT(2) /* \0 \t\n|&;<>() */
#define C_VAR1 BIT(3) /* *@#!$-? */
#define C_IFSWS BIT(4) /* \t \n (IFS white space) */
#define C_SUBOP1 BIT(5) /* "=-+?" */
#define C_SUBOP2 BIT(6) /* "#%" */
#define C_IFS BIT(7) /* $IFS */
#define C_QUOTE BIT(8) /* \n\t"#$&'()*;<>?[\`| (needing quoting) */
extern short ctypes [];
#define ctype(c, t) !!(ctypes[(unsigned char)(c)]&(t))
#define letter(c) ctype(c, C_ALPHA)
#define digit(c) ctype(c, C_DIGIT)
#define letnum(c) ctype(c, C_ALPHA|C_DIGIT)
EXTERN int ifs0 I__(' '); /* for "$*" */
/* Argument parsing for built-in commands and getopts command */
/* Values for Getopt.flags */
#define GF_ERROR BIT(0) /* call errorf() if there is an error */
#define GF_PLUSOPT BIT(1) /* allow +c as an option */
#define GF_NONAME BIT(2) /* don't print argv[0] in errors */
/* Values for Getopt.info */
#define GI_MINUS BIT(0) /* an option started with -... */
#define GI_PLUS BIT(1) /* an option started with +... */
#define GI_MINUSMINUS BIT(2) /* arguments were ended with -- */
typedef struct {
int optind;
int uoptind;/* what user sees in $OPTIND */
char *optarg;
int flags; /* see GF_* */
int info; /* see GI_* */
unsigned int p; /* 0 or index into argv[optind - 1] */
char buf[2]; /* for bad option OPTARG value */
} Getopt;
EXTERN Getopt builtin_opt; /* for shell builtin commands */
EXTERN Getopt user_opt; /* parsing state for getopts builtin command */
#ifdef KSH
/* This for co-processes */
typedef INT32 Coproc_id; /* something that won't (realisticly) wrap */
struct coproc {
int read; /* pipe from co-process's stdout */
int readw; /* other side of read (saved temporarily) */
int write; /* pipe to co-process's stdin */
Coproc_id id; /* id of current output pipe */
int njobs; /* number of live jobs using output pipe */
void *job; /* 0 or job of co-process using input pipe */
};
EXTERN struct coproc coproc;
#endif /* KSH */
/* Used in jobs.c and by coprocess stuff in exec.c */
#ifdef JOB_SIGS
EXTERN sigset_t sm_default, sm_sigchld;
#endif /* JOB_SIGS */
extern const char ksh_version[];
/* name of called builtin function (used by error functions) */
EXTERN char *builtin_argv0;
EXTERN Tflag builtin_flag; /* flags of called builtin (SPEC_BI, etc.) */
/* current working directory, and size of memory allocated for same */
EXTERN char *current_wd;
EXTERN int current_wd_size;
#ifdef EDIT
/* Minimium required space to work with on a line - if the prompt leaves less
* space than this on a line, the prompt is truncated.
*/
# define MIN_EDIT_SPACE 7
/* Minimium allowed value for x_cols: 2 for prompt, 3 for " < " at end of line
*/
# define MIN_COLS (2 + MIN_EDIT_SPACE + 3)
EXTERN int x_cols I__(80); /* tty columns */
#else
# define x_cols 80 /* for pr_menu(exec.c) */
#endif
/* These to avoid bracket matching problems */
#define OPAREN '('
#define CPAREN ')'
#define OBRACK '['
#define CBRACK ']'
#define OBRACE '{'
#define CBRACE '}'
/* Determine the location of the system (common) profile */
#ifndef KSH_SYSTEM_PROFILE
# ifdef __NeXT
# define KSH_SYSTEM_PROFILE "/etc/profile.std"
# else /* __NeXT */
# define KSH_SYSTEM_PROFILE "/etc/profile"
# endif /* __NeXT */
#endif /* KSH_SYSTEM_PROFILE */
/* Used by v_evaluate() and setstr() to control action when error occurs */
#define KSH_UNWIND_ERROR 0 /* unwind the stack (longjmp) */
#define KSH_RETURN_ERROR 1 /* return 1/0 for success/failure */
#include "shf.h"
#include "table.h"
#include "tree.h"
#include "expand.h"
#include "lex.h"
#include "proto.h"
/* be sure not to interfere with anyone else's idea about EXTERN */
#ifdef EXTERN_DEFINED
# undef EXTERN_DEFINED
# undef EXTERN
#endif
#undef I__

1295
shf.c Normal file

File diff suppressed because it is too large Load Diff

86
shf.h Normal file
View File

@ -0,0 +1,86 @@
/* $OpenBSD: shf.h,v 1.2 1999/01/08 20:25:02 millert Exp $ */
#ifndef SHF_H
# define SHF_H
/*
* Shell file I/O routines
*/
#define SHF_BSIZE 512
#define shf_fileno(shf) ((shf)->fd)
#define shf_setfileno(shf,nfd) ((shf)->fd = (nfd))
#define shf_getc(shf) ((shf)->rnleft > 0 ? (shf)->rnleft--, *(shf)->rp++ : \
shf_getchar(shf))
#define shf_putc(c, shf) ((shf)->wnleft == 0 ? shf_putchar((c), (shf)) \
: ((shf)->wnleft--, *(shf)->wp++ = (c)))
#define shf_eof(shf) ((shf)->flags & SHF_EOF)
#define shf_error(shf) ((shf)->flags & SHF_ERROR)
#define shf_errno(shf) ((shf)->errno_)
#define shf_clearerr(shf) ((shf)->flags &= ~(SHF_EOF | SHF_ERROR))
/* Flags passed to shf_*open() */
#define SHF_RD 0x0001
#define SHF_WR 0x0002
#define SHF_RDWR (SHF_RD|SHF_WR)
#define SHF_ACCMODE 0x0003 /* mask */
#define SHF_GETFL 0x0004 /* use fcntl() to figure RD/WR flags */
#define SHF_UNBUF 0x0008 /* unbuffered I/O */
#define SHF_CLEXEC 0x0010 /* set close on exec flag */
#define SHF_MAPHI 0x0020 /* make fd > FDBASE (and close orig)
* (shf_open() only) */
#define SHF_DYNAMIC 0x0040 /* string: increase buffer as needed */
#define SHF_INTERRUPT 0x0080 /* EINTR in read/write causes error */
/* Flags used internally */
#define SHF_STRING 0x0100 /* a string, not a file */
#define SHF_ALLOCS 0x0200 /* shf and shf->buf were alloc()ed */
#define SHF_ALLOCB 0x0400 /* shf->buf was alloc()ed */
#define SHF_ERROR 0x0800 /* read()/write() error */
#define SHF_EOF 0x1000 /* read eof (sticky) */
#define SHF_READING 0x2000 /* currently reading: rnleft,rp valid */
#define SHF_WRITING 0x4000 /* currently writing: wnleft,wp valid */
struct shf {
int flags; /* see SHF_* */
unsigned char *rp; /* read: current position in buffer */
int rbsize; /* size of buffer (1 if SHF_UNBUF) */
int rnleft; /* read: how much data left in buffer */
unsigned char *wp; /* write: current position in buffer */
int wbsize; /* size of buffer (0 if SHF_UNBUF) */
int wnleft; /* write: how much space left in buffer */
unsigned char *buf; /* buffer */
int fd; /* file descriptor */
int errno_; /* saved value of errno after error */
int bsize; /* actual size of buf */
Area *areap; /* area shf/buf were allocated in */
};
extern struct shf shf_iob[];
struct shf *shf_open ARGS((const char *name, int oflags, int mode,
int sflags));
struct shf *shf_fdopen ARGS((int fd, int sflags, struct shf *shf));
struct shf *shf_reopen ARGS((int fd, int sflags, struct shf *shf));
struct shf *shf_sopen ARGS((char *buf, int bsize, int sflags,
struct shf *shf));
int shf_close ARGS((struct shf *shf));
int shf_fdclose ARGS((struct shf *shf));
char *shf_sclose ARGS((struct shf *shf));
int shf_finish ARGS((struct shf *shf));
int shf_flush ARGS((struct shf *shf));
int shf_seek ARGS((struct shf *shf, off_t where, int from));
int shf_read ARGS((char *buf, int bsize, struct shf *shf));
char *shf_getse ARGS((char *buf, int bsize, struct shf *shf));
int shf_getchar ARGS((struct shf *shf));
int shf_ungetc ARGS((int c, struct shf *shf));
int shf_putchar ARGS((int c, struct shf *shf));
int shf_puts ARGS((const char *s, struct shf *shf));
int shf_write ARGS((const char *buf, int nbytes, struct shf *shf));
int shf_fprintf ARGS((struct shf *shf, const char *fmt, ...));
int shf_snprintf ARGS((char *buf, int bsize, const char *fmt, ...));
char *shf_smprintf ARGS((const char *fmt, ...));
int shf_vfprintf ARGS((struct shf *, const char *fmt, va_list args));
#endif /* SHF_H */

56
siglist.in Normal file
View File

@ -0,0 +1,56 @@
# $OpenBSD: siglist.in,v 1.1.1.1 1996/08/14 06:19:11 downsj Exp $
#
# List of signals used to initialize ksh's signal table (see trap.c
# and siglist.sh).
#
# Note that if a system has multiple defines for the same signal
# (eg, SIGABRT vs SIGIOT, SIGCHLD vs SIGCLD), only the first one
# will be seen, so the order in this list is important.
#
HUP Hangup
INT Interrupt
QUIT Quit
ILL Illegal instruction
TRAP Trace trap
# before IOT (ABRT is posix and ABRT is sometimes the same as IOT)
ABRT Abort
IOT IOT instruction
EMT EMT trap
FPE Floating point exception
KILL Killed
# before BUS (linux doesn't really have a BUS, but defines it to UNUSED)
UNUSED Unused
BUS Bus error
SEGV Memory fault
SYS Bad system call
PIPE Broken pipe
ALRM Alarm clock
TERM Terminated
STKFLT Stack fault
IO I/O possible
XCPU CPU time limit exceeded
XFSZ File size limit exceeded
VTALRM Virtual timer expired
PROF Profiling timer expired
WINCH Window size change
LOST File lock lost
USR1 User defined signal 1
USR2 User defined signal 2
PWR Power-fail/Restart
POLL Pollable event occurred
STOP Stopped (signal)
TSTP Stopped
CONT Continued
# before CLD (CHLD is posix and CHLD is sometimes the same as CLD)
CHLD Child exited
CLD Child exited
TTIN Stopped (tty input)
TTOU Stopped (tty output)
INFO Information request
URG Urgent I/O condition
# Solaris (svr4?) signals
WAITING No runnable LWPs
LWP Inter-LWP signal
FREEZE Checkpoint freeze
THAW Checkpoint thaw
CANCEL Thread cancellation

42
siglist.sh Normal file
View File

@ -0,0 +1,42 @@
#!/bin/sh
# $OpenBSD: siglist.sh,v 1.4 1997/06/19 13:58:47 kstailey Exp $
#
# Script to generate a sorted, complete list of signals, suitable
# for inclusion in trap.c as array initializer.
#
set -e
in=tmpi$$.c
out=tmpo$$.c
ecode=1
trapsigs='0 1 2 13 15'
trap 'rm -f $in $out; trap 0; exit $ecode' $trapsigs
CPP="${1-cc -E}"
# The trap here to make up for a bug in bash (1.14.3(1)) that calls the trap
(trap $trapsigs;
echo '#include "sh.h"';
echo ' { QwErTy SIGNALS , "DUMMY" , "hook for number of signals" },';
sed -e '/^[ ]*#/d' -e 's/^[ ]*\([^ ][^ ]*\)[ ][ ]*\(.*[^ ]\)[ ]*$/#ifdef SIG\1\
{ QwErTy SIG\1 , "\1", "\2" },\
#endif/') > $in
$CPP $in > $out
sed -n 's/{ QwErTy/{/p' < $out | awk '{print NR, $0}' | sort +2n +0n |
sed 's/^[0-9]* //' |
awk 'BEGIN { last=0; nsigs=0; }
{
if ($2 ~ /^[0-9][0-9]*$/ && $3 == ",") {
n = $2;
if (n > 0 && n != last) {
while (++last < n) {
printf "\t{ %d , (char *) 0, `Signal %d` } ,\n", last, last;
}
print;
}
}
}' |
tr '`' '"' | grep -v '"DUMMY"'
ecode=0

947
syn.c Normal file
View File

@ -0,0 +1,947 @@
/* $OpenBSD: syn.c,v 1.13 2002/06/09 05:47:27 todd Exp $ */
/*
* shell parser (C version)
*/
#include "sh.h"
#include "c_test.h"
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
int start_line; /* line nesting began on */
};
static void yyparse ARGS((void));
static struct op *pipeline ARGS((int cf));
static struct op *andor ARGS((void));
static struct op *c_list ARGS((int multi));
static struct ioword *synio ARGS((int cf));
static void musthave ARGS((int c, int cf));
static struct op *nested ARGS((int type, int smark, int emark));
static struct op *get_command ARGS((int cf));
static struct op *dogroup ARGS((void));
static struct op *thenpart ARGS((void));
static struct op *elsepart ARGS((void));
static struct op *caselist ARGS((void));
static struct op *casepart ARGS((int endtok));
static struct op *function_body ARGS((char *name, int ksh_func));
static char ** wordlist ARGS((void));
static struct op *block ARGS((int type, struct op *t1, struct op *t2,
char **wp));
static struct op *newtp ARGS((int type));
static void syntaxerr ARGS((const char *what))
GCC_FUNC_ATTR(noreturn);
static void nesting_push ARGS((struct nesting_state *save, int tok));
static void nesting_pop ARGS((struct nesting_state *saved));
static int assign_command ARGS((char *s));
static int inalias ARGS((struct source *s));
#ifdef KSH
static int dbtestp_isa ARGS((Test_env *te, Test_meta meta));
static const char *dbtestp_getopnd ARGS((Test_env *te, Test_op op,
int do_eval));
static int dbtestp_eval ARGS((Test_env *te, Test_op op, const char *opnd1,
const char *opnd2, int do_eval));
static void dbtestp_error ARGS((Test_env *te, int offset, const char *msg));
#endif /* KSH */
static struct op *outtree; /* yyparse output */
static struct nesting_state nesting; /* \n changed to ; */
static int reject; /* token(cf) gets symbol again */
static int symbol; /* yylex value */
#define REJECT (reject = 1)
#define ACCEPT (reject = 0)
#define token(cf) \
((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
#define tpeek(cf) \
((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
static void
yyparse()
{
int c;
ACCEPT;
outtree = c_list(source->type == SSTRING);
c = tpeek(0);
if (c == 0 && !outtree)
outtree = newtp(TEOF);
else if (c != '\n' && c != 0)
syntaxerr((char *) 0);
}
static struct op *
pipeline(cf)
int cf;
{
register struct op *t, *p, *tl = NULL;
t = get_command(cf);
if (t != NULL) {
while (token(0) == '|') {
if ((p = get_command(CONTIN)) == NULL)
syntaxerr((char *) 0);
if (tl == NULL)
t = tl = block(TPIPE, t, p, NOWORDS);
else
tl = tl->right = block(TPIPE, tl->right, p, NOWORDS);
}
REJECT;
}
return (t);
}
static struct op *
andor()
{
register struct op *t, *p;
register int c;
t = pipeline(0);
if (t != NULL) {
while ((c = token(0)) == LOGAND || c == LOGOR) {
if ((p = pipeline(CONTIN)) == NULL)
syntaxerr((char *) 0);
t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
}
REJECT;
}
return (t);
}
static struct op *
c_list(multi)
int multi;
{
register struct op *t = NULL, *p, *tl = NULL;
register int c;
int have_sep;
while (1) {
p = andor();
/* Token has always been read/rejected at this point, so
* we don't worry about what flags to pass token()
*/
c = token(0);
have_sep = 1;
if (c == '\n' && (multi || inalias(source))) {
if (!p) /* ignore blank lines */
continue;
} else if (!p)
break;
else if (c == '&' || c == COPROC)
p = block(c == '&' ? TASYNC : TCOPROC,
p, NOBLOCK, NOWORDS);
else if (c != ';')
have_sep = 0;
if (!t)
t = p;
else if (!tl)
t = tl = block(TLIST, t, p, NOWORDS);
else
tl = tl->right = block(TLIST, tl->right, p, NOWORDS);
if (!have_sep)
break;
}
REJECT;
return t;
}
static struct ioword *
synio(cf)
int cf;
{
register struct ioword *iop;
int ishere;
if (tpeek(cf) != REDIR)
return NULL;
ACCEPT;
iop = yylval.iop;
ishere = (iop->flag&IOTYPE) == IOHERE;
musthave(LWORD, ishere ? HEREDELIM : 0);
if (ishere) {
iop->delim = yylval.cp;
if (*ident != 0) /* unquoted */
iop->flag |= IOEVAL;
if (herep >= &heres[HERES])
yyerror("too many <<'s\n");
*herep++ = iop;
} else
iop->name = yylval.cp;
return iop;
}
static void
musthave(c, cf)
int c, cf;
{
if ((token(cf)) != c)
syntaxerr((char *) 0);
}
static struct op *
nested(type, smark, emark)
int type, smark, emark;
{
register struct op *t;
struct nesting_state old_nesting;
nesting_push(&old_nesting, smark);
t = c_list(TRUE);
musthave(emark, KEYWORD|ALIAS);
nesting_pop(&old_nesting);
return (block(type, t, NOBLOCK, NOWORDS));
}
static struct op *
get_command(cf)
int cf;
{
register struct op *t;
register int c, iopn = 0, syniocf;
struct ioword *iop, **iops;
XPtrV args, vars;
struct nesting_state old_nesting;
iops = (struct ioword **) alloc(sizeofN(struct ioword *, NUFILE+1),
ATEMP);
XPinit(args, 16);
XPinit(vars, 16);
syniocf = KEYWORD|ALIAS;
switch (c = token(cf|KEYWORD|ALIAS|VARASN)) {
default:
REJECT;
afree((void*) iops, ATEMP);
XPfree(args);
XPfree(vars);
return NULL; /* empty line */
case LWORD:
case REDIR:
REJECT;
syniocf &= ~(KEYWORD|ALIAS);
t = newtp(TCOM);
t->lineno = source->line;
while (1) {
cf = (t->u.evalflags ? ARRAYVAR : 0)
| (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD);
switch (tpeek(cf)) {
case REDIR:
if (iopn >= NUFILE)
yyerror("too many redirections\n");
iops[iopn++] = synio(cf);
break;
case LWORD:
ACCEPT;
/* the iopn == 0 and XPsize(vars) == 0 are
* dubious but at&t ksh acts this way
*/
if (iopn == 0 && XPsize(vars) == 0
&& XPsize(args) == 0
&& assign_command(ident))
t->u.evalflags = DOVACHECK;
if ((XPsize(args) == 0 || Flag(FKEYWORD))
&& is_wdvarassign(yylval.cp))
XPput(vars, yylval.cp);
else
XPput(args, yylval.cp);
break;
case '(':
/* Check for "> foo (echo hi)", which at&t ksh
* allows (not POSIX, but not disallowed)
*/
afree(t, ATEMP);
if (XPsize(args) == 0 && XPsize(vars) == 0) {
ACCEPT;
goto Subshell;
}
/* Must be a function */
if (iopn != 0 || XPsize(args) != 1
|| XPsize(vars) != 0)
syntaxerr((char *) 0);
ACCEPT;
/*(*/
musthave(')', 0);
t = function_body(XPptrv(args)[0], FALSE);
goto Leave;
default:
goto Leave;
}
}
Leave:
break;
Subshell:
case '(':
t = nested(TPAREN, '(', ')');
break;
case '{': /*}*/
t = nested(TBRACE, '{', '}');
break;
#ifdef KSH
case MDPAREN:
{
static const char let_cmd[] = { CHAR, 'l', CHAR, 'e',
CHAR, 't', EOS };
/* Leave KEYWORD in syniocf (allow if (( 1 )) then ...) */
t = newtp(TCOM);
t->lineno = source->line;
ACCEPT;
XPput(args, wdcopy(let_cmd, ATEMP));
musthave(LWORD,LETEXPR);
XPput(args, yylval.cp);
break;
}
#endif /* KSH */
#ifdef KSH
case DBRACKET: /* [[ .. ]] */
/* Leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */
t = newtp(TDBRACKET);
ACCEPT;
{
Test_env te;
te.flags = TEF_DBRACKET;
te.pos.av = &args;
te.isa = dbtestp_isa;
te.getopnd = dbtestp_getopnd;
te.eval = dbtestp_eval;
te.error = dbtestp_error;
test_parse(&te);
}
break;
#endif /* KSH */
case FOR:
case SELECT:
t = newtp((c == FOR) ? TFOR : TSELECT);
musthave(LWORD, ARRAYVAR);
if (!is_wdvarname(yylval.cp, TRUE))
yyerror("%s: bad identifier\n",
c == FOR ? "for" : "select");
t->str = str_save(ident, ATEMP);
nesting_push(&old_nesting, c);
t->vars = wordlist();
t->left = dogroup();
nesting_pop(&old_nesting);
break;
case WHILE:
case UNTIL:
nesting_push(&old_nesting, c);
t = newtp((c == WHILE) ? TWHILE : TUNTIL);
t->left = c_list(TRUE);
t->right = dogroup();
nesting_pop(&old_nesting);
break;
case CASE:
t = newtp(TCASE);
musthave(LWORD, 0);
t->str = yylval.cp;
nesting_push(&old_nesting, c);
t->left = caselist();
nesting_pop(&old_nesting);
break;
case IF:
nesting_push(&old_nesting, c);
t = newtp(TIF);
t->left = c_list(TRUE);
t->right = thenpart();
musthave(FI, KEYWORD|ALIAS);
nesting_pop(&old_nesting);
break;
case BANG:
syniocf &= ~(KEYWORD|ALIAS);
t = pipeline(0);
if (t == (struct op *) 0)
syntaxerr((char *) 0);
t = block(TBANG, NOBLOCK, t, NOWORDS);
break;
case TIME:
syniocf &= ~(KEYWORD|ALIAS);
t = pipeline(0);
t = block(TTIME, t, NOBLOCK, NOWORDS);
break;
case FUNCTION:
musthave(LWORD, 0);
t = function_body(yylval.cp, TRUE);
break;
}
while ((iop = synio(syniocf)) != NULL) {
if (iopn >= NUFILE)
yyerror("too many redirections\n");
iops[iopn++] = iop;
}
if (iopn == 0) {
afree((void*) iops, ATEMP);
t->ioact = NULL;
} else {
iops[iopn++] = NULL;
iops = (struct ioword **) aresize((void*) iops,
sizeofN(struct ioword *, iopn), ATEMP);
t->ioact = iops;
}
if (t->type == TCOM || t->type == TDBRACKET) {
XPput(args, NULL);
t->args = (char **) XPclose(args);
XPput(vars, NULL);
t->vars = (char **) XPclose(vars);
} else {
XPfree(args);
XPfree(vars);
}
return t;
}
static struct op *
dogroup()
{
register int c;
register struct op *list;
c = token(CONTIN|KEYWORD|ALIAS);
/* A {...} can be used instead of do...done for for/select loops
* but not for while/until loops - we don't need to check if it
* is a while loop because it would have been parsed as part of
* the conditional command list...
*/
if (c == DO)
c = DONE;
else if (c == '{')
c = '}';
else
syntaxerr((char *) 0);
list = c_list(TRUE);
musthave(c, KEYWORD|ALIAS);
return list;
}
static struct op *
thenpart()
{
register struct op *t;
musthave(THEN, KEYWORD|ALIAS);
t = newtp(0);
t->left = c_list(TRUE);
if (t->left == NULL)
syntaxerr((char *) 0);
t->right = elsepart();
return (t);
}
static struct op *
elsepart()
{
register struct op *t;
switch (token(KEYWORD|ALIAS|VARASN)) {
case ELSE:
if ((t = c_list(TRUE)) == NULL)
syntaxerr((char *) 0);
return (t);
case ELIF:
t = newtp(TELIF);
t->left = c_list(TRUE);
t->right = thenpart();
return (t);
default:
REJECT;
}
return NULL;
}
static struct op *
caselist()
{
register struct op *t, *tl;
int c;
c = token(CONTIN|KEYWORD|ALIAS);
/* A {...} can be used instead of in...esac for case statements */
if (c == IN)
c = ESAC;
else if (c == '{')
c = '}';
else
syntaxerr((char *) 0);
t = tl = NULL;
while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */
struct op *tc = casepart(c);
if (tl == NULL)
t = tl = tc, tl->right = NULL;
else
tl->right = tc, tl = tc;
}
musthave(c, KEYWORD|ALIAS);
return (t);
}
static struct op *
casepart(endtok)
int endtok;
{
register struct op *t;
register int c;
XPtrV ptns;
XPinit(ptns, 16);
t = newtp(TPAT);
c = token(CONTIN|KEYWORD); /* no ALIAS here */
if (c != '(')
REJECT;
do {
musthave(LWORD, 0);
XPput(ptns, yylval.cp);
} while ((c = token(0)) == '|');
REJECT;
XPput(ptns, NULL);
t->vars = (char **) XPclose(ptns);
musthave(')', 0);
t->left = c_list(TRUE);
/* Note: Posix requires the ;; */
if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok)
musthave(BREAK, CONTIN|KEYWORD|ALIAS);
return (t);
}
static struct op *
function_body(name, ksh_func)
char *name;
int ksh_func; /* function foo { ... } vs foo() { .. } */
{
char *sname, *p;
struct op *t;
int old_func_parse;
sname = wdstrip(name);
/* Check for valid characters in name. posix and ksh93 say only
* allow [a-zA-Z_0-9] but this allows more as old pdksh's have
* allowed more (the following were never allowed:
* nul space nl tab $ ' " \ ` ( ) & | ; = < >
* C_QUOTE covers all but = and adds # [ ? *)
*/
for (p = sname; *p; p++)
if (ctype(*p, C_QUOTE) || *p == '=')
yyerror("%s: invalid function name\n", sname);
t = newtp(TFUNCT);
t->str = sname;
t->u.ksh_func = ksh_func;
t->lineno = source->line;
/* Note that POSIX allows only compound statements after foo(), sh and
* at&t ksh allow any command, go with the later since it shouldn't
* break anything. However, for function foo, at&t ksh only accepts
* an open-brace.
*/
if (ksh_func) {
musthave('{', CONTIN|KEYWORD|ALIAS); /* } */
REJECT;
}
old_func_parse = e->flags & EF_FUNC_PARSE;
e->flags |= EF_FUNC_PARSE;
if ((t->left = get_command(CONTIN)) == (struct op *) 0) {
/*
* Probably something like foo() followed by eof or ;.
* This is accepted by sh and ksh88.
* To make "typset -f foo" work reliably (so its output can
* be used as input), we pretend there is a colon here.
*/
t->left = newtp(TCOM);
t->left->args = (char **) alloc(sizeof(char *) * 2, ATEMP);
t->left->args[0] = alloc(sizeof(char) * 3, ATEMP);
t->left->args[0][0] = CHAR;
t->left->args[0][1] = ':';
t->left->args[0][2] = EOS;
t->left->args[1] = (char *) 0;
t->left->vars = (char **) alloc(sizeof(char *), ATEMP);
t->left->vars[0] = (char *) 0;
t->left->lineno = 1;
}
if (!old_func_parse)
e->flags &= ~EF_FUNC_PARSE;
return t;
}
static char **
wordlist()
{
register int c;
XPtrV args;
XPinit(args, 16);
/* Posix does not do alias expansion here... */
if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) {
if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */
REJECT;
return NULL;
}
while ((c = token(0)) == LWORD)
XPput(args, yylval.cp);
if (c != '\n' && c != ';')
syntaxerr((char *) 0);
if (XPsize(args) == 0) {
XPfree(args);
return NULL;
} else {
XPput(args, NULL);
return (char **) XPclose(args);
}
}
/*
* supporting functions
*/
static struct op *
block(type, t1, t2, wp)
int type;
struct op *t1, *t2;
char **wp;
{
register struct op *t;
t = newtp(type);
t->left = t1;
t->right = t2;
t->vars = wp;
return (t);
}
const struct tokeninfo {
const char *name;
short val;
short reserved;
} tokentab[] = {
/* Reserved words */
{ "if", IF, TRUE },
{ "then", THEN, TRUE },
{ "else", ELSE, TRUE },
{ "elif", ELIF, TRUE },
{ "fi", FI, TRUE },
{ "case", CASE, TRUE },
{ "esac", ESAC, TRUE },
{ "for", FOR, TRUE },
#ifdef KSH
{ "select", SELECT, TRUE },
#endif /* KSH */
{ "while", WHILE, TRUE },
{ "until", UNTIL, TRUE },
{ "do", DO, TRUE },
{ "done", DONE, TRUE },
{ "in", IN, TRUE },
{ "function", FUNCTION, TRUE },
{ "time", TIME, TRUE },
{ "{", '{', TRUE },
{ "}", '}', TRUE },
{ "!", BANG, TRUE },
#ifdef KSH
{ "[[", DBRACKET, TRUE },
#endif /* KSH */
/* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
{ "&&", LOGAND, FALSE },
{ "||", LOGOR, FALSE },
{ ";;", BREAK, FALSE },
#ifdef KSH
{ "((", MDPAREN, FALSE },
{ "|&", COPROC, FALSE },
#endif /* KSH */
/* and some special cases... */
{ "newline", '\n', FALSE },
{ 0 }
};
void
initkeywords()
{
register struct tokeninfo const *tt;
register struct tbl *p;
tinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */
for (tt = tokentab; tt->name; tt++) {
if (tt->reserved) {
p = tenter(&keywords, tt->name, hash(tt->name));
p->flag |= DEFINED|ISSET;
p->type = CKEYWD;
p->val.i = tt->val;
}
}
}
static void
syntaxerr(what)
const char *what;
{
char redir[6]; /* 2<<- is the longest redirection, I think */
const char *s;
struct tokeninfo const *tt;
int c;
if (!what)
what = "unexpected";
REJECT;
c = token(0);
Again:
switch (c) {
case 0:
if (nesting.start_token) {
c = nesting.start_token;
source->errline = nesting.start_line;
what = "unmatched";
goto Again;
}
/* don't quote the EOF */
yyerror("syntax error: unexpected EOF\n");
/*NOTREACHED*/
case LWORD:
s = snptreef((char *) 0, 32, "%S", yylval.cp);
break;
case REDIR:
s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
break;
default:
for (tt = tokentab; tt->name; tt++)
if (tt->val == c)
break;
if (tt->name)
s = tt->name;
else {
if (c > 0 && c < 256) {
redir[0] = c;
redir[1] = '\0';
} else
shf_snprintf(redir, sizeof(redir),
"?%d", c);
s = redir;
}
}
yyerror("syntax error: `%s' %s\n", s, what);
}
static void
nesting_push(save, tok)
struct nesting_state *save;
int tok;
{
*save = nesting;
nesting.start_token = tok;
nesting.start_line = source->line;
}
static void
nesting_pop(saved)
struct nesting_state *saved;
{
nesting = *saved;
}
static struct op *
newtp(type)
int type;
{
register struct op *t;
t = (struct op *) alloc(sizeof(*t), ATEMP);
t->type = type;
t->u.evalflags = 0;
t->args = t->vars = NULL;
t->ioact = NULL;
t->left = t->right = NULL;
t->str = NULL;
return (t);
}
struct op *
compile(s)
Source *s;
{
nesting.start_token = 0;
nesting.start_line = 0;
herep = heres;
source = s;
yyparse();
return outtree;
}
/* This kludge exists to take care of sh/at&t ksh oddity in which
* the arguments of alias/export/readonly/typeset have no field
* splitting, file globbing, or (normal) tilde expansion done.
* at&t ksh seems to do something similar to this since
* $ touch a=a; typeset a=[ab]; echo "$a"
* a=[ab]
* $ x=typeset; $x a=[ab]; echo "$a"
* a=a
* $
*/
static int
assign_command(s)
char *s;
{
char c = *s;
if (Flag(FPOSIX) || !*s)
return 0;
return (c == 'a' && strcmp(s, "alias") == 0)
|| (c == 'e' && strcmp(s, "export") == 0)
|| (c == 'r' && strcmp(s, "readonly") == 0)
|| (c == 't' && strcmp(s, "typeset") == 0);
}
/* Check if we are in the middle of reading an alias */
static int
inalias(s)
struct source *s;
{
for (; s && s->type == SALIAS; s = s->next)
if (!(s->flags & SF_ALIASEND))
return 1;
return 0;
}
#ifdef KSH
/* Order important - indexed by Test_meta values
* Note that ||, &&, ( and ) can't appear in as unquoted strings
* in normal shell input, so these can be interpreted unambiguously
* in the evaluation pass.
*/
static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS };
static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS };
static const char dbtest_not[] = { CHAR, '!', EOS };
static const char dbtest_oparen[] = { CHAR, '(', EOS };
static const char dbtest_cparen[] = { CHAR, ')', EOS };
const char *const dbtest_tokens[] = {
dbtest_or, dbtest_and, dbtest_not,
dbtest_oparen, dbtest_cparen
};
const char db_close[] = { CHAR, ']', CHAR, ']', EOS };
const char db_lthan[] = { CHAR, '<', EOS };
const char db_gthan[] = { CHAR, '>', EOS };
/* Test if the current token is a whatever. Accepts the current token if
* it is. Returns 0 if it is not, non-zero if it is (in the case of
* TM_UNOP and TM_BINOP, the returned value is a Test_op).
*/
static int
dbtestp_isa(te, meta)
Test_env *te;
Test_meta meta;
{
int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
int uqword = 0;
char *save = (char *) 0;
int ret = 0;
/* unquoted word? */
uqword = c == LWORD && *ident;
if (meta == TM_OR)
ret = c == LOGOR;
else if (meta == TM_AND)
ret = c == LOGAND;
else if (meta == TM_NOT)
ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0;
else if (meta == TM_OPAREN)
ret = c == '(' /*)*/;
else if (meta == TM_CPAREN)
ret = c == /*(*/ ')';
else if (meta == TM_UNOP || meta == TM_BINOP) {
if (meta == TM_BINOP && c == REDIR
&& (yylval.iop->flag == IOREAD
|| yylval.iop->flag == IOWRITE))
{
ret = 1;
save = wdcopy(yylval.iop->flag == IOREAD ?
db_lthan : db_gthan, ATEMP);
} else if (uqword && (ret = (int) test_isop(te, meta, ident)))
save = yylval.cp;
} else /* meta == TM_END */
ret = uqword && strcmp(yylval.cp, db_close) == 0;
if (ret) {
ACCEPT;
if (meta != TM_END) {
if (!save)
save = wdcopy(dbtest_tokens[(int) meta], ATEMP);
XPput(*te->pos.av, save);
}
}
return ret;
}
static const char *
dbtestp_getopnd(te, op, do_eval)
Test_env *te;
Test_op op;
int do_eval;
{
int c = tpeek(ARRAYVAR);
if (c != LWORD)
return (const char *) 0;
ACCEPT;
XPput(*te->pos.av, yylval.cp);
return null;
}
static int
dbtestp_eval(te, op, opnd1, opnd2, do_eval)
Test_env *te;
Test_op op;
const char *opnd1;
const char *opnd2;
int do_eval;
{
return 1;
}
static void
dbtestp_error(te, offset, msg)
Test_env *te;
int offset;
const char *msg;
{
te->flags |= TEF_ERROR;
if (offset < 0) {
REJECT;
/* Kludgy to say the least... */
symbol = LWORD;
yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av)
+ offset);
}
syntaxerr(msg);
}
#endif /* KSH */

240
table.c Normal file
View File

@ -0,0 +1,240 @@
/* $OpenBSD: table.c,v 1.5 1999/01/10 17:55:03 millert Exp $ */
/*
* dynamic hashed associative table for commands and variables
*/
#include "sh.h"
#define INIT_TBLS 8 /* initial table size (power of 2) */
static void texpand ARGS((struct table *tp, int nsize));
static int tnamecmp ARGS((void *p1, void *p2));
unsigned int
hash(n)
register const char * n;
{
register unsigned int h = 0;
while (*n != '\0')
h = 2*h + *n++;
return h * 32821; /* scatter bits */
}
void
tinit(tp, ap, tsize)
register struct table *tp;
register Area *ap;
int tsize;
{
tp->areap = ap;
tp->tbls = NULL;
tp->size = tp->nfree = 0;
if (tsize)
texpand(tp, tsize);
}
static void
texpand(tp, nsize)
register struct table *tp;
int nsize;
{
register int i;
register struct tbl *tblp, **p;
register struct tbl **ntblp, **otblp = tp->tbls;
int osize = tp->size;
ntblp = (struct tbl**) alloc(sizeofN(struct tbl *, nsize), tp->areap);
for (i = 0; i < nsize; i++)
ntblp[i] = NULL;
tp->size = nsize;
tp->nfree = 8*nsize/10; /* table can get 80% full */
tp->tbls = ntblp;
if (otblp == NULL)
return;
for (i = 0; i < osize; i++)
if ((tblp = otblp[i]) != NULL) {
if ((tblp->flag&DEFINED)) {
for (p = &ntblp[hash(tblp->name)
& (tp->size-1)];
*p != NULL; p--)
if (p == ntblp) /* wrap */
p += tp->size;
*p = tblp;
tp->nfree--;
} else if (!(tblp->flag & FINUSE)) {
afree((void*)tblp, tp->areap);
}
}
afree((void*)otblp, tp->areap);
}
struct tbl *
tsearch(tp, n, h)
register struct table *tp; /* table */
register const char *n; /* name to enter */
unsigned int h; /* hash(n) */
{
register struct tbl **pp, *p;
if (tp->size == 0)
return NULL;
/* search for name in hashed table */
for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
if (*p->name == *n && strcmp(p->name, n) == 0
&& (p->flag&DEFINED))
return p;
if (pp == tp->tbls) /* wrap */
pp += tp->size;
}
return NULL;
}
struct tbl *
tenter(tp, n, h)
register struct table *tp; /* table */
register const char *n; /* name to enter */
unsigned int h; /* hash(n) */
{
register struct tbl **pp, *p;
register int len;
if (tp->size == 0)
texpand(tp, INIT_TBLS);
Search:
/* search for name in hashed table */
for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
if (*p->name == *n && strcmp(p->name, n) == 0)
return p; /* found */
if (pp == tp->tbls) /* wrap */
pp += tp->size;
}
if (tp->nfree <= 0) { /* too full */
texpand(tp, 2*tp->size);
goto Search;
}
/* create new tbl entry */
len = strlen(n) + 1;
p = (struct tbl *) alloc(offsetof(struct tbl, name[0]) + len,
tp->areap);
p->flag = 0;
p->type = 0;
p->areap = tp->areap;
p->u2.field = 0;
p->u.array = (struct tbl *)0;
memcpy(p->name, n, len);
/* enter in tp->tbls */
tp->nfree--;
*pp = p;
return p;
}
void
tdelete(p)
register struct tbl *p;
{
p->flag = 0;
}
void
twalk(ts, tp)
struct tstate *ts;
struct table *tp;
{
ts->left = tp->size;
ts->next = tp->tbls;
}
struct tbl *
tnext(ts)
struct tstate *ts;
{
while (--ts->left >= 0) {
struct tbl *p = *ts->next++;
if (p != NULL && (p->flag&DEFINED))
return p;
}
return NULL;
}
static int
tnamecmp(p1, p2)
void *p1, *p2;
{
return strcmp(((struct tbl *)p1)->name, ((struct tbl *)p2)->name);
}
struct tbl **
tsort(tp)
register struct table *tp;
{
register int i;
register struct tbl **p, **sp, **dp;
p = (struct tbl **)alloc(sizeofN(struct tbl *, tp->size+1), ATEMP);
sp = tp->tbls; /* source */
dp = p; /* dest */
for (i = 0; i < tp->size; i++)
if ((*dp = *sp++) != NULL && (((*dp)->flag&DEFINED) ||
((*dp)->flag&ARRAY)))
dp++;
i = dp - p;
qsortp((void**)p, (size_t)i, tnamecmp);
p[i] = NULL;
return p;
}
#ifdef PERF_DEBUG /* performance debugging */
void tprintinfo ARGS((struct table *tp));
void
tprintinfo(tp)
struct table *tp;
{
struct tbl *te;
char *n;
unsigned int h;
int ncmp;
int totncmp = 0, maxncmp = 0;
int nentries = 0;
struct tstate ts;
shellf("table size %d, nfree %d\n", tp->size, tp->nfree);
shellf(" Ncmp name\n");
twalk(&ts, tp);
while ((te = tnext(&ts))) {
register struct tbl **pp, *p;
h = hash(n = te->name);
ncmp = 0;
/* taken from tsearch() and added counter */
for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp); pp--) {
ncmp++;
if (*p->name == *n && strcmp(p->name, n) == 0
&& (p->flag&DEFINED))
break; /* return p; */
if (pp == tp->tbls) /* wrap */
pp += tp->size;
}
shellf(" %4d %s\n", ncmp, n);
totncmp += ncmp;
nentries++;
if (ncmp > maxncmp)
maxncmp = ncmp;
}
if (nentries)
shellf(" %d entries, worst ncmp %d, avg ncmp %d.%02d\n",
nentries, maxncmp,
totncmp / nentries,
(totncmp % nentries) * 100 / nentries);
}
#endif /* PERF_DEBUG */

183
table.h Normal file
View File

@ -0,0 +1,183 @@
/* $OpenBSD: table.h,v 1.5 1999/06/15 01:18:36 millert Exp $ */
/* $From: table.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */
/*
* generic hashed associative table for commands and variables.
*/
struct table {
Area *areap; /* area to allocate entries */
short size, nfree; /* hash size (always 2^^n), free entries */
struct tbl **tbls; /* hashed table items */
};
struct tbl { /* table item */
Tflag flag; /* flags */
int type; /* command type (see below), base (if INTEGER),
* or offset from val.s of value (if EXPORT) */
Area *areap; /* area to allocate from */
union {
char *s; /* string */
long i; /* integer */
int (*f) ARGS((char **)); /* int function */
struct op *t; /* "function" tree */
} val; /* value */
int index; /* index for an array */
union {
int field; /* field with for -L/-R/-Z */
int errno_; /* CEXEC/CTALIAS */
} u2;
union {
struct tbl *array; /* array values */
char *fpath; /* temporary path to undef function */
} u;
char name[4]; /* name -- variable length */
};
/* common flag bits */
#define ALLOC BIT(0) /* val.s has been allocated */
#define DEFINED BIT(1) /* is defined in block */
#define ISSET BIT(2) /* has value, vp->val.[si] */
#define EXPORT BIT(3) /* exported variable/function */
#define TRACE BIT(4) /* var: user flagged, func: execution tracing */
/* (start non-common flags at 8) */
/* flag bits used for variables */
#define SPECIAL BIT(8) /* PATH, IFS, SECONDS, etc */
#define INTEGER BIT(9) /* val.i contains integer value */
#define RDONLY BIT(10) /* read-only variable */
#define LOCAL BIT(11) /* for local typeset() */
#define ARRAY BIT(13) /* array */
#define LJUST BIT(14) /* left justify */
#define RJUST BIT(15) /* right justify */
#define ZEROFIL BIT(16) /* 0 filled if RJUSTIFY, strip 0s if LJUSTIFY */
#define LCASEV BIT(17) /* convert to lower case */
#define UCASEV_AL BIT(18)/* convert to upper case / autoload function */
#define INT_U BIT(19) /* unsigned integer */
#define INT_L BIT(20) /* long integer (no-op) */
#define IMPORT BIT(21) /* flag to typeset(): no arrays, must have = */
#define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */
#define EXPRINEVAL BIT(23) /* contents currently being evaluated */
#define EXPRLVALUE BIT(24) /* useable as lvalue (temp flag) */
/* flag bits used for taliases/builtins/aliases/keywords/functions */
#define KEEPASN BIT(8) /* keep command assignments (eg, var=x cmd) */
#define FINUSE BIT(9) /* function being executed */
#define FDELETE BIT(10) /* function deleted while it was executing */
#define FKSH BIT(11) /* function defined with function x (vs x()) */
#define SPEC_BI BIT(12) /* a POSIX special builtin */
#define REG_BI BIT(13) /* a POSIX regular builtin */
/* Attributes that can be set by the user (used to decide if an unset param
* should be repoted by set/typeset). Does not include ARRAY or LOCAL.
*/
#define USERATTRIB (EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL\
|LCASEV|UCASEV_AL|INT_U|INT_L)
/* command types */
#define CNONE 0 /* undefined */
#define CSHELL 1 /* built-in */
#define CFUNC 2 /* function */
#define CEXEC 4 /* executable command */
#define CALIAS 5 /* alias */
#define CKEYWD 6 /* keyword */
#define CTALIAS 7 /* tracked alias */
/* Flags for findcom()/comexec() */
#define FC_SPECBI BIT(0) /* special builtin */
#define FC_FUNC BIT(1) /* function builtin */
#define FC_REGBI BIT(2) /* regular builtin */
#define FC_UNREGBI BIT(3) /* un-regular builtin (!special,!regular) */
#define FC_BI (FC_SPECBI|FC_REGBI|FC_UNREGBI)
#define FC_PATH BIT(4) /* do path search */
#define FC_DEFPATH BIT(5) /* use default path in path search */
#define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */
#define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */
#define AI_ARGV(a, i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
#define AI_ARGC(a) ((a).argc_ - (a).skip)
/* Argument info. Used for $#, $* for shell, functions, includes, etc. */
struct arg_info {
int flags; /* AF_* */
char **argv;
int argc_;
int skip; /* first arg is argv[0], second is argv[1 + skip] */
};
/*
* activation record for function blocks
*/
struct block {
Area area; /* area to allocate things */
/*struct arg_info argi;*/
char **argv;
int argc;
int flags; /* see BF_* */
struct table vars; /* local variables */
struct table funs; /* local functions */
Getopt getopts_state;
#if 1
char * error; /* error handler */
char * exit; /* exit handler */
#else
Trap error, exit;
#endif
struct block *next; /* enclosing block */
};
/* Values for struct block.flags */
#define BF_DOGETOPTS BIT(0) /* save/restore getopts state */
/*
* Used by twalk() and tnext() routines.
*/
struct tstate {
int left;
struct tbl **next;
};
EXTERN struct table taliases; /* tracked aliases */
EXTERN struct table builtins; /* built-in commands */
EXTERN struct table aliases; /* aliases */
EXTERN struct table keywords; /* keywords */
EXTERN struct table homedirs; /* homedir() cache */
struct builtin {
const char *name;
int (*func) ARGS((char **));
};
/* these really are externs! Look in table.c for them */
extern const struct builtin shbuiltins [], kshbuiltins [];
/* var spec values */
#define V_NONE 0
#define V_PATH 1
#define V_IFS 2
#define V_SECONDS 3
#define V_OPTIND 4
#define V_MAIL 5
#define V_MAILPATH 6
#define V_MAILCHECK 7
#define V_RANDOM 8
#define V_HISTSIZE 9
#define V_HISTFILE 10
#define V_VISUAL 11
#define V_EDITOR 12
#define V_COLUMNS 13
#define V_POSIXLY_CORRECT 14
#define V_TMOUT 15
#define V_TMPDIR 16
#define V_LINENO 17
/* values for set_prompt() */
#define PS1 0 /* command */
#define PS2 1 /* command continuation */
EXTERN char *path; /* copy of either PATH or def_path */
EXTERN const char *def_path; /* path to use if PATH not set */
EXTERN char *tmpdir; /* TMPDIR value */
EXTERN const char *prompt;
EXTERN int cur_prompt; /* PS1 or PS2 */
EXTERN int current_lineno; /* LINENO value */

22
tests/README Normal file
View File

@ -0,0 +1,22 @@
Tests can be assigned categories to restrict what program they
are applied to (eg, pdksh, ksh88, etc.). The following are
a list of names to be used for various shells (to keep things
consistent):
sh generic any v7 bourne shell like thing
sh-v generic any system V bourne shell like thing
ksh generic any ksh
posix generic basic posix shell
posix-upu generic `user portability utility' options
sh-v7 specific the real v7 bourne shell
sh-sysv specific the real sysv bourne shell
pdksh specific public domain ksh
ksh88 specific at&t ksh88
ksh93 specific at&t ksh93
bash specific GNU bourne-again shell
The idea is to categorize all the tests according to the `best match'
(most generic thing). All generics that apply should be specified.
Generally, at most one specific shell will be given.
At the moment, most (all) tests have not been categorized (any volunteers?).

112
tests/alias.t Normal file
View File

@ -0,0 +1,112 @@
name: alias-1
description:
Check that recursion is detected/avoided in aliases.
stdin:
alias fooBar=fooBar
fooBar
exit 0
expected-stderr-pattern:
/fooBar.*not found.*/
---
name: alias-2
description:
Check that recursion is detected/avoided in aliases.
stdin:
alias fooBar=barFoo
alias barFoo=fooBar
fooBar
barFoo
exit 0
expected-stderr-pattern:
/fooBar.*not found.*\n.*barFoo.*not found/
---
name: alias-3
description:
Check that recursion is detected/avoided in aliases.
stdin:
alias Echo='echo '
alias fooBar=barFoo
alias barFoo=fooBar
Echo fooBar
unalias barFoo
Echo fooBar
expected-stdout:
fooBar
barFoo
---
name: alias-4
description:
Check that alias expansion isn't done on keywords (in keyword
postitions).
stdin:
alias Echo='echo '
alias while=While
while false; do echo hi ; done
Echo while
expected-stdout:
While
---
name: alias-5
description:
Check that alias expansion done after alias with trailing space.
stdin:
alias Echo='echo '
alias foo='bar stuff '
alias bar='Bar1 Bar2 '
alias stuff='Stuff'
alias blah='Blah'
Echo foo blah
expected-stdout:
Bar1 Bar2 Stuff Blah
---
name: alias-6
description:
Check that alias expansion done after alias with trailing space.
stdin:
alias Echo='echo '
alias foo='bar bar'
alias bar='Bar '
alias blah=Blah
Echo foo blah
expected-stdout:
Bar Bar Blah
---
name: alias-7
description:
Check that alias expansion done after alias with trailing space
after a keyword.
stdin:
alias X='case '
alias Y=Z
X Y in 'Y') echo is y ;; Z) echo is z ; esac
expected-stdout:
is z
---
name: alias-8
description:
Check that newlines in an alias don't cause the command to be lost.
stdin:
alias foo='
echo hi
echo there
'
foo
expected-stdout:
hi
there
---

79
tests/arith.t Normal file
View File

@ -0,0 +1,79 @@
name: arith-lazy-1
description:
Check that only one side of ternary operator is evaluated
stdin:
x=i+=2
y=j+=2
typeset -i i=1 j=1
echo $((1 ? 20 : (x+=2)))
echo $i,$x
echo $((0 ? (y+=2) : 30))
echo $j,$y
expected-stdout:
20
1,i+=2
30
1,j+=2
---
name: arith-lazy-2
description:
Check that assignments not done on non-evaluated side of ternary
operator
stdin:
x=i+=2
y=j+=2
typeset -i i=1 j=1
echo $((1 ? 20 : (x+=2)))
echo $i,$x
echo $((0 ? (y+=2) : 30))
echo $i,$y
expected-stdout:
20
1,i+=2
30
1,j+=2
---
name: arith-ternary-prec-1
description:
Check precidance of ternary operator vs assignment
stdin:
typeset -i x=2
y=$((1 ? 20 : x+=2))
expected-exit: e != 0
expected-stderr-pattern:
/.*:.*1 \? 20 : x\+=2.*lvalue.*\n$/
---
name: arith-ternary-prec-2
description:
Check precidance of ternary operator vs assignment
stdin:
typeset -i x=2
echo $((0 ? x+=2 : 20))
expected-stdout:
20
---
name: arith-div-assoc-1
description:
Check associativity of division operator
stdin:
echo $((20 / 2 / 2))
expected-stdout:
5
---
name: arith-assop-assoc-1
description:
Check associativity of assignment-operator operator
stdin:
typeset -i i=1 j=2 k=3
echo $((i += j += k))
echo $i,$j,$k
expected-stdout:
6
6,5,3
---

341
tests/bksl-nl.t Normal file
View File

@ -0,0 +1,341 @@
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
#
# These tests deal with how \newline is handled in various situations. The
# first group of tests are places where it shouldn't be collapsed, the next
# group of tests are places where it should be collapsed.
#
name: bksl-nl-ign-1
description:
Check that \newline is not collasped after #
stdin:
echo hi #there \
echo folks
expected-stdout:
hi
folks
---
name: bksl-nl-ign-2
description:
Check that \newline is not collasped inside single quotes
stdin:
echo 'hi \
there'
echo folks
expected-stdout:
hi \
there
folks
---
name: bksl-nl-ign-3
description:
Check that \newline is not collasped inside single quotes
stdin:
cat << \EOF
hi \
there
EOF
expected-stdout:
hi \
there
---
name: blsk-nl-ign-4
description:
Check interaction of aliases, single quotes and here-documents
with backslash-newline
(don't know what posix has to say about this)
stdin:
a=2
alias x='echo hi
cat << "EOF"
foo\
bar
some'
x
more\
stuff$a
EOF
expected-stdout:
hi
foo\
bar
some
more\
stuff$a
---
name: blsk-nl-ign-5
description:
Check what happens with backslash at end of input
(the old bourne shell trashes them; so do we)
stdin: !
echo `echo foo\\`bar
echo hi\
expected-stdout:
foobar
hi
---
#
# Places \newline should be collapsed
#
name: bksl-nl-1
description:
Check that \newline is collasped before, in the middle of, and
after words
stdin:
\
echo hi\
There, \
folks
expected-stdout:
hiThere, folks
---
name: bksl-nl-2
description:
Check that \newline is collasped in $ sequences
(ksh93 fails this)
stdin:
a=12
ab=19
echo $\
a
echo $a\
b
echo $\
{a}
echo ${a\
b}
echo ${ab\
}
expected-stdout:
12
19
12
19
19
---
name: bksl-nl-3
description:
Check that \newline is collasped in $(..) and `...` sequences
(ksh93 fails this)
stdin:
echo $\
(echo foobar1)
echo $(\
echo foobar2)
echo $(echo foo\
bar3)
echo $(echo foobar4\
)
echo `
echo stuff1`
echo `echo st\
uff2`
expected-stdout:
foobar1
foobar2
foobar3
foobar4
stuff1
stuff2
---
name: bksl-nl-4
description:
Check that \newline is collasped in $((..)) sequences
(ksh93 fails this)
stdin:
echo $\
((1+2))
echo $(\
(1+2+3))
echo $((\
1+2+3+4))
echo $((1+\
2+3+4+5))
echo $((1+2+3+4+5+6)\
)
expected-stdout:
3
6
10
15
21
---
name: bksl-nl-5
description:
Check that \newline is collasped in double quoted strings
stdin:
echo "\
hi"
echo "foo\
bar"
echo "folks\
"
expected-stdout:
hi
foobar
folks
---
name: bksl-nl-6
description:
Check that \newline is collasped in here document delimiters
(ksh93 fails second part of this)
stdin:
a=12
cat << EO\
F
a=$a
foo\
bar
EOF
cat << E_O_F
foo
E_O_\
F
echo done
expected-stdout:
a=12
foobar
foo
done
---
name: bksl-nl-7
description:
Check that \newline is collasped in double-quoted here-document
delimiter.
stdin:
a=12
cat << "EO\
F"
a=$a
foo\
bar
EOF
echo done
expected-stdout:
a=$a
foo\
bar
done
---
name: bksl-nl-8
description:
Check that \newline is collasped in various 2+ character tokens
delimiter.
(ksh93 fails this)
stdin:
echo hi &\
& echo there
echo foo |\
| echo bar
cat <\
< EOF
stuff
EOF
cat <\
<\
- EOF
more stuff
EOF
cat <<\
EOF
abcdef
EOF
echo hi >\
> /dev/null
echo $?
i=1
case $i in
(\
x|\
1\
) echo hi;\
;
(*) echo oops
esac
expected-stdout:
hi
there
foo
stuff
more stuff
abcdef
0
hi
---
name: blsk-nl-9
description:
Check that \ at the end of an alias is collapsed when followed
by a newline
(don't know what posix has to say about this)
stdin:
alias x='echo hi\'
x
echo there
expected-stdout:
hiecho there
---
name: blsk-nl-10
description:
Check that \newline in a keyword is collapsed
stdin:
i\
f true; then\
echo pass; el\
se echo fail; fi
expected-stdout:
pass
---
#
# Places \newline should be collapsed (ksh extensions)
#
name: blsk-nl-ksh-1
description:
Check that \newline is collapsed in extended globbing
(ksh93 fails this)
stdin:
xxx=foo
case $xxx in
(f*\
(\
o\
)\
) echo ok ;;
*) echo bad
esac
expected-stdout:
ok
---
name: blsk-nl-ksh-2
description:
Check that \newline is collapsed in ((...)) expressions
(ksh93 fails this)
stdin:
i=1
(\
(\
i=i+2\
)\
)
echo $i
expected-stdout:
3
---

195
tests/brkcont.t Normal file
View File

@ -0,0 +1,195 @@
name: break-1
description:
See if break breaks out of loops
stdin:
for i in a b c; do echo $i; break; echo bad-$i; done
echo end-1
for i in a b c; do echo $i; break 1; echo bad-$i; done
echo end-2
for i in a b c; do
for j in x y z; do
echo $i:$j
break
echo bad-$i
done
echo end-$i
done
echo end-3
expected-stdout:
a
end-1
a
end-2
a:x
end-a
b:x
end-b
c:x
end-c
end-3
---
name: break-2
description:
See if break breaks out of nested loops
stdin:
for i in a b c; do
for j in x y z; do
echo $i:$j
break 2
echo bad-$i
done
echo end-$i
done
echo end
expected-stdout:
a:x
end
---
name: break-3
description:
What if break used outside of any loops
(ksh88,ksh93 don't print error messages here)
stdin:
break
expected-stderr-pattern:
/.*break.*/
---
name: break-4
description:
What if break N used when only N-1 loops
(ksh88,ksh93 don't print error messages here)
stdin:
for i in a b c; do echo $i; break 2; echo bad-$i; done
echo end
expected-stdout:
a
end
expected-stderr-pattern:
/.*break.*/
---
name: break-5
description:
Error if break argument isn't a number
stdin:
for i in a b c; do echo $i; break abc; echo more-$i; done
echo end
expected-stdout:
a
expected-exit: e != 0
expected-stderr-pattern:
/.*break.*/
---
name: continue-1
description:
See if continue continues loops
stdin:
for i in a b c; do echo $i; continue; echo bad-$i ; done
echo end-1
for i in a b c; do echo $i; continue 1; echo bad-$i; done
echo end-2
for i in a b c; do
for j in x y z; do
echo $i:$j
continue
echo bad-$i-$j
done
echo end-$i
done
echo end-3
expected-stdout:
a
b
c
end-1
a
b
c
end-2
a:x
a:y
a:z
end-a
b:x
b:y
b:z
end-b
c:x
c:y
c:z
end-c
end-3
---
name: continue-2
description:
See if continue breaks out of nested loops
stdin:
for i in a b c; do
for j in x y z; do
echo $i:$j
continue 2
echo bad-$i-$j
done
echo end-$i
done
echo end
expected-stdout:
a:x
b:x
c:x
end
---
name: continue-3
description:
What if continue used outside of any loops
(ksh88,ksh93 don't print error messages here)
stdin:
continue
expected-stderr-pattern:
/.*continue.*/
---
name: continue-4
description:
What if continue N used when only N-1 loops
(ksh88,ksh93 don't print error messages here)
stdin:
for i in a b c; do echo $i; continue 2; echo bad-$i; done
echo end
expected-stdout:
a
b
c
end
expected-stderr-pattern:
/.*continue.*/
---
name: continue-5
description:
Error if continue argument isn't a number
stdin:
for i in a b c; do echo $i; continue abc; echo more-$i; done
echo end
expected-stdout:
a
expected-exit: e != 0
expected-stderr-pattern:
/.*continue.*/
---

162
tests/cdhist.t Normal file
View File

@ -0,0 +1,162 @@
name: cd-history
description:
Test someone's CD history package (uses arrays)
# Fails on OS/2, since directory names are prepended with drive letter.
category: !os:os2
stdin:
# go to known place before doing anything
cd /
alias cd=_cd
function _cd
{
typeset -i cdlen i
typeset t
if [ $# -eq 0 ]
then
set -- $HOME
fi
if [ "$CDHISTFILE" -a -r "$CDHISTFILE" ] # if directory history exists
then
typeset CDHIST
i=-1
while read -r t # read directory history file
do
CDHIST[i=i+1]=$t
done <$CDHISTFILE
fi
if [ "${CDHIST[0]}" != "$PWD" -a "$PWD" != "" ]
then
_cdins # insert $PWD into cd history
fi
cdlen=${#CDHIST[*]} # number of elements in history
case "$@" in
-) # cd to new dir
if [ "$OLDPWD" = "" ] && ((cdlen>1))
then
'print' ${CDHIST[1]}
'cd' ${CDHIST[1]}
_pwd
else
'cd' $@
_pwd
fi
;;
-l) # print directory list
typeset -R3 num
((i=cdlen))
while (((i=i-1)>=0))
do
num=$i
'print' "$num ${CDHIST[i]}"
done
return
;;
-[0-9]|-[0-9][0-9]) # cd to dir in list
if (((i=${1#-})<cdlen))
then
'print' ${CDHIST[i]}
'cd' ${CDHIST[i]}
_pwd
else
'cd' $@
_pwd
fi
;;
-*) # cd to matched dir in list
t=${1#-}
i=1
while ((i<cdlen))
do
case ${CDHIST[i]} in
*$t*)
'print' ${CDHIST[i]}
'cd' ${CDHIST[i]}
_pwd
break
;;
esac
((i=i+1))
done
if ((i>=cdlen))
then
'cd' $@
_pwd
fi
;;
*) # cd to new dir
'cd' $@
_pwd
;;
esac
_cdins # insert $PWD into cd history
if [ "$CDHISTFILE" ]
then
cdlen=${#CDHIST[*]} # number of elements in history
i=0
while ((i<cdlen))
do
'print' -r ${CDHIST[i]} # update directory history
((i=i+1))
done >$CDHISTFILE
fi
}
function _cdins # insert $PWD into cd history
{ # meant to be called only by _cd
typeset -i i
((i=0))
while ((i<${#CDHIST[*]})) # see if dir is already in list
do
if [ "${CDHIST[$i]}" = "$PWD" ]
then
break
fi
((i=i+1))
done
if ((i>22)) # limit max size of list
then
i=22
fi
while (((i=i-1)>=0)) # bump old dirs in list
do
CDHIST[i+1]=${CDHIST[i]}
done
CDHIST[0]=$PWD # insert new directory in list
}
function _pwd
{
if [ -n "$ECD" ]
then
pwd 1>&6
fi
}
# Start of test
cd /tmp
cd /bin
cd /etc
cd -
cd -2
cd -l
expected-stdout:
/bin
/tmp
3 /
2 /etc
1 /bin
0 /tmp
---

145
tests/eglob.t Normal file
View File

@ -0,0 +1,145 @@
name: eglob-bad-1
description:
Check that globbing isn't done when glob has syntax error
file-setup: file 644 "abcx"
file-setup: file 644 "abcz"
file-setup: file 644 "bbc"
stdin:
echo !([*)*
echo +(a|b[)*
expected-stdout:
!([*)*
+(a|b[)*
---
name: eglob-bad-2
description:
Check that globbing isn't done when glob has syntax error
(at&t ksh fails this test)
file-setup: file 644 "abcx"
file-setup: file 644 "abcz"
file-setup: file 644 "bbc"
stdin:
echo [a*(]*)z
expected-stdout:
[a*(]*)z
---
name: eglob-infinite-plus
description:
Check that shell doesn't go into infinite loop expanding +(...)
expressions.
file-setup: file 644 "abc"
time-limit: 3
stdin:
echo +()c
echo +()x
echo +(*)c
echo +(*)x
expected-stdout:
+()c
+()x
abc
+(*)x
---
name: eglob-subst-1
description:
Check that eglobbing isn't done on substitution results
file-setup: file 644 "abc"
stdin:
x='@(*)'
echo $x
expected-stdout:
@(*)
---
name: eglob-nomatch-1
description:
Check that the pattern doesn't match
stdin:
echo 1: no-file+(a|b)stuff
echo 2: no-file+(a*(c)|b)stuff
echo 3: no-file+((((c)))|b)stuff
expected-stdout:
1: no-file+(a|b)stuff
2: no-file+(a*(c)|b)stuff
3: no-file+((((c)))|b)stuff
---
name: eglob-match-1
description:
Check that the pattern matches correctly
file-setup: file 644 "abd"
file-setup: file 644 "acd"
file-setup: file 644 "abac"
stdin:
echo 1: a+(b|c)d
echo 2: a!(@(b|B))d
echo 3: *(a(b|c)) # (...|...) can be used within X(..)
echo 4: a[b*(foo|bar)]d # patterns not special inside [...]
expected-stdout:
1: abd acd
2: acd
3: abac
4: abd
---
name: eglob-case-1
description:
Simple negation tests
stdin:
case foo in !(foo|bar)) echo yes;; *) echo no;; esac
case bar in !(foo|bar)) echo yes;; *) echo no;; esac
expected-stdout:
no
no
---
name: eglob-case-2
description:
Simple kleene tests
stdin:
case foo in *(a|b[)) echo yes;; *) echo no;; esac
case foo in *(a|b[)|f*) echo yes;; *) echo no;; esac
case '*(a|b[)' in *(a|b[)) echo yes;; *) echo no;; esac
expected-stdout:
no
yes
yes
---
name: eglob-trim-1
description:
Eglobing in trim expressions...
(at&t ksh fails this - docs say # matches shortest string, ## matches
longest...)
stdin:
x=abcdef
echo 1: ${x#a|abc}
echo 2: ${x##a|abc}
echo 3: ${x%def|f}
echo 4: ${x%%f|def}
expected-stdout:
1: bcdef
2: def
3: abcde
4: abc
---
name: eglob-trim-2
description:
Check eglobing works in trims...
stdin:
x=abcdef
echo 1: ${x#*(a|b)cd}
echo 2: "${x#*(a|b)cd}"
echo 3: ${x#"*(a|b)cd"}
echo 4: ${x#a(b|c)}
expected-stdout:
1: ef
2: ef
3: abcdef
4: cdef
---

99
tests/glob.t Normal file
View File

@ -0,0 +1,99 @@
name: glob-bad-1
description:
Check that globbing isn't done when glob has syntax error
file-setup: dir 755 "[x"
file-setup: file 644 "[x/foo"
stdin:
echo [*
echo *[x
echo [x/*
expected-stdout:
[*
*[x
[x/foo
---
name: glob-bad-2
description:
Check that symbolic links aren't stat()'d
category: !os:os2
file-setup: dir 755 "dir"
file-setup: symlink 644 "dir/abc"
non-existant-file
stdin:
echo d*/*
echo d*/abc
expected-stdout:
dir/abc
dir/abc
---
name: glob-range-1
description:
Test range matching
file-setup: file 644 ".bc"
file-setup: file 644 "abc"
file-setup: file 644 "bbc"
file-setup: file 644 "cbc"
file-setup: file 644 "-bc"
stdin:
echo [ab-]*
echo [-ab]*
echo [!-ab]*
echo [!ab]*
echo []ab]*
expected-stdout:
-bc abc bbc
-bc abc bbc
cbc
-bc cbc
abc bbc
---
name: glob-range-2
description:
Test range matching
(at&t ksh fails this; POSIX says invalid)
file-setup: file 644 "abc"
stdin:
echo [a--]*
expected-stdout:
[a--]*
---
name: glob-range-3
description:
Check that globbing matches the right things...
file-setup: file 644 "aÂc"
stdin:
echo a[Á-Ú]*
expected-stdout:
aÂc
---
name: glob-range-4
description:
Results unspecified according to POSIX
file-setup: file 644 ".bc"
stdin:
echo [a.]*
expected-stdout:
[a.]*
---
name: glob-range-5
description:
Results unspecified according to POSIX
(at&t ksh treats this like [a-cc-e]*)
file-setup: file 644 "abc"
file-setup: file 644 "bbc"
file-setup: file 644 "cbc"
file-setup: file 644 "dbc"
file-setup: file 644 "ebc"
file-setup: file 644 "-bc"
stdin:
echo [a-c-e]*
expected-stdout:
-bc abc bbc cbc ebc
---

331
tests/heredoc.t Normal file
View File

@ -0,0 +1,331 @@
name: heredoc-1
description:
Check ordering/content of redundent here documents.
stdin:
cat << EOF1 << EOF2
hi
EOF1
there
EOF2
expected-stdout:
there
---
name: heredoc-2
description:
Check quoted here-doc is protected.
stdin:
a=foo
cat << 'EOF'
hi\
there$a
stuff
EO\
F
EOF
expected-stdout:
hi\
there$a
stuff
EO\
F
---
name: heredoc-3
description:
Check that newline isn't needed after heredoc-delimiter marker.
stdin: !
cat << EOF
hi
there
EOF
expected-stdout:
hi
there
---
name: heredoc-4
description:
Check that an error occurs if the heredoc-delimiter is missing.
stdin: !
cat << EOF
hi
there
expected-exit: e > 0
expected-stderr-pattern: /.*/
---
name: heredoc-5
description:
Check that backslash quotes a $, ` and \ and kills a \newline
stdin:
a=BAD
b=ok
cat << EOF
h\${a}i
h\\${b}i
th\`echo not-run\`ere
th\\`echo is-run`ere
fol\\ks
more\\
last \
line
EOF
expected-stdout:
h${a}i
h\oki
th`echo not-run`ere
th\is-runere
fol\ks
more\
last line
---
name: heredoc-6
description:
Check that \newline in initial here-delim word doesn't imply
a quoted here-doc.
stdin:
a=i
cat << EO\
F
h$a
there
EOF
expected-stdout:
hi
there
---
name: heredoc-7
description:
Check that double quoted $ expressions in here delimiters are
not expanded and match the delimiter.
POSIX says only quote removal is applied to the delimiter.
stdin:
a=b
cat << "E$a"
hi
h$a
hb
E$a
echo done
expected-stdout:
hi
h$a
hb
done
---
name: heredoc-8
description:
Check that double quoted escaped $ expressions in here
delimiters are not expanded and match the delimiter.
POSIX says only quote removal is applied to the delimiter
(\ counts as a quote).
stdin:
a=b
cat << "E\$a"
hi
h$a
h\$a
hb
h\b
E$a
echo done
expected-stdout:
hi
h$a
h\$a
hb
h\b
done
---
name: heredoc-tmpfile-1
description:
Check that heredoc temp files aren't removed too soon or too late.
Heredoc in simple command.
stdin:
TMPDIR=$PWD
eval '
cat <<- EOF
hi
EOF
for i in a b ; do
cat <<- EOF
more
EOF
done
' &
sleep 1
echo Left overs: *
expected-stdout:
hi
more
more
Left overs: *
---
name: heredoc-tmpfile-2
description:
Check that heredoc temp files aren't removed too soon or too late.
Heredoc in function, multiple calls to function.
stdin:
TMPDIR=$PWD
eval '
foo() {
cat <<- EOF
hi
EOF
}
foo
foo
' &
sleep 1
echo Left overs: *
expected-stdout:
hi
hi
Left overs: *
---
name: heredoc-tmpfile-3
description:
Check that heredoc temp files aren't removed too soon or too late.
Heredoc in function in loop, multiple calls to function.
stdin:
TMPDIR=$PWD
eval '
foo() {
cat <<- EOF
hi
EOF
}
for i in a b; do
foo
foo() {
cat <<- EOF
folks $i
EOF
}
done
foo
' &
sleep 1
echo Left overs: *
expected-stdout:
hi
folks b
folks b
Left overs: *
---
name: heredoc-tmpfile-4
description:
Check that heredoc temp files aren't removed too soon or too late.
Backgrounded simple command with here doc
stdin:
TMPDIR=$PWD
eval '
cat <<- EOF &
hi
EOF
' &
sleep 1
echo Left overs: *
expected-stdout:
hi
Left overs: *
---
name: heredoc-tmpfile-5
description:
Check that heredoc temp files aren't removed too soon or too late.
Backgrounded subshell command with here doc
stdin:
TMPDIR=$PWD
eval '
(
sleep 1 # so parent exits
echo A
cat <<- EOF
hi
EOF
echo B
) &
' &
sleep 2
echo Left overs: *
expected-stdout:
A
hi
B
Left overs: *
---
name: heredoc-tmpfile-6
description:
Check that heredoc temp files aren't removed too soon or too late.
Heredoc in pipeline.
stdin:
TMPDIR=$PWD
eval '
cat <<- EOF | sed "s/hi/HI/"
hi
EOF
' &
sleep 1
echo Left overs: *
expected-stdout:
HI
Left overs: *
---
name: heredoc-tmpfile-7
description:
Check that heredoc temp files aren't removed too soon or too late.
Heredoc in backgrounded pipeline.
stdin:
TMPDIR=$PWD
eval '
cat <<- EOF | sed 's/hi/HI/' &
hi
EOF
' &
sleep 1
echo Left overs: *
expected-stdout:
HI
Left overs: *
---
name: heredoc-tmpfile-8
description:
Check that heredoc temp files aren't removed too soon or too late.
Heredoc in function, backgrounded call to function.
stdin:
TMPDIR=$PWD
# Background eval so main shell doesn't do parsing
eval '
foo() {
cat <<- EOF
hi
EOF
}
foo
# sleep so eval can die
(sleep 1; foo) &
(sleep 1; foo) &
foo
' &
sleep 2
echo Left overs: *
expected-stdout:
hi
hi
hi
hi
Left overs: *
---

559
tests/history.t Normal file
View File

@ -0,0 +1,559 @@
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# Not tested yet:
# - commands in history file are not numbered negatively
# (and a few hundred other things)
name: history-basic
description:
See if we can test history at all
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo hi
fc -l
expected-stdout:
hi
1 echo hi
expected-stderr-pattern:
/^X*$/
---
name: history-e-minus-1
description:
Check if more recent command is executed
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo hi
echo there
fc -e -
expected-stdout:
hi
there
there
expected-stderr-pattern:
/^X*echo there\nX*$/
---
name: history-e-minus-2
description:
Check that repeated command is printed before command
is re-executed.
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
exec 2>&1
echo hi
echo there
fc -e -
expected-stdout-pattern:
/X*hi\nX*there\nX*echo there\nthere\nX*/
expected-stderr-pattern:
/^X*$/
---
name: history-e-minus-3
description:
fc -e - fails when there is no history
(ksh93 has a bug that causes this to fail)
(ksh88 loops on this)
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
fc -e -
echo ok
expected-stdout:
ok
expected-stderr-pattern:
/^X*.*:.*history.*\nX*$/
---
name: history-e-minus-4
description:
Check if "fc -e -" command output goes to stdout.
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo abc
fc -e - | (read x; echo "A $x")
echo ok
expected-stdout:
abc
A abc
ok
expected-stderr-pattern:
/^X*echo abc\nX*/
---
name: history-e-minus-5
description:
fc is replaced in history by new command.
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo abc def
echo ghi jkl
fc -e - echo
fc -l 2 4
expected-stdout:
abc def
ghi jkl
ghi jkl
2 echo ghi jkl
3 echo ghi jkl
4 fc -l 2 4
expected-stderr-pattern:
/^X*echo ghi jkl\nX*$/
---
name: history-list-1
description:
List lists correct range
(ksh88 fails 'cause it lists the fc command)
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
fc -l -- -2
expected-stdout:
line 1
line 2
line 3
2 echo line 2
3 echo line 3
expected-stderr-pattern:
/^X*$/
---
name: history-list-2
description:
Lists oldest history if given pre-historic number
(ksh93 has a bug that causes this to fail)
(ksh88 fails 'cause it lists the fc command)
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
fc -l -- -40
expected-stdout:
line 1
line 2
line 3
1 echo line 1
2 echo line 2
3 echo line 3
expected-stderr-pattern:
/^X*$/
---
name: history-list-3
description:
Can give number `options' to fc
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
echo line 4
fc -l -3 -2
expected-stdout:
line 1
line 2
line 3
line 4
2 echo line 2
3 echo line 3
expected-stderr-pattern:
/^X*$/
---
name: history-list-4
description:
-1 refers to previous command
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
echo line 4
fc -l -1 -1
expected-stdout:
line 1
line 2
line 3
line 4
4 echo line 4
expected-stderr-pattern:
/^X*$/
---
name: history-list-5
description:
List command stays in history
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
echo line 4
fc -l -1 -1
fc -l -2 -1
expected-stdout:
line 1
line 2
line 3
line 4
4 echo line 4
4 echo line 4
5 fc -l -1 -1
expected-stderr-pattern:
/^X*$/
---
name: history-list-6
description:
HISTSIZE limits about of history kept.
(ksh88 fails 'cause it lists the fc command)
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!HISTSIZE=3!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
echo line 4
echo line 5
fc -l
expected-stdout:
line 1
line 2
line 3
line 4
line 5
4 echo line 4
5 echo line 5
expected-stderr-pattern:
/^X*$/
---
name: history-list-7
description:
fc allows too old/new errors in range specification
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!HISTSIZE=3!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
echo line 4
echo line 5
fc -l 1 30
expected-stdout:
line 1
line 2
line 3
line 4
line 5
4 echo line 4
5 echo line 5
6 fc -l 1 30
expected-stderr-pattern:
/^X*$/
---
name: history-list-r-1
description:
test -r flag in history
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
echo line 4
echo line 5
fc -l -r 2 4
expected-stdout:
line 1
line 2
line 3
line 4
line 5
4 echo line 4
3 echo line 3
2 echo line 2
expected-stderr-pattern:
/^X*$/
---
name: history-list-r-2
description:
If first is newer than last, -r is implied.
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
echo line 4
echo line 5
fc -l 4 2
expected-stdout:
line 1
line 2
line 3
line 4
line 5
4 echo line 4
3 echo line 3
2 echo line 2
expected-stderr-pattern:
/^X*$/
---
name: history-list-r-3
description:
If first is newer than last, -r is cancelled.
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2
echo line 3
echo line 4
echo line 5
fc -l -r 4 2
expected-stdout:
line 1
line 2
line 3
line 4
line 5
2 echo line 2
3 echo line 3
4 echo line 4
expected-stderr-pattern:
/^X*$/
---
name: history-subst-1
description:
Basic substitution
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo abc def
echo ghi jkl
fc -e - abc=AB 'echo a'
expected-stdout:
abc def
ghi jkl
AB def
expected-stderr-pattern:
/^X*echo AB def\nX*$/
---
name: history-subst-2
description:
Does subst find previous command?
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo abc def
echo ghi jkl
fc -e - jkl=XYZQRT 'echo g'
expected-stdout:
abc def
ghi jkl
ghi XYZQRT
expected-stderr-pattern:
/^X*echo ghi XYZQRT\nX*$/
---
name: history-subst-3
description:
Does subst find previous command when no arguments given
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo abc def
echo ghi jkl
fc -e - jkl=XYZQRT
expected-stdout:
abc def
ghi jkl
ghi XYZQRT
expected-stderr-pattern:
/^X*echo ghi XYZQRT\nX*$/
---
name: history-subst-4
description:
Global substitutions work
(ksh88 and ksh93 do not have -g option)
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo abc def asjj sadjhasdjh asdjhasd
fc -e - -g a=FooBAR
expected-stdout:
abc def asjj sadjhasdjh asdjhasd
FooBARbc def FooBARsjj sFooBARdjhFooBARsdjh FooBARsdjhFooBARsd
expected-stderr-pattern:
/^X*echo FooBARbc def FooBARsjj sFooBARdjhFooBARsdjh FooBARsdjhFooBARsd\nX*$/
---
name: history-subst-5
description:
Make sure searches don't find current (fc) command
(ksh88/ksh93 don't have the ? prefix thing so they fail this test)
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo abc def
echo ghi jkl
fc -e - abc=AB \?abc
expected-stdout:
abc def
ghi jkl
AB def
expected-stderr-pattern:
/^X*echo AB def\nX*$/
---
name: history-ed-1
description:
Basic (ed) editing works (assumes you have generic ed editor
that prints no prompts).
# No ed on os/2 (yet?).
category: !os:os2
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo abc def
fc echo
s/abc/FOOBAR/
w
q
expected-stdout:
abc def
13
16
FOOBAR def
expected-stderr-pattern:
/^X*echo FOOBAR def\nX*$/
---
name: history-ed-2
description:
Correct command is edited when number given
category: !os:os2
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo line 1
echo line 2 is here
echo line 3
echo line 4
fc 2
s/is here/is changed/
w
q
expected-stdout:
line 1
line 2 is here
line 3
line 4
20
23
line 2 is changed
expected-stderr-pattern:
/^X*echo line 2 is changed\nX*$/
---
name: history-ed-3
description:
Newly created multi line commands show up as single command
in history.
(NOTE: will fail if using COMPLEX HISTORY compile time option)
(ksh88 fails 'cause it lists the fc command)
category: !os:os2
arguments: !-i!
env-setup: !ENV=./Env!HISTFILE=hist.file!
file-setup: file 644 "Env"
PS1=X
stdin:
echo abc def
fc echo
s/abc/FOOBAR/
$a
echo a new line
.
w
q
fc -l
expected-stdout:
abc def
13
32
FOOBAR def
a new line
1 echo abc def
2 echo FOOBAR def
echo a new line
expected-stderr-pattern:
/^X*echo FOOBAR def\necho a new line\nX*$/
---

162
tests/ifs.t Normal file
View File

@ -0,0 +1,162 @@
name: IFS-space-1
description:
Simple test, default IFS
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
set -- A B C
showargs 1 $*
showargs 2 "$*"
showargs 3 $@
showargs 4 "$@"
expected-stdout:
<1> <A> <B> <C>
<2> <A B C>
<3> <A> <B> <C>
<4> <A> <B> <C>
---
name: IFS-colon-1
description:
Simple test, IFS=:
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
IFS=:
set -- A B C
showargs 1 $*
showargs 2 "$*"
showargs 3 $@
showargs 4 "$@"
expected-stdout:
<1> <A> <B> <C>
<2> <A:B:C>
<3> <A> <B> <C>
<4> <A> <B> <C>
---
name: IFS-null-1
description:
Simple test, IFS=""
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
IFS=""
set -- A B C
showargs 1 $*
showargs 2 "$*"
showargs 3 $@
showargs 4 "$@"
expected-stdout:
<1> <A B C>
<2> <ABC>
<3> <A B C>
<4> <A B C>
---
name: IFS-space-colon-1
description:
Simple test, IFS=<white-space>:
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
IFS="IFS:"
set --
showargs 1 $*
showargs 2 "$*"
showargs 3 $@
showargs 4 "$@"
showargs 5 : "$@"
expected-stdout:
<1>
<2> <>
<3>
<4>
<5> <:>
---
name: IFS-space-colon-2
description:
Simple test, IFS=<white-space>:
At&t ksh fails this, POSIX says the test is correct.
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
IFS="IFS:"
set --
showargs :"$@"
expected-stdout:
<:>
---
name: IFS-space-colon-3
description:
Simple test, IFS=<white-space>:
pdksh fails both of these tests
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
IFS="IFS:"
x=
set --
showargs "$x$@"
showargs "$@$x"
expected-fail: yes
expected-stdout:
<>
<>
---
name: IFS-space-colon-4
description:
Simple test, IFS=<white-space>:
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
IFS="IFS:"
set --
showargs "$@$@"
expected-stdout:
---
name: IFS-space-colon-5
description:
Simple test, IFS=<white-space>:
Don't know what POSIX thinks of this. at&t ksh does not do this.
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
IFS="IFS:"
set --
showargs "${@:-}"
expected-stdout:
<>
---
name: IFS-subst-1
description:
Simple test, IFS=<white-space>:
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
IFS="$IFS:"
x=":b: :"
echo -n '1:'; for i in $x ; do echo -n " [$i]" ; done ; echo
echo -n '2:'; for i in :b:: ; do echo -n " [$i]" ; done ; echo
showargs 3 $x
showargs 4 :b::
x="a:b:"
echo -n '5:'; for i in $x ; do echo -n " [$i]" ; done ; echo
showargs 6 $x
x="a::c"
echo -n '7:'; for i in $x ; do echo -n " [$i]" ; done ; echo
showargs 8 $x
echo -n '9:'; for i in ${FOO-`echo -n h:i`th:ere} ; do echo -n " [$i]" ; done ; echo
showargs 10 ${FOO-`echo -n h:i`th:ere}
showargs 11 "${FOO-`echo -n h:i`th:ere}"
expected-stdout:
1: [] [b] [] []
2: [:b::]
<3> <> <b> <> <>
<4> <:b::>
5: [a] [b] []
<6> <a> <b> <>
7: [a] [] [c]
<8> <a> <> <c>
9: [h] [ith] [ere]
<10> <h> <ith> <ere>
<11> <h:ith:ere>
---

218
tests/integer.t Normal file
View File

@ -0,0 +1,218 @@
name: integer-base-err-1
description:
Can't have 0 base (causes shell to exit)
expected-exit: e != 0
stdin:
typeset -i i
i=3
i=0#4
echo $i
expected-stderr-pattern:
/^.*:.*0#4.*\n$/
---
name: integer-base-err-2
description:
Can't have multiple bases in a `constant' (causes shell to exit)
(ksh88 fails this test)
expected-exit: e != 0
stdin:
typeset -i i
i=3
i=2#110#11
echo $i
expected-stderr-pattern:
/^.*:.*2#110#11.*\n$/
---
name: integer-base-err-3
description:
Syntax errors in expressions and effects on bases
(interactive so errors don't cause exits)
(ksh88 fails this test - shell exits, even with -i)
arguments: !-i!
stdin:
PS1= # minimize prompt hassles
typeset -i4 a=10
typeset -i a=2+
echo $a
typeset -i4 a=10
typeset -i2 a=2+
echo $a
expected-stderr-pattern:
/^([#\$] )?.*:.*2+.*\n.*:.*2+.*\n$/
expected-stdout:
4#22
4#22
---
name: integer-base-err-4
description:
Are invalid digits (according to base) errors?
(ksh93 fails this test)
expected-exit: e != 0
stdin:
typeset -i i;
i=3#4
expected-stderr-pattern:
/^([#\$] )?.*:.*3#4.*\n$/
---
name: integer-base-1
description:
Missing number after base is treated as 0.
stdin:
typeset -i i
i=3
i=2#
echo $i
expected-stdout:
0
---
name: integer-base-2
description:
Check `stickyness' of base in various situations
stdin:
typeset -i i=8
echo $i
echo ---------- A
typeset -i4 j=8
echo $j
echo ---------- B
typeset -i k=8
typeset -i4 k=8
echo $k
echo ---------- C
typeset -i4 l
l=3#10
echo $l
echo ---------- D
typeset -i m
m=3#10
echo $m
echo ---------- E
n=2#11
typeset -i n
echo $n
n=10
echo $n
echo ---------- F
typeset -i8 o=12
typeset -i4 o
echo $o
echo ---------- G
typeset -i p
let p=8#12
echo $p
expected-stdout:
8
---------- A
4#20
---------- B
4#20
---------- C
4#3
---------- D
3#10
---------- E
2#11
2#1010
---------- F
4#30
---------- G
8#12
---
name: integer-base-3
description:
More base parsing (hmm doesn't test much..)
stdin:
typeset -i aa
aa=1+12#10+2
echo $aa
typeset -i bb
bb=1+$aa
echo $bb
typeset -i bb
bb=$aa
echo $bb
typeset -i cc
cc=$aa
echo $cc
expected-stdout:
15
16
15
15
---
name: integer-base-4
description:
Check that things not declared as integers are not made integers,
also, check if base is not reset by -i with no arguments.
(ksh93 fails - prints 10#20 - go figure)
stdin:
xx=20
let xx=10
typeset -i | grep '^xx='
typeset -i4 a=10
typeset -i a=20
echo $a
expected-stdout:
4#110
---
name: integer-base-5
description:
More base stuff
stdin:
typeset -i4 a=3#10
echo $a
echo --
typeset -i j=3
j=~3
echo $j
echo --
typeset -i k=1
x[k=k+1]=3
echo $k
echo --
typeset -i l
for l in 1 2+3 4; do echo $l; done
expected-stdout:
4#3
--
-4
--
2
--
1
5
4
---
name: integer-base-6
description:
Even more base stuff
(ksh93 fails this test - prints 0)
stdin:
typeset -i7 i
i=
echo $i
expected-stdout:
7#0
---
name: integer-base-7
description:
Check that non-integer parameters don't get bases assigned
stdin:
echo $(( zz = 8#100 ))
echo $zz
expected-stdout:
64
64
---

111
tests/lineno.t Normal file
View File

@ -0,0 +1,111 @@
name: lineno-stdin
description:
See if $LINENO is updated and can be modified.
stdin:
echo A $LINENO
echo B $LINENO
LINENO=20
echo C $LINENO
expected-stdout:
A 1
B 2
C 20
---
name: lineno-inc
description:
See if $LINENO is set for .'d files.
file-setup: file 644 "dotfile"
echo dot A $LINENO
echo dot B $LINENO
LINENO=20
echo dot C $LINENO
stdin:
echo A $LINENO
echo B $LINENO
. ./dotfile
expected-stdout:
A 1
B 2
dot A 1
dot B 2
dot C 20
---
name: lineno-func
description:
See if $LINENO is set for commands in a function.
stdin:
echo A $LINENO
echo B $LINENO
bar() {
echo func A $LINENO
echo func B $LINENO
}
bar
echo C $LINENO
expected-stdout:
A 1
B 2
func A 4
func B 5
C 8
---
name: lineno-unset
description:
See if unsetting LINENO makes it non-magic.
file-setup: file 644 "dotfile"
echo dot A $LINENO
echo dot B $LINENO
stdin:
unset LINENO
echo A $LINENO
echo B $LINENO
bar() {
echo func A $LINENO
echo func B $LINENO
}
bar
. ./dotfile
echo C $LINENO
expected-stdout:
A
B
func A
func B
dot A
dot B
C
---
name: lineno-unset-use
description:
See if unsetting LINENO makes it non-magic even
when it is re-used.
file-setup: file 644 "dotfile"
echo dot A $LINENO
echo dot B $LINENO
stdin:
unset LINENO
LINENO=3
echo A $LINENO
echo B $LINENO
bar() {
echo func A $LINENO
echo func B $LINENO
}
bar
. ./dotfile
echo C $LINENO
expected-stdout:
A 3
B 3
func A 3
func B 3
dot A 3
dot B 3
C 3
---

58
tests/read.t Normal file
View File

@ -0,0 +1,58 @@
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
#
# To test:
# POSIX:
# - if no -r, \ is escape character
# - \newline disappear
# - \<IFS> -> don't break here
# - \<anything-else> -> <anything-else>
# - if -r, backslash is not special
# - if stdin is tty and shell interactive
# - prompt for continuation if \newline (prompt to stderr)
# - a here-document isn't terminated after newline ????
# - remaining vars set to empty string (not null)
# - check field splitting
# - left over fields and their separators assigned to last var
# - exit status is normally 0
# - exit status is > 0 on eof
# - exit status > 0 on error
# - signals interrupt reads
# extra:
# - can't change read-only variables
# - error if var name bogus
# - set -o allexport effects read
# ksh:
# x check default variable: REPLY
# - check -p, -s, -u options
# - check var?prompt stuff
# - "echo a b | read x y" sets x,y in parent shell (at&t)
#
name: read-IFS-1
description:
Simple test, default IFS
stdin:
echo "A B " > IN
unset x y z
read x y z < IN
echo 1: "x[$x] y[$y] z[$z]"
echo 1a: ${z-z not set}
read x < IN
echo 2: "x[$x]"
expected-stdout:
1: x[A] y[B] z[]
1a:
2: x[A B]
---
name: read-ksh-1
description:
If no var specified, REPLY is used
stdin:
echo "abc" > IN
read < IN
echo "[$REPLY]";
expected-stdout:
[abc]
---

1093
tests/regress.t Normal file

File diff suppressed because it is too large Load Diff

10
tests/syntax.t Normal file
View File

@ -0,0 +1,10 @@
name: syntax-1
description:
Check that lone ampersand is a syntax error
stdin:
&
expected-exit: e != 0
expected-stderr-pattern:
/syntax error/
---

1206
tests/th Normal file

File diff suppressed because it is too large Load Diff

30
tests/th-sh Normal file
View File

@ -0,0 +1,30 @@
#!/bin/sh
# $OpenBSD: th-sh,v 1.2 2001/01/28 23:04:57 niklas Exp $
#
# Simple script to find perl and run it
#
# Avoid common problems with ENV (though perl shouldn't let it through)
# (can you believe some shells don't have an unset???)
unset ENV
x=x
[ -x /bin/sh ] 2> /dev/null || x=f
IFS=:$IFS
perl=
for i in $PATH; do
[ X"$i" = X ] && i=.
for j in perl perl4 perl5 ; do
[ -$x "$i/$j" ] && perl=$i/$j && break 2
done
done
[ X"$perl" = X ] && {
echo "$0: can't find perl - bye\n" 1>&2
exit 1
}
exec $perl "$@"

30
tests/th.sh Normal file
View File

@ -0,0 +1,30 @@
#!/bin/sh
# $OpenBSD: th.sh,v 1.4 2001/01/28 23:04:57 niklas Exp $
#
# Simple script to find perl and run it
#
# Avoid common problems with ENV (though perl shouldn't let it through)
# (can you believe some shells don't have an unset???)
unset ENV
x=x
[ -x /bin/sh ] 2> /dev/null || x=f
IFS=:$IFS
perl=
for i in $PATH; do
[ X"$i" = X ] && i=.
for j in perl perl4 perl5 ; do
[ -$x "$i/$j" ] && perl=$i/$j && break 2
done
done
[ X"$perl" = X ] && {
echo "$0: can't find perl - bye\n" 1>&2
exit 1
}
exec $perl "$@"

99
tests/unclass1.t Normal file
View File

@ -0,0 +1,99 @@
name: xxx-quoted-newline-1
description:
Check that \<newline> works inside of ${}
stdin:
abc=2
echo ${ab\
c}
expected-stdout:
2
---
name: xxx-quoted-newline-2
description:
Check that \<newline> works at the start of a here document
stdin:
cat << EO\
F
hi
EOF
expected-stdout:
hi
---
name: xxx-quoted-newline-3
description:
Check that \<newline> works at the end of a here document
stdin:
cat << EOF
hi
EO\
F
expected-stdout:
hi
---
name: xxx-multi-assignment-cmd
description:
Check that assignments in a command affect subsequent assignments
in the same command
stdin:
FOO=abc
FOO=123 BAR=$FOO
echo $BAR
expected-stdout:
123
---
name: xxx-exec-environment-1
description:
Check to see if exec sets it's environment correctly
stdin:
FOO=bar exec env
expected-stdout-pattern:
/(^|.*\n)FOO=bar\n/
---
name: xxx-exec-environment-2
description:
Check to make sure exec doesn't change environment if a program
isn't exec-ed
# Under os/2, _emx_sig environment variable changes.
category: !os:os2
stdin:
env > bar1
FOO=bar exec; env > bar2
cmp -s bar1 bar2
---
name: xxx-what-do-you-call-this-1
stdin:
echo "${foo:-"a"}*"
expected-stdout:
a*
---
name: xxx-prefix-strip-1
stdin:
foo='a cdef'
echo ${foo#a c}
expected-stdout:
def
---
name: xxx-prefix-strip-2
stdin:
set a c
x='a cdef'
echo ${x#$*}
expected-stdout:
def
---
name: xxx-variable-syntax-1
stdin:
echo ${:}
expected-stderr-pattern:
/bad substitution/
expected-exit: 1
---

163
tests/unclass2.t Normal file
View File

@ -0,0 +1,163 @@
name: xxx-subsitution-eval-order
description:
Check order of evaluation of expressions
stdin:
i=1 x= y=
set -A A abc def GHI j G k
echo ${A[x=(i+=1)]#${A[y=(i+=2)]}}
echo $x $y
expected-stdout:
HI
2 4
---
name: xxx-set-option-1
description:
Check option parsing in set
stdin:
set -vsA foo -- A 1 3 2
echo ${foo[*]}
expected-stderr:
echo ${foo[*]}
expected-stdout:
1 2 3 A
---
name: xxx-exec-1
description:
Check that exec exits for built-ins
arguments: !-i!
stdin:
exec print hi
echo still herre
expected-stdout:
hi
expected-stderr-pattern: /.*/
---
name: xxx-while-1
description:
Check the return value of while loops
XXX need to do same for for/select/until loops
stdin:
i=x
while [ $i != xxx ] ; do
i=x$i
if [ $i = xxx ] ; then
false
continue
fi
done
echo loop1=$?
i=x
while [ $i != xxx ] ; do
i=x$i
if [ $i = xxx ] ; then
false
break
fi
done
echo loop2=$?
i=x
while [ $i != xxx ] ; do
i=x$i
false
done
echo loop3=$?
expected-stdout:
loop1=0
loop2=0
loop3=1
---
name: xxx-status-1
description:
Check that blank lines don't clear $?
arguments: !-i!
stdin:
(exit 1)
echo $?
(exit 1)
echo $?
true
expected-stdout:
1
1
expected-stderr-pattern: /.*/
---
name: xxx-status-2
description:
Check that $? is preserved in subshells, includes, traps.
stdin:
(exit 1)
echo blank: $?
(exit 2)
(echo subshell: $?)
echo 'echo include: $?' > foo
(exit 3)
. ./foo
trap 'echo trap: $?' ERR
(exit 4)
echo exit: $?
expected-stdout:
blank: 1
subshell: 2
include: 3
trap: 4
exit: 4
---
name: xxx-clean-chars-1
description:
Check MAGIC character is stuffed correctly
stdin:
echo `echo [£`
expected-stdout:
[£
---
name: xxx-param-subst-qmark-1
description:
Check suppresion of error message with null string. According to
POSIX, it shouldn't print the error as `word' isn't ommitted.
stdin:
unset foo
x=
echo x${foo?$x}
expected-exit: 1
expected-fail: yes
expected-stderr-pattern: !/not set/
---
name: xxx-param-_-1
description:
Check c flag is set.
arguments: !-c!echo "[$-]"!
expected-stdout-pattern: /^\[.*c.*\]$/
---
name: env-prompt
description:
Check that prompt not printed when processing ENV
env-setup: !ENV=./foo!
file-setup: file 644 "foo"
XXX=_
PS1=X
false && echo hmmm
arguments: !-i!
stdin:
echo hi${XXX}there
expected-stdout:
hi_there
expected-stderr: !
XX
---

9
tests/version.t Normal file
View File

@ -0,0 +1,9 @@
name: version-1
description:
Check version of shell.
category: pdksh
stdin:
echo $KSH_VERSION
expected-stdout:
@(#)PD KSH v5.2.14 99/07/13.2
---

451
trap.c Normal file
View File

@ -0,0 +1,451 @@
/* $OpenBSD: trap.c,v 1.13 2003/02/28 09:45:09 jmc Exp $ */
/*
* signal handling
*/
/* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */
#define FROM_TRAP_C
#include "sh.h"
/* Table is indexed by signal number
*
* The script siglist.sh generates siglist.out, which is a sorted, complete
* list of signals
*/
Trap sigtraps[SIGNALS+1] = {
{ SIGEXIT_, "EXIT", "Signal 0" },
#include "siglist.out" /* generated by siglist.sh */
{ SIGERR_, "ERR", "Error handler" },
};
static struct sigaction Sigact_ign, Sigact_trap;
void
inittraps()
{
#ifdef HAVE_SYS_SIGLIST
# ifndef SYS_SIGLIST_DECLARED
extern char *sys_siglist[];
# endif
int i;
/* Use system description, if available, for unknown signals... */
for (i = 0; i < NSIG; i++)
if (!sigtraps[i].name && sys_siglist[i] && sys_siglist[i][0])
sigtraps[i].mess = sys_siglist[i];
#endif /* HAVE_SYS_SIGLIST */
sigemptyset(&Sigact_ign.sa_mask);
Sigact_ign.sa_flags = KSH_SA_FLAGS;
Sigact_ign.sa_handler = SIG_IGN;
Sigact_trap = Sigact_ign;
Sigact_trap.sa_handler = trapsig;
sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
sigtraps[SIGHUP].flags |= TF_FATAL;
sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
/* these are always caught so we can clean up any temporary files. */
setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
}
#ifdef KSH
static RETSIGTYPE alarm_catcher ARGS((int sig));
void
alarm_init()
{
sigtraps[SIGALRM].flags |= TF_SHELL_USES;
setsig(&sigtraps[SIGALRM], alarm_catcher,
SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
}
static RETSIGTYPE
alarm_catcher(sig)
int sig;
{
int errno_ = errno;
if (ksh_tmout_state == TMOUT_READING) {
int left = alarm(0);
if (left == 0) {
ksh_tmout_state = TMOUT_LEAVING;
intrsig = 1;
} else
alarm(left);
}
errno = errno_;
return RETSIGVAL;
}
#endif /* KSH */
Trap *
gettrap(name, igncase)
const char *name;
int igncase;
{
int i;
register Trap *p;
if (digit(*name)) {
int n;
if (getn(name, &n) && 0 <= n && n < SIGNALS)
return &sigtraps[n];
return NULL;
}
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->name) {
if (igncase) {
if (p->name && (!strcasecmp(p->name, name) ||
(strlen(name) > 3 && !strncasecmp("SIG",
p->name, 3) &&
!strcasecmp(p->name, name + 3))))
return p;
} else {
if (p->name && (!strcmp(p->name, name) ||
(strlen(name) > 3 && !strncmp("SIG",
p->name, 3) && !strcmp(p->name, name + 3))))
return p;
}
}
return NULL;
}
/*
* trap signal handler
*/
RETSIGTYPE
trapsig(i)
int i;
{
Trap *p = &sigtraps[i];
int errno_ = errno;
trap = p->set = 1;
if (p->flags & TF_DFL_INTR)
intrsig = 1;
if ((p->flags & TF_FATAL) && !p->trap) {
fatal_trap = 1;
intrsig = 1;
}
if (p->shtrap)
(*p->shtrap)(i);
#ifdef V7_SIGNALS
if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */
sigaction(i, &Sigact_trap, (struct sigaction *) 0);
#endif /* V7_SIGNALS */
errno = errno_;
return RETSIGVAL;
}
/* called when we want to allow the user to ^C out of something - won't
* work if user has trapped SIGINT.
*/
void
intrcheck()
{
if (intrsig)
runtraps(TF_DFL_INTR|TF_FATAL);
}
/* called after EINTR to check if a signal with normally causes process
* termination has been received.
*/
int
fatal_trap_check()
{
int i;
Trap *p;
/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
/* return value is used as an exit code */
return 128 + p->signal;
return 0;
}
/* Returns the signal number of any pending traps: ie, a signal which has
* occurred for which a trap has been set or for which the TF_DFL_INTR flag
* is set.
*/
int
trap_pending()
{
int i;
Trap *p;
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->set && ((p->trap && p->trap[0])
|| ((p->flags & (TF_DFL_INTR|TF_FATAL))
&& !p->trap)))
return p->signal;
return 0;
}
/*
* run any pending traps. If intr is set, only run traps that
* can interrupt commands.
*/
void
runtraps(flag)
int flag;
{
int i;
register Trap *p;
#ifdef KSH
if (ksh_tmout_state == TMOUT_LEAVING) {
ksh_tmout_state = TMOUT_EXECUTING;
warningf(FALSE, "timed out waiting for input");
unwind(LEXIT);
} else
/* XXX: this means the alarm will have no effect if a trap
* is caught after the alarm() was started...not good.
*/
ksh_tmout_state = TMOUT_EXECUTING;
#endif /* KSH */
if (!flag)
trap = 0;
if (flag & TF_DFL_INTR)
intrsig = 0;
if (flag & TF_FATAL)
fatal_trap = 0;
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->set && (!flag
|| ((p->flags & flag) && p->trap == (char *) 0)))
runtrap(p);
}
void
runtrap(p)
Trap *p;
{
int i = p->signal;
char *trapstr = p->trap;
int oexstat;
int UNINITIALIZED(old_changed);
p->set = 0;
if (trapstr == (char *) 0) { /* SIG_DFL */
if (p->flags & TF_FATAL) {
/* eg, SIGHUP */
exstat = 128 + i;
unwind(LLEAVE);
}
if (p->flags & TF_DFL_INTR) {
/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
exstat = 128 + i;
unwind(LINTR);
}
return;
}
if (trapstr[0] == '\0') /* SIG_IGN */
return;
if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */
old_changed = p->flags & TF_CHANGED;
p->flags &= ~TF_CHANGED;
p->trap = (char *) 0;
}
oexstat = exstat;
/* Note: trapstr is fully parsed before anything is executed, thus
* no problem with afree(p->trap) in settrap() while still in use.
*/
command(trapstr);
exstat = oexstat;
if (i == SIGEXIT_ || i == SIGERR_) {
if (p->flags & TF_CHANGED)
/* don't clear TF_CHANGED */
afree(trapstr, APERM);
else
p->trap = trapstr;
p->flags |= old_changed;
}
}
/* clear pending traps and reset user's trap handlers; used after fork(2) */
void
cleartraps()
{
int i;
Trap *p;
trap = 0;
intrsig = 0;
fatal_trap = 0;
for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) {
p->set = 0;
if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
settrap(p, (char *) 0);
}
}
/* restore signals just before an exec(2) */
void
restoresigs()
{
int i;
Trap *p;
for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++)
if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
SS_RESTORE_CURR|SS_FORCE);
}
void
settrap(p, s)
Trap *p;
char *s;
{
handler_t f;
if (p->trap)
afree(p->trap, APERM);
p->trap = str_save(s, APERM); /* handles s == 0 */
p->flags |= TF_CHANGED;
f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
p->flags |= TF_USER_SET;
if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
f = trapsig;
else if (p->flags & TF_SHELL_USES) {
if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
/* do what user wants at exec time */
p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
if (f == SIG_IGN)
p->flags |= TF_EXEC_IGN;
else
p->flags |= TF_EXEC_DFL;
}
/* assumes handler already set to what shell wants it
* (normally trapsig, but could be j_sigchld() or SIG_IGN)
*/
return;
}
/* todo: should we let user know signal is ignored? how? */
setsig(p, f, SS_RESTORE_CURR|SS_USER);
}
/* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
* kill shell (unless user catches it and exits)
*/
int
block_pipe()
{
int restore_dfl = 0;
Trap *p = &sigtraps[SIGPIPE];
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
setsig(p, SIG_IGN, SS_RESTORE_CURR);
if (p->flags & TF_ORIG_DFL)
restore_dfl = 1;
} else if (p->cursig == SIG_DFL) {
setsig(p, SIG_IGN, SS_RESTORE_CURR);
restore_dfl = 1; /* restore to SIG_DFL */
}
return restore_dfl;
}
/* Called by c_print() to undo whatever block_pipe() did */
void
restore_pipe(restore_dfl)
int restore_dfl;
{
if (restore_dfl)
setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
}
/* Set action for a signal. Action may not be set if original
* action was SIG_IGN, depending on the value of flags and
* FTALKING.
*/
int
setsig(p, f, flags)
Trap *p;
handler_t f;
int flags;
{
struct sigaction sigact;
if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
return 1;
/* First time setting this signal? If so, get and note the current
* setting.
*/
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
sigaction(p->signal, &Sigact_ign, &sigact);
p->flags |= sigact.sa_handler == SIG_IGN ?
TF_ORIG_IGN : TF_ORIG_DFL;
p->cursig = SIG_IGN;
}
/* Generally, an ignored signal stays ignored, except if
* - the user of an interactive shell wants to change it
* - the shell wants for force a change
*/
if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE)
&& (!(flags & SS_USER) || !Flag(FTALKING)))
return 0;
setexecsig(p, flags & SS_RESTORE_MASK);
/* This is here 'cause there should be a way of clearing shtraps, but
* don't know if this is a sane way of doing it. At the moment,
* all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
*/
if (!(flags & SS_USER))
p->shtrap = (handler_t) 0;
if (flags & SS_SHTRAP) {
p->shtrap = f;
f = trapsig;
}
if (p->cursig != f) {
p->cursig = f;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = KSH_SA_FLAGS;
sigact.sa_handler = f;
sigaction(p->signal, &sigact, (struct sigaction *) 0);
}
return 1;
}
/* control what signal is set to before an exec() */
void
setexecsig(p, restore)
Trap *p;
int restore;
{
/* XXX debugging */
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
internal_errorf(1, "setexecsig: unset signal %d(%s)",
p->signal, p->name);
/* restore original value for exec'd kids */
p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
switch (restore & SS_RESTORE_MASK) {
case SS_RESTORE_CURR: /* leave things as they currently are */
break;
case SS_RESTORE_ORIG:
p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
break;
case SS_RESTORE_DFL:
p->flags |= TF_EXEC_DFL;
break;
case SS_RESTORE_IGN:
p->flags |= TF_EXEC_IGN;
break;
}
}

760
tree.c Normal file
View File

@ -0,0 +1,760 @@
/* $OpenBSD: tree.c,v 1.10 2002/02/27 19:37:09 dhartmei Exp $ */
/*
* command tree climbing
*/
#include "sh.h"
#define INDENT 4
#define tputc(c, shf) shf_putchar(c, shf);
static void ptree ARGS((struct op *t, int indent, struct shf *f));
static void pioact ARGS((struct shf *f, int indent, struct ioword *iop));
static void tputC ARGS((int c, struct shf *shf));
static void tputS ARGS((char *wp, struct shf *shf));
static void vfptreef ARGS((struct shf *shf, int indent, const char *fmt, va_list va));
static struct ioword **iocopy ARGS((struct ioword **iow, Area *ap));
static void iofree ARGS((struct ioword **iow, Area *ap));
/*
* print a command tree
*/
static void
ptree(t, indent, shf)
register struct op *t;
int indent;
register struct shf *shf;
{
register char **w;
struct ioword **ioact;
struct op *t1;
Chain:
if (t == NULL)
return;
switch (t->type) {
case TCOM:
if (t->vars)
for (w = t->vars; *w != NULL; )
fptreef(shf, indent, "%S ", *w++);
else
fptreef(shf, indent, "#no-vars# ");
if (t->args)
for (w = t->args; *w != NULL; )
fptreef(shf, indent, "%S ", *w++);
else
fptreef(shf, indent, "#no-args# ");
break;
case TEXEC:
#if 0 /* ?not useful - can't be called? */
/* Print original vars */
if (t->left->vars)
for (w = t->left->vars; *w != NULL; )
fptreef(shf, indent, "%S ", *w++);
else
fptreef(shf, indent, "#no-vars# ");
/* Print expanded vars */
if (t->args)
for (w = t->args; *w != NULL; )
fptreef(shf, indent, "%s ", *w++);
else
fptreef(shf, indent, "#no-args# ");
/* Print original io */
t = t->left;
#else
t = t->left;
goto Chain;
#endif
case TPAREN:
fptreef(shf, indent + 2, "( %T) ", t->left);
break;
case TPIPE:
fptreef(shf, indent, "%T| ", t->left);
t = t->right;
goto Chain;
case TLIST:
fptreef(shf, indent, "%T%;", t->left);
t = t->right;
goto Chain;
case TOR:
case TAND:
fptreef(shf, indent, "%T%s %T",
t->left, (t->type==TOR) ? "||" : "&&", t->right);
break;
case TBANG:
fptreef(shf, indent, "! ");
t = t->right;
goto Chain;
case TDBRACKET:
{
int i;
fptreef(shf, indent, "[[");
for (i = 0; t->args[i]; i++)
fptreef(shf, indent, " %S", t->args[i]);
fptreef(shf, indent, " ]] ");
break;
}
#ifdef KSH
case TSELECT:
fptreef(shf, indent, "select %s ", t->str);
/* fall through */
#endif /* KSH */
case TFOR:
if (t->type == TFOR)
fptreef(shf, indent, "for %s ", t->str);
if (t->vars != NULL) {
fptreef(shf, indent, "in ");
for (w = t->vars; *w; )
fptreef(shf, indent, "%S ", *w++);
fptreef(shf, indent, "%;");
}
fptreef(shf, indent + INDENT, "do%N%T", t->left);
fptreef(shf, indent, "%;done ");
break;
case TCASE:
fptreef(shf, indent, "case %S in", t->str);
for (t1 = t->left; t1 != NULL; t1 = t1->right) {
fptreef(shf, indent, "%N(");
for (w = t1->vars; *w != NULL; w++)
fptreef(shf, indent, "%S%c", *w,
(w[1] != NULL) ? '|' : ')');
fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left);
}
fptreef(shf, indent, "%Nesac ");
break;
case TIF:
case TELIF:
/* 3 == strlen("if ") */
fptreef(shf, indent + 3, "if %T", t->left);
for (;;) {
t = t->right;
if (t->left != NULL) {
fptreef(shf, indent, "%;");
fptreef(shf, indent + INDENT, "then%N%T",
t->left);
}
if (t->right == NULL || t->right->type != TELIF)
break;
t = t->right;
fptreef(shf, indent, "%;");
/* 5 == strlen("elif ") */
fptreef(shf, indent + 5, "elif %T", t->left);
}
if (t->right != NULL) {
fptreef(shf, indent, "%;");
fptreef(shf, indent + INDENT, "else%;%T", t->right);
}
fptreef(shf, indent, "%;fi ");
break;
case TWHILE:
case TUNTIL:
/* 6 == strlen("while"/"until") */
fptreef(shf, indent + 6, "%s %T",
(t->type==TWHILE) ? "while" : "until",
t->left);
fptreef(shf, indent, "%;do");
fptreef(shf, indent + INDENT, "%;%T", t->right);
fptreef(shf, indent, "%;done ");
break;
case TBRACE:
fptreef(shf, indent + INDENT, "{%;%T", t->left);
fptreef(shf, indent, "%;} ");
break;
case TCOPROC:
fptreef(shf, indent, "%T|& ", t->left);
break;
case TASYNC:
fptreef(shf, indent, "%T& ", t->left);
break;
case TFUNCT:
fptreef(shf, indent,
t->u.ksh_func ? "function %s %T" : "%s() %T",
t->str, t->left);
break;
case TTIME:
fptreef(shf, indent, "time %T", t->left);
break;
default:
fptreef(shf, indent, "<botch>");
break;
}
if ((ioact = t->ioact) != NULL) {
int need_nl = 0;
while (*ioact != NULL)
pioact(shf, indent, *ioact++);
/* Print here documents after everything else... */
for (ioact = t->ioact; *ioact != NULL; ) {
struct ioword *iop = *ioact++;
/* heredoc is 0 when tracing (set -x) */
if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc) {
tputc('\n', shf);
shf_puts(iop->heredoc, shf);
fptreef(shf, indent, "%s",
evalstr(iop->delim, 0));
need_nl = 1;
}
}
/* Last delimiter must be followed by a newline (this often
* leads to an extra blank line, but its not worth worrying
* about)
*/
if (need_nl)
tputc('\n', shf);
}
}
static void
pioact(shf, indent, iop)
register struct shf *shf;
int indent;
register struct ioword *iop;
{
int flag = iop->flag;
int type = flag & IOTYPE;
int expected;
expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0
: (type == IOCAT || type == IOWRITE) ? 1
: (type == IODUP && (iop->unit == !(flag & IORDUP))) ?
iop->unit
: iop->unit + 1;
if (iop->unit != expected)
tputc('0' + iop->unit, shf);
switch (type) {
case IOREAD:
fptreef(shf, indent, "< ");
break;
case IOHERE:
if (flag&IOSKIP)
fptreef(shf, indent, "<<- ");
else
fptreef(shf, indent, "<< ");
break;
case IOCAT:
fptreef(shf, indent, ">> ");
break;
case IOWRITE:
if (flag&IOCLOB)
fptreef(shf, indent, ">| ");
else
fptreef(shf, indent, "> ");
break;
case IORDWR:
fptreef(shf, indent, "<> ");
break;
case IODUP:
if (flag & IORDUP)
fptreef(shf, indent, "<&");
else
fptreef(shf, indent, ">&");
break;
}
/* name/delim are 0 when printing syntax errors */
if (type == IOHERE) {
if (iop->delim)
fptreef(shf, indent, "%S ", iop->delim);
} else if (iop->name)
fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
iop->name);
}
/*
* variants of fputc, fputs for ptreef and snptreef
*/
static void
tputC(c, shf)
register int c;
register struct shf *shf;
{
if ((c&0x60) == 0) { /* C0|C1 */
tputc((c&0x80) ? '$' : '^', shf);
tputc(((c&0x7F)|0x40), shf);
} else if ((c&0x7F) == 0x7F) { /* DEL */
tputc((c&0x80) ? '$' : '^', shf);
tputc('?', shf);
} else
tputc(c, shf);
}
static void
tputS(wp, shf)
register char *wp;
register struct shf *shf;
{
register int c, quoted=0;
/* problems:
* `...` -> $(...)
* 'foo' -> "foo"
* could change encoding to:
* OQUOTE ["'] ... CQUOTE ["']
* COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
*/
while (1)
switch ((c = *wp++)) {
case EOS:
return;
case CHAR:
tputC(*wp++, shf);
break;
case QCHAR:
c = *wp++;
if (!quoted || (c == '"' || c == '`' || c == '$'))
tputc('\\', shf);
tputC(c, shf);
break;
case COMSUB:
tputc('$', shf);
tputc('(', shf);
while (*wp != 0)
tputC(*wp++, shf);
tputc(')', shf);
wp++;
break;
case EXPRSUB:
tputc('$', shf);
tputc('(', shf);
tputc('(', shf);
while (*wp != 0)
tputC(*wp++, shf);
tputc(')', shf);
tputc(')', shf);
wp++;
break;
case OQUOTE:
quoted = 1;
tputc('"', shf);
break;
case CQUOTE:
quoted = 0;
tputc('"', shf);
break;
case OSUBST:
tputc('$', shf);
if (*wp++ == '{')
tputc('{', shf);
while ((c = *wp++) != 0)
tputC(c, shf);
break;
case CSUBST:
if (*wp++ == '}')
tputc('}', shf);
break;
#ifdef KSH
case OPAT:
tputc(*wp++, shf);
tputc('(', shf);
break;
case SPAT:
tputc('|', shf);
break;
case CPAT:
tputc(')', shf);
break;
#endif /* KSH */
}
}
/*
* this is the _only_ way to reliably handle
* variable args with an ANSI compiler
*/
/* VARARGS */
int
#ifdef HAVE_PROTOTYPES
fptreef(struct shf *shf, int indent, const char *fmt, ...)
#else
fptreef(shf, indent, fmt, va_alist)
struct shf *shf;
int indent;
const char *fmt;
va_dcl
#endif
{
va_list va;
SH_VA_START(va, fmt);
vfptreef(shf, indent, fmt, va);
va_end(va);
return 0;
}
/* VARARGS */
char *
#ifdef HAVE_PROTOTYPES
snptreef(char *s, int n, const char *fmt, ...)
#else
snptreef(s, n, fmt, va_alist)
char *s;
int n;
const char *fmt;
va_dcl
#endif
{
va_list va;
struct shf shf;
shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
SH_VA_START(va, fmt);
vfptreef(&shf, 0, fmt, va);
va_end(va);
return shf_sclose(&shf); /* null terminates */
}
static void
vfptreef(shf, indent, fmt, va)
register struct shf *shf;
int indent;
const char *fmt;
register va_list va;
{
register int c;
while ((c = *fmt++))
if (c == '%') {
register long n;
register char *p;
int neg;
switch ((c = *fmt++)) {
case 'c':
tputc(va_arg(va, int), shf);
break;
case 's':
p = va_arg(va, char *);
while (*p)
tputc(*p++, shf);
break;
case 'S': /* word */
p = va_arg(va, char *);
tputS(p, shf);
break;
case 'd': case 'u': /* decimal */
n = (c == 'd') ? va_arg(va, int)
: va_arg(va, unsigned int);
neg = c=='d' && n<0;
p = ulton((neg) ? -n : n, 10);
if (neg)
*--p = '-';
while (*p)
tputc(*p++, shf);
break;
case 'T': /* format tree */
ptree(va_arg(va, struct op *), indent, shf);
break;
case ';': /* newline or ; */
case 'N': /* newline or space */
if (shf->flags & SHF_STRING) {
if (c == ';')
tputc(';', shf);
tputc(' ', shf);
} else {
int i;
tputc('\n', shf);
for (i = indent; i >= 8; i -= 8)
tputc('\t', shf);
for (; i > 0; --i)
tputc(' ', shf);
}
break;
case 'R':
pioact(shf, indent, va_arg(va, struct ioword *));
break;
default:
tputc(c, shf);
break;
}
} else
tputc(c, shf);
}
/*
* copy tree (for function definition)
*/
struct op *
tcopy(t, ap)
register struct op *t;
Area *ap;
{
register struct op *r;
register char **tw, **rw;
if (t == NULL)
return NULL;
r = (struct op *) alloc(sizeof(struct op), ap);
r->type = t->type;
r->u.evalflags = t->u.evalflags;
r->str = t->type == TCASE ? wdcopy(t->str, ap) : str_save(t->str, ap);
if (t->vars == NULL)
r->vars = NULL;
else {
for (tw = t->vars; *tw++ != NULL; )
;
rw = r->vars = (char **)
alloc((tw - t->vars + 1) * sizeof(*tw), ap);
for (tw = t->vars; *tw != NULL; )
*rw++ = wdcopy(*tw++, ap);
*rw = NULL;
}
if (t->args == NULL)
r->args = NULL;
else {
for (tw = t->args; *tw++ != NULL; )
;
rw = r->args = (char **)
alloc((tw - t->args + 1) * sizeof(*tw), ap);
for (tw = t->args; *tw != NULL; )
*rw++ = wdcopy(*tw++, ap);
*rw = NULL;
}
r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
r->left = tcopy(t->left, ap);
r->right = tcopy(t->right, ap);
r->lineno = t->lineno;
return r;
}
char *
wdcopy(wp, ap)
const char *wp;
Area *ap;
{
size_t len = wdscan(wp, EOS) - wp;
return memcpy(alloc(len, ap), wp, len);
}
/* return the position of prefix c in wp plus 1 */
char *
wdscan(wp, c)
register const char *wp;
register int c;
{
register int nest = 0;
while (1)
switch (*wp++) {
case EOS:
return (char *) wp;
case CHAR:
case QCHAR:
wp++;
break;
case COMSUB:
case EXPRSUB:
while (*wp++ != 0)
;
break;
case OQUOTE:
case CQUOTE:
break;
case OSUBST:
nest++;
while (*wp++ != '\0')
;
break;
case CSUBST:
wp++;
if (c == CSUBST && nest == 0)
return (char *) wp;
nest--;
break;
#ifdef KSH
case OPAT:
nest++;
wp++;
break;
case SPAT:
case CPAT:
if (c == wp[-1] && nest == 0)
return (char *) wp;
if (wp[-1] == CPAT)
nest--;
break;
#endif /* KSH */
default:
internal_errorf(0,
"wdscan: unknown char 0x%x (carrying on)",
wp[-1]);
}
}
/* return a copy of wp without any of the mark up characters and
* with quote characters (" ' \) stripped.
* (string is allocated from ATEMP)
*/
char *
wdstrip(wp)
const char *wp;
{
struct shf shf;
int c;
shf_sopen((char *) 0, 32, SHF_WR | SHF_DYNAMIC, &shf);
/* problems:
* `...` -> $(...)
* x${foo:-"hi"} -> x${foo:-hi}
* x${foo:-'hi'} -> x${foo:-hi}
*/
while (1)
switch ((c = *wp++)) {
case EOS:
return shf_sclose(&shf); /* null terminates */
case CHAR:
case QCHAR:
shf_putchar(*wp++, &shf);
break;
case COMSUB:
shf_putchar('$', &shf);
shf_putchar('(', &shf);
while (*wp != 0)
shf_putchar(*wp++, &shf);
shf_putchar(')', &shf);
break;
case EXPRSUB:
shf_putchar('$', &shf);
shf_putchar('(', &shf);
shf_putchar('(', &shf);
while (*wp != 0)
shf_putchar(*wp++, &shf);
shf_putchar(')', &shf);
shf_putchar(')', &shf);
break;
case OQUOTE:
break;
case CQUOTE:
break;
case OSUBST:
shf_putchar('$', &shf);
if (*wp++ == '{')
shf_putchar('{', &shf);
while ((c = *wp++) != 0)
shf_putchar(c, &shf);
break;
case CSUBST:
if (*wp++ == '}')
shf_putchar('}', &shf);
break;
#ifdef KSH
case OPAT:
shf_putchar(*wp++, &shf);
shf_putchar('(', &shf);
break;
case SPAT:
shf_putchar('|', &shf);
break;
case CPAT:
shf_putchar(')', &shf);
break;
#endif /* KSH */
}
}
static struct ioword **
iocopy(iow, ap)
register struct ioword **iow;
Area *ap;
{
register struct ioword **ior;
register int i;
for (ior = iow; *ior++ != NULL; )
;
ior = (struct ioword **) alloc((ior - iow + 1) * sizeof(*ior), ap);
for (i = 0; iow[i] != NULL; i++) {
register struct ioword *p, *q;
p = iow[i];
q = (struct ioword *) alloc(sizeof(*p), ap);
ior[i] = q;
*q = *p;
if (p->name != (char *) 0)
q->name = wdcopy(p->name, ap);
if (p->delim != (char *) 0)
q->delim = wdcopy(p->delim, ap);
if (p->heredoc != (char *) 0)
q->heredoc = str_save(p->heredoc, ap);
}
ior[i] = NULL;
return ior;
}
/*
* free tree (for function definition)
*/
void
tfree(t, ap)
register struct op *t;
Area *ap;
{
register char **w;
if (t == NULL)
return;
if (t->str != NULL)
afree((void*)t->str, ap);
if (t->vars != NULL) {
for (w = t->vars; *w != NULL; w++)
afree((void*)*w, ap);
afree((void*)t->vars, ap);
}
if (t->args != NULL) {
for (w = t->args; *w != NULL; w++)
afree((void*)*w, ap);
afree((void*)t->args, ap);
}
if (t->ioact != NULL)
iofree(t->ioact, ap);
tfree(t->left, ap);
tfree(t->right, ap);
afree((void*)t, ap);
}
static void
iofree(iow, ap)
struct ioword **iow;
Area *ap;
{
register struct ioword **iop;
register struct ioword *p;
for (iop = iow; (p = *iop++) != NULL; ) {
if (p->name != NULL)
afree((void*)p->name, ap);
if (p->delim != NULL)
afree((void*)p->delim, ap);
if (p->heredoc != NULL)
afree((void*)p->heredoc, ap);
afree((void*)p, ap);
}
}

142
tree.h Normal file
View File

@ -0,0 +1,142 @@
/* $OpenBSD: tree.h,v 1.7 1999/07/14 13:37:24 millert Exp $ */
/*
* command trees for compile/execute
*/
/* $From: tree.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */
#define NOBLOCK ((struct op *)NULL)
#define NOWORD ((char *)NULL)
#define NOWORDS ((char **)NULL)
/*
* Description of a command or an operation on commands.
*/
struct op {
short type; /* operation type, see below */
union { /* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */
short evalflags; /* TCOM: arg expansion eval() flags */
short ksh_func; /* TFUNC: function x (vs x()) */
} u;
char **args; /* arguments to a command */
char **vars; /* variable assignments */
struct ioword **ioact; /* IO actions (eg, < > >>) */
struct op *left, *right; /* descendents */
char *str; /* word for case; identifier for for,
* select, and functions;
* path to execute for TEXEC;
* time hook for TCOM.
*/
int lineno; /* TCOM/TFUNC: LINENO for this */
};
/* Tree.type values */
#define TEOF 0
#define TCOM 1 /* command */
#define TPAREN 2 /* (c-list) */
#define TPIPE 3 /* a | b */
#define TLIST 4 /* a ; b */
#define TOR 5 /* || */
#define TAND 6 /* && */
#define TBANG 7 /* ! */
#define TDBRACKET 8 /* [[ .. ]] */
#define TFOR 9
#define TSELECT 10
#define TCASE 11
#define TIF 12
#define TWHILE 13
#define TUNTIL 14
#define TELIF 15
#define TPAT 16 /* pattern in case */
#define TBRACE 17 /* {c-list} */
#define TASYNC 18 /* c & */
#define TFUNCT 19 /* function name { command; } */
#define TTIME 20 /* time pipeline */
#define TEXEC 21 /* fork/exec eval'd TCOM */
#define TCOPROC 22 /* coprocess |& */
/*
* prefix codes for words in command tree
*/
#define EOS 0 /* end of string */
#define CHAR 1 /* unquoted character */
#define QCHAR 2 /* quoted character */
#define COMSUB 3 /* $() substitution (0 terminated) */
#define EXPRSUB 4 /* $(()) substitution (0 terminated) */
#define OQUOTE 5 /* opening " or ' */
#define CQUOTE 6 /* closing " or ' */
#define OSUBST 7 /* opening ${ subst (followed by { or X) */
#define CSUBST 8 /* closing } of above (followed by } or X) */
#define OPAT 9 /* open pattern: *(, @(, etc. */
#define SPAT 10 /* separate pattern: | */
#define CPAT 11 /* close pattern: ) */
/*
* IO redirection
*/
struct ioword {
int unit; /* unit affected */
int flag; /* action (below) */
char *name; /* file name (unused if heredoc) */
char *delim; /* delimiter for <<,<<- */
char *heredoc;/* content of heredoc */
};
/* ioword.flag - type of redirection */
#define IOTYPE 0xF /* type: bits 0:3 */
#define IOREAD 0x1 /* < */
#define IOWRITE 0x2 /* > */
#define IORDWR 0x3 /* <>: todo */
#define IOHERE 0x4 /* << (here file) */
#define IOCAT 0x5 /* >> */
#define IODUP 0x6 /* <&/>& */
#define IOEVAL BIT(4) /* expand in << */
#define IOSKIP BIT(5) /* <<-, skip ^\t* */
#define IOCLOB BIT(6) /* >|, override -o noclobber */
#define IORDUP BIT(7) /* x<&y (as opposed to x>&y) */
#define IONAMEXP BIT(8) /* name has been expanded */
/* execute/exchild flags */
#define XEXEC BIT(0) /* execute without forking */
#define XFORK BIT(1) /* fork before executing */
#define XBGND BIT(2) /* command & */
#define XPIPEI BIT(3) /* input is pipe */
#define XPIPEO BIT(4) /* output is pipe */
#define XPIPE (XPIPEI|XPIPEO) /* member of pipe */
#define XXCOM BIT(5) /* `...` command */
#define XPCLOSE BIT(6) /* exchild: close close_fd in parent */
#define XCCLOSE BIT(7) /* exchild: close close_fd in child */
#define XERROK BIT(8) /* non-zero exit ok (for set -e) */
#define XCOPROC BIT(9) /* starting a co-process */
#define XTIME BIT(10) /* timeing TCOM command */
#define XINTACT BIT(11) /* OS2: proc started from interactive session */
/*
* flags to control expansion of words (assumed by t->evalflags to fit
* in a short)
*/
#define DOBLANK BIT(0) /* perform blank interpretation */
#define DOGLOB BIT(1) /* expand [?* */
#define DOPAT BIT(2) /* quote *?[ */
#define DOTILDE BIT(3) /* normal ~ expansion (first char) */
#define DONTRUNCOMMAND BIT(4) /* do not run $(command) things */
#define DOASNTILDE BIT(5) /* assignment ~ expansion (after =, :) */
#define DOBRACE_ BIT(6) /* used by expand(): do brace expansion */
#define DOMAGIC_ BIT(7) /* used by expand(): string contains MAGIC */
#define DOTEMP_ BIT(8) /* ditto : in word part of ${..[%#=?]..} */
#define DOVACHECK BIT(9) /* var assign check (for typeset, set, etc) */
#define DOMARKDIRS BIT(10) /* force markdirs behaviour */
/*
* The arguments of [[ .. ]] expressions are kept in t->args[] and flags
* indicating how the arguments have been munged are kept in t->vars[].
* The contents of t->vars[] are stuffed strings (so they can be treated
* like all other t->vars[]) in which the second character is the one that
* is examined. The DB_* defines are the values for these second characters.
*/
#define DB_NORM 1 /* normal argument */
#define DB_OR 2 /* || -> -o conversion */
#define DB_AND 3 /* && -> -a conversion */
#define DB_BE 4 /* an inserted -BE */
#define DB_PAT 5 /* a pattern argument */

179
tty.c Normal file
View File

@ -0,0 +1,179 @@
/* $OpenBSD: tty.c,v 1.2 1996/10/01 02:05:51 downsj Exp $ */
#include "sh.h"
#include "ksh_stat.h"
#define EXTERN
#include "tty.h"
#undef EXTERN
int
get_tty(fd, ts)
int fd;
TTY_state *ts;
{
int ret;
# ifdef HAVE_TERMIOS_H
ret = tcgetattr(fd, ts);
# else /* HAVE_TERIOS_H */
# ifdef HAVE_TERMIO_H
ret = ioctl(fd, TCGETA, ts);
# else /* HAVE_TERMIO_H */
ret = ioctl(fd, TIOCGETP, &ts->sgttyb);
# ifdef TIOCGATC
if (ioctl(fd, TIOCGATC, &ts->lchars) < 0)
ret = -1;
# else
if (ioctl(fd, TIOCGETC, &ts->tchars) < 0)
ret = -1;
# ifdef TIOCGLTC
if (ioctl(fd, TIOCGLTC, &ts->ltchars) < 0)
ret = -1;
# endif /* TIOCGLTC */
# endif /* TIOCGATC */
# endif /* HAVE_TERMIO_H */
# endif /* HAVE_TERIOS_H */
return ret;
}
int
set_tty(fd, ts, flags)
int fd;
TTY_state *ts;
int flags;
{
int ret = 0;
# ifdef HAVE_TERMIOS_H
ret = tcsetattr(fd, TCSADRAIN, ts);
# else /* HAVE_TERIOS_H */
# ifdef HAVE_TERMIO_H
# ifndef TCSETAW /* e.g. Cray-2 */
/* first wait for output to drain */
# ifdef TCSBRK
if (ioctl(tty_fd, TCSBRK, 1) < 0)
ret = -1;
# else /* the following kludge is minimally intrusive, but sometimes fails */
if (flags & TF_WAIT)
sleep((unsigned)1); /* fake it */
# endif
# endif /* !TCSETAW */
# if defined(_BSD_SYSV) || !defined(TCSETAW)
/* _BSD_SYSV must force TIOCSETN instead of TIOCSETP (preserve type-ahead) */
if (ioctl(tty_fd, TCSETA, ts) < 0)
ret = -1;
# else
if (ioctl(tty_fd, TCSETAW, ts) < 0)
ret = -1;
# endif
# else /* HAVE_TERMIO_H */
# if defined(__mips) && (defined(_SYSTYPE_BSD43) || defined(__SYSTYPE_BSD43))
/* Under RISC/os 5.00, bsd43 environment, after a tty driver
* generated interrupt (eg, INTR, TSTP), all output to tty is
* lost until a SETP is done (there must be a better way of
* doing this...).
*/
if (flags & TF_MIPSKLUDGE)
ret = ioctl(fd, TIOCSETP, &ts->sgttyb);
else
# endif /* _SYSTYPE_BSD43 */
ret = ioctl(fd, TIOCSETN, &ts->sgttyb);
# ifdef TIOCGATC
if (ioctl(fd, TIOCSATC, &ts->lchars) < 0)
ret = -1;
# else
if (ioctl(fd, TIOCSETC, &ts->tchars) < 0)
ret = -1;
# ifdef TIOCGLTC
if (ioctl(fd, TIOCSLTC, &ts->ltchars) < 0)
ret = -1;
# endif /* TIOCGLTC */
# endif /* TIOCGATC */
# endif /* HAVE_TERMIO_H */
# endif /* HAVE_TERIOS_H */
return ret;
}
/* Initialize tty_fd. Used for saving/reseting tty modes upon
* foreground job completion and for setting up tty process group.
*/
void
tty_init(init_ttystate)
int init_ttystate;
{
int do_close = 1;
int tfd;
if (tty_fd >= 0) {
close(tty_fd);
tty_fd = -1;
}
tty_devtty = 1;
/* SCO can't job control on /dev/tty, so don't try... */
#if !defined(__SCO__)
if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) {
#ifdef __NeXT
/* rlogin on NeXT boxes does not set up the controlling tty,
* so force it to be done here...
*/
{
extern char *ttyname ARGS((int));
char *s = ttyname(isatty(2) ? 2 : 0);
int fd;
if (s && (fd = open(s, O_RDWR, 0)) >= 0) {
close(fd);
tfd = open("/dev/tty", O_RDWR, 0);
}
}
#endif /* __NeXT */
/* X11R5 xterm on mips doesn't set controlling tty properly - temporary hack */
# if !defined(__mips) || !(defined(_SYSTYPE_BSD43) || defined(__SYSTYPE_BSD43))
if (tfd < 0) {
tty_devtty = 0;
warningf(FALSE,
"No controlling tty (open /dev/tty: %s)",
strerror(errno));
}
# endif /* __mips */
}
#else /* !__SCO__ */
tfd = -1;
#endif /* __SCO__ */
if (tfd < 0) {
do_close = 0;
if (isatty(0))
tfd = 0;
else if (isatty(2))
tfd = 2;
else {
warningf(FALSE, "Can't find tty file descriptor");
return;
}
}
if ((tty_fd = ksh_dupbase(tfd, FDBASE)) < 0) {
warningf(FALSE, "j_ttyinit: dup of tty fd failed: %s",
strerror(errno));
} else if (fd_clexec(tty_fd) < 0) {
warningf(FALSE, "j_ttyinit: can't set close-on-exec flag: %s",
strerror(errno));
close(tty_fd);
tty_fd = -1;
} else if (init_ttystate)
get_tty(tty_fd, &tty_state);
if (do_close)
close(tfd);
}
void
tty_close()
{
if (tty_fd >= 0) {
close(tty_fd);
tty_fd = -1;
}
}

109
tty.h Normal file
View File

@ -0,0 +1,109 @@
/* $OpenBSD: tty.h,v 1.2 1996/11/21 07:59:36 downsj Exp $ */
/*
tty.h -- centralized definitions for a variety of terminal interfaces
created by DPK, Oct. 1986
Rearranged to work with autoconf, added TTY_state, get_tty/set_tty
Michael Rendell, May '94
last edit: 30-Jul-1987 D A Gwyn
*/
/* some useful #defines */
#ifdef EXTERN
# define I__(i) = i
#else
# define I__(i)
# define EXTERN extern
# define EXTERN_DEFINED
#endif
/* Don't know of a system on which including sys/ioctl.h with termios.h
* causes problems. If there is one, these lines need to be deleted and
* aclocal.m4 needs to have stuff un-commented.
*/
#ifdef SYS_IOCTL_WITH_TERMIOS
# define SYS_IOCTL_WITH_TERMIOS
#endif /* SYS_IOCTL_WITH_TERMIOS */
#ifdef SYS_IOCTL_WITH_TERMIO
# define SYS_IOCTL_WITH_TERMIO
#endif /* SYS_IOCTL_WITH_TERMIO */
#ifdef HAVE_TERMIOS_H
# include <termios.h>
# ifdef SYS_IOCTL_WITH_TERMIOS
# if !(defined(sun) && !defined(__svr4__)) /* too many warnings on sunos */
/* Need to include sys/ioctl.h on some systems to get the TIOCGWINSZ
* stuff (eg, digital unix).
*/
# include <sys/ioctl.h>
# endif /* !(sun && !__svr4__) */
# endif /* SYS_IOCTL_WITH_TERMIOS */
typedef struct termios TTY_state;
#else
# ifdef HAVE_TERMIO_H
# include <termio.h>
# ifdef SYS_IOCTL_WITH_TERMIO
# include <sys/ioctl.h> /* see comment above in termios stuff */
# endif /* SYS_IOCTL_WITH_TERMIO */
# if _BSD_SYSV /* BRL UNIX System V emulation */
# ifndef NTTYDISC
# define TIOCGETD _IOR( 't', 0, int )
# define TIOCSETD _IOW( 't', 1, int )
# define NTTYDISC 2
# endif
# ifndef TIOCSTI
# define TIOCSTI _IOW( 't', 114, char )
# endif
# ifndef TIOCSPGRP
# define TIOCSPGRP _IOW( 't', 118, int )
# endif
# endif /* _BSD_SYSV */
typedef struct termio TTY_state;
# else /* HAVE_TERMIO_H */
/* Assume BSD tty stuff. Uses TIOCGETP, TIOCSETN; uses TIOCGATC/TIOCSATC if
* available, otherwise it uses TIOCGETC/TIOCSETC (also uses TIOCGLTC/TIOCSLTC
* if available)
*/
# ifdef _MINIX
# include <sgtty.h>
# define TIOCSETN TIOCSETP
# else
# include <sys/ioctl.h>
# endif
typedef struct {
struct sgttyb sgttyb;
# ifdef TIOCGATC
struct lchars lchars;
# else /* TIOCGATC */
struct tchars tchars;
# ifdef TIOCGLTC
struct ltchars ltchars;
# endif /* TIOCGLTC */
# endif /* TIOCGATC */
} TTY_state;
# endif /* HAVE_TERMIO_H */
#endif /* HAVE_TERMIOS_H */
/* Flags for set_tty() */
#define TF_NONE 0x00
#define TF_WAIT 0x01 /* drain output, even it requires sleep() */
#define TF_MIPSKLUDGE 0x02 /* kludge to unwedge RISC/os 5.0 tty driver */
EXTERN int tty_fd I__(-1); /* dup'd tty file descriptor */
EXTERN int tty_devtty; /* true if tty_fd is from /dev/tty */
EXTERN TTY_state tty_state; /* saved tty state */
extern int get_tty ARGS((int fd, TTY_state *ts));
extern int set_tty ARGS((int fd, TTY_state *ts, int flags));
extern void tty_init ARGS((int init_ttystate));
extern void tty_close ARGS((void));
/* be sure not to interfere with anyone else's idea about EXTERN */
#ifdef EXTERN_DEFINED
# undef EXTERN_DEFINED
# undef EXTERN
#endif
#undef I__

1236
var.c Normal file

File diff suppressed because it is too large Load Diff

10
version.c Normal file
View File

@ -0,0 +1,10 @@
/* $OpenBSD: version.c,v 1.12 1999/07/14 13:37:24 millert Exp $ */
/*
* value of $KSH_VERSION (or $SH_VERSION)
*/
#include "sh.h"
const char ksh_version [] =
"@(#)PD KSH v5.2.14 99/07/13.2";

2190
vi.c Normal file

File diff suppressed because it is too large Load Diff