=head1 NAME

psh - Perl Shell


=head1 SYNOPSIS

B<psh> is a perl program which executes a read-eval loop with enough
options so that general behavior reasonably similar to more
traditional shells like 'sh' or 'bash' can be achieved, while still
allowing arbitrary perl expressions to be evaluated.

By default within B<psh>, the C<-w> flag and 'C<use strict>' are not
employed so that the user is not bound by their stipulations. 
They can both be turned on via a command-line flag; or setting
C<$^W = 1> will turn on warnings, and calling 'C<use strict>' will
(almost) do the usual thing if called by the user (see LIMITATIONS, below).

=head1 OPTIONS

The following command line options are available:

=over 4

=item * C<-w> Enables Perl's warning mode

=item * C<-d> Enables psh's debugging mode

=item * C<-c string> If the -c flag is present, then commands are read from C<string>

=item * C<-r file> If the -r flag is present, initialization commands are not read from the user's .pshrc file but from the specified file

=head1 DESCRIPTION

Each line of input is read. B<psh> knows a number of possible
strategies for evaluating the line, such as "send it to system() if it
starts with the name of an executable visible in $ENV{PATH}". (See
below for a complete list.)
Each strategy in turn (from a user-definable
list) examines the command line to see if it can apply, and the first
matching strategy evaluates the line. There is a psh configuration
variable (see below) which controls whether the perl value of the
evaluation is saved and printed after each command.

B<psh> automatically collects several lines of input into a unit
processed as a single line if there are unfinished Perl constructs on
the line. In particular, if there is an unmatched quote, paren, brace,
or square bracket, input is read until these characters match. If an
input line contains the Perl "here document" construct as in
C<E<lt>E<lt>XXX>, (anywhere on the line), then input is read and
accumulated until C<XXX> occurs on a line by itself. Then the
accumulated input is processed as if it were a single line.

=head2 TOKENIZATION

Some evaluation strategies examine the "words" of the input. These are
produced by a tokenizer which behaves very similarly to traditional
shells: words are broken at whitespace, '&' is a metacharacter which
means that it always forms its own word, and backslash and double and
single quotes act as quoting characters, preventing word breaks at
whitespace and the "meta-ness" of &.

If the description of the strategy does not mention the "words", then
the tokenization is irrelevant to that strategy.

=head2 STANDARD EVALUATION STRATEGIES

B<psh> includes the following evaluation strategies:

=over 4

=item * C<comment>

If the first word of the input line begins with a '#' character, ignore it.

=item * C<bang>

If the first word of the input line begins with a '!' character, send
everything after the '!' to system().

=item * C<built_in>

If the first word of the input line matches a B<psh> "built-in"
function, call the subroutine associated with that built-in; the
subroutine receives a single argument, which is the remainder of the
input line exactly as entered.

=item * C<perlfunc>

If the first word of the input line matches either a built-in Perl
function, or a defined subroutine in the main:: package, pass the line
to eval. If $psh::perlfunc_expand_arguments is true and the line
contains no parens, or braces or commas (except for {a,b,c} as in
shell brace-expansion), then this strategy tries to
interpret the arguments on the command line in a "shell-like" manner:
strings are literal except for variable expansion, brace expansion,
and glob expansion. 

The idea of this strategy is to allow perl functions, especially
subroutines in main, to be called like "ordinary commands" (i.e.,
executables on disk). Or put another way, the idea is to replace
bash's "shell function" capacity with ordinary Perl subroutines. The
slogan is, "If the command line looks like an ordinary shell command,
interpret it like one, even if the first word is a Perl subroutine."

=item * C<perlscript>

If the first word of the input line matches the name of a file the
path $ENV{PATH} which starts with #!/.../perl, and that perl is the
same as the Perl under which psh is running, psh will fork and run
the script using the already-loaded Perl interpreter. The idea is to
save the exec half of the fork-exec that the executable strategy would 
do; typically the exec is more expensive. Right now this strategy can
only handle the B<-w> command-line switch on the #! line.

=item * C<executable>

If the first word of the input line matches the name of an executable
file in the path given by $ENV{PATH}, then pass the line to
system. Perl variable substitution will be done on the line first if
the $psh::executable_expand_arguments configuration variable is
true. 

