6 import java.util.regex.*;
8 import org.ibex.util.*;
9 import org.ibex.util.Collections;
14 public static void main(String[] args) throws Exception {
15 if (args.length == 0 || args.length > 2|| !args[0].startsWith("http://")) {
18 Shell shell = new Shell(new URL(args[0]));
20 if (args.length == 2) {
23 shell.listen(new InputStreamReader(System.in), new OutputStreamWriter(System.out));
27 private static void printUsage() {
28 System.out.println("Usage: xish url [command]");
31 protected Command[] commands = new Command[] {
42 /** Current JS path using '.' as a seperator. */
43 protected String pwd = ".";
45 /** Create a new Shell using the given url for the server. */
46 public Shell(URL url) { server = url; }
48 public void listen(Reader r, Writer w) throws IOException {
49 LineNumberReader in = new LineNumberReader(r);
50 PrintWriter out = new PrintWriter(w);
52 out.println("ibex xt shell: type help or exit");
58 while ((line = in.readLine()) != null) {
59 if (line.length() > 0) {
60 if (line.startsWith("exit")) return;
61 if (line.charAt(line.length() - 1) == '\\') {
62 buffer += line.substring(0, line.length() - 1);
64 out.flush(); continue;
70 if (buffer.length() > 0) {
71 String[] c = buffer.split(" ");
72 int i=0; while (i < commands.length) {
73 if (commands[i].name().equals(c[0])) {
74 commands[i].execute(out, c); break;
78 if (i == commands.length) {
80 w.write(": command not found\n");
89 /** Returns a path, based on console input. */
90 private String path(String c) {
91 if (c.equals("") || c.equals(".") || c.equals("/")) {
93 } else if (c.equals("..")) {
94 c = c.substring(0, c.lastIndexOf('.'));
95 if (c.equals("")) c = ".";
97 if (c.charAt(0) != '/') c = pwd + c;
98 c = c.replaceAll("/+", ".");
99 if (c.length() > 1 && c.charAt(c.length() - 1) == '.')
100 c = c.substring(0, c.length() - 1);
106 private String cookie = null;
107 public Object send(Request request) throws IOException {
108 URLConnection c = server.openConnection();
109 ((HttpURLConnection)c).setRequestMethod("POST");
111 if (cookie != null) c.setRequestProperty("Cookie", cookie);
115 ObjectOutputStream out = new ObjectOutputStream(c.getOutputStream());
116 out.writeObject(request);
119 String cook = c.getHeaderField("Set-Cookie");
120 if (cook != null && !cook.equals("")) cookie = cook.substring(0, cook.indexOf(';'));
123 return new ObjectInputStream(c.getInputStream()).readObject();
124 } catch (ClassNotFoundException e) {
126 throw new IOException("unexpected ClassNotFoundException");
130 public abstract class Command {
131 /** Returns the command name. */
132 public abstract String name();
134 /** Returns single-line of usage information, eg. <tt>pattern]</tt> */
135 public abstract String usage();
137 /** Returns single-line description of command. */
138 public abstract String summary();
140 /** Returns multi-line description. */
141 public abstract String help();
143 /** Writes result of execution, even if result is an error. */
144 public abstract void execute(Writer w, String[] args) throws IOException;
147 /** Returns the command matching the given name. */
148 protected Command command(String name) {
149 for (int i=0; i < commands.length; i++)
150 if (commands[i].name().equals(name)) return commands[i];
154 public class HelpCommand extends Command {
155 public String name() { return "help"; }
156 public String usage() { return "[command name]"; }
157 public String summary() { return "Lists available commands."; }
158 public String help() { return ""; }
160 public void execute(Writer w, String[] c) throws IOException {
162 Command cmd = command(c[1]);
166 w.write(": command not found\n");
171 w.write(cmd.usage());
178 for (int i=0; i < commands.length; i++)
179 len = Math.max(commands[i].name().length(), len);
181 w.write("Available commands:\n");
182 for (int i=0; i < commands.length; i++) {
183 Command cmd = commands[i];
186 for (int j=len - cmd.name().length(); j >= 0; j--) w.write(" ");
188 w.write(cmd.summary());
191 w.write("\nFor usage details, type help [command name].\n");
196 public class LsCommand extends Command {
197 public String name() { return "ls"; }
198 public String usage() { return "[path]"; }
199 public String summary() { return "List object entries."; }
200 public String help() { return
201 "Lists the keys in an object. Modelled after the UNIX ls command.";
204 public void execute(Writer w, String[] c) throws IOException {
205 if (c.length > 2) { w.write(usage()); return; }
207 Object ret = send(new KeyRequest(path(c[1])));
209 w.write("error: (unexpected) returned object is null\n");
210 } else if (ret instanceof JSExn) {
211 String e = ((JSExn)ret).getMessage();
212 // FIXME: messy way to get info from server
213 if (e.endsWith("does not exist")) {
216 w.write(": no such path\n");
222 } else if (ret instanceof List) {
223 List l = (List)ret; Collections.sort(l);
224 Iterator i = l.iterator(); while (i.hasNext()) {
225 w.write(i.next().toString());
229 w.write("error: (unexpected) returned object is of unknown type: ");
230 w.write(ret.getClass().getName());
236 public class PwdCommand extends Command {
237 public String name() { return "pwd"; }
238 public String usage() { return ""; }
239 public String summary() { return "Path to current object."; }
240 public String help() { return "Print the path to the current object."; }
241 public void execute(Writer w, String[] c) throws IOException {
242 w.write(c.length == 1 ? pwd.replace('.', '/') : usage());
247 public class CdCommand extends Command {
248 public String name() { return "cd"; }
249 public String usage() { return "[path]"; }
250 public String summary() { return "Change current object."; }
251 public String help() { return
252 "Chnages the current object that all other commands use "+
253 "as the base for running.\n Pass either a relative path "+
254 "(e.g. in /prevalent, type cd myob, now in /prevalent/myob) "+
255 "or an absolute path (e.g. cd /prevalent/myob).\n\n" +
256 "To go up one level, cd .. can be used.";
258 public void execute(Writer w, String[] c) throws IOException {
259 if (c.length > 2) w.write(usage());
260 else if (c.length == 1 || c[1].equals("") || c[1].equals("/")) pwd = ".";
261 else if (c[1].equals("..")) {
262 String n = pwd.substring(0, pwd.lastIndexOf('.'));
263 pwd = n.equals("") ? "." : n;
265 String n = path(c[1]);
266 Object ret = send(new KeyRequest(n));
269 w.write("error: (unexpected) server returned null\n");
270 } else if (ret instanceof List && ((List)ret).size() == 1) {
272 } else if (ret instanceof JSExn ||
273 (ret instanceof List && ((List)ret).size() == 0)) {
276 w.write(": no such path\n");
278 w.write("error: (unexpected) server returned ");
279 w.write(ret.toString());
286 public class RmCommand extends Command {
287 public String name() { return "rm"; }
288 public String usage() { return "[options] [path]"; }
289 public String summary() { return "Removes objects."; }
290 public String help() { return "Removes objects."; } // FIXME
291 public void execute(Writer w, String[] c) throws IOException {
292 if (c.length == 1) { w.write(usage()); }
294 String[] r = new String[c.length - 1];
295 for (int i=0; i < r.length; i++) r[i] = path(c[i + 1]);
296 // Object ret = send(new KeyRequest( FIXME: CompositeRequest
300 public static abstract class Request implements Serializable {
301 public abstract Object process(JSScope root) throws JSExn;
304 public static class KeyRequest extends Request {
305 private String path, matcher;
306 public KeyRequest() {}
307 public KeyRequest(String c) {
308 int pos = c.lastIndexOf('.');
309 path = c.substring(0, pos);
310 matcher = c.substring(pos + 1).replaceAll("\\*+", ".*");
312 public KeyRequest(String path, String matcher) {
313 this.path = path; this.matcher = matcher;
316 /** Returns a List. */
317 public Object process(JSScope root) throws JSExn {
318 String p = path == null ? "" : path.replaceAll("\\.+", "\\.");
319 if (p.length() > 0 && p.charAt(0) == '.') p = p.substring(1);
320 if (p.length() > 0 && p.charAt(p.length() - 1) == '.') p = p.substring(0, p.length() - 1);
321 System.out.println("searching path '"+p+"' for pattern '"+matcher+"'");
323 Object o = p.equals("") ? root : root.get(p);
324 if (o == null || o instanceof JSDate ||
325 o instanceof JSArray ||
326 !(o instanceof JS)) {
327 System.out.println("hit bad object: "+o+", class="+
328 (o == null ? null : o.getClass().getName()));
329 throw new JSExn("path /" + p + " does not exist");
331 Pattern pat = Pattern.compile(matcher);
332 List keys = new ArrayList();
334 Iterator i = ((JS)o).keys().iterator(); while(i.hasNext()) {
335 String k = i.next().toString();
336 if (pat.matcher(k).matches()) keys.add(k);
344 public static class ExecRequest extends Request {
346 public ExecRequest() {}
347 public ExecRequest(JS exec) { this.exec = exec; }
348 public ExecRequest(String source) throws IOException, JSExn {
349 this(new StringReader(source));
351 public ExecRequest(Reader source) throws IOException, JSExn {
352 exec = JS.fromReader("xsh", 0, source);
355 /** Returns the result of <tt>JS.eval()</tt>. */
356 public Object process(JSScope root) throws JSExn {
357 return JS.eval(JS.cloneWithNewParentScope(exec, root));