1 /* Copyright (c) 2006-2012 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.
22 #define TIG_VERSION "unknown-version"
29 /* necessary on Snow Leopard to use WINDOW struct */
33 #define NCURSES_OPAQUE 0
44 #include <sys/types.h>
47 #include <sys/select.h>
59 /* ncurses(3): Must be defined to have extended wide-character functions. */
60 #define _XOPEN_SOURCE_EXTENDED
62 #ifdef HAVE_NCURSESW_H
63 #include <ncursesw/ncurses.h>
69 #define __NORETURN __attribute__((__noreturn__))
74 #define ABS(x) ((x) >= 0 ? (x) : -(x))
75 #define MIN(x, y) ((x) < (y) ? (x) : (y))
76 #define MAX(x, y) ((x) > (y) ? (x) : (y))
78 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
79 #define STRING_SIZE(x) (sizeof(x) - 1)
81 #define SIZEOF_STR 1024 /* Default string size. */
82 #define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */
83 #define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL. */
84 #define SIZEOF_ARG 32 /* Default argument array size. */
86 /* This color name can be used to refer to the default term colors. */
87 #define COLOR_DEFAULT (-1)
89 #define ENCODING_UTF8 "UTF-8"
90 #define ENCODING_SEP ": encoding: "
91 #define ENCODING_ARG "--encoding=" ENCODING_UTF8
93 #define ICONV_NONE ((iconv_t) -1)
95 #define ICONV_CONST /* nothing */
97 #define ICONV_TRANSLIT "//TRANSLIT"
99 /* The format and size of the date column in the main view. */
100 #define DATE_FORMAT "%Y-%m-%d %H:%M"
101 #define DATE_COLS STRING_SIZE("2006-04-29 14:21 ")
102 #define DATE_SHORT_COLS STRING_SIZE("2006-04-29 ")
105 #define AUTHOR_COLS 19
106 #define FILENAME_COLS 19
108 #define MIN_VIEW_HEIGHT 4
110 #define NULL_ID "0000000000000000000000000000000000000000"
112 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
114 /* Some ASCII-shorthands fitted into the ncurses namespace. */
115 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
117 #define KEY_RETURN '\r'
121 * Allocation helpers ... Entering macro hell to never be seen again.
124 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
126 name(type **mem, size_t size, size_t increase) \
128 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
129 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
132 if (mem == NULL || num_chunks != num_chunks_new) { \
133 size_t newsize = num_chunks_new * chunk_size * sizeof(type); \
135 tmp = realloc(tmp, newsize); \
138 if (num_chunks_new > num_chunks) { \
139 size_t offset = num_chunks * chunk_size; \
140 size_t oldsize = offset * sizeof(type); \
142 memset(tmp + offset, 0, newsize - oldsize); \
154 #define prefixcmp(str1, str2) \
155 strncmp(str1, str2, STRING_SIZE(str2))
158 string_isnumber(const char *str
)
162 for (pos
= 0; str
[pos
]; pos
++) {
163 if (!isdigit(str
[pos
]))
175 for (pos
= 0; str
[pos
]; pos
++) {
176 if (!isxdigit(str
[pos
]))
180 return 7 <= pos
&& pos
< SIZEOF_REV
;
186 if (c
>= 'a' && c
<= 'z')
194 if (c
>= 'A' && c
<= 'Z')
200 suffixcmp(const char *str
, int slen
, const char *suffix
)
202 size_t len
= slen
>= 0 ? slen
: strlen(str
);
203 size_t suffixlen
= strlen(suffix
);
205 return suffixlen
< len
? strcmp(str
+ len
- suffixlen
, suffix
) : -1;
209 string_ncopy_do(char *dst
, size_t dstlen
, const char *src
, size_t srclen
)
211 if (srclen
> dstlen
- 1)
214 strncpy(dst
, src
, srclen
);
218 /* Shorthands for safely copying into a fixed buffer. */
220 #define FORMAT_BUFFER(buf, bufsize, fmt, retval, allow_truncate) \
223 va_start(args, fmt); \
224 retval = vsnprintf(buf, bufsize, fmt, args); \
226 if (retval >= (bufsize) && allow_truncate) { \
227 (buf)[(bufsize) - 1] = 0; \
228 (buf)[(bufsize) - 2] = '.'; \
229 (buf)[(bufsize) - 3] = '.'; \
230 (buf)[(bufsize) - 4] = '.'; \
231 retval = (bufsize) - 1; \
232 } else if (retval < 0 || retval >= (bufsize)) { \
237 #define string_copy(dst, src) \
238 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
240 #define string_ncopy(dst, src, srclen) \
241 string_ncopy_do(dst, sizeof(dst), src, srclen)
244 string_copy_rev(char *dst
, const char *src
)
248 for (srclen
= 0; srclen
< SIZEOF_REV
; srclen
++)
249 if (isspace(src
[srclen
]))
252 string_ncopy_do(dst
, SIZEOF_REV
, src
, srclen
);
255 #define string_add(dst, from, src) \
256 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
259 string_expand(char *dst
, size_t dstlen
, const char *src
, int tabsize
)
263 for (size
= pos
= 0; size
< dstlen
- 1 && src
[pos
]; pos
++) {
264 if (src
[pos
] == '\t') {
265 size_t expanded
= tabsize
- (size
% tabsize
);
267 if (expanded
+ size
>= dstlen
- 1)
268 expanded
= dstlen
- size
- 1;
269 memcpy(dst
+ size
, " ", expanded
);
272 dst
[size
++] = src
[pos
];
281 chomp_string(char *name
)
285 while (isspace(*name
))
288 namelen
= strlen(name
) - 1;
289 while (namelen
> 0 && isspace(name
[namelen
]))
296 string_nformat(char *buf
, size_t bufsize
, size_t *bufpos
, const char *fmt
, ...)
298 size_t pos
= bufpos
? *bufpos
: 0;
301 FORMAT_BUFFER(buf
+ pos
, bufsize
- pos
, fmt
, retval
, FALSE
);
302 if (bufpos
&& retval
> 0)
303 *bufpos
= pos
+ retval
;
305 return pos
>= bufsize
? FALSE
: TRUE
;
308 #define string_format(buf, fmt, args...) \
309 string_nformat(buf, sizeof(buf), NULL, fmt, args)
311 #define string_format_from(buf, from, fmt, args...) \
312 string_nformat(buf, sizeof(buf), from, fmt, args)
315 strcmp_null(const char *s1
, const char *s2
)
318 return (!!s1
) - (!!s2
);
321 return strcmp(s1
, s2
);
334 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
336 #define ENUM_SYM_MACRO(prefix, name) prefix##_##name
337 #define ENUM_MAP_MACRO(prefix, name) ENUM_MAP(#name, ENUM_SYM_MACRO(prefix, name))
339 #define DEFINE_ENUM(name, info) \
340 enum name { info(ENUM_SYM_MACRO) }; \
341 static const struct enum_map name##_map[] = { info(ENUM_MAP_MACRO) }
344 string_enum_compare(const char *str1
, const char *str2
, int len
)
348 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
350 /* Diff-Header == DIFF_HEADER */
351 for (i
= 0; i
< len
; i
++) {
352 if (ascii_toupper(str1
[i
]) == ascii_toupper(str2
[i
]))
355 if (string_enum_sep(str1
[i
]) &&
356 string_enum_sep(str2
[i
]))
359 return str1
[i
] - str2
[i
];
365 #define enum_equals(entry, str, len) \
366 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
369 enum_map_name(const char *name
, size_t namelen
)
371 static char buf
[SIZEOF_STR
];
374 for (bufpos
= 0; bufpos
<= namelen
; bufpos
++) {
375 buf
[bufpos
] = ascii_tolower(name
[bufpos
]);
376 if (buf
[bufpos
] == '_')
384 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
387 map_enum_do(const struct enum_map
*map
, size_t map_size
, int *value
, const char *name
)
389 size_t namelen
= strlen(name
);
392 for (i
= 0; i
< map_size
; i
++)
393 if (enum_equals(map
[i
], name
, namelen
)) {
394 *value
= map
[i
].value
;
401 #define map_enum(attr, map, name) \
402 map_enum_do(map, ARRAY_SIZE(map), attr, name)
405 * Unicode / UTF-8 handling
407 * NOTE: Much of the following code for dealing with Unicode is derived from
408 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
409 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
413 unicode_width(unsigned long c
, int tab_size
)
416 (c
<= 0x115f /* Hangul Jamo */
419 || (c
>= 0x2e80 && c
<= 0xa4cf && c
!= 0x303f)
421 || (c
>= 0xac00 && c
<= 0xd7a3) /* Hangul Syllables */
422 || (c
>= 0xf900 && c
<= 0xfaff) /* CJK Compatibility Ideographs */
423 || (c
>= 0xfe30 && c
<= 0xfe6f) /* CJK Compatibility Forms */
424 || (c
>= 0xff00 && c
<= 0xff60) /* Fullwidth Forms */
425 || (c
>= 0xffe0 && c
<= 0xffe6)
426 || (c
>= 0x20000 && c
<= 0x2fffd)
427 || (c
>= 0x30000 && c
<= 0x3fffd)))
436 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
437 * Illegal bytes are set one. */
438 static const unsigned char utf8_bytes
[256] = {
439 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
440 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
441 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
442 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
443 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
444 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
445 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,
446 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,
449 static inline unsigned char
450 utf8_char_length(const char *string
, const char *end
)
452 int c
= *(unsigned char *) string
;
454 return utf8_bytes
[c
];
457 /* Decode UTF-8 multi-byte representation into a Unicode character. */
458 static inline unsigned long
459 utf8_to_unicode(const char *string
, size_t length
)
461 unsigned long unicode
;
468 unicode
= (string
[0] & 0x1f) << 6;
469 unicode
+= (string
[1] & 0x3f);
472 unicode
= (string
[0] & 0x0f) << 12;
473 unicode
+= ((string
[1] & 0x3f) << 6);
474 unicode
+= (string
[2] & 0x3f);
477 unicode
= (string
[0] & 0x0f) << 18;
478 unicode
+= ((string
[1] & 0x3f) << 12);
479 unicode
+= ((string
[2] & 0x3f) << 6);
480 unicode
+= (string
[3] & 0x3f);
483 unicode
= (string
[0] & 0x0f) << 24;
484 unicode
+= ((string
[1] & 0x3f) << 18);
485 unicode
+= ((string
[2] & 0x3f) << 12);
486 unicode
+= ((string
[3] & 0x3f) << 6);
487 unicode
+= (string
[4] & 0x3f);
490 unicode
= (string
[0] & 0x01) << 30;
491 unicode
+= ((string
[1] & 0x3f) << 24);
492 unicode
+= ((string
[2] & 0x3f) << 18);
493 unicode
+= ((string
[3] & 0x3f) << 12);
494 unicode
+= ((string
[4] & 0x3f) << 6);
495 unicode
+= (string
[5] & 0x3f);
501 /* Invalid characters could return the special 0xfffd value but NUL
502 * should be just as good. */
503 return unicode
> 0xffff ? 0 : unicode
;
506 /* Calculates how much of string can be shown within the given maximum width
507 * and sets trimmed parameter to non-zero value if all of string could not be
508 * shown. If the reserve flag is TRUE, it will reserve at least one
509 * trailing character, which can be useful when drawing a delimiter.
511 * Returns the number of bytes to output from string to satisfy max_width. */
513 utf8_length(const char **start
, size_t skip
, int *width
, size_t max_width
, int *trimmed
, bool reserve
, int tab_size
)
515 const char *string
= *start
;
516 const char *end
= strchr(string
, '\0');
517 unsigned char last_bytes
= 0;
518 size_t last_ucwidth
= 0;
523 while (string
< end
) {
524 unsigned char bytes
= utf8_char_length(string
, end
);
526 unsigned long unicode
;
528 if (string
+ bytes
> end
)
531 /* Change representation to figure out whether
532 * it is a single- or double-width character. */
534 unicode
= utf8_to_unicode(string
, bytes
);
535 /* FIXME: Graceful handling of invalid Unicode character. */
539 ucwidth
= unicode_width(unicode
, tab_size
);
541 skip
-= ucwidth
<= skip
? ucwidth
: skip
;
545 if (*width
> max_width
) {
548 if (reserve
&& *width
== max_width
) {
549 string
-= last_bytes
;
550 *width
-= last_ucwidth
;
556 last_bytes
= ucwidth
? bytes
: 0;
557 last_ucwidth
= ucwidth
;
560 return string
- *start
;