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.
15 #include "tig/parse.h"
19 parse_size(const char *text
)
26 while (isdigit(*text
))
27 size
= (size
* 10) + (*text
++ - '0');
33 * Parsing of ident lines.
37 parse_timesec(struct time
*time
, const char *sec
)
39 time
->sec
= (time_t) atol(sec
);
43 parse_timezone(struct time
*time
, const char *zone
)
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;
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
);
70 email
= chomp_string(nameend
+ 1);
72 name
= *email
? email
: unknown_ident
.name
;
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);
95 parse_number(const char **posref
, size_t *number
, size_t min
, size_t max
)
97 const char *pos
= *posref
;
100 pos
= strchr(pos
+ 1, ' ');
101 if (!pos
|| !isdigit(pos
[1]))
103 *number
= atoi(pos
+ 1);
104 if (*number
< min
|| *number
> max
)
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] != ' ')
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))
130 match_blame_header(const char *name
, char **line
)
132 size_t namelen
= strlen(name
);
133 bool matched
= !strncmp(name
, *line
, namelen
);
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
, '>');
154 commit
->author
= get_author(author
, line
);
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
)
169 string_copy_rev(commit
->parent_id
, line
);
171 commit
->parent_filename
= get_path(line
);
172 if (!commit
->parent_filename
)
175 } else if (match_blame_header("filename ", &line
)) {
176 commit
->filename
= get_path(line
);
188 parse_ulong(const char **pos_ptr
, unsigned long *value
, char skip
, bool optional
)
190 const char *start
= *pos_ptr
;
197 *value
= strtoul(start
, &end
, 10);
201 while (isspace(*end
))
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 '-'. */ ;
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);
228 parse_chunk_lineno(unsigned long *lineno
, const char *chunk
, int marker
)
230 struct chunk_header chunk_header
;
234 if (!parse_chunk_header(&chunk_header
, chunk
))
237 *lineno
= marker
== '-' ? chunk_header
.old
.position
: chunk_header
.new.position
;
249 DEFINE_STRING_MAP(path_cache
, struct path_entry
*, path
, 32)
251 /* Small cache to reduce memory consumption. No entries are ever
254 get_path(const char *path
)
256 struct path_entry
*entry
= string_map_get(&path_cache
, path
);
259 entry
= calloc(1, sizeof(*entry
) + strlen(path
));
260 if (!entry
|| !string_map_put(&path_cache
, path
, entry
)) {
264 strncpy(entry
->path
, path
, strlen(path
));
270 DEFINE_STRING_MAP(author_cache
, const struct ident
*, email
, 32)
272 /* Small author cache to reduce memory consumption. No entries
275 get_author(const char *name
, const char *email
)
277 struct ident
*ident
= string_map_get(&author_cache
, email
);
282 ident
= calloc(1, sizeof(*ident
));
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
);
298 /* vim: set ts=8 sw=8 noexpandtab: */