001: /*
002: * ShellCommand.java
003: *
004: * Copyright (C) 2000-2004 Peter Graves
005: * $Id: ShellCommand.java,v 1.2 2004/02/23 00:09:55 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.lisp;
023:
024: import java.io.BufferedReader;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.InputStreamReader;
028: import java.util.ArrayList;
029: import java.util.List;
030:
031: public final class ShellCommand extends Lisp implements Runnable {
032: private final String command;
033: private final String directory;
034: private final StringBuffer output = new StringBuffer();
035:
036: private int exitValue = -1;
037:
038: private ShellCommand(String command, String directory)
039: throws ConditionThrowable {
040: this .command = command;
041: this .directory = directory;
042: }
043:
044: private final String getOutput() {
045: return output.toString();
046: }
047:
048: private final int exitValue() {
049: return exitValue;
050: }
051:
052: private void appendOutput(String s) {
053: output.append(s);
054: }
055:
056: public void run() {
057: Process process = null;
058: try {
059: if (command != null) {
060: if (Utilities.isPlatformUnix()) {
061: if (directory != null) {
062: StringBuffer sb = new StringBuffer("\\cd \"");
063: sb.append(directory);
064: sb.append("\" && ");
065: sb.append(command);
066: String[] cmdarray = { "/bin/sh", "-c",
067: sb.toString() };
068: process = Runtime.getRuntime().exec(cmdarray);
069: } else {
070: String[] cmdarray = { "/bin/sh", "-c", command };
071: process = Runtime.getRuntime().exec(cmdarray);
072: }
073: } else if (Utilities.isPlatformWindows()) {
074: ArrayList list = new ArrayList();
075: list.add("cmd.exe");
076: list.add("/c");
077: if (directory != null) {
078: StringBuffer sb = new StringBuffer("cd /d \"");
079: sb.append(directory);
080: sb.append("\" && ");
081: sb.append(command);
082: list.addAll(tokenize(sb.toString()));
083: } else
084: list.addAll(tokenize(command));
085: final int size = list.size();
086: String[] cmdarray = new String[size];
087: for (int i = 0; i < size; i++)
088: cmdarray[i] = (String) list.get(i);
089: process = Runtime.getRuntime().exec(cmdarray);
090: }
091: }
092: } catch (IOException e) {
093: Debug.trace(e);
094: }
095: if (process != null) {
096: ReaderThread stdoutThread = new ReaderThread(process
097: .getInputStream());
098: stdoutThread.start();
099: ReaderThread stderrThread = new ReaderThread(process
100: .getErrorStream());
101: stderrThread.start();
102: try {
103: exitValue = process.waitFor();
104: } catch (InterruptedException e) {
105: Debug.trace(e);
106: }
107: try {
108: stdoutThread.join();
109: } catch (InterruptedException e) {
110: Debug.trace(e);
111: }
112: try {
113: stderrThread.join();
114: } catch (InterruptedException e) {
115: Debug.trace(e);
116: }
117: }
118: }
119:
120: // Does not handle embedded single-quoted strings.
121: private static List tokenize(String s) {
122: ArrayList list = new ArrayList();
123: StringBuffer sb = new StringBuffer();
124: boolean inQuote = false;
125: final int limit = s.length();
126: for (int i = 0; i < limit; i++) {
127: char c = s.charAt(i);
128: switch (c) {
129: case ' ':
130: if (inQuote)
131: sb.append(c);
132: else if (sb.length() > 0) {
133: list.add(sb.toString());
134: sb.setLength(0);
135: }
136: break;
137: case '"':
138: if (inQuote) {
139: if (sb.length() > 0) {
140: list.add(sb.toString());
141: sb.setLength(0);
142: }
143: inQuote = false;
144: } else
145: inQuote = true;
146: break;
147: default:
148: sb.append(c);
149: break;
150: }
151: }
152: if (sb.length() > 0)
153: list.add(sb.toString());
154: return list;
155: }
156:
157: private class ReaderThread extends Thread {
158: private char[] buf = new char[4096];
159: private final InputStream inputStream;
160: private final BufferedReader reader;
161: private boolean done = false;
162:
163: public ReaderThread(InputStream inputStream) {
164: this .inputStream = inputStream;
165: reader = new BufferedReader(new InputStreamReader(
166: inputStream));
167: }
168:
169: public void run() {
170: while (!done) {
171: String s = read();
172: if (s == null)
173: return;
174: appendOutput(s);
175: }
176: }
177:
178: private String read() {
179: StringBuffer sb = new StringBuffer();
180: try {
181: do {
182: int numChars = reader.read(buf, 0, buf.length); // Blocks.
183: if (numChars < 0) {
184: done = true;
185: break;
186: }
187: if (numChars > 0)
188: sb.append(buf, 0, numChars);
189: Thread.sleep(10);
190: } while (reader.ready());
191: } catch (IOException e) {
192: return null;
193: } catch (InterruptedException e) {
194: return null;
195: } catch (Throwable t) {
196: return null;
197: }
198: return sb.toString();
199: }
200: }
201:
202: // run-shell-command command &key :output
203: // ### %run-shell-command command directory output => exit-code
204: private static final Primitive _RUN_SHELL_COMMAND = new Primitive(
205: "%run-shell-command", PACKAGE_SYS, false) {
206: public LispObject execute(LispObject[] args)
207: throws ConditionThrowable {
208: if (args.length != 3)
209: signal(new WrongNumberOfArgumentsException(this ));
210: String command = args[0].getStringValue();
211: LispObject directory = args[1];
212: LispObject output = args[2];
213: String namestring = null;
214: Stream outputStream = null;
215: if (directory != NIL) {
216: Pathname pathname = Pathname
217: .coerceToPathname(directory);
218: namestring = pathname.getNamestring();
219: if (namestring == null) {
220: return signal(new SimpleError(
221: "Pathname has no namestring: " + pathname));
222: }
223: }
224: if (output != NIL)
225: outputStream = checkStream(output);
226: ShellCommand shellCommand = new ShellCommand(command,
227: namestring);
228: shellCommand.run();
229: if (outputStream != null)
230: outputStream._writeString(shellCommand.getOutput());
231: return number(shellCommand.exitValue());
232: }
233: };
234: }
|