1 /* find - look for files satisfying a predicate Author: E. Baalbergen */
3 /* Original author: Erik Baalbergen; POSIX compliant version: Bert Laverman */
19 /*######################## DEFINITIONS ##############################*/
27 #define SHELL "/bin/sh"
28 #define MAXARG 256 /* maximum length for an argv for -exec */
29 #define BSIZE 512 /* POSIX wants 512 byte blocks */
30 #define SECS_PER_DAY (24L*60L*60L) /* check your planet */
32 #define OP_NAME 1 /* match name */
33 #define OP_PERM 2 /* check file permission bits */
34 #define OP_TYPE 3 /* check file type bits */
35 #define OP_LINKS 4 /* check link count */
36 #define OP_USER 5 /* check owner */
37 #define OP_GROUP 6 /* check group ownership */
38 #define OP_SIZE 7 /* check size, blocks or bytes */
39 #define OP_SIZEC 8 /* this is a fake for -size with 'c' */
40 #define OP_INUM 9 /* compare inode number */
41 #define OP_ATIME 10 /* check last access time */
42 #define OP_CTIME 11 /* check creation time */
43 #define OP_MTIME 12 /* check last modification time */
44 #define OP_EXEC 13 /* execute command */
45 #define OP_OK 14 /* execute with confirmation */
46 #define OP_PRINT 15 /* print name */
47 #define OP_PRINT0 16 /* print name null terminated */
48 #define OP_NEWER 17 /* compare modification times */
49 #define OP_CNEWER 18 /* compare modification times */
50 #define OP_AND 19 /* logical and (short circuit) */
51 #define OP_OR 20 /* logical or (short circuit) */
52 #define OP_XDEV 21 /* do not cross file-system boundaries */
53 #define OP_DEPTH 22 /* descend directory before testing */
54 #define OP_PRUNE 23 /* don't descend into current directory */
55 #define OP_NOUSER 24 /* check validity of user id */
56 #define OP_NOGROUP 25 /* check validity of group id */
57 #define LPAR 26 /* left parenthesis */
58 #define RPAR 27 /* right parenthesis */
59 #define NOT 28 /* logical not */
61 /* Some return values: */
62 #define EOI -1 /* end of expression */
63 #define NONE 0 /* not a valid predicate */
65 /* For -perm with symbolic modes: */
66 #define ISWHO(c) ((c == 'u') || (c == 'g') || (c == 'o') || (c == 'a'))
67 #define ISOPER(c) ((c == '-') || (c == '=') || (c == '+'))
68 #define ISMODE(c) ((c == 'r') || (c == 'w') || (c == 'x') || \
69 (c == 's') || (c == 't'))
81 int n_type
; /* any OP_ or NOT */
90 struct node
*n_left
, *n_right
;
170 "nogroup", OP_NOGROUP
178 char **ipp
; /* pointer to next argument during parsing */
179 char *prog
; /* program name (== argv [0]) */
180 char *epath
; /* value of PATH environment string */
181 long current_time
; /* for computing age */
182 int tty
; /* fd for /dev/tty when using -ok */
183 int xdev_flag
= 0; /* cross device boundaries? */
184 int devnr
; /* device nr of first inode */
185 int depth_flag
= 0; /* descend before check? */
186 int prune_here
; /* This is Baaaad! Don't ever do this again! */
187 int um
; /* current umask() */
188 int needprint
= 1; /* implicit -print needed? */
191 /* The prototypes: */
192 _PROTOTYPE(int main
, (int argc
, char **argv
));
193 _PROTOTYPE(char *Malloc
, (int n
));
194 _PROTOTYPE(char *Salloc
, (char *s
));
195 _PROTOTYPE(void find
, (char *path
, struct node
* pred
, char *last
));
196 _PROTOTYPE(int check
, (char *path
, struct stat
* st
, struct node
* n
, char *last
));
197 _PROTOTYPE(int ichk
, (long val
, struct node
* n
));
198 _PROTOTYPE(int lex
, (char *str
));
199 _PROTOTYPE(struct node
* newnode
, (int t
));
200 _PROTOTYPE(int isnumber
, (char *str
, int base
, int sign
));
201 _PROTOTYPE(void number
, (char *str
, int base
, long *pl
, int *ps
));
202 _PROTOTYPE(void fmode
, (char *str
, long *pl
, int *ps
));
203 _PROTOTYPE(struct node
* expr
, (int t
));
204 _PROTOTYPE(struct node
* primary
, (int t
));
205 _PROTOTYPE(struct node
* secondary
, (int t
));
206 _PROTOTYPE(void checkarg
, (char *arg
));
207 _PROTOTYPE(struct node
* simple
, (int t
));
208 _PROTOTYPE(void nonfatal
, (char *s1
, char *s2
));
209 _PROTOTYPE(void fatal
, (char *s1
, char *s2
));
210 _PROTOTYPE(int smatch
, (char *s
, char *t
));
211 _PROTOTYPE(char *find_bin
, (char *s
));
212 _PROTOTYPE(int execute
, (int op
, struct exec
* e
, char *path
));
213 _PROTOTYPE(void domode
, (int op
, int *mode
, int bits
));
216 /* Malloc: a certified malloc */
222 if ((m
= (char *) malloc(n
)) == (char *) NULL
) fatal("out of memory", "");
226 /* Salloc: allocate space for a string */
230 return strcpy(Malloc(strlen(s
) + 1), s
);
234 /* Main: the main body */
239 char **pathlist
, *path
, *last
;
243 prog
= *argv
++; /* set program name (for diagnostics) */
244 if ((epath
= getenv("PATH")) == (char *) NULL
)
245 fatal("Can't get path from environment", "");
246 (void) umask(um
= umask(0)); /* non-destructive get-umask :-) */
247 time(¤t_time
); /* get current time */
250 while (--argc
> 0 && lex(*argv
) == NONE
) { /* find paths */
254 if (pathcnt
== 0) /* there must be at least one path */
255 fatal("Usage: path-list [predicate-list]", "");
257 ipp
= argv
; /* prepare for parsing */
258 if (argc
!= 0) { /* If there is anything to parse, */
259 pred
= expr(lex(*ipp
)); /* then do so */
260 if (lex(*++ipp
) != EOI
) /* Make sure there's nothing left */
261 fatal("syntax error: garbage at end of predicate", "");
262 } else /* No predicate list */
263 pred
= (struct node
*) NULL
;
265 for (i
= 0; i
< pathcnt
; i
++) {
266 if (xdev_flag
) xdev_flag
= 2;
268 if ((last
= strrchr(path
, '/')) == NULL
) last
= path
; else last
++;
269 find(path
, pred
, last
);
274 void find(path
, pred
, last
)
278 char spath
[PATH_MAX
];
279 register char *send
= spath
, *p
;
284 if (path
[1] == '\0' && *path
== '/') {
288 while (*send
++ = *path
++) {
291 if (LSTAT(spath
, &st
) == -1)
292 nonfatal("can't get status of ", spath
);
298 if (st
.st_dev
!= devnr
) return;
300 case 2: /* set current device number */
307 if (!depth_flag
&& check(spath
, &st
, pred
, last
) && needprint
)
308 printf("%s\n", spath
);
309 if (!prune_here
&& (st
.st_mode
& S_IFMT
) == S_IFDIR
) {
310 if ((dp
= opendir(spath
)) == NULL
) {
311 nonfatal("can't read directory ", spath
);
316 while ((de
= readdir(dp
)) != NULL
) {
318 if ((de
->d_name
[0] != '.') || ((de
->d_name
[1])
319 && ((de
->d_name
[1] != '.')
320 || (de
->d_name
[2])))) {
321 strcpy(send
, de
->d_name
);
322 find(spath
, pred
, send
);
329 if (check(spath
, &st
, pred
, last
) && needprint
)
330 printf("%s\n", spath
);
335 int check(path
, st
, n
, last
)
337 register struct stat
*st
;
338 register struct node
*n
;
340 if (n
== (struct node
*) NULL
) return 1;
343 return check(path
, st
, n
->n_info
.n_opnd
.n_left
, last
) &&
344 check(path
, st
, n
->n_info
.n_opnd
.n_right
, last
);
346 return check(path
, st
, n
->n_info
.n_opnd
.n_left
, last
) ||
347 check(path
, st
, n
->n_info
.n_opnd
.n_right
, last
);
349 return !check(path
, st
, n
->n_info
.n_opnd
.n_left
, last
);
351 return smatch(last
, n
->n_info
.n_str
);
353 if (n
->n_info
.n_int
.n_sign
< 0)
354 return(st
->st_mode
& (int) n
->n_info
.n_int
.n_val
) ==
355 (int) n
->n_info
.n_int
.n_val
;
356 return(st
->st_mode
& 07777) == (int) n
->n_info
.n_int
.n_val
;
358 return st
->st_mtime
> n
->n_info
.n_int
.n_val
;
360 return st
->st_ctime
> n
->n_info
.n_int
.n_val
;
362 return(st
->st_mode
& S_IFMT
) == (mode_t
) n
->n_info
.n_int
.n_val
;
364 return ichk((long) (st
->st_nlink
), n
);
366 return st
->st_uid
== n
->n_info
.n_int
.n_val
;
368 return st
->st_gid
== n
->n_info
.n_int
.n_val
;
370 return ichk((st
->st_size
== 0) ? 0L :
371 (long) ((st
->st_size
- 1) / BSIZE
+ 1), n
);
373 return ichk((long) st
->st_size
, n
);
375 return ichk((long) (st
->st_ino
), n
);
377 return ichk(st
->st_atime
, n
);
379 return ichk(st
->st_ctime
, n
);
381 return ichk(st
->st_mtime
, n
);
384 return execute(n
->n_type
, n
->n_info
.n_exec
, path
);
386 printf("%s\n", path
);
389 printf("%s", path
); putchar(0);
398 return(getpwuid(st
->st_uid
) == (struct passwd
*) NULL
);
400 return(getgrgid(st
->st_gid
) == (struct group
*) NULL
);
402 fatal("ILLEGAL NODE", "");
403 return 0; /* Never reached */
410 switch (n
->n_info
.n_int
.n_sign
) {
412 return val
== n
->n_info
.n_int
.n_val
;
414 return val
> n
->n_info
.n_int
.n_val
;
415 case -1: return val
< n
->n_info
.n_int
.n_val
;
417 fatal("internal: bad n_sign", "");
418 return 0; /* Never reached */
424 if (str
== (char *) NULL
) return EOI
;
426 register struct oper
*op
;
429 for (op
= ops
; op
->op_str
; op
++)
430 if (strcmp(str
, op
->op_str
) == 0) break;
439 case '!': return NOT
;
449 struct node
*n
= (struct node
*) Malloc(sizeof(struct node
));
455 /*########################### PARSER ###################################*/
457 * expr : primary | primary OR expr;
458 * primary : secondary | secondary AND primary | secondary primary;
459 * secondary : NOT secondary | LPAR expr RPAR | simple;
460 * simple : -OP args...
463 /* Isnumber checks correct number syntax. A sign is allowed, but the '+'
464 * only if the number is to be in decimal.
466 int isnumber(str
, base
, sign
)
471 if (sign
&& ((*str
== '-') || ((base
== 8) && (*str
== '+')))) str
++;
472 while ((*str
>= '0') && (*str
< ('0' + base
))) str
++;
473 return(*str
== '\0' ? 1 : 0);
476 /* Convert a string to an integer, storing sign info in *ps. */
477 void number(str
, base
, pl
, ps
)
483 int up
= '0' + base
- 1;
486 *ps
= ((*str
== '-' || *str
== '+') ? ((*str
++ == '-') ? -1 : 1) : 0);
487 while (*str
>= '0' && *str
<= up
) val
= base
* val
+ *str
++ - '0';
488 if (*str
) fatal("syntax error: illegal numeric value", "");
493 void domode(op
, mode
, bits
)
501 break; /* clear bits */
504 break; /* set bits */
506 *mode
|= (bits
& ~um
); /* set, but take umask in account */
510 void fmode(str
, pl
, ps
)
527 w
= MUSER
| MGROUP
| MOTHERS
;
529 fatal("u, g, o, or a expected: ", p
);
543 w
= MUSER
| MGROUP
| MOTHERS
;
547 if (!ISOPER(*p
)) fatal("-, + or = expected: ", p
);
553 if (w
& MUSER
) domode(op
, &m
, S_IRUSR
);
554 if (w
& MGROUP
) domode(op
, &m
, S_IRGRP
);
555 if (w
& MOTHERS
) domode(op
, &m
, S_IROTH
);
558 if (w
& MUSER
) domode(op
, &m
, S_IWUSR
);
559 if (w
& MGROUP
) domode(op
, &m
, S_IWGRP
);
560 if (w
& MOTHERS
) domode(op
, &m
, S_IWOTH
);
563 if (w
& MUSER
) domode(op
, &m
, S_IXUSR
);
564 if (w
& MGROUP
) domode(op
, &m
, S_IXGRP
);
565 if (w
& MOTHERS
) domode(op
, &m
, S_IXOTH
);
568 if (w
& MUSER
) domode(op
, &m
, S_ISUID
);
569 if (w
& MGROUP
) domode(op
, &m
, S_ISGID
);
572 domode(op
, &m
, S_ISVTX
);
580 fatal("garbage at end of mode string: ", p
);
590 struct node
*nd
, *p
, *nd2
;
593 if ((t
= lex(*++ipp
)) == OP_OR
) {
594 nd2
= expr(lex(*++ipp
));
596 p
->n_info
.n_opnd
.n_left
= nd
;
597 p
->n_info
.n_opnd
.n_right
= nd2
;
608 struct node
*nd
, *p
, *nd2
;
611 if ((t
= lex(*++ipp
)) != OP_AND
) {
613 if (t
== EOI
|| t
== RPAR
|| t
== OP_OR
) return nd
;
615 nd2
= primary(lex(*++ipp
));
617 p
->n_info
.n_opnd
.n_left
= nd
;
618 p
->n_info
.n_opnd
.n_right
= nd2
;
629 n
= expr(lex(*++ipp
));
630 if (lex(*++ipp
) != RPAR
) fatal("syntax error, ) expected", "");
634 n
= secondary(lex(*++ipp
));
636 p
->n_info
.n_opnd
.n_left
= n
;
645 if (arg
== 0) fatal("syntax error, argument expected", "");
652 struct node
*p
= newnode(t
);
665 p
->n_info
.n_int
.n_val
= S_IFBLK
;
668 p
->n_info
.n_int
.n_val
= S_IFCHR
;
671 p
->n_info
.n_int
.n_val
= S_IFDIR
;
674 p
->n_info
.n_int
.n_val
= S_IFREG
;
677 p
->n_info
.n_int
.n_val
= S_IFIFO
;
680 p
->n_info
.n_int
.n_val
= ~0;
684 p
->n_info
.n_int
.n_val
= S_IFLNK
;
686 p
->n_info
.n_int
.n_val
= ~0; /* Always unequal. */
690 fatal("-type needs b, c, d, f, p, s or l", "");
695 if (((pw
= getpwnam(*ipp
)) == NULL
)
696 && isnumber(*ipp
, 10, 0))
697 number(*ipp
, 10, &(p
->n_info
.n_int
.n_val
),
698 &(p
->n_info
.n_int
.n_sign
));
701 fatal("unknown user: ", *ipp
);
702 p
->n_info
.n_int
.n_val
= pw
->pw_uid
;
703 p
->n_info
.n_int
.n_sign
= 0;
708 if (((gr
= getgrnam(*ipp
)) == NULL
)
709 && isnumber(*ipp
, 10, 0))
710 number(*ipp
, 10, &(p
->n_info
.n_int
.n_val
),
711 &(p
->n_info
.n_int
.n_sign
));
714 fatal("unknown group: ", *ipp
);
715 p
->n_info
.n_int
.n_val
= gr
->gr_gid
;
716 p
->n_info
.n_int
.n_sign
= 0;
721 i
= strlen(*ipp
) - 1;
722 if ((*ipp
)[i
] == 'c') {
723 p
->n_type
= OP_SIZEC
; /* Count in bytes i.s.o. blocks */
726 number(*ipp
, 10, &(p
->n_info
.n_int
.n_val
),
727 &(p
->n_info
.n_int
.n_sign
));
732 number(*ipp
, 10, &(p
->n_info
.n_int
.n_val
),
733 &(p
->n_info
.n_int
.n_sign
));
737 if (isnumber(*ipp
, 8, 1)) number(*ipp
, 8, &(p
->n_info
.n_int
.n_val
),
738 &(p
->n_info
.n_int
.n_sign
));
740 fmode(*ipp
, &(p
->n_info
.n_int
.n_val
),
741 &(p
->n_info
.n_int
.n_sign
));
747 number(*ipp
, 10, &l
, &(p
->n_info
.n_int
.n_sign
));
748 p
->n_info
.n_int
.n_val
= current_time
- l
* SECS_PER_DAY
;
749 /* More than n days old means less than the absolute time */
750 p
->n_info
.n_int
.n_sign
*= -1;
755 e
= (struct exec
*) Malloc(sizeof(struct exec
));
758 p
->n_info
.n_exec
= e
;
760 if (**ipp
== ';' && (*ipp
)[1] == '\0') {
761 e
->e_vec
[e
->e_cnt
] = 0;
764 e
->e_vec
[(e
->e_cnt
)++] =
765 (**ipp
== '{' && (*ipp
)[1] == '}'
766 && (*ipp
)[2] == '\0') ? (char *) (-1) : *ipp
;
769 if (*ipp
== 0) fatal("-exec/-ok: ; missing", "");
770 if ((e
->e_vec
[1] = find_bin(e
->e_vec
[2])) == 0)
771 fatal("can't find program ", e
->e_vec
[2]);
773 if ((tty
= open("/dev/tty", O_RDWR
)) < 0)
774 fatal("can't open /dev/tty", "");
779 if (LSTAT(*ipp
, &est
) == -1)
780 fatal("-newer: can't get status of ", *ipp
);
781 p
->n_info
.n_int
.n_val
= est
.st_mtime
;
785 p
->n_info
.n_str
= *ipp
;
787 case OP_XDEV
: xdev_flag
= 1; break;
788 case OP_DEPTH
: depth_flag
= 1; break;
792 case OP_NOUSER
: case OP_NOGROUP
: break;
794 fatal("syntax error, operator expected", "");
796 if ((t
== OP_PRINT
) || (t
== OP_PRINT0
) || (t
== OP_EXEC
) || (t
== OP_OK
))
802 /*######################## DIAGNOSTICS ##############################*/
804 void nonfatal(s1
, s2
)
807 fprintf(stderr
, "%s: %s%s\n", prog
, s1
, s2
);
817 /*################### SMATCH #########################*/
818 /* Don't try to understand the following one... */
819 int smatch(s
, t
) /* shell-like matching */
824 if (*t
== '\0') return *s
== '\0';
828 if (smatch(s
, t
)) return 1;
829 while (*s
++ != '\0');
832 if (*s
== '\0') return 0;
833 if (*t
== '\\') return (*s
== *++t
) ? smatch(++s
, ++t
) : 0;
834 if (*t
== '?') return smatch(++s
, ++t
);
836 while (*++t
!= ']') {
842 return smatch(++s
, ++t
);
845 if (*(t
+ 2) == ']') return(*s
== *t
|| *s
== '-');
846 n
= (*(t
+ 2) == '\\') ? 3 : 2;
847 if (*s
>= *t
&& *s
<= *(t
+ n
)) {
850 return smatch(++s
, ++t
);
856 return(*s
== *t
) ? smatch(++s
, ++t
) : 0;
859 /*####################### EXECUTE ###########################*/
860 /* Do -exec or -ok */
866 char *f
, *l
, buf
[PATH_MAX
];
868 if (*s
== '/') /* absolute path name */
869 return(access(s
, 1) == 0) ? s
: 0;
872 if (*l
== ':' || *l
== 0) {
874 if (access(s
, 1) == 0) return Salloc(s
);
877 register char *p
= buf
, *q
= s
;
879 while (f
!= l
) *p
++ = *f
++;
882 while (*p
++ = *q
++) {
884 if (access(buf
, 1) == 0) return Salloc(buf
);
893 int execute(op
, e
, path
)
900 register char **p
, **q
;
902 for (p
= e
->e_vec
, q
= argv
; *p
;) /* replace the {}s */
903 if ((*q
++ = *p
++) == (char *) -1) q
[-1] = path
;
908 for (p
= &argv
[2]; *p
; p
++) {
909 write(tty
, *p
, strlen(*p
));
913 if (read(tty
, answer
, 10) < 2 || *answer
!= 'y') return 0;
915 if ((pid
= fork()) == -1) fatal("can't fork", "");
919 while (close(i
++) == 0) {
921 execv(argv
[1], &argv
[2]); /* binary itself? */
922 execv(argv
[0], &argv
[1]); /* shell command? */
923 fatal("exec failure: ", argv
[1]); /* none of them! */
926 return wait(&s
) == pid
&& s
== 0;