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.
22 #define TIG_VERSION "unknown-version"
37 #include <sys/types.h>
40 #include <sys/select.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>
62 #define __NORETURN __attribute__((__noreturn__))
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)
84 #define ICONV_CONST /* nothing */
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 ")
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 */
104 #define KEY_RETURN '\r'
108 * Allocation helpers ... Entering macro hell to never be seen again.
111 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
113 name(type **mem, size_t size, size_t increase) \
115 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
116 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
119 if (mem == NULL || num_chunks != num_chunks_new) { \
120 tmp = realloc(tmp, num_chunks_new * chunk_size * sizeof(type)); \
132 #define prefixcmp(str1, str2) \
133 strncmp(str1, str2, STRING_SIZE(str2))
136 suffixcmp(const char *str
, int slen
, const char *suffix
)
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;
145 string_ncopy_do(char *dst
, size_t dstlen
, const char *src
, size_t srclen
)
147 if (srclen
> dstlen
- 1)
150 strncpy(dst
, src
, srclen
);
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))
169 string_expand(char *dst
, size_t dstlen
, const char *src
, int tabsize
)
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
);
182 dst
[size
++] = src
[pos
];
191 chomp_string(char *name
)
195 while (isspace(*name
))
198 namelen
= strlen(name
) - 1;
199 while (namelen
> 0 && isspace(name
[namelen
]))
206 string_nformat(char *buf
, size_t bufsize
, size_t *bufpos
, const char *fmt
, ...)
209 size_t pos
= bufpos
? *bufpos
: 0;
212 pos
+= vsnprintf(buf
+ pos
, bufsize
- pos
, fmt
, args
);
218 return pos
>= bufsize
? FALSE
: TRUE
;
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)
237 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
240 string_enum_compare(const char *str1
, const char *str2
, int len
)
244 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
246 /* Diff-Header == DIFF_HEADER */
247 for (i
= 0; i
< len
; i
++) {
248 if (toupper(str1
[i
]) == toupper(str2
[i
]))
251 if (string_enum_sep(str1
[i
]) &&
252 string_enum_sep(str2
[i
]))
255 return str1
[i
] - str2
[i
];
261 #define enum_equals(entry, str, len) \
262 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
265 enum_map_name(const char *name
, size_t namelen
)
267 static char buf
[SIZEOF_STR
];
270 for (bufpos
= 0; bufpos
<= namelen
; bufpos
++) {
271 buf
[bufpos
] = tolower(name
[bufpos
]);
272 if (buf
[bufpos
] == '_')
280 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
283 map_enum_do(const struct enum_map
*map
, size_t map_size
, int *value
, const char *name
)
285 size_t namelen
= strlen(name
);
288 for (i
= 0; i
< map_size
; i
++)
289 if (enum_equals(map
[i
], name
, namelen
)) {
290 *value
= map
[i
].value
;
297 #define map_enum(attr, map, name) \
298 map_enum_do(map, ARRAY_SIZE(map), attr, name)
301 * Unicode / UTF-8 handling
303 * NOTE: Much of the following code for dealing with Unicode is derived from
304 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
305 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
309 unicode_width(unsigned long c
, int tab_size
)
312 (c
<= 0x115f /* Hangul Jamo */
315 || (c
>= 0x2e80 && c
<= 0xa4cf && c
!= 0x303f)
317 || (c
>= 0xac00 && c
<= 0xd7a3) /* Hangul Syllables */
318 || (c
>= 0xf900 && c
<= 0xfaff) /* CJK Compatibility Ideographs */
319 || (c
>= 0xfe30 && c
<= 0xfe6f) /* CJK Compatibility Forms */
320 || (c
>= 0xff00 && c
<= 0xff60) /* Fullwidth Forms */
321 || (c
>= 0xffe0 && c
<= 0xffe6)
322 || (c
>= 0x20000 && c
<= 0x2fffd)
323 || (c
>= 0x30000 && c
<= 0x3fffd)))
332 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
333 * Illegal bytes are set one. */
334 static const unsigned char utf8_bytes
[256] = {
335 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,
336 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,
337 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,
338 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,
339 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,
340 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,
341 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,
342 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,
345 static inline unsigned char
346 utf8_char_length(const char *string
, const char *end
)
348 int c
= *(unsigned char *) string
;
350 return utf8_bytes
[c
];
353 /* Decode UTF-8 multi-byte representation into a Unicode character. */
354 static inline unsigned long
355 utf8_to_unicode(const char *string
, size_t length
)
357 unsigned long unicode
;
364 unicode
= (string
[0] & 0x1f) << 6;
365 unicode
+= (string
[1] & 0x3f);
368 unicode
= (string
[0] & 0x0f) << 12;
369 unicode
+= ((string
[1] & 0x3f) << 6);
370 unicode
+= (string
[2] & 0x3f);
373 unicode
= (string
[0] & 0x0f) << 18;
374 unicode
+= ((string
[1] & 0x3f) << 12);
375 unicode
+= ((string
[2] & 0x3f) << 6);
376 unicode
+= (string
[3] & 0x3f);
379 unicode
= (string
[0] & 0x0f) << 24;
380 unicode
+= ((string
[1] & 0x3f) << 18);
381 unicode
+= ((string
[2] & 0x3f) << 12);
382 unicode
+= ((string
[3] & 0x3f) << 6);
383 unicode
+= (string
[4] & 0x3f);
386 unicode
= (string
[0] & 0x01) << 30;
387 unicode
+= ((string
[1] & 0x3f) << 24);
388 unicode
+= ((string
[2] & 0x3f) << 18);
389 unicode
+= ((string
[3] & 0x3f) << 12);
390 unicode
+= ((string
[4] & 0x3f) << 6);
391 unicode
+= (string
[5] & 0x3f);
397 /* Invalid characters could return the special 0xfffd value but NUL
398 * should be just as good. */
399 return unicode
> 0xffff ? 0 : unicode
;
402 /* Calculates how much of string can be shown within the given maximum width
403 * and sets trimmed parameter to non-zero value if all of string could not be
404 * shown. If the reserve flag is TRUE, it will reserve at least one
405 * trailing character, which can be useful when drawing a delimiter.
407 * Returns the number of bytes to output from string to satisfy max_width. */
409 utf8_length(const char **start
, size_t skip
, int *width
, size_t max_width
, int *trimmed
, bool reserve
, int tab_size
)
411 const char *string
= *start
;
412 const char *end
= strchr(string
, '\0');
413 unsigned char last_bytes
= 0;
414 size_t last_ucwidth
= 0;
419 while (string
< end
) {
420 unsigned char bytes
= utf8_char_length(string
, end
);
422 unsigned long unicode
;
424 if (string
+ bytes
> end
)
427 /* Change representation to figure out whether
428 * it is a single- or double-width character. */
430 unicode
= utf8_to_unicode(string
, bytes
);
431 /* FIXME: Graceful handling of invalid Unicode character. */
435 ucwidth
= unicode_width(unicode
, tab_size
);
437 skip
-= ucwidth
<= skip
? ucwidth
: skip
;
441 if (*width
> max_width
) {
444 if (reserve
&& *width
== max_width
) {
445 string
-= last_bytes
;
446 *width
-= last_ucwidth
;
452 last_bytes
= ucwidth
? bytes
: 0;
453 last_ucwidth
= ucwidth
;
456 return string
- *start
;