File Tree Walk

2025-03-04, Tue

Nontrivial program that processes a bunch of files typically involves traversal of a directory recursively. (Un)Surprisingly, there is the ftw set of library functions dedicated to this job.

Since ftw(3) is deprecated by nftw(3) according to the standard1. It's the later one that will be talked about here. A simple example that prints out all files recursively in a directory is listed below:

#include <ftw.h> /* for nftw */
#include <stdio.h> /* for stdout, stderr and fprintf */
#include <stdlib.h> /* for exit(3) */
#include <errno.h> /* for errno */
#include <string.h> /* for strerror */

int
proc_fun(const char *fpath, const struct stat *sb,
         int typeflag, struct FTW *ftwbuf)
{
  fprintf(stdout, "%s\n", fpath);
  return 0;
}

int
main(int argc, char *argv[])
{
  if (argc != 2) {
    fprintf(stdout, "usage: %s <directory>\n", argv[0]);
    exit(0);
  }

  if (nftw(argv[1], proc_fun, 1, 0)) {
    fprintf(stderr, "unable to handle \"%s\": %s\n",
            argv[1], strerror(errno));
    exit(errno);
  }

  return 0;
}

A while ago I wrote a script in JavaScript to do the similar task. With the API provided by Node.js, the snippet looks like this:

import * as fs from 'node:fs/promises';
import path from 'nod:path';

async function walkDirectory(dir, procFun = console.log) {
    const files = await fs.readdir(dir);
    for(const name of files) {
        const filePath = path.resolve(dir, name);
        const stat = await fs.stat(filePath);
        if (stat.isFile()) {
            await procFun(filePath);
        } else if (stat.isDirectory()) {
            await walkDirectory(filePath, procFun);
        } else {
            throw 'unknown type';
        }
    }
}

As for Emacs Lisp, it provides directory-files-recursively to achieve the same goal.

Footnotes:

1

Or should I say, moved to obsolescent status? Likewise, should fdw be called file tree traversal? The verbiage difference among documentation of different Unix distros span from annoyance to confusion: IBM z/OS calls the same function "Traverse a file tree", while Oracle Solaris calls it "walk a file tree", and macOS calls it "traverse (walk) a file tree". You gotta be kidding me.