shortlog: do not crash on parsing "[PATCH"
[git/jnareb-git.git] / builtin-shortlog.c
blob26212b009c6b1bb1a17dacc174d2e59d0dc54ef1
1 #include "builtin.h"
2 #include "cache.h"
3 #include "commit.h"
4 #include "diff.h"
5 #include "path-list.h"
6 #include "revision.h"
7 #include <string.h>
9 static const char shortlog_usage[] =
10 "git-shortlog [-n] [-s] [<commit-id>... ]\n";
12 static int compare_by_number(const void *a1, const void *a2)
14 const struct path_list_item *i1 = a1, *i2 = a2;
15 const struct path_list *l1 = i1->util, *l2 = i2->util;
17 if (l1->nr < l2->nr)
18 return -1;
19 else if (l1->nr == l2->nr)
20 return 0;
21 else
22 return +1;
25 static struct path_list_item mailmap_list[] = {
26 { "R.Marek@sh.cvut.cz", (void*)"Rudolf Marek" },
27 { "Ralf.Wildenhues@gmx.de", (void*)"Ralf Wildenhues" },
28 { "aherrman@de.ibm.com", (void*)"Andreas Herrmann" },
29 { "akpm@osdl.org", (void*)"Andrew Morton" },
30 { "andrew.vasquez@qlogic.com", (void*)"Andrew Vasquez" },
31 { "aquynh@gmail.com", (void*)"Nguyen Anh Quynh" },
32 { "axboe@suse.de", (void*)"Jens Axboe" },
33 { "blaisorblade@yahoo.it", (void*)"Paolo 'Blaisorblade' Giarrusso" },
34 { "bunk@stusta.de", (void*)"Adrian Bunk" },
35 { "domen@coderock.org", (void*)"Domen Puncer" },
36 { "dougg@torque.net", (void*)"Douglas Gilbert" },
37 { "dwmw2@shinybook.infradead.org", (void*)"David Woodhouse" },
38 { "ecashin@coraid.com", (void*)"Ed L Cashin" },
39 { "felix@derklecks.de", (void*)"Felix Moeller" },
40 { "fzago@systemfabricworks.com", (void*)"Frank Zago" },
41 { "gregkh@suse.de", (void*)"Greg Kroah-Hartman" },
42 { "hch@lst.de", (void*)"Christoph Hellwig" },
43 { "htejun@gmail.com", (void*)"Tejun Heo" },
44 { "jejb@mulgrave.(none)", (void*)"James Bottomley" },
45 { "jejb@titanic.il.steeleye.com", (void*)"James Bottomley" },
46 { "jgarzik@pretzel.yyz.us", (void*)"Jeff Garzik" },
47 { "johnpol@2ka.mipt.ru", (void*)"Evgeniy Polyakov" },
48 { "kay.sievers@vrfy.org", (void*)"Kay Sievers" },
49 { "minyard@acm.org", (void*)"Corey Minyard" },
50 { "mshah@teja.com", (void*)"Mitesh shah" },
51 { "pj@ludd.ltu.se", (void*)"Peter A Jonsson" },
52 { "rmps@joel.ist.utl.pt", (void*)"Rui Saraiva" },
53 { "santtu.hyrkko@gmail.com", (void*)"Santtu Hyrkk\e,Av\e(B" },
54 { "simon@thekelleys.org.uk", (void*)"Simon Kelley" },
55 { "ssant@in.ibm.com", (void*)"Sachin P Sant" },
56 { "terra@gnome.org", (void*)"Morten Welinder" },
57 { "tony.luck@intel.com", (void*)"Tony Luck" },
58 { "welinder@anemone.rentec.com", (void*)"Morten Welinder" },
59 { "welinder@darter.rentec.com", (void*)"Morten Welinder" },
60 { "welinder@troll.com", (void*)"Morten Welinder" }
63 static struct path_list mailmap = {
64 mailmap_list,
65 sizeof(mailmap_list) / sizeof(struct path_list_item), 0, 0
68 static int map_email(char *email, char *name, int maxlen)
70 char *p;
71 struct path_list_item *item;
73 /* autocomplete common developers */
74 p = strchr(email, '>');
75 if (!p)
76 return 0;
78 *p = '\0';
79 item = path_list_lookup(email, &mailmap);
80 if (item != NULL) {
81 const char *realname = (const char *)item->util;
82 strncpy(name, realname, maxlen);
83 return 1;
85 return 0;
88 static void insert_author_oneline(struct path_list *list,
89 const char *author, int authorlen,
90 const char *oneline, int onelinelen)
92 const char *dot3 = "/pub/scm/linux/kernel/git/";
93 char *buffer, *p;
94 struct path_list_item *item;
95 struct path_list *onelines;
97 while (authorlen > 0 && isspace(author[authorlen - 1]))
98 authorlen--;
100 buffer = xmalloc(authorlen + 1);
101 memcpy(buffer, author, authorlen);
102 buffer[authorlen] = '\0';
104 item = path_list_insert(buffer, list);
105 if (item->util == NULL)
106 item->util = xcalloc(1, sizeof(struct path_list));
107 else
108 free(buffer);
110 if (!strncmp(oneline, "[PATCH", 6)) {
111 char *eob = strchr(oneline, ']');
113 if (eob) {
114 while (isspace(eob[1]) && eob[1] != '\n')
115 eob++;
116 if (eob - oneline < onelinelen) {
117 onelinelen -= eob - oneline;
118 oneline = eob;
123 while (onelinelen > 0 && isspace(oneline[0])) {
124 oneline++;
125 onelinelen--;
128 while (onelinelen > 0 && isspace(oneline[onelinelen - 1]))
129 onelinelen--;
131 buffer = xmalloc(onelinelen + 1);
132 memcpy(buffer, oneline, onelinelen);
133 buffer[onelinelen] = '\0';
135 while ((p = strstr(buffer, dot3)) != NULL) {
136 memcpy(p, "...", 3);
137 strcpy(p + 2, p + sizeof(dot3) - 1);
141 onelines = item->util;
142 if (onelines->nr >= onelines->alloc) {
143 onelines->alloc = alloc_nr(onelines->nr);
144 onelines->items = xrealloc(onelines->items,
145 onelines->alloc
146 * sizeof(struct path_list_item));
149 onelines->items[onelines->nr].util = NULL;
150 onelines->items[onelines->nr++].path = buffer;
153 static void read_from_stdin(struct path_list *list)
155 char buffer[1024];
157 while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
158 char *bob;
159 if ((buffer[0] == 'A' || buffer[0] == 'a') &&
160 !strncmp(buffer + 1, "uthor: ", 7) &&
161 (bob = strchr(buffer + 7, '<')) != NULL) {
162 char buffer2[1024], offset = 0;
164 if (map_email(bob + 1, buffer, sizeof(buffer)))
165 bob = buffer + strlen(buffer);
166 else {
167 offset = 8;
168 while (isspace(bob[-1]))
169 bob--;
172 while (fgets(buffer2, sizeof(buffer2), stdin) &&
173 buffer2[0] != '\n')
174 ; /* chomp input */
175 if (fgets(buffer2, sizeof(buffer2), stdin))
176 insert_author_oneline(list,
177 buffer + offset,
178 bob - buffer - offset,
179 buffer2, strlen(buffer2));
184 static void get_from_rev(struct rev_info *rev, struct path_list *list)
186 char scratch[1024];
187 struct commit *commit;
189 prepare_revision_walk(rev);
190 while ((commit = get_revision(rev)) != NULL) {
191 char *author = NULL, *oneline, *buffer;
192 int authorlen = authorlen, onelinelen;
194 /* get author and oneline */
195 for (buffer = commit->buffer; buffer && *buffer != '\0' &&
196 *buffer != '\n'; ) {
197 char *eol = strchr(buffer, '\n');
199 if (eol == NULL)
200 eol = buffer + strlen(buffer);
201 else
202 eol++;
204 if (!strncmp(buffer, "author ", 7)) {
205 char *bracket = strchr(buffer, '<');
207 if (bracket == NULL || bracket > eol)
208 die("Invalid commit buffer: %s",
209 sha1_to_hex(commit->object.sha1));
211 if (map_email(bracket + 1, scratch,
212 sizeof(scratch))) {
213 author = scratch;
214 authorlen = strlen(scratch);
215 } else {
216 while (bracket[-1] == ' ')
217 bracket--;
219 author = buffer + 7;
220 authorlen = bracket - buffer - 7;
223 buffer = eol;
226 if (author == NULL)
227 die ("Missing author: %s",
228 sha1_to_hex(commit->object.sha1));
230 if (buffer == NULL || *buffer == '\0') {
231 oneline = "<none>";
232 onelinelen = sizeof(oneline) + 1;
233 } else {
234 char *eol;
236 oneline = buffer + 1;
237 eol = strchr(oneline, '\n');
238 if (eol == NULL)
239 onelinelen = strlen(oneline);
240 else
241 onelinelen = eol - oneline;
244 insert_author_oneline(list,
245 author, authorlen, oneline, onelinelen);
250 int cmd_shortlog(int argc, const char **argv, const char *prefix)
252 struct rev_info rev;
253 struct path_list list = { NULL, 0, 0, 1 };
254 int i, j, sort_by_number = 0, summary = 0;
256 init_revisions(&rev, prefix);
257 argc = setup_revisions(argc, argv, &rev, NULL);
258 while (argc > 1) {
259 if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
260 sort_by_number = 1;
261 else if (!strcmp(argv[1], "-s") ||
262 !strcmp(argv[1], "--summary"))
263 summary = 1;
264 else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
265 usage(shortlog_usage);
266 else
267 die ("unrecognized argument: %s", argv[1]);
268 argv++;
269 argc--;
272 if (rev.pending.nr == 1)
273 die ("Need a range!");
274 else if (rev.pending.nr == 0)
275 read_from_stdin(&list);
276 else
277 get_from_rev(&rev, &list);
279 if (sort_by_number)
280 qsort(list.items, sizeof(struct path_list_item), list.nr,
281 compare_by_number);
283 for (i = 0; i < list.nr; i++) {
284 struct path_list *onelines = list.items[i].util;
286 printf("%s (%d):\n", list.items[i].path, onelines->nr);
287 if (!summary) {
288 for (j = onelines->nr - 1; j >= 0; j--)
289 printf(" %s\n", onelines->items[j].path);
290 printf("\n");
293 onelines->strdup_paths = 1;
294 path_list_clear(onelines, 1);
295 free(onelines);
296 list.items[i].util = NULL;
299 list.strdup_paths = 1;
300 path_list_clear(&list, 1);
302 return 0;