Use TMPDIR for temporary files
[tig.git] / tig.h
blob3b5541156e45d0fc3bef343af139a26f0d8a2b5b
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 #ifndef TIG_VERSION
22 #define TIG_VERSION "unknown-version"
23 #endif
25 #ifndef DEBUG
26 #define NDEBUG
27 #endif
29 /* necessary on Snow Leopard to use WINDOW struct */
30 #ifdef NCURSES_OPAQUE
31 #undef NCURSES_OPAQUE
32 #endif
33 #define NCURSES_OPAQUE 0
36 #include <assert.h>
37 #include <errno.h>
38 #include <ctype.h>
39 #include <signal.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <sys/stat.h>
47 #include <sys/select.h>
48 #include <unistd.h>
49 #include <sys/time.h>
50 #include <time.h>
51 #include <fcntl.h>
53 #include <regex.h>
55 #include <locale.h>
56 #include <langinfo.h>
57 #include <iconv.h>
59 /* ncurses(3): Must be defined to have extended wide-character functions. */
60 #define _XOPEN_SOURCE_EXTENDED
62 #if defined HAVE_NCURSESW_CURSES_H
63 # include <ncursesw/curses.h>
64 #elif defined HAVE_NCURSESW_H
65 # include <ncursesw.h>
66 #elif defined HAVE_NCURSES_CURSES_H
67 # include <ncurses/curses.h>
68 #elif defined HAVE_NCURSES_H
69 # include <ncurses.h>
70 #elif defined HAVE_CURSES_H
71 # include <curses.h>
72 #else
73 # warning SysV or X/Open-compatible Curses installation is required.
74 # warning Will assume Curses is found in default include and library path.
75 # warning To fix any build issues please use autotools to configure Curses.
76 # warning See INSTALL file for instructions.
77 # include <curses.h>
78 #endif
80 #if __GNUC__ >= 3
81 #define TIG_NORETURN __attribute__((__noreturn__))
82 #define PRINTF_LIKE(fmt, args) __attribute__((format (printf, fmt, args)))
83 #else
84 #define TIG_NORETURN
85 #define PRINTF_LIKE(fmt, args)
86 #endif
88 #define ABS(x) ((x) >= 0 ? (x) : -(x))
89 #define MIN(x, y) ((x) < (y) ? (x) : (y))
90 #define MAX(x, y) ((x) > (y) ? (x) : (y))
92 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
93 #define STRING_SIZE(x) (sizeof(x) - 1)
95 #define SIZEOF_STR 1024 /* Default string size. */
96 #define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */
97 #define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL. */
98 #define SIZEOF_ARG 32 /* Default argument array size. */
100 /* This color name can be used to refer to the default term colors. */
101 #define COLOR_DEFAULT (-1)
103 #define ENCODING_UTF8 "UTF-8"
104 #define ENCODING_SEP ": encoding: "
105 #define ENCODING_ARG "--encoding=" ENCODING_UTF8
107 #define ICONV_NONE ((iconv_t) -1)
108 #ifndef ICONV_CONST
109 #define ICONV_CONST /* nothing */
110 #endif
111 #define ICONV_TRANSLIT "//TRANSLIT"
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 ID_WIDTH 7
119 #define AUTHOR_WIDTH 18
120 #define FILENAME_WIDTH 18
122 #define MIN_VIEW_HEIGHT 4
123 #define MIN_VIEW_WIDTH 4
125 #define NULL_ID "0000000000000000000000000000000000000000"
127 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
129 /* Some ASCII-shorthands fitted into the ncurses namespace. */
130 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
131 #define KEY_TAB '\t'
132 #define KEY_RETURN '\r'
133 #define KEY_ESC 27
136 * Allocation helpers ... Entering macro hell to never be seen again.
139 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
140 static type * \
141 name(type **mem, size_t size, size_t increase) \
143 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
144 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
145 type *tmp = *mem; \
147 if (mem == NULL || num_chunks != num_chunks_new) { \
148 size_t newsize = num_chunks_new * chunk_size * sizeof(type); \
150 tmp = realloc(tmp, newsize); \
151 if (tmp) { \
152 *mem = tmp; \
153 if (num_chunks_new > num_chunks) { \
154 size_t offset = num_chunks * chunk_size; \
155 size_t oldsize = offset * sizeof(type); \
157 memset(tmp + offset, 0, newsize - oldsize); \
162 return tmp; \
166 * Strings.
169 #define prefixcmp(str1, str2) \
170 strncmp(str1, str2, STRING_SIZE(str2))
172 static inline bool
173 string_isnumber(const char *str)
175 int pos;
177 for (pos = 0; str[pos]; pos++) {
178 if (!isdigit(str[pos]))
179 return FALSE;
182 return pos > 0;
185 static inline bool
186 iscommit(char *str)
188 int pos;
190 for (pos = 0; str[pos]; pos++) {
191 if (!isxdigit(str[pos]))
192 return FALSE;
195 return 7 <= pos && pos < SIZEOF_REV;
198 static inline int
199 ascii_toupper(int c)
201 if (c >= 'a' && c <= 'z')
202 c &= ~0x20;
203 return c;
206 static inline int
207 ascii_tolower(int c)
209 if (c >= 'A' && c <= 'Z')
210 c |= 0x20;
211 return c;
214 static inline int
215 suffixcmp(const char *str, int slen, const char *suffix)
217 size_t len = slen >= 0 ? slen : strlen(str);
218 size_t suffixlen = strlen(suffix);
220 return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
223 static inline void
224 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
226 if (srclen > dstlen - 1)
227 srclen = dstlen - 1;
229 strncpy(dst, src, srclen);
230 dst[srclen] = 0;
233 /* Shorthands for safely copying into a fixed buffer. */
235 #define FORMAT_BUFFER(buf, bufsize, fmt, retval, allow_truncate) \
236 do { \
237 va_list args; \
238 va_start(args, fmt); \
239 retval = vsnprintf(buf, bufsize, fmt, args); \
240 va_end(args); \
241 if (retval >= (bufsize) && allow_truncate) { \
242 (buf)[(bufsize) - 1] = 0; \
243 (buf)[(bufsize) - 2] = '.'; \
244 (buf)[(bufsize) - 3] = '.'; \
245 (buf)[(bufsize) - 4] = '.'; \
246 retval = (bufsize) - 1; \
247 } else if (retval < 0 || retval >= (bufsize)) { \
248 retval = -1; \
250 } while (0)
252 #define string_copy(dst, src) \
253 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
255 #define string_ncopy(dst, src, srclen) \
256 string_ncopy_do(dst, sizeof(dst), src, srclen)
258 static inline void
259 string_copy_rev(char *dst, const char *src)
261 size_t srclen;
263 if (!*src)
264 return;
266 for (srclen = 0; srclen < SIZEOF_REV; srclen++)
267 if (isspace(src[srclen]))
268 break;
270 string_ncopy_do(dst, SIZEOF_REV, src, srclen);
273 #define string_rev_is_null(rev) !strncmp(rev, NULL_ID, STRING_SIZE(NULL_ID))
275 #define string_add(dst, from, src) \
276 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
278 static inline size_t
279 string_expanded_length(const char *src, size_t srclen, size_t tabsize, size_t max_size)
281 size_t size, pos;
283 for (size = pos = 0; pos < srclen && size < max_size; pos++) {
284 if (src[pos] == '\t') {
285 size_t expanded = tabsize - (size % tabsize);
287 size += expanded;
288 } else {
289 size++;
293 return pos;
296 static inline size_t
297 string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
299 size_t size, pos;
301 for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) {
302 if (src[pos] == '\t') {
303 size_t expanded = tabsize - (size % tabsize);
305 if (expanded + size >= dstlen - 1)
306 expanded = dstlen - size - 1;
307 memcpy(dst + size, " ", expanded);
308 size += expanded;
309 } else {
310 dst[size++] = src[pos];
314 dst[size] = 0;
315 return pos;
318 static inline char *
319 chomp_string(char *name)
321 int namelen;
323 while (isspace(*name))
324 name++;
326 namelen = strlen(name) - 1;
327 while (namelen > 0 && isspace(name[namelen]))
328 name[namelen--] = 0;
330 return name;
333 static inline bool PRINTF_LIKE(4, 5)
334 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
336 size_t pos = bufpos ? *bufpos : 0;
337 int retval;
339 FORMAT_BUFFER(buf + pos, bufsize - pos, fmt, retval, FALSE);
340 if (bufpos && retval > 0)
341 *bufpos = pos + retval;
343 return pos >= bufsize ? FALSE : TRUE;
346 #define string_format(buf, fmt, args...) \
347 string_nformat(buf, sizeof(buf), NULL, fmt, args)
349 #define string_format_size(buf, size, fmt, args...) \
350 string_nformat(buf, size, NULL, fmt, args)
352 #define string_format_from(buf, from, fmt, args...) \
353 string_nformat(buf, sizeof(buf), from, fmt, args)
355 static inline int
356 strcmp_null(const char *s1, const char *s2)
358 if (!s1 || !s2) {
359 return (!!s1) - (!!s2);
362 return strcmp(s1, s2);
366 * Enumerations
369 struct enum_map {
370 const char *name;
371 int namelen;
372 int value;
375 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
377 #define ENUM_SYM_MACRO(prefix, name) prefix##_##name
378 #define ENUM_MAP_MACRO(prefix, name) ENUM_MAP(#name, ENUM_SYM_MACRO(prefix, name))
380 #define DEFINE_ENUM(name, info) \
381 enum name { info(ENUM_SYM_MACRO) }; \
382 static const struct enum_map name##_map[] = { info(ENUM_MAP_MACRO) }
384 static inline int
385 string_enum_compare(const char *str1, const char *str2, int len)
387 size_t i;
389 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
391 /* Diff-Header == DIFF_HEADER */
392 for (i = 0; i < len; i++) {
393 if (ascii_toupper(str1[i]) == ascii_toupper(str2[i]))
394 continue;
396 if (string_enum_sep(str1[i]) &&
397 string_enum_sep(str2[i]))
398 continue;
400 return str1[i] - str2[i];
403 return 0;
406 #define enum_equals(entry, str, len) \
407 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
409 static inline char *
410 enum_map_name(const char *name, size_t namelen)
412 static char buf[SIZEOF_STR];
413 int bufpos;
415 for (bufpos = 0; bufpos <= namelen; bufpos++) {
416 buf[bufpos] = ascii_tolower(name[bufpos]);
417 if (buf[bufpos] == '_')
418 buf[bufpos] = '-';
421 buf[bufpos] = 0;
422 return buf;
425 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
427 static inline bool
428 map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
430 size_t namelen = strlen(name);
431 int i;
433 for (i = 0; i < map_size; i++)
434 if (enum_equals(map[i], name, namelen)) {
435 *value = map[i].value;
436 return TRUE;
439 return FALSE;
442 #define map_enum(attr, map, name) \
443 map_enum_do(map, ARRAY_SIZE(map), attr, name)
446 * Unicode / UTF-8 handling
448 * NOTE: Much of the following code for dealing with Unicode is derived from
449 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
450 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
453 static inline int
454 unicode_width(unsigned long c, int tab_size)
456 if (c >= 0x1100 &&
457 (c <= 0x115f /* Hangul Jamo */
458 || c == 0x2329
459 || c == 0x232a
460 || (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f)
461 /* CJK ... Yi */
462 || (c >= 0xac00 && c <= 0xd7a3) /* Hangul Syllables */
463 || (c >= 0xf900 && c <= 0xfaff) /* CJK Compatibility Ideographs */
464 || (c >= 0xfe30 && c <= 0xfe6f) /* CJK Compatibility Forms */
465 || (c >= 0xff00 && c <= 0xff60) /* Fullwidth Forms */
466 || (c >= 0xffe0 && c <= 0xffe6)
467 || (c >= 0x20000 && c <= 0x2fffd)
468 || (c >= 0x30000 && c <= 0x3fffd)))
469 return 2;
471 if (c == '\t')
472 return tab_size;
474 return 1;
477 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
478 * Illegal bytes are set one. */
479 static const unsigned char utf8_bytes[256] = {
480 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,
481 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,
482 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,
483 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,
484 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,
485 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,
486 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,
487 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,
490 static inline unsigned char
491 utf8_char_length(const char *string, const char *end)
493 int c = *(unsigned char *) string;
495 return utf8_bytes[c];
498 /* Decode UTF-8 multi-byte representation into a Unicode character. */
499 static inline unsigned long
500 utf8_to_unicode(const char *string, size_t length)
502 unsigned long unicode;
504 switch (length) {
505 case 1:
506 unicode = string[0];
507 break;
508 case 2:
509 unicode = (string[0] & 0x1f) << 6;
510 unicode += (string[1] & 0x3f);
511 break;
512 case 3:
513 unicode = (string[0] & 0x0f) << 12;
514 unicode += ((string[1] & 0x3f) << 6);
515 unicode += (string[2] & 0x3f);
516 break;
517 case 4:
518 unicode = (string[0] & 0x0f) << 18;
519 unicode += ((string[1] & 0x3f) << 12);
520 unicode += ((string[2] & 0x3f) << 6);
521 unicode += (string[3] & 0x3f);
522 break;
523 case 5:
524 unicode = (string[0] & 0x0f) << 24;
525 unicode += ((string[1] & 0x3f) << 18);
526 unicode += ((string[2] & 0x3f) << 12);
527 unicode += ((string[3] & 0x3f) << 6);
528 unicode += (string[4] & 0x3f);
529 break;
530 case 6:
531 unicode = (string[0] & 0x01) << 30;
532 unicode += ((string[1] & 0x3f) << 24);
533 unicode += ((string[2] & 0x3f) << 18);
534 unicode += ((string[3] & 0x3f) << 12);
535 unicode += ((string[4] & 0x3f) << 6);
536 unicode += (string[5] & 0x3f);
537 break;
538 default:
539 return 0;
542 /* Invalid characters could return the special 0xfffd value but NUL
543 * should be just as good. */
544 return unicode > 0xffff ? 0 : unicode;
547 /* Calculates how much of string can be shown within the given maximum width
548 * and sets trimmed parameter to non-zero value if all of string could not be
549 * shown. If the reserve flag is TRUE, it will reserve at least one
550 * trailing character, which can be useful when drawing a delimiter.
552 * Returns the number of bytes to output from string to satisfy max_width. */
553 static inline size_t
554 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
556 const char *string = *start;
557 const char *end = strchr(string, '\0');
558 unsigned char last_bytes = 0;
559 size_t last_ucwidth = 0;
561 *width = 0;
562 *trimmed = 0;
564 while (string < end) {
565 unsigned char bytes = utf8_char_length(string, end);
566 size_t ucwidth;
567 unsigned long unicode;
569 if (string + bytes > end)
570 break;
572 /* Change representation to figure out whether
573 * it is a single- or double-width character. */
575 unicode = utf8_to_unicode(string, bytes);
576 /* FIXME: Graceful handling of invalid Unicode character. */
577 if (!unicode)
578 break;
580 ucwidth = unicode_width(unicode, tab_size);
581 if (skip > 0) {
582 skip -= ucwidth <= skip ? ucwidth : skip;
583 *start += bytes;
585 *width += ucwidth;
586 if (*width > max_width) {
587 *trimmed = 1;
588 *width -= ucwidth;
589 if (reserve && *width == max_width) {
590 string -= last_bytes;
591 *width -= last_ucwidth;
593 break;
596 string += bytes;
597 last_bytes = ucwidth ? bytes : 0;
598 last_ucwidth = ucwidth;
601 return string - *start;
604 #endif
606 /* vim: set ts=8 sw=8 noexpandtab: */