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.
15 #include "tig/parse.h"
18 parse_size(const char *text
)
25 while (isdigit(*text
))
26 size
= (size
* 10) + (*text
++ - '0');
32 * Parsing of ident lines.
36 parse_timesec(struct time
*time
, const char *sec
)
38 time
->sec
= (time_t) atol(sec
);
42 parse_timezone(struct time
*time
, const char *zone
)
46 tz
= ('0' - zone
[1]) * 60 * 60 * 10;
47 tz
+= ('0' - zone
[2]) * 60 * 60;
48 tz
+= ('0' - zone
[3]) * 60 * 10;
49 tz
+= ('0' - zone
[4]) * 60;
59 parse_author_line(char *ident
, const struct ident
**author
, struct time
*time
)
61 char *nameend
= strchr(ident
, '<');
62 char *emailend
= strchr(ident
, '>');
63 const char *name
, *email
= "";
65 if (nameend
&& emailend
)
66 *nameend
= *emailend
= 0;
67 name
= chomp_string(ident
);
69 email
= chomp_string(nameend
+ 1);
71 name
= *email
? email
: unknown_ident
.name
;
73 email
= *name
? name
: unknown_ident
.email
;
75 *author
= get_author(name
, email
);
77 /* Parse epoch and timezone */
78 if (time
&& emailend
&& emailend
[1] == ' ') {
79 char *secs
= emailend
+ 2;
80 char *zone
= strchr(secs
, ' ');
82 parse_timesec(time
, secs
);
84 if (zone
&& strlen(zone
) == STRING_SIZE(" +0700"))
85 parse_timezone(time
, zone
+ 1);
94 parse_number(const char **posref
, size_t *number
, size_t min
, size_t max
)
96 const char *pos
= *posref
;
99 pos
= strchr(pos
+ 1, ' ');
100 if (!pos
|| !isdigit(pos
[1]))
102 *number
= atoi(pos
+ 1);
103 if (*number
< min
|| *number
> max
)
111 parse_blame_header(struct blame_header
*header
, const char *text
, size_t max_lineno
)
113 const char *pos
= text
+ SIZEOF_REV
- 2;
115 if (strlen(text
) <= SIZEOF_REV
|| pos
[1] != ' ')
118 string_ncopy(header
->id
, text
, SIZEOF_REV
);
120 if (!parse_number(&pos
, &header
->orig_lineno
, 1, 9999999) ||
121 !parse_number(&pos
, &header
->lineno
, 1, max_lineno
) ||
122 !parse_number(&pos
, &header
->group
, 1, max_lineno
- header
->lineno
+ 1))
129 match_blame_header(const char *name
, char **line
)
131 size_t namelen
= strlen(name
);
132 bool matched
= !strncmp(name
, *line
, namelen
);
141 parse_blame_info(struct blame_commit
*commit
, char author
[SIZEOF_STR
], char *line
)
143 if (match_blame_header("author ", &line
)) {
144 string_ncopy_do(author
, SIZEOF_STR
, line
, strlen(line
));
146 } else if (match_blame_header("author-mail ", &line
)) {
147 char *end
= strchr(line
, '>');
153 commit
->author
= get_author(author
, line
);
156 } else if (match_blame_header("author-time ", &line
)) {
157 parse_timesec(&commit
->time
, line
);
159 } else if (match_blame_header("author-tz ", &line
)) {
160 parse_timezone(&commit
->time
, line
);
162 } else if (match_blame_header("summary ", &line
)) {
163 string_ncopy(commit
->title
, line
, strlen(line
));
165 } else if (match_blame_header("previous ", &line
)) {
166 if (strlen(line
) <= SIZEOF_REV
)
168 string_copy_rev(commit
->parent_id
, line
);
170 commit
->parent_filename
= get_path(line
);
171 if (!commit
->parent_filename
)
174 } else if (match_blame_header("filename ", &line
)) {
175 commit
->filename
= get_path(line
);
187 parse_ulong(const char **pos_ptr
, unsigned long *value
, const char *skip
)
189 const char *start
= *pos_ptr
;
192 if (!isdigit(*start
))
195 *value
= strtoul(start
, &end
, 10);
200 while (skip
&& *start
&& strchr(skip
, *start
))
207 parse_chunk_header(struct chunk_header
*header
, const char *line
)
209 memset(header
, 0, sizeof(*header
));
211 if (!prefixcmp(line
, "@@ -"))
212 line
+= STRING_SIZE("@@ -");
213 else if (!prefixcmp(line
, "@@@ -") &&
214 (line
= strchr(line
+ STRING_SIZE("@@@ -"), '-')))
220 return parse_ulong(&line
, &header
->old
.position
, ",") &&
221 parse_ulong(&line
, &header
->old
.lines
, " +") &&
222 parse_ulong(&line
, &header
->new.position
, ",") &&
223 parse_ulong(&line
, &header
->new.lines
, NULL
);
227 parse_chunk_lineno(unsigned long *lineno
, const char *chunk
, int marker
)
229 struct chunk_header chunk_header
;
233 if (!parse_chunk_header(&chunk_header
, chunk
))
236 *lineno
= marker
== '-' ? chunk_header
.old
.position
: chunk_header
.new.position
;
244 DEFINE_ALLOCATOR(realloc_paths
, const char *, 256)
246 /* Small cache to reduce memory consumption. It uses binary search to
247 * lookup or find place to position new entries. No entries are ever
250 get_path(const char *path
)
252 static const char **paths
;
253 static size_t paths_size
;
254 int from
= 0, to
= paths_size
- 1;
258 size_t pos
= (to
+ from
) / 2;
259 int cmp
= strcmp(path
, paths
[pos
]);
270 if (!realloc_paths(&paths
, paths_size
, 1))
272 entry
= strdup(path
);
276 memmove(paths
+ from
+ 1, paths
+ from
, (paths_size
- from
) * sizeof(*paths
));
283 DEFINE_ALLOCATOR(realloc_authors
, struct ident
*, 256)
285 /* Small author cache to reduce memory consumption. It uses binary
286 * search to lookup or find place to position new entries. No entries
289 get_author(const char *name
, const char *email
)
291 static struct ident
**authors
;
292 static size_t authors_size
;
293 int from
= 0, to
= authors_size
- 1;
297 size_t pos
= (to
+ from
) / 2;
298 int cmp
= strcmp(email
, authors
[pos
]->email
);
309 if (!realloc_authors(&authors
, authors_size
, 1))
311 ident
= calloc(1, sizeof(*ident
));
314 ident
->name
= strdup(name
);
315 ident
->email
= strdup(email
);
316 if (!ident
->name
|| !ident
->email
) {
317 free((void *) ident
->name
);
322 memmove(authors
+ from
+ 1, authors
+ from
, (authors_size
- from
) * sizeof(*authors
));
323 authors
[from
] = ident
;
329 /* vim: set ts=8 sw=8 noexpandtab: */