index cad66c8e6e20d85fe8f84eb262e007c7e2112dc7..47daba930c2831d267ad6665e3d0660a8027c719 100644 (file)
--- a/tig.c
+++ b/tig.c
static void __NORETURN die(const char *err, ...);
static void warn(const char *msg, ...);
static void report(const char *msg, ...);
-static size_t utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size);
-static inline unsigned char utf8_char_length(const char *string, const char *end);
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
enum format_flags {
FORMAT_ALL, /* Perform replacement in all arguments. */
- FORMAT_DASH, /* Perform replacement up until "--". */
FORMAT_NONE /* No replacement should be performed. */
};
}
+/*
+ * Unicode / UTF-8 handling
+ *
+ * NOTE: Much of the following code for dealing with Unicode is derived from
+ * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
+ * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
+ */
+
+static inline int
+unicode_width(unsigned long c, int tab_size)
+{
+ if (c >= 0x1100 &&
+ (c <= 0x115f /* Hangul Jamo */
+ || c == 0x2329
+ || c == 0x232a
+ || (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f)
+ /* CJK ... Yi */
+ || (c >= 0xac00 && c <= 0xd7a3) /* Hangul Syllables */
+ || (c >= 0xf900 && c <= 0xfaff) /* CJK Compatibility Ideographs */
+ || (c >= 0xfe30 && c <= 0xfe6f) /* CJK Compatibility Forms */
+ || (c >= 0xff00 && c <= 0xff60) /* Fullwidth Forms */
+ || (c >= 0xffe0 && c <= 0xffe6)
+ || (c >= 0x20000 && c <= 0x2fffd)
+ || (c >= 0x30000 && c <= 0x3fffd)))
+ return 2;
+
+ if (c == '\t')
+ return tab_size;
+
+ return 1;
+}
+
+/* Number of bytes used for encoding a UTF-8 character indexed by first byte.
+ * Illegal bytes are set one. */
+static const unsigned char utf8_bytes[256] = {
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4, 5,5,5,5,6,6,1,1,
+};
+
+static inline unsigned char
+utf8_char_length(const char *string, const char *end)
+{
+ int c = *(unsigned char *) string;
+
+ return utf8_bytes[c];
+}
+
+/* Decode UTF-8 multi-byte representation into a Unicode character. */
+static inline unsigned long
+utf8_to_unicode(const char *string, size_t length)
+{
+ unsigned long unicode;
+
+ switch (length) {
+ case 1:
+ unicode = string[0];
+ break;
+ case 2:
+ unicode = (string[0] & 0x1f) << 6;
+ unicode += (string[1] & 0x3f);
+ break;
+ case 3:
+ unicode = (string[0] & 0x0f) << 12;
+ unicode += ((string[1] & 0x3f) << 6);
+ unicode += (string[2] & 0x3f);
+ break;
+ case 4:
+ unicode = (string[0] & 0x0f) << 18;
+ unicode += ((string[1] & 0x3f) << 12);
+ unicode += ((string[2] & 0x3f) << 6);
+ unicode += (string[3] & 0x3f);
+ break;
+ case 5:
+ unicode = (string[0] & 0x0f) << 24;
+ unicode += ((string[1] & 0x3f) << 18);
+ unicode += ((string[2] & 0x3f) << 12);
+ unicode += ((string[3] & 0x3f) << 6);
+ unicode += (string[4] & 0x3f);
+ break;
+ case 6:
+ unicode = (string[0] & 0x01) << 30;
+ unicode += ((string[1] & 0x3f) << 24);
+ unicode += ((string[2] & 0x3f) << 18);
+ unicode += ((string[3] & 0x3f) << 12);
+ unicode += ((string[4] & 0x3f) << 6);
+ unicode += (string[5] & 0x3f);
+ break;
+ default:
+ return 0;
+ }
+
+ /* Invalid characters could return the special 0xfffd value but NUL
+ * should be just as good. */
+ return unicode > 0xffff ? 0 : unicode;
+}
+
+/* Calculates how much of string can be shown within the given maximum width
+ * and sets trimmed parameter to non-zero value if all of string could not be
+ * shown. If the reserve flag is TRUE, it will reserve at least one
+ * trailing character, which can be useful when drawing a delimiter.
+ *
+ * Returns the number of bytes to output from string to satisfy max_width. */
+static size_t
+utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
+{
+ const char *string = *start;
+ const char *end = strchr(string, '\0');
+ unsigned char last_bytes = 0;
+ size_t last_ucwidth = 0;
+
+ *width = 0;
+ *trimmed = 0;
+
+ while (string < end) {
+ unsigned char bytes = utf8_char_length(string, end);
+ size_t ucwidth;
+ unsigned long unicode;
+
+ if (string + bytes > end)
+ break;
+
+ /* Change representation to figure out whether
+ * it is a single- or double-width character. */
+
+ unicode = utf8_to_unicode(string, bytes);
+ /* FIXME: Graceful handling of invalid Unicode character. */
+ if (!unicode)
+ break;
+
+ ucwidth = unicode_width(unicode, tab_size);
+ if (skip > 0) {
+ skip -= ucwidth <= skip ? ucwidth : skip;
+ *start += bytes;
+ }
+ *width += ucwidth;
+ if (*width > max_width) {
+ *trimmed = 1;
+ *width -= ucwidth;
+ if (reserve && *width == max_width) {
+ string -= last_bytes;
+ *width -= last_ucwidth;
+ }
+ break;
+ }
+
+ string += bytes;
+ last_bytes = ucwidth ? bytes : 0;
+ last_ucwidth = ucwidth;
+ }
+
+ return string - *start;
+}
+
+
#define DATE_INFO \
DATE_(NO), \
DATE_(DEFAULT), \
return *argc < SIZEOF_ARG;
}
-static void
+static bool
argv_from_env(const char **argv, const char *name)
{
char *env = argv ? getenv(name) : NULL;
if (env && *env)
env = strdup(env);
- if (env && !argv_from_string(argv, &argc, env))
- die("Too many arguments in the `%s` environment variable", name);
+ return !env || argv_from_string(argv, &argc, env);
}
#define REQ_(req, help) REQ_##req
/* Offset all requests to avoid conflicts with ncurses getch values. */
- REQ_OFFSET = KEY_MAX + 1,
+ REQ_UNKNOWN = KEY_MAX + 1,
+ REQ_OFFSET,
REQ_INFO
#undef REQ_GROUP
if (enum_equals(req_info[i], name, namelen))
return req_info[i].request;
- return REQ_NONE;
+ return REQ_UNKNOWN;
}
enum request request;
};
-static const struct keybinding default_keybindings[] = {
+static struct keybinding default_keybindings[] = {
/* View switching */
{ 'm', REQ_VIEW_MAIN },
{ 'd', REQ_VIEW_DIFF },
die("Failed to allocate keybinding");
table->data[table->size].alias = key;
table->data[table->size++].request = request;
+
+ if (request == REQ_NONE && keymap == KEYMAP_GENERIC) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(default_keybindings); i++)
+ if (default_keybindings[i].alias == key)
+ default_keybindings[i].request = REQ_NONE;
+ }
}
/* Looks for a key binding first in the given map, then in the generic map, and
add_builtin_run_requests(void)
{
const char *cherry_pick[] = { "git", "cherry-pick", "%(commit)", NULL };
+ const char *checkout[] = { "git", "checkout", "%(branch)", NULL };
const char *commit[] = { "git", "commit", NULL };
const char *gc[] = { "git", "gc", NULL };
struct {
} reqs[] = {
{ KEYMAP_MAIN, 'C', ARRAY_SIZE(cherry_pick) - 1, cherry_pick },
{ KEYMAP_STATUS, 'C', ARRAY_SIZE(commit) - 1, commit },
+ { KEYMAP_BRANCH, 'C', ARRAY_SIZE(checkout) - 1, checkout },
{ KEYMAP_GENERIC, 'G', ARRAY_SIZE(gc) - 1, gc },
};
int i;
}
request = get_request(argv[2]);
- if (request == REQ_NONE) {
+ if (request == REQ_UNKNOWN) {
static const struct enum_map obsolete[] = {
ENUM_MAP("cherry-pick", REQ_NONE),
ENUM_MAP("screen-resize", REQ_NONE),
return ERR;
}
}
- if (request == REQ_NONE && *argv[2]++ == '!')
+ if (request == REQ_UNKNOWN && *argv[2]++ == '!')
request = add_run_request(keymap, key, argc - 2, argv + 2);
- if (request == REQ_NONE) {
+ if (request == REQ_UNKNOWN) {
config_msg = "Unknown request name";
return ERR;
}
static char ref_blob[SIZEOF_REF] = "";
static char ref_commit[SIZEOF_REF] = "HEAD";
static char ref_head[SIZEOF_REF] = "HEAD";
+static char ref_branch[SIZEOF_REF] = "";
+
+enum view_type {
+ VIEW_MAIN,
+ VIEW_DIFF,
+ VIEW_LOG,
+ VIEW_TREE,
+ VIEW_BLOB,
+ VIEW_BLAME,
+ VIEW_BRANCH,
+ VIEW_HELP,
+ VIEW_PAGER,
+ VIEW_STATUS,
+ VIEW_STAGE,
+};
struct view {
+ enum view_type type; /* View type */
const char *name; /* View name */
const char *cmd_env; /* Command line set via environment */
const char *id; /* Points to either of ref_{head,commit,blob} */
enum keymap keymap; /* What keymap does this view have */
bool git_dir; /* Whether the view requires a git directory. */
+ bool refresh; /* Whether the view supports refreshing. */
char ref[SIZEOF_REF]; /* Hovered commit reference */
char vid[SIZEOF_REF]; /* View ID. Set to id member when updating. */
static struct view_ops tree_ops;
static struct view_ops branch_ops;
-#define VIEW_STR(name, env, ref, ops, map, git) \
- { name, #env, ref, ops, map, git }
-
-#define VIEW_(id, name, ops, git, ref) \
- VIEW_STR(name, TIG_##id##_CMD, ref, ops, KEYMAP_##id, git)
+#define VIEW_STR(type, name, env, ref, ops, map, git, refresh) \
+ { type, name, #env, ref, ops, map, git, refresh }
+#define VIEW_(id, name, ops, git, refresh, ref) \
+ VIEW_STR(VIEW_##id, name, TIG_##id##_CMD, ref, ops, KEYMAP_##id, git, refresh)
static struct view views[] = {
- VIEW_(MAIN, "main", &main_ops, TRUE, ref_head),
- VIEW_(DIFF, "diff", &diff_ops, TRUE, ref_commit),
- VIEW_(LOG, "log", &log_ops, TRUE, ref_head),
- VIEW_(TREE, "tree", &tree_ops, TRUE, ref_commit),
- VIEW_(BLOB, "blob", &blob_ops, TRUE, ref_blob),
- VIEW_(BLAME, "blame", &blame_ops, TRUE, ref_commit),
- VIEW_(BRANCH, "branch", &branch_ops, TRUE, ref_head),
- VIEW_(HELP, "help", &help_ops, FALSE, ""),
- VIEW_(PAGER, "pager", &pager_ops, FALSE, "stdin"),
- VIEW_(STATUS, "status", &status_ops, TRUE, ""),
- VIEW_(STAGE, "stage", &stage_ops, TRUE, ""),
+ VIEW_(MAIN, "main", &main_ops, TRUE, TRUE, ref_head),
+ VIEW_(DIFF, "diff", &diff_ops, TRUE, FALSE, ref_commit),
+ VIEW_(LOG, "log", &log_ops, TRUE, TRUE, ref_head),
+ VIEW_(TREE, "tree", &tree_ops, TRUE, FALSE, ref_commit),
+ VIEW_(BLOB, "blob", &blob_ops, TRUE, FALSE, ref_blob),
+ VIEW_(BLAME, "blame", &blame_ops, TRUE, FALSE, ref_commit),
+ VIEW_(BRANCH, "branch", &branch_ops, TRUE, TRUE, ref_head),
+ VIEW_(HELP, "help", &help_ops, FALSE, FALSE, ""),
+ VIEW_(PAGER, "pager", &pager_ops, FALSE, FALSE, "stdin"),
+ VIEW_(STATUS, "status", &status_ops, TRUE, TRUE, ""),
+ VIEW_(STAGE, "stage", &stage_ops, TRUE, TRUE, ""),
};
#define VIEW(req) (&views[(req) - REQ_OFFSET - 1])
-#define VIEW_REQ(view) ((view) - views + REQ_OFFSET + 1)
#define foreach_view(view, i) \
for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
#define view_is_displayed(view) \
(view == display[0] || view == display[1])
+#define view_has_parent(view, child_type, parent_type) \
+ (view->type == child_type && view->parent && view->parent->type == parent_type)
static inline void
set_view_attr(struct view *view, enum line_type type)
assert(view_is_displayed(view));
- if (view != VIEW(REQ_VIEW_STATUS) && view->lines) {
+ if (view->type != VIEW_STATUS && view->lines) {
unsigned int view_lines = view->offset + view->height;
unsigned int lines = view->lines
? MIN(view_lines, view->lines) * 100 / view->lines
FORMAT_VAR("%(head)", ref_head, ""),
FORMAT_VAR("%(commit)", ref_commit, ""),
FORMAT_VAR("%(blob)", ref_blob, ""),
+ FORMAT_VAR("%(branch)", ref_branch, ""),
};
int i;
if (!strncmp(name, vars[i].name, vars[i].namelen))
return *vars[i].value ? vars[i].value : vars[i].value_if_empty;
+ report("Unknown replacement: `%s`", name);
return NULL;
}
@@ -2984,8 +3170,6 @@ format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags fl
const char *value;
if (!next || noreplace) {
- if (flags == FORMAT_DASH && !strcmp(arg, "--"))
- noreplace = TRUE;
len = strlen(arg);
value = "";
@@ -2993,7 +3177,6 @@ format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags fl
value = format_arg(next);
if (!value) {
- report("Unknown replacement: `%s`", next);
return FALSE;
}
}
}
static bool
-prepare_update(struct view *view, const char *argv[], const char *dir,
- enum format_flags flags)
+prepare_update(struct view *view, const char *argv[], const char *dir)
{
if (view->pipe)
end_update(view, TRUE);
- return io_format(&view->io, dir, IO_RD, argv, flags);
+ return io_format(&view->io, dir, IO_RD, argv, FORMAT_NONE);
}
static bool
/* Keep the displayed view in sync with line number scaling. */
if (digits != view->digits) {
view->digits = digits;
- if (opt_line_number || view == VIEW(REQ_VIEW_BLAME))
+ if (opt_line_number || view->type == VIEW_BLAME)
redraw = TRUE;
}
}
if (request > REQ_NONE) {
open_run_request(request);
/* FIXME: When all views can refresh always do this. */
- if (view == VIEW(REQ_VIEW_STATUS) ||
- view == VIEW(REQ_VIEW_MAIN) ||
- view == VIEW(REQ_VIEW_LOG) ||
- view == VIEW(REQ_VIEW_BRANCH) ||
- view == VIEW(REQ_VIEW_STAGE))
+ if (view->refresh)
request = REQ_REFRESH;
else
return TRUE;
case REQ_PREVIOUS:
request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
- if ((view == VIEW(REQ_VIEW_DIFF) &&
- view->parent == VIEW(REQ_VIEW_MAIN)) ||
- (view == VIEW(REQ_VIEW_DIFF) &&
- view->parent == VIEW(REQ_VIEW_BLAME)) ||
- (view == VIEW(REQ_VIEW_STAGE) &&
- view->parent == VIEW(REQ_VIEW_STATUS)) ||
- (view == VIEW(REQ_VIEW_BLOB) &&
- view->parent == VIEW(REQ_VIEW_TREE)) ||
- (view == VIEW(REQ_VIEW_MAIN) &&
- view->parent == VIEW(REQ_VIEW_BRANCH))) {
+ if (view_has_parent(view, VIEW_DIFF, VIEW_MAIN) ||
+ view_has_parent(view, VIEW_DIFF, VIEW_BLAME) ||
+ view_has_parent(view, VIEW_STAGE, VIEW_STATUS) ||
+ view_has_parent(view, VIEW_BLOB, VIEW_TREE) ||
+ view_has_parent(view, VIEW_MAIN, VIEW_BRANCH)) {
int line;
view = view->parent;
break;
case REQ_STOP_LOADING:
- for (i = 0; i < ARRAY_SIZE(views); i++) {
- view = &views[i];
+ foreach_view(view, i) {
if (view->pipe)
report("Stopped loading the %s view", view->name),
end_update(view, TRUE);
list = get_ref_list(commit_id);
if (!list) {
- if (view == VIEW(REQ_VIEW_DIFF))
+ if (view->type == VIEW_DIFF)
goto try_add_describe_ref;
return;
}
is_tag = TRUE;
}
- if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
+ if (!is_tag && view->type == VIEW_DIFF) {
try_add_describe_ref:
/* Add <tag>-g<commit_id> "fake" reference. */
if (!add_describe_ref(buf, &bufpos, commit_id, sep))
return FALSE;
if (line->type == LINE_COMMIT &&
- (view == VIEW(REQ_VIEW_DIFF) ||
- view == VIEW(REQ_VIEW_LOG)))
+ (view->type == VIEW_DIFF ||
+ view->type == VIEW_LOG))
add_pager_refs(view, line);
return TRUE;
return request;
if (line->type == LINE_COMMIT &&
- (view == VIEW(REQ_VIEW_LOG) ||
- view == VIEW(REQ_VIEW_PAGER))) {
+ (view->type == VIEW_LOG ||
+ view->type == VIEW_PAGER)) {
open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
split = 1;
}
if (line->type == LINE_COMMIT) {
char *text = (char *)line->data + STRING_SIZE("commit ");
- if (view != VIEW(REQ_VIEW_PAGER))
+ if (view->type != VIEW_PAGER)
string_copy_rev(view->ref, text);
string_copy_rev(ref_commit, text);
}
diff_index_argv[7] = "/dev/null";
}
- if (!prepare_update(diff, diff_index_argv, NULL, FORMAT_DASH)) {
+ if (!prepare_update(diff, diff_index_argv, NULL)) {
report("Failed to allocate diff command");
break;
}
};
struct view *main_view = VIEW(REQ_VIEW_MAIN);
- if (!prepare_update(main_view, all_branches_argv, NULL, FORMAT_NONE)) {
+ if (!prepare_update(main_view, all_branches_argv, NULL)) {
report("Failed to load view of all branches");
return REQ_NONE;
}
string_copy_rev(view->ref, branch->ref->id);
string_copy_rev(ref_commit, branch->ref->id);
string_copy_rev(ref_head, branch->ref->id);
+ string_copy_rev(ref_branch, branch->ref->name);
}
static struct view_ops branch_ops = {
"--", "/dev/null", newpath, NULL
};
- if (!prepare_update(stage, no_head_diff_argv, opt_cdup, FORMAT_DASH))
+ if (!prepare_update(stage, no_head_diff_argv, opt_cdup))
return status_load_error(view, stage, newpath);
} else {
const char *index_show_argv[] = {
oldpath, newpath, NULL
};
- if (!prepare_update(stage, index_show_argv, opt_cdup, FORMAT_DASH))
+ if (!prepare_update(stage, index_show_argv, opt_cdup))
return status_load_error(view, stage, newpath);
}
"-C", "-M", "--", oldpath, newpath, NULL
};
- if (!prepare_update(stage, files_show_argv, opt_cdup, FORMAT_DASH))
+ if (!prepare_update(stage, files_show_argv, opt_cdup))
return status_load_error(view, stage, newpath);
if (status)
info = "Unstaged changes to %s";
};
-/*
- * Unicode / UTF-8 handling
- *
- * NOTE: Much of the following code for dealing with Unicode is derived from
- * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
- * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
- */
-
-static inline int
-unicode_width(unsigned long c, int tab_size)
-{
- if (c >= 0x1100 &&
- (c <= 0x115f /* Hangul Jamo */
- || c == 0x2329
- || c == 0x232a
- || (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f)
- /* CJK ... Yi */
- || (c >= 0xac00 && c <= 0xd7a3) /* Hangul Syllables */
- || (c >= 0xf900 && c <= 0xfaff) /* CJK Compatibility Ideographs */
- || (c >= 0xfe30 && c <= 0xfe6f) /* CJK Compatibility Forms */
- || (c >= 0xff00 && c <= 0xff60) /* Fullwidth Forms */
- || (c >= 0xffe0 && c <= 0xffe6)
- || (c >= 0x20000 && c <= 0x2fffd)
- || (c >= 0x30000 && c <= 0x3fffd)))
- return 2;
-
- if (c == '\t')
- return tab_size;
-
- return 1;
-}
-
-/* Number of bytes used for encoding a UTF-8 character indexed by first byte.
- * Illegal bytes are set one. */
-static const unsigned char utf8_bytes[256] = {
- 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
- 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4, 5,5,5,5,6,6,1,1,
-};
-
-static inline unsigned char
-utf8_char_length(const char *string, const char *end)
-{
- int c = *(unsigned char *) string;
-
- return utf8_bytes[c];
-}
-
-/* Decode UTF-8 multi-byte representation into a Unicode character. */
-static inline unsigned long
-utf8_to_unicode(const char *string, size_t length)
-{
- unsigned long unicode;
-
- switch (length) {
- case 1:
- unicode = string[0];
- break;
- case 2:
- unicode = (string[0] & 0x1f) << 6;
- unicode += (string[1] & 0x3f);
- break;
- case 3:
- unicode = (string[0] & 0x0f) << 12;
- unicode += ((string[1] & 0x3f) << 6);
- unicode += (string[2] & 0x3f);
- break;
- case 4:
- unicode = (string[0] & 0x0f) << 18;
- unicode += ((string[1] & 0x3f) << 12);
- unicode += ((string[2] & 0x3f) << 6);
- unicode += (string[3] & 0x3f);
- break;
- case 5:
- unicode = (string[0] & 0x0f) << 24;
- unicode += ((string[1] & 0x3f) << 18);
- unicode += ((string[2] & 0x3f) << 12);
- unicode += ((string[3] & 0x3f) << 6);
- unicode += (string[4] & 0x3f);
- break;
- case 6:
- unicode = (string[0] & 0x01) << 30;
- unicode += ((string[1] & 0x3f) << 24);
- unicode += ((string[2] & 0x3f) << 18);
- unicode += ((string[3] & 0x3f) << 12);
- unicode += ((string[4] & 0x3f) << 6);
- unicode += (string[5] & 0x3f);
- break;
- default:
- die("Invalid Unicode length");
- }
-
- /* Invalid characters could return the special 0xfffd value but NUL
- * should be just as good. */
- return unicode > 0xffff ? 0 : unicode;
-}
-
-/* Calculates how much of string can be shown within the given maximum width
- * and sets trimmed parameter to non-zero value if all of string could not be
- * shown. If the reserve flag is TRUE, it will reserve at least one
- * trailing character, which can be useful when drawing a delimiter.
- *
- * Returns the number of bytes to output from string to satisfy max_width. */
-static size_t
-utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
-{
- const char *string = *start;
- const char *end = strchr(string, '\0');
- unsigned char last_bytes = 0;
- size_t last_ucwidth = 0;
-
- *width = 0;
- *trimmed = 0;
-
- while (string < end) {
- unsigned char bytes = utf8_char_length(string, end);
- size_t ucwidth;
- unsigned long unicode;
-
- if (string + bytes > end)
- break;
-
- /* Change representation to figure out whether
- * it is a single- or double-width character. */
-
- unicode = utf8_to_unicode(string, bytes);
- /* FIXME: Graceful handling of invalid Unicode character. */
- if (!unicode)
- break;
-
- ucwidth = unicode_width(unicode, tab_size);
- if (skip > 0) {
- skip -= ucwidth <= skip ? ucwidth : skip;
- *start += bytes;
- }
- *width += ucwidth;
- if (*width > max_width) {
- *trimmed = 1;
- *width -= ucwidth;
- if (reserve && *width == max_width) {
- string -= last_bytes;
- *width -= last_ucwidth;
- }
- break;
- }
-
- string += bytes;
- last_bytes = ucwidth ? bytes : 0;
- last_ucwidth = ucwidth;
- }
-
- return string - *start;
-}
-
-
/*
* Status management
*/
size_t i;
if (!init) {
- argv_from_env(ls_remote_argv, "TIG_LS_REMOTE");
+ if (!argv_from_env(ls_remote_argv, "TIG_LS_REMOTE"))
+ die("TIG_LS_REMOTE contains too many arguments");
init = TRUE;
}
die("command too long");
}
- if (!prepare_update(VIEW(request), custom_argv, NULL, FORMAT_NONE))
+ if (!prepare_update(VIEW(request), custom_argv, NULL))
die("Failed to format arguments");
return request;
die("Failed to load refs.");
foreach_view (view, i)
- argv_from_env(view->ops->argv, view->cmd_env);
+ if (!argv_from_env(view->ops->argv, view->cmd_env))
+ die("Too many arguments in the `%s` environment variable",
+ view->cmd_env);
init_display();
/* Some low-level request handling. This keeps access to
* status_win restricted. */
switch (request) {
+ case REQ_NONE:
+ report("Unknown key, press %s for help",
+ get_key(view->keymap, REQ_VIEW_HELP));
+ break;
case REQ_PROMPT:
{
char *cmd = read_prompt(":");
if (!argv_from_string(argv, &argc, cmd)) {
report("Too many arguments");
- } else if (!prepare_update(next, argv, NULL, FORMAT_DASH)) {
+ } else if (!prepare_update(next, argv, NULL)) {
report("Failed to format command");
} else {
open_view(view, REQ_VIEW_PAGER, OPEN_PREPARED);