/*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "tweak.h" #ifdef HAVE_UNISTD_H #ifndef __hpux #include #endif #endif #include #ifdef STDC_HEADERS #include #endif #include "my-string.h" #include #include #include #ifdef _POSIX_SOURCE #include "limits.h" #if !defined(MAXPATHLEN) && defined(PATH_MAX) #define MAXPATHLEN PATH_MAX #endif #endif #ifdef HAVE_TZFILE_H #include #endif #include "common_def.h" #include "client_def.h" #include "c_extern.h" #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #ifdef HAVE_SYS_WAIT_H #include #endif #ifndef WEXITSTATUS #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED #define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif #include "find.h" #define FIND_EQUAL 0 #define FIND_LESSTHAN 1 #define FIND_GREATER 2 #define COMPARE(a, b) { \ switch(plan->flags) { \ case FIND_EQUAL: \ return(a == b); \ case FIND_LESSTHAN: \ return(a < b); \ case FIND_GREATER: \ return(a > b); \ } \ return(0); \ } static PLAN *palloc (enum ntype t, int (*f)()) { PLAN *new; new = (PLAN *) malloc(sizeof(PLAN)); if ( new ) { new->type = t; new->eval = f; new->flags = 0; new->next = NULL; return(new); } perror("palloc"); exit(EX_OSERR); } extern int isoutput; extern int process; /* * find_parsenum -- * Parse a string of the form [+-]# and return the value. */ static long find_parsenum (PLAN * plan, const char * option, char * str, char * endch) { long value; char *endchar; /* pointer to character ending conversion */ /* determine comparison from leading + or - */ switch(*str) { case '+': ++str; plan->flags = FIND_GREATER; break; case '-': ++str; plan->flags = FIND_LESSTHAN; break; default: plan->flags = FIND_EQUAL; break; } /* * convert the string with strtol(). Note, if strtol() returns zero * and endchar points to the beginning of the string we know we have * a syntax error. */ value = strtol(str, &endchar, 10); if ( (!value && endchar == str) || (endchar[0] && (!endch || endchar[0] != *endch))) { fprintf(stderr,"%s: %s", option, "illegal numeric value"); exit(EX_USAGE); } if (endch) *endch = endchar[0]; return(value); } /* * -time n functions -- * * True if the difference between the file time and the * current time is n 24 hour periods. * */ extern time_t now; static int find_time (PLAN * plan, struct stat * sbuf, char * path) { /* with FSP sbuf.st_atime=sbuf.st_ctime=sbuf.st_mtime */ COMPARE((now - sbuf->st_atime + SECSPERDAY - 1) / SECSPERDAY, plan->t_data); } PLAN * c_time (char * arg) { PLAN *new; new = palloc(N_TIME, find_time); new->t_data = find_parsenum(new, "-time", arg, NULL); return(new); } /* * brace_subst -- * Replace occurrences of {} in s1 with s2 and return the result string. */ static void brace_subst (char * orig, char ** store, char * path, int len) { register int plen; register char ch, *p; plen = strlen(path); for (p = *store; (ch = *orig) ; ++orig) if (ch == '{' && orig[1] == '}') { while ((p - *store) + plen > len) if (!(*store = (char *)realloc(*store, len *= 2))) { perror("realloc"); client_done(); exit(EX_OSERR); } bcopy(path, p, plen); p += plen; ++orig; } else *p++ = ch; *p = '\0'; } /* * queryuser -- * print a message to standard error and then read input from standard * input. If the input is 'y' then 1 is returned. */ static int queryuser (char ** argv) { int ch, first, nl; fprintf(stderr, "\"%s", *argv); while (*++argv) fprintf(stderr, " %s", *argv); fprintf(stderr, "\"? "); fflush(stderr); first = ch = getchar(); for (nl = 0;;) { if (ch == '\n') { nl = 1; break; } if (ch == EOF) break; ch = getchar(); } if (!nl) { fprintf(stderr, "\n"); fflush(stderr); } return(first == 'y'); } /* * [-exec | -ok] utility [arg ... ] ; functions -- * * True if the executed utility returns a zero value as exit status. * The end of the primary expression is delimited by a semicolon. If * "{}" occurs anywhere, it gets replaced by the current pathname. * The current directory for the execution of utility is the same as * the current directory when the find utility was started. * * The primary -ok is different in that it requests affirmation of the * user before executing the utility. */ static int find_exec (PLAN * plan, struct stat * sbuf, char * path) { register int cnt; pid_t pid; #ifndef HAVE_UNION_WAIT int status; #else union wait status; #endif for (cnt = 0; plan->e_argv[cnt]; ++cnt) if (plan->e_len[cnt]) brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], path, plan->e_len[cnt]); if (plan->flags && !queryuser(plan->e_argv)) return(0); switch(pid = fork()) { case -1: perror ("fork"); exit(EX_OSERR); case 0: execvp(plan->e_argv[0], plan->e_argv); perror ("execvp"); exit(EX_OSERR); } pid = wait(&status); return(pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); } static char *emalloc_ffind (unsigned int len) { char *p; if ( (p = (char *)malloc(len))) return((char *)p); perror("malloc"); exit(EX_OSERR); } /* * c_exec -- * build three parallel arrays, one with pointers to the strings passed * on the command line, one with (possibly duplicated) pointers to the * argv array, and one with integer values that are lengths of the * strings, but also flags meaning that the string has to be massaged. */ PLAN *c_exec (char *** argvp, int isok) { PLAN *new; /* node returned */ register int cnt; register char **argv, **ap, *p; isoutput = 1; new = palloc(N_EXEC, find_exec); new->flags = isok; for (ap = argv = *argvp;; ++ap) { if (!*ap) { fprintf(stderr,"%s: no terminating", isok ? "-ok" : "-exec"); exit(EX_USAGE); } if (**ap == ';') break; } cnt = ap - *argvp + 1; new->e_argv = (char **)emalloc_ffind((unsigned int)cnt * sizeof(char *)); new->e_orig = (char **)emalloc_ffind((unsigned int)cnt * sizeof(char *)); new->e_len = (int *)emalloc_ffind((unsigned int)cnt * sizeof(int)); for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { new->e_orig[cnt] = *argv; for (p = *argv; *p; ++p) if (p[0] == '{' && p[1] == '}') { new->e_argv[cnt] = (char *)emalloc_ffind((unsigned int)MAXPATHLEN); new->e_len[cnt] = MAXPATHLEN; break; } if (!*p) { new->e_argv[cnt] = *argv; new->e_len[cnt] = 0; } } new->e_argv[cnt] = new->e_orig[cnt] = NULL; *argvp = argv + 1; return(new); } static void printtime_ffind (time_t ftime) { int i; char *longstring; longstring = (char *)ctime(&ftime); for (i = 4; i < 11; ++i) putchar(longstring[i]); #define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) if (ftime + SIXMONTHS > time((time_t *)NULL)) for (i = 11; i < 16; ++i) putchar(longstring[i]); else { (void)putchar(' '); for (i = 20; i < 24; ++i) putchar(longstring[i]); } putchar(' '); } #define BLK(A) (((A)+1023)/1024) static void printlong_ffind (char * name, struct stat * sb) { const char *modep; printf("%4ld ", (long)BLK(sb->st_size)); modep = ((S_IFDIR & sb->st_mode)) ? "drwxrwxrwx" : "-rw-rw-rw-" ; printf("%s %3u %-*s %-*s ", modep, sb->st_nlink, 8, "nobody", 8, "nobody"); printf("%8ld ", (long)sb->st_size); printtime_ffind(sb->st_mtime); printf("%s", name); putchar('\n'); } /* * -ls functions -- * * Always true - prints the current sbuf to stdout in "ls" format. */ static int find_ls (PLAN * plan, struct stat * sbuf, char * path) { printlong_ffind(path, sbuf); return(1); } PLAN *c_ls (void) { isoutput = 1; return(palloc(N_LS, find_ls)); } /* * -name functions -- * * True if the basename of the filename being examined * matches pattern using Pattern Matching Notation S3.14 */ static int find_name (PLAN * plan, struct stat * sbuf, char * path) { register char *name; /* extract filename */ for (name = path + strlen(path); name > path && *name != '/'; name--); if (*name == '/') name++; return(fnmatch(plan->c_data, name)); } PLAN *c_name (char * pattern) { PLAN *new; new = palloc(N_NAME, find_name); new->c_data = pattern; return(new); } /* * -newer file functions -- * * True if the current file has been modified more recently * then the modification time of the file named by the pathname * file. */ static int find_newer (PLAN * plan, struct stat * sbuf, char * path) { return(sbuf->st_mtime > plan->t_data); } PLAN *c_newer (char * filename) { PLAN *new; struct stat sb; if (stat(filename, &sb)) { perror("stat"); exit(EX_NOINPUT); } new = palloc(N_NEWER, find_newer); new->t_data = sb.st_mtime; return(new); } /* * -print functions -- * * Always true, causes the current pathame to be written to * standard output. */ static int find_print (PLAN * plan, struct stat * sbuf, char * path) { (void)printf("%s\n", path); return(1); } PLAN *c_print (void) { isoutput = 1; return(palloc(N_PRINT, find_print)); } /* * -prune functions -- * * Prune a portion of the hierarchy. */ static int find_prune (PLAN * plan, struct stat * sbuf, char * path) { process = -1; return(1); } PLAN *c_prune (void) { return(palloc(N_PRUNE, find_prune)); } /* * -size n[c] functions -- * * True if the file size in bytes, divided by an implementation defined * value and rounded up to the next integer, is n. If n is followed by * a c, the size is in bytes. */ #define FIND_SIZE 512 static int divsize = 1; static int find_size (PLAN * plan, struct stat * sbuf, char * path) { off_t size; size = divsize ? ((sbuf->st_size + FIND_SIZE - 1)/FIND_SIZE) : sbuf->st_size; COMPARE(size, plan->o_data); } PLAN *c_size (char * arg) { PLAN *new; char endch='c'; new = palloc(N_SIZE, find_size); new->o_data = find_parsenum(new, "-size", arg, &endch); if (endch == 'c') divsize = 0; return(new); } /* * -type c functions -- * * True if the type of the file is c, where c is d or f for * directory or regular file, respectively. */ static int find_type (PLAN * plan, struct stat * sbuf, char * path) { return((sbuf->st_mode & S_IFMT) == plan->m_data); } PLAN *c_type (char * typestring) { PLAN *new; mode_t mask; switch (typestring[0]) { case 'd': mask = S_IFDIR; break; case 'f': mask = S_IFREG; break; default: fprintf(stderr,"-type: unknown type"); exit(EX_USAGE); } new = palloc(N_TYPE, find_type); new->m_data = mask; return(new); } /* * ( expression ) functions -- * * True if expression is true. */ int find_expr (PLAN * plan, struct stat * sbuf, char * path) { register PLAN *p; register int state; for(p=plan->p_data[0]; p && (state=(p->eval)(p, sbuf, path)); p=p->next); return(state); } /* * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are * eliminated during phase 2 of find_formplan() --- the '(' node is converted * to a N_EXPR node containing the expression and the ')' node is discarded. */ PLAN *c_openparen (void) { return(palloc(N_OPENPAREN, (int (*)())-1)); } PLAN *c_closeparen (void) { return(palloc(N_CLOSEPAREN, (int (*)())-1)); } /* * ! expression functions -- * * Negation of a primary; the unary NOT operator. */ static int find_not (PLAN * plan, struct stat * sbuf, char * path) { register PLAN *p; register int state; for(p=plan->p_data[0]; p && (state=(p->eval)(p, sbuf, path)); p=p->next); return(!state); } PLAN *c_not (void) { return(palloc(N_NOT, find_not)); } /* * expression -o expression functions -- * * Alternation of primaries; the OR operator. The second expression is * not evaluated if the first expression is true. */ static int find_or (PLAN * plan, struct stat * sbuf, char * path) { register PLAN *p; register int state; for(p=plan->p_data[0]; p && (state=(p->eval)(p, sbuf, path)); p=p->next); if (state) return(1); for(p=plan->p_data[1]; p && (state=(p->eval)(p, sbuf, path)); p=p->next); return(state); } PLAN *c_or (void) { return(palloc(N_OR, find_or)); }