import java.net.*;
import java.util.*;
import java.util.regex.*;
-import javax.servlet.*;
-import javax.servlet.http.*;
import org.ibex.util.*;
import org.ibex.util.Collections;
import org.ibex.js.*;
-import org.prevayler.*;
-
public class Shell {
public static void main(String[] args) throws Exception {
Shell shell = new Shell(new URL(args[0]));
if (args.length == 2) {
- System.out.println(shell.execute(args[1]));
+ // FIXME
} else {
shell.listen(new InputStreamReader(System.in), new OutputStreamWriter(System.out));
}
System.out.println("Usage: xish url [command]");
}
+ protected Command[] commands = new Command[] {
+ new LsCommand(),
+ new PwdCommand(),
+ new CdCommand(),
+ new HelpCommand()
+ };
+
/** URL of server. */
- private URL server;
+ protected URL server;
/** Current JS path using '.' as a seperator. */
- private String pwd = ".";
+ protected String pwd = ".";
/** Create a new Shell using the given url for the server. */
public Shell(URL url) { server = url; }
LineNumberReader in = new LineNumberReader(r);
PrintWriter out = new PrintWriter(w);
- out.println("Ibex xt shell. Type help or exit.");
+ out.println("ibex xt shell: type help or exit");
out.print("xt: ");
out.flush();
}
if (buffer.length() > 0) {
- execute(buffer, out);
+ String[] c = buffer.split(" ");
+ int i=0; while (i < commands.length) {
+ if (commands[i].name().equals(c[0])) {
+ commands[i].execute(out, c); break;
+ }
+ i++;
+ }
+ if (i == commands.length) {
+ out.write(c[0]);
+ w.write(": command not found\n");
+ }
buffer = "";
- } else {
- out.println("");
}
out.print("xt: ");
out.flush();
}
}
- public String execute(String cmd) throws IOException {
- StringWriter s = new StringWriter();
- execute(cmd, s);
- return s.toString();
+ private String cookie = null;
+ public Object send(Request request) throws IOException {
+ URLConnection c = server.openConnection();
+ ((HttpURLConnection)c).setRequestMethod("POST");
+ c.setDoOutput(true);
+ if (cookie != null) c.setRequestProperty("Cookie", cookie);
+
+ c.connect();
+
+ ObjectOutputStream out = new ObjectOutputStream(c.getOutputStream());
+ out.writeObject(request);
+ out.close();
+
+ String cook = c.getHeaderField("Set-Cookie");
+ if (cook != null && !cook.equals("")) cookie = cook.substring(0, cook.indexOf(';'));
+
+ try {
+ return new ObjectInputStream(c.getInputStream()).readObject();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ throw new IOException("unexpected ClassNotFoundException");
+ }
+ }
+
+ public abstract class Command {
+ /** Returns the command name. */
+ public abstract String name();
+
+ /** Returns single-line of usage information, eg. <tt>pattern]</tt> */
+ public abstract String usage();
+
+ /** Returns single-line description of command. */
+ public abstract String summary();
+
+ /** Returns multi-line description. */
+ public abstract String help();
+
+ /** Writes result of execution, even if result is an error. */
+ public abstract void execute(Writer w, String[] args) throws IOException;
}
- public void execute(String cmd, Writer w) throws IOException {
- if (cmd == null || cmd.length() == 0) return;
- String[] c = cmd.split(" ");
+ /** Returns the command matching the given name. */
+ protected Command command(String name) {
+ for (int i=0; i < commands.length; i++)
+ if (commands[i].name().equals(name)) return commands[i];
+ return null;
+ }
+
+ public class HelpCommand extends Command {
+ public String name() { return "help"; }
+ public String usage() { return "[command name]"; }
+ public String summary() { return "Lists available commands."; }
+ public String help() { return ""; }
+
+ public void execute(Writer w, String[] c) throws IOException {
+ if (c.length > 1) {
+ Command cmd = command(c[1]);
+ if (c == null) {
+ w.write("help: ");
+ w.write(c[1]);
+ w.write(": command not found\n");
+ } else {
+ w.write("usage: ");
+ w.write(cmd.name());
+ w.write(" ");
+ w.write(cmd.usage());
+ w.write("\n\n");
+ w.write(cmd.help());
+ w.write("\n");
+ }
+ } else {
+ int len = 3;
+ for (int i=0; i < commands.length; i++)
+ len = Math.max(commands[i].name().length(), len);
+
+ w.write("Available commands:\n");
+ for (int i=0; i < commands.length; i++) {
+ Command cmd = commands[i];
+ w.write(" ");
+ w.write(cmd.name());
+ for (int j=len - cmd.name().length(); j >= 0; j--) w.write(" ");
+ w.write(" - ");
+ w.write(cmd.summary());
+ w.write("\n");
+ }
+ w.write("\nFor usage details, type help [command name].\n");
+ }
+ }
+ }
+
+ public class LsCommand extends Command {
+ public String name() { return "ls"; }
+ public String usage() { return "[path]"; }
+ public String summary() { return "List object entries."; }
+ public String help() { return
+ "Lists the keys in an object. Modelled after the UNIX ls command.";
+ }
+
+ public void execute(Writer w, String[] c) throws IOException {
+ if (c.length > 2) { w.write(usage()); return; }
- //#switch(c[0])
- case "ls":
- if (c.length > 2) { w.write("usage: ls [path]\n"); return; }
String path = pwd;
String matcher = ".*";
if (c.length == 2) {
- int pos = c[1].lastIndexOf('/');
- path += c[1].substring(0, pos);
- path.replaceAll("/", "\\.");
- if (pos + 1 < c[1].length()) {
- matcher = c[1].substring(pos + 1);
- matcher.replaceAll("\\.", "\\.");
- matcher.replaceAll("\\*", ".*");
+ int pos = c[1].lastIndexOf('/') + 1;
+ path += "." + c[1].substring(0, pos);
+ path = path.replaceAll("/+", ".");
+ if (pos < c[1].length()) {
+ matcher = c[1].substring(pos);
+ matcher = matcher.replaceAll("\\.", "\\.");
+ matcher = matcher.replaceAll("\\*", ".*");
}
}
Object ret = send(new KeyRequest(path, matcher));
if (ret == null) {
- error(w, "returned object is null"); return;
+ w.write("error: (unexpected) returned object is null\n");
} else if (ret instanceof JSExn) {
- error(w, (JSExn)ret); return;
+ String e = ((JSExn)ret).getMessage();
+ // FIXME: messy way to get info from server
+ if (e.endsWith("does not exist")) {
+ w.write("ls ");
+ w.write(c[1]);
+ w.write(": no such path\n");
+ } else {
+ w.write("error: ");
+ w.write(e);
+ w.write("\n");
+ }
} else if (ret instanceof List) {
List l = (List)ret; Collections.sort(l);
Iterator i = l.iterator(); while (i.hasNext()) {
- w.write(" ");
w.write(i.next().toString());
w.write("\n");
}
- return;
} else {
- error(w, "returned object of unknown type: "+ret.getClass().getName());return;
+ w.write("error: (unexpected) returned object is of unknown type: ");
+ w.write(ret.getClass().getName());
+ w.write("\n");
}
- case "pwd":
- w.write(pwd.replace('.', '/'));
- w.write("\n");
- case "cd":
- throw new Error("not yet implemented");
- case "help":
- w.write("Available commands:\n");
- w.write(" ls - list entries in current object\n");
- w.write(" pwd - path of current object\n");
- w.write(" help - this message\n");
- w.write(" exit - close xt shell\n");
- //#end
- }
-
- public void error(Writer w, String s) throws IOException {
- w.write("Unexpected error: ");
- w.write(s);
- w.write("\n");
+ }
}
- public void error(Writer w, JSExn e) throws IOException {
- w.write("\nerror: ");
- w.write(e.getMessage());
- w.write('\n');
+ public class PwdCommand extends Command {
+ public String name() { return "pwd"; }
+ public String usage() { return ""; }
+ public String summary() { return "Path to current object."; }
+ public String help() { return "Print the path to the current object."; }
+ public void execute(Writer w, String[] c) throws IOException {
+ w.write(c.length == 1 ? pwd.replace('.', '/') : usage());
+ w.write("\n");
+ }
}
- public Object send(Request request) throws IOException {
- URLConnection c = server.openConnection();
- ((HttpURLConnection)c).setRequestMethod("POST");
- c.setDoOutput(true);
- c.connect();
-
- ObjectOutputStream out = new ObjectOutputStream(c.getOutputStream());
- out.writeObject(request);
- out.close();
-
- try {
- return new ObjectInputStream(c.getInputStream()).readObject();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- throw new IOException("unexpected ClassNotFoundException");
+ public class CdCommand extends Command {
+ public String name() { return "cd"; }
+ public String usage() { return "[path]"; }
+ public String summary() { return "Change current object."; }
+ public String help() { return
+ "Chnages the current object that all other commands use "+
+ "as the base for running. Pass either a relative path "+
+ "(e.g. in /prevalent, type cd myob, now in /prevalent/myob) "+
+ "or an absolute path (e.g. cd /prevalent/myob).\n\n" +
+ "To go up one level, cd .. can be used.";
+ }
+ public void execute(Writer w, String[] c) throws IOException {
+ if (c.length > 2) w.write(usage());
+ else if (c.length == 1 || c[1].equals("") || c[1].equals("/")) pwd = ".";
+ else if (c[1].equals("..")) {
+ String n = pwd.substring(0, pwd.lastIndexOf('.'));
+ pwd = n.equals("") ? "." : n;
+ } else {
+ String n = c[1];
+ if (n.charAt(0) != '/') n = pwd + n;
+ n = n.replaceAll("/+", ".");
+ if (n.length() > 1 && n.charAt(n.length() - 1) == '.')
+ n = n.substring(0, n.length() - 1);
+
+ int pos = n.lastIndexOf('.');
+ String path = n.substring(0, pos);
+ String matcher = n.substring(pos + 1);
+ Object ret = send(new KeyRequest(path, matcher));
+
+ if (ret == null) {
+ w.write("error: (unexpected) server returned null\n");
+ } else if (ret instanceof List && ((List)ret).size() == 1) {
+ pwd = n;
+ } else if (ret instanceof JSExn ||
+ (ret instanceof List && ((List)ret).size() == 0)) {
+ w.write("cd ");
+ w.write(c[1]);
+ w.write(": no such path\n");
+ } else {
+ w.write("error: (unexpected) server returned ");
+ w.write(ret.toString());
+ w.write("\n");
+ }
+ }
}
}