/* Copyright (C) 1992-1998, 2000, 2002-2003, 2009-2023 Free Software Foundation, Inc. This file is part of the GNU C Library. This file 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 3 of the License, or (at your option) any later version. This file 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 this program. If not, see . */ #include #include #include #include #include #if _LIBC # include #endif #undef select #ifndef _D_EXACT_NAMLEN # define _D_EXACT_NAMLEN(d) strlen ((d)->d_name) #endif #ifndef _D_ALLOC_NAMLEN # ifndef __KLIBC__ # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1) # else /* On OS/2 kLIBC, d_name is not the last field of struct dirent. See . */ # include # define _D_ALLOC_NAMLEN(d) (sizeof (struct dirent) - \ offsetof (struct dirent, d_name)) # endif #endif #if _LIBC # ifndef SCANDIR # define SCANDIR scandir # define READDIR __readdir # define DIRENT_TYPE struct dirent # endif #else # define SCANDIR scandir # define READDIR readdir # define DIRENT_TYPE struct dirent # define __opendir opendir # define __closedir closedir # define __set_errno(val) errno = (val) /* The results of opendir() in this file are not used with dirfd and fchdir, and we do not leak fds to any single-threaded code that could use stdio, therefore save some unnecessary recursion in fchdir.c and opendir_safer.c. FIXME - if the kernel ever adds support for multi-thread safety for avoiding standard fds, then we should use opendir_safer. */ # undef opendir # undef closedir #endif #ifndef SCANDIR_CANCEL # define SCANDIR_CANCEL struct scandir_cancel_struct { DIR *dp; void *v; size_t cnt; }; # if _LIBC static void cancel_handler (void *arg) { struct scandir_cancel_struct *cp = arg; size_t i; void **v = cp->v; for (i = 0; i < cp->cnt; ++i) free (v[i]); free (v); (void) __closedir (cp->dp); } # endif #endif int #ifndef __KLIBC__ SCANDIR (const char *dir, DIRENT_TYPE ***namelist, int (*select) (const DIRENT_TYPE *), int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **)) #else /* On OS/2 kLIBC, scandir() declaration is different from POSIX. See . */ SCANDIR (const char *dir, DIRENT_TYPE ***namelist, int (*select) (DIRENT_TYPE *), int (*cmp) (const void *, const void *)) #endif { DIR *dp = __opendir (dir); DIRENT_TYPE **v = NULL; size_t vsize = 0; struct scandir_cancel_struct c; DIRENT_TYPE *d; int save; if (dp == NULL) return -1; save = errno; __set_errno (0); c.dp = dp; c.v = NULL; c.cnt = 0; #if _LIBC __libc_cleanup_push (cancel_handler, &c); #endif while ((d = READDIR (dp)) != NULL) { int use_it = select == NULL; if (! use_it) { use_it = select (d); /* The select function might have changed errno. It was zero before and it need to be again to make the latter tests work. */ __set_errno (0); } if (use_it) { DIRENT_TYPE *vnew; size_t dsize; /* Ignore errors from select or readdir */ __set_errno (0); if (__builtin_expect (c.cnt == vsize, 0)) { DIRENT_TYPE **new; if (vsize == 0) vsize = 10; else vsize *= 2; new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v)); if (new == NULL) break; v = new; c.v = (void *) v; } dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d; vnew = (DIRENT_TYPE *) malloc (dsize); if (vnew == NULL) break; v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize); } } if (__builtin_expect (errno, 0) != 0) { save = errno; while (c.cnt > 0) free (v[--c.cnt]); free (v); c.cnt = -1; } else { /* Sort the list if we have a comparison function to sort with. */ if (cmp != NULL) qsort (v, c.cnt, sizeof (*v), (int (*) (const void *, const void *)) cmp); *namelist = v; } #if _LIBC __libc_cleanup_pop (0); #endif (void) __closedir (dp); __set_errno (save); return c.cnt; }