X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=tig.c;h=718e6c2d186254f5cca36a5fcee1742d7b9265ed;hb=609028fbfc199d0626bca8ba1917988aee2b4041;hp=903efdeb7fe264ba5312e0a7979cfcc99fe00c5f;hpb=5b1f4fe12444f406fc16d8cfa423710960cc30ce;p=tig.git diff --git a/tig.c b/tig.c index 903efde..718e6c2 100644 --- a/tig.c +++ b/tig.c @@ -11,114 +11,12 @@ * GNU General Public License for more details. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef TIG_VERSION -#define TIG_VERSION "unknown-version" -#endif - -#ifndef DEBUG -#define NDEBUG -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -/* ncurses(3): Must be defined to have extended wide-character functions. */ -#define _XOPEN_SOURCE_EXTENDED - -#ifdef HAVE_NCURSESW_NCURSES_H -#include -#else -#ifdef HAVE_NCURSES_NCURSES_H -#include -#else -#include -#endif -#endif - -#if __GNUC__ >= 3 -#define __NORETURN __attribute__((__noreturn__)) -#else -#define __NORETURN -#endif +#include "tig.h" static void __NORETURN die(const char *err, ...); static void warn(const char *msg, ...); static void report(const char *msg, ...); -#define ABS(x) ((x) >= 0 ? (x) : -(x)) -#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#define MAX(x, y) ((x) > (y) ? (x) : (y)) - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -#define STRING_SIZE(x) (sizeof(x) - 1) - -#define SIZEOF_STR 1024 /* Default string size. */ -#define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */ -#define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL. */ -#define SIZEOF_ARG 32 /* Default argument array size. */ - -/* Revision graph */ - -#define REVGRAPH_INIT 'I' -#define REVGRAPH_MERGE 'M' -#define REVGRAPH_BRANCH '+' -#define REVGRAPH_COMMIT '*' -#define REVGRAPH_BOUND '^' - -#define SIZEOF_REVGRAPH 19 /* Size of revision ancestry graphics. */ - -/* This color name can be used to refer to the default term colors. */ -#define COLOR_DEFAULT (-1) - -#define ICONV_NONE ((iconv_t) -1) -#ifndef ICONV_CONST -#define ICONV_CONST /* nothing */ -#endif - -/* The format and size of the date column in the main view. */ -#define DATE_FORMAT "%Y-%m-%d %H:%M" -#define DATE_COLS STRING_SIZE("2006-04-29 14:21 ") -#define DATE_SHORT_COLS STRING_SIZE("2006-04-29 ") - -#define ID_COLS 8 -#define AUTHOR_COLS 19 - -#define MIN_VIEW_HEIGHT 4 - -#define NULL_ID "0000000000000000000000000000000000000000" - -#define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000) - -/* Some ASCII-shorthands fitted into the ncurses namespace. */ -#define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */ -#define KEY_TAB '\t' -#define KEY_RETURN '\r' -#define KEY_ESC 27 - struct ref { char id[SIZEOF_REV]; /* Commit SHA1 ID */ @@ -161,356 +59,6 @@ struct menu_item { static bool prompt_menu(const char *prompt, const struct menu_item *items, int *selected); -/* - * Allocation helpers ... Entering macro hell to never be seen again. - */ - -#define DEFINE_ALLOCATOR(name, type, chunk_size) \ -static type * \ -name(type **mem, size_t size, size_t increase) \ -{ \ - size_t num_chunks = (size + chunk_size - 1) / chunk_size; \ - size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\ - type *tmp = *mem; \ - \ - if (mem == NULL || num_chunks != num_chunks_new) { \ - tmp = realloc(tmp, num_chunks_new * chunk_size * sizeof(type)); \ - if (tmp) \ - *mem = tmp; \ - } \ - \ - return tmp; \ -} - -/* - * String helpers - */ - -static inline void -string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen) -{ - if (srclen > dstlen - 1) - srclen = dstlen - 1; - - strncpy(dst, src, srclen); - dst[srclen] = 0; -} - -/* Shorthands for safely copying into a fixed buffer. */ - -#define string_copy(dst, src) \ - string_ncopy_do(dst, sizeof(dst), src, sizeof(src)) - -#define string_ncopy(dst, src, srclen) \ - string_ncopy_do(dst, sizeof(dst), src, srclen) - -#define string_copy_rev(dst, src) \ - string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1) - -#define string_add(dst, from, src) \ - string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src)) - -static size_t -string_expand(char *dst, size_t dstlen, const char *src, int tabsize) -{ - size_t size, pos; - - for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) { - if (src[pos] == '\t') { - size_t expanded = tabsize - (size % tabsize); - - if (expanded + size >= dstlen - 1) - expanded = dstlen - size - 1; - memcpy(dst + size, " ", expanded); - size += expanded; - } else { - dst[size++] = src[pos]; - } - } - - dst[size] = 0; - return pos; -} - -static char * -chomp_string(char *name) -{ - int namelen; - - while (isspace(*name)) - name++; - - namelen = strlen(name) - 1; - while (namelen > 0 && isspace(name[namelen])) - name[namelen--] = 0; - - return name; -} - -static bool -string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...) -{ - va_list args; - size_t pos = bufpos ? *bufpos : 0; - - va_start(args, fmt); - pos += vsnprintf(buf + pos, bufsize - pos, fmt, args); - va_end(args); - - if (bufpos) - *bufpos = pos; - - return pos >= bufsize ? FALSE : TRUE; -} - -#define string_format(buf, fmt, args...) \ - string_nformat(buf, sizeof(buf), NULL, fmt, args) - -#define string_format_from(buf, from, fmt, args...) \ - string_nformat(buf, sizeof(buf), from, fmt, args) - -static int -string_enum_compare(const char *str1, const char *str2, int len) -{ - size_t i; - -#define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.') - - /* Diff-Header == DIFF_HEADER */ - for (i = 0; i < len; i++) { - if (toupper(str1[i]) == toupper(str2[i])) - continue; - - if (string_enum_sep(str1[i]) && - string_enum_sep(str2[i])) - continue; - - return str1[i] - str2[i]; - } - - return 0; -} - -#define enum_equals(entry, str, len) \ - ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len)) - -struct enum_map { - const char *name; - int namelen; - int value; -}; - -#define ENUM_MAP(name, value) { name, STRING_SIZE(name), value } - -static char * -enum_map_name(const char *name, size_t namelen) -{ - static char buf[SIZEOF_STR]; - int bufpos; - - for (bufpos = 0; bufpos <= namelen; bufpos++) { - buf[bufpos] = tolower(name[bufpos]); - if (buf[bufpos] == '_') - buf[bufpos] = '-'; - } - - buf[bufpos] = 0; - return buf; -} - -#define enum_name(entry) enum_map_name((entry).name, (entry).namelen) - -static bool -map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name) -{ - size_t namelen = strlen(name); - int i; - - for (i = 0; i < map_size; i++) - if (enum_equals(map[i], name, namelen)) { - *value = map[i].value; - return TRUE; - } - - return FALSE; -} - -#define map_enum(attr, map, name) \ - map_enum_do(map, ARRAY_SIZE(map), attr, name) - -#define prefixcmp(str1, str2) \ - strncmp(str1, str2, STRING_SIZE(str2)) - -static inline int -suffixcmp(const char *str, int slen, const char *suffix) -{ - size_t len = slen >= 0 ? slen : strlen(str); - size_t suffixlen = strlen(suffix); - - return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1; -} - - -/* - * 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, 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), \ @@ -1044,9 +592,11 @@ io_run_buf(const char **argv, char buf[], size_t bufsize) return io_run(&io, IO_RD, NULL, argv) && io_read_buf(&io, buf, bufsize); } +typedef int (*io_read_fn)(char *, size_t, char *, size_t, void *data); + static int io_load(struct io *io, const char *separators, - int (*read_property)(char *, size_t, char *, size_t)) + io_read_fn read_property, void *data) { char *name; int state = OK; @@ -1069,7 +619,7 @@ io_load(struct io *io, const char *separators, valuelen = 0; } - state = read_property(name, namelen, value, valuelen); + state = read_property(name, namelen, value, valuelen, data); } if (state != ERR && io_error(io)) @@ -1081,13 +631,13 @@ io_load(struct io *io, const char *separators, static int io_run_load(const char **argv, const char *separators, - int (*read_property)(char *, size_t, char *, size_t)) + io_read_fn read_property, void *data) { struct io io; if (!io_run(&io, IO_RD, NULL, argv)) return ERR; - return io_load(&io, separators, read_property); + return io_load(&io, separators, read_property, data); } @@ -1154,7 +704,6 @@ io_run_load(const char **argv, const char *separators, REQ_(OPTIONS, "Open option menu"), \ REQ_(TOGGLE_LINENO, "Toggle line numbers"), \ REQ_(TOGGLE_DATE, "Toggle date display"), \ - REQ_(TOGGLE_DATE_SHORT, "Toggle short (date-only) dates"), \ REQ_(TOGGLE_AUTHOR, "Toggle author display"), \ REQ_(TOGGLE_REV_GRAPH, "Toggle revision graph visualization"), \ REQ_(TOGGLE_REFS, "Toggle reference display (tags/branches)"), \ @@ -1224,6 +773,7 @@ static bool opt_line_number = FALSE; static bool opt_line_graphics = TRUE; static bool opt_rev_graph = FALSE; static bool opt_show_refs = TRUE; +static bool opt_untracked_dirs_content = TRUE; static int opt_num_interval = 5; static double opt_hscroll = 0.50; static double opt_scale_split_view = 2.0 / 3.0; @@ -1244,9 +794,9 @@ static char opt_git_dir[SIZEOF_STR] = ""; 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; -static const char **opt_diff_args = NULL; -static const char **opt_rev_args = NULL; -static const char **opt_file_args = NULL; +static const char **opt_diff_argv = NULL; +static const char **opt_rev_argv = NULL; +static const char **opt_file_argv = NULL; #define is_initial_commit() (!get_ref_head()) #define is_head_commit(rev) (!strcmp((rev), "HEAD") || (get_ref_head() && !strcmp(rev, get_ref_head()->id))) @@ -1452,6 +1002,7 @@ static struct keybinding default_keybindings[] = { { KEY_HOME, REQ_MOVE_FIRST_LINE }, { KEY_END, REQ_MOVE_LAST_LINE }, { KEY_NPAGE, REQ_MOVE_PAGE_DOWN }, + { KEY_CTL('D'), REQ_MOVE_PAGE_DOWN }, { ' ', REQ_MOVE_PAGE_DOWN }, { KEY_PPAGE, REQ_MOVE_PAGE_UP }, { KEY_CTL('U'), REQ_MOVE_PAGE_UP }, @@ -1799,9 +1350,36 @@ add_builtin_run_requests(void) * User config file handling. */ -static int config_lineno; -static bool config_errors; -static const char *config_msg; +#define OPT_ERR_INFO \ + OPT_ERR_(INTEGER_VALUE_OUT_OF_BOUND, "Integer value out of bound"), \ + OPT_ERR_(INVALID_STEP_VALUE, "Invalid step value"), \ + OPT_ERR_(NO_OPTION_VALUE, "No option value"), \ + OPT_ERR_(NO_VALUE_ASSIGNED, "No value assigned"), \ + OPT_ERR_(OBSOLETE_REQUEST_NAME, "Obsolete request name"), \ + OPT_ERR_(TOO_MANY_OPTION_ARGUMENTS, "Too many option arguments"), \ + OPT_ERR_(UNKNOWN_ATTRIBUTE, "Unknown attribute"), \ + OPT_ERR_(UNKNOWN_COLOR, "Unknown color"), \ + OPT_ERR_(UNKNOWN_COLOR_NAME, "Unknown color name"), \ + OPT_ERR_(UNKNOWN_KEY, "Unknown key"), \ + OPT_ERR_(UNKNOWN_KEY_MAP, "Unknown key map"), \ + OPT_ERR_(UNKNOWN_OPTION_COMMAND, "Unknown option command"), \ + OPT_ERR_(UNKNOWN_REQUEST_NAME, "Unknown request name"), \ + OPT_ERR_(UNKNOWN_VARIABLE_NAME, "Unknown variable name"), \ + OPT_ERR_(UNMATCHED_QUOTATION, "Unmatched quotation"), \ + OPT_ERR_(WRONG_NUMBER_OF_ARGUMENTS, "Wrong number of arguments"), + +enum option_code { +#define OPT_ERR_(name, msg) OPT_ERR_ ## name + OPT_ERR_INFO +#undef OPT_ERR_ + OPT_OK +}; + +static const char *option_errors[] = { +#define OPT_ERR_(name, msg) msg + OPT_ERR_INFO +#undef OPT_ERR_ +}; static const struct enum_map color_map[] = { #define COLOR_MAP(name) ENUM_MAP(#name, COLOR_##name) @@ -1829,39 +1407,37 @@ static const struct enum_map attr_map[] = { #define set_attribute(attr, name) map_enum(attr, attr_map, name) -static int parse_step(double *opt, const char *arg) +static enum option_code +parse_step(double *opt, const char *arg) { *opt = atoi(arg); if (!strchr(arg, '%')) - return OK; + return OPT_OK; /* "Shift down" so 100% and 1 does not conflict. */ *opt = (*opt - 1) / 100; if (*opt >= 1.0) { *opt = 0.99; - config_msg = "Step value larger than 100%"; - return ERR; + return OPT_ERR_INVALID_STEP_VALUE; } if (*opt < 0.0) { *opt = 1; - config_msg = "Invalid step value"; - return ERR; + return OPT_ERR_INVALID_STEP_VALUE; } - return OK; + return OPT_OK; } -static int +static enum option_code parse_int(int *opt, const char *arg, int min, int max) { int value = atoi(arg); if (min <= value && value <= max) { *opt = value; - return OK; + return OPT_OK; } - config_msg = "Integer value out of bound"; - return ERR; + return OPT_ERR_INTEGER_VALUE_OUT_OF_BOUND; } static bool @@ -1875,15 +1451,13 @@ set_color(int *color, const char *name) } /* Wants: object fgcolor bgcolor [attribute] */ -static int +static enum option_code option_color_command(int argc, const char *argv[]) { struct line_info *info; - if (argc < 3) { - config_msg = "Wrong number of arguments given to color command"; - return ERR; - } + if (argc < 3) + return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS; info = get_line_info(argv[0]); if (!info) { @@ -1894,61 +1468,55 @@ option_color_command(int argc, const char *argv[]) }; int index; - if (!map_enum(&index, obsolete, argv[0])) { - config_msg = "Unknown color name"; - return ERR; - } + if (!map_enum(&index, obsolete, argv[0])) + return OPT_ERR_UNKNOWN_COLOR_NAME; info = &line_info[index]; } if (!set_color(&info->fg, argv[1]) || - !set_color(&info->bg, argv[2])) { - config_msg = "Unknown color"; - return ERR; - } + !set_color(&info->bg, argv[2])) + return OPT_ERR_UNKNOWN_COLOR; info->attr = 0; while (argc-- > 3) { int attr; - if (!set_attribute(&attr, argv[argc])) { - config_msg = "Unknown attribute"; - return ERR; - } + if (!set_attribute(&attr, argv[argc])) + return OPT_ERR_UNKNOWN_ATTRIBUTE; info->attr |= attr; } - return OK; + return OPT_OK; } -static int parse_bool(bool *opt, const char *arg) +static enum option_code +parse_bool(bool *opt, const char *arg) { *opt = (!strcmp(arg, "1") || !strcmp(arg, "true") || !strcmp(arg, "yes")) ? TRUE : FALSE; - return OK; + return OPT_OK; } -static int parse_enum_do(unsigned int *opt, const char *arg, - const struct enum_map *map, size_t map_size) +static enum option_code +parse_enum_do(unsigned int *opt, const char *arg, + const struct enum_map *map, size_t map_size) { bool is_true; assert(map_size > 1); if (map_enum_do(map, map_size, (int *) opt, arg)) - return OK; - - if (parse_bool(&is_true, arg) != OK) - return ERR; + return OPT_OK; + parse_bool(&is_true, arg); *opt = is_true ? map[1].value : map[0].value; - return OK; + return OPT_OK; } #define parse_enum(opt, arg, map) \ parse_enum_do(opt, arg, map, ARRAY_SIZE(map)) -static int +static enum option_code parse_string(char *opt, const char *arg, size_t optsize) { int arglen = strlen(arg); @@ -1956,30 +1524,24 @@ parse_string(char *opt, const char *arg, size_t optsize) switch (arg[0]) { case '\"': case '\'': - if (arglen == 1 || arg[arglen - 1] != arg[0]) { - config_msg = "Unmatched quotation"; - return ERR; - } + if (arglen == 1 || arg[arglen - 1] != arg[0]) + return OPT_ERR_UNMATCHED_QUOTATION; arg += 1; arglen -= 2; default: string_ncopy_do(opt, optsize, arg, arglen); - return OK; + return OPT_OK; } } /* Wants: name = value */ -static int +static enum option_code option_set_command(int argc, const char *argv[]) { - if (argc != 3) { - config_msg = "Wrong number of arguments given to set command"; - return ERR; - } + if (argc != 3) + return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS; - if (strcmp(argv[1], "=")) { - config_msg = "No value assigned"; - return ERR; - } + if (strcmp(argv[1], "=")) + return OPT_ERR_NO_VALUE_ASSIGNED; if (!strcmp(argv[0], "show-author")) return parse_enum(&opt_author, argv[2], author_map); @@ -2017,33 +1579,29 @@ option_set_command(int argc, const char *argv[]) if (!strcmp(argv[0], "commit-encoding")) return parse_string(opt_encoding, argv[2], sizeof(opt_encoding)); - config_msg = "Unknown variable name"; - return ERR; + if (!strcmp(argv[0], "status-untracked-dirs")) + return parse_bool(&opt_untracked_dirs_content, argv[2]); + + return OPT_ERR_UNKNOWN_VARIABLE_NAME; } /* Wants: mode request key */ -static int +static enum option_code option_bind_command(int argc, const char *argv[]) { enum request request; int keymap = -1; int key; - if (argc < 3) { - config_msg = "Wrong number of arguments given to bind command"; - return ERR; - } + if (argc < 3) + return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS; - if (!set_keymap(&keymap, argv[0])) { - config_msg = "Unknown key map"; - return ERR; - } + if (!set_keymap(&keymap, argv[0])) + return OPT_ERR_UNKNOWN_KEY_MAP; key = get_key_value(argv[1]); - if (key == ERR) { - config_msg = "Unknown key"; - return ERR; - } + if (key == ERR) + return OPT_ERR_UNKNOWN_KEY; request = get_request(argv[2]); if (request == REQ_UNKNOWN) { @@ -2057,32 +1615,27 @@ option_bind_command(int argc, const char *argv[]) if (map_enum(&alias, obsolete, argv[2])) { if (alias != REQ_NONE) add_keybinding(keymap, alias, key); - config_msg = "Obsolete request name"; - return ERR; + return OPT_ERR_OBSOLETE_REQUEST_NAME; } } if (request == REQ_UNKNOWN && *argv[2]++ == '!') request = add_run_request(keymap, key, argv + 2); - if (request == REQ_UNKNOWN) { - config_msg = "Unknown request name"; - return ERR; - } + if (request == REQ_UNKNOWN) + return OPT_ERR_UNKNOWN_REQUEST_NAME; add_keybinding(keymap, request, key); - return OK; + return OPT_OK; } -static int +static enum option_code set_option(const char *opt, char *value) { const char *argv[SIZEOF_ARG]; int argc = 0; - if (!argv_from_string(argv, &argc, value)) { - config_msg = "Too many option arguments"; - return ERR; - } + if (!argv_from_string(argv, &argc, value)) + return OPT_ERR_TOO_MANY_OPTION_ARGUMENTS; if (!strcmp(opt, "color")) return option_color_command(argc, argv); @@ -2093,17 +1646,21 @@ set_option(const char *opt, char *value) if (!strcmp(opt, "bind")) return option_bind_command(argc, argv); - config_msg = "Unknown option command"; - return ERR; + return OPT_ERR_UNKNOWN_OPTION_COMMAND; } +struct config_state { + int lineno; + bool errors; +}; + static int -read_option(char *opt, size_t optlen, char *value, size_t valuelen) +read_option(char *opt, size_t optlen, char *value, size_t valuelen, void *data) { - int status = OK; + struct config_state *config = data; + enum option_code status = OPT_ERR_NO_OPTION_VALUE; - config_lineno++; - config_msg = "Internal error"; + config->lineno++; /* Check for comment markers, since read_properties() will * only ensure opt and value are split at first " \t". */ @@ -2111,11 +1668,7 @@ read_option(char *opt, size_t optlen, char *value, size_t valuelen) if (optlen == 0) return OK; - if (opt[optlen] != 0) { - config_msg = "No option value"; - status = ERR; - - } else { + if (opt[optlen] == 0) { /* Look for comment endings in the value. */ size_t len = strcspn(value, "#"); @@ -2127,10 +1680,10 @@ read_option(char *opt, size_t optlen, char *value, size_t valuelen) status = set_option(opt, value); } - if (status == ERR) { + if (status != OPT_OK) { warn("Error on line %d, near '%.*s': %s", - config_lineno, (int) optlen, opt, config_msg); - config_errors = TRUE; + config->lineno, (int) optlen, opt, option_errors[status]); + config->errors = TRUE; } /* Always keep going if errors are encountered. */ @@ -2140,17 +1693,15 @@ read_option(char *opt, size_t optlen, char *value, size_t valuelen) static void load_option_file(const char *path) { + struct config_state config = { 0, FALSE }; struct io io; /* It's OK that the file doesn't exist. */ if (!io_open(&io, "%s", path)) return; - config_lineno = 0; - config_errors = FALSE; - - if (io_load(&io, " \t", read_option) == ERR || - config_errors == TRUE) + if (io_load(&io, " \t", read_option, &config) == ERR || + config.errors == TRUE) warn("Errors while loading %s.", path); } @@ -2178,14 +1729,14 @@ load_options(void) * that conflict with keybindings. */ add_builtin_run_requests(); - if (!opt_diff_args && tig_diff_opts && *tig_diff_opts) { + if (!opt_diff_argv && tig_diff_opts && *tig_diff_opts) { static const char *diff_opts[SIZEOF_ARG] = { NULL }; int argc = 0; if (!string_format(buf, "%s", tig_diff_opts) || !argv_from_string(diff_opts, &argc, buf)) die("TIG_DIFF_OPTS contains too many arguments"); - else if (!argv_copy(&opt_diff_args, diff_opts)) + else if (!argv_copy(&opt_diff_argv, diff_opts)) die("Failed to format TIG_DIFF_OPTS arguments"); } @@ -2202,6 +1753,8 @@ struct view_ops; /* The display array of active views and the index of the current view. */ static struct view *display[2]; +static WINDOW *display_win[2]; +static WINDOW *display_title[2]; static unsigned int current_view; #define foreach_displayed_view(view, i) \ @@ -2245,7 +1798,6 @@ struct view { int height, width; /* The width and height of the main window */ WINDOW *win; /* The main window */ - WINDOW *title; /* The title window living below the main window */ /* Navigation */ unsigned long offset; /* Offset of the window top */ @@ -2403,11 +1955,12 @@ draw_chars(struct view *view, enum line_type type, const char *string, } waddnstr(view->win, string, len); - } - if (trimmed && use_tilde) { - set_view_attr(view, LINE_DELIMITER); - waddch(view->win, '~'); - col++; + + if (trimmed && use_tilde) { + set_view_attr(view, LINE_DELIMITER); + waddch(view->win, '~'); + col++; + } } return col; @@ -2432,14 +1985,14 @@ draw_space(struct view *view, enum line_type type, int max, int spaces) } static bool -draw_text(struct view *view, enum line_type type, const char *string, bool trim) +draw_text(struct view *view, enum line_type type, const char *string) { char text[SIZEOF_STR]; do { size_t pos = string_expand(text, sizeof(text), string, opt_tab_size); - view->col += draw_chars(view, type, text, view->width + view->yoffset - view->col, trim); + view->col += draw_chars(view, type, text, view->width + view->yoffset - view->col, TRUE); string += pos; } while (*string && view->width + view->yoffset > view->col); @@ -2631,6 +2184,7 @@ update_view_title(struct view *view) char buf[SIZEOF_STR]; char state[SIZEOF_STR]; size_t bufpos = 0, statelen = 0; + WINDOW *window = display[0] == view ? display_title[0] : display_title[1]; assert(view_is_displayed(view)); @@ -2671,13 +2225,13 @@ update_view_title(struct view *view) } if (view == display[current_view]) - wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS)); + wbkgdset(window, get_line_attr(LINE_TITLE_FOCUS)); else - wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR)); + wbkgdset(window, get_line_attr(LINE_TITLE_BLUR)); - mvwaddnstr(view->title, 0, 0, buf, bufpos); - wclrtoeol(view->title); - wnoutrefresh(view->title); + mvwaddnstr(window, 0, 0, buf, bufpos); + wclrtoeol(window); + wnoutrefresh(window); } static int @@ -2721,23 +2275,25 @@ resize_display(void) offset = 0; foreach_displayed_view (view, i) { - if (!view->win) { - view->win = newwin(view->height, 0, offset, 0); - if (!view->win) + if (!display_win[i]) { + display_win[i] = newwin(view->height, view->width, offset, 0); + if (!display_win[i]) die("Failed to create %s view", view->name); - scrollok(view->win, FALSE); + scrollok(display_win[i], FALSE); - view->title = newwin(1, 0, offset + view->height, 0); - if (!view->title) + display_title[i] = newwin(1, view->width, offset + view->height, 0); + if (!display_title[i]) die("Failed to create title window"); } else { - wresize(view->win, view->height, view->width); - mvwin(view->win, offset, 0); - mvwin(view->title, offset + view->height, 0); + wresize(display_win[i], view->height, view->width); + mvwin(display_win[i], offset, 0); + mvwin(display_title[i], offset + view->height, 0); } + view->win = display_win[i]; + offset += view->height + 1; } } @@ -2761,49 +2317,56 @@ redraw_display(bool clear) * Option management */ -static void -toggle_enum_option_do(unsigned int *opt, const char *help, - const struct enum_map *map, size_t size) -{ - *opt = (*opt + 1) % size; - redraw_display(FALSE); - report("Displaying %s %s", enum_name(map[*opt]), help); -} - -#define toggle_enum_option(opt, help, 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) +#define TOGGLE_MENU \ + TOGGLE_(LINENO, '.', "line numbers", &opt_line_number, NULL) \ + TOGGLE_(DATE, 'D', "dates", &opt_date, date_map) \ + TOGGLE_(AUTHOR, 'A', "author names", &opt_author, author_map) \ + TOGGLE_(REV_GRAPH, 'g', "revision graph", &opt_rev_graph, NULL) \ + TOGGLE_(REFS, 'F', "reference display", &opt_show_refs, NULL) static void -toggle_view_option(bool *option, const char *help) -{ - *option = !*option; - redraw_display(FALSE); - report("%sabling %s", *option ? "En" : "Dis", help); -} - -static void -open_option_menu(void) -{ +toggle_option(enum request request) +{ + const struct { + enum request request; + const struct enum_map *map; + size_t map_size; + } data[] = { +#define TOGGLE_(id, key, help, value, map) { REQ_TOGGLE_ ## id, map, ARRAY_SIZE(map) }, + TOGGLE_MENU +#undef TOGGLE_ + }; const struct menu_item menu[] = { - { '.', "line numbers", &opt_line_number }, - { 'D', "date display", &opt_date }, - { 'A', "author display", &opt_author }, - { 'g', "revision graph display", &opt_rev_graph }, - { 'F', "reference display", &opt_show_refs }, +#define TOGGLE_(id, key, help, value, map) { key, help, value }, + TOGGLE_MENU +#undef TOGGLE_ { 0 } }; - int selected = 0; + int i = 0; - 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); + if (request == REQ_OPTIONS) { + if (!prompt_menu("Toggle option", menu, &i)) + return; + } else { + while (i < ARRAY_SIZE(data) && data[i].request != request) + i++; + if (i >= ARRAY_SIZE(data)) + die("Invalid request (%d)", request); + } + + if (data[i].map != NULL) { + unsigned int *opt = menu[i].data; + + *opt = (*opt + 1) % data[i].map_size; + redraw_display(FALSE); + report("Displaying %s %s", enum_name(data[i].map[*opt]), menu[i].text); + + } else { + bool *option = menu[i].data; + + *option = !*option; + redraw_display(FALSE); + report("%sabling %s", *option ? "En" : "Dis", menu[i].text); } } @@ -3219,7 +2782,7 @@ format_arg(const char *name) } static bool -format_argv(const char ***dst_argv, const char *src_argv[], bool replace) +format_argv(const char ***dst_argv, const char *src_argv[], bool replace, bool first) { char buf[SIZEOF_STR]; int argc; @@ -3231,17 +2794,18 @@ format_argv(const char ***dst_argv, const char *src_argv[], bool replace) size_t bufpos = 0; if (!strcmp(arg, "%(fileargs)")) { - if (!argv_append_array(dst_argv, opt_file_args)) + if (!argv_append_array(dst_argv, opt_file_argv)) break; continue; } else if (!strcmp(arg, "%(diffargs)")) { - if (!argv_append_array(dst_argv, opt_diff_args)) + if (!argv_append_array(dst_argv, opt_diff_argv)) break; continue; - } else if (!strcmp(arg, "%(revargs)")) { - if (!argv_append_array(dst_argv, opt_rev_args)) + } else if (!strcmp(arg, "%(revargs)") || + (first && !strcmp(arg, "%(commit)"))) { + if (!argv_append_array(dst_argv, opt_rev_argv)) break; continue; } @@ -3326,7 +2890,7 @@ static bool prepare_io(struct view *view, const char *dir, const char *argv[], bool replace) { view->dir = dir; - return format_argv(&view->argv, argv, replace); + return format_argv(&view->argv, argv, replace, !view->prev); } static bool @@ -3663,7 +3227,7 @@ open_run_request(enum request request) return; } - if (format_argv(&argv, req->argv, TRUE)) + if (format_argv(&argv, req->argv, TRUE, FALSE)) open_external_viewer(argv, NULL); if (argv) argv_free(argv); @@ -3817,27 +3381,12 @@ view_driver(struct view *view, enum request request) break; case REQ_OPTIONS: - open_option_menu(); - break; - case REQ_TOGGLE_LINENO: - toggle_view_option(&opt_line_number, "line numbers"); - break; - case REQ_TOGGLE_DATE: - toggle_date(); - break; - case REQ_TOGGLE_AUTHOR: - toggle_author(); - break; - case REQ_TOGGLE_REV_GRAPH: - toggle_view_option(&opt_rev_graph, "revision graph display"); - break; - case REQ_TOGGLE_REFS: - toggle_view_option(&opt_show_refs, "reference display"); + toggle_option(request); break; case REQ_TOGGLE_SORT_FIELD: @@ -4046,7 +3595,7 @@ pager_draw(struct view *view, struct line *line, unsigned int lineno) if (opt_line_number && draw_lineno(view, lineno)) return TRUE; - draw_text(view, line->type, line->data, TRUE); + draw_text(view, line->type, line->data); return TRUE; } @@ -4229,9 +3778,9 @@ diff_read(struct view *view, char *data) { if (!data) { /* Fall back to retry if no diff will be shown. */ - if (view->lines == 0 && opt_file_args) { + if (view->lines == 0 && opt_file_argv) { int pos = argv_size(view->argv) - - argv_size(opt_file_args) - 1; + - argv_size(opt_file_argv) - 1; if (pos > 0 && !strcmp(view->argv[pos], "--")) { for (; view->argv[pos]; pos++) { @@ -4670,7 +4219,7 @@ tree_draw(struct view *view, struct line *line, unsigned int lineno) struct tree_entry *entry = line->data; if (line->type == LINE_TREE_HEAD) { - if (draw_text(view, line->type, "Directory path /", TRUE)) + if (draw_text(view, line->type, "Directory path /")) return TRUE; } else { if (draw_mode(view, entry->mode)) @@ -4682,8 +4231,8 @@ tree_draw(struct view *view, struct line *line, unsigned int lineno) if (opt_date && draw_date(view, &entry->time)) return TRUE; } - if (draw_text(view, line->type, entry->name, TRUE)) - return TRUE; + + draw_text(view, line->type, entry->name); return TRUE; } @@ -5172,7 +4721,7 @@ blame_draw(struct view *view, struct line *line, unsigned int lineno) if (draw_lineno(view, lineno)) return TRUE; - draw_text(view, LINE_DEFAULT, blame->text, TRUE); + draw_text(view, LINE_DEFAULT, blame->text); return TRUE; } @@ -5390,7 +4939,7 @@ branch_draw(struct view *view, struct line *line, unsigned int lineno) if (opt_author && draw_author(view, branch->author)) return TRUE; - draw_text(view, type, branch->ref == &branch_all ? "All branches" : branch->ref->name, TRUE); + draw_text(view, type, branch->ref == &branch_all ? "All branches" : branch->ref->name); return TRUE; } @@ -5699,7 +5248,7 @@ static const char *status_diff_files_argv[] = { }; static const char *status_list_other_argv[] = { - "git", "ls-files", "-z", "--others", "--exclude-standard", opt_prefix, NULL + "git", "ls-files", "-z", "--others", "--exclude-standard", opt_prefix, NULL, NULL, }; static const char *status_list_no_head_argv[] = { @@ -5804,6 +5353,9 @@ status_open(struct view *view) return FALSE; } + if (!opt_untracked_dirs_content) + status_list_other_argv[ARRAY_SIZE(status_list_other_argv) - 2] = "--directory"; + if (!status_run(view, status_diff_files_argv, 0, LINE_STAT_UNSTAGED) || !status_run(view, status_list_other_argv, '?', LINE_STAT_UNTRACKED)) return FALSE; @@ -5856,13 +5408,13 @@ status_draw(struct view *view, struct line *line, unsigned int lineno) static char buf[] = { '?', ' ', ' ', ' ', 0 }; buf[0] = status->status; - if (draw_text(view, line->type, buf, TRUE)) + if (draw_text(view, line->type, buf)) return TRUE; type = LINE_DEFAULT; text = status->new.name; } - draw_text(view, type, text, TRUE); + draw_text(view, type, text); return TRUE; } @@ -6814,17 +6366,17 @@ main_draw(struct view *view, struct line *line, unsigned int lineno) else type = LINE_MAIN_REF; - if (draw_text(view, type, "[", TRUE) || - draw_text(view, type, ref->name, TRUE) || - draw_text(view, type, "]", TRUE)) + if (draw_text(view, type, "[") || + draw_text(view, type, ref->name) || + draw_text(view, type, "]")) return TRUE; - if (draw_text(view, LINE_DEFAULT, " ", TRUE)) + if (draw_text(view, LINE_DEFAULT, " ")) return TRUE; } } - draw_text(view, LINE_DEFAULT, commit->title, TRUE); + draw_text(view, LINE_DEFAULT, commit->title); return TRUE; } @@ -6933,6 +6485,8 @@ main_request(struct view *view, enum request request, struct line *line) switch (request) { case REQ_ENTER: + if (view_is_displayed(view) && display[0] != view) + maximize_view(view); open_view(view, REQ_VIEW_DIFF, flags); break; case REQ_REFRESH: @@ -7094,7 +6648,7 @@ init_display(void) init_colors(); getmaxyx(stdscr, y, x); - status_win = newwin(1, 0, y - 1, 0); + status_win = newwin(1, x, y - 1, 0); if (!status_win) die("Failed to create status window"); @@ -7102,7 +6656,11 @@ init_display(void) keypad(status_win, TRUE); wbkgdset(status_win, get_line_attr(LINE_STATUS)); +#if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20080119) + set_tabsize(opt_tab_size); +#else TABSIZE = opt_tab_size; +#endif term = getenv("XTERM_VERSION") ? NULL : getenv("COLORTERM"); if (term && !strcmp(term, "gnome-terminal")) { @@ -7422,7 +6980,7 @@ get_ref_list(const char *id) } static int -read_ref(char *id, size_t idlen, char *name, size_t namelen) +read_ref(char *id, size_t idlen, char *name, size_t namelen, void *data) { struct ref *ref = NULL; bool tag = FALSE; @@ -7540,7 +7098,7 @@ load_refs(void) for (i = 0; i < refs_size; i++) refs[i]->id[0] = 0; - if (io_run_load(ls_remote_argv, "\t", read_ref) == ERR) + if (io_run_load(ls_remote_argv, "\t", read_ref, NULL) == ERR) return ERR; /* Update the ref lists to reflect changes. */ @@ -7575,19 +7133,19 @@ set_remote_branch(const char *name, const char *value, size_t valuelen) } static void -set_repo_config_option(char *name, char *value, int (*cmd)(int, const char **)) +set_repo_config_option(char *name, char *value, enum option_code (*cmd)(int, const char **)) { const char *argv[SIZEOF_ARG] = { name, "=" }; int argc = 1 + (cmd == option_set_command); - int error = ERR; + enum option_code error; if (!argv_from_string(argv, &argc, value)) - config_msg = "Too many option arguments"; + error = OPT_ERR_TOO_MANY_OPTION_ARGUMENTS; else error = cmd(argc, argv); - if (error == ERR) - warn("Option 'tig.%s': %s", name, config_msg); + if (error != OPT_OK) + warn("Option 'tig.%s': %s", name, option_errors[error]); } static bool @@ -7629,7 +7187,7 @@ set_work_tree(const char *value) } static int -read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen) +read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen, void *data) { if (!strcmp(name, "i18n.commitencoding")) string_ncopy(opt_encoding, value, valuelen); @@ -7661,11 +7219,11 @@ load_git_config(void) { const char *config_list_argv[] = { "git", "config", "--list", NULL }; - return io_run_load(config_list_argv, "=", read_repo_config_option); + return io_run_load(config_list_argv, "=", read_repo_config_option, NULL); } static int -read_repo_info(char *name, size_t namelen, char *value, size_t valuelen) +read_repo_info(char *name, size_t namelen, char *value, size_t valuelen, void *data) { if (!opt_git_dir[0]) { string_ncopy(opt_git_dir, name, namelen); @@ -7696,7 +7254,7 @@ load_repo_info(void) "--show-cdup", "--show-prefix", NULL }; - return io_run_load(rev_parse_argv, "=", read_repo_info); + return io_run_load(rev_parse_argv, "=", read_repo_info, NULL); } @@ -7754,11 +7312,11 @@ warn(const char *msg, ...) va_end(args); } -static const char ***filter_args; - static int -read_filter_args(char *name, size_t namelen, char *value, size_t valuelen) +read_filter_args(char *name, size_t namelen, char *value, size_t valuelen, void *data) { + const char ***filter_args = data; + return argv_append(filter_args, name) ? OK : ERR; } @@ -7768,10 +7326,9 @@ filter_rev_parse(const char ***args, const char *arg1, const char *arg2, const c const char *rev_parse_argv[SIZEOF_ARG] = { "git", "rev-parse", arg1, arg2 }; const char **all_argv = NULL; - filter_args = args; if (!argv_append_array(&all_argv, rev_parse_argv) || !argv_append_array(&all_argv, argv) || - !io_run_load(all_argv, "\n", read_filter_args) == ERR) + !io_run_load(all_argv, "\n", read_filter_args, args) == ERR) die("Failed to split arguments"); argv_free(all_argv); free(all_argv); @@ -7780,9 +7337,9 @@ filter_rev_parse(const char ***args, const char *arg1, const char *arg2, const c static void filter_options(const char *argv[]) { - filter_rev_parse(&opt_file_args, "--no-revs", "--no-flags", argv); - filter_rev_parse(&opt_diff_args, "--no-revs", "--flags", argv); - filter_rev_parse(&opt_rev_args, "--symbolic", "--revs-only", argv); + filter_rev_parse(&opt_file_argv, "--no-revs", "--no-flags", argv); + filter_rev_parse(&opt_diff_argv, "--no-revs", "--flags", argv); + filter_rev_parse(&opt_rev_argv, "--symbolic", "--revs-only", argv); } static enum request @@ -7830,7 +7387,7 @@ parse_options(int argc, const char *argv[]) const char *opt = argv[i]; if (seen_dashdash) { - argv_append(&opt_file_args, opt); + argv_append(&opt_file_argv, opt); continue; } else if (!strcmp(opt, "--")) { @@ -7846,7 +7403,7 @@ parse_options(int argc, const char *argv[]) quit(0); } else if (!strcmp(opt, "--all")) { - argv_append(&opt_rev_args, opt); + argv_append(&opt_rev_argv, opt); continue; }