Move truncation logic to FORMAT_BUFFER
[tig.git] / tig.h
blobb35636339e690e3cf6affa4da4e5b919b4825fca
1 /* Copyright (c) 2006-2010 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 #ifdef HAVE_NCURSESW_H
63 #include <ncursesw/ncurses.h>
64 #else
65 #include <ncurses.h>
66 #endif
68 #if __GNUC__ >= 3
69 #define __NORETURN __attribute__((__noreturn__))
70 #else
71 #define __NORETURN
72 #endif
74 #define ABS(x) ((x) >= 0 ? (x) : -(x))
75 #define MIN(x, y) ((x) < (y) ? (x) : (y))
76 #define MAX(x, y) ((x) > (y) ? (x) : (y))
78 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
79 #define STRING_SIZE(x) (sizeof(x) - 1)
81 #define SIZEOF_STR 1024 /* Default string size. */
82 #define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */
83 #define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL. */
84 #define SIZEOF_ARG 32 /* Default argument array size. */
86 /* This color name can be used to refer to the default term colors. */
87 #define COLOR_DEFAULT (-1)
89 #define ENCODING_UTF8 "UTF-8"
91 #define ICONV_NONE ((iconv_t) -1)
92 #ifndef ICONV_CONST
93 #define ICONV_CONST /* nothing */
94 #endif
95 #define ICONV_TRANSLIT "//TRANSLIT"
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
104 #define FILENAME_COLS 19
106 #define MIN_VIEW_HEIGHT 4
108 #define NULL_ID "0000000000000000000000000000000000000000"
110 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
112 /* Some ASCII-shorthands fitted into the ncurses namespace. */
113 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
114 #define KEY_TAB '\t'
115 #define KEY_RETURN '\r'
116 #define KEY_ESC 27
119 * Allocation helpers ... Entering macro hell to never be seen again.
122 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
123 static type * \
124 name(type **mem, size_t size, size_t increase) \
126 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
127 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
128 type *tmp = *mem; \
130 if (mem == NULL || num_chunks != num_chunks_new) { \
131 tmp = realloc(tmp, num_chunks_new * chunk_size * sizeof(type)); \
132 if (tmp) \
133 *mem = tmp; \
136 return tmp; \
140 * Strings.
143 #define prefixcmp(str1, str2) \
144 strncmp(str1, str2, STRING_SIZE(str2))
146 static inline bool
147 string_isnumber(const char *str)
149 int pos;
151 for (pos = 0; str[pos]; pos++) {
152 if (!isdigit(str[pos]))
153 return FALSE;
156 return pos > 0;
159 static inline bool
160 iscommit(char *str)
162 int pos;
164 for (pos = 0; str[pos]; pos++) {
165 if (!isxdigit(str[pos]))
166 return FALSE;
169 return 7 <= pos && pos < SIZEOF_REV;
172 static inline int
173 ascii_toupper(int c)
175 if (c >= 'a' && c <= 'z')
176 c &= ~0x20;
177 return c;
180 static inline int
181 ascii_tolower(int c)
183 if (c >= 'A' && c <= 'Z')
184 c |= 0x20;
185 return c;
188 static inline int
189 suffixcmp(const char *str, int slen, const char *suffix)
191 size_t len = slen >= 0 ? slen : strlen(str);
192 size_t suffixlen = strlen(suffix);
194 return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
197 static inline void
198 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
200 if (srclen > dstlen - 1)
201 srclen = dstlen - 1;
203 strncpy(dst, src, srclen);
204 dst[srclen] = 0;
207 /* Shorthands for safely copying into a fixed buffer. */
209 #define FORMAT_BUFFER(buf, bufsize, fmt, retval, allow_truncate) \
210 do { \
211 va_list args; \
212 va_start(args, fmt); \
213 retval = vsnprintf(buf, bufsize, fmt, args); \
214 va_end(args); \
215 if (retval >= (bufsize) && allow_truncate) { \
216 (buf)[(bufsize) - 1] = 0; \
217 (buf)[(bufsize) - 2] = '.'; \
218 (buf)[(bufsize) - 3] = '.'; \
219 (buf)[(bufsize) - 4] = '.'; \
220 retval = (bufsize) - 1; \
221 } else if (retval < 0 || retval >= (bufsize)) { \
222 retval = -1; \
224 } while (0)
226 #define string_copy(dst, src) \
227 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
229 #define string_ncopy(dst, src, srclen) \
230 string_ncopy_do(dst, sizeof(dst), src, srclen)
232 #define string_copy_rev(dst, src) \
233 string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
235 #define string_add(dst, from, src) \
236 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
238 static inline size_t
239 string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
241 size_t size, pos;
243 for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) {
244 if (src[pos] == '\t') {
245 size_t expanded = tabsize - (size % tabsize);
247 if (expanded + size >= dstlen - 1)
248 expanded = dstlen - size - 1;
249 memcpy(dst + size, " ", expanded);
250 size += expanded;
251 } else {
252 dst[size++] = src[pos];
256 dst[size] = 0;
257 return pos;
260 static inline char *
261 chomp_string(char *name)
263 int namelen;
265 while (isspace(*name))
266 name++;
268 namelen = strlen(name) - 1;
269 while (namelen > 0 && isspace(name[namelen]))
270 name[namelen--] = 0;
272 return name;
275 static inline bool
276 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
278 size_t pos = bufpos ? *bufpos : 0;
279 int retval;
281 FORMAT_BUFFER(buf + pos, bufsize - pos, fmt, retval, FALSE);
282 if (bufpos && retval > 0)
283 *bufpos = pos + retval;
285 return pos >= bufsize ? FALSE : TRUE;
288 #define string_format(buf, fmt, args...) \
289 string_nformat(buf, sizeof(buf), NULL, fmt, args)
291 #define string_format_from(buf, from, fmt, args...) \
292 string_nformat(buf, sizeof(buf), from, fmt, args)
294 static inline int
295 strcmp_null(const char *s1, const char *s2)
297 if (!s1 || !s2) {
298 return (!!s1) - (!!s2);
301 return strcmp(s1, s2);
305 * Enumerations
308 struct enum_map {
309 const char *name;
310 int namelen;
311 int value;
314 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
316 #define ENUM_SYM_MACRO(prefix, name) prefix##_##name
317 #define ENUM_MAP_MACRO(prefix, name) ENUM_MAP(#name, ENUM_SYM_MACRO(prefix, name))
319 #define DEFINE_ENUM(name, info) \
320 enum name { info(ENUM_SYM_MACRO) }; \
321 static const struct enum_map name##_map[] = { info(ENUM_MAP_MACRO) }
323 static inline int
324 string_enum_compare(const char *str1, const char *str2, int len)
326 size_t i;
328 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
330 /* Diff-Header == DIFF_HEADER */
331 for (i = 0; i < len; i++) {
332 if (ascii_toupper(str1[i]) == ascii_toupper(str2[i]))
333 continue;
335 if (string_enum_sep(str1[i]) &&
336 string_enum_sep(str2[i]))
337 continue;
339 return str1[i] - str2[i];
342 return 0;
345 #define enum_equals(entry, str, len) \
346 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
348 static inline char *
349 enum_map_name(const char *name, size_t namelen)
351 static char buf[SIZEOF_STR];
352 int bufpos;
354 for (bufpos = 0; bufpos <= namelen; bufpos++) {
355 buf[bufpos] = ascii_tolower(name[bufpos]);
356 if (buf[bufpos] == '_')
357 buf[bufpos] = '-';
360 buf[bufpos] = 0;
361 return buf;
364 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
366 static inline bool
367 map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
369 size_t namelen = strlen(name);
370 int i;
372 for (i = 0; i < map_size; i++)
373 if (enum_equals(map[i], name, namelen)) {
374 *value = map[i].value;
375 return TRUE;
378 return FALSE;
381 #define map_enum(attr, map, name) \
382 map_enum_do(map, ARRAY_SIZE(map), attr, name)
385 * Unicode / UTF-8 handling
387 * NOTE: Much of the following code for dealing with Unicode is derived from
388 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
389 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
392 static inline int
393 unicode_width(unsigned long c, int tab_size)
395 if (c >= 0x1100 &&
396 (c <= 0x115f /* Hangul Jamo */
397 || c == 0x2329
398 || c == 0x232a
399 || (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f)
400 /* CJK ... Yi */
401 || (c >= 0xac00 && c <= 0xd7a3) /* Hangul Syllables */
402 || (c >= 0xf900 && c <= 0xfaff) /* CJK Compatibility Ideographs */
403 || (c >= 0xfe30 && c <= 0xfe6f) /* CJK Compatibility Forms */
404 || (c >= 0xff00 && c <= 0xff60) /* Fullwidth Forms */
405 || (c >= 0xffe0 && c <= 0xffe6)
406 || (c >= 0x20000 && c <= 0x2fffd)
407 || (c >= 0x30000 && c <= 0x3fffd)))
408 return 2;
410 if (c == '\t')
411 return tab_size;
413 return 1;
416 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
417 * Illegal bytes are set one. */
418 static const unsigned char utf8_bytes[256] = {
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 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,
425 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,
426 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,
429 static inline unsigned char
430 utf8_char_length(const char *string, const char *end)
432 int c = *(unsigned char *) string;
434 return utf8_bytes[c];
437 /* Decode UTF-8 multi-byte representation into a Unicode character. */
438 static inline unsigned long
439 utf8_to_unicode(const char *string, size_t length)
441 unsigned long unicode;
443 switch (length) {
444 case 1:
445 unicode = string[0];
446 break;
447 case 2:
448 unicode = (string[0] & 0x1f) << 6;
449 unicode += (string[1] & 0x3f);
450 break;
451 case 3:
452 unicode = (string[0] & 0x0f) << 12;
453 unicode += ((string[1] & 0x3f) << 6);
454 unicode += (string[2] & 0x3f);
455 break;
456 case 4:
457 unicode = (string[0] & 0x0f) << 18;
458 unicode += ((string[1] & 0x3f) << 12);
459 unicode += ((string[2] & 0x3f) << 6);
460 unicode += (string[3] & 0x3f);
461 break;
462 case 5:
463 unicode = (string[0] & 0x0f) << 24;
464 unicode += ((string[1] & 0x3f) << 18);
465 unicode += ((string[2] & 0x3f) << 12);
466 unicode += ((string[3] & 0x3f) << 6);
467 unicode += (string[4] & 0x3f);
468 break;
469 case 6:
470 unicode = (string[0] & 0x01) << 30;
471 unicode += ((string[1] & 0x3f) << 24);
472 unicode += ((string[2] & 0x3f) << 18);
473 unicode += ((string[3] & 0x3f) << 12);
474 unicode += ((string[4] & 0x3f) << 6);
475 unicode += (string[5] & 0x3f);
476 break;
477 default:
478 return 0;
481 /* Invalid characters could return the special 0xfffd value but NUL
482 * should be just as good. */
483 return unicode > 0xffff ? 0 : unicode;
486 /* Calculates how much of string can be shown within the given maximum width
487 * and sets trimmed parameter to non-zero value if all of string could not be
488 * shown. If the reserve flag is TRUE, it will reserve at least one
489 * trailing character, which can be useful when drawing a delimiter.
491 * Returns the number of bytes to output from string to satisfy max_width. */
492 static inline size_t
493 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
495 const char *string = *start;
496 const char *end = strchr(string, '\0');
497 unsigned char last_bytes = 0;
498 size_t last_ucwidth = 0;
500 *width = 0;
501 *trimmed = 0;
503 while (string < end) {
504 unsigned char bytes = utf8_char_length(string, end);
505 size_t ucwidth;
506 unsigned long unicode;
508 if (string + bytes > end)
509 break;
511 /* Change representation to figure out whether
512 * it is a single- or double-width character. */
514 unicode = utf8_to_unicode(string, bytes);
515 /* FIXME: Graceful handling of invalid Unicode character. */
516 if (!unicode)
517 break;
519 ucwidth = unicode_width(unicode, tab_size);
520 if (skip > 0) {
521 skip -= ucwidth <= skip ? ucwidth : skip;
522 *start += bytes;
524 *width += ucwidth;
525 if (*width > max_width) {
526 *trimmed = 1;
527 *width -= ucwidth;
528 if (reserve && *width == max_width) {
529 string -= last_bytes;
530 *width -= last_ucwidth;
532 break;
535 string += bytes;
536 last_bytes = ucwidth ? bytes : 0;
537 last_ucwidth = ucwidth;
540 return string - *start;
543 #endif