clojureでtar.gzファイルにアクセスしてみる

元々はJavaでtar.gzファイルにアクセスする方法を調べていて、Clojureの勉強も兼ねてそっちのコードも書いてみる。
leiningenでプロジェクト作って試していたので、-main関数とかも書いて、引数でファイル名を受け取れるようにしている。
ポイントはloopマクロ、recur辺りかな。
プログラミングClojureを読んでいるときにはloopマクロがイマイチよく分からなかったけど、実際に書いてみると何となく分かった気がする。

(ns sample.core
  (:gen-class)
  (:import (org.apache.tools.tar TarInputStream)
	   (java.util.zip GZIPInputStream)
	   (java.io FileInputStream ByteArrayOutputStream)))

;tar.gz内のファイル名をベクタで取得する
(defn get-targz-names
  [file_name]
  (with-open [file (TarInputStream. (GZIPInputStream. (FileInputStream. file_name)))]
    (loop [file_list [] entry (.getNextEntry file)]
      (if (nil? entry) file_list
	  (recur (conj file_list (.getName entry)) (.getNextEntry file))))))

;tar.gz内のファイルを標準出力に表示する
(defn print-targz-file
  [file_name]
  (with-open [file (TarInputStream. (GZIPInputStream. (FileInputStream. file_name)))]
    (loop [entry (.getNextEntry file)]
      (if (nil? entry)
	nil
	(do
	  (let [size (.getSize entry) bos (ByteArrayOutputStream. size)]
	   ;(.copyEntryContents file bos)
	    (. file copyEntryContents bos)
	    (println (String. (.toByteArray bos))))
	  (recur (.getNextEntry file)))))))

(defn -main
  [& args]
  (def file_name (first args))
  (println (get-targz-names file_name))
  (print-targz-file file_name))

leiningenで使うproject.cljファイルはこんな感じ。
TarInputStreamはApache-Antに含まれているので、依存ライブラリとして追加している。

(defproject sample "1.0.0-SNAPSHOT"
  :description "FIXME: write"
  :dependencies [[org.clojure/clojure "1.1.0"]
                 [org.clojure/clojure-contrib "1.1.0"]
                 [org.apache.ant/ant "1.8.0"]]
  :main sample.core)