Racket/Hexdump

Från Täpp-Anders
Hoppa till navigeringHoppa till sök
#lang racket

;;;;;
;; Hexdump implementerad i Racket
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se
;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SYNOPSIS
;;         racket hexdump.rkt [ OPTIONS ] filename
;;
;; OPTIONS
;;         -h                     Gives a short help text to the utility
;;         --bytes-per-line, -n   Determines how many bytes are decoded per line (default 16)
;;         --hex, -x              Show only hex dump
;;         --ascii, -a            Show only ASCII dump

(require racket/cmdline)

(define (hexdump-file filename 
                      #:only-hex? [only-hex? #f]
                      #:only-ascii? [only-ascii? #f]
                      #:bytes-per-line [n 16])
  (printf "Läser fil: ~a (~a bytes)\n\n" filename (file-size filename))
  (call-with-input-file filename
    (λ (in)
      (let loop ([offset 0])
        (define chunk (read-bytes n in))
        (cond
          [(eof-object? chunk) (void)]
          [(bytes? chunk)
           (define len (bytes-length chunk))
           (printf "~a  " (~r offset #:base 16 #:min-width 8 #:pad-string "0"))

           (define show-both? (not (xor only-hex? only-ascii?)))

           (cond
             [(and only-hex? (not show-both?))
              (for ([i (in-range n)])
                (printf "~a " (if (< i len)
                                  (~r (bytes-ref chunk i) #:base 16 #:min-width 2 #:pad-string "0")
                                  "  "))
                (when (and (= (modulo (add1 i) 8) 0) (< i (sub1 n)))
                  (display " ")))
              (newline)]

             [(and only-ascii? (not show-both?))
              (display "   |")
              (for ([b (in-bytes chunk)]) (display (if (and (>= b 32) (<= b 126)) (integer->char b) ".")))
              (for ([_ (in-range (- n len))]) (display " "))
              (display "|\n")]

             [else  ; båda eller ingen flagga
              (for ([i (in-range n)])
                (printf "~a " (if (< i len)
                                  (~r (bytes-ref chunk i) #:base 16 #:min-width 2 #:pad-string "0")
                                  "  "))
                (when (and (= (modulo (add1 i) 8) 0) (< i (sub1 n)))
                  (display " ")))
              (display "|")
              (for ([b (in-bytes chunk)]) (display (if (and (>= b 32) (<= b 126)) (integer->char b) ".")))
              (for ([_ (in-range (- n len))]) (display " "))
              (display "|\n")])

           (loop (+ offset n))])))))

(define-values (filename only-hex? only-ascii? bytes-per-line)
  (let ([only-hex? #f] [only-ascii? #f] [bytes-per-line 16])
    (command-line
     #:program "hexdump"
     #:usage-help "Användning: racket hexdump.rkt [alternativ] <fil>"
     #:once-each
     [("-x" "--hex")   "Visa endast hex-delen"  (set! only-hex? #t)]
     [("-a" "--ascii") "Visa endast ASCII-delen" (set! only-ascii? #t)]
     [("-n" "--bytes-per-line") n
      "Antal bytes per rad (standard: 16)" (let ([num (string->number n)]) (when num (set! bytes-per-line num)))]
     #:args (filename)
     (values filename only-hex? only-ascii? bytes-per-line))))

(if (not (file-exists? filename))
    (printf "Fel: Filen \"~a\" finns inte.\n" filename)
    (hexdump-file filename 
                  #:only-hex? only-hex?
                  #:only-ascii? only-ascii?
                  #:bytes-per-line bytes-per-line))