;;; hexdump.clj -- part of the pretty printer for Clojure

;; by Tom Faulhaber
;; April 3, 2009

;   Copyright (c) Tom Faulhaber, Dec 2008. All rights reserved.
;   The use and distribution terms for this software are covered by the
;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;   which can be found in the file epl-v10.html at the root of this distribution.
;   By using this software in any fashion, you are agreeing to be bound by
;   the terms of this license.
;   You must not remove this notice, or any other, from this software.

;; This example is a classic hexdump program written using cl-format.

;; For some local color, it was written in Dulles Airport while waiting for a flight
;; home to San Francisco.

(ns clojure.contrib.pprint.examples.hexdump
  (:use clojure.contrib.pprint
        clojure.contrib.pprint.utilities)
  (:gen-class (:main true)))

(def *buffer-length* 1024)

(defn zip-array [base-offset arr]
  (let [grouped (partition 16 arr)]
    (first (map-passing-context
            (fn [line offset]
              [[offset 
                (map #(if (neg? %) (+ % 256) %) line)
                (- 16 (count line))
                (map #(if (<= 32 % 126) (char %) \.) line)]
               (+ 16 offset)])
            base-offset grouped))))


(defn hexdump 
  ([in-stream] (hexdump in-stream true 0))
  ([in-stream out-stream] (hexdump [in-stream out-stream 0]))
  ([in-stream out-stream offset] 
     (let [buf (make-array Byte/TYPE *buffer-length*)]
       (loop [offset offset
              count (.read in-stream buf)]
         (if (neg? count)
           nil
           (let [bytes (take count buf)
                 zipped (zip-array offset bytes)]
             (cl-format out-stream 
                        "~:{~8,'0X: ~2{~8@{~#[   ~:;~2,'0X ~]~}  ~}~v@{   ~}~2{~8@{~A~} ~}~%~}" 
                        zipped) 
             (recur (+ offset *buffer-length*) (.read in-stream buf))))))))

(defn hexdump-file 
  ([file-name] (hexdump-file file-name true))
  ([file-name stream] 
     (with-open [s (java.io.FileInputStream. file-name)] 
       (hexdump s))))

;; I don't quite understand how to invoke main funcs w/o AOT yet
(defn -main [& args]
  (hexdump-file (first args)))

