libgit-thin: git_commit_committer(): avoid malloc() & memcpy()
[git/libgit-gsoc.git] / libgit-thin / ltcommit.c
blob40df535b5b5759eb162bd167e3eebc7808476837
1 /**
2 * @file
3 *
4 * Commit handling functions.
6 * The functions implemented in this file retrieve all kinds of
7 * information from commits.
8 *
9 * Luiz Fernando N. Capitulino
10 * <lcapitulino@gmail.com>
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <time.h>
17 #include <cache.h>
18 #include <commit.h>
20 #include "ltcommit.h"
22 /**< Commit structure */
23 struct git_commit {
24 struct commit *commit;
25 char *author_name;
26 size_t author_name_len;
27 char *author_email;
28 size_t author_email_len;
29 int author_date;
30 int author_tz;
31 time_t author_time;
32 char *committer_name;
33 size_t committer_name_len;
34 char *committer_email;
35 int committer_date;
36 int committer_tz;
37 time_t committer_time;
38 char *message;
39 int tree_id;
40 unsigned char tree_sha1[20];
43 static char *commit_info_alloc(const char *src, size_t len)
45 char *p;
47 p = malloc(len + 1);
48 if (!p)
49 return NULL;
51 memcpy(p, src, len);
52 p[len] = '\0';
54 return p;
57 static char *commit_strstr(const struct git_commit *commit,
58 const char *needle)
60 char *p, *endp;
62 if (!commit->commit)
63 return NULL;
65 p = strstr(commit->commit->buffer, needle);
66 if (!p)
67 return NULL;
69 endp = strstr(commit->commit->buffer, "\n\n");
70 if (!endp)
71 endp = commit->commit->buffer + strlen(commit->commit->buffer);
73 return (p < endp) ? p : NULL;
76 static char *email_line_strchr(char *p, int c)
78 while (*p && *p != c && *p != '\n')
79 p++;
81 return (*p != c) ? NULL : p;
84 static inline char *email_line_start(char *p)
86 return email_line_strchr(p, '<');
89 static inline char *email_line_end(char *p)
91 return email_line_strchr(p, '>');
94 static inline char *tz_start(char *p)
96 return email_line_strchr(p, ' ');
99 static inline int check_strtoul_ret(unsigned long ret)
101 if ((!ret || ret == ULONG_MAX) && errno != 0)
102 return -1;
103 return 0;
106 static inline int check_strtol_ret(long ret)
108 if ((ret == LONG_MIN || ret == LONG_MAX) && errno != 0)
109 return -1;
110 return 0;
114 * Get the commit's message.
116 * \param commit git_commit structure to get the message from
118 * \return A pointer to the commit's message on success,
119 * NULL otherwise.
121 const char *git_commit_message(struct git_commit *commit)
123 if (!commit)
124 goto out_err;
126 if (!commit->message) {
127 char *p;
129 if (!commit->commit)
130 goto out_err;
132 p = strstr(commit->commit->buffer, "\n\n");
133 if (!p) {
134 /* empty message (probably) */
135 errno = 0;
136 return NULL;
139 p += 2;
140 if (!p)
141 goto out_err;
143 commit->message = commit_info_alloc(p, strlen(p));
146 return commit->message;
148 out_err:
149 errno = EINVAL;
150 return NULL;
154 * Get the commit's committer email.
156 * \param commit git_commit structure to get the committer's email
157 * from
159 * \return A pointer to the commit's committer email on success,
160 * NULL otherwise.
162 const char *git_commit_committer_email(struct git_commit *commit)
164 if (!commit)
165 goto out_err;
167 if (!commit->committer_email) {
168 char *p, *endp;
170 p = commit_strstr(commit, "committer ");
171 if (!p)
172 goto out_err;
174 p = email_line_start(p);
175 if (!p)
176 goto out_err;
178 endp = email_line_end(p);
179 if (!endp)
180 goto out_err;
182 ++p;
183 commit->committer_email = commit_info_alloc(p, endp - p);
186 return commit->committer_email;
188 out_err:
189 errno = EINVAL;
190 return NULL;
194 * Get the commit's committer name.
196 * \param commit git_commit structure to get the committer's name
197 * from
198 * \param ret pointer to return the committer's name into
199 * \param len committer's name length
201 * \return 0 on success, NULL otherwise
203 int git_commit_committer_name(struct git_commit *commit,
204 const char **ret, size_t *len)
206 if (!commit || !ret)
207 goto out_err;
209 if (!commit->committer_name) {
210 char *p, *endp;
212 p = commit_strstr(commit, "committer ");
213 if (!p)
214 goto out_err;
216 endp = email_line_start(p);
217 if (!endp)
218 goto out_err;
220 --endp;
221 if (*endp != ' ')
222 goto out_err;
224 p += 10;
225 commit->committer_name = p;
226 commit->committer_name_len = endp - p;
229 *ret = commit->committer_name;
230 if (len)
231 *len = commit->committer_name_len;
233 return 0;
235 out_err:
236 errno = EINVAL;
237 return -1;
241 * Get the commit's (committer) date
243 * \param commit git_commit structure to get the (committer's) date from
244 * \param com_time return the date timestamp as seconds since the
245 * epoch, UTC (might be NULL)
246 * \param com_tz return the timezone information as the number of
247 * hours and minutes offset from UTC (might be NULL)
249 * \return 0 on succes, -1 otherwise
251 int git_commit_committer_date(struct git_commit *commit,
252 time_t *com_time, int *com_tz)
254 if (!commit)
255 goto out_err;
257 if (!com_time && !com_tz)
258 goto out_err;
260 if (!commit->committer_date) {
261 int err;
262 char *p;
264 p = commit_strstr(commit, "committer ");
265 if (!p)
266 goto out_err;
268 p = email_line_end(p);
269 if (!p)
270 goto out_err;
272 ++p;
273 if (*p != ' ')
274 goto out_err;
276 errno = 0;
277 commit->committer_time = (time_t) strtoul(++p, NULL, 10);
278 err = check_strtoul_ret(commit->committer_time);
279 if (err)
280 return -1;
282 p = tz_start(p);
283 if (!p)
284 goto out_err;
286 errno = 0;
287 commit->committer_tz = strtol(++p, NULL, 10);
288 err = check_strtol_ret(commit->committer_tz);
289 if (err)
290 return -1;
292 commit->committer_date = 1;
295 if (com_time)
296 *com_time = commit->committer_time;
298 if (com_tz)
299 *com_tz = commit->committer_tz;
301 return 0;
303 out_err:
304 errno = EINVAL;
305 return -1;
309 * Get the commit's author email.
311 * \param commit git_commit structure to get the author's email
312 * from
314 * \return A pointer to the commit's author's email on success,
315 * NULL otherwise.
317 int git_commit_author_email(struct git_commit *commit,
318 const char **ret, size_t *len)
320 if (!commit)
321 goto out_err;
323 if (!commit->author_email) {
324 char *p, *endp;
326 p = commit_strstr(commit, "author ");
327 if (!p)
328 goto out_err;
330 p = email_line_start(p);
331 if (!p)
332 goto out_err;
334 endp = email_line_end(p);
335 if (!endp)
336 goto out_err;
338 commit->author_email = ++p;
339 commit->author_email_len = endp - p;
342 *ret = commit->author_email;
343 if (len)
344 *len = commit->author_email_len;
346 return 0;
348 out_err:
349 errno = EINVAL;
350 return -1;
354 * Get the commit's (author) date
356 * \param commit git_commit structure to get the (author's) date from
357 * \param com_time return the date timestamp as seconds since the
358 * epoch, UTC (might be NULL)
359 * \param com_tz return the timezone information as the number of
360 * hours and minutes offset from UTC (might be NULL)
362 * \return 0 on succes, -1 otherwise
364 int git_commit_author_date(struct git_commit *commit,
365 time_t *com_time, int *com_tz)
367 if (!commit)
368 goto out_err;
370 if (!com_time && !com_tz)
371 goto out_err;
373 if (!commit->author_date) {
374 int err;
375 char *p;
377 p = commit_strstr(commit, "author ");
378 if (!p)
379 goto out_err;
381 p = email_line_end(p);
382 if (!p)
383 goto out_err;
385 ++p;
386 if (*p != ' ')
387 goto out_err;
389 errno = 0;
390 commit->author_time = (time_t) strtoul(++p, NULL, 10);
391 err = check_strtoul_ret(commit->author_time);
392 if (err)
393 return -1;
395 p = tz_start(p);
396 if (!p)
397 goto out_err;
399 errno = 0;
400 commit->author_tz = strtol(++p, NULL, 10);
401 err = check_strtol_ret(commit->author_tz);
402 if (err)
403 return -1;
405 commit->author_date = 1;
408 if (com_time)
409 *com_time = commit->author_time;
411 if (com_tz)
412 *com_tz = commit->author_tz;
414 return 0;
416 out_err:
417 errno = EINVAL;
418 return -1;
422 * Get the commit's author name.
424 * \param commit git_commit structure to get the author's name
425 * from
426 * \param ret pointer to return the author's name into
427 * \param len author's name length
429 * \return 0 on success, NULL otherwise
431 int git_commit_author_name(struct git_commit *commit,
432 const char **ret, size_t *len)
434 if (!commit || !ret)
435 goto out_err;
437 if (!commit->author_name) {
438 char *p, *endp;
440 p = commit_strstr(commit, "author ");
441 if (!p)
442 goto out_err;
444 endp = email_line_start(p);
445 if (!endp)
446 goto out_err;
448 --endp;
449 if (*endp != ' ')
450 goto out_err;
452 p += 7;
453 commit->author_name = p;
454 commit->author_name_len = endp - p;
457 *ret = commit->author_name;
458 if (len)
459 *len = commit->author_name_len;
461 return 0;
463 out_err:
464 errno = EINVAL;
465 return -1;
469 * Get the commit's SHA1.
471 * \param commit git_commit structure to get the commit's SHA1 from
472 * \param sha1 pointer to an 20-byte array to return the SHA1 into
474 * \return 0 on success, -1 otherwise
476 int git_commit_id(struct git_commit *commit, unsigned char *sha1)
478 if (!commit || !commit->commit || !sha1) {
479 errno = EINVAL;
480 return -1;
483 memcpy(sha1, commit->commit->object.sha1, 20);
484 return 0;
488 * Get the commit's tree SHA1.
490 * \param commit git_commit structure to get the tree SHA1 from
491 * \param sha1 pointer to an 20-byte array to return the SHA1 into
493 * \return 0 on success, -1 otherwise
495 int git_commit_tree(struct git_commit *commit, unsigned char *sha1)
497 if (!commit || !sha1)
498 goto out_err;
500 if (!commit->tree_id) {
501 char *p;
502 int err;
504 p = commit_strstr(commit, "tree ");
505 if (!p)
506 goto out_err;
508 p += 5;
509 if (!p)
510 goto out_err;
512 err = get_sha1_hex(p, commit->tree_sha1);
513 if (err)
514 return -1;
516 commit->tree_id = 1;
519 memcpy(sha1, commit->tree_sha1, 20);
520 return 0;
522 out_err:
523 errno = EINVAL;
524 return -1;
528 * Get the commit's buffer raw data.
530 * \param commit git_commit structure to get the raw buffer
531 * from
533 * \return A pointer to the commit's raw buffer (if any).
535 const char *git_commit_raw(struct git_commit *commit)
537 if (!commit || !commit->commit) {
538 errno = EINVAL;
539 return NULL;
542 return commit->commit->buffer;
546 * Free all the memory allocated by the git_commit structure's
547 * members.
549 * This is a low-level function, only use it if you know
550 * what you're doing.
552 * \param commit git_commit structure to have the contents
553 * freed
555 void __git_commit_free(struct git_commit *commit)
557 if (!commit->commit)
558 return;
560 free(commit->commit->buffer);
561 commit->commit->buffer = NULL;
563 free(commit->committer_email);
564 free(commit->message);
568 * Free all the memory allocated by a git_commit structure.
570 * \param commit git_commit structure to be freed.
572 void git_commit_free(struct git_commit *commit)
574 if (commit) {
575 __git_commit_free(commit);
576 free(commit);
581 * Initialize a git_commit structure.
583 * This is a low-level function, only use it if you know
584 * what you're doing.
586 * \param commit git_commit structure to be initialized
588 void __git_commit_init(struct git_commit *commit)
590 commit->commit = NULL;
591 commit->author_name = NULL;
592 commit->author_email = NULL;
593 commit->author_date = 0;
594 commit->committer_name = NULL;
595 commit->committer_email = NULL;
596 commit->committer_date = 0;
597 commit->message = NULL;
598 commit->tree_id = 0;
602 * Allocate and initialize a git_commit structure.
604 * Should be called before using git_revlist_next().
606 * \return A pointer to an allocated git_commit structure
607 * on success, NULL otherwise.
609 struct git_commit *git_commit_init(void)
611 struct git_commit *commit;
613 commit = malloc(sizeof(*commit));
614 if (!commit)
615 return NULL;
617 __git_commit_init(commit);
618 return commit;
622 * Compare two commits.
624 * \param a git_commit structure to compare with
625 * \param b git_commit structure to compare with
627 * \return 1 if commits are equal, 0 if they don't and -1 on
628 * error.
630 int git_commit_equal(const struct git_commit *a,const struct git_commit *b)
632 if (!a || !b) {
633 errno = EINVAL;
634 return -1;
637 return (a->commit == b->commit);
641 * Get the raw GIT commit object from a git_commit
642 * structure.
644 * This is a low-level function, only use it if you know
645 * what you're doing.
647 * \param commit git_commit structure to get the commit object
648 * from
650 * \return A pointer to the raw GIT commit object if any,
651 * NULL otherwise.
653 struct commit *__git_commit_obj(struct git_commit *commit)
655 return commit->commit;
659 * Set the git_commit structure's (raw) GIT commit object
660 * contents.
662 * This is a low-level function, only use it if you know
663 * what you're doing.
665 * \param commit git_commit structure to set the raw GIT
666 * commit object
667 * \param new The raw GIT commit object to set into the git_commit
668 * structure
670 void __git_commit_obj_set(struct git_commit *commit, struct commit *new)
672 commit->commit = new;
676 * Return a filled git_commit structure from a given commit's SHA1
678 * \param sha1 commit's SHA1 to be read
680 * \returns git_commit structure on success, NULL otherwise.
682 struct git_commit *git_commit_lookup(const unsigned char *sha1)
684 struct git_commit *commit;
686 if (!sha1) {
687 errno = EINVAL;
688 return NULL;
691 commit = git_commit_init();
692 if (!commit)
693 return NULL;
695 commit->commit = lookup_commit_reference_gently(sha1, 1);
696 if (!commit->commit) {
697 git_commit_free(commit);
698 return NULL;
701 return commit;