Merge from emacs-24; up to 2012-12-23T02:41:17Z!rgm@gnu.org
[bpt/emacs.git] / lisp / hexl.el
CommitLineData
2fbc1934 1;;; hexl.el --- edit a file in a hex dump format using the hexl filter -*- lexical-binding: t -*-
e5167999 2
ab422c4d
PE
3;; Copyright (C) 1989, 1994, 1998, 2001-2013 Free Software Foundation,
4;; Inc.
a2535589 5
3a801d0c 6;; Author: Keith Gabryelski <ag@wheaties.ai.mit.edu>
b7f66977
RS
7;; Maintainer: FSF
8;; Keywords: data
3a801d0c 9
a2535589
JA
10;; This file is part of GNU Emacs.
11
eb3fa2cf 12;; GNU Emacs is free software: you can redistribute it and/or modify
a2535589 13;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
a2535589
JA
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
eb3fa2cf 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
a2535589 24
e5167999
ER
25;;; Commentary:
26
e41b2db1
ER
27;; This package implements a major mode for editing binary files. It uses
28;; a program called hexl, supplied with the GNU Emacs distribution, that
29;; can filter a binary into an editable format or from the format back into
30;; binary. For full instructions, invoke `hexl-mode' on an empty buffer and
a0164df5 31;; do M-x `describe-mode'.
e41b2db1 32;;
a0164df5 33;; NOTE: Remember to change `hexl-program' or `hexl-options' if needed.
a2535589
JA
34;;
35;; Currently hexl only supports big endian hex output with 16 bit
36;; grouping.
37;;
38;; -iso in `hexl-options' will allow iso characters to display in the
37dc4767 39;; ASCII region of the screen (if your Emacs supports this) instead of
a2535589
JA
40;; changing them to dots.
41
e5167999
ER
42;;; Code:
43
01ff9136 44(require 'eldoc)
a464a6c7 45(eval-when-compile (require 'cl-lib))
01ff9136 46
a2535589
JA
47;;
48;; vars here
49;;
50
00ed33e7
RS
51(defgroup hexl nil
52 "Edit a file in a hex dump format using the hexl filter."
53 :group 'data)
54
0d15b5ba
VD
55(defcustom hexl-bits 16
56 "The bit grouping that hexl will use."
57 :type '(choice (const 8 )
58 (const 16)
59 (const 32)
60 (const 64))
da748f05 61 :group 'hexl
2a1e2476 62 :version "24.3")
00ed33e7
RS
63
64(defcustom hexl-program "hexl"
65e5f4bc 65 "The program that will hexlify and dehexlify its stdin.
57f07931 66`hexl-program' will always be concatenated with `hexl-options'
00ed33e7
RS
67and \"-de\" when dehexlifying a buffer."
68 :type 'string
69 :group 'hexl)
a2535589 70
00ed33e7 71(defcustom hexl-iso ""
37dc4767 72 "If your Emacs can handle ISO characters, this should be set to
00ed33e7
RS
73\"-iso\" otherwise it should be \"\"."
74 :type 'string
75 :group 'hexl)
a2535589 76
00ed33e7 77(defcustom hexl-options (format "-hex %s" hexl-iso)
b8c49a19 78 "Space separated options to `hexl-program' that suit your needs.
0d15b5ba
VD
79Quoting cannot be used, so the arguments cannot themselves contain spaces.
80If you wish to set the `-group-by-X-bits' options, set `hexl-bits' instead,
81as that will override any bit grouping options set here."
00ed33e7
RS
82 :type 'string
83 :group 'hexl)
a2535589 84
db6c5b92
SE
85(defcustom hexl-follow-ascii t
86 "If non-nil then highlight the ASCII character corresponding to point."
87 :type 'boolean
cd32a7ba
DN
88 :group 'hexl
89 :version "20.3")
db6c5b92 90
9fd76d04
MY
91(defcustom hexl-mode-hook '(hexl-follow-line hexl-activate-ruler)
92 "Normal hook run when entering Hexl mode."
93 :type 'hook
94 :options '(hexl-follow-line hexl-activate-ruler turn-on-eldoc-mode)
95 :group 'hexl)
96
2d77d354 97(defface hexl-address-region
9fd76d04 98 '((t (:inherit header-line)))
5232b4cd 99 "Face used in address area of hexl-mode buffer."
9fd76d04
MY
100 :group 'hexl)
101
2d77d354 102(defface hexl-ascii-region
9fd76d04 103 '((t (:inherit header-line)))
5232b4cd 104 "Face used in ascii area of hexl-mode buffer."
9fd76d04
MY
105 :group 'hexl)
106
a2535589
JA
107(defvar hexl-max-address 0
108 "Maximum offset into hexl buffer.")
109
a0310a6c
DN
110(defvar hexl-mode-map
111 (let ((map (make-keymap)))
112 ;; Make all self-inserting keys go through hexl-self-insert-command,
113 ;; because we need to convert them to unibyte characters before
114 ;; inserting them into the buffer.
115 (define-key map [remap self-insert-command] 'hexl-self-insert-command)
116
117 (define-key map "\C-m" 'hexl-self-insert-command)
118 (define-key map [left] 'hexl-backward-char)
119 (define-key map [right] 'hexl-forward-char)
120 (define-key map [up] 'hexl-previous-line)
121 (define-key map [down] 'hexl-next-line)
122 (define-key map [M-left] 'hexl-backward-short)
123 (define-key map [?\e left] 'hexl-backward-short)
124 (define-key map [M-right] 'hexl-forward-short)
125 (define-key map [?\e right] 'hexl-forward-short)
126 (define-key map [next] 'hexl-scroll-up)
127 (define-key map [prior] 'hexl-scroll-down)
128 (define-key map [home] 'hexl-beginning-of-line)
129 (define-key map [end] 'hexl-end-of-line)
130 (define-key map [C-home] 'hexl-beginning-of-buffer)
131 (define-key map [C-end] 'hexl-end-of-buffer)
132 (define-key map [deletechar] 'undefined)
133 (define-key map [deleteline] 'undefined)
134 (define-key map [insertline] 'undefined)
135 (define-key map [S-delete] 'undefined)
136 (define-key map "\177" 'undefined)
137
138 (define-key map "\C-a" 'hexl-beginning-of-line)
139 (define-key map "\C-b" 'hexl-backward-char)
140 (define-key map "\C-d" 'undefined)
141 (define-key map "\C-e" 'hexl-end-of-line)
142 (define-key map "\C-f" 'hexl-forward-char)
143
144 (if (not (memq (key-binding (char-to-string help-char))
145 '(help-command ehelp-command)))
146 (define-key map (char-to-string help-char) 'undefined))
147
148 (define-key map "\C-k" 'undefined)
149 (define-key map "\C-n" 'hexl-next-line)
150 (define-key map "\C-o" 'undefined)
151 (define-key map "\C-p" 'hexl-previous-line)
152 (define-key map "\C-q" 'hexl-quoted-insert)
153 (define-key map "\C-t" 'undefined)
154 (define-key map "\C-v" 'hexl-scroll-up)
155 (define-key map "\C-w" 'undefined)
156 (define-key map "\C-y" 'undefined)
157
158 (fset 'hexl-ESC-prefix (copy-keymap 'ESC-prefix))
159 (define-key map "\e" 'hexl-ESC-prefix)
160 (define-key map "\e\C-a" 'hexl-beginning-of-512b-page)
161 (define-key map "\e\C-b" 'hexl-backward-short)
162 (define-key map "\e\C-d" 'hexl-insert-decimal-char)
163 (define-key map "\e\C-e" 'hexl-end-of-512b-page)
164 (define-key map "\e\C-f" 'hexl-forward-short)
165 (define-key map "\e\C-i" 'undefined)
166 (define-key map "\e\C-j" 'undefined)
167 (define-key map "\e\C-k" 'undefined)
168 (define-key map "\e\C-o" 'hexl-insert-octal-char)
169 (define-key map "\e\C-q" 'undefined)
170 (define-key map "\e\C-t" 'undefined)
171 (define-key map "\e\C-x" 'hexl-insert-hex-char)
172 (define-key map "\eb" 'hexl-backward-word)
173 (define-key map "\ec" 'undefined)
174 (define-key map "\ed" 'undefined)
175 (define-key map "\ef" 'hexl-forward-word)
176 (define-key map "\eg" 'hexl-goto-hex-address)
177 (define-key map "\ei" 'undefined)
178 (define-key map "\ej" 'hexl-goto-address)
179 (define-key map "\ek" 'undefined)
180 (define-key map "\el" 'undefined)
181 (define-key map "\eq" 'undefined)
182 (define-key map "\es" 'undefined)
183 (define-key map "\et" 'undefined)
184 (define-key map "\eu" 'undefined)
185 (define-key map "\ev" 'hexl-scroll-down)
186 (define-key map "\ey" 'undefined)
187 (define-key map "\ez" 'undefined)
188 (define-key map "\e<" 'hexl-beginning-of-buffer)
189 (define-key map "\e>" 'hexl-end-of-buffer)
190
191 (fset 'hexl-C-c-prefix (copy-keymap mode-specific-map))
192 (define-key map "\C-c" 'hexl-C-c-prefix)
193 (define-key map "\C-c\C-c" 'hexl-mode-exit)
194
195 (fset 'hexl-C-x-prefix (copy-keymap 'Control-X-prefix))
196 (define-key map "\C-x" 'hexl-C-x-prefix)
197 (define-key map "\C-x[" 'hexl-beginning-of-1k-page)
198 (define-key map "\C-x]" 'hexl-end-of-1k-page)
199 (define-key map "\C-x\C-p" 'undefined)
200 (define-key map "\C-x\C-s" 'hexl-save-buffer)
201 (define-key map "\C-x\C-t" 'undefined)
202 map))
a2535589 203
3fc29559 204;; Variable declarations for suppressing warnings from the byte-compiler.
b27ce24f
RS
205(defvar ruler-mode)
206(defvar ruler-mode-ruler-function)
207(defvar hl-line-mode)
3fc29559
MY
208(defvar hl-line-range-function)
209(defvar hl-line-face)
b27ce24f 210
3fc29559 211;; Variables where the original values are stored to.
4391b429
SM
212(defvar hexl-mode--old-var-vals ())
213(make-variable-buffer-local 'hexl-mode--old-var-vals)
f39c6650 214
db6c5b92
SE
215(defvar hexl-ascii-overlay nil
216 "Overlay used to highlight ASCII element corresponding to current point.")
217(make-variable-buffer-local 'hexl-ascii-overlay)
218
639b8e4d
MY
219(defvar hexl-font-lock-keywords
220 '(("^\\([0-9a-f]+:\\).\\{40\\} \\(.+$\\)"
221 ;; "^\\([0-9a-f]+:\\).+ \\(.+$\\)"
2d77d354
MY
222 (1 'hexl-address-region t t)
223 (2 'hexl-ascii-region t t)))
639b8e4d
MY
224 "Font lock keywords used in `hexl-mode'.")
225
5ae255c7 226(defun hexl-rulerize (string bits)
0d15b5ba
VD
227 (let ((size (/ bits 4)) (strlen (length string)) (pos 0) (ruler ""))
228 (while (< pos strlen)
229 (setq ruler (concat ruler " " (substring string pos (+ pos size))))
230 (setq pos (+ pos size)))
231 (substring ruler 1) ))
232
233(defvar hexl-rulers
234 (mapcar
235 (lambda (bits)
236 (cons bits
237 (concat " 87654321 "
5ae255c7 238 (hexl-rulerize "00112233445566778899aabbccddeeff" bits)
0d15b5ba
VD
239 " 0123456789abcdef")))
240 '(8 16 32 64)))
a2535589
JA
241;; routines
242
2d902813
RS
243(put 'hexl-mode 'mode-class 'special)
244
0d15b5ba
VD
245;; 10 chars for the "address: "
246;; 32 chars for the hexlified bytes
247;; 1 char for the space
248;; 16 chars for the character display
249;; X chars for the spaces (128 bits divided by the hexl-bits)
250;; 1 char for the newline.
251(defun hexl-line-displen ()
252 "The length of a hexl display line (varies with `hexl-bits')."
253 (+ 60 (/ 128 (or hexl-bits 16))))
4391b429
SM
254
255(defun hexl-mode--minor-mode-p (var)
256 (memq var '(ruler-mode hl-line-mode)))
257
258(defun hexl-mode--setq-local (var val)
259 ;; `var' can be either a symbol or a pair, in which case the `car'
260 ;; is the getter function and the `cdr' is the corresponding setter.
261 (unless (or (member var hexl-mode--old-var-vals)
262 (assoc var hexl-mode--old-var-vals))
263 (push (if (or (consp var) (boundp var))
264 (cons var
265 (if (consp var) (funcall (car var)) (symbol-value var)))
266 var)
267 hexl-mode--old-var-vals))
268 (cond
269 ((consp var) (funcall (cdr var) val))
270 ((hexl-mode--minor-mode-p var) (funcall var (if val 1 -1)))
271 (t (set (make-local-variable var) val))))
272
31c75fa7 273;;;###autoload
a2535589 274(defun hexl-mode (&optional arg)
330bd7c3
PR
275 "\\<hexl-mode-map>A mode for editing binary files in hex dump format.
276This is not an ordinary major mode; it alters some aspects
fe0a77c6 277of the current mode's behavior, but not all; also, you can exit
330bd7c3 278Hexl mode and return to the previous mode using `hexl-mode-exit'.
a2535589
JA
279
280This function automatically converts a buffer into the hexl format
281using the function `hexlify-buffer'.
282
8a1281b5 283Each line in the buffer has an \"address\" (displayed in hexadecimal)
a2535589
JA
284representing the offset into the file that the characters on this line
285are at and 16 characters from the file (displayed as hexadecimal
0d15b5ba 286values grouped every `hexl-bits' bits) and as their ASCII values.
a2535589
JA
287
288If any of the characters (displayed as ASCII characters) are
289unprintable (control or meta characters) they will be replaced as
290periods.
291
8a1281b5
RS
292If `hexl-mode' is invoked with an argument the buffer is assumed to be
293in hexl format.
a2535589
JA
294
295A sample format:
296
297 HEX ADDR: 0001 0203 0405 0607 0809 0a0b 0c0d 0e0f ASCII-TEXT
298 -------- ---- ---- ---- ---- ---- ---- ---- ---- ----------------
299 00000000: 5468 6973 2069 7320 6865 786c 2d6d 6f64 This is hexl-mod
300 00000010: 652e 2020 4561 6368 206c 696e 6520 7265 e. Each line re
301 00000020: 7072 6573 656e 7473 2031 3620 6279 7465 presents 16 byte
302 00000030: 7320 6173 2068 6578 6164 6563 696d 616c s as hexadecimal
303 00000040: 2041 5343 4949 0a61 6e64 2070 7269 6e74 ASCII.and print
304 00000050: 6162 6c65 2041 5343 4949 2063 6861 7261 able ASCII chara
305 00000060: 6374 6572 732e 2020 416e 7920 636f 6e74 cters. Any cont
306 00000070: 726f 6c20 6f72 206e 6f6e 2d41 5343 4949 rol or non-ASCII
71296446 307 00000080: 2063 6861 7261 6374 6572 730a 6172 6520 characters.are
a2535589
JA
308 00000090: 6469 7370 6c61 7965 6420 6173 2070 6572 displayed as per
309 000000a0: 696f 6473 2069 6e20 7468 6520 7072 696e iods in the prin
71296446 310 000000b0: 7461 626c 6520 6368 6172 6163 7465 7220 table character
a2535589
JA
311 000000c0: 7265 6769 6f6e 2e0a region..
312
89c24779 313Movement is as simple as movement in a normal Emacs text buffer. Most
65e7ca35
PE
314cursor movement bindings are the same: use \\[hexl-backward-char], \\[hexl-forward-char], \\[hexl-next-line], and \\[hexl-previous-line]
315to move the cursor left, right, down, and up.
a2535589
JA
316
317Advanced cursor movement commands (ala \\[hexl-beginning-of-line], \\[hexl-end-of-line], \\[hexl-beginning-of-buffer], and \\[hexl-end-of-buffer]) are
318also supported.
319
320There are several ways to change text in hexl mode:
321
322ASCII characters (character between space (0x20) and tilde (0x7E)) are
323bound to self-insert so you can simply type the character and it will
324insert itself (actually overstrike) into the buffer.
325
326\\[hexl-quoted-insert] followed by another keystroke allows you to insert the key even if
327it isn't bound to self-insert. An octal number can be supplied in place
328of another key to insert the octal number's ASCII representation.
329
330\\[hexl-insert-hex-char] will insert a given hexadecimal value (if it is between 0 and 0xFF)
331into the buffer at the current point.
332
333\\[hexl-insert-octal-char] will insert a given octal value (if it is between 0 and 0377)
334into the buffer at the current point.
335
336\\[hexl-insert-decimal-char] will insert a given decimal value (if it is between 0 and 255)
337into the buffer at the current point.
338
a2535589
JA
339\\[hexl-mode-exit] will exit hexl-mode.
340
31c75fa7
RS
341Note: saving the file with any of the usual Emacs commands
342will actually convert it back to binary format while saving.
a2535589 343
330bd7c3 344You can use \\[hexl-find-file] to visit a file in Hexl mode.
a2535589
JA
345
346\\[describe-bindings] for advanced commands."
347 (interactive "p")
330bd7c3 348 (unless (eq major-mode 'hexl-mode)
753c1309
RS
349 (let ((modified (buffer-modified-p))
350 (inhibit-read-only t)
312d24fb 351 (original-point (- (point) (point-min))))
753c1309
RS
352 (and (eobp) (not (bobp))
353 (setq original-point (1- original-point)))
312d24fb
SM
354 ;; If `hexl-mode' is invoked with an argument the buffer is assumed to
355 ;; be in hexl format.
356 (when (memq arg '(1 nil))
7851eb98
EZ
357 ;; If the buffer's EOL type is -dos, we need to account for
358 ;; extra CR characters added when hexlify-buffer writes the
359 ;; buffer to a file.
312d24fb 360 ;; FIXME: This doesn't take into account multibyte coding systems.
7851eb98 361 (when (eq (coding-system-eol-type buffer-file-coding-system) 1)
312d24fb 362 (setq original-point (+ (count-lines (point-min) (point))
7851eb98
EZ
363 original-point))
364 (or (bolp) (setq original-point (1- original-point))))
753c1309 365 (hexlify-buffer)
37dc4767 366 (restore-buffer-modified-p modified))
312d24fb 367 (set (make-local-variable 'hexl-max-address)
0d15b5ba 368 (+ (* (/ (1- (buffer-size)) (hexl-line-displen)) 16) 15))
55391f5e
RS
369 (condition-case nil
370 (hexl-goto-address original-point)
371 (error nil)))
753c1309 372
0e4889b2
RS
373 ;; We do not turn off the old major mode; instead we just
374 ;; override most of it. That way, we can restore it perfectly.
a2535589 375
4391b429 376 (hexl-mode--setq-local '(current-local-map . use-local-map) hexl-mode-map)
87b3b78a 377
4391b429
SM
378 (hexl-mode--setq-local 'mode-name "Hexl")
379 (hexl-mode--setq-local 'isearch-search-fun-function
380 'hexl-isearch-search-function)
381 (hexl-mode--setq-local 'major-mode 'hexl-mode)
a2535589 382
4391b429
SM
383 (hexl-mode--setq-local '(syntax-table . set-syntax-table)
384 (standard-syntax-table))
0e4889b2 385
87b3b78a 386 (add-hook 'write-contents-functions 'hexl-save-buffer nil t)
31c75fa7 387
4391b429 388 (hexl-mode--setq-local 'require-final-newline nil)
0e4889b2 389
2fbc1934 390
4391b429 391 (hexl-mode--setq-local 'font-lock-defaults '(hexl-font-lock-keywords t))
639b8e4d 392
4391b429
SM
393 (hexl-mode--setq-local 'revert-buffer-function
394 #'hexl-revert-buffer-function)
db6c5b92
SE
395 (add-hook 'change-major-mode-hook 'hexl-maybe-dehexlify-buffer nil t)
396
01ff9136 397 ;; Set a callback function for eldoc.
4391b429
SM
398 (hexl-mode--setq-local 'eldoc-documentation-function
399 #'hexl-print-current-point-info)
01ff9136 400 (eldoc-add-command-completions "hexl-")
625c1523 401 (eldoc-remove-command "hexl-save-buffer"
01ff9136
MY
402 "hexl-current-address")
403
db6c5b92 404 (if hexl-follow-ascii (hexl-follow-ascii 1)))
36555efc 405 (run-mode-hooks 'hexl-mode-hook))
a2535589 406
87b3b78a
SM
407
408(defun hexl-isearch-search-function ()
409 (if (and (not isearch-regexp) (not isearch-word))
410 (lambda (string &optional bound noerror count)
411 (funcall
412 (if isearch-forward 're-search-forward 're-search-backward)
37dc4767
SM
413 (let ((textre
414 (if (> (length string) 80)
415 (regexp-quote string)
416 (mapconcat (lambda (c) (regexp-quote (string c))) string
417 "\\(?:\n\\(?:[:a-f0-9]+ \\)+ \\)?"))))
418 (if (string-match "\\` ?\\([a-f0-9]+ \\)*[a-f0-9]+ ?\\'" string)
419 (concat textre "\\|"
420 (mapconcat 'regexp-quote (split-string string " ")
421 " \\(?: .+\n[a-f0-9]+: \\)?"))
422 textre))
87b3b78a 423 bound noerror count))
8cbd80f7 424 (isearch-search-fun-default)))
87b3b78a 425
ac2e902d
JB
426(defvar hexl-in-save-buffer nil)
427
a2535589
JA
428(defun hexl-save-buffer ()
429 "Save a hexl format buffer as binary in visited file if modified."
430 (interactive)
ac2e902d 431 (if hexl-in-save-buffer nil
37dc4767
SM
432 (restore-buffer-modified-p
433 (if (buffer-modified-p)
434 (let ((buf (generate-new-buffer " hexl"))
435 (name (buffer-name))
436 (start (point-min))
437 (end (point-max))
438 modified)
439 (with-current-buffer buf
440 (insert-buffer-substring name start end)
441 (set-buffer name)
442 (dehexlify-buffer)
443 ;; Prevent infinite recursion.
444 (let ((hexl-in-save-buffer t))
445 (save-buffer))
446 (setq modified (buffer-modified-p))
447 (delete-region (point-min) (point-max))
448 (insert-buffer-substring buf start end)
449 (kill-buffer buf)
450 modified))
451 (message "(No changes need to be saved)")
452 nil))
ac2e902d
JB
453 ;; Return t to indicate we have saved t
454 t))
a2535589 455
31c75fa7 456;;;###autoload
a2535589 457(defun hexl-find-file (filename)
7f9da0f3
EZ
458 "Edit file FILENAME as a binary file in hex dump format.
459Switch to a buffer visiting file FILENAME, creating one if none exists,
460and edit the file in `hexl-mode'."
e49a45ad
MB
461 (interactive
462 (list
463 (let ((completion-ignored-extensions nil))
464 (read-file-name "Filename: " nil nil 'ret-must-match))))
14acf2f5 465 ;; Ignore the user's setting of default major-mode.
a464a6c7 466 (cl-letf (((default-value 'major-mode) 'fundamental-mode))
3db6aff6 467 (find-file-literally filename))
a2535589
JA
468 (if (not (eq major-mode 'hexl-mode))
469 (hexl-mode)))
470
2fbc1934 471(defun hexl-revert-buffer-function (_ignore-auto _noconfirm)
08ffb131
DU
472 (let ((coding-system-for-read 'no-conversion)
473 revert-buffer-function)
474 ;; Call the original `revert-buffer' without code conversion; also
475 ;; prevent it from changing the major mode to normal-mode, which
476 ;; calls `set-auto-mode'.
477 (revert-buffer nil nil t)
478 ;; A couple of hacks are necessary here:
479 ;; 1. change the major-mode to one other than hexl-mode since the
480 ;; function `hexl-mode' does nothing if the current major-mode is
481 ;; already hexl-mode.
482 ;; 2. reset change-major-mode-hook in case that `hexl-mode'
483 ;; previously added hexl-maybe-dehexlify-buffer to it.
484 (remove-hook 'change-major-mode-hook 'hexl-maybe-dehexlify-buffer t)
485 (setq major-mode 'fundamental-mode)
486 (hexl-mode)))
487
a2535589 488(defun hexl-mode-exit (&optional arg)
31c75fa7 489 "Exit Hexl mode, returning to previous mode.
a2535589
JA
490With arg, don't unhexlify buffer."
491 (interactive "p")
492 (if (or (eq arg 1) (not arg))
493 (let ((modified (buffer-modified-p))
900014dd 494 (inhibit-read-only t)
a2535589 495 (original-point (1+ (hexl-current-address))))
a2535589 496 (dehexlify-buffer)
87b3b78a 497 (remove-hook 'write-contents-functions 'hexl-save-buffer t)
37dc4767 498 (restore-buffer-modified-p modified)
7851eb98
EZ
499 (goto-char original-point)
500 ;; Maybe adjust point for the removed CR characters.
501 (when (eq (coding-system-eol-type buffer-file-coding-system) 1)
502 (setq original-point (- original-point
503 (count-lines (point-min) (point))))
504 (or (bobp) (setq original-point (1+ original-point))))
a2535589 505 (goto-char original-point)))
0e4889b2 506
0e4889b2 507 (remove-hook 'change-major-mode-hook 'hexl-maybe-dehexlify-buffer t)
8e7df2e6
SE
508 (remove-hook 'post-command-hook 'hexl-follow-ascii-find t)
509 (setq hexl-ascii-overlay nil)
0e4889b2 510
4391b429
SM
511 (let ((mms ()))
512 (dolist (varval hexl-mode--old-var-vals)
513 (let* ((bound (consp varval))
514 (var (if bound (car varval) varval))
515 (val (cdr-safe varval)))
516 (cond
517 ((consp var) (funcall (cdr var) val))
518 ((hexl-mode--minor-mode-p var) (push (cons var val) mms))
519 (bound (set (make-local-variable var) val))
520 (t (kill-local-variable var)))))
521 (kill-local-variable 'hexl-mode--old-var-vals)
522 ;; Enable/disable minor modes. Do it after having reset the other vars,
523 ;; since some of them may affect the minor modes.
524 (dolist (mm mms)
525 (funcall (car mm) (if (cdr mm) 1 -1))))
2fbc1934 526
8eeac2ce
RS
527 (force-mode-line-update))
528
529(defun hexl-maybe-dehexlify-buffer ()
530 "Convert a hexl format buffer to binary.
531Ask the user for confirmation."
532 (if (y-or-n-p "Convert contents back to binary format? ")
533 (let ((modified (buffer-modified-p))
534 (inhibit-read-only t)
535 (original-point (1+ (hexl-current-address))))
536 (dehexlify-buffer)
87b3b78a 537 (remove-hook 'write-contents-functions 'hexl-save-buffer t)
37dc4767 538 (restore-buffer-modified-p modified)
8eeac2ce 539 (goto-char original-point))))
a2535589 540
6bbb008e 541(defun hexl-current-address (&optional validate)
a2535589
JA
542 "Return current hexl-address."
543 (interactive)
0d15b5ba
VD
544 (let ((current-column
545 (- (% (- (point) (point-min) -1) (hexl-line-displen)) 11))
a2535589 546 (hexl-address 0))
6bbb008e
RS
547 (if (< current-column 0)
548 (if validate
549 (error "Point is not on a character in the file")
550 (setq current-column 0)))
551 (setq hexl-address
0d15b5ba
VD
552 (+ (* (/ (- (point) (point-min) -1)
553 (hexl-line-displen)) 16)
554 (if (>= current-column (- (hexl-ascii-start-column) 10))
555 (- current-column (- (hexl-ascii-start-column) 10))
556 (/ (- current-column
557 (/ current-column (1+ (/ hexl-bits 4)))) 2))))
32226619 558 (when (called-interactively-p 'interactive)
01ff9136 559 (message "Current address is %d/0x%08x" hexl-address hexl-address))
a2535589
JA
560 hexl-address))
561
01ff9136
MY
562(defun hexl-print-current-point-info ()
563 "Return current hexl-address in string.
12d96c20 564This function is intended to be used as eldoc callback."
01ff9136
MY
565 (let ((addr (hexl-current-address)))
566 (format "Current address is %d/0x%08x" addr addr)))
567
0d15b5ba
VD
568(defun hexl-ascii-start-column ()
569 "Column at which the ascii portion of the hexl display starts."
570 (+ 43 (/ 128 hexl-bits)))
571
a2535589 572(defun hexl-address-to-marker (address)
059c2e18 573 "Return buffer position for ADDRESS."
a2535589 574 (interactive "nAddress: ")
0d15b5ba
VD
575 (let ((N (* (% address 16) 2)))
576 (+ (* (/ address 16) (hexl-line-displen)) ; hexl line no * display length
577 10 ; 10 chars for the "address: " prefix
578 (point-min) ; base offset (point usually starts at 1, not 0)
579 (+ N (/ N (/ hexl-bits 4))) )) ) ; char offset into hexl display line
a2535589
JA
580
581(defun hexl-goto-address (address)
61dd70aa 582 "Go to hexl-mode (decimal) address ADDRESS.
5232b4cd 583Signal error if ADDRESS is out of range."
a2535589
JA
584 (interactive "nAddress: ")
585 (if (or (< address 0) (> address hexl-max-address))
55391f5e 586 (error "Out of hexl region"))
a2535589
JA
587 (goto-char (hexl-address-to-marker address)))
588
589(defun hexl-goto-hex-address (hex-address)
4c8c7ae9 590 "Go to hexl-mode address (hex string) HEX-ADDRESS.
a2535589
JA
591Signal error if HEX-ADDRESS is out of range."
592 (interactive "sHex Address: ")
593 (hexl-goto-address (hexl-hex-string-to-integer hex-address)))
594
595(defun hexl-hex-string-to-integer (hex-string)
596 "Return decimal integer for HEX-STRING."
597 (interactive "sHex number: ")
598 (let ((hex-num 0))
599 (while (not (equal hex-string ""))
600 (setq hex-num (+ (* hex-num 16)
601 (hexl-hex-char-to-integer (string-to-char hex-string))))
602 (setq hex-string (substring hex-string 1)))
603 hex-num))
604
605(defun hexl-octal-string-to-integer (octal-string)
606 "Return decimal integer for OCTAL-STRING."
607 (interactive "sOctal number: ")
608 (let ((oct-num 0))
609 (while (not (equal octal-string ""))
610 (setq oct-num (+ (* oct-num 8)
611 (hexl-oct-char-to-integer
612 (string-to-char octal-string))))
613 (setq octal-string (substring octal-string 1)))
614 oct-num))
615
616;; move point functions
617
618(defun hexl-backward-char (arg)
619 "Move to left ARG bytes (right if ARG negative) in hexl-mode."
620 (interactive "p")
621 (hexl-goto-address (- (hexl-current-address) arg)))
622
623(defun hexl-forward-char (arg)
5232b4cd 624 "Move to right ARG bytes (left if ARG negative) in hexl-mode."
a2535589
JA
625 (interactive "p")
626 (hexl-goto-address (+ (hexl-current-address) arg)))
627
628(defun hexl-backward-short (arg)
629 "Move to left ARG shorts (right if ARG negative) in hexl-mode."
630 (interactive "p")
631 (hexl-goto-address (let ((address (hexl-current-address)))
632 (if (< arg 0)
633 (progn
634 (setq arg (- arg))
635 (while (> arg 0)
4391b429
SM
636 (setq address
637 (if (> address hexl-max-address)
638 (progn
639 (message "End of buffer.")
640 hexl-max-address)
641 (if (equal address (logior address 3))
642 (+ address 4)
643 (logior address 3))))
a2535589 644 (setq arg (1- arg)))
4391b429
SM
645 (setq address
646 (if (> address hexl-max-address)
647 (progn
648 (message "End of buffer.")
649 hexl-max-address)
650 (logior address 3))))
a2535589
JA
651 (while (> arg 0)
652 (if (not (equal address (logand address -4)))
653 (setq address (logand address -4))
654 (if (not (equal address 0))
655 (setq address (- address 4))
656 (message "Beginning of buffer.")))
657 (setq arg (1- arg))))
658 address)))
659
660(defun hexl-forward-short (arg)
5232b4cd 661 "Move to right ARG shorts (left if ARG negative) in hexl-mode."
a2535589
JA
662 (interactive "p")
663 (hexl-backward-short (- arg)))
664
665(defun hexl-backward-word (arg)
666 "Move to left ARG words (right if ARG negative) in hexl-mode."
667 (interactive "p")
668 (hexl-goto-address (let ((address (hexl-current-address)))
669 (if (< arg 0)
670 (progn
671 (setq arg (- arg))
672 (while (> arg 0)
4391b429
SM
673 (setq address
674 (if (> address hexl-max-address)
675 (progn
676 (message "End of buffer.")
677 hexl-max-address)
678 (if (equal address (logior address 7))
679 (+ address 8)
680 (logior address 7))))
a2535589 681 (setq arg (1- arg)))
4391b429
SM
682 (setq address
683 (if (> address hexl-max-address)
684 (progn
685 (message "End of buffer.")
686 hexl-max-address)
687 (logior address 7))))
a2535589
JA
688 (while (> arg 0)
689 (if (not (equal address (logand address -8)))
690 (setq address (logand address -8))
691 (if (not (equal address 0))
692 (setq address (- address 8))
693 (message "Beginning of buffer.")))
694 (setq arg (1- arg))))
695 address)))
696
697(defun hexl-forward-word (arg)
5232b4cd 698 "Move to right ARG words (left if ARG negative) in hexl-mode."
a2535589
JA
699 (interactive "p")
700 (hexl-backward-word (- arg)))
701
702(defun hexl-previous-line (arg)
4c8c7ae9 703 "Move vertically up ARG lines [16 bytes] (down if ARG negative) in hexl-mode.
5232b4cd 704If there is no byte at the target address move to the last byte in that line."
a2535589
JA
705 (interactive "p")
706 (hexl-next-line (- arg)))
707
708(defun hexl-next-line (arg)
4c8c7ae9
JB
709 "Move vertically down ARG lines [16 bytes] (up if ARG negative) in hexl-mode.
710If there is no byte at the target address move to the last byte in that line."
a2535589 711 (interactive "p")
e8a57935 712 (hexl-goto-address (let ((address (+ (hexl-current-address) (* arg 16))))
a2535589
JA
713 (if (and (< arg 0) (< address 0))
714 (progn (message "Out of hexl region.")
715 (setq address
716 (% (hexl-current-address) 16)))
717 (if (and (> address hexl-max-address)
718 (< (% hexl-max-address 16) (% address 16)))
719 (setq address hexl-max-address)
720 (if (> address hexl-max-address)
721 (progn (message "Out of hexl region.")
722 (setq
723 address
724 (+ (logand hexl-max-address -16)
725 (% (hexl-current-address) 16)))))))
726 address)))
727
728(defun hexl-beginning-of-buffer (arg)
4c8c7ae9
JB
729 "Move to the beginning of the hexl buffer.
730Leaves `hexl-mark' at previous position.
731With prefix arg N, puts point N bytes of the way from the true beginning."
a2535589
JA
732 (interactive "p")
733 (push-mark (point))
734 (hexl-goto-address (+ 0 (1- arg))))
735
736(defun hexl-end-of-buffer (arg)
4c8c7ae9 737 "Go to `hexl-max-address' minus ARG."
a2535589
JA
738 (interactive "p")
739 (push-mark (point))
740 (hexl-goto-address (- hexl-max-address (1- arg))))
741
742(defun hexl-beginning-of-line ()
743 "Goto beginning of line in hexl mode."
744 (interactive)
0d15b5ba 745 (goto-char (+ (* (/ (point) (hexl-line-displen)) (hexl-line-displen)) 11)))
a2535589
JA
746
747(defun hexl-end-of-line ()
748 "Goto end of line in hexl mode."
749 (interactive)
750 (hexl-goto-address (let ((address (logior (hexl-current-address) 15)))
751 (if (> address hexl-max-address)
752 (setq address hexl-max-address))
753 address)))
754
755(defun hexl-scroll-down (arg)
756 "Scroll hexl buffer window upward ARG lines; or near full window if no ARG."
757 (interactive "P")
4391b429
SM
758 (setq arg (if (null arg)
759 (1- (window-height))
760 (prefix-numeric-value arg)))
a2535589
JA
761 (hexl-scroll-up (- arg)))
762
763(defun hexl-scroll-up (arg)
d565f6aa
EZ
764 "Scroll hexl buffer window upward ARG lines; or near full window if no ARG.
765If there's no byte at the target address, move to the first or last line."
a2535589 766 (interactive "P")
4391b429
SM
767 (setq arg (if (null arg)
768 (1- (window-height))
769 (prefix-numeric-value arg)))
d565f6aa
EZ
770 (let* ((movement (* arg 16))
771 (address (hexl-current-address))
772 (dest (+ address movement)))
773 (cond
774 ;; If possible, try to stay at the same offset from the beginning
775 ;; of the 16-byte group, even if we move to the first or last
776 ;; group.
777 ((and (> dest hexl-max-address)
778 (>= (% hexl-max-address 16) (% address 16)))
779 (setq dest (+ (logand hexl-max-address -16) (% address 16))))
780 ((> dest hexl-max-address)
781 (setq dest hexl-max-address))
782 ((< dest 0)
783 (setq dest (% address 16))))
784 (if (/= dest (+ address movement))
785 (message "Out of hexl region."))
786 (hexl-goto-address dest)
787 (recenter 0)))
a2535589
JA
788
789(defun hexl-beginning-of-1k-page ()
5232b4cd 790 "Go to beginning of 1KB boundary."
a2535589
JA
791 (interactive)
792 (hexl-goto-address (logand (hexl-current-address) -1024)))
793
794(defun hexl-end-of-1k-page ()
5232b4cd 795 "Go to end of 1KB boundary."
a2535589 796 (interactive)
4391b429
SM
797 (hexl-goto-address
798 (max hexl-max-address (logior (hexl-current-address) 1023))))
a2535589
JA
799
800(defun hexl-beginning-of-512b-page ()
65e5f4bc 801 "Go to beginning of 512 byte boundary."
a2535589
JA
802 (interactive)
803 (hexl-goto-address (logand (hexl-current-address) -512)))
804
805(defun hexl-end-of-512b-page ()
65e5f4bc 806 "Go to end of 512 byte boundary."
a2535589 807 (interactive)
4391b429
SM
808 (hexl-goto-address
809 (max hexl-max-address (logior (hexl-current-address) 511))))
a2535589
JA
810
811(defun hexl-quoted-insert (arg)
812 "Read next input character and insert it.
02aec07b
EZ
813Useful for inserting control characters and non-ASCII characters given their
814numerical code.
815You may also type octal digits, to insert a character with that code."
a2535589 816 (interactive "p")
02aec07b 817 (hexl-insert-multibyte-char (read-quoted-char) arg))
a2535589
JA
818
819;00000000: 0011 2233 4455 6677 8899 aabb ccdd eeff 0123456789ABCDEF
820
0d15b5ba
VD
821(defun hexl-options (&optional test)
822 "Combine `hexl-bits' with `hexl-options', altering `hexl-options' as needed
823to produce the command line options to pass to the hexl command."
824 (let ((opts (or test hexl-options)))
825 (when (memq hexl-bits '(8 16 32 64))
826 (when (string-match "\\(.*\\)-group-by-[0-9]+-bits\\(.*\\)" opts)
827 (setq opts (concat (match-string 1 opts)
828 (match-string 2 opts))))
829 (setq opts (format "%s -group-by-%d-bits " opts hexl-bits)) )
830 opts))
831
f4e3d4eb 832;;;###autoload
a2535589 833(defun hexlify-buffer ()
aa3757b8
RS
834 "Convert a binary buffer to hexl format.
835This discards the buffer's undo information."
a2535589 836 (interactive)
62222158 837 (and (consp buffer-undo-list)
aa3757b8 838 (or (y-or-n-p "Converting to hexl format discards undo info; ok? ")
62222158
SM
839 (error "Aborted"))
840 (setq buffer-undo-list nil))
a749e5e5
EZ
841 ;; Don't decode text in the ASCII part of `hexl' program output.
842 (let ((coding-system-for-read 'raw-text)
0716afa2 843 (coding-system-for-write buffer-file-coding-system)
aa3757b8 844 (buffer-undo-list t))
b8c49a19
SM
845 (apply 'call-process-region (point-min) (point-max)
846 (expand-file-name hexl-program exec-directory)
4a729d58
SM
847 t t nil
848 ;; Manually encode the args, otherwise they're encoded using
849 ;; coding-system-for-write (i.e. buffer-file-coding-system) which
850 ;; may not be what we want (e.g. utf-16 on a non-utf-16 system).
312d24fb
SM
851 (mapcar (lambda (s)
852 (if (not (multibyte-string-p s)) s
853 (encode-coding-string s locale-coding-system)))
0d15b5ba 854 (split-string (hexl-options))))
059c2e18
PR
855 (if (> (point) (hexl-address-to-marker hexl-max-address))
856 (hexl-goto-address hexl-max-address))))
a2535589
JA
857
858(defun dehexlify-buffer ()
aa3757b8
RS
859 "Convert a hexl format buffer to binary.
860This discards the buffer's undo information."
a2535589 861 (interactive)
62222158 862 (and (consp buffer-undo-list)
aa3757b8 863 (or (y-or-n-p "Converting from hexl format discards undo info; ok? ")
62222158
SM
864 (error "Aborted"))
865 (setq buffer-undo-list nil))
a749e5e5 866 (let ((coding-system-for-write 'raw-text)
0716afa2 867 (coding-system-for-read buffer-file-coding-system)
aa3757b8 868 (buffer-undo-list t))
b8c49a19
SM
869 (apply 'call-process-region (point-min) (point-max)
870 (expand-file-name hexl-program exec-directory)
0d15b5ba 871 t t nil "-de" (split-string (hexl-options)))))
a2535589
JA
872
873(defun hexl-char-after-point ()
874 "Return char for ASCII hex digits at point."
686fc9ab
RS
875 (hexl-htoi (char-after (point))
876 (char-after (1+ (point)))))
a2535589
JA
877
878(defun hexl-htoi (lh rh)
879 "Hex (char) LH (char) RH to integer."
880 (+ (* (hexl-hex-char-to-integer lh) 16)
881 (hexl-hex-char-to-integer rh)))
882
883(defun hexl-hex-char-to-integer (character)
884 "Take a char and return its value as if it was a hex digit."
885 (if (and (>= character ?0) (<= character ?9))
886 (- character ?0)
887 (let ((ch (logior character 32)))
888 (if (and (>= ch ?a) (<= ch ?f))
889 (- ch (- ?a 10))
19e31f7c 890 (error "Invalid hex digit `%c'" ch)))))
a2535589
JA
891
892(defun hexl-oct-char-to-integer (character)
893 "Take a char and return its value as if it was a octal digit."
894 (if (and (>= character ?0) (<= character ?7))
895 (- character ?0)
19e31f7c 896 (error "Invalid octal digit `%c'" character)))
a2535589
JA
897
898(defun hexl-printable-character (ch)
899 "Return a displayable string for character CH."
9c23ca47
JB
900 (format "%c" (if (equal hexl-iso "")
901 (if (or (< ch 32) (>= ch 127))
a2535589
JA
902 46
903 ch)
9c23ca47 904 (if (or (< ch 32) (and (>= ch 127) (< ch 160)))
a2535589
JA
905 46
906 ch))))
907
02aec07b
EZ
908(defun hexl-insert-multibyte-char (ch num)
909 "Insert a possibly multibyte character CH NUM times.
910
911Non-ASCII characters are first encoded with `buffer-file-coding-system',
912and their encoded form is inserted byte by byte."
913 (let ((charset (char-charset ch))
914 (coding (if (or (null buffer-file-coding-system)
915 ;; coding-system-type equals t means undecided.
916 (eq (coding-system-type buffer-file-coding-system) t))
b56a5ae0 917 (default-value 'buffer-file-coding-system)
02aec07b
EZ
918 buffer-file-coding-system)))
919 (cond ((and (> ch 0) (< ch 256))
920 (hexl-insert-char ch num))
921 ((eq charset 'unknown)
922 (error
165b4283 923 "0x%x -- invalid character code; use \\[hexl-insert-hex-string]"
02aec07b
EZ
924 ch))
925 (t
926 (let ((encoded (encode-coding-char ch coding))
927 (internal (string-as-unibyte (char-to-string ch)))
928 internal-hex)
929 ;; If encode-coding-char returns nil, it means our character
930 ;; cannot be safely encoded with buffer-file-coding-system.
931 ;; In that case, we offer to insert the internal representation
932 ;; of that character, byte by byte.
933 (when (null encoded)
934 (setq internal-hex
935 (mapconcat (function (lambda (c) (format "%x" c)))
936 internal " "))
937 (if (yes-or-no-p
938 (format
939 "Insert char 0x%x's internal representation \"%s\"? "
940 ch internal-hex))
941 (setq encoded internal)
942 (error
165b4283 943 "Can't encode `0x%x' with this buffer's coding system; try \\[hexl-insert-hex-string]"
02aec07b
EZ
944 ch)))
945 (while (> num 0)
946 (mapc
947 (function (lambda (c) (hexl-insert-char c 1))) encoded)
948 (setq num (1- num))))))))
949
a2535589 950(defun hexl-self-insert-command (arg)
02aec07b
EZ
951 "Insert this character.
952Interactively, with a numeric argument, insert this character that many times.
953
954Non-ASCII characters are first encoded with `buffer-file-coding-system',
955and their encoded form is inserted byte by byte."
a2535589 956 (interactive "p")
8989a920 957 (hexl-insert-multibyte-char last-command-event arg))
a2535589
JA
958
959(defun hexl-insert-char (ch num)
02aec07b
EZ
960 "Insert the character CH NUM times in a hexl buffer.
961
962CH must be a unibyte character whose value is between 0 and 255."
963 (if (or (< ch 0) (> ch 255))
45ad49ba 964 (error "Invalid character 0x%x -- must be in the range [0..255]" ch))
6bbb008e 965 (let ((address (hexl-current-address t)))
a2535589 966 (while (> num 0)
b472a594 967 (let ((hex-position (hexl-address-to-marker address))
6bbb008e 968 (ascii-position
0d15b5ba
VD
969 (+ (* (/ address 16) (hexl-line-displen))
970 (hexl-ascii-start-column)
971 (point-min)
972 (% address 16)))
6bbb008e
RS
973 at-ascii-position)
974 (if (= (point) ascii-position)
975 (setq at-ascii-position t))
976 (goto-char hex-position)
977 (delete-char 2)
978 (insert (format "%02x" ch))
979 (goto-char ascii-position)
980 (delete-char 1)
981 (insert (hexl-printable-character ch))
982 (or (eq address hexl-max-address)
983 (setq address (1+ address)))
984 (hexl-goto-address address)
985 (if at-ascii-position
986 (progn
987 (beginning-of-line)
0d15b5ba 988 (forward-char (hexl-ascii-start-column))
6bbb008e 989 (forward-char (% address 16)))))
a2535589
JA
990 (setq num (1- num)))))
991
992;; hex conversion
993
994(defun hexl-insert-hex-char (arg)
02aec07b 995 "Insert a character given by its hexadecimal code ARG times at point."
a2535589
JA
996 (interactive "p")
997 (let ((num (hexl-hex-string-to-integer (read-string "Hex number: "))))
02aec07b 998 (if (< num 0)
19e31f7c 999 (error "Hex number out of range")
02aec07b 1000 (hexl-insert-multibyte-char num arg))))
a2535589 1001
9f6bff44
GM
1002(defun hexl-insert-hex-string (str arg)
1003 "Insert hexadecimal string STR at point ARG times.
1004Embedded whitespace, dashes, and periods in the string are ignored."
1005 (interactive "sHex string: \np")
1006 (setq str (replace-regexp-in-string "[- \t.]" "" str))
1007 (let ((chars '()))
1008 (let ((len (length str))
1009 (idx 0))
1010 (if (eq (logand len 1) 1)
1011 (let ((num (hexl-hex-string-to-integer (substring str 0 1))))
1012 (setq chars (cons num chars))
1013 (setq idx 1)))
1014 (while (< idx len)
1015 (let* ((nidx (+ idx 2))
1016 (num (hexl-hex-string-to-integer (substring str idx nidx))))
1017 (setq chars (cons num chars))
1018 (setq idx nidx))))
1019 (setq chars (nreverse chars))
1020 (while (> arg 0)
1021 (let ((chars chars))
1022 (while chars
1023 (hexl-insert-char (car chars) 1)
1024 (setq chars (cdr chars))))
1025 (setq arg (- arg 1)))))
1026
a2535589 1027(defun hexl-insert-decimal-char (arg)
02aec07b 1028 "Insert a character given by its decimal code ARG times at point."
a2535589 1029 (interactive "p")
027a4b6b 1030 (let ((num (string-to-number (read-string "Decimal Number: "))))
02aec07b 1031 (if (< num 0)
19e31f7c 1032 (error "Decimal number out of range")
02aec07b 1033 (hexl-insert-multibyte-char num arg))))
a2535589
JA
1034
1035(defun hexl-insert-octal-char (arg)
02aec07b 1036 "Insert a character given by its octal code ARG times at point."
a2535589
JA
1037 (interactive "p")
1038 (let ((num (hexl-octal-string-to-integer (read-string "Octal Number: "))))
02aec07b 1039 (if (< num 0)
19e31f7c 1040 (error "Decimal number out of range")
02aec07b 1041 (hexl-insert-multibyte-char num arg))))
a2535589 1042
db6c5b92
SE
1043(defun hexl-follow-ascii (&optional arg)
1044 "Toggle following ASCII in Hexl buffers.
1045With prefix ARG, turn on following if and only if ARG is positive.
1046When following is enabled, the ASCII character corresponding to the
1047element under the point is highlighted.
1048Customize the variable `hexl-follow-ascii' to disable this feature."
1049 (interactive "P")
71296446 1050 (let ((on-p (if arg
db6c5b92
SE
1051 (> (prefix-numeric-value arg) 0)
1052 (not hexl-ascii-overlay))))
1053
db6c5b92
SE
1054 (if on-p
1055 ;; turn it on
1056 (if (not hexl-ascii-overlay)
1057 (progn
1058 (setq hexl-ascii-overlay (make-overlay 1 1)
1059 hexl-follow-ascii t)
1060 (overlay-put hexl-ascii-overlay 'face 'highlight)
1061 (add-hook 'post-command-hook 'hexl-follow-ascii-find nil t)))
1062 ;; turn it off
1063 (if hexl-ascii-overlay
1064 (progn
1065 (delete-overlay hexl-ascii-overlay)
1066 (setq hexl-ascii-overlay nil
1067 hexl-follow-ascii nil)
1068 (remove-hook 'post-command-hook 'hexl-follow-ascii-find t)
1069 )))))
1070
9fd76d04 1071(defun hexl-activate-ruler ()
37dc4767 1072 "Activate `ruler-mode'."
9fd76d04 1073 (require 'ruler-mode)
4391b429
SM
1074 (hexl-mode--setq-local 'ruler-mode-ruler-function
1075 #'hexl-mode-ruler)
1076 (hexl-mode--setq-local 'ruler-mode t))
9fd76d04
MY
1077
1078(defun hexl-follow-line ()
37dc4767 1079 "Activate `hl-line-mode'."
9fd76d04 1080 (require 'hl-line)
4391b429
SM
1081 (hexl-mode--setq-local 'hl-line-range-function
1082 #'hexl-highlight-line-range)
1083 (hexl-mode--setq-local 'hl-line-face 'highlight)
1084 (hexl-mode--setq-local 'hl-line-mode t))
9fd76d04
MY
1085
1086(defun hexl-highlight-line-range ()
2d77d354 1087 "Return the range of address region for the point.
5232b4cd 1088This function is assumed to be used as callback function for `hl-line-mode'."
9fd76d04
MY
1089 (cons
1090 (line-beginning-position)
1091 ;; 9 stands for (length "87654321:")
1092 (+ (line-beginning-position) 9)))
1093
db6c5b92
SE
1094(defun hexl-follow-ascii-find ()
1095 "Find and highlight the ASCII element corresponding to current point."
0d15b5ba 1096 (let ((pos (+ (hexl-ascii-start-column)
db6c5b92
SE
1097 (- (point) (current-column))
1098 (mod (hexl-current-address) 16))))
1099 (move-overlay hexl-ascii-overlay pos (1+ pos))
1100 ))
1101
9fd76d04
MY
1102(defun hexl-mode-ruler ()
1103 "Return a string ruler for hexl mode."
1104 (let* ((highlight (mod (hexl-current-address) 16))
0d15b5ba 1105 (s (cdr (assq hexl-bits hexl-rulers)))
4c4ac516 1106 (pos 0))
9fd76d04
MY
1107 (set-text-properties 0 (length s) nil s)
1108 ;; Turn spaces in the header into stretch specs so they work
1109 ;; regardless of the header-line face.
1110 (while (string-match "[ \t]+" s pos)
1111 (setq pos (match-end 0))
1112 (put-text-property (match-beginning 0) pos 'display
1113 ;; Assume fixed-size chars
4c4ac516 1114 `(space :align-to ,(1- pos))
9fd76d04
MY
1115 s))
1116 ;; Highlight the current column.
0d15b5ba
VD
1117 (let ( (offset (+ (* 2 highlight) (/ (* 8 highlight) hexl-bits))) )
1118 (put-text-property (+ 11 offset) (+ 13 offset) 'face 'highlight s))
9fd76d04 1119 ;; Highlight the current ascii column
0d15b5ba
VD
1120 (put-text-property (+ (hexl-ascii-start-column) highlight 1)
1121 (+ (hexl-ascii-start-column) highlight 2)
1122 'face 'highlight s)
4c4ac516 1123 s))
9fd76d04 1124
a2535589
JA
1125;; startup stuff.
1126
61dd70aa
DN
1127(easy-menu-define hexl-menu hexl-mode-map "Hexl Mode menu"
1128 `("Hexl"
1129 :help "Hexl-specific Features"
1130
1131 ["Backward short" hexl-backward-short
1132 :help "Move to left a short"]
1133 ["Forward short" hexl-forward-short
1134 :help "Move to right a short"]
1135 ["Backward word" hexl-backward-short
1136 :help "Move to left a word"]
1137 ["Forward word" hexl-forward-short
1138 :help "Move to right a word"]
1139 "-"
1140 ["Beginning of 512b page" hexl-beginning-of-512b-page
1141 :help "Go to beginning of 512 byte boundary"]
1142 ["End of 512b page" hexl-end-of-512b-page
1143 :help "Go to end of 512 byte boundary"]
1144 ["Beginning of 1K page" hexl-beginning-of-1k-page
1145 :help "Go to beginning of 1KB boundary"]
1146 ["End of 1K page" hexl-end-of-1k-page
1147 :help "Go to end of 1KB boundary"]
1148 "-"
1149 ["Go to address" hexl-goto-address
1150 :help "Go to hexl-mode (decimal) address"]
1151 ["Go to address" hexl-goto-hex-address
1152 :help "Go to hexl-mode (hex string) address"]
1153 "-"
1154 ["Insert decimal char" hexl-insert-decimal-char
1155 :help "Insert a character given by its decimal code"]
1156 ["Insert hex char" hexl-insert-hex-char
1157 :help "Insert a character given by its hexadecimal code"]
1158 ["Insert octal char" hexl-insert-octal-char
1159 :help "Insert a character given by its octal code"]
1160 "-"
1161 ["Exit hexl mode" hexl-mode-exit
1162 :help "Exit hexl mode returning to previous mode"]))
1163
19e31f7c
RS
1164(provide 'hexl)
1165
1a06eabd 1166;;; hexl.el ends here