=item * C<eval>

Pass the line to eval, regardless of any condition. This is a catch-all
strategy; strategies placed after it will never be triggered.

=back

The list of evaluation strategies to try is kept in the configuration
variable $psh::strategies, which defaults to
C<qw(comment bang built_in perlfunc executable eval)>. 

=head2 BUILT-IN FUNCTIONS

The following functions are provided as built-in functions of
C<psh>. You can add an additional built-in called 'foo' by setting the
hash $psh::built_ins{foo} to a reference to a subroutine. When a line
like "foo bar $baz" is then evaluated by the 'built_in' strategy, the
string 'bar $baz' will be passed to your subroutine as its only
argument. This is precisely the difference between making 'foo' a
built-in as opposed to simply defining a procedure main::foo and
letting the eval strategy call foo: the built-in subroutine will get
the precise input string entered, whereas the ordinary procedure will
of course get arguments that have already been evaluated by the Perl
evaluator. Which you want depends on the nature of the function being
defined.

=over 4

=item * C<. FILE> [or C<source FILE>]

Read and process the contents of the given file as a sequence of psh
commands.

=item * C<alias [NAME [=] REPLACEMENT]> 

Add NAME as a built-in so that NAME <REST_OF_LINE> will execute
exactly as if REPLACEMENT <REST_OF_LINE> had been entered. For
example, one can execute C<alias ls ls -F> to always supply the B<-F>
option to "ls". Note the built-in is defined to avoid recursion
here. Note also that any previous definition of NAME as a built-in is
destroyed by alias, so that "alias source source /home/scripts/" won't
work to prepend "/home/scripts/" to every source command.

With no arguments, prints out a list of the current aliases.

=item * C<bg [JOB]>

Put a job into the background. If JOB is omitted, uses the
highest-numbered stopped job, if any.

=item * C<cd DIR>

Change the working directory to DIR or $ENV{HOME} if DIR is not specified.
The special DIR '-' is interpreted as "return to the previous
directory'.

=item * C<exit>

Exit out of the shell.

=item * C<export VAR [=VALUE]>

Just like setenv, below, except that it also ties the variable (in the
Perl sense) so that subsequent changes to the variable automatically
affect the environment. Variables who are lists and appear in
%psh::array_exports will also by tied to the array of the same name.
Note that the variable must be specified without any Perl specifier
like $ or @

=item * C<fg JOB>

Bring a job into the foreground. If JOB is omitted, uses the
highest-numbered stopped job, or, failing that, the highest-numbered job.

=item * C<jobs>

List the currently running jobs.

=item * C<kill [SIGNAL] [%JOB | PID]>

Send SIGNAL (which defaults to TERM) to the given process, specified
either as a job (%NN) or as a pid (a number).

=item * C<readline>

Prints out information about the current ReadLine module which is
being used for command line input. Very rudimentary at present, should 
be extended to allow rebinding, etc.

=item * C<setenv NAME [=] VALUE>

Sets the environment variable NAME to VALUE.

=item * C<which COMMAND-LINE>

Describe how psh will execute the given COMMAND-LINE, under the
current setting of $psh::strategies.

=back

=head2 INVOKING PSH

The command-line arguments to B<psh> are:

 psh [-d] [-w] [-r RC_FILE] [FILE1 FILE2 ....]

The B<-w> switch runs perl with the C<"-w"> switch and "use strict;".
The B<-d> option puts B<psh> into "debugging" mode, which prints
diagnostic output, including primarily the result of the "which"
builtin before every execution. Note that you can also enter/leave this
debugging mode in a running psh via the $psh::debugging variable.

The B<-r> option specifies a file of commands to be read in and
evaluated before processing begins. If it is not set, and
C<$ENV{HOME}> is set, and the file C<$ENV{HOME}/.pshrc> is present, it
will be used. If B<-r> is not specified and the current directory is
different from C<$ENV{HOME}> and it contains a F<.pshrc> file, that
file will be read and executed in addition to
C<$ENV{HOME}/.pshrc>. 

If any FILE1 ... arguments are specified on the command line, they
will be read and executed and then psh will exit. Otherwise, B<psh>
will enter an interactive command loop.

=head2 PSH VARIABLES and FUNCTIONS

