diff options
author | Roland McGrath <roland@hack.frob.com> | 2015-05-13 12:34:11 -0700 |
---|---|---|
committer | Roland McGrath <roland@hack.frob.com> | 2015-05-13 12:34:11 -0700 |
commit | d2ee815ad677bba720c4f0275c1d6065f5809e7a (patch) | |
tree | bdf787aecee9e951a71ce1d852e26b4412743803 /dirent/scandir-tail.c | |
parent | 83c10893259916152d277327fa37d8f70bb4d4c2 (diff) | |
download | glibc-d2ee815ad677bba720c4f0275c1d6065f5809e7a.tar.gz glibc-d2ee815ad677bba720c4f0275c1d6065f5809e7a.tar.xz glibc-d2ee815ad677bba720c4f0275c1d6065f5809e7a.zip |
Refactor scandir/scandirat to use common tail.
Diffstat (limited to 'dirent/scandir-tail.c')
-rw-r--r-- | dirent/scandir-tail.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/dirent/scandir-tail.c b/dirent/scandir-tail.c new file mode 100644 index 0000000000..4560834af5 --- /dev/null +++ b/dirent/scandir-tail.c @@ -0,0 +1,110 @@ +/* Logic guts of scandir*. + Copyright (C) 1992-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <dirent.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <bits/libc-lock.h> + +#ifndef SCANDIR_TAIL +# define SCANDIR_TAIL __scandir_tail +# define READDIR __readdir +# define DIRENT_TYPE struct dirent +#endif + +internal_function +int +SCANDIR_TAIL (DIR *dp, + DIRENT_TYPE ***namelist, + int (*select) (const DIRENT_TYPE *), + int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **)) +{ + if (dp == NULL) + return -1; + + int save = errno; + __set_errno (0); + + int result; + struct scandir_cancel_struct c = { .dp = dp }; + __libc_cleanup_push (&__scandir_cancel_handler, &c); + + DIRENT_TYPE **v = NULL; + size_t vsize = 0; + DIRENT_TYPE *d; + while ((d = READDIR (dp)) != NULL) + { + if (select != NULL) + { + int selected = (*select) (d); + + /* The SELECT function might have changed errno. It was + zero before and it need to be again to make the later + tests work. */ + __set_errno (0); + + if (!selected) + continue; + } + else + __set_errno (0); + + if (__glibc_unlikely (c.cnt == vsize)) + { + if (vsize == 0) + vsize = 10; + else + vsize *= 2; + DIRENT_TYPE **new = realloc (v, vsize * sizeof *v); + if (new == NULL) + break; + c.v = v = new; + } + + size_t dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d; + DIRENT_TYPE *vnew = malloc (dsize); + if (vnew == NULL) + break; + v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize); + } + + if (__glibc_likely (errno == 0)) + { + __closedir (dp); + + /* Sort the list if we have a comparison function to sort with. */ + if (cmp != NULL) + qsort (v, c.cnt, sizeof *v, (__compar_fn_t) cmp); + + *namelist = v; + result = c.cnt; + } + else + { + /* This frees everything and calls closedir. */ + __scandir_cancel_handler (&c); + result = -1; + } + + __libc_cleanup_pop (0); + + if (result >= 0) + __set_errno (save); + return result; +} |