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