B<psh> makes a number of variables and functions accessible to the
user in the psh:: package for configuration or utility
purposes. Their default values are given in parentheses below. If the
variable is also marked "[late]", then it is undefined at the start of 
the F<.pshrc> file, but any value given to it during that file will be 
used instead of the default setting.

=over 4

=item $psh::bin (the basename of the file B<psh> was invoked by)

The name of the current shell.

=item $psh::cmd

The command serial number in the currently-executing processing loop.

=item $psh::currently_active (0)

The pid of the process B<psh> will currently forward signals to, or 0
if B<psh> will handle the signals internally. Usually
0 unless B<psh> is waiting for a process in the "foreground".

=item $psh::debugging (1 if -d option specified, 0 if not)

Whether B<psh>'s internal debugging output should be produced.

=item $psh::echo (0)

Controls whether the processing loop saves and displays the Perl value 
of executing a line of input. Three cases are distinguished: a false
value, in which case no results are displayed; a subroutine reference, 
in which case the subroutine is called with the results (this may be
multiple arguments if the eval returned an array) and should return
true to display, false to not; or a true (scalar) value, in which case 
any non-undef, non-empty value is displayed.

In addition to displaying the value, it is pushed onto @psh::val.
Note that scalar values are
pushed directly onto @psh::val, but array values are pushed by
reference. 

=item $psh::eval_preamble ("package main;")

Every Perl expression that psh evaluates as part of its read-eval loop
is prepended with this string, intended primarily to set the expected
package context.

=item $psh::executable_expand_arguments (0)

If this variable is true, then the following
procedure is applied to each argument ARG of the command line before
passing the command line on to "system":
If ARG starts with a single quote, it is untouched.
If ARG starts with a double quote, it is passed to "eval", and then double
quotes are placed around the result.
Otherwise, double quotes are placed around ARG, it is passed to eval,
and the result is used directly.
The intention of these steps is to do variable substitution of perl
variables that appear in the command line as the user would expect, and
in a way similar to shells such as bash. For example, if 
@files = (file1, file2, file3), then
 'ls @files' will expand to 'ls file1 file2 file3', whereas
 'echo "@files"' will expand to 'echo "file1 file2 file3"'.

=item $psh::history_file ("$ENV{HOME}/.${bin}_history") [late]

The filename B<psh> will use to save the command history in from one
invocation to the next, if $psh::save_history is true.

=item $psh::history_length (50) [late]

The number of lines of command history to keep.

=item $psh::host (the output of "hostname -s") [late]

The short host name of the machine psh is currently running on.

=item $psh::longhost (the output of "hostname") [late]

The fully qualified host name of the machine psh is running on.

=item $psh::news_file ("$psh::bin.NEWS")

A file giving news for the shell.

=item $psh::perlfunc_expand_arguments (0)

If this variable is true, then command lines interpreted by the
perlfunc strategy which contain no parens, or  braces or commas except 
in bash-like brace-expansion expressions, will not
be simply evaluated. Rather, they will be interpreted much like
traditional shells do: the line will be spilt into words, which
undergo globbing, brace expansion, and variable expansion, and the
resulting array of words is passed to the specified function as its
arguments. 

=item $psh::prompt ('\s\$') [late]

Controls the prompt string in interactive use, see below.

=item $psh::save_history (1) [late]

If this is true, the command history is saved in $psh::history_file
from one invocation of B<psh> to the next.

=item $psh::handle_segfaults (0)

If set to true, psh will ignore all segfaults and attempt to
continue.

=item @psh::netprograms ( ping, ftp, ncftp, lynx etc. )

Contains names of a number of net based programs for enabling
TAB completion of hostnames/bookmarks

=item @psh::bookmarks ( 'http://', 'ftp://' )

Supposed to contain your most used IP numbers, hostnames or URLs.
Those will be eligible for TAB completion if you use one of the
programs in @psh::netprograms. Default only contains 'http://' and
'ftp://'.

=item @psh::mon ( Jan, Feb, etc. )

An array of month names used in printing dates, for example in prompt
strings.

=item @psh::strategies ( comment, bang, built_in, executable, eval)

The list of strategies for executing a line, tried in order.

=item @psh::val 

