home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
m
/
msh21.zip
/
MSHFILES.ZIP
/
REFMAN.DOC
< prev
next >
Wrap
Text File
|
1992-08-19
|
46KB
|
855 lines
Mi-Shell Reference Manual
Jean Michel
Version 2.10 --- 19 August 1992
Introduction:
MSH is a ``shell'' which provides a convenient and configurable way to
browse through directories and execute commands on files and sets of files
by a ``point and shoot'' interface. In contrast to many popular shells,
MSH is designed to be particularly useful to a ``power user;'' there is
no limit to the number of shortcuts in everyday work she can implement
with it: an arbitrary sequence of actions (which is written as an ``MSH
script'') may be assigned to each keyboard key, and may depend on the
environment (like the file currently pointed to, or its extension, etc...).
Of course, MSH is also useful to the average user.
Installation:
Installation of MSH is simple. You only have to copy MSH.EXE to
some directory in your PATH (the directories which are scanned by MS-DOS1
to look for commands), and copy the script files (with extension .MSH)
to the same directory. You should then modify the file CONFIG.MSH to
indicate your display type, the drives you can access, whether you own
The Berkeley Utilities or not, which directory to use for swap files, which
editor and browser you want to use, etc... In the file there is a series of
lines of the form:
| in the line below, replace "c_blue.msh" by the name
| of one of the available files describing attributes:
| c_mda.msh for monochrome displays
| c_pcga.msh for plasma cga displays
| c_blue.msh, c_blue1.msh,
| c_blue2.msh, c_white.msh various possibilities
| for color displays
|
"c_blue.msh"loaddefs | load blue color
| set attributes
you should do at it says, e.g. replace c_blue by c_mda if you have
a monochrome display. You can define your own colors for MSH; see the
appendix on attributes.
You now modify the following two lines to define your available drives:
"CD" fixed_drives! | define your fixed drives
"AB"floppy_drives! | define your floppy drives
Similarly, there are these 3 lines in the file:
true delta!
true berk!
true berk_cpmvrm!
----------------------------
1 MS-DOS is a trademark of Microsoft Corporation
If you do not own DELTA, you should replace true by false on the
first line and similarly for the second and third if you do not own
The Berkeley Utilities. The third line refers to whether you want to use
cp, mv and rm or DOS's copy and delete commands for these functions.
Finally, there are lines reading:
| next line defines the place for "magic" swap files
(prog_dir)tmp_dir!
| put swap files in same directory as MSH.EXE
They specify the directory to use for swap files (see ``Technical highlight
no. 2''; used when MSH cannot swap itself to extended memory). This
should be a virtual disk or a fast hard disk if you have one. You can set
tmp_dir to the empty string ("") to disable swapping to disk.
Also, MSH does not provide a built-in editor: the idea is rather to
let you use your own. If you do not have one, we include MWE.EXE, a
multi-window editor which was developed as a demo of the power of TextLib,
a keyboard and screen management C Library distributed by OPENetwork.
Similarly, you may use your own browser instead of the built-in one (it
will be slower). Of course, you must specify what it is to MSH; so, in
CONFIG.MSH you must adapt the lines which say:
"mwe "editor!
"more "browser!
to specify what is your editor and browser; for instance, if you use
the mediocre but widespread Wordstar as an editor, and the excellent
public-domain browser list by V.Buerg, you may change the above lines to:
"ws "editor!
"list "browser!
Some people have such a good editor, or like their bad one so much, that
they will use it as their browser. If your editor or browser does not
work with the simple syntax ``edit filename'' or needs extra arguments or
parameters after the filename, you can still manage, but you will have to
make more complex modifications to CONFIG.MSH.
Starting to use the default configuration
You can then start MSH just by typing its name. The initial display
you get should be: a menu bar at the top above two panels, each containing
a display of the files in your current directory, and a list of available
drives. All this is completely configurable. We describe here the
behavior of MSH when you don't modify the CONFIG.MSH file.
We support extended video modes and extended versions of ANSI.SYS. The
initial number of lines of a panel will be half the number of total lines
of your display(e.g. in 43-lines EGA mode, you will get 21 lines, and 25 in
the 50-lines VGA mode instead of the usual 12 in a 25-lines mode; if you
have a 132-columns mode you will get 3 panels, and 1 panel in a 40-columns
mode). If you have a VGA or EGA card, you can ask MSH to first switch the
screen to given number n of lines by giving the option -ln on the command
line (the letter ell); for instance msh -l50 asks MSH to switch the screen
to 50-line mode before starting. The only values of n currently understood
are 25, 43 and 50.
The first line of a panel contains a ``pattern'', i.e. a wildcard
describing all files in the current directory at the left (e.g.
c:\dos\*.*). This is highlighted if the panel is the active panel. At
the bottom of a panel are titles for the various columns of the display
(Name.ext, size, time) which are highlighted according to the current sort
order of the panel.
Most of your interaction with MSH is via what you type on the command
line. MSH monitors all your keystrokes and decides what to do with them
according to your own instructions given via the CONFIG.MSH file (the
default configuration sets things so that you can also execute Mi-Shell
script instructions directly from the command line with the key AltX).
MSH when started always tries to locate the file CONFIG.MSH in the
directory where MSH.EXE is located, unless you give it an explicit filename
as argument. Two tutorials are on-line when you hit the F1 key to help you
get started.
The Mi-Shell script language:
The configuration of MSH is done by its reading upon start the file
CONFIG.MSH. This file is entirely in the MSH script language. Any text
following a non-quoted | up to the end of a line is a comment and is
ignored. The rest of the text represents instructions in the MSH script
language, which is a small Forth-like stack language.
As we proceed to describe the language, you can find some useful
examples which might help in the section ``quick start at modifying
CONFIG.MSH''.
The only elements of the language are " which delimits character
strings, brackets () which delimit blocks of code, and identifiers which
are either words formed from the characters a--z A--Z 0--9 or dot (.)
or underscore (_) or dollar ($), or symbols constituted by any other
single character. Identifiers represent primitive or user-defined actions
(their value is then a block of code), or variables holding character
strings, excepted that identifiers starting with a digit are interpreted as
character strings as if they were quoted. This is to allow you to write
numbers without having to surround them with " (negative numbers like "-1"
still have to be quoted). A number is just a particular string whose
content is interpretable as a number, and all string operation apply to it;
only the arithmetic operations try to interpret their string arguments as
numbers. Below are some strings:
"abc", "you can put '\"' in a string", 3, "3".
The last 2 strings are the same. Below are some identifiers
foo, bar, foo_bar, +, !, f31.
The language is a stack language like FORTH. The stack contains
either character strings or pieces (blocks) of code. In the subsequent
descriptions [top] represents the stack top, [top+1] the next element,
etc...; push something is the action of pushing something onto the stack.
Actions typically read one or more values by popping them from the stack
(reading [top] always unstacks it) and return possible results by pushing
them on the stack. Some actions are predicate-like: the logical value
false is represented by the empty string and true by any other string.
The variables false and true are primitives and contain the values "" and
"true" respectively. Variables which hold the value true or false will be
called boolean in what follows.
A character string is written "string" where internal `"' must be
quoted by preceding them with \; a character string may span several lines
and contain embedded carriage returns. Merely writing a character string
pushes it onto the stack. Other identifiers cause their corresponding
actions to be executed or their value to be pushed when encountered, except
that a sequence of actions (or character string denotations) between `()'
is pushed onto the stack as a piece of code instead.
A character string or a piece of code may be assigned to an identifier
by the action !; e.g., "hello" "H"! or ("")"false"!. In BASIC, this would
be written as H = "hello" or as
false = "". As a special syntactic convention, the form name! is
equivalent to "name"!, so the above examples may be written "hello"H! and
("")false!.
We should mention that some variables are ``system variables'': like
primitives, they are defined by the system. Some of them can be set by the
system as well as the user, and when the user sets them some sytem-defined
action results. An example is cwd, the current working directory: setting
its value will change the current directory.
Also, as a special facility, a variable name beginning with a `$'
represents a MS-DOS system variable (such as PATH or COMSPEC); the name
must be in uppercase letters to be recognized as a system variable.
Assigning to such variables will modify the MS-DOS environment. For
instance:
$PATH ";\mydir"& $PATH!
can be used to modify the PATH. Beware! Only the local copy of the
environment owned by MSH will be modified (which means that you will lose
your modifications to the environment when leaving MSH; we might provide a
way around that in future versions).
Some identifiers represent the name of keys, and they are called
whenever the corresponding key is pressed. The definable keys and their
names are:
Function keys F1--F10 ShiftF1--ShiftF10
CtrlF1--CtrlF10 AltF1--AltF10
Control keys CtrlA--CtrlZ
Ctrl Break is always a ``break'' key, used to interrupt
actions (like get out of loops)
``Alt'' keys AltA--AltZ Alt0--Alt9 AltMinus AltEqual
Other keys Home End Left Right PgDn PgUp Down Up
Delete Insert BackSpace
CtrlHome CtrlEnd CtrlLeft CtrlRight
CtrlPgDn CtrlPgUp
ShiftDown ShiftUp ShiftLeft ShiftRight Esc Tab BackTab
Enter CtrlEnter
GreyPlus GreyMinus GreyStar CtrlPrint
We now proceed to list the primitive actions, arranged by category.
``Language'' functions:
drop Do nothing with [top] (and so just unstack it).
dup Duplicate [top].
pick [top] should be some number n. If these are the objects on
the stack, starting from the bottom:
O(n), O(n-1) ... O(2) O(1) n
then after the command the stack will look like:
O(n) O(n-1) ... O(2) O(1) O(n)
so that 1 pick is equivalent to dup.
swap Exchange [top] and [top+1].
roll [top] should be some number n. If objects on top of stack
are:
O(n), O(n-1) ... O(2) O(1) n
after the command they will be:
O(n-1) ... O(2) O(1) O(n)
so that 2 roll is equivalent to swap.
stack_size push as a number the size of the stack.
not Push false if [top] is not false, true otherwise.
= If [top]=[top+1], push [top] else push false.
& push the catenation of [top] and [top+1].
match If [top] matches [top+1], push [top] else push false.
cutfirst Cuts [top+1] at [top], that is look for first occurence of
string [top] in [top+1], and replace [top] and [top+1] by
the part of [top+1] after [top] and the part of [top+1]
before [top] (the first may be empty if [top] does not occur
in [top+1]).
cutlast Like cutfirst, but looks for last occurence of [top] in
[top+1].
length push the length of the string [top].
substring extract from [top+2] the substring which starts with the
[top+1]th character and ends with the [top]th character, and
push it on the stack.
if If [top+1] is not false execute [top] else do nothing.
ifnot If [top+1] is false execute [top] else do nothing.
ifelse If [top+2] is false execute [top] else execute [top+1].
while While execution of [top+1] does not yield false, execute
[top].
# Interpret the string [top] as a piece of code and execute
it.
@ Interpret [top] as an identifier name and replace it by
its value. If the value is a piece of code, it is
``uncompiled'' as a character string.
isdefined Interpret [top] as an identifier name. Push true if this
identifier has been assigned a value, false otherwise.
! Interpret [top] as an identifier name and assign [top+1] to
it.
+ Push [top+1]+[top](interpreted as numbers).
- Push [top+1]-[top](interpreted as numbers).
* Push [top+1]*[top](interpreted as numbers).
/ Push [top+1]/[top](interpreted as numbers).
< Push true if [top+1]<[top](interpreted as numbers), false
otherwise.
le Push true if [top+1]<=[top](interpreted as numbers), false
otherwise.
> Push "true" if [top+1]>[top](interpreted as numbers), false
otherwise.
ge Push "true" if [top+1]>=[top](interpreted as numbers), false
otherwise.
debug System variable. If "true", MSH is in ``debug'' mode:
before any identifier is executed, a window pops up showing
it and the current state of the stack.
Panels, menus, windows and mouse support:
MSH lets you manipulate several kinds of windows:
- Panels, wich are ``directory browsers''. You get two of them in
the default configuration. You can move them around, change their
attributes, what they display, keys associated to them, etc...These
are ``permanent'' windows (they don't disappear when you have finished
interacting with them). Permanent windows have numbers (``handles'')
associated with them on creation. You may use these numbers to
reactivate them (put them in front, ready to receive your keystrokes)
later (windows can also be activated by clicking on them with the
mouse).
- Menus, which are actually general ``hypertext'' windows where fields
in the window have actions associated to them. They can be either
permanent (they have then numbers associated to them) or temporary
(they disappear when you make a choice).
- Message, choice and dialog windows. The error windows that the system
displays sometimes are of a similar kind. These are temporary windows.
A message just displays some text. A dialog window displays some text
and waits for your input. A choice window allows you to give a Yes/No
answer. A menu could do the same job but this provides a simpler
solution.
At present, mouse support is limited to actions done on clicks and
double clicks. More complex interaction (like drags) are not yet
implemented, mostly because of the complexity of the necessary language
support to describe them.
Panels functions:
panel.install Creates a new panel. Only one panel displaying
nothing (with number 1) exists at the start. Other
panels must be created with this command. One
argument must be given on [top], the initial selection
pattern of the newly created panel. The current
panel is set to the created panel. Other attributes
of a panel are set/modified with the following system
variables. panel.install pushes one result, the
number assigned to the panel.
panel.pattern System variable. Represents the `pattern' of the
current panel (the selection pattern displayed at the
top of the panel). Setting this variable causes
the panel to be refreshed by reading from disk the
list of files matching the pattern (the father and
subdirectories of the current directory are always
displayed). As a special facility, this can be
assigned a pathname composed of the pathname of a
``.zip'' or ``.arj'' archive file + a pattern. The
panel will then display the list of all files inside
that archive which correspond to the given pattern.
We might add support for other kinds of archives
(.lzh, .arc, .zoo) in the future.
panel.type Returns the type of the current panel: "" for
ordinary panels, ".zip" for a zip archive, ".arj" for
an arj archive.
panel.sort System variable. Represents the sort order of the
current panel. It can be assigned the following
values:
"N" --- sort the panel by name;
"S" --- sort the panel by size;
"T" --- sort the panel by date;
"E" --- sort the panel by extension. Sorting by extension
keeps as a secondary key the previous sort, whatever
it was. This is reflected in the highlighted titles
at the bottom of the panel.It also means that in the
case of an extension sort, the value of panel.sort is
actually one of "EN", "ES" or "ET".
panel.size Boolean system variable which controls whether the
file size is displayed for each file of the current
panel.
panel.attrs Like panel.size, controls whether the read/write/system--
/hidden attributes are displayed.
panel.date Like panel.size, controls whether the last modified
date is displayed.
panel.time Like panel.size, controls whether the last modified
time is displayed.
panel.sec Like panel.size, controls whether the last modified
time is displayed up to the second.
panel.on Boolean system variable. Controls whether the current
panel is on. When off, the panel won't be displayed;
this is useful to have temporarily an unimpeded view
of the screen behind the panel (in addition to the
panels, the menu bar can be shut off by uninstalling
it; this allows the use of MSH as a ``silent''
background task).
panel.rc System variable representing the number of rows and
columns of the current panel. When read causes 2
values to be pushed on the stack, first the number of
lines and then the number of columns. Similarly it
should be set by pushing 2 values on the stack (e.g.
12 40 panel.rc!).
panel.startrc System variable. Represents the start row and start
column of the current panel (the coordinates of the
top left corner).
panel.nbfiles push the total number of entries of the current panel.
panel.att Function taking 4 arguments representing the video
attributes used to display the pattern. They should
be given in ANSI format and represent:
Normal --- the normal attribute for files in a panel;
Current --- the attribute of current name, the file
pointed to by the cursor;
Selected --- the attribute of selected files;
Selected + Current --- the attribute of current name
when it happens to be a selected file.
current.name push the filename of the entry pointed by the cursor
in the current panel (this entry will be called
`current name' in the subsequent descriptions).
current.isdir push false if current name is not a directory,
otherwise push current name (usable as a predicate).
current.size push the size (in bytes) of current name.
current.time push the last modified time of current name. This
time is encoded as an integer by the formula:
2x(seconds+
32x(minutes+
64x(hours+
32x(days+
32x(months+
16x(years-1980))))))
current.selected System variable whose value is true or false. Setting
it to true selects current name; this highlights
it and marks it as ``selected'' which means that
current.selected will return true until it is again
deselected by setting current.selected to false.
current.pos System variable which represents the position of the
cursor in the current panel relative to the total
number of files. Setting this variable moves the
cursor. Values outside of the interval [0,panel.nb-
files-1] are mapped to 0 or panel.nbfiles-1.
Mouse actions:
User-defined mouse actions on a panel are provided as follows: when a
mouse event on the panel is detected:
- It is activated if it was not active (and panel.on_activate (which
should contain an action) is executed).
- A two-part name is built and is executed (it should contain an action);
- The first part of the name describes the event:
LeftClick, LeftDbleClick, RightClick, RightDbleClick, MiddleClick,
MiddleDbleClick.
- The end part of the name describes the area of the panel where the
event occured:
N --- On the word ``name'' at the bottom of the panel.
E --- On the word ``ext''.
S --- On the word ``size''.
T --- On the word ``time''.
UA --- On the up arrow of the scroll bar.
DA --- On the down arrow of the scroll bar.
Entry --- On an entry. In that case before executing the name the
number of the entry is pushed.
Menu functions:
The only function proper to menus is menu.install. The way a menu
works is by executing actions (pieces of code) associated to fields in its
window (``menu selections''). When a menu is active, the cursor keys move
between menu selections. The currently highlighted selection is executed
when pressing the key Enter or when clicking with the mouse on it. An
Escape or clicking outside a selection field quits the menu without any
selection being done. The arguments to menu.install describe completely
the menu. They are in order:
- A list of actions (pieces of code) to be executed for various menu
selections.
- A string, the text of the menu window. Fields which should operate as
menu selections are delimited by % in that string (a % can be put in
the menu by doubling it: %%). The fields are matched to actions in
order. There should be exactly as many actions as there are fields.
- An action which is executed when the menu is exited without any
selection made (often the empty action () is suitable). This will
occur when the user has activated a menu and then hits ESC.
- The coordinates of where to put the upper left corner of the menu
window (a "-1" means center the menu in that direction).
- Three video attributes given as ANSI strings, to use for displaying the
menu:
Normal --- the attribute for normal text in the menu window;
Selection --- the attribute of a menu selection;
Highlighted --- the attribute of the highlighted menu selection;
- A string which determines what kind of border the menu should have, and
if it should have a title:
"" --- no border.
":" --- single line border.
"v:" --- single line horizontal / double line vertical border.
"h:" --- single line vertical /double line horizontal border.
"vh:" --- double line vertical /double line horizontal border.
To specify a title add it to the string. For example, "v:Hello"
specifies a border with double vertical lines, and single horizontal lines,
with 'Hello' as title.
- A boolean value wich determines if the menu should be permanent.
For a permanent menu, menu.install returns a value, the number assigned
to the menu.
Permanent Windows Control:
Two functions control permanent windows:
window.activate Takes as argument the number of the window to activate
(panel or menu). An active menu is recognized by
having a higlighted selection. An active panel has
a highlighted title (and if any files are displayed,
current name is highlighted). When activating a
panel, the action panel.on_activate is executed. This
can be set by the user; in the default configuration
what it does is attach to the directory of the panel.
An active window is put on top of the others.
window.uninstall Takes as argument the number of the window to delete;
deletes that window (menu or panel).
It is sometimes convenient to give consecutive numbers to a series of
panels or menus (the functions next_panel and previous_panel of the default
configuration do that). The number given to a newly created window is
the first available number, so this can be arranged by just creating the
windows consecutively when there has been no deletions.
Other Windows:
message Display [top] as a message with title [top+1]. This message
is left displayed until any key is typed. If the text of the
message does not fit on the screen, it is shown screenful by
screenful until the end. This can provide a primitive kind
of browser.
flash Display [top] as a message with title [top+1]. This message
is left displayed for the number of milliseconds specified by
[top+2].
ok Display [top] as a message and ask confirmation with a Yes No
choice. Replaces [top] by false in case of non confirmation
(so may be used as a predicate).
input Display [top+1] as a message and ask for input in a field of
length [top]. The input is pushed on the stack.
get_key Display [top] as a message with title [top+1]. Return as a
character string the name of the first key typed.
alert_att System variable whose value represents (in ANSI format) the
color attribute to be used for the message, flash, ok, input
and get_key windows. (``alert'' refers to the temporary
``pop-up'' windows of MSH: the ones mentioned above, the
warnings and the debug window).
Command line:
cmd.del Delete the current character in the command line.
cmd.pos System variable representing the position of the cursor
in the command line (first position is 0).
cmd.wordleft Go one word left in the command line.
cmd.wordright Go one word right in the command line.
cmd.clear Reset to zero the command line.
cmd System variable representing the command line. Reading
it pushes the command line contents. Setting it writes
[top] to the command line; but note that the text is
added to the command line where the cursor is in it.
Call cmd.clear first to replace the command line contents
by new contents.
cmd.imode Boolean system variable representing if the command line
is in insert mode.
Other:
read Read the file whose name is [top] and push as a string
its contents.
write Writes (creates or appends) to the file whose name is
[top+1] the string [top].
unlink removes file whose name is [top].
rename renames file whose name is [top+1] to name given by
[top]. As for the DOS call, it is possible to rename
empty directories, and to move files between directories
of the same drive with this call. pushes [top] if
successful, false otherwise (e.g. target exists or is in
a different drive).
copy copies file whose name is [top+1] to name given by [top].
[top] will be overwritten if it exists; copy works across
different drives. pushes [top] if successful, false
otherwise (e.g. if there was a disk full on the target
drive).
testfile Returns false if there exists no file with name [top]
(which includes the case when a directory with name [top]
exists), otherwise pushes the size and last modified time
(see format in current.time) of found file. This routine
can test if a file exists inside a supported archive
type.
tempname Creates a temporary file name (a name guaranteed to be a
non existent file) in the directory specified by [top].
disk_stats pushes the following information on the drive whose
letter is given by [top] (if [top] is "" returns
information on current drive): size of a cluster in
bytes, number of available clusters, total number of
clusters.
capture_screenGrabs the contents of the screen as a string and push
it on the stack. Takes one boolean argument: if
true, makes a graphic capture, encoding video attributes
as ansi escape sequences, which allows to redisplay
the screen colors using the built-in browser of MSH or
MS-DOS's ANSI.SYS.
prog_pathname push the pathname of msh.exe.
cwd System variable holding the name of the current
directory. Setting it attaches to the directory whose
name is [top].
linescols System variable holding the total number of lines and
columns on the screen. It may be used in conjunction
with panel.rc and panel.startrc to program a setup for
the screen.
execute Send [top] to command.com to be executed.
magic System variable. If not false, should specify a
directory where MSH will swap itself out of memory before
sending any command to MS-DOS for execution if it cannot
swap itself to extended memory. As a special convention,
if it starts with a `*', it means that swapping to XMS is
not desired. See ``Technical highlight no. 2''.
page Call the built-in browser. It takes 9 arguments: the
name of the file to browse followed by the coordinates
of the window for the browser (start row, start column,
number of rows, number of colums) and by the list of
video attributes to use in the browser:
Browser window --- the attribute of the window of the
browser.
Browser highlight --- the attribute of the status line
and of highlighted text in the browser.
Empty --- the attribute used when showing empty space in
the browser.
Tab --- the attribute used when showing tabs in the
browser.
beep As its name says.
time push the current time (in the same format as
current.time).
timer MSH has a ``timer'' which allows you to do some actions
at regular times (which may be used to show a clock) or
after some delay (which may be used to set an alarm).
[top] should contain the action to do (as a piece of
code) and [top+1] a number specifying how many ticks
(1=17 sec.) from now this action should be done. To
do an action repeatedly, you can write an action which
contains a call to set the timer with itself, e.g.
after (f 1000 (g)timer)g! a call to g will execute f
repeatedly every 1000 ticks.
reinit_mouse With some versions of the Microsoft mouse driver and
some mouse programs, the mouse driver in MSH may become
inactivated when returning from execute. This command
reinitializes it (it is not done systematically since it
takes 1 to 2 seconds).
mouse.click System variable. Holds the interval between the 2 clicks
of a mouse double click. By default its value is 3 which
means 3 ticks (ie 3 seventeenths of asec.). You may want
this to be a bit slower if you are not a Mac user ...
quit Quit MSH.
A quick start at modifying .MSH files:
The first thing you might want to do is add to EXT.MSH instructions
to deal with other types of files, recognized by their extension. For
instance, I use Knuth's TeX text-processing language, which produces
device-independent output files ready to be printed or viewed on the
screen; what I want to do when I point to such a file, say foo.dvi,
and order MSH to act by pressing the Enter key, is to see what's
in it, i.e. call my previewer (called view) with a command such as
view foo.dvi. What I did in order to get this is add a line to the
series of lines in EXT.MSH which end by extension_something (a typical one:
(current.name pager)extension_cfg!) saying:
("view "current.name & execute)extension_dvi!
how does this work? When enter is pressed, by a mechanism described
below, MSH sees that the command line is empty and decides to execute the
identifier extension_xxx where xxx is the extension of current name. It
is the line above which gives a definition to the identifier extension_dvi.
The final ! is the assignment operator which assigns to extension_dvi
what's on the stack. What is there is the code in brackets at the
beginning of the line, which has been pushed by the simple virtue of
appearing there. What does this code do? First, it pushes the
string "view " on the stack, then current name, then catenates [top] and
[top+1](operator &); now [top] contains "view foo.dvi"; finally execute
sends that string to MS-DOS to be executed; you can watch the whole thing
in action by doing the following steps:
- Position cursor on foo.dvi and make sure the command line is empty.
- Press AltD. If your configuration is the default one, this make MSH
enter debug mode.
- Press Enter. You will then see the debugger window, which includes a
view of the stack. Press s to step through the code of extension_dvi.
- Press g (go) when in debug window to leave debug mode.
Let us now have a more elaborate text explanation by looking at the
code which is executed when pressing Enter. The code is the following
lines in KEYBIND.MSH:
(cmd
(execsave)
(act_on_cur)
ifelse
)Enter!
We first put the contents of the command line on the stack (cmd). To
understand what happens next you have to notice that the rest consists
of 2 blocks of code followed by ifelse. That's a perfectly ordinary
if construct, excepted that control structures don't always look nice in
reverse polish notation. The way ifelse works is: it expects 2 blocks of
code on the stack, and, above that, a value which is false (empty string)
or not (non-empty string), and execute one or the other piece of code based
on that value. The net effect here is to execute the first piece of code
if the command line is non-empty and the second otherwise.
The first piece is a definition:
(cmd dup cmdlist nl&swap&cmdlist!execute
cmd.clear refresh)execsave!
it recalls again the command line, duplicates it, uses one copy to store in
the circular list of commands cmdlist (where previously typed commands are
kept, separated by newlines (the content of the variable nl), for reuse if
recalled by the user), and executes the other copy; then clears the command
line and calls refresh; this last is itself a definition that you can find
in STDDEFS.MSH:
(cwd panel.pattern filepart makename panel.pattern!)
refresh!
The purpose is to refresh the display in the current panel, which might
be necessary since the executed command may have destroyed files and/or
changed directories. To do that, we first get the current directory's name
on the stack, then catenate to it the filename part of the wildcard of the
current panel (usually "*.*", but it may be set to something else), then
put that as the pattern to select files in the current panel. filepart and
makename are also definitions that we don't show here.
Let's look now at what we do if the command line is empty:
(current.isdir
(current.name cwd! refresh)
("extension_" current.name extension)
ifelse
)act_on_cur!
We have again an if which tests if current name is a directory or
not. current.isdir is a predicate, which as all predicates, by convention
returns one of the strings "" (false) or "true". If current name is a
directory, we attach to it (current.name cwd!) and refresh. Otherwise, we
construct an identifier extension_xxx where xxx is the extension of current
name and execute that (#). extension is defined as follows:
("."cutlast swap drop)extension!
we cut the name into its part before the "." and after it, and drop the
part before it.
The other thing I want to do with dvi files is turn them into HP
LaserJet output, which I do with dvihp.exe, and that second type of action
was associated with the CtrlEnter key.
Technical highlight number one: long command lines and The Berkeley Utili-
ties:
MSH supports (almost) arbitrarily long command lines for programs which
recognize its conventions (like The Berkeley Utilities). For instance, if
you use the BERK.MSH configuration with cp, mv, rm enabled, and select a
lot of files and press F6 (move), you will be asked to confirm a very long
command starting with mv, and if you agree mv will correctly move the whole
list to the next panel. This is quite advantageous compared to looping
on mv of each file, since mv is loaded only once, so the job is done
much faster. You may wonder how this is possible, since MS-DOS limits any
command line to 127 characters. We do this by a simple trick, which is
understood by all The Berkeley Utilities, and which you may make your own
programs understand! When the command line is greater than 127 characters,
we send only the first 127 to dos; but before that we put the rest in a
buffer, preceded by its length as a 2-byte integer, and we add to the
MS-DOS environment a variable CMD@=xx, where xx is the adress (as a long
decimal integer) of the buffer. The called program has just to check if
that variable exists, and if it is the case catenate to its command line
the rest of the command line gotten from the address that variable holds.
Here is some sample code which does just that; this should help you write
your own.
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <dos.h>
/* this program prints the command line under MSH,
adding the extra part at the end of the normal command.
This version gets the psp by the TURBO C built-in
variable _psp. You have to get it another way
if you use another compiler. */
int main()
{ unsigned cmdlen=*(char far *)((((long)_psp)<<16)+0x80),
extralen=0;
char *com;
int far *extra;
char *s=getenv("CMD@");
if(NULL!=s && (extra=(int far *)atol(s)))
extralen=*extra;
com=malloc(cmdlen+extralen+1);
movedata(_psp,0x81,
FP_SEG((char far *)com),
FP_OFF((char far *)com),cmdlen);
if(extralen)
{ extra++;
movedata(FP_SEG(extra),FP_OFF(extra),
FP_SEG((char far *)(com+cmdlen)),
FP_OFF((char far *)(com+cmdlen)),
extralen);
}
com[cmdlen+extralen]=0;
printf("command=<%s>\n",com);
return 0;
}
Technical highlight number two: large memory for your commands:
MSH, like a few advanced products, can let your commands use almost
all available memory in the system when executing. In order to do that,
MSH swaps itself out of memory, writing most of its image to disk or
to extended memory (if available) and leaving only a small (8K) image in
memory. If you have no (or not enough) extended memory and cannot swap to
a virtual disk (in low or expanded memory) this swapping takes some time so
you may want to disable it. It is under control of the variable magic: if
magic is false (the empty string), its default value, no swapping to disk
is done. Otherwise magic must contain the name of the directory to be
used for swapping (you may specify ``.'' for the current directory, but
we don't recommend it since you will then have problems on a diskette; you
should specify some directory on your hard disk or a virtual disk). If you
want to run a program which requires the use of all your extended memory
(this happens to be the case for Borland's BC++3.0 on my machine which has
only 2megs), you may want to disable swapping to XMS: this is done by the
special convention of starting the variable magic with a `*'.
Command line options:
In addition to the -l option explained above, there is another possible
option on the command line:
-m Disables mouse. If for some reason, you have a mouse but want MSH not
to use it, use this option.
How to add your own primitives coded in C to msh:
Read the file TOOLKIT.DOC which is in the toolkit.