diff --git a/include/dirent.h b/include/dirent.h index 064dc8f952..5f5f9987c1 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -50,9 +50,9 @@ * Pre-processor Definitions ****************************************************************************/ -/* File type code for the d_type field in dirent struct. - * Note that because of the simplified filesystem organization - * of NuttX, an inode can be BOTH a file and a directory +/* File type code for the d_type field in dirent structure. + * Note that because of the simplified filesystem organization of the NuttX, + * top-level, pseudo-file system, an inode can be BOTH a file and a directory */ #define DTYPE_UNKNOWN 0 /* The file type could not be determined */ @@ -61,6 +61,8 @@ #define DTYPE_BLK (1 << 2) /* Bit 2: Block device */ #define DTYPE_DIRECTORY (1 << 3) /* Bit 3: Directory */ #define DTYPE_LINK (1 << 4) /* Bit 4: Symbolic link */ +#define DTYPE_FIFO (1 << 5) /* Bit 5: Named Pipe (FIFO) */ +#define DTYPE_SOCK (1 << 6) /* Bit 6: UNIX domain socket */ #define DIRENT_ISFILE(dtype) (((dtype) & DTYPE_FILE) != 0) #define DIRENT_ISCHR(dtype) (((dtype) & DTYPE_CHR) != 0) @@ -68,6 +70,22 @@ #define DIRENT_ISDIRECTORY(dtype) (((dtype) & DTYPE_DIRECTORY) != 0) #define DIRENT_ISLINK(dtype) (((dtype) & DTYPE_LINK) != 0) +/* The d_type field of the dirent structure is not specified by POSIX. It + * is a non-standard, 4.5BSD extension that is implemented by most OSs. A + * POSIX compliant OS may not implement the d_type field at all. Many OS's + * (including glibc) may use the following alternative naming for the file + * type names: + */ + +#define DT_UNKNOWN DTYPE_UNKNOWN +#define DT_FIFO DTYPE_FIFO +#define DT_CHR DTYPE_CHR +#define DT_DIR DTYPE_DIRECTORY +#define DT_BLK DTYPE_BLK +#define DT_REG DTYPE_FILE +#define DT_LNK DTYPE_LINK +#define DT_SOCK DTYPE_SOCK + /**************************************************************************** * Public Type Definitions ****************************************************************************/ @@ -75,12 +93,15 @@ /* The POSIX specification requires that the caller of readdir_r provide * storage "large enough for a dirent with the d_name member and an array * of char containing at least {NAME_MAX} plus one elements. + * + * POSIX also requires the field d_ino (type ino_t) that provides the file + * serial number. This functionality is not implemented in NuttX. */ struct dirent { - uint8_t d_type; /* type of file */ - char d_name[NAME_MAX+1]; /* filename */ + uint8_t d_type; /* Type of file */ + char d_name[NAME_MAX + 1]; /* File name */ }; typedef void DIR; @@ -112,6 +133,12 @@ int readdir_r(FAR DIR *dirp, FAR struct dirent *entry, void rewinddir(FAR DIR *dirp); void seekdir(FAR DIR *dirp, off_t loc); off_t telldir(FAR DIR *dirp); +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 **)); +int alphasort(FAR const struct dirent **a, + FAR const struct dirent **b); #undef EXTERN #if defined(__cplusplus) diff --git a/libs/libc/dirent/Make.defs b/libs/libc/dirent/Make.defs index 96c09d4203..638781f5e3 100644 --- a/libs/libc/dirent/Make.defs +++ b/libs/libc/dirent/Make.defs @@ -35,7 +35,7 @@ # Add the dirent C files to the build -CSRCS += lib_readdirr.c lib_telldir.c +CSRCS += lib_readdirr.c lib_telldir.c lib_alphasort.c lib_scandir.c # Add the dirent directory to the build diff --git a/libs/libc/dirent/lib_alphasort.c b/libs/libc/dirent/lib_alphasort.c new file mode 100644 index 0000000000..cf4c0afe14 --- /dev/null +++ b/libs/libc/dirent/lib_alphasort.c @@ -0,0 +1,75 @@ +/**************************************************************************** + * libs/libc/dirent/lib_alphasort.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Michael Jung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: alphasort + * + * Description: + * The alphasort() function can be used as the comparison function compar() + * for scandir(). It sorts directory entries using strcoll on the strings + * (*a)->d_name and (*b)->d_name. + * + * Input Parameters: + * a - The first direntry to compare + * b - The second direntry to compare + * + * Returned Value: + * An integer less than, equal to, or greater than zero if the first + * argument is considered to be respectively less than, equal to, or greater + * than the second. + * + ****************************************************************************/ + +int alphasort(FAR const struct dirent **a, FAR const struct dirent **b) +{ +#ifdef CONFIG_LIBC_LOCALE + return strcoll((*a)->d_name, (*b)->d_name); +#else + return strcmp((*a)->d_name, (*b)->d_name); +#endif +} diff --git a/libs/libc/dirent/lib_scandir.c b/libs/libc/dirent/lib_scandir.c new file mode 100644 index 0000000000..2b64b4c5ae --- /dev/null +++ b/libs/libc/dirent/lib_scandir.c @@ -0,0 +1,232 @@ +/**************************************************************************** + * libs/libc/dirent/lib_scandir.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Michael Jung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +/**************************************************************************** + * 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 **)) +{ + struct dirent *d; + struct dirent *dnew; + struct dirent **list = NULL; + size_t listsize = 0; + size_t cnt = 0; + int errsv; + int result; + 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. + */ + + 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 = 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] - (char *)d); + + dnew = 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) + { + qsort(list, cnt, sizeof(*list), + (CODE int (*)(FAR const void *, FAR const void *))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++) + { + free(list[i]); + } + + free(list); + + result = -1; + } + + closedir(dirp); + + if (result >= 0) + { + /* Restore original errno value in case of success. */ + + set_errno(errsv); + } + + return result; +}