X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=tig.c;h=c1dd51366a2694bffe61de408c9e3f2c0ce37e61;hb=74323336d4df16c9465109dd72cee4316afbb04f;hp=fc7e8c35e9644b2ccc765564d7211cf15c93c67b;hpb=5c054b1f63469fac38075ed425fbbae94fe9b495;p=tig.git diff --git a/tig.c b/tig.c index fc7e8c3..c1dd513 100644 --- a/tig.c +++ b/tig.c @@ -70,6 +70,7 @@ static void warn(const char *msg, ...); 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)) @@ -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 AUTHOR_COLS 19 #define MIN_VIEW_HEIGHT 4 @@ -298,7 +300,7 @@ string_enum_compare(const char *str1, const char *str2, int len) } #define enum_equals(entry, str, len) \ - ((entry).namelen == (len) && string_enum_compare((entry).name, str, len)) + ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len)) struct enum_map { const char *name; @@ -357,32 +359,6 @@ suffixcmp(const char *str, int slen, const char *suffix) } -/* - * 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), \ @@ -401,8 +377,18 @@ static const struct enum_map date_map[] = { #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 * -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[] = { @@ -417,7 +403,7 @@ string_date(const time_t *time, enum date date) 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; @@ -437,11 +423,65 @@ string_date(const time_t *time, enum date date) } } - gmtime_r(time, &tm); + gmtime_r(&time->sec, &tm); 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) { @@ -1007,7 +1047,7 @@ get_request(const char *name) /* 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; @@ -1016,7 +1056,7 @@ static int opt_num_interval = 5; 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] = ""; @@ -1741,7 +1781,7 @@ option_set_command(int argc, const char *argv[]) } 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); @@ -2203,9 +2243,9 @@ draw_field(struct view *view, enum line_type type, const char *text, int len, bo } 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); @@ -2214,25 +2254,11 @@ draw_date(struct view *view, time_t *time) static bool draw_author(struct view *view, const char *author) { - bool trim = opt_author_cols == 0 || opt_author_cols > 5 || !author; + bool trim = opt_author_cols == 0 || opt_author_cols > 5; + bool abbreviate = opt_author == AUTHOR_ABBREVIATED || !trim; - if (!trim) { - static char initials[10]; - size_t pos; - -#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); } @@ -2498,6 +2524,7 @@ toggle_enum_option_do(unsigned int *opt, const char *help, 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) @@ -2523,6 +2550,8 @@ open_option_menu(void) 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); } @@ -2914,6 +2943,32 @@ free_argv(const char *argv[]) free((void *) argv[argc]); } +static const char * +format_arg(const char *name) +{ + static struct { + const char *name; + size_t namelen; + const char *value; + const char *value_if_empty; + } vars[] = { +#define FORMAT_VAR(name, value, value_if_empty) \ + { name, STRING_SIZE(name), value, value_if_empty } + FORMAT_VAR("%(directory)", opt_path, ""), + FORMAT_VAR("%(file)", opt_file, ""), + FORMAT_VAR("%(ref)", opt_ref, "HEAD"), + FORMAT_VAR("%(head)", ref_head, ""), + FORMAT_VAR("%(commit)", ref_commit, ""), + FORMAT_VAR("%(blob)", ref_blob, ""), + }; + int i; + + for (i = 0; i < ARRAY_SIZE(vars); i++) + if (!strncmp(name, vars[i].name, vars[i].namelen)) + return *vars[i].value ? vars[i].value : vars[i].value_if_empty; + + return NULL; +} static bool format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags flags) { @@ -2938,27 +2993,13 @@ format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags fl len = strlen(arg); value = ""; - } else if (!prefixcmp(next, "%(directory)")) { - value = opt_path; - - } else if (!prefixcmp(next, "%(file)")) { - value = opt_file; - - } else if (!prefixcmp(next, "%(ref)")) { - value = *opt_ref ? opt_ref : "HEAD"; - - } else if (!prefixcmp(next, "%(head)")) { - value = ref_head; - - } else if (!prefixcmp(next, "%(commit)")) { - value = ref_commit; - - } else if (!prefixcmp(next, "%(blob)")) { - value = ref_blob; - } else { - report("Unknown replacement: `%s`", next); - return FALSE; + value = format_arg(next); + + if (!value) { + report("Unknown replacement: `%s`", next); + return FALSE; + } } if (!string_format_from(buf, &bufpos, "%.*s%s", len, arg, value)) @@ -3039,7 +3080,7 @@ prepare_update_file(struct view *view, const char *name) { 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 @@ -3317,7 +3358,7 @@ open_mergetool(const char *file) } 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; @@ -3333,7 +3374,7 @@ open_editor(bool from_root, const char *file) 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 @@ -3523,7 +3564,7 @@ view_driver(struct view *view, enum request request) break; case REQ_TOGGLE_AUTHOR: - toggle_view_option(&opt_author, "author display"); + toggle_author(); break; case REQ_TOGGLE_REV_GRAPH: @@ -3677,7 +3718,13 @@ get_author(const char *name) } 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; @@ -3689,14 +3736,15 @@ parse_timezone(time_t *time, const char *zone) if (zone[0] == '-') tz = -tz; - *time -= tz; + time->tz = tz; + time->sec -= tz; } /* Parse author lines where the name may be empty: * author 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, '>'); @@ -3718,7 +3766,7 @@ parse_author_line(char *ident, const char **author, time_t *time) 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); @@ -4037,7 +4085,7 @@ help_open_keymap(struct view *view, enum keymap keymap) 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); @@ -4188,7 +4236,7 @@ push_tree_stack_entry(const char *name, unsigned long lineno) 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]; }; @@ -4227,7 +4275,7 @@ tree_compare(const void *l1, const void *l2) 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)); @@ -4264,7 +4312,7 @@ static bool 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; @@ -4411,7 +4459,7 @@ tree_draw(struct view *view, struct line *line, unsigned int lineno) 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)) @@ -4430,7 +4478,7 @@ open_blob_editor() 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); } @@ -4456,7 +4504,7 @@ tree_request(struct view *view, enum request request, struct line *line) } else if (!is_head_commit(view->vid)) { open_blob_editor(); } else { - open_editor(TRUE, opt_file); + open_editor(opt_file); } return REQ_NONE; @@ -4654,7 +4702,7 @@ struct blame_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. */ }; @@ -4841,7 +4889,7 @@ blame_read(struct view *view, char *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); @@ -4864,7 +4912,7 @@ static bool 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]; @@ -5056,7 +5104,7 @@ static struct view_ops blame_ops = { 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. */ }; @@ -5075,7 +5123,7 @@ branch_compare(const void *l1, const void *l2) 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)); @@ -5092,7 +5140,7 @@ branch_draw(struct view *view, struct line *line, unsigned int lineno) 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)) @@ -5408,7 +5456,7 @@ static const char *status_diff_files_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[] = { @@ -5916,7 +5964,7 @@ status_request(struct view *view, enum request request, struct line *line) return REQ_NONE; } - open_editor(status->status != '?', status->new.name); + open_editor(status->new.name); break; case REQ_VIEW_BLAME: @@ -6198,7 +6246,7 @@ stage_request(struct view *view, enum request request, struct line *line) return REQ_NONE; } - open_editor(stage_status.status != '?', stage_status.new.name); + open_editor(stage_status.new.name); break; case REQ_REFRESH: @@ -6265,7 +6313,7 @@ struct 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. */ @@ -6753,6 +6801,14 @@ static const unsigned char utf8_bytes[256] = { 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) @@ -6820,8 +6876,7 @@ utf8_length(const char **start, size_t skip, int *width, size_t max_width, int * *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;