diff --git a/lisp/hexl.el b/lisp/hexl.el index 2cdd449..0eebe55 100644 --- a/lisp/hexl.el +++ b/lisp/hexl.el @@ -51,6 +51,13 @@ "Edit a file in a hex dump format using the hexl filter." :group 'data) +(defcustom hexl-bits 16 + "The bit grouping that hexl will use" + :type '(choice (const 8 ) + (const 16) + (const 32) + (const 64)) + :group 'hexl) (defcustom hexl-program "hexl" "The program that will hexlify and dehexlify its stdin. @@ -135,6 +142,15 @@ Quoting cannot be used, so the arguments cannot themselves contain spaces." (put 'hexl-mode 'mode-class 'special) +;; 10 chars for the "address: " +;; 32 chars for the hexlified bytes +;; 1 char for the space +;; 16 chars for the character display +;; X chars for the spaces (128 bits divided by the hexl-bits) +;; 1 char for the newline. +(defun hexl-line-displen () + (+ 60 (/ 128 (or hexl-bits 16)))) + ;;;###autoload (defun hexl-mode (&optional arg) "\\A mode for editing binary files in hex dump format. @@ -213,27 +229,33 @@ You can use \\[hexl-find-file] to visit a file in Hexl mode. (unless (eq major-mode 'hexl-mode) (let ((modified (buffer-modified-p)) (inhibit-read-only t) - (original-point (- (point) (point-min)))) + (original-point (- (point) (point-min))) + max-address) (and (eobp) (not (bobp)) (setq original-point (1- original-point))) - ;; If `hexl-mode' is invoked with an argument the buffer is assumed to - ;; be in hexl format. - (when (memq arg '(1 nil)) + (if (not (memq arg '(1 nil))) + ;; if no argument then we guess at hexl-max-address + (setq max-address + (+ (* (/ (1- (buffer-size)) (hexl-line-displen)) 16) 15)) + (setq max-address (1- (buffer-size))) ;; If the buffer's EOL type is -dos, we need to account for ;; extra CR characters added when hexlify-buffer writes the ;; buffer to a file. ;; FIXME: This doesn't take into account multibyte coding systems. (when (eq (coding-system-eol-type buffer-file-coding-system) 1) - (setq original-point (+ (count-lines (point-min) (point)) + (setq max-address (+ (count-lines (point-min) (point-max)) + max-address)) + ;; But if there's no newline at the last line, we are off by + ;; one; adjust. + (or (eq (char-before (point-max)) ?\n) + (setq max-address (1- max-address))) + (setq original-point (+ (count-lines (point-min) (point)) original-point)) (or (bolp) (setq original-point (1- original-point)))) (hexlify-buffer) (restore-buffer-modified-p modified)) - (set (make-local-variable 'hexl-max-address) - (let* ((full-lines (/ (buffer-size) 68)) - (last-line (% (buffer-size) 68)) - (last-line-bytes (% last-line 52))) - (+ last-line-bytes (* full-lines 16) -1))) + (make-local-variable 'hexl-max-address) + (setq hexl-max-address max-address) (condition-case nil (hexl-goto-address original-point) (error nil))) @@ -436,19 +458,23 @@ Ask the user for confirmation." (defun hexl-current-address (&optional validate) "Return current hexl-address." (interactive) - (let ((current-column (- (% (- (point) (point-min) -1) 68) 11)) + (let ((current-column + (- (% (- (point) (point-min) -1) (hexl-line-displen)) 11)) (hexl-address 0)) (if (< current-column 0) (if validate (error "Point is not on a character in the file") (setq current-column 0))) (setq hexl-address - (+ (* (/ (- (point) (point-min) -1) 68) 16) - (if (>= current-column 41) - (- current-column 41) - (/ (- current-column (/ current-column 5)) 2)))) + (+ (* (/ (- (point) (point-min) -1) + (hexl-line-displen)) 16) + (if (>= current-column (- (hexl-ascii-start-column) 10)) + (- current-column (- (hexl-ascii-start-column) 10)) + (/ (- current-column + (/ current-column (1+ (/ hexl-bits 4)))) 2) )) ) (when (interactive-p) - (message "Current address is %d/0x%08x" hexl-address hexl-address)) + (message "Current address is [column: %d] %d/0x%08x" + current-column hexl-address hexl-address)) hexl-address)) (defun hexl-print-current-point-info () @@ -457,10 +483,18 @@ This function is intended to be used as eldoc callback." (let ((addr (hexl-current-address))) (format "Current address is %d/0x%08x" addr addr))) +(defun hexl-ascii-start-column () + "Column at which the ascii display starts." + (+ 43 (/ 128 hexl-bits))) + (defun hexl-address-to-marker (address) "Return buffer position for ADDRESS." (interactive "nAddress: ") - (+ (* (/ address 16) 68) 10 (point-min) (/ (* (% address 16) 5) 2))) + (let ((N (* (% address 16) 2))) + (+ (* (/ address 16) (hexl-line-displen)) ; hexl line no * display length + 10 ; 10 chars for the "address: " prefix + (point-min) ; base offset (point usually starts at 1, not 0) + (+ N (/ N (/ hexl-bits 4))) )) ) ; char offset into hexl display line (defun hexl-goto-address (address) "Go to hexl-mode (decimal) address ADDRESS. @@ -505,7 +539,7 @@ Signal error if HEX-ADDRESS is out of range." (hexl-goto-address (- (hexl-current-address) arg))) (defun hexl-forward-char (arg) - "Move to right ARG bytes (left if ARG negative) in hexl-mode." + "Move right ARG bytes (left if ARG negative) in hexl-mode." (interactive "p") (hexl-goto-address (+ (hexl-current-address) arg))) @@ -544,7 +578,7 @@ Signal error if HEX-ADDRESS is out of range." address))) (defun hexl-forward-short (arg) - "Move to right ARG shorts (left if ARG negative) in hexl-mode." + "Move right ARG shorts (left if ARG negative) in hexl-mode." (interactive "p") (hexl-backward-short (- arg))) @@ -583,7 +617,7 @@ Signal error if HEX-ADDRESS is out of range." address))) (defun hexl-forward-word (arg) - "Move to right ARG words (left if ARG negative) in hexl-mode." + "Move right ARG words (left if ARG negative) in hexl-mode." (interactive "p") (hexl-backward-word (- arg))) @@ -630,7 +664,7 @@ With prefix arg N, puts point N bytes of the way from the true beginning." (defun hexl-beginning-of-line () "Goto beginning of line in hexl mode." (interactive) - (goto-char (+ (* (/ (point) 68) 68) 11))) + (goto-char (+ (* (/ (point) (hexl-line-displen)) (hexl-line-displen)) 11))) (defun hexl-end-of-line () "Goto end of line in hexl mode." @@ -675,12 +709,12 @@ If there's no byte at the target address, move to the first or last line." (recenter 0))) (defun hexl-beginning-of-1k-page () - "Go to beginning of 1KB boundary." + "Go to the beginning of the current 1 KiB block" (interactive) (hexl-goto-address (logand (hexl-current-address) -1024))) (defun hexl-end-of-1k-page () - "Go to end of 1KB boundary." + "Go to the end of the current 1 KiB block." (interactive) (hexl-goto-address (let ((address (logior (hexl-current-address) 1023))) (if (> address hexl-max-address) @@ -710,6 +744,15 @@ You may also type octal digits, to insert a character with that code." ;00000000: 0011 2233 4455 6677 8899 aabb ccdd eeff 0123456789ABCDEF +(defun hexl-options (&optional test) + (let ((opts (or test hexl-options))) + (when (memq hexl-bits '(8 16 32 64)) + (when (string-match "\\(.*\\)-group-by-[0-9]+-bits\\(.*\\)" opts) + (setq opts (concat (match-string 1 opts) + (match-string 2 opts))) ) + (setq opts (format "%s -group-by-%d-bits " opts hexl-bits)) ) + opts)) + ;;;###autoload (defun hexlify-buffer () "Convert a binary buffer to hexl format. @@ -732,7 +775,7 @@ This discards the buffer's undo information." (mapcar (lambda (s) (if (not (multibyte-string-p s)) s (encode-coding-string s locale-coding-system))) - (split-string hexl-options))) + (split-string (hexl-options)))) (if (> (point) (hexl-address-to-marker hexl-max-address)) (hexl-goto-address hexl-max-address)))) @@ -749,7 +792,7 @@ This discards the buffer's undo information." (buffer-undo-list t)) (apply 'call-process-region (point-min) (point-max) (expand-file-name hexl-program exec-directory) - t t nil "-de" (split-string hexl-options)))) + t t nil "-de" (split-string (hexl-options))))) (defun hexl-char-after-point () "Return char for ASCII hex digits at point." @@ -846,12 +889,15 @@ CH must be a unibyte character whose value is between 0 and 255." (let ((address (hexl-current-address t))) (while (> num 0) (let ((hex-position - (+ (* (/ address 16) 68) + (+ (* (/ address 16) (hexl-line-displen)) 10 (point-min) (* 2 (% address 16)) (/ (% address 16) 2))) (ascii-position - (+ (* (/ address 16) 68) 51 (point-min) (% address 16))) + (+ (* (/ address 16) (hexl-line-displen)) + (hexl-ascii-start-column) + (point-min) + (% address 16))) at-ascii-position) (if (= (point) ascii-position) (setq at-ascii-position t)) @@ -867,7 +913,7 @@ CH must be a unibyte character whose value is between 0 and 255." (if at-ascii-position (progn (beginning-of-line) - (forward-char 51) + (forward-char (hexl-ascii-start-column)) (forward-char (% address 16))))) (setq num (1- num))))) @@ -985,7 +1031,7 @@ This function is assumed to be used as callback function for `hl-line-mode'." (defun hexl-follow-ascii-find () "Find and highlight the ASCII element corresponding to current point." - (let ((pos (+ 51 + (let ((pos (+ (hexl-ascii-start-column) (- (point) (current-column)) (mod (hexl-current-address) 16)))) (move-overlay hexl-ascii-overlay pos (1+ pos))