1 /* Copyright (c) 2006-2014 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 timecmp(const struct time
*t1
, const struct time
*t2
)
103 return t1
->sec
- t2
->sec
;
107 mkdate(const struct time
*time
, enum date date
)
109 static char buf
[STRING_SIZE("2006-04-29 14:21") + 1];
110 static const struct enum_map_entry reldate
[] = {
111 { "second", 1, 60 * 2 },
112 { "minute", 60, 60 * 60 * 2 },
113 { "hour", 60 * 60, 60 * 60 * 24 * 2 },
114 { "day", 60 * 60 * 24, 60 * 60 * 24 * 7 * 2 },
115 { "week", 60 * 60 * 24 * 7, 60 * 60 * 24 * 7 * 5 },
116 { "month", 60 * 60 * 24 * 30, 60 * 60 * 24 * 365 },
117 { "year", 60 * 60 * 24 * 365, 0 },
122 if (!date
|| !time
|| !time
->sec
)
125 if (date
== DATE_RELATIVE
) {
127 time_t date
= time
->sec
+ time
->tz
;
131 gettimeofday(&now
, NULL
);
132 seconds
= now
.tv_sec
< date
? date
- now
.tv_sec
: now
.tv_sec
- date
;
133 for (i
= 0; i
< ARRAY_SIZE(reldate
); i
++) {
134 if (seconds
>= reldate
[i
].value
&& reldate
[i
].value
)
137 seconds
/= reldate
[i
].namelen
;
138 if (!string_format(buf
, "%ld %s%s %s",
139 seconds
, reldate
[i
].name
,
140 seconds
> 1 ? "s" : "",
141 now
.tv_sec
>= date
? "ago" : "ahead"))
147 if (date
== DATE_LOCAL
) {
148 time_t date
= time
->sec
+ time
->tz
;
149 localtime_r(&date
, &tm
);
152 gmtime_r(&time
->sec
, &tm
);
155 format
= date
== DATE_SHORT
? "%Y-%m-%d" : "%Y-%m-%d %H:%M";
156 return strftime(buf
, sizeof(buf
), format
, &tm
) ? buf
: NULL
;
160 mkfilesize(unsigned long size
, enum file_size format
)
162 static char buf
[64 + 1];
163 static const char relsize
[] = {
164 'B', 'K', 'M', 'G', 'T', 'P'
170 if (format
== FILE_SIZE_UNITS
) {
171 const char *fmt
= "%.0f%c";
175 for (i
= 0; i
< ARRAY_SIZE(relsize
); i
++) {
176 if (rsize
> 1024.0 && i
+ 1 < ARRAY_SIZE(relsize
)) {
185 return string_format(buf
, fmt
, rsize
, relsize
[i
])
190 return string_format(buf
, "%ld", size
) ? buf
: NULL
;
193 const struct ident unknown_ident
= { "Unknown", "unknown@localhost" };
196 ident_compare(const struct ident
*i1
, const struct ident
*i2
)
199 return (!!i1
) - (!!i2
);
200 if (!i1
->name
|| !i2
->name
)
201 return (!!i1
->name
) - (!!i2
->name
);
202 return strcmp(i1
->name
, i2
->name
);
206 get_author_initials(const char *author
)
208 static char initials
[256];
210 const char *end
= strchr(author
, '\0');
212 #define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@' || (c) == '-')
214 memset(initials
, 0, sizeof(initials
));
215 while (author
< end
) {
219 while (author
< end
&& is_initial_sep(*author
))
222 bytes
= utf8_char_length(author
);
223 if (bytes
>= sizeof(initials
) - 1 - pos
)
226 initials
[pos
++] = *author
++;
230 while (author
< end
&& !is_initial_sep(*author
)) {
231 bytes
= utf8_char_length(author
);
232 if (bytes
>= sizeof(initials
) - 1 - i
) {
233 while (author
< end
&& !is_initial_sep(*author
))
238 initials
[i
++] = *author
++;
249 get_email_user(const char *email
)
251 static char user
[SIZEOF_STR
+ 1];
252 const char *end
= strchr(email
, '@');
253 int length
= end
? end
- email
: strlen(email
);
255 string_format(user
, "%.*s%c", length
, email
, 0);
260 mkauthor(const struct ident
*ident
, int cols
, enum author author
)
262 bool trim
= author_trim(cols
);
263 bool abbreviate
= author
== AUTHOR_ABBREVIATED
|| !trim
;
265 if (author
== AUTHOR_NO
|| !ident
)
267 if (author
== AUTHOR_EMAIL
&& ident
->email
)
269 if (author
== AUTHOR_EMAIL_USER
&& ident
->email
)
270 return get_email_user(ident
->email
);
271 if (abbreviate
&& ident
->name
)
272 return get_author_initials(ident
->name
);
281 else if (S_ISLNK(mode
))
283 else if (S_ISGITLINK(mode
))
285 else if (S_ISREG(mode
) && mode
& S_IXUSR
)
287 else if (S_ISREG(mode
))
294 mkstatus(const char status
, enum status_label label
)
296 static char default_label
[] = { '?', 0 };
297 static const char *labels
[][2] = {
299 { "?", "untracked" },
309 if (label
== STATUS_LABEL_NO
)
312 for (i
= 0; i
< ARRAY_SIZE(labels
); i
++) {
313 if (status
== *labels
[i
][0]) {
314 if (label
== STATUS_LABEL_LONG
)
321 default_label
[0] = status
;
322 return default_label
;
325 /* vim: set ts=8 sw=8 noexpandtab: */