2

[PATCH] Add shell/entry.c

Details
Message ID
<20181227180533.24011-1-sir@cmpwn.com>
Download raw message
Patch +143 -93
---
 include/mrsh/entry.h |  35 +++++++++++++++
 main.c               | 100 +++----------------------------------------
 meson.build          |   1 +
 shell/entry.c        | 100 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 143 insertions(+), 93 deletions(-)
 create mode 100644 include/mrsh/entry.h
 create mode 100644 shell/entry.c

diff --git a/include/mrsh/entry.h b/include/mrsh/entry.h
new file mode 100644
index 0000000..a700e6f
--- /dev/null
+++ b/include/mrsh/entry.h
@@ -0,0 +1,35 @@
+#ifndef MRSH_ENTRY_H
+#define MRSH_ENTRY_H
+
+#include <mrsh/shell.h>
+
+/**
+ * Expands $PS1 or returns the POSIX-specified default of "$" or "#". The caller
+ * must free the return value.
+ */
+char *mrsh_get_ps1(struct mrsh_state *state, int next_history_id);
+
+/**
+ * Expands $PS2 or returns the POSIX-specified default of ">". The caller must
+ * free the return value.
+ */
+char *mrsh_get_ps2(struct mrsh_state *state);
+
+/**
+ * Copies variables from the environment and sets up internal variables like
+ * IFS, PPID, PWD, etc.
+ */
+void mrsh_populate_env(struct mrsh_state *state);
+
+/**
+ * Sources /etc/profile and $HOME/.profile. Note that this behavior is not
+ * specified by POSIX. It is recommended to call this file in login shells
+ * (for which argv[0][0] == '-' by convention).
+ */
+void mrsh_source_profile(struct mrsh_state *state);
+
+/** Sources $ENV. Note that this behavior is not specified by POSIX. It is
+ * recommended to source this file in interactive shells. */
+void mrsh_source_env(struct mrsh_state *state);
+
+#endif
diff --git a/main.c b/main.c
index f4e2b6a..06bc0ab 100644
--- a/main.c
+++ b/main.c
@@ -5,82 +5,19 @@
 #include <mrsh/ast.h>
 #include <mrsh/buffer.h>
 #include <mrsh/builtin.h>
+#include <mrsh/entry.h>
 #include <mrsh/parser.h>
 #include <mrsh/shell.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include "builtin.h"
 #include "frontend.h"
 
-static char *expand_ps(struct mrsh_state *state, const char *ps1) {
-	struct mrsh_parser *parser = mrsh_parser_with_data(ps1, strlen(ps1));
-	struct mrsh_word *word = mrsh_parse_word(parser);
-	mrsh_parser_destroy(parser);
-	if (word == NULL) {
-		return NULL;
-	}
-
-	mrsh_run_word(state, &word);
-
-	return mrsh_word_str(word);
-}
-
-static char *get_ps1(struct mrsh_state *state) {
-	const char *ps1 = mrsh_env_get(state, "PS1", NULL);
-	if (ps1 != NULL) {
-		// TODO: Replace ! with next history ID
-		return expand_ps(state, ps1);
-	}
-	char *p = malloc(3);
-	sprintf(p, "%s", getuid() ? "$ " : "# ");
-	return p;
-}
-
-static char *get_ps2(struct mrsh_state *state) {
-	const char *ps2 = mrsh_env_get(state, "PS2", NULL);
-	if (ps2 != NULL) {
-		return expand_ps(state, ps2);
-	}
-	return strdup("> ");
-}
-
-static void source_file(struct mrsh_state *state, char *path) {
-	if (access(path, F_OK) == -1) {
-		return;
-	}
-	char *env_argv[] = { ".", path };
-	mrsh_run_builtin(state, sizeof(env_argv) / sizeof(env_argv[0]), env_argv);
-}
-
-static void source_profile(struct mrsh_state *state) {
-	source_file(state, "/etc/profile");
-
-	char path[PATH_MAX];
-	int n = snprintf(path, sizeof(path), "%s/.profile", getenv("HOME"));
-	if (n == sizeof(path)) {
-		fprintf(stderr, "Warning: $HOME/.profile is longer than PATH_MAX\n");
-		return;
-	}
-	source_file(state, path);
-}
-
-static void source_env(struct mrsh_state *state) {
-	char *path = getenv("ENV");
-	if (path == NULL) {
-		return;
-	}
-	// TODO: parameter expansion
-	source_file(state, path);
-}
-
 static const char *get_alias(const char *name, void *data) {
 	struct mrsh_state *state = data;
 	return mrsh_hashtable_get(&state->aliases, name);
 }
 
