source: branches/0.20.x/abcl/src/org/armedbear/lisp/ShellCommand.java @ 12669

Last change on this file since 12669 was 12513, checked in by ehuelsmann, 15 years ago

Remove 'private' keyword to eliminate the Java requirement

for the compiler to generate synthetic accessors: functions that
don't appear in the source but do appear in the class file.

Patch by: Douglas Miles <dmiles _at_ users.sf.net>

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.3 KB
Line 
1/*
2 * ShellCommand.java
3 *
4 * Copyright (C) 2000-2005 Peter Graves
5 * $Id: ShellCommand.java 12513 2010-03-02 22:35:36Z 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 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 =
239        new Primitive("%run-shell-command", PACKAGE_SYS, false)
240    {
241        @Override
242        public LispObject execute(LispObject first, LispObject second,
243                                  LispObject third)
244
245        {
246            if (!(Utilities.isPlatformUnix || Utilities.isPlatformWindows)) {
247              return error(new LispError("run-shell-command not implemented for "
248                                       + System.getProperty("os.name")));
249            }
250            else {
251                String command = first.getStringValue();
252                String namestring = null;
253                Stream outputStream = null;
254                if (second != NIL) {
255                    Pathname pathname = coerceToPathname(second);
256                    namestring = pathname.getNamestring();
257                    if (namestring == null) {
258                        return error(new FileError("Pathname has no namestring: " + pathname.writeToString(),
259                                                    pathname));
260                    }
261                }
262                if (third != NIL)
263                    outputStream = checkStream(third);
264                ShellCommand shellCommand = new ShellCommand(command, namestring,
265                                                             outputStream);
266                shellCommand.run();
267                if (outputStream != null)
268                    outputStream._finishOutput();
269                return number(shellCommand.exitValue());
270            }
271        }
272    };
273}
Note: See TracBrowser for help on using the repository browser.