Commit 0ee7065d8d7e2f4c85b8e806bda98645435f08ac

Stefan Sperling 2019-05-13T13:56:40

make 'got commit' look up EDITOR in PATH The previous code only worked if EDITOR contained an absolute path.

diff --git a/got/got.c b/got/got.c
index 0aeb627..23370a0 100644
--- a/got/got.c
+++ b/got/got.c
@@ -197,27 +197,33 @@ usage(void)
 }
 
 static const struct got_error *
-get_editor(char **editorp)
+get_editor(char **abspath)
 {
+	const struct got_error *err = NULL;
 	const char *editor;
 
 	editor = getenv("VISUAL");
 	if (editor == NULL)
 		editor = getenv("EDITOR");
-	if (editor == NULL)
-		editor = "/bin/ed";
 
-	*editorp = realpath(editor, NULL);
-	if (*editorp == NULL)
-		return got_error_from_errno("relpath");
+	if (editor) {
+		err = got_path_find_prog(abspath, editor);
+		if (err)
+			return err;
+	}
+
+	if (*abspath == NULL) {
+		*abspath = strdup("/bin/ed");
+		if (*abspath == NULL)
+			return got_error_from_errno("strdup");
+	}
 
 	return NULL;
 }
 
 static const struct got_error *
 apply_unveil(const char *repo_path, int repo_read_only,
-    const char *worktree_path, int create_worktree,
-    int unveil_editor)
+    const char *worktree_path, int create_worktree, char **editor)
 {
 	const struct got_error *err;
 	static char err_msg[MAXPATHLEN + 36];
@@ -243,17 +249,16 @@ apply_unveil(const char *repo_path, int repo_read_only,
 			return err;
 	}
 
-	if (unveil_editor) {
-		char *editor;
-		err = get_editor(&editor);
+	if (editor) {
+		err = get_editor(editor);
 		if (err)
 			return err;
-		if (unveil(editor, "x") != 0) {
-			err = got_error_from_errno2("unveil", editor);
-			free(editor);
+		if (unveil(*editor, "x") != 0) {
+			err = got_error_from_errno2("unveil", *editor);
+			free(*editor);
+			*editor = NULL;
 			return err;
 		}
-		free(editor);
 	}
 
 	if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
@@ -437,7 +442,8 @@ cmd_checkout(int argc, char *argv[])
 	if (error != NULL)
 		goto done;
 
-	error = apply_unveil(got_repo_get_path(repo), 0, worktree_path, 1, 0);
+	error = apply_unveil(got_repo_get_path(repo), 0, worktree_path, 1,
+	    NULL);
 	if (error)
 		goto done;
 
@@ -575,7 +581,7 @@ cmd_update(int argc, char *argv[])
 		goto done;
 
 	error = apply_unveil(got_repo_get_path(repo), 0,
-	    got_worktree_get_root_path(worktree), 0, 0);
+	    got_worktree_get_root_path(worktree), 0, NULL);
 	if (error)
 		goto done;
 
@@ -947,7 +953,7 @@ cmd_log(int argc, char *argv[])
 		goto done;
 
 	error = apply_unveil(got_repo_get_path(repo), 1,
-	    worktree ? got_worktree_get_root_path(worktree) : NULL, 0, 0);
+	    worktree ? got_worktree_get_root_path(worktree) : NULL, 0, NULL);
 	if (error)
 		goto done;
 
@@ -1209,7 +1215,7 @@ cmd_diff(int argc, char *argv[])
 		goto done;
 
 	error = apply_unveil(got_repo_get_path(repo), 1,
-	    worktree ? got_worktree_get_root_path(worktree) : NULL, 0, 0);
+	    worktree ? got_worktree_get_root_path(worktree) : NULL, 0, NULL);
 	if (error)
 		goto done;
 
@@ -1369,7 +1375,7 @@ cmd_blame(int argc, char *argv[])
 	if (error != NULL)
 		goto done;
 
-	error = apply_unveil(got_repo_get_path(repo), 1, NULL, 0, 0);
+	error = apply_unveil(got_repo_get_path(repo), 1, NULL, 0, NULL);
 	if (error)
 		goto done;
 
@@ -1597,7 +1603,7 @@ cmd_tree(int argc, char *argv[])
 	if (error != NULL)
 		goto done;
 
-	error = apply_unveil(got_repo_get_path(repo), 1, NULL, 0, 0);
+	error = apply_unveil(got_repo_get_path(repo), 1, NULL, 0, NULL);
 	if (error)
 		goto done;
 
@@ -1726,7 +1732,7 @@ cmd_status(int argc, char *argv[])
 		goto done;
 
 	error = apply_unveil(got_repo_get_path(repo), 1,
-	    got_worktree_get_root_path(worktree), 0, 0);
+	    got_worktree_get_root_path(worktree), 0, NULL);
 	if (error)
 		goto done;
 
