001    /*
002     * LAPIS lightweight structured text processing system
003     *
004     * Copyright (C) 1998-2002 Carnegie Mellon University,
005     * Copyright (C) 2003 Massachusetts Institute of Technology.
006     * All rights reserved.
007     *
008     * This library is free software; you can redistribute it
009     * and/or modify it under the terms of the GNU General
010     * Public License as published by the Free Software
011     * Foundation, version 2.
012     *
013     * LAPIS homepage: http://graphics.lcs.mit.edu/lapis/
014     */
015    
016    
017    package lapisx.util;
018    
019    import java.io.*;
020    
021    public abstract class Exec {
022        public static Debug debug = Debug.QUIET;
023    
024        public static Process exec (String[] cmdarray) throws IOException {
025            return exec (cmdarray, null, null);
026        }
027    
028        public static Process exec (String[] cmdarray, String[] envp) throws IOException {
029            return exec (cmdarray, envp, null);
030        }
031    
032        public static Process exec (String[] cmdarray, String[] envp, File directory) throws IOException {
033            return 
034                isWindows ()
035                ? execWindows (cmdarray, envp, directory)
036                : execUnix (cmdarray, envp, directory);
037        } 
038    
039        /*
040         * Unix
041         */
042    
043        static Process execUnix (String[] cmdarray, String[] envp, File directory) throws IOException {
044            // instead of calling command directly, we'll call the shell to change
045            // directory and set environment variables.
046    
047            // start constructing the sh command line.
048            StringBuffer buf = new StringBuffer ();
049    
050            if (directory != null) {
051                // change to directory
052                buf.append ("cd '");
053                buf.append (escapeQuote (directory.toString ()));
054                buf.append ("'; ");
055            }
056    
057            if (envp != null) {
058                // set environment variables.  Quote the value (but not the name).
059                for (int i = 0; i < envp.length; ++i) {
060                    String nameval = envp[i];
061                    int equals = nameval.indexOf ('=');
062                    if (equals == -1)
063                        throw new IOException ("environment variable '" + nameval 
064                                               + "' should have form NAME=VALUE");
065                    buf.append (nameval.substring (0, equals+1));
066                    buf.append ('\'');
067                    buf.append (escapeQuote (nameval.substring (equals+1)));
068                    buf.append ("\' ");
069                }
070            }
071            
072            // now that we have the directory and environment, run "which" 
073            // to test if the command name is found somewhere in the path.
074            // If "which" fails, throw an IOException.
075            String cmdname = escapeQuote (cmdarray[0]); 
076            Runtime rt = Runtime.getRuntime ();
077            String[] sharray = new String[] { "sh", "-c", buf.toString () + " which \'" + cmdname + "\'" };
078            Process which = rt.exec (sharray);
079            try {
080                which.waitFor ();
081            } catch (InterruptedException e) {
082                throw new IOException ("interrupted");
083            }
084    
085            if (which.exitValue () != 0) 
086                throw new IOException ("can't execute " + cmdname + ": bad command or filename"); 
087    
088            // finish in 
089            buf.append ("exec \'");
090            buf.append (cmdname);
091            buf.append ("\' ");
092    
093            // quote each argument in the command
094            for (int i = 1; i < cmdarray.length; ++i) {
095                buf.append ('\'');
096                buf.append (escapeQuote (cmdarray[i]));
097                buf.append ("\' ");
098            }
099    
100            debug.println ("executing " + buf);
101            sharray[2] = buf.toString ();
102            return rt.exec (sharray);
103        }
104    
105        static String escapeQuote (String s) {
106            // replace single quotes with a bit of magic (end-quote, escaped-quote, start-quote) 
107            // that works in a single-quoted string in the Unix shell
108            if (s.indexOf ('\'') != -1) {
109                debug.println ("replacing single-quotes in " + s);
110                s = Str.replace (s, "'", "'\\''");
111                debug.println ("to get " + s);
112            }
113            return s;
114        }
115    
116        /*
117         * Windows
118         */
119    
120         static boolean isWindows () {
121            String os = System.getProperty ("os.name");
122            return (os != null && os.startsWith ("Windows"));
123         }
124    
125         static boolean isJview () {
126            String vendor = System.getProperty ("java.vendor");
127            return (vendor != null && vendor.startsWith ("Microsoft"));
128         }
129    
130        static Process execWindows (String[] cmdarray, String[] envp, File directory) throws IOException {
131            if (envp != null || directory != null) {
132                if (isJview ())
133                    // jview doesn't support JNI, so can't call putenv/chdir
134                    throw new IOException 
135                        ("can't use Exec.exec() under Microsoft JVM");
136                
137                if (!linked) {
138                    try {
139                        debug.println ("loading win32exec");
140                        System.loadLibrary ("win32exec");
141                        linked = true;
142                    } catch (LinkageError e) {
143                        throw new IOException ("can't use Exec.exec(): "
144                                               + e.getMessage ());
145                    }
146                }
147                
148                if (envp != null) {
149                    for (int i = 0; i < envp.length; ++i)
150                        putenv (envp[i]);
151                }
152                
153                if (directory != null)
154                    chdir (directory.toString ());
155            }
156    
157            return Runtime.getRuntime ().exec (cmdarray);
158        }
159    
160        static boolean linked = false; // true after System.loadLibrary() is called
161        static native boolean putenv (String env);
162        static native boolean chdir (String dir);
163    }