tig-2.1.1
[tig.git] / src / parse.c
blob17e16fb8ace22658c0ba46e69f5571a9f6b3cb6b
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.
14 #include "tig/tig.h"
15 #include "tig/parse.h"
16 #include "tig/map.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, char skip, bool optional)
190 const char *start = *pos_ptr;
191 char *end;
193 if (*start != skip)
194 return optional;
196 start++;
197 *value = strtoul(start, &end, 10);
198 if (end == start)
199 return FALSE;
201 while (isspace(*end))
202 end++;
203 *pos_ptr = end;
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("@@ -") - 1;
214 else if (!prefixcmp(line, "@@@ -") &&
215 (line = strchr(line + STRING_SIZE("@@@ -"), '-')))
216 /* Stay at that '-'. */ ;
217 else
218 return FALSE;
220 return parse_ulong(&line, &header->old.position, '-', FALSE) &&
221 parse_ulong(&line, &header->old.lines, ',', TRUE) &&
222 parse_ulong(&line, &header->new.position, '+', FALSE) &&
223 parse_ulong(&line, &header->new.lines, ',', FALSE);
226 bool
227 parse_chunk_lineno(unsigned long *lineno, const char *chunk, int marker)
229 struct chunk_header chunk_header;
231 *lineno = 0;
233 if (!parse_chunk_header(&chunk_header, chunk))
234 return FALSE;
236 *lineno = marker == '-' ? chunk_header.old.position : chunk_header.new.position;
237 return TRUE;
241 * Caches.
244 struct path_entry {
245 char path[1];
248 DEFINE_STRING_MAP(path_cache, struct path_entry *, path, 32)
250 /* Small cache to reduce memory consumption. No entries are ever
251 * freed. */
252 const char *
253 get_path(const char *path)
255 struct path_entry *entry = string_map_get(&path_cache, path);
257 if (!entry) {
258 entry = calloc(1, sizeof(*entry) + strlen(path));
259 if (!entry || !string_map_put(&path_cache, path, entry)) {
260 free(entry);
261 return NULL;
263 strncpy(entry->path, path, strlen(path));
266 return entry->path;
269 DEFINE_STRING_MAP(author_cache, const struct ident *, email, 32)
271 /* Small author cache to reduce memory consumption. No entries
272 * are ever freed. */
273 struct ident *
274 get_author(const char *name, const char *email)
276 struct ident *ident = string_map_get(&author_cache, email);
278 if (ident)
279 return ident;
281 ident = calloc(1, sizeof(*ident));
282 if (!ident)
283 return NULL;
284 ident->name = strdup(name);
285 ident->email = strdup(email);
286 if (!ident->name || !ident->email ||
287 !string_map_put(&author_cache, email, ident)) {
288 free((void *) ident->name);
289 free((void *) ident->email);
290 free(ident);
291 return NULL;
294 return ident;
297 /* vim: set ts=8 sw=8 noexpandtab: */