Support nanosecond timestamps and time arguments with fractional parts
[findutils.git] / find / ftsfind.c
blob6212d97eca634e62e42f1a0f4d67672bb43b8898
1 /* find -- search for files in a directory hierarchy (fts version)
2 Copyright (C) 1990, 91, 92, 93, 94, 2000,
3 2003, 2004, 2005, 2006, 2007 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)
8 any later version.
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,
18 USA.*/
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>.
31 #include "defs.h"
34 #define USE_SAFE_CHDIR 1
35 #undef STAT_MOUNTPOINTS
38 #include <errno.h>
39 #include <assert.h>
41 #ifdef HAVE_FCNTL_H
42 #include <fcntl.h>
43 #else
44 #include <sys/file.h>
45 #endif
48 #include "../gnulib/lib/xalloc.h"
49 #include "closeout.h"
50 #include <modetype.h>
51 #include "quotearg.h"
52 #include "quote.h"
53 #include "fts_.h"
55 #ifdef HAVE_LOCALE_H
56 #include <locale.h>
57 #endif
59 #if ENABLE_NLS
60 # include <libintl.h>
61 # define _(Text) gettext (Text)
62 #else
63 # define _(Text) Text
64 #define textdomain(Domain)
65 #define bindtextdomain(Package, Directory)
66 #endif
67 #ifdef gettext_noop
68 # define N_(String) gettext_noop (String)
69 #else
70 /* See locate.c for explanation as to why not use (String) */
71 # define N_(String) String
72 #endif
75 #ifdef STAT_MOUNTPOINTS
76 static void init_mounted_dev_list(void);
77 #endif
79 /* We have encountered an error which should 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
82 * reduce it to 1.
84 static void
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;
95 static char *
96 get_fts_info_name(int info)
98 static char buf[10];
99 switch (info)
101 HANDLECASE(FTS_D);
102 HANDLECASE(FTS_DC);
103 HANDLECASE(FTS_DEFAULT);
104 HANDLECASE(FTS_DNR);
105 HANDLECASE(FTS_DOT);
106 HANDLECASE(FTS_DP);
107 HANDLECASE(FTS_ERR);
108 HANDLECASE(FTS_F);
109 HANDLECASE(FTS_INIT);
110 HANDLECASE(FTS_NS);
111 HANDLECASE(FTS_NSOK);
112 HANDLECASE(FTS_SL);
113 HANDLECASE(FTS_SLNONE);
114 HANDLECASE(FTS_W);
115 default:
116 sprintf(buf, "[%d]", info);
117 return buf;
121 static void
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);
141 static const char*
142 partial_quotearg_n(int n, char *s, size_t len, enum quoting_style style)
144 if (0 == len)
146 return quotearg_n_style(n, style, "");
148 else
150 char saved;
151 const char *result;
153 saved = s[len];
154 s[len] = 0;
155 result = quotearg_n_style(n, style, s);
156 s[len] = saved;
157 return result;
162 /* We've detected a filesystem loop. This is caused by one of
163 * two things:
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
167 * symbolic link.
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.
173 static void
174 issue_loop_warning(FTSENT * ent)
176 if (S_ISLNK(ent->fts_statp->st_mode))
178 error(0, 0,
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));
182 else
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 /a/b/c/d points to /a, not
190 * to /a/b/c.
192 error(0, 0,
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.
209 static boolean
210 symlink_loop(const char *name)
212 struct stat stbuf;
213 int rv;
214 if (following_links())
215 rv = stat(name, &stbuf);
216 else
217 rv = lstat(name, &stbuf);
218 return (0 != rv) && (ELOOP == errno);
222 static void
223 consider_visiting(FTS *p, FTSENT *ent)
225 struct stat statbuf;
226 mode_t mode;
227 int ignore, isdir;
229 if (options.debug_options & DebugSearch)
230 fprintf(stderr,
231 "consider_visiting: fts_info=%-6s, fts_level=%2d, "
232 "fts_path=%s\n",
233 get_fts_info_name(ent->fts_info),
234 (int)ent->fts_level,
235 quotearg_n_style(0, locale_quoting_style, ent->fts_path));
237 /* Cope with various error conditions. */
238 if (ent->fts_info == FTS_ERR
239 || ent->fts_info == FTS_NS
240 || ent->fts_info == FTS_DNR)
242 error(0, ent->fts_errno, ent->fts_path);
243 error_severity(1);
244 return;
246 else if (ent->fts_info == FTS_DC)
248 issue_loop_warning(ent);
249 error_severity(1);
250 return;
252 else if (ent->fts_info == FTS_SLNONE)
254 /* fts_read() claims that ent->fts_accpath is a broken symbolic
255 * link. That would be fine, but if this is part of a symbolic
256 * link loop, we diagnose the problem and also ensure that the
257 * eventual return value is nonzero. Note that while the path
258 * we stat is local (fts_accpath), we print the fill path name
259 * of the file (fts_path) in the error message.
261 if (symlink_loop(ent->fts_accpath))
263 error(0, ELOOP, ent->fts_path);
264 error_severity(1);
265 return;
269 /* Not an error, cope with the usual cases. */
270 if (ent->fts_info == FTS_NSOK)
272 assert(!state.have_stat);
273 assert(!state.have_type);
274 state.type = mode = 0;
276 else
278 state.have_stat = true;
279 state.have_type = true;
280 statbuf = *(ent->fts_statp);
281 state.type = mode = statbuf.st_mode;
284 if (mode)
286 if (!digest_mode(mode, ent->fts_path, ent->fts_name, &statbuf, 0))
287 return;
290 /* examine this item. */
291 ignore = 0;
292 isdir = S_ISDIR(statbuf.st_mode)
293 || (FTS_D == ent->fts_info)
294 || (FTS_DP == ent->fts_info)
295 || (FTS_DC == ent->fts_info);
297 if (isdir && (ent->fts_info == FTS_NSOK))
299 /* This is a directory, but fts did not stat it, so
300 * presumably would not be planning to search its
301 * children. Force a stat of the file so that the
302 * children can be checked.
304 fts_set(p, ent, FTS_AGAIN);
305 return;
308 if (options.maxdepth >= 0)
310 if (ent->fts_level >= options.maxdepth)
312 fts_set(p, ent, FTS_SKIP); /* descend no further */
314 if (ent->fts_level > options.maxdepth)
315 ignore = 1; /* don't even look at this one */
319 if ( (ent->fts_info == FTS_D) && !options.do_dir_first )
321 /* this is the preorder visit, but user said -depth */
322 ignore = 1;
324 else if ( (ent->fts_info == FTS_DP) && options.do_dir_first )
326 /* this is the postorder visit, but user didn't say -depth */
327 ignore = 1;
329 else if (ent->fts_level < options.mindepth)
331 ignore = 1;
334 if (!ignore)
336 visit(p, ent, &statbuf);
340 if (ent->fts_info == FTS_DP)
342 /* we're leaving a directory. */
343 state.stop_at_current_level = false;
344 complete_pending_execdirs(get_eval_tree());
349 static void
350 find(char *arg)
352 char * arglist[2];
353 int ftsoptions;
354 FTS *p;
355 FTSENT *ent;
358 state.starting_path_length = strlen(arg);
360 arglist[0] = arg;
361 arglist[1] = NULL;
363 ftsoptions = FTS_NOSTAT;
365 /* Try to work around Savannah bug #17877 (but actually this change
366 * doesn't fix the bug).
368 ftsoptions |= FTS_TIGHT_CYCLE_CHECK;
370 switch (options.symlink_handling)
372 case SYMLINK_ALWAYS_DEREF:
373 ftsoptions |= FTS_COMFOLLOW|FTS_LOGICAL;
374 break;
376 case SYMLINK_DEREF_ARGSONLY:
377 ftsoptions |= FTS_COMFOLLOW|FTS_PHYSICAL;
378 break;
380 case SYMLINK_NEVER_DEREF:
381 ftsoptions |= FTS_PHYSICAL;
382 break;
385 if (options.stay_on_filesystem)
386 ftsoptions |= FTS_XDEV;
388 p = fts_open(arglist, ftsoptions, NULL);
389 if (NULL == p)
391 error (0, errno,
392 _("cannot search %s"),
393 quotearg_n_style(0, locale_quoting_style, arg));
395 else
397 while ( (ent=fts_read(p)) != NULL )
399 state.have_stat = false;
400 state.have_type = false;
401 state.type = 0;
403 consider_visiting(p, ent);
405 fts_close(p);
406 p = NULL;
411 static void
412 process_all_startpoints(int argc, char *argv[])
414 int i;
416 /* figure out how many start points there are */
417 for (i = 0; i < argc && !looks_like_expression(argv[i], true); i++)
419 state.starting_path_length = strlen(argv[i]); /* TODO: is this redundant? */
420 find(argv[i]);
423 if (i == 0)
426 * We use a temporary variable here because some actions modify
427 * the path temporarily. Hence if we use a string constant,
428 * we get a coredump. The best example of this is if we say
429 * "find -printf %H" (note, not "find . -printf %H").
431 char defaultpath[2] = ".";
432 find(defaultpath);
440 main (int argc, char **argv)
442 int end_of_leading_options = 0; /* First arg after any -H/-L etc. */
443 struct predicate *eval_tree;
445 program_name = argv[0];
446 state.exit_status = 0;
449 /* Set the option defaults before we do the the locale
450 * initialisation as check_nofollow() needs to be executed in the
451 * POSIX locale.
453 set_option_defaults(&options);
455 #ifdef HAVE_SETLOCALE
456 setlocale (LC_ALL, "");
457 #endif
459 bindtextdomain (PACKAGE, LOCALEDIR);
460 textdomain (PACKAGE);
461 atexit (close_stdout);
463 /* Check for -P, -H or -L options. Also -D and -O, which are
464 * both GNU extensions.
466 end_of_leading_options = process_leading_options(argc, argv);
468 if (options.debug_options & DebugStat)
469 options.xstat = debug_stat;
471 #ifdef DEBUG
472 fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start));
473 #endif /* DEBUG */
476 /* We are now processing the part of the "find" command line
477 * after the -H/-L options (if any).
479 eval_tree = build_expression_tree(argc, argv, end_of_leading_options);
481 /* safely_chdir() needs to check that it has ended up in the right place.
482 * To avoid bailing out when something gets automounted, it checks if
483 * the target directory appears to have had a directory mounted on it as
484 * we chdir()ed. The problem with this is that in order to notice that
485 * a filesystem was mounted, we would need to lstat() all the mount points.
486 * That strategy loses if our machine is a client of a dead NFS server.
488 * Hence if safely_chdir() and wd_sanity_check() can manage without needing
489 * to know the mounted device list, we do that.
491 if (!options.open_nofollow_available)
493 #ifdef STAT_MOUNTPOINTS
494 init_mounted_dev_list();
495 #endif
499 starting_desc = open (".", O_RDONLY);
500 if (0 <= starting_desc && fchdir (starting_desc) != 0)
502 close (starting_desc);
503 starting_desc = -1;
505 if (starting_desc < 0)
507 starting_dir = xgetcwd ();
508 if (! starting_dir)
509 error (1, errno, _("cannot get current directory"));
513 process_all_startpoints(argc-end_of_leading_options, argv+end_of_leading_options);
515 /* If "-exec ... {} +" has been used, there may be some
516 * partially-full command lines which have been built,
517 * but which are not yet complete. Execute those now.
519 cleanup();
520 return state.exit_status;
523 boolean
524 is_fts_enabled()
526 /* this version of find (i.e. this main()) uses fts. */
527 return true;