From 09863a10639de265b2299938a292bba15abc570a Mon Sep 17 00:00:00 2001 From: Kiyoshi Aman Date: Sun, 2 Jun 2019 05:39:48 -0500 Subject: usr.bin/find: replace with heirloom find --- usr.bin/find/extern.h | 99 --- usr.bin/find/find.1 | 1488 +++++++++++++---------------------- usr.bin/find/find.c | 1696 ++++++++++++++++++++++++++++++++++------ usr.bin/find/find.h | 138 ---- usr.bin/find/function.c | 1956 ----------------------------------------------- usr.bin/find/getdir.c | 148 ++++ usr.bin/find/getdir.h | 33 + usr.bin/find/ls.c | 135 ---- usr.bin/find/main.c | 154 ---- usr.bin/find/misc.c | 142 ---- usr.bin/find/operator.c | 263 ------- usr.bin/find/option.c | 184 ----- 12 files changed, 2183 insertions(+), 4253 deletions(-) delete mode 100644 usr.bin/find/extern.h delete mode 100644 usr.bin/find/find.h delete mode 100644 usr.bin/find/function.c create mode 100644 usr.bin/find/getdir.c create mode 100644 usr.bin/find/getdir.h delete mode 100644 usr.bin/find/ls.c delete mode 100644 usr.bin/find/main.c delete mode 100644 usr.bin/find/misc.c delete mode 100644 usr.bin/find/operator.c delete mode 100644 usr.bin/find/option.c (limited to 'usr.bin') diff --git a/usr.bin/find/extern.h b/usr.bin/find/extern.h deleted file mode 100644 index 825351e..0000000 --- a/usr.bin/find/extern.h +++ /dev/null @@ -1,99 +0,0 @@ -/* $NetBSD: extern.h,v 1.29 2016/06/13 00:04:40 pgoyette Exp $ */ - -/*- - * Copyright (c) 1991, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * 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. 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. - * - * from: @(#)extern.h 8.3 (Berkeley) 4/16/94 - */ - -void brace_subst(char *, char **, char *, int *); -PLAN *find_create(char ***); -int find_execute(PLAN *, char **); -PLAN *find_formplan(char **); -int find_traverse(PLAN *, int (*)(PLAN *, void *), void *); -int f_expr(PLAN *, FTSENT *); -PLAN *not_squish(PLAN *); -PLAN *or_squish(PLAN *); -PLAN *paren_squish(PLAN *); -int plan_cleanup(PLAN *, void *); -void printlong(char *, char *, struct stat *); -int queryuser(char **); -void show_path(int); - -PLAN *c_amin(char ***, int, char *); -PLAN *c_anewer(char ***, int, char *); -PLAN *c_asince(char ***, int, char *); -PLAN *c_atime(char ***, int, char *); -PLAN *c_cmin(char ***, int, char *); -PLAN *c_cnewer(char ***, int, char *); -PLAN *c_csince(char ***, int, char *); -PLAN *c_ctime(char ***, int, char *); -PLAN *c_delete(char ***, int, char *); -PLAN *c_depth(char ***, int, char *); -PLAN *c_empty(char ***, int, char *); -PLAN *c_exec(char ***, int, char *); -PLAN *c_execdir(char ***, int, char *); -PLAN *c_exit(char ***, int, char *); -PLAN *c_false(char ***, int, char *); -PLAN *c_follow(char ***, int, char *); -PLAN *c_fprint(char ***, int, char *); -PLAN *c_fstype(char ***, int, char *); -PLAN *c_group(char ***, int, char *); -PLAN *c_iname(char ***, int, char *); -PLAN *c_inum(char ***, int, char *); -PLAN *c_iregex(char ***, int, char *); -PLAN *c_links(char ***, int, char *); -PLAN *c_ls(char ***, int, char *); -PLAN *c_maxdepth(char ***, int, char *); -PLAN *c_mindepth(char ***, int, char *); -PLAN *c_mmin(char ***, int, char *); -PLAN *c_mtime(char ***, int, char *); -PLAN *c_name(char ***, int, char *); -PLAN *c_newer(char ***, int, char *); -PLAN *c_nogroup(char ***, int, char *); -PLAN *c_nouser(char ***, int, char *); -PLAN *c_path(char ***, int, char *); -PLAN *c_perm(char ***, int, char *); -PLAN *c_print(char ***, int, char *); -PLAN *c_print0(char ***, int, char *); -PLAN *c_printx(char ***, int, char *); -PLAN *c_prune(char ***, int, char *); -PLAN *c_regex(char ***, int, char *); -PLAN *c_since(char ***, int, char *); -PLAN *c_size(char ***, int, char *); -PLAN *c_type(char ***, int, char *); -PLAN *c_user(char ***, int, char *); -PLAN *c_xdev(char ***, int, char *); -PLAN *c_openparen(char ***, int, char *); -PLAN *c_closeparen(char ***, int, char *); -PLAN *c_not(char ***, int, char *); -PLAN *c_or(char ***, int, char *); -PLAN *c_null(char ***, int, char *); - -extern int ftsoptions, isdeprecated, isdepth, isoutput, issort, isxargs, - regcomp_flags; diff --git a/usr.bin/find/find.1 b/usr.bin/find/find.1 index 60ce94a..a12b404 100644 --- a/usr.bin/find/find.1 +++ b/usr.bin/find/find.1 @@ -1,974 +1,558 @@ -.\" $NetBSD: find.1,v 1.89 2017/07/03 21:34:57 wiz Exp $ -.\" -.\" Copyright (c) 1990, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" This code is derived from software contributed to Berkeley by -.\" the Institute of Electrical and Electronics Engineers, Inc. +'\" t +.\" Sccsid @(#)find.1 1.44 (gritter) 8/14/05 +.\" Parts taken from find(1), Unix 7th edition: +.\" Copyright(C) Caldera International Inc. 2001-2002. All rights reserved. .\" .\" 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 +.\" Redistributions of source code and documentation must retain the +.\" above copyright notice, this list of conditions and the following +.\" disclaimer. +.\" 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. 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. +.\" All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed or owned by Caldera +.\" International, Inc. +.\" Neither the name of Caldera International, Inc. nor the names of +.\" other contributors may be used to endorse or promote products +.\" derived from this software without specific prior written permission. .\" -.\" from: @(#)find.1 8.7 (Berkeley) 5/9/95 +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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. .\" -.Dd June 13, 2016 -.Dt FIND 1 -.Os -.Sh NAME -.Nm find -.Nd walk a file hierarchy -.Sh SYNOPSIS -.Nm -.Op Fl H | Fl L | Fl P -.Op Fl dEhsXx -.Ar file -.Op Ar file ... -.Op Ar expression -.Nm -.Op Fl H | Fl L | Fl P -.Op Fl dEhsXx -.Fl f Ar file -.Op Ar file ... -.Op Ar expression -.Sh DESCRIPTION -.Nm -recursively descends the directory tree for each -.Ar file -listed, evaluating an -.Ar expression -(composed of the -.Dq primaries +.TH FIND 1 "8/14/05" "Heirloom Toolchest" "User Commands" +.SH NAME +find \- find files +.SH SYNOPSIS +.B find +.I pathname-list expression +.SH DESCRIPTION +.I Find +recursively descends +the directory hierarchy for +each pathname in the +.I pathname-list +(i.\|e., one or more pathnames) +seeking files that match a boolean +.I expression +written in the primaries given below. +In the descriptions, the argument +.I n +is used as a decimal integer +where +.I +n +means more than +.I n, +.I \-n +means less than +.I n and -.Dq operands -listed below) in terms -of each file in the tree. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl H -Causes the file information and file type (see -.Xr stat 2 ) -returned for each symbolic link encountered on the command line to be -those of the file referenced by the link, not the link itself. -If the referenced file does not exist, the file information and type will -be for the link itself. -File information of all symbolic links not on the command line is that -of the link itself. -.It Fl L -Causes the file information and file type (see -.Xr stat 2 ) -returned for each symbolic link to be those of the file referenced by the -link, not the link itself. -If the referenced file does not exist, the file information and type will -be for the link itself. -.It Fl P -Causes the file information and file type (see -.Xr stat 2 ) -returned for each symbolic link to be those of the link itself. -.It Fl d -Causes -.Nm -to perform a depth-first traversal, i.e., directories -are visited in post-order, and all entries in a directory will be acted -on before the directory itself. -By default, -.Nm -visits directories in pre-order, i.e., before their contents. -Note, the default is -.Em not -a breadth-first traversal. -.It Fl E -Causes -.Ar regexp -arguments to primaries to be interpreted as extended regular -expressions (see -.Xr re_format 7 ) . -.It Fl f -Specifies a file hierarchy for -.Nm -to traverse. -File hierarchies may also be specified as the operands immediately -following the options. -.It Fl h -Causes the file information and file type (see -.Xr stat 2 ) -returned for each symbolic link to be those of the file referenced by the -link, not the link itself. -If the referenced file does not exist, the file information and type will -be for the link itself. -.It Fl s -Causes the entries of each directory to be sorted in -lexicographical order. -Note that the sorting is done only inside of each directory; -files in different directories are not sorted. -Therefore, -.Sq Li a/b -appears before -.Sq Li a.b , -which is different from -.Dq Li "find ... \&| sort" -order. -.It Fl X -Modifies the output to permit -.Nm -to be safely used in conjunction with -.Xr xargs 1 . -If a file name contains any of the delimiting characters used by -.Xr xargs 1 , -a diagnostic message is displayed on standard error, and the file -is skipped. -The delimiting characters include single -.Pq Dq \&' -and double -.Pq Dq \&" -quotes, backslash -.Pq Dq \e , -space, tab, and newline characters. -Alternatively, the -.Ic -print0 -or -.Ic -printx -primaries can be used to format the output in a way that -.Xr xargs 1 -can accept. -.It Fl x -Restricts the search to the file system containing the -directory specified. -Does not list mount points to other file systems. -.El -.Sh PRIMARIES -All primaries which take a numeric argument of -.Ar n -allow the number to be preceded by a plus sign -.Pq Dq \&+ -or a minus sign -.Pq Dq \- . -A preceding plus sign means -.Dq more than Ar n , -a preceding minus sign means -.Dq less than Ar n , -and neither means -.Dq exactly Ar n . -(The argument specified for the -.Ic -user +.I n +means exactly +.IR n . +.TP 10n +.BR \-name " filename" +True if the +.I filename +argument matches the current file name. +Normal +Shell +argument syntax +as described in +.IR glob (7) +may be used if escaped (watch out for +`[', `?' and `*'). +The internationalization constructs +`[[:class:]]', `[[=e=]]', and `[[.cs.]]' +are understood with +.BR /usr/5bin/s42/find , +.BR /usr/5bin/posix/find , and -.Ic -group -primaries -are similarly treated if the value is numeric and does not correspond to a -valid user or group name.) -.Pp -For primaries which take a -.Ar timestamp -argument, the argument must be valid input to -.Xr parsedate 3 . -If the argument contains multiple words, enclose the argument in quotes. -.Pp -.Bl -tag -width Ds -compact -.It Ic -amin Ar n -True if the difference between the file last access time and the time -.Nm -was started, rounded up to the next full minute, is -.Ar n -minutes. -.Pp -.It Ic -anewer Ar file -True if the current file has a more recent last access time than -.Ar file . -.Pp -.It Ic -asince Ar timestamp -True if the file last access time is greater than the specified -.Ar timestamp . -.Pp -.It Ic -atime Ar n -True if the difference between the file last access time and the time -.Nm -was started, rounded up to the next full 24-hour period, is -.Ar n -24-hour periods. -.Pp -.It Ic -cmin Ar n -True if the difference between the time of last change of file status -information and the time -.Nm -was started, rounded up to the next full minute, is -.Ar n -minutes. -.Pp -.It Ic -cnewer Ar file -True if the current file has a more recent last change time than -.Ar file . -.Pp -.It Ic -csince Ar timestamp -True if the file last status change time is greater than the specified -.Ar timestamp . -.Pp -.It Ic -ctime Ar n -True if the difference between the time of last change of file status -information and the time -.Nm -was started, rounded up to the next full 24-hour period, is -.Ar n -24-hour periods. -.Pp -.It Ic -delete -Delete found files, symbolic links, and directories. -Always returns true. -This executes from the current working directory as -.Nm -recurses down the tree. -To avoid deleting unexpected files, it will ignore any filenames that -.Xr fts 3 -returns that contain a -.Dq / -.Xr ( fts 3 -should not return such pathnames). -Depth-first traversal processing is implied by this option. -This primary can also be invoked as -.Ic -rm . -.Pp -.It Ic -empty -True if the current file or directory is empty. -.Pp -.It Ic -exec Ar utility Oo argument ... Oc Ic \&; -.It Ic -exec Ar utility Oo argument ... Oc Ic {} Ic \&+ -Execute the specified -.Ar utility -with the specified arguments. -.Pp -The list of arguments for -.Ar utility -is terminated by a lone semicolon -.Dq Ic \&; -or plus -.Dq Ic \&+ -character as a separate parameter. -The command specified by -.Ar utility -will be executed with its current working directory being the directory -from which -.Nm -was executed. -.Pp -If the list of arguments is terminated by a semicolon -.Pq Dq Ic \&; , -then -.Ar utility -is invoked once per pathname. -If -the string -.Dq Ic {} -appears one or more times in the utility name or arguments, -then it is replaced by the pathname of the current file -(but it need not appear, in which case the pathname -will not be passed to -.Ar utility ) . -The semicolon-terminated form of the -.Ic -exec -primary returns true if and only if -.Ar utility -exits with a zero exit status. -Note that the semicolon will have to be escaped on the shell command line -in order to be passed as a parameter. -.Pp -If the list of arguments is terminated by a plus sign -.Pq Dq Ic \&+ , -then the pathnames for which the primary is evaluated are aggregated -into sets, and -.Ar utility -will be invoked once per set, similar to -.Xr xargs 1 . -In this case the string -.Dq Ic {} -must appear, and must appear as the last item in the argument list, -just before the -.Dq Ic \&+ -parameter, and is replaced by the pathnames of the current set of files. -Each set is limited to no more than 5,000 pathnames, -and is also limited such that the total number of bytes in the argument -list does not exceed -.Dv ARG_MAX . -The plus-terminated form of the -.Ic -exec -primary always returns true. -If the plus-terminated form of the -.Ic -exec -primary results in any invocation of -.Ar utility -exiting with non-zero exit status, then -.Nm -will eventually exit with non-zero status as well, -but this does not cause -.Nm -to exit early. -.Pp -.It Ic -execdir Ar utility Oo argument ... Oc Ic \&; -The -.Ic -execdir -primary is similar to the semicolon-terminated -.Pq Dq Ic \&; -variant of the -.Ic -exec -primary, with the exception that -.Ar utility -will be executed from the directory that holds -the current file. -Only the base filename is substituted for the string -.Dq Ic {} . -Set aggregation -.Pq Do Ic \&+ Dc termination -is not supported. -.Pp -.It Ic -exit Op Ar status -This primary causes -.Nm -to stop traversing the file system and exit immediately, -with the specified numeric exit status. -If the -.Ar status -value is not specified, then -.Nm -will exit with status zero. -Note that any preceding primaries will be evaluated and acted upon -before exiting. -.Pp -.It Ic -false -This primary always evaluates to false. -This can be used following a primary that caused the -expression to be true to make the expression to be false. -This can be useful after using a -.Ic -fprint -primary so it can continue to the next expression (using an -.Cm -or -operator, for example). -.Pp -.It Ic -flags Oo Fl Oc Ns Ar flags -If -.Ar flags -are preceded by a dash -.Pq Dq Ic \- , -this primary evaluates to true -if at least all of the bits in -.Ar flags -are set in the file's flags bits. +.BR /usr/5bin/posix2001/find , +but not with +.BR /usr/5bin/find . +.TP +.BR \-perm " mode" +True if the file permission flags +exactly +match the +octal number +or symbolic +.I mode +(see +.IR chmod (1)). If -.Ar flags -are not preceded by a dash, this primary evaluates to true if -the bits in -.Ar flags -exactly match the file's flags bits. -If -.Ar flags +.I mode +is prefixed by a minus sign, +the flags are compared: +.IR (flags&mode)==mode . +.TP +.BR \-type " c" +True if the type of the file is -.Dq none , -files with no flags bits set are matched. -(See -.Xr chflags 1 -for more information about file flags.) -.Pp -.It Ic -follow -Follow symbolic links. -.Pp -.It Ic -fprint Ar filename -This primary always evaluates to true. -This creates -.Ar filename -or overwrites the file if it already exists. -The file is created at startup. -It writes the pathname of the current file to this file, followed -by a newline character. -The file will be empty if no files are matched. -.Pp -.It Ic -fstype Ar type -True if the file is contained in a file system of type -.Ar type . -The -.Xr sysctl 8 -command can be used to find out the types of file systems -that are available on the system: -.Bd -literal -offset indent -sysctl vfs.generic.fstypes -.Ed -.Pp -In addition, there are two pseudo-types, -.Dq local -and -.Dq rdonly . -The former matches any file system physically mounted on the system where -the -.Nm -is being executed, and the latter matches any file system which is -mounted read-only. -.Pp -.It Ic -group Ar gname -True if the file belongs to the group -.Ar gname . -If -.Ar gname -is numeric and there is no such group name, then -.Ar gname -is treated as a group id (and considered a numeric argument). -.Pp -.It Ic -iname Ar pattern -True if the last component of the pathname being examined matches -.Ar pattern -in a case-insensitive manner. -Special shell pattern matching characters -.Po -.Dq \&[ , -.Dq \&] , -.Dq \&* , -and -.Dq \&? -.Pc -may be used as part of -.Ar pattern . -These characters may be matched explicitly by escaping them with a -backslash -.Pq Dq \e . -.Pp -.It Ic -inum Ar n -True if the file has inode number -.Ar n . -.Pp -.It Ic -iregex Ar regexp -True if the path name of the current file matches the case-insensitive -basic regular expression -.Pq see Xr re_format 7 -.Ar regexp . -This is a match on the whole path, not a search for the regular expression -within the path. -.Pp -.It Ic -links Ar n +.I c, +where +.I c +is +.sp +.TS +lfB l. +b block special file; +c character special file; +d directory; +D Solaris door; +f plain file; +l symbolic link; +n HP-UX network special file; +p named pipe; +s socket. +.TE +.TP +.B \-follow +Always true; +causes find to follow symbolic links. +The `\fB\-type\fR l' condition never occurs in this case. +.TP +.BR \-links " n" True if the file has -.Ar n +.I n links. -.Pp -.It Ic -rm -This primary is an alias for -.Ic -delete . -.Pp -.It Ic -ls -This primary always evaluates to true. -The following information for the current file is written to standard output: -its inode number, size in 512-byte blocks, file permissions, number of hard -links, owner, group, size in bytes, last modification time, and pathname. -If the file is a block or character special file, the major and minor numbers -will be displayed instead of the size in bytes. -If the file is a symbolic link, the pathname of the linked-to file will be -displayed preceded by -.Dq -> . -The format is identical to that produced by -.Dq ls -dgils . -.Pp -.It Ic -maxdepth Ar depth -True if the current search depth is less than or equal to what is specified in -.Ar depth . -.Pp -.It Ic -mindepth Ar depth -True if the current search depth is at least what is specified in -.Ar depth . -.Pp -.It Ic -mmin Ar n -True if the difference between the file last modification time and the time -.Nm -was started, rounded up to the next full minute, is -.Ar n -minutes. -.Pp -.It Ic -mtime Ar n -True if the difference between the file last modification time and the time -.Nm -was started, rounded up to the next full 24-hour period, is -.Ar n -24-hour periods. -.Pp -.It Ic -ok Ar utility Oo argument ... Oc Ic \&; +.TP +.BR \-user " uname" +True if the file belongs to the user +.I uname +(login name or numeric user ID). +.TP +.BR \-group " gname" +True if the file belongs to group +.I gname +(group name or numeric group ID). +.TP +.BR \-size " n[" c ] +True if the file is +.I n +blocks long (512 bytes per block), +or, with +.BR c , +.I n +bytes long. +.TP +.BR \-inum " n" +True if the file has inode number +.I n. +.TP +.BR \-atime " n" +True if the file has been accessed in +.I n +days. +.TP +.BR \-mtime " n" +True if the file has been modified in +.I n +days. +.TP +.BR \-ctime " n" +True if the file inode has been changed in +.I n +days. +.TP +.BR \-exec " command ... " ; +True if the executed command returns +a zero value as exit status. +The end of the command must be punctuated by an (escaped) +semicolon. +A command argument `{}' is replaced by the +current pathname. +.TP +.BR \-exec " command ... " "{} +" +Always true. The -.Ic -ok -primary is similar to the semicolon-terminated -.Pq Dq \&; -variant of the -.Ic -exec -primary, with the exception that -.Nm -requests user affirmation for the execution of -.Ar utility -by printing -a message to the terminal and reading a response. -If the response is other than -.Dq y , -the command is not executed and the -.Ic -ok -primary evaluates to false. -Set aggregation -.Pq Do \&+ Dc termination -is not supported. -.Pp -.It Ic -name Ar pattern -True if the last component of the pathname being examined matches -.Ar pattern . -Special shell pattern matching characters -.Po -.Dq \&[ , -.Dq \&] , -.Dq \&* , +.B {} +argument is replaced by a set of aggregated pathnames. +Each pathname is passed to the command as a single argument. +Every time a limit of arguments is reached +by the pathnames found so far, +the command is executed, +and aggregating starts using a new set +beginning with the next pathname. +If any invocation of command +returns a non-zero exit status, +find will return a non-zero exit status +when its processing is done. +.TP +.BR \-ok " command ... " ; +Like +.B \-exec +except that the generated command is written on +the standard output, then the standard input is read +and the command executed only upon response +.BR y . +.TP +.B \-print +Always true; +causes the current pathname to be printed. +If no expression is given, +.B \-print +is used per default +(as a change introduced by POSIX.2). +.TP +.BR \-newer " file" +True if +the current file has been modified more recently than the argument +.I file. +.TP +.BR \-anewer " file" +True if +the current file has been accessed more recently than the argument +.I file +was modified. +This primary is an extension. +.TP +.BR \-cnewer " file" +True if a status change +has occurred on the current file +more recently than the argument +.I file +was modified. +This primary is an extension. +.TP +.B \-depth +Always true; +causes the contents of each directory +to be examined before the directory itselves. +.TP +.BR \-fstype " type" +True if the current file +resides on a file system of the given type. +.TP +.B \-local +True if the file is on a local file system. +Are file system types except for +.I nfs and -.Dq \&? -.Pc -may be used as part of -.Ar pattern . -These characters may be matched explicitly by escaping them with a -backslash -.Pq Dq \e . -.Pp -.It Ic -newer Ar file -True if the current file has a more recent last modification time than -.Ar file . -.Pp -.It Ic -newerXY Ar reference -For compatibility with Gnu findutils. -.Bl -column -offset indent ".Sy findutils" ".Sy equivalent" -.It Sy findutils Ta Sy find -.It Sy option Ta Sy equivalent -.It -neweraa Ta -anewer -.It -newerat Ta -asince -.It -newercc Ta -cnewer -.It -newerct Ta -csince -.It -newermm Ta -newer -.It -newermt Ta -since -.El -.Pp -Other option variants from findutils are not implemented. -.Pp -.It Ic -nouser -True if the file belongs to an unknown user. -.Pp -.It Ic -nogroup -True if the file belongs to an unknown group. -.Pp -.It Ic -path Ar pattern -True if the pathname being examined matches -.Ar pattern . -Special shell pattern matching characters -.Po -.Dq \&[ , -.Dq \&] , -.Dq \&* , +.I smbfs +are currently considered local. +.TP +.B \-mount +Always true; +restricts the search to directories +that have the same device id +as the currently examined path operand. +.TP +.B \-xdev +The same as +.BR \-mount . +This primary has been introduced by POSIX. +.TP +.B \-nouser +True if the file is owned by a user +that has no login name. +.TP +.B \-nogroup +True if the file is owned by a group +that lacks a group name. +.TP +.B \-prune +Always true. +Causes the search for the current path +to be stopped once the primary is evaluated. +When combined with +.BR \-depth , +.B \-prune +has no effect. +.TP +.BR \-cpio " device" +Always true. +Writes the file on the named device +in binary cpio format (5120-byte records). +Implies +.BR \-depth . +.TP +.BR \-ncpio " device" +Always true. +Writes the file on the named device +in SVR4 ASCII cpio format (5120-byte records). +Implies +.BR \-depth . +.PP +The primaries may be combined using the following operators +(in order of decreasing precedence): +.TP 4 +1) +A parenthesized group of primaries and operators +(parentheses are special to the Shell and must be escaped). +.TP 4 +2) +The negation of a primary +(`!' is the unary +.I not +operator). +.TP 4 +3) +Concatenation of primaries +(the +.I and +operation +is implied by the juxtaposition of two primaries +or by an explicit +.B \-a +operator). +.TP 4 +4) +Alternation of primaries +.RB "(`" \-o "' is the" +.I or +operator). +.PP +Options have been introduced by POSIX.1-2001 +in addition to the expression operators. +They must preceed the +.I pathname-list +one the command line +and have no effect on boolean expression processing. +.TP +.B \-H +Follow symbolic links given on the command line, +but do not follow symbolic links encountered during directory traversal. +.TP +.B \-L +Follow all symbolic links found, +like the +.I \-follow +primary. +.PP +With the +.I \-follow +primary or the +.I \-L +option, hierarchy loops caused by symbolic links are detected, +but only +.B /usr/5bin/posix2001/find +prints an error message. +The offending link is not followed, +and operation continues with the next directory entry found. +.SH EXAMPLES +To remove all files named +`a.out' or `*.o' that have not been accessed for a week: +.IP "" .2i +find / \\( \-name a.out \-o \-name \'*.o\' \\) +\-atime +7 \-exec rm {} \\; +.PP +The rm command is executed once for each file. +The form +.IP "" .2i +find / \\( \-name a.out \-o \-name \'*.o\' \\) +\-atime +7 \-exec rm {} + +.PP +is faster since the rm command is executed with a set of pathnames. +.PP +To find all files below the directory `documents' +that contain the regular expression `string': +.IP "" .2i +find documents \-type f \-exec grep string {} + +.PP +To find all files in the directory `home', +not descending into its subdirectories: +.IP "" .2i +find home ! \-name home \-prune +.PP +To check whether the file `diary' +has been updated within the last two days; +the name of the file is printed if true, +and is not printed otherwise: +.IP "" .2i +find diary \-prune \-mtime \-2 +.SH FILES +/etc/passwd +.br +/etc/group +.SH "ENVIRONMENT VARIABLES" +.TP +.BR LANG ", " LC_ALL +See +.IR locale (7). +.TP +.B LC_COLLATE +Affects the collation order for range expressions, +equivalence classes, and collation symbols in patterns with +.BR /usr/5bin/s42/find , +.BR /usr/5bin/posix/find , and -.Dq \&? -.Pc -may be used as part of -.Ar pattern . -These characters may be matched explicitly by escaping them with a -backslash -.Pq Dq \e . -Slashes -.Pq Dq / -are treated as normal characters and do not have to be -matched explicitly. -.Pp -.It Ic -perm Oo Fl Oc Ns Ar mode -The -.Ar mode -may be either symbolic (see -.Xr chmod 1 ) -or an octal number. -If the mode is symbolic, a starting value of zero is assumed and the -mode sets or clears permissions without regard to the process' file mode -creation mask. -If the mode is octal, only bits 07777 -.Pf ( Dv S_ISUID -| -.Dv S_ISGID -| -.Dv S_ISTXT -| -.Dv S_IRWXU -| -.Dv S_IRWXG -| -.Dv S_IRWXO ) -of the file's mode bits participate -in the comparison. -If the mode is preceded by a dash -.Pq Dq Ic \- , -this primary evaluates to true -if at least all of the bits in the mode are set in the file's mode bits. -If the mode is not preceded by a dash, this primary evaluates to true if -the bits in the mode exactly match the file's mode bits. -Note, the first character of a symbolic mode may not be a dash -.Pq Dq Ic \- . -.Pp -.It Ic -print -This primary always evaluates to true. -It prints the pathname of the current file to standard output, followed -by a newline character. -If none of -.Ic -delete , -.Ic -exec , -.Ic -execdir , -.Ic -exit , -.Ic -fprint , -.Ic -ls , -.Ic -ok , -.Ic -print0 , -.Ic -printx , -nor -.Ic -rm -is specified, the given expression shall be effectively replaced by -.Cm \&( Ns Ar given\& expression Ns Cm \&) -.Ic -print . -.Pp -.It Ic -print0 -This primary always evaluates to true. -It prints the pathname of the current file to standard output, followed -by a NUL character. -.Pp -.It Ic -printx -This primary always evaluates to true. -It prints the pathname of the current file to standard output, -with each space, tab, newline, backslash, dollar sign, and single, -double, or back quotation mark prefixed by a backslash, so the output of -.Nm -can safely be used as input to -.Xr xargs 1 . -.Pp -.It Ic -prune -This primary always evaluates to true. -It causes -.Nm -to not descend into the current file. -Note, the -.Ic -prune -primary has no effect if the -.Fl d -option was specified. -.Pp -.It Ic -regex Ar regexp -True if the path name of the current file matches the case-sensitive -basic regular expression -.Pq see Xr re_format 7 -.Ar regexp . -This is a match on the whole path, not a search for the regular expression -within the path. -.Pp -.It Ic -since Ar timestamp -True if the file last modification time is more recent than -.Ar timestamp . -.Pp -.It Ic -size Ar n Ns Op Cm c -True if the file's size, rounded up, in 512-byte blocks is -.Ar n . -If -.Ar n -is followed by a -.Dq Ic c , -then the primary is true if the file's size is -.Ar n -bytes. -.Pp -.It Ic -type Ar t -True if the file is of the specified type. -Possible file types are as follows: -.Pp -.Bl -tag -width flag -offset indent -compact -.It Cm b -block special -.It Cm c -character special -.It Cm d -directory -.It Cm f -regular file -.It Cm l -symbolic link -.It Cm p -FIFO -.It Cm s -socket -.It Cm W -whiteout -.It Cm w -whiteout -.El -.Pp -.It Ic -user Ar username -True if the file belongs to the user -.Ar username . -If -.Ar username -is numeric and there is no such user on the system, then -.Ar username -is treated as a user id (and considered a numeric argument). -.Pp -.It Ic -xdev -This primary always evaluates to true. -It causes find not to descend past directories that have a different -device ID -.Va ( st_dev , -see -.Xr stat 2 -S5.6.2 [POSIX.1]). -.El -.Sh OPERATORS -The primaries may be combined using the following operators. -The operators are listed in order of decreasing precedence. -.Bl -tag -width (expression) -.It Cm \&( Ar expression Cm \&) -This evaluates to true if the parenthesized expression evaluates to -true. -.It Cm \&! Ar expression -This is the unary -.Tn NOT -operator. -It evaluates to true if the expression is false. -.It Ar expression Cm -and Ar expression -.It Ar expression expression -The -.Cm -and -operator is the logical -.Tn AND -operator. -As it is implied by the juxtaposition of two expressions it does not -have to be specified. -The expression evaluates to true if both expressions are true. -The second expression is not evaluated if the first expression is false. -.It Ar expression Cm -or Ar expression +.BR /usr/5bin/posix2001/find . +.TP +.B LC_CTYPE +Determines the mapping of bytes to characters +and character class expressions +in patterns. +.TP +.B SYSV3 +Causes the text of some diagnostic messages to be changed; +causes +.I \-ncpio +to create traditional ASCII cpio format archives. +.SH "SEE ALSO" +chmod(1), +cpio(1), +pax(1), +sh(1), +xargs(1), +stat(2), +glob(7), +locale(7) +.SH NOTES +Undesired effects can result if file names printed by +.I find +contain newline characters, +as shown by the following command sequence: +.RS +.sp +.nf +$ mkdir \-p \'dummy +> /etc\' +$ touch \'dummy +> /etc/passwd\' +$ find . \-print +\&. +\&./dummy +.sp +\&./dummy +/etc +\&./dummy +/etc/passwd +$\ +.fi +.sp +.RE +Shell scripts or utilities unaware of this problem +will operate on +.I /etc/passwd +(or other arbitrary file names) +when reading such output; +a malicious user might create such files +to read or overwrite privileged information. +To circumvent this problem, +one of the following proposals should be taken +unless the file hierarchy traversed by the +.I find +command is definitively known not to contain such file names: +.IP \(en 2 +If the output is read by the +.I xargs +utility to gain faster execution by aggregating command arguments as in +.in +2 +.sp +find . \-print | xargs \fIcommand\fR +.sp +.in -2 +a safe and equally fast substitute is the +.in +2 +.sp +find . \-exec \fIcommand\fR {} + +.sp +.in -2 +operand of +.IR find ; +it is not portably accepted by +.I find +implementations, though. +.IP \(en 2 +A universal solution for submitting file names to the +.I xargs +utility is given in the +.I NOTES +section of +.IR xargs (1). +.IP \(en 2 +The method employed by this script can be generalized as follows: +If the script or utility reading the output of +.I find +provides the necessary parsing capabilities, +special path prefixes can be given to the +.I find +command, such as +.in +2 +.sp +find /.//. \-print +.sp +.in -2 +for absolute path names or +.in +2 +.sp +find .//. \-print +.sp +.in -2 +for relative path names. +Since adjacent slash characters never appear +in relative file names found during directory traversal, +they can be taken as delimiters; +a line starts a new path name +only if the delimiter appears. +.IP \(en 2 The -.Cm -or -operator is the logical -.Tn OR -operator. -The expression evaluates to true if either the first or the second expression -is true. -The second expression is not evaluated if the first expression is true. -.El -.Pp -All operands and primaries must be separate arguments to -.Nm . -Primaries which themselves take arguments expect each argument -to be a separate argument to -.Nm . -.Sh EXIT STATUS +.I \-name +operand can be used to exclude all path names +that contain newline characters, as in +.in +2 +.sp +.nf +$ find . \-name \'* +> *\' \-prune \-o ! \-name \'* +> *\' \-print +.sp +.fi +.in -2 +Note that certain other implementations of +.I find +require a leading period in the pattern +to match file names with a leading period; +it may be necessary to exclude such patterns as well. +.IP \(en 2 The -.Nm -utility normally exits 0 on success, and exits with 1 under certain -internal error conditions. -If any invocations of -.Dq Ic -exec Ar ... Ic \&+ -primaries return non-zero exit-status, then -.Nm -will do so as well. -.Sh EXAMPLES -The following examples are shown as given to the shell: -.Bl -tag -width findx -.It Li "find / \e! -name \*q*.c\*q \-print" -Print out a list of all the files whose names do not end in -.Dq \&.c . -.It Li "find / \-newer ttt \-user wnj \-print" -Print out a list of all the files owned by user -.Dq wnj -that are newer than the file -.Dq ttt . -.It Li "find . \-type f \-mmin \-30 \-print \-or \-mindepth 1 \-prune" -Print out a list of all the files in the current directory that are -newer than 30 minutes. -.It Li "find . \-type f \-atime +10 \-mindepth 2 \-print" -Print out a list of all the files in any sub-directories that have not -been accessed in the past ten days. -.It Li "find . \-mtime +90 \-exec rm \-i {} + \-or \-mindepth 1 \-prune" -Interactively remove all of the files in the current directory that have -not been modified in 90 days. -.It Li "find . \-type f \-mtime +90 \-ok mv {} {}.old \e;" -Interactively rename all of the files in the current directory and all -sub-directories that have not been modified in 90 days. -.It Li "find / \e! \e( \-newer ttt \-user wnj \e) \-print" -Print out a list of all the files which are not both newer than -.Dq ttt -and owned by -.Dq wnj . -.It Li "find / \e( \-newer ttt \-or \-user wnj \e) \-print" -Print out a list of all the files that are either owned by -.Dq wnj -or that are newer than -.Dq ttt . -.It Li "find / \e( \-newer ttt \-or \-user wnj \e) \-exit 1" -Return immediately with a value of 1 if any files are found that are either -owned by -.Dq wnj -or that are newer than -.Dq ttt , -but do not print them. -.It Li "find / \e( \-newer ttt \-or \-user wnj \e) \-ls \-exit 1" -Same as above, but list the first file matching the criteria before exiting -with a value of 1. -.It Li "find . \-type f \-exec sh \-c 'file=\*[q]$1\*[q]; ...;' - {} \;" -Perform an arbitrarily complex shell command for every file. -.El -.Sh SEE ALSO -.Xr chflags 1 , -.Xr chmod 1 , -.Xr locate 1 , -.Xr xargs 1 , -.Xr stat 2 , -.Xr fts 3 , -.Xr getgrent 3 , -.Xr getpwent 3 , -.Xr strmode 3 , -.Xr re_format 7 , -.Xr symlink 7 , -.Xr sysctl 8 -.Sh STANDARDS +.I \-depth +operand cannot be combined with the +.I \-prune +operand used in the previous example. +When the directory name must be printed +after file names below that directory, +as with the +.IR cpio +command, +file names that leave the specified path hierarchy +should be filtered out: +.in +2 +.sp +find . \-depth | egrep \'^\e./\' | cpio \-oc \-O /dev/rmt/c0s0 +.sp +.in -2 +(note the escaped regular expression meta-character). +.IP \(en 2 The -.Nm -utility syntax is a superset of the syntax specified by the -.St -p1003.2 -standard. -.Pp -The options and the -.Ic -amin , -.Ic -anewer , -.Ic -asince , -.Ic -cmin , -.Ic -cnewer , -.Ic -csince , -.Ic -delete , -.Ic -empty , -.Ic -execdir , -.Ic -follow , -.Ic -fstype , -.Ic -iname , -.Ic -inum , -.Ic -iregex , -.Ic -links , -.Ic -ls , -.Ic -maxdepth , -.Ic -mindepth , -.Ic -mmin , -.Ic -path , -.Ic -print0 , -.Ic -printx , -.Ic -regex , -.Ic -rm , -and -.Ic -since -primaries are extensions to -.St -p1003.2 . -.Pp -Historically, the -.Fl d , -.Fl h , +.I \-cpio and -.Fl x -options were implemented using the primaries -.Dq Ic -depth , -.Dq Ic -follow , -and -.Dq Ic -xdev . -These primaries always evaluated to true, and always -took effect when the -.Ar expression -was parsed, before the file system traversal began. -As a result, some legal expressions could be confusing. -For example, in the expression -.Dq Ic -print Ic -or Ic -depth , -.Ic -print -always evaluates to true, so the standard meaning of -.Ic -or -implies that -.Ic -depth -would never be evaluated, but that is not what happens; -in fact, -.Ic -depth -takes effect immediately, without testing whether -.Ic -print -returns true or false. -.Pp -Historically, the operator -.Dq Ic -or -was implemented as -.Dq Ic -o , -and the operator -.Dq Ic -and -was implemented as -.Dq Ic -a . -.Pp -Historic implementations of the -.Dq Ic -exec -and -.Dq Ic -ok -primaries did not replace the string -.Dq Ic {} -in the utility name or the -utility arguments if it did not appear as a separate argument. -This version replaces it no matter where in the utility name or arguments -it appears. -.Pp -Support for -.Dq Ic -exec Ar ... Ic \&+ -is consistent with -.Em IEEE PASC Interpretation 1003.2 #210 , -though the feature originated in -.Tn SVR4 . -.Pp +.I \-ncpio +operands will automatically exclude file names +that contain newline characters +with this +.I find +implementation. +.PP The -.Ic -delete -primary does not interact well with other options that cause the file system -tree traversal options to be changed. -.Sh HISTORY -A much simpler -.Nm -command appeared in First Edition AT&T Unix. -The syntax had become similar to the present version by -the time of the Fifth Edition. -.Sh BUGS -The special characters used by -.Nm -are also special characters to many shell programs. -In particular, the characters -.Dq \&* , -.Dq \&[ , -.Dq \&] , -.Dq \&? , -.Dq \&( , -.Dq \&) , -.Dq \&! , -.Dq \e , -and -.Dq \&; -may have to be escaped from the shell. -.Pp -As there is no delimiter separating options and file names or file -names and the -.Ar expression , -it is difficult to specify files named -.Dq -xdev -or -.Dq \&! . -These problems are handled by the -.Fl f -option and the -.Xr getopt 3 -.Dq -- -construct. +.I \-print0 +operand supported by some other implementations +is considered a very limited work-around +since it does not allow the output to be processed +by utilities unaware of NUL characters; +it has therefore not been included here. diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c index 7b966bd..c56fedc 100644 --- a/usr.bin/find/find.c +++ b/usr.bin/find/find.c @@ -1,297 +1,1533 @@ -/* $NetBSD: find.c,v 1.30 2016/06/13 00:04:40 pgoyette Exp $ */ - -/*- - * Copyright (c) 1991, 1993, 1994 - * 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. +/* find COMPILE: cc -o find -s -O -i find.c -lS */ +/* + * Changes by Gunnar Ritter, Freiburg i. Br., Germany, September 2003. + */ +/* from Unix 7th Edition /usr/src/cmd/find.c */ +/* + * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved. * * 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 + * Redistributions of source code and documentation must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. + * 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. 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. + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed or owned by Caldera + * International, Inc. + * Neither the name of Caldera International, Inc. nor the names of + * other 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. + * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA + * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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 -#include +#if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4 +#define USED __attribute__ ((used)) +#elif defined __GNUC__ +#define USED __attribute__ ((unused)) +#else +#define USED +#endif +#if defined (SU3) +static const char sccsid[] USED = "@(#)find_su3.sl 1.45 (gritter) 5/8/06"; +#elif defined (SUS) +static const char sccsid[] USED = "@(#)find_sus.sl 1.45 (gritter) 5/8/06"; +#else +static const char sccsid[] USED = "@(#)find.sl 1.45 (gritter) 5/8/06"; +#endif -#include -#include -#include -#include #include -#include #include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef major +#include +#endif +#if __NetBSD_Version__>= 300000000 +#include +#define statfs statvfs +#endif +#include "getdir.h" +#define A_DAY 86400L /* a day full of seconds */ +#define EQ(x, y) (strcmp(x, y)==0) + +#ifndef MNTTYPE_IGNORE +#define MNTTYPE_IGNORE "" +#endif + +#ifndef S_IFDOOR +#define S_IFDOOR 0xD000 +#endif -#include "find.h" +#ifndef S_IFNWK +#define S_IFNWK 0x9000 +#endif -static int ftscompare(const FTSENT **, const FTSENT **); +#undef ctime +#define ctime find_ctime + +static char *Pathname; + +struct aggregate { /* for exec ... {} + */ + long a_cnt; /* count of arguments */ + long a_cur; /* current position in aggregate */ + long a_csz; /* aggregate current length */ + long a_msz; /* aggregate maximum length */ + char **a_vec; /* arguments */ + char *a_spc; /* aggregate space */ + long a_maxarg; /* maximum arguments in e_vec */ +}; + +struct anode { + int (*F)(struct anode *); + union anode_l { + struct anode *L; + char *pat; + time_t t; + uid_t u; + gid_t g; + ino_t i; + nlink_t link; + off_t sz; + mode_t per; + int com; + FILE *fp; + char *fstype; + } l; + union anode_r { + struct anode *R; + int s; + pid_t pid; + struct aggregate *a; + } r; +}; +static char *Fname; +static time_t Now; +static int Argc, + Ai, + Pi; +static char **Argv; +/* cpio stuff */ +static int Cpio; + +static struct stat Statb; /* - * find_formplan -- - * process the command line and create a "plan" corresponding to the - * command arguments. + * Keep track of all visited directories, to avoid loops caused by + * symbolic links and to free storage and close files after fork(). */ -PLAN * -find_formplan(char **argv) -{ - PLAN *plan, *tail, *new; - - /* - * for each argument in the command line, determine what kind of node - * it is, create the appropriate node type and add the new plan node - * to the end of the existing plan. The resulting plan is a linked - * list of plan nodes. For example, the string: - * - * % find . -name foo -newer bar -print - * - * results in the plan: - * - * [-name foo]--> [-newer bar]--> [-print] - * - * in this diagram, `[-name foo]' represents the plan node generated - * by c_name() with an argument of foo and `-->' represents the - * plan->next pointer. - */ - for (plan = tail = NULL; *argv;) { - if (!(new = find_create(&argv))) - continue; - if (plan == NULL) - tail = plan = new; - else { - tail->next = new; - tail = new; +static struct visit { + struct getdb *v_db; /* getdb struct for this level */ + ino_t v_ino; /* inode number */ + int v_fd; /* file descriptor */ + dev_t v_dev; /* device id */ +} *visited; +static int vismax; /* number of members in visited */ + +/* + * For -fstype, keep track of all filesystem types known to the system. If + * we had st_fstype in struct stat as SVR4 does, this would be far more + * reliable. + */ +#if defined (__linux__) || defined (_AIX) || defined (__hpux) +static struct fstype { + dev_t fsdev; /* device id of filesystem */ + char *fstype; /* filesystem type */ +} *fstypes, *fscur; +#endif /* __linux__ || _AIX || __hpux */ + +static int Home = -1; +static int wanthome; +static mode_t um; /* user's umask */ +static const char *progname; +static int status; /* exit status */ +static int depth; /* -depth flag */ +static int Print = 1; /* implicit -print */ +static int Prune; /* -prune at this point */ +static int Mount; /* -mount, -xdev */ +static int Execplus; /* have a -exec command {} + node */ +static int HLflag; /* -H or -L option given */ +static char *Statfs; /* result of statfs() on FreeBSD */ +static int incomplete; /* encountered an incomplete statement */ +static int sysv3; + +static int (*statfn)(const char *, struct stat *) = lstat; + +static struct anode *expr(void); +static struct anode *e1(void); +static struct anode *e2(void); +static struct anode *e3(void); +static struct anode *mk(struct anode *); +static void oper(const char **); +static char *nxtarg(int); +static int and(struct anode *); +static int or(struct anode *); +static int not(struct anode *); +static int glob(struct anode *); +static int print(struct anode *); +static int prune(struct anode *); +static int null(struct anode *); +static int mtime(struct anode *); +static int atime(struct anode *); +static int ctime(struct anode *); +static int user(struct anode *); +static int ino(struct anode *); +static int group(struct anode *); +static int nogroup(struct anode *); +static int nouser(struct anode *); +static int links(struct anode *); +static int size(struct anode *); +static int sizec(struct anode *); +static int perm(struct anode *); +static int type(struct anode *); +static int exeq(struct anode *); +static int ok(struct anode *); +static int cpio(struct anode *); +static int newer(struct anode *); +static int cnewer(struct anode *); +static int anewer(struct anode *); +static int fstype(struct anode *); +static int local(struct anode *); +static int scomp(long long, long long, char); +static int doex(int, struct aggregate *); +static struct aggregate *mkagg(long); +static uid_t getunum(const char *); +static gid_t getgnum(const char *); +static const char *getuser(uid_t); +static const char *getgroup(gid_t); +#if defined (__linux__) || defined (_AIX) || defined (__hpux) +static void getfscur(dev_t); +static void getfstypes(void); +#endif /* __linux__ || _AIX || __hpux */ +static int descend(char *, struct anode *, int); +static int descend1(char *, struct anode *, int); +static int descend2(char *, struct anode *, int); +static void setpath(char *, const char *, int); +static void pr(const char *, ...); +static void er(const char *, ...); +static void usage(void); +static void *srealloc(void *, size_t); +static void mkcpio(struct anode *, const char *, int); +static void trailer(struct anode *, int); +static void mknewer(struct anode *, const char *, int (*)(struct anode *)); +static mode_t newmode(const char *ms, const mode_t pm); + +int +main(int argc, char **argv) +{ + struct anode *exlist; + struct anode nlist = { null, { 0 }, { 0 } }; + int paths; + register char *sp = 0; + int i, j; + + time(&Now); + umask(um = umask(0)); + progname = basename(argv[0]); + setlocale(LC_COLLATE, ""); + setlocale(LC_CTYPE, ""); + if (getenv("SYSV3") != NULL) + sysv3 = 1; + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-' || argv[i][1] == '\0') + break; + if (argv[i][1] == '-') { + i++; + break; + } + for (j = 1; argv[i][j]; j++) + if (argv[i][j] != 'H' && argv[i][j] != 'L') + goto brk; + for (j = 1; argv[i][j]; j++) + HLflag = argv[i][j]; + } +brk: if (HLflag == 'L') + statfn = stat; + argc -= i - 1; + argv += i - 1; + Argc = argc; Argv = argv; + if(argc<2) { + pr("insufficient number of arguments"); + usage(); + } + for(Ai = paths = 1; Ai < argc; ++Ai, ++paths) + if(*Argv[Ai] == '-' || EQ(Argv[Ai], "(") || EQ(Argv[Ai], "!")) + break; + if(paths == 1) /* no path-list */ + usage(); + if(Ai 2) + wanthome = 1; + if (wanthome && (Home = open(".", O_RDONLY)) < 0) + er("bad starting directory"); + for(Pi = 1; Pi < paths; ++Pi) { + if (Pi > 1 && Home >= 0 && fchdir(Home) < 0) + er("bad starting directory"); + setpath(Pathname, Argv[Pi], 0); + Fname = sp = Pathname; + do + if (sp[0] == '/') + Fname = &sp[1]; + while (*sp++); + descend(Pathname, exlist, 0); /* to find files that match */ + } + if(Cpio || Execplus) + trailer(exlist, 1); + exit(status); +} + +/* compile time functions: priority is expr()=Argc || strikes==3) + er("incomplete statement"); + if(Ai>=Argc) { + if (must >= 0) + strikes++; + incomplete = 1; + Ai = Argc + 1; + return(""); } + return(Argv[Ai++]); +} - /* - * if the user didn't specify one of -print, -ok, -fprint, -exec, or - * -exit, then -print is assumed so we bracket the current expression - * with parens, if necessary, and add a -print node on the end. - */ - if (!isoutput) { - if (plan == NULL) { - new = c_print(NULL, 0, NULL); - tail = plan = new; +/* execution time functions */ +static int and(register struct anode *p) +{ + return(((*p->l.L->F)(p->l.L)) && ((*p->r.R->F)(p->r.R))?1:0); +} +static int or(register struct anode *p) +{ + return(((*p->l.L->F)(p->l.L)) || ((*p->r.R->F)(p->r.R))?1:0); +} +static int not(register struct anode *p) +{ + return( !((*p->l.L->F)(p->l.L))); +} +static int glob(register struct anode *p) +{ + int val; +#ifdef __GLIBC__ + /* avoid glibc's broken [^...] */ + extern char **environ; + char **savenv = environ; + char *newenv[] = { "POSIXLY_CORRECT=", NULL }; + environ = newenv; +#endif /* __GLIBC__ */ + val = fnmatch(p->l.pat, Fname, FNM_PATHNAME) == 0; +#ifdef __GLIBC__ + environ = savenv; +#endif /* __GLIBC__ */ + return val; +} +/*ARGSUSED*/ +static int print(register struct anode *p) +{ + puts(Pathname); + return(1); +} +/*ARGSUSED*/ +static int prune(register struct anode *p) +{ + if (!depth) + Prune = 1; + return(1); +} +/*ARGSUSED*/ +static int null(register struct anode *p) +{ + return(1); +} +static int mtime(register struct anode *p) +{ + return(scomp((Now - Statb.st_mtime) / A_DAY, p->l.t, p->r.s)); +} +static int atime(register struct anode *p) +{ + return(scomp((Now - Statb.st_atime) / A_DAY, p->l.t, p->r.s)); +} +static int ctime(register struct anode *p) +{ + return(scomp((Now - Statb.st_ctime) / A_DAY, p->l.t, p->r.s)); +} +static int user(register struct anode *p) +{ + return(scomp(Statb.st_uid, p->l.u, p->r.s)); +} +static int ino(register struct anode *p) +{ + return(scomp(Statb.st_ino, p->l.u, p->r.s)); +} +static int group(register struct anode *p) +{ + return(p->l.u == Statb.st_gid); +} +static int nogroup(register struct anode *p) +{ + return(getgroup(Statb.st_gid) == NULL); +} +static int nouser(register struct anode *p) +{ + return(getuser(Statb.st_uid) == NULL); +} +static int links(register struct anode *p) +{ + return(scomp(Statb.st_nlink, p->l.link, p->r.s)); +} +static int size(register struct anode *p) +{ + return(scomp(Statb.st_size?(Statb.st_size+511)>>9:0, p->l.sz, p->r.s)); +} +static int sizec(register struct anode *p) +{ + return(scomp(Statb.st_size, p->l.sz, p->r.s)); +} +static int perm(register struct anode *p) +{ + register int i; + i = (p->r.s=='-') ? p->l.per : 07777; /* '-' means only arg bits */ + return((Statb.st_mode & i & 07777) == p->l.per); +} +static int type(register struct anode *p) +{ + return((Statb.st_mode&S_IFMT)==p->l.per); +} +static int exeq(register struct anode *p) +{ + if (p->r.a) { + if (Pathname) { + size_t sz = strlen(Pathname) + 1; + if (p->r.a->a_csz + sz <= p->r.a->a_msz && + p->r.a->a_cur < p->r.a->a_maxarg-1) { + strcpy(p->r.a->a_vec[p->r.a->a_cur++] = + &p->r.a->a_spc[p->r.a->a_csz], + Pathname); + p->r.a->a_csz += sz; + return 1; + } else { + if (p->r.a->a_cur == 0) { + p->r.a->a_vec[p->r.a->a_cur++] = + Pathname; + p->r.a->a_vec[p->r.a->a_cur] = NULL; + } + else { + p->r.a->a_vec[p->r.a->a_cur] = NULL; + fflush(stdout); + doex(p->l.com, p->r.a); + return exeq(p); + } + } } else { - new = c_openparen(NULL, 0, NULL); - new->next = plan; - plan = new; - new = c_closeparen(NULL, 0, NULL); - tail->next = new; - tail = new; - new = c_print(NULL, 0, NULL); - tail->next = new; - tail = new; + if (p->r.a->a_cur == 0) + return 1; + p->r.a->a_vec[p->r.a->a_cur] = NULL; } } + fflush(stdout); /* to flush possible `-print' */ + return(doex(p->l.com, p->r.a)); +} +static int ok(struct anode *p) +{ + char c; int yes; + yes = 0; + fflush(stdout); /* to flush possible `-print' */ + fprintf(stderr, "< %s ... %s >? ", Argv[p->l.com], Pathname); + if (read(0, &c, 1) != 1) + exit(2); + yes = c == 'y'; + if (c != '\n') + while (read(0, &c, 1) == 1 && c != '\n'); + if(yes) return(doex(p->l.com, 0)); + return(0); +} - /* - * the command line has been completely processed into a search plan - * except for the (, ), !, and -o operators. Rearrange the plan so - * that the portions of the plan which are affected by the operators - * are moved into operator nodes themselves. For example: - * - * [!]--> [-name foo]--> [-print] - * - * becomes - * - * [! [-name foo] ]--> [-print] - * - * and - * - * [(]--> [-depth]--> [-name foo]--> [)]--> [-print] - * - * becomes - * - * [expr [-depth]-->[-name foo] ]--> [-print] - * - * operators are handled in order of precedence. - */ - - plan = paren_squish(plan); /* ()'s */ - plan = not_squish(plan); /* !'s */ - plan = or_squish(plan); /* -o's */ - return (plan); +static int cpio(struct anode *p) +{ + if (strchr(Pathname, '\n')) { + pr("file name \"%s\" contains a newline character; " + "file not archived", Pathname); + status |= 1; + } else + fprintf(p->l.fp, "%s\n", Pathname); + return(1); +} +static int newer(register struct anode *p) +{ + return Statb.st_mtime > p->l.t; +} +static int anewer(register struct anode *p) +{ + return Statb.st_atime > p->l.t; +} +static int cnewer(register struct anode *p) +{ + return Statb.st_ctime > p->l.t; +} +static int fstype(register struct anode *p) +{ +#if defined (__linux__) || defined (_AIX) || defined (__hpux) + return(EQ(fscur->fstype, p->l.fstype)); +#elif defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) \ + || defined (__DragonFly__) || defined (__APPLE__) + return(EQ(Statfs, p->l.fstype)); +#else + return(EQ(Statb.st_fstype, p->l.fstype)); +#endif +} +static int local(register struct anode *p) +{ +#if defined (__linux__) || defined (_AIX) || defined (__hpux) + return(strcmp(fscur->fstype, "nfs") && strcmp(fscur->fstype, "smbfs")); +#elif defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) \ + || defined (__DragonFly__) || defined (__APPLE__) + return(strcmp(Statfs, "nfs") != 0); +#else + return(strcmp(Statb.st_fstype, "nfs") != 0); +#endif +} + +/* support functions */ +/* funny signed compare */ +static int scomp(register long long a, register long long b, register char s) +{ + if(s == '+') + return(a > b); + if(s == '-') + return(a < (b * -1)); + return(a == b); } static int -ftscompare(const FTSENT **e1, const FTSENT **e2) +doex(int com, struct aggregate *a) +{ + register int np; + register char *na; + char **oargv; + int oargc; + static char **nargv; + static int narga; + static int ccode; + pid_t pid; + + ccode = np = 0; + oargv = Argv; + oargc = com; + while (na=oargv[oargc++]) { + if (np >= narga-1) + nargv = srealloc(nargv, (narga+=20) * sizeof *nargv); + if(strcmp(na, ";")==0 && oargv == Argv) break; + if(strcmp(na, "{}")==0 && oargv == Argv) { + if (a) { + oargv = a->a_vec; + oargc = 0; + } else + nargv[np++] = Pathname; + } + else nargv[np++] = na; + } + if (a) { + a->a_cur = 0; + a->a_csz = 0; + } + if (np==0) return(9); + nargv[np] = 0; + if(pid = fork()) /*parent*/ while (wait(&ccode) != pid); + else { /*child*/ + if (fchdir(Home) < 0) { + pr("bad starting directory"); + _exit(1); + } + execvp(nargv[0], nargv); + _exit(1); + } + if (a && ccode) { + if (WIFSIGNALED(ccode)) + status |= WTERMSIG(ccode) | 0200; + else if (WIFEXITED(ccode)) + status |= WEXITSTATUS(ccode); + } + return(ccode && a==NULL ? 0:1); +} + +static struct aggregate *mkagg(long baselen) { + static size_t envsz; + extern char **environ; + register int i; + struct aggregate *a; - return (strcoll((*e1)->fts_name, (*e2)->fts_name)); + a = srealloc(NULL, sizeof *a); + if (envsz == 0) + for (i = 0; environ[i]; i++) + envsz += strlen(environ[i]) + 1; + a->a_msz = sysconf(_SC_ARG_MAX) - baselen - envsz - 2048; + a->a_spc = srealloc(NULL, a->a_msz); + a->a_maxarg = 8192; + a->a_vec = srealloc(NULL, a->a_maxarg * sizeof *a->a_vec); + a->a_csz = 0; + a->a_cur = 0; + Execplus = 1; + return a; } -static sigset_t ss; -static bool notty; +static uid_t getunum(const char *s) { /* find user name and return number */ + struct passwd *pwd; + char *x; + uid_t u; -static __inline void -sig_init(void) + if ((pwd = getpwnam(s)) != NULL) + return pwd->pw_uid; + u = strtol(s, &x, 10); + if (*x == '\0') + return u; + er("cannot find %s name", s); + /*NOTREACHED*/ + return 0; +} + +static gid_t getgnum(const char *s) { /* find group name and return number */ + struct group *grp; + char *x; + gid_t g; + + if ((grp = getgrnam(s)) != NULL) + return grp->gr_gid; + g = strtol(s, &x, 10); + if (*x == '\0') + return g; + er("cannot find %s name", s); + /*NOTREACHED*/ + return 0; +} + +#define CACHESIZE 16 + +static const char *getuser(uid_t uid) { - struct sigaction sa; - notty = !(isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) || - isatty(STDERR_FILENO)); - if (notty) - return; - sigemptyset(&ss); - sigaddset(&ss, SIGHUP); /* block SIGINFO */ + static struct { + char *name; + uid_t uid; + } cache[CACHESIZE]; + static int last; + int i; + struct passwd *pwd; + const char *name; - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_RESTART; - sa.sa_handler = show_path; - (void)sigaction(SIGHUP, &sa, NULL); + for (i = 0; i < CACHESIZE && cache[i].name; i++) + if (cache[i].uid == uid) + goto found; + if ((pwd = getpwuid(uid)) != NULL) + name = pwd->pw_name; + else + name = ""; + if (i >= CACHESIZE) { + if (last >= CACHESIZE) + last = 0; + i = last++; + } + if (cache[i].name) + free(cache[i].name); + cache[i].name = strdup(name); + cache[i].uid = uid; +found: return cache[i].name[0] ? cache[i].name : NULL; +} +static const char *getgroup(gid_t gid) +{ + static struct { + char *name; + gid_t gid; + } cache[CACHESIZE]; + static int last; + int i; + struct group *grp; + const char *name; + + for (i = 0; i < CACHESIZE && cache[i].name; i++) + if (cache[i].gid == gid) + goto found; + if ((grp = getgrgid(gid)) != NULL) + name = grp->gr_name; + else + name = ""; + if (i >= CACHESIZE) { + if (last >= CACHESIZE) + last = 0; + i = last++; + } + if (cache[i].name) + free(cache[i].name); + cache[i].name = strdup(name); + cache[i].gid = gid; +found: return cache[i].name[0] ? cache[i].name : NULL; } -static __inline void -sig_lock(sigset_t *s) +#if defined (__linux__) || defined (_AIX) || defined (__hpux) +static void getfscur(dev_t dev) { - if (notty) - return; - sigprocmask(SIG_BLOCK, &ss, s); + int i; + + for (i = 0; fstypes[i].fstype; i++) + if (fstypes[i].fsdev == dev) { + fscur = &fstypes[i]; + return; + } + er("filesystem type for %s unknown", Pathname); } -static __inline void -sig_unlock(const sigset_t *s) +static void getfstypes(void) { - if (notty) + struct stat st; + FILE *fp; + struct mntent *mp; +#ifdef __hpux + const char mtab[] = "/etc/mnttab"; +#else /* __linux__, _AIX */ + const char mtab[] = "/etc/mtab"; +#endif /* __linux__, _AIX */ + int i = 0; + + if (fstypes) return; - sigprocmask(SIG_SETMASK, s, NULL); + if ((fp = setmntent(mtab, "r")) == NULL) + er("cannot open %s: %s", mtab, strerror(errno)); + while ((mp = getmntent(fp)) != NULL) { + if (EQ(mp->mnt_type, MNTTYPE_IGNORE)) + continue; + if (stat(mp->mnt_dir, &st) < 0) + continue; + fstypes = srealloc(fstypes, (i+1) * sizeof *fstypes); + fstypes[i].fsdev = st.st_dev; + fstypes[i].fstype = strdup(mp->mnt_type); + i++; + } + endmntent(fp); } - -FTS *tree; /* pointer to top of FTS hierarchy */ -FTSENT *g_entry; /* shared with SIGINFO handler */ +#endif /* __linux__ || _AIX || __hpux */ /* - * find_execute -- - * take a search plan and an array of search paths and executes the plan - * over all FTSENT's returned for the given search paths. + * First part of descend, called for any file found. */ -int -find_execute(PLAN *plan, char **paths) +static int descend(char *fname, struct anode *exlist, int level) { - PLAN *p; - int r, rval, cval; - sigset_t s; + struct stat ost; + register char *c1; + int i; + int rv = 0; + + if(statfn(fname, &Statb)<0) { + if (statfn != lstat && lstat(fname, &Statb) == 0) + nof: c1 = "cannot follow symbolic link %s: %s"; + else if (sysv3) + c1 = "stat() failed: %s: %s"; + else if (errno == ENOENT || errno == ENOTDIR) + c1 = "cannot open %s: %s"; + else + c1 = "stat() error %s: %s"; + pr(c1, Pathname, strerror(errno)); + status = 18; + return(0); + } + if (level == 0 && HLflag == 'H' && (Statb.st_mode&S_IFMT) == S_IFLNK) { + struct stat nst; + if (stat(fname, &nst) == 0) + Statb = nst; + else if (errno == ELOOP) + goto nof; + } +#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || \ + defined (__DragonFly__) || defined (__APPLE__) + if (Statfs != NULL) { + static struct statfs sf; + if (statfs(fname, &sf) < 0) { + pr("statfs() error %s: %s", Pathname, strerror(errno)); + status = 18; + return(0); + } + Statfs = sf.f_fstypename; + } +#endif /* __FreeBSD__, __NetBSD__, __OpenBSD__, __DragonFly__, __APPLE__ */ + if (Mount) { + static dev_t curdev; + if (level == 0) + curdev = Statb.st_dev; + else if (curdev != Statb.st_dev) + return(0); + } + Prune = 0; + if (!depth) { +#if defined (__linux__) || defined (_AIX) || defined (__hpux) + if (fstypes) + getfscur(Statb.st_dev); +#endif /* __linux__ || _AIX || __hpux */ + if((*exlist->F)(exlist) && Print) + puts(Pathname); + } else + ost = Statb; + if(Prune || (Statb.st_mode&S_IFMT)!=S_IFDIR) + goto reg; + if (statfn != lstat) { + for (i = 0; i < level; i++) + if (Statb.st_dev == visited[i].v_dev && + Statb.st_ino == visited[i].v_ino) { +#ifdef SU3 + pr("Symbolic link loop at %s", Pathname); + status = 18; +#endif /* SU3 */ + goto reg; + } + } + if (level >= vismax) { + vismax += 20; + visited = srealloc(visited, sizeof *visited * vismax); + } + visited[level].v_dev = Statb.st_dev; + visited[level].v_ino = Statb.st_ino; - cval = 1; + rv = descend1(fname, exlist, level); - if (!(tree = fts_open(paths, ftsoptions, issort ? ftscompare : NULL))) - err(1, "ftsopen"); +reg: + if (depth) { + Statb = ost; +#if defined (__linux__) || defined (_AIX) || defined (__hpux) + if (fstypes) + getfscur(Statb.st_dev); +#endif /* __linux__ || _AIX || __hpux */ + if ((*exlist->F)(exlist) && Print) + puts(Pathname); + } + return(rv); +} - sig_init(); - sig_lock(&s); - for (rval = 0; cval && (g_entry = fts_read(tree)) != NULL;) { - switch (g_entry->fts_info) { - case FTS_D: - if (isdepth) - continue; - break; - case FTS_DP: - if (!isdepth) - continue; - break; - case FTS_DNR: - case FTS_ERR: - case FTS_NS: - sig_unlock(&s); - (void)fflush(stdout); - warnx("%s: %s", - g_entry->fts_path, strerror(g_entry->fts_errno)); - rval = 1; - sig_lock(&s); - continue; - } -#define BADCH " \t\n\\'\"" - if (isxargs && strpbrk(g_entry->fts_path, BADCH)) { - sig_unlock(&s); - (void)fflush(stdout); - warnx("%s: illegal path", g_entry->fts_path); - rval = 1; - sig_lock(&s); +/* + * Second part of descend, called for any directory found. + */ +static int descend1(char *fname, struct anode *exlist, int level) +{ + int dir = 0; /* open directory */ + register char *c1; + struct getdb *db; + register struct direc *dp; + int endofname; + int err; + int oflags = O_RDONLY; + +#ifdef O_DIRECTORY + oflags |= O_DIRECTORY; +#endif +#ifdef O_NOFOLLOW + if (statfn == lstat && (HLflag != 'H' || level > 0)) + oflags |= O_NOFOLLOW; +#endif + if ((dir = open(fname, oflags)) < 0 || + fcntl(dir, F_SETFD, FD_CLOEXEC) < 0 || + fchdir(dir) < 0) { + if (dir >= 0) + close(dir); + else if (errno == EMFILE && descend2(fname, exlist, level)) + /* + * A possible performance improvement would be to + * call descend2() in the directory above, since + * the current method involves one fork() call per + * subdirectory at this level. The condition occurs + * so rarely that it seems hardly worth optimization + * though. + */ + return 0; + pr("cannot open %s: %s", Pathname, strerror(errno)); + status = 18; + return 0; + } + if ((db = getdb_alloc(Pathname, dir)) == NULL) { + write(2, "no memory\n", 10); + _exit(077); + } + visited[level].v_db = db; + visited[level].v_fd = dir; + for(c1 = Pathname; *c1; ++c1); + if(*(c1-1) == '/') + --c1; + endofname = c1 - Pathname; + + while ((dp = getdir(db, &err)) != NULL) { + if((dp->d_name[0]=='.' && dp->d_name[1]=='\0') || + (dp->d_name[0]=='.' && + dp->d_name[1]=='.' && dp->d_name[2]=='\0')) continue; + setpath(&Pathname[endofname], dp->d_name, 1); + Fname = &Pathname[endofname+1]; + if(descend(Fname, exlist, level+1)) { + if (fchdir(dir) < 0) + er("bad directory tree"); } + } + Pathname[endofname] = '\0'; + getdb_free(db); + if (err) { + pr("cannot read dir %s: %s", Pathname, strerror(errno)); + status = 18; + } + close(dir); + visited[level].v_fd = -1; + return 1; +} + +/* + * Third part of descend, called if the limit of open file descriptors + * is exceeded (EMFILE). + */ +static int descend2(char *fname, struct anode *exlist, int level) +{ + pid_t pid; + int i; - /* - * Call all the functions in the execution plan until one is - * false or all have been executed. This is where we do all - * the work specified by the user on the command line. - */ - sig_unlock(&s); - for (p = plan; p && (p->eval)(p, g_entry); p = p->next) - if (p->type == N_EXIT) { - rval = p->exit_val; - cval = 0; + if (Cpio || Execplus) + trailer(exlist, 0); + fflush(stdout); + switch (pid = fork()) { + case 0: + for (i = 0; i < level-1; i++) { + if (visited[i].v_fd >= 0) { + getdb_free(visited[i].v_db); + close(visited[i].v_fd); + visited[i].v_fd = -1; } - sig_lock(&s); + } + status |= 0; + descend1(fname, exlist, level); + if (Cpio || Execplus) + trailer(exlist, 0); + exit(status); + /*NOTREACHED*/ + default: + while (waitpid(pid, &i, 0) != pid); + if (i && WIFSIGNALED(i)) { + struct rlimit rl; + + rl.rlim_cur = rl.rlim_max = 0; + setrlimit(RLIMIT_CORE, &rl); + raise(WTERMSIG(i)); + pause(); + } + if (i) + status |= WEXITSTATUS(i); + return 1; + case -1: + return 0; + } +} +static void setpath(char *eos, const char *fn, int slash) +{ + static char *pathend; + char *opath; + + for (;;) { + if (eos >= pathend) { + pathend += 14; + opath = Pathname; + Pathname = srealloc(Pathname, pathend - Pathname); + eos += Pathname - opath; + pathend += Pathname - opath; + } + if (slash) { + *eos++ = '/'; + slash = 0; + } else + if ((*eos++ = *fn++) == '\0') + break; } +} - sig_unlock(&s); - if (g_entry == NULL && errno) - err(1, "fts_read"); - (void)fts_close(tree); +static void pr(const char *s, ...) +{ + va_list ap; - /* - * Cleanup any plans with leftover state. - * Keep the last non-zero return value. - */ - if ((r = find_traverse(plan, plan_cleanup, NULL)) != 0) - rval = r; + fprintf(stderr, "%s: ", progname); + va_start(ap, s); + vfprintf(stderr, s, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void er(const char *s, ...) +{ + va_list ap; - return (rval); + fprintf(stderr, "%s: ", progname); + va_start(ap, s); + vfprintf(stderr, s, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(1); } +static void usage(void) +{ + er("path-list predicate-list"); +} + +static void *srealloc(void *op, size_t n) +{ + void *np; + + if ((np = realloc(op, n)) == NULL) { + write(2, "no memory\n", 10); + _exit(077); + } + return np; +} + +static void mkcpio(struct anode *p, const char *b, int ascii) +{ + int fd, pd[2]; + char flags[20], *cp; + + p->F = cpio; + if (*b == '\0') + return; + depth = 1; + Print = 0; + Cpio = 1; + if (pipe(pd) < 0 || (p->l.fp = fdopen(pd[1], "w")) == NULL) + er("pipe() %s", strerror(errno)); + if ((fd = creat(b, 0666)) < 0) + er("cannot create %s", b); + switch (p->r.pid = fork()) { + case -1: + er("can't fork"); + /*NOTREACHED*/ + case 0: + dup2(pd[0], 0); + close(pd[0]); + close(pd[1]); + dup2(fd, 1); + close(fd); + cp = flags; + *cp++ = '-'; + *cp++ = 'o'; + *cp++ = 'B'; + if (ascii) + *cp++ = 'c'; + if (statfn == stat) + *cp++ = 'L'; + *cp = '\0'; + execlp("cpio", "cpio", flags, NULL); + pr("cannot exec cpio: %s", strerror(errno)); + _exit(0177); + /*NOTREACHED*/ + } + close(pd[0]); + close(fd); +} + +static void +trailer(register struct anode *p, int termcpio) +{ + char *Opath = Pathname; + Pathname = 0; + if (p->F == or || p->F == and) { + trailer(p->l.L, termcpio); + trailer(p->r.R, termcpio); + } else if (p->F == not) + trailer(p->l.L, termcpio); + else if (p->F == cpio) { + if (termcpio) { + int s; + + fclose(p->l.fp); + while (waitpid(p->r.pid, &s, 0) != p->r.pid); + if (s) { + if (WIFEXITED(s)) + status |= WEXITSTATUS(s); + else if (WIFSIGNALED(s)) + status |= WTERMSIG(s) | 0200; + } + } else + fflush(p->l.fp); + } else if (p->F == exeq && p->r.a) + exeq(p); + Pathname = Opath; +} + +static void +mknewer(struct anode *p, const char *b, int (*f)(struct anode *)) +{ + if (*b && stat(b, &Statb) < 0) + er("cannot access %s", b); + p->l.t = Statb.st_mtime; + p->F = f; +} + +/* + * Changes by Gunnar Ritter, Freiburg i. Br., Germany, September 2003. + */ +/* from Unix 7th Edition /usr/src/cmd/chmod.c */ /* - * find_traverse -- - * traverse the plan tree and execute func() on all plans. This - * does not evaluate each plan's eval() function; it is intended - * for operations that must run on all plans, such as state - * cleanup. + * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * Redistributions of source code and documentation must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. + * 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. + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed or owned by Caldera + * International, Inc. + * Neither the name of Caldera International, Inc. nor the names of + * other contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. * - * If any func() returns non-zero, then so will find_traverse(). + * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA + * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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. */ -int -find_traverse(PLAN *plan, int (*func)(PLAN *, void *), void *arg) -{ - PLAN *p; - int r, rval; - - rval = 0; - for (p = plan; p; p = p->next) { - if ((r = func(p, arg)) != 0) - rval = r; - if (p->type == N_EXPR || p->type == N_OR) { - if (p->p_data[0]) - if ((r = find_traverse(p->p_data[0], - func, arg)) != 0) - rval = r; - if (p->p_data[1]) - if ((r = find_traverse(p->p_data[1], - func, arg)) != 0) - rval = r; + +#define USER 05700 /* user's bits */ +#define GROUP 02070 /* group's bits */ +#define OTHER 00007 /* other's bits */ +#define ALL 07777 /* all */ + +#define READ 00444 /* read permit */ +#define WRITE 00222 /* write permit */ +#define EXEC 00111 /* exec permit */ +#define SETID 06000 /* set[ug]id */ +#define STICKY 01000 /* sticky bit */ + +#ifndef S_ENFMT +#define S_ENFMT 02000 /* mandatory locking bit */ +#endif + +static mode_t absol(const char **); +static mode_t who(const char **, mode_t *); +static int what(const char **); +static mode_t where(const char **, mode_t, int *, int *, const mode_t); + +static mode_t +newmode(const char *ms, const mode_t pm) +{ + register mode_t o, m, b; + int lock, setsgid = 0, cleared = 0, copy = 0; + mode_t nm, om, mm; + const char *mo = ms; + + nm = om = pm; + m = absol(&ms); + if (!*ms) { + nm = m; + goto out; + } + if ((lock = (nm&S_IFMT) != S_IFDIR && (nm&(S_ENFMT|S_IXGRP)) == S_ENFMT) + == 01) + nm &= ~(mode_t)S_ENFMT; + do { + m = who(&ms, &mm); + while (o = what(&ms)) { + b = where(&ms, nm, &lock, ©, pm); + switch (o) { + case '+': + nm |= b & m & ~mm; + if (b & S_ISGID) + setsgid = 1; + if (lock & 04) + lock |= 02; + break; + case '-': + nm &= ~(b & m & ~mm); + if (b & S_ISGID) + setsgid = 1; + if (lock & 04) + lock = 0; + break; + case '=': + nm &= ~m; + nm |= b & m & ~mm; + lock &= ~01; + if (lock & 04) + lock |= 02; + om = 0; + if (copy == 0) + cleared = 1; + break; + } + lock &= ~04; } + } while (*ms++ == ','); + if (*--ms) + er("bad permissions: %s", mo); +out: if (pm & S_IFDIR) { + if ((pm & S_ISGID) && setsgid == 0) + nm |= S_ISGID; + else if ((nm & S_ISGID) && setsgid == 0) + nm &= ~(mode_t)S_ISGID; + } + return(nm); +} + +static mode_t +absol(const char **ms) +{ + register int c, i; + + i = 0; + while ((c = *(*ms)++) >= '0' && c <= '7') + i = (i << 3) + (c - '0'); + (*ms)--; + return(i); +} + +static mode_t +who(const char **ms, mode_t *mp) +{ + register int m; + + m = 0; + *mp = 0; + for (;;) switch (*(*ms)++) { + case 'u': + m |= USER; + continue; + case 'g': + m |= GROUP; + continue; + case 'o': + m |= OTHER; + continue; + case 'a': + m |= ALL; + continue; + default: + (*ms)--; + if (m == 0) { + m = ALL; + *mp = um; + } + return m; + } +} + +static int +what(const char **ms) +{ + switch (**ms) { + case '+': + case '-': + case '=': + return *(*ms)++; + } + return(0); +} + +static mode_t +where(const char **ms, mode_t om, int *lock, int *copy, const mode_t pm) +{ + register mode_t m; + + m = 0; + *copy = 0; + switch (**ms) { + case 'u': + m = (om & USER) >> 6; + goto dup; + case 'g': + m = (om & GROUP) >> 3; + goto dup; + case 'o': + m = (om & OTHER); + dup: + *copy = 1; + m &= (READ|WRITE|EXEC); + m |= (m << 3) | (m << 6); + ++(*ms); + return m; + } + for (;;) switch (*(*ms)++) { + case 'r': + m |= READ; + continue; + case 'w': + m |= WRITE; + continue; + case 'x': + m |= EXEC; + continue; + case 'X': + if ((pm&S_IFMT) == S_IFDIR || (pm & EXEC)) + m |= EXEC; + continue; + case 'l': + if ((pm&S_IFMT) != S_IFDIR) + *lock |= 04; + continue; + case 's': + m |= SETID; + continue; + case 't': + m |= STICKY; + continue; + default: + (*ms)--; + return m; } - return rval; } diff --git a/usr.bin/find/find.h b/usr.bin/find/find.h deleted file mode 100644 index 912e60d..0000000 --- a/usr.bin/find/find.h +++ /dev/null @@ -1,138 +0,0 @@ -/* $NetBSD: find.h,v 1.26 2016/06/13 00:04:40 pgoyette Exp $ */ - -/*- - * Copyright (c) 1990, 1993 - * 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. 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. - * - * from: @(#)find.h 8.1 (Berkeley) 6/6/93 - */ - -#include -#include - -/* node type */ -enum ntype { - N_AND = 1, /* must start > 0 */ - N_AMIN, N_ANEWER, N_ASINCE, N_ATIME, N_CLOSEPAREN, N_CMIN, N_CNEWER, - N_CSINCE, N_CTIME, N_DEPTH, N_EMPTY, N_EXEC, N_EXECDIR, N_EXIT, - N_EXPR, N_FALSE, N_FLAGS, N_FOLLOW, N_FPRINT, N_FSTYPE, N_GROUP, - N_INAME, N_INUM, N_IREGEX, N_LINKS, N_LS, N_MINDEPTH, N_MAXDEPTH, - N_MMIN, N_MTIME, N_NAME, N_NEWER, N_NOGROUP, N_NOT, N_NOUSER, N_OK, - N_OPENPAREN, N_OR, N_PATH, N_PERM, N_PRINT, N_PRINT0, N_PRINTX, - N_PRUNE, N_REGEX, N_SINCE, N_SIZE, N_TYPE, N_USER, N_XDEV, N_DELETE -}; - -/* node definition */ -typedef struct _plandata { - struct _plandata *next; /* next node */ - int (*eval)(struct _plandata *, FTSENT *); - /* node evaluation function */ -#define F_EQUAL 1 /* [acm]time inum links size */ -#define F_LESSTHAN 2 -#define F_GREATER 3 -#define F_NEEDOK 1 /* exec ok */ -#define F_PLUSSET 2 /* -exec ... {} + */ -#define F_MTFLAG 1 /* fstype */ -#define F_MTTYPE 2 -#define F_ATLEAST 1 /* perm */ - int flags; /* private flags */ - enum ntype type; /* plan node type */ - union { - u_int32_t _f_data; /* flags */ - gid_t _g_data; /* gid */ - ino_t _i_data; /* inode */ - mode_t _m_data; /* mode mask */ - nlink_t _l_data; /* link count */ - off_t _o_data; /* file size */ - time_t _t_data; /* time value */ - struct timespec _ts_data; /* time value */ - uid_t _u_data; /* uid */ - short _mt_data; /* mount flags */ - struct _plandata *_p_data[2]; /* PLAN trees */ - struct _ex { - char **_e_argv; /* argv array */ - char **_e_orig; /* original strings */ - int *_e_len; /* allocated length */ - char **_ep_bxp; /* ptr to 1st addt'l arg */ - char *_ep_p; /* current buffer pointer */ - char *_ep_bbp; /* begin buffer pointer */ - char *_ep_ebp; /* end buffer pointer */ - int _ep_maxargs; /* max #args */ - int _ep_narg; /* # addt'l args */ - int _ep_rval; /* return value */ - } ex; - char *_a_data[2]; /* array of char pointers */ - char *_c_data; /* char pointer */ - int _exit_val; /* exit value */ - int _max_data; /* tree depth */ - int _min_data; /* tree depth */ - regex_t _regexp_data; /* compiled regexp */ - FILE *_fprint_file; /* file stream for -fprint */ - } p_un; -} PLAN; -#define a_data p_un._a_data -#define c_data p_un._c_data -#define i_data p_un._i_data -#define f_data p_un._f_data -#define g_data p_un._g_data -#define l_data p_un._l_data -#define m_data p_un._m_data -#define mt_data p_un._mt_data -#define o_data p_un._o_data -#define p_data p_un._p_data -#define t_data p_un._t_data -#define ts_data p_un._ts_data -#define u_data p_un._u_data -#define e_argv p_un.ex._e_argv -#define e_orig p_un.ex._e_orig -#define e_len p_un.ex._e_len -#define ep_p p_un.ex._ep_p -#define ep_bbp p_un.ex._ep_bbp -#define ep_ebp p_un.ex._ep_ebp -#define ep_bxp p_un.ex._ep_bxp -#define ep_cnt p_un.ex._ep_cnt -#define ep_maxargs p_un.ex._ep_maxargs -#define ep_nline p_un.ex._ep_nline -#define ep_narg p_un.ex._ep_narg -#define ep_rval p_un.ex._ep_rval -#define exit_val p_un._exit_val -#define max_data p_un._max_data -#define min_data p_un._min_data -#define regexp_data p_un._regexp_data -#define fprint_file p_un._fprint_file - -typedef struct _option { - const char *name; /* option name */ - enum ntype token; /* token type */ - PLAN *(*create)(char ***, int, char *); /* create function */ - int arg; /* function needs arg */ -} OPTION; - -#include "extern.h" diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c deleted file mode 100644 index be880e9..0000000 --- a/usr.bin/find/function.c +++ /dev/null @@ -1,1956 +0,0 @@ -/* $NetBSD: function.c,v 1.77 2018/09/04 15:16:15 kre Exp $ */ - -/*- - * Copyright (c) 1990, 1993 - * 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. 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 -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "find.h" - -#define COMPARE(a, b) { \ - switch (plan->flags) { \ - case F_EQUAL: \ - return (a == b); \ - case F_LESSTHAN: \ - return (a < b); \ - case F_GREATER: \ - return (a > b); \ - default: \ - abort(); \ - } \ -} - -static int64_t find_parsenum(PLAN *, const char *, const char *, char *); -static void run_f_exec(PLAN *); - int f_always_true(PLAN *, FTSENT *); - int f_amin(PLAN *, FTSENT *); - int f_anewer(PLAN *, FTSENT *); - int f_asince(PLAN *, FTSENT *); - int f_atime(PLAN *, FTSENT *); - int f_cmin(PLAN *, FTSENT *); - int f_cnewer(PLAN *, FTSENT *); - int f_csince(PLAN *, FTSENT *); - int f_ctime(PLAN *, FTSENT *); - int f_delete(PLAN *, FTSENT *); - int f_empty(PLAN *, FTSENT *); - int f_exec(PLAN *, FTSENT *); - int f_execdir(PLAN *, FTSENT *); - int f_false(PLAN *, FTSENT *); - int f_flags(PLAN *, FTSENT *); - int f_fprint(PLAN *, FTSENT *); - int f_fstype(PLAN *, FTSENT *); - int f_group(PLAN *, FTSENT *); - int f_iname(PLAN *, FTSENT *); - int f_inum(PLAN *, FTSENT *); - int f_links(PLAN *, FTSENT *); - int f_ls(PLAN *, FTSENT *); - int f_mindepth(PLAN *, FTSENT *); - int f_maxdepth(PLAN *, FTSENT *); - int f_mmin(PLAN *, FTSENT *); - int f_mtime(PLAN *, FTSENT *); - int f_name(PLAN *, FTSENT *); - int f_newer(PLAN *, FTSENT *); -/* - * Unimplemented Gnu findutils options - * - int f_newerBB(PLAN *, FTSENT *); - int f_newerBa(PLAN *, FTSENT *); - int f_newerBc(PLAN *, FTSENT *); - int f_newerBm(PLAN *, FTSENT *); - int f_newerBt(PLAN *, FTSENT *); - int f_neweraB(PLAN *, FTSENT *); - int f_newerac(PLAN *, FTSENT *); - int f_neweram(PLAN *, FTSENT *); - int f_newerca(PLAN *, FTSENT *); - int f_newercm(PLAN *, FTSENT *); - int f_newercB(PLAN *, FTSENT *); - int f_newermB(PLAN *, FTSENT *); - int f_newerma(PLAN *, FTSENT *); - int f_newermc(PLAN *, FTSENT *); - * - */ - int f_nogroup(PLAN *, FTSENT *); - int f_nouser(PLAN *, FTSENT *); - int f_path(PLAN *, FTSENT *); - int f_perm(PLAN *, FTSENT *); - int f_print(PLAN *, FTSENT *); - int f_print0(PLAN *, FTSENT *); - int f_printx(PLAN *, FTSENT *); - int f_prune(PLAN *, FTSENT *); - int f_regex(PLAN *, FTSENT *); - int f_since(PLAN *, FTSENT *); - int f_size(PLAN *, FTSENT *); - int f_type(PLAN *, FTSENT *); - int f_user(PLAN *, FTSENT *); - int f_not(PLAN *, FTSENT *); - int f_or(PLAN *, FTSENT *); -static PLAN *c_regex_common(char ***, int, enum ntype, bool); -static PLAN *palloc(enum ntype, int (*)(PLAN *, FTSENT *)); - -extern int dotfd; -extern FTS *tree; -extern time_t now; - -/* - * find_parsenum -- - * Parse a string of the form [+-]# and return the value. - */ -static int64_t -find_parsenum(PLAN *plan, const char *option, const char *vp, char *endch) -{ - int64_t value; - const char *str; - char *endchar; /* Pointer to character ending conversion. */ - - /* Determine comparison from leading + or -. */ - str = vp; - switch (*str) { - case '+': - ++str; - plan->flags = F_GREATER; - break; - case '-': - ++str; - plan->flags = F_LESSTHAN; - break; - default: - plan->flags = F_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 = strtoll(str, &endchar, 10); - if (value == 0 && endchar == str) - errx(1, "%s: %s: illegal numeric value", option, vp); - if (endchar[0] && (endch == NULL || endchar[0] != *endch)) - errx(1, "%s: %s: illegal trailing character", option, vp); - if (endch) - *endch = endchar[0]; - return (value); -} - -/* - * find_parsedate -- - * - * Validate the timestamp argument or report an error - */ -static time_t -find_parsedate(PLAN *plan, const char *option, const char *vp) -{ - time_t timestamp; - - errno = 0; - timestamp = parsedate(vp, NULL, NULL); - if (timestamp == -1 && errno != 0) - errx(1, "%s: %s: invalid timestamp value", option, vp); - return timestamp; -} - -/* - * The value of n for the inode times (atime, ctime, and mtime) is a range, - * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with - * -n, such that "-mtime -1" would be less than 0 days, which isn't what the - * user wanted. Correct so that -1 is "less than 1". - */ -#define TIME_CORRECT(p, ttype) \ - if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ - ++((p)->t_data); - -/* - * -amin n functions -- - * - * True if the difference between the file access time and the - * current time is n 1 minute periods. - */ -int -f_amin(PLAN *plan, FTSENT *entry) -{ -#define SECSPERMIN 60 - COMPARE((now - entry->fts_statp->st_atime + - SECSPERMIN - 1) / SECSPERMIN, plan->t_data); -} - -PLAN * -c_amin(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_AMIN, f_amin); - new->t_data = find_parsenum(new, opt, arg, NULL); - TIME_CORRECT(new, N_AMIN); - return (new); -} - -/* - * -anewer file functions -- - * - * True if the current file has been accessed more recently - * than the access time of the file named by the pathname - * file. - */ -int -f_anewer(PLAN *plan, FTSENT *entry) -{ - - return timespeccmp(&entry->fts_statp->st_atim, &plan->ts_data, >); -} - -PLAN * -c_anewer(char ***argvp, int isok, char *opt) -{ - char *filename = **argvp; - PLAN *new; - struct stat sb; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - if (stat(filename, &sb)) - err(1, "%s: %s", opt, filename); - new = palloc(N_ANEWER, f_anewer); - new->ts_data = sb.st_atim; - return (new); -} - -/* - * -asince "timestamp" functions -- - * - * True if the file access time is greater than the timestamp value - */ -int -f_asince(PLAN *plan, FTSENT *entry) -{ - COMPARE(entry->fts_statp->st_atime, plan->t_data); -} - -PLAN * -c_asince(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_ASINCE, f_asince); - new->t_data = find_parsedate(new, opt, arg); - new->flags = F_GREATER; - return (new); -} - -/* - * -atime n functions -- - * - * True if the difference between the file access time and the - * current time is n 24 hour periods. - */ -int -f_atime(PLAN *plan, FTSENT *entry) -{ -#define SECSPERDAY 24*60*60 - COMPARE((now - entry->fts_statp->st_atime + - SECSPERDAY - 1) / SECSPERDAY, plan->t_data); -} - -PLAN * -c_atime(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_ATIME, f_atime); - new->t_data = find_parsenum(new, opt, arg, NULL); - TIME_CORRECT(new, N_ATIME); - return (new); -} - -/* - * -cmin n functions -- - * - * True if the difference between the last change of file - * status information and the current time is n 24 hour periods. - */ -int -f_cmin(PLAN *plan, FTSENT *entry) -{ - COMPARE((now - entry->fts_statp->st_ctime + - SECSPERMIN - 1) / SECSPERMIN, plan->t_data); -} - -PLAN * -c_cmin(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_CMIN, f_cmin); - new->t_data = find_parsenum(new, opt, arg, NULL); - TIME_CORRECT(new, N_CMIN); - return (new); -} - -/* - * -cnewer file functions -- - * - * True if the current file has been changed more recently - * than the changed time of the file named by the pathname - * file. - */ -int -f_cnewer(PLAN *plan, FTSENT *entry) -{ - - return timespeccmp(&entry->fts_statp->st_ctim, &plan->ts_data, >); -} - -PLAN * -c_cnewer(char ***argvp, int isok, char *opt) -{ - char *filename = **argvp; - PLAN *new; - struct stat sb; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - if (stat(filename, &sb)) - err(1, "%s: %s ", opt, filename); - new = palloc(N_CNEWER, f_cnewer); - new->ts_data = sb.st_ctim; - return (new); -} - -/* - * -csince "timestamp" functions -- - * - * True if the file status change time is greater than the timestamp value - */ -int -f_csince(PLAN *plan, FTSENT *entry) -{ - COMPARE(entry->fts_statp->st_ctime, plan->t_data); -} - -PLAN * -c_csince(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_CSINCE, f_csince); - new->t_data = find_parsedate(new, opt, arg); - new->flags = F_GREATER; - return (new); -} - -/* - * -ctime n functions -- - * - * True if the difference between the last change of file - * status information and the current time is n 24 hour periods. - */ -int -f_ctime(PLAN *plan, FTSENT *entry) -{ - COMPARE((now - entry->fts_statp->st_ctime + - SECSPERDAY - 1) / SECSPERDAY, plan->t_data); -} - -PLAN * -c_ctime(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_CTIME, f_ctime); - new->t_data = find_parsenum(new, opt, arg, NULL); - TIME_CORRECT(new, N_CTIME); - return (new); -} - -/* - * -delete functions -- - * - * Always true. Makes its best shot and continues on regardless. - */ -int -f_delete(PLAN *plan, FTSENT *entry) -{ - int flags; - FILE *file; - /* ignore these from fts */ - if (strcmp(entry->fts_accpath, ".") == 0 || - strcmp(entry->fts_accpath, "..") == 0) - return 1; - - /* sanity check */ - if (isdepth == 0 || /* depth off */ - (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ - !(ftsoptions & FTS_PHYSICAL) || /* physical off */ - (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ - errx(1, "-delete: insecure options got turned on"); - - /* Potentially unsafe - do not accept relative paths whatsoever */ - if (entry->fts_level > 0 && strchr(entry->fts_accpath, '/') != NULL) - errx(1, "-delete: %s: relative path potentially not safe", - entry->fts_accpath); - - file = fopen(entry->fts_name, "r"); - if (file == NULL) { - warn("couldn't check flags for %s", entry->fts_name); - } else { - ioctl(fileno(file), FS_IOC_GETFLAGS, &flags); - /* turn off immutable bit if running as root */ - if ((geteuid() == 0) && (flags & FS_IMMUTABLE_FL)) { - flags = flags & ~FS_IMMUTABLE_FL; - ioctl(fileno(file), FS_IOC_SETFLAGS, &flags); - } - } - - /* rmdir directories, unlink everything else */ - if (S_ISDIR(entry->fts_statp->st_mode)) { - if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) - warn("-delete: rmdir(%s)", entry->fts_path); - } else { - if (unlink(entry->fts_accpath) < 0) - warn("-delete: unlink(%s)", entry->fts_path); - } - - /* "succeed" */ - return 1; -} - -PLAN * -c_delete(char ***argvp, int isok, char *opt) -{ - - ftsoptions &= ~FTS_NOSTAT; /* no optimize */ - ftsoptions |= FTS_PHYSICAL; /* disable -follow */ - ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ - isoutput = 1; /* possible output */ - isdepth = 1; /* -depth implied */ - - return palloc(N_DELETE, f_delete); -} - -/* - * -depth functions -- - * - * Always true, causes descent of the directory hierarchy to be done - * so that all entries in a directory are acted on before the directory - * itself. - */ -int -f_always_true(PLAN *plan, FTSENT *entry) -{ - - return (1); -} - -PLAN * -c_depth(char ***argvp, int isok, char *opt) -{ - isdepth = 1; - - return (palloc(N_DEPTH, f_always_true)); -} - -/* - * -empty functions -- - * - * True if the file or directory is empty - */ -int -f_empty(PLAN *plan, FTSENT *entry) -{ - if (S_ISREG(entry->fts_statp->st_mode) && - entry->fts_statp->st_size == 0) - return (1); - if (S_ISDIR(entry->fts_statp->st_mode)) { - struct dirent *dp; - int empty; - DIR *dir; - - empty = 1; - dir = opendir(entry->fts_accpath); - if (dir == NULL) - return (0); - for (dp = readdir(dir); dp; dp = readdir(dir)) - if (dp->d_name[0] != '.' || - (dp->d_name[1] != '\0' && - (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { - empty = 0; - break; - } - closedir(dir); - return (empty); - } - return (0); -} - -PLAN * -c_empty(char ***argvp, int isok, char *opt) -{ - ftsoptions &= ~FTS_NOSTAT; - - return (palloc(N_EMPTY, f_empty)); -} - -/* - * [-exec | -ok] utility [arg ... ] ; functions -- - * [-exec | -ok] utility [arg ... ] {} + functions -- - * - * If the end of the primary expression is delimited by a - * semicolon: true if the executed utility returns a zero value - * as exit status. If "{}" occurs anywhere, it gets replaced by - * the current pathname. - * - * If the end of the primary expression is delimited by a plus - * sign: always true. Pathnames for which the primary is - * evaluated shall be aggregated into sets. The utility will be - * executed once per set, with "{}" replaced by the entire set of - * pathnames (as if xargs). "{}" must appear last. - * - * 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. - */ -int -f_exec(PLAN *plan, FTSENT *entry) -{ - size_t cnt; - int l; - pid_t pid; - int status; - - if (plan->flags & F_PLUSSET) { - /* - * Confirm sufficient buffer space, then copy the path - * to the buffer. - */ - l = strlen(entry->fts_path); - if (plan->ep_p + l < plan->ep_ebp) { - plan->ep_bxp[plan->ep_narg++] = - strcpy(plan->ep_p, entry->fts_path); - plan->ep_p += l + 1; - - if (plan->ep_narg == plan->ep_maxargs) - run_f_exec(plan); - } else { - /* - * Without sufficient space to copy in the next - * argument, run the command to empty out the - * buffer before re-attepting the copy. - */ - run_f_exec(plan); - if ((plan->ep_p + l < plan->ep_ebp)) { - plan->ep_bxp[plan->ep_narg++] - = strcpy(plan->ep_p, entry->fts_path); - plan->ep_p += l + 1; - } else - errx(1, "insufficient space for argument"); - } - return (1); - } else { - for (cnt = 0; plan->e_argv[cnt]; ++cnt) - if (plan->e_len[cnt]) - brace_subst(plan->e_orig[cnt], - &plan->e_argv[cnt], - entry->fts_path, - &plan->e_len[cnt]); - if (plan->flags & F_NEEDOK && !queryuser(plan->e_argv)) - return (0); - - /* Don't mix output of command with find output. */ - fflush(stdout); - fflush(stderr); - - switch (pid = vfork()) { - case -1: - err(1, "vfork"); - /* NOTREACHED */ - case 0: - if (fchdir(dotfd)) { - warn("chdir"); - _exit(1); - } - execvp(plan->e_argv[0], plan->e_argv); - warn("%s", plan->e_argv[0]); - _exit(1); - } - pid = waitpid(pid, &status, 0); - return (pid != -1 && WIFEXITED(status) - && !WEXITSTATUS(status)); - } -} - -static void -run_f_exec(PLAN *plan) -{ - pid_t pid; - int rval, status; - - /* Ensure arg list is null terminated. */ - plan->ep_bxp[plan->ep_narg] = NULL; - - /* Don't mix output of command with find output. */ - fflush(stdout); - fflush(stderr); - - switch (pid = vfork()) { - case -1: - err(1, "vfork"); - /* NOTREACHED */ - case 0: - if (fchdir(dotfd)) { - warn("chdir"); - _exit(1); - } - execvp(plan->e_argv[0], plan->e_argv); - warn("%s", plan->e_argv[0]); - _exit(1); - } - - /* Clear out the argument list. */ - plan->ep_narg = 0; - plan->ep_bxp[plan->ep_narg] = NULL; - /* As well as the argument buffer. */ - plan->ep_p = plan->ep_bbp; - *plan->ep_p = '\0'; - - pid = waitpid(pid, &status, 0); - if (WIFEXITED(status)) - rval = WEXITSTATUS(status); - else - rval = -1; - - /* - * If we have a non-zero exit status, preserve it so find(1) can - * later exit with it. - */ - if (rval) - plan->ep_rval = rval; -} - -/* - * 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. - * - * If -exec ... {} +, use only the first array, but make it large - * enough to hold 5000 args (cf. src/usr.bin/xargs/xargs.c for a - * discussion), and then allocate ARG_MAX - 4K of space for args. - */ -PLAN * -c_exec(char ***argvp, int isok, char *opt) -{ - PLAN *new; /* node returned */ - size_t cnt; - int brace, lastbrace; - char **argv, **ap, *p; - - isoutput = 1; - - new = palloc(N_EXEC, f_exec); - if (isok) - new->flags |= F_NEEDOK; - - /* - * Terminate if we encounter an arg exactly equal to ";", or an - * arg exactly equal to "+" following an arg exactly equal to - * "{}". - */ - for (ap = argv = *argvp, brace = 0;; ++ap) { - if (!*ap) - errx(1, "%s: no terminating \";\" or \"+\"", opt); - lastbrace = brace; - brace = 0; - if (strcmp(*ap, "{}") == 0) - brace = 1; - if (strcmp(*ap, ";") == 0) - break; - if (strcmp(*ap, "+") == 0 && lastbrace) { - new->flags |= F_PLUSSET; - break; - } - } - - /* - * POSIX says -ok ... {} + "need not be supported," and it does - * not make much sense anyway. - */ - if (new->flags & F_NEEDOK && new->flags & F_PLUSSET) - errx(1, "%s: terminating \"+\" not permitted.", opt); - - if (new->flags & F_PLUSSET) { - size_t c, bufsize; - - cnt = ap - *argvp - 1; /* units are words */ - new->ep_maxargs = ARG_MAX / (sizeof (char *) + 16); - if (new->ep_maxargs > 5000) - new->ep_maxargs = 5000; - new->e_argv = malloc((cnt + new->ep_maxargs) - * sizeof(*new->e_argv)); - - /* We start stuffing arguments after the user's last one. */ - new->ep_bxp = &new->e_argv[cnt]; - new->ep_narg = 0; - - /* - * Count up the space of the user's arguments, and - * subtract that from what we allocate. - */ -#define MAXARG (ARG_MAX - 4 * 1024) - for (argv = *argvp, c = 0, cnt = 0; - argv < ap; - ++argv, ++cnt) { - c += strlen(*argv) + 1; - if (c >= MAXARG) - errx(1, "Arguments too long"); - new->e_argv[cnt] = *argv; - } - if (c + new->ep_maxargs * sizeof (char *) >= MAXARG) - errx(1, "Arguments too long"); - bufsize = MAXARG - c - new->ep_maxargs * sizeof (char *); - - /* - * Allocate, and then initialize current, base, and - * end pointers. - */ - new->ep_p = new->ep_bbp = malloc(bufsize + 1); - new->ep_ebp = new->ep_bbp + bufsize - 1; - new->ep_rval = 0; - } else { /* !F_PLUSSET */ - cnt = ap - *argvp + 1; - new->e_argv = malloc(cnt * sizeof(*new->e_argv)); - new->e_orig = malloc(cnt * sizeof(*new->e_orig)); - new->e_len = malloc(cnt * sizeof(*new->e_len)); - - 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] = - malloc(MAXPATHLEN); - new->e_len[cnt] = MAXPATHLEN; - break; - } - if (!*p) { - new->e_argv[cnt] = *argv; - new->e_len[cnt] = 0; - } - } - new->e_orig[cnt] = NULL; - } - - new->e_argv[cnt] = NULL; - *argvp = argv + 1; - return (new); -} - -/* - * -execdir 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 unqualified pathname. - * The current directory for the execution of utility is the same as - * the directory where the file lives. - */ -int -f_execdir(PLAN *plan, FTSENT *entry) -{ - size_t cnt; - pid_t pid; - int status; - char *file; - - /* XXX - if file/dir ends in '/' this will not work -- can it? */ - if ((file = strrchr(entry->fts_path, '/'))) - file++; - else - file = entry->fts_path; - - for (cnt = 0; plan->e_argv[cnt]; ++cnt) - if (plan->e_len[cnt]) - brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], - file, &plan->e_len[cnt]); - - /* don't mix output of command with find output */ - fflush(stdout); - fflush(stderr); - - switch (pid = vfork()) { - case -1: - err(1, "fork"); - /* NOTREACHED */ - case 0: - execvp(plan->e_argv[0], plan->e_argv); - warn("%s", plan->e_argv[0]); - _exit(1); - } - pid = waitpid(pid, &status, 0); - return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); -} - -/* - * c_execdir -- - * 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_execdir(char ***argvp, int isok, char *opt) -{ - PLAN *new; /* node returned */ - size_t cnt; - char **argv, **ap, *p; - - ftsoptions &= ~FTS_NOSTAT; - isoutput = 1; - - new = palloc(N_EXECDIR, f_execdir); - - for (ap = argv = *argvp;; ++ap) { - if (!*ap) - errx(1, "%s: no terminating \";\"", opt); - if (**ap == ';') - break; - } - - cnt = ap - *argvp + 1; - new->e_argv = malloc(cnt * sizeof(*new->e_argv)); - new->e_orig = malloc(cnt * sizeof(*new->e_orig)); - new->e_len = malloc(cnt * sizeof(*new->e_len)); - - 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] = malloc(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); -} - -PLAN * -c_exit(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - /* not technically true, but otherwise '-print' is implied */ - isoutput = 1; - - new = palloc(N_EXIT, f_always_true); - - if (arg) { - (*argvp)++; - new->exit_val = find_parsenum(new, opt, arg, NULL); - } else - new->exit_val = 0; - - return (new); -} - - -/* - * -false function - */ -int -f_false(PLAN *plan, FTSENT *entry) -{ - - return (0); -} - -PLAN * -c_false(char ***argvp, int isok, char *opt) -{ - return (palloc(N_FALSE, f_false)); -} - -/* - * -follow functions -- - * - * Always true, causes symbolic links to be followed on a global - * basis. - */ -PLAN * -c_follow(char ***argvp, int isok, char *opt) -{ - ftsoptions &= ~FTS_PHYSICAL; - ftsoptions |= FTS_LOGICAL; - - return (palloc(N_FOLLOW, f_always_true)); -} - -/* -fprint functions -- - * - * Causes the current pathame to be written to the defined output file. - */ -int -f_fprint(PLAN *plan, FTSENT *entry) -{ - - if (-1 == fprintf(plan->fprint_file, "%s\n", entry->fts_path)) - warn("fprintf"); - - return(1); - - /* no descriptors are closed; they will be closed by - operating system when this find command exits. */ -} - -PLAN * -c_fprint(char ***argvp, int isok, char *opt) -{ - PLAN *new; - - isoutput = 1; /* do not assume -print */ - - new = palloc(N_FPRINT, f_fprint); - - if (NULL == (new->fprint_file = fopen(**argvp, "w"))) - err(1, "%s: %s: cannot create file", opt, **argvp); - - (*argvp)++; - return (new); -} -#define MNT_RDONLY 0x01 -/* - * -fstype functions -- - * - * True if the file is of a certain type. - */ -int -f_fstype(PLAN *plan, FTSENT *entry) -{ - static char *fstype = NULL; - FILE *file; - struct mntent *mnt; - int flags; - char *filename = strdup(entry->fts_name); - char *temp = strrchr(filename, '/'); - temp[0] = '\0'; - - file = setmntent("/etc/mtab", "r"); - if (file == NULL) { - err(1, "couldn't read mount table"); - } - - while ((mnt = getmntent(file)) != NULL) { - if (!strncmp(mnt->mnt_dir, filename, strlen(mnt->mnt_dir))) { - fstype = strdup(mnt->mnt_type); - if (strstr(mnt->mnt_opts, MNTOPT_RO) != NULL) { - flags |= MNT_RDONLY; - } - break; - } - } - endmntent(file); - - switch (plan->flags) { - case F_MTFLAG: - return (flags & plan->mt_data); - case F_MTTYPE: - return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0); - default: - abort(); - } -} - -PLAN * -c_fstype(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_FSTYPE, f_fstype); - - switch (*arg) { - case 'r': - if (!strcmp(arg, "rdonly")) { - new->flags = F_MTFLAG; - new->mt_data = MNT_RDONLY; - return (new); - } - break; - } - - new->flags = F_MTTYPE; - new->c_data = arg; - return (new); -} - -/* - * -group gname functions -- - * - * True if the file belongs to the group gname. If gname is numeric and - * an equivalent of the getgrnam() function does not return a valid group - * name, gname is taken as a group ID. - */ -int -f_group(PLAN *plan, FTSENT *entry) -{ - - COMPARE(entry->fts_statp->st_gid, plan->g_data); -} - -PLAN * -c_group(char ***argvp, int isok, char *opt) -{ - char *gname = **argvp; - PLAN *new; - struct group *g; - gid_t gid; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_GROUP, f_group); - g = getgrnam(gname); - if (g == NULL) { - if (atoi(gname) == 0 && gname[0] != '0' && - strcmp(gname, "+0") && strcmp(gname, "-0")) - errx(1, "%s: %s: no such group", opt, gname); - gid = find_parsenum(new, "-group", gname, NULL); - - } else { - new->flags = F_EQUAL; - gid = g->gr_gid; - } - - new->g_data = gid; - return (new); -} - -/* - * -inum n functions -- - * - * True if the file has inode # n. - */ -int -f_inum(PLAN *plan, FTSENT *entry) -{ - - COMPARE(entry->fts_statp->st_ino, plan->i_data); -} - -PLAN * -c_inum(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_INUM, f_inum); - new->i_data = find_parsenum(new, opt, arg, NULL); - return (new); -} - -/* - * -links n functions -- - * - * True if the file has n links. - */ -int -f_links(PLAN *plan, FTSENT *entry) -{ - - COMPARE(entry->fts_statp->st_nlink, plan->l_data); -} - -PLAN * -c_links(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_LINKS, f_links); - new->l_data = (nlink_t)find_parsenum(new, opt, arg, NULL); - return (new); -} - -/* - * -ls functions -- - * - * Always true - prints the current entry to stdout in "ls" format. - */ -int -f_ls(PLAN *plan, FTSENT *entry) -{ - - printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); - return (1); -} - -PLAN * -c_ls(char ***argvp, int isok, char *opt) -{ - - ftsoptions &= ~FTS_NOSTAT; - isoutput = 1; - - return (palloc(N_LS, f_ls)); -} - -/* - * - maxdepth n functions -- - * - * True if the current search depth is less than or equal to the - * maximum depth specified - */ -int -f_maxdepth(PLAN *plan, FTSENT *entry) -{ - extern FTS *tree; - - if (entry->fts_level >= plan->max_data) - fts_set(tree, entry, FTS_SKIP); - return (entry->fts_level <= plan->max_data); -} - -PLAN * -c_maxdepth(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - new = palloc(N_MAXDEPTH, f_maxdepth); - new->max_data = atoi(arg); - return (new); -} - -/* - * - mindepth n functions -- - * - * True if the current search depth is greater than or equal to the - * minimum depth specified - */ -int -f_mindepth(PLAN *plan, FTSENT *entry) -{ - return (entry->fts_level >= plan->min_data); -} - -PLAN * -c_mindepth(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - new = palloc(N_MINDEPTH, f_mindepth); - new->min_data = atoi(arg); - return (new); -} - -/* - * -mmin n functions -- - * - * True if the difference between the file modification time and the - * current time is n 24 hour periods. - */ -int -f_mmin(PLAN *plan, FTSENT *entry) -{ -#define SECSPERMIN 60 - COMPARE((now - entry->fts_statp->st_mtime + SECSPERMIN - 1) / - SECSPERMIN, plan->t_data); -} - -PLAN * -c_mmin(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_MMIN, f_mmin); - new->t_data = find_parsenum(new, opt, arg, NULL); - TIME_CORRECT(new, N_MMIN); - return (new); -} - -/* - * -mtime n functions -- - * - * True if the difference between the file modification time and the - * current time is n 24 hour periods. - */ -int -f_mtime(PLAN *plan, FTSENT *entry) -{ - COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) / - SECSPERDAY, plan->t_data); -} - -PLAN * -c_mtime(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_MTIME, f_mtime); - new->t_data = find_parsenum(new, opt, arg, NULL); - TIME_CORRECT(new, N_MTIME); - return (new); -} - -/* - * -name functions -- - * - * True if the basename of the filename being examined - * matches pattern using Pattern Matching Notation S3.14 - */ -int -f_name(PLAN *plan, FTSENT *entry) -{ - - return (!fnmatch(plan->c_data, entry->fts_name, 0)); -} - -PLAN * -c_name(char ***argvp, int isok, char *opt) -{ - char *pattern = **argvp; - PLAN *new; - - (*argvp)++; - new = palloc(N_NAME, f_name); - new->c_data = pattern; - return (new); -} - -/* - * -iname functions -- - * - * Similar to -name, but does case insensitive matching - * - */ -int -f_iname(PLAN *plan, FTSENT *entry) -{ - return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD)); -} - -PLAN * -c_iname(char ***argvp, int isok, char *opt) -{ - char *pattern = **argvp; - PLAN *new; - - (*argvp)++; - new = palloc(N_INAME, f_iname); - new->c_data = pattern; - return (new); -} - -/* - * -newer file functions -- - * - * True if the current file has been modified more recently - * than the modification time of the file named by the pathname - * file. - */ -int -f_newer(PLAN *plan, FTSENT *entry) -{ - - return timespeccmp(&entry->fts_statp->st_mtim, &plan->ts_data, >); -} - -PLAN * -c_newer(char ***argvp, int isok, char *opt) -{ - char *filename = **argvp; - PLAN *new; - struct stat sb; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - if (stat(filename, &sb)) - err(1, "%s: %s", opt, filename); - new = palloc(N_NEWER, f_newer); - new->ts_data = sb.st_mtim; - return (new); -} - -/* - * -nogroup functions -- - * - * True if file belongs to a user ID for which the equivalent - * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. - */ -int -f_nogroup(PLAN *plan, FTSENT *entry) -{ - return getgrgid(entry->fts_statp->st_gid) != NULL; -} - -PLAN * -c_nogroup(char ***argvp, int isok, char *opt) -{ - ftsoptions &= ~FTS_NOSTAT; - - return (palloc(N_NOGROUP, f_nogroup)); -} - -/* - * -nouser functions -- - * - * True if file belongs to a user ID for which the equivalent - * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. - */ -int -f_nouser(PLAN *plan, FTSENT *entry) -{ - return getpwuid(entry->fts_statp->st_uid) != NULL; -} - -PLAN * -c_nouser(char ***argvp, int isok, char *opt) -{ - ftsoptions &= ~FTS_NOSTAT; - - return (palloc(N_NOUSER, f_nouser)); -} - -/* - * -path functions -- - * - * True if the path of the filename being examined - * matches pattern using Pattern Matching Notation S3.14 - */ -int -f_path(PLAN *plan, FTSENT *entry) -{ - - return (!fnmatch(plan->c_data, entry->fts_path, 0)); -} - -PLAN * -c_path(char ***argvp, int isok, char *opt) -{ - char *pattern = **argvp; - PLAN *new; - - (*argvp)++; - new = palloc(N_NAME, f_path); - new->c_data = pattern; - return (new); -} - -/* - * -perm functions -- - * - * The mode argument is used to represent file mode bits. If it starts - * with a leading digit, it's treated as an octal mode, otherwise as a - * symbolic mode. - */ -int -f_perm(PLAN *plan, FTSENT *entry) -{ - mode_t mode; - - mode = entry->fts_statp->st_mode & - (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); - if (plan->flags == F_ATLEAST) - return ((plan->m_data | mode) == mode); - else - return (mode == plan->m_data); - /* NOTREACHED */ -} - -PLAN * -c_perm(char ***argvp, int isok, char *opt) -{ - char *perm = **argvp; - PLAN *new; - mode_t *set; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_PERM, f_perm); - - if (*perm == '-') { - new->flags = F_ATLEAST; - ++perm; - } - - if ((set = setmode(perm)) == NULL) - err(1, "%s: Cannot set file mode `%s'", opt, perm); - - new->m_data = getmode(set, 0); - free(set); - return (new); -} - -/* - * -print functions -- - * - * Always true, causes the current pathame to be written to - * standard output. - */ -int -f_print(PLAN *plan, FTSENT *entry) -{ - - (void)printf("%s\n", entry->fts_path); - return (1); -} - -int -f_print0(PLAN *plan, FTSENT *entry) -{ - - (void)fputs(entry->fts_path, stdout); - (void)fputc('\0', stdout); - return (1); -} - -int -f_printx(PLAN *plan, FTSENT *entry) -{ - char *cp; - - for (cp = entry->fts_path; *cp; cp++) { - if (*cp == '\'' || *cp == '\"' || *cp == ' ' || - *cp == '$' || *cp == '`' || - *cp == '\t' || *cp == '\n' || *cp == '\\') - fputc('\\', stdout); - - fputc(*cp, stdout); - } - - fputc('\n', stdout); - return (1); -} - -PLAN * -c_print(char ***argvp, int isok, char *opt) -{ - - isoutput = 1; - - return (palloc(N_PRINT, f_print)); -} - -PLAN * -c_print0(char ***argvp, int isok, char *opt) -{ - - isoutput = 1; - - return (palloc(N_PRINT0, f_print0)); -} - -PLAN * -c_printx(char ***argvp, int isok, char *opt) -{ - - isoutput = 1; - - return (palloc(N_PRINTX, f_printx)); -} - -/* - * -prune functions -- - * - * Prune a portion of the hierarchy. - */ -int -f_prune(PLAN *plan, FTSENT *entry) -{ - if (fts_set(tree, entry, FTS_SKIP)) - err(1, "%s", entry->fts_path); - return (1); -} - -PLAN * -c_prune(char ***argvp, int isok, char *opt) -{ - - return (palloc(N_PRUNE, f_prune)); -} - -/* - * -regex regexp (and related) functions -- - * - * True if the complete file path matches the regular expression regexp. - * For -regex, regexp is a case-sensitive (basic) regular expression. - * For -iregex, regexp is a case-insensitive (basic) regular expression. - */ -int -f_regex(PLAN *plan, FTSENT *entry) -{ - - return (regexec(&plan->regexp_data, entry->fts_path, 0, NULL, 0) == 0); -} - -static PLAN * -c_regex_common(char ***argvp, int isok, enum ntype type, bool icase) -{ - char errbuf[LINE_MAX]; - regex_t reg; - char *regexp = **argvp; - char *lineregexp; - PLAN *new; - int rv; - size_t len; - - (*argvp)++; - - len = strlen(regexp) + 1 + 6; - lineregexp = malloc(len); /* max needed */ - if (lineregexp == NULL) - err(1, NULL); - snprintf(lineregexp, len, "^%s(%s%s)$", - (regcomp_flags & REG_EXTENDED) ? "" : "\\", regexp, - (regcomp_flags & REG_EXTENDED) ? "" : "\\"); - rv = regcomp(®, lineregexp, REG_NOSUB|regcomp_flags| - (icase ? REG_ICASE : 0)); - free(lineregexp); - if (rv != 0) { - regerror(rv, ®, errbuf, sizeof errbuf); - errx(1, "regexp %s: %s", regexp, errbuf); - } - - new = palloc(type, f_regex); - new->regexp_data = reg; - return (new); -} - -PLAN * -c_regex(char ***argvp, int isok, char *opt) -{ - - return (c_regex_common(argvp, isok, N_REGEX, false)); -} - -PLAN * -c_iregex(char ***argvp, int isok, char *opt) -{ - - return (c_regex_common(argvp, isok, N_IREGEX, true)); -} - -/* - * -since "timestamp" functions -- - * - * True if the file modification time is greater than the timestamp value - */ -int -f_since(PLAN *plan, FTSENT *entry) -{ - COMPARE(entry->fts_statp->st_mtime, plan->t_data); -} - -PLAN * -c_since(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_SINCE, f_since); - new->t_data = find_parsedate(new, opt, arg); - new->flags = F_GREATER; - return (new); -} - -/* - * -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; - -int -f_size(PLAN *plan, FTSENT *entry) -{ - off_t size; - - size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / - FIND_SIZE : entry->fts_statp->st_size; - COMPARE(size, plan->o_data); -} - -PLAN * -c_size(char ***argvp, int isok, char *opt) -{ - char *arg = **argvp; - PLAN *new; - char endch; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_SIZE, f_size); - endch = 'c'; - new->o_data = find_parsenum(new, opt, arg, &endch); - if (endch == 'c') - divsize = 0; - return (new); -} - -/* - * -type c functions -- - * - * True if the type of the file is c, where c is b, c, d, p, f or w - * for block special file, character special file, directory, FIFO, - * regular file or whiteout respectively. - */ -int -f_type(PLAN *plan, FTSENT *entry) -{ - - return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); -} - -PLAN * -c_type(char ***argvp, int isok, char *opt) -{ - char *typestring = **argvp; - PLAN *new; - mode_t mask = (mode_t)0; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - switch (typestring[0]) { - case 'b': - mask = S_IFBLK; - break; - case 'c': - mask = S_IFCHR; - break; - case 'd': - mask = S_IFDIR; - break; - case 'f': - mask = S_IFREG; - break; - case 'l': - mask = S_IFLNK; - break; - case 'p': - mask = S_IFIFO; - break; - case 's': - mask = S_IFSOCK; - break; -#ifdef S_IFWHT - case 'W': - case 'w': - mask = S_IFWHT; -#ifdef FTS_WHITEOUT - ftsoptions |= FTS_WHITEOUT; -#endif - break; -#endif /* S_IFWHT */ - default: - errx(1, "%s: %s: unknown type", opt, typestring); - } - - new = palloc(N_TYPE, f_type); - new->m_data = mask; - return (new); -} - -/* - * -user uname functions -- - * - * True if the file belongs to the user uname. If uname is numeric and - * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not - * return a valid user name, uname is taken as a user ID. - */ -int -f_user(PLAN *plan, FTSENT *entry) -{ - - COMPARE(entry->fts_statp->st_uid, plan->u_data); -} - -PLAN * -c_user(char ***argvp, int isok, char *opt) -{ - char *username = **argvp; - PLAN *new; - struct passwd *p; - uid_t uid; - - (*argvp)++; - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_USER, f_user); - p = getpwnam(username); - if (p == NULL) { - if (atoi(username) == 0 && username[0] != '0' && - strcmp(username, "+0") && strcmp(username, "-0")) - errx(1, "%s: %s: no such user", opt, username); - uid = find_parsenum(new, opt, username, NULL); - - } else { - new->flags = F_EQUAL; - uid = p->pw_uid; - } - - new->u_data = uid; - return (new); -} - -/* - * -xdev functions -- - * - * Always true, causes find not to descend past directories that have a - * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) - */ -PLAN * -c_xdev(char ***argvp, int isok, char *opt) -{ - ftsoptions |= FTS_XDEV; - - return (palloc(N_XDEV, f_always_true)); -} - -/* - * ( expression ) functions -- - * - * True if expression is true. - */ -int -f_expr(PLAN *plan, FTSENT *entry) -{ - PLAN *p; - int state; - - state = 0; - for (p = plan->p_data[0]; - p && (state = (p->eval)(p, entry)); 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(char ***argvp, int isok, char *opt) -{ - - return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1)); -} - -PLAN * -c_closeparen(char ***argvp, int isok, char *opt) -{ - - return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1)); -} - -/* - * ! expression functions -- - * - * Negation of a primary; the unary NOT operator. - */ -int -f_not(PLAN *plan, FTSENT *entry) -{ - PLAN *p; - int state; - - state = 0; - for (p = plan->p_data[0]; - p && (state = (p->eval)(p, entry)); p = p->next); - return (!state); -} - -PLAN * -c_not(char ***argvp, int isok, char *opt) -{ - - return (palloc(N_NOT, f_not)); -} - -/* - * expression -o expression functions -- - * - * Alternation of primaries; the OR operator. The second expression is - * not evaluated if the first expression is true. - */ -int -f_or(PLAN *plan, FTSENT *entry) -{ - PLAN *p; - int state; - - state = 0; - for (p = plan->p_data[0]; - p && (state = (p->eval)(p, entry)); p = p->next); - - if (state) - return (1); - - for (p = plan->p_data[1]; - p && (state = (p->eval)(p, entry)); p = p->next); - return (state); -} - -PLAN * -c_or(char ***argvp, int isok, char *opt) -{ - - return (palloc(N_OR, f_or)); -} - -PLAN * -c_null(char ***argvp, int isok, char *opt) -{ - - return (NULL); -} - - -/* - * plan_cleanup -- - * Check and see if the specified plan has any residual state, - * and if so, clean it up as appropriate. - * - * At the moment, only N_EXEC has state. Two kinds: 1) - * lists of files to feed to subprocesses 2) State on exit - * statusses of past subprocesses. - */ -/* ARGSUSED1 */ -int -plan_cleanup(PLAN *plan, void *arg) -{ - if (plan->type==N_EXEC && plan->ep_narg) - run_f_exec(plan); - - return plan->ep_rval; /* Passed save exit-status up chain */ -} - -static PLAN * -palloc(enum ntype t, int (*f)(PLAN *, FTSENT *)) -{ - PLAN *new; - - if ((new = malloc(sizeof(PLAN))) == NULL) - err(1, NULL); - memset(new, 0, sizeof(PLAN)); - new->type = t; - new->eval = f; - return (new); -} diff --git a/usr.bin/find/getdir.c b/usr.bin/find/getdir.c new file mode 100644 index 0000000..3b3669d --- /dev/null +++ b/usr.bin/find/getdir.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2003 Gunnar Ritter + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + */ +/* Sccsid @(#)getdir.c 1.20 (gritter) 5/14/06 */ + +#include +#include +#include +#include +#include +#include +#include "getdir.h" + +#define DIBSIZE 5120 + +struct getdb { +#if !defined (__FreeBSD__) && !defined (__NetBSD__) && !defined (__OpenBSD__) \ + && !defined (__DragonFly__) && !defined (__APPLE__) + off_t g_offs; +#else /* __FreeBSD__, __NetBSD__, __OpenBSD__, __DragonFly__, __APPLE__ */ + long g_offs; +#endif /* __FreeBSD__, __NetBSD__, __OpenBSD__, __DragonFly__, __APPLE__ */ + struct dirent *g_dirp; + const char *g_path; + struct direc g_dic; + union { + char g_dirbuf[DIBSIZE+1]; + struct dirent g_dummy[1]; + } g_u; + int g_num; + int g_fd; +}; + +struct getdb * +getdb_alloc(const char *path, int fd) +{ + struct getdb *db; + + if ((db = malloc(sizeof *db)) == NULL) + return NULL; + db->g_dirp = NULL; + db->g_offs = 0; + db->g_fd = fd; + db->g_path = path; + return db; +} + +void +getdb_free(struct getdb *db) +{ + free(db); +} + +struct direc * +getdir(struct getdb *db, int *err) +{ + int reclen; + + *err = 0; + while (db->g_dirp == NULL) + { + /*LINTED*/ + db->g_num = getdents(db->g_fd, + (struct dirent *)db->g_u.g_dirbuf, + DIBSIZE); + if (db->g_num <= 0) { + if (db->g_num < 0) + *err = errno; + db->g_offs = 0; + return NULL; + } + /*LINTED*/ + db->g_dirp = (struct dirent *)db->g_u.g_dirbuf; + while (db->g_dirp && +#if !defined (__FreeBSD__) && !defined (__NetBSD__) && !defined (__OpenBSD__) \ + && !defined (__DragonFly__) && !defined (__APPLE__) + db->g_dirp->d_ino == 0 +#else /* __FreeBSD__, __NetBSD__, __OpenBSD__, __DragonFly__, __APPLE__ */ + (db->g_dirp->d_fileno == 0 +#ifdef DT_WHT + || db->g_dirp->d_type == DT_WHT +#endif + ) +#endif /* __FreeBSD__, __NetBSD__, __OpenBSD__, __DragonFly__, __APPLE__ */ + ) + { + next: +#ifndef __DragonFly__ + reclen = db->g_dirp->d_reclen; +#else + reclen = _DIRENT_DIRSIZ(db->g_dirp); +#endif + if ((db->g_num -= reclen) == 0 || reclen == 0) + db->g_dirp = NULL; + else + db->g_dirp = + /*LINTED*/ + (struct dirent *)((char *)db->g_dirp + + reclen); + } + } +#if !defined (__FreeBSD__) && !defined (__NetBSD__) && !defined (__OpenBSD__) \ + && !defined (__DragonFly__) && !defined (__APPLE__) + if (db->g_dirp->d_ino == 0) + goto next; + db->g_dic.d_ino = db->g_dirp->d_ino; +#else /* __FreeBSD__, __NetBSD__, __OpenBSD__, __DragonFly__, __APPLE__ */ + if (db->g_dirp->d_fileno == 0 +#ifdef DT_WHT + || db->g_dirp->d_type == DT_WHT +#endif + ) + { + goto next; + } + db->g_dic.d_ino = db->g_dirp->d_fileno; +#endif /* __FreeBSD__, __NetBSD__, __OpenBSD__, __DragonFly__, __APPLE__ */ + db->g_dic.d_name = db->g_dirp->d_name; +#ifndef __DragonFly__ + reclen = db->g_dirp->d_reclen; +#else + reclen = _DIRENT_DIRSIZ(db->g_dirp); +#endif + if ((db->g_num -= reclen) == 0 || reclen == 0) + db->g_dirp = NULL; + else + /*LINTED*/ + db->g_dirp = (struct dirent *)((char *)db->g_dirp + reclen); + return &(db->g_dic); +} diff --git a/usr.bin/find/getdir.h b/usr.bin/find/getdir.h new file mode 100644 index 0000000..29d107b --- /dev/null +++ b/usr.bin/find/getdir.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2003 Gunnar Ritter + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + */ +/* Sccsid @(#)getdir.h 1.4 (gritter) 10/19/03 */ + +#include + +struct direc { + unsigned long long d_ino; + char *d_name; +}; + +extern struct getdb *getdb_alloc(const char *, int); +extern void getdb_free(struct getdb *); +extern struct direc *getdir(struct getdb *, int *); diff --git a/usr.bin/find/ls.c b/usr.bin/find/ls.c deleted file mode 100644 index 14ce841..0000000 --- a/usr.bin/find/ls.c +++ /dev/null @@ -1,135 +0,0 @@ -/* $NetBSD: ls.c,v 1.21 2011/08/31 16:24:57 plunky Exp $ */ - -/* - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * 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. 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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "find.h" - -/* Derived from the print routines in the ls(1) source code. */ - -static void printlink(char *); -static void printtime(time_t); - -void -printlong(char *name, /* filename to print */ - char *accpath, /* current valid path to filename */ - struct stat *sb) /* stat buffer */ -{ - char modep[15]; - struct passwd *user = getpwuid(sb->st_uid); - struct group *group = getgrgid(sb->st_gid); - char *username, *groupname; - - if (user == NULL) { - username = malloc(21); - snprintf(username, 20, "%d", sb->st_uid); - } else { - username = user->pw_name; - } - if (group == NULL) { - groupname = malloc(21); - snprintf(groupname, 20, "%d", sb->st_gid); - } else { - groupname = group->gr_name; - } - - (void)printf("%7lu %6lld ", (u_long)sb->st_ino, - (long long)sb->st_blocks); - (void)strmode(sb->st_mode, modep); - (void)printf("%s %3lu %-*s %-*s ", modep, (unsigned long)sb->st_nlink, - LOGIN_NAME_MAX, username, LOGIN_NAME_MAX, groupname); - - if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode)) - (void)printf("%3llu,%5llu ", - (unsigned long long)major(sb->st_rdev), - (unsigned long long)minor(sb->st_rdev)); - else - (void)printf("%9lld ", (long long)sb->st_size); - printtime(sb->st_mtime); - (void)printf("%s", name); - if (S_ISLNK(sb->st_mode)) - printlink(accpath); - (void)putchar('\n'); - - if (user == NULL) free(username); - if (group == NULL) free(groupname); -} - -static void -printtime(time_t ftime) -{ - int i; - char *longstring; - - longstring = ctime(&ftime); - for (i = 4; i < 11; ++i) - (void)putchar(longstring[i]); -#define DAYSPERNYEAR 365 -#define SECSPERDAY 24 * 60 * 60 -#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) - if (ftime + SIXMONTHS > time(NULL)) - for (i = 11; i < 16; ++i) - (void)putchar(longstring[i]); - else { - (void)putchar(' '); - for (i = 20; i < 24; ++i) - (void)putchar(longstring[i]); - } - (void)putchar(' '); -} - -static void -printlink(char *name) -{ - int lnklen; - char path[MAXPATHLEN + 1]; - - if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { - warn("%s", name); - return; - } - path[lnklen] = '\0'; - (void)printf(" -> %s", path); -} diff --git a/usr.bin/find/main.c b/usr.bin/find/main.c deleted file mode 100644 index 6ff7e3c..0000000 --- a/usr.bin/find/main.c +++ /dev/null @@ -1,154 +0,0 @@ -/* $NetBSD: main.c,v 1.31 2013/01/24 17:50:08 christos Exp $ */ - -/*- - * Copyright (c) 1990, 1993, 1994 - * 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. 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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "find.h" - -time_t now; /* time find was run */ -int dotfd; /* starting directory */ -int ftsoptions; /* options for the ftsopen(3) call */ -int isdeprecated; /* using deprecated syntax */ -int isdepth; /* do directories on post-order visit */ -int isoutput; /* user specified output operator */ -int issort; /* sort directory entries */ -int isxargs; /* don't permit xargs delimiting chars */ -int regcomp_flags = 0; /* regex compilation flags */ - -static void usage(void); - -int -main(int argc, char *argv[]) -{ - char **p, **start; - int ch; - - (void)time(&now); /* initialize the time-of-day */ - (void)setlocale(LC_ALL, ""); - - /* array to hold dir list. at most (argc - 1) elements. */ - p = start = malloc(argc * sizeof (char *)); - if (p == NULL) - err(1, NULL); - - ftsoptions = FTS_NOSTAT | FTS_PHYSICAL; - while ((ch = getopt(argc, argv, "HLPdEf:hsXx")) != -1) - switch (ch) { - case 'H': - ftsoptions &= ~FTS_LOGICAL; - ftsoptions |= FTS_PHYSICAL|FTS_COMFOLLOW; - break; - case 'L': - ftsoptions &= ~(FTS_COMFOLLOW|FTS_PHYSICAL); - ftsoptions |= FTS_LOGICAL; - break; - case 'P': - ftsoptions &= ~(FTS_COMFOLLOW|FTS_LOGICAL); - ftsoptions |= FTS_PHYSICAL; - break; - case 'd': - isdepth = 1; - break; - case 'E': - regcomp_flags = REG_EXTENDED; - break; - case 'f': - *p++ = optarg; - break; - case 'h': - ftsoptions &= ~FTS_PHYSICAL; - ftsoptions |= FTS_LOGICAL; - break; - case 's': - issort = 1; - break; - case 'X': - isxargs = 1; - break; - case 'x': - ftsoptions |= FTS_XDEV; - break; - case '?': - default: - break; - } - - argc -= optind; - argv += optind; - - /* - * Find first option to delimit the file list. The first argument - * that starts with a -, or is a ! or a ( must be interpreted as a - * part of the find expression, according to POSIX .2. - */ - for (; *argv != NULL; *p++ = *argv++) { - if (argv[0][0] == '-') - break; - if ((argv[0][0] == '!' || argv[0][0] == '(') && - argv[0][1] == '\0') - break; - } - - if (p == start) - usage(); - - *p = NULL; - - if ((dotfd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1) - err(1, "."); - - exit(find_execute(find_formplan(argv), start)); -} - -static void -usage(void) -{ - - (void)fprintf(stderr, "Usage: %s [-H | -L | -P] [-dEhsXx] [-f file] " - "file [file ...] [expression]\n", getprogname()); - exit(1); -} diff --git a/usr.bin/find/misc.c b/usr.bin/find/misc.c deleted file mode 100644 index a6401cb..0000000 --- a/usr.bin/find/misc.c +++ /dev/null @@ -1,142 +0,0 @@ -/* $NetBSD: misc.c,v 1.14 2006/10/11 19:51:10 apb Exp $ */ - -/*- - * Copyright (c) 1990, 1993, 1994 - * 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. 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 -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "find.h" - -/* - * brace_subst -- - * Replace occurrences of {} in orig with path, and place it in a malloced - * area of memory set in store. - */ -void -brace_subst(char *orig, char **store, char *path, int *len) -{ - int nlen, plen, rest; - char ch, *p, *ostore; - - plen = strlen(path); - for (p = *store; (ch = *orig) != '\0'; ++orig) - if (ch == '{' && orig[1] == '}') { - /* Length of string after the {}. */ - rest = strlen(&orig[2]); - - nlen = *len; - while ((p - *store) + plen + rest + 1 > nlen) - nlen *= 2; - - if (nlen > *len) { - ostore = *store; - if ((*store = realloc(ostore, nlen)) == NULL) - err(1, "realloc"); - *len = nlen; - p += *store - ostore; /* Relocate. */ - } - memmove(p, path, 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. - */ -int -queryuser(char **argv) -{ - int ch, first, nl; - - (void)fprintf(stderr, "\"%s", *argv); - while (*++argv) - (void)fprintf(stderr, " %s", *argv); - (void)fprintf(stderr, "\"? "); - (void)fflush(stderr); - - first = ch = getchar(); - for (nl = 0;;) { - if (ch == '\n') { - nl = 1; - break; - } - if (ch == EOF) - break; - ch = getchar(); - } - - if (!nl) { - (void)fprintf(stderr, "\n"); - (void)fflush(stderr); - } - return (first == 'y'); -} - -/* - * show_path -- - * called on SIGINFO - */ -/* ARGSUSED */ -void -show_path(int sig) -{ - extern FTSENT *g_entry; - int errno_bak; - - if (g_entry == NULL) { - /* - * not initialized yet. - * assumption: pointer assignment is atomic. - */ - return; - } - - errno_bak = errno; - write(STDERR_FILENO, "find path: ", 11); - write(STDERR_FILENO, g_entry->fts_path, g_entry->fts_pathlen); - write(STDERR_FILENO, "\n", 1); - errno = errno_bak; -} diff --git a/usr.bin/find/operator.c b/usr.bin/find/operator.c deleted file mode 100644 index e65677b..0000000 --- a/usr.bin/find/operator.c +++ /dev/null @@ -1,263 +0,0 @@ -/* $NetBSD: operator.c,v 1.10 2014/10/18 08:33:30 snj Exp $ */ - -/*- - * Copyright (c) 1990, 1993 - * 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. 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 - -#include -#include -#include - -#include "find.h" - -static PLAN *yanknode(PLAN **); -static PLAN *yankexpr(PLAN **); - -/* - * yanknode -- - * destructively removes the top from the plan - */ -static PLAN * -yanknode(PLAN **planp) /* pointer to top of plan (modified) */ -{ - PLAN *node; /* top node removed from the plan */ - - if ((node = (*planp)) == NULL) - return (NULL); - (*planp) = (*planp)->next; - node->next = NULL; - return (node); -} - -/* - * yankexpr -- - * Removes one expression from the plan. This is used mainly by - * paren_squish. In comments below, an expression is either a - * simple node or a N_EXPR node containing a list of simple nodes. - */ -static PLAN * -yankexpr(PLAN **planp) /* pointer to top of plan (modified) */ -{ - PLAN *next; /* temp node holding subexpression results */ - PLAN *node; /* pointer to returned node or expression */ - PLAN *tail; /* pointer to tail of subplan */ - PLAN *subplan; /* pointer to head of ( ) expression */ - - /* first pull the top node from the plan */ - if ((node = yanknode(planp)) == NULL) - return (NULL); - - /* - * If the node is an '(' then we recursively slurp up expressions - * until we find its associated ')'. If it's a closing paren we - * just return it and unwind our recursion; all other nodes are - * complete expressions, so just return them. - */ - if (node->type == N_OPENPAREN) - for (tail = subplan = NULL;;) { - if ((next = yankexpr(planp)) == NULL) - err(1, "(: missing closing ')'"); - /* - * If we find a closing ')' we store the collected - * subplan in our '(' node and convert the node to - * a N_EXPR. The ')' we found is ignored. Otherwise, - * we just continue to add whatever we get to our - * subplan. - */ - if (next->type == N_CLOSEPAREN) { - if (subplan == NULL) - errx(1, "(): empty inner expression"); - node->p_data[0] = subplan; - node->type = N_EXPR; - node->eval = f_expr; - break; - } else { - if (subplan == NULL) - tail = subplan = next; - else { - tail->next = next; - tail = next; - } - tail->next = NULL; - } - } - return (node); -} - -/* - * paren_squish -- - * replaces "parentheisized" plans in our search plan with "expr" nodes. - */ -PLAN * -paren_squish(PLAN *plan) /* plan with ( ) nodes */ -{ - PLAN *expr; /* pointer to next expression */ - PLAN *tail; /* pointer to tail of result plan */ - PLAN *result; /* pointer to head of result plan */ - - result = tail = NULL; - - /* - * the basic idea is to have yankexpr do all our work and just - * collect its results together. - */ - while ((expr = yankexpr(&plan)) != NULL) { - /* - * if we find an unclaimed ')' it means there is a missing - * '(' someplace. - */ - if (expr->type == N_CLOSEPAREN) - errx(1, "): no beginning '('"); - - /* add the expression to our result plan */ - if (result == NULL) - tail = result = expr; - else { - tail->next = expr; - tail = expr; - } - tail->next = NULL; - } - return (result); -} - -/* - * not_squish -- - * compresses "!" expressions in our search plan. - */ -PLAN * -not_squish(PLAN *plan) /* plan to process */ -{ - PLAN *next; /* next node being processed */ - PLAN *node; /* temporary node used in N_NOT processing */ - PLAN *tail; /* pointer to tail of result plan */ - PLAN *result; /* pointer to head of result plan */ - - tail = result = next = NULL; - - while ((next = yanknode(&plan)) != NULL) { - /* - * if we encounter a ( expression ) then look for nots in - * the expr subplan. - */ - if (next->type == N_EXPR) - next->p_data[0] = not_squish(next->p_data[0]); - - /* - * if we encounter a not, then snag the next node and place - * it in the not's subplan. As an optimization we compress - * several not's to zero or one not. - */ - if (next->type == N_NOT) { - int notlevel = 1; - - node = yanknode(&plan); - while (node != NULL && node->type == N_NOT) { - ++notlevel; - node = yanknode(&plan); - } - if (node == NULL) - errx(1, "!: no following expression"); - if (node->type == N_OR) - errx(1, "!: nothing between ! and -o"); - if (node->type == N_EXPR) - node = not_squish(node); - if (notlevel % 2 != 1) - next = node; - else - next->p_data[0] = node; - } - - /* add the node to our result plan */ - if (result == NULL) - tail = result = next; - else { - tail->next = next; - tail = next; - } - tail->next = NULL; - } - return (result); -} - -/* - * or_squish -- - * compresses -o expressions in our search plan. - */ -PLAN * -or_squish(PLAN *plan) /* plan with ors to be squished */ -{ - PLAN *next; /* next node being processed */ - PLAN *tail; /* pointer to tail of result plan */ - PLAN *result; /* pointer to head of result plan */ - - tail = result = next = NULL; - - while ((next = yanknode(&plan)) != NULL) { - /* - * if we encounter a ( expression ) then look for or's in - * the expr subplan. - */ - if (next->type == N_EXPR) - next->p_data[0] = or_squish(next->p_data[0]); - - /* if we encounter a not then look for not's in the subplan */ - if (next->type == N_NOT) - next->p_data[0] = or_squish(next->p_data[0]); - - /* - * if we encounter an or, then place our collected plan in the - * or's first subplan and then recursively collect the - * remaining stuff into the second subplan and return the or. - */ - if (next->type == N_OR) { - if (result == NULL) - errx(1, "-o: no expression before -o"); - next->p_data[0] = result; - next->p_data[1] = or_squish(plan); - if (next->p_data[1] == NULL) - errx(1, "-o: no expression after -o"); - return (next); - } - - /* add the node to our result plan */ - if (result == NULL) - tail = result = next; - else { - tail->next = next; - tail = next; - } - tail->next = NULL; - } - return (result); -} diff --git a/usr.bin/find/option.c b/usr.bin/find/option.c deleted file mode 100644 index 0834c27..0000000 --- a/usr.bin/find/option.c +++ /dev/null @@ -1,184 +0,0 @@ -/* $NetBSD: option.c,v 1.27 2016/06/13 00:04:40 pgoyette Exp $ */ - -/*- - * Copyright (c) 1990, 1993, 1994 - * 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. 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 -#include - -#include -#include -#include -#include -#include - -#include "find.h" - -int typecompare(const void *, const void *); -static OPTION *option(char *); - -/* NB: the following table must be sorted lexically. */ -static OPTION const options[] = { - { "!", N_NOT, c_not, 0 }, - { "(", N_OPENPAREN, c_openparen, 0 }, - { ")", N_CLOSEPAREN, c_closeparen, 0 }, - { "-a", N_AND, c_null, 0 }, - { "-amin", N_AMIN, c_amin, 1 }, - { "-and", N_AND, c_null, 0 }, - { "-anewer", N_ANEWER, c_anewer, 1 }, - { "-asince", N_ASINCE, c_asince, 1 }, - { "-atime", N_ATIME, c_atime, 1 }, - { "-cmin", N_CMIN, c_cmin, 1 }, - { "-cnewer", N_CNEWER, c_cnewer, 1 }, - { "-csince", N_CSINCE, c_csince, 1 }, - { "-ctime", N_CTIME, c_ctime, 1 }, - { "-delete", N_DELETE, c_delete, 0 }, - { "-depth", N_DEPTH, c_depth, 0 }, - { "-empty", N_EMPTY, c_empty, 0 }, - { "-exec", N_EXEC, c_exec, 1 }, - { "-execdir", N_EXECDIR, c_execdir, 1 }, - { "-exit", N_EXIT, c_exit, 0 }, - { "-false", N_FALSE, c_false, 0 }, - { "-follow", N_FOLLOW, c_follow, 0 }, - { "-fprint", N_FPRINT, c_fprint, 1 }, - { "-fstype", N_FSTYPE, c_fstype, 1 }, - { "-group", N_GROUP, c_group, 1 }, - { "-iname", N_INAME, c_iname, 1 }, - { "-inum", N_INUM, c_inum, 1 }, - { "-iregex", N_IREGEX, c_iregex, 1 }, - { "-links", N_LINKS, c_links, 1 }, - { "-ls", N_LS, c_ls, 0 }, - { "-maxdepth", N_MAXDEPTH, c_maxdepth, 1 }, - { "-mindepth", N_MINDEPTH, c_mindepth, 1 }, - { "-mmin", N_MMIN, c_mmin, 1 }, - { "-mtime", N_MTIME, c_mtime, 1 }, - { "-name", N_NAME, c_name, 1 }, - { "-newer", N_NEWER, c_newer, 1 }, - -/* Aliases for compatability with Gnu findutils */ - { "-neweraa", N_ANEWER, c_anewer, 1 }, - { "-newerat", N_ASINCE, c_asince, 1 }, - { "-newercc", N_CNEWER, c_cnewer, 1 }, - { "-newerct", N_CSINCE, c_csince, 1 }, - { "-newermm", N_NEWER, c_newer, 1 }, - { "-newermt", N_SINCE, c_since, 1 }, - -/* - * Unimplemented Gnu findutils options - * - * If you implement any of these, be sure to re-sort the table - * in ascii(7) order! - * - { "-newerBB", N_UNIMPL, c_unimpl, 1 }, - { "-newerBa", N_UNIMPL, c_unimpl, 1 }, - { "-newerBc", N_UNIMPL, c_unimpl, 1 }, - { "-newerBm", N_UNIMPL, c_unimpl, 1 }, - { "-newerBt", N_UNIMPL, c_unimpl, 1 }, - { "-neweraB", N_UNIMPL, c_unimpl, 1 }, - { "-newerac", N_UNIMPL, c_unimpl, 1 }, - { "-neweram", N_UNIMPL, c_unimpl, 1 }, - { "-newerca", N_UNIMPL, c_unimpl, 1 }, - { "-newercm", N_UNIMPL, c_unimpl, 1 }, - { "-newercB", N_UNIMPL, c_unimpl, 1 }, - { "-newermB", N_UNIMPL, c_unimpl, 1 }, - { "-newerma", N_UNIMPL, c_unimpl, 1 }, - { "-newermc", N_UNIMPL, c_unimpl, 1 }, - * - */ - - { "-nogroup", N_NOGROUP, c_nogroup, 0 }, - { "-nouser", N_NOUSER, c_nouser, 0 }, - { "-o", N_OR, c_or, 0 }, - { "-ok", N_OK, c_exec, 1 }, - { "-or", N_OR, c_or, 0 }, - { "-path", N_PATH, c_path, 1 }, - { "-perm", N_PERM, c_perm, 1 }, - { "-print", N_PRINT, c_print, 0 }, - { "-print0", N_PRINT0, c_print0, 0 }, - { "-printx", N_PRINTX, c_printx, 0 }, - { "-prune", N_PRUNE, c_prune, 0 }, - { "-regex", N_REGEX, c_regex, 1 }, - { "-rm", N_DELETE, c_delete, 0 }, - { "-since", N_SINCE, c_since, 1 }, - { "-size", N_SIZE, c_size, 1 }, - { "-type", N_TYPE, c_type, 1 }, - { "-user", N_USER, c_user, 1 }, - { "-xdev", N_XDEV, c_xdev, 0 } -}; - -/* - * find_create -- - * create a node corresponding to a command line argument. - * - * TODO: - * add create/process function pointers to node, so we can skip - * this switch stuff. - */ -PLAN * -find_create(char ***argvp) -{ - OPTION *p; - PLAN *new; - char **argv; - char *opt; - - argv = *argvp; - opt = *argv; - - if ((p = option(opt)) == NULL) - errx(1, "%s: unknown option", opt); - ++argv; - if (p->arg && !*argv) - errx(1, "%s: requires additional arguments", opt); - - new = (p->create)(&argv, p->token == N_OK, opt); - - *argvp = argv; - return (new); -} - -static OPTION * -option(char *name) -{ - OPTION tmp; - - tmp.name = name; - return ((OPTION *)bsearch(&tmp, options, - sizeof(options)/sizeof(OPTION), sizeof(OPTION), typecompare)); -} - -int -typecompare(const void *a, const void *b) -{ - - return (strcmp(((const OPTION *)a)->name, ((const OPTION *)b)->name)); -} -- cgit v1.2.3-70-g09d2