   a simple command-line UI letting user pick one, unless
   choose-first-discovered? is set to true in which case the UI is bypassed"
   [choose-first-discovered? opts]
+  {:pre [(map? opts)]}
   (let [reg-type "_http._tcp.local."
         name-endpoint-map (atom (sorted-map))
         mdns-service (JmDNS/create)


(defn- mount-webdav
  [repl-env bonjour-name endpoint-address endpoint-port]
  (let [webdav-mount-point (str "/Volumes/Ambly-" (format "%08X" (hash bonjour-name)))
        output-dir (io/file webdav-mount-point)]
    (when (.exists output-dir)


(defn sh
  [& args]
  {:pre [(every? string? args)]}
  (.waitFor (.exec (Runtime/getRuntime) (string/join " " args))))

(defn print-fn [opts]
  {:pre [(map? opts)]}
  (or (:print-no-newline opts) print))

(defn println-fn [opts]
  {:pre [(map? opts)]}
  (or (:print opts) println))

(defn flush-fn [opts]
  {:pre [(map? opts)]}
  (or (:flush opts) flush))

(defn set-logging-level [logger-name level]
  {:pre [(string? logger-name) (instance? java.util.logging.Level level)]}
  (.setLevel (java.util.logging.Logger/getLogger logger-name) level))

(def ambly-bonjour-name-prefix "Ambly ")

(defn is-ambly-bonjour-name? [bonjour-name]
  {:pre [(string? bonjour-name)]}
  (.startsWith bonjour-name ambly-bonjour-name-prefix))

(defn bonjour-name->display-name
  [bonjour-name]
  {:pre [(is-ambly-bonjour-name? bonjour-name)]
   :post [(string? %)]}
  (subs bonjour-name (count ambly-bonjour-name-prefix)))

(defn name-endpoint-map->choice-list [name-endpoint-map]
  {:pre [(map? name-endpoint-map)]}
  (map vector (iterate inc 1) name-endpoint-map))

(defn print-discovered-devices [name-endpoint-map opts]
  {:pre [(map? name-endpoint-map) (map? opts)]}
  (if (empty? name-endpoint-map)
    ((println-fn opts) "(No devices)")
    (doseq [[choice-number [bonjour-name _]] (name-endpoint-map->choice-list name-endpoint-map)]