@@ -1897,7 +1903,7 @@ cmd_ref(int argc, char *argv[])
 		goto done;
 
 	error = apply_unveil(got_repo_get_path(repo), do_list,
-	    worktree ? got_worktree_get_root_path(worktree) : NULL, 0, 0);
+	    worktree ? got_worktree_get_root_path(worktree) : NULL, 0, NULL);
 	if (error)
 		goto done;
 
@@ -1976,7 +1982,7 @@ cmd_add(int argc, char *argv[])
 		goto done;
 
 	error = apply_unveil(got_repo_get_path(repo), 1,
-	    got_worktree_get_root_path(worktree), 0, 0);
+	    got_worktree_get_root_path(worktree), 0, NULL);
 	if (error)
 		goto done;
 
@@ -2062,7 +2068,7 @@ cmd_rm(int argc, char *argv[])
 		goto done;
 
 	error = apply_unveil(got_repo_get_path(repo), 1,
-	    got_worktree_get_root_path(worktree), 0, 0);
+	    got_worktree_get_root_path(worktree), 0, NULL);
 	if (error)
 		goto done;
 
@@ -2139,7 +2145,7 @@ cmd_revert(int argc, char *argv[])
 		goto done;
 
 	error = apply_unveil(got_repo_get_path(repo), 1,
-	    got_worktree_get_root_path(worktree), 0, 0);
+	    got_worktree_get_root_path(worktree), 0, NULL);
 	if (error)
 		goto done;
 
@@ -2366,13 +2372,10 @@ cmd_commit(int argc, char *argv[])
 		goto done;
 
 	error = apply_unveil(got_repo_get_path(repo), 0,
-	    got_worktree_get_root_path(worktree), 0, 1);
+	    got_worktree_get_root_path(worktree), 0, &editor);
 	if (error)
 		goto done;
 
-	error = get_editor(&editor);
-	if (error)
-		goto done;
 	cl_arg.editor = editor;
 	cl_arg.cmdline_log = logmsg;
 	cl_arg.worktree_path = got_worktree_get_root_path(worktree);
diff --git a/include/got_path.h b/include/got_path.h
index c7b7e0f..999fdf6 100644
--- a/include/got_path.h
+++ b/include/got_path.h
@@ -98,3 +98,6 @@ const struct got_error *got_path_dirname(char **, const char *);
 
 /* Strip trailing slashes from a path; path will be modified in-place. */
 void got_path_strip_trailing_slashes(char *);
+
+/* Look up the absolute path of a program in $PATH */
+const struct got_error *got_path_find_prog(char **, const char *);
diff --git a/lib/path.c b/lib/path.c
index 90f647a..fa5d77b 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2018, 2019 Stefan Sperling <stsp@openbsd.org>
  * Copyright (c) 2015 Theo de Raadt <deraadt@openbsd.org>
+ * Copyright (c) 1997 Todd C. Miller <millert@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -26,6 +27,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <dirent.h>
+#include <paths.h>
 
 #include "got_error.h"
 #include "got_path.h"
@@ -377,3 +379,57 @@ got_path_strip_trailing_slashes(char *path)
 	while (path[x = strlen(path) - 1] == '/')
 		path[x] = '\0';
 }
+
+const struct got_error *
+got_path_find_prog(char **filename, const char *prog)
+{
+	char *p;
+	int len;
+	struct stat sbuf;
+	char *path, *pathcpy;
+
+	*filename = NULL;
+
+	path = getenv("PATH");
+	if (path == NULL)
+		path = _PATH_DEFPATH;
+
+	/* Special case if prog contains '/' */
+	if (strchr(prog, '/')) {
+		if ((stat(prog, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
+		    access(prog, X_OK) == 0) {
+			*filename = strdup(prog);
+			if (*filename == NULL)
+				return got_error_from_errno("strdup");
+		}
+		return NULL;
+	}
+
+	if ((path = strdup(path)) == NULL)
+		return got_error_from_errno("strdup");
+	pathcpy = path;
+
+	while ((p = strsep(&pathcpy, ":")) != NULL) {
+		if (*p == '\0')
+			p = ".";
+
+		len = strlen(p);
+		while (len > 0 && p[len-1] == '/')
+			p[--len] = '\0';	/* strip trailing '/' */
+
+		if (asprintf(filename, "%s/%s", p, prog) == -1) {
+			free(path);
+			return got_error_from_errno("asprintf");
+		}
+		if ((stat(*filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
+		    access(*filename, X_OK) == 0) {
+			free(path);
+			return NULL;
+		}
+		free(*filename);
+		*filename = NULL;
+		continue;
+	}
+	free(path);
+	return NULL;
+}