The following lisp code puts an entry "Hexl Isearch Mode" into the "Hexl" menu.
That menu item (de-)activates the minor mode hexl-isearch-mode
.
If you activate that mode isearch
searches in the binary data instead of the hexl buffer.
The search string is read with read
.
So all escape sequences for lisp strings do work.
As an example you can search for \x0a\x0d
or \^M\n
to search for dos line ends.
The code is not perfect.
Let's say you search for a string ELF\x01
which only occurs at the beginning of a file. Furthermore, assume there is a string ELf\x00
later on in the binary. Then when you arrive at ELF\x0
with typing Emacs will find the later match and if you go on in typing ELF\x01
Emacs thinks there are no occurrences of that string because it already arrived at ELF\x0
which comes later in the file than ELF\x01
. It is worth to do an overlapped search in such a case. (That problem is already fixed in the git-version of the package.)
Only the byte sequence is correctly high-lighted in the hexl buffer not the string representation at the right-hand side.
If the search string spans two lines in the hexl buffer the string representation at the end of the line and the address at the beginning of the line are also highlighted. That is not because they belong to the match but because they are in the way when highlighting the byte sequence.
(require 'hexl)
(defvar-local hexl-isearch-raw-buffer nil
"Buffer with the dehexlified content of the hexl buffer for hexl-isearch-mode.
This variable is set in the original hexl-mode buffer.")
(defvar-local hexl-isearch-original-buffer nil
"This variable is set in the buffer with the dehexlified content.
It points to the corresponding hexl buffer.")
(defun hexl-address (position)
"Return address of hexl buffer POSITION."
(save-excursion
(goto-char position)
(hexl-current-address)))
(defun hexl-isearch-startup ()
"Prepare hexl buffer for `hexl-isearch'."
(let ((original-buf (current-buffer)))
(setq-local hexl-isearch-raw-buffer (generate-new-buffer " hexl"))
(setq-local isearch-search-fun-function (lambda () #'hexl-isearch-fun))
(with-current-buffer hexl-isearch-raw-buffer
(set-buffer-multibyte nil)
(setq-local hexl-isearch-original-buffer original-buf)
(insert-buffer-substring original-buf 1 (buffer-size original-buf))
(dehexlify-buffer))))
(defun hexl-isearch-end ()
"Cleanup after `hexl-isearch'."
(let ((isearch-raw-buffer hexl-isearch-raw-buffer))
(setq-local hexl-isearch-raw-buffer nil)
(when (buffer-live-p isearch-raw-buffer)
(kill-buffer isearch-raw-buffer))))
(defun hexl-isearch-fun (string &optional bound noerror count)
"Search for byte sequence of STRING in hexl buffer.
The arguments BOUND and NOERROR work like in `search-forward'."
(when bound (setq bound (1+ (hexl-address bound))))
(setq string (read (concat "\"" string "\"")))
(let ((point (1+ (hexl-current-address)))
match-data)
(with-current-buffer hexl-isearch-raw-buffer
(goto-char point)
(setq point (funcall (if isearch-forward #'re-search-forward #'re-search-backward)
(if isearch-regexp
string
(regexp-quote string))
bound noerror count))
(setq match-data (match-data t nil t)))
(when point
(prog1
(hexl-goto-address (1- point))
(set-match-data
(mapcar (lambda (el)
(if (integerp el)
(hexl-address-to-marker (1- el))
el))
match-data))))))
(define-minor-mode hexl-isearch-mode
"Search for binary string with isearch in hexl buffer."
:lighter " hi"
(if hexl-isearch-mode
(progn
(setq-local isearch-search-fun-function #'hexl-isearch-fun)
(add-hook 'isearch-mode-hook #'hexl-isearch-startup t t)
(add-hook 'isearch-mode-end-hook #'hexl-isearch-end t t))
(setq-local isearch-search-fun-function #'isearch-search-fun-default)
(remove-hook 'isearch-mode-hook #'hexl-isearch-startup t)
(remove-hook 'isearch-mode-end-hook #'hexl-isearch-end t)))
(easy-menu-add-item hexl-mode-map '(menu-bar Hexl)
["Hexl Isearch Mode" (if hexl-isearch-mode (hexl-isearch-mode -1) (hexl-isearch-mode)) :style toggle :selected hexl-isearch-mode] "Go to address")