Partly restore old refresh behavior
[tig.git] / src / parse.c
blob524de1acea8d4d9ec8b4d7da10932dd5ba981858
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/tig.h"
15 #include "tig/util.h"
16 #include "tig/parse.h"
18 size_t
19 parse_size(const char *text)
21 size_t size = 0;
23 while (*text == ' ')
24 text++;
26 while (isdigit(*text))
27 size = (size * 10) + (*text++ - '0');
29 return size;
33 * Parsing of ident lines.
36 static void
37 parse_timesec(struct time *time, const char *sec)
39 time->sec = (time_t) atol(sec);
42 static void
43 parse_timezone(struct time *time, const char *zone)
45 long tz;
47 tz = ('0' - zone[1]) * 60 * 60 * 10;
48 tz += ('0' - zone[2]) * 60 * 60;
49 tz += ('0' - zone[3]) * 60 * 10;
50 tz += ('0' - zone[4]) * 60;
52 if (zone[0] == '-')
53 tz = -tz;
55 time->tz = tz;
56 time->sec -= tz;
59 void
60 parse_author_line(char *ident, const struct ident **author, struct time *time)
62 char *nameend = strchr(ident, '<');
63 char *emailend = strchr(ident, '>');
64 const char *name, *email = "";
66 if (nameend && emailend)
67 *nameend = *emailend = 0;
68 name = chomp_string(ident);
69 if (nameend)
70 email = chomp_string(nameend + 1);
71 if (!*name)
72 name = *email ? email : unknown_ident.name;
73 if (!*email)
74 email = *name ? name : unknown_ident.email;
76 *author = get_author(name, email);
78 /* Parse epoch and timezone */
79 if (time && emailend && emailend[1] == ' ') {
80 char *secs = emailend + 2;
81 char *zone = strchr(secs, ' ');
83 parse_timesec(time, secs);
85 if (zone && strlen(zone) == STRING_SIZE(" +0700"))
86 parse_timezone(time, zone + 1);
91 * Blame.
94 static bool
95 parse_number(const char **posref, size_t *number, size_t min, size_t max)
97 const char *pos = *posref;
99 *posref = NULL;
100 pos = strchr(pos + 1, ' ');
101 if (!pos || !isdigit(pos[1]))
102 return FALSE;
103 *number = atoi(pos + 1);
104 if (*number < min || *number > max)
105 return FALSE;
107 *posref = pos;
108 return TRUE;
111 bool
112 parse_blame_header(struct blame_header *header, const char *text, size_t max_lineno)
114 const char *pos = text + SIZEOF_REV - 2;
116 if (strlen(text) <= SIZEOF_REV || pos[1] != ' ')
117 return FALSE;
119 string_ncopy(header->id, text, SIZEOF_REV);
121 if (!parse_number(&pos, &header->orig_lineno, 1, 9999999) ||
122 !parse_number(&pos, &header->lineno, 1, max_lineno) ||
123 !parse_number(&pos, &header->group, 1, max_lineno - header->lineno + 1))
124 return FALSE;
126 return TRUE;
129 static bool
130 match_blame_header(const char *name, char **line)
132 size_t namelen = strlen(name);
133 bool matched = !strncmp(name, *line, namelen);
135 if (matched)
136 *line += namelen;
138 return matched;
141 bool
142 parse_blame_info(struct blame_commit *commit, char author[SIZEOF_STR], char *line)
144 if (match_blame_header("author ", &line)) {
145 string_ncopy_do(author, SIZEOF_STR, line, strlen(line));
147 } else if (match_blame_header("author-mail ", &line)) {
148 char *end = strchr(line, '>');
150 if (end)
151 *end = 0;
152 if (*line == '<')
153 line++;
154 commit->author = get_author(author, line);
155 author[0] = 0;
157 } else if (match_blame_header("author-time ", &line)) {
158 parse_timesec(&commit->time, line);
160 } else if (match_blame_header("author-tz ", &line)) {
161 parse_timezone(&commit->time, line);
163 } else if (match_blame_header("summary ", &line)) {
164 string_ncopy(commit->title, line, strlen(line));
166 } else if (match_blame_header("previous ", &line)) {
167 if (strlen(line) <= SIZEOF_REV)
168 return FALSE;
169 string_copy_rev(commit->parent_id, line);
170 line += SIZEOF_REV;
171 commit->parent_filename = get_path(line);
172 if (!commit->parent_filename)
173 return TRUE;
175 } else if (match_blame_header("filename ", &line)) {
176 commit->filename = get_path(line);
177 return TRUE;
180 return FALSE;
184 * Diff.
187 static bool
188 parse_ulong(const char **pos_ptr, unsigned long *value, const char *skip)
190 const char *start = *pos_ptr;
191 char *end;
193 if (!isdigit(*start))
194 return 0;
196 *value = strtoul(start, &end, 10);
197 if (end == start)
198 return FALSE;
200 start = end;
201 while (skip && *start && strchr(skip, *start))
202 start++;
203 *pos_ptr = start;
204 return TRUE;
207 bool
208 parse_chunk_header(struct chunk_header *header, const char *line)
210 memset(header, 0, sizeof(*header));
212 if (!prefixcmp(line, "@@ -"))
213 line += STRING_SIZE("@@ -");
214 else if (!prefixcmp(line, "@@@ -") &&
215 (line = strchr(line + STRING_SIZE("@@@ -"), '-')))
216 line += 1;
217 else
218 return FALSE;
221 return parse_ulong(&line, &header->old.position, ",") &&
222 parse_ulong(&line, &header->old.lines, " +") &&
223 parse_ulong(&line, &header->new.position, ",") &&
224 parse_ulong(&line, &header->new.lines, NULL);
227 bool
228 parse_chunk_lineno(unsigned long *lineno, const char *chunk, int marker)
230 struct chunk_header chunk_header;
232 *lineno = 0;
234 if (!parse_chunk_header(&chunk_header, chunk))
235 return FALSE;
237 *lineno = marker == '-' ? chunk_header.old.position : chunk_header.new.position;
238 return TRUE;
242 * Caches.
245 DEFINE_ALLOCATOR(realloc_paths, const char *, 256)
247 /* Small cache to reduce memory consumption. It uses binary search to
248 * lookup or find place to position new entries. No entries are ever
249 * freed. */
250 const char *
251 get_path(const char *path)
253 static const char **paths;
254 static size_t paths_size;
255 int from = 0, to = paths_size - 1;
256 char *entry;
258 while (from <= to) {
259 size_t pos = (to + from) / 2;
260 int cmp = strcmp(path, paths[pos]);
262 if (!cmp)
263 return paths[pos];
265 if (cmp < 0)
266 to = pos - 1;
267 else
268 from = pos + 1;
271 if (!realloc_paths(&paths, paths_size, 1))
272 return NULL;
273 entry = strdup(path);
274 if (!entry)
275 return NULL;
277 memmove(paths + from + 1, paths + from, (paths_size - from) * sizeof(*paths));
278 paths[from] = entry;
279 paths_size++;
281 return entry;
284 DEFINE_ALLOCATOR(realloc_authors, struct ident *, 256)
286 /* Small author cache to reduce memory consumption. It uses binary
287 * search to lookup or find place to position new entries. No entries
288 * are ever freed. */
289 struct ident *
290 get_author(const char *name, const char *email)
292 static struct ident **authors;
293 static size_t authors_size;
294 int from = 0, to = authors_size - 1;
295 struct ident *ident;
297 while (from <= to) {
298 size_t pos = (to + from) / 2;
299 int cmp = strcmp(email, authors[pos]->email);
301 if (!cmp)
302 return authors[pos];
304 if (cmp < 0)
305 to = pos - 1;
306 else
307 from = pos + 1;
310 if (!realloc_authors(&authors, authors_size, 1))
311 return NULL;
312 ident = calloc(1, sizeof(*ident));
313 if (!ident)
314 return NULL;
315 ident->name = strdup(name);
316 ident->email = strdup(email);
317 if (!ident->name || !ident->email) {
318 free((void *) ident->name);
319 free(ident);
320 return NULL;
323 memmove(authors + from + 1, authors + from, (authors_size - from) * sizeof(*authors));
324 authors[from] = ident;
325 authors_size++;
327 return ident;
330 /* vim: set ts=8 sw=8 noexpandtab: */