* un-hook bin/ksh, usr.bin/xmlwf and lib/libexpat from the build

- expat as discussed with bsiegert@ today on the phone
  - ksh as announced earlier on the lists
* un-hook lib/libexpat from make includes
* remove /usr/include/{,open}ssl upgrade workaround from includes/Makefile
* nuke old bin/ksh
* nuke libexpat and xmlwf
This commit is contained in:
tg 2005-10-21 11:33:15 +00:00
parent b65ac4d1e3
commit d8d708aa45
65 changed files with 0 additions and 37170 deletions

View File

@ -1,133 +0,0 @@
$MirOS$
$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 and 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
* Thorsten Glaser (tg@66h.42h.de) releases mksh and is the guy
to blame for all mirbsdksh-specific or MirOS-specific bugs.
Released versions: 5.2.14 MirOS 1.1-1.x
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.

View File

@ -1,27 +0,0 @@
# $MirOS: src/bin/ksh/Makefile,v 1.4 2005/08/29 20:54:39 tg Exp $
# $OpenBSD: Makefile,v 1.18 2004/02/16 19:07:19 deraadt 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 main.c misc.c missing.c \
path.c shf.c syn.c table.c trap.c tree.c tty.c var.c vi.c
MAN= ksh.1
.include <bsd.own.mk>
COPTS+= -Wall -Wextra -pedantic
CPPFLAGS+= -DHAVE_CONFIG_H -I. -DMIRBSD_NATIVE
CLEANFILES+= emacs.out
LINKS= ${BINDIR}/ksh ${BINDIR}/rksh
MLINKS= ksh.1 rksh.1
beforedepend: emacs.out
emacs.out: emacs.c
${SHELL} ${.CURDIR}/emacs-gen.sh ${.CURDIR}/emacs.c >emacs.out
check:
/usr/bin/perl ${.CURDIR}/tests/th -s ${.CURDIR}/tests \
-p ./ksh -C pdksh,sh,ksh,posix,posix-upu
.include <bsd.prog.mk>

305
NOTES
View File

@ -1,305 +0,0 @@
$MirOS: src/bin/ksh/NOTES,v 1.1.7.1 2005/03/06 15:42:53 tg Exp $
$OpenBSD: NOTES,v 1.9 2003/10/26 15:07:25 jmc Exp $
* http://www.research.att.com/~gsf/public/ifs.sh
# tests 6856 passed 5192 failed 1664
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
- MAIL, MAILPATH, MAILCHECK have been removed in mirbsdksh.
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 occurring
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.
[...]
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.

78
README
View File

@ -1,78 +0,0 @@
$MirOS$
This is the README for mirbsdksh, developed as part of the MirBSD
operating system at The MirOS Project, and produced portably.
Legal information is provided about a screenpage farther down.
To build mksh on MirOS, issue 'make obj && make depend && make'.
To build mksh on other operating systems, read Builsd.sh first
or consult http://wiki.mirbsd.de/MirbsdKsh for further infos.
Set the CC, CFLAGS, CPPFLAGS and LDFLAGS environment variables
to your likes. Don't complain if anything doesn't work.
LEGAL INFORMATION
This package as a whole is placed under the MirOS licence template
as contained in the "Build.sh" source file in this distribution,
by having the project leader asserting a collective copyright.
The file alloc.c by Marc Espie is provided under a 2-clause BSD licence.
All files from the original pdksh distribution which were not public
domain have been removed from the source tree, with the exception of
aclocal.m4, from which only the functions which required the file to
be under the GNU GPL have been removed.
The information below is mostly unmaintained.
________________________________________________________________________
$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.
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).
Newer versions of pdksh may be available from
ftp://ftp.cs.mun.ca:/pub/pdksh/
[...]
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

122
alloc.c
View File

@ -1,122 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: alloc.c,v 1.7 2004/02/19 18:51:17 deraadt 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(sizeof(struct link) + size);
if (l == NULL)
internal_errorf(1, "unable to allocate memory");
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, sizeof(struct link) + size);
if (l2 == NULL)
internal_errorf(1, "unable to allocate memory");
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);
}

1406
c_ksh.c

File diff suppressed because it is too large Load Diff

861
c_sh.c
View File

@ -1,861 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: c_sh.c,v 1.25 2004/12/22 18:48:56 millert Exp $ */
/*
* built-in Bourne commands
*/
#include "sh.h"
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
__RCSID("$MirOS$");
static void p_time(struct shf *, int, struct timeval *, int, char *, char *);
/* :, false and true */
int
c_label(char **wp)
{
return wp[0][0] == 'f' ? 1 : 0;
}
int
c_shift(char **wp)
{
struct block *l = e->loc;
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(char **wp)
{
int i;
char *cp;
int symbolic = 0;
mode_t 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 {
mode_t 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(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 = NULL;
}
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(char **wp)
{
int UNINITIALIZED(rv);
int sig;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
wp += builtin_opt.optind;
if (*wp == NULL) {
while (waitfor(NULL, &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(char **wp)
{
int c = 0;
int expand = 1, history = 0;
int expanding;
int ecode = 0;
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) {
case 'p':
if ((fd = coproc_getfd(R_OK, &emsg)) < 0) {
bi_errorf("-p: %s", emsg);
return 1;
}
break;
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);
}
}
/* 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);
*/
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')
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, NULL);
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);
}
/* 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);
return ecode ? ecode : c == EOF;
}
int
c_eval(char **wp)
{
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(char **wp)
{
int i;
char *s;
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 = NSIG + 1; --i >= 0; p++) {
if (p->trap == NULL)
anydfl = 1;
else {
shprintf("trap -- ");
print_value_quoted(p->trap);
shprintf(" %s\n", p->name);
}
}
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(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(NULL); /* get rid of any i/o redirections */
unwind(how);
/*NOTREACHED*/
return 0;
}
int
c_brkcont(char **wp)
{
int n, quit;
struct env *ep, *last_ep = NULL;
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(char **wp)
{
int argi, setargs;
struct block *l = e->loc;
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(char **wp)
{
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;
}
static void
p_time(struct shf *shf, int posix, struct timeval *tv, int width,
char *prefix, char *suffix)
{
if (posix)
shf_fprintf(shf, "%s%*ld.%02ld%s", prefix ? prefix : "",
width, (long)tv->tv_sec, tv->tv_usec / 10000, suffix);
else
shf_fprintf(shf, "%s%*ldm%ld.%02lds%s", prefix ? prefix : "",
width, (long)(tv->tv_sec / 60), (long)(tv->tv_sec % 60),
tv->tv_usec / 10000, suffix);
}
int
c_times(char **wp GCC_FUNC_ATTR(unused))
{
struct rusage usage;
(void) getrusage(RUSAGE_SELF, &usage);
p_time(shl_stdout, 0, &usage.ru_utime, 0, NULL, " ");
p_time(shl_stdout, 0, &usage.ru_stime, 0, NULL, "\n");
(void) getrusage(RUSAGE_CHILDREN, &usage);
p_time(shl_stdout, 0, &usage.ru_utime, 0, NULL, " ");
p_time(shl_stdout, 0, &usage.ru_stime, 0, NULL, "\n");
return 0;
}
/*
* time pipeline (really a statement, not a built-in command)
*/
int
timex(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 rusage ru0, ru1, cru0, cru1;
struct timeval usrtime, systime, tv0, tv1;
int tf = 0;
extern struct timeval j_usrtime, j_systime; /* computed by j_wait */
char opts[1];
gettimeofday(&tv0, NULL);
getrusage(RUSAGE_SELF, &ru0);
getrusage(RUSAGE_CHILDREN, &cru0);
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).
*/
timerclear(&j_usrtime);
timerclear(&j_systime);
if (t->left->type == TCOM)
t->left->str = opts;
opts[0] = 0;
rv = execute(t->left, f | XTIME);
tf |= opts[0];
gettimeofday(&tv1, NULL);
getrusage(RUSAGE_SELF, &ru1);
getrusage(RUSAGE_CHILDREN, &cru1);
} else
tf = TF_NOARGS;
if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */
tf |= TF_NOREAL;
timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime);
timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime);
} else {
timersub(&ru1.ru_utime, &ru0.ru_utime, &usrtime);
timeradd(&usrtime, &j_usrtime, &usrtime);
timersub(&ru1.ru_stime, &ru0.ru_stime, &systime);
timeradd(&systime, &j_systime, &systime);
}
if (!(tf & TF_NOREAL)) {
timersub(&tv1, &tv0, &tv1);
if (tf & TF_POSIX)
p_time(shl_out, 1, &tv1, 5, "real ", "\n");
else
p_time(shl_out, 0, &tv1, 5, NULL, " real ");
}
if (tf & TF_POSIX)
p_time(shl_out, 1, &usrtime, 5, "user ", "\n");
else
p_time(shl_out, 0, &usrtime, 5, NULL, " user ");
if (tf & TF_POSIX)
p_time(shl_out, 1, &systime, 5, "sys ", "\n");
else
p_time(shl_out, 0, &systime, 5, NULL, " system\n");
shf_flush(shl_out);
return rv;
}
void
timex_hook(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;
}
/* exec with no args - args case is taken care of in comexec() */
int
c_exec(char **wp GCC_FUNC_ATTR(unused))
{
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).
*/
if (!Flag(FSH) && i > 2 && e->savefd[i])
fcntl(i, F_SETFD, FD_CLOEXEC);
}
e->savefd = NULL;
}
return 0;
}
/* dummy function, special case in comexec() */
int
c_builtin(char **wp GCC_FUNC_ATTR(unused))
{
return 0;
}
extern int c_test(char **wp); /* in c_test.c */
extern int c_ulimit(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},
{NULL, NULL}
};

626
c_test.c
View File

