~emersion/mrsh

1af38099508a577e3e312693048bf1148c9ad768 — Ben Brown 1 year, 1 month ago 22232fa
Adding getopt implementation

It's pretty simple but I think it covers all of items described in the
standard.
M builtin/alias.c => builtin/alias.c +6 -6
@@ 1,5 1,5 @@
#define _POSIX_C_SOURCE 200809L
#include <getopt.h>
#include <mrsh/getopt.h>
#include <mrsh/builtin.h>
#include <stdio.h>
#include <string.h>


@@ 17,19 17,19 @@ static void print_alias_iterator(const char *key, void *_value,
}

int builtin_alias(struct mrsh_state *state, int argc, char *argv[]) {
	optind = 0;
	if (getopt(argc, argv, ":") != -1) {
		fprintf(stderr, "alias: unknown option -- %c\n", optopt);
	mrsh_optind = 1;
	if (mrsh_getopt(argc, argv, ":") != -1) {
		fprintf(stderr, "alias: unknown option -- %c\n", mrsh_optopt);
		fprintf(stderr, alias_usage);
		return EXIT_FAILURE;
	}

	if (optind == argc) {
	if (mrsh_optind == argc) {
		mrsh_hashtable_for_each(&state->aliases, print_alias_iterator, NULL);
		return EXIT_SUCCESS;
	}

	for (int i = optind; i < argc; ++i) {
	for (int i = mrsh_optind; i < argc; ++i) {
		char *alias = argv[i];
		char *equal = strchr(alias, '=');
		if (equal != NULL) {

M builtin/cd.c => builtin/cd.c +7 -6
@@ 1,6 1,7 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <limits.h>
#include <mrsh/getopt.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>


@@ 36,9 37,9 @@ static int isdir(char *path) {
}

int builtin_cd(struct mrsh_state *state, int argc, char *argv[]) {
	optind = 0;
	mrsh_optind = 1;
	int opt;
	while ((opt = getopt(argc, argv, ":LP")) != -1) {
	while ((opt = mrsh_getopt(argc, argv, ":LP")) != -1) {
		switch (opt) {
		case 'L':
		case 'P':


@@ 46,17 47,17 @@ int builtin_cd(struct mrsh_state *state, int argc, char *argv[]) {
			fprintf(stderr, "cd: `-L` and `-P` not yet implemented\n");
			return EXIT_FAILURE;
		default:
			fprintf(stderr, "cd: unknown option -- %c\n", optopt);
			fprintf(stderr, "cd: unknown option -- %c\n", mrsh_optopt);
			fprintf(stderr, cd_usage);
			return EXIT_FAILURE;
		}
	}
	if (optind + 1 > argc) {
	if (mrsh_optind + 1 > argc) {
		fprintf(stderr, cd_usage);
		return EXIT_FAILURE;
	}

	if (optind == argc) {
	if (mrsh_optind == argc) {
		const char *home = mrsh_env_get(state, "HOME", NULL);
		if (home && home[0] != '\0') {
			return cd(state, home);


@@ 66,7 67,7 @@ int builtin_cd(struct mrsh_state *state, int argc, char *argv[]) {
		return EXIT_FAILURE;
	}

	char *curpath = argv[optind];
	char *curpath = argv[mrsh_optind];
	// `cd -`
	if (strcmp(curpath, "-") == 0) {
		// This case is special as we print `pwd` at the end

M builtin/getopts.c => builtin/getopts.c +21 -21
@@ 2,38 2,38 @@
#include <errno.h>
#include <limits.h>
#include <mrsh/buffer.h>
#include <mrsh/getopt.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "builtin.h"

static const char getopts_usage[] = "usage: getopts optstring name [arg...]\n";

int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]) {
	optind = 0;
	if (getopt(argc, argv, ":") != -1) {
		fprintf(stderr, "getopts: unknown option -- %c\n", optopt);
	mrsh_optind = 1;
	if (mrsh_getopt(argc, argv, ":") != -1) {
		fprintf(stderr, "getopts: unknown option -- %c\n", mrsh_optopt);
		fprintf(stderr, getopts_usage);
		return EXIT_FAILURE;
	}
	if (optind + 2 < argc) {
	if (mrsh_optind + 2 < argc) {
		fprintf(stderr, getopts_usage);
		return EXIT_FAILURE;
	}

	int optc;
	char **optv;
	if (optind + 2 > argc) {
		optc = argc - optind - 2;
		optv = &argv[optind + 2];
	if (mrsh_optind + 2 > argc) {
		optc = argc - mrsh_optind - 2;
		optv = &argv[mrsh_optind + 2];
	} else {
		optc = state->args->argc;
		optv = state->args->argv;
	}
	char *optstring = argv[optind];
	char *name = argv[optind + 1];
	char *optstring = argv[mrsh_optind];
	char *name = argv[mrsh_optind + 1];

	const char *optind_str = mrsh_env_get(state, "OPTIND", NULL);
	if (optind_str == NULL) {


@@ 46,29 46,29 @@ int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]) {
		fprintf(stderr, "getopts: OPTIND is not a positive integer\n");
		return EXIT_FAILURE;
	}
	optind = (int)optind_long;
	mrsh_optind = (int)optind_long;

	optopt = 0;
	int opt = getopt(optc, optv, optstring);
	mrsh_optopt = 0;
	int opt = mrsh_getopt(optc, optv, optstring);

	char optind_fmt[16];
	snprintf(optind_fmt, sizeof(optind_fmt), "%d", optind);
	snprintf(optind_fmt, sizeof(optind_fmt), "%d", mrsh_optind);
	mrsh_env_set(state, "OPTIND", optind_fmt, MRSH_VAR_ATTRIB_NONE);

	if (optopt != 0) {
	if (mrsh_optopt != 0) {
		if (opt == ':') {
			char value[] = {(char)optopt, '\0'};
			char value[] = {(char)mrsh_optopt, '\0'};
			mrsh_env_set(state, "OPTARG", value, MRSH_VAR_ATTRIB_NONE);
		} else if (optstring[0] != ':') {
			mrsh_env_unset(state, "OPTARG");
		} else {
			// either missing option-argument or unknown option character
			// in the former case, unset OPTARG
			// in the latter case, set OPTARG to optopt
			// in the latter case, set OPTARG to mrsh_optopt
			bool opt_exists = false;
			size_t len = strlen(optstring);
			for (size_t i = 0; i < len; ++i) {
				if (optstring[i] == optopt) {
				if (optstring[i] == mrsh_optopt) {
					opt_exists = true;
					break;
				}


@@ 76,12 76,12 @@ int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]) {
			if (opt_exists) {
				mrsh_env_unset(state, "OPTARG");
			} else {
				char value[] = {(char)optopt, '\0'};
				char value[] = {(char)mrsh_optopt, '\0'};
				mrsh_env_set(state, "OPTARG", value, MRSH_VAR_ATTRIB_NONE);
			}
		}
	} else if (optarg != NULL) {
		mrsh_env_set(state, "OPTARG", optarg, MRSH_VAR_ATTRIB_NONE);
	} else if (mrsh_optarg != NULL) {
		mrsh_env_set(state, "OPTARG", mrsh_optarg, MRSH_VAR_ATTRIB_NONE);
	} else {
		mrsh_env_unset(state, "OPTARG");
	}

M builtin/pwd.c => builtin/pwd.c +5 -5
@@ 1,5 1,5 @@
#define _POSIX_C_SOURCE 200809L
#include <getopt.h>
#include <mrsh/getopt.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>


@@ 8,9 8,9 @@
static const char pwd_usage[] = "usage: pwd [-L|-P]\n";

int builtin_pwd(struct mrsh_state *state, int argc, char *argv[]) {
	optind = 0;
	mrsh_optind = 1;
	int opt;
	while ((opt = getopt(argc, argv, ":LP")) != -1) {
	while ((opt = mrsh_getopt(argc, argv, ":LP")) != -1) {
		switch (opt) {
		case 'L':
		case 'P':


@@ 18,12 18,12 @@ int builtin_pwd(struct mrsh_state *state, int argc, char *argv[]) {
			fprintf(stderr, "pwd: `-L` and `-P` not yet implemented\n");
			return EXIT_FAILURE;
		default:
			fprintf(stderr, "pwd: unknown option -- %c\n", optopt);
			fprintf(stderr, "pwd: unknown option -- %c\n", mrsh_optopt);
			fprintf(stderr, pwd_usage);
			return EXIT_FAILURE;
		}
	}
	if (optind < argc) {
	if (mrsh_optind < argc) {
		fprintf(stderr, pwd_usage);
		return EXIT_FAILURE;
	}

M builtin/read.c => builtin/read.c +12 -12
@@ 1,12 1,12 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <mrsh/buffer.h>
#include <mrsh/getopt.h>
#include <mrsh/shell.h>
#include <shell/word.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "builtin.h"

static const char read_usage[] = "usage: read [-r] var...\n";


@@ 14,20 14,20 @@ static const char read_usage[] = "usage: read [-r] var...\n";
int builtin_read(struct mrsh_state *state, int argc, char *argv[]) {
	bool raw = false;

	optind = 0;
	mrsh_optind = 1;
	int opt;
	while ((opt = getopt(argc, argv, ":r")) != -1) {
	while ((opt = mrsh_getopt(argc, argv, ":r")) != -1) {
		switch (opt) {
		case 'r':
			raw = true;
			break;
		default:
			fprintf(stderr, "read: unknown option -- %c\n", optopt);
			fprintf(stderr, "read: unknown option -- %c\n", mrsh_optopt);
			fprintf(stderr, read_usage);
			return EXIT_FAILURE;
		}
	}
	if (optind == argc) {
	if (mrsh_optind == argc) {
		fprintf(stderr, read_usage);
		return EXIT_FAILURE;
	}


@@ 60,19 60,19 @@ int builtin_read(struct mrsh_state *state, int argc, char *argv[]) {
	split_fields(&fields, &ws->word, mrsh_env_get(state, "IFS", NULL));
	mrsh_word_destroy(&ws->word);

	if (fields.len <= (size_t)(argc - optind)) {
	if (fields.len <= (size_t)(argc - mrsh_optind)) {
		for (size_t i = 0; i < fields.len; ++i) {
			mrsh_env_set(state, argv[optind + i], (char *)fields.data[i], MRSH_VAR_ATTRIB_NONE);
			mrsh_env_set(state, argv[mrsh_optind + i], (char *)fields.data[i], MRSH_VAR_ATTRIB_NONE);
		}
		for (size_t i = fields.len; i < (size_t)(argc - optind); ++i) {
			mrsh_env_set(state, argv[optind + i], "", MRSH_VAR_ATTRIB_NONE);
		for (size_t i = fields.len; i < (size_t)(argc - mrsh_optind); ++i) {
			mrsh_env_set(state, argv[mrsh_optind + i], "", MRSH_VAR_ATTRIB_NONE);
		}
	} else {
		for (int i = 0; i < argc - optind - 1; ++i) {
			mrsh_env_set(state, argv[optind + i], (char *)fields.data[i], MRSH_VAR_ATTRIB_NONE);
		for (int i = 0; i < argc - mrsh_optind - 1; ++i) {
			mrsh_env_set(state, argv[mrsh_optind + i], (char *)fields.data[i], MRSH_VAR_ATTRIB_NONE);
		}
		struct mrsh_buffer buf_last = {0};
		for (size_t i = (size_t)(argc - optind - 1); i < fields.len; ++i) {
		for (size_t i = (size_t)(argc - mrsh_optind - 1); i < fields.len; ++i) {
			char *field = (char *)fields.data[i];
			mrsh_buffer_append(&buf_last, field, strlen(field));
			if (i != fields.len - 1) {

M builtin/type.c => builtin/type.c +6 -6
@@ 1,6 1,6 @@
#define _POSIX_C_SOURCE 200809L
#include <getopt.h>
#include <mrsh/builtin.h>
#include <mrsh/getopt.h>
#include <mrsh/shell.h>
#include <shell/path.h>
#include <stdlib.h>


@@ 9,19 9,19 @@
static const char type_usage[] = "usage: type name...\n";

int builtin_type(struct mrsh_state *state, int argc, char *argv[]) {
	optind = 0;
	if (getopt(argc, argv, ":") != -1) {
		fprintf(stderr, "type: unknown option -- %c\n", optopt);
	mrsh_optind = 1;
	if (mrsh_getopt(argc, argv, ":") != -1) {
		fprintf(stderr, "type: unknown option -- %c\n", mrsh_optopt);
		fprintf(stderr, type_usage);
		return EXIT_FAILURE;
	}
	if (optind == argc) {
	if (mrsh_optind == argc) {
		fprintf(stderr, type_usage);
		return EXIT_FAILURE;
	}

	bool error = false;
	for (int i = optind; i < argc; ++i) {
	for (int i = mrsh_optind; i < argc; ++i) {
		char *name = argv[i];

		char *alias = mrsh_hashtable_get(&state->aliases, name);

M builtin/umask.c => builtin/umask.c +7 -7
@@ 1,6 1,6 @@
#define _POSIX_C_SOURCE 200809L
#include <getopt.h>
#include <mrsh/builtin.h>
#include <mrsh/getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>


@@ 162,23 162,23 @@ int builtin_umask(struct mrsh_state *state, int argc, char *argv[]) {
	mode_t mode;
	bool umask_symbolic = false;

	optind = 0;
	mrsh_optind = 1;
	int opt;

	while ((opt = getopt(argc, argv, ":S")) != -1) {
	while ((opt = mrsh_getopt(argc, argv, ":S")) != -1) {
		switch (opt) {
		case 'S':
			umask_symbolic = true;
			break;

		default:
			fprintf(stderr, "Unknown option -- '%c'\n", optopt);
			fprintf(stderr, "Unknown option -- '%c'\n", mrsh_optopt);
			fprintf(stderr, umask_usage);
			return EXIT_FAILURE;
		}
	}

	if (optind == argc) {
	if (mrsh_optind == argc) {
		mode = umask_current_mask();

		if (umask_symbolic) {


@@ 191,10 191,10 @@ int builtin_umask(struct mrsh_state *state, int argc, char *argv[]) {
	}

	char *endptr;
	mode = strtol(argv[optind], &endptr, 8);
	mode = strtol(argv[mrsh_optind], &endptr, 8);

	if (*endptr != '\0') {
		if (!umask_mode(&mode, argv[optind])) {
		if (!umask_mode(&mode, argv[mrsh_optind])) {
			fprintf(stderr, umask_usage);
			return EXIT_FAILURE;
		}

M builtin/unalias.c => builtin/unalias.c +7 -7
@@ 1,6 1,6 @@
#define _POSIX_C_SOURCE 200809L
#include <getopt.h>
#include <mrsh/builtin.h>
#include <mrsh/getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


@@ 15,22 15,22 @@ static void delete_alias_iterator(const char *key, void *_value, void *user_data
int builtin_unalias(struct mrsh_state *state, int argc, char *argv[]) {
	bool all = false;

	optind = 0;
	mrsh_optind = 1;
	int opt;
	while ((opt = getopt(argc, argv, ":a")) != -1) {
	while ((opt = mrsh_getopt(argc, argv, ":a")) != -1) {
		switch (opt) {
		case 'a':
			all = true;
			break;
		default:
			fprintf(stderr, "unalias: unknown option -- %c\n", optopt);
			fprintf(stderr, "unalias: unknown option -- %c\n", mrsh_optopt);
			fprintf(stderr, unalias_usage);
			return EXIT_FAILURE;
		}
	}

	if (all) {
		if (optind < argc) {
		if (mrsh_optind < argc) {
			fprintf(stderr, unalias_usage);
			return EXIT_FAILURE;
		}


@@ 38,12 38,12 @@ int builtin_unalias(struct mrsh_state *state, int argc, char *argv[]) {
		return EXIT_SUCCESS;
	}

	if (optind == argc) {
	if (mrsh_optind == argc) {
		fprintf(stderr, unalias_usage);
		return EXIT_FAILURE;
	}

	for (int i = optind; i < argc; ++i) {
	for (int i = mrsh_optind; i < argc; ++i) {
		free(mrsh_hashtable_del(&state->aliases, argv[i]));
	}
	return EXIT_SUCCESS;

M builtin/unset.c => builtin/unset.c +6 -6
@@ 1,9 1,9 @@
#define _POSIX_C_SOURCE 200809L
#include <mrsh/builtin.h>
#include <mrsh/getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "builtin.h"

static const char unset_usage[] = "usage: unset [-fv] name...\n";


@@ 11,9 11,9 @@ static const char unset_usage[] = "usage: unset [-fv] name...\n";
int builtin_unset(struct mrsh_state *state, int argc, char *argv[]) {
	bool funcs = false;

	optind = 0;
	mrsh_optind = 1;
	int opt;
	while ((opt = getopt(argc, argv, ":fv")) != -1) {
	while ((opt = mrsh_getopt(argc, argv, ":fv")) != -1) {
		switch (opt) {
		case 'f':
			funcs = true;


@@ 22,16 22,16 @@ int builtin_unset(struct mrsh_state *state, int argc, char *argv[]) {
			funcs = false;
			break;
		default:
			fprintf(stderr, "unset: unknown option -- %c\n", optopt);
			fprintf(stderr, "unset: unknown option -- %c\n", mrsh_optopt);
			fprintf(stderr, unset_usage);
			return EXIT_FAILURE;
		}
	}
	if (optind >= argc) {
	if (mrsh_optind >= argc) {
		fprintf(stderr, unset_usage);
		return EXIT_FAILURE;
	}
	for (int i = optind; i < argc; ++i) {
	for (int i = mrsh_optind; i < argc; ++i) {
		if (!funcs) {
			uint32_t prev_attribs = 0;
			if (mrsh_env_get(state, argv[i], &prev_attribs)) {

A getopt.c => getopt.c +74 -0
@@ 0,0 1,74 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <mrsh/getopt.h>
#include <stdio.h>

char *mrsh_optarg = NULL;
int mrsh_optind = 1;
int mrsh_opterr = 1;
int mrsh_optopt = 0;

int mrsh_getopt(int argc, char *const argv[], const char *optstring) {
	assert(argv[argc] == NULL);
	mrsh_optarg = NULL;

	if (mrsh_optind >= argc) {
		return -1;
	}

	if (argv[mrsh_optind][0] != '-') {
		return -1;
	}

	if (argv[mrsh_optind][1] == '\0') {
		return -1;
	}

	if (argv[mrsh_optind][1] == '-') {
		mrsh_optind++;
		return -1;
	}

	const char *c = optstring;
	if (*c == ':') {
		c++;
	}

	mrsh_optopt = 0;
	int opt = argv[mrsh_optind][1];
	for (; *c != '\0'; c++) {
		if (*c != opt) {
			continue;
		}

		if (c[1] != ':') {
			mrsh_optind++;
			return opt;
		}

		if (argv[mrsh_optind][2] != '\0') {
			mrsh_optarg = &argv[mrsh_optind][2]; 
		} else {
			if (mrsh_optind + 2 > argc) {
				mrsh_optopt = opt;
				if (mrsh_opterr != 0 && optstring[0] != ':') {
					fprintf(stderr, "%s: Option '%c' requires an argument.\n",
						argv[0], mrsh_optopt);
				}

				return optstring[0] == ':' ? ':' : '?';
			}

			mrsh_optarg = argv[++mrsh_optind];
		}

		mrsh_optind++;
		return opt;
	}

	if (mrsh_opterr != 0 && optstring[0] != ':') {
		fprintf(stderr, "%s: Option '%c' not found.\n", argv[0], opt);
	}

	return '?';
}

A include/mrsh/getopt.h => include/mrsh/getopt.h +9 -0
@@ 0,0 1,9 @@
#ifndef MRSH_GETOPT_H
#define MRSH_GETOPT_H

extern char *mrsh_optarg;
extern int mrsh_opterr, mrsh_optind, mrsh_optopt;

int mrsh_getopt(int argc, char * const argv[], const char *optstring);

#endif

M meson.build => meson.build +1 -0
@@ 67,6 67,7 @@ lib_mrsh = library(
		'builtin/umask.c',
		'builtin/unalias.c',
		'builtin/unset.c',
		'getopt.c',
		'hashtable.c',
		'parser/arithm.c',
		'parser/parser.c',