locate.el 25.7 KB
Newer Older
1
;;; locate.el --- interface to the locate command  -*- lexical-binding:t -*-
Richard M. Stallman's avatar
Richard M. Stallman committed
2

Paul Eggert's avatar
Paul Eggert committed
3
;; Copyright (C) 1996, 1998, 2001-2020 Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
4

5
;; Author: Peter Breton <pbreton@cs.umb.edu>
Dave Love's avatar
Dave Love committed
6
;; Keywords: unix files
Richard M. Stallman's avatar
Richard M. Stallman committed
7 8 9

;; This file is part of GNU Emacs.

10
;; GNU Emacs is free software: you can redistribute it and/or modify
Richard M. Stallman's avatar
Richard M. Stallman committed
11
;; it under the terms of the GNU General Public License as published by
12 13
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Richard M. Stallman's avatar
Richard M. Stallman committed
14 15 16 17 18 19 20

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
21
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
Richard M. Stallman's avatar
Richard M. Stallman committed
22 23 24

;;; Commentary:

25
;; Search a database of files and use dired commands on the result.
Richard M. Stallman's avatar
Richard M. Stallman committed
26 27 28 29 30 31 32
;;
;; Locate.el provides an interface to a program which searches a
;; database of file names. By default, this program is the GNU locate
;; command, but it could also be the BSD-style find command, or even a
;; user specified command.
;;
;; To use the BSD-style "fast find", or any other shell command of the
33
;; form
Richard M. Stallman's avatar
Richard M. Stallman committed
34 35 36
;;
;;   SHELLPROGRAM  Name-to-find
;;
37
;; set the variable `locate-command' in your init file.
Richard M. Stallman's avatar
Richard M. Stallman committed
38
;;
39
;;   To use a more complicated expression, create a function which
40 41 42
;; takes a string (the name to find) as input and returns a list.
;; The first element should be the command to be executed, the remaining
;; elements should be the arguments (including the name to find). Then put
Richard M. Stallman's avatar
Richard M. Stallman committed
43
;;
44
;; (setq locate-make-command-line 'my-locate-command-line)
Richard M. Stallman's avatar
Richard M. Stallman committed
45 46
;;
;; in your .emacs, using the name of your function in place of
47
;; my-locate-command-line.
Richard M. Stallman's avatar
Richard M. Stallman committed
48 49 50 51 52
;;
;; You should make sure that whichever command you use works correctly
;; from a shell prompt. GNU locate and BSD find expect the file databases
;; to either be in standard places or located via environment variables.
;; If the latter, make sure these environment variables are set in
53
;; your emacs process.
Richard M. Stallman's avatar
Richard M. Stallman committed
54 55 56 57
;;
;; Locate-mode assumes that each line output from the locate-command
;; consists exactly of a file name, possibly preceded or trailed by
;; whitespace. If your file database has other information on the line (for
58
;; example, the file size), you will need to redefine the function
59
;; `locate-get-file-positions' to return a list consisting of the first
Richard M. Stallman's avatar
Richard M. Stallman committed
60 61 62 63 64 65 66 67 68
;; character in the file name and the last character in the file name.
;;
;; To use locate-mode, simply type M-x locate and then the string
;; you wish to find. You can use almost all of the dired commands in
;; the resulting *Locate* buffer.  It is worth noting that your commands
;; do not, of course, affect the file database. For example, if you
;; compress a file in the locate buffer, the actual file will be
;; compressed, but the entry in the file database will not be
;; affected. Consequently, the database and the filesystem will be out
69
;; of sync until the next time the database is updated.
Richard M. Stallman's avatar
Richard M. Stallman committed
70
;;
71
;; The command `locate-with-filter' keeps only lines matching a
Richard M. Stallman's avatar
Richard M. Stallman committed
72 73
;; regular expression; this is often useful to constrain a big search.
;;
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

;;;;; Building a database of files ;;;;;;;;;
;;
;; You can create a simple files database with a port of the Unix find command
;; and one of the various Windows NT various scheduling utilities,
;; for example the AT command from the NT Resource Kit, WinCron which is
;; included with Microsoft FrontPage, or the shareware NTCron program.
;;
;; To set up a function which searches the files database, do something
;; like this:
;;
;; (defvar locate-fcodes-file       "c:/users/peter/fcodes")
;; (defvar locate-make-command-line 'nt-locate-make-command-line)
;;
;; (defun nt-locate-make-command-line (arg)
;;  (list "grep" "-i" arg locate-fcodes-file))
;;
;;;;;;;; ADVICE For dired-make-relative: ;;;;;;;;;
;;
;; For certain dired commands to work right, you should also include the
;; following in your _emacs/.emacs:
;;
;; (defadvice dired-make-relative (before set-no-error activate)
;;   "For locate mode and Windows, don't return errors"
98
;;   (if (and (derived-mode-p 'locate-mode)
Glenn Morris's avatar
Glenn Morris committed
99
;; 	   (memq system-type '(windows-nt ms-dos)))
100 101 102 103 104 105
;;       (ad-set-arg 2 t)
;;     ))
;;
;; Otherwise, `dired-make-relative' will give error messages like
;; "FILENAME: not in directory tree growing at /"

