/plugins/Console/tags/release-3-1/console/SystemShell.java

# · Java · 552 lines · 391 code · 78 blank · 83 comment · 81 complexity · 10baa778d9bcf950165fa8eb9745c927 MD5 · raw file

  1. /*
  2. * SystemShell.java - Executes OS commands
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2000, 2001 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package console;
  23. //{{{ Imports
  24. import java.io.*;
  25. import java.util.*;
  26. import org.gjt.sp.jedit.*;
  27. import org.gjt.sp.util.Log;
  28. //}}}
  29. class SystemShell extends Shell
  30. {
  31. //{{{ SystemShell constructor
  32. public SystemShell()
  33. {
  34. super("System");
  35. } //}}}
  36. //{{{ printInfoMessage() method
  37. public void printInfoMessage(Output output)
  38. {
  39. output.print(null,jEdit.getProperty("console.shell.info"));
  40. } //}}}
  41. //{{{ execute() method
  42. public void execute(Console console, Output output, String command)
  43. {
  44. // comments, for possible future scripting support
  45. if(command.startsWith("#"))
  46. {
  47. output.commandDone();
  48. return;
  49. }
  50. // lazily initialize aliases and variables
  51. init();
  52. Vector args = parse(command);
  53. // will be null if the command is an empty string
  54. if(args == null)
  55. {
  56. output.commandDone();
  57. return;
  58. }
  59. args = preprocess(console.getView(),args);
  60. String commandName = (String)args.elementAt(0);
  61. if(commandName.charAt(0) == '%')
  62. {
  63. // a console built-in
  64. args.removeElementAt(0);
  65. SystemShellBuiltIn.executeBuiltIn(console,output,
  66. commandName.substring(1),args);
  67. output.commandDone();
  68. }
  69. else
  70. {
  71. boolean foreground;
  72. if(args.elementAt(args.size() - 1).equals("&"))
  73. {
  74. // run in background
  75. args.removeElementAt(args.size() - 1);
  76. foreground = false;
  77. }
  78. else
  79. {
  80. // run in foreground
  81. foreground = true;
  82. }
  83. String[] _args = new String[args.size()];
  84. args.copyInto(_args);
  85. String[] env;
  86. if(OperatingSystem.getOperatingSystem()
  87. .supportsEnvironmentVariables())
  88. {
  89. env = new String[variables.size()];
  90. int counter = 0;
  91. Enumeration keys = variables.keys();
  92. while(keys.hasMoreElements())
  93. {
  94. Object key = keys.nextElement();
  95. env[counter++]= (key + "=" + variables.get(key));
  96. }
  97. }
  98. else
  99. env = null;
  100. new ConsoleProcess(console,output,_args,env,foreground);
  101. }
  102. } //}}}
  103. //{{{ stop() method
  104. public void stop(Console console)
  105. {
  106. ConsoleState consoleState = getConsoleState(console);
  107. ConsoleProcess process = consoleState.process;
  108. if(process != null)
  109. process.stop();
  110. else
  111. {
  112. console.print(console.getErrorColor(),
  113. jEdit.getProperty("console.shell.noproc"));
  114. }
  115. } //}}}
  116. //{{{ waitFor() method
  117. public boolean waitFor(Console console)
  118. {
  119. ConsoleState consoleState = getConsoleState(console);
  120. ConsoleProcess process = consoleState.process;
  121. if(process != null)
  122. {
  123. try
  124. {
  125. synchronized(process)
  126. {
  127. process.wait();
  128. }
  129. }
  130. catch(InterruptedException e)
  131. {
  132. }
  133. return process.getExitStatus();
  134. }
  135. else
  136. return true;
  137. } //}}}
  138. //{{{ Package-private members
  139. //{{{ consoleOpened() method
  140. static void consoleOpened(Console console)
  141. {
  142. consoleStateMap.put(console,new ConsoleState());
  143. } //}}}
  144. //{{{ consoleClosed() method
  145. static void consoleClosed(Console console)
  146. {
  147. ConsoleProcess process = getConsoleState(console).process;
  148. if(process != null)
  149. process.stop();
  150. consoleStateMap.remove(console);
  151. } //}}}
  152. //{{{ getConsoleState() method
  153. static ConsoleState getConsoleState(Console console)
  154. {
  155. return (ConsoleState)consoleStateMap.get(console);
  156. } //}}}
  157. //{{{ getAliases() method
  158. static Hashtable getAliases()
  159. {
  160. return aliases;
  161. } //}}}
  162. //{{{ getVariables() method
  163. static Hashtable getVariables()
  164. {
  165. return variables;
  166. } //}}}
  167. //{{{ propertiesChanged() method
  168. static void propertiesChanged()
  169. {
  170. aliases = null;
  171. variables = null;
  172. // next time execute() is called, init() will reload everything
  173. } //}}}
  174. //}}}
  175. //{{{ Private members
  176. //{{{ Instance variables
  177. private static Hashtable consoleStateMap = new Hashtable();
  178. private static final char dosSlash = 127;
  179. private static Hashtable aliases;
  180. private static Hashtable variables;
  181. //}}}
  182. //{{{ init() method
  183. private static void init()
  184. {
  185. if(aliases != null && variables != null)
  186. return;
  187. aliases = new Hashtable();
  188. // some built-ins can be invoked without the % prefix
  189. aliases.put("cd","%cd");
  190. aliases.put("pwd","%pwd");
  191. // load aliases from properties
  192. String alias;
  193. int i = 0;
  194. while((alias = jEdit.getProperty("console.shell.alias." + i)) != null)
  195. {
  196. aliases.put(alias,jEdit.getProperty("console.shell.alias."
  197. + i + ".expansion"));
  198. i++;
  199. }
  200. OperatingSystem os = OperatingSystem.getOperatingSystem();
  201. os.setUpDefaultAliases(aliases);
  202. variables = os.getEnvironmentVariables();
  203. if(jEdit.getJEditHome() != null)
  204. variables.put("JEDIT_HOME",jEdit.getJEditHome());
  205. if(jEdit.getSettingsDirectory() != null)
  206. variables.put("JEDIT_SETTINGS",jEdit.getSettingsDirectory());
  207. // for the sake of Unix programs that try to be smart
  208. variables.put("TERM","dumb");
  209. // load variables from properties
  210. String varname;
  211. i = 0;
  212. while((varname = jEdit.getProperty("console.shell.variable." + i)) != null)
  213. {
  214. variables.put(varname,jEdit.getProperty("console.shell.variable."
  215. + i + ".value"));
  216. i++;
  217. }
  218. } //}}}
  219. //{{{ parse() method
  220. /**
  221. * Convert a command into a vector of arguments.
  222. */
  223. private Vector parse(String command)
  224. {
  225. Vector args = new Vector();
  226. // We replace \ with a non-printable char because
  227. // StreamTokenizer handles \ specially, which causes
  228. // problems on Windows as \ is the file separator
  229. // there.
  230. // After parsing is done, the non printable char is
  231. // changed to \ once again.
  232. // StreamTokenizer needs a way to disable backslash
  233. // handling...
  234. command = command.replace('\\',dosSlash);
  235. StreamTokenizer st = new StreamTokenizer(new StringReader(command));
  236. st.resetSyntax();
  237. st.wordChars('!',255);
  238. st.whitespaceChars(0,' ');
  239. st.quoteChar('"');
  240. st.quoteChar('\'');
  241. try
  242. {
  243. loop: for(;;)
  244. {
  245. switch(st.nextToken())
  246. {
  247. case StreamTokenizer.TT_EOF:
  248. break loop;
  249. case StreamTokenizer.TT_WORD:
  250. case '"':
  251. case '\'':
  252. args.addElement(st.sval.replace(dosSlash,'\\'));
  253. break;
  254. }
  255. }
  256. }
  257. catch(IOException io)
  258. {
  259. // won't happen
  260. }
  261. if(args.size() == 0)
  262. return null;
  263. else
  264. return args;
  265. } //}}}
  266. //{{{ preprocess() method
  267. /**
  268. * Expand aliases, variables and globs.
  269. */
  270. private Vector preprocess(View view, Vector args)
  271. {
  272. Vector newArgs = new Vector();
  273. // expand aliases
  274. String commandName = (String)args.elementAt(0);
  275. String expansion = (String)aliases.get(commandName);
  276. if(expansion != null)
  277. {
  278. Vector expansionArgs = parse(expansion);
  279. for(int i = 0; i < expansionArgs.size(); i++)
  280. {
  281. expandGlobs(view,newArgs,(String)expansionArgs
  282. .elementAt(i));
  283. }
  284. }
  285. else
  286. expandGlobs(view,newArgs,commandName);
  287. // add remaining arguments
  288. for(int i = 1; i < args.size(); i++)
  289. expandGlobs(view,newArgs,(String)args.elementAt(i));
  290. return newArgs;
  291. } //}}}
  292. //{{{ expandGlobs() method
  293. private void expandGlobs(View view, Vector args, String arg)
  294. {
  295. // XXX: to do
  296. args.addElement(expandVariables(view,arg));
  297. } //}}}
  298. //{{{ expandVariables() method
  299. private String expandVariables(View view, String arg)
  300. {
  301. StringBuffer buf = new StringBuffer();
  302. String varName;
  303. for(int i = 0; i < arg.length(); i++)
  304. {
  305. char c = arg.charAt(i);
  306. switch(c)
  307. {
  308. case dosSlash:
  309. buf.append('\\');
  310. break;
  311. //{{{ DOS-style variable (%name%)
  312. case '%':
  313. int index = arg.indexOf('%',i + 1);
  314. if(index != -1)
  315. {
  316. if(index == i)
  317. break;
  318. varName = arg.substring(i + 1,index);
  319. i = index;
  320. String expansion = getExpansion(view,varName);
  321. if(expansion != null)
  322. buf.append(expansion);
  323. }
  324. else
  325. buf.append('%');
  326. break;
  327. //}}}
  328. //{{{ Unix-style variables ($name, ${name})
  329. case '$':
  330. if(i == arg.length() - 1)
  331. {
  332. buf.append(c);
  333. break;
  334. }
  335. if(arg.charAt(i + 1) == '{')
  336. {
  337. index = arg.indexOf('}',i + 1);
  338. if(index == -1)
  339. index = arg.length();
  340. varName = arg.substring(i + 2,index);
  341. i = index;
  342. }
  343. else
  344. {
  345. for(index = i + 1; index < arg.length(); index++)
  346. {
  347. char ch = arg.charAt(index);
  348. if(!Character.isLetterOrDigit(ch)
  349. && ch != '_')
  350. {
  351. index--;
  352. break;
  353. }
  354. }
  355. if(index == i)
  356. break;
  357. varName = arg.substring(i + 1,index);
  358. i = index;
  359. }
  360. String expansion = getExpansion(view,varName);
  361. if(expansion != null)
  362. buf.append(expansion);
  363. break;
  364. //}}}
  365. //{{{ Home directory (~)
  366. case '~':
  367. String home = System.getProperty("user.home");
  368. if(arg.length() == 1)
  369. {
  370. buf.append(home);
  371. break;
  372. }
  373. if(i != 0)
  374. {
  375. c = arg.charAt(i - 1);
  376. if(c == '/' || c == File.separatorChar)
  377. {
  378. buf.append(home);
  379. break;
  380. }
  381. }
  382. if(i != arg.length() - 1)
  383. {
  384. c = arg.charAt(i + 1);
  385. if(c == '/' || c == File.separatorChar)
  386. {
  387. buf.append(home);
  388. break;
  389. }
  390. }
  391. buf.append('~');
  392. break;
  393. //}}}
  394. default:
  395. buf.append(c);
  396. break;
  397. }
  398. }
  399. return buf.toString();
  400. } //}}}
  401. //{{{ getExpansion() method
  402. private String getExpansion(View view, String varName)
  403. {
  404. String expansion;
  405. // Expand some special variables
  406. Buffer buffer = view.getBuffer();
  407. if(varName.equals("$") || varName.equals("%"))
  408. expansion = varName;
  409. else if(varName.equals("d"))
  410. {
  411. expansion = MiscUtilities.getParentOfPath(
  412. buffer.getPath());
  413. if(expansion.endsWith("/")
  414. || expansion.endsWith(File.separator))
  415. {
  416. expansion = expansion.substring(0,
  417. expansion.length() - 1);
  418. }
  419. }
  420. else if(varName.equals("u"))
  421. {
  422. expansion = buffer.getPath();
  423. if(!MiscUtilities.isURL(expansion))
  424. {
  425. expansion = "file:/" + expansion
  426. .replace(File.separatorChar,'/');
  427. }
  428. }
  429. else if(varName.equals("f"))
  430. expansion = buffer.getPath();
  431. else if(varName.equals("n"))
  432. {
  433. expansion = buffer.getName();
  434. int index = expansion.lastIndexOf('.');
  435. if(index != -1)
  436. expansion = expansion.substring(0,index);
  437. }
  438. else
  439. expansion = (String)variables.get(varName);
  440. return expansion;
  441. } //}}}
  442. //}}}
  443. //{{{ ConsoleState class
  444. static class ConsoleState
  445. {
  446. String currentDirectory = System.getProperty("user.dir");
  447. Stack directoryStack = new Stack();
  448. ConsoleProcess process;
  449. void setCurrentDirectory(Console console, String newDir)
  450. {
  451. if(!OperatingSystem.getOperatingSystem().cdCommandAvailable())
  452. {
  453. console.print(console.getErrorColor(),
  454. jEdit.getProperty("console.shell.cd.unsup"));
  455. return;
  456. }
  457. String[] pp = { newDir };
  458. if(new File(newDir).exists())
  459. {
  460. currentDirectory = newDir;
  461. console.print(console.getInfoColor(),
  462. jEdit.getProperty(
  463. "console.shell.cd.ok",pp));
  464. }
  465. else
  466. {
  467. console.print(console.getErrorColor(),
  468. jEdit.getProperty(
  469. "console.shell.cd.error",pp));
  470. }
  471. }
  472. } //}}}
  473. }