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