core: Add file counting + state management

Signed-off-by: Ian Moffett <ian@mirocom.org>
This commit is contained in:
2026-02-09 16:53:02 -05:00
parent 5fcbdc9525
commit 931850beac

View File

@@ -1,9 +1,32 @@
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#define CAV_VERSION "0.0.1"
static const char *input_name = NULL;
static const char *output_name = "out.cav";
/*
* Represents the consume and vomit state machine
*
* @dir_fd: Input directory file descriptor
* @dir: Opened directory
* @pass_count: Number of passes made in file
* @file_count: Number of files encountered including subdirectories
*/
struct cav_state {
int dir_fd;
DIR *dir;
uint8_t pass_count;
size_t file_count;
};
static void
help(void)
{
@@ -12,6 +35,8 @@ help(void)
"--------------------------\n"
"[-h] Display this help menu\n"
"[-v] Display the version\n"
"[-i] Input directory\n"
"[-o] Output filename\n"
);
}
@@ -27,12 +52,119 @@ version(void)
);
}
/*
* Initialize the cav state machine
*
* @state: State to initialize
*
* Returns zero on success
*/
static int
state_init(struct cav_state *state)
{
if (state == NULL) {
errno = -EINVAL;
return -1;
}
state->dir_fd = open(input_name, O_RDONLY);
if (state->dir_fd < 0) {
return -1;
}
state->dir = fdopendir(state->dir_fd);
if (state->dir == NULL) {
close(state->dir_fd);
return -1;
}
state->pass_count = 0;
return 0;
}
/*
* Destroy the consume and vomit state
*
* @state: State to destroy
*/
static void
state_destroy(struct cav_state *state)
{
if (state == NULL) {
return;
}
closedir(state->dir);
close(state->dir_fd);
}
/*
* Begin recursively scanning a directory to obtain information
* needed to structure the offsets at the beginning of the file.
*/
static void
consume_scan(const char *dir_path, struct cav_state *state, DIR *subdir)
{
struct dirent *dirent;
char buf[256];
DIR *dir;
if (dir_path == NULL || state == NULL) {
return;
}
dir = (subdir == NULL) ? state->dir : subdir;
while ((dirent = readdir(dir)) != NULL) {
if (dirent->d_name[0] == '.') {
continue;
}
switch (dirent->d_type) {
case DT_DIR:
snprintf(buf, sizeof(buf), "%s/%s", dir_path, dirent->d_name);
if ((subdir = opendir(buf)) == NULL) {
printf("failed to open subdir %s\n", buf);
perror("opendir");
continue;
}
consume_scan(buf, state, subdir);
break;
default:
++state->file_count;
break;
}
}
}
/*
* Consume a single pass in our yummy yummy consumption
* process only to be thrown up again along with stomach
* acids of your extraterrestrial friends.
*/
static void
consume_pass(struct cav_state *state)
{
if (state == NULL) {
return;
}
switch (state->pass_count++) {
case 0:
/* Scan pass */
consume_scan(input_name, state, NULL);
printf("[pass 0] scanned %zu files\n", state->file_count);
break;
}
}
int
main(int argc, char **argv)
{
struct cav_state state;
int opt;
while ((opt = getopt(argc, argv, "hv")) != -1) {
while ((opt = getopt(argc, argv, "hvi:o:")) != -1) {
switch (opt) {
case 'h':
help();
@@ -40,8 +172,37 @@ main(int argc, char **argv)
case 'v':
version();
return -1;
case 'i':
input_name = strdup(optarg);
break;
case 'o':
output_name = strdup(optarg);
break;
}
}
if (input_name == NULL) {
printf("fatal: expected input path\n");
return -1;
}
/* Initialize the state machine */
if (state_init(&state) < 0) {
perror("state_init");
return -1;
}
/*
* There are three passes required in the consumption operation
*
* pass 0): Obtain number of files for offset table creation
* pass 1): Construct an offset table using what we know
* pass 2): Construct the archive segments
*/
for (int i = 0; i < 2; ++i) {
consume_pass(&state);
}
state_destroy(&state);
return 0;
}