1 package org.ibex.xt.shell;
3 import java.io.StringReader;
5 import java.io.IOException;
8 import java.util.regex.*;
11 import org.ibex.util.*;
13 public abstract class Command {
14 protected Shell shell;
16 protected Command(Shell s) { shell = s; }
18 /** Returns the command name. */
19 public abstract String name();
21 /** Returns a single-line of parameter information, eg. <tt>[pattern]</tt> */
22 public abstract String params();
24 /** Returns single-line description of command. */
25 public abstract String summary();
27 /** Returns multi-line description. */
28 public abstract String help();
30 /** Writes result of execution, even if result is an error. */
31 public abstract void execute(Writer w, String[] args) throws IOException;
34 /** Returns single-line of usage information, eg. <tt>usage: ls [pattern]</tt> */
35 public void usage(Writer w) throws IOException {
43 public List fromShellPath(String s) throws Shell.BadPathException {
44 return fromShellPath(s, null);
47 /** Converts a shell path "/foo/etc/../bar" to its component form,
48 * { "foo", "bar" } and loads it into the returned list.
49 * Handles relative positioning to shell.getPath(). */
50 public List fromShellPath(String s, List l) throws Shell.BadPathException {
51 if (s == null) return null;
52 if (l == null) l = new ArrayList();
54 s = s.trim().replace("/+", "/");
55 if (s.charAt(0) != '/') l.addAll(Arrays.asList(shell.getPath()));
57 StringTokenizer st = new StringTokenizer(s, "/");
58 while (st.hasMoreTokens()) {
59 String part = st.nextToken();
60 if (part == null || part.equals("") || part.equals(".")) continue;
61 else if (part.equals("..")) { if (l.size() > 0) l.remove(l.size() - 1); }
68 public static class Help extends Command {
69 public Help(Shell s) { super(s); }
70 public String name() { return "help"; }
71 public String params() { return "[command name]"; }
72 public String summary() { return "Lists available commands."; }
73 public String help() { return ""; }
75 public void execute(Writer w, String[] c) throws IOException {
77 Command cmd = shell.getCommand(c[1]);
81 w.write(": command not found\n");
90 Command[] cmds = shell.getCommands();
91 for (int i=0; i < cmds.length; i++)
92 len = Math.max(cmds[i].name().length(), len);
94 w.write("Available commands:\n");
95 for (int i=0; i < cmds.length; i++) {
96 Command cmd = cmds[i];
99 for (int j=len - cmd.name().length(); j >= 0; j--) w.write(" ");
101 w.write(cmd.summary());
104 w.write("\nFor usage details, type help [command name].\n");
109 public static class Ls extends Command {
110 public Ls(Shell s) { super(s); }
111 public String name() { return "ls"; }
112 public String params() { return "[path]"; }
113 public String summary() { return "List object entries."; }
114 public String help() { return
115 "Lists the keys in an object. Modelled after the UNIX ls command.";
118 public void execute(Writer w, String[] c) throws IOException {
119 if (c.length > 2) { usage(w); return; }
120 String p = c.length == 1 ? "*" : c[1];
121 if (p.endsWith("/")) p += "*";
124 List path = fromShellPath(p);
125 Object key = path.remove(path.size() - 1);
126 Object po = shell.getFromPath(path.toArray());
127 if (po == null || !(po instanceof JS))
128 throw new Shell.BadPathException();
131 if (key instanceof String &&
132 ((String)key).indexOf('*') >= 0) {
133 String last = (String)key;
134 last = last.replaceAll("\\.", "\\.");
135 last = last.replaceAll("\\*", ".*");
136 Pattern pat = Pattern.compile(last);
137 Iterator i = cur.keys().iterator(); while (i.hasNext()) {
139 if (o == null || !(o instanceof String)) continue;
140 String s = (String)o;
141 if (!pat.matcher(s).matches()) continue;
145 } else if (cur.containsKey(key)) {
146 w.write(key.toString());
149 } catch (Shell.BadPathException e) {
150 w.write("error: no such path: ");
154 w.write("error: no such path: ");
157 w.write(e.getMessage());
163 public static class Rm extends Command {
164 public Rm(Shell s) { super(s); }
165 public String name() { return "rm"; }
166 public String params() { return "[options] [path]"; }
167 public String summary() { return "Removes objects."; }
168 public String help() { return "Removes objects."; } // FIXME info
169 public void execute(Writer w, String[] c) throws IOException {
170 if (c.length == 1) { usage(w); return; }
172 boolean force = false; // FIXME provide ability to set
174 StringBuffer func = new StringBuffer();
176 for (int ic=1; ic < c.length; ic++) {
177 String p = c.length == 1 ? "*" : c[ic];
178 if (p.endsWith("/")) p += "*";
181 // get the base of the path
182 List path = fromShellPath(p);
183 Object key = path.remove(path.size() - 1);
184 Object po = shell.getFromPath(path.toArray());
185 if (po == null || !(po instanceof JS))
186 throw new Shell.BadPathException();
189 if (cur.containsKey(key)) {
190 Object o = cur.get(key);
191 if (!force && o != null && o instanceof JS && ((JS)o).keys().size() > 0)
192 throw new Shell.BadPathException("key is not empty");
193 func.append("prevalent.");
194 for(int i=0; i < path.size(); i++) {
195 func.append(path.get(i)); func.append('.'); }
196 func.append("Delete(\"");
197 func.append(key.toString());
198 func.append("\");\n");
199 } else if (key instanceof String && ((String)key).indexOf('*') >= 0) {
200 String last = (String)key;
201 last = last.replaceAll("\\.", "\\.");
202 last = last.replaceAll("\\*", ".*");
203 Pattern pat = Pattern.compile(last);
204 Collection curkeys = cur.keys();
205 if (curkeys.size() == 0) throw new Shell.BadPathException();
206 Iterator it = curkeys.iterator(); while (it.hasNext()) {
207 Object o = it.next();
208 if (o == null || !(o instanceof String)) continue;
209 String s = (String)o;
210 if (!pat.matcher(s).matches()) continue;
212 func.append("prevalent.");
213 for(int i=0; i < path.size(); i++) {
214 func.append(path.get(i)); func.append('.'); }
215 func.append("Delete(\"");
217 func.append("\");\n");
219 } else throw new Shell.BadPathException();
222 w.write("error: cannot remove '");
225 w.write(e.getMessage());
228 } catch (Shell.BadPathException e) {
229 w.write("error: cannot remove '");
232 w.write(e.getMessage() != null ? e.getMessage() : "no such path");
238 shell.transaction(JS.fromReader(
239 "rm-transaction", 0, new StringReader(func.toString())));
243 public static class Pwd extends Command {
244 public Pwd(Shell s) { super(s); }
245 public String name() { return "pwd"; }
246 public String params() { return ""; }
247 public String summary() { return "Path to current object."; }
248 public String help() { return "Print the path to the current object."; }
249 public void execute(Writer w, String[] c) throws IOException {
250 if (c.length != 1) { usage(w); return; }
251 Object[] path = shell.getPath();
252 for (int i=0; i < path.length; i++) {
254 w.write(path[i].toString());
256 if (path.length == 0) w.write("/");
261 public static class Cd extends Command {
262 public Cd(Shell s) { super(s); }
263 public String name() { return "cd"; }
264 public String params() { return "[path]"; }
265 public String summary() { return "Change current object."; }
266 public String help() { return
267 "Chnages the current object that all other commands use "+
268 "as the base for running.\n Pass either a relative path "+
269 "(e.g. in .prevalent, type cd myob, now in .prevalent.myob) "+
270 "or an absolute path (e.g. cd .prevalent.myob).\n\n" +
271 "To go up one level, cd .. can be used.";
273 public void execute(Writer w, String[] c) throws IOException {
274 if (c.length > 2) { usage(w); return; }
275 String path = c.length == 1 ? "/" : c[1];
277 try { shell.setPath(fromShellPath(path).toArray()); }
278 catch (Shell.BadPathException e) {
279 w.write("error: no such path: ");