add mkdir and replace functions
[org.ibex.xt-crawshaw.git] / src / java / org / ibex / xt / shell / Shell.java
1 package org.ibex.xt.shell;
2
3 import java.io.*;
4 import java.net.*;
5
6 import org.ibex.js.*;
7 import org.ibex.util.*;
8
9 public class Shell {
10
11     public static void main(String[] args) throws Exception {
12         if (args.length == 0 || args.length > 2||  !args[0].startsWith("http://")) {
13             printUsage(); return;
14         }
15         Shell shell = new Shell(new URL(args[0]));
16
17         if (args.length == 2) {
18             // FIXME
19         } else {
20             shell.listen(new InputStreamReader(System.in), new OutputStreamWriter(System.out));
21         }
22     }
23
24     private static void printUsage() {
25         System.out.println("Usage: xish url [command]");
26     }
27
28
29     /** Supported commands. */
30     private Command[] commands;
31
32     /** Root context. */
33     private final JS root;
34
35     /** Current JS context for the shell. */
36     private JS scope;
37
38     /** Current path to <tt>scope</tt> in <tt>root</tt>. */
39     private Object[] path;
40
41     /** Returns the object represented by the given path,
42      *  ignoring the current shell path context.*/
43     public Object getFromPath(Object[] path) throws BadPathException, JSExn {
44         if (path.length == 0) return root;
45
46         if (root instanceof JSRemote) {
47             // JSRemote will automatically process its keys for '.' seperators
48             StringBuffer sb = new StringBuffer(path[0].toString());
49             for (int i=1; i < path.length; i++) {
50                 sb.append('.'); sb.append(path[i].toString());
51             }
52             return root.get(sb.toString());
53         } else {
54             JS cur = root;
55             for (int i=0; i < path.length - 1; i++) {
56                 Object o =  cur.get(path[i]);
57                 if (o == null || !(o instanceof JS)) throw new Shell.BadPathException();
58                 cur = (JS)o;
59             }
60             return cur.get(path[path.length - 1]);
61         }
62     }
63
64     public void transaction(JS t) {
65         if (root instanceof JSRemote) {
66             ((JSRemote)root).transaction(
67                 JS.cloneWithNewParentScope(t, new JSScope(null)));
68         } else {
69             // FIXME JS.eval(JS.cloneWithNewParentScope(t, root));
70         }
71     }
72
73     /** Set the current path of the shell, modifiying the result of getScope(). */
74     public void setPath(Object[] s) throws BadPathException {
75         JS cur = root;
76         if (s == null) s = new Object[0];
77         for (int i=0; i < s.length; i++) {
78             Object o;
79             try { o = cur.get(s[i]); } catch (JSExn e) { throw new BadPathException(); }
80             if (o == null || !(o instanceof JS)) throw new BadPathException();
81             cur = (JS)o;
82         }
83         scope = cur;
84         path = s;
85     }
86
87     /** Returns the current path of the shell. */
88     public Object[] getPath() { return path; }
89
90     /** Returns String represntation of path, using '/' as a seperator. */
91     public String getPathAsString() {
92         if (path.length == 0) return "/";
93         StringBuffer sb = new StringBuffer();
94         for (int i=0; i < path.length; i++) {
95             sb.append('/'); sb.append(path[i].toString());
96         }
97         return sb.toString();
98     }
99
100     /** Returns the context matching the current path. */
101     public JS getScope() { return scope; }
102
103     /** Returns the context matching the current path. */
104     public JS getRootScope() { return root; }
105
106     /** Returns all supported commands. */
107     public Command[] getCommands() { return commands; }
108
109     /** Returns the command matching the given name. */
110     public Command getCommand(String name) {
111         for (int i=0; i < commands.length; i++)
112             if (commands[i].name().equals(name)) return commands[i];
113         return null;
114     }
115
116     /** Create a new Shell using the given url for the server. */
117     public Shell(URL url) {
118         root = scope = new JSRemote(url);
119         path = new String[0];
120         commands = new Command[] {
121             new Command.Ls(this),
122             new Command.Pwd(this),
123             new Command.Cd(this),
124             new Command.Rm(this),
125             new Command.Replace(this),
126             new Command.Mkdir(this),
127             new Command.Help(this)
128         };
129     }
130
131     public void listen(Reader r, Writer w) throws IOException {
132         LineNumberReader in = new LineNumberReader(r);
133         PrintWriter out = new PrintWriter(w);
134
135         out.println("ibex xt shell: type help or exit");
136         out.print("xt:"); out.print(getPathAsString()); out.print("# ");
137         out.flush();
138
139         String line;
140         String buffer = "";
141         int countBraceOpen = 0, countBraceClose = 0;
142         int countBracketOpen = 0, countBracketClose = 0;
143         int countSQBracketOpen = 0, countSQBracketClose = 0;
144         while ((line = in.readLine()) != null) {
145             if (line.length() > 0) {
146                 if (line.startsWith("exit")) return;
147
148                 for (int i=0; i < line.length(); i++) {
149                     switch (line.charAt(i)) {
150                         case '{': countBraceOpen++; break;
151                         case '}': countBraceClose++; break;
152                         case '(': countBracketOpen++; break;
153                         case ')': countBracketClose++; break;
154                         case '[': countSQBracketOpen++; break;
155                         case ']': countSQBracketClose++; break;
156                     }
157                 }
158
159                 boolean nonendchar = line.charAt(line.length() - 1) == '\\';
160                 if (nonendchar) line = line.substring(0, line.length() - 1);
161                 buffer += line;
162
163                 if (nonendchar ||
164                         countBracketOpen != countBracketClose ||
165                         countBraceOpen != countBraceClose ||
166                         countSQBracketOpen != countSQBracketClose) {
167                     out.print('>');
168                     out.flush(); continue;
169                 }
170             }
171
172             if (buffer.length() > 0) {
173                 String[] c = buffer.split(" ");
174                 Command cmd = getCommand(c[0]);
175
176                 if (cmd == null) {
177                     out.write(c[0]);
178                     w.write(": command not found\n");
179                 } else cmd.execute(out, c);
180
181                 buffer = "";
182             }
183             out.print("xt:"); out.print(getPathAsString()); out.print("# ");
184             out.flush();
185         }
186     }
187
188     public static class BadPathException extends Exception {
189         public BadPathException() {}
190         public BadPathException(String msg) { super(msg); }
191     }
192 }