]>
git.saurik.com Git - apple/shell_cmds.git/blob - find/function.c
efa1abe97fc6f604d7713e59967dcd1e0ce71c2d
1 /* $NetBSD: function.c,v 1.24 1998/02/21 22:47:20 christos Exp $ */
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Cimarron D. Taylor of the University of California, Berkeley.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #include <sys/cdefs.h>
42 static char sccsid
[] = "from: @(#)function.c 8.10 (Berkeley) 5/4/95";
44 __RCSID("$NetBSD: function.c,v 1.24 1998/02/21 22:47:20 christos Exp $");
48 #include <sys/param.h>
49 #include <sys/ucred.h>
52 #include <sys/mount.h>
68 #define COMPARE(a, b) { \
69 switch (plan->flags) { \
81 static long find_parsenum
__P((PLAN
*, char *, char *, char *));
82 int f_always_true
__P((PLAN
*, FTSENT
*));
83 int f_atime
__P((PLAN
*, FTSENT
*));
84 int f_ctime
__P((PLAN
*, FTSENT
*));
85 int f_exec
__P((PLAN
*, FTSENT
*));
86 int f_fstype
__P((PLAN
*, FTSENT
*));
87 int f_group
__P((PLAN
*, FTSENT
*));
88 int f_inum
__P((PLAN
*, FTSENT
*));
89 int f_links
__P((PLAN
*, FTSENT
*));
90 int f_ls
__P((PLAN
*, FTSENT
*));
91 int f_mtime
__P((PLAN
*, FTSENT
*));
92 int f_name
__P((PLAN
*, FTSENT
*));
93 int f_newer
__P((PLAN
*, FTSENT
*));
94 int f_nogroup
__P((PLAN
*, FTSENT
*));
95 int f_nouser
__P((PLAN
*, FTSENT
*));
96 int f_path
__P((PLAN
*, FTSENT
*));
97 int f_perm
__P((PLAN
*, FTSENT
*));
98 int f_print
__P((PLAN
*, FTSENT
*));
99 int f_print0
__P((PLAN
*, FTSENT
*));
100 int f_prune
__P((PLAN
*, FTSENT
*));
101 int f_size
__P((PLAN
*, FTSENT
*));
102 int f_type
__P((PLAN
*, FTSENT
*));
103 int f_user
__P((PLAN
*, FTSENT
*));
104 int f_not
__P((PLAN
*, FTSENT
*));
105 int f_or
__P((PLAN
*, FTSENT
*));
106 static PLAN
*palloc
__P((enum ntype
, int (*) __P((PLAN
*, FTSENT
*))));
110 * Parse a string of the form [+-]# and return the value.
113 find_parsenum(plan
, option
, vp
, endch
)
115 char *option
, *vp
, *endch
;
118 char *endchar
, *str
; /* Pointer to character ending conversion. */
120 /* Determine comparison from leading + or -. */
125 plan
->flags
= F_GREATER
;
129 plan
->flags
= F_LESSTHAN
;
132 plan
->flags
= F_EQUAL
;
137 * Convert the string with strtol(). Note, if strtol() returns zero
138 * and endchar points to the beginning of the string we know we have
141 value
= strtol(str
, &endchar
, 10);
142 if (value
== 0 && endchar
== str
)
143 errx(1, "%s: %s: illegal numeric value", option
, vp
);
144 if (endchar
[0] && (endch
== NULL
|| endchar
[0] != *endch
))
145 errx(1, "%s: %s: illegal trailing character", option
, vp
);
152 * The value of n for the inode times (atime, ctime, and mtime) is a range,
153 * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
154 * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
155 * user wanted. Correct so that -1 is "less than 1".
157 #define TIME_CORRECT(p, ttype) \
158 if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \
162 * -atime n functions --
164 * True if the difference between the file access time and the
165 * current time is n 24 hour periods.
174 COMPARE((now
- entry
->fts_statp
->st_atime
+
175 SECSPERDAY
- 1) / SECSPERDAY
, plan
->t_data
);
187 ftsoptions
&= ~FTS_NOSTAT
;
189 new = palloc(N_ATIME
, f_atime
);
190 new->t_data
= find_parsenum(new, "-atime", arg
, NULL
);
191 TIME_CORRECT(new, N_ATIME
);
195 * -ctime n functions --
197 * True if the difference between the last change of file
198 * status information and the current time is n 24 hour periods.
207 COMPARE((now
- entry
->fts_statp
->st_ctime
+
208 SECSPERDAY
- 1) / SECSPERDAY
, plan
->t_data
);
220 ftsoptions
&= ~FTS_NOSTAT
;
222 new = palloc(N_CTIME
, f_ctime
);
223 new->t_data
= find_parsenum(new, "-ctime", arg
, NULL
);
224 TIME_CORRECT(new, N_CTIME
);
229 * -depth functions --
231 * Always true, causes descent of the directory hierarchy to be done
232 * so that all entries in a directory are acted on before the directory
236 f_always_true(plan
, entry
)
250 return (palloc(N_DEPTH
, f_always_true
));
254 * [-exec | -ok] utility [arg ... ] ; functions --
256 * True if the executed utility returns a zero value as exit status.
257 * The end of the primary expression is delimited by a semicolon. If
258 * "{}" occurs anywhere, it gets replaced by the current pathname.
259 * The current directory for the execution of utility is the same as
260 * the current directory when the find utility was started.
262 * The primary -ok is different in that it requests affirmation of the
263 * user before executing the utility.
275 for (cnt
= 0; plan
->e_argv
[cnt
]; ++cnt
)
276 if (plan
->e_len
[cnt
])
277 brace_subst(plan
->e_orig
[cnt
], &plan
->e_argv
[cnt
],
278 entry
->fts_path
, plan
->e_len
[cnt
]);
280 if (plan
->flags
== F_NEEDOK
&& !queryuser(plan
->e_argv
))
283 /* don't mix output of command with find output */
287 switch (pid
= vfork()) {
296 execvp(plan
->e_argv
[0], plan
->e_argv
);
297 warn("%s", plan
->e_argv
[0]);
300 pid
= waitpid(pid
, &status
, 0);
301 return (pid
!= -1 && WIFEXITED(status
) && !WEXITSTATUS(status
));
306 * build three parallel arrays, one with pointers to the strings passed
307 * on the command line, one with (possibly duplicated) pointers to the
308 * argv array, and one with integer values that are lengths of the
309 * strings, but also flags meaning that the string has to be massaged.
316 PLAN
*new; /* node returned */
318 char **argv
, **ap
, *p
;
322 new = palloc(N_EXEC
, f_exec
);
324 new->flags
= F_NEEDOK
;
326 for (ap
= argv
= *argvp
;; ++ap
) {
329 "%s: no terminating \";\"", isok
? "-ok" : "-exec");
334 cnt
= ap
- *argvp
+ 1;
335 new->e_argv
= (char **)emalloc((u_int
)cnt
* sizeof(char *));
336 new->e_orig
= (char **)emalloc((u_int
)cnt
* sizeof(char *));
337 new->e_len
= (int *)emalloc((u_int
)cnt
* sizeof(int));
339 for (argv
= *argvp
, cnt
= 0; argv
< ap
; ++argv
, ++cnt
) {
340 new->e_orig
[cnt
] = *argv
;
341 for (p
= *argv
; *p
; ++p
)
342 if (p
[0] == '{' && p
[1] == '}') {
343 new->e_argv
[cnt
] = emalloc((u_int
)MAXPATHLEN
);
344 new->e_len
[cnt
] = MAXPATHLEN
;
348 new->e_argv
[cnt
] = *argv
;
352 new->e_argv
[cnt
] = new->e_orig
[cnt
] = NULL
;
359 * -follow functions --
361 * Always true, causes symbolic links to be followed on a global
365 c_follow(argvp
, isok
)
369 ftsoptions
&= ~FTS_PHYSICAL
;
370 ftsoptions
|= FTS_LOGICAL
;
372 return (palloc(N_FOLLOW
, f_always_true
));
376 * -fstype functions --
378 * True if the file is of a certain type.
381 f_fstype(plan
, entry
)
385 static dev_t curdev
; /* need a guaranteed illegal dev value */
386 static int first
= 1;
389 static char fstype
[MFSNAMELEN
];
392 /* Only check when we cross mount point. */
393 if (first
|| curdev
!= entry
->fts_statp
->st_dev
) {
394 curdev
= entry
->fts_statp
->st_dev
;
397 * Statfs follows symlinks; find wants the link's file system,
398 * not where it points.
400 if (entry
->fts_info
== FTS_SL
||
401 entry
->fts_info
== FTS_SLNONE
) {
402 if ((p
= strrchr(entry
->fts_accpath
, '/')) != NULL
)
405 p
= entry
->fts_accpath
;
414 if (statfs(entry
->fts_accpath
, &sb
))
415 err(1, "%s", entry
->fts_accpath
);
425 * Further tests may need both of these values, so
426 * always copy both of them.
429 strncpy(fstype
, sb
.f_fstypename
, MFSNAMELEN
);
431 switch (plan
->flags
) {
433 return (val
& plan
->mt_data
);
435 return (strncmp(fstype
, plan
->c_data
, MFSNAMELEN
) == 0);
442 c_fstype(argvp
, isok
)
450 ftsoptions
&= ~FTS_NOSTAT
;
452 new = palloc(N_FSTYPE
, f_fstype
);
456 if (!strcmp(arg
, "local")) {
457 new->flags
= F_MTFLAG
;
458 new->mt_data
= MNT_LOCAL
;
463 if (!strcmp(arg
, "rdonly")) {
464 new->flags
= F_MTFLAG
;
465 new->mt_data
= MNT_RDONLY
;
471 new->flags
= F_MTTYPE
;
477 * -group gname functions --
479 * True if the file belongs to the group gname. If gname is numeric and
480 * an equivalent of the getgrnam() function does not return a valid group
481 * name, gname is taken as a group ID.
488 return (entry
->fts_statp
->st_gid
== plan
->g_data
);
496 char *gname
= **argvp
;
502 ftsoptions
&= ~FTS_NOSTAT
;
507 if (gid
== 0 && gname
[0] != '0')
508 errx(1, "-group: %s: no such group", gname
);
512 new = palloc(N_GROUP
, f_group
);
518 * -inum n functions --
520 * True if the file has inode # n.
527 COMPARE(entry
->fts_statp
->st_ino
, plan
->i_data
);
539 ftsoptions
&= ~FTS_NOSTAT
;
541 new = palloc(N_INUM
, f_inum
);
542 new->i_data
= find_parsenum(new, "-inum", arg
, NULL
);
547 * -links n functions --
549 * True if the file has n links.
556 COMPARE(entry
->fts_statp
->st_nlink
, plan
->l_data
);
568 ftsoptions
&= ~FTS_NOSTAT
;
570 new = palloc(N_LINKS
, f_links
);
571 new->l_data
= (nlink_t
)find_parsenum(new, "-links", arg
, NULL
);
578 * Always true - prints the current entry to stdout in "ls" format.
585 printlong(entry
->fts_path
, entry
->fts_accpath
, entry
->fts_statp
);
594 ftsoptions
&= ~FTS_NOSTAT
;
597 return (palloc(N_LS
, f_ls
));
601 * -mtime n functions --
603 * True if the difference between the file modification time and the
604 * current time is n 24 hour periods.
613 COMPARE((now
- entry
->fts_statp
->st_mtime
+ SECSPERDAY
- 1) /
614 SECSPERDAY
, plan
->t_data
);
626 ftsoptions
&= ~FTS_NOSTAT
;
628 new = palloc(N_MTIME
, f_mtime
);
629 new->t_data
= find_parsenum(new, "-mtime", arg
, NULL
);
630 TIME_CORRECT(new, N_MTIME
);
637 * True if the basename of the filename being examined
638 * matches pattern using Pattern Matching Notation S3.14
645 return (!fnmatch(plan
->c_data
, entry
->fts_name
, 0));
653 char *pattern
= **argvp
;
657 new = palloc(N_NAME
, f_name
);
658 new->c_data
= pattern
;
663 * -newer file functions --
665 * True if the current file has been modified more recently
666 * then the modification time of the file named by the pathname
674 return (entry
->fts_statp
->st_mtime
> plan
->t_data
);
682 char *filename
= **argvp
;
687 ftsoptions
&= ~FTS_NOSTAT
;
689 if (stat(filename
, &sb
))
690 err(1, "%s", filename
);
691 new = palloc(N_NEWER
, f_newer
);
692 new->t_data
= sb
.st_mtime
;
697 * -nogroup functions --
699 * True if file belongs to a user ID for which the equivalent
700 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
703 f_nogroup(plan
, entry
)
708 return (group_from_gid(entry
->fts_statp
->st_gid
, 1) ? 0 : 1);
712 c_nogroup(argvp
, isok
)
716 ftsoptions
&= ~FTS_NOSTAT
;
718 return (palloc(N_NOGROUP
, f_nogroup
));
722 * -nouser functions --
724 * True if file belongs to a user ID for which the equivalent
725 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
728 f_nouser(plan
, entry
)
733 return (user_from_uid(entry
->fts_statp
->st_uid
, 1) ? 0 : 1);
737 c_nouser(argvp
, isok
)
741 ftsoptions
&= ~FTS_NOSTAT
;
743 return (palloc(N_NOUSER
, f_nouser
));
749 * True if the path of the filename being examined
750 * matches pattern using Pattern Matching Notation S3.14
757 return (!fnmatch(plan
->c_data
, entry
->fts_path
, 0));
765 char *pattern
= **argvp
;
769 new = palloc(N_NAME
, f_path
);
770 new->c_data
= pattern
;
777 * The mode argument is used to represent file mode bits. If it starts
778 * with a leading digit, it's treated as an octal mode, otherwise as a
788 mode
= entry
->fts_statp
->st_mode
&
789 (S_ISUID
|S_ISGID
|S_ISTXT
|S_IRWXU
|S_IRWXG
|S_IRWXO
);
790 if (plan
->flags
== F_ATLEAST
)
791 return ((plan
->m_data
| mode
) == mode
);
793 return (mode
== plan
->m_data
);
802 char *perm
= **argvp
;
807 ftsoptions
&= ~FTS_NOSTAT
;
809 new = palloc(N_PERM
, f_perm
);
812 new->flags
= F_ATLEAST
;
816 if ((set
= setmode(perm
)) == NULL
)
817 err(1, "-perm: %s: illegal mode string", perm
);
819 new->m_data
= getmode(set
, 0);
824 * -print functions --
826 * Always true, causes the current pathame to be written to
834 (void)printf("%s\n", entry
->fts_path
);
839 f_print0(plan
, entry
)
843 (void)fputs(entry
->fts_path
, stdout
);
844 (void)fputc('\0', stdout
);
855 return (palloc(N_PRINT
, f_print
));
859 c_print0(argvp
, isok
)
865 return (palloc(N_PRINT0
, f_print0
));
869 * -prune functions --
871 * Prune a portion of the hierarchy.
880 if (fts_set(tree
, entry
, FTS_SKIP
))
881 err(1, "%s", entry
->fts_path
);
890 return (palloc(N_PRUNE
, f_prune
));
894 * -size n[c] functions --
896 * True if the file size in bytes, divided by an implementation defined
897 * value and rounded up to the next integer, is n. If n is followed by
898 * a c, the size is in bytes.
900 #define FIND_SIZE 512
901 static int divsize
= 1;
910 size
= divsize
? (entry
->fts_statp
->st_size
+ FIND_SIZE
- 1) /
911 FIND_SIZE
: entry
->fts_statp
->st_size
;
912 COMPARE(size
, plan
->o_data
);
925 ftsoptions
&= ~FTS_NOSTAT
;
927 new = palloc(N_SIZE
, f_size
);
929 new->o_data
= find_parsenum(new, "-size", arg
, &endch
);
936 * -type c functions --
938 * True if the type of the file is c, where c is b, c, d, p, f or w
939 * for block special file, character special file, directory, FIFO,
940 * regular file or whiteout respectively.
947 return ((entry
->fts_statp
->st_mode
& S_IFMT
) == plan
->m_data
);
955 char *typestring
= **argvp
;
957 mode_t mask
= (mode_t
)0;
960 ftsoptions
&= ~FTS_NOSTAT
;
962 switch (typestring
[0]) {
966 ftsoptions
|= FTS_WHITEOUT
;
995 ftsoptions
|= FTS_WHITEOUT
;
997 #endif /* FTS_WHITEOUT */
999 errx(1, "-type: %s: unknown type", typestring
);
1002 new = palloc(N_TYPE
, f_type
);
1008 * -user uname functions --
1010 * True if the file belongs to the user uname. If uname is numeric and
1011 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1012 * return a valid user name, uname is taken as a user ID.
1019 return (entry
->fts_statp
->st_uid
== plan
->u_data
);
1027 char *username
= **argvp
;
1033 ftsoptions
&= ~FTS_NOSTAT
;
1035 p
= getpwnam(username
);
1037 uid
= atoi(username
);
1038 if (uid
== 0 && username
[0] != '0')
1039 errx(1, "-user: %s: no such user", username
);
1043 new = palloc(N_USER
, f_user
);
1049 * -xdev functions --
1051 * Always true, causes find not to decend past directories that have a
1052 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1059 ftsoptions
|= FTS_XDEV
;
1061 return (palloc(N_XDEV
, f_always_true
));
1065 * ( expression ) functions --
1067 * True if expression is true.
1078 for (p
= plan
->p_data
[0];
1079 p
&& (state
= (p
->eval
)(p
, entry
)); p
= p
->next
);
1084 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1085 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1086 * to a N_EXPR node containing the expression and the ')' node is discarded.
1089 c_openparen(argvp
, isok
)
1093 return (palloc(N_OPENPAREN
, (int (*) __P((PLAN
*, FTSENT
*)))-1));
1097 c_closeparen(argvp
, isok
)
1101 return (palloc(N_CLOSEPAREN
, (int (*) __P((PLAN
*, FTSENT
*)))-1));
1105 * ! expression functions --
1107 * Negation of a primary; the unary NOT operator.
1118 for (p
= plan
->p_data
[0];
1119 p
&& (state
= (p
->eval
)(p
, entry
)); p
= p
->next
);
1128 return (palloc(N_NOT
, f_not
));
1132 * expression -o expression functions --
1134 * Alternation of primaries; the OR operator. The second expression is
1135 * not evaluated if the first expression is true.
1146 for (p
= plan
->p_data
[0];
1147 p
&& (state
= (p
->eval
)(p
, entry
)); p
= p
->next
);
1152 for (p
= plan
->p_data
[1];
1153 p
&& (state
= (p
->eval
)(p
, entry
)); p
= p
->next
);
1162 return (palloc(N_OR
, f_or
));
1176 int (*f
) __P((PLAN
*, FTSENT
*));
1180 if ((new = malloc(sizeof(PLAN
))) == NULL
)