@ -1,626 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: c_test.c,v 1.10 2003/10/10 19:09:07 millert 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"
__RCSID("$MirOS$");
/* 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 },
{"==", TO_STEQL },
{"!=", 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(const char *path, struct stat *statb);
static int test_eaccess(const char *path, int mode);
static int test_oexpr(Test_env *te, int do_eval);
static int test_aexpr(Test_env *te, int do_eval);
static int test_nexpr(Test_env *te, int do_eval);
static int test_primary(Test_env *te, int do_eval);
static int ptest_isa(Test_env *te, Test_meta meta);
static const char *ptest_getopnd(Test_env *te, Test_op op, int do_eval);
static int ptest_eval(Test_env *te, Test_op op, const char *opnd1,
const char *opnd2, int do_eval);
static void ptest_error(Test_env *te, int offset, const char *msg);
int
c_test(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,
NULL, 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(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(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(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(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 */
res = eaccess(path, mode);
/*
* On most (all?) unixes, access() says everything is executable for
* root - avoid this on files by using stat().
*/
if (res == 0 && ksheuid == 0 && (mode & X_OK)) {
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;
}
return res;
}
int
test_parse(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(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(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(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(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, NULL, 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, NULL, 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(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(Test_env *te, Test_op op, int do_eval GCC_FUNC_ATTR(unused))
{
if (te->pos.wp >= te->wp_end)
return op == TO_FILTT ? "1" : NULL;
return *te->pos.wp++;
}
static int
ptest_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(Test_env *te, int offset, const char *msg)
{
const char *op = te->pos.wp + offset >= te->wp_end ?
NULL : te->pos.wp[offset];
te->flags |= TEF_ERROR;
if (op)
bi_errorf("%s: %s", op, msg);
else
bi_errorf("%s", msg);
}

View File

@ -1,61 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: c_test.h,v 1.2 2003/10/22 07:40:38 jmc Exp $ */
#ifndef C_TEST_H
#define C_TEST_H
/* Various types of operations. Keeping things grouped nicely
* (unary,binary) makes switch() statements more efficient.
*/
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)(Test_env *te, Test_meta meta);
const char *(*getopnd)(Test_env *te, Test_op op, int do_eval);
int (*eval)(Test_env *te, Test_op op, const char *opnd1,
const char *opnd2, int do_eval);
void (*error)(Test_env *te, int offset, const char *msg);
};
Test_op test_isop(Test_env *te, Test_meta meta, const char *s);
int test_eval(Test_env *te, Test_op op, const char *opnd1,
const char *opnd2, int do_eval);
int test_parse(Test_env *te);
#endif /* ndef C_TEST_H */

View File

@ -1,284 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: c_ulimit.c,v 1.10 2003/10/22 07:40:38 jmc Exp $ */
/*
ulimit -- handle "ulimit" builtin
Reworked to use getrusage() and ulimit() at once (as needed on
some schizophrenic 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 <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 */
__RCSID("$MirOS$");
#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(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_TIME
{ "humantime(seconds)", RLIMIT, RLIMIT_TIME, RLIMIT_TIME, 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(KiB)", RLIMIT, RLIMIT_DATA, RLIMIT_DATA, 1024, 'd' },
#endif
#ifdef RLIMIT_STACK
{ "stack(KiB)", RLIMIT, RLIMIT_STACK, RLIMIT_STACK, 1024, 's' },
#endif
#ifdef RLIMIT_MEMLOCK
{ "lockedmem(KiB)", RLIMIT, RLIMIT_MEMLOCK, RLIMIT_MEMLOCK, 1024, 'l' },
#endif
#ifdef RLIMIT_RSS
{ "memory(KiB)", 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(KiB)", 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(KiB)", RLIMIT, RLIMIT_SWAP, RLIMIT_SWAP, 1024, 'w' },
#endif
{ NULL, RLIMIT, 0, 0, 0, 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;
}
#ifdef HAVE_ULIMIT
else
#endif
#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;
}
}
#ifdef HAVE_ULIMIT
else
#endif
#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;
}

View File

@ -1,82 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: conf-end.h,v 1.2 1996/08/25 12:37:58 downsj Exp $ */
#ifndef CONF_END_H
#define CONF_END_H
/* Include job control? */
#define JOBS 1
/* Include complex history? */
#define COMPLEX_HISTORY
/* Specify default $ENV? */
/* #undef DEFAULT_ENV */
/*
* The above are defined for mirbsdksh via external
* means, such as this header ;-)
* End of configuration stuff for PD ksh.
*/
#if SIZEOF_INT < 4
# error "int cannot hold 32 bit"
#endif
/*
* 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(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)
# 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
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#else
# ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
# endif /* HAVE_SYS_TYPES_H */
#endif /* HAVE_SYS_PARAM_H */
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#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 */
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#else
#if defined(__GNUC__) && __GNUC__ >= 3
/* Support for _C99: type _Bool is already built-in. */
#define false 0
#define true 1
#else
typedef enum {
false = 0,
true = 1
} _Bool;
#define false false
#define true true
#endif
#define bool _Bool
#endif
#endif /* ndef CONF_END_H */

322
config.h
View File

@ -1,322 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: config.h,v 1.9 2003/10/22 07:40:38 jmc Exp $ */
/*
* This is mirbsdksh "config.h" for both MirOS systems, on which it
* is statically generated by the operating system maintainers, and
* for the portable mirbsdksh, where it is being fed to and genera-
* ted by the configure script.
*/
#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 <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 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 kernel 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 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 __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 to 'unsigned' if <signal.h> doesn't define */
/* #undef sigset_t */
/* 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 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 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 */
#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 */
/* The number of bytes in a int. */
#define SIZEOF_INT 4
/* The number of bytes in a long. */
#define SIZEOF_LONG 4
/* Define if you have the _setjmp function. */
/* #undef HAVE__SETJMP */
/* Define if you have the arc4random function. */
#define HAVE_ARC4RANDOM 1
/* Define if you have the arc4random_addrandom function. */
#define HAVE_ARC4RANDOM_ADDRANDOM 1
/* Define if you have the arc4random_push function. */
#define HAVE_ARC4RANDOM_PUSH 1
/* Define if you have the break function. */
/* #undef HAVE_BREAK */
/* Define if you have the confstr function. */
#define HAVE_CONFSTR 1
/* Define if you have the dup2 function. */
#define HAVE_DUP2 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. */
#define HAVE_GETGROUPS 1
/* Define if you have the getpagesize function. */
#define HAVE_GETPAGESIZE 1
/* Define if you have the getrusage function. */
#define HAVE_GETRUSAGE 1
/* 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 mkstemp function. */
#define HAVE_MKSTEMP 1
/* Define if you have the nice function. */
#define HAVE_NICE 1
/* Define if you have the random function. */
#define HAVE_RANDOM 1
/* Define if you have the revoke function. */
#define HAVE_REVOKE 1
/* Define if you have the setrlimit function. */
#define HAVE_SETRLIMIT 1
/* Define if you have the setsid function. */
#define HAVE_SETSID 1
/* Define if you have the sigsetjmp function. */
#define HAVE_SIGSETJMP 1
/* Define if you have the srandom function. */
#define HAVE_SRANDOM 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 strlcat function. */
#define HAVE_STRLCAT 1
/* Define if you have the strlcpy function. */
#define HAVE_STRLCPY 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 <stdbool.h> header file. */
#define HAVE_STDBOOL_H 1
/* Define if you have the <stddef.h> header file. */
#define HAVE_STDDEF_H 1
/* Define if you have the <stdint.h> header file. */
#define HAVE_STDINT_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/types.h> header file. */
#define HAVE_SYS_TYPES_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 */

955
edit.c
View File

@ -1,955 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: edit.c,v 1.23 2004/12/18 22:12:23 millert Exp $ */
/*
* Command line editing - common code
*
*/
#include "config.h"
#include "sh.h"
#include "tty.h"
#define EXTERN
#include "edit.h"
#undef EXTERN
#ifdef OS_SCO /* SCO Unix 3.2v4.1 */
# include <sys/stream.h> /* needed for <sys/ptem.h> */
# include <sys/ptem.h> /* needed for struct winsize */
#endif /* OS_SCO */
#include <sys/ioctl.h>
#include <ctype.h>
#include "ksh_stat.h"
__RCSID("$MirOS$");
#if defined(TIOCGWINSZ)
static RETSIGTYPE x_sigwinch(int sig);
static volatile sig_atomic_t got_sigwinch;
static void check_sigwinch(void);
#endif /* TIOCGWINSZ */
#if !defined(_POSIX_VDISABLE) || (_POSIX_VDISABLE < 0)
#define _POSIX_VDISABLE 0377
#endif
static int x_file_glob(int flags, const char *str, int slen,
char ***wordsp);
static int x_command_glob(int flags, const char *str, int slen,
char ***wordsp);
static int x_locate_word(const char *buf, int buflen, int pos,
int *startp, int *is_command);
/* Called from main */
void
x_init(void)
{
/* set to -2 to force initial binding */
edchars.erase = edchars.kill = edchars.intr = edchars.quit
= edchars.eof = -2;
/* default value for deficient systems */
edchars.werase = 027; /* ^W */
#ifdef TIOCGWINSZ
# ifdef SIGWINCH
if (setsig(&sigtraps[SIGWINCH], x_sigwinch, SS_RESTORE_ORIG|SS_SHTRAP))
sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
# endif /* SIGWINCH */
got_sigwinch = 1; /* force initial check */
check_sigwinch();
#endif /* TIOCGWINSZ */
x_init_emacs();
}
#if defined(TIOCGWINSZ)
static RETSIGTYPE
x_sigwinch(int sig GCC_FUNC_ATTR(unused))
{
got_sigwinch = 1;
return RETSIGVAL;
}
static void
check_sigwinch(void)
{
if (got_sigwinch) {
struct winsize ws;
got_sigwinch = 0;
if (procpid == kshpid && ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
struct tbl *vp;
/* Do NOT export COLUMNS/LINES. Many applications
* check COLUMNS/LINES before checking ws.ws_col/row,
* so if the app is started with C/L in the environ
* and the window is then resized, the app won't
* see the change cause the environ doesn't change.
*/
if (ws.ws_col) {
x_cols = ws.ws_col < MIN_COLS ? MIN_COLS
: ws.ws_col;
if ((vp = typeset("COLUMNS", 0, 0, 0, 0)))
setint(vp, (long) ws.ws_col);
}
if (ws.ws_row
&& (vp = typeset("LINES", 0, 0, 0, 0)))
setint(vp, (long) ws.ws_row);
}
}
}
#endif /* TIOCGWINSZ */
/*
* read an edited command line
*/
int
x_read(char *buf, size_t len)
{
int i;
#if defined(TIOCGWINSZ)
if (got_sigwinch)
check_sigwinch();
#endif /* TIOCGWINSZ */
x_mode(true);
if (Flag(FEMACS) || Flag(FGMACS))
i = x_emacs(buf, len);
else if (Flag(FVI))
i = x_vi(buf, len);
else
i = -1; /* internal error */
x_mode(false);
return i;
}
/* tty I/O */
int
x_getc(void)
{
char c;
int n;
while ((n = blocking_read(0, &c, 1)) < 0 && errno == EINTR)
if (trap) {
x_mode(false);
runtraps(0);
x_mode(true);
}
if (n != 1)
return -1;
return (int) (unsigned char) c;
}
void
x_flush(void)
{
shf_flush(shl_out);
}
void
x_putc(int c)
{
shf_putc(c, shl_out);
}
void
x_puts(const char *s)
{
while (*s != 0)
shf_putc(*s++, shl_out);
}
bool
x_mode(bool onoff)
{
static bool x_cur_mode;
bool prev;
if (x_cur_mode == onoff)
return x_cur_mode;
prev = x_cur_mode;
x_cur_mode = onoff;
if (onoff) {
struct termios cb;
X_chars oldchars;
oldchars = edchars;
cb = tty_state;
#if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H)
edchars.erase = cb.c_cc[VERASE];
edchars.kill = cb.c_cc[VKILL];
edchars.intr = cb.c_cc[VINTR];
edchars.quit = cb.c_cc[VQUIT];
edchars.eof = cb.c_cc[VEOF];
# ifdef VWERASE
edchars.werase = cb.c_cc[VWERASE];
# endif
# ifdef _CRAY2 /* brain-damaged terminal handler */
cb.c_lflag &= ~(ICANON|ECHO);
/* rely on print routine to map '\n' to CR,LF */
# else
cb.c_iflag &= ~(INLCR|ICRNL);
# ifdef _BSD_SYSV /* need to force CBREAK instead of RAW (need CRMOD on output) */
cb.c_lflag &= ~(ICANON|ECHO);
# else
cb.c_lflag &= ~(ISIG|ICANON|ECHO);
# endif
# ifdef VLNEXT
/* osf/1 processes lnext when ~icanon */
cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
# endif /* VLNEXT */
# ifdef VDISCARD
/* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */
cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
# endif /* VDISCARD */
cb.c_cc[VTIME] = 0;
cb.c_cc[VMIN] = 1;
# endif /* _CRAY2 */
#else
/* Assume BSD tty stuff. */
edchars.erase = cb.sgttyb.sg_erase;
edchars.kill = cb.sgttyb.sg_kill;
cb.sgttyb.sg_flags &= ~ECHO;
cb.sgttyb.sg_flags |= CBREAK;
# ifdef TIOCGATC
edchars.intr = cb.lchars.tc_intrc;
edchars.quit = cb.lchars.tc_quitc;
edchars.eof = cb.lchars.tc_eofc;
edchars.werase = cb.lchars.tc_werasc;
cb.lchars.tc_suspc = -1;
cb.lchars.tc_dsuspc = -1;
cb.lchars.tc_lnextc = -1;
cb.lchars.tc_statc = -1;
cb.lchars.tc_intrc = -1;
cb.lchars.tc_quitc = -1;
cb.lchars.tc_rprntc = -1;
# else
edchars.intr = cb.tchars.t_intrc;
edchars.quit = cb.tchars.t_quitc;
edchars.eof = cb.tchars.t_eofc;
cb.tchars.t_intrc = -1;
cb.tchars.t_quitc = -1;
# ifdef TIOCGLTC
edchars.werase = cb.ltchars.t_werasc;
cb.ltchars.t_suspc = -1;
cb.ltchars.t_dsuspc = -1;
cb.ltchars.t_lnextc = -1;
cb.ltchars.t_rprntc = -1;
# endif
# endif /* TIOCGATC */
#endif /* HAVE_TERMIOS_H || HAVE_TERMIO_H */
tcsetattr(tty_fd, TCSADRAIN, &cb);
#ifdef __CYGWIN__
if (edchars.eof == '\0')
edchars.eof = '\4';
#endif /* __CYGWIN__ */
/* Convert unset values to internal 'unset' value */
if (edchars.erase == _POSIX_VDISABLE)
edchars.erase = -1;
if (edchars.kill == _POSIX_VDISABLE)
edchars.kill = -1;
if (edchars.intr == _POSIX_VDISABLE)
edchars.intr = -1;
if (edchars.quit == _POSIX_VDISABLE)
edchars.quit = -1;
if (edchars.eof == _POSIX_VDISABLE)
edchars.eof = -1;
if (edchars.werase == _POSIX_VDISABLE)
edchars.werase = -1;
if (memcmp(&edchars, &oldchars, sizeof(edchars)) != 0)
x_emacs_keys(&edchars);
} else {
tcsetattr(tty_fd, TCSADRAIN, &tty_state);
}
return prev;
}
/* NAME:
* promptlen - calculate the length of PS1 etc.
*
* DESCRIPTION:
* This function is based on a fix from guy@demon.co.uk
* It fixes a bug in that if PS1 contains '!', the length
* given by strlen() is probably wrong.
*
* RETURN VALUE:
* length
*/
int
promptlen(const char *cp, const char **spp)
{
int count = 0;
const char *sp = cp;
char delimiter = 0;
int indelimit = 0;
/* Undocumented AT&T ksh feature:
* If the second char in the prompt string is \r then the first char
* is taken to be a non-printing delimiter and any chars between two
* instances of the delimiter are not considered to be part of the
* prompt length
*/
if (*cp && cp[1] == '\r') {
delimiter = *cp;
cp += 2;
}
for (; *cp; cp++) {
if (indelimit && *cp != delimiter)
;
else if (*cp == '\n' || *cp == '\r') {
count = 0;
sp = cp + 1;
} else if (*cp == '\t') {
count = (count | 7) + 1;
} else if (*cp == '\b') {
if (count > 0)
count--;
} else if (*cp == delimiter)
indelimit = !indelimit;
else
count++;
}
if (spp)
*spp = sp;
return count;
}
void
set_editmode(const char *ed)
{
static const enum sh_flag edit_flags[] = {
FEMACS, FGMACS,
FVI,
};
char *rcp;
unsigned i;
if ((rcp = ksh_strrchr_dirsep(ed)))
ed = ++rcp;
for (i = 0; i < NELEM(edit_flags); i++)
if (strstr(ed, options[(int) edit_flags[i]].name)) {
change_flag(edit_flags[i], OF_SPECIAL, 1);
return;
}
}
/* ------------------------------------------------------------------------- */
/* Misc common code for vi/emacs */
/* Handle the commenting/uncommenting of a line.
* Returns:
* 1 if a carriage return is indicated (comment added)
* 0 if no return (comment removed)
* -1 if there is an error (not enough room for comment chars)
* If successful, *lenp contains the new length. Note: cursor should be
* moved to the start of the line after (un)commenting.
*/
int
x_do_comment(char *buf, int bsize, int *lenp)
{
int i, j;
int len = *lenp;
if (len == 0)
return 1; /* somewhat arbitrary - it's what at&t ksh does */
/* Already commented? */
if (buf[0] == '#') {
int saw_nl = 0;
for (j = 0, i = 1; i < len; i++) {
if (!saw_nl || buf[i] != '#')
buf[j++] = buf[i];
saw_nl = buf[i] == '\n';
}
*lenp = j;
return 0;
} else {
int n = 1;
/* See if there's room for the #'s - 1 per \n */
for (i = 0; i < len; i++)
if (buf[i] == '\n')
n++;
if (len + n >= bsize)
return -1;
/* Now add them... */
for (i = len, j = len + n; --i >= 0; ) {
if (buf[i] == '\n')
buf[--j] = '#';
buf[--j] = buf[i];
}
buf[0] = '#';
*lenp += n;
return 1;
}
}
/* ------------------------------------------------------------------------- */
/* Common file/command completion code for vi/emacs */
static char *add_glob(const char *str, int slen);
static void glob_table(const char *pat, XPtrV *wp, struct table *tp);
static void glob_path(int flags, const char *pat, XPtrV *wp,
const char *path);
void
x_print_expansions(int nwords, char *const *words, int is_command)
{
int use_copy = 0;
int prefix_len;
XPtrV l;
/* Check if all matches are in the same directory (in this
* case, we want to omit the directory name)
*/
if (!is_command
&& (prefix_len = x_longest_prefix(nwords, words)) > 0)
{
int i;
/* Special case for 1 match (prefix is whole word) */
if (nwords == 1)
prefix_len = x_basename(words[0], NULL);
/* Any (non-trailing) slashes in non-common word suffixes? */
for (i = 0; i < nwords; i++)
if (x_basename(words[i] + prefix_len, NULL)
> prefix_len)
break;
/* All in same directory? */
if (i == nwords) {
while (prefix_len > 0
&& !ISDIRSEP(words[0][prefix_len - 1]))
prefix_len--;
use_copy = 1;
XPinit(l, nwords + 1);
for (i = 0; i < nwords; i++)
XPput(l, words[i] + prefix_len);
XPput(l, NULL);
}
}
/*
* Enumerate expansions
*/
x_putc('\r');
x_putc('\n');
pr_list(use_copy ? (char **) XPptrv(l) : words);
if (use_copy)
XPfree(l); /* not x_free_words() */
}
/*
* Do file globbing:
* - appends * to (copy of) str if no globbing chars found
* - does expansion, checks for no match, etc.
* - sets *wordsp to array of matching strings
* - returns number of matching strings
*/
static int
x_file_glob(int flags GCC_FUNC_ATTR(unused), const char *str,
int slen, char ***wordsp)
{
char *toglob;
char **words;
int nwords, i, idx, escaping;
XPtrV w;
struct source *s, *sold;
if (slen < 0)
return 0;
toglob = add_glob(str, slen);
/* remove all escaping backward slashes */
escaping = 0;
for(i = 0, idx = 0; toglob[i]; i++) {
if (toglob[i] == '\\' && !escaping) {
escaping = 1;
continue;
}
toglob[idx] = toglob[i];
idx++;
if (escaping) escaping = 0;
}
toglob[idx] = '\0';
/*
* Convert "foo*" (toglob) to an array of strings (words)
*/
sold = source;
s = pushs(SWSTR, ATEMP);
s->start = s->str = toglob;
source = s;
if (yylex(ONEWORD) != LWORD) {
source = sold;
internal_errorf(0, "fileglob: substitute error");
return 0;
}
source = sold;
XPinit(w, 32);
expand(yylval.cp, &w, DOGLOB|DOTILDE|DOMARKDIRS);
XPput(w, NULL);
words = (char **) XPclose(w);
for (nwords = 0; words[nwords]; nwords++)
;
if (nwords == 1) {
struct stat statb;
/* Check if globbing failed (returned glob pattern),
* but be careful (E.g. toglob == "ab*" when the file
* "ab*" exists is not an error).
* Also, check for empty result - happens if we tried
* to glob something which evaluated to an empty
* string (e.g., "$FOO" when there is no FOO, etc).
*/
if ((strcmp(words[0], toglob) == 0
&& stat(words[0], &statb) < 0)
|| words[0][0] == '\0')
{
x_free_words(nwords, words);
nwords = 0;
}
}
afree(toglob, ATEMP);
*wordsp = nwords ? words : NULL;
return nwords;
}
/* Data structure used in x_command_glob() */
struct path_order_info {
char *word;
int base;
int path_order;
};
static int path_order_cmp(const void *aa, const void *bb);
/* Compare routine used in x_command_glob() */
static int
path_order_cmp(const void *aa, const void *bb)
{
const struct path_order_info *a = (const struct path_order_info *) aa;
const struct path_order_info *b = (const struct path_order_info *) bb;
int t;
t = FILECMP(a->word + a->base, b->word + b->base);
return t ? t : a->path_order - b->path_order;
}
static int
x_command_glob(int flags, const char *str, int slen, char ***wordsp)
{
char *toglob;
char *pat;
char *fpath;
int nwords;
XPtrV w;
struct block *l;
if (slen < 0)
return 0;
toglob = add_glob(str, slen);
/* Convert "foo*" (toglob) to a pattern for future use */
pat = evalstr(toglob, DOPAT|DOTILDE);
afree(toglob, ATEMP);
XPinit(w, 32);
glob_table(pat, &w, &keywords);
glob_table(pat, &w, &aliases);
glob_table(pat, &w, &builtins);
for (l = e->loc; l; l = l->next)
glob_table(pat, &w, &l->funs);
glob_path(flags, pat, &w, path);
if ((fpath = str_val(global("FPATH"))) != null)
glob_path(flags, pat, &w, fpath);
nwords = XPsize(w);
if (!nwords) {
*wordsp = NULL;
XPfree(w);
return 0;
}
/* Sort entries */
if (flags & XCF_FULLPATH) {
/* Sort by basename, then path order */
struct path_order_info *info;
struct path_order_info *last_info = 0;
char **words = (char **) XPptrv(w);
int path_order = 0;
int i;
info = (struct path_order_info *)
alloc(sizeof(struct path_order_info) * nwords, ATEMP);
for (i = 0; i < nwords; i++) {
info[i].word = words[i];
info[i].base = x_basename(words[i], NULL);
if (!last_info || info[i].base != last_info->base
|| FILENCMP(words[i],
last_info->word, info[i].base) != 0)
{
last_info = &info[i];
path_order++;
}
info[i].path_order = path_order;
}
qsort(info, nwords, sizeof(struct path_order_info),
path_order_cmp);
for (i = 0; i < nwords; i++)
words[i] = info[i].word;
afree((void *) info, ATEMP);
} else {
/* Sort and remove duplicate entries */
char **words = (char **) XPptrv(w);
int i, j;
qsortp(XPptrv(w), (size_t) nwords, xstrcmp);
for (i = j = 0; i < nwords - 1; i++) {
if (strcmp(words[i], words[i + 1]))
words[j++] = words[i];
else
afree(words[i], ATEMP);
}
words[j++] = words[i];
nwords = j;
w.cur = (void **) &words[j];
}
XPput(w, NULL);
*wordsp = (char **) XPclose(w);
return nwords;
}
#define IS_WORDC(c) !( ctype(c, C_LEX1) || (c) == '\'' || (c) == '"' \
|| (c) == '`' || (c) == '=' || (c) == ':' )
static int
x_locate_word(const char *buf, int buflen, int pos, int *startp, int *is_commandp)
{
int p;
int start, end;
/* Bad call? Probably should report error */
if (pos < 0 || pos > buflen) {
*startp = pos;
*is_commandp = 0;
return 0;
}
/* The case where pos == buflen happens to take care of itself... */
start = pos;
/* Keep going backwards to start of word (has effect of allowing
* one blank after the end of a word)
*/
for (; (start > 0 && IS_WORDC(buf[start - 1]))
|| (start > 1 && buf[start-2] == '\\'); start--)
;
/* Go forwards to end of word */
for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
if (buf[end] == '\\' && (end+1) < buflen)
end++;
}
if (is_commandp) {
int iscmd;
/* Figure out if this is a command */
for (p = start - 1; p >= 0 && isspace(buf[p]); p--)
;
iscmd = p < 0 || strchr(";|&()`", buf[p]);
if (iscmd) {
/* If command has a /, path, etc. is not searched;
* only current directory is searched, which is just
* like file globbing.
*/
for (p = start; p < end; p++)
if (ISDIRSEP(buf[p]))
break;
iscmd = p == end;
}
*is_commandp = iscmd;
}
*startp = start;
return end - start;
}
int
x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp, int *endp, char ***wordsp, int *is_commandp)
{
int len;
int nwords;
char **words;
int is_command;
len = x_locate_word(buf, buflen, pos, startp, &is_command);
if (!(flags & XCF_COMMAND))
is_command = 0;
/* Don't do command globing on zero length strings - it takes too
* long and isn't very useful. File globs are more likely to be
* useful, so allow these.
*/
if (len == 0 && is_command)
return 0;
nwords = (is_command ? x_command_glob : x_file_glob)(flags,
buf + *startp, len, &words);
if (nwords == 0) {
*wordsp = NULL;
return 0;
}
if (is_commandp)
*is_commandp = is_command;
*wordsp = words;
*endp = *startp + len;
return nwords;
}
/* Given a string, copy it and possibly add a '*' to the end. The
* new string is returned.
*/
static char *
add_glob(const char *str, int slen)
{
char *toglob;
char *s;
bool saw_slash = false;
if (slen < 0)
return NULL;
toglob = str_nsave(str, slen + 1, ATEMP); /* + 1 for "*" */
toglob[slen] = '\0';
/*
* If the pathname contains a wildcard (an unquoted '*',
* '?', or '[') or parameter expansion ('$'), or a ~username
* with no trailing slash, then it is globbed based on that
* value (i.e., without the appended '*').
*/
for (s = toglob; *s; s++) {
if (*s == '\\' && s[1])
s++;
else if (*s == '*' || *s == '[' || *s == '?' || *s == '$'
|| (s[1] == '(' /*)*/ && strchr("*+?@!", *s)))
break;
else if (ISDIRSEP(*s))
saw_slash = true;
}
if (!*s && (*toglob != '~' || saw_slash)) {
toglob[slen] = '*';
toglob[slen + 1] = '\0';
}
return toglob;
}
/*
* Find longest common prefix
*/
int
x_longest_prefix(int nwords, char *const *words)
{
int i, j;
int prefix_len;
char *p;
if (nwords <= 0)
return 0;
prefix_len = strlen(words[0]);
for (i = 1; i < nwords; i++)
for (j = 0, p = words[i]; j < prefix_len; j++)
if (FILECHCONV(p[j]) != FILECHCONV(words[0][j])) {
prefix_len = j;
break;
}
return prefix_len;
}
void
x_free_words(int nwords, char **words)
{
int i;
for (i = 0; i < nwords; i++)
if (words[i])
afree(words[i], ATEMP);
afree(words, ATEMP);
}
/* Return the offset of the basename of string s (which ends at se - need not
* be null terminated). Trailing slashes are ignored. If s is just a slash,
* then the offset is 0 (actually, length - 1).
* s Return
* /etc 1
* /etc/ 1
* /etc// 1
* /etc/fo 5
* foo 0
* /// 2
* 0
*/
int
x_basename(const char *s, const char *se)
{
const char *p;
if (se == NULL)
se = s + strlen(s);
if (s == se)
return 0;
/* Skip trailing slashes */
for (p = se - 1; p > s && ISDIRSEP(*p); p--)
;
for (; p > s && !ISDIRSEP(*p); p--)
;
if (ISDIRSEP(*p) && p + 1 < se)
p++;
return p - s;
}
/*
* Apply pattern matching to a table: all table entries that match a pattern
* are added to wp.
*/
static void
glob_table(const char *pat, XPtrV *wp, struct table *tp)
{
struct tstate ts;
struct tbl *te;
for (twalk(&ts, tp); (te = tnext(&ts)); ) {
if (gmatch(te->name, pat, false))
XPput(*wp, str_save(te->name, ATEMP));
}
}
static void
glob_path(int flags, const char *pat, XPtrV *wp, const char *path)
{
const char *sp, *p;
char *xp;
int staterr;
int pathlen;
int patlen;
int oldsize, newsize, i, j;
char **words;
XString xs;
patlen = strlen(pat) + 1;
sp = path;
Xinit(xs, xp, patlen + 128, ATEMP);
while (sp) {
xp = Xstring(xs, xp);
if (!(p = strchr(sp, PATHSEP)))
p = sp + strlen(sp);
pathlen = p - sp;
if (pathlen) {
/* Copy sp into xp, stuffing any MAGIC characters
* on the way
*/
const char *s = sp;
XcheckN(xs, xp, pathlen * 2);
while (s < p) {
if (ISMAGIC(*s))
*xp++ = MAGIC;
*xp++ = *s++;
}
*xp++ = DIRSEP;
pathlen++;
}
sp = p;
XcheckN(xs, xp, patlen);
memcpy(xp, pat, patlen);
oldsize = XPsize(*wp);
glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */
newsize = XPsize(*wp);
/* Check that each match is executable... */
words = (char **) XPptrv(*wp);
for (i = j = oldsize; i < newsize; i++) {
staterr = 0;
if ((search_access(words[i], X_OK, &staterr) >= 0)
|| (staterr == EISDIR)) {
words[j] = words[i];
if (!(flags & XCF_FULLPATH))
memmove(words[j], words[j] + pathlen,
strlen(words[j] + pathlen) + 1);
j++;
} else
afree(words[i], ATEMP);
}
wp->cur = (void **) &words[j];
if (!*sp++)
break;
}
Xfree(xs, xp);
}
/*
* if argument string contains any special characters, they will
* be escaped and the result will be put into edit buffer by
* keybinding-specific function
*/
int
x_escape(const char *s, size_t len, int (*putbuf_func) (const char *, size_t))
{
size_t add, wlen;
const char *ifs = str_val(local("IFS", 0));
int rval=0;
for (add = 0, wlen = len; wlen - add > 0; add++) {
if (strchr("\\$(){}[]*&;#|<>\"'`", s[add]) || strchr(ifs, s[add])) {
if (putbuf_func(s, add) != 0) {
rval = -1;
break;
}
putbuf_func("\\", 1);
putbuf_func(&s[add], 1);
add++;
wlen -= add;
s += add;
add = -1; /* after the increment it will go to 0 */
}
}
if (wlen > 0 && rval == 0)
rval = putbuf_func(s, wlen);
return (rval);
}

