dd6462b5001bfb957198a3ce4f1672e2548c5cb2
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 struct conf_info conf;
33};
34
35static LIST_HEAD(conf_head);
36
37static char *separators = ":";
38
39#define TRAILER_ARG_STRING "$ARG"
40
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
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}
83
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
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
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);
104 free(item->token);
105 free(item->value);
106 free(item);
107}
108
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
118static void print_tok_val(FILE *outfile, const char *tok, const char *val)
119{
120 char c = last_non_space_char(tok);
121 if (!c)
122 return;
123 if (strchr(separators, c))
124 fprintf(outfile, "%s%s\n", tok, val);
125 else
126 fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
127}
128
129static void print_all(FILE *outfile, struct list_head *head, int trim_empty)
130{
131 struct list_head *pos;
132 struct trailer_item *item;
133 list_for_each(pos, head) {
134 item = list_entry(pos, struct trailer_item, list);
135 if (!trim_empty || strlen(item->value) > 0)
136 print_tok_val(outfile, item->token, item->value);
137 }
138}
139
140static void add_arg_to_input_list(struct trailer_item *on_tok,
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);
147}
148
149static int check_if_different(struct trailer_item *in_tok,
150 struct trailer_item *arg_tok,
151 int check_all,
152 struct list_head *head)
153{
154 enum action_where where = arg_tok->conf.where;
155 struct list_head *next_head;
156 do {
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 */
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);
168 } while (check_all);
169 return 1;
170}
171
172static char *apply_command(const char *command, const char *arg)
173{
174 struct strbuf cmd = STRBUF_INIT;
175 struct strbuf buf = STRBUF_INIT;
176 struct child_process cp = CHILD_PROCESS_INIT;
177 const char *argv[] = {NULL, NULL};
178 char *result;
179
180 strbuf_addstr(&cmd, command);
181 if (arg)
182 strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
183
184 argv[0] = cmd.buf;
185 cp.argv = argv;
186 cp.env = local_repo_env;
187 cp.no_stdin = 1;
188 cp.use_shell = 1;
189
190 if (capture_command(&cp, &buf, 1024)) {
191 error(_("running trailer command '%s' failed"), cmd.buf);
192 strbuf_release(&buf);
193 result = xstrdup("");
194 } else {
195 strbuf_trim(&buf);
196 result = strbuf_detach(&buf, NULL);
197 }
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
220static void apply_arg_if_exists(struct trailer_item *in_tok,
221 struct trailer_item *arg_tok,
222 struct trailer_item *on_tok,
223 struct list_head *head)
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:
230 apply_item_command(in_tok, arg_tok);
231 add_arg_to_input_list(on_tok, arg_tok);
232 list_del(&in_tok->list);
233 free_trailer_item(in_tok);
234 break;
235 case EXISTS_ADD:
236 apply_item_command(in_tok, arg_tok);
237 add_arg_to_input_list(on_tok, arg_tok);
238 break;
239 case EXISTS_ADD_IF_DIFFERENT:
240 apply_item_command(in_tok, arg_tok);
241 if (check_if_different(in_tok, arg_tok, 1, head))
242 add_arg_to_input_list(on_tok, arg_tok);
243 else
244 free_trailer_item(arg_tok);
245 break;
246 case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
247 apply_item_command(in_tok, arg_tok);
248 if (check_if_different(on_tok, arg_tok, 0, head))
249 add_arg_to_input_list(on_tok, arg_tok);
250 else
251 free_trailer_item(arg_tok);
252 break;
253 }
254}
255
256static void apply_arg_if_missing(struct list_head *head,
257 struct trailer_item *arg_tok)
258{
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;
267 apply_item_command(NULL, arg_tok);
268 if (after_or_end(where))
269 list_add_tail(&arg_tok->list, head);
270 else
271 list_add(&arg_tok->list, head);
272 }
273}
274
275static int find_same_and_apply_arg(struct list_head *head,
276 struct trailer_item *arg_tok)
277{
278 struct list_head *pos;
279 struct trailer_item *in_tok;
280 struct trailer_item *on_tok;
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);
285 struct trailer_item *start_tok;
286
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);
296 if (!same_token(in_tok, arg_tok))
297 continue;
298 on_tok = middle ? in_tok : start_tok;
299 apply_arg_if_exists(in_tok, arg_tok, on_tok, head);
300 return 1;
301 }
302 return 0;
303}
304
305static void process_trailers_lists(struct list_head *head,
306 struct list_head *arg_head)
307{
308 struct list_head *pos, *p;
309 struct trailer_item *arg_tok;
310
311 list_for_each_safe(pos, p, arg_head) {
312 int applied = 0;
313 arg_tok = list_entry(pos, struct trailer_item, list);
314
315 list_del(pos);
316
317 applied = find_same_and_apply_arg(head, arg_tok);
318
319 if (!applied)
320 apply_arg_if_missing(head, arg_tok);
321 }
322}
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
367static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
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{
380 struct list_head *pos;
381 struct trailer_item *item;
382
383 /* Look up item with same name */
384 list_for_each(pos, &conf_head) {
385 item = list_entry(pos, struct trailer_item, list);
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
395 list_add_tail(&item->list, &conf_head);
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:
498 die("BUG: trailer.c: unhandled type %d", type);
499 }
500 return 0;
501}
502
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)
521{
522 size_t len;
523 struct strbuf seps = STRBUF_INIT;
524 struct trailer_item *item;
525 int tok_len;
526 struct list_head *pos;
527
528 strbuf_addstr(&seps, separators);
529 strbuf_addch(&seps, '=');
530 len = strcspn(trailer, seps.buf);
531 strbuf_release(&seps);
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 }
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 }
547
548 /* Lookup if the token matches something in the config */
549 tok_len = token_len_without_separator(tok->buf, tok->len);
550 *conf = &default_conf_info;
551 list_for_each(pos, &conf_head) {
552 item = list_entry(pos, struct trailer_item, list);
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 }
560 }
561
562 return 0;
563}
564
565static void add_trailer_item(struct list_head *head, char *tok, char *val,
566 const struct conf_info *conf)
567{
568 struct trailer_item *new = xcalloc(sizeof(*new), 1);
569 new->token = tok;
570 new->value = val;
571 duplicate_conf(&new->conf, conf);
572 list_add_tail(&new->list, head);
573}
574
575static void process_command_line_args(struct list_head *arg_head,
576 struct string_list *trailers)
577{
578 struct string_list_item *tr;
579 struct trailer_item *item;
580 struct strbuf tok = STRBUF_INIT;
581 struct strbuf val = STRBUF_INIT;
582 const struct conf_info *conf;
583 struct list_head *pos;
584
585 /* Add a trailer item for each configured trailer with a command */
586 list_for_each(pos, &conf_head) {
587 item = list_entry(pos, struct trailer_item, list);
588 if (item->conf.command)
589 add_trailer_item(arg_head,
590 xstrdup(token_from_item(item, NULL)),
591 xstrdup(""),
592 &item->conf);
593 }
594
595 /* Add a trailer item for each trailer on the command line */
596 for_each_string_list_item(tr, trailers) {
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);
602 }
603}
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{
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;
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 */
664 for (start = count - 1; start >= end_of_title; start--) {
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
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
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
709static void print_lines(FILE *outfile, struct strbuf **lines, int start, int end)
710{
711 int i;
712 for (i = start; lines[i] && i < end; i++)
713 fprintf(outfile, "%s", lines[i]->buf);
714}
715
716static int process_input_file(FILE *outfile,
717 struct strbuf **lines,
718 struct list_head *head)
719{
720 int count = 0;
721 int patch_start, trailer_start, trailer_end, i;
722 struct strbuf tok = STRBUF_INIT;
723 struct strbuf val = STRBUF_INIT;
724 const struct conf_info *conf;
725
726 /* Get the line count */
727 while (lines[count])
728 count++;
729
730 patch_start = find_patch_start(lines, count);
731 trailer_end = find_trailer_end(lines, patch_start);
732 trailer_start = find_trailer_start(lines, trailer_end);
733
734 /* Print lines before the trailers as is */
735 print_lines(outfile, lines, 0, trailer_start);
736
737 if (!has_blank_line_before(lines, trailer_start - 1))
738 fprintf(outfile, "\n");
739
740 /* Parse trailer lines */
741 for (i = trailer_start; i < trailer_end; i++) {
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);
748 }
749
750 return trailer_end;
751}
752
753static void free_all(struct list_head *head)
754{
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));
759 }
760}
761
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)
794{
795 LIST_HEAD(head);
796 LIST_HEAD(arg_head);
797 struct strbuf **lines;
798 int trailer_end;
799 FILE *outfile = stdout;
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
807 if (in_place)
808 outfile = create_in_place_tempfile(file);
809
810 /* Print the lines before the trailers */
811 trailer_end = process_input_file(outfile, lines, &head);
812
813 process_command_line_args(&arg_head, trailers);
814
815 process_trailers_lists(&head, &arg_head);
816
817 print_all(outfile, &head, trim_empty);
818
819 free_all(&head);
820
821 /* Print the lines after the trailers as is */
822 print_lines(outfile, lines, trailer_end, INT_MAX);
823
824 if (in_place)
825 if (rename_tempfile(&trailers_tempfile, file))
826 die_errno(_("could not rename temporary file to %s"), file);
827
828 strbuf_list_free(lines);
829}