index 0a93ad204bfa51a66449893f0b13fc73b0419ad7..54d99a721378cee897c49df99e728b61357dba55 100644 (file)
--- a/tig.c
+++ b/tig.c
-/* Copyright (c) 2006-2009 Jonas Fonseca <fonseca@diku.dk>
+/* Copyright (c) 2006-2010 Jonas Fonseca <fonseca@diku.dk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
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);
+static size_t utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size);
+static inline unsigned char utf8_char_length(const char *string, const char *end);
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
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);
}
-/*
- * What value of "tz" was in effect back then at "time" in the
- * local timezone?
- */
-static int local_tzoffset(time_t time)
-{
- time_t t, t_local;
- struct tm tm;
- int offset, eastwest;
-
- t = time;
- localtime_r(&t, &tm);
- t_local = mktime(&tm);
-
- if (t_local < t) {
- eastwest = -1;
- offset = t - t_local;
- } else {
- eastwest = 1;
- offset = t_local - t;
- }
- offset /= 60; /* in minutes */
- offset = (offset % 60) + ((offset / 60) * 100);
- return offset * eastwest;
-}
-
#define DATE_INFO \
DATE_(NO), \
DATE_(DEFAULT), \
#undef DATE_
};
+struct time {
+ time_t sec;
+ int tz;
+};
+
+static inline int timecmp(const struct time *t1, const struct time *t2)
+{
+ return t1->sec - t2->sec;
+}
+
static const char *
-string_date(const time_t *time, enum date date)
+string_date(const struct time *time, enum date date)
{
static char buf[DATE_COLS + 1];
static const struct enum_map reldate[] = {
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;
}
}
- gmtime_r(time, &tm);
+ gmtime_r(&time->sec, &tm);
return strftime(buf, sizeof(buf), DATE_FORMAT, &tm) ? buf : NULL;
}
#undef AUTHOR_
};
-/* FIXME: Handle multi-byte and multi-column characters. */
static const char *
-get_author_initials(const char *author, size_t max_columns)
+get_author_initials(const char *author)
{
- static char initials[AUTHOR_COLS];
- size_t pos;
+ 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));
- for (pos = 0; *author && pos < sizeof(initials) - 1; author++, pos++) {
+ while (author < end) {
+ unsigned char bytes;
+ size_t i;
+
while (is_initial_sep(*author))
author++;
- strncpy(&initials[pos], author, sizeof(initials) - 1 - pos);
- while (*author && author[1] && !is_initial_sep(author[1]))
- 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 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] = "";
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 is_initial_commit() (!get_ref_head())
+#define is_head_commit(rev) (!strcmp((rev), "HEAD") || (get_ref_head() && !strcmp(rev, get_ref_head()->id)))
#define mkdate(time) string_date(time, opt_date)
(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;
}
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) {
@@ -2244,9 +2233,9 @@ draw_field(struct view *view, enum line_type type, const char *text, int len, bo
}
static bool
-draw_date(struct view *view, time_t *time)
+draw_date(struct view *view, struct time *time)
{
- const char *date = time ? mkdate(time) : "";
+ const char *date = time && time->sec ? mkdate(time) : "";
int cols = opt_date == DATE_SHORT ? DATE_SHORT_COLS : DATE_COLS;
return draw_field(view, LINE_DATE, date, cols, FALSE);
bool abbreviate = opt_author == AUTHOR_ABBREVIATED || !trim;
if (abbreviate && author)
- author = get_author_initials(author, opt_author_cols);
+ author = get_author_initials(author);
return draw_field(view, LINE_AUTHOR, author, opt_author_cols, trim);
}
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) {
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
while (!view->ops->read(view, NULL))
if (!force)
return;
- set_nonblocking_input(FALSE);
if (force)
kill_io(view->pipe);
done_io(view->pipe);
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;
{
if (view->pipe)
end_update(view, TRUE);
- return io_open(&view->io, "%s", name);
+ return io_open(&view->io, "%s/%s", opt_cdup[0] ? opt_cdup : ".", name);
}
static bool
}
static 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;
if (zone[0] == '-')
tz = -tz;
- *time -= tz;
+ time->tz = tz;
+ time->sec -= tz;
}
/* Parse author lines where the name may be empty:
* author <email@address.tld> 1138474660 +0100
*/
static void
-parse_author_line(char *ident, const char **author, time_t *time)
+parse_author_line(char *ident, const char **author, struct time *time)
{
char *nameend = strchr(ident, '<');
char *emailend = strchr(ident, '>');
char *secs = emailend + 2;
char *zone = strchr(secs, ' ');
- *time = (time_t) atol(secs);
+ parse_timesec(time, secs);
if (zone && strlen(zone) == STRING_SIZE(" +0700"))
parse_timezone(time, zone + 1);
if (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);
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];
};
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));
tree_read_date(struct view *view, char *text, bool *read_date)
{
static const char *author_name;
- static time_t author_time;
+ static struct time author_time;
if (!text && *read_date) {
*read_date = FALSE;
if (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))
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. */
};
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);
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];
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. */
};
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));
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))
};
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[] = {
char id[SIZEOF_REV]; /* SHA1 ID. */
char title[128]; /* First line of the commit message. */
const char *author; /* Author of the commit. */
- time_t time; /* Date from the author ident. */
+ struct time time; /* Date from the author ident. */
struct ref_list *refs; /* Repository references. */
chtype graph[SIZEOF_REVGRAPH]; /* Ancestry chain graphics. */
size_t graph_size; /* The width of the graph array. */
struct 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++) {
*/
static inline int
-unicode_width(unsigned long c)
+unicode_width(unsigned long c, int tab_size)
{
if (c >= 0x1100 &&
(c <= 0x115f /* Hangul Jamo */
return 2;
if (c == '\t')
- return opt_tab_size;
+ return tab_size;
return 1;
}
3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4, 5,5,5,5,6,6,1,1,
};
+static inline unsigned char
+utf8_char_length(const char *string, const char *end)
+{
+ int c = *(unsigned char *) string;
+
+ return utf8_bytes[c];
+}
+
/* Decode UTF-8 multi-byte representation into a Unicode character. */
static inline unsigned long
utf8_to_unicode(const char *string, size_t length)
*
* 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)
+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');
@@ -6862,8 +6863,7 @@ utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *
*trimmed = 0;
while (string < end) {
- int c = *(unsigned char *) string;
- unsigned char bytes = utf8_bytes[c];
+ unsigned char bytes = utf8_char_length(string, end);
size_t ucwidth;
unsigned long unicode;
@@ -6878,7 +6878,7 @@ utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *
if (!unicode)
break;
- ucwidth = unicode_width(unicode);
+ ucwidth = unicode_width(unicode, tab_size);
if (skip > 0) {
skip -= ucwidth <= skip ? ucwidth : skip;
*start += bytes;
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)
{
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")) {
{
struct view *view;
int i, key, cursor_y, cursor_x;
+ bool loading = FALSE;
if (prompt_position)
input_mode = TRUE;
use_scroll_redrawwin)
redrawwin(view->win);
view->has_scrolled = FALSE;
+ if (view->pipe)
+ loading = TRUE;
}
/* Update the cursor position. */
/* Refresh, accept single keystroke of input */
doupdate();
+ nodelay(status_win, loading);
key = wgetch(status_win);
/* wgetch() with nodelay() enabled returns ERR when
@@ -7259,6 +7249,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;
break;
}
+static struct ref *
+get_ref_head()
+{
+ return refs_head;
+}
+
static struct ref_list *
get_ref_list(const char *id)
{
} 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
ref->tracked = tracked;
string_copy_rev(ref->id, id);
+ if (head)
+ refs_head = ref;
return OK;
}
memmove(opt_head, offset, strlen(offset) + 1);
}
+ refs_head = NULL;
for (i = 0; i < refs_size; i++)
refs[i]->id[0] = 0;
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;
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)
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");
}