home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume25
/
pdksh
/
part02
< prev
next >
Wrap
Text File
|
1991-11-12
|
56KB
|
2,072 lines
Newsgroups: comp.sources.misc
From: sjg@zen.void.oz.au (Simon J. Gerraty)
Subject: v25i048: pdksh - Public Domain Korn Shell, v4, Part02/09
Message-ID: <1991Nov13.031036.15763@sparky.imd.sterling.com>
X-Md4-Signature: 7c582cc4d925bcf5e285068e383734fd
Date: Wed, 13 Nov 1991 03:10:36 GMT
Approved: kent@sparky.imd.sterling.com
Submitted-by: sjg@zen.void.oz.au (Simon J. Gerraty)
Posting-number: Volume 25, Issue 48
Archive-name: pdksh/part02
Environment: UNIX
#! /bin/sh
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents: ksh.1 sh/jobs.c sh/trace.c
# Wrapped by kent@sparky on Tue Nov 12 20:44:32 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 2 (of 9)."'
if test -f 'ksh.1' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'ksh.1'\"
else
echo shar: Extracting \"'ksh.1'\" \(31646 characters\)
sed "s/^X//" >'ksh.1' <<'END_OF_FILE'
X.\" $Header: /usr/local/src/nksh/sh/RCS/ksh.1,v 3.1 88/11/22 10:44:17 egisin Exp $
X.nr OJ 1 \" Job Control
X.nr OE 1 \" Command Editing
X.nr OB 1 \" BSD enhanced ulimit options
X.ds OK [\|
X.ds CK \|]
X.TH KSH 1 "January 1988"
X.SH NAME
Xksh \- Bourne / Korn Shell (Public Domain)
X.SH SYNOPSIS
X\fBksh\fP
X[\fB\-st\fP] [\fB\-c\fP \fIcommand\fP]
X[\fIfile\fP [\fIargument ...\fP]]
X.SH INTRODUCTION
XThis document only summarizes the System V, release 2 shell features.
XAll of the System V features except for ``restricted mode''
Xare implemented.
XSee also the BUGS section.
X.LP
XFeatures of the Korn shell are described in more detail.
XOnly a subset of the Korn shell features are currently implemented.
X.SH DESCRIPTION
X.SS Command syntax
XThe ``#'' character begins a one-line comment,
Xunless the ``#'' occurs inside a word.
XThe tokens ``;'', ``|'', ``&'', ``;;'', ``||'', ``&&'', ``('', and ``)''
Xstand by themselves.
XA \fIword\fP is a sequence of any other non-whitespace characters,
Xwhich may also contain quoted strings
X(quote character are ``\''', ``"'', ``\`'',
Xor a matching ``${ }'' or ``$( )'' pair).
XA \fIname\fP is an unquoted word made up of letters, digits, or ``_''.
XAny number of whitespace characters (space and tab) may separate words and tokens.
X.LP
XIn the following syntax, { ... }? indicates an optional thing,
X{ ... }* indicates zero or more repetitions, { ... | ... } indicates alternatives.
X.de S
X.br
X\\$1
X.br
X..
X.IP statement:
X.S "\fB(\fP list \fB)\fP"
X.S "\fB{\fP list \fB;\fP \fB}\fP"
X.S "\fBfor\fP name { \fBin\fP { word }* }? \fBdo\fP list \fB;\fP \fBdone\fP"
X.S "{ \fBwhile\fP | \fBuntil\fP } list \fB;\fP \fBdo\fP list \fB;\fP \fBdone\fP"
X.S "\fBif\fP list \fB;\fP \fBthen\fP list \fB;\fP { \fBelif\fP list \fB;\fP \fBthen\fP list \fB;\fP }* { \fBelse\fP list \fB;\fP }?\fBfi\fP"
X.S "\fBcase\fP name \fBin\fP { \fB(\fP word { \fB|\fP word } \fB)\fP list \fB;;\fP }* \fBesac\fP"
X.S "\fBfunction\fP name \fB{\fP list \fB;\fP \fB}\fP"
X.S "name \fB() {\fP list \fB;\fP \fB}\fP"
X.S "\fBtime\fP pipe"
XThe opening parenthesis of the pattern is optional.
XRedirection may occur at the beginning or end of a statement.
X.IP command:
X.S "{ name=word }* { word }*"
XRedirection may occur anywhere in a command.
X.IP list:
X.S "cond"
X.S "cond \fB;\fP list"
X.S "cond \fB&\fP list"
X.IP cond:
X.S "pipe"
X.S "pipe \fB&&\fP cond"
X.S "pipe \fB||\fP cond"
X.IP pipe:
X.S "statement { \fB|\fP statement }*"
X.SS Alias expansion
XAlias expansion occurs when the first word of a
Xstatement is a defined alias,
Xexcept when that alias is already being expanded.
XIt also occurs after the expansion of an alias whose
Xdefinition ends with a space.
X.SS Shell variables
XThe following standard special variables exist:
X\fB!\fP, \fB#\fP, \fB$\fP, \fB\-\fP, \fB?\fP.
X.IP "_"
XIn interactive use this parameter is set to the last word of
Xthe previous command. When a command is executed this parameter
Xis set to the full path of the command and placed in the environment
Xfor the command. See also \fBMAILPATH\fP.
X.IP CDPATH
XThe search path for the \fIcd\fP command.
X.IP ENV
XIf this variable is set at start-up
X(after any profile files are executed),
Xthe expanded value is used as shell start-up file.
XIt typically contains function and alias definitions.
X.IP FCEDIT
XThe editor used by the \fIfc\fP command.
X.IP IFS
X\fIInternal field separator\fP,
Xused during substitution and the \fIread\fP command.
X.IP HOME
XThe default directory for the \fIcd\fP command.
X.IP MAIL
XIf set, the user will be informed of the arrival of mail
Xin the named file. This variable is ignored if
Xthe \fBMAILPATH\fP variable is set.
X.IP MAILCHECK
XHow often, in seconds, the shell will check for mail in the
Xfile(s) specified by \fBMAIL\fP or \fBMAILPATH\fP. If 0,
Xthe shell checks before each prompt. The default is 600
Xseconds.
X.IP MAILPATH
XA list of files to be checked for mail. The list is colon
Xseparated, and each file may be followed by a \fB?\fP and
Xa message to be printed if new mail has arrived. Command
Xand parameter substitution is performed on the message, and
Xthe parameter \fB$_\fP is set to the name of the file.
XThe default message is ``you have mail in $_''.
X.IP PATH
XThe search path for executable commands and \fB.\fP'd files.
X.IP PPID
XThe process number of the parent of the shell.
X.IP "PS1 PS2"
X\fBPS1\fP is the primary prompt for interactive shells.
XDollar substitution is performed, and \fB!\fP is replaced
Xwith the command number (see \fIfc\fP).
X.IP "PWD OLDPWD"
XThe current and previous working directories.
X.IP RANDOM
XA random integer. The random number generator may be seeded
Xby assigning an integer value to this variable.
X.IP SECONDS
XThe number of seconds since the shell timer was started or
Xreset. Assigning an integer value to this variable resets
Xthe timer.
X.SS Substitution
XIn addition to the System Vr2 substitutions,
Xthe following are available.
X.IP "$(command)"
XLike `command`, but no escapes are recognized.
X.IP "$(<file)"
XEquivalent to $(cat file), but without forking.
X.IP "${#var}"
XThe length of the string value of \fIvar\fP,
Xor the number of arguments if \fIvar\fP is \fB*\fP or \fB@\fP.
X.IP "${var#pattern} ${var##pattern}"
XIf \fIpattern\fP matches the beginning of the value of \fIvar\fP,
Xthe matched text is deleted from the result of substitution.
XA single \fB#\fP results in the shortest match,
Xtwo \fB#\fP's results in the longest match.
X.IP "${var%pattern} ${var%%pattern}"
XLike \fB#\fP substition, but deleting from the end of the value.
X.SS Expressions
XExpressions can be used with the \fBlet\fP command,
Xas numeric arguments to the \fBtest\fP command,
Xand as the value of an assignment to an integer variable.
X.LP
XExpression may contain alpha-numeric variable identifiers and integer constants
Xand may be combined with the following operators:
X.IP "== != <= < > >= + - * / % ! ( )"
X.SS Command execution
XAfter evaluation of keyword assignments and arguments,
Xthe type of command is determined.
XA command may execute a shell function, a shell built-in,
Xor an executable file.
X.LP
XAny keyword assignments are then performed according to
Xthe type of command.
XIn function calls assignments are local to the function.
XAssignments in built-in commands marked with a \(dg persist,
Xotherwise they are temporary.
XAssignments in executable commands are exported to the sub-process
Xexecuting the command.
X.LP
XEven on systems where the exec() family does not support #!
Xnotation for scripts, ksh can be configured to fake it.
X.LP
XThere are several built-in commands.
X.IP ":"
XOnly expansion and assignment are performed.
XThis is the default if a command has no arguments.
X.IP ". \fIfile\fP"
XExecute the commands in \fIfile\fP without forking.
XThe file is searched in the directories of $PATH.
XPassing arguments is not implemented.
X.IP "alias [\fIname\fB=\fIvalue\fI ...]\fR"
XWithout arguments, \fBalias\fP lists all aliases and their values.
XFor any name without a value, its value is listed.
XAny name with a value defines an alias, see "Alias Expansion" above.
XKorn's tracked aliases are not implemented,
Xbut System V command hashing is (see "hash").
X.IP "alias -d [\fIname\fB=\fIvalue\fI ...]\fR"
XDirectory aliases for tilde expansion, eg.
X.br
Xalias -d fac=/usr/local/usr/facilities
X.br
Xcd ~fac/bin
X.IP "break [\fIlevels\fP]"
X.IP "builtin \fIcommand arg ...\fP"
X\fICommand\fP is executed as a built-in command.
X.IP "cd [\fIpath\fP]"
XSet the working directory to \fIpath\fP. If the parameter
XCDPATH is set, it lists the search path for the directory
Xcontaining \fIpath\fP. A null path means the current directory.
XIf \fIpath\fP is missing, the home directory ($HOME) is used.
XIf \fIpath\fP is \fB\-\fP, the previous working directory is used.
XIf \fIpath\fP is \fB..\fP, the shell changes directory to the
Xparent directory, as determined from the value of PWD.
XThe PWD and OLDPWD variables are reset.
X.IP "cd \fIold new\fP"
XThe string \fInew\fP is substituted for \fIold\fP in the current
Xdirectory, and the shell attempts to change to the new directory.
X.IP "continue [\fIlevels\fP]"
X.IP "echo ..."
X\fIEcho\fP is replaced with the alias echo='print' in the Korn shell.
X.IP "eval \fIcommand ...\fP"
X.IP "exec \fIcommand arg ...\fP"
XThe executable command is executed without forking.
XIf no arguments are given, any IO redirection is permanent.
X.IP "exit [\fIstatus\fP]"
X.IP "fc [\fB\-e\fP \fIeditor\fP] [\fB\-lnr\fP] [\fIfirst\fP [\fIlast\fP]]"
X\fIFirst\fP and \fIlast\fP select commands.
XCommands can be selected by history number,
Xor a string specifing the most recent command starting with that string.
XThe \fB\-l\fP option lists the command on stdout,
Xand \fB\-n\fP inhibits the default command numbers.
XThe \fB\-r\fP option reverses the order of the list.
XWithout \fB\-l\fP, the selected commands can be edited by
Xthe editor specified with the \fB\-e\fP option, or if no \fB\-e\fP
Xis specified,
Xthe \fB$FCEDIT\fP editor, then executed by the shell.
X.IP "fc \fB\-e \-\fP [\fB\-g\fP] [\fIold\fB=\fInew\fR] [\fIcommand\fP]"
XRe-execute the selected command (the previous command by default)
Xafter performing the optional substitution of \fIold\fP with \fInew\fP.
XIf \fB\-g\fP is specified, all occurrences of \fIold\fP are
Xreplaced with \fInew\fP.
XThis command is usually accessed with the predefined alias
Xr=``fc \-e \-''.
X.IP "getopts"
XSee the attached manual page.
X.IP "hash [\fB\-r\fP] [\fIname ...\fP]"
XWithout arguments, any hashed executable command pathnames are listed.
XThe \fB\-r\fP flag causes all hashed commands to be removed.
XEach \fIname\fP is searched as if it were a command name
Xand added to the hash table if it is an executable command.
X.IP "kill [\fB\-\fIsignal\fR] \fIprocess\fP ..."
XSend a signal (TERM by default) to the named process.
XThe signal may be specified as a number or a mnemonic from <signal.h>
Xwith the SIG prefix removed.
X.IP "let [\fIexpression ...\fP]"
XEach expression is evaluated, see "Expressions" above.
XA zero status is returned if the last expression evaluates
Xto a non-zero value, otherwise a non-zero status is returned.
XSince may expressions need to be quoted, \fI(( expr ))\fP is
Xsyntactic sugar for \fIlet "expr"\fP.
X.IP "print [\fB\-nreu\fIn\fR] [\fIargument ...\fP]"
X\fBPrint\fP prints its arguments on the standard output,
Xseparated by spaces, and terminated with a newline.
XThe \fB\-n\fP option eliminates the newline.
X.IP
XBy default, certain C escapes are translated.
XThese include \eb, \ef, \en, \er, \et, \ev, and \e### (# is an octal digit).
X\ec is equivalent to the \fB\-n\fP option.
XThis expansion may be inhibitted with the \fB\-r\fP option,
Xand may be re-enabled with the addition of the \fB\-e\fP option.
X.IP "read [\fB\-ru\fIn\fR] \fIname ...\fP"
XThe first variable name may be of the form \fIname\fB?\fIprompt\fR.
X.IP "readonly [\fIname ...\fP]"
X.IP "return [\fIstatus\fP]"
X.ta 5n 10n 30n
X.de O
X.br
X\t\\$1\t\\$2\t\\$3
X..
X.IP "set [\fB\(+-\fP\fI[a-z]\fP] [\fB\(+-o\fP \fIkeyword\fP] ..."
XSet (\fB\-\fP) or clear (\fB+\fP) a shell option:
X.O \-a allexport "all new variable are created with export attribute"
X.O \-e errexit "exit on non-zero status [incorrect]"
X.O "" bgnice "background jobs are run with lower priority"
X.if \n(OE \{
X.O "" emacs "BRL emacs-like line editing"\}
X.O "" ignoreeof "shell will not exit of EOF, must use \fIexit\fP"
X.O \-k keyword "variable assignments are recognized anywhere in command"
X.O "" markdirs "[not implemented]"
X.O \-m monitor "job control enabled (default for interactive shell)"
X.O \-n noexec "compile input but do not execute (ignored if interactive)"
X.O \-f noglob "don't expand filenames"
X.O \-u nounset "dollar expansion of unset variables is an error"
X.O \-v verbose "echo shell commands on stdout when compiling"
X.O \-h trackall "add command pathnames to hash table"
X.O "" vi "VI-like line editing"
X.O \-x xtrace "echo simple commands while executing"
X.IP "set [\fB\-\-\fP] \fIarg ...\fP"
XSet shell arguments.
X.IP "shift [\fInumber\fP]"
X.IP "test"
XSee the attached manual page.
X.IP "times"
X.IP "trap [\fIhandler\fP] [\fIsignal ...\fP]"
X.IP "typeset [\fB\(+-irtx\fP] [\fIname\fP[\fB=\fIvalue\fR] ...]"
XIf no arguments are given, lists all variables and their attributes.
X.PP
XIf options but no names are given, lists variables with specified
Xattributes, and their values if unless ``+'' is used.
X.PP
XIf names are given, set the attributes of the named variables.
XVariables may also be assigned a value.
XIf used inside a function, the created variable are local to the function.
X.PP
XThe attributes are as follows.
X.ta 5n 10n
X\t\-i\tThe variable's value is stored as an integer.
X.br
X\t\-x\tThe variable is exported to the enviroment.
X.br
X\t\-r\tThe variable is read-only cannot be reassigned a value.
X.br
X\t\-t\tTrace (not implemented).
X.br
X\t\-f\tList functions instead of variable.
X.\".IP "ulimit [\fB\-f\fP] [\fIvalue\fP]"
X.ds OZ <OZ>
X.IP "\fBulimit\fP \*(OK \fB\-\*(OZ\fP \*(CK \*(OK \fIn\fP \*(CK"
X.RS
X.TP "\w'\fB\-\-\ \ \ 'u"
X.if \n(OB \{.B \-c
XImpose a size limit of
X.I n\^
Xblocks on the size of core dumps.
X.TP
X.B \-d
XImpose a size limit of
X.I n\^
Xblocks on the size of the data area.\}
X.TP
X.B \-f
XImpose a size limit of
X.I n
Xblocks on files written by the shell
Xand its child processes (files of any size may be read).
X.if \n(OB \{.TP
X.B \-m
XImpose a soft limit of
X.I n\^
Xblocks on the size of physical memory.
X.TP
X.B \-t
XImpose a time limit of
X.I n\^
Xseconds to be used by each process.\}
X.PP
XIf no option is given,
X.B \-f
Xis assumed.
XIf
X.I n
Xis omitted, the current limit is printed.
XAs far as
X.B ulimit
Xis concerned, a ``block'' is 512 bytes.
X.PP
XYou may lower your own resource limit,
Xbut only a super-user (see
X.IR su (1M))
Xcan raise a limit.
X.RE
X.IP "umask [\fIvalue\fP]"
X.IP "unalias \fIname ...\fP"
XThe aliases for the given names are removed.
X.IP "unset [\fB\-f\fP] \fIname ...\fP"
X.IP "wait [\fIprocess-id\fP]"
X.IP "whence [\fB\-v\fP] name ..."
XFor each name, the type of command is listed.
XThe \fB\-v\fP flag causes function and alias values to be listed.
X.SS Job Control
XJob control features are enabled by the
X\fB\-m\fP or \fB\-o monitor\fP flags.
XWhen job control is enabled, and the system supports job control,
Xbackground commands and foreground commands that have been stopped
X(usually by a
X.SM SIGTSTP
Xsignal generated by typing
X.IR ^Z\^ )
Xare placed into separate individual
X.IR "process groups" .
XThe following commands are used to manipulate these process groups:
X.PP
X.PD 0
X.TP "\w'\fBkill\fP \*(OK \fIjob\fP \*(CK\ \ \ 'u"
X\fBjobs\fP
XDisplay information about the controlled jobs.
XThe job number is given preceeded by a percent sign,
Xfollowed by a plus sign if it is the ``current job'',
Xor by a minus sign if it is the ``previous job'',
Xthen the process group number for the job,
Xthen the command.
X.TP
X\fBkill\fP [\fB\-\fIsignal\fR] \fIjob\fP ...
XSend a signal (TERM by default) to the named job process group.
X.TP
X\fBfg\fP \*(OK \fIjob\fP \*(CK
XResume the stopped foreground job in the foreground.
XIf the process group
X.I n
Xis not specified then the ``current job'' is resumed.
X.TP
X\fBbg\fP \*(OK \fIjob\fP \*(CK
XResume the stopped foreground job in the background.
XIf the process group
X.I n
Xis not specified then the ``current job'' is resumed.
X.PD
X.PP
XThe \fBfg\fP, \fBbg\fP, \fBkill\fP, and \fBwait\fP commands
Xmay refer to jobs with the following ``percent'' sequences.
XThe percent sign is optional with the fg and bg commands.
X.PP
X.PD 0
X.TP "\w'\fBbg\fP \*(OK \fIn\fP \*(CK\ \ \ 'u"
X.BR %+ ( %\- )
XIf there is a ``current job'' (``previous job''),
Xthen that job is selected.
X.TP
X.BI % n
XIf the specified job number is one of the known jobs,
Xthen that job is selected.
X.TP
X.BI % string
XIf the string matches the initial part of a job's command,
Xthen that job is selected.
X.TP
X.BI %? string
XAs above, but the string may match any portion of the command.
X.sp
X.PP
XIf the system does not support job control, monitor mode enables
Xjob reporting. The jobs and kill commands
Xfunctions as above, and you will
Xbe informed when background jobs complete. Fg and bg are not
Xavailiable.
X.PD
X.br
X.SS "Interactive Input Line Editing"
XWhen the
X.B emacs
Xoption is set,
Xinteractive input line editing is enabled.
XThis mode is slightly different from the emacs mode in AT&T's KornShell.
XIn this mode various
X.I "editing commands"
X(typically bound to one or more control characters)
Xcause immediate actions without waiting for a new-line.
XSeveral
X.I "editing commands"
Xare bound to particular control characters
Xwhen the shell is invoked;
Xthese bindings can be changed using the following commands:
X.br
X.PP
X.PD 0
X.TP 2i
X\fBbind\fP
XThe current bindings are listed.
X.TP
X\fBbind\fP \*(OK \fIstring\fP \*(CK = \*(OK \fIediting-command\fP \*(CK
XThe specified
X.I "editing command\^"
Xis bound to the given
X.IR string ,
Xwhich should consist of a control character
X(which may be written using ``caret notation'' \fB^\fP\fIx\fP\|),
Xoptionally preceded by one of the two prefix characters.
XFuture input of the
X.I string
Xwill cause the
X.I "editing command\^"
Xto be immediately invoked.
X.br
XNote that although only two prefix characters (normal ESC and ^X)
Xare supported, some multi-character sequences can be supported:
X.br
Xbind '^[['=prefix-2
X.br
Xbind '^XA'=up-history
X.br
Xbind '^XB'=down-history
X.br
Xbind '^XC'=forward-char
X.br
Xbind '^XC'=backward-char
X.br
Xwill bind the arrow keys on an ANSI terminal. Of course some escape
Xsequences won't work out quite that nicely.
X.TP
X\fBbind -m\fP \*(OK \fIstring\fP \*(CK = \*(OK \fIsubstitute\fP \*(CK
XThe specified input
X.I string
Xwill afterwards be immediately replaced by the given
X.I substitute
Xstring,
Xwhich may contain
X.IR "editing commands" .
X.PD
X.PP
XThe following
X.I "editing commands"
Xare available;
Xfirst the command name is given
Xfollowed by its default binding (if any)
Xusing caret notation
X(note that the ASCII
X.SM ESC
Xcharacter is written as \s-1^[\s0\|),
Xthen the editing function performed is decribed.
XNote that
X.I "editing command"
Xnames are used only with the
X.B bind
Xcommand.
XFurthermore,
Xmany
X.I "editing commands"
Xare useful only on terminals with a visible cursor.
XThe default bindings were chosen to resemble corresponding EMACS key bindings.
XThe users tty characters (eg. erase) are bound to reasonable
Xsubstitutes.
X.br
X.PP
X.PD 0
X.TP "\w'\fBdelete-word-backward\ \ ^[\|ERASE\fP\ \ \ 'u"
X\fBabort\ \ ^G\fP
XUseful as a response to a request for a
X.B search-history
Xpattern in order to abort the search.
X.br
X.TP
X\fBauto-insert\fP
XSimply causes the character to appear as literal input.
X(Most ordinary characters are bound to this.)
X.br
X.TP
X\fBbackward-char\ \ ^B\fP
XMoves the cursor backward one character.
X.br
X.TP
X\fBbackward-word\ \ ^[\|b\fP
XMoves the cursor backward to the beginning of a word.
X.br
X.TP
X\fBbeginning-of-line\ \ ^A\fP
XMoves the cursor to the beginning of the input line
X(after the prompt string).
X.br
X.TP
X\fBcomplete\ \ ^[\|^[\fP
XAutomatically completes as much as is unique of the hashed command name
Xor the file name containing the cursor.
XIf the entire remaining command or file name is unique
Xa space is printed after its completion,
Xunless it is a directory name in which case
X.B /
Xis postpended.
XIf there is no hashed command or file name with the current partial word
Xas its prefix,
Xa bell character is output (usually causing a ``beep'').
X.br
X.TP
X\fBcomplete-command\ \ ^X^[\fP
XAutomatically completes as much as is unique of the hashed command name
Xhaving the partial word up to the cursor as its prefix,
Xas in the
X.B complete
Xcommand described above.
XOnly command and function names seen since the last
X.B "hash \-r"
Xcommand are available for completion;
Xthe
X.B "hash"
Xcommand may be used to register additional names.
X.br
X.TP
X\fBcomplete-file\ \ ^[\|^X\fP
XAutomatically completes as much as is unique of the file name
Xhaving the partial word up to the cursor as its prefix,
Xas in the
X.B complete
Xcommand described above.
X.br
X.TP
X\fBcopy-last-arg\ \ ^[\|_\fP
XThe last word of the previous command is inserted at the
Xcursor. Note I/O redirections do not count as words of
Xthe command.
X.br
X.TP
X\fBdelete-char-backward\ \ ERASE\fP
XDeletes the character before the cursor.
X.br
X.TP
X\fBdelete-char-forward\fP
XDeletes the character after the cursor.
X.br
X.TP
X\fBdelete-word-backward\ \ ^[\|ERASE\fP
XDeletes characters before the cursor back to the beginning of a word.
X.br
X.TP
X\fBdelete-word-forward\ \ ^[\|d\fP
XDeletes characters after the cursor up to the end of a word.
X.br
X.TP
X\fBdown-history\ \ ^N\fP
XScrolls the history buffer forward one line (later).
XEach input line originally starts just after
Xthe last entry in the history buffer,
Xso
X.B down-history
Xis not useful until either
X.B search-history
Xor
X.B up-history
Xhas been performed.
X.br
X.TP
X\fBend-of-line\ \ ^E\fP
XMoves the cursor to the end of the input line.
X.br
X.TP
X\fBeot\ \ ^_\fP
XActs as an end-of-file;
Xthis is useful because edit-mode input
Xdisables normal terminal input canonicalization.
X.br
X.TP
X\fBeot-or-delete\ \ ^D\fP
XActs as eot if alone on a line;
Xotherwise acts as delete-char-forward.
X.br
X.TP
X\fBexchange-point-and-mark\ \ ^X\|^X\fP
XPlaces the cursor where the mark is, and sets the
Xmark to where the cursor was.
X.br
X.TP
X\fBforward-char\ \ ^F\fP
XMoves the cursor forward one position.
X.br
X.TP
X\fBforward-word\ \ ^[\|f\fP
XMoves the cursor forward to the end of a word.
X.br
X.TP
X\fBkill-line\ \ KILL\fP
XDeletes the entire input line.
X.br
X.TP
X\fBkill-to-eol\ \ ^K\fP
XDeletes the input from the cursor to the end of the line.
X.br
X.TP
X\fBkill-region\ \ ^W\fP
XDeletes the input between the cursor and the mark.
X.br
X.TP
X\fBlist\ \ ^[\|?\fP
XPrints a sorted, columnated list of hashed command names or file names
X(if any) that can complete the partial word containing the cursor.
XDirectory names have
X.B /
Xpostpended to them,
Xand executable file names are followed by
X.BR \(** .
X.br
X.TP
X\fBlist-command\ \ ^X\|?\fP
XPrints a sorted, columnated list of hashed command names
X(if any) that can complete the partial word containing the cursor.
X.br
X.TP
X\fBlist-file\fP
XPrints a sorted, columnated list of file names
X(if any) that can complete the partial word containing the cursor.
XFile type indicators are postpended as described under
X.B list
Xabove.
X.br
X.TP
X\fBnewline\ \ ^J\ \fP\fIand\^\fP\fB\ ^M\fP
XCauses the current input line to be processed by the shell.
X(The current cursor position may be anywhere on the line.)
X.br
X.TP
X\fBnewline-and-next\ \ ^O\fP
XCauses the current input line to be processed by the shell, and
Xthe next line from history becomes the current line. This is
Xonly useful after an up-history or search-history.
X.br
X.TP
X\fBno-op\ \ QUIT\fP
XDoes nothing.
X.br
X.TP
X\fBprefix-1\ \ ^[\fP
XIntroduces a 2-character command sequence.
X.br
X.TP
X\fBprefix-2\ \ ^X\fP
XIntroduces a 2-character command sequence.
X.br
X.TP
X\fBquote\ \ ^^\fP
XThe following character is taken literally
Xrather than as an
X.IR "editing command" .
X.br
X.TP
X\fBredraw\ \ ^L\fP
XReprints the prompt string and the current input line.
X.br
X.TP
X\fBsearch-character\ \ ^]\fP
XSearch forward in the current line for the next keyboard character.
X.br
X.TP
X\fBsearch-history\ \ ^R\fP
XEnter incremental search mode.
XThe internal history list is searched backwards for commands matching the input.
XAn initial ``^'' in the search string anchors the search.
XThe escape key will leave search mode.
XOther commands will be executed after leaving search mode (unless
Xof course they are prefixed by escape, in which case they will
Xalmost certainly do the wrong thing).
XSuccessive
X.B search-history
Xcommands continue searching backward
Xto the next previous occurrence of the pattern.
XThe history buffer retains only a finite number of lines;
Xthe oldest are discarded as necessary.
X.br
X.TP
X\fBset-mark-command\ \ ^]\|<space>\fP
XSearch forward in the current line for the next keyboard character.
X.br
X.ie \n(OX \{.TP
X\fBstuff\ \ ^T\fP\}
X.el \{.TP
X\fBstuff\fP\}
XOn systems supporting it,
Xpushes the bound character back onto the terminal input
Xwhere it may receive special processing by the terminal handler.
X.if \n(OX \{This is useful for the BRL
X.B ^T
X``mini-systat'' feature,
Xfor example.\}
X.br
X.TP
X\fBstuff-reset\fP
XActs like
X.BR stuff\^ ,
Xthen aborts input the same as an interrupt.
X.br
X.ie \n(OX \{.TP
X\fBtranspose-chars\fP\}
X.el \{.TP
X\fBtranspose-chars\ \ ^T\fP\}
XExchanges the two characters on either side of the cursor, or
Xthe two previous characters if the cursor is at end of line.
X.br
X.TP
X\fBup-history\ \ ^P\fP
XScrolls the history buffer backward one line (earlier).
X.br
X.TP
X\fByank\ \ ^Y\fP
XInserts the most recently killed text string at the current cursor position.
X.br
X.TP
X\fByank-pop\ \ ^[\|y\fP
XImmediately after a
X.BR yank ,
Xreplaces the inserted text string with the
Xnext previous killed text string.
X.PD
X.br
X.SH FILES
X~/.profile
X.br
X/etc/profile
X.SH SEE ALSO
XSh(1) on System V or Sun OS.
X.LP
X.I "UNIX Shell Programming,"
XStephan G. Kochan,
XPatrick H. Wood,
XHayden.
X.LP
X.I "KornShell: Command and Programming Language (not yet published),"
XMorris Bolsky and David Korn.
X.SH AUTHORS
XBased on the public domain 7th edition Bourne shell.
X.LP
XSystem V and Korn modifications by Eric Gisin,
Xwith contributions by
XRon Natalie, Arnold Robbins, Doug Gwyn, Erik Baalbergen, AT&T (getopt(3)).
X.SH DIFFERENCES FROM AT&T VERSION
XVi editing mode is not implemented.
XThe \fBselect\fP statement is not implemented.
XVariable arrays are not implemented.
XVariable attributes other than integer are not implemented.
XThe \fBERR\fP and \fBEXIT\fP traps are not implemented for functions.
XAlias expansion is inhibited at the beginning of an alias definition
Xin the AT&T version.
XKorn evaluates expressions differently [elaborate].
X.SH BUGS
XInteractive shells will occasionally hang while waiting for a job
Xin the BSD version.
X.LP
XThe 8th bit is stripped in emacs mode.
X.LP
XQuoting double-quote (") characters inside back-quote (`) inside
Xdouble-quotes does not behave properly. Why are you doing this?
X.LP
XJob control on System V is not really job control. In fact it is
Xnot much of anything.
X.LP
XThe emacs mode can ``lose'' stty command done by the user.
X.LP
XUnsetting special variables
Xmay cause unexpected results.
X.LP
XFunctions declared as having local scope really have global scope.
X.LP
XHere documents inside functions do not work correctly.
X.LP
XExit on error (\fBset \-e\fP or \fBset -o errexit\fP) does not work
Xcorrectly.
X.TH TEST 1 "January 1988" "Korn shell"
X.ta 30n
X.de X
X.br
X\\$1\t\\$2
X..
X.SH NAME
Xtest \- test condition (Korn and 8th edition)
X.SH SYNOPSIS
X\fBtest\fP \fIexpression\fP
X.br
X\fB[\fP \fIexpression\fP \fB]\fP
X.SH DESCRIPTION
X\fBTest\f evalutates the \fIexpression\fP and returns zero status if true,
Xand non-zero status otherwise.
XIt is normally used as the controlling command of the \fBif\fP and \fBwhile\fP statements.
X.LP
XThe following basic expressions are available.
X.IP
X.X "-r file" "file exists and is readable"
X.X "-w file" "file exists and is writable"
X.X "-x file" "file exists and is executable"
X.X "-f file" "file is a regular file"
X.X "-d file" "file is a directory"
X.X "-c file" "file is a character special device"
X.X "-b file" "file is a block special device"
X.X "-p file" "file is a named pipe"
X.X "-u file" "file mode has setuid bit"
X.X "-g file" "file mode has setgid bit"
X.X "-k file" "file mode has sticky bit"
X.X "-s file" "file is not empty"
X.X "-L file" "file is a symbolic link"
X.X "-S file" "file is a socket"
X.X "file -nt file" "first file is newer than second file"
X.X "file -ot file" "first file is older than second file"
X.X "file -ef file" "first file is the same file as second file"
X.X "-t filedes" "file descriptor is a tty device"
X.IP
X.X "string" "string is not null"
X.X "-z string" "string is null"
X.X "-n string" "string is not null"
X.X "string = string" "strings are equal"
X.X "string != string" "strings are not equal"
X.IP
X.X "number -eq number" "numbers compare equal"
X.X "number -ne number" "numbers compare not equal"
X.X "number -ge number" "numbers compare greater than or equal"
X.X "number -gt number" "numbers compare greater than"
X.X "number -le number" "numbers compare less than or equal"
X.X "number -lt number" "numbers compare less than"
X.LP
XThe above basic expressions may be combined with the following operators.
X.IP
X.X "expr -o expr" "logical or"
X.X "expr -a expr" "logical and"
X.X "! expr" "logical not"
X.X "( expr )" "grouping"
X.SH AUTHOR
XErik Baalbergen. Modified by Arnold Robbins.
X.rn LP P
X.TH GETOPTS 1 "January 1988" "Korn shell"
X.SH NAME
Xgetopts \- parse command options
X.SH SYNOPSIS
X.B getopts
Xoptstring name [arg ...]
X.SH DESCRIPTION
X.I getopts
Xis used by shell procedures
Xto parse positional parameters and to check for legal options.
XIt supports all applicable rules of the command syntax standard
X(see Rules 3-10,
X.IR intro (1)).
XIt should be used in place of the
X.IR getopt (1)
Xcommand.
X(See the
X.BR \s-1WARNING\s0 ,
Xbelow.)
X.PP
X.I optstring
Xmust contain the option letters the command using
X.I getopts
Xwill recognize;
Xif a letter is followed by a colon,
Xthe option is expected to have an argument
Xwhich should be separated from it by white space.
X.PP
XEach time it is invoked,
X.I getopts
Xwill place the next option in the shell variable
X.I name
Xand the index of the next argument to be processed in the shell variable
X.BR \s-1OPTIND\s0 .
XWhenever the shell or a shell procedure is invoked,
X.B \s-1OPTIND\s0
Xis initialized to
X.BR 1 .
X.PP
XWhen an option requires an option-argument,
X.I getopts
Xplaces it in the shell variable
X.BR \s-1OPTARG\s0 .
X.P
XIf an illegal option is encountered,
X.B ?\&
Xwill be placed in
X.IR name .
X.P
XWhen the end of the options is encountered,
X.I getopts
Xexits with a non-zero exit status.
XThe special option
X.RB `` \-\- ''
Xmay be used to delimit the end of the options.
X.P
XBy default,
X.I getopts
Xparses the positional parameters.
XIf extra arguments
X.RI ( arg
X\&...) are given on the
X.I getopts
Xcommand line,
X.I getopts
Xwill parse them instead.
X.PP
XSo all new commands will adhere to the command syntax standard described in
X.IR intro (1),
Xthey should use
X.IR getopts (1)
Xor
X.IR getopt (3C)
Xto parse positional parameters
Xand check for options that are legal for that command
X(see
X.BR \s-1WARNINGS\s0 ,
Xbelow).
X.SH EXAMPLE
XThe following fragment of a shell program
Xshows how one might process the arguments
Xfor a command that can take the options
X.B a
Xor
X.BR b ,
Xas well as the option
X.BR o ,
Xwhich requires an option-argument:
X.PP
X.RS
X.nf
X.ss 18
X.ta +.5i +1i
X\fBwhile getopts abo: c
Xdo
X case $c in
X a\(bvb) FLAGS=$FLAGS$c;;
X o) OARG=$OPTARG;;
X \e?) echo $USAGE 1>&2
X exit 2;;
X esac
Xdone
Xshift OPTIND\-1\fP
X.fi
X.ta
X.ss 12
X.RE
X.PP
XThis code will accept any of the following as equivalent:
X.PP
X.RS
X.nf
X.ss 18
X\fBcmd \-a \-b \-o "xxx z yy" file
Xcmd \-a \-b \-o "xxx z yy" \-\- file
Xcmd \-ab \-o "xxx z yy" file
Xcmd \-ab \-o "xxx z yy" \-\- file\fP
X.fi
X.ss 12
X.RE
X.SH SEE ALSO
Xintro(1),
Xsh(1).
X.br
Xgetopt(3C)
Xin the
X.IR "Programmer's Reference Manual" .
X.br
X.IR "UNIX System V Release 3.0 Release Notes" .
X.SH WARNING
XAlthough the following command syntax rule (see
X.IR intro (1))
Xrelaxations are permitted under the current implementation,
Xthey should not be used because they may not be supported
Xin future releases of the system.
XAs in the
X.B \s-1EXAMPLE\s0
Xsection above,
X.B a
Xand
X.B b
Xare options,
Xand the option
X.B o
Xrequires an option-argument:
X.PP
X.RS
X.nf
X.ta +1i +1.5i
X\fBcmd \-aboxxx file\fP (Rule 5 violation: options with
X option-arguments must not be grouped with other options)
X\fBcmd \-ab \-oxxx file\fP (Rule 6 violation: there must be
X white space after an option that takes an option-argument)
X.fi
X.ta
X.RE
X.PP
XChanging the value of the shell variable
X.B \s-1OPTIND\s0
Xor parsing different sets of arguments
Xmay lead to unexpected results.
X.SH DIAGNOSTICS
X.I getopts
Xprints an error message on the standard error output
Xwhen it encounters an option letter not included in
X.IR optstring .
END_OF_FILE
if test 31646 -ne `wc -c <'ksh.1'`; then
echo shar: \"'ksh.1'\" unpacked with wrong size!
fi
# end of 'ksh.1'
fi
if test -f 'sh/jobs.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sh/jobs.c'\"
else
echo shar: Extracting \"'sh/jobs.c'\" \(16759 characters\)
sed "s/^X//" >'sh/jobs.c' <<'END_OF_FILE'
X/*
X * Process and job control
X */
X#ifndef lint
Xstatic char *RCSid = "$Id: jobs.c,v 3.5 89/03/27 15:51:01 egisin Exp $";
Xstatic char sccs_id[] = "@(#)jobs.c 1.4 91/08/16 17:36:59 (sjg)";
X#endif
X
X/*
X * based on version by Ron Natalie, BRL
X * modified by Simon J. Gerraty <sjg@melb.bull.oz.au>
X *
X * TODO:
X * change Proc table to Job table, with array of pids.
X * make %+ be jobs, %- be jobs->next.
X * do not JFREE members of pipeline.
X * consider procs[] related critical sections.
X * signal(SIGCHLD, j_sigchld) should be
X * sigaction(SIGCHLD, sigchld, NULL),
X * with sigchld.sa_flags = SA_RESTART.
X * There is a simple work-around if there is no SA_RESTART.
X */
X
X#include <stddef.h>
X#include <string.h>
X#include <stdio.h>
X#include <errno.h>
X#include <unistd.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <sys/types.h>
X#include <sys/times.h>
X#include <sys/wait.h>
X#include "sh.h"
X#include "tree.h"
X#ifdef JOBS
X#ifdef _BSD
X#include <sys/ioctl.h>
X#else
X#include "termios.h"
X#endif
X#endif
X
X#ifndef WIFCORED
X#define WIFCORED(x) (!!((x)&0x80)) /* non-standard */
X#endif
X
X/* as of P1003.1 Draft 12.3:
X * pid_t getpgrp(void); // Get process group id
X * pid_t setsid(void); // Create session and Set process group id
X * int setpgid(pid_t pid, pid_t pgid); // Set process group id for job control
X */
X
Xstatic int sigchld_caught; /* for recording child terminations */
X
X#ifdef JOBS
X#ifdef _BSD /* _BSD 4.* */
X#define setpgid(p, pg) setpgrp(p, pg)
X#define getpgid(p) getpgrp(p)
X#define tcsetpgrp(fd,p) ioctl(fd, TIOCSPGRP, &(p))
X#else /* POSIX-compatible */
X#define getpgid(p) getpgrp() /* 1003.1 stupidity */
X#define killpg(p, s) kill(-(p), s)
X#endif
X#endif
X
X#ifndef SIGCHLD
X#define SIGCHLD SIGCLD
X#endif
X
Xtypedef struct Proc Proc;
Xstruct Proc {
X Proc *next; /* `procs' list link */
X int job; /* job number: %n */
X short Volatile state; /* proc state */
X short Volatile notify; /* proc state has changed */
X Proc *prev; /* prev member of pipeline */
X pid_t proc; /* process id */
X pid_t pgrp; /* process group if flag[FMONITOR] */
X short flags; /* execute flags */
X int status; /* wait status */
X clock_t utime, stime; /* user/system time when JDONE */
X char com [48]; /* command */
X};
X
X/* proc states */
X#define JFREE 0 /* unused proc */
X#define JRUN 1 /* foreground */
X#define JEXIT 2 /* exit termination */
X#define JSIGNAL 3 /* signal termination */
X#define JSTOP 4 /* stopped */
X
Xstatic Proc *procs = NULL; /* job/process table */
X
Xclock_t j_utime, j_stime; /* user and system time for last job a-waited */
X#ifdef JOBS
Xstatic int sm_default, sm_sigchld; /* signal masks */
Xstatic int our_pgrp; /* shell's pgrp */
X#endif
Xstatic Proc *j_lastj; /* last proc created by exchild */
Xstatic int j_lastjob = 0; /* last created job */
Xstatic int j_current = 0; /* current job */
Xstatic int j_previous = 0; /* previous job */
X
Xstatic int j_newjob ARGS((void));
Xstatic void j_print ARGS((Proc *j));
Xstatic Proc *j_search ARGS((int job));
Xstatic int j_waitj ARGS((Proc *j, int intr));
Xstatic void j_sigchld ARGS((int sig));
X
X/* initialize job control */
Xvoid
Xj_init()
X{
X#ifdef JOBS
X#ifdef NTTYDISC
X int ldisc = NTTYDISC; /* BSD brain damage */
X
X if (ttyfd >= 0)
X ioctl(ttyfd, TIOCSETD, &ldisc);
X#endif
X our_pgrp = getpgid(0);
X sigchld_caught = 0;
X sm_default = 0;
X sm_sigchld = sigmask(SIGCHLD);
X _TRACE(5, ("j_init: sm_sigchld == 0x%x", sm_sigchld));
X#endif
X#if defined(_SYSV) && !defined(JOBS)
X signal(SIGCHLD, SIG_DFL); /* necessary ??? */
X#endif
X}
X
X/* job cleanup before shell exit */
Xvoid
Xj_exit()
X{
X register Proc *j;
X int killed = 0;
X
X#ifdef JOBS
X /* kill stopped jobs */
X for (j = procs; j != NULL; j = j->next)
X if (j->state == JSTOP) {
X killed ++;
X killpg(j->pgrp, SIGHUP);
X killpg(j->pgrp, SIGCONT);
X }
X if (killed)
X sleep(1);
X#endif
X j_notify();
X
X#ifdef JOBS
X if (flag[FMONITOR]) {
X flag[FMONITOR] = 0;
X j_change();
X }
X#endif
X}
X
X#ifdef JOBS
X/* turn job control on or off according to flag[FMONITOR] */
Xvoid
Xj_change()
X{
X static handler_t old_tstp, old_ttin, old_ttou;
X
X if (flag[FMONITOR]) {
X if (ttyfd < 0) {
X flag[FMONITOR] = 0;
X shellf("job control requires tty\n");
X return;
X }
X (void) signal(SIGCHLD, j_sigchld);
X sigtraps[SIGCHLD].sig_dfl = 1; /* restore on fork */
X old_tstp = signal(SIGTSTP, SIG_IGN);
X sigtraps[SIGTSTP].sig_dfl = 1;
X old_ttin = signal(SIGTTIN, SIG_IGN);
X sigtraps[SIGTTIN].sig_dfl = 1;
X old_ttou = signal(SIGTTOU, SIG_IGN);
X sigtraps[SIGTTOU].sig_dfl = 1;
X sigsetmask(sm_default);
X tcsetpgrp(ttyfd, our_pgrp);
X } else {
X (void) signal(SIGCHLD, SIG_DFL);
X (void) signal(SIGTSTP, old_tstp);
X sigtraps[SIGTSTP].sig_dfl = 0;
X (void) signal(SIGTTIN, old_ttin);
X sigtraps[SIGTTIN].sig_dfl = 0;
X (void) signal(SIGTTOU, old_ttou);
X sigtraps[SIGTTOU].sig_dfl = 0;
X }
X}
X#endif
X
X/* execute tree in child subprocess */
Xint
Xexchild(t, flags)
X struct op *t;
X int flags;
X{
X register int i;
X register Proc *j;
X int rv = 0;
X int forksleep;
X
X flags &= ~XFORK;
X if ((flags&XEXEC))
X return execute(t, flags);
X
X /* get free Proc entry */
X for (j = procs; j != NULL; j = j->next)
X if (j->state == JFREE)
X goto Found;
X j = (Proc*) alloc(sizeof(Proc), APERM);
X j->next = procs;
X j->state = JFREE;
X procs = j;
X Found:
X
X j->prev = ((flags&XPIPEI)) ? j_lastj : NULL;
X j->proc = j->pgrp = 0;
X j->flags = flags;
X j->job = (flags&XPIPEI) ? j_lastjob : j_newjob();
X snptreef(j->com, sizeof(j->com), "%T", t); /* save proc's command */
X j->com[sizeof(j->com)-1] = '\0';
X j->state = JRUN;
X
X /* stdio buffer must be flushed and invalidated */
X for (i = 0; i < NUFILE; i++)
X flushshf(i);
X
X /* create child process */
X forksleep = 0;
X while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
X if (forksleep) {
X sleep(forksleep);
X forksleep <<= 1;
X } else
X forksleep = 1;
X }
X if (i < 0) {
X j->state = JFREE;
X errorf("cannot fork - try again\n");
X }
X j->proc = (i != 0) ? i : getpid();
X
X#ifdef JOBS
X /* job control set up */
X if (flag[FMONITOR] && !(flags&XXCOM)) {
X j->pgrp = !(flags&XPIPEI) ? j->proc : j_lastj->pgrp;
X /* do in both parent and child to avoid fork race condition */
X if (!(flags&XBGND))
X tcsetpgrp(ttyfd, j->pgrp); /* could be trouble */
X setpgid(j->proc, j->pgrp);
X }
X#endif
X j_lastj = j;
X
X if (i == 0) { /* child */
X e.oenv = NULL;
X if (flag[FTALKING])
X restoresigs();
X if ((flags&XBGND) && !flag[FMONITOR]) {
X signal(SIGINT, SIG_IGN);
X signal(SIGQUIT, SIG_IGN);
X if (flag[FTALKING])
X signal(SIGTERM, SIG_DFL);
X if (!(flags&XPIPEI)) {
X i = open("/dev/null", 0);
X (void) dup2(i, 0);
X close(i);
X }
X }
X for (j = procs; j != NULL; j = j->next)
X j->state = JFREE;
X ttyfd = -1;
X flag[FMONITOR] = flag[FTALKING] = 0;
X cleartraps();
X execute(t, flags|XEXEC); /* no return */
X /* NOTREACHED */
X }
X
X /* shell (parent) stuff */
X if ((flags&XBGND)) { /* async statement */
X async = j->proc;
X j_previous = j_current;
X j_current = j->job;
X if (flag[FTALKING])
X j_print(j);
X } else { /* sync statement */
X if (!(flags&XPIPE))
X rv = j_waitj(j, 0);
X }
X
X return rv;
X}
X
X/* wait for last job: pipeline or $() sub-process */
Xint
Xwaitlast()
X{
X return j_waitj(j_lastj, 0);
X}
X
X/* wait for job to complete or change state */
Xstatic int
Xj_waitj(aj, intr)
X Proc *aj;
X int intr; /* interruptable */
X{
X register Proc *j;
X int rv = 1;
X int ttysig = 0;
X
X#ifdef JOBS
X if (flag[FMONITOR])
X {
X _TRACE(5, ("j_waitj: sigsetmask(sm_sigchld==0x%x)", sm_sigchld));
X sigsetmask(sm_sigchld);
X }
X#endif
X /* wait for all members of pipeline */
X for (j = aj; j != NULL; j = j->prev) {
X /* wait for job to finish, stop, or ^C of built-in wait */
X while (j->state == JRUN) {
X#ifdef JOBS
X if (flag[FMONITOR])
X {
X /*
X * 91-07-07 <sjg@sun0>
X * we don't want to wait for a signal
X * that has already arrived :-)
X */
X if (!sigchld_caught)
X {
X _TRACE(4, ("j_waitj: sigpause(%d), sigchld_caught==%d", sm_default, sigchld_caught));
X sigpause(sm_default);
X _TRACE(4, ("j_waitj: sigpause() returned %d, sigchld_caught==%d", errno, sigchld_caught));
X }
X }
X else
X#endif
X j_sigchld(0);
X /*
X * Children to reap
X */
X if (sigchld_caught)
X j_reapchld();
X _TRACE(4, ("j_waitj: j->proc==%d, j->com=='%s', j->state==0x%hx, j->status==0x%x, j->notify==%hd", j->proc, j->com, j->state, j->status, j->notify));
X
X if (sigtraps[SIGINT].set && intr)
X goto Break;
X }
X if (j->state == JEXIT) { /* exit termination */
X if (!(j->flags&XPIPEO))
X rv = WEXITSTATUS(j->status);
X j->notify = 0;
X } else
X if (j->state == JSIGNAL) { /* signalled to death */
X if (!(j->flags&XPIPEO))
X rv = 0x80 + WTERMSIG(j->status);
X if (WTERMSIG(j->status) == SIGINT ||
X WTERMSIG(j->status) == SIGPIPE && (j->flags&XPIPEO))
X j->notify = 0;
X if (WTERMSIG(j->status) == SIGINT ||
X WTERMSIG(j->status) == SIGQUIT)
X ttysig = 1;
X } else
X#ifdef JOBS
X if (j->state == JSTOP)
X if (WSTOPSIG(j->status) == SIGTSTP)
X ttysig = 1;
X#else
X ;
X#endif
X }
X
X /* compute total child time for time statement */
X for (j = aj; j != NULL; j = j->prev)
X j_utime += j->utime, j_stime += j->stime;
X
X /* find new current job */
X#ifdef JOBS
X if (aj->state == JSTOP) {
X j_previous = j_current;
X j_current = aj->job;
X } else {
X#else
X if (1) {
X#endif
X int hijob = 0;
X
X /* todo: this needs to be done in j_notify */
X /* todo: figure out what to do with j_previous */
X j_current = 0;
X for (j = procs; j != NULL; j = j->next)
X if ((j->state == JRUN || j->state == JSTOP)
X && j->job > hijob) {
X hijob = j->job;
X j_current = j->job;
X }
X }
X
X Break:
X#ifdef JOBS
X if (flag[FMONITOR]) {
X /* reset shell job control state */
X sigsetmask(sm_default);
X tcsetpgrp(ttyfd, our_pgrp);
X }
X#endif
X if (ttysig)
X fputc('\n', shlout);
X j_notify();
X
X return rv;
X}
X
X/* SIGCHLD handler to reap children */
X/*
X * 91-07-07 <sjg@sun0>
X * On the Sun SS2 this appears to get called
X * too quickly!
X * So just record the event and process later.
X */
Xstatic void
Xj_sigchld(sig)
X int sig;
X{
X sigchld_caught++; /* acknowledge it */
X#if defined(_SYSV) && !defined(JOBS)
X /* non-zero sig means called as handler */
X /* 5.2 handlers must reinstate themselves */
X if (sig) signal(SIGCHLD, j_sigchld);
X#endif
X}
X
X/*
X * 91-07-07 <sjg@sun0>
X * This now gets called when j_sigchld()
X * has recorded some signals...
X */
Xj_reapchld()
X{
X struct tms t0, t1;
X#ifdef JOBS
X int sm_old;
X
X sm_old = sigblock(0); /* just get current mask */
X#endif
X _TRACE(5, ("j_reapchld: sm_old==0x%x, sigchld_caught==%d", sm_old, sigchld_caught));
X (void) times(&t0);
X
X do {
X register Proc *j;
X int pid, status;
X#ifdef JOBS
X if (flag[FMONITOR])
X pid = waitpid(-1, &status, (WNOHANG|WUNTRACED));
X else
X#endif
X pid = wait(&status);
X if (pid <= 0) /* return if would block (0) ... */
X break; /* ... or no children or interrupted (-1) */
X (void) times(&t1);
X
X _TRACE(5, ("j_reapchld: looking for pid==%d", pid));
X
X for (j = procs; j != NULL; j = j->next)
X {
X _TRACE(6, ("j_reapchld: j->proc==%d, j->com=='%s', j->state==0x%hx, j->status==0x%x, j->notify==%hd", j->proc, j->com, j->state, j->status, j->notify));
X if (j->state != JFREE && j->proc == pid)
X goto Found;
X }
X _TRACE(5, ("j_reapchld: did not find pid==%d", pid));
X continue;
X Found:
X _TRACE(5, ("j_reapchld: found pid==%d", pid));
X j->notify = 1;
X j->status = status;
X#ifdef JOBS
X if (WIFSTOPPED(status))
X j->state = JSTOP;
X else
X#endif
X if (WIFEXITED(status))
X j->state = JEXIT;
X else
X if (WIFSIGNALED(status))
X j->state = JSIGNAL;
X
X /* compute child's time */
X /* todo: what does a stopped job do? */
X j->utime = t1.tms_cutime - t0.tms_cutime;
X j->stime = t1.tms_cstime - t0.tms_cstime;
X t0 = t1;
X#ifdef JOBS
X sigblock(sm_sigchld);
X#endif
X if (--sigchld_caught < 0) /* reduce the count */
X sigchld_caught = 0;
X#ifdef JOBS
X _TRACE(5, ("j_reapchld: j->proc==%d, j->com=='%s', j->state==0x%hx, j->status==0x%x, j->notify==%hd", j->proc, j->com, j->state, j->status, j->notify));
X sigsetmask(sm_old); /* restore old mask */
X#endif
X#ifdef JOBS
X } while (flag[FMONITOR]);
X#else
X } while (0); /* only once if wait()ing */
X#endif
X}
X
Xj_reap()
X{
X if (sigchld_caught)
X j_reapchld();
X#if defined(_SYSV) && !defined(JOBS)
X signal(SIGCHLD, j_sigchld);
X signal(SIGCLD, SIG_DFL);
X#endif
X return(0);
X}
X
X/* wait for child, interruptable */
Xint
Xwaitfor(job)
X int job;
X{
X register Proc *j;
X
X if (job == 0 && j_current == 0)
X errorf("no current job\n");
X j = j_search((job == 0) ? j_current : job);
X if (j == NULL)
X errorf("no such job: %d\n", job);
X if (flag[FTALKING])
X j_print(j);
X if (e.interactive) { /* flush stdout, shlout */
X fflush(shf[1]);
X fflush(shf[2]);
X }
X return j_waitj(j, 1);
X}
X
X/* kill (built-in) a job */
Xvoid
Xj_kill(job, sig)
X int job;
X int sig;
X{
X register Proc *j;
X
X j = j_search(job);
X if (j == NULL)
X errorf("cannot find job\n");
X if (j->pgrp == 0) { /* !flag[FMONITOR] */
X if (kill(j->proc, sig) < 0) /* todo: all member of pipeline */
X errorf("kill: %s\n", strerror(errno));
X#ifdef JOBS
X } else {
X if (sig == SIGTERM || sig == SIGHUP)
X (void) killpg(j->pgrp, SIGCONT);
X if (killpg(j->pgrp, sig) < 0)
X errorf("killpg: %s\n", strerror(errno));
X#endif
X }
X}
X
X#ifdef JOBS
X
X/* fg and bg built-ins */
Xint
Xj_resume(job, bg)
X int job;
X int bg;
X{
X register Proc *j;
X
X j = j_search((job == 0) ? j_current : job);
X if (j == NULL)
X errorf("cannot find job\n", job);
X if (j->pgrp == 0)
X errorf("job not job-controlled\n");
X
X j->state = JRUN;
X j_print(j);
X flushshf(2);
X
X if (!bg)
X tcsetpgrp(ttyfd, j->pgrp); /* attach shell to job */
X if (killpg(j->pgrp, SIGCONT) < 0)
X errorf("cannot continue job %%%d\n", job);
X if (!bg)
X return j_waitj(j, 0);
X return 0;
X}
X
X#endif
X
X/* list jobs for jobs built-in */
Xvoid
Xj_jobs()
X{
X register Proc *j;
X
X for (j = procs; j != NULL; j = j->next)
X if (j->state != JFREE)
X j_print(j);
X}
X
X/* list jobs for top-level notification */
Xvoid
Xj_notify()
X{
X register Proc *j;
X
X /*
X * since reaping is no longer done in the signal handler
X * we had better try here...
X */
X if (sigchld_caught)
X j_reapchld();
X
X for (j = procs; j != NULL; j = j->next) {
X if (j->state == JEXIT && !flag[FTALKING])
X j->notify = 0;
X if (j->state != JFREE && j->notify)
X j_print(j);
X if (j->state == JEXIT || j->state == JSIGNAL)
X j->state = JFREE;
X j->notify = 0;
X }
X}
X
Xstatic void
Xj_print(j)
X register Proc *j;
X{
X char buf [64], *s = buf;
X
X switch (j->state) {
X case JRUN:
X s = "Running";
X break;
X
X#ifdef JOBS
X case JSTOP:
X strcpy(buf, "Stopped ");
X s = strchr(sigtraps[WSTOPSIG(j->status)].mess, '(');
X if (s != NULL)
X strcat(buf, s);
X s = buf;
X break;
X#endif
X
X case JEXIT: {
X int rv;
X rv = WEXITSTATUS(j->status);
X sprintf(buf, "Done (%d)", rv);
X if (rv == 0)
X *strchr(buf, '(') = 0;
X j->state = JFREE;
X } break;
X
X case JSIGNAL: {
X int sig = WTERMSIG(j->status);
X char *n = sigtraps[sig].mess;
X if (n != NULL)
X sprintf(buf, "%s", n);
X else
X sprintf(buf, "Signal %d", sig);
X if (WIFCORED(j->status))
X strcat(buf, " - core dumped");
X j->state = JFREE;
X } break;
X
X default:
X s = "Hideous job state";
X j->state = JFREE;
X break;
X }
X shellf("%%%-2d%c %5d %-20s %s%s\n", j->job,
X (j_current==j->job) ? '+' : (j_previous==j->job) ? '-' : ' ',
X j->proc, s, j->com, (j->flags&XPIPEO) ? "|" : "");
X}
X
X/* convert % sequence to job number */
Xint
Xj_lookup(cp)
X char *cp;
X{
X register Proc *j;
X int len, job = 0;
X
X if (*cp == '%') /* leading % is optional */
X cp++;
X switch (*cp) {
X case '\0':
X case '+':
X job = j_current;
X break;
X
X case '-':
X job = j_previous;
X break;
X
X case '0': case '1': case '2': case '3': case '4':
X case '5': case '6': case '7': case '8': case '9':
X job = atoi(cp);
X break;
X
X case '?': /* %?string */
X for (j = procs; j != NULL; j = j->next)
X if (j->state != JFREE && strstr(j->com, cp+1) != NULL)
X job = j->job;
X break;
X
X default: /* %string */
X len = strlen(cp);
X for (j = procs; j != NULL; j = j->next)
X if (j->state != JFREE && strncmp(cp, j->com, len) == 0)
X job = j->job;
X break;
X }
X if (job == 0)
X errorf("%s: no such job\n", cp);
X return job;
X}
X
X/* are any stopped jobs ? */
X#ifdef JOBS
Xint
Xj_stopped()
X{
X register Proc *j;
X
X for (j = procs; j != NULL; j = j->next)
X if (j->state == JSTOP)
X return 1;
X return 0;
X}
X#endif
X
X/* create new job number */
Xstatic int
Xj_newjob()
X{
X register Proc *j;
X register int max = 0;
X
X j_lastjob ++;
X for (j = procs; j != NULL; j = j->next)
X if (j->state != JFREE && j->job)
X if (j->job > max)
X max = j->job;
X if (j_lastjob > max)
X j_lastjob = max + 1;
X return j_lastjob;
X}
X
X/* search for job by job number */
Xstatic Proc *
Xj_search(job)
X int job;
X{
X register Proc *j;
X
X for (j = procs; j != NULL; j = j->next)
X if (j->state != JFREE && job == j->job && !(j->flags&XPIPEO))
X return j;
X return NULL;
X}
X
END_OF_FILE
if test 16759 -ne `wc -c <'sh/jobs.c'`; then
echo shar: \"'sh/jobs.c'\" unpacked with wrong size!
fi
# end of 'sh/jobs.c'
fi
if test -f 'sh/trace.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sh/trace.c'\"
else
echo shar: Extracting \"'sh/trace.c'\" \(3039 characters\)
sed "s/^X//" >'sh/trace.c' <<'END_OF_FILE'
X/* NAME:
X * trace.c - a simple trace facility
X *
X * SYNOPSIS:
X * TRACE(level, (fmt [, ...]));
X *
X * DESCRIPTION:
X * This module provides a simple trace facility via
X * a call to checkpoint() which opens a log file and writes
X * an entry and closes the log (so that process crashes
X * won't destroy the log :-). checkpoint() takes options
X * like printf(). If Trace_log is not initialized then
X * stderr is used.
X *
X * The header file trace.h defines a macro TRACE() which
X * is useful in that it allows checkpoint to be called
X * based on the value of Trace_level, and the calls can be
X * eliminated by undefining USE_TRACE.
X *
X * RETURN VALUE:
X * None.
X *
X * FILES:
X * None.
X *
X * SEE ALSO:
X *
X *
X * BUGS:
X *
X *
X * AMENDED:
X * %E% %U% (%Y%)
X *
X * RELEASED:
X * %D% %T% v%I%
X *
X * @(#)Copyright (c) 1990 Simon J. Gerraty.
X */
X#ifdef USE_TRACE
X
X#ifndef lint
Xstatic char sccs_id[] = "%Z%%M% %I% %E% %U% (%Y%)";
X#endif
X
X/* include files */
X#include <stdio.h>
X#ifdef __STDC__
X# include <stdlib.h>
X#endif
X
X#define EXTERN
X#include "trace.h"
X#undef EXTERN
X
X/* some useful #defines */
X#ifndef ENTRY
X# define ENTRY
X# define LOCAL static
X# define BOOLEAN int
X#endif
X#ifndef TRUE
X# define TRUE 1
X# define FALSE 0
X#endif
X#ifndef _P_
X# if defined(__STDC__) || defined(PROTO)
X# define _P_(p) p
X# else
X# define _P_(p) ()
X# endif
X#endif
X
X
X/* NAME:
X * checkpoint - write a logfile entry
X *
X * SYNOPSIS:
X * checkpoint(fmt, ...)
X *
X * DESCRIPTION:
X * This function takes a variable number or args
X * like the printf(3S) family of functions.
X *
X * RETURN VALUE:
X * None
X */
Xextern char * _CDECL strdup _P_((char *s));
X
X#ifdef __STDC__
X# include <stdarg.h>
X
XENTRY void _CDECL
Xcheckpoint(fmt)
X char *fmt;
X{
X int c;
X va_list arg_ptr;
X FILE *fp;
X register char *rcp;
X char *mode;
X static setup;
X
X va_start(arg_ptr, fmt);
X#else /* __STDC__ */
X# include <varargs.h>
X
XENTRY void _CDECL
Xcheckpoint(va_alist)
X va_dcl
X{
X extern char *getenv _P_((char *var));
X char *fmt;
X int c;
X va_list arg_ptr;
X FILE *fp;
X register char *rcp;
X char *mode;
X static setup;
X
X va_start(arg_ptr);
X fmt = va_arg(arg_ptr, char *);
X#endif /* __STDC__ */
X
X /* 42 is a "magic" number */
X if (setup == 42)
X mode = "a";
X else
X {
X if (Trace_level == 0 && (rcp = getenv("TRACE_LEVEL")))
X Trace_level = atoi(rcp);
X if (Trace_log == NULL || *Trace_log == '\0')
X {
X if (rcp = getenv("TRACE_LOG"))
X Trace_log = strdup(rcp);
X else
X Trace_log = NULL;
X }
X setup = 42;
X mode= "w";
X }
X if (Trace_log)
X fp = fopen(Trace_log, mode);
X else
X fp = stderr;
X if (fp != (FILE *)NULL)
X {
X vfprintf(fp, fmt, arg_ptr);
X fputc('\n', fp);
X if (fp == stderr)
X fflush(fp);
X else
X fclose(fp);
X }
X}
X
X#endif /* USE_TRACE */
X
X/* This lot (for GNU-Emacs) goes at the end of the file. */
X/*
X * Local Variables:
X * version-control:t
X * comment-column:40
X * End:
X */
END_OF_FILE
if test 3039 -ne `wc -c <'sh/trace.c'`; then
echo shar: \"'sh/trace.c'\" unpacked with wrong size!
fi
# end of 'sh/trace.c'
fi
echo shar: End of archive 2 \(of 9\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 9 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD UUCP: uunet!sparky!kent
Phone: (402) 291-8300 FAX: (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.