GUIDE to
SAM Kernel
Updated 2012-08-08, 2023-05-13

Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2023
Joseph Rosevear



SAM is the union of a SAM kernel and application.  The application
brings much to SAM, but the nature of SAM is defined by the kernel. 
That is what I will describe here.

Read this guide, but also use other ways to learn about SAM. Just
trying and doing is a good way.  Also you can look at the scripts from
which the SAM kernel is made.  There aren't very many.  There are three
C language executables.  You can look at their source files.  The
examples and explanatory files that may have come with your SAM program
are an excellent way to learn about SAM too.

Another way to learn about SAM is to run it in debug mode. When you do
this you will see messages that tell where SAM is in its execution and
a little about what SAM is doing.  SAM will also run more slowly to
give you time to read the messages as the appear on the screen.  To use
debug mode type "export sam_debug=on".  Then use SAM in the normal way. 
It is helpful to write down the comments so that you can then study
what SAM did.  To leave debug mode type "unset sam_debug".

Here is the guide:


1.  The terms "local" and "current"

SAM and its documentation use the terms "local" and "current".  The
term "current" is used in its familiar way as in "current directory". 

The term "local", however, belongs to SAM.  It is not the same as
"current".  Any directory can be made local.  The "bound" command in
the global menu can be used to do this.  When you use bound $1 is added
to PATH and the file $1/menu.dat is displayed (if it exists) along with
other text.  This is what is meant by "local".

The other text displayed describes the global menu which corresponds to
the commands of the $sam and $sam_tool directories.  These directories
have their own files menu.dat which are used when the global menu is
displayed.


2.  The "bound" command

When you use "bound" you will want to name a complete path that goes to
a directory where you have programs (scripts, executables or functions)
that you want to use.  This way you can easily document (briefly) what
the programs do and how to use them (in the menu), and you can easily
put your programs in separate directories without the need to have the
paths to these directories always in your PATH variable.

SAM takes care of the PATH variable, adding to and subtracting from it
in a way that is consistent with its use.  SAM displays both the local
directory and the current directory in its prompt.  Try using the
command "bound" to see this.


3.  SAM's shells  

SAM displays a shell counter, a numeral, at the end of the first line
of the prompt.  SAM uses one genuine Bash shell.  The other shells that
SAM makes are "synthetic".  SAM needs these synthetic shells for it's
operation, but you may interact with them as well.  The synthetic shell
works by pushing (some of) the current variables onto a stack--which is
actually a set of files in $sam_temp_dir, and incrementing the variable
sam_env_num.  To "pop" it decrements sam_env_num and unsets, sets, and
exports as needed to restore the previous environment.  It's actually
just a bit more complex than this.  Look at the source code in
sam/source.  You should understand that SAM's synthetic shells work by
manipulating the current environment.

Let me explain the "some of".  SAM pushes variables that begin with a
lower case letter onto a stack.  And variables that begin with some
other characters too.  The C code that is operating here (set_filt.exe)
selects all variables (for pushing onto the stack) that begin with a
letter with an ACII value greater than 96.  If you examine an ASCII
table you will see that this includes a-z and some unusual characters,
but excludes A-Z.  The intention was to exclude variables in all caps
which are normally environment variables, but you can see that with my
lazy coding I excluded more than just these.

An exception to the above is the treatment of the variables PATH and
PS1.  The C code pushes these onto the stack as well.  This was done so
that PATH (the search path) and PS1 (the prompt) would both belong to
the synthetic shells.  This enables an important SAM behavior.  Since
SAM's menus get their character from the search path and the prompt,
putting these on the stack effectively puts the menu on the stack. 
This gives the menus a natural hierarchy--menus contain menus.  Leaving
a menu, by popping it from the stack, returns you to menu that came
before it.  SAM has commands "push" and "pop" in its kernel.  These are
the foundation of other kernel commands: "hello", "bye", "bound",
"love", and "unlove".


4.  Some notes on scripting

SAM is script-able.  What you do at the command line you can put into
ordinary shell scripts.  This guide will not define the full scripting
capability, instead it will make a few notes.

There are two kinds of scripts that SAM uses.  These are: (1)
undesignated, meaning not of the following kind, and (2) authorized
scripts with names that end in ".sam".  A script is "authorized" if it
contains the line "#OK SAM" somewhere in its first ten lines.

Scripts of the .sam kind are used to define functions.  Each such
script may define one function.  That function should have the same
name as the script less ".sam".  What is special about a
.sam script is that SAM makes the function it contains available when
and only when the directory that contains the script has been added to
PATH by use of SAM's commands.  In this way the functions in these
scripts can be used like executables in a directory.  Thus they fall
under SAM's menuing control.

I invented this feature (used when scripts ending .sam are referenced),
because some things can be done conveniently by functions that I could
not do otherwise.  Yet I needed the functions to behave like ordinary
commands for them to be useful to SAM.  In particular, functions are
convenient whenever it is desired that the result of the operation
performed apply to the current environment.  This is needed when coding
a tool to change current directories-- perhaps your tool uses "cd". 
And it is needed when your tool changes the local (SAM term) directory
or the current set of local or exported variables as you might do with
the bound or sam_shel commands.  I tried using "source" (or ".") to
trick ordinary scripts into doing this, but I got into a tangle (with
positional parameters) which made it difficult at best.

The current method works, but be aware of what's happening.  If you
want to investigate, see files $sam/dofun, $sam/upfun.sam, and
$sam_temp_dir/unfun.

