/**************************************************************************** * libs/libc/dirent/lib_scandir.c * * 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 #include #include #include #include #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. */ 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__ */