1 /* Copyright (c) 2006-2010 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"
91 #define ICONV_NONE ((iconv_t) -1)
93 #define ICONV_CONST /* nothing */
95 #define ICONV_TRANSLIT "//TRANSLIT"
97 /* The format and size of the date column in the main view. */
98 #define DATE_FORMAT "%Y-%m-%d %H:%M"
99 #define DATE_COLS STRING_SIZE("2006-04-29 14:21 ")
100 #define DATE_SHORT_COLS STRING_SIZE("2006-04-29 ")
103 #define AUTHOR_COLS 19
104 #define FILENAME_COLS 19
106 #define MIN_VIEW_HEIGHT 4
108 #define NULL_ID "0000000000000000000000000000000000000000"
110 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
112 /* Some ASCII-shorthands fitted into the ncurses namespace. */
113 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
115 #define KEY_RETURN '\r'
119 * Allocation helpers ... Entering macro hell to never be seen again.
122 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
124 name(type **mem, size_t size, size_t increase) \
126 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
127 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
130 if (mem == NULL || num_chunks != num_chunks_new) { \
131 tmp = realloc(tmp, num_chunks_new * chunk_size * sizeof(type)); \
143 #define prefixcmp(str1, str2) \
144 strncmp(str1, str2, STRING_SIZE(str2))
147 string_isnumber(const char *str
)
151 for (pos
= 0; str
[pos
]; pos
++) {
152 if (!isdigit(str
[pos
]))
164 for (pos
= 0; str
[pos
]; pos
++) {
165 if (!isxdigit(str
[pos
]))
169 return 7 <= pos
&& pos
< SIZEOF_REV
;
175 if (c
>= 'a' && c
<= 'z')
183 if (c
>= 'A' && c
<= 'Z')
189 suffixcmp(const char *str
, int slen
, const char *suffix
)
191 size_t len
= slen
>= 0 ? slen
: strlen(str
);
192 size_t suffixlen
= strlen(suffix
);
194 return suffixlen
< len
? strcmp(str
+ len
- suffixlen
, suffix
) : -1;
198 string_ncopy_do(char *dst
, size_t dstlen
, const char *src
, size_t srclen
)
200 if (srclen
> dstlen
- 1)
203 strncpy(dst
, src
, srclen
);
207 /* Shorthands for safely copying into a fixed buffer. */
209 #define FORMAT_BUFFER(buf, bufsize, fmt, retval) \
212 va_start(args, fmt); \
213 retval = vsnprintf(buf, bufsize, fmt, args); \
215 if (0 < retval && retval >= bufsize) { \
220 #define string_copy(dst, src) \
221 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
223 #define string_ncopy(dst, src, srclen) \
224 string_ncopy_do(dst, sizeof(dst), src, srclen)
226 #define string_copy_rev(dst, src) \
227 string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
229 #define string_add(dst, from, src) \
230 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
233 string_expand(char *dst
, size_t dstlen
, const char *src
, int tabsize
)
237 for (size
= pos
= 0; size
< dstlen
- 1 && src
[pos
]; pos
++) {
238 if (src
[pos
] == '\t') {
239 size_t expanded
= tabsize
- (size
% tabsize
);
241 if (expanded
+ size
>= dstlen
- 1)
242 expanded
= dstlen
- size
- 1;
243 memcpy(dst
+ size
, " ", expanded
);
246 dst
[size
++] = src
[pos
];
255 chomp_string(char *name
)
259 while (isspace(*name
))
262 namelen
= strlen(name
) - 1;
263 while (namelen
> 0 && isspace(name
[namelen
]))
270 string_nformat(char *buf
, size_t bufsize
, size_t *bufpos
, const char *fmt
, ...)
272 size_t pos
= bufpos
? *bufpos
: 0;
275 FORMAT_BUFFER(buf
+ pos
, bufsize
- pos
, fmt
, retval
);
276 if (bufpos
&& retval
> 0)
277 *bufpos
= pos
+ retval
;
279 return pos
>= bufsize
? FALSE
: TRUE
;
282 #define string_format(buf, fmt, args...) \
283 string_nformat(buf, sizeof(buf), NULL, fmt, args)
285 #define string_format_from(buf, from, fmt, args...) \
286 string_nformat(buf, sizeof(buf), from, fmt, args)
289 strcmp_null(const char *s1
, const char *s2
)
292 return (!!s1
) - (!!s2
);
295 return strcmp(s1
, s2
);
308 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
310 #define ENUM_SYM_MACRO(prefix, name) prefix##_##name
311 #define ENUM_MAP_MACRO(prefix, name) ENUM_MAP(#name, ENUM_SYM_MACRO(prefix, name))
313 #define DEFINE_ENUM(name, info) \
314 enum name { info(ENUM_SYM_MACRO) }; \
315 static const struct enum_map name##_map[] = { info(ENUM_MAP_MACRO) }
318 string_enum_compare(const char *str1
, const char *str2
, int len
)
322 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
324 /* Diff-Header == DIFF_HEADER */
325 for (i
= 0; i
< len
; i
++) {
326 if (ascii_toupper(str1
[i
]) == ascii_toupper(str2
[i
]))
329 if (string_enum_sep(str1
[i
]) &&
330 string_enum_sep(str2
[i
]))
333 return str1
[i
] - str2
[i
];
339 #define enum_equals(entry, str, len) \
340 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
343 enum_map_name(const char *name
, size_t namelen
)
345 static char buf
[SIZEOF_STR
];
348 for (bufpos
= 0; bufpos
<= namelen
; bufpos
++) {
349 buf
[bufpos
] = ascii_tolower(name
[bufpos
]);
350 if (buf
[bufpos
] == '_')
358 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
361 map_enum_do(const struct enum_map
*map
, size_t map_size
, int *value
, const char *name
)
363 size_t namelen
= strlen(name
);
366 for (i
= 0; i
< map_size
; i
++)
367 if (enum_equals(map
[i
], name
, namelen
)) {
368 *value
= map
[i
].value
;
375 #define map_enum(attr, map, name) \
376 map_enum_do(map, ARRAY_SIZE(map), attr, name)
379 * Unicode / UTF-8 handling
381 * NOTE: Much of the following code for dealing with Unicode is derived from
382 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
383 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
387 unicode_width(unsigned long c
, int tab_size
)
390 (c
<= 0x115f /* Hangul Jamo */
393 || (c
>= 0x2e80 && c
<= 0xa4cf && c
!= 0x303f)
395 || (c
>= 0xac00 && c
<= 0xd7a3) /* Hangul Syllables */
396 || (c
>= 0xf900 && c
<= 0xfaff) /* CJK Compatibility Ideographs */
397 || (c
>= 0xfe30 && c
<= 0xfe6f) /* CJK Compatibility Forms */
398 || (c
>= 0xff00 && c
<= 0xff60) /* Fullwidth Forms */
399 || (c
>= 0xffe0 && c
<= 0xffe6)
400 || (c
>= 0x20000 && c
<= 0x2fffd)
401 || (c
>= 0x30000 && c
<= 0x3fffd)))
410 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
411 * Illegal bytes are set one. */
412 static const unsigned char utf8_bytes
[256] = {
413 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
414 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
415 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
416 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
417 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
418 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
419 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,
420 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,
423 static inline unsigned char
424 utf8_char_length(const char *string
, const char *end
)
426 int c
= *(unsigned char *) string
;
428 return utf8_bytes
[c
];
431 /* Decode UTF-8 multi-byte representation into a Unicode character. */
432 static inline unsigned long
433 utf8_to_unicode(const char *string
, size_t length
)
435 unsigned long unicode
;
442 unicode
= (string
[0] & 0x1f) << 6;
443 unicode
+= (string
[1] & 0x3f);
446 unicode
= (string
[0] & 0x0f) << 12;
447 unicode
+= ((string
[1] & 0x3f) << 6);
448 unicode
+= (string
[2] & 0x3f);
451 unicode
= (string
[0] & 0x0f) << 18;
452 unicode
+= ((string
[1] & 0x3f) << 12);
453 unicode
+= ((string
[2] & 0x3f) << 6);
454 unicode
+= (string
[3] & 0x3f);
457 unicode
= (string
[0] & 0x0f) << 24;
458 unicode
+= ((string
[1] & 0x3f) << 18);
459 unicode
+= ((string
[2] & 0x3f) << 12);
460 unicode
+= ((string
[3] & 0x3f) << 6);
461 unicode
+= (string
[4] & 0x3f);
464 unicode
= (string
[0] & 0x01) << 30;
465 unicode
+= ((string
[1] & 0x3f) << 24);
466 unicode
+= ((string
[2] & 0x3f) << 18);
467 unicode
+= ((string
[3] & 0x3f) << 12);
468 unicode
+= ((string
[4] & 0x3f) << 6);
469 unicode
+= (string
[5] & 0x3f);
475 /* Invalid characters could return the special 0xfffd value but NUL
476 * should be just as good. */
477 return unicode
> 0xffff ? 0 : unicode
;
480 /* Calculates how much of string can be shown within the given maximum width
481 * and sets trimmed parameter to non-zero value if all of string could not be
482 * shown. If the reserve flag is TRUE, it will reserve at least one
483 * trailing character, which can be useful when drawing a delimiter.
485 * Returns the number of bytes to output from string to satisfy max_width. */
487 utf8_length(const char **start
, size_t skip
, int *width
, size_t max_width
, int *trimmed
, bool reserve
, int tab_size
)
489 const char *string
= *start
;
490 const char *end
= strchr(string
, '\0');
491 unsigned char last_bytes
= 0;
492 size_t last_ucwidth
= 0;
497 while (string
< end
) {
498 unsigned char bytes
= utf8_char_length(string
, end
);
500 unsigned long unicode
;
502 if (string
+ bytes
> end
)
505 /* Change representation to figure out whether
506 * it is a single- or double-width character. */
508 unicode
= utf8_to_unicode(string
, bytes
);
509 /* FIXME: Graceful handling of invalid Unicode character. */
513 ucwidth
= unicode_width(unicode
, tab_size
);
515 skip
-= ucwidth
<= skip
? ucwidth
: skip
;
519 if (*width
> max_width
) {
522 if (reserve
&& *width
== max_width
) {
523 string
-= last_bytes
;
524 *width
-= last_ucwidth
;
530 last_bytes
= ucwidth
? bytes
: 0;
531 last_ucwidth
= ucwidth
;
534 return string
- *start
;