SCANDIR(3)

๐Ÿ“ฆ
version 15.1 ยท 2 versions
, in freebsd 15.1

โœจ SCANDIR(3) โœจ

okay so bestie, we're in section 3 of the Library Functions Manual today, which means we're hanging out in the Standard C Library (libc, -lc) and we're gonna talk about scanning directories. yes. that thing. it's giving "the most useful thing you'll ever do with a directory" and it lowkey slays ๐Ÿ’…


๐ŸŽ€ NAME ๐ŸŽ€

scandir, fdscandir, scandirat, scandir_b, fdscandir_b, fdscandirat_b, alphasort, versionsort โ€” scan a directory ๐Ÿ’–

(and yes, fdscandirat_b really is in the NAME list even though the rest of the page only ever talks about scandirat_b, iykyk โ€” I'm not the one who wrote the header, I'm just the one telling you about it ๐Ÿซถ)


๐ŸŒธ LIBRARY ๐ŸŒธ

Standard C Library (libc, -lc)


๐Ÿฆ‹ SYNOPSIS ๐Ÿฆ‹

okay bestie, get your #include ready:

#include <dirent.h>

then, the icons themselves:

int
scandir(const char *dirname, struct dirent ***namelist,
    int (*select)(const struct dirent *),
    int (*compar)(const struct dirent **, const struct dirent **));

int
fdscandir(int dirfd, struct dirent ***namelist,
    int (*select)(const struct dirent *),
    int (*compar)(const struct dirent **, const struct dirent **));

int
scandirat(int dirfd, const char *dirname, struct dirent ***namelist,
    int (*select)(const struct dirent *),
    int (*compar)(const struct dirent **, const struct dirent **));

int
scandir_b(const char *dirname, struct dirent ***namelist,
    int (^select)(const struct dirent *),
    int (^compar)(const struct dirent **, const struct dirent **));

int
fdscandir_b(int dirfd, struct dirent ***namelist,
    int (^select)(const struct dirent *),
    int (^compar)(const struct dirent **, const struct dirent **));

int
scandirat_b(int dirfd, const char *dirname, struct dirent ***namelist,
    int (^select)(const struct dirent *),
    int (^compar)(const struct dirent **, const struct dirent **));

int
alphasort(const struct dirent **d1, const struct dirent **d2);

int
versionsort(const struct dirent **d1, const struct dirent **d2);

notice the ^ blocks on the _b ones? that's the whole vibe. block energy ๐Ÿ”ฅ


๐Ÿ’– DESCRIPTION ๐Ÿ’–

the main character: scandir() ๐Ÿ‘‘

So scandir() reads the directory dirname and builds up an array of pointers to directory entries, using malloc(3). it hands you back the number of entries in the array. and the pointer to that array of directory entries? it gets stored in the location referenced by namelist โ€” even if no entries were selected. yes, even then. it's still there for you, bestie ๐Ÿซถ

select โ€” the vibe check ๐Ÿงš

The select argument is a pointer to a user-supplied subroutine that scandir() calls to decide which entries make the cut for the array. select gets handed a pointer to a directory entry, and it should return a non-zero value if that entry should be included. if select is null? all the entries get in. everyone's invited, no bouncer ๐ŸŒˆ

compar โ€” the sorting agenda ๐Ÿ’…

The compar argument is a pointer to a user-supplied subroutine that gets passed to qsort(3) to sort the finished array. if this pointer is null, the array is just not sorted. chaos. we love her ๐ŸŒธ

the two ready-made comparators โญ

  • alphasort() โ€” a routine you can totally use as the compar argument. it sorts the array alphabetically using strcoll(3).
  • versionsort() โ€” a routine you can also use as the compar argument. it sorts the array naturally using strverscmp(3). so file2 actually comes before file10, the way nature intended ๐Ÿ˜ญ

cleanup time ๐Ÿงน

the memory allocated for the array can be deallocated with free(3) โ€” you free each pointer in the array first, and then the array itself. order matters, bestie. don't skip leg day, don't skip the inner frees.

the fd variants ๐Ÿฆ‹

fdscandir() is basically scandir() but it takes a file descriptor that references a directory instead of a path. one thing though, no cap: the file descriptor is left open on return, regardless of outcome. even if things went sideways, it's still open. so that's on you to handle ๐Ÿ‘€

scandirat() is scandir()'s sibling with an extra dirfd argument. here's the breakdown:

  • if the dirname you supplied is absolute, it behaves identically to scandir() and the dirfd argument is just... unused. sitting there. vibing. ๐Ÿ™ˆ
  • if dirname is relative, then dirfd has to be a valid file descriptor referencing a directory, and the dirname lookup happens relative to the directory that dirfd points at.
  • if dirfd has the special value AT_FDCWD, then the current process directory is used as the base for relative lookups.
  • and for all the extra details, go see openat(2). ๐Ÿ“š

the _b girlies ๐ŸŽ€

scandir_b(), fdscandir_b(), and scandirat_b() behave exactly the same way as scandir(), fdscandir(), and scandirat() respectively โ€” except they take blocks as arguments instead of function pointers, and they call qsort_b() rather than qsort(). that's the entire difference. slay. ๐Ÿ”ฅ


๐Ÿ‘‘ DIAGNOSTICS ๐Ÿ‘‘

so scandir(), fdscandir(), scandirat(), scandir_b(), fdscandir_b(), and scandirat_b() all return the number of directory entries found on success. ๐Ÿ’–

but if:

  • the directory can't be opened for reading, or
  • an error happens while reading the directory, or
  • malloc(3) can't allocate enough memory to hold all the directory entries,

then they return -1 and set errno to an appropriate value. ๐Ÿ˜ญ it's giving "it's not me, it's errno".


๐Ÿ’€ ERRORS ๐Ÿ’€

(spot the difference: opendir(3) vs fdopendir(3). that's it. that's the whole switch-up ๐Ÿซถ)


๐ŸŒˆ SEE ALSO ๐ŸŒˆ

openat(2), directory(3), malloc(3), qsort(3), strcoll(3), strverscmp(3), dir(5)

go read them. or don't. but like, they're right there ๐Ÿ’œ


๐Ÿ“ STANDARDS ๐Ÿ“

  • alphasort() and scandir() are expected to conform to IEEE Std 1003.1-2008 (โ€œPOSIX.1โ€). iconic, certified, periodt โœ…
  • scandirat() and versionsort() are GNU extensions and conform to no standard. they're doing their own thing, no rules, main character energy ๐Ÿ’…
  • fdscandir(), scandir_b(), fdscandir_b(), and scandirat_b() are FreeBSD extensions. ๐Ÿฆ‹

๐Ÿงš HISTORY ๐Ÿงš

  • scandir() and alphasort() appeared in 4.2BSD. the elders. the originals. ๐Ÿ‘‘
  • scandir_b() was added in FreeBSD 11.0.
  • scandirat() and versionsort() were added in FreeBSD 13.2.
  • fdscandir(), fdscandir_b(), and scandirat_b() were added in FreeBSD 15.0. the babies of the family ๐Ÿ“

okay that's the whole page bestie, go forth and scandir() responsibly ๐Ÿ’–โœจ๐ŸŽ€

this edition came to you from Debian, dated June 25, 2025, and that's the SCANDIR(3) sign-off โ€” mwah ๐Ÿ’‹

๐Ÿ“ฆ where this page comes from
  • freebsd 15.1: base 15.1
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms