source: branches/0.17.x/abcl/src/org/armedbear/lisp/ShellCommand.java

Last change on this file was 12254, checked in by ehuelsmann, 16 years ago

Remove 'throws ConditionThrowable?' method annotations:

it's an unchecked exception now, so no need to declare it thrown.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.5 KB
Line 
1/*
2 * ShellCommand.java
3 *
4 * Copyright (C) 2000-2005 Peter Graves
5 * $Id: ShellCommand.java 12254 2009-11-06 20:07:54Z ehuelsmann $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import java.io.BufferedReader;
37import java.io.IOException;
38import java.io.InputStream;
39import java.io.InputStreamReader;
40import java.util.ArrayList;
41import java.util.List;
42
43public final class ShellCommand extends Lisp implements Runnable
44{
45    private final String command;
46    private final String directory;
47    private final Stream outputStream;
48    private final StringBuffer output;
49
50    private int exitValue = -1;
51
52    public ShellCommand(String command, String directory, Stream outputStream)
53
54    {
55        this.command = command;
56        this.directory = directory;
57        this.outputStream = outputStream;
58        this.output = (outputStream == null) ? new StringBuffer() : null;
59    }
60
61    public final String getOutput()
62    {
63        return (output != null) ? output.toString() : "";
64    }
65
66    private final int exitValue()
67    {
68        return exitValue;
69    }
70
71    private void processOutput(String s)
72    {
73        if (outputStream != null)
74            outputStream._writeString(s);
75        else
76            output.append(s);
77    }
78
79    public void run()
80    {
81        Process process = null;
82        try {
83            if (command != null) {
84                if (Utilities.isPlatformUnix) {
85                    if (directory != null) {
86                        FastStringBuffer sb = new FastStringBuffer("\\cd \"");
87                        sb.append(directory);
88                        sb.append("\" && ");
89                        sb.append(command);
90                        String[] cmdarray = {"/bin/sh", "-c", sb.toString()};
91                        process = Runtime.getRuntime().exec(cmdarray);
92                    } else {
93                        String[] cmdarray = {"/bin/sh", "-c", command};
94                        process = Runtime.getRuntime().exec(cmdarray);
95                    }
96                } else if (Utilities.isPlatformWindows) {
97                    ArrayList<String> list = new ArrayList<String>();
98                    list.add("cmd.exe");
99                    list.add("/c");
100                    if (directory != null) {
101                        FastStringBuffer sb = new FastStringBuffer("cd /d \"");
102                        sb.append(directory);
103                        sb.append("\" && ");
104                        sb.append(command);
105                        list.addAll(tokenize(sb.toString()));
106                    } else
107                        list.addAll(tokenize(command));
108                    final int size = list.size();
109                    String[] cmdarray = new String[size];
110                    for (int i = 0; i < size; i++)
111                        cmdarray[i] = (String) list.get(i);
112                    process = Runtime.getRuntime().exec(cmdarray);
113                }
114            }
115        }
116        catch (IOException e) {
117            Debug.trace(e);
118        }
119        if (process != null) {
120            ReaderThread stdoutThread =
121                new ReaderThread(process.getInputStream());
122            stdoutThread.start();
123            ReaderThread stderrThread =
124                new ReaderThread(process.getErrorStream());
125            stderrThread.start();
126            try {
127                exitValue = process.waitFor();
128            }
129            catch (InterruptedException e) {
130                Debug.trace(e);
131            }
132            try {
133                stdoutThread.join();
134            }
135            catch (InterruptedException e) {
136                Debug.trace(e);
137            }
138            try {
139                stderrThread.join();
140            }
141            catch (InterruptedException e) {
142                Debug.trace(e);
143            }
144        }
145    }
146
147    // Does not handle embedded single-quoted strings.
148    private static List<String> tokenize(String s)
149    {
150        ArrayList<String> list = new ArrayList<String>();
151        StringBuffer sb = new StringBuffer();
152        boolean inQuote = false;
153        final int limit = s.length();
154        for (int i = 0; i < limit; i++) {
155            char c = s.charAt(i);
156            switch (c) {
157                case ' ':
158                    if (inQuote)
159                        sb.append(c);
160                    else if (sb.length() > 0) {
161                        list.add(sb.toString());
162                        sb.setLength(0);
163                    }
164                    break;
165                case '"':
166                    if (inQuote) {
167                        if (sb.length() > 0) {
168                            list.add(sb.toString());
169                            sb.setLength(0);
170                        }
171                        inQuote = false;
172                    } else
173                        inQuote = true;
174                    break;
175                default:
176                    sb.append(c);
177                    break;
178            }
179        }
180        if (sb.length() > 0)
181            list.add(sb.toString());
182        return list;
183    }
184
185    private class ReaderThread extends Thread
186    {
187        private char[] buf = new char[4096];
188        private final InputStream inputStream;
189        private final BufferedReader reader;
190        private boolean done = false;
191
192        public ReaderThread(InputStream inputStream)
193        {
194            this.inputStream = inputStream;
195            reader = new BufferedReader(new InputStreamReader(inputStream));
196        }
197
198        @Override
199        public void run()
200        {
201            while (!done) {
202                String s = read();
203                if (s == null)
204                    return;
205                try {
206                    processOutput(s);
207                }
208                catch (Throwable t) {
209                    Debug.trace(t);
210                }
211            }
212        }
213
214        private String read()
215        {
216            StringBuffer sb = new StringBuffer();
217            try {
218                do {
219                    int numChars = reader.read(buf, 0, buf.length); // Blocks.
220                    if (numChars < 0) {
221                        done = true;
222                        break;
223                    }
224                    if (numChars > 0)
225                        sb.append(buf, 0, numChars);
226                    Thread.sleep(10);
227                } while (reader.ready());
228            }
229            catch (IOException e) {
230                return null;
231            }
232            catch (InterruptedException e) {
233                return null;
234            }
235            catch (Throwable t) {
236                return null;
237            }
238            return sb.toString();
239        }
240    }
241
242    // run-shell-command command &key directory (output *standard-output*)
243    // ### %run-shell-command command directory output => exit-code
244    private static final Primitive _RUN_SHELL_COMMAND =
245        new Primitive("%run-shell-command", PACKAGE_SYS, false)
246    {
247        @Override
248        public LispObject execute(LispObject first, LispObject second,
249                                  LispObject third)
250
251        {
252            if (!(Utilities.isPlatformUnix || Utilities.isPlatformWindows)) {
253              return error(new LispError("run-shell-command not implemented for "
254                                       + System.getProperty("os.name")));
255            }
256            else {
257                String command = first.getStringValue();
258                String namestring = null;
259                Stream outputStream = null;
260                if (second != NIL) {
261                    Pathname pathname = coerceToPathname(second);
262                    namestring = pathname.getNamestring();
263                    if (namestring == null) {
264                        return error(new FileError("Pathname has no namestring: " + pathname.writeToString(),
265                                                    pathname));
266                    }
267                }
268                if (third != NIL)
269                    outputStream = checkStream(third);
270                ShellCommand shellCommand = new ShellCommand(command, namestring,
271                                                             outputStream);
272                shellCommand.run();
273                if (outputStream != null)
274                    outputStream._finishOutput();
275                return number(shellCommand.exitValue());
276            }
277        }
278    };
279}
Note: See TracBrowser for help on using the repository browser.