Code

Add specialized open methods for each view
[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 suffixcmp(const char *str, int slen, const char *suffix)
138         size_t len = slen >= 0 ? slen : strlen(str);
139         size_t suffixlen = strlen(suffix);
141         return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
144 static inline void
145 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
147         if (srclen > dstlen - 1)
148                 srclen = dstlen - 1;
150         strncpy(dst, src, srclen);
151         dst[srclen] = 0;
154 /* Shorthands for safely copying into a fixed buffer. */
156 #define string_copy(dst, src) \
157         string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
159 #define string_ncopy(dst, src, srclen) \
160         string_ncopy_do(dst, sizeof(dst), src, srclen)
162 #define string_copy_rev(dst, src) \
163         string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
165 #define string_add(dst, from, src) \
166         string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
168 static inline size_t
169 string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
171         size_t size, pos;
173         for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) {
174                 if (src[pos] == '\t') {
175                         size_t expanded = tabsize - (size % tabsize);
177                         if (expanded + size >= dstlen - 1)
178                                 expanded = dstlen - size - 1;
179                         memcpy(dst + size, "        ", expanded);
180                         size += expanded;
181                 } else {
182                         dst[size++] = src[pos];
183                 }
184         }
186         dst[size] = 0;
187         return pos;
190 static inline char *
191 chomp_string(char *name)
193         int namelen;
195         while (isspace(*name))
196                 name++;
198         namelen = strlen(name) - 1;
199         while (namelen > 0 && isspace(name[namelen]))
200                 name[namelen--] = 0;
202         return name;
205 static inline bool
206 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
208         va_list args;
209         size_t pos = bufpos ? *bufpos : 0;
211         va_start(args, fmt);
212         pos += vsnprintf(buf + pos, bufsize - pos, fmt, args);
213         va_end(args);
215         if (bufpos)
216                 *bufpos = pos;
218         return pos >= bufsize ? FALSE : TRUE;
221 #define string_format(buf, fmt, args...) \
222         string_nformat(buf, sizeof(buf), NULL, fmt, args)
224 #define string_format_from(buf, from, fmt, args...) \
225         string_nformat(buf, sizeof(buf), from, fmt, args)
227 /*
228  * Enumerations
229  */
231 struct enum_map {
232         const char *name;
233         int namelen;
234         int value;
235 };
237 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
239 static inline int
240 string_enum_compare(const char *str1, const char *str2, int len)
242         size_t i;
244 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
246         /* Diff-Header == DIFF_HEADER */
247         for (i = 0; i < len; i++) {
248                 if (toupper(str1[i]) == toupper(str2[i]))
249                         continue;
251                 if (string_enum_sep(str1[i]) &&
252                     string_enum_sep(str2[i]))
253                         continue;
255                 return str1[i] - str2[i];
256         }
258         return 0;
261 #define enum_equals(entry, str, len) \
262         ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
264 static inline char *
265 enum_map_name(const char *name, size_t namelen)
267         static char buf[SIZEOF_STR];
268         int bufpos;
270         for (bufpos = 0; bufpos <= namelen; bufpos++) {
271                 buf[bufpos] = tolower(name[bufpos]);
272                 if (buf[bufpos] == '_')
273                         buf[bufpos] = '-';
274         }
276         buf[bufpos] = 0;
277         return buf;
280 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
282 static inline bool
283 map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
285         size_t namelen = strlen(name);
286         int i;
288         for (i = 0; i < map_size; i++)
289                 if (enum_equals(map[i], name, namelen)) {
290                         *value = map[i].value;
291                         return TRUE;
292                 }
294         return FALSE;
297 #define map_enum(attr, map, name) \
298         map_enum_do(map, ARRAY_SIZE(map), attr, name)
300 /*
301  * Unicode / UTF-8 handling
302  *
303  * NOTE: Much of the following code for dealing with Unicode is derived from
304  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
305  * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
306  */
308 static inline int
309 unicode_width(unsigned long c, int tab_size)
311         if (c >= 0x1100 &&
312            (c <= 0x115f                         /* Hangul Jamo */
313             || c == 0x2329
314             || c == 0x232a
315             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
316                                                 /* CJK ... Yi */
317             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
318             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
319             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
320             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
321             || (c >= 0xffe0  && c <= 0xffe6)
322             || (c >= 0x20000 && c <= 0x2fffd)
323             || (c >= 0x30000 && c <= 0x3fffd)))
324                 return 2;
326         if (c == '\t')
327                 return tab_size;
329         return 1;
332 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
333  * Illegal bytes are set one. */
334 static const unsigned char utf8_bytes[256] = {
335         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,
336         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,
337         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,
338         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,
339         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,
340         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,
341         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,
342         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,
343 };
345 static inline unsigned char
346 utf8_char_length(const char *string, const char *end)
348         int c = *(unsigned char *) string;
350         return utf8_bytes[c];
353 /* Decode UTF-8 multi-byte representation into a Unicode character. */
354 static inline unsigned long
355 utf8_to_unicode(const char *string, size_t length)
357         unsigned long unicode;
359         switch (length) {
360         case 1:
361                 unicode  =   string[0];
362                 break;
363         case 2:
364                 unicode  =  (string[0] & 0x1f) << 6;
365                 unicode +=  (string[1] & 0x3f);
366                 break;
367         case 3:
368                 unicode  =  (string[0] & 0x0f) << 12;
369                 unicode += ((string[1] & 0x3f) << 6);
370                 unicode +=  (string[2] & 0x3f);
371                 break;
372         case 4:
373                 unicode  =  (string[0] & 0x0f) << 18;
374                 unicode += ((string[1] & 0x3f) << 12);
375                 unicode += ((string[2] & 0x3f) << 6);
376                 unicode +=  (string[3] & 0x3f);
377                 break;
378         case 5:
379                 unicode  =  (string[0] & 0x0f) << 24;
380                 unicode += ((string[1] & 0x3f) << 18);
381                 unicode += ((string[2] & 0x3f) << 12);
382                 unicode += ((string[3] & 0x3f) << 6);
383                 unicode +=  (string[4] & 0x3f);
384                 break;
385         case 6:
386                 unicode  =  (string[0] & 0x01) << 30;
387                 unicode += ((string[1] & 0x3f) << 24);
388                 unicode += ((string[2] & 0x3f) << 18);
389                 unicode += ((string[3] & 0x3f) << 12);
390                 unicode += ((string[4] & 0x3f) << 6);
391                 unicode +=  (string[5] & 0x3f);
392                 break;
393         default:
394                 return 0;
395         }
397         /* Invalid characters could return the special 0xfffd value but NUL
398          * should be just as good. */
399         return unicode > 0xffff ? 0 : unicode;
402 /* Calculates how much of string can be shown within the given maximum width
403  * and sets trimmed parameter to non-zero value if all of string could not be
404  * shown. If the reserve flag is TRUE, it will reserve at least one
405  * trailing character, which can be useful when drawing a delimiter.
406  *
407  * Returns the number of bytes to output from string to satisfy max_width. */
408 static inline size_t
409 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
411         const char *string = *start;
412         const char *end = strchr(string, '\0');
413         unsigned char last_bytes = 0;
414         size_t last_ucwidth = 0;
416         *width = 0;
417         *trimmed = 0;
419         while (string < end) {
420                 unsigned char bytes = utf8_char_length(string, end);
421                 size_t ucwidth;
422                 unsigned long unicode;
424                 if (string + bytes > end)
425                         break;
427                 /* Change representation to figure out whether
428                  * it is a single- or double-width character. */
430                 unicode = utf8_to_unicode(string, bytes);
431                 /* FIXME: Graceful handling of invalid Unicode character. */
432                 if (!unicode)
433                         break;
435                 ucwidth = unicode_width(unicode, tab_size);
436                 if (skip > 0) {
437                         skip -= ucwidth <= skip ? ucwidth : skip;
438                         *start += bytes;
439                 }
440                 *width  += ucwidth;
441                 if (*width > max_width) {
442                         *trimmed = 1;
443                         *width -= ucwidth;
444                         if (reserve && *width == max_width) {
445                                 string -= last_bytes;
446                                 *width -= last_ucwidth;
447                         }
448                         break;
449                 }
451                 string  += bytes;
452                 last_bytes = ucwidth ? bytes : 0;
453                 last_ucwidth = ucwidth;
454         }
456         return string - *start;
459 #endif