1 /* Copyright (c) 2006-2015 Jonas Fonseca <jonas.fonseca@gmail.com>
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.
21 static const char *status_messages
[] = {
23 #define STATUS_CODE_MESSAGE(name, msg) msg
24 STATUS_CODE_INFO(STATUS_CODE_MESSAGE
)
27 static char status_custom_message
[SIZEOF_STR
];
28 static bool status_success_message
= false;
31 get_status_message(enum status_code code
)
33 if (code
== SUCCESS
) {
34 const char *message
= status_success_message
? status_custom_message
: "";
36 status_success_message
= false;
39 if (code
== ERROR_CUSTOM_MESSAGE
)
40 return status_custom_message
;
41 return status_messages
[code
];
45 error(const char *msg
, ...)
49 FORMAT_BUFFER(status_custom_message
, sizeof(status_custom_message
), msg
, retval
, true);
50 status_success_message
= false;
52 return ERROR_CUSTOM_MESSAGE
;
56 success(const char *msg
, ...)
60 FORMAT_BUFFER(status_custom_message
, sizeof(status_custom_message
), msg
, retval
, true);
61 status_success_message
= true;
67 warn(const char *msg
, ...)
72 fputs("tig warning: ", stderr
);
73 vfprintf(stderr
, msg
, args
);
78 die_fn die_callback
= NULL
;
80 die(const char *err
, ...)
88 fputs("tig: ", stderr
);
89 vfprintf(stderr
, err
, args
);
97 * Git data formatters and parsers.
101 time_now(struct timeval
*timeval
, struct timezone
*tz
)
103 static bool check_env
= true;
108 if ((time
= getenv("TEST_TIME_NOW"))) {
109 memset(timeval
, 0, sizeof(*timeval
));
111 memset(tz
, 0, sizeof(*tz
));
112 timeval
->tv_sec
= atoi(time
);
119 return gettimeofday(timeval
, tz
);
123 timecmp(const struct time
*t1
, const struct time
*t2
)
125 return t1
->sec
- t2
->sec
;
129 get_relative_date(const struct time
*time
, char *buf
, size_t buflen
)
131 static const struct enum_map_entry reldate
[] = {
132 { "second", 1, 60 * 2 },
133 { "minute", 60, 60 * 60 * 2 },
134 { "hour", 60 * 60, 60 * 60 * 24 * 2 },
135 { "day", 60 * 60 * 24, 60 * 60 * 24 * 7 * 2 },
136 { "week", 60 * 60 * 24 * 7, 60 * 60 * 24 * 7 * 5 },
137 { "month", 60 * 60 * 24 * 30, 60 * 60 * 24 * 365 },
138 { "year", 60 * 60 * 24 * 365, 0 },
141 time_t timestamp
= time
->sec
+ time
->tz
;
145 if (time_now(&now
, NULL
))
148 seconds
= now
.tv_sec
< timestamp
? timestamp
- now
.tv_sec
: now
.tv_sec
- timestamp
;
150 for (i
= 0; i
< ARRAY_SIZE(reldate
); i
++) {
151 if (seconds
>= reldate
[i
].value
&& reldate
[i
].value
)
154 seconds
/= reldate
[i
].namelen
;
155 if (!string_nformat(buf
, buflen
, NULL
, "%ld %s%s %s",
156 seconds
, reldate
[i
].name
,
157 seconds
> 1 ? "s" : "",
158 now
.tv_sec
>= timestamp
? "ago" : "ahead"))
168 mkdate(const struct time
*time
, enum date date
)
170 static char buf
[STRING_SIZE("2006-04-29 14:21") + 1];
174 if (!date
|| !time
|| !time
->sec
)
177 if (date
== DATE_RELATIVE
)
178 return get_relative_date(time
, buf
, sizeof(buf
));
180 if (date
== DATE_LOCAL
) {
181 time_t date
= time
->sec
+ time
->tz
;
182 localtime_r(&date
, &tm
);
185 gmtime_r(&time
->sec
, &tm
);
188 format
= date
== DATE_SHORT
? "%Y-%m-%d" : "%Y-%m-%d %H:%M";
189 return strftime(buf
, sizeof(buf
), format
, &tm
) ? buf
: NULL
;
193 mkfilesize(unsigned long size
, enum file_size format
)
195 static char buf
[64 + 1];
196 static const char relsize
[] = {
197 'B', 'K', 'M', 'G', 'T', 'P'
203 if (format
== FILE_SIZE_UNITS
) {
204 const char *fmt
= "%.0f%c";
208 for (i
= 0; i
< ARRAY_SIZE(relsize
); i
++) {
209 if (rsize
> 1024.0 && i
+ 1 < ARRAY_SIZE(relsize
)) {
218 return string_format(buf
, fmt
, rsize
, relsize
[i
])
223 return string_format(buf
, "%ld", size
) ? buf
: NULL
;
226 const struct ident unknown_ident
= { "Unknown", "unknown@localhost" };
229 ident_compare(const struct ident
*i1
, const struct ident
*i2
)
232 return (!!i1
) - (!!i2
);
233 if (!i1
->name
|| !i2
->name
)
234 return (!!i1
->name
) - (!!i2
->name
);
235 return strcmp(i1
->name
, i2
->name
);
239 get_author_initials(const char *author
)
241 static char initials
[256];
243 const char *end
= strchr(author
, '\0');
245 #define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@' || (c) == '-')
247 memset(initials
, 0, sizeof(initials
));
248 while (author
< end
) {
252 while (author
< end
&& is_initial_sep(*author
))
255 bytes
= utf8_char_length(author
);
256 if (bytes
>= sizeof(initials
) - 1 - pos
)
259 initials
[pos
++] = *author
++;
263 while (author
< end
&& !is_initial_sep(*author
)) {
264 bytes
= utf8_char_length(author
);
265 if (bytes
>= sizeof(initials
) - 1 - i
) {
266 while (author
< end
&& !is_initial_sep(*author
))
271 initials
[i
++] = *author
++;
282 get_email_user(const char *email
)
284 static char user
[SIZEOF_STR
+ 1];
285 const char *end
= strchr(email
, '@');
286 int length
= end
? end
- email
: strlen(email
);
288 string_format(user
, "%.*s%c", length
, email
, 0);
293 mkauthor(const struct ident
*ident
, int cols
, enum author author
)
295 bool trim
= author_trim(cols
);
296 bool abbreviate
= author
== AUTHOR_ABBREVIATED
|| !trim
;
298 if (author
== AUTHOR_NO
|| !ident
)
300 if (author
== AUTHOR_EMAIL
&& ident
->email
)
302 if (author
== AUTHOR_EMAIL_USER
&& ident
->email
)
303 return get_email_user(ident
->email
);
304 if (abbreviate
&& ident
->name
)
305 return get_author_initials(ident
->name
);
314 else if (S_ISLNK(mode
))
316 else if (S_ISGITLINK(mode
))
318 else if (S_ISREG(mode
) && mode
& S_IXUSR
)
320 else if (S_ISREG(mode
))
327 mkstatus(const char status
, enum status_label label
)
329 static char default_label
[] = { '?', 0 };
330 static const char *labels
[][2] = {
332 { "?", "untracked" },
342 if (label
== STATUS_LABEL_NO
)
345 for (i
= 0; i
< ARRAY_SIZE(labels
); i
++) {
346 if (status
== *labels
[i
][0]) {
347 if (label
== STATUS_LABEL_LONG
)
354 default_label
[0] = status
;
355 return default_label
;
363 chunk_allocator(void *mem
, size_t type_size
, size_t chunk_size
, size_t size
, size_t increase
)
365 size_t num_chunks
= (size
+ chunk_size
- 1) / chunk_size
;
366 size_t num_chunks_new
= (size
+ increase
+ chunk_size
- 1) / chunk_size
;
368 if (mem
== NULL
|| num_chunks
!= num_chunks_new
) {
369 size_t newsize
= num_chunks_new
* chunk_size
* type_size
;
370 void *tmp
= realloc(mem
, newsize
);
375 if (num_chunks_new
> num_chunks
) {
376 size_t oldsize
= num_chunks
* chunk_size
* type_size
;
378 memset(tmp
+ oldsize
, 0, newsize
- oldsize
);
387 /* vim: set ts=8 sw=8 noexpandtab: */