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

Last change on this file was 13440, checked in by ehuelsmann, 13 years ago

Rename writeToString() to printObject() since that's what it's being used for.
Additionally, create princToString() for use in error messages, making the

required replacement where appropriate.

  • 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 13440 2011-08-05 21:25:10Z 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 = 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.princToString(),
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.