fsp/bsd_src/function.c
2003-10-04 11:26:09 +00:00

630 lines
14 KiB
C

/*-
* 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 <unistd.h>
#endif
#endif
#include <stdio.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#endif
#include "my-string.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#ifdef _POSIX_SOURCE
#include "limits.h"
#if !defined(MAXPATHLEN) && defined(PATH_MAX)
#define MAXPATHLEN PATH_MAX
#endif
#endif
#ifdef HAVE_TZFILE_H
#include <tzfile.h>
#endif
#include "common_def.h"
#include "client_def.h"
#include "c_extern.h"
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#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); \
}
#ifdef PROTOTYPES
static PLAN *palloc (enum ntype t, int (*f)())
#else
static PLAN *palloc (t, f)
enum ntype t;
int (*f)();
#endif
{
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(1);
}
extern int isoutput;
extern int process;
/*
* find_parsenum --
* Parse a string of the form [+-]# and return the value.
*/
static long find_parsenum PROTO4(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(1);
}
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 PROTO3(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 PROTO1(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 PROTO4(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(1);
}
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 PROTO1(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 PROTO3(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(1);
case 0:
execvp(plan->e_argv[0], plan->e_argv);
perror ("execvp");
exit(1);
}
pid = wait(&status);
return(pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
}
static char *emalloc_ffind PROTO1(unsigned int, len)
{
char *p;
if ( (p = (char *)malloc(len))) return((char *)p);
perror("malloc");
exit(1);
}
/*
* 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 PROTO2(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(1);
}
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 PROTO1(time_t, ftime)
{
int i;
char *longstring;
longstring = (char *)ctime((long *)&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 PROTO2(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 PROTO3(PLAN *, plan, struct stat *, sbuf, char *, path)
{
printlong_ffind(path, sbuf);
return(1);
}
PLAN *c_ls PROTO0((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 PROTO3(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 PROTO1(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 PROTO3(PLAN *, plan, struct stat *, sbuf, char *, path)
{
return(sbuf->st_mtime > plan->t_data);
}
PLAN *c_newer PROTO1(char *, filename)
{
PLAN *new;
struct stat sb;
if (stat(filename, &sb)) {
perror("stat");
exit(1);
}
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 PROTO3(PLAN *, plan, struct stat *, sbuf, char *, path)
{
(void)printf("%s\n", path);
return(1);
}
PLAN *c_print PROTO0((void))
{
isoutput = 1;
return(palloc(N_PRINT, find_print));
}
/*
* -prune functions --
*
* Prune a portion of the hierarchy.
*/
static int find_prune PROTO3(PLAN *, plan, struct stat *, sbuf, char *, path)
{
process = -1;
return(1);
}
PLAN *c_prune PROTO0((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 PROTO3(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 PROTO1(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 PROTO3(PLAN *, plan, struct stat *, sbuf, char *, path)
{
return((sbuf->st_mode & S_IFMT) == plan->m_data);
}
PLAN *c_type PROTO1(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(1);
}
new = palloc(N_TYPE, find_type);
new->m_data = mask;
return(new);
}
/*
* ( expression ) functions --
*
* True if expression is true.
*/
int find_expr PROTO3(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 PROTO0((void))
{
return(palloc(N_OPENPAREN, (int (*)())-1));
}
PLAN *c_closeparen PROTO0((void))
{
return(palloc(N_CLOSEPAREN, (int (*)())-1));
}
/*
* ! expression functions --
*
* Negation of a primary; the unary NOT operator.
*/
static int find_not PROTO3(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 PROTO0((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 PROTO3(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 PROTO0((void))
{
return(palloc(N_OR, find_or));
}