nuttx/libs/libc/dirent/lib_scandir.c
Marco Casaroli f741c9369a libs/libc/dirent: Add a blank line after a declaration.
nxstyle wants a blank line between a declaration and the statements that
follow it.  The line is not new, but it sits within three lines of the
FDPIC change in this series, so CI reads it as part of the patch.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-09-25 10:46:48 -03:00

244 lines
6.6 KiB
C

/****************************************************************************
* libs/libc/dirent/lib_scandir.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#ifdef CONFIG_FDPIC
# include <nuttx/fdpic.h>
#endif
#include "libc.h"
/* The scandir() function is not appropriate for use within the kernel in its
* current form because it uses user space memory allocators and modifies
* the errno value.
*/
#ifndef __KERNEL__
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: scandir
*
* Description:
* The scandir() function scans the directory dirp, calling filter() on
* each directory entry. Entries for which filter() returns nonzero are
* stored in strings allocated via malloc(), sorted using qsort() with
* comparison function compar(), and collected in array namelist which is
* allocated via malloc(). If filter is NULL, all entries are selected.
*
* Input Parameters:
* path - Pathname of the directory to scan
* namelist - An array of pointers to directory entries, which is allocated
* by scandir via malloc. Each directory entry is allocated via
* malloc as well. The caller is responsible to free said
* objects.
* filter - Directory entries for which filter returns zero are not
* included in the namelist. If filter is NULL, all entries are
* included.
* compar - Comparison function used with qsort() to sort the namelist.
*
* Returned Value:
* If successful, the scandir() function returns the number of entries in
* the namelist. Otherwise, it returns -1 and errno is set to indicate the
* error.
*
****************************************************************************/
int scandir(FAR const char *path, FAR struct dirent ***namelist,
CODE int (*filter)(FAR const struct dirent *),
CODE int (*compar)(FAR const struct dirent **,
FAR const struct dirent **))
{
FAR struct dirent *d;
FAR struct dirent *dnew;
FAR struct dirent **list = NULL;
size_t listsize = 0;
size_t cnt = 0;
int errsv;
int result;
FAR DIR *dirp;
/* This scandir implementation relies on errno being set by other service
* functions that it is calling to figure if it was successful. We save
* the original errno value to be able to restore it in case of success.
*/
#ifdef CONFIG_FDPIC
/* Resolve the filter descriptor. compar is not resolved here: qsort()
* does it, and resolving twice reads a code address as a descriptor.
*/
filter = (CODE int (*)(FAR const struct dirent *))
fdpic_callback((FAR void *)filter);
#endif
errsv = get_errno();
dirp = opendir(path);
if (!dirp)
{
return -1;
}
/* opendir might have set errno. Reset to zero. */
set_errno(0);
for (d = readdir(dirp); d != NULL; d = readdir(dirp))
{
size_t dsize;
/* If the caller provided a filter function which tells scandir to skip
* the current directory entry, do so.
*/
if (filter && !filter(d))
{
continue;
}
/* The caller provided filter function might have set errno. Reset to
* zero.
*/
set_errno(0);
/* Grow the directory entry list, if required. */
if (cnt == listsize)
{
struct dirent **newlist;
if (!listsize)
{
listsize = 4;
}
else
{
listsize *= 2;
}
newlist = lib_realloc(list, listsize * sizeof(*list));
if (!newlist)
{
/* realloc failed and set errno. This will tell follow up code
* that we failed.
*/
break;
}
list = newlist;
}
/* Allocate a new directory entry, but restrict its heap size to what
* is really required given the directories' path name.
*/
dsize = (size_t)(&d->d_name[strlen(d->d_name) + 1] - (FAR char *)d);
dnew = lib_malloc(dsize);
if (!dnew)
{
/* malloc failed and set errno. This will tell follow up code that
* we failed.
*/
break;
}
/* Copy directory entry to newly allocated one and update the list
* accordingly.
*/
memcpy(dnew, d, dsize);
list[cnt] = dnew;
cnt++;
/* Some service function might have set errno as a side effect. Reset
* to zero.
*/
set_errno(0);
}
if (get_errno() == 0)
{
/* If the caller provided a comparison function, use it to sort the
* list of directory entries.
*/
if (compar)
{
typedef int (*compar_fn_t)(FAR const void *, FAR const void *);
qsort(list, cnt, sizeof(*list), (compar_fn_t)compar);
}
/* Set the output parameters. */
*namelist = list;
result = (int)cnt;
}
else
{
size_t i;
/* Something failed along the way. Clean up. */
for (i = 0; i < cnt; i++)
{
lib_free(list[i]);
}
lib_free(list);
result = -1;
}
closedir(dirp);
if (result >= 0)
{
/* Restore original errno value in case of success. */
set_errno(errsv);
}
return result;
}
#endif /* __KERNEL__ */