Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 470 lines (380 sloc) 11.712 kb
609028f @jonas Move includes, macros and core utilities to tig.h
jonas authored
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 */
13
14 #ifndef TIG_H
15 #define TIG_H
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #ifndef TIG_VERSION
22 #define TIG_VERSION "unknown-version"
23 #endif
24
25 #ifndef DEBUG
26 #define NDEBUG
27 #endif
28
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>
45
46 #include <regex.h>
47
48 #include <locale.h>
49 #include <langinfo.h>
50 #include <iconv.h>
51
52 /* ncurses(3): Must be defined to have extended wide-character functions. */
53 #define _XOPEN_SOURCE_EXTENDED
54
55 #ifdef HAVE_NCURSESW_H
56 #include <ncursesw/ncurses.h>
57 #else
58 #include <ncurses.h>
59 #endif
60
61 #if __GNUC__ >= 3
62 #define __NORETURN __attribute__((__noreturn__))
63 #else
64 #define __NORETURN
65 #endif
66
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))
70
71 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
72 #define STRING_SIZE(x) (sizeof(x) - 1)
73
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. */
78
79 /* Revision graph */
80
81 #define REVGRAPH_INIT 'I'
82 #define REVGRAPH_MERGE 'M'
83 #define REVGRAPH_BRANCH '+'
84 #define REVGRAPH_COMMIT '*'
85 #define REVGRAPH_BOUND '^'
86
87 #define SIZEOF_REVGRAPH 19 /* Size of revision ancestry graphics. */
88
89 /* This color name can be used to refer to the default term colors. */
90 #define COLOR_DEFAULT (-1)
91
92 #define ICONV_NONE ((iconv_t) -1)
93 #ifndef ICONV_CONST
94 #define ICONV_CONST /* nothing */
95 #endif
96
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 ")
101
102 #define ID_COLS 8
103 #define AUTHOR_COLS 19
104
105 #define MIN_VIEW_HEIGHT 4
106
107 #define NULL_ID "0000000000000000000000000000000000000000"
108
109 #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
110
111 /* Some ASCII-shorthands fitted into the ncurses namespace. */
112 #define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */
113 #define KEY_TAB '\t'
114 #define KEY_RETURN '\r'
115 #define KEY_ESC 27
116
117 /*
118 * Allocation helpers ... Entering macro hell to never be seen again.
119 */
120
121 #define DEFINE_ALLOCATOR(name, type, chunk_size) \
122 static type * \
123 name(type **mem, size_t size, size_t increase) \
124 { \
125 size_t num_chunks = (size + chunk_size - 1) / chunk_size; \
126 size_t num_chunks_new = (size + increase + chunk_size - 1) / chunk_size;\
127 type *tmp = *mem; \
128 \
129 if (mem == NULL || num_chunks != num_chunks_new) { \
130 tmp = realloc(tmp, num_chunks_new * chunk_size * sizeof(type)); \
131 if (tmp) \
132 *mem = tmp; \
133 } \
134 \
135 return tmp; \
136 }
137
138 /*
139 * Strings.
140 */
141
142 #define prefixcmp(str1, str2) \
143 strncmp(str1, str2, STRING_SIZE(str2))
144
145 static inline int
146 suffixcmp(const char *str, int slen, const char *suffix)
147 {
148 size_t len = slen >= 0 ? slen : strlen(str);
149 size_t suffixlen = strlen(suffix);
150
151 return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
152 }
153
154 static inline void
155 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
156 {
157 if (srclen > dstlen - 1)
158 srclen = dstlen - 1;
159
160 strncpy(dst, src, srclen);
161 dst[srclen] = 0;
162 }
163
164 /* Shorthands for safely copying into a fixed buffer. */
165
166 #define string_copy(dst, src) \
167 string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
168
169 #define string_ncopy(dst, src, srclen) \
170 string_ncopy_do(dst, sizeof(dst), src, srclen)
171
172 #define string_copy_rev(dst, src) \
173 string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
174
175 #define string_add(dst, from, src) \
176 string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
177
178 static inline size_t
179 string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
180 {
181 size_t size, pos;
182
183 for (size = pos = 0; size < dstlen - 1 && src[pos]; pos++) {
184 if (src[pos] == '\t') {
185 size_t expanded = tabsize - (size % tabsize);
186
187 if (expanded + size >= dstlen - 1)
188 expanded = dstlen - size - 1;
189 memcpy(dst + size, " ", expanded);
190 size += expanded;
191 } else {
192 dst[size++] = src[pos];
193 }
194 }
195
196 dst[size] = 0;
197 return pos;
198 }
199
200 static inline char *
201 chomp_string(char *name)
202 {
203 int namelen;
204
205 while (isspace(*name))
206 name++;
207
208 namelen = strlen(name) - 1;
209 while (namelen > 0 && isspace(name[namelen]))
210 name[namelen--] = 0;
211
212 return name;
213 }
214
215 static inline bool
216 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
217 {
218 va_list args;
219 size_t pos = bufpos ? *bufpos : 0;
220
221 va_start(args, fmt);
222 pos += vsnprintf(buf + pos, bufsize - pos, fmt, args);
223 va_end(args);
224
225 if (bufpos)
226 *bufpos = pos;
227
228 return pos >= bufsize ? FALSE : TRUE;
229 }
230
231 #define string_format(buf, fmt, args...) \
232 string_nformat(buf, sizeof(buf), NULL, fmt, args)
233
234 #define string_format_from(buf, from, fmt, args...) \
235 string_nformat(buf, sizeof(buf), from, fmt, args)
236
237 /*
238 * Enumerations
239 */
240
241 struct enum_map {
242 const char *name;
243 int namelen;
244 int value;
245 };
246
247 #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
248
249 static inline int
250 string_enum_compare(const char *str1, const char *str2, int len)
251 {
252 size_t i;
253
254 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
255
256 /* Diff-Header == DIFF_HEADER */
257 for (i = 0; i < len; i++) {
258 if (toupper(str1[i]) == toupper(str2[i]))
259 continue;
260
261 if (string_enum_sep(str1[i]) &&
262 string_enum_sep(str2[i]))
263 continue;
264
265 return str1[i] - str2[i];
266 }
267
268 return 0;
269 }
270
271 #define enum_equals(entry, str, len) \
272 ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
273
274 static inline char *
275 enum_map_name(const char *name, size_t namelen)
276 {
277 static char buf[SIZEOF_STR];
278 int bufpos;
279
280 for (bufpos = 0; bufpos <= namelen; bufpos++) {
281 buf[bufpos] = tolower(name[bufpos]);
282 if (buf[bufpos] == '_')
283 buf[bufpos] = '-';
284 }
285
286 buf[bufpos] = 0;
287 return buf;
288 }
289
290 #define enum_name(entry) enum_map_name((entry).name, (entry).namelen)
291
292 static inline bool
293 map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
294 {
295 size_t namelen = strlen(name);
296 int i;
297
298 for (i = 0; i < map_size; i++)
299 if (enum_equals(map[i], name, namelen)) {
300 *value = map[i].value;
301 return TRUE;
302 }
303
304 return FALSE;
305 }
306
307 #define map_enum(attr, map, name) \
308 map_enum_do(map, ARRAY_SIZE(map), attr, name)
309
310 /*
311 * Unicode / UTF-8 handling
312 *
313 * NOTE: Much of the following code for dealing with Unicode is derived from
314 * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
315 * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
316 */
317
318 static inline int
319 unicode_width(unsigned long c, int tab_size)
320 {
321 if (c >= 0x1100 &&
322 (c <= 0x115f /* Hangul Jamo */
323 || c == 0x2329
324 || c == 0x232a
325 || (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f)
326 /* CJK ... Yi */
327 || (c >= 0xac00 && c <= 0xd7a3) /* Hangul Syllables */
328 || (c >= 0xf900 && c <= 0xfaff) /* CJK Compatibility Ideographs */
329 || (c >= 0xfe30 && c <= 0xfe6f) /* CJK Compatibility Forms */
330 || (c >= 0xff00 && c <= 0xff60) /* Fullwidth Forms */
331 || (c >= 0xffe0 && c <= 0xffe6)
332 || (c >= 0x20000 && c <= 0x2fffd)
333 || (c >= 0x30000 && c <= 0x3fffd)))
334 return 2;
335
336 if (c == '\t')
337 return tab_size;
338
339 return 1;
340 }
341
342 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
343 * Illegal bytes are set one. */
344 static const unsigned char utf8_bytes[256] = {
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 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,
349 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,
350 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,
351 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,
352 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,
353 };
354
355 static inline unsigned char
356 utf8_char_length(const char *string, const char *end)
357 {
358 int c = *(unsigned char *) string;
359
360 return utf8_bytes[c];
361 }
362
363 /* Decode UTF-8 multi-byte representation into a Unicode character. */
364 static inline unsigned long
365 utf8_to_unicode(const char *string, size_t length)
366 {
367 unsigned long unicode;
368
369 switch (length) {
370 case 1:
371 unicode = string[0];
372 break;
373 case 2:
374 unicode = (string[0] & 0x1f) << 6;
375 unicode += (string[1] & 0x3f);
376 break;
377 case 3:
378 unicode = (string[0] & 0x0f) << 12;
379 unicode += ((string[1] & 0x3f) << 6);
380 unicode += (string[2] & 0x3f);
381 break;
382 case 4:
383 unicode = (string[0] & 0x0f) << 18;
384 unicode += ((string[1] & 0x3f) << 12);
385 unicode += ((string[2] & 0x3f) << 6);
386 unicode += (string[3] & 0x3f);
387 break;
388 case 5:
389 unicode = (string[0] & 0x0f) << 24;
390 unicode += ((string[1] & 0x3f) << 18);
391 unicode += ((string[2] & 0x3f) << 12);
392 unicode += ((string[3] & 0x3f) << 6);
393 unicode += (string[4] & 0x3f);
394 break;
395 case 6:
396 unicode = (string[0] & 0x01) << 30;
397 unicode += ((string[1] & 0x3f) << 24);
398 unicode += ((string[2] & 0x3f) << 18);
399 unicode += ((string[3] & 0x3f) << 12);
400 unicode += ((string[4] & 0x3f) << 6);
401 unicode += (string[5] & 0x3f);
402 break;
403 default:
404 return 0;
405 }
406
407 /* Invalid characters could return the special 0xfffd value but NUL
408 * should be just as good. */
409 return unicode > 0xffff ? 0 : unicode;
410 }
411
412 /* Calculates how much of string can be shown within the given maximum width
413 * and sets trimmed parameter to non-zero value if all of string could not be
414 * shown. If the reserve flag is TRUE, it will reserve at least one
415 * trailing character, which can be useful when drawing a delimiter.
416 *
417 * Returns the number of bytes to output from string to satisfy max_width. */
418 static inline size_t
419 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve, int tab_size)
420 {
421 const char *string = *start;
422 const char *end = strchr(string, '\0');
423 unsigned char last_bytes = 0;
424 size_t last_ucwidth = 0;
425
426 *width = 0;
427 *trimmed = 0;
428
429 while (string < end) {
430 unsigned char bytes = utf8_char_length(string, end);
431 size_t ucwidth;
432 unsigned long unicode;
433
434 if (string + bytes > end)
435 break;
436
437 /* Change representation to figure out whether
438 * it is a single- or double-width character. */
439
440 unicode = utf8_to_unicode(string, bytes);
441 /* FIXME: Graceful handling of invalid Unicode character. */
442 if (!unicode)
443 break;
444
445 ucwidth = unicode_width(unicode, tab_size);
446 if (skip > 0) {
447 skip -= ucwidth <= skip ? ucwidth : skip;
448 *start += bytes;
449 }
450 *width += ucwidth;
451 if (*width > max_width) {
452 *trimmed = 1;
453 *width -= ucwidth;
454 if (reserve && *width == max_width) {
455 string -= last_bytes;
456 *width -= last_ucwidth;
457 }
458 break;
459 }
460
461 string += bytes;
462 last_bytes = ucwidth ? bytes : 0;
463 last_ucwidth = ucwidth;
464 }
465
466 return string - *start;
467 }
468
469 #endif
Something went wrong with that request. Please try again.