autoprint command, however, it is useful to define a
function to print the current line number and contents:
lineout () {
Echo -n "$lineno: "
$dd if=$ed.c 2>/dev/null
}
The variable autoprint will contain the name of a function
to automatically call after some commands. This can be set to
lineout to turn autoprint on, to true to do
nothing (or to any other function which doesn't do anything useful),
or to whatever other function we might need when extending the editor.
The current file is in the variable name, while
state can have values "closed", "open",
or "changed": they mean that there is no current file,
that there is one and any changes have been saved, and that there
is one with unsaved changed, respectively.
The variables are initialised as follows:
state=closed name= autoprint=trueThe main program is a loop which reads a command, with a large
case
statement to execute it. We use "command:state" in the case, so we
can easily distinuish conditions like closing an unopened file, and so on.
The loop is:
while true
do
Echo -n '> '
read cmd arg
case "$cmd:$state" in
.... cases here ....
# empty commands are ignored
:*) ;;
# anything else produces an error
*) Echo "Command not understood" ;;
esac
done
Each command requires one or more case label, depending on whether we
want to distinguish between different states. They are shown below.
The loop would never terminate, but we can always use exit.
After checking if there are unsaved changes, of course:
quit:changed) Echo "Use 'save' or 'discard'" ;;
quit:*) Echo "bye"; exit;;
"Open" and "new" check that there isn't a current file
already, and set the current file to the file they open. The only difference
between them is that "new" ignores the return value from the
function open, while "open" gives an error
message if the file could not be opened.
open:open) Echo "There is a file open already" ;;
open:*) if open "$arg"
then state=open; name="$arg"; $autoprint
else Echo "Cannot open $arg"
fi
;;
new:open) Echo "There is a file open already" ;;
new:*) open "$arg"
state=open
name="$arg"
$autoprint
;;
The "close" command does not save changes. However, it will
refuse to close a file if there are unsaved changes, so the user can
call "save" or "discard"
close:changed) Echo "Use 'discard' or 'save'" ;;
close:closed) Echo "Closed already" ;;
close:*) state=closed ;;
"save" and "discard" only need to check if there
is an open file - it might be faster (less slow?) to avoid saving a
file if there weren't unsaved changes, but I don't think it is
worth the extra line and I'm too lazy to add it anyway. If "save"
is called with an argument, it uses that name instead of the current
file name. This is another reason to always save - you can implement
a slow copy command calling the editor and redirecting the standard
input from a file containing:
open source_file
save destination_file
quit
The code for these two operation is rather simple:
save:closed) Echo "There isn't a file to save" ;;
save:*) case "$arg" in
?*) save "$arg" ;;
*) save "$name" ;;
esac
state=open
;;
discard:changed) Echo "Your problem!"; state=closed ;;
discard:*) state=closed ;;
The "name" commands just needs to change the editor's idea
of the current file name:
name:closed) Echo "No current file" ;;
name:*) name="$arg" ;;
The line movement commands just need to call the functions supplied:
goto:closed) Echo "No current file" ;;
goto:*) goto "$arg"; $autoprint ;;
next:closed) Echo "No current file" ;;
next:*) next; $autoprint ;;
prev:closed) Echo "No current file" ;;
prev:*) prev; $autoprint ;;
The text manipulation commands just call the given functions. Note
that $arg is unquoted in "replace" and
"nreplace", which means that the shell will split the
arguments for you - it also means that you can't have spaces in
any argument except the last one.
replace:closed) Echo "No current file" ;;
replace:*) if rstring 1 $arg
then state=changed; $autoprint
else Echo "Not found"
fi
;;
nreplace:closed) Echo "No current file" ;;
nreplace:*) if rstring $arg
then state=changed; $autoprint
else Echo "Not found"
fi
;;
delete:closed) Echo "No current file" ;;
delete:*) delete; state=changed; $autoprint ;;
insert:closed) Echo "No current file" ;;
insert:*) insert "$arg"; prev; state=changed; $autoprint ;;
To print the current line, we use the function lineout we defined
earlier; the commands "autoprint" and "noprint" just
need to assign to the variable "autoprint".
print:closed) Echo "No current file" ;;
print:*) lineout ;;
autoprint:*) autoprint="lineout" ;;
noprint:*) autoprint="true" ;;
People who are paranoid about the system crashing on their editing might want
to add:
paranoid:*) autoprint='eval save "$name.autosave"' ;;
The command "paranoid" asks the editor to save the current file
with the suffix .autosave appended after every command which
might change the text, and even after most commands which cannot change
it (but just in case...)