ZMSH(1zm)                                                            ZMSH(1zm)

       zmsh - zmailer shell

       zmsh [-CIJLOPRSYisaefhntuvx] [-c  command] [script ...]

       The zmsh(1zm) is an implementation of the Bourne shell suitable for use
       with the ZMailer router(8zm) as its configuration file language  inter-
       preter.  It contains extensions that allow structured data (in the form
       of lists) to be manipulated.

       The shell supports three basic kinds of functions: Unix commands, user-
       defined functions, and builtin commands.  The later comes in two varia-
       tions: normal functions which take string arguments and return a status
       code  (much as an embedded Unix command would work), and list-manipula-
       tion functions which understand list  arguments  and  can  return  list
       arguments.   The  defined  functions  can take any form of argument and
       return any form of value (a status code, a string, or a list).

       Shell operations (pipes, backquote evaluation  and  substitution)  will
       work  between combinations of builtin functions, defined functions, and
       Unix commands.

       The shell precompiles its input to  a  (possibly  optimized)  byte-code
       form, which is then interpreted as required.  This means that the orig-
       inal form of the input is not kept around in-core for future reference.
       If  the input is an included file, the shell will try to save the byte-
       code form in a .fc file associated with the input file.   For  example,
       if  input  is from, the shell will try to create fc/file.fc and
       then file.fc.  These files will in turn  be  searched  for  and  loaded
       (after a consistency check) whenever a .cf file is included.

       The effects of input and output redirections are predicted prior to the
       execution of a command and its I/O setup.

       zmsh is based on the System V release 3 shell as described in  a  SunOS
       4.0  manual page.  It conforms to the behaviour of that shell, with the
       following differences:

              ^ is not accepted as an alternative pipe character.

              A symbol may have both a function definition and a value.

              The shell is 8 bit transparent.

              All occurrences in the sh(1) manual page of "if any character in
              the  word is quoted" should be read as "if the word or the first
              character of it is quoted".

              The -k option is not supported.

              Inside backquotes, for efficient deadlock avoidance,  Unix  com-
              mands  are  executed in parallel.  If you need serial execution,
              use `(c1 ; c2)` instead of `c1 ; c2`.

              Builtin commands that want to write more than  one  pipe  buffer
              full  should detach themselves into a grandchild and run to com-
              pletion independently.

              The hash builtin is not fully  supported.   The  fancy  printing
              options are not implemented, but program locations are hashed.

              If PS1 or PS2 are defined functions, then their function defini-
              tion will be assumed to print  an  appropriate  prompt  and  the
              function will be called instead of printing out the value of the
              PS1/PS2 shell variables.

              In IFS, newline is always ignored (it  is  always  a  separator)
              when  reading  commands.   This  seems  to  match  normal  shell
              behaviour but not obviously so from the manual page.

              The SHELL environment variable is not special, since there is no
              restricted mode.

              SIGSEGV is not special.

              The message printed for ${FOO?...} starts with progname: instead
              of FOO:.

              The functions login, newgrp, pwd, and readonly,  are  not  built

              Functions  can  have a comma-separated list of named parameters.
              If the argument list  is  exhausted  on  a  function  call,  the
              remainder of the parameters are set to the empty string.  If the
              parameter list is exhausted,  the  @  variable  is  set  to  the
              remainder of the arguments.  This behaviour is backward compati-
              ble with normal shell functions.

              The local builtin statement declares local  variables  within  a
              function scope.

              Builtin commands in pipelines are run within the shell, so vari-
              able settings in that context will affect the shell process.

              The type function does not print a shell function definition  in
              text form.

              There  is a builtin function to force its arguments to be evalu-
              ated as builtin function call.

              The termination string in here documents must be static.   Some-
              thing like cat << `echo EOF` will not work.

       The  following  additions and extensions have been made relative to the
       base shell:

       The Korn shell backquote mechanism is supported.  This means that  `foo
       bar`  is  equivalent  to $(foo bar) in all contexts, although the later
       form is preferred for clarity.

       If you are used to an old Bourne shell, the following are  the  unusual
       builtin functions in this shell:

             test (or [), getopts, times, type, builtin, sleep.

       The type and builtin functions are lifted from the Ninth Edition Bourne
       shell.  The test and sleep functions  are  in  the  shell  because  the
       mailer will use them very frequently.

       The  ssift/tsift  statements  are  special-purpose  constructs  for the
       mailer.  They act like a case statement, except the labels are  regular
       expressions, are separated from the label body by whitespace instead of
       being terminated by a closing parenthesis, and  exiting  a  ssift/tsift
       label  body  will  just  cause a fall-through to the next ssift / tsift
       label.  This is similar to the production-rule semantics of Sendmail.

       The tsift structure has (for compability  reasons)  alias:  sift.   The
       difference  in  between  ssift  and  tsift(sift)  is  on  what is being
       matched: The tsift matches TOKENS in  RFC-822  sense,  while  ssift  at
       first  expands the token sequence into a string, and then does the reg-
       expr matching.

       The local statement can appear anywhere in a scope (a  {...}  grouping)
       and  declares  variables  that  are  created  in  the current scope and
       destroyed on exit from it.  Such variables are initialized to refer  to
       a null string.

       Functions  may be defined with named parameters, which are scoped vari-
       ables and destroyed on return from the function.

       The semantics of lists have been defined in various contexts:


            (a b (c (d e) f (g)))

              A list may contain strings and other lists as elements.   It  is
              printed and usually entered using traditional Lisp syntax.

       Variable assignment:

            recipient=(what ever)

              A shell variable value may be either a string or a list.  A list
              value can be entered directly.

       Element counts:

            $(length $(expression))

              Every list-valued variable has a length accessed  using  the  $#
              prefix,  as  in csh (1) syntax.  The length builtin function can
              also be used to count the number of toplevel elements of a list.
              Empty lists have length 0.  Strings have no length.

       Command line:

            router $(list smtp neat.cs rayan) (...)

              Command-line  arguments  to  builtin or defined functions may be
              lists.  The first argument cannot be entered directly as a list,
              but  later arguments may be.  The restriction is due to a syntax
              clash with function definitions.


            for i in yes (a b c) no; do; ...; done

              A list in a loop list is treated as a single element.  The  loop
              variable will be bound to it.

       Associative (property) lists:

            $(get variable symbol)

              The  value  of  variable  should  be a list of even length, with
              alternating attributes and values.  The given symbol is  matched
              with an attribute, and the following value is returned.

       Associative (property) assignment:

            setf $(get variable symbol) value

              The  setf  function  is  used  to change the value that would be
              returned by an expression, typically this  is  done  by  pointer

       Some new builtin functions have been defined to operate on lists:

       car    (or  first),  returns the first element of the list which is its

       cdr    (or rest), returns the list after the first element of the  list
              which is its argument.

              is  used  to explode a list, for use in a loop or to concatenate
              the list elements together.  For example:

                   hostlist=(neat.cs smoke.cs)
                   for hosts in $(elements hostlist)

       get    is a property list lookup function.  Property lists are lists of
              alternating keywords (properties or attributes) and values.  For

                   jane=(hair brown eyes blue)
                   get $jane eyes

       grind  is used for lists instead of echo, use it to print a list value.

       last   returns the last element of the list which is its argument.

       length returns the count of elements of the list which is its argument.

       list   returns the list containing its arguments as elements.

       setf   takes a retrieval command and a  new  value  as  arguments.   At
              present  it works with (combinations of) car, cdr, get and last.
              For example:

                   setf $(get $jane eyes) azur

       Lists are ignored in any context where they aren't expected.  For exam-
       ple,  a list value as an argument to echo would behave just like a null
       argument.  Builtin functions either know about  lists  (which  subsumes
       normal  strings), or about string values.  Defined functions are flexi-
       ble, whatever you pass them will show up in their argument list.

       On a command line, the first argument can never be written  as  a  list
       (since  the  parser  won't be able to tell that from a function defini-
       tion).  However, the following arguments may be written as lists.  This
       leads to constructs like:

            aliasexpand $(list local - rayan) plist

       The  one  exception  to  this  is in the return statement, which may be
       given a list (or string) as its argument.  If so, the return value from
       the defined function becomes list-valued, and the status code is set to
       0.  If the argument to the return statement is numeric, it  is  assumed
       to be the desired status code.

       Be  forewarned of strange effects if you print to stdout in a list-val-
       ued function that is called within a backquote (i.e. where  the  return
       value, not the status, is desired) and you expect a list back.

       The  following  debugging options are specific to the internal function
       of zmsh:

       -C     print code generation output onto stdout.   If  this  option  is
              doubled, the non-optimized code is printed out instead.

       -I     print runtime interpreter activity onto /dev/tty; argv:s of exe-
              cutes, assignments, variables, ...

       -J     print runtime interpreter activity onto /dev/tty; just argv:s of

       -L     print lexer output onto stdout.

       -O     optimize  the  compiled  script.  If this option is doubled, the
              optimized code is also printed out.

       -P     print parser output (S/SL trace output) onto stdout.

       -R     print I/O actions onto /dev/tty.

       -S     print scanner output (token assembly) onto stdout.

       -Y     open /dev/tty for internal debugging use.

       These are the normal shell options that are supported by zmsh:

       -c     run the given argument as a shell command script.

       -i     this shell is interactive,  meaning  prompts  are  printed  when
              ready  for more input, SIGTERM is ignored, and the shell doesn't
              exit easily.  This flag is automatically set if stdin and stderr
              are both attached to a tty.

       -s     read  commands from stdin.  If there are non-option arguments to
              the shell, the first of these will be  interpreted  as  a  shell
              script  to  open  on  stdin,  and  the  rest as arguments to the

       -a     automatically export new or changed shell variables.

       -e     exit on non-zero status return of any command.

       -f     disables filename generation (a.k.a. "globbing").

       -h     hash and cache the location of Unix commands.   This  option  is
              set by default.

       -n     read commands but do not execute them.

       -t     exit after running one command.

       -u     unset variables produce an error on substitution.

       -v     print shell input as it is read.

       -x     print commands as they are executed.

       Nested  here documents don't work with optimization off, and terminator
       string isn't stacked.

       Hitting interrupt during I/O stuff is a bad thing.

       The file descriptor  prediction  doesn't  always  work,  especially  in
       (nested) multi-pipe command lines.

       sh(1), router(8zm), zmailer.conf(1zm).

       This program authored and copyright by:
          Rayan Zachariassen <no address>
       Some "small" tweaks by:
          Matti Aarnio <>

                                  2003-Aug-28                        ZMSH(1zm)