Move format_argv to argv module
[tig.git] / src / util.c
blob531dd19192eb536fc8f027af3d78410ad8521694
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.
14 #include "tig.h"
15 #include "util.h"
18 * Error handling.
21 static const char *status_messages[] = {
22 "Success",
23 #define STATUS_CODE_MESSAGE(name, msg) msg
24 STATUS_CODE_INFO(STATUS_CODE_MESSAGE)
27 const char *
28 get_status_message(enum status_code code)
30 if (code == SUCCESS)
31 return "";
32 return status_messages[code];
35 void
36 warn(const char *msg, ...)
38 va_list args;
40 va_start(args, msg);
41 fputs("tig warning: ", stderr);
42 vfprintf(stderr, msg, args);
43 fputs("\n", stderr);
44 va_end(args);
47 die_fn die_callback = NULL;
48 void TIG_NORETURN
49 die(const char *err, ...)
51 va_list args;
53 if (die_callback)
54 die_callback();
56 va_start(args, err);
57 fputs("tig: ", stderr);
58 vfprintf(stderr, err, args);
59 fputs("\n", stderr);
60 va_end(args);
62 exit(1);
66 * Git data formatters and parsers.
69 int
70 timecmp(const struct time *t1, const struct time *t2)
72 return t1->sec - t2->sec;
75 const char *
76 mkdate(const struct time *time, enum date date)
78 static char buf[DATE_WIDTH + 1];
79 static const struct enum_map_entry reldate[] = {
80 { "second", 1, 60 * 2 },
81 { "minute", 60, 60 * 60 * 2 },
82 { "hour", 60 * 60, 60 * 60 * 24 * 2 },
83 { "day", 60 * 60 * 24, 60 * 60 * 24 * 7 * 2 },
84 { "week", 60 * 60 * 24 * 7, 60 * 60 * 24 * 7 * 5 },
85 { "month", 60 * 60 * 24 * 30, 60 * 60 * 24 * 365 },
86 { "year", 60 * 60 * 24 * 365, 0 },
88 struct tm tm;
90 if (!date || !time || !time->sec)
91 return "";
93 if (date == DATE_RELATIVE) {
94 struct timeval now;
95 time_t date = time->sec + time->tz;
96 time_t seconds;
97 int i;
99 gettimeofday(&now, NULL);
100 seconds = now.tv_sec < date ? date - now.tv_sec : now.tv_sec - date;
101 for (i = 0; i < ARRAY_SIZE(reldate); i++) {
102 if (seconds >= reldate[i].value && reldate[i].value)
103 continue;
105 seconds /= reldate[i].namelen;
106 if (!string_format(buf, "%ld %s%s %s",
107 seconds, reldate[i].name,
108 seconds > 1 ? "s" : "",
109 now.tv_sec >= date ? "ago" : "ahead"))
110 break;
111 return buf;
115 if (date == DATE_LOCAL) {
116 time_t date = time->sec + time->tz;
117 localtime_r(&date, &tm);
119 else {
120 gmtime_r(&time->sec, &tm);
122 return strftime(buf, sizeof(buf), DATE_FORMAT, &tm) ? buf : NULL;
125 const char *
126 mkfilesize(unsigned long size, enum file_size format)
128 static char buf[64 + 1];
129 static const char relsize[] = {
130 'B', 'K', 'M', 'G', 'T', 'P'
133 if (!format)
134 return "";
136 if (format == FILE_SIZE_UNITS) {
137 const char *fmt = "%.0f%c";
138 double rsize = size;
139 int i;
141 for (i = 0; i < ARRAY_SIZE(relsize); i++) {
142 if (rsize > 1024.0 && i + 1 < ARRAY_SIZE(relsize)) {
143 rsize /= 1024;
144 continue;
147 size = rsize * 10;
148 if (size % 10 > 0)
149 fmt = "%.1f%c";
151 return string_format(buf, fmt, rsize, relsize[i])
152 ? buf : NULL;
156 return string_format(buf, "%ld", size) ? buf : NULL;
159 const struct ident unknown_ident = { "Unknown", "unknown@localhost" };
162 ident_compare(const struct ident *i1, const struct ident *i2)
164 if (!i1 || !i2)
165 return (!!i1) - (!!i2);
166 if (!i1->name || !i2->name)
167 return (!!i1->name) - (!!i2->name);
168 return strcmp(i1->name, i2->name);
171 static const char *
172 get_author_initials(const char *author)
174 static char initials[256];
175 size_t pos = 0;
176 const char *end = strchr(author, '\0');
178 #define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@' || (c) == '-')
180 memset(initials, 0, sizeof(initials));
181 while (author < end) {
182 unsigned char bytes;
183 size_t i;
185 while (author < end && is_initial_sep(*author))
186 author++;
188 bytes = utf8_char_length(author, end);
189 if (bytes >= sizeof(initials) - 1 - pos)
190 break;
191 while (bytes--) {
192 initials[pos++] = *author++;
195 i = pos;
196 while (author < end && !is_initial_sep(*author)) {
197 bytes = utf8_char_length(author, end);
198 if (bytes >= sizeof(initials) - 1 - i) {
199 while (author < end && !is_initial_sep(*author))
200 author++;
201 break;
203 while (bytes--) {
204 initials[i++] = *author++;
208 initials[i++] = 0;
211 return initials;
214 static const char *
215 get_email_user(const char *email)
217 static char user[SIZEOF_STR + 1];
218 const char *end = strchr(email, '@');
219 int length = end ? end - email : strlen(email);
221 string_format(user, "%.*s%c", length, email, 0);
222 return user;
225 const char *
226 mkauthor(const struct ident *ident, int cols, enum author author)
228 bool trim = author_trim(cols);
229 bool abbreviate = author == AUTHOR_ABBREVIATED || !trim;
231 if (author == AUTHOR_NO || !ident)
232 return "";
233 if (author == AUTHOR_EMAIL && ident->email)
234 return ident->email;
235 if (author == AUTHOR_EMAIL_USER && ident->email)
236 return get_email_user(ident->email);
237 if (abbreviate && ident->name)
238 return get_author_initials(ident->name);
239 return ident->name;
242 const char *
243 mkmode(mode_t mode)
245 if (S_ISDIR(mode))
246 return "drwxr-xr-x";
247 else if (S_ISLNK(mode))
248 return "lrwxrwxrwx";
249 else if (S_ISGITLINK(mode))
250 return "m---------";
251 else if (S_ISREG(mode) && mode & S_IXUSR)
252 return "-rwxr-xr-x";
253 else if (S_ISREG(mode))
254 return "-rw-r--r--";
255 else
256 return "----------";
259 /* vim: set ts=8 sw=8 noexpandtab: */