Commit Diff


commit - 8641a33271eb0cef324fbeb4440485de8f433134
commit + 557d33657977f5752a77f1ac50dc51aaab381f41
blob - 8afaab18b7d71f4b1b6d555d34c39d3d3df20166
blob + ca273f088abf440f2971a8633e52b3f2fe3bb8c9
--- tog/tog.c
+++ tog/tog.c
@@ -620,7 +620,8 @@ struct tog_io {
 	FILE	*cin;
 	FILE	*cout;
 	FILE	*f;
-};
+} tog_io;
+static int using_mock_io;
 
 #define TOG_SCREEN_DUMP		"SCREENDUMP"
 #define TOG_SCREEN_DUMP_LEN	(sizeof(TOG_SCREEN_DUMP) - 1)
@@ -1337,7 +1338,7 @@ view_search_start(struct tog_view *view, int fast_refr
 	cbreak();
 	noecho();
 	nodelay(v->window, TRUE);
-	if (!fast_refresh)
+	if (!fast_refresh && !using_mock_io)
 		halfdelay(10);
 	if (ret == ERR)
 		return NULL;
@@ -1661,7 +1662,7 @@ done:
 
 static const struct got_error *
 view_input(struct tog_view **new, int *done, struct tog_view *view,
-    struct tog_view_list_head *views, struct tog_io *tog_io, int fast_refresh)
+    struct tog_view_list_head *views, int fast_refresh)
 {
 	const struct got_error *err = NULL;
 	struct tog_view *v;
@@ -1698,8 +1699,8 @@ view_input(struct tog_view **new, int *done, struct to
 	if (errcode)
 		return got_error_set_errno(errcode, "pthread_mutex_unlock");
 
-	if (tog_io && tog_io->f) {
-		err = tog_read_script_key(tog_io->f, &ch, done);
+	if (using_mock_io) {
+		err = tog_read_script_key(tog_io.f, &ch, done);
 		if (err)
 			return err;
 	} else if (view->count && --view->count) {
@@ -1922,24 +1923,22 @@ view_needs_focus_indication(struct tog_view *view)
 }
 
 static const struct got_error *
-tog_io_close(struct tog_io *tog_io)
+tog_io_close(void)
 {
 	const struct got_error *err = NULL;
 
-	if (tog_io->cin && fclose(tog_io->cin) == EOF)
-		err = got_ferror(tog_io->cin, GOT_ERR_IO);
-	if (tog_io->cout && fclose(tog_io->cout) == EOF && err == NULL)
-		err = got_ferror(tog_io->cout, GOT_ERR_IO);
-	if (tog_io->f && fclose(tog_io->f) == EOF && err == NULL)
-		err = got_ferror(tog_io->f, GOT_ERR_IO);
-	free(tog_io);
-	tog_io = NULL;
+	if (tog_io.cin && fclose(tog_io.cin) == EOF)
+		err = got_ferror(tog_io.cin, GOT_ERR_IO);
+	if (tog_io.cout && fclose(tog_io.cout) == EOF && err == NULL)
+		err = got_ferror(tog_io.cout, GOT_ERR_IO);
+	if (tog_io.f && fclose(tog_io.f) == EOF && err == NULL)
+		err = got_ferror(tog_io.f, GOT_ERR_IO);
 
 	return err;
 }
 
 static const struct got_error *
-view_loop(struct tog_view *view, struct tog_io *tog_io)
+view_loop(struct tog_view *view)
 {
 	const struct got_error *err = NULL;
 	struct tog_view_list_head views;
@@ -1970,11 +1969,10 @@ view_loop(struct tog_view *view, struct tog_io *tog_io
 	while (!TAILQ_EMPTY(&views) && !done && !tog_thread_error &&
 	    !tog_fatal_signal_received()) {
 		/* Refresh fast during initialization, then become slower. */
-		if (fast_refresh && --fast_refresh == 0)
+		if (fast_refresh && --fast_refresh == 0 && !using_mock_io)
 			halfdelay(10); /* switch to once per second */
 
-		err = view_input(&new_view, &done, view, &views, tog_io,
-		    fast_refresh);
+		err = view_input(&new_view, &done, view, &views, fast_refresh);
 		if (err)
 			break;
 
@@ -2718,7 +2716,7 @@ draw_commits(struct tog_view *view)
 		}
 	}
 
-	if (s->thread_args.commits_needed == 0)
+	if (s->thread_args.commits_needed == 0 && !using_mock_io)
 		halfdelay(10); /* disable fast refresh */
 
 	if (s->thread_args.commits_needed > 0 || s->thread_args.load_all) {
@@ -2893,7 +2891,8 @@ trigger_log_thread(struct tog_view *view, int wait)
 	struct tog_log_thread_args *ta = &view->state.log.thread_args;
 	int errcode;
 
-	halfdelay(1); /* fast refresh while loading commits */
+	if (!using_mock_io)
+		halfdelay(1); /* fast refresh while loading commits */
 
 	while (!ta->log_complete && !tog_thread_error &&
 	    (ta->commits_needed > 0 || ta->load_all)) {
@@ -4162,42 +4161,34 @@ apply_unveil(const char *repo_path, const char *worktr
 }
 
 static const struct got_error *
-init_mock_term(struct tog_io **tog_io, const char *test_script_path)
+init_mock_term(const char *test_script_path)
 {
 	const struct got_error	*err = NULL;
-	struct tog_io		*io;
 
-	if (*tog_io)
-		*tog_io = NULL;
-
 	if (test_script_path == NULL || *test_script_path == '\0')
 		return got_error_msg(GOT_ERR_IO, "GOT_TOG_TEST not defined");
 
-	io = calloc(1, sizeof(*io));
-	if (io == NULL)
-		return got_error_from_errno("calloc");
-
-	io->f = fopen(test_script_path, "re");
-	if (io->f == NULL) {
+	tog_io.f = fopen(test_script_path, "re");
+	if (tog_io.f == NULL) {
 		err = got_error_from_errno_fmt("fopen: %s",
 		    test_script_path);
 		goto done;
 	}
 
 	/* test mode, we don't want any output */
-	io->cout = fopen("/dev/null", "w+");
-	if (io->cout == NULL) {
+	tog_io.cout = fopen("/dev/null", "w+");
+	if (tog_io.cout == NULL) {
 		err = got_error_from_errno("fopen: /dev/null");
 		goto done;
 	}
 
-	io->cin = fopen("/dev/tty", "r+");
-	if (io->cin == NULL) {
+	tog_io.cin = fopen("/dev/tty", "r+");
+	if (tog_io.cin == NULL) {
 		err = got_error_from_errno("fopen: /dev/tty");
 		goto done;
 	}
 
-	if (fseeko(io->f, 0L, SEEK_SET) == -1) {
+	if (fseeko(tog_io.f, 0L, SEEK_SET) == -1) {
 		err = got_error_from_errno("fseeko");
 		goto done;
 	}
@@ -4206,23 +4197,20 @@ init_mock_term(struct tog_io **tog_io, const char *tes
 	 * XXX Perhaps we should define "xterm" as the terminal
 	 * type for standardised testing instead of using $TERM?
 	 */
-	if (newterm(NULL, io->cout, io->cin) == NULL)
+	if (newterm(NULL, tog_io.cout, tog_io.cin) == NULL)
 		err = got_error_msg(GOT_ERR_IO,
 		    "newterm: failed to initialise curses");
+
+	using_mock_io = 1;
 done:
 	if (err)
-		tog_io_close(io);
-	else
-		*tog_io = io;
+		tog_io_close();
 	return err;
 }
 
-static const struct got_error *
-init_curses(struct tog_io **tog_io)
+static void
+init_curses(void)
 {
-	const struct got_error	*err = NULL;
-	const char		*test_script_path;
-
 	/*
 	 * Override default signal handlers before starting ncurses.
 	 * This should prevent ncurses from installing its own
@@ -4234,16 +4222,13 @@ init_curses(struct tog_io **tog_io)
 	signal(SIGINT, tog_sigint);
 	signal(SIGTERM, tog_sigterm);
 
-	test_script_path = getenv("GOT_TOG_TEST");
-	if (test_script_path != NULL) {
-		err = init_mock_term(tog_io, test_script_path);
-		if (err)
-			return err;
-	} else
-		initscr();
+	if (using_mock_io) /* In test mode we use a fake terminal */
+		return;
 
+	initscr();
+
 	cbreak();
-	halfdelay(1); /* Do fast refresh while initial view is loading. */
+	halfdelay(1); /* Fast refresh while initial view is loading. */
 	noecho();
 	nonl();
 	intrflush(stdscr, FALSE);
@@ -4254,7 +4239,7 @@ init_curses(struct tog_io **tog_io)
 		use_default_colors();
 	}
 
-	return NULL;
+	return;
 }
 
 static const struct got_error *
@@ -4303,7 +4288,6 @@ cmd_log(int argc, char *argv[])
 	const char *head_ref_name = NULL;
 	int ch, log_branches = 0;
 	struct tog_view *view;
-	struct tog_io *tog_io = NULL;
 	int *pack_fds = NULL;
 
 	while ((ch = getopt(argc, argv, "bc:r:")) != -1) {
@@ -4363,9 +4347,7 @@ cmd_log(int argc, char *argv[])
 	if (error)
 		goto done;
 
-	error = init_curses(&tog_io);
-	if (error)
-		goto done;
+	init_curses();
 
 	error = apply_unveil(got_repo_get_path(repo),
 	    worktree ? got_worktree_get_root_path(worktree) : NULL);
@@ -4412,7 +4394,7 @@ cmd_log(int argc, char *argv[])
 		got_worktree_close(worktree);
 		worktree = NULL;
 	}
-	error = view_loop(view, tog_io);
+	error = view_loop(view);
 done:
 	free(in_repo_path);
 	free(repo_path);
@@ -4434,8 +4416,8 @@ done:
 		if (error == NULL)
 			error = pack_err;
 	}
-	if (tog_io != NULL) {
-		io_err = tog_io_close(tog_io);
+	if (using_mock_io) {
+		io_err = tog_io_close();
 		if (error == NULL)
 			error = io_err;
 	}
@@ -5415,7 +5397,7 @@ open_diff_view(struct tog_view *view, struct got_objec
 	s->parent_view = parent_view;
 	s->repo = repo;
 
-	if (has_colors() && getenv("TOG_COLORS") != NULL) {
+	if (has_colors() && getenv("TOG_COLORS") != NULL && !using_mock_io) {
 		int rc;
 
 		rc = init_pair(GOT_DIFF_LINE_MINUS,
@@ -5819,7 +5801,6 @@ cmd_diff(int argc, char *argv[])
 	int ch, force_text_diff = 0;
 	const char *errstr;
 	struct tog_view *view;
-	struct tog_io *tog_io = NULL;
 	int *pack_fds = NULL;
 
 	while ((ch = getopt(argc, argv, "aC:r:w")) != -1) {
@@ -5887,9 +5868,7 @@ cmd_diff(int argc, char *argv[])
 	if (error)
 		goto done;
 
-	error = init_curses(&tog_io);
-	if (error)
-		goto done;
+	init_curses();
 
 	error = apply_unveil(got_repo_get_path(repo), NULL);
 	if (error)
@@ -5918,7 +5897,7 @@ cmd_diff(int argc, char *argv[])
 	    ignore_whitespace, force_text_diff, NULL,  repo);
 	if (error)
 		goto done;
-	error = view_loop(view, tog_io);
+	error = view_loop(view);
 done:
 	free(label1);
 	free(label2);
@@ -5937,8 +5916,8 @@ done:
 		if (error == NULL)
 			error = pack_err;
 	}
-	if (tog_io != NULL) {
-		io_err = tog_io_close(tog_io);
+	if (using_mock_io) {
+		io_err = tog_io_close();
 		if (error == NULL)
 			error = io_err;
 	}
@@ -6574,10 +6553,11 @@ show_blame_view(struct tog_view *view)
 		if (errcode)
 			return got_error_set_errno(errcode, "pthread_create");
 
-		halfdelay(1); /* fast refresh while annotating  */
+		if (!using_mock_io)
+			halfdelay(1); /* fast refresh while annotating  */
 	}
 
-	if (s->blame_complete)
+	if (s->blame_complete && !using_mock_io)
 		halfdelay(10); /* disable fast refresh */
 
 	err = draw_blame(view);
@@ -6925,7 +6905,6 @@ cmd_blame(int argc, char *argv[])
 	char *commit_id_str = NULL;
 	int ch;
 	struct tog_view *view;
-	struct tog_io *tog_io = NULL;
 	int *pack_fds = NULL;
 
 	while ((ch = getopt(argc, argv, "c:r:")) != -1) {
@@ -6982,9 +6961,7 @@ cmd_blame(int argc, char *argv[])
 	if (error)
 		goto done;
 
-	error = init_curses(&tog_io);
-	if (error)
-		goto done;
+	init_curses();
 
 	error = apply_unveil(got_repo_get_path(repo), NULL);
 	if (error)
@@ -7033,7 +7010,7 @@ cmd_blame(int argc, char *argv[])
 		got_worktree_close(worktree);
 		worktree = NULL;
 	}
-	error = view_loop(view, tog_io);
+	error = view_loop(view);
 done:
 	free(repo_path);
 	free(in_repo_path);
@@ -7055,8 +7032,8 @@ done:
 		if (error == NULL)
 			error = pack_err;
 	}
-	if (tog_io != NULL) {
-		io_err = tog_io_close(tog_io);
+	if (using_mock_io) {
+		io_err = tog_io_close();
 		if (error == NULL)
 			error = io_err;
 	}
@@ -7904,7 +7881,6 @@ cmd_tree(int argc, char *argv[])
 	const char *head_ref_name = NULL;
 	int ch;
 	struct tog_view *view;
-	struct tog_io *tog_io = NULL;
 	int *pack_fds = NULL;
 
 	while ((ch = getopt(argc, argv, "c:r:")) != -1) {
@@ -7961,9 +7937,7 @@ cmd_tree(int argc, char *argv[])
 	if (error)
 		goto done;
 
-	error = init_curses(&tog_io);
-	if (error)
-		goto done;
+	init_curses();
 
 	error = apply_unveil(got_repo_get_path(repo), NULL);
 	if (error)
@@ -8016,7 +7990,7 @@ cmd_tree(int argc, char *argv[])
 		got_worktree_close(worktree);
 		worktree = NULL;
 	}
-	error = view_loop(view, tog_io);
+	error = view_loop(view);
 done:
 	free(repo_path);
 	free(cwd);
@@ -8035,8 +8009,8 @@ done:
 		if (error == NULL)
 			error = pack_err;
 	}
-	if (tog_io != NULL) {
-		io_err = tog_io_close(tog_io);
+	if (using_mock_io) {
+		io_err = tog_io_close();
 		if (error == NULL)
 			error = io_err;
 	}
@@ -8793,7 +8767,6 @@ cmd_ref(int argc, char *argv[])
 	char *cwd = NULL, *repo_path = NULL;
 	int ch;
 	struct tog_view *view;
-	struct tog_io *tog_io = NULL;
 	int *pack_fds = NULL;
 
 	while ((ch = getopt(argc, argv, "r:")) != -1) {
@@ -8842,9 +8815,7 @@ cmd_ref(int argc, char *argv[])
 	if (error != NULL)
 		goto done;
 
-	error = init_curses(&tog_io);
-	if (error)
-		goto done;
+	init_curses();
 
 	error = apply_unveil(got_repo_get_path(repo), NULL);
 	if (error)
@@ -8869,7 +8840,7 @@ cmd_ref(int argc, char *argv[])
 		got_worktree_close(worktree);
 		worktree = NULL;
 	}
-	error = view_loop(view, tog_io);
+	error = view_loop(view);
 done:
 	free(repo_path);
 	free(cwd);
@@ -8884,8 +8855,8 @@ done:
 		if (error == NULL)
 			error = pack_err;
 	}
-	if (tog_io != NULL) {
-		io_err = tog_io_close(tog_io);
+	if (using_mock_io) {
+		io_err = tog_io_close();
 		if (error == NULL)
 			error = io_err;
 	}
@@ -9764,10 +9735,24 @@ main(int argc, char *argv[])
 	    { NULL, 0, NULL, 0}
 	};
 	char *diff_algo_str = NULL;
+	const char *test_script_path;
 
 	setlocale(LC_CTYPE, "");
 
-#if !defined(PROFILE) && !defined(TOG_REGRESS)
+	/*
+	 * Test mode init must happen before pledge() because "tty" will
+	 * not allow TTY-related ioctls to occur via regular files.
+	 */
+	test_script_path = getenv("GOT_TOG_TEST");
+	if (test_script_path != NULL) {
+		error = init_mock_term(test_script_path);
+		if (error) {
+			fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
+			return 1;
+		}
+	}
+
+#if !defined(PROFILE)
 	if (pledge("stdio rpath wpath cpath flock proc tty exec sendfd unveil",
 	    NULL) == -1)
 		err(1, "pledge");