[comp.unix.shell] Wanted: thoughts about history mechanisms.

dfenyes@thesis1.med.uth.tmc.edu (David Fenyes) (04/24/91)

Hello,

I'm working on a small shell and I'd like it to have much of the 
functionality of ksh and bash without taking up as much space.
If I want the code itself to fit in 64K, I've got about 8K code space
to implement a history mechanism.  I figure the approach would be
to have the shell simply store the lines, and offer access to them
in a meaningful way.  All processing & re-execution, etc. can then
be done with shell funcs.  (The shell is based on Kenneth Almquist's ash)

What is the best way to approach a history mechanism?

Since the way we think of commands typed into a shell is in 'blocks'
of one 'major' prompt (PS1) and the subsequent continuations (PS2's),
This might be one way for the shell to think about chunks of history.

Here are my thoughts for a low-level shell history support:

- implement history as a dynamically resizable list.

- Mark each line as major or minor prompt (ps1 or ps2)

- history command is basically a lister.

- A load command (which is already implemented in the shell) allows
  a line to be pre-fed to the editor, so a line is already in the
  buffer when the editor is invoked.  If 'load' is invoked with
  a line already in the buffer, the previous line is saved in the
  history and the new line is stored in the buffer.

- `load' command would contain an option to load as minor prompt line,
  loading as a major prompt line as default.

- The history command contains options to list from <line> to <line>,
  where <line> could be relative or absolute, or can list 'blocks'
  from the nth back major prompt to the n+1st, etc.  History options
  would allow lines to be listed either plain, or with appropriate
  prefixed option to the 'load' command to allow it to read in the
  line as either a major or minor prompt line.


Using these tools, history can be saved in a file either wholesale
with a trap 0, or the most recent chunk before each prompt via the
EVERY variable, which is executed before a PS1.  At logout, the
file can be truncated to HISTSIZE with /bin/tail, and at login,
the file can be read in with a function that precedes each line
with the word 'load' and executes the result.

Re-executing a command or block could be accomplished via something
like :
eval $(history options args...)
(done in a shell function.)

substitution could be done with something like
eval $(subst $1 $2 "$(history opts args)"), where subst() is a
shell function using builtin expr.

These are just some rough thoughts, so I thought I'd ask this group
for input, since most of us do some thinking about shells.

Thanks,

David.
----
David Fenyes                                 dfenyes@thesis1.hsch.utexas.edu
University of Texas Medical School           Houston, Texas

alex@am.sublink.org (Alex Martelli) (04/26/91)

I've also hacked a simple mechanism into ash for history processing
via external commands (mostly for Minix and Coherent, actually); the
credit for the idea must go, at least 3/4ths of it, to my friend and
colleague Lanfranco Albani, albani@cadlab.sublink.org, although I was
the one that did most of the implementation.
1. the shell just appends each logical-line to a history-file; this is
   how any history-like external command accesses the history;
2. if, when the shell is about to emit a primary-prompt to terminal, it
   finds that a file named $HOME/..dothis exists, it opens, unlinks, 
   and then sources it; this is how the shell accesses the results of
   any history-like external command.

Several enhancements suggest themselves (have both history-file and
doit-file named by environment variables, and so on) - and the best
thing is that, far from needing 8K of codespace, both of these changes
will fit in just a few bytes... another one of this class, if you like
csh-like history-expansion as well as interactive one, is to have an
environment variable HISTORY-WHEN whose syntax is a colon-separated
list of pairs regular-expression//program-name: if a logical line
matches a regular expression, then it is passed to the standard input
of the associated program name (at a quite early stage of the 
processing) - the program can emit the commands to be performed to
standard output and/or to the doit-file as before.
For example, an HISTORY-WHEN of '/^^|[^\\]!/cshclone' would strictly
imitate csh's behavior (assuming cshclone does so) - the /'s are just
arbitrary separators, the regular expression means 'a caret at line
start or a non-backslash followed by a bang' (...the heavy overloading
of the caret sign makes it LOOK more complex that it is...:-).
As you can probably tell, this last suggestion is one I have not 
implemented, nor even fully finalized in design [what exactly should
be the difference between the history-filter emitting to stdout versus
putting commands into the doit-file, and what if it does both, what
*should* be put into the history-file, etc, to keep it reasonably
simple and usable].  Notice that ash already has regexps, as a part
of the expr/test builtin, so their price in codesize has been paid
already...

As is typical of good ideas, these simple tricks have many uses - the
doit-file, for example, allows not only history-like mechanisms but
all sorts of external mechanisms able to set the shell's environment,
change its current-directory, and so on.
Together with the "executable prompt string" that we already 
discussed, these ideas work well in my ash ('mash').  I would of
course be honored if any shell author felt it worth his or her while
to use or elaborate them in another shell... 
-- 
Alex Martelli - (home snailmail:) v. Barontini 27, 40138 Bologna, ITALIA
Email: (work:) martelli@cadlab.sublink.org, (home:) alex@am.sublink.org
Phone: (work:) ++39 (51) 371099, (home:) ++39 (51) 250434; 
Fax: ++39 (51) 366964 (work only), Fidonet: 332/401.3 (home only).

jmason@gpu.utcs.utoronto.ca (Jamie Mason) (04/28/91)

In article <1991Apr25.212431.1109@am.sublink.org> alex@am.sublink.org (Alex Martelli) writes:
>2. if, when the shell is about to emit a primary-prompt to terminal, it
>   finds that a file named $HOME/..dothis exists, it opens, unlinks, 
>   and then sources it; this is how the shell accesses the results of
>   any history-like external command.

	SECURITY HOLE!!  Someone else could easlily write this file, and
the shell would execute their commands.  The shell should *at least*
enforce that ~/.doit be a) owned by the effective uid of the shell and b)
of mode 600 (or 700, since it *is* being executed, sort of).

Jamie  ...  Segmentation fault (core dumped)
Written On  Sunday, April 28, 1991  at  12:27:03am EDT

alex@am.sublink.org (Alex Martelli) (05/05/91)

jmason@gpu.utcs.utoronto.ca (Jamie Mason) writes:
	...
:In article <1991Apr25.212431.1109@am.sublink.org> alex@am.sublink.org (Alex Martelli) writes:
:>2. if, when the shell is about to emit a primary-prompt to terminal, it
:>   finds that a file named $HOME/..dothis exists, it opens, unlinks, 
:>   and then sources it; this is how the shell accesses the results of
:>   any history-like external command.
:
:	SECURITY HOLE!!  Someone else could easlily write this file, and
:the shell would execute their commands.  The shell should *at least*
:enforce that ~/.doit be a) owned by the effective uid of the shell and b)
:of mode 600 (or 700, since it *is* being executed, sort of).

'easily'?  ALL dot files in your home directory can be thought of as
"security holes" in this way - if you leave them writable (in general,
if you leave your home directory writable!), you're already asking
for big trouble, and, no, I don't think such mode-600ness is enforced
today for .profile, .exrc, .cshrc, .login, .rhosts, whatever $ENV
points at in ksh, and so on!
-- 
Alex Martelli - (home snailmail:) v. Barontini 27, 40138 Bologna, ITALIA
Email: (work:) martelli@cadlab.sublink.org, (home:) alex@am.sublink.org
Phone: (work:) ++39 (51) 371099, (home:) ++39 (51) 250434; 
Fax: ++39 (51) 366964 (work only), Fidonet: 332/401.3 (home only).

meissner@osf.org (Michael Meissner) (05/08/91)

In article <1991May05.003216.300@am.sublink.org> alex@am.sublink.org
(Alex Martelli) writes:

| 'easily'?  ALL dot files in your home directory can be thought of as
| "security holes" in this way - if you leave them writable (in general,
| if you leave your home directory writable!), you're already asking
| for big trouble, and, no, I don't think such mode-600ness is enforced
| today for .profile, .exrc, .cshrc, .login, .rhosts, whatever $ENV
| points at in ksh, and so on!

Of the files listed, only .rhosts should (and in fact must) be
protected by 0600.  I see no point in making the rest world
unreadable.  It helps newbies if I can point them to existing dot
files for examples of how to customize things (though of course my
personel dot files have been overcustomized over the years, and tend
to overwhelm).
--
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA, 02142

Considering the flames and intolerance, shouldn't USENET be spelled ABUSENET?