OSDN Git Service

Last portion of libc_hidden_proto removal.
[uclinux-h8/uClibc.git] / libc / misc / ftw / ftw.c
1 /* File tree walker functions.
2    Copyright (C) 1996-2003, 2004 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <features.h>
26 #ifdef __UCLIBC__
27 #undef _LIBC
28 #define HAVE_DIRENT_H 1
29 #define HAVE_SYS_PARAM_H 1
30 #define HAVE_DECL_STPCPY 1
31 #define HAVE_MEMPCPY 1
32 #endif
33
34 #if __GNUC__
35 # define alloca __builtin_alloca
36 #else
37 # if HAVE_ALLOCA_H
38 #  include <alloca.h>
39 # else
40 #  ifdef _AIX
41  #  pragma alloca
42 #  else
43 char *alloca ();
44 #  endif
45 # endif
46 #endif
47
48 #if defined _LIBC
49 # include <dirent.h>
50 # define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent)
51 #else
52 # if HAVE_DIRENT_H
53 #  include <dirent.h>
54 #  define NAMLEN(dirent) strlen ((dirent)->d_name)
55 # else
56 #  define dirent direct
57 #  define NAMLEN(dirent) (dirent)->d_namlen
58 #  if HAVE_SYS_NDIR_H
59 #   include <sys/ndir.h>
60 #  endif
61 #  if HAVE_SYS_DIR_H
62 #   include <sys/dir.h>
63 #  endif
64 #  if HAVE_NDIR_H
65 #   include <ndir.h>
66 #  endif
67 # endif
68 #endif
69
70 #include <errno.h>
71 #include <ftw.h>
72 #include <limits.h>
73 #include <search.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <unistd.h>
77 #if HAVE_SYS_PARAM_H || defined _LIBC
78 # include <sys/param.h>
79 #endif
80 #ifdef _LIBC
81 # include <include/sys/stat.h>
82 #else
83 # include <sys/stat.h>
84 #endif
85
86 /* Experimentally off - libc_hidden_proto(memset) */
87 /* Experimentally off - libc_hidden_proto(strchr) */
88 /* Experimentally off - libc_hidden_proto(strlen) */
89 /* libc_hidden_proto(dirfd) */
90 /* libc_hidden_proto(tsearch) */
91 /* libc_hidden_proto(tfind) */
92 /* libc_hidden_proto(tdestroy) */
93 /* libc_hidden_proto(getcwd) */
94 /* libc_hidden_proto(chdir) */
95 /* libc_hidden_proto(fchdir) */
96 /* Experimentally off - libc_hidden_proto(mempcpy) */
97 /* libc_hidden_proto(opendir) */
98 #ifdef __UCLIBC_HAS_LFS__
99 /* libc_hidden_proto(readdir64) */
100 /* libc_hidden_proto(lstat64) */
101 /* libc_hidden_proto(stat64) */
102 #endif
103 /* libc_hidden_proto(closedir) */
104 /* Experimentally off - libc_hidden_proto(stpcpy) */
105 /* libc_hidden_proto(lstat) */
106 /* libc_hidden_proto(stat) */
107
108 #if ! _LIBC && !HAVE_DECL_STPCPY && !defined stpcpy
109 char *stpcpy ();
110 #endif
111
112 #if ! _LIBC && ! defined HAVE_MEMPCPY && ! defined mempcpy
113 /* Be CAREFUL that there are no side effects in N.  */
114 # define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
115 #endif
116
117 /* #define NDEBUG 1 */
118 #include <assert.h>
119
120 #if !defined _LIBC
121 # undef __chdir
122 # define __chdir chdir
123 # undef __closedir
124 # define __closedir closedir
125 # undef __fchdir
126 # define __fchdir fchdir
127 # undef __getcwd
128 # ifndef __UCLIBC__
129 # define __getcwd(P, N) xgetcwd ()
130 extern char *xgetcwd (void);
131 # else
132 # define __getcwd getcwd
133 # endif
134 # undef __mempcpy
135 # define __mempcpy mempcpy
136 # undef __opendir
137 # define __opendir opendir
138 # undef __readdir64
139 # ifndef __UCLIBC_HAS_LFS__
140 # define __readdir64 readdir
141 # else
142 # define __readdir64 readdir64
143 # endif
144 # undef __stpcpy
145 # define __stpcpy stpcpy
146 # undef __tdestroy
147 # define __tdestroy tdestroy
148 # undef __tfind
149 # define __tfind tfind
150 # undef __tsearch
151 # define __tsearch tsearch
152 # undef internal_function
153 # define internal_function /* empty */
154 # ifndef __UCLIBC_HAS_LFS__
155 # undef dirent64
156 # define dirent64 dirent
157 # endif
158 # undef MAX
159 # define MAX(a, b) ((a) > (b) ? (a) : (b))
160 #endif
161
162 /* Arrange to make lstat calls go through the wrapper function
163    on systems with an lstat function that does not dereference symlinks
164    that are specified with a trailing slash.  */
165 #if ! _LIBC && ! LSTAT_FOLLOWS_SLASHED_SYMLINK && !defined __UCLIBC__
166 int rpl_lstat (const char *, struct stat *);
167 # undef lstat
168 # define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
169 #endif
170
171 #ifndef __set_errno
172 # define __set_errno(Val) errno = (Val)
173 #endif
174
175 /* Support for the LFS API version.  */
176 #ifndef FTW_NAME
177 # define FTW_NAME ftw
178 # define NFTW_NAME nftw
179 # define NFTW_OLD_NAME __old_nftw
180 # define NFTW_NEW_NAME __new_nftw
181 # define INO_T ino_t
182 # define STAT stat
183 # ifdef _LIBC
184 #  define LXSTAT __lxstat
185 #  define XSTAT __xstat
186 # else
187 #  define LXSTAT(V,f,sb) lstat (f,sb)
188 #  define XSTAT(V,f,sb) stat (f,sb)
189 # endif
190 # define FTW_FUNC_T __ftw_func_t
191 # define NFTW_FUNC_T __nftw_func_t
192 #endif
193
194 /* We define PATH_MAX if the system does not provide a definition.
195    This does not artificially limit any operation.  PATH_MAX is simply
196    used as a guesstimate for the expected maximal path length.
197    Buffers will be enlarged if necessary.  */
198 #ifndef PATH_MAX
199 # define PATH_MAX 1024
200 #endif
201
202 struct dir_data
203 {
204   DIR *stream;
205   char *content;
206 };
207
208 struct known_object
209 {
210   dev_t dev;
211   INO_T ino;
212 };
213
214 struct ftw_data
215 {
216   /* Array with pointers to open directory streams.  */
217   struct dir_data **dirstreams;
218   size_t actdir;
219   size_t maxdir;
220
221   /* Buffer containing name of currently processed object.  */
222   char *dirbuf;
223   size_t dirbufsize;
224
225   /* Passed as fourth argument to `nftw' callback.  The `base' member
226      tracks the content of the `dirbuf'.  */
227   struct FTW ftw;
228
229   /* Flags passed to `nftw' function.  0 for `ftw'.  */
230   int flags;
231
232   /* Conversion array for flag values.  It is the identity mapping for
233      `nftw' calls, otherwise it maps the values to those known by
234      `ftw'.  */
235   const int *cvt_arr;
236
237   /* Callback function.  We always use the `nftw' form.  */
238   NFTW_FUNC_T func;
239
240   /* Device of starting point.  Needed for FTW_MOUNT.  */
241   dev_t dev;
242
243   /* Data structure for keeping fingerprints of already processed
244      object.  This is needed when not using FTW_PHYS.  */
245   void *known_objects;
246 };
247
248
249 /* Internally we use the FTW_* constants used for `nftw'.  When invoked
250    as `ftw', map each flag to the subset of values used by `ftw'.  */
251 static const int nftw_arr[] =
252 {
253   FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
254 };
255
256 static const int ftw_arr[] =
257 {
258   FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
259 };
260
261
262 /* Forward declarations of local functions.  */
263 static int ftw_dir (struct ftw_data *data, struct STAT *st,
264                     struct dir_data *old_dir) internal_function;
265
266
267 static int
268 object_compare (const void *p1, const void *p2)
269 {
270   /* We don't need a sophisticated and useful comparison.  We are only
271      interested in equality.  However, we must be careful not to
272      accidentally compare `holes' in the structure.  */
273   const struct known_object *kp1 = p1, *kp2 = p2;
274   int cmp1;
275   cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
276   if (cmp1 != 0)
277     return cmp1;
278   return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
279 }
280
281
282 static __inline__ int
283 add_object (struct ftw_data *data, struct STAT *st)
284 {
285   struct known_object *newp = malloc (sizeof (struct known_object));
286   if (newp == NULL)
287     return -1;
288   newp->dev = st->st_dev;
289   newp->ino = st->st_ino;
290   return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
291 }
292
293
294 static __inline__ int
295 find_object (struct ftw_data *data, struct STAT *st)
296 {
297   struct known_object obj;
298   obj.dev = st->st_dev;
299   obj.ino = st->st_ino;
300   return __tfind (&obj, &data->known_objects, object_compare) != NULL;
301 }
302
303
304 static __inline__ int
305 __attribute ((always_inline))
306 open_dir_stream (struct ftw_data *data, struct dir_data *dirp)
307 {
308   int result = 0;
309
310   if (data->dirstreams[data->actdir] != NULL)
311     {
312       /* Oh, oh.  We must close this stream.  Get all remaining
313          entries and store them as a list in the `content' member of
314          the `struct dir_data' variable.  */
315       size_t bufsize = 1024;
316       char *buf = malloc (bufsize);
317
318       if (buf == NULL)
319         result = -1;
320       else
321         {
322           DIR *st = data->dirstreams[data->actdir]->stream;
323           struct dirent64 *d;
324           size_t actsize = 0;
325
326           while ((d = __readdir64 (st)) != NULL)
327             {
328               size_t this_len = NAMLEN (d);
329               if (actsize + this_len + 2 >= bufsize)
330                 {
331                   char *newp;
332                   bufsize += MAX (1024, 2 * this_len);
333                   newp = (char *) realloc (buf, bufsize);
334                   if (newp == NULL)
335                     {
336                       /* No more memory.  */
337                       int save_err = errno;
338                       free (buf);
339                       __set_errno (save_err);
340                       result = -1;
341                       break;
342                     }
343                   buf = newp;
344                 }
345
346               *((char *) __mempcpy (buf + actsize, d->d_name, this_len))
347                 = '\0';
348               actsize += this_len + 1;
349             }
350
351           /* Terminate the list with an additional NUL byte.  */
352           buf[actsize++] = '\0';
353
354           /* Shrink the buffer to what we actually need.  */
355           data->dirstreams[data->actdir]->content = realloc (buf, actsize);
356           if (data->dirstreams[data->actdir]->content == NULL)
357             {
358               int save_err = errno;
359               free (buf);
360               __set_errno (save_err);
361               result = -1;
362             }
363           else
364             {
365               __closedir (st);
366               data->dirstreams[data->actdir]->stream = NULL;
367               data->dirstreams[data->actdir] = NULL;
368             }
369         }
370     }
371
372   /* Open the new stream.  */
373   if (result == 0)
374     {
375       const char *name = ((data->flags & FTW_CHDIR)
376                           ? data->dirbuf + data->ftw.base: data->dirbuf);
377       assert (data->dirstreams[data->actdir] == NULL);
378
379       dirp->stream = __opendir (name);
380       if (dirp->stream == NULL)
381         result = -1;
382       else
383         {
384           dirp->content = NULL;
385           data->dirstreams[data->actdir] = dirp;
386
387           if (++data->actdir == data->maxdir)
388             data->actdir = 0;
389         }
390     }
391
392   return result;
393 }
394
395
396 static int
397 internal_function
398 process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
399                size_t namlen)
400 {
401   struct STAT st;
402   int result = 0;
403   int flag = 0;
404   size_t new_buflen;
405
406   if (name[0] == '.' && (name[1] == '\0'
407                          || (name[1] == '.' && name[2] == '\0')))
408     /* Don't process the "." and ".." entries.  */
409     return 0;
410
411   new_buflen = data->ftw.base + namlen + 2;
412   if (data->dirbufsize < new_buflen)
413     {
414       /* Enlarge the buffer.  */
415       char *newp;
416
417       data->dirbufsize = 2 * new_buflen;
418       newp = (char *) realloc (data->dirbuf, data->dirbufsize);
419       if (newp == NULL)
420         return -1;
421       data->dirbuf = newp;
422     }
423
424   *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
425
426   if ((data->flags & FTW_CHDIR) == 0)
427     name = data->dirbuf;
428
429   if (((data->flags & FTW_PHYS)
430        ? LXSTAT (_STAT_VER, name, &st)
431        : XSTAT (_STAT_VER, name, &st)) < 0)
432     {
433       if (errno != EACCES && errno != ENOENT)
434         result = -1;
435       else if (!(data->flags & FTW_PHYS)
436                && LXSTAT (_STAT_VER, name, &st) == 0
437                && S_ISLNK (st.st_mode))
438         flag = FTW_SLN;
439       else
440         flag = FTW_NS;
441     }
442   else
443     {
444       if (S_ISDIR (st.st_mode))
445         flag = FTW_D;
446       else if (S_ISLNK (st.st_mode))
447         flag = FTW_SL;
448       else
449         flag = FTW_F;
450     }
451
452   if (result == 0
453       && (flag == FTW_NS
454           || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
455     {
456       if (flag == FTW_D)
457         {
458           if ((data->flags & FTW_PHYS)
459               || (!find_object (data, &st)
460                   /* Remember the object.  */
461                   && (result = add_object (data, &st)) == 0))
462             result = ftw_dir (data, &st, dir);
463         }
464       else
465         result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
466                                 &data->ftw);
467     }
468
469   if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
470     result = 0;
471
472   return result;
473 }
474
475
476 static int
477 __attribute ((noinline))
478 internal_function
479 ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir)
480 {
481   struct dir_data dir;
482   struct dirent64 *d;
483   int previous_base = data->ftw.base;
484   int result;
485   char *startp;
486
487   /* Open the stream for this directory.  This might require that
488      another stream has to be closed.  */
489   result = open_dir_stream (data, &dir);
490   if (result != 0)
491     {
492       if (errno == EACCES)
493         /* We cannot read the directory.  Signal this with a special flag.  */
494         result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw);
495
496       return result;
497     }
498
499   /* First, report the directory (if not depth-first).  */
500   if (!(data->flags & FTW_DEPTH))
501     {
502       result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw);
503       if (result != 0)
504         {
505           int save_err;
506 fail:
507           save_err = errno;
508           __closedir (dir.stream);
509           __set_errno (save_err);
510
511           if (data->actdir-- == 0)
512             data->actdir = data->maxdir - 1;
513           data->dirstreams[data->actdir] = NULL;
514           return result;
515         }
516     }
517
518   /* If necessary, change to this directory.  */
519   if (data->flags & FTW_CHDIR)
520     {
521       if (__fchdir (dirfd (dir.stream)) < 0)
522         {
523           result = -1;
524           goto fail;
525         }
526     }
527
528   /* Next, update the `struct FTW' information.  */
529   ++data->ftw.level;
530   startp = strchr (data->dirbuf, '\0');
531   /* There always must be a directory name.  */
532   assert (startp != data->dirbuf);
533   if (startp[-1] != '/')
534     *startp++ = '/';
535   data->ftw.base = startp - data->dirbuf;
536
537   while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL)
538     {
539       result = process_entry (data, &dir, d->d_name, NAMLEN (d));
540       if (result != 0)
541         break;
542     }
543
544   if (dir.stream != NULL)
545     {
546       /* The stream is still open.  I.e., we did not need more
547          descriptors.  Simply close the stream now.  */
548       int save_err = errno;
549
550       assert (dir.content == NULL);
551
552       __closedir (dir.stream);
553       __set_errno (save_err);
554
555       if (data->actdir-- == 0)
556         data->actdir = data->maxdir - 1;
557       data->dirstreams[data->actdir] = NULL;
558     }
559   else
560     {
561       int save_err;
562       char *runp = dir.content;
563
564       while (result == 0 && *runp != '\0')
565         {
566           char *endp = strchr (runp, '\0');
567
568           result = process_entry (data, &dir, runp, endp - runp);
569
570           runp = endp + 1;
571         }
572
573       save_err = errno;
574       free (dir.content);
575       __set_errno (save_err);
576     }
577
578   if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
579     result = 0;
580
581   /* Prepare the return, revert the `struct FTW' information.  */
582   data->dirbuf[data->ftw.base - 1] = '\0';
583   --data->ftw.level;
584   data->ftw.base = previous_base;
585
586   /* Finally, if we process depth-first report the directory.  */
587   if (result == 0 && (data->flags & FTW_DEPTH))
588     result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw);
589
590   if (old_dir
591       && (data->flags & FTW_CHDIR)
592       && (result == 0
593           || ((data->flags & FTW_ACTIONRETVAL)
594               && (result != -1 && result != FTW_STOP))))
595     {
596       /* Change back to the parent directory.  */
597       int done = 0;
598       if (old_dir->stream != NULL)
599         if (__fchdir (dirfd (old_dir->stream)) == 0)
600           done = 1;
601
602       if (!done)
603         {
604           if (data->ftw.base == 1)
605             {
606               if (__chdir ("/") < 0)
607                 result = -1;
608             }
609           else
610             if (__chdir ("..") < 0)
611               result = -1;
612         }
613     }
614
615   return result;
616 }
617
618
619 static int
620 __attribute ((noinline))
621 internal_function
622 ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
623              int flags)
624 {
625   struct ftw_data data;
626   struct STAT st;
627   int result = 0;
628   int save_err;
629   char *cwd = NULL;
630   char *cp;
631
632   /* First make sure the parameters are reasonable.  */
633   if (dir[0] == '\0')
634     {
635       __set_errno (ENOENT);
636       return -1;
637     }
638
639   data.maxdir = descriptors < 1 ? 1 : descriptors;
640   data.actdir = 0;
641   data.dirstreams = (struct dir_data **) alloca (data.maxdir
642                                                  * sizeof (struct dir_data *));
643   memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *));
644
645   /* PATH_MAX is always defined when we get here.  */
646   data.dirbufsize = MAX (2 * strlen (dir), PATH_MAX);
647   data.dirbuf = (char *) malloc (data.dirbufsize);
648   if (data.dirbuf == NULL)
649     return -1;
650   cp = __stpcpy (data.dirbuf, dir);
651   /* Strip trailing slashes.  */
652   while (cp > data.dirbuf + 1 && cp[-1] == '/')
653     --cp;
654   *cp = '\0';
655
656   data.ftw.level = 0;
657
658   /* Find basename.  */
659   while (cp > data.dirbuf && cp[-1] != '/')
660     --cp;
661   data.ftw.base = cp - data.dirbuf;
662
663   data.flags = flags;
664
665   /* This assignment might seem to be strange but it is what we want.
666      The trick is that the first three arguments to the `ftw' and
667      `nftw' callback functions are equal.  Therefore we can call in
668      every case the callback using the format of the `nftw' version
669      and get the correct result since the stack layout for a function
670      call in C allows this.  */
671   data.func = (NFTW_FUNC_T) func;
672
673   /* Since we internally use the complete set of FTW_* values we need
674      to reduce the value range before calling a `ftw' callback.  */
675   data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;
676
677   /* No object known so far.  */
678   data.known_objects = NULL;
679
680   /* Now go to the directory containing the initial file/directory.  */
681   if (flags & FTW_CHDIR)
682     {
683       /* GNU extension ahead.  */
684       cwd =  __getcwd (NULL, 0);
685       if (cwd == NULL)
686         result = -1;
687       else if (data.ftw.base > 0)
688         {
689           /* Change to the directory the file is in.  In data.dirbuf
690              we have a writable copy of the file name.  Just NUL
691              terminate it for now and change the directory.  */
692           if (data.ftw.base == 1)
693             /* I.e., the file is in the root directory.  */
694             result = __chdir ("/");
695           else
696             {
697               char ch = data.dirbuf[data.ftw.base - 1];
698               data.dirbuf[data.ftw.base - 1] = '\0';
699               result = __chdir (data.dirbuf);
700               data.dirbuf[data.ftw.base - 1] = ch;
701             }
702         }
703     }
704
705   /* Get stat info for start directory.  */
706   if (result == 0)
707     {
708       const char *name = ((data.flags & FTW_CHDIR)
709                           ? data.dirbuf + data.ftw.base
710                           : data.dirbuf);
711
712       if (((flags & FTW_PHYS)
713            ? LXSTAT (_STAT_VER, name, &st)
714            : XSTAT (_STAT_VER, name, &st)) < 0)
715         {
716           if (!(flags & FTW_PHYS)
717               && errno == ENOENT
718               && LXSTAT (_STAT_VER, name, &st) == 0
719               && S_ISLNK (st.st_mode))
720             result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN],
721                                    &data.ftw);
722           else
723             /* No need to call the callback since we cannot say anything
724                about the object.  */
725             result = -1;
726         }
727       else
728         {
729           if (S_ISDIR (st.st_mode))
730             {
731               /* Remember the device of the initial directory in case
732                  FTW_MOUNT is given.  */
733               data.dev = st.st_dev;
734
735               /* We know this directory now.  */
736               if (!(flags & FTW_PHYS))
737                 result = add_object (&data, &st);
738
739               if (result == 0)
740                 result = ftw_dir (&data, &st, NULL);
741             }
742           else
743             {
744               int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F;
745
746               result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag],
747                                      &data.ftw);
748             }
749         }
750
751       if ((flags & FTW_ACTIONRETVAL)
752           && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
753         result = 0;
754     }
755
756   /* Return to the start directory (if necessary).  */
757   if (cwd != NULL)
758     {
759       save_err = errno;
760       __chdir (cwd);
761       free (cwd);
762       __set_errno (save_err);
763     }
764
765   /* Free all memory.  */
766   save_err = errno;
767   __tdestroy (data.known_objects, free);
768   free (data.dirbuf);
769   __set_errno (save_err);
770
771   return result;
772 }
773
774
775
776 /* Entry points.  */
777
778 int
779 FTW_NAME (const char *path, FTW_FUNC_T func, int descriptors)
780 {
781   return ftw_startup (path, 0, func, descriptors, 0);
782 }
783
784 #ifndef _LIBC
785 int
786 NFTW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
787 {
788   return ftw_startup (path, 1, func, descriptors, flags);
789 }
790 #else
791
792 #include <shlib-compat.h>
793
794 int NFTW_NEW_NAME (const char *, NFTW_FUNC_T, int, int);
795
796 int
797 NFTW_NEW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
798 {
799   if (flags
800       & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL))
801     {
802       __set_errno (EINVAL);
803       return -1;
804     }
805   return ftw_startup (path, 1, func, descriptors, flags);
806 }
807
808 versioned_symbol (libc, NFTW_NEW_NAME, NFTW_NAME, GLIBC_2_3_3);
809
810 #if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
811
812 /* Older nftw* version just ignored all unknown flags.  */
813
814 int NFTW_OLD_NAME (const char *, NFTW_FUNC_T, int, int);
815
816 int
817 attribute_compat_text_section
818 NFTW_OLD_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
819 {
820   flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH);
821   return ftw_startup (path, 1, func, descriptors, flags);
822 }
823
824 compat_symbol (libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1);
825 #endif
826 #endif