Add support for using path encoding from git attributes
[tig.git] / tig.h
blob12ebe3ddc2fb9967b1597e056231e326e8909a74
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.
14 #ifndef TIG_H
15 #define TIG_H
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
21 #ifndef TIG_VERSION
22 #define TIG_VERSION "unknown-version"
23 #endif
25 #ifndef DEBUG
26 #define NDEBUG
27 #endif
29 /* necessary on Snow Leopard to use WINDOW struct */
30 #ifdef NCURSES_OPAQUE
31 #undef NCURSES_OPAQUE
32 #endif
33 #define NCURSES_OPAQUE 0
36 #include <assert.h>
37 #include <errno.h>
38 #include <ctype.h>
39 #include <signal.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <sys/stat.h>
47 #include <sys/select.h>
48 #include <unistd.h>
49 #include <sys/time.h>
50 #include <time.h>
51 #include <fcntl.h>
53 #include <regex.h>
55 #include <locale.h>
56 #include <langinfo.h>
57 #include <iconv.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>
64 #else
65 #include <ncurses.h>
66 #endif
68 #if __GNUC__ >= 3
69 #define __NORETURN __attribute__((__noreturn__))
70 #else
71 #define __NORETURN
72 #endif
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: "
92 #define ICONV_NONE ((iconv_t) -1)
93 #ifndef ICONV_CONST
94 #define ICONV_CONST /* nothing */
95 #endif
96 #define ICONV_TRANSLIT "//TRANSLIT"
98 /* The format and size of the date column in the main view. */
99 #define DATE_FORMAT "%Y-%m-%d %H:%M"
100 #define DATE_COLS STRING_SIZE("2006-04-29 14:21 ")
101 #define DATE_SHORT_COLS STRING_SIZE("2006-04-29 ")
103 #define ID_COLS 8
104 #define AUTHOR_COLS 19
105 #define FILENAME_COLS 19
107 #define MIN_VIEW_HEIGHT 4
109 #define NULL_ID "0000000000000000000000000000000000000000"
111 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
113 /* Some ASCII-shorthands fitted into the ncurses namespace. */
114 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
115 #define KEY_TAB '\t'
116 #define KEY_RETURN '\r'
117 #define KEY_ESC 27
120 * Allocation helpers ... Entering macro hell to never be seen again.
123 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
124 static type * \
125 name(type **mem, size_t size, size_t increase) \
127 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
128 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
129 type *tmp = *mem; \
131 if (mem == NULL || num_chunks != num_chunks_new) { \
132 tmp = realloc(tmp, num_chunks_new * chunk_size * sizeof(type)); \
133 if (tmp) \
134 *mem = tmp; \
137 return tmp; \
141 * Strings.
144 #define prefixcmp(str1, str2) \
145 strncmp(str1, str2, STRING_SIZE(str2))
147 static inline bool
148 string_isnumber(const char *str)
150 int pos;
152 for (pos = 0; str[pos]; pos++) {
153 if (!isdigit(str[pos]))
154 return FALSE;
157 return pos > 0;
160 static inline bool
161 iscommit(char *str)
163 int pos;
165 for (pos = 0; str[pos]; pos++) {
166 if (!isxdigit(str[pos]))
167 return FALSE;
170 return 7 <= pos && pos < SIZEOF_REV;
173 static inline int
174 ascii_toupper(int c)
176 if (c >= 'a' && c <= 'z')
177 c &= ~0x20;
178 return c;
181 static inline int
182 ascii_tolower(int c)
184 if (c >= 'A' && c <= 'Z')
185 c |= 0x20;
186 return c;
189 static inline int
190 suffixcmp(const char *str, int slen, const char *suffix)
192 size_t len = slen >= 0 ? slen : strlen(str);
193 size_t suffixlen = strlen(suffix);
195 return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
198 static inline void
199 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
201 if (srclen > dstlen - 1)
202 srclen = dstlen - 1;
204 strncpy(dst, src, srclen);
205 dst[srclen] = 0;
208 /* Shorthands for safely copying into a fixed buffer. */
210 #define FORMAT_BUFFER(buf, bufsize, fmt, retval, allow_truncate) \
211 do { \
212 va_list args; \
213 va_start(args, fmt); \
214 retval = vsnprintf(buf, bufsize, fmt, args); \
215 va_end(args); \
216 if (retval >= (bufsize) && allow_truncate) { \
217 (buf)[(bufsize) - 1] = 0; \
218 (buf)[(bufsize) - 2] = '.'; \
219 (buf)[(bufsize) - 3] = '.'; \
220 (buf)[(bufsize) - 4] = '.'; \
221 retval = (bufsize) - 1; \
222 } else if (retval < 0 || retval >= (bufsize)) { \
223 retval = -1; \
225 } while (0)
227 #define string_copy(dst, src) \
228 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
230 #define string_ncopy(dst, src, srclen) \
231 string_ncopy_do(dst, sizeof(dst), src, srclen)
233 #define string_copy_rev(dst, src) \
234 string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
236 #define string_add(dst, from, src) \
237 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
239 static inline size_t
240 string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
242 size_t size, pos;
244 for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) {
245 if (src[pos] == '\t') {
246 size_t expanded = tabsize - (size % tabsize);
248 if (expanded + size >= dstlen - 1)
249 expanded = dstlen - size - 1;
250 memcpy(dst + size, " ", expanded);
251 size += expanded;
252 } else {
253 dst[size++] = src[pos];
257 dst[size] = 0;
258 return pos;
261 static inline char *
262 chomp_string(char *name)
264 int namelen;
266 while (isspace(*name))
267 name++;
269 namelen = strlen(name) - 1;
270 while (namelen > 0 && isspace(name[namelen]))
271 name[namelen--] = 0;
273 return name;
276 static inline bool
277 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
279 size_t pos = bufpos ? *bufpos : 0;
280 int retval;
282 FORMAT_BUFFER(buf + pos, bufsize - pos, fmt, retval, FALSE);
283 if (bufpos && retval > 0)
284 *bufpos = pos + retval;
286 return pos >= bufsize ? FALSE : TRUE;
289 #define string_format(buf, fmt, args...) \
290 string_nformat(buf, sizeof(buf), NULL, fmt, args)
292 #define string_format_from(buf, from, fmt, args...) \
293 string_nformat(buf, sizeof(buf), from, fmt, args)
295 static inline int
296 strcmp_null(const char *s1, const char *s2)
298 if (!s1 || !s2) {
299 return (!!s1) - (!!s2);
302 return strcmp(s1, s2);
306 * Enumerations
309 struct enum_map {
310 const char *name;
311 int namelen;
312 int value;
315 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
317 #define ENUM_SYM_MACRO(prefix, name) prefix##_##name
318 #define ENUM_MAP_MACRO(prefix, name) ENUM_MAP(#name, ENUM_SYM_MACRO(prefix, name))
320 #define DEFINE_ENUM(name, info) \
321 enum name { info(ENUM_SYM_MACRO) }; \
322 static const struct enum_map name##_map[] = { info(ENUM_MAP_MACRO) }
324 static inline int
325 string_enum_compare(const char *str1, const char *str2, int len)
327 size_t i;
329 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
331 /* Diff-Header == DIFF_HEADER */
332 for (i = 0; i < len; i++) {
333 if (ascii_toupper(str1[i]) == ascii_toupper(str2[i]))
334 continue;
336 if (string_enum_sep(str1[i]) &&
337 string_enum_sep(str2[i]))
338 continue;
340 return str1[i] - str2[i];
343 return 0;
346 #define enum_equals(entry, str, len) \
347 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
349 static inline char *
350 enum_map_name(const char *name, size_t namelen)
352 static char buf[SIZEOF_STR];
353 int bufpos;
355 for (bufpos = 0; bufpos <= namelen; bufpos++) {
356 buf[bufpos] = ascii_tolower(name[bufpos]);
357 if (buf[bufpos] == '_')
358 buf[bufpos] = '-';
361 buf[bufpos] = 0;
362 return buf;
365 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
367 static inline bool
368 map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
370 size_t namelen = strlen(name);
371 int i;
373 for (i = 0; i < map_size; i++)
374 if (enum_equals(map[i], name, namelen)) {
375 *value = map[i].value;
376 return TRUE;
379 return FALSE;
382 #define map_enum(attr, map, name) \
383 map_enum_do(map, ARRAY_SIZE(map), attr, name)
386 * Unicode / UTF-8 handling
388 * NOTE: Much of the following code for dealing with Unicode is derived from
389 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
390 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
393 static inline int
394 unicode_width(unsigned long c, int tab_size)
396 if (c >= 0x1100 &&
397 (c <= 0x115f /* Hangul Jamo */
398 || c == 0x2329
399 || c == 0x232a
400 || (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f)
401 /* CJK ... Yi */
402 || (c >= 0xac00 && c <= 0xd7a3) /* Hangul Syllables */
403 || (c >= 0xf900 && c <= 0xfaff) /* CJK Compatibility Ideographs */
404 || (c >= 0xfe30 && c <= 0xfe6f) /* CJK Compatibility Forms */
405 || (c >= 0xff00 && c <= 0xff60) /* Fullwidth Forms */
406 || (c >= 0xffe0 && c <= 0xffe6)
407 || (c >= 0x20000 && c <= 0x2fffd)
408 || (c >= 0x30000 && c <= 0x3fffd)))
409 return 2;
411 if (c == '\t')
412 return tab_size;
414 return 1;
417 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
418 * Illegal bytes are set one. */
419 static const unsigned char utf8_bytes[256] = {
420 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,
421 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,
422 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,
423 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,
424 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,
425 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,
426 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,
427 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,
430 static inline unsigned char
431 utf8_char_length(const char *string, const char *end)
433 int c = *(unsigned char *) string;
435 return utf8_bytes[c];
438 /* Decode UTF-8 multi-byte representation into a Unicode character. */
439 static inline unsigned long
440 utf8_to_unicode(const char *string, size_t length)
442 unsigned long unicode;
444 switch (length) {
445 case 1:
446 unicode = string[0];
447 break;
448 case 2:
449 unicode = (string[0] & 0x1f) << 6;
450 unicode += (string[1] & 0x3f);
451 break;
452 case 3:
453 unicode = (string[0] & 0x0f) << 12;
454 unicode += ((string[1] & 0x3f) << 6);
455 unicode += (string[2] & 0x3f);
456 break;
457 case 4:
458 unicode = (string[0] & 0x0f) << 18;
459 unicode += ((string[1] & 0x3f) << 12);
460 unicode += ((string[2] & 0x3f) << 6);
461 unicode += (string[3] & 0x3f);
462 break;
463 case 5:
464 unicode = (string[0] & 0x0f) << 24;
465 unicode += ((string[1] & 0x3f) << 18);
466 unicode += ((string[2] & 0x3f) << 12);
467 unicode += ((string[3] & 0x3f) << 6);
468 unicode += (string[4] & 0x3f);
469 break;
470 case 6:
471 unicode = (string[0] & 0x01) << 30;
472 unicode += ((string[1] & 0x3f) << 24);
473 unicode += ((string[2] & 0x3f) << 18);
474 unicode += ((string[3] & 0x3f) << 12);
475 unicode += ((string[4] & 0x3f) << 6);
476 unicode += (string[5] & 0x3f);
477 break;
478 default:
479 return 0;
482 /* Invalid characters could return the special 0xfffd value but NUL
483 * should be just as good. */
484 return unicode > 0xffff ? 0 : unicode;
487 /* Calculates how much of string can be shown within the given maximum width
488 * and sets trimmed parameter to non-zero value if all of string could not be
489 * shown. If the reserve flag is TRUE, it will reserve at least one
490 * trailing character, which can be useful when drawing a delimiter.
492 * Returns the number of bytes to output from string to satisfy max_width. */
493 static inline size_t
494 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
496 const char *string = *start;
497 const char *end = strchr(string, '\0');
498 unsigned char last_bytes = 0;
499 size_t last_ucwidth = 0;
501 *width = 0;
502 *trimmed = 0;
504 while (string < end) {
505 unsigned char bytes = utf8_char_length(string, end);
506 size_t ucwidth;
507 unsigned long unicode;
509 if (string + bytes > end)
510 break;
512 /* Change representation to figure out whether
513 * it is a single- or double-width character. */
515 unicode = utf8_to_unicode(string, bytes);
516 /* FIXME: Graceful handling of invalid Unicode character. */
517 if (!unicode)
518 break;
520 ucwidth = unicode_width(unicode, tab_size);
521 if (skip > 0) {
522 skip -= ucwidth <= skip ? ucwidth : skip;
523 *start += bytes;
525 *width += ucwidth;
526 if (*width > max_width) {
527 *trimmed = 1;
528 *width -= ucwidth;
529 if (reserve && *width == max_width) {
530 string -= last_bytes;
531 *width -= last_ucwidth;
533 break;
536 string += bytes;
537 last_bytes = ucwidth ? bytes : 0;
538 last_ucwidth = ucwidth;
541 return string - *start;
544 #endif