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