]> git.ipfire.org Git - thirdparty/git.git/blame - trailer.c
trailer: streamline trailer item create and add
[thirdparty/git.git] / trailer.c
CommitLineData
9385b5d7 1#include "cache.h"
f0a90b4e 2#include "string-list.h"
85039fb6 3#include "run-command.h"
61cfef4c 4#include "commit.h"
e1f89863 5#include "tempfile.h"
b1d78d77 6#include "trailer.h"
8966a394 7#include "list.h"
9385b5d7
CC
8/*
9 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
10 */
11
12enum action_where { WHERE_END, WHERE_AFTER, WHERE_BEFORE, WHERE_START };
13enum action_if_exists { EXISTS_ADD_IF_DIFFERENT_NEIGHBOR, EXISTS_ADD_IF_DIFFERENT,
14 EXISTS_ADD, EXISTS_REPLACE, EXISTS_DO_NOTHING };
15enum action_if_missing { MISSING_ADD, MISSING_DO_NOTHING };
16
17struct conf_info {
18 char *name;
19 char *key;
20 char *command;
21 enum action_where where;
22 enum action_if_exists if_exists;
23 enum action_if_missing if_missing;
24};
25
26static struct conf_info default_conf_info;
27
28struct trailer_item {
8966a394 29 struct list_head list;
d65fd424
JT
30 char *token;
31 char *value;
9385b5d7
CC
32 struct conf_info conf;
33};
34
8966a394 35static LIST_HEAD(conf_head);
9385b5d7
CC
36
37static char *separators = ":";
38
85039fb6
CC
39#define TRAILER_ARG_STRING "$ARG"
40
8966a394
JT
41/* Iterate over the elements of the list. */
42#define list_for_each_dir(pos, head, is_reverse) \
43 for (pos = is_reverse ? (head)->prev : (head)->next; \
44 pos != (head); \
45 pos = is_reverse ? pos->prev : pos->next)
46
9385b5d7
CC
47static int after_or_end(enum action_where where)
48{
49 return (where == WHERE_AFTER) || (where == WHERE_END);
50}
51
52/*
53 * Return the length of the string not including any final
54 * punctuation. E.g., the input "Signed-off-by:" would return
55 * 13, stripping the trailing punctuation but retaining
56 * internal punctuation.
57 */
58static size_t token_len_without_separator(const char *token, size_t len)
59{
60 while (len > 0 && !isalnum(token[len - 1]))
61 len--;
62 return len;
63}
64
65static int same_token(struct trailer_item *a, struct trailer_item *b)
66{
67 size_t a_len = token_len_without_separator(a->token, strlen(a->token));
68 size_t b_len = token_len_without_separator(b->token, strlen(b->token));
69 size_t min_len = (a_len > b_len) ? b_len : a_len;
70
71 return !strncasecmp(a->token, b->token, min_len);
72}
73
74static int same_value(struct trailer_item *a, struct trailer_item *b)
75{
76 return !strcasecmp(a->value, b->value);
77}
78
79static int same_trailer(struct trailer_item *a, struct trailer_item *b)
80{
81 return same_token(a, b) && same_value(a, b);
82}
4103818d 83
2013d850
CC
84static inline int contains_only_spaces(const char *str)
85{
86 const char *s = str;
87 while (*s && isspace(*s))
88 s++;
89 return !*s;
90}
91
85039fb6
CC
92static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)
93{
94 const char *ptr = strstr(sb->buf, a);
95 if (ptr)
96 strbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));
97}
98
4103818d
CC
99static void free_trailer_item(struct trailer_item *item)
100{
101 free(item->conf.name);
102 free(item->conf.key);
103 free(item->conf.command);
d65fd424
JT
104 free(item->token);
105 free(item->value);
4103818d
CC
106 free(item);
107}
108
b1d78d77
CC
109static char last_non_space_char(const char *s)
110{
111 int i;
112 for (i = strlen(s) - 1; i >= 0; i--)
113 if (!isspace(s[i]))
114 return s[i];
115 return '\0';
116}
117
d0d2344a 118static void print_tok_val(FILE *outfile, const char *tok, const char *val)
b1d78d77
CC
119{
120 char c = last_non_space_char(tok);
121 if (!c)
122 return;
123 if (strchr(separators, c))
d0d2344a 124 fprintf(outfile, "%s%s\n", tok, val);
b1d78d77 125 else
d0d2344a 126 fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
b1d78d77
CC
127}
128
8966a394 129static void print_all(FILE *outfile, struct list_head *head, int trim_empty)
b1d78d77 130{
8966a394 131 struct list_head *pos;
b1d78d77 132 struct trailer_item *item;
8966a394
JT
133 list_for_each(pos, head) {
134 item = list_entry(pos, struct trailer_item, list);
b1d78d77 135 if (!trim_empty || strlen(item->value) > 0)
d0d2344a 136 print_tok_val(outfile, item->token, item->value);
b1d78d77
CC
137 }
138}
139
4103818d 140static void add_arg_to_input_list(struct trailer_item *on_tok,
8966a394
JT
141 struct trailer_item *arg_tok)
142{
143 if (after_or_end(arg_tok->conf.where))
144 list_add(&arg_tok->list, &on_tok->list);
145 else
146 list_add_tail(&arg_tok->list, &on_tok->list);
4103818d
CC
147}
148
149static int check_if_different(struct trailer_item *in_tok,
150 struct trailer_item *arg_tok,
8966a394
JT
151 int check_all,
152 struct list_head *head)
4103818d
CC
153{
154 enum action_where where = arg_tok->conf.where;
8966a394 155 struct list_head *next_head;
4103818d 156 do {
4103818d
CC
157 if (same_trailer(in_tok, arg_tok))
158 return 0;
159 /*
160 * if we want to add a trailer after another one,
161 * we have to check those before this one
162 */
8966a394
JT
163 next_head = after_or_end(where) ? in_tok->list.prev
164 : in_tok->list.next;
165 if (next_head == head)
166 break;
167 in_tok = list_entry(next_head, struct trailer_item, list);
4103818d
CC
168 } while (check_all);
169 return 1;
170}
171
d65fd424 172static char *apply_command(const char *command, const char *arg)
85039fb6
CC
173{
174 struct strbuf cmd = STRBUF_INIT;
175 struct strbuf buf = STRBUF_INIT;
b226293b 176 struct child_process cp = CHILD_PROCESS_INIT;
85039fb6 177 const char *argv[] = {NULL, NULL};
d65fd424 178 char *result;
85039fb6
CC
179
180 strbuf_addstr(&cmd, command);
181 if (arg)
182 strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
183
184 argv[0] = cmd.buf;
85039fb6
CC
185 cp.argv = argv;
186 cp.env = local_repo_env;
187 cp.no_stdin = 1;
85039fb6
CC
188 cp.use_shell = 1;
189
c5eadcaa 190 if (capture_command(&cp, &buf, 1024)) {
13ad56f8 191 error(_("running trailer command '%s' failed"), cmd.buf);
85039fb6
CC
192 strbuf_release(&buf);
193 result = xstrdup("");
c5eadcaa
JK
194 } else {
195 strbuf_trim(&buf);
85039fb6 196 result = strbuf_detach(&buf, NULL);
c5eadcaa 197 }
85039fb6
CC
198
199 strbuf_release(&cmd);
200 return result;
201}
202
203static void apply_item_command(struct trailer_item *in_tok, struct trailer_item *arg_tok)
204{
205 if (arg_tok->conf.command) {
206 const char *arg;
207 if (arg_tok->value && arg_tok->value[0]) {
208 arg = arg_tok->value;
209 } else {
210 if (in_tok && in_tok->value)
211 arg = xstrdup(in_tok->value);
212 else
213 arg = xstrdup("");
214 }
215 arg_tok->value = apply_command(arg_tok->conf.command, arg);
216 free((char *)arg);
217 }
218}
219
4103818d
CC
220static void apply_arg_if_exists(struct trailer_item *in_tok,
221 struct trailer_item *arg_tok,
222 struct trailer_item *on_tok,
8966a394 223 struct list_head *head)
4103818d
CC
224{
225 switch (arg_tok->conf.if_exists) {
226 case EXISTS_DO_NOTHING:
227 free_trailer_item(arg_tok);
228 break;
229 case EXISTS_REPLACE:
85039fb6 230 apply_item_command(in_tok, arg_tok);
8966a394
JT
231 add_arg_to_input_list(on_tok, arg_tok);
232 list_del(&in_tok->list);
4103818d
CC
233 free_trailer_item(in_tok);
234 break;
235 case EXISTS_ADD:
85039fb6 236 apply_item_command(in_tok, arg_tok);
8966a394 237 add_arg_to_input_list(on_tok, arg_tok);
4103818d
CC
238 break;
239 case EXISTS_ADD_IF_DIFFERENT:
85039fb6 240 apply_item_command(in_tok, arg_tok);
8966a394
JT
241 if (check_if_different(in_tok, arg_tok, 1, head))
242 add_arg_to_input_list(on_tok, arg_tok);
4103818d
CC
243 else
244 free_trailer_item(arg_tok);
245 break;
246 case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
85039fb6 247 apply_item_command(in_tok, arg_tok);
8966a394
JT
248 if (check_if_different(on_tok, arg_tok, 0, head))
249 add_arg_to_input_list(on_tok, arg_tok);
4103818d
CC
250 else
251 free_trailer_item(arg_tok);
252 break;
253 }
254}
255
8966a394 256static void apply_arg_if_missing(struct list_head *head,
4103818d
CC
257 struct trailer_item *arg_tok)
258{
4103818d
CC
259 enum action_where where;
260
261 switch (arg_tok->conf.if_missing) {
262 case MISSING_DO_NOTHING:
263 free_trailer_item(arg_tok);
264 break;
265 case MISSING_ADD:
266 where = arg_tok->conf.where;
85039fb6 267 apply_item_command(NULL, arg_tok);
8966a394
JT
268 if (after_or_end(where))
269 list_add_tail(&arg_tok->list, head);
270 else
271 list_add(&arg_tok->list, head);
4103818d
CC
272 }
273}
274
8966a394 275static int find_same_and_apply_arg(struct list_head *head,
4103818d
CC
276 struct trailer_item *arg_tok)
277{
8966a394 278 struct list_head *pos;
4103818d
CC
279 struct trailer_item *in_tok;
280 struct trailer_item *on_tok;
4103818d
CC
281
282 enum action_where where = arg_tok->conf.where;
283 int middle = (where == WHERE_AFTER) || (where == WHERE_BEFORE);
284 int backwards = after_or_end(where);
8966a394 285 struct trailer_item *start_tok;
4103818d 286
8966a394
JT
287 if (list_empty(head))
288 return 0;
289
290 start_tok = list_entry(backwards ? head->prev : head->next,
291 struct trailer_item,
292 list);
293
294 list_for_each_dir(pos, head, backwards) {
295 in_tok = list_entry(pos, struct trailer_item, list);
4103818d
CC
296 if (!same_token(in_tok, arg_tok))
297 continue;
298 on_tok = middle ? in_tok : start_tok;
8966a394 299 apply_arg_if_exists(in_tok, arg_tok, on_tok, head);
4103818d
CC
300 return 1;
301 }
302 return 0;
303}
304
8966a394
JT
305static void process_trailers_lists(struct list_head *head,
306 struct list_head *arg_head)
4103818d 307{
8966a394 308 struct list_head *pos, *p;
4103818d 309 struct trailer_item *arg_tok;
4103818d 310
8966a394 311 list_for_each_safe(pos, p, arg_head) {
4103818d 312 int applied = 0;
8966a394 313 arg_tok = list_entry(pos, struct trailer_item, list);
4103818d 314
8966a394 315 list_del(pos);
4103818d 316
8966a394 317 applied = find_same_and_apply_arg(head, arg_tok);
4103818d
CC
318
319 if (!applied)
8966a394 320 apply_arg_if_missing(head, arg_tok);
4103818d
CC
321 }
322}
46a0613f
CC
323
324static int set_where(struct conf_info *item, const char *value)
325{
326 if (!strcasecmp("after", value))
327 item->where = WHERE_AFTER;
328 else if (!strcasecmp("before", value))
329 item->where = WHERE_BEFORE;
330 else if (!strcasecmp("end", value))
331 item->where = WHERE_END;
332 else if (!strcasecmp("start", value))
333 item->where = WHERE_START;
334 else
335 return -1;
336 return 0;
337}
338
339static int set_if_exists(struct conf_info *item, const char *value)
340{
341 if (!strcasecmp("addIfDifferent", value))
342 item->if_exists = EXISTS_ADD_IF_DIFFERENT;
343 else if (!strcasecmp("addIfDifferentNeighbor", value))
344 item->if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
345 else if (!strcasecmp("add", value))
346 item->if_exists = EXISTS_ADD;
347 else if (!strcasecmp("replace", value))
348 item->if_exists = EXISTS_REPLACE;
349 else if (!strcasecmp("doNothing", value))
350 item->if_exists = EXISTS_DO_NOTHING;
351 else
352 return -1;
353 return 0;
354}
355
356static int set_if_missing(struct conf_info *item, const char *value)
357{
358 if (!strcasecmp("doNothing", value))
359 item->if_missing = MISSING_DO_NOTHING;
360 else if (!strcasecmp("add", value))
361 item->if_missing = MISSING_ADD;
362 else
363 return -1;
364 return 0;
365}
366
d65fd424 367static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
46a0613f
CC
368{
369 *dst = *src;
370 if (src->name)
371 dst->name = xstrdup(src->name);
372 if (src->key)
373 dst->key = xstrdup(src->key);
374 if (src->command)
375 dst->command = xstrdup(src->command);
376}
377
378static struct trailer_item *get_conf_item(const char *name)
379{
8966a394 380 struct list_head *pos;
46a0613f 381 struct trailer_item *item;
46a0613f
CC
382
383 /* Look up item with same name */
8966a394
JT
384 list_for_each(pos, &conf_head) {
385 item = list_entry(pos, struct trailer_item, list);
46a0613f
CC
386 if (!strcasecmp(item->conf.name, name))
387 return item;
388 }
389
390 /* Item does not already exists, create it */
391 item = xcalloc(sizeof(struct trailer_item), 1);
392 duplicate_conf(&item->conf, &default_conf_info);
393 item->conf.name = xstrdup(name);
394
8966a394 395 list_add_tail(&item->list, &conf_head);
46a0613f
CC
396
397 return item;
398}
399
400enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
401 TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
402
403static struct {
404 const char *name;
405 enum trailer_info_type type;
406} trailer_config_items[] = {
407 { "key", TRAILER_KEY },
408 { "command", TRAILER_COMMAND },
409 { "where", TRAILER_WHERE },
410 { "ifexists", TRAILER_IF_EXISTS },
411 { "ifmissing", TRAILER_IF_MISSING }
412};
413
414static int git_trailer_default_config(const char *conf_key, const char *value, void *cb)
415{
416 const char *trailer_item, *variable_name;
417
418 if (!skip_prefix(conf_key, "trailer.", &trailer_item))
419 return 0;
420
421 variable_name = strrchr(trailer_item, '.');
422 if (!variable_name) {
423 if (!strcmp(trailer_item, "where")) {
424 if (set_where(&default_conf_info, value) < 0)
425 warning(_("unknown value '%s' for key '%s'"),
426 value, conf_key);
427 } else if (!strcmp(trailer_item, "ifexists")) {
428 if (set_if_exists(&default_conf_info, value) < 0)
429 warning(_("unknown value '%s' for key '%s'"),
430 value, conf_key);
431 } else if (!strcmp(trailer_item, "ifmissing")) {
432 if (set_if_missing(&default_conf_info, value) < 0)
433 warning(_("unknown value '%s' for key '%s'"),
434 value, conf_key);
435 } else if (!strcmp(trailer_item, "separators")) {
436 separators = xstrdup(value);
437 }
438 }
439 return 0;
440}
441
442static int git_trailer_config(const char *conf_key, const char *value, void *cb)
443{
444 const char *trailer_item, *variable_name;
445 struct trailer_item *item;
446 struct conf_info *conf;
447 char *name = NULL;
448 enum trailer_info_type type;
449 int i;
450
451 if (!skip_prefix(conf_key, "trailer.", &trailer_item))
452 return 0;
453
454 variable_name = strrchr(trailer_item, '.');
455 if (!variable_name)
456 return 0;
457
458 variable_name++;
459 for (i = 0; i < ARRAY_SIZE(trailer_config_items); i++) {
460 if (strcmp(trailer_config_items[i].name, variable_name))
461 continue;
462 name = xstrndup(trailer_item, variable_name - trailer_item - 1);
463 type = trailer_config_items[i].type;
464 break;
465 }
466
467 if (!name)
468 return 0;
469
470 item = get_conf_item(name);
471 conf = &item->conf;
472 free(name);
473
474 switch (type) {
475 case TRAILER_KEY:
476 if (conf->key)
477 warning(_("more than one %s"), conf_key);
478 conf->key = xstrdup(value);
479 break;
480 case TRAILER_COMMAND:
481 if (conf->command)
482 warning(_("more than one %s"), conf_key);
483 conf->command = xstrdup(value);
484 break;
485 case TRAILER_WHERE:
486 if (set_where(conf, value))
487 warning(_("unknown value '%s' for key '%s'"), value, conf_key);
488 break;
489 case TRAILER_IF_EXISTS:
490 if (set_if_exists(conf, value))
491 warning(_("unknown value '%s' for key '%s'"), value, conf_key);
492 break;
493 case TRAILER_IF_MISSING:
494 if (set_if_missing(conf, value))
495 warning(_("unknown value '%s' for key '%s'"), value, conf_key);
496 break;
497 default:
ef1177d1 498 die("BUG: trailer.c: unhandled type %d", type);
46a0613f
CC
499 }
500 return 0;
501}
f0a90b4e 502
63ab3f34
JT
503static const char *token_from_item(struct trailer_item *item, char *tok)
504{
505 if (item->conf.key)
506 return item->conf.key;
507 if (tok)
508 return tok;
509 return item->conf.name;
510}
511
512static int token_matches_item(const char *tok, struct trailer_item *item, int tok_len)
513{
514 if (!strncasecmp(tok, item->conf.name, tok_len))
515 return 1;
516 return item->conf.key ? !strncasecmp(tok, item->conf.key, tok_len) : 0;
517}
518
519static int parse_trailer(struct strbuf *tok, struct strbuf *val,
520 const struct conf_info **conf, const char *trailer)
f0a90b4e
CC
521{
522 size_t len;
523 struct strbuf seps = STRBUF_INIT;
63ab3f34
JT
524 struct trailer_item *item;
525 int tok_len;
526 struct list_head *pos;
527
f0a90b4e
CC
528 strbuf_addstr(&seps, separators);
529 strbuf_addch(&seps, '=');
530 len = strcspn(trailer, seps.buf);
531 strbuf_release(&seps);
d52adf1f
CC
532 if (len == 0) {
533 int l = strlen(trailer);
534 while (l > 0 && isspace(trailer[l - 1]))
535 l--;
536 return error(_("empty trailer token in trailer '%.*s'"), l, trailer);
537 }
f0a90b4e
CC
538 if (len < strlen(trailer)) {
539 strbuf_add(tok, trailer, len);
540 strbuf_trim(tok);
541 strbuf_addstr(val, trailer + len + 1);
542 strbuf_trim(val);
543 } else {
544 strbuf_addstr(tok, trailer);
545 strbuf_trim(tok);
546 }
f0a90b4e
CC
547
548 /* Lookup if the token matches something in the config */
63ab3f34
JT
549 tok_len = token_len_without_separator(tok->buf, tok->len);
550 *conf = &default_conf_info;
8966a394
JT
551 list_for_each(pos, &conf_head) {
552 item = list_entry(pos, struct trailer_item, list);
63ab3f34
JT
553 if (token_matches_item(tok->buf, item, tok_len)) {
554 char *tok_buf = strbuf_detach(tok, NULL);
555 *conf = &item->conf;
556 strbuf_addstr(tok, token_from_item(item, tok_buf));
557 free(tok_buf);
558 break;
559 }
f0a90b4e
CC
560 }
561
63ab3f34 562 return 0;
f0a90b4e
CC
563}
564
63ab3f34
JT
565static void add_trailer_item(struct list_head *head, char *tok, char *val,
566 const struct conf_info *conf)
f0a90b4e 567{
63ab3f34
JT
568 struct trailer_item *new = xcalloc(sizeof(*new), 1);
569 new->token = tok;
570 new->value = val;
571 duplicate_conf(&new->conf, conf);
8966a394 572 list_add_tail(&new->list, head);
f0a90b4e
CC
573}
574
8966a394
JT
575static void process_command_line_args(struct list_head *arg_head,
576 struct string_list *trailers)
f0a90b4e 577{
f0a90b4e 578 struct string_list_item *tr;
85039fb6 579 struct trailer_item *item;
63ab3f34
JT
580 struct strbuf tok = STRBUF_INIT;
581 struct strbuf val = STRBUF_INIT;
582 const struct conf_info *conf;
8966a394 583 struct list_head *pos;
85039fb6
CC
584
585 /* Add a trailer item for each configured trailer with a command */
8966a394
JT
586 list_for_each(pos, &conf_head) {
587 item = list_entry(pos, struct trailer_item, list);
63ab3f34
JT
588 if (item->conf.command)
589 add_trailer_item(arg_head,
590 xstrdup(token_from_item(item, NULL)),
591 xstrdup(""),
592 &item->conf);
85039fb6 593 }
f0a90b4e 594
85039fb6 595 /* Add a trailer item for each trailer on the command line */
f0a90b4e 596 for_each_string_list_item(tr, trailers) {
63ab3f34
JT
597 if (!parse_trailer(&tok, &val, &conf, tr->string))
598 add_trailer_item(arg_head,
599 strbuf_detach(&tok, NULL),
600 strbuf_detach(&val, NULL),
601 conf);
f0a90b4e 602 }
f0a90b4e 603}
2013d850
CC
604
605static struct strbuf **read_input_file(const char *file)
606{
607 struct strbuf **lines;
608 struct strbuf sb = STRBUF_INIT;
609
610 if (file) {
611 if (strbuf_read_file(&sb, file, 0) < 0)
612 die_errno(_("could not read input file '%s'"), file);
613 } else {
614 if (strbuf_read(&sb, fileno(stdin), 0) < 0)
615 die_errno(_("could not read from stdin"));
616 }
617
618 lines = strbuf_split(&sb, '\n');
619
620 strbuf_release(&sb);
621
622 return lines;
623}
624
625/*
626 * Return the (0 based) index of the start of the patch or the line
627 * count if there is no patch in the message.
628 */
629static int find_patch_start(struct strbuf **lines, int count)
630{
631 int i;
632
633 /* Get the start of the patch part if any */
634 for (i = 0; i < count; i++) {
635 if (starts_with(lines[i]->buf, "---"))
636 return i;
637 }
638
639 return count;
640}
641
642/*
643 * Return the (0 based) index of the first trailer line or count if
644 * there are no trailers. Trailers are searched only in the lines from
645 * index (count - 1) down to index 0.
646 */
647static int find_trailer_start(struct strbuf **lines, int count)
648{
5c99995d
CC
649 int start, end_of_title, only_spaces = 1;
650
651 /* The first paragraph is the title and cannot be trailers */
652 for (start = 0; start < count; start++) {
653 if (lines[start]->buf[0] == comment_line_char)
654 continue;
655 if (contains_only_spaces(lines[start]->buf))
656 break;
657 }
658 end_of_title = start;
2013d850
CC
659
660 /*
661 * Get the start of the trailers by looking starting from the end
662 * for a line with only spaces before lines with one separator.
663 */
5c99995d 664 for (start = count - 1; start >= end_of_title; start--) {
2013d850
CC
665 if (lines[start]->buf[0] == comment_line_char)
666 continue;
667 if (contains_only_spaces(lines[start]->buf)) {
668 if (only_spaces)
669 continue;
670 return start + 1;
671 }
672 if (strcspn(lines[start]->buf, separators) < lines[start]->len) {
673 if (only_spaces)
674 only_spaces = 0;
675 continue;
676 }
677 return count;
678 }
679
680 return only_spaces ? count : 0;
681}
682
61cfef4c
CC
683/* Get the index of the end of the trailers */
684static int find_trailer_end(struct strbuf **lines, int patch_start)
685{
686 struct strbuf sb = STRBUF_INIT;
687 int i, ignore_bytes;
688
689 for (i = 0; i < patch_start; i++)
690 strbuf_addbuf(&sb, lines[i]);
691 ignore_bytes = ignore_non_trailer(&sb);
692 strbuf_release(&sb);
693 for (i = patch_start - 1; i >= 0 && ignore_bytes > 0; i--)
694 ignore_bytes -= lines[i]->len;
695
696 return i + 1;
697}
698
2013d850
CC
699static int has_blank_line_before(struct strbuf **lines, int start)
700{
701 for (;start >= 0; start--) {
702 if (lines[start]->buf[0] == comment_line_char)
703 continue;
704 return contains_only_spaces(lines[start]->buf);
705 }
706 return 0;
707}
708
d0d2344a 709static void print_lines(FILE *outfile, struct strbuf **lines, int start, int end)
2013d850
CC
710{
711 int i;
712 for (i = start; lines[i] && i < end; i++)
d0d2344a 713 fprintf(outfile, "%s", lines[i]->buf);
2013d850
CC
714}
715
d0d2344a
TK
716static int process_input_file(FILE *outfile,
717 struct strbuf **lines,
8966a394 718 struct list_head *head)
2013d850
CC
719{
720 int count = 0;
61cfef4c 721 int patch_start, trailer_start, trailer_end, i;
63ab3f34
JT
722 struct strbuf tok = STRBUF_INIT;
723 struct strbuf val = STRBUF_INIT;
724 const struct conf_info *conf;
2013d850
CC
725
726 /* Get the line count */
727 while (lines[count])
728 count++;
729
730 patch_start = find_patch_start(lines, count);
61cfef4c
CC
731 trailer_end = find_trailer_end(lines, patch_start);
732 trailer_start = find_trailer_start(lines, trailer_end);
2013d850
CC
733
734 /* Print lines before the trailers as is */
d0d2344a 735 print_lines(outfile, lines, 0, trailer_start);
2013d850
CC
736
737 if (!has_blank_line_before(lines, trailer_start - 1))
d0d2344a 738 fprintf(outfile, "\n");
2013d850
CC
739
740 /* Parse trailer lines */
61cfef4c 741 for (i = trailer_start; i < trailer_end; i++) {
63ab3f34
JT
742 if (lines[i]->buf[0] != comment_line_char &&
743 !parse_trailer(&tok, &val, &conf, lines[i]->buf))
744 add_trailer_item(head,
745 strbuf_detach(&tok, NULL),
746 strbuf_detach(&val, NULL),
747 conf);
2013d850
CC
748 }
749
61cfef4c 750 return trailer_end;
2013d850 751}
b1d78d77 752
8966a394 753static void free_all(struct list_head *head)
b1d78d77 754{
8966a394
JT
755 struct list_head *pos, *p;
756 list_for_each_safe(pos, p, head) {
757 list_del(pos);
758 free_trailer_item(list_entry(pos, struct trailer_item, list));
b1d78d77
CC
759 }
760}
761
e1f89863
TK
762static struct tempfile trailers_tempfile;
763
764static FILE *create_in_place_tempfile(const char *file)
765{
766 struct stat st;
767 struct strbuf template = STRBUF_INIT;
768 const char *tail;
769 FILE *outfile;
770
771 if (stat(file, &st))
772 die_errno(_("could not stat %s"), file);
773 if (!S_ISREG(st.st_mode))
774 die(_("file %s is not a regular file"), file);
775 if (!(st.st_mode & S_IWUSR))
776 die(_("file %s is not writable by user"), file);
777
778 /* Create temporary file in the same directory as the original */
779 tail = strrchr(file, '/');
780 if (tail != NULL)
781 strbuf_add(&template, file, tail - file + 1);
782 strbuf_addstr(&template, "git-interpret-trailers-XXXXXX");
783
784 xmks_tempfile_m(&trailers_tempfile, template.buf, st.st_mode);
785 strbuf_release(&template);
786 outfile = fdopen_tempfile(&trailers_tempfile, "w");
787 if (!outfile)
788 die_errno(_("could not open temporary file"));
789
790 return outfile;
791}
792
793void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)
b1d78d77 794{
8966a394
JT
795 LIST_HEAD(head);
796 LIST_HEAD(arg_head);
b1d78d77 797 struct strbuf **lines;
61cfef4c 798 int trailer_end;
d0d2344a 799 FILE *outfile = stdout;
b1d78d77
CC
800
801 /* Default config must be setup first */
802 git_config(git_trailer_default_config, NULL);
803 git_config(git_trailer_config, NULL);
804
805 lines = read_input_file(file);
806
e1f89863
TK
807 if (in_place)
808 outfile = create_in_place_tempfile(file);
809
b1d78d77 810 /* Print the lines before the trailers */
8966a394 811 trailer_end = process_input_file(outfile, lines, &head);
b1d78d77 812
8966a394 813 process_command_line_args(&arg_head, trailers);
b1d78d77 814
8966a394 815 process_trailers_lists(&head, &arg_head);
b1d78d77 816
8966a394 817 print_all(outfile, &head, trim_empty);
b1d78d77 818
8966a394 819 free_all(&head);
b1d78d77
CC
820
821 /* Print the lines after the trailers as is */
d0d2344a 822 print_lines(outfile, lines, trailer_end, INT_MAX);
b1d78d77 823
e1f89863
TK
824 if (in_place)
825 if (rename_tempfile(&trailers_tempfile, file))
826 die_errno(_("could not rename temporary file to %s"), file);
827
b1d78d77
CC
828 strbuf_list_free(lines);
829}