(isearch-mode-map): Undo previous change.
[bpt/emacs.git] / lisp / locate.el
CommitLineData
092af6d8 1;;; locate.el --- interface to the locate command
6aea3b07 2
5421b899 3;; Copyright (C) 1996, 1998, 2001 Free Software Foundation, Inc.
6aea3b07 4
dc268724 5;; Author: Peter Breton <pbreton@cs.umb.edu>
f947a7fa 6;; Keywords: unix files
6aea3b07
RS
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
24
25;;; Commentary:
26
271a87e8 27;; Search a database of files and use dired commands on the result.
6aea3b07
RS
28;;
29;; Locate.el provides an interface to a program which searches a
30;; database of file names. By default, this program is the GNU locate
31;; command, but it could also be the BSD-style find command, or even a
32;; user specified command.
33;;
34;; To use the BSD-style "fast find", or any other shell command of the
83346ee8 35;; form
6aea3b07
RS
36;;
37;; SHELLPROGRAM Name-to-find
38;;
be8bf2d0 39;; set the variable `locate-command' in your .emacs file.
6aea3b07 40;;
83346ee8 41;; To use a more complicated expression, create a function which
be8bf2d0
RS
42;; takes a string (the name to find) as input and returns a list.
43;; The first element should be the command to be executed, the remaining
44;; elements should be the arguments (including the name to find). Then put
6aea3b07 45;;
83346ee8 46;; (setq locate-make-command-line 'my-locate-command-line)
6aea3b07
RS
47;;
48;; in your .emacs, using the name of your function in place of
be8bf2d0 49;; my-locate-command-line.
6aea3b07
RS
50;;
51;; You should make sure that whichever command you use works correctly
52;; from a shell prompt. GNU locate and BSD find expect the file databases
53;; to either be in standard places or located via environment variables.
54;; If the latter, make sure these environment variables are set in
be8bf2d0 55;; your emacs process.
6aea3b07
RS
56;;
57;; Locate-mode assumes that each line output from the locate-command
58;; consists exactly of a file name, possibly preceded or trailed by
59;; whitespace. If your file database has other information on the line (for
83346ee8 60;; example, the file size), you will need to redefine the function
be8bf2d0 61;; `locate-get-file-positions' to return a list consisting of the first
6aea3b07
RS
62;; character in the file name and the last character in the file name.
63;;
64;; To use locate-mode, simply type M-x locate and then the string
65;; you wish to find. You can use almost all of the dired commands in
66;; the resulting *Locate* buffer. It is worth noting that your commands
67;; do not, of course, affect the file database. For example, if you
68;; compress a file in the locate buffer, the actual file will be
69;; compressed, but the entry in the file database will not be
70;; affected. Consequently, the database and the filesystem will be out
be8bf2d0 71;; of sync until the next time the database is updated.
6aea3b07 72;;
be8bf2d0 73;; The command `locate-with-filter' keeps only lines matching a
6aea3b07
RS
74;; regular expression; this is often useful to constrain a big search.
75;;
76\f
271a87e8
LT
77;;;;; Building a database of files ;;;;;;;;;
78;;
79;; You can create a simple files database with a port of the Unix find command
80;; and one of the various Windows NT various scheduling utilities,
81;; for example the AT command from the NT Resource Kit, WinCron which is
82;; included with Microsoft FrontPage, or the shareware NTCron program.
83;;
84;; To set up a function which searches the files database, do something
85;; like this:
86;;
87;; (defvar locate-fcodes-file "c:/users/peter/fcodes")
88;; (defvar locate-make-command-line 'nt-locate-make-command-line)
89;;
90;; (defun nt-locate-make-command-line (arg)
91;; (list "grep" "-i" arg locate-fcodes-file))
92;;
93;;;;;;;; ADVICE For dired-make-relative: ;;;;;;;;;
94;;
95;; For certain dired commands to work right, you should also include the
96;; following in your _emacs/.emacs:
97;;
98;; (defadvice dired-make-relative (before set-no-error activate)
99;; "For locate mode and Windows, don't return errors"
100;; (if (and (eq major-mode 'locate-mode)
101;; (memq system-type (list 'windows-nt 'ms-dos)))
102;; (ad-set-arg 2 t)
103;; ))
104;;
105;; Otherwise, `dired-make-relative' will give error messages like
106;; "FILENAME: not in directory tree growing at /"
107
108\f
6aea3b07
RS
109;;; Code:
110
111(eval-when-compile
112 (require 'dired))
113
114;; Variables
6aea3b07 115
be8bf2d0
RS
116(defvar locate-current-filter nil)
117
d979dc2b
SE
118(defgroup locate nil
119 "Interface to the locate command."
120 :prefix "locate-"
121 :group 'external)
6aea3b07 122
d979dc2b
SE
123(defcustom locate-command "locate"
124 "*The executable program used to search a database of files."
125 :type 'string
126 :group 'locate)
6aea3b07 127
d979dc2b
SE
128(defvar locate-history-list nil
129 "The history list used by the \\[locate] command.")
6aea3b07 130
83346ee8
PB
131(defvar locate-grep-history-list nil
132 "The history list used by the \\[locate-with-filter] command.")
133
d979dc2b
SE
134(defcustom locate-make-command-line 'locate-default-make-command-line
135 "*Function used to create the locate command line."
136 :type 'function
137 :group 'locate)
138
139(defcustom locate-buffer-name "*Locate*"
140 "*Name of the buffer to show results from the \\[locate] command."
141 :type 'string
142 :group 'locate)
143
144(defcustom locate-fcodes-file nil
be8bf2d0
RS
145 "*File name for the database of file names."
146 :type '(choice file (const nil))
d979dc2b
SE
147 :group 'locate)
148
83346ee8 149(defcustom locate-header-face nil
d979dc2b
SE
150 "*Face used to highlight the locate header."
151 :type 'face
152 :group 'locate)
6aea3b07 153
271a87e8
LT
154;;;###autoload
155(defcustom locate-ls-subdir-switches "-al"
156 "`ls' switches for inserting subdirectories in `*Locate*' buffers.
2f9a6938
LT
157This should contain the \"-l\" switch, but not the \"-F\" or
158\"-b\" switches."
271a87e8
LT
159 :type 'string
160 :group 'locate
161 :version "21.4")
162
be8bf2d0
RS
163(defcustom locate-update-command "updatedb"
164 "The command used to update the locate database."
165 :type 'string
166 :group 'locate)
6aea3b07 167
83346ee8 168(defcustom locate-prompt-for-command nil
442c8150 169 "If non-nil, the locate command prompts for a command to run.
83346ee8
PB
170Otherwise, that behavior is invoked via a prefix argument."
171 :group 'locate
172 :type 'boolean
173 )
174
6aea3b07
RS
175;; Functions
176
177(defun locate-default-make-command-line (search-string)
be8bf2d0 178 (list locate-command search-string))
6aea3b07 179
96c1776c
PB
180(defun locate-word-at-point ()
181 (let ((pt (point)))
182 (buffer-substring-no-properties
183 (save-excursion
184 (skip-chars-backward "-a-zA-Z0-9.")
185 (point))
186 (save-excursion
187 (skip-chars-forward "-a-zA-Z0-9.")
188 (skip-chars-backward "." pt)
189 (point)))))
190
1d96c2ff 191;;;###autoload
8348e1f9 192(defun locate (search-string &optional filter)
83346ee8
PB
193 "Run the program `locate', putting results in `*Locate*' buffer.
194With prefix arg, prompt for the locate command to run."
6aea3b07 195 (interactive
83346ee8 196 (list
8348e1f9
PB
197 (if (or (and current-prefix-arg
198 (not locate-prompt-for-command))
83346ee8 199 (and (not current-prefix-arg) locate-prompt-for-command))
8348e1f9
PB
200 (let ((locate-cmd (funcall locate-make-command-line "")))
201 (read-from-minibuffer
202 "Run locate (like this): "
203 (cons
204 (concat (car locate-cmd) " "
205 (mapconcat 'identity (cdr locate-cmd) " "))
206 (+ 2 (length (car locate-cmd))))
207 nil nil 'locate-history-list))
fe8c7212 208 (let* ((default (locate-word-at-point))
f1180544 209 (input
fe8c7212
RS
210 (read-from-minibuffer
211 (if (> (length default) 0)
212 (format "Locate (default `%s'): " default)
213 (format "Locate: "))
214 nil nil nil 'locate-history-list default t)))
215 (and (equal input "") default
216 (setq input default))
217 input))))
218 (if (equal search-string "")
c19813f3 219 (error "Please specify a filename to search for"))
be8bf2d0 220 (let* ((locate-cmd-list (funcall locate-make-command-line search-string))
6aea3b07
RS
221 (locate-cmd (car locate-cmd-list))
222 (locate-cmd-args (cdr locate-cmd-list))
83346ee8 223 (run-locate-command
8348e1f9
PB
224 (or (and current-prefix-arg (not locate-prompt-for-command))
225 (and (not current-prefix-arg) locate-prompt-for-command)))
be8bf2d0 226 )
83346ee8 227
6aea3b07 228 ;; Find the Locate buffer
be8bf2d0
RS
229 (save-window-excursion
230 (set-buffer (get-buffer-create locate-buffer-name))
231 (locate-mode)
271a87e8
LT
232 (let ((inhibit-read-only t))
233 (erase-buffer)
83346ee8 234
271a87e8 235 (setq locate-current-filter filter)
83346ee8 236
271a87e8
LT
237 (if run-locate-command
238 (shell-command search-string locate-buffer-name)
239 (apply 'call-process locate-cmd nil t nil locate-cmd-args))
83346ee8 240
271a87e8
LT
241 (and filter
242 (locate-filter-output filter))
6aea3b07 243
271a87e8
LT
244 (locate-do-setup search-string)
245 ))
be8bf2d0
RS
246 (and (not (string-equal (buffer-name) locate-buffer-name))
247 (switch-to-buffer-other-window locate-buffer-name))
83346ee8 248
96c1776c 249 (run-hooks 'dired-mode-hook)
271a87e8 250 (dired-next-line 3) ;move to first matching file.
83346ee8 251 (run-hooks 'locate-post-command-hook)
be8bf2d0
RS
252 )
253 )
6aea3b07 254
1d96c2ff 255;;;###autoload
6aea3b07 256(defun locate-with-filter (search-string filter)
9be8f657
GM
257 "Run the locate command with a filter.
258
259The filter is a regular expression. Only results matching the filter are
260shown; this is often useful to constrain a big search."
6aea3b07 261 (interactive
be8bf2d0
RS
262 (list (read-from-minibuffer "Locate: " nil nil
263 nil 'locate-history-list)
264 (read-from-minibuffer "Filter: " nil nil
83346ee8 265 nil 'locate-grep-history-list)))
8348e1f9 266 (locate search-string filter))
6aea3b07
RS
267
268(defun locate-filter-output (filter)
269 "Filter output from the locate command."
270 (goto-char (point-min))
83346ee8 271 (delete-non-matching-lines filter))
6aea3b07
RS
272
273(defvar locate-mode-map nil
274 "Local keymap for Locate mode buffers.")
275(if locate-mode-map
276 nil
277
278 (require 'dired)
279
280 (setq locate-mode-map (copy-keymap dired-mode-map))
281
282 ;; Undefine Useless Dired Menu bars
283 (define-key locate-mode-map [menu-bar Dired] 'undefined)
284 (define-key locate-mode-map [menu-bar subdir] 'undefined)
285
286 (define-key locate-mode-map [menu-bar mark executables] 'undefined)
287 (define-key locate-mode-map [menu-bar mark directory] 'undefined)
288 (define-key locate-mode-map [menu-bar mark directories] 'undefined)
289 (define-key locate-mode-map [menu-bar mark symlinks] 'undefined)
290
271a87e8 291 (define-key locate-mode-map [M-mouse-2] 'locate-mouse-view-file)
83346ee8 292 (define-key locate-mode-map "\C-c\C-t" 'locate-tags)
6aea3b07 293
271a87e8 294 (define-key locate-mode-map "l" 'locate-do-redisplay)
83346ee8
PB
295 (define-key locate-mode-map "U" 'dired-unmark-all-files)
296 (define-key locate-mode-map "V" 'locate-find-directory)
6aea3b07
RS
297)
298
299;; This variable is used to indent the lines and then to search for
300;; the file name
301(defconst locate-filename-indentation 4
be8bf2d0 302 "The amount of indentation for each file.")
6aea3b07 303
6aea3b07
RS
304(defun locate-get-file-positions ()
305 (save-excursion
be8bf2d0
RS
306 (end-of-line)
307 (let ((eol (point)))
308 (beginning-of-line)
83346ee8 309
be8bf2d0
RS
310 ;; Assumes names end at the end of the line
311 (forward-char locate-filename-indentation)
312 (list (point) eol))))
6aea3b07
RS
313
314;; From SQL-mode
be8bf2d0 315(defun locate-current-line-number ()
6aea3b07 316 "Return the current line number, as an integer."
6aea3b07
RS
317 (+ (count-lines (point-min) (point))
318 (if (eq (current-column) 0)
319 1
320 0)))
321
322(defun locate-get-filename ()
323 (let ((pos (locate-get-file-positions))
be8bf2d0 324 (lineno (locate-current-line-number)))
83346ee8
PB
325 (and (not (eq lineno 1))
326 (not (eq lineno 2))
6aea3b07
RS
327 (buffer-substring (elt pos 0) (elt pos 1)))))
328
271a87e8
LT
329(defun locate-main-listing-line-p ()
330 "Return t if current line contains a file name listed by locate.
331This function returns nil if the current line either contains no
332file name or is inside a subdirectory."
333 (save-excursion
334 (forward-line 0)
335 (looking-at (concat "."
336 (make-string (1- locate-filename-indentation) ?\ )
337 "\\(/\\|[A-Za-z]:\\)"))))
338
83346ee8 339(defun locate-mouse-view-file (event)
6aea3b07 340 "In Locate mode, view a file, using the mouse."
83346ee8 341 (interactive "@e")
6aea3b07
RS
342 (save-excursion
343 (goto-char (posn-point (event-start event)))
271a87e8
LT
344 (if (locate-main-listing-line-p)
345 (view-file (locate-get-filename))
346 (message "This command only works inside main listing."))))
6aea3b07
RS
347
348;; Define a mode for locate
349;; Default directory is set to "/" so that dired commands, which
350;; expect to be in a tree, will work properly
351(defun locate-mode ()
442c8150 352 "Major mode for the `*Locate*' buffer made by \\[locate].
271a87e8 353\\<locate-mode-map>\
442c8150
LT
354In that buffer, you can use almost all the usual dired bindings.
355\\[locate-find-directory] visits the directory of the file on the current line.
356
271a87e8
LT
357Operating on listed files works, but does not always
358automatically update the buffer as in ordinary Dired.
359This is true both for the main listing and for subdirectories.
360Reverting the buffer using \\[revert-buffer] deletes all subdirectories.
361Specific `locate-mode' commands, such as \\[locate-find-directory],
362do not work in subdirectories.
363
442c8150 364\\{locate-mode-map}"
271a87e8 365 ;; Not to be called interactively.
6aea3b07 366 (kill-all-local-variables)
271a87e8 367 ;; Avoid clobbering this variable
83346ee8 368 (make-local-variable 'dired-subdir-alist)
6aea3b07
RS
369 (use-local-map locate-mode-map)
370 (setq major-mode 'locate-mode
371 mode-name "Locate"
271a87e8
LT
372 default-directory "/"
373 buffer-read-only t
374 selective-display t)
83346ee8 375 (dired-alist-add-1 default-directory (point-min-marker))
271a87e8
LT
376 (set (make-local-variable 'dired-directory) "/")
377 (set (make-local-variable 'dired-subdir-switches) locate-ls-subdir-switches)
378 (setq dired-switches-alist nil)
5c6a8dfe 379 (make-local-variable 'dired-move-to-filename-regexp)
83346ee8 380 ;; This should support both Unix and Windoze style names
5c6a8dfe 381 (setq dired-move-to-filename-regexp
271a87e8 382 (concat "^."
83346ee8 383 (make-string (1- locate-filename-indentation) ?\ )
271a87e8
LT
384 "\\(/\\|[A-Za-z]:\\)\\|"
385 (default-value 'dired-move-to-filename-regexp)))
5c6a8dfe
RS
386 (make-local-variable 'dired-actual-switches)
387 (setq dired-actual-switches "")
388 (make-local-variable 'dired-permission-flags-regexp)
83346ee8
PB
389 (setq dired-permission-flags-regexp
390 (concat "^.\\("
391 (make-string (1- locate-filename-indentation) ?\ )
271a87e8
LT
392 "\\)\\|"
393 (default-value 'dired-permission-flags-regexp)))
be8bf2d0 394 (make-local-variable 'revert-buffer-function)
83346ee8 395 (setq revert-buffer-function 'locate-update)
271a87e8 396 (set (make-local-variable 'page-delimiter) "\n\n")
6aea3b07
RS
397 (run-hooks 'locate-mode-hook))
398
8348e1f9
PB
399(defun locate-do-setup (search-string)
400 (goto-char (point-min))
401 (save-excursion
83346ee8 402
8348e1f9
PB
403 ;; Nothing returned from locate command?
404 (and (eobp)
405 (progn
406 (kill-buffer locate-buffer-name)
407 (if locate-current-filter
408 (error "Locate: no match for %s in database using filter %s"
409 search-string locate-current-filter)
410 (error "Locate: no match for %s in database" search-string))))
83346ee8 411
8348e1f9 412 (locate-insert-header search-string)
83346ee8 413
8348e1f9
PB
414 (while (not (eobp))
415 (insert-char ?\ locate-filename-indentation t)
416 (locate-set-properties)
417 (forward-line 1)))
418 (goto-char (point-min)))
6aea3b07
RS
419
420(defun locate-set-properties ()
421 (save-excursion
422 (let ((pos (locate-get-file-positions)))
83346ee8 423 (dired-insert-set-properties (elt pos 0) (elt pos 1)))))
6aea3b07
RS
424
425(defun locate-insert-header (search-string)
271a87e8
LT
426 ;; There needs to be a space before `Matches, because otherwise,
427 ;; `*!" would erase the `M'. We can not use two spaces, or the line
428 ;; would mistakenly fit `dired-subdir-regexp'.
429 (let ((locate-format-string " /:\n Matches for %s")
6aea3b07
RS
430 (locate-regexp-match
431 (concat " *Matches for \\(" (regexp-quote search-string) "\\)"))
432 (locate-format-args (list search-string))
433 )
83346ee8 434
be8bf2d0 435 (and locate-fcodes-file
6aea3b07
RS
436 (setq locate-format-string
437 (concat locate-format-string " in %s")
438 locate-regexp-match
439 (concat locate-regexp-match
440 " in \\("
441 (regexp-quote locate-fcodes-file)
442 "\\)")
443 locate-format-args
444 (append (list locate-fcodes-file) locate-format-args)))
445
be8bf2d0 446 (and locate-current-filter
6aea3b07
RS
447 (setq locate-format-string
448 (concat locate-format-string " using filter %s")
449 locate-regexp-match
450 (concat locate-regexp-match
451 " using filter "
452 "\\("
453 (regexp-quote locate-current-filter)
454 "\\)")
455 locate-format-args
456 (append (list locate-current-filter) locate-format-args)))
83346ee8 457
6aea3b07 458 (setq locate-format-string
5421b899 459 (concat locate-format-string ":\n\n")
6aea3b07 460 locate-regexp-match
5421b899 461 (concat locate-regexp-match ":\n"))
83346ee8 462
5c6a8dfe 463 (insert (apply 'format locate-format-string (reverse locate-format-args)))
83346ee8 464
6aea3b07
RS
465 (save-excursion
466 (goto-char (point-min))
271a87e8 467 (forward-line 1)
6aea3b07
RS
468 (if (not (looking-at locate-regexp-match))
469 nil
470 (add-text-properties (match-beginning 1) (match-end 1)
471 (list 'face locate-header-face))
472 (and (match-beginning 2)
473 (add-text-properties (match-beginning 2) (match-end 2)
474 (list 'face locate-header-face)))
475 (and (match-beginning 3)
476 (add-text-properties (match-beginning 3) (match-end 3)
477 (list 'face locate-header-face)))
478 ))))
479
480(defun locate-tags ()
481 "Visit a tags table in `*Locate*' mode."
482 (interactive)
271a87e8
LT
483 (if (locate-main-listing-line-p)
484 (let ((tags-table (locate-get-filename)))
485 (and (y-or-n-p (format "Visit tags table %s? " tags-table))
486 (visit-tags-table tags-table)))
487 (message "This command only works inside main listing.")))
be8bf2d0
RS
488
489;; From Stephen Eglen <stephen@cns.ed.ac.uk>
490(defun locate-update (ignore1 ignore2)
491 "Update the locate database.
492Database is updated using the shell command in `locate-update-command'."
493 (let ((str (car locate-history-list)))
494 (cond ((yes-or-no-p "Update locate database (may take a few seconds)? ")
495 (shell-command locate-update-command)
8348e1f9 496 (locate str)))))
83346ee8
PB
497
498;;; Modified three functions from `dired.el':
499;;; dired-find-directory,
500;;; dired-find-directory-other-window
501;;; dired-get-filename
502
503(defun locate-find-directory ()
504 "Visit the directory of the file mentioned on this line."
505 (interactive)
271a87e8
LT
506 (if (locate-main-listing-line-p)
507 (let ((directory-name (locate-get-dirname)))
508 (if (file-directory-p directory-name)
509 (find-file directory-name)
510 (if (file-symlink-p directory-name)
511 (error "Directory is a symlink to a nonexistent target")
512 (error "Directory no longer exists; run `updatedb' to update database"))))
513 (message "This command only works inside main listing.")))
83346ee8
PB
514
515(defun locate-find-directory-other-window ()
516 "Visit the directory of the file named on this line in other window."
517 (interactive)
518 (find-file-other-window (locate-get-dirname)))
519
520(defun locate-get-dirname ()
521 "Return the directory name of the file mentioned on this line."
522 (let (file (filepos (locate-get-file-positions)))
523 (if (setq file (buffer-substring (nth 0 filepos) (nth 1 filepos)))
524 (progn
525 ;; Get rid of the mouse-face property that file names have.
526 (set-text-properties 0 (length file) nil file)
527 (setq file (file-name-directory file))
528 ;; Unquote names quoted by ls or by dired-insert-directory.
529 ;; Using read to unquote is much faster than substituting
530 ;; \007 (4 chars) -> ^G (1 char) etc. in a lisp loop.
531 (setq file
532 (read
533 (concat "\""
534 ;; some ls -b don't escape quotes, argh!
535 ;; This is not needed for GNU ls, though.
536 (or (dired-string-replace-match
537 "\\([^\\]\\|\\`\\)\"" file "\\1\\\\\"" nil t)
538 file)
539 "\"")))))
540 (and file buffer-file-coding-system
541 (not file-name-coding-system)
542 (setq file (encode-coding-string file buffer-file-coding-system)))
543 file))
544
545;; Only for GNU locate
546(defun locate-in-alternate-database (search-string database)
547 "Run the GNU locate command, using an alternate database."
548 (interactive
549 (list
550 (progn
551 ;; (require 'locate)
552 (read-from-minibuffer "Locate: " nil nil
553 nil 'locate-history-list))
554 (read-file-name "Locate using Database: " )
555 ))
556 (or (file-exists-p database)
557 (error "Database file %s does not exist" database))
558 (let ((locate-make-command-line
559 (function (lambda (string)
560 (cons locate-command
561 (list (concat "--database="
562 (expand-file-name database))
563 string))))))
8348e1f9 564 (locate search-string)))
6aea3b07 565
271a87e8
LT
566(defun locate-do-redisplay (&optional arg test-for-subdir)
567 "Like `dired-do-redisplay', but adapted for `*Locate*' buffers."
568 (interactive "P\np")
569 (if (string= (dired-current-directory) "/")
570 (message "This command only works in subdirectories.")
571 (let ((dired-actual-switches locate-ls-subdir-switches))
572 (dired-do-redisplay arg test-for-subdir))))
573
6aea3b07
RS
574(provide 'locate)
575
ab5796a9 576;;; arch-tag: 60c4d098-b5d5-4b3c-a3e0-51a2e9f43898
6aea3b07 577;;; locate.el ends here