Move enum definitions to new types module
[tig.git] / include / tig.h
blob2e503b336d717e2865f07fa1c5cde51ef28d4966
1 /* Copyright (c) 2006-2013 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 #include "compat/compat.h"
23 #ifndef TIG_VERSION
24 #define TIG_VERSION "unknown-version"
25 #endif
27 #ifndef DEBUG
28 #define NDEBUG
29 #endif
31 /* necessary on Snow Leopard to use WINDOW struct */
32 #ifdef NCURSES_OPAQUE
33 #undef NCURSES_OPAQUE
34 #endif
35 #define NCURSES_OPAQUE 0
38 #include <assert.h>
39 #include <errno.h>
40 #include <ctype.h>
41 #include <signal.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sys/types.h>
47 #include <sys/wait.h>
48 #include <sys/stat.h>
49 #include <sys/select.h>
50 #include <unistd.h>
51 #include <sys/time.h>
52 #include <time.h>
53 #include <fcntl.h>
55 #include <regex.h>
57 #include <locale.h>
58 #include <langinfo.h>
59 #include <iconv.h>
61 /* ncurses(3): Must be defined to have extended wide-character functions. */
62 #define _XOPEN_SOURCE_EXTENDED
64 #if defined HAVE_NCURSESW_CURSES_H
65 # include <ncursesw/curses.h>
66 #elif defined HAVE_NCURSESW_H
67 # include <ncursesw.h>
68 #elif defined HAVE_NCURSES_CURSES_H
69 # include <ncurses/curses.h>
70 #elif defined HAVE_NCURSES_H
71 # include <ncurses.h>
72 #elif defined HAVE_CURSES_H
73 # include <curses.h>
74 #else
75 #ifdef WARN_MISSING_CURSES_CONFIGURATION
76 # warning SysV or X/Open-compatible Curses installation is required.
77 # warning Will assume Curses is found in default include and library path.
78 # warning To fix any build issues please use autotools to configure Curses.
79 # warning See INSTALL.adoc file for instructions.
80 #endif
81 # include <curses.h>
82 #endif
84 #if __GNUC__ >= 3
85 #define TIG_NORETURN __attribute__((__noreturn__))
86 #define PRINTF_LIKE(fmt, args) __attribute__((format (printf, fmt, args)))
87 #else
88 #define TIG_NORETURN
89 #define PRINTF_LIKE(fmt, args)
90 #endif
92 #define ABS(x) ((x) >= 0 ? (x) : -(x))
93 #define MIN(x, y) ((x) < (y) ? (x) : (y))
94 #define MAX(x, y) ((x) > (y) ? (x) : (y))
96 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
97 #define STRING_SIZE(x) (sizeof(x) - 1)
99 #define SIZEOF_STR 1024 /* Default string size. */
100 #define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */
101 #define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL. */
102 #define SIZEOF_ARG 32 /* Default argument array size. */
104 /* This color name can be used to refer to the default term colors. */
105 #define COLOR_DEFAULT (-1)
107 #define ICONV_NONE ((iconv_t) -1)
108 #ifndef ICONV_CONST
109 #define ICONV_CONST /* nothing */
110 #endif
111 #define ICONV_TRANSLIT "//TRANSLIT//IGNORE"
113 /* The format and size of the date column in the main view. */
114 #define DATE_FORMAT "%Y-%m-%d %H:%M"
115 #define DATE_WIDTH STRING_SIZE("2006-04-29 14:21")
116 #define DATE_SHORT_WIDTH STRING_SIZE("2006-04-29")
118 #define MIN_VIEW_HEIGHT 4
119 #define MIN_VIEW_WIDTH 4
121 #define NULL_ID "0000000000000000000000000000000000000000"
123 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
125 /* Some ASCII-shorthands fitted into the ncurses namespace. */
126 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
127 #define KEY_TAB '\t'
128 #define KEY_RETURN '\r'
129 #define KEY_ESC 27
132 * Allocation helpers ... Entering macro hell to never be seen again.
135 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
136 static type * \
137 name(type **mem, size_t size, size_t increase) \
139 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
140 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
141 type *tmp = *mem; \
143 if (mem == NULL || num_chunks != num_chunks_new) { \
144 size_t newsize = num_chunks_new * chunk_size * sizeof(type); \
146 tmp = realloc(tmp, newsize); \
147 if (tmp) { \
148 *mem = tmp; \
149 if (num_chunks_new > num_chunks) { \
150 size_t offset = num_chunks * chunk_size; \
151 size_t oldsize = offset * sizeof(type); \
153 memset(tmp + offset, 0, newsize - oldsize); \
158 return tmp; \
161 static inline int
162 count_digits(unsigned long i)
164 int digits;
166 for (digits = 0; i; digits++)
167 i /= 10;
168 return digits;
172 * Strings.
175 #define prefixcmp(str1, str2) \
176 strncmp(str1, str2, STRING_SIZE(str2))
178 static inline bool
179 string_isnumber(const char *str)
181 int pos;
183 for (pos = 0; str[pos]; pos++) {
184 if (!isdigit(str[pos]))
185 return FALSE;
188 return pos > 0;
191 static inline bool
192 iscommit(char *str)
194 int pos;
196 for (pos = 0; str[pos]; pos++) {
197 if (!isxdigit(str[pos]))
198 return FALSE;
201 return 7 <= pos && pos < SIZEOF_REV;
204 static inline int
205 ascii_toupper(int c)
207 if (c >= 'a' && c <= 'z')
208 c &= ~0x20;
209 return c;
212 static inline int
213 ascii_tolower(int c)
215 if (c >= 'A' && c <= 'Z')
216 c |= 0x20;
217 return c;
220 static inline int
221 suffixcmp(const char *str, int slen, const char *suffix)
223 size_t len = slen >= 0 ? slen : strlen(str);
224 size_t suffixlen = strlen(suffix);
226 return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
229 static inline void
230 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
232 if (srclen > dstlen - 1)
233 srclen = dstlen - 1;
235 strncpy(dst, src, srclen);
236 dst[srclen] = 0;
239 /* Shorthands for safely copying into a fixed buffer. */
241 #define FORMAT_BUFFER(buf, bufsize, fmt, retval, allow_truncate) \
242 do { \
243 va_list args; \
244 va_start(args, fmt); \
245 retval = vsnprintf(buf, bufsize, fmt, args); \
246 va_end(args); \
247 if (retval >= (bufsize) && allow_truncate) { \
248 (buf)[(bufsize) - 1] = 0; \
249 (buf)[(bufsize) - 2] = '.'; \
250 (buf)[(bufsize) - 3] = '.'; \
251 (buf)[(bufsize) - 4] = '.'; \
252 retval = (bufsize) - 1; \
253 } else if (retval < 0 || retval >= (bufsize)) { \
254 retval = -1; \
256 } while (0)
258 #define string_copy(dst, src) \
259 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
261 #define string_ncopy(dst, src, srclen) \
262 string_ncopy_do(dst, sizeof(dst), src, srclen)
264 static inline void
265 string_copy_rev(char *dst, const char *src)
267 size_t srclen;
269 if (!*src)
270 return;
272 for (srclen = 0; srclen < SIZEOF_REV; srclen++)
273 if (isspace(src[srclen]))
274 break;
276 string_ncopy_do(dst, SIZEOF_REV, src, srclen);
279 static inline void
280 string_copy_rev_from_commit_line(char *dst, const char *src)
282 string_copy_rev(dst, src + STRING_SIZE("commit "));
285 #define string_rev_is_null(rev) !strncmp(rev, NULL_ID, STRING_SIZE(NULL_ID))
287 #define string_add(dst, from, src) \
288 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
290 static inline size_t
291 string_expanded_length(const char *src, size_t srclen, size_t tabsize, size_t max_size)
293 size_t size, pos;
295 for (size = pos = 0; pos < srclen && size < max_size; pos++) {
296 if (src[pos] == '\t') {
297 size_t expanded = tabsize - (size % tabsize);
299 size += expanded;
300 } else {
301 size++;
305 return pos;
308 static inline size_t
309 string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
311 size_t size, pos;
313 for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) {
314 if (src[pos] == '\t') {
315 size_t expanded = tabsize - (size % tabsize);
317 if (expanded + size >= dstlen - 1)
318 expanded = dstlen - size - 1;
319 memcpy(dst + size, " ", expanded);
320 size += expanded;
321 } else {
322 dst[size++] = src[pos];
326 dst[size] = 0;
327 return pos;
330 static inline char *
331 chomp_string(char *name)
333 int namelen;
335 while (isspace(*name))
336 name++;
338 namelen = strlen(name) - 1;
339 while (namelen > 0 && isspace(name[namelen]))
340 name[namelen--] = 0;
342 return name;
345 static inline bool PRINTF_LIKE(4, 5)
346 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
348 size_t pos = bufpos ? *bufpos : 0;
349 int retval;
351 FORMAT_BUFFER(buf + pos, bufsize - pos, fmt, retval, FALSE);
352 if (bufpos && retval > 0)
353 *bufpos = pos + retval;
355 return pos >= bufsize ? FALSE : TRUE;
358 #define string_format(buf, fmt, args...) \
359 string_nformat(buf, sizeof(buf), NULL, fmt, args)
361 #define string_format_size(buf, size, fmt, args...) \
362 string_nformat(buf, size, NULL, fmt, args)
364 #define string_format_from(buf, from, fmt, args...) \
365 string_nformat(buf, sizeof(buf), from, fmt, args)
367 static inline int
368 strcmp_null(const char *s1, const char *s2)
370 if (!s1 || !s2) {
371 return (!!s1) - (!!s2);
374 return strcmp(s1, s2);
378 * Unicode / UTF-8 handling
380 * NOTE: Much of the following code for dealing with Unicode is derived from
381 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
382 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
385 static inline int
386 unicode_width(unsigned long c, int tab_size)
388 if (c >= 0x1100 &&
389 (c <= 0x115f /* Hangul Jamo */
390 || c == 0x2329
391 || c == 0x232a
392 || (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f)
393 /* CJK ... Yi */
394 || (c >= 0xac00 && c <= 0xd7a3) /* Hangul Syllables */
395 || (c >= 0xf900 && c <= 0xfaff) /* CJK Compatibility Ideographs */
396 || (c >= 0xfe30 && c <= 0xfe6f) /* CJK Compatibility Forms */
397 || (c >= 0xff00 && c <= 0xff60) /* Fullwidth Forms */
398 || (c >= 0xffe0 && c <= 0xffe6)
399 || (c >= 0x20000 && c <= 0x2fffd)
400 || (c >= 0x30000 && c <= 0x3fffd)))
401 return 2;
403 if ((c >= 0x0300 && c <= 0x036f) /* combining diacretical marks */
404 || (c >= 0x1dc0 && c <= 0x1dff) /* combining diacretical marks supplement */
405 || (c >= 0x20d0 && c <= 0x20ff) /* combining diacretical marks for symbols */
406 || (c >= 0xfe20 && c <= 0xfe2f)) /* combining half marks */
407 return 0;
409 if (c == '\t')
410 return tab_size;
412 return 1;
415 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
416 * Illegal bytes are set one. */
417 static const unsigned char utf8_bytes[256] = {
418 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,
419 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,
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 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,
425 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,
428 static inline unsigned char
429 utf8_char_length(const char *string, const char *end)
431 int c = *(unsigned char *) string;
433 return utf8_bytes[c];
436 /* Decode UTF-8 multi-byte representation into a Unicode character. */
437 static inline unsigned long
438 utf8_to_unicode(const char *string, size_t length)
440 unsigned long unicode;
442 switch (length) {
443 case 1:
444 unicode = string[0];
445 break;
446 case 2:
447 unicode = (string[0] & 0x1f) << 6;
448 unicode += (string[1] & 0x3f);
449 break;
450 case 3:
451 unicode = (string[0] & 0x0f) << 12;
452 unicode += ((string[1] & 0x3f) << 6);
453 unicode += (string[2] & 0x3f);
454 break;
455 case 4:
456 unicode = (string[0] & 0x0f) << 18;
457 unicode += ((string[1] & 0x3f) << 12);
458 unicode += ((string[2] & 0x3f) << 6);
459 unicode += (string[3] & 0x3f);
460 break;
461 case 5:
462 unicode = (string[0] & 0x0f) << 24;
463 unicode += ((string[1] & 0x3f) << 18);
464 unicode += ((string[2] & 0x3f) << 12);
465 unicode += ((string[3] & 0x3f) << 6);
466 unicode += (string[4] & 0x3f);
467 break;
468 case 6:
469 unicode = (string[0] & 0x01) << 30;
470 unicode += ((string[1] & 0x3f) << 24);
471 unicode += ((string[2] & 0x3f) << 18);
472 unicode += ((string[3] & 0x3f) << 12);
473 unicode += ((string[4] & 0x3f) << 6);
474 unicode += (string[5] & 0x3f);
475 break;
476 default:
477 return 0;
480 /* Invalid characters could return the special 0xfffd value but NUL
481 * should be just as good. */
482 return unicode > 0xffff ? 0 : unicode;
485 /* Calculates how much of string can be shown within the given maximum width
486 * and sets trimmed parameter to non-zero value if all of string could not be
487 * shown. If the reserve flag is TRUE, it will reserve at least one
488 * trailing character, which can be useful when drawing a delimiter.
490 * Returns the number of bytes to output from string to satisfy max_width. */
491 static inline size_t
492 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
494 const char *string = *start;
495 const char *end = strchr(string, '\0');
496 unsigned char last_bytes = 0;
497 size_t last_ucwidth = 0;
499 *width = 0;
500 *trimmed = 0;
502 while (string < end) {
503 unsigned char bytes = utf8_char_length(string, end);
504 size_t ucwidth;
505 unsigned long unicode;
507 if (string + bytes > end)
508 break;
510 /* Change representation to figure out whether
511 * it is a single- or double-width character. */
513 unicode = utf8_to_unicode(string, bytes);
514 /* FIXME: Graceful handling of invalid Unicode character. */
515 if (!unicode)
516 break;
518 ucwidth = unicode_width(unicode, tab_size);
519 if (skip > 0) {
520 skip -= ucwidth <= skip ? ucwidth : skip;
521 *start += bytes;
523 *width += ucwidth;
524 if (*width > max_width) {
525 *trimmed = 1;
526 *width -= ucwidth;
527 if (reserve && *width == max_width) {
528 string -= last_bytes;
529 *width -= last_ucwidth;
531 break;
534 string += bytes;
535 if (ucwidth) {
536 last_bytes = bytes;
537 last_ucwidth = ucwidth;
538 } else {
539 last_bytes += bytes;
543 return string - *start;
546 #endif
548 /* vim: set ts=8 sw=8 noexpandtab: */