You may write and use your own scripts of both kinds.


4b.  Batch mode

SAM has a batch mode.  It is selected by exporting thusly:

   export sam_batch=batch

before invoking SAM.  In batch mode a shell will not stay open waiting
for a command line input.  This capability was added to SAM to allow me
to invoke it from crontab files.  It wouldn't work otherwise.

See $sam/sam_core for the details.


5.  Bugs in SAM

recursion:

You can invoke SAM inside of SAM, but there is one peculiarity when
doing so.  The functions that were defined before the nested SAM was
invoked will be inherited by the nested SAM.  That is they will be
unless something is done before SAM is invoked.

This is either a feature of SAM or a bug.  It is something that you
should be aware of.

SAM is written so that you can easily remove the existing function
definitions.  This guide is not about the application of SAM, but you
can be sure that any application of SAM that I write will automatically
remove inherited function definitions.  Still, you should be aware of
this, and be careful.

lockfile and msdos:

SAM uses the lockfile command to augment the protection given by the
use of the "in_use" file.  The in_use file protects against multiple
simultaneous uses of the same $sam_temp_dir, however it fails at this
when the user invokes SAM rapidly in succession.  That's where the
lockfile command comes in.  It almost fixes things.  The problem is
that I haven't been able to get lockfile to put a lock on a file in an
msdos file system.  There is no such trouble with the native Linux ext2
file system, so maybe this is a non-issue.

SAM tries to put the lock file in sam_temp_dir.  So if you are using an
msdos file system for $sam_temp_dir, be aware that there is no
protection against rapid successive invocations of SAM causing multiple
simultaneous use of a $sam_temp_dir.  You will see a warning from
lockfile that it cannot do it's job--something about praying.  If you
avoid rapid successive invocations of SAM when using $sam_temp_dir in
an msdos file system you'll be OK.


6.  SAM limits

SAM has some limits that might be seen as bugs as follows:

Don't run instructional text files:

Be careful not to run instructional text files (such as this file). 
BAD things could happen.  Ordinarily such files cannot be run, because
they don't have execute permission.  However, if you run SAM from an
msdos file system, then you will not have this protection.  Or this
could also be the case if I (or someone) have given a text file execute
permission by mistake.

Beware commands and variables:

SAM has a few scripts and executables that you may not know about
unless you look.  They are not likely to do any harm, but it would be
smart for you to look and see what they are so you don't use them by
accident.  The documentation does not currently list all the scripts
and executables.

Similarly, SAM adds many variables to the environment.  It does this in
a shell, so you original environment is protected.  However, SAM
depends on these variables to do what it does.  You could have
difficulty if you alter one of them.  The documentation does not
currently list these variables.
  
cd `pwd`:

SAM does this (the above) in sam/pop.sam.  It is an odd thing to have
to do.  It is needed to make the prompt correct in certain situations,
but this isn't fully understood.

Disk space:

You can run out of disk space needed for SAM's $sam_temp_dir.  Although
this isn't likely, SAM does nothing to prevent it.  It would cause SAM
to fail.

Limited buffers:

A buffer in set_filt.c is of limited size and can cause failure.  The
code checks to prevent writing beyond the end of the buffer, but
unfortunately SAM cannot proceed if the buffer isn't big enough.  See
the source code for details, and be aware that this error message

   Buffer size exceeded.

means the buffer size limit has been encountered.  You are free to
change the buffer size and recompile.

Non existent local directories:

SAM doesn't care if you do this:  "bound /hey".  It works whether /hey
exists or not.  This is good, because SAM is a tool.  You may have a
reason to do this even if /hey doesn't exist.  Similarly, you can do
this: "bound hey".  The leading "/" has been removed.  Again you may
have a reason to do this.  In both cases if you aren't aware of what
you are doing, SAM may surprise you.

Mistakes of comprehension:

When writing SAM scripts you are really writing ordinary Bash shell
scripts.  Yet, there are mistakes of comprehension that can be made. 
It is easy to misunderstand what SAM does.  The methods of scripting
shown by the examples (that may have come with your distribution) work
well when each method is understood and followed.  If a method is not
followed, then it may not work correctly.  Similarly, if a method is
not understood, it may seem to not work correctly.  Both situations can
be frustrating, but such are not necessarily errors belonging to SAM.

Limitation in xargs:

SAM puts a lot of stuff in the environment.  In particular, it exports
a lot of variables and functions.  This is why I sometimes got an error
message which said "xargs: environment is too large for exec".  The
solution I found was to change xargs and recompile it, then replace the
original xargs with the corrected one.

Here's the piece of code I changed, before and after:

   Before:

      if (arg_max > 20 * 1024)
        arg_max = 20 * 1024;

   After:

    /* JHR 050813 I commented out and changed the two lines below */
    /*if (arg_max > 20 * 1024)
        arg_max = 20 * 1024; */
      if (arg_max > 40 * 1024)
        arg_max = 40 * 1024;

I learned that this was the needed fix by what I found when searching
in Google Groups "www.groups.google.com" on the quoted error message.

After doubling the limit (from 20K to 40K) I've had no trouble...yet.

Here's an update (today is 4-18-12):  The native xargs seems to work
fine now, so I removed the modifed xargs.  Perhaps the solution came
from a change that I made.  I added a filter called "slice" to the
kernel.  It is used by "push" and treats an odd runaway growth of the
environment (in particular, the variable TERMCAP).  Read the comments
in slice to learn more.
