summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-09-14 07:45:37 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-09-15 20:33:11 (UTC)
commita8305a9543969206aa7cec03948c5a19950eedb9 (patch) (unidiff)
tree8e2cf67ec72bc296b76a4fcb6db5ec8250f0a502
parentb28765135dd6f52635977454eaf95d0e6c7e7271 (diff)
downloadcgit-a8305a9543969206aa7cec03948c5a19950eedb9.zip
cgit-a8305a9543969206aa7cec03948c5a19950eedb9.tar.gz
cgit-a8305a9543969206aa7cec03948c5a19950eedb9.tar.bz2
parsing.c: be prepared for unexpected content in commit/tag objects
When parsing commits and tags cgit made too many assumptions about the formatting of said objects. This patch tries to make the code be more prepared to handle 'malformed' objects. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.h2
-rw-r--r--parsing.c159
2 files changed, 97 insertions, 64 deletions
diff --git a/cgit.h b/cgit.h
index 1615616..08fd95a 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,241 +1,241 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4 4
5#include <git-compat-util.h> 5#include <git-compat-util.h>
6#include <cache.h> 6#include <cache.h>
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <xdiff/xdiff.h> 18#include <xdiff/xdiff.h>
19#include <utf8.h> 19#include <utf8.h>
20 20
21 21
22/* 22/*
23 * Dateformats used on misc. pages 23 * Dateformats used on misc. pages
24 */ 24 */
25#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 25#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
26#define FMT_SHORTDATE "%Y-%m-%d" 26#define FMT_SHORTDATE "%Y-%m-%d"
27#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 27#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
28 28
29 29
30/* 30/*
31 * Limits used for relative dates 31 * Limits used for relative dates
32 */ 32 */
33#define TM_MIN 60 33#define TM_MIN 60
34#define TM_HOUR (TM_MIN * 60) 34#define TM_HOUR (TM_MIN * 60)
35#define TM_DAY (TM_HOUR * 24) 35#define TM_DAY (TM_HOUR * 24)
36#define TM_WEEK (TM_DAY * 7) 36#define TM_WEEK (TM_DAY * 7)
37#define TM_YEAR (TM_DAY * 365) 37#define TM_YEAR (TM_DAY * 365)
38#define TM_MONTH (TM_YEAR / 12.0) 38#define TM_MONTH (TM_YEAR / 12.0)
39 39
40 40
41/* 41/*
42 * Default encoding 42 * Default encoding
43 */ 43 */
44#define PAGE_ENCODING "UTF-8" 44#define PAGE_ENCODING "UTF-8"
45 45
46typedef void (*configfn)(const char *name, const char *value); 46typedef void (*configfn)(const char *name, const char *value);
47typedef void (*filepair_fn)(struct diff_filepair *pair); 47typedef void (*filepair_fn)(struct diff_filepair *pair);
48typedef void (*linediff_fn)(char *line, int len); 48typedef void (*linediff_fn)(char *line, int len);
49 49
50struct cgit_repo { 50struct cgit_repo {
51 char *url; 51 char *url;
52 char *name; 52 char *name;
53 char *path; 53 char *path;
54 char *desc; 54 char *desc;
55 char *owner; 55 char *owner;
56 char *defbranch; 56 char *defbranch;
57 char *group; 57 char *group;
58 char *module_link; 58 char *module_link;
59 char *readme; 59 char *readme;
60 char *clone_url; 60 char *clone_url;
61 int snapshots; 61 int snapshots;
62 int enable_log_filecount; 62 int enable_log_filecount;
63 int enable_log_linecount; 63 int enable_log_linecount;
64}; 64};
65 65
66struct cgit_repolist { 66struct cgit_repolist {
67 int length; 67 int length;
68 int count; 68 int count;
69 struct cgit_repo *repos; 69 struct cgit_repo *repos;
70}; 70};
71 71
72struct commitinfo { 72struct commitinfo {
73 struct commit *commit; 73 struct commit *commit;
74 char *author; 74 char *author;
75 char *author_email; 75 char *author_email;
76 unsigned long author_date; 76 unsigned long author_date;
77 char *committer; 77 char *committer;
78 char *committer_email; 78 char *committer_email;
79 unsigned long committer_date; 79 unsigned long committer_date;
80 char *subject; 80 char *subject;
81 char *msg; 81 char *msg;
82 char *msg_encoding; 82 char *msg_encoding;
83}; 83};
84 84
85struct taginfo { 85struct taginfo {
86 char *tagger; 86 char *tagger;
87 char *tagger_email; 87 char *tagger_email;
88 int tagger_date; 88 unsigned long tagger_date;
89 char *msg; 89 char *msg;
90}; 90};
91 91
92struct refinfo { 92struct refinfo {
93 const char *refname; 93 const char *refname;
94 struct object *object; 94 struct object *object;
95 union { 95 union {
96 struct taginfo *tag; 96 struct taginfo *tag;
97 struct commitinfo *commit; 97 struct commitinfo *commit;
98 }; 98 };
99}; 99};
100 100
101struct reflist { 101struct reflist {
102 struct refinfo **refs; 102 struct refinfo **refs;
103 int alloc; 103 int alloc;
104 int count; 104 int count;
105}; 105};
106 106
107struct cgit_query { 107struct cgit_query {
108 int has_symref; 108 int has_symref;
109 int has_sha1; 109 int has_sha1;
110 char *raw; 110 char *raw;
111 char *repo; 111 char *repo;
112 char *page; 112 char *page;
113 char *search; 113 char *search;
114 char *grep; 114 char *grep;
115 char *head; 115 char *head;
116 char *sha1; 116 char *sha1;
117 char *sha2; 117 char *sha2;
118 char *path; 118 char *path;
119 char *name; 119 char *name;
120 char *mimetype; 120 char *mimetype;
121 int ofs; 121 int ofs;
122}; 122};
123 123
124struct cgit_config { 124struct cgit_config {
125 char *agefile; 125 char *agefile;
126 char *cache_root; 126 char *cache_root;
127 char *clone_prefix; 127 char *clone_prefix;
128 char *css; 128 char *css;
129 char *favicon; 129 char *favicon;
130 char *footer; 130 char *footer;
131 char *index_header; 131 char *index_header;
132 char *index_info; 132 char *index_info;
133 char *logo; 133 char *logo;
134 char *logo_link; 134 char *logo_link;
135 char *module_link; 135 char *module_link;
136 char *repo_group; 136 char *repo_group;
137 char *robots; 137 char *robots;
138 char *root_title; 138 char *root_title;
139 char *root_desc; 139 char *root_desc;
140 char *root_readme; 140 char *root_readme;
141 char *script_name; 141 char *script_name;
142 char *virtual_root; 142 char *virtual_root;
143 int cache_size; 143 int cache_size;
144 int cache_dynamic_ttl; 144 int cache_dynamic_ttl;
145 int cache_max_create_time; 145 int cache_max_create_time;
146 int cache_repo_ttl; 146 int cache_repo_ttl;
147 int cache_root_ttl; 147 int cache_root_ttl;
148 int cache_static_ttl; 148 int cache_static_ttl;
149 int enable_index_links; 149 int enable_index_links;
150 int enable_log_filecount; 150 int enable_log_filecount;
151 int enable_log_linecount; 151 int enable_log_linecount;
152 int local_time; 152 int local_time;
153 int max_repo_count; 153 int max_repo_count;
154 int max_commit_count; 154 int max_commit_count;
155 int max_lock_attempts; 155 int max_lock_attempts;
156 int max_msg_len; 156 int max_msg_len;
157 int max_repodesc_len; 157 int max_repodesc_len;
158 int nocache; 158 int nocache;
159 int renamelimit; 159 int renamelimit;
160 int snapshots; 160 int snapshots;
161 int summary_branches; 161 int summary_branches;
162 int summary_log; 162 int summary_log;
163 int summary_tags; 163 int summary_tags;
164}; 164};
165 165
166struct cgit_page { 166struct cgit_page {
167 time_t modified; 167 time_t modified;
168 time_t expires; 168 time_t expires;
169 size_t size; 169 size_t size;
170 char *mimetype; 170 char *mimetype;
171 char *charset; 171 char *charset;
172 char *filename; 172 char *filename;
173 char *title; 173 char *title;
174}; 174};
175 175
176struct cgit_context { 176struct cgit_context {
177 struct cgit_query qry; 177 struct cgit_query qry;
178 struct cgit_config cfg; 178 struct cgit_config cfg;
179 struct cgit_repo *repo; 179 struct cgit_repo *repo;
180 struct cgit_page page; 180 struct cgit_page page;
181}; 181};
182 182
183struct cgit_snapshot_format { 183struct cgit_snapshot_format {
184 const char *suffix; 184 const char *suffix;
185 const char *mimetype; 185 const char *mimetype;
186 write_archive_fn_t write_func; 186 write_archive_fn_t write_func;
187 int bit; 187 int bit;
188}; 188};
189 189
190extern const char *cgit_version; 190extern const char *cgit_version;
191 191
192extern struct cgit_repolist cgit_repolist; 192extern struct cgit_repolist cgit_repolist;
193extern struct cgit_context ctx; 193extern struct cgit_context ctx;
194extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 194extern const struct cgit_snapshot_format cgit_snapshot_formats[];
195 195
196extern struct cgit_repo *cgit_add_repo(const char *url); 196extern struct cgit_repo *cgit_add_repo(const char *url);
197extern struct cgit_repo *cgit_get_repoinfo(const char *url); 197extern struct cgit_repo *cgit_get_repoinfo(const char *url);
198extern void cgit_repo_config_cb(const char *name, const char *value); 198extern void cgit_repo_config_cb(const char *name, const char *value);
199 199
200extern int chk_zero(int result, char *msg); 200extern int chk_zero(int result, char *msg);
201extern int chk_positive(int result, char *msg); 201extern int chk_positive(int result, char *msg);
202extern int chk_non_negative(int result, char *msg); 202extern int chk_non_negative(int result, char *msg);
203 203
204extern char *trim_end(const char *str, char c); 204extern char *trim_end(const char *str, char c);
205extern char *strlpart(char *txt, int maxlen); 205extern char *strlpart(char *txt, int maxlen);
206extern char *strrpart(char *txt, int maxlen); 206extern char *strrpart(char *txt, int maxlen);
207 207
208extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 208extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
209extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 209extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
210 int flags, void *cb_data); 210 int flags, void *cb_data);
211 211
212extern void *cgit_free_commitinfo(struct commitinfo *info); 212extern void *cgit_free_commitinfo(struct commitinfo *info);
213 213
214extern int cgit_diff_files(const unsigned char *old_sha1, 214extern int cgit_diff_files(const unsigned char *old_sha1,
215 const unsigned char *new_sha1, 215 const unsigned char *new_sha1,
216 linediff_fn fn); 216 linediff_fn fn);
217 217
218extern void cgit_diff_tree(const unsigned char *old_sha1, 218extern void cgit_diff_tree(const unsigned char *old_sha1,
219 const unsigned char *new_sha1, 219 const unsigned char *new_sha1,
220 filepair_fn fn, const char *prefix); 220 filepair_fn fn, const char *prefix);
221 221
222extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 222extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
223 223
224extern char *fmt(const char *format,...); 224extern char *fmt(const char *format,...);
225 225
226extern struct commitinfo *cgit_parse_commit(struct commit *commit); 226extern struct commitinfo *cgit_parse_commit(struct commit *commit);
227extern struct taginfo *cgit_parse_tag(struct tag *tag); 227extern struct taginfo *cgit_parse_tag(struct tag *tag);
228extern void cgit_parse_url(const char *url); 228extern void cgit_parse_url(const char *url);
229 229
230extern const char *cgit_repobasename(const char *reponame); 230extern const char *cgit_repobasename(const char *reponame);
231 231
232extern int cgit_parse_snapshots_mask(const char *str); 232extern int cgit_parse_snapshots_mask(const char *str);
233 233
234/* libgit.a either links against or compiles its own implementation of 234/* libgit.a either links against or compiles its own implementation of
235 * strcasestr(), and we'd like to reuse it. Simply re-declaring it 235 * strcasestr(), and we'd like to reuse it. Simply re-declaring it
236 * seems to do the trick. 236 * seems to do the trick.
237 */ 237 */
238extern char *strcasestr(const char *haystack, const char *needle); 238extern char *strcasestr(const char *haystack, const char *needle);
239 239
240 240
241#endif /* CGIT_H */ 241#endif /* CGIT_H */
diff --git a/parsing.c b/parsing.c
index 66e8b3d..c8f3048 100644
--- a/parsing.c
+++ b/parsing.c
@@ -1,208 +1,241 @@
1/* config.c: parsing of config files 1/* config.c: parsing of config files
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11/* 11/*
12 * url syntax: [repo ['/' cmd [ '/' path]]] 12 * url syntax: [repo ['/' cmd [ '/' path]]]
13 * repo: any valid repo url, may contain '/' 13 * repo: any valid repo url, may contain '/'
14 * cmd: log | commit | diff | tree | view | blob | snapshot 14 * cmd: log | commit | diff | tree | view | blob | snapshot
15 * path: any valid path, may contain '/' 15 * path: any valid path, may contain '/'
16 * 16 *
17 */ 17 */
18void cgit_parse_url(const char *url) 18void cgit_parse_url(const char *url)
19{ 19{
20 char *cmd, *p; 20 char *cmd, *p;
21 21
22 ctx.repo = NULL; 22 ctx.repo = NULL;
23 if (!url || url[0] == '\0') 23 if (!url || url[0] == '\0')
24 return; 24 return;
25 25
26 ctx.repo = cgit_get_repoinfo(url); 26 ctx.repo = cgit_get_repoinfo(url);
27 if (ctx.repo) { 27 if (ctx.repo) {
28 ctx.qry.repo = ctx.repo->url; 28 ctx.qry.repo = ctx.repo->url;
29 return; 29 return;
30 } 30 }
31 31
32 cmd = strchr(url, '/'); 32 cmd = strchr(url, '/');
33 while (!ctx.repo && cmd) { 33 while (!ctx.repo && cmd) {
34 cmd[0] = '\0'; 34 cmd[0] = '\0';
35 ctx.repo = cgit_get_repoinfo(url); 35 ctx.repo = cgit_get_repoinfo(url);
36 if (ctx.repo == NULL) { 36 if (ctx.repo == NULL) {
37 cmd[0] = '/'; 37 cmd[0] = '/';
38 cmd = strchr(cmd + 1, '/'); 38 cmd = strchr(cmd + 1, '/');
39 continue; 39 continue;
40 } 40 }
41 41
42 ctx.qry.repo = ctx.repo->url; 42 ctx.qry.repo = ctx.repo->url;
43 p = strchr(cmd + 1, '/'); 43 p = strchr(cmd + 1, '/');
44 if (p) { 44 if (p) {
45 p[0] = '\0'; 45 p[0] = '\0';
46 if (p[1]) 46 if (p[1])
47 ctx.qry.path = trim_end(p + 1, '/'); 47 ctx.qry.path = trim_end(p + 1, '/');
48 } 48 }
49 if (cmd[1]) 49 if (cmd[1])
50 ctx.qry.page = xstrdup(cmd + 1); 50 ctx.qry.page = xstrdup(cmd + 1);
51 return; 51 return;
52 } 52 }
53} 53}
54 54
55char *substr(const char *head, const char *tail) 55char *substr(const char *head, const char *tail)
56{ 56{
57 char *buf; 57 char *buf;
58 58
59 buf = xmalloc(tail - head + 1); 59 buf = xmalloc(tail - head + 1);
60 strncpy(buf, head, tail - head); 60 strncpy(buf, head, tail - head);
61 buf[tail - head] = '\0'; 61 buf[tail - head] = '\0';
62 return buf; 62 return buf;
63} 63}
64 64
65char *parse_user(char *t, char **name, char **email, unsigned long *date)
66{
67 char *p = t;
68 int mode = 1;
69
70 while (p && *p) {
71 if (mode == 1 && *p == '<') {
72 *name = substr(t, p - 1);
73 t = p;
74 mode++;
75 } else if (mode == 1 && *p == '\n') {
76 *name = substr(t, p);
77 p++;
78 break;
79 } else if (mode == 2 && *p == '>') {
80 *email = substr(t, p + 1);
81 t = p;
82 mode++;
83 } else if (mode == 2 && *p == '\n') {
84 *email = substr(t, p);
85 p++;
86 break;
87 } else if (mode == 3 && isdigit(*p)) {
88 *date = atol(p);
89 mode++;
90 } else if (*p == '\n') {
91 p++;
92 break;
93 }
94 p++;
95 }
96 return p;
97}
98
99const char *reencode(char **txt, const char *src_enc, const char *dst_enc)
100{
101 char *tmp;
102
103 if (!txt || !*txt || !src_enc || !dst_enc)
104 return *txt;
105
106 tmp = reencode_string(*txt, src_enc, dst_enc);
107 if (tmp) {
108 free(*txt);
109 *txt = tmp;
110 }
111 return *txt;
112}
113
65struct commitinfo *cgit_parse_commit(struct commit *commit) 114struct commitinfo *cgit_parse_commit(struct commit *commit)
66{ 115{
67 struct commitinfo *ret; 116 struct commitinfo *ret;
68 char *p = commit->buffer, *t = commit->buffer; 117 char *p = commit->buffer, *t = commit->buffer;
69 118
70 ret = xmalloc(sizeof(*ret)); 119 ret = xmalloc(sizeof(*ret));
71 ret->commit = commit; 120 ret->commit = commit;
72 ret->author = NULL; 121 ret->author = NULL;
73 ret->author_email = NULL; 122 ret->author_email = NULL;
74 ret->committer = NULL; 123 ret->committer = NULL;
75 ret->committer_email = NULL; 124 ret->committer_email = NULL;
76 ret->subject = NULL; 125 ret->subject = NULL;
77 ret->msg = NULL; 126 ret->msg = NULL;
78 ret->msg_encoding = NULL; 127 ret->msg_encoding = NULL;
79 128
80 if (p == NULL) 129 if (p == NULL)
81 return ret; 130 return ret;
82 131
83 if (strncmp(p, "tree ", 5)) 132 if (strncmp(p, "tree ", 5))
84 die("Bad commit: %s", sha1_to_hex(commit->object.sha1)); 133 die("Bad commit: %s", sha1_to_hex(commit->object.sha1));
85 else 134 else
86 p += 46; // "tree " + hex[40] + "\n" 135 p += 46; // "tree " + hex[40] + "\n"
87 136
88 while (!strncmp(p, "parent ", 7)) 137 while (!strncmp(p, "parent ", 7))
89 p += 48; // "parent " + hex[40] + "\n" 138 p += 48; // "parent " + hex[40] + "\n"
90 139
91 if (!strncmp(p, "author ", 7)) { 140 if (p && !strncmp(p, "author ", 7)) {
92 p += 7; 141 p = parse_user(p + 7, &ret->author, &ret->author_email,
93 t = strchr(p, '<') - 1; 142 &ret->author_date);
94 ret->author = substr(p, t);
95 p = t;
96 t = strchr(t, '>') + 1;
97 ret->author_email = substr(p, t);
98 ret->author_date = atol(t+1);
99 p = strchr(t, '\n') + 1;
100 } 143 }
101 144
102 if (!strncmp(p, "committer ", 9)) { 145 if (p && !strncmp(p, "committer ", 9)) {
103 p += 9; 146 p = parse_user(p + 9, &ret->committer, &ret->committer_email,
104 t = strchr(p, '<') - 1; 147 &ret->committer_date);
105 ret->committer = substr(p, t);
106 p = t;
107 t = strchr(t, '>') + 1;
108 ret->committer_email = substr(p, t);
109 ret->committer_date = atol(t+1);
110 p = strchr(t, '\n') + 1;
111 } 148 }
112 149
113 if (!strncmp(p, "encoding ", 9)) { 150 if (p && !strncmp(p, "encoding ", 9)) {
114 p += 9; 151 p += 9;
115 t = strchr(p, '\n') + 1; 152 t = strchr(p, '\n');
116 ret->msg_encoding = substr(p, t); 153 if (t) {
117 p = t; 154 ret->msg_encoding = substr(p, t + 1);
118 } else 155 p = t + 1;
119 ret->msg_encoding = xstrdup(PAGE_ENCODING); 156 }
157 }
120 158
121 while (*p && (*p != '\n')) 159 // skip unknown header fields
122 p = strchr(p, '\n') + 1; // skip unknown header fields 160 while (p && *p && (*p != '\n')) {
161 p = strchr(p, '\n');
162 if (p)
163 p++;
164 }
123 165
124 while (*p == '\n') 166 // skip empty lines between headers and message
125 p = strchr(p, '\n') + 1; 167 while (p && *p == '\n')
168 p++;
169
170 if (!p)
171 return ret;
126 172
127 t = strchr(p, '\n'); 173 t = strchr(p, '\n');
128 if (t) { 174 if (t) {
129 if (*t == '\0') 175 ret->subject = substr(p, t);
130 ret->subject = "** empty **";
131 else
132 ret->subject = substr(p, t);
133 p = t + 1; 176 p = t + 1;
134 177
135 while (*p == '\n') 178 while (p && *p == '\n') {
136 p = strchr(p, '\n') + 1; 179 p = strchr(p, '\n');
137 ret->msg = xstrdup(p); 180 if (p)
138 } else 181 p++;
139 ret->subject = substr(p, p+strlen(p));
140
141 if(strcmp(ret->msg_encoding, PAGE_ENCODING)) {
142 t = reencode_string(ret->subject, PAGE_ENCODING,
143 ret->msg_encoding);
144 if(t) {
145 free(ret->subject);
146 ret->subject = t;
147 } 182 }
183 if (p)
184 ret->msg = xstrdup(p);
185 } else
186 ret->subject = xstrdup(p);
148 187
149 t = reencode_string(ret->msg, PAGE_ENCODING, 188 if (ret->msg_encoding) {
150 ret->msg_encoding); 189 reencode(&ret->subject, PAGE_ENCODING, ret->msg_encoding);
151 if(t) { 190 reencode(&ret->msg, PAGE_ENCODING, ret->msg_encoding);
152 free(ret->msg);
153 ret->msg = t;
154 }
155 } 191 }
156 192
157 return ret; 193 return ret;
158} 194}
159 195
160 196
161struct taginfo *cgit_parse_tag(struct tag *tag) 197struct taginfo *cgit_parse_tag(struct tag *tag)
162{ 198{
163 void *data; 199 void *data;
164 enum object_type type; 200 enum object_type type;
165 unsigned long size; 201 unsigned long size;
166 char *p, *t; 202 char *p;
167 struct taginfo *ret; 203 struct taginfo *ret;
168 204
169 data = read_sha1_file(tag->object.sha1, &type, &size); 205 data = read_sha1_file(tag->object.sha1, &type, &size);
170 if (!data || type != OBJ_TAG) { 206 if (!data || type != OBJ_TAG) {
171 free(data); 207 free(data);
172 return 0; 208 return 0;
173 } 209 }
174 210
175 ret = xmalloc(sizeof(*ret)); 211 ret = xmalloc(sizeof(*ret));
176 ret->tagger = NULL; 212 ret->tagger = NULL;
177 ret->tagger_email = NULL; 213 ret->tagger_email = NULL;
178 ret->tagger_date = 0; 214 ret->tagger_date = 0;
179 ret->msg = NULL; 215 ret->msg = NULL;
180 216
181 p = data; 217 p = data;
182 218
183 while (p && *p) { 219 while (p && *p) {
184 if (*p == '\n') 220 if (*p == '\n')
185 break; 221 break;
186 222
187 if (!strncmp(p, "tagger ", 7)) { 223 if (!strncmp(p, "tagger ", 7)) {
188 p += 7; 224 p = parse_user(p + 7, &ret->tagger, &ret->tagger_email,
189 t = strchr(p, '<') - 1; 225 &ret->tagger_date);
190 ret->tagger = substr(p, t); 226 } else {
191 p = t; 227 p = strchr(p, '\n');
192 t = strchr(t, '>') + 1; 228 if (p)
193 ret->tagger_email = substr(p, t); 229 p++;
194 ret->tagger_date = atol(t+1);
195 } 230 }
196 p = strchr(p, '\n') + 1;
197 } 231 }
198 232
199 while (p && *p && (*p != '\n')) 233 // skip empty lines between headers and message
200 p = strchr(p, '\n') + 1; // skip unknown tag fields 234 while (p && *p == '\n')
235 p++;
201 236
202 while (p && (*p == '\n'))
203 p = strchr(p, '\n') + 1;
204 if (p && *p) 237 if (p && *p)
205 ret->msg = xstrdup(p); 238 ret->msg = xstrdup(p);
206 free(data); 239 free(data);
207 return ret; 240 return ret;
208} 241}