CLIte/clite/commands.js

906 lines
18 KiB
JavaScript

clite.commands.data = function() {
// insert commands below this line
clite.commands.load('help',function(args,env,io) {
var stdio = io.include('stdio');
if (args.length > 1) {
// TODO: implement command help lookup
stdio.printf('help with arguments is a work in progress');
}
stdio.printf(`
Welcome to CLIte (pronounced like 'site' with an added L).
Various unix-like commands are available for navigating the CLIte shell:
ls - list directory contents
cat - concatenate files and print to std output or local file
touch - create a new file
less - simple text file viewer
file - determines the file type
Under Development:
view - file viewer
edit - text file editor
Running any command with the argument -? will give you help for that program.
Or you can run help <command> to get the same info.
As a basic guide for guests:
Use ls to see what content is available, eg: ls web
Use view or less to view the content, eg: view web/about
`);
return 0;
});
clite.commands.load('ls',function(args,env,io) {
var dirs = [];
var long = false;
var all = false;
var stdlib = io.include('stdlib');
var stdio = io.include('stdio');
var clite = io.include('clite');
function help() {
stdio.printf(`
ls - list directory contents
Usage: ls [OPTION] [FILE]
Options:
-? Print this help information
-l Display data in long format
-a Do not ignore entries starting with .
`);
}
function writeNodeData(st,fd) {
if (long) {
if (st.type == stdio.types.FT_LINK) {
var d = stdio.readAll(fd);
if (!d)
d = '?';
stdio.printf(st.perms+'\t'+st.uid+'\t'+st.gid+'\t'+st.name+' -> '+d);
}else{
stdio.printf(st.perms+'\t'+st.uid+'\t'+st.gid+'\t'+st.name);
}
}else{
stdio.printf(st.name);
}
}
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
case 'l':
long = true;
break;
case 'a':
all = true;
break;
default:
stdio.write(io.stderr,'unknown argument: -'+args[i][j]);
}
}
}else{
dirs.push(clite.resolvePath(args[i],false));
}
}
if (dirs.length < 1)
dirs.push(env.PWD);
dirs.forEach(function(dir,index) {
if (dirs.length > 1)
stdio.printf(dir+':');
var fd = stdio.open(dir,false);
if (!fd) {
stdio.write(io.stderr,'cannot open directory: '+dir);
return;
}
var st = stdio.fstat(fd);
if (!st) {
stdio.write(io.stderr,'cannot read file: '+dir);
stdio.close(fd);
return;
}
if (st.type != stdio.types.FT_DIR) {
writeNodeData(st,fd);
stdio.close(fd);
return;
}
var e;
while ((e = stdio.read(fd)) != null) {
if (e[0] == '.' && !all)
continue;
var efd = stdio.open(dir+'/'+e,false,true);
if (!efd) {
stdio.write(io.stderr,'cannot read contents: '+dir);
break;
}
var est = stdio.fstat(efd);
if (!est) {
stdio.close(efd);
stdio.write(io.stderr,'cannot read content: '+dir);
break;
}
writeNodeData(est,efd);
stdio.close(efd);
}
stdio.close(fd);
});
return 0;
});
clite.commands.load('cat',function(args,env,io) {
var files = [];
var download = false;
var remote = true;
var pending = 0;
var stdlib = io.include('stdlib');
var stdio = io.include('stdio');
var clite = io.include('clite');
function help() {
stdio.printf(`
cat - concatenate files and print to std output or local file
Usage: cat [OPTION] <FILE> [FILE]
Options:
-? Print this help information
-d Download the file data, instead of printing it
-l Only display files if remote data is loaded
`);
}
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
case 'd':
download = true;
break;
case 'l':
remote = false;
break;
default:
stdio.write(io.stderr,'unknown argument: -'+args[i][j]);
}
}
}else{
files.push(clite.resolvePath(args[i],false));
}
}
if (files.length < 1)
return 1;
pending = files.length;
// check if we're on a webserver
if (!remote) {
var u = stdlib.uname();
if (u && u.nodename != 'localhost')
remote = true;
}
function fcb(fd) {
var st = stdio.fstat(fd);
// check if it's a directory or link
if (
!st
|| st.type == stdio.types.FT_DIR
|| st.type == stdio.types.FT_LINK
|| st.type == stdio.types.FT_BINARY
|| st.type == stdio.types.FT_UNKOWN
|| st.type == stdio.types.FT_IMAGE
) {
if (st) {
stdio.write(io.stderr,'not a normal file: '+st.name);
}else{
stdio.write(io.stderr,'not a normal file');
}
}else{
var d = stdio.readAll(fd);
if (d != null) {
if (download) {
var ld = stdio.open('/dev/local',false);
if (ld) {
stdio.write(ld,{name:st.name,data:d});
stdio.close(ld);
}else{
stdio.write(io.stderr,'can not write to local device');
}
}else{
stdio.printf(d);
}
}
}
pending--;
stdio.close(fd);
if (pending <1)
io.exit(0);
}
files.forEach(function(file,index) {
var st = stdio.stat(file);
// check if it's a directory or link
if (
!st
|| st.type == stdio.types.FT_DIR
|| st.type == stdio.types.FT_LINK
|| st.type == stdio.types.FT_BINARY
|| st.type == stdio.types.FT_UNKOWN
|| st.type == stdio.types.FT_IMAGE
) {
stdio.write(io.stderr,'not a normal file: '+file);
pending--;
return;
}
if (!remote && st.type == stdio.types.FT_REMOTE) {
pending--;
return;
}
var fd = stdio.open(file,fcb);
if (!fd) {
stdio.write(io.stderr,'cannot open file: '+file);
pending--;
return;
}
});
if (pending <1)
return 0;
return null;
});
clite.commands.load('touch',function(args,env,io) {
var file = null;
var stdlib = io.include('stdlib');
var stdio = io.include('stdio');
var clite = io.include('clite');
function help() {
stdio.printf(`
touch - create a new empty file
Usage: touch [OPTION] <FILE>
Options:
-? Print this help information
`);
}
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
default:
stdio.write(io.stderr,'unknown argument: -'+args[i][j]);
}
}
}else if (file == null) {
file = clite.resolvePath(args[i],false);
}else{
stdio.write(io.stderr,'unknown argument: '+args[i]);
}
}
if (file == null) {
stdio.write(io.stderr,'no file specified');
return 1;
}
function loadRemote(fd) {
if (!fd) {
stdio.write(io.stderr,'unable to create file: '+file);
io.exit(1);
return;
}
io.exit(0);
}
if (!stdio.creat(file)) {
var fd = stdio.open(file,loadRemote);
return null;
}
return 0;
});
clite.commands.load('less',function(args,env,io) {
var stdlib = io.include('stdlib');
var stdio = io.include('stdio');
var term = io.include('term');
var curses = io.include('curses');
var clite = io.include('clite');
var file = null;
function help() {
stdio.printf(`
less - text file viewer
Usage: less [OPTION] <FILE>
Options:
-? Print this help information
`);
}
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
default:
stdio.write(io.stderr,'unknown argument: -'+args[i][j]);
}
}
}else if (file == null) {
file = clite.resolvePath(args[i],false);
}else{
stdio.write(io.stderr,'unknown argument: '+args[i]);
}
}
var cols;
var rows;
var lines = [];
var topLine = 0;
var bottomLine = 0;
function showAt(line) {
var start = line;
var end = line+rows;
if (end > lines.length)
end = lines.length;
curses.clear();
for (var i=start; i<end; i++) {
curses.printw(lines[i]+'\n');
}
}
function prepFile(fd) {
if (!fd) {
stdio.write(io.stderr,'could not open file: '+file);
io.exit(1);
return;
}
var st = stdio.fstat(fd);
if ( !st || (st.type != stdio.types.FT_TEXT && st.type != stdio.types.FT_SCRIPT)) {
stdio.write(io.stderr,'can not read files of this type');
io.exit(1);
return;
}
var txt = stdio.readAll(fd);
stdio.close(fd);
var parts = txt.split('\n');
parts.forEach(function(line) {
if (line.length > cols) {
var sects = line.match(new RegExp('(.{1,'+cols+'})','g'));
lines.push.apply(lines,sects);
return;
}
lines.push(line);
});
bottomLine = (lines.length-rows);
showAt(0);
}
var fd = null;
if (file == null) {
if (stdio.isatty(io.stdin)) {
stdio.write(io.stderr,'no file specified');
return 1;
}
fd = io.stdin;
}else{
fd = stdio.open(file,prepFile);
if (!fd) {
stdio.write(io.stderr,'could not open file: '+file);
return 1;
}
}
curses.initscr();
cols = curses.getmaxx();
rows = curses.getmaxy();
curses.cbreak();
curses.noecho();
curses.printw('Loading...');
function input(key) {
switch (key) {
case 'Escape':
case 'q':
curses.endwin();
io.exit(0);
return;
break;
case 'Up':
case 'ArrowUp':
if (topLine > 0) {
topLine--;
showAt(topLine);
}
break;
case 'Down':
case 'ArrowDown':
if (topLine < bottomLine) {
topLine++;
showAt(topLine);
}
break;
case 'PageUp':
if (topLine > 0) {
topLine -= rows-1;
if (topLine < 0)
topLine = 0;
showAt(topLine);
}
break;
case 'PageDown':
if (topLine < bottomLine) {
topLine += rows-1;
if (topLine >= bottomLine)
topLine = bottomLine;
showAt(topLine);
}
break;
case 'Home':
topLine = 0;
showAt(topLine);
break;
case 'End':
topLine = bottomLine;
showAt(topLine);
break;
}
curses.getch(input);
}
curses.getch(input);
return null;
});
clite.commands.load('view',function(args,env,io) {
stdio.printf('view is under development');
return 0;
});
clite.commands.load('rm',function(args,env,io) {
var short = null;
var file = null;
var stdio = io.include('stdio');
var clite = io.include('clite');
var recurse = false;
var force = false;
function help() {
stdio.printf(`
rm - remove files or directories
Usage: rm [OPTION] <FILE>
Options:
-r Remove directories and their contents recusively
-f Ignore nonexistant files and arguments
-? Print this help information
`);
}
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case 'r':
recurse = true;
break;
case 'f':
force = true;
break;
case '?':
help();
return 0;
break;
default:
stdio.fprintf(io.stderr,'unknown argument: -%c\n',args[i][j]);
}
}
}else if (file == null) {
short = args[i];
file = clite.resolvePath(args[i],false);
}else{
stdio.fprintf(io.stderr,'unknown argument: %s\n',args[i]);
}
}
if (file == null) {
stdio.fprintf(io.stderr,'no file specified');
return 1;
}
function removeFile(path) {
var st = stdio.stat(path);
if (!st) {
if (force)
return 0;
stdio.fprintf(io.stderr,'no such file or directory: %s',path);
return 1;
}
var fd = stdio.open(path,false,true);
if (!fd) {
stdio.fprintf(io.stderr,'permission denied: %s',path);
return 1;
}
if (st.type == stdio.types.FT_DIR) {
if (!recurse) {
var i = stdio.read(fd);
if (i) {
stdio.close(fd);
stdio.fprintf(io.stderr,'cannot remove non-empty directory: %s',path);
return 1;
}
}else{
var f;
while ((f = stdio.read(fd)) != null) {
if (removeFile(path+'/'+f)) {
stdio.close(fd);
return 1;
}
}
}
}
stdio.close(fd);
if (st.type == stdio.types.FT_DEV) {
stdio.fprintf(io.stderr,'cannot remove device files: %s',path);
return 1;
}
if (!stdio.remove(path)) {
stdio.fprintf(io.stderr,'permission denied: %s',path);
return 1;
}
return 0;
}
return removeFile(file);
});
clite.commands.load('mkdir',function(args,env,io) {
var short = null;
var file = null;
var stdio = io.include('stdio');
var stdlib = io.include('stdlib');
var clite = io.include('clite');
var recurse = false;
function help() {
stdio.printf(`
mkdir - remove files or directories
Usage: mkdir [OPTION] <DIRECTORY>
Options:
-p Make the full path, not just the final directory
-? Print this help information
`);
}
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case 'p':
recurse = true;
break;
case '?':
help();
return 0;
break;
default:
stdio.fprintf(io.stderr,'unknown argument: -%c\n',args[i][j]);
}
}
}else if (file == null) {
short = args[i];
file = clite.resolvePath(args[i],false);
}else{
stdio.fprintf(io.stderr,'unknown argument: %s\n',args[i]);
}
}
if (file == null) {
stdio.fprintf(io.stderr,'no path specified');
return 1;
}
var st = stdio.stat(file);
if (st) {
stdio.fprintf(io.stderr,'already exists: %s\n',short);
return 1;
}
if (!stdio.mkdir(file)) {
if (!recurse) {
stdio.fprintf(io.stderr,'persmission denied: %s\n',short);
return 1;
}
var parts = file.split('/');
var path = '';
for (var i=0; i<parts.length; i++) {
path += '/'+parts[i];
st = stdio.stat(path);
if (st) {
if (st.type != stdio.types.FT_DIR) {
stdio.fprintf(io.stderr,'permission denied: %s',path);
return 1;
}
continue;
}
if (!stdio.mkdir(path)) {
stdio.fprintf(io.stderr,'permission denied: %s',path);
return 1;
}
}
}
return 0;
});
clite.commands.load('file',function(args,env,io) {
var short = '';
var file = null;
var stdlib = io.include('stdlib');
var stdio = io.include('stdio');
var clite = io.include('clite');
function help() {
stdio.printf(`
file - determine the file type
Usage: file [OPTION] <FILE>
Options:
-? Print this help information
`);
}
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
default:
stdio.write(io.stderr,'unknown argument: -'+args[i][j]);
}
}
}else if (file == null) {
short = args[i];
file = clite.resolvePath(args[i],false);
}else{
stdio.write(io.stderr,'unknown argument: '+args[i]);
}
}
if (file == null) {
stdio.write(io.stderr,'no file specified');
return 1;
}
var fd = stdio.open(file,false);
if (!fd) {
stdio.write(io.stderr,'cannot open file: '+file);
return 1;
}
var st = stdio.fstat(fd);
stdio.close(fd);
var txt = short+': ';
if (!st) {
txt += 'Unknown';
}else{
switch (st.type) {
case stdio.types.FT_TEXT:
txt += 'Plain Text';
break;
case stdio.types.FT_BINARY:
txt += 'CLIte Executable';
break;
case stdio.types.FT_DIR:
txt += 'Directory';
break;
case stdio.types.FT_LINK:
txt += 'Link';
break;
case stdio.types.FT_DEV:
txt += 'Device File';
break;
case stdio.types.FT_REMOTE:
txt += 'Unloaded Remote Data';
break;
case stdio.types.FT_SCRIPT:
txt += 'Shell Script';
break;
case stdio.types.FT_IMAGE:
txt += 'Image';
break;
case stdio.types.FT_LIBRARY:
txt += 'Shared Object';
break;
default:
txt += 'Unknown Data';
break;
}
}
stdio.printf(txt);
return 0;
});
clite.commands.load('reboot',function(args,env,io) {
var stdio = io.include('stdio');
function help() {
stdio.printf(`
reboot - reboot the system, forcing reload of all code
Usage: reboot [OPTION]
Options:
-? Print this help information
`);
}
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
default:
stdio.write(io.stderr,'unknown argument: -'+args[i][j]);
}
}
}else{
stdio.write(io.stderr,'unknown argument: '+args[i]);
}
}
var fd = stdio.open('/dev/initctl',false);
if (!fd)
return 1;
stdio.write(fd,'6'); // switch to runlevel 6, reboot
stdio.close(fd);
return 0;
});
clite.commands.load('uname',function(args,env,io) {
var stdio = io.include('stdio');
var stdlib = io.include('stdlib');
var sysname = false;
var nodename = false;
var release = false;
var version = false;
var machine = false;
function help() {
stdio.printf(`
uname - print system information
Usage: uname [OPTION]
Options:
-a All, equivalent to -mnrsv
-m Print the hardware info (User Agent)
-n Print the network name
-r Print the operating system release
-s Print the operating system name
-v Print the operating system version
-? Print this help information
`);
}
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
case 'a':
machine = true;
nodename = true;
release = true;
sysname = true;
version = true;
break;
case 'm':
machine = true;
break;
case 'n':
nodename = true;
break;
case 'r':
release = true;
break;
case 's':
sysname = true;
break;
case 'v':
version = true;
break;
default:
stdio.write(io.stderr,'unknown argument: -'+args[i][j]);
}
}
}else{
stdio.write(io.stderr,'unknown argument: '+args[i]);
}
}
if (!machine && !nodename && !release && !sysname && !version)
sysname = true;
var ut = stdlib.uname();
var txt = '';
if (sysname)
txt += ut.sysname+' ';
if (nodename)
txt += ut.nodename+' ';
if (release)
txt += ut.release+' ';
if (version)
txt += ut.version+' ';
if (machine)
txt += ut.machine;
stdio.printf(txt);
return 0;
});
if (window.location.protocol == 'file:')
clite.commands.load('test',function(args,env,io) {
var stdio = io.include('stdio');
var stdlib = io.include('stdlib');
stdio.printf('Hello world');
stdio.printf('uid:%d',stdlib.getuid());
stdio.printf('%#15x:%15o:%15.5f:%0$15.5f:%%:',10,12,8.123456789);
function rcb(data) {
stdio.printf(data);
io.exit(0);
}
stdio.read(io.stdin,rcb);
return null;
});
// insert commands above this line
}