Richard M. Stallman's avatar
Richard M. Stallman committed
106 107 108

;;; Code:

109
(require 'dired)
Richard M. Stallman's avatar
Richard M. Stallman committed
110 111 112

;; Variables

113
(defvar locate-current-filter nil)
114 115
(defvar locate-local-filter nil)
(defvar locate-local-search nil)
116
(defvar locate-local-prompt nil)
117

Stephen Eglen's avatar
Stephen Eglen committed
118 119 120 121
(defgroup locate nil
  "Interface to the locate command."
  :prefix "locate-"
  :group 'external)
Richard M. Stallman's avatar
Richard M. Stallman committed
122

Stephen Eglen's avatar
Stephen Eglen committed
123
(defcustom locate-command "locate"
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  "Executable program for searching a database of files.
The Emacs commands `locate' and `locate-with-filter' use this.
The value should be a program that can be called from a shell
with one argument, SEARCH-STRING.  The program determines which
database it searches.  The output of the program should consist
of those file names in the database that match SEARCH-STRING,
listed one per line, possibly with leading or trailing
whitespace.  If the output is in another form, you may have to
redefine the function `locate-get-file-positions'.

The program may interpret SEARCH-STRING as a literal string, a
shell pattern or a regular expression.  The exact rules of what
constitutes a match may also depend on the program.

The standard value of this variable is \"locate\".
This program normally searches a database of all files on your
system, or of all files that you have access to.  Consult the
documentation of that program for the details about how it determines
which file names match SEARCH-STRING.  (Those details vary highly with
the version.)"
Stephen Eglen's avatar
Stephen Eglen committed
144 145
  :type 'string
  :group 'locate)
Richard M. Stallman's avatar
Richard M. Stallman committed
146

147 148 149 150 151
(defcustom locate-post-command-hook nil
  "List of hook functions run after `locate' (see `run-hooks')."
  :type  'hook
  :group 'locate)

Stephen Eglen's avatar
Stephen Eglen committed
152 153
(defvar locate-history-list nil
  "The history list used by the \\[locate] command.")
Richard M. Stallman's avatar
Richard M. Stallman committed
154

155 156 157
(defvar locate-grep-history-list nil
  "The history list used by the \\[locate-with-filter] command.")

Stephen Eglen's avatar
Stephen Eglen committed
158
(defcustom locate-make-command-line 'locate-default-make-command-line
159 160 161 162 163 164
  "Function used to create the locate command line.
The Emacs commands `locate' and `locate-with-filter' use this.
This function should take one argument, a string (the name to find)
and return a list of strings.  The first element of the list should be
the name of a command to be executed by a shell, the remaining elements
should be the arguments to that command (including the name to find)."
Stephen Eglen's avatar
Stephen Eglen committed
165 166 167 168
  :type 'function
  :group 'locate)

(defcustom locate-buffer-name "*Locate*"
169
  "Name of the buffer to show results from the \\[locate] command."
Stephen Eglen's avatar
Stephen Eglen committed
170 171 172 173
  :type 'string
  :group 'locate)

(defcustom locate-fcodes-file nil
174 175 176 177 178 179 180 181
  "File name for the database of file names used by `locate'.
If non-nil, `locate' uses this name in the header of the `*Locate*'
buffer.  If nil, it mentions no file name in that header.

Just setting this variable does not actually change the database
that `locate' searches.  The executive program that the Emacs
function `locate' uses, as given by the variables `locate-command'
or `locate-make-command-line', determines the database."
182
  :type '(choice (const :tag "None" nil) file)
Stephen Eglen's avatar
Stephen Eglen committed
183 184
  :group 'locate)

185
(defcustom locate-header-face nil
186
  "Face used to highlight the locate header."
187
  :type '(choice (const :tag "None" nil) face)
Stephen Eglen's avatar
Stephen Eglen committed
188
  :group 'locate)
Richard M. Stallman's avatar
Richard M. Stallman committed
189

190
;;;###autoload
191
(defcustom locate-ls-subdir-switches (purecopy "-al")
192
  "`ls' switches for inserting subdirectories in `*Locate*' buffers.
193
This should contain the \"-l\" switch, but not the \"-F\" or \"-b\" switches."
194 195
  :type 'string
  :group 'locate
196
  :version "22.1")
197

198 199 200 201 202 203 204 205 206 207
(defcustom locate-update-when-revert nil
  "This option affects how the *Locate* buffer gets reverted.
If non-nil, offer to update the locate database when reverting that buffer.
\(Normally, you need to have root privileges for this to work.  See the
option `locate-update-path'.)
If nil, reverting does not update the locate database."
  :type 'boolean
  :group 'locate
  :version "22.1")

208
(defcustom locate-update-command "updatedb"
209
  "The executable program used to update the locate database."
210 211
  :type 'string
  :group 'locate)
Richard M. Stallman's avatar
Richard M. Stallman committed
212

213 214 215 216 217 218 219 220 221 222 223
(defcustom locate-update-path "/"
  "The default directory from where `locate-update-command' is called.
Usually, root permissions are required to run that command.  This
can be achieved by setting this option to \"/su::\" or \"/sudo::\"
\(if you have the appropriate authority).  If your current user
permissions are sufficient to run the command, you can set this
option to \"/\"."
  :type 'string
  :group 'locate
  :version "22.1")

224
(defcustom locate-prompt-for-command nil
225
  "If non-nil, the `locate' command prompts for a command to run.
226 227 228 229
Otherwise, that behavior is invoked via a prefix argument.

Setting this option non-nil actually inverts the meaning of a prefix arg;
that is, with a prefix arg, you get the default behavior."
230
  :group 'locate
231
  :type 'boolean)
232

233 234 235 236 237
(defcustom locate-mode-hook nil
  "List of hook functions run by `locate-mode' (see `run-mode-hooks')."
  :type  'hook
  :group 'locate)

Richard M. Stallman's avatar
Richard M. Stallman committed
238 239 240
;; Functions

(defun locate-default-make-command-line (search-string)
241
  (list locate-command search-string))
Richard M. Stallman's avatar
Richard M. Stallman committed
242

243 244 245 246 247 248 249 250 251 252 253
(defun locate-word-at-point ()
  (let ((pt (point)))
    (buffer-substring-no-properties
     (save-excursion
       (skip-chars-backward "-a-zA-Z0-9.")
       (point))
     (save-excursion
       (skip-chars-forward "-a-zA-Z0-9.")
       (skip-chars-backward "." pt)
       (point)))))

Chong Yidong's avatar
Chong Yidong committed
254
;; Function for use in interactive declarations.
255 256 257 258 259 260 261 262 263
(defun locate-prompt-for-search-string ()
  (if (or (and current-prefix-arg
	       (not locate-prompt-for-command))
	  (and (not current-prefix-arg) locate-prompt-for-command))
      (let ((locate-cmd (funcall locate-make-command-line "")))
	(read-from-minibuffer
	 "Run locate (like this): "
	 (cons
	  (concat (car locate-cmd) "  "
264
		  (mapconcat #'identity (cdr locate-cmd) " "))
265 266 267 268 269 270 271 272 273 274 275 276 277
	  (+ 2 (length (car locate-cmd))))
	 nil nil 'locate-history-list))
    (let* ((default (locate-word-at-point))
	   (input
	    (read-from-minibuffer
	     (if  (> (length default) 0)
		 (format "Locate (default %s): " default)
	       (format "Locate: "))
	     nil nil nil 'locate-history-list default t)))
      (and (equal input "") default
	   (setq input default))
      input)))

Dan Nicolaescu's avatar
Dan Nicolaescu committed
278
;;;###autoload
279
(defun locate (search-string &optional filter arg)
280
  "Run the program `locate', putting results in `*Locate*' buffer.
281
Pass it SEARCH-STRING as argument.  Interactively, prompt for SEARCH-STRING.
282
With prefix arg ARG, prompt for the exact shell command to run instead.
283 284 285 286 287 288 289 290 291 292 293 294 295

This program searches for those file names in a database that match
SEARCH-STRING and normally outputs all matching absolute file names,
one per line.  The database normally consists of all files on your
system, or of all files that you have access to.  Consult the
documentation of the program for the details about how it determines
which file names match SEARCH-STRING.  (Those details vary highly with
the version.)

You can specify another program for this command to run by customizing
the variables `locate-command' or `locate-make-command-line'.

The main use of FILTER is to implement `locate-with-filter'.  See
296 297
the docstring of that function for its meaning.

298 299
After preparing the results buffer, this runs `dired-mode-hook' and
then `locate-post-command-hook'."
Richard M. Stallman's avatar
Richard M. Stallman committed
300
  (interactive
301 302 303 304 305
   (list
    (locate-prompt-for-search-string)
    nil
    current-prefix-arg))

306
  (if (equal search-string "")
307
      (error "Please specify a filename to search for"))
308
  (let* ((locate-cmd-list (funcall locate-make-command-line search-string))
Richard M. Stallman's avatar
Richard M. Stallman committed
309 310
	 (locate-cmd (car locate-cmd-list))
	 (locate-cmd-args (cdr locate-cmd-list))
311
	 (run-locate-command
312
	  (or (and arg (not locate-prompt-for-command))
313
	      (and (not arg) locate-prompt-for-command))))
314

Richard M. Stallman's avatar
Richard M. Stallman committed
315
    ;; Find the Locate buffer
316
    (save-window-excursion              ;FIXME: What window-excursion?
317
      (set-buffer (get-buffer-create locate-buffer-name))
318
      (locate-mode)
319
      (let ((inhibit-read-only t)
320 321
	    (buffer-undo-list t))
	(erase-buffer)
322

323 324 325
	(setq locate-current-filter filter)
	(set (make-local-variable 'locate-local-search) search-string)
	(set (make-local-variable 'locate-local-filter) filter)
326
	(set (make-local-variable 'locate-local-prompt) run-locate-command)
327

328 329
	(if run-locate-command
	    (shell-command search-string locate-buffer-name)
330
	  (apply #'call-process locate-cmd nil t nil locate-cmd-args))
331

332 333
	(and filter
	     (locate-filter-output filter))
Richard M. Stallman's avatar
Richard M. Stallman committed
334

335
	(locate-do-setup search-string)))
336
    (and (not (string-equal (buffer-name) locate-buffer-name))
337
	(pop-to-buffer locate-buffer-name))
338

339
    (run-hooks 'dired-mode-hook)
340
    (dired-next-line 3)			;move to first matching file.
341
    (run-hooks 'locate-post-command-hook)))
Richard M. Stallman's avatar
Richard M. Stallman committed
342

Dan Nicolaescu's avatar
Dan Nicolaescu committed
343
;;;###autoload
344
(defun locate-with-filter (search-string filter &optional arg)
345 346 347 348 349 350 351 352 353
  "Run the executable program `locate' with a filter.
This function is similar to the function `locate', which see.
The difference is that, when invoked interactively, the present function
prompts for both SEARCH-STRING and FILTER.  It passes SEARCH-STRING
to the locate executable program.  It produces a `*Locate*' buffer
that lists only those lines in the output of the locate program that
contain a match for the regular expression FILTER; this is often useful
to constrain a big search.

354 355
ARG is the interactive prefix arg, which has the same effect as in `locate'.

356 357
When called from Lisp, this function is identical with `locate',
except that FILTER is not optional."
Richard M. Stallman's avatar
Richard M. Stallman committed
358
  (interactive
359 360 361 362 363 364
   (list
    (locate-prompt-for-search-string)
    (read-from-minibuffer "Filter: " nil nil
			  nil 'locate-grep-history-list)
    current-prefix-arg))
  (locate search-string filter arg))
Richard M. Stallman's avatar
Richard M. Stallman committed
365 366 367 368

(defun locate-filter-output (filter)
  "Filter output from the locate command."
  (goto-char (point-min))
369
  (keep-lines filter))
Richard M. Stallman's avatar
Richard M. Stallman committed
370

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
(defvar locate-mode-map
  (let ((map (copy-keymap dired-mode-map)))
    ;; Undefine Useless Dired Menu bars
    (define-key map [menu-bar Dired]   'undefined)
    (define-key map [menu-bar subdir]  'undefined)
    (define-key map [menu-bar mark executables] 'undefined)
    (define-key map [menu-bar mark directory]   'undefined)
    (define-key map [menu-bar mark directories] 'undefined)
    (define-key map [menu-bar mark symlinks]    'undefined)
    (define-key map [M-mouse-2] 'locate-mouse-view-file)
    (define-key map "\C-c\C-t"  'locate-tags)
    (define-key map "l"       'locate-do-redisplay)
    (define-key map "U"       'dired-unmark-all-files)
    (define-key map "V"       'locate-find-directory)
    map)
Richard M. Stallman's avatar
Richard M. Stallman committed
386 387 388 389 390
  "Local keymap for Locate mode buffers.")

;; This variable is used to indent the lines and then to search for
;; the file name
(defconst locate-filename-indentation 4
391
  "The amount of indentation for each file.")
Richard M. Stallman's avatar
Richard M. Stallman committed
392 393

(defun locate-get-file-positions ()
394 395 396 397 398 399 400 401 402
  "Return list of start and end of the file name on the current line.
This is a list of two buffer positions.

You should only call this function on lines that contain a file name
listed by the locate program.  Inside inserted subdirectories, or if
there is no file name on the current line, the return value is
meaningless.  You can check whether the current line contains a file
listed by the locate program, using the function
`locate-main-listing-line-p'."
403 404 405 406
  (list (+ locate-filename-indentation
           (line-beginning-position))
        ;; Assume names end at the end of the line.
        (line-end-position)))
Richard M. Stallman's avatar
Richard M. Stallman committed
407 408

;; From SQL-mode
409
(defun locate-current-line-number ()
Richard M. Stallman's avatar
Richard M. Stallman committed
410 411 412 413 414 415
  "Return the current line number, as an integer."
  (+ (count-lines (point-min) (point))
     (if (eq (current-column) 0)
	 1
       0)))

416 417 418 419 420 421
;; You should only call this function on lines that contain a file name
;; listed by the locate program.  Inside inserted subdirectories, or if
;; there is no file name on the current line, the return value is
;; meaningless.  You can check whether the current line contains a file
;; listed by the locate program, using the function
;; `locate-main-listing-line-p'.
Richard M. Stallman's avatar
Richard M. Stallman committed
422 423
(defun locate-get-filename ()
  (let ((pos    (locate-get-file-positions))
424
	(lineno (locate-current-line-number)))
425 426
    (and (not (eq lineno 1))
	 (not (eq lineno 2))
Richard M. Stallman's avatar
Richard M. Stallman committed
427 428
	 (buffer-substring (elt pos 0) (elt pos 1)))))

429 430 431 432 433 434 435
(defun locate-main-listing-line-p ()
  "Return t if current line contains a file name listed by locate.
This function returns nil if the current line either contains no
file name or is inside a subdirectory."
  (save-excursion
    (forward-line 0)
    (looking-at (concat "."
436
			(make-string (1- locate-filename-indentation) ?\s)
437 438
			"\\(/\\|[A-Za-z]:\\)"))))

439
(defun locate-mouse-view-file (event)
Richard M. Stallman's avatar
Richard M. Stallman committed
440
  "In Locate mode, view a file, using the mouse."
441
  (interactive "@e")
Richard M. Stallman's avatar
Richard M. Stallman committed
442 443
  (save-excursion
    (goto-char (posn-point (event-start event)))
444 445 446
    (if (locate-main-listing-line-p)
	(view-file (locate-get-filename))
      (message "This command only works inside main listing."))))
Richard M. Stallman's avatar
Richard M. Stallman committed
447 448 449 450

;; Define a mode for locate
;; Default directory is set to "/" so that dired commands, which
;; expect to be in a tree, will work properly
451
(define-derived-mode locate-mode special-mode "Locate"
452
  "Major mode for the `*Locate*' buffer made by \\[locate].
453
\\<locate-mode-map>\
454 455
In that buffer, you can use almost all the usual dired bindings.
\\[locate-find-directory] visits the directory of the file on the current line.
456
This function runs `locate-mode-hook' before returning.
457

458 459 460 461 462 463 464
Operating on listed files works, but does not always
automatically update the buffer as in ordinary Dired.
This is true both for the main listing and for subdirectories.
Reverting the buffer using \\[revert-buffer] deletes all subdirectories.
Specific `locate-mode' commands, such as \\[locate-find-directory],
do not work in subdirectories.

465
\\{locate-mode-map}"
466
  ;; Avoid clobbering this variable
467
  (make-local-variable 'dired-subdir-alist)
468
  (setq default-directory   "/"
469 470
	buffer-read-only    t)
  (add-to-invisibility-spec '(dired . t))
471
  (dired-alist-add-1 default-directory (point-min-marker))
472 473 474
  (set (make-local-variable 'dired-directory) "/")
  (set (make-local-variable 'dired-subdir-switches) locate-ls-subdir-switches)
  (setq dired-switches-alist nil)
475
  ;; This should support both Unix and Windoze style names
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
  (setq-local directory-listing-before-filename-regexp
              (concat "^.\\("
                      (make-string (1- locate-filename-indentation) ?\s)
                      "\\)\\|"
                      (default-value
                        'directory-listing-before-filename-regexp)))
  (setq-local dired-actual-switches "")
  (setq-local dired-permission-flags-regexp
              (concat "^.\\("
                      (make-string (1- locate-filename-indentation) ?\s)
                      "\\)\\|"
                      (default-value 'dired-permission-flags-regexp)))

  (setq-local revert-buffer-function #'locate-update)
  (setq-local page-delimiter "\n\n"))
491
(put 'locate-mode 'derived-mode-parent 'dired-mode)
Richard M. Stallman's avatar
Richard M. Stallman committed
492

493 494 495
(defun locate-do-setup (search-string)
  (goto-char (point-min))
  (save-excursion
496

497 498 499
    ;; Nothing returned from locate command?
    (and (eobp)
	 (progn
500 501
	   (kill-buffer locate-buffer-name)
	   (if locate-current-filter
502
	       (user-error "Locate: no match for %s in database using filter %s"
503
		      search-string locate-current-filter)
504
	     (user-error "Locate: no match for %s in database" search-string))))
505

506
    (locate-insert-header search-string)
507

508
    (while (not (eobp))
509
      (insert-char ?\s locate-filename-indentation t)
510 511 512
      (locate-set-properties)
      (forward-line 1)))
  (goto-char (point-min)))
Richard M. Stallman's avatar
Richard M. Stallman committed
513 514 515 516

(defun locate-set-properties ()
  (save-excursion
    (let ((pos (locate-get-file-positions)))
517
      (dired-insert-set-properties (elt pos 0) (elt pos 1)))))
Richard M. Stallman's avatar
Richard M. Stallman committed
518 519

(defun locate-insert-header (search-string)
520 521 522 523
  ;; There needs to be a space before `Matches, because otherwise,
  ;; `*!" would erase the `M'.  We can not use two spaces, or the line
  ;; would mistakenly fit `dired-subdir-regexp'.
  (let ((locate-format-string "  /:\n Matches for %s")
Richard M. Stallman's avatar
Richard M. Stallman committed
524 525 526 527
	(locate-regexp-match
	 (concat " *Matches for \\(" (regexp-quote search-string) "\\)"))
	(locate-format-args (list search-string))
	)
528

529
    (and locate-fcodes-file
Richard M. Stallman's avatar
Richard M. Stallman committed
530 531 532 533 534 535 536 537 538 539
	(setq locate-format-string
	      (concat locate-format-string " in %s")
	      locate-regexp-match
	      (concat locate-regexp-match
		      " in \\("
		      (regexp-quote locate-fcodes-file)
		      "\\)")
	      locate-format-args
	      (append (list locate-fcodes-file) locate-format-args)))

540
    (and locate-current-filter
Richard M. Stallman's avatar
Richard M. Stallman committed
541 542 543 544 545 546 547 548 549 550
	(setq locate-format-string
	      (concat locate-format-string " using filter %s")
	      locate-regexp-match
	      (concat locate-regexp-match
		      " using filter "
		      "\\("
		      (regexp-quote locate-current-filter)
		      "\\)")
	      locate-format-args
	      (append (list locate-current-filter) locate-format-args)))
551

Richard M. Stallman's avatar
Richard M. Stallman committed
552
    (setq locate-format-string
553
	  (concat locate-format-string ":\n\n")
Richard M. Stallman's avatar
Richard M. Stallman committed
554
	  locate-regexp-match
555
	  (concat locate-regexp-match ":\n"))
556

557
    (insert (apply #'format locate-format-string (reverse locate-format-args)))
558

Richard M. Stallman's avatar
Richard M. Stallman committed
559 560
    (save-excursion
      (goto-char (point-min))
561
      (forward-line 1)
Richard M. Stallman's avatar
Richard M. Stallman committed
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
      (if (not (looking-at locate-regexp-match))
	  nil
	(add-text-properties (match-beginning 1) (match-end 1)
			     (list 'face locate-header-face))
	(and (match-beginning 2)
	     (add-text-properties (match-beginning 2) (match-end 2)
				  (list 'face locate-header-face)))
	(and (match-beginning 3)
	     (add-text-properties (match-beginning 3) (match-end 3)
				  (list 'face locate-header-face)))
	))))

(defun locate-tags ()
  "Visit a tags table in `*Locate*' mode."
  (interactive)
577 578 579 580 581
  (if (locate-main-listing-line-p)
      (let ((tags-table (locate-get-filename)))
	(and (y-or-n-p (format "Visit tags table %s? " tags-table))
	     (visit-tags-table tags-table)))
    (message "This command only works inside main listing.")))
582 583

;; From Stephen Eglen <stephen@cns.ed.ac.uk>
584
(defun locate-update (_ignore1 _ignore2)
585 586 587
  "Revert the *Locate* buffer.
If `locate-update-when-revert' is non-nil, offer to update the
locate database using the shell command in `locate-update-command'."
588 589
  (let ((locate-buffer-name (buffer-name))
	(locate-prompt-for-command locate-local-prompt))
590 591 592 593 594 595 596 597
    (and locate-update-when-revert
	 (yes-or-no-p "Update locate database (may take a few seconds)? ")
	 ;; `expand-file-name' is used in order to autoload Tramp if
	 ;; necessary.  It cannot be loaded when `default-directory'
	 ;; is remote.
	 (let ((default-directory (expand-file-name locate-update-path)))
	   (shell-command locate-update-command)))
    (locate locate-local-search locate-local-filter)))
598 599 600 601 602 603 604 605 606

;;; Modified three functions from `dired.el':
;;;   dired-find-directory,
;;;   dired-find-directory-other-window
;;;   dired-get-filename

(defun locate-find-directory ()
  "Visit the directory of the file mentioned on this line."
  (interactive)
607 608 609 610 611 612 613 614
  (if (locate-main-listing-line-p)
      (let ((directory-name (locate-get-dirname)))
	(if (file-directory-p directory-name)
	    (find-file directory-name)
	  (if (file-symlink-p directory-name)
	      (error "Directory is a symlink to a nonexistent target")
	    (error "Directory no longer exists; run `updatedb' to update database"))))
    (message "This command only works inside main listing.")))
615 616 617 618

(defun locate-find-directory-other-window ()
  "Visit the directory of the file named on this line in other window."
  (interactive)
619 620 621
  (if (locate-main-listing-line-p)
      (find-file-other-window (locate-get-dirname))
    (message "This command only works inside main listing.")))
622

623 624 625 626 627 628
;; You should only call this function on lines that contain a file name
;; listed by the locate program.  Inside inserted subdirectories, or if
;; there is no file name on the current line, the return value is
;; meaningless.  You can check whether the current line contains a file
;; listed by the locate program, using the function
;; `locate-main-listing-line-p'.
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
(defun locate-get-dirname ()
  "Return the directory name of the file mentioned on this line."
  (let (file (filepos (locate-get-file-positions)))
    (if (setq file (buffer-substring (nth 0 filepos) (nth 1 filepos)))
	(progn
	  ;; Get rid of the mouse-face property that file names have.
	  (set-text-properties 0 (length file) nil file)
	  (setq file (file-name-directory file))
	  ;; Unquote names quoted by ls or by dired-insert-directory.
	  ;; Using read to unquote is much faster than substituting
	  ;; \007 (4 chars) -> ^G  (1 char) etc. in a lisp loop.
	  (setq file
		(read
		 (concat "\""
			 ;; some ls -b don't escape quotes, argh!
			 ;; This is not needed for GNU ls, though.
			 (or (dired-string-replace-match
			      "\\([^\\]\\|\\`\\)\"" file "\\1\\\\\"" nil t)
			     file)
			 "\"")))))
    (and file buffer-file-coding-system
	 (not file-name-coding-system)
	 (setq file (encode-coding-string file buffer-file-coding-system)))
    file))

;; Only for GNU locate
(defun locate-in-alternate-database  (search-string database)
656
  "Run the GNU locate program, using an alternate database.
657 658 659 660 661

This command only works if you use GNU locate.  It does not work
properly if `locate-prompt-for-command' is set to t.  In that
case, you can just run the regular `locate' command and specify
the database on the command line."
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
  (interactive
      (list
       (progn
	 ;; (require 'locate)
	 (read-from-minibuffer "Locate: " nil nil
			       nil 'locate-history-list))
       (read-file-name "Locate using Database: " )
       ))
  (or (file-exists-p database)
      (error "Database file %s does not exist" database))
  (let ((locate-make-command-line
	 (function (lambda (string)
		     (cons locate-command
			   (list (concat "--database="
					 (expand-file-name database))
				 string))))))
678
    (locate search-string)))
Richard M. Stallman's avatar
Richard M. Stallman committed
679

680 681 682 683 684 685 686 687
(defun locate-do-redisplay (&optional arg test-for-subdir)
  "Like `dired-do-redisplay', but adapted for `*Locate*' buffers."
  (interactive "P\np")
  (if (string= (dired-current-directory) "/")
      (message "This command only works in subdirectories.")
    (let ((dired-actual-switches locate-ls-subdir-switches))
      (dired-do-redisplay arg test-for-subdir))))

Richard M. Stallman's avatar
Richard M. Stallman committed
688 689 690
(provide 'locate)

;;; locate.el ends here