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.
16 #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
, const char *skip
)
190 const char *start
= *pos_ptr
;
193 if (!isdigit(*start
))
196 *value
= strtoul(start
, &end
, 10);
201 while (skip
&& *start
&& strchr(skip
, *start
))
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("@@@ -"), '-')))
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
);
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
;
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
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;
259 size_t pos
= (to
+ from
) / 2;
260 int cmp
= strcmp(path
, paths
[pos
]);
271 if (!realloc_paths(&paths
, paths_size
, 1))
273 entry
= strdup(path
);
277 memmove(paths
+ from
+ 1, paths
+ from
, (paths_size
- from
) * sizeof(*paths
));
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
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;
298 size_t pos
= (to
+ from
) / 2;
299 int cmp
= strcmp(email
, authors
[pos
]->email
);
310 if (!realloc_authors(&authors
, authors_size
, 1))
312 ident
= calloc(1, sizeof(*ident
));
315 ident
->name
= strdup(name
);
316 ident
->email
= strdup(email
);
317 if (!ident
->name
|| !ident
->email
) {
318 free((void *) ident
->name
);
323 memmove(authors
+ from
+ 1, authors
+ from
, (authors_size
- from
) * sizeof(*authors
));
324 authors
[from
] = ident
;
330 /* vim: set ts=8 sw=8 noexpandtab: */