X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=tig.c;h=cd3428cc38c29f783bdbc12800f3dd5ad26d86bf;hb=b206672a46f06a652fbc0028129c72cea438f9a3;hp=d2dff15fd17bcd60c6a56209a420f33c9fc37a5a;hpb=7ea608c5cb1c74066278a65ca09c4c837915a7b2;p=tig.git diff --git a/tig.c b/tig.c index d2dff15..cd3428c 100644 --- a/tig.c +++ b/tig.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2006-2009 Jonas Fonseca +/* Copyright (c) 2006-2010 Jonas Fonseca * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -68,8 +68,6 @@ static void __NORETURN die(const char *err, ...); 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); #define ABS(x) ((x) >= 0 ? (x) : -(x)) #define MIN(x, y) ((x) < (y) ? (x) : (y)) @@ -107,6 +105,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 @@ -136,13 +135,13 @@ struct ref_list { struct ref **refs; /* References for this ID. */ }; +static struct ref *get_ref_head(); static struct ref_list *get_ref_list(const char *id); static void foreach_ref(bool (*visitor)(void *data, const struct ref *ref), void *data); static int load_refs(void); enum format_flags { FORMAT_ALL, /* Perform replacement in all arguments. */ - FORMAT_DASH, /* Perform replacement up until "--". */ FORMAT_NONE /* No replacement should be performed. */ }; @@ -358,31 +357,165 @@ suffixcmp(const char *str, int slen, const char *suffix) /* - * What value of "tz" was in effect back then at "time" in the - * local timezone? + * Unicode / UTF-8 handling + * + * NOTE: Much of the following code for dealing with Unicode is derived from + * ELinks' UTF-8 code developed by Scrool . Origin file is + * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28. */ -static int local_tzoffset(time_t time) + +static inline int +unicode_width(unsigned long c, int tab_size) { - time_t t, t_local; - struct tm tm; - int offset, eastwest; + 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; - t = time; - localtime_r(&t, &tm); - t_local = mktime(&tm); + if (c == '\t') + return tab_size; - if (t_local < t) { - eastwest = -1; - offset = t - t_local; - } else { - eastwest = 1; - offset = t_local - t; + 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; } - offset /= 60; /* in minutes */ - offset = (offset % 60) + ((offset / 60) * 100); - return offset * eastwest; + + return string - *start; } + #define DATE_INFO \ DATE_(NO), \ DATE_(DEFAULT), \ @@ -401,8 +534,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) +mkdate(const struct time *time, enum date date) { static char buf[DATE_COLS + 1]; static const struct enum_map reldate[] = { @@ -415,9 +558,12 @@ string_date(const time_t *time, enum date date) }; struct tm tm; + if (!date || !time || !time->sec) + return ""; + 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 +583,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) { @@ -460,7 +660,7 @@ argv_from_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd) return *argc < SIZEOF_ARG; } -static void +static bool argv_from_env(const char **argv, const char *name) { char *env = argv ? getenv(name) : NULL; @@ -468,8 +668,7 @@ argv_from_env(const char **argv, const char *name) 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); } @@ -489,7 +688,7 @@ enum io_type { struct io { enum io_type type; /* The requested type of pipe. */ const char *dir; /* Directory from which to execute. */ - pid_t pid; /* Pipe for reading or writing. */ + pid_t pid; /* PID of spawned process. */ int pipe; /* Pipe end for reading or writing. */ int error; /* Error status. */ const char *argv[SIZEOF_ARG]; /* Shell command arguments. */ @@ -501,7 +700,7 @@ struct io { }; static void -reset_io(struct io *io) +io_reset(struct io *io) { io->pipe = -1; io->pid = 0; @@ -512,18 +711,18 @@ reset_io(struct io *io) } static void -init_io(struct io *io, const char *dir, enum io_type type) +io_init(struct io *io, const char *dir, enum io_type type) { - reset_io(io); + io_reset(io); io->type = type; io->dir = dir; } static bool -init_io_rd(struct io *io, const char *argv[], const char *dir, - enum format_flags flags) +io_format(struct io *io, const char *dir, enum io_type type, + const char *argv[], enum format_flags flags) { - init_io(io, dir, IO_RD); + io_init(io, dir, type); return format_argv(io->argv, argv, flags); } @@ -534,7 +733,7 @@ io_open(struct io *io, const char *fmt, ...) bool fits; va_list args; - init_io(io, NULL, IO_FD); + io_init(io, NULL, IO_FD); va_start(args, fmt); fits = vsnprintf(name, sizeof(name), fmt, args) < sizeof(name); @@ -551,20 +750,20 @@ io_open(struct io *io, const char *fmt, ...) } static bool -kill_io(struct io *io) +io_kill(struct io *io) { return io->pid == 0 || kill(io->pid, SIGKILL) != -1; } static bool -done_io(struct io *io) +io_done(struct io *io) { pid_t pid = io->pid; if (io->pipe != -1) close(io->pipe); free(io->buf); - reset_io(io); + io_reset(io); while (pid > 0) { int status; @@ -573,7 +772,7 @@ done_io(struct io *io) if (waiting < 0) { if (errno == EINTR) continue; - report("waitpid failed (%s)", strerror(errno)); + io->error = errno; return FALSE; } @@ -587,20 +786,23 @@ done_io(struct io *io) } static bool -start_io(struct io *io) +io_start(struct io *io) { int pipefds[2] = { -1, -1 }; if (io->type == IO_FD) return TRUE; - if ((io->type == IO_RD || io->type == IO_WR) && - pipe(pipefds) < 0) + if ((io->type == IO_RD || io->type == IO_WR) && pipe(pipefds) < 0) { + io->error = errno; return FALSE; - else if (io->type == IO_AP) + } else if (io->type == IO_AP) { pipefds[1] = io->pipe; + } if ((io->pid = fork())) { + if (io->pid == -1) + io->error = errno; if (pipefds[!(io->type == IO_WR)] != -1) close(pipefds[!(io->type == IO_WR)]); if (io->pid != -1) { @@ -627,10 +829,10 @@ start_io(struct io *io) } if (io->dir && *io->dir && chdir(io->dir) == -1) - die("Failed to change directory: %s", strerror(errno)); + exit(errno); execvp(io->argv[0], (char *const*) io->argv); - die("Failed to execute program: %s", strerror(errno)); + exit(errno); } if (pipefds[!!(io->type == IO_WR)] != -1) @@ -639,59 +841,58 @@ start_io(struct io *io) } static bool -run_io(struct io *io, const char **argv, const char *dir, enum io_type type) +io_run(struct io *io, const char **argv, const char *dir, enum io_type type) { - init_io(io, dir, type); + io_init(io, dir, type); if (!format_argv(io->argv, argv, FORMAT_NONE)) return FALSE; - return start_io(io); + return io_start(io); } static int -run_io_do(struct io *io) +io_complete(struct io *io) { - return start_io(io) && done_io(io); + return io_start(io) && io_done(io); } static int -run_io_bg(const char **argv) +io_run_bg(const char **argv) { struct io io = {}; - init_io(&io, NULL, IO_BG); - if (!format_argv(io.argv, argv, FORMAT_NONE)) + if (!io_format(&io, NULL, IO_BG, argv, FORMAT_NONE)) return FALSE; - return run_io_do(&io); + return io_complete(&io); } static bool -run_io_fg(const char **argv, const char *dir) +io_run_fg(const char **argv, const char *dir) { struct io io = {}; - init_io(&io, dir, IO_FG); - if (!format_argv(io.argv, argv, FORMAT_NONE)) + if (!io_format(&io, dir, IO_FG, argv, FORMAT_NONE)) return FALSE; - return run_io_do(&io); + return io_complete(&io); } static bool -run_io_append(const char **argv, enum format_flags flags, int fd) +io_run_append(const char **argv, enum format_flags flags, int fd) { struct io io = {}; - init_io(&io, NULL, IO_AP); + if (!io_format(&io, NULL, IO_AP, argv, flags)) { + close(fd); + return FALSE; + } + io.pipe = fd; - if (format_argv(io.argv, argv, flags)) - return run_io_do(&io); - close(fd); - return FALSE; + return io_complete(&io); } static bool -run_io_rd(struct io *io, const char **argv, const char *dir, enum format_flags flags) +io_run_rd(struct io *io, const char **argv, const char *dir, enum format_flags flags) { - return init_io_rd(io, argv, dir, flags) && start_io(io); + return io_format(io, dir, IO_RD, argv, flags) && io_start(io); } static bool @@ -740,7 +941,7 @@ io_read(struct io *io, void *buf, size_t bufsize) } while (1); } -DEFINE_ALLOCATOR(realloc_io_buf, char, BUFSIZ) +DEFINE_ALLOCATOR(io_realloc_buf, char, BUFSIZ) static char * io_get(struct io *io, int c, bool can_read) @@ -777,7 +978,7 @@ io_get(struct io *io, int c, bool can_read) memmove(io->buf, io->bufpos, io->bufsize); if (io->bufalloc == io->bufsize) { - if (!realloc_io_buf(&io->buf, io->bufalloc, BUFSIZ)) + if (!io_realloc_buf(&io->buf, io->bufalloc, BUFSIZ)) return NULL; io->bufalloc += BUFSIZ; } @@ -820,15 +1021,15 @@ io_read_buf(struct io *io, char buf[], size_t bufsize) string_ncopy_do(buf, bufsize, result, strlen(result)); } - return done_io(io) && result; + return io_done(io) && result; } static bool -run_io_buf(const char **argv, char buf[], size_t bufsize) +io_run_buf(const char **argv, char buf[], size_t bufsize) { struct io io = {}; - return run_io_rd(&io, argv, NULL, FORMAT_NONE) + return io_run_rd(&io, argv, NULL, FORMAT_NONE) && io_read_buf(&io, buf, bufsize); } @@ -839,7 +1040,7 @@ io_load(struct io *io, const char *separators, char *name; int state = OK; - if (!start_io(io)) + if (!io_start(io)) return ERR; while (state == OK && (name = io_get(io, '\n', TRUE))) { @@ -865,18 +1066,18 @@ io_load(struct io *io, const char *separators, if (state != ERR && io_error(io)) state = ERR; - done_io(io); + io_done(io); return state; } static int -run_io_load(const char **argv, const char *separators, +io_run_load(const char **argv, const char *separators, int (*read_property)(char *, size_t, char *, size_t)) { struct io io = {}; - return init_io_rd(&io, argv, NULL, FORMAT_NONE) + return io_format(&io, NULL, IO_RD, argv, FORMAT_NONE) ? io_load(&io, separators, read_property) : ERR; } @@ -1007,7 +1208,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,15 +1217,13 @@ 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] = ""; static char opt_head[SIZEOF_REF] = ""; -static char opt_head_rev[SIZEOF_REV] = ""; static char opt_remote[SIZEOF_REF] = ""; static char opt_encoding[20] = "UTF-8"; -static char opt_codeset[20] = "UTF-8"; static iconv_t opt_iconv_in = ICONV_NONE; static iconv_t opt_iconv_out = ICONV_NONE; static char opt_search[SIZEOF_STR] = ""; @@ -1035,9 +1234,8 @@ static signed char opt_is_inside_work_tree = -1; /* set to TRUE or FALSE */ static char opt_editor[SIZEOF_STR] = ""; static FILE *opt_tty = NULL; -#define is_initial_commit() (!*opt_head_rev) -#define is_head_commit(rev) (!strcmp((rev), "HEAD") || !strcmp(opt_head_rev, (rev))) -#define mkdate(time) string_date(time, opt_date) +#define is_initial_commit() (!get_ref_head()) +#define is_head_commit(rev) (!strcmp((rev), "HEAD") || (get_ref_head() && !strcmp(rev, get_ref_head()->id))) /* @@ -1531,6 +1729,7 @@ static void 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 { @@ -1541,6 +1740,7 @@ add_builtin_run_requests(void) } 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; @@ -1741,7 +1941,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); @@ -1958,6 +2158,7 @@ static unsigned int current_view; 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] = ""; struct view { const char *name; /* View name */ @@ -2075,19 +2276,11 @@ static struct view views[] = { (view == display[0] || view == display[1]) -enum line_graphic { - LINE_GRAPHIC_VLINE -}; - -static chtype line_graphics[] = { - /* LINE_GRAPHIC_VLINE: */ '|' -}; - static inline void set_view_attr(struct view *view, enum line_type type) { if (!view->curline->selected && view->curtype != type) { - wattrset(view->win, get_line_attr(type)); + (void) wattrset(view->win, get_line_attr(type)); wchgat(view->win, -1, 0, type, NULL); view->curtype = type; } @@ -2106,7 +2299,7 @@ draw_chars(struct view *view, enum line_type type, const char *string, if (max_len <= 0) return 0; - len = utf8_length(&string, skip, &col, max_len, &trimmed, use_tilde); + len = utf8_length(&string, skip, &col, max_len, &trimmed, use_tilde, opt_tab_size); set_view_attr(view, type); if (len > 0) { @@ -2203,9 +2396,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 = mkdate(time, opt_date); int cols = opt_date == DATE_SHORT ? DATE_SHORT_COLS : DATE_COLS; return draw_field(view, LINE_DATE, date, cols, FALSE); @@ -2214,25 +2407,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; - - 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); } @@ -2265,6 +2444,7 @@ draw_lineno(struct view *view, unsigned int lineno) int digits3 = view->digits < 3 ? 3 : view->digits; int max = MIN(view->width + view->yoffset - view->col, digits3); char *text = NULL; + chtype separator = opt_line_graphics ? ACS_VLINE : '|'; lineno += view->offset + 1; if (lineno == 1 || (lineno % opt_num_interval) == 0) { @@ -2278,7 +2458,7 @@ draw_lineno(struct view *view, unsigned int lineno) view->col += draw_chars(view, LINE_LINE_NUMBER, text, max, TRUE); else view->col += draw_space(view, LINE_LINE_NUMBER, max, digits3); - return draw_graphic(view, LINE_DEFAULT, &line_graphics[LINE_GRAPHIC_VLINE], 1); + return draw_graphic(view, LINE_DEFAULT, &separator, 1); } static bool @@ -2498,6 +2678,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 +2704,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); } @@ -2931,6 +3114,7 @@ format_arg(const char *name) FORMAT_VAR("%(head)", ref_head, ""), FORMAT_VAR("%(commit)", ref_commit, ""), FORMAT_VAR("%(blob)", ref_blob, ""), + FORMAT_VAR("%(branch)", ref_branch, ""), }; int i; @@ -2938,8 +3122,10 @@ format_arg(const char *name) 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; } + static bool format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags flags) { @@ -2959,8 +3145,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 = ""; @@ -2968,7 +3152,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; } } @@ -3020,17 +3203,15 @@ end_update(struct view *view, bool force) while (!view->ops->read(view, NULL)) if (!force) return; - set_nonblocking_input(FALSE); if (force) - kill_io(view->pipe); - done_io(view->pipe); + io_kill(view->pipe); + io_done(view->pipe); view->pipe = NULL; } static void setup_update(struct view *view, const char *vid) { - set_nonblocking_input(TRUE); reset_view(view); string_copy_rev(view->vid, vid); view->pipe = &view->io; @@ -3038,12 +3219,11 @@ setup_update(struct view *view, const char *vid) } 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 init_io_rd(&view->io, argv, dir, flags); + return io_format(&view->io, dir, IO_RD, argv, FORMAT_NONE); } static bool @@ -3051,7 +3231,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 @@ -3064,7 +3244,7 @@ begin_update(struct view *view, bool refresh) if (view->ops->prepare) { if (!view->ops->prepare(view)) return FALSE; - } else if (!init_io_rd(&view->io, view->ops->argv, NULL, FORMAT_ALL)) { + } else if (!io_format(&view->io, NULL, IO_RD, view->ops->argv, FORMAT_ALL)) { return FALSE; } @@ -3075,7 +3255,7 @@ begin_update(struct view *view, bool refresh) string_copy_rev(view->ref, view->id); } - if (!start_io(&view->io)) + if (!io_start(&view->io)) return FALSE; setup_update(view, view->id); @@ -3313,7 +3493,7 @@ open_external_viewer(const char *argv[], const char *dir) { def_prog_mode(); /* save current tty modes */ endwin(); /* restore original tty modes */ - run_io_fg(argv, dir); + io_run_fg(argv, dir); fprintf(stderr, "Press Enter to continue"); getc(opt_tty); reset_prog_mode(); @@ -3329,7 +3509,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; @@ -3345,7 +3525,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 @@ -3535,7 +3715,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: @@ -3689,7 +3869,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; @@ -3701,14 +3887,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, '>'); @@ -3730,7 +3917,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); @@ -3755,7 +3942,7 @@ open_commit_parent_menu(char buf[SIZEOF_STR], int *parents) for (i = 0; i < *parents; i++) { string_copy_rev(rev, &buf[SIZEOF_REV * i]); - if (!run_io_buf(revlist_argv, text, sizeof(text)) || + if (!io_run_buf(revlist_argv, text, sizeof(text)) || !(items[i].text = strdup(text))) { ok = FALSE; break; @@ -3782,7 +3969,7 @@ select_commit_parent(const char *id, char rev[SIZEOF_REV], const char *path) }; int parents; - if (!run_io_buf(revlist_argv, buf, sizeof(buf)) || + if (!io_run_buf(revlist_argv, buf, sizeof(buf)) || (parents = strlen(buf) / 40) < 0) { report("Failed to get parent information"); return FALSE; @@ -3825,7 +4012,7 @@ add_describe_ref(char *buf, size_t *bufpos, const char *commit_id, const char *s const char *describe_argv[] = { "git", "describe", commit_id, NULL }; char ref[SIZEOF_STR]; - if (!run_io_buf(describe_argv, ref, sizeof(ref)) || !*ref) + if (!io_run_buf(describe_argv, ref, sizeof(ref)) || !*ref) return TRUE; /* This is the only fatal call, since it can "corrupt" the buffer. */ @@ -4049,7 +4236,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); @@ -4200,7 +4387,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]; }; @@ -4239,7 +4426,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)); @@ -4276,7 +4463,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; @@ -4297,12 +4484,12 @@ tree_read_date(struct view *view, char *text, bool *read_date) return TRUE; } - if (!run_io_rd(&io, log_file, opt_cdup, FORMAT_NONE)) { + if (!io_run_rd(&io, log_file, opt_cdup, FORMAT_NONE)) { report("Failed to load tree data"); return TRUE; } - done_io(view->pipe); + io_done(view->pipe); view->io = io; *read_date = TRUE; return FALSE; @@ -4341,7 +4528,7 @@ tree_read_date(struct view *view, char *text, bool *read_date) } if (annotated == view->lines) - kill_io(view->pipe); + io_kill(view->pipe); } return TRUE; } @@ -4423,7 +4610,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)) @@ -4439,10 +4626,10 @@ open_blob_editor() if (fd == -1) report("Failed to create temporary file"); - else if (!run_io_append(blob_ops.argv, FORMAT_ALL, fd)) + else if (!io_run_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); } @@ -4468,7 +4655,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; @@ -4539,7 +4726,7 @@ tree_grep(struct view *view, struct line *line) const char *text[] = { entry->name, opt_author ? entry->author : "", - opt_date ? mkdate(&entry->time) : "", + mkdate(&entry->time, opt_date), NULL }; @@ -4585,7 +4772,7 @@ tree_prepare(struct view *view) opt_path[0] = 0; } - return init_io_rd(&view->io, view->ops->argv, opt_cdup, FORMAT_ALL); + return io_format(&view->io, opt_cdup, IO_RD, view->ops->argv, FORMAT_ALL); } static const char *tree_argv[SIZEOF_ARG] = { @@ -4666,7 +4853,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. */ }; @@ -4689,7 +4876,7 @@ blame_open(struct view *view) } if (*opt_ref || !io_open(&view->io, "%s%s", opt_cdup, opt_file)) { - if (!run_io_rd(&view->io, blame_cat_file_argv, opt_cdup, FORMAT_ALL)) + if (!io_run_rd(&view->io, blame_cat_file_argv, opt_cdup, FORMAT_ALL)) return FALSE; } @@ -4785,12 +4972,12 @@ blame_read_file(struct view *view, const char *line, bool *read_file) if (view->lines == 0 && !view->parent) die("No blame exist for %s", view->vid); - if (view->lines == 0 || !run_io_rd(&io, argv, opt_cdup, FORMAT_ALL)) { + if (view->lines == 0 || !io_run_rd(&io, argv, opt_cdup, FORMAT_ALL)) { report("Failed to load blame data"); return TRUE; } - done_io(view->pipe); + io_done(view->pipe); view->io = io; *read_file = FALSE; return FALSE; @@ -4853,7 +5040,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); @@ -4876,7 +5063,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]; @@ -4927,7 +5114,7 @@ setup_blame_parent_line(struct view *view, struct blame *blame) int blamed_lineno = -1; char *line; - if (!run_io(&io, diff_tree_argv, NULL, IO_RD)) + if (!io_run(&io, diff_tree_argv, NULL, IO_RD)) return; while ((line = io_get(&io, '\n', TRUE))) { @@ -4948,7 +5135,7 @@ setup_blame_parent_line(struct view *view, struct blame *blame) } } - done_io(&io); + io_done(&io); } static enum request @@ -5000,7 +5187,7 @@ blame_request(struct view *view, enum request request, struct line *line) 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; } @@ -5029,7 +5216,7 @@ blame_grep(struct view *view, struct line *line) commit ? commit->title : "", commit ? commit->id : "", commit && opt_author ? commit->author : "", - commit && opt_date ? mkdate(&commit->time) : "", + commit ? mkdate(&commit->time, opt_date) : "", NULL }; @@ -5068,7 +5255,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. */ }; @@ -5087,7 +5274,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)); @@ -5104,7 +5291,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)) @@ -5138,7 +5325,7 @@ branch_request(struct view *view, enum request request, struct line *line) }; 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; } @@ -5219,7 +5406,7 @@ branch_open(struct view *view) "--simplify-by-decoration", "--all", NULL }; - if (!run_io_rd(&view->io, branch_log, NULL, FORMAT_NONE)) { + if (!io_run_rd(&view->io, branch_log, NULL, FORMAT_NONE)) { report("Failed to load branch data"); return TRUE; } @@ -5253,6 +5440,7 @@ branch_select(struct view *view, struct line *line) 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 = { @@ -5339,7 +5527,7 @@ status_run(struct view *view, const char *argv[], char status, enum line_type ty char *buf; struct io io = {}; - if (!run_io(&io, argv, opt_cdup, IO_RD)) + if (!io_run(&io, argv, opt_cdup, IO_RD)) return FALSE; add_line_data(view, NULL, type); @@ -5398,14 +5586,14 @@ status_run(struct view *view, const char *argv[], char status, enum line_type ty if (io_error(&io)) { error_out: - done_io(&io); + io_done(&io); return FALSE; } if (!view->line[view->lines - 1].data) add_line_data(view, NULL, LINE_STAT_NONE); - done_io(&io); + io_done(&io); return TRUE; } @@ -5420,7 +5608,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[] = { @@ -5516,7 +5704,7 @@ status_open(struct view *view) add_line_data(view, NULL, LINE_STAT_HEAD); status_update_onbranch(); - run_io_bg(update_index_argv); + io_run_bg(update_index_argv); if (is_initial_commit()) { if (!status_run(view, status_list_no_head_argv, 'A', LINE_STAT_STAGED)) @@ -5622,7 +5810,7 @@ status_enter(struct view *view, struct line *line) "--", "/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[] = { @@ -5631,7 +5819,7 @@ status_enter(struct view *view, struct line *line) 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); } @@ -5648,7 +5836,7 @@ status_enter(struct view *view, struct line *line) "-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"; @@ -5734,11 +5922,11 @@ status_update_prepare(struct io *io, enum line_type type) switch (type) { case LINE_STAT_STAGED: - return run_io(io, staged_argv, opt_cdup, IO_WR); + return io_run(io, staged_argv, opt_cdup, IO_WR); case LINE_STAT_UNSTAGED: case LINE_STAT_UNTRACKED: - return run_io(io, others_argv, opt_cdup, IO_WR); + return io_run(io, others_argv, opt_cdup, IO_WR); default: die("line type %d not handled in switch", type); @@ -5784,7 +5972,7 @@ status_update_file(struct status *status, enum line_type type) return FALSE; result = status_update_write(&io, status, type); - return done_io(&io) && result; + return io_done(&io) && result; } static bool @@ -5821,7 +6009,7 @@ status_update_files(struct view *view, struct line *line) } string_copy(view->ref, buf); - return done_io(&io) && result; + return io_done(&io) && result; } static bool @@ -5884,13 +6072,13 @@ status_revert(struct status *status, enum line_type type, bool has_none) reset_argv[4] = NULL; } - if (!run_io_fg(reset_argv, opt_cdup)) + if (!io_run_fg(reset_argv, opt_cdup)) return FALSE; if (status->old.mode == 0 && status->new.mode == 0) return TRUE; } - return run_io_fg(checkout_argv, opt_cdup); + return io_run_fg(checkout_argv, opt_cdup); } return FALSE; @@ -5928,7 +6116,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: @@ -6077,15 +6265,15 @@ stage_apply_chunk(struct view *view, struct line *chunk, bool revert) apply_argv[argc++] = "-R"; apply_argv[argc++] = "-"; apply_argv[argc++] = NULL; - if (!run_io(&io, apply_argv, opt_cdup, IO_WR)) + if (!io_run(&io, apply_argv, opt_cdup, IO_WR)) return FALSE; if (!stage_diff_write(&io, diff_hdr, chunk) || !stage_diff_write(&io, chunk, view->line + view->lines)) chunk = NULL; - done_io(&io); - run_io_bg(update_index_argv); + io_done(&io); + io_run_bg(update_index_argv); return chunk ? TRUE : FALSE; } @@ -6210,7 +6398,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: @@ -6277,7 +6465,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. */ @@ -6404,9 +6592,7 @@ draw_rev_graph(struct rev_graph *graph) struct rev_filler *filler; size_t i; - if (opt_line_graphics) - fillers[DEFAULT].line = line_graphics[LINE_GRAPHIC_VLINE]; - + fillers[DEFAULT].line = opt_line_graphics ? ACS_VLINE : '|'; filler = &fillers[DEFAULT]; for (i = 0; i < graph->pos; i++) { @@ -6692,7 +6878,7 @@ main_grep(struct view *view, struct line *line) const char *text[] = { commit->title, opt_author ? commit->author : "", - opt_date ? mkdate(&commit->time) : "", + mkdate(&commit->time, opt_date), NULL }; @@ -6720,159 +6906,6 @@ static struct view_ops main_ops = { }; -/* - * Unicode / UTF-8 handling - * - * NOTE: Much of the following code for dealing with Unicode is derived from - * ELinks' UTF-8 code developed by Scrool . 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) -{ - 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 opt_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, -}; - -/* 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) -{ - 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) { - int c = *(unsigned char *) string; - unsigned char bytes = utf8_bytes[c]; - 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); - 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 */ @@ -6939,17 +6972,6 @@ report(const char *msg, ...) update_view_title(view); } -/* Controls when nodelay should be in effect when polling user input. */ -static void -set_nonblocking_input(bool loading) -{ - static unsigned int loading_views; - - if ((loading == FALSE && loading_views-- == 1) || - (loading == TRUE && loading_views++ == 0)) - nodelay(status_win, loading); -} - static void init_display(void) { @@ -6989,9 +7011,6 @@ init_display(void) wbkgdset(status_win, get_line_attr(LINE_STATUS)); TABSIZE = opt_tab_size; - if (opt_line_graphics) { - line_graphics[LINE_GRAPHIC_VLINE] = ACS_VLINE; - } term = getenv("XTERM_VERSION") ? NULL : getenv("COLORTERM"); if (term && !strcmp(term, "gnome-terminal")) { @@ -7020,6 +7039,7 @@ get_input(int prompt_position) { struct view *view; int i, key, cursor_y, cursor_x; + bool loading = FALSE; if (prompt_position) input_mode = TRUE; @@ -7031,6 +7051,8 @@ get_input(int prompt_position) use_scroll_redrawwin) redrawwin(view->win); view->has_scrolled = FALSE; + if (view->pipe) + loading = TRUE; } /* Update the cursor position. */ @@ -7047,6 +7069,7 @@ get_input(int prompt_position) /* Refresh, accept single keystroke of input */ doupdate(); + nodelay(status_win, loading); key = wgetch(status_win); /* wgetch() with nodelay() enabled returns ERR when @@ -7229,6 +7252,7 @@ static bool prompt_menu(const char *prompt, const struct menu_item *items, int * static struct ref **refs = NULL; static size_t refs_size = 0; +static struct ref *refs_head = NULL; static struct ref_list **ref_lists = NULL; static size_t ref_lists_size = 0; @@ -7266,6 +7290,12 @@ foreach_ref(bool (*visitor)(void *data, const struct ref *ref), void *data) break; } +static struct ref * +get_ref_head() +{ + return refs_head; +} + static struct ref_list * get_ref_list(const char *id) { @@ -7330,11 +7360,15 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen) } else if (!prefixcmp(name, "refs/heads/")) { namelen -= STRING_SIZE("refs/heads/"); name += STRING_SIZE("refs/heads/"); - head = !strncmp(opt_head, name, namelen); + if (!strncmp(opt_head, name, namelen)) + return OK; } else if (!strcmp(name, "HEAD")) { - string_ncopy(opt_head_rev, id, idlen); - return OK; + head = TRUE; + if (*opt_head) { + namelen = strlen(opt_head); + name = opt_head; + } } /* If we are reloading or it's an annotated tag, replace the @@ -7376,6 +7410,8 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen) ref->tracked = tracked; string_copy_rev(ref->id, id); + if (head) + refs_head = ref; return OK; } @@ -7392,24 +7428,26 @@ load_refs(void) 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; } if (!*opt_git_dir) return OK; - if (run_io_buf(head_argv, opt_head, sizeof(opt_head)) && + if (io_run_buf(head_argv, opt_head, sizeof(opt_head)) && !prefixcmp(opt_head, "refs/heads/")) { char *offset = opt_head + STRING_SIZE("refs/heads/"); memmove(opt_head, offset, strlen(offset) + 1); } + refs_head = NULL; for (i = 0; i < refs_size; i++) refs[i]->id[0] = 0; - if (run_io_load(ls_remote_argv, "\t", read_ref) == ERR) + if (io_run_load(ls_remote_argv, "\t", read_ref) == ERR) return ERR; /* Update the ref lists to reflect changes. */ @@ -7530,7 +7568,7 @@ load_git_config(void) { const char *config_list_argv[] = { "git", "config", "--list", NULL }; - return run_io_load(config_list_argv, "=", read_repo_config_option); + return io_run_load(config_list_argv, "=", read_repo_config_option); } static int @@ -7565,7 +7603,7 @@ load_repo_info(void) "--show-cdup", "--show-prefix", NULL }; - return run_io_load(rev_parse_argv, "=", read_repo_info); + return io_run_load(rev_parse_argv, "=", read_repo_info); } @@ -7696,7 +7734,7 @@ parse_options(int argc, const char *argv[]) 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; @@ -7705,6 +7743,7 @@ parse_options(int argc, const char *argv[]) int main(int argc, const char *argv[]) { + const char *codeset = "UTF-8"; enum request request = parse_options(argc, argv); struct view *view; size_t i; @@ -7713,9 +7752,7 @@ main(int argc, const char *argv[]) signal(SIGPIPE, SIG_IGN); if (setlocale(LC_ALL, "")) { - char *codeset = nl_langinfo(CODESET); - - string_ncopy(opt_codeset, codeset, strlen(codeset)); + codeset = nl_langinfo(CODESET); } if (load_repo_info() == ERR) @@ -7731,14 +7768,14 @@ main(int argc, const char *argv[]) if (!opt_git_dir[0] && request != REQ_VIEW_PAGER) die("Not a git repository"); - if (*opt_encoding && strcmp(opt_codeset, "UTF-8")) { + if (*opt_encoding && strcmp(codeset, "UTF-8")) { opt_iconv_in = iconv_open("UTF-8", opt_encoding); if (opt_iconv_in == ICONV_NONE) die("Failed to initialize character set conversion"); } - if (*opt_codeset && strcmp(opt_codeset, "UTF-8")) { - opt_iconv_out = iconv_open(opt_codeset, "UTF-8"); + if (codeset && strcmp(codeset, "UTF-8")) { + opt_iconv_out = iconv_open(codeset, "UTF-8"); if (opt_iconv_out == ICONV_NONE) die("Failed to initialize character set conversion"); } @@ -7747,7 +7784,9 @@ main(int argc, const char *argv[]) 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(); @@ -7790,7 +7829,7 @@ main(int argc, const char *argv[]) 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);