Code

Move includes, macros and core utilities to tig.h
[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 /* Revision graph */
81 #define REVGRAPH_INIT   'I'
82 #define REVGRAPH_MERGE  'M'
83 #define REVGRAPH_BRANCH '+'
84 #define REVGRAPH_COMMIT '*'
85 #define REVGRAPH_BOUND  '^'
87 #define SIZEOF_REVGRAPH 19      /* Size of revision ancestry graphics. */
89 /* This color name can be used to refer to the default term colors. */
90 #define COLOR_DEFAULT   (-1)
92 #define ICONV_NONE      ((iconv_t) -1)
93 #ifndef ICONV_CONST
94 #define ICONV_CONST     /* nothing */
95 #endif
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 ")
102 #define ID_COLS         8
103 #define AUTHOR_COLS     19
105 #define MIN_VIEW_HEIGHT 4
107 #define NULL_ID         "0000000000000000000000000000000000000000"
109 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
111 /* Some ASCII-shorthands fitted into the ncurses namespace. */
112 #define KEY_CTL(x)      ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
113 #define KEY_TAB         '\t'
114 #define KEY_RETURN      '\r'
115 #define KEY_ESC         27
117 /*
118  * Allocation helpers ... Entering macro hell to never be seen again.
119  */
121 #define DEFINE_ALLOCATOR(name, type, chunk_size)                                \
122 static type *                                                                   \
123 name(type **mem, size_t size, size_t increase)                                  \
124 {                                                                               \
125         size_t num_chunks = (size + chunk_size - 1) / chunk_size;               \
126         size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
127         type *tmp = *mem;                                                       \
128                                                                                 \
129         if (mem == NULL || num_chunks != num_chunks_new) {                      \
130                 tmp = realloc(tmp, num_chunks_new * chunk_size * sizeof(type)); \
131                 if (tmp)                                                        \
132                         *mem = tmp;                                             \
133         }                                                                       \
134                                                                                 \
135         return tmp;                                                             \
138 /*
139  * Strings.
140  */
142 #define prefixcmp(str1, str2) \
143         strncmp(str1, str2, STRING_SIZE(str2))
145 static inline int
146 suffixcmp(const char *str, int slen, const char *suffix)
148         size_t len = slen >= 0 ? slen : strlen(str);
149         size_t suffixlen = strlen(suffix);
151         return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
154 static inline void
155 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
157         if (srclen > dstlen - 1)
158                 srclen = dstlen - 1;
160         strncpy(dst, src, srclen);
161         dst[srclen] = 0;
164 /* Shorthands for safely copying into a fixed buffer. */
166 #define string_copy(dst, src) \
167         string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
169 #define string_ncopy(dst, src, srclen) \
170         string_ncopy_do(dst, sizeof(dst), src, srclen)
172 #define string_copy_rev(dst, src) \
173         string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
175 #define string_add(dst, from, src) \
176         string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
178 static inline size_t
179 string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
181         size_t size, pos;
183         for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) {
184                 if (src[pos] == '\t') {
185                         size_t expanded = tabsize - (size % tabsize);
187                         if (expanded + size >= dstlen - 1)
188                                 expanded = dstlen - size - 1;
189                         memcpy(dst + size, "        ", expanded);
190                         size += expanded;
191                 } else {
192                         dst[size++] = src[pos];
193                 }
194         }
196         dst[size] = 0;
197         return pos;
200 static inline char *
201 chomp_string(char *name)
203         int namelen;
205         while (isspace(*name))
206                 name++;
208         namelen = strlen(name) - 1;
209         while (namelen > 0 && isspace(name[namelen]))
210                 name[namelen--] = 0;
212         return name;
215 static inline bool
216 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
218         va_list args;
219         size_t pos = bufpos ? *bufpos : 0;
221         va_start(args, fmt);
222         pos += vsnprintf(buf + pos, bufsize - pos, fmt, args);
223         va_end(args);
225         if (bufpos)
226                 *bufpos = pos;
228         return pos >= bufsize ? FALSE : TRUE;
231 #define string_format(buf, fmt, args...) \
232         string_nformat(buf, sizeof(buf), NULL, fmt, args)
234 #define string_format_from(buf, from, fmt, args...) \
235         string_nformat(buf, sizeof(buf), from, fmt, args)
237 /*
238  * Enumerations
239  */
241 struct enum_map {
242         const char *name;
243         int namelen;
244         int value;
245 };
247 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
249 static inline int
250 string_enum_compare(const char *str1, const char *str2, int len)
252         size_t i;
254 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
256         /* Diff-Header == DIFF_HEADER */
257         for (i = 0; i < len; i++) {
258                 if (toupper(str1[i]) == toupper(str2[i]))
259                         continue;
261                 if (string_enum_sep(str1[i]) &&
262                     string_enum_sep(str2[i]))
263                         continue;
265                 return str1[i] - str2[i];
266         }
268         return 0;
271 #define enum_equals(entry, str, len) \
272         ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
274 static inline char *
275 enum_map_name(const char *name, size_t namelen)
277         static char buf[SIZEOF_STR];
278         int bufpos;
280         for (bufpos = 0; bufpos <= namelen; bufpos++) {
281                 buf[bufpos] = tolower(name[bufpos]);
282                 if (buf[bufpos] == '_')
283                         buf[bufpos] = '-';
284         }
286         buf[bufpos] = 0;
287         return buf;
290 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
292 static inline bool
293 map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
295         size_t namelen = strlen(name);
296         int i;
298         for (i = 0; i < map_size; i++)
299                 if (enum_equals(map[i], name, namelen)) {
300                         *value = map[i].value;
301                         return TRUE;
302                 }
304         return FALSE;
307 #define map_enum(attr, map, name) \
308         map_enum_do(map, ARRAY_SIZE(map), attr, name)
310 /*
311  * Unicode / UTF-8 handling
312  *
313  * NOTE: Much of the following code for dealing with Unicode is derived from
314  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
315  * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
316  */
318 static inline int
319 unicode_width(unsigned long c, int tab_size)
321         if (c >= 0x1100 &&
322            (c <= 0x115f                         /* Hangul Jamo */
323             || c == 0x2329
324             || c == 0x232a
325             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
326                                                 /* CJK ... Yi */
327             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
328             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
329             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
330             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
331             || (c >= 0xffe0  && c <= 0xffe6)
332             || (c >= 0x20000 && c <= 0x2fffd)
333             || (c >= 0x30000 && c <= 0x3fffd)))
334                 return 2;
336         if (c == '\t')
337                 return tab_size;
339         return 1;
342 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
343  * Illegal bytes are set one. */
344 static const unsigned char utf8_bytes[256] = {
345         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,
346         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,
347         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,
348         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,
349         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,
350         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,
351         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,
352         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,
353 };
355 static inline unsigned char
356 utf8_char_length(const char *string, const char *end)
358         int c = *(unsigned char *) string;
360         return utf8_bytes[c];
363 /* Decode UTF-8 multi-byte representation into a Unicode character. */
364 static inline unsigned long
365 utf8_to_unicode(const char *string, size_t length)
367         unsigned long unicode;
369         switch (length) {
370         case 1:
371                 unicode  =   string[0];
372                 break;
373         case 2:
374                 unicode  =  (string[0] & 0x1f) << 6;
375                 unicode +=  (string[1] & 0x3f);
376                 break;
377         case 3:
378                 unicode  =  (string[0] & 0x0f) << 12;
379                 unicode += ((string[1] & 0x3f) << 6);
380                 unicode +=  (string[2] & 0x3f);
381                 break;
382         case 4:
383                 unicode  =  (string[0] & 0x0f) << 18;
384                 unicode += ((string[1] & 0x3f) << 12);
385                 unicode += ((string[2] & 0x3f) << 6);
386                 unicode +=  (string[3] & 0x3f);
387                 break;
388         case 5:
389                 unicode  =  (string[0] & 0x0f) << 24;
390                 unicode += ((string[1] & 0x3f) << 18);
391                 unicode += ((string[2] & 0x3f) << 12);
392                 unicode += ((string[3] & 0x3f) << 6);
393                 unicode +=  (string[4] & 0x3f);
394                 break;
395         case 6:
396                 unicode  =  (string[0] & 0x01) << 30;
397                 unicode += ((string[1] & 0x3f) << 24);
398                 unicode += ((string[2] & 0x3f) << 18);
399                 unicode += ((string[3] & 0x3f) << 12);
400                 unicode += ((string[4] & 0x3f) << 6);
401                 unicode +=  (string[5] & 0x3f);
402                 break;
403         default:
404                 return 0;
405         }
407         /* Invalid characters could return the special 0xfffd value but NUL
408          * should be just as good. */
409         return unicode > 0xffff ? 0 : unicode;
412 /* Calculates how much of string can be shown within the given maximum width
413  * and sets trimmed parameter to non-zero value if all of string could not be
414  * shown. If the reserve flag is TRUE, it will reserve at least one
415  * trailing character, which can be useful when drawing a delimiter.
416  *
417  * Returns the number of bytes to output from string to satisfy max_width. */
418 static inline size_t
419 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
421         const char *string = *start;
422         const char *end = strchr(string, '\0');
423         unsigned char last_bytes = 0;
424         size_t last_ucwidth = 0;
426         *width = 0;
427         *trimmed = 0;
429         while (string < end) {
430                 unsigned char bytes = utf8_char_length(string, end);
431                 size_t ucwidth;
432                 unsigned long unicode;
434                 if (string + bytes > end)
435                         break;
437                 /* Change representation to figure out whether
438                  * it is a single- or double-width character. */
440                 unicode = utf8_to_unicode(string, bytes);
441                 /* FIXME: Graceful handling of invalid Unicode character. */
442                 if (!unicode)
443                         break;
445                 ucwidth = unicode_width(unicode, tab_size);
446                 if (skip > 0) {
447                         skip -= ucwidth <= skip ? ucwidth : skip;
448                         *start += bytes;
449                 }
450                 *width  += ucwidth;
451                 if (*width > max_width) {
452                         *trimmed = 1;
453                         *width -= ucwidth;
454                         if (reserve && *width == max_width) {
455                                 string -= last_bytes;
456                                 *width -= last_ucwidth;
457                         }
458                         break;
459                 }
461                 string  += bytes;
462                 last_bytes = ucwidth ? bytes : 0;
463                 last_ucwidth = ucwidth;
464         }
466         return string - *start;
469 #endif