delete_temp_file fix
[bpt/emacs.git] / lisp / find-cmd.el
CommitLineData
6dfcbe31
SM
1;;; find-cmd.el --- Build a valid find(1) command with sexps
2
ba318903 3;; Copyright (C) 2008-2014 Free Software Foundation, Inc.
6dfcbe31
SM
4
5;; Author: Philip Jackson <phil@shellarchive.co.uk>
6;; Version: 0.6
7
8;; This file is part of GNU Emacs.
9
eb3fa2cf 10;; GNU Emacs is free software: you can redistribute it and/or modify
ae2a7459 11;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
6dfcbe31 14
ae2a7459
GM
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.
6dfcbe31
SM
19
20;; You should have received a copy of the GNU General Public License
eb3fa2cf 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
6dfcbe31
SM
22
23;;; Commentary:
24
25;; With this module you can build up a (hopefully) valid find(1)
9762b219 26;; string ready for the command line. For example:
6dfcbe31
SM
27
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 40;;; Code:
6dfcbe31
SM
41
42(defconst find-constituents
43 '((and . find-and)
44 (not . find-not)
45 (or . find-or)
46
47 (a . find-and)
48 (n . find-not)
49 (o . find-or)
50
51 (prune . find-prune)
52
53 ;; switches
54 (L . (0))
55 (P . (0))
56 (H . (0))
57
58 ;; generic tests
59 (amin . (1))
60 (anewer . (1))
61 (atime . (1))
62 (cmin . (1))
63 (cnewer . (1))
64 (ctime . (1))
65 (empty . (0))
930a6273 66 (executable . (0))
6dfcbe31
SM
67 (false . (0))
68 (fstype . (1))
69 (gid . (1))
70 (group . (1))
71 (ilname . (1))
72 (iname . (1))
73 (inum . (1))
930a6273 74 (ipath . (1))
6dfcbe31 75 (iregex . (1))
930a6273 76 (iwholename . (1))
6dfcbe31
SM
77 (links . (1))
78 (lname . (1))
79 (mmin . (1))
80 (mtime . (1))
81 (name . (1))
82 (newer . (1))
6dfcbe31 83 (nogroup . (0))
930a6273 84 (nouser . (0))
6dfcbe31
SM
85 (path . (1))
86 (perm . (0))
930a6273 87 (readable . (0))
6dfcbe31 88 (regex . (1))
930a6273 89 (samefile . (1))
6dfcbe31
SM
90 (size . (1))
91 (true . (0))
92 (type . (1))
93 (uid . (1))
94 (used . (1))
95 (user . (1))
930a6273
CY
96 (wholename . (1))
97 (writable . (0))
6dfcbe31
SM
98 (xtype . (nil))
99
100 ;; normal options (always true)
930a6273 101 (daystart . (0))
6dfcbe31
SM
102 (depth . (0))
103 (maxdepth . (1))
104 (mindepth . (1))
105 (mount . (0))
106 (noleaf . (0))
6dfcbe31
SM
107 (ignore_readdir_race . (0))
108 (noignore_readdir_race . (0))
930a6273
CY
109 (regextype . (1))
110 (xdev . (0))
6dfcbe31
SM
111
112 ;; actions
113 (delete . (0))
114 (print0 . (0))
115 (printf . (1))
116 (fprintf . (2))
117 (print . (0))
118 (fprint0 . (1))
119 (fprint . (1))
120 (ls . (0))
121 (fls . (1))
122 (prune . (0))
123 (quit . (0))
124
125 ;; these need to be terminated with a ;
126 (exec . (1 find-command t))
127 (ok . (1 find-command t))
128 (execdir . (1 find-command t))
129 (okdir . (1 find-command t)))
9762b219
JB
130 "Holds details of each of the find options.
131The car of each alist is the name. The cdr is minimum args, the
d1f18ec0 132function used to join many occurrences of the argument together,
9762b219
JB
133and whether or not to leave quotes off the string (non-nil means
134the string will be quoted).")
6dfcbe31
SM
135
136;;;###autoload
137(defun find-cmd (&rest subfinds)
9762b219
JB
138 "Initiate the building of a find command.
139For example:
6dfcbe31
SM
140
141\(find-cmd '\(prune \(name \".svn\" \".git\" \".CVS\"\)\)
142 '\(and \(or \(name \"*.pl\" \"*.pm\" \"*.t\"\)
143 \(mtime \"+1\"\)\)
144 \(fstype \"nfs\" \"ufs\"\)\)\)\)
145
9762b219 146`default-directory' is used as the initial search path. The
6dfcbe31
SM
147result is a string that should be ready for the command line."
148 (concat
149 "find " (shell-quote-argument (expand-file-name default-directory)) " "
150 (cond
151 ((cdr subfinds)
152 (mapconcat 'find-to-string subfinds ""))
153 (t
154 (find-to-string (car subfinds))))))
155
156(defun find-and (form)
157 "And FORMs together, so:
158 \(and \(mtime \"+1\"\) \(name \"something\"\)\)
159will produce:
160 find . \\\( -mtime '+1' -and -name 'something' \\\)"
161 (if (< (length form) 2)
162 (find-to-string (car form))
163 (concat "\\( "
164 (mapconcat 'find-to-string form "-and ")
165 "\\) ")))
166
167(defun find-or (form)
168 "Or FORMs together, so:
169 \(or \(mtime \"+1\"\) \(name \"something\"\)\)
170will produce:
171 find . \\\( -mtime '+1' -or -name 'something' \\\)"
172 (if (< (length form) 2)
173 (find-to-string (car form))
174 (concat "\\( "
175 (mapconcat 'find-to-string form "-or ")
176 "\\) ")))
177
178(defun find-not (form)
179 "Or FORMs together and prefix with a -not, so:
180 \(not \(mtime \"+1\"\) \(name \"something\"\)\)
181will produce:
182 -not \\\( -mtime '+1' -or -name 'something' \\\)
183If you wanted the FORMs -and(ed) together instead then this would
184suffice:
185 \(not \(and \(mtime \"+1\"\) \(name \"something\"\)\)\)"
186 (concat "-not " (find-or (mapcar 'find-to-string form))))
187
188(defun find-prune (form)
9762b219 189 "-or together FORMs postfix '-prune' and then -or that with a
6dfcbe31
SM
190-true, so:
191 \(prune \(name \".svn\" \".git\"\)\) \(name \"*.pm\"\)
192will produce (unwrapped):
193 \\\( \\\( \\\( -name '.svn' -or -name '.git' \\\) /
194 -prune -or -true \\\) -and -name '*.pm' \\\)"
195 (find-or
196 (list
197 (concat (find-or (mapcar 'find-to-string form)) (find-generic "prune"))
198 (find-generic "true"))))
199
200(defun find-generic (option &optional oper argcount args dont-quote)
9762b219
JB
201 "Allow an arbitrary string to be used as a form.
202OPTION is the name of the form, OPER is the function used to either
203OR or AND multiple results together. ARGCOUNT is the minimum of
204args that OPTION can receive and ARGS are the arguments for OPTION.
205If DONT-QUOTE is non-nil, arguments are quoted for passing them to
206the shell."
6dfcbe31
SM
207 (when (and (numberp argcount) (< (length args) argcount))
208 (error "'%s' needs at least %d arguments" option argcount))
209 (let ((oper (or oper 'find-or)))
210 (if (and args (length args))
211 (funcall oper (mapcar (lambda (x)
212 (concat "-" option
213 (if dont-quote
214 (concat " " x " ")
215 (concat " "
216 (shell-quote-argument x)
217 " "))))
218 args))
219 (concat "-" option " "))))
220
221(defun find-command (form)
222 "For each item in FORM add a terminating semi-colon and turn
9762b219 223them into valid switches. The result is -and(ed) together."
6dfcbe31
SM
224 (find-and (mapcar (lambda (x)
225 (concat (find-to-string x) "\\; "))
226 form)))
227
228(defun find-to-string (form)
229 "Parse FORM to produce a set of valid find arguments."
230 (cond
231 ((stringp form)
232 form)
233 ((consp form)
234 (let ((option (cdr (assoc (car form) find-constituents))))
235 (cond
236 ((and (symbolp option) (fboundp option))
237 (funcall option (cdr form)))
238 ((consp option)
239 (let ((option (symbol-name (car form)))
240 (argcnt (car option))
241 (oper (cadr option))
242 (dont-quote (car (cddr option))))
243 (find-to-string
244 (find-generic option oper argcnt (cdr form) dont-quote))))
245 (t
246 (error "Sorry I don't know how to handle '%s'" (car form))))))))
247
248(provide 'find-cmd)
5bd8042b 249
5bd8042b 250;;; find-cmd.el ends here