1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Cimarron D. Taylor of the University of California, Berkeley.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 
38 #include "tweak.h"
39 #ifdef HAVE_UNISTD_H
40 #ifndef __hpux
41 #include <unistd.h>
42 #endif
43 #endif
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include "my-string.h"
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/param.h>
50 #ifdef _POSIX_SOURCE
51 #include "limits.h"
52 #if !defined(MAXPATHLEN) && defined(PATH_MAX)
53 #define MAXPATHLEN PATH_MAX
54 #endif
55 #endif
56 #ifdef HAVE_TZFILE_H
57 #include <tzfile.h>
58 #endif
59 #include "common_def.h"
60 #include "client_def.h"
61 #include "c_extern.h"
62 #ifdef TIME_WITH_SYS_TIME
63 # include <sys/time.h>
64 # include <time.h>
65 #else
66 # ifdef HAVE_SYS_TIME_H
67 #  include <sys/time.h>
68 # else
69 #  include <time.h>
70 # endif
71 #endif
72 
73 #ifdef HAVE_SYS_WAIT_H
74 #include <sys/wait.h>
75 #endif
76 #ifndef WEXITSTATUS
77 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
78 #endif
79 #ifndef WIFEXITED
80 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
81 #endif
82 #include "find.h"
83 
84 
85 #define	FIND_EQUAL	0
86 #define	FIND_LESSTHAN	1
87 #define	FIND_GREATER	2
88 
89 #define	COMPARE(a, b) { \
90   switch(plan->flags) { \
91     case FIND_EQUAL: \
92       return(a == b); \
93     case FIND_LESSTHAN: \
94       return(a < b); \
95     case FIND_GREATER: \
96       return(a > b); \
97   } \
98   return(0); \
99 }
100 
palloc(enum ntype t,int (* f)())101 static PLAN *palloc (enum ntype t, int (*f)())
102 {
103   PLAN *new;
104 
105   new = (PLAN *) malloc(sizeof(PLAN));
106 
107   if ( new ) {
108     new->type = t;
109     new->eval = f;
110     new->flags = 0;
111     new->next = NULL;
112     return(new);
113   }
114   perror("palloc");
115   exit(EX_OSERR);
116 }
117 
118 extern int isoutput;
119 extern int process;
120 
121 /*
122  * find_parsenum --
123  *	Parse a string of the form [+-]# and return the value.
124  */
find_parsenum(PLAN * plan,const char * option,char * str,char * endch)125 static long find_parsenum (PLAN * plan, const char * option, char * str,
126 				 char * endch)
127 {
128   long value;
129   char *endchar;		/* pointer to character ending conversion */
130 
131   /* determine comparison from leading + or - */
132   switch(*str) {
133     case '+':
134       ++str;
135       plan->flags = FIND_GREATER;
136       break;
137     case '-':
138       ++str;
139       plan->flags = FIND_LESSTHAN;
140       break;
141     default:
142       plan->flags = FIND_EQUAL;
143       break;
144   }
145 
146   /*
147    * convert the string with strtol().  Note, if strtol() returns zero
148    * and endchar points to the beginning of the string we know we have
149    * a syntax error.
150    */
151   value = strtol(str, &endchar, 10);
152   if ( (!value && endchar == str) || (endchar[0] &&
153       (!endch || endchar[0] != *endch))) {
154     fprintf(stderr,"%s: %s", option, "illegal numeric value");
155     exit(EX_USAGE);
156   }
157   if (endch)
158     *endch = endchar[0];
159   return(value);
160 }
161 
162 /*
163  * -time n functions --
164  *
165  *	True if the difference between the file time and the
166  *	current time is n 24 hour periods.
167  *
168  */
169 
170 extern time_t now;
find_time(PLAN * plan,struct stat * sbuf,char * path)171 static int find_time (PLAN * plan, struct stat * sbuf,  char * path)
172 {
173 
174   /* with FSP sbuf.st_atime=sbuf.st_ctime=sbuf.st_mtime */
175   COMPARE((now - sbuf->st_atime + SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
176 }
177 
178 
c_time(char * arg)179 PLAN * c_time (char * arg)
180 {
181   PLAN *new;
182 
183   new = palloc(N_TIME, find_time);
184   new->t_data = find_parsenum(new, "-time", arg, NULL);
185   return(new);
186 }
187 
188 /*
189  * brace_subst --
190  *      Replace occurrences of {} in s1 with s2 and return the result string.
191  */
brace_subst(char * orig,char ** store,char * path,int len)192 static void brace_subst (char * orig, char ** store, char * path, int len)
193 {
194   register int plen;
195   register char ch, *p;
196 
197   plen = strlen(path);
198   for (p = *store; (ch = *orig) ; ++orig)
199     if (ch == '{' && orig[1] == '}') {
200       while ((p - *store) + plen > len)
201         if (!(*store = (char *)realloc(*store, len *= 2))) {
202           perror("realloc");
203           client_done();
204           exit(EX_OSERR);
205         }
206       memmove(p,path,plen);
207       p += plen;
208       ++orig;
209     } else
210       *p++ = ch;
211   *p = '\0';
212 }
213 
214 /*
215  * queryuser --
216  *      print a message to standard error and then read input from standard
217  *      input. If the input is 'y' then 1 is returned.
218  */
queryuser(char ** argv)219 static int queryuser (char ** argv)
220 {
221   int ch, first, nl;
222 
223   fprintf(stderr, "\"%s", *argv);
224   while (*++argv) fprintf(stderr, " %s", *argv);
225   fprintf(stderr, "\"? ");
226   fflush(stderr);
227 
228   first = ch = getchar();
229   for (nl = 0;;)
230   {
231     if (ch == '\n')
232     {
233       nl = 1;
234       break;
235     }
236     if (ch == EOF) break;
237     ch = getchar();
238   }
239 
240   if (!nl) {
241     fprintf(stderr, "\n");
242     fflush(stderr);
243   }
244   return(first == 'y');
245 }
246 
247 /*
248  * [-exec | -ok] utility [arg ... ] ; functions --
249  *
250  *	True if the executed utility returns a zero value as exit status.
251  *	The end of the primary expression is delimited by a semicolon.  If
252  *	"{}" occurs anywhere, it gets replaced by the current pathname.
253  *	The current directory for the execution of utility is the same as
254  *	the current directory when the find utility was started.
255  *
256  *	The primary -ok is different in that it requests affirmation of the
257  *	user before executing the utility.
258  */
find_exec(PLAN * plan,struct stat * sbuf,char * path)259 static int find_exec (PLAN * plan, struct stat * sbuf, char * path)
260 {
261   register int cnt;
262   pid_t pid;
263 #ifndef HAVE_UNION_WAIT
264   int status;
265 #else
266   union wait status;
267 #endif
268 
269   for (cnt = 0; plan->e_argv[cnt]; ++cnt)
270     if (plan->e_len[cnt])
271       brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], path,
272 		  plan->e_len[cnt]);
273 
274   if (plan->flags && !queryuser(plan->e_argv)) return(0);
275 
276   switch(pid = fork())
277    {
278     case -1:
279       perror ("fork");
280       exit(EX_OSERR);
281     case 0:
282       execvp(plan->e_argv[0], plan->e_argv);
283       perror ("execvp");
284       exit(EX_OSERR);
285   }
286   pid = wait(&status);
287 
288  return(pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
289 }
290 
emalloc_ffind(unsigned int len)291 static char *emalloc_ffind (unsigned int len)
292 {
293   char *p;
294 
295   if ( (p = (char *)malloc(len))) return((char *)p);
296   perror("malloc");
297   exit(EX_OSERR);
298 }
299 
300 /*
301  * c_exec --
302  *	build three parallel arrays, one with pointers to the strings passed
303  *	on the command line, one with (possibly duplicated) pointers to the
304  *	argv array, and one with integer values that are lengths of the
305  *	strings, but also flags meaning that the string has to be massaged.
306  */
c_exec(char *** argvp,int isok)307 PLAN *c_exec (char *** argvp, int isok)
308 {
309   PLAN *new;			/* node returned */
310   register int cnt;
311   register char **argv, **ap, *p;
312 
313   isoutput = 1;
314 
315   new = palloc(N_EXEC, find_exec);
316   new->flags = isok;
317 
318   for (ap = argv = *argvp;; ++ap) {
319     if (!*ap) {
320       fprintf(stderr,"%s: no terminating", isok ? "-ok" : "-exec");
321       exit(EX_USAGE);
322     }
323     if (**ap == ';') break;
324   }
325 
326   cnt = ap - *argvp + 1;
327   new->e_argv = (char **)emalloc_ffind((unsigned int)cnt * sizeof(char *));
328   new->e_orig = (char **)emalloc_ffind((unsigned int)cnt * sizeof(char *));
329   new->e_len = (int *)emalloc_ffind((unsigned int)cnt * sizeof(int));
330 
331   for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
332     new->e_orig[cnt] = *argv;
333     for (p = *argv; *p; ++p)
334       if (p[0] == '{' && p[1] == '}') {
335        	new->e_argv[cnt] = (char *)emalloc_ffind((unsigned int)MAXPATHLEN);
336       	new->e_len[cnt] = MAXPATHLEN;
337       	break;
338       }
339     if (!*p) {
340       new->e_argv[cnt] = *argv;
341       new->e_len[cnt] = 0;
342     }
343   }
344   new->e_argv[cnt] = new->e_orig[cnt] = NULL;
345 
346   *argvp = argv + 1;
347   return(new);
348 }
349 
printtime_ffind(time_t ftime)350 static void printtime_ffind (time_t ftime)
351 {
352   int i;
353   char *longstring;
354 
355   longstring = (char *)ctime(&ftime);
356   for (i = 4; i < 11; ++i) putchar(longstring[i]);
357 
358 #define SIXMONTHS       ((DAYSPERNYEAR / 2) * SECSPERDAY)
359 
360   if (ftime + SIXMONTHS > time((time_t *)NULL))
361     for (i = 11; i < 16; ++i) putchar(longstring[i]);
362   else {
363     (void)putchar(' ');
364     for (i = 20; i < 24; ++i) putchar(longstring[i]);
365   }
366   putchar(' ');
367 }
368 
369 #define BLK(A) (((A)+1023)/1024)
370 
printlong_ffind(char * name,struct stat * sb)371 static void printlong_ffind (char * name, struct stat * sb)
372 {
373   const char *modep;
374 
375   printf("%4ld ", (long)BLK(sb->st_size));
376   modep = ((S_IFDIR & sb->st_mode)) ? "drwxrwxrwx" : "-rw-rw-rw-" ;
377   printf("%s %3u %-*s %-*s ", modep, (unsigned int)sb->st_nlink, 8, "nobody", 8, "nobody");
378 
379   printf("%8ld ", (long)sb->st_size);
380   printtime_ffind(sb->st_mtime);
381   printf("%s", name);
382   putchar('\n');
383 }
384 
385 /*
386  * -ls functions --
387  *
388  *	Always true - prints the current sbuf to stdout in "ls" format.
389  */
find_ls(PLAN * plan,struct stat * sbuf,char * path)390 static int find_ls (PLAN * plan, struct stat * sbuf, char * path)
391 {
392   printlong_ffind(path, sbuf);
393   return(1);
394 }
395 
c_ls(void)396 PLAN *c_ls (void)
397 {
398   isoutput = 1;
399   return(palloc(N_LS, find_ls));
400 }
401 
402 /*
403  * -name functions --
404  *
405  *	True if the basename of the filename being examined
406  *	matches pattern using Pattern Matching Notation S3.14
407  */
find_name(PLAN * plan,struct stat * sbuf,char * path)408 static int find_name (PLAN * plan, struct stat * sbuf, char * path)
409 {
410   register char *name;
411 
412   /* extract filename */
413   for (name = path + strlen(path); name > path && *name != '/'; name--);
414   if (*name == '/') name++;
415   return(fnmatch(plan->c_data, name));
416 }
417 
c_name(char * pattern)418 PLAN *c_name (char * pattern)
419 {
420   PLAN *new;
421 
422   new = palloc(N_NAME, find_name);
423   new->c_data = pattern;
424   return(new);
425 }
426 
427 /*
428  * -newer file functions --
429  *
430  *	True if the current file has been modified more recently
431  *	then the modification time of the file named by the pathname
432  *	file.
433  */
find_newer(PLAN * plan,struct stat * sbuf,char * path)434 static int find_newer (PLAN * plan, struct stat * sbuf, char * path)
435 {
436   return(sbuf->st_mtime > plan->t_data);
437 }
438 
c_newer(char * filename)439 PLAN *c_newer (char * filename)
440 {
441   PLAN *new;
442   struct stat sb;
443 
444   if (stat(filename, &sb)) {
445     perror("stat");
446     exit(EX_NOINPUT);
447   }
448   new = palloc(N_NEWER, find_newer);
449   new->t_data = sb.st_mtime;
450   return(new);
451 }
452 
453 /*
454  * -print functions --
455  *
456  *	Always true, causes the current pathame to be written to
457  *	standard output.
458  */
find_print(PLAN * plan,struct stat * sbuf,char * path)459 static int find_print (PLAN * plan, struct stat * sbuf, char * path)
460 {
461   (void)printf("%s\n", path);
462   return(1);
463 }
464 
c_print(void)465 PLAN *c_print (void)
466 {
467   isoutput = 1;
468 
469   return(palloc(N_PRINT, find_print));
470 }
471 
472 /*
473  * -prune functions --
474  *
475  *	Prune a portion of the hierarchy.
476  */
find_prune(PLAN * plan,struct stat * sbuf,char * path)477 static int find_prune (PLAN * plan, struct stat * sbuf, char * path)
478 {
479   process = -1;
480   return(1);
481 }
482 
c_prune(void)483 PLAN *c_prune (void)
484 {
485   return(palloc(N_PRUNE, find_prune));
486 }
487 
488 /*
489  * -size n[c] functions --
490  *
491  *	True if the file size in bytes, divided by an implementation defined
492  *	value and rounded up to the next integer, is n.  If n is followed by
493  *	a c, the size is in bytes.
494  */
495 #define	FIND_SIZE	512
496 static int divsize = 1;
497 
find_size(PLAN * plan,struct stat * sbuf,char * path)498 static int find_size (PLAN * plan, struct stat * sbuf, char * path)
499 {
500   off_t size;
501 
502   size = divsize ? ((sbuf->st_size + FIND_SIZE - 1)/FIND_SIZE) : sbuf->st_size;
503   COMPARE(size, plan->o_data);
504 }
505 
c_size(char * arg)506 PLAN *c_size (char * arg)
507 {
508   PLAN *new;
509   char endch='c';
510 
511   new = palloc(N_SIZE, find_size);
512   new->o_data = find_parsenum(new, "-size", arg, &endch);
513   if (endch == 'c') divsize = 0;
514   return(new);
515 }
516 
517 /*
518  * -type c functions --
519  *
520  *	True if the type of the file is c, where c is d or f for
521  *	directory or regular file, respectively.
522  */
find_type(PLAN * plan,struct stat * sbuf,char * path)523 static int find_type (PLAN * plan, struct stat * sbuf, char * path)
524 {
525   return((sbuf->st_mode & S_IFMT) == plan->m_data);
526 }
527 
c_type(char * typestring)528 PLAN *c_type (char * typestring)
529 {
530   PLAN *new;
531   mode_t  mask;
532 
533   switch (typestring[0]) {
534     case 'd':
535       mask = S_IFDIR;
536       break;
537     case 'f':
538       mask = S_IFREG;
539       break;
540     default:
541       fprintf(stderr,"-type: unknown type");
542       exit(EX_USAGE);
543   }
544 
545   new = palloc(N_TYPE, find_type);
546   new->m_data = mask;
547   return(new);
548 }
549 
550 /*
551  * ( expression ) functions --
552  *
553  *	True if expression is true.
554  */
find_expr(PLAN * plan,struct stat * sbuf,char * path)555 int find_expr (PLAN * plan, struct stat * sbuf, char * path)
556 {
557   register PLAN *p;
558   register int state;
559 
560   for(p=plan->p_data[0]; p && (state=(p->eval)(p, sbuf, path)); p=p->next);
561   return(state);
562 }
563 
564 /*
565  * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
566  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
567  * to a N_EXPR node containing the expression and the ')' node is discarded.
568  */
c_openparen(void)569 PLAN *c_openparen (void)
570 {
571   return(palloc(N_OPENPAREN, (int (*)())-1));
572 }
573 
c_closeparen(void)574 PLAN *c_closeparen (void)
575 {
576   return(palloc(N_CLOSEPAREN, (int (*)())-1));
577 }
578 
579 
580 /*
581  * ! expression functions --
582  *
583  *	Negation of a primary; the unary NOT operator.
584  */
find_not(PLAN * plan,struct stat * sbuf,char * path)585 static int find_not (PLAN * plan, struct stat * sbuf, char * path)
586 {
587   register PLAN *p;
588   register int state;
589 
590   for(p=plan->p_data[0]; p && (state=(p->eval)(p, sbuf, path)); p=p->next);
591   return(!state);
592 }
593 
c_not(void)594 PLAN *c_not (void)
595 {
596   return(palloc(N_NOT, find_not));
597 }
598 
599 /*
600  * expression -o expression functions --
601  *
602  *	Alternation of primaries; the OR operator.  The second expression is
603  * not evaluated if the first expression is true.
604  */
find_or(PLAN * plan,struct stat * sbuf,char * path)605 static int find_or (PLAN * plan, struct stat * sbuf, char * path)
606 {
607   register PLAN *p;
608   register int state;
609 
610   for(p=plan->p_data[0]; p && (state=(p->eval)(p, sbuf, path)); p=p->next);
611 
612   if (state) return(1);
613 
614   for(p=plan->p_data[1]; p && (state=(p->eval)(p, sbuf, path)); p=p->next);
615   return(state);
616 }
617 
c_or(void)618 PLAN *c_or (void)
619 {
620   return(palloc(N_OR, find_or));
621 }
622