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

Last change on this file was 12803, checked in by Mark Evenson, 14 years ago

Revert bad commit.

  • 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 12803 2010-07-12 09:55:11Z mevenson $
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 static org.armedbear.lisp.Lisp.*;
37
38import java.io.BufferedReader;
39import java.io.IOException;
40import java.io.InputStream;
41import java.io.InputStreamReader;
42import java.util.ArrayList;
43import java.util.List;
44
45public final class ShellCommand implements Runnable
46{
47    private final String command;
48    private final String directory;
49    private final Stream outputStream;
50    private final StringBuffer output;
51
52    private int exitValue = -1;
53
54    public ShellCommand(String command, String directory, Stream outputStream)
55
56    {
57        this.command = command;
58        this.directory = directory;
59        this.outputStream = outputStream;
60        this.output = (outputStream == null) ? new StringBuffer() : null;
61    }
62
63    public final String getOutput()
64    {
65        return (output != null) ? output.toString() : "";
66    }
67
68    final int exitValue()
69    {
70        return exitValue;
71    }
72
73    void processOutput(String s)
74    {
75        if (outputStream != null)
76            outputStream._writeString(s);
77        else
78            output.append(s);
79    }
80
81    public void run()
82    {
83        Process process = null;
84        try {
85            if (command != null) {
86                if (Utilities.isPlatformUnix) {
87                    if (directory != null) {
88                        StringBuilder sb = new StringBuilder("\\cd \"");
89                        sb.append(directory);
90                        sb.append("\" && ");
91                        sb.append(command);
92                        String[] cmdarray = {"/bin/sh", "-c", sb.toString()};
93                        process = Runtime.getRuntime().exec(cmdarray);
94                    } else {
95                        String[] cmdarray = {"/bin/sh", "-c", command};
96                        process = Runtime.getRuntime().exec(cmdarray);
97                    }
98                } else if (Utilities.isPlatformWindows) {
99                    ArrayList<String> list = new ArrayList<String>();
100                    list.add("cmd.exe");
101                    list.add("/c");
102                    if (directory != null) {
103                        StringBuilder sb = new StringBuilder("cd /d \"");
104                        sb.append(directory);
105                        sb.append("\" && ");
106                        sb.append(command);
107                        list.addAll(tokenize(sb.toString()));
108                    } else
109                        list.addAll(tokenize(command));
110                    final int size = list.size();
111                    String[] cmdarray = new String[size];
112                    for (int i = 0; i < size; i++)
113                        cmdarray[i] = (String) list.get(i);
114                    process = Runtime.getRuntime().exec(cmdarray);
115                }
116            }
117        }
118        catch (IOException e) {
119            Debug.trace(e);
120        }
121        if (process != null) {
122            ReaderThread stdoutThread =
123                new ReaderThread(process.getInputStream());
124            stdoutThread.start();
125            ReaderThread stderrThread =
126                new ReaderThread(process.getErrorStream());
127            stderrThread.start();
128            try {
129                exitValue = process.waitFor();
130            }
131            catch (InterruptedException e) {
132                Debug.trace(e);
133            }
134            try {
135                stdoutThread.join();
136            }
137            catch (InterruptedException e) {
138                Debug.trace(e);
139            }
140            try {
141                stderrThread.join();
142            }
143            catch (InterruptedException e) {
144                Debug.trace(e);
145            }
146        }
147    }
148
149    // Does not handle embedded single-quoted strings.
150    private static List<String> tokenize(String s)
151    {
152        ArrayList<String> list = new ArrayList<String>();
153        StringBuffer sb = new StringBuffer();
154        boolean inQuote = false;
155        final int limit = s.length();
156        for (int i = 0; i < limit; i++) {
157            char c = s.charAt(i);
158            switch (c) {
159                case ' ':
160                    if (inQuote)
161                        sb.append(c);
162                    else if (sb.length() > 0) {
163                        list.add(sb.toString());
164                        sb.setLength(0);
165                    }
166                    break;
167                case '"':
168                    if (inQuote) {
169                        if (sb.length() > 0) {
170                            list.add(sb.toString());
171                            sb.setLength(0);
172                        }
173                        inQuote = false;
174                    } else
175                        inQuote = true;
176                    break;
177                default:
178                    sb.append(c);
179                    break;
180            }
181        }
182        if (sb.length() > 0)
183            list.add(sb.toString());
184        return list;
185    }
186
187    private class ReaderThread extends Thread
188    {
189        private char[] buf = new char[4096];
190        private final InputStream inputStream;
191        private final BufferedReader reader;
192        private boolean done = false;
193
194        public ReaderThread(InputStream inputStream)
195        {
196            this.inputStream = inputStream;
197            reader = new BufferedReader(new InputStreamReader(inputStream));
198        }
199
200        @Override
201        public void run()
202        {
203            while (!done) {
204                String s = read();
205                if (s == null)
206                    return;
207                processOutput(s);
208            }
209        }
210
211        private String read()
212        {
213            StringBuffer sb = new StringBuffer();
214            try {
215                do {
216                    int numChars = reader.read(buf, 0, buf.length); // Blocks.
217                    if (numChars < 0) {
218                        done = true;
219                        break;
220                    }
221                    if (numChars > 0)
222                        sb.append(buf, 0, numChars);
223                    Thread.sleep(10);
224                } while (reader.ready());
225            }
226            catch (IOException e) {
227                return null;
228            }
229            catch (InterruptedException e) {
230                return null;
231            }
232            return sb.toString();
233        }
234    }
235
236    // run-shell-command command &key directory (output *standard-output*)
237    // ### %run-shell-command command directory output => exit-code
238    private static final Primitive _RUN_SHELL_COMMAND = new pf_run_shell_command();
239    private static class pf_run_shell_command extends Primitive {
240        pf_run_shell_command() {
241            super("%run-shell-command", PACKAGE_SYS, false,
242                  "command directory output => exit-code");
243        }
244       
245        @Override
246        public LispObject execute(LispObject first, LispObject second,
247                                  LispObject third)
248
249        {
250            if (!(Utilities.isPlatformUnix || Utilities.isPlatformWindows)) {
251              return error(new LispError("run-shell-command not implemented for "
252                                       + System.getProperty("os.name")));
253            }
254            else {
255                String command = first.getStringValue();
256                String namestring = null;
257                Stream outputStream = null;
258                if (second != NIL) {
259                    Pathname pathname = coerceToPathname(second);
260                    namestring = pathname.getNamestring();
261                    if (namestring == null) {
262                        return error(new FileError("Pathname has no namestring: " + pathname.writeToString(),
263                                                    pathname));
264                    }
265                }
266                if (third != NIL)
267                    outputStream = checkStream(third);
268                ShellCommand shellCommand = new ShellCommand(command, namestring,
269                                                             outputStream);
270                shellCommand.run();
271                if (outputStream != null)
272                    outputStream._finishOutput();
273                return number(shellCommand.exitValue());
274            }
275        }
276    };
277}
Note: See TracBrowser for help on using the repository browser.