* 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:
parent
b65ac4d1e3
commit
d8d708aa45
133
CONTRIBUTORS
133
CONTRIBUTORS
@ -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.
|
|
27
Makefile
27
Makefile
@ -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
305
NOTES
@ -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
78
README
@ -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
122
alloc.c
@ -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);
|
|
||||||
}
|
|
861
c_sh.c
861
c_sh.c
@ -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
626
c_test.c
@ -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);
|
|
||||||
}
|
|
61
c_test.h
61
c_test.h
@ -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 */
|
|
284
c_ulimit.c
284
c_ulimit.c
@ -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;
|
|
||||||
}
|
|
82
conf-end.h
82
conf-end.h
@ -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
322
config.h
@ -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
955
edit.c
@ -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
74
edit.h
@ -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 */
|
|
45
emacs-gen.sh
45
emacs-gen.sh
@ -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
|
|
113
expand.h
113
expand.h
@ -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
580
expr.c
@ -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;
|
|
||||||
}
|
|
476
io.c
476
io.c
@ -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;
|
|
||||||
}
|
|
71
ksh_stat.h
71
ksh_stat.h
@ -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 */
|
|
57
ksh_wait.h
57
ksh_wait.h
@ -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 */
|
|
131
lex.h
131
lex.h
@ -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
751
main.c
@ -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*/
|
|
||||||
}
|
|
148
missing.c
148
missing.c
@ -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
309
path.c
@ -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
297
proto.h
@ -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
660
sh.h
@ -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 */
|
|
87
shf.h
87
shf.h
@ -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
910
syn.c
@ -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
231
table.c
@ -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
182
table.h
@ -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 */
|
|
111
tests/alias.t
111
tests/alias.t
@ -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
|
|
||||||
---
|
|
@ -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
|
|
||||||
---
|
|
340
tests/bksl-nl.t
340
tests/bksl-nl.t
@ -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
|
|
||||||
---
|
|
193
tests/brkcont.t
193
tests/brkcont.t
@ -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.*/
|
|
||||||
---
|
|
162
tests/cdhist.t
162
tests/cdhist.t
@ -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
|
|
||||||
---
|
|
144
tests/eglob.t
144
tests/eglob.t
@ -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
|
|
||||||
---
|
|
98
tests/glob.t
98
tests/glob.t
@ -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
|
|
||||||
---
|
|
330
tests/heredoc.t
330
tests/heredoc.t
@ -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: *
|
|
||||||
---
|
|
560
tests/history.t
560
tests/history.t
@ -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*$/
|
|
||||||
---
|
|
175
tests/ifs.t
175
tests/ifs.t
@ -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>
|
|
||||||
---
|
|
217
tests/integer.t
217
tests/integer.t
@ -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
|
|
||||||
---
|
|
110
tests/lineno.t
110
tests/lineno.t
@ -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
|
|
||||||
---
|
|
57
tests/read.t
57
tests/read.t
@ -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]
|
|
||||||
---
|
|
1091
tests/regress.t
1091
tests/regress.t
File diff suppressed because it is too large
Load Diff
@ -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/
|
|
||||||
---
|
|
@ -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
|
|
||||||
---
|
|
162
tests/unclass2.t
162
tests/unclass2.t
@ -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
|
|
||||||
---
|
|
@ -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
426
trap.c
@ -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
695
tree.c
@ -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
146
tree.h
@ -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
63
tty.c
@ -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
44
tty.h
@ -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 */
|
|
Loading…
x
Reference in New Issue
Block a user