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; \
126 }
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)
137 {
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;
142 }
144 static inline void
145 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
146 {
147 if (srclen > dstlen - 1)
148 srclen = dstlen - 1;
150 strncpy(dst, src, srclen);
151 dst[srclen] = 0;
152 }
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)
170 {
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;
188 }
190 static inline char *
191 chomp_string(char *name)
192 {
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;
203 }
205 static inline bool
206 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
207 {
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;
219 }
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 #define ENUM_SYM_MACRO(prefix, name) prefix##_##name
240 #define ENUM_MAP_MACRO(prefix, name) ENUM_MAP(#name, ENUM_SYM_MACRO(prefix, name))
242 #define DEFINE_ENUM(name, info) \
243 enum name { info(ENUM_SYM_MACRO) }; \
244 static const struct enum_map name##_map[] = { info(ENUM_MAP_MACRO) }
246 static inline int
247 string_enum_compare(const char *str1, const char *str2, int len)
248 {
249 size_t i;
251 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
253 /* Diff-Header == DIFF_HEADER */
254 for (i = 0; i < len; i++) {
255 if (toupper(str1[i]) == toupper(str2[i]))
256 continue;
258 if (string_enum_sep(str1[i]) &&
259 string_enum_sep(str2[i]))
260 continue;
262 return str1[i] - str2[i];
263 }
265 return 0;
266 }
268 #define enum_equals(entry, str, len) \
269 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
271 static inline char *
272 enum_map_name(const char *name, size_t namelen)
273 {
274 static char buf[SIZEOF_STR];
275 int bufpos;
277 for (bufpos = 0; bufpos <= namelen; bufpos++) {
278 buf[bufpos] = tolower(name[bufpos]);
279 if (buf[bufpos] == '_')
280 buf[bufpos] = '-';
281 }
283 buf[bufpos] = 0;
284 return buf;
285 }
287 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
289 static inline bool
290 map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
291 {
292 size_t namelen = strlen(name);
293 int i;
295 for (i = 0; i < map_size; i++)
296 if (enum_equals(map[i], name, namelen)) {
297 *value = map[i].value;
298 return TRUE;
299 }
301 return FALSE;
302 }
304 #define map_enum(attr, map, name) \
305 map_enum_do(map, ARRAY_SIZE(map), attr, name)
307 /*
308 * Unicode / UTF-8 handling
309 *
310 * NOTE: Much of the following code for dealing with Unicode is derived from
311 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
312 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
313 */
315 static inline int
316 unicode_width(unsigned long c, int tab_size)
317 {
318 if (c >= 0x1100 &&
319 (c <= 0x115f /* Hangul Jamo */
320 || c == 0x2329
321 || c == 0x232a
322 || (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f)
323 /* CJK ... Yi */
324 || (c >= 0xac00 && c <= 0xd7a3) /* Hangul Syllables */
325 || (c >= 0xf900 && c <= 0xfaff) /* CJK Compatibility Ideographs */
326 || (c >= 0xfe30 && c <= 0xfe6f) /* CJK Compatibility Forms */
327 || (c >= 0xff00 && c <= 0xff60) /* Fullwidth Forms */
328 || (c >= 0xffe0 && c <= 0xffe6)
329 || (c >= 0x20000 && c <= 0x2fffd)
330 || (c >= 0x30000 && c <= 0x3fffd)))
331 return 2;
333 if (c == '\t')
334 return tab_size;
336 return 1;
337 }
339 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
340 * Illegal bytes are set one. */
341 static const unsigned char utf8_bytes[256] = {
342 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,
343 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,
344 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,
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 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,
349 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,
350 };
352 static inline unsigned char
353 utf8_char_length(const char *string, const char *end)
354 {
355 int c = *(unsigned char *) string;
357 return utf8_bytes[c];
358 }
360 /* Decode UTF-8 multi-byte representation into a Unicode character. */
361 static inline unsigned long
362 utf8_to_unicode(const char *string, size_t length)
363 {
364 unsigned long unicode;
366 switch (length) {
367 case 1:
368 unicode = string[0];
369 break;
370 case 2:
371 unicode = (string[0] & 0x1f) << 6;
372 unicode += (string[1] & 0x3f);
373 break;
374 case 3:
375 unicode = (string[0] & 0x0f) << 12;
376 unicode += ((string[1] & 0x3f) << 6);
377 unicode += (string[2] & 0x3f);
378 break;
379 case 4:
380 unicode = (string[0] & 0x0f) << 18;
381 unicode += ((string[1] & 0x3f) << 12);
382 unicode += ((string[2] & 0x3f) << 6);
383 unicode += (string[3] & 0x3f);
384 break;
385 case 5:
386 unicode = (string[0] & 0x0f) << 24;
387 unicode += ((string[1] & 0x3f) << 18);
388 unicode += ((string[2] & 0x3f) << 12);
389 unicode += ((string[3] & 0x3f) << 6);
390 unicode += (string[4] & 0x3f);
391 break;
392 case 6:
393 unicode = (string[0] & 0x01) << 30;
394 unicode += ((string[1] & 0x3f) << 24);
395 unicode += ((string[2] & 0x3f) << 18);
396 unicode += ((string[3] & 0x3f) << 12);
397 unicode += ((string[4] & 0x3f) << 6);
398 unicode += (string[5] & 0x3f);
399 break;
400 default:
401 return 0;
402 }
404 /* Invalid characters could return the special 0xfffd value but NUL
405 * should be just as good. */
406 return unicode > 0xffff ? 0 : unicode;
407 }
409 /* Calculates how much of string can be shown within the given maximum width
410 * and sets trimmed parameter to non-zero value if all of string could not be
411 * shown. If the reserve flag is TRUE, it will reserve at least one
412 * trailing character, which can be useful when drawing a delimiter.
413 *
414 * Returns the number of bytes to output from string to satisfy max_width. */
415 static inline size_t
416 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
417 {
418 const char *string = *start;
419 const char *end = strchr(string, '\0');
420 unsigned char last_bytes = 0;
421 size_t last_ucwidth = 0;
423 *width = 0;
424 *trimmed = 0;
426 while (string < end) {
427 unsigned char bytes = utf8_char_length(string, end);
428 size_t ucwidth;
429 unsigned long unicode;
431 if (string + bytes > end)
432 break;
434 /* Change representation to figure out whether
435 * it is a single- or double-width character. */
437 unicode = utf8_to_unicode(string, bytes);
438 /* FIXME: Graceful handling of invalid Unicode character. */
439 if (!unicode)
440 break;
442 ucwidth = unicode_width(unicode, tab_size);
443 if (skip > 0) {
444 skip -= ucwidth <= skip ? ucwidth : skip;
445 *start += bytes;
446 }
447 *width += ucwidth;
448 if (*width > max_width) {
449 *trimmed = 1;
450 *width -= ucwidth;
451 if (reserve && *width == max_width) {
452 string -= last_bytes;
453 *width -= last_ucwidth;
454 }
455 break;
456 }
458 string += bytes;
459 last_bytes = ucwidth ? bytes : 0;
460 last_ucwidth = ucwidth;
461 }
463 return string - *start;
464 }
466 #endif