d4e63a2bd24c9b31a9a44e182a33978d787f8464 — Simon Ser 3 months ago 7c20ded
builtin/hash: new utility

This implements a minimal hash(1) utility, without any caching.
5 files changed, 57 insertions(+), 1 deletions(-)

M builtin/builtin.c
A builtin/hash.c
M configure
M include/builtin.h
M meson.build
M builtin/builtin.c => builtin/builtin.c +2 -1
@@ 28,6 28,7 @@ static const struct builtin builtins[] = {
 	{ "false", builtin_false, false },
 	{ "fg", builtin_fg, false },
 	{ "getopts", builtin_getopts, false },
+	{ "hash", builtin_hash, false },
 	{ "jobs", builtin_jobs, false },
 	{ "pwd", builtin_pwd, false },
 	{ "read", builtin_read, false },


@@ 69,7 70,7 @@ static int builtin_compare(const void *_a, const void *_b) {
 
 static int unspecified_compare(const void *_a, const void *_b) {
 	const char *a = _a;
-	const char * const *b = _b;
+	const char *const *b = _b;
 	return strcmp(a, *b);
 }
 

A builtin/hash.c => builtin/hash.c +52 -0
@@ 0,0 1,52 @@
+#include <mrsh/getopt.h>
+#include <mrsh/builtin.h>
+#include <shell/path.h>
+#include <stdio.h>
+#include <string.h>
+#include "builtin.h"
+
+static const char hash_usage[] = "usage: hash -r|utility...\n";
+
+int builtin_hash(struct mrsh_state *state, int argc, char *argv[]) {
+	/* Hashing and remembering executable location isn't implemented. Thus most
+	 * of this builtin just does nothing. */
+	mrsh_optind = 0;
+	int opt;
+	while ((opt = mrsh_getopt(argc, argv, ":r")) != -1) {
+		switch (opt) {
+		case 'r':
+			/* no-op: reset list of cached utilities */
+			return 0;
+		default:
+			fprintf(stderr, "hash: unknown option -- %c\n", mrsh_optopt);
+			fprintf(stderr, hash_usage);
+			return 1;
+		}
+	}
+
+	if (argc == 1) {
+		/* no-op: print list of cached utilities */
+		return 0;
+	}
+
+	for (int i = 1; i < argc; i++) {
+		const char *utility = argv[i];
+		if (strchr(utility, '/') != NULL) {
+			fprintf(stderr,
+				"hash: undefined behaviour: utility contains a slash\n");
+			return 1;
+		}
+
+		if (mrsh_has_builtin(utility)) {
+			continue;
+		}
+
+		const char *path = expand_path(state, utility, true);
+		if (path == NULL) {
+			fprintf(stderr, "hash: command not found: %s\n", utility);
+			return 1;
+		}
+	}
+
+	return 0;
+}

M configure => configure +1 -0
@@ 47,6 47,7 @@ libmrsh() {
 		'builtin/false.c' \
 		'builtin/fg.c' \
 		'builtin/getopts.c' \
+		'builtin/hash.c' \
 		'builtin/jobs.c' \
 		'builtin/pwd.c' \
 		'builtin/read.c' \

M include/builtin.h => include/builtin.h +1 -0
@@ 21,6 21,7 @@ int builtin_export(struct mrsh_state *state, int argc, char *argv[]);
 int builtin_false(struct mrsh_state *state, int argc, char *argv[]);
 int builtin_fg(struct mrsh_state *state, int argc, char *argv[]);
 int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]);
+int builtin_hash(struct mrsh_state *state, int argc, char *argv[]);
 int builtin_jobs(struct mrsh_state *state, int argc, char *argv[]);
 int builtin_pwd(struct mrsh_state *state, int argc, char *argv[]);
 int builtin_read(struct mrsh_state *state, int argc, char *argv[]);

M meson.build => meson.build +1 -0
@@ 86,6 86,7 @@ lib_mrsh = library(
 		'builtin/false.c',
 		'builtin/fg.c',
 		'builtin/getopts.c',
+		'builtin/hash.c',
 		'builtin/jobs.c',
 		'builtin/pwd.c',
 		'builtin/read.c',