index d2dff15fd17bcd60c6a56209a420f33c9fc37a5a..c1dd51366a2694bffe61de408c9e3f2c0ce37e61 100644 (file)
--- a/tig.c
+++ b/tig.c
static void report(const char *msg, ...);
static void set_nonblocking_input(bool loading);
static size_t utf8_length(const char **string, size_t col, int *width, size_t max_width, int *trimmed, bool reserve);
static void report(const char *msg, ...);
static void set_nonblocking_input(bool loading);
static size_t utf8_length(const char **string, size_t col, int *width, size_t max_width, int *trimmed, bool reserve);
+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))
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
@@ -107,6 +108,7 @@ static size_t utf8_length(const char **string, size_t col, int *width, size_t ma
#define DATE_SHORT_COLS STRING_SIZE("2006-04-29 ")
#define ID_COLS 8
#define DATE_SHORT_COLS STRING_SIZE("2006-04-29 ")
#define ID_COLS 8
+#define AUTHOR_COLS 19
#define MIN_VIEW_HEIGHT 4
#define MIN_VIEW_HEIGHT 4
}
}
-/*
- * What value of "tz" was in effect back then at "time" in the
- * local timezone?
- */
-static int local_tzoffset(time_t time)
-{
- time_t t, t_local;
- struct tm tm;
- int offset, eastwest;
-
- t = time;
- localtime_r(&t, &tm);
- t_local = mktime(&tm);
-
- if (t_local < t) {
- eastwest = -1;
- offset = t - t_local;
- } else {
- eastwest = 1;
- offset = t_local - t;
- }
- offset /= 60; /* in minutes */
- offset = (offset % 60) + ((offset / 60) * 100);
- return offset * eastwest;
-}
-
#define DATE_INFO \
DATE_(NO), \
DATE_(DEFAULT), \
#define DATE_INFO \
DATE_(NO), \
DATE_(DEFAULT), \
#undef DATE_
};
#undef DATE_
};
+struct time {
+ time_t sec;
+ int tz;
+};
+
+static inline int timecmp(const struct time *t1, const struct time *t2)
+{
+ return t1->sec - t2->sec;
+}
+
static const char *
static const char *
-string_date(const time_t *time, enum date date)
+string_date(const struct time *time, enum date date)
{
static char buf[DATE_COLS + 1];
static const struct enum_map reldate[] = {
{
static char buf[DATE_COLS + 1];
static const struct enum_map reldate[] = {
if (date == DATE_RELATIVE) {
struct timeval now;
if (date == DATE_RELATIVE) {
struct timeval now;
- time_t date = *time + local_tzoffset(*time);
+ time_t date = time->sec + time->tz;
time_t seconds;
int i;
time_t seconds;
int i;
}
}
}
}
- gmtime_r(time, &tm);
+ gmtime_r(&time->sec, &tm);
return strftime(buf, sizeof(buf), DATE_FORMAT, &tm) ? buf : NULL;
}
return strftime(buf, sizeof(buf), DATE_FORMAT, &tm) ? buf : NULL;
}
+#define AUTHOR_VALUES \
+ AUTHOR_(NO), \
+ AUTHOR_(FULL), \
+ AUTHOR_(ABBREVIATED)
+
+enum author {
+#define AUTHOR_(name) AUTHOR_##name
+ AUTHOR_VALUES,
+#undef AUTHOR_
+ AUTHOR_DEFAULT = AUTHOR_FULL
+};
+
+static const struct enum_map author_map[] = {
+#define AUTHOR_(name) ENUM_MAP(#name, AUTHOR_##name)
+ AUTHOR_VALUES
+#undef AUTHOR_
+};
+
+static const char *
+get_author_initials(const char *author)
+{
+ static char initials[AUTHOR_COLS * 6 + 1];
+ size_t pos = 0;
+ const char *end = strchr(author, '\0');
+
+#define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@' || (c) == '-')
+
+ memset(initials, 0, sizeof(initials));
+ while (author < end) {
+ unsigned char bytes;
+ size_t i;
+
+ while (is_initial_sep(*author))
+ author++;
+
+ bytes = utf8_char_length(author, end);
+ if (bytes < sizeof(initials) - 1 - pos) {
+ while (bytes--) {
+ initials[pos++] = *author++;
+ }
+ }
+
+ for (i = pos; author < end && !is_initial_sep(*author); author++) {
+ if (i < sizeof(initials) - 1)
+ initials[i++] = *author;
+ }
+
+ initials[i++] = 0;
+ }
+
+ return initials;
+}
+
+
static bool
argv_from_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
{
static bool
argv_from_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
{
/* Option and state variables. */
static enum date opt_date = DATE_DEFAULT;
/* Option and state variables. */
static enum date opt_date = DATE_DEFAULT;
-static bool opt_author = TRUE;
+static enum author opt_author = AUTHOR_DEFAULT;
static bool opt_line_number = FALSE;
static bool opt_line_graphics = TRUE;
static bool opt_rev_graph = FALSE;
static bool opt_line_number = FALSE;
static bool opt_line_graphics = TRUE;
static bool opt_rev_graph = FALSE;
static double opt_hscroll = 0.50;
static double opt_scale_split_view = 2.0 / 3.0;
static int opt_tab_size = 8;
static double opt_hscroll = 0.50;
static double opt_scale_split_view = 2.0 / 3.0;
static int opt_tab_size = 8;
-static int opt_author_cols = 19;
+static int opt_author_cols = AUTHOR_COLS;
static char opt_path[SIZEOF_STR] = "";
static char opt_file[SIZEOF_STR] = "";
static char opt_ref[SIZEOF_REF] = "";
static char opt_path[SIZEOF_STR] = "";
static char opt_file[SIZEOF_STR] = "";
static char opt_ref[SIZEOF_REF] = "";
}
if (!strcmp(argv[0], "show-author"))
}
if (!strcmp(argv[0], "show-author"))
- return parse_bool(&opt_author, argv[2]);
+ return parse_enum(&opt_author, argv[2], author_map);
if (!strcmp(argv[0], "show-date"))
return parse_enum(&opt_date, argv[2], date_map);
if (!strcmp(argv[0], "show-date"))
return parse_enum(&opt_date, argv[2], date_map);
@@ -2203,9 +2243,9 @@ draw_field(struct view *view, enum line_type type, const char *text, int len, bo
}
static bool
}
static bool
-draw_date(struct view *view, time_t *time)
+draw_date(struct view *view, struct time *time)
{
{
- const char *date = time ? mkdate(time) : "";
+ const char *date = time && time->sec ? mkdate(time) : "";
int cols = opt_date == DATE_SHORT ? DATE_SHORT_COLS : DATE_COLS;
return draw_field(view, LINE_DATE, date, cols, FALSE);
int cols = opt_date == DATE_SHORT ? DATE_SHORT_COLS : DATE_COLS;
return draw_field(view, LINE_DATE, date, cols, FALSE);
static bool
draw_author(struct view *view, const char *author)
{
static bool
draw_author(struct view *view, const char *author)
{
- bool trim = opt_author_cols == 0 || opt_author_cols > 5 || !author;
-
- if (!trim) {
- static char initials[10];
- size_t pos;
+ bool trim = opt_author_cols == 0 || opt_author_cols > 5;
+ bool abbreviate = opt_author == AUTHOR_ABBREVIATED || !trim;
-#define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@')
-
- memset(initials, 0, sizeof(initials));
- for (pos = 0; *author && pos < opt_author_cols - 1; author++, pos++) {
- while (is_initial_sep(*author))
- author++;
- strncpy(&initials[pos], author, sizeof(initials) - 1 - pos);
- while (*author && !is_initial_sep(author[1]))
- author++;
- }
-
- author = initials;
- }
+ if (abbreviate && author)
+ author = get_author_initials(author);
return draw_field(view, LINE_AUTHOR, author, opt_author_cols, trim);
}
return draw_field(view, LINE_AUTHOR, author, opt_author_cols, trim);
}
toggle_enum_option_do(opt, help, map, ARRAY_SIZE(map))
#define toggle_date() toggle_enum_option(&opt_date, "dates", date_map)
toggle_enum_option_do(opt, help, map, ARRAY_SIZE(map))
#define toggle_date() toggle_enum_option(&opt_date, "dates", date_map)
+#define toggle_author() toggle_enum_option(&opt_author, "author names", author_map)
static void
toggle_view_option(bool *option, const char *help)
static void
toggle_view_option(bool *option, const char *help)
if (prompt_menu("Toggle option", menu, &selected)) {
if (menu[selected].data == &opt_date)
toggle_date();
if (prompt_menu("Toggle option", menu, &selected)) {
if (menu[selected].data == &opt_date)
toggle_date();
+ else if (menu[selected].data == &opt_author)
+ toggle_author();
else
toggle_view_option(menu[selected].data, menu[selected].text);
}
else
toggle_view_option(menu[selected].data, menu[selected].text);
}
{
if (view->pipe)
end_update(view, TRUE);
{
if (view->pipe)
end_update(view, TRUE);
- return io_open(&view->io, "%s", name);
+ return io_open(&view->io, "%s/%s", opt_cdup[0] ? opt_cdup : ".", name);
}
static bool
}
static bool
}
static void
}
static void
-open_editor(bool from_root, const char *file)
+open_editor(const char *file)
{
const char *editor_argv[] = { "vi", file, NULL };
const char *editor;
{
const char *editor_argv[] = { "vi", file, NULL };
const char *editor;
editor = "vi";
editor_argv[0] = editor;
editor = "vi";
editor_argv[0] = editor;
- open_external_viewer(editor_argv, from_root ? opt_cdup : NULL);
+ open_external_viewer(editor_argv, opt_cdup);
}
static void
}
static void
break;
case REQ_TOGGLE_AUTHOR:
break;
case REQ_TOGGLE_AUTHOR:
- toggle_view_option(&opt_author, "author display");
+ toggle_author();
break;
case REQ_TOGGLE_REV_GRAPH:
break;
case REQ_TOGGLE_REV_GRAPH:
}
static void
}
static void
-parse_timezone(time_t *time, const char *zone)
+parse_timesec(struct time *time, const char *sec)
+{
+ time->sec = (time_t) atol(sec);
+}
+
+static void
+parse_timezone(struct time *time, const char *zone)
{
long tz;
{
long tz;
if (zone[0] == '-')
tz = -tz;
if (zone[0] == '-')
tz = -tz;
- *time -= tz;
+ time->tz = tz;
+ time->sec -= tz;
}
/* Parse author lines where the name may be empty:
* author <email@address.tld> 1138474660 +0100
*/
static void
}
/* Parse author lines where the name may be empty:
* author <email@address.tld> 1138474660 +0100
*/
static void
-parse_author_line(char *ident, const char **author, time_t *time)
+parse_author_line(char *ident, const char **author, struct time *time)
{
char *nameend = strchr(ident, '<');
char *emailend = strchr(ident, '>');
{
char *nameend = strchr(ident, '<');
char *emailend = strchr(ident, '>');
char *secs = emailend + 2;
char *zone = strchr(secs, ' ');
char *secs = emailend + 2;
char *zone = strchr(secs, ' ');
- *time = (time_t) atol(secs);
+ parse_timesec(time, secs);
if (zone && strlen(zone) == STRING_SIZE(" +0700"))
parse_timezone(time, zone + 1);
if (zone && strlen(zone) == STRING_SIZE(" +0700"))
parse_timezone(time, zone + 1);
if (add_title && help_open_keymap_title(view, keymap))
return;
if (add_title && help_open_keymap_title(view, keymap))
return;
- add_title = false;
+ add_title = FALSE;
if (group) {
add_line_text(view, group, LINE_HELP_GROUP);
if (group) {
add_line_text(view, group, LINE_HELP_GROUP);
struct tree_entry {
char id[SIZEOF_REV];
mode_t mode;
struct tree_entry {
char id[SIZEOF_REV];
mode_t mode;
- time_t time; /* Date from the author ident. */
+ struct time time; /* Date from the author ident. */
const char *author; /* Author of the commit. */
char name[1];
};
const char *author; /* Author of the commit. */
char name[1];
};
switch (get_sort_field(tree_sort_state)) {
case ORDERBY_DATE:
switch (get_sort_field(tree_sort_state)) {
case ORDERBY_DATE:
- return sort_order(tree_sort_state, entry1->time - entry2->time);
+ return sort_order(tree_sort_state, timecmp(&entry1->time, &entry2->time));
case ORDERBY_AUTHOR:
return sort_order(tree_sort_state, strcmp(entry1->author, entry2->author));
case ORDERBY_AUTHOR:
return sort_order(tree_sort_state, strcmp(entry1->author, entry2->author));
tree_read_date(struct view *view, char *text, bool *read_date)
{
static const char *author_name;
tree_read_date(struct view *view, char *text, bool *read_date)
{
static const char *author_name;
- static time_t author_time;
+ static struct time author_time;
if (!text && *read_date) {
*read_date = FALSE;
if (!text && *read_date) {
*read_date = FALSE;
if (opt_author && draw_author(view, entry->author))
return TRUE;
if (opt_author && draw_author(view, entry->author))
return TRUE;
- if (opt_date && draw_date(view, entry->author ? &entry->time : NULL))
+ if (opt_date && draw_date(view, &entry->time))
return TRUE;
}
if (draw_text(view, line->type, entry->name, TRUE))
return TRUE;
}
if (draw_text(view, line->type, entry->name, TRUE))
else if (!run_io_append(blob_ops.argv, FORMAT_ALL, fd))
report("Failed to save blob data to file");
else
else if (!run_io_append(blob_ops.argv, FORMAT_ALL, fd))
report("Failed to save blob data to file");
else
- open_editor(FALSE, file);
+ open_editor(file);
if (fd != -1)
unlink(file);
}
if (fd != -1)
unlink(file);
}
} else if (!is_head_commit(view->vid)) {
open_blob_editor();
} else {
} else if (!is_head_commit(view->vid)) {
open_blob_editor();
} else {
- open_editor(TRUE, opt_file);
+ open_editor(opt_file);
}
return REQ_NONE;
}
return REQ_NONE;
char id[SIZEOF_REV]; /* SHA1 ID. */
char title[128]; /* First line of the commit message. */
const char *author; /* Author of the commit. */
char id[SIZEOF_REV]; /* SHA1 ID. */
char title[128]; /* First line of the commit message. */
const char *author; /* Author of the commit. */
- time_t time; /* Date from the author ident. */
+ struct time time; /* Date from the author ident. */
char filename[128]; /* Name of file. */
bool has_previous; /* Was a "previous" line detected. */
};
char filename[128]; /* Name of file. */
bool has_previous; /* Was a "previous" line detected. */
};
commit->author = get_author(line);
} else if (match_blame_header("author-time ", &line)) {
commit->author = get_author(line);
} else if (match_blame_header("author-time ", &line)) {
- commit->time = (time_t) atol(line);
+ parse_timesec(&commit->time, line);
} else if (match_blame_header("author-tz ", &line)) {
parse_timezone(&commit->time, line);
} else if (match_blame_header("author-tz ", &line)) {
parse_timezone(&commit->time, line);
blame_draw(struct view *view, struct line *line, unsigned int lineno)
{
struct blame *blame = line->data;
blame_draw(struct view *view, struct line *line, unsigned int lineno)
{
struct blame *blame = line->data;
- time_t *time = NULL;
+ struct time *time = NULL;
const char *id = NULL, *author = NULL;
char text[SIZEOF_STR];
const char *id = NULL, *author = NULL;
char text[SIZEOF_STR];
struct branch {
const char *author; /* Author of the last commit. */
struct branch {
const char *author; /* Author of the last commit. */
- time_t time; /* Date of the last activity. */
+ struct time time; /* Date of the last activity. */
const struct ref *ref; /* Name and commit ID information. */
};
const struct ref *ref; /* Name and commit ID information. */
};
switch (get_sort_field(branch_sort_state)) {
case ORDERBY_DATE:
switch (get_sort_field(branch_sort_state)) {
case ORDERBY_DATE:
- return sort_order(branch_sort_state, branch1->time - branch2->time);
+ return sort_order(branch_sort_state, timecmp(&branch1->time, &branch2->time));
case ORDERBY_AUTHOR:
return sort_order(branch_sort_state, strcmp(branch1->author, branch2->author));
case ORDERBY_AUTHOR:
return sort_order(branch_sort_state, strcmp(branch1->author, branch2->author));
struct branch *branch = line->data;
enum line_type type = branch->ref->head ? LINE_MAIN_HEAD : LINE_DEFAULT;
struct branch *branch = line->data;
enum line_type type = branch->ref->head ? LINE_MAIN_HEAD : LINE_DEFAULT;
- if (opt_date && draw_date(view, branch->author ? &branch->time : NULL))
+ if (opt_date && draw_date(view, &branch->time))
return TRUE;
if (opt_author && draw_author(view, branch->author))
return TRUE;
if (opt_author && draw_author(view, branch->author))
};
static const char *status_list_other_argv[] = {
};
static const char *status_list_other_argv[] = {
- "git", "ls-files", "-z", "--others", "--exclude-standard", NULL
+ "git", "ls-files", "-z", "--others", "--exclude-standard", opt_prefix, NULL
};
static const char *status_list_no_head_argv[] = {
};
static const char *status_list_no_head_argv[] = {
return REQ_NONE;
}
return REQ_NONE;
}
- open_editor(status->status != '?', status->new.name);
+ open_editor(status->new.name);
break;
case REQ_VIEW_BLAME:
break;
case REQ_VIEW_BLAME:
return REQ_NONE;
}
return REQ_NONE;
}
- open_editor(stage_status.status != '?', stage_status.new.name);
+ open_editor(stage_status.new.name);
break;
case REQ_REFRESH:
break;
case REQ_REFRESH:
char id[SIZEOF_REV]; /* SHA1 ID. */
char title[128]; /* First line of the commit message. */
const char *author; /* Author of the commit. */
char id[SIZEOF_REV]; /* SHA1 ID. */
char title[128]; /* First line of the commit message. */
const char *author; /* Author of the commit. */
- time_t time; /* Date from the author ident. */
+ struct time time; /* Date from the author ident. */
struct ref_list *refs; /* Repository references. */
chtype graph[SIZEOF_REVGRAPH]; /* Ancestry chain graphics. */
size_t graph_size; /* The width of the graph array. */
struct ref_list *refs; /* Repository references. */
chtype graph[SIZEOF_REVGRAPH]; /* Ancestry chain graphics. */
size_t graph_size; /* The width of the graph array. */
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,
};
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)
/* Decode UTF-8 multi-byte representation into a Unicode character. */
static inline unsigned long
utf8_to_unicode(const char *string, size_t length)
@@ -6832,8 +6876,7 @@ utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *
*trimmed = 0;
while (string < end) {
*trimmed = 0;
while (string < end) {
- int c = *(unsigned char *) string;
- unsigned char bytes = utf8_bytes[c];
+ unsigned char bytes = utf8_char_length(string, end);
size_t ucwidth;
unsigned long unicode;
size_t ucwidth;
unsigned long unicode;