1*40947Sbostic /*- 2*40947Sbostic * Copyright (c) 1990 The Regents of the University of California. 3*40947Sbostic * All rights reserved. 4*40947Sbostic * 5*40947Sbostic * This code is derived from software contributed to Berkeley by 6*40947Sbostic * Cimarron D. Taylor of the University of California, Berkeley. 7*40947Sbostic * 8*40947Sbostic * %sccs.include.redist.c% 9*40947Sbostic */ 10*40947Sbostic 11*40947Sbostic #ifndef lint 12*40947Sbostic static char sccsid[] = "@(#)function.c 5.1 (Berkeley) 04/16/90"; 13*40947Sbostic #endif /* not lint */ 14*40947Sbostic 15*40947Sbostic #include <sys/types.h> 16*40947Sbostic #include <sys/stat.h> 17*40947Sbostic #include <sys/wait.h> 18*40947Sbostic #include <sys/mount.h> 19*40947Sbostic #include <grp.h> 20*40947Sbostic #include <pwd.h> 21*40947Sbostic #include <fts.h> 22*40947Sbostic #include <unistd.h> 23*40947Sbostic #include <tzfile.h> 24*40947Sbostic #include <stdio.h> 25*40947Sbostic #include "find.h" 26*40947Sbostic 27*40947Sbostic #define FIND_EQUAL 0 28*40947Sbostic #define FIND_LESSTHAN 1 29*40947Sbostic #define FIND_GREATER 2 30*40947Sbostic 31*40947Sbostic #define FIND_FALSE 0 32*40947Sbostic #define FIND_TRUE 1 33*40947Sbostic 34*40947Sbostic #define COMPARE(a, b) { \ 35*40947Sbostic switch(plan->flags) { \ 36*40947Sbostic case FIND_EQUAL: \ 37*40947Sbostic return(a == b); \ 38*40947Sbostic case FIND_LESSTHAN: \ 39*40947Sbostic return(a < b); \ 40*40947Sbostic case FIND_GREATER: \ 41*40947Sbostic return(a > b); \ 42*40947Sbostic } \ 43*40947Sbostic return(FIND_FALSE); \ 44*40947Sbostic } 45*40947Sbostic 46*40947Sbostic #define NEW(t, f) { \ 47*40947Sbostic new = (PLAN *)emalloc(sizeof(PLAN)); \ 48*40947Sbostic new->type = t; \ 49*40947Sbostic new->eval = f; \ 50*40947Sbostic new->flags = 0; \ 51*40947Sbostic new->next = NULL; \ 52*40947Sbostic } 53*40947Sbostic 54*40947Sbostic /* 55*40947Sbostic * find_parsenum -- 56*40947Sbostic * Parse a string of the form [+-]# and return the value. 57*40947Sbostic */ 58*40947Sbostic long 59*40947Sbostic find_parsenum(plan, option, str, endch) 60*40947Sbostic PLAN *plan; 61*40947Sbostic char *option, *str, *endch; 62*40947Sbostic { 63*40947Sbostic long value; 64*40947Sbostic char *endchar; /* pointer to character ending conversion */ 65*40947Sbostic 66*40947Sbostic /* determine comparison from leading + or - */ 67*40947Sbostic switch(*str) { 68*40947Sbostic case '+': 69*40947Sbostic ++str; 70*40947Sbostic plan->flags = FIND_GREATER; 71*40947Sbostic break; 72*40947Sbostic case '-': 73*40947Sbostic ++str; 74*40947Sbostic plan->flags = FIND_LESSTHAN; 75*40947Sbostic break; 76*40947Sbostic default: 77*40947Sbostic plan->flags = FIND_EQUAL; 78*40947Sbostic break; 79*40947Sbostic } 80*40947Sbostic 81*40947Sbostic /* 82*40947Sbostic * convert the string with strtol(). Note, if strtol() returns zero 83*40947Sbostic * and endchar points to the beginning of the string we know we have 84*40947Sbostic * a syntax error. 85*40947Sbostic */ 86*40947Sbostic value = strtol(str, &endchar, 10); 87*40947Sbostic if (!value && endchar == str || 88*40947Sbostic endchar[0] && (!endch || endchar[0] != *endch)) 89*40947Sbostic bad_arg(option, "illegal numeric value"); 90*40947Sbostic if (endch) 91*40947Sbostic *endch = endchar[0]; 92*40947Sbostic return(value); 93*40947Sbostic } 94*40947Sbostic 95*40947Sbostic /* 96*40947Sbostic * -atime n functions -- 97*40947Sbostic * 98*40947Sbostic * True if the difference between the file access time and the 99*40947Sbostic * current time is n 24 hour periods. 100*40947Sbostic * 101*40947Sbostic */ 102*40947Sbostic f_atime(plan, entry) 103*40947Sbostic PLAN *plan; 104*40947Sbostic FTSENT *entry; 105*40947Sbostic { 106*40947Sbostic extern time_t now; 107*40947Sbostic 108*40947Sbostic COMPARE((now - entry->statb.st_atime + SECSPERDAY - 1) / SECSPERDAY, 109*40947Sbostic plan->t_data); 110*40947Sbostic } 111*40947Sbostic 112*40947Sbostic PLAN * 113*40947Sbostic c_atime(arg) 114*40947Sbostic char *arg; 115*40947Sbostic { 116*40947Sbostic PLAN *new; 117*40947Sbostic 118*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 119*40947Sbostic 120*40947Sbostic NEW(T_ATIME, f_atime); 121*40947Sbostic new->t_data = find_parsenum(new, "-atime", arg, (char *)NULL); 122*40947Sbostic return(new); 123*40947Sbostic } 124*40947Sbostic /* 125*40947Sbostic * -ctime n functions -- 126*40947Sbostic * 127*40947Sbostic * True if the difference between the last change of file 128*40947Sbostic * status information and the current time is n 24 hour periods. 129*40947Sbostic */ 130*40947Sbostic f_ctime(plan, entry) 131*40947Sbostic PLAN *plan; 132*40947Sbostic FTSENT *entry; 133*40947Sbostic { 134*40947Sbostic extern time_t now; 135*40947Sbostic 136*40947Sbostic COMPARE((now - entry->statb.st_ctime + SECSPERDAY - 1) / SECSPERDAY, 137*40947Sbostic plan->t_data); 138*40947Sbostic } 139*40947Sbostic 140*40947Sbostic PLAN * 141*40947Sbostic c_ctime(arg) 142*40947Sbostic char *arg; 143*40947Sbostic { 144*40947Sbostic PLAN *new; 145*40947Sbostic 146*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 147*40947Sbostic 148*40947Sbostic NEW(T_CTIME, f_ctime); 149*40947Sbostic new->t_data = find_parsenum(new, "-ctime", arg, (char *)NULL); 150*40947Sbostic return(new); 151*40947Sbostic } 152*40947Sbostic 153*40947Sbostic /* 154*40947Sbostic * -depth functions -- 155*40947Sbostic * 156*40947Sbostic * Always true, causes descent of the directory hierarchy to be done 157*40947Sbostic * so that all entries in a directory are acted on before the directory 158*40947Sbostic * itself. 159*40947Sbostic */ 160*40947Sbostic /* ARGSUSED */ 161*40947Sbostic f_always_true(plan, entry) 162*40947Sbostic PLAN *plan; 163*40947Sbostic FTSENT *entry; 164*40947Sbostic { 165*40947Sbostic return(FIND_TRUE); 166*40947Sbostic } 167*40947Sbostic 168*40947Sbostic PLAN * 169*40947Sbostic c_depth() 170*40947Sbostic { 171*40947Sbostic extern int depth; 172*40947Sbostic PLAN *new; 173*40947Sbostic 174*40947Sbostic depth = 1; 175*40947Sbostic 176*40947Sbostic NEW(T_DEPTH, f_always_true); 177*40947Sbostic return(new); 178*40947Sbostic } 179*40947Sbostic 180*40947Sbostic /* 181*40947Sbostic * [-exec | -ok] utility [arg ... ] ; functions -- 182*40947Sbostic * 183*40947Sbostic * True if the executed utility returns a zero value as exit status. 184*40947Sbostic * The end of the primary expression is delimited by a semicolon. If 185*40947Sbostic * "{}" occurs anywhere, it gets replaced by the current pathname. 186*40947Sbostic * The current directory for the execution of utility is the same as 187*40947Sbostic * the current directory when the find utility was started. 188*40947Sbostic * 189*40947Sbostic * The primary -ok is different in that it requests affirmation of the 190*40947Sbostic * user before executing the utility. 191*40947Sbostic */ 192*40947Sbostic f_exec(plan, entry) 193*40947Sbostic register PLAN *plan; 194*40947Sbostic FTSENT *entry; 195*40947Sbostic { 196*40947Sbostic register int cnt; 197*40947Sbostic char *find_subst(); 198*40947Sbostic union wait pstat; 199*40947Sbostic pid_t pid, waitpid(); 200*40947Sbostic 201*40947Sbostic for (cnt = 0; plan->e_argv[cnt]; ++cnt) 202*40947Sbostic if (plan->e_len[cnt]) 203*40947Sbostic find_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 204*40947Sbostic entry->path, plan->e_len[cnt]); 205*40947Sbostic 206*40947Sbostic if (plan->flags && !find_queryuser(plan->e_argv)) 207*40947Sbostic return(FIND_FALSE); 208*40947Sbostic 209*40947Sbostic switch(pid = vfork()) { 210*40947Sbostic case -1: 211*40947Sbostic (void)fprintf(stderr, "find: fork: %s.\n", strerror(errno)); 212*40947Sbostic exit(1); 213*40947Sbostic /* NOTREACHED */ 214*40947Sbostic case 0: 215*40947Sbostic execvp(plan->e_argv[0], plan->e_argv); 216*40947Sbostic (void)fprintf(stderr, 217*40947Sbostic "find: %s: %s.\n", plan->e_argv[0], strerror(errno)); 218*40947Sbostic exit(1); 219*40947Sbostic /* NOTREACHED */ 220*40947Sbostic } 221*40947Sbostic pid = waitpid(pid, &pstat, 0); 222*40947Sbostic return(pid == -1 || pstat.w_status ? FIND_FALSE : FIND_TRUE); 223*40947Sbostic } 224*40947Sbostic 225*40947Sbostic /* 226*40947Sbostic * c_exec -- 227*40947Sbostic * build three parallel arrays, one with pointers to the strings passed 228*40947Sbostic * on the command line, one with (possibly duplicated) pointers to the 229*40947Sbostic * argv array, and one with integer values that are lengths of the 230*40947Sbostic * strings, but also flags meaning that the string has to be massaged. 231*40947Sbostic */ 232*40947Sbostic PLAN * 233*40947Sbostic c_exec(argvp, isok) 234*40947Sbostic char ***argvp; 235*40947Sbostic int isok; 236*40947Sbostic { 237*40947Sbostic PLAN *new; /* node returned */ 238*40947Sbostic register int cnt; 239*40947Sbostic register char **argv, **ap, *p; 240*40947Sbostic 241*40947Sbostic ftsoptions |= FTS_NOCHDIR; 242*40947Sbostic output_specified = 1; 243*40947Sbostic 244*40947Sbostic NEW(T_EXEC, f_exec); 245*40947Sbostic new->flags = isok; 246*40947Sbostic 247*40947Sbostic for (ap = argv = *argvp;; ++ap) { 248*40947Sbostic if (!*ap) 249*40947Sbostic bad_arg(isok ? "-ok" : "-exec", "no terminating \";\""); 250*40947Sbostic if (**ap == ';') 251*40947Sbostic break; 252*40947Sbostic } 253*40947Sbostic 254*40947Sbostic cnt = ap - *argvp + 1; 255*40947Sbostic new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 256*40947Sbostic new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 257*40947Sbostic new->e_len = (int *)emalloc((u_int)cnt * sizeof(u_char)); 258*40947Sbostic 259*40947Sbostic for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 260*40947Sbostic new->e_orig[cnt] = *argv; 261*40947Sbostic for (p = *argv; *p; ++p) 262*40947Sbostic if (p[0] == '{' && p[1] == '}') { 263*40947Sbostic new->e_argv[cnt] = emalloc((u_int)1024); 264*40947Sbostic new->e_len[cnt] = 1024; 265*40947Sbostic break; 266*40947Sbostic } 267*40947Sbostic if (!*p) { 268*40947Sbostic new->e_argv[cnt] = *argv; 269*40947Sbostic new->e_len[cnt] = 0; 270*40947Sbostic } 271*40947Sbostic } 272*40947Sbostic new->e_argv[cnt] = new->e_orig[cnt] = NULL; 273*40947Sbostic 274*40947Sbostic *argvp = argv + 1; 275*40947Sbostic return(new); 276*40947Sbostic } 277*40947Sbostic 278*40947Sbostic /* 279*40947Sbostic * -follow functions -- 280*40947Sbostic * 281*40947Sbostic * Always true, causes symbolic links to be followed on a global 282*40947Sbostic * basis. 283*40947Sbostic */ 284*40947Sbostic PLAN * 285*40947Sbostic c_follow() 286*40947Sbostic { 287*40947Sbostic PLAN *new; 288*40947Sbostic 289*40947Sbostic ftsoptions &= ~FTS_PHYSICAL; 290*40947Sbostic ftsoptions |= FTS_LOGICAL; 291*40947Sbostic 292*40947Sbostic NEW(T_FOLLOW, f_always_true); 293*40947Sbostic return(new); 294*40947Sbostic } 295*40947Sbostic 296*40947Sbostic /* 297*40947Sbostic * -fstype functions -- 298*40947Sbostic * 299*40947Sbostic * True if the file is of a certain type. 300*40947Sbostic */ 301*40947Sbostic f_fstype(plan, entry) 302*40947Sbostic PLAN *plan; 303*40947Sbostic FTSENT *entry; 304*40947Sbostic { 305*40947Sbostic extern dev_t curdev; 306*40947Sbostic struct statfs sb; 307*40947Sbostic static short curtype; 308*40947Sbostic 309*40947Sbostic /* only check when we cross mount point */ 310*40947Sbostic if (curdev != entry->statb.st_dev) { 311*40947Sbostic if (statfs(entry->name, &sb)) { 312*40947Sbostic (void)fprintf(stderr, "find: %s: %s.\n", 313*40947Sbostic entry->name, strerror(errno)); 314*40947Sbostic exit(1); 315*40947Sbostic } 316*40947Sbostic curtype = sb.f_type; 317*40947Sbostic } 318*40947Sbostic return(plan->flags == curtype); 319*40947Sbostic } 320*40947Sbostic 321*40947Sbostic PLAN * 322*40947Sbostic c_fstype(arg) 323*40947Sbostic char *arg; 324*40947Sbostic { 325*40947Sbostic register PLAN *new; 326*40947Sbostic 327*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 328*40947Sbostic 329*40947Sbostic NEW(T_FSTYPE, f_fstype); 330*40947Sbostic switch(*arg) { 331*40947Sbostic case 'm': 332*40947Sbostic if (!strcmp(arg, "mfs")) { 333*40947Sbostic new->flags = MOUNT_MFS; 334*40947Sbostic return(new); 335*40947Sbostic } 336*40947Sbostic break; 337*40947Sbostic case 'n': 338*40947Sbostic if (!strcmp(arg, "nfs")) { 339*40947Sbostic new->flags = MOUNT_NFS; 340*40947Sbostic return(new); 341*40947Sbostic } 342*40947Sbostic break; 343*40947Sbostic case 'p': 344*40947Sbostic if (!strcmp(arg, "pc")) { 345*40947Sbostic new->flags = MOUNT_PC; 346*40947Sbostic return(new); 347*40947Sbostic } 348*40947Sbostic break; 349*40947Sbostic case 'u': 350*40947Sbostic if (!strcmp(arg, "ufs")) { 351*40947Sbostic new->flags = MOUNT_UFS; 352*40947Sbostic return(new); 353*40947Sbostic } 354*40947Sbostic break; 355*40947Sbostic } 356*40947Sbostic (void)fprintf(stderr, "find: unknown file type %s.\n", arg); 357*40947Sbostic exit(1); 358*40947Sbostic /* NOTREACHED */ 359*40947Sbostic } 360*40947Sbostic 361*40947Sbostic /* 362*40947Sbostic * -group gname functions -- 363*40947Sbostic * 364*40947Sbostic * True if the file belongs to the group gname. If gname is numeric and 365*40947Sbostic * an equivalent of the getgrnam() function does not return a valid group 366*40947Sbostic * name, gname is taken as a group ID. 367*40947Sbostic */ 368*40947Sbostic f_group(plan, entry) 369*40947Sbostic PLAN *plan; 370*40947Sbostic FTSENT *entry; 371*40947Sbostic { 372*40947Sbostic return(entry->statb.st_gid == plan->g_data ? FIND_TRUE : FIND_FALSE); 373*40947Sbostic } 374*40947Sbostic 375*40947Sbostic PLAN * 376*40947Sbostic c_group(gname) 377*40947Sbostic char *gname; 378*40947Sbostic { 379*40947Sbostic PLAN *new; 380*40947Sbostic struct group *g; 381*40947Sbostic gid_t gid; 382*40947Sbostic 383*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 384*40947Sbostic 385*40947Sbostic g = getgrnam(gname); 386*40947Sbostic if (g == NULL) { 387*40947Sbostic gid = atoi(gname); 388*40947Sbostic if (gid == 0 && gname[0] != '0') 389*40947Sbostic bad_arg("-group", "no such group"); 390*40947Sbostic } else 391*40947Sbostic gid = g->gr_gid; 392*40947Sbostic 393*40947Sbostic NEW(T_GROUP, f_group); 394*40947Sbostic new->g_data = gid; 395*40947Sbostic return(new); 396*40947Sbostic } 397*40947Sbostic 398*40947Sbostic /* 399*40947Sbostic * -inum n functions -- 400*40947Sbostic * 401*40947Sbostic * True if the file has inode # n. 402*40947Sbostic */ 403*40947Sbostic f_inum(plan, entry) 404*40947Sbostic PLAN *plan; 405*40947Sbostic FTSENT *entry; 406*40947Sbostic { 407*40947Sbostic COMPARE(entry->statb.st_ino, plan->i_data); 408*40947Sbostic } 409*40947Sbostic 410*40947Sbostic PLAN * 411*40947Sbostic c_inum(arg) 412*40947Sbostic char *arg; 413*40947Sbostic { 414*40947Sbostic PLAN *new; 415*40947Sbostic 416*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 417*40947Sbostic 418*40947Sbostic NEW(T_INUM, f_inum); 419*40947Sbostic new->i_data = find_parsenum(new, "-inum", arg, (char *)NULL); 420*40947Sbostic return(new); 421*40947Sbostic } 422*40947Sbostic 423*40947Sbostic /* 424*40947Sbostic * -links n functions -- 425*40947Sbostic * 426*40947Sbostic * True if the file has n links. 427*40947Sbostic */ 428*40947Sbostic f_links(plan, entry) 429*40947Sbostic PLAN *plan; 430*40947Sbostic FTSENT *entry; 431*40947Sbostic { 432*40947Sbostic COMPARE(entry->statb.st_nlink, plan->l_data); 433*40947Sbostic } 434*40947Sbostic 435*40947Sbostic PLAN * 436*40947Sbostic c_links(arg) 437*40947Sbostic char *arg; 438*40947Sbostic { 439*40947Sbostic PLAN *new; 440*40947Sbostic 441*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 442*40947Sbostic 443*40947Sbostic NEW(T_LINKS, f_links); 444*40947Sbostic new->l_data = find_parsenum(new, "-links", arg, (char *)NULL); 445*40947Sbostic return(new); 446*40947Sbostic } 447*40947Sbostic 448*40947Sbostic /* 449*40947Sbostic * -ls functions -- 450*40947Sbostic * 451*40947Sbostic * Always true - prints the current entry to stdout in "ls" format. 452*40947Sbostic */ 453*40947Sbostic /* ARGSUSED */ 454*40947Sbostic f_ls(plan, entry) 455*40947Sbostic PLAN *plan; 456*40947Sbostic FTSENT *entry; 457*40947Sbostic { 458*40947Sbostic printlong(entry->path, entry->accpath, &entry->statb); 459*40947Sbostic return(FIND_TRUE); 460*40947Sbostic } 461*40947Sbostic 462*40947Sbostic PLAN * 463*40947Sbostic c_ls() 464*40947Sbostic { 465*40947Sbostic PLAN *new; 466*40947Sbostic 467*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 468*40947Sbostic output_specified = 1; 469*40947Sbostic 470*40947Sbostic NEW(T_LS, f_ls); 471*40947Sbostic return(new); 472*40947Sbostic } 473*40947Sbostic 474*40947Sbostic /* 475*40947Sbostic * -name functions -- 476*40947Sbostic * 477*40947Sbostic * True if the basename of the filename being examined 478*40947Sbostic * matches pattern using Pattern Matching Notation S3.14 479*40947Sbostic */ 480*40947Sbostic f_name(plan, entry) 481*40947Sbostic PLAN *plan; 482*40947Sbostic FTSENT *entry; 483*40947Sbostic { 484*40947Sbostic return(fnmatch(plan->c_data, entry->name, FNM_QUOTE) ? 485*40947Sbostic FIND_TRUE : FIND_FALSE); 486*40947Sbostic } 487*40947Sbostic 488*40947Sbostic PLAN * 489*40947Sbostic c_name(pattern) 490*40947Sbostic char *pattern; 491*40947Sbostic { 492*40947Sbostic PLAN *new; 493*40947Sbostic 494*40947Sbostic NEW(T_NAME, f_name); 495*40947Sbostic new->c_data = pattern; 496*40947Sbostic return(new); 497*40947Sbostic } 498*40947Sbostic 499*40947Sbostic /* 500*40947Sbostic * -newer file functions -- 501*40947Sbostic * 502*40947Sbostic * True if the current file has been modified more recently 503*40947Sbostic * then the modification time of the file named by the pathname 504*40947Sbostic * file. 505*40947Sbostic */ 506*40947Sbostic f_newer(plan, entry) 507*40947Sbostic PLAN *plan; 508*40947Sbostic FTSENT *entry; 509*40947Sbostic { 510*40947Sbostic return(entry->statb.st_mtime > plan->t_data ? FIND_TRUE : FIND_FALSE); 511*40947Sbostic } 512*40947Sbostic 513*40947Sbostic PLAN * 514*40947Sbostic c_newer(filename) 515*40947Sbostic char *filename; 516*40947Sbostic { 517*40947Sbostic PLAN *new; 518*40947Sbostic struct stat sb; 519*40947Sbostic 520*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 521*40947Sbostic 522*40947Sbostic if (stat(filename, &sb)) { 523*40947Sbostic (void)fprintf(stderr, "find: %s: %s.\n", 524*40947Sbostic filename, strerror(errno)); 525*40947Sbostic exit(1); 526*40947Sbostic } 527*40947Sbostic NEW(T_NEWER, f_newer); 528*40947Sbostic new->t_data = sb.st_mtime; 529*40947Sbostic return(new); 530*40947Sbostic } 531*40947Sbostic 532*40947Sbostic /* 533*40947Sbostic * -nogroup functions -- 534*40947Sbostic * 535*40947Sbostic * True if file belongs to a user ID for which the equivalent 536*40947Sbostic * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 537*40947Sbostic */ 538*40947Sbostic /* ARGSUSED */ 539*40947Sbostic f_nogroup(plan, entry) 540*40947Sbostic PLAN *plan; 541*40947Sbostic FTSENT *entry; 542*40947Sbostic { 543*40947Sbostic return(group_from_gid(entry->statb.st_gid, 1) ? FIND_FALSE : FIND_TRUE); 544*40947Sbostic } 545*40947Sbostic 546*40947Sbostic PLAN * 547*40947Sbostic c_nogroup() 548*40947Sbostic { 549*40947Sbostic PLAN *new; 550*40947Sbostic 551*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 552*40947Sbostic 553*40947Sbostic NEW(T_NOGROUP, f_nogroup); 554*40947Sbostic return(new); 555*40947Sbostic } 556*40947Sbostic 557*40947Sbostic /* 558*40947Sbostic * -nouser functions -- 559*40947Sbostic * 560*40947Sbostic * True if file belongs to a user ID for which the equivalent 561*40947Sbostic * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 562*40947Sbostic */ 563*40947Sbostic /* ARGSUSED */ 564*40947Sbostic f_nouser(plan, entry) 565*40947Sbostic PLAN *plan; 566*40947Sbostic FTSENT *entry; 567*40947Sbostic { 568*40947Sbostic return(user_from_uid(entry->statb.st_uid, 1) ? FIND_FALSE : FIND_TRUE); 569*40947Sbostic } 570*40947Sbostic 571*40947Sbostic PLAN * 572*40947Sbostic c_nouser() 573*40947Sbostic { 574*40947Sbostic PLAN *new; 575*40947Sbostic 576*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 577*40947Sbostic 578*40947Sbostic NEW(T_NOUSER, f_nouser); 579*40947Sbostic return(new); 580*40947Sbostic } 581*40947Sbostic 582*40947Sbostic /* 583*40947Sbostic * -perm functions -- 584*40947Sbostic * 585*40947Sbostic * The mode argument is used to represent file mode bits. If it starts 586*40947Sbostic * with a leading digit, it's treated as an octal mode, otherwise as a 587*40947Sbostic * symbolic mode. 588*40947Sbostic */ 589*40947Sbostic f_perm(plan, entry) 590*40947Sbostic PLAN *plan; 591*40947Sbostic FTSENT *entry; 592*40947Sbostic { 593*40947Sbostic mode_t mode; 594*40947Sbostic 595*40947Sbostic mode = entry->statb.st_mode & 596*40947Sbostic (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 597*40947Sbostic if (plan->flags) 598*40947Sbostic return((plan->m_data | mode) == mode); 599*40947Sbostic else 600*40947Sbostic return(mode == plan->m_data); 601*40947Sbostic /* NOTREACHED */ 602*40947Sbostic } 603*40947Sbostic 604*40947Sbostic PLAN * 605*40947Sbostic c_perm(perm) 606*40947Sbostic char *perm; 607*40947Sbostic { 608*40947Sbostic PLAN *new; 609*40947Sbostic 610*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 611*40947Sbostic 612*40947Sbostic NEW(T_PERM, f_perm); 613*40947Sbostic 614*40947Sbostic if (*perm == '-') { 615*40947Sbostic new->flags = 1; 616*40947Sbostic ++perm; 617*40947Sbostic } 618*40947Sbostic 619*40947Sbostic if (setmode(perm)) 620*40947Sbostic bad_arg("-perm", "illegal mode string"); 621*40947Sbostic 622*40947Sbostic new->m_data = getmode(0); 623*40947Sbostic return(new); 624*40947Sbostic } 625*40947Sbostic 626*40947Sbostic /* 627*40947Sbostic * -print functions -- 628*40947Sbostic * 629*40947Sbostic * Always true, causes the current pathame to be written to 630*40947Sbostic * standard output. 631*40947Sbostic */ 632*40947Sbostic /* ARGSUSED */ 633*40947Sbostic f_print(plan, entry) 634*40947Sbostic PLAN *plan; 635*40947Sbostic FTSENT *entry; 636*40947Sbostic { 637*40947Sbostic (void)printf("%s\n", entry->path); 638*40947Sbostic return(FIND_TRUE); 639*40947Sbostic } 640*40947Sbostic 641*40947Sbostic PLAN * 642*40947Sbostic c_print() 643*40947Sbostic { 644*40947Sbostic PLAN *new; 645*40947Sbostic 646*40947Sbostic output_specified = 1; 647*40947Sbostic 648*40947Sbostic NEW(T_PRINT, f_print); 649*40947Sbostic return(new); 650*40947Sbostic } 651*40947Sbostic 652*40947Sbostic /* 653*40947Sbostic * -prune functions -- 654*40947Sbostic * 655*40947Sbostic * Prune a portion of the hierarchy. 656*40947Sbostic */ 657*40947Sbostic /* ARGSUSED */ 658*40947Sbostic f_prune(plan, entry) 659*40947Sbostic PLAN *plan; 660*40947Sbostic FTSENT *entry; 661*40947Sbostic { 662*40947Sbostic extern FTS *tree; 663*40947Sbostic 664*40947Sbostic if (ftsset(tree, entry, FTS_SKIP)) { 665*40947Sbostic (void)fprintf(stderr, 666*40947Sbostic "find: %s: %s.\n", entry->path, strerror(errno)); 667*40947Sbostic exit(1); 668*40947Sbostic } 669*40947Sbostic return(FIND_TRUE); 670*40947Sbostic } 671*40947Sbostic 672*40947Sbostic PLAN * 673*40947Sbostic c_prune() 674*40947Sbostic { 675*40947Sbostic PLAN *new; 676*40947Sbostic 677*40947Sbostic NEW(T_PRUNE, f_prune); 678*40947Sbostic return(new); 679*40947Sbostic } 680*40947Sbostic 681*40947Sbostic /* 682*40947Sbostic * -size n[c] functions -- 683*40947Sbostic * 684*40947Sbostic * True if the file size in bytes, divided by an implementation defined 685*40947Sbostic * value and rounded up to the next integer, is n. If n is followed by 686*40947Sbostic * a c, the size is in bytes. 687*40947Sbostic */ 688*40947Sbostic #define FIND_SIZE 512 689*40947Sbostic static int divsize = 1; 690*40947Sbostic 691*40947Sbostic f_size(plan, entry) 692*40947Sbostic PLAN *plan; 693*40947Sbostic FTSENT *entry; 694*40947Sbostic { 695*40947Sbostic off_t size; 696*40947Sbostic 697*40947Sbostic size = divsize ? 698*40947Sbostic (entry->statb.st_size + FIND_SIZE - 1) / FIND_SIZE : 699*40947Sbostic entry->statb.st_size; 700*40947Sbostic COMPARE(size, plan->o_data); 701*40947Sbostic } 702*40947Sbostic 703*40947Sbostic PLAN * 704*40947Sbostic c_size(arg) 705*40947Sbostic char *arg; 706*40947Sbostic { 707*40947Sbostic PLAN *new; 708*40947Sbostic char endch; 709*40947Sbostic 710*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 711*40947Sbostic 712*40947Sbostic NEW(T_SIZE, f_size); 713*40947Sbostic new->o_data = find_parsenum(new, "-size", arg, &endch); 714*40947Sbostic if (endch == 'c') 715*40947Sbostic divsize = 0; 716*40947Sbostic return(new); 717*40947Sbostic } 718*40947Sbostic 719*40947Sbostic /* 720*40947Sbostic * -type c functions -- 721*40947Sbostic * 722*40947Sbostic * True if the type of the file is c, where c is b, c, d, p, or f for 723*40947Sbostic * block special file, character special file, directory, FIFO, or 724*40947Sbostic * regular file, respectively. 725*40947Sbostic */ 726*40947Sbostic f_type(plan, entry) 727*40947Sbostic PLAN *plan; 728*40947Sbostic FTSENT *entry; 729*40947Sbostic { 730*40947Sbostic return(entry->statb.st_mode & plan->m_data ? FIND_TRUE : FIND_FALSE); 731*40947Sbostic } 732*40947Sbostic 733*40947Sbostic PLAN * 734*40947Sbostic c_type(typestring) 735*40947Sbostic char *typestring; 736*40947Sbostic { 737*40947Sbostic PLAN *new; 738*40947Sbostic mode_t mask; 739*40947Sbostic 740*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 741*40947Sbostic 742*40947Sbostic switch (typestring[0]) { 743*40947Sbostic case 'b': 744*40947Sbostic mask = S_IFBLK; 745*40947Sbostic break; 746*40947Sbostic case 'c': 747*40947Sbostic mask = S_IFCHR; 748*40947Sbostic break; 749*40947Sbostic case 'd': 750*40947Sbostic mask = S_IFDIR; 751*40947Sbostic break; 752*40947Sbostic case 'f': 753*40947Sbostic mask = S_IFREG; 754*40947Sbostic break; 755*40947Sbostic case 'l': 756*40947Sbostic mask = S_IFLNK; 757*40947Sbostic break; 758*40947Sbostic case 'p': 759*40947Sbostic mask = S_IFIFO; 760*40947Sbostic break; 761*40947Sbostic case 's': 762*40947Sbostic mask = S_IFSOCK; 763*40947Sbostic break; 764*40947Sbostic default: 765*40947Sbostic bad_arg("-type", "unknown type"); 766*40947Sbostic } 767*40947Sbostic 768*40947Sbostic NEW(T_TYPE, f_type); 769*40947Sbostic new->m_data = mask; 770*40947Sbostic return(new); 771*40947Sbostic } 772*40947Sbostic 773*40947Sbostic /* 774*40947Sbostic * -user uname functions -- 775*40947Sbostic * 776*40947Sbostic * True if the file belongs to the user uname. If uname is numeric and 777*40947Sbostic * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 778*40947Sbostic * return a valid user name, uname is taken as a user ID. 779*40947Sbostic */ 780*40947Sbostic f_user(plan, entry) 781*40947Sbostic PLAN *plan; 782*40947Sbostic FTSENT *entry; 783*40947Sbostic { 784*40947Sbostic return(entry->statb.st_uid == plan->u_data ? FIND_TRUE : FIND_FALSE); 785*40947Sbostic } 786*40947Sbostic 787*40947Sbostic PLAN * 788*40947Sbostic c_user(username) 789*40947Sbostic char *username; 790*40947Sbostic { 791*40947Sbostic PLAN *new; 792*40947Sbostic struct passwd *p; 793*40947Sbostic uid_t uid; 794*40947Sbostic 795*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 796*40947Sbostic 797*40947Sbostic p = getpwnam(username); 798*40947Sbostic if (p == NULL) { 799*40947Sbostic uid = atoi(username); 800*40947Sbostic if (uid == 0 && username[0] != '0') 801*40947Sbostic bad_arg("-user", "no such user"); 802*40947Sbostic } else 803*40947Sbostic uid = p->pw_uid; 804*40947Sbostic 805*40947Sbostic NEW(T_USER, f_user); 806*40947Sbostic new->u_data = uid; 807*40947Sbostic return(new); 808*40947Sbostic } 809*40947Sbostic 810*40947Sbostic /* 811*40947Sbostic * -xdev functions -- 812*40947Sbostic * 813*40947Sbostic * Always true, causes find not to decend past directories that have a 814*40947Sbostic * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 815*40947Sbostic * 816*40947Sbostic * Note: this checking is done in find_execute(). 817*40947Sbostic */ 818*40947Sbostic PLAN * 819*40947Sbostic c_xdev() 820*40947Sbostic { 821*40947Sbostic extern int xdev; 822*40947Sbostic PLAN *new; 823*40947Sbostic 824*40947Sbostic xdev = 1; 825*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 826*40947Sbostic 827*40947Sbostic NEW(T_XDEV, f_always_true); 828*40947Sbostic return(new); 829*40947Sbostic } 830*40947Sbostic 831*40947Sbostic /* 832*40947Sbostic * ( expression ) functions -- 833*40947Sbostic * 834*40947Sbostic * True if expression is true. 835*40947Sbostic */ 836*40947Sbostic f_expr(plan, entry) 837*40947Sbostic PLAN *plan; 838*40947Sbostic FTSENT *entry; 839*40947Sbostic { 840*40947Sbostic register PLAN *p; 841*40947Sbostic register int state; 842*40947Sbostic 843*40947Sbostic for (p = plan->p_data[0]; 844*40947Sbostic p && (state = (p->eval)(p, entry)); p = p->next); 845*40947Sbostic return(state); 846*40947Sbostic } 847*40947Sbostic 848*40947Sbostic /* 849*40947Sbostic * T_OPENPAREN and T_CLOSEPAREN nodes are temporary place markers. They are 850*40947Sbostic * eliminated during phase 2 of find_formplan() --- the '(' node is converted 851*40947Sbostic * to a T_EXPR node containing the expression and the ')' node is discarded. 852*40947Sbostic */ 853*40947Sbostic PLAN * 854*40947Sbostic c_openparen() 855*40947Sbostic { 856*40947Sbostic PLAN *new; 857*40947Sbostic 858*40947Sbostic NEW(T_OPENPAREN, (int (*)())-1); 859*40947Sbostic return(new); 860*40947Sbostic } 861*40947Sbostic 862*40947Sbostic PLAN * 863*40947Sbostic c_closeparen() 864*40947Sbostic { 865*40947Sbostic PLAN *new; 866*40947Sbostic 867*40947Sbostic NEW(T_CLOSEPAREN, (int (*)())-1); 868*40947Sbostic return(new); 869*40947Sbostic } 870*40947Sbostic 871*40947Sbostic /* 872*40947Sbostic * -mtime n functions -- 873*40947Sbostic * 874*40947Sbostic * True if the difference between the file modification time and the 875*40947Sbostic * current time is n 24 hour periods. 876*40947Sbostic */ 877*40947Sbostic f_mtime(plan, entry) 878*40947Sbostic PLAN *plan; 879*40947Sbostic FTSENT *entry; 880*40947Sbostic { 881*40947Sbostic extern time_t now; 882*40947Sbostic 883*40947Sbostic COMPARE((now - entry->statb.st_mtime + SECSPERDAY - 1) / SECSPERDAY, 884*40947Sbostic plan->t_data); 885*40947Sbostic } 886*40947Sbostic 887*40947Sbostic PLAN * 888*40947Sbostic c_mtime(arg) 889*40947Sbostic char *arg; 890*40947Sbostic { 891*40947Sbostic PLAN *new; 892*40947Sbostic 893*40947Sbostic ftsoptions &= ~FTS_NOSTAT; 894*40947Sbostic 895*40947Sbostic NEW(T_MTIME, f_mtime); 896*40947Sbostic new->t_data = find_parsenum(new, "-mtime", arg, (char *)NULL); 897*40947Sbostic return(new); 898*40947Sbostic } 899*40947Sbostic 900*40947Sbostic /* 901*40947Sbostic * ! expression functions -- 902*40947Sbostic * 903*40947Sbostic * Negation of a primary; the unary NOT operator. 904*40947Sbostic */ 905*40947Sbostic f_not(plan, entry) 906*40947Sbostic PLAN *plan; 907*40947Sbostic FTSENT *entry; 908*40947Sbostic { 909*40947Sbostic register PLAN *p; 910*40947Sbostic register int state; 911*40947Sbostic 912*40947Sbostic for (p = plan->p_data[0]; 913*40947Sbostic p && (state = (p->eval)(p, entry)); p = p->next); 914*40947Sbostic return(!state); 915*40947Sbostic } 916*40947Sbostic 917*40947Sbostic PLAN * 918*40947Sbostic c_not() 919*40947Sbostic { 920*40947Sbostic PLAN *new; 921*40947Sbostic 922*40947Sbostic NEW(T_NOT, f_not); 923*40947Sbostic return(new); 924*40947Sbostic } 925*40947Sbostic 926*40947Sbostic /* 927*40947Sbostic * expression -o expression functions -- 928*40947Sbostic * 929*40947Sbostic * Alternation of primaries; the OR operator. The second expression is 930*40947Sbostic * not evaluated if the first expression is true. 931*40947Sbostic */ 932*40947Sbostic f_or(plan, entry) 933*40947Sbostic PLAN *plan; 934*40947Sbostic FTSENT *entry; 935*40947Sbostic { 936*40947Sbostic register PLAN *p; 937*40947Sbostic register int state; 938*40947Sbostic 939*40947Sbostic for (p = plan->p_data[0]; 940*40947Sbostic p && (state = (p->eval)(p, entry)); p = p->next); 941*40947Sbostic 942*40947Sbostic if (state) 943*40947Sbostic return(FIND_TRUE); 944*40947Sbostic 945*40947Sbostic for (p = plan->p_data[1]; 946*40947Sbostic p && (state = (p->eval)(p, entry)); p = p->next); 947*40947Sbostic return(state); 948*40947Sbostic } 949*40947Sbostic 950*40947Sbostic PLAN * 951*40947Sbostic c_or() 952*40947Sbostic { 953*40947Sbostic PLAN *new; 954*40947Sbostic 955*40947Sbostic NEW(T_OR, f_or); 956*40947Sbostic return(new); 957*40947Sbostic } 958