Minor documentation and NEWS tweak
[emacs.git] / lisp / find-cmd.el
Commit [+]AuthorDateLineData
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +00001;;; find-cmd.el --- Build a valid find(1) command with sexps
2
7e09ef09 Paul Eggert2015-01-01 14:26:41 -08003;; Copyright (C) 2008-2015 Free Software Foundation, Inc.
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +00004
5;; Author: Philip Jackson <phil@shellarchive.co.uk>
6;; Version: 0.6
7
8;; This file is part of GNU Emacs.
9
eb3fa2cf Glenn Morris2008-05-06 08:06:51 +000010;; GNU Emacs is free software: you can redistribute it and/or modify
ae2a7459 Glenn Morris2008-04-29 07:01:01 +000011;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
Glenn Morris2008-05-06 08:06:51 +000012;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +000014
ae2a7459
GM
Glenn Morris2008-04-29 07:01:01 +000015;; 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.
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +000019
20;; You should have received a copy of the GNU General Public License
eb3fa2cf Glenn Morris2008-05-06 08:06:51 +000021;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +000022
23;;; Commentary:
24
25;; With this module you can build up a (hopefully) valid find(1)
9762b219 Juanma Barranquero2009-12-17 02:06:31 +000026;; string ready for the command line. For example:
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +000027
28;; (find-cmd '(prune (name ".svn" ".git" ".CVS"))
29;; '(and (or (name "*.pl" "*.pm" "*.t")
30;; (mtime "+1"))
31;; (fstype "nfs" "ufs"))))
32
33;; will become (un-wrapped):
34
35;; "find '/home/phil/' \\( \\( -name '.svn' -or -name '.git' -or
36;; -name '.CVS' \\) -prune -or -true \\) \\( \\( \\( -name '*.pl'
37;; -or -name '*.pm' -or -name '*.t' \\) -or -mtime '+1' \\) -and \\(
38;; -fstype 'nfs' -or -fstype 'ufs' \\) \\)"
39
5bd8042b Glenn Morris2008-04-29 07:05:39 +000040;;; Code:
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +000041
0e176389
SM
Stefan Monnier2014-09-24 15:23:13 -040042(require 'grep)
43
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +000044(defconst find-constituents
45 '((and . find-and)
46 (not . find-not)
47 (or . find-or)
48
49 (a . find-and)
50 (n . find-not)
51 (o . find-or)
52
53 (prune . find-prune)
54
55 ;; switches
56 (L . (0))
57 (P . (0))
58 (H . (0))
59
60 ;; generic tests
61 (amin . (1))
62 (anewer . (1))
63 (atime . (1))
64 (cmin . (1))
65 (cnewer . (1))
66 (ctime . (1))
67 (empty . (0))
930a6273 Chong Yidong2012-11-23 16:33:20 +080068 (executable . (0))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +000069 (false . (0))
70 (fstype . (1))
71 (gid . (1))
72 (group . (1))
73 (ilname . (1))
74 (iname . (1))
75 (inum . (1))
930a6273 Chong Yidong2012-11-23 16:33:20 +080076 (ipath . (1))
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +000077 (iregex . (1))
930a6273 Chong Yidong2012-11-23 16:33:20 +080078 (iwholename . (1))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +000079 (links . (1))
80 (lname . (1))
81 (mmin . (1))
82 (mtime . (1))
83 (name . (1))
84 (newer . (1))
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +000085 (nogroup . (0))
930a6273 Chong Yidong2012-11-23 16:33:20 +080086 (nouser . (0))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +000087 (path . (1))
88 (perm . (0))
930a6273 Chong Yidong2012-11-23 16:33:20 +080089 (readable . (0))
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +000090 (regex . (1))
930a6273 Chong Yidong2012-11-23 16:33:20 +080091 (samefile . (1))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +000092 (size . (1))
93 (true . (0))
94 (type . (1))
95 (uid . (1))
96 (used . (1))
97 (user . (1))
930a6273
CY
Chong Yidong2012-11-23 16:33:20 +080098 (wholename . (1))
99 (writable . (0))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000100 (xtype . (nil))
101
102 ;; normal options (always true)
930a6273 Chong Yidong2012-11-23 16:33:20 +0800103 (daystart . (0))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000104 (depth . (0))
105 (maxdepth . (1))
106 (mindepth . (1))
107 (mount . (0))
108 (noleaf . (0))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000109 (ignore_readdir_race . (0))
110 (noignore_readdir_race . (0))
930a6273
CY
Chong Yidong2012-11-23 16:33:20 +0800111 (regextype . (1))
112 (xdev . (0))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000113
114 ;; actions
115 (delete . (0))
116 (print0 . (0))
117 (printf . (1))
118 (fprintf . (2))
119 (print . (0))
120 (fprint0 . (1))
121 (fprint . (1))
122 (ls . (0))
123 (fls . (1))
124 (prune . (0))
125 (quit . (0))
126
127 ;; these need to be terminated with a ;
128 (exec . (1 find-command t))
129 (ok . (1 find-command t))
130 (execdir . (1 find-command t))
131 (okdir . (1 find-command t)))
9762b219
JB
Juanma Barranquero2009-12-17 02:06:31 +0000132 "Holds details of each of the find options.
133The car of each alist is the name. The cdr is minimum args, the
d1f18ec0 Juanma Barranquero2010-01-14 19:59:31 +0100134function used to join many occurrences of the argument together,
9762b219
JB
Juanma Barranquero2009-12-17 02:06:31 +0000135and whether or not to leave quotes off the string (non-nil means
136the string will be quoted).")
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000137
138;;;###autoload
139(defun find-cmd (&rest subfinds)
9762b219
JB
Juanma Barranquero2009-12-17 02:06:31 +0000140 "Initiate the building of a find command.
141For example:
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +0000142
875a5d0e
PE
Paul Eggert2015-08-24 23:39:33 -0700143\(find-cmd \\='\(prune \(name \".svn\" \".git\" \".CVS\"\)\)
144 \\='\(and \(or \(name \"*.pl\" \"*.pm\" \"*.t\"\)
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000145 \(mtime \"+1\"\)\)
146 \(fstype \"nfs\" \"ufs\"\)\)\)\)
147
9762b219 Juanma Barranquero2009-12-17 02:06:31 +0000148`default-directory' is used as the initial search path. The
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +0000149result is a string that should be ready for the command line."
0e176389
SM
Stefan Monnier2014-09-24 15:23:13 -0400150 ;; FIXME: Provide a version that returns a list of strings (ready to pass to
151 ;; call-process).
152 (concat find-program " "
153 (shell-quote-argument (expand-file-name default-directory)) " "
154 (cond
155 ((cdr subfinds)
156 (mapconcat #'find-to-string subfinds ""))
157 (t
158 (find-to-string (car subfinds))))))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000159
160(defun find-and (form)
161 "And FORMs together, so:
162 \(and \(mtime \"+1\"\) \(name \"something\"\)\)
163will produce:
875a5d0e Paul Eggert2015-08-24 23:39:33 -0700164 find . \\\( -mtime +1 -and -name something \\\)"
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000165 (if (< (length form) 2)
166 (find-to-string (car form))
167 (concat "\\( "
0e176389 Stefan Monnier2014-09-24 15:23:13 -0400168 (mapconcat #'find-to-string form "-and ")
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000169 "\\) ")))
170
171(defun find-or (form)
172 "Or FORMs together, so:
173 \(or \(mtime \"+1\"\) \(name \"something\"\)\)
174will produce:
875a5d0e Paul Eggert2015-08-24 23:39:33 -0700175 find . \\\( -mtime +1 -or -name something \\\)"
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000176 (if (< (length form) 2)
177 (find-to-string (car form))
178 (concat "\\( "
0e176389 Stefan Monnier2014-09-24 15:23:13 -0400179 (mapconcat #'find-to-string form "-or ")
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000180 "\\) ")))
181
182(defun find-not (form)
183 "Or FORMs together and prefix with a -not, so:
184 \(not \(mtime \"+1\"\) \(name \"something\"\)\)
185will produce:
875a5d0e Paul Eggert2015-08-24 23:39:33 -0700186 -not \\\( -mtime +1 -or -name something \\\)
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000187If you wanted the FORMs -and(ed) together instead then this would
188suffice:
189 \(not \(and \(mtime \"+1\"\) \(name \"something\"\)\)\)"
0e176389 Stefan Monnier2014-09-24 15:23:13 -0400190 (concat "-not " (find-or (mapcar #'find-to-string form))))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000191
192(defun find-prune (form)
875a5d0e Paul Eggert2015-08-24 23:39:33 -0700193 "-or together FORMs postfix `-prune' and then -or that with a
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +0000194-true, so:
875a5d0e Paul Eggert2015-08-24 23:39:33 -0700195 \(\(prune \(name \".svn\" \".git\"\)\) \(name \"*.pm\"\)\)
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +0000196will produce (unwrapped):
875a5d0e
PE
Paul Eggert2015-08-24 23:39:33 -0700197 \\\( \\\( \\\( -name .svn -or -name .git \\\) /
198 -prune -or -true \\\) -and -name *.pm \\\)"
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000199 (find-or
200 (list
0e176389 Stefan Monnier2014-09-24 15:23:13 -0400201 (concat (find-or (mapcar #'find-to-string form)) (find-generic "prune"))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000202 (find-generic "true"))))
203
204(defun find-generic (option &optional oper argcount args dont-quote)
9762b219
JB
Juanma Barranquero2009-12-17 02:06:31 +0000205 "Allow an arbitrary string to be used as a form.
206OPTION is the name of the form, OPER is the function used to either
207OR or AND multiple results together. ARGCOUNT is the minimum of
208args that OPTION can receive and ARGS are the arguments for OPTION.
209If DONT-QUOTE is non-nil, arguments are quoted for passing them to
210the shell."
6dfcbe31 Stefan Monnier2008-04-29 06:09:32 +0000211 (when (and (numberp argcount) (< (length args) argcount))
875a5d0e Paul Eggert2015-08-24 23:39:33 -0700212 (error "ā€˜%sā€™ needs at least %d arguments" option argcount))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000213 (let ((oper (or oper 'find-or)))
214 (if (and args (length args))
215 (funcall oper (mapcar (lambda (x)
216 (concat "-" option
217 (if dont-quote
218 (concat " " x " ")
219 (concat " "
220 (shell-quote-argument x)
221 " "))))
222 args))
223 (concat "-" option " "))))
224
225(defun find-command (form)
226 "For each item in FORM add a terminating semi-colon and turn
9762b219 Juanma Barranquero2009-12-17 02:06:31 +0000227them into valid switches. The result is -and(ed) together."
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000228 (find-and (mapcar (lambda (x)
229 (concat (find-to-string x) "\\; "))
230 form)))
231
232(defun find-to-string (form)
233 "Parse FORM to produce a set of valid find arguments."
234 (cond
235 ((stringp form)
236 form)
237 ((consp form)
238 (let ((option (cdr (assoc (car form) find-constituents))))
239 (cond
240 ((and (symbolp option) (fboundp option))
241 (funcall option (cdr form)))
242 ((consp option)
243 (let ((option (symbol-name (car form)))
244 (argcnt (car option))
245 (oper (cadr option))
246 (dont-quote (car (cddr option))))
247 (find-to-string
248 (find-generic option oper argcnt (cdr form) dont-quote))))
249 (t
875a5d0e Paul Eggert2015-08-24 23:39:33 -0700250 (error "Sorry I don't know how to handle ā€˜%sā€™" (car form))))))))
6dfcbe31
SM
Stefan Monnier2008-04-29 06:09:32 +0000251
252(provide 'find-cmd)
5bd8042b Glenn Morris2008-04-29 07:05:39 +0000253
5bd8042b Glenn Morris2008-04-29 07:05:39 +0000254;;; find-cmd.el ends here