Mercurial > lasercutter
view src/clojure/contrib/shell.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line source
1 ; Copyright (c) Chris Houser, Jan 2009. All rights reserved.2 ; The use and distribution terms for this software are covered by the3 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)4 ; which can be found in the file epl-v10.html at the root of this distribution.5 ; By using this software in any fashion, you are agreeing to be bound by6 ; the terms of this license.7 ; You must not remove this notice, or any other, from this software.9 ; :dir and :env options added by Stuart Halloway11 ; Conveniently launch a sub-process providing to its stdin and12 ; collecting its stdout14 ;; DEPRECATED in 1.2: Promoted to clojure.java.shell16 (ns17 ^{:author "Chris Houser",18 :deprecated "1.2"19 :doc "Conveniently launch a sub-process providing to its stdin and20 collecting its stdout"}21 clojure.contrib.shell22 (:import (java.io InputStreamReader OutputStreamWriter)))24 (def *sh-dir* nil)25 (def *sh-env* nil)27 (defmacro with-sh-dir [dir & forms]28 "Sets the directory for use with sh, see sh for details."29 `(binding [*sh-dir* ~dir]30 ~@forms))32 (defmacro with-sh-env [env & forms]33 "Sets the environment for use with sh, see sh for details."34 `(binding [*sh-env* ~env]35 ~@forms))37 (defn- stream-seq38 "Takes an InputStream and returns a lazy seq of integers from the stream."39 [stream]40 (take-while #(>= % 0) (repeatedly #(.read stream))))42 (defn- aconcat43 "Concatenates arrays of given type."44 [type & xs]45 (let [target (make-array type (apply + (map count xs)))]46 (loop [i 0 idx 0]47 (when-let [a (nth xs i nil)]48 (System/arraycopy a 0 target idx (count a))49 (recur (inc i) (+ idx (count a)))))50 target))52 (defn- parse-args53 "Takes a seq of 'sh' arguments and returns a map of option keywords54 to option values."55 [args]56 (loop [[arg :as args] args opts {:cmd [] :out "UTF-8" :dir *sh-dir* :env *sh-env*}]57 (if-not args58 opts59 (if (keyword? arg)60 (recur (nnext args) (assoc opts arg (second args)))61 (recur (next args) (update-in opts [:cmd] conj arg))))))63 (defn- as-env-key [arg]64 "Helper so that callers can use symbols, keywords, or strings65 when building an environment map."66 (cond67 (symbol? arg) (name arg)68 (keyword? arg) (name arg)69 (string? arg) arg))71 (defn- as-file [arg]72 "Helper so that callers can pass a String for the :dir to sh."73 (cond74 (string? arg) (java.io.File. arg)75 (nil? arg) nil76 (instance? java.io.File arg) arg))78 (defn- as-env-string [arg]79 "Helper so that callers can pass a Clojure map for the :env to sh."80 (cond81 (nil? arg) nil82 (map? arg) (into-array String (map (fn [[k v]] (str (as-env-key k) "=" v)) arg))83 true arg))86 (defn sh87 "Passes the given strings to Runtime.exec() to launch a sub-process.89 Options are91 :in may be given followed by a String specifying text to be fed to the92 sub-process's stdin.93 :out option may be given followed by :bytes or a String. If a String94 is given, it will be used as a character encoding name (for95 example \"UTF-8\" or \"ISO-8859-1\") to convert the96 sub-process's stdout to a String which is returned.97 If :bytes is given, the sub-process's stdout will be stored in98 a byte array and returned. Defaults to UTF-8.99 :return-map100 when followed by boolean true, sh returns a map of101 :exit => sub-process's exit code102 :out => sub-process's stdout (as byte[] or String)103 :err => sub-process's stderr (as byte[] or String)104 when not given or followed by false, sh returns a single105 array or String of the sub-process's stdout followed by its106 stderr107 :env override the process env with a map (or the underlying Java108 String[] if you are a masochist).109 :dir override the process dir with a String or java.io.File.111 You can bind :env or :dir for multiple operations using with-sh-env112 and with-sh-dir."113 [& args]114 (let [opts (parse-args args)115 proc (.exec (Runtime/getRuntime)116 (into-array (:cmd opts))117 (as-env-string (:env opts))118 (as-file (:dir opts)))]119 (if (:in opts)120 (with-open [osw (OutputStreamWriter. (.getOutputStream proc))]121 (.write osw (:in opts)))122 (.close (.getOutputStream proc)))123 (with-open [stdout (.getInputStream proc)124 stderr (.getErrorStream proc)]125 (let [[[out err] combine-fn]126 (if (= (:out opts) :bytes)127 [(for [strm [stdout stderr]]128 (into-array Byte/TYPE (map byte (stream-seq strm))))129 #(aconcat Byte/TYPE %1 %2)]130 [(for [strm [stdout stderr]]131 (apply str (map char (stream-seq132 (InputStreamReader. strm (:out opts))))))133 str])134 exit-code (.waitFor proc)]135 (if (:return-map opts)136 {:exit exit-code :out out :err err}137 (combine-fn out err))))))139 (comment141 (println (sh "ls" "-l"))142 (println (sh "ls" "-l" "/no-such-thing"))143 (println (sh "sed" "s/[aeiou]/oo/g" :in "hello there\n"))144 (println (sh "cat" :in "x\u25bax\n"))145 (println (sh "echo" "x\u25bax"))146 (println (sh "echo" "x\u25bax" :out "ISO-8859-1")) ; reads 4 single-byte chars147 (println (sh "cat" "myimage.png" :out :bytes)) ; reads binary file into bytes[]149 )