Don't reload the stash view when switching back to it
[tig.git] / src / parse.c
blob519a49118b866bb6dc9b98e20aa5c4a7e0e7f8ec
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));
211 header->new.lines = header->old.lines = 1;
213 if (!prefixcmp(line, "@@ -"))
214 line += STRING_SIZE("@@ -") - 1;
215 else if (!prefixcmp(line, "@@@ -") &&
216 (line = strchr(line + STRING_SIZE("@@@ -"), '-')))
217 /* Stay at that '-'. */ ;
218 else
219 return false;
221 return parse_ulong(&line, &header->old.position, '-', false) &&
222 parse_ulong(&line, &header->old.lines, ',', true) &&
223 parse_ulong(&line, &header->new.position, '+', false) &&
224 parse_ulong(&line, &header->new.lines, ',', false);
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 struct path_entry {
246 char path[1];
249 DEFINE_STRING_MAP(path_cache, struct path_entry *, path, 32)
251 /* Small cache to reduce memory consumption. No entries are ever
252 * freed. */
253 const char *
254 get_path(const char *path)
256 struct path_entry *entry = string_map_get(&path_cache, path);
258 if (!entry) {
259 entry = calloc(1, sizeof(*entry) + strlen(path));
260 if (!entry || !string_map_put(&path_cache, path, entry)) {
261 free(entry);
262 return NULL;
264 strncpy(entry->path, path, strlen(path));
267 return entry->path;
270 DEFINE_STRING_MAP(author_cache, const struct ident *, email, 32)
272 /* Small author cache to reduce memory consumption. No entries
273 * are ever freed. */
274 struct ident *
275 get_author(const char *name, const char *email)
277 struct ident *ident = string_map_get(&author_cache, email);
279 if (ident)
280 return ident;
282 ident = calloc(1, sizeof(*ident));
283 if (!ident)
284 return NULL;
285 ident->name = strdup(name);
286 ident->email = strdup(email);
287 if (!ident->name || !ident->email ||
288 !string_map_put(&author_cache, email, ident)) {
289 free((void *) ident->name);
290 free((void *) ident->email);
291 free(ident);
292 return NULL;
295 return ident;
298 /* vim: set ts=8 sw=8 noexpandtab: */