Using the libraries and APIs is better unless you need the flexibility. Don't call an external program unless you need to. On top of the pain of fork and execing, it's also much more expensive and far less efficient.
Also, curl is just a thin wrapper around libcurl anyway.
It's hard to know what would be the proper way exactly without knowing your exact use case. The best option, though, is to either use libcurl and readdir/readdir_r for those two uses, or luacurl and luafilesystem, depending on which part of your program and how integrated you want that functionality to be.
You usually don't want to fork and exec unless you want to be able to invoke external functionality dynamically, such as having the user specify a command to use (like with a shell, or with any system that needs user plugins that should be language-agnostic, can work with program output, and don't need real IPC).
Don't call an external command unless you really need to and it fits your workflow. Doing an external call to something like curl or find is redundant, pointless, and prone to way more bugs. Not to mention that the overhead of managing the forked process as a child and making sure it exits properly and everything that goes with it is way more work than just using libcurl and readdir properly.
Here's the basic way with a fork:
int pipefd[2];
pipe(pipefd);
pid_t parent = getpid();
pid_t pid = fork();
if (pid == -1)
{
// Some error forking
return 1;
} else if (pid > 0)
{
// I am the parent
// Close writing end
close(pipefd[1]);
ssize_t newstart = 0;
char buffer[BUFSIZE];
while (true)
{
// Do the BUFSIZE - newstart - 2 for necessary injected characters, if needed
ssize_t size = read(pipefd[0], buffer + newstart, BUFSIZE - newstart - 2);
// This is done because the buffer was already offset. We want size
// here to be the size of the active buffer, not the size of the read
// characters from the pipe
size += newstart;
if (feof(pipefd[0]))
{
if (size > 0)
{
buffer[size] = '\n';
buffer[size + 1] = '\0';
// This calls dosomethingtofile on each filename split, then if
// there is a trailing one without a newline at the end, it
// moves it to the beginning of the buffer and returns the
// character position immediately after
newstart = splitfilenames(buffer, size);
}
break;
}
if (size > 0)
{
buffer[size] = '\n';
buffer[size + 1] = '\0';
// See previous call
newstart = splitfilenames(buffer, size);
}
}
int status;
do {
waitpid(pid, &status, 0);
} while (!(WIFEXITED(status) || WIFSIGNALED(status)));
} else
{
// I am the child
// Close reading end
close(pipefd[0]);
// Exec the command
execlp("find", "find", "/path/to/dir", (char *)NULL);
_exit(1); // exec never returns
}
Note that you still need to define splitfilenames here, which would call dosomethingtofile on each filename it finds, and if it has a fragment of one last in the buffer, it moves it to the beginning of the buffer and returns its size (or zero if there is no fragment).
And the basic way with readdir:
void recursivelydodir(int fd)
{
DIR *dir = fdopendir(fd);
struct dirent *dirent = NULL;
while ((dirent = readdir(dir)))
{
char *name = dirent->d_name;
size_t length = strlen(name);
// Skip "" . and ..
if (length == 0 || (length == 1 && name[0] == '.') || (length == 2 && name[0] == '.' && name[1] == '.'))
{
continue;
}
struct stat st;
fstatat(fd, name, &st, 0);
if (S_ISREG(st.st_mode))
{
dosomethingtofile(name);
} else if (S_ISDIR(st.st_mode))
{
int newfd = openat(fd, name, O_RDONLY | O_DIRECTORY);
recursivelydodir(newfd);
close(newfd);
}
}
}
void foo()
{
int fd = open("/path/to/dir", O_RDONLY | O_DIRECTORY);
recursivelydodir(fd);
close(fd);
}
Not only is readdir easier, easier to read, more reliable, and faster, it's also more portable, because it (or some compatible version of it) is implemented in most Unixlike OSes, and Windows through Cygwin.
Note that I have not compiled or run either of these samples which I just wrote up on the spot, so they should be treated as pseudo-code at best. There should also be a lot more error-checking and boilerplate stuff, and the fork example should also probably check the exit status.