1 /* find -- search for files in a directory hierarchy (fts version)
2 Copyright (C) 1990, 91, 92, 93, 94, 2000,
3 2003, 2004, 2005 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 /* This file was written by James Youngman, based on find.c.
22 GNU find was written by Eric Decker <cire@cisco.com>,
23 with enhancements by David MacKenzie <djm@gnu.org>,
24 Jay Plett <jay@silence.princeton.nj.us>,
25 and Tim Wood <axolotl!tim@toad.com>.
26 The idea for -print0 and xargs -0 came from
27 Dan Bernstein <brnstnd@kramden.acf.nyu.edu>.
34 #define USE_SAFE_CHDIR 1
35 #undef STAT_MOUNTPOINTS
48 #include "../gnulib/lib/xalloc.h"
61 # define _(Text) gettext (Text)
64 #define textdomain(Domain)
65 #define bindtextdomain(Package, Directory)
68 # define N_(String) gettext_noop (String)
70 /* See locate.c for explanation as to why not use (String) */
71 # define N_(String) String
75 #ifdef STAT_MOUNTPOINTS
76 static void init_mounted_dev_list(void);
79 /* We have encountered an error which shoudl affect the exit status.
80 * This is normally used to change the exit status from 0 to 1.
81 * However, if the exit status is already 2 for example, we don't want to
85 error_severity(int level
)
87 if (state
.exit_status
< level
)
88 state
.exit_status
= level
;
92 #define STRINGIFY(X) #X
93 #define HANDLECASE(N) case N: return #N;
96 get_fts_info_name(int info
)
103 HANDLECASE(FTS_DEFAULT
);
109 HANDLECASE(FTS_INIT
);
111 HANDLECASE(FTS_NSOK
);
113 HANDLECASE(FTS_SLNONE
);
116 sprintf(buf
, "[%d]", info
);
122 visit(FTS
*p
, FTSENT
*ent
, struct stat
*pstat
)
124 struct predicate
*eval_tree
;
126 state
.curdepth
= ent
->fts_level
;
127 state
.have_stat
= (ent
->fts_info
!= FTS_NS
) && (ent
->fts_info
!= FTS_NSOK
);
128 state
.rel_pathname
= ent
->fts_accpath
;
130 /* Apply the predicates to this path. */
131 eval_tree
= get_eval_tree();
132 (*(eval_tree
)->pred_func
)(ent
->fts_path
, pstat
, eval_tree
);
134 /* Deal with any side effects of applying the predicates. */
135 if (state
.stop_at_current_level
)
137 fts_set(p
, ent
, FTS_SKIP
);
142 partial_quotearg_n(int n
, char *s
, size_t len
, enum quoting_style style
)
146 return quotearg_n_style(n
, style
, "");
155 result
= quotearg_n_style(n
, style
, s
);
162 /* We've detected a filesystem loop. This is caused by one of
165 * 1. Option -L is in effect and we've hit a symbolic link that
166 * points to an ancestor. This is harmless. We won't traverse the
169 * 2. We have hit a real cycle in the directory hierarchy. In this
170 * case, we issue a diagnostic message (POSIX requires this) and we
171 * skip that directory entry.
174 issue_loop_warning(FTSENT
* ent
)
176 if (S_ISLNK(ent
->fts_statp
->st_mode
))
179 _("Symbolic link %s is part of a loop in the directory hierarchy; we have already visited the directory to which it points."),
180 quotearg_n_style(0, locale_quoting_style
, ent
->fts_path
));
184 /* We have found an infinite loop. POSIX requires us to
185 * issue a diagnostic. Usually we won't get to here
186 * because when the leaf optimisation is on, it will cause
187 * the subdirectory to be skipped. If /a/b/c/d is a hard
188 * link to /a/b, then the link count of /a/b/c is 2,
189 * because the ".." entry of /b/b/c/d points to /a, not
193 _("Filesystem loop detected; "
194 "%s is part of the same filesystem loop as %s."),
195 quotearg_n_style(0, locale_quoting_style
, ent
->fts_path
),
196 partial_quotearg_n(1,
197 ent
->fts_cycle
->fts_path
,
198 ent
->fts_cycle
->fts_pathlen
,
199 locale_quoting_style
));
204 * Return true if NAME corresponds to a file which forms part of a
205 * symbolic link loop. The command
206 * rm -f a b; ln -s a b; ln -s b a
207 * produces such a loop.
210 symlink_loop(const char *name
)
214 if (following_links())
215 rv
= stat(name
, &stbuf
);
217 rv
= lstat(name
, &stbuf
);
218 return (0 != rv
) && (ELOOP
== errno
);
223 consider_visiting(FTS
*p
, FTSENT
*ent
)
229 if (options
.debug_options
& DebugSearch
)
231 "consider_visiting: end->fts_info=%s, ent->fts_path=%s\n",
232 get_fts_info_name(ent
->fts_info
),
233 quotearg_n_style(0, locale_quoting_style
, ent
->fts_path
));
235 /* Cope with various error conditions. */
236 if (ent
->fts_info
== FTS_ERR
237 || ent
->fts_info
== FTS_NS
238 || ent
->fts_info
== FTS_DNR
)
240 error(0, ent
->fts_errno
, ent
->fts_path
);
244 else if (ent
->fts_info
== FTS_DC
)
246 issue_loop_warning(ent
);
250 else if (ent
->fts_info
== FTS_SLNONE
)
252 /* fts_read() claims that ent->fts_accpath is a broken symbolic
253 * link. That would be fine, but if this is part of a symbolic
254 * link loop, we diagnose the problem and also ensure that the
255 * eventual return value is nonzero. Note that while the path
256 * we stat is local (fts_accpath), we print the fill path name
257 * of the file (fts_path) in the error message.
259 if (symlink_loop(ent
->fts_accpath
))
261 error(0, ELOOP
, ent
->fts_path
);
267 /* Not an error, cope with the usual cases. */
268 if (ent
->fts_info
== FTS_NSOK
)
270 assert(!state
.have_stat
);
271 assert(!state
.have_type
);
272 state
.type
= mode
= 0;
276 state
.have_stat
= true;
277 state
.have_type
= true;
278 statbuf
= *(ent
->fts_statp
);
279 state
.type
= mode
= statbuf
.st_mode
;
282 if (0 == ent
->fts_level
&& (0u == state
.starting_path_length
))
283 state
.starting_path_length
= ent
->fts_pathlen
;
287 if (!digest_mode(mode
, ent
->fts_path
, ent
->fts_name
, &statbuf
, 0))
291 /* examine this item. */
293 isdir
= S_ISDIR(statbuf
.st_mode
)
294 || (FTS_D
== ent
->fts_info
)
295 || (FTS_DP
== ent
->fts_info
)
296 || (FTS_DC
== ent
->fts_info
);
298 if (isdir
&& (ent
->fts_info
== FTS_NSOK
))
300 /* This is a directory, but fts did not stat it, so
301 * presumably would not be planning to search its
302 * children. Force a stat of the file so that the
303 * children can be checked.
305 fts_set(p
, ent
, FTS_AGAIN
);
309 if (options
.maxdepth
>= 0 && (ent
->fts_level
> options
.maxdepth
))
312 fts_set(p
, ent
, FTS_SKIP
);
314 else if ( (ent
->fts_info
== FTS_D
) && !options
.do_dir_first
)
316 /* this is the preorder visit, but user said -depth */
319 else if ( (ent
->fts_info
== FTS_DP
) && options
.do_dir_first
)
321 /* this is the postorder visit, but user didn't say -depth */
324 else if (ent
->fts_level
< options
.mindepth
)
331 visit(p
, ent
, &statbuf
);
335 if (ent
->fts_info
== FTS_DP
)
337 /* we're leaving a directory. */
338 state
.stop_at_current_level
= false;
339 complete_pending_execdirs(get_eval_tree());
356 ftsoptions
= FTS_NOSTAT
;
357 switch (options
.symlink_handling
)
359 case SYMLINK_ALWAYS_DEREF
:
360 ftsoptions
|= FTS_COMFOLLOW
|FTS_LOGICAL
;
363 case SYMLINK_DEREF_ARGSONLY
:
364 ftsoptions
|= FTS_COMFOLLOW
;
367 case SYMLINK_NEVER_DEREF
:
368 ftsoptions
|= FTS_PHYSICAL
;
372 if (options
.stay_on_filesystem
)
373 ftsoptions
|= FTS_XDEV
;
375 p
= fts_open(arglist
, ftsoptions
, NULL
);
379 _("cannot search %s"),
380 quotearg_n_style(0, locale_quoting_style
, arg
));
384 while ( (ent
=fts_read(p
)) != NULL
)
386 state
.have_stat
= false;
387 state
.have_type
= false;
390 consider_visiting(p
, ent
);
399 process_all_startpoints(int argc
, char *argv
[])
403 /* figure out how many start points there are */
404 for (i
= 0; i
< argc
&& !looks_like_expression(argv
[i
], true); i
++)
412 * We use a temporary variable here because some actions modify
413 * the path temporarily. Hence if we use a string constant,
414 * we get a coredump. The best example of this is if we say
415 * "find -printf %H" (note, not "find . -printf %H").
417 char defaultpath
[2] = ".";
426 main (int argc
, char **argv
)
428 int end_of_leading_options
= 0; /* First arg after any -H/-L etc. */
429 struct predicate
*eval_tree
;
431 program_name
= argv
[0];
432 state
.exit_status
= 0;
435 /* Set the option defaults before we do the the locale
436 * initialisation as check_nofollow() needs to be executed in the
439 set_option_defaults(&options
);
441 #ifdef HAVE_SETLOCALE
442 setlocale (LC_ALL
, "");
445 bindtextdomain (PACKAGE
, LOCALEDIR
);
446 textdomain (PACKAGE
);
447 atexit (close_stdout
);
449 /* Check for -P, -H or -L options. Also -D and -O, which are
450 * both GNU extensions.
452 end_of_leading_options
= process_leading_options(argc
, argv
);
454 if (options
.debug_options
& DebugStat
)
455 options
.xstat
= debug_stat
;
458 fprintf (stderr
, "cur_day_start = %s", ctime (&options
.cur_day_start
));
462 /* We are now processing the part of the "find" command line
463 * after the -H/-L options (if any).
465 eval_tree
= build_expression_tree(argc
, argv
, end_of_leading_options
);
467 /* safely_chdir() needs to check that it has ended up in the right place.
468 * To avoid bailing out when something gets automounted, it checks if
469 * the target directory appears to have had a directory mounted on it as
470 * we chdir()ed. The problem with this is that in order to notice that
471 * a filesystem was mounted, we would need to lstat() all the mount points.
472 * That strategy loses if our machine is a client of a dead NFS server.
474 * Hence if safely_chdir() and wd_sanity_check() can manage without needing
475 * to know the mounted device list, we do that.
477 if (!options
.open_nofollow_available
)
479 #ifdef STAT_MOUNTPOINTS
480 init_mounted_dev_list();
485 starting_desc
= open (".", O_RDONLY
);
486 if (0 <= starting_desc
&& fchdir (starting_desc
) != 0)
488 close (starting_desc
);
491 if (starting_desc
< 0)
493 starting_dir
= xgetcwd ();
495 error (1, errno
, _("cannot get current directory"));
499 process_all_startpoints(argc
-end_of_leading_options
, argv
+end_of_leading_options
);
501 /* If "-exec ... {} +" has been used, there may be some
502 * partially-full command lines which have been built,
503 * but which are not yet complete. Execute those now.
506 return state
.exit_status
;
509 boolean
is_fts_enabled()
511 /* this version of find (i.e. this main()) uses fts. */