mirror of
https://codeberg.org/TicklishHoneyBee/CLIte.git
synced 2026-03-11 09:04:37 +00:00
515 lines
17 KiB
Text
515 lines
17 KiB
Text
|
|
Welcome to CLIte (pronounced like 'site' with an added L, or like 'slight').
|
|
|
|
CLIte is an experimental javascript project based on answering the
|
|
question no one asked: "What if a website worked like a Unix Terminal?"
|
|
|
|
CLIte is licensed under the GNU Affero General Public License, V3.
|
|
Though this may change.
|
|
|
|
A functioning test instance of CLIte is available at
|
|
http://www.ltmnet.com/clite/index.html
|
|
CLite has also been designed, with some work-arounds and bugs, to work
|
|
locally without a web server. Just download or clone the git repository
|
|
locally and open index.html in your browser.
|
|
|
|
The following commands are currently available in the CLIte shell:
|
|
|
|
help - a simple help command for new users, gives a basic introduction
|
|
to "What do I do now?"
|
|
|
|
ls - list directory contents, accepts the -l argument for showing
|
|
output in long form, with more detail.
|
|
|
|
cat - prints files to either the terminal, or with the -d argument
|
|
to the local device (downloads the file)
|
|
|
|
touch - create a new file, or load a file's remote data
|
|
|
|
less - simple text file viewer, up and down arrows will scroll the
|
|
text, q or escape to exit
|
|
|
|
file - determines the file type
|
|
|
|
reboot - reboots the system, this will force-reload all javascript, so
|
|
helps get around issues with browser cache
|
|
|
|
uname - prints system information
|
|
|
|
|
|
Under Development:
|
|
view - file viewer, should display files something like the way a
|
|
text mode browser would, so is for actually viewing the website
|
|
content
|
|
edit - text file editor, will probably work similar to nano, unless
|
|
it ends up being a vi clone
|
|
|
|
Running any command with the argument -? will give you help for that
|
|
program.
|
|
|
|
The shell supports argument in "quotes" and 'single quotes', as well as
|
|
$VARIABLES (environment variables include PATH, USER, PWD, and HOME).
|
|
Support for `command substitution` is in progress.
|
|
Some tabfill support is functional, but more work is needed to tabfill
|
|
paths.
|
|
Shell paths support tilde (~) for the user's home directory, as well as
|
|
dot (.) and dot dot (..) for current and parent directories. The
|
|
asterisk (*) wildcard is not currently supported
|
|
The shell maintains a command history, use up and down arrows to access.
|
|
Piping and io redirects are not currently supported, yet.
|
|
The following shell builtin commands or macros are available:
|
|
|
|
clear - clears the terminal window
|
|
cd - changes the present working directory
|
|
pwd - prints the present working directory (equivalent to `echo $PWD')
|
|
echo - prints text or variables
|
|
which - prints the full path of a command: `which ls' - > "ls is /bin/ls"
|
|
type - similar to which, but also tells if a command is a shell builtin
|
|
whoami - prints the current username (equivalent to `echo $USER')
|
|
export - without arguments: will print all environment variables and their
|
|
values.
|
|
- with argument: allows an environment variable to be changed or
|
|
created. `export FOO=bar'
|
|
|
|
|
|
Command details and API:
|
|
|
|
Commands are currently all contained in clite/commands.js and are loaded in
|
|
at runtime. Each command looks something like:
|
|
|
|
|
|
clite.commands.load('name',function(args,env,io) {
|
|
io.write("Hello World!");
|
|
return 0;
|
|
}
|
|
|
|
The load function is only available during boot time, and will:
|
|
1. load the program into the vfs at /bin/name
|
|
2. load the source of the program into the vfs at /usr/src/name.js
|
|
|
|
The function in the second argument is roughly equivalent to main() in C.
|
|
This function takes 3 arguments:
|
|
'args' is equivalent to argv in C, being an array of strings containing
|
|
the command line arguments, args[0] is the command itself, args.length
|
|
is equivalent to argc in C.
|
|
'env' contains the current environment variables: env.PWD contains the
|
|
present working directory, and so on.
|
|
'io' contains file descriptors for accessing standard input, output,
|
|
and error, as well as a method for loading in libraries:
|
|
|
|
io.stdout
|
|
file descriptor for standard output
|
|
|
|
io.stderr
|
|
file descriptor for standard error
|
|
|
|
io.stdin
|
|
file descriptor for standard input
|
|
|
|
Note that writting to stdout or stderr, when it is a tty, will
|
|
currently always print that output as a line, with a newline
|
|
appended. This may change in the future.
|
|
|
|
io.exit(value) exits the program, equivalent to the C exit() function.
|
|
A program can also be exited by returning a non-null value from the 'main'
|
|
function.
|
|
|
|
io.include('name') loads a library into the current scope for use. See
|
|
below for more details.
|
|
|
|
|
|
Libraries:
|
|
|
|
System libraries may be loaded into a program for use of their API using the
|
|
io.include() function:
|
|
|
|
var stdio = io.include('stdio');
|
|
|
|
This returns a reference to the library which can be stored in a variable as
|
|
seen above. Calls to library functions can then be made using that reference:
|
|
|
|
stdio.open('/path/to/file');
|
|
|
|
There is no need to name the variable the same as the library name, however
|
|
this is considered good practice.
|
|
|
|
The libraries, and their API functions are listed below:
|
|
|
|
stdlib: io.include('stdlib')
|
|
Provides a growing standard unix-like library.
|
|
|
|
basename('path')
|
|
returns the base name of a file path:
|
|
'/usr/home/guest/file.txt' -> 'file.txt'
|
|
|
|
dirname('path')
|
|
returns the directory name of a file path:
|
|
'/usr/home/guest/file.txt' -> '/usr/home/guest'
|
|
|
|
resolvePath('path','base')
|
|
special function that resolves a relative path to a full path,
|
|
using the present working directory or 'base':
|
|
resolvePath('file.txt') -> '/usr/home/guest/file.txt'
|
|
resolvePath('file.txt','/etc') -> '/etc/file.txt'
|
|
resolvePath('~/../file.txt') -> '/usr/home/file.txt'
|
|
|
|
strToArgs('string')
|
|
special function that splits a string into an array of arguments
|
|
for passing to exec(), supports quotes and so on:
|
|
'ls -l /var' -> ['ls','-l','var']
|
|
|
|
uname()
|
|
returns an object containing system information:
|
|
{
|
|
sysname:'CLIte', // system name, always 'CLIte'
|
|
nodename:'localhost', // network hostname
|
|
release:'0.1...', // contains the current CLIte version as stored in clite.state.version
|
|
version:'0.1...', // same as release
|
|
machine:navigator.userAgent // contains the browser user agent string
|
|
}
|
|
|
|
nodename is either the website domain, or 'localhost' if loaded
|
|
without a webserver. This allows programs to test if the system
|
|
is running locally or not (example is `cat -l' which does not
|
|
print unloaded files if running without a webserver).
|
|
|
|
fork(env,io,call)
|
|
Creates a new process, with environment and io data passed to it.
|
|
Returns the pid of the new process, or 0 on failure.
|
|
call should be a function that accepts the env and io as
|
|
arguments, this is where the new process begins.
|
|
|
|
function newProc(env,io) {
|
|
io.write('this is a new process!');
|
|
io.exit(0);
|
|
}
|
|
var pid = fork(env,io,newProc);
|
|
|
|
exec(path,args,env,io)
|
|
Executes a new program, replacing the current one.
|
|
Returns 0 on success, non-zero on failure.
|
|
Unlike typical unix exec(), this always returns. On success the
|
|
original program should do nothing more, including not exiting.
|
|
|
|
path is the fully resolved file path of the program to be
|
|
executed, such as '/bin/ls'.
|
|
args is the argument array created by passing a command line to
|
|
strToArgs, see above.
|
|
env and io, are the current environment and io data.
|
|
|
|
var command = "ls -l";
|
|
var args = stdlib.strToArgs(command);
|
|
var path = stdlib.resolvePath(args[0],'/bin');
|
|
var r = stdlib.exec(path,args,env,io);
|
|
if (r == 0)
|
|
return;
|
|
|
|
wait(cb)
|
|
Calls cb(pid) once any child of the current process has exited.
|
|
Calls immediately if the are no child processes.
|
|
Returns false on error.
|
|
|
|
waitpid(pid,cb)
|
|
Calls cb(pid) when the process with id pid has exited. Calls
|
|
immediately if the process does not exist.
|
|
If pid is less than 0, functions like wait(cb)
|
|
Returns false on error.
|
|
|
|
waitall(cb)
|
|
Calls cb(pid) once all child processes of the current process
|
|
group have exited. Calls immediately if the are no child processes.
|
|
Returns false on error.
|
|
|
|
getuid()
|
|
Returns the numeric user id of the current user.
|
|
|
|
getgid()
|
|
Returns teh numeric group id of the current user.
|
|
|
|
|
|
stdio: io.include('stdio')
|
|
Provides access to io functions and types for file access
|
|
|
|
stdio.types:
|
|
object for mapping values of stat.type:
|
|
FT_UNKOWN: 0 Unknown file type
|
|
FT_TEXT: 1 Plain text file
|
|
FT_BINARY: 2 Binary file, likely a javascript function
|
|
FT_DIR: 3 Directory
|
|
FT_LINK: 4 Symbolic link
|
|
FT_DEV: 5 Device
|
|
FT_REMOTE: 6 Unloaded remote data (will change after loading)
|
|
FT_SCRIPT: 7 Plain text file beginning with #!
|
|
FT_IMAGE: 8 Image file, specifically a javascript Image object
|
|
|
|
creat('path','-')
|
|
creates a new file at path
|
|
returns true on success
|
|
|
|
open('path',callback,open_link)
|
|
Open the file at path, returns a file descriptor.
|
|
On error returns null, and calls callback(null) if set.
|
|
If callback is set, will call the function at callback with the
|
|
file descriptor, this allows remote data to be loaded for the
|
|
file before beginning read or write operations.
|
|
If callback is false, open will return directly, without loading
|
|
remote data.
|
|
If open_link is set and true, and 'path' is a symbolic link, the
|
|
returned file descriptor is for the link, not the file pointed to.
|
|
|
|
var fd = stdio.open('path',false); // opens the file without loading data
|
|
var fd = stdio.open('path',callback); // calls callback(fd) when data is loaded
|
|
var fd = stdio.open('path',callback,true); // as above, but will not follow a link
|
|
var fd = stdio.open('path'); // as a general rule, don't do this
|
|
|
|
close(fd)
|
|
Closes a file opened with open()
|
|
|
|
stdio.close(fd);
|
|
|
|
read(fd,callback)
|
|
Reads a single character or keystroke from a file.
|
|
callback is an optional callback function, used soley for
|
|
asynchronously reading from a tty.
|
|
Returns null if there is no data to read.
|
|
|
|
When reading from a regular file, will return a single character.
|
|
When reading from a tty:
|
|
Returns true on success or false on failure.
|
|
Calls callback() and passes as an argument either:
|
|
A full line of text as entered by the user.
|
|
\1 (start of header) followed by a special key
|
|
name (such as ArrowUp).
|
|
null if the tty cannot be read from.
|
|
|
|
var c = stdio.read(fd);
|
|
|
|
readLine(fd,callback)
|
|
Reads a line from a file, up to the next newline, or end of file.
|
|
Returns null if there is no data to read.
|
|
|
|
When reading from a tty, functions the same as read().
|
|
|
|
var line = stdio.readLine(fd);
|
|
|
|
readAll(fd)
|
|
Returns the entire content of a file.
|
|
Returns null if there is no data to read.
|
|
Works only on regular files (and some non-tty devices).
|
|
|
|
var data = stdio.readAll(fd);
|
|
|
|
write(fd,data)
|
|
Write data to a file.
|
|
Returns true on success.
|
|
|
|
if (stdio.write(fd,'string')) {
|
|
// it worked
|
|
}else{
|
|
// it failed
|
|
}
|
|
|
|
ftruncate(fd,length)
|
|
truncate('path',length)
|
|
Truncates a file's size to no more than length.
|
|
Does not increase a file's size to length.
|
|
Returns true on success.
|
|
|
|
var result = stdio.ftruncate(fd,10);
|
|
var result = stdio.truncate('/usr/home/guest/file.txt',10);
|
|
|
|
seek(fd,pos)
|
|
Moves the read/write position of an open file to pos.
|
|
If pos is less than 0, then returns the current position without
|
|
changing, otherwise returns the new position.
|
|
pos is always relative to the start of the file. When a file is
|
|
first opened, pos will be set to the start of the file.
|
|
To set the position to the end of a file, first get the file size
|
|
using stat() or fstat().
|
|
|
|
var p = stdio.seek(fd,10);
|
|
|
|
remove('path')
|
|
Deletes a file or directory.
|
|
Returns true on success.
|
|
To delete a directory, the directory must be empty.
|
|
|
|
if (stdio.remove('/usr/home/guest/file.txt'))
|
|
// success!
|
|
|
|
link('path','target')
|
|
Creates a new symbolic link at 'path' which points to 'target'.
|
|
'target' must exist.
|
|
If 'path' exists, and is already a link, will update the link.
|
|
Returns true on success.
|
|
|
|
if (stdio.link('/usr/home/guest/logfile','/vr/logs'))
|
|
// success!
|
|
|
|
stat('path')
|
|
fstat(fd)
|
|
Returns a stat object with infomation about a file.
|
|
Returns null on error.
|
|
Editing the returned object does not change anything for the
|
|
actual file, it just means your stat object is now wrong.
|
|
|
|
var st = stdio.stat('/usr/home/guest/file.txt');
|
|
var st = stdio.fstat(fd);
|
|
|
|
Stat object contents:
|
|
st.name: string containing the file name
|
|
st.type: file type identifier, see stdio.types above for more info
|
|
st.uid: numeric id of the file owner
|
|
st.gid: numeric id of the file group
|
|
st.size: file size, or 0 for non text files
|
|
st.perms: the permissions string for the file, see chmod below.
|
|
|
|
chmod('path','mode')
|
|
fchmod(fd,'mode')
|
|
Change a file's mode (permissions).
|
|
Returns true on success.
|
|
|
|
The mode string is a 9 or 10 character string describing the
|
|
file permissions. The optional first character describes the
|
|
file type, and cannot be changed. Attempting to change the first
|
|
character will not cause the function to fail, but only the
|
|
permissions will be changed.
|
|
|
|
10 character string: -rwxrwxrwx
|
|
9 character string: rwxrwxrwx
|
|
|
|
After the option first character, the mode string is comprised of
|
|
3 sets of permissions for Read, Write, and eXecute, one each for
|
|
the user, group, and others.
|
|
|
|
rwx permissions for the file's owner
|
|
rwx permissions for users in the file's group
|
|
rwx permissions for other users
|
|
|
|
Replacing any of the permissions with a dash '-' will remove that
|
|
permission from that set:
|
|
|
|
rwxr-xr--
|
|
The user has all permissions, the group has read and execute
|
|
permissions, others have only read permissions.
|
|
|
|
isattty(fd)
|
|
returns true if fd refers to a tty.
|
|
|
|
fprintf(fd,format,args)
|
|
Print formatted text to the file at fd.
|
|
Actually formatting is a work in progress, ignore the args.
|
|
Returns true on success.
|
|
|
|
printf(fmt,args)
|
|
Equivalent to fprintf(io.stdout,fmt,args);
|
|
|
|
term: io.include('term')
|
|
Provides access to raw terminal and tty functions.
|
|
|
|
clear()
|
|
Clears the current terminal, equivalent to running `clear' from
|
|
the shell.
|
|
|
|
opentty()
|
|
Creates a new blank terminal (tty) with raw key events, for
|
|
custom displays and interactions. (The less command uses this).
|
|
Returns a reference object for interacting with the new tty.
|
|
Returns null on error.
|
|
|
|
var tty = term.opentty();
|
|
|
|
closetty(tty)
|
|
Closes a tty created with opentty().
|
|
|
|
ttyctrl(func,v)
|
|
Special function for setting and accessing CLIte-specific tty
|
|
data. Specifically for interacting directly with the form used
|
|
for user data input.
|
|
func is a string containing the intended function.
|
|
v is the value to set.
|
|
Returns either the requested data, or false on error.
|
|
|
|
term.ttyctrl('iset',string); // sets the current input value
|
|
var txt = term.ttyctrl('iget'); // gets the current input value
|
|
term.ttyctrl('prompt',string); // sets the current prompt text
|
|
|
|
|
|
Example Programs:
|
|
Due to the nature of javascript, it is not possible to simply stop half
|
|
way through a function to wait for user input or for some remote data
|
|
to load. Instead we have to use a callback function, which complicates
|
|
things a little, and means there are two kinds of programs: Syncronous,
|
|
and Asyncronous.
|
|
|
|
Syncronous Program:
|
|
A Syncronous program runs and then exits, with no waiting for callbacks.
|
|
As such, it looks much like a regular unix program might, and simply
|
|
returns with an exit code.
|
|
Here's a "Hello World" as an example:
|
|
|
|
clite.commands.load('hello',function(args,env,io) {
|
|
io.write("Hello World!");
|
|
return 0;
|
|
}
|
|
|
|
Asyncronous Program:
|
|
An Asycronous program typically uses io calls to interact with data
|
|
that may not be immediately available; such as remotely loading file
|
|
data or reading input from a user. As such callbacks are needed to
|
|
handle that data once it is available.
|
|
Therefore a return null is used, which lets the system know it is an
|
|
asyncronous program that will exit later using the io.exit() function.
|
|
Here's a simple program that reads in a file, and prints it to stdout:
|
|
|
|
clite.commands.load('show',function(args,env,io) {
|
|
var stdlib = io.include('stdlib');
|
|
var stdio = io.include('stdio');
|
|
|
|
// check there's a file to read from
|
|
if (args.length != 2) {
|
|
io.error('Specify a file to read');
|
|
return 1; // not asyncronous yet, so just return
|
|
}
|
|
|
|
// take the argument, and get it's full path
|
|
var file = stdlib.resolvePath(args[1]);
|
|
|
|
// file callback function that will receive the file descriptor
|
|
// once the file has data in it
|
|
function fcb(fd) {
|
|
if (!fd) {
|
|
// print an error, exit the program, then end
|
|
io.error('could not open file');
|
|
io.exit(1);
|
|
return;
|
|
}
|
|
|
|
// read in the whole file in one go
|
|
var data = stdio.readAll(fd);
|
|
// close the file
|
|
stdio.close(fd);
|
|
|
|
// check there's something there
|
|
if (!data) {
|
|
// print an error, exit the program, then end
|
|
io.error('could not read file');
|
|
io.exit(1);
|
|
return;
|
|
}
|
|
|
|
// write to stdout
|
|
io.write(data);
|
|
|
|
// and exit successfully
|
|
io.exit(0);
|
|
}
|
|
|
|
// open the file, and set the callback
|
|
var fd = stdio.open(file,fcb);
|
|
|
|
// we don't want to exit the program yet,
|
|
// so return null to let the system know that the
|
|
// program is asyncronous (reads user data, or loads remote data)
|
|
return null;
|
|
}
|