74
edit.h
View File

@ -1,74 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: edit.h,v 1.3 1999/11/14 22:04:02 d Exp $ */
/* $From: edit.h,v 1.2 1994/05/19 18:32:40 michael Exp michael $ */
#ifndef EDIT_H
#define EDIT_H
/* 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(void);
void x_flush(void);
void x_putc(int c);
void x_puts(const char *s);
bool x_mode(bool onoff);
int promptlen(const char *cp, const char **spp);
int x_do_comment(char *buf, int bsize, int *lenp);
void x_print_expansions(int nwords, char *const *words, int is_command);
int x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp,
int *endp, char ***wordsp, int *is_commandp);
int x_longest_prefix(int nwords, char *const *words);
int x_basename(const char *s, const char *se);
void x_free_words(int nwords, char **words);
int x_escape(const char *, size_t, int (*)(const char *s, size_t len));
/* emacs.c */
int x_emacs(char *buf, size_t len);
void x_init_emacs(void);
void x_emacs_keys(X_chars *ec);
/* vi.c */
int x_vi(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__
#endif /* ndef EDIT_H */

View File

@ -1,45 +0,0 @@
#!/bin/sh
# $MirOS$
# $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(int c);\n", fname;
nfunc++;
}
}' || exit 1
exit 0

2026
emacs.c

