次のlispコードは、「Hexl Isearch Mode」というエントリを「Hexl」メニューに追加します。
そのメニュー項目は、マイナーモードを(非)アクティブにしますhexl-isearch-mode
。このモードをアクティブisearch
にすると、hexlバッファーの代わりにバイナリデータで検索されます。
検索文字列はで読み取られread
ます。したがって、lisp文字列のすべてのエスケープシーケンスは機能します。例として、dos line endを検索\x0a\x0d
または検索できます\^M\n
。
コードは完璧ではありません。
ELF\x01
ファイルの先頭にのみ出現する文字列を検索するとします。さらに、ELf\x00
後でバイナリに文字列があると仮定します。次に、ELF\x0
Emacsと入力して到着すると、後で一致するものが見つかります。入力を続けると、ELF\x01
Emacsは、その文字列がすでに到着しているため、その文字列の出現はないと考えELF\x0
ますELF\x01
。このような場合、重複検索を行う価値があります。(この問題は、パッケージのgitバージョンですでに修正されています。)
右側の文字列表現ではなく、バイトシーケンスのみがhexlバッファーで正しく強調表示されます。
検索文字列がhexlバッファーの2行にまたがっている場合、行の最後の文字列表現と行の先頭のアドレスも強調表示されます。これは、それらが一致に属しているためではなく、バイトシーケンスを強調表示するときに邪魔になるためです。
(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")