ident.c: add split_ident_line() to parse formatted ident line

The commit formatting logic format_person_part() in pretty.c implements the logic to split an author/committer ident line into its parts, intermixed with logic to compute its output using these piece it computes. Separate the former out to a helper function split_ident_line() so that other codepath can use the same logic, and rewrite the function using the helper function. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Mar 11, 2012 at 01:25 UTC 4b340cfab9c7a18e39bc531d6a6ffaffdf95f62d
3 files changed +104 -44
cache.h
+16
@@ -928,6 +928,22 @@ extern const char *fmt_name(const char *name, const char *email);
928 extern const char *git_editor(void);
929 extern const char *git_pager(int stdout_is_tty);
930
931 +struct ident_split {
932 + const char *name_begin;
933 + const char *name_end;
934 + const char *mail_begin;
935 + const char *mail_end;
936 + const char *date_begin;
937 + const char *date_end;
938 + const char *tz_begin;
939 + const char *tz_end;
940 +};
941 +/*
942 + * Signals an success with 0, but time part of the result may be NULL
943 + * if the input lacks timestamp and zone
944 + */
945 +extern int split_ident_line(struct ident_split *, const char *, int);
946 +
947 struct checkout {
948 const char *base_dir;
949 int base_dir_len;
ident.c
+68
@@ -220,6 +220,74 @@ static int copy(char *buf, size_t size, int offset, const char *src)
220 return offset;
221 }
222
223 +/*
224 + * Reverse of fmt_ident(); given an ident line, split the fields
225 + * to allow the caller to parse it.
226 + * Signal a success by returning 0, but date/tz fields of the result
227 + * can still be NULL if the input line only has the name/email part
228 + * (e.g. reading from a reflog entry).
229 + */
230 +int split_ident_line(struct ident_split *split, const char *line, int len)
231 +{
232 + const char *cp;
233 + size_t span;
234 + int status = -1;
235 +
236 + memset(split, 0, sizeof(*split));
237 +
238 + split->name_begin = line;
239 + for (cp = line; *cp && cp < line + len; cp++)
240 + if (*cp == '<') {
241 + split->mail_begin = cp + 1;
242 + break;
243 + }
244 + if (!split->mail_begin)
245 + return status;
246 +
247 + for (cp = split->mail_begin - 2; line < cp; cp--)
248 + if (!isspace(*cp)) {
249 + split->name_end = cp + 1;
250 + break;
251 + }
252 + if (!split->name_end)
253 + return status;
254 +
255 + for (cp = split->mail_begin; cp < line + len; cp++)
256 + if (*cp == '>') {
257 + split->mail_end = cp;
258 + break;
259 + }
260 + if (!split->mail_end)
261 + return status;
262 +
263 + for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
264 + ;
265 + if (line + len <= cp)
266 + goto person_only;
267 + split->date_begin = cp;
268 + span = strspn(cp, "0123456789");
269 + if (!span)
270 + goto person_only;
271 + split->date_end = split->date_begin + span;
272 + for (cp = split->date_end; cp < line + len && isspace(*cp); cp++)
273 + ;
274 + if (line + len <= cp || (*cp != '+' && *cp != '-'))
275 + goto person_only;
276 + split->tz_begin = cp;
277 + span = strspn(cp + 1, "0123456789");
278 + if (!span)
279 + goto person_only;
280 + split->tz_end = split->tz_begin + 1 + span;
281 + return 0;
282 +
283 +person_only:
284 + split->date_begin = NULL;
285 + split->date_end = NULL;
286 + split->tz_begin = NULL;
287 + split->tz_end = NULL;
288 + return 0;
289 +}
290 +
291 static const char *env_hint =
292 "\n"
293 "*** Please tell me who you are.\n"
pretty.c
+20 -44
@@ -531,41 +531,24 @@ static size_t format_person_part(struct strbuf *sb, char part,
531 {
532 /* currently all placeholders have same length */
533 const int placeholder_len = 2;
534 - int start, end, tz = 0;
534 + int tz;
535 unsigned long date = 0;
536 - char *ep;
537 - const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len;
536 char person_name[1024];
537 char person_mail[1024];
538 + struct ident_split s;
539 + const char *name_start, *name_end, *mail_start, *mail_end;
540
541 - /* advance 'end' to point to email start delimiter */
542 - for (end = 0; end < len && msg[end] != '<'; end++)
543 - ; /* do nothing */
544 -
545 - /*
546 - * When end points at the '<' that we found, it should have
547 - * matching '>' later, which means 'end' must be strictly
548 - * below len - 1.
549 - */
550 - if (end >= len - 2)
541 + if (split_ident_line(&s, msg, len) < 0)
542 goto skip;
543
553 - /* Seek for both name and email part */
554 - name_start = msg;
555 - name_end = msg+end;
556 - while (name_end > name_start && isspace(*(name_end-1)))
557 - name_end--;
558 - mail_start = msg+end+1;
559 - mail_end = mail_start;
560 - while (mail_end < msg_end && *mail_end != '>')
561 - mail_end++;
562 - if (mail_end == msg_end)
563 - goto skip;
564 - end = mail_end-msg;
544 + name_start = s.name_begin;
545 + name_end = s.name_end;
546 + mail_start = s.mail_begin;
547 + mail_end = s.mail_end;
548
549 if (part == 'N' || part == 'E') { /* mailmap lookup */
567 - strlcpy(person_name, name_start, name_end-name_start+1);
568 - strlcpy(person_mail, mail_start, mail_end-mail_start+1);
550 + strlcpy(person_name, name_start, name_end - name_start + 1);
551 + strlcpy(person_mail, mail_start, mail_end - mail_start + 1);
552 mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
553 name_start = person_name;
554 name_end = name_start + strlen(person_name);
@@ -581,28 +564,20 @@ static size_t format_person_part(struct strbuf *sb, char part,
564 return placeholder_len;
565 }
566
584 - /* advance 'start' to point to date start delimiter */
585 - for (start = end + 1; start < len && isspace(msg[start]); start++)
586 - ; /* do nothing */
587 - if (start >= len)
588 - goto skip;
589 - date = strtoul(msg + start, &ep, 10);
590 - if (msg + start == ep)
567 + if (!s.date_begin)
568 goto skip;
569
570 + date = strtoul(s.date_begin, NULL, 10);
571 +
572 if (part == 't') { /* date, UNIX timestamp */
594 - strbuf_add(sb, msg + start, ep - (msg + start));
573 + strbuf_add(sb, s.date_begin, s.date_end - s.date_begin);
574 return placeholder_len;
575 }
576
577 /* parse tz */
599 - for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
600 - ; /* do nothing */
601 - if (start + 1 < len) {
602 - tz = strtoul(msg + start + 1, NULL, 10);
603 - if (msg[start] == '-')
604 - tz = -tz;
605 - }
578 + tz = strtoul(s.tz_begin + 1, NULL, 10);
579 + if (*s.tz_begin == '-')
580 + tz = -tz;
581
582 switch (part) {
583 case 'd': /* date */
@@ -621,8 +596,9 @@ static size_t format_person_part(struct strbuf *sb, char part,
596
597 skip:
598 /*
624 - * bogus commit, 'sb' cannot be updated, but we still need to
625 - * compute a valid return value.
599 + * reading from either a bogus commit, or a reflog entry with
600 + * %gn, %ge, etc.; 'sb' cannot be updated, but we still need
601 + * to compute a valid return value.
602 */
603 if (part == 'n' || part == 'e' || part == 't' || part == 'd'
604 || part == 'D' || part == 'r' || part == 'i')