diff options
| author | Adam Spragg <adam@spra.gg> | 2022-12-07 17:54:15 +0000 | 
|---|---|---|
| committer | Adam Spragg <adam@spra.gg> | 2022-12-07 17:54:15 +0000 | 
| commit | 302e325770f749180c8c95297eb6dff53cc9d8ab (patch) | |
| tree | 327dbecf88c7fac5eff9f51e145705711a26dfed /7.c | |
| parent | 1fc8bea7c7a5a0f0b0e9bbbfd4bf5fbdf98f56f5 (diff) | |
Solve puzzle 7 part 1
Diffstat (limited to '7.c')
| -rw-r--r-- | 7.c | 333 | 
1 files changed, 333 insertions, 0 deletions
| @@ -0,0 +1,333 @@ + +#include <dirent.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +//#define DENT_DUMP + +struct dent { +	int type; +	size_t size; +	struct dent * parent; +	struct dent * next; +	struct dent * child; +	char name[]; +}; + + +// Create a new directory entry. Insert it into the tree if parent is given. +struct dent * +dent_create(int type, size_t size, struct dent * parent, char const * name) +{ +	int namelen; +	struct dent * d; + +	if (!name) +		return NULL; + +	namelen = strlen(name); +	d = malloc(sizeof(struct dent) + namelen + 1); +	if (!d) +		return NULL; + +	d->type = type; +	d->size = size; +	d->parent = parent; +	if (parent) { +		d->next = d->parent->child; +		d->parent->child = d; +	} +	else { +		d->next = NULL; +	} +	d->child = NULL; +	memcpy(d->name, name, namelen + 1); + +	return d; +} + + +// Free a directory entry and its children +void +dent_free(struct dent * d) +{ +	if (!d) +		return; + +	dent_free(d->child); +	dent_free(d->next); +	free(d); +} + + +// Find a directory entry by name, in parent only (not recursive) +struct dent * +dent_find(struct dent * parent, char const * name) +{ +	struct dent * d; + +	for (d = parent->child; d != NULL; d = d->next) { +		if (strcmp(d->name, name) == 0) { +			return d; +		} +	} + +	return NULL; +} + + +// Find the next entry for walking an entire directory tree. +struct dent * +dent_next(struct dent * d) +{ +	if (!d) +		return NULL; + +	if (d->child) +		return d->child; +	if (d->next) +		return d->next; + +	while ((d = d->parent) != NULL) +		if (d->next) +			return d->next; + +	return NULL; +} + + +// Get the recursive usage of a directory entry. +// +// This caches directory sizes in their dents. +size_t +dent_du(struct dent * d) +{ +	struct dent * child; + +	switch (d->type) { +	case DT_REG: +		return d->size; + +	case DT_DIR: +		d->size = 0; +		for (child = d->child; child != NULL; child = child->next) { +			d->size += dent_du(child); +		} +		return d->size; +	} + +	return 0; +} + + +#ifdef DENT_DUMP +void +dent_dump(FILE * out, struct dent * d, int depth) +{ +	struct dent * child; + +	if (!d) +		return; + +	fprintf(out, "%*s %s", depth * 2, "-", d->name); +	switch (d->type) { +	case DT_DIR: +		fprintf(out, " (dir)"); +		break; +	case DT_REG: +		fprintf(out, " (file, size=%zu)", d->size); +		break; +	default: +		fprintf(out, " (unknown!)"); +		break; +	} +	fprintf(out, "\n"); + +	for (child = d->child; child != NULL; child = child->next) { +		dent_dump(out, child, depth + 1); +	} +} +#endif + + +// Parse an `ls` command. +int +cmd_ls(struct dent ** cwd, char * buf, size_t bufsiz, FILE * in, char const * arg) +{ +	static regex_t dir, file; +	static int init = 0; + +	if (init == 0) { +		init = 1; +		if (regcomp(&dir, "^(dir) ([[:alnum:].]+)\n?$", REG_EXTENDED) != 0 +				|| regcomp(&file, "^([[:digit:]]+) ([[:alnum:].]+)\n?$", REG_EXTENDED) != 0) +		{ +			fprintf(stderr, "ls: Unable to compile regexes"); +			init = -1; +		} +	} +	if (init == -1) { +		return -1; +	} +	if (!buf && !in) { +		regfree(&dir); +		regfree(&file); +		init = 0; +		return 0; +	} + +	if (arg) { +		fprintf(stderr, "%s: Unexpected arg to `ls`\n", __FUNCTION__); +		return -1; +	} + +	while (fgets(buf, bufsiz, in)) { +		regmatch_t matches[3]; +		struct dent * d; + +		if (buf[0] == '$') { +			return 0; +		} +		else if (regexec(&dir, buf, 3, matches, 0) == 0) { +			buf[matches[2].rm_eo] = '\0'; +			if ((d = dent_find(*cwd, buf + matches[2].rm_so)) == NULL) { +				d = dent_create(DT_DIR, 0, *cwd, buf + matches[2].rm_so); +			} +			if (d->type != DT_DIR) { +				fprintf(stderr, "%s: Mismatched type for %s\n", __FUNCTION__, d->name); +			} +		} +		else if (regexec(&file, buf, 3, matches, 0) == 0) { +			buf[matches[2].rm_eo] = '\0'; +			if ((d = dent_find(*cwd, buf + matches[2].rm_so)) == NULL) { +				d = dent_create(DT_REG, atoi(buf + matches[1].rm_so), +						*cwd, buf + matches[2].rm_so); +			} + +			if (d->type != DT_REG) { +				fprintf(stderr, "%s: Mismatched type for %s\n", __FUNCTION__, d->name); +			} +		} +		else { +			fprintf(stderr, "%s: Unexpected output: %s\n", __FUNCTION__, buf); +			return -1; +		} +	} + +	buf[0] = '\0'; +	return -1; +} + + +// Parse a `cd` command. +int +cmd_cd(struct dent ** cwd, char * buf, size_t bufsiz, FILE * in, char const * arg) +{ +	if (!arg) { +		fprintf(stderr, "%s: Missing parameter\n", __FUNCTION__); +		return -1; +	} + +	if (strcmp(arg, "/") == 0) { +		if (!*cwd) +			*cwd = dent_create(DT_DIR, 0, NULL, "/"); + +		while (*cwd && (*cwd)->parent) +			*cwd = (*cwd)->parent; +	} +	else if (strcmp(arg, "..") == 0) { +		if (!*cwd) { +			fprintf(stderr, "%s: No cwd!\n", __FUNCTION__); +			return -1; +		} +		if (!(*cwd)->parent) { +			fprintf(stderr, "%s: No parent dir!\n", __FUNCTION__); +			return -1; +		} +		*cwd = (*cwd)->parent; +	} +	else { +		struct dent * d = dent_find(*cwd, arg); +		if (!d) { +			fprintf(stderr, "%s: No subdirectory: %s\n", __FUNCTION__, arg); +			return -1; +		} +		if (d->type != DT_DIR) { +			fprintf(stderr, "%s: Is not a directory: %s\n", __FUNCTION__, arg); +			return -1; +		} +		*cwd = d; +	} + +	return fgets(buf, bufsiz, in) ? 0 : -1; +} + + +int +main() +{ +	char buf[BUFSIZ]; +	struct dent * cwd = NULL, * root, * d; +	int i; +	size_t s; + +	i = fgets(buf, sizeof(buf), stdin) ? 0 : -1; +	while (i == 0) { +		char * tok, * toksave = NULL; + +		tok = strtok_r(buf, " \n", &toksave); +		if (strcmp(tok, "$") != 0) { +			fprintf(stderr, "Not a command: %s\n", buf); +			return -1; +		} + +		// Get the next command. +		// +		// Command parsers are responsible for parsing the command argument, +		// and any output of the command, and reading the line containing the +		// next command into `buf`. +		// +		// They should return 0 for success, -1 on error. +		tok = strtok_r(NULL, " \n", &toksave); +		if (strcmp(tok, "cd") == 0) { +			tok = strtok_r(NULL, "\n", &toksave); +			i = cmd_cd(&cwd, buf, sizeof(buf), stdin, tok); +		} +		else if (strcmp(tok, "ls") == 0) { +			tok = strtok_r(NULL, "\n", &toksave); +			i = cmd_ls(&cwd, buf, sizeof(buf), stdin, tok); +		} +		else { +			fprintf(stderr, "Unexpected command: %s\n", tok); +			return -1; +		} +	} + +	root = cwd; +	while (root && root->parent) +		root = root->parent; + +#ifdef DENT_DUMP +	dent_dump(stderr, root, 0); +#endif + +	// Calculate disk usage sizes; +	dent_du(root); + +	// Find directories whose size is less than a limit, and sum them. +	s = 0; +	for (d = root; d != NULL; d = dent_next(d)) { +		if (d->type == DT_DIR && d->size <= 100000) +			s += d->size; +	} +	printf("Sum of dir sizes where size <= 100000: %zu\n", s); + +	// Tidy up +	dent_free(root); +	cmd_ls(NULL, NULL, 0, NULL, NULL); + +	return 0; +} + | 