Stores away the results of executing lines, as described in $psh::echo
above. 

=item @psh::wday ( Mon, Tue, Wed, Thu, Fri, Sat)

An array of the weekday names used in printing dates, for example in
prompt strings.

=item %psh::built_ins

The keys are command names, the values are subroutine references which
are called with the remainder of the line exactly as input.

=item %psh::prompt_vars

The keys of this hash are single characters, and the values are
subroutine references that implement the given escape character in
prompt strings. (See L<PROMPT STRINGS> below.)

=item %psh::strategy_which

=item %psh::strategy_eval

These hashes implement the named strategies used in B<psh>'s
evaluation of input lines. It is possible to add new strategies, see below.

=item %psh::array_exports

Contains a list of environment variables which should be tied
to arrays. The key of the hash is the name of the variable, the
value is the delimiter of the list (e.g. ':' in PATH)

=item &psh::evl

This function takes a string, evaluates it as if it were a line of
B<psh> input, and returns the value. Useful in loops like:
 
 psh$ C<for $file (glob $pat) { psh::evl("ls -ld $file"); }>

=item &psh::is_number

Returns true if its first argument is a number. Intended for use in
filter subroutines placed in $psh::echo. For example,
C<$psh::echo = \&psh::is_number;> will cause only numeric return
values to be printed.

=item &psh::news

Returns the current news, as a string.

=item * &psh::print_debug, print_error, print_out, print_warning

These four functions are called whenever B<psh> wants to produce B<-d>-mode 
output, error messages, normal output, and warnings,
respectively. They could conceivably be redefined to implement logging 
or similar facilities.

=item &psh::symbols

Takes one argument, a package name, and shows all of the symbols in
that package.


There are other functions in the psh:: package, but they are probably
not useful except internally to B<psh>.

=head2 PROMPT STRINGS

Setting the variable C<$psh::prompt> to a string will cause that string
to be used as the prompt-string. Setting it to a subroutine reference
causes the result of running that subroutine to be used each time.
For example,

  $psh::prompt = sub { $i++; "psh [$i]\$ "; }

will cause the prompt to be C<psh [1]$> followed by C<psh [2]$>, and so on.

C<psh> uses some of the same ``prompting variables'' as C<bash>. They are
accessed by placing a backslash followed by the code in the prompt string,
either hard coded, or as returned by the prompt string function. The
variables supported are:

=over 4

=item d The date in ``Weekday Month Day'' format

=item h The hostname

=item n A carriage return and line feed

=item s The name of the shell

=item t The current time in HH:MM:SS format

=item u The username of the current user

=item w The current working directory

=item W The basename of the current working directory

=item # The command number of the current command

=item $ `#' if the effective UID is zero, else `$'

=back

Custom prompting variables may be added by adding entries to the array
C<%psh::prompt_vars> keyed by the single character code. The entries
should be subroutine references that return the replacement string.

=head2 DEFINING EVALUATION STRATEGIES

