Import OpenBSD 3.3 source repository from CTM 3132 the first time
This opens an OpenBSD-mirabile (aka MirBSD) repository. ### MirBSD is: # Copyright (c) 1982-2003 by Thorsten "mirabile" Glaser <x86@ePost.de> # Copyright © 1968-2003 The authors of And contributors to UNIX®, the # C Language, BSD/Berkeley Unix; 386BSD, NetBSD 1.1 and OpenBSD. # # Anyone who obtained a copy of this work is hereby permitted to freely use, # distribute, modify, merge, sublicence, give away or sell it as long as the # authors are given due credit and the following notice is retained: # # This work is provided "as is", with no explicit or implicit warranty what- # soever. Use it only at your own risk. In no event may an author or contri- # butor be held liable for any damage, directly or indirectly, that origina- # ted through or is caused by creation or modification of this work. MirBSD is my private tree. MirBSD does not differ very much from OpenBSD and intentionally tracks OpenBSD. That's why it _is_ OpenBSD, just not the official one. It's like with DarrenBSD. At time of this writing, no advertising for MirBSD must be done, because the advertising clause has not yet been sorted out. http://templeofhate.com/tglaser/MirBSD/index.php
This commit is contained in:
commit
a34b05d2e6
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