140947Sbostic /*- 240947Sbostic * Copyright (c) 1990 The Regents of the University of California. 340947Sbostic * All rights reserved. 440947Sbostic * 540947Sbostic * This code is derived from software contributed to Berkeley by 640947Sbostic * Cimarron D. Taylor of the University of California, Berkeley. 740947Sbostic * 840947Sbostic * %sccs.include.redist.c% 940947Sbostic */ 1040947Sbostic 1140947Sbostic #ifndef lint 12*50437Sbostic static char sccsid[] = "@(#)function.c 5.18 (Berkeley) 07/19/91"; 1340947Sbostic #endif /* not lint */ 1440947Sbostic 1545868Sbostic #include <sys/param.h> 1640947Sbostic #include <sys/stat.h> 1740947Sbostic #include <sys/wait.h> 1840947Sbostic #include <sys/mount.h> 1947192Sbostic #include <errno.h> 2040947Sbostic #include <grp.h> 2140947Sbostic #include <pwd.h> 2240947Sbostic #include <fts.h> 2340947Sbostic #include <unistd.h> 2440947Sbostic #include <tzfile.h> 2540947Sbostic #include <stdio.h> 2647192Sbostic #include <stdlib.h> 2742047Sbostic #include <string.h> 2840947Sbostic #include "find.h" 2940947Sbostic 3040947Sbostic #define COMPARE(a, b) { \ 3140947Sbostic switch(plan->flags) { \ 32*50437Sbostic case F_EQUAL: \ 3340947Sbostic return(a == b); \ 34*50437Sbostic case F_LESSTHAN: \ 3540947Sbostic return(a < b); \ 36*50437Sbostic case F_GREATER: \ 3740947Sbostic return(a > b); \ 3840947Sbostic } \ 3942255Sbostic return(0); \ 4040947Sbostic } 4140947Sbostic 4249868Sbostic static PLAN *palloc __P((enum ntype, int (*)())); 4340947Sbostic 4440947Sbostic /* 4540947Sbostic * find_parsenum -- 4640947Sbostic * Parse a string of the form [+-]# and return the value. 4740947Sbostic */ 4840947Sbostic long 4940947Sbostic find_parsenum(plan, option, str, endch) 5040947Sbostic PLAN *plan; 5140947Sbostic char *option, *str, *endch; 5240947Sbostic { 5340947Sbostic long value; 5440947Sbostic char *endchar; /* pointer to character ending conversion */ 5540947Sbostic 5640947Sbostic /* determine comparison from leading + or - */ 5740947Sbostic switch(*str) { 5840947Sbostic case '+': 5940947Sbostic ++str; 60*50437Sbostic plan->flags = F_GREATER; 6140947Sbostic break; 6240947Sbostic case '-': 6340947Sbostic ++str; 64*50437Sbostic plan->flags = F_LESSTHAN; 6540947Sbostic break; 6640947Sbostic default: 67*50437Sbostic plan->flags = F_EQUAL; 6840947Sbostic break; 6940947Sbostic } 7040947Sbostic 7140947Sbostic /* 7240947Sbostic * convert the string with strtol(). Note, if strtol() returns zero 7340947Sbostic * and endchar points to the beginning of the string we know we have 7440947Sbostic * a syntax error. 7540947Sbostic */ 7640947Sbostic value = strtol(str, &endchar, 10); 7740947Sbostic if (!value && endchar == str || 7840947Sbostic endchar[0] && (!endch || endchar[0] != *endch)) 7949868Sbostic err("%s: %s", option, "illegal numeric value"); 8040947Sbostic if (endch) 8140947Sbostic *endch = endchar[0]; 8240947Sbostic return(value); 8340947Sbostic } 8440947Sbostic 8540947Sbostic /* 8640947Sbostic * -atime n functions -- 8740947Sbostic * 8840947Sbostic * True if the difference between the file access time and the 8940947Sbostic * current time is n 24 hour periods. 9040947Sbostic * 9140947Sbostic */ 9240947Sbostic f_atime(plan, entry) 9340947Sbostic PLAN *plan; 9440947Sbostic FTSENT *entry; 9540947Sbostic { 9640947Sbostic extern time_t now; 9740947Sbostic 9842255Sbostic COMPARE((now - entry->fts_statb.st_atime + 9942255Sbostic SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 10040947Sbostic } 10140947Sbostic 10240947Sbostic PLAN * 10340947Sbostic c_atime(arg) 10440947Sbostic char *arg; 10540947Sbostic { 10640947Sbostic PLAN *new; 10740947Sbostic 10840947Sbostic ftsoptions &= ~FTS_NOSTAT; 10940947Sbostic 11049868Sbostic new = palloc(N_ATIME, f_atime); 11149868Sbostic new->t_data = find_parsenum(new, "-atime", arg, NULL); 11240947Sbostic return(new); 11340947Sbostic } 11440947Sbostic /* 11540947Sbostic * -ctime n functions -- 11640947Sbostic * 11740947Sbostic * True if the difference between the last change of file 11840947Sbostic * status information and the current time is n 24 hour periods. 11940947Sbostic */ 12040947Sbostic f_ctime(plan, entry) 12140947Sbostic PLAN *plan; 12240947Sbostic FTSENT *entry; 12340947Sbostic { 12440947Sbostic extern time_t now; 12540947Sbostic 12642255Sbostic COMPARE((now - entry->fts_statb.st_ctime + 12742255Sbostic SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 12840947Sbostic } 12940947Sbostic 13040947Sbostic PLAN * 13140947Sbostic c_ctime(arg) 13240947Sbostic char *arg; 13340947Sbostic { 13440947Sbostic PLAN *new; 13540947Sbostic 13640947Sbostic ftsoptions &= ~FTS_NOSTAT; 13740947Sbostic 13849868Sbostic new = palloc(N_CTIME, f_ctime); 13940947Sbostic new->t_data = find_parsenum(new, "-ctime", arg, (char *)NULL); 14040947Sbostic return(new); 14140947Sbostic } 14240947Sbostic 14340947Sbostic /* 14440947Sbostic * -depth functions -- 14540947Sbostic * 14640947Sbostic * Always true, causes descent of the directory hierarchy to be done 14740947Sbostic * so that all entries in a directory are acted on before the directory 14840947Sbostic * itself. 14940947Sbostic */ 15040947Sbostic /* ARGSUSED */ 15140947Sbostic f_always_true(plan, entry) 15240947Sbostic PLAN *plan; 15340947Sbostic FTSENT *entry; 15440947Sbostic { 15542255Sbostic return(1); 15640947Sbostic } 15740947Sbostic 15840947Sbostic PLAN * 15940947Sbostic c_depth() 16040947Sbostic { 16145615Sbostic isdepth = 1; 16240947Sbostic 16349868Sbostic return(palloc(N_DEPTH, f_always_true)); 16440947Sbostic } 16540947Sbostic 16640947Sbostic /* 16740947Sbostic * [-exec | -ok] utility [arg ... ] ; functions -- 16840947Sbostic * 16940947Sbostic * True if the executed utility returns a zero value as exit status. 17040947Sbostic * The end of the primary expression is delimited by a semicolon. If 17140947Sbostic * "{}" occurs anywhere, it gets replaced by the current pathname. 17240947Sbostic * The current directory for the execution of utility is the same as 17340947Sbostic * the current directory when the find utility was started. 17440947Sbostic * 17540947Sbostic * The primary -ok is different in that it requests affirmation of the 17640947Sbostic * user before executing the utility. 17740947Sbostic */ 17840947Sbostic f_exec(plan, entry) 17940947Sbostic register PLAN *plan; 18040947Sbostic FTSENT *entry; 18140947Sbostic { 18249869Sbostic extern int dotfd; 18340947Sbostic register int cnt; 18447192Sbostic pid_t pid; 18547192Sbostic int status; 18640947Sbostic 18740947Sbostic for (cnt = 0; plan->e_argv[cnt]; ++cnt) 18840947Sbostic if (plan->e_len[cnt]) 18945616Sbostic brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 19049869Sbostic entry->fts_path, plan->e_len[cnt]); 19140947Sbostic 192*50437Sbostic if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) 19342255Sbostic return(0); 19440947Sbostic 19540947Sbostic switch(pid = vfork()) { 19640947Sbostic case -1: 19749868Sbostic err("fork: %s", strerror(errno)); 19840947Sbostic /* NOTREACHED */ 19940947Sbostic case 0: 20049869Sbostic if (fchdir(dotfd)) { 20149869Sbostic (void)fprintf(stderr, 20249869Sbostic "find: chdir: %s\n", strerror(errno)); 20349869Sbostic _exit(1); 20449869Sbostic } 20540947Sbostic execvp(plan->e_argv[0], plan->e_argv); 20649869Sbostic (void)fprintf(stderr, 20749869Sbostic "find: %s: %s\n", plan->e_argv[0], strerror(errno)); 20849869Sbostic _exit(1); 20940947Sbostic } 21047192Sbostic pid = waitpid(pid, &status, 0); 21147192Sbostic return(pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 21240947Sbostic } 21340947Sbostic 21440947Sbostic /* 21540947Sbostic * c_exec -- 21640947Sbostic * build three parallel arrays, one with pointers to the strings passed 21740947Sbostic * on the command line, one with (possibly duplicated) pointers to the 21840947Sbostic * argv array, and one with integer values that are lengths of the 21940947Sbostic * strings, but also flags meaning that the string has to be massaged. 22040947Sbostic */ 22140947Sbostic PLAN * 22240947Sbostic c_exec(argvp, isok) 22340947Sbostic char ***argvp; 22440947Sbostic int isok; 22540947Sbostic { 22640947Sbostic PLAN *new; /* node returned */ 22740947Sbostic register int cnt; 22840947Sbostic register char **argv, **ap, *p; 22940947Sbostic 23047192Sbostic isoutput = 1; 23140947Sbostic 23249868Sbostic new = palloc(N_EXEC, f_exec); 233*50437Sbostic if (isok) 234*50437Sbostic new->flags = F_NEEDOK; 23540947Sbostic 23640947Sbostic for (ap = argv = *argvp;; ++ap) { 23740947Sbostic if (!*ap) 23849868Sbostic err("%s: %s", 23949868Sbostic isok ? "-ok" : "-exec", "no terminating \";\""); 24040947Sbostic if (**ap == ';') 24140947Sbostic break; 24240947Sbostic } 24340947Sbostic 24440947Sbostic cnt = ap - *argvp + 1; 24540947Sbostic new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 24640947Sbostic new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 24745868Sbostic new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 24840947Sbostic 24940947Sbostic for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 25040947Sbostic new->e_orig[cnt] = *argv; 25140947Sbostic for (p = *argv; *p; ++p) 25240947Sbostic if (p[0] == '{' && p[1] == '}') { 25345868Sbostic new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 25445868Sbostic new->e_len[cnt] = MAXPATHLEN; 25540947Sbostic break; 25640947Sbostic } 25740947Sbostic if (!*p) { 25840947Sbostic new->e_argv[cnt] = *argv; 25940947Sbostic new->e_len[cnt] = 0; 26040947Sbostic } 26140947Sbostic } 26240947Sbostic new->e_argv[cnt] = new->e_orig[cnt] = NULL; 26340947Sbostic 26440947Sbostic *argvp = argv + 1; 26540947Sbostic return(new); 26640947Sbostic } 26740947Sbostic 26840947Sbostic /* 26940947Sbostic * -follow functions -- 27040947Sbostic * 27140947Sbostic * Always true, causes symbolic links to be followed on a global 27240947Sbostic * basis. 27340947Sbostic */ 27440947Sbostic PLAN * 27540947Sbostic c_follow() 27640947Sbostic { 27740947Sbostic ftsoptions &= ~FTS_PHYSICAL; 27840947Sbostic ftsoptions |= FTS_LOGICAL; 27940947Sbostic 28049868Sbostic return(palloc(N_FOLLOW, f_always_true)); 28140947Sbostic } 28240947Sbostic 28340947Sbostic /* 28440947Sbostic * -fstype functions -- 28540947Sbostic * 28640947Sbostic * True if the file is of a certain type. 28740947Sbostic */ 28840947Sbostic f_fstype(plan, entry) 28940947Sbostic PLAN *plan; 29040947Sbostic FTSENT *entry; 29140947Sbostic { 29242275Sbostic static dev_t curdev; /* need a guaranteed illegal dev value */ 29342275Sbostic static int first = 1; 29440947Sbostic struct statfs sb; 29542255Sbostic static short val; 29645626Sbostic char *p, save[2]; 29740947Sbostic 298*50437Sbostic /* Only check when we cross mount point. */ 29942275Sbostic if (first || curdev != entry->fts_statb.st_dev) { 30043856Sbostic curdev = entry->fts_statb.st_dev; 30145626Sbostic 30245626Sbostic /* 30345626Sbostic * Statfs follows symlinks; find wants the link's file system, 30445626Sbostic * not where it points. 30545626Sbostic */ 30645626Sbostic if (entry->fts_info == FTS_SL || 30745626Sbostic entry->fts_info == FTS_SLNONE) { 30845626Sbostic if (p = rindex(entry->fts_accpath, '/')) 30945626Sbostic ++p; 31045626Sbostic else 31145626Sbostic p = entry->fts_accpath; 31245626Sbostic save[0] = p[0]; 31345626Sbostic p[0] = '.'; 31445626Sbostic save[1] = p[1]; 31545626Sbostic p[1] = '\0'; 31645626Sbostic 31745626Sbostic } else 31845626Sbostic p = NULL; 31945626Sbostic 32049868Sbostic if (statfs(entry->fts_accpath, &sb)) 32149868Sbostic err("%s: %s", entry->fts_accpath, strerror(errno)); 32245626Sbostic 32345626Sbostic if (p) { 32445626Sbostic p[0] = save[0]; 32545626Sbostic p[1] = save[1]; 32645626Sbostic } 32745626Sbostic 32842275Sbostic first = 0; 329*50437Sbostic switch(plan->flags) { 330*50437Sbostic case F_MTFLAG: 331*50437Sbostic val = sb.f_flags; 332*50437Sbostic break; 333*50437Sbostic case F_MTTYPE: 334*50437Sbostic val = sb.f_type; 335*50437Sbostic break; 336*50437Sbostic } 33740947Sbostic } 338*50437Sbostic switch(plan->flags) { 339*50437Sbostic case F_MTFLAG: 340*50437Sbostic return(val & plan->mt_data); 341*50437Sbostic case F_MTTYPE: 342*50437Sbostic return(val == plan->mt_data); 343*50437Sbostic } 34440947Sbostic } 34540947Sbostic 34640947Sbostic PLAN * 34740947Sbostic c_fstype(arg) 34840947Sbostic char *arg; 34940947Sbostic { 35040947Sbostic register PLAN *new; 35140947Sbostic 35240947Sbostic ftsoptions &= ~FTS_NOSTAT; 35340947Sbostic 35449868Sbostic new = palloc(N_FSTYPE, f_fstype); 35540947Sbostic switch(*arg) { 35642255Sbostic case 'l': 35742255Sbostic if (!strcmp(arg, "local")) { 358*50437Sbostic new->flags = F_MTFLAG; 359*50437Sbostic new->mt_data = MNT_LOCAL; 36042255Sbostic return(new); 36142255Sbostic } 36242255Sbostic break; 36340947Sbostic case 'm': 36440947Sbostic if (!strcmp(arg, "mfs")) { 365*50437Sbostic new->flags = F_MTTYPE; 366*50437Sbostic new->mt_data = MOUNT_MFS; 36740947Sbostic return(new); 36840947Sbostic } 36940947Sbostic break; 37040947Sbostic case 'n': 37140947Sbostic if (!strcmp(arg, "nfs")) { 372*50437Sbostic new->flags = F_MTTYPE; 373*50437Sbostic new->mt_data = MOUNT_NFS; 37440947Sbostic return(new); 37540947Sbostic } 37640947Sbostic break; 37740947Sbostic case 'p': 37840947Sbostic if (!strcmp(arg, "pc")) { 379*50437Sbostic new->flags = F_MTTYPE; 380*50437Sbostic new->mt_data = MOUNT_PC; 38140947Sbostic return(new); 38240947Sbostic } 38340947Sbostic break; 384*50437Sbostic case 'r': 385*50437Sbostic if (!strcmp(arg, "rdonly")) { 386*50437Sbostic new->flags = F_MTFLAG; 387*50437Sbostic new->mt_data = MNT_RDONLY; 388*50437Sbostic return(new); 389*50437Sbostic } 390*50437Sbostic break; 39140947Sbostic case 'u': 39240947Sbostic if (!strcmp(arg, "ufs")) { 393*50437Sbostic new->flags = F_MTTYPE; 394*50437Sbostic new->mt_data = MOUNT_UFS; 39540947Sbostic return(new); 39640947Sbostic } 39740947Sbostic break; 39840947Sbostic } 39949868Sbostic err("unknown file type %s", arg); 40040947Sbostic /* NOTREACHED */ 40140947Sbostic } 40240947Sbostic 40340947Sbostic /* 40440947Sbostic * -group gname functions -- 40540947Sbostic * 40640947Sbostic * True if the file belongs to the group gname. If gname is numeric and 40740947Sbostic * an equivalent of the getgrnam() function does not return a valid group 40840947Sbostic * name, gname is taken as a group ID. 40940947Sbostic */ 41040947Sbostic f_group(plan, entry) 41140947Sbostic PLAN *plan; 41240947Sbostic FTSENT *entry; 41340947Sbostic { 41442255Sbostic return(entry->fts_statb.st_gid == plan->g_data); 41540947Sbostic } 41640947Sbostic 41740947Sbostic PLAN * 41840947Sbostic c_group(gname) 41940947Sbostic char *gname; 42040947Sbostic { 42140947Sbostic PLAN *new; 42240947Sbostic struct group *g; 42340947Sbostic gid_t gid; 42440947Sbostic 42540947Sbostic ftsoptions &= ~FTS_NOSTAT; 42640947Sbostic 42740947Sbostic g = getgrnam(gname); 42840947Sbostic if (g == NULL) { 42940947Sbostic gid = atoi(gname); 43040947Sbostic if (gid == 0 && gname[0] != '0') 43149868Sbostic err("%s: %s", "-group", "no such group"); 43240947Sbostic } else 43340947Sbostic gid = g->gr_gid; 43440947Sbostic 43549868Sbostic new = palloc(N_GROUP, f_group); 43640947Sbostic new->g_data = gid; 43740947Sbostic return(new); 43840947Sbostic } 43940947Sbostic 44040947Sbostic /* 44140947Sbostic * -inum n functions -- 44240947Sbostic * 44340947Sbostic * True if the file has inode # n. 44440947Sbostic */ 44540947Sbostic f_inum(plan, entry) 44640947Sbostic PLAN *plan; 44740947Sbostic FTSENT *entry; 44840947Sbostic { 44942255Sbostic COMPARE(entry->fts_statb.st_ino, plan->i_data); 45040947Sbostic } 45140947Sbostic 45240947Sbostic PLAN * 45340947Sbostic c_inum(arg) 45440947Sbostic char *arg; 45540947Sbostic { 45640947Sbostic PLAN *new; 45740947Sbostic 45840947Sbostic ftsoptions &= ~FTS_NOSTAT; 45940947Sbostic 46049868Sbostic new = palloc(N_INUM, f_inum); 46140947Sbostic new->i_data = find_parsenum(new, "-inum", arg, (char *)NULL); 46240947Sbostic return(new); 46340947Sbostic } 46440947Sbostic 46540947Sbostic /* 46640947Sbostic * -links n functions -- 46740947Sbostic * 46840947Sbostic * True if the file has n links. 46940947Sbostic */ 47040947Sbostic f_links(plan, entry) 47140947Sbostic PLAN *plan; 47240947Sbostic FTSENT *entry; 47340947Sbostic { 47442255Sbostic COMPARE(entry->fts_statb.st_nlink, plan->l_data); 47540947Sbostic } 47640947Sbostic 47740947Sbostic PLAN * 47840947Sbostic c_links(arg) 47940947Sbostic char *arg; 48040947Sbostic { 48140947Sbostic PLAN *new; 48240947Sbostic 48340947Sbostic ftsoptions &= ~FTS_NOSTAT; 48440947Sbostic 48549868Sbostic new = palloc(N_LINKS, f_links); 48645616Sbostic new->l_data = (nlink_t)find_parsenum(new, "-links", arg, (char *)NULL); 48740947Sbostic return(new); 48840947Sbostic } 48940947Sbostic 49040947Sbostic /* 49140947Sbostic * -ls functions -- 49240947Sbostic * 49340947Sbostic * Always true - prints the current entry to stdout in "ls" format. 49440947Sbostic */ 49540947Sbostic /* ARGSUSED */ 49640947Sbostic f_ls(plan, entry) 49740947Sbostic PLAN *plan; 49840947Sbostic FTSENT *entry; 49940947Sbostic { 50042255Sbostic printlong(entry->fts_path, entry->fts_accpath, &entry->fts_statb); 50142255Sbostic return(1); 50240947Sbostic } 50340947Sbostic 50440947Sbostic PLAN * 50540947Sbostic c_ls() 50640947Sbostic { 50740947Sbostic ftsoptions &= ~FTS_NOSTAT; 50845615Sbostic isoutput = 1; 50940947Sbostic 51049868Sbostic return(palloc(N_LS, f_ls)); 51140947Sbostic } 51240947Sbostic 51340947Sbostic /* 514*50437Sbostic * -mtime n functions -- 515*50437Sbostic * 516*50437Sbostic * True if the difference between the file modification time and the 517*50437Sbostic * current time is n 24 hour periods. 518*50437Sbostic */ 519*50437Sbostic f_mtime(plan, entry) 520*50437Sbostic PLAN *plan; 521*50437Sbostic FTSENT *entry; 522*50437Sbostic { 523*50437Sbostic extern time_t now; 524*50437Sbostic 525*50437Sbostic COMPARE((now - entry->fts_statb.st_mtime + SECSPERDAY - 1) / 526*50437Sbostic SECSPERDAY, plan->t_data); 527*50437Sbostic } 528*50437Sbostic 529*50437Sbostic PLAN * 530*50437Sbostic c_mtime(arg) 531*50437Sbostic char *arg; 532*50437Sbostic { 533*50437Sbostic PLAN *new; 534*50437Sbostic 535*50437Sbostic ftsoptions &= ~FTS_NOSTAT; 536*50437Sbostic 537*50437Sbostic new = palloc(N_MTIME, f_mtime); 538*50437Sbostic new->t_data = find_parsenum(new, "-mtime", arg, (char *)NULL); 539*50437Sbostic return(new); 540*50437Sbostic } 541*50437Sbostic 542*50437Sbostic /* 54340947Sbostic * -name functions -- 54440947Sbostic * 54540947Sbostic * True if the basename of the filename being examined 54640947Sbostic * matches pattern using Pattern Matching Notation S3.14 54740947Sbostic */ 54840947Sbostic f_name(plan, entry) 54940947Sbostic PLAN *plan; 55040947Sbostic FTSENT *entry; 55140947Sbostic { 55242255Sbostic return(fnmatch(plan->c_data, entry->fts_name, FNM_QUOTE)); 55340947Sbostic } 55440947Sbostic 55540947Sbostic PLAN * 55640947Sbostic c_name(pattern) 55740947Sbostic char *pattern; 55840947Sbostic { 55940947Sbostic PLAN *new; 56040947Sbostic 56149868Sbostic new = palloc(N_NAME, f_name); 56240947Sbostic new->c_data = pattern; 56340947Sbostic return(new); 56440947Sbostic } 56540947Sbostic 56640947Sbostic /* 56740947Sbostic * -newer file functions -- 56840947Sbostic * 56940947Sbostic * True if the current file has been modified more recently 57040947Sbostic * then the modification time of the file named by the pathname 57140947Sbostic * file. 57240947Sbostic */ 57340947Sbostic f_newer(plan, entry) 57440947Sbostic PLAN *plan; 57540947Sbostic FTSENT *entry; 57640947Sbostic { 57742255Sbostic return(entry->fts_statb.st_mtime > plan->t_data); 57840947Sbostic } 57940947Sbostic 58040947Sbostic PLAN * 58140947Sbostic c_newer(filename) 58240947Sbostic char *filename; 58340947Sbostic { 58440947Sbostic PLAN *new; 58540947Sbostic struct stat sb; 58640947Sbostic 58740947Sbostic ftsoptions &= ~FTS_NOSTAT; 58840947Sbostic 58949868Sbostic if (stat(filename, &sb)) 59049868Sbostic err("%s: %s", filename, strerror(errno)); 59149868Sbostic new = palloc(N_NEWER, f_newer); 59240947Sbostic new->t_data = sb.st_mtime; 59340947Sbostic return(new); 59440947Sbostic } 59540947Sbostic 59640947Sbostic /* 59740947Sbostic * -nogroup functions -- 59840947Sbostic * 59940947Sbostic * True if file belongs to a user ID for which the equivalent 60040947Sbostic * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 60140947Sbostic */ 60240947Sbostic /* ARGSUSED */ 60340947Sbostic f_nogroup(plan, entry) 60440947Sbostic PLAN *plan; 60540947Sbostic FTSENT *entry; 60640947Sbostic { 60745615Sbostic char *group_from_gid(); 60845615Sbostic 60945615Sbostic return(group_from_gid(entry->fts_statb.st_gid, 1) ? 1 : 0); 61040947Sbostic } 61140947Sbostic 61240947Sbostic PLAN * 61340947Sbostic c_nogroup() 61440947Sbostic { 61540947Sbostic ftsoptions &= ~FTS_NOSTAT; 61640947Sbostic 61749868Sbostic return(palloc(N_NOGROUP, f_nogroup)); 61840947Sbostic } 61940947Sbostic 62040947Sbostic /* 62140947Sbostic * -nouser functions -- 62240947Sbostic * 62340947Sbostic * True if file belongs to a user ID for which the equivalent 62440947Sbostic * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 62540947Sbostic */ 62640947Sbostic /* ARGSUSED */ 62740947Sbostic f_nouser(plan, entry) 62840947Sbostic PLAN *plan; 62940947Sbostic FTSENT *entry; 63040947Sbostic { 63145615Sbostic char *user_from_uid(); 63245615Sbostic 63345615Sbostic return(user_from_uid(entry->fts_statb.st_uid, 1) ? 1 : 0); 63440947Sbostic } 63540947Sbostic 63640947Sbostic PLAN * 63740947Sbostic c_nouser() 63840947Sbostic { 63940947Sbostic ftsoptions &= ~FTS_NOSTAT; 64040947Sbostic 64149868Sbostic return(palloc(N_NOUSER, f_nouser)); 64240947Sbostic } 64340947Sbostic 64440947Sbostic /* 645*50437Sbostic * -path functions -- 646*50437Sbostic * 647*50437Sbostic * True if the path of the filename being examined 648*50437Sbostic * matches pattern using Pattern Matching Notation S3.14 649*50437Sbostic */ 650*50437Sbostic f_path(plan, entry) 651*50437Sbostic PLAN *plan; 652*50437Sbostic FTSENT *entry; 653*50437Sbostic { 654*50437Sbostic return(fnmatch(plan->c_data, entry->fts_path, FNM_QUOTE)); 655*50437Sbostic } 656*50437Sbostic 657*50437Sbostic PLAN * 658*50437Sbostic c_path(pattern) 659*50437Sbostic char *pattern; 660*50437Sbostic { 661*50437Sbostic PLAN *new; 662*50437Sbostic 663*50437Sbostic new = palloc(N_NAME, f_path); 664*50437Sbostic new->c_data = pattern; 665*50437Sbostic return(new); 666*50437Sbostic } 667*50437Sbostic 668*50437Sbostic /* 66940947Sbostic * -perm functions -- 67040947Sbostic * 67140947Sbostic * The mode argument is used to represent file mode bits. If it starts 67240947Sbostic * with a leading digit, it's treated as an octal mode, otherwise as a 67340947Sbostic * symbolic mode. 67440947Sbostic */ 67540947Sbostic f_perm(plan, entry) 67640947Sbostic PLAN *plan; 67740947Sbostic FTSENT *entry; 67840947Sbostic { 67940947Sbostic mode_t mode; 68040947Sbostic 68142255Sbostic mode = entry->fts_statb.st_mode & 68240947Sbostic (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 683*50437Sbostic if (plan->flags == F_ATLEAST) 68440947Sbostic return((plan->m_data | mode) == mode); 68540947Sbostic else 68640947Sbostic return(mode == plan->m_data); 68740947Sbostic /* NOTREACHED */ 68840947Sbostic } 68940947Sbostic 69040947Sbostic PLAN * 69140947Sbostic c_perm(perm) 69240947Sbostic char *perm; 69340947Sbostic { 69440947Sbostic PLAN *new; 69547192Sbostic mode_t *set; 69640947Sbostic 69740947Sbostic ftsoptions &= ~FTS_NOSTAT; 69840947Sbostic 69949868Sbostic new = palloc(N_PERM, f_perm); 70040947Sbostic 70140947Sbostic if (*perm == '-') { 702*50437Sbostic new->flags = F_ATLEAST; 70340947Sbostic ++perm; 70440947Sbostic } 70540947Sbostic 70644764Strent if ((set = setmode(perm)) == NULL) 70749868Sbostic err("%s: %s", "-perm", "illegal mode string"); 70840947Sbostic 70944764Strent new->m_data = getmode(set, 0); 71040947Sbostic return(new); 71140947Sbostic } 71240947Sbostic 71340947Sbostic /* 71440947Sbostic * -print functions -- 71540947Sbostic * 71640947Sbostic * Always true, causes the current pathame to be written to 71740947Sbostic * standard output. 71840947Sbostic */ 71940947Sbostic /* ARGSUSED */ 72040947Sbostic f_print(plan, entry) 72140947Sbostic PLAN *plan; 72240947Sbostic FTSENT *entry; 72340947Sbostic { 72442255Sbostic (void)printf("%s\n", entry->fts_path); 72542255Sbostic return(1); 72640947Sbostic } 72740947Sbostic 72840947Sbostic PLAN * 72940947Sbostic c_print() 73040947Sbostic { 73145615Sbostic isoutput = 1; 73240947Sbostic 73349868Sbostic return(palloc(N_PRINT, f_print)); 73440947Sbostic } 73540947Sbostic 73640947Sbostic /* 73740947Sbostic * -prune functions -- 73840947Sbostic * 73940947Sbostic * Prune a portion of the hierarchy. 74040947Sbostic */ 74140947Sbostic /* ARGSUSED */ 74240947Sbostic f_prune(plan, entry) 74340947Sbostic PLAN *plan; 74440947Sbostic FTSENT *entry; 74540947Sbostic { 74640947Sbostic extern FTS *tree; 74740947Sbostic 74849868Sbostic if (fts_set(tree, entry, FTS_SKIP)) 74949868Sbostic err("%s: %s", entry->fts_path, strerror(errno)); 75042255Sbostic return(1); 75140947Sbostic } 75240947Sbostic 75340947Sbostic PLAN * 75440947Sbostic c_prune() 75540947Sbostic { 75649868Sbostic return(palloc(N_PRUNE, f_prune)); 75740947Sbostic } 75840947Sbostic 75940947Sbostic /* 76040947Sbostic * -size n[c] functions -- 76140947Sbostic * 76240947Sbostic * True if the file size in bytes, divided by an implementation defined 76340947Sbostic * value and rounded up to the next integer, is n. If n is followed by 76440947Sbostic * a c, the size is in bytes. 76540947Sbostic */ 76640947Sbostic #define FIND_SIZE 512 76740947Sbostic static int divsize = 1; 76840947Sbostic 76940947Sbostic f_size(plan, entry) 77040947Sbostic PLAN *plan; 77140947Sbostic FTSENT *entry; 77240947Sbostic { 77340947Sbostic off_t size; 77440947Sbostic 77542255Sbostic size = divsize ? (entry->fts_statb.st_size + FIND_SIZE - 1) / 77642255Sbostic FIND_SIZE : entry->fts_statb.st_size; 77740947Sbostic COMPARE(size, plan->o_data); 77840947Sbostic } 77940947Sbostic 78040947Sbostic PLAN * 78140947Sbostic c_size(arg) 78240947Sbostic char *arg; 78340947Sbostic { 78440947Sbostic PLAN *new; 78540947Sbostic char endch; 78640947Sbostic 78740947Sbostic ftsoptions &= ~FTS_NOSTAT; 78840947Sbostic 78949868Sbostic new = palloc(N_SIZE, f_size); 79040947Sbostic new->o_data = find_parsenum(new, "-size", arg, &endch); 79140947Sbostic if (endch == 'c') 79240947Sbostic divsize = 0; 79340947Sbostic return(new); 79440947Sbostic } 79540947Sbostic 79640947Sbostic /* 79740947Sbostic * -type c functions -- 79840947Sbostic * 79940947Sbostic * True if the type of the file is c, where c is b, c, d, p, or f for 80040947Sbostic * block special file, character special file, directory, FIFO, or 80140947Sbostic * regular file, respectively. 80240947Sbostic */ 80340947Sbostic f_type(plan, entry) 80440947Sbostic PLAN *plan; 80540947Sbostic FTSENT *entry; 80640947Sbostic { 80744764Strent return((entry->fts_statb.st_mode & S_IFMT) == plan->m_data); 80840947Sbostic } 80940947Sbostic 81040947Sbostic PLAN * 81140947Sbostic c_type(typestring) 81240947Sbostic char *typestring; 81340947Sbostic { 81440947Sbostic PLAN *new; 81540947Sbostic mode_t mask; 81640947Sbostic 81740947Sbostic ftsoptions &= ~FTS_NOSTAT; 81840947Sbostic 81940947Sbostic switch (typestring[0]) { 82040947Sbostic case 'b': 82140947Sbostic mask = S_IFBLK; 82240947Sbostic break; 82340947Sbostic case 'c': 82440947Sbostic mask = S_IFCHR; 82540947Sbostic break; 82640947Sbostic case 'd': 82740947Sbostic mask = S_IFDIR; 82840947Sbostic break; 82940947Sbostic case 'f': 83040947Sbostic mask = S_IFREG; 83140947Sbostic break; 83240947Sbostic case 'l': 83340947Sbostic mask = S_IFLNK; 83440947Sbostic break; 83540947Sbostic case 'p': 83640947Sbostic mask = S_IFIFO; 83740947Sbostic break; 83840947Sbostic case 's': 83940947Sbostic mask = S_IFSOCK; 84040947Sbostic break; 84140947Sbostic default: 84249868Sbostic err("%s: %s", "-type", "unknown type"); 84340947Sbostic } 84440947Sbostic 84549868Sbostic new = palloc(N_TYPE, f_type); 84640947Sbostic new->m_data = mask; 84740947Sbostic return(new); 84840947Sbostic } 84940947Sbostic 85040947Sbostic /* 85140947Sbostic * -user uname functions -- 85240947Sbostic * 85340947Sbostic * True if the file belongs to the user uname. If uname is numeric and 85440947Sbostic * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 85540947Sbostic * return a valid user name, uname is taken as a user ID. 85640947Sbostic */ 85740947Sbostic f_user(plan, entry) 85840947Sbostic PLAN *plan; 85940947Sbostic FTSENT *entry; 86040947Sbostic { 86142255Sbostic return(entry->fts_statb.st_uid == plan->u_data); 86240947Sbostic } 86340947Sbostic 86440947Sbostic PLAN * 86540947Sbostic c_user(username) 86640947Sbostic char *username; 86740947Sbostic { 86840947Sbostic PLAN *new; 86940947Sbostic struct passwd *p; 87040947Sbostic uid_t uid; 87140947Sbostic 87240947Sbostic ftsoptions &= ~FTS_NOSTAT; 87340947Sbostic 87440947Sbostic p = getpwnam(username); 87540947Sbostic if (p == NULL) { 87640947Sbostic uid = atoi(username); 87740947Sbostic if (uid == 0 && username[0] != '0') 87849868Sbostic err("%s: %s", "-user", "no such user"); 87940947Sbostic } else 88040947Sbostic uid = p->pw_uid; 88140947Sbostic 88249868Sbostic new = palloc(N_USER, f_user); 88340947Sbostic new->u_data = uid; 88440947Sbostic return(new); 88540947Sbostic } 88640947Sbostic 88740947Sbostic /* 88840947Sbostic * -xdev functions -- 88940947Sbostic * 89040947Sbostic * Always true, causes find not to decend past directories that have a 89140947Sbostic * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 89240947Sbostic */ 89340947Sbostic PLAN * 89440947Sbostic c_xdev() 89540947Sbostic { 89642275Sbostic ftsoptions |= FTS_XDEV; 89740947Sbostic 89849868Sbostic return(palloc(N_XDEV, f_always_true)); 89940947Sbostic } 90040947Sbostic 90140947Sbostic /* 90240947Sbostic * ( expression ) functions -- 90340947Sbostic * 90440947Sbostic * True if expression is true. 90540947Sbostic */ 90640947Sbostic f_expr(plan, entry) 90740947Sbostic PLAN *plan; 90840947Sbostic FTSENT *entry; 90940947Sbostic { 91040947Sbostic register PLAN *p; 91140947Sbostic register int state; 91240947Sbostic 91340947Sbostic for (p = plan->p_data[0]; 91440947Sbostic p && (state = (p->eval)(p, entry)); p = p->next); 91540947Sbostic return(state); 91640947Sbostic } 91740947Sbostic 91840947Sbostic /* 91949864Sbostic * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are 92040947Sbostic * eliminated during phase 2 of find_formplan() --- the '(' node is converted 92149864Sbostic * to a N_EXPR node containing the expression and the ')' node is discarded. 92240947Sbostic */ 92340947Sbostic PLAN * 92440947Sbostic c_openparen() 92540947Sbostic { 92649868Sbostic return(palloc(N_OPENPAREN, (int (*)())-1)); 92740947Sbostic } 92840947Sbostic 92940947Sbostic PLAN * 93040947Sbostic c_closeparen() 93140947Sbostic { 93249868Sbostic return(palloc(N_CLOSEPAREN, (int (*)())-1)); 93340947Sbostic } 93440947Sbostic 93540947Sbostic /* 93640947Sbostic * ! expression functions -- 93740947Sbostic * 93840947Sbostic * Negation of a primary; the unary NOT operator. 93940947Sbostic */ 94040947Sbostic f_not(plan, entry) 94140947Sbostic PLAN *plan; 94240947Sbostic FTSENT *entry; 94340947Sbostic { 94440947Sbostic register PLAN *p; 94540947Sbostic register int state; 94640947Sbostic 94740947Sbostic for (p = plan->p_data[0]; 94840947Sbostic p && (state = (p->eval)(p, entry)); p = p->next); 94940947Sbostic return(!state); 95040947Sbostic } 95140947Sbostic 95240947Sbostic PLAN * 95340947Sbostic c_not() 95440947Sbostic { 95549868Sbostic return(palloc(N_NOT, f_not)); 95640947Sbostic } 95740947Sbostic 95840947Sbostic /* 95940947Sbostic * expression -o expression functions -- 96040947Sbostic * 96140947Sbostic * Alternation of primaries; the OR operator. The second expression is 96240947Sbostic * not evaluated if the first expression is true. 96340947Sbostic */ 96440947Sbostic f_or(plan, entry) 96540947Sbostic PLAN *plan; 96640947Sbostic FTSENT *entry; 96740947Sbostic { 96840947Sbostic register PLAN *p; 96940947Sbostic register int state; 97040947Sbostic 97140947Sbostic for (p = plan->p_data[0]; 97240947Sbostic p && (state = (p->eval)(p, entry)); p = p->next); 97340947Sbostic 97440947Sbostic if (state) 97542255Sbostic return(1); 97640947Sbostic 97740947Sbostic for (p = plan->p_data[1]; 97840947Sbostic p && (state = (p->eval)(p, entry)); p = p->next); 97940947Sbostic return(state); 98040947Sbostic } 98140947Sbostic 98240947Sbostic PLAN * 98340947Sbostic c_or() 98440947Sbostic { 98549868Sbostic return(palloc(N_OR, f_or)); 98649868Sbostic } 98749868Sbostic 98849868Sbostic static PLAN * 98949868Sbostic palloc(t, f) 99049868Sbostic enum ntype t; 99149868Sbostic int (*f)(); 99249868Sbostic { 99340947Sbostic PLAN *new; 99440947Sbostic 99549868Sbostic if (new = malloc(sizeof(PLAN))) { 99649868Sbostic new->type = t; 99749868Sbostic new->eval = f; 99849868Sbostic new->flags = 0; 99949868Sbostic new->next = NULL; 100049868Sbostic return(new); 100149868Sbostic } 100249868Sbostic err("%s", strerror(errno)); 100349868Sbostic /* NOTREACHED */ 100440947Sbostic } 1005