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
;
130 const char compact_symbol
;
131 int in_seconds
, interval
;
134 static const struct reldate reldate
[] = {
135 { "second", 's', 1, 60 * 2 },
136 { "minute", 'M', 60, 60 * 60 * 2 },
137 { "hour", 'h', 60 * 60, 60 * 60 * 24 * 2 },
138 { "day", 'd', 60 * 60 * 24, 60 * 60 * 24 * 7 * 2 },
139 { "week", 'w', 60 * 60 * 24 * 7, 60 * 60 * 24 * 7 * 5 },
140 { "month", 'm', 60 * 60 * 24 * 30, 60 * 60 * 24 * 365 },
141 { "year", 'y', 60 * 60 * 24 * 365, 0 },
145 get_relative_date(const struct time
*time
, char *buf
, size_t buflen
, bool compact
)
148 time_t timestamp
= time
->sec
+ time
->tz
;
152 if (time_now(&now
, NULL
))
155 seconds
= now
.tv_sec
< timestamp
? timestamp
- now
.tv_sec
: now
.tv_sec
- timestamp
;
157 for (i
= 0; i
< ARRAY_SIZE(reldate
); i
++) {
158 if (seconds
>= reldate
[i
].interval
&& reldate
[i
].interval
)
161 seconds
/= reldate
[i
].in_seconds
;
163 if (!string_nformat(buf
, buflen
, NULL
, "%s%ld%c",
164 now
.tv_sec
>= timestamp
? "" : "-",
165 seconds
, reldate
[i
].compact_symbol
))
168 } else if (!string_nformat(buf
, buflen
, NULL
, "%ld %s%s %s",
169 seconds
, reldate
[i
].name
,
170 seconds
> 1 ? "s" : "",
171 now
.tv_sec
>= timestamp
? "ago" : "ahead"))
181 mkdate(const struct time
*time
, enum date date
, bool local
, const char *custom_format
)
183 static char buf
[STRING_SIZE("2006-04-29 14:21") + 1];
187 if (!date
|| !time
|| !time
->sec
)
190 if (date
== DATE_RELATIVE
|| date
== DATE_RELATIVE_COMPACT
)
191 return get_relative_date(time
, buf
, sizeof(buf
),
192 date
== DATE_RELATIVE_COMPACT
);
195 time_t date
= time
->sec
+ time
->tz
;
196 localtime_r(&date
, &tm
);
199 gmtime_r(&time
->sec
, &tm
);
202 format
= date
!= DATE_CUSTOM
204 : custom_format
? custom_format
: "%Y-%m-%d";
205 return strftime(buf
, sizeof(buf
), format
, &tm
) ? buf
: NULL
;
209 mkfilesize(unsigned long size
, enum file_size format
)
211 static char buf
[64 + 1];
212 static const char relsize
[] = {
213 'B', 'K', 'M', 'G', 'T', 'P'
219 if (format
== FILE_SIZE_UNITS
) {
220 const char *fmt
= "%.0f%c";
224 for (i
= 0; i
< ARRAY_SIZE(relsize
); i
++) {
225 if (rsize
> 1024.0 && i
+ 1 < ARRAY_SIZE(relsize
)) {
234 return string_format(buf
, fmt
, rsize
, relsize
[i
])
239 return string_format(buf
, "%ld", size
) ? buf
: NULL
;
242 const struct ident unknown_ident
= { "Unknown", "unknown@localhost" };
245 ident_compare(const struct ident
*i1
, const struct ident
*i2
)
248 return (!!i1
) - (!!i2
);
249 if (!i1
->name
|| !i2
->name
)
250 return (!!i1
->name
) - (!!i2
->name
);
251 return strcmp(i1
->name
, i2
->name
);
255 get_author_initials(const char *author
)
257 static char initials
[256];
259 const char *end
= strchr(author
, '\0');
261 #define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@' || (c) == '-')
263 memset(initials
, 0, sizeof(initials
));
264 while (author
< end
) {
268 while (author
< end
&& is_initial_sep(*author
))
271 bytes
= utf8_char_length(author
);
272 if (bytes
>= sizeof(initials
) - 1 - pos
)
275 initials
[pos
++] = *author
++;
279 while (author
< end
&& !is_initial_sep(*author
)) {
280 bytes
= utf8_char_length(author
);
281 if (bytes
>= sizeof(initials
) - 1 - i
) {
282 while (author
< end
&& !is_initial_sep(*author
))
287 initials
[i
++] = *author
++;
298 get_email_user(const char *email
)
300 static char user
[SIZEOF_STR
+ 1];
301 const char *end
= strchr(email
, '@');
302 int length
= end
? end
- email
: strlen(email
);
304 string_format(user
, "%.*s%c", length
, email
, 0);
309 mkauthor(const struct ident
*ident
, int cols
, enum author author
)
311 bool trim
= author_trim(cols
);
312 bool abbreviate
= author
== AUTHOR_ABBREVIATED
|| !trim
;
314 if (author
== AUTHOR_NO
|| !ident
)
316 if (author
== AUTHOR_EMAIL
&& ident
->email
)
318 if (author
== AUTHOR_EMAIL_USER
&& ident
->email
)
319 return get_email_user(ident
->email
);
320 if (abbreviate
&& ident
->name
)
321 return get_author_initials(ident
->name
);
330 else if (S_ISLNK(mode
))
332 else if (S_ISGITLINK(mode
))
334 else if (S_ISREG(mode
) && mode
& S_IXUSR
)
336 else if (S_ISREG(mode
))
343 mkstatus(const char status
, enum status_label label
)
345 static char default_label
[] = { '?', 0 };
346 static const char *labels
[][2] = {
348 { "?", "untracked" },
358 if (label
== STATUS_LABEL_NO
)
361 for (i
= 0; i
< ARRAY_SIZE(labels
); i
++) {
362 if (status
== *labels
[i
][0]) {
363 if (label
== STATUS_LABEL_LONG
)
370 default_label
[0] = status
;
371 return default_label
;
379 chunk_allocator(void *mem
, size_t type_size
, size_t chunk_size
, size_t size
, size_t increase
)
381 size_t num_chunks
= (size
+ chunk_size
- 1) / chunk_size
;
382 size_t num_chunks_new
= (size
+ increase
+ chunk_size
- 1) / chunk_size
;
384 if (mem
== NULL
|| num_chunks
!= num_chunks_new
) {
385 size_t newsize
= num_chunks_new
* chunk_size
* type_size
;
386 void *tmp
= realloc(mem
, newsize
);
391 if (num_chunks_new
> num_chunks
) {
392 size_t oldsize
= num_chunks
* chunk_size
* type_size
;
394 memset(tmp
+ oldsize
, 0, newsize
- oldsize
);
403 /* vim: set ts=8 sw=8 noexpandtab: */