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:
parent
7fbdbe578d
commit
80609f6010
1398
BUG-REPORTS
Normal file
1398
BUG-REPORTS
Normal file
File diff suppressed because it is too large
Load Diff
129
CONTRIBUTORS
Normal file
129
CONTRIBUTORS
Normal 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.
|
3589
ChangeLog.0
Normal file
3589
ChangeLog.0
Normal file
File diff suppressed because it is too large
Load Diff
18
IAFA-PACKAGE
Normal file
18
IAFA-PACKAGE
Normal 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
151
INSTALL
Normal 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
36
LEGAL
Normal 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
33
Makefile
Normal 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
662
NEWS
Normal 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
545
NOTES
Normal 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
111
PROJECTS
Normal 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
197
README
Normal 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
119
alloc.c
Normal 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);
|
||||
}
|
906
c_sh.c
Normal file
906
c_sh.c
Normal 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
664
c_test.c
Normal 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
55
c_test.h
Normal 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
273
c_ulimit.c
Normal 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
62
conf-end.h
Normal 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
364
config.h
Normal 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 */
|
87
edit.h
Normal file
87
edit.h
Normal 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
44
emacs-gen.sh
Normal 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
|
107
expand.h
Normal file
107
expand.h
Normal 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
607
expr.c
Normal 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;
|
||||
}
|
560
io.c
Normal file
560
io.c
Normal 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;
|
||||
}
|
26
ksh_dir.h
Normal file
26
ksh_dir.h
Normal 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
24
ksh_limval.h
Normal 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
59
ksh_stat.h
Normal 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
26
ksh_time.h
Normal 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
20
ksh_times.h
Normal 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
51
ksh_wait.h
Normal 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 */
|
132
lex.h
Normal file
132
lex.h
Normal 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
205
mail.c
Normal 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
863
main.c
Normal 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*/
|
||||
}
|
295
missing.c
Normal file
295
missing.c
Normal 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
312
path.c
Normal 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
303
proto.h
Normal 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
|
747
sh.h
Normal file
747
sh.h
Normal 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__
|
86
shf.h
Normal file
86
shf.h
Normal 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
56
siglist.in
Normal 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
42
siglist.sh
Normal 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
947
syn.c
Normal 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
240
table.c
Normal 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
183
table.h
Normal 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
22
tests/README
Normal 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
112
tests/alias.t
Normal 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
79
tests/arith.t
Normal 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
341
tests/bksl-nl.t
Normal 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
195
tests/brkcont.t
Normal 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
162
tests/cdhist.t
Normal 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
145
tests/eglob.t
Normal 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
99
tests/glob.t
Normal 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
331
tests/heredoc.t
Normal 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
559
tests/history.t
Normal 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
162
tests/ifs.t
Normal 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
218
tests/integer.t
Normal 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
111
tests/lineno.t
Normal 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
58
tests/read.t
Normal 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
1093
tests/regress.t
Normal file
File diff suppressed because it is too large
Load Diff
10
tests/syntax.t
Normal file
10
tests/syntax.t
Normal 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/
|
||||
---
|
||||
|
30
tests/th-sh
Normal file
30
tests/th-sh
Normal 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
30
tests/th.sh
Normal 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
99
tests/unclass1.t
Normal 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
163
tests/unclass2.t
Normal 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
9
tests/version.t
Normal 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
451
trap.c
Normal 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
760
tree.c
Normal 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
142
tree.h
Normal 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
179
tty.c
Normal 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
109
tty.h
Normal 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__
|
10
version.c
Normal file
10
version.c
Normal 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";
|
Loading…
x
Reference in New Issue
Block a user