diff options
author | Ulrich Drepper <drepper@redhat.com> | 1997-05-29 12:06:58 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 1997-05-29 12:06:58 +0000 |
commit | 76b87c039ba8d20add4f52ba43f3471fd92e210b (patch) | |
tree | 08c6a1bc32f699056a97f2683bc859cb0d7f6871 /io/ftw.c | |
parent | 06bdbaa0a614c256b34214fde7c395f9e0a6206c (diff) | |
download | glibc-76b87c039ba8d20add4f52ba43f3471fd92e210b.tar.gz |
Update.
1997-05-29 12:48 Ulrich Drepper <drepper@cygnus.com>
* io/ftw.c: Complete rewrite. Add implementation of `nftw'.
* io/ftw.h: Update for new implementation and XPG4.2.
* login/Makefile: Update for UTMP daemon implementation.
Update resolver code to bind-4.9.6-T1A.
* resolv/Banner: Update.
* nss/digits_dots.c: Adapt text address matching to T1A.
* nss/nss_files/files-hosts.c: Always use inet_pton.
* resolv/base64.c (b64_pton): Follow T1A but don't use this code since
it would lead to warnings.
* resolv/gethnamaddr.c (getanswer): Test host name for maximal length
at several places.
* resolv/inet_net_pton.c (inet_net_pton_ipv4): Correct typo in comment.
* resolv/res_comp.c (dn_expand): Check for overflow.
(dn_comp): Likewise.
* resolv/res_debug.c (precsize_aton): Better implementation.
* resolv/res_init.c (res_init): Make `buf' of size MAXDNAME.
* resolv/res_send.c (res_send): Check for overflow in descriptor set.
* resolv/nss_dns/dns-host.c (getanswer_r): Test host name for maximal
length at several places.
1997-05-29 12:51 Mark Kettenis <kettenis@phys.uva.nl>
* login/utmp-private.h (struct utfuncs): Add one more parameter
to updwtmp function.
Declare all three function jump tables.
* login/utmp.h: Declare __utmpname.
* login/getutent_r.c: Remove db backend and provide support for
utmpd backend.
* login/login.c: Use `updwtmp' function insteead of writing the
record ourself.
* login/logwtmp.c: Move `updwtmp' function to...
* login/updwtmp.c: ...here. New file.
* login/utmp_db.h: Removed.
* login/utmp_file.c: Add updwtmp function to write to file.
* login/utmp_daemon.c: New file. Daemon backend.
* login/utmpname.c: New file. Implementation of utmpname function.
* login/utmpdump.c: New file. Tool to dump utmp-like files.
* login/utmpd/connection.c: New file.
* login/utmpd/database.c: New file.
* login/utmpd/error.c: New file.
* login/utmpd/request.c: New file.
* login/utmpd/utmpd-private.h: New file.
* login/utmpd/utmpd.c: New file.
* login/utmpd/utmpd.h: New file.
* login/utmpd/xtmp.c: New file.
* login/utmpd/xtmp.h: New file.
1997-05-29 12:28 Jim Meyering <meyering@eng.ascend.com>
* time/strftime.c: Correct/normalize indentation in cpp directives.
1997-05-28 20:43 Philip Blundell <pjb27@cam.ac.uk>
* nis/nis_error.c: Include <string.h> to fix warning.
* nis/nis_print.c: Likewise.
* nis/nss_nisplus/nisplus-hosts.c: Arg 3 of map_v4v6_hostent
is int* not size_t*.
1997-05-28 21:56 Andreas Jaeger <aj@arthur.rhein-neckar.de>
* math/cmathcalls.h: Correct typo in comment.
* inet/netinet/icmp6.h: Include <netinet/in.h> for in6_addr.
* sysdeps/unix/sysv/linux/netinet/ip_fw.h: Include <net/if.h> for
IFNAMSIZ.
* sysdeps/unix/sysv/linux/net/ppp_defs.h: Include <time.h> for
time_t.
* login/pty.h: Include <ioctl-types.h> for definition of struct
winsize.
* misc/regexp.h (compile): Correct typo.
* argp/argp.h: Put extern before __const in defintion of
argp_program_bug_address.
1997-05-29 00:20 Ulrich Drepper <drepper@cygnus.com>
* sysdeps/wordsize-32/inttypes.h: Correct names of unsigned fast
and least types. Correct names of ?INT_FAST*_{MIN,MAX} macros.
* sysdeps/wordsize-64/inttypes.h: Likewise.
Reported by Andreas Jaeger <aj@arthur.rhein-neckar.de>.
1997-05-28 22:51 Ulrich Drepper <drepper@cygnus.com>
* sysdeps/unix/Makefile (make-ioctls-CFLAGS): Use generic
ttydefaults.h file instead of non-existing version in termios/sys.
Reported by Zack Weinberg <zack@rabi.phys.columbia.edu>.
* time/strptime.c (strptime_internal, case 'Y'): Restrict year
number to four digits and to representable range for 4 byte time_t
values.
Patch by H.J. Lu <hjl@lucon.org>.
1997-05-28 18:19 Philip Blundell <pjb27@cam.ac.uk>
* posix/execl.c: Include <alloca.h> to avoid warning.
1997-05-27 18:19 Andreas Jaeger <aj@arthur.rhein-neckar.de>
* math/libm-test.c: Implement testing of inlined functions, make
output nicer, update comments.
* math/test-idouble.c: New file. Frontend for double tests of
inlined functions.
* math/test-ildoubl.c: New file. Frontend for long double tests of
inlined functions.
* math/test-ifloat.c: New file. Frontend for float tests of
inlined functions.
* math/test-longdouble.c: Rename to...
* math/test-ldouble.c: ...this.
* math/Makefile: Add rules for new test programs, change rules for
renaming of longdouble test.
1997-05-20 15:50 H.J. Lu <hjl@gnu.ai.mit.edu>
* sunrpc/rpc/svc.h (__dispatch_fn_t): New.
(svc_register): Use __dispatch_fn_t in prototype.
1997-05-28 17:02 Ulrich Drepper <drepper@cygnus.com>
* sysdeps/generic/bzero.c (bzero): Fix typo.
Patch by Witek Wnuk <spider@pest.waw.ids.edu.pl>.
1997-05-27 12:00 Andreas Jaeger <aj@arthur.rhein-neckar.de>
* sysdeps/generic/vtimes.c: Use ISO C declaration style.
* sysdeps/unix/bsd/ualarm.c: Include <unistd.h> for prototype.
* sysdeps/generic/memccpy.c: Include <string.h> for prototype.
* signal/tst-signal.c (handler): Correct function declaration to
avoid warning.
* stdlib/testsort.c (compare): Likewise.
* string/tester.c: Likewise.
1997-05-27 14:16 Miles Bader <miles@gnu.ai.mit.edu>
* argp-help.c (argp_args_usage): Supply correct argp to filter_doc.
1997-05-27 17:51 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* db/hash/extern.h, db/hash/hash.c, db/hash/hash.h,
db/hash/hash_log2.c: Rename __log2 to __hash_log2 to avoid clash
with libm.
1997-05-27 14:47 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* sysdeps/m68k/fpu/e_atan2.c: Fix missing negate. Use __m81_test
instead of explicit comparisons.
1997-05-26 18:36 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* inet/netinet/icmp6.h: Remove use of <asm/bitops.h> which has no
place in a generic header and is no user include file.
Diffstat (limited to 'io/ftw.c')
-rw-r--r-- | io/ftw.c | 585 |
1 files changed, 448 insertions, 137 deletions
@@ -1,6 +1,7 @@ -/* Copyright (C) 1992, 1995, 1996, 1997 Free Software Foundation, Inc. +/* File tree walker functions. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. - Contributed by Ian Lance Taylor (ian@airs.com). + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -17,197 +18,507 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <dirent.h> #include <errno.h> -#include <limits.h> +#include <ftw.h> +#include <search.h> #include <stdlib.h> #include <string.h> -#include <dirent.h> -#include <sys/types.h> +#include <unistd.h> +#include <sys/param.h> #include <sys/stat.h> -#include <ftw.h> +/* #define NDEBUG 1 */ +#include <assert.h> -#ifndef PATH_MAX -#define PATH_MAX 1024 /* XXX */ -#endif +struct dir_data +{ + DIR *stream; + char *content; +}; -/* Traverse one level of a directory tree. */ - -static int -ftw_dir (DIR **dirs, int level, int descriptors, char *dir, size_t len, - int (*func) (const char *file, const struct stat *status, int flag)) +struct ftw_data { - int got; - struct dirent *entry; + struct dir_data **dirstreams; + size_t actdir; + size_t maxdir; - got = 0; + char *dirbuf; + size_t dirbufsize; + struct FTW ftw; - __set_errno (0); + int flags; - while ((entry = readdir (dirs[level])) != NULL) - { - struct stat s; - int flag, retval, newlev; - size_t namlen; + int *cvt_arr; + __nftw_func_t func; - ++got; + struct stat st; - if (entry->d_name[0] == '.' - && (entry->d_name[1] == '\0' || - (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) - { - __set_errno (0); - continue; - } + dev_t dev; +}; - namlen = _D_EXACT_NAMLEN (entry); - if (namlen + len + 1 > PATH_MAX) - { -#ifdef ENAMETOOLONG - __set_errno (ENAMETOOLONG); -#else - __set_errno (ENOMEM); -#endif - return -1; - } +/* Internally we use the FTW_* constants used for `nftw'. When the + process called `ftw' we must reduce the flag to the known flags + for `ftw'. */ +static int nftw_arr[] = +{ + FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN +}; - dir[len] = '/'; - memcpy ((void *) (dir + len + 1), (void *) entry->d_name, - namlen + 1); +static int ftw_arr[] = +{ + FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS +}; - if (stat (dir, &s) < 0) - { - if (errno != EACCES && errno != ENOENT) - return -1; - flag = FTW_NS; - } - else if (S_ISDIR (s.st_mode)) + +/* Forward declarations of local functions. */ +static int ftw_dir (struct ftw_data *data); + + +static inline int +open_dir_stream (struct ftw_data *data, struct dir_data *dirp) +{ + int result = 0; + + if (data->dirstreams[data->actdir] != NULL) + { + /* Oh, oh. We must close this stream. Get all remaining + entries and store them as a list in the `content' member of + the `struct dir_data' variable. */ + size_t bufsize = 1024; + char *buf = malloc (bufsize); + + if (buf == NULL) + result = -1; + else { - newlev = (level + 1) % descriptors; + DIR *st = data->dirstreams[data->actdir]->stream; + struct dirent *d; + size_t actsize = 0; + + while ((d = readdir (st)) != NULL) + { + size_t this_len = _D_EXACT_NAMLEN (d); + if (actsize + this_len + 2 >= bufsize) + { + char *newp; + bufsize += MAX (1024, 2 * this_len); + newp = realloc (buf, bufsize); + if (newp == NULL) + { + /* No more memory. */ + int save_err = errno; + free (buf); + __set_errno (save_err); + result = -1; + break; + } + buf = newp; + } + + memcpy (buf + actsize, d->d_name, this_len); + actsize += this_len; + buf[actsize++] = '\0'; + } - if (dirs[newlev] != NULL) - closedir (dirs[newlev]); + /* Terminate the list with an additional NUL byte. */ + buf[actsize++] = '\0'; - dirs[newlev] = opendir (dir); - if (dirs[newlev] != NULL) - flag = FTW_D; + /* Shrink the buffer to what we actually need. */ + data->dirstreams[data->actdir]->content = realloc (buf, actsize); + if (data->dirstreams[data->actdir]->content == NULL) + { + int save_err = errno; + free (buf); + __set_errno (save_err); + result = -1; + } else { - if (errno != EACCES) - return -1; - flag = FTW_DNR; + closedir (st); + data->dirstreams[data->actdir]->stream = NULL; + data->dirstreams[data->actdir] = NULL; } } + } + + /* Open the new stream. */ + if (result == 0) + { + assert (data->dirstreams[data->actdir] == NULL); + + dirp->stream = opendir (data->dirbuf); + if (dirp->stream == NULL) + result = -1; else - flag = FTW_F; + { + dirp->content = NULL; + data->dirstreams[data->actdir] = dirp; + + if (++data->actdir == data->maxdir) + data->actdir = 0; + } + } + + return result; +} + + +static inline int +process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, + size_t namlen) +{ + int result = 0; + int flag; + + if (name[0] == '.' && (name[1] == '\0' + || (name[1] == '.' && name[2] == '\0'))) + /* Don't process the "." and ".." entries. */ + return 0; + + if (data->dirbufsize < data->ftw.base + namlen + 2) + { + /* Enlarge the buffer. */ + char *newp; + + data->dirbufsize *= 2; + newp = realloc (data->dirbuf, data->dirbufsize); + if (newp == NULL) + return -1; + data->dirbuf = newp; + } - retval = (*func) (dir, &s, flag); + memcpy (data->dirbuf + data->ftw.base, name, namlen); + data->dirbuf[data->ftw.base + namlen] = '\0'; + if (((data->flags & FTW_PHYS) ? lstat : stat) (data->dirbuf, &data->st) < 0) + { + if (errno != EACCES && errno != ENOENT) + result = -1; + else if (!(data->flags & FTW_PHYS) + && lstat (data->dirbuf, &data->st) == 0 + && S_ISLNK (data->st.st_mode)) + flag = FTW_SLN; + else + flag = FTW_NS; + } + else + { + if (S_ISDIR (data->st.st_mode)) + flag = FTW_D; + else if (S_ISLNK (data->st.st_mode)) + flag = FTW_SL; + else + flag = FTW_F; + } + + if (result == 0 + && (!(data->flags & FTW_MOUNT) || data->st.st_dev == data->dev)) + { if (flag == FTW_D) { - if (retval == 0) - retval = ftw_dir (dirs, newlev, descriptors, dir, - namlen + len + 1, func); - if (dirs[newlev] != NULL) - { - int save; + result = ftw_dir (data); - save = errno; - closedir (dirs[newlev]); - __set_errno (save); - dirs[newlev] = NULL; + if (result == 0 && (data->flags & FTW_CHDIR)) + { + /* Change back to current directory. */ + int done = 0; + if (dir->stream != NULL) + if (fchdir (dirfd (dir->stream)) == 0) + done = 1; + + if (!done) + { + if (data->ftw.base == 1) + { + if (chdir ("/") < 0) + result = -1; + } + else + { + /* Please note that we overwrite a slash. */ + data->dirbuf[data->ftw.base - 1] = '\0'; + + if (chdir (data->dirbuf) < 0) + result = -1; + else + data->dirbuf[data->ftw.base - 1] = '/'; + } + } } } + else + result = (*data->func) (data->dirbuf, &data->st, data->cvt_arr[flag], + &data->ftw); + } + + return result; +} + + +static int +ftw_dir (struct ftw_data *data) +{ + struct dir_data dir; + struct dirent *d; + int previous_base = data->ftw.base; + int result = 0; + char *startp; + + /* First, report the directory (if not depth-first). */ + if (!(data->flags & FTW_DEPTH)) + { + result = (*data->func) (data->dirbuf, &data->st, FTW_D, &data->ftw); + if (result != 0) + return result; + } - if (retval != 0) - return retval; + /* Open the stream for this directory. This might require that + another stream has to be closed. */ + result = open_dir_stream (data, &dir); + if (result != 0) + return result; - if (dirs[level] == NULL) + /* If necessary, change to this directory. */ + if (data->flags & FTW_CHDIR) + { + if (fchdir (dirfd (dir.stream)) < 0) { - int skip; - - dir[len] = '\0'; - dirs[level] = opendir (dir); - if (dirs[level] == NULL) - return -1; - skip = got; - while (skip-- != 0) + if (errno == ENOSYS) { - __set_errno (0); - if (readdir (dirs[level]) == NULL) - return errno == 0 ? 0 : -1; + if (chdir (data->dirbuf) < 0) + result = -1; } + else + result = -1; } - __set_errno (0); + if (result != 0) + { + int save_err = errno; + closedir (dir.stream); + __set_errno (save_err); + + if (data->actdir-- == 0) + data->actdir = data->maxdir - 1; + data->dirstreams[data->actdir] = NULL; + + return result; + } } - return errno == 0 ? 0 : -1; -} + /* Next, update the `struct FTW' information. */ + ++data->ftw.level; + startp = strchr (data->dirbuf, '\0'); + *startp++ = '/'; + data->ftw.base = startp - data->dirbuf; -/* Call a function on every element in a directory tree. */ + while (dir.stream != NULL && (d = readdir (dir.stream)) != NULL) + { + result = process_entry (data, &dir, d->d_name, _D_EXACT_NAMLEN (d)); + if (result != 0) + break; + } -int -ftw (const char *dir, - int (*func) (const char *file, const struct stat *status, int flag), - int descriptors) -{ - DIR **dirs; - size_t len; - char buf[PATH_MAX + 1]; - struct stat s; - int flag, retval; - int i; - - if (descriptors <= 0) - descriptors = 1; - - dirs = (DIR **) __alloca (descriptors * sizeof (DIR *)); - i = descriptors; - while (i-- > 0) - dirs[i] = NULL; - - if (stat (dir, &s) < 0) + if (dir.stream != NULL) { - if (errno != EACCES && errno != ENOENT) - return -1; - flag = FTW_NS; + /* The stream is still open. I.e., we did not need more + descriptors. Simply close the stream now. */ + int save_err = errno; + + assert (dir.content == NULL); + + closedir (dir.stream); + __set_errno (save_err); + + if (data->actdir-- == 0) + data->actdir = data->maxdir - 1; + data->dirstreams[data->actdir] = NULL; } - else if (S_ISDIR (s.st_mode)) + else { - dirs[0] = opendir (dir); - if (dirs[0] != NULL) - flag = FTW_D; - else + int save_err; + char *runp = dir.content; + + assert (result == 0); + + while (*runp != '\0') { - if (errno != EACCES) - return -1; - flag = FTW_DNR; + char *endp = strchr (runp, '\0'); + + result = process_entry (data, &dir, runp, endp - runp); + if (result != 0) + break; + + runp = endp + 1; } + + save_err = errno; + free (dir.content); + __set_errno (save_err); } - else - flag = FTW_F; - len = strlen (dir); - memcpy ((void *) buf, (void *) dir, len + 1); + /* Prepare the return, revert the `struct FTW' information. */ + --data->ftw.level; + data->ftw.base = previous_base; + + /* Finally, if we process depth-first report the directory. */ + if (result == 0 && (data->flags & FTW_DEPTH)) + result = (*data->func) (data->dirbuf, &data->st, FTW_DP, &data->ftw); - retval = (*func) (buf, &s, flag); + return result; +} + + +static int +ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, + int flags) +{ + struct ftw_data data; + int result = 0; + int save_err; + char *cwd; + char *cp; + + /* First make sure the parameters are reasonable. */ + if (dir[0] == '\0') + { + __set_errno (ENOTDIR); + return -1; + } - if (flag == FTW_D) + data.maxdir = descriptors < 1 ? 1 : descriptors; + data.actdir = 0; + data.dirstreams = (struct dir_data **) alloca (data.maxdir + * sizeof (struct dir_data *)); + memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *)); + + data.dirbufsize = MAX (2 * strlen (dir), PATH_MAX); + data.dirbuf = (char *) malloc (data.dirbufsize); + if (data.dirbuf == NULL) + return -1; + cp = stpcpy (data.dirbuf, dir); + /* Strip trailing slashes. */ + while (cp > data.dirbuf + 1 && cp[-1] == '/') + --cp; + *cp = '\0'; + + data.ftw.level = 0; + + /* Find basename. */ + while (cp > data.dirbuf && cp[-1] != '/') + --cp; + data.ftw.base = cp - data.dirbuf; + + data.flags = flags; + + /* This assignment might seem to be strange but it is what we want. + The trick is that the first three arguments to the `ftw' and + `nftw' callback functions are equal. Therefore we can call in + every case the callback using the format of the `nftw' version + and get the correct result since the stack layout for a function + call in C allows this. */ + data.func = (__nftw_func_t) func; + + /* Since we internally use the complete set of FTW_* values we need + to reduce the value range before calling a `ftw' callback. */ + data.cvt_arr = is_nftw ? nftw_arr : ftw_arr; + + /* Now go to the directory containing the initial file/directory. */ + if ((flags & FTW_CHDIR) && data.ftw.base > 0) { - if (retval == 0) - retval = ftw_dir (dirs, 0, descriptors, buf, len, func); - if (dirs[0] != NULL) + /* GNU extension ahead. */ + cwd = getcwd (NULL, 0); + if (cwd == NULL) + result = -1; + else { - int save; - - save = errno; - closedir (dirs[0]); - __set_errno (save); + /* Change to the directory the file is in. In data.dirbuf + we have a writable copy of the file name. Just NUL + terminate it for now and change the directory. */ + if (data.ftw.base == 1) + /* I.e., the file is in the root directory. */ + result = chdir ("/"); + else + { + char ch = data.dirbuf[data.ftw.base - 1]; + data.dirbuf[data.ftw.base - 1] = '\0'; + result = chdir (data.dirbuf); + data.dirbuf[data.ftw.base - 1] = ch; + } } } - return retval; + /* Get stat info for start directory. */ + if (result == 0) + if (((flags & FTW_PHYS) ? lstat : stat) (data.dirbuf, &data.st) < 0) + { + if (errno == EACCES) + result = (*data.func) (data.dirbuf, &data.st, FTW_NS, &data.ftw); + else if (!(flags & FTW_PHYS) + && errno == ENOENT + && lstat (dir, &data.st) == 0 && S_ISLNK (data.st.st_mode)) + result = (*data.func) (data.dirbuf, &data.st, data.cvt_arr[FTW_SLN], + &data.ftw); + else + /* No need to call the callback since we cannot say anything + about the object. */ + result = -1; + } + else + { + if (S_ISDIR (data.st.st_mode)) + { + data.dev = data.st.st_dev; + result = ftw_dir (&data); + } + else + { + int flag = S_ISLNK (data.st.st_mode) ? FTW_SL : FTW_F; + + result = (*data.func) (data.dirbuf, &data.st, data.cvt_arr[flag], + &data.ftw); + } + } + + /* Return to the start directory (if necessary). */ + if (cwd != NULL) + { + int save_err = errno; + chdir (cwd); + free (cwd); + __set_errno (save_err); + } + + /* Free all memory. */ + save_err = errno; + free (data.dirbuf); + __set_errno (save_err); + + return result; +} + + + +/* Entry points. */ + +int +ftw (path, func, descriptors) + const char *path; + __ftw_func_t func; + int descriptors; +{ + return ftw_startup (path, 0, func, descriptors, 0); +} + +int +nftw (path, func, descriptors, flags) + const char *path; + __nftw_func_t func; + int descriptors; + int flags; +{ + return ftw_startup (path, 1, func, descriptors, flags); } |