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.adoc 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")
118 #define MIN_VIEW_HEIGHT 4
119 #define MIN_VIEW_WIDTH 4
121 #define NULL_ID "0000000000000000000000000000000000000000"
123 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
125 /* Some ASCII-shorthands fitted into the ncurses namespace. */
126 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
128 #define KEY_RETURN '\r'
132 * Allocation helpers ... Entering macro hell to never be seen again.
135 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
137 name(type **mem, size_t size, size_t increase) \
139 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
140 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
143 if (mem == NULL || num_chunks != num_chunks_new) { \
144 size_t newsize = num_chunks_new * chunk_size * sizeof(type); \
146 tmp = realloc(tmp, newsize); \
149 if (num_chunks_new > num_chunks) { \
150 size_t offset = num_chunks * chunk_size; \
151 size_t oldsize = offset * sizeof(type); \
153 memset(tmp + offset, 0, newsize - oldsize); \
162 count_digits(unsigned long i
)
166 for (digits
= 0; i
; digits
++)
175 #define prefixcmp(str1, str2) \
176 strncmp(str1, str2, STRING_SIZE(str2))
179 string_isnumber(const char *str
)
183 for (pos
= 0; str
[pos
]; pos
++) {
184 if (!isdigit(str
[pos
]))
196 for (pos
= 0; str
[pos
]; pos
++) {
197 if (!isxdigit(str
[pos
]))
201 return 7 <= pos
&& pos
< SIZEOF_REV
;
207 if (c
>= 'a' && c
<= 'z')
215 if (c
>= 'A' && c
<= 'Z')
221 suffixcmp(const char *str
, int slen
, const char *suffix
)
223 size_t len
= slen
>= 0 ? slen
: strlen(str
);
224 size_t suffixlen
= strlen(suffix
);
226 return suffixlen
< len
? strcmp(str
+ len
- suffixlen
, suffix
) : -1;
230 string_ncopy_do(char *dst
, size_t dstlen
, const char *src
, size_t srclen
)
232 if (srclen
> dstlen
- 1)
235 strncpy(dst
, src
, srclen
);
239 /* Shorthands for safely copying into a fixed buffer. */
241 #define FORMAT_BUFFER(buf, bufsize, fmt, retval, allow_truncate) \
244 va_start(args, fmt); \
245 retval = vsnprintf(buf, bufsize, fmt, args); \
247 if (retval >= (bufsize) && allow_truncate) { \
248 (buf)[(bufsize) - 1] = 0; \
249 (buf)[(bufsize) - 2] = '.'; \
250 (buf)[(bufsize) - 3] = '.'; \
251 (buf)[(bufsize) - 4] = '.'; \
252 retval = (bufsize) - 1; \
253 } else if (retval < 0 || retval >= (bufsize)) { \
258 #define string_copy(dst, src) \
259 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
261 #define string_ncopy(dst, src, srclen) \
262 string_ncopy_do(dst, sizeof(dst), src, srclen)
265 string_copy_rev(char *dst
, const char *src
)
272 for (srclen
= 0; srclen
< SIZEOF_REV
; srclen
++)
273 if (isspace(src
[srclen
]))
276 string_ncopy_do(dst
, SIZEOF_REV
, src
, srclen
);
280 string_copy_rev_from_commit_line(char *dst
, const char *src
)
282 string_copy_rev(dst
, src
+ STRING_SIZE("commit "));
285 #define string_rev_is_null(rev) !strncmp(rev, NULL_ID, STRING_SIZE(NULL_ID))
287 #define string_add(dst, from, src) \
288 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
291 string_expanded_length(const char *src
, size_t srclen
, size_t tabsize
, size_t max_size
)
295 for (size
= pos
= 0; pos
< srclen
&& size
< max_size
; pos
++) {
296 if (src
[pos
] == '\t') {
297 size_t expanded
= tabsize
- (size
% tabsize
);
309 string_expand(char *dst
, size_t dstlen
, const char *src
, int tabsize
)
313 for (size
= pos
= 0; size
< dstlen
- 1 && src
[pos
]; pos
++) {
314 if (src
[pos
] == '\t') {
315 size_t expanded
= tabsize
- (size
% tabsize
);
317 if (expanded
+ size
>= dstlen
- 1)
318 expanded
= dstlen
- size
- 1;
319 memcpy(dst
+ size
, " ", expanded
);
322 dst
[size
++] = src
[pos
];
331 chomp_string(char *name
)
335 while (isspace(*name
))
338 namelen
= strlen(name
) - 1;
339 while (namelen
> 0 && isspace(name
[namelen
]))
345 static inline bool PRINTF_LIKE(4, 5)
346 string_nformat(char *buf
, size_t bufsize
, size_t *bufpos
, const char *fmt
, ...)
348 size_t pos
= bufpos
? *bufpos
: 0;
351 FORMAT_BUFFER(buf
+ pos
, bufsize
- pos
, fmt
, retval
, FALSE
);
352 if (bufpos
&& retval
> 0)
353 *bufpos
= pos
+ retval
;
355 return pos
>= bufsize
? FALSE
: TRUE
;
358 #define string_format(buf, fmt, args...) \
359 string_nformat(buf, sizeof(buf), NULL, fmt, args)
361 #define string_format_size(buf, size, fmt, args...) \
362 string_nformat(buf, size, NULL, fmt, args)
364 #define string_format_from(buf, from, fmt, args...) \
365 string_nformat(buf, sizeof(buf), from, fmt, args)
368 strcmp_null(const char *s1
, const char *s2
)
371 return (!!s1
) - (!!s2
);
374 return strcmp(s1
, s2
);
381 struct enum_map_entry
{
388 const struct enum_map_entry
*entries
;
392 #define ENUM_MAP_ENTRY(name, value) { name, STRING_SIZE(name), value }
394 #define ENUM_SYM_MACRO(prefix, name) prefix##_##name
395 #define ENUM_MAP_MACRO(prefix, name) ENUM_MAP_ENTRY(#name, ENUM_SYM_MACRO(prefix, name))
397 #define DEFINE_ENUM(name, info) \
398 enum name { info(ENUM_SYM_MACRO) }; \
399 static const struct enum_map_entry name##_map_entries[] = { info(ENUM_MAP_MACRO) }; \
400 static const struct enum_map name##_map[] = { { name##_map_entries, ARRAY_SIZE(name##_map_entries) } }
403 string_enum_compare(const char *str1
, const char *str2
, int len
)
407 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
409 /* Diff-Header == DIFF_HEADER */
410 for (i
= 0; i
< len
; i
++) {
411 if (ascii_toupper(str1
[i
]) == ascii_toupper(str2
[i
]))
414 if (string_enum_sep(str1
[i
]) &&
415 string_enum_sep(str2
[i
]))
418 return str1
[i
] - str2
[i
];
424 #define enum_equals(entry, str, len) \
425 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
428 enum_map_name(const char *name
, size_t namelen
)
430 static char buf
[SIZEOF_STR
];
433 for (bufpos
= 0; bufpos
<= namelen
; bufpos
++) {
434 buf
[bufpos
] = ascii_tolower(name
[bufpos
]);
435 if (buf
[bufpos
] == '_')
443 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
444 #define enum_copy_name(buf, entry) string_ncopy(buf, (entry).name, (entry).namelen)
447 map_enum_do(const struct enum_map_entry
*map
, size_t map_size
, int *value
, const char *name
)
449 size_t namelen
= strlen(name
);
452 for (i
= 0; i
< map_size
; i
++)
453 if (enum_equals(map
[i
], name
, namelen
)) {
454 *value
= map
[i
].value
;
461 #define map_enum(attr, map, name) \
462 map_enum_do(map, ARRAY_SIZE(map), attr, name)
465 * Unicode / UTF-8 handling
467 * NOTE: Much of the following code for dealing with Unicode is derived from
468 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
469 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
473 unicode_width(unsigned long c
, int tab_size
)
476 (c
<= 0x115f /* Hangul Jamo */
479 || (c
>= 0x2e80 && c
<= 0xa4cf && c
!= 0x303f)
481 || (c
>= 0xac00 && c
<= 0xd7a3) /* Hangul Syllables */
482 || (c
>= 0xf900 && c
<= 0xfaff) /* CJK Compatibility Ideographs */
483 || (c
>= 0xfe30 && c
<= 0xfe6f) /* CJK Compatibility Forms */
484 || (c
>= 0xff00 && c
<= 0xff60) /* Fullwidth Forms */
485 || (c
>= 0xffe0 && c
<= 0xffe6)
486 || (c
>= 0x20000 && c
<= 0x2fffd)
487 || (c
>= 0x30000 && c
<= 0x3fffd)))
490 if ((c
>= 0x0300 && c
<= 0x036f) /* combining diacretical marks */
491 || (c
>= 0x1dc0 && c
<= 0x1dff) /* combining diacretical marks supplement */
492 || (c
>= 0x20d0 && c
<= 0x20ff) /* combining diacretical marks for symbols */
493 || (c
>= 0xfe20 && c
<= 0xfe2f)) /* combining half marks */
502 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
503 * Illegal bytes are set one. */
504 static const unsigned char utf8_bytes
[256] = {
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 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
509 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
510 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
511 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,
512 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,
515 static inline unsigned char
516 utf8_char_length(const char *string
, const char *end
)
518 int c
= *(unsigned char *) string
;
520 return utf8_bytes
[c
];
523 /* Decode UTF-8 multi-byte representation into a Unicode character. */
524 static inline unsigned long
525 utf8_to_unicode(const char *string
, size_t length
)
527 unsigned long unicode
;
534 unicode
= (string
[0] & 0x1f) << 6;
535 unicode
+= (string
[1] & 0x3f);
538 unicode
= (string
[0] & 0x0f) << 12;
539 unicode
+= ((string
[1] & 0x3f) << 6);
540 unicode
+= (string
[2] & 0x3f);
543 unicode
= (string
[0] & 0x0f) << 18;
544 unicode
+= ((string
[1] & 0x3f) << 12);
545 unicode
+= ((string
[2] & 0x3f) << 6);
546 unicode
+= (string
[3] & 0x3f);
549 unicode
= (string
[0] & 0x0f) << 24;
550 unicode
+= ((string
[1] & 0x3f) << 18);
551 unicode
+= ((string
[2] & 0x3f) << 12);
552 unicode
+= ((string
[3] & 0x3f) << 6);
553 unicode
+= (string
[4] & 0x3f);
556 unicode
= (string
[0] & 0x01) << 30;
557 unicode
+= ((string
[1] & 0x3f) << 24);
558 unicode
+= ((string
[2] & 0x3f) << 18);
559 unicode
+= ((string
[3] & 0x3f) << 12);
560 unicode
+= ((string
[4] & 0x3f) << 6);
561 unicode
+= (string
[5] & 0x3f);
567 /* Invalid characters could return the special 0xfffd value but NUL
568 * should be just as good. */
569 return unicode
> 0xffff ? 0 : unicode
;
572 /* Calculates how much of string can be shown within the given maximum width
573 * and sets trimmed parameter to non-zero value if all of string could not be
574 * shown. If the reserve flag is TRUE, it will reserve at least one
575 * trailing character, which can be useful when drawing a delimiter.
577 * Returns the number of bytes to output from string to satisfy max_width. */
579 utf8_length(const char **start
, size_t skip
, int *width
, size_t max_width
, int *trimmed
, bool reserve
, int tab_size
)
581 const char *string
= *start
;
582 const char *end
= strchr(string
, '\0');
583 unsigned char last_bytes
= 0;
584 size_t last_ucwidth
= 0;
589 while (string
< end
) {
590 unsigned char bytes
= utf8_char_length(string
, end
);
592 unsigned long unicode
;
594 if (string
+ bytes
> end
)
597 /* Change representation to figure out whether
598 * it is a single- or double-width character. */
600 unicode
= utf8_to_unicode(string
, bytes
);
601 /* FIXME: Graceful handling of invalid Unicode character. */
605 ucwidth
= unicode_width(unicode
, tab_size
);
607 skip
-= ucwidth
<= skip
? ucwidth
: skip
;
611 if (*width
> max_width
) {
614 if (reserve
&& *width
== max_width
) {
615 string
-= last_bytes
;
616 *width
-= last_ucwidth
;
624 last_ucwidth
= ucwidth
;
630 return string
- *start
;
635 /* vim: set ts=8 sw=8 noexpandtab: */