]> git.ipfire.org Git - thirdparty/git.git/blame - trailer.c
trailer: use list.h for doubly-linked list
[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
CC
502
503static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trailer)
504{
505 size_t len;
506 struct strbuf seps = STRBUF_INIT;
507 strbuf_addstr(&seps, separators);
508 strbuf_addch(&seps, '=');
509 len = strcspn(trailer, seps.buf);
510 strbuf_release(&seps);
d52adf1f
CC
511 if (len == 0) {
512 int l = strlen(trailer);
513 while (l > 0 && isspace(trailer[l - 1]))
514 l--;
515 return error(_("empty trailer token in trailer '%.*s'"), l, trailer);
516 }
f0a90b4e
CC
517 if (len < strlen(trailer)) {
518 strbuf_add(tok, trailer, len);
519 strbuf_trim(tok);
520 strbuf_addstr(val, trailer + len + 1);
521 strbuf_trim(val);
522 } else {
523 strbuf_addstr(tok, trailer);
524 strbuf_trim(tok);
525 }
526 return 0;
527}
528
529static const char *token_from_item(struct trailer_item *item, char *tok)
530{
531 if (item->conf.key)
532 return item->conf.key;
533 if (tok)
534 return tok;
535 return item->conf.name;
536}
537
538static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,
539 char *tok, char *val)
540{
541 struct trailer_item *new = xcalloc(sizeof(*new), 1);
85039fb6 542 new->value = val ? val : xstrdup("");
f0a90b4e
CC
543
544 if (conf_item) {
545 duplicate_conf(&new->conf, &conf_item->conf);
546 new->token = xstrdup(token_from_item(conf_item, tok));
547 free(tok);
548 } else {
549 duplicate_conf(&new->conf, &default_conf_info);
550 new->token = tok;
551 }
552
553 return new;
554}
555
556static int token_matches_item(const char *tok, struct trailer_item *item, int tok_len)
557{
558 if (!strncasecmp(tok, item->conf.name, tok_len))
559 return 1;
560 return item->conf.key ? !strncasecmp(tok, item->conf.key, tok_len) : 0;
561}
562
563static struct trailer_item *create_trailer_item(const char *string)
564{
565 struct strbuf tok = STRBUF_INIT;
566 struct strbuf val = STRBUF_INIT;
567 struct trailer_item *item;
568 int tok_len;
8966a394 569 struct list_head *pos;
f0a90b4e
CC
570
571 if (parse_trailer(&tok, &val, string))
572 return NULL;
573
574 tok_len = token_len_without_separator(tok.buf, tok.len);
575
576 /* Lookup if the token matches something in the config */
8966a394
JT
577 list_for_each(pos, &conf_head) {
578 item = list_entry(pos, struct trailer_item, list);
f0a90b4e
CC
579 if (token_matches_item(tok.buf, item, tok_len))
580 return new_trailer_item(item,
581 strbuf_detach(&tok, NULL),
582 strbuf_detach(&val, NULL));
583 }
584
585 return new_trailer_item(NULL,
586 strbuf_detach(&tok, NULL),
587 strbuf_detach(&val, NULL));
588}
589
8966a394 590static void add_trailer_item(struct list_head *head, struct trailer_item *new)
f0a90b4e
CC
591{
592 if (!new)
593 return;
8966a394 594 list_add_tail(&new->list, head);
f0a90b4e
CC
595}
596
8966a394
JT
597static void process_command_line_args(struct list_head *arg_head,
598 struct string_list *trailers)
f0a90b4e 599{
f0a90b4e 600 struct string_list_item *tr;
85039fb6 601 struct trailer_item *item;
8966a394 602 struct list_head *pos;
85039fb6
CC
603
604 /* Add a trailer item for each configured trailer with a command */
8966a394
JT
605 list_for_each(pos, &conf_head) {
606 item = list_entry(pos, struct trailer_item, list);
85039fb6
CC
607 if (item->conf.command) {
608 struct trailer_item *new = new_trailer_item(item, NULL, NULL);
8966a394 609 add_trailer_item(arg_head, new);
85039fb6
CC
610 }
611 }
f0a90b4e 612
85039fb6 613 /* Add a trailer item for each trailer on the command line */
f0a90b4e
CC
614 for_each_string_list_item(tr, trailers) {
615 struct trailer_item *new = create_trailer_item(tr->string);
8966a394 616 add_trailer_item(arg_head, new);
f0a90b4e 617 }
f0a90b4e 618}
2013d850
CC
619
620static struct strbuf **read_input_file(const char *file)
621{
622 struct strbuf **lines;
623 struct strbuf sb = STRBUF_INIT;
624
625 if (file) {
626 if (strbuf_read_file(&sb, file, 0) < 0)
627 die_errno(_("could not read input file '%s'"), file);
628 } else {
629 if (strbuf_read(&sb, fileno(stdin), 0) < 0)
630 die_errno(_("could not read from stdin"));
631 }
632
633 lines = strbuf_split(&sb, '\n');
634
635 strbuf_release(&sb);
636
637 return lines;
638}
639
640/*
641 * Return the (0 based) index of the start of the patch or the line
642 * count if there is no patch in the message.
643 */
644static int find_patch_start(struct strbuf **lines, int count)
645{
646 int i;
647
648 /* Get the start of the patch part if any */
649 for (i = 0; i < count; i++) {
650 if (starts_with(lines[i]->buf, "---"))
651 return i;
652 }
653
654 return count;
655}
656
657/*
658 * Return the (0 based) index of the first trailer line or count if
659 * there are no trailers. Trailers are searched only in the lines from
660 * index (count - 1) down to index 0.
661 */
662static int find_trailer_start(struct strbuf **lines, int count)
663{
5c99995d
CC
664 int start, end_of_title, only_spaces = 1;
665
666 /* The first paragraph is the title and cannot be trailers */
667 for (start = 0; start < count; start++) {
668 if (lines[start]->buf[0] == comment_line_char)
669 continue;
670 if (contains_only_spaces(lines[start]->buf))
671 break;
672 }
673 end_of_title = start;
2013d850
CC
674
675 /*
676 * Get the start of the trailers by looking starting from the end
677 * for a line with only spaces before lines with one separator.
678 */
5c99995d 679 for (start = count - 1; start >= end_of_title; start--) {
2013d850
CC
680 if (lines[start]->buf[0] == comment_line_char)
681 continue;
682 if (contains_only_spaces(lines[start]->buf)) {
683 if (only_spaces)
684 continue;
685 return start + 1;
686 }
687 if (strcspn(lines[start]->buf, separators) < lines[start]->len) {
688 if (only_spaces)
689 only_spaces = 0;
690 continue;
691 }
692 return count;
693 }
694
695 return only_spaces ? count : 0;
696}
697
61cfef4c
CC
698/* Get the index of the end of the trailers */
699static int find_trailer_end(struct strbuf **lines, int patch_start)
700{
701 struct strbuf sb = STRBUF_INIT;
702 int i, ignore_bytes;
703
704 for (i = 0; i < patch_start; i++)
705 strbuf_addbuf(&sb, lines[i]);
706 ignore_bytes = ignore_non_trailer(&sb);
707 strbuf_release(&sb);
708 for (i = patch_start - 1; i >= 0 && ignore_bytes > 0; i--)
709 ignore_bytes -= lines[i]->len;
710
711 return i + 1;
712}
713
2013d850
CC
714static int has_blank_line_before(struct strbuf **lines, int start)
715{
716 for (;start >= 0; start--) {
717 if (lines[start]->buf[0] == comment_line_char)
718 continue;
719 return contains_only_spaces(lines[start]->buf);
720 }
721 return 0;
722}
723
d0d2344a 724static void print_lines(FILE *outfile, struct strbuf **lines, int start, int end)
2013d850
CC
725{
726 int i;
727 for (i = start; lines[i] && i < end; i++)
d0d2344a 728 fprintf(outfile, "%s", lines[i]->buf);
2013d850
CC
729}
730
d0d2344a
TK
731static int process_input_file(FILE *outfile,
732 struct strbuf **lines,
8966a394 733 struct list_head *head)
2013d850
CC
734{
735 int count = 0;
61cfef4c 736 int patch_start, trailer_start, trailer_end, i;
2013d850
CC
737
738 /* Get the line count */
739 while (lines[count])
740 count++;
741
742 patch_start = find_patch_start(lines, count);
61cfef4c
CC
743 trailer_end = find_trailer_end(lines, patch_start);
744 trailer_start = find_trailer_start(lines, trailer_end);
2013d850
CC
745
746 /* Print lines before the trailers as is */
d0d2344a 747 print_lines(outfile, lines, 0, trailer_start);
2013d850
CC
748
749 if (!has_blank_line_before(lines, trailer_start - 1))
d0d2344a 750 fprintf(outfile, "\n");
2013d850
CC
751
752 /* Parse trailer lines */
61cfef4c 753 for (i = trailer_start; i < trailer_end; i++) {
2887103b
CC
754 if (lines[i]->buf[0] != comment_line_char) {
755 struct trailer_item *new = create_trailer_item(lines[i]->buf);
8966a394 756 add_trailer_item(head, new);
2887103b 757 }
2013d850
CC
758 }
759
61cfef4c 760 return trailer_end;
2013d850 761}
b1d78d77 762
8966a394 763static void free_all(struct list_head *head)
b1d78d77 764{
8966a394
JT
765 struct list_head *pos, *p;
766 list_for_each_safe(pos, p, head) {
767 list_del(pos);
768 free_trailer_item(list_entry(pos, struct trailer_item, list));
b1d78d77
CC
769 }
770}
771
e1f89863
TK
772static struct tempfile trailers_tempfile;
773
774static FILE *create_in_place_tempfile(const char *file)
775{
776 struct stat st;
777 struct strbuf template = STRBUF_INIT;
778 const char *tail;
779 FILE *outfile;
780
781 if (stat(file, &st))
782 die_errno(_("could not stat %s"), file);
783 if (!S_ISREG(st.st_mode))
784 die(_("file %s is not a regular file"), file);
785 if (!(st.st_mode & S_IWUSR))
786 die(_("file %s is not writable by user"), file);
787
788 /* Create temporary file in the same directory as the original */
789 tail = strrchr(file, '/');
790 if (tail != NULL)
791 strbuf_add(&template, file, tail - file + 1);
792 strbuf_addstr(&template, "git-interpret-trailers-XXXXXX");
793
794 xmks_tempfile_m(&trailers_tempfile, template.buf, st.st_mode);
795 strbuf_release(&template);
796 outfile = fdopen_tempfile(&trailers_tempfile, "w");
797 if (!outfile)
798 die_errno(_("could not open temporary file"));
799
800 return outfile;
801}
802
803void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)
b1d78d77 804{
8966a394
JT
805 LIST_HEAD(head);
806 LIST_HEAD(arg_head);
b1d78d77 807 struct strbuf **lines;
61cfef4c 808 int trailer_end;
d0d2344a 809 FILE *outfile = stdout;
b1d78d77
CC
810
811 /* Default config must be setup first */
812 git_config(git_trailer_default_config, NULL);
813 git_config(git_trailer_config, NULL);
814
815 lines = read_input_file(file);
816
e1f89863
TK
817 if (in_place)
818 outfile = create_in_place_tempfile(file);
819
b1d78d77 820 /* Print the lines before the trailers */
8966a394 821 trailer_end = process_input_file(outfile, lines, &head);
b1d78d77 822
8966a394 823 process_command_line_args(&arg_head, trailers);
b1d78d77 824
8966a394 825 process_trailers_lists(&head, &arg_head);
b1d78d77 826
8966a394 827 print_all(outfile, &head, trim_empty);
b1d78d77 828
8966a394 829 free_all(&head);
b1d78d77
CC
830
831 /* Print the lines after the trailers as is */
d0d2344a 832 print_lines(outfile, lines, trailer_end, INT_MAX);
b1d78d77 833
e1f89863
TK
834 if (in_place)
835 if (rename_tempfile(&trailers_tempfile, file))
836 die_errno(_("could not rename temporary file to %s"), file);
837
b1d78d77
CC
838 strbuf_list_free(lines);
839}