-extern char **environ;
-
 int main(int argc, char *argv[]) {
 	struct mrsh_state state = {0};
 	mrsh_state_init(&state);
@@ -91,39 +28,15 @@ int main(int argc, char *argv[]) {
 		return EXIT_FAILURE;
 	}
 
-	for (size_t i = 0; environ[i] != NULL; ++i) {
-		char *eql = strchr(environ[i], '=');
-		size_t klen = eql - environ[i];
-		char *key = strndup(environ[i], klen);
-		char *val = &eql[1];
-		mrsh_env_set(&state, key, val, MRSH_VAR_ATTRIB_EXPORT);
-		free(key);
-	}
-
-	mrsh_env_set(&state, "IFS", " \t\n", MRSH_VAR_ATTRIB_NONE);
-
-	pid_t ppid = getppid();
-	char ppid_str[24];
-	snprintf(ppid_str, sizeof(ppid_str), "%d", ppid);
-	mrsh_env_set(&state, "PPID", ppid_str, MRSH_VAR_ATTRIB_NONE);
-
-	// TODO check if path is well-formed, has . or .., and handle symbolic links
-	const char *pwd = mrsh_env_get(&state, "PWD", NULL);
-	if (pwd == NULL || strlen(pwd) >= PATH_MAX) {
-		char cwd[PATH_MAX];
-		getcwd(cwd, PATH_MAX);
-		mrsh_env_set(&state, "PWD", cwd, MRSH_VAR_ATTRIB_EXPORT);
-	}
-
-	mrsh_env_set(&state, "OPTIND", "1", MRSH_VAR_ATTRIB_NONE);
+	mrsh_populate_env(&state);
 
 	if (!(state.options & MRSH_OPT_NOEXEC)) {
 		// If argv[0] begins with `-`, it's a login shell
 		if (state.args->argv[0][0] == '-') {
-			source_profile(&state);
+			mrsh_source_profile(&state);
 		}
 		if (state.interactive) {
-			source_env(&state);
+			mrsh_source_env(&state);
 		}
 	}
 
@@ -159,9 +72,10 @@ int main(int argc, char *argv[]) {
 		if (state.interactive) {
 			char *prompt;
 			if (read_buffer.len > 0) {
-				prompt = get_ps2(&state);
+				prompt = mrsh_get_ps2(&state);
 			} else {
-				prompt = get_ps1(&state);
+				// TODO: next_history_id
+				prompt = mrsh_get_ps1(&state, 0);
 			}
 			char *line = NULL;
 			size_t n = interactive_next(&state, &line, prompt);
diff --git a/meson.build b/meson.build
index 1767ce2..8e1a7b8 100644
--- a/meson.build
+++ b/meson.build
@@ -74,6 +74,7 @@ lib_mrsh = library(
 		'parser/program.c',
 		'parser/word.c',
 		'shell/arithm.c',
+		'shell/entry.c',
 		'shell/match.c',
 		'shell/path.c',
 		'shell/process.c',
diff --git a/shell/entry.c b/shell/entry.c
new file mode 100644
index 0000000..fd420a1
--- /dev/null
+++ b/shell/entry.c
@@ -0,0 +1,100 @@
+#define _POSIX_C_SOURCE 200809L
+#include <mrsh/builtin.h>
+#include <mrsh/entry.h>
+#include <mrsh/shell.h>
+#include <mrsh/parser.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "builtin.h"
+
+extern char **environ;
+
+static char *expand_ps(struct mrsh_state *state, const char *ps1) {
+	struct mrsh_parser *parser = mrsh_parser_with_data(ps1, strlen(ps1));
+	struct mrsh_word *word = mrsh_parse_word(parser);
+	mrsh_parser_destroy(parser);
+	if (word == NULL) {
+		return NULL;
+	}
+	mrsh_run_word(state, &word);
+	return mrsh_word_str(word);
+}
+
+char *mrsh_get_ps1(struct mrsh_state *state, int next_history_id) {
+	// TODO: Replace ! with next history ID
+	const char *ps1 = mrsh_env_get(state, "PS1", NULL);
+	if (ps1 != NULL) {
+		return expand_ps(state, ps1);
+	}
+	char *p = malloc(3);
+	sprintf(p, "%s", getuid() ? "$ " : "# ");
+	return p;
+}
+
+char *mrsh_get_ps2(struct mrsh_state *state) {
+	// TODO: Replace ! with next history ID
+	const char *ps2 = mrsh_env_get(state, "PS2", NULL);
+	if (ps2 != NULL) {
+		return expand_ps(state, ps2);
+	}
+	return strdup("> ");
+}
+
+void mrsh_populate_env(struct mrsh_state *state) {
+	for (size_t i = 0; environ[i] != NULL; ++i) {
+		char *eql = strchr(environ[i], '=');
+		size_t klen = eql - environ[i];
+		char *key = strndup(environ[i], klen);
+		char *val = &eql[1];
+		mrsh_env_set(state, key, val, MRSH_VAR_ATTRIB_EXPORT);
+		free(key);
+	}
+
+	mrsh_env_set(state, "IFS", " \t\n", MRSH_VAR_ATTRIB_NONE);
+
+	pid_t ppid = getppid();
+	char ppid_str[24];
+	snprintf(ppid_str, sizeof(ppid_str), "%d", ppid);
+	mrsh_env_set(state, "PPID", ppid_str, MRSH_VAR_ATTRIB_NONE);
+
+	// TODO check if path is well-formed, has . or .., and handle symbolic links
+	const char *pwd = mrsh_env_get(state, "PWD", NULL);
+	if (pwd == NULL || strlen(pwd) >= PATH_MAX) {
+		char cwd[PATH_MAX];
+		getcwd(cwd, PATH_MAX);
+		mrsh_env_set(state, "PWD", cwd, MRSH_VAR_ATTRIB_EXPORT);
+	}
+
+	mrsh_env_set(state, "OPTIND", "1", MRSH_VAR_ATTRIB_NONE);
+}
+
+static void source_file(struct mrsh_state *state, char *path) {
+	if (access(path, F_OK) == -1) {
+		return;
+	}
+	char *env_argv[] = { ".", path };
+	mrsh_run_builtin(state, sizeof(env_argv) / sizeof(env_argv[0]), env_argv);
+}
+
+void mrsh_source_profile(struct mrsh_state *state) {
+	source_file(state, "/etc/profile");
+
+	char path[PATH_MAX];
+	int n = snprintf(path, sizeof(path), "%s/.profile", getenv("HOME"));
+	if (n == sizeof(path)) {
+		fprintf(stderr, "Warning: $HOME/.profile is longer than PATH_MAX\n");
+		return;
+	}
+	source_file(state, path);
+}
+
+void mrsh_source_env(struct mrsh_state *state) {
+	char *path = getenv("ENV");
+	if (path == NULL) {
+		return;
+	}
+	// TODO: parameter expansion
+	source_file(state, path);
+}
-- 
2.20.1
Details
Message ID
<liQO61RzF79Dzs51PpvldZ5QPuizTJc63BGznn49hm9RH_M1hKBKMWz8Ri3p7cysC8TON2WkjUULtGDt_ig9fhnn2548zllI8FQz23ODuvo=@emersion.fr>
In-Reply-To
<20181227180533.24011-1-sir@cmpwn.com> (view parent)
Download raw message
Pushed:

To git.sr.ht:~emersion/mrsh
   da37e62..a217d07  master -> master

Thanks!
Details
Message ID
<oyrw5J77tYZ-twgfZtF2-X02UdbQnZfsL-d_OHNupiQQcKDluIoMDGwzd791KYc49sQOoB7uZocaUJS9nfcXT2Oq2JbA02ja2Ob-whOecNI=@emersion.fr>
In-Reply-To
<liQO61RzF79Dzs51PpvldZ5QPuizTJc63BGznn49hm9RH_M1hKBKMWz8Ri3p7cysC8TON2WkjUULtGDt_ig9fhnn2548zllI8FQz23ODuvo=@emersion.fr> (view parent)
Download raw message
Disregard that, I mixed things up with "Switch FreeBSD build to
freebsd/latest".

Patch does not apply anymore, can you rebase?

> +/** Sources $ENV. Note that this behavior is not specified by POSIX. It is
> + * recommended to source this file in interactive shells. */

This is specified by POSIX. See "2.5.3 Shell Variables".