Baby X uses FileSystem XML to mount embedded data for processing by user programs. This creates a need for a few simple editing tools to manipulate the FileSystem XML files, and the temptation to write a UNIX style shell and add Basic scripting is irresistible. And the the result is a tiny, 100% portable shell which uses a FIleSystem XML file as backing store and is therefore techncially a tiny virtual computer in. its own right. And it has a serious use as a very flexible commnandline FileSystem XML editor, and it is great fun to play with and an excuse for implenting a shell from the ground up with no non-ANSI C system calls.
http://BabyXFS_shell.zip
Introduction
The Baby X FIleSystem shell is a portable shell written in ANSI C which is used for editing FileSystem XML files.
Baby X is a portable GUI system for X and MS Windows, and it is designed to be simple, lean, and easy to use for small or "baby" programs. And it also uses the Baby X resource compiler, babyxrc, to convert assets to comileable C source and include them in Baby X programs.
And one of the most common requirements is to embed a folder or directory in a program so that it can be accessed like a regular backing store disk, but shipped in the same file as the executable. And this functionality is sufficiently complicated to set up and generally usable to justify its own subsystem, Baby X FS. Just as the Baby X resource compiler will produce embedded portable C source dumps of images and other assets that can be used independently of Baby X, the Baby X FileSystem XML is built round an open XML format for hierarchical data, and it can be used in many programs.
FIleSystem XML is a simple, human-readable, human editable format for folders or directories. It has two child element types, directories and files. The files can be plain text, or they can be uu-encoded binary. And the hierarchy is implicit in the structure of XML itself. So an XML file is essentially an uncompressed .zip or .tar format file in an open, XML format. And it can be edited with a text editor like any XML file.
However the most powerful way to browse and edit it is with a shell-like interface. So that's the job of the shell program bbxfs_shell. It is invoked on a FileSystem XML file, and the regular terminal prompt is replaced by a BBX$ prompt, and there is suite of programs you can apply to navigate up and down the hierarchy, list files, copy and delete them.
And there is also a requirement for uploading files from the host operating system and downloading them, and for simple editing. And instead of trying to write a fully-featured editor, we borrow a command line editor from the host. And this works very well from the user's perspective, but is is diffcult to provide portably.
And a shell also runs other programs. And so as well as built in commands, there is also a way of adding new comands without touching the shell source files themselves, though they have to be compiled with the shell. And there's also a little Basic interpreter, so the shell is technically a little virtual computer in its own right. However the Baby Basic scripting is largely for fun, it's unlikely that anyone is going to want to write serious utilities for processing files in Baby Basic.
And there's also shell commandline expansion, and redirecting input and output. And everything has to be build from the ground up. No dependencies are allowed at all except for the C standad library. Which makes the project useful as a template for you own shell for a different project.
Background
A shell is very familiar to most programmers, as it is the primary interface to an operating system. And the shell does two main jobs. It maintains a current directory and allows the user to navigate through the directory system, maintaining a position and listing files. And it also allows the user to invoke programs on these files.
Now there is a fuzzy difference between core utilities which are designed to arrange data on the directory system, so commands to move, copy, and delete files, and useful programs which are designed to achieve whatever the computer exists for - e.g. playing video games, running databases, editing images. And the Baby X FileSystem shell doesn't really have such a purpose. But the shell is structured so that you can give it such a purpose. There's a concept of "internal", and "external" commands, and there's a scripting language, which is Baby Basic.
And you can add external commands without touching the core files, and the command "grep" is implemented like that. Or you can write little Baby Basic scripts to operate on batches of files. Or you can run the C "system
" subroutine call - though this runs programs under the host shell. Or you can dive into the code and it's fairly easy to add a new internal command. However the Baby Basic scripting will need improvement if it is to be used seriously. And the shell does minor piping and redirection of stdin
and stdout
. And another common requirement is to do simple editing of text files in place. And the best way of doing this is not to try to write something like vi, but to invoke the host shell's editor. And so on program launch, the user gives the Baby X FileSystem shell the name of the host shell's commandline text editor to use.
Help is also very important. The Baby X resource compiler has been used to set up the help file resources . Every command should be listed and have a short help description.
Using the Baby X FileSystem Shell
The Baby X FileSystem shell is a program for running a shell over FileSystem XML files. It's the most powerful way of editing FileSystem XML files there is, because it is a little computer in its own right. And is is the jewel in the crown of the babyxfs_ suite of programs.
Usage
babyxfs_shell <filesystem.xml> <filename> [options]
The program accepts a FileSystem XML file as an argument, and
a path, and runs the shell over it.
options:
-editor <editor program>
Example:
babysfxs_shell poems.xml
babyxfs_shell poems.xml -editor vi
This will run a shell and mount poems.xml.
in the second case, the program "vi" is used to edit the
files.
And you should get a BBX$ prompt, and you are in the shell, and everything you do now is Baby X.
The shell commands
- help
- quit
- cd
- ls
- cp
- mv
- rm
- cat
- edit
- import
- export
- system
- edit
- bb
help
help invokes the internal help system.
quit
quit exits the shell and returns control to the host.
cd
cd changes directories.
ls
ls lists the files in a directory.
cp
cp copies files.
mv
mv moves files.
rm
rm removes files.
cat
cat prints out files.
edit
edit runs an interactive editor on files.
import
import uploads files from the host machine.
export
export downloads files to hthe host machine.
system
system runs a coomand on the host machine.
bb
bb invokes the BabyBasic intepreter.
Using the code
If you wish to modify or call the code you probably want to use the Baby X FiIeSystem shell as a toy, as a little computer in its own right to play with. And Baby X is primarily designed for the hobby programmer. And the shell is designed to be completely portable, and every single thing is implemented form the ground up with no dependencies other that the standard library. So it's a good basis for your own virtual microcomputer.
Adding an external commmand
To add an external command, use "grep" as the pattern.
shell = bbx_fs_shell(fs);
bbx_fs_shell_addcommand(shell, "hello", helloworld, 0);
bbx_fs_shell_addcommand(shell, "grep", babyxfs_grep_main, 0);
The subroutine bbx_fs_shell_addcommand takes a pointer to the shell object created, the name oof the command, and a function pointer back to the command, together with a context pointer to pass back to make this what we call a "closure".
Here's the function itself.
int babyxfs_grep_main(int argc, char **argv,FILE *out, FILE *in, FILE *err, BBX_FS_SHELL *shell, void *ptr)
{
FILE *fp;
char line[1024];
char pline[1024];
char errormessage[1024];
int length;
BBX_Options *opt;
char *pattern = 0;
char *fname = 0;
int i_flag;
int v_flag;
int n_flag;
int w_flag;
int c_flag;
int Nargs;
int count = 0;
int lineno = 0;
opt = bbx_options(argc, argv, "-ivnwc");
i_flag = bbx_options_get(opt, "-i", 0);
v_flag = bbx_options_get(opt, "-v", 0);
n_flag = bbx_options_get(opt, "-n", 0);
w_flag = bbx_options_get(opt, "-w", 0);
c_flag = bbx_options_get(opt, "-c", 0);
if (bbx_options_error(opt, errormessage, 1024))
{
fprintf(err, "%s\n", errormessage);
return 0;
}
Nargs = bbx_options_Nargs(opt);
if (Nargs != 2)
{
grep_usage(out);
return 0;
}
pattern = bbx_options_arg(opt, 0);
fname = bbx_options_arg(opt, 1);
bbx_options_kill(opt);
opt = 0;
if (i_flag)
pattoupper(pattern);
fp = bbx_fs_shell_fopen(shell, fname, "r");
if (!fp)
{
fprintf(err, "Can't open %s\n", argv[2]);
}
while (fgets(line, 1024, fp))
{
int m;
strcpy(pline, line);
if (i_flag)
strtoupper(pline);
if (w_flag)
m = matchword(pattern, pline, &length);
else
m = re_match(pattern, pline, &length);
lineno++;
if ( (v_flag && m < 0) || (!v_flag && m >= 0))
{
if (c_flag)
count++;
else
{
if (n_flag)
fprintf(out, "%d: ", lineno);
fprintf(out, "%s", line);
}
}
}
if (c_flag)
fprintf(out, "%d matching lines\n", count);
bbx_fs_shell_fclose(shell, fp);
return 0;
}
Effectively this is the "main" function for a standard ANSI C commandline program. The main difference is that we are passed a pointer to the BBX_FS_SHELL
object which we have to call to obtain fopen()
and fclose()
, because these files are in the FileSystem XML, they are not in regular native operating system space. And to support redirection and piping, we are passed stdin
and stdout
and stderr
.
stdin
is the main problem. You can't write Space Invaders on top of stdin
. And this is an obvious thing to do if you want to use the Baby X FileSystem shell as the basis for your own little computer. You will have to do something non-portable to get a usable interactive key up / key down event pair from the user. stdout
is easy to use. Just obtain form the user the screen character cell dimensions, and print out screens composed in a temporary buffer as a raster.
Using Baby Basic
BabyBasic is based on MiniBasic, which is a very simple, portable Basic interpreter. The code has been forked. And you can write little scripts which you can invoke, but you can't really do very mcuh as all you can write are little filters which take lines from stdin
and process them, echoing to stdout
. And you'll want to do more than that, and almost certainly you'll want to grab the keyboard and implement a kbhit()
.
And this is the direction for futre work on the shell. But there's a question mark over how serious it is. The shell is intended as a program that the user can use to browse and rearrange and trivially edit FIleSystem XML files. It belongs in the FiIeSystem XML project, and improving the scripting is unlikely to really help a serious user. He will just extact the FileSystem XML to a directory on his own host system, edit it will all the facilities of a modern hosted operating system, and wrap it back up.
And playing with a script engine is a fun thing to do.
Points of Interest
I have never actually written a shell before. The challenge is to write it in pure ANSI C, so that it will runa nywhere there is a C compiler and three streams for stdin
, stdout
and stderr
. Snd it's a nice mix between serious code - just another comandline utility for looking at FIleSystem XML files - and extremely enjoyable coding up of one's own little computer. And doing it simply, robustly, and solidly.
History
Keep a running update of any changes or improvements you've made here.