Skip to content

Instantly share code, notes, and snippets.

@Cxarli
Last active October 24, 2021 18:56
Show Gist options
  • Save Cxarli/7f8c66221aed61598e4a49f06ff4c059 to your computer and use it in GitHub Desktop.
Save Cxarli/7f8c66221aed61598e4a49f06ff4c059 to your computer and use it in GitHub Desktop.
A little script to write an index of all files in the current directory
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
void printdirent(struct dirent *entry, int parent_fd, char *parent_name);
void collect(char *dirname, int parent_fd, char *parent_name);
void printdirent(struct dirent *entry, int parent_fd, char *parent_name)
{
// Get entryname
char *name = entry->d_name;
// Get some stats such as size and modification time
struct stat statbuf;
if (fstatat(parent_fd, name, &statbuf, AT_SYMLINK_NOFOLLOW) == -1) {
fprintf(stderr, "lstat failed for %s: %s\n", name, strerror(errno));
return;
}
off_t size = statbuf.st_size;
struct timespec modiftime = statbuf.st_mtim;
unsigned mode = statbuf.st_mode;
char *type = S_ISREG(mode) ? "REG" : S_ISDIR(mode) ? "DIR" : S_ISLNK(mode) ? "LNK" : "UNK";
// Print all stats
printf("size=%ld\1mtime=%lu\1type=%s\1path=%s\1name=%s\n",
size, modiftime.tv_sec, type, parent_name, name);
}
void collect(char *dirname, int parent_fd, char *parent_name)
{
// skip Trash directory
if (strcmp(dirname, ".Trash-1000") == 0) return;
// Open FD for directory
int dir_fd = openat(parent_fd, dirname, O_DIRECTORY | O_RDONLY);
if (dir_fd == -1) {
fprintf(stderr, "open failed for %s%s: %s\n", parent_name, dirname, strerror(errno));
return;
}
// Get DIR struct for directory
DIR *dir = fdopendir(dir_fd);
if (dir == NULL) {
fprintf(stderr, "opendir failed for %s%s: %s\n", parent_name, dirname, strerror(errno));
return;
}
// Current directory string
// TODO: support paths longer than 1024 chars?
char *dirstr = malloc(1024 * sizeof(*dirstr));
strncpy(dirstr, parent_name, 1024);
strncat(dirstr, dirname, 1024);
strncat(dirstr, "/", 1024);
// Loop over all entries in directory
struct dirent *stats;
while ((stats = readdir(dir)) != NULL) {
// Ignore . and .. but do track dotfiles
if (strcmp(stats->d_name, ".") == 0 || strcmp(stats->d_name, "..") == 0) continue;
// Print stats for current entry
printdirent(stats, dir_fd, dirstr);
// If directory: recurse
if (stats->d_type == DT_DIR) {
collect(stats->d_name, dir_fd, dirstr);
}
}
// Clean up all resources
free(dirstr);
closedir(dir);
close(dir_fd);
}
int main(int argc, char *argv[])
{
// Get directory name from first argument or default to .
char *dirname = ".";
if (argc > 1) {
dirname = argv[1];
}
collect(dirname, AT_FDCWD, "");
}
@Cxarli
Copy link
Author

Cxarli commented Oct 24, 2021

Suggestions left for the reader:

  • use \x1F (unit separator) instead of \x01 (start of heading) as separator
  • don't do strncat on the original pointer several times, but keep track where the previous one ended and continue there
  • allow paths over 1024 characters long
  • don't duplicate the full path for every file in the index but only print it at the directory line
  • benchmark it against du -a --time
  • RIIR 🦀 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment