Support jumping to specific SHAs in the main view
[tig.git] / tig.h
blobd45b06afd36af69ea1c0aab2da6a19b587d78629
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 #include <assert.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <signal.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <sys/stat.h>
40 #include <sys/select.h>
41 #include <unistd.h>
42 #include <sys/time.h>
43 #include <time.h>
44 #include <fcntl.h>
46 #include <regex.h>
48 #include <locale.h>
49 #include <langinfo.h>
50 #include <iconv.h>
52 /* ncurses(3): Must be defined to have extended wide-character functions. */
53 #define _XOPEN_SOURCE_EXTENDED
55 #ifdef HAVE_NCURSESW_H
56 #include <ncursesw/ncurses.h>
57 #else
58 #include <ncurses.h>
59 #endif
61 #if __GNUC__ >= 3
62 #define __NORETURN __attribute__((__noreturn__))
63 #else
64 #define __NORETURN
65 #endif
67 #define ABS(x) ((x) >= 0 ? (x) : -(x))
68 #define MIN(x, y) ((x) < (y) ? (x) : (y))
69 #define MAX(x, y) ((x) > (y) ? (x) : (y))
71 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
72 #define STRING_SIZE(x) (sizeof(x) - 1)
74 #define SIZEOF_STR 1024 /* Default string size. */
75 #define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */
76 #define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL. */
77 #define SIZEOF_ARG 32 /* Default argument array size. */
79 /* This color name can be used to refer to the default term colors. */
80 #define COLOR_DEFAULT (-1)
82 #define ICONV_NONE ((iconv_t) -1)
83 #ifndef ICONV_CONST
84 #define ICONV_CONST /* nothing */
85 #endif
87 /* The format and size of the date column in the main view. */
88 #define DATE_FORMAT "%Y-%m-%d %H:%M"
89 #define DATE_COLS STRING_SIZE("2006-04-29 14:21 ")
90 #define DATE_SHORT_COLS STRING_SIZE("2006-04-29 ")
92 #define ID_COLS 8
93 #define AUTHOR_COLS 19
95 #define MIN_VIEW_HEIGHT 4
97 #define NULL_ID "0000000000000000000000000000000000000000"
99 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
101 /* Some ASCII-shorthands fitted into the ncurses namespace. */
102 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
103 #define KEY_TAB '\t'
104 #define KEY_RETURN '\r'
105 #define KEY_ESC 27
108 * Allocation helpers ... Entering macro hell to never be seen again.
111 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
112 static type * \
113 name(type **mem, size_t size, size_t increase) \
115 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
116 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
117 type *tmp = *mem; \
119 if (mem == NULL || num_chunks != num_chunks_new) { \
120 tmp = realloc(tmp, num_chunks_new * chunk_size * sizeof(type)); \
121 if (tmp) \
122 *mem = tmp; \
125 return tmp; \
129 * Strings.
132 #define prefixcmp(str1, str2) \
133 strncmp(str1, str2, STRING_SIZE(str2))
135 static inline bool
136 isnumber(char *str)
138 int pos;
140 for (pos = 0; str[pos]; pos++) {
141 if (!isdigit(str[pos]))
142 return FALSE;
145 return pos > 0;
148 static inline bool
149 iscommit(char *str)
151 int pos;
153 for (pos = 0; str[pos]; pos++) {
154 if (!isxdigit(str[pos]))
155 return FALSE;
158 return 7 <= pos && pos < SIZEOF_REV;
161 static inline int
162 ascii_toupper(int c)
164 if (c >= 'a' && c <= 'z')
165 c &= ~0x20;
166 return c;
169 static inline int
170 ascii_tolower(int c)
172 if (c >= 'A' && c <= 'Z')
173 c |= 0x20;
174 return c;
177 static inline int
178 suffixcmp(const char *str, int slen, const char *suffix)
180 size_t len = slen >= 0 ? slen : strlen(str);
181 size_t suffixlen = strlen(suffix);
183 return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
186 static inline void
187 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
189 if (srclen > dstlen - 1)
190 srclen = dstlen - 1;
192 strncpy(dst, src, srclen);
193 dst[srclen] = 0;
196 /* Shorthands for safely copying into a fixed buffer. */
198 #define string_copy(dst, src) \
199 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
201 #define string_ncopy(dst, src, srclen) \
202 string_ncopy_do(dst, sizeof(dst), src, srclen)
204 #define string_copy_rev(dst, src) \
205 string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
207 #define string_add(dst, from, src) \
208 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
210 static inline size_t
211 string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
213 size_t size, pos;
215 for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) {
216 if (src[pos] == '\t') {
217 size_t expanded = tabsize - (size % tabsize);
219 if (expanded + size >= dstlen - 1)
220 expanded = dstlen - size - 1;
221 memcpy(dst + size, " ", expanded);
222 size += expanded;
223 } else {
224 dst[size++] = src[pos];
228 dst[size] = 0;
229 return pos;
232 static inline char *
233 chomp_string(char *name)
235 int namelen;
237 while (isspace(*name))
238 name++;
240 namelen = strlen(name) - 1;
241 while (namelen > 0 && isspace(name[namelen]))
242 name[namelen--] = 0;
244 return name;
247 static inline bool
248 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
250 va_list args;
251 size_t pos = bufpos ? *bufpos : 0;
253 va_start(args, fmt);
254 pos += vsnprintf(buf + pos, bufsize - pos, fmt, args);
255 va_end(args);
257 if (bufpos)
258 *bufpos = pos;
260 return pos >= bufsize ? FALSE : TRUE;
263 #define string_format(buf, fmt, args...) \
264 string_nformat(buf, sizeof(buf), NULL, fmt, args)
266 #define string_format_from(buf, from, fmt, args...) \
267 string_nformat(buf, sizeof(buf), from, fmt, args)
269 static inline int
270 strcmp_null(const char *s1, const char *s2)
272 if (!s1 || !s2) {
273 return (!!s1) - (!!s2);
276 return strcmp(s1, s2);
280 * Enumerations
283 struct enum_map {
284 const char *name;
285 int namelen;
286 int value;
289 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
291 #define ENUM_SYM_MACRO(prefix, name) prefix##_##name
292 #define ENUM_MAP_MACRO(prefix, name) ENUM_MAP(#name, ENUM_SYM_MACRO(prefix, name))
294 #define DEFINE_ENUM(name, info) \
295 enum name { info(ENUM_SYM_MACRO) }; \
296 static const struct enum_map name##_map[] = { info(ENUM_MAP_MACRO) }
298 static inline int
299 string_enum_compare(const char *str1, const char *str2, int len)
301 size_t i;
303 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
305 /* Diff-Header == DIFF_HEADER */
306 for (i = 0; i < len; i++) {
307 if (ascii_toupper(str1[i]) == ascii_toupper(str2[i]))
308 continue;
310 if (string_enum_sep(str1[i]) &&
311 string_enum_sep(str2[i]))
312 continue;
314 return str1[i] - str2[i];
317 return 0;
320 #define enum_equals(entry, str, len) \
321 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
323 static inline char *
324 enum_map_name(const char *name, size_t namelen)
326 static char buf[SIZEOF_STR];
327 int bufpos;
329 for (bufpos = 0; bufpos <= namelen; bufpos++) {
330 buf[bufpos] = ascii_tolower(name[bufpos]);
331 if (buf[bufpos] == '_')
332 buf[bufpos] = '-';
335 buf[bufpos] = 0;
336 return buf;
339 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
341 static inline bool
342 map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
344 size_t namelen = strlen(name);
345 int i;
347 for (i = 0; i < map_size; i++)
348 if (enum_equals(map[i], name, namelen)) {
349 *value = map[i].value;
350 return TRUE;
353 return FALSE;
356 #define map_enum(attr, map, name) \
357 map_enum_do(map, ARRAY_SIZE(map), attr, name)
360 * Unicode / UTF-8 handling
362 * NOTE: Much of the following code for dealing with Unicode is derived from
363 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
364 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
367 static inline int
368 unicode_width(unsigned long c, int tab_size)
370 if (c >= 0x1100 &&
371 (c <= 0x115f /* Hangul Jamo */
372 || c == 0x2329
373 || c == 0x232a
374 || (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f)
375 /* CJK ... Yi */
376 || (c >= 0xac00 && c <= 0xd7a3) /* Hangul Syllables */
377 || (c >= 0xf900 && c <= 0xfaff) /* CJK Compatibility Ideographs */
378 || (c >= 0xfe30 && c <= 0xfe6f) /* CJK Compatibility Forms */
379 || (c >= 0xff00 && c <= 0xff60) /* Fullwidth Forms */
380 || (c >= 0xffe0 && c <= 0xffe6)
381 || (c >= 0x20000 && c <= 0x2fffd)
382 || (c >= 0x30000 && c <= 0x3fffd)))
383 return 2;
385 if (c == '\t')
386 return tab_size;
388 return 1;
391 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
392 * Illegal bytes are set one. */
393 static const unsigned char utf8_bytes[256] = {
394 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,
395 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,
396 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,
397 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,
398 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,
399 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,
400 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,
401 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,
404 static inline unsigned char
405 utf8_char_length(const char *string, const char *end)
407 int c = *(unsigned char *) string;
409 return utf8_bytes[c];
412 /* Decode UTF-8 multi-byte representation into a Unicode character. */
413 static inline unsigned long
414 utf8_to_unicode(const char *string, size_t length)
416 unsigned long unicode;
418 switch (length) {
419 case 1:
420 unicode = string[0];
421 break;
422 case 2:
423 unicode = (string[0] & 0x1f) << 6;
424 unicode += (string[1] & 0x3f);
425 break;
426 case 3:
427 unicode = (string[0] & 0x0f) << 12;
428 unicode += ((string[1] & 0x3f) << 6);
429 unicode += (string[2] & 0x3f);
430 break;
431 case 4:
432 unicode = (string[0] & 0x0f) << 18;
433 unicode += ((string[1] & 0x3f) << 12);
434 unicode += ((string[2] & 0x3f) << 6);
435 unicode += (string[3] & 0x3f);
436 break;
437 case 5:
438 unicode = (string[0] & 0x0f) << 24;
439 unicode += ((string[1] & 0x3f) << 18);
440 unicode += ((string[2] & 0x3f) << 12);
441 unicode += ((string[3] & 0x3f) << 6);
442 unicode += (string[4] & 0x3f);
443 break;
444 case 6:
445 unicode = (string[0] & 0x01) << 30;
446 unicode += ((string[1] & 0x3f) << 24);
447 unicode += ((string[2] & 0x3f) << 18);
448 unicode += ((string[3] & 0x3f) << 12);
449 unicode += ((string[4] & 0x3f) << 6);
450 unicode += (string[5] & 0x3f);
451 break;
452 default:
453 return 0;
456 /* Invalid characters could return the special 0xfffd value but NUL
457 * should be just as good. */
458 return unicode > 0xffff ? 0 : unicode;
461 /* Calculates how much of string can be shown within the given maximum width
462 * and sets trimmed parameter to non-zero value if all of string could not be
463 * shown. If the reserve flag is TRUE, it will reserve at least one
464 * trailing character, which can be useful when drawing a delimiter.
466 * Returns the number of bytes to output from string to satisfy max_width. */
467 static inline size_t
468 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
470 const char *string = *start;
471 const char *end = strchr(string, '\0');
472 unsigned char last_bytes = 0;
473 size_t last_ucwidth = 0;
475 *width = 0;
476 *trimmed = 0;
478 while (string < end) {
479 unsigned char bytes = utf8_char_length(string, end);
480 size_t ucwidth;
481 unsigned long unicode;
483 if (string + bytes > end)
484 break;
486 /* Change representation to figure out whether
487 * it is a single- or double-width character. */
489 unicode = utf8_to_unicode(string, bytes);
490 /* FIXME: Graceful handling of invalid Unicode character. */
491 if (!unicode)
492 break;
494 ucwidth = unicode_width(unicode, tab_size);
495 if (skip > 0) {
496 skip -= ucwidth <= skip ? ucwidth : skip;
497 *start += bytes;
499 *width += ucwidth;
500 if (*width > max_width) {
501 *trimmed = 1;
502 *width -= ucwidth;
503 if (reserve && *width == max_width) {
504 string -= last_bytes;
505 *width -= last_ucwidth;
507 break;
510 string += bytes;
511 last_bytes = ucwidth ? bytes : 0;
512 last_ucwidth = ucwidth;
515 return string - *start;
518 #endif