There are two hashes, %psh::strategy_which and %psh::strategy_eval.
An evaluation strategy called "foo" is implemented by putting a
subroutine object in each of these hashes keyed by "foo". The first
subroutine should accept a reference to a string (the exact input
line) and a reference to an array of strings (the array of "words" in
the input line produced by &psh::decompose, provided as a convenience
so that each individual strategy doesn't have to recompute this). It
should return a string, which should be empty if the strategy does not
apply to that input line, and otherwise should be an arbitrary
non-null string describing how that strategy applies to that line. It
is guaranteed that the string passed in will contain some
non-whitespace, and that the first string in the array of words is
non-empty.

The $strategy_eval{foo} routine accepts the same two first arguments
and a third argument, which is the string returned by
$strategy_which{foo}. It should do the evaluation, and return the
result. Note that the $strategy_eval function will be evaluated in
an array context. 

=head1 LIMITATIONS

The loop inside C<psh> will clobber C<$1> and other variables because
it uses matches to implement some of its special functions.

Very little error checking is done. For example, if you evaluate
!blork by the bang strategy and there is no command "blork" on your
system, you get no feedback whatsoever.

Right now, job control simply assumes that the POSIX interface is
fully implemented. There should be a way to turn job control off if
this is not the case.

The "exit status" of programs invoked in the foreground by the
"executable" strategy (or even the "bang" strategy) isn't available
from within psh.

Note that since expressions like 'use foo' return undef when sent to
eval(), it is not possible to use that return value as indication of
an error. Instead, we use the heuristic that there was no error unless
the special Perl variable '$@' is non-empty. Note that the side
effects of 'use foo' as a B<psh> command line appear to be exactly as
expected. 

Not exactly a C<psh> limitation: Term::ReadLine::Gnu seems to be rather
buggy on Linux systems and leads to continous crashes of C<psh>.
Use Term::ReadLine::Perl instead ( set PERL_RL to Perl to disable
Term::ReadLine::Gnu)

=head1 REQUIREMENTS

psh needs several optional Perl modules to offer full functionality:

=over 4

=item Term::ReadLine::Gnu or Term::ReadLine::Perl for readline support
(command history, special editing chars etc.). Term::ReadLine::Gnu
support is by far more advanced currently than support for
Term::ReadLine::Perl

=item Term::Size or Term::ReadKey to offer the ability to change the
environment variables LINES and COLUMNS when the terminal window size
changes while running as standard shell

=back

=head1 OTHER PERL SHELLS

Larry Wall exhibits the simple Perl shell C<while (E<lt>E<gt>) { eval; print $@; }> on
page 161 of the Camel Book (2nd Edition).

Rich Graves E<lt>F<rcgraves@brandeis.edu>E<gt> posted a comment to the original
psh-0.001 announcement on C<http://freshmeat.net>, which contained this
gem that leverages the Perl debugger: C<perl -d -e 1>;

Some versions of the Perl faq mention an interactive Perl shell called
SoftList, which can still be found at
C<http://www.mit.edu/afs/sipb/contrib/perl/SoftList/>. It predates
Term::Readline and was apparently last touched in 1993, so it seems to
be obsolescent.

In an example of convergent evolution, at C<http://jenda.krynicky.cz/>
there is a Perl shell module called F<PSH.pm> which is quite similar
to this B<psh>. It is designed to provide a command line that can be called
inside some other program via C<PSH::prompt();>, but a small file
F<psh.pl> is also included that uses PSH to provide a standalone
shell. Perhaps some merger of these efforts would be beneficial to all?

=head1 FILES

C<psh> - The Perl Shell executable script.

C<.pshrc> - The user's Perl Shell `profile'. May be in C<$HOME> or the
current directory; if both are present, both will be read in the order
mentioned.


=head1 AUTHOR

Gregor N. Purdy, E<lt>F<gregor@focusresearch.com>E<gt>


=head1 CREDITS

The following people have contributed to the development of C<psh>:

=over 4

=item Prodigious Contributors

Markus Peter E<lt>F<warp@spin.de><E<gt> added job and signal handling and filename
completion. He also made some updates to history handling.

Glen Whitney E<lt>F<gwhitney@post.harvard.edu>E<gt> added evaluation
strategies, improved interrupt/job handling, &psh::evl, $psh::echo,
more extensive documentation, and other more minor features.

=item ReadLine Support

Code examples showing how to apply the Term::ReadLine package were contributed
by Billy Naylor E<lt>F<billy.naylor@eu.net>E<gt> (in his C<pash.pl> program,
which is his own Perl shell).

=item Symbol Table Dumping

Billy Naylor E<lt>F<billy.naylor@eu.net>E<gt> also had an example of a symbol
table printing function that was used as the starting point for the C<psh>
function C<psh::symbols()>. The C<psh> version adds the ability to specify a
package name, and it also filters out some special variables. The implementation
technique is also different from Billy's.

=item Prompt String Variables

Matthew D. Allen E<lt>F<s2mdalle@titan.vcu.edu>E<gt> contributed an enhanced
prompt string handling routine that emulates the C<bash> prompt variables. This
was expanded into the form now present.

=item Typo Spotting

Allan Kelly E<lt>F<akelly@holyrood.ed.ac.uk>E<gt> found some problems with the
generated documentation.

=back


=head1 COPYRIGHT

Copyright (C) 1999 Gregor N. Purdy. All rights reserved.
This script is free software. It may be copied or modified according
to the same terms as Perl itself.

=cut