File diff suppressed because it is too large Load Diff

1310
eval.c

File diff suppressed because it is too large Load Diff

1497
exec.c

File diff suppressed because it is too large Load Diff

113
expand.h
View File

@ -1,113 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: expand.h,v 1.3 2001/03/26 16:19:45 todd Exp $ */
#ifndef EXPAND_H
#define EXPAND_H
/*
* 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_(XString *xsp, char *xp, size_t 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 { \
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)
#endif /* ndef EXPAND_H */

580
expr.c
View File

@ -1,580 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: expr.c,v 1.16 2004/12/22 18:57:28 otto Exp $ */
/*
* Korn expression evaluation
*/
#include "sh.h"
#include <ctype.h>
__RCSID("$MirOS$");
/* 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(Expr_state *es, enum error_type type,
const char *str) GCC_FUNC_ATTR(noreturn);
static struct tbl *evalexpr(Expr_state *es, enum prec prec);
static void token(Expr_state *es);
static struct tbl *do_ppmm(Expr_state *es, enum token op,
struct tbl *vasn, bool is_prefix);
static void assign_check(Expr_state *es, enum token op,
struct tbl *vasn);
static struct tbl *tempvar(void);
static struct tbl *intvar(Expr_state *es, struct tbl *vp);
/*
* parse and evaluate expression
*/
int
evaluate(const char *expr, long int *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 evaluate expression, storing result in vp.
*/
int
v_evaluate(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 = NULL;
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(NULL);
if (i == LAEXPR) {
if (error_ok == KSH_RETURN_ERROR)
return 0;
errorf(null);
}
unwind(i);
/*NOTREACHED*/
}
token(es);
if (es->tok == END) {
es->tok = LIT;
es->val = tempvar();
}
v = intvar(es, evalexpr(es, MAX_PREC));
if (es->tok != END)
evalerr(es, ET_UNEXPECTED, NULL);
if (vp->flag & INTEGER)
setint_v(vp, v);
else
/* can fail if readonly */
setstr(vp, str_val(v), error_ok);
quitenv(NULL);
return 1;
}
static void
evalerr(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(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, NULL);
/*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(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;
} 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
*/
;
}
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(Expr_state *es, enum token op, struct tbl *vasn, bool 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(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(void)
{
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(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 = NULL;
}
return vq;
}

1113
history.c

File diff suppressed because it is too large Load Diff

476
io.c
View File

@ -1,476 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: io.c,v 1.17 2004/12/18 22:35:41 millert 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
errorf(const char *fmt, ...)
{
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
warningf(int fileline, const char *fmt, ...)
{
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
bi_errorf(const char *fmt, ...)
{
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 = NULL;
unwind(LERROR);
}
}
/* Called when something that shouldn't happen does */
void
internal_errorf(int jump, const char *fmt, ...)
{
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(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
shellf(const char *fmt, ...)
{
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
shprintf(const char *fmt, ...)
{
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
kshdebug_printf_(const char *fmt, ...)
{
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(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(void)
{
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(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(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;
fcntl(nfd, F_SETFD, FD_CLOEXEC);
return nfd;
}
void
restfd(int fd, int ofd)
{
if (fd == 2)
shf_flush(&shf_iob[fd]);
if (ofd < 0) /* original fd closed */
close(fd);
else if (fd != ofd) {
ksh_dup2(ofd, fd, true); /* XXX: what to do if this fails? */
close(ofd);
}
}
void
openpipe(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(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(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;
/* 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;
}
return fd;
} else if (name[0] == 'p' && !name[1])
return coproc_getfd(mode, emsgp);
if (emsgp)
*emsgp = "illegal file descriptor name";
return -1;
}
/* Called once from main */
void
coproc_init(void)
{
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(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(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(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(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(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;
}
}
/*
* temporary files
*/
struct temp *
maketemp(Area *ap, Temp_type type, struct temp **tlist)
{
#ifndef HAVE_MKSTEMP
static unsigned int inc;
#endif
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 = NULL;
tp->type = type;
#ifdef HAVE_MKSTEMP
shf_snprintf(path, len, "%s/shXXXXXXXX", dir);
fd = mkstemp(path);
if (fd >= 0)
tp->shf = shf_fdopen(fd, SHF_WR, NULL);
#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, NULL);
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 /* !def HAVE_MKSTEMP */
tp->pid = procpid;
tp->next = *tlist;
*tlist = tp;
return tp;
}

1816
jobs.c

File diff suppressed because it is too large Load Diff

5367
ksh.1

File diff suppressed because it is too large Load Diff

View File

@ -1,71 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: ksh_stat.h,v 1.3 1996/10/01 02:05:39 downsj Exp $ */
#ifndef KSH_STAT_H
#define KSH_STAT_H
/* 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 */
#if defined(S_ISSOCK) && defined(S_IFIFO)
# if S_ISSOCK (S_IFIFO)
# define STAT_MACROS_BROKEN
# endif
#endif
#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 */
#endif /* ndef KSH_STAT_H */

View File

@ -1,57 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: ksh_wait.h,v 1.3 1997/06/19 13:58:43 kstailey Exp $ */
#ifndef KSH_WAIT_H
#define KSH_WAIT_H
/* 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), NULL)
#else /* !HAVE_WAITPID && HAVE_WAIT3 */
# define ksh_waitpid(p, s, o) waitpid((p), (s), (o))
#endif /* !HAVE_WAITPID && HAVE_WAIT3 */
#endif /* ndef KSH_WAIT_H */

1301
lex.c

File diff suppressed because it is too large Load Diff

131
lex.h
View File

@ -1,131 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: lex.h,v 1.8 2004/11/02 22:09:24 deraadt Exp $ */
/* $From: lex.h,v 1.4 1994/05/31 13:34:34 michael Exp $ */
#ifndef LEX_H
#define LEX_H
/*
* Source input, lexer and parser
*/
#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() */
#define SLETPAREN 2 /* inside (( )), implicit quoting */
#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
#define MDPAREN 277 /* (( )) */
#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];
#define HISTORYSIZE 511 /* size of saved history */
EXTERN char **history; /* saved commands */
EXTERN char **histptr; /* last history item */
EXTERN int histsize; /* history size */
#endif /* ndef LEX_H */

751
main.c
View File

@ -1,751 +0,0 @@
/** $MirOS: src/bin/ksh/main.c,v 1.2 2005/05/21 16:36:53 tg Exp $ */
/* $OpenBSD: main.c,v 1.35 2004/12/22 18:57:28 otto Exp $ */
/*
* startup, main loop, environments and error handling
*/
#define EXTERN /* define EXTERNs in sh.h */
#include "sh.h"
#include "ksh_stat.h"
#include <time.h>
/*
* shell version
*/
__RCSID("$MirOS: src/bin/ksh/main.c,v 1.2 2005/05/21 16:36:53 tg Exp $");
const char ksh_version[] =
"@(#)PD KSH v5.2.14 MirOS R20u in "
#ifdef MIRBSD_NATIVE
"native "
#endif
"KSH mode"
#ifndef MIRBSD_NATIVE
" as mksh"
#endif
;
/*
* global data
*/
extern char **environ;
static void reclaim(void);
static void remove_temps(struct temp *tp);
static int is_restricted(char *name);
/*
* shell initialization
*/
static const char initifs[] = "IFS= \t\n";
static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }";
static const char *const initcoms [] = {
"typeset", "-x", "SHELL", "PATH", "HOME", NULL,
"typeset", "-r", "KSH_VERSION", NULL,
"typeset", "-i", "PGRP=0", "PPID", NULL,
"typeset", "-i", "OPTIND=1", NULL,
"eval", "typeset -i RANDOM SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"", NULL,
"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
"autoload=typeset -fu",
"functions=typeset -f",
"history=fc -l",
"integer=typeset -i",
"nohup=nohup ",
"local=typeset",
"r=fc -e -",
/* Aliases that are builtin commands in at&t */
"login=exec login",
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(int argc, char *argv[])
{
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 */
/* make sure argv[] is sane */
if (!*argv) {
static const char *empty_argv[] = {"mksh", NULL};
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();
argi = parse_args(argv, OF_FIRSTTIME, NULL);
if (argi < 0)
exit(1);
initvar();
initctypes();
inittraps();
coproc_init();
/* 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, NULL, 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 now - 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).
*/
Flag(FBRACEEXPAND) = 1;
/* 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. */
change_flag(FEMACS, OF_SPECIAL, 1);
Flag(FVITABCOMPLETE) = 1;
/* 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 = NULL;
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();
rnd_seed( (*((long *)kshname)) ^ ((long)time(NULL) * kshpid * ppid) );
setint(global("PPID"), (long) ppid);
/* Note: don't setint here, it loops into rnd_put() */
rnd_get();
if (!Flag(FSH))
setstr(global("KSH_VERSION"), 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, NULL);
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);
s->file = argv[argi++];
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),
NULL);
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 = NULL;
}
}
/* 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);
/* Do this after j_init(), as tty_fd is not initialized 'til then */
if (Flag(FTALKING))
x_init();
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)) {
include(KSH_SYSTEM_PROFILE, 0, NULL, 1);
if (!Flag(FPRIVILEGED))
include(substitute("$HOME/.profile", 0), 0,
NULL, 1);
}
if (Flag(FPRIVILEGED))
include("/etc/suid_profile", 0, NULL, 1);
else {
char *env_file;
/* 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, NULL, 1);
}
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",
NULL
};
shcomexec((char **) restr_com);
/* After typeset command... */
Flag(FRESTRICTED) = 1;
}
if (errexit)
Flag(FERREXIT) = 1;
if (Flag(FTALKING)) {
hist_init(s);
alarm_init();
} else
Flag(FTRACKALL) = 1; /* set after ENV */
shell(s, true); /* doesn't return */
return 0;
}
int
include(const char *name, int argc, char **argv, int intr_ok)
{
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 = NULL;
old_argc = 0;
}
newenv(E_INCL);
i = ksh_sigsetjmp(e->jbuf, 0);
if (i) {
quitenv(s ? s->u.shf : NULL);
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);
quitenv(s->u.shf);
if (old_argv) {
e->loc->argv = old_argv;
e->loc->argc = old_argc;
}
return i & 0xff; /* & 0xff to ensure value not -1 */
}
int
command(const char *comm)
{
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(Source *volatile s, volatile int toplevel)
/* input source */
{
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(NULL);
unwind(i); /* keep on going */
/*NOREACHED*/
default:
source = old_source;
quitenv(NULL);
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();
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(NULL);
source = old_source;
return exstat;
}
/* return to closest error handler or shell(), exit if none found */
void
unwind(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(NULL);
}
}
}
void
newenv(int type)
{
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(struct shf *shf)
{
struct env *ep = e;
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);
}
/* 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) &&
getpgrp() == kshpid) {
setsig(&sigtraps[sig], SIG_DFL,
SS_RESTORE_CURR|SS_FORCE);
kill(0, sig);
}
}
#ifdef MEM_DEBUG
chmem_allfree();
#endif /* MEM_DEBUG */
}
if (shf)
shf_close(shf);
reclaim();
exit(exstat);
}
if (shf)
shf_close(shf);
reclaim();
e = e->oenv;
afree(ep, ATEMP);
}
/* Called after a fork to cleanup stuff left over from parents environment */
void
cleanup_parents_env(void)
{
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 = NULL;
}
}
e->oenv = NULL;
}
/* Called just before an execve cleanup stuff temporary files */
void
cleanup_proc_env(void)
{
struct env *ep;
for (ep = e; ep; ep = ep->oenv)
remove_temps(ep->temps);
}
/* remove temp files and free ATEMP Area */
static void
reclaim(void)
{
remove_temps(e->temps);
e->temps = NULL;
afreeall(&e->area);
}
static void
remove_temps(struct temp *tp)
{
for (; tp != NULL; tp = tp->next)
if (tp->pid == procpid)
unlink(tp->name);
}
/* Returns true if name refers to a restricted shell */
static int
is_restricted(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")
&& !strstr(p-2, "mirbsdksh") && !strstr(p-2, "mirosksh");
}
void
aerror(Area *ap GCC_FUNC_ATTR(unused), const char *msg)
{
internal_errorf(1, "alloc: %s", msg);
errorf(null); /* this is never executed - keeps gcc quiet */
/*NOTREACHED*/
}

1329
misc.c

File diff suppressed because it is too large Load Diff

148
missing.c
View File

@ -1,148 +0,0 @@
/** $MirOS: src/bin/ksh/missing.c,v 1.1.7.1 2005/03/06 15:42:54 tg Exp $ */
/* $OpenBSD: missing.c,v 1.5 2003/05/16 18:49:46 jsyn Exp $ */
/*
* Routines which may be missing on some machines
*/
#include "sh.h"
#include "ksh_stat.h"
__RCSID("$MirOS: src/bin/ksh/missing.c,v 1.1.7.1 2005/03/06 15:42:54 tg Exp $");
#ifndef HAVE_STRERROR
char *
strerror(err)
int err;
{
static char buf[64];
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_STRERROR */
#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 NULL;
if (!S_ISDIR(statb.st_mode)) {
errno = ENOTDIR;
return NULL;
}
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_DUP2 */
/*
* Random functions
*/
#ifndef HAVE_SRANDOM
#undef HAVE_RANDOM
#endif
int rnd_state;
void
rnd_seed(long newval)
{
rnd_put(newval);
rnd_state = 0;
}
long
rnd_get(void)
{
#ifdef HAVE_ARC4RANDOM
if (!rnd_state)
return arc4random() & 0x7FFF;
#endif
#ifdef HAVE_RANDOM
return random() & 0x7FFF;
#else
return rand();
#endif
}
void
rnd_put(long newval)
{
long sv;
rnd_state = 1 | rnd_get();
sv = (rnd_get() << (newval & 7)) ^ newval;
#if defined(HAVE_ARC4RANDOM_PUSH)
arc4random_push(sv);
#elif defined(HAVE_ARC4RANDOM_ADDRANDOM)
arc4random_addrandom((unsigned char *)&sv, sizeof(sv));
#endif
#ifdef HAVE_ARC4RANDOM
sv ^= arc4random();
#endif
#ifdef HAVE_RANDOM
srandom(sv);
#else
srand(sv);
#endif
while (rnd_state) {
rnd_get();
rnd_state >>= 1;
}
rnd_state = 1;
}

309
path.c
View File

@ -1,309 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: path.c,v 1.9 2003/10/22 07:40:38 jmc Exp $ */
#include "sh.h"
#include "ksh_stat.h"
__RCSID("$MirOS$");
/*
* 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(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 appended to result.
*/
int
make_path(const char *cwd, const char *file, char **cdpathp, XString *xsp, int *phys_pathp)
/* & of : separated list */
{
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 : NULL;
}
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 = NULL;
return rval;
}
/*
* Simplify pathnames containing "." and ".." entries.
* ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
*/
void
simplify_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 (__CYGWIN__)
if (path[0] && path[1] == ':') /* skip a: */
very_start += 2;
#endif
/* Before After
* /foo/ /foo
* /foo/../../bar /bar
* /foo/./blah/.. /foo
* . .
* .. ..
* ./foo foo
* foo/../../../bar ../../bar
* 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(char *path)
{
int len;
char *p = path;
if (!p && !(p = ksh_get_wd(NULL, 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(const char *path)
{
XString xs;
char *xp;
Xinit(xs, xp, strlen(path) + 1, ATEMP);
xp = do_phys_path(&xs, xp, path);
if (!xp)
return NULL;
if (Xlength(xs, xp) == 0)
Xput(xs, xp, DIRSEP);
Xput(xs, xp, '\0');
return Xclose(xs, xp);
}
static char *
do_phys_path(XString *xsp, char *xp, const char *path)
{
const char *p, *q;
size_t len;
int savepos, llen;
char lbuf[PATH_MAX];
Xcheck(*xsp, xp);
for (p = path; p; p = q) {
while (ISDIRSEP(*p))
p++;
if (!*p)
break;
len = (q = ksh_strchr_dirsep(p)) ? (size_t)(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 NULL;
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 NULL;
}
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 */

297
proto.h
View File

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

660
sh.h
View File

@ -1,660 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: sh.h,v 1.23 2004/12/18 22:11:43 millert Exp $ */
/* $From: sh.h,v 1.2 1994/05/19 18:32:40 michael Exp michael $ */
#ifndef SH_H
#define SH_H
/*
* mirbsdksh - MirOS Project Korn-Shell
* from: Public Domain Bourne/Korn shell
*/
#include "config.h" /* system and option configuration info */
/* 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(const char *);
extern void * malloc(size_t);
extern void * realloc(void *, size_t);
extern int free(void *);
extern int exit(int);
extern int atoi(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(const char *, int);
extern int open(const char *, int, ...);
extern int creat(const char *, mode_t);
extern int read(int, char *, unsigned);
extern int write(int, const char *, unsigned);
extern off_t lseek(int, off_t, int);
extern int close(int);
extern int pipe(int []);
extern int dup2(int, int);
extern int unlink(const char *);
extern int fork(void);
extern int execve(const char *, char * const[], char * const[]);
extern int chdir(const char *);
extern int kill(pid_t, int);
extern char *getcwd(); /* no ARGS here - differs on different machines */
extern int geteuid(void);
extern int readlink(const char *, char *, int);
extern int getegid(void);
extern int getpid(void);
extern int getppid(void);
extern unsigned int sleep(unsigned int);
extern int isatty(int);
# ifdef POSIX_PGRP
extern int getpgrp(void);
extern int setpgid(pid_t, pid_t);
# endif /* POSIX_PGRP */
# ifdef BSD_PGRP
extern int getpgrp(pid_t);
extern int setpgrp(pid_t, pid_t);
# endif /* BSD_PGRP */
# ifdef SYSV_PGRP
extern int getpgrp(void);
extern int setpgrp(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(const char *s, const char *p);
#endif /* HAVE_STRSTR */
#ifndef HAVE_STRCASECMP
int strcasecmp(const char *s1, const char *s2);
int strncasecmp(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(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(void *d, const void *s, size_t n);
# endif
#endif /* HAVE_MEMMOVE */
# include <stdarg.h>
# define SH_VA_START(va, argn) va_start(va, argn)
#include <errno.h>
#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 */
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif /* HAVE_LIMITS_H */
#include <signal.h>
#ifndef NSIG
#define NSIG 32
#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) */
# 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 */
/* this is a hang-over from older versions of the os2 port */
#define ksh_dupbase(fd, base) fcntl(fd, F_DUPFD, base)
#if defined(__sun__) && defined(__svr4__)
typedef void (*sig_t)();
#endif
#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(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.
*/
# define INT32 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
#ifndef EXECSHELL
/* shell to exec scripts (see also $SHELL initialization in main.c) */
# ifdef _PATH_BSHELL
# define EXECSHELL _PATH_BSHELL
# else
# define EXECSHELL "/bin/mksh"
# endif
# define EXECSHELL_STR "EXECSHELL"
#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
*/
# 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)
#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 4096 /* input line size */
#ifndef PATH_MAX
#define PATH_MAX 1024 /* pathname size (todo: pathconf()) */
#endif
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_FIRSTTIME 0x10 /* as early as possible, once */
#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 */
FBRACEEXPAND, /* enable {} globbing */
FBGNICE, /* bgnice */
FCOMMAND, /* -c: (invocation) execute specified command */
FEMACS, /* emacs command editing */
FEMACSUSEMETA, /* use 8th bit as meta */
FERREXIT, /* -e: quit on error */
FGMACS, /* gmacs command editing */
FIGNOREEOF, /* eof does not exit */
FTALKING, /* -i: interactive */
FKEYWORD, /* -k: name=value anywhere */
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 behaviour */
FSTDIN, /* -s: (invocation) parse stdin */
FTRACKALL, /* -h: create tracked aliases for all commands */
FVERBOSE, /* -v: echo input */
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 */
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 */
volatile sig_atomic_t set; /* trap pending */
int flags; /* TF_* */
sig_t cursig; /* current handler (valid if TF_ORIG_* set) */
sig_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_ NSIG /* for trap ERR */
EXTERN volatile sig_atomic_t trap; /* traps pending? */
EXTERN volatile sig_atomic_t intrsig; /* pending trap interrupts executing command */
EXTERN volatile sig_atomic_t 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[NSIG + 1];
#endif /* !FROM_TRAP_C */
/*
* 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);
/* 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 */
/* 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;
/* 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;
/* Minimum 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
/* Minimum 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 */
/* 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__
#ifndef __RCSID
#ifdef __ELF__
#define __RCSID(x) static const char __rcsid[] \
GCC_FUNC_ATTR(section(".comment")) = (x)
#else
#define __RCSID(x) static const char __rcsid[] = (x)
#endif
#endif
#endif /* ndef SH_H */

1226
shf.c

File diff suppressed because it is too large Load Diff

87
shf.h
View File

@ -1,87 +0,0 @@
/** $MirOS$ */
/* $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(const char *name, int oflags, int mode,
int sflags);
struct shf *shf_fdopen(int fd, int sflags, struct shf *shf);
struct shf *shf_reopen(int fd, int sflags, struct shf *shf);
struct shf *shf_sopen(char *buf, int bsize, int sflags,
struct shf *shf);
int shf_close(struct shf *shf);
int shf_fdclose(struct shf *shf);
char *shf_sclose(struct shf *shf);
int shf_finish(struct shf *shf);
int shf_flush(struct shf *shf);
int shf_seek(struct shf *shf, off_t where, int from);
int shf_read(char *buf, int bsize, struct shf *shf);
char *shf_getse(char *buf, int bsize, struct shf *shf);
int shf_getchar(struct shf *shf);
int shf_ungetc(int c, struct shf *shf);
int shf_putchar(int c, struct shf *shf);
int shf_puts(const char *s, struct shf *shf);
int shf_write(const char *buf, int nbytes, struct shf *shf);
int shf_fprintf(struct shf *shf, const char *fmt, ...);
int shf_snprintf(char *buf, int bsize, const char *fmt, ...);
char *shf_smprintf(const char *fmt, ...);
int shf_vfprintf(struct shf *, const char *fmt, va_list args);
#endif /* SHF_H */

910
syn.c
View File

@ -1,910 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: syn.c,v 1.14 2003/10/22 07:40:38 jmc Exp $ */
/*
* shell parser (C version)
*/
#include "sh.h"
#include "c_test.h"
__RCSID("$MirOS$");
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
int start_line; /* line nesting began on */
};
static void yyparse(void);
static struct op *pipeline(int cf);
static struct op *andor(void);
static struct op *c_list(int multi);
static struct ioword *synio(int cf);
static void musthave(int c, int cf);
static struct op *nested(int type, int smark, int emark);
static struct op *get_command(int cf);
static struct op *dogroup(void);
static struct op *thenpart(void);
static struct op *elsepart(void);
static struct op *caselist(void);
static struct op *casepart(int endtok);
static struct op *function_body(char *name, int ksh_func);
static char ** wordlist(void);
static struct op *block(int type, struct op *t1, struct op *t2,
char **wp);
static struct op *newtp(int type);
static void syntaxerr(const char *what)
GCC_FUNC_ATTR(noreturn);
static void nesting_push(struct nesting_state *save, int tok);
static void nesting_pop(struct nesting_state *saved);
static int assign_command(char *s);
static int inalias(struct source *s);
static int dbtestp_isa(Test_env *te, Test_meta meta);
static const char *dbtestp_getopnd(Test_env *te, Test_op op,
int do_eval);
static int dbtestp_eval(Test_env *te, Test_op op, const char *opnd1,
const char *opnd2, int do_eval);
static void dbtestp_error(Test_env *te, int offset, const char *msg);
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(void)
{
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(NULL);
}
static struct op *
pipeline(int cf)
{
struct op *t, *p, *tl = NULL;
t = get_command(cf);
if (t != NULL) {
while (token(0) == '|') {
if ((p = get_command(CONTIN)) == NULL)
syntaxerr(NULL);
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(void)
{
struct op *t, *p;
int c;
t = pipeline(0);
if (t != NULL) {
while ((c = token(0)) == LOGAND || c == LOGOR) {
if ((p = pipeline(CONTIN)) == NULL)
syntaxerr(NULL);
t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
}
REJECT;
}
return (t);
}
static struct op *
c_list(int multi)
{
struct op *t = NULL, *p, *tl = NULL;
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(int cf)
{
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(int c, int cf)
{
if ((token(cf)) != c)
syntaxerr(NULL);
}
static struct op *
nested(int type, int smark, int emark)
{
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(int cf)
{
struct op *t;
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(NULL);
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;
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;
}
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;
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 == NULL)
syntaxerr(NULL);
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(void)
{
int c;
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(NULL);
list = c_list(true);
musthave(c, KEYWORD|ALIAS);
return list;
}
static struct op *
thenpart(void)
{
struct op *t;
musthave(THEN, KEYWORD|ALIAS);
t = newtp(0);
t->left = c_list(true);
if (t->left == NULL)
syntaxerr(NULL);
t->right = elsepart();
return (t);
}
static struct op *
elsepart(void)
{
struct op *t;
switch (token(KEYWORD|ALIAS|VARASN)) {
case ELSE:
if ((t = c_list(true)) == NULL)
syntaxerr(NULL);
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(void)
{
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(NULL);
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(int endtok)
{
struct op *t;
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(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 pdkshs 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)) == NULL) {
/*
* Probably something like foo() followed by eof or ;.
* This is accepted by sh and ksh88.
* To make "typeset -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] = NULL;
t->left->vars = (char **) alloc(sizeof(char *), ATEMP);
t->left->vars[0] = NULL;
t->left->lineno = 1;
}
if (!old_func_parse)
e->flags &= ~EF_FUNC_PARSE;
return t;
}
static char **
wordlist(void)
{
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(NULL);
if (XPsize(args) == 0) {
XPfree(args);
return NULL;
} else {
XPput(args, NULL);
return (char **) XPclose(args);
}
}
/*
* supporting functions
*/
static struct op *
block(int type, struct op *t1, struct op *t2, char **wp)
{
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 },
{ "select", SELECT, true },
{ "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 },
{ "[[", DBRACKET, true },
/* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
{ "&&", LOGAND, false },
{ "||", LOGOR, false },
{ ";;", BREAK, false },
{ "((", MDPAREN, false },
{ "|&", COPROC, false },
/* and some special cases... */
{ "newline", '\n', false },
{ NULL, 0, false }
};
void
initkeywords(void)
{
struct tokeninfo const *tt;
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(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(NULL, 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(struct nesting_state *save, int tok)
{
*save = nesting;
nesting.start_token = tok;
nesting.start_line = source->line;
}
static void
nesting_pop(struct nesting_state *saved)
{
nesting = *saved;
}
static struct op *
newtp(int type)
{
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(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(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(struct source *s)
{
for (; s && s->type == SALIAS; s = s->next)
if (!(s->flags & SF_ALIASEND))
return 1;
return 0;
}
/* 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(Test_env *te, Test_meta meta)
{
int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
int uqword = 0;
char *save = NULL;
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(Test_env *te, Test_op op GCC_FUNC_ATTR(unused),
int do_eval GCC_FUNC_ATTR(unused))
{
int c = tpeek(ARRAYVAR);
if (c != LWORD)
return NULL;
ACCEPT;
XPput(*te->pos.av, yylval.cp);
return null;
}
static int
dbtestp_eval(Test_env *te GCC_FUNC_ATTR(unused),
Test_op op GCC_FUNC_ATTR(unused),
const char *opnd1 GCC_FUNC_ATTR(unused),
const char *opnd2 GCC_FUNC_ATTR(unused),
int do_eval GCC_FUNC_ATTR(unused))
{
return 1;
}
static void
dbtestp_error(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);
}

231
table.c
View File

@ -1,231 +0,0 @@
/** $MirOS$ */
/* $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"
__RCSID("$MirOS$");
#define INIT_TBLS 8 /* initial table size (power of 2) */
static void texpand(struct table *tp, int nsize);
static int tnamecmp(void *p1, void *p2);
unsigned int
hash(const char *n)
{
unsigned int h = 0;
while (*n != '\0')
h = 2*h + *n++;
return h * 32821; /* scatter bits */
}
void
tinit(struct table *tp, Area *ap, int tsize)
{
tp->areap = ap;
tp->tbls = NULL;
tp->size = tp->nfree = 0;
if (tsize)
texpand(tp, tsize);
}
static void
texpand(struct table *tp, int nsize)
{
int i;
struct tbl *tblp, **p;
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(struct table *tp, const char *n, unsigned int h)
/* table */
/* name to enter */
/* hash(n) */
{
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(struct table *tp, const char *n, unsigned int h)
/* table */
/* name to enter */
/* hash(n) */
{
struct tbl **pp, *p;
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 = NULL;
memcpy(p->name, n, len);
/* enter in tp->tbls */
tp->nfree--;
*pp = p;
return p;
}
void
tdelete(struct tbl *p)
{
p->flag = 0;
}
void
twalk(struct tstate *ts, struct table *tp)
{
ts->left = tp->size;
ts->next = tp->tbls;
}
struct tbl *
tnext(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(void *p1, void *p2)
{
return strcmp(((struct tbl *)p1)->name, ((struct tbl *)p2)->name);
}
struct tbl **
tsort(struct table *tp)
{
int i;
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(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))) {
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 */

182
table.h
View File

@ -1,182 +0,0 @@
/** $MirOS$ */
/* $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 $ */
#ifndef TABLE_H
#define TABLE_H
/*
* 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)(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;
char * error; /* error handler */
char * exit; /* exit handler */
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)(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_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
#define V_PGRP 18
/* 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 */
#endif /* ndef TABLE_H */

View File

@ -1,111 +0,0 @@
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
---

View File

@ -1,78 +0,0 @@
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
---

View File

@ -1,340 +0,0 @@
# $MirOS$
# $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
---

View File

@ -1,193 +0,0 @@
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.*/
---

View File

@ -1,162 +0,0 @@
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
---

View File

@ -1,144 +0,0 @@
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
---

View File

@ -1,98 +0,0 @@
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-existent-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
---

View File

@ -1,330 +0,0 @@
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: *
---

View File

@ -1,560 +0,0 @@
# $MirOS$
# $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*$/
---

View File

@ -1,175 +0,0 @@
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>
---
name: IFS-subst-2
description:
manual page test, IFS=<space>:
stdin:
showargs() { for i; do echo -n " <$i>"; done; echo; }
IFS=" :"
x=" A : B::D"
echo -n '1:'; for i in $x ; do echo -n " [$i]" ; done ; echo
showargs 2 $x
expected-stdout:
1: [A] [B] [] [D]
<2> <A> <B> <> <D>
---

View File

@ -1,217 +0,0 @@
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
---

View File

@ -1,110 +0,0 @@
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
---

View File

@ -1,57 +0,0 @@
# $MirOS$
# $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]
---

File diff suppressed because it is too large Load Diff

View File

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

1205
tests/th

File diff suppressed because it is too large Load Diff

View File

@ -1,99 +0,0 @@
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
---

View File

@ -1,162 +0,0 @@
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
---

View File

@ -1,9 +0,0 @@
name: version-1
description:
Check version of shell.
category: pdksh
stdin:
echo $KSH_VERSION
expected-stdout-pattern:
/PD KSH v5\.2\.14 MirOS R20u in (native )?KSH mode( as mksh)?/
---

426
trap.c
View File

@ -1,426 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: trap.c,v 1.19 2004/12/22 17:47:03 millert Exp $ */
/*
* signal handling
*/
/* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */
#define FROM_TRAP_C
#include "sh.h"
__RCSID("$MirOS$");
Trap sigtraps[NSIG + 1];
static struct sigaction Sigact_ign, Sigact_trap;
void
inittraps(void)
{
int i;
/* Populate sigtraps based on sys_signame and sys_siglist. */
for (i = 0; i <= NSIG; i++) {
sigtraps[i].signal = i;
if (i == SIGERR_) {
sigtraps[i].name = "ERR";
sigtraps[i].mess = "Error handler";
} else {
sigtraps[i].name = sys_signame[i];
sigtraps[i].mess = sys_siglist[i];
}
}
sigtraps[SIGEXIT_].name = "EXIT"; /* our name for signal 0 */
sigemptyset(&Sigact_ign.sa_mask);
Sigact_ign.sa_flags = 0; /* interruptible */
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);
}
static RETSIGTYPE alarm_catcher(int sig);
void
alarm_init(void)
{
sigtraps[SIGALRM].flags |= TF_SHELL_USES;
setsig(&sigtraps[SIGALRM], alarm_catcher,
SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
}
static RETSIGTYPE
alarm_catcher(int sig GCC_FUNC_ATTR(unused))
{
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;
}
Trap *
gettrap(const char *name, int igncase)
{
int i;
Trap *p;
if (digit(*name)) {
int n;
if (getn(name, &n) && 0 <= n && n < NSIG)
return &sigtraps[n];
return NULL;
}
for (p = sigtraps, i = NSIG+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(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);
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(void)
{
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(void)
{
int i;
Trap *p;
/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
for (p = sigtraps, i = NSIG + 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(void)
{
int i;
Trap *p;
for (p = sigtraps, i = NSIG + 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(int flag)
{
int i;
Trap *p;
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;
if (!flag)
trap = 0;
if (flag & TF_DFL_INTR)
intrsig = 0;
if (flag & TF_FATAL)
fatal_trap = 0;
for (p = sigtraps, i = NSIG + 1; --i >= 0; p++)
if (p->set && (!flag
|| ((p->flags & flag) && p->trap == NULL)))
runtrap(p);
}
void
runtrap(Trap *p)
{
int i = p->signal;
char *trapstr = p->trap;
int oexstat;
int UNINITIALIZED(old_changed);
p->set = 0;
if (trapstr == NULL) { /* 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 = NULL;
}
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(void)
{
int i;
Trap *p;
trap = 0;
intrsig = 0;
fatal_trap = 0;
for (i = NSIG + 1, p = sigtraps; --i >= 0; p++) {
p->set = 0;
if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
settrap(p, NULL);
}
}
/* restore signals just before an exec(2) */
void
restoresigs(void)
{
int i;
Trap *p;
for (i = NSIG + 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(Trap *p, char *s)
{
sig_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(void)
{
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(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(Trap *p, sig_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 = NULL;
if (flags & SS_SHTRAP) {
p->shtrap = f;
f = trapsig;
}
if (p->cursig != f) {
p->cursig = f;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0 /* interruptible */;
sigact.sa_handler = f;
sigaction(p->signal, &sigact, NULL);
}
return 1;
}
/* control what signal is set to before an exec() */
void
setexecsig(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;
}
}

695
tree.c
View File

@ -1,695 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: tree.c,v 1.10 2002/02/27 19:37:09 dhartmei Exp $ */
/*
* command tree climbing
*/
#include "sh.h"
__RCSID("$MirOS$");
#define INDENT 4
#define tputc(c, shf) shf_putchar(c, shf);
static void ptree(struct op *t, int indent, struct shf *f);
static void pioact(struct shf *f, int indent, struct ioword *iop);
static void tputC(int c, struct shf *shf);
static void tputS(char *wp, struct shf *shf);
static void vfptreef(struct shf *shf, int indent, const char *fmt, va_list va);
static struct ioword **iocopy(struct ioword **iow, Area *ap);
static void iofree(struct ioword **iow, Area *ap);
/*
* print a command tree
*/
static void
ptree(struct op *t, int indent, struct shf *shf)
{
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:
t = t->left;
goto Chain;
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;
}
case TSELECT:
fptreef(shf, indent, "select %s ", t->str);
/* fall through */
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(struct shf *shf, int indent, 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(int c, 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(char *wp, struct shf *shf)
{
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;
case OPAT:
tputc(*wp++, shf);
tputc('(', shf);
break;
case SPAT:
tputc('|', shf);
break;
case CPAT:
tputc(')', shf);
break;
}
}
/*
* this is the _only_ way to reliably handle
* variable args with an ANSI compiler
*/
/* VARARGS */
int
fptreef(struct shf *shf, int indent, const char *fmt, ...)
{
va_list va;
SH_VA_START(va, fmt);
vfptreef(shf, indent, fmt, va);
va_end(va);
return 0;
}
/* VARARGS */
char *
snptreef(char *s, int n, const char *fmt, ...)
{
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(struct shf *shf, int indent, const char *fmt, va_list va)
{
int c;
while ((c = *fmt++))
if (c == '%') {
long n;
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') ? (long)(va_arg(va, int))
: (long)(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(struct op *t, Area *ap)
{
struct op *r;
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(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(const char *wp, int c)
{
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;
case OPAT:
nest++;
wp++;
break;
case SPAT:
case CPAT:
if (c == wp[-1] && nest == 0)
return (char *) wp;
if (wp[-1] == CPAT)
nest--;
break;
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(const char *wp)
{
struct shf shf;
int c;
shf_sopen(NULL, 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;
case OPAT:
shf_putchar(*wp++, &shf);
shf_putchar('(', &shf);
break;
case SPAT:
shf_putchar('|', &shf);
break;
case CPAT:
shf_putchar(')', &shf);
break;
}
}
static struct ioword **
iocopy(struct ioword **iow, Area *ap)
{
struct ioword **ior;
int i;
for (ior = iow; *ior++ != NULL; )
;
ior = (struct ioword **) alloc((ior - iow + 1) * sizeof(*ior), ap);
for (i = 0; iow[i] != NULL; i++) {
struct ioword *p, *q;
p = iow[i];
q = (struct ioword *) alloc(sizeof(*p), ap);
ior[i] = q;
*q = *p;
if (p->name != NULL)
q->name = wdcopy(p->name, ap);
if (p->delim != NULL)
q->delim = wdcopy(p->delim, ap);
if (p->heredoc != NULL)
q->heredoc = str_save(p->heredoc, ap);
}
ior[i] = NULL;
return ior;
}
/*
* free tree (for function definition)
*/
void
tfree(struct op *t, Area *ap)
{
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(struct ioword **iow, Area *ap)
{
struct ioword **iop;
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);
}
}

146
tree.h
View File

@ -1,146 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: tree.h,v 1.8 2003/10/22 07:40:38 jmc Exp $ */
/* $From: tree.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */
#ifndef TREE_H
#define TREE_H
/*
* command trees for compile/execute
*/
#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) /* timing TCOM command */
/*
* 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 */
#endif /* ndef TREE_H */

63
tty.c
View File

@ -1,63 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: tty.c,v 1.5 2004/12/18 22:35:41 millert Exp $ */
#include "sh.h"
#include "ksh_stat.h"
#define EXTERN
#include "tty.h"
#undef EXTERN
__RCSID("$MirOS$");
/* Initialize tty_fd. Used for saving/reseting tty modes upon
* foreground job completion and for setting up tty process group.
*/
void
tty_init(int init_ttystate)
{
int do_close = 1;
int tfd;
if (tty_fd >= 0) {
close(tty_fd);
tty_fd = -1;
}
tty_devtty = 1;
if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) {
tty_devtty = 0;
warningf(false,
"No controlling tty (open /dev/tty: %s)",
strerror(errno));
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 (fcntl(tty_fd, F_SETFD, FD_CLOEXEC) < 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)
tcgetattr(tty_fd, &tty_state);
if (do_close)
close(tfd);
}
void
tty_close(void)
{
if (tty_fd >= 0) {
close(tty_fd);
tty_fd = -1;
}
}

44
tty.h
View File

@ -1,44 +0,0 @@
/** $MirOS$ */
/* $OpenBSD: tty.h,v 1.4 2004/12/18 22:12:23 millert Exp $ */
#ifndef TTY_H
#define TTY_H
/* 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 */
#include <termios.h>
typedef struct termios TTY_state;
EXTERN int tty_fd I__(-1); /* dup'd tty file descriptor */
EXTERN int tty_devtty; /* true if tty_fd is from /dev/tty */
EXTERN struct termios tty_state; /* saved tty state */
extern void tty_init(int init_ttystate);
extern void tty_close(void);
/* be sure not to interfere with anyone else's idea about EXTERN */
#ifdef EXTERN_DEFINED
# undef EXTERN_DEFINED
# undef EXTERN
#endif
#undef I__
#endif /* ndef TTY_H */

1163
var.c

File diff suppressed because it is too large Load Diff

2114
vi.c

File diff suppressed because it is too large Load Diff