示例#1
0
文件: refdb.c 项目: ChunHungLiu/tig
void
ref_update_env(struct argv_env *env, const struct ref *ref, bool recurse)
{
	bool clear = recurse ? !ref->next : true;

	if (recurse && ref->next)
		ref_update_env(env, ref->next, true);

	if (clear)
		env->tag[0] = env->remote[0] = env->branch[0] = 0;

	string_copy_rev(env->commit, ref->id);

	if (ref_is_tag(ref)) {
		string_ncopy(env->tag, ref->name, strlen(ref->name));

	} else if (ref_is_remote(ref)) {
		const char *sep = strchr(ref->name, '/');

		if (!sep)
			return;
		string_ncopy(env->remote, ref->name, sep - ref->name);
		string_ncopy(env->branch, sep + 1, strlen(sep + 1));

	} else if (ref->type == REFERENCE_BRANCH || ref->type == REFERENCE_HEAD) {
		string_ncopy(env->branch, ref->name, strlen(ref->name));
	}
}
示例#2
0
文件: blame.c 项目: peff/tig
static void
blame_go_forward(struct view *view, struct blame *blame, bool parent)
{
	struct blame_state *state = view->private;
	struct blame_history_state *history_state = &state->history_state;
	struct blame_commit *commit = blame->commit;
	const char *id = parent ? commit->parent_id : commit->id;
	const char *filename = parent ? commit->parent_filename : commit->filename;

	if (!*id && parent) {
		report("The selected commit has no parents");
		return;
	}

	if (!strcmp(history_state->id, id) && !strcmp(history_state->filename, filename)) {
		report("The selected commit is already displayed");
		return;
	}

	if (!push_view_history_state(&blame_view_history, &view->pos, history_state)) {
		report("Failed to save current view state");
		return;
	}

	string_ncopy(view->env->ref, id, sizeof(commit->id));
	string_ncopy(view->env->file, filename, strlen(filename));
	if (parent)
		setup_blame_parent_line(view, blame);
	view->env->lineno = blame->lineno;
	reload_view(view);
}
示例#3
0
文件: keys.c 项目: bbolli/tig
enum status_code
add_keybinding(struct keymap *table, enum request request, struct key_input *input)
{
	char buf[SIZEOF_STR];
	bool conflict = FALSE;
	size_t i;

	for (i = 0; i < table->size; i++) {
		if (keybinding_equals(&table->data[i].input, input, &conflict)) {
			enum request old_request = table->data[i].request;
			const char *old_name;

			table->data[i].request = request;
			if (!conflict)
				return SUCCESS;

			old_name = get_request_name(old_request);
			string_ncopy(buf, old_name, strlen(old_name));
			return error("Key binding for %s and %s conflict; "
				     "keys using Ctrl are case insensitive",
				     buf, get_request_name(request));
		}
	}

	table->data = realloc(table->data, (table->size + 1) * sizeof(*table->data));
	if (!table->data)
		die("Failed to allocate keybinding");
	table->data[table->size].input = *input;
	table->data[table->size++].request = request;
	return SUCCESS;
}
示例#4
0
文件: display.c 项目: JakeSc/tig
void
open_editor(const char *file, unsigned int lineno)
{
    const char *editor_argv[SIZEOF_ARG + 3] = { "vi", file, NULL };
    char editor_cmd[SIZEOF_STR];
    char lineno_cmd[SIZEOF_STR];
    const char *editor;
    int argc = 0;

    editor = getenv("GIT_EDITOR");
    if (!editor && *opt_editor)
        editor = opt_editor;
    if (!editor)
        editor = getenv("VISUAL");
    if (!editor)
        editor = getenv("EDITOR");
    if (!editor)
        editor = "vi";

    string_ncopy(editor_cmd, editor, strlen(editor));
    if (!argv_from_string_no_quotes(editor_argv, &argc, editor_cmd)) {
        report("Failed to read editor command");
        return;
    }

    if (lineno && opt_editor_line_number && string_format(lineno_cmd, "+%u", lineno))
        editor_argv[argc++] = lineno_cmd;
    editor_argv[argc] = file;
    if (!open_external_viewer(editor_argv, repo.cdup, FALSE, TRUE, EDITOR_LINENO_MSG))
        opt_editor_line_number = FALSE;
}
示例#5
0
static enum status_code
read_repo_info(char *name, size_t namelen, char *value, size_t valuelen, void *data)
{
    struct repo_info_state *state = data;
    const char *arg = *state->argv ? *state->argv++ : "";

    if (!strcmp(arg, REPO_INFO_GIT_DIR)) {
        string_ncopy(repo.git_dir, name, namelen);

    } else if (!strcmp(arg, REPO_INFO_WORK_TREE)) {
        /* This can be 3 different values depending on the
         * version of git being used. If git-rev-parse does not
         * understand --is-inside-work-tree it will simply echo
         * the option else either "true" or "false" is printed.
         * Default to true for the unknown case. */
        repo.is_inside_work_tree = strcmp(name, "false") ? true : false;

    } else if (!strcmp(arg, REPO_INFO_SHOW_CDUP)) {
        string_ncopy(repo.cdup, name, namelen);

    } else if (!strcmp(arg, REPO_INFO_SHOW_PREFIX)) {
        /* Some versions of Git does not emit anything for --show-prefix
         * when the user is in the repository root directory. Try to detect
         * this special case by looking at the emitted value. If it looks
         * like a commit ID and there's no cdup path assume that no value
         * was emitted. */
        if (!*repo.cdup && namelen == 40 && iscommit(name))
            return read_repo_info(name, namelen, value, valuelen, data);

        string_ncopy(repo.prefix, name, namelen);

    } else if (!strcmp(arg, REPO_INFO_RESOLVED_HEAD)) {
        string_ncopy(repo.head_id, name, namelen);

    } else if (!strcmp(arg, REPO_INFO_SYMBOLIC_HEAD)) {
        if (!prefixcmp(name, "refs/heads/")) {
            const char *head = name + STRING_SIZE("refs/heads/");

            string_ncopy(repo.head, head, strlen(head) + 1);
            add_ref(repo.head_id, name, repo.remote, repo.head);
        }
        state->argv++;
    }

    return SUCCESS;
}
示例#6
0
文件: options.c 项目: bbolli/tig
const char *
diff_context_arg()
{
	static char opt_diff_context_arg[9]	= "";

	if (!string_format(opt_diff_context_arg, "-U%u", opt_diff_context))
		string_ncopy(opt_diff_context_arg, "-U3", 3);

	return opt_diff_context_arg;
}
示例#7
0
文件: blame.c 项目: peff/tig
static void
blame_select(struct view *view, struct line *line)
{
	struct blame *blame = line->data;
	struct blame_commit *commit = blame->commit;

	if (!commit)
		return;

	if (string_rev_is_null(commit->id))
		string_ncopy(view->env->commit, "HEAD", 4);
	else
		string_copy_rev(view->env->commit, commit->id);
}
示例#8
0
文件: blame.c 项目: peff/tig
static void
blame_go_back(struct view *view)
{
	struct blame_history_state history_state;

	if (!pop_view_history_state(&blame_view_history, &view->pos, &history_state)) {
		report("Already at start of history");
		return;
	}

	string_copy(view->env->ref, history_state.id);
	string_ncopy(view->env->file, history_state.filename, strlen(history_state.filename));
	view->env->lineno = view->pos.lineno;
	reload_view(view);
}
示例#9
0
文件: options.c 项目: jlsandell/tig
static void
set_remote_branch(const char *name, const char *value, size_t valuelen)
{
	if (!strcmp(name, ".remote")) {
		string_ncopy(repo.remote, value, valuelen);

	} else if (*repo.remote && !strcmp(name, ".merge")) {
		size_t from = strlen(repo.remote);

		if (!prefixcmp(value, "refs/heads/"))
			value += STRING_SIZE("refs/heads/");

		if (!string_format_from(repo.remote, &from, "/%s", value))
			repo.remote[0] = 0;
	}
}
示例#10
0
文件: options.c 项目: MarkTseng/tig
void
update_options_from_argv(const char *argv[])
{
	int next, flags_pos;

	for (next = flags_pos = 0; argv[next]; next++) {
		const char *flag = argv[next];
		int value = -1;

		if (map_enum(&value, commit_order_arg_map, flag)) {
			opt_commit_order = value;
			mark_option_seen(&opt_commit_order);
			continue;
		}

		if (map_enum(&value, ignore_space_arg_map, flag)) {
			opt_ignore_space = value;
			mark_option_seen(&opt_ignore_space);
			continue;
		}

		if (!strcmp(flag, "--no-notes")) {
			opt_show_notes = FALSE;
			mark_option_seen(&opt_show_notes);
			continue;
		}

		if (!prefixcmp(flag, "--show-notes") ||
		    !prefixcmp(flag, "--notes")) {
			opt_show_notes = TRUE;
			string_ncopy(opt_notes_arg, flag, strlen(flag));
			mark_option_seen(&opt_show_notes);
			continue;
		}

		if (!prefixcmp(flag, "-U")
		    && parse_int(&value, flag + 2, 0, 999999) == SUCCESS) {
			opt_diff_context = value;
			mark_option_seen(&opt_diff_context);
			continue;
		}

		argv[flags_pos++] = flag;
	}

	argv[flags_pos] = NULL;
}
示例#11
0
文件: parse.c 项目: Avinash-Bhat/tig
bool
parse_blame_header(struct blame_header *header, const char *text, size_t max_lineno)
{
	const char *pos = text + SIZEOF_REV - 2;

	if (strlen(text) <= SIZEOF_REV || pos[1] != ' ')
		return FALSE;

	string_ncopy(header->id, text, SIZEOF_REV);

	if (!parse_number(&pos, &header->orig_lineno, 1, 9999999) ||
	    !parse_number(&pos, &header->lineno, 1, max_lineno) ||
	    !parse_number(&pos, &header->group, 1, max_lineno - header->lineno + 1))
		return FALSE;

	return TRUE;
}
示例#12
0
文件: parse.c 项目: Avinash-Bhat/tig
bool
parse_blame_info(struct blame_commit *commit, char author[SIZEOF_STR], char *line)
{
	if (match_blame_header("author ", &line)) {
		string_ncopy_do(author, SIZEOF_STR, line, strlen(line));

	} else if (match_blame_header("author-mail ", &line)) {
		char *end = strchr(line, '>');

		if (end)
			*end = 0;
		if (*line == '<')
			line++;
		commit->author = get_author(author, line);
		author[0] = 0;

	} else if (match_blame_header("author-time ", &line)) {
		parse_timesec(&commit->time, line);

	} else if (match_blame_header("author-tz ", &line)) {
		parse_timezone(&commit->time, line);

	} else if (match_blame_header("summary ", &line)) {
		string_ncopy(commit->title, line, strlen(line));

	} else if (match_blame_header("previous ", &line)) {
		if (strlen(line) <= SIZEOF_REV)
			return FALSE;
		string_copy_rev(commit->parent_id, line);
		line += SIZEOF_REV;
		commit->parent_filename = get_path(line);
		if (!commit->parent_filename)
			return TRUE;

	} else if (match_blame_header("filename ", &line)) {
		commit->filename = get_path(line);
		return TRUE;
	}

	return FALSE;
}
示例#13
0
文件: options.c 项目: MarkTseng/tig
static int
read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen, void *data)
{
	if (!strcmp(name, "i18n.commitencoding"))
		set_encoding(&default_encoding, value, FALSE);

	else if (!strcmp(name, "gui.encoding"))
		set_encoding(&default_encoding, value, TRUE);

	else if (!strcmp(name, "core.editor"))
		string_ncopy(opt_editor, value, valuelen);

	else if (!strcmp(name, "core.worktree"))
		set_work_tree(value);

	else if (!strcmp(name, "core.abbrev"))
		parse_int(&opt_id_width, value, 0, SIZEOF_REV - 1);

	else if (!prefixcmp(name, "tig.color."))
		set_repo_config_option(name + 10, value, option_color_command);

	else if (!prefixcmp(name, "tig.bind."))
		set_repo_config_option(name + 9, value, option_bind_command);

	else if (!prefixcmp(name, "tig."))
		set_repo_config_option(name + 4, value, option_set_command);

	else if (!prefixcmp(name, "color."))
		set_git_color_option(name + STRING_SIZE("color."), value);

	else if (*repo.head && !prefixcmp(name, "branch.") &&
		 !strncmp(name + 7, repo.head, strlen(repo.head)))
		set_remote_branch(name + 7 + strlen(repo.head), value, valuelen);

	else if (!strcmp(name, "diff.context")) {
		if (!find_option_info_by_value(&opt_diff_context)->seen)
			opt_diff_context = -atoi(value);
	}

	return OK;
}
示例#14
0
文件: file.c 项目: DongXiao1983/uva
static void __set_input_filename (const char *fileName)
{
	const char*  head = fileName;
	const char* tail = file_get_basename (head);

	if (g_file.name != NULL)
		string_delete(g_file.name);
    
	g_file.name = string_new_init(fileName);

	if (g_file.path != NULL)
		string_delete (g_file.path);
    
	if (tail == head){
		g_file.path = NULL;
    }
	else{
		const size_t length = tail - head - 1;
		g_file.path = string_new();
		string_ncopy(g_file.path, fileName, length);
	}
}
示例#15
0
文件: blame.c 项目: peff/tig
static struct blame_commit *
get_blame_commit(struct view *view, const char *id)
{
	size_t i;

	for (i = 0; i < view->lines; i++) {
		struct blame *blame = view->line[i].data;

		if (!blame->commit)
			continue;

		if (!strncmp(blame->commit->id, id, SIZEOF_REV - 1))
			return blame->commit;
	}

	{
		struct blame_commit *commit = calloc(1, sizeof(*commit));

		if (commit)
			string_ncopy(commit->id, id, SIZEOF_REV);
		return commit;
	}
}
示例#16
0
文件: refdb.c 项目: JakeSc/tig
void
ref_update_env(struct argv_env *env, const struct ref *ref, bool clear)
{
	if (clear)
		env->tag[0] = env->remote[0] = env->branch[0] = 0;

	string_copy_rev(env->commit, ref->id);

	if (ref_is_tag(ref)) {
		string_copy_rev(env->tag, ref->name);

	} else if (ref_is_remote(ref)) {
		const char *sep = strchr(ref->name, '/');

		if (!sep)
			return;
		string_ncopy(env->remote, ref->name, sep - ref->name);
		string_copy_rev(env->branch, sep + 1);

	} else if (ref->type == REFERENCE_BRANCH) {
		string_copy_rev(env->branch, ref->name);
	}
}
示例#17
0
文件: keys.c 项目: acklinr/tig
enum status_code
add_keybinding(struct keymap *table, enum request request,
	       const struct key key[], size_t keys)
{
	struct keybinding *keybinding;
	char buf[SIZEOF_STR];
	bool conflict = false;
	size_t i;

	for (i = 0; i < table->size; i++) {
		if (keybinding_equals(table->data[i], key, keys, &conflict)) {
			enum request old_request = table->data[i]->request;
			const char *old_name;

			table->data[i]->request = request;
			if (!conflict)
				return SUCCESS;

			old_name = get_request_name(old_request);
			string_ncopy(buf, old_name, strlen(old_name));
			return error("Key binding for %s and %s conflict; "
				     "keys using Ctrl are case insensitive",
				     buf, get_request_name(request));
		}
	}

	table->data = realloc(table->data, (table->size + 1) * sizeof(*table->data));
	keybinding = calloc(1, sizeof(*keybinding) + (sizeof(*key) * (keys - 1)));
	if (!table->data || !keybinding)
		die("Failed to allocate keybinding");

	memcpy(keybinding->key, key, sizeof(*key) * keys);
	keybinding->keys = keys;
	keybinding->request = request;
	table->data[table->size++] = keybinding;
	return SUCCESS;
}
示例#18
0
文件: prompt.c 项目: bbolli/tig
enum request
run_prompt_command(struct view *view, const char *argv[])
{
	enum request request;
	const char *cmd = argv[0];
	size_t cmdlen = cmd ? strlen(cmd) : 0;

	if (!cmd)
		return REQ_NONE;

	if (string_isnumber(cmd)) {
		int lineno = view->pos.lineno + 1;

		if (parse_int(&lineno, cmd, 1, view->lines + 1) == SUCCESS) {
			select_view_line(view, lineno - 1);
			report_clear();
		} else {
			report("Unable to parse '%s' as a line number", cmd);
		}
	} else if (iscommit(cmd)) {
		string_ncopy(view->env->search, cmd, cmdlen);
		return REQ_JUMP_COMMIT;

	} else if (cmdlen > 1 && (cmd[0] == '/' || cmd[0] == '?')) {
		char search[SIZEOF_STR];

		if (!argv_to_string(argv, search, sizeof(search), " ")) {
			report("Failed to copy search string");
			return REQ_NONE;
		}

		if (!strcmp(search + 1, view->env->search))
			return cmd[0] == '/' ? REQ_FIND_NEXT : REQ_FIND_PREV;

		string_ncopy(view->env->search, search + 1, strlen(search + 1));
		return cmd[0] == '/' ? REQ_SEARCH : REQ_SEARCH_BACK;

	} else if (cmdlen > 1 && cmd[0] == '!') {
		struct view *next = &pager_view;
		bool copied;

		/* Trim the leading '!'. */
		argv[0] = cmd + 1;
		copied = argv_format(view->env, &next->argv, argv, FALSE, TRUE);
		argv[0] = cmd;

		if (!copied) {
			report("Argument formatting failed");
		} else {
			/* When running random commands, initially show the
			 * command in the title. However, it maybe later be
			 * overwritten if a commit line is selected. */
			argv_to_string(next->argv, next->ref, sizeof(next->ref), " ");

			next->dir = NULL;
			open_pager_view(view, OPEN_PREPARED | OPEN_WITH_STDERR);
		}

	} else if (!strcmp(cmd, "toggle")) {
		char action[SIZEOF_STR] = "";
		enum view_flag flags = prompt_toggle(view, argv, action);
		int i;

		if (flags & VIEW_RESET_DISPLAY) {
			resize_display();
			redraw_display(TRUE);
		}

		foreach_displayed_view(view, i) {
			if (view_has_flags(view, flags) && !view->unrefreshable)
				reload_view(view);
			else
				redraw_view(view);
		}

		if (*action)
			report("%s", action);

	} else {
示例#19
0
文件: blame.c 项目: peff/tig
static bool
blame_open(struct view *view, enum open_flags flags)
{
	struct blame_state *state = view->private;
	const char *file_argv[] = { repo.cdup, view->env->file , NULL };
	char path[SIZEOF_STR];
	size_t i;

	if (is_initial_view(view)) {
		/* Finish validating and setting up blame options */
		if (!opt_file_argv || opt_file_argv[1] || (opt_rev_argv && opt_rev_argv[1]))
			usage("Invalid number of options to blame");

		if (opt_rev_argv) {
			string_ncopy(view->env->ref, opt_rev_argv[0], strlen(opt_rev_argv[0]));
		}

		string_ncopy(view->env->file, opt_file_argv[0], strlen(opt_file_argv[0]));

		opt_blame_options = opt_cmdline_argv;
		opt_cmdline_argv = NULL;
	}

	if (!view->env->file[0]) {
		report("No file chosen, press %s to open tree view",
			get_view_key(view, REQ_VIEW_TREE));
		return FALSE;
	}

	if (!view->prev && *repo.prefix && !(flags & (OPEN_RELOAD | OPEN_REFRESH))) {
		string_copy(path, view->env->file);
		if (!string_format(view->env->file, "%s%s", repo.prefix, path)) {
			report("Failed to setup the blame view");
			return FALSE;
		}
	}

	if (*view->env->ref || !begin_update(view, repo.cdup, file_argv, flags)) {
		const char *blame_cat_file_argv[] = {
			"git", "cat-file", "blob", "%(ref):%(file)", NULL
		};

		if (!begin_update(view, repo.cdup, blame_cat_file_argv, flags))
			return FALSE;
	}

	/* First pass: remove multiple references to the same commit. */
	for (i = 0; i < view->lines; i++) {
		struct blame *blame = view->line[i].data;

		if (blame->commit && blame->commit->id[0])
			blame->commit->id[0] = 0;
		else
			blame->commit = NULL;
	}

	/* Second pass: free existing references. */
	for (i = 0; i < view->lines; i++) {
		struct blame *blame = view->line[i].data;

		if (blame->commit)
			free(blame->commit);
	}

	if (!(flags & OPEN_RELOAD))
		reset_view_history(&blame_view_history);
	string_copy_rev(state->history_state.id, view->env->ref);
	state->history_state.filename = get_path(view->env->file);
	if (!state->history_state.filename)
		return FALSE;
	string_format(view->vid, "%s", view->env->file);
	string_format(view->ref, "%s ...", view->env->file);

	return TRUE;
}
示例#20
0
文件: options.c 项目: jlsandell/tig
void
update_diff_context_arg(int diff_context)
{
	if (!string_format(opt_diff_context_arg, "-U%u", diff_context))
		string_ncopy(opt_diff_context_arg, "-U3", 3);
}
示例#21
0
文件: tig.c 项目: lcd047/tig
static enum request
run_prompt_command(struct view *view, char *cmd)
{
	enum request request;

	if (cmd && string_isnumber(cmd)) {
		int lineno = view->pos.lineno + 1;

		if (parse_int(&lineno, cmd, 1, view->lines + 1) == SUCCESS) {
			select_view_line(view, lineno - 1);
			report_clear();
		} else {
			report("Unable to parse '%s' as a line number", cmd);
		}
	} else if (cmd && iscommit(cmd)) {
		string_ncopy(view->env->search, cmd, strlen(cmd));

		request = view_request(view, REQ_JUMP_COMMIT);
		if (request == REQ_JUMP_COMMIT) {
			report("Jumping to commits is not supported by the '%s' view", view->name);
		}

	} else if (cmd && strlen(cmd) == 1) {
		struct key_input input = { { cmd[0] } };

		return get_keybinding(&view->ops->keymap, &input);

	} else if (cmd && cmd[0] == '!') {
		struct view *next = VIEW(REQ_VIEW_PAGER);
		const char *argv[SIZEOF_ARG];
		int argc = 0;

		cmd++;
		/* When running random commands, initially show the
		 * command in the title. However, it maybe later be
		 * overwritten if a commit line is selected. */
		string_ncopy(next->ref, cmd, strlen(cmd));

		if (!argv_from_string(argv, &argc, cmd)) {
			report("Too many arguments");
		} else if (!argv_format(view->env, &next->argv, argv, FALSE, TRUE)) {
			report("Argument formatting failed");
		} else {
			next->dir = NULL;
			open_view(view, REQ_VIEW_PAGER, OPEN_PREPARED);
		}

	} else if (cmd) {
		request = get_request(cmd);
		if (request != REQ_UNKNOWN)
			return request;

		char *args = strchr(cmd, ' ');
		if (args) {
			*args++ = 0;
			if (set_option(cmd, args) == SUCCESS) {
				request = !view->unrefreshable ? REQ_REFRESH : REQ_SCREEN_REDRAW;
				if (!strcmp(cmd, "color"))
					init_colors();
			}
		}
		return request;
	}
	return REQ_NONE;
}
示例#22
0
文件: prompt.c 项目: gonzus/tig
enum request
run_prompt_command(struct view *view, const char *argv[])
{
	enum request request;
	const char *cmd = argv[0];
	size_t cmdlen = cmd ? strlen(cmd) : 0;

	if (!cmd)
		return REQ_NONE;

	if (string_isnumber(cmd)) {
		int lineno = view->pos.lineno + 1;

		if (parse_int(&lineno, cmd, 0, view->lines + 1) == SUCCESS) {
			if (!lineno)
				lineno = 1;
			select_view_line(view, lineno - 1);
			report_clear();
		} else {
			report("Unable to parse '%s' as a line number", cmd);
		}
	} else if (iscommit(cmd)) {
		int lineno;

		if (!(view->ops->column_bits & view_column_bit(ID))) {
			report("Jumping to commits is not supported by the %s view", view->name);
			return REQ_NONE;
		}

		for (lineno = 0; lineno < view->lines; lineno++) {
			struct view_column_data column_data = {};
			struct line *line = &view->line[lineno];

			if (view->ops->get_column_data(view, line, &column_data) &&
			    column_data.id &&
			    !strncasecmp(column_data.id, cmd, cmdlen)) {
				string_ncopy(view->env->search, cmd, cmdlen);
				select_view_line(view, lineno);
				report_clear();
				return REQ_NONE;
			}
		}

		report("Unable to find commit '%s'", view->env->search);
		return REQ_NONE;

	} else if (cmdlen > 1 && (cmd[0] == '/' || cmd[0] == '?')) {
		char search[SIZEOF_STR];

		if (!argv_to_string(argv, search, sizeof(search), " ")) {
			report("Failed to copy search string");
			return REQ_NONE;
		}

		if (!strcmp(search + 1, view->env->search))
			return cmd[0] == '/' ? REQ_FIND_NEXT : REQ_FIND_PREV;

		string_ncopy(view->env->search, search + 1, strlen(search + 1));
		return cmd[0] == '/' ? REQ_SEARCH : REQ_SEARCH_BACK;

	} else if (cmdlen > 1 && cmd[0] == '!') {
		struct view *next = &pager_view;
		bool copied;

		/* Trim the leading '!'. */
		argv[0] = cmd + 1;
		copied = argv_format(view->env, &next->argv, argv, FALSE, TRUE);
		argv[0] = cmd;

		if (!copied) {
			report("Argument formatting failed");
		} else {
			/* When running random commands, initially show the
			 * command in the title. However, it maybe later be
			 * overwritten if a commit line is selected. */
			argv_to_string(next->argv, next->ref, sizeof(next->ref), " ");

			next->dir = NULL;
			open_pager_view(view, OPEN_PREPARED | OPEN_WITH_STDERR);
		}

	} else if (!strcmp(cmd, "save-display")) {
		const char *path = argv[1] ? argv[1] : "tig-display.txt";

		if (!save_display(path))
			report("Failed to save screen to %s", path);
		else
			report("Saved screen to %s", path);

	} else if (!strcmp(cmd, "exec")) {
		struct run_request req = { view->keymap, {}, argv + 1 };
		enum status_code code = parse_run_request_flags(&req.flags, argv + 1);

		if (code != SUCCESS) {
			report("Failed to execute command: %s", get_status_message(code));
		} else {
			return exec_run_request(view, &req);
		}

	} else if (!strcmp(cmd, "toggle")) {
		enum view_flag flags = VIEW_NO_FLAGS;
		enum status_code code = prompt_toggle(view, argv, &flags);
		const char *action = get_status_message(code);

		if (code != SUCCESS) {
			report("%s", action);
			return REQ_NONE;
		}

		prompt_update_display(flags);

		if (*action)
			report("%s", action);

	} else if (!strcmp(cmd, "script")) {
		if (is_script_executing()) {
			report("Scripts cannot be run from scripts");
		} else if (!open_script(argv[1])) {
			report("Failed to open %s", argv[1]);
		}

	} else {
		struct key key = {};
		enum status_code code;
		enum view_flag flags = VIEW_NO_FLAGS;

		/* Try :<key> */
		key.modifiers.multibytes = 1;
		string_ncopy(key.data.bytes, cmd, cmdlen);
		request = get_keybinding(view->keymap, &key, 1);
		if (request != REQ_NONE)
			return request;

		/* Try :<command> */
		request = get_request(cmd);
		if (request != REQ_UNKNOWN)
			return request;

		code = set_option(argv[0], argv_size(argv + 1), &argv[1]);
		if (code != SUCCESS) {
			report("%s", get_status_message(code));
			return REQ_NONE;
		}

		if (!strcmp(cmd, "set")) {
			struct prompt_toggle *toggle;

			toggle = find_prompt_toggle(option_toggles, ARRAY_SIZE(option_toggles),
						    "", argv[1], strlen(argv[1]));

			if (toggle)
				flags = toggle->flags;
		}

		if (flags) {
			prompt_update_display(flags);

		} else {
			request = view_can_refresh(view) ? REQ_REFRESH : REQ_SCREEN_REDRAW;
			if (!strcmp(cmd, "color"))
				init_colors();
			resize_display();
			redraw_display(TRUE);
		}

	}
	return REQ_NONE;
}