1 /* Copyright (c) 2006-2013 Jonas Fonseca <fonseca@diku.dk>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
21 #include "compat/compat.h"
24 #define TIG_VERSION "unknown-version"
31 /* necessary on Snow Leopard to use WINDOW struct */
35 #define NCURSES_OPAQUE 0
46 #include <sys/types.h>
49 #include <sys/select.h>
61 /* ncurses(3): Must be defined to have extended wide-character functions. */
62 #define _XOPEN_SOURCE_EXTENDED
64 #if defined HAVE_NCURSESW_CURSES_H
65 # include <ncursesw/curses.h>
66 #elif defined HAVE_NCURSESW_H
67 # include <ncursesw.h>
68 #elif defined HAVE_NCURSES_CURSES_H
69 # include <ncurses/curses.h>
70 #elif defined HAVE_NCURSES_H
72 #elif defined HAVE_CURSES_H
75 #ifdef WARN_MISSING_CURSES_CONFIGURATION
76 # warning SysV or X/Open-compatible Curses installation is required.
77 # warning Will assume Curses is found in default include and library path.
78 # warning To fix any build issues please use autotools to configure Curses.
79 # warning See INSTALL file for instructions.
85 #define TIG_NORETURN __attribute__((__noreturn__))
86 #define PRINTF_LIKE(fmt, args) __attribute__((format (printf, fmt, args)))
89 #define PRINTF_LIKE(fmt, args)
92 #define ABS(x) ((x) >= 0 ? (x) : -(x))
93 #define MIN(x, y) ((x) < (y) ? (x) : (y))
94 #define MAX(x, y) ((x) > (y) ? (x) : (y))
96 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
97 #define STRING_SIZE(x) (sizeof(x) - 1)
99 #define SIZEOF_STR 1024 /* Default string size. */
100 #define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */
101 #define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL. */
102 #define SIZEOF_ARG 32 /* Default argument array size. */
104 /* This color name can be used to refer to the default term colors. */
105 #define COLOR_DEFAULT (-1)
107 #define ICONV_NONE ((iconv_t) -1)
109 #define ICONV_CONST /* nothing */
111 #define ICONV_TRANSLIT "//TRANSLIT//IGNORE"
113 /* The format and size of the date column in the main view. */
114 #define DATE_FORMAT "%Y-%m-%d %H:%M"
115 #define DATE_WIDTH STRING_SIZE("2006-04-29 14:21")
116 #define DATE_SHORT_WIDTH STRING_SIZE("2006-04-29")
119 #define AUTHOR_WIDTH 18
120 #define FILENAME_WIDTH 18
122 #define MIN_VIEW_HEIGHT 4
123 #define MIN_VIEW_WIDTH 4
125 #define NULL_ID "0000000000000000000000000000000000000000"
127 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
129 /* Some ASCII-shorthands fitted into the ncurses namespace. */
130 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
132 #define KEY_RETURN '\r'
136 * Allocation helpers ... Entering macro hell to never be seen again.
139 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
141 name(type **mem, size_t size, size_t increase) \
143 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
144 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
147 if (mem == NULL || num_chunks != num_chunks_new) { \
148 size_t newsize = num_chunks_new * chunk_size * sizeof(type); \
150 tmp = realloc(tmp, newsize); \
153 if (num_chunks_new > num_chunks) { \
154 size_t offset = num_chunks * chunk_size; \
155 size_t oldsize = offset * sizeof(type); \
157 memset(tmp + offset, 0, newsize - oldsize); \
166 count_digits(unsigned long i
)
170 for (digits
= 0; i
; digits
++)
179 #define prefixcmp(str1, str2) \
180 strncmp(str1, str2, STRING_SIZE(str2))
183 string_isnumber(const char *str
)
187 for (pos
= 0; str
[pos
]; pos
++) {
188 if (!isdigit(str
[pos
]))
200 for (pos
= 0; str
[pos
]; pos
++) {
201 if (!isxdigit(str
[pos
]))
205 return 7 <= pos
&& pos
< SIZEOF_REV
;
211 if (c
>= 'a' && c
<= 'z')
219 if (c
>= 'A' && c
<= 'Z')
225 suffixcmp(const char *str
, int slen
, const char *suffix
)
227 size_t len
= slen
>= 0 ? slen
: strlen(str
);
228 size_t suffixlen
= strlen(suffix
);
230 return suffixlen
< len
? strcmp(str
+ len
- suffixlen
, suffix
) : -1;
234 string_ncopy_do(char *dst
, size_t dstlen
, const char *src
, size_t srclen
)
236 if (srclen
> dstlen
- 1)
239 strncpy(dst
, src
, srclen
);
243 /* Shorthands for safely copying into a fixed buffer. */
245 #define FORMAT_BUFFER(buf, bufsize, fmt, retval, allow_truncate) \
248 va_start(args, fmt); \
249 retval = vsnprintf(buf, bufsize, fmt, args); \
251 if (retval >= (bufsize) && allow_truncate) { \
252 (buf)[(bufsize) - 1] = 0; \
253 (buf)[(bufsize) - 2] = '.'; \
254 (buf)[(bufsize) - 3] = '.'; \
255 (buf)[(bufsize) - 4] = '.'; \
256 retval = (bufsize) - 1; \
257 } else if (retval < 0 || retval >= (bufsize)) { \
262 #define string_copy(dst, src) \
263 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
265 #define string_ncopy(dst, src, srclen) \
266 string_ncopy_do(dst, sizeof(dst), src, srclen)
269 string_copy_rev(char *dst
, const char *src
)
276 for (srclen
= 0; srclen
< SIZEOF_REV
; srclen
++)
277 if (isspace(src
[srclen
]))
280 string_ncopy_do(dst
, SIZEOF_REV
, src
, srclen
);
284 string_copy_rev_from_commit_line(char *dst
, const char *src
)
286 string_copy_rev(dst
, src
+ STRING_SIZE("commit "));
289 #define string_rev_is_null(rev) !strncmp(rev, NULL_ID, STRING_SIZE(NULL_ID))
291 #define string_add(dst, from, src) \
292 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
295 string_expanded_length(const char *src
, size_t srclen
, size_t tabsize
, size_t max_size
)
299 for (size
= pos
= 0; pos
< srclen
&& size
< max_size
; pos
++) {
300 if (src
[pos
] == '\t') {
301 size_t expanded
= tabsize
- (size
% tabsize
);
313 string_expand(char *dst
, size_t dstlen
, const char *src
, int tabsize
)
317 for (size
= pos
= 0; size
< dstlen
- 1 && src
[pos
]; pos
++) {
318 if (src
[pos
] == '\t') {
319 size_t expanded
= tabsize
- (size
% tabsize
);
321 if (expanded
+ size
>= dstlen
- 1)
322 expanded
= dstlen
- size
- 1;
323 memcpy(dst
+ size
, " ", expanded
);
326 dst
[size
++] = src
[pos
];
335 chomp_string(char *name
)
339 while (isspace(*name
))
342 namelen
= strlen(name
) - 1;
343 while (namelen
> 0 && isspace(name
[namelen
]))
349 static inline bool PRINTF_LIKE(4, 5)
350 string_nformat(char *buf
, size_t bufsize
, size_t *bufpos
, const char *fmt
, ...)
352 size_t pos
= bufpos
? *bufpos
: 0;
355 FORMAT_BUFFER(buf
+ pos
, bufsize
- pos
, fmt
, retval
, FALSE
);
356 if (bufpos
&& retval
> 0)
357 *bufpos
= pos
+ retval
;
359 return pos
>= bufsize
? FALSE
: TRUE
;
362 #define string_format(buf, fmt, args...) \
363 string_nformat(buf, sizeof(buf), NULL, fmt, args)
365 #define string_format_size(buf, size, fmt, args...) \
366 string_nformat(buf, size, NULL, fmt, args)
368 #define string_format_from(buf, from, fmt, args...) \
369 string_nformat(buf, sizeof(buf), from, fmt, args)
372 strcmp_null(const char *s1
, const char *s2
)
375 return (!!s1
) - (!!s2
);
378 return strcmp(s1
, s2
);
391 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
393 #define ENUM_SYM_MACRO(prefix, name) prefix##_##name
394 #define ENUM_MAP_MACRO(prefix, name) ENUM_MAP(#name, ENUM_SYM_MACRO(prefix, name))
396 #define DEFINE_ENUM(name, info) \
397 enum name { info(ENUM_SYM_MACRO) }; \
398 static const struct enum_map name##_map[] = { info(ENUM_MAP_MACRO) }
401 string_enum_compare(const char *str1
, const char *str2
, int len
)
405 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
407 /* Diff-Header == DIFF_HEADER */
408 for (i
= 0; i
< len
; i
++) {
409 if (ascii_toupper(str1
[i
]) == ascii_toupper(str2
[i
]))
412 if (string_enum_sep(str1
[i
]) &&
413 string_enum_sep(str2
[i
]))
416 return str1
[i
] - str2
[i
];
422 #define enum_equals(entry, str, len) \
423 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
426 enum_map_name(const char *name
, size_t namelen
)
428 static char buf
[SIZEOF_STR
];
431 for (bufpos
= 0; bufpos
<= namelen
; bufpos
++) {
432 buf
[bufpos
] = ascii_tolower(name
[bufpos
]);
433 if (buf
[bufpos
] == '_')
441 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
444 map_enum_do(const struct enum_map
*map
, size_t map_size
, int *value
, const char *name
)
446 size_t namelen
= strlen(name
);
449 for (i
= 0; i
< map_size
; i
++)
450 if (enum_equals(map
[i
], name
, namelen
)) {
451 *value
= map
[i
].value
;
458 #define map_enum(attr, map, name) \
459 map_enum_do(map, ARRAY_SIZE(map), attr, name)
462 * Unicode / UTF-8 handling
464 * NOTE: Much of the following code for dealing with Unicode is derived from
465 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
466 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
470 unicode_width(unsigned long c
, int tab_size
)
473 (c
<= 0x115f /* Hangul Jamo */
476 || (c
>= 0x2e80 && c
<= 0xa4cf && c
!= 0x303f)
478 || (c
>= 0xac00 && c
<= 0xd7a3) /* Hangul Syllables */
479 || (c
>= 0xf900 && c
<= 0xfaff) /* CJK Compatibility Ideographs */
480 || (c
>= 0xfe30 && c
<= 0xfe6f) /* CJK Compatibility Forms */
481 || (c
>= 0xff00 && c
<= 0xff60) /* Fullwidth Forms */
482 || (c
>= 0xffe0 && c
<= 0xffe6)
483 || (c
>= 0x20000 && c
<= 0x2fffd)
484 || (c
>= 0x30000 && c
<= 0x3fffd)))
487 if ((c
>= 0x0300 && c
<= 0x036f) /* combining diacretical marks */
488 || (c
>= 0x1dc0 && c
<= 0x1dff) /* combining diacretical marks supplement */
489 || (c
>= 0x20d0 && c
<= 0x20ff) /* combining diacretical marks for symbols */
490 || (c
>= 0xfe20 && c
<= 0xfe2f)) /* combining half marks */
499 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
500 * Illegal bytes are set one. */
501 static const unsigned char utf8_bytes
[256] = {
502 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
503 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
504 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
505 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
506 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
507 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
508 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,
509 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,
512 static inline unsigned char
513 utf8_char_length(const char *string
, const char *end
)
515 int c
= *(unsigned char *) string
;
517 return utf8_bytes
[c
];
520 /* Decode UTF-8 multi-byte representation into a Unicode character. */
521 static inline unsigned long
522 utf8_to_unicode(const char *string
, size_t length
)
524 unsigned long unicode
;
531 unicode
= (string
[0] & 0x1f) << 6;
532 unicode
+= (string
[1] & 0x3f);
535 unicode
= (string
[0] & 0x0f) << 12;
536 unicode
+= ((string
[1] & 0x3f) << 6);
537 unicode
+= (string
[2] & 0x3f);
540 unicode
= (string
[0] & 0x0f) << 18;
541 unicode
+= ((string
[1] & 0x3f) << 12);
542 unicode
+= ((string
[2] & 0x3f) << 6);
543 unicode
+= (string
[3] & 0x3f);
546 unicode
= (string
[0] & 0x0f) << 24;
547 unicode
+= ((string
[1] & 0x3f) << 18);
548 unicode
+= ((string
[2] & 0x3f) << 12);
549 unicode
+= ((string
[3] & 0x3f) << 6);
550 unicode
+= (string
[4] & 0x3f);
553 unicode
= (string
[0] & 0x01) << 30;
554 unicode
+= ((string
[1] & 0x3f) << 24);
555 unicode
+= ((string
[2] & 0x3f) << 18);
556 unicode
+= ((string
[3] & 0x3f) << 12);
557 unicode
+= ((string
[4] & 0x3f) << 6);
558 unicode
+= (string
[5] & 0x3f);
564 /* Invalid characters could return the special 0xfffd value but NUL
565 * should be just as good. */
566 return unicode
> 0xffff ? 0 : unicode
;
569 /* Calculates how much of string can be shown within the given maximum width
570 * and sets trimmed parameter to non-zero value if all of string could not be
571 * shown. If the reserve flag is TRUE, it will reserve at least one
572 * trailing character, which can be useful when drawing a delimiter.
574 * Returns the number of bytes to output from string to satisfy max_width. */
576 utf8_length(const char **start
, size_t skip
, int *width
, size_t max_width
, int *trimmed
, bool reserve
, int tab_size
)
578 const char *string
= *start
;
579 const char *end
= strchr(string
, '\0');
580 unsigned char last_bytes
= 0;
581 size_t last_ucwidth
= 0;
586 while (string
< end
) {
587 unsigned char bytes
= utf8_char_length(string
, end
);
589 unsigned long unicode
;
591 if (string
+ bytes
> end
)
594 /* Change representation to figure out whether
595 * it is a single- or double-width character. */
597 unicode
= utf8_to_unicode(string
, bytes
);
598 /* FIXME: Graceful handling of invalid Unicode character. */
602 ucwidth
= unicode_width(unicode
, tab_size
);
604 skip
-= ucwidth
<= skip
? ucwidth
: skip
;
608 if (*width
> max_width
) {
611 if (reserve
&& *width
== max_width
) {
612 string
-= last_bytes
;
613 *width
-= last_ucwidth
;
619 last_bytes
= ucwidth
? bytes
: 0;
620 last_ucwidth
= ucwidth
;
623 return string
- *start
;
628 /* vim: set ts=8 sw=8 noexpandtab: */