Code

get_author_initials: various fixes
[tig.git] / tig.h
1 /* Copyright (c) 2006-2010 Jonas Fonseca <fonseca@diku.dk>
2  *
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.
7  *
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.
12  */
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
107 /*
108  * Allocation helpers ... Entering macro hell to never be seen again.
109  */
111 #define DEFINE_ALLOCATOR(name, type, chunk_size)                                \
112 static type *                                                                   \
113 name(type **mem, size_t size, size_t increase)                                  \
114 {                                                                               \
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;                                                       \
118                                                                                 \
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;                                             \
123         }                                                                       \
124                                                                                 \
125         return tmp;                                                             \
128 /*
129  * Strings.
130  */
132 #define prefixcmp(str1, str2) \
133         strncmp(str1, str2, STRING_SIZE(str2))
135 static inline int
136 ascii_toupper(int c)
138         if (c >= 'a' && c <= 'z')
139                 c &= ~0x20;
140         return c;
143 static inline int
144 ascii_tolower(int c)
146         if (c >= 'A' && c <= 'Z')
147                 c |= 0x20;
148         return c;
151 static inline int
152 suffixcmp(const char *str, int slen, const char *suffix)
154         size_t len = slen >= 0 ? slen : strlen(str);
155         size_t suffixlen = strlen(suffix);
157         return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
160 static inline void
161 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
163         if (srclen > dstlen - 1)
164                 srclen = dstlen - 1;
166         strncpy(dst, src, srclen);
167         dst[srclen] = 0;
170 /* Shorthands for safely copying into a fixed buffer. */
172 #define string_copy(dst, src) \
173         string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
175 #define string_ncopy(dst, src, srclen) \
176         string_ncopy_do(dst, sizeof(dst), src, srclen)
178 #define string_copy_rev(dst, src) \
179         string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
181 #define string_add(dst, from, src) \
182         string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
184 static inline size_t
185 string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
187         size_t size, pos;
189         for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) {
190                 if (src[pos] == '\t') {
191                         size_t expanded = tabsize - (size % tabsize);
193                         if (expanded + size >= dstlen - 1)
194                                 expanded = dstlen - size - 1;
195                         memcpy(dst + size, "        ", expanded);
196                         size += expanded;
197                 } else {
198                         dst[size++] = src[pos];
199                 }
200         }
202         dst[size] = 0;
203         return pos;
206 static inline char *
207 chomp_string(char *name)
209         int namelen;
211         while (isspace(*name))
212                 name++;
214         namelen = strlen(name) - 1;
215         while (namelen > 0 && isspace(name[namelen]))
216                 name[namelen--] = 0;
218         return name;
221 static inline bool
222 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
224         va_list args;
225         size_t pos = bufpos ? *bufpos : 0;
227         va_start(args, fmt);
228         pos += vsnprintf(buf + pos, bufsize - pos, fmt, args);
229         va_end(args);
231         if (bufpos)
232                 *bufpos = pos;
234         return pos >= bufsize ? FALSE : TRUE;
237 #define string_format(buf, fmt, args...) \
238         string_nformat(buf, sizeof(buf), NULL, fmt, args)
240 #define string_format_from(buf, from, fmt, args...) \
241         string_nformat(buf, sizeof(buf), from, fmt, args)
243 /*
244  * Enumerations
245  */
247 struct enum_map {
248         const char *name;
249         int namelen;
250         int value;
251 };
253 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
255 #define ENUM_SYM_MACRO(prefix, name)    prefix##_##name
256 #define ENUM_MAP_MACRO(prefix, name)    ENUM_MAP(#name, ENUM_SYM_MACRO(prefix, name))
258 #define DEFINE_ENUM(name, info) \
259         enum name { info(ENUM_SYM_MACRO) }; \
260         static const struct enum_map name##_map[] = { info(ENUM_MAP_MACRO) }
262 static inline int
263 string_enum_compare(const char *str1, const char *str2, int len)
265         size_t i;
267 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
269         /* Diff-Header == DIFF_HEADER */
270         for (i = 0; i < len; i++) {
271                 if (ascii_toupper(str1[i]) == ascii_toupper(str2[i]))
272                         continue;
274                 if (string_enum_sep(str1[i]) &&
275                     string_enum_sep(str2[i]))
276                         continue;
278                 return str1[i] - str2[i];
279         }
281         return 0;
284 #define enum_equals(entry, str, len) \
285         ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
287 static inline char *
288 enum_map_name(const char *name, size_t namelen)
290         static char buf[SIZEOF_STR];
291         int bufpos;
293         for (bufpos = 0; bufpos <= namelen; bufpos++) {
294                 buf[bufpos] = ascii_tolower(name[bufpos]);
295                 if (buf[bufpos] == '_')
296                         buf[bufpos] = '-';
297         }
299         buf[bufpos] = 0;
300         return buf;
303 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
305 static inline bool
306 map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
308         size_t namelen = strlen(name);
309         int i;
311         for (i = 0; i < map_size; i++)
312                 if (enum_equals(map[i], name, namelen)) {
313                         *value = map[i].value;
314                         return TRUE;
315                 }
317         return FALSE;
320 #define map_enum(attr, map, name) \
321         map_enum_do(map, ARRAY_SIZE(map), attr, name)
323 /*
324  * Unicode / UTF-8 handling
325  *
326  * NOTE: Much of the following code for dealing with Unicode is derived from
327  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
328  * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
329  */
331 static inline int
332 unicode_width(unsigned long c, int tab_size)
334         if (c >= 0x1100 &&
335            (c <= 0x115f                         /* Hangul Jamo */
336             || c == 0x2329
337             || c == 0x232a
338             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
339                                                 /* CJK ... Yi */
340             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
341             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
342             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
343             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
344             || (c >= 0xffe0  && c <= 0xffe6)
345             || (c >= 0x20000 && c <= 0x2fffd)
346             || (c >= 0x30000 && c <= 0x3fffd)))
347                 return 2;
349         if (c == '\t')
350                 return tab_size;
352         return 1;
355 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
356  * Illegal bytes are set one. */
357 static const unsigned char utf8_bytes[256] = {
358         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,
359         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,
360         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,
361         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,
362         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,
363         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,
364         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,
365         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,
366 };
368 static inline unsigned char
369 utf8_char_length(const char *string, const char *end)
371         int c = *(unsigned char *) string;
373         return utf8_bytes[c];
376 /* Decode UTF-8 multi-byte representation into a Unicode character. */
377 static inline unsigned long
378 utf8_to_unicode(const char *string, size_t length)
380         unsigned long unicode;
382         switch (length) {
383         case 1:
384                 unicode  =   string[0];
385                 break;
386         case 2:
387                 unicode  =  (string[0] & 0x1f) << 6;
388                 unicode +=  (string[1] & 0x3f);
389                 break;
390         case 3:
391                 unicode  =  (string[0] & 0x0f) << 12;
392                 unicode += ((string[1] & 0x3f) << 6);
393                 unicode +=  (string[2] & 0x3f);
394                 break;
395         case 4:
396                 unicode  =  (string[0] & 0x0f) << 18;
397                 unicode += ((string[1] & 0x3f) << 12);
398                 unicode += ((string[2] & 0x3f) << 6);
399                 unicode +=  (string[3] & 0x3f);
400                 break;
401         case 5:
402                 unicode  =  (string[0] & 0x0f) << 24;
403                 unicode += ((string[1] & 0x3f) << 18);
404                 unicode += ((string[2] & 0x3f) << 12);
405                 unicode += ((string[3] & 0x3f) << 6);
406                 unicode +=  (string[4] & 0x3f);
407                 break;
408         case 6:
409                 unicode  =  (string[0] & 0x01) << 30;
410                 unicode += ((string[1] & 0x3f) << 24);
411                 unicode += ((string[2] & 0x3f) << 18);
412                 unicode += ((string[3] & 0x3f) << 12);
413                 unicode += ((string[4] & 0x3f) << 6);
414                 unicode +=  (string[5] & 0x3f);
415                 break;
416         default:
417                 return 0;
418         }
420         /* Invalid characters could return the special 0xfffd value but NUL
421          * should be just as good. */
422         return unicode > 0xffff ? 0 : unicode;
425 /* Calculates how much of string can be shown within the given maximum width
426  * and sets trimmed parameter to non-zero value if all of string could not be
427  * shown. If the reserve flag is TRUE, it will reserve at least one
428  * trailing character, which can be useful when drawing a delimiter.
429  *
430  * Returns the number of bytes to output from string to satisfy max_width. */
431 static inline size_t
432 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
434         const char *string = *start;
435         const char *end = strchr(string, '\0');
436         unsigned char last_bytes = 0;
437         size_t last_ucwidth = 0;
439         *width = 0;
440         *trimmed = 0;
442         while (string < end) {
443                 unsigned char bytes = utf8_char_length(string, end);
444                 size_t ucwidth;
445                 unsigned long unicode;
447                 if (string + bytes > end)
448                         break;
450                 /* Change representation to figure out whether
451                  * it is a single- or double-width character. */
453                 unicode = utf8_to_unicode(string, bytes);
454                 /* FIXME: Graceful handling of invalid Unicode character. */
455                 if (!unicode)
456                         break;
458                 ucwidth = unicode_width(unicode, tab_size);
459                 if (skip > 0) {
460                         skip -= ucwidth <= skip ? ucwidth : skip;
461                         *start += bytes;
462                 }
463                 *width  += ucwidth;
464                 if (*width > max_width) {
465                         *trimmed = 1;
466                         *width -= ucwidth;
467                         if (reserve && *width == max_width) {
468                                 string -= last_bytes;
469                                 *width -= last_ucwidth;
470                         }
471                         break;
472                 }
474                 string  += bytes;
475                 last_bytes = ucwidth ? bytes : 0;
476                 last_ucwidth = ucwidth;
477         }
479         return string - *start;
482 #endif