Category : UNIX Files
Archive   : PDKSH512.ZIP
Filename : PDKSH512.TAR

 
Output of file : PDKSH512.TAR contained in archive : PDKSH512.ZIP
pdksh-5.1.2/ 42755 0 133 0 5670633230 11360 5ustar rootlsourcepdksh-5.1.2/etc/ 42755 0 133 0 5670633215 12136 5ustar rootlsourcepdksh-5.1.2/etc/ksh.kshrc100644 0 133 10452 5606774334 14103 0ustar rootlsource:
# NAME:
# ksh.kshrc - global initialization for ksh
#
# DESCRIPTION:
# Each invocation of /bin/ksh processes the file pointed
# to by $ENV (usually $HOME/.kshrc).
# This file is intended as a global .kshrc file for the
# Korn shell. A user's $HOME/.kshrc file simply requires
# the line:
# . /etc/ksh.kshrc
# at or near the start to pick up the defaults in this
# file which can then be overridden as desired.
#
# SEE ALSO:
# $HOME/.kshrc
#

# RCSid:
# $Id: ksh.kshrc,v 1.4 1992/12/05 13:14:48 sjg Exp $
#
# @(#)Copyright (c) 1991 Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
# Permission to copy, redistribute or otherwise
# use this file is hereby granted provided that
# the above copyright notice and this notice are
# left intact.

case "$-" in
*i*) # we are interactive
# we may have su'ed so reset these
# NOTE: SCO-UNIX doesn't have whoami,
# install whoami.sh
USER=`whoami 2>/dev/null`
USER=${USER:-`id | sed 's/^[^(]*(\([^)]*\)).*/\1/'`}
case $UID in
0) PS1S='# ';;
esac
PS1S=${PS1S:-'$ '}
HOSTNAME=${HOSTNAME:-`uname -n`}
HOST=${HOSTNAME%%.*}

PROMPT="$USER:!$PS1S"
#PROMPT="<$USER@$HOST:!>$PS1S"
PPROMPT='$USER:$PWD:!'"$PS1S"
#PPROMPT='<$USER@$HOST:$PWD:!>'"$PS1S"
PS1=$PPROMPT
# $TTY is the tty we logged in on,
# $tty is that which we are in now (might by pty)
tty=`tty`
tty=`basename $tty`
TTY=${TTY:-$tty}

set -o emacs

alias ls='ls -CF'
alias h='fc -l | more'
_cd() { "cd" $*; }
# the PD ksh is not 100% compatible
case "$KSH_VERSION" in
*PD*) # PD ksh
case "$TERM" in
pc3|xterm*)
# bind arrow keys
bind '^[['=prefix-2
bind '^XA'=up-history
bind '^XB'=down-history
bind '^XC'=forward-char
bind '^XD'=backward-char
;;
esac
;;
*) # real ksh ?
[ -r $HOME/.functions ] && . $HOME/.functions
set -o trackall
;;
esac
case "$TERM" in
sun*)
# these are not as neat as their csh equivalents
if [ "$tty" != console ]; then
# ilabel
ILS='\033]L'; ILE='\033\\'
# window title bar
WLS='\033]l'; WLE='\033\\'
fi
;;
xterm*)
ILS='\033]1;'; ILE='\007'
WLS='\033]2;'; WLE='\007'
parent="`ps -ax 2>/dev/null | grep $PPID | grep -v grep`"
case "$parent" in
*telnet*)
export TERM=xterms;;
esac
;;
*) ;;
esac
# do we want window decorations?
if [ "$ILS" ]; then
ilabel () { print -n "${ILS}$*${ILE}"; }
label () { print -n "${WLS}$*${WLE}"; }

alias stripe='label "$USER@$HOST ($tty) - $PWD"'
alias istripe='ilabel "$USER@$HOST ($tty)"'

wftp () { ilabel "ftp $*"; "ftp" $*; eval istripe; }
wcd () { _cd $*; eval stripe; }
wtelnet ()
{
"telnet" "$@"
eval istripe
eval stripe
}
wrlogin ()
{
"rlogin" "$@"
eval istripe
eval stripe
}
wsu ()
{
"su" "$@"
eval istripe
eval stripe
}
alias su=wsu
alias cd=wcd
alias ftp=wftp
alias telnet=wtelnet
alias rlogin=wrlogin
eval stripe
eval istripe
PS1=$PROMPT
fi
alias quit=exit
alias cls=clear
alias logout=exit
alias bye=exit
alias p='ps -l'
alias j=jobs
alias o='fg %-'
alias ls='ls -gCF'

# add your favourite aliases here
OS=${OS:-`uname -s`}
case $OS in
HP-UX)
alias ls='ls -CF'
;;
*BSD)
alias df='df -k'
alias du='du -k'
;;
esac
alias rsize='eval `/usr/bin/X11/resize`'

case "$TERM" in
sun*|xterm*)
case $tty in
tty[p-w]*)
case "$DISPLAY" in
"")
DISPLAY="`who | grep $TTY | sed -n 's/.*(\([^:)]*\)[:)].*/\1/p' | sed 's/\([a-zA-Z][^.]*\).*/\1/'`:0"
;;
esac
;;
esac
case "$DISPLAY" in
ozen*|:*)
stty erase "^?"
;;
*)
stty erase "^h"
;;
esac
export DISPLAY
;;
esac

;;
*) # non-interactive
;;
esac
# commands for both interactive and non-interactive shells

# is $1 missing from $2 (or PATH) ?
no_path () {
eval _v="\$${2:-PATH}"
case :$_v: in
*:$1:*) return 1;; # no we have it
esac
return 0
}
# if $1 exists and is not in path, append it
add_path () {
[ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
[ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}
pdksh-5.1.2/etc/profile100644 0 133 15550 5606774333 13650 0ustar rootlsource:
# NAME:
# profile - global initialization for sh,ksh
#
# DESCRIPTION:
# This file is processed during login by /bin/sh
# and /bin/ksh. It is used to setup the default user
# environment.
#
# SEE ALSO:
# $HOME/.profile
# /etc/ksh.kshrc

# RCSid:
# $Id: profile,v 1.4 1992/08/10 12:00:11 sjg Exp $
#
# @(#)Copyright (c) 1991 Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
# Permission to copy, redistribute or otherwise
# use this file is hereby granted provided that
# the above copyright notice and this notice are
# left intact.

sigs="2 3"
trap "" $sigs # don't interrupt us

# simple versions. See ksh.kshrc for the clever ones
add_path () { [ -d $1 ] && eval ${2:-PATH}="\$${2:-PATH}:$1"; }
pre_path () { [ -d $1 ] && eval ${2:-PATH}="$1:\$${2:-PATH}"; }
del_path () { eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`; }

case "$_INIT_" in
*env*) ;;
*) # do these once
_INIT_="$_INIT_"env
export _INIT_
case `echo -n ""` in
-n*)
N=""; C="\c";;
*)
N="-n"; C="";;
esac

if [ -f /unix ]; then
# System V
[ -z "$TZ" -a -f /etc/TIMEZONE ] && . /etc/TIMEZONE

set -- `who -r`
case "$3" in
S|5|0) SINGLE=y;;
*) SINGLE=n;;
esac
else
SINGLE=n # doesn't matter so much
fi

OS=${OS:-`uname -s`}
ARCH=${ARCH:-`uname -m`}
HOSTNAME=`hostname 2>/dev/null`
HOSTNAME=${HOSTNAME:-`uname -n`}
export OS ARCH HOSTNAME

# pick one of the following for the default umask
umask 002 # relaxed -rwxrwxr-x
# umask 022 # cautious -rwxr-xr-x
# umask 027 # uptight -rwxr-x---
# umask 077 # paranoid -rwx------
# you can override the default umask
# for specific groups later...

if [ -d /local ]; then
LOCAL=/local
else
LOCAL=/usr/local
fi

# defaults (might be reset below)
PATH=/bin:/usr/bin
MANPATH=/usr/man
SPOOL=/usr/spool
defterm=vt220

# set system specific things,
# eg. set PATH,MANPATH
# override default ulimit if desired.
# defult ulmit is unlimited on SunOS
# and 4Mb for most System V
case $OS in
SunOS)
# On sun's /bin -> /usr/bin so leave it out!
PATH=/usr/bin:/usr/ucb:/usr/5bin:/usr/etc
SPOOL=/var/spool
LD_LIBRARY_PATH=/usr/lib
add_path /usr/snm/lib LD_LIBRARY_PATH
add_path /usr/X11R5/lib LD_LIBRARY_PATH
add_path /usr/openwin/lib LD_LIBRARY_PATH
export LD_LIBRARY_PATH
;;
SCO-UNIX)
defterm=ansi
;;
B.O.S.)
MANPATH=/usr/catman
SRC_COMPAT=_SYSV
export SRC_COMPAT
;;
NetBSD|386bsd)
MACHINE_ARCH=`uname -m`
MANPATH=/usr/share/man
add_path /usr/X386/man MANPATH
MAILDIR=/var/mail
SPOOL=/var/spool
export MACHINE_ARCH
;;
esac
# add_path only adds them if they exist
add_path /sbin
add_path /usr/sbin
add_path /usr/distbin
add_path /usr/ucb
add_path /usr/lbin
add_path /usr/dbin
add_path /usr/ldbin
add_path ${LOCAL}/bin
add_path /usr/bin/X11
add_path /usr/X11R5/bin
add_path /usr/openwin/bin
# ensure . is at end
PATH=$PATH:.

case "$HOME" in
/) ;;
""|/tmp)
echo "Using /tmp for HOME"
HOME=/tmp; export HOME
;;
*)
pre_path $HOME/bin
;;
esac
add_path /usr/X11R5/man MANPATH
add_path ${LOCAL}/man MANPATH

# make sure these are set at least once
LOGNAME=${LOGNAME:-`logname`}
USER=${USER:-$LOGNAME}

# NOTE: set up $GROUPDIR such that users cannot modify/install
# their own $GROUPDIR/profile
GROUPDIR=`dirname $HOME`
[ "$GROUPDIR" != /etc -a -f $GROUPDIR/profile ] && . $GROUPDIR/profile

export LOCAL TTY PATH LOGNAME USER

if [ -t 1 ]; then
# we are interactive
TTY=`tty`
TTY=`basename $TTY`
if [ -f /etc/organization ]; then
ORGANIZATION="`cat /etc/organization`"
COPYRIGHT="Copyright (c) `date +19%y` $ORGANIZATION"
export ORGANIZATION COPYRIGHT
fi
# set up some env variables
MAIL=${MAILDIR:-$SPOOL/mail}/$USER
MAILPATH=$MAIL:/etc/motd
EMACSDIR=${LOCAL}/lib/emacs
PAGER=${PAGER:-more}
export MAIL EMACSDIR MANPATH MAILPATH PAGER

CVSROOT=${LOCAL}/src/master
EDITOR=vi
VISUAL=vi
FCEDIT=$EDITOR
export CVSROOT FCEDIT EDITOR VISUAL
case $UID in
0) PS1S='# ';;
esac
PS1S=${PS1S:-'$ '}
PROMPT="<$LOGNAME@$HOSTNAME>$PS1S"
[ -f /etc/profile.TeX ] && . /etc/profile.TeX
else
TTY=none
fi

# test (and setup if we are Korn shell)
if [ "$RANDOM" != "$RANDOM" ]; then
# we are Korn shell
SHELL=/bin/ksh
ENV=${HOME%/}/.kshrc
if [ ! -f $ENV ]; then
ENV=/etc/ksh.kshrc
fi
HISTFILE=${HOME%/}/.ksh_hist
PROMPT="<$LOGNAME@$HOSTNAME:!>$PS1S"
export HISTSIZE HISTFILE ENV
CDPATH=.:$HOME
if [ "$TMOUT" ]; then
typeset -r TMOUT
fi
set -o emacs # biased ๐Ÿ™‚
else
SHELL=/bin/sh
fi
PS1=$PROMPT
export SHELL PS1 EDITOR PATH PROMPT HOSTNAME CDPATH
;;
esac

# login time initialization
case "$_INIT_" in
*log*) ;;
*) _INIT_="$_INIT_"log
case "$SINGLE" in
y) ;;
*)
if [ TTY != none -a "$0" != "-su" -a "$LOGNAME" = "`logname`" ]
then
case $TTY in
tty0*)
echo "`date '+%b %d %H:%M:%S'` $LOGNAME logged in on $TTY" > /dev/console;;
esac
stty sane # usually a good idea ๐Ÿ™‚
if [ ! -f ~/.hushlogin ]; then
# ensure known state
case $OS in
SunOS|*BSD) ;;
*)
stty isig icanon intr '^c' erase '^h' kill '^u' eof '^d'
mesg y
;;
esac

case $TERM in
network|unknown|dialup|"")
echo ${N} "Enter terminal type [$defterm]: ${C}" 1>&2
read tmpterm
TERM=${tmpterm:-$defterm}
;;
esac
case "$TERM" in
pc3|xterm)
stty erase ^?
;;
esac
# not all of the following are appropriate at all sites
# Sun's don't need to cat /etc/motd for instance
case "$OS" in
SunOS) ;;
SCO-UNIX)
[ -s /etc/motd ] && cat /etc/motd
[ -x /usr/bin/mail -a -s "$MAIL" ] &&
echo "You have mail."
[ -x /usr/bin/news ] && /usr/bin/news -n
;;
NetBSD|386bsd)
# hardware flow control works so use it
case $TTY in
tty0*) # dialups
stty -ixon -ixany
stty crtscts
;;
esac
;;
*)
[ -s /etc/motd ] && cat /etc/motd
if [ -x /usr/bin/mailx ]; then
if mailx -e; then
echo "You have mail."
# show the the headers, this might
# be better done in .profile so they
# can override it.
# mailx -H
fi
fi
[ -x /usr/bin/news ] && /usr/bin/news -n
;;
esac
if [ -f $LOCAL/etc/1stlogin.ann ]; then
[ -f $HOME/... ] || sh $LOCAL/etc/1stlogin.ann
fi
# [ -x /usr/games/fortune ] && /usr/games/fortune -a
# remind folk who turned on reply.pl to turn it off.
if [ -f $HOME/.forward ]; then
echo "Your mail is being forwarded to:"
cat $HOME/.forward
if [ -f $HOME/.recording ]; then
echo "Perhaps you should run \"reply.pl off\""
fi
fi
[ -x /usr/ucb/msgs ] && /usr/ucb/msgs -fq
fi
fi
unset tmpterm defterm C N
esac
case "$TERM" in
network|unknown|"") TERM=$defterm;;
esac
export TERM TTY
;;
esac
# Handle X-terminals if necessary
[ "$SINGLE" = n -a -f /etc/profile.X11 ] && . /etc/profile.X11

# make sure you have this bit last
trap $sigs # restore signals
unset sigs
pdksh-5.1.2/etc/sys_config.sh100755 0 133 2757 5571430510 14737 0ustar rootlsource:
# NAME:
# sys_config.sh - set system specific variables
#
# SYNOPSIS:
# . /etc/sys_config.sh
#
# DESCRIPTION:
# Source this script into shell scripts that want to handle
# various system types.
# You may well want to edit this on a particular system replacing
# `uname -s` etc with the result. So that the facility will work
# even when in single user mode and uname et al are not available.
#
# SEE ALSO:
# /etc/profile

# RCSid:
# $Id: sys_config.sh,v 1.5 93/09/29 08:59:36 sjg Exp $
#
# @(#)Copyright (c) 1991 Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
# Permission to copy, redistribute or otherwise
# use this file is hereby granted provided that
# the above copyright notice and this notice are
# left intact.
#

# determin machine type
if [ -f /386bsd ]; then # doesn't have uname or arch
ARCH=i386
OS=386bsd
HOSTNAME=`hostname`
elif [ -f /usr/bin/arch ]; then
ARCH=`arch`
elif [ -f /usr/bin/uname -o -f /bin/uname ]; then
ARCH=`uname -m`
fi
#
case "$ARCH" in
sun386) uname=/usr/5bin/uname
OS=SunOS
;;
*) uname=uname;;
esac

# set the operating system type
# you can't use `uname -s` with SCO UNIX
# it returns the same string as `uname -n`
# so set it manually
# OS=SCO-UNIX
# The eval below is a workaround for a bug in the PD ksh.
OS=${OS:-`eval $uname -s`}
HOSTNAME=${HOSTNAME:-`eval $uname -n`}

case `echo -n ""` in
-n*) _C_=""; _N_="-n";;
*) _C_="\c"; _N_="";;
esac
N="${_N_}"
C="${_C_}"
export OS ARCH HOSTNAME uname
pdksh-5.1.2/misc/ 42755 0 133 0 5670633216 12317 5ustar rootlsourcepdksh-5.1.2/misc/COPYING100644 0 133 43076 5613512714 13473 0ustar rootlsource GNU GENERAL PUBLIC LICENSE
Version 2, June 1991

Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

Preamble

The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.

When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.

We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

The precise terms and conditions for copying, distribution and
modification follow.

GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.

b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.

c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,

b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,

c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.

9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

NO WARRANTY

11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

END OF TERMS AND CONDITIONS

Appendix: How to Apply These Terms to Your New Programs

If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.


Copyright (C) 19yy

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:

Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.

, 1 April 1989
Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
pdksh-5.1.2/misc/ChangeLog.sjg100755 0 133 3320 5571430331 14740 0ustar rootlsourceFri Dec 3 21:39:47 1993 Piercarlo Grandi ([email protected])

* polished 4.9 for linux; corrected a terrible, linux
specific performance bug that caused all streams to
become unbuffered; made emacs.c more understandable.
With 4.9 pdksh is twice as fast and one third the size
of bash 1.13, and has most of the relevant functionality.
To me it is a drop-in replacement.

Sat May 8 15:55:24 1993 Simon J. Gerraty ([email protected])

* Ported to 386bsd - sh directory only, stc/* not needed.

Sat Aug 1 17:11:24 1992 Simon J. Gerraty (sjg@zen)

* Incorporated massive contribution from Peter Collinson
Refer to Changes.pc

* Incorporated Emacs-style completion provided by
[email protected] this a bit nicer than the standard ksh
file completion.

Sun May 3 17:50:03 1992 Simon J. Gerraty (sjg@zen)

* Updated MACHINES.
* Placed source under CVS. This should help with processing fixes
from the field.

Sat Apr 25 10:53:20 1992 Simon J. Gerraty (sjg@zen)

* Getting ready for 4.3 release.

Fri Nov 22 22:24:29 1991 Simon J. Gerraty (sjg at zen)

* Cleaned up the build process slightly. Makefiles in ./std tree
now maintain objects within the libraries rather than simply
building the .o's and archiving them. Of course the make(1) used
must know how to maintain libraries ๐Ÿ™‚

* Added bug.report as a template for bug reporting.

* Source in ./sh can be built independently of ./std tree if
desired. See comments in ./sh/Makefile.

* As originally distributed some of libstdc.a was not used and
libposix.a was not used at all. On Sun's this highlighted a bug
(incompatibility) in the times() call. Now the ./std/libs are
used fully, and the supplied times() call functions as expected.

pdksh-5.1.2/misc/Changes.jrm100755 0 133 4477 5571430421 14504 0ustar rootlsourceChanges to the PD ksh since last time:

- clean up the option configuration stuff. Options in config.h should
now just be #define'd or not, not #define'd to 1 if you want them
and 0 if you don't

- ksh now uses the shell specified by the variable EXECSHELL to run
shell scripts. If EXECSHELL is unset or null it defaults to
/bin/sh. It does a path lookup on the value, so if you set it to
``ksh'' the ksh will run all scripts that don't start with #!. It
seems to run most sh scripts fine (now). I'd be very interested to
hear your experiences if you try this out, as for my next trick I'd
like to make ksh just fork itself to run scripts, which would speed
things up, and allow exportable functions and aliases (like he real
ksh). Just to assure you that it can do some hairy sh scripts, both
CC and coco work with ksh.

EXECSHELL won't work if the system's exec(2) call runs scripts...

- the ``let'' builtin now evaluates null or unset vars to 0, as per
The Book

- Various memory allocation/use problems were cleaned up. Hopefully
you'll never see the ``freeing free object'' error again (if you've
ever seen it).

- the ``test'' builtin is now much more tolerant in parsing its
arguments. This was to make it like the /bin/sh test.

- Temp files (for here documents or ``fc'') are now mode 0600

- Some format strings in the tree printing routines got ``expanded''
into SCCS keywords, so the results of``type '' were
gave you interesting things like the time I last checked in the
file. This has been fixed.

- ``unset -f'' now really does unset functions.

- the ``trailing blank or tab in alias definition'' feature now works.

- A bug in command completion was fixed. Note command completion only
works on commands that have been hashed, so you want to ``set -h''
in your .kshrc, and you may wish to force hashing of some common
commands with the ``hash'' builtin.

- ``echo $$ | cat'' now works as in /bin/sh

Not new features, but newly discovered bugs:

- Local functions don't work correctly. This shouldn't be much
problem, since sh doesn't have them.

- Here documents in functions don't work. This is a problem with the
temp file allocation that requires more work to fix than I've gotten
around to doing. So avoid things like:

foo() {
cat <<- HereDoc
This is a test
HereDoc
}
pdksh-5.1.2/misc/Changes.mlj100755 0 133 3361 5571430421 14465 0ustar rootlsourceI got the pd-ksh from John MacMillan after he indicated that he
had a version of it that had vi editing (I'd seen various versions
with emacs-editing, but none with vi).

It had a few bugs and areas which were not quite complete. I fixed
(or at least tried) to fix several; there are still some things
which I plan on doing (or at least looking into).

Bugs fixed (or at least abated):

vi-mode changes:
- Changed memcpy() to memmove(), which fixed the trashing of
the end of the edit buffer while inserting in the middle
of a line or with use of '#'
- using 'r' replacing the current character with ^@
- typing ctrl-c resulting in next command being garbled
- lack of support for '-' and '+' (pretty trivial)
- finish adding support for '*' (not entirely sure I'm freeing
malloc'ed memory correctly here, but I haven't had any problems)
- treats '_' as end of a word

general changes:
- reporting "not found" when a file actually doesn't have
the appropriate execute bit set (now says "cannot execute"
or "not found", as appropriate)


Still to do:

vi changes:
- fix ctrl-r (I've come up with a hack, but it involves
redrawing the screen a lot when it isn't necessary; I
really wouldn't consider this a fix)
- add support for 'v'

general changes:
- seems to be a memory leak when executing shells in the
current shell; repeatedly executing ". /etc/profile"
increased the size of the program as reported in the
"SZ" field of "ps -l"
- don't give a file its complete pathname in argv[0]; only
its filename (religious issue?)
- history recall should start at the previous command, not
the current one (typing "r r" causes an infinite loop)
pdksh-5.1.2/misc/Changes.pc100755 0 133 2110 5571430422 14275 0ustar rootlsourceChanges by Peter Collinson - Hillside Systems/BSDI - July 1992


a) Add select command - this cannot be ksh without that.
(It NEEDS typedefs too)
b) Remove all the bcopys from vi.c
add
#define memmove in sh.h for BSD systems
c) Add * command to vi mode - expands to a list of files
using the menu printing routine
d) Add my version of history, that works much like the `proper' ksh
storing data in a file that is shared between different invocations
of the shell.
e) Add the ability to redirect to am expansion... ie

ls > o*
if o* is unique then it puts it into the file that matches
otherwise it puts it to a file called o*... this is current
behaviour.
f) Add alternations, from Csh.d) This is not part of ksh but is something
that csh users really miss from the Bourne shell derivatives. The idea
is that lists inside curly braces expand to arguments. ie.
exampl{a,b,c,d,e}
will expand to 5 arguments
exampla examplb examplc exampld example
Recursive lists are permitted.
g) Add suspend as a built-in alias.
h) Port to BSD/386 - add _POSIX_TERM and _BSDI as defines.
pdksh-5.1.2/misc/README.sjg100755 0 133 11515 5571430370 14076 0ustar rootlsource Public Domain Korn Shell
Version 4.9

PD KSH:

This is the latest version of the PD ksh (pdksh). It is not intended
to be the ultimate shell but rather a usable ksh work alike. For
those of us who have to work on multiple systems it is nice to have
the same user interface on all. I resisted moving to the ksh on a
System V machine where I worked, for nearly a year due to the lack of
a ksh on my Sun systems. When I first picked up the 3.2 PD KSH a
couple of years ago, it took only a few minutes to convert a C-shell
fan to a ksh fan ๐Ÿ™‚ Pdksh is not 100% compatible with the ksh.
Having said that, I use it daily beside a real ksh88 and find them
virtually indistinguishable. With one exception - arrays! If some
one feels like adding arrays, I for one would appreciate it ๐Ÿ™‚

I only run this shell on sun's and BSD systems and only for
interactive use. I use it on sun4c, sun3, sun386 and 386bsd systems.
The shell itself has been compiled on the sun's both with and without
the POSIX/ANSI compatability libraries in ./std. I do not use ./std
for 386bsd and friends. See the file MACHINES for details of systems
that the shell has been built on.

I released version 4.0 of the shell (with the kind permission of
the previous maintainers and major contributors) to ensure that
it is available from usenet archive sites. Of course it remains
in the Public Domain. Equally obviously neither myself nor any
other contributors make any claims of suitability etc. Ie. NO
WARRANTY!!! If you make any changes and distribute them, please
leave your own finger prints in the source. Its bad enough
being flamed for my own bugs let alone anyone elses ๐Ÿ™‚


HISTORY:

This shell was written by Eric Gisin. It is based on Charles
Forsyth's public domain V7 shell, which he later contributed to
Minix. John R MacMillan picked up Eric Gisin's version after
Eric moved on to other projects (see ReadMe.jrm).

Since then there have been many contributors to this shell.
Most have left their fingerprints within the source and various
ReadMe.xxx and Changes.xxx files reflect their input.

This version is basically that known as Eric Gisin's version 3.3
alpha which I obtained indirectly from John R MacMillan who is
the most recent maintainer. This version has much improved
emacs-mode command line editing (my main contribution) plus
enough extra emacs-mode features to make it difficult to
distinguish from ksh88. Bug fixes from various contributors are
the only other changes from John MacMillan's version.

I have upped the version number for this release to distinguish
it from the original 3.3 version. This version is much improved
despite the small number of new features.

INSTALLATION:

The file INSTALL contains intructions for building and
installing the shell.

The original instructions indicated that a POSIX compliant
environment and possibly an ANSI compiler are required. I have
used both gcc and native Sun and the GreenHills ANSI compiler
without problems.

The POSIX/STDC compatability stuff in ./std seems to cause lots
of problems for some systems. This was at least in part because
I distributed it with half the librraies disabled :-), in any
case the shell itself in ./sh can now be compiled without any of
the ./std stuff which makes things much simpler on systems that
have a real POSIX environment.

Porting to new environemnts can be a real pain. I don't really
plan to make a huge effort in this area since I expect that this
shell will be mainly required on exotic or obscure systems (the
ones that the vendor does not provide a ksh for). Thus the
small "market" does not warrant a C-news or X11 style
portability effort. Of course if people send patches for
various systems I'm happy to try and integrate them.

ENVIRONMENT:

My main interest in this shell is for Sun workstations. Every
other UNIX system I use comes with a real ksh. Being a strictly
C-shell environment, some improved profile files are in order on
Sun's.

The etc directory contains a set of useful environment files.
These are the same files I use on several systems (many use a
real ksh):
./etc/profile
./etc/sys_config.sh
./etc/ksh.kshrc

The first one is obvious. The second, sys_config.sh is sourced
by /etc/profile and several other scripts. It is used to
determine the system type so that scripts like profile can be
used on multiple platforms.
The third one is also quite useful, add
. /etc/ksh.kshrc
to user's ~/.kshrc to simplify alias management.

BUGS:

Many folk have contributed to this shell.
I have attempted to credit (in sh/ChangeLog) the authors of bug
fixes received since the previous release.
There are surely still plenty of bugs to be found/fixed.

There is a template bug report in bug-report [borrowed from the
X11R5 mit tree], just fill in the blanks and mail to
[email protected].

I hope you find this shell as useful as I do...

Simon J. Gerraty
pdksh-5.1.2/misc/ReadMe.eg100644 0 133 3104 5571433502 14056 0ustar rootlsource Public Domain KornShell

Quick installation notes for PD KornShell

PD KornShell can be installed on 4.2+ BSD systems, System V, and
POSIX-compatable systems. The makefiles all define _BSD, change
this to _SYSV, or _POSIX. The makefiles also contain CC=gcc,
delete this if you don't have GNU C. The ksh makefile also
contains some options, including JOBS (BSD/POSIX job control)
and EDIT (emacs command editing).

PD KornShell assumes you have standard C (ANSI) and POSIX header
files and functions. Since you probably don't, they are provided
in the "std" directory.

The Alpha test version will probably come as two tar files.
std.tar contains standard C and POSIX emulation and must be
extracted into a directory called std. ksh.tar contains the ksh
source and should be extracted into a directory called src or
ksh.

See std/ReadMe and install it. Only then can you make ksh in the
"src" directory.

To clear up questions about the origin of this shell, this shell
is NOT based on the "Minix shell". It is based on Charles
Forsyth's public domain V7 shell, which he later contributed to
Minix.

I have permission directly from Charles Forsyth to use his shell.

Eric Gisin, [email protected] (or Waterloo.EDU)

Things to do
- add sxt-based job control (see Brown's contribution on the Usenix 87 tape).
- add arrays and variable attributes.
- add MAILPATH and CDPATH.
- add vi editing mode (apparently someone has a PD version).
- add new features described in Korn's book.

Machines ported to
VAX, 68000, 80386

OS's ported to
BSD 4.2, BSD 4.3 (with and without YP and NFS
Sys V.3
pdksh-5.1.2/misc/ReadMe.emacs100755 0 133 5273 5571430422 14565 0ustar rootlsourceHORIZONTAL SCROLLING
====================

I have migrated my 3.2 ksh edit.c mods into the 3.3 ksh
This file describes the mods in general and really only applies
(at this stage) to emacs-mode. I have not touched vi-mode apart
from making it use pprompt() so that '!' is correctly expanded
as in emacs-mode.

I would prefer to see both vi.c and emacs.c use a common set of
primatives in edit.c - but that looks like a lot of work.

Basically my mods affect the functions that move the cursor
about and redraw the edit line.

The input buffer "buf" is pointed to by "xbuf" and its end is
pointed to by "xend". The current position in "xbuf" and end of
the edit line are pointed to by "xcp" and "xep" respectively.
I have added "xbp" which points to the start of a display window
within "xbuf".

[A] starting position

buf
|<-------- $COLUMNS --------->|
| |<---- x_displen ------->|
| PS1|
+==========+==============--------+.......+
|\ \ \ \
xbuf xbp xcp xep xend

[B] scrolled

buf
| |<----- COLUMNS -------->|
| |<----- x_displen ------>|
|
+-----------+==========+==============--------+.......+
\ \ \ \ \
xbuf xbp xcp xep xend

In the above -------- represents the current edit line while
===== represents that portion which is visible on the screen.
Note that initially xbp == xbuf and PS1 is displayed.
PS1 uses some of the screen width and thus "x_displen" is less
than $COLUMNS.

Any time that "xcp" moves outside the region bounded by "xbp"
and "xbp" + "x_displen", the function x_adjust() is called to
relocate "xbp" appropriately and redraw the line.

Excessive I/O is avoided where possible. x_goto() for instance
calculates whether the destination is outside the visible
region, and if so simply adjusts "xcp" and calls x_adjust()
directly. Normally though x_adjust() is called from x_putc().

The above mechanism has be updated to use a function x_lastcp()
that returns a pointer that accurately reflects the last
visible char in the edit buffer. That is a more accurate
version of xbp + x_displen which does not account for TABS.

Other changes
=============

I have also added some emacs mode functions.
M-[0-9]
Set a numeric arg (positive only).
Used by all word related commands.
M-_
M-.
Retrieve word from previous command (default is last
word). Use M-[1-9] to select word.
M-u
M-l
M-c
UPPER,lower,Capitalize word.


Commands like delete-word now push the deleted text so that it
may be yanked back into place.

BUGS?
=====

There are bound to be some. Please report same to me:

Simon J. Gerraty

pdksh-5.1.2/misc/ReadMe.jrm100755 0 133 11527 5571430422 14304 0ustar rootlsourceBUILDING THE PD KSH
===================

As usual, there are differences between BSD and System V
versions. Ideally, all you have to do is edit the Makefile in
this directory to set the CONFIG macro to the appropriate value.
(Actually, you may wish to change the CONFIG macro in all
Makefiles; if you always invoke make(1) from here, CONFIG will
be passed down, but if you invoke make(1) from a subdirectory
you'll want the appropriate definition in that Makefile.)

Of course it's not quite that simple. You may, however, take
solace in the knowledge that it used to be worse.

The Compatibility Libraries
---------------------------

Eric Gisin wrote this shell using ANSI C and POSIX as
portability guidlines. Realizing that nobody had a POSIX
system and almost no one had an ANSI C environment, he provided
minimal compatibility libraries.

There are two libraries, one for POSIX (libposix.a) and one for
ANSI C (libstdc.a).

Libposix.a is pretty simple. Nothing in it has ever broken on
me, so I'd just leave it. It provides a version of dup2() for
System V systems.

Libstdc.a is a bit hairy. I recommend looking at the routines
provided and, and editing the std/stdc Makefile and only
including objects that have routines your system libc.a is
lacking. Various of the provided routines are just plain
flaky, but only when they're not really needed. The other
hairy thing he does is craft an ANSI stdio.h from the system
supplied one and one of his own. Again, it's better than it
used to be, but it's still a hack, and you may have to modify
it by hand.

You will also need a POSIX compatible set of directory reading
routines. System V.3 systems have this in libc.a. The
std/posix directory provides a something for BSD systems. I
use a slightly modified version of Doug Gwyn's PD version.

(The ``slightly modified'' is to work around a bug in Gwyn's version.
The POSIX routines are documented as returning failure if the file for
opendir(3) is not a directory. Gwyn attempts to open(2) the file, and
then stats it to see if the file is a directory. However, if the file
is a special file, and the open(2) doesn't return, you're screwed. My
change was to open the file with the O_NDELAY flag, but Gwyn didn't
feel this was portable (true, but I only do it where it works) and
that stat-ing before the open would be too slow (true). The upshot is
if you use his routines unmodified, don't ever do an "ls -l /dev/*/*".)

The Shell Source
----------------

The source for the shell itself is in the sh directory. There you
will want to edit config.h to determine the configuration options. Vi
mode is in kind of rough shape, but does work. DIRSTACK routines
aren't implemented yet, so again, why bother. SWTCH is a bit arcane,
but it you use shl(1) and you define EMACS or VI you want to define
this. JOBS is really only useful on BSD systems. It might work on
systems that have POSIX job control, but I wouldn't bet on it.
SHARPBANG is only useful on systems where the exec() family don't
honour the #!/prog/path convention.

This is where the shell gets built so you may wish to change
the OTHERLIBS macro in the Makefile to point to your POSIX
directory routines, or to use -lc_s, or whatever.

Miscellaneous
-------------

The Makefiles that actually compile things use the macro
CCOPTS, so you can change it in individual Makefiles or specify
it on the command line, eg. "make CCOPTS=-O OTHERLIBS=-lc_s".
LDOPTS is used in the Makefile where the ksh is actually built.

The very first time on a new system, do a "make clobber"

Good luck.

Documentation
-------------

The ksh.1 is a man page for the PD ksh, although it lags
somewhat behind the code. You get what you pay for.

The ksh88.1 is a man page for AT&T's ksh88 (the latest version)
provided for comparison.

History
-------

Much of the shell was written by Eric Gisin at the University
of Waterloo, using some pieces of other stuff, notably Charles
Forsythe's V7 shell, and some enhancements from the BRL shell.
He placed it (in a alpha test state) into the public domain
while I was at UW. I snarfed a copy, and got it running on my
UNIXpc, and later some machines at work. I sent Gisin some bug
reports, but he seems to have lost interest in the project.
This may be because he now does some work for MKS, who produce a
commercial version of the ksh for MS-DOS machines, so there may
be a conflict of interest.

So I gave up on getting future versions, and adopted it. I've
made some enhancements, and quite a few bug fixes, and various
people at work have contributed as well. It remains in the
public domain, although I imagine the people involved would
appreciate leaving their names attached (I'm exempt; I haven't
actually included my name anywhere but here).

The README in the sh directory is Gisin's, and tells a bit of
the story from his point of view. Note that his compilation
instructions don't really apply anymore.

John R. MacMillan
pdksh-5.1.2/os2/ 42755 0 133 0 5670633221 12063 5ustar rootlsourcepdksh-5.1.2/os2/Makefile100644 0 133 21435 5670633174 13652 0ustar rootlsourcesrcdir = .
VPATH = .

CC = gcc
CPP = cpp

INSTALL = /bin/install -c
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL) -m 644

DEFS = -DHAVE_CONFIG_H
LIBS = -los2

# configure.in sets GCC_WARNFLAGS to the contents of $(srcdir)/Warn-flags
# if gcc is being used (normally something like
# -Wall -Wno-comments -Dno_RCSids)
CFLAGS = -DOS2
LDSTATIC =
LDFLAGS = -g -O $(LDSTATIC)

prefix = c:/usr
exec_prefix = $(prefix)
binprefix =
manprefix =

bindir = $(exec_prefix)/bin
mandir = $(prefix)/man/man1
manext = 1

SHELL = /bin/sh

SRCS = alloc.c c_ksh.c c_sh.c c_test.c c_ulimit.c edit.c emacs.c \
eval.c exec.c expr.c history.c io.c jobs.c lex.c mail.c \
main.c misc.c missing.c path.c shf.c sigact.c syn.c table.c trap.c \
tree.c tty.c var.c version.c vi.c subject.c
OBJS = os2.o alloc.o c_ksh.o c_sh.o c_test.o c_ulimit.o edit.o emacs.o \
eval.o exec.o expr.o history.o io.o jobs.o lex.o mail.o \
main.o misc.o missing.o path.o shf.o sigact.o syn.o table.o trap.o \
tree.o tty.o var.o version.o vi.o subject.o
HDRS = edit.h expand.h ksh_dir.h ksh_limval.h ksh_stat.h ksh_time.h \
ksh_times.h ksh_wait.h lex.h options.h proto.h sh.h shf.h sigact.h \
table.h tree.h tty.h subject.h
RCSFILES = $(SRCS) $(HDRS) ksh.1 Makefile.in configure.in config.h.top \
config.h.bot config.h.in conf-end.h acconfig.h aclocal.m4 \
mkinstalldirs install.sh new-version.sh siglist.in siglist.sh \
check-fd.c check-pgrp.c check-sigs.c \
README NEWS CONTRIBUTORS PROJECTS
DISTFILES = $(RCSFILES) configure stamp-h.in Bugs BUG-REPORTS ChangeLog \
INSTALL NOTES
# ETCFILES also disted, but handled differently
ETCFILES = etc/ksh.kshrc etc/profile etc/sys_config.sh
# MISCFILES also disted, but handled differently
MISCFILES = misc/COPYING misc/ChangeLog.sjg misc/Changes.jrm misc/Changes.mlj \
misc/Changes.pc misc/README.sjg misc/ReadMe.eg misc/ReadMe.emacs \
misc/ReadMe.jrm
# OS2FILES also disted, but handled differently
OS2FILES = os2/Makefile os2/config.h os2/config.status os2/configure.cmd \
os2/kshrc.ksh os2/make.sed os2/os2.c os2/os2siglist.out \
os2/README.os2 os2/configure.save

.PRECIOUS: configure config.h.in Makefile config.status

# Suffix for executables: nothing for unix, .exe for os/2.
E=.exe

all: ksh$E

.c.o:
$(CC) -c $(CPPFLAGS) $(DEFS) -I. -I$(srcdir) $(CFLAGS) $<

install: installdirs all $(srcdir)/ksh.1
$(INSTALL_PROGRAM) ksh $(bindir)/$(binprefix)ksh.new
/local/admbin/Rdist -`/bin/machtype -b` $(bindir)/$(binprefix)ksh.new
-$(INSTALL_DATA) $(srcdir)/ksh.1 $(mandir)/$(manprefix)ksh.$(manext)
-@test -f /etc/shells \
&& (grep '^$(bindir)/$(binprefix)ksh.new$$' /etc/shells > /dev/null \
|| echo \
'NOTE: /etc/shells does not contain $(bindir)/$(binprefix)ksh.new \
you should add it if you want to set your shell to ksh')

installdirs:
$(srcdir)/mkinstalldirs $(bindir) $(mandir)

uninstall:
rm -f $(bindir)/$(binprefix)ksh
rm -f $(mandir)/$(manprefix)ksh.$(manext)

check:
ENV= ./ksh $(srcdir)/Bugs ./ksh

ksh$E: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

info:
@echo "No info (yet)"
dvi:

config.h:
xcopy $(srcdir)\os2\config.h config.h > nul
touch config.h

Makefile:
xcopy $(srcdir)\os2\Makefile Makefile > null
touch Makefile

# two steps to prevent the creation of a bogus siglist.out
siglist.out:
xcopy $(srcdir)\os2\os2siglist.out siglist.out > null
touch siglist.out

debugtools: check-fd$E check-pgrp$E check-sigs$E

check-fd.o check-pgrp.o check-sigs.o: config.h
check-fd$E: check-fd.o
$(CC) $(LDFLAGS) -o $@ check-fd.o $(LIBS)

check-pgrp$E: check-pgrp.o
$(CC) $(LDFLAGS) -o $@ check-pgrp.o $(LIBS)

check-sigs$E: check-sigs.o
$(CC) $(LDFLAGS) -o $@ check-sigs.o $(LIBS)

TAGS: $(SRCS) $(HDRS)
cd $(srcdir) && etags $(SRCS) $(HDRS)

tags: $(SRCS) $(HDRS)
cd $(srcdir) && ctags -wt $(SRCS) $(HDRS)

clean:
rm -f ksh$E $(OBJS) siglist.out core version.c.bak Makefile.bak \
Makefile.tmp check-fd$E check-pgrp$E check-sigs$E

mostlyclean: clean

distclean: clean
rm -f Makefile config.h stamp-h config.status tags TAGS

realclean: distclean

rcs-ci:
cd $(srcdir) && ci -l -q $(RCSFILES)

rcs-diff:
cd $(srcdir) && for i in $(RCSFILES); do \
rcsdiff -kkv -c $$i; \
done

dist: $(DISTFILES) $(ETCFILES) $(MISCFILES) $(OS2FILES)
cd $(srcdir) && \
{ \
sed -f os2/make.sed < Makefile.in > os2/Makefile; \
./new-version.sh; \
FNAME=pdksh-`sed -e '/"@(.)/!d' \
-e 's/[^0-9]*\([0-9.]*\).*/\1/' -e q version.c`; \
echo Creating version $$FNAME; \
rm -rf $$FNAME; \
mkdir $$FNAME $$FNAME/etc $$FNAME/misc $$FNAME/os2; \
cp -p $(DISTFILES) $$FNAME; \
cp -p $(ETCFILES) $$FNAME/etc; \
cp -p $(MISCFILES) $$FNAME/misc; \
cp -p $(OS2FILES) $$FNAME/os2; \
test -x ./Dist-fixup && ./Dist-fixup $$FNAME; \
chmod -R a+rX,u+w,og-w $$FNAME; \
tar chzf $$FNAME.tar.gz $$FNAME; \
find $$FNAME -print | xargs pathchk -p; \
}

depend: $(SRCS)
sed -n '1,/[ ]PUT ANYTHING BELOW THIS LINE/p' < Makefile > Makefile.tmp
srcs=; for i in $(SRCS) ; do srcs="$$srcs $(srcdir)/$$i"; done; \
$(CC) -M $(DEFS) -I. -I$(srcdir) $(CFLAGS) $$srcs | \
sed -e 's?[ ]/[^ ]*??g' -e 's?[ ]./? ?g' \
-e 's?[ ]$(srcdir)//*? ?g' -e 's?^$(srcdir)//*??' \
-e '/^[ ]*\\[ ]*$$/d' -e '/^[^:]*:[ ]*$$/d' \
-e 's/^\([ ]*\)$$/\1sh.h/' \
>> Makefile.tmp
mv Makefile.tmp Makefile
@echo 'Make depend done (stopping make)'; false

# DON'T PUT ANYTHING BELOW THIS LINE (and don't delete it - its for make depend)
alloc.o : alloc.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
c_ksh.o : c_ksh.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
c_sh.o : c_sh.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_time.h \
ksh_times.h
c_test.o : c_test.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h
c_ulimit.o : c_ulimit.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h \
sh.h
edit.o : edit.c config.h options.h conf-end.h sh.h \
shf.h table.h \
tree.h expand.h lex.h proto.h tty.h \
edit.h
emacs.o : emacs.c config.h options.h conf-end.h sh.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_dir.h \
edit.h
eval.o : eval.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h \
ksh_dir.h \
ksh_stat.h
exec.o : exec.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h \
ksh_stat.h
expr.o : expr.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
history.o : history.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h
io.o : io.c sh.h \
config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h
jobs.o : jobs.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_wait.h \
ksh_times.h tty.h \
sh.h
lex.o : lex.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
mail.o : mail.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_time.h \
sh.h
main.o : main.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_time.h \
sh.h
misc.o : misc.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
missing.o : missing.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_dir.h \
ksh_time.h \
ksh_times.h
path.o : path.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
shf.o : shf.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_limval.h
sigact.o : sigact.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
syn.o : syn.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
table.o : table.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
trap.o : trap.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h siglist.out
tree.o : tree.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
tty.o : tty.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h tty.h \
sh.h
var.o : var.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_time.h \
ksh_limval.h \
ksh_stat.h
version.o : version.c
vi.o : vi.c config.h options.h conf-end.h sh.h \
shf.h table.h \
tree.h expand.h lex.h proto.h \
ksh_stat.h edit.h
subject.o : subject.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_time.h \
subject.h
pdksh-5.1.2/os2/config.h100644 0 133 21455 5666512300 13622 0ustar rootlsource/* config.h. Generated automatically by configure. */
/* config.h.in. Generated automatically from configure.in by autoheader. */
/* Copyright (C) 1994, Memorial University of Newfoundland.
* This file is covered by the GNU General Public License, version 2, see
* the file misc/COPYING for details.
*/
/*
* Top portion of the configuration file for the PD ksh
*
* RCSid: $Id: config.h.top,v 1.1 1994/05/31 13:34:34 michael Exp $
*/

#ifndef CONFIG_H
#define CONFIG_H

#include "options.h"

/* end of config.h.top */

/* 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 to empty if the keyword does not work. */
/* #define const

/* Define if you have dirent.h. */
#define DIRENT 1


/* Define to `int' if 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 `union wait' is the type of the first arg to wait functions. */
/* #define HAVE_UNION_WAIT 1 */

/* Define if you have unistd.h. */
#define HAVE_UNISTD_H 1

/* Define if on MINIX. */
/* #undef _MINIX */

/* Define to `int' if doesn't define. */
/* #undef mode_t */

/* Define if you don't have dirent.h, but have ndir.h. */
/* #undef NDIR */

/* Define to `long' if doesn't define. */
/* #undef off_t */

/* Define to `int' if doesn't define. */
/* #undef pid_t */
#define pid_t int

/* 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. */
#define _POSIX_SOURCE 1

/* Define as the return type of signal handlers (int or void). */
#define RETSIGTYPE void

/* Define if the `S_IS*' macros in do not work properly. */
/* #undef STAT_MACROS_BROKEN */

/* Define if you don't have dirent.h, but have sys/dir.h. */
/* #undef SYSDIR */

/* Define if you don't have dirent.h, but have sys/ndir.h. */
/* #undef SYSNDIR */

/* Define if `sys_siglist' is declared by . */
/* #undef SYS_SIGLIST_DECLARED */

/* Define if you can safely include both and . */
#define TIME_WITH_SYS_TIME 1

/* Define to `int' if doesn't define. */
/* #undef uid_t */

/* Define if the closedir function returns void instead of int. */
/* #undef VOID_CLOSEDIR */

/* Copyright (C) 1994, Memorial University of Newfoundland.
* This file is covered by the GNU General Public License, version 2, see
* the file misc/COPYING for details.
*/
/* Define if your kernal doesn't handle scripts starting with #! */
#define SHARPBANG 1

/* Define if dup2() preserves the close-on-exec flag (ultrix does this) */
/* #undef DUP2_BROKEN */

/* Define if you have posix signal routines (sigaction(), et. al.) */
/* #define POSIX_SIGNALS 1 */

/* Define if you have BSD4.2 signal routines (sigsetmask(), et. al.) */
/* #undef BSD42_SIGNALS */

/* Define if you have BSD4.1 signal routines (sigset(), et. al.) */
/* #undef BSD41_SIGNALS */

/* Define if you have v7 signal routines (signal(), signal reset on delivery) */
/* #undef V7_SIGNALS */

/* Define to use the fake posix signal routines (sigact.[ch]) */
#define USE_FAKE_SIGACT 1

/* Define if you have bsd versions of the setpgrp() and getpgrp() routines */
/* #define BSD_PGRP 1 */

/* Define if you have bsd versions of the setpgid() and getpgrp() routines */
/* #undef POSIX_PGRP */

/* Define if you have sysV versions of the setpgrp() and getpgrp() routines */
/* #undef SYSV_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 */
/* #define volatile */

/* Define to 32-bit signed integer type */
#define INT32 long

/* Define to 32-bit signed integer type if doesn't define */
/* #undef clock_t */

/* Define to 32-bit signed integer type if doesn't define */
/* #undef time_t */

/* Define if time() is declared in */
#define TIME_DECLARED 1

/* Define to `unsigned' if doesn't define */
#define sigset_t unsigned

/* Define if sys_errlist[] and sys_nerr are in the C library */
/* #define HAVE_SYS_ERRLIST 1 */

/* Define if sys_errlist[] and sys_nerr are defined in */
/* #undef SYS_ERRLIST_DECLARED */

/* Define if sys_siglist[] is in the C library */
/* #define HAVE_SYS_SIGLIST 1 */

/* Define if sys_siglist[] are defined in or */
/* #undef SYS_SIGLIST_DECLARED */

/* Define if C compiler groks function prototypes */
#define HAVE_PROTOTYPES 1

/* Define if C compiler groks __attribute__((...)) (const, noreturn, format) */
#define HAVE_GCC_FUNC_ATTR 1

/* Define if you have a sane header file */
#define HAVE_UNISTD_H 1

/* Define if you have a sane header file */
/* #define HAVE_TERMIOS_H 1 */

/* Define if you don't have times() or if it always returns 0 */
/* #define TIMES_BROKEN 1 */

/* 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 have bcopy. */
#define HAVE_BCOPY 1

/* Define if you have confstr. */
/* #undef HAVE_CONFSTR */

/* Define if you have getcwd. */
#define HAVE_GETCWD 1

/* Define if you have getrusage. */
#undef HAVE_GETRUSAGE

/* Define if you have killpg. */
/* #define HAVE_KILLPG 1 */

/* Define if you have memmove. */
#define HAVE_MEMMOVE 1

/* Define if you have memset. */
#define HAVE_MEMSET 1

/* Define if you have setrlimit. */
/* #define HAVE_SETRLIMIT 1 */

/* Define if you have strcasecmp. */
/* #define HAVE_STRCASECMP 1 */

/* Define if you have strerror. */
#define HAVE_STRERROR 1

/* Define if you have strstr. */
#define HAVE_STRSTR 1

/* Define if you have sysconf. */
/* #define HAVE_SYSCONF 1 */

/* Define if you have tcsetpgrp. */
/* #define HAVE_TCSETPGRP 1 */

/* Define if you have ulimit. */
/* #define HAVE_ULIMIT 1

/* Define if you have wait3. */
/* #define HAVE_WAIT3 1 */

/* Define if you have waitpid. */
#define HAVE_WAITPID 1

/* Define if you have the header file. */
#define HAVE_FCNTL_H 1

/* Define if you have the header file. */
#define HAVE_LIMITS_H 1

/* Define if you have the header file. */
#define HAVE_MEMORY_H 1

/* Define if you have the header file. */
/* #undef HAVE_PATHS_H */

/* Define if you have the header file. */
#define HAVE_STDDEF_H 1

/* Define if you have the header file. */
#define HAVE_STDLIB_H 1

/* Define if you have the header file. */
#define HAVE_STRING_H 1

/* Define if you have the header file. */
#define HAVE_SYS_TIME_H 1

/* Define if you have the header file. */
#define HAVE_SYS_WAIT_H 1

/* Define if you have the header file. */
#define HAVE_TERMIO_H 1

/* Define if you have the header file. */
/* #undef HAVE_ULIMIT_H */

/* Define if you have the header file. */
/* #define HAVE_VALUES_H 1 */

/* Copyright (C) 1994, Memorial University of Newfoundland.
* This file is covered by the GNU General Public License, version 2, see
* the file misc/COPYING for details.
*/
/*
* Bottom portion of the configuration file for the PD ksh
*
* RCSid: $Id: config.h.bot,v 1.1 1994/05/31 13:34:34 michael Exp $
*/

/*
* 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)
# define EASY_HISTORY /* sjg's trivial history file */
#endif

/* Can we safely catch sigchld and wait for processes? */
#if (defined(HAVE_WAITPID) || defined(HAVE_WAIT3)) \
&& (defined(POSIX_SIGNALS) || defined(BSD42_SIGNALS))
# define JOB_SIGS
#else
# undef JOBS /* if no JOB_SIGS, no job control support */
#endif

/* pdksh assumes system calls return EINTR if a signal happened (this so
* the signal handler doesn't have to longjmp()). I don't know if this
* happens (or can be made to happen) with sigset() et. al., so, to be on
* the safe side, make sure a bogus shell doesn't get compiled.
* If BSD41_SIGNALS isn't defined and your compiler chokes on this, delete
* the hash in front of the error (and file a bug report).
*/
#ifdef BSD41_SIGNALS
# error pdksh needs interruptable system calls.
#endif /* BSD41_SIGNALS */

#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 */

#if defined(EMACS) || defined(VI)
# define EDIT
#else
/* # undef EDIT */
#endif

/* end of config.h.bot */
#endif /* CONFIG_H */
pdksh-5.1.2/os2/config.status100644 0 133 26 5663731210 14625 0ustar rootlsourceThis is a dummy line.
pdksh-5.1.2/os2/configure.cmd100644 0 133 2365 5667330272 14640 0ustar rootlsource@echo off
set configure_args=%1;%2;%3
set build=NONE
set exec_prefix=
set host=NONE
set nonopt=NONE
rem Skip options for now
if exist conftest.c erase conftest.c
if exist confdefs.h erase confdefs.h
echo checking for compiler
for %%i in (%path%) do if exist %%i\gcc.exe goto g_success
rem for the future we'll use sed processing
rem for %%i in (%path%) do if exist %%i\sed.exe goto s_success
rem echo No sed in search path. You must have sed.exe on your system.
rem goto end
:s_success
for %%i in (%path%) do if exist %%i\bcc.exe goto b_success
for %%i in (%path%) do if exist %%i\icc.exe goto i_success
echo compiler not found. Check your path
goto end
:b_success
echo Borland C compiler found. Configuration not complete for
echo this compiler. Just copying files for you.
goto copystuff
:i_success
echo IBM C compiler found. Configuration not complete for
echo this compiler. Just copying files for you.
goto copystuff
:g_success
echo GNU C compiler found. This is the standard configuration.
echo Copying files for you.
set CC=gcc
set CPP=cpp
rem
rem for now just copy the pre-configured config.h and makefiles
:copystuff
copy os2\makefile makefile
copy os2\config.h config.h
copy os2\config.status config.status
if not exist os2.c copy os2\os2.c os2.c
:end

pdksh-5.1.2/os2/kshrc.ksh100644 0 133 1752 5667332752 14017 0ustar rootlsource# .kshrc for OS/2 version of ksh

set -o emacs
set -o trackall

bind ^Q=quote
bind ^I=complete
#bind ^[^[=list-file

#The next four have been preprogrammed
bind ^0H=up-history
bind ^0P=down-history
bind ^0K=backward-char
bind ^0M=forward-char

bind ^0s=backward-word
bind ^0t=forward-word
bind ^0G=beginning-of-line
bind ^0O=end-of-line
bind ^0w=beginning-of-history
bind ^0u=end-of-history
bind ^0S=eot-or-delete


FCEDIT=t2
PS1='$PWD: '

alias a:='cd a:.'
alias b:='cd b:.'
alias c:='cd c:.'
alias d:='cd d:.'
alias e:='cd e:.'
alias f:='cd f:.'
alias g:='cd g:.'

alias h='fc -l'
alias j='jobs'
#alias which='type'
alias back='cd -'
alias cls='print -n "\033[H\033[2J"'

alias dir='cmd /c dir'
alias del='cmd /c del'
alias copy='cmd /c copy'
alias start='cmd /c start'

alias ll='ls -lsAFk'
alias lf='ls -CAFk'
alias cp='cp -p'
alias ls='ls -F'

clock_p () {
PS1='${__[(H=SECONDS/3600%24)==(M=SECONDS/60%60)==(S=SECONDS%60)]-$H:$M:$S}>'
typeset -Z2 H M S; let SECONDS=`date '+(%H*60+%M)*60+%S'`
}


pdksh-5.1.2/os2/make.sed100644 0 133 1305 5667417740 13603 0ustar rootlsources/@srcdir@/./
s/@CC@/gcc/
s/@CPP@/cpp/
s/@INSTALL@/\/bin\/install -c/
s/@INSTALL_PROGRAM@/$(INSTALL)/
s/@INSTALL_DATA@/$(INSTALL) -m 644/
s/@DEFS@/-DHAVE_CONFIG_H/
s/@LIBS@/-los2/
s/@LDSTATIC@//
s/@LDFLAGS@//
s/^\(CFLAGS[ ]*=\).*/\1 -DOS2/
s/^\(prefix[ ]*=\).*/\1 c:\/usr/
s/^\(OBJS[ ]*=\)/\1 os2.o/
s/^\(E[ ]*=\)/\1.exe/
/^configure:/,/^$/d
/^config.h.in:/,/^$/d
/^config.h:/,/^$/c\
config.h: \
xcopy $(srcdir)\\os2\\config.h config.h > nul\
touch config.h \

/^Makefile:/,/^$/c\
Makefile: \
xcopy $(srcdir)\\os2\\Makefile Makefile > null\
touch Makefile \

/^config.status:/,/^$/d
/^siglist.out:/,/^$/c\
siglist.out: \
xcopy $(srcdir)\\os2\\os2siglist.out siglist.out > null\
touch siglist.out \

pdksh-5.1.2/os2/os2.c100644 0 133 10416 5663767474 13073 0ustar rootlsource#define INCL_DOS
#define INCL_DOSERRORS
#define INCL_DOSSESMGR
#define INCL_WINPROGRAMLIST
/* #define USE_OS2_TOOLKIT_HEADERS */
#include
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include

/* emx library seems to define these
They should probably be removed from here unless some
other compiler needs them.
int getegid(void)
{
return 0;
}

int geteuid(void)
{
return 0;
}
*/

void noinherit(int fd)
{
DosSetFHState(fd, OPEN_FLAGS_NOINHERIT);
}

int getdup(int fd, int base)
{
int newfd, type;

for (newfd = base; newfd < _NFILES; newfd++)
if (ioctl(newfd, FGETHTYPE, &type) != -1)
continue;
else
return dup2(fd, newfd);

return -1;
}

static int isfullscreen(void)
{
PTIB ptib;
PPIB ppib;

DosGetInfoBlocks(&ptib, &ppib);
return (ppib -> pib_ultype != SSF_TYPE_WINDOWABLEVIO);
}

static int
newsession(int type, int mode, char *cmd, char **args, char **env)
{
STARTDATA sd;
STATUSDATA st;
REQUESTDATA qr;
ULONG sid, pid, len, cnt, rc;
PVOID ptr;
BYTE prio;
static char queue[18];
static HQUEUE qid = -1;
char *ap, *ep, *p;

for ( cnt = 1, len = 0; args[cnt] != NULL; cnt++ )
len += strlen(args[cnt]) + 1;
p = ap = alloca(len + 2);
*p = 0;
for ( cnt = 1, len = 0; args[cnt] != NULL; cnt++ )
{
if ( cnt > 1 )
*p++ = ' ';
strcpy(p, args[cnt]);
p += strlen(p);
}
for ( cnt = 0, len = 0; env[cnt] != NULL; cnt++ )
len += strlen(env[cnt]) + 1;
p = ep = alloca(len + 2);
*p = 0;
for ( cnt = 0, len = 0; env[cnt] != NULL; cnt++ )
{
strcpy(p, env[cnt]);
p += strlen(p) + 1;
}
*p = 0;

if ( mode == P_WAIT && qid == -1 )
{
sprintf(queue, "\\queues\\ksh%04d", getpid());
if ( DosCreateQueue(&qid, QUE_FIFO, queue) )
return -1;
}

sd.Length = sizeof(sd);
sd.Related = (mode == P_WAIT) ? SSF_RELATED_CHILD : SSF_RELATED_INDEPENDENT;
sd.FgBg = SSF_FGBG_FORE;
sd.TraceOpt = SSF_TRACEOPT_NONE;
sd.PgmTitle = NULL;
sd.PgmName = cmd;
sd.PgmInputs = (PBYTE) ap;
sd.TermQ = (mode == P_WAIT) ? (PBYTE) queue : NULL;
sd.Environment = NULL;
sd.InheritOpt = SSF_INHERTOPT_PARENT;
sd.SessionType = type;
sd.IconFile = NULL;
sd.PgmHandle = 0;
sd.PgmControl = 0;

if ( DosStartSession(&sd, &sid, &pid) )
return errno = ENOEXEC, -1;

if ( mode == P_WAIT )
{
st.Length = sizeof(st);
st.SelectInd = SET_SESSION_UNCHANGED;
st.BondInd = SET_SESSION_BOND;
DosSetSession(sid, &st);
if ( DosReadQueue(qid, &qr, &len, &ptr, 0, DCWW_WAIT, &prio, 0) )
return -1;
rc = ((PUSHORT)ptr)[1];
DosFreeMem(ptr);
exit(rc);
}
else
exit(0);
}

int _execve(char *cmd, char **args, char **env)
{
ULONG apptype;
char path[256], *p;
int rc;

strcpy(path, cmd);
for ( p = path; *p; p++ )
if ( *p == '/' )
*p = '\\';

if ( DosQueryAppType(path, &apptype) == 0 )
{
if (apptype & FAPPTYP_DOS)
return newsession(isfullscreen() ? SSF_TYPE_VDM :
SSF_TYPE_WINDOWEDVDM,
P_WAIT, path, args, env);

if ((apptype & FAPPTYP_WINDOWSREAL) ||
(apptype & FAPPTYP_WINDOWSPROT) ||
(apptype & FAPPTYP_WINDOWSPROT31))
return newsession(isfullscreen() ? PROG_WINDOW_AUTO :
PROG_SEAMLESSCOMMON,
P_WAIT, path, args, env);

if ( (apptype & FAPPTYP_EXETYPE) == FAPPTYP_WINDOWAPI )
return newsession(SSF_TYPE_PM, P_WAIT, path, args, env);

if ( (apptype & FAPPTYP_EXETYPE) == FAPPTYP_NOTWINDOWCOMPAT ||
(apptype & FAPPTYP_EXETYPE) == FAPPTYP_NOTSPEC )
if ( !isfullscreen() )
return newsession(SSF_TYPE_FULLSCREEN, P_WAIT, path, args, env);
}
if ( (rc = spawnve(P_WAIT, path, args, env)) != -1 )
exit(rc);

return -1;
}

void UnixName(char *path)
{
for ( ; *path; path++ )
if ( *path == '\\' )
*path = '/';
}

char *strchr_dirsep(char *path)
{
char *p1 = strchr(path, '\\');
char *p2 = strchr(path, '/');
if ( !p1 ) return p2;
if ( !p2 ) return p1;
return (p1 > p2) ? p2 : p1;
}


char *strrchr_dirsep(char *path)
{
char *p1 = strrchr(path, '\\');
char *p2 = strrchr(path, '/');
if ( !p1 ) return p2;
if ( !p2 ) return p1;
return (p1 > p2) ? p1 : p2;
}
pdksh-5.1.2/os2/os2siglist.out100644 0 133 1245 5656326416 15024 0ustar rootlsource/*
* signal handling
*/
{SIGHUP, "HUP", "Hangup"},
{SIGINT, "INT", "Interrupt"},
{SIGQUIT, "QUIT", "Quit"},
{SIGILL, "ILL", "Illegal instruction"},
{SIGTRAP, "TRAP", "Trace trap"},
{SIGABRT, "ABRT", "Abort"},
{SIGEMT, "EMT", "EMT trap"},
{SIGFPE, "FPE", "Floating exception"},
{SIGKILL, "KILL", "Killed"},
{SIGBUS, "BUS", "Bus error"},
{SIGSEGV, "SEGV", "Memory fault"},
{SIGSYS, "SYS", "Bad system call"},
{SIGPIPE, "PIPE", "Broken pipe"},
{SIGALRM, "ALRM", "Alarm clock"},
{SIGTERM, "TERM", "Terminated"},
{SIGUSR1, "USR1", "User defined #1"},
{SIGUSR2, "USR2", "User defined #2"},
{SIGCLD, "CLD", "Death of a child"},
{SIGBREAK, "BREAK", "Ctrl-Break"},
pdksh-5.1.2/os2/README.os2100644 0 133 31604 5667336300 13570 0ustar rootlsource OS/2 port of pdksh version 5
Nov 30, 1994

This document is intended to define the specific features and
differences in the os/2 port of pdksh. For general features and
descriptions of pdksh itself please refer to the standard pdksh readme
or the man page. In general the port has tried to maintain as many of
the standard ksh features as possible.

A default build of pdksh, called ksh.exe, is included in the os2
run time package. This version was built using the emx facility and
therefore requires that an emx run time package .08h or later be
installed on the system. This is not normally a problem since you
probably already installed it for 'ls' and other unix commands.

If you would rather build your own, then obtain the sources, and cd to
the source directory and run the script configure.cmd. (If there isn't
one there then copy the one in the os2 subdirectory.) This will
prepare the build environment so that your version of make can be used
to build the executable. Note that configure.cmd is still under
construction and you may need to make changes to the Makefile and to
config.h to match your specific needs. Copies of all of these files
will be placed into the source directory. In addition you may want to
make changes to options.h to define your particular preferences.

KSH 5 versus KSH 4.9

The KSH version 5 effort is being spearheaded by Michael Rendell, who
took over the effort by Simon J. Gerraty who maintained the version 4
code. While the version 4 code was a good implementation of ksh
version 5 strives to continue the conformance to the actual posix
definitions and to AT&T ksh.

For this first version 5 port to os/2, I merely took the 4.9 os/2
changes and applied them to the new version as best I could. Many
bugs and limitations from that port are still there (and there are
probably some new ones). A few things did get fixed, most notably
arguments to scripts are now passed in correctly. One of my goals for
this version is to make ksh not only and interactive shell but one
that can run some limited Unix(tm) shell scripts as well.

Generally the two shells behave the same. Version 5 has no logout
script and the option 'hashall' has been replaced with 'trackall'.
(To see all of the options use the command 'set -o'.) In addition the
prompt has been changed to conform with the at&t ksh shell instead of
the csh like implemetation of version 4. The '!' sign is used for the
history number and $ substitutions can be used in the prompt. The
commands behave more consistently. For example bind now goes to
standard out so it can be redirected or piped. Arrays are now
implemented as are almost all AT&T ksh features plus a few more.

OS2 implementation vs. Unix

The original OS/2 port was done by Kai Uwe Rommel. I have re-implemented
his ideas in this os/2 version. The os/2 version of ksh has been
modified to accept the standard os/2 conventions. Drive designations
a:, etc., are accepted and path separators in variables have been
changed to ';'. In addition either '/' or '\' can be used as a
directory separator. The bind command in emacs mode has been enhanced
to accept function key and alt key bindings. (To see what the alt key
binding should be, use ^Q followed by the key you are interested in.
Replace the ^ alpha you see with ^0 in the bind command.)

The remapping of keys can lead to some confusion for Unix and OS/2
users. The '\' key has a different meaning in Unix where it is used
to escape a special meaning for the character following it or in the
case of the echo (typeset) command it provides special meanings to
certain characters. At the end of a line the '\' is used to escape the
line feed permitting a command to extend to multiple lines. In OS/2
this key is generally the directory name separator. To provide for
both functions in the OS/2 pdksh the '\' is defined to be a directory
separator in any pathname specification and will keep its escape
meaning when it is followed by any non-alphanumeric character. The
echo command retains its special interpretation of '\' and will, for
example, interpret \t as a tab when printing. This can be
disconcerting when you echo the variable that you just set to c:\tmp.
If you want to use echo on a variable with pathnames in it you should
either use uppercase names or a '/' as a separator. If you have loaded
the printenv command it can be used to look at variables.

Unix uses '/' as a directory separator and the OS/2 implementation
permits and in some cases prefers this use as well. Generally you
can freely mix '/' and '\' characters in a pathname. However, 'cd \'
will not complete since '\' will escape the new line command and you
will get the secondary prompt. Just enter a / and the command will
complete. For many os/2 commands the '/' is used to indicate an
option. This should still work with pdksh as long as it is not
attached directly to the command name.

Another conflict is the use of ':' and ';'. Unix uses ':' to separate
entries in variable assignment and ';' to indicate multiple commands
on the same line. OS/2 uses the ';' to separate entries in a variable.
This could lead to problems when making an assignment to a variable in
pdksh. You will need to escape the ';' in OS/2 to prevent the shell
from interpreting it as the end of the command. Either place the
assignment command in quotes or use '\;'. Note that since a ';' is
not a valid filename or pathname character the '\' will be correctly
interpreted as an escape character.

In OS/2 the ':' is used to separate the drive letter from the rest of
the pathname. This usage had been preserved in pdksh. You can imbed
the drive letter as needed in pathnames. In addition pdksh preserves
the notion of separate contexts for each drive. To change drives you
would use the cd command. "cd A:/" would change to the root on drive
A while "cd C:." would change to whatever was current context on drive
C.

OS/2 and pdksh have similar notions about wildcard characters '*' and
'?' except that pdksh handles the expansion of these wildcard within
the shell and then passes the answer as a list to the application. If
the application needs to see the wildcard character then you must
escape it from the shell. Note that pdksh knows about other wildcard
techniques as well. Please see the man page.

PDSKH, OS/2 and the ENVIRONMENT.

The environment in OS/2 and Unix are quite different. For one thing
you don't actually login to your machine in OS/2 and your initial
environment is established by your CONFIG.SYS file. The Shell will
use the variables that were set in CONFIG.SYS and you should really
consider assigning TMPDIR and HOME for use by pdksh. It will also
use ENV as a variable that names a startup file that will run each
time the shell is started. This start up file is located in your
home directory. For compatability with 4.9 this shell will also
automatically use a startup shell kshrc.ksh if it finds one in your
home directory.

It is more difficult to get an OS/2 shell to read login files although
this shell will honor a loginfile called profile.ksh if located in
$INIT/etc, c:/usr/etc, or your home. The trick is to fool it into
thinking that you logged in. Renaming the shell to something beginning
with a '-' will do the trick. Real Unix systems modify argument 0
during the login process to accomplish this. Now if you start this
shell it will think that you just logged in. Too bad OS/2 doesn't
support symbolic links. A future release will add a special start up
option (was -0 in 4.9) to support logging in.

Should you mess up your PATH variable try 'unset PATH'. A default
path may get you going again. In addition pdksh for OS/2 always uses
commands from current context first in the search path even if it is
not explicitly set.

The shell itself can be called any name you wish. Good names include
pdksh.exe, ksh.exe, sh.exe. Be careful with names like rksh.exe or
you could end up with a restricted shell that can't do everything. In
Unix an executable can be tagged with an attribute to make it known to
the system as an executable. In OS/2 this is done with an extension.
This shell knows all of the standard OS/2 extensions plus .ksh. (Yes
it can run dos commands and OS/2 command files as well.) The .ksh
extension tells the shell that this is an executable shell script. It
need not necessarily be a ksh shell script however. the standard Unix
#! line at the top of the file determines the actual comand that will
be used to run the script. (Perhaps in the future Michael or I will
design an alias capability that permits you to define your own
extensions.)

The sample kshrc.ksh file that came with the distribution can be used
as an example that shows you how to create aliases that will simulate
the standard OS/2 commands. This means you can still use copy, dir,
and del if you want to. Keyboard bindings supplied will still provide
the command stack and suddenly you also have command line completion.
However, to get the most from the shell you will probably want the
set of Unix commands ported by the Gnu team.

Unix file systems are case sensitive and ksh expects this also. This
means that command completion is done on a case sensitive nature and
internal commands as well as aliases are case sensitive. You can use
this to your advantage. For example you want to run a dos shell only
to find out that 'command' is captured as an internal command by ksh.
Try 'COMMAND' or even 'Command' and you will get what you wanted.

SHELL SCRIPT PROGRAMMING

One of my goals in porting this shell was to be able to do shell
level script programming and to run unix shell scripts with minimal
modification. The first goal is easy and fully functional in this
release. You can write shell scripts for this or other shells. The
name of the shell you want to run the script is entered in the first
line of the script itself after a '#!' sequence. If you only enter
the name of the command then pdksh will use your search path to find
the shell. This is the recommended approach. To write portable scripts
use the 'uname' command from Gnu to set a variable that can be checked
for specialized approaches.

It is even possible to use ksh as a scripting language when your main
shell is a standard OS/2 shell. To do this you would write your ksh
script as usual except that the first line in the script should read
'extproc ksh'. The script should be named with a '.cmd' extension so
that the OS/2 shell can find it. When the cmd.exe finds this line in
the file it will call ksh to process the script for you. A script
that is written this way can also be called directly from ksh. As a
matter of fact you could use this technique entirely for script
creation and name all your scripts with the .cmd extension. Pdksh
will honor 'extproc' exactly like the standard Unix '#!' processing.

The second goal is much more difficult to achieve. Unix makes many
assumptions about how the system is set up which makes fully portable
scripts difficult to accomplish without the knowledge of the script
internals. Many script assume the presense of /dev/null and /tmp. Some
assume /bin and certain commands within /bin (usually cp and sh). Until
I can figure out how to make this more transparent you can simply make
these directories on the drive that you intend to run the script on.
(Of course, you could also modify the script.) Another standard "trick"
in Bourne shell script programming is to modify IFS to include a colon
and then use the set command to parse a variable setting $1, $2, etc.
In OS/2 this needs to be a ';'. For now you will have to hand modify
the script.

Of course Unix scripts expect the presence of Unix commands. You will
need to install a set of Unix utilities, Unix text processing
commands, and probably sed and a version of awk. I have created a
c:/usr directory on my system for Unix stuff. I have /usr/bin,
/usr/man, /usr/etc, /usr/home, and /usr/local. You could establish
even more or perhaps less Unix conformance. You will also need a ps
command. I use procs.exe which I renamed to ps.exe. Kill is a ksh
builtin.

WORK IN PROGRESS

There is still much to do to complete this project. The confiure.cmd
file needs work as does the Makefile. The standard defaults work in
the Makefile but many other things have not been checked. It is known
to work with nmake and dmake but gnu make has problems. The config.h
file was hand built to get something to work but other options and
configurations could be tried. Ksh job control emulation isn't done
nor is ksh background processing (you can use the OS/2 start command).
And of course there are bugs to fix and enhancements to be made.

Please let me know if you like this port, have found a porting bug,
have fixed a bug, or have coded a spiffy enhancement. Michael Rendell
should be consulted for general pdksh items and is now actually
maintaining and enhancing all of the code. Please check the standard
readme for more information. I can be reached at [email protected].
Note that this is a home brew project and is in no way related to my
employment.

Dale DePriest

pdksh-5.1.2/os2/configure.save100644 0 133 207764 5663723550 15106 0ustar rootlsource#!ksh
# Guess values for system-dependent variables and create Makefiles.
# Generated automatically using autoconf version 1.11
# Copyright (C) 1991, 1992, 1993, 1994 Free Software Foundation, Inc.

# This configure script is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.

# This script is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# Save the original args to write them into config.status later.
configure_args="$*"

# Only options that might do something get documented.
ac_usage="Usage: configure [options] [host]
Options: [defaults in brackets after descriptions]
--build=BUILD configure for building on BUILD [BUILD=HOST]
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--exec-prefix=PREFIX install host dependent files in PREFIX [/usr/local]
--help print this message
--host=HOST configure for HOST [guessed]
--prefix=PREFIX install host independent files in PREFIX [/usr/local]
--quiet, --silent do not print \`checking for...' messages
--srcdir=DIR find the sources in DIR [configure dir or ..]
--target=TARGET configure for TARGET [TARGET=HOST]
--verbose print results of checks
--version print the version of autoconf that created configure
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--x-includes=DIR X include files are in DIR
--x-libraries=DIR X library files are in DIR"

# Initialize some variables set by options.
# The variables have the same names as the options, with
# dashes changed to underlines.
build=NONE
exec_prefix=
host=NONE
no_create=
nonopt=NONE
norecursion=
prefix=
program_prefix=
program_suffix=
program_transform_name=
silent=
srcdir=
target=NONE
verbose=
x_includes=
x_libraries=

ac_prev=
for ac_option
do

# If the previous option needs an argument, assign it.
if test -n "$ac_prev"; then
eval "$ac_prev=\$ac_option"
ac_prev=
continue
fi

# Accept (but ignore some of) the important Cygnus configure
# options, so we can diagnose typos.

case "$ac_option" in
-*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
*) ac_optarg= ;;
esac

case "$ac_option" in

-build | --build | --buil | --bui | --bu | --b)
ac_prev=build ;;
-build=* | --build=* | --buil=* | --bui=* | --bu=* | --b=*)
build="$ac_optarg" ;;

-disable-* | --disable-*)
ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
# Reject names that aren't valid shell variable names.
if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
echo "configure: $ac_feature: invalid feature name" >&2; exit 1
fi
ac_feature=`echo $ac_feature| sed 's/-/_/g'`
eval "enable_${ac_feature}=no" ;;

-enable-* | --enable-*)
ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
# Reject names that aren't valid shell variable names.
if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
echo "configure: $ac_feature: invalid feature name" >&2; exit 1
fi
ac_feature=`echo $ac_feature| sed 's/-/_/g'`
case "$ac_option" in
*=*) ;;
*) ac_optarg=yes ;;
esac
eval "enable_${ac_feature}='$ac_optarg'" ;;

# For backward compatibility, recognize -exec-prefix and --exec_prefix.
-exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
| --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
| --exec | --exe | --ex)
ac_prev=exec_prefix ;;
-exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
| --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
| --exec=* | --exe=* | --ex=*)
exec_prefix="$ac_optarg" ;;

-gas | --gas | --ga | --g)
with_gas=yes ;; # Obsolete; use --with-gas.

-help | --help | --hel | --he)
cat << EOF
$ac_usage
EOF
exit 0 ;;

-host | --host | --hos | --ho)
ac_prev=host ;;
-host=* | --host=* | --hos=* | --ho=*)
host="$ac_optarg" ;;

-nfp | --nfp | --nf)
with_fp=no ;; # Obsolete; use --without-fp.

-no-create | --no-create | --no-creat | --no-crea | --no-cre \
| --no-cr | --no-c)
no_create=yes ;;

-norecursion | --norecursion | --norecursio | --norecursi \
| --norecurs | --norecur | --norecu | --norec | --nore | --nor)
norecursion=yes ;;

-prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
ac_prev=prefix ;;
-prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
prefix="$ac_optarg" ;;

-program-prefix | --program-prefix | --program-prefi | --program-pref \
| --program-pre | --program-pr | --program-p)
ac_prev=program_prefix ;;
-program-prefix=* | --program-prefix=* | --program-prefi=* \
| --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
program_prefix="$ac_optarg" ;;

-program-suffix | --program-suffix | --program-suffi | --program-suff \
| --program-suf | --program-su | --program-s)
ac_prev=program_suffix ;;
-program-suffix=* | --program-suffix=* | --program-suffi=* \
| --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
program_suffix="$ac_optarg" ;;

-program-transform-name | --program-transform-name \
| --program-transform-nam | --program-transform-na \
| --program-transform-n | --program-transform- \
| --program-transform | --program-transfor \
| --program-transfo | --program-transf \
| --program-trans | --program-tran \
| --progr-tra | --program-tr | --program-t)
ac_prev=program_transform_name ;;
-program-transform-name=* | --program-transform-name=* \
| --program-transform-nam=* | --program-transform-na=* \
| --program-transform-n=* | --program-transform-=* \
| --program-transform=* | --program-transfor=* \
| --program-transfo=* | --program-transf=* \
| --program-trans=* | --program-tran=* \
| --progr-tra=* | --program-tr=* | --program-t=*)
program_transform_name="$ac_optarg" ;;

-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;

-srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
ac_prev=srcdir ;;
-srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
srcdir="$ac_optarg" ;;

-target | --target | --targe | --targ | --tar | --ta | --t)
ac_prev=target ;;
-target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
target="$ac_optarg" ;;

-v | -verbose | --verbose | --verbos | --verbo | --verb)
verbose=yes ;;

-version | --version | --versio | --versi | --vers)
echo "configure generated by autoconf version 1.11"
exit 0 ;;

-with-* | --with-*)
ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
# Reject names that aren't valid shell variable names.
if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
echo "configure: $ac_package: invalid package name" >&2; exit 1
fi
ac_package=`echo $ac_package| sed 's/-/_/g'`
case "$ac_option" in
*=*) ;;
*) ac_optarg=yes ;;
esac
eval "with_${ac_package}='$ac_optarg'" ;;

-without-* | --without-*)
ac_package=`echo $ac_option|sed -e 's/-*without-//'`
# Reject names that aren't valid shell variable names.
if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
echo "configure: $ac_package: invalid package name" >&2; exit 1
fi
ac_package=`echo $ac_package| sed 's/-/_/g'`
eval "with_${ac_package}=no" ;;

--x) with_x=yes ;; # Obsolete; use --with-x.

-x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
| --x-incl | --x-inc | --x-in | --x-i)
ac_prev=x_includes ;;
-x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
| --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
x_includes="$ac_optarg" ;;

-x-libraries | --x-libraries | --x-librarie | --x-librari \
| --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
ac_prev=x_libraries ;;
-x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
| --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
x_libraries="$ac_optarg" ;;

-*) echo "configure: $ac_option: invalid option; use --help to show usage" >&2; exit 1
;;

*)
if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
echo "configure: warning: $ac_option: invalid host type" >&2
fi
if test "x$nonopt" != xNONE; then
echo "configure: can only configure for one host and one target at a time" >&2; exit 1
fi
nonopt="$ac_option"
;;

esac
done

if test -n "$ac_prev"; then
echo "configure: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" >&2; exit 1
fi

trap 'rm -fr conftest* confdefs* core $ac_clean_files; exit 1' 1 2 15
trap 'rm -fr confdefs* $ac_clean_files' 0

# Save the original args if we used an alternate arg parser.
ac_configure_temp="${configure_args-$*}"
# Strip out --no-create and --norecursion so they don't pile up.
configure_args=
for ac_arg in $ac_configure_temp; do
case "$ac_arg" in
-no-create | --no-create | --no-creat | --no-crea | --no-cre \
| --no-cr | --no-c) ;;
-norecursion | --norecursion | --norecursio | --norecursi \
| --norecurs | --norecur | --norecu | --norec | --nore | --nor) ;;
*) configure_args="$configure_args $ac_arg" ;;
esac
done

# NLS nuisances.
# These must not be set unconditionally because not all systems understand
# e.g. LANG=C (notably SCO).
if test "${LC_ALL+set}" = 'set'; then LC_ALL=C; export LC_ALL; fi
if test "${LANG+set}" = 'set'; then LANG=C; export LANG; fi

# confdefs.h avoids OS command line length limits that DEFS can exceed.
rm -rf conftest* confdefs.h
# AIX cpp loses on an empty file, so make sure it contains at least a newline.
echo > confdefs.h

# A filename unique to this package, relative to the directory that
# configure is in, which we can look for to find out if srcdir is correct.
ac_unique_file=c_ksh.c

# Find the source files, if location was not specified.
if test -z "$srcdir"; then
ac_srcdir_defaulted=yes
# Try the directory containing this script, then `..'.
ac_prog=$0
ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
srcdir=$ac_confdir
if test ! -r $srcdir/$ac_unique_file; then
srcdir=. #modified for now
fi
fi
if test ! -r $srcdir/$ac_unique_file; then
if test x$ac_srcdir_defaulted = xyes; then
echo "configure: can not find sources in ${ac_confdir} or .." >&2; exit 1
else
echo "configure: can not find sources in ${srcdir}" >&2; exit 1
fi
fi
ac_ext=c
# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
ac_cpp='${CPP}'
ac_compile='${CC-cc} $CFLAGS $LDFLAGS conftest.${ac_ext} -o conftest $LIBS >/dev/null 2>&1'



LDSTATIC=${LDSTATIC-}
if test -z "$CC"; then
# Extract the first word of `gcc', so it can be a program name with args.
set ac_dummy gcc; ac_word=$2
test -n "$silent" || echo "checking for $ac_word"
IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
for ac_dir in $PATH; do
test -z "$ac_dir" && ac_dir=.
if test -f $ac_dir/$ac_word; then
CC="gcc"
break
fi
done
IFS="$ac_save_ifs"
fi
test -z "$CC" && CC="cc"
test -n "$CC" && test -n "$verbose" && echo " setting CC to $CC"

# Find out if we are using GNU C, under whatever name.
cat > conftest.c < #ifdef __GNUC__
yes
#endif
EOF
${CC-cc} -E conftest.c > conftest.out 2>&1
if egrep yes conftest.out >/dev/null 2>&1; then
GCC=1 # For later tests.
fi
rm -f conftest*

test -n "$silent" || echo "checking how to run the C preprocessor"
if test -z "$CPP"; then
# This must be in double quotes, not single quotes, because CPP may get
# substituted into the Makefile and ``${CC-cc}'' will simply confuse
# make. It must be expanded now.
CPP="${CC-cc} -E"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
Syntax Error
EOF
# Some shells (Coherent) do redirections in the wrong order, so need
# the parens.
ac_err=`eval "($ac_cpp conftest.${ac_ext} >/dev/null) 2>&1"`
if test -z "$ac_err"; then
:
else
rm -rf conftest*
CPP="${CC-cc} -E -traditional-cpp"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
Syntax Error
EOF
# Some shells (Coherent) do redirections in the wrong order, so need
# the parens.
ac_err=`eval "($ac_cpp conftest.${ac_ext} >/dev/null) 2>&1"`
if test -z "$ac_err"; then
:
else
rm -rf conftest*
CPP=/lib/cpp
fi
rm -f conftest*
fi
rm -f conftest*
fi
test -n "$verbose" && echo " setting CPP to $CPP"

if test -n "$GCC"; then
test -n "$silent" || echo "checking whether -traditional is needed"
ac_pattern="Autoconf.*'x'"
ac_prog='#include
Autoconf TIOCGETP'
cat > conftest.${ac_ext} < #include "confdefs.h"
$ac_prog
EOF
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "$ac_pattern" conftest.out >/dev/null 2>&1; then
rm -rf conftest*
ac_need_trad=1

fi
rm -f conftest*


if test -z "$ac_need_trad"; then
ac_prog='#include
Autoconf TCGETA'
cat > conftest.${ac_ext} < #include "confdefs.h"
$ac_prog
EOF
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "$ac_pattern" conftest.out >/dev/null 2>&1; then
rm -rf conftest*
ac_need_trad=1

fi
rm -f conftest*

fi
test -n "$ac_need_trad" && CC="$CC -traditional"
fi

GCC_WARNFLAGS=
if test -n "$GCC" && test -f $srcdir/Warn-flags; then
GCC_WARNFLAGS="`cat $srcdir/Warn-flags`"
fi
test -n "$silent" || echo "checking for POSIXized ISC"
if test -d /etc/conf/kconfig.d &&
grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1
then
ISC=1 # If later tests want to check for ISC.

{
test -n "$verbose" && \
echo " defining _POSIX_SOURCE"
echo "#define" _POSIX_SOURCE "1" >> confdefs.h
DEFS="$DEFS -D_POSIX_SOURCE=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}_POSIX_SOURCE\${ac_dB}_POSIX_SOURCE\${ac_dC}1\${ac_dD}
\${ac_uA}_POSIX_SOURCE\${ac_uB}_POSIX_SOURCE\${ac_uC}1\${ac_uD}
\${ac_eA}_POSIX_SOURCE\${ac_eB}_POSIX_SOURCE\${ac_eC}1\${ac_eD}
"
}

if test -n "$GCC"; then
CC="$CC -posix"
else
CC="$CC -Xp"
fi
fi

test -n "$silent" || echo "checking for minix/config.h"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
EOF
# Some shells (Coherent) do redirections in the wrong order, so need
# the parens.
ac_err=`eval "($ac_cpp conftest.${ac_ext} >/dev/null) 2>&1"`
if test -z "$ac_err"; then
rm -rf conftest*
MINIX=1

fi
rm -f conftest*

# The Minix shell can't assign to the same variable on the same line!
if test -n "$MINIX"; then

{
test -n "$verbose" && \
echo " defining _POSIX_SOURCE"
echo "#define" _POSIX_SOURCE "1" >> confdefs.h
DEFS="$DEFS -D_POSIX_SOURCE=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}_POSIX_SOURCE\${ac_dB}_POSIX_SOURCE\${ac_dC}1\${ac_dD}
\${ac_uA}_POSIX_SOURCE\${ac_uB}_POSIX_SOURCE\${ac_uC}1\${ac_uD}
\${ac_eA}_POSIX_SOURCE\${ac_eB}_POSIX_SOURCE\${ac_eC}1\${ac_eD}
"
}


{
test -n "$verbose" && \
echo " defining" _POSIX_1_SOURCE to be "2"
echo "#define" _POSIX_1_SOURCE "2" >> confdefs.h
DEFS="$DEFS -D_POSIX_1_SOURCE=2"
ac_sed_defs="${ac_sed_defs}\${ac_dA}_POSIX_1_SOURCE\${ac_dB}_POSIX_1_SOURCE\${ac_dC}2\${ac_dD}
\${ac_uA}_POSIX_1_SOURCE\${ac_uB}_POSIX_1_SOURCE\${ac_uC}2\${ac_uD}
\${ac_eA}_POSIX_1_SOURCE\${ac_eB}_POSIX_1_SOURCE\${ac_eC}2\${ac_eD}
"
}


{
test -n "$verbose" && \
echo " defining _MINIX"
echo "#define" _MINIX "1" >> confdefs.h
DEFS="$DEFS -D_MINIX=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}_MINIX\${ac_dB}_MINIX\${ac_dC}1\${ac_dD}
\${ac_uA}_MINIX\${ac_uB}_MINIX\${ac_uC}1\${ac_uD}
\${ac_eA}_MINIX\${ac_eB}_MINIX\${ac_eC}1\${ac_eD}
"
}

fi

test -n "$silent" || echo "checking for directory library header"
ac_dir_header=
if test -z "$ac_dir_header"; then
test -n "$silent" || echo "checking for dirent.h"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include
int main() { return 0; }
int t() { DIR *dirp = 0;; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining DIRENT"
echo "#define" DIRENT "1" >> confdefs.h
DEFS="$DEFS -DDIRENT=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}DIRENT\${ac_dB}DIRENT\${ac_dC}1\${ac_dD}
\${ac_uA}DIRENT\${ac_uB}DIRENT\${ac_uC}1\${ac_uD}
\${ac_eA}DIRENT\${ac_eB}DIRENT\${ac_eC}1\${ac_eD}
"
}
ac_dir_header=dirent.h

fi
rm -f conftest*
fi
if test -z "$ac_dir_header"; then
test -n "$silent" || echo "checking for sys/ndir.h"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include
int main() { return 0; }
int t() { DIR *dirp = 0;; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining SYSNDIR"
echo "#define" SYSNDIR "1" >> confdefs.h
DEFS="$DEFS -DSYSNDIR=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}SYSNDIR\${ac_dB}SYSNDIR\${ac_dC}1\${ac_dD}
\${ac_uA}SYSNDIR\${ac_uB}SYSNDIR\${ac_uC}1\${ac_uD}
\${ac_eA}SYSNDIR\${ac_eB}SYSNDIR\${ac_eC}1\${ac_eD}
"
}
ac_dir_header=sys/ndir.h

fi
rm -f conftest*
fi
if test -z "$ac_dir_header"; then
test -n "$silent" || echo "checking for sys/dir.h"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include
int main() { return 0; }
int t() { DIR *dirp = 0;; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining SYSDIR"
echo "#define" SYSDIR "1" >> confdefs.h
DEFS="$DEFS -DSYSDIR=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}SYSDIR\${ac_dB}SYSDIR\${ac_dC}1\${ac_dD}
\${ac_uA}SYSDIR\${ac_uB}SYSDIR\${ac_uC}1\${ac_uD}
\${ac_eA}SYSDIR\${ac_eB}SYSDIR\${ac_eC}1\${ac_eD}
"
}
ac_dir_header=sys/dir.h

fi
rm -f conftest*
fi
if test -z "$ac_dir_header"; then
test -n "$silent" || echo "checking for ndir.h"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include
int main() { return 0; }
int t() { DIR *dirp = 0;; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining NDIR"
echo "#define" NDIR "1" >> confdefs.h
DEFS="$DEFS -DNDIR=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}NDIR\${ac_dB}NDIR\${ac_dC}1\${ac_dD}
\${ac_uA}NDIR\${ac_uB}NDIR\${ac_uC}1\${ac_uD}
\${ac_eA}NDIR\${ac_eB}NDIR\${ac_eC}1\${ac_eD}
"
}
ac_dir_header=ndir.h

fi
rm -f conftest*
fi

test -n "$silent" || echo "checking for closedir return value"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include <$ac_dir_header>
int closedir(); main() { exit(closedir(opendir(".")) != 0); }
EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then
:
else

{
test -n "$verbose" && \
echo " defining VOID_CLOSEDIR"
echo "#define" VOID_CLOSEDIR "1" >> confdefs.h
DEFS="$DEFS -DVOID_CLOSEDIR=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}VOID_CLOSEDIR\${ac_dB}VOID_CLOSEDIR\${ac_dC}1\${ac_dD}
\${ac_uA}VOID_CLOSEDIR\${ac_uB}VOID_CLOSEDIR\${ac_uC}1\${ac_uD}
\${ac_eA}VOID_CLOSEDIR\${ac_eB}VOID_CLOSEDIR\${ac_eC}1\${ac_eD}
"
}

fi
rm -fr conftest*

test -n "$silent" || echo "checking for Xenix"
cat > conftest.${ac_ext} < #include "confdefs.h"
#if defined(M_XENIX) && !defined(M_UNIX)
yes
#endif

EOF
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "yes" conftest.out >/dev/null 2>&1; then
rm -rf conftest*
XENIX=1

fi
rm -f conftest*

if test -n "$XENIX"; then
LIBS="$LIBS -lx"
case "$DEFS" in
*SYSNDIR*) ;;
*) LIBS="-ldir $LIBS" ;; # Make sure -ldir precedes any -lx.
esac
fi

test -n "$silent" || echo "checking for sane unistd.h"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
#ifdef _POSIX_VERSION
# include
#endif

int main() { return 0; }
int t() { ; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_UNISTD_H"
echo "#define" HAVE_UNISTD_H "1" >> confdefs.h
DEFS="$DEFS -DHAVE_UNISTD_H=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_UNISTD_H\${ac_dB}HAVE_UNISTD_H\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_UNISTD_H\${ac_uB}HAVE_UNISTD_H\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_UNISTD_H\${ac_eB}HAVE_UNISTD_H\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*

test -n "$silent" || echo "checking for sane termios.h"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
int main() { return 0; }
int t() { struct termios t;
tcgetattr(0, &t); tcsetattr(0, TCSADRAIN, &t);
; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_TERMIOS_H"
echo "#define" HAVE_TERMIOS_H "1" >> confdefs.h
DEFS="$DEFS -DHAVE_TERMIOS_H=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_TERMIOS_H\${ac_dB}HAVE_TERMIOS_H\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_TERMIOS_H\${ac_uB}HAVE_TERMIOS_H\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_TERMIOS_H\${ac_eB}HAVE_TERMIOS_H\${ac_eC}1\${ac_eD}
"
}


else
rm -rf conftest*
test -n "$silent" || echo "checking for BSD tty interface"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
int main() { return 0; }
int t() {
struct sgttyb sb; ioctl(0, TIOCGETP, &sb);
#ifdef TIOCGATC
{ struct ttychars lc; ioctl(0, TIOCGATC, &lc); }
#else /* TIOCGATC */
{ struct tchars tc; ioctl(0, TIOCGETC, &tc); }
# ifdef TIOCGLTC
{ struct ltchars ltc; ioctl(0, TIOCGLTC, <c); }
# endif /* TIOCGLTC */
#endif /* TIOCGATC */
; return 0; }
EOF
if eval $ac_compile; then
:
else
rm -rf conftest*
for ac_hdr in termio.h
do
ac_tr_hdr=HAVE_`echo $ac_hdr | tr '[a-z]./' '[A-Z]__'`
test -n "$silent" || echo "checking for ${ac_hdr}"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include <${ac_hdr}>
EOF
# Some shells (Coherent) do redirections in the wrong order, so need
# the parens.
ac_err=`eval "($ac_cpp conftest.${ac_ext} >/dev/null) 2>&1"`
if test -z "$ac_err"; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining ${ac_tr_hdr}"
echo "#define" ${ac_tr_hdr} "1" >> confdefs.h
DEFS="$DEFS -D${ac_tr_hdr}=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}${ac_tr_hdr}\${ac_dB}${ac_tr_hdr}\${ac_dC}1\${ac_dD}
\${ac_uA}${ac_tr_hdr}\${ac_uB}${ac_tr_hdr}\${ac_uC}1\${ac_uD}
\${ac_eA}${ac_tr_hdr}\${ac_eB}${ac_tr_hdr}\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*
done

fi
rm -f conftest*

fi
rm -f conftest*

for ac_hdr in stddef.h stdlib.h string.h memory.h \
fcntl.h limits.h paths.h values.h ulimit.h sys/time.h sys/wait.h
do
ac_tr_hdr=HAVE_`echo $ac_hdr | tr '[a-z]./' '[A-Z]__'`
test -n "$silent" || echo "checking for ${ac_hdr}"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include <${ac_hdr}>
EOF
# Some shells (Coherent) do redirections in the wrong order, so need
# the parens.
ac_err=`eval "($ac_cpp conftest.${ac_ext} >/dev/null) 2>&1"`
if test -z "$ac_err"; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining ${ac_tr_hdr}"
echo "#define" ${ac_tr_hdr} "1" >> confdefs.h
DEFS="$DEFS -D${ac_tr_hdr}=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}${ac_tr_hdr}\${ac_dB}${ac_tr_hdr}\${ac_dC}1\${ac_dD}
\${ac_uA}${ac_tr_hdr}\${ac_uB}${ac_tr_hdr}\${ac_uC}1\${ac_uD}
\${ac_eA}${ac_tr_hdr}\${ac_eB}${ac_tr_hdr}\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*
done

test -n "$silent" || echo "checking for whether time.h and sys/time.h may both be included"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include
#include
int main() { return 0; }
int t() { struct tm *tp;; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining TIME_WITH_SYS_TIME"
echo "#define" TIME_WITH_SYS_TIME "1" >> confdefs.h
DEFS="$DEFS -DTIME_WITH_SYS_TIME=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}TIME_WITH_SYS_TIME\${ac_dB}TIME_WITH_SYS_TIME\${ac_dC}1\${ac_dD}
\${ac_uA}TIME_WITH_SYS_TIME\${ac_uB}TIME_WITH_SYS_TIME\${ac_uC}1\${ac_uD}
\${ac_eA}TIME_WITH_SYS_TIME\${ac_eB}TIME_WITH_SYS_TIME\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*

test -n "$silent" || echo "checking for off_t in sys/types.h"
echo '#include "confdefs.h"
#include ' > conftest.${ac_ext}
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "(^|[^a-zA-Z0-9_])off_t([^a-zA-Z0-9_]|\$)" conftest.out >/dev/null 2>&1; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" off_t to be "long"
echo "#define" off_t "long" >> confdefs.h
DEFS="$DEFS -Doff_t=long"
ac_sed_defs="${ac_sed_defs}\${ac_dA}off_t\${ac_dB}off_t\${ac_dC}long\${ac_dD}
\${ac_uA}off_t\${ac_uB}off_t\${ac_uC}long\${ac_uD}
\${ac_eA}off_t\${ac_eB}off_t\${ac_eC}long\${ac_eD}
"
}

fi
rm -f conftest*

test -n "$silent" || echo "checking for mode_t in sys/types.h"
echo '#include "confdefs.h"
#include ' > conftest.${ac_ext}
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "(^|[^a-zA-Z0-9_])mode_t([^a-zA-Z0-9_]|\$)" conftest.out >/dev/null 2>&1; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" mode_t to be "int"
echo "#define" mode_t "int" >> confdefs.h
DEFS="$DEFS -Dmode_t=int"
ac_sed_defs="${ac_sed_defs}\${ac_dA}mode_t\${ac_dB}mode_t\${ac_dC}int\${ac_dD}
\${ac_uA}mode_t\${ac_uB}mode_t\${ac_uC}int\${ac_uD}
\${ac_eA}mode_t\${ac_eB}mode_t\${ac_eC}int\${ac_eD}
"
}

fi
rm -f conftest*

test -n "$silent" || echo "checking for pid_t in sys/types.h"
echo '#include "confdefs.h"
#include ' > conftest.${ac_ext}
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "(^|[^a-zA-Z0-9_])pid_t([^a-zA-Z0-9_]|\$)" conftest.out >/dev/null 2>&1; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" pid_t to be "int"
echo "#define" pid_t "int" >> confdefs.h
DEFS="$DEFS -Dpid_t=int"
ac_sed_defs="${ac_sed_defs}\${ac_dA}pid_t\${ac_dB}pid_t\${ac_dC}int\${ac_dD}
\${ac_uA}pid_t\${ac_uB}pid_t\${ac_uC}int\${ac_uD}
\${ac_eA}pid_t\${ac_eB}pid_t\${ac_eC}int\${ac_eD}
"
}

fi
rm -f conftest*

test -n "$silent" || echo "checking for uid_t in sys/types.h"
echo '#include "confdefs.h"
#include ' > conftest.${ac_ext}
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "(^|[^a-zA-Z0-9_])uid_t([^a-zA-Z0-9_]|\$)" conftest.out >/dev/null 2>&1; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" uid_t to be "int"
echo "#define" uid_t "int" >> confdefs.h
DEFS="$DEFS -Duid_t=int"
ac_sed_defs="${ac_sed_defs}\${ac_dA}uid_t\${ac_dB}uid_t\${ac_dC}int\${ac_dD}
\${ac_uA}uid_t\${ac_uB}uid_t\${ac_uC}int\${ac_uD}
\${ac_eA}uid_t\${ac_eB}uid_t\${ac_eC}int\${ac_eD}
"
}

{
test -n "$verbose" && \
echo " defining" gid_t to be "int"
echo "#define" gid_t "int" >> confdefs.h
DEFS="$DEFS -Dgid_t=int"
ac_sed_defs="${ac_sed_defs}\${ac_dA}gid_t\${ac_dB}gid_t\${ac_dC}int\${ac_dD}
\${ac_uA}gid_t\${ac_uB}gid_t\${ac_uC}int\${ac_uD}
\${ac_eA}gid_t\${ac_eB}gid_t\${ac_eC}int\${ac_eD}
"
}

fi
rm -f conftest*


test -n "$silent" || echo "checking for signed 32 bit integer type"
cat > conftest.${ac_ext} < #include "confdefs.h"
main() { if (sizeof(long) != 4) exit(1); exit(0); }
EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then

{
test -n "$verbose" && \
echo " defining" INT32 to be "long"
echo "#define" INT32 "long" >> confdefs.h
DEFS="$DEFS -DINT32=long"
ac_sed_defs="${ac_sed_defs}\${ac_dA}INT32\${ac_dB}INT32\${ac_dC}long\${ac_dD}
\${ac_uA}INT32\${ac_uB}INT32\${ac_uC}long\${ac_uD}
\${ac_eA}INT32\${ac_eB}INT32\${ac_eC}long\${ac_eD}
"
}


else

{
test -n "$verbose" && \
echo " defining" INT32 to be "int"
echo "#define" INT32 "int" >> confdefs.h
DEFS="$DEFS -DINT32=int"
ac_sed_defs="${ac_sed_defs}\${ac_dA}INT32\${ac_dB}INT32\${ac_dC}int\${ac_dD}
\${ac_uA}INT32\${ac_uB}INT32\${ac_uC}int\${ac_uD}
\${ac_eA}INT32\${ac_eB}INT32\${ac_eC}int\${ac_eD}
"
}

fi
rm -fr conftest*
test -n "$silent" || echo "checking for clock_t in sys/types.h"
echo '#include "confdefs.h"
#include ' > conftest.${ac_ext}
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "(^|[^a-zA-Z0-9_])clock_t([^a-zA-Z0-9_]|\$)" conftest.out >/dev/null 2>&1; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" clock_t to be "INT32"
echo "#define" clock_t "INT32" >> confdefs.h
DEFS="$DEFS -Dclock_t=INT32"
ac_sed_defs="${ac_sed_defs}\${ac_dA}clock_t\${ac_dB}clock_t\${ac_dC}INT32\${ac_dD}
\${ac_uA}clock_t\${ac_uB}clock_t\${ac_uC}INT32\${ac_uD}
\${ac_eA}clock_t\${ac_eB}clock_t\${ac_eC}INT32\${ac_eD}
"
}

fi
rm -f conftest*

test -n "$silent" || echo "checking for time_t in sys/types.h"
echo '#include "confdefs.h"
#include ' > conftest.${ac_ext}
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "(^|[^a-zA-Z0-9_])time_t([^a-zA-Z0-9_]|\$)" conftest.out >/dev/null 2>&1; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" time_t to be "INT32"
echo "#define" time_t "INT32" >> confdefs.h
DEFS="$DEFS -Dtime_t=INT32"
ac_sed_defs="${ac_sed_defs}\${ac_dA}time_t\${ac_dB}time_t\${ac_dC}INT32\${ac_dD}
\${ac_uA}time_t\${ac_uB}time_t\${ac_uC}INT32\${ac_uD}
\${ac_eA}time_t\${ac_eB}time_t\${ac_eC}INT32\${ac_eD}
"
}

fi
rm -f conftest*

test -n "$silent" || echo "checking for sigset_t in signal.h"
echo '#include "confdefs.h"
#include ' > conftest.${ac_ext}
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "(^|[^a-zA-Z0-9_])sigset_t([^a-zA-Z0-9_]|\$)" conftest.out >/dev/null 2>&1; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" sigset_t to be "unsigned"
echo "#define" sigset_t "unsigned" >> confdefs.h
DEFS="$DEFS -Dsigset_t=unsigned"
ac_sed_defs="${ac_sed_defs}\${ac_dA}sigset_t\${ac_dB}sigset_t\${ac_dC}unsigned\${ac_dD}
\${ac_uA}sigset_t\${ac_uB}sigset_t\${ac_uC}unsigned\${ac_uD}
\${ac_eA}sigset_t\${ac_eB}sigset_t\${ac_eC}unsigned\${ac_eD}
"
}

fi
rm -f conftest*

test -n "$silent" || echo "checking for return type of signal handlers"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include
#ifdef signal
#undef signal
#endif
extern void (*signal ()) ();
int main() { return 0; }
int t() { int i;; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" RETSIGTYPE to be "void"
echo "#define" RETSIGTYPE "void" >> confdefs.h
DEFS="$DEFS -DRETSIGTYPE=void"
ac_sed_defs="${ac_sed_defs}\${ac_dA}RETSIGTYPE\${ac_dB}RETSIGTYPE\${ac_dC}void\${ac_dD}
\${ac_uA}RETSIGTYPE\${ac_uB}RETSIGTYPE\${ac_uC}void\${ac_uD}
\${ac_eA}RETSIGTYPE\${ac_eB}RETSIGTYPE\${ac_eC}void\${ac_eD}
"
}


else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" RETSIGTYPE to be "int"
echo "#define" RETSIGTYPE "int" >> confdefs.h
DEFS="$DEFS -DRETSIGTYPE=int"
ac_sed_defs="${ac_sed_defs}\${ac_dA}RETSIGTYPE\${ac_dB}RETSIGTYPE\${ac_dC}int\${ac_dD}
\${ac_uA}RETSIGTYPE\${ac_uB}RETSIGTYPE\${ac_uC}int\${ac_uD}
\${ac_eA}RETSIGTYPE\${ac_eB}RETSIGTYPE\${ac_eC}int\${ac_eD}
"
}

fi
rm -f conftest*


for ac_func in bcopy confstr getcwd killpg memset memmove setrlimit strerror \
strcasecmp strstr sysconf tcsetpgrp ulimit waitpid wait3
do
ac_tr_func=HAVE_`echo $ac_func | tr '[a-z]' '[A-Z]'`
test -n "$silent" || echo "checking for ${ac_func}"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
int main() { return 0; }
int t() {
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined (__stub_${ac_func}) || defined (__stub___${ac_func})
choke me
#else
/* Override any gcc2 internal prototype to avoid an error. */
extern char ${ac_func}(); ${ac_func}();
#endif
; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*
{
test -n "$verbose" && \
echo " defining ${ac_tr_func}"
echo "#define" ${ac_tr_func} "1" >> confdefs.h
DEFS="$DEFS -D${ac_tr_func}=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}${ac_tr_func}\${ac_dB}${ac_tr_func}\${ac_dC}1\${ac_dD}
\${ac_uA}${ac_tr_func}\${ac_uB}${ac_tr_func}\${ac_uC}1\${ac_uD}
\${ac_eA}${ac_tr_func}\${ac_eB}${ac_tr_func}\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*
done


test -n "$silent" || echo "checking for working mmap"
cat > conftest.${ac_ext} < #include "confdefs.h"
/* Thanks to Mike Haertel and Jim Avera for this test. */
#include
#include
#include

#ifdef BSD
#ifndef BSD4_1
#define HAVE_GETPAGESIZE
#endif
#endif
#ifndef HAVE_GETPAGESIZE
#include
#ifdef EXEC_PAGESIZE
#define getpagesize() EXEC_PAGESIZE
#else
#ifdef NBPG
#define getpagesize() NBPG * CLSIZE
#ifndef CLSIZE
#define CLSIZE 1
#endif /* no CLSIZE */
#else /* no NBPG */
#ifdef NBPC
#define getpagesize() NBPC
#else /* no NBPC */
#define getpagesize() PAGESIZE /* SVR4 */
#endif /* no NBPC */
#endif /* no NBPG */
#endif /* no EXEC_PAGESIZE */
#endif /* not HAVE_GETPAGESIZE */

#ifdef __osf__
#define valloc malloc
#endif

#ifndef MAP_FILE
# define MAP_FILE 0
#endif /* MAP_FILE */

extern char *valloc();
extern char *malloc();

int
main()
{
char *buf1, *buf2, *buf3;
int i = getpagesize(), j;
int i2 = getpagesize()*2;
int fd;

buf1 = valloc(i2);
buf2 = valloc(i);
buf3 = malloc(i2);
for (j = 0; j < i2; ++j)
*(buf1 + j) = rand();
fd = open("conftestmmap", O_CREAT | O_RDWR, 0666);
write(fd, buf1, i2);
mmap(buf2, i, PROT_READ | PROT_WRITE, MAP_FILE | MAP_FIXED | MAP_PRIVATE, fd, 0);
for (j = 0; j < i; ++j)
if (*(buf1 + j) != *(buf2 + j))
exit(1);
lseek(fd, (long)i, 0);
read(fd, buf2, i); /* read into mapped memory -- file should not change */
/* (it does in i386 SVR4.0 - Jim Avera) */
lseek(fd, (long)0, 0);
read(fd, buf3, i2);
for (j = 0; j < i2; ++j)
if (*(buf1 + j) != *(buf3 + j))
exit(1);
exit(0);
}

EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then

{
test -n "$verbose" && \
echo " defining HAVE_MMAP"
echo "#define" HAVE_MMAP "1" >> confdefs.h
DEFS="$DEFS -DHAVE_MMAP=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_MMAP\${ac_dB}HAVE_MMAP\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_MMAP\${ac_uB}HAVE_MMAP\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_MMAP\${ac_eB}HAVE_MMAP\${ac_eC}1\${ac_eD}
"
}


fi
rm -fr conftest*

test -n "$silent" || echo "checking for sys_errlist declaration in errno.h"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
int main() { return 0; }
int t() { char *msg = *(sys_errlist + sys_nerr - 1);; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_SYS_ERRLIST"
echo "#define" HAVE_SYS_ERRLIST "1" >> confdefs.h
DEFS="$DEFS -DHAVE_SYS_ERRLIST=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_SYS_ERRLIST\${ac_dB}HAVE_SYS_ERRLIST\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_SYS_ERRLIST\${ac_uB}HAVE_SYS_ERRLIST\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_SYS_ERRLIST\${ac_eB}HAVE_SYS_ERRLIST\${ac_eC}1\${ac_eD}
"
}

{
test -n "$verbose" && \
echo " defining SYS_ERRLIST_DECLARED"
echo "#define" SYS_ERRLIST_DECLARED "1" >> confdefs.h
DEFS="$DEFS -DSYS_ERRLIST_DECLARED=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}SYS_ERRLIST_DECLARED\${ac_dB}SYS_ERRLIST_DECLARED\${ac_dC}1\${ac_dD}
\${ac_uA}SYS_ERRLIST_DECLARED\${ac_uB}SYS_ERRLIST_DECLARED\${ac_uC}1\${ac_uD}
\${ac_eA}SYS_ERRLIST_DECLARED\${ac_eB}SYS_ERRLIST_DECLARED\${ac_eC}1\${ac_eD}
"
}


else
rm -rf conftest*
test -n "$silent" || echo "checking for sys_errlist"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
int main() { return 0; }
int t() {
extern char *sys_errlist[];
extern int sys_nerr;
char *p;
p = sys_errlist[sys_nerr - 1];
; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_SYS_ERRLIST"
echo "#define" HAVE_SYS_ERRLIST "1" >> confdefs.h
DEFS="$DEFS -DHAVE_SYS_ERRLIST=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_SYS_ERRLIST\${ac_dB}HAVE_SYS_ERRLIST\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_SYS_ERRLIST\${ac_uB}HAVE_SYS_ERRLIST\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_SYS_ERRLIST\${ac_eB}HAVE_SYS_ERRLIST\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*

fi
rm -f conftest*

test -n "$silent" || echo "checking for sys_siglist declaration in signal.h or unistd.h"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
#ifdef HAVE_UNISTD_H
# include
#endif

int main() { return 0; }
int t() { char *msg = sys_siglist[1];; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_SYS_SIGLIST"
echo "#define" HAVE_SYS_SIGLIST "1" >> confdefs.h
DEFS="$DEFS -DHAVE_SYS_SIGLIST=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_SYS_SIGLIST\${ac_dB}HAVE_SYS_SIGLIST\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_SYS_SIGLIST\${ac_uB}HAVE_SYS_SIGLIST\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_SYS_SIGLIST\${ac_eB}HAVE_SYS_SIGLIST\${ac_eC}1\${ac_eD}
"
}

{
test -n "$verbose" && \
echo " defining SYS_SIGLIST_DECLARED"
echo "#define" SYS_SIGLIST_DECLARED "1" >> confdefs.h
DEFS="$DEFS -DSYS_SIGLIST_DECLARED=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}SYS_SIGLIST_DECLARED\${ac_dB}SYS_SIGLIST_DECLARED\${ac_dC}1\${ac_dD}
\${ac_uA}SYS_SIGLIST_DECLARED\${ac_uB}SYS_SIGLIST_DECLARED\${ac_uC}1\${ac_uD}
\${ac_eA}SYS_SIGLIST_DECLARED\${ac_eB}SYS_SIGLIST_DECLARED\${ac_eC}1\${ac_eD}
"
}


else
rm -rf conftest*
test -n "$silent" || echo "checking for sys_siglist"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
#ifdef HAVE_UNISTD_H
# include
#endif

int main() { return 0; }
int t() {
extern char *sys_siglist[];
char *p = sys_siglist[2];
; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_SYS_SIGLIST"
echo "#define" HAVE_SYS_SIGLIST "1" >> confdefs.h
DEFS="$DEFS -DHAVE_SYS_SIGLIST=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_SYS_SIGLIST\${ac_dB}HAVE_SYS_SIGLIST\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_SYS_SIGLIST\${ac_uB}HAVE_SYS_SIGLIST\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_SYS_SIGLIST\${ac_eB}HAVE_SYS_SIGLIST\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*

fi
rm -f conftest*

test -n "$silent" || echo "checking for time() declaration in time.h"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
int main() { return 0; }
int t() { time_t (*f)() = time;; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining TIME_DECLARED"
echo "#define" TIME_DECLARED "1" >> confdefs.h
DEFS="$DEFS -DTIME_DECLARED=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}TIME_DECLARED\${ac_dB}TIME_DECLARED\${ac_dC}1\${ac_dD}
\${ac_uA}TIME_DECLARED\${ac_uB}TIME_DECLARED\${ac_uC}1\${ac_uD}
\${ac_eA}TIME_DECLARED\${ac_eB}TIME_DECLARED\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*

test -n "$silent" || echo "checking for broken/missing times()"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
#include

main()
{
extern clock_t times();
struct tms tms;
times(&tms);
sleep(1);
if (times(&tms) == 0)
exit(1);
exit(0);
}

EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then
:
else

{
test -n "$verbose" && \
echo " defining TIMES_BROKEN"
echo "#define" TIMES_BROKEN "1" >> confdefs.h
DEFS="$DEFS -DTIMES_BROKEN=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}TIMES_BROKEN\${ac_dB}TIMES_BROKEN\${ac_dC}1\${ac_dD}
\${ac_uA}TIMES_BROKEN\${ac_uB}TIMES_BROKEN\${ac_uC}1\${ac_uD}
\${ac_eA}TIMES_BROKEN\${ac_eB}TIMES_BROKEN\${ac_eC}1\${ac_eD}
"
}
for ac_func in getrusage
do
ac_tr_func=HAVE_`echo $ac_func | tr '[a-z]' '[A-Z]'`
test -n "$silent" || echo "checking for ${ac_func}"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
int main() { return 0; }
int t() {
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined (__stub_${ac_func}) || defined (__stub___${ac_func})
choke me
#else
/* Override any gcc2 internal prototype to avoid an error. */
extern char ${ac_func}(); ${ac_func}();
#endif
; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*
{
test -n "$verbose" && \
echo " defining ${ac_tr_func}"
echo "#define" ${ac_tr_func} "1" >> confdefs.h
DEFS="$DEFS -D${ac_tr_func}=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}${ac_tr_func}\${ac_dB}${ac_tr_func}\${ac_dC}1\${ac_dD}
\${ac_uA}${ac_tr_func}\${ac_uB}${ac_tr_func}\${ac_uC}1\${ac_uD}
\${ac_eA}${ac_tr_func}\${ac_eB}${ac_tr_func}\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*
done

fi
rm -fr conftest*
test -n "$silent" || echo "checking for broken stat file mode macros"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include
#ifdef S_ISBLK
#if S_ISBLK (S_IFDIR)
You lose.
#endif
#ifdef S_IFCHR
#if S_ISBLK (S_IFCHR)
You lose.
#endif
#endif /* S_IFCHR */
#endif /* S_ISBLK */
#ifdef S_ISLNK
#if S_ISLNK (S_IFREG)
You lose.
#endif
#endif /* S_ISLNK */
#ifdef S_ISSOCK
#if S_ISSOCK (S_IFREG)
You lose.
#endif
#endif /* S_ISSOCK */

EOF
eval "$ac_cpp conftest.${ac_ext} > conftest.out 2>&1"
if egrep "You lose" conftest.out >/dev/null 2>&1; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining STAT_MACROS_BROKEN"
echo "#define" STAT_MACROS_BROKEN "1" >> confdefs.h
DEFS="$DEFS -DSTAT_MACROS_BROKEN=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}STAT_MACROS_BROKEN\${ac_dB}STAT_MACROS_BROKEN\${ac_dC}1\${ac_dD}
\${ac_uA}STAT_MACROS_BROKEN\${ac_uB}STAT_MACROS_BROKEN\${ac_uC}1\${ac_uD}
\${ac_eA}STAT_MACROS_BROKEN\${ac_eB}STAT_MACROS_BROKEN\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*

test -n "$silent" || echo "checking for st_rdev in struct stat"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include
int main() { return 0; }
int t() { struct stat s; s.st_rdev;; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_ST_RDEV"
echo "#define" HAVE_ST_RDEV "1" >> confdefs.h
DEFS="$DEFS -DHAVE_ST_RDEV=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_ST_RDEV\${ac_dB}HAVE_ST_RDEV\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_ST_RDEV\${ac_uB}HAVE_ST_RDEV\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_ST_RDEV\${ac_eB}HAVE_ST_RDEV\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*

ac_prog='/* Ultrix mips cc rejects this. */
typedef int charset[2]; const charset x;
/* SunOS 4.1.1 cc rejects this. */
char const *const *ccp;
char **p;
/* AIX XL C 1.02.0.0 rejects this.
It does not let you subtract one const X* pointer from another in an arm
of an if-expression whose if-part is not a constant expression */
const char *g = "string";
ccp = &g + (g ? g-g : 0);
/* HPUX 7.0 cc rejects these. */
++ccp;
p = (char**) ccp;
ccp = (char const *const *) p;
{ /* SCO 3.2v4 cc rejects this. */
char *t;
char const *s = 0 ? (char *) 0 : (char const *) 0;

*t++ = 0;
}
{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
int x[] = {25,17};
const int *foo = &x[0];
++foo;
}
{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
typedef const int *iptr;
iptr p = 0;
++p;
}
{ /* AIX XL C 1.02.0.0 rejects this saying
"k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
struct s { int j; const int *ap[3]; };
struct s *b; b->j = 5;
}
{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
const int foo = 10;
}'
test -n "$silent" || echo "checking for lack of working const"
cat > conftest.${ac_ext} < #include "confdefs.h"

int main() { return 0; }
int t() { $ac_prog; return 0; }
EOF
if eval $ac_compile; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" const to be empty
echo "#define" const "" >> confdefs.h
DEFS="$DEFS -Dconst="
ac_sed_defs="${ac_sed_defs}\${ac_dA}const\${ac_dB}const\${ac_dC}\${ac_dD}
\${ac_uA}const\${ac_uB}const\${ac_uC}\${ac_uD}
\${ac_eA}const\${ac_eB}const\${ac_eC}\${ac_eD}
"
}

fi
rm -f conftest*

test -n "$silent" || echo "checking for lack of working void"
cat > conftest.${ac_ext} < #include "confdefs.h"

void foo() { }
/* Some compilers (old pcc ones) like "void *a;", but a can't be used */
void *bar(a) void *a; { int *b = (int *) a; *b = 1; }

int main() { return 0; }
int t() { ; return 0; }
EOF
if eval $ac_compile; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" void to be "char"
echo "#define" void "char" >> confdefs.h
DEFS="$DEFS -Dvoid=char"
ac_sed_defs="${ac_sed_defs}\${ac_dA}void\${ac_dB}void\${ac_dC}char\${ac_dD}
\${ac_uA}void\${ac_uB}void\${ac_uC}char\${ac_uD}
\${ac_eA}void\${ac_eB}void\${ac_eC}char\${ac_eD}
"
}

fi
rm -f conftest*

test -n "$silent" || echo "checking for lack of working volatile"
cat > conftest.${ac_ext} < #include "confdefs.h"
int x, y, z;
int main() { return 0; }
int t() { volatile int a; int * volatile b = x ? &y : &z;
/* Older MIPS compilers (eg., in Ultrix 4.2) don't like *b = 0 */
*b = 0;; return 0; }
EOF
if eval $ac_compile; then
:
else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining" volatile to be empty
echo "#define" volatile "" >> confdefs.h
DEFS="$DEFS -Dvolatile="
ac_sed_defs="${ac_sed_defs}\${ac_dA}volatile\${ac_dB}volatile\${ac_dC}\${ac_dD}
\${ac_uA}volatile\${ac_uB}volatile\${ac_uC}\${ac_uD}
\${ac_eA}volatile\${ac_eB}volatile\${ac_eC}\${ac_eD}
"
}

fi
rm -f conftest*

test -n "$silent" || echo "checking for working function prototypes"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
void foo(char *fmt, ...);
int bar(int a, char b, char *c);
int bar(a, b, c) int a; char b; char *c;
{ foo("%d%c%s\n", a, b, c); return a + b + *c; }
void foo(char *fmt, ...) { va_list a; va_start(a, fmt); va_end(a); }

int main() { return 0; }
int t() { ; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_PROTOTYPES"
echo "#define" HAVE_PROTOTYPES "1" >> confdefs.h
DEFS="$DEFS -DHAVE_PROTOTYPES=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_PROTOTYPES\${ac_dB}HAVE_PROTOTYPES\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_PROTOTYPES\${ac_uB}HAVE_PROTOTYPES\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_PROTOTYPES\${ac_eB}HAVE_PROTOTYPES\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*

test -n "$silent" || echo "checking whether \`#!' works in shell scripts"
echo '#!/bin/cat
exit 69
' > conftest
chmod u+x conftest
(SHELL=/bin/sh; export SHELL; ./conftest > /dev/null)
if test $? -ne 69; then
:; test -n "$verbose" && echo " it does"
else
:; test -n "$verbose" && echo " it doesn't"
{
test -n "$verbose" && \
echo " defining SHARPBANG"
echo "#define" SHARPBANG "1" >> confdefs.h
DEFS="$DEFS -DSHARPBANG=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}SHARPBANG\${ac_dB}SHARPBANG\${ac_dC}1\${ac_dD}
\${ac_uA}SHARPBANG\${ac_uB}SHARPBANG\${ac_uC}1\${ac_uD}
\${ac_eA}SHARPBANG\${ac_eB}SHARPBANG\${ac_eC}1\${ac_eD}
"
}

fi
rm -f conftest
# Make sure to not get the incompatible SysV /etc/install and
# /usr/sbin/install, which might be in PATH before a BSD-like install,
# or the SunOS /usr/etc/install directory, or the AIX /bin/install,
# or the AFS install, which mishandles nonexistent args, or
# /usr/ucb/install on SVR4, which tries to use the nonexistent group
# `staff', or /sbin/install on IRIX which has incompatible command-line
# syntax. Sigh.
#
# On most BSDish systems install is in /usr/bin, not /usr/ucb
# anyway.
# This turns out not to be true, so the mere pathname isn't an indication
# of whether the program works. What we really need is a set of tests for
# the install program to see if it actually works in all the required ways.
#
# Avoid using ./install, which might have been erroneously created
# by make from ./install.sh.
if test -z "${INSTALL}"; then
test -n "$silent" || echo "checking for a BSD compatible install"
IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
for ac_dir in $PATH; do
case "$ac_dir" in
''|.|/etc|/sbin|/usr/sbin|/usr/etc|/usr/afsws/bin|/usr/ucb) ;;
*)
# OSF1 and SCO ODT 3.0 have their own names for install.
for ac_prog in installbsd scoinst install; do
if test -f $ac_dir/$ac_prog; then
if test $ac_prog = install &&
grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
# AIX install. It has an incompatible calling convention.
# OSF/1 installbsd also uses dspmsg, but is usable.
:
else
INSTALL="$ac_dir/$ac_prog -c"
break 2
fi
fi
done
;;
esac
done
IFS="$ac_save_ifs"
fi

if test -z "$INSTALL"; then
# As a last resort, use the slow shell script.
for ac_dir in ${srcdir} ${srcdir}/.. ${srcdir}/../..; do
if test -f $ac_dir/install.sh; then
INSTALL="$ac_dir/install.sh -c"; break
fi
done
fi
if test -z "$INSTALL"; then
echo "configure: can not find install.sh in ${srcdir} or ${srcdir}/.. or ${srcdir}/../.." >&2; exit 1
fi
test -n "$verbose" && echo " setting INSTALL to $INSTALL"

# Use test -z because SunOS4 sh mishandles ${INSTALL_PROGRAM-'${INSTALL}'}.
# It thinks the first close brace ends the variable substitution.
test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
test -n "$verbose" && echo " setting INSTALL_PROGRAM to $INSTALL_PROGRAM"

test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
test -n "$verbose" && echo " setting INSTALL_DATA to $INSTALL_DATA"

test -n "$silent" || echo "checking if dup2() fails to reset the close-on-exec flag"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
#ifdef HAVE_FCNTL_H
# include
#endif /* HAVE_FCNTL_H */

#ifndef F_GETFD
# define F_GETFD 1
#endif
#ifndef F_SETFD
# define F_SETFD 2
#endif
#ifndef O_RDONLY
# define O_RDONLY 0
#endif

/* On some systems (Ultrix 2.1..4.2 (and more?)), dup2() does not clear
the close on exec flag */
main()
{
int fd1, fd2;
fd1 = open("/dev/null", O_RDONLY);
if (fcntl(fd1, F_SETFD, 1) < 0)
exit(1);
fd2 = dup2(fd1, fd1 + 1);
if (fd2 < 0)
exit(2);
exit(fcntl(fd2, F_GETFD, 0) == 0 ? 0 : 3);
}

EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then
:
else

{
test -n "$verbose" && \
echo " defining DUP2_BROKEN"
echo "#define" DUP2_BROKEN "1" >> confdefs.h
DEFS="$DEFS -DDUP2_BROKEN=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}DUP2_BROKEN\${ac_dB}DUP2_BROKEN\${ac_dC}1\${ac_dD}
\${ac_uA}DUP2_BROKEN\${ac_uB}DUP2_BROKEN\${ac_uC}1\${ac_uD}
\${ac_eA}DUP2_BROKEN\${ac_eB}DUP2_BROKEN\${ac_eC}1\${ac_eD}
"
}

fi
rm -fr conftest*
test -n "$silent" || echo "checking for posix signals"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
int main() { return 0; }
int t() {
sigset_t ss;
struct sigaction sa;
sigemptyset(&ss); sigsuspend(&ss);
sigaction(SIGINT, &sa, (struct sigaction *) 0);
sigprocmask(SIG_BLOCK, &ss, (sigset_t *) 0);
; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining POSIX_SIGNALS"
echo "#define" POSIX_SIGNALS "1" >> confdefs.h
DEFS="$DEFS -DPOSIX_SIGNALS=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}POSIX_SIGNALS\${ac_dB}POSIX_SIGNALS\${ac_dC}1\${ac_dD}
\${ac_uA}POSIX_SIGNALS\${ac_uB}POSIX_SIGNALS\${ac_uC}1\${ac_uD}
\${ac_eA}POSIX_SIGNALS\${ac_eB}POSIX_SIGNALS\${ac_eC}1\${ac_eD}
"
}


else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining USE_FAKE_SIGACT"
echo "#define" USE_FAKE_SIGACT "1" >> confdefs.h
DEFS="$DEFS -DUSE_FAKE_SIGACT=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}USE_FAKE_SIGACT\${ac_dB}USE_FAKE_SIGACT\${ac_dC}1\${ac_dD}
\${ac_uA}USE_FAKE_SIGACT\${ac_uB}USE_FAKE_SIGACT\${ac_uC}1\${ac_uD}
\${ac_eA}USE_FAKE_SIGACT\${ac_eB}USE_FAKE_SIGACT\${ac_eC}1\${ac_eD}
"
}
test -n "$silent" || echo "checking for BSD4.2 signals"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
int main() { return 0; }
int t() {
int mask = sigmask(SIGINT);
sigsetmask(mask); sigblock(mask); sigpause(mask);
; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining BSD42_SIGNALS"
echo "#define" BSD42_SIGNALS "1" >> confdefs.h
DEFS="$DEFS -DBSD42_SIGNALS=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}BSD42_SIGNALS\${ac_dB}BSD42_SIGNALS\${ac_dC}1\${ac_dD}
\${ac_uA}BSD42_SIGNALS\${ac_uB}BSD42_SIGNALS\${ac_uC}1\${ac_uD}
\${ac_eA}BSD42_SIGNALS\${ac_eB}BSD42_SIGNALS\${ac_eC}1\${ac_eD}
"
}


else
rm -rf conftest*
test -n "$silent" || echo "checking for BSD4.1 signals"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
RETSIGTYPE foo() { }

int main() { return 0; }
int t() {
int mask = sigmask(SIGINT);
sigset(SIGINT, foo); sigrelse(SIGINT); sighold(SIGINT); sigpause(SIGINT);
; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining BSD41_SIGNALS"
echo "#define" BSD41_SIGNALS "1" >> confdefs.h
DEFS="$DEFS -DBSD41_SIGNALS=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}BSD41_SIGNALS\${ac_dB}BSD41_SIGNALS\${ac_dC}1\${ac_dD}
\${ac_uA}BSD41_SIGNALS\${ac_uB}BSD41_SIGNALS\${ac_uC}1\${ac_uD}
\${ac_eA}BSD41_SIGNALS\${ac_eB}BSD41_SIGNALS\${ac_eC}1\${ac_eD}
"
}


else
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining V7_SIGNALS"
echo "#define" V7_SIGNALS "1" >> confdefs.h
DEFS="$DEFS -DV7_SIGNALS=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}V7_SIGNALS\${ac_dB}V7_SIGNALS\${ac_dC}1\${ac_dD}
\${ac_uA}V7_SIGNALS\${ac_uB}V7_SIGNALS\${ac_uC}1\${ac_uD}
\${ac_eA}V7_SIGNALS\${ac_eB}V7_SIGNALS\${ac_eC}1\${ac_eD}
"
}

fi
rm -f conftest*

fi
rm -f conftest*

fi
rm -f conftest*

test -n "$silent" || echo "checking for POSIX vs BSD vs SYSV setpgrp()/getpgrp()"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
#ifdef HAVE_UNISTD_H
# include
#endif /* HAVE_UNISTD_H */

main()
{
int ecode = 0, child = fork();
if (child < 0)
exit(1);
if (child == 0) {
signal(SIGTERM, SIG_DFL); /* just to make sure */
sleep(10);
exit(9);
}
if (setpgrp(child, child) < 0)
ecode = 2;
else if (getpgrp(child) != child)
ecode = 3;
kill(child, SIGTERM);
exit(ecode);
}

EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then

{
test -n "$verbose" && \
echo " defining BSD_PGRP"
echo "#define" BSD_PGRP "1" >> confdefs.h
DEFS="$DEFS -DBSD_PGRP=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}BSD_PGRP\${ac_dB}BSD_PGRP\${ac_dC}1\${ac_dD}
\${ac_uA}BSD_PGRP\${ac_uB}BSD_PGRP\${ac_uC}1\${ac_uD}
\${ac_eA}BSD_PGRP\${ac_eB}BSD_PGRP\${ac_eC}1\${ac_eD}
"
}


else
cat > conftest.${ac_ext} < #include "confdefs.h"

#ifdef HAVE_UNISTD_H
# include
#endif /* HAVE_UNISTD_H */

main()
{
int child;
int n, p1[2], p2[2];
char buf[1];

if (pipe(p1) < 0 || pipe(p2) < 0)
exit(1);
if ((child = fork()) < 0)
exit(2);
if (child == 0) {
n = read(p1[0], buf, sizeof(buf)); /* wait for parent to setpgid */
buf[0] = (n != 1 ? 10 : (getpgrp() != getpid() ? 11 : 0));
if (write(p2[1], buf, sizeof(buf)) != 1)
exit(12);
exit(0);
}
if (setpgid(child, child) < 0)
exit(3);
if (write(p1[1], buf, 1) != 1)
exit(4);
if (read(p2[0], buf, 1) != 1)
exit(5);
exit((int) buf[0]);
}

EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then

{
test -n "$verbose" && \
echo " defining POSIX_PGRP"
echo "#define" POSIX_PGRP "1" >> confdefs.h
DEFS="$DEFS -DPOSIX_PGRP=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}POSIX_PGRP\${ac_dB}POSIX_PGRP\${ac_dC}1\${ac_dD}
\${ac_uA}POSIX_PGRP\${ac_uB}POSIX_PGRP\${ac_uC}1\${ac_uD}
\${ac_eA}POSIX_PGRP\${ac_eB}POSIX_PGRP\${ac_eC}1\${ac_eD}
"
}


else
cat > conftest.${ac_ext} < #include "confdefs.h"

#ifdef HAVE_UNISTD_H
# include
#endif /* HAVE_UNISTD_H */

main()
{
int child;
int n, p[2];
char buf[1];

if (pipe(p) < 0)
exit(1);
if ((child = fork()) < 0)
exit(2);
if (child == 0) {
buf[0] = (setpgrp() < 0 ? 10 : (getpgrp() != getpid() ? 11 : 0));
if (write(p[1], buf, sizeof(buf)) != 1)
exit(11);
exit(0);
}
if (read(p[0], buf, 1) != 1)
exit(3);
exit((int) buf[0]);
}

EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then

{
test -n "$verbose" && \
echo " defining SYSV_PGRP"
echo "#define" SYSV_PGRP "1" >> confdefs.h
DEFS="$DEFS -DSYSV_PGRP=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}SYSV_PGRP\${ac_dB}SYSV_PGRP\${ac_dC}1\${ac_dD}
\${ac_uA}SYSV_PGRP\${ac_uB}SYSV_PGRP\${ac_uC}1\${ac_uD}
\${ac_eA}SYSV_PGRP\${ac_eB}SYSV_PGRP\${ac_eC}1\${ac_eD}
"
}


fi
rm -fr conftest*
fi
rm -fr conftest*
fi
rm -fr conftest*
test -n "$silent" || echo "checking if process group synchronization is required"
cat > conftest.${ac_ext} < #include "confdefs.h"

main()
{
#if defined(POSIX_PGRP) || defined(BSD_PGRP)
# ifdef POSIX_PGRP
# define getpgID() getpgrp()
# else
# define getpgID() getpgrp(0)
# define setpgid(x,y) setpgrp(x,y)
# endif
int pid1, pid2, fds[2];
int status;
char ok;

switch (pid1 = fork()) {
case -1:
exit(1);
case 0:
setpgid(0, getpid());
exit(0);
}
setpgid(pid1, pid1);

sleep(2); /* let first child die */

if (pipe(fds) < 0)
exit(2);

switch (pid2 = fork()) {
case -1:
exit(3);
case 0:
setpgid(0, pid1);
ok = getpgID() == pid1;
write(fds[1], &ok, 1);
exit(0);
}
setpgid(pid2, pid1);

close(fds[1]);
if (read(fds[0], &ok, 1) != 1)
exit(4);
wait(&status);
wait(&status);
exit(ok ? 0 : 5);
#else /* POSIX_PGRP || BSD_PGRP */
exit(20);
#endif /* POSIX_PGRP || BSD_PGRP */
}

EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then
:
else

{
test -n "$verbose" && \
echo " defining NEED_PGRP_SYNC"
echo "#define" NEED_PGRP_SYNC "1" >> confdefs.h
DEFS="$DEFS -DNEED_PGRP_SYNC=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}NEED_PGRP_SYNC\${ac_dB}NEED_PGRP_SYNC\${ac_dC}1\${ac_dD}
\${ac_uA}NEED_PGRP_SYNC\${ac_uB}NEED_PGRP_SYNC\${ac_uC}1\${ac_uD}
\${ac_eA}NEED_PGRP_SYNC\${ac_eB}NEED_PGRP_SYNC\${ac_eC}1\${ac_eD}
"
}

fi
rm -fr conftest*
test -n "$silent" || echo "checking if C compiler groks __attribute__(( .. ))"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
void test_fmt(char *fmt, ...) __attribute__((format(printf, 1, 2)));
void test_fmt(char *fmt, ...) { return; }
void test_cnst(int) __attribute__((const));
void test_cnst(int x) { return x + 1; }
void test_nr() __attribute__((noreturn));
void test_nr() { exit(1); }
void test_uk() __attribute__((blah));
void test_uk() { return; }

int main() { return 0; }
int t() { test_nr("%d", 10); test_cnst(2); test_uk(); test_nr();; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_GCC_FUNC_ATTR"
echo "#define" HAVE_GCC_FUNC_ATTR "1" >> confdefs.h
DEFS="$DEFS -DHAVE_GCC_FUNC_ATTR=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_GCC_FUNC_ATTR\${ac_dB}HAVE_GCC_FUNC_ATTR\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_GCC_FUNC_ATTR\${ac_uB}HAVE_GCC_FUNC_ATTR\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_GCC_FUNC_ATTR\${ac_eB}HAVE_GCC_FUNC_ATTR\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*

test -n "$silent" || echo "checking if opendir() opens non-directories"
cat > conftest.${ac_ext} < #include "confdefs.h"

#include
#include
#ifdef HAVE_UNISTD_H
# include
#endif /* HAVE_UNISTD_H */
#if defined(DIRENT) || defined(_POSIX_VERSION)
# include
#else
# define dirent direct
# ifdef SYSNDIR
# include
# endif /* SYSNDIR */
# ifdef SYSDIR
# include
# endif /* SYSDIR */
# ifdef NDIR
# include
# endif /* NDIR */
#endif /* DIRENT || _POSIX_VERSION */

main()
{
int i, ret = 0;
FILE *fp;
char *fname = "conftestod", buf[256];

for (i = 0; i < sizeof(buf); i++) /* memset(buf, 0, sizeof(buf)) */
buf[i] = 0;
unlink(fname); /* paranoia */
i = ((fp = fopen(fname, "w")) == (FILE *) 0 && (ret = 1))
|| (fwrite(buf, sizeof(buf), 1, fp) != 1 && (ret = 2))
|| (fclose(fp) == EOF && (ret = 3))
|| (opendir(fname) && (ret = 4))
|| (opendir("/dev/null") && (ret = 5));

unlink(fname);
exit(ret);
}

EOF
eval $ac_compile
if test -s conftest && (./conftest; exit) 2>/dev/null; then
:
else

{
test -n "$verbose" && \
echo " defining OPENDIR_DOES_NONDIR"
echo "#define" OPENDIR_DOES_NONDIR "1" >> confdefs.h
DEFS="$DEFS -DOPENDIR_DOES_NONDIR=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}OPENDIR_DOES_NONDIR\${ac_dB}OPENDIR_DOES_NONDIR\${ac_dC}1\${ac_dD}
\${ac_uA}OPENDIR_DOES_NONDIR\${ac_uB}OPENDIR_DOES_NONDIR\${ac_uC}1\${ac_uD}
\${ac_eA}OPENDIR_DOES_NONDIR\${ac_eB}OPENDIR_DOES_NONDIR\${ac_eC}1\${ac_eD}
"
}

fi
rm -fr conftest*
test -n "$silent" || echo "checking for union wait"
cat > conftest.${ac_ext} < #include "confdefs.h"
#include
#include
int main() { return 0; }
int t() { union wait status; int pid; pid = wait (&status);
#ifdef WEXITSTATUS
/* Some POSIXoid systems have both the new-style macros and the old
union wait type, and they do not work together. If union wait
conflicts with WEXITSTATUS et al, we don't want to use it at all. */
if (WEXITSTATUS (status) != 0) pid = -1;
#endif
#ifdef HAVE_WAITPID
/* Make sure union wait works with waitpid. */
pid = waitpid (-1, &status, 0);
#endif
; return 0; }
EOF
if eval $ac_compile; then
rm -rf conftest*

{
test -n "$verbose" && \
echo " defining HAVE_UNION_WAIT"
echo "#define" HAVE_UNION_WAIT "1" >> confdefs.h
DEFS="$DEFS -DHAVE_UNION_WAIT=1"
ac_sed_defs="${ac_sed_defs}\${ac_dA}HAVE_UNION_WAIT\${ac_dB}HAVE_UNION_WAIT\${ac_dC}1\${ac_dD}
\${ac_uA}HAVE_UNION_WAIT\${ac_uB}HAVE_UNION_WAIT\${ac_uC}1\${ac_uD}
\${ac_eA}HAVE_UNION_WAIT\${ac_eB}HAVE_UNION_WAIT\${ac_eC}1\${ac_eD}
"
}


fi
rm -f conftest*


# The preferred way to propogate these variables is regular @ substitutions.
if test -n "$prefix"; then
ac_prsub="s%^prefix\\([ ]*\\)=\\([ ]*\\).*$%prefix\\1=\\2$prefix%"
else
prefix=/usr/local
fi
if test -n "$exec_prefix"; then
ac_prsub="$ac_prsub
s%^exec_prefix\\([ ]*\\)=\\([ ]*\\).*$%exec_prefix\\1=\\2$exec_prefix%"
else
exec_prefix='${prefix}' # Let make expand it.
fi

# Any assignment to VPATH causes Sun make to only execute
# the first set of double-colon rules, so remove it if not needed.
# If there is a colon in the path, we need to keep it.
if test "x$srcdir" = x.; then
ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
fi

# Quote sed substitution magic chars in DEFS.
cat >conftest.def < $DEFS
EOF
ac_escape_ampersand_and_backslash='s%[&\\]%\\&%g'
DEFS=`sed "$ac_escape_ampersand_and_backslash" rm -f conftest.def
# Substitute for predefined variables.

trap 'rm -f config.status; exit 1' 1 2 15
echo creating config.status
rm -f config.status
cat > config.status < #!ksh
# Generated automatically by configure.
# Run this file to recreate the current configuration.
# This directory was configured as follows,
# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
#
# $0 $configure_args

ac_cs_usage="Usage: config.status [--recheck] [--version] [--help]"
for ac_option
do
case "\$ac_option" in
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
echo running \${CONFIG_SHELL-/bin/sh} $0 $configure_args --no-create
exec \${CONFIG_SHELL-/bin/sh} $0 $configure_args --no-create ;;
-version | --version | --versio | --versi | --vers | --ver | --ve | --v)
echo "config.status generated by autoconf version 1.11"
exit 0 ;;
-help | --help | --hel | --he | --h)
echo "\$ac_cs_usage"; exit 0 ;;
*) echo "\$ac_cs_usage"; exit 1 ;;
esac
done

trap 'rm -fr Makefile config.h conftest*; exit 1' 1 2 15
LDSTATIC='$LDSTATIC'
CC='$CC'
CPP='$CPP'
GCC_WARNFLAGS='$GCC_WARNFLAGS'
INSTALL='$INSTALL'
INSTALL_PROGRAM='$INSTALL_PROGRAM'
INSTALL_DATA='$INSTALL_DATA'
LIBS='$LIBS'
srcdir='$srcdir'
top_srcdir='$top_srcdir'
prefix='$prefix'
exec_prefix='$exec_prefix'
ac_prsub='$ac_prsub'
ac_vpsub='$ac_vpsub'
extrasub='$extrasub'
EOF
cat >> config.status <
ac_given_srcdir=$srcdir

CONFIG_FILES=${CONFIG_FILES-"Makefile"}
for ac_file in .. ${CONFIG_FILES}; do if test "x$ac_file" != x..; then
# Remove last slash and all that follows it. Not all systems have dirname.
ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
# The file is in a subdirectory.
test ! -d "$ac_dir" && mkdir "$ac_dir"
ac_dir_suffix="/$ac_dir"
else
ac_dir_suffix=
fi

# A "../" for each directory in $ac_dir_suffix.
ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
case "$ac_given_srcdir" in
.) srcdir=.
if test -z "$ac_dir_suffix"; then top_srcdir=.
else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
/*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
*) # Relative path.
srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
top_srcdir="$ac_dots$ac_given_srcdir" ;;
esac

echo creating "$ac_file"
rm -f "$ac_file"
comment_str="Generated automatically from `echo $ac_file|sed 's|.*/||'`.in by configure."
case "$ac_file" in
*.c | *.h | *.C | *.cc | *.m ) echo "/* $comment_str */" > "$ac_file" ;;
* ) echo "# $comment_str" > "$ac_file" ;;
esac
sed -e "
$ac_prsub
$ac_vpsub
$extrasub
s%@LDSTATIC@%$LDSTATIC%g
s%@CC@%$CC%g
s%@CPP@%$CPP%g
s%@GCC_WARNFLAGS@%$GCC_WARNFLAGS%g
s%@INSTALL@%$INSTALL%g
s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
s%@INSTALL_DATA@%$INSTALL_DATA%g
s%@LIBS@%$LIBS%g
s%@srcdir@%$srcdir%g
s%@top_srcdir@%$top_srcdir%g
s%@prefix@%$prefix%g
s%@exec_prefix@%$exec_prefix%g
s%@DEFS@%-DHAVE_CONFIG_H%" $ac_given_srcdir/${ac_file}.in >> $ac_file
fi; done

# These sed commands are put into ac_sed_defs when defining a macro.
# They are broken into pieces to make the sed script easier to manage.
# They are passed to sed as "A NAME B NAME C VALUE D", where NAME
# is the cpp macro being defined and VALUE is the value it is being given.
# Each defining turns into a single global substitution command.
# Hopefully no one uses "!" as a variable value.
# Other candidates for the sed separators, like , and @, do get used.
#
# ac_d sets the value in "#define NAME VALUE" lines.
ac_dA='s!^\([ ]*\)#\([ ]*define[ ][ ]*\)'
ac_dB='\([ ][ ]*\)[^ ]*!\1#\2'
ac_dC='\3'
ac_dD='!g'
# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
ac_uA='s!^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
ac_uB='\([ ]\)!\1#\2define\3'
ac_uC=' '
ac_uD='\4!g'
# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
ac_eA='s!^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
ac_eB='$!\1#\2define\3'
ac_eC=' '
ac_eD='!g'
rm -f conftest.sed
EOF
# Turn off quoting long enough to insert the sed commands.
rm -f conftest.sh
cat > conftest.sh < $ac_sed_defs
EOF

# Break up $ac_sed_defs (now in conftest.sh) because some shells have a limit
# on the size of here documents.

# Maximum number of lines to put in a single here document.
ac_max_sh_lines=9

while :
do
# wc gives bogus results for an empty file on some AIX systems.
ac_lines=`grep -c . conftest.sh`
if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
rm -f conftest.s1 conftest.s2
sed ${ac_max_sh_lines}q conftest.sh > conftest.s1 # Like head -9.
sed 1,${ac_max_sh_lines}d conftest.sh > conftest.s2 # Like tail +10.
# Write a limited-size here document to append to conftest.sed.
echo 'cat >> conftest.sed <> config.status
cat conftest.s1 >> config.status
echo 'CONFEOF' >> config.status
rm -f conftest.s1 conftest.sh
mv conftest.s2 conftest.sh
done
rm -f conftest.sh

# Now back to your regularly scheduled config.status.
cat >> config.status < # This sed command replaces #undef's with comments. This is necessary, for
# example, in the case of _POSIX_SOURCE, which is predefined and required
# on some systems where configure will not decide to define it in
# config.h.
cat >> conftest.sed < s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
CONFEOF
rm -f conftest.h
# Break up the sed commands because old seds have small limits.
ac_max_sed_lines=20

CONFIG_HEADERS=${CONFIG_HEADERS-"config.h"}
for ac_file in .. ${CONFIG_HEADERS}; do if test "x$ac_file" != x..; then
echo creating $ac_file

cp $ac_given_srcdir/$ac_file.in conftest.h1
cp conftest.sed conftest.stm
while :
do
ac_lines=`grep -c . conftest.stm`
if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
rm -f conftest.s1 conftest.s2 conftest.h2
sed ${ac_max_sed_lines}q conftest.stm > conftest.s1 # Like head -20.
sed 1,${ac_max_sed_lines}d conftest.stm > conftest.s2 # Like tail +21.
sed -f conftest.s1 < conftest.h1 > conftest.h2
rm -f conftest.s1 conftest.h1 conftest.stm
mv conftest.h2 conftest.h1
mv conftest.s2 conftest.stm
done
rm -f conftest.stm conftest.h
echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
cat conftest.h1 >> conftest.h
rm -f conftest.h1
if cmp -s $ac_file conftest.h 2>/dev/null; then
# The file exists and we would not be changing it.
echo "$ac_file is unchanged"
rm -f conftest.h
else
rm -f $ac_file
mv conftest.h $ac_file
fi
fi; done
rm -f conftest.sed



exit 0
EOF
mv config.status config.status
# Some shells look in PATH for config.status without the "./".
test -n "$no_create" || ${CONFIG_SHELL-/bin/sh} ./config.status

pdksh-5.1.2/alloc.c100644 0 133 13511 5666653020 12736 0ustar rootlsource/*
* area-based allocation built on malloc/free
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id";
#endif

#include "sh.h"
#ifdef MEM_DEBUG
# undef alloc
# undef aresize
# undef afree
#endif /* MEM_DEBUG */

#define ICELLS 100 /* number of Cells in small Block */

typedef union Cell Cell;
typedef struct Block Block;

/*
* The Cells in a Block are organized as a set of objects.
* Each object (pointed to by dp) begins with a size in (dp-1)->size,
* followed with "size" data Cells. Free objects are
* linked together via dp->next.
*/

union Cell {
size_t size;
Cell *next;
struct {int _;} junk; /* alignment */
};

struct Block {
Block *next; /* list of Blocks in Area */
Cell *freelist; /* object free list */
Cell *last; /* &b.cell[size] */
Cell cell [1]; /* [size] Cells for allocation */
};

Block aempty = {&aempty, aempty.cell, aempty.cell};

/* create empty Area */
Area *
ainit(ap)
register Area *ap;
{
ap->freelist = &aempty;
return ap;
}

/* free all object in Area */
void
afreeall(ap)
register Area *ap;
{
register Block *bp;
register Block *tmp;

bp = ap->freelist;
if (bp != NULL && bp != &aempty) {
do {
tmp = bp->next;
free((void*)bp);
bp = tmp;
} while (bp != ap->freelist);
ap->freelist = &aempty;
}
}

/* allocate object from Area */
void *
alloc(size, ap)
size_t size;
register Area *ap;
{
int cells, split;
register Block *bp;
register Cell *dp, *fp, *fpp;

if (size <= 0) {
aerror(ap, "allocate bad size");
return NULL;
}
cells = (unsigned)(size - 1) / sizeof(Cell) + 1;

/* find Cell large enough */
for (bp = ap->freelist; ; bp = bp->next) {
for (fpp = NULL, fp = bp->freelist;
fp != bp->last; fpp = fp, fp = fpp->next)
if ((fp-1)->size >= cells)
goto Found;

/* wrapped around Block list, create new Block */
if (bp->next == ap->freelist) {
bp = (Block*) malloc(offsetof(Block, cell[ICELLS + cells]));
if (bp == NULL) {
aerror(ap, "cannot allocate");
return NULL;
}
if (ap->freelist == &aempty)
bp->next = bp;
else {
bp->next = ap->freelist->next;
ap->freelist->next = bp;
}
bp->last = bp->cell + ICELLS + cells;
fp = bp->freelist = bp->cell + 1; /* initial free list */
(fp-1)->size = ICELLS + cells - 1;
fp->next = bp->last;
fpp = NULL;
break;
}
}
Found:
ap->freelist = bp;
dp = fp; /* allocated object */
split = (dp-1)->size - cells;
if (split < 0)
aerror(ap, "allocated object too small");
if (--split <= 0) { /* allocate all */
fp = fp->next;
} else { /* allocate head, free tail */
(fp-1)->size = cells;
fp += cells + 1;
(fp-1)->size = split;
fp->next = dp->next;
}
if (fpp == NULL)
bp->freelist = fp;
else
fpp->next = fp;
return (void*) dp;
}

/* change size of object -- like realloc */
void *
aresize(ptr, size, ap)
register void *ptr;
size_t size;
Area *ap;
{
int cells;
register Cell *dp = (Cell*) ptr;

if (size <= 0) {
aerror(ap, "allocate bad size");
return NULL;
}
cells = (unsigned)(size - 1) / sizeof(Cell) + 1;

if (dp == NULL || (dp-1)->size < cells) { /* enlarge object */
/* XXX check for available adjacent free block */
ptr = alloc(size, ap);
if (dp != NULL) {
memcpy(ptr, dp, (dp-1)->size * sizeof(Cell));
afree((void *) dp, ap);
}
} else { /* shrink object */
int split;

split = (dp-1)->size - cells;
if (--split <= 0) /* cannot split */
;
else { /* shrink head, free tail */
(dp-1)->size = cells;
dp += cells + 1;
(dp-1)->size = split;
afree((void*)dp, ap);
}
}
return (void*) ptr;
}

void
afree(ptr, ap)
void *ptr;
register Area *ap;
{
register Block *bp;
register Cell *fp, *fpp;
register Cell *dp = (Cell*)ptr;

/* find Block containing Cell */
for (bp = ap->freelist; ; bp = bp->next) {
if (bp->cell <= dp && dp < bp->last)
break;
if (bp->next == ap->freelist) {
aerror(ap, "freeing with invalid area");
return;
}
}

/* find position in free list */
for (fpp = NULL, fp = bp->freelist; fp < dp; fpp = fp, fp = fpp->next)
;

if (fp == dp) {
aerror(ap, "freeing free object");
return;
}

/* join object with next */
if (dp + (dp-1)->size == fp-1) { /* adjacent */
(dp-1)->size += (fp-1)->size + 1;
dp->next = fp->next;
} else /* non-adjacent */
dp->next = fp;

/* join previous with object */
if (fpp == NULL)
bp->freelist = dp;
else if (fpp + (fpp-1)->size == dp-1) { /* adjacent */
(fpp-1)->size += (dp-1)->size + 1;
fpp->next = dp->next;
} else /* non-adjacent */
fpp->next = dp;
}

#if DEBUG_ALLOC
void
aprint(ap, ptr, size)
register Area *ap;
void *ptr;
size_t size;
{
Block *bp;

if (!ap)
shellf("aprint: null area pointer\n");
else if (!(bp = ap->freelist))
shellf("aprint: null area freelist\n");
else if (bp == &aempty)
shellf("aprint: area is empty\n");
else {
int i;
Cell *fp;

for (i = 0; !i || bp != ap->freelist; bp = bp->next, i++) {
if (ptr) {
void *eptr = (void *) (((char *) ptr) + size);
/* print block only if it overlaps ptr/size */
if (!((ptr >= (void *) bp
&& ptr <= (void *) bp->last)
|| (eptr >= (void *) bp
&& eptr <= (void *) bp->last)))
continue;
shellf("aprint: overlap of 0x%p .. 0x%p\n",
ptr, eptr);
}
shellf("aprint: block %2d: 0x%p .. 0x%p (%d)\n", i,
bp->cell, bp->last,
(char *) bp->last - (char *) bp->cell);
for (fp = bp->freelist; fp != bp->last; fp = fp->next)
shellf(
"aprint: 0x%p .. 0x%p (%d) free\n",
(fp-1), (fp-1) + (fp-1)->size,
(fp-1)->size * sizeof(Cell));
}
}
}
#endif /* DEBUG_ALLOC */


#ifdef TEST_ALLOC

Area a;

main(int argc, char **argv) {
int i;
char *p [9];

ainit(&a);
for (i = 0; i < 9; i++) {
p[i] = alloc(124, &a);
printf("alloc: %x\n", p[i]);
}
for (i = 1; i < argc; i++)
afree(p[atoi(argv[i])], &a);
afreeall(&a);
return 0;
}

void aerror(Area *ap, const char *msg) {
abort();
}

#endif

pdksh-5.1.2/c_ksh.c100644 0 133 63705 5667105673 12754 0ustar rootlsource/*
* built-in Korn commands: c_*
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: c_ksh.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include

int
c_cd(wp)
char **wp;
{
int rval;
int cdnode; /* was a node from cdpath added in? */
int printpath = 0; /* print where we cd'd? */
char *cp, *cdpath, *pwd, *oldpwd;
struct tbl *pwd_s, *oldpwd_s;
char path[PATH];
extern int make_path();
extern void simplify_path();

if (Flag(FRESTRICTED)) {
bi_errorf("restricted shell - can't cd");
return 1;
}
pwd = strval(pwd_s = global("PWD"));
oldpwd = strval(oldpwd_s = global("OLDPWD"));

if ((cp = wp[1]) == (char *) 0) {
if ((cp = strval(global("HOME"))) == null) {
bi_errorf("no home directory");
return 1;
}
} else if (wp[2] == (char *) 0) {
if (strcmp(cp, "-") == 0) {
if ((cp = oldpwd) == null) {
bi_errorf("no OLDPWD");
return 1;
}
printpath++;
}
} else if (wp[3] == (char *) 0) {
int ilen, olen, nlen;

/* substitue arg1 for arg2 in current path.
* if the first substitution fails because the cd fails
* we could try to find another substitution. For now
* we don't
*/
if ((cp = strstr(pwd, wp[1])) == (char *) 0) {
bi_errorf("bad substitution");
return 1;
}
ilen = cp - pwd;
olen = strlen(wp[1]);
nlen = strlen(wp[2]);
if (ilen + nlen + strlen(pwd + ilen + olen) + 1 > sizeof(path))
{
bi_errorf("path too long");
return 1;
}
strncpy(path, pwd, ilen);
strcpy(path + ilen, wp[2]);
strcpy(path + ilen + nlen, pwd + ilen + olen);
cp = path;
printpath++;
/* XXX: make_path() copies cp to path, in this case cp to cp. */
} else {
bi_errorf("too many arguments");
return 1;
}

cdpath = strval(global("CDPATH"));
do {
cdnode = make_path(pwd, cp, &cdpath, path, sizeof(path));
simplify_path(path);
rval = chdir(path);
} while (rval < 0 && cdpath != (char *) 0);

if (rval < 0) {
bi_errorf("%s: bad directory", cp);
return 1;
}

flushcom(0);

if (printpath || cdnode)
shprintf("%s\n", path);

#ifdef OS2
/* OS2 is like MSDOS in that paths can be relative to a drive
* specification, hence, the above code doesn't always calculate
* the current directory correctly. Workaround is to let getcwd()
* figure it out.
*/
getcwd(path, sizeof(path));
#endif /* OS2 */

setstr(oldpwd_s, pwd);
setstr(pwd_s, path);

return 0;
}

int
c_print(wp)
register char **wp;
{
#define PO_NL BIT(0) /* print newline */
#define PO_EXPAND BIT(1) /* expand backslash sequences */
#define PO_ECHO BIT(2) /* act (kind of) like BSD echo */
#define PO_HIST BIT(3) /* print to history instead of stdout */
#define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */
int fd = 1;
int flags = PO_EXPAND|PO_NL;
char *s;
char *emsg;
XString xs;
char *xp;

if (wp[0][0] == 'e') { /* echo command */
int nflags = flags;

/* A compromise between sysV and BSD echo commands:
* escape sequences are enabled by default, and
* -n, -e and -E are recognized if they appear
* in arguments with no illegal options (ie, echo -nq
* will print -nq).
* Different from sysV echo since options are recognized,
* different from BSD echo since escape sequences are enabled
* by default.
*/
wp += 1;
while ((s = *wp) && *s == '-') {
while (*++s)
if (*s == 'n')
nflags &= ~PO_NL;
else if (*s == 'e')
nflags |= PO_EXPAND;
else if (*s == 'E')
nflags &= ~PO_EXPAND;
else
/* bad option: don't use nflags, print
* argument
*/
break;
if (*s)
break;
wp++;
flags = nflags;
}
} else {
int optc;
char *options = "Rnprsu,";

while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF)
switch (optc) {
case 'R':
flags |= PO_ECHO; /* fake BSD echo command */
flags &= ~PO_EXPAND;
options = "ne";
break;
case 'e':
flags |= PO_EXPAND;
break;
case 'n':
flags &= ~PO_NL;
break;
case 'p':
if ((fd = get_coproc_fd(W_OK, &emsg)) < 0) {
bi_errorf("-p: %s", emsg);
return 1;
}
break;
case 'r':
flags &= ~PO_EXPAND;
break;
case 's':
flags |= PO_HIST;
break;
case 'u':
if (!*(s = builtin_opt.optarg))
fd = 0;
else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
bi_errorf("-u: %s: %s", s, emsg);
return 1;
}
break;
case '?':
return 1;
}
if (!(builtin_opt.info & GI_MINUSMINUS)) {
/* treat a lone - like -- */
if (wp[builtin_opt.optind]
&& strcmp(wp[builtin_opt.optind], "-") == 0)
builtin_opt.optind++;
} else if (flags & PO_ECHO)
builtin_opt.optind--;
wp += builtin_opt.optind;
}

Xinit(xs, xp, 128, ATEMP);

while (*wp != NULL) {
register int c;
s = *wp;
while ((c = *s++) != '\0') {
Xcheck(xs, xp);
if ((flags & PO_EXPAND) && c == '\\') {
switch ((c = *s++)) {
/* oddly enough, \007 seems more portable than
* \a (due to HP-UX cc, Ultrix cc, old pcc's,
* etc.).
*/
case 'a': c = '\007'; break;
case 'b': c = '\b'; break;
case 'c': flags &= ~PO_NL;
continue; /* AT&T brain damage */
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = 0x0B; break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
c = c - '0';
if (*s >= '0' && *s <= '7')
c = 8*c + *s++ - '0';
if (*s >= '0' && *s <= '7')
c = 8*c + *s++ - '0';
break;
case '\0': s--; c = '\\'; break;
case '\\': break;
default:
Xput(xs, xp, '\\');
}
}
Xput(xs, xp, c);
}
if (*++wp != NULL)
Xput(xs, xp, ' ');
}
if (flags & PO_NL)
Xput(xs, xp, '\n');

if (flags & PO_HIST) {
Xput(xs, xp, '\0');
source->line++;
histsave(source->line, Xstring(xs, xp), 1);
Xfree(xs, xp);
} else {
int n, len = Xlength(xs, xp);
int UNINITIALIZED(opipe);

/* Ensure we aren't killed by a SIGPIPE while writing to
* a coprocess. at&t ksh doesn't seem to do this (seems
* to just check that the co-process is alive, which is
* not enough).
*/
if (coproc.write >= 0 && coproc.write == fd) {
flags |= PO_COPROC;
opipe = block_pipe();
}
for (s = Xstring(xs, xp); len > 0; ) {
n = write(fd, s, len);
if (n < 0) {
if (flags & PO_COPROC)
restore_pipe(opipe);
if (errno == EINTR) {
/* allow user to ^C out */
intrcheck();
if (flags & PO_COPROC)
opipe = block_pipe();
continue;
}
if (errno == EPIPE)
coproc_write_close(fd);
return 1;
}
s += n;
len -= n;
}
if (flags & PO_COPROC)
restore_pipe(opipe);
}

return 0;
}

#ifdef KSH
int
c_whence(wp)
register char **wp;
{
struct tbl *tp;
char *id;
int pflag = 0, vflag = 0, Vflag = 0;
int ret = 0;
int optc;
int iam_whence = wp[0][0] == 'w';
int fcflags;
char *options = iam_whence ? "pv" : "pvV";

while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF)
switch (optc) {
case 'p':
pflag = 1;
break;
case 'v':
vflag = 1;
break;
case 'V':
Vflag = 1;
break;
case '?':
return 1;
}
wp += builtin_opt.optind;


fcflags = FC_BI | FC_PATH | FC_FUNC | FC_NOAUTOLOAD;
if (!iam_whence) {
/* Note that -p on its own is deal with in comexec() */
if (pflag)
fcflags |= FC_DEFPATH;
/* Convert command options to whence options - note that
* command -pV uses a different path search than whence -v
* or whence -pv. This should be considered a feature.
*/
vflag = Vflag;
}
if (pflag)
fcflags &= ~(FC_BI | FC_FUNC);

while ((vflag || ret == 0) && (id = *wp++) != NULL) {
tp = NULL;
if (iam_whence && !pflag)
tp = tsearch(&keywords, id, hash(id));
if (!tp && !pflag) {
tp = tsearch(&aliases, id, hash(id));
if (tp && !(tp->flag & ISSET))
tp = NULL;
}
if (!tp)
tp = findcom(id, fcflags);
if (vflag || (tp->type != CALIAS && tp->type != CEXEC
&& tp->type != CTALIAS))
shprintf("%s", id);
switch (tp->type) {
case CKEYWD:
if (vflag)
shprintf(" is a keyword");
break;
case CALIAS:
if (vflag)
shprintf(" is an %salias for ",
(tp->flag & EXPORT) ? "exported "
: null);
if (!iam_whence && !vflag)
shprintf("alias %s=", id);
print_value_quoted(tp->val.s);
break;
case CFUNC:
if (vflag) {
shprintf(" is a");
if (tp->flag & EXPORT)
shprintf(" exported");
if (tp->flag & TRACE)
shprintf(" traced");
if (!(tp->flag & ISSET))
shprintf(" undefined");
shprintf(" function");
}
break;
case CSHELL:
if (vflag)
shprintf(" is a%s shell builtin",
(tp->flag & SPEC_BI) ? " special" : null);
break;
case CTALIAS:
case CEXEC:
if (tp->flag & ISSET) {
if (vflag) {
shprintf(" is ");
if (tp->type == CTALIAS)
shprintf(
"a tracked %salias for ",
(tp->flag & EXPORT) ?
"exported "
: null);
}
shprintf("%s", tp->val.s);
} else {
if (vflag)
shprintf(" not found");
ret = 1;
}
break;
default:
shprintf("%s is *GOK*", id);
break;
}
if (vflag || !ret)
shprintf(newline);
}
return ret;
}
#endif /* KSH */

/* Deal with command -vV - command -p dealt with in comexec() */
int
c_command(wp)
register char **wp;
{
#ifdef KSH
/* Let c_whence do the work. Note that c_command() must be
* a distinct function from c_whence() (tested in comexec()).
*/
return c_whence(wp);
#else /* KSH */
/* Can't get here: command with no options taken care of in
* comexec().
*/
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
return 0;
#endif /* KSH */
}

/* typeset, export, and readonly */
int
c_typeset(wp)
register char **wp;
{
struct block *l = e->loc;
struct tbl *vp, **p;
int fset = 0, fclr = 0;
int thing = 0, func = 0, local = 0;
char *options = "L#R#UZ#fi#lrtux"; /* see comment below */
char *fieldstr, *basestr;
int field, base;
int optc, flag;
int pflag = 0;

switch (**wp) {
case 'e': /* export */
fset |= EXPORT;
options = "p";
break;
case 'r': /* readonly */
fset |= RDONLY;
options = "p";
break;
case 's': /* set */
/* called with 'typeset -' */
break;
case 't': /* typeset */
local = 1;
break;
}

fieldstr = basestr = (char *) 0;
builtin_opt.flags |= GF_PLUSOPT;
/* at&t ksh seems to have 0-9 as options, which are multiplied
* to get a number that is used with -L, -R, -Z or -i (eg, -1R2
* sets right justify in a field of 12). This allows options
* to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
* does not allow the number to be specified as a seperate argument
* Here, the number must follow the RLZi option, but is optional
* (see the # kludge in ksh_getopt()).
*/
while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF) {
flag = 0;
switch (optc) {
case 'L':
flag |= LJUST;
fieldstr = builtin_opt.optarg;
break;
case 'R':
flag |= RJUST;
fieldstr = builtin_opt.optarg;
break;
case 'U':
/* at&t ksh uses u, but this conflicts with
* upper/lower case. If this option is changed,
* need to change the -U below as well
*/
flag |= INT_U;
break;
case 'Z':
flag |= ZEROFIL;
fieldstr = builtin_opt.optarg;
break;
case 'f':
func = 1;
break;
case 'i':
flag |= INTEGER;
basestr = builtin_opt.optarg;
break;
case 'l':
flag |= LCASEV;
break;
case 'p': /* posix export/readonly -p flag */
pflag = 1;
break;
case 'r':
flag |= RDONLY;
break;
case 't':
flag |= TRACE;
break;
case 'u':
flag |= UCASEV_AL; /* upper case / autoload */
break;
case 'x':
flag |= EXPORT;
break;
case '?':
return 1;
}
if (builtin_opt.info & GI_PLUS) {
fclr |= flag;
fset &= ~flag;
thing = '+';
} else {
fset |= flag;
fclr &= ~flag;
thing = '-';
}
}

field = 0;
if (fieldstr && !bi_getn(fieldstr, &field))
return 1;
base = 0;
if (basestr && !bi_getn(basestr, &base))
return 1;

if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind]
&& (wp[builtin_opt.optind][0] == '-'
|| wp[builtin_opt.optind][0] == '+')
&& wp[builtin_opt.optind][1] == '\0')
{
thing = wp[builtin_opt.optind][0];
builtin_opt.optind++;
}

if (func && ((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT))) {
bi_errorf("only -t, -u and -x options may be used with -f");
return 1;
} else if (wp[builtin_opt.optind]) {
/* Take care of exclusions */
/* setting these attributes clears the others, unless they
* are also set in this command
*/
if (fset & (LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER
|INT_U|INT_L))
fclr |= ~fset &
(LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER
|INT_U|INT_L);
fclr &= ~fset; /* set wins */
if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
fset |= RJUST;
fclr &= ~RJUST;
}
if (fset & LCASEV) /* LCASEV has priority */
fclr |= UCASEV_AL;
else if (fset & UCASEV_AL)
fclr |= LCASEV;
if (fset & LJUST) /* LJUST has priority */
fclr |= RJUST;
else if (fset & RJUST)
fclr |= LJUST;
if ((fset | fclr) & INTEGER) {
if (!(fset | fclr) & INT_U)
fclr |= INT_U;
if (!(fset | fclr) & INT_L)
fclr |= INT_L;
}
fset &= ~fclr; /* in case of something like -LR */
}

/* set variables and attributes */
if (wp[builtin_opt.optind]) {
int i;
struct tbl *f;

if (local && !func)
fset |= LOCAL;
for (i = builtin_opt.optind; wp[i]; i++) {
if (func) {
f = findfunc(wp[i], hash(wp[i]),
(fset&UCASEV_AL) ? TRUE : FALSE);
if (!f)
continue;
if (fset | fclr) {
f->flag |= fset;
f->flag &= ~fclr;
} else
fptreef(shl_stdout, "function %s %T\n",
wp[i], f->val.t);
} else if (!typeset(wp[i], fset, fclr, field, base)) {
bi_errorf("%s: not identifier", wp[i]);
return 1;
}
}
return 0;
}

/* list variables and attributes */
flag = fset | fclr; /* no difference at this point.. */
for (l = e->loc; l; l = l->next) {
for (p = tsort(func ? &l->funs : &l->vars); (vp = *p++); )
for (; vp; vp = vp->array) {
if (!(vp->flag&ISSET))
continue;
/* no arguments */
if (thing == 0 && flag == 0) {
/* at&t ksh prints things like export, integer,
* leftadj, zerofill, etc., but POSIX says must
* be suitable for re-entry...
*/
shprintf("typeset ");
if ((vp->flag&INTEGER))
shprintf("-i ");
if ((vp->flag&EXPORT))
shprintf("-x ");
if ((vp->flag&RDONLY))
shprintf("-r ");
if ((vp->flag&TRACE))
shprintf("-t ");
if ((vp->flag&LJUST))
shprintf("-L%d ", vp->field);
if ((vp->flag&RJUST))
shprintf("-R%d ", vp->field);
if ((vp->flag&ZEROFIL))
shprintf("-Z ");
if ((vp->flag&LCASEV))
shprintf("-l ");
if ((vp->flag&UCASEV_AL))
shprintf("-u ");
if ((vp->flag&INT_U))
shprintf("-U ");
if (vp->flag&ARRAY)
shprintf("%s[%d]\n", vp->name,vp->index);
else
shprintf("%s\n", vp->name);
} else {
if (flag && (vp->flag & flag) == 0)
continue;
if (func) {
if (thing == '-')
fptreef(shl_stdout, "function %s %T\n",
vp->name, vp->val.t);
else
shprintf("%s\n", vp->name);
} else {
if (pflag)
shprintf("%s ",
(flag & EXPORT) ? "export" : "readonly");
if (vp->flag&ARRAY)
shprintf("%s[%d]", vp->name, vp->index);
else
shprintf("%s", vp->name);
if (thing == '-') {
char *s = strval(vp);

shprintf("=");
/* at&t ksh can't have justified integers.. */
if ((vp->flag & (INTEGER|LJUST|RJUST))
== INTEGER)
shprintf("%s", s);
else
print_value_quoted(s);
}
shprintf(newline);
}
}
}
}
return 0;
}

int
c_alias(wp)
register char **wp;
{
struct table *t = &aliases;
int rv = 0, rflag = 0, tflag, Uflag = 0, xflag = 0;
int optc;

while ((optc = ksh_getopt(wp, &builtin_opt, "drtUx")) != EOF)
switch (optc) {
case 'd':
t = &homedirs;
break;
case 'r':
rflag = 1;
break;
case 't':
t = &taliases;
break;
case 'U': /* kludge for tracked alias initialization
* (don't do a path search, just make an entry)
*/
Uflag = 1;
break;
case 'x':
xflag = EXPORT;
break;
case '?':
return 1;
}
wp += builtin_opt.optind;

tflag = t == &taliases;

/* "hash -r" means reset all the tracked aliases.. */
if (rflag) {
static char *args[] = { "unalias", "-ta", (char *) 0 };

if (!tflag || *wp) {
shprintf(
"alias: -r flag can only be used with -t and without arguments\n");
return 1;
}
return c_unalias(args);
}

if (*wp == NULL) {
struct tbl *ap, **p;

for (p = tsort(t); (ap = *p++) != NULL; )
if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
shprintf("%s=", ap->name);
print_value_quoted(ap->val.s);
shprintf(newline);
}
}

for (; *wp != NULL; wp++) {
char *alias = *wp;
char *val = strchr(alias, '=');
char *newval;
struct tbl *ap;
int h;

if (val)
alias = strnsave(alias, val++ - alias, ATEMP);
h = hash(alias);
if (val == NULL && !tflag && !xflag) {
ap = tsearch(t, alias, h);
if (ap != NULL && (ap->flag&ISSET)) {
shprintf("%s=", ap->name);
print_value_quoted(ap->val.s);
shprintf(newline);
} else {
shprintf("%s alias not found\n", alias);
rv = 1;
}
continue;
}
ap = tenter(t, alias, h);
ap->type = tflag ? CTALIAS : CALIAS;
/* Are we setting the value or just some flags? */
if ((val && !tflag) || (!val && tflag && !Uflag)) {
if (ap->flag&ALLOC) {
ap->flag &= ~(ALLOC|ISSET);
afree((void*)ap->val.s, APERM);
}
/* ignore values for -t (at&t ksh does this) */
newval = tflag ? search(alias, path, X_OK) : val;
if (newval) {
ap->val.s = strsave(newval, APERM);
ap->flag |= ALLOC|ISSET;
} else
ap->flag &= ~ISSET;
}
ap->flag |= DEFINED|xflag;
if (val)
afree(alias, ATEMP);
}

return rv;
}

int
c_unalias(wp)
register char **wp;
{
register struct table *t = &aliases;
register struct tbl *ap;
int rv = 0, all = 0;
int optc;

while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != EOF)
switch (optc) {
case 'a':
all = 1;
break;
case 'd':
t = &homedirs;
break;
case 't':
t = &taliases;
break;
case '?':
return 1;
}
wp += builtin_opt.optind;

for (; *wp != NULL; wp++) {
ap = tsearch(t, *wp, hash(*wp));
if (ap == NULL) {
rv = 1; /* POSIX */
continue;
}
if (ap->flag&ALLOC) {
ap->flag &= ~(ALLOC|ISSET);
afree((void*)ap->val.s, APERM);
}
ap->flag &= ~(DEFINED|ISSET|EXPORT);
}

if (all) {
for (twalk(t); (ap = tnext()); ) {
if (ap->flag&ALLOC) {
ap->flag &= ~(ALLOC|ISSET);
afree((void*)ap->val.s, APERM);
}
ap->flag &= ~(DEFINED|ISSET|EXPORT);
}
}

return rv;
}

int
c_let(wp)
char **wp;
{
int rv = 1;

if (wp[1] == (char *) 0) /* at&t ksh does this */
bi_errorf("no arguments");
else
for (wp++; *wp; wp++)
rv = evaluate(*wp) == 0;
return rv;
}

int
c_jobs(wp)
char **wp;
{
int optc;
int flag = 0;
int nflag = 0;
int rv = 0;

while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != EOF)
switch (optc) {
case 'l':
flag = 1;
break;
case 'p':
flag = 2;
break;
case 'n':
nflag = 1;
break;
case 'z': /* debugging: print zombies */
nflag = -1;
break;
case '?':
return 1;
}
wp += builtin_opt.optind;
if (!*wp)
if (j_jobs((char *) 0, flag, nflag))
rv = 1;
else
for (; *wp; wp++)
if (j_jobs(*wp, flag, nflag))
rv = 1;
return rv;
}

#ifdef JOBS
int
c_fgbg(wp)
register char **wp;
{
int bg = strcmp(*wp, "bg") == 0;
int UNINITIALIZED(rv);

if (!Flag(FMONITOR)) {
bi_errorf("job control not enabled\n");
return 1;
}
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
wp += builtin_opt.optind;
if (*wp)
for (; *wp; wp++)
rv = j_resume(*wp, bg);
else
rv = j_resume("%%", bg);
/* POSIX says fg shall return 0 (unless an error occurs).
* at&t ksh returns the exit value of the job...
*/
return (bg || Flag(FPOSIX)) ? 0 : rv;
}
#endif

struct kill_info {
int num_width;
int name_width;
};

/* format a single kill item */
static char *
kill_fmt_entry(arg, i, buf, buflen)
void *arg;
int i;
char *buf;
int buflen;
{
struct kill_info *ki = (struct kill_info *) arg;

i++;
if (sigtraps[i].name)
shf_snprintf(buf, buflen, "%*d %*s %s",
ki->num_width, i,
ki->name_width, sigtraps[i].name,
sigtraps[i].mess);
else
shf_snprintf(buf, buflen, "%*d %*d %s",
ki->num_width, i,
ki->name_width, sigtraps[i].signal,
sigtraps[i].mess);
return buf;
}


int
c_kill(wp)
register char **wp;
{
Trap *t = (Trap *) 0;
char *p;
int lflag = 0;
int i, n, rv, sig;

/* assume old style options if -digits or -UPPERCASE */
if ((p = wp[1]) && *p == '-' && (digit(p[1]) || isupper(p[1]))) {
if (!(t = gettrap(p + 1))) {
bi_errorf("bad signal `%s'", p + 1);
return 1;
}
i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
} else {
int optc;

while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != EOF)
switch (optc) {
case 'l':
lflag = 1;
break;
case 's':
if (!(t = gettrap(builtin_opt.optarg))) {
bi_errorf("bad signal `%s'",
builtin_opt.optarg);
return 1;
}
case '?':
return 1;
}
i = builtin_opt.optind;
}
if ((lflag && t) || (!wp[i] && !lflag)) {
shf_fprintf(shl_out,
"Usage: kill [ -s signame | -signum | -signame ] {pid|job}...\n\
kill -l [exit_status]\n"
);
bi_errorf((char *) 0);
return 1;
}

if (lflag) {
if (wp[i]) {
for (; wp[i]; i++) {
if (!bi_getn(wp[i], &n))
return 1;
if (n > 128 && n < 128 + SIGNALS)
n -= 128;
if (n > 0 && n < SIGNALS && sigtraps[n].name)
shprintf("%s\n", sigtraps[n].name);
else
shprintf("%d\n", n);
}
} else if (Flag(FPOSIX)) {
p = null;
for (i = 1; i < SIGNALS; i++, p = space)
if (sigtraps[i].name)
shprintf("%s%s", p, sigtraps[i].name);
shprintf(newline);
} else {
int w, i;
int mess_width;
struct kill_info ki;

for (i = SIGNALS, ki.num_width = 1; i >= 10; i /= 10)
ki.num_width++;
ki.name_width = mess_width = 0;
for (i = 0; i < SIGNALS; i++) {
w = sigtraps[i].name ? strlen(sigtraps[i].name)
: ki.num_width;
if (w > ki.name_width)
ki.name_width = w;
w = strlen(sigtraps[i].mess);
if (w > mess_width)
mess_width = w;
}

print_columns(shl_stdout, SIGNALS - 1,
kill_fmt_entry, (void *) &ki,
ki.num_width + ki.name_width + mess_width + 3);
}
return 0;
}
rv = 0;
sig = t ? t->signal : SIGTERM;
for (; (p = wp[i]); i++) {
if (*p == '%') {
if (j_kill(p, sig))
rv = 1;
} else if (!getn(p, &n)) {
bi_errorf("%s: arguments must be jobs or process ids",
p);
rv = 1;
} else {
/* use killpg if < -1 since -1 does special things for
* some non-killpg-endowed kills
*/
if ((n < -1 ? killpg(-n, sig) : kill(n, sig)) < 0) {
bi_errorf("%s: %s", p, strerror(errno));
rv = 1;
}
}
}
return rv;
}

static Getopt user_opt; /* parsing state for getopts builtin command */
static int getopts_noset; /* stop OPTIND assign from resetting state */

void
getopts_reset(val)
int val;
{
if (!getopts_noset && val >= 1) {
ksh_getopt_reset(&user_opt, Flag(FPOSIX) ? 0 : GF_PLUSOPT);
user_opt.optind = val;
}
}

int
c_getopts(wp)
char **wp;
{
int argc;
char *options;
char *var;
int optc;
char buf[3];
struct tbl *vq;

if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
wp += builtin_opt.optind;

options = *wp++;
if (options == (char *) 0) {
bi_errorf("missing options argument");
return 1;
}

var = *wp++;
if (var == (char *) 0) {
bi_errorf("missing name argument");
return 1;
}
if (!*var || *skip_varname(var, TRUE)) {
bi_errorf("%s: is not an identifier", var);
return 1;
}

if (e->loc->next == (struct block *) 0) {
internal_errorf(0, "c_getopts: no argv");
return 1;
}
/* Which arguments are we parsing... */
if (*wp == (char *) 0)
wp = e->loc->next->argv;
else
*--wp = e->loc->next->argv[0];

/* Check that our saved state won't cause a core dump... */
for (argc = 0; wp[argc]; argc++)
;
if (user_opt.optind > argc
|| (user_opt.p != 0
&& user_opt.p > strlen(wp[user_opt.optind - 1])))
{
bi_errorf("arguments changed since last call");
return 1;
}

user_opt.optarg = (char *) 0;
optc = ksh_getopt(wp, &user_opt, options);

if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
buf[0] = '+';
buf[1] = optc;
buf[2] = '\0';
} else {
/* POSIX says var is set to ? at end-of-options, at&t ksh
* sets it to null - we go with POSIX...
*/
buf[0] = optc < 0 ? '?' : optc;
buf[1] = '\0';
}
vq = global(var);
if (vq->flag & RDONLY)
bi_errorf("%s is readonly", var);
setstr(vq, buf);

getopts_noset = 1;
setint(global("OPTIND"), (long) user_opt.optind);
getopts_noset = 0;

if (user_opt.optarg == (char *) 0)
unset(global("OPTARG"));
else
setstr(global("OPTARG"), user_opt.optarg);

if (optc < 0)
return 1;

return 0;
}

#ifdef EMACS
int
c_bind(wp)
register char **wp;
{
int rv = 0, macro = 0;
register char *cp;
int optc;

while ((optc = ksh_getopt(wp, &builtin_opt, "m")) != EOF)
switch (optc) {
case 'm':
macro = 1;
break;
case '?':
return 1;
}
wp += builtin_opt.optind;

if (*wp == NULL) /* list all */
rv = x_bind((char*)NULL, (char*)NULL, 0);

for (; *wp != NULL; wp++) {
cp = strchr(*wp, '=');
if (cp != NULL)
*cp++ = '\0';
if (x_bind(*wp, cp, macro))
rv = 1;
}

return rv;
}
#endif

/* 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 kshbuiltins [] = {
{"+alias", c_alias}, /* no =: at&t manual wrong */
{"+cd", c_cd},
{"+command", c_command},
{"echo", c_print},
{"*=export", c_typeset},
#ifdef HISTORY
{"+fc", c_fc},
#endif /* HISTORY */
{"+getopts", c_getopts},
{"+jobs", c_jobs},
{"+kill", c_kill},
{"let", c_let},
{"print", c_print},
{"*=readonly", c_typeset},
{"=typeset", c_typeset},
{"+unalias", c_unalias},
#ifdef KSH
{"whence", c_whence},
#endif /* KSH */
#ifdef JOBS
{"+bg", c_fgbg},
{"+fg", c_fgbg},
#endif
#ifdef EMACS
{"bind", c_bind},
#endif
{NULL, NULL}
};
pdksh-5.1.2/c_sh.c100644 0 133 34612 5667072201 12562 0ustar rootlsource/*
* built-in Bourne commands
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: c_sh.c,v 1.5 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include "ksh_stat.h" /* umask() */
#include "ksh_time.h"
#include "ksh_times.h"

static char *clocktos ARGS((clock_t t));

/* :, false and true */
int
c_label(wp)
char **wp;
{
return wp[0][0] == 'f' ? 1 : 0;
}

int
c_shift(wp)
register char **wp;
{
register struct block *l = e->loc;
register int n;

n = wp[1] ? evaluate(wp[1]) : 1;
if (n < 0) {
bi_errorf("%s: bad number", wp[1]);
return (1);
}
if (l->argc < n) {
bi_errorf("nothing to shift");
return (1);
}
l->argv[n] = l->argv[0];
l->argv += n;
l->argc -= n;
return 0;
}

int
c_umask(wp)
register char **wp;
{
register int i;
register char *cp;
int symbolic = 0;
int old_umask;
int optc;

while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != EOF)
switch (optc) {
case 'S':
symbolic = 1;
break;
case '?':
return 1;
}
cp = wp[builtin_opt.optind];
if (cp == NULL) {
old_umask = umask(0);
umask(old_umask);
if (symbolic) {
char buf[18];
int j;

old_umask = ~old_umask;
cp = buf;
for (i = 0; i < 3; i++) {
*cp++ = "ugo"[i];
*cp++ = '=';
for (j = 0; j < 3; j++)
if (old_umask & (1 << (8 - (3*i + j))))
*cp++ = "rwx"[j];
*cp++ = ',';
}
cp[-1] = '\0';
shprintf("%s\n", buf);
} else
shprintf("%#3.3o\n", old_umask);
} else {
int new_umask;

if (digit(*cp)) {
for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
new_umask = new_umask * 8 + (*cp - '0');
if (*cp) {
bi_errorf("bad number");
return 1;
}
} else {
/* symbolic format */
int positions, new_val;
char op;

old_umask = umask(0);
umask(old_umask); /* in case of error */
old_umask = ~old_umask;
new_umask = old_umask;
positions = 0;
while (*cp) {
while (*cp && strchr("augo", *cp))
switch (*cp++) {
case 'a': positions |= 0111; break;
case 'u': positions |= 0100; break;
case 'g': positions |= 0010; break;
case 'o': positions |= 0001; break;
}
if (!positions)
positions = 0111; /* default is a */
if (!strchr("=+-", op = *cp))
break;
cp++;
new_val = 0;
while (*cp && strchr("rwxugoXs", *cp))
switch (*cp++) {
case 'r': new_val |= 04; break;
case 'w': new_val |= 02; break;
case 'x': new_val |= 01; break;
case 'u': new_val |= old_umask >> 6;
break;
case 'g': new_val |= old_umask >> 3;
break;
case 'o': new_val |= old_umask >> 0;
break;
case 'X': if (old_umask & 0111)
new_val |= 01;
break;
case 's': /* ignored */
break;
}
new_val = (new_val & 07) * positions;
switch (op) {
case '-':
new_umask &= ~new_val;
break;
case '=':
new_umask = new_val
| (new_umask & ~(positions * 07));
break;
case '+':
new_umask |= new_val;
}
if (*cp == ',') {
positions = 0;
cp++;
} else if (!strchr("=+-", *cp))
break;
}
if (*cp) {
bi_errorf("bad mask");
return 1;
}
new_umask = ~new_umask;
}
umask(new_umask);
}
return 0;
}

int
c_dot(wp)
char **wp;
{
char *file, *cp;
char **argv;
int argc;
int i;

if ((cp = wp[1]) == NULL)
return 0;
file = search(cp, path, R_OK);
if (file == NULL) {
bi_errorf("%s: not found", cp);
return 1;
}

/* Set positional parameters? */
if (wp[2]) {
argv = ++wp;
argv[0] = e->loc->argv[0]; /* preserve $0 */
for (argc = -1; *wp++; argc++)
;
} else {
argc = 0;
argv = (char **) 0;
}
i = include(file, argc, argv);
if (i < 0) { /* should not happen */
bi_errorf("%s: %s", cp, strerror(errno));
return 1;
}
return i;
}

int
c_wait(wp)
char **wp;
{
int UNINITIALIZED(rv);
int sig;

if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
wp += builtin_opt.optind;
if (*wp == (char *) 0) {
while (waitfor((char *) 0, &sig) >= 0)
;
rv = sig;
} else {
for (; *wp; wp++)
rv = waitfor(*wp, &sig);
if (rv < 0)
rv = sig ? sig : 127; /* magic exit code: bad job-id */
}
return rv;
}

int
c_read(wp)
register char **wp;
{
register int c = 0;
int expand = 1, history = 0;
int expanding;
int ecode = 0;
register char *cp;
int fd = 0;
struct shf *shf;
int optc;
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 = get_coproc_fd(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 (Flag(FTALKING)) {
/* at&t says it prints prompt on fd if its open
* for writing and is a tty, but it doesn't do it
*/
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.
*/
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) && isatty(fd)) {
/* set prompt in case this is
* called from .profile or $ENV
*/
set_prompt(PS2);
pprompt(prompt);
}
} 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);
if (vp->flag & RDONLY) {
shf_flush(shf);
bi_errorf("%s is read only", *wp);
return 1;
}
setstr(vp, Xstring(cs, cp));
}

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 */
if (c == EOF && !ecode)
coproc_read_close(fd);

return ecode ? ecode : c == EOF;
}

int
c_eval(wp)
register char **wp;
{
register struct source *s;

s = pushs(SWORDS, ATEMP);
s->u.strv = wp+1;
return shell(s, FALSE);
}

int
c_trap(wp)
register char **wp;
{
int i;
char *s;
register Trap *p;

if (ksh_getopt(wp, &builtin_opt, null) == '?')
return 1;
wp += builtin_opt.optind;

if (*wp == NULL) {
int anydfl = 0;

for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) {
if (p->trap == NULL)
anydfl = 1;
else {
shprintf("trap -- ");
print_value_quoted(p->trap);
shprintf(" %s\n", p->name);
}
}
#if 0 /* this is ugly and not clear POSIX needs it */
/* POSIX may need this so output of trap can be saved and
* used to restore trap conditions
*/
if (anydfl) {
shprintf("trap -- -");
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->trap == NULL && p->name)
shprintf(" %s", p->name);
shprintf(newline);
}
#endif
return 0;
}

s = (gettrap(*wp) == NULL) ? *wp++ : NULL; /* get command */
if (s != NULL && s[0] == '-' && s[1] == '\0')
s = NULL;

/* set/clear traps */
while (*wp != NULL) {
p = gettrap(*wp++);
if (p == NULL) {
bi_errorf("bad signal %s", wp[-1]);
return 1;
}
settrap(p, s);
}
return 0;
}

int
c_exitreturn(wp)
char **wp;
{
int how = LEXIT;

if (wp[1] != NULL && !getn(wp[1], &exstat)) {
exstat = 1;
warningf(TRUE, "%s: bad number\n", wp[1]);
}
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;
}
}

#ifdef JOBS
if (how == LEXIT && !really_exit && j_stopped()) {
really_exit = 1;
how = LSHELL;
}
#endif /* JOBS */

quitenv(); /* get rid of any i/o redirections */
unwind(how);
/*NOTREACHED*/
return 0;
}

int
c_brkcont(wp)
register char **wp;
{
int n, quit;
struct env *ep;

if (!wp[1])
n = 1;
else if (!bi_getn(wp[1], &n))
return 1;
quit = n;
if (quit <= 0) {
/* at&t ksh does this for non-interactive shells only - weird */
bi_errorf("bad option `%s'", wp[1]);
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;
}

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\n", wp[0], wp[0]);
return 0;
}
warningf(TRUE, "%s: can only %s %d level(s)\n",
wp[0], wp[0], n - quit);
}

unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
/*NOTREACHED*/
}

int
c_set(wp)
register char **wp;
{
int argi, setargs;
struct block *l = e->loc;
register char **owp = wp;

if (wp[1] == NULL) {
static char *args [] = {"set", "-", NULL};
return c_typeset(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 = strsave(*wp, &l->area);
l->argc = wp - owp - 1;
l->argv = (char **) alloc(sizeofN(char *, l->argc+2), &l->area);
for (wp = l->argv; (*wp++ = *owp++) != NULL; )
;
}
/* POSIX says set exit status is 0, but old scripts that use
* getopt(1), use the construct: set -- `getopt ab:c "$@"`
* which assumes the exit value set will be that of the ``
* (subst_exstat is cleared in execute() so that it will be 0
* if there are no command substitutions).
*/
return Flag(FPOSIX) ? 0 : subst_exstat;
}

int
c_unset(wp)
register char **wp;
{
register char *id;
int optc, unset_var = 1;

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&RDONLY)) {
bi_errorf("%s is read only", vp->name);
return 1;
}
unset(vp);
} else /* unset function */
define(id, (struct op *)NULL);
return 0;
}

int
c_times(wp)
char **wp;
{
struct tms all;

(void) ksh_times(&all);
shprintf("Shell: %8s user ", clocktos(all.tms_utime));
shprintf("%8s system\n", clocktos(all.tms_stime));
shprintf("Kids: %8s user ", clocktos(all.tms_cutime));
shprintf("%8s system\n", clocktos(all.tms_cstime));

return 0;
}

/*
* time pipeline (really a statement, not a built-in command)
*/
int
timex(t, f)
struct op *t;
int f;
{
int rv;
struct tms t0, t1;
clock_t t0t, t1t;
extern clock_t j_utime, j_stime; /* computed by j_wait */

j_utime = j_stime = 0;
t0t = ksh_times(&t0);
rv = execute(t->left, f);
t1t = ksh_times(&t1);

shf_fprintf(shl_out, "%8s real ", clocktos(t1t - t0t));
shf_fprintf(shl_out, "%8s user ",
clocktos(t1.tms_utime - t0.tms_utime + j_utime));
shf_fprintf(shl_out, "%8s system ",
clocktos(t1.tms_stime - t0.tms_stime + j_stime));
shf_fprintf(shl_out, newline);

return rv;
}

static char *
clocktos(t)
clock_t t;
{
static char temp[20];
register int i;
register char *cp = temp + sizeof(temp);

if (CLK_TCK != 100) /* convert to 1/100'ths */
t = (t < 1000000000/CLK_TCK) ?
(t * 100) / CLK_TCK : (t / CLK_TCK) * 100;

*--cp = '\0';
*--cp = 's';
for (i = -2; i <= 0 || t > 0; i++) {
if (i == 0)
*--cp = '.';
*--cp = '0' + (char)(t%10);
t /= 10;
}
return cp;
}

/* exec with no args - args case is taken care of in comexec() */
int
c_exec(wp)
char ** wp;
{
int i;

/* make sure redirects stay in place */
if (e->savefd != NULL) {
for (i = 0; i < NUFILE; i++) {
if (e->savefd[i] > 0)
close(e->savefd[i]);
/* keep anything > 2 private */
if (i > 2 && e->savefd[i])
fd_clexec(i);
}
e->savefd = NULL;
}
return 0;
}

/* dummy function, special case in comexec() */
int
c_builtin(wp)
char ** wp;
{
return 0;
}

extern int c_test ARGS((char **wp)); /* in c_test.c */
extern int c_ulimit ARGS((char **wp)); /* in c_ulimit.c */

/* A leading = means assignments before command are kept;
* a leading * means a POSIX special builtin;
* a leading + means a POSIX regular builtin
* (* and + should not be combined).
*/
const struct builtin shbuiltins [] = {
{"*=.", c_dot},
{"*=:", c_label},
{"[", c_test},
{"*=break", c_brkcont},
{"=builtin", c_builtin},
{"*=continue", c_brkcont},
{"*=eval", c_eval},
{"*=exec", c_exec},
{"*=exit", c_exitreturn},
{"+false", c_label},
{"*=return", c_exitreturn},
{"*=set", c_set},
{"*=shift", c_shift},
{"=times", c_times},
{"*=trap", c_trap},
{"+=wait", c_wait},
{"+read", c_read},
{"test", c_test},
{"+true", c_label},
{"ulimit", c_ulimit},
{"+umask", c_umask},
{"*=unset", c_unset},
#ifdef OS2
/* In OS2, the first line of a file can be "extproc name", which
* tells the command interpreter (cmd.exe) to use name to execute
* the file. For this to be useful, ksh must ignore commands
* starting with extproc and this does the trick...
*/
{"extproc", c_label},
#endif /* OS2 */
{NULL, NULL}
};
pdksh-5.1.2/c_test.c100644 0 133 24541 5664515542 13136 0ustar rootlsource/*
* 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.
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: c_test.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include "ksh_stat.h"

/* test(1) accepts the following grammar:
oexpr ::= aexpr | aexpr "-o" oexpr ;
aexpr ::= nexpr | nexpr "-a" aexpr ;
nexpr ::= primary | "!" nexpr ;
primary ::= unary-operator operand
| operand binary-operator operand
| operand
| "(" oexpr ")"
;

unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
"-L"|"-h"|"-S"|"-H";

binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
"-nt"|"-ot"|"-ef"|
"<"|">" # rules used for [[ .. ]] expressions
;
operand ::=
*/


#define is_unop(s) is_op(u_ops, s)
#define is_binop(s) is_op(b_ops, s)
#define is_not(s) ((s)[0] == '!' && (s)[1] == '\0')
#define is_and(s) ((s)[0] == '-' && (s)[1] == 'a' && (s)[2] == '\0')
#define is_or(s) ((s)[0] == '-' && (s)[1] == 'o' && (s)[2] == '\0')

#define T_ERR_EXIT 2 /* POSIX says > 1 for errors */

/* Various types of operations. Keeping things grouped nicely
* (unary,binary) makes switch() statements more efficeint.
*/
enum Op {
NONOP = 0, /* non-operator */
/* unary operators */
STNZE, STZER, OPTION,
FILEXST,
FILREG, FILBDEV, FILCDEV, FILSYM, FILFIFO, FILSOCK, FILCDF,
FILID, FILGID, FILSETG, FILSTCK, FILUID, FILRD, FILGZ,
FILTT, FILSETU, FILWR, FILEX,
/* binary operators */
STEQL, STNEQ, STLT, STGT, INTEQ, INTNE, INTGT, INTGE, INTLT, INTLE,
FILEQ, FILNT, FILOT
};
typedef enum Op Op;

struct t_op {
char op_text[4];
Op op_num;
};
static const struct t_op u_ops [] = {
{"-a", FILEXST },
{"-b", FILBDEV },
{"-c", FILCDEV },
{"-d", FILID },
{"-e", FILEXST },
{"-f", FILREG },
{"-G", FILGID },
{"-g", FILSETG },
{"-h", FILSYM },
{"-H", FILCDF },
{"-k", FILSTCK },
{"-L", FILSYM },
{"-n", STNZE },
{"-O", FILUID },
{"-o", OPTION },
{"-p", FILFIFO },
{"-r", FILRD },
{"-s", FILGZ },
{"-S", FILSOCK },
{"-t", FILTT },
{"-u", FILSETU },
{"-w", FILWR },
{"-x", FILEX },
{"-z", STZER },
{"", NONOP }
};
static const struct t_op b_ops [] = {
{"=", STEQL },
{"!=", STNEQ },
{"<", STLT },
{">", STGT },
{"-eq", INTEQ },
{"-ne", INTNE },
{"-gt", INTGT },
{"-ge", INTGE },
{"-lt", INTLT },
{"-le", INTLE },
{"-ef", FILEQ },
{"-nt", FILNT },
{"-ot", FILOT },
{"", NONOP }
};

static char **t_wp;
static int isdbracket; /* true when evaluating [[ .. ]] expressions */
static int t_error;

static void syntax ARGS((char *op, char *msg));
static int oexpr ARGS((void));
static int aexpr ARGS((void));
static int nexpr ARGS((void));
static int primary ARGS((void));
static int eval_unop ARGS((Op op, char *opnd1));
static int eval_binop ARGS((Op op, char *opnd1, char *opnd2));
static Op is_op ARGS((const struct t_op *otab, char *s));

int
c_test(wp)
char **wp;
{
Op op;
int argc;
int res;

t_error = 0;

isdbracket = strcmp(wp[0], "[[") == 0;

for (argc = 0; wp[argc]; argc++)
;

if (strcmp(wp[0], "[") == 0) {
if (strcmp(wp[--argc], "]") != 0) {
syntax(NULL, "missing ]");
return T_ERR_EXIT;
}
wp[argc] = NULL;
}

/*
* 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 ommited steps.
*/
if (!isdbracket && argc <= 5) {
char **owp = wp;
int val, invert = 0;

while (--argc >= 0) {
if (argc == 0)
return !0;
else if (argc == 3 && (op = is_binop(wp[2]))) {
t_wp = wp + 2; /* for error message */
val = eval_binop(op, wp[1], wp[3]);
if (t_error)
return T_ERR_EXIT;
if (invert & 1)
val = !val;
return !val;
} else if (argc == 1) {
val = *wp[1] != '\0';
if (invert & 1)
val = !val;
return !val;
} else if (is_not(wp[1]))
invert++;
else
break;
wp++;
}
wp = owp;
}

t_wp = wp + 1;
res = oexpr();

if (!t_error && *t_wp != NULL)
syntax(*t_wp, "unexpected operator/operand");

return t_error ? T_ERR_EXIT : !res;
}

/* is_db_*op() are called from syn.c for [[ .. ]] expression */
int
is_db_unop(s)
char *s;
{
isdbracket = TRUE;
return (int) is_unop(s);
}

int
is_db_binop(s)
char *s;
{
isdbracket = TRUE;
return (int) is_binop(s);
}

/* returns true if second argument of binary operator op is a pattern */
int
is_db_patop(op)
int op;
{
return (Op) op == STEQL || (Op) op == STNEQ;
}

static void
syntax(op, msg)
char *op;
char *msg;
{
t_error++;
if (op)
bi_errorf("%s: %s", op, msg);
else
bi_errorf("%s", msg);
}

static int
oexpr()
{
int res;

res = aexpr();
if (!t_error && *t_wp && is_or(*t_wp)) {
t_wp++;
return oexpr() || res;
}
return res;
}

static int
aexpr()
{
int res;

res = nexpr();
if (!t_error && *t_wp && is_and(*t_wp)) {
t_wp++;
return aexpr() && res;
}
return res;
}

static int
nexpr()
{
if (*t_wp && is_not(*t_wp)) {
t_wp++;
return !nexpr();
}
return primary();
}

static int
primary()
{
register char *opnd1, *opnd2;
int res;
Op op;

if (*t_wp == NULL) {
syntax(NULL, "argument expected");
return 1;
}
if (strcmp(*t_wp, "("/*)*/) == 0) {
t_wp++;
res = oexpr();
if (t_error)
return 1;
if (*t_wp == NULL || strcmp(*t_wp, /*(*/")") != 0) {
syntax(*t_wp, "closing paren expected");
return 1;
}
t_wp++;
return res;
}
if (isdbracket && strcmp(*t_wp, "-BE") == 0)
/* next 3 args form binary expression */
t_wp++;
else if ((op = is_unop(*t_wp))) {
/* unary expression */
if ((opnd1 = *++t_wp) == NULL) {
if (op != FILTT) {
syntax(t_wp[-1], "argument expected");
return 1;
}
} else
t_wp++;

return eval_unop(op, opnd1);
}
opnd1 = *t_wp++;
if (*t_wp != NULL && (op = is_binop(*t_wp))) {
if ((opnd2 = *++t_wp) == NULL) {
syntax(t_wp[-1], "argument expected");
return 1;
}
t_wp++;

return eval_binop(op, opnd1, opnd2);
}
/* XXX debugging */
if (isdbracket) {
syntax(opnd1, "internal error - unexpected token in [[ ]]");
return 1;
}
return *opnd1 != '\0';
}

static int
eval_unop(op, opnd1)
Op op;
char *opnd1;
{
int res;
struct stat s;
switch ((int) op) {
case STNZE:
return *opnd1 != '\0';
case STZER:
return *opnd1 == '\0';
case OPTION:
res = option(opnd1);
return res < 0 ? 0 : Flag(res);
case FILRD:
return eaccess(opnd1, R_OK) == 0;
case FILWR:
return eaccess(opnd1, W_OK) == 0;
case FILEX:
return eaccess(opnd1, X_OK) == 0;
case FILEXST:
return stat(opnd1, &s) == 0;
case FILREG:
return stat(opnd1, &s) == 0 && S_ISREG(s.st_mode);
case FILID:
return stat(opnd1, &s) == 0 && S_ISDIR(s.st_mode);
case FILCDEV:
#ifdef S_ISCHR
return stat(opnd1, &s) == 0 && S_ISCHR(s.st_mode);
#else
return 0;
#endif
case FILBDEV:
#ifdef S_ISBLK
return stat(opnd1, &s) == 0 && S_ISBLK(s.st_mode);
#else
return 0;
#endif
case FILFIFO:
#ifdef S_ISFIFO
return stat(opnd1, &s) == 0 && S_ISFIFO(s.st_mode);
#else
return 0;
#endif
case FILSYM:
#ifdef S_ISLNK
return lstat(opnd1, &s) == 0 && S_ISLNK(s.st_mode);
#else
return 0;
#endif
case FILSOCK:
#ifdef S_ISSOCK
return stat(opnd1, &s) == 0 && S_ISSOCK(s.st_mode);
#else
return 0;
#endif
case FILCDF: /* HP context dependent files (actually 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 = strnsave(opnd1, len + 1, ATEMP);

p[len++] = '+';
p[len] = '\0';
return stat(p, &s) == 0 && S_ISCDF(s.st_mode);
}
#else
return 0;
#endif
case FILSETU:
#ifdef S_ISUID
return stat(opnd1, &s) == 0 && (s.st_mode & S_ISUID) == S_ISUID;
#else
return 0;
#endif
case FILSETG:
#ifdef S_ISGID
return stat(opnd1, &s) == 0 && (s.st_mode & S_ISGID) == S_ISGID;
#else
return 0;
#endif
case FILSTCK:
return stat(opnd1, &s) == 0 && (s.st_mode & S_ISVTX) == S_ISVTX;
case FILGZ:
return stat(opnd1, &s) == 0 && s.st_size > 0L;
case FILTT:
if (opnd1 && !bi_getn(opnd1, &res)) {
t_error++;
res = 0;
} else
res = isatty(opnd1 ? res : 0);
return res;
case FILUID:
return stat(opnd1, &s) == 0 && s.st_uid == geteuid();
case FILGID:
return stat(opnd1, &s) == 0 && s.st_gid == getegid();
default:
syntax(t_wp[-2], "internal error: unknown unop");
return 1;
}
}

static int
eval_binop(op, opnd1, opnd2)
Op op;
char *opnd1;
char *opnd2;
{
struct stat b1, b2;

switch ((int) op) { /* keep gcc quiet */
case STEQL:
if (isdbracket)
return gmatch(opnd1, opnd2);
return strcmp(opnd1, opnd2) == 0;
case STNEQ:
if (isdbracket)
return !gmatch(opnd1, opnd2);
return strcmp(opnd1, opnd2) != 0;
case STLT:
return strcmp(opnd1, opnd2) < 0;
case STGT:
return strcmp(opnd1, opnd2) > 0;
case INTEQ:
return evaluate(opnd1) == evaluate(opnd2);
case INTNE:
return evaluate(opnd1) != evaluate(opnd2);
case INTGE:
return evaluate(opnd1) >= evaluate(opnd2);
case INTGT:
return evaluate(opnd1) > evaluate(opnd2);
case INTLE:
return evaluate(opnd1) <= evaluate(opnd2);
case INTLT:
return evaluate(opnd1) < evaluate(opnd2);
case FILNT:
return stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0
&& b1.st_mtime > b2.st_mtime;
case FILOT:
return stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0
&& b1.st_mtime < b2.st_mtime;
case FILEQ:
return stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0
&& b1.st_dev == b2.st_dev
&& b1.st_ino == b2.st_ino;
default:
syntax(t_wp[-2], "internal error: unknown binop");
return 1;
}
}

static Op
is_op(otab, s)
const struct t_op *otab;
char *s;
{
char sc1;

if (*s) {
sc1 = s[1];
for (; otab->op_text[0]; otab++)
if (sc1 == otab->op_text[1]
&& strcmp(s, otab->op_text) == 0
&& (isdbracket || (otab->op_num != STLT
&& otab->op_num != STGT)))
return otab->op_num;
}
return NONOP;
}
pdksh-5.1.2/c_ulimit.c100644 0 133 13613 5655451351 13455 0ustar rootlsource/*
ulimit -- handle "ulimit" builtin

Reworked to use getrusage() and ulimit() at once (as needed on
some schizophenic systems, eg, HP-UX 9.01), made argument parsing
conform to at&t ksh, added autoconf support. Michael Rendell, May, '94

Eric Gisin, September 1988
Adapted to PD KornShell. Removed AT&T code.

last edit: 06-Jun-1987 D A Gwyn

This started out as the BRL UNIX System V system call emulation
for 4.nBSD, and was later extended by Doug Kingston to handle
the extended 4.nBSD resource limits. It now includes the code
that was originally under case SYSULIMIT in source file "xec.c".
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: c_ulimit.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#ifdef HAVE_SETRLIMIT
# include
# include
#endif /* HAVE_SETRLIMIT */
#ifdef HAVE_ULIMIT_H
# include
#else /* HAVE_ULIMIT_H */
# ifdef HAVE_ULIMIT
extern long ulimit ARGS((int, ...));
# endif /* HAVE_ULIMIT */
#endif /* HAVE_ULIMIT_H */

#define SOFT 0x1
#define HARD 0x2

int
c_ulimit(wp)
char **wp;
{
static struct limits {
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(seconds)", RLIMIT, RLIMIT_CPU, RLIMIT_CPU, 1, 't' },
#endif
#ifdef RLIMIT_FSIZE
{ "file(blocks)", RLIMIT, RLIMIT_FSIZE, RLIMIT_FSIZE, 512, 'f' },
#else /* RLIMIT_FSIZE */
# ifdef UL_GETFSIZE /* x/open */
{ "file(blocks)", ULIMIT, UL_GETFSIZE, UL_SETFSIZE, 1, 'f' },
# else /* UL_GETFSIZE */
# ifdef UL_GFILLIM /* svr4/xenix */
{ "file(blocks)", ULIMIT, UL_GFILLIM, UL_SFILLIM, 1, 'f' },
# else /* UL_GFILLIM */
{ "file(blocks)", ULIMIT, 1, 2, 1, 'f' },
# endif /* UL_GFILLIM */
# endif /* UL_GETFSIZE */
#endif /* RLIMIT_FSIZE */
#ifdef RLIMIT_DATA
{ "data(kbytes)", RLIMIT, RLIMIT_DATA, RLIMIT_DATA, 1024, 'd' },
#endif
#ifdef RLIMIT_STACK
{ "stack(kbytes)", RLIMIT, RLIMIT_STACK, RLIMIT_STACK, 1024, 's' },
#endif
#ifdef RLIMIT_RSS
{ "memory(kbytes)", RLIMIT, RLIMIT_RSS, RLIMIT_RSS, 1024, 'm' },
#endif
#ifdef RLIMIT_CORE
{ "coredump(blocks)", RLIMIT, RLIMIT_CORE, RLIMIT_CORE, 512, 'c' },
#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_VMEM
{ "vmemory(kbytes)", RLIMIT, RLIMIT_VMEM, RLIMIT_VMEM, 1024, 'v' },
#else /* RLIMIT_VMEM */
/* These are not quite right - really should subtract etext or something */
# ifdef UL_GMEMLIM /* svr4/xenix */
{ "vmemory(maxaddr)", ULIMIT, UL_GMEMLIM, -1, 1, 'v' },
# else /* UL_GMEMLIM */
# ifdef UL_GETBREAK /* osf/1 */
{ "vmemory(maxaddr)", ULIMIT, UL_GETBREAK, -1, 1, 'v' },
# else /* UL_GETBREAK */
# ifdef UL_GETMAXBRK /* hpux */
{ "vmemory(maxaddr)", ULIMIT, UL_GETMAXBRK, -1, 1, 'v' },
# endif /* UL_GETMAXBRK */
# endif /* UL_GETBREAK */
# endif /* UL_GMEMLIM */
#endif /* RLIMIT_VMEM */
#ifdef RLIMIT_SWAP
{ "swap(kbytes)", RLIMIT_SWAP, RLIMIT_SWAP, 1024, 'w' },
#endif
{ (char *) 0 }
};
static char options[3 + NELEM(limits)];
long UNINITIALIZED(val);
int how = SOFT | HARD;
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) {
char *p = *wp;

if (all || wp[1]) {
bi_errorf("too many arguments");
return 1;
}
#ifdef RLIM_INFINITY
if (strcmp(p, "unlimited") == 0)
val = RLIM_INFINITY;
else
#endif /* RLIM_INFINITY */
val = evaluate(p) * l->factor;
}
if (all) {
for (l = limits; l->name; l++) {
#ifdef HAVE_SETRLIMIT
if (l->which == RLIMIT) {
getrlimit(l->gcmd, &limit);
if (how & SOFT)
val = limit.rlim_cur;
else if (how & HARD)
val = limit.rlim_max;
} else
#endif /* HAVE_SETRLIMIT */
#ifdef HAVE_ULIMIT
{
val = ulimit(l->gcmd);
}
#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", 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) {
bi_errorf("bad limit: %s", strerror(errno));
return 1;
}
} else {
if (how & SOFT)
val = limit.rlim_cur;
else if (how & HARD)
val = limit.rlim_max;
}
} else
#endif /* HAVE_SETRLIMIT */
#ifdef HAVE_ULIMIT
{
if (set) {
if (l->scmd == -1) {
bi_errorf("can't change limit");
return 1;
} else if (ulimit(l->scmd, val) < 0) {
bi_errorf("bad limit: %s", strerror(errno));
return 1;
}
} else
val = ulimit(l->gcmd);
}
#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", val);
}
}
return 0;
}
pdksh-5.1.2/edit.c100644 0 133 13324 5667370263 12601 0ustar rootlsource/*
* Command line editing - common code
*
*/

#include "config.h"
#ifdef EDIT

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: edit.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include "tty.h"
#define EXTERN
#include "edit.h"
#undef EXTERN

static char vdisable_c;


/* Called from main */
void
x_init()
{
/* set to -1 to force initial binding */
edchars.erase = edchars.kill = edchars.intr = edchars.quit
= edchars.eof = -1;
/* default value for deficient systems */
edchars.werase = 027; /* ^W */
#ifdef TIOCGWINSZ
{
struct winsize ws;

if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0 && ws.ws_col) {
x_cols = ws.ws_col;
setint(global("COLUMNS"), (long) x_cols);
}
}
#endif /* TIOCGWINSZ */
#ifdef EMACS
x_init_emacs();
#endif /* EMACS */

/* Bizarreness to figure out how to disable
* a struct termios.c_cc[] char
*/
#ifdef _POSIX_VDISABLE
if (_POSIX_VDISABLE >= 0)
vdisable_c = _POSIX_VDISABLE;
else
/* `feature not available' */
vdisable_c = 0377;
#else
# if defined(HAVE_PATHCONF) && defined(_PC_VDISABLE)
vdisable_c = fpathconf(tty_fd, _PC_VDISABLE);
# else
vdisable_c = 0377; /* default to old BSD value */
# endif
#endif /* _POSIX_VDISABLE */
}

/*
* read an edited command line
*/
int
x_read(buf, len)
char *buf;
size_t len;
{
int i;

x_mode(TRUE);
#ifdef EMACS
if (Flag(FEMACS) || Flag(FGMACS))
i = x_emacs(buf, len);
else
#endif
#ifdef VI
if (Flag(FVI))
i = x_vi(buf, len);
else
#endif
i = -1; /* internal error */
x_mode(FALSE);
return i;
}

/* tty I/O */

int
x_getc()
{
#ifdef OS2
unsigned char c = _read_kbd(0, 1, 0);
return c == 0 ? 0xE0 : c;
#else /* OS2 */
char c;
int n;

while ((n = read(0, &c, 1)) < 0 && errno == EINTR)
if (trap) {
x_mode(FALSE);
runtraps(0);
x_mode(TRUE);
}
if (n != 1)
return -1;
return (unsigned char) c;
#endif /* OS2 */
}

void
x_flush()
{
shf_flush(shl_out);
}

void
x_putc(c)
int c;
{
shf_putc(c, shl_out);
}

void
x_puts(s)
register char *s;
{
while (*s != 0)
shf_putc(*s++, shl_out);
}

bool_t
x_mode(onoff)
bool_t onoff;
{
static bool_t x_cur_mode = FALSE;
bool_t prev;

if (x_cur_mode == onoff)
return x_cur_mode;
prev = x_cur_mode;
x_cur_mode = onoff;
#ifndef OS2
if (onoff) {
TTY_state 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
# ifdef SWTCH /* need CBREAK to handle swtch char */
cb.c_lflag &= ~(ICANON|ECHO);
cb.c_lflag |= ISIG;
cb.c_cc[VINTR] = vdisable_c;
cb.c_cc[VQUIT] = vdisable_c;
# else
cb.c_lflag &= ~(ISIG|ICANON|ECHO);
# endif
# endif
# ifdef VLNEXT
/* osf/1 processes lnext when ~icanon */
cb.c_cc[VLNEXT] = vdisable_c;
# endif /* VLNEXT */
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 */

set_tty(tty_fd, &cb, TF_WAIT);

if (memcmp(&edchars, &oldchars, sizeof(edchars)) != 0) {
# ifdef EMACS
x_emacs_keys(&edchars);
# endif
}
} else
/* TF_WAIT doesn't seem to be necessary when leaving xmode */
set_tty(tty_fd, &tty_state, TF_NONE);
#endif /* !OS2 */
return prev;
}

/* NAME:
* promptlen - calculate the length of PS1 etc.
*
* DESCRIPTION:
* This function is based on a fix from [email protected]
* It fixes a bug in that if PS1 contains '!', the length
* given by strlen() is probably wrong.
*
* RETURN VALUE:
* length
*/

int
promptlen(cp)
register char *cp;
{
register int count = 0;

while (*cp)
{
if ( *cp == '\n' || *cp == '\r' )
{
count = 0;
cp++;
}
else if ( *cp == '\t' )
{
count = (count | 7) + 1;
cp++;
}
else if ( *cp == '\b' )
{
if (count > 0)
count--;
cp++;
}
else if ( *cp++ != '!' )
count++;
else if ( *cp == '!' )
{
cp++;
count++;
}
else
{
register int i = source->line + 1;

do
{
count ++;
}
while( ( i /= 10 ) > 0 );
}
}
return count;
}

void
set_editmode(ed)
char *ed;
{
static const enum sh_flag edit_flags[] = {
#ifdef EMACS
FEMACS, FGMACS,
#endif
#ifdef VI
FVI,
#endif
};
char *rcp;
int i;

if ((rcp = 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;
}
}
#endif /* EDIT */
pdksh-5.1.2/emacs.c100644 0 133 123536 5667354636 13001 0ustar rootlsource/*
* Emacs-like command line editing and history
*
* created by Ron Natalie at BRL
* modified by Doug Kingston, Doug Gwyn, and Lou Salkind
* adapted to PD ksh by Eric Gisin
*/

#include "config.h"
#ifdef EMACS

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: emacs.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include "ksh_stat.h"
#include "ksh_dir.h"
#include
#include "edit.h"

#define PUSH_DELETE 1 /* push all deletes of >1 char */

static Area aedit;
#define AEDIT &aedit /* area for kill ring and macro defns */

#undef CTRL /* _BSD brain damage */
#define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */
#define UNCTRL(x) ((x) == 0x7F ? '?' : (x) | 0x40) /* ASCII */


/* values returned by keyboard functions */
#define KSTD 0
#define KPREF 1 /* ^[, ^X */
#define KEOL 2 /* ^M, ^J */
#define KINTR 3 /* ^G, ^C */
#define KNULL 4

struct x_ftab {
int (*xf_func)();
char *xf_name;
char xf_db_tab;
unsigned char xf_db_char;
short xf_flags;
};

#define XF_ALLOC 2
#define XF_NOBIND 4

#define iscfs(c) (c == ' ' || c == '\t') /* Separator for completion */
#define ismfs(c) (!(isalnum(c)|| c == '$')) /* Separator for motion */

#ifdef OS2
/* Deal with 8 bit chars & an extra prefix for function key (these two
* changes increase memory usage from 9,216 bytes to 24,416 bytes...)
*/
# define CHARMASK 0xFF /* 8-bit ASCII character mask */
# define X_TABSZ 256 /* size of keydef tables etc */
# define X_NTABS 4 /* normal, meta1, meta2, meta3 */
static int x_prefix3 = 0xE0;
static int x_meta3 ARGS((int c));
#else /* OS2 */
# define CHARMASK 0x7F /* 7-bit ASCII character mask */
# define X_TABSZ 128 /* size of keydef tables etc */
# define X_NTABS 3 /* normal, meta1, meta2 */
#endif /* OS2 */

/* { from 4.9 edit.h */
/*
* The following are used for my horizontal scrolling stuff
*/
static char *xbuf; /* beg input buffer */
static char *xend; /* end input buffer */
static char *xcp; /* current position */
static char *xep; /* current end */
static char *xbp; /* start of visible portion of input buffer */
static char *xlp; /* last char visible on screen */
static int x_adj_ok;
/*
* we use x_adj_done so that functions can tell
* whether x_adjust() has been called while they are active.
*/
static int x_adj_done;

static int x_col;
static int x_displen;
static int x_arg; /* general purpose arg */

static int xlp_valid;
/* end from 4.9 edit.h } */

static int x_prefix1 = CTRL('['), x_prefix2 = CTRL('X');
static char **x_histp; /* history position */
static char **x_nextcmdp; /* for newline-and-next */
static char *xmp; /* mark pointer */
static int (*x_last_command)();
static struct x_ftab const *(*x_tab)[X_TABSZ] = NULL; /* key definition */
static char *(*x_atab)[X_TABSZ] = NULL; /* macro definitions */
#define KILLSIZE 20
static char *killstack[KILLSIZE];
static int killsp, killtp;
static int x_curprefix;
static char *macroptr;
static int x_maxlen; /* to determine column width */

static int x_insert ARGS((int c));
static int x_ins_string ARGS((int c));
static void x_ins ARGS((char *cp));
static int x_del_back ARGS((int c));
static int x_del_char ARGS((int c));
static void x_delete ARGS((int nc));
static int x_del_bword ARGS((int c));
static int x_mv_bword ARGS((int c));
static int x_mv_fword ARGS((int c));
static int x_del_fword ARGS((int c));
static int x_bword ARGS((void));
static int x_fword ARGS((void));
static void x_goto ARGS((char *cp));
static void x_bs ARGS((int c));
static int x_size_str ARGS((char *cp));
static int x_size ARGS((int c));
static void x_zots ARGS((char *str));
static void x_zotc ARGS((int c));
static int x_mv_back ARGS((int c));
static int x_mv_forw ARGS((int c));
static int x_search_char ARGS((int c));
static int x_newline ARGS((int c));
static int x_end_of_text ARGS((int c));
static int x_beg_hist ARGS((int c));
static int x_end_hist ARGS((int c));
static int x_prev_com ARGS((int c));
static int x_next_com ARGS((int c));
static void x_load_hist ARGS((char **hp));
static int x_nl_next_com ARGS((int c));
static int x_eot_del ARGS((int c));
static int x_search_hist ARGS((int c));
static int x_search ARGS((char *pat, int offset));
static int x_match ARGS((char *str, char *pat));
static int x_del_line ARGS((int c));
static int x_mv_end ARGS((int c));
static int x_mv_begin ARGS((int c));
static int x_draw_line ARGS((int c));
static void x_redraw ARGS((int limit));
static int x_transpose ARGS((int c));
static int x_literal ARGS((int c));
static int x_meta1 ARGS((int c));
static int x_meta2 ARGS((int c));
static int x_kill ARGS((int c));
static void x_push ARGS((int nchars));
static int x_yank ARGS((int c));
static int x_meta_yank ARGS((int c));
static int x_abort ARGS((int c));
static int x_error ARGS((int c));
static int x_stuffreset ARGS((int c));
static int x_stuff ARGS((int c));
static void x_mapin ARGS((char *cp));
static char * x_mapout ARGS((int c));
static void x_print ARGS((int prefix, int key));
static int x_set_mark ARGS((int c));
static int x_kill_region ARGS((int c));
static int x_xchg_point_mark ARGS((int c));
static int x_version ARGS((int c));
static int x_noop ARGS((int c));
#ifdef SILLY
static int x_game_of_life ARGS((int c));
#endif
static void add_stash ARGS((char *dirnam, char *name));
static void list_stash ARGS((void));
static int x_comp_comm ARGS((int c));
static int x_list_comm ARGS((int c));
static int x_complete ARGS((int c));
static int x_enumerate ARGS((int c));
static int x_comp_file ARGS((int c));
static int x_list_file ARGS((int c));
static int x_comp_list ARGS((int c));
static void compl_dec ARGS((int type));
static void compl_file ARGS((int type));
static void compl_command ARGS((int type));
static int strmatch ARGS((char *s1, char *s2));
static void x_adjust ARGS((void));
static int x_e_getc ARGS((void));
static void x_e_putc ARGS((int c));
#ifdef DEBUG
static int x_debug_info ARGS((void));
#endif /* DEBUG */
static void x_e_puts ARGS((char *s));
static int x_set_arg ARGS((int c));
static int x_prev_histword ARGS((void));
static int x_fold_case ARGS((int c));
static char *x_lastcp ARGS(());


static struct x_ftab const x_ftab[] = {

#define xft_insert &x_ftab[0]
#define xft_error &x_ftab[1]
#define xft_erase &x_ftab[2]
#define xft_werase &x_ftab[3]
#define xft_kill &x_ftab[4]
#define xft_intr &x_ftab[5]
#define xft_quit &x_ftab[6]
#define xft_ins_string &x_ftab[7]

/*0*/ {x_insert, "auto-insert", 0, 0, 0 },
/*1*/ {x_error, "error", 0, 0, 0 },
/*2*/ {x_del_back, "delete-char-backward", 0, CTRL('?'), 0 },
/*3*/ {x_del_bword, "delete-word-backward", 1, CTRL('?'), 0 },
/*4*/ {x_del_line, "kill-line", 0, 0, 0 },
/*5*/ {x_abort, "abort", 0, 0, 0 },
/*6*/ {x_noop, "no-op", 0, 0, 0 },
/*7*/ {x_ins_string, "macro-string", 0, 0, XF_NOBIND|XF_ALLOC},

/* Do not move the above! */
{x_del_char, "delete-char-forward", 0, 0, 0 },
{x_eot_del, "eot-or-delete", 0, CTRL('D'), 0 },
{x_del_back, "delete-char-backward", 0, CTRL('H'), 0 },
{x_del_bword, "delete-word-backward", 1, CTRL('H'), 0 },
{x_mv_bword, "backward-word", 1, 'b', 0 },
{x_mv_fword, "forward-word", 1, 'f', 0 },
{x_del_fword, "delete-word-forward", 1, 'd', 0 },
{x_mv_back, "backward-char", 0, CTRL('B'), 0 },
{x_mv_forw, "forward-char", 0, CTRL('F'), 0 },
{x_search_char, "search-character", 0, CTRL(']'), 0 },
{x_newline, "newline", 0, CTRL('M'), 0 },
{x_newline, "newline", 0, CTRL('J'), 0 },
{x_end_of_text, "eot", 0, CTRL('_'), 0 },
{x_abort, "abort", 0, CTRL('G'), 0 },
{x_prev_com, "up-history", 0, CTRL('P'), 0},
{x_next_com, "down-history", 0, CTRL('N'), 0},
{x_search_hist, "search-history", 0, CTRL('R'), 0},
{x_beg_hist, "beginning-of-history", 1, '<', 0},
{x_end_hist, "end-of-history", 1, '>', 0},
{x_mv_end, "end-of-line", 0, CTRL('E'), 0 },
{x_mv_begin, "beginning-of-line", 0, CTRL('A'), 0 },
{x_draw_line, "redraw", 0, CTRL('L'), 0 },
{x_meta1, "prefix-1", 0, CTRL('['), 0 },
{x_meta2, "prefix-2", 0, CTRL('X'), 0 },
{x_kill, "kill-to-eol", 0, CTRL('K'), 0 },
{x_yank, "yank", 0, CTRL('Y'), 0 },
{x_meta_yank, "yank-pop", 1, 'y', 0 },
{x_literal, "quote", 0, CTRL('^'), 0 },
{x_stuffreset, "stuff-reset", 0, 0, 0 },
#if defined(BRL) && defined(TIOCSTI)
{x_stuff, "stuff", 0, CTRL('T'), 0 },
{x_transpose, "transpose-chars", 0, 0, 0 },
#else
{x_stuff, "stuff", 0, 0, 0 },
{x_transpose, "transpose-chars", 0, CTRL('T'), 0 },
#endif
{x_complete, "complete", 1, CTRL('['), 0 },
{x_comp_list, "complete-list", 1, '=', 0 },
{x_enumerate, "list", 1, '?', 0 },
{x_comp_file, "complete-file", 1, CTRL('X'), 0 },
{x_comp_comm, "complete-command", 2, CTRL('['), 0 },
{x_list_file, "list-file", 0, 0, 0 },
{x_list_comm, "list-command", 2, '?', 0 },
{x_nl_next_com, "newline-and-next", 0, CTRL('O'), 0 },
{x_set_mark, "set-mark-command", 1, ' ', 0 },
{x_kill_region, "kill-region", 0, CTRL('W'), 0 },
{x_xchg_point_mark, "exchange-point-and-mark", 2, CTRL('X'), 0 },
{x_version, "version", 0, CTRL('V'), 0 },
#ifdef SILLY
{x_game_of_life, "play-game-of-life", 0, 0, 0 },
#endif
#ifdef DEBUG
{x_debug_info, "debug-info", 1, CTRL('H'), 0 },
#endif
{x_prev_histword, "prev-hist-word", 1, '.', 0 },
{x_prev_histword, "prev-hist-word", 1, '_', 0 },
{x_set_arg, null, 1, '0', 0 },
{x_set_arg, null, 1, '1', 0 },
{x_set_arg, null, 1, '2', 0 },
{x_set_arg, null, 1, '3', 0 },
{x_set_arg, null, 1, '4', 0 },
{x_set_arg, null, 1, '5', 0 },
{x_set_arg, null, 1, '6', 0 },
{x_set_arg, null, 1, '7', 0 },
{x_set_arg, null, 1, '8', 0 },
{x_set_arg, null, 1, '9', 0 },
{x_fold_case, "upcase-word", 1, 'U', 0 },
{x_fold_case, "downcase-word", 1, 'L', 0 },
{x_fold_case, "capitalize-word", 1, 'C', 0 },
{x_fold_case, "upcase-word", 1, 'u', 0 },
{x_fold_case, "downcase-word", 1, 'l', 0 },
{x_fold_case, "capitalize-word", 1, 'c', 0 },
/* These for ansi arrow keys: arguablely shouldn't be here by
* default, but its simpler/faster/smaller than using termcap
* entries.
*/
{x_meta2, "prefix-2", 1, '[', 0 },
{x_prev_com, "up-history", 2, 'A', 0},
{x_next_com, "next-history", 2, 'B', 0},
{x_mv_forw, "forward-char", 2, 'C', 0},
{x_mv_back, "backward-char", 2, 'D', 0},
#ifdef OS2
{x_meta3, "prefix-3", 0, 0xE0, 0 },
{x_mv_back, "backward-char", 3, 'K', 0 },
{x_mv_forw, "forward-char", 3, 'M', 0 },
{x_next_com, "down-history", 3, 'P', 0},
{x_prev_com, "up-history", 3, 'H', 0},
#endif /* OS2 */
{ 0 }
};

int
x_emacs(buf, len)
char *buf;
size_t len;
{
int c;
int i;
int (*func)();
extern x_insert();

xbp = xbuf = buf; xend = buf + len;
xlp = xcp = xep = buf;
*xcp = 0;
xlp_valid = TRUE;
xmp = NULL;
x_curprefix = 0;
macroptr = null;
x_histp = histptr + 1;

if (x_nextcmdp != NULL) {
x_load_hist(x_nextcmdp);
x_nextcmdp = NULL;
}

x_col = promptlen(prompt);
x_adj_ok = 1;
x_displen = x_cols - 2 - x_col;
x_adj_done = 0;

while (1) {
x_flush();
if (*macroptr) {
c = *macroptr++;
if (*macroptr == 0)
macroptr = null;
}
else {
if ((c = x_e_getc()) < 0)
return 0;
}

if (x_curprefix == -1)
func = x_insert;
else
func = x_tab[x_curprefix][c&CHARMASK]->xf_func;
if (func == NULL)
func = x_error;
i = c | (x_curprefix << 8);
x_curprefix = 0;
switch (i = (*func)(i)) {
case KSTD:
x_last_command = func;
case KPREF:
case KNULL:
break;
case KEOL:
i = xep - xbuf;
x_last_command = 0;
return i;
case KINTR: /* special case for interrupt */
trapsig(SIGINT);
x_mode(FALSE);
unwind(LSHELL);
}
}
}

static int
x_insert(c)
int c;
{
char str[2];

/*
* Should allow tab and control chars.
*/
if (c == 0) {
x_e_putc(BEL);
return KSTD;
}
str[0] = c;
str[1] = '\0';
x_ins(str);
return KSTD;
}

static int
x_ins_string(c)
int c;
{
if (*macroptr) {
x_e_putc(BEL);
return KSTD;
}
macroptr = x_atab[c>>8][c & CHARMASK];
return KSTD;
}

static void
x_ins(cp)
char *cp;
{
int count;
register int adj = x_adj_done;

count = strlen(cp);
if (xep+count >= xend) {
x_e_putc(BEL);
return;
}

if (xcp != xep)
memmove(xcp+count, xcp, xep - xcp + 1);
else
xcp[count] = '\0';
memmove(xcp, cp, count);
/*
* x_zots() may result in a call to x_adjust()
* we want xcp to reflect the new position.
*/
cp = xcp;
xcp += count;
xep += count;
xlp_valid = FALSE;
x_lastcp();
x_adj_ok = (xcp >= xlp);
x_zots(cp);
if (adj == x_adj_done) /* has x_adjust() been called? */
{
/* no */
for (cp = xlp; cp > xcp; )
x_bs(*--cp);
}

x_adj_ok = 1;
return;
}

static int
x_del_back(c)
int c;
{
if (xcp == xbuf) {
x_e_putc(BEL);
return KSTD;
}
x_goto(xcp - 1);
x_delete(1);
return KSTD;
}

static int
x_del_char(c)
int c;
{
if (xcp == xep) {
x_e_putc(BEL);
return KSTD;
}
x_delete(1);
return KSTD;
}

static void
x_delete(nc)
int nc;
{
int i,j;
char *cp;

if (nc == 0)
return;
if (xmp != NULL) {
if (xcp + nc > xmp)
xmp = xcp;
else if (xmp > xcp)
xmp -= nc;
}
#ifdef PUSH_DELETE
/*
* This lets us yank a word we have deleted.
*/
if (nc > 1)
x_push(nc);
#endif
xep -= nc;
cp = xcp;
j = 0;
i = nc;
while (i--) {
j += x_size(*cp++);
}
memmove(xcp, xcp+nc, xep - xcp + 1); /* Copies the null */
x_adj_ok = 0; /* don't redraw */
x_zots(xcp);
/*
* if we are already filling the line,
* there is no need to ' ','\b'.
* But if we must, make sure we do the minimum.
*/
if ((i = x_cols - 2 - x_col) > 0)
{
j = (j < i) ? j : i;
i = j;
while (i--)
x_e_putc(' ');
i = j;
while (i--)
x_e_putc('\b');
}
/*x_goto(xcp);*/
x_adj_ok = 1;
xlp_valid = FALSE;
for (cp = x_lastcp(); cp > xcp; )
x_bs(*--cp);

return;
}

static int
x_del_bword(c)
int c;
{
x_delete(x_bword());
return KSTD;
}

static int
x_mv_bword(c)
int c;
{
(void)x_bword();
return KSTD;
}

static int
x_mv_fword(c)
int c;
{
x_goto(xcp + x_fword());
return KSTD;
}

static int
x_del_fword(c)
int c;
{
x_delete(x_fword());
return KSTD;
}

static int
x_bword()
{
int nc = 0;
register char *cp = xcp;

if (cp == xbuf) {
x_e_putc(BEL);
return 0;
}
if (x_last_command != x_set_arg)
x_arg = 1;
while (x_arg--)
{
while (cp != xbuf && ismfs(cp[-1]))
{
cp--;
nc++;
}
while (cp != xbuf && !ismfs(cp[-1]))
{
cp--;
nc++;
}
}
x_goto(cp);
return nc;
}

static int
x_fword()
{
int nc = 0;
register char *cp = xcp;

if (cp == xep) {
x_e_putc(BEL);
return 0;
}
if (x_last_command != x_set_arg)
x_arg = 1;
while (x_arg--)
{
while (cp != xep && ismfs(*cp))
{
cp++;
nc++;
}
while (cp != xep && !ismfs(*cp))
{
cp++;
nc++;
}
}
return nc;
}

static void
x_goto(cp)
register char *cp;
{
if (cp < xbp || cp >= (xbp + x_displen))
{
/* we are heading off screen */
xcp = cp;
x_adjust();
}
else
{
if (cp < xcp) /* move back */
{
while (cp < xcp)
x_bs(*--xcp);
}
else
{
if (cp > xcp) /* move forward */
{
while (cp > xcp)
x_zotc(*xcp++);
}
}
}
}

static void
x_bs(c)
int c;
{
register i;
i = x_size(c);
while (i--)
x_e_putc('\b');
}

static int
x_size_str(cp)
register char *cp;
{
register size = 0;
while (*cp)
size += x_size(*cp++);
return size;
}

static int
x_size(c)
int c;
{
if (c=='\t')
return 4; /* Kludge, tabs are always four spaces. */
if (c < ' ' || c == 0x7F) /* ASCII control char */
return 2;
return 1;
}

static void
x_zots(str)
register char *str;
{
register int adj = x_adj_done;

x_lastcp();
while (*str && str < xlp && adj == x_adj_done)
x_zotc(*str++);
}

static void
x_zotc(c)
int c;
{
if (c == '\t') {
/* Kludge, tabs are always four spaces. */
x_e_puts(" ");
} else if (c < ' ' || c == 0x7F) { /* ASCII */
x_e_putc('^');
x_e_putc(UNCTRL(c));
} else
x_e_putc(c);
}

static int
x_mv_back(c)
int c;
{
if (xcp == xbuf) {
x_e_putc(BEL);
return KSTD;
}
x_goto(xcp-1);
return KSTD;
}

static int
x_mv_forw(c)
int c;
{
if (xcp == xep) {
x_e_putc(BEL);
return KSTD;
}
x_goto(xcp+1);
return KSTD;
}

static int
x_search_char(c)
int c;
{
char *cp;

*xep = '\0';
if ((c = x_e_getc()) < 0 ||
/* we search forward, I don't know what Korn does */
((cp = (xcp == xep) ? NULL : strchr(xcp+1, c)) == NULL &&
(cp = strchr(xbuf, c)) == NULL)) {
x_e_putc(BEL);
return KSTD;
}
x_goto(cp);
return KSTD;
}

static int
x_newline(c)
int c;
{
x_e_putc('\r');
x_e_putc('\n');
x_flush();
*xep++ = '\n';
return KEOL;
}

static int
x_end_of_text(c)
int c;
{
#if 0
x_store_hist();
#endif
return KEOL;
}

static int x_beg_hist(c) int c; {x_load_hist(history); return KSTD;}

static int x_end_hist(c) int c; {x_load_hist(histptr); return KSTD;}

static int x_prev_com(c) int c; {x_load_hist(x_histp-1); return KSTD;}

static int x_next_com(c) int c; {x_load_hist(x_histp+1); return KSTD;}

static void
x_load_hist(hp)
register char **hp;
{
int oldsize;

if (hp < history || hp > histptr) {
x_e_putc(BEL);
return;
}
x_histp = hp;
oldsize = x_size_str(xbuf);
(void)strcpy(xbuf, *hp);
xbp = xbuf;
xep = xcp = xbuf + strlen(*hp);
xlp_valid = FALSE;
if (xep > x_lastcp())
x_goto(xep);
else
x_redraw(oldsize);
}

static int
x_nl_next_com(c)
int c;
{
x_nextcmdp = x_histp + 1;
return (x_newline(c));
}

static int
x_eot_del(c)
int c;
{
if (xep == xbuf)
return (x_end_of_text(c));
else
return (x_del_char(c));
}

/* reverse incremental history search */
static int
x_search_hist(c)
int c;
{
int offset = -1; /* offset of match in xbuf, else -1 */
char pat [256+1]; /* pattern buffer */
register char *p = pat;
int (*func)();

*p = '\0';
while (1) {
if (offset < 0) {
x_e_puts("\nI-search: ");
x_zots(pat);
}
x_flush();
if ((c = x_e_getc()) < 0)
return KSTD;
func = x_tab[0][c&CHARMASK]->xf_func;
if (c == CTRL('['))
break;
else if (func == x_search_hist)
offset = x_search(pat, offset);
else if (func == x_del_back)
continue; /* todo: del-back in incremental search */
else if (func == x_insert) {
/* add char to pattern */
/* overflow check... */
if (p >= &pat[sizeof(pat) - 1]) {
x_e_putc(BEL);
continue;
}
*p++ = c, *p = '\0';
if (offset >= 0) {
/* already have partial match */
offset = x_match(xbuf, pat);
if (offset >= 0) {
x_goto(xbuf + offset + (p - pat) - (*pat == '^'));
continue;
}
}
offset = x_search(pat, offset);
} else { /* other command */
static char push[2];
push[0] = c;
macroptr = push; /* push command */
break;
}
}
if (offset < 0)
x_redraw(-1);
return KSTD;
}

/* search backward from current line */
static int
x_search(pat, offset)
char *pat;
int offset;
{
register char **hp;
int i;

for (hp = x_histp; --hp >= history; ) {
i = x_match(*hp, pat);
if (i >= 0) {
if (offset < 0)
x_e_putc('\n');
x_load_hist(hp);
x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
return i;
}
}
x_e_putc(BEL);
x_histp = histptr;
return -1;
}

/* return position of first match of pattern in string, else -1 */
static int
x_match(str, pat)
char *str, *pat;
{
if (*pat == '^') {
return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1;
} else {
char *q = strstr(str, pat);
return (q == NULL) ? -1 : q - str;
}
}

static int
x_del_line(c)
int c;
{
int i, j;

*xep = 0;
i = xep- xbuf;
j = x_size_str(xbuf);
xcp = xbuf;
x_push(i);
xlp = xbp = xep = xbuf;
xlp_valid = TRUE;
*xcp = 0;
xmp = NULL;
x_redraw(j);
return KSTD;
}

static int
x_mv_end(c)
int c;
{
x_goto(xep);
return KSTD;
}

static int
x_mv_begin(c)
int c;
{
x_goto(xbuf);
return KSTD;
}

static int
x_draw_line(c)
int c;
{
x_redraw(-1);
return KSTD;

}

static void
x_redraw(limit)
int limit;
{
int i, j;
char *cp;

x_adj_ok = 0;
if (limit == -1)
x_e_putc('\n');
else
x_e_putc('\r');
x_flush();
if (xbp == xbuf)
{
pprompt(prompt);
x_col = promptlen(prompt);
}
x_displen = x_cols - 2 - x_col;
xlp_valid = FALSE;
cp = x_lastcp();
x_zots(xbp);
if (xbp != xbuf || xep > xlp)
limit = x_cols;
if (limit >= 0)
{
if (xep > xlp)
i = 0; /* we fill the line */
else
i = limit - (xlp - xbp);

for (j = 0; j < i && x_col < (x_cols - 2); j++)
x_e_putc(' ');
i = ' ';
if (xep > xlp) /* more off screen */
{
if (xbp > xbuf)
i = '*';
else
i = '>';
}
else
if (xbp > xbuf)
i = '<';
x_e_putc(i);
j++;
while (j--)
x_e_putc('\b');
}
for (cp = xlp; cp > xcp; )
x_bs(*--cp);
x_adj_ok = 1;
_D_(x_flush();)
return;
}

static int
x_transpose(c)
int c;
{
char tmp;

/* What transpose is meant to do seems to be up for debate. This
* is a general summary of the options; the text is abcd with the
* upper case character or underscore indicating the cursor positiion:
* Who Before After Before After
* at&t ksh in emacs mode: abCd abdC abcd_ (bell)
* at&t ksh in gmacs mode: abCd baCd abcd_ abdc_
* gnu emacs: abCd acbD abcd_ abdc_
* Pdksh currently goes with GNU behavior since I believe this is the
* most common version of emacs, unless in gmacs mode, in which case
* it does the at&t ksh gmacs mdoe.
* This should really be broken up into 3 functions so users can bind
* to the one they want.
*/
if (xcp == xbuf) {
x_e_putc(BEL);
return KSTD;
} else if (xcp == xep || Flag(FGMACS)) {
if (xcp - xbuf == 1) {
x_e_putc(BEL);
return KSTD;
}
/* Gosling/Unipress emacs style: Swap two characters before the
* cursor, do not change cursor position
*/
x_bs(xcp[-1]);
x_bs(xcp[-2]);
x_zotc(xcp[-1]);
x_zotc(xcp[-2]);
tmp = xcp[-1];
xcp[-1] = xcp[-2];
xcp[-2] = tmp;
} else {
/* GNU emacs style: Swap the characters before and under the
* cursor, move cursor position along one.
*/
x_bs(xcp[-1]);
x_zotc(xcp[0]);
x_zotc(xcp[-1]);
tmp = xcp[-1];
xcp[-1] = xcp[0];
xcp[0] = tmp;
x_bs(xcp[0]);
x_goto(xcp + 1);
}
return KSTD;
}

static int
x_literal(c)
int c;
{
x_curprefix = -1;
return KSTD;
}

static int
x_meta1(c)
int c;
{
x_curprefix = 1;
return KPREF;
}

static int
x_meta2(c)
int c;
{
x_curprefix = 2;
return KPREF;
}

#ifdef OS2
static int
x_meta3(c)
int c;
{
x_curprefix = 3;
return KPREF;
}
#endif /* OS2 */

static int
x_kill(c)
int c;
{
int i;

i = xep - xcp;
xlp = xcp;
xlp_valid = TRUE;
x_push(i);
x_delete(i);
return KSTD;
}

static void
x_push(nchars)
int nchars;
{
char *cp = strnsave(xcp, nchars, AEDIT);
if (killstack[killsp])
afree((void *)killstack[killsp], AEDIT);
killstack[killsp] = cp;
killsp = (killsp + 1) % KILLSIZE;
}

static int
x_yank(c)
int c;
{
if (killsp == 0)
killtp = KILLSIZE;
else
killtp = killsp;
killtp --;
if (killstack[killtp] == 0) {
x_e_puts("\nnothing to yank");
x_redraw(-1);
return KSTD;
}
xmp = xcp;
x_ins(killstack[killtp]);
return KSTD;
}

static int
x_meta_yank(c)
int c;
{
int len;
if (x_last_command != x_yank && x_last_command != x_meta_yank) {
x_e_puts("\nyank something first");
x_redraw(-1);
return KSTD;
}
len = strlen(killstack[killtp]);
x_goto(xcp - len);
x_delete(len);
do {
if (killtp == 0)
killtp = KILLSIZE - 1;
else
killtp--;
} while (killstack[killtp] == 0);
x_ins(killstack[killtp]);
return KSTD;
}

static int
x_abort(c)
int c;
{
/* x_zotc(c); */
xlp = xep = xcp = xbp = xbuf;
xlp_valid = TRUE;
*xcp = 0;
return KINTR;
}

static int
x_error(c)
int c;
{
x_e_putc(BEL);
return KSTD;
}

static int
x_stuffreset(c)
int c;
{
#ifdef TIOCSTI
(void)x_stuff(c);
return KINTR;
#else
x_zotc(c);
xlp = xcp = xep = xbp = xbuf;
xlp_valid = TRUE;
*xcp = 0;
x_redraw(-1);
return KSTD;
#endif
}

static int
x_stuff(c)
int c;
{
#if 0 || defined TIOCSTI
char ch = c;
bool_t savmode = x_mode(FALSE);

(void)ioctl(TTY, TIOCSTI, &ch);
(void)x_mode(savmode);
x_redraw(-1);
#endif
return KSTD;
}

static void
x_mapin(cp)
char *cp;
{
char *op;

op = cp;
while (*cp) {
/* XXX -- should handle \^ escape? */
if (*cp == '^') {
cp++;
#ifdef OS2
if (*cp == '0') /* To define function keys */
*op++ = 0xE0;
else
#endif /* OS2 */
if (*cp >= '?') /* includes '?'; ASCII */
*op++ = CTRL(*cp);
else {
*op++ = '^';
cp--;
}
} else
*op++ = *cp;
cp++;
}
*op = '\0';
}

static char *
x_mapout(c)
int c;
{
static char buf[8];
register char *p = buf;

if (c < ' ' || c == 0x7F) { /* ASCII */
*p++ = '^';
*p++ = (c == 0x7F) ? '?' : (c | 0x40);
#ifdef OS2
} else if (c == 0xE0) {
*p++ = '^';
*p++ = '0';
#endif /* OS2 */
} else
*p++ = c;
*p = 0;
return buf;
}

static void
x_print(prefix, key)
int prefix, key;
{
if (prefix == 1)
shprintf("%s", x_mapout(x_prefix1));
if (prefix == 2)
shprintf("%s", x_mapout(x_prefix2));
#ifdef OS2
if (prefix == 3)
shprintf("%s", x_mapout(x_prefix3));
#endif /* OS2 */
shprintf("%s = ", x_mapout(key));
if (x_tab[prefix][key]->xf_func != x_ins_string)
shprintf("%s\n", x_tab[prefix][key]->xf_name);
else
shprintf("'%s'\n", x_atab[prefix][key]);
}

int
x_bind(a1, a2, macro)
char *a1, *a2;
int macro; /* bind -m */
{
struct x_ftab const *fp;
int prefix, key;
char *sp = NULL;

if (x_tab == NULL) {
bi_errorf("cannot bind, not a tty");
return 1;
}

if (a1 == NULL) {
for (prefix = 0; prefix < X_NTABS; prefix++)
for (key = 0; key < X_TABSZ; key++) {
fp = x_tab[prefix][key];
if (fp == NULL ||
fp->xf_func == x_insert || fp->xf_func == x_error)
continue;
x_print(prefix, key);
}
return 0;
}

x_mapin(a1);
prefix = key = 0;
for (;; a1++) {
key = *a1 & CHARMASK;
if (x_tab[prefix][key]->xf_func == x_meta1)
prefix = 1;
else if (x_tab[prefix][key]->xf_func == x_meta2)
prefix = 2;
#ifdef OS2
else if (x_tab[prefix][key]->xf_func == x_meta3)
prefix = 3;
#endif /* OS2 */
else
break;
}

if (a2 == NULL) {
x_print(prefix, key);
return 0;
}

if (*a2 == 0)
fp = xft_insert;
else if (!macro) {
for (fp = x_ftab; fp->xf_func; fp++)
if (strcmp(fp->xf_name, a2) == 0)
break;
if (fp->xf_func == NULL || (fp->xf_flags & XF_NOBIND)) {
bi_errorf("%s: no such function", a2);
return 1;
}
#if 0 /* This breaks the bind commands that map arrow keys */
if (fp->xf_func == x_meta1)
x_prefix1 = key;
if (fp->xf_func == x_meta2)
x_prefix2 = key;
#endif /* 0 */
} else {
fp = xft_ins_string;
x_mapin(a2);
sp = strsave(a2, AEDIT);
}

if ((x_tab[prefix][key]->xf_flags & XF_ALLOC) && x_atab[prefix][key])
afree((void *)x_atab[prefix][key], AEDIT);
x_tab[prefix][key] = fp;
x_atab[prefix][key] = sp;

return 0;
}

void
x_init_emacs()
{
register int i, j;
struct x_ftab const *fp;

ainit(AEDIT);

x_tab = (struct x_ftab const *(*)[X_TABSZ]) alloc(sizeofN(*x_tab, X_NTABS), AEDIT);
for (j = 0; j < X_TABSZ; j++)
x_tab[0][j] = xft_insert;
for (i = 1; i < X_NTABS; i++)
for (j = 0; j < X_TABSZ; j++)
x_tab[i][j] = xft_error;
for (fp = x_ftab; fp->xf_func; fp++)
if (fp->xf_db_char || fp->xf_db_tab)
x_tab[fp->xf_db_tab][fp->xf_db_char] = fp;

x_atab = (char *(*)[X_TABSZ]) alloc(sizeofN(*x_atab, X_NTABS), AEDIT);
for (i = 1; i < X_NTABS; i++)
for (j = 0; j < X_TABSZ; j++)
x_atab[i][j] = NULL;
}

void
x_emacs_keys(ec)
X_chars *ec;
{
x_tab[0][ec->erase] = xft_erase;
x_tab[0][ec->kill] = xft_kill;
x_tab[0][ec->werase] = xft_werase;
x_tab[0][ec->intr] = xft_intr;
x_tab[0][ec->quit] = xft_quit;
x_tab[1][ec->erase] = xft_werase;
}

static int
x_set_mark(c)
int c;
{
xmp = xcp;
return KSTD;
}

static int
x_kill_region(c)
int c;
{
int rsize;
char *xr;

if (xmp == NULL) {
x_e_putc(BEL);
return KSTD;
}
if (xmp > xcp) {
rsize = xmp - xcp;
xr = xcp;
} else {
rsize = xcp - xmp;
xr = xmp;
}
x_goto(xr);
x_push(rsize);
x_delete(rsize);
xmp = xr;
return KSTD;
}

static int
x_xchg_point_mark(c)
int c;
{
char *tmp;

if (xmp == NULL) {
x_e_putc(BEL);
return KSTD;
}
tmp = xmp;
xmp = xcp;
x_goto( tmp );
return KSTD;
}

static int
x_version(c)
int c;
{
static char push[2];
extern char ksh_version[];
char *o_xbuf = xbuf, *o_xend = xend;
char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
int lim = x_lastcp() - xbp;

xbuf = xbp = xcp = ksh_version + 4;
xend = xep = ksh_version + 4 + strlen(ksh_version + 4);
x_redraw(lim);
x_flush();

c = x_e_getc();
xbuf = o_xbuf;
xend = o_xend;
xbp = o_xbp;
xep = o_xep;
xcp = o_xcp;
x_redraw(strlen(ksh_version));

if (c < 0)
return KSTD;
push[0] = c;
macroptr = push; /* push command */
return KSTD;
}

static int
x_noop(c)
int c;
{
return KNULL;
}

#ifdef SILLY
static int
x_game_of_life(c)
int c;
{
char newbuf [256+1];
register char *ip, *op;
int i, len;

i = xep - xbuf;
*xep = 0;
len = x_size_str(xbuf);
xcp = xbp = xbuf;
memmove(newbuf+1, xbuf, i);
newbuf[0] = 'A';
newbuf[i] = 'A';
for (ip = newbuf+1, op = xbuf; --i >= 0; ip++, op++) {
/* Empty space */
if (*ip < '@' || *ip == '_' || *ip == 0x7F) {
/* Two adults, make whoopee */
if (ip[-1] < '_' && ip[1] < '_') {
/* Make kid look like parents. */
*op = '`' + ((ip[-1] + ip[1])/2)%32;
if (*op == 0x7F) /* Birth defect */
*op = '`';
}
else
*op = ' '; /* nothing happens */
continue;
}
/* Child */
if (*ip > '`') {
/* All alone, dies */
if (ip[-1] == ' ' && ip[1] == ' ')
*op = ' ';
else /* Gets older */
*op = *ip-'`'+'@';
continue;
}
/* Adult */
/* Overcrowded, dies */
if (ip[-1] >= '@' && ip[1] >= '@') {
*op = ' ';
continue;
}
*op = *ip;
}
*op = 0;
x_redraw(len);
return KSTD;
}
#endif

/*
* File/command name completion routines
*/

/* type: 0 for list, 1 for completion */

static XPtrV words;

static void
add_stash(dirnam, name)
char *dirnam; /* directory name, if file */
char *name;
{
char *cp;
register int type = 0; /* '*' if executable, '/' if directory, else 0 */
register int len = strlen(name);

/* determine file type */
if (dirnam) {
struct stat statb;
char *buf = alloc((size_t)(strlen(dirnam)+len+2), ATEMP);

if (strcmp(dirnam, ".") == 0)
*buf = '\0';
else if (strcmp(dirnam, slash) == 0)
(void)strcpy(buf, slash);
else
(void)strcat(strcpy(buf, dirnam), slash);
(void)strcat(buf, name);
if (stat(buf, &statb)==0)
if (S_ISDIR(statb.st_mode))
type = DIRSEP;
else if (S_ISREG(statb.st_mode) && access(buf, X_OK)==0)
type = '*';
if (type)
++len;
afree((void *)buf, ATEMP);
}

if (len > x_maxlen)
x_maxlen = len;

/* stash name for later sorting */
cp = strnsave(name, len, ATEMP);
if (dirnam && type) { /* append file type indicator */
cp[len-1] = type;
cp[len] = '\0';
}
XPput(words, cp);
}

static void
list_stash()
{
register char **array, **record;
int items = 0, tabstop, loc, nrows, jump, offset;

items = XPsize(words);
array = (char**) XPptrv(words);
if (items == 0)
return;
qsortp(XPptrv(words), (size_t)XPsize(words), xstrcmp);

/* print names */
x_maxlen = (x_maxlen/8 + 1) * 8; /* column width */
nrows = (items-1) / (x_cols/x_maxlen) + 1;
for (offset = 0; offset < nrows; ++offset) {
tabstop = loc = 0;
x_e_putc('\n');
for (jump = 0; offset+jump < items; jump += nrows) {
if (jump)
while (loc < tabstop) {
x_e_putc('\t');
loc = (loc/8 + 1) * 8;
}
record = array + (offset + jump);
x_e_puts(*record);
loc += strlen(*record);
tabstop += x_maxlen; /* next tab stop */
afree((void *)*record, ATEMP);
}
}

afree((void*)array, ATEMP);
x_redraw(-1);
}

static int
x_comp_comm(c)
int c;
{
compl_command(1);
return KSTD;
}
static int
x_list_comm(c)
int c;
{
compl_command(0);
return KSTD;
}
static int
x_complete(c)
int c;
{
compl_dec(1);
return KSTD;
}
static int
x_enumerate(c)
int c;
{
compl_dec(0);
return KSTD;
}
static int
x_comp_file(c)
int c;
{
compl_file(1);
return KSTD;
}
static int
x_list_file(c)
int c;
{
compl_file(0);
return KSTD;
}
static int
x_comp_list(c)
int c;
{
compl_dec(2);
return KSTD;
}

static void
compl_dec(type)
int type;
{
char *cp = xcp;
int have_word;

/* find start of word */
while (cp != xbuf && !iscfs(cp[-1]))
--cp;

have_word = cp < xep && !iscfs(*cp);

/* skip any space before word */
while (cp != xbuf && iscfs(cp[-1]))
--cp;

/* XXX strchr is not good here - will go past the first word */
if (have_word && cp == xbuf && strchr_dirsep(cp) == NULL)
compl_command(type);
else
compl_file(type);
}

static void
compl_file(type)
int type;
{
char *str;
register char *cp, *xp;
char *lastp;
char *dirnam;
char *buf;
char *bug;
int buglen;
DIR *dirp;
struct dirent *dp;
long loc = -1;
int len;
int multi = 0;

/* type == 0 for list, 1 for complete and 2 for complete-list */
str = xcp;
xp = str;
while (xp != xbuf) {
--xp;
if (iscfs(*xp)) {
xp++;
break;
}
}
if (digit(*xp) && (xp[1] == '<' || xp[1] == '>'))
xp++;
while (*xp == '<' || *xp == '>')
xp++;
if (type) { /* for complete */
while (*xcp && !iscfs(*xcp))
x_zotc(*xcp++);
}
if (type != 1) { /* for list */
x_maxlen = 0;
XPinit(words, 16);
}
cp = xp;
while (*xp && !iscfs(*xp))
xp++;
cp = strnsave(cp, xp - cp, ATEMP);

buf = substitute(cp, DOTILDE);
afree((void*)cp, ATEMP);
lastp = strrchr_dirsep(buf);
if (lastp)
*lastp = '\0';

dirnam = (lastp == NULL) ? "." : (lastp == buf) ? slash : buf;
dirp = ksh_opendir(dirnam);
if (dirp == NULL) {
x_e_putc(BEL);
afree((void *) buf, ATEMP);
return;
}

if (lastp == NULL)
lastp = buf;
else
lastp++;
len = strlen(lastp);

bug = alloc(buglen = 64, ATEMP);
while ((dp = readdir(dirp)) != NULL) {
cp = dp->d_name;
if (cp[0] == '.' &&
(cp[1] == '\0' || (cp[1] == '.' && cp[2] == '\0')))
continue; /* always ignore . and .. */
if (strncmp(lastp, cp, len) == 0) {
if (type) { /* for complete */
if (loc == -1) {
loc = NLENGTH(dp);
while (len >= buglen) {
buglen *= 2;
bug = aresize(bug, buglen,
ATEMP);
}
(void)strcpy(bug, cp);
} else {
multi = 1;
loc = strmatch(bug, cp);
bug[loc] = '\0';
}
}
if (type != 1) { /* for list */
add_stash(dirnam, cp);
}
}
}
(void)closedir(dirp);

if (type) { /* for complete */
if (loc < 0 ||
(loc == 0 && type != 2)) {
x_e_putc(BEL);
afree((void *) buf, ATEMP);
afree((void *) bug, ATEMP);
return;
}
x_ins(bug + len);
if (!multi) {
struct stat statb;
int ret;

if (lastp == buf)
ret = stat(bug, &statb);
else {
char *tbuf = strnsave(dirnam, strlen(dirnam)
+ 1 + strlen(bug) + 1, ATEMP);
if (lastp != buf + 1)
strcat(tbuf, slash);
strcat(tbuf, bug);
ret = stat(tbuf, &statb);
afree((void *) tbuf, ATEMP);
}
if (ret >= 0 && S_ISDIR(statb.st_mode))
x_ins(slash);
else
x_ins(space);
}
}
afree((void *) buf, ATEMP);
afree((void *) bug, ATEMP);
if (type == 0 || /* if list */
(type == 2 && multi)) { /* or complete-list and ambiguous */
list_stash();
}
}

static void
compl_command(type)
int type;
{
register struct tbl *tp;
char *str;
char *buf;
char *bug;
int buglen;
char *xp;
char *cp;
int len;
int multi;
int loc;

/* type == 0 for list, 1 for complete and 2 for complete-list */
str = xcp;
xp = str;
while (xp != xbuf) {
--xp;
if (iscfs(*xp)) {
xp++;
break;
}
}
if (type) /* for complete */
while (*xcp && !iscfs(*xcp))
x_zotc(*xcp++);
if (type != 1) { /* for list */
x_maxlen = 0;
XPinit(words, 16);
}
cp = xp;
while (*xp && !iscfs(*xp))
xp++;
buf = strnsave(cp, xp - cp, ATEMP);

len = strlen(buf);
loc = -1;
multi = 0;

bug = alloc(buglen = 64, ATEMP);
for (twalk(&taliases); (tp = tnext()) != NULL; ) {
int klen;

if (!(tp->flag&ISSET))
continue;
klen = strlen(tp->name);
if (klen < len)
continue;
if (strncmp(buf, tp->name, len) ==0) {
if (type) { /* for complete */
if (loc == -1) {
loc = klen;
while (len >= buglen) {
buglen *= 2;
bug = aresize(bug, buglen,
ATEMP);
}
(void)strcpy(bug, tp->name);
} else {
multi = 1;
loc = strmatch(bug, tp->name);
bug[loc] = '\0';
}
}
if (type != 1) { /* for list */
add_stash((char *)0, tp->name);
}
}
}
afree((void *) buf, ATEMP);

if (type) { /* for complete */
if (loc < 0 ||
(loc == 0 && type != 2)) {
x_e_putc(BEL);
afree((void *) bug, ATEMP);
return;
}
x_ins(bug + len);
if (!multi)
x_ins(space);
}
afree((void *) bug, ATEMP);

if (type == 0 /* if list */
|| (type == 2 && multi)) /* or complete-list and ambiguous */
list_stash();
}

static int
strmatch(s1, s2)
register char *s1, *s2;
{
register char *p;

for (p = s1; *p == *s2++ && *p != 0; p++)
;
return p - s1;
}

/* NAME:
* x_adjust - redraw the line adjusting starting point etc.
*
* DESCRIPTION:
* This function is called when we have exceeded the bounds
* of the edit window. It increments x_adj_done so that
* functions like x_ins and x_delete know that we have been
* called and can skip the x_bs() stuff which has already
* been done by x_redraw.
*
* RETURN VALUE:
* None
*/

static void
x_adjust()
{
x_adj_done++; /* flag the fact that we were called. */
/*
* we had a problem if the prompt length > x_cols / 2
*/
if ((xbp = xcp - (x_displen / 2)) < xbuf)
xbp = xbuf;
xlp_valid = FALSE;
x_redraw(x_cols);
x_flush();
}

static int
x_e_getc()
{
int c = x_getc();

return c <= CHARMASK ? c : (c & CHARMASK);
}

static void
x_e_putc(c)
int c;
{
if (c == '\r' || c == '\n')
x_col = 0;
if (x_col < x_cols)
{
x_putc(c);
switch(c)
{
case BEL:
break;
case '\r':
case '\n':
break;
case '\b':
x_col--;
break;
default:
x_col++;
break;
}
}
if (x_adj_ok && (x_col < 0 || x_col >= (x_cols - 2)))
{
x_adjust();
}
}

#ifdef DEBUG
static int
x_debug_info()
{
x_flush();
printf("\nksh debug:\n");
printf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n",
x_col, x_cols, x_displen);
printf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep);
printf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf);
printf("\txlp == 0x%lx\n", (long) xlp);
printf("\txlp == 0x%lx\n", (long) x_lastcp());
printf(newline);
x_redraw(-1);
return 0;
}
#endif

static void
x_e_puts(s)
register char *s;
{
register int adj = x_adj_done;

while (*s && adj == x_adj_done)
x_e_putc(*s++);
}

/* NAME:
* x_set_arg - set an arg value for next function
*
* DESCRIPTION:
* This is a simple implementation of M-[0-9].
*
* RETURN VALUE:
* KSTD
*/

static int
x_set_arg(c)
int c;
{
if ((x_arg = (c &= CHARMASK) - '0') < 0 || x_arg > 9)
{
x_arg = 1;
x_e_putc(BEL);
}
return KSTD;
}


/* NAME:
* x_prev_histword - recover word from prev command
*
* DESCRIPTION:
* This function recovers the last word from the previous
* command and inserts it into the current edit line. If a
* numeric arg is supplied then the n'th word from the
* start of the previous command is used.
*
* Bound to M-.
*
* RETURN VALUE:
* KSTD
*/

static int
x_prev_histword()
{
register char *rcp;
char *cp;
char **hp;

hp = x_histp-1;
if (hp < history || hp > histptr)
{
x_e_putc(BEL);
return KSTD;
}
cp = *hp;
if (x_last_command != x_set_arg)
{
rcp = &cp[strlen(cp) - 1];
/*
* ignore white-space after the last word
*/
while (rcp > cp && iscfs(*rcp))
rcp--;
while (rcp > cp && !iscfs(*rcp))
rcp--;
if (iscfs(*rcp))
rcp++;
x_ins(rcp);
}
else
{
int c;

rcp = cp;
/*
* ignore white-space at start of line
*/
while (*rcp && iscfs(*rcp))
rcp++;
while (x_arg-- > 1)
{
while (*rcp && !iscfs(*rcp))
rcp++;
while (*rcp && iscfs(*rcp))
rcp++;
}
cp = rcp;
while (*rcp && !iscfs(*rcp))
rcp++;
c = *rcp;
*rcp = '\0';
x_ins(cp);
*rcp = c;
}
return KSTD;
}

/* NAME:
* x_fold_case - convert word to UPPER/lower case
*
* DESCRIPTION:
* This function is used to implement M-u,M-l and M-c
* to upper case, lower case or Capitalize words.
*
* RETURN VALUE:
* None
*/

static int
x_fold_case(c)
int c;
{
register char *cp = xcp;

if (cp == xep)
{
x_e_putc(BEL);
return 0;
}
c &= 0137; /* strip prefixes and case */
if (x_last_command != x_set_arg)
x_arg = 1;
while (x_arg--)
{
/*
* fisrt skip over any white-space
*/
while (cp != xep && ismfs(*cp))
{
cp++;
}
/*
* do the first char on its own since it may be
* a different action than for the rest.
*/
if (cp != xep)
{
if (c == 'L') /* M-l */
{
if (isupper(*cp))
*cp = tolower(*cp);
}
else /* M-u or M-c */
{
if (islower(*cp))
*cp = toupper(*cp);
}
cp++;
}
/*
* now for the rest of the word
*/
while (cp != xep && !ismfs(*cp))
{
if (c == 'U') /* M-u */
{
if (islower(*cp))
*cp = toupper(*cp);
}
else /* M-l or M-c */
{
if (isupper(*cp))
*cp = tolower(*cp);
}
cp++;
}
}
x_goto(cp);
return 0;
}

/* NAME:
* x_lastcp - last visible char
*
* SYNOPSIS:
* x_lastcp()
*
* DESCRIPTION:
* This function returns a pointer to that char in the
* edit buffer that will be the last displayed on the
* screen. The sequence:
*
* for (cp = x_lastcp(); cp > xcp; cp)
* x_bs(*--cp);
*
* Will position the cursor correctly on the screen.
*
* RETURN VALUE:
* cp or NULL
*/

static char *
x_lastcp()
{
register char *rcp;
register int i;

if (!xlp_valid)
{
for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++)
i += x_size(*rcp);
xlp = rcp;
}
xlp_valid = TRUE;
return (xlp);
}

#endif /* EDIT */
pdksh-5.1.2/eval.c100644 0 133 70144 5670630535 12601 0ustar rootlsource/*
* Expansion - quoting, separation, substitution, globbing
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: eval.c,v 1.4 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include
#include "ksh_dir.h"
#include "ksh_stat.h"

/*
* string expansion
*
* first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
* second pass: alternation ({,}), filename expansion (*?[]).
*/

/* expansion generator state */
typedef struct Expand {
/* int type; */ /* see expand() */
char *str; /* string */
union {
char **strv; /* string[] */
struct shf *shf;/* file */
} u; /* source */
struct tbl *var; /* variable in ${var..} */
short split; /* split "$@" / call waitlast $() */
} Expand;

#define XBASE 0 /* scanning original */
#define XSUB 1 /* expanding ${} string */
#define XARGSEP 2 /* ifs0 between "$*" */
#define XARG 3 /* expanding $*, $@ */
#define XCOM 4 /* expanding $() */
#define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */

/* States used for field splitting */
#define IFS_WORD 0 /* word has chars (or quotes) */
#define IFS_WS 1 /* have seen IFS white-space */
#define IFS_NWS 2 /* have seen IFS non-white-space */

static int varsub ARGS((Expand *xp, char *sp, char *word, int *stypep));
static int comsub ARGS((Expand *xp, char *cp));
static char *trimsub ARGS((char *str, char *pat, int how));
static void glob ARGS((char *cp, XPtrV *wp));
static void globit ARGS((XString *xs, char **xpp, char *sp, XPtrV *wp,
int check));
static int copy_non_glob ARGS((XString *xs, char **xpp, char *p));
static char *debunk ARGS((char *cp));
static char *maybe_expand_tilde ARGS((char *p, XString *dsp, char **dpp,
int isassign));
static char *tilde ARGS((char *acp));
static char *homedir ARGS((char *name));
#ifdef BRACEEXPAND
static void alt_expand ARGS((XPtrV *wp, char *start, char *exp_start,
char *end, int fdo));
#endif

/* compile and expand word */
char *
substitute(cp, f)
const char *cp;
int f;
{
struct source *s, *sold;

sold = source;
s = pushs(SWSTR, ATEMP);
s->str = (char *) cp;
source = s;
if (yylex(ONEWORD) != LWORD)
internal_errorf(1, "substitute");
source = sold;
afree(s, ATEMP);
return evalstr(yylval.cp, f);
}

/*
* expand arg-list
*/
char **
eval(ap, f)
register char **ap;
int f;
{
XPtrV w;

if (*ap == NULL)
return ap;
XPinit(w, 32);
XPput(w, NULL); /* space for shell name */
#ifdef SHARPBANG
XPput(w, NULL); /* and space for one arg */
#endif
while (*ap != NULL)
expand(*ap++, &w, f);
XPput(w, NULL);
#ifdef SHARPBANG
return (char **) XPclose(w) + 2;
#else
return (char **) XPclose(w) + 1;
#endif
}

/*
* expand string
*/
char *
evalstr(cp, f)
char *cp;
int f;
{
XPtrV w;

XPinit(w, 1);
expand(cp, &w, f);
cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w);
XPfree(w);
return cp;
}

/*
* expand string - return only one component
* used from iosetup to expand redirection files
*/
char *
evalonestr(cp, f)
register char *cp;
int f;
{
XPtrV w;

XPinit(w, 1);
expand(cp, &w, f);
switch (XPsize(w)) {
case 0:
cp = null;
break;
case 1:
cp = (char*) *XPptrv(w);
break;
default:
cp = evalstr(cp, f&~DOGLOB);
break;
}
XPfree(w);
return cp;
}

/* for nested substitution: ${var:=$var2} */
typedef struct SubType {
short stype; /* [=+-?%#] action after expanded word */
short base; /* begin position of expanded word */
short f; /* saved value of f (DOPAT, etc) */
struct tbl *var; /* variable for ${var..} */
short quote; /* saved value of quote (for ${..[%#]..}) */
struct SubType *prev; /* old type */
struct SubType *next; /* poped type (to avoid re-allocating) */
} SubType;

void
expand(cp, wp, f)
char *cp; /* input word */
register XPtrV *wp; /* output words */
int f; /* DO* flags */
{
register int UNINITIALIZED(c);
register int type; /* expansion type */
register int quote = 0; /* quoted */
XString ds; /* destination string */
register char *dp, *sp; /* dest., source */
int fdo, word; /* second pass flags; have word */
int doblank; /* field spliting of parameter/command subst */
Expand x; /* expansion variables */
SubType st_head, *st;
int UNINITIALIZED(newlines); /* For trailing newlines in COMSUB */
int saw_eq, tilde_ok;

if (cp == NULL)
internal_errorf(1, "expand(NULL)");
if (Flag(FNOGLOB))
f &= ~DOGLOB;
#ifdef BRACEEXPAND
if (Flag(FBRACEEXPAND) && (f & DOGLOB))
f |= DOBRACE_;
#endif /* BRACEEXPAND */

Xinit(ds, dp, 128, ATEMP); /* init dest. string */
type = XBASE;
sp = cp;
fdo = 0;
saw_eq = 0;
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */
doblank = 0;
word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
st_head.next = (SubType *) 0;
st = &st_head;

while (1) {
Xcheck(ds, dp);

switch (type) {
case XBASE: /* original prefixed string */
c = *sp++;
switch (c) {
case EOS:
c = 0;
break;
case CHAR:
c = *sp++;
break;
case QCHAR:
quote |= 2; /* temporary quote */
c = *sp++;
break;
case OQUOTE:
word = IFS_WORD;
tilde_ok = 0;
quote = 1;
continue;
case CQUOTE:
quote = 0;
continue;
case COMSUB:
tilde_ok = 0;
if (f & DONTRUNCOMMAND) {
word = IFS_WORD;
*dp++ = '$'; *dp++ = '(';
while (*sp != '\0') {
Xcheck(ds, dp);
*dp++ = *sp++;
}
*dp++ = ')';
} else {
type = comsub(&x, sp);
if (type == XCOM && (f&DOBLANK))
doblank++;
sp = strchr(sp, 0) + 1;
newlines = 0;
}
continue;
case EXPRSUB:
word = IFS_WORD;
tilde_ok = 0;
if (f & DONTRUNCOMMAND) {
*dp++ = '$'; *dp++ = '('; *dp++ = '(';
while (*sp != '\0') {
Xcheck(ds, dp);
*dp++ = *sp++;
}
*dp++ = ')'; *dp++ = ')';
} else {
struct tbl v;
char *p;

v.flag = DEFINED|ISSET|INTEGER;
v.type = 0;
v.name[0] = '\0';
v.val.i = evaluate(substitute(sp, 0));
sp = strchr(sp, 0) + 1;
for (p = strval(&v); *p; ) {
Xcheck(ds, dp);
*dp++ = *p++;
}
}
continue;
case OSUBST: /* ${{#}var{:}[=+-?#%]word} */
/* format is:
* OSUBST plain-variable-part \0
* compiled-word-part CSUBST
* This is were all syntax checking gets done...
*/
{
char *varname = sp;
int stype;

sp = strchr(sp, '\0') + 1; /* skip variable */
type = varsub(&x, varname, sp, &stype);
if (type < 0) {
char endc;
char *str, *end;

end = (char *) wdscan(sp, CSUBST);
endc = *end;
*end = EOS;
str = snptreef((char *) 0, 64, "%S",
varname - 1);
*end = endc;
errorf("%s: bad substitution", str);
}
if (f&DOBLANK)
doblank++;
tilde_ok = 0;
if (type == XBASE) { /* expand? */
if (!st->next) {
SubType *newst;

newst = (SubType *) alloc(
sizeof(SubType), ATEMP);
newst->next = (SubType *) 0;
newst->prev = st;
st->next = newst;
}
st = st->next;
st->stype = stype;
st->base = Xsavepos(ds, dp);
st->f = f;
st->var = x.var;
st->quote = quote;
/* skip qualifier(s) */
if (stype) {
sp += 2;
/* :[-+=?] or double [#%] */
if (stype & 0x80)
sp += 2;
}
switch (stype & 0x7f) {
case '#':
case '%':
/* ! DOBLANK,DOBRACE_,DOTILDE */
f = DOPAT | (f&DONTRUNCOMMAND)
| DOTEMP_;
quote = 0;
break;
case '=':
/* Enabling tilde expansion
* after :'s here is
* non-standard ksh, but is
* consistent with rules for
* other assignments. Not
* sure what POSIX thinks of
* this.
* Not doing tilde expansion
* for integer variables is a
* non-POSIX thing - makes
* sense though, since ~ is
* a arithmetic operator.
*/
#if !defined(__hppa) || __GNUC__ != 2 /* gcc 2.3.3 on hp-pa dies on this - ifdef goes away as soon as I get a new version of gcc.. */
if (!(x.var->flag & INTEGER))
f |= DOASNTILDE|DOTILDE;
f |= DOTEMP_;
#else
f |= DOTEMP_|DOASNTILDE|DOTILDE;
#endif
/* These will be done after the
* value has been assigned.
*/
f &= ~(DOBLANK|DOGLOB|DOBRACE_);
tilde_ok = 1;
break;
case '?':
f &= ~DOBLANK;
f |= DOTEMP_;
/* fall through */
default:
/* Enable tilde expansion */
tilde_ok = 1;
f |= DOTILDE;
}
} else
/* skip word */
sp = (char *) wdscan(sp, CSUBST);
continue;
}
case CSUBST: /* only get here if expanding word */
tilde_ok = 0; /* in case of ${unset:-} */
*dp = '\0';
quote = st->quote;
f = st->f;
if (f&DOBLANK)
doblank--;
switch (st->stype&0x7f) {
case '#':
case '%':
dp = Xrestpos(ds, dp, st->base);
/* Must use st->var since calling
* global would break things
* like x[i+=1].
*/
x.str = trimsub(strval(st->var),
dp, st->stype);
type = XSUB;
if (f&DOBLANK)
doblank++;
st = st->prev;
continue;
case '=':
if (st->var->flag & RDONLY)
/* XXX POSIX says this is only
* fatal for special builtins
*/
errorf("%s: is read only",
st->var->name);
/* Restore our position and substitute
* the value of st->var (may not be
* the assigned value in the presence
* of integer/right-adj/etc attributes).
*/
dp = Xrestpos(ds, dp, st->base);
/* Must use st->var since calling
* global would cause with things
* like x[i+=1].
*/
setstr(st->var, debunk(strsave(dp,
ATEMP)));
x.str = strval(st->var);
type = XSUB;
if (f&DOBLANK)
doblank++;
st = st->prev;
continue;
case '?':
{
char *s = Xrestpos(ds, dp, st->base);

if (dp == s)
s = "parameter null or not set";
else
s = debunk(s);
errorf("%s: %s", st->var->name, s);
}
}
st = st->prev;
type = XBASE;
continue;
}
break;

case XNULLSUB:
/* Special case for "$@" (and "${foo[@]}") - no
* word is generated if $# is 0 (unless there is
* other stuff inside the quotes).
*/
type = XBASE;
if (f&DOBLANK) {
doblank--;
/* not really correct: x=; "$x$@" should
* generate a null argument and
* set A; "${@:+}" shouldn't.
*/
if (dp == Xstring(ds, dp))
word = IFS_WS;
}
continue;

case XSUB:
if ((c = *x.str++) == 0) {
type = XBASE;
if (f&DOBLANK)
doblank--;
continue;
}
break;

case XARGSEP:
type = XARG;
quote = 1;
case XARG:
if ((c = *x.str++) == '\0') {
/* force null words to be created so
* set -- '' 2 ''; foo "$@" will do
* the right thing
*/
if (quote && x.split)
word = IFS_WORD;
if ((x.str = *x.u.strv++) == NULL) {
type = XBASE;
if (f&DOBLANK)
doblank--;
continue;
}
c = ifs0;
if (c == 0) {
if (quote && !x.split)
continue;
c = ' ';
}
if (quote && x.split) {
/* terminate word for "$@" */
type = XARGSEP;
quote = 0;
}
}
break;

case XCOM:
if (newlines) { /* Spit out saved nl's */
c = '\n';
--newlines;
} else {
while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
if (c == '\n')
newlines++; /* Save newlines */
if (newlines && c != EOF) {
shf_ungetc(c, x.u.shf);
c = '\n';
--newlines;
}
}
if (c == EOF) {
newlines = 0;
shf_close(x.u.shf);
if (x.split)
subst_exstat = waitlast();
type = XBASE;
if (f&DOBLANK)
doblank--;
continue;
}
break;
}

/* check for end of word or IFS separation */
if (c == 0 || (!quote && (f & DOBLANK) && doblank
&& ctype(c, C_IFS)))
{
/* How words are broken up:
* | value of c
* word | ws nws 0
* -----------------------------------
* IFS_WORD w/WS w/NWS w
* IFS_WS -/WS w/NWS -
* IFS_NWS -/NWS w/NWS w
* (w means generate a word)
* Note that IFS_NWS/0 generates a word (at&t ksh
* doesn't do this, but POSIX does).
*/
if (word == IFS_WORD
|| (!ctype(c, C_IFSWS) && (c || word == IFS_NWS)))
{
char *p;

*dp++ = '\0';
p = Xclose(ds, dp);
#ifdef BRACEEXPAND
if (fdo & DOBRACE_)
/* also does globing */
alt_expand(wp, p, p,
p + Xlength(ds, (dp - 1)),
fdo);
else
#endif /* BRACEEXPAND */
if (fdo & DOGLOB)
glob(p, wp);
else if ((f & DOPAT) || !(fdo & DOMAGIC_))
XPput(*wp, p);
else
XPput(*wp, debunk(p));
fdo = 0;
saw_eq = 0;
tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
if (c != 0)
Xinit(ds, dp, 128, ATEMP);
}
if (c == 0)
return;
if (word != IFS_NWS)
word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
} else {
/* age tilde_ok info - ~ code tests second bit */
tilde_ok <<= 1;
/* mark any special second pass chars */
if (!quote)
switch (c) {
case '[':
case NOT:
case '-':
case ']':
/* For character classes - doesn't hurt
* to have magic !,-,]'s outside of
* [...] expressions.
*/
if (f & (DOPAT | DOGLOB)) {
fdo |= DOMAGIC_;
if (c == '[')
fdo |= f & DOGLOB;
*dp++ = MAGIC;
}
break;
case '*':
case '?':
if (f & (DOPAT | DOGLOB)) {
fdo |= DOMAGIC_ | (f & DOGLOB);
*dp++ = MAGIC;
}
break;
#ifdef BRACEEXPAND
case OBRACE:
case ',':
case CBRACE:
if ((f & DOBRACE_) && (c == OBRACE
|| (fdo & DOBRACE_)))
{
fdo |= DOBRACE_|DOMAGIC_;
*dp++ = MAGIC;
}
break;
#endif /* BRACEEXPAND */
case '=':
/* Note first unquoted = for ~ */
if (!(f & DOTEMP_) && !saw_eq) {
saw_eq = 1;
tilde_ok = 1;
}
break;
case PATHSEP: /* : */
/* Note unquoted : for ~ */
if (!(f & DOTEMP_) && (f & DOASNTILDE))
tilde_ok = 1;
break;
case '~':
/* tilde_ok is reset whenever
* any of ' " $( $(( ${ } are seen.
* Note that tilde_ok must be preserved
* through the sequence ${A=a=}~
*/
if (type == XBASE
&& (f & (DOTILDE|DOASNTILDE))
&& (tilde_ok & 2))
{
char *p, *dp_x;

dp_x = dp;
p = maybe_expand_tilde(sp,
&ds, &dp_x,
f & DOASNTILDE);
if (p) {
if (dp != dp_x)
word = IFS_WORD;
dp = dp_x;
sp = p;
continue;
}
}
break;
}
else
quote &= ~2; /* undo temporary */

word = IFS_WORD;
if ((char) c == MAGIC) {
fdo |= DOMAGIC_;
*dp++ = MAGIC;
}
*dp++ = c; /* save output char */
}
}
}

/*
* Prepare to generate the string returned by ${} substitution.
*/
static int
varsub(xp, sp, word, stypep)
Expand *xp;
char *sp;
char *word;
int *stypep;
{
int c;
int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
int stype; /* substitution type */
char *p;
struct tbl *vp;

if (sp[0] == '\0') /* Bad variable name */
return -1;

xp->var = (struct tbl *) 0;

/* ${#var}, string length or array size */
if (sp[0] == '#' && (c = sp[1]) != '\0') {
int zero_ok = 0;

/* Can't have any modifiers for ${#...} */
if (*word != CSUBST)
return -1;
sp++;
/* Check for size of array */
if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
c = 0;
vp = global(basename(sp));
if (vp->flag & (ISSET|ARRAY))
zero_ok = 1;
for (; vp; vp = vp->array)
if (vp->flag&ISSET)
c = vp->index+1;
} else if (c == '*' || c == '@')
c = e->loc->argc;
else {
p = strval(global(sp));
zero_ok = p != null;
c = strlen(p);
}
if (Flag(FNOUNSET) && c == 0 && !zero_ok)
errorf("%s: unset variable", sp);
*stypep = 0; /* unqualified variable/string substitution */
xp->str = strsave(ulton((unsigned long)c, 10), ATEMP);
return XSUB;
}

/* Check for qualifiers in word part */
stype = 0;
c = *word == CHAR ? word[1] : 0;
if (c == ':') {
stype = 0x80;
c = word[2] == CHAR ? word[3] : 0;
}
if (ctype(c, C_SUBOP1))
stype |= c;
else if (stype) /* :, :# or :% is not ok */
return -1;
else if (ctype(c, C_SUBOP2)) {
stype = c;
if (word[2] == CHAR && c == word[3])
stype |= 0x80;
}
if (!stype && *word != CSUBST)
return -1;
*stypep = stype;

c = sp[0];
if (c == '*' || c == '@') {
switch (stype & 0x7f) {
case '=': /* can't assign to a vector */
case '%': /* can't trim a vector */
case '#':
return -1;
}
if (e->loc->argc == 0) {
xp->str = null;
state = c == '@' ? XNULLSUB : XSUB;
} else {
xp->u.strv = e->loc->argv + 1;
xp->str = *xp->u.strv++;
xp->split = c == '@'; /* $@ */
state = XARG;
}
} else {
if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
XPtrV wv;

switch (stype & 0x7f) {
case '=': /* can't assign to a vector */
case '%': /* can't trim a vector */
case '#':
return -1;
}
XPinit(wv, 32);
vp = global(basename(sp));
for (; vp; vp = vp->array) {
if (!(vp->flag&ISSET))
continue;
XPput(wv, strval(vp));
}
if (XPsize(wv) == 0) {
xp->str = null;
state = p[1] == '@' ? XNULLSUB : XSUB;
XPfree(wv);
} else {
XPput(wv, 0);
xp->u.strv = (char **) XPptrv(wv);
xp->str = *xp->u.strv++;
xp->split = p[1] == '@'; /* ${foo[@]} */
state = XARG;
}
} else {
/* Can't assign things like $! or $1 */
if ((stype & 0x7f) == '='
&& (ctype(*sp, C_VAR1) || digit(*sp)))
return -1;
xp->var = global(sp);
xp->str = strval(xp->var);
state = XSUB;
}
}

c = stype&0x7f;
/* test the compiler's code generator */
if (ctype(c, C_SUBOP2) ||
(((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
c == '=' || c == '-' || c == '?' : c == '+'))
state = XBASE; /* expand word instead of variable value */
if (Flag(FNOUNSET) && (ctype(c, C_SUBOP2)
|| (state != XBASE && xp->str == null
&& c != '+')))
errorf("%s: unset variable", sp);
return state;
}

/*
* Run the command in $(...) and read its output.
*/
static int
comsub(xp, cp)
register Expand *xp;
char *cp;
{
Source *s, *sold;
register struct op *t;
struct shf *shf;

s = pushs(SSTRING, ATEMP);
s->str = cp;
sold = source;
t = compile(s);
source = sold;

if (t == NULL)
return XBASE;

if (t != NULL && t->type == TCOM && /* $( *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
register struct ioword *io = *t->ioact;
char *name;

if ((io->flag&IOTYPE) != IOREAD)
errorf("funny $() command: %s",
snptreef((char *) 0, 32, "%R", io));
shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
SHF_MAPHI|SHF_CLEXEC);
if (shf == NULL)
errorf("%s: cannot open $() input", name);
xp->split = 0; /* no waitlast() */
} else {
int ofd1, pv[2];
openpipe(pv);
shf = shf_fdopen(pv[0], SHF_RD, (struct shf *) 0);
ofd1 = savefd(1); /* fd 1 may be closed... */
ksh_dup2(pv[1], 1, FALSE);
close(pv[1]);
execute(t, XFORK|XXCOM|XPIPEO);
restfd(1, ofd1);
startlast();
xp->split = 1; /* waitlast() */
}

xp->u.shf = shf;
return XCOM;
}

/*
* perform #pattern and %pattern substitution in ${}
*/

static char *
trimsub(str, pat, how)
register char *str;
char *pat;
int how;
{
register char *end = strchr(str, 0);
register char *p, c, *match;

switch (how&0xff) { /* UCHAR_MAX maybe? */
case '#': /* shortest at begin */
for (p = str; p <= end; p++) {
c = *p; *p = '\0';
if (gmatch(str, pat)) {
*p = c;
return p;
}
*p = c;
}
break;
case '#'|0x80: /* longest match at begin */
for (p = end; p >= str; p--) {
c = *p; *p = '\0';
if (gmatch(str, pat)) {
*p = c;
return p;
}
*p = c;
}
break;
case '%': /* shortest match at end */
for (p = end; p >= str; p--) {
if (gmatch(p, pat)) {
c = *p; *p = '\0';
match = strsave(str, ATEMP);
*p = c;
return match;
}
}
break;
case '%'|0x80: /* longest match at end */
for (p = str; p <= end; p++) {
if (gmatch(p, pat)) {
c = *p; *p = '\0';
match = strsave(str, ATEMP);
*p = c;
return match;
}
}
break;
}

return str; /* no match, return string */
}

/*
* glob
* Name derived from V6's /etc/glob, the program that expanded filenames.
*/

static void
glob(cp, wp)
char *cp;
register XPtrV *wp;
{
int oldsize;
XString xs;
char *xp;

oldsize = XPsize(*wp);
Xinit(xs, xp, 256, ATEMP);
globit(&xs, &xp, cp, wp, 0);
Xfree(xs, xp);

if (XPsize(*wp) == oldsize)
XPput(*wp, debunk(cp));
else
qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp);
}

static void
globit(xs, xpp, sp, wp, check)
XString *xs; /* dest string */
char **xpp; /* ptr to dest end */
char *sp; /* source path */
register XPtrV *wp; /* output list */
int check; /* bit 0: check dest existence;
* bit 1: result of glob */
{
register char *np; /* next source component */
char *xp = *xpp;

/* This to allow long expansions to be interrupted */
intrcheck();

if (sp == NULL) { /* end of source path */
/* We only need to check if the file exists if a pattern
* is followed by a non-pattern (eg, foo*x/bar; no check
* is needed for foo* since the match must exist) or if
* any patterns were expanded and the markdirs option is set.
* Symlinks make things a bit tricky...
*/
if ((check & 0x1) || (Flag(FMARKDIRS) && (check & 0x2))) {
#define stat_check() (stat_done ? stat_done : \
(stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
? -1 : 1))
struct stat lstatb, statb;
int stat_done = 0; /* -1: failed, 1 ok */

if (lstat(Xstring(*xs, xp), &lstatb) < 0)
return;
/* special case for systems which strip trailing
* slashes from regular files (eg, /etc/passwd/).
* SunOS 4.1.3 does this...
*/
if ((check & 0x1) && xp > Xstring(*xs, xp)
&& ISDIRSEP(xp[-1]) && !S_ISDIR(lstatb.st_mode)
#ifdef S_ISLNK
&& (!S_ISLNK(lstatb.st_mode)
|| stat_check() < 0
|| !S_ISDIR(statb.st_mode))
#endif /* S_ISLNK */
)
return;
/* Possibly tack on a trailing / if there isn't already
* one and if the file is a directory or a symlink to a
* directory
*/
if ((Flag(FMARKDIRS) && (check & 0x2))
&& xp > Xstring(*xs, xp) && !ISDIRSEP(xp[-1])
&& (S_ISDIR(lstatb.st_mode)
#ifdef S_ISLNK
|| (S_ISLNK(lstatb.st_mode)
&& stat_check() > 0
&& S_ISDIR(statb.st_mode))
#endif /* S_ISLNK */
))
{
*xp++ = DIRSEP;
*xp = '\0';
}
}
XPput(*wp, strnsave(Xstring(*xs, xp), Xlength(*xs, xp), ATEMP));
return;
}

if (xp > Xstring(*xs, xp))
*xp++ = DIRSEP;
while (ISDIRSEP(*sp)) {
Xcheck(*xs, xp);
*xp++ = *sp++;
}
np = strchr_dirsep(sp);
if (np != NULL)
*np++ = '\0';

*xpp = xp;

/* Check if sp needs globing - done to avoid pattern checks for strings
* containing MAGIC characters, open ['s without the matching close ],
* etc. (otherwise opendir() will be called which may fail because the
* directory isn't readable - if no globing is needed, only execute
* permission should be required (as per POSIX)).
*/
if (copy_non_glob(xs, xpp, sp))
globit(xs, xpp, np, wp, check);
else {
DIR *dirp;
struct dirent *d;
char *name;
int len;
int prefix_len;

xp = *xpp; /* copy_non_glob() may have re-alloc'd xs */
*xp = '\0';
prefix_len = Xlength(*xs, xp);
dirp = ksh_opendir(prefix_len ? Xstring(*xs, xp) : ".");
if (dirp == NULL)
goto Nodir;
while ((d = readdir(dirp)) != NULL) {
name = d->d_name;
if (name[0] == '.' &&
(name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue; /* always ignore . and .. */
if ((*name == '.' && *sp != '.') || !gmatch(name, sp))
continue;

len = NLENGTH(d) + 1;
XcheckN(*xs, xp, len);
memcpy(xp, name, len);
*xpp = xp + len - 1;
globit(xs, xpp, np, wp, 0x2 | (np != NULL ? 1 : 0));
xp = Xstring(*xs, xp) + prefix_len;
}
closedir(dirp);
Nodir:;
}

if (np != NULL)
*--np = DIRSEP;
}

/* Check if p contains something that needs globbing; if it does, 0 is
* returned; if not, p is copied into xs/xp after stripping any MAGICs
*/
static int
copy_non_glob(xs, xpp, p)
XString *xs;
char **xpp;
char *p;
{
char *xp;
int len = strlen(p);

XcheckN(*xs, *xpp, len);
xp = *xpp;
for (; *p; p++) {
if (*p == MAGIC) {
int c = *++p;

if (c == '*' || c == '?')
return 0;
if (*p == '[') {
char *q = p + 1;

if (*q == MAGIC && q[1] == NOT)
q += 2;
if (*q == MAGIC && q[1] == ']')
q += 2;
for (; *q; q++)
if (*q == MAGIC && *++q == ']')
return 0;
/* pass a literal [ through */
}
/* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */
}
*xp++ = *p;
}
*xp = '\0';
*xpp = xp;
return 1;
}

/* remove MAGIC from string */
static char *
debunk(cp)
char *cp;
{
register char *dp, *sp;

if ((sp = strchr(cp, MAGIC))) {
for (dp = sp; *sp; sp++)
*dp++ = (*sp == MAGIC) ? *++sp : *sp;
*dp = '\0';
}
return cp;
}

/* Check if p is an unquoted name, possibly followed by a / or :. If so
* puts the expanded version in *dsp,dp and returns a pointer in p just
* past the name, otherwise returns 0.
*/
static char *
maybe_expand_tilde(p, dsp, dpp, isassign)
char *p;
XString *dsp;
char **dpp;
int isassign;
{
XString ts;
char *dp = *dpp;
char *tp, *r;

Xinit(ts, tp, 16, ATEMP);
/* : only for DOASNTILDE form */
while (p[0] == CHAR && !ISDIRSEP(p[1])
&& (!isassign || p[1] != PATHSEP))
{
Xcheck(ts, tp);
*tp++ = p[1];
p += 2;
}
*tp = '\0';
r = (p[0] == EOS || p[0] == CHAR) ? tilde(Xstring(ts, tp)) : (char *) 0;
Xfree(ts, tp);
if (r) {
while (*r) {
Xcheck(*dsp, dp);
if (*r == MAGIC)
*dp++ = MAGIC;
*dp++ = *r++;
}
*dpp = dp;
r = p;
}
return r;
}

/*
* tilde expansion
*
* based on a version by Arnold Robbins
*/

static char *
tilde(cp)
char *cp;
{
char *dp;

if (cp[0] == '\0')
dp = strval(global("HOME"));
else if (cp[0] == '+' && cp[1] == '\0')
dp = strval(global("PWD"));
else if (cp[0] == '-' && cp[1] == '\0')
dp = strval(global("OLDPWD"));
else
dp = homedir(cp);
return dp;
}

/*
* map userid to user's home directory.
* note that 4.3's getpw adds more than 6K to the shell,
* and the YP version probably adds much more.
* we might consider our own version of getpwnam() to keep the size down.
*/

static char *
homedir(name)
char *name;
{
register struct tbl *ap;

ap = tenter(&homedirs, name, hash(name));
if (!(ap->flag & ISSET)) {
#ifdef OS2
/* No usernames in OS2 - punt */
return NULL;
#else /* OS2 */
register struct passwd *pw;
extern struct passwd *getpwnam();

pw = getpwnam(name);
if (pw == NULL)
return NULL;
ap->val.s = strsave(pw->pw_dir, APERM);
ap->flag |= DEFINED|ISSET|ALLOC;
#endif /* OS2 */
}
return ap->val.s;
}

#ifdef BRACEEXPAND
static void
alt_expand(wp, start, exp_start, end, fdo)
XPtrV *wp;
char *start, *exp_start;
char *end;
int fdo;
{
int UNINITIALIZED(count);
char *brace_start, *brace_end, *UNINITIALIZED(comma);
char *field_start;
char *p;

/* search for open brace */
for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)
;
brace_start = p;

/* find matching close brace, if any */
if (p) {
comma = (char *) 0;
count = 1;
for (p += 2; *p && count; p++) {
if (*p == MAGIC) {
if (*++p == OBRACE)
count++;
else if (*p == CBRACE)
--count;
else if (*p == ',' && count == 1)
comma = p;
}
}
}
/* no valid expansions... */
if (!p || count != 0) {
/* Note that given a{{b,c} we do not expand anything (this is
* what at&t ksh does. This may be changed to do the {b,c}
* expansion. }
*/
if (fdo & DOGLOB)
glob(start, wp);
else
XPput(*wp, debunk(start));
return;
}
brace_end = p;
if (!comma) {
alt_expand(wp, start, brace_end, end, fdo);
return;
}

/* expand expression */
field_start = brace_start + 2;
count = 1;
for (p = brace_start + 2; p != brace_end; p++) {
if (*p == MAGIC) {
if (*++p == OBRACE)
count++;
else if ((*p == CBRACE && --count == 0)
|| (*p == ',' && count == 1))
{
char *new;
int l1, l2, l3;

l1 = brace_start - start;
l2 = (p - 1) - field_start;
l3 = end - brace_end;
new = (char *) alloc(l1 + l2 + l3 + 1, ATEMP);
memcpy(new, start, l1);
memcpy(new + l1, field_start, l2);
memcpy(new + l1 + l2, brace_end, l3);
new[l1 + l2 + l3] = '\0';
alt_expand(wp, new, new + l1,
new + l1 + l2 + l3, fdo);
field_start = p + 1;
}
}
}
return;
}
#endif /* BRACEEXPAND */
pdksh-5.1.2/exec.c100644 0 133 71722 5667370267 12612 0ustar rootlsource/*
* execute command tree
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: exec.c,v 1.4 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include
#include "ksh_stat.h"

static int comexec ARGS((struct op *t, struct tbl *volatile tp, char **ap,
int volatile flags));
static void scriptexec ARGS((struct op *tp, char **ap));
static int call_builtin ARGS((struct tbl *tp, char **wp));
static int iosetup ARGS((struct ioword *iop));
static int herein ARGS((char *hname, int sub));
#ifdef KSH
static char *do_selectargs ARGS((char **ap));
static int pr_menu ARGS((register char **ap));
#endif KSH


/*
* handle systems that don't have F_SETFD
*/
#ifndef F_SETFD
# ifndef MAXFD
# define MAXFD 64
# endif
/* a bit field would be smaller, but this will work */
static char clexec_tab[MAXFD+1];
#endif

/*
* we now use this function always.
*/
int
fd_clexec(fd)
int fd;
{
#ifndef F_SETFD
if (fd >= 0 && fd < sizeof(clexec_tab)) {
clexec_tab[fd] = 1;
return 0;
}
return -1;
#else
return fcntl(fd, F_SETFD, 1);
#endif
}


/*
* execute command tree
*/
int
execute(t, flags)
struct op * volatile t;
volatile int flags; /* if XEXEC don't fork */
{
int i;
volatile int rv = 0;
int pv[2];
char ** volatile ap;
char *s, *cp;
struct ioword **iowp;
struct tbl *tp = NULL;

if (t == NULL)
return 0;

if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)
return exchild(t, flags, -1); /* run in sub-process */

newenv(E_EXEC);
if (trap)
runtraps(0);

if (t->type == TCOM) {
/* Clear subst_exstat before argument expansion. Used by
* null commands (see comexec()) and by c_set().
*/
subst_exstat = 0;

/* POSIX says expand command words first, then redirections,
* and assignments last..
*/
ap = eval(t->args, t->evalflags ? t->evalflags
: DOBLANK|DOGLOB|DOTILDE);
if (Flag(FXTRACE) && ap[0]) {
shf_fprintf(shl_out, "%s",
substitute(strval(global("PS4")), 0));
for (i = 0; ap[i]; i++)
shf_fprintf(shl_out, "%s%s", ap[i],
ap[i + 1] ? space : newline);
shf_flush(shl_out);
}
if (ap[0])
tp = findcom(ap[0], FC_BI|FC_FUNC|FC_NOAUTOLOAD);
}

if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) {
e->savefd = (short *) alloc(sizeofN(short, NUFILE), ATEMP);
/* initialize to not redirected */
memset(e->savefd, 0, sizeofN(short, NUFILE));
/* mark fd 0/1 in-use if pipeline */
/* Don't think this is needed any more... -mhr, Nov 10/94
if (flags&XPIPEI)
e->savefd[0] = -1;
if (flags&XPIPEO)
e->savefd[1] = -1;
*/
}

/* do redirection, to be restored in quitenv() */
if (t->ioact != NULL)
for (iowp = t->ioact; *iowp != NULL; iowp++) {
if (iosetup(*iowp) < 0) {
exstat = rv = 1;
/* Redirection failures for special commands
* cause (non-interactive) shell to exit.
*/
if (tp && tp->type == CSHELL
&& (tp->flag & SPEC_BI))
errorf((char *) 0);
/* Deal with FERREXIT, quitenv(), etc. */
goto Break;
}
}

switch(t->type) {
case TCOM:
rv = comexec(t, tp, ap, flags);
break;

case TPAREN:
rv = execute(t->left, flags|XFORK);
break;

case TPIPE:
flags |= XFORK;
flags &= ~XEXEC;
e->savefd[0] = savefd(0);
(void) ksh_dup2(e->savefd[0], 0, FALSE); /* stdin of first */
e->savefd[1] = savefd(1);
while (t->type == TPIPE) {
openpipe(pv);
(void) ksh_dup2(pv[1], 1, FALSE); /* stdout of curr */
/* Let exchild() close pv[0] in child
* (if this isn't done, commands like
* (: ; cat /etc/termcap) | sleep 1
* will hang forever).
*/
exchild(t->left, flags|XPIPEO|XCCLOSE, pv[0]);
(void) ksh_dup2(pv[0], 0, FALSE); /* stdin of next */
closepipe(pv);
flags |= XPIPEI;
t = t->right;
}
restfd(1, e->savefd[1]); /* stdout of last */
e->savefd[1] = 0; /* no need to re-restore this */
/* Let exchild() close 0 in parent, after fork, before wait */
i = exchild(t, flags|XPCLOSE, 0);
if (!(flags&XBGND) && !(flags&XXCOM))
rv = i;
break;

case TLIST:
while (t->type == TLIST) {
execute(t->left, flags & XERROK);
t = t->right;
}
rv = execute(t, flags & XERROK);
break;

case TCOPROC:
{
if (coproc.job && coproc.write >= 0)
errorf("coprocess already exists");
/* Can we re-use the existing co-process pipe? */
cleanup_coproc(TRUE);
/* do this before opening pipes, in case these fail */
e->savefd[0] = savefd(0);
e->savefd[1] = savefd(1);

openpipe(pv);
ksh_dup2(pv[0], 0, FALSE);
close(pv[0]);
coproc.write = pv[1];

if (coproc.readw >= 0)
ksh_dup2(coproc.readw, 1, FALSE);
else {
openpipe(pv);
coproc.read = pv[0];
ksh_dup2(pv[1], 1, FALSE);
coproc.readw = pv[1]; /* closed before first read */
}

/* exchild() closes coproc.* in child after fork */
flags &= ~XEXEC;
exchild(t->left, flags|XBGND|XFORK|XCOPROC|XCCLOSE,
coproc.readw);
break;
}

case TASYNC:
rv = execute(t->left, flags|XBGND|XFORK);
break;

case TOR:
case TAND:
rv = execute(t->left, XERROK);
if (t->right != NULL && (rv == 0) == (t->type == TAND))
rv = execute(t->right, 0);
else
flags |= XERROK;
break;

case TBANG:
rv = !execute(t->right, XERROK);
break;

#ifdef KSH
case TDBRACKET:
{
XPtrV ap;

/* Must build arguments one by one because the second
* operand of = and != need to be eval'd with DOPAT.
*/
for (i = 0; t->args[i]; i++)
;
XPinit(ap, i + 1);
for (i = 0; t->args[i]; i++)
XPput(ap, evalstr(t->args[i], DOTILDE
|(t->vars[i][1] == DB_PAT ? DOPAT : 0)));
XPput(ap, (char *) 0);
rv = call_builtin(findcom("test", FC_BI), (char **) XPptrv(ap));
break;
}
#endif /* KSH */

case TFOR:
#ifdef KSH
case TSELECT:
#endif /* KSH */
ap = (t->vars != NULL) ?
eval(t->vars, DOBLANK|DOGLOB|DOTILDE)
: e->loc->argv + 1;
e->type = E_LOOP;
while ((i = setjmp(e->jbuf)))
if ((e->flags&EF_BRKCONT_PASS)
|| (i != LBREAK && i != LCONTIN))
{
quitenv();
unwind(i);
} else if (i == LBREAK) {
rv = 0;
goto Break;
}
rv = 0; /* in case of a continue */
if (t->type == TFOR) {
struct tbl *vq;

while (*ap != NULL) {
vq = global(t->str);
if (vq->flag & RDONLY)
errorf("%s is read only", t->str);
setstr(vq, *ap++);
rv = execute(t->left, flags & XERROK);
}
}
#ifdef KSH
else { /* TSELECT */
struct tbl *vq;

for (;;) {
if ((cp = do_selectargs(ap)) == (char *) 0) {
rv = 1;
break;
}
vq = global(t->str);
if (vq->flag & RDONLY)
errorf("%s is read only", t->str);
setstr(vq, cp);
rv = execute(t->left, flags & XERROK);
}
}
#endif /* KSH */
break;

case TWHILE:
case TUNTIL:
e->type = E_LOOP;
while ((i = setjmp(e->jbuf)))
if ((e->flags&EF_BRKCONT_PASS)
|| (i != LBREAK && i != LCONTIN))
{
quitenv();
unwind(i);
} else if (i == LBREAK) {
rv = 0;
goto Break;
}
rv = 0; /* in case of a continue */
while ((execute(t->left, XERROK) == 0) == (t->type == TWHILE))
rv = execute(t->right, flags & XERROK);
break;

case TIF:
case TELIF:
if (t->right == NULL)
break; /* should be error */
rv = execute(t->left, XERROK) == 0 ?
execute(t->right->left, flags & XERROK) :
execute(t->right->right, flags & XERROK);
break;

case TCASE:
cp = evalstr(t->str, DOTILDE);
for (t = t->left; t != NULL && t->type == TPAT; t = t->right)
for (ap = t->vars; *ap; ap++)
if ((s = evalstr(*ap, DOTILDE|DOPAT)) && gmatch(cp, s))
goto Found;
break;
Found:
rv = execute(t->left, flags & XERROK);
break;

case TBRACE:
rv = execute(t->left, flags & XERROK);
break;

case TFUNCT:
rv = define(t->str, t->left);
break;

case TTIME:
rv = timex(t, flags);
break;

case TEXEC: /* an eval'd TCOM */
s = t->args[0];
ap = makenv();
#ifndef F_SETFD
for (i = 0; i < sizeof(clexec_tab); i++)
if (clexec_tab[i]) {
close(i);
clexec_tab[i] = 0;
}
#endif
restoresigs();
ksh_execve(t->str, t->args, ap);
if (errno == ENOEXEC)
scriptexec(t, ap);
else
errorf("%s: %s", s, strerror(errno));
}
Break:
exstat = rv;

quitenv(); /* restores IO */
if ((flags&XEXEC))
exit(rv); /* exit child */
if (rv != 0 && !(flags & XERROK)) {
if (Flag(FERREXIT)) {
exstat = rv;
unwind(LERROR);
}
trapsig(SIGERR_);
}
return rv;
}

/*
* execute simple command
*/

static int
comexec(t, tp, ap, flags)
struct op *t;
struct tbl *volatile tp;
register char **ap;
int volatile flags;
{
int i;
int rv = 0;
register char *cp;
register char **lastp;
static struct op texec = {TEXEC};
int type_flags;
int no_keepasn;
int fcflags = FC_BI|FC_FUNC|FC_PATH;

/* snag the last argument for $_ XXX not the same as at&t ksh,
* which only seems to set $_ after a newline (but not in
* functions/dot scripts, but in interactive and scipt) -
* perhaps save last arg here and set it in shell()?.
*/
if (*(lastp = ap)) {
while (*++lastp)
;
setstr(typeset("_", LOCAL, 0, 0, 0), *--lastp);
}

/* Deal with the shell builtins builtin, exec and command since
* they can be followed by other commands. This must be done before
* we know if we should create a local block, which must be done
* before we can do a path search (in case the assignments change
* PATH).
* Odd cases:
* FOO=bar exec > /dev/null FOO is kept but not exported
* FOO=bar exec foobar FOO is exported
* FOO=bar command exec > /dev/null FOO is neither kept nor exported
* FOO=bar command FOO is neither kept nor exported
* PATH=... foobar use new PATH in foobar search
*/
no_keepasn = 0;
while (tp && tp->type == CSHELL) {
fcflags = FC_BI|FC_FUNC|FC_PATH;/* undo effects of command */
if (tp->val.f == c_builtin) {
if ((cp = *++ap) == NULL) {
tp = NULL;
break;
}
tp = findcom(cp, FC_BI);
if (tp == NULL)
errorf("builtin: %s: not a builtin", cp);
continue;
} else if (tp->val.f == c_exec) {
if (ap[1] == NULL)
break;
ap++;
flags |= XEXEC;
} else if (tp->val.f == c_command) {
int optc, saw_p = 0;

/* Ugly dealing with options in two places (here and
* in c_command(), but such is life)
*/
ksh_getopt_reset(&builtin_opt, 0);
while ((optc = ksh_getopt(ap, &builtin_opt, ":p"))
== 'p')
saw_p = 1;
if (optc != EOF)
break; /* command -vV or something */
/* don't look for functions */
fcflags = FC_BI|FC_PATH;
if (saw_p) {
if (Flag(FRESTRICTED)) {
warningf(TRUE,
"command -p: restricted\n");
rv = 1;
goto Leave;
}
fcflags |= FC_DEFPATH;
}
ap += builtin_opt.optind;
/* POSIX says special builtins loose their status
* if accessed using command.
*/
no_keepasn = 1;
if (!ap[0]) {
/* ensure command with no args exits with 0 */
subst_exstat = 0;
break;
}
} else
break;
tp = findcom(ap[0], (fcflags & (FC_BI|FC_FUNC)) |FC_NOAUTOLOAD);
}
/* todo: POSIX says assignments preceding a function are kept, at&t
* ksh does not do this
*/
if (!no_keepasn && (!ap[0] || (tp && tp->flag & KEEPASN)))
type_flags = 0;
else {
/* create new variable/function block */
newblock();
type_flags = LOCAL|EXPORT;
}
for (i = 0; t->vars[i]; i++) {
cp = evalstr(t->vars[i], DOASNTILDE);
if (Flag(FXTRACE)) {
if (i == 0)
shf_fprintf(shl_out, "%s",
substitute(strval(global("PS4")), 0));
shf_fprintf(shl_out, "%s%s", cp,
t->vars[i + 1] ? space : newline);
if (!t->vars[i + 1])
shf_flush(shl_out);
}
typeset(cp, type_flags, 0, 0, 0);
}

if ((cp = *ap) == NULL) {
rv = subst_exstat;
goto Leave;
} else if (!tp || (tp->type == CFUNC && !(tp->flag & ISSET))) {
if (Flag(FRESTRICTED) && strchr_dirsep(cp)) {
warningf(TRUE, "%s: restricted\n", cp);
rv = 1;
goto Leave;
}
tp = findcom(cp, fcflags);
}

switch (tp->type) {
case CSHELL: /* shell built-in */
rv = call_builtin(tp, ap);
break;

case CFUNC: /* function call */
{
volatile int old_xflag;
volatile int old_inuse;

if (!(tp->flag&ISSET))
internal_errorf(1, "%s: undefined function", cp);

/* XXX: posix says $0 remains unchanged */
kshname = ap[0];
e->loc->argv = ap;
for (i = 0; *ap++ != NULL; i++)
;
e->loc->argc = i - 1;
getopts_reset(1);

old_xflag = Flag(FXTRACE);
Flag(FXTRACE) = tp->flag & TRACE ? TRUE : FALSE;

old_inuse = tp->flag & FINUSE;
tp->flag |= FINUSE;

e->type = E_FUNC;
if ((i = setjmp(e->jbuf)) == 0) {
/* seems odd to pass XERROK here, but at&t ksh does */
exstat = execute(tp->val.t, flags & XERROK);
i = LRETURN;
}
Flag(FXTRACE) = old_xflag;
tp->flag = (tp->flag & ~FINUSE) | old_inuse;
/* Were we deleted while executing? If so, free the execution
* tree. Unfortunately, the table entry is never re-used.
*/
if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) {
if (tp->flag & ALLOC) {
tp->flag &= ~ALLOC;
tfree(tp->val.t, tp->areap);
}
tp->flag = 0;
}
switch (i) {
case LRETURN:
case LERROR:
rv = exstat;
break;
case LINTR:
case LEXIT:
case LLEAVE:
case LSHELL:
quitenv();
unwind(i);
/*NOTREACHED*/
default:
quitenv();
internal_errorf(1, "CFUNC %d", i);
}
break;
}

case CEXEC: /* executable command */
case CTALIAS: /* tracked alias */
if (!(tp->flag&ISSET)) {
/*
* mlj addition:
*
* If you specify a full path to a file
* (or type the name of a file in .) which
* doesn't have execute priv's, it used to
* just say "not found". Kind of annoying,
* particularly if you just wrote a script
* but forgot to say chmod 755 script.
*
* This should probably be done in eaccess(),
* but it works here (at least I haven't noticed
* changing errno here breaking something
* else).
*
* So, we assume that if the file exists, it
* doesn't have execute privs; else, it really
* is not found.
*/
if (access(cp, F_OK) < 0)
warningf(TRUE, "%s: not found\n", cp);
else
warningf(TRUE, "%s: cannot execute\n", cp);
/* XXX posix says 126 if in path and cannot execute */
rv = 127;
break;
}

/* set $_ to program's full path */
setstr(typeset("_", LOCAL|EXPORT, 0, 0, 0), tp->val.s);

if ((flags&XEXEC)) {
j_exit();
if (!(flags&XBGND) || Flag(FMONITOR)) {
setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG);
setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG);
}
}

/* to fork we set up a TEXEC node and call execute */
texec.left = t; /* for tprint */
texec.str = tp->val.s;
texec.args = ap;
rv = exchild(&texec, flags, -1);
break;
}
Leave:
if (flags & XEXEC) {
exstat = rv;
unwind(LLEAVE);
}
return rv;
}

static void
scriptexec(tp, ap)
register struct op *tp;
register char **ap;
{
char *shell;

shell = strval(global(EXECSHELL_STR));
if (shell && *shell)
shell = search(shell, path, X_OK);
if (!shell || !*shell)
shell = EXECSHELL;

*tp->args-- = tp->str;
#ifdef SHARPBANG
{
char buf[LINE];
register char *cp;
register int fd, n;

buf[0] = '\0';
if ((fd = open(tp->str, O_RDONLY)) >= 0) {
if ((n = read(fd, buf, LINE - 1)) > 0)
buf[n] = '\0';
(void) close(fd);
}
if ((buf[0] == '#' && buf[1] == '!' && (cp = &buf[2]))
# ifdef OS2
|| (strncmp(buf, "extproc", 7) == 0 && isspace(buf[7])
&& (cp = &buf[7]))
# endif /* OS2 */
)
{
while (*cp && (*cp == ' ' || *cp == '\t'))
cp++;
if (*cp && *cp != '\n') {
char *a0 = cp, *a1 = (char *) 0;

while (*cp && *cp != '\n' && *cp != ' '
&& *cp != '\t')
cp++;
if (*cp && *cp != '\n') {
*cp++ = '\0';
while (*cp
&& (*cp == ' ' || *cp == '\t'))
cp++;
if (*cp && *cp != '\n') {
a1 = cp;
/* all one argument */
while (*cp && *cp != '\n')
cp++;
}
}
if (*cp == '\n') {
*cp = '\0';
if (a1)
*tp->args-- = a1;
shell = a0;
}
}
# ifdef OS2
} else {
/* Should really check shell type before
* making this assumption.
*/
*tp->args-- = "/c";
# endif /* OS2 */
}
}
#endif /* SHARPBANG */
*tp->args = shell;

ksh_execve(tp->args[0], tp->args, ap);

/* report both the program that was run and the bogus shell */
errorf("%s: %s: %s", tp->str, shell, strerror(errno));
}

int
shcomexec(wp)
register char **wp;
{
register struct tbl *tp;

tp = tsearch(&builtins, *wp, hash(*wp));
if (tp == NULL)
internal_errorf(1, "shcomexec: %s", *wp);
return call_builtin(tp, wp);
}

/*
* Search function tables for a function. If create set, a table entry
* is created if none is found.
*/
struct tbl *
findfunc(name, h, create)
char *name;
int h;
int create;
{
struct block *l;
struct tbl *tp = (struct tbl *) 0;

for (l = e->loc; l; l = l->next) {
tp = tsearch(&l->funs, name, h);
if (tp && (tp->flag & DEFINED))
break;
if (!l->next && create) {
tp = tenter(&l->funs, name, h);
tp->flag = DEFINED;
tp->type = CFUNC;
break;
}
}
return tp;
}

/*
* define function
*/
int
define(name, t)
char *name;
struct op *t;
{
register struct tbl *tp;

tp = findfunc(name, hash(name), TRUE);

/* If this function is currently being executed, we zap this
* table entry so findfunc() won't see it
*/
if (tp->flag & FINUSE) {
tp->name[0] = '\0';
tp->flag &= ~DEFINED; /* ensure it won't be found */
tp->flag |= FDELETE;
tp = findfunc(name, hash(name), TRUE);
}

if (tp->flag & ALLOC) {
tp->flag &= ~(ISSET|ALLOC);
tfree(tp->val.t, tp->areap);
}

if (t == NULL) { /* undefine */
tdelete(tp);
return 0;
}

tp->val.t = tcopy(t, tp->areap);
tp->flag |= (ISSET|ALLOC);

return 0;
}

/*
* add builtin
*/
void
builtin(name, func)
char *name;
int (*func)();
{
register struct tbl *tp;
int flag;

/* see if any flags should be set for this builtin */
for (flag = 0; ; name++) {
if (*name == '=') /* command does variable assignment */
flag |= KEEPASN;
else if (*name == '*') /* POSIX special builtin */
flag |= SPEC_BI;
else if (*name == '+') /* POSIX regular builtin */
flag |= REG_BI;
else
break;
}

tp = tenter(&builtins, name, hash(name));
tp->flag = DEFINED | flag;
tp->type = CSHELL;
tp->val.f = func;
}

/*
* find command
* either function, hashed command, or built-in (in that order)
*/
struct tbl *
findcom(name, flags)
char *name;
int flags; /* FC_* */
{
static struct tbl temp;
unsigned int h = hash(name);
struct tbl *tp = NULL, *tbi;
int insert = Flag(FTRACKALL); /* insert if not found */

if (strchr_dirsep(name) != NULL) {
insert = 0;
goto Search;
}
tbi = (flags & FC_BI) ? tsearch(&builtins, name, h) : NULL;
/* POSIX says special builtins first, then functions, then
* POSIX regular builtins, then search path...
*/
if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI))
tp = tbi;
if (!tp && (flags & FC_FUNC)) {
tp = findfunc(name, h, FALSE);
if (tp && !(tp->flag & ISSET) && !(flags & FC_NOAUTOLOAD)) {
char *fpath, *fname;

if ((fpath = strval(global("FPATH"))) == null
|| (fname = search(name, fpath, R_OK)) == 0
|| include(fname, 0, (char **) 0) != 0
|| (tp = findfunc(name, h, FALSE)) == 0
|| !(tp->flag & ISSET))
tp = NULL;
}
}
if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI))
tp = tbi;
/* todo: posix says non-special/non-regular builtins must
* be triggered by some user-controllable means like a
* special directory in PATH. Requires modifications to
* the search() function. Tracked aliases should be
* modified to allow tracking of builtin commands.
* This should be under control of the FPOSIX flag.
* If this is changed, also change c_whence...
*/
if (!tp && (flags & FC_UNREGBI) && tbi)
tp = tbi;
if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) {
tp = tsearch(&taliases, name, h);
if (tp && (tp->flag & ISSET) && eaccess(tp->val.s, X_OK) != 0) {
if (tp->flag & ALLOC) {
tp->flag &= ~ALLOC;
afree(tp->val.s, APERM);
}
tp->flag &= ~ISSET;
}
}

Search:
if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET)))
&& (flags & FC_PATH))
{
if (!tp) {
if (insert && !(flags & FC_DEFPATH)) {
tp = tenter(&taliases, name, h);
tp->type = CTALIAS;
} else {
tp = &temp;
tp->type = CEXEC;
}
tp->flag = DEFINED; /* make ~ISSET */
}
name = search(name, flags & FC_DEFPATH ? def_path : path, X_OK);
if (name != NULL) {
tp->val.s = tp == &temp ? name : strsave(name, APERM);
tp->flag |= ISSET|ALLOC;
}
}
return tp;
}

/*
* flush executable commands with relative paths
*/
void
flushcom(all)
int all; /* just relative or all */
{
register struct tbl *tp;

for (twalk(&taliases); (tp = tnext()) != NULL; )
if ((tp->flag&ISSET) && (all || !ISDIRSEP(tp->val.s[0]))) {
if (tp->flag&ALLOC) {
tp->flag &= ~(ALLOC|ISSET);
afree(tp->val.s, APERM);
}
tp->flag = ~ISSET;
}
}

/* Check if path is something we want to find. Returns -1 for failure. */
static int
search_access(path, mode)
char *path;
int mode;
{
int ret = eaccess(path, mode);
struct stat statb;

/* if executable pipes come along, this will have to change */
if (ret == 0 && mode == X_OK
&& (stat(path, &statb) < 0 || !S_ISREG(statb.st_mode)))
ret = -1;
return ret;
}

#ifdef OS2
static int
search_access1(path, mode)
char *path;
int mode;
{
static char *suffixes[] = { "", ".exe", ".com", ".ksh", ".cmd" };
int i;
int ret;
char *tp = path + strlen(path);

for (i = 0; i < NELEN(suffixes); i++) {
strcpy(tp, suffixes[i]);
if ((ret = search_access(path, mode)) == 0)
break;
*tp = '\0';
}
return ret;
}
#endif /* OS2 */

/*
* search for command with PATH
*/
char *
search(name, path, mode)
char *name, *path;
int mode; /* R_OK or X_OK */
{
register char *sp, *p, *xp;
XString xs;
int namelen;

#ifdef OS2
/* Xinit() allocates 8 additional bytes, so appended suffixes won't
* overflow the memory.
*/
namelen = strlen(name) + 1;
Xinit(xs, xp, namelen, ATEMP);
memcpy(Xstring(xs, xp), name, namlen);

if (strchr_dirsep(name)) {
if (search_access1(Xstring(xs, xp), mode) >= 0)
return Xstring(xs, xp); /* not Xclose() - see above */
Xfree(xs, xp);
return NULL;
}

/* Look in current context always. (os2 style) */
if (search_access1(Xstring(xs, xp), mode) == 0)
return Xstring(xs, xp); /* not Xclose() - xp may be wrong */
#else /* OS2 */
if (strchr_dirsep(name)) {
if (search_access(name, mode) == 0)
return name;
return NULL;
}

namelen = strlen(name) + 1;
Xinit(xs, xp, 128, ATEMP);
#endif /* OS2 */

sp = path;
while (sp != NULL) {
xp = Xstring(xs, xp);
if (!(p = strchr(sp, PATHSEP)))
p = sp + strlen(sp);
if (p != sp) {
XcheckN(xs, xp, p - sp);
memcpy(xp, sp, p - sp);
xp += p - sp;
*xp++ = DIRSEP;
}
sp = p;
XcheckN(xs, xp, namelen);
memcpy(xp, name, namelen);
#ifdef OS2
if (search_access1(Xstring(xs, xp), mode) == 0)
return Xstring(xs, xp); /* Not Xclose() - see above */
#else /* OS2 */
if (search_access(Xstring(xs, xp), mode) == 0)
return Xclose(xs, xp + namelen);
#endif /* OS2 */
if (*sp++ == '\0')
sp = NULL;
}
Xfree(xs, xp);
return NULL;
}

static int
call_builtin(tp, wp)
struct tbl *tp;
char **wp;
{
int rv;

builtin_argv0 = wp[0];
builtin_flag = tp->flag;
shf_reopen(1, SHF_WR, shl_stdout);
shl_stdout_ok = 1;
ksh_getopt_reset(&builtin_opt, GF_ERROR);
rv = (*tp->val.f)(wp);
shf_flush(shl_stdout);
shl_stdout_ok = 0;
builtin_flag = 0;
builtin_argv0 = (char *) 0;
return rv;
}

/*
* set up redirection, saving old fd's in e->savefd
*/
static int
iosetup(iop)
register struct ioword *iop;
{
register int u = -1;
char *cp = iop->name;
int do_open, UNINITIALIZED(flags);

if ((iop->flag&IOTYPE) != IOHERE)
cp = evalonestr(cp, DOTILDE|DOGLOB);

if (Flag(FXTRACE)) {
struct ioword iotmp;

iotmp = *iop;
/* Print the expanded version */
iotmp.name = (iop->flag & IOTYPE) == IOHERE ? (char *) 0 : cp;
shellf("%s%s\n",
substitute(strval(global("PS4")), 0),
snptreef((char *) 0, 32, "%R", &iotmp));
}

/* Do not save if it has already been redirected (i.e. "cat >x >y") */
if (e->savefd[iop->unit] == 0)
/* c_exec() assumes e->savefd[fd] set for any redirections */
e->savefd[iop->unit] = savefd(iop->unit);

do_open = 1;
switch (iop->flag&IOTYPE) {
case IOREAD:
flags = O_RDONLY;
break;

case IOCAT:
flags = O_WRONLY | O_APPEND | O_CREAT;
break;

case IOWRITE:
flags = O_WRONLY | O_CREAT | O_TRUNC;
if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB))
flags |= O_EXCL;
break;

case IORDWR:
flags = O_RDWR | O_CREAT;
break;

case IOHERE:
do_open = 0;
u = herein(cp, iop->flag&IOEVAL);
/* cp may have wrong name */
break;

case IODUP:
{
char *emsg;

do_open = 0;
if (*cp == '-' && !cp[1])
close(u = iop->unit);
else {
int saveu;
u = check_fd(cp,
X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
&emsg);
if (u < 0) {
warningf(TRUE, "%d>&%s: %s\n",
iop->unit, cp, emsg);
return -1;
}
u = ksh_dup2(saveu = u, iop->unit, TRUE);
if (iop->flag & IORDUP) /* possible <&p */
/* Ensure other side of read pipe is closed
* so EOF can be read properly
*/
coproc_readw_close(saveu);
else /* possible >&p */
/* If co-process input is duped,
* close shell's copy
*/
coproc_write_close(saveu);
}
break;
}
}
if (do_open) {
if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
warningf(TRUE, "%s: restricted\n", cp);
return -1;
}
u = open(cp, flags, 0666);
}
if (u < 0) {
/* herein() may already have printed message */
if (u == -1)
warningf(TRUE, "cannot %s %s: %s\n",
(iop->flag&IOTYPE) == IODUP ? "dup"
: ((iop->flag&IOTYPE) == IOREAD
|| (iop->flag&IOTYPE) == IOHERE) ? "open"
: "create", cp, strerror(errno));
return -1;
}
if (u != iop->unit) {
(void) ksh_dup2(u, iop->unit, FALSE);
if (iop->flag != IODUP) /* always true */
close(u);
}
if (u == 2) /* Clear any write errors */
shf_reopen(2, SHF_WR, shl_out);
return 0;
}

/*
* open here document temp file.
* if unquoted here, expand here temp file into second temp file.
*/
static int
herein(hname, sub)
char *hname;
int sub;
{
int fd;

if (sub) {
char *cp;
struct source *s, *volatile osource = source;
struct temp *h;
struct shf *volatile shf;
int i;

/* must be before newenv() 'cause shf uses ATEMP */
shf = shf_open(hname, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
if (shf == NULL)
return -1;
newenv(E_ERRH);
if ((i = setjmp(e->jbuf))) {
if (shf)
shf_close(shf);
source = osource;
quitenv(); /* after shf_close() due to alloc */
/* indicate that error message should not be printed */
return -2;
}
/* set up yylex input from here file */
s = pushs(SFILE, ATEMP);
s->u.shf = shf;
source = s;
if (yylex(ONEWORD) != LWORD)
internal_errorf(1, "herein: yylex");
shf_close(shf);
shf = (struct shf *) 0;
cp = evalstr(yylval.cp, 0);

/* write expanded input to another temp file */
h = maketemp(ATEMP);
h->next = e->temps; e->temps = h;
shf = shf_open(h->name, O_WRONLY|O_CREAT|O_TRUNC, 0666, 0);
if (shf == NULL
|| (fd = open(h->name, O_RDONLY, 0)) < 0)
errorf("%s: %s", h->name, strerror(errno));
shf_write(cp, strlen(cp), shf);
if (shf_close(shf) == EOF) {
close(fd);
shf = (struct shf *) 0;
errorf("error writing %s: %s", h->name,
strerror(errno));
}
shf = (struct shf *) 0;

quitenv();
} else {
fd = open(hname, O_RDONLY, 0);
if (fd < 0)
return -1;
}

return fd;
}

#ifdef KSH
/*
* ksh special - the select command processing section
* print the args in column form - assuming that we can
*/
static char *
do_selectargs(ap)
register char **ap;
{
extern int c_read ARGS((char **wp));

static char *read_args[] = {
"read", "-r", "REPLY", (char *) 0
};
char *s;
int i, UNINITIALIZED(argct);

while (1) {
argct = pr_menu(ap);
shellf("%s", strval(global("PS3")));
if (call_builtin(findcom("read", FC_BI), read_args) != 0) {
shellf(newline);
return (char *) 0;
}
s = strval(global("REPLY"));
while (*s && isspace(*s))
s++;
if (*s) {
i = atoi(s);
return (i >= 1 && i <= argct) ? ap[i - 1] : null;
}
}
}

struct select_menu_info {
char **args;
int arg_width;
int num_width;
} info;

/* format a single select menu item */
static char *
select_fmt_entry(arg, i, buf, buflen)
void *arg;
int i;
char *buf;
int buflen;
{
struct select_menu_info *smi = (struct select_menu_info *) arg;

shf_snprintf(buf, buflen, "%*d) %s",
smi->num_width, i + 1, smi->args[i]);
return buf;
}

/*
* print a select style menu
*/
static int
pr_menu(ap)
register char **ap;
{
struct select_menu_info smi;
char **pp;
int nwidth, dwidth;
int i, n;

/* Width/column calculations were done once and saved, but this
* means select can't be used recursively so we re-calculate each
* time (could save in a structure that is returned, but its probably
* not worth the bother).
*/

/*
* get dimensions of the list
*/
for (n = 0, nwidth = 0, pp = ap; *pp; n++, pp++) {
i = strlen(*pp);
nwidth = (i > nwidth) ? i : nwidth;
}
/*
* we will print an index of the form
* %d)
* in front of each entry
* get the max width of this
*/
for (i = n, dwidth = 1; i >= 10; i /= 10)
dwidth++;

smi.args = ap;
smi.arg_width = nwidth;
smi.num_width = dwidth;
print_columns(shl_out, n, select_fmt_entry, (void *) &smi,
dwidth + nwidth + 2);

return n;
}
#endif /* KSH */
pdksh-5.1.2/expr.c100644 0 133 25245 5655442472 12636 0ustar rootlsource/*
* Korn expression evaluation
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: expr.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include


/* The order of these enums is constrained by the order of opinfo[] */
enum token {
/* binary operators */
O_EQ = 0, 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,
/* 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 LAST_BINOP O_TERN
#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 /* = *= /= %= += -= <<= >>= &= ^= |= */
};
#define MAX_PREC P_ASSIGN

struct opinfo {
char name[4];
int len; /* name length */
enum prec prec; /* precidence: 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_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_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 assignments (for ?:) */
struct tbl *val; /* value from token() */
Expr_state *volatile prev; /* previous state */
};

enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_BADVAR, ET_STR };

static Expr_state *es;

static void evalerr ARGS((enum error_type type, char *str))
GCC_FUNC_ATTR(noreturn);
static struct tbl *evalexpr ARGS((enum prec prec));
static void token ARGS((void));
static struct tbl *tempvar ARGS((void));
static struct tbl *intvar ARGS((struct tbl *vp));

/*
* parse and evalute expression
*/
long
evaluate(expr)
const char *expr;
{
struct tbl v;

v.flag = DEFINED|INTEGER;
v.type = 0;
v_evaluate(&v, expr);
return v.val.i;
}

/*
* parse and evalute expression, storing result in vp.
*/
void
v_evaluate(vp, expr)
struct tbl *vp;
const char *expr;
{
struct tbl *v;
Expr_state curstate;
int i;

/* save state to allow recursive calls */
curstate.expression = curstate.tokp = expr;
curstate.noassign = 0;
curstate.prev = es;
es = &curstate;

newenv(E_ERRH);
if ((i = setjmp(e->jbuf))) {
quitenv();
es = curstate.prev;
unwind(i);
/*NOTREACHED*/
}

token();
#if 1 /* ifdef-out to disallow empty expressions to be treated as 0 */
if (es->tok == END) {
es->tok = LIT;
es->val = tempvar();
}
#endif /* 0 */
v = intvar(evalexpr(MAX_PREC));

if (es->tok != END)
evalerr(ET_UNEXPECTED, (char *) 0);

if (vp->flag & INTEGER)
strint(vp, v);
else
setstr(vp, strval(v));

es = curstate.prev;
quitenv();
}

static void
evalerr(type, str)
enum error_type type;
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 = strval(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;
}
/* XXX have errorfs() just go back to error handler? */
errorf("%s: unexpected `%s'", es->expression, s);
break;

case ET_BADLIT:
errorf("%s: bad number `%s'", es->expression, str);
break;

case ET_BADVAR:
errorf("%s: value of variable `%s' not a number",
es->expression, str);
break;

default: /* keep gcc happy */
case ET_STR:
errorf("%s: %s", es->expression, str);
break;
}
}

static struct tbl *
evalexpr(prec)
enum prec prec;
{
register struct tbl *vl, UNINITIALIZED(*vr), *vasn;
register 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();
vl = intvar(evalexpr(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();
vl = evalexpr(MAX_PREC);
if (es->tok != CLOSE_PAREN)
evalerr(ET_STR, "missing )");
token();
} else if (op == VAR || op == LIT) {
vl = es->val;
token();
} else {
evalerr(ET_UNEXPECTED, (char *) 0);
/*NOTREACHED*/
}
return vl;
}
vl = evalexpr(((int) prec) - 1);
while ((int) (op = es->tok) <= (int) LAST_BINOP && opinfo[(int) op].prec == prec) {
token();
vasn = vl;
if (op != O_ASN) /* vl may not have a value yet */
vl = intvar(vl);
if (IS_ASSIGNOP(op)) {
if (vasn->name[0] == '\0')
evalerr(ET_STR, "assignment to non-lvalue");
else if (vasn->flag & RDONLY)
evalerr(ET_STR,
"assignment to read only variable");
vr = intvar(evalexpr(P_ASSIGN));
} else if (op != O_TERN && op != O_LAND && op != O_LOR)
vr = intvar(evalexpr(((int) prec) - 1));
if (op != O_TERN && vr->val.i == 0
&& (op == O_DIV || op == O_MOD || op == O_DIVASN
|| op == O_MODASN))
{
if (es->noassign)
vr->val.i = 1;
else
evalerr(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(evalexpr(((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(evalexpr(((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(MAX_PREC);
if (!e)
es->noassign--;
if (es->tok != CTERN)
evalerr(ET_STR, "missing :");
token();
if (e)
es->noassign++;
vr = evalexpr(MAX_PREC);
if (e)
es->noassign--;
vl = e ? vl : vr;
}
break;
case O_ASN:
res = vr->val.i;
break;
}
if (IS_ASSIGNOP(op)) {
vr->val.i = res;
if (vasn->flag & INTEGER)
strint(vasn, vr);
else
setstr(vasn, strval(vr));
vl = vr;
} else if (op != O_TERN)
vl->val.i = res;
}
return vl;
}

static void
token()
{
register const char *cp;
register 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++)
;
if (c == '[') {
int len;

len = array_ref_len(cp - 1);
if (len == 0)
evalerr(ET_STR, "missing ]");
cp += len;
}
if (es->noassign)
es->val = tempvar();
else {
tvar = strnsave(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 = strnsave(es->tokp, --cp - es->tokp, ATEMP);
es->val = tempvar();
es->val->flag &= ~INTEGER;
es->val->type = 0;
es->val->val.s = tvar;
if (strint(es->val, es->val) == NULL)
evalerr(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;
}

static struct tbl *
tempvar()
{
register struct tbl *vp;

vp = (struct tbl*) alloc(sizeof(struct tbl), ATEMP);
vp->flag = ISSET|INTEGER;
vp->type = 0;
vp->areap = ATEMP;
vp->val.i = 0;
vp->name[0] = '\0';
return vp;
}

/* cast (string) variable to temporary integer variable */
static struct tbl *
intvar(vp)
register struct tbl *vp;
{
register struct tbl *vq;

/* try to avoid replacing a temp var with another temp var */
if (vp->name[0] == '\0'
&& (vp->flag & (ISSET|INTEGER)) == (ISSET|INTEGER))
return vp;

vq = tempvar();
vq->type = 0;
if (strint(vq, vp) == NULL) {
evalerr(ET_BADVAR, vp->name);
/*
if ((vp->flag&ISSET) && vp->val.s && *(vp->val.s)) {
evalerr("bad number");
} else {
vq->flag |= (ISSET|INTEGER);
vq->type = 10;
vq->val.i = 0;
}
*/
}
return vq;
}
pdksh-5.1.2/history.c100644 0 133 50466 5667220735 13363 0ustar rootlsource/*
* command history
*
* only implements in-memory history.
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: history.c,v 1.5 1994/06/17 19:52:41 michael Exp michael $";
#endif
/*
* This file contains
* a) the original in-memory history mechanism
* b) a simple file saving history mechanism done by sjg@zen
* define EASY_HISTORY to get this
* c) a more complicated mechanism done by [email protected]
* that more closely follows the real ksh way of doing
* things. You need to have the mmap system call for this
* to work on your system
*/

#include "sh.h"
#include "ksh_stat.h"

#ifdef HISTORY
# ifdef EASY_HISTORY

# ifndef HISTFILE
# ifdef OS2
# define HISTFILE "history.ksh"
# else /* OS2 */
# define HISTFILE ".pdksh_hist"
# endif /* OS2 */
# endif

# else
/* Defines and includes for the complicated case */

# include
# include

/*
* variables for handling the data file
*/
static int histfd;
static int hsize;

static int hist_count_lines ARGS((unsigned char *, int));
static int hist_shrink ARGS((unsigned char *, int));
static unsigned char *hist_skip_back ARGS((unsigned char *,int *,int));
static void histload ARGS((Source *, unsigned char *, int));
static void histinsert ARGS((Source *, int, unsigned char *));
static void writehistfile ARGS((int, char *));
static int sprinkle ARGS((int));

# ifdef MAP_FILE
# define MAP_FLAGS (MAP_FILE|MAP_PRIVATE)
# else
# define MAP_FLAGS MAP_PRIVATE
# endif

# endif /* of EASY_HISTORY */

static void histbackup ARGS((void));
static char *histrpl ARGS((char *s, char *pat, char *rep, int global));

static char **current; /* current postition in history[] */
static int curpos; /* current index in history[] */
static char *hname; /* current name of history file */
static int hstarted; /* set after hist_init() called */


int
c_fc(wp)
register char **wp;
{
register char *id;
struct shf *shf;
struct temp UNINITIALIZED(*tf);
register char **hp;
char **hbeg, **hend;
char *p, *cmd = NULL;
int lflag = 0, nflag = 0, sflag = 0, rflag = 0, gflag = 0;
int optc;

while ((optc = ksh_getopt(wp, &builtin_opt, "e:glnr")) != EOF)
switch (optc) {
case 'e':
p = builtin_opt.optarg;
if (strcmp(p, "-") == 0)
sflag++;
else {
cmd = strnsave(p, strlen(p) + 4, ATEMP);
strcat(cmd, " $_");
}
break;
case 'g': /* non-at&t ksh */
gflag++;
break;
case 'l':
lflag++;
break;
case 'n':
nflag++;
break;
case 'r':
rflag++;
break;
case '?':
return 1;
}
wp += builtin_opt.optind;

if (sflag) {
char *pat = NULL, *rep = NULL;

hp = histptr - 1;
while ((id = *wp++) != NULL) {
/* todo: multiple substitutions */
if (*id && (p = strchr(id + 1, '=')) != NULL) {
pat = id;
rep = p;
*rep++ = '\0';
} else
hp = histget(id);
}

if (hp == NULL || hp < history) {
/* XXX improve error */
bi_errorf("cannot find history");
return 1;
}
if (pat == NULL) {
int len = strlen(*hp);
p = strnsave(*hp, len + 1, source->areap);
p[len] = '\n';
p[len + 1] = '\0';
} else if (!(p = histrpl(*hp, pat, rep, gflag)))
return 1;
histbackup();
histsave(source->line+1, p, 1);
histpush--;
return 0;
}

if (*wp != NULL) {
hbeg = histget(*wp++); /* first */
if (*wp != NULL)
hend = histget(*wp++); /* last */
else if (lflag)
hend = (histptr - hbeg > 16) ? hbeg + 16 : histptr;
else
hend = hbeg;
} else {
if (lflag)
hbeg = histptr - 16, hend = histptr;
else
hbeg = hend = histptr - 1;
if (hbeg < history)
hbeg = history;
}
if (hbeg == NULL || hend == NULL) {
/* XXX improve error */
bi_errorf("can't find history");
return 1;
}

if (lflag)
shf = shf_fdopen(1, SHF_WR, (struct shf *) 0);
else {
nflag++;
tf = maketemp(ATEMP);
tf->next = e->temps; e->temps = tf;
shf = shf_open(tf->name, O_WRONLY|O_CREAT|O_TRUNC, 0666, 0);
if (shf == NULL) {
bi_errorf("cannot create temp file %s", tf->name);
return 1;
}
}

for (hp = (rflag ? hend : hbeg); rflag ? (hp >= hbeg) : (hp <= hend);
rflag ? hp-- : hp++) {
if (lflag) {
char *s, *t;

if (!nflag)
shf_fprintf(shf, "%d",
source->line - (int)(histptr-hp));
shf_putchar('\t', shf);
/* print multi-line commands correctly */
for (s = *hp; (t = strchr(s, '\n')); s = t)
shf_fprintf(shf, "%.*s\t", ++t - s, s, null);
shf_fprintf(shf, "%s\n", s);
} else
shf_fprintf(shf, "%s\n", *hp);
}

shf_flush(shf);
if (lflag)
return 0;
shf_close(shf);

setstr(local("_"), tf->name);

/* XXX: source should not get trashed by this.. */
{
Source *sold = source;

if (cmd)
command(cmd); /* edit temp file */
else
command("${FCEDIT:-/bin/ed} $_");
source = sold;
}

{
struct stat statb;
XString xs;
char *xp, *p;
int n;
int first = 1;

if (!(shf = shf_open(tf->name, O_RDONLY, 0666, 0))) {
bi_errorf("cannot open temp file %s", tf->name);
return 1;
}

histbackup();
n = fstat(shf_fileno(shf), &statb) < 0 ? 128
: statb.st_size + 1;
Xinit(xs, xp, n, source->areap);
while (1) {
/* read a line */
xp = Xstring(xs, xp);
while ((p = shf_getse(xp, Xnleft(xs, xp), shf))) {
xp = p;
if (Xstring(xs, xp) != xp && p[-1] == '\n')
break;
XcheckN(xs, xp, Xlength(xs, xp));
}
if (shf_error(shf)) {
bi_errorf("error reading temp file %s - %s",
tf->name, strerror(shf_errno(shf)));
shf_close(shf);
return 1;
}
if (!p && xp == Xstring(xs, xp))
break;
strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
# ifdef EASY_HISTORY
if (!first) {
histappend(Xstring(xs, xp), 1);
continue;
}
first = 0;
# endif
histpush--;
histsave(source->line+1, Xstring(xs, xp), 1);
}
shf_close(shf);
}

return 0;
}

/******************************/
/* Back up over last histsave */
/******************************/
static void
histbackup()
{
static int last_line = -1;

if (histptr > history && last_line != source->line) {
source->line--;
afree((void*)*histptr, APERM);
histptr--;
last_line = source->line;
}
}

/*
* get pointer to history given pattern
* pattern is a number or string
*/
char **
histget(str)
char *str;
{
register char **hp = NULL;
int n;

if (getn(str, &n)) {
if (n < 0)
hp = histptr + n;
else
hp = histptr + (n - source->line);
} else {
int anchored = *str == '?' ? (++str, 0) : 1;

hp = &history[findhist((histptr - 1) - history, 0, str,
anchored)];
}

return (history <= hp && hp <= histptr) ? hp : NULL;
}

static char *
histrpl(s, pat, rep, global)
char *s;
char *pat, *rep;
int global;
{
char *s1, *last = NULL;
int pat_len = strlen(pat);
int rep_len = strlen(rep);
XString xs;
char *xp;

Xinit(xs, xp, 128, source->areap);
while ((s1 = strstr(s, pat))) {
XcheckN(xs, xp, (s1 - s) + rep_len);
memcpy(xp, s, s1 - s); /* first part */
xp += s1 - s;
memcpy(xp, rep, rep_len); /* replacement */
xp += rep_len;
s = last = s1 + pat_len;
if (!global)
break;
}
if (last) {
int last_len = strlen(last);

XcheckN(xs, xp, last_len);
memcpy(xp, last, last_len);
xp += last_len;
*xp++ = '\n';
*xp++ = '\0';
} else {
bi_errorf("substitution failed");
Xfree(xs, xp);
return (char *) 0;
}
return Xclose(xs, xp);
}

/*
* Return the current position.
*/
char **
histpos()
{
return current;
}

int
histN()
{
return curpos;
}

int
histnum(n)
int n;
{
int last = histptr - history;

if (n < 0 || n >= last) {
current = histptr;
curpos = last;
return last;
} else {
current = &history[n];
curpos = n;
return n;
}
}

/*
* This will become unecessary if histget is modified to allow
* searching from positions other than the end, and in either
* direction.
*/
int
findhist(start, fwd, str, anchored)
int start;
int fwd;
char *str;
int anchored;
{
char **hp;
int maxhist = histptr - history;
int incr = fwd ? 1 : -1;
int len = strlen(str);

if (start < 0 || start >= maxhist)
start = maxhist;

hp = &history[start];
for (; hp >= history && hp <= histptr; hp += incr)
if ((anchored && strncmp(*hp, str, len) == 0)
|| (!anchored && strstr(*hp, str)))
return hp - history;

return -1;
}

/*
* set history
* this means reallocating the dataspace
*/
void
sethistsize(n)
int n;
{
if (n > 0 && n != histsize) {
int cursize = histptr - history;

/* save most recent history */
if (n < cursize) {
memmove(history, histptr - n, n * sizeof(char *));
cursize = n;
}

history = (char **)aresize(history, n*sizeof(char *), APERM);

histsize = n;
histptr = history + cursize;
}
}

/*
* set history file
* This can mean reloading/resetting/starting history file
* maintenance
*/
void
sethistfile(name)
char *name;
{
/* if not started then nothing to do */
if (hstarted == 0)
return;

/* if the name is the same as the name we have */
if (hname && strcmp(hname, name) == 0)
return;

/*
* its a new name - possibly
*/
# ifdef EASY_HISTORY
if (hname) {
afree(hname, APERM);
hname = NULL;
}
# else
if (histfd) {
/* yes the file is open */
(void) close(histfd);
histfd = 0;
hsize = 0;
afree(hname, APERM);
hname = NULL;
/* let's reset the history */
histptr = history - 1;
source->line = 0;
}
# endif

hist_init(source);
}

/*
* initialise the history vector
*/
void
init_histvec()
{
if (history == (char **)NULL) {
histsize = HISTORYSIZE;
history = (char **)alloc(histsize*sizeof (char *), APERM);
histptr = history - 1;
}
}

# ifdef EASY_HISTORY
/*
* save command in history
*/
void
histsave(lno, cmd, dowrite)
int lno; /* ignored (compatibility with COMPLEX_HISTORY) */
char *cmd;
int dowrite; /* ignored (compatibility with COMPLEX_HISTORY) */
{
register char **hp = histptr;
char *cp;

if (++hp >= history + histsize) { /* remove oldest command */
afree((void*)history[0], APERM);
memmove(history, history + 1,
sizeof(history[0]) * (histsize - 1));
hp = &history[histsize - 1];
}
*hp = strsave(cmd, APERM);
/* trash trailing newline but allow imbedded newlines */
cp = *hp + strlen(*hp);
if (cp > *hp && cp[-1] == '\n')
cp[-1] = '\0';
histptr = hp;
}

/*
* Append an entry to the last saved command. Used for multiline
* commands
*/
void
histappend(cmd, nl_seperate)
char *cmd;
int nl_seperate;
{
int hlen, clen;
char *p;

hlen = strlen(*histptr);
clen = strlen(cmd);
if (clen > 0 && cmd[clen-1] == '\n')
clen--;
p = *histptr = (char *) aresize(*histptr, hlen + clen + 2, APERM);
p += hlen;
if (nl_seperate)
*p++ = '\n';
memcpy(p, cmd, clen);
p[clen] = '\0';
}

/*
* 92-04-25
* A simple history file implementation.
* At present we only save the history when we exit.
* This can cause problems when there are multiple shells are
* running under the same user-id. The last shell to exit gets
* to save its history.
*/
void
hist_init(s)
Source *s;
{
char *f;
FILE *fh;

if (Flag(FTALKING) == 0)
return;

hstarted = 1;

if ((f = strval(global("HISTFILE"))) == NULL || *f == '\0') {
# if 1 /* Don't use history file unless the user asks for it */
hname = NULL;
return;
# else
char *home = strval(global("HOME"));
int len;

if (home == NULL)
home = null;
f = HISTFILE;
hname = alloc(len = strlen(home) + strlen(f) + 2, APERM);
shf_snprintf(hname, len, "%s/%s", home, f);
# endif
} else
hname = strsave(f, APERM);

if ((fh = fopen(hname, "r"))) {
int pos = 0, nread = 0;
int contin = 0; /* continuation of previous command */
char *end;
char hline[LINE + 1];

while (1) {
if (pos >= nread) {
pos = 0;
nread = fread(hline, 1, LINE, fh);
if (nread <= 0)
break;
hline[nread] = '\0';
}
end = strchr(hline + pos, 0); /* will always succeed */
if (contin)
histappend(hline + pos, 0);
else
histsave(0, hline + pos, 0);
pos = end - hline + 1;
contin = end == &hline[nread];
}
fclose(fh);
}
}

/*
* save our history.
* We check that we do not have more than we are allowed.
* If the history file is read-only we do nothing.
* Handy for having all shells start with a useful history set.
*/

void
hist_finish()
{
static int once = 0;
FILE *fh;
register int i;
register char **hp;

if (once++)
return;
/* check how many we have */
i = histptr - history;
if (i >= histsize)
hp = &histptr[-histsize];
else
hp = history;
if (hname && (fh = fopen(hname, "w")))
{
for (i = 0; hp + i <= histptr && hp[i]; i++)
fprintf(fh, "%s%c", hp[i], '\0');
fclose(fh);
}
}

# else /* EASY_HISTORY */

/*
* Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
* a) permit HISTSIZE to control number of lines of history stored
* b) maintain a physical history file
*
* It turns out that there is a lot of ghastly hackery here
*/


/*
* save command in history
*/
void
histsave(lno, cmd, dowrite)
int lno;
char *cmd;
int dowrite;
{
register char **hp;
char *cp;

cmd = strsave(cmd, APERM);
if ((cp = strchr(cmd, '\n')) != NULL)
*cp = '\0';

if (histfd && dowrite)
writehistfile(lno, cmd);

hp = histptr;

if (++hp >= history + histsize) { /* remove oldest command */
afree((void*)*history, APERM);
for (hp = history; hp < history + histsize - 1; hp++)
hp[0] = hp[1];
}
*hp = cmd;
histptr = hp;
}

/*
* Write history data to a file nominated by HISTFILE
* if HISTFILE is unset then history still happens, but
* the data is not written to a file
* All copies of ksh looking at the file will maintain the
* same history. This is ksh behaviour.
*
* This stuff uses mmap()
* if your system ain't got it - then you'll have to undef HISTORYFILE
*/

/*
* Open a history file
* Format is:
* Bytes 1, 2: HMAGIC - just to check that we are dealing with
* the correct object
* Then follows a number of stored commands
* Each command is
*
*/
# define HMAGIC1 0xab
# define HMAGIC2 0xcd
# define COMMAND 0xff

void
hist_init(s)
Source *s;
{
unsigned char *base;
int lines;
int fd;

if (Flag(FTALKING) == 0)
return;

hstarted = 1;

hname = strval(global("HISTFILE"));
if (hname == NULL)
return;
hname = strsave(hname, APERM);

retry:
/* we have a file and are interactive */
if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0)
return;

histfd = savefd(fd);

(void) flock(histfd, LOCK_EX);

hsize = lseek(histfd, 0L, L_XTND);

if (hsize == 0) {
/* add magic */
if (sprinkle(histfd)) {
hist_finish();
return;
}
}
else if (hsize > 0) {
/*
* we have some data
*/
base = (unsigned char *)mmap(0, hsize, PROT_READ, MAP_FLAGS, histfd, 0);
/*
* check on its validity
*/
if ((int)base == -1 || *base != HMAGIC1 || base[1] != HMAGIC2) {
if ((int)base != -1)
munmap((caddr_t)base, hsize);
hist_finish();
unlink(hname);
goto retry;
}
if (hsize > 2) {
lines = hist_count_lines(base+2, hsize-2);
if (lines > histsize) {
/* we need to make the file smaller */
if (hist_shrink(base, hsize))
unlink(hname);
munmap((caddr_t)base, hsize);
hist_finish();
goto retry;
}
}
histload(s, base+2, hsize-2);
munmap((caddr_t)base, hsize);
}
(void) flock(histfd, LOCK_UN);
hsize = lseek(histfd, 0L, L_XTND);
}

typedef enum state {
shdr, /* expecting a header */
sline, /* looking for a null byte to end the line */
sn1, /* bytes 1 to 4 of a line no */
sn2, sn3, sn4,
} State;

static int
hist_count_lines(base, bytes)
register unsigned char *base;
register int bytes;
{
State state = shdr;
register lines = 0;

while (bytes--) {
switch (state)
{
case shdr:
if (*base == COMMAND)
state = sn1;
break;
case sn1:
state = sn2; break;
case sn2:
state = sn3; break;
case sn3:
state = sn4; break;
case sn4:
state = sline; break;
case sline:
if (*base == '\0')
lines++, state = shdr;
}
base++;
}
return lines;
}

/*
* Shrink the history file to histsize lines
*/
static int
hist_shrink(oldbase, oldbytes)
unsigned char *oldbase;
int oldbytes;
{
int fd;
char nfile[1024];
struct stat statb;
unsigned char *nbase = oldbase;
int nbytes = oldbytes;

nbase = hist_skip_back(nbase, &nbytes, histsize);
if (nbase == NULL)
return 1;
if (nbase == oldbase)
return 0;

/*
* create temp file
*/
(void) shf_snprintf(nfile, sizeof(nfile), "%s.%d", hname, procpid);
if ((fd = creat(nfile, 0600)) < 0)
return 1;

if (sprinkle(fd)) {
close(fd);
unlink(nfile);
return 1;
}
if (write(fd, nbase, nbytes) != nbytes) {
close(fd);
unlink(nfile);
return 1;
}
/*
* worry about who owns this file
*/
if (fstat(histfd, &statb) >= 0)
fchown(fd, statb.st_uid, statb.st_gid);
close(fd);

/*
* rename
*/
if (rename(nfile, hname) < 0)
return 1;
return 0;
}


/*
* find a pointer to the data `no' back from the end of the file
* return the pointer and the number of bytes left
*/
static unsigned char *
hist_skip_back(base, bytes, no)
unsigned char *base;
int *bytes;
int no;
{
register int lines = 0;
register unsigned char *ep;

for (ep = base + *bytes; --ep > base; ) {
/* this doesn't really work: the 4 byte line number that is
* encoded after the COMMAND byte can itself contain the
* COMMAND byte....
*/
for (; ep > base && *ep != COMMAND; ep--)
;
if (ep == base)
break;
if (++lines == no) {
*bytes = *bytes - ((char *)ep - (char *)base);
return ep;
}
}
return NULL;
}

/*
* load the history structure from the stored data
*/
static void
histload(s, base, bytes)
Source *s;
register unsigned char *base;
register int bytes;
{
State state;
int lno;
unsigned char *line;

for (state = shdr; bytes-- > 0; base++) {
switch (state) {
case shdr:
if (*base == COMMAND)
state = sn1;
break;
case sn1:
lno = (((*base)&0xff)<<24);
state = sn2;
break;
case sn2:
lno |= (((*base)&0xff)<<16);
state = sn3;
break;
case sn3:
lno |= (((*base)&0xff)<<8);
state = sn4;
break;
case sn4:
lno |= (*base)&0xff;
line = base+1;
state = sline;
break;
case sline:
if (*base == '\0') {
/* worry about line numbers */
if (histptr >= history && lno-1 != s->line) {
/* a replacement ? */
histinsert(s, lno, line);
}
else {
s->line = lno;
histsave(lno, (char *)line, 0);
}
state = shdr;
}
}
}
}

/*
* Insert a line into the history at a specified number
*/
static void
histinsert(s, lno, line)
Source *s;
int lno;
unsigned char *line;
{
register char **hp;

if (lno >= s->line-(histptr-history) && lno <= s->line) {
hp = &histptr[lno-s->line];
if (*hp)
afree((void*)*hp, APERM);
*hp = strsave((char *)line, APERM);
}
}

/*
* write a command to the end of the history file
* This *MAY* seem easy but it's also necessary to check
* that the history file has not changed in size.
* If it has - then some other shell has written to it
* and we should read those commands to update our history
*/
static void
writehistfile(lno, cmd)
int lno;
char *cmd;
{
int sizenow;
unsigned char *base;
unsigned char *new;
int bytes;
char hdr[5];

(void) flock(histfd, LOCK_EX);
sizenow = lseek(histfd, 0L, L_XTND);
if (sizenow != hsize) {
/*
* Things have changed
*/
if (sizenow > hsize) {
/* someone has added some lines */
bytes = sizenow - hsize;
base = (unsigned char *)mmap(0, sizenow, PROT_READ, MAP_FLAGS, histfd, 0);
if ((int)base == -1)
goto bad;
new = base + hsize;
if (*new != COMMAND) {
munmap((caddr_t)base, sizenow);
goto bad;
}
source->line--;
histload(source, new, bytes);
source->line++;
lno = source->line;
munmap((caddr_t)base, sizenow);
hsize = sizenow;
} else {
/* it has shrunk */
/* but to what? */
/* we'll give up for now */
goto bad;
}
}
/*
* we can write our bit now
*/
hdr[0] = COMMAND;
hdr[1] = (lno>>24)&0xff;
hdr[2] = (lno>>16)&0xff;
hdr[3] = (lno>>8)&0xff;
hdr[4] = lno&0xff;
(void) write(histfd, hdr, 5);
(void) write(histfd, cmd, strlen(cmd)+1);
hsize = lseek(histfd, 0L, L_XTND);
(void) flock(histfd, LOCK_UN);
return;
bad:
hist_finish();
}

void
hist_finish()
{
(void) flock(histfd, LOCK_UN);
(void) close(histfd);
histfd = 0;
}

/*
* add magic to the history file
*/
static int
sprinkle(fd)
int fd;
{
static char mag[] = { HMAGIC1, HMAGIC2 };

return(write(fd, mag, 2) != 2);
}

# endif
#else /* HISTORY */

/* No history to be compiled in: dummy routines to avoid lots more ifdefs */
void
init_histvec()
{
}
void
hist_init(s)
Source *s;
{
}
void
hist_finish()
{
}
void
histsave(lno, cmd, dowrite)
int lno;
char *cmd;
int dowrite;
{
errorf("history not enabled");
}
#endif /* HISTORY */
pdksh-5.1.2/io.c100644 0 133 20132 5666702647 12262 0ustar rootlsource/*
* shell buffered IO and formatted output
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: io.c,v 1.2 1994/05/19 18:32:40 michael Exp michael $";
#endif

#include
#include "sh.h"
#include "ksh_stat.h"

/*
* formatted output functions
*/


/* A shell error occured (eg, syntax error, etc.) */
void
#ifdef HAVE_PROTOTYPES
errorf(const char *fmt, ...)
#else
errorf(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list va;

shl_stdout_ok = 0; /* debugging: note that stdout not valid */
exstat = 1;
if (fmt) {
error_prefix(TRUE);
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_putchar('\n', shl_out);
}
shf_flush(shl_out);
unwind(LERROR);
}

/* like errorf(), but no unwind is done */
void
#ifdef HAVE_PROTOTYPES
warningf(int fileline, const char *fmt, ...)
#else
warningf(fileline, fmt, va_alist)
int fileline;
const char *fmt;
va_dcl
#endif
{
va_list va;

error_prefix(fileline);
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_flush(shl_out);
}

/* Used by built-in utilities to prefix shell and utility name to message
* (also unwinds environments for special builtins).
*/
void
#ifdef HAVE_PROTOTYPES
bi_errorf(const char *fmt, ...)
#else
bi_errorf(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list va;

shl_stdout_ok = 0; /* debugging: note that stdout not valid */
exstat = 1;
if (fmt) {
error_prefix(TRUE);
/* not set when main() calls parse_args() */
if (builtin_argv0)
shf_fprintf(shl_out, "%s: ", builtin_argv0);
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_putchar('\n', shl_out);
}
shf_flush(shl_out);
/* POSIX special builtins and ksh special builtins cause
* non-interactive shells to exit.
* XXX odd use of KEEPASN; also may not want LERROR here
*/
if ((builtin_flag & SPEC_BI)
|| (Flag(FPOSIX) && (builtin_flag & KEEPASN)))
{
builtin_argv0 = (char *) 0;
unwind(LERROR);
}
}

/* Called when something that shouldn't happen does */
void
#ifdef HAVE_PROTOTYPES
internal_errorf(int jump, const char *fmt, ...)
#else
internal_errorf(jump, fmt, va_alist)
int jump;
const char *fmt;
va_dcl
#endif
{
va_list va;

error_prefix(TRUE);
shf_fprintf(shl_out, "internal error: ");
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_putchar('\n', shl_out);
shf_flush(shl_out);
if (jump)
unwind(LERROR);
}

/* used by error reporting functions to print "ksh: .kshrc[25]: " */
void
error_prefix(fileline)
int fileline;
{
shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
if (fileline && source && source->file != NULL) {
shf_fprintf(shl_out, "%s[%d]: ", source->file,
source->errline > 0 ? source->errline : source->line);
source->errline = 0;
}
}

/* printf to shl_out (stderr) with flush */
void
#ifdef HAVE_PROTOTYPES
shellf(const char *fmt, ...)
#else
shellf(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list va;

SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
shf_flush(shl_out);
}

/* printf to shl_stdout (stdout) */
void
#ifdef HAVE_PROTOTYPES
shprintf(const char *fmt, ...)
#else
shprintf(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list va;

if (!shl_stdout_ok)
internal_errorf(1, "shl_stdout not valid");
SH_VA_START(va, fmt);
shf_vfprintf(shl_stdout, fmt, va);
va_end(va);
}

/* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */
int
can_seek(fd)
int fd;
{
struct stat statb;

return fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ?
SHF_UNBUF : 0;
}

struct shf shf_iob[3];

void
initio()
{
shf_fdopen(1, SHF_WR, shl_stdout); /* force buffer allocation */
shf_fdopen(2, SHF_WR, shl_out);
shf_fdopen(2, SHF_WR, shl_spare); /* force buffer allocation */
}

/* A dup2() with error checking */
int
ksh_dup2(ofd, nfd, errok)
int ofd;
int nfd;
int errok;
{
int ret = dup2(ofd, nfd);

if (ret < 0 && errno != EBADF && !errok)
errorf("too many files open in shell");

#ifdef DUP2_BROKEN
/* Ultrix systems like to preserve the close-on-exec flag */
if (ret >= 0)
(void) fcntl(nfd, F_SETFD, 0);
#endif /* DUP2_BROKEN */

return ret;
}

/*
* move fd from user space (0<=fd<10) to shell space (fd>=10),
* set close-on-exec flag.
*/
int
savefd(fd)
int fd;
{
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");
close(fd);
} else
nfd = fd;
fd_clexec(nfd);
return nfd;
}

void
restfd(fd, ofd)
int fd, ofd;
{
if (fd == 2)
shf_flush(&shf_iob[fd]);
if (ofd < 0) /* original fd closed */
close(fd);
else {
ksh_dup2(ofd, fd, TRUE); /* XXX: what to do if this fails? */
close(ofd);
}
}

void
openpipe(pv)
register int *pv;
{
if (pipe(pv) < 0)
errorf("can't create pipe - try again");
pv[0] = savefd(pv[0]);
pv[1] = savefd(pv[1]);
}

void
closepipe(pv)
register int *pv;
{
close(pv[0]);
close(pv[1]);
}

/* Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn
* a string (the X in 2>&X, read -uX, print -uX) into a file descriptor.
*/
int
check_fd(name, mode, emsgp)
char *name;
int mode;
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 get_coproc_fd(mode, emsgp);
if (emsgp)
*emsgp = "illegal file descriptor name";
return -1;
}

/* Called once from main */
void
coproc_init()
{
coproc.read = coproc.readw = coproc.write = -1;
}

/* Called by c_read() when eof is read - close fd if it is the co-process fd */
void
coproc_read_close(fd)
int fd;
{
if (coproc.read >= 0 && fd == coproc.read) {
close(coproc.read);
coproc.read = -1;
if (coproc.readw >= 0) {
close(coproc.readw);
coproc.readw = -1;
}
}
}

/* Called by c_read() and by iosetup() to close the other side of the
* read pipe, so reads will actually terminate.
*/
void
coproc_readw_close(fd)
int fd;
{
if (coproc.read >= 0 && fd == coproc.read && coproc.readw >= 0) {
close(coproc.readw);
coproc.readw = -1;
}
}

/* Called by c_print when a write to a fd fails with EPIPE and by iosetup
* when co-process input is dup'd
*/
void
coproc_write_close(fd)
int fd;
{
if (coproc.write >= 0 && fd == coproc.write) {
close(coproc.write);
coproc.write = -1;
}
}

/* Called to check for existance of/value of the co-process file descriptor.
* (Used by check_fd() and by c_read/c_print to deal with -p option).
*/
int
get_coproc_fd(mode, emsgp)
int mode;
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) */
void
cleanup_coproc(reuse)
int reuse;
{
coproc.job = (void *) 0;
/* 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(ap)
Area *ap;
{
static unsigned int inc = 0;
struct temp *tp;
int len;
char *path, *tmp;

tmp = tmpdir ? tmpdir : "/tmp";
/* The 20 + 20 is a paranoid worst case for pid/inc */
len = strlen(tmp) + 3 + 20 + 20 + 1;
tp = (struct temp *) alloc(sizeof(struct temp) + len, ap);
tp->name = path = (char *) &tp[1];
shf_snprintf(path, len, "%s/sh%05u%02u",
tmp, (unsigned) procpid, inc++);
close(creat(path, 0600)); /* to get safe permissions */
tp->next = NULL;
tp->pid = procpid;
return tp;
}
pdksh-5.1.2/jobs.c100644 0 133 122041 5667372224 12625 0ustar rootlsource/*
* Process and job control
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: jobs.c,v 1.2 1994/05/19 18:32:40 michael Exp michael $";
#endif

/*
* Reworked/Rewritten version of Eric Gisin's/Ron Natalie's code by
* Larry Bouzane ([email protected]) and hacked again by
* Michael Rendell ([email protected])
*
* The interface to the rest of the shell should probably be changed
* to allow use of vfork() when available but that would be way too much
* work ๐Ÿ™‚
*
* Notes regarding the copious ifdefs:
* - JOB_SIGS is independent of JOBS - it is defined if there are modern
* signal and wait routines available. This is prefered, even when
* JOBS is not defined, since the shell will not otherwise notice when
* background jobs die until the shell waits for a foreground process
* to die.
* - TTY_PGRP defined iff JOBS is defined - defined if there are tty
* process groups
* - NEED_PGRP_SYNC defined iff JOBS is defined - see comment below
*/

#include "sh.h"
#include "ksh_wait.h"
#include "ksh_times.h"
#include "tty.h"

/* Start of system configuration stuff */

/* We keep CHILD_MAX zombie processes around (exact value isn't critical) */
#ifndef CHILD_MAX
# if defined(HAVE_SYSCONF) && defined(_SC_CHILD_MAX)
# define CHILD_MAX sysconf(_SC_CHILD_MAX)
# else /* _SC_CHILD_MAX */
# ifdef _POSIX_CHILD_MAX
# define CHILD_MAX ((_POSIX_CHILD_MAX) * 2)
# else /* _POSIX_CHILD_MAX */
# define CHILD_MAX 20
# endif /* _POSIX_CHILD_MAX */
# endif /* _SC_CHILD_MAX */
#endif /* !CHILD_MAX */

#ifdef JOBS
# if defined(HAVE_TCSETPGRP) || defined(TIOCSPGRP)
# define TTY_PGRP
# endif
# ifdef BSD_PGRP
# define setpgid setpgrp
# define getpgID() getpgrp(0)
# else
# define getpgID() getpgrp()
# endif
# if !defined(HAVE_TCSETPGRP) && defined(TIOCSPGRP)
int tcsetpgrp ARGS((int fd, pid_t grp));
int tcgetpgrp ARGS((int fd));

int
tcsetpgrp(fd, grp)
int fd;
pid_t grp;
{
return ioctl(fd, TIOCSPGRP, &grp);
}

int
tcgetpgrp(fd)
int fd;
{
int r, grp;

if ((r = ioctl(fd, TIOCGPGRP, &grp)) < 0)
return r;
return grp;
}
# endif /* !HAVE_TCSETPGRP && TIOCSPGRP */
#else /* JOBS */
/* These so we can use ifdef xxx instead of if defined(JOBS) && defined(xxx) */
# undef TTY_PGRP
# undef NEED_PGRP_SYNC
#endif /* JOBS */

/* End of system configuration stuff */


/* Order important! */
#define PRUNNING 0
#define PEXITED 1
#define PSIGNALLED 2
#define PSTOPPED 3

typedef struct proc Proc;
struct proc {
Proc *next; /* next process in pipeline (if any) */
int state;
WAIT_T status; /* wait status */
pid_t pid; /* process id */
char command[48]; /* process command string */
};

/* Notify/print flag - j_print() argument */
#define JP_NONE 0 /* don't print anything */
#define JP_SHORT 1 /* print signals processes were killed by */
#define JP_MEDIUM 2 /* print [job-num] -/+ command */
#define JP_LONG 3 /* print [job-num] -/+ pid command */
#define JP_PGRP 4 /* print pgrp */

/* put_job() flags */
#define PJ_ON_FRONT 0 /* at very front */
#define PJ_PAST_STOPPED 1 /* just past any stopped jobs */

/* Job.flags values */
#define JF_STARTED 0x001 /* set when all processes in job are started */
#define JF_WAITING 0x002 /* set if j_waitj() is waiting on job */
#define JF_W_ASYNCNOTIFY 0x004 /* set if waiting and async notification ok */
#define JF_XXCOM 0x008 /* set for `command` jobs */
#define JF_FG 0x010 /* running in foreground (also has tty pgrp) */
#define JF_SAVEDTTY 0x020 /* j->ttystate is valid */
#define JF_CHANGED 0x040 /* process has changed state */
#define JF_KNOWN 0x080 /* $! referenced */
#define JF_ZOMBIE 0x100 /* known, unwaited process */
#define JF_REMOVE 0x200 /* flaged for removal (j_jobs()/j_noityf()) */
#define JF_ORIGFG 0x400 /* originally started in foreground */

typedef struct job Job;
struct job {
Job *next; /* next job in list */
int job; /* job number: %n */
int flags; /* see JF_* */
int state; /* job state */
int status; /* exit status of last process */
pid_t pgrp; /* process group of job */
pid_t ppid; /* pid of process that forked job */
INT32 age; /* number of jobs started */
clock_t stime; /* system time used by job */
clock_t utime; /* user time used by job */
Proc *proc_list; /* process list */
Proc *last_proc; /* last process in list */
#ifdef TTY_PGRP
TTY_state ttystate; /* saved tty state for stopped jobs */
#endif /* TTY_PGRP */
};

/* Flags for j_waitj() */
#define JW_NONE 0x00
#define JW_INTERRUPT 0x01 /* ^C will stop the wait */
#define JW_ASYNCNOTIFY 0x02 /* asynchronous notification during wait ok */
#define JW_STOPPEDWAIT 0x04 /* wait even if job stopped */

/* Error codes for j_lookup() */
#define JL_OK 0
#define JL_NOSUCH 1 /* no such job */
#define JL_AMBIG 2 /* %foo or %?foo is ambiguous */
#define JL_INVALID 3 /* non-pid, non-% job id */

static char *lookup_msgs[] = {
null,
"no such job",
"ambiguous",
"argument must be %job or process id",
(char *) 0
};
clock_t j_stime, j_utime; /* user and system time of last j_waitjed job */

static Job *job_list = (Job *) 0; /* job list */
static Job *last_job = (Job *) 0;
static Job *async_job = (Job *) 0;
static pid_t async_pid;

static int nzombie; /* # of zombies owned by this process */
static INT32 njobs; /* # of jobs started */
static int child_max; /* CHILD_MAX */


#ifdef JOB_SIGS
static sigset_t sm_default, sm_sigchld;
/* held_sigchld is set if sigchld occurs before a job is completely started */
static int held_sigchld;
#endif /* JOB_SIGS */

#ifdef JOBS
static struct shf *shl_j;
#endif /* JOBS */

#ifdef NEED_PGRP_SYNC
/* On some systems, the kernel doesn't count zombie processes when checking
* if a process group is valid, which can cause problems in creating the
* pipeline "cmd1 | cmd2": if cmd1 can die (and go into the zombie state)
* before cmd2 is started, the kernel doesn't allow the setpgrp() for cmd2
* to succeed. Solution is to create a pipe between the parent and the first
* process; the first process doesn't do anything until the pipe is closed
* and the parent doesn't close the pipe until all the processes are started.
*/
static int j_sync_pipe[2];
static int j_sync_open;
#endif /* NEED_PGRP_SYNC */

#ifdef TTY_PGRP
static int ttypgrp_ok; /* set if can use tty pgrps */
static pid_t restore_ttypgrp = -1;
static pid_t our_pgrp;
static int tt_sigs[] = { SIGTSTP, SIGTTIN, SIGTTOU };
#endif /* TTY_PGRP */

static void j_set_async ARGS((Job *j));
static void j_startjob ARGS((Job *j));
static int j_waitj ARGS((Job *j, int flags, char *where));
static RETSIGTYPE j_sigchld ARGS((int sig));
static void j_print ARGS((Job *j, int how, struct shf *shf));
static Job *j_lookup ARGS((char *cp, int *ecodep));
static Job *new_job ARGS((void));
static Proc *new_proc ARGS((void));
static void check_job ARGS((Job *j));
static void put_job ARGS((Job *j, int where));
static void remove_job ARGS((Job *j, char *where));
static void kill_job ARGS((Job *j));
static void fill_command ARGS((char *c, int len, struct op *t));

/* initialize job control */
void
j_init(mflagset)
int mflagset;
{
child_max = CHILD_MAX; /* so syscon() isn't always being called */

#ifdef JOB_SIGS
sigemptyset(&sm_default);
sigprocmask(SIG_SETMASK, &sm_default, (sigset_t *) 0);

sigemptyset(&sm_sigchld);
sigaddset(&sm_sigchld, SIGCHLD);

setsig(&sigtraps[SIGCHLD], j_sigchld, SS_RESTORE_ORIG|SS_FORCE);
#else /* JOB_SIGS */
/* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */
setsig(&sigtraps[SIGCHLD], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE);
#endif /* JOB_SIGS */

#ifdef JOBS
if (!mflagset && Flag(FTALKING))
Flag(FMONITOR) = 1;

/* shl_j is used to do asynchronous notification (used in
* an interrupt handler, so need a distinct shf)
*/
shl_j = shf_fdopen(2, SHF_WR, (struct shf *) 0);

# ifdef TTY_PGRP
if (Flag(FMONITOR) || Flag(FTALKING)) {
int i;

/* j_change() sets these to SS_RESTORE_DFL if FMONITOR */
for (i = 3; --i >= 0; ) {
sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES;
setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
SS_RESTORE_IGN|SS_FORCE);
}
}
# endif /* TTY_PGRP */

/* j_change() calls tty_init() */
if (Flag(FMONITOR))
j_change();
else
#endif /* JOBS */
if (Flag(FTALKING))
tty_init(TRUE);
}

/* job cleanup before shell exit */
void
j_exit()
{
#ifdef JOBS
/* kill stopped jobs */
Job *j;
int killed = 0;

for (j = job_list; j != (Job *) 0; j = j->next)
if (j->ppid == procpid && j->state == PSTOPPED) {
killed = 1;
killpg(j->pgrp, SIGCONT);
killpg(j->pgrp, SIGHUP);
}
if (killed)
sleep(1);
#endif /* JOBS */
j_notify();

#ifdef JOBS
# ifdef TTY_PGRP
if (kshpid == procpid && restore_ttypgrp >= 0) {
/* Need to restore the tty pgrp to what it was when the
* shell started up, so that the process that started us
* will be able to access the tty when we are done.
* Also need to restore our process group in case we are
* about to do an exec so that both our parent and the
* process we are to become will be able to access the tty.
*/
tcsetpgrp(tty_fd, restore_ttypgrp);
setpgid(0, restore_ttypgrp);
}
# endif /* TTY_PGRP */
if (Flag(FMONITOR)) {
Flag(FMONITOR) = 0;
j_change();
}
#endif /* JOBS */
}

#ifdef JOBS
/* turn job control on or off according to Flag(FMONITOR) */
void
j_change()
{
int i;

if (Flag(FMONITOR)) {
/* Don't call get_tty() 'til we own the tty process group */
tty_init(FALSE);

# ifdef TTY_PGRP
/* no controlling tty, no SIGT* */
ttypgrp_ok = tty_fd >= 0 && tty_devtty;

if (ttypgrp_ok && (our_pgrp = getpgID()) < 0) {
warningf(FALSE, "j_init: getpgrp() failed: %s\n",
strerror(errno));
ttypgrp_ok = 0;
}
if (ttypgrp_ok) {
setsig(&sigtraps[SIGTTIN], SIG_DFL,
SS_RESTORE_ORIG|SS_FORCE);
/* wait to be given tty (POSIX.1, B.2, job control) */
while (1) {
pid_t ttypgrp;

if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
warningf(FALSE,
"j_init: tcgetpgrp() failed: %s\n",
strerror(errno));
ttypgrp_ok = 0;
break;
}
if (ttypgrp == our_pgrp)
break;
kill(0, SIGTTIN);
}
}
for (i = 3; --i >= 0; )
setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
SS_RESTORE_DFL|SS_FORCE);
if (ttypgrp_ok && our_pgrp != kshpid) {
if (setpgid(0, kshpid) < 0) {
warningf(FALSE,
"j_init: setpgid() failed: %s\n",
strerror(errno));
ttypgrp_ok = 0;
} else {
if (tcsetpgrp(tty_fd, kshpid) < 0) {
warningf(FALSE,
"j_init: tcsetpgrp() failed: %s\n",
strerror(errno));
ttypgrp_ok = 0;
} else
restore_ttypgrp = our_pgrp;
our_pgrp = kshpid;
}
}
# if defined(NTTYDISC) && defined(TIOCSETD) && !defined(HAVE_TERMIOS_H) && !defined(HAVE_TERMIO_H)
if (ttypgrp_ok) {
int ldisc = NTTYDISC;

if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0)
warningf(FALSE,
"j_init: can't set new line discipline: %s\n",
strerror(errno));
}
# endif /* NTTYDISC && TIOCSETD */
if (!ttypgrp_ok)
warningf(FALSE,
"warning: won't have full job control\n");
# endif /* TTY_PGRP */
if (tty_fd >= 0)
get_tty(tty_fd, &tty_state);
} else {
# ifdef TTY_PGRP
ttypgrp_ok = 0;
/* the TF_SHELL_USES test is a kludge that lets us know if
* if the signals have been changed by the shell.
*/
if (Flag(FTALKING))
for (i = 3; --i >= 0; )
setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
SS_RESTORE_IGN|SS_FORCE);
else
for (i = 3; --i >= 0; ) {
if (sigtraps[tt_sigs[i]].flags & (TF_ORIG_IGN
|TF_ORIG_DFL))
setsig(&sigtraps[tt_sigs[i]],
(sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ? SIG_IGN : SIG_DFL,
SS_RESTORE_CURR|SS_FORCE);
}
# endif /* TTY_PGRP */
if (!Flag(FTALKING))
tty_close();
}
}
#endif /* JOBS */

/* execute tree in child subprocess */
int
exchild(t, flags, close_fd)
struct op *t;
int flags;
int close_fd; /* used if XPCLOSE or XCCLOSE */
{
static Proc *last_proc; /* for pipelines */

int i;
#ifdef JOB_SIGS
sigset_t omask;
#endif /* JOB_SIGS */
Proc *p;
Job *j;
int rv = 0;
int forksleep;
int orig_flags = flags;
int ischild;

flags &= ~(XFORK|XPCLOSE|XCCLOSE|XCOPROC);
if (flags & XEXEC)
return execute(t, flags);

#ifdef JOB_SIGS
/* no SIGCHLD's while messing with job and process lists */
sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
#endif /* JOB_SIGS */

p = new_proc();
p->next = (Proc *) 0;
p->state = PRUNNING;
WSTATUS(p->status) = 0;
p->pid = 0;

/* link process into jobs list */
if (flags&XPIPEI) { /* continuing with a pipe */
j = last_job;
last_proc->next = p;
last_proc = p;
} else {
#ifdef NEED_PGRP_SYNC
if (j_sync_open) {
closepipe(j_sync_pipe);
j_sync_open = 0;
}
/* don't do the sync pipe business if there is no pipeline */
if (flags & XPIPEO) {
openpipe(j_sync_pipe);
j_sync_open = 1;
}
#endif /* NEED_PGRP_SYNC */
j = new_job(); /* fills in j->job */
/* we don't consider XXCOM's foreground since they don't get
* tty process group and we don't save or restore tty modes.
*/
j->flags = (flags & XXCOM) ? JF_XXCOM
: ((flags & XBGND) ? 0 : (JF_FG|JF_ORIGFG));
j->utime = j->stime = 0;
j->state = PRUNNING;
j->pgrp = 0;
j->ppid = procpid;
j->age = ++njobs;
j->proc_list = p;
last_job = j;
last_proc = p;
if (flags & XXCOM)
j->flags |= JF_XXCOM;
else if (!(flags & XBGND))
j->flags |= JF_FG;
put_job(j, PJ_PAST_STOPPED);
}

fill_command(p->command, sizeof(p->command), t);

/* create child process */
forksleep = 1;
while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
sleep(forksleep);
forksleep <<= 1;
}
if (i < 0) {
kill_job(j);
remove_job(j, "fork failed");
#ifdef NEED_PGRP_SYNC
if (j_sync_open) {
closepipe(j_sync_pipe);
j_sync_open = 0;
}
#endif /* NEED_PGRP_SYNC */
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
errorf("cannot fork - try again");
}
ischild = i == 0;
if (ischild)
p->pid = procpid = getpid();
else
p->pid = i;

#ifdef JOBS
/* job control set up */
if (Flag(FMONITOR) && !(flags&XXCOM)) {
int dotty = 0;
# ifdef NEED_PGRP_SYNC
int dosync = 0;
# endif /* NEED_PGRP_SYNC */

if (j->pgrp == 0) { /* First process */
j->pgrp = p->pid;
dotty = 1;
# ifdef NEED_PGRP_SYNC
if (j_sync_open) {
close(j_sync_pipe[ischild ? 1 : 0]);
j_sync_pipe[ischild ? 1 : 0] = -1;
dosync = ischild;
}
# endif /* NEED_PGRP_SYNC */
}

/* set pgrp in both parent and child to deal with race
* condition
*/
setpgid(p->pid, j->pgrp);
# ifdef TTY_PGRP
/* YYY: should this be
if (ttypgrp_ok && ischild && !(flags&XBGND))
tcsetpgrp(tty_fd, j->pgrp);
instead? (see also YYY below)
*/
if (ttypgrp_ok && dotty && !(flags & XBGND))
tcsetpgrp(tty_fd, j->pgrp);
# endif /* TTY_PGRP */
# ifdef NEED_PGRP_SYNC
if (ischild && j_sync_open) {
if (dosync) {
char c;
while (read(j_sync_pipe[0], &c, 1) == -1
&& errno == EINTR)
;
}
close(j_sync_pipe[0]);
j_sync_open = 0;
}
# endif /* NEED_PGRP_SYNC */
}
#endif /* JOBS */

/* used to close pipe input fd */
if (close_fd >= 0 && (((orig_flags & XPCLOSE) && i != 0)
|| ((orig_flags & XCCLOSE) && i == 0)))
close(close_fd);
if (i == 0) { /* child */
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
cleanup_parents_env();
if (orig_flags & XCOPROC)
cleanup_coproc(FALSE);
#ifdef TTY_PGRP
/* If FMONITOR or FTALKING is set, these signals are ignored,
* if neither FMONITOR nor FTALKING are set, the signals have
* their inherited values.
*/
if (Flag(FMONITOR) && !(flags & XXCOM)) {
for (i = 3; --i >= 0; )
setsig(&sigtraps[tt_sigs[i]], SIG_DFL,
SS_RESTORE_DFL|SS_FORCE);
}
#endif /* TTY_PGRP */
#ifdef HAVE_NICE
if (Flag(FBGNICE) && (flags & XBGND))
nice(4);
#endif /* HAVE_NICE */
if ((flags & XBGND) && !Flag(FMONITOR)) {
setsig(&sigtraps[SIGINT], SIG_IGN,
SS_RESTORE_IGN|SS_FORCE);
setsig(&sigtraps[SIGQUIT], SIG_IGN,
SS_RESTORE_IGN|SS_FORCE);
if (!(orig_flags & (XPIPEI | XCOPROC))) {
i = open("/dev/null", 0);
(void) ksh_dup2(i, 0, TRUE);
close(i);
}
}
remove_job(j, "child"); /* in case of `jobs` command */
nzombie = 0;
#ifdef JOBS
ttypgrp_ok = 0;
Flag(FMONITOR) = 0;
#endif /* JOBS */
Flag(FTALKING) = 0;
tty_close();
cleartraps();
execute(t, flags|XEXEC); /* no return */
/* NOTREACHED */
}

/* shell (parent) stuff */
if (!(flags & XPIPEO)) { /* last process in a job */
#ifdef TTY_PGRP
/* YYY: Is this needed? (see also YYY above)
if (Flag(FMONITOR) && !(flags&(XXCOM|XBGND)))
tcsetpgrp(tty_fd, j->pgrp);
*/
#endif /* TTY_PGRP */
j_startjob(j);
if (flags & XCOPROC)
coproc.job = (void *) j;
if (flags & XBGND) {
j_set_async(j);
if (Flag(FTALKING)) {
shf_fprintf(shl_out, "[%d]", j->job);
for (p = j->proc_list; p; p = p->next)
shf_fprintf(shl_out, " %d", p->pid);
shf_putchar('\n', shl_out);
shf_flush(shl_out);
}
} else
rv = j_waitj(j, JW_NONE, "jw:last proc");
}

#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */

return rv;
}

/* start the last job: only used for `command` jobs */
void
startlast()
{
#ifdef JOB_SIGS
sigset_t omask;

sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
#endif /* JOB_SIGS */

if (last_job) { /* no need to report error - waitlast() will do it */
/* ensure it isn't removed by check_job() */
last_job->flags |= JF_WAITING;
j_startjob(last_job);
}
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
}

/* wait for last job: only used for `command` jobs */
int
waitlast()
{
int rv;
Job *j;
#ifdef JOB_SIGS
sigset_t omask;

sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
#endif /* JOB_SIGS */

j = last_job;
if (!j || !(j->flags & JF_STARTED)) {
if (!j)
warningf(TRUE, "waitlast: no last job\n");
else
internal_errorf(0, "waitlast: not started");
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
return 125; /* not so arbitrary, non-zero value */
}

rv = j_waitj(j, JW_NONE, "jw:waitlast");

#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */

return rv;
}

/* wait for child, interruptable. */
int
waitfor(cp, sigp)
char *cp;
int *sigp;
{
int rv;
Job *j;
int ecode;
int flags = JW_INTERRUPT|JW_ASYNCNOTIFY;
#ifdef JOB_SIGS
sigset_t omask;

sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
#endif /* JOB_SIGS */

*sigp = 0;

if (cp == (char *) 0) {
/* wait for an unspecified job - always returns 0, so
* don't have to worry about exited/signaled jobs
*/
for (j = job_list; j; j = j->next)
/* at&t ksh will wait for stopped jobs - we don't */
if (j->ppid == procpid && j->state == PRUNNING)
break;
if (!j) {
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
return -1;
}
} else if ((j = j_lookup(cp, &ecode))) {
/* don't report normal job completion */
flags &= ~JW_ASYNCNOTIFY;
if (j->ppid != procpid) {
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
return -1;
}
} else {
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
if (ecode == JL_NOSUCH)
return -1;
bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
}

/* at&t ksh will wait for stopped jobs - we don't */
rv = j_waitj(j, flags, "jw:waitfor");

#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */

if (rv < 0) /* we were interrupted */
*sigp = 128 + -rv;

return rv;
}

/* kill (built-in) a job */
int
j_kill(cp, sig)
char *cp;
int sig;
{
Job *j;
Proc *p;
int rv = 0;
int ecode;
#ifdef JOB_SIGS
sigset_t omask;

sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
#endif /* JOB_SIGS */

if ((j = j_lookup(cp, &ecode)) == (Job *) 0) {
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
return 1;
}

if (j->pgrp == 0) { /* started when !Flag(FMONITOR) */
for (p=j->proc_list; p != (Proc *) 0; p = p->next)
if (kill(p->pid, sig) < 0) {
bi_errorf("%s: %s", cp, strerror(errno));
rv = 1;
}
} else {
#ifdef JOBS
if (j->state == PSTOPPED && (sig == SIGTERM || sig == SIGHUP))
(void) killpg(j->pgrp, SIGCONT);
#endif /* JOBS */
if (killpg(j->pgrp, sig) < 0) {
bi_errorf("%s: %s", cp, strerror(errno));
rv = 1;
}
}

#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */

return rv;
}

#ifdef JOBS
/* fg and bg built-ins: called only if Flag(FMONITOR) set */
int
j_resume(cp, bg)
char *cp;
int bg;
{
Job *j;
Proc *p;
int ecode;
int running;
int rv = 0;
sigset_t omask;

sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);

if ((j = j_lookup(cp, &ecode)) == (Job *) 0) {
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
return 1;
}

if (j->pgrp == 0) {
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
bi_errorf("job not job-controlled");
return 1;
}

if (bg)
shprintf("[%d] ", j->job);

running = 0;
for (p = j->proc_list; p != (Proc *) 0; p = p->next) {
if (p->state == PSTOPPED) {
p->state = PRUNNING;
WSTATUS(p->status) = 0;
running = 1;
}
shprintf("%s%s", p->command, p->next ? "| " : null);
}
shprintf(newline);
shf_flush(shl_stdout);
if (running)
j->state = PRUNNING;

put_job(j, PJ_PAST_STOPPED);
if (bg)
j_set_async(j);
else {
# ifdef TTY_PGRP
/* attach tty to job */
if (j->state == PRUNNING) {
if (ttypgrp_ok && (j->flags & JF_SAVEDTTY)) {
set_tty(tty_fd, &j->ttystate, TF_NONE);
}
if (ttypgrp_ok && tcsetpgrp(tty_fd, j->pgrp) < 0) {
if (j->flags & JF_SAVEDTTY)
set_tty(tty_fd, &tty_state, TF_NONE);
sigprocmask(SIG_SETMASK, &omask,
(sigset_t *) 0);
bi_errorf("1st tcsetpgrp(%d, %d) failed: %s",
tty_fd, j->pgrp, strerror(errno));
return 1;
}
}
# endif /* TTY_PGRP */
j->flags |= JF_FG;
j->flags &= ~JF_KNOWN;
if (j == async_job)
async_job = (Job *) 0;
}

if (j->state == PRUNNING && killpg(j->pgrp, SIGCONT) < 0) {
int err = errno;

if (!bg) {
j->flags &= ~JF_FG;
# ifdef TTY_PGRP
if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
set_tty(tty_fd, &tty_state, TF_NONE);
if (ttypgrp_ok && tcsetpgrp(tty_fd, our_pgrp) < 0) {
warningf(TRUE,
"fg: 2nd tcsetpgrp(%d, %d) failed: %s\n",
tty_fd, our_pgrp,
strerror(errno));
}
# endif /* TTY_PGRP */
}
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
bi_errorf("cannot continue job %s: %s",
cp, strerror(err));
return 1;
}
if (!bg) {
# ifdef TTY_PGRP
if (ttypgrp_ok) {
j->flags &= ~JF_SAVEDTTY;
}
# endif /* TTY_PGRP */
rv = j_waitj(j, JW_NONE, "jw:resume");
}
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
return rv;
}
#endif /* JOBS */

#ifdef JOBS
/* are there any stopped jobs ? */
int
j_stopped()
{
Job *j;

for (j = job_list; j != (Job *) 0; j = j->next)
if (j->ppid == procpid && j->state == PSTOPPED) {
shellf("You have stopped jobs\n");
return 1;
}
return 0;
}
#endif /* JOBS */

/* list jobs for jobs built-in */
int
j_jobs(cp, slp, nflag)
char *cp;
int slp; /* 0: short, 1: long, 2: pgrp */
int nflag;
{
Job *j, *tmp;
int how;
int zflag = 0;
#ifdef JOB_SIGS
sigset_t omask;

sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
#endif /* JOB_SIGS */

if (nflag < 0) { /* kludge: print zombies */
nflag = 0;
zflag = 1;
}
if (cp) {
int ecode;

if ((j = j_lookup(cp, &ecode)) == (Job *) 0) {
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
return 1;
}
} else
j = job_list;
how = slp == 0 ? JP_MEDIUM : (slp == 1 ? JP_LONG : JP_PGRP);
for (; j; j = j->next) {
if ((!(j->flags & JF_ZOMBIE) || zflag)
&& (!nflag || (j->flags & JF_CHANGED)))
{
j_print(j, how, shl_stdout);
if (j->state == PEXITED || j->state == PSIGNALLED)
j->flags |= JF_REMOVE;
}
if (cp)
break;
}
/* Remove jobs after printing so there won't be multiple + or - jobs */
for (j = job_list; j; j = tmp) {
tmp = j->next;
if (j->flags & JF_REMOVE)
remove_job(j, "jobs");
}
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
return 0;
}

/* list jobs for top-level notification */
void
j_notify()
{
Job *j, *tmp;
#ifdef JOB_SIGS
sigset_t omask;

sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
#endif /* JOB_SIGS */
for (j = job_list; j; j = j->next) {
#ifdef JOBS
if (Flag(FMONITOR) && (j->flags & JF_CHANGED))
j_print(j, JP_MEDIUM, shl_out);
#endif /* JOBS */
/* Remove job after doing reports so there aren't
* multiple +/- jobs.
*/
if (j->state == PEXITED || j->state == PSIGNALLED)
j->flags |= JF_REMOVE;
}
for (j = job_list; j; j = tmp) {
tmp = j->next;
if (j->flags & JF_REMOVE)
remove_job(j, "notify");
}
shf_flush(shl_out);
#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */
}

/* Return pid of last process in last asynchornous job */
pid_t
j_async()
{
#ifdef JOB_SIGS
sigset_t omask;

sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
#endif /* JOB_SIGS */

if (async_job)
async_job->flags |= JF_KNOWN;

#ifdef JOB_SIGS
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
#endif /* JOB_SIGS */

return async_pid;
}

/* Make j the last async process
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static void
j_set_async(j)
Job *j;
{
Job *jl, *oldest;

if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)
remove_job(async_job, "async");
if (!(j->flags & JF_STARTED)) {
internal_errorf(0, "j_async: job not started");
return;
}
async_job = j;
async_pid = j->last_proc->pid;
while (nzombie > child_max) {
oldest = (Job *) 0;
for (jl = job_list; jl; jl = jl->next)
if (jl != async_job && (jl->flags & JF_ZOMBIE)
&& (!oldest || jl->age < oldest->age))
oldest = jl;
if (!oldest) {
/* XXX debugging */
if (!(async_job->flags & JF_ZOMBIE) || nzombie != 1) {
internal_errorf(0, "j_async: bad nzombie (%d)", nzombie);
nzombie = 0;
}
break;
}
remove_job(oldest, "zombie");
}
}

/* Start a job: set STARTED, check for held signals and set j->last_proc
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static void
j_startjob(j)
Job *j;
{
Proc *p;

j->flags |= JF_STARTED;
for (p = j->proc_list; p->next; p = p->next)
;
j->last_proc = p;

#ifdef NEED_PGRP_SYNC
if (j_sync_open) {
closepipe(j_sync_pipe);
j_sync_open = 0;
}
#endif /* NEED_PGRP_SYNC */
#ifdef JOB_SIGS
if (held_sigchld) {
held_sigchld = 0;
/* Don't call j_sigchild() as it may remove job... */
kill(procpid, SIGCHLD);
}
#endif /* JOB_SIGS */
}

/*
* wait for job to complete or change state
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static int
j_waitj(j, flags, where)
Job *j;
int flags; /* see JW_* */
char *where;
{
int rv;

/*
* No auto-notify on the job we are waiting on.
*/
j->flags |= JF_WAITING;
if (flags & JW_ASYNCNOTIFY)
j->flags |= JF_W_ASYNCNOTIFY;

if (!Flag(FMONITOR))
flags |= JW_STOPPEDWAIT;

while ((volatile int) j->state == PRUNNING
|| ((flags & JW_STOPPEDWAIT)
&& (volatile int) j->state == PSTOPPED))
{
#ifdef JOB_SIGS
sigsuspend(&sm_default);
#else /* JOB_SIGS */
j_sigchld(SIGCHLD);
#endif /* JOB_SIGS */
if (fatal_trap) {
int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY);
j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
runtraps(TF_FATAL);
j->flags |= oldf; /* not reached... */
}
if ((flags & JW_INTERRUPT) && (rv = trap_pending())) {
j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
return -rv;
}
}
j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);

if (j->flags & JF_FG) {
WAIT_T status;

j->flags &= ~JF_FG;
#ifdef TTY_PGRP
if (Flag(FMONITOR) && ttypgrp_ok && j->pgrp) {
if (tcsetpgrp(tty_fd, our_pgrp) < 0) {
warningf(TRUE,
"j_waitj: tcsetpgrp(%d, %d) failed: %s\n",
tty_fd, our_pgrp,
strerror(errno));
}
if (j->state == PSTOPPED) {
j->flags |= JF_SAVEDTTY;
get_tty(tty_fd, &j->ttystate);
}
}
#endif /* TTY_PGRP */
if (tty_fd >= 0) {
/* Only restore tty settings if job was originally
* started in the foreground. Problems can be
* caused by things like `more foobar &' which will
* typically get and save the shell's vi/emacs tty
* settings before setting up the tty for itself;
* when more exits, it restores the `original'
* settings, and things go down hill from there...
*/
if (j->state == PEXITED && j->status == 0
&& (j->flags & JF_ORIGFG))
{
get_tty(tty_fd, &tty_state);
} else {
set_tty(tty_fd, &tty_state,
(j->state == PEXITED) ? 0 : TF_MIPSKLUDGE);
}
}
/* If it looks like user hit ^C to kill a job, pretend we got
* one too to break out of for loops, etc. (at&t ksh does this
* even when not monitoring, but this doesn't make sense since
* a tty generated ^C goes to the whole process group)
*/
status = j->last_proc->status;
#ifdef JOBS
if (Flag(FMONITOR) && j->state == PSIGNALLED
&& WIFSIGNALED(status)
&& (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR))
trapsig(WTERMSIG(status));
#endif /* JOBS */
}

j_utime = j->utime;
j_stime = j->stime;
rv = j->status;

if (!(flags & JW_ASYNCNOTIFY)
&& (!Flag(FMONITOR) || j->state != PSTOPPED))
{
j_print(j, JP_SHORT, shl_out);
shf_flush(shl_out);
}
if (j->state != PSTOPPED
&& (!Flag(FMONITOR) || !(flags & JW_ASYNCNOTIFY)))
remove_job(j, where);

return rv;
}

/* SIGCHLD handler to reap children and update job states
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static RETSIGTYPE
j_sigchld(sig)
int sig;
{
int errno_ = errno;
Job *j;
Proc UNINITIALIZED(*p);
int pid;
WAIT_T status;
struct tms t0, t1;

trapsig(sig);

#ifdef JOB_SIGS
/* Don't wait for any processes if a job is partially started.
* This is so we don't do away with the process group leader
* before all the processes in a pipe line are started (so the
* setpgrp() won't fail)
*/
for (j = job_list; j; j = j->next)
if (j->ppid == procpid && !(j->flags & JF_STARTED)) {
held_sigchld = 1;
return;
}
#endif /* JOB_SIGS */

ksh_times(&t0);
do {
#ifdef JOB_SIGS
pid = ksh_waitpid(-1, &status, (WNOHANG|WUNTRACED));
#else /* JOB_SIGS */
pid = wait(&status);
#endif /* JOB_SIGS */

if (pid <= 0) /* return if would block (0) ... */
break; /* ... or no children or interrupted (-1) */

ksh_times(&t1);

/* find job and process structures for this pid */
for (j = job_list; j != (Job *) 0; j = j->next)
for (p = j->proc_list; p != (Proc *) 0; p = p->next)
if (p->pid == pid)
goto found;
found:
if (j == (Job *) 0) {
/* Can occur if process has kids, then execs shell
warningf(TRUE, "bad process waited for (pid = %d)\n",
pid);
*/
t0 = t1;
continue;
}

j->utime += t1.tms_cutime - t0.tms_cutime;
j->stime += t1.tms_cstime - t0.tms_cstime;
t0 = t1;
p->status = status;
#ifdef JOBS
if (WIFSTOPPED(status))
p->state = PSTOPPED;
else
#endif /* JOBS */
if (WIFSIGNALED(status))
p->state = PSIGNALLED;
else
p->state = PEXITED;

check_job(j); /* check to see if entire job is done */
}
#ifdef JOB_SIGS
while (1);
#else /* JOB_SIGS */
while (0);
#endif /* JOB_SIGS */

errno = errno_;
}

/*
* Called only when a process in j has exited/stopped (ie, called only
* from j_sigchild()). If no processes are running, the job status
* and state are updated, asynchronous job notification is done and,
* if unneeded, the job is removed.
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static void
check_job(j)
Job *j;
{
int jstate;
Proc *p;

/* XXX debugging (nasty - interrupt routine using shl_out) */
if (!(j->flags & JF_STARTED)) {
internal_errorf(0, "check_job: job started (flags 0x%x)",
j->flags);
return;
}

jstate = PRUNNING;
for (p=j->proc_list; p != (Proc *) 0; p = p->next) {
if (p->state == PRUNNING)
return; /* some processes still running */
if (p->state > jstate)
jstate = p->state;
}
j->state = jstate;

switch (j->last_proc->state) {
case PEXITED:
j->status = WEXITSTATUS(j->last_proc->status);
break;
case PSIGNALLED:
j->status = 128 + WTERMSIG(j->last_proc->status);
break;
default:
j->status = 0;
break;
}

/* Note when co-process dies: can't be done in j_wait() nor
* remove_job() since neither may be called for non-interactive
* shells.
*/
if ((j->state == PEXITED || j->state == PSIGNALLED)
&& coproc.job == (void *) j)
coproc.job = (void *) 0;

j->flags |= JF_CHANGED;
#ifdef JOBS
if (Flag(FMONITOR) && !(j->flags & JF_XXCOM)) {
/* Only put stopped jobs at the front to avoid confusing
* the user (don't want finished jobs effecting %+ or %-)
*/
if (j->state == PSTOPPED)
put_job(j, PJ_ON_FRONT);
if (Flag(FNOTIFY)
&& (j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY)) != JF_WAITING)
{
/* Look for the real file descriptor 2 */
{
struct env *ep;
int fd = 2;

for (ep = e; ep; ep = ep->oenv)
if (ep->savefd && ep->savefd[2])
fd = ep->savefd[2];
shf_reopen(fd, SHF_WR, shl_j);
}
/* Can't call j_notify() as it removes jobs. The job
* must stay in the job list as j_waitj() may be
* running with this job.
*/
j_print(j, JP_MEDIUM, shl_j);
shf_flush(shl_j);
if (!(j->flags & JF_WAITING) && j->state != PSTOPPED)
remove_job(j, "notify");
}
}
#endif /* JOBS */
if (!Flag(FMONITOR) && !(j->flags & (JF_WAITING|JF_FG))
&& j->state != PSTOPPED)
{
if (j == async_job || (j->flags & JF_KNOWN)) {
j->flags |= JF_ZOMBIE;
j->job = -1;
nzombie++;
} else
remove_job(j, "checkjob");
}
}

/*
* Print job status in either short, medium or long format.
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static void
j_print(j, how, shf)
Job *j;
int how;
struct shf *shf;
{
Proc *p;
int state;
WAIT_T status;
int coredumped;
char jobchar = ' ';
char buf[64], *filler;
int output = 0;

if (how == JP_PGRP) {
/* POSIX doesn't say what to do it there is no process
* group leader (ie, !FMONITOR). We arbitrarily return
* last pid (which is what $! returns).
*/
shf_fprintf(shf, "%d\n", j->pgrp ? j->pgrp
: (j->last_proc ? j->last_proc->pid : 0));
return;
}
j->flags &= ~JF_CHANGED;
filler = j->job > 10 ? "\n " : "\n ";
if (j == job_list)
jobchar = '+';
else if (j == job_list->next)
jobchar = '-';

for (p = j->proc_list; p != (Proc *) 0;) {
coredumped = 0;
switch (p->state) {
case PRUNNING:
strcpy(buf, "Running");
break;
case PSTOPPED:
strcpy(buf, sigtraps[WSTOPSIG(p->status)].mess);
break;
case PEXITED:
if (how == JP_SHORT)
buf[0] = '\0';
else if (WEXITSTATUS(p->status) == 0)
strcpy(buf, "Done");
else
shf_snprintf(buf, sizeof(buf), "Done (%d)",
WEXITSTATUS(p->status));
break;
case PSIGNALLED:
if (WIFCORED(p->status))
coredumped = 1;
/* kludge for not reporting `normal termination signals'
* (ie, SIGINT, SIGPIPE)
*/
if (how == JP_SHORT && !coredumped
&& (WTERMSIG(p->status) == SIGINT
|| WTERMSIG(p->status) == SIGPIPE)) {
buf[0] = '\0';
} else
strcpy(buf, sigtraps[WTERMSIG(p->status)].mess);
break;
}

if (how != JP_SHORT)
if (p == j->proc_list)
shf_fprintf(shf, "[%d] %c ", j->job, jobchar);
else
shf_fprintf(shf, "%s", filler);

if (how == JP_LONG)
shf_fprintf(shf, "%5d ", p->pid);

if (how == JP_SHORT) {
if (buf[0]) {
output = 1;
shf_fprintf(shf, "%s%s ",
buf, coredumped ? " (core dumped)" : null);
}
} else {
output = 1;
shf_fprintf(shf, "%-20s %s%s%s", buf, p->command,
p->next ? "|" : null,
coredumped ? " (core dumped)" : null);
}

state = p->state;
status = p->status;
p = p->next;
while (p && p->state == state
&& WSTATUS(p->status) == WSTATUS(status))
{
if (how == JP_LONG)
shf_fprintf(shf, "%s%5d %-20s %s%s", filler, p->pid,
space, p->command, p->next ? "|" : null);
else if (how == JP_MEDIUM)
shf_fprintf(shf, " %s%s", p->command,
p->next ? "|" : null);
p = p->next;
}
}
if (output)
shf_fprintf(shf, newline);
}

/* Convert % sequence to job
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static Job *
j_lookup(cp, ecodep)
char *cp;
int *ecodep;
{
Job *j, *last_match;
Proc *p;
int len, job = 0;

if (digit(*cp)) {
job = atoi(cp);
/* Look for last_proc->pid (what $! returns) first... */
for (j = job_list; j != (Job *) 0; j = j->next)
if (j->last_proc && j->last_proc->pid == job)
return j;
/* ...then look for process group (this is non-POSIX),
* but should not break anything (so FPOSIX isn't used).
*/
for (j = job_list; j != (Job *) 0; j = j->next)
if (j->pgrp && j->pgrp == job)
return j;
if (ecodep)
*ecodep = JL_NOSUCH;
return (Job *) 0;
}
if (*cp != '%') {
if (ecodep)
*ecodep = JL_INVALID;
return (Job *) 0;
}
switch (*++cp) {
case '\0': /* non-standard */
case '+':
case '%':
if (job_list != (Job *) 0)
return job_list;
break;

case '-':
if (job_list != (Job *) 0 && job_list->next)
return job_list->next;
break;

case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
job = atoi(cp);
for (j = job_list; j != (Job *) 0; j = j->next)
if (j->job == job)
return j;
break;

case '?': /* %?string */
last_match = (Job *) 0;
for (j = job_list; j != (Job *) 0; j = j->next)
for (p = j->proc_list; p != (Proc *) 0; p = p->next)
if (strstr(p->command, cp+1) != (char *) 0) {
if (last_match) {
if (ecodep)
*ecodep = JL_AMBIG;
return (Job *) 0;
}
last_match = j;
}
if (last_match)
return last_match;
break;

default: /* %string */
len = strlen(cp);
last_match = (Job *) 0;
for (j = job_list; j != (Job *) 0; j = j->next)
if (strncmp(cp, j->proc_list->command, len) == 0) {
if (last_match) {
if (ecodep)
*ecodep = JL_AMBIG;
return (Job *) 0;
}
last_match = j;
}
if (last_match)
return last_match;
break;
}
if (ecodep)
*ecodep = JL_NOSUCH;
return (Job *) 0;
}

static Job *free_jobs = (Job *) 0;
static Proc *free_procs = (Proc *) 0;

/* allocate a new job and fill in the job number.
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static Job *
new_job()
{
int i;
Job *newj, *j;

if (free_jobs != (Job *) 0) {
newj = free_jobs;
free_jobs = free_jobs->next;
} else
newj = (Job *) alloc(sizeof(Job), APERM);

/* brute force method */
for (i = 1; ; i++) {
for (j = job_list; j && j->job != i; j = j->next)
;
if (j == (Job *) 0)
break;
}
newj->job = i;

return newj;
}

/* Allocate new process strut
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static Proc *
new_proc()
{
Proc *p;

if (free_procs != (Proc *) 0) {
p = free_procs;
free_procs = free_procs->next;
} else
p = (Proc *) alloc(sizeof(Proc), APERM);

return p;
}

/* Take job out of job_list and put old structures into free list.
* Keeps nzombies, last_job and async_job up to date.
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static void
remove_job(j, where)
Job *j;
char *where;
{
Proc *p, *tmp;
Job **prev, *curr;

prev = &job_list;
curr = *prev;
for (; curr != (Job *) 0 && curr != j; prev = &curr->next, curr = *prev)
;
if (curr != j) {
internal_errorf(0, "remove_job: job not found (%s)", where);
return;
}
*prev = curr->next;

/* free up proc structures */
for (p = j->proc_list; p != (Proc *) 0; ) {
tmp = p;
p = p->next;
tmp->next = free_procs;
free_procs = tmp;
}

if ((j->flags & JF_ZOMBIE) && j->ppid == procpid)
--nzombie;
j->next = free_jobs;
free_jobs = j;

if (j == last_job)
last_job = (Job *) 0;
if (j == async_job)
async_job = (Job *) 0;
}

/* put j in a particular location (taking it out job_list if it is there
* already)
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static void
put_job(j, where)
Job *j;
int where;
{
Job **prev, *curr;

/* Remove job from list (if there) */
prev = &job_list;
curr = job_list;
for (; curr && curr != j; prev = &curr->next, curr = *prev)
;
if (curr == j)
*prev = curr->next;

switch (where) {
case PJ_ON_FRONT:
j->next = job_list;
job_list = j;
break;

case PJ_PAST_STOPPED:
prev = &job_list;
curr = job_list;
for (; curr && curr->state == PSTOPPED; prev = &curr->next,
curr = *prev)
;
j->next = curr;
*prev = j;
break;
}
}

/* nuke a job (called when unable to start full job).
*
* If jobs are compiled in then this routine expects sigchld to be blocked.
*/
static void
kill_job(j)
Job *j;
{
Proc *p;

for (p = j->proc_list; p != (Proc *) 0; p = p->next)
if (p->pid != 0)
(void) kill(p->pid, 9);
}

/* put a more useful name on a process than snptreef does (in certain cases) */
static void
fill_command(c, len, t)
char *c;
int len;
struct op *t;
{
int alen;
char **ap;
extern char **eval();

if (t->type == TEXEC || t->type == TCOM) {
if (t->type == TCOM)
ap = eval(t->args, DOBLANK|DONTRUNCOMMAND);
else
ap = t->args;
--len; /* save room for the null */
while (len > 0 && *ap != (char *) 0) {
alen = strlen(*ap);
if (alen > len)
alen = len;
memcpy(c, *ap, alen);
c += alen;
len -= alen;
if (len > 0) {
*c++ = ' '; len--;
}
ap++;
}
*c = '\0';
} else
snptreef(c, len, "%T", t);
}
pdksh-5.1.2/lex.c100644 0 133 46273 5667370272 12455 0ustar rootlsource/*
* lexical analysis and source input
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: lex.c,v 1.4 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include

static void readhere ARGS((struct ioword *iop));
static int getsc_ ARGS((void));
static char *get_brace_var ARGS((XString *wsp, char *wp));
static int arraysub ARGS((char **strp));

static void gethere ARGS((void));

/* optimized getsc_() */
#define getsc() ((*source->str != 0) ? *source->str++ : getsc_())
#define ungetsc() (source->str != null ? source->str-- : source->str)

/*
* Lexical analyzer
*
* tokens are not regular expressions, they are LL(1).
* for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
* hence the state stack.
*/

int
yylex(cf)
int cf;
{
register int c, state;
char states [64], *statep = states;
XString ws; /* expandable output word */
register char *wp; /* output word pointer */
register char *sp, *dp;
char UNINITIALIZED(*ddparen_start);
int istate;
int UNINITIALIZED(c2);
int UNINITIALIZED(nparen), UNINITIALIZED(csstate);
int UNINITIALIZED(ndparen);
int UNINITIALIZED(indquotes);


Again:
Xinit(ws, wp, 64, ATEMP);

if (cf&ONEWORD)
istate = SWORD;
else if (cf&LETEXPR) {
*wp++ = OQUOTE; /* enclose arguments in (double) quotes */
istate = SDPAREN;
ndparen = 0;
} else { /* normal lexing */
istate = SBASE;
while ((c = getsc()) == ' ' || c == '\t')
;
if (c == '#')
while ((c = getsc()) != '\0' && c != '\n')
;
ungetsc();
}
if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */
source->flags &= ~SF_ALIAS;
/* in POSIX mode, a trailing space only counts if we are
* parsing a simple command */
if (!Flag(FPOSIX) || (cf & CMDWORD))
cf |= ALIAS;
}

/* collect non-special or quoted characters to form word */
for (*statep = state = istate;
!((c = getsc()) == 0 || (state == SBASE && ctype(c, C_LEX1))); ) {
Xcheck(ws, wp);
switch (state) {
case SBASE:
Sbase:
switch (c) {
case '\\':
c = getsc();
if (c != '\n') {
#ifdef OS2
if (isalnum(c)) {
*wp++ = CHAR, *wp++ = '\\';
*wp++ = CHAR, *wp++ = c;
} else
#endif
*wp++ = QCHAR, *wp++ = c;
} else
if (wp == Xstring(ws, wp)) {
Xfree(ws, wp); /* free word */
goto Again;
}
break;
case '\'':
*++statep = state = SSQUOTE;
*wp++ = OQUOTE;
break;
case '"':
*++statep = state = SDQUOTE;
*wp++ = OQUOTE;
break;
default:
goto Subst;
}
break;

Subst:
switch (c) {
case '\\':
c = getsc();
switch (c) {
case '\n':
break;
case '"': case '\\':
case '$': case '`':
*wp++ = QCHAR, *wp++ = c;
break;
default:
Xcheck(ws, wp);
*wp++ = CHAR, *wp++ = '\\';
*wp++ = CHAR, *wp++ = c;
break;
}
break;
case '$':
c = getsc();
if (c == '(') /*)*/ {
c = getsc();
if (c == '(') /*)*/ {
*++statep = state = SDDPAREN;
nparen = 2;
ddparen_start = wp;
*wp++ = EXPRSUB;
} else {
ungetsc();
*++statep = state = SPAREN;
nparen = 1;
csstate = 0;
*wp++ = COMSUB;
}
} else if (c == '{') /*}*/ {
*++statep = state = SBRACE;
*wp++ = OSUBST;
wp = get_brace_var(&ws, wp);
} else if (ctype(c, C_ALPHA)) {
*wp++ = OSUBST;
do {
Xcheck(ws, wp);
*wp++ = c;
c = getsc();
} while (ctype(c, C_ALPHA|C_DIGIT));
*wp++ = '\0';
*wp++ = CSUBST;
ungetsc();
} else if (ctype(c, C_DIGIT|C_VAR1)) {
Xcheck(ws, wp);
*wp++ = OSUBST;
*wp++ = c;
*wp++ = '\0';
*wp++ = CSUBST;
} else {
*wp++ = CHAR, *wp++ = '$';
ungetsc();
}
break;
case '`':
*++statep = state = SBQUOTE;
*wp++ = COMSUB;
/* Need to know if we are inside double quotes
* since sh/at&t-ksh translate the \" to " in
* "`..\"..`".
*/
indquotes = 0;
if (!Flag(FPOSIX))
for (sp = statep; sp > states; --sp)
if (*sp == SDQUOTE)
indquotes = 1;
break;
case '[':
*wp = EOS; /* temporary */
if (state == SBASE && (cf & (VARASN|ARRAYVAR))
&& is_wdvarname(Xstring(ws, wp), FALSE))
{
char *p, *tmp;

ungetsc();
if (arraysub(&tmp)) {
for (p = tmp; *p; ) {
Xcheck(ws, wp);
*wp++ = CHAR;
*wp++ = *p++;
}
afree(tmp, ATEMP);
break;
} else {
Source *s;

s = pushs(SREREAD,
source->areap);
s->str = tmp;
s->u.start = tmp;
s->next = source;
source = s;

getsc(); /* '[' */
}
}
*wp++ = CHAR;
*wp++ = c;
break;
default:
*wp++ = CHAR, *wp++ = c;
}
break;

case SSQUOTE:
if (c == '\'') {
state = *--statep;
*wp++ = CQUOTE;
} else
*wp++ = QCHAR, *wp++ = c;
break;

case SDQUOTE:
if (c == '"') {
state = *--statep;
*wp++ = CQUOTE;
} else
goto Subst;
break;

case SPAREN: /* $( .. ) */
/* todo: deal with $(...) quoting properly
* kludge to partly fake quoting inside $(..): doesn't
* really work because nested $(..) or ${..} inside
* double quotes aren't dealt with.
*/
switch (csstate) {
case 0: /* normal */
switch (c) {
case '(':
nparen++;
break;
case ')':
nparen--;
break;
case '\\':
csstate = 1;
break;
case '"':
csstate = 2;
break;
case '\'':
csstate = 4;
break;
}
break;

case 1: /* backslash in normal mode */
case 3: /* backslash in double quotes */
--csstate;
break;

case 2: /* double quotes */
if (c == '"')
csstate = 0;
else if (c == '\\')
csstate = 3;
break;

case 4: /* single quotes */
if (c == '\'')
csstate = 0;
break;
}
if (nparen == 0) {
state = *--statep;
*wp++ = 0; /* end of COMSUB */
} else
*wp++ = c;
break;

case SDDPAREN: /* $(( .. )) */
/* todo: deal with $((...); (...)) properly */
if (c == '(')
nparen++;
else if (c == ')') {
nparen--;
if (nparen == 1) {
/*(*/
if (getsc() == ')') {
state = *--statep;
*wp++ = 0; /* end of EXPRSUB */
break;
} else {
ungetsc();
/* mismatched parenthesis -
* assume we were really
* parsing a $(..) expression
*/
memmove(ddparen_start + 1,
ddparen_start,
wp - ddparen_start);
*ddparen_start++ = COMSUB;
*ddparen_start = '('; /*)*/
wp++;
csstate = 0;
*statep = state = SPAREN;
}
}
}
*wp++ = c;
break;

case SBRACE:
/*{*/
if (c == '}') {
state = *--statep;
*wp++ = CSUBST;
} else
goto Sbase;
break;

case SBQUOTE:
if (c == '`') {
*wp++ = 0;
state = *--statep;
} else if (c == '\\') {
switch (c = getsc()) {
case '\n':
break;
case '\\':
case '$': case '`':
*wp++ = c;
break;
case '"':
if (indquotes) {
*wp++ = c;
break;
}
/* fall through.. */
default:
*wp++ = '\\';
*wp++ = c;
break;
}
} else
*wp++ = c;
break;

case SWORD: /* ONEWORD */
goto Subst;

case SDPAREN: /* LETEXPR: (( ... )) */
/*(*/
if (c == ')') {
if (ndparen > 0)
--ndparen;
/*(*/
else if (getsc() == ')') {
c = 0;
*wp++ = CQUOTE;
goto Done;
} else
ungetsc();
} else if (c == '(')
/* parenthesis inside quotes and backslashes
* are lost, but at&t ksh doesn't count them
* either
*/
++ndparen;
goto Sbase;
}
}
Done:
Xcheck(ws, wp);
if (state != istate)
yyerror("no closing quote\n");

if ((c == '<' || c == '>') && state == SBASE) {
char *cp = Xstring(ws, wp);
if (Xlength(ws, wp) == 2 && cp[0] == CHAR && digit(cp[1])) {
wp = cp; /* throw away word */
c2/*unit*/ = cp[1] - '0';
} else
c2/*unit*/ = c == '>'; /* 0 for <, 1 for > */
}

if (wp == Xstring(ws, wp) && state == SBASE) {
Xfree(ws, wp); /* free word */
/* no word, process LEX1 character */
switch (c) {
default:
return c;

case '|':
case '&':
case ';':
if ((c2 = getsc()) == c)
c = (c == ';') ? BREAK :
(c == '|') ? LOGOR :
(c == '&') ? LOGAND :
YYERRCODE;
else if (c == '|' && c2 == '&')
c = COPROC;
else
ungetsc();
return c;

case '>':
case '<': {
register struct ioword *iop;

iop = (struct ioword *) alloc(sizeof(*iop), ATEMP);
iop->unit = c2/*unit*/;

c2 = getsc();
/* <<, >>, <> are ok, >< is not */
if (c == c2 || (c == '<' && c2 == '>')) {
iop->flag = c == c2 ?
(c == '>' ? IOCAT : IOHERE) : IORDWR;
if (iop->flag == IOHERE)
if (getsc() == '-')
iop->flag |= IOSKIP;
else
ungetsc();
} else if (c2 == '&')
iop->flag = IODUP | (c == '<' ? IORDUP : 0);
else {
iop->flag = c == '>' ? IOWRITE : IOREAD;
if (c == '>' && c2 == '|')
iop->flag |= IOCLOB;
else
ungetsc();
}

yylval.iop = iop;
return REDIR;
}
case '\n':
gethere();
if (cf & CONTIN)
goto Again;
return c;

case '(': /*)*/
if (getsc() == '(') /*)*/
c = MDPAREN;
else
ungetsc();
return c;
/*(*/
case ')':
return c;
}
}

*wp++ = EOS; /* terminate word */
yylval.cp = Xclose(ws, wp);
if (state == SWORD || state == SDPAREN) /* ONEWORD? */
return LWORD;
ungetsc(); /* unget terminator */

/* copy word to unprefixed string ident */
for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
*dp++ = *sp++;
/* Make sure the ident array stays '\0' paded */
memset(dp, 0, (ident+IDENT) - dp + 1);
if (c != EOS)
*ident = 0; /* word is not unquoted */

if (*ident != 0 && (cf&(KEYWORD|ALIAS))) {
struct tbl *p;
int h = hash(ident);

if ((cf & KEYWORD) && (p = tsearch(&keywords, ident, h))
&& (!(cf & ESACONLY) || p->val.i == ESAC))
{
afree(yylval.cp, ATEMP);
return p->val.i;
}
if ((cf & ALIAS) && (p = tsearch(&aliases, ident, h))
&& (p->flag & ISSET))
{
register Source *s;

for (s = source; s->type == SALIAS; s = s->next)
if (s->u.tblp == p)
return LWORD;
/* push alias expansion */
s = pushs(SALIAS, source->areap);
s->str = p->val.s;
s->u.tblp = p;
s->next = source;
source = s;
afree(yylval.cp, ATEMP);
goto Again;
}
}

return LWORD;
}

static void
gethere()
{
register struct ioword **p;

for (p = heres; p < herep; p++)
readhere(*p);
herep = heres;
}

/*
* read "< */

static void
readhere(iop)
register struct ioword *iop;
{
struct shf *volatile shf;
struct temp *h;
register int c;
char *volatile eof;
char *eofp;
int skiptabs;
int i;

eof = evalstr(iop->name, 0);

if (e->flags & EF_FUNC_PARSE) {
h = maketemp(APERM);
h->next = func_heredocs;
func_heredocs = h;
} else {
h = maketemp(ATEMP);
h->next = e->temps;
e->temps = h;
}
iop->name = h->name;
shf = shf_open(h->name, O_WRONLY|O_CREAT|O_TRUNC, 0666, 0);
if (shf == NULL)
yyerror("cannot create temporary file\n");

newenv(E_ERRH);
if ((i = setjmp(e->jbuf))) {
quitenv();
shf_close(shf);
unwind(i);
}

for (;;) {
eofp = eof;
skiptabs = iop->flag & IOSKIP;
while ((c = getsc()) != '\n' && c != 0) {
if (skiptabs) {
if (c == '\t')
continue;
skiptabs = 0;
}
if (c != *eofp)
break;
eofp++;
}
/* Allow EOF here so commands with out trailing newlines
* (eg, ksh -c '...', $(...), etc) will work
*/
if (*eofp == '\0' && (c == 0 || c == '\n'))
break;
ungetsc();
shf_write(eof, eofp - eof, shf);
while ((c = getsc()) != '\n') {
if (c == 0)
yyerror("here document `%s' unclosed\n", eof);
shf_putc(c, shf);
}
shf_putc(c, shf);
}
shf_flush(shf);
if (shf_error(shf))
yyerror("error saving here document `%s': %s\n",
eof, strerror(shf_errno(shf)));
/*XXX add similar checks for write errors everywhere */
quitenv();
shf_close(shf);
}

void
#ifdef HAVE_PROTOTYPES
yyerror(const char *fmt, ...)
#else
yyerror(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list va;

yynerrs++;
/* pop aliases and re-reads */
while (source->type == SALIAS || source->type == SREREAD)
source = source->next;
source->str = null; /* zap pending input */

error_prefix(TRUE);
SH_VA_START(va, fmt);
shf_vfprintf(shl_out, fmt, va);
va_end(va);
errorf((char *) 0);
}

/*
* input for yylex with alias expansion
*/

Source *
pushs(type, areap)
int type;
Area *areap;
{
register Source *s;

s = (Source *) alloc(sizeof(Source), areap);
s->type = type;
s->str = null;
s->line = 0;
s->errline = 0;
s->file = NULL;
s->flags = 0;
s->next = NULL;
s->areap = areap;
if (type == SFILE || type == SSTDIN) {
char *dummy;
Xinit(s->xs, dummy, 256, s->areap);
} else
memset(&s->xs, 0, sizeof(s->xs));
return s;
}

static int
getsc_()
{
register Source *s = source;
register int c;
static char line[LINE + 1];

while ((c = *s->str++) == 0) {
s->str = NULL; /* return 0 for EOF by default */
switch (s->type) {
case SEOF:
s->str = null;
return 0;

case STTY:
#ifdef HISTORY
if (histpush < 0) { /* commands pushed by dofc */
s->type = SHIST;
s->str = null;
continue;
}
#endif /* HISTORY */
s->str = line;
line[0] = '\0';
mprint(); /* print mail messages */
pprompt(prompt);
#ifdef KSH
if (ksh_tmout) {
ksh_tmout_state = TMOUT_READING;
alarm(ksh_tmout);
}
#endif /* KSH */
#ifdef EDIT
if (0
# ifdef VI
|| Flag(FVI)
# endif /* VI */
# ifdef EMACS
|| Flag(FEMACS) || Flag(FGMACS)
# endif /* EMACS */
)
c = x_read(line, LINE);
else
#endif
{
/*
* This allows the arival of a SIGCHLD
* to not disturb us until we are ready.
*/
while ((c = read(0, line, LINE)) < 0
&& errno == EINTR)
if (trap)
runtraps(0);
}

/* XXX: temporary kludge to restore source after a
* trap may have been executed.
*/
source = s;

#ifdef KSH
if (ksh_tmout) {
ksh_tmout_state = TMOUT_EXECUTING;
alarm(0);
}
#endif /* KSH */
if (c < 0) /* read error */
c = 0;
strip_nuls(line, c);
line[c] = '\0';
if (c == 0) /* EOF */
s->str = NULL;
else {
c = 0;
while (line[c] && ctype(line[c], C_IFS)
&& ctype(line[c], C_IFSWS))
c++;
#ifdef HISTORY
if (line[c]) {
# ifdef EASY_HISTORY
if (cur_prompt == PS2)
histappend(line, 1);
else
# endif
{
s->line++;
histsave(s->line, s->str, 1);
}
}
#endif /* HISTORY */
}
set_prompt(PS2);
break;

#ifdef HISTORY
case SHIST:
if (histpush == 0) {
s->type = STTY;
s->str = null;
continue;
}
s->str = histptr[++histpush];
s->line++;
shellf("%s\n", s->str);
strcpy(line, s->str);
s->str = strchr(line, '\0');
*s->str++ = '\n';
*s->str = '\0';
s->str = line;
break;
#endif /* HISTORY */

case SFILE:
case SSTDIN:
{
char *xp = Xstring(s->xs, xp), *p;

s->line++;
while (1) {
p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
if (!p || (xp = p, xp[-1] == '\n')) {
s->str = Xlength(s->xs, xp) ?
Xstring(s->xs, xp) : NULL;
strip_nuls(Xstring(s->xs, xp),
Xlength(s->xs, xp));
break;
}
/* double buffer size */
xp++; /* move past null so doubling works... */
XcheckN(s->xs, xp, Xlength(s->xs, xp));
xp--; /* ...and move back again */
}
/* flush any unwanted input so other programs/builtins
* can read it. Not very optimal, but less error prone
* than flushing else where, dealing with redirections,
* etc..
* todo: reduce size of shf buffer (~128?) if SSTDIN
*/
if (s->type == SSTDIN)
shf_flush(s->u.shf);
else if (s->str == NULL)
shf_fdclose(s->u.shf);
break;
}

case SWSTR:
break;

case SSTRING:
break;

case SWORDS:
s->str = *s->u.strv++;
s->type = SWORDSEP;
break;

case SWORDSEP:
if (*s->u.strv == NULL) {
s->str = newline;
s->type = SEOF;
} else {
s->str = space;
s->type = SWORDS;
}
break;

case SALIAS:
if (s->flags & SF_ALIASEND) {
/* pass on an unused SF_ALIAS flag */
source = s->next;
source->flags |= s->flags & SF_ALIAS;
s = source;
} else if (*s->u.tblp->val.s
&& isspace(strchr(s->u.tblp->val.s, 0)[-1]))
{
source = s = s->next; /* pop source stack */
s->flags |= SF_ALIAS;
} else {
/* put a fake space at the end of the alias.
* This keeps the current alias in the source
* list so recursive aliases can be detected.
* The addition of a space after an alias
* never affects anything (I think).
*/
s->flags |= SF_ALIASEND;
s->str = space;
}
continue;

case SREREAD:
afree(s->u.start, ATEMP);
source = s = s->next;
continue;
}
if (s->str == NULL) {
s->type = SEOF;
s->str = null;
return 0;
}
if (s->flags & SF_ECHO) {
shf_write(s->str, strlen(s->str), shl_out);
shf_flush(shl_out);
}
}
return c;
}

void
set_prompt(to)
int to;
{
cur_prompt = to;

switch (to) {
case PS1: /* command */
prompt = substitute(strval(global("PS1")), 0);
break;

case PS2: /* command continuation */
prompt = strval(global("PS2"));
break;
}
}

/* See also related routine, promptlen() in edit.c */
void
pprompt(cp)
register char *cp;
{
while (*cp != 0)
if (*cp != '!')
shf_putc(*cp++, shl_out);
else
if (*++cp == '!')
shf_putc(*cp++, shl_out);
else
shf_fprintf(shl_out, "%d", source->line + 1);
shf_flush(shl_out);
}

/* Read the variable part of a ${...} expression (ie, up to but not including
* the :[-+?=#%] or close-brace.
*/
static char *
get_brace_var(wsp, wp)
XString *wsp;
char *wp;
{
enum parse_state {
PS_INITIAL, PS_SAW_HASH, PS_IDENT,
PS_NUMBER, PS_VAR1, PS_END
}
state;
char c;

state = PS_INITIAL;
while (1) {
c = getsc();
/* State machine to figure out where the variable part ends. */
switch (state) {
case PS_INITIAL:
if (c == '#') {
state = PS_SAW_HASH;
break;
}
/* fall through.. */
case PS_SAW_HASH:
if (letter(c))
state = PS_IDENT;
else if (digit(c))
state = PS_NUMBER;
else if (ctype(c, C_VAR1))
state = PS_VAR1;
else
state = PS_END;
break;
case PS_IDENT:
if (!letnum(c)) {
state = PS_END;
if (c == '[') {
char *tmp, *p;

ungetsc();
if (!arraysub(&tmp))
yyerror("missing ]\n");
for (p = tmp; *p; ) {
Xcheck(*wsp, wp);
*wp++ = *p++;
}
afree(tmp, ATEMP);
c = getsc();
}
}
break;
case PS_NUMBER:
if (!digit(c))
state = PS_END;
break;
case PS_VAR1:
state = PS_END;
break;
case PS_END: /* keep gcc happy */
break;
}
if (state == PS_END) {
*wp++ = '\0'; /* end of variable part */
ungetsc();
break;
}
Xcheck(*wsp, wp);
*wp++ = c;
}
return wp;
}

/*
* Save an array subscript - returns true if matching bracket found, false
* if eof or newline was found.
* (Returned string double null terminated)
*/
static int
arraysub(strp)
char **strp;
{
XString ws;
char *wp;
char c;
int depth = 0;

Xinit(ws, wp, 32, ATEMP);

do {
c = getsc();
Xcheck(ws, wp);
*wp++ = c;
if (c == '[')
depth++;
else if (c == ']')
depth--;
} while (depth > 0 && c && c != '\n');

*wp++ = '\0';
*strp = Xclose(ws, wp);

return depth == 0 ? 1 : 0;
}
pdksh-5.1.2/mail.c100644 0 133 10656 5657471726 12610 0ustar rootlsource/*
* Mailbox checking code by Robert J. Gibson, adapted for PD ksh by
* John R. MacMillan
*/
#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: mail.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include "ksh_stat.h"
#include "ksh_time.h"

#define MBMESSAGE "you have mail in $_"

typedef struct mbox {
struct mbox *mb_next; /* next mbox in list */
char *mb_path; /* path to mail file */
char *mb_msg; /* to announce arrival of new mail */
time_t mb_mtime; /* mtime of mail file */
} mbox_t;

struct mailmsg {
char *msg; /* Text of message */
struct mailmsg *next; /* Next message */
};

/*
* $MAILPATH is a linked list of mboxes. $MAIL is a treated as a
* special case of $MAILPATH, where the list has only one node. The
* same list is used for both since they are exclusive.
*/

static mbox_t *mplist = NULL;
static mbox_t mbox = { NULL, NULL, NULL, 0 };
static time_t mlastchkd = 0; /* when mail was last checked */
static struct mailmsg *mmsgs = NULL; /* Messages to be printed */

static void munset ARGS((mbox_t *mlist)); /* free mlist and mval */
static mbox_t * mballoc ARGS((char *p, char *m)); /* allocate a new mbox */
static void maddmsg ARGS((mbox_t *mbp));

void
mcheck()
{
register mbox_t *mbp;
time_t now;
long mailcheck;
struct tbl *vp;
struct stat stbuf;

if (getint(global("MAILCHECK"), &mailcheck) < 0)
return;

now = time((time_t *) 0);
if (mlastchkd == 0)
mlastchkd = now;
if (now - mlastchkd >= mailcheck) {
mlastchkd = now;

vp = global("MAILPATH");
if (vp && (vp->flag & ISSET))
mbp = mplist;
else if ((vp = global("MAIL")) && (vp->flag & ISSET))
mbp = &mbox;
else
mbp = NULL;

while (mbp) {
if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0
&& S_ISREG(stbuf.st_mode))
{
if (stbuf.st_size
&& mbp->mb_mtime != stbuf.st_mtime
&& stbuf.st_atime <= stbuf.st_mtime)
maddmsg(mbp);
mbp->mb_mtime = stbuf.st_mtime;
} else {
/*
* Some mail readers remove the mail
* file if all mail is read. If file
* does not exist, assume this is the
* case and set mtime to zero.
*/
mbp->mb_mtime = 0;
}
mbp = mbp->mb_next;
}
}
}

void
mbset(p)
register char *p;
{
struct stat stbuf;

if (mbox.mb_msg)
afree((void *)mbox.mb_msg, APERM);
mbox.mb_path = p;
mbox.mb_msg = NULL;
if (p && stat(p,&stbuf) == 0 && S_ISREG(stbuf.st_mode))
mbox.mb_mtime = stbuf.st_mtime;
else
mbox.mb_mtime = 0;
}

void
mpset(mptoparse)
register char *mptoparse;
{
register mbox_t *mbp;
register char *mpath, *mmsg, *mval;
char *p;

munset( mplist );
mplist = NULL;
mval = strsave(mptoparse, APERM);
while (mval) {
mpath = mval;
if ((mval = strchr(mval, PATHSEP)) != NULL) {
*mval = '\0', mval++;
}
/* POSIX/bourne-shell say file%message */
for (p = mpath; (mmsg = strchr(p, '%')); ) {
/* a literal percent? (POSIXism) */
if (mmsg[-1] == '\\') {
/* use memmove() to avoid overlap problems */
memmove(mmsg - 1, mmsg, strlen(mmsg) + 1);
p = mmsg + 1;
continue;
}
break;
}
/* at&t ksh says file?message */
if (!mmsg && !Flag(FPOSIX))
mmsg = strchr(mpath, '?');
if (mmsg) {
*mmsg = '\0';
mmsg++;
}
mbp = mballoc(mpath, mmsg);
mbp->mb_next = mplist;
mplist = mbp;
}
}

static void
munset(mlist)
register mbox_t *mlist;
{
register mbox_t *mbp;

while (mlist != NULL) {
mbp = mlist;
mlist = mbp->mb_next;
if (!mlist)
afree((void *)mbp->mb_path, APERM);
afree((void *)mbp, APERM);
}
}

static mbox_t *
mballoc(p, m)
char *p;
char *m;
{
struct stat stbuf;
register mbox_t *mbp;

mbp = (mbox_t *)alloc(sizeof(mbox_t), APERM);
mbp->mb_next = NULL;
mbp->mb_path = p;
mbp->mb_msg = m;
if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
mbp->mb_mtime = stbuf.st_mtime;
else
mbp->mb_mtime = 0;
return(mbp);
}

void
mprint()
{
struct mailmsg *mm;

while ((mm = mmsgs) != NULL) {
shellf("%s\n", mm->msg);
afree((void *)mm->msg, APERM);
mmsgs = mm->next;
afree((void *)mm, APERM);
}
}

static void
maddmsg( mbp )
mbox_t *mbp;
{
struct mailmsg *message;
struct tbl *vp;

message = (struct mailmsg *)alloc(sizeof(struct mailmsg), APERM);
setstr((vp = typeset("_", LOCAL, 0, 0, 0)), mbp->mb_path);

if (mbp->mb_msg)
message->msg = strsave(substitute(mbp->mb_msg,0),APERM);
else
message->msg = strsave(substitute(MBMESSAGE,0),APERM);

unset(vp);
message->next = mmsgs;
mmsgs = message;
}
pdksh-5.1.2/main.c100644 0 133 36002 5667370276 12602 0ustar rootlsource/*
* startup, main loop, enviroments and error handling
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: main.c,v 1.5 1994/05/31 13:34:34 michael Exp $";
#endif

#define EXTERN /* define EXTERNs in sh.h */

#include "sh.h"
#include "ksh_stat.h"
#include "ksh_time.h"

/*
* global data
*/

static void reclaim ARGS((void));
static void remove_temps ARGS((struct temp *tp));
static int is_restricted ARGS((char *name));

/*
* shell initialization
*/

static char initifs [] = "IFS= \t\n"; /* must be R/W */

static const char initsubs [] =
"${PS1=$ } ${PS2=> } ${PS3=#? } ${PS4=+ }";

static const char version_param[] =
#ifdef KSH
"KSH_VERSION"
#else /* KSH */
"SH_VERSION"
#endif /* KSH */
;

static const char *initcoms [] = {
"typeset", "-x", "SHELL", "PATH", "HOME", NULL,
"typeset", "-r", version_param, "PWD", "OLDPWD", NULL,
"typeset", "-ri", "PPID", NULL,
"typeset", "-i", "OPTIND=1", "MAILCHECK=600",
#ifdef KSH
"SECONDS=0", "RANDOM", "TMOUT=0",
#endif /* KSH */
NULL,
"alias",
/* Standard ksh aliases */
"hash=alias -t --",
#ifdef JOBS
"stop=kill -STOP",
"suspend=kill -STOP $$",
#endif
#ifdef KSH
"autoload=typeset -fu",
"functions=typeset -f",
"history=fc -l",
"integer=typeset -i",
"nohup=nohup ",
"local=typeset",
"r=fc -e -",
"type=whence -v",
#endif /* KSH */
#ifdef KSH
/* Aliases that are builtin commands in at&t */
"pwd=print -r - \"$PWD\"",
"login=exec login",
"newgrp=exec newgrp",
#endif /* KSH */
NULL,
/* this is what at&t ksh seems to track, with the addition of emacs */
"alias", "-tU",
"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
"mail", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who",
NULL,
NULL
};

int
main(argc, argv, envp)
int argc;
register char **argv;
char **envp;
{
register int i;
char *arg;
int argi;
Source *s;
struct block *l;
int restricted;
char **wp;
struct stat s1, s2;
extern char ksh_version [];
struct env env;

#ifdef MEM_DEBUG
chmem_push("+c", 1);
/*chmem_push("+cd", 1);*/
#endif

/* make sure argv[] is sane */
if (!*argv) {
static char *empty_argv[] = { "pdksh", (char *) 0 };

argv = empty_argv;
argc = 1;
}
kshname = *argv;

ainit(&aperm); /* initialize permanent Area */

/* set up base enviroment */
env.type = E_NONE;
ainit(&env.area);
env.savefd = NULL;
env.oenv = NULL;
env.loc = (struct block *) 0;
e = &env;
newblock(); /* set up global l->vars and l->funs */

/* Do this first so output routines (eg, errorf, shellf) can work */
initio();

initvar();

initctypes();

inittraps();

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, (char *) 0, 0);

if (len > 0) {
def_path = alloc(len + 1, APERM);
confstr(_CS_PATH, def_path, len + 1);
}
}
#endif /* HAVE_CONFSTR && _CS_PATH */
path = def_path;


/* Turn on brace expansion by default. At&t ksh's that have
* alternation always have it on. BUT, posix doesn't have
* brace expansion, so set this before setting up FPOSIX
* (change_flag() clears FBRACEEXPAND when FPOSIX is set).
*/
#ifdef BRACEEXPAND
Flag(FBRACEEXPAND) = 1;
#endif /* BRACEEXPAND */

/* set posix flag just before environment so that it will have
* exactly the same effect as the POSIXLY_CORRECT environment
* variable. If this needs to be done sooner to ensure correct posix
* operation, an initial scan of the environment will also have
* done sooner.
*/
#ifdef POSIXLY_CORRECT
change_flag(FPOSIX, OF_SPECIAL, 1);
#endif /* POSIXLY_CORRECT */

/* import enviroment */
if (envp != NULL)
for (wp = envp; *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);

/* assign PWD (using enviroment PWD if it passes test below) */
arg = strval(global("PWD"));
if (*arg == '\0'
|| stat(arg, &s1) < 0 || stat(".", &s2) < 0
|| s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino)
{
/* Ignore imported PWD - it's wrong */
arg = alloc(PATH, ATEMP);
if (getcwd(arg, PATH) == (char *) 0) {
warningf(FALSE,
"Can't find current directory (%s), changing to /\n",
strerror(errno));
if (arg)
afree(arg, ATEMP);
arg = slash;
(void) chdir(arg);
}
} else
/* Dup arg so we don't copy over its old value */
arg = strsave(arg, ATEMP);
setstr(global("PWD"), arg);
if (arg != slash)
afree(arg, ATEMP);
setint(global("PPID"), (long) getppid());
#ifdef KSH
setint(global("RANDOM"), (long) time((time_t *)0));
#endif /* KSH */
setstr(global(version_param), ksh_version);

/* execute initialization statements */
for (wp = (char**) initcoms; *wp != NULL; wp++) {
shcomexec(wp);
for (; *wp != NULL; wp++)
;
}

if (geteuid() == 0)
setstr(global("PS1"), "# ");

/* Set this before parsing arguments */
Flag(FPRIVILEGED) = getuid() != geteuid() || getgid() != getegid();

/* this to note if monitor is set on command line (see below) */
Flag(FMONITOR) = 127;

argi = parse_args(argv, OF_CMDLINE, (int *) 0);
if (argi < 0)
exit(1);

if (Flag(FCOMMAND)) {
s = pushs(SSTRING, ATEMP);
if (!(s->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;
if (isatty(0) && isatty(2))
Flag(FTALKING) = 1;
if (Flag(FTALKING)) {
s = pushs(STTY, ATEMP);
#ifdef EDIT
x_init();
#endif
} else {
s = pushs(SSTDIN, ATEMP);
s->file = "";
s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),
(struct shf *) 0);
}
}

/* This bizarreness is mandated by POSIX */
if (fstat(0, &s1) >= 0 && S_ISCHR(s1.st_mode)) {
int flags = fcntl(0, F_GETFL, 0);

if (flags >= 0 & (flags & O_NONBLOCK)) {
flags &= O_NONBLOCK;
fcntl(0, F_SETFL, flags);
}
}

/* initialize job control */
i = Flag(FMONITOR) != 127;
Flag(FMONITOR) = 0;
j_init(i);

l = e->loc;
l->argv = &argv[argi - 1];
l->argc = argc - argi;
l->argv[0] = kshname;
getopts_reset(1);

/* Disable during .profile/ENV reading */
restricted = Flag(FRESTRICTED);
Flag(FRESTRICTED) = 0;

if (argv[0][0] == '-'
|| ((arg = strrchr_dirsep(argv[0])) && *++arg == '-'))
{
#ifdef OS2
/* Need a startup option for OS/2 (4.9 used -0 as an option)
* since you don't really login to os2. This is not yet
* implemented
*/
/* XXX: if $INIT isn't set, /profile.ksh will be included
* which may be ./profile.ksh (ie, done twice).
*/
arg = substitute("$INIT/profile.ksh", 0);
include(*arg ? arg : "/etc/profile.ksh", 0, (char **) 0);
include(*arg ? arg : "c:/usr/etc/profile.ksh", 0, (char **) 0);
include("profile.ksh", 0, (char **) 0);
include(substitute("$HOME/profile.ksh", 0), 0, (char **) 0);
#else /* OS2 */
include("/etc/profile", 0, (char **) 0);
if (!Flag(FPRIVILEGED))
include(substitute("$HOME/.profile", 0), 0,
(char **) 0);
#endif /* OS2 */
}

if (Flag(FPRIVILEGED))
include("/etc/suid_profile", 0, (char **) 0);
else {
/* include $ENV */
arg = substitute(strval(global("ENV")), DOTILDE);
if (*arg != '\0')
include(arg, 0, (char **) 0);
#ifdef OS2
else
include(substitute("$HOME/kshrc.ksh", 0), 0,
(char **) 0);
#endif /* OS2 */
}

if (is_restricted(argv[0]) || is_restricted(strval(global("SHELL"))))
restricted = 1;
if (restricted) {
static const char *restr_com[] = {
"typeset", "-r", "PATH",
"ENV", "SHELL",
(char *) 0
};
shcomexec((char **) restr_com);
/* After typeset command... */
Flag(FRESTRICTED) = 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(name, argc, argv)
register char *name;
int argc;
char **argv;
{
register Source *volatile s = NULL;
struct shf *shf;
char **volatile old_argv;
volatile int old_argc;
int i;

shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
if (shf == NULL)
return -1;

if (argv) {
old_argv = e->loc->argv;
old_argc = e->loc->argc;
} else {
old_argv = (char **) 0;
old_argc = 0;
}
newenv(E_INCL);
if ((i = setjmp(e->jbuf))) {
quitenv();
if (s)
shf_close(s->u.shf);
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:
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 = strsave(name, ATEMP);
i = shell(s, FALSE);
quitenv();
shf_close(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(comm)
register char *comm;
{
register Source *s;

s = pushs(SSTRING, ATEMP);
s->str = comm;
return shell(s, FALSE);
}

/*
* run the commands from the input source, returning status.
*/
int
shell(s, exit_atend)
Source *volatile s; /* input source */
int volatile exit_atend;
{
struct op *t;
int wastty;
volatile int attempts = 13;
volatile int interactive = Flag(FTALKING) && s->type == STTY;
int i;

newenv(E_PARSE);
exstat = 0;
#ifdef JOBS
if (interactive)
really_exit = 0;
#endif /* JOBS */
if ((i = setjmp(e->jbuf))) {
s->str = null;
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)
s->type = STTY;
/* 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/STTY)...
*/
break;
}
/* fall through... */
case LEXIT:
case LLEAVE:
case LRETURN:
quitenv();
unwind(i); /* keep on going */
/*NOREACHED*/
default:
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();

if ((wastty = (s->type == STTY)) || s->type == SHIST) {
set_prompt(PS1);
mcheck();
}

t = compile(s);
if (t != NULL && t->type == TEOF) {
if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
shellf("Use `exit' to leave ksh\n");
s->type = STTY;
#ifdef JOBS
} else if (wastty && !really_exit && j_stopped()) {
really_exit = 1;
s->type = STTY;
#endif /* JOBS */
} else {
/* this for POSIX, which says EXIT traps
* shell be taken in the environment
* immediately after the last command
* executed.
*/
if (exit_atend)
unwind(LEXIT);
break;
}
}

if (t && (!Flag(FNOEXEC) || s->type == STTY))
exstat = execute(t, 0);

#ifdef JOBS
if (t != NULL && t->type != TEOF && interactive && really_exit)
really_exit = 0;
#endif /* JOBS */

reclaim();
}
quitenv();
return exstat;
}

/* return to closest error handler or shell(), exit if none found */
void
unwind(i)
int i;
{
/* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */
if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR)
&& sigtraps[SIGEXIT_].trap))
{
runtrap(&sigtraps[SIGEXIT_]);
i = LLEAVE;
} else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
runtrap(&sigtraps[SIGERR_]);
i = LLEAVE;
}
while (1) {
switch (e->type) {
case E_PARSE:
case E_FUNC:
case E_INCL:
case E_LOOP:
case E_ERRH:
longjmp(e->jbuf, i);
/*NOTREACHED*/

case E_NONE: /* bottom of the stack */
{
if (Flag(FTALKING))
hist_finish();
j_exit();
remove_temps(func_heredocs);
if (i == LINTR) {
int sig = exstat - 128;

/* ham up our death a bit (at&t ksh
* only seems to do this for SIGTERM)
* Don't do it for SIGQUIT, since we'd
* dump a core..
*/
if (sig == SIGINT || sig == SIGTERM) {
setsig(&sigtraps[sig], SIG_DFL,
SS_RESTORE_CURR|SS_FORCE);
kill(0, sig);
}
}
exit(exstat);
/* NOTREACHED */
}

default:
quitenv();
}
}
}

void
newenv(type)
int type;
{
register struct env *ep;

ep = (struct env *) alloc(sizeof(*ep), ATEMP);
ep->type = type;
ep->flags = 0;
ainit(&ep->area);
ep->loc = e->loc;
ep->savefd = NULL;
ep->oenv = e;
ep->temps = NULL;
e = ep;
}

void
quitenv()
{
register struct env *ep = e;
register int fd;

if (ep->oenv == NULL) /* cleanup_parents_env() was called */
exit(exstat); /* exit child */
if (ep->oenv->loc != ep->loc)
popblock();
if (ep->savefd != NULL) {
for (fd = 0; fd < NUFILE; fd++)
/* if ep->savefd[fd] < 0, means fd was closed */
if (ep->savefd[fd])
restfd(fd, ep->savefd[fd]);
if (ep->savefd[2]) /* Clear any write errors */
shf_reopen(2, SHF_WR, shl_out);
}
reclaim();
e = e->oenv;
afree(ep, ATEMP);
}

/* Called after a fork to cleanup stuff left over from parents environment */
void
cleanup_parents_env()
{
struct env *ep;
int fd;

/* Don't clean up temporary files - parent will probably need them.
* Also, can't easily reclaim memory since variables, etc. could be
* anywyere.
*/

/* 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]);
}
e->oenv = (struct env *) 0;
}

/* remove temp files and free ATEMP Area */
static void
reclaim()
{
remove_temps(e->temps);
e->temps = NULL;
afreeall(&e->area);
}

static void
remove_temps(tp)
struct temp *tp;
{
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(name)
char *name;
{
char *p;

if ((p = strrchr_dirsep(name)))
name = p;
/* accepts rsh, rksh, rpdksh, pdrksh, etc. */
return (p = strchr(name, 'r')) && strstr(p, "sh");
}

void
aerror(ap, msg)
Area *ap;
const char *msg;
{
internal_errorf(1, "alloc: %s", msg);
errorf((char *) 0); /* this is never executed - keeps gcc quiet */
/*NOTREACHED*/
}
pdksh-5.1.2/misc.c100644 0 133 47211 5666706275 12616 0ustar rootlsource/*
* Miscellaneous functions
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: misc.c,v 1.2 1994/05/19 18:32:40 michael Exp michael $";
#endif

#include "sh.h"
#ifdef HAVE_LIMITS_H
# include
#endif

#ifndef UCHAR_MAX
# define UCHAR_MAX 0xFF
#endif

char ctypes [UCHAR_MAX+1]; /* type bits for unsigned char */

static char *cclass ARGS((char *p, int sub));

/*
* Fast character classes
*/
void
setctypes(s, t)
register const char *s;
register int t;
{
register int i;

if ((t&C_IFS)) {
for (i = 0; i < UCHAR_MAX+1; i++)
ctypes[i] &= ~C_IFS;
ctypes[0] |= C_IFS; /* include \0 in C_IFS */
}
while (*s != 0)
ctypes[(unsigned char) *s++] |= t;
}

void
initctypes()
{
register int c;

for (c = 'a'; c <= 'z'; c++)
ctypes[c] |= C_ALPHA;
for (c = 'A'; c <= 'Z'; c++)
ctypes[c] |= C_ALPHA;
ctypes['_'] |= C_ALPHA;
setctypes("0123456789", C_DIGIT);
setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */
setctypes("*@#!$-?", C_VAR1);
setctypes(" \t\n", C_IFSWS);
setctypes("=-+?", C_SUBOP1);
setctypes("#%", C_SUBOP2);
}

/* convert unsigned long to base N string */

char *
ulton(n, base)
register unsigned long n;
int base;
{
register char *p;
static char buf [20];

p = &buf[sizeof(buf)];
*--p = '\0';
do {
*--p = "0123456789ABCDEF"[n%base];
n /= base;
} while (n != 0);
return p;
}

char *
strsave(s, ap)
register const char *s;
Area *ap;
{
return s ? strcpy((char*) alloc((size_t)strlen(s)+1, ap), s) : NULL;
}

/* Allocate a string of size n+1 and copy upto n characters from the possibly
* null terminated string s into it. Always returns a null terminated string
* (unless n < 0).
*/
char *
strnsave(s, n, ap)
register const char *s;
int n;
Area *ap;
{
char *ns;

if (n < 0)
return 0;
ns = alloc(n + 1, ap);
ns[0] = '\0';
return strncat(ns, s, n);
}

/* called from expand.h:XcheckN() to grow buffer */
char *
Xcheck_grow_(xsp, xp, more)
XString *xsp;
char *xp;
int more;
{
char *old_beg = xsp->beg;

xsp->len += more > xsp->len ? more : xsp->len;
xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap);
xsp->end = xsp->beg + xsp->len;
return xsp->beg + (xp - old_beg);
}

struct option options[] = {
/* Special cases (see parse_args()): -A, -o, -s.
* Options are sorted by their longnames - the order of these
* entries MUST match the order of sh_flag F* enumerations in sh.h.
*/
{ "allexport", 'a', OF_ANY },
#ifdef BRACEEXPAND
{ "braceexpand", 0, OF_ANY }, /* non-standard */
#endif
{ "bgnice", 0, OF_ANY },
{ null, 'c', OF_CMDLINE },
#ifdef EMACS
{ "emacs", 0, OF_ANY },
#endif
{ "errexit", 'e', OF_ANY },
#ifdef EMACS
{ "gmacs", 0, OF_ANY },
#endif
{ "ignoreeof", 0, OF_ANY },
{ "interactive",'i', OF_CMDLINE },
{ "keyword", 'k', OF_ANY },
{ "markdirs", 'X', OF_ANY },
#ifdef JOBS
{ "monitor", 'm', OF_ANY },
#else /* JOBS */
{ null, 'm', 0 }, /* so FMONITOR not ifdef'd */
#endif /* JOBS */
{ "noclobber", 'C', OF_ANY },
{ "noexec", 'n', OF_ANY },
{ "noglob", 'f', OF_ANY },
{ "nolog", 0, OF_ANY }, /* no effect */
#ifdef JOBS
{ "notify", 'b', OF_ANY },
#endif /* JOBS */
{ "nounset", 'u', OF_ANY },
{ "posix", 0, OF_ANY }, /* non-standard */
{ "privileged", 'p', OF_ANY },
{ "restricted", 'r', OF_CMDLINE },
{ "stdin", 's', OF_CMDLINE }, /* pseudo non-standard */
{ "trackall", 'h', OF_ANY },
{ "verbose", 'v', OF_ANY },
#ifdef VI
{ "vi", 0, OF_ANY },
{ "viraw", 0, OF_ANY }, /* no effect */
{ "vi-show8", 0, OF_ANY }, /* non-standard */
{ "vi-tabcomplete", 0, OF_ANY }, /* non-standard */
#endif
{ "xtrace", 'x', OF_ANY },
{ NULL, 0, 0 }
};

/*
* translate -o option into F* constant (also used for test -o option)
*/
int
option(n)
const char *n;
{
int i;

for (i = 0; options[i].name; i++)
if (strcmp(options[i].name, n) == 0)
return i;

return -1;
}

struct options_info {
int opt_width;
struct {
char *name;
int flag;
} opts[NELEM(options)];
};

/* format a single select menu item */
static char *
options_fmt_entry(arg, i, buf, buflen)
void *arg;
int i;
char *buf;
int buflen;
{
struct options_info *oi = (struct options_info *) arg;

shf_snprintf(buf, buflen, "%-*s %s",
oi->opt_width, oi->opts[i].name,
Flag(oi->opts[i].flag) ? "on" : "off");
return buf;
}

static void
printoptions(verbose)
int verbose;
{
int i;

if (verbose) {
struct options_info oi;
int n, len;

/* verbose version */
shprintf("Current option settings\n");

for (i = n = oi.opt_width = 0; options[i].name; i++)
if (options[i].name[0]) {
len = strlen(options[i].name);
oi.opts[n].name = options[i].name;
oi.opts[n++].flag = i;
if (len > oi.opt_width)
oi.opt_width = len;
}
print_columns(shl_stdout, n, options_fmt_entry, &oi,
oi.opt_width + 5);
} else {
/* short version */
for (i = 0; options[i].name; i++)
if (Flag(i) && options[i].name[0])
shprintf("%s ", options[i].name);
shprintf(newline);
}
}

char *
getoptions()
{
int i;
char m[FNFLAGS + 1];
register char *cp = m;

for (i = 0; options[i].name; i++)
if (options[i].c && Flag(i))
*cp++ = options[i].c;
*cp = 0;
return strsave(m, ATEMP);
}

/* change a Flag(*) value; takes care of special actions */
void
change_flag(f, what, newval)
enum sh_flag f; /* flag to change */
int what; /* what is changing the flag (command line vs set) */
int newval;
{
int oldval;

oldval = Flag(f);
Flag(f) = newval;
#ifdef JOBS
if (f == FMONITOR) {
if (what != OF_CMDLINE && newval != oldval)
j_change();
} else
#endif /* JOBS */
#ifdef EDIT
if (0
# ifdef VI
|| f == FVI
# endif /* VI */
# ifdef EMACS
|| f == FEMACS || f == FGMACS
# endif /* EMACS */
)
{
if (newval) {
# ifdef VI
Flag(FVI) = 0;
# endif /* VI */
# ifdef EMACS
Flag(FEMACS) = Flag(FGMACS) = 0;
# endif /* EMACS */
Flag(f) = newval;
}
} else
#endif /* EDIT */
/* Turning off -p? */
if (f == FPRIVILEGED && oldval && !newval) {
#ifdef OS2
;
#else /* OS2 */
setuid(getuid());
setgid(getgid());
#endif /* OS2 */
} else if (f == FPOSIX && newval) {
#ifdef BRACEEXPAND
Flag(FBRACEEXPAND) = 0
#endif /* BRACEEXPAND */
;
}
}

/* parse command line & set command arguments. returns the index of
* non-option arguments, -1 if there is an error.
*/
int
parse_args(argv, what, setargsp)
char **argv;
int what; /* OF_CMDLINE or OF_SET */
int *setargsp;
{
static char cmd_opts[NELEM(options) + 3]; /* o:\0 */
static char set_opts[NELEM(options) + 5]; /* Ao;s\0 */
char *opts;
char *arrayname;
Getopt go;
int i, optc, set, sortargs = 0, arrayset = 0;

/* First call? Build option strings... */
if (cmd_opts[0] == '\0') {
char *p;

/* c is also in options[], but it needs a trailing : */
strcpy(cmd_opts, "o:"); /* see cmd_opts[] declaration */
p = cmd_opts + strlen(cmd_opts);
for (i = 0; options[i].name; i++)
if (options[i].c && (options[i].flags & OF_CMDLINE))
*p++ = options[i].c;
*p = '\0';

strcpy(set_opts, "Ao;s"); /* see set_opts[] declaration */
p = set_opts + strlen(set_opts);
for (i = 0; options[i].name; i++)
if (options[i].c && (options[i].flags & OF_CMDLINE))
*p++ = options[i].c;
*p = '\0';
}

opts = (what == OF_CMDLINE) ? cmd_opts : set_opts;
ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
while ((optc = ksh_getopt(argv, &go, opts)) != EOF) {
set = (go.info & GI_PLUS) ? 0 : 1;
switch (optc) {
case 'A':
arrayset = set ? 1 : -1;
break;

case 'o':
if (go.optarg == (char *) 0) {
/* lone -o: print options
*
* Note that on the command line, -o requires
* an option (ie, can't get here if what is
* OF_CMDLINE).
*/
printoptions(set);
break;
}
i = option(go.optarg);
if (i >= 0 && (options[i].flags & what))
change_flag((enum sh_flag) i, what, set);
else {
bi_errorf("%s: bad option", go.optarg);
return -1;
}
break;

case '?':
return -1;

default:
/* -s: sort positional params (at&t ksh stupidity) */
if (what == OF_SET && optc == 's') {
sortargs = 1;
break;
}
for (i = 0; options[i].name; i++)
if (optc == options[i].c
&& (what & options[i].flags))
{
change_flag((enum sh_flag) i, what,
set);
break;
}
if (!options[i].name) {
internal_errorf(1, "parse_args: `%c'", optc);
return -1; /* not reached */
}
}
}
if (!(go.info & GI_MINUSMINUS) && argv[go.optind]
&& (argv[go.optind][0] == '-' || argv[go.optind][0] == '+')
&& argv[go.optind][1] == '\0')
{
/* lone - clears -v and -x flags */
if (argv[go.optind][0] == '-' && !Flag(FPOSIX))
Flag(FVERBOSE) = Flag(FXTRACE) = 0;
/* set skips lone - or + option */
go.optind++;
}
if (setargsp)
/* -- means set $#/$* even if there are no arguments */
*setargsp = !arrayset && ((go.info & GI_MINUSMINUS)
|| argv[go.optind]);

if (arrayset) {
arrayname = argv[go.optind++];
if (!arrayname) {
bi_errorf("-A: missing array name\n");
return -1;
}
if (!*arrayname || *skip_varname(arrayname, FALSE)) {
bi_errorf("%s: is not an identifier\n",
arrayname);
return -1;
}
} else
arrayname = (char *) 0; /* keep gcc happy */
if (sortargs) {
for (i = go.optind; argv[i]; i++)
;
qsortp((void **) &argv[go.optind], (size_t) (i - go.optind),
xstrcmp);
}
if (arrayset) {
set_array(arrayname, arrayset, argv + go.optind);
for (; argv[go.optind]; go.optind++)
;
}

return go.optind;
}

/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
int
getn(as, ai)
char *as;
int *ai;
{
register char *s;
register int n;
int sawdigit = 0;

s = as;
if (*s == '-')
s++;
for (n = 0; digit(*s); s++, sawdigit = 1)
n = n * 10 + (*s - '0');
*ai = (*as == '-') ? -n : n;
if (*s || !sawdigit)
return 0;
return 1;
}

/* getn() that prints error */
int
bi_getn(as, ai)
char *as;
int *ai;
{
int rv = getn(as, ai);

if (!rv)
bi_errorf("%s: bad number", as);
return rv;
}

/* -------- gmatch.c -------- */

/*
* int gmatch(string, pattern)
* char *string, *pattern;
*
* Match a pattern as in sh(1).
* pattern character are prefixed with MAGIC by expand.
*/

int
gmatch(s, p)
register char *s, *p;
{
register int sc, pc;

if (s == NULL || p == NULL)
return 0;
while ((pc = *p++) != 0) {
sc = *s++;
if (pc == MAGIC)
switch (*p++) {
case '[':
if (sc == 0 || (p = cclass(p, sc)) == NULL)
return (0);
break;

case '?':
if (sc == 0)
return (0);
break;

case '*':
s--;
do {
if (*p == '\0' || gmatch(s, p))
return (1);
} while (*s++ != '\0');
return (0);

#if 0
/* [!+*?@](pattern|pattern|..) */
case '!': /* matches none of the patterns */
case '+': /* matches one or more times */
case '*': /* matches zero or more times */
case '?': /* matches zero or once */
case '@': /* matches once */
/* fall through */
#endif /* 0 */

default:
if (sc != p[-1])
return 0;
break;
}
else
if (sc != pc)
return 0;
}
return (*s == 0);
}

static char *
cclass(p, sub)
register char *p;
register int sub;
{
register int c, d, not, found = 0;
char *orig_p = p;

if ((not = (*p == MAGIC && *++p == NOT)))
p++;
do {
if (*p == MAGIC)
p++;
if (*p == '\0')
/* No closing ] - act as if the opening [ was quoted */
return sub == '[' ? orig_p : NULL;
c = *p++;
if (p[0] == MAGIC && p[1] == '-'
&& (p[2] != MAGIC || p[3] != ']'))
{
p += 2; /* MAGIC- */
if (*p == MAGIC)
p++;
d = *p++;
/* POSIX says this is an invalid expression */
if (c > d)
return NULL;
} else
d = c;
if (c == sub || (c <= sub && sub <= d))
found = 1;
} while (!(p[0] == MAGIC && p[1] == ']'));

return (found != not) ? p+2 : NULL;
}

/* -------- qsort.c -------- */

/*
* quick sort of array of generic pointers to objects.
*/
static void qsort1 ARGS((void **base, void **lim, int (*f)(void *, void *)));

void
qsortp(base, n, f)
void **base; /* base address */
size_t n; /* elements */
int (*f)(); /* compare function */
{
qsort1(base, base + n, f);
}

#define swap2(a, b) {\
register void *t; t = *(a); *(a) = *(b); *(b) = t;\
}
#define swap3(a, b, c) {\
register void *t; t = *(a); *(a) = *(c); *(c) = *(b); *(b) = t;\
}

static void
qsort1(base, lim, f)
void **base, **lim;
int (*f)();
{
register void **i, **j;
register void **lptr, **hptr;
size_t n;
int c;

top:
n = (lim - base) / 2;
if (n == 0)
return;
hptr = lptr = base+n;
i = base;
j = lim - 1;

for (;;) {
if (i < lptr) {
if ((c = (*f)(*i, *lptr)) == 0) {
lptr --;
swap2(i, lptr);
continue;
}
if (c < 0) {
i += 1;
continue;
}
}

begin:
if (j > hptr) {
if ((c = (*f)(*hptr, *j)) == 0) {
hptr ++;
swap2(hptr, j);
goto begin;
}
if (c > 0) {
if (i == lptr) {
hptr ++;
swap3(i, hptr, j);
i = lptr += 1;
goto begin;
}
swap2(i, j);
j -= 1;
i += 1;
continue;
}
j -= 1;
goto begin;
}

if (i == lptr) {
if (lptr-base >= lim-hptr) {
qsort1(hptr+1, lim, f);
lim = lptr;
} else {
qsort1(base, lptr, f);
base = hptr+1;
}
goto top;
}

lptr -= 1;
swap3(j, lptr, i);
j = hptr -= 1;
}
}

int
xstrcmp(p1, p2)
void *p1, *p2;
{
return (strcmp((char *)p1, (char *)p2));
}

/* Initialize a Getopt structure */
void
ksh_getopt_reset(go, flags)
Getopt *go;
int flags;
{
go->optind = 1;
go->optarg = (char *) 0;
go->p = 0;
go->flags = flags;
go->info = 0;
go->buf[1] = '\0';
}


/* getopt() used for shell built-in commands, the getopts command, and
* command line options.
* A leading ':' in options means don't print errors, instead return '?'
* or ':' and set go->optarg to the offending option character.
* If GF_ERROR is set (and option doesn't start with :), errors result in
* a call to bi_errorf().
*
* Non-standard features:
* - ';' is like ':' in options, except the argument is optional
* (if it isn't present, optarg is set to 0).
* Used for 'set -o'.
* - ',' is like ':' in options, except the argument always immediately
* follows the option character (optarg is set to the null string if
* the option is missing).
* Used for 'read -u2' and 'print -u2'.
* - '#' is like ':' in options, expect that the argument is optional
* and must start with a digit. If the argument doesn't start with a
* digit, it is assumed to be missing and normal option processing
* continues (optarg is set to 0 if the option is missing).
* Used for 'typeset -LZ4'.
* - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
* option starting with + is accepted, the GI_PLUS flag will be set
* in go->info. Once a - or + has been seen, all other options must
* start with the same character.
*/
int
ksh_getopt(argv, go, options)
char **argv;
Getopt *go;
char *options;
{
char c;
char *o;

/* protect against multiple calls which could cause optind or p
* to go out of range. Also prevents skipping --.
*/
if (go->info & GI_DONE)
return EOF;
if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
char *arg = argv[go->optind], flag = arg ? *arg : '\0';

go->p = 1;
if (flag == '-' && arg[1] == '-' && arg[2] == '\0') {
go->optind++;
go->info |= GI_DONE | GI_MINUSMINUS;
return EOF;
}
if (arg == (char *) 0
|| ((flag != '-' || (go->info & GI_PLUS))
&& (!(go->flags & GF_PLUSOPT) || (go->info & GI_MINUS)
|| flag != '+'))
|| (c = arg[1]) == '\0')
{
go->info |= GI_DONE;
return EOF;
}
go->optind++;
go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
}
go->p++;
if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#'
|| !(o = strchr(options, c)))
{
go->info |= GI_DONE;
if (options[0] == ':') {
go->buf[0] = c;
go->optarg = go->buf;
} else {
warningf(TRUE, "%s: -%c: bad option\n",
argv[0], c);
if (go->flags & GF_ERROR)
bi_errorf((char *) 0);
}
return '?';
}
/* : means argument must be present, may be part of option argument
* or the next argument
* ; same as : but argument may be missing
* , means argument is part of option argument, and may be null.
*/
if (*++o == ':' || *o == ';') {
if (argv[go->optind - 1][go->p])
go->optarg = argv[go->optind - 1] + go->p;
else if (argv[go->optind])
go->optarg = argv[go->optind++];
else if (*o == ';')
go->optarg = (char *) 0;
else {
go->info |= GI_DONE;
if (options[0] == ':') {
go->buf[0] = c;
go->optarg = go->buf;
return ':';
}
warningf(TRUE, "%s: -`%c' requires argument\n",
argv[0], c);
if (go->flags & GF_ERROR)
bi_errorf((char *) 0);
return '?';
}
go->p = 0;
} else if (*o == ',') {
/* argument is attatched to option character, even if null */
go->optarg = argv[go->optind - 1] + go->p;
go->p = 0;
} else if (*o == '#') {
/* argument is optional and may be attatched or unattatched
* but must start with a digit. optarg is set to 0 if the
* argument is missing.
*/
if (argv[go->optind - 1][go->p]) {
if (digit(argv[go->optind - 1][go->p])) {
go->optarg = argv[go->optind - 1] + go->p;
go->p = 0;
} else
go->optarg = (char *) 0;;
} else {
if (argv[go->optind] && digit(argv[go->optind][0])) {
go->optarg = argv[go->optind++];
go->p = 0;
} else
go->optarg = (char *) 0;;
}
}
return c;
}

/* print variable/alias value using necessary quotes
* (POSIX says they should be suitable for re-entry...)
* No trailing newline is printed.
*/
void
print_value_quoted(s)
char *s;
{
char *p;
int inquote = 0;

/* Test if any quotes are needed */
for (p = s; *p; p++)
if (!letnum(*p) && *p != '/')
break;
if (!*p) {
shprintf("%s", s);
return;
}
for (p = s; *p; p++) {
if (*p == '\'') {
shprintf("'\\'" + 1 - inquote);
inquote = 0;
} else {
if (!inquote) {
shprintf("'");
inquote = 1;
}
shf_putc(*p, shl_stdout);
}
}
if (inquote)
shprintf("'");
}

/* Print things in columns and rows - func() is called to format the ith
* element
*/
void
print_columns(shf, n, func, arg, max_width)
struct shf *shf;
int n;
char *(*func) ARGS((void *, int, char *, int));
void *arg;
int max_width;
{
char *str = (char *) alloc(max_width + 1, ATEMP);
int i;
int r, c;
int rows, cols;
int nspace;

/* max_width + 1 for the space. Note that no space
* is printed after the last column to avoid problems
* with terminals that have auto-wrap.
*/
cols = x_cols / (max_width + 1);
if (!cols)
cols = 1;
rows = (n + cols - 1) / cols;
if (n && cols > rows) {
int tmp = rows;

rows = cols;
cols = tmp;
if (rows > n)
rows = n;
}

nspace = (x_cols - max_width * cols) / cols;
if (nspace <= 0)
nspace = 1;
for (r = 0; r < rows; r++) {
for (c = 0; c < cols; c++) {
i = c * rows + r;
if (i < n) {
shf_fprintf(shf, "%-*s",
max_width,
(*func)(arg, i, str, max_width + 1));
if (c + 1 < cols)
shf_fprintf(shf, "%*s", nspace, null);
}
}
shf_putchar('\n', shf);
}
afree(str, ATEMP);
}

/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */
int
strip_nuls(buf, nbytes)
char *buf;
int nbytes;
{
char *dst;

/* nbytes check because some systems (older freebsd's) have a buggy
* memchr()
*/
if (nbytes && (dst = memchr(buf, '\0', nbytes))) {
char *end = buf + nbytes;
char *p, *q;

for (p = dst; p < end; p = q) {
/* skip a block of nulls */
while (++p < end && *p == '\0')
;
/* find end of non-null block */
if (!(q = memchr(p, '\0', end - p)))
q = end;
memmove(dst, p, q - p);
dst += q - p;
}
*dst = '\0';
return dst - buf;
}
return nbytes;
}

/* Copy at most dsize-1 bytes from src to dst, ensuring dst is null terminated.
* Returns dst.
*/
char *
str_zcpy(dst, src, dsize)
char *dst;
char *src;
int dsize;
{
if (dsize > 0) {
int len = strlen(src);

if (len >= dsize)
len = dsize - 1;
memcpy(dst, src, len);
dst[len] = '\0';
}
return dst;
}
pdksh-5.1.2/missing.c100644 0 133 15303 5660425773 13324 0ustar rootlsource/*
* Routines which may be missing on some machines
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: missing.c,v 1.1 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include "ksh_stat.h"
#include "ksh_dir.h"

#ifndef HAVE_MEMSET
void *
memset(d, c, n)
void *d;
int c;
size_t n;
{
unsigned char *p = (unsigned char *) d;

/* Not amazingly fast.. */
for (; n > 0; --n)
*p++ = c;
return d;
}
#endif /* !HAVE_MEMSET */


#if !defined(HAVE_MEMMOVE) && !defined(HAVE_BCOPY)
void *
memmove(d, s, n)
void *d;
const void *s;
size_t n;
{
char *dp = (char *) d, *sp = (char *) s;

if (n <= 0)
;
else if (dp < sp)
do
*dp++ = *sp++;
while (--n > 0);
else if (dp > sp) {
dp += n;
sp += n;
do
*--dp = *--sp;
while (--n > 0);
}
return d;
}
#endif /* !HAVE_MEMMOVE && !HAVE_BCOPY */


#ifndef HAVE_STRCASECMP
/*
* Case insensitive string compare routines, same semantics as str[n]cmp()
* (assumes ASCII..).
*/
static const char strichars[256] = {
0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};

int
strcasecmp(s1, s2)
const char *s1;
const char *s2;
{
const unsigned char *us1 = (const unsigned char *) s1;
const unsigned char *us2 = (const unsigned char *) s2;

while (strichars[*us1] == strichars[*us2++])
if (!*us1++)
return 0;

return strichars[*us1] - strichars[*--us2];
}

int
strncasecmp(s1, s2, n)
const char *s1;
const char *s2;
int n;
{
const unsigned char *us1 = (const unsigned char *) s1;
const unsigned char *us2 = (const unsigned char *) s2;

while (--n >= 0 && strichars[*us1] == strichars[*us2++])
if (!*us1++)
return 0;

return n < 0 ? 0 : strichars[*us1] - strichars[*--us2];
}
#endif /* HAVE_STRCASECMP */


#ifndef HAVE_STRSTR
char *
strstr(s, p)
char *s;
char *p;
{
int len;

if (s && p)
for (len = strlen(p); *s; s++)
if (*s == *p && strncmp(s, p, len) == 0)
return s;

return 0;
}
#endif /* HAVE_STRSTR */


#ifndef HAVE_STRERROR
char *
strerror(err)
int err;
{
static char buf[64];
# ifdef HAVE_SYS_ERRLIST
# ifndef SYS_ERRLIST_DECLARED
extern int sys_nerr;
extern char *sys_errlist[];
# endif
char *p;

if (err < 0 || err >= sys_nerr)
shf_snprintf(p = buf, sizeof(buf), "Unknown system error %d",
err);
else
p = sys_errlist[err];
return p;
# else /* HAVE_SYS_ERRLIST */
switch (err) {
case EINVAL:
return "Invalid argument";
case EACCES:
return "Permission denied";
case ESRCH:
return "No such process";
case EPERM:
return "Not owner";
case ENOENT:
return "No such file or directory";
case ENOTDIR:
return "Not a directory";
case ENOEXEC:
return "Exec format error";
case ENOMEM:
return "Not enough memory";
case E2BIG:
return "Argument list too long";
default:
shf_snprintf(buf, sizeof(buf), "Unknown system error %d", err);
return buf;
}
# endif /* HAVE_SYS_ERRLIST */
}
#endif /* !HAVE_STRERROR */


#ifndef HAVE_GETCWD
# include
# ifndef MAXPATHLEN
# define MAXPATHLEN 1024
# endif /* MAXPATHLEN */

char *
getcwd(buf, size)
char *buf;
int size;
{
extern char *getwd ARGS((char *));
char tmp_buf[MAXPATHLEN + 1];

if (size <= 0) {
errno = EINVAL;
return (char *) 0;
}
if (getwd(size < MAXPATHLEN ? tmp_buf : buf) == (char *) 0) {
errno = EACCES;
return (char *) 0;
}
if (size < MAXPATHLEN) {
tmp_buf[sizeof(tmp_buf) - 1] = '\0';
if (strlen(tmp_buf) >= size) {
errno = ERANGE;
return (char *) 0;
}
strcpy(buf, tmp_buf);
}
return buf;
}
#endif /* !HAVE_GETCWD */


#ifdef TIMES_BROKEN
# include "ksh_time.h"
# include "ksh_times.h"
# ifdef HAVE_GETRUSAGE
# include
# else /* HAVE_GETRUSAGE */
# include
# endif /* HAVE_GETRUSAGE */

clock_t
ksh_times(tms)
struct tms *tms;
{
static clock_t base_sec;
clock_t rv;

# ifdef HAVE_GETRUSAGE
{
struct timeval tv;
struct rusage ru;

getrusage(RUSAGE_SELF, &ru);
tms->tms_utime = ru.ru_utime.tv_sec * CLK_TCK
+ ru.ru_utime.tv_usec * CLK_TCK / 1000000;
tms->tms_stime = ru.ru_stime.tv_sec * CLK_TCK
+ ru.ru_stime.tv_usec * CLK_TCK / 1000000;

getrusage(RUSAGE_CHILDREN, &ru);
tms->tms_cutime = ru.ru_utime.tv_sec * CLK_TCK
+ ru.ru_utime.tv_usec * CLK_TCK / 1000000;
tms->tms_cstime = ru.ru_stime.tv_sec * CLK_TCK
+ ru.ru_stime.tv_usec * CLK_TCK / 1000000;

gettimeofday(&tv, (struct timezone *) 0);
if (base_sec == 0)
base_sec = tv.tv_sec;
rv = (tv.tv_sec - base_sec) * CLK_TCK;
rv += tv.tv_usec * CLK_TCK / 1000000;
}
# else /* HAVE_GETRUSAGE */
/* Assume times() available, but always returns 0
* (also assumes ftime() available)
*/
{
struct timeb tb;

if (times(tms) == (clock_t) -1)
return (clock_t) -1;
ftime(&tb);
if (base_sec == 0)
base_sec = tb.time;
rv = (tb.time - base_sec) * CLK_TCK;
rv += tb.millitm * CLK_TCK / 1000;
}
# endif /* HAVE_GETRUSAGE */
return rv;
}
#endif /* TIMES_BROKEN */

#ifdef OPENDIR_DOES_NONDIR
/* Prevent opendir() from attempting to open non-directories. Such
* behavior can cause problems if it attempts to open special devices...
*/
DIR *
ksh_opendir(d)
char *d;
{
struct stat statb;

if (stat(d, &statb) != 0)
return (DIR *) 0;
if (!S_ISDIR(statb.st_mode)) {
errno = ENOTDIR;
return (DIR *) 0;
}
return opendir(d);
}
#endif /* OPENDIR_DOES_NONDIR */
pdksh-5.1.2/path.c100644 0 133 12047 5667133002 12575 0ustar rootlsource#if !defined(lint) && !defined(no_RCSids)
static char *RCSid =
"$Id: path.c,v 1.2 1994/05/19 18:32:40 michael Exp michael $";
#endif
#include "sh.h"

/*
* Contains a routine to search a : seperated list of
* paths (a la CDPATH) and make appropiate file names.
* Also contains a routine to simplify .'s and ..'s out of
* a path name.
*
* Larry Bouzane ([email protected])
*/

/*
* $Log: path.c,v $
* Revision 1.2 1994/05/19 18:32:40 michael
* Merge complete, stdio replaced, various fixes. (pre autoconf)
*
* Revision 1.1 1994/04/06 13:14:03 michael
* Initial revision
*
* Revision 4.2 1990/12/06 18:05:24 larry
* Updated test code to reflect parameter change.
* Fixed problem with /a/./.dir being simplified to /a and not /a/.dir due
* to *(cur+2) == *f test instead of the correct cur+2 == f
*
* Revision 4.1 90/10/29 14:42:19 larry
* base MUN version
*
* Revision 3.1.0.4 89/02/16 20:28:36 larry
* Forgot to set *pathlist to NULL when last changed make_path().
*
* Revision 3.1.0.3 89/02/13 20:29:55 larry
* Fixed up cd so that it knew when a node from CDPATH was used and would
* print a message only when really necessary.
*
* Revision 3.1.0.2 89/02/13 17:51:22 larry
* Merged with Eric Gisin's version.
*
* Revision 3.1.0.1 89/02/13 17:50:58 larry
* *** empty log message ***
*
* Revision 3.1 89/02/13 17:49:28 larry
* *** empty log message ***
*
*/

/*
* Makes a filename into result using the following algorithm.
* - make result NULL
* - if file starts with '/', append file to result & set pathlist to NULL
* - if file starts with ./ or ../ append curpath and file to result
* and set pathlist to NULL
* - if the first element of pathlist doesnt start with a '/' xx or '.' xx
* then curpath is appended to result.
* - the first element of pathlist is appended to result
* - file is appended to result
* - pathlist is set to the start of the next element in pathlist (or NULL
* if there are no more elements.
* The return value indicates whether a non-null element from pathlist
* was appened to result.
*/
int
make_path(curpath, file, pathlist, result, len)
char *curpath;
char *file;
char **pathlist; /* & of : seperated list (a la PATH/CDPATH) */
char *result;
int len; /* size of result */
{
int rval = 0;
int use_pathlist = 1;
char c;
char *plist;
char *result_end = result + len - 1; /* save room for null byte */

if (file == (char *) 0)
file = null;

if (ISDIRSEP(*file)
#ifdef OS2
|| (isalpha(*file) && file[1] == ':')
#endif /* OS2 */
)
{
while (result < result_end && *file != '\0')
*result++ = *file++;
*result = '\0';
*pathlist = (char *) 0;
return 0;
}

if (*file == '.') {
if ((c = *(file+1)) == '.') {
c = *(file+2);
if (ISDIRSEP(c) || c == '\0')
use_pathlist = 0;
} else if (ISDIRSEP(c) || c == '\0')
use_pathlist = 0;
}

plist = *pathlist;
if (plist == (char *) 0)
plist = null;

if (use_pathlist == 0 || !ISDIRSEP(*plist)) {
if (curpath != (char *) 0 && *curpath != '\0') {
while (result < result_end && *curpath != '\0')
*result++ = *curpath++;
if (result < result_end && !ISDIRSEP(curpath[-1]))
*result++ = DIRSEP;
}
}
if (use_pathlist) {
if (*plist == PATHSEP)
plist++;
else if (*plist != '\0') {
rval = 1;
while (*plist != '\0' && *plist != PATHSEP) {
if (result < result_end)
*result++ = *plist;
plist++;
}
if (result < result_end && !ISDIRSEP(curpath[-1]))
*result++ = DIRSEP;
if (plist[0] == PATHSEP && plist[1] != '\0')
plist++;
}
}
while (result < result_end && *file != '\0')
*result++ = *file++;

*result = '\0';
*pathlist = (use_pathlist && *plist != '\0') ? plist : (char *) 0;
return rval;
}

/*
* Simplify pathnames containing "." and ".." entries.
* ie, path_simplify("/a/b/c/./../d/..") returns "/a/b"
*/
void
simplify_path(path)
char *path;
{
char *f;
char *cur = path;
char *t = path + 1;

/* Only simplify absolute paths */
if (!ISDIRSEP(*cur))
return;

while (1) {
/* treat multiple '/'s as one '/' */
while (ISDIRSEP(*t))
t++;

if (*t == '\0') {
if (cur != path)
*cur = '\0';
else
*(cur+1) = '\0';
break;
}

/* find/copy next component of pathname */
*cur = DIRSEP;
f = cur + 1;
while (*t != '\0' && !ISDIRSEP(*t))
*f++ = *t++;

/* check for a ". or ".." entry and simplify */
if (*(cur+1) == '.') {
if (*(cur+2) == '.' && (cur+3) == f) {
if (cur != path) {
cur -= 2;
while (!ISDIRSEP(*cur))
cur--;
}
} else if ((cur+2) != f)
cur = f;
} else
cur = f;
}
}

#ifdef TEST
void simplify_path();
int make_path();

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 */
pdksh-5.1.2/shf.c100644 0 133 61470 5666702664 12444 0ustar rootlsource/*
* Shell file I/O routines
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: shf.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"
#include "ksh_limval.h"


/* flags to shf_emptybuf() */
#define EB_READSW 0x01 /* about to switch to reading */
#define EB_GROW 0x02 /* grow buffer if necessary (STRING+DYNAMIC) */

/*
* Replacement stdio routines. Stdio is too flakey on too many machines
* to be useful when you have multiple processes using the same underlying
* file descriptors.
*/

static int shf_fillbuf ARGS((struct shf *shf));
static int shf_emptybuf ARGS((struct shf *shf, int flags));

/* Open a file. First three args are for open(), last arg is flags for
* this package. Returns NULL if file could not be opened, or if a dup
* fails.
*/
struct shf *
shf_open(name, oflags, mode, sflags)
char *name;
int oflags;
int mode;
int sflags;
{
int fd;

fd = open(name, oflags, mode);
if (fd < 0)
return NULL;
if ((sflags & SHF_MAPHI) && fd < FDBASE) {
int nfd;

nfd = ksh_dupbase(fd, FDBASE);
close(fd);
if (nfd < 0)
return NULL;
fd = nfd;
}
sflags &= ~SHF_ACCMODE;
sflags |= (oflags & O_ACCMODE) == O_RDONLY ? SHF_RD
: ((oflags & O_ACCMODE) == O_WRONLY ? SHF_WR
: SHF_RDWR);

return shf_fdopen(fd, sflags, (struct shf *) 0);
}

/* Set up the shf structure for a file descriptor. Doesn't fail. */
struct shf *
shf_fdopen(fd, sflags, shf)
int fd;
int sflags;
struct shf *shf;
{
int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;

/* use fcntl() to figure out correct read/write flags */
if (sflags & SHF_GETFL) {
int flags = fcntl(fd, F_GETFL, 0);

if (flags < 0)
/* will get an error on first read/write */
sflags |= SHF_RDWR;
else
switch (flags & O_ACCMODE) {
case O_RDONLY: sflags |= SHF_RD; break;
case O_WRONLY: sflags |= SHF_WR; break;
case O_RDWR: sflags |= SHF_RDWR; break;
}
}

if (!(sflags & (SHF_RD | SHF_WR)))
internal_errorf(1, "shf_fdopen: missing read/write");

if (shf) {
if (bsize) {
shf->buf = (unsigned char *) alloc(bsize, ATEMP);
sflags |= SHF_ALLOCB;
} else
shf->buf = (unsigned char *) 0;
} else {
shf = (struct shf *) alloc(sizeof(struct shf) + bsize, ATEMP);
shf->buf = (unsigned char *) &shf[1];
sflags |= SHF_ALLOCS;
}
shf->areap = ATEMP;
shf->fd = fd;
shf->rp = shf->wp = shf->buf;
shf->rnleft = 0;
shf->rbsize = bsize;
shf->wnleft = 0; /* force call to shf_emptybuf() */
shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
shf->flags = sflags;
shf->errno_ = 0;
shf->bsize = bsize;
if (sflags & SHF_CLEXEC)
fd_clexec(fd);
return shf;
}

/* Set up an existing shf (and buffer) to use the given fd */
struct shf *
shf_reopen(fd, sflags, shf)
int fd;
int sflags;
struct shf *shf;
{
int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;

/* use fcntl() to figure out correct read/write flags */
if (sflags & SHF_GETFL) {
int flags = fcntl(fd, F_GETFL, 0);

if (flags < 0)
/* will get an error on first read/write */
sflags |= SHF_RDWR;
else
switch (flags & O_ACCMODE) {
case O_RDONLY: sflags |= SHF_RD; break;
case O_WRONLY: sflags |= SHF_WR; break;
case O_RDWR: sflags |= SHF_RDWR; break;
}
}

if (!(sflags & (SHF_RD | SHF_WR)))
internal_errorf(1, "shf_reopen: missing read/write");
if (!shf || !shf->buf || shf->bsize < bsize)
internal_errorf(1, "shf_reopen: bad shf/buf/bsize");

/* assumes shf->buf and shf->bsize already set up */
shf->fd = fd;
shf->rp = shf->wp = shf->buf;
shf->rnleft = 0;
shf->rbsize = bsize;
shf->wnleft = 0; /* force call to shf_emptybuf() */
shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
shf->flags = (shf->flags & (SHF_ALLOCS | SHF_ALLOCB)) | sflags;
shf->errno_ = 0;
if (sflags & SHF_CLEXEC)
fd_clexec(fd);
return shf;
}

/* Open a string for reading or writing. If reading, bsize is the number
* of bytes that can be read. If writing, bsize is the maximum number of
* bytes that can be written. If shf is not null, it is filled in and
* returned, if it is null, shf is allocated. If writing and buf is null
* and SHF_DYNAMIC is set, the buffer is allocated (if bsize > 0, it is
* used for the initial size). Doesn't fail.
* When writing, a byte is reserved for a trailing null - see shf_sclose().
*/
struct shf *
shf_sopen(buf, bsize, sflags, shf)
char *buf;
int bsize;
int sflags;
struct shf *shf;
{
/* can't have a read+write string */
if (!(sflags & (SHF_RD | SHF_WR))
|| (sflags & (SHF_RD | SHF_WR)) == (SHF_RD | SHF_WR))
internal_errorf(1, "shf_sopen: flags 0x%x", sflags);

if (!shf) {
shf = (struct shf *) alloc(sizeof(struct shf), ATEMP);
sflags |= SHF_ALLOCS;
}
shf->areap = ATEMP;
if (!buf && (sflags & SHF_WR) && (sflags & SHF_DYNAMIC)) {
if (bsize <= 0)
bsize = 64;
sflags |= SHF_ALLOCB;
buf = alloc(bsize, shf->areap);
}
shf->fd = -1;
shf->buf = shf->rp = shf->wp = (unsigned char *) buf;
shf->rnleft = bsize;
shf->rbsize = bsize;
shf->wnleft = bsize - 1; /* space for a '\0' */
shf->wbsize = bsize;
shf->flags = sflags | SHF_STRING;
shf->errno_ = 0;
shf->bsize = bsize;

return shf;
}

/* Flush and close file descriptor, free the shf structure */
int
shf_close(shf)
struct shf *shf;
{
int ret = 0;

if (shf->fd >= 0) {
ret = shf_flush(shf);
if (close(shf->fd) < 0)
ret = EOF;
}
if (shf->flags & SHF_ALLOCS)
afree(shf, shf->areap);
else if (shf->flags & SHF_ALLOCB)
afree(shf->buf, shf->areap);

return ret;
}

/* Flush and close file descriptor, don't free file structure */
int
shf_fdclose(shf)
struct shf *shf;
{
int ret = 0;

if (shf->fd >= 0) {
ret = shf_flush(shf);
if (close(shf->fd) < 0)
ret = EOF;
shf->rnleft = 0;
shf->rp = shf->buf;
shf->wnleft = 0;
shf->fd = -1;
}

return ret;
}

/* Close a string - if it was opened for writing, it is null terminated;
* returns a pointer to the string and frees shf if it was allocated
* (does not free string if it was allocated).
*/
char *
shf_sclose(shf)
struct shf *shf;
{
unsigned char *s = shf->buf;

/* null terminate */
if (shf->flags & SHF_WR) {
shf->wnleft++;
shf_putc('\0', shf);
}
if (shf->flags & SHF_ALLOCS)
afree(shf, shf->areap);
return (char *) s;
}

/* Flush and free file structure, don't close file descriptor */
int
shf_finish(shf)
struct shf *shf;
{
int ret = 0;

if (shf->fd >= 0)
ret = shf_flush(shf);
if (shf->flags & SHF_ALLOCS)
afree(shf, shf->areap);
else if (shf->flags & SHF_ALLOCB)
afree(shf->buf, shf->areap);

return ret;
}

/* Un-read what has been read but not examined, or write what has been
* buffered. Returns 0 for success, EOF for (write) error.
*/
int
shf_flush(shf)
struct shf *shf;
{
if (shf->flags & SHF_STRING)
return (shf->flags & SHF_WR) ? EOF : 0;

if (shf->fd < 0)
internal_errorf(1, "shf_flush: no fd");

if (shf->flags & SHF_ERROR) {
errno = shf->errno_;
return EOF;
}

if (shf->flags & SHF_READING) {
shf->flags &= ~(SHF_EOF | SHF_READING);
if (shf->rnleft > 0) {
lseek(shf->fd, (off_t) -shf->rnleft, 1);
shf->rnleft = 0;
shf->rp = shf->buf;
}
return 0;
} else if (shf->flags & SHF_WRITING)
return shf_emptybuf(shf, 0);

return 0;
}

/* Write out any buffered data. If currently reading, flushes the read
* buffer. Returns 0 for success, EOF for (write) error.
*/
static int
shf_emptybuf(shf, flags)
struct shf *shf;
int flags;
{
int ret = 0;

if (!(shf->flags & SHF_STRING) && shf->fd < 0)
internal_errorf(1, "shf_emptybuf: no fd");

if (shf->flags & SHF_ERROR) {
errno = shf->errno_;
return EOF;
}

if (shf->flags & SHF_READING) {
if (flags & EB_READSW) /* doesn't happen */
return 0;
ret = shf_flush(shf);
shf->flags &= ~SHF_READING;
}
if (shf->flags & SHF_STRING) {
unsigned char *nbuf;

/* Note that we assume SHF_ALLOCS is not set if SHF_ALLOCB
* is set... (changing the shf pointer could cause problems)
*/
if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC)
|| !(shf->flags & SHF_ALLOCB))
return EOF;
/* allocate more space for buffer */
nbuf = (unsigned char *) aresize(shf->buf, shf->wbsize * 2,
shf->areap);
shf->rp = nbuf + (shf->rp - shf->buf);
shf->wp = nbuf + (shf->wp - shf->buf);
shf->rbsize += shf->wbsize;
shf->wbsize += shf->wbsize;
shf->wnleft += shf->wbsize;
shf->wbsize *= 2;
shf->buf = nbuf;
} else {
if (shf->flags & SHF_WRITING) {
int ntowrite = shf->wp - shf->buf;
unsigned char *buf = shf->buf;
int n;

while (ntowrite > 0) {
n = write(shf->fd, buf, ntowrite);
if (n < 0) {
if (errno == EINTR
&& !(shf->flags & SHF_INTERRUPT))
continue;
shf->flags |= SHF_ERROR;
shf->errno_ = errno;
shf->wnleft = 0;
if (buf != shf->buf) {
/* allow a second flush
* to work */
memmove(shf->buf, buf,
ntowrite);
shf->wp = shf->buf + ntowrite;
}
return EOF;
}
buf += n;
ntowrite -= n;
}
if (flags & EB_READSW) {
shf->wp = shf->buf;
shf->wnleft = 0;
shf->flags &= ~SHF_WRITING;
return 0;
}
}
shf->wp = shf->buf;
shf->wnleft = shf->wbsize;
}
shf->flags |= SHF_WRITING;

return ret;
}

/* Fill up a read buffer. Returns EOF for a read error, 0 otherwise. */
static int
shf_fillbuf(shf)
struct shf *shf;
{
if (shf->flags & SHF_STRING)
return 0;

if (shf->fd < 0)
internal_errorf(1, "shf_fillbuf: no fd");

if (shf->flags & (SHF_EOF | SHF_ERROR)) {
if (shf->flags & SHF_ERROR)
errno = shf->errno_;
return EOF;
}

if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
return EOF;

shf->flags |= SHF_READING;

shf->rp = shf->buf;
while (1) {
shf->rnleft = read(shf->fd, shf->buf, shf->rbsize);
if (shf->rnleft < 0 && errno == EINTR
&& !(shf->flags & SHF_INTERRUPT))
continue;
break;
}
if (shf->rnleft <= 0) {
if (shf->rnleft < 0) {
shf->flags |= SHF_ERROR;
shf->errno_ = errno;
shf->rnleft = 0;
shf->rp = shf->buf;
return EOF;
}
shf->flags |= SHF_EOF;
}
return 0;
}

/* Read a buffer from shf. Returns the number of bytes read into buf,
* if no bytes were read, returns 0 if end of file was seen, EOF if
* a read error occurred.
*/
int
shf_read(buf, bsize, shf)
char *buf;
int bsize;
struct shf *shf;
{
int orig_bsize = bsize;
int ncopy;

if (!(shf->flags & SHF_RD))
internal_errorf(1, "shf_read: flags %x", shf->flags);

if (bsize <= 0)
internal_errorf(1, "shf_read: bsize %d", bsize);

while (bsize > 0) {
if (shf->rnleft == 0
&& (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
break;
ncopy = shf->rnleft;
if (ncopy > bsize)
ncopy = bsize;
memcpy(buf, shf->rp, ncopy);
buf += ncopy;
bsize -= ncopy;
shf->rp += ncopy;
shf->rnleft -= ncopy;
}
/* Note: fread(3S) returns 0 for errors - this doesn't */
return orig_bsize == bsize ? (shf_error(shf) ? EOF : 0)
: orig_bsize - bsize;
}

/* Read up to a newline or EOF. The newline is put in buf; buf is always
* null terminated. Returns NULL on read error or if nothing was read before
* end of file, returns a pointer to the null byte in buf otherwise.
*/
char *
shf_getse(buf, bsize, shf)
char *buf;
int bsize;
struct shf *shf;
{
unsigned char *end;
int ncopy;
char *orig_buf = buf;

if (!(shf->flags & SHF_RD))
internal_errorf(1, "shf_getse: flags %x", shf->flags);

if (bsize <= 0)
return (char *) 0;

--bsize; /* save room for null */
do {
if (shf->rnleft == 0) {
if (shf_fillbuf(shf) == EOF)
return NULL;
if (shf->rnleft == 0) {
*buf = '\0';
return buf == orig_buf ? NULL : buf;
}
}
end = (unsigned char *) memchr((char *) shf->rp, '\n',
shf->rnleft);
ncopy = end ? end - shf->rp + 1 : shf->rnleft;
if (ncopy > bsize)
ncopy = bsize;
memcpy(buf, (char *) shf->rp, ncopy);
shf->rp += ncopy;
shf->rnleft -= ncopy;
buf += ncopy;
bsize -= ncopy;
} while (!end && bsize);
*buf = '\0';
return buf;
}

/* Returns the char read. Returns EOF for error and end of file. */
int
shf_getchar(shf)
struct shf *shf;
{
if (!(shf->flags & SHF_RD))
internal_errorf(1, "shf_getchar: flags %x", shf->flags);

if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
return EOF;
--shf->rnleft;
return *shf->rp++;
}

/* Put a character back in the input stream. Returns the character if
* successful, EOF if there is no room.
*/
int
shf_ungetc(c, shf)
int c;
struct shf *shf;
{
if (!(shf->flags & SHF_RD))
internal_errorf(1, "shf_ungetc: flags %x", shf->flags);

if ((shf->flags & SHF_ERROR) || c == EOF
|| (shf->rp == shf->buf && shf->rnleft))
return EOF;

if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
return EOF;

if (shf->rp == shf->buf)
shf->rp = shf->buf + shf->rbsize;
if (shf->flags & SHF_STRING) {
/* Can unget what was read, but not something different - we
* don't want to modify a string.
*/
if (shf->rp[-1] != c)
return EOF;
shf->flags &= ~SHF_EOF;
shf->rp--;
shf->rnleft++;
return c;
}
shf->flags &= ~SHF_EOF;
*--(shf->rp) = c;
shf->rnleft++;
return c;
}

/* Write a character. Returns the character if successful, EOF if
* the char could not be written.
*/
int
shf_putchar(c, shf)
int c;
struct shf *shf;
{
if (!(shf->flags & SHF_WR))
internal_errorf(1, "shf_putchar: flags %x", shf->flags);

if (c == EOF)
return EOF;

if (shf->flags & SHF_UNBUF) {
char cc = c;
int n;

if (shf->fd < 0)
internal_errorf(1, "shf_putchar: no fd");
if (shf->flags & SHF_ERROR) {
errno = shf->errno_;
return EOF;
}
while ((n = write(shf->fd, &cc, 1)) != 1)
if (n < 0) {
if (errno == EINTR
&& !(shf->flags & SHF_INTERRUPT))
continue;
shf->flags |= SHF_ERROR;
shf->errno_ = errno;
return EOF;
}
} else {
/* Flush deals with strings and sticky errors */
if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == EOF)
return EOF;
shf->wnleft--;
*shf->wp++ = c;
}

return c;
}

/* Write a buffer. Returns nbytes if successful, EOF if there is an error. */
int
shf_write(buf, nbytes, shf)
char *buf;
int nbytes;
struct shf *shf;
{
int orig_nbytes = nbytes;
int n;
int ncopy;

if (!(shf->flags & SHF_WR))
internal_errorf(1, "shf_write: flags %x", shf->flags);

if (nbytes < 0)
internal_errorf(1, "shf_write: nbytes %d", nbytes);

if ((ncopy = shf->wnleft)) {
if (ncopy > nbytes)
ncopy = nbytes;
memcpy(shf->wp, buf, ncopy);
nbytes -= ncopy;
buf += ncopy;
shf->wp += ncopy;
shf->wnleft -= ncopy;
}
if (nbytes > 0) {
/* Flush deals with strings and sticky errors */
if (shf_emptybuf(shf, EB_GROW) == EOF)
return EOF;
if (nbytes > shf->wbsize) {
ncopy = nbytes;
if (shf->wbsize)
ncopy -= nbytes % shf->wbsize;
nbytes -= ncopy;
while (ncopy > 0) {
n = write(shf->fd, buf, ncopy);
if (n < 0) {
if (errno == EINTR
&& !(shf->flags & SHF_INTERRUPT))
continue;
shf->flags |= SHF_ERROR;
shf->errno_ = errno;
shf->wnleft = 0;
/* Note: fwrite(3S) returns 0 for
* errors - this doesn't */
return EOF;
}
buf += n;
ncopy -= n;
}
}
if (nbytes > 0) {
memcpy(shf->wp, buf, nbytes);
shf->wp += nbytes;
shf->wnleft -= nbytes;
}
}

return orig_nbytes;
}

int
#ifdef HAVE_PROTOTYPES
shf_fprintf(struct shf *shf, const char *fmt, ...)
#else
shf_fprintf(shf, fmt, va_alist)
struct shf *shf;
const char *fmt;
va_dcl
#endif
{
va_list args;
int n;

SH_VA_START(args, fmt);
n = shf_vfprintf(shf, fmt, args);
va_end(args);

return n;
}

int
#ifdef HAVE_PROTOTYPES
shf_snprintf(char *buf, int bsize, char *fmt, ...)
#else
shf_snprintf(buf, bsize, fmt, va_alist)
char *buf;
int bsize;
char *fmt;
va_dcl
#endif
{
struct shf shf;
va_list args;
int n;

if (!buf || bsize <= 0)
internal_errorf(1, "shf_sprintf: buf %lx, bsize %d",
(long) buf, bsize);

shf_sopen(buf, bsize, SHF_WR, &shf);
SH_VA_START(args, fmt);
n = shf_vfprintf(&shf, fmt, args);
va_end(args);
shf_sclose(&shf); /* null terminates */
return n;
}

#undef FP /* if you want floating point stuff */

#define BUF_SIZE 128
#define FPBUF_SIZE (DMAXEXP+16)/* this must be >
* MAX(DMAXEXP, log10(pow(2, DSIGNIF)))
* + ceil(log10(DMAXEXP)) + 8 (I think).
* Since this is hard to express as a
* constant, just use a large buffer.
*/

/*
* What kinda of machine we on? Hopefully the C compiler will optimize
* this out...
*
* For shorts, we want sign extend for %d but not for %[oxu] - on 16 bit
* machines it don't matter. Assmumes C compiler has converted shorts to
* ints before pushing them.
*/
#define POP_INT(f, s, a) (((f) & FL_LONG) ? \
va_arg((a), unsigned long) \
: \
(sizeof(int) < sizeof(long) ? \
((s) ? \
(long) va_arg((a), int) \
: \
va_arg((a), unsigned)) \
: \
va_arg((a), unsigned)))

#define ABIGNUM 32000 /* big numer that will fit in a short */
#define LOG2_10 3.321928094887362347870319429 /* log base 2 of 10 */

#define FL_HASH 0x001 /* `#' seen */
#define FL_PLUS 0x002 /* `+' seen */
#define FL_RIGHT 0x004 /* `-' seen */
#define FL_BLANK 0x008 /* ` ' seen */
#define FL_SHORT 0x010 /* `h' seen */
#define FL_LONG 0x020 /* `l' seen */
#define FL_ZERO 0x040 /* `0' seen */
#define FL_DOT 0x080 /* '.' seen */
#define FL_UPPER 0x100 /* format character was uppercase */
#define FL_NUMBER 0x200 /* a number was formated %[douxefg] */


#ifdef FP
static double
my_ceil(d)
double d;
{
extern double modf();
double i;

return d - modf(d, &i) + (d < 0 ? -1 : 1);
}
#endif /* FP */

int
shf_vfprintf(shf, fmt, args)
struct shf *shf;
char *fmt;
va_list args;
{
char c, *s, *p;
int UNINITIALIZED(tmp);
int field, precision;
int len;
int flags;
unsigned long lnum;
/* %#o produces the longest output */
char numbuf[(BITS(long) + 2) / 3 + 1];
/* this stuff for dealing with the buffer */
int nwritten = 0;
#ifdef FP
extern double frexp();
extern char *ecvt();

double fpnum;
int exp, decpt;
char style;
char fpbuf[FPBUF_SIZE];
#endif /* FP */

if (!fmt)
return 0;

while ((c = *fmt++)) {
if (c != '%') {
shf_putc(c, shf);
nwritten++;
continue;
}
/*
* This will accept flags/fields in any order - not
* just the order specified in printf(3), but this is
* the way _doprnt() seems to work (on bsd and sysV).
* The only resriction is that the format character must
* come last :-).
*/
flags = field = precision = 0;
for ( ; (c = *fmt++) ; ) {
switch (c) {
case '#':
flags |= FL_HASH;
continue;

case '+':
flags |= FL_PLUS;
continue;

case '-':
flags |= FL_RIGHT;
continue;

case ' ':
flags |= FL_BLANK;
continue;

case '0':
if (!(flags & FL_DOT))
flags |= FL_ZERO;
continue;

case '.':
flags |= FL_DOT;
precision = 0;
continue;

case '*':
tmp = va_arg(args, int);
if (flags & FL_DOT)
precision = tmp;
else if ((field = tmp) < 0) {
field = -field;
flags |= FL_RIGHT;
}
continue;

case 'l':
flags |= FL_LONG;
continue;

case 'h':
flags |= FL_SHORT;
continue;
}
if (digit(c)) {
tmp = c - '0';
while (c = *fmt++, digit(c))
tmp = tmp * 10 + c - '0';
--fmt;
if (tmp < 0) /* overflow? */
tmp = 0;
if (flags & FL_DOT)
precision = tmp;
else
field = tmp;
continue;
}
break;
}

if (precision < 0)
precision = 0;

if (!c) /* nasty format */
break;

if (c >= 'A' && c <= 'Z') {
flags |= FL_UPPER;
c = c - 'A' + 'a';
}

switch (c) {
case 'p': /* pointer */
flags &= ~(FL_LONG | FL_SHORT);
if (sizeof(char *) > sizeof(int))
flags |= FL_LONG; /* hope it fits.. */
/* aaahhh... */
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
flags |= FL_NUMBER;
s = &numbuf[sizeof(numbuf)];
lnum = POP_INT(flags, c == 'd', args);
switch (c) {
case 'd':
case 'i':
if (0 > (long) lnum)
lnum = - (long) lnum, tmp = 1;
else
tmp = 0;
/* aaahhhh..... */

case 'u':
do {
*--s = lnum % 10 + '0';
lnum /= 10;
} while (lnum);

if (c != 'u') {
if (tmp)
*--s = '-';
else if (flags & FL_PLUS)
*--s = '+';
else if (flags & FL_BLANK)
*--s = ' ';
}
break;

case 'o':
do {
*--s = (lnum & 0x7) + '0';
lnum >>= 3;
} while (lnum);

if ((flags & FL_HASH) && *s != '0')
*--s = '0';
break;

case 'p':
case 'x':
p = (flags & FL_UPPER) ? "0123456789ABCDEF" :
"0123456789abcdef";
do {
*--s = p[lnum & 0xf];
lnum >>= 4;
} while (lnum);

if (flags & FL_HASH) {
*--s = (flags & FL_UPPER) ? 'X' : 'x';
*--s = '0';
}
}
len = &numbuf[sizeof(numbuf)] - s;
if (flags & FL_DOT) {
if (precision > len) {
field = precision;
flags |= FL_ZERO;
} else
precision = len; /* no loss */
}
break;

#ifdef FP
case 'e':
case 'g':
case 'f':
/*
* This could proabably be done better,
* but it seems to work. Note that gcvt()
* is not used, as you cannot tell it to
* not strip the zeros.
*/
flags |= FL_NUMBER;
if (!(flags & FL_DOT))
precision = 6; /* default */
/*
* Assumes doubles are pushed on
* the stack. If this is not so, then
* FL_LONG/FL_SHORT should be checked.
*/
fpnum = va_arg(args, double);
s = fpbuf;
style = c;
/*
* This is the same as
* exp = ceil(log10(fpnum))
* but doesn't need -lm. This is an
* aproximation as exp is rounded up.
*/
(void) frexp(fpnum, &exp);
exp = my_ceil(exp / LOG2_10);

if (exp < 0)
exp = 0;

p = ecvt(fpnum, precision + 1 + exp,
&decpt, &tmp);
if (c == 'g') {
if (decpt < -4 || decpt > precision)
style = 'e';
else
style = 'f';
if (decpt > 0 && (precision -= decpt) < 0)
precision = 0;
}
if (tmp)
*--s = '-';
else if (flags & FL_PLUS)
*--s = '+';
else if (flags & FL_BLANK)
*--s = ' ';

if (style == 'e')
*s++ = *p++;
else {
if (decpt > 0) {
/* Overflow check - should
* never have this problem.
*/
if (decpt >
&fpbuf[sizeof(fpbuf)]
- s - 8)
decpt =
&fpbuf[sizeof(fpbuf)]
- s - 8;
(void) memcpy(s, p, decpt);
s += decpt;
p += decpt;
} else
*s++ = '0';
}

/* print the fraction? */
if (precision > 0) {
*s++ = '.';
/* Overflow check - should
* never have this problem.
*/
if (precision > &fpbuf[sizeof(fpbuf)]
- s - 7)
precision =
&fpbuf[sizeof(fpbuf)]
- s - 7;
for (tmp = decpt; tmp++ < 0 &&
precision > 0 ; precision--)
*s++ = '0';
tmp = strlen(p);
if (precision > tmp)
precision = tmp;
/* Overflow check - should
* never have this problem.
*/
if (precision > &fpbuf[sizeof(fpbuf)]
- s - 7)
precision =
&fpbuf[sizeof(fpbuf)]
- s - 7;
(void) memcpy(s, p, precision);
s += precision;
/*
* `g' format strips trailing
* zeros after the decimal.
*/
if (c == 'g' && !(flags & FL_HASH)) {
while (*--s == '0')
;
if (*s != '.')
s++;
}
} else if (flags & FL_HASH)
*s++ = '.';

if (style == 'e') {
*s++ = (flags & FL_UPPER) ? 'E' : 'e';
if (--decpt >= 0)
*s++ = '+';
else {
*s++ = '-';
decpt = -decpt;
}
p = &numbuf[sizeof(numbuf)];
for (tmp = 0; tmp < 2 || decpt ; tmp++) {
*--p = '0' + decpt % 10;
decpt /= 10;
}
tmp = &numbuf[sizeof(numbuf)] - p;
(void) memcpy(s, p, tmp);
s += tmp;
}

len = s - fpbuf;
s = fpbuf;
precision = len;
break;
#endif /* FP */

case 's':
if (!(s = va_arg(args, char *)))
s = "(null %s)";
len = strlen(s);
break;

case 'c':
flags &= ~FL_DOT;
numbuf[0] = va_arg(args, int);
s = numbuf;
len = 1;
break;

case '%':
default:
numbuf[0] = c;
s = numbuf;
len = 1;
break;
}

/*
* At this point s should point to a string that is
* to be formatted, and len should be the length of the
* string.
*/
if (!(flags & FL_DOT) || len < precision)
precision = len;
if (field > precision) {
field -= precision;
if (!(flags & FL_RIGHT)) {
field = -field;
/* skip past sign or 0x when padding with 0 */
if ((flags & FL_ZERO) && (flags & FL_NUMBER)) {
if (*s == '+' || *s == '-' || *s ==' ')
{
shf_putc(*s, shf);
s++;
precision--;
nwritten++;
} else if (*s == '0') {
shf_putc(*s, shf);
s++;
nwritten++;
if (--precision > 0 &&
(*s | 0x20) == 'x')
{
shf_putc(*s, shf);
s++;
precision--;
nwritten++;
}
}
c = '0';
} else
c = flags & FL_ZERO ? '0' : ' ';
if (field < 0) {
nwritten += -field;
for ( ; field < 0 ; field++)
shf_putc(c, shf);
}
} else
c = ' ';
} else
field = 0;

if (precision > 0) {
nwritten += precision;
for ( ; precision-- > 0 ; s++)
shf_putc(*s, shf);
}
if (field > 0) {
nwritten += field;
for ( ; field > 0 ; --field)
shf_putc(c, shf);
}
}

return shf_error(shf) ? EOF : nwritten;
}
pdksh-5.1.2/sigact.c100644 0 133 25451 5627367631 13133 0ustar rootlsource/* NAME:
* sigact.c - fake sigaction(2)
*
* SYNOPSIS:
* #include "sigact.h"
*
* int sigaction(int sig, struct sigaction *act,
* struct sigaction *oact);
* int sigaddset(sigset_t *mask, int sig);
* int sigdelset(sigset_t *mask, int sig);
* int sigemptyset(sigset_t *mask);
* int sigfillset(sigset_t *mask);
* int sigismember(sigset_t *mask, int sig);
* int sigpending(sigset_t *set);
* int sigprocmask(int how, sigset_t *set, sigset_t *oset);
* int sigsuspend(sigset_t *mask);
*
* RETSIGTYPE (*Signal(int sig, RETSIGTYPE (*disp)(int)))(int);
*
* DESCRIPTION:
* This is a fake sigaction implementation. It uses
* sigsetmask(2) et al or sigset(2) and friends if
* available, otherwise it just uses signal(2). If it
* thinks sigaction(2) really exists it compiles to "almost"
* nothing.
*
* In any case it provides a Signal() function that is
* implemented in terms of sigaction().
* If not using signal(2) as part of the underlying
* implementation (USE_SIGNAL or USE_SIGMASK), and
* NO_SIGNAL is not defined, it also provides a signal()
* function that calls Signal().
*
* The need for all this mucking about is the problems
* caused by mixing various signal handling mechanisms in
* the one process. This module allows for a consistent
* POSIX compliant interface to whatever is actually
* available.
*
* sigaction() allows the caller to examine and/or set the
* action to be associated with a given signal. "act" and
* "oact" are pointers to 'sigaction structs':
*.nf
*
* struct sigaction
* {
* RETSIGTYPE (*sa_handler)();
* sigset_t sa_mask;
* int sa_flags;
* };
*.fi
*
* RETSIGTYPE is normally 'void' in the POSIX implementation
* and for most current systems. On some older UNIX
* systems, signal handlers do not return 'void', so
* this implementation keeps 'sa_handler' inline with the
* hosts normal signal handling conventions.
* 'sa_mask' controls which signals will be blocked while
* the selected signal handler is active. It is not used
* in this implementation.
* 'sa_flags' controls various semantics such as whether
* system calls should be automagically restarted
* (SA_RESTART) etc. It is not used in this
* implementation.
* Either "act" or "oact" may be NULL in which case the
* appropriate operation is skipped.
*
* sigaddset() adds "sig" to the sigset_t pointed to by "mask".
*
* sigdelset() removes "sig" from the sigset_t pointed to
* by "mask".
*
* sigemptyset() makes the sigset_t pointed to by "mask" empty.
*
* sigfillset() makes the sigset_t pointed to by "mask"
* full ie. match all signals.
*
* sigismember() returns true if "sig" is found in "*mask".
*
* sigpending() is supposed to return "set" loaded with the
* set of signals that are blocked and pending for the
* calling process. It does nothing in this impementation.
*
* sigprocmask() is used to examine and/or change the
* signal mask for the calling process. Either "set" or
* "oset" may be NULL in which case the appropriate
* operation is skipped. "how" may be one of SIG_BLOCK,
* SIG_UNBLOCK or SIG_SETMASK. If this package is built
* with USE_SIGNAL, then this routine achieves nothing.
*
* sigsuspend() sets the signal mask to "*mask" and waits
* for a signal to be delivered after which the previous
* mask is restored.
*
*
* RETURN VALUE:
* 0==success, -1==failure
*
* BUGS:
* Since we fake most of this, don't expect fancy usage to
* work.
*
* AUTHOR:
* Simon J. Gerraty
*/
/* COPYRIGHT:
* @(#)Copyright (c) 1992 Simon J. Gerraty
*
* This is free software. It comes with NO WARRANTY.
* Permission to use, modify and distribute this source code
* is granted subject to the following conditions.
* 1/ that that the above copyright notice and this notice
* are preserved in all copies and that due credit be given
* to the author.
* 2/ that any changes to this code are clearly commented
* as such so that the author does get blamed for bugs
* other than his own.
*
* Please send copies of changes and bug-fixes to:
* [email protected]
*
*/
/* Changes to sigact.c for pdksh, Michael Rendell :
* - sigsuspend(): pass *mask to bsd4.2 sigpause instead of mask.
* - changed SIG_HDLR to RETSIGTYPE for use with GNU autoconf
* - include sh.h instead of signal.h (to get *_SIGNALS macros)
* - changed if !SA_NOCLDSTOP ... to USE_FAKE_SIGACT to avoid confusion
* - set the USE_* defines using the *_SIGNALS defines from autoconf
* - sigaction(): if using BSD signals, use sigvec() (used to use
* signal()) and set the SV_INTERRUPT flag (POSIX says syscalls
* are interrupted and pdksh needs this behaviour).
* - define IS_KSH before including anything; ifdef out routines
* not used in ksh if IS_KSH is defined (same in sigact.h).
*/
#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: sigact.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

/*
#include
*/
#define IS_KSH
#include "sh.h"

#ifndef __P
# if defined(__STDC__) || defined(__cplusplus)
# define __P(p) p
# else
# define __P(p) ()
# endif
#endif


/*
* some systems have a faulty sigaction() implementation!
* Allow us to bypass it.
* Or they may have installed sigact.h as signal.h which is why
* we have SA_NOCLDSTOP defined.
*/
#ifdef USE_FAKE_SIGACT /* let autoconf decide.. */
/* #if !defined(SA_NOCLDSTOP) || defined(_SIGACT_H) || defined(USE_SIGNAL) || defined(USE_SIGSET) || defined(USE_SIGMASK) */

/* Let autoconf decide which to use */
#ifdef BSD42_SIGNALS
# define USE_SIGMASK
#else
# ifdef BSD41_SIGNALS
# define USE_SIGSET
# else
# define USE_SIGNAL
# endif
#endif /* BSD42_SIGNALS */

/*
* if we haven't been told,
* try and guess what we should implement with.
*/
#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL)
# if defined(sigmask) || defined(BSD) || defined(_BSD) && !defined(BSD41)
# define USE_SIGMASK
# else
# ifndef NO_SIGSET
# define USE_SIGSET
# else
# define USE_SIGNAL
# endif
# endif
#endif
/*
* if we still don't know, we're in trouble
*/
#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL)
error must know what to implement with
#endif

#include "sigact.h"

/*
* in case signal() has been mapped to our Signal().
*/
#undef signal


int
sigaction(sig, act, oact)
int sig;
struct sigaction *act, *oact;
{
RETSIGTYPE (*oldh)();

if (act)
{
#ifdef USE_SIGSET
oldh = sigset(sig, act->sa_handler);
#else
# ifdef USE_SIGMASK
struct sigvec nsv,osv;

nsv.sv_handler = act->sa_handler;
nsv.sv_mask = 0; /* punt */
nsv.sv_flags = SV_INTERRUPT; /* punt */
sigvec(sig, &nsv, &osv);
oldh = osv.sv_handler;
# else USE_SIGMASK
oldh = signal(sig, act->sa_handler);
# endif /* USE_SIGMASK */
#endif
}
else
{
if (oact)
{
#ifdef USE_SIGSET
oldh = sigset(sig, SIG_IGN);
#else
oldh = signal(sig, SIG_IGN);
#endif
if (oldh != SIG_IGN && oldh != SIG_ERR)
{
#ifdef USE_SIGSET
(void) sigset(sig, oldh);
#else
(void) signal(sig, oldh);
#endif
}
}
}
if (oact)
{
oact->sa_handler = oldh;
}
return 0; /* hey we're faking it */
}


int
sigaddset(mask, sig)
sigset_t *mask;
int sig;
{
*mask |= sigmask(sig);
return 0;
}


#ifndef IS_KSH
int
sigdelset(mask, sig)
sigset_t *mask;
int sig;
{
*mask &= ~(sigmask(sig));
return 0;
}
#endif /* IS_KSH */


int
sigemptyset(mask)
sigset_t *mask;
{
*mask = 0;
return 0;
}


#ifndef IS_KSH
int
sigfillset(mask)
sigset_t *mask;
{
*mask = ~0;
return 0;
}
#endif /* IS_KSH */


#ifndef IS_KSH
int
sigismember(mask, sig)
sigset_t *mask;
int sig;
{
return ((*mask) & sigmask(sig));
}
#endif /* IS_KSH */


#ifndef IS_KSH
int
sigpending(set)
sigset_t *set;
{
return 0; /* faking it! */
}
#endif /* IS_KSH */


int
sigprocmask(how, set, oset)
int how;
sigset_t *set, *oset;
{
#ifdef USE_SIGSET
register int i;
#endif
static sigset_t sm;
static int once = 0;

if (!once)
{
/*
* initally we clear sm,
* there after, it represents the last
* thing we did.
*/
once++;
#ifdef USE_SIGMASK
sm = sigblock(0);
#else
sm = 0;
#endif
}

if (oset)
*oset = sm;
if (set)
{
switch (how)
{
case SIG_BLOCK:
sm |= *set;
break;
case SIG_UNBLOCK:
sm &= ~(*set);
break;
case SIG_SETMASK:
sm = *set;
break;
}
#ifdef USE_SIGMASK
(void) sigsetmask(sm);
#else
# ifdef USE_SIGSET
for (i = 1; i < NSIG; i++)
{
if (how == SIG_UNBLOCK)
{
if (*set & sigmask(i))
sigrelse(i);
}
else
if (sm & sigmask(i))
{
sighold(i);
}
}
# endif
#endif
}
return 0;
}


int
sigsuspend(mask)
sigset_t *mask;
{
#ifdef USE_SIGMASK
sigpause(*mask);
#else
register int i;

# ifdef USE_SIGSET

for (i = 1; i < NSIG; i++)
{
if (*mask & sigmask(i))
{
/* not the same sigpause() as above! */
sigpause(i);
break;
}
}
# else /* signal(2) only */
RETSIGTYPE (*oldh)();

/*
* make sure that signals in mask will not
* be ignored.
*/
for (i = 1; i < NSIG; i++)
{
if (*mask & sigmask(i))
{
if ((oldh = signal(i, SIG_DFL)) != SIG_ERR &&
oldh != SIG_IGN &&
oldh != SIG_DFL)
(void) signal(i, oldh); /* restore handler */
}
}
pause(); /* wait for a signal */
# endif
#endif
return 0;
}

#endif /* USE_FAKE_SIGACT (was ! SA_NOCLDSTOP) */

#if !defined(RETSIGTYPE)
# define RETSIGTYPE void
#endif
#if !defined(SIG_ERR)
# define SIG_ERR (RETSIGTYPE (*)())-1
#endif

/*
* an implementation of signal() using sigaction().
*/

#ifndef IS_KSH
RETSIGTYPE (*Signal(sig, handler)) __P((int))
int sig;
RETSIGTYPE (*handler) __P((int));
{
struct sigaction act, oact;

act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (sigaction(sig, &act, &oact) < 0)
return (SIG_ERR);
return (oact.sa_handler);
}
#endif /* IS_KSH */

#ifndef IS_KSH
#if !defined(USE_SIGNAL) && !defined(USE_SIGMASK) && !defined(NO_SIGNAL)
/*
* ensure we avoid signal mayhem
*/

RETSIGTYPE (*signal(sig, handler)) __P((int))
int sig;
RETSIGTYPE (*handler) __P((int));
{
return (Signal(sig, handler));
}
#endif
#endif /* IS_KSH */

/* This lot (for GNU-Emacs) goes at the end of the file. */
/*
* Local Variables:
* version-control:t
* comment-column:40
* End:
*/
pdksh-5.1.2/syn.c100644 0 133 46340 5667655022 12470 0ustar rootlsource/*
* shell parser (C version)
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: syn.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"

struct multiline_state {
int on; /* set in multiline commands (\n becomes ๐Ÿ˜‰ */
int start_token; /* token multiline is for (eg, FOR, {, etc.) */
int start_line; /* line multiline command started on */
};

static void yyparse ARGS((void));
static struct op *pipeline ARGS((int cf));
static struct op *andor ARGS((void));
static struct op *c_list ARGS((void));
static struct ioword *synio ARGS((int cf));
static void musthave ARGS((int c, int cf));
static struct op *nested ARGS((int type, int smark, int emark));
static struct op *get_command ARGS((int cf));
static struct op *dogroup ARGS((void));
static struct op *thenpart ARGS((void));
static struct op *elsepart ARGS((void));
static struct op *caselist ARGS((void));
static struct op *casepart ARGS((void));
static struct op *function_body ARGS((char *name, int ksh_func));
static char ** wordlist ARGS((void));
static struct op *block ARGS((int type, struct op *t1, struct op *t2,
char **wp));
static struct op *newtp ARGS((int type));
static void syntaxerr ARGS((void)) GCC_FUNC_ATTR(noreturn);
static void multiline_push ARGS((struct multiline_state *save, int tok));
static void multiline_pop ARGS((struct multiline_state *saved));
static int assign_command ARGS((char *s));
#ifdef KSH
static void db_parse ARGS((XPtrV *argsp, XPtrV *varps));
static void db_oaexpr ARGS((XPtrV *argsp, XPtrV *varps));
static void db_nexpr ARGS((XPtrV *argsp, XPtrV *varps));
static void db_primary ARGS((XPtrV *argsp, XPtrV *varps));
#endif /* KSH */

static struct op *outtree; /* yyparse output */

static struct multiline_state multiline; /* \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()
{
ACCEPT;
yynerrs = 0;
if ((tpeek(KEYWORD|ALIAS|VARASN)) == 0) /* EOF */
outtree = newtp(TEOF);
else {
int c;
outtree = c_list();
if ((c = token(0)) != '\n' && c != 0)
syntaxerr();
}
}

static struct op *
pipeline(cf)
int cf;
{
register struct op *t, *p, *tl = NULL;

t = get_command(cf);
if (t != NULL) {
while (token(0) == '|') {
if ((p = get_command(CONTIN)) == NULL)
syntaxerr();
if (tl == NULL)
t = tl = block(TPIPE, t, p, NOWORDS);
else
tl = tl->right = block(TPIPE, tl->right, p, NOWORDS);
}
REJECT;
}
return (t);
}

static struct op *
andor()
{
register struct op *t, *p;
register int c;

t = pipeline(0);
if (t != NULL) {
while ((c = token(0)) == LOGAND || c == LOGOR) {
if ((p = pipeline(CONTIN)) == NULL)
syntaxerr();
t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
}
REJECT;
}
return (t);
}

static struct op *
c_list()
{
register struct op *t, *p, *tl = NULL;
register int c;

t = andor();
if (t != NULL) {
while ((c = token(0)) == ';' || c == '&' || c == COPROC ||
(c == '\n' && (multiline.on || source->type == SSTRING
|| source->type == SALIAS)))
{
if (c == '&' || c == COPROC) {
int type = c == '&' ? TASYNC : TCOPROC;
if (tl)
tl->right = block(type, tl->right,
NOBLOCK, NOWORDS);
else
t = block(type, t, NOBLOCK, NOWORDS);
}
if ((p = andor()) == NULL)
return (t);
if (tl == NULL)
t = tl = block(TLIST, t, p, NOWORDS);
else
tl = tl->right = block(TLIST, tl->right, p, NOWORDS);
}
REJECT;
}
return (t);
}

static struct ioword *
synio(cf)
int cf;
{
register struct ioword *iop;

if (tpeek(cf) != REDIR)
return NULL;
ACCEPT;
iop = yylval.iop;
musthave(LWORD, 0);
iop->name = yylval.cp;
if ((iop->flag&IOTYPE) == IOHERE) {
if (*ident != 0) /* unquoted */
iop->flag |= IOEVAL;
if (herep >= &heres[HERES])
yyerror("too many <<'s\n");
*herep++ = iop;
}
return iop;
}

static void
musthave(c, cf)
int c, cf;
{
if ((token(cf)) != c)
syntaxerr();
}

static struct op *
nested(type, smark, emark)
int type, smark, emark;
{
register struct op *t;
struct multiline_state old_multiline;

multiline_push(&old_multiline, smark);
t = c_list();
musthave(emark, KEYWORD|ALIAS);
multiline_pop(&old_multiline);
return (block(type, t, NOBLOCK, NOWORDS));
}

static struct op *
get_command(cf)
int cf;
{
register struct op *t;
register int c, iopn = 0, syniocf;
struct ioword *iop, **iops;
XPtrV args, vars;
struct multiline_state old_multiline;

iops = (struct ioword **) alloc(sizeofN(struct ioword *, NUFILE+1),
ATEMP);
XPinit(args, 16);
XPinit(vars, 16);

if (multiline.on)
cf = CONTIN;
syniocf = KEYWORD|ALIAS;
switch (c = token(cf|KEYWORD|ALIAS|VARASN)) {
default:
REJECT;
return NULL; /* empty line */

case LWORD:
case REDIR:
REJECT;
syniocf &= ~(KEYWORD|ALIAS);
t = newtp(TCOM);
while (1) {
cf = (t->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->evalflags = DOASNTILDE;
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();
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 };
syniocf &= ~(KEYWORD|ALIAS);
t = newtp(TCOM);
ACCEPT;
XPput(args, wdcopy(let_cmd, ATEMP));
musthave(LWORD,LETEXPR);
XPput(args, yylval.cp);
break;
}

#ifdef KSH
case DBRACKET: /* [[ .. ]] */
syniocf &= ~(KEYWORD|ALIAS);
t = newtp(TDBRACKET);
ACCEPT;
db_parse(&args, &vars);
break;
#endif /* KSH */

case FOR:
case SELECT:
t = newtp((c == FOR) ? TFOR : TSELECT);
musthave(LWORD, ARRAYVAR);
if (!is_wdvarname(yylval.cp, TRUE))
yyerror("%s: bad identifier\n",
c == FOR ? "for" : "select");
t->str = strsave(ident, ATEMP);
multiline_push(&old_multiline, c);
t->vars = wordlist();
t->left = dogroup();
multiline_pop(&old_multiline);
break;

case WHILE:
case UNTIL:
multiline_push(&old_multiline, c);
t = newtp((c == WHILE) ? TWHILE: TUNTIL);
t->left = c_list();
t->right = dogroup();
multiline_pop(&old_multiline);
break;

case CASE:
t = newtp(TCASE);
musthave(LWORD, 0);
t->str = yylval.cp;
multiline_push(&old_multiline, c);
musthave(IN, CONTIN|KEYWORD|ALIAS);
t->left = caselist();
musthave(ESAC, KEYWORD|ALIAS);
multiline_pop(&old_multiline);
break;

case IF:
multiline_push(&old_multiline, c);
t = newtp(TIF);
t->left = c_list();
t->right = thenpart();
musthave(FI, KEYWORD|ALIAS);
multiline_pop(&old_multiline);
break;

case BANG:
syniocf &= ~(KEYWORD|ALIAS);
t = pipeline(0);
if (t == (struct op *) 0)
syntaxerr();
t = block(TBANG, NOBLOCK, t, NOWORDS);
break;

case TIME:
syniocf &= ~(KEYWORD|ALIAS);
t = pipeline(0);
t = block(TTIME, t, NOBLOCK, NOWORDS);
break;

case FUNCTION:
musthave(LWORD, 0);
t = function_body(yylval.cp, TRUE);
break;
}

while ((iop = synio(syniocf)) != NULL) {
if (iopn >= NUFILE)
yyerror("too many redirections\n");
iops[iopn++] = iop;
}

if (iopn == 0) {
afree((void*) iops, ATEMP);
t->ioact = NULL;
} else {
iops[iopn++] = NULL;
iops = (struct ioword **) aresize((void*) iops,
sizeofN(struct ioword *, iopn), ATEMP);
t->ioact = iops;
}

if (t->type == TCOM || t->type == TDBRACKET) {
XPput(args, NULL);
t->args = (char **) XPclose(args);
XPput(vars, NULL);
t->vars = (char **) XPclose(vars);
} else {
XPfree(args);
XPfree(vars);
}

return t;
}

static struct op *
dogroup()
{
register int c;
register struct op *list;

c = token(CONTIN|KEYWORD|ALIAS);
/* A {...} can be used instead of do...done for for/select loops
* but not for while/until loops - we don't need to check if it
* is a while loop because it would have been parsed as part of
* the conditional command list...
*/
if (c == DO)
c = DONE;
else if (c == '{')
c = '}';
else
syntaxerr();
list = c_list();
musthave(c, KEYWORD|ALIAS);
return list;
}

static struct op *
thenpart()
{
register struct op *t;

musthave(THEN, KEYWORD|ALIAS);
t = newtp(0);
t->left = c_list();
if (t->left == NULL)
syntaxerr();
t->right = elsepart();
return (t);
}

static struct op *
elsepart()
{
register struct op *t;

switch (token(KEYWORD|ALIAS|VARASN)) {
case ELSE:
if ((t = c_list()) == NULL)
syntaxerr();
return (t);

case ELIF:
t = newtp(TELIF);
t->left = c_list();
t->right = thenpart();
return (t);

default:
REJECT;
return NULL;
}
}

static struct op *
caselist()
{
register struct op *t, *tl;

t = tl = NULL;
while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != ESAC) { /* no ALIAS here */
struct op *tc = casepart();
if (tl == NULL)
t = tl = tc, tl->right = NULL;
else
tl->right = tc, tl = tc;
}
return (t);
}

static struct op *
casepart()
{
register struct op *t;
register int c;
XPtrV ptns;

XPinit(ptns, 16);
t = newtp(TPAT);
c = token(CONTIN|KEYWORD); /* no ALIAS here */
if (c != '(')
REJECT;
do {
musthave(LWORD, 0);
XPput(ptns, yylval.cp);
} while ((c = token(0)) == '|');
REJECT;
XPput(ptns, NULL);
t->vars = (char **) XPclose(ptns);
musthave(')', 0);

t->left = c_list();
if ((tpeek(CONTIN|KEYWORD|ALIAS)) != ESAC)
musthave(BREAK, CONTIN|KEYWORD|ALIAS);
return (t);
}

static struct op *
function_body(name, ksh_func)
char *name;
int ksh_func; /* function foo { } vs foo() { .. } */
{
XString xs;
char *xp, *p;
struct op *t;
int old_func_parse;

Xinit(xs, xp, 16, ATEMP);
for (p = name; ; ) {
if ((*p == EOS && Xlength(xs, xp) == 0)
|| (*p != EOS && *p != CHAR && *p != QCHAR
&& *p != OQUOTE && *p != CQUOTE))
{
p = snptreef((char *) 0, 32, "%S", name);
yyerror("%s: invalid function name\n", p);
}
Xcheck(xs, xp);
if (*p == EOS) {
Xput(xs, xp, '\0');
break;
} else if (*p == CHAR || *p == QCHAR) {
Xput(xs, xp, p[1]);
p += 2;
} else
p++; /* OQUOTE/CQUOTE */
}
t = newtp(TFUNCT);
t->str = Xclose(xs, xp);

/* Note that POSIX allows only compound statements after foo(), sh and
* at&t ksh allow any command, go with the later since it shouldn't
* break anything. However, for function foo, at&t ksh only accepts
* an open-brace.
*/
if (ksh_func) {
musthave('{', CONTIN|KEYWORD|ALIAS); /* } */
REJECT;
}

old_func_parse = e->flags & EF_FUNC_PARSE;
e->flags |= EF_FUNC_PARSE;
if ((t->left = get_command(CONTIN)) == (struct op *) 0) {
/* create empty command so foo(): will work */
t->left = newtp(TCOM);
t->args = (char **) alloc(sizeof(char *), ATEMP);
t->args[0] = (char *) 0;
t->vars = (char **) alloc(sizeof(char *), ATEMP);
t->vars[0] = (char *) 0;
}
if (!old_func_parse)
e->flags &= ~EF_FUNC_PARSE;

return t;
}

static char **
wordlist()
{
register int c;
XPtrV args;

XPinit(args, 16);
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();
if (XPsize(args) == 0) {
XPfree(args);
return NULL;
} else {
XPput(args, NULL);
return (char **) XPclose(args);
}
}

/*
* supporting functions
*/

static struct op *
block(type, t1, t2, wp)
int type;
struct op *t1, *t2;
char **wp;
{
register struct op *t;

t = newtp(type);
t->left = t1;
t->right = t2;
t->vars = wp;
return (t);
}

const struct tokeninfo {
char *name;
short val;
short reserved;
} tokentab[] = {
/* Reserved words */
{ "if", IF, TRUE },
{ "then", THEN, TRUE },
{ "else", ELSE, TRUE },
{ "elif", ELIF, TRUE },
{ "fi", FI, TRUE },
{ "case", CASE, TRUE },
{ "esac", ESAC, TRUE },
{ "for", FOR, TRUE },
#ifdef KSH
{ "select", SELECT, TRUE },
#endif /* KSH */
{ "while", WHILE, TRUE },
{ "until", UNTIL, TRUE },
{ "do", DO, TRUE },
{ "done", DONE, TRUE },
{ "in", IN, TRUE },
{ "function", FUNCTION, TRUE },
{ "time", TIME, TRUE },
{ "{", '{', TRUE },
{ "}", '}', TRUE },
{ "!", BANG, TRUE },
#ifdef KSH
{ "[[", DBRACKET, TRUE },
#endif /* KSH */
/* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
{ "&&", LOGAND, FALSE },
{ "||", LOGOR, FALSE },
{ ";;", BREAK, FALSE },
{ "((", MDPAREN, FALSE },
{ "|&", COPROC, FALSE },
/* and some special cases... */
{ "newline", '\n', FALSE },
{ 0 }
};

void
initkeywords()
{
register struct tokeninfo const *tt;
register struct tbl *p;

tinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */
for (tt = tokentab; tt->name; tt++) {
if (tt->reserved) {
p = tenter(&keywords, tt->name, hash(tt->name));
p->flag |= DEFINED|ISSET;
p->type = CKEYWD;
p->val.i = tt->val;
}
}
}

static void
syntaxerr()
{
char redir[6]; /* 2<<- is the longest redirection, I think */
char *s;
char *what = "unexpected";
struct tokeninfo const *tt;
int c;

REJECT;
c = token(0);
Again:
switch (c) {
case 0:
if (multiline.on) {
c = multiline.start_token;
source->errline = multiline.start_line;
what = "unmatched";
goto Again;
}
/* don't quote the EOF */
yyerror("syntax error: unexpected EOF\n");
/*NOTREACHED*/

case LWORD:
s = snptreef((char *) 0, 32, "%S", yylval.cp);
break;

case REDIR:
yylval.iop->name = (char *) 0;
snptreef(s = 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) {
s = redir;
redir[0] = c;
redir[1] = '\0';
} else
shf_snprintf(s = redir, sizeof(redir),
"?%d", c);
}
}
yyerror("syntax error: `%s' %s\n", s, what);
}

static void
multiline_push(save, tok)
struct multiline_state *save;
int tok;
{
*save = multiline;
multiline.on = TRUE;
multiline.start_token = tok;
multiline.start_line = source->line;
}

static void
multiline_pop(saved)
struct multiline_state *saved;
{
multiline = *saved;
}

static struct op *
newtp(type)
int type;
{
register struct op *t;

t = (struct op *) alloc(sizeof(*t), ATEMP);
t->type = type;
t->evalflags = 0;
t->args = t->vars = NULL;
t->ioact = NULL;
t->left = t->right = NULL;
t->str = NULL;
return (t);
}

struct op *
compile(s)
Source *s;
{
yynerrs = 0;
multiline.on = FALSE;
herep = heres;
source = s;
yyparse();
return outtree;
}

/* This kludge exists to take care of sh/at&t ksh oddity in which
* the arguments of alias/export/readonly/typeset have no field
* splitting, file globbing, or (normal) tilde expansion done.
* at&t ksh seems to do something similar to this since
* $ touch a=a; typeset a=[ab]; echo "$a"
* a=[ab]
* $ x=typeset; $x a=[ab]; echo "$a"
* a=a
* $
*/
static int
assign_command(s)
char *s;
{
char c = *s;

if (Flag(FPOSIX) || !*s)
return 0;
return (c == 'a' && strcmp(s, "alias") == 0)
|| (c == 'e' && strcmp(s, "export") == 0)
|| (c == 'r' && strcmp(s, "readonly") == 0)
|| (c == 't' && strcmp(s, "typeset") == 0);
}


#ifdef KSH
# define db_makevar(c) (db_fakearg[1] = (c), wdcopy(db_fakearg, ATEMP))

/* used by db_makevar() */
static char db_fakearg[3] = { CHAR, 'x', EOS };

/*
* Parse a [[ ]] expression, converting it to a [ .. ] style expression,
* saving the result in t->args.
*/
static void
db_parse(argsp, varsp)
XPtrV *argsp, *varsp;
{
static const char db_start[] = { CHAR, '[', CHAR, '[', EOS };
static const char db_close[] = { CHAR, ']', CHAR, ']', EOS };

XPput(*argsp, wdcopy(db_start, ATEMP));
XPput(*varsp, db_makevar(DB_NORM));
db_oaexpr(argsp, varsp);
if (tpeek(ARRAYVAR|CONTIN) != LWORD || strcmp(yylval.cp, db_close) != 0)
syntaxerr();
ACCEPT;
}

static void
db_oaexpr(argsp, varsp)
XPtrV *argsp, *varsp;
{
static const char or[] = { CHAR, '-', CHAR, 'o', EOS };
static const char and[] = { CHAR, '-', CHAR, 'a', EOS };
int c;

db_nexpr(argsp, varsp);
if ((c = tpeek(ARRAYVAR|CONTIN)) == LOGOR || c == LOGAND) {
ACCEPT;
XPput(*argsp, wdcopy(c == LOGOR ? or : and, ATEMP));
XPput(*varsp, db_makevar(c == LOGOR ? DB_OR : DB_AND));
db_oaexpr(argsp, varsp);
}
}

static void
db_nexpr(argsp, varsp)
XPtrV *argsp, *varsp;
{
static const char not[] = { CHAR, '!', EOS };

if (tpeek(ARRAYVAR|CONTIN) == LWORD && strcmp(yylval.cp, not) == 0) {
ACCEPT;
XPput(*argsp, yylval.cp);
XPput(*varsp, db_makevar(DB_NORM));
db_nexpr(argsp, varsp);
} else
db_primary(argsp, varsp);
}

static void
db_primary(argsp, varsp)
XPtrV *argsp, *varsp;
{
static const char oparen[] = { CHAR, '(', EOS };
static const char cparen[] = { CHAR, ')', EOS };
int c;

c = token(ARRAYVAR|CONTIN);
if (c == '(' /*)*/) {
XPput(*argsp, wdcopy(oparen, ATEMP));
XPput(*varsp, db_makevar(DB_NORM));
db_oaexpr(argsp, varsp);
/*(*/
if (token(ARRAYVAR|CONTIN) != ')')
/*(*/
yyerror("missing )\n");
XPput(*argsp, wdcopy(cparen, ATEMP));
XPput(*varsp, db_makevar(DB_NORM));
} else if (c == LWORD) {
if (*ident && is_db_unop(ident)) {
XPput(*argsp, yylval.cp);
XPput(*varsp, db_makevar(DB_NORM));
if (token(ARRAYVAR) != LWORD)
syntaxerr();
XPput(*argsp, yylval.cp);
XPput(*varsp, db_makevar(DB_NORM));
} else {
static const char binop_marker[] =
{ CHAR, '-', CHAR, 'B', CHAR, 'E', EOS };

/* must be a binary operator: mark this
* with special -BE operator so we can't confuse
* the binary expression '-z = foobar' for
* something else. (-BE is only recognized
* when parsing [[ ]] expressions)
*/
XPput(*argsp, wdcopy(binop_marker, ATEMP));
XPput(*varsp, db_makevar(DB_BE));
XPput(*argsp, yylval.cp);
XPput(*varsp, db_makevar(DB_NORM));
c = token(ARRAYVAR);
/* convert REDIR < and > to LWORD < and > */
if (c == REDIR
&& (yylval.iop->flag == IOREAD
|| yylval.iop->flag == IOWRITE))
{
static const char lthan[] = { CHAR, '<', EOS };
static const char gthan[] = { CHAR, '>', EOS };
const char *what = yylval.iop->flag == IOREAD ?
lthan : gthan;

afree(yylval.iop, ATEMP);
yylval.cp = wdcopy(what, ATEMP);
c = symbol = LWORD;
ident[0] = what[1];
ident[1] = '\0';
}
if (c == LWORD && *ident && (c = is_db_binop(ident))) {
XPput(*argsp, yylval.cp);
XPput(*varsp, db_makevar(DB_NORM));
if (token(ARRAYVAR) != LWORD)
syntaxerr();
XPput(*argsp, yylval.cp);
XPput(*varsp, db_makevar(is_db_patop(c) ?
DB_PAT : DB_NORM));
} else
syntaxerr();
}
} else
syntaxerr();
}
#endif /* KSH */
pdksh-5.1.2/table.c100644 0 133 11327 5666417363 12746 0ustar rootlsource#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: table.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

/*
* dynamic hashed associative table for commands and variables
*/

#include "sh.h"

#define INIT_TBLS 8 /* initial table size (power of 2) */

static struct tstate {
int left;
struct tbl **next;
} tstate;

static void texpand ARGS((struct table *tp, int nsize));
static int tnamecmp ARGS((void *p1, void *p2));


unsigned int
hash(n)
register const char * n;
{
register unsigned int h = 0;

while (*n != '\0')
h = 2*h + *n++;
return h * 32821; /* scatter bits */
}

void
tinit(tp, ap, tsize)
register struct table *tp;
register Area *ap;
int tsize;
{
tp->areap = ap;
tp->tbls = NULL;
tp->size = tp->nfree = 0;
if (tsize)
texpand(tp, tsize);
}

static void
texpand(tp, nsize)
register struct table *tp;
int nsize;
{
register int i;
register struct tbl *tblp, **p;
register struct tbl **ntblp, **otblp = tp->tbls;
int osize = tp->size;

ntblp = (struct tbl**) alloc(sizeofN(struct tbl *, nsize), tp->areap);
for (i = 0; i < nsize; i++)
ntblp[i] = NULL;
tp->size = nsize;
tp->nfree = 8*nsize/10; /* table can get 80% full */
tp->tbls = ntblp;
if (otblp == NULL)
return;
for (i = 0; i < osize; i++)
if ((tblp = otblp[i]) != NULL)
if ((tblp->flag&DEFINED)) {
for (p = &ntblp[hash(tblp->name)
& (tp->size-1)];
*p != NULL; p--)
if (p == ntblp) /* wrap */
p += tp->size;
*p = tblp;
tp->nfree--;
} else {
afree((void*)tblp, tp->areap);
}
afree((void*)otblp, tp->areap);
}

struct tbl *
tsearch(tp, n, h)
register struct table *tp; /* table */
register const char *n; /* name to enter */
unsigned int h; /* hash(n) */
{
register struct tbl **pp, *p;

if (tp->size == 0)
return NULL;

/* search for name in hashed table */
for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
if (*p->name == *n && strcmp(p->name, n) == 0
&& (p->flag&DEFINED))
return p;
if (pp == tp->tbls) /* wrap */
pp += tp->size;
}

return NULL;
}

struct tbl *
tenter(tp, n, h)
register struct table *tp; /* table */
register const char *n; /* name to enter */
unsigned int h; /* hash(n) */
{
register struct tbl **pp, *p;
register int len;

if (tp->size == 0)
texpand(tp, INIT_TBLS);
Search:
/* search for name in hashed table */
for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
if (*p->name == *n && strcmp(p->name, n) == 0)
return p; /* found */
if (pp == tp->tbls) /* wrap */
pp += tp->size;
}

if (tp->nfree <= 0) { /* too full */
texpand(tp, 2*tp->size);
goto Search;
}

/* create new tbl entry */
len = strlen(n) + 1;
p = (struct tbl *) alloc(offsetof(struct tbl, name[len]), tp->areap);
p->flag = 0;
p->type = 0;
p->areap = tp->areap;
p->field = 0;
p->array = (struct tbl *)0;
memcpy(p->name, n, len);

/* enter in tp->tbls */
tp->nfree--;
*pp = p;
return p;
}

void
tdelete(p)
register struct tbl *p;
{
p->flag = 0;
}

void
twalk(tp)
register struct table *tp;
{
tstate.left = tp->size;
tstate.next = tp->tbls;
}

struct tbl *
tnext()
{
while (--tstate.left >= 0) {
struct tbl *p = *tstate.next++;
if (p != NULL && (p->flag&DEFINED))
return p;
}
return NULL;
}

static int
tnamecmp(p1, p2)
void *p1, *p2;
{
return strcmp(((struct tbl *)p1)->name, ((struct tbl *)p2)->name);
}

struct tbl **
tsort(tp)
register struct table *tp;
{
register int i;
register struct tbl **p, **sp, **dp;

p = (struct tbl **)alloc(sizeofN(struct tbl *, tp->size+1), ATEMP);
sp = tp->tbls; /* source */
dp = p; /* dest */
for (i = 0; i < tp->size; i++)
if ((*dp = *sp++) != NULL && (((*dp)->flag&DEFINED) ||
((*dp)->flag&ARRAY)))
dp++;
i = dp - p;
qsortp((void**)p, (size_t)i, tnamecmp);
p[i] = NULL;
return p;
}

#ifdef PERF_DEBUG /* performance debugging */

void
tprintinfo(tp)
struct table *tp;
{
struct tbl *te;
char *n;
unsigned int h;
int ncmp;
int totncmp = 0, maxncmp = 0;
int nentries = 0;

shellf("table size %d, nfree %d\n", tp->size, tp->nfree);
shellf(" Ncmp name\n");
twalk(tp);
while ((te = tnext())) {
register struct tbl **pp, *p;

h = hash(n = te->name);
ncmp = 0;

/* taken from tsearch() and added counter */
for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp); pp--) {
ncmp++;
if (*p->name == *n && strcmp(p->name, n) == 0
&& (p->flag&DEFINED))
break; /* return p; */
if (pp == tp->tbls) /* wrap */
pp += tp->size;
}
shellf(" %4d %s\n", ncmp, n);
totncmp += ncmp;
nentries++;
if (ncmp > maxncmp)
maxncmp = ncmp;
}
if (nentries)
shellf(" %d entries, worst ncmp %d, avg ncmp %d.%02d\n",
nentries, maxncmp,
totncmp / nentries,
(totncmp % nentries) * 100 / nentries);
}
#endif /* PERF_DEBUG */
pdksh-5.1.2/trap.c100644 0 133 22155 5667372420 12621 0ustar rootlsource/*
* signal handling
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: trap.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"

/* Table is indexed by signal number
*
* The script siglist.sh generates siglist.out, which is a sorted, complete
* list of signals
*/
Trap sigtraps[SIGNALS+1] = {
#ifdef OS2
{ SIGEXIT_, "EXIT", "" },
#else /* OS2 */
{ SIGEXIT_, "EXIT", "Signal 0" },
#endif /* OS2 */
#include "siglist.out" /* generated by siglist.sh */
{ SIGERR_, "ERR", "Error handler" },
};

static RETSIGTYPE alarm_catcher ARGS((int sig));

static struct sigaction Sigact_ign, Sigact_trap, Sigact_alarm;

void
inittraps()
{
#ifdef HAVE_SYS_SIGLIST
# ifndef SYS_SIGLIST_DECLARED
extern char *sys_siglist[];
# endif
int i;

/* Use system description, if available, for unknown signals... */
for (i = 0; i < NSIG; i++)
if (!sigtraps[i].name && sys_siglist[i][0])
sigtraps[i].mess = sys_siglist[i];
#endif /* HAVE_SYS_SIGLIST */

sigemptyset(&Sigact_ign.sa_mask);
Sigact_ign.sa_flags = KSH_SA_FLAGS;
Sigact_ign.sa_handler = SIG_IGN;
Sigact_trap = Sigact_ign;
Sigact_trap.sa_handler = trapsig;
Sigact_alarm = Sigact_ign;
Sigact_alarm.sa_handler = alarm_catcher;

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 temproary 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);
}

void
alarm_init()
{
sigtraps[SIGALRM].flags |= TF_SHELL_USES;
setsig(&sigtraps[SIGALRM], alarm_catcher,
SS_RESTORE_ORIG|SS_FORCE);
}

static RETSIGTYPE
alarm_catcher(sig)
int sig;
{
trapsig(sig);
#ifdef KSH
if (ksh_tmout_state == TMOUT_READING) {
int left = alarm(0);

if (left == 0) {
ksh_tmout_state = TMOUT_LEAVING;
intrsig = 1;
} else
alarm(left);
}
#endif /* KSH */
#ifdef V7_SIGNALS
sigaction(i, &Sigact_alarm, (struct sigaction *) 0);
#endif /* V7_SIGNALS */
}

Trap *
gettrap(name)
char *name;
{
int i;
register Trap *p;

if (digit(*name)) {
int n;

if (getn(name, &n) && 0 <= n && n < SIGNALS)
return &sigtraps[n];
return NULL;
}
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->name && strcasecmp(p->name, name) == 0)
return p;
return NULL;
}

/*
* trap signal handler
*/
RETSIGTYPE
trapsig(i)
int i;
{
Trap *p = &sigtraps[i];

trap = p->set = 1;
if (p->flags & TF_DFL_INTR)
intrsig = 1;
if ((p->flags & TF_FATAL) && !p->trap) {
fatal_trap = 1;
intrsig = 1;
}
#ifdef V7_SIGNALS
if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */
sigaction(i, &Sigact_trap, (struct sigaction *) 0);
#endif /* V7_SIGNALS */
}

/* called when we want to allow the user to ^C out of something - won't
* work if user has trapped SIGINT.
*/
void
intrcheck()
{
if (intrsig)
runtraps(TF_DFL_INTR|TF_FATAL);
}

/* called after EINTR to check if a signal with normally causes process
* termination has been received.
*/
int
fatal_trap_check()
{
int i;
Trap *p;

/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
/* return value is used as an exit code */
return 128 + p->signal;
return 0;
}

/* Returns the signal number of any pending traps: ie, a signal which has
* occured for which a trap has been set or for which the TF_DFL_INTR flag
* is set.
*/
int
trap_pending()
{
int i;
Trap *p;

for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->set && ((p->trap && p->trap[0])
|| ((p->flags & (TF_DFL_INTR|TF_FATAL))
&& !p->trap)))
return p->signal;
return 0;
}

/*
* run any pending traps. If intr is set, only run traps that
* can interrupt commands.
*/
void
runtraps(flag)
int flag;
{
int i;
register Trap *p;

#ifdef KSH
if (ksh_tmout_state == TMOUT_LEAVING) {
ksh_tmout_state = TMOUT_EXECUTING;
warningf(FALSE, "timed out waiting for input\n");
unwind(LEXIT);
} else
/* XXX: this means the alarm will have no effect if a trap
* is caught after the alarm() was started...not good.
*/
ksh_tmout_state = TMOUT_EXECUTING;
#endif /* KSH */
if (!flag)
trap = 0;
if (flag & TF_DFL_INTR)
intrsig = 0;
if (flag & TF_FATAL)
fatal_trap = 0;
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
if (p->set && (!flag
|| ((p->flags & flag) && p->trap == (char *) 0)))
runtrap(p);
}

void
runtrap(p)
Trap *p;
{
int i = p->signal;
char *trapstr = p->trap;
int oexstat;
int UNINITIALIZED(old_changed);

p->set = 0;
if (trapstr == (char *) 0) { /* SIG_DFL */
if (p->flags & TF_FATAL) {
/* eg, SIGHUP */
exstat = 128 + i;
unwind(LLEAVE);
}
if (p->flags & TF_DFL_INTR) {
/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
exstat = 128 + i;
unwind(LINTR);
}
return;
}
if (trapstr[0] == '\0') /* SIG_IGN */
return;
if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */
old_changed = p->flags & TF_CHANGED;
p->flags &= ~TF_CHANGED;
p->trap = (char *) 0;
}
oexstat = exstat;
command(trapstr);
exstat = oexstat;
if (i == SIGEXIT_ || i == SIGERR_) {
if (p->flags & TF_CHANGED)
/* don't clear TF_CHANGED */
afree(trapstr, APERM);
else
p->trap = trapstr;
p->flags |= old_changed;
}
}

/* clear pending traps and reset user's trap handlers; used after fork(2) */
void
cleartraps()
{
int i;
Trap *p;

trap = 0;
intrsig = 0;
fatal_trap = 0;
for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) {
p->set = 0;
if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
settrap(p, (char *) 0);
}
}

/* restore signals just before an exec(2) */
void
restoresigs()
{
int i;
Trap *p;

for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++)
if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
SS_RESTORE_CURR|SS_FORCE);
}

void
settrap(p, s)
Trap *p;
char *s;
{
RETSIGTYPE (*f)();

if (p->trap)
afree(p->trap, APERM);
p->trap = strsave(s, APERM); /* handles s == 0 */
p->flags |= TF_CHANGED;
f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;

p->flags |= TF_USER_SET;
if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
f = trapsig;
else if (p->flags & TF_SHELL_USES) {
if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
/* do what user wants at exec time */
p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
if (f == SIG_IGN)
p->flags |= TF_EXEC_IGN;
else
p->flags |= TF_EXEC_DFL;
}
/* assumes handler already set to what shell wants it
* (normally trapsig, but could be j_sigchld() or SIG_IGN)
*/
return;
}

/* todo: should we let user know signal is ignored? how? */
setsig(p, f, SS_RESTORE_CURR|SS_USER);
}

/* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
* kill shell (unless user catches it and exits)
*/
int
block_pipe()
{
int restore_dfl = 0;
Trap *p = &sigtraps[SIGPIPE];

if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
setsig(p, SIG_IGN, SS_RESTORE_CURR);
if (p->flags & TF_ORIG_DFL)
restore_dfl = 1;
} else if (p->cursig == SIG_DFL) {
setsig(p, SIG_IGN, SS_RESTORE_CURR);
restore_dfl = 1; /* restore to SIG_DFL */
}
return restore_dfl;
}

/* Called by c_print() to undo whatever block_pipe() did */
void
restore_pipe(restore_dfl)
int restore_dfl;
{
if (restore_dfl)
setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
}

/* Set action for a signal. Action may not be set if original
* action was SIG_IGN, depending on the value of flags and
* FTALKING.
*/
int
setsig(p, f, flags)
Trap *p;
RETSIGTYPE (*f)();
int flags;
{
struct sigaction sigact;

if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
return 1;

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;
}
if ((p->flags & TF_ORIG_IGN) && (flags & SS_USER)
&& !(flags & SS_FORCE) && !Flag(FTALKING))
return 0;
if (!(flags & SS_USER)) {
if (!(flags & SS_FORCE) && (p->flags & TF_ORIG_IGN))
return 0;
}

setexecsig(p, flags & SS_RESTORE_MASK);

if (p->cursig != f) {
p->cursig = f;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = KSH_SA_FLAGS;
sigact.sa_handler = f;
sigaction(p->signal, &sigact, (struct sigaction *) 0);
}

return 1;
}

/* control what signal is set to before an exec() */
void
setexecsig(p, restore)
Trap *p;
int restore;
{
/* XXX debugging */
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
internal_errorf(1, "setexecsig: unset signal %d(%s)",
p->signal, p->name);

/* restore original value for exec'd kids */
p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
switch (restore & SS_RESTORE_MASK) {
case SS_RESTORE_CURR: /* leave things as they currently are */
break;
case SS_RESTORE_ORIG:
p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
break;
case SS_RESTORE_DFL:
p->flags |= TF_EXEC_DFL;
break;
case SS_RESTORE_IGN:
p->flags |= TF_EXEC_IGN;
break;
}
}
pdksh-5.1.2/tree.c100644 0 133 24132 5660422003 12571 0ustar rootlsource/*
* command tree climbing
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: tree.c,v 1.3 1994/05/31 13:34:34 michael Exp $";
#endif

#include "sh.h"

#define tputc(c, shf) shf_putchar(c, shf);
static void tputC ARGS((int c, struct shf *shf));
static void tputS ARGS((char *wp, struct shf *shf));
static void vfptreef ARGS((struct shf *shf, char *fmt, va_list va));
static struct ioword **iocopy ARGS((struct ioword **iow, Area *ap));
static void iofree ARGS((struct ioword **iow, Area *ap));

/*
* print a command tree
*/

void
ptree(t, shf)
register struct op *t;
register struct shf *shf;
{
register char **w;
struct ioword **ioact;
struct op *t1;

Chain:
if (t == NULL)
return;
switch (t->type) {
case TCOM:
if (t->vars)
for (w = t->vars; *w != NULL; )
fptreef(shf, "%S ", *w++);
else
fptreef(shf, "#no-vars# ");
if (t->args)
for (w = t->args; *w != NULL; )
fptreef(shf, "%S ", *w++);
else
fptreef(shf, "#no-args# ");
break;
case TEXEC:
t = t->left;
goto Chain;
case TPAREN:
fptreef(shf, "(%T) ", t->left);
break;
case TPIPE:
fptreef(shf, "%T | ", t->left);
t = t->right;
goto Chain;
case TLIST:
fptreef(shf, "%T%;", t->left);
t = t->right;
goto Chain;
case TOR:
case TAND:
fptreef(shf, "%T %s %T",
t->left, (t->type==TOR) ? "||" : "&&", t->right);
break;
case TBANG:
fptreef(shf, "! ");
t = t->right;
goto Chain;
case TDBRACKET:
{
int i;

/* the opening [[ is in args[0] */
for (i = 0; t->args[i]; i++)
switch (t->vars[i][1]) {
case DB_NORM:
fptreef(shf, "%S ", t->args[i]);
break;
case DB_OR:
fptreef(shf, "|| ");
break;
case DB_AND:
fptreef(shf, "&& ");
case DB_BE:
/* print nothing */
break;
}
fptreef(shf, "]] ");
break;
}
case TFOR:
fptreef(shf, "for %s ", t->str);
if (t->vars != NULL) {
fptreef(shf, "in ");
for (w = t->vars; *w; )
fptreef(shf, "%S ", *w++);
fptreef(shf, "%;");
}
fptreef(shf, "do %T%;done ", t->left);
break;
case TCASE:
fptreef(shf, "case %S in%;", t->str);
for (t1 = t->left; t1 != NULL; t1 = t1->right) {
fptreef(shf, "(");
for (w = t1->vars; *w != NULL; w++)
fptreef(shf, "%S%c", *w, (w[1] != NULL) ? '|' : ')');
fptreef(shf, " %T;;%;", t1->left);
}
fptreef(shf, "esac ");
break;
case TIF:
fptreef(shf, "if %T%;", t->left);
t = t->right;
if (t->left != NULL)
fptreef(shf, "then %T%;", t->left);
if (t->right != NULL)
fptreef(shf, "else %T%;", t->right);
fptreef(shf, "fi ");
break;
case TWHILE:
case TUNTIL:
fptreef(shf, "%s %T%;do %T%;done ",
(t->type==TWHILE) ? "while" : "until",
t->left, t->right);
break;
case TBRACE:
fptreef(shf, "{%;%T%;} ", t->left);
break;
case TCOPROC:
fptreef(shf, "%T |&", t->left);
break;
case TASYNC:
fptreef(shf, "%T &", t->left);
break;
case TFUNCT:
fptreef(shf, "function %s %T", t->str, t->left);
break;
case TTIME:
fptreef(shf, "time %T", t->left);
break;
default:
fptreef(shf, "");
break;
}
if ((ioact = t->ioact) != NULL)
while (*ioact != NULL)
pioact(shf, *ioact++);
}

void
pioact(shf, iop)
register struct shf *shf;
register struct ioword *iop;
{
int flag = iop->flag;
int type = flag & IOTYPE;
int expected;

expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0
: (type == IOCAT || type == IOWRITE) ? 1
: (type == IODUP && (iop->unit == !(flag & IORDUP))) ?
iop->unit
: iop->unit + 1;
if (iop->unit != expected)
tputc('0' + iop->unit, shf);

switch(flag&IOTYPE) {
case IOREAD:
fptreef(shf, "< ");
break;
case IOHERE:
if (flag&IOSKIP)
fptreef(shf, "<<- ");
else
fptreef(shf, "<< ");
break;
case IOCAT:
fptreef(shf, ">> ");
break;
case IOWRITE:
if (flag&IOCLOB)
fptreef(shf, ">| ");
else
fptreef(shf, "> ");
break;
case IORDWR:
fptreef(shf, "<> ");
break;
case IODUP:
if (flag & IORDUP)
fptreef(shf, "<&");
else
fptreef(shf, ">&");
break;
}

if (iop->name) { /* name is 0 when printing syntax errors */
if ((flag&IOTYPE) == IOHERE) {
if (flag&IOEVAL)
fptreef(shf, "%s ", iop->name);
else
fptreef(shf, "'%s' ", iop->name);
} else
fptreef(shf, "%S ", iop->name);
}
}


/*
* variants of fputc, fputs for ptreef and snptreef
*/

static void
tputC(c, shf)
register int c;
register struct shf *shf;
{
if ((c&0x60) == 0) { /* C0|C1 */
tputc((c&0x80) ? '$' : '^', shf);
tputc(((c&0x7F)|0x40), shf);
} else if ((c&0x7F) == 0x7F) { /* DEL */
tputc((c&0x80) ? '$' : '^', shf);
tputc('?', shf);
} else
tputc(c, shf);
}

static void
tputS(wp, shf)
register char *wp;
register struct shf *shf;
{
register int c, quoted=0;

while (1)
switch ((c = *wp++)) {
case EOS:
return;
case CHAR:
tputC(*wp++, shf);
break;
case QCHAR:
if (!quoted)
tputc('\\', shf);
tputC(*wp++, shf);
break;
case OQUOTE:
quoted = 1;
tputc('"', shf);
break;
case CQUOTE:
quoted = 0;
tputc('"', shf);
break;
case OSUBST:
tputc('$', shf);
tputc('{', shf);
while ((c = *wp++) != 0)
tputc(c, shf);
break;
case CSUBST:
tputc('}', shf);
break;
case COMSUB:
tputc('$', shf);
tputc('(', shf);
while (*wp != 0)
tputC(*wp++, shf);
tputc(')', shf);
break;
case EXPRSUB:
tputc('$', shf);
tputc('(', shf);
tputc('(', shf);
while (*wp != 0)
tputC(*wp++, shf);
tputc(')', shf);
tputc(')', shf);
break;
}
}

/*
* this is the _only_ way to reliably handle
* variable args with an ANSI compiler
*/
/* VARARGS */
int
#ifdef HAVE_PROTOTYPES
fptreef(struct shf *shf, char *fmt, ...)
#else
fptreef(shf, fmt, va_alist)
struct shf *shf;
char *fmt;
va_dcl
#endif
{
va_list va;

SH_VA_START(va, fmt);

vfptreef(shf, fmt, va);
va_end(va);
return 0;
}

/* VARARGS */
char *
#ifdef HAVE_PROTOTYPES
snptreef(char *s, int n, char *fmt, ...)
#else
snptreef(s, n, fmt, va_alist)
char *s;
int n;
char *fmt;
va_dcl
#endif
{
va_list va;
struct shf shf;

shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);

SH_VA_START(va, fmt);
vfptreef(&shf, fmt, va);
va_end(va);

return shf_sclose(&shf); /* null terminates */
}

static void
vfptreef(shf, fmt, va)
register struct shf *shf;
register char *fmt;
register va_list va;
{
register int c;

while ((c = *fmt++))
if (c == '%') {
register long n;
register char *p;
int neg;

switch ((c = *fmt++)) {
case 'c':
tputc(va_arg(va, int), shf);
break;
case 's':
p = va_arg(va, char *);
while (*p)
tputc(*p++, shf);
break;
case 'S': /* word */
p = va_arg(va, char *);
tputS(p, shf);
break;
case 'd': case 'u': /* decimal */
n = (c == 'd') ? va_arg(va, int) : va_arg(va, unsigned int);
neg = c=='d' && n<0;
p = ulton((neg) ? -n : n, 10);
if (neg)
*--p = '-';
while (*p)
tputc(*p++, shf);
break;
case 'T': /* format tree */
ptree(va_arg(va, struct op *), shf);
break;
case ';': /* newline or ; */
p = (shf->flags & SHF_STRING) ? "; " : newline;
while (*p)
tputc(*p++, shf);
break;
case 'R':
pioact(shf, va_arg(va, struct ioword *));
break;
default:
tputc(c, shf);
break;
}
} else
tputc(c, shf);
}

/*
* copy tree (for function definition)
*/

struct op *
tcopy(t, ap)
register struct op *t;
Area *ap;
{
register struct op *r;
register char **tw, **rw;

if (t == NULL)
return NULL;

r = (struct op *) alloc(sizeof(struct op), ap);

r->type = t->type;
r->evalflags = t->evalflags;

r->str = t->type == TCASE ? wdcopy(t->str, ap) : strsave(t->str, ap);

if (t->vars == NULL)
r->vars = NULL;
else {
for (tw = t->vars; *tw++ != NULL; )
;
rw = r->vars = (char **)
alloc((int)(tw - t->vars) * 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((int)(tw - t->args) * 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);

return r;
}

char *
wdcopy(wp, ap)
const char *wp;
Area *ap;
{
size_t len = wdscan(wp, EOS) - wp;
return memcpy(alloc(len, ap), wp, len);
}

/* return the position of prefix c in wp plus 1 */
const char *
wdscan(wp, c)
register const char *wp;
register int c;
{
register int nest = 0;

while (1)
switch (*wp++) {
case EOS:
return wp;
case CHAR:
case QCHAR:
wp++;
break;
case OQUOTE:
case CQUOTE:
break;
case OSUBST:
nest++;
while (*wp++ != '\0')
;
break;
case CSUBST:
if (c == CSUBST && nest == 0)
return wp;
nest--;
break;
case COMSUB:
case EXPRSUB:
while (*wp++ != 0)
;
break;
}
}

static struct ioword **
iocopy(iow, ap)
register struct ioword **iow;
Area *ap;
{
register struct ioword **ior;
register int i;

for (ior = iow; *ior++ != NULL; )
;
ior = (struct ioword **) alloc((int)(ior - iow) * sizeof(*ior), ap);

for (i = 0; iow[i] != NULL; i++) {
register struct ioword *p, *q;

p = iow[i];
q = (struct ioword *) alloc(sizeof(*p), ap);
ior[i] = q;
*q = *p;
if (p->name != NULL)
q->name = wdcopy(p->name, ap);
}
ior[i] = NULL;

return ior;
}

/*
* free tree (for function definition)
*/

void
tfree(t, ap)
register struct op *t;
Area *ap;
{
register char **w;

if (t == NULL)
return;

if (t->str != NULL)
afree((void*)t->str, ap);

if (t->vars != NULL) {
for (w = t->vars; *w != NULL; w++)
afree((void*)*w, ap);
afree((void*)t->vars, ap);
}

if (t->args != NULL) {
for (w = t->args; *w != NULL; w++)
afree((void*)*w, ap);
afree((void*)t->args, ap);
}

if (t->ioact != NULL)
iofree(t->ioact, ap);

tfree(t->left, ap);
tfree(t->right, ap);

afree((void*)t, ap);
}

static void
iofree(iow, ap)
struct ioword **iow;
Area *ap;
{
register struct ioword **iop;
register struct ioword *p;

for (iop = iow; (p = *iop++) != NULL; ) {
if (p->name != NULL)
afree((void*)p->name, ap);
afree((void*)p, ap);
}
}
pdksh-5.1.2/tty.c100644 0 133 6340 5666702675 12461 0ustar rootlsource#include "sh.h"
#define EXTERN
#include "tty.h"
#undef EXTERN

int
get_tty(fd, ts)
int fd;
TTY_state *ts;
{
int ret;

# ifdef HAVE_TERMIOS_H
ret = tcgetattr(fd, ts);
# else /* HAVE_TERIOS_H */
# ifdef HAVE_TERMIO_H
ret = ioctl(fd, TCGETA, ts);
# else /* HAVE_TERMIO_H */
ret = ioctl(fd, TIOCGETP, &ts->sgttyb);
# ifdef TIOCGATC
if (ioctl(fd, TIOCGATC, &ts->lchars) < 0)
ret = -1;
# else
if (ioctl(fd, TIOCGETC, &ts->tchars) < 0)
ret = -1;
# ifdef TIOCGLTC
if (ioctl(fd, TIOCGLTC, &ts->ltchars) < 0)
ret = -1;
# endif /* TIOCGLTC */
# endif /* TIOCGATC */
# endif /* HAVE_TERMIO_H */
# endif /* HAVE_TERIOS_H */
return ret;
}

int
set_tty(fd, ts, flags)
int fd;
TTY_state *ts;
int flags;
{
int ret = 0;

# ifdef HAVE_TERMIOS_H
ret = tcsetattr(fd, TCSADRAIN, ts);
# else /* HAVE_TERIOS_H */
# ifdef HAVE_TERMIO_H
# ifndef TCSETAW /* e.g. Cray-2 */
/* first wait for output to drain */
# ifdef TCSBRK
if (ioctl(tty_fd, TCSBRK, 1) < 0)
ret = -1;
# else /* the following kludge is minimally intrusive, but sometimes fails */
if (flags & TF_WAIT)
sleep((unsigned)1); /* fake it */
# endif
# endif /* !TCSETAW */
# if defined(_BSD_SYSV) || !defined(TCSETAW)
/* _BSD_SYSV must force TIOCSETN instead of TIOCSETP (preserve type-ahead) */
if (ioctl(tty_fd, TCSETA, ts) < 0)
ret = -1;
# else
if (ioctl(tty_fd, TCSETAW, ts) < 0)
ret = -1;
# endif
# else /* HAVE_TERMIO_H */
# if defined(__mips) && (defined(_SYSTYPE_BSD43) || defined(__SYSTYPE_BSD43))
/* Under RISC/os 5.00, bsd43 environment, after a tty driver
* generated interrupt (eg, INTR, TSTP), all output to tty is
* lost until a SETP is done (there must be a better way of
* doing this...).
*/
if (flags & TF_MIPSKLUDGE)
ret = ioctl(fd, TIOCSETP, &ts->sgttyb);
else
# endif /* _SYSTYPE_BSD43 */
ret = ioctl(fd, TIOCSETN, &ts->sgttyb);
# ifdef TIOCGATC
if (ioctl(fd, TIOCSATC, &ts->lchars) < 0)
ret = -1;
# else
if (ioctl(fd, TIOCSETC, &ts->tchars) < 0)
ret = -1;
# ifdef TIOCGLTC
if (ioctl(fd, TIOCSLTC, &ts->ltchars) < 0)
ret = -1;
# endif /* TIOCGLTC */
# endif /* TIOCGATC */
# endif /* HAVE_TERMIO_H */
# endif /* HAVE_TERIOS_H */
return ret;
}


/* Initialize tty_fd. Used for saving/reseting tty modes upon
* foreground job completion and for setting up tty process group.
*/
void
tty_init(init_ttystate)
int init_ttystate;
{
int do_close = 1;
int tfd;

if (tty_fd >= 0) {
close(tty_fd);
tty_fd = -1;
}
tty_devtty = 1;
if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) {
/* X11R5 xterm on mips doesn't set controlling tty properly - temporary hack */
#if !defined(__mips) || !(defined(_SYSTYPE_BSD43) || defined(__SYSTYPE_BSD43))
tty_devtty = 0;
#endif
do_close = 0;
if (isatty(0))
tfd = 0;
else if (isatty(2))
tfd = 2;
else
return;
}
if ((tty_fd = ksh_dupbase(tfd, FDBASE)) < 0) {
warningf(FALSE, "j_ttyinit: dup of tty fd failed: %s\n",
strerror(errno));
} else if (fd_clexec(tty_fd) < 0) {
warningf(FALSE, "j_ttyinit: can't set close-on-exec flag: %s\n",
strerror(errno));
close(tty_fd);
tty_fd = -1;
} else if (init_ttystate)
get_tty(tty_fd, &tty_state);
if (do_close)
close(tfd);
}

void
tty_close()
{
if (tty_fd >= 0) {
close(tty_fd);
tty_fd = -1;
}
}
pdksh-5.1.2/var.c100644 0 133 55614 5666434227 12454 0ustar rootlsource#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: var.c,v 1.2 1994/05/19 18:32:40 michael Exp michael $";
#endif

#include "sh.h"
#include "ksh_time.h"
#include "ksh_limval.h"
#include "ksh_stat.h"
#include

/*
* Variables
*
* WARNING: unreadable code, needs a rewrite
*
* if (flag&INTEGER), val.i contains integer value, and type contains base.
* otherwise, (val.s + type) contains string value.
* if (flag&EXPORT), val.s contains "name=value" for E-Z exporting.
*/
static struct tbl vtemp;
static struct table specials;
static char *formatstr ARGS((struct tbl *vp, char *s));
static void export ARGS((struct tbl *vp, char *val));
static int special ARGS((const char *name));
static void getspec ARGS((struct tbl *vp));
static void setspec ARGS((struct tbl *vp));
static void unsetspec ARGS((struct tbl *vp));
static struct tbl *arraysearch ARGS((struct tbl *, int));

/*
* create a new block for function calls and simple commands
* assume caller has allocated and set up e->loc
*/
void
newblock()
{
register struct block *l;
static char *empty[] = {null};

l = (struct block *) alloc(sizeof(struct block), ATEMP);
ainit(&l->area);
l->argc = 0;
l->argv = empty;
l->exit = l->error = NULL;
tinit(&l->vars, &l->area, 0);
tinit(&l->funs, &l->area, 0);
l->next = e->loc;
e->loc = l;
}

/*
* pop a block handling special variables
*/
void
popblock()
{
register struct block *l = e->loc;
register struct tbl *vp, **vpp = l->vars.tbls, *vq;
register int i;

e->loc = l->next; /* pop block */
kshname = e->loc->argv[0];
for (i = l->vars.size; --i >= 0; )
if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL))
if ((vq = global(vp->name))->flag & ISSET)
setspec(vq);
else
unsetspec(vq);
afreeall(&l->area);
afree(l, ATEMP);
}

/* called by main() to initialize variable data structures */
void
initvar()
{
static struct {
char *name;
int v;
} names[] = {
{ "COLUMNS", V_COLUMNS },
{ "IFS", V_IFS },
{ "MAIL", V_MAIL },
{ "MAILCHECK", V_MAILCHECK },
{ "MAILPATH", V_MAILPATH },
{ "OPTIND", V_OPTIND },
{ "PATH", V_PATH },
{ "POSIXLY_CORRECT", V_POSIXLY_CORRECT },
{ "TMPDIR", V_TMPDIR },
#ifdef KSH
{ "EDITOR", V_EDITOR },
{ "HISTFILE", V_HISTFILE },
{ "HISTSIZE", V_HISTSIZE },
{ "RANDOM", V_RANDOM },
{ "SECONDS", V_SECONDS },
{ "TMOUT", V_TMOUT },
{ "VISUAL", V_VISUAL },
#endif /* KSH */
{ (char *) 0, 0 }
};
int i;
struct tbl *tp;

tinit(&specials, APERM, 32); /* must be 2^n (currently 16 speciasl) */
for (i = 0; names[i].name; i++) {
tp = tenter(&specials, names[i].name, hash(names[i].name));
tp->flag = DEFINED|ISSET;
tp->type = names[i].v;
}
}

/*
* Search for variable, if not found create globally.
*/
struct tbl *
global(n)
register const char *n;
{
register struct block *l = e->loc;
register struct tbl *vp;
register int c;
unsigned h;
int array = 0, UNINITIALIZED(val), len;
char *p;

/* Check to see if this is an array */
p = skip_varname(n, FALSE);
if (p != n && *p == '[' && (len = array_ref_len(p))) {
char *sub, *tmp;

/* Calculate the value of the subscript */
array = 1;
tmp = strnsave(p+1, len-2, ATEMP);
sub = substitute(tmp, 0);
afree(tmp, ATEMP);
n = strnsave(n, p - n, ATEMP);
val = evaluate(sub);
if (val < 0 || val > ARRAYMAX)
errorf("%s: subscript out of range", n);
afree(sub, ATEMP);
}
h = hash(n);
c = n[0];
if (digit(c)) {
if (array)
errorf("bad substitution");
vp = &vtemp;
vp->flag = (DEFINED|RDONLY);
vp->type = 0;
vp->areap = ATEMP;
*vp->name = c; /* should strncpy */
for (c = 0; digit(*n); n++)
c = c*10 + *n-'0';
if (c <= l->argc)
setstr(vp, l->argv[c]);
return vp;
}
if (!letter(c)) {
if (array)
errorf("bad substitution");
vp = &vtemp;
vp->flag = (DEFINED|RDONLY);
vp->type = 0;
vp->areap = ATEMP;
*vp->name = c;
if (n[1] != '\0')
return vp;
vp->flag |= ISSET|INTEGER;
switch (c) {
case '$':
vp->val.i = kshpid;
break;
case '!':
/* If no job, expand to nothing */
if ((vp->val.i = j_async()) == 0)
vp->flag &= ~(ISSET|INTEGER);
break;
case '?':
vp->val.i = exstat;
break;
case '#':
vp->val.i = l->argc;
break;
case '-':
vp->flag &= ~INTEGER;
vp->val.s = getoptions();
break;
default:
vp->flag &= ~(ISSET|INTEGER);
}
return vp;
}
for (l = e->loc; l != NULL; l = l->next) {
vp = tsearch(&l->vars, n, h);
if (vp != NULL)
if (array)
return arraysearch(vp, val);
else
return vp;
if (l->next == NULL)
break;
}
vp = tenter(&l->vars, n, h);
if (array)
vp = arraysearch(vp, val);
vp->flag |= DEFINED;
if (special(n))
vp->flag |= SPECIAL;
return vp;
}

/*
* Search for local variable, if not found create locally.
*/
struct tbl *
local(n)
register const char *n;
{
register struct block *l = e->loc;
register struct tbl *vp;
unsigned h;
int array = 0, UNINITIALIZED(val), len;
char *p;

/* Check to see if this is an array */
p = skip_varname(n, FALSE);
if (p != n && *p == '[' && (len = array_ref_len(p))) {
char *sub, *tmp;

/* Calculate the value of the subscript */
array = 1;
tmp = strnsave(p+1, len-2, ATEMP);
sub = substitute(tmp, 0);
afree(tmp, ATEMP);
n = strnsave(n, p - n, ATEMP);
val = evaluate(sub);
if (val < 0 || val > ARRAYMAX)
errorf("%s: subscript out of range", n);
afree(sub, ATEMP);
}
h = hash(n);
if (!letter(*n)) {
vp = &vtemp;
vp->flag = (DEFINED|RDONLY);
vp->type = 0;
vp->areap = ATEMP;
return vp;
}
vp = tenter(&l->vars, n, h);
if (array)
vp = arraysearch(vp, val);
vp->flag |= DEFINED;
if (special(n))
vp->flag |= SPECIAL;
return vp;
}

/* get variable string value */
char *
strval(vp)
register struct tbl *vp;
{
char *s;

if ((vp->flag&SPECIAL))
getspec(vp);
if (!(vp->flag&ISSET))
s = null; /* special to dollar() */
else if (!(vp->flag&INTEGER)) /* string source */
s = vp->val.s + vp->type;
else { /* integer source */
/* worst case number length is when base=2, so use BITS(long) */
/* minus base # number null */
static char strbuf[1 + 2 + 1 + BITS(long) + 1];
char *digits = (vp->flag & UCASEV_AL) ?
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
: "0123456789abcdefghijklmnopqrstuvwxyz";
register unsigned long n;
register int base;

s = strbuf + sizeof(strbuf);
if (vp->flag & INT_U)
n = (unsigned long) vp->val.i;
else
n = (vp->val.i < 0) ? -vp->val.i : vp->val.i;
base = (vp->type == 0) ? 10 : vp->type;

*--s = '\0';
do {
*--s = digits[n % base];
n /= base;
} while (n != 0);
if (base != 10) {
*--s = '#';
*--s = digits[base % 10];
if (base >= 10)
*--s = digits[base / 10];
}
if (!(vp->flag & INT_U) && vp->val.i < 0)
*--s = '-';
if (vp->flag & (RJUST|LJUST)) /* case already dealt with */
s = formatstr(vp, s);
}
return s;
}

/* get variable integer value, with error checking */
long
intval(vp)
register struct tbl *vp;
{
long num;
int base;

base = getint(vp, &num);
if (base == -1)
/* XXX check calls - is error here ok by POSIX? */
errorf("%s: bad number", strval(vp));
return num;
}

/* set variable to string value */
void
setstr(vq, s)
register struct tbl *vq;
char *s;
{
if (!(vq->flag&INTEGER)) { /* string dest */
if ((vq->flag&ALLOC)) {
/* debugging */
if (s >= vq->val.s
&& s <= vq->val.s + strlen(vq->val.s))
internal_errorf(TRUE,
"setstr: assigning to self");
afree((void*)vq->val.s, vq->areap);
}
vq->flag &= ~(ISSET|ALLOC);
vq->type = 0;
if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
s = formatstr(vq, s);
if ((vq->flag&EXPORT))
export(vq, s);
else {
vq->val.s = strsave(s, vq->areap);
if (vq->val.s) /* don't lie */
vq->flag |= ALLOC;
}
} else /* integer dest */
v_evaluate(vq, s);
vq->flag |= ISSET;
if ((vq->flag&SPECIAL))
setspec(vq);
}

/* set variable to integer */
void
setint(vq, n)
register struct tbl *vq;
long n;
{
if (!(vq->flag&INTEGER)) {
register struct tbl *vp = &vtemp;
vp->flag = (ISSET|INTEGER);
vp->type = 0;
vp->areap = ATEMP;
vp->val.i = n;
setstr(vq, strval(vp));
} else
vq->val.i = n;
vq->flag |= ISSET;
if ((vq->flag&SPECIAL))
setspec(vq);
}

int
getint(vp, nump)
struct tbl *vp;
long *nump;
{
register char *s;
register int c;
int base, neg;
long num;

if (vp->flag&SPECIAL)
getspec(vp);
/* XXX is it possible for ISSET to be set and val.s to be 0? */
if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL))
return -1;
if (vp->flag&INTEGER) {
*nump = vp->val.i;
return vp->type;
}
s = vp->val.s + vp->type;
if (s == NULL) /* redundent given initial test */
s = null;
base = 10;
num = 0;
neg = 0;
for (c = *s++; c ; c = *s++) {
if (c == '-') {
neg++;
} else if (c == '#') {
base = (int) num;
if (base < 2 || base > 36)
return -1;
num = 0;
} else if (letnum(c)) {
if (isdigit(c))
c -= '0';
else if (islower(c))
c -= 'a' - 10; /* todo: assumes ascii */
else if (isupper(c))
c -= 'A' - 10; /* todo: assumes ascii */
else
c = -1; /* _: force error */
if (c < 0 || c >= base)
return -1;
num = num * base + c;
} else
return -1;
}
if (neg)
num = -num;
*nump = num;
return base;
}

/* convert variable vq to integer variable, setting its value from vp
* (vq and vp may be the same)
*/
struct tbl *
strint(vq, vp)
register struct tbl *vq, *vp;
{
int base;
long num;

if ((base = getint(vp, &num)) == -1)
return NULL;
if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
vq->flag &= ~ALLOC;
afree(vq->val.s, vq->areap);
}
vq->val.i = num;
if (vq->type == 0) /* default base */
vq->type = base;
vq->flag |= ISSET|INTEGER;
if (vq->flag&SPECIAL)
setspec(vq);
return vq;
}

static char *
formatstr(vp, s)
struct tbl *vp;
char *s;
{
int olen, nlen;
char *p;

olen = strlen(s);

if (vp->flag & (RJUST|LJUST)) {
if (!vp->field) /* default field width */
vp->field = olen;
nlen = vp->field;
} else
nlen = olen;

p = (char *) alloc(nlen + 1, ATEMP);
if (vp->flag & (RJUST|LJUST)) {
char *fmt;

if (vp->flag & RJUST) {
char *q = s + olen;
/* strip trailing spaces (at&t ksh uses q[-1] == ' ') */
while (q > s && isspace(q[-1]))
--q;
*q = '\0';
if (q - s > vp->field)
s += (q - s) - vp->field;
fmt = ((vp->flag & ZEROFIL) && digit(*s)) ?
"%0*.*s"
: "%*.*s";
} else {
/* strip leading spaces/zeros */
while (isspace(*s))
s++;
if (vp->flag & ZEROFIL)
while (*s == '0')
s++;
fmt = "%-*.*s";
}
shf_snprintf(p, nlen + 1, fmt,
vp->field, vp->field, s);
} else
memcpy(p, s, olen + 1);

if (vp->flag & UCASEV_AL) {
for (s = p; *s; s++)
if (islower(*s))
*s = toupper(*s);
} else if (vp->flag & LCASEV) {
for (s = p; *s; s++)
if (isupper(*s))
*s = tolower(*s);
}

return p;
}

/*
* make vp->val.s be "name=value" for quick exporting.
*/
static void
export(vp, val)
register struct tbl *vp;
char *val;
{
register char *xp;
char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
int namelen = strlen(vp->name);
int vallen = strlen(val) + 1;

vp->flag |= ALLOC;
xp = (char*)alloc(namelen + 1 + vallen, vp->areap);
memcpy(vp->val.s = xp, vp->name, namelen);
xp += namelen;
*xp++ = '=';
vp->type = xp - vp->val.s; /* offset to value */
memcpy(xp, val, vallen);
if (op != NULL)
afree((void*)op, vp->areap);
}

/*
* lookup variable (according to (set&LOCAL)),
* set its attributes (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL,
* LCASEV, UCASEV_AL), and optionally set its value if an assignment.
*/
struct tbl *
typeset(var, set, clr, field, base)
register char *var;
int clr, set;
int field, base;
{
register struct tbl *vp;
struct tbl *vpbase, *t;
register char *tvar, *val;

/* check for valid variable name, search for value */
val = skip_varname(var, FALSE);
if (val == var)
return NULL;
if (*val == '[') {
int len;

len = array_ref_len(val);
if (len == 0)
return NULL;
/* IMPORT is only used when the shell starts up and is
* setting up its environment. Allow only simple array
* references at this time since parameter/command substitution
* is preformed on the [expression], which would be a major
* security hole.
*/
if (set & IMPORT) {
int i;
for (i = 1; i < len - 1; i++)
if (!digit(val[i]))
return NULL;
}
val += len;
}
if (*val == '=')
tvar = strnsave(var, val++ - var, ATEMP);
else {
/* Importing from original envirnment: must have an = */
if (set & IMPORT)
return NULL;
tvar = var;
val = NULL;
}

/* Prevent typeset from creating a local PATH/ENV/SHELL */
if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0
|| strcmp(tvar, "ENV") == 0
|| strcmp(tvar, "SHELL") == 0))
errorf("%s: restricted", tvar);

vp = (set&LOCAL) ? local(tvar) : global(tvar);
set &= ~LOCAL;
if (val)
afree(tvar, ATEMP);

vpbase = (vp->flag & ARRAY) ? global(basename(var)) : vp;

/* only allow export flag to be set. at&t ksh allows any attribute to
* be changed, which means it can be truncated or modified
* (-L/-R/-Z/-i).
*/
if ((vpbase->flag&RDONLY)
&& (val || clr || (set & ~EXPORT)))
/* XXX check calls - is error here ok by POSIX? */
errorf("%s: is read only", var);

/* most calls are with set/clr == 0 */
if (set | clr) {
/* XXX if x[0] isn't set, there will be problems: need to have
* one copy of attributes for arrays...
*/
for (t = vpbase; t; t = t->array) {
int fake_assign;
char UNINITIALIZED(*s);
char UNINITIALIZED(*free_me);

fake_assign = (t->flag & ISSET) && (!val || t != vp)
&& ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL))
|| ((t->flag & INTEGER) && (clr & INTEGER))
|| (!(t->flag & INTEGER) && (set & INTEGER)));
if (fake_assign) {
if (t->flag & INTEGER) {
s = strval(t);
free_me = (char *) 0;
} else {
s = t->val.s + t->type;
free_me = (t->flag & ALLOC) ? t->val.s
: (char *) 0;
}
t->flag &= ~ALLOC;
}
if (!(t->flag & INTEGER) && (set & INTEGER)) {
t->type = 0;
t->flag &= ~ALLOC;
}
t->flag = (t->flag | set) & ~clr;
/* Don't change base if assignment is to be done,
* in case assignment fails.
*/
if ((set & INTEGER) && base > 0 && (!val || t != vp))
t->type = base;
if (set & (LJUST|RJUST|ZEROFIL))
t->field = field;
if (fake_assign) {
setstr(t, s);
if (free_me)
afree((void *) free_me, t->areap);
}
}
}

if (val != NULL) {
if (vp->flag&INTEGER) {
/* do not zero base before assignment */
setstr(vp, val);
/* Done after assignment to override default */
if (base > 0)
vp->type = base;
} else
setstr(vp, val);
}

/* only x[0] is ever exported, so use vpbase */
if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER)
&& vpbase->type == 0)
export(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);

return vp;
}

void
unset(vp)
register struct tbl *vp;
{
if ((vp->flag&ALLOC))
afree((void*)vp->val.s, vp->areap);
vp->flag &= SPECIAL; /* Should ``unspecial'' some vars */
if (vp->flag & SPECIAL)
unsetspec(vp);
}

/* return a pointer to the first char past a legal variable name (returns the
* argument if there is no legal name, returns * a pointer to the terminating
* null if whole string is legal).
*/
char *
skip_varname(s, aok)
const char *s;
int aok;
{
int alen;

if (!s || !letter(*s))
return (char *) s;
while (*++s && letnum(*s))
;
if (aok && *s == '[' && (alen = array_ref_len(s)))
s += alen;
return (char *) s;
}

/* Return a pointer to the first character past any legal variable name. */
char *
skip_wdvarname(s, aok)
char *s;
int aok; /* skip array de-reference? */
{
if (s[0] == CHAR && letter(s[1])) {
do
s += 2;
while (s[0] == CHAR && letnum(s[1]));
if (aok) {
/* skip possible array de-reference */
char *p = s;
char c;
int depth = 0;

while (1) {
if (p[0] != CHAR)
break;
c = p[1];
p += 2;
if (c == '[')
depth++;
else if (c == ']' && --depth == 0) {
s = p;
break;
}
}
}
}
return s;
}

/* Check if coded string s is a variable name */
int
is_wdvarname(s, aok)
char *s;
int aok;
{
char *p = skip_wdvarname(s, aok);

return p != s && p[0] == EOS;
}

/* Check if coded string s is a variable assignment */
int
is_wdvarassign(s)
char *s;
{
char *p = skip_wdvarname(s, TRUE);

return p != s && p[0] == CHAR && p[1] == '=';
}

/*
* Make the exported environment from the exported names in the dictionary.
*/
char **
makenv()
{
struct block *l = e->loc;
XPtrV env;
register struct tbl *vp, **vpp;
register int i;

XPinit(env, 64);
for (l = e->loc; l != NULL; l = l->next)
for (vpp = l->vars.tbls, i = l->vars.size; --i >= 0; )
if ((vp = *vpp++) != NULL
&& (vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) {
register struct block *l2;
register struct tbl *vp2;
unsigned h = hash(vp->name);

/* unexport any redefined instances */
for (l2 = l->next; l2 != NULL; l2 = l2->next) {
vp2 = tsearch(&l2->vars, vp->name, h);
if (vp2 != NULL)
vp2->flag &= ~EXPORT;
}
if ((vp->flag&INTEGER)) {
/* integer to string */
char *val;
val = strval(vp);
vp->flag &= ~INTEGER;
setstr(vp, val);
}
XPput(env, vp->val.s);
}
XPput(env, NULL);
return (char **) XPclose(env);
}

/*
* handle special variables with side effects - PATH, SECONDS.
*/

/* Test if name is a special parameter */
static int
special(name)
register const char * name;
{
register struct tbl *tp;

tp = tsearch(&specials, name, hash(name));
return tp ? tp->type : V_NONE;
}

#ifdef KSH
static time_t seconds; /* time SECONDS last set */
#endif /* KSH */

static void
getspec(vp)
register struct tbl *vp;
{
switch (special(vp->name)) {
#ifdef KSH
case V_SECONDS:
vp->flag &= ~SPECIAL;
setint(vp, (long) (time((time_t *)0) - seconds));
vp->flag |= SPECIAL;
break;
case V_RANDOM:
vp->flag &= ~SPECIAL;
setint(vp, (long) (rand() & 0x7fff));
vp->flag |= SPECIAL;
break;
case V_HISTSIZE:
vp->flag &= ~SPECIAL;
setint(vp, (long) histsize);
vp->flag |= SPECIAL;
break;
#endif /* KSH */
}
}

static void
setspec(vp)
register struct tbl *vp;
{
extern void mbset(), mpset();
char *s;

switch (special(vp->name)) {
case V_PATH:
path = strval(vp);
flushcom(1); /* clear tracked aliases */
break;
case V_IFS:
setctypes(s = strval(vp), C_IFS);
ifs0 = *s;
break;
case V_OPTIND:
getopts_reset((int) intval(vp));
break;
case V_MAIL:
mbset(strval(vp));
break;
case V_MAILPATH:
mpset(strval(vp));
break;
case V_MAILCHECK:
/* mail_check_set(intval(vp)); */
break;
case V_POSIXLY_CORRECT:
change_flag(FPOSIX, OF_SPECIAL, 1);
break;
case V_TMPDIR:
if (tmpdir) {
afree(tmpdir, APERM);
tmpdir = (char *) 0;
}
/* Use tmpdir iff it is an absolute path, is writable and
* searchable and is a directory...
*/
{
struct stat statb;
s = strval(vp);
if (ISDIRSEP(s[0]) && eaccess(s, W_OK|X_OK) == 0
&& stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
tmpdir = strsave(s, APERM);
}
break;
#ifdef KSH
case V_RANDOM:
vp->flag &= ~SPECIAL;
srand((unsigned int)intval(vp));
vp->flag |= SPECIAL;
break;
case V_SECONDS:
vp->flag &= ~SPECIAL;
seconds = time((time_t*) 0) - intval(vp);
vp->flag |= SPECIAL;
break;
case V_HISTSIZE:
vp->flag &= ~SPECIAL;
sethistsize((int) intval(vp));
vp->flag |= SPECIAL;
break;
case V_HISTFILE:
sethistfile(strval(vp));
break;
# ifdef EDIT
case V_VISUAL:
set_editmode(strval(vp));
break;
case V_EDITOR:
if (!(global("VISUAL")->flag & ISSET))
set_editmode(strval(vp));
break;
case V_COLUMNS:
if ((x_cols = intval(vp)) <= 0)
x_cols = 16;
break;
# endif /* EDIT */
case V_TMOUT:
/* at&t ksh seems to do this (only listen if integer) */
if (vp->flag & INTEGER)
ksh_tmout = vp->val.i >= 0 ? vp->val.i : 0;
break;
#endif /* KSH */
}
}

static void
unsetspec(vp)
register struct tbl *vp;
{
extern void mbset(), mpset();

switch (special(vp->name)) {
case V_PATH:
path = def_path;
flushcom(1); /* clear tracked aliases */
break;
case V_IFS:
setctypes(" \t\n", C_IFS);
ifs0 = ' ';
break;
case V_MAIL:
mbset((char *) 0);
break;
case V_MAILPATH:
mpset((char *) 0);
break;
case V_TMPDIR:
/* should not become unspecial */
if (tmpdir) {
afree(tmpdir, APERM);
tmpdir = (char *) 0;
}
break;
#ifdef KSH
case V_TMOUT:
/* at&t ksh doesn't do this. TMOUT becomes unspecial so
* future assignments don't have effect. Could be
* useful (eg, after "TMOUT=60; unset TMOUT", user
* can't get rid of the timeout...). Should be handled
* by generic unset code...
*/
ksh_tmout = 0;
break;
#endif /* KSH */
/* todo: generic action for specials (at&t says variables
* loose their special meaning when unset but global() checks
* the name of new vars to see if they are special)
* loose meaning: SECONDS, RANDOM, COLUMNS
* unknown: OPTIND, MAIL, MAILPATH, MAILCHECK, HISTSIZE, HISTFILE,
* VISUAL, EDITOR
* no effect: POSIXLY_CORRECT (use set +o posix instead)
*/
}
}

/*
* Search for (and possibly create) a table entry starting with
* vp, indexed by val.
*/
static struct tbl *
arraysearch(vp, val)
struct tbl *vp;
int val;
{
struct tbl *prev, *curr, *new;

vp->flag |= ARRAY|DEFINED;

/* The table entry is always [0] */
if (val == 0) {
vp->index = 0;
return vp;
}
prev = vp;
curr = vp->array;
while (curr && curr->index < val) {
prev = curr;
curr = curr->array;
}
if (curr && curr->index == val) {
if (curr->flag&ISSET)
return curr;
else
new = curr;
} else
new = (struct tbl *)alloc(sizeof(struct tbl)+strlen(vp->name)+1, vp->areap);
strcpy(new->name, vp->name);
new->flag = vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL);
new->type = vp->type;
new->areap = vp->areap;
new->field = vp->field;
new->index = val;
if (curr != new) { /* not reusing old array entry */
prev->array = new;
new->array = curr;
}
return new;
}

/* Return the length of an array reference (eg, [1+2]) - cp is assumed
* to point to the open bracket. Returns 0 if there is no matching closing
* bracket.
*/
int
array_ref_len(cp)
const char *cp;
{
const char *s = cp;
int c;
int depth = 0;

while ((c = *s++) && (c != ']' || --depth))
if (c == '[')
depth++;
if (!c)
return 0;
return s - cp;
}

/*
* Make a copy of the base of an array name
*/
char *
basename(str)
char *str;
{
char *p;

if ((p = strchr(str, '[')) == 0)
/* Shouldn't happen, but why worry? */
return str;

return strnsave(str, p - str, ATEMP);
}

/* Set (or overwrite, if !reset) the array variable var to the values in vals.
*/
void
set_array(var, reset, vals)
char *var;
int reset;
char **vals;
{
struct tbl *vp, *vq;
int i;

/* to get local array, use "typeset foo; set -A foo" */
vp = global(var);

/* Note: at&t ksh allows set -A but not set +A of a read-only var */
if ((vp->flag&RDONLY))
errorf("%s: is read only", var);
/* This code is quite non-optimal */
if (reset > 0)
/* trash existing values and attributes */
unset(vp);
for (i = 0; vals[i]; i++) {
vq = arraysearch(vp, i);
setstr(vq, vals[i]);
}
}
pdksh-5.1.2/version.c100644 0 133 360 5670633175 13273 0ustar rootlsource/*
* value of $KSH_VERSION (or $SH_VERSION)
*/

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: version.c,v 1.2 1994/05/19 18:32:40 michael Exp michael $";
#endif

char ksh_version [] =
"@(#)PD KSH v5.1.2 94/12/05";
pdksh-5.1.2/vi.c100644 0 133 114701 5666416430 12307 0ustar rootlsource/*
* vi command editing
* written by John Rochester (initially for nsh)
* bludgeoned to fit pdksh by Larry Bouzane, Jeff Sparkes & Eric Gisin
*
*/
#include "config.h"
#ifdef VI

#if !defined(lint) && !defined(no_RCSids)
static char *RCSid = "$Id: vi.c,v 1.3 1994/05/31 13:34:34 michael Exp michael $";
#endif

#include "sh.h"
#include
#include "ksh_stat.h" /* completion */
#include "edit.h"

#define CMDLEN 1024
#define Ctrl(c) (c&0x1f)
#define iswordch(c) (letnum(c))

typedef struct glob Glob;

static void vi_reset ARGS((char *buf, size_t len));
static int nextstate ARGS((int ch));
static int vi_insert ARGS((int ch));
static int vi_cmd ARGS((int argcnt, char *cmd));
static int domove ARGS((int argcnt, char *cmd, int sub));
static int redo_insert ARGS((int count));
static void yank_range ARGS((int a, int b));
static int bracktype ARGS((int ch));
static void save_cbuf ARGS((void));
static void restore_cbuf ARGS((void));
static void edit_reset ARGS((char *buf, size_t len));
static int putbuf ARGS((char *buf, int len, int repl));
static void del_range ARGS((int a, int b));
static int findch ARGS((int ch, int cnt, int forw, int incl));
static int forwword ARGS((int argcnt));
static int backword ARGS((int argcnt));
static int endword ARGS((int argcnt));
static int Forwword ARGS((int argcnt));
static int Backword ARGS((int argcnt));
static int Endword ARGS((int argcnt));
static int grabhist ARGS((int save, int n));
static int grabsearch ARGS((int save, int start, int fwd, char *pat));
static void redraw_line ARGS((void));
static void refresh ARGS((int leftside));
static int outofwin ARGS((void));
static void rewindow ARGS((void));
static int newcol ARGS((int ch, int col));
static void display ARGS((char *wb1, char *wb2, int leftside));
static void ed_mov_opt ARGS((int col, char *wb));
static char **globstr ARGS((char *cp));
static int glob_word ARGS((Glob *g, int command));
static int expand_word ARGS((int command));
static int complete_word ARGS((int command));
static int print_expansions ARGS((int command));
static int char_len ARGS((int c));
static void x_vi_zotc ARGS((int c));

#define C_ 0x1
#define M_ 0x2
#define E_ 0x4
#define X_ 0x8
#define U_ 0x10
#define B_ 0x20
#define S_ 0x80

#define isbad(c) (classify[(c)&0x7f]&B_)
#define iscmd(c) (classify[(c)&0x7f]&(M_|E_|C_|U_))
#define ismove(c) (classify[(c)&0x7f]&M_)
#define isextend(c) (classify[(c)&0x7f]&E_)
#define islong(c) (classify[(c)&0x7f]&X_)
#define isundoable(c) (!(classify[(c)&0x7f]&U_))
#define issrch(c) (classify[(c)&0x7f]&S_)

char classify[128] = {
/* 0 1 2 3 4 5 6 7 */
/* 0 */ B_, 0, 0, 0, 0, C_|U_, C_|U_, 0,
/* 01 */ C_|M_, 0, 0, 0, C_|U_, 0, C_, 0,
/* 02 */ C_, 0, C_|U_, 0, 0, 0, 0, 0,
/* 03 */ 0, 0, C_, 0, 0, 0, 0, 0,
/* 04 */ M_, 0, 0, C_, M_, M_, 0, 0,
/* 05 */ 0, 0, C_, C_, M_, C_, 0, C_|S_,
/* 06 */ M_, 0, 0, 0, 0, 0, 0, 0,
/* 07 */ 0, 0, 0, M_, 0, C_, 0, C_|S_,
/* 010 */ 0, C_, M_, C_, C_, M_, M_|X_, C_,
/* 011 */ 0, C_, 0, 0, 0, 0, C_, 0,
/* 012 */ C_, 0, C_, C_, M_|X_, 0, 0, M_,
/* 013 */ C_, C_, 0, 0, C_, 0, M_, C_,
/* 014 */ 0, C_, M_, E_, E_, M_, M_|X_, C_,
/* 015 */ M_, C_, C_, C_, M_, 0, C_, 0,
/* 016 */ C_, 0, X_, C_, M_|X_, C_|U_, C_, M_,
/* 017 */ C_, E_, 0, 0, M_, 0, C_, 0
};

#define MAXVICMD 3
#define SRCHLEN 40

#define INSERT 1
#define REPLACE 2

#define VNORMAL 0 /* command, insert or replace mode */
#define VARG1 1 /* digit prefix (first, eg, 5l) */
#define VEXTCMD 2 /* cmd + movement (eg, cl) */
#define VARG2 3 /* digit prefix (second, eg, 2c3l) */
#define VXCH 4 /* f, F, t, T */
#define VFAIL 5 /* bad command */
#define VCMD 6 /* single char command (eg, X) */
#define VREDO 7 /* . */
#define VLIT 8 /* ^V */
#define VSEARCH 9 /* /, ? */

struct edstate {
int winleft;
char *cbuf;
int cbufsize;
int linelen;
int cursor;
};

struct glob {
char **result;
int start, end;
int addspace;
};

static char undocbuf[CMDLEN];

static struct edstate *save_edstate ARGS((struct edstate *old));
static void restore_edstate ARGS((struct edstate *old, struct edstate *new));
static void free_edstate ARGS((struct edstate *old));

static struct edstate ebuf;
static struct edstate undobuf = { 0, undocbuf, CMDLEN, 0, 0 };

static struct edstate *es; /* current editor state */
static struct edstate *undo;

static char ibuf[CMDLEN]; /* input buffer */
static int inslen; /* length of input buffer */
static int srchlen; /* length of current search pattern */
static char ybuf[CMDLEN]; /* yank buffer */
static int yanklen; /* length of yank buffer */
static int fsavecmd = ' '; /* last find command */
static int fsavech; /* character to find */
static char lastcmd[MAXVICMD]; /* last non-move command */
static int lastac; /* argcnt for lastcmd */
static int lastsearch = ' '; /* last search command */
static char srchpat[SRCHLEN]; /* last search pattern */
static int insert; /* non-zero in insert mode */
static int hnum; /* position in history */
static int hlast; /* 1 past last position in history */
static int modified; /* buffer has been "modified" */
static int state;

enum expand_mode { NONE, EXPAND, COMPLETE, PRINT };
static enum expand_mode expanded = NONE;/* last input was expanded */

int
vi_hook(ch)
int ch;
{
static char curcmd[MAXVICMD];
static char locpat[SRCHLEN];
static int cmdlen;
static int argc1, argc2;

switch (state) {

case VNORMAL:
if (insert != 0) {
if (ch == Ctrl('v')) {
state = VLIT;
ch = '^';
}
switch (vi_insert(ch)) {
case -1:
x_putc(BEL);
state = VNORMAL;
break;
case 0:
if (state == VLIT) {
es->cursor--;
refresh(0);
} else
refresh(insert != 0);
break;
case 1:
return 1;
}
} else {
if (ch == '\r' || ch == '\n')
return 1;
cmdlen = 0;
argc1 = 0;
if (ch >= '1' && ch <= '9') {
argc1 = ch - '0';
state = VARG1;
} else {
curcmd[cmdlen++] = ch;
state = nextstate(ch);
if (state == VSEARCH) {
save_cbuf();
es->cursor = 0;
es->linelen = 0;
if (ch == '/') {
if (putbuf("/", 1, 0) != 0) {
return -1;
}
} else if (putbuf("?", 1, 0) != 0)
return -1;
refresh(0);
}
}
}
break;

case VLIT:
if (isbad(ch)) {
del_range(es->cursor, es->cursor + 1);
x_putc(BEL);
} else
es->cbuf[es->cursor++] = ch;
refresh(1);
state = VNORMAL;
break;

case VARG1:
if (isdigit(ch))
argc1 = argc1 * 10 + ch - '0';
else {
curcmd[cmdlen++] = ch;
state = nextstate(ch);
}
break;

case VEXTCMD:
argc2 = 0;
if (ch >= '1' && ch <= '9') {
argc2 = ch - '0';
state = VARG2;
return 0;
} else {
curcmd[cmdlen++] = ch;
if (ch == curcmd[0])
state = VCMD;
else if (ismove(ch))
state = nextstate(ch);
else
state = VFAIL;
}
break;

case VARG2:
if (isdigit(ch))
argc2 = argc2 * 10 + ch - '0';
else {
if (argc1 == 0)
argc1 = argc2;
else
argc1 *= argc2;
curcmd[cmdlen++] = ch;
if (ch == curcmd[0])
state = VCMD;
else if (ismove(ch))
state = nextstate(ch);
else
state = VFAIL;
}
break;

case VXCH:
if (ch == Ctrl('['))
state = VNORMAL;
else {
curcmd[cmdlen++] = ch;
state = VCMD;
}
break;

case VSEARCH:
if (ch == '\r' || ch == '\n' /*|| ch == Ctrl('[')*/ ) {
restore_cbuf();
locpat[srchlen] = '\0';
(void) strcpy(srchpat, locpat);
state = VCMD;
} else if (ch == edchars.erase || ch == Ctrl('h')) {
if (srchlen != 0) {
srchlen--;
es->linelen -= char_len((unsigned char) locpat[srchlen]);
es->cursor = es->linelen;
refresh(0);
return 0;
}
restore_cbuf();
state = VNORMAL;
refresh(0);
} else if (ch == edchars.kill) {
srchlen = 0;
es->linelen = 1;
es->cursor = 1;
refresh(0);
return 0;
} else if (ch == edchars.werase) {
int i;
int n = srchlen;

while (n > 0 && isspace(locpat[n - 1]))
n--;
while (n > 0 && !isspace(locpat[n - 1]))
n--;
for (i = srchlen; --i >= n; )
es->linelen -= char_len((unsigned char) locpat[i]);
srchlen = n;
es->cursor = es->linelen;
refresh(0);
return 0;
} else {
if (srchlen == SRCHLEN - 1)
x_putc(BEL);
else {
locpat[srchlen++] = ch;
if ((ch & 0x80) && Flag(FVISHOW8)) {
es->cbuf[es->linelen++] = 'M';
es->cbuf[es->linelen++] = '-';
ch &= 0x7f;
}
if (ch < ' ' || ch == 0x7f) {
es->cbuf[es->linelen++] = '^';
es->cbuf[es->linelen++] = ch ^ '@';
} else
es->cbuf[es->linelen++] = ch;
es->cursor = es->linelen;
refresh(0);
}
return 0;
}
break;
}

switch (state) {
case VCMD:
state = VNORMAL;
switch (vi_cmd(argc1, curcmd)) {
case -1:
x_putc(BEL);
break;
case 0:
if (insert != 0)
inslen = 0;
refresh(insert != 0);
break;
case 1:
refresh(0);
return 1;
case 2:
/* back from a 'v' command - don't redraw the screen */
return 1;
}
break;

case VREDO:
state = VNORMAL;
if (argc1 != 0)
lastac = argc1;
switch (vi_cmd(lastac, lastcmd) != 0) {
case -1:
x_putc(BEL);
refresh(0);
break;
case 0:
if (insert != 0) {
if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
lastcmd[0] == 'C') {
if (redo_insert(1) != 0)
x_putc(BEL);
} else {
if (redo_insert(lastac) != 0)
x_putc(BEL);
}
}
refresh(0);
break;
case 1:
refresh(0);
return 1;
case 2:
/* back from a 'v' command - don't redraw the screen */
return 1;
}
break;

case VFAIL:
state = VNORMAL;
x_putc(BEL);
break;
}
return 0;
}

static void
vi_reset(buf, len)
char *buf;
size_t len;
{
state = VNORMAL;
hnum = hlast = histnum(-1) + 1;
insert = INSERT;
yanklen = 0;
inslen = 0;
lastcmd[0] = 'a';
lastac = 1;
modified = 1;
edit_reset(buf, len);
}

static int
nextstate(ch)
int ch;
{
if (isextend(ch))
return VEXTCMD;
else if (issrch(ch))
return VSEARCH;
else if (islong(ch))
return VXCH;
else if (ch == '.')
return VREDO;
else if (iscmd(ch))
return VCMD;
else
return VFAIL;
}

static int
vi_insert(ch)
int ch;
{
int tcursor;

if (ch == edchars.erase || ch == Ctrl('h')) {
if (insert == REPLACE) {
if (es->cursor == undo->cursor) {
x_putc(BEL);
return 0;
}
if (inslen > 0)
inslen--;
es->cursor--;
if (es->cursor >= undo->linelen)
es->linelen--;
else
es->cbuf[es->cursor] = undo->cbuf[es->cursor];
} else {
if (es->cursor == 0) {
/* x_putc(BEL); no annoying bell here */
return 0;
}
if (inslen > 0)
inslen--;
es->cursor--;
es->linelen--;
memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor+1],
es->linelen - es->cursor + 1);
}
expanded = NONE;
return 0;
}
if (ch == edchars.kill) {
if (es->cursor != 0) {
inslen = 0;
memmove(es->cbuf, &es->cbuf[es->cursor],
es->linelen - es->cursor);
es->linelen -= es->cursor;
es->cursor = 0;
}
expanded = NONE;
return 0;
}
if (ch == edchars.werase) {
if (es->cursor != 0) {
tcursor = Backword(1);
memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
es->linelen - es->cursor);
es->linelen -= es->cursor - tcursor;
if (inslen < es->cursor - tcursor)
inslen = 0;
else
inslen -= es->cursor - tcursor;
es->cursor = tcursor;
}
expanded = NONE;
return 0;
}
switch (ch) {

case '\0':
return -1;

case '\r':
case '\n':
return 1;

case Ctrl('['):
expanded = NONE;
if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
lastcmd[0] == 'C')
return redo_insert(0);
else
return redo_insert(lastac - 1);

/* { Begin nonstandard vi commands */
case Ctrl('x'):
expand_word(0);
break;

case Ctrl('f'):
complete_word(0);
break;

case Ctrl('e'):
print_expansions(0);
break;

case Ctrl('i'):
if (Flag(FVITABCOMPLETE)) {
complete_word(0);
break;
}
/* FALLTHROUGH */
/* End nonstandard vi commands } */

default:
if (es->linelen == es->cbufsize - 1)
return -1;
ibuf[inslen++] = ch;
if (insert == INSERT) {
memmove(&es->cbuf[es->cursor+1], &es->cbuf[es->cursor],
es->linelen - es->cursor);
es->linelen++;
}
es->cbuf[es->cursor++] = ch;
if (insert == REPLACE && es->cursor > es->linelen)
es->linelen++;
expanded = NONE;
}
return 0;
}

static int
vi_cmd(argcnt, cmd)
int argcnt;
char *cmd;
{
int ncursor;
int cur, c1, c2, c3 = 0;
struct edstate *t;

if (argcnt == 0) {
if (*cmd != 'G' && *cmd != 'g' && *cmd != '_' && *cmd != '|'
&& *cmd != 'v')
argcnt = 1;
}

if (ismove(*cmd)) {
if ((cur = domove(argcnt, cmd, 0)) >= 0) {
if (cur == es->linelen && cur != 0)
cur--;
es->cursor = cur;
} else
return -1;
} else {
if (isundoable(*cmd)) {
undo->winleft = es->winleft;
memmove(undo->cbuf, es->cbuf, es->linelen);
undo->linelen = es->linelen;
undo->cursor = es->cursor;
lastac = argcnt;
memmove(lastcmd, cmd, MAXVICMD);
}
switch (*cmd) {

case Ctrl('l'):
case Ctrl('r'):
redraw_line();
break;

case 'a':
modified = 1; hnum = hlast;
if (es->linelen != 0)
es->cursor++;
insert = INSERT;
break;

case 'A':
modified = 1; hnum = hlast;
del_range(0, 0);
es->cursor = es->linelen;
insert = INSERT;
break;

case 'S':
es->cursor = domove(1, "^", 1);
del_range(es->cursor, es->linelen);
modified = 1; hnum = hlast;
insert = INSERT;
break;

case 'Y':
cmd = "y$";
/* ahhhhhh... */
case 'c':
case 'd':
case 'y':
if (*cmd == cmd[1]) {
c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
c2 = es->linelen;
} else if (!ismove(cmd[1]))
return -1;
else {
if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
return -1;
if (*cmd == 'c' &&
(cmd[1]=='w' || cmd[1]=='W') &&
!isspace(es->cbuf[es->cursor])) {
while (isspace(es->cbuf[--ncursor]))
;
ncursor++;
}
if (ncursor > es->cursor) {
c1 = es->cursor;
c2 = ncursor;
} else {
c1 = ncursor;
c2 = es->cursor;
}
}
if (*cmd != 'c' && c1 != c2)
yank_range(c1, c2);
if (*cmd != 'y') {
del_range(c1, c2);
es->cursor = c1;
}
if (*cmd == 'c') {
modified = 1; hnum = hlast;
insert = INSERT;
}
break;

case 'p':
modified = 1; hnum = hlast;
if (es->linelen != 0)
es->cursor++;
while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
;
if (es->cursor != 0)
es->cursor--;
if (argcnt != 0)
return -1;
break;

case 'P':
modified = 1; hnum = hlast;
while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
;
if (es->cursor != 0)
es->cursor--;
if (argcnt != 0)
return -1;
break;

case 'C':
modified = 1; hnum = hlast;
del_range(es->cursor, es->linelen);
insert = INSERT;
break;

case 'D':
yank_range(es->cursor, es->linelen);
del_range(es->cursor, es->linelen);
if (es->cursor != 0)
es->cursor--;
break;

case 'g':
if (!argcnt)
argcnt = hlast + 1;
/* fall through */
case 'G':
if (!argcnt)
argcnt = 1;
else
argcnt = hlast - (source->line - argcnt);
if (grabhist(modified, argcnt - 1) < 0)
return -1;
else {
modified = 0;
hnum = argcnt - 1;
}
break;

case 'i':
modified = 1; hnum = hlast;
insert = INSERT;
break;

case 'I':
modified = 1; hnum = hlast;
es->cursor = domove(1, "^", 1);
insert = INSERT;
break;

case 'j':
case '+':
case Ctrl('n'):
if (grabhist(modified, hnum + argcnt) < 0)
return -1;
else {
modified = 0;
hnum += argcnt;
}
break;

case 'k':
case '-':
case Ctrl('p'):
if (grabhist(modified, hnum - argcnt) < 0)
return -1;
else {
modified = 0;
hnum -= argcnt;
}
break;

case 'r':
if (es->linelen == 0)
return -1;
modified = 1; hnum = hlast;
if (cmd[1] == 0)
x_putc(BEL);
else
es->cbuf[es->cursor] = cmd[1];
break;

case 'R':
modified = 1; hnum = hlast;
insert = REPLACE;
break;

case 's':
if (es->linelen == 0)
return -1;
modified = 1; hnum = hlast;
if (es->cursor + argcnt > es->linelen)
argcnt = es->linelen - es->cursor;
del_range(es->cursor, es->cursor + argcnt);
insert = INSERT;
break;

case 'v':
if (es->linelen == 0)
return -1;
if (!argcnt) {
if (modified) {
es->cbuf[es->linelen] = '\0';
source->line++;
histsave(source->line, es->cbuf, 1);
} else
argcnt = source->line + 1
- (hlast - hnum);
}
shf_snprintf(es->cbuf, es->cbufsize,
argcnt ? "%s %d" : "%s",
"fc -e ${VISUAL:-${EDITOR:-vi}} --",
argcnt);
es->linelen = strlen(es->cbuf);
return 2;

case 'x':
if (es->linelen == 0)
return -1;
modified = 1; hnum = hlast;
if (es->cursor + argcnt > es->linelen)
argcnt = es->linelen - es->cursor;
yank_range(es->cursor, es->cursor + argcnt);
del_range(es->cursor, es->cursor + argcnt);
break;

case 'X':
if (es->cursor > 0) {
modified = 1; hnum = hlast;
if (es->cursor < argcnt)
argcnt = es->cursor;
yank_range(es->cursor - argcnt, es->cursor);
del_range(es->cursor - argcnt, es->cursor);
es->cursor -= argcnt;
} else
return -1;
break;

case 'u':
t = es;
es = undo;
undo = t;
break;

case '?':
if (hnum == hlast)
hnum = -1;
/* ahhh */
case '/':
c3 = 1;
srchlen = 0;
lastsearch = *cmd;
/* fall through */
case 'n':
case 'N':
if (lastsearch == ' ')
return -1;
if (lastsearch == '?')
c1 = 1;
else
c1 = 0;
if (*cmd == 'N')
c1 = !c1;
if ((c2 = grabsearch(modified, hnum,
c1, srchpat)) < 0) {
if (c3) {
restore_cbuf();
refresh(0);
}
return -1;
} else {
modified = 0;
hnum = c2;
}
break;
case '_': {
int inspace;
char *p, *sp;

if (histnum(-1) < 0)
return -1;
p = *histpos();
#define issp(c) (isspace((c)) || (c) == '\n')
if (argcnt) {
while (*p && issp(*p))
p++;
while (*p && --argcnt) {
while (*p && !issp(*p))
p++;
while (*p && issp(*p))
p++;
}
if (!*p)
return -1;
sp = p;
} else {
sp = p;
inspace = 0;
while (*p) {
if (issp(*p))
inspace = 1;
else if (inspace) {
inspace = 0;
sp = p;
}
p++;
}
p = sp;
}
modified = 1; hnum = hlast;
if (es->cursor != es->linelen)
es->cursor++;
while (*p && !issp(*p)) {
argcnt++;
p++;
}
if (putbuf(space, 1, 0) != 0)
argcnt = -1;
else if (putbuf(sp, argcnt, 0) != 0)
argcnt = -1;
if (argcnt < 0) {
if (es->cursor != 0)
es->cursor--;
return -1;
}
insert = INSERT;
}
break;

case '~': {
char *p;

if (es->linelen == 0)
return -1;
p = &es->cbuf[es->cursor];
if (islower(*p)) {
modified = 1; hnum = hlast;
*p = toupper(*p);
} else if (isupper(*p)) {
modified = 1; hnum = hlast;
*p = tolower(*p);
}
if (es->cursor < es->linelen - 1)
es->cursor++;
}
break;

case '#':
es->cursor = 0;
if (es->linelen > 0 && putbuf("#", 1, 0) != 0)
return -1;
return 1;

case '=': /* at&t ksh */
case Ctrl('e'): /* Nonstandard vi/ksh */
print_expansions(1);
break;

/* case Ctrl('['): some annoying at&t ksh's */
case '\\': /* at&t ksh */
case Ctrl('f'): /* Nonstandard vi/ksh */
complete_word(0);
break;


case '*': /* at&t ksh */
case Ctrl('x'): /* Nonstandard vi/ksh */
expand_word(1);
break;
}
if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen)
es->cursor--;
}
return 0;
}

static int
domove(argcnt, cmd, sub)
int argcnt;
char *cmd;
int sub;
{
int bcount, UNINITIALIZED(i), t;
int UNINITIALIZED(ncursor);

switch (*cmd) {

case 'b':
if (!sub && es->cursor == 0)
return -1;
ncursor = backword(argcnt);
break;

case 'B':
if (!sub && es->cursor == 0)
return -1;
ncursor = Backword(argcnt);
break;

case 'e':
if (!sub && es->cursor + 1 >= es->linelen)
return -1;
ncursor = endword(argcnt);
if (sub && ncursor < es->linelen)
ncursor++;
break;

case 'E':
if (!sub && es->cursor + 1 >= es->linelen)
return -1;
ncursor = Endword(argcnt);
if (sub && ncursor < es->linelen)
ncursor++;
break;

case 'f':
case 'F':
case 't':
case 'T':
fsavecmd = *cmd;
fsavech = cmd[1];
/* drop through */

case ',':
case ';':
if (fsavecmd == ' ')
return -1;
i = fsavecmd == 'f' || fsavecmd == 'F';
t = fsavecmd > 'a';
if (*cmd == ',')
t = !t;
if ((ncursor = findch(fsavech, argcnt, t, i)) < 0)
return -1;
if (sub && t)
ncursor++;
break;

case 'h':
case Ctrl('h'):
if (!sub && es->cursor == 0)
return -1;
ncursor = es->cursor - argcnt;
if (ncursor < 0)
ncursor = 0;
break;

case ' ':
case 'l':
if (!sub && es->cursor + 1 >= es->linelen)
return -1;
if (es->linelen != 0) {
ncursor = es->cursor + argcnt;
if (ncursor > es->linelen)
ncursor = es->linelen;
}
break;

case 'w':
if (!sub && es->cursor + 1 >= es->linelen)
return -1;
ncursor = forwword(argcnt);
break;

case 'W':
if (!sub && es->cursor + 1 >= es->linelen)
return -1;
ncursor = Forwword(argcnt);
break;

case '0':
ncursor = 0;
break;

case '^':
ncursor = 0;
while (ncursor < es->linelen - 1 && isspace(es->cbuf[ncursor]))
ncursor++;
break;

case '|':
ncursor = argcnt;
if (ncursor > es->linelen)
ncursor = es->linelen;
if (ncursor)
ncursor--;
break;

case '$':
if (es->linelen != 0)
ncursor = es->linelen;
else
ncursor = 0;
break;

case '%':
ncursor = es->cursor;
while (ncursor < es->linelen &&
(i = bracktype(es->cbuf[ncursor])) == 0)
ncursor++;
if (ncursor == es->linelen)
return -1;
bcount = 1;
do {
if (i > 0) {
if (++ncursor >= es->linelen)
return -1;
} else {
if (--ncursor < 0)
return -1;
}
t = bracktype(es->cbuf[ncursor]);
if (t == i)
bcount++;
else if (t == -i)
bcount--;
} while (bcount != 0);
if (sub)
ncursor++;
break;

default:
return -1;
}
return ncursor;
}

static int
redo_insert(count)
int count;
{
while (count-- > 0)
if (putbuf(ibuf, inslen, insert==REPLACE) != 0)
return -1;
if (es->cursor > 0)
es->cursor--;
insert = 0;
return 0;
}

static void
yank_range(a, b)
int a, b;
{
yanklen = b - a;
if (yanklen != 0)
memmove(ybuf, &es->cbuf[a], yanklen);
}

static int
bracktype(ch)
int ch;
{
switch (ch) {

case '(':
return 1;

case '[':
return 2;

case '{':
return 3;

case ')':
return -1;

case ']':
return -2;

case '}':
return -3;

default:
return 0;
}
}

/*
* Non user interface editor routines below here
*/

static int cur_col; /* current column on line */
static int pwidth; /* width of prompt */
static int winwidth; /* width of window */
static char *wbuf[2]; /* window buffers */
static int wbuf_len; /* length of window buffers (x_cols-3)*/
static int win; /* window buffer in use */
static char morec; /* more character at right of window */
static int lastref; /* argument to last refresh() */
static char holdbuf[CMDLEN]; /* place to hold last edit buffer */
static int holdlen; /* length of holdbuf */

static void
save_cbuf()
{
memmove(holdbuf, es->cbuf, es->linelen);
holdlen = es->linelen;
holdbuf[holdlen] = '\0';
}

static void
restore_cbuf()
{
es->cursor = 0;
es->linelen = holdlen;
memmove(es->cbuf, holdbuf, holdlen);
}

/* return a new edstate */
static struct edstate *
save_edstate(old)
struct edstate *old;
{
struct edstate *new;

new = (struct edstate *)alloc(sizeof(struct edstate), APERM);
new->cbuf = alloc(old->cbufsize, APERM);
new->cbufsize = old->cbufsize;
strcpy(new->cbuf, old->cbuf);
new->linelen = old->linelen;
new->cursor = old->cursor;
new->winleft = old->winleft;
return new;
}

static void
restore_edstate(new, old)
struct edstate *old, *new;
{
strncpy(new->cbuf, old->cbuf, old->linelen);
new->linelen = old->linelen;
new->cursor = old->cursor;
new->winleft = old->winleft;
free_edstate (old);
}

static void
free_edstate(old)
struct edstate *old;
{
afree(old->cbuf, APERM);
afree((char *)old, APERM);
}



static void
edit_reset(buf, len)
char *buf;
size_t len;
{
es = &ebuf;
es->cbuf = buf;
es->cbufsize = len;
undo = &undobuf;
undo->cbufsize = len;

es->linelen = undo->linelen = 0;
es->cursor = undo->cursor = 0;
es->winleft = undo->winleft = 0;

cur_col = pwidth = promptlen(prompt);
if (!wbuf_len || wbuf_len != x_cols - 3) {
wbuf_len = x_cols - 3;
wbuf[0] = aresize(wbuf[0], wbuf_len, APERM);
wbuf[1] = aresize(wbuf[1], wbuf_len, APERM);
}
(void) memset(wbuf[0], ' ', wbuf_len);
(void) memset(wbuf[1], ' ', wbuf_len);
winwidth = x_cols - pwidth - 3;
win = 0;
morec = ' ';
lastref = 1;
}

static int
putbuf(buf, len, repl)
char *buf;
int len;
int repl;
{
if (len == 0)
return 0;
if (repl) {
if (es->cursor + len >= es->cbufsize - 1)
return -1;
if (es->cursor + len > es->linelen)
es->linelen = es->cursor + len;
} else {
if (es->linelen + len >= es->cbufsize - 1)
return -1;
memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
es->linelen - es->cursor);
es->linelen += len;
}
memmove(&es->cbuf[es->cursor], buf, len);
es->cursor += len;
return 0;
}

static void
del_range(a, b)
int a, b;
{
if (es->linelen != b)
memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
es->linelen -= b - a;
}

static int
findch(ch, cnt, forw, incl)
int ch;
int cnt;
int forw;
int incl;
{
int ncursor;

if (es->linelen == 0)
return -1;
ncursor = es->cursor;
while (cnt--) {
do {
if (forw) {
if (++ncursor == es->linelen)
return -1;
} else {
if (--ncursor < 0)
return -1;
}
} while (es->cbuf[ncursor] != ch);
}
if (!incl) {
if (forw)
ncursor--;
else
ncursor++;
}
return ncursor;
}

static int
forwword(argcnt)
int argcnt;
{
int ncursor;

ncursor = es->cursor;
while (ncursor < es->linelen && argcnt--) {
if (iswordch(es->cbuf[ncursor]))
while (iswordch(es->cbuf[ncursor]) &&
ncursor < es->linelen)
ncursor++;
else if (!isspace(es->cbuf[ncursor]))
while (!iswordch(es->cbuf[ncursor]) &&
!isspace(es->cbuf[ncursor]) &&
ncursor < es->linelen)
ncursor++;
while (isspace(es->cbuf[ncursor]) && ncursor < es->linelen)
ncursor++;
}
return ncursor;
}

static int
backword(argcnt)
int argcnt;
{
int ncursor;

ncursor = es->cursor;
while (ncursor > 0 && argcnt--) {
while (--ncursor > 0 && isspace(es->cbuf[ncursor]))
;
if (ncursor > 0) {
if (iswordch(es->cbuf[ncursor]))
while (--ncursor >= 0 &&
iswordch(es->cbuf[ncursor]))
;
else
while (--ncursor >= 0 &&
!iswordch(es->cbuf[ncursor]) &&
!isspace(es->cbuf[ncursor]))
;
ncursor++;
}
}
return ncursor;
}

static int
endword(argcnt)
int argcnt;
{
int ncursor;

ncursor = es->cursor;
while (ncursor < es->linelen && argcnt--) {
while (++ncursor < es->linelen - 1 &&
isspace(es->cbuf[ncursor]))
;
if (ncursor < es->linelen - 1) {
if (iswordch(es->cbuf[ncursor]))
while (++ncursor < es->linelen &&
iswordch(es->cbuf[ncursor]))
;
else
while (++ncursor < es->linelen &&
!iswordch(es->cbuf[ncursor]) &&
!isspace(es->cbuf[ncursor]))
;
ncursor--;
}
}
return ncursor;
}

static int
Forwword(argcnt)
int argcnt;
{
int ncursor;

ncursor = es->cursor;
while (ncursor < es->linelen && argcnt--) {
while (!isspace(es->cbuf[ncursor]) && ncursor < es->linelen)
ncursor++;
while (isspace(es->cbuf[ncursor]) && ncursor < es->linelen)
ncursor++;
}
return ncursor;
}

static int
Backword(argcnt)
int argcnt;
{
int ncursor;

ncursor = es->cursor;
while (ncursor > 0 && argcnt--) {
while (--ncursor >= 0 && isspace(es->cbuf[ncursor]))
;
while (ncursor >= 0 && !isspace(es->cbuf[ncursor]))
ncursor--;
ncursor++;
}
return ncursor;
}

static int
Endword(argcnt)
int argcnt;
{
int ncursor;

ncursor = es->cursor;
while (ncursor < es->linelen - 1 && argcnt--) {
while (++ncursor < es->linelen - 1 &&
isspace(es->cbuf[ncursor]))
;
if (ncursor < es->linelen - 1) {
while (++ncursor < es->linelen &&
!isspace(es->cbuf[ncursor]))
;
ncursor--;
}
}
return ncursor;
}

static int
grabhist(save, n)
int save;
int n;
{
char *hptr;

if (n < 0 || n > hlast)
return -1;
if (n == hlast) {
restore_cbuf();
return 0;
}
(void) histnum(n);
if ((hptr = *histpos()) == NULL) {
internal_errorf(0, "grabhist: bad history array");
return -1;
}
if (save)
save_cbuf();
if ((es->linelen = strlen(hptr)) >= es->cbufsize - 1)
es->linelen = es->cbufsize - 1;
memmove(es->cbuf, hptr, es->linelen);
es->cursor = 0;
return 0;
}

static int
grabsearch(save, start, fwd, pat)
int save, start, fwd;
char *pat;
{
char *hptr;
int hist;
int anchored;

if ((start == 0 && fwd == 0) || (start >= hlast-1 && fwd == 1))
return -1;
if (fwd)
start++;
else
start--;
anchored = *pat == '^' ? (++pat, 1) : 0;
if ((hist = findhist(start, fwd, pat, anchored)) < 0) {
/* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */
if (start != 0 && fwd && strcmp(holdbuf, pat) >= 0) {
restore_cbuf();
return 0;
} else
return -1;
}
if (save)
save_cbuf();
histnum(hist);
hptr = *histpos();
if ((es->linelen = strlen(hptr)) >= es->cbufsize - 1)
es->linelen = es->cbufsize - 1;
memmove(es->cbuf, hptr, es->linelen);
es->cursor = 0;
return hist;
}

static void
redraw_line()
{
(void) memset(wbuf[win], ' ', wbuf_len);
x_putc('\r');
x_putc('\n');
pprompt(prompt);
cur_col = pwidth;
morec = ' ';
}

static void
refresh(leftside)
int leftside;
{
if (leftside < 0)
leftside = lastref;
else
lastref = leftside;
if (outofwin())
rewindow();
display(wbuf[1 - win], wbuf[win], leftside);
win = 1 - win;
}

static int
outofwin()
{
int cur, col;

if (es->cursor < es->winleft)
return 1;
col = 0;
cur = es->winleft;
while (cur < es->cursor)
col = newcol((unsigned char) es->cbuf[cur++], col);
if (col >= winwidth)
return 1;
return 0;
}

static void
rewindow()
{
register int tcur, tcol;
int holdcur1, holdcol1;
int holdcur2, holdcol2;

holdcur1 = holdcur2 = tcur = 0;
holdcol1 = holdcol2 = tcol = 0;
while (tcur < es->cursor) {
if (tcol - holdcol2 > winwidth / 2) {
holdcur1 = holdcur2;
holdcol1 = holdcol2;
holdcur2 = tcur;
holdcol2 = tcol;
}
tcol = newcol((unsigned char) es->cbuf[tcur++], tcol);
}
while (tcol - holdcol1 > winwidth / 2)
holdcol1 = newcol((unsigned char) es->cbuf[holdcur1++],
holdcol1);
es->winleft = holdcur1;
}

static int
newcol(ch, col)
int ch, col;
{
if (ch == '\t')
return (col | 7) + 1;
return col + char_len(ch);
}

static void
display(wb1, wb2, leftside)
char *wb1, *wb2;
int leftside;
{
unsigned char ch;
char *twb1, *twb2, mc;
int cur, col, cnt;
int UNINITIALIZED(ncol);
int moreright;

col = 0;
cur = es->winleft;
moreright = 0;
twb1 = wb1;
while (col < winwidth && cur < es->linelen) {
if (cur == es->cursor && leftside)
ncol = col + pwidth;
if ((ch = es->cbuf[cur]) == '\t') {
do {
*twb1++ = ' ';
} while (++col < winwidth && (col & 7) != 0);
} else {
if ((ch & 0x80) && Flag(FVISHOW8)) {
*twb1++ = 'M';
if (++col < winwidth) {
*twb1++ = '-';
col++;
}
ch &= 0x7f;
}
if (col < winwidth) {
if (ch < ' ' || ch == 0x7f) {
*twb1++ = '^';
if (++col < winwidth) {
*twb1++ = es->cbuf[cur] ^ '@';
col++;
}
} else {
*twb1++ = es->cbuf[cur];
col++;
}
}
}
if (cur == es->cursor && !leftside)
ncol = col + pwidth - 1;
cur++;
}
if (cur == es->cursor)
ncol = col + pwidth;
if (col < winwidth) {
while (col < winwidth) {
*twb1++ = ' ';
col++;
}
} else
moreright++;
*twb1 = ' ';

col = pwidth;
cnt = winwidth;
twb1 = wb1;
twb2 = wb2;
while (cnt--) {
if (*twb1 != *twb2) {
if (cur_col != col)
ed_mov_opt(col, wb1);
x_putc(*twb1);
cur_col++;
}
twb1++;
twb2++;
col++;
}
if (es->winleft > 0 && moreright)
mc = '+';
else if (es->winleft > 0)
mc = '<';
else if (moreright)
mc = '>';
else
mc = ' ';
if (mc != morec) {
ed_mov_opt(x_cols - 2, wb1);
x_putc(mc);
cur_col++;
morec = mc;
}
if (cur_col != ncol)
ed_mov_opt(ncol, wb1);
}

static void
ed_mov_opt(col, wb)
int col;
char *wb;
{
if (col < cur_col) {
if (col + 1 < cur_col - col) {
x_putc('\r');
pprompt(prompt);
cur_col = pwidth;
while (cur_col++ < col)
x_putc(*wb++);
} else {
while (cur_col-- > col)
x_putc('\b');
}
} else {
wb = &wb[cur_col - pwidth];
while (cur_col++ < col)
x_putc(*wb++);
}
cur_col = col;
}

int
x_vi(buf, len)
char *buf;
size_t len;
{
int c;

vi_reset(buf, len > CMDLEN ? CMDLEN : len);
x_flush();
while (1) {
if ((c = x_getc()) == -1)
break;
if (state != VLIT) {
if (c == edchars.intr || c == edchars.quit) {
/* pretend we got an interrupt */
x_vi_zotc(c);
x_flush();
trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
x_mode(FALSE);
unwind(LSHELL);
} else if (c == edchars.eof) {
if (es->linelen == 0) {
x_vi_zotc(edchars.eof);
c = -1;
break;
}
continue;
}
}
if (vi_hook(c))
break;
x_flush();
}

x_putc('\r'); x_putc('\n'); x_flush();

if (c == -1)
return -1;

if (es->cbuf != buf)
memmove(buf, es->cbuf, es->linelen);

buf[es->linelen++] = '\n';

return es->linelen;
}

/*
* glob string. Useful for the command line editors.
* (mostly just a copy of eval())
*/
static char **
globstr(cp)
char *cp;
{
XPtrV w;
struct source *s, *sold;

sold = source;
s = pushs(SWSTR, ATEMP);
s->str = (char *) cp;
source = s;
if (yylex(ONEWORD) != LWORD) {
source = sold;
internal_errorf(0, "globstr: substitute error");
return (char **) 0;
}
source = sold;
XPinit(w, 10);
expand(yylval.cp, &w, (DOGLOB|DOTILDE));
XPput(w, NULL);
return (char **) XPclose(w);

#ifdef notdef
XPtrV w;

XPinit(w, 10);
XPput(w, NULL); /* space for shell name */
expand(cp, &w, (DOGLOB|DOTILDE));
XPput(w, NULL);
return (char **) XPclose(w) + 1;
#endif /* notdef */
}

static int
glob_word(g, command)
Glob *g;
int command;
{
int rval;
int star;
int start, end;
char *toglob = undo->cbuf;

if (!command && es->linelen > 0)
es->cursor--;
/*
* expansion normally maps a*b to `^a.*b.*'; if the cursor
* is just past the end of the pattern, then map a*b to `^a.*b$'.
*/
if (isspace(es->cbuf[es->cursor])) {
if (es->cursor == 0 || isspace(es->cbuf[es->cursor - 1])) {
if (!command && es->linelen > 0)
es->cursor++;
x_putc(BEL);
return -1;
}
start = es->cursor - 1;
star = 0;
} else {
start = es->cursor;
star = 1;
}
while (start > -1 && !isspace(es->cbuf[start]))
start--;
start++;
end = es->cursor;
while (end < es->linelen && !isspace(es->cbuf[end]))
end++;
if (start == end) {
if (!command && es->linelen > 0)
es->cursor++;
return -1;
}

/* use undo buffer to build word up in */
memmove(toglob, &es->cbuf[start], end-start);
if (star && toglob[end-start-1] != '*') {
toglob[end-start] = '*';
toglob[end-start+1] = '\0';
} else
toglob[end-start] = '\0';

g->start = start;
g->end = end;
g->result = globstr(toglob);
g->addspace = 0;
rval = 0;

if (strcmp(g->result[0], toglob) == 0 && g->result[1] == (char *) 0) {
struct stat statb;

/* If no star was appended, we need an additional check to
* see if this really is an error. E.g. toglob == "abc"
* when the file "abc" exists is not an error; if
* "abc" does exist it is an error.
*/
if (star || stat(g->result[0], &statb) < 0) {
if (!command && es->linelen > 0)
es->cursor++;
x_putc(BEL);
rval = -1;
}
}

if (rval == 0 && isspace(es->cbuf[es->cursor]))
g->addspace = 1;

/* restore undo buffer that we used temporarily */
memmove(toglob, es->cbuf, es->linelen);
return 0;
}

static int
expand_word(command)
int command;
{
int rval = 0;
Glob g;
static struct edstate *buf = 0;

/* Undo previous expansion */
if (command == 0 && expanded == EXPAND && buf) {
restore_edstate(es, buf);
if (es->cursor != es->linelen)
es->cursor++;
buf = 0;
expanded = NONE;
return 0;
}
if (glob_word(&g, command) < 0)
return -1;
buf = save_edstate(es);
expanded = EXPAND;
del_range(g.start, g.end);
es->cursor = g.start;
if (g.result == (char **)0)
rval = -1;
else while (1) {
if (putbuf(*g.result, (int) strlen(*g.result), 0) != 0) {
rval = -1;
break;
}
if (*++g.result == (char *) 0)
break;
if (putbuf(space, 1, 0) != 0) {
rval = -1;
break;
}
}
if (g.addspace && rval == 0)
rval = putbuf(space, 1, 0);
modified = 1; hnum = hlast;
insert = INSERT;
refresh(0);
return rval;
}

static int
complete_word(command)
int command;
{
int rval = 0;
Glob g;
int i, escape = 0, len;
struct stat s;
static struct edstate *buf = 0;

/* Undo previous completion */
if (command == 0 && expanded == COMPLETE) {
print_expansions(0);
expanded = PRINT;
return 0;
}
if (command == 0 && expanded == PRINT && buf) {
restore_edstate(es, buf);
if (es->cursor != es->linelen)
es->cursor++;
buf = 0;
expanded = NONE;
return 0;
}
if (glob_word(&g, command) < 0)
return -1;
/* Check for globbing in arg string */
for (i=g.start; i switch (es->cbuf[i]) {
case '\\':
escape = 1;
break;
case '*':
case '?':
case '[':
if (!escape) {
x_putc(BEL);
return -1;
}
break;
default:
escape = 0;
}
}

buf = save_edstate(es);
expanded = COMPLETE;
del_range(g.start, g.end);
es->cursor = g.start;
if (g.result == (char **)0)
rval = -1;
else {
/* find the shortest match */
char *save = g.result[0], *p;
len = strlen(g.result[0]);
if (g.result[1] != 0)
x_putc(BEL);
for (i=1; (p = g.result[i]); i++) {
int j;

for (j=0; j if (p[j] != save[j]) {
len = j;
break;
}
}
if (putbuf(g.result[0], len, 0) != 0)
rval = -1;
}
/* Check to see if it's a directory. */
if (g.result[1] == 0) {
if (stat(g.result[0], &s) < 0)
rval = -1;
else {
if (S_ISDIR(s.st_mode))
putbuf(slash, 1, 0);
else
putbuf(space, 1, 0);
}
}
modified = 1; hnum = hlast;
insert = INSERT;
refresh(0);
return rval;
}

static int
print_expansions(command)
int command;
{
Glob g;
char **p;
int i;

if (glob_word(&g, command) < 0)
return -1;
if ((p = g.result) == (char **)0)
return -1;
x_putc('\r');
x_putc('\n');
for (i = 0; i < cur_col; i++)
x_putc(' ');
while (*p && **p) {
char *q;
if ((q = strrchr_dirsep(*p)) == 0)
x_puts(*p);
else
x_puts(++q);
x_putc(' ');
p++;
}
if (!command && es->cursor != es->linelen)
es->cursor++;
redraw_line();
return 0;
}

/* How long is char when displayed (not counting tabs) */
static int
char_len(c)
int c;
{
int len = 1;

if ((c & 0x80) && Flag(FVISHOW8)) {
len += 2;
c &= 0x7f;
}
if (c < ' ' || c == 0x7f)
len++;
return len;
}

/* Similar to x_zotc(emacs.c), but no tab wierdness */
static void
x_vi_zotc(c)
int c;
{
if (Flag(FVISHOW8) && (c & 0x80)) {
x_puts("M-");
c &= 0x7f;
}
if (c < ' ' || c == 0x7f) {
x_putc('^');
c ^= '@';
}
x_putc(c);
}

#endif /* VI */
pdksh-5.1.2/edit.h100644 0 133 2111 5660210666 12547 0ustar rootlsource/* NAME:
* edit.h - globals for edit modes
*
* DESCRIPTION:
* This header defines various global edit objects.
*
* SEE ALSO:
*
*
* RCSid:
* $Id: edit.h,v 1.2 1994/05/19 18:32:40 michael Exp michael $
*
*/

/* some useful #defines */
#ifdef EXTERN
# define _I_(i) = i
#else
# define _I_(i)
# define EXTERN extern
# define EXTERN_DEFINED
#endif

#define BEL 0x07

/* tty driver characters we are interested in */
typedef struct {
int erase;
int kill;
int werase;
int intr;
int quit;
int eof;
} X_chars;

EXTERN X_chars edchars;

/* emacs.c */
int x_emacs ARGS((char *buf, size_t len));
void x_init_emacs ARGS((void));
void x_emacs_keys ARGS((X_chars *ec));
/* vi.c */
int vi_hook ARGS((int ch));
int x_vi ARGS((char *buf, size_t len));

#ifdef DEBUG
# define _D_(x) x
#else
# define _D_(x)
#endif

/* This lot goes at the END */
/* be sure not to interfere with anyone else's idea about EXTERN */
#ifdef EXTERN_DEFINED
# undef EXTERN_DEFINED
# undef EXTERN
#endif
#undef _I_
/*
* Local Variables:
* version-control:t
* comment-column:40
* End:
*/
pdksh-5.1.2/expand.h100644 0 133 5154 5656451512 13115 0ustar rootlsource/*
* Expanding strings
*/
/* $Id$ */


#if 0 /* Usage */
XString xs;
char *xp;

Xinit(xs, xp, 128, ATEMP); /* allocate initial string */
while ((c = generate()) {
Xcheck(xs, xp); /* expand string if neccessary */
Xput(xs, xp, c); /* add character */
}
return Xclose(xs, xp); /* resize string */
/*
* NOTE:
* The Xcheck and Xinit macros have a magic + 8 in the lengths. This is
* so that you can put up to 4 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 + 8, (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 Xsavepos(xs, xp) ((xp) - (xs).beg)
#define Xrestpos(xs, xp, n) ((xs).beg + (n))

char * Xcheck_grow_ ARGS((XString *xsp, char *xp, int more));

/*
* expandable vector of generic pointers
*/

typedef struct XPtrV {
void **cur; /* next avail pointer */
void **beg, **end; /* begin, end of vector */
} XPtrV;

#define XPinit(x, n) do { \
register void **vp__; \
vp__ = (void**) alloc(sizeofN(void*, n), ATEMP); \
(x).cur = (x).beg = vp__; \
(x).end = vp__ + n; \
} while (0)

#define XPput(x, p) do { \
if ((x).cur >= (x).end) { \
int n = XPsize(x); \
(x).beg = (void**) aresize((void*) (x).beg, \
sizeofN(void*, n*2), ATEMP); \
(x).cur = (x).beg + n; \
(x).end = (x).cur + n; \
} \
*(x).cur++ = (p); \
} while (0)

#define XPptrv(x) ((x).beg)
#define XPsize(x) ((x).cur - (x).beg)

#define XPclose(x) (void**) aresize((void*)(x).beg, \
sizeofN(void*, XPsize(x)), ATEMP)

#define XPfree(x) afree((void*) (x).beg, ATEMP)
pdksh-5.1.2/ksh_dir.h100644 0 133 1171 5666650226 13260 0ustar rootlsource/* Wrapper around the ugly dir includes/ifdefs */
/* $Id$ */

#if defined(DIRENT) || defined(_POSIX_VERSION)
# include
# define NLENGTH(dirent) (strlen(dirent->d_name))
#else
# define dirent direct
# define NLENGTH(dirent) (dirent->d_namlen)
# ifdef SYSNDIR
# include
# endif /* SYSNDIR */
# ifdef SYSDIR
# include
# endif /* SYSDIR */
# ifdef NDIR
# include
# endif /* NDIR */
#endif /* DIRENT || _POSIX_VERSION */

#ifdef OPENDIR_DOES_NONDIR
extern DIR *ksh_opendir ARGS((char *d));
#else /* OPENDIR_DOES_NONDIR */
# define ksh_opendir(d) opendir(d)
#endif /* OPENDIR_DOES_NONDIR */
pdksh-5.1.2/ksh_limval.h100644 0 133 700 5636322374 13740 0ustar rootlsource/* Wrapper around the values.h/limits.h includes/ifdefs */
/* $Id$ */

#ifdef HAVE_VALUES_H
# include
#endif /* HAVE_VALUES_H */
/* limits.h is included in sh.h */

#ifndef DMAXEXP
# define DMAXEXP 128 /* should be big enough */
#endif

#ifndef BITSPERBYTE
# ifdef CHAR_BIT
# define BITSPERBYTE CHAR_BIT
# else
# define BITSPERBYTE 8 /* probably true.. */
# endif
#endif

#ifndef BITS
# define BITS(t) (BITSPERBYTE * sizeof(t))
#endif
pdksh-5.1.2/ksh_stat.h100644 0 133 2512 5662152560 13447 0ustar rootlsource/* Wrapper around the ugly sys/stat includes/ifdefs */
/* $Id$ */

/* assumes already included */
#include

#ifndef HAVE_LSTAT
# define lstat(path, buf) stat(path, buf)
#endif /* HAVE_LSTAT */

#ifdef STAT_MACROS_BROKEN
# undef S_ISREG
# undef S_ISDIR
# undef S_ISCHR
# undef S_ISBLK
# undef S_ISFIFO
# undef S_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_IFFIFO)
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFFIFO)
#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 */
pdksh-5.1.2/ksh_time.h100644 0 133 636 5606021717 13414 0ustar rootlsource/* Wrapper around the ugly time.h,sys/time.h includes/ifdefs */
/* $Id$ */

#ifdef TIME_WITH_SYS_TIME
# include
# include
#else /* TIME_WITH_SYS_TIME */
# ifdef HAVE_SYS_TIME_H
# include
# else
# include
# endif
#endif /* TIME_WITH_SYS_TIME */

#ifndef TIME_DECLARED
extern time_t time ARGS((time_t *));
#endif

#ifndef CLK_TCK
# define CLK_TCK 60 /* 60HZ */
#endif
pdksh-5.1.2/ksh_times.h100644 0 133 375 5661744754 13615 0ustar rootlsource#include

#ifdef TIMES_BROKEN
extern clock_t ksh_times ARGS((struct tms *));
#else /* TIMES_BROKEN */
# define ksh_times times
#endif /* TIMES_BROKEN */

#ifdef HAVE_TIMES
extern clock_t times ARGS((struct tms *));
#endif /* HAVE_TIMES */
pdksh-5.1.2/ksh_wait.h100644 0 133 3147 5664137647 13460 0ustar rootlsource/* Wrapper around the ugly sys/wait includes/ifdefs */
/* $Id$ */

#ifdef HAVE_SYS_WAIT_H
# include
#endif

#ifdef HAVE_UNION_WAIT
typedef union wait WAIT_T;
# ifndef WIFCORED
# define WIFCORED(s) ((s).w_coredump)
# endif
# define WSTATUS(s) ((s).w_status)

# ifndef WEXITSTATUS
# define WEXITSTATUS(s) ((s).w_retcode)
# endif
# ifndef WTERMSIG
# define WTERMSIG(s) ((s).w_termsig)
# endif
# ifndef WSTOPSIG
# define WSTOPSIG(s) ((s).w_stopsig)
# endif
#else /* HAVE_UNION_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
#endif /* HAVE_UNION_WAIT */

#if !defined(HAVE_WAITPID) && defined(HAVE_WAIT3)
# define ksh_waitpid(p, s, o) wait3((s), (o), (struct rusage *) 0)
#else /* !HAVE_WAITPID && HAVE_WAIT3 */
# ifdef HAVE_UNION_WAIT
/* The cast here is because most POSIX BSD systems have union wait,
* but pass an int * to waitpid() since POSIX says it takes an int *.
*/
# define ksh_waitpid(p, s, o) waitpid((p), (int *) (s), (o))
# else /* HAVE_UNION_WAIT */
# define ksh_waitpid(p, s, o) waitpid((p), (s), (o))
# endif /* HAVE_UNION_WAIT */
#endif /* !HAVE_WAITPID && HAVE_WAIT3 */
pdksh-5.1.2/lex.h100644 0 133 6545 5666427561 12443 0ustar rootlsource/*
* Source input, lexer and parser
*/

/* $Id: lex.h,v 1.4 1994/05/31 13:34:34 michael Exp $ */

#define IDENT 64

typedef struct source Source;
struct source {
char *str; /* input pointer */
int type; /* input type */
union {
char *start; /* string */
char **strv; /* string [] */
struct shf *shf; /* shell file */
struct tbl *tblp; /* alias */
} u;
int line; /* line number */
int errline; /* line the error occured on (0 if not set) */
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 STTY 1 /* terminal input */
#define SFILE 2 /* file input */
#define SSTDIN 3 /* read stdin */
#define SSTRING 4 /* string */
#define SWSTR 5 /* string without \n */
#define SWORDS 6 /* string[] */
#define SWORDSEP 7 /* string[] seperator */
#define SALIAS 8 /* alias expansion */
#define SHIST 9 /* history expansion */
#define SREREAD 10 /* read ahead to be re-scanned */

/* Source.flags values */
#define SF_ECHO BIT(0) /* echo niput to shlout */
#define SF_ALIAS BIT(1) /* faking space at end of alias */
#define SF_ALIASEND BIT(2) /* faking space at end of alias */

/*
* states while lexing word
*/
#define SBASE 0 /* outside any lexical constructs */
#define SWORD 1 /* implicit quoting for substitute() */
#define SDPAREN 2 /* inside (( )), implicit quoting */
#define SSQUOTE 3 /* inside '' */
#define SDQUOTE 4 /* inside "" */
#define SBRACE 5 /* inside ${} */
#define SPAREN 6 /* inside $() */
#define SBQUOTE 7 /* inside `` */
#define SDDPAREN 8 /* inside $(( )) */

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 HERES 10 /* max << in line */

EXTERN Source *source; /* yyparse/yylex source */
EXTERN YYSTYPE yylval; /* result from yylex */
EXTERN int yynerrs;
EXTERN struct ioword *heres [HERES], **herep;
EXTERN char ident [IDENT+1];

#ifdef HISTORY
# define HISTORYSIZE 128 /* size of saved history */

EXTERN char **history; /* saved commands */
EXTERN char **histptr; /* last history item */
EXTERN int histsize; /* history size */
EXTERN int histpush; /* number of pushed fc commands */
#endif /* HISTORY */
pdksh-5.1.2/options.h100644 0 133 5407 5667225063 13334 0ustar rootlsource/*
* Options configuration file for the PD ksh
*
* RCSid: $Id$
*/

/* Define this to the path to use if the PATH environment variable is
* not set (ie, either never set or explicitly unset with the unset
* command). A value without . in it is safest.
* THIS DEFINE IS NOT USED if confstr() and _CS_PATH are available or
* if defines _PATH_DEFPATH.
*/
#ifdef OS2
# define DEFAULT_PATH "c:/usr/bin;c:/os2" /* OS/2 only */
#else /* OS2 */
# define DEFAULT_PATH "/bin:/usr/bin:/usr/ucb" /* Unix */
#endif /* OS2 */


/* Define KSH to get KSH features; otherwise, you get a fairly basic
* Bourne/POSIXish shell (undefining this results in EMACS, VI and
* COMPLEX_HISTORY being undefined as well, regardless of their setting
* here).
*/
#define KSH

/* Define EMACS if you want emacs command line editing compiled in (enabled
* with "set -o emacs", or by setting the VISUAL or EDITOR variables to
* something ending in emacs).
*/
#define EMACS

/* Define VI if you want vi command line editing compiled in (enabled with
* "set -o vi", or by setting the VISUAL or EDITOR variables to something
* ending in vi).
*/
#define VI

/* Define JOBS if you want job control compiled in. This requires that your
* system support process groups and reliable signal handling routines (it
* will be automatically undefined if your system doesn't have them).
*/
#define JOBS

/* Define BRACEEXPAND if you want csh-like {} globbing compiled in and enabled
* (can be disabled with "set +o braceexpand"; also disabled by "set -o posix",
* but can be re-enabled with "set -o braceexpand").
*/
#define BRACEEXPAND

/* Define COMPLEX_HISTORY if you want at&t ksh style history files (ie, file
* is updated after each command is read; concurrent ksh's read each other's
* commands, etc.). This option uses the mmap() function - if mmap isn't
* available, the option is automatically undefined. If option is not defined,
* a simplier history mechanism which reads/saves the history at startup/exit
* time, respectively, is used. COMPLEX_HISTORY is courtesy of Peter Collinson.
*/
#undef COMPLEX_HISTORY

/* Define POSIXLY_CORRECT if you want POSIX behavior by default (otherwise,
* posix behavior is only turned on if the environment variable POSIXLY_CORRECT
* is present or by using "set -o posix"; it can be turned off with
* "set +o posix").
* See the POSIX Mode section in the man page for details on what this option
* affects.
* NOTE: posix mode is not compatable with some bourne sh/at&t ksh scripts.
*/
#undef POSIXLY_CORRECT

/* Define SWTCH to handle SWITCH character, for use with shell layers (shl(1)).
* This has not been tested for some time.
*/
#undef SWTCH

/* SILLY: The name says it all - compile game of life code into the emacs
* command line editing code.
*/
#undef SILLY
pdksh-5.1.2/proto.h100644 0 133 22713 5667220727 13025 0ustar rootlsource/*
* prototypes for PD-KSH
* originally generated using "cproto.c 3.5 92/04/11 19:28:01 cthuang "
* $Id: proto.h,v 1.3 1994/05/19 18:32:40 michael Exp michael $
*/

/* alloc.c */
Area * ainit ARGS((Area *ap));
void afreeall ARGS((Area *ap));
void * alloc ARGS((size_t size, Area *ap));
void * aresize ARGS((void *ptr, size_t size, Area *ap));
void afree ARGS((void *ptr, Area *ap));
/* c_ksh.c */
int c_hash ARGS((char **wp));
int c_cd ARGS((char **wp));
int c_print ARGS((char **wp));
int c_whence ARGS((char **wp));
int c_command ARGS((char **wp));
int c_typeset ARGS((char **wp));
int c_alias ARGS((char **wp));
int c_unalias ARGS((char **wp));
int c_let ARGS((char **wp));
int c_jobs ARGS((char **wp));
int c_fgbg ARGS((char **wp));
int c_kill ARGS((char **wp));
void getopts_reset ARGS((int val));
int c_getopts ARGS((char **wp));
int c_bind ARGS((char **wp));
/* c_sh.c */
int c_label ARGS((char **wp));
int c_shift ARGS((char **wp));
int c_umask ARGS((char **wp));
int c_dot ARGS((char **wp));
int c_wait ARGS((char **wp));
int c_read ARGS((char **wp));
int c_eval ARGS((char **wp));
int c_trap ARGS((char **wp));
int c_brkcont ARGS((char **wp));
int c_exitreturn ARGS((char **wp));
int c_set ARGS((char **wp));
int c_unset ARGS((char **wp));
int c_ulimit ARGS((char **wp));
int c_times ARGS((char **wp));
int timex ARGS((struct op *t, int f));
int c_exec ARGS((char **wp));
int c_builtin ARGS((char **wp));
/* c_test.c */
int c_test ARGS((char **wp));
int is_db_unop ARGS((char *s));
int is_db_binop ARGS((char *s));
int is_db_patop ARGS((int op));
/* edit.c */
void x_init ARGS((void));
int x_read ARGS((char *buf, size_t len));
int x_getc ARGS((void));
void x_flush ARGS((void));
void x_putc ARGS((int c));
void x_puts ARGS((char *s));
bool_t x_mode ARGS((bool_t onoff));
int promptlen ARGS((char *cp));
void set_editmode ARGS((char *ed));
/* emacs.c: most prototypes in edit.h */
int x_bind ARGS((char *a1, char *a2, int macro));
/* eval.c */
char * substitute ARGS((const char *cp, int f));
char ** eval ARGS((char **ap, int f));
char * evalstr ARGS((char *cp, int f));
char * evalonestr ARGS((char *cp, int f));
/* XPtrV not defined at this point... */
void expand ARGS((/*char *cp, XPtrV *wp, int f*/));
/* exec.c */
int fd_clexec ARGS((int fd));
int execute ARGS((struct op * volatile t, volatile int flags));
int shcomexec ARGS((char **wp));
struct tbl * findfunc ARGS((char *name, int h, int create));
int define ARGS((char *name, struct op *t));
void builtin ARGS((char *name, int (*func)()));
struct tbl * findcom ARGS((char *name, int flags));
void flushcom ARGS((int all));
char * search ARGS((char *name, char *path, int mode));
/* expr.c */
long evaluate ARGS((const char *expr));
void v_evaluate ARGS((struct tbl *vp, const char *expr));
/* history.c */
void init_histvec ARGS((void));
void hist_init ARGS((Source *s));
void hist_finish ARGS((void));
void histsave ARGS((int lno, char *cmd, int dowrite));
#ifdef HISTORY
int c_fc ARGS((register char **wp));
void sethistsize ARGS((int n));
void sethistfile ARGS((char *name));
# ifdef EASY_HISTORY
void histappend ARGS((char *cmd, int nl_seperate));
# endif
char ** histget ARGS((char *str));
char ** histpos ARGS((void));
int histN ARGS((void));
int histnum ARGS((int n));
int findhist ARGS((int start, int fwd, char *str, int anchored));
#endif /* HISTORY */
/* io.c */
void errorf ARGS((const char *fmt, ...))
GCC_FUNC_ATTR2(noreturn, format(printf, 1, 2));
void warningf ARGS((int fileline, const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 2, 3));
void bi_errorf ARGS((const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 1, 2));
void internal_errorf ARGS((int jump, const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 2, 3));
void error_prefix ARGS((int fileline));
void shellf ARGS((const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 1, 2));
void shprintf ARGS((const char *fmt, ...))
GCC_FUNC_ATTR(format(printf, 1, 2));
int can_seek ARGS((int fd));
void initio ARGS((void));
int ksh_dup2 ARGS((int ofd, int nfd, int errok));
int savefd ARGS((int fd));
void restfd ARGS((int fd, int ofd));
void openpipe ARGS((int *pv));
void closepipe ARGS((int *pv));
int check_fd ARGS((char *name, int mode, char **emsgp));
void coproc_init ARGS((void));
void coproc_read_close ARGS((int fd));
void coproc_readw_close ARGS((int fd));
void coproc_write_close ARGS((int fd));
int get_coproc_fd ARGS((int mode, char **emsgp));
void cleanup_coproc ARGS((int reuse));
struct temp *maketemp ARGS((Area *ap));
/* jobs.c */
void j_init ARGS((int mflagset));
void j_exit ARGS((void));
void j_change ARGS((void));
int exchild ARGS((struct op *t, int flags, int close_fd));
void startlast ARGS((void));
int waitlast ARGS((void));
int waitfor ARGS((char *cp, int *sigp));
int j_kill ARGS((char *cp, int sig));
int j_resume ARGS((char *cp, int bg));
int j_jobs ARGS((char *cp, int slp, int nflag));
void j_notify ARGS((void));
pid_t j_async ARGS((void));
int j_stopped ARGS((void));
/* lex.c */
int yylex ARGS((int cf));
void yyerror ARGS((const char *fmt, ...))
GCC_FUNC_ATTR2(noreturn, format(printf, 1, 2));
Source * pushs ARGS((int type, Area *areap));
void set_prompt ARGS((int to));
void pprompt ARGS((char *cp));
/* mail.c */
void mcheck ARGS((void));
void mbset ARGS((char *p));
void mpset ARGS((char *mptoparse));
void mprint ARGS((void));
/* main.c */
int main ARGS((int argc, char **argv, char **envp));
int include ARGS((char *name, int argc, char **argv));
int command ARGS((char *comm));
int shell ARGS((Source *volatile s, int volatile exit_atend));
void unwind ARGS((int i)) GCC_FUNC_ATTR(noreturn);
void newenv ARGS((int type));
void quitenv ARGS((void));
void cleanup_parents_env ARGS((void));
void aerror ARGS((Area *ap, const char *msg))
GCC_FUNC_ATTR(noreturn);
/* misc.c */
void setctypes ARGS((const char *s, int t));
void initctypes ARGS((void));
char * ulton ARGS((unsigned long n, int base));
char * strsave ARGS((const char *s, Area *ap));
char * strnsave ARGS((const char *s, int n, Area *ap));
int option ARGS((const char *n));
char * getoptions ARGS((void));
void change_flag ARGS((enum sh_flag f, int what, int newval));
int parse_args ARGS((char **argv, int what, int *setargsp));
int getn ARGS((char *as, int *ai));
int bi_getn ARGS((char *as, int *ai));
char * strerror ARGS((int i));
int gmatch ARGS((char *s, char *p));
void qsortp ARGS((void **base, size_t n, int (*f)()));
int xstrcmp ARGS((void *p1, void *p2));
void ksh_getopt_reset ARGS((Getopt *go, int));
int ksh_getopt ARGS((char **argv, Getopt *go, char *options));
void print_value_quoted ARGS((char *s));
void print_columns ARGS((struct shf *shf, int n,
char *(*func) ARGS((void *, int, char *, int)),
void *arg, int max_width));
int strip_nuls ARGS((char *buf, int nbytes));
char *str_zcpy ARGS((char *dst, char *src, int dsize));
/* path.c */
int make_path ARGS((char *curpath, char *file, char **pathlist,
char *result, int len));
void simplify_path ARGS((char *path));
/* syn.c */
void initkeywords ARGS((void));
struct op * compile ARGS((Source *s));
/* table.c */
unsigned int hash ARGS((const char *n));
void tinit ARGS((struct table *tp, Area *ap, int tsize));
struct tbl * tsearch ARGS((struct table *tp, const char *n, unsigned int h));
struct tbl * tenter ARGS((struct table *tp, const char *n, unsigned int h));
void tdelete ARGS((struct tbl *p));
void twalk ARGS((struct table *tp));
struct tbl * tnext ARGS((void));
struct tbl ** tsort ARGS((struct table *tp));
/* trace.c */
/* trap.c */
void inittraps ARGS((void));
void alarm_init ARGS((void));
Trap * gettrap ARGS((char *name));
RETSIGTYPE trapsig ARGS((int i));
void intrcheck ARGS((void));
int fatal_trap_check ARGS((void));
int trap_pending ARGS((void));
void runtraps ARGS((int intr));
void runtrap ARGS((Trap *p));
void cleartraps ARGS((void));
void restoresigs ARGS((void));
void settrap ARGS((Trap *p, char *s));
int block_pipe ARGS((void));
void restore_pipe ARGS((int restore_dfl));
int setsig ARGS((Trap *p, RETSIGTYPE (*f)(), int flags));
void setexecsig ARGS((Trap *p, int restore));
/* tree.c */
void ptree ARGS((struct op *t, struct shf *f));
void pioact ARGS((struct shf *f, struct ioword *iop));
int fptreef ARGS((struct shf *f, char *fmt, ...));
char * snptreef ARGS((char *s, int n, char *fmt, ...));
struct op * tcopy ARGS((struct op *t, Area *ap));
char * wdcopy ARGS((const char *wp, Area *ap));
const char *wdscan ARGS((const char *wp, int c));
void tfree ARGS((struct op *t, Area *ap));
/* var.c */
void newblock ARGS((void));
void popblock ARGS((void));
void initvar ARGS((void));
struct tbl * global ARGS((const char *n));
struct tbl * local ARGS((const char *n));
char * strval ARGS((struct tbl *vp));
long intval ARGS((struct tbl *vp));
void setstr ARGS((struct tbl *vq, char *s));
struct tbl * strint ARGS((struct tbl *vq, struct tbl *vp));
void setint ARGS((struct tbl *vq, long n));
int getint ARGS((struct tbl *vp, long *nump));
struct tbl * typeset ARGS((char *var, int set, int clr, int field, int base));
void unset ARGS((struct tbl *vp));
char * skip_varname ARGS((const char *s, int aok));
char *skip_wdvarname ARGS((char *s, int aok));
int is_wdvarname ARGS((char *s, int aok));
int is_wdvarassign ARGS((char *s));
char ** makenv ARGS((void));
int array_ref_len ARGS((const char *cp));
char * basename ARGS((char *str));
void set_array ARGS((char *var, int reset, char **vals));
/* version.c */
/* vi.c: prototypes in edit.h */
pdksh-5.1.2/sh.h100644 0 133 41343 5667366527 12305 0ustar rootlsource/*
* Public Domain Bourne/Korn shell
*/

/* $Id: sh.h,v 1.2 1994/05/19 18:32:40 michael Exp michael $ */

#include "config.h" /* system and option configuration info */

#ifdef HAVE_PROTOTYPES
# define ARGS(args) args /* prototype declaration */
#else
# define ARGS(args) () /* K&R declaration */
#endif

/* Start of common headers */

#include
#include
#include
#ifdef HAVE_STDDEF_H
# include
#endif

#ifdef HAVE_STDLIB_H
# include
#else
/* just a useful subset of what stdlib.h would have */
extern char * getenv ARGS((const char *));
extern void * malloc ARGS((size_t));
extern int free ARGS((void *));
extern int exit ARGS((int));
extern int rand ARGS((void));
extern void srand ARGS((unsigned int));
extern int atoi ARGS((const char *));
#endif /* HAVE_STDLIB_H */

#ifdef HAVE_UNISTD_H
# include
#else
/* just a useful subset of what unistd.h would have */
extern int access ARGS((const char *, int));
extern int open ARGS((const char *, int, ...));
extern int creat ARGS((const char *, mode_t));
extern int read ARGS((int, char *, unsigned));
extern int write ARGS((int, const char *, unsigned));
extern off_t lseek ARGS((int, off_t, int));
extern int close ARGS((int));
extern int pipe ARGS((int []));
extern int dup2 ARGS((int, int));
extern int unlink ARGS((const char *));
extern int fork ARGS((void));
extern int execve ARGS((const char *, char * const[], char * const[]));
extern int chdir ARGS((const char *));
extern int kill ARGS((pid_t, int));
extern char *getcwd(); /* no ARGS here - differs on different machines */
extern int geteuid ARGS((void));
extern int getegid ARGS((void));
extern int getpid ARGS((void));
extern int getppid ARGS((void));
extern unsigned int sleep ARGS((unsigned int));
extern int isatty ARGS((int));
# ifdef POSIX_PGRP
extern int getpgrp ARGS((void));
extern int setpgid ARGS((pid_t, pid_t));
# endif /* POSIX_PGRP */
# ifdef BSD_PGRP
extern int getpgrp ARGS((pid_t));
extern int setpgrp ARGS((pid_t, pid_t));
# endif /* BSD_PGRP */
# ifdef SVR3_PGRP
extern int getpgrp ARGS((void));
extern int setpgrp ARGS((void));
# endif /* SVR3_PGRP */
#endif /* HAVE_UNISTD_H */

#ifdef HAVE_STRING_H
# include
#else
# include
# define strchr index
# define strrchr rindex
#endif /* HAVE_STRING_H */
#ifndef HAVE_STRSTR
char *strstr ARGS((char *s, char *p));
#endif /* HAVE_STRSTR */
#ifndef HAVE_STRCASECMP
int strcasecmp ARGS((const char *s1, const char *s2));
int strncasecmp ARGS((const char *s1, const char *s2, int n));
#endif /* HAVE_STRCASECMP */

#ifdef HAVE_MEMORY_H
# include
#endif
#ifndef HAVE_MEMSET
# define memcpy(d, s, n) bcopy(s, d, n)
# define memcmp(s1, s2, n) bcmp(s1, s2, n)
void *memset ARGS((void *d, int c, size_t n));
#endif /* HAVE_MEMSET */
#ifndef HAVE_MEMMOVE
# ifdef HAVE_BCOPY
# define memmove(d, s, n) bcopy(s, d, n)
# else
void *memmove ARGS((void *d, const void *s, size_t n));
# endif
#endif /* HAVE_MEMMOVE */

#ifdef HAVE_PROTOTYPES
# include
# define SH_VA_START(va, argn) va_start(va, argn)
#else
# include
# define SH_VA_START(va, argn) va_start(va)
#endif /* HAVE_PROTOTYPES */

#include
extern int errno;

#ifdef HAVE_FCNTL_H
# include
#else
# include
#endif /* HAVE_FCNTL_H */
#ifndef O_ACCMODE
# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
#endif /* O_ACCMODE */
#ifndef O_NONBLOCK
# ifdef O_NDELAY
# define O_NONBLOCK O_NDELAY
# else /* O_NDELAY */
# define O_NONBLOCK FNDELAY /* hope this exists... */
# endif /* O_NDELAY */
#endif /* O_NONBLOCK */

#ifndef F_OK /* access() arguments */
# define F_OK 0
# define X_OK 1
# define W_OK 2
# define R_OK 4
#endif /* F_OK */

/* Some machines (eg, FreeBSD 1.1.5) define CLK_TCK in limits.h
* (ksh_limval.h assumes limits has been included, if available)
*/
#ifdef HAVE_LIMITS_H
# include
#endif /* HAVE_LIMITS_H */

#include
#ifdef NSIG
# define SIGNALS NSIG
#else
# ifdef _MINIX
# define SIGNALS (_NSIG+1) /* _NSIG is # of signals used, excluding 0. */
# else
# ifdef _SIGMAX /* QNX */
# define SIGNALS _SIGMAX
# else /* _SIGMAX */
# define SIGNALS 32
# endif /* _SIGMAX */
# endif /* _MINIX */
#endif /* NSIG */
#ifndef SIGCHLD
# define SIGCHLD SIGCLD
#endif
/* struct sigaction.sa_flags is set to KSH_SA_FLAGS. Used to ensure
* system calls are interrupted
*/
#ifdef SA_INTERRUPT
# define KSH_SA_FLAGS SA_INTERRUPT
#else /* SA_INTERRUPT */
# define KSH_SA_FLAGS 0
#endif /* SA_INTERRUPT */

#ifdef USE_FAKE_SIGACT
# include "sigact.h" /* use sjg's fake sigaction() */
#endif

#ifdef HAVE_PATHS_H
# include
#endif /* HAVE_PATHS_H */
#ifdef _PATH_DEFPATH
# define DEFAULT__PATH _PATH_DEFPATH
#else /* _PATH_DEFPATH */
# define DEFAULT__PATH DEFAULT_PATH
#endif /* _PATH_DEFPATH */

#ifndef offsetof
# define offsetof(type,id) ((size_t)&((type*)NULL)->id)
#endif

#ifndef HAVE_KILLPG
# define killpg(p, s) kill(-(p), (s))
#endif /* !HAVE_KILLPG */

/* Special cases for execve(2) */
#ifdef OS2
# define ksh_execve(p, av, ev) _execve(p, av, ev)
#else /* OS2 */
# if defined(isc386) && defined(_POSIX_SOURCE)
/* Kludge for ISC 3.2 (and other versions?) so programs will run correctly. */
# define ksh_execve(p, av, ev) do { \
__setostype(0); \
execve(p, av, ev); \
__setostype(1); \
} while (0)
# else /* isc386 && _POSIX */
# define ksh_execve(p, av, ev) execve(p, av, ev)
# endif /* isc386 && _POSIX */
#endif /* OS2 */

#ifdef OS2
extern int ksh_dupbase ARGS((int fd, int base));
#else /* OS2 */
# define ksh_dupbase(fd, base) fcntl(fd, F_DUPFD, base)
#endif /* OS2 */

/* 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 OS2
# define EXECSHELL "c:\\os2\\cmd.exe"
# define EXECSHELL_STR "OS2_SHELL"
# else /* OS2 */
# define EXECSHELL "/bin/sh"
# define EXECSHELL_STR "EXECSHELL"
# endif /* OS2 */
#endif

/* These defines aren't used everywhere yet, but it is being worked on */
#ifdef OS2
# define PATHSEP ';'
# define DIRSEP '\\'
# define DIRSEPSTR "\\"
# define ISDIRSEP(c) ((c) == '\\' || (c) == '/')
extern char *strchr_dirsep(char *path);
extern char *strrchr_dirsep(char *path);
# define chdir _chdir2
# define getcwd _getcwd2
#else
# define PATHSEP ':'
# define DIRSEP '/'
# define DIRSEPSTR "/"
# define ISDIRSEP(c) ((c) == '/')
# define strchr_dirsep(p) strchr(p, DIRSEP)
# define strrchr_dirsep(p) strrchr(p, DIRSEP)
#endif

typedef int bool_t;
#define FALSE 0
#define TRUE 1

#define NELEM(a) (sizeof(a) / sizeof((a)[0]))
#define sizeofN(type, n) (sizeof(type) * (n))
#define BIT(i) (1<<(i)) /* define bit in flag */

#define NUFILE 10 /* 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 */
#define MAGIC (char)(0x80+'!')/* prefix for *?[!{,} during expand */
#define NOT '!' /* might use ^ (ie, [!...] vs [^..]) */

#define LINE 1024 /* input line size */
#define PATH 1024 /* pathname size (todo: PATH_MAX/pathconf()) */
#define ARRAYMAX 1023 /* max array index */

EXTERN char *kshname; /* $0 */
EXTERN pid_t kshpid; /* $$, shell pid */
EXTERN pid_t procpid; /* pid of executing process */
EXTERN int exstat; /* exit status */
EXTERN int subst_exstat; /* exit status of last $(..)/`..` */


/*
* Area-based allocation built on malloc/free
*/

typedef struct Area {
struct Block *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 */


/*
* parsing & execution environment
*/
EXTERN struct env {
short type; /* enviroment 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 enviroment */
jmp_buf jbuf; /* long jump back to env creator */
struct temp *temps; /* temp files */
} *e;

/* struct env.type values */
#define E_NONE 0 /* dummy enviroment */
#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 */

/* 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 longjmp(e->jbuf) */
#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() */


/* option processing */
#define OF_CMDLINE 0x01 /* command line */
#define OF_SET 0x02 /* set builtin */
#define OF_SPECIAL 0x04 /* a special variable changing */
#define OF_ANY (OF_CMDLINE | OF_SET | OF_SPECIAL)

struct option {
char *name; /* long name of option */
char c; /* character flag (if any) */
short flags; /* OF_* */
};
extern struct option options[];

/*
* flags (the order of these enums MUST match the order in options[])
*/
enum sh_flag {
FEXPORT = 0, /* -a: export all */
#ifdef BRACEEXPAND
FBRACEEXPAND, /* enable {} globing */
#endif
FBGNICE, /* bgnice */
FCOMMAND, /* -c: (invocation) execute specified command */
#ifdef EMACS
FEMACS, /* emacs command editing */
#endif
FERREXIT, /* -e: quit on error */
#ifdef EMACS
FGMACS, /* gmacs command editing */
#endif
FIGNOREEOF, /* eof does not exit */
FTALKING, /* -i: interactive */
FKEYWORD, /* -k: name=value anywere */
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 */
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 */
FPOSIX, /* -o posix: be posixly correct */
FPRIVILEGED, /* -p: use suid_profile */
FRESTRICTED, /* -r: restricted shell */
FSTDIN, /* -s: (invocation) parse stdin */
FTRACKALL, /* -h: create tracked aliases for all commands */
FVERBOSE, /* -v: echo input */
#ifdef VI
FVI, /* vi command editing */
FVIRAW, /* always read in raw mode (ignored) */
FVISHOW8, /* display chars with 8th bit set as is (versus M-) */
FVITABCOMPLETE, /* enable tab as file name completion char */
#endif
FXTRACE, /* -x: execution trace */
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_("/");

typedef RETSIGTYPE (*handler_t)(); /* signal handler */

/* temp/here files. the file is removed when the struct is freed */
struct temp {
struct temp *next;
int pid; /* pid of process parsed here-doc */
char *name;
};

/* here documents in functions are treated specially (the get removed when
* shell exis) */
EXTERN struct temp *func_heredocs;

/*
* 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 */
char *name; /* short name */
const char *mess; /* descriptive name */
char *trap; /* trap command */
int volatile set; /* trap pending */
int flags; /* TF_* */
RETSIGTYPE (*cursig)(); /* current handler (valid if TF_ORIG_* set) */
} 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 SIG_DFL */
#define SS_RESTORE_IGN 3 /* restore 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 SIGEXIT_ 0 /* for trap EXIT */
#define SIGERR_ SIGNALS /* for trap ERR */

EXTERN int volatile trap; /* traps pending? */
EXTERN int volatile intrsig; /* pending trap interrupts executing command */
EXTERN int volatile fatal_trap;/* received a fatal signal */
extern Trap sigtraps[SIGNALS+1];


/*
* 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 _I_(0);
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 0x01 /* a-z_A-Z */
#define C_DIGIT 0x02 /* 0-9 */
#define C_LEX1 0x04 /* \0 \t\n|&;<>() */
#define C_VAR1 0x08 /* *@#!$-? */
#define C_IFSWS 0x10 /* \t \n (IFS white space) */
#define C_SUBOP1 0x20 /* "=-+?" */
#define C_SUBOP2 0x40 /* "#%" */
#define C_IFS 0x80 /* $IFS */

extern char 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 */

/* Values for Getopt.info */
#define GI_DONE BIT(0) /* set when at end of options or option error */
#define GI_MINUS BIT(1) /* an option started with -... */
#define GI_PLUS BIT(2) /* an option started with +... */
#define GI_MINUSMINUS BIT(3) /* arguments were ended with -- */

typedef struct {
int optind;
char *optarg;
int flags; /* see GF_* */
int info; /* see GI_* */
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 */


/* This for co-processes */
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 */
void *job; /* 0 if no co-process, or co-process died */
};
EXTERN struct coproc coproc;


/* name of called builtin function (used by error functions) */
EXTERN char *builtin_argv0;
EXTERN int builtin_flag; /* flags of called builtin (SPEC_BI, etc.) */


#ifdef EDIT
EXTERN int x_cols _I_(80); /* tty columns */
#else
# define x_cols 80 /* for pr_menu(exec.c) */
#endif


/* These to avoid bracket matching problems */
#define OPAREN '('
#define CPAREN ')'
#define OBRACK '['
#define CBRACK ']'
#define OBRACE '{'
#define CBRACE '}'


#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_
pdksh-5.1.2/shf.h100644 0 133 6374 5662415121 12414 0ustar rootlsource/*
* Shell file I/O routines
*/
/* $Id$ */

#define SHF_BSIZE 512

#define shf_fileno(shf) ((shf)->fd)
#define shf_setfileno(shf,nfd) ((shf)->fd = (nfd))
#define shf_getc(shf) ((shf)->rnleft > 0 ? (shf)->rnleft--, *(shf)->rp++ : \
shf_getchar(shf))
#define shf_putc(c, shf) ((shf)->wnleft == 0 ? shf_putchar((c), (shf)) \
: ((shf)->wnleft--, *(shf)->wp++ = (c)))
#define shf_eof(shf) ((shf)->flags & SHF_EOF)
#define shf_error(shf) ((shf)->flags & SHF_ERROR)
#define shf_errno(shf) ((shf)->errno_)
#define shf_clearerr(shf) ((shf)->flags &= ~(SHF_EOF | SHF_ERROR))

/* Flags passed to shf_*open() */
#define SHF_RD 0x0001
#define SHF_WR 0x0002
#define SHF_RDWR (SHF_RD|SHF_WR)
#define SHF_ACCMODE 0x0003 /* mask */
#define SHF_GETFL 0x0004 /* use fcntl() to figure RD/WR flags */
#define SHF_UNBUF 0x0008 /* unbuffered I/O */
#define SHF_CLEXEC 0x0010 /* set close on exec flag */
#define SHF_MAPHI 0x0020 /* make fd > FDBASE (and close orig)
* (shf_open() only) */
#define SHF_DYNAMIC 0x0040 /* string: increase buffer as needed */
#define SHF_INTERRUPT 0x0080 /* EINTR in read/write causes error */
/* Flags used internally */
#define SHF_STRING 0x0100 /* a string, not a file */
#define SHF_ALLOCS 0x0200 /* shf and shf->buf were alloc()ed */
#define SHF_ALLOCB 0x0400 /* shf->buf was alloc()ed */
#define SHF_ERROR 0x0800 /* read()/write() error */
#define SHF_EOF 0x1000 /* read eof (sticky) */
#define SHF_READING 0x2000 /* currently reading: rnleft,rp valid */
#define SHF_WRITING 0x4000 /* currently writing: wnleft,wp valid */


struct shf {
int flags; /* see SHF_* */
unsigned char *rp; /* read: current position in buffer */
int rbsize; /* size of buffer (1 if SHF_UNBUF) */
int rnleft; /* read: how much data left in buffer */
unsigned char *wp; /* write: current position in buffer */
int wbsize; /* size of buffer (0 if SHF_UNBUF) */
int wnleft; /* write: how much space left in buffer */
unsigned char *buf; /* buffer */
int fd; /* file descriptor */
int errno_; /* saved value of errno after error */
int bsize; /* actual size of buf */
Area *areap; /* area shf/buf were allocated in */
};

extern struct shf shf_iob[];

struct shf *shf_open ARGS((char *name, int oflags, int mode, int sflags));
struct shf *shf_fdopen ARGS((int fd, int sflags, struct shf *shf));
struct shf *shf_reopen ARGS((int fd, int sflags, struct shf *shf));
struct shf *shf_sopen ARGS((char *buf, int bsize, int sflags,
struct shf *shf));
int shf_close ARGS((struct shf *shf));
int shf_fdclose ARGS((struct shf *shf));
char *shf_sclose ARGS((struct shf *shf));
int shf_finish ARGS((struct shf *shf));
int shf_flush ARGS((struct shf *shf));
int shf_read ARGS((char *buf, int bsize, struct shf *shf));
char *shf_getse ARGS((char *buf, int bsize, struct shf *shf));
int shf_getchar ARGS((struct shf *shf));
int shf_ungetc ARGS((int c, struct shf *shf));
int shf_putchar ARGS((int c, struct shf *shf));
int shf_write ARGS((char *buf, int nbytes, struct shf *shf));
int shf_fprintf ARGS((struct shf *shf, const char *fmt, ...));
int shf_snprintf ARGS((char *buf, int bsize, char *fmt, ...));
/* stdarg/varargs may not have been included yet.. */
int shf_vfprintf ARGS((/*struct shf *, char *fmt, va_list args*/));
pdksh-5.1.2/sigact.h100644 0 133 6424 5627370147 13113 0ustar rootlsource/* NAME:
* sigact.h - sigaction et al
*
* SYNOPSIS:
* #include "sigact.h"
*
* DESCRIPTION:
* This header is the interface to a fake sigaction(2)
* implementation. It provides a POSIX compliant interface
* to whatever signal handling mechanisms are available.
* It also provides a Signal() function that is implemented
* in terms of sigaction().
* If not using signal(2) as part of the underlying
* implementation (USE_SIGNAL or USE_SIGMASK), and
* NO_SIGNAL is not defined, it also provides a signal()
* function that calls Signal().
*
* SEE ALSO:
* sigact.c
*/
/*
* RCSid:
* $Id: sigact.h,v 1.2 1994/05/31 13:34:34 michael Exp $
*/
/* Changes to sigact.h for pdksh, Michael Rendell :
* - changed SIG_HDLR to RETSIGTYPE for use with GNU autoconf
* - ifdef'd out ARGS(), volatile and const initializations
* - ifdef'd out sigset_t definition - let autoconf handle it
* - ifdef out routines not used in ksh if IS_KSH is defined
* (same in sigact.c).
*/
#ifndef _SIGACT_H
#define _SIGACT_H

/*
* most modern systems use void for signal handlers but
* not all.
*/
#ifndef RETSIGTYPE
# define RETSIGTYPE void
#endif

#if 0 /* ARGS(), volatile and const are already set up in config*.h -mhr */
#undef ARGS
#if defined(__STDC__) || defined(__cplusplus)
# define ARGS(p) p
#else
# define ARGS(p) ()
# define volatile /* don't optimize please */
# define const /* read only */
#endif
#endif

#ifndef IS_KSH
RETSIGTYPE (*Signal ARGS((int sig, RETSIGTYPE (*disp)(int)))) ARGS((int));
#endif /* IS_KSH */

/*
* if you want to install this header as signal.h,
* modify this to pick up the original signal.h
*/
#ifndef SIGKILL
# include
#endif

#ifndef SIG_ERR
# define SIG_ERR (RETSIGTYPE (*)())-1
#endif
#ifndef BADSIG
# define BADSIG SIG_ERR
#endif

#ifndef SA_NOCLDSTOP
/* we assume we need the fake sigaction */
/* sa_flags */
#define SA_NOCLDSTOP 1 /* don't send SIGCHLD on child stop */
#define SA_RESTART 2 /* re-start I/O */

/* sigprocmask flags */
#define SIG_BLOCK 1
#define SIG_UNBLOCK 2
#define SIG_SETMASK 4

#if 0 /* autoconf will define sigset_t if it isn't available */
/*
* this is a bit untidy
*/
#if !defined(__sys_stdtypes_h)
typedef unsigned int sigset_t;
#endif
#endif /* 0 */

/*
* POSIX sa_handler should return void, but since we are
* implementing in terms of something else, it may
* be appropriate to use the normal RETSIGTYPE return type
*/
struct sigaction
{
RETSIGTYPE (*sa_handler)();
sigset_t sa_mask;
int sa_flags;
};


int sigaction ARGS(( int sig, struct sigaction *act, struct sigaction *oact ));
int sigaddset ARGS(( sigset_t *mask, int sig ));
#ifndef IS_KSH
int sigdelset ARGS(( sigset_t *mask, int sig ));
#endif /* IS_KSH */
int sigemptyset ARGS(( sigset_t *mask ));
#ifndef IS_KSH
int sigfillset ARGS(( sigset_t *mask ));
int sigismember ARGS(( sigset_t *mask, int sig ));
int sigpending ARGS(( sigset_t *set ));
#endif /* IS_KSH */
int sigprocmask ARGS(( int how, sigset_t *set, sigset_t *oset ));
int sigsuspend ARGS(( sigset_t *mask ));

#ifndef sigmask
# define sigmask(s) (1<<((s)-1)) /* convert SIGnum to mask */
#endif
#if !defined(NSIG) && defined(_NSIG)
# define NSIG _NSIG
#endif
#endif /* ! SA_NOCLDSTOP */
#endif /* _SIGACT_H */
pdksh-5.1.2/table.h100644 0 133 10701 5644636332 12741 0ustar rootlsource/* $Id: table.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */

/*
* generic hashed associative table for commands and variables.
*/

struct table {
Area *areap; /* area to allocate entries */
short size, nfree; /* hash size (always 2^^n), free entries */
struct tbl **tbls; /* hashed table items */
};

struct tbl { /* table item */
INT32 flag; /* flags */
int type; /* command type (see below), base (if INTEGER),
* or offset from val.s of value (if EXPORT) */
Area *areap; /* area to allocate from */
union {
char *s; /* string */
long i; /* integer */
int (*f) ARGS((char **)); /* int function */
struct op *t; /* "function" tree */
} val; /* value */
int index; /* index for an array */
int field; /* field with for -L/-R/-Z */
struct tbl *array; /* array values */
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 = */
/* 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 SPEC_BI BIT(11) /* a POSIX special builtin */
#define REG_BI BIT(12) /* a POSIX regular builtin */

/* 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 FC_NOAUTOLOAD BIT(6) /* no function autoloading */

/*
* activation record for function blocks
*/
struct block {
Area area; /* area to allocate things */
int argc; /* current $# */
char ** argv; /* current $* */
struct table vars; /* local variables */
struct table funs; /* local functions */
#if 1
char * error; /* error handler */
char * exit; /* exit handler */
#else
Trap error, exit;
#endif
struct block *next; /* enclosing block */
};

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 {
char *name;
int (*func)();
};

/* these really are externs! Look in table.c for them */
extern const struct builtin shbuiltins [], kshbuiltins [];

/* var spec values */
#define V_NONE 0
#define V_PATH 1
#define V_IFS 2
#define V_SECONDS 3
#define V_OPTIND 4
#define V_MAIL 5
#define V_MAILPATH 6
#define V_MAILCHECK 7
#define V_RANDOM 8
#define V_HISTSIZE 9
#define V_HISTFILE 10
#define V_VISUAL 11
#define V_EDITOR 12
#define V_COLUMNS 13
#define V_POSIXLY_CORRECT 14
#define V_TMOUT 15
#define V_TMPDIR 16

/* values for set_prompt() */
#define PS1 0 /* command */
#define PS2 1 /* command continuation */

EXTERN char *path; /* PATH value */
EXTERN char *def_path; /* path to use if PATH not set */
EXTERN char *tmpdir; /* TMPDIR value */
EXTERN char *prompt;
EXTERN int cur_prompt; /* PS1 or PS2 */
pdksh-5.1.2/tree.h100644 0 133 10226 5647247633 12620 0ustar rootlsource/*
* command trees for compile/execute
*/

/* $Id: tree.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */

#define NOBLOCK ((struct op *)NULL)
#define NOWORD ((char *)NULL)
#define NOWORDS ((char **)NULL)

/*
* Description of a command or an operation on commands.
*/
struct op {
short type; /* operation type, see below */
short evalflags; /* eval() flags for arg expansion */
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
*/
};

/* 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 ${ substitution */
#define CSUBST 8 /* closing } of above */

/*
* IO redirection
*/
struct ioword {
int unit; /* unit affected */
int flag; /* action (below) */
char *name; /* file name */
};

/* 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) */

/* 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 */

/*
* 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 ${..[%#=?]..} */

/*
* 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 */
pdksh-5.1.2/tty.h100644 0 133 4253 5663163547 12463 0ustar rootlsource/*
tty.h -- centralized definitions for a variety of terminal interfaces

created by DPK, Oct. 1986

Rearranged to work with autoconf, added TTY_state, get_tty/set_tty
Michael Rendell, May '94

last edit: 30-Jul-1987 D A Gwyn
*/
/* $Id$ */

/* some useful #defines */
#ifdef EXTERN
# define _I_(i) = i
#else
# define _I_(i)
# define EXTERN extern
# define EXTERN_DEFINED
#endif

#ifdef HAVE_TERMIOS_H
# include
typedef struct termios TTY_state;
#else
# ifdef HAVE_TERMIO_H
# include
# if _BSD_SYSV /* BRL UNIX System V emulation */
# ifndef NTTYDISC
# define TIOCGETD _IOR( 't', 0, int )
# define TIOCSETD _IOW( 't', 1, int )
# define NTTYDISC 2
# endif
# ifndef TIOCSTI
# define TIOCSTI _IOW( 't', 114, char )
# endif
# ifndef TIOCSPGRP
# define TIOCSPGRP _IOW( 't', 118, int )
# endif
# endif /* _BSD_SYSV */
typedef struct termio TTY_state;
# else /* HAVE_TERMIO_H */
/* Assume BSD tty stuff. Uses TIOCGETP, TIOCSETN; uses TIOCGATC/TIOCSATC if
* available, otherwise it uses TIOCGETC/TIOCSETC (also uses TIOCGLTC/TIOCSLTC
* if available)
*/
# ifdef _MINIX
# include
# define TIOCSETN TIOCSETP
# else
# include
# endif
typedef struct {
struct sgttyb sgttyb;
# ifdef TIOCGATC
struct lchars lchars;
# else /* TIOCGATC */
struct tchars tchars;
# ifdef TIOCGLTC
struct ltchars ltchars;
# endif /* TIOCGLTC */
# endif /* TIOCGATC */
} TTY_state;
# endif /* HAVE_TERMIO_H */
#endif /* HAVE_TERMIOS_H */

/* Flags for set_tty() */
#define TF_NONE 0x00
#define TF_WAIT 0x01 /* drain output, even it requires sleep() */
#define TF_MIPSKLUDGE 0x02 /* kludge to unwedge RISC/os 5.0 tty driver */

EXTERN int tty_fd _I_(-1); /* dup'd tty file descriptor */
EXTERN int tty_devtty; /* true if tty_fd is from /dev/tty */
EXTERN TTY_state tty_state; /* saved tty state */

extern int get_tty ARGS((int fd, TTY_state *ts));
extern int set_tty ARGS((int fd, TTY_state *ts, int flags));
extern void tty_init ARGS((int init_ttystate));
extern void tty_close ARGS((void));

/* be sure not to interfere with anyone else's idea about EXTERN */
#ifdef EXTERN_DEFINED
# undef EXTERN_DEFINED
# undef EXTERN
#endif
#undef _I_
pdksh-5.1.2/ksh.1100644 0 133 345625 5666432670 12413 0ustar rootlsource'\" t
.\"{{{}}}
.\"{{{ To do
.\" todo: Things not covered that should be:
.\" - distinguish (POSIX) special built-in's, (POSIX) regular built-in's,
.\" and sh/ksh weirdo built-in's (put S,R,X superscripts after command
.\" name in built-in commands section?)
.\" - explain the difference between special, regular, weirdo built-in's
.\" - need to be consistent about notation for `See section-name', `
.\" See description of foobar command', `See section section-name', etc.
.\" - need to use the term `external command' meaning `a command that is
.\" executed using execve(2)' (as opposed to a built-in command or
.\" function) for more clear description.
.\" - how commands are found (PATH, etc.) in Command Execution section
.\" - mention how exit status is calculated in Command Execution.
.\" - use .if "\n(sh"0" \{A special ksh feature\} more to mark ksh-only
.\" (non-sh) features.
.\"}}}
.\"{{{ Title
.\" To get a sh (not ksh) manual, set this to 1.
.nr sh 0
.TH KSH 1 "November 28, 1994" "" "User commands"
.\"}}}
.\"{{{ Name
.SH NAME
ksh \- Public domain Korn shell
.\"}}}
.\"{{{ Synopsis
.SH SYNOPSIS
.ad l
\fBksh\fP [\fB\(+-abCefhikmnprsuvxX\fP] [\fB\(+-o\fP \fIoption\fP] [ [ \fB\-c\fP \fIcommand-string\fP [\fIcommand-name\fP] | \fB\-s\fP | \fIfile\fP ] [\fIargument\fP ...] ]
.ad b
.\"}}}
.\"{{{ Description
.SH DESCRIPTION
\fBksh\fP is a command interpreter that is intended for both interactive and
shell script use. Its command language is a superset of the \fIsh\fP(1)
shell language.
.\"{{{ Shell Startup
.SS "Shell Startup"
The following options can be specified only on the command line:
.IP "\fB\-c\fP \fIcommand-string\fP"
the shell executes the command(s) contained in \fIcommand-string\fP
.IP \fB\-i\fP
interactive mode \(em see below
.IP \fB\-s\fP
the shell reads commands from standard input; all non-option arguments
are positional parameters
.IP \fB\-r\fP
restricted mode \(em see below
.PP
In addition to the above, the options described in the \fBset\fP builtin
command can also be used on the command line.
.PP
If neither the \fB\-c\fP nor the \fB\-s\fP options are specified, the
first non-option argument specifies the name of a file the shell reads
commands from; if there are no non-option arguments, the shell reads
commands from standard input.
The name of the shell (i.e., the contents of the \fB$0\fP) parameter
is determined as follows: if the \fB\-c\fP option is used and there is
a non-option argument, it is used as the name; if commands are being
read from a file, the file is used as the name; otherwise the name
the shell was called with (i.e., argv[0]) is used.
.PP
A shell is \fIinteractive\fP if the \fB\-i\fP option is used or
if both standard input and standard error are attached to a tty.
An interactive shell has job control enabled (if available),
ignores the INT, QUIT and TERM signals, and prints prompts before
reading input (see \fBPS1\fP and \fBPS2\fP parameters).
For non-interactive shells, the \fBtrackall\fP option is on by default
(see \fBset\fP command below).
.PP
A shell is \fBrestricted\fP if the \fB\-r\fP option is used or if either
the basename of the name the shell is invoked with or the \fBSHELL\fP
parameter match the pattern *r*sh (e.g., rsh, rksh, rpdksh, etc.).
The following restrictions come into effect after the shell processes
any profile and \fB$ENV\fP files:
.nr P2 \n(PD
.nr PD 0
.IP \ \ \(bu
the \fBcd\fP command is disabled
.IP \ \ \(bu
the \fBSHELL\fP, \fBENV\fP and \fBPATH\fP parameters can't be changed
.IP \ \ \(bu
command names can't be specified with absolute or relative paths
.IP \ \ \(bu
the \fB\-p\fP option of the \fBcommand\fP builtin can't be used
.IP \ \ \(bu
redirections that create files can't be used (i.e., \fB>\fP,
\fB>|\fP, \fB>>\fP, \fB<>\fP)
.nr PD \n(P2
.PP
A shell is \fBprivileged\fP if the \fB\-p\fP option is used or if
the real user-id or group-id does not match the effective user-id
or group-id (see \fIgetuid\fP(2), \fIgetgid\fP(2)).
A privileged shell does not process $HOME/.profile nor the \fBENV\fP
parameter (see below), instead the file /etc/suid_profile is processed.
Clearing the privileged option causes the shell to set its effective
user-id (group-id) to its real user-id (group-id).
.PP
If the basename of the name the shell is called with (i.e., argv[0]) starts
with \fB\-\fP, the shell is assumed to be a login shell and the shell
reads and executes the contents of \fB/etc/profile\fP and \fB$HOME/.profile\fP
if they exist and are readable.
.PP
If the \fBENV\fP parameter is set when the shell starts (or, in the
case of login shells, after any profiles are processed), its value
is subjected to parameter, command and arithmetic substitution and
the resulting file (if any) is read and executed.
.PP
The exit status of the shell is 127 if the command file specified
on the command line could not be opened, or non-zero if a fatal syntax
error occurred during the execution of a script.
In the absence of fatal errors, the exit status is that of the last
command executed, or zero, if no command is executed.
.\"}}}
.\"{{{ Command Syntax
.SS "Command Syntax"
.\"{{{ words and tokens
The shell begins parsing its input by breaking it into \fIword\fPs.
Words, which are sequences of characters, are delimited by unquoted
\fIwhite-space\fP characters (space, tab and newline) or \fImeta-characters\fP
(\fB<\fP, \fB>\fP, \fB|\fP, \fB;\fP, \fB&\fP, \fB(\fP and \fB)\fP).
Aside from delimiting words, spaces and tabs are ignored, while
newlines usually delimit commands.
The meta-characters are used in building the following tokens:
\fB<\fP, \fB<&\fP, \fB<<\fP, \fB>\fP, \fB>&\fP, \fB>>\fP, etc. used to
specify redirections (see Input/Output Redirection below);
\fB|\fP used to create pipelines;
\fB|&\fP used to create co-processes (see Co-Processes below);
\fB;\fP used to separate commands;
\fB&\fP used to create asynchronous pipelines;
\fB&&\fP and \fB||\fP used to specify conditional execution;
\fB;;\fP used in \fBcase\fP statements;
\fB(\fP .. \fB)\fP used to start subshells;
and lastly,
\fB((\fP .. \fB))\fP which are used in arithmetic expressions;
.PP
White-space and meta-characters can be quoted individually using
backslash (\fB\e\fP), or in groups using double (\fB"\fP) or single (\fB'\fP)
quotes.
Note that the following characters are also treated specially by the shell and
must be quoted if they are to represent themselves:
\fB\e\fP, \fB"\fP, \fB'\fP, \fB#\fP, \fB$\fP, \fB`\fP, \fB~\fP, \fB{\fP,
\fB}\fP, \fB*\fP, \fB?\fP and \fB[\fP.
The first three of these are the above mentioned quoting characters
(see Quoting below);
\fB#\fP, if used at the beginning of a word, introduces a comment \(em everything
after the \fB#\fP up to the nearest newline is ignored;
\fB$\fP is used to introduce parameter, command and arithmetic substitutions
(see Substitution below);
\fB`\fP introduces an old-style command substitution
(see Substitution below);
\fB~\fP begins a directory expansion (see Tilde Expansion below);
\fB{\fP and \fB}\fP delimit \fIcsh\fP(1) style alternations
(see Brace Expansion below);
and, finally, \fB*\fP, \fB?\fP and \fB[\fP are used in file name generation
(see File Name Patterns below).
.\"}}}
.\"{{{ simple-command
.PP
As words and tokens are parsed, the shell builds commands, of which
there are two basic types: \fIsimple-commands\fP, typically programs
that are executed, and \fIcompound-commands\fP, such as \fBfor\fP and
\fBif\fP statements, grouping constructs and function definitions.
.PP
A simple-command consists of some combination of parameter assignments (see
Parameters below), input/output redirections (see Input/Output Redirections
below), and command words; the only restriction is that parameter assignments
come before any command words.
The command words, if any, define the command that is to be executed and its
arguments.
The command may be a shell built-in command, a function or an \fIexternal
command\fP, i.e., a separate executable file that is located using the
\fBPATH\fP parameter (see Command Execution below).
Note that all command constructs have an \fIexit status\fP: for external
commands, this is related to the status returned by \fIwait\fP(2);
the exit status of other command constructs (built-in commands, functions,
compound-commands, pipelines, lists, etc.) are all well defined and are
described where the construct is described.
The exit status of a command consisting only of parameter assignments is that
of the last command substitution performed during the parameter assignment
or zero if there were no command substitutions.
.\"}}}
.\"{{{ pipeline
.PP
Commands can be chained together using the \fB|\fP token to
form \fIpipelines\fP, in which the standard output of each command but
the last is piped (see \fIpipe\fP(2)) to the standard input of the following
command.
The exit status of a pipeline is that of its last command.
A pipeline may be prefixed by the \fB!\fP reserved word which
causes the exit status of the pipeline to be logically
complemented: if the original status was 0 the complemented status will
be 1, and if the original status was not 0, then the complemented
status will be 0.
.\"}}}
.\"{{{ lists
.PP
\fILists\fP of commands can be created by separating pipelines by
any of the following tokens: \fB&&\fP, \fB||\fP, \fB&\fP, \fB|&\fP and \fB;\fP.
The first two are for conditional execution: \fIcmd1\fP \fB&&\fP \fIcmd2\fP
executes \fIcmd2\fP only if the exit status of \fIcmd1\fP is zero;
\fB||\fP is the opposite \(em \fIcmd2\fP is executed only if the exit status
of \fIcmd1\fP is non-zero.
\fB&&\fP and \fB||\fP have equal precedence which is higher than that of
\fB&\fP, \fB|&\fP and \fB;\fP, which also have equal precedence.
The \fB&\fP token causes the preceding command to be executed asynchronously,
that is, the shell starts the command, but does not wait for it to complete
(the shell does keep track of the status of asynchronous commands \(em see
Job Control below).
When an asynchronous command is started when job control is disabled (i.e.,
in most scripts), the command is started with signals INT and QUIT ignored
and with input redirected from /dev/null (however, redirections specified in
the asynchronous command have precedence).
The \fB|&\fP operator starts a \fIco-process\fP which is special kind of
asynchronous process (see Co-Processes below).
Note that a command must follow the \fB&&\fP and \fB||\fP operators, while
a command need not follow \fB&\fP, \fB|&\fP and \fB;\fP.
The exit status of a list is that of the last command executed, with the
exception of asynchronous lists, for which the exit status is 0.
.\"}}}
.\"{{{ compound-commands
.PP
Compound commands are created used the following reserved words \(em these
words are only recognized if they are unquoted and if they are used as
the first word of a command (i.e., they can't be preceded by parameter
assignments or redirections):
.TS
center;
lfB lfB lfB lfB lfB .
case else function then !
do esac if time [[
done fi in until {
elif for select while }
.TE
\fBNote:\fP Some shells (but not ksh) execute control structure commands
in a subshell when one or more of their file descriptors are redirected, so
any environment changes inside them may fail.
To be portable, the \fBexec\fP statement should be used instead to redirect
file descriptors before the control structure.
.\"{{{ ( list )
.IP "\fB(\fP \fIlist\fP \fB)\fP"
Execute \fIlist\fP in a subshell. There is no implicit way to pass
environment changes from a subshell back to its parent.
.\"}}}
.\"{{{ { list }
.IP "\fB{\fP \fIlist\fP \fB}\fP"
Compound construct; \fIlist\fP is executed, but not in a subshell.
Note that \fB{\fP and \fB}\fP are not meta-characters and thus must be
distinct words.
.\"}}}
.\"{{{ case word in [ [ ( ] pattern [ | pattern ] ... ) list ;; ] ... esac
.IP "\fBcase\fP \fIword\fP \fBin\fP [ [\fB(\fP] \fIpattern\fP [\fB|\fP \fIpattern\fP] ... \fB)\fP \fIlist\fP \fB;;\fP ] ... \fBesac\fP"
The \fBcase\fP statement attempts to match \fIword\fP against the specified
\fIpattern\fPs; the \fIlist\fP associated with the first successfully matched
pattern is executed. Patterns used in \fBcase\fP statements are the same as
those used for file name patterns except that the restrictions regarding
\fB\&.\fP and \fB/\fP are dropped. Note that any unquoted space before and
after a pattern is stripped; any space with a pattern must be quoted. Both the
word and the patterns are subject to parameter, command, and arithmetic
substitution as well as tilde substitution. The exit status of a \fBcase\fP
statement is that of the executed \fIlist\fP; if no \fIlist\fP is executed,
the exit status is zero.
.\"}}}
.\"{{{ for name [ in word ... term ] do list done
.IP "\fBfor\fP \fIname\fP [ \fBin\fP \fIword\fP ... \fIterm\fP ] \fBdo\fP \fIlist\fP \fBdone\fP"
where \fIterm\fP is either a newline or a \fB;\fP.
For each \fIword\fP in the specified word list, the parameter \fIname\fP is
set to the word and \fIlist\fP is executed. If \fBin\fP is not used to
specify a word list, the positional parameters (\fB$1\fP, \fB$2\fP, etc.) are
used instead. The exit status of a \fBfor\fP statement is the last exit status
of \fIlist\fP; if \fIlist\fP is never executed, the exit status is zero.
.\"}}}
.\"{{{ if list then list [ elif list then list ] ... [ else list ] fi
.IP "\fBif\fP \fIlist\fP \fBthen\fP \fIlist\fP [\fBelif\fP \fIlist\fP \fBthen\fP \fIlist\fP] ... [\fBelse\fP \fIlist\fP] \fBfi\fP
If the exit status of the first \fIlist\fP is zero, the second \fIlist\fP
is executed; otherwise the \fIlist\fP following the \fBelif\fP, if any, is
executed with similar consequences. If all the lists following the \fBif\fP
and \fBelif\fPs fail (i.e., exit with non-zero status), the \fIlist\fP following
the \fBelse\fP is executed. The exit status of an \fBif\fP statement is that
of non-conditional \fIlist\fP that is executed; if no non-conditional
\fIlist\fP is executed, the exit status is zero.
.\"}}}
.\"{{{ select name [ in word ... ] do list done
.if "\n(sh"0"\{.IP "\fBselect\fP \fIname\fP [ \fBin\fP \fIword\fP ... \fIterm\fP ] \fBdo\fP \fIlist\fP \fBdone\fP"
where \fIterm\fP is either a newline or a \fB;\fP.
The \fBselect\fP statement provides an automatic method of presenting
the user with a menu and selecting from it.
An enumerated list of the specified \fIwords\fP are printed on standard
error, followed by a prompt (\fBPS3\fP, normally `\fB#? \fP').
A number corresponding to one of the enumerated words is then read
from standard input, \fIname\fP is set to the selected word (or is
unset if the selection is not valid), \fBREPLY\fP
is set to what was read, and \fIlist\fP is executed.
This process is repeated until an end-of-file is read, an interrupt is
received or a break statement is executed inside the loop.
If \fBin\fP \fIword\fP \fB\&...\fP is omitted, the positional parameters
are used (i.e., \fB$@\fP).
The exit status of a \fBselect\fP statement is zero if a break statement
is used to exit the loop, non-zero otherwise.
\}
.\"}}}
.\"{{{ until list do list done
.IP "\fBuntil\fP \fIlist\fP \fBdo\fP \fIlist\fP \fBdone\fP"
This works like \fBwhile\fP, except that the body is executed only while the
exit status of the first \fIlist\fP is non-zero.
.\"}}}
.\"{{{ while list do list done
.IP "\fBwhile\fP \fIlist\fP \fBdo\fP \fIlist\fP \fBdone\fP"
A \fBwhile\fP is a prechecked loop. Its body is executed as often
as the exit status of the first \fIlist\fP is zero.
The exit status of a \fBwhile\fP statement is the last exit status
of the \fIlist\fP in the body of the loop; if the body is not executed,
the exit status is zero.
.\"}}}
.\"{{{ function name { list }
.IP "\fBfunction\fP \fIname\fP \fB{\fP \fIlist\fP \fB}\fP"
Defines the function \fIname\fP.
See Functions below.
Note that redirections specified after a function definition are
performed whenever the function is executed, not when the function
definition is executed.
.\"}}}
.\"{{{ name () command
.IP "\fIname\fP \fB()\fP \fIcommand\fP"
Mostly the same as \fBfunction\fP.
See Functions below.
.\"}}}
.\"{{{ (( expression ))
.IP "\fB((\fP \fIexpression\fP \fB))\fP"
The arithmetic expression \fIexpression\fP is evaluated;
equivalent to \fBlet "\fP\fIexpression\fP\fB"\fP.
See Arithmetic Expressions and the \fBlet\fP command below.
.\"}}}
.\"{{{ [[ expression ]]
.IP "\fB[[\fP \fIexpression\fP \fB]]\fP"
Similar to the \fBtest\fP and \fB[\fP \&... \fB]\fP commands (described later),
with the following exceptions:
.RS
.nr P2 \n(PD
.nr PD 0
.IP \ \ \(bu
Field splitting and file name generation are not performed on
arguments.
.IP \ \ \(bu
Operators (e.g. \fB\-f\fP, \fB=\fP, \fB!\fP, etc.) must be unquoted.
.IP \ \ \(bu
The second operand of \fB!=\fP and \fB=\fP
expressions are patterns (e.g., the comparison in \fB[[ foobar = f*r ]]\fP
succeeds)
.IP \ \ \(bu
There are two additional binary operators:
\fB<\fP and \fB>\fP
which return true if their first string operand is less than,
or greater than, their second string operand, respectively.
.IP \ \ \(bu
The single argument form
of \fBtest\fP, which tests if the argument has non-zero length, is not valid
because explicit operators must be always be used.
.nr PD \n(P2
.RE
.\"}}}
.\"}}}
.\"}}}
.\"{{{ Quoting
.SS Quoting
Quoting is used to prevent the shell from treating characters or words
specially.
There are three methods of quoting: First, \fB\e\fP quotes
the following character, unless it is at the end of a line, in which
case both the \fB\e\fP and the newline are stripped.
Second, a single quote (\fB'\fP) quotes everything up to the next single
quote (this may span lines).
Third, a double quote (\fB"\fP) quotes all characters,
except \fB$\fP, \fB`\fP and \fB\e\fP, up to the next unquoted double quote.
\fB$\fP and \fB`\fP inside double quotes have their usual meaning (i.e.,
parameter, command or arithmetic substitution) except no field splitting
is carried out on the results of double-quoted substitutions.
If a \fB\e\fP inside a double-quoted string is followed by \fB\e\fP, \fB$\fP,
\fB`\fP or \fB"\fP, it is replaced by the second character; if it is
followed by a newline, both the \fB\e\fP and the newline are stripped;
otherwise, both the \fB\e\fP and the character following are unchanged.
.PP
Note: see POSIX Mode below for a special rule regarding sequences
of the form \fB"\fP...\fB`\fP...\fB\e"\fP...\fB`\fP..\fB"\fP.
.\"}}}
.\"{{{ Aliases
.SS "Aliases"
There are two types of aliases: normal command aliases and tracked
aliases. Command aliases are normally used as a short hand for a long
or often used command. The shell expands command aliases (i.e.,
substitutes the alias name for its value) when it reads the first word
of a command. An expanded alias is re-processed to check for more
aliases. If a command alias ends in a space or tab, the following word
is also checked for alias expansion. The alias expansion process stops
when a word that is not an alias is found, when a quoted word is found
or when an alias word that is currently being expanded is found.
.sp
The following command aliases are defined automatically by the shell:
.ft B
.RS
.nf
autoload='typeset \-uf'
functions='typeset \-f'
hash='alias \-t \-\-'
history='fc \-l'
integer='typeset \-i'
local='typeset'
login='exec login'
newgrp='exec newgrp'
nohup='nohup '
pwd='print \-r \-\- $PWD'
r='fc \-e \-'
stop='kill \-STOP $$'
suspend='kill \-STOP $$'
type='whence \-v'
.fi
.RE
.ft P
.sp
Tracked aliases allow the shell to remember where it found a particular
command. The first time the shell does a path search for a command that
is marked as a tracked alias, it saves the full path of the command.
The next time the command is executed, the shell checks the saved path
to see that it is still valid, and if so, avoids repeating the path
search. Tracked aliases can be listed and created using \fBalias
\-t\fP. Note that changing the \fBPATH\fP parameter clears the saved
paths for all tracked aliases. If the \fBtrackall\fP option is set (i.e.,
\fBset \-o trackall\fP or \fBset \-h\fP), the shell tracks all
commands. This option is set automatically for non-interactive shells.
For interactive shells, only the following commands are automatically
tracked: \fBcat\fP, \fBcc\fP, \fBchmod\fP, \fBcp\fP, \fBdate\fP, \fBed\fP,
\fBemacs\fP, \fBgrep\fP, \fBls\fP, \fBmail\fP, \fBmake\fP, \fBmv\fP,
\fBpr\fP, \fBrm\fP, \fBsed\fP, \fBsh\fP, \fBvi\fP and \fBwho\fP.
.\"}}}
.\"{{{ Substitution
.SS "Substitution"
The first step the shell takes in executing a simple-command is to
perform substitutions on the words of the command.
There are three kinds of substitution: parameter, command and arithmetic.
Parameter substitutions, which are described in detail in the next section,
take the form \fB$name\fP or \fB${\fP...\fB}\fP; command substitutions take
the form \fB$(\fP\fIcommand\fP\fB)\fP or \fB`\fP\fIcommand\fP\fB`\fP;
and arithmetic substitutions take the form \fB$((\fP\fIexpression\fP\fB))\fP.
.PP
If a substitution appears outside of double quotes, the results of the
substitution are generally subject to word or field splitting according to
the current value of the \fBIFS\fP parameter.
The \fBIFS\fP parameter specifies a list of characters which
are used to break a string up into several words;
any characters from the set space, tab and newline that appear in the
IFS characters are called \fIIFS white space\fP.
Sequences of one or more IFS white space characters, in combination with
zero or one non-IFS white space characters delimit a field.
As a special case, leading and trailing IFS white space is stripped (i.e.,
no leading or trailing empty field is created by it); leading or trailing
non-IFS white space does create an empty field.
Example: if \fBIFS\fP is set to `:', the sequence of characters
`A:B::D' contains four fields: `A', `B', `' and `D'.
Note that if the \fBIFS\fP parameter is set to the null string, no
field splitting is done; if the parameter is unset, the default value
of space, tab and newline is used.
.PP
The results of substitution are, unless otherwise specified, also subject
to brace expansion and file name expansion (see the relevant sections
below).
.PP
A command substitution is replaced by the output generated by the specified
command, which is run in a subshell.
For \fB$(\fP\fIcommand\fP\fB)\fP substitutions, normal quoting rules
are used when \fIcommand\fP is parsed, however, for the
\fB`\fP\fIcommand\fP\fB`\fP form, a \fB\e\fP followed by any of
\fB$\fP, \fB`\fP or \fB\e\fP is stripped (a \fB\e\fP followed by any other
character is unchanged).
As a special case in command substitutions, a command of the form
\fB<\fP \fIfile\fP is interpreted to mean substitute the contents
of \fIfile\fP ($(< foo) has the same effect as $(cat foo), but it
is carried out more efficiently because no process is started).
.br
.\"todo: fix this( $(..) parenthesis counting).
NOTE: \fB$(\fP\fIcommand\fP\fB)\fP expressions are currently parsed by
finding the matching parenthesis, regardless of quoting. This will hopefully
be fixed soon.
.PP
Arithmetic substitutions are replaced by the value of the specified
expression.
For example, the command \fBecho $((2+3*4))\fP prints 14.
See Arithmetic Expressions for a description of an \fIexpression\fP.
.\"}}}
.\"{{{ Parameters
.SS "Parameters"
Parameters are shell variables; they can be assigned values and
their values can be accessed using a parameter substitution.
A parameter name is either one of the special single punctuation or digit
character parameters described below, or a letter followed by zero or more
letters or digits (`_' counts as a letter).
Parameter substitutions take the form \fB$\fP\fIname\fP or
\fB${\fP\fIname\fP\fB}\fP, where \fIname\fP is a parameter name.
If substitution is performed on a parameter that is not set, a null
string is substituted unless the \fBnounset\fP option (\fBset \-o nounset\fP
or \fBset \-u\fP) is set, in which case an error occurs.
.PP
.\"{{{ parameter assignment
Parameters can be assigned values in a number of ways.
First, the shell implicitly sets some parameters like \fB#\fP, \fBPWD\fP,
etc.; this is the only way the special single character parameters are
set.
Second, parameters are imported from the shell's environment at startup.
Third, parameters can be assigned values on the command line, for example,
`\fBFOO=bar\fP' sets the parameter FOO to bar; multiple parameter
assignments can be given on a single command line and they can
be followed by a simple-command, in which case the assignments are
in effect only for the duration of the command (such assignments are
also exported, see below for implications of this).
Note that both the parameter name and the \fB=\fP must be unquoted for
the shell to recognize a parameter assignment.
The fourth way of setting a parameter is with the \fBexport\fP, \fBreadonly\fP
and \fBtypeset\fP commands; see their descriptions in the Command Execution
section.
Fifth, \fBfor\fP and \fBselect\fP loops set parameters as well as
the \fBgetopts\fP, \fBread\fP and \fBset \-A\fP commands.
Lastly, parameters can be assigned values using assignment operators
inside arithmetic expressions (see Arithmetic Expressions below) or
using the \fB${\fP\fIname\fP[\fB:\fP]\fB=\fP\fIvalue\fP\fB}\fP form
of parameter substitution (see below).
.\"}}}
.PP
.\"{{{ environment
Parameters with the export attribute (set using the \fBexport\fP or
\fBtypeset \-x\fP commands, or by parameter assignments followed by simple
commands) are put in the environment (see \fIenviron\fP(5)) of commands
run by the shell as \fIname\fP\fB=\fP\fIvalue\fP pairs.
The order in which parameters appear in the environment of a command
is unspecified.
When the shell starts up, it extracts parameters and their values from its
environment and automatically sets the export attribute for those parameters.
.\"}}}
.\"{{{ ${name[:][-+=?]word}
.PP
Modifiers can be applied to the \fB${\fP\fIname\fP\fB}\fP form of parameter
substitution:
.IP \fB${\fP\fIname\fP\fB:-\fP\fIword\fP\fB}\fP
if \fIname\fP is set and not null, it is substituted, otherwise \fIword\fP is
substituted.
.IP \fB${\fP\fIname\fP\fB:+\fP\fIword\fP\fB}\fP
if \fIname\fP is set and not null, \fIword\fP is substituted, otherwise nothing is substituted.
.IP \fB${\fP\fIname\fP\fB:=\fP\fIword\fP\fB}\fP
if \fIname\fP is set and not null, it is substituted, otherwise it is
assigned \fIword\fP and the resulting value of \fIname\fP is substituted.
.IP \fB${\fP\fIname\fP\fB:?\fP\fIword\fP\fB}\fP
if \fIname\fP is set and not null, it is substituted, otherwise \fIword\fP
is printed on standard error (preceded by \fIname\fP:) and an error occurs
(normally causing termination of a shell script, function or \&.-script).
If word is omitted the string `parameter null or not set' is used instead.
.PP
In the above modifiers, the \fB:\fP can be omitted, in which case the
conditions only depend on \fIname\fP being set (as opposed to set and
not null).
In addition to brace and file name expansion, tilde expansion is also
performed on \fIword\fP.
Also, \fIword\fP is evaluated only if the condition is true (e.g.,
\fBecho ${v-$(sleep 10; echo hi)}\fP will immediately print the value of
\fBv\fP if it is set, otherwise, it will sleep for 10 seconds and print
hi).
.\"}}}
.PP
The following forms of parameter substitution can also be used:
.\"{{{ ${#name}
.IP \fB${#\fP\fIname\fP\fB}\fP
The number of positional parameters if \fIname\fP is \fB*\fP or \fB@\fP,
the number of elements in the named array if \fIname\fP is an array,
or finally, the length of the string value of parameter \fIname\fP.
.\"}}}
.\"{{{ ${name#pattern}, ${name##pattern}
.IP "\fB${\fP\fIname\fP\fB#\fP\fIpattern\fP\fB}\fP, \fB${\fP\fIname\fP\fB##\fP\fIpattern\fP\fB}\fP"
If \fIpattern\fP matches the beginning of the value of parameter \fIname\fP,
the matched text is deleted from the result of substitution. A single
\fB#\fP results in the shortest match, two \fB#\fP's results in the
longest match.
.\"}}}
.\"{{{ ${name%pattern}, ${name%%pattern}
.IP "\fB${\fP\fIname\fP\fB%\fP\fIpattern\fP\fB}\fP, \fB${\fP\fIname\fP\fB%%\fP\fIpattern\fP\fB}\fP"
Like \fB${\fP..\fB#\fP..\fB}\fP substitution, but it deletes from the end of the
value.
.\"}}}
.\"{{{ special shell parameters
.PP
The following special parameters are implicitly set by the shell and cannot be
set directly using assignments:
.\"{{{ !
.IP \fB!\fP
Process id of the last background process started. If no background
processes have been started, the parameter is not set.
.\"}}}
.\"{{{ #
.IP \fB#\fP
The number of positional parameters (i.e., \fB$1\fP, \fB$2\fP, etc.).
.\"}}}
.\"{{{ $
.IP \fB$\fP
The process ID of the shell, or the PID of the original shell if
it is a subshell.
.\"}}}
.\"{{{ -
.IP \fB\-\fP
The concatenation of the current single letter options
(see \fBset\fP command below for list of options).
.\"}}}
.\"{{{ ?
.IP \fB?\fP
The exit status of the last non-asynchronous command executed.
If the last command was killed by a signal, \fB$?\fP is set to 128 plus
the signal number.
.\"}}}
.\"{{{ 0
.IP "\fB0\fP"
The name the shell was invoked with (i.e., \fBargv[0]\fP), or the
\fBcommand-name\fP if it was invoked with the \fB\-c\fP option and the
\fBcommand-name\fP was supplied, or the \fIfile\fP argument, if it was
supplied.
In functions and \fB.\fP-scripts, \fB$0\fP is the name of the function or
script, respectively.
.\"}}}
.\"{{{ 1-9
.IP "\fB1\fP ... \fB9\fP"
The first nine positional parameters that were supplied to the shell,
function or \fB.\fP-script.
Further positional parameters may be accessed using
\fB${\fP\fInumber\fP\fB}\fP.
.\"}}}
.\"{{{ *
.IP \fB*\fP
All positional parameters (except parameter 0) concatenated to one string.
.\"}}}
.\"{{{ @
.IP \fB@\fP
Same as \fB$*\fP, unless it is used inside double quotes, in which case
a separate word is generated for each positional parameter \- if there
are no positional parameters, no word is generated ("$@" can be used
to access arguments, verbatim, without losing null arguments or
splitting arguments with spaces).
.\"}}}
.\"}}}
.\"{{{ general shell parameters
.PP
The following parameters are set and/or used by the shell:
.\"{{{ _
.IP "\fB_\fP \fI(underscore)\fP"
In interactive use, this parameter is set to the last word of the
previous command. When a command is executed, this parameter is set to
the full path of the command and is placed in the command's environment.
When \fBMAILPATH\fP messages are evaluated, this parameter contains
the name of the file that changed (see \fBMAILPATH\fP parameter below).
.\"}}}
.\"{{{ CDPATH
.IP \fBCDPATH\fP
Search path for the \fBcd\fP built-in command. Works the same way as
\fBPATH\fP for those directories not beginning with \fB/\fP in \fBcd\fP
commands.
.\"}}}
.\"{{{ COLUMNS
.IP \fBCOLUMNS\fP
Set to the number of columns on the terminal or window.
Currently set to the \fBcols\fP value as reported by \fIstty\fP(1) if that
value is non-zero.
This parameter is used by the interactive line editing modes, and by
\fBselect\fP, \fBset \-o\fP and \fBkill \-l\fP commands
to format information in columns.
.\"}}}
.\"{{{ EDITOR
.IP \fBEDITOR\fP
If the \fBVISUAL\fP parameter is not set, this parameter controls the
command line editing mode for interactive shells.
See \fBVISUAL\fP parameter below for how this works.
.\"}}}
.\"{{{ ENV
.IP \fBENV\fP
If this parameter is found to be set after any profile files are
executed, the expanded value is used as a shell start-up file. It
typically contains function and alias definitions.
.\"}}}
.\"{{{ ERRNO
.IP \fBERRNO\fP
Integer value of the shell's errno variable \- indicates the reason
the last system call failed.
.\" todo: ERRNO variable
.sp
Not implemented yet.
.\"}}}
.\"{{{ EXECSHELL
.IP \fBEXECSHELL\fP
If set, this parameter is assumed to contain the shell that is to be
used to execute commands that \fIexecve\fP(2) fails to execute and
which do not start with a `\fB#!\fP \fIshell\fP' sequence.
.\"}}}
.\"{{{ FCEDIT
.IP \fBFCEDIT\fP
The editor used by the \fBfc\fP command.
.\"}}}
.\"{{{ FPATH
.IP \fBFPATH\fP
Like \fBPATH\fP, but used when an undefined function is executed to locate
the file defining the function.
See Functions below for more information.
.\"}}}
.\"{{{ HISTFILE
.IP \fBHISTFILE\fP
The name of the file used to store history.
When assigned to, history is loaded from the specified file.
Also, several invocations of the
shell running on the same machine will share history if their
\fBHISTFILE\fP parameters all point at the same file.
.\"}}}
.\"{{{ HISTSIZE
.IP \fBHISTSIZE\fP
The number of commands normally stored for history, default 128.
.\"}}}
.\"{{{ HOME
.IP \fBHOME\fP
The default directory for the \fBcd\fP command and the value
substituted for an unqualified \fB~\fP (see Tilde Expansion below).
.\"}}}
.\"{{{ IFS
.IP \fBIFS\fP
Internal field separator, used during substitution and by the \fBread\fP
command, to split values into distinct arguments; normally set to
space, tab and newline. See Substitution above for details.
.br
\fBNote:\fP this parameter is not imported from the environment
when the shell is started.
.\"}}}
.\"{{{ KSH_VERSION
.IP \fBKSH_VERSION\fP
The version of shell and the date the version was created (readonly).
.\"}}}
.\"{{{ LINENO
.IP \fBLINENO\fP
The line number of the function or shell script that is currently being
executed.
.\" todo: LINENO variable
.sp
Not implemented yet.
.\"}}}
.\"{{{ LINES
.IP \fBLINES\fP
Set to the number of lines on the terminal or window.
.\"Currently set to the \fBrows\fP value as reported by \fIstty\fP(1) if that
.\"value is non-zero.
.\" todo: LINES variable
.sp
Not implemented yet.
.\"}}}
.\"{{{ MAIL
.IP \fBMAIL\fP
If set, the user will be informed of the arrival of mail in the named
file. This parameter is ignored if the \fBMAILPATH\fP parameter is set.
.\"}}}
.\"{{{ MAILCHECK
.IP \fBMAILCHECK\fP
How often, in seconds, the shell will check for mail in the file(s)
specified by \fBMAIL\fP or \fBMAILPATH\fP. If 0, the shell checks
before each prompt. The default is 600 (5 minutes).
.\"}}}
.\"{{{ MAILPATH
.IP \fBMAILPATH\fP
A list of files to be checked for mail. The list is colon separated,
and each file may be followed by a \fB?\fP and a message to be printed
if new mail has arrived. Command and parameter substitution is
performed on the message, and, during substitution, the parameter \fB$_\fP
contains the name of the file.
The default message is \fByou have mail in $_\fP.
.\"}}}
.\"{{{ OLDPWD
.IP \fBOLDPWD\fP
The previous working directory.
Unset if \fBcd\fP has not successfully changed directories since the
shell started (readonly).
.\"}}}
.\"{{{ OPTARG
.IP \fBOPTARG\fP
When using \fBgetopts\fP, it contains the argument for a parsed option,
if it requires one.
.\"}}}
.\"{{{ OPTIND
.IP \fBOPTIND\fP
The index of the last argument processed when using \fBgetopts\fP.
Assigning 1 to this parameter causes \fBgetopts\fP to
process arguments from the beginning the next time it is invoked.
.\"}}}
.\"{{{ PATH
.IP \fBPATH\fP
A colon separated list of directories that are searched when looking
for commands and \fB.\fP'd files.
An empty string resulting from a leading or trailing colon, or two adjacent
colons is treated as a `.', the current directory.
.\"}}}
.\"{{{ POSIXLY_CORRECT
.IP \fBPOSIXLY_CORRECT\fP
If set, this parameter causes the \fBposix\fP option to be enabled.
See POSIX Mode below.
.\"}}}
.\"{{{ PPID
.IP \fBPPID\fP
The process ID of the shell's parent (readonly).
.\"}}}
.\"{{{ PS1
.IP \fBPS1\fP
\fBPS1\fP is the primary prompt for interactive shells.
Parameter, command and arithmetic substitutions are performed, and
\fB!\fP is replaced with the current command number (see \fBfc\fP
command below).
Default is `\fB$\ \fP'.
.\"}}}
.\"{{{ PS2
.IP \fBPS2\fP
Secondary prompt string, by default `\fB>\fP ', used when more input is
needed to complete a command.
.\"}}}
.\"{{{ PS3
.if "\n(sh"0" \{.IP \fBPS3\fP
Prompt used by \fBselect\fP statement when reading a menu selection.
Default is `\fB#?\ \fP'.
\}
.\"}}}
.\"{{{ PS4
.if "\n(sh"0" \{.IP \fBPS4\fP
Used to prefix commands that are printed during execution tracing
(see \fBset \-x\fP command below).
Parameter, command and arithmetic substitutions are performed
before it is printed.
\}
.\"}}}
.\"{{{ PWD
.IP \fBPWD\fP
The current working directory (readonly).
.\"}}}
.\"{{{ RANDOM
.if "\n(sh"0" \{.IP \fBRANDOM\fP
A simple random number generator. Every time \fBRANDOM\fP is
referenced, it is assigned the next number in a random number series.
The point in the series can be set by assigning a number to
\fBRANDOM\fP (see \fIrand\fP(3)).
\}
.\"}}}
.\"{{{ REPLY
.IP \fBREPLY\fP
Default parameter for the \fBread\fP command if no names are given.
Also used in \fBselect\fP loops to store the value that is read from
standard input.
.\"}}}
.\"{{{ SECONDS
.if "\n(sh"0" \{.IP \fBSECONDS\fP
The number of seconds since the shell started or, if the parameter has been
assigned an integer value, the number of seconds since the assignment plus
the value that was assigned.
\}
.\"}}}
.\"{{{ TMOUT
.if "\n(sh"0" \{.IP \fBTMOUT\fP
If set to a positive integer in an interactive shell, it specifies
the maximum number of seconds the shell will wait for input after
printing the primary prompt (\fBPS1\fP). If the time is exceeded, the
shell exits.
\}
.\"}}}
.\"{{{ TMPDIR
.IP \fBTMPDIR\fP
The directory shell temporary files are created in. If this parameter
is not set, or does not contain the absolute path of a writable
directory, temporary files are created in \fB/tmp\fP.
.\"}}}
.\"{{{ VISUAL
.IP \fBVISUAL\fP
If set, this parameter controls the command line editing mode for
interactive shells. If the last component of the path specified in this
parameter contains the string \fBvi\fP, \fBemacs\fP or \fBgmacs\fP, the
vi, emacs or gmacs (Gosling emacs) editing mode is enabled, respectively.
.\"}}}
.\"}}}
.\"}}}
.\"{{{ Tilde Expansion
.SS "Tilde Expansion"
Tilde expansion, which is done in parallel with parameter substitution,
is done on words starting with an unquoted \fB~\fP. The characters
following the tilde, up to the first \fB/\fP, if any, are assumed to be
a login name. If the login name is empty, \fB+\fP or \fB\-\fP, the
value of the \fBHOME\fP, \fBPWD\fP, or \fBOLDPWD\fP parameter is
substituted, respectively. Otherwise, the password file is searched for
the login name, and the tilde expression is substituted with the
user's home directory. If the login name is not found in the password
file or if any quoting or parameter substitution occurs in the login name,
no substitution is performed.
.PP
In parameter assignments (those preceding a simple-command or those
occurring in the arguments of \fBalias\fP, \fBexport\fP \fBreadonly\fP,
and \fBtypeset\fP), tilde expansion is done after any unquoted colon
(\fB:\fP), and login names are also delimited by colons.
.PP
The home directory of previously expanded login names are cached and
re-used. The \fBalias \-d\fP command may be used to list, change and
add to this cache (e.g., `alias \-d fac=/usr/local/facilities; cd
~fac/bin').
.\"}}}
.\"{{{ Brace Expansion
.SS "Brace Expansion (alternation)"
Brace expressions, which take the form
.ce
\fIprefix\fP \fB{\fP\fIstr\fP1\fB,\fP...\fB,\fP\fIstr\fPN\fB}\fP\fIsuffix\fP
are expanded to N words, each of which is the concatenation of
\fIprefix\fP, \fIstr\fPi and \fIsuffix\fP
(e.g., `a{c,b{X,Y},d}e' expands to four word: ace, abXe, abYe, and ade).
As noted in the example, brace expressions can be nested and the resulting
words are not sorted.
Brace expressions must contain an unquoted comma (\fB,\fP) for expansion
to occur (i.e., \fB{}\fP and \fB{foo}\fP are not expanded).
Brace expansion is carried out after parameter substitution and before
file name generation.
.\"}}}
.\"{{{ File Name Patterns
.SS "File Name Patterns"
.PP
A file name pattern is a word containing one or more unquoted \fB?\fP or
\fB*\fP characters or \fB[\fP..\fB]\fP sequences. Once brace expansion has
been performed, the shell replaces file name patterns with the sorted names
of all the files that match the pattern (if no files match, the word is
left unchanged). The pattern elements have the following meaning:
.IP \fB?\fP
matches any single character.
.IP \fB*\fP
matches any sequence of characters.
.IP \fB[\fP..\fB]\fP
matches any of the characters inside the brackets. Ranges of characters
can be specified by separating two characters by a \fB\-\fP, e.g., \fB[a0\-9]\fP
matches the letter \fBa\fP or any digit. In order to represent itself, a
\fB\-\fP must either be quoted or the first or last character in the character
list. Similarly, a \fB]\fP must be quoted or the first character in the list
if it is represent itself instead of the end of the list. Also, a \fB!\fP
appearing at the start of the list has special meaning (see below), so to
represent itself it must be quoted or appear later in the list.
.IP \fB[!\fP..\fB]\fP
like \fB[\fP..\fB]\fP, except it matches any character not inside the brackets.
.PP
Note that pdksh currently never matches \fB.\fP and \fB..\fP, but the original
ksh, Bourne sh and bash do, so this may have to change (too bad).
.PP
Note that none of the above pattern elements match either a period (\fB.\fP)
at the start of a file name or a slash (\fB/\fP), even if they are explicitly
used in a \fB[\fP..\fB]\fP sequence; also, the names \fB.\fP and \fB..\fP
are never matched, even by the pattern \fB.*\fP.
.PP
If the \fBmarkdirs\fP option is set, any directories that result from
file name generation are marked with a trailing \fB/\fP.
.PP
.\" todo: implement this (?(pattern-list), [[:alpha:]], etc.)
The \fB?(\fP\fIpattern-list\fP\fB)\fP, \fB*(\fP\fIp-l\fP\fB)\fP,
\fB+(\fP\fIp-l\fP\fB)\fP, \fB@(\fP\fIp-l\fP\fB)\fP and \fB!(\fP\fIp-l\fP\fB)\fP
elements found in the original Korn shell are not yet implemented; the same
is true for the POSIX character classes (i.e.,
\fB[:\fP\fIclass-name\fP\fB:]\fP inside a \fB[\fP..\fB]\fP expression).
.\"}}}
.\"{{{ Input/Output Redirection
.SS "Input/Output Redirection"
When a command is executed, its standard input, standard output and
standard error (file descriptors 0, 1 and 2, respectively) are normally
inherited from the shell.
Three exceptions to this are commands in pipelines, for which standard input
and/or standard output are those set up by the pipeline, asynchronous commands
created when job control is disabled, for which standard input is initially
set to be from \fB/dev/null\fP, and commands for which any of the following
redirections have been specified:
.IP "\fB>\fP \fIfile\fP"
standard output is redirected to \fIfile\fP. If \fIfile\fP does not exist,
it is created; if it does exist and the \fBnoclobber\fP option is set, an
error occurs, otherwise the file is truncated.
Note that this means the command \fIcmd < foo > foo\fP will open
\fIfoo\fP for reading and then truncate it when it opens it for writing,
before \fIcmd\fP gets a chance to actually read \fIfoo\fP.
.IP "\fB>|\fP \fIfile\fP"
same as \fB>\fP, except the file is truncated, even if the \fBnoclobber\fP
option is set.
.IP "\fB>>\fP \fIfile\fP"
same as \fB>\fP, except the file an existing file is appended to instead
of being truncated. Also, the file is opened in append mode, so writes
always go to the end of the file (see \fIopen\fP(2)).
.IP "\fB<\fP \fIfile\fP"
standard input is redirected from \fIfile\fP, which is opened for reading.
.IP "\fB<>\fP \fIfile\fP"
same as \fB<\fP, except the file is opened for reading and writing.
.IP "\fB<<\fP \fImarker\fP"
after reading the command line containing this kind of redirection (called a
here document), the shell copies lines from the command source into a temporary
file until a line matching \fImarker\fP is read.
When the command is executed, standard input is redirected from the temporary
file.
If \fImarker\fP contains no quoted characters, the contents of the
temporary file are processed as if enclosed in double quotes each time
the command is executed, so parameter, command and arithmetic substitutions
are performed, along with backslash (\fB\e\fP) escapes for
\fB$\fP, \fB`\fP, \fB\e\fP and \fB\enewline\fP.
If multiple here documents are used on the same command line, they are
saved in order.
.IP "\fB<<-\fP \fImarker\fP"
same as \fB<<\fP, except leading tabs are stripped from lines in the
here document.
.IP "\fB<&\fP \fIfd\fP"
standard input is duplicated from file descriptor \fIfd\fP.
\fIfd\fP can be a single digit, indicating the number of an existing
file descriptor, the letter \fBp\fP, indicating the file descriptor
associated with the output of the current co-process, or
the character \fB\-\fP, indicating standard input is to be closed.
.IP "\fB>&\fP \fIfd\fP"
same as \fB<&\fP, except the operation is done on standard output.
.PP
In any of the above redirections, the file descriptor that is redirected
(i.e., standard input or standard output) can be explicitly given by preceding
the redirection with a single digit.
Parameter, command and arithmetic substitutions, tilde substitutions and
file name generation are all performed on the \fIfile\fP, \fImarker\fP and
\fIfd\fP arguments of redirections.
Note however, that the results of any file name generation are only used
if a single file is matched; if multiple files match, the word with the
unexpanded file name generation characters is used.
Note that in restricted shells, redirections which can create files cannot
be used.
.PP
For simple-commands, redirections may appear anywhere in the command, for
compound-commands (\fBif\fP statements, etc.), redirections must appear at
the end.
Redirections are processed after pipelines are created and in the order they
are given, so
.ce
\fBcat /foo/bar 2>&1 > /dev/null | cat -n\fP
will print an error with a line number prepended to it.
.\"}}}
.\"{{{ Arithmetic Expressions
.SS "Arithmetic Expressions"
Integer arithmetic expressions can be used with the \fBlet\fP command,
inside \fB$(\fP..\fB)\fP expressions,
inside array references (e.g., \fIname\fP\fB[\fP\fIexpr\fP\fB]\fP),
as numeric arguments to the \fBtest\fP command,
and as the value of an assignment to an integer parameter.
.PP
Expression may contain alpha-numeric parameter identifiers, array
references, and integer constants and may be combined with the
following operators (listed and grouped in increasing order of precedence).
.TP
Unary operators:
\fB+ \- ! ~\fP
.TP
Binary operators:
\fB= *= /= %= += \-= <<= >>= &= ^= |=\fP,
\fB||\fP,
\fB&&\fP,
\fB|\fP,
\fB^\fP,
\fB&\fP,
\fB== !=\fP,
\fB< <= >= >\fP,
\fB<< >>\fP,
\fB+ \-\fP,
\fB* / %\fP
.TP
Ternary operators:
\fB? :\fP (precedence is between that of binary assignment and \fB||\fP
operators)
.TP
Grouping operators:
\fB( )\fP
.PP
Integer constants may be specified with arbitrary bases using the notation
\fIbase\fP\fB#\fP\fInumber\fP, where \fIbase\fP is a decimal integer specifying
the base, and \fInumber\fP is a number in the specified base.
.\"}}}
.\"{{{ Co-Processes
.SS "Co-Processes"
A Co-process, which is a pipeline created with the \fB|&\fP operator,
is an asynchronous process that the shell can both write to
(using \fBprint \-p\fP) and read from (using \fBread \-p\fP).
Only one co-process may be active at any given time, unless the input
of an existing co-process has been duplicated using a \fB>&p\fP redirection.
In this case, the shell closes its copy of the co-process input (note
that a \fB<&p\fP redirection does not cause the shell to close its copy
of the co-process output).
Once \fBread \-p\fP has read the end-of-file, the co-process input is
closed, and further attempts to read will produce diagnostics.
Similarly, once \fBprint \-p\fP fails due to an EPIPE, the co-process
output is closed.
Note also, that \fBprint \-p\fP will ignore SIGPIPE signals during writes
if the signal is not being trapped or ignored; the same is not true if
the co-process input has been duplicated to another file descriptor and
\fBprint \-u\fP\fIn\fP is used.
.PP
If another co-process is started before doing a read or \fB<&p\fP redirection
from existing co-processes, the output of the co-processes will be shared
(e.g., \fBecho hi |& echo there|& cat <&p\fP will print both "hi" and "there").
This is slightly different from the original Korn shell in which output is
shared as long as the existing co-process job has not exited.
As a side affect, end-of-file can not be read reliably in the original Korn
shell (e.g., if the co-process closes its output but does not exit, the
end of file will not be read).
.\"}}}
.\"{{{ Functions
.SS "Functions"
Functions are defined using either Korn shell \fBfunction\fP \fIname\fP
syntax or the Bourne/POSIX shell \fIname\fP\fB()\fP syntax.
At the moment, there is no difference between the two, but see below.
Functions are like \fB.\fP-scripts in that they are executed in
the current environment, however, unlike \fB.\fP-scripts, shell arguments
(i.e., positional parameters, \fB$1\fP, etc.) are never visible inside them.
When the shell is determining the location of as command, functions are
searched after special built-in commands, and before regular and non-regular
built-ins, and before the \fBPATH\fP is searched.
.PP
An existing function may be deleted using \fBunset \-f\fP \fIfunction-name\fP.
A list of functions can be obtained using \fBtypeset +f\fP and the
function definitions can be listed using \fBtypeset -f\fP.
\fBautoload\fP (which is an alias for \fBtypeset \-fu\fP) may be used to
create undefined functions; when an undefined function is executed, the
shell searches the path specified in the \fBFPATH\fP parameter for a file
with the same name as the function, which, if found is read and executed.
If after executing the file, the named function is found to be defined, the
function is executed, otherwise, the normal command search is continued
(i.e., the shell searches the builtin-command table and \fBPATH\fP).
.PP
Functions can have two attributes, trace and export, which can be set
with \fBtypeset \-ft\fP and \fBtypeset \-fx\fP, respectively.
When a traced function is executed, the shell's \fBxtrace\fP option is turned
on for the functions duration, otherwise the \fBxtrace\fP option is turned off.
The export attribute of functions is currently not used. In the original
Korn shell, exported functions are visible to shell scripts that are executed.
.PP
Since functions are executed in the current shell environment, parameter
assignments made inside functions are visible after the function completes.
If this is not the desired effect, the \fBtypeset\fP command can be used
inside a function to create a local parameter.
Note that special parameters (e.g., \fB$$\fP, \fB$!\fP) can't be scoped in
this way.
.PP
The exit status of a function is that of the last command executed in
the function.
A function can be made to finish immediately using the \fBreturn\fP command;
this may also be used to explicitly specify the exit status.
.PP
In future, functions defined with the \fBfunction\fP keyword will be treated
differently than functions defined with the \fB()\fP notation:
.nr P2 \n(PD
.nr PD 0
.IP \ \ \(bu
A separate trap/signal environment is used during the execution of functions.
This means traps set inside a function do not affect the shell's traps and
signals that are not ignored in the shell (but may be trapped) have their
default effect in a function.
.IP \ \ \(bu
The EXIT trap, if set in a function, is executed after the function returns.
.IP \ \ \(bu
the \fB$0\fP parameter will be set to the name of the function (this is
currently the case for both styles of function).
.nr PD \n(P2
.\"}}}
.\"{{{ POSIX mode
.SS "POSIX Mode"
.\" XXX Shouldn't the POSIX effects be documented along with the commands?
.\" XXX That way when looking up a commands, you can not miss a side effect
.\" XXX of POSIX.
.\" YYY Yes - this is done in some instances, not in others. But the info
.\" YYY needs to be centralized as well so people can decide if it is a
.\" YYY good thing to do.
pdksh is intended to be POSIX compliant, however, in some cases, POSIX
behaviour is contrary either to the original Korn shell behaviour or to
user convenience.
How the shell behaves in these cases is determined by the state of
the posix option (\fBset \-o posix\fP) \(em if it is on, the POSIX behaviour
is followed, otherwise it isn't.
The \fBposix\fP option is set automatically when the shell starts up
if the environment contains the \fBPOSIXLY_CORRECT\fP parameter.
(pdksh can also be compiled so that it is in POSIX mode by default,
however this is usually not desirable).
.PP
The following is a list of things that are affected by the state of
the \fBposix\fP option:
.nr P2 \n(PD
.nr PD 0
.IP \ \ \(bu
\fB\e"\fP inside double quoted \fB`\fP..\fB`\fP command substitutions:
in posix mode, the \fB\e"\fP is interpreted when the command is interpreted;
in non-posix mode, the backslash is stripped before the command substitution
is interpreted. For example, \fBecho "`echo \e"hi\e"`"\fP produces `"hi"' in
posix mode, `hi' in non-posix mode. To avoid problems, use the \fB$(...\fP)
form of command substitution.
.IP \ \ \(bu
\fBkill -l\fP output: in posix mode, signal names are listed one a single line;
in non-posix mode, signal numbers, names and descriptions are printed in
columns.
In future, a new option (\fB\-v\fP perhaps) will be added to distinguish
the two behaviours.
.IP \ \ \(bu
\fBfg\fP exit status: in posix mode, the exit status is 0 if no errors occur;
in non-posix mode, the exit status is that of the last foregrounded job.
.IP \ \ \(bu
\fBgetopts\fP: in posix mode, options must start with a \fB\-\fP; in non-posix
mode, options can start with either \fB\-\fP or \fB+\fP.
.IP \ \ \(bu
brace expansion (also known as alternation): in posix mode, brace expansion
is disabled; in non-posix mode, brace expansion enabled.
Note that \fBset -o posix\fP (or setting the \fBPOSIXLY_CORRECT\fP parameter)
automatically turns the \fBbraceexpand\fP option off, however it can be
explicitly turned on later.
.IP \ \ \(bu
\fBset \-\fP: in posix mode, this does not clear the \fBverbose\fP or
\fBxtrace\fP options; in non-posix mode, it does.
.IP \ \ \(bu
\fBset\fP exit status: in posix mode, the exit status of set is 0
if there are no errors; in non-posix mode, the exit status is that of
any command substitutions performed in generating the set command.
For example, `\fBset \-\- `false`; echo $?\fP' prints 0 in posix mode,
1 in non-posix mode. This construct is used in most shell scripts that
use the old \fIgetopt\fP(1) command.
.IP \ \ \(bu
argument expansion of \fBalias\fP, \fBexport\fP, \fBreadonly\fP, and
\fBtypeset\fP commands: in posix mode, normal argument expansion done;
in non-posix mode, field splitting, file globing, (normal) tilde expansion
are turned off, and assignment tilde expansion is turned on.
.IP \ \ \(bu
signal specification: in posix mode, signals can be specified as digits only
if signal numbers match POSIX values (i.e., HUP=1, INT=2, QUIT=3, ABRT=6,
KILL=9, ALRM=14, and TERM=15); in non-posix mode, signals can be always digits.
.IP \ \ \(bu
alias expansion: in posix mode, alias expansion is only carried out when
reading command words; in non-posix mode, alias expansion is carried out
on any word following an alias that ended in a space.
For example, the following for loop
.RS
.ft B
.nf
alias a='for ' i='j'
a i in 1 2; do echo i=$i j=$j; done
.fi
.ft P
.RE
uses parameter \fBi\fP in posix mode, \fBj\fP in non-posix mode.
.nr PD \n(P2
.\"}}}
.\"{{{ Command Execution (builtin commands)
.SS "Command Execution"
After evaluation of keyword assignments and arguments, the type of
command is determined.
A command may execute a special shell built-in,
a shell function, a regular shell built-in, or an executable file.
.PP
Any keyword assignments are then performed (and exported) for the duration
of the command.
.PP
Even on systems where the \fIexec\fP(2) family does not support \fB#!\fP
notation for scripts, ksh can be configured to fake it.
.PP
There are several built-in commands.
.\"{{{ : [ ... ]
.IP "\fB:\fP [ ... ]"
The null command.
Exit status is set to zero.
.\"}}}
.\"{{{ . file [ arg1 ... ]
.IP "\fB\&.\fP \fIfile\fP [\fIarg1\fP ...]"
Execute the commands in \fIfile\fP in the current environment.
The file is searched for in the directories of \fBPATH\fP.
If arguments are given, the positional parameters may be used to
access them while \fIfile\fP is being executed.
If no arguments are given, the positional parameters are those of the
environment the command is used in.
.\"}}}
.\"{{{ alias [ -d | -t [ -r ] ] [-x] [name1[=value1] ...]
.IP "\fBalias\fP [ \fB\-d\fP | \fB\-t\fP [\fB\-r\fP] ] [\fB\-x\fP] [\fIname1\fP[\fB=\fP\fIvalue1\fP] ...]"
Without arguments, \fBalias\fP lists all aliases and their values. For
any name without a value, its value is listed. Any name with a value
defines an alias (see Aliases above).
.sp
The \fB\-x\fP option sets the export attribute of an alias, or, if no
names are given, lists the aliases with the export attribute
(exporting an alias currently has no affect).
.sp
The \fB\-t\fP option indicates that tracked aliases are to be listed/set
(values specified on the command line are ignored for tracked aliases).
The \fB\-r\fP option indicates that all tracked aliases are to be reset.
.sp
The \fB\-d\fP causes directory aliases, which are used in tilde expansion,
to be listed or set (see Tilde Expansion above).
.\"}}}
.\"{{{ bg [job ...]
.IP "\fBbg\fP [\fIjob\fP ...]"
Resume the specified stopped job(s) in the background.
If no jobs are specified, \fB%+\fP is assumed.
This command is only available on systems which support job control.
See Job Control below for more information.
.\"}}}
.\"{{{ bind [-m] [key[=editing-command] ...]
.IP "\fBbind\fP [\fB\-m\fP] [\fIkey\fP[\fB=\fP\fIediting-command\fP] ...]"
Set or view the current emacs command editing key bindings/macros.
See Emacs Interactive Input Line Editing below for a complete description.
.\"}}}
.\"{{{ break [level]
.IP "\fBbreak\fP [\fIlevel\fP]"
\fBbreak\fP exits the \fIlevel\fPth inner most for, select, until, or while
loop.
\fIlevel\fP defaults to 1.
.\"}}}
.\"{{{ builtin command [arg1 ...]
.IP "\fBbuiltin\fP \fIcommand\fP [\fIarg1\fP ...]"
Execute the built-in command \fIcommand\fP.
.\"}}}
.\"{{{ cd [dir]
.IP "\fBcd\fP [\fIdir\fP]"
Set the working directory to \fIdir\fP. If the parameter \fBCDPATH\fP
is set, it lists the search path for the directory containing
\fIdir\fP. A null path mans the current directory. If \fIdir\fP is
missing, the home directory \fB$HOME\fP is used. If \fIdir\fP is
\fB\-\fP, the previous working directory is used. If \fIdir\fP is
\fB..\fP, the shell changes directory to the parent directory, as
determined from the value of \fBPWD\fP.
The \fBPWD\fP and \fBOLDPWD\fP parameters are updated to reflect the
current and old wording directory, respectively.
.\" todo: cd -L/-P
.sp
Note: the \fB\-L\fP and \fB\-P\fP options available in some Korn shells
are not implemented yet; the same goes for the \fBpwd\fP command.
.\"}}}
.\"{{{ cd old new
.IP "\fBcd\fP \fIold new\fP"
The string \fInew\fP is substituted for \fIold\fP in the current
directory, and the shell attempts to change to the new directory.
.\"}}}
.\"{{{ command [ -pvV ] cmd [arg1 ...]
.IP "\fBcommand\fP [\fB\-pvV\fP] \fIcmd\fP [\fIarg1\fP ...]"
If neither the \fB\-v\fP nor \fB\-V\fP options are given, \fIcmd\fP
is executed exactly as if the \fBcommand\fP had not been specified,
with two exceptions: first, \fIcmd\fP cannot be a shell function, and
second, special built-in commands lose their specialness (i.e., redirection
and utility errors do not cause the shell to exit, and command assignments
are not permanent).
If the \fB\-p\fP option is given, a default search path is used instead of
the current value of \fBPATH\fP (the actual value of the default path is
system dependent: on POSIXish systems, it is the value returned by
\fBgetconf CS_PATH\fP).
.sp
If the \fB\-v\fP option is given, instead of executing \fIcmd\fP, information
about what would be executed is given (and the same is done for \fIarg1\fP
...):
for keywords, special and regular built-in commands and functions, their names
are simply printed,
for aliases, a command that defines them is printed,
and for commands found by searching the \fBPATH\fP parameter,
the full path of the command is printed.
If no command is be found, (i.e., the path search fails), nothing is printed
and \fBcommand\fP exits with a non-zero status.
The \fB\-V\fP option is like the \fB\-v\fP option, except it is more verbose.
.\"}}}
.\"{{{ continue [levels]
.IP "\fBcontinue\fP [\fIlevels\fP]"
\fBcontinue\fP jumps to the beginning of the \fIlevel\fPth inner most for,
select, until, or while loop.
\fIlevel\fP defaults to 1.
.\"}}}
.\"{{{ echo [-neE] [arg ...]
.IP "\fBecho\fP [\fB\-neE\fP] [\fIarg\fP ...]"
Prints its arguments (separated by spaces) followed by a newline, to
standard out.
The newline is suppressed if any of the arguments contain the backslash
sequence \fB\ec\fP.
See \fBprint\fP command below for a list of other backslash sequences
that are recognized.
.sp
The options are provided for compatibility with BSD shell scripts:
\fB\-n\fP suppresses the trailing newline, \fB\-e\fP enables backslash
interpretation (a no-op, since this is normally done), and \fB\-E\fP which
suppresses backslash interpretation.
.\"}}}
.\"{{{ eval command ...
.IP "\fBeval\fP \fIcommand ...\fP"
The arguments are concatenated (with spaces between them) to form
a single string which the shell then parses and executes
in the current environment.
.\"}}}
.\"{{{ exec [command [arg ...]]
.IP "\fBexec\fP [\fIcommand\fP [\fIarg\fP ...]]"
The command is executed without forking, replacing the shell process.
.sp
If no arguments are given, any IO redirection is permanent and the shell
is not replaced.
Any file descriptors greater than 2 which are opened or \fIdup\fP(2)-ed
in this way are not made available to other executed commands (i.e.,
commands that are not built-in to the shell).
.\"}}}
.\"{{{ exit [status]
.IP "\fBexit\fP [\fIstatus\fP]"
The shell exits with the specified exit status.
If \fIstatus\fP is not specified, the exit status is the current
value of the \fB?\fP parameter.
.\"}}}
.\"{{{ export [-p] [parameter[=value] ...]
.IP "\fBexport\fP [\fB\-p\fP] [\fIparameter\fP[\fB=\fP\fIvalue\fP]] ..."
Sets the export attribute of the named parameters.
Exported parameters are passed in the environment to executed commands.
If values are specified, the named parameters also assigned.
.sp
If no parameters are specified, the names of all parameters with the export
attribute are printed one per line, unless the \fB\-p\fP option is used,
in which case \fBexport\fP commands defining all exported
parameters, including their values, are printed.
.\"}}}
.\"{{{ false
.IP "\fBfalse\fP"
A command that exits with a non-zero status.
.\"}}}
.\"{{{ fc [-e editor | -l [-n]] [-r] [first [ last ]]
.IP "\fBfc\fP [\fB\-e\fP \fIeditor\fP | \fB\-l\fP [\fB\-n\fP]] [\fB\-r\fP] [\fIfirst\fP [\fIlast\fP]]"
\fIfirst\fP and \fIlast\fP select commands from the history.
Commands can be selected by
history number, or a string specifying the most recent command starting
with that string. The \fB\-l\fP option lists the command on stdout,
and \fB\-n\fP inhibits the default command numbers. The \fB\-r\fP
option reverses the order of the list. Without \fB\-l\fP, the selected
commands are edited by the editor specified with the \fB\-e\fP
option, or if no \fB\-e\fP is specified, the editor specified by the
\fBFCEDIT\fP parameter (if this parameter is not set, \fB/bin/ed\fP is used),
and then executed by the shell.
.\"}}}
.\"{{{ fc -e - [-g] [old=new] [prefix]
.IP "\fBfc \-e \-\fP [\fB\-g\fP] [\fIold\fB=\fInew\fR] [\fIprefix\fP]"
Re-execute the selected command (the previous command by default) after
performing the optional substitution of \fIold\fP with \fInew\fP. If
\fB\-g\fP is specified, all occurrences of \fIold\fP are replaced with
\fInew\fP. This command is usually accessed with the predefined alias
\fBr='fc \-e \-'\fP.
.\"}}}
.\"{{{ fg [job ...]
.IP "\fBfg\fP [\fIjob\fP ...]"
Resume the specified job(s) in the foreground.
If no jobs are specified, \fB%+\fP is assumed.
This command is only available on systems which support job control.
See Job Control below for more information.
.\"}}}
.\"{{{ getopts optstring name [arg ...]
.IP "\fBgetopts\fP \fIoptstring\fP \fIname\fP [\fIarg\fP ...]"
\fBgetopts\fP is used by shell procedures to parse the specified arguments
(or positional parameters, if no arguments are given) and to check for legal
options.
\fIoptstring\fP contains the option letters that
\fBgetopts\fP is to recognize. If a letter is followed by a colon, the
option is expected to have an argument.
Arguments containing options must all start with either a \fB\-\fP or
a \fB+\fP, options that do not take arguments may be grouped in a single
argument.
If an option takes an argument and the option character is not the last
character of the argument it is found in, the remainder of the argument
is taken to be the option's argument, otherwise, the next argument is
the option's argument.
.sp
Each time \fBgetopts\fP is invoked, it places the next option in
the shell parameter \fIname\fP and the index of the next argument to be
processed in the shell parameter \fBOPTIND\fP.
If the option was introduced with a \fB+\fP, the option placed in
\fIname\fP is prefixed with a \fB+\fP.
When an option requires an argument, \fBgetopts\fP places it in the
shell parameter \fBOPTARG\fP.
The action taken when an illegal option or a missing option argument is
encountered depends on \fIoptstring\fP: if it does not begin with a
colon, an error message is printed on standard error and a
question mark is placed in \fIname\fP;
if \fBoptstring\fP starts with a colon, no error message is printed,
a question mark or colon is placed in \fIname\fP, indicating an
illegal option or missing argument, respectively, and \fBOPTARG\fP
is set to the option character that caused the problem.
.sp
When the end of the options is encountered, \fBgetopts\fP exits with a
non-zero exit status.
Options end at the first (non-option argument) argument that does not
start with a \-, or when a \fB\-\-\fP argument is encountered.
.sp
Option parsing can be reset by setting \fBOPTIND\fP to 1 (this is done
automatically whenever the shell or a shell procedure is invoked).
.sp
Warning: Changing the value of the shell parameter \fBOPTIND\fP to
a value other than 1, or parsing different sets of arguments without
resetting \fBOPTIND\fP may lead to unexpected results.
.\"}}}
.\"{{{ hash [-r] [name ...]
.IP "\fBhash\fP [\fB\-r\fP] [\fIname ...\fP]"
Without arguments, any hashed executable command pathnames are listed.
The \fB\-r\fP option causes all hashed commands to be removed
from the hash table.
Each \fIname\fP is searched as if it where a command name and added to the
hash table if it is an executable command.
.\"}}}
.\"{{{ jobs [-lpn] [job ...]
.IP "\fBjobs\fP [\fB\-lpn\fP] [\fIjob\fP ...]"
Display information about the specified jobs; if no jobs are specified,
all jobs are displayed.
The \fB\-n\fP option causes information to be displayed only for jobs
that have changed state since the last notification.
If the \fB\-l\fP option is used, the process-id of each process in a job
is also listed.
The \fB\-p\fP option causes only the process group of each job to be printed.
See Job Control below for the format of \fIjob\fP and the displayed job.
.\"}}}
.\"{{{ kill [-s signame | -signum | -signame] { job | pid | -pgrp } ...
.IP "\fBkill\fP [\fB\-s\fP \fIsigname\fP | \fB\-signum\fP | \fB\-signame\fP ] { \fIjob\fP | \fIpid\fP | \fB\-\fP\fIpgrp\fP } ...
Send the specified signal to the specified jobs, process ids, or process groups.
If no signal is specified, the signal TERM is sent.
If a job is specified, the signal is sent to the job's process group.
See Job Control below for the format of \fIjob\fP.
.\"}}}
.\"{{{ kill -l [exit-status ...]
.IP "\fBkill \-l\fP [\fIexit-status\fP ...]
Print the name of the signal that killed a process which exited with
the specified \fIexit-status\fPes.
If no arguments are specified, a list of all the signals, their numbers and
a short description of them are printed.
.\"}}}
.\"{{{ let [expression ...]
.IP "\fBlet\fP [\fIexpression\fP ...]"
Each expression is evaluated, see Arithmetic Expressions above. A zero
status is returned if the last expression evaluates to a non-zero value,
otherwise a non-zero status is returned. Since expressions may need to be
quoted, \fB((\fP \fIexpr\fP \fB))\fP is syntactic sugar for \fBlet
"\fP\fIexpr\fP\fB"\fP.
.\"}}}
.\"{{{ print [-nprsun | -R [-en]] [argument ...]
.IP "\fBprint\fP [\fB\-nprsu\fIn\fP | \fB\-R\fP [\fB\-en\fP]] [\fIargument ...\fP]"
\fBPrint\fP prints its arguments on the standard output, separated by
spaces, and terminated with a newline. The \fB\-n\fP option suppresses
the newline. By default, certain C escapes are translated. These
include \eb, \ef, \en, \er, \et, \ev, and \e### (# is an octal digit).
\ec is equivalent to using the \fB\-n\fP option. \e expansion may be
inhibited with the \fB\-r\fP option.
The \fB\-s\fP option prints to the history file instead of standard output,
the \fB\-u\fP option prints to file descriptor \fIn\fP (\fIn\fP
defaults to 1 if omitted), and the \fB\-p\fP option prints to the co-process
(see Co-Processes above).
.sp
The \fB\-R\fP option is used to emulate, to some degree, the BSD echo
command, which does not process \e sequences unless the \fB\-e\fP option
is given.
As above, the \fB\-n\fP option suppresses the trailing newline.
.\"}}}
.\"{{{ read [-prsun] [parameter ...]
.IP "\fBread\fP [\fB\-prsu\fIn\fR] [\fIparameter ...\fP]"
Reads a line of input from standard input, separate the line into fields using
the \fBIFS\fP parameter (see Substitution above), and assign each field to the
specified parameters.
If there are more parameters than fields, the extra parameters are set to null,
or alternatively, if there are more fields than parameters, the last parameter
is assigned the remaining fields (inclusive of any separating spaces).
If no parameters are specified, the \fBREPLY\fP parameter is used.
If the input line ends in a backslash and the \fB\-r\fP option was not used, the
backslash and newline are stripped and more input is read.
If no input is read, \fBread\fP exits with a non-zero status.
.sp
A prompt, which is printed to standard error before any input is read, may be
specified by appending and question mark and the prompt to the
first parameter (e.g., \fBread nfoo?'number of foos: '\fP).
.sp
The \fB\-u\fP\fIn\fP and \fB\-p\fP options cause input to be read
from file descriptor \fIn\fP or the current co-process, respectively.
If the \fB\-s\fP option is used, input is saved to the history file.
.\"}}}
.\"{{{ readonly [-p] [parameter[=value] ...]
.IP "\fBreadonly\fP [\fB\-p\fP] [\fIparameter\fP[\fB=\fP\fIvalue\fP]] ..."
Sets the readonly attribute of the named parameters. If values are given,
parameters are set to them before setting the attribute.
Once a parameter is made readonly, it cannot be unset and its value cannot
be changed.
.sp
If no parameters are specified, the names of all parameters with the readonly
attribute are printed one per line, unless the \fB\-p\fP option is used,
in which case \fBreadonly\fP commands defining all readonly
parameters, including their values, are printed.
.\"}}}
.\"{{{ return [status]
.IP "\fBreturn\fP [\fIstatus\fP]"
Returns from a function or \fB.\fP script, with exit status \fIstatus\fP.
If no \fIstatus\fP is given, the exit status of the last executed command
is used.
If used outside of a function or \fB.\fP script, it has the same effect
as \fBexit\fP.
Note that pdksh treats both profile and \fB$ENV\fP files as
\fB.\fP scripts, while the original Korn shell only treats profiles as
\fB.\fP scripts.
.\"}}}
.\"{{{ set [+-abCefhkmnpsuvxX] [+-o [option]] [+-A name] [--] [arg ...]
.IP "\fBset\fP [\fB\(+-abCefhkmnpsuvxX\fP] [\fB\(+-o\fP [\fIoption\fP]] [\fB\(+-A\fP \fIname\fP] [\fB\-\-\fP] [\fIarg\fP ...]"
Set (\fB\-\fP) or clear (\fB+\fP) a shell option:
.sp
.TS
expand;
afB lfB lw(3i).
\-A T{
Sets the elements of the array parameter \fIname\fP to \fIarg\fP ...;
If \fB\-A\fP is used, the array is reset (i.e., emptied) first.
T}
\-a allexport T{
all new parameters are created with the export attribute
T}
\-b notify T{
Print job notification messages asynchronously, instead of just before the
prompt.
Only used if job control is enabled (\fB\-m\fP).
T}
\-C noclobber T{
Prevent \fB>\fP redirection from overwriting existing files (\fB>!\fP must
be used to force an overwrite).
T}
\-e errexit T{
Exit as soon as an error occurs or a command fails (i.e., exits with a
non-zero status).
This does not apply to commands whose exit status is explicitly tested by a
shell construct such as \fBif\fP, \fBuntil\fP, \fBwhile\fP, \fB&&\fP or
\fB||\fP statements.
T}
\-f noglob T{
Do not expand file name patterns.
T}
\-h trackall T{
Create tracked aliases for all executed commands (see Aliases above).
On by default for non-interactive shells.
T}
\-i interactive T{
Enable interactive mode \- this can only be set/unset when the shell is
invoked.
T}
\-k keyword T{
Parameter assignments are recognized anywhere in a command.
T}
\-m monitor T{
Enable job control (default for interactive shells).
T}
\-n noexec T{
Do not execute any commands \- useful for checking the syntax of scripts
(ignored if interactive).
T}
\-p privileged T{
Set automatically if, when the shell starts, the read uid or gid does not
match the effective uid or gid, respectively.
See Shell Startup above for a description of what this
means.
T}
-r restricted T{
Enable restricted mode \(em this option can only be used when the shell is
invoked. See Shell Startup above for a description of what this
means.
T}
\-s T{
T}
\-s stdin T{
If used when the shell is invoked, commands are read from standard input.
Set automatically if the shell is invoked with no arguments.
.sp
When \fB\-s\fP is used in the \fBset\fP command, it causes the arguments to
be sorted before assigning them to the positional parameters
(or to array \fIname\fP, if \fB\-A\fP is used).
T}
\-u nounset T{
Referencing of an unset parameter is treated as an error, unless
one of the \fB\-\fP, \fB+\fP or \fB=\fP modifiers is used.
T}
\-v verbose T{
Write shell input to standard error as it is read.
T}
\-x xtrace T{
Print commands and parameter assignments when they are executed,
preceded by the value of \fBPS4\fP.
T}
\-X markdirs T{
Mark directories with a trailing \fB/\fP during file name generation.
T}
bgnice T{
Background jobs are run with lower priority.
T}
braceexpand T{
Enable brace expansion (aka, alternation).
T}
emacs T{
Enable BRL emacs-like command line editing (interactive shells only);
see Emacs Interactive Input Line Editing.
T}
gmacs T{
Enable gmacs-like (Gosling emacs) command line editing (interactive shells
only);
currently identical to emacs editing except that transpose (^T) acts
slightly differently.
T}
ignoreeof T{
The shell will not exit on when end-of-file is read, \fBexit\fP must be used.
T}
nolog T{
No effect \- in the original Korn shell, this prevents function definitions
from being stored in the history file.
T}
posix T{
Enable posix mode. See POSIX Mode above.
T}
vi T{
Enable vi-like command line editing (interactive shells only).
T}
viraw T{
No effect \- in the original Korn shell, unless viraw was set, the vi command
line mode would let the tty driver do the work until ESC (^[) was entered.
pdksh is always in viraw mode.
T}
vi-show8 T{
Prefix characters with the eighth bit set with `M-'.
If this option is not set, characters in the range
128-160 are printed as is, which may cause problems.
T}
vi-tabcomplete T{
In vi command line editing, do file name completion when tab (^I) is entered
in insert mode.
T}
.TE
.sp
These options can also be used upon invocation of the shell. The current
set of options can be found in the parameter \fB\-\fP. Remaining
arguments, if any, are positional parameters and are assigned, in order, to the
positional parameters (i.e., \fB1\fP, \fB2\fP, etc.).
If options are ended with \fB\-\-\fP and there are no remaining arguments,
all positional parameters are cleared.
If no options or arguments are given, then the values of all names are printed.
For unknown historical reasons, a lone \fB\-\fP option is treated specially:
it clears both the \fB\-x\fP and \fB\-v\fP options.
.\"}}}
.\"{{{ shift [number]
.IP "\fBshift\fP [\fInumber\fP]"
The positional parameters \fInumber\fP, \fInumber+1\fP etc.\& are renamed
to \fB1\fP, \fB2\fP, etc.
\fInumber\fP defaults to 1.
.\"}}}
.\"{{{ test expression, [ expression ]
.IP "\fBtest\fP \fIexpression\fP"
\fBtest\fP evaluates the \fIexpression\fP and returns zero status if
true, and non-zero status otherwise. It is normally used as the
condition command of \fBif\fP and \fBwhile\fP statements.
The following basic expressions are available:
.sp
.TS
afB ltw(2.8i).
\-r \fIfile\fP T{
\fIfile\fP exists and is readable
T}
\-w \fIfile\fP T{
\fIfile\fP exists and is writable
T}
\-x \fIfile\fP T{
\fIfile\fP exists and is executable
T}
\-a \fIfile\fP T{
\fIfile\fP exists
T}
\-e \fIfile\fP T{
\fIfile\fP exists
T}
\-f \fIfile\fP T{
\fIfile\fP is a regular file
T}
\-d \fIfile\fP T{
\fIfile\fP is a directory
T}
\-c \fIfile\fP T{
\fIfile\fP is a character special device
T}
\-b \fIfile\fP T{
\fIfile\fP is a block special device
T}
\-p \fIfile\fP T{
\fIfile\fP is a named pipe
T}
\-u \fIfile\fP T{
\fIfile\fP's mode has setuid bit set
T}
\-g \fIfile\fP T{
\fIfile\fP's mode has setgid bit set
T}
\-k \fIfile\fP T{
\fIfile\fP's mode has sticky bit set
T}
\-s \fIfile\fP T{
\fIfile\fP is not empty
T}
\-O \fIfile\fP T{
\fIfile\fP's owner is the shell's effective user-ID
T}
\-G \fIfile\fP T{
\fIfile\fP's group is the shell's effective group-ID
T}
\-h \fIfile\fP T{
\fIfile\fP is a symbolic link
T}
\-H \fIfile\fP T{
\fIfile\fP is a context dependent directory (only useful on HP-UX)
T}
\-L \fIfile\fP T{
\fIfile\fP is a symbolic link
T}
\-S \fIfile\fP T{
\fIfile\fP is a socket
T}
\-o \fIoption\fP T{
shell \fIoption\fP is set (see \fBset\fP command above for list of options)
T}
\fIfile\fP \-nt \fIfile\fP T{
first \fIfile\fP is newer than second \fIfile\fP
T}
\fIfile\fP \-ot \fIfile\fP T{
first \fIfile\fP is older than second \fIfile\fP
T}
\fIfile\fP \-ef \fIfile\fP T{
first \fIfile\fP is the same file as second \fIfile\fP
T}
\-t [\fIfd\fP] T{
file descriptor is a tty device.
Default value of \fIfd\fP is 1.
T}
\fIstring\fP T{
\fIstring\fP is not empty
T}
\-z \fIstring\fP T{
\fIstring\fP is empty
T}
\-n \fIstring\fP T{
\fIstring\fP is not empty
T}
\fIstring\fP = \fIstring\fP T{
strings are equal
T}
\fIstring\fP != \fIstring\fP T{
strings are not equal
T}
\fInumber\fP \-eq \fInumber\fP T{
numbers compare equal
T}
\fInumber\fP \-ne \fInumber\fP T{
numbers compare not equal
T}
\fInumber\fP \-ge \fInumber\fP T{
numbers compare greater than or equal
T}
\fInumber\fP \-gt \fInumber\fP T{
numbers compare greater than
T}
\fInumber\fP \-le \fInumber\fP T{
numbers compare less than or equal
T}
\fInumber\fP \-lt \fInumber\fP T{
numbers compare less than
T}
.TE
.sp
The above basic expressions, in which unary operators have precedence over
binary operators, may be combined with the following operators
(listed in increasing order of precedence):
.sp
.TS
afB l.
\fIexpr\fP \-o \fIexpr\fP logical or
\fIexpr\fP \-a \fIexpr\fP logical and
! \fIexpr\fP logical not
( \fIexpr\fP ) grouping
.TE
.sp
Note that some special rules are applied (courtesy of POSIX) if the
number of arguments to \fBtest\fP or \fB[\fP \&... \fB]\fP is less than
five: if leading \fB!\fP arguments can be stripped such that only one
argument remains then a string length test is performed (again, even if
the argument is a unary operator);
if leading \fB!\fP arguments can be stripped such that three
arguments remain and the second argument is a binary operator, then the
binary operation is performed (even if first argument is a unary
operator, including an unstripped \fB!\fP).
.sp
\fBNote:\fP A common mistake is to use \fBif [ $foo = bar ]\fP which
fails if parameter \fBfoo\fP is null or unset, if it has embedded spaces
(i.e., \fBIFS\fP characters), or if it is a unary operator like \fB!\fP or
\fB\-n\fP. Use tests like \fBif [ "X$foo" = Xbar ]\fP instead.
.\"}}}
.\"{{{ times
.IP \fBtimes\fP
Print the accumulated user and system times used by the shell and by
processes which have exited that the shell started.
.\"}}}
.\"{{{ trap [handler signal ...]
.IP "\fBtrap\fP [\fIhandler\fP \fIsignal ...\fP]"
Sets trap handler that is to be executed when any of the specified signals
are received.
\fBHandler\fP is either a null string, indicating the signals are to
be ignored, a minus (\fB\-\fP), indicating that the default action is to
be taken for the signals (see signal(2 or 3)), or a string containing shell
commands to be evaluated and executed at the first opportunity (i.e., when the
current command completes, or before printing the next \fBPS1\fP prompt) after
receipt of one of the signals.
\fBSignal\fP is the name of a signal (e.g., PIPE or ALRM) or the number
of the signal (see \fBkill \-l\fP command above).
There are two special signals: \fBEXIT\fP (also known as \fB0\fP), which
is executed when the shell is about to exit, and \fBERR\fP which is
executed after an error occurs (an error is something that would cause
the shell to exit if the \fB\-e\fP or \fBerrexit\fP option were set -
see \fBset\fP command above).
\fBEXIT\fP handlers are executed in the environment of the last executed
command.
Note that for non-interactive shells, the trap handler cannot be changed for
signals that were ignored when the shell started.
.sp
With no arguments, \fBtrap\fP lists, as a series of \fBtrap\fP commands,
the current state of the traps that have been set since the shell started.
.sp
.\" todo: add these features (trap DEBUG, trap ERR/EXIT in function)
The original Korn shell's \fBDEBUG\fP trap and the handling of \fBERR\fP and
\fBEXIT\fP traps in functions are not yet implemented.
.\"}}}
.\"{{{ true
.IP \fBtrue\fP
A command that exits with a zero value.
.\"}}}
.\"{{{ typeset [[+-Ulrtux] [-L[n]] [-R[n]] [-Z[n]] [-i[n]] | -f [-tux]] [name[=value] ...]
.IP "\fBtypeset\fP [[\(+-Ulrtux] [\fB\-L\fP[\fIn\fP]] [\fB\-R\fP[\fIn\fP]] [\fB\-Z\fP[\fIn\fP]] [\fB\-i\fP[\fIn\fP]] | \fB\-f\fP [\fB\-tux\fP]] [\fIname\fP[\fB=\fP\fIvalue\fP] ...]"
Display or set parameter attributes.
With no \fIname\fP arguments, parameter attributes are displayed: if no options
arg used, the current attributes of all parameters are printed as typeset
commands; if an option is given (or \fB\-\fP with no option letter)
all parameters and their values with the specified attributes are printed;
if options are introduced with \fB+\fP, parameter values are not printed.
.sp
If \fIname\fP arguments are given, the attributes of the named parameters
are set (\fB\-\fP) or cleared (\fB+\fP).
Values for parameters may optionally be specified.
If typeset is used inside a function, any newly created parameters are local
to the function.
.sp
When \fB\-f\fP is used, typeset operates on the attributes of functions.
As with parameters, if no \fIname\fPs are given, functions are listed
with their values (i.e., definitions) unless options are introduced with
\fB+\fP, in which case only the function names are reported.
.sp
.TS
expand;
afB lw(4.5i).
\-L\fIn\fP T{
Left justify attribute: \fIn\fP specifies the field width.
If \fIn\fP is not specified, the current width of a parameter (or the
width of its first assigned value) is used.
Leading white space (and zeros, if used with the \fB\-Z\fP option) is stripped.
If necessary, values are either truncated or space padded to fit the
field width.
T}
\-R\fIn\fP T{
Right justify attribute: \fIn\fP specifies the field width.
If \fIn\fP is not specified, the current width of a parameter (or the
width of its first assigned value) is used.
Trailing white space are stripped.
If necessary, values are either stripped of leading characters
or space padded to make them fit the field width.
T}
\-Z\fIn\fP T{
Zero fill attribute: if not combined with \fB\-L\fP, this is the
same as \fB\-R\fP, except zero padding is used instead of space padding.
T}
\-i\fIn\fP T{
integer attribute:
\fIn\fP specifies the base to use when displaying the integer
(if not specified, the base given in the first assignment is used).
Parameters with this attribute may be assigned values containing
arithmetic expressions.
T}
\-U T{
unsigned integer attribute: integers are printed as unsigned values
(only useful when combined with the \fB\-i\fP option).
This option is not in the original Korn shell.
T}
\-f T{
Function mode: display or set functions and their attributes, instead of
parameters.
T}
\-l T{
Lower case attribute: all upper case characters in values are converted to
lower case.
(In the original Korn shell, this parameter meant `long integer' when used
with the \fB\-i\fP option).
T}
\-r T{
Readonly attribute: parameters with the this attribute may not be assigned to
or unset.
Once this attribute is set, it can not be turned off.
T}
\-t T{
Tag attribute: has no meaning to the shell; provided for application use.
.sp
For functions, \fB\-t\fP is the trace attribute.
When functions with the trace attribute are executed, the \fBxtrace\fP (\fB\-x\fP) shell option is temporarily turned on.
T}
\-u T{
Upper case attribute: all lower case characters in values are converted to
upper case.
(In the original Korn shell, this parameter meant `unsigned integer' when used
with the \fB\-i\fP option, which meant upper case letters would never be used
for bases greater than 10. See the \fB\-U\fP option).
.sp
For functions, \fB\-u\fP is the undefined attribute. See Functions above
for the implications of this.
T}
\-x T{
Export attribute: parameters (or functions) are placed in the environment of
any executed commands. Exported functions are not implemented yet.
T}
.TE
.\"}}}
.\"{{{ ulimit [-acdfHmnsStvw] [value]
.IP "\fBulimit\fP [\fB\-acdfHmnsStvw\fP] [\fIvalue\fP]"
Display or set process limits.
If no options are used, the file size limit (\fB\-f\fP) is assumed.
\fBvalue\fP, if specified, may be either be an arithmetic expression or the
word \fBunlimited\fP.
The limits affect the shell and any processes created by the shell after
a limit is imposed.
Note that some systems may not allow limits to be increased once they
are set.
Also note that the types of limits available are system dependent \- some
systems have only the \fB\-f\fP limit.
.RS
.IP \fB\-a\fP
Displays all limits.
.IP \fB\-H\fP
Set the hard limit only (default is to set both hard and soft limits).
.IP \fB\-S\fP
Set the soft limit only (default is to set both hard and soft limits).
.IP \fB\-c\fP
Impose a size limit of \fIn\fP blocks on the size of core dumps.
.IP \fB\-d\fP
Impose a size limit of \fIn\fP kbytes on the size of the data area.
.IP \fB\-f\fP
Impose a size limit of \fIn\fP blocks on files written by the shell and
its child processes (files of any size may be read).
.IP \fB\-m\fP
Impose a limit of \fIn\fP kbytes on the amount of physical memory used.
.IP \fB\-n\fP
Impose a limit of \fIn\fP file descriptors that can be open at once.
.IP \fB\-s\fP
Impose a size limit of \fIn\fP kbytes on the size of the stack area.
.IP \fB\-t\fP
Impose a time limit of \fIn\fP seconds to be used by each process.
.IP \fB\-v\fP
Impose a limit of \fIn\fP kbytes on the amount of virtual memory used;
on some systems this is the maximum allowable virtual address (in bytes,
not kbytes).
.IP \fB\-w\fP
Impose a limit of \fIn\fP kbytes on the amount of swap space used.
.PP
As far as \fBulimit\fP is concerned, a block is 512 bytes.
.RE
.\"}}}
.\"{{{ umask [-S] [mask]
.IP "\fBumask\fP [\fB\-S\fP] [\fImask\fP]"
.RS
Display or set the file permission creation mask, or umask (see \fIumask\fP(2)).
If the \fB\-S\fP option is used, the mask displayed or set is symbolic,
otherwise it is an octal number.
.sp
Symbolic masks are like those used by \fIchmod\fP(1):
.ce
[\fBugoa\fP]{{\fB=+-\fP}{\fBrwx\fP}*}+[\fB,\fP...]
in which the first group of characters is the \fIwho\fP part, the second
group is the \fIop\fP part, and the last group is the \fIperm\fP part.
The \fIwho\fP part specifies which part of the umask is to be modified.
The letters mean:
.RS
.IP \fBu\fP
the user permissions
.IP \fBg\fP
the group permissions
.IP \fBo\fP
the other permissions (non-user, non-group)
.IP \fBa\fP
all permissions (user, group and other)
.RE
.sp
The \fIop\fP part indicates how the \fIwho\fP permissions are to be modified:
.RS
.IP \fB=\fP
set
.IP \fB+\fP
added to
.IP \fB\-\fP
removed from
.RE
.sp
The \fIperm\fP part specifies which permissions are to be set, added or removed:
.RS
.IP \fBr\fP
read permission
.IP \fBw\fP
write permission
.IP \fBx\fP
execute permission
.RE
.sp
When symbolic masks are used, they describe what permissions may
be made available (as opposed to octal masks in which a set bit means
the corresponding bit is to be cleared).
Example: `ug=rwx,o=' sets the mask so files will not be readable, writable
or executable by `others', and is equivalent (on most systems) to the octal
mask `07'.
.RE
.\"}}}
.\"{{{ unalias [-adt] name ...
.IP "\fBunalias\fP [\fB\-adt\fP] [\fIname1\fP ...]"
The aliases for the given names are removed.
If the \fB\-a\fP option is used, all aliases are removed.
If the \fB\-t\fP or \fB\-d\fP options are used, the indicated operations
are carried out on tracked or directory aliases, respectively.
.\"}}}
.\"{{{ unset [-fv] parameter ...
.IP "\fBunset\fP [\fB\-fv\fP] \fIparameter\fP ..."
Unset the named parameters (\fB\-v\fP, the default) or functions (\fB\-f\fP).
.\"}}}
.\"{{{ wait [job]
.IP "\fBwait\fP [\fIjob\fP]"
Wait for the specified job(s) to finish.
The exit status of wait is that of the last specified job:
if the last job is killed by a signal, the exit status is 128 + the
number of the signal (see \fBkill \-l\fP \fIexit-status\fP above); if the last
specified job can't be found (because it never existed, or had already
finished), the exit status of wait is 127.
See Job Control below for the format of \fIjob\fP.
\fBWait\fP will return if a signal for which a trap has been set is received,
or if a HUP, INT or QUIT signal is received.
.sp
If no jobs are specified, \fBwait\fP waits for all currently running jobs
(if any) to finish and exits with a zero status.
If job monitoring is enabled, the completion status of jobs is
printed (this is not the case when jobs are explicitly specified).
.\"}}}
.\"{{{ whence [-pv] [name ...]
.IP "\fBwhence\fP [\fB\-pv\fP] [name ...]"
For each name, the type of command is listed (keyword, built-in, alias,
function, tracked alias or executable).
If the \fB\-p\fP option is used, a path search done even if \fIname\fP
is a keyword, alias, etc.
Without the \fB\-v\fP option, \fBwhence\fP is the same as \fBcommand \-v\fP;
with the \fB\-V\fP option, it is the same as \fBcommand \-V\fP.
Note that for \fBwhence\fP, the \fB\-p\fP option does not affect the search
path used, as it does for \fBcommand\fP.
If the type of one or more of the names could not be determined, the
exit status is non-zero.
.\"}}}
.\"}}}
.\"{{{ job control (and its builtin commands)
.SS "Job Control"
Job control refers to the shell's ability to monitor and control \fBjobs\fP,
which are processes or groups of processes created for commands or pipelines.
At a minimum, the shell keeps track of the status of the background
(i.e., asynchronous) jobs that currently exist; this information can be
displayed using the \fBjobs\fP command.
If job control is fully enabled (using \fBset \-m\fP or
\fBset \-o monitor\fP), as it is for interactive shells,
the processes of a job are placed in their own process group,
foreground jobs can be stopped by typing the suspend character from the
terminal (normally ^Z),
jobs can be restarted in either the foreground
or background, using the \fBfg\fP and \fBbg\fP commands, respectively,
and the state of the terminal is saved or restored when a foreground
job is stopped or restarted, respectively.
.sp
Note that only commands that create processes (e.g., asynchronous commands,
subshell commands, and non-builtin, non-function commands) can be
stopped; commands like \fBread\fP cannot be.
.sp
When a job is created, it is assigned a job-number.
For interactive shells, this number is printed inside \fB[\fP..\fB]\fP,
followed by the process-ids of the processes in the job when an asynchronous
command is run.
A job may be referred to in \fBbg\fP, \fBfg\fP, \fBjobs\fP, \fBkill\fP and
\fBwait\fP commands either by the process id of the last process in the
command pipeline (as stored in the \fB$!\fP parameter) or by prefixing the
job-number with a percent sign (\fB%\fP).
Other percent sequences can also be used to refer to jobs:
.sp
.TS
expand;
afB lw(4.5i).
%+ T{
The most recently stopped job, or, if there are no stopped jobs, the oldest
running job.
T}
%%\fR, \fP% T{
Same as \fB%+\fP.
T}
%\- T{
The job that would be the \fB%+\fP job, if the later did not exist.
T}
%\fIn\fP T{
The job with job-number \fIn\fP.
T}
%?\fIstring\fP T{
The job containing the string \fIstring\fP (an error occurs if multiple jobs
are matched).
T}
%\fIstring\fP T{
The job starting with string \fIstring\fP (an error occurs if multiple jobs
are matched).
T}
.TE
.sp
When a job changes state (e.g., a background job finishes or foreground
job is stopped), the shell prints the following status information:
.ce 1
\fB[\fP\fInumber\fP\fB]\fP \fIflag status command\fP
where
.IP "\ \fInumber\fP"
is the job-number of the job.
.IP "\ \fIflag\fP"
is \fB+\fP or \fB-\fP if the job is the \fB%+\fP or \fB%-\fP job,
respectively, or space if it is neither.
.IP "\ \fIstatus\fP"
indicates the current state of the job and can be
.RS
.IP "\fBRunning\fP
the job has neither stopped or exited (note that running does not
necessarily mean consuming CPU time \(em the process could be blocked waiting
for some event).
.IP "\fBDone\fP [\fB(\fP\fInumber\fP\fB)\fP]"
the job exited. \fInumber\fP is the exit status of the job, which is
omitted if the status is zero.
.IP "\fBStopped\fP [\fB(\fP\fIsignal\fP\fB)\fP]
the job was stopped by the indicated \fIsignal\fP (if no signal is given,
the job was stopped by SIGTSTP).
.IP "\fIsignal-description\fP [\fB(core dumped)\fP]"
the job was killed by a signal (e.g., Memory\ fault, Hangup, etc. \(em use
\fBkill \-l\fP for a list of signal descriptions).
The \fB(core\ dumped)\fP message indicates the process created a core file.
.RE
.IP "\ \fIcommand\fP"
is the command that created the process.
If there are multiple processes in the job, then each process will
have a line showing its \fIcommand\fP and possibly its \fIstatus\fP,
if it is different from the status of the previous process.
.\"}}}
.\"{{{ Emacs Interactive Input Line Editing
.SS "Emacs Interactive Input Line Editing"
When the \fBemacs\fP option is set, interactive input line editing is
enabled. \fBWarning\fP: This mode is slightly different from the emacs
mode in the original Korn shell and the 8th bit is stripped in emacs mode.
In this mode various editing commands (typically bound to one or more
control characters) cause immediate actions without waiting for a
new-line. Several editing commands are bound to particular control
characters when the shell is invoked; these bindings can be changed
using the following commands:
.\"{{{ bind
.IP \fBbind\fP
The current bindings are listed.
.\"}}}
.\"{{{ bind string=[editing-command]
.IP "\fBbind\fP \fIstring\fP\fB=\fP[\fIediting-command\fP]
The specified editing command is bound to the given \fBstring\fP, which
should consist of a control character (which may be written using caret
notation \fB^\fP\fIX\fP), optionally preceded by one of the two prefix
characters. Future input of the \fIstring\fP will cause the editing
command to be immediately invoked. Note that although only two prefix
characters (usually ESC and ^X) are supported, some multi-character
sequences can be supported. The following binds the arrow keys on
an ANSI terminal, or xterm (these are in the default bindings). Of course
some escape sequences won't work out quite this nicely:
.sp
.RS
\fBbind '^[['=prefix\-2
.br
bind '^XA'=up\-history
.br
bind '^XB'=down\-history
.br
bind '^XC'=forward\-char
.br
bind '^XD'=backward\-char\fP
.RE
.\"}}}
.\"{{{ bind -m string=[substitute]
.IP "\fBbind \-m\fP \fIstring\fP\fB=\fP[\fIsubstitute\fP]"
The specified input \fIstring\fP will afterwards be immediately
replaced by the given \fIsubstitute\fP string, which may contain
editing commands.
.\"}}}
.PP
The following editing commands are available; first the command name is
given followed by its default binding (if any) using caret notation
(note that the ASCII ESC character is written as ^[), then the editing
function performed is described. Note that editing command names are
used only with the \fBbind\fP command. Furthermore, many editing
commands are useful only on terminals with a visible cursor. The
default bindings were chosen to resemble corresponding EMACS key
bindings. The users tty characters (e.g., ERASE) are bound to
reasonable substitutes.
.\"{{{ abort ^G
.IP "\fBabort ^G\fP"
Useful as a response to a request for a \fBsearch-history\fP pattern in
order to abort the search.
.\"}}}
.\"{{{ auto-insert
.IP \fBauto-insert\fP
Simply causes the character to appear as literal input. Most ordinary
characters are bound to this.
.\"}}}
.\"{{{ backward-char
.IP "\fBbackward-char ^b\fP"
Moves the cursor backward one character.
.\"}}}
.\"{{{ backward-word ^[b
.IP "\fBbackward-word ^[b\fP"
Moves the cursor backward to the beginning of a word; words are
delimited by the current setting of \fBIFS\fP.
.\"}}}
.\"{{{ beginning-of-line ^A
.IP "\fBbeginning-of-line ^A\fP"
Moves the cursor to the beginning of the edited input line.
.\"}}}
.\"{{{ complete ^[^[
.IP "\fBcomplete ^[^[\fP"
Automatically completes as much as is unique of the hashed command name
or the file name containing the cursor. If the entire remaining command
or file name is unique a space is printed after its completion, unless
it is a directory name in which case \fB/\fP is appended. If there is
no hashed command or file name with the current partial word as its
prefix, a bell character is output (usually causing a audio beep).
.\"}}}
.\"{{{ complete-command ^X^[
.IP "\fBcomplete-command ^X^[\fP"
Automatically completes as much as is unique of the hashed command name
having the partial word up to the cursor as its prefix, as in the
\fBcomplete\fP command described above. Only command and function names
seen since the last \fBhash \-r\fP command are available for
completion; the \fBhash\fP command may be used to register additional
names.
.\"}}}
.\"{{{ complete-file ^X^X
.IP "\fBcomplete-file ^X^X\fP"
Automatically completes as much as is unique of the file name having
the partial word up to the cursor as its prefix, as in the
\fBcomplete\fP command described above.
.\"}}}
.\"{{{ copy-last-arg ^[_
.IP "\fBcopy-last-arg ^[_\fP"
The last word of the previous command is inserted at the cursor. Note
I/O redirections do not count as words of the command.
.\"}}}
.\"{{{ delete-char-backward ERASE
.IP "\fBdelete-char-backward ERASE\fP"
Deletes the character before the cursor.
.\"}}}
.\"{{{ delete-char-forward
.IP "\fBdelete-char-forward\fP"
Deletes the character after the cursor.
.\"}}}
.\"{{{ delete-word-backward ^[ERASE
.IP "\fBdelete-word-backward ^[ERASE\fP"
Deletes characters before the cursor back to the beginning of a word.
.\"}}}
.\"{{{ delete-word-forward ^[d
.IP "\fBdelete-word-forward ^[d\fP"
Deletes characters after the cursor up to the end of a word.
.\"}}}
.\"{{{ down-history ^N
.IP "\fBdown-history ^N\fP"
Scrolls the history buffer forward one line (later). Each input line
originally starts just after the last entry in the history buffer, so
\fBdown-history\fP is not useful until either \fBsearch-history\fP or
\fBup-history\fP has been performed.
.\"}}}
.\"{{{ end-of-line ^E
.IP "\fBend-of-line ^E\fP"
Moves the cursor to the end of the input line.
.\"}}}
.\"{{{ eot ^_
.IP "\fBeot ^_\fP"
Acts as an end-of-file; this is useful because edit-mode input disables
normal terminal input canonicalization.
.\"}}}
.\"{{{ eot-or-delete ^d
.IP "\fBeot-or-delete ^d\fP"
Acts as eot if alone on a line; otherwise acts as delete-char-forward.
.\"}}}
.\"{{{ exchange-point-and-mark ^X^X
.IP "\fBexchange-point-and-mark ^X^X\fP"
Places the cursor where the mark is, and sets the mark to where the
cursor was.
.\"}}}
.\"{{{ forward-char ^F
.IP "\fBforward-char ^F\fP"
Moves the cursor forward one position.
.\"}}}
.\"{{{ forward-word ^[f
.IP "\fBforward-word ^[f\fP"
Moves the cursor forward to the end of a word.
.\"}}}
.\"{{{ kill-line KILL
.IP "\fBkill-line KILL\fP"
Deletes the entire input line.
.\"}}}
.\"{{{ kill-to-eol ^K
.IP "\fBkill-to-eol ^K\fP"
Deletes the input from the cursor to the end of the line.
.\"}}}
.\"{{{ kill-region ^W
.IP "\fBkill-region ^W\fP"
Deletes the input between the cursor and the mark.
.\"}}}
.\"{{{ list ^[?
.IP "\fBlist ^[?\fP"
Prints a sorted, columnated list of hashed command names or file names
(if any) that can complete the partial word containing the cursor.
Directory names have \fB/\fP appended to them, and executable file
names are followed by a \fB*\fP.
.\"}}}
.\"{{{ list-command ^X?
.IP "\fBlist-command ^X?\fP"
Prints a sorted, columnated list of hashed command names (if any) that
can complete the partial word containing the cursor.
.\"}}}
.\"{{{ list-file ^X^Y
.IP "\fBlist-file ^X^Y\fP"
Prints a sorted, columnated list of file names (if any) that can
complete the partial word containing the cursor. File type indicators
are appended as described under \fBlist\fP above.
.\"}}}
.\"{{{ newline ^J and ^M
.IP "\fBnewline ^J\fP"
.IP "\fBnewline ^M\fP"
Causes the current input line to be processed by the shell. The
current cursor position may be anywhere on the line.
.\"}}}
.\"{{{ newline-and-next ^O
.IP "\fBnewline-and-next ^O\fP"
Causes the current input line to be processed by the shell, and
the next line from history becomes the current line. This is
only useful after an up-history or search-history.
.\"}}}
.\"{{{ no-op QUIT
.IP "\fBno-op QUIT\fP"
This does nothing.
.\"}}}
.\"{{{ prefix-1 ^[
.IP "\fBprefix-1 ^[\fP"
Introduces a 2-character command sequence.
.\"}}}
.\"{{{ prefix-2 ^X and ^[[
.IP "\fBprefix-2 ^X\fP"
.IP "\fBprefix-2 ^[[\fP"
Introduces a 2-character command sequence.
.\"}}}
.\"{{{ quote ^^
.IP "\fBquote ^^\fP"
The following character is taken literally rather than as an editing
command.
.\"}}}
.\"{{{ redraw ^L
.IP "\fBredraw ^L\fP"
Reprints the prompt string and the current input line.
.\"}}}
.\"{{{ search-character ^]
.IP "\fBsearch-character ^]\fP"
Search forward in the current line for the next keyboard character.
.\"}}}
.\"{{{ search-history ^R
.IP "\fBsearch-history ^R\fP"
Enter incremental search mode. The internal history list is searched
backwards for commands matching the input. An initial \fB^\fP in the
search string anchors the search. The abort key will leave search mode.
Other commands will be executed after leaving search mode. Successive
\fBsearch-history\fP commands continue searching backward to the next
previous occurrence of the pattern. The history buffer retains only a
finite number of lines; the oldest are discarded as necessary.
.\"}}}
.\"{{{ set-mark-command
.IP "\fBset-mark-command ^[\fP"
Set the mark at the cursor position.
.\"}}}
.\"{{{ stuff
.IP "\fBstuff\fP"
On systems supporting it, pushes the bound character back onto the
terminal input where it may receive special processing by the terminal
handler. This is useful for the BRL \fB^T\fP mini-systat feature, for
example.
.\"}}}
.\"{{{ stuff-reset
.IP \fBstuff-reset\fP
Acts like \fBstuff\fP, then aborts input the same as an interrupt.
.\"}}}
.\"{{{ transport-chars ^T
.IP "\fBtranspose-chars ^T\fP"
If at the end of line, or if the \fBgmacs\fP option is set,
this exchanges the two previous characters; otherwise, it
exchanges the previous and current characters and moves the cursor
one character to the right.
.\"}}}
.\"{{{ up-history ^P
.IP "\fBup-history ^P\fP"
Scrolls the history buffer backward one line (earlier).
.\"}}}
.\"{{{ yank ^Y
.IP "\fByank ^Y\fP"
Inserts the most recently killed text string at the current cursor position.
.\"}}}
.\"{{{ yank-pop ^[y
.IP "\fByank-pop ^[y\fP"
Immediately after a \fByank\fP, replaces the inserted text string with
the next previous killed text string.
.\"}}}
.\"{{{ version ^V
.IP "\fBversion ^V\fP"
Display the version of ksh. The current edit buffer is restored as soon
as any key is pressed (the key is then processed).
.\"}}}
.\"}}}
.\"{{{ Vi Interactive Input Line Editing
.\"{{{ introduction
.SS "Vi Interactive Input Line Editing"
The vi command line editor in ksh has basically the same commands as the
vi editor (see \fIvi\fP(1)), with the following exceptions:
.nr P2 \n(PD
.IP \ \ \(bu
you start out in insert mode,
.IP \ \ \(bu
there are file name completion commands (\fB=\fP, \fB\e\fP, \fB*\fP, \fB^X\fP,
\fB^E\fP, \fB^F\fP and, optionally, \fB^I\fP),
.IP \ \ \(bu
the \fB_\fP command is different (in ksh it is the last argument command,
in vi it goes to the start of the current line),
.IP \ \ \(bu
the \fB/\fP and \fBG\fP commands move in the opposite direction as the \fBj\fP
command
.IP \ \ \(bu
and commands which don't make sense in a single line editor aren't available
(e.g., screen movement commands, ex \fB:\fP commands, etc.).
.nr PD \n(P2
.LP
Note that the \fB^X\fP stands for control-X; also \fB\fP, \fB\fP
and \fB\fP are used for escape, space and tab, respectively (no kidding).
.\"}}}
.\"{{{ modes
.PP
Like vi, there are two modes: insert mode and command mode.
In insert mode, most characters are simply put in the buffer at the
current cursor position as they are typed, however, some characters
are treated specially.
In particular, the following characters are taken from current tty settings
(see \fIstty\fP(1)) and have their usual meaning (normal values are in
parentheses):
kill (\fB^U\fP), erase (\fB^?\fP), werase (\fB^W\fP), eof (\fB^D\fP),
intr (\fB^C\fP) and quit (\fB^\e\fP).
In addition to the above, the following characters are also treated
specially in insert mode:
.TS
expand;
afB lw(4.5i).
^H T{
erases previous character
T}
^V T{
literal next: the next character typed is not treated specially (can be
used to insert the characters being described here)
T}
^J ^M T{
end of line: the current line is read, parsed and executed by the shell
T}
T{
puts the editor in command mode (see below)
T}
^E T{
file name enumeration (see below)
T}
^F T{
file name completion (see below)
T}
^X T{
file name expansion (see below)
T}
T{
optional file name completion (see below), enabled with
\fBset -o vi-tabcomplete\fP
T}
.TE
.\"}}}
.\"{{{ display
.PP
If a line is longer that the screen width (see \fBCOLUMNS\fP parameter),
a \fB>\fP, \fB+\fP or \fB<\fP character is displayed in the last column
indicating that there are more characters after, before and after, or
before the current position, respectively.
The line is scrolled horizontally as necessary.
.\"}}}
.\"{{{ command mode
.PP
In command mode, each character is interpreted as a command.
Characters that don't correspond to commands, are illegal combinations of
commands or are commands that can't be carried out all cause beeps.
In the following command descriptions, a \fIn\fP indicates the
command may be prefixed by a number (e.g., \fB10l\fP moves right 10
characters); if no number prefix is used, \fIn\fP is assumed to be 1
unless otherwise specified.
The term `current position' refers to the position between the cursor
and the character preceding the cursor.
A `word' is a sequence of letters, digits and underscore characters or a
sequence of non-letter, non-digit, non-underscore, non-white-space characters
(e.g., ab2*&^ contains two words) and a `big-word' is a sequence of
non-white-space characters.
.\"{{{ Special ksh vi commands
.IP "Special ksh vi commands"
The following commands are not in, or are different from, the normal vi file
editor:
.RS
.IP "\fIn\fP\fB_\fP"
insert a space followed by the \fIn\fPth big-word from the last command in the
history at the current position and enter insert mode; if \fIn\fP is not
specified, the last word is inserted.
.IP "\fB#\fP"
insert the comment character (\fB#\fP) at the start of the current line and
return the line to the shell (equivalent to \fBI#^J\fP).
.IP "\fIn\fP\fBg\fP"
like \fBG\fP, except if \fIn\fP is not specified, it goes to the most recent
remembered line.
.IP "\fIn\fP\fBv\fP"
edit line \fIn\fP using the vi editor;
if \fIn\fP is not specified, the current line is edited.
The actual command executed is
`\fBfc \-e ${VISUAL:-${EDITOR:-vi}}\fP \fIn\fP'.
.IP "\fB*\fP and \fB^X\fP"
file name expansion: replace the current big-word with the words obtained by
appending a * to it and doing file name expansion. After expansion, the
cursor is placed just past the last word and the editor is in insert mode.
.IP "\fB\e\fP and \fB^F\fP"
file name completion: replace the current big-word with the longest unique
match obtained after performing file name expansion.
.IP "\fB=\fP and \fB^E\fP"
file name enumeration: list all the files that match the current big-word.
.RE
.\"}}}
.\"{{{ Intra-line movement commands
.IP "Intra-line movement commands"
.RS
.IP "\fIn\fP\fBh\fP and \fIn\fP\fB^H\fP"
move left \fIn\fP characters.
.IP "\fIn\fP\fBl\fP and \fIn\fP\fB\fP"
move right \fIn\fP characters.
.IP "\fB0\fP"
move to column 0.
.IP "\fB^\fP"
move to the first non-space character.
.IP "\fIn\fP\fB|\fP"
move to column \fIn\fP.
.IP "\fB$\fP"
move to the last character.
.IP "\fIn\fP\fBb\fP"
move back \fIn\fP words.
.IP "\fIn\fP\fBB\fP"
move back \fIn\fP big-words.
.IP "\fIn\fP\fBf\fP"
move forward \fIn\fP words.
.IP "\fIn\fP\fBF\fP"
move forward \fIn\fP big-words.
.IP "\fIn\fP\fBe\fP"
move forward to the end the word, \fIn\fP times.
.IP "\fIn\fP\fBE\fP"
move forward to the end the big-word, \fIn\fP times.
.IP "\fB%\fP"
find match: the editor looks forward for the nearest parenthesis,
bracket or brace and then moves the to the matching parenthesis, bracket or
brace.
.IP "\fIn\fP\fBf\fP\fIc\fP"
move forward to the \fIn\fPth occurrence of the character \fIc\fP.
.IP "\fIn\fP\fBF\fP\fIc\fP"
move backward to the \fIn\fPth occurrence of the character \fIc\fP.
.IP "\fIn\fP\fBt\fP\fIc\fP"
move forward to just before the \fIn\fPth occurrence of the character \fIc\fP.
.IP "\fIn\fP\fBT\fP\fIc\fP"
move backward to just before the \fIn\fPth occurrence of the character \fIc\fP.
.IP "\fIn\fP\fB;\fP"
repeats the last \fBf\fP, \fBF\fP, \fBt\fP or \fBT\fP command.
.IP "\fIn\fP\fB,\fP"
repeats the last \fBf\fP, \fBF\fP, \fBt\fP or \fBT\fP command, but moves
in the opposite direction.
.RE
.\"}}}
.\"{{{ Inter-line movement commands
.IP "Inter-line movement commands"
.RS
.IP "\fIn\fP\fBj\fP and \fIn\fP\fB+\fP and \fIn\fP\fB^N\fP"
move to the \fIn\fPth next line in the history.
.IP "\fIn\fP\fBk\fP and \fIn\fP\fB-\fP and \fIn\fP\fB^P\fP"
move to the \fIn\fPth previous line in the history.
.IP "\fIn\fP\fBG\fP"
move to line \fIn\fP in the history; if \fIn\fP is not specified, the
number first remembered line is used.
.IP "\fIn\fP\fBg\fP"
like \fBG\fP, except if \fIn\fP is not specified, it goes to the most recent
remembered line.
.IP "\fIn\fP\fB/\fP\fIstring\fP"
search backward through the history for the \fIn\fPth line containing
\fIstring\fP; if \fIstring\fP starts with \fB^\fP, the remainder of the
string must appear at the start of the history line for it to match.
.IP "\fIn\fP\fB?\fP\fIstring\fP"
same as \fB/\fP, except it searches forward through the history.
.IP "\fIn\fP\fBn\fP"
search for the \fIn\fPth occurrence of the last search string; the
direction of the search is the same as the last search.
.IP "\fIn\fP\fBN\fP"
search for the \fIn\fPth occurrence of the last search string; the
direction of the search is the opposite of the last search.
.RE
.\"}}}
.\"{{{ Edit commands
.IP "Edit commands"
.RS
.IP "\fIn\fP\fBa\fP"
append text \fIn\fP times: goes into insert mode just after the current
position.
The append is only replicated if command mode is re-entered (i.e., is
used).
.IP "\fIn\fP\fBA\fP"
same as \fBa\fP, except it appends at the end of the line.
.IP "\fIn\fP\fBi\fP"
insert text \fIn\fP times: goes into insert mode at the current
position.
The insertion is only replicated if command mode is re-entered (i.e., is
used).
.IP "\fIn\fP\fBI\fP"
same as \fBi\fP, except the insertion is done just before the first non-blank
character.
.IP "\fIn\fP\fBs\fP"
substitute the next \fIn\fP characters (i.e., delete the characters
and go into insert mode).
.IP "\fBS\fP"
substitute whole line: all characters from the first non-blank character
to the end of line are deleted and insert mode is entered.
.IP "\fIn\fP\fBc\fP\fImove-cmd\fP"
change from the current position to the position resulting from \fIn\fP
\fImove-cmd\fPs (i.e., delete the indicated region and go into insert mode);
if \fImove-cmd\fP is \fBc\fP, the line starting from the first non-blank
character is changed.
.IP "\fBC\fP"
change from the current position to the end of the line (i.e., delete to
the end of the line and go into insert mode).
.IP "\fIn\fP\fBx\fP"
delete the next \fIn\fP characters.
.IP "\fIn\fP\fBX\fP"
delete the previous \fIn\fP characters.
.IP "\fIn\fP\fBD\fP"
delete to the end of the line.
.IP "\fIn\fP\fBd\fP\fImove-cmd\fP"
delete from the current position to the position resulting from \fImove-cmd\fP;
\fImove-cmd\fP is a movement command (see above) or \fBd\fP, in which case
the current line is deleted.
.IP "\fIn\fP\fBr\fP\fIc\fP"
replace the next \fIn\fP characters with the character \fIc\fP.
.IP "\fIn\fP\fBR\fP"
replace: enter insert mode but overwrite existing characters instead of
inserting before existing characters. The replacement is repeated \fIn\fP
times.
.IP "\fIn\fP\fB~\fP"
change the case of the next \fIn\fP characters.
.IP "\fIn\fP\fBy\fP\fImove-cmd\fP"
yank from the current position to the position resulting from \fIn\fP
\fImove-cmd\fPs into the yank buffer; if \fImove-cmd\fP is \fBy\fP, the
whole line is yanked.
.IP "\fBY\fP"
yank from the current position to the end of the line.
.IP "\fIn\fP\fBp\fP"
paste the contents of the yank buffer just after the current position,
\fIn\fP times.
.IP "\fIn\fP\fBP\fP"
same as \fBp\fP, except the buffer is pasted at the current position.
.RE
.\"}}}
.\"{{{ Miscellaneous vi commands
.IP "Miscellaneous vi commands"
.RS
.IP "\fB^J\fP and \fB^M\fP"
the current line is read, parsed and executed by the shell.
.IP "\fB^L\fP and \fB^R\fP"
redraw the current line.
.IP "\fIn\fP\fB.\fP"
redo the last edit command \fIn\fP times.
.IP "\fBu\fP"
undo the last edit command.
.IP "\fIintr\fP and \fIquit\fP"
the interrupt and quit terminal characters cause the current line to
be deleted and a new prompt to be printed.
.RE
.\"Has all vi commands except:
.\" movement: { } [[ ]] ^E ^Y ^U ^D ^F ^B H L M ()
.\" tag commands: ^T ^]
.\" mark commands: m ` '
.\" named-buffer commands: " @
.\" file/shell/ex-commands: Q ZZ ^^ : ! &
.\" multi-line change commands: o O J
.\" shift commands: << >>
.\" status command: ^G
.\"}}}
.\"{{{ Missing vi commands
.IP "Missing vi commands"
The following vi commands are in the original Korn shell, but are not
yet in pdksh:
.RS
.IP "\fBU\fP"
undo all changes that have been made to the current line.
.IP "\fB@\fP\fIc\fP"
macro expansion: execute the commands found in the alias _\fIc\fP.
.RE
.\"}}}
.\"}}}
.\"}}}
.\"}}}
.\"{{{ Files
.SH FILES
~/.profile
.br
/etc/profile
.br
/etc/suid_profile
.\"}}}
.\"{{{ Bugs
.SH BUGS
Any bugs in pdksh should be reported to [email protected]. Please
include the version of pdksh (echo $KSH_VERSION shows it), 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). The following, if relevant (if you aren't sure, include them),
are also helpful: options you are using (both options.h options and set
\-o options) and a copy of your config.h (the file generated by the
configure script). New versions of pdksh can be obtained from
ftp.cs.mun.ca:pub/.
.\"}}}
.\"{{{ Authors
.SH AUTHORS
pdksh is based on the public domain 7th edition Bourne shell by
Charles Forsyth and parts of the BRL shell by Doug A.\& Gwyn, Doug Kingston,
Ron Natalie, Arnold Robbins, Lou Salkind and others. The first release of
pdksh was created by Eric Gisin, and it was subsequently maintained by
John R.\& MacMillan ([email protected]), and
Simon J.\& Gerraty ([email protected]). The current maintainer is
Michael Rendell ([email protected]).
The CONTRIBUTORS file in the source distribution contains a more complete
list of people and their part in pdksh.
.\"}}}
.\"{{{ See also
.SH "SEE ALSO"
awk(1), sh(1), csh(1), ed(1), getconf(1), getopt(1), sed(1), stty(1), vi(1),
dup(2), execve(2), getgid(2), getuid(2), open(2), pipe(2), wait(2),
getopt(3), rand(3), signal(3), system(3),
environ(5)
.PP
.\" XXX ISBN missing
.I "UNIX Shell Programming,"
Stephen G.\& Kochan, Patrick H.\& Wood, Hayden.
.PP
.I "The KornShell Command and Programming Language,"
Morris Bolsky and David Korn, 1989, ISBN 0-13-516972-0.
.\"}}}
pdksh-5.1.2/Makefile.in100644 0 133 22176 5667422364 13562 0ustar rootlsourcesrcdir = @srcdir@
VPATH = @srcdir@

CC = @CC@
CPP = @CPP@

INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@

DEFS = @DEFS@
LIBS = @LIBS@

# configure.in sets GCC_WARNFLAGS to the contents of $(srcdir)/Warn-flags
# if gcc is being used (normally something like
# -Wall -Wno-comments -Dno_RCSids)
CFLAGS = -g -O @GCC_WARNFLAGS@
LDSTATIC = @LDSTATIC@
LDFLAGS = -g -O $(LDSTATIC)

prefix = /usr/local
exec_prefix = $(prefix)
binprefix =
manprefix =

bindir = $(exec_prefix)/bin
mandir = $(prefix)/man/man1
manext = 1

SHELL = /bin/sh

SRCS = alloc.c c_ksh.c c_sh.c c_test.c c_ulimit.c edit.c emacs.c \
eval.c exec.c expr.c history.c io.c jobs.c lex.c mail.c \
main.c misc.c missing.c path.c shf.c sigact.c syn.c table.c trap.c \
tree.c tty.c var.c version.c vi.c
OBJS = alloc.o c_ksh.o c_sh.o c_test.o c_ulimit.o edit.o emacs.o \
eval.o exec.o expr.o history.o io.o jobs.o lex.o mail.o \
main.o misc.o missing.o path.o shf.o sigact.o syn.o table.o trap.o \
tree.o tty.o var.o version.o vi.o
HDRS = edit.h expand.h ksh_dir.h ksh_limval.h ksh_stat.h ksh_time.h \
ksh_times.h ksh_wait.h lex.h options.h proto.h sh.h shf.h sigact.h \
table.h tree.h tty.h
RCSFILES = $(SRCS) $(HDRS) ksh.1 Makefile.in configure.in config.h.top \
config.h.bot config.h.in conf-end.h acconfig.h aclocal.m4 \
mkinstalldirs install.sh new-version.sh siglist.in siglist.sh \
check-fd.c check-pgrp.c check-sigs.c \
README NEWS CONTRIBUTORS PROJECTS
DISTFILES = $(RCSFILES) configure stamp-h.in Bugs BUG-REPORTS ChangeLog \
INSTALL NOTES
# ETCFILES also disted, but handled differently
ETCFILES = etc/ksh.kshrc etc/profile etc/sys_config.sh
# MISCFILES also disted, but handled differently
MISCFILES = misc/COPYING misc/ChangeLog.sjg misc/Changes.jrm misc/Changes.mlj \
misc/Changes.pc misc/README.sjg misc/ReadMe.eg misc/ReadMe.emacs \
misc/ReadMe.jrm
# OS2FILES also disted, but handled differently
OS2FILES = os2/Makefile os2/config.h os2/config.status os2/configure.cmd \
os2/kshrc.ksh os2/make.sed os2/os2.c os2/os2siglist.out \
os2/README.os2 os2/configure.save

.PRECIOUS: configure config.h.in Makefile config.status

# Suffix for executables: nothing for unix, .exe for os/2.
E=

all: ksh$E

.c.o:
$(CC) -c $(CPPFLAGS) $(DEFS) -I. -I$(srcdir) $(CFLAGS) $<

install: installdirs all $(srcdir)/ksh.1
$(INSTALL_PROGRAM) ksh $(bindir)/$(binprefix)ksh
-$(INSTALL_DATA) $(srcdir)/ksh.1 $(mandir)/$(manprefix)ksh.$(manext)
-@test -f /etc/shells \
&& (grep '^$(bindir)/$(binprefix)ksh$$' /etc/shells > /dev/null \
|| echo \
'NOTE: /etc/shells does not contain $(bindir)/$(binprefix)ksh \
you should add it if you want to set your shell to ksh')

installdirs:
$(srcdir)/mkinstalldirs $(bindir) $(mandir)

uninstall:
rm -f $(bindir)/$(binprefix)ksh
rm -f $(mandir)/$(manprefix)ksh.$(manext)

check:
ENV= ./ksh $(srcdir)/Bugs ./ksh

ksh$E: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

info:
@echo "No info (yet)"
dvi:

configure: configure.in aclocal.m4
cd $(srcdir) && autoconf

config.h.in: stamp-h.in
stamp-h.in: configure.in aclocal.m4 acconfig.h config.h.top config.h.bot
cd $(srcdir) && autoheader
touch $(srcdir)/stamp-h.in

config.h: stamp-h
stamp-h: config.h.in config.status
CONFIG_FILES="" CONFIG_HEADERS=config.h ./config.status
touch stamp-h

Makefile: Makefile.in config.status
CONFIG_FILES=Makefile CONFIG_HEADERS= ./config.status

config.status: configure
LDSTATIC="$(LDSTATIC)" ./config.status --recheck

# two steps to prevent the creation of a bogus siglist.out
siglist.out: config.h sh.h siglist.in siglist.sh
$(srcdir)/siglist.sh "$(CPP) $(CPPFLAGS) $(DEFS) -I. -I$(srcdir)" < $(srcdir)/siglist.in > tmpsiglist.out
mv tmpsiglist.out siglist.out

debugtools: check-fd$E check-pgrp$E check-sigs$E

check-fd.o check-pgrp.o check-sigs.o: config.h
check-fd$E: check-fd.o
$(CC) $(LDFLAGS) -o $@ check-fd.o $(LIBS)

check-pgrp$E: check-pgrp.o
$(CC) $(LDFLAGS) -o $@ check-pgrp.o $(LIBS)

check-sigs$E: check-sigs.o
$(CC) $(LDFLAGS) -o $@ check-sigs.o $(LIBS)

TAGS: $(SRCS) $(HDRS)
cd $(srcdir) && etags $(SRCS) $(HDRS)

tags: $(SRCS) $(HDRS)
cd $(srcdir) && ctags -wt $(SRCS) $(HDRS)

clean:
rm -f ksh$E $(OBJS) siglist.out core version.c.bak Makefile.bak \
Makefile.tmp check-fd$E check-pgrp$E check-sigs$E

mostlyclean: clean

distclean: clean
rm -f Makefile config.h stamp-h config.status tags TAGS

realclean: distclean

rcs-ci:
cd $(srcdir) && ci -l -q $(RCSFILES)

rcs-diff:
cd $(srcdir) && for i in $(RCSFILES); do \
rcsdiff -kkv -c $$i; \
done

dist: $(DISTFILES) $(ETCFILES) $(MISCFILES) $(OS2FILES)
cd $(srcdir) && \
{ \
sed -f os2/make.sed < Makefile.in > os2/Makefile; \
./new-version.sh; \
FNAME=pdksh-`sed -e '/"@(.)/!d' \
-e 's/[^0-9]*\([0-9.]*\).*/\1/' -e q version.c`; \
echo Creating version $$FNAME; \
rm -rf $$FNAME; \
mkdir $$FNAME $$FNAME/etc $$FNAME/misc $$FNAME/os2; \
cp -p $(DISTFILES) $$FNAME; \
cp -p $(ETCFILES) $$FNAME/etc; \
cp -p $(MISCFILES) $$FNAME/misc; \
cp -p $(OS2FILES) $$FNAME/os2; \
test -x ./Dist-fixup && ./Dist-fixup $$FNAME; \
chmod -R a+rX,u+w,og-w $$FNAME; \
tar chzf $$FNAME.tar.gz $$FNAME; \
find $$FNAME -print | xargs pathchk -p; \
}

depend: $(SRCS)
sed -n '1,/[ ]PUT ANYTHING BELOW THIS LINE/p' < Makefile > Makefile.tmp
srcs=; for i in $(SRCS) ; do srcs="$$srcs $(srcdir)/$$i"; done; \
$(CC) -M $(DEFS) -I. -I$(srcdir) $(CFLAGS) $$srcs | \
sed -e 's?[ ]/[^ ]*??g' -e 's?[ ]./? ?g' \
-e 's?[ ]$(srcdir)//*? ?g' -e 's?^$(srcdir)//*??' \
-e '/^[ ]*\\[ ]*$$/d' -e '/^[^:]*:[ ]*$$/d' \
-e 's/^\([ ]*\)$$/\1sh.h/' \
>> Makefile.tmp
mv Makefile.tmp Makefile
@echo 'Make depend done (stopping make)'; false

# DON'T PUT ANYTHING BELOW THIS LINE (and don't delete it - its for make depend)
alloc.o : alloc.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
c_ksh.o : c_ksh.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
c_sh.o : c_sh.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_time.h \
ksh_times.h
c_test.o : c_test.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h
c_ulimit.o : c_ulimit.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h \
sh.h
edit.o : edit.c config.h options.h conf-end.h sh.h \
shf.h table.h \
tree.h expand.h lex.h proto.h tty.h \
edit.h
emacs.o : emacs.c config.h options.h conf-end.h sh.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_dir.h \
edit.h
eval.o : eval.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h \
ksh_dir.h \
ksh_stat.h
exec.o : exec.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h \
ksh_stat.h
expr.o : expr.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
history.o : history.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h
io.o : io.c sh.h \
config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h
jobs.o : jobs.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_wait.h \
ksh_times.h tty.h \
sh.h
lex.o : lex.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
mail.o : mail.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_time.h \
sh.h
main.o : main.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_time.h \
sh.h
misc.o : misc.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
missing.o : missing.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_stat.h ksh_dir.h \
ksh_time.h \
ksh_times.h
path.o : path.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
shf.o : shf.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_limval.h
sigact.o : sigact.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
syn.o : syn.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
table.o : table.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
trap.o : trap.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h siglist.out
tree.o : tree.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h
tty.o : tty.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h tty.h \
sh.h
var.o : var.c sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_time.h \
ksh_limval.h \
ksh_stat.h
version.o : version.c
vi.o : vi.c config.h options.h conf-end.h sh.h \
shf.h table.h \
tree.h expand.h lex.h proto.h \
ksh_stat.h edit.h
subject.o : sh.h config.h options.h conf-end.h \
shf.h table.h \
tree.h expand.h lex.h proto.h ksh_time.h \

pdksh-5.1.2/configure.in100644 0 133 4017 5666705567 14010 0ustar rootlsourcednl Copyright (C) 1994, Memorial University of Newfoundland.
dnl This file is covered by the GNU General Public License, version 2, see
dnl the file misc/COPYING for details.
dnl
dnl
dnl Process this file with autoconf to produce a configure script
dnl
AC_INIT(c_ksh.c)
AC_CONFIG_HEADER(config.h)
dnl
dnl
dnl If LDSTATIC set in environment, pass it on to the Makefile and use it when
dnl doing compile checks to ensure we are checking the right thing.
AC_SUBST(LDSTATIC) LDSTATIC=${LDSTATIC-}
test X"$LDSTATIC" != X && LDFLAGS="$LDSTATIC $LDFLAGS"
dnl
AC_PROG_CC
AC_PROG_CPP
AC_GCC_TRADITIONAL
dnl A hack to turn on warning messages for gcc - Warn-flags is not in
dnl the distribution since not everyone wants to see this stuff and it
dnl compiles out the RCSid's
dnl (Warn-flags contains: -Wall -Wno-comments -Dno_RCSids)
AC_SUBST(GCC_WARNFLAGS) GCC_WARNFLAGS=
if test -n "$GCC" && test -f $srcdir/Warn-flags; then
GCC_WARNFLAGS="`cat $srcdir/Warn-flags`"
fi
AC_ISC_POSIX
AC_MINIX
dnl
dnl
dnl Headers
dnl
AC_DIR_HEADER
AC_XENIX_DIR
KSH_UNISTD_H
KSH_TERM_CHECK
AC_HAVE_HEADERS(stddef.h stdlib.h string.h memory.h \
fcntl.h limits.h paths.h values.h ulimit.h sys/time.h sys/wait.h)
AC_TIME_WITH_SYS_TIME
dnl
dnl
dnl Typedefs
dnl
AC_OFF_T
AC_MODE_T
AC_PID_T
AC_UID_T
KSH_INT32
KSH_CLOCK_T
KSH_TIME_T
KSH_SIGSET_T
AC_RETSIGTYPE
dnl
dnl
dnl Library functions
dnl
AC_HAVE_FUNCS(bcopy confstr getcwd killpg lstat memset memmove nice setrlimit \
strerror strcasecmp strstr sysconf tcsetpgrp ulimit waitpid wait3)
AC_MMAP
KSH_SYS_ERRLIST
KSH_SYS_SIGLIST
KSH_TIME_DECLARED
KSH_TIMES_CHECK
dnl
dnl
dnl Structures
dnl
AC_STAT_MACROS_BROKEN
AC_ST_RDEV
dnl
dnl
dnl Compiler characteristics
dnl
AC_CONST
KSH_VOID
KSH_VOLATILE
KSH_PROTOTYPES
dnl
dnl
dnl System services
dnl
AC_HAVE_POUNDBANG(AC_VERBOSE(it does),
AC_VERBOSE(it doesn't)AC_DEFINE(SHARPBANG))dnl
AC_PROG_INSTALL
dnl
dnl
dnl Misc ksh tests
dnl
KSH_DUP2_CLEXEC_CHECK
KSH_SIGNAL_CHECK
KSH_PGRP_CHECK
KSH_PGRP_SYNC
KSH_GCC_FUNC_ATTR
KSH_OPENDIR_CHECK
AC_UNION_WAIT
dnl
dnl
AC_OUTPUT(Makefile)
pdksh-5.1.2/config.h.top100644 0 133 612 5613542201 13643 0ustar rootlsource/* Copyright (C) 1994, Memorial University of Newfoundland.
* This file is covered by the GNU General Public License, version 2, see
* the file misc/COPYING for details.
*/
/*
* Top portion of the configuration file for the PD ksh
*
* RCSid: $Id: config.h.top,v 1.1 1994/05/31 13:34:34 michael Exp $
*/

#ifndef CONFIG_H
#define CONFIG_H

#include "options.h"

/* end of config.h.top */
pdksh-5.1.2/config.h.bot100644 0 133 747 5667214741 13654 0ustar rootlsource/* Copyright (C) 1994, Memorial University of Newfoundland.
* This file is covered by the GNU General Public License, version 2, see
* the file misc/COPYING for details.
*/
/*
* Bottom portion of the configuration file for the PD ksh
*
* RCSid: $Id: config.h.bot,v 1.1 1994/05/31 13:34:34 michael Exp $
*/

/* Need to use a separate file to keep the configure script from commenting
* out the undefs....
*/
#include "conf-end.h"

/* end of config.h.bot */
#endif /* CONFIG_H */
pdksh-5.1.2/config.h.in100644 0 133 16510 5667215030 13521 0ustar rootlsource/* config.h.in. Generated automatically from configure.in by autoheader. */
/* Copyright (C) 1994, Memorial University of Newfoundland.
* This file is covered by the GNU General Public License, version 2, see
* the file misc/COPYING for details.
*/
/*
* Top portion of the configuration file for the PD ksh
*
* RCSid: $Id: config.h.top,v 1.1 1994/05/31 13:34:34 michael Exp $
*/

#ifndef CONFIG_H
#define CONFIG_H

#include "options.h"

/* end of config.h.top */

/* 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 to empty if the keyword does not work. */
#undef const

/* Define if you have dirent.h. */
#undef DIRENT


/* Define to `int' if doesn't define. */
#undef gid_t

/* Define if you have a working `mmap' system call. */
#undef HAVE_MMAP

/* Define if your struct stat has st_rdev. */
#undef HAVE_ST_RDEV

/* Define if `union wait' is the type of the first arg to wait functions. */
#undef HAVE_UNION_WAIT

/* Define if you have unistd.h. */
#undef HAVE_UNISTD_H

/* Define if on MINIX. */
#undef _MINIX

/* Define to `int' if doesn't define. */
#undef mode_t

/* Define if you don't have dirent.h, but have ndir.h. */
#undef NDIR

/* Define to `long' if doesn't define. */
#undef off_t

/* Define to `int' if 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). */
#undef RETSIGTYPE

/* Define if the `S_IS*' macros in do not work properly. */
#undef STAT_MACROS_BROKEN

/* Define if you don't have dirent.h, but have sys/dir.h. */
#undef SYSDIR

/* Define if you don't have dirent.h, but have sys/ndir.h. */
#undef SYSNDIR

/* Define if `sys_siglist' is declared by . */
#undef SYS_SIGLIST_DECLARED

/* Define if you can safely include both and . */
#undef TIME_WITH_SYS_TIME

/* Define to `int' if doesn't define. */
#undef uid_t

/* Define if the closedir function returns void instead of int. */
#undef VOID_CLOSEDIR

/* Copyright (C) 1994, Memorial University of Newfoundland.
* This file is covered by the GNU General Public License, version 2, see
* the file misc/COPYING for details.
*/
/* Define if your kernal doesn't handle scripts starting with #! */
#undef SHARPBANG

/* Define if dup2() preserves the close-on-exec flag (ultrix does this) */
#undef DUP2_BROKEN

/* Define if you have posix signal routines (sigaction(), et. al.) */
#undef POSIX_SIGNALS

/* Define if you have BSD4.2 signal routines (sigsetmask(), et. al.) */
#undef BSD42_SIGNALS

/* Define if you have BSD4.1 signal routines (sigset(), et. al.) */
#undef BSD41_SIGNALS

/* Define if you have v7 signal routines (signal(), signal reset on delivery) */
#undef V7_SIGNALS

/* Define to use the fake posix signal routines (sigact.[ch]) */
#undef USE_FAKE_SIGACT

/* Define if you have bsd versions of the setpgrp() and getpgrp() routines */
#undef BSD_PGRP

/* Define if you have bsd versions of the setpgid() and getpgrp() routines */
#undef POSIX_PGRP

/* Define if you have sysV versions of the setpgrp() and getpgrp() routines */
#undef SYSV_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 to 32-bit signed integer type */
#undef INT32

/* Define to 32-bit signed integer type if doesn't define */
#undef clock_t

/* Define to 32-bit signed integer type if doesn't define */
#undef time_t

/* Define if time() is declared in */
#undef TIME_DECLARED

/* Define to `unsigned' if doesn't define */
#undef sigset_t

/* Define if sys_errlist[] and sys_nerr are in the C library */
#undef HAVE_SYS_ERRLIST

/* Define if sys_errlist[] and sys_nerr are defined in */
#undef SYS_ERRLIST_DECLARED

/* Define if sys_siglist[] is in the C library */
#undef HAVE_SYS_SIGLIST

/* Define if sys_siglist[] are defined in or */
#undef SYS_SIGLIST_DECLARED

/* Define if C compiler groks function prototypes */
#undef HAVE_PROTOTYPES

/* Define if C compiler groks __attribute__((...)) (const, noreturn, format) */
#undef HAVE_GCC_FUNC_ATTR

/* Define if you have a sane header file */
#undef HAVE_UNISTD_H

/* Define if you have a sane header file */
#undef HAVE_TERMIOS_H

/* Define if you don't have times() or if it always returns 0 */
#undef TIMES_BROKEN

/* Define if opendir() will open non-directory files */
#undef OPENDIR_DOES_NONDIR

/* Define if the pgrp of setpgrp() can't be the pid of a zombie process */
#undef NEED_PGRP_SYNC

/* Define if clock_t is not defined in but in */
#undef CLOCK_T_IN_SYS_TIMES_H

/* Define if you have bcopy. */
#undef HAVE_BCOPY

/* Define if you have confstr. */
#undef HAVE_CONFSTR

/* Define if you have getcwd. */
#undef HAVE_GETCWD

/* Define if you have getrusage. */
#undef HAVE_GETRUSAGE

/* Define if you have killpg. */
#undef HAVE_KILLPG

/* Define if you have lstat. */
#undef HAVE_LSTAT

/* Define if you have memmove. */
#undef HAVE_MEMMOVE

/* Define if you have memset. */
#undef HAVE_MEMSET

/* Define if you have nice. */
#undef HAVE_NICE

/* Define if you have setrlimit. */
#undef HAVE_SETRLIMIT

/* Define if you have strcasecmp. */
#undef HAVE_STRCASECMP

/* Define if you have strerror. */
#undef HAVE_STRERROR

/* Define if you have strstr. */
#undef HAVE_STRSTR

/* Define if you have sysconf. */
#undef HAVE_SYSCONF

/* Define if you have tcsetpgrp. */
#undef HAVE_TCSETPGRP

/* Define if you have ulimit. */
#undef HAVE_ULIMIT

/* Define if you have wait3. */
#undef HAVE_WAIT3

/* Define if you have waitpid. */
#undef HAVE_WAITPID

/* Define if you have the header file. */
#undef HAVE_FCNTL_H

/* Define if you have the header file. */
#undef HAVE_LIMITS_H

/* Define if you have the header file. */
#undef HAVE_MEMORY_H

/* Define if you have the header file. */
#undef HAVE_PATHS_H

/* Define if you have the header file. */
#undef HAVE_STDDEF_H

/* Define if you have the header file. */
#undef HAVE_STDLIB_H

/* Define if you have the header file. */
#undef HAVE_STRING_H

/* Define if you have the header file. */
#undef HAVE_SYS_TIME_H

/* Define if you have the header file. */
#undef HAVE_SYS_WAIT_H

/* Define if you have the header file. */
#undef HAVE_TERMIO_H

/* Define if you have the header file. */
#undef HAVE_ULIMIT_H

/* Define if you have the header file. */
#undef HAVE_VALUES_H
/* Copyright (C) 1994, Memorial University of Newfoundland.
* This file is covered by the GNU General Public License, version 2, see
* the file misc/COPYING for details.
*/
/*
* Bottom portion of the configuration file for the PD ksh
*
* RCSid: $Id: config.h.bot,v 1.1 1994/05/31 13:34:34 michael Exp $
*/

/* Need to use a separate file to keep the configure script from commenting
* out the undefs....
*/
#include "conf-end.h"

/* end of config.h.bot */
#endif /* CONFIG_H */
pdksh-5.1.2/conf-end.h100644 0 133 3165 5667224611 13327 0ustar rootlsource/*
* End of configuration stuff for PD ksh.
*
* RCSid: $Id$
*/

/* No ksh features means no editing, history or brace expansion... */
#if !defined(KSH)
# undef EMACS
# undef VI
# undef COMPLEX_HISTORY
# undef EASY_HISTORY
# undef HISTORY
#else
# define HISTORY
#endif /* !KSH */

/*
* if you don't have mmap() you can't use Peter Collinson's history
* mechanism. If that is the case, then define EASY_HISTORY
*/
#if defined(HISTORY) && (!defined(COMPLEX_HISTORY) || !defined(HAVE_MMAP))
# undef COMPLEX_HISTORY
# define EASY_HISTORY /* sjg's trivial history file */
#endif

/* Can we safely catch sigchld and wait for processes? */
#if (defined(HAVE_WAITPID) || defined(HAVE_WAIT3)) \
&& (defined(POSIX_SIGNALS) || defined(BSD42_SIGNALS))
# define JOB_SIGS
#else
# undef JOBS /* if no JOB_SIGS, no job control support */
#endif

/* pdksh assumes system calls return EINTR if a signal happened (this so
* the signal handler doesn't have to longjmp()). I don't know if this
* happens (or can be made to happen) with sigset() et. al., so, to be on
* the safe side, make sure a bogus shell doesn't get compiled.
* If BSD41_SIGNALS isn't defined and your compiler chokes on this, delete
* the hash in front of the error (and file a bug report).
*/
#ifdef BSD41_SIGNALS
# error pdksh needs interruptable system calls.
#endif /* BSD41_SIGNALS */

#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 */

#if defined(EMACS) || defined(VI)
# define EDIT
#else
# undef EDIT
#endif
pdksh-5.1.2/acconfig.h100644 0 133 5256 5653562422 13412 0ustar rootlsource/* Copyright (C) 1994, Memorial University of Newfoundland.
* This file is covered by the GNU General Public License, version 2, see
* the file misc/COPYING for details.
*/
/* Define if your kernal doesn't handle scripts starting with #! */
#undef SHARPBANG

/* Define if dup2() preserves the close-on-exec flag (ultrix does this) */
#undef DUP2_BROKEN

/* Define if you have posix signal routines (sigaction(), et. al.) */
#undef POSIX_SIGNALS

/* Define if you have BSD4.2 signal routines (sigsetmask(), et. al.) */
#undef BSD42_SIGNALS

/* Define if you have BSD4.1 signal routines (sigset(), et. al.) */
#undef BSD41_SIGNALS

/* Define if you have v7 signal routines (signal(), signal reset on delivery) */
#undef V7_SIGNALS

/* Define to use the fake posix signal routines (sigact.[ch]) */
#undef USE_FAKE_SIGACT

/* Define if you have bsd versions of the setpgrp() and getpgrp() routines */
#undef BSD_PGRP

/* Define if you have bsd versions of the setpgid() and getpgrp() routines */
#undef POSIX_PGRP

/* Define if you have sysV versions of the setpgrp() and getpgrp() routines */
#undef SYSV_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 to 32-bit signed integer type */
#undef INT32

/* Define to 32-bit signed integer type if doesn't define */
#undef clock_t

/* Define to 32-bit signed integer type if doesn't define */
#undef time_t

/* Define if time() is declared in */
#undef TIME_DECLARED

/* Define to `unsigned' if doesn't define */
#undef sigset_t

/* Define if sys_errlist[] and sys_nerr are in the C library */
#undef HAVE_SYS_ERRLIST

/* Define if sys_errlist[] and sys_nerr are defined in */
#undef SYS_ERRLIST_DECLARED

/* Define if sys_siglist[] is in the C library */
#undef HAVE_SYS_SIGLIST

/* Define if sys_siglist[] are defined in or */
#undef SYS_SIGLIST_DECLARED

/* Define if C compiler groks function prototypes */
#undef HAVE_PROTOTYPES

/* Define if C compiler groks __attribute__((...)) (const, noreturn, format) */
#undef HAVE_GCC_FUNC_ATTR

/* Define if you have a sane header file */
#undef HAVE_UNISTD_H

/* Define if you have a sane header file */
#undef HAVE_TERMIOS_H

/* Define if you don't have times() or if it always returns 0 */
#undef TIMES_BROKEN

/* Define if opendir() will open non-directory files */
#undef OPENDIR_DOES_NONDIR

/* Define if the pgrp of setpgrp() can't be the pid of a zombie process */
#undef NEED_PGRP_SYNC

/* Define if clock_t is not defined in but in */
#undef CLOCK_T_IN_SYS_TIMES_H
pdksh-5.1.2/aclocal.m4100644 0 133 41754 5664200055 13344 0ustar rootlsourcednl Copyright (C) 1994, Memorial University of Newfoundland.
dnl This file is covered by the GNU General Public License, version 2, see
dnl the file misc/COPYING for details.
dnl
dnl
dnl Ksh specific tests
dnl
dnl
dnl Check if dup2() does not clear the close on exec flag
define(KSH_DUP2_CLEXEC_CHECK,
[AC_PROVIDE([$0])AC_CHECKING(if dup2() fails to reset the close-on-exec flag)
AC_TEST_PROGRAM([
#include
#ifdef HAVE_FCNTL_H
# include
#endif /* HAVE_FCNTL_H */

#ifndef F_GETFD
# define F_GETFD 1
#endif
#ifndef F_SETFD
# define F_SETFD 2
#endif
#ifndef O_RDONLY
# define O_RDONLY 0
#endif

/* On some systems (Ultrix 2.1..4.2 (and more?)), dup2() does not clear
the close on exec flag */
main()
{
int fd1, fd2;
fd1 = open("/dev/null", O_RDONLY);
if (fcntl(fd1, F_SETFD, 1) < 0)
exit(1);
fd2 = dup2(fd1, fd1 + 1);
if (fd2 < 0)
exit(2);
exit(fcntl(fd2, F_GETFD, 0) == 0 ? 0 : 3);
}
], , AC_DEFINE(DUP2_BROKEN))])dnl
dnl
dnl Check type of signal routines (posix, 4.2bsd, 4.1bsd or v7)
define(KSH_SIGNAL_CHECK,
[AC_REQUIRE([AC_RETSIGTYPE])AC_PROVIDE([$0])dnl
AC_COMPILE_CHECK(posix signals, [#include ],[
sigset_t ss;
struct sigaction sa;
sigemptyset(&ss); sigsuspend(&ss);
sigaction(SIGINT, &sa, (struct sigaction *) 0);
sigprocmask(SIG_BLOCK, &ss, (sigset_t *) 0);
], AC_DEFINE(POSIX_SIGNALS), AC_DEFINE(USE_FAKE_SIGACT) dnl
AC_COMPILE_CHECK(BSD4.2 signals, [#include ], [
int mask = sigmask(SIGINT);
sigsetmask(mask); sigblock(mask); sigpause(mask);
], AC_DEFINE(BSD42_SIGNALS),
AC_COMPILE_CHECK(BSD4.1 signals, [
#include
RETSIGTYPE foo() { }
], [
int mask = sigmask(SIGINT);
sigset(SIGINT, foo); sigrelse(SIGINT); sighold(SIGINT); sigpause(SIGINT);
], AC_DEFINE(BSD41_SIGNALS), AC_DEFINE(V7_SIGNALS))))])dnl
dnl
dnl What kind of process groups: POSIX, BSD, or SYSV
dnl BSD uses setpgrp(pid, pgrp), getpgrp(pid)
dnl POSIX uses setpid(pid, pgrp), getpgrp(void)
dnl SYSV uses setpgrp(void), getpgrp(void)
dnl Checks for BSD first since the posix test may succeed on BSDish systems
dnl (depends on what random value gets passed to getpgrp()).
define(KSH_PGRP_CHECK,
[AC_REQUIRE([KSH_UNISTD_H])AC_PROVIDE([$0])dnl
AC_CHECKING(for POSIX vs BSD vs SYSV setpgrp()/getpgrp())
AC_TEST_PROGRAM([
#include
#ifdef HAVE_UNISTD_H
# include
#endif /* HAVE_UNISTD_H */

main()
{
int ecode = 0, child = fork();
if (child < 0)
exit(1);
if (child == 0) {
signal(SIGTERM, SIG_DFL); /* just to make sure */
sleep(10);
exit(9);
}
if (setpgrp(child, child) < 0)
ecode = 2;
else if (getpgrp(child) != child)
ecode = 3;
kill(child, SIGTERM);
exit(ecode);
}
], AC_DEFINE(BSD_PGRP),
AC_TEST_PROGRAM([[
#ifdef HAVE_UNISTD_H
# include
#endif /* HAVE_UNISTD_H */

main()
{
int child;
int n, p1[2], p2[2];
char buf[1];

if (pipe(p1) < 0 || pipe(p2) < 0)
exit(1);
if ((child = fork()) < 0)
exit(2);
if (child == 0) {
n = read(p1[0], buf, sizeof(buf)); /* wait for parent to setpgid */
buf[0] = (n != 1 ? 10 : (getpgrp() != getpid() ? 11 : 0));
if (write(p2[1], buf, sizeof(buf)) != 1)
exit(12);
exit(0);
}
if (setpgid(child, child) < 0)
exit(3);
if (write(p1[1], buf, 1) != 1)
exit(4);
if (read(p2[0], buf, 1) != 1)
exit(5);
exit((int) buf[0]);
}
]], AC_DEFINE(POSIX_PGRP),
AC_TEST_PROGRAM([[[
#ifdef HAVE_UNISTD_H
# include
#endif /* HAVE_UNISTD_H */

main()
{
int child;
int n, p[2];
char buf[1];

if (pipe(p) < 0)
exit(1);
if ((child = fork()) < 0)
exit(2);
if (child == 0) {
buf[0] = (setpgrp() < 0 ? 10 : (getpgrp() != getpid() ? 11 : 0));
if (write(p[1], buf, sizeof(buf)) != 1)
exit(11);
exit(0);
}
if (read(p[0], buf, 1) != 1)
exit(3);
exit((int) buf[0]);
}
]]], AC_DEFINE(SYSV_PGRP))))])dnl
dnl
dnl
dnl Check if the pgrp of setpgrp() can't be the pid of a zombie process.
dnl On some systems, the kernel doesn't count zombie processes when checking
dnl if a process group is valid, which can cause problems in creating the
dnl pipeline "cmd1 | cmd2": if cmd1 can die (and go into the zombie state)
dnl before cmd2 is started, the kernel doesn't allow the setpgrp() for cmd2
dnl to succeed. This test defines NEED_PGRP_SYNC if the kernel has this bug.
define(KSH_PGRP_SYNC,
[AC_PROVIDE([$0])AC_REQUIRE([KSH_PGRP_CHECK])dnl
AC_CHECKING(if process group synchronization is required)
AC_TEST_PROGRAM([
main()
{
#if defined(POSIX_PGRP) || defined(BSD_PGRP)
# ifdef POSIX_PGRP
# define getpgID() getpgrp()
# else
# define getpgID() getpgrp(0)
# define setpgid(x,y) setpgrp(x,y)
# endif
int pid1, pid2, fds[2];
int status;
char ok;

switch (pid1 = fork()) {
case -1:
exit(1);
case 0:
setpgid(0, getpid());
exit(0);
}
setpgid(pid1, pid1);

sleep(2); /* let first child die */

if (pipe(fds) < 0)
exit(2);

switch (pid2 = fork()) {
case -1:
exit(3);
case 0:
setpgid(0, pid1);
ok = getpgID() == pid1;
write(fds[1], &ok, 1);
exit(0);
}
setpgid(pid2, pid1);

close(fds[1]);
if (read(fds[0], &ok, 1) != 1)
exit(4);
wait(&status);
wait(&status);
exit(ok ? 0 : 5);
#else /* POSIX_PGRP || BSD_PGRP */
exit(20);
#endif /* POSIX_PGRP || BSD_PGRP */
}
],,AC_DEFINE(NEED_PGRP_SYNC))])dnl
dnl
dnl
dnl
dnl Somewhat generic tests that aren't in autoconf, but perhaps should be
dnl
dnl Like AC_HAVE_HEADER(unistd.h) but only defines HAVE_UNISTD_H if
dnl the header file is sane (MIPS RISC/os 5.0 (and later?) bas a unistd.h
dnl in the bsd43 environ that is incorrect - it defines POSIX_VERSION even
dnl though its non-posix).
define(KSH_UNISTD_H,
[AC_PROVIDE([$0])dnl
AC_COMPILE_CHECK(sane unistd.h, [
#include
#ifdef _POSIX_VERSION
# include
#endif
], , AC_DEFINE(HAVE_UNISTD_H))])dnl
dnl
dnl Some systems (eg, SunOS 4.0.3) have and but don't
dnl have the related functions/defines (eg, tcsetattr(), TCSADRAIN, etc.)
dnl or the functions don't work well with tty process groups. Sun's bad
dnl termios can be detected by the lack of tcsetattr(), but its bad termio
dnl is harder to detect - so check for (sane) termios first, then check for
dnl BSD, then termio.
define(KSH_TERM_CHECK,
[AC_PROVIDE([$0])dnl
AC_COMPILE_CHECK(sane termios.h, [#include ],
[struct termios t;
#if defined(ultrix) || defined(__ultrix__)
Termios in ultrix 4.2 botches type-ahead when going from cooked to
cbreak mode. The BSD tty interface works fine though, so use it
(would be good to know if alter versions of ultrix work).
#endif /* ultrix */
tcgetattr(0, &t); tcsetattr(0, TCSADRAIN, &t);
],
AC_DEFINE(HAVE_TERMIOS_H),
AC_COMPILE_CHECK(BSD tty interface, [#include ],
[
struct sgttyb sb; ioctl(0, TIOCGETP, &sb);
#ifdef TIOCGATC
{ struct ttychars lc; ioctl(0, TIOCGATC, &lc); }
#else /* TIOCGATC */
{ struct tchars tc; ioctl(0, TIOCGETC, &tc); }
# ifdef TIOCGLTC
{ struct ltchars ltc; ioctl(0, TIOCGLTC, <c); }
# endif /* TIOCGLTC */
#endif /* TIOCGATC */
], , [[AC_HAVE_HEADERS(termio.h)]]))])dnl
dnl
define(KSH_VOID,
[AC_PROVIDE([$0])dnl
AC_COMPILE_CHECK(lack of working void, [
void foo() { }
/* Some compilers (old pcc ones) like "void *a;", but a can't be used */
void *bar(a) void *a; { int *b = (int *) a; *b = 1; }
], , , AC_DEFINE(void,char))])dnl
dnl
dnl Early MIPS compilers (used in Ultrix 4.2) don't like
dnl "int x; int *volatile a = &x; *a = 0;"
define(KSH_VOLATILE,
[AC_PROVIDE([$0])dnl
AC_COMPILE_CHECK(lack of working volatile, [int x, y, z;],
[volatile int a; int * volatile b = x ? &y : &z;
/* Older MIPS compilers (eg., in Ultrix 4.2) don't like *b = 0 */
*b = 0;], ,
AC_DEFINE(volatile,))])dnl
dnl
dnl Check if function prototypes work (including stdc vararg prototypes)
define(KSH_PROTOTYPES,
[AC_PROVIDE([$0])dnl
AC_COMPILE_CHECK(working function prototypes, [
#include
void foo(char *fmt, ...);
int bar(int a, char b, char *c);
int bar(a, b, c) int a; char b; char *c;
{ foo("%d%c%s\n", a, b, c); return a + b + *c; }
void foo(char *fmt, ...) { va_list a; va_start(a, fmt); va_end(a); }
], , AC_DEFINE(HAVE_PROTOTYPES))])dnl
dnl
dnl Figure out what integer type has 32 bits (assuming 8 bit bytes)
define(KSH_INT32,
[AC_PROVIDE([$0])AC_CHECKING(for signed 32 bit integer type)
AC_TEST_PROGRAM([main() { if (sizeof(long) != 4) exit(1); exit(0); }],
AC_DEFINE(INT32, long), AC_DEFINE(INT32, int))])dnl
dnl
define(KSH_CLOCK_T,
[AC_REQUIRE([KSH_INT32])AC_PROVIDE([$0])AC_CHECKING(for clock_t in sys/types.h)
AC_HEADER_EGREP([[(^|[^a-zA-Z0-9_])clock_t([^a-zA-Z0-9_]|\$)]], sys/types.h, ,
AC_HEADER_EGREP([[(^|[^a-zA-Z0-9_])clock_t([^a-zA-Z0-9_]|\$)]], sys/times.h,
AC_DEFINE(CLOCK_T_IN_SYS_TIMES_H), AC_DEFINE(clock_t, INT32)))])dnl
dnl
define(KSH_TIME_T,
[AC_REQUIRE([KSH_INT32])AC_PROVIDE([$0])AC_CHECKING(for time_t in sys/types.h)
AC_HEADER_EGREP([[(^|[^a-zA-Z0-9_])time_t([^a-zA-Z0-9_]|\$)]], sys/types.h, ,
AC_DEFINE(time_t, INT32))])dnl
dnl
define(KSH_SIGSET_T,
[AC_PROVIDE([$0])AC_CHECKING(for sigset_t in signal.h)
AC_HEADER_EGREP([[(^|[^a-zA-Z0-9_])sigset_t([^a-zA-Z0-9_]|\$)]], signal.h, ,
AC_DEFINE(sigset_t, unsigned))])dnl
dnl
define(KSH_TIME_DECLARED,
[AC_REQUIRE([KSH_TIME_T])AC_PROVIDE([$0])dnl
AC_COMPILE_CHECK(time() declaration in time.h, [#include ],
[time_t (*f)() = time;], AC_DEFINE(TIME_DECLARED))])dnl
dnl
dnl Check for sys_errlist[] and sys_nerr, check for declaration
define(KSH_SYS_ERRLIST,
[AC_PROVIDE([$0])dnl
AC_COMPILE_CHECK(sys_errlist declaration in errno.h,
[#include ], [char *msg = *(sys_errlist + sys_nerr - 1);],
AC_DEFINE(HAVE_SYS_ERRLIST) AC_DEFINE(SYS_ERRLIST_DECLARED),
AC_COMPILE_CHECK(sys_errlist, [#include ], [[
extern char *sys_errlist[];
extern int sys_nerr;
char *p;
p = sys_errlist[sys_nerr - 1];
]], AC_DEFINE(HAVE_SYS_ERRLIST)))])dnl
dnl
dnl Check for sys_siglist[], check for declaration
define(KSH_SYS_SIGLIST,
[AC_PROVIDE([$0])dnl
AC_COMPILE_CHECK(sys_siglist declaration in signal.h or unistd.h,
[
#include
#ifdef HAVE_UNISTD_H
# include
#endif
], [char *msg = sys_siglist[1];],
AC_DEFINE(HAVE_SYS_SIGLIST) AC_DEFINE(SYS_SIGLIST_DECLARED),
AC_COMPILE_CHECK(sys_siglist, [
#include
#ifdef HAVE_UNISTD_H
# include
#endif
], [[
extern char *sys_siglist[];
char *p = sys_siglist[2];
if (p)
return 12;
]], AC_DEFINE(HAVE_SYS_SIGLIST)))])dnl
dnl
dnl Check if C compiler understands gcc's __attribute((...)).
dnl checks for noreturn, const, and format(type,fmt,param), also checks
dnl that the compiler doesn't die when it sees an unknown attribute (this
dnl isn't perfect since gcc doesn't parse unknown attributes with parameters)
define(KSH_GCC_FUNC_ATTR,
[AC_PROVIDE([$0])AC_CHECKING(if C compiler groks __attribute__(( .. )))
AC_COMPILE_CHECK(, [
#include
void test_fmt(char *fmt, ...) __attribute__((format(printf, 1, 2)));
void test_fmt(char *fmt, ...) { return; }
void test_cnst(int) __attribute__((const));
void test_cnst(int x) { return x + 1; }
void test_nr() __attribute__((noreturn));
void test_nr() { exit(1); }
void test_uk() __attribute__((blah));
void test_uk() { return; }
], [test_nr("%d", 10); test_cnst(2); test_uk(); test_nr();],
AC_DEFINE(HAVE_GCC_FUNC_ATTR))])dnl
dnl
dnl
dnl Check for working times (ie, it exists and doesn't always return 0).
dnl Defines TIMES_BROKEN if it doesn't exist or if it always returns 0
dnl (also checks for existance of getrusage if times doesn't work).
define(KSH_TIMES_CHECK,
[AC_PROVIDE([$0])AC_REQUIRE([KSH_CLOCK_T])dnl
AC_CHECKING(for broken/missing times())
AC_TEST_PROGRAM([
#include
#include

main()
{
extern clock_t times();
struct tms tms;
times(&tms);
sleep(1);
if (times(&tms) == 0)
exit(1);
exit(0);
}
],,AC_DEFINE(TIMES_BROKEN)[AC_HAVE_FUNCS(getrusage)])])dnl
dnl
dnl
dnl Check to see if opendir will open non-directories (not a nice thing)
define(KSH_OPENDIR_CHECK,
[AC_PROVIDE([$0])AC_REQUIRE([AC_DIR_HEADER])dnl
AC_CHECKING(if opendir() opens non-directories)
AC_TEST_PROGRAM([
#include
#include
#ifdef HAVE_UNISTD_H
# include
#endif /* HAVE_UNISTD_H */
#if defined(DIRENT) || defined(_POSIX_VERSION)
# include
#else
# define dirent direct
# ifdef SYSNDIR
# include
# endif /* SYSNDIR */
# ifdef SYSDIR
# include
# endif /* SYSDIR */
# ifdef NDIR
# include
# endif /* NDIR */
#endif /* DIRENT || _POSIX_VERSION */

main()
{
int i, ret = 0;
FILE *fp;
char