(root)/
glibc-2.38/
dirent/
scandir-tail-common.c
       1  /* Common implementation for scandir{at}.
       2     Copyright (C) 2018-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library 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 GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <string.h>
      20  #include <errno.h>
      21  
      22  int
      23  SCANDIR_TAIL (DIR *dp,
      24                DIRENT_TYPE ***namelist,
      25                int (*select) (const DIRENT_TYPE *),
      26                int (*cmp) (const DIRENT_TYPE **, const DIRENT_TYPE **))
      27  {
      28    if (dp == NULL)
      29      return -1;
      30  
      31    int save = errno;
      32    __set_errno (0);
      33  
      34    int result;
      35    struct scandir_cancel_struct c = { .dp = dp };
      36    __libc_cleanup_push (&__scandir_cancel_handler, &c);
      37  
      38    DIRENT_TYPE **v = NULL;
      39    size_t vsize = 0;
      40    DIRENT_TYPE *d;
      41    while ((d = READDIR (dp)) != NULL)
      42      {
      43        if (select != NULL)
      44          {
      45            int selected = (*select) (d);
      46  
      47  	  /* The SELECT function might have set errno to non-zero on
      48  	     success.  It was zero before and it needs to be again to
      49  	     make the later tests work.  */
      50  	  __set_errno (0);
      51  
      52            if (!selected)
      53              continue;
      54          }
      55  
      56        if (__glibc_unlikely (c.cnt == vsize))
      57          {
      58            if (vsize == 0)
      59              vsize = 10;
      60            else
      61              vsize *= 2;
      62            DIRENT_TYPE **new = realloc (v, vsize * sizeof *v);
      63            if (new == NULL)
      64              break;
      65            c.v = v = new;
      66          }
      67  
      68        size_t dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
      69        DIRENT_TYPE *vnew = malloc (dsize);
      70        if (vnew == NULL)
      71          break;
      72        v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
      73  
      74        /* Ignore errors from readdir, malloc or realloc.  These functions
      75  	 might have set errno to non-zero on success.  It was zero before
      76  	 and it needs to be again to make the latter tests work.  */
      77        __set_errno (0);
      78      }
      79  
      80    if (__glibc_likely (errno == 0))
      81      {
      82        __closedir (dp);
      83  
      84        /* Sort the list if we have a comparison function to sort with.  */
      85        if (cmp != NULL)
      86  	qsort (v, c.cnt, sizeof *v, (__compar_fn_t) cmp);
      87  
      88        *namelist = v;
      89        result = c.cnt;
      90      }
      91    else
      92      {
      93        /* This frees everything and calls closedir.  */
      94        __scandir_cancel_handler (&c);
      95        result = -1;
      96      }
      97  
      98    __libc_cleanup_pop (0);
      99  
     100    if (result >= 0)
     101      __set_errno (save);
     102    return result;
     103  }