]> git.ipfire.org Git - thirdparty/git.git/blame - trailer.c
trailer: make args have their own struct
[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;
cc71b0de
JT
32};
33
34struct arg_item {
35 struct list_head list;
36 char *token;
37 char *value;
9385b5d7
CC
38 struct conf_info conf;
39};
40
8966a394 41static LIST_HEAD(conf_head);
9385b5d7
CC
42
43static char *separators = ":";
44
85039fb6
CC
45#define TRAILER_ARG_STRING "$ARG"
46
8966a394
JT
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
9385b5d7
CC
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
cc71b0de 71static int same_token(struct trailer_item *a, struct arg_item *b)
9385b5d7
CC
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
cc71b0de 80static int same_value(struct trailer_item *a, struct arg_item *b)
9385b5d7
CC
81{
82 return !strcasecmp(a->value, b->value);
83}
84
cc71b0de 85static int same_trailer(struct trailer_item *a, struct arg_item *b)
9385b5d7
CC
86{
87 return same_token(a, b) && same_value(a, b);
88}
4103818d 89
2013d850
CC
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
85039fb6
CC
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
4103818d 105static void free_trailer_item(struct trailer_item *item)
cc71b0de
JT
106{
107 free(item->token);
108 free(item->value);
109 free(item);
110}
111
112static void free_arg_item(struct arg_item *item)
4103818d
CC
113{
114 free(item->conf.name);
115 free(item->conf.key);
116 free(item->conf.command);
d65fd424
JT
117 free(item->token);
118 free(item->value);
4103818d
CC
119 free(item);
120}
121
b1d78d77
CC
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
d0d2344a 131static void print_tok_val(FILE *outfile, const char *tok, const char *val)
b1d78d77
CC
132{
133 char c = last_non_space_char(tok);
134 if (!c)
135 return;
136 if (strchr(separators, c))
d0d2344a 137 fprintf(outfile, "%s%s\n", tok, val);
b1d78d77 138 else
d0d2344a 139 fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
b1d78d77
CC
140}
141
8966a394 142static void print_all(FILE *outfile, struct list_head *head, int trim_empty)
b1d78d77 143{
8966a394 144 struct list_head *pos;
b1d78d77 145 struct trailer_item *item;
8966a394
JT
146 list_for_each(pos, head) {
147 item = list_entry(pos, struct trailer_item, list);
b1d78d77 148 if (!trim_empty || strlen(item->value) > 0)
d0d2344a 149 print_tok_val(outfile, item->token, item->value);
b1d78d77
CC
150 }
151}
152
cc71b0de
JT
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
4103818d 163static void add_arg_to_input_list(struct trailer_item *on_tok,
cc71b0de 164 struct arg_item *arg_tok)
8966a394 165{
cc71b0de
JT
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);
8966a394 170 else
cc71b0de 171 list_add_tail(&to_add->list, &on_tok->list);
4103818d
CC
172}
173
174static int check_if_different(struct trailer_item *in_tok,
cc71b0de 175 struct arg_item *arg_tok,
8966a394
JT
176 int check_all,
177 struct list_head *head)
4103818d
CC
178{
179 enum action_where where = arg_tok->conf.where;
8966a394 180 struct list_head *next_head;
4103818d 181 do {
4103818d
CC
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 */
8966a394
JT
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);
4103818d
CC
193 } while (check_all);
194 return 1;
195}
196
d65fd424 197static char *apply_command(const char *command, const char *arg)
85039fb6
CC
198{
199 struct strbuf cmd = STRBUF_INIT;
200 struct strbuf buf = STRBUF_INIT;
b226293b 201 struct child_process cp = CHILD_PROCESS_INIT;
85039fb6 202 const char *argv[] = {NULL, NULL};
d65fd424 203 char *result;
85039fb6
CC
204
205 strbuf_addstr(&cmd, command);
206 if (arg)
207 strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
208
209 argv[0] = cmd.buf;
85039fb6
CC
210 cp.argv = argv;
211 cp.env = local_repo_env;
212 cp.no_stdin = 1;
85039fb6
CC
213 cp.use_shell = 1;
214
c5eadcaa 215 if (capture_command(&cp, &buf, 1024)) {
13ad56f8 216 error(_("running trailer command '%s' failed"), cmd.buf);
85039fb6
CC
217 strbuf_release(&buf);
218 result = xstrdup("");
c5eadcaa
JK
219 } else {
220 strbuf_trim(&buf);
85039fb6 221 result = strbuf_detach(&buf, NULL);
c5eadcaa 222 }
85039fb6
CC
223
224 strbuf_release(&cmd);
225 return result;
226}
227
cc71b0de 228static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
85039fb6
CC
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
4103818d 245static void apply_arg_if_exists(struct trailer_item *in_tok,
cc71b0de 246 struct arg_item *arg_tok,
4103818d 247 struct trailer_item *on_tok,
8966a394 248 struct list_head *head)
4103818d
CC
249{
250 switch (arg_tok->conf.if_exists) {
251 case EXISTS_DO_NOTHING:
cc71b0de 252 free_arg_item(arg_tok);
4103818d
CC
253 break;
254 case EXISTS_REPLACE:
85039fb6 255 apply_item_command(in_tok, arg_tok);
8966a394
JT
256 add_arg_to_input_list(on_tok, arg_tok);
257 list_del(&in_tok->list);
4103818d
CC
258 free_trailer_item(in_tok);
259 break;
260 case EXISTS_ADD:
85039fb6 261 apply_item_command(in_tok, arg_tok);
8966a394 262 add_arg_to_input_list(on_tok, arg_tok);
4103818d
CC
263 break;
264 case EXISTS_ADD_IF_DIFFERENT:
85039fb6 265 apply_item_command(in_tok, arg_tok);
8966a394
JT
266 if (check_if_different(in_tok, arg_tok, 1, head))
267 add_arg_to_input_list(on_tok, arg_tok);
4103818d 268 else
cc71b0de 269 free_arg_item(arg_tok);
4103818d
CC
270 break;
271 case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
85039fb6 272 apply_item_command(in_tok, arg_tok);
8966a394
JT
273 if (check_if_different(on_tok, arg_tok, 0, head))
274 add_arg_to_input_list(on_tok, arg_tok);
4103818d 275 else
cc71b0de 276 free_arg_item(arg_tok);
4103818d
CC
277 break;
278 }
279}
280
8966a394 281static void apply_arg_if_missing(struct list_head *head,
cc71b0de 282 struct arg_item *arg_tok)
4103818d 283{
4103818d 284 enum action_where where;
cc71b0de 285 struct trailer_item *to_add;
4103818d
CC
286
287 switch (arg_tok->conf.if_missing) {
288 case MISSING_DO_NOTHING:
cc71b0de 289 free_arg_item(arg_tok);
4103818d
CC
290 break;
291 case MISSING_ADD:
292 where = arg_tok->conf.where;
85039fb6 293 apply_item_command(NULL, arg_tok);
cc71b0de 294 to_add = trailer_from_arg(arg_tok);
8966a394 295 if (after_or_end(where))
cc71b0de 296 list_add_tail(&to_add->list, head);
8966a394 297 else
cc71b0de 298 list_add(&to_add->list, head);
4103818d
CC
299 }
300}
301
8966a394 302static int find_same_and_apply_arg(struct list_head *head,
cc71b0de 303 struct arg_item *arg_tok)
4103818d 304{
8966a394 305 struct list_head *pos;
4103818d
CC
306 struct trailer_item *in_tok;
307 struct trailer_item *on_tok;
4103818d
CC
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);
8966a394 312 struct trailer_item *start_tok;
4103818d 313
8966a394
JT
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);
4103818d
CC
323 if (!same_token(in_tok, arg_tok))
324 continue;
325 on_tok = middle ? in_tok : start_tok;
8966a394 326 apply_arg_if_exists(in_tok, arg_tok, on_tok, head);
4103818d
CC
327 return 1;
328 }
329 return 0;
330}
331
8966a394
JT
332static void process_trailers_lists(struct list_head *head,
333 struct list_head *arg_head)
4103818d 334{
8966a394 335 struct list_head *pos, *p;
cc71b0de 336 struct arg_item *arg_tok;
4103818d 337
8966a394 338 list_for_each_safe(pos, p, arg_head) {
4103818d 339 int applied = 0;
cc71b0de 340 arg_tok = list_entry(pos, struct arg_item, list);
4103818d 341
8966a394 342 list_del(pos);
4103818d 343
8966a394 344 applied = find_same_and_apply_arg(head, arg_tok);
4103818d
CC
345
346 if (!applied)
8966a394 347 apply_arg_if_missing(head, arg_tok);
4103818d
CC
348 }
349}
46a0613f
CC
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
d65fd424 394static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
46a0613f
CC
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
cc71b0de 405static struct arg_item *get_conf_item(const char *name)
46a0613f 406{
8966a394 407 struct list_head *pos;
cc71b0de 408 struct arg_item *item;
46a0613f
CC
409
410 /* Look up item with same name */
8966a394 411 list_for_each(pos, &conf_head) {
cc71b0de 412 item = list_entry(pos, struct arg_item, list);
46a0613f
CC
413 if (!strcasecmp(item->conf.name, name))
414 return item;
415 }
416
417 /* Item does not already exists, create it */
cc71b0de 418 item = xcalloc(sizeof(*item), 1);
46a0613f
CC
419 duplicate_conf(&item->conf, &default_conf_info);
420 item->conf.name = xstrdup(name);
421
8966a394 422 list_add_tail(&item->list, &conf_head);
46a0613f
CC
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;
cc71b0de 472 struct arg_item *item;
46a0613f
CC
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:
ef1177d1 525 die("BUG: trailer.c: unhandled type %d", type);
46a0613f
CC
526 }
527 return 0;
528}
f0a90b4e 529
cc71b0de 530static const char *token_from_item(struct arg_item *item, char *tok)
63ab3f34
JT
531{
532 if (item->conf.key)
533 return item->conf.key;
534 if (tok)
535 return tok;
536 return item->conf.name;
537}
538
cc71b0de 539static int token_matches_item(const char *tok, struct arg_item *item, int tok_len)
63ab3f34
JT
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)
f0a90b4e
CC
548{
549 size_t len;
550 struct strbuf seps = STRBUF_INIT;
cc71b0de 551 struct arg_item *item;
63ab3f34
JT
552 int tok_len;
553 struct list_head *pos;
554
f0a90b4e
CC
555 strbuf_addstr(&seps, separators);
556 strbuf_addch(&seps, '=');
557 len = strcspn(trailer, seps.buf);
558 strbuf_release(&seps);
d52adf1f
CC
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 }
f0a90b4e
CC
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 }
f0a90b4e
CC
574
575 /* Lookup if the token matches something in the config */
63ab3f34 576 tok_len = token_len_without_separator(tok->buf, tok->len);
cc71b0de
JT
577 if (conf)
578 *conf = &default_conf_info;
8966a394 579 list_for_each(pos, &conf_head) {
cc71b0de 580 item = list_entry(pos, struct arg_item, list);
63ab3f34
JT
581 if (token_matches_item(tok->buf, item, tok_len)) {
582 char *tok_buf = strbuf_detach(tok, NULL);
cc71b0de
JT
583 if (conf)
584 *conf = &item->conf;
63ab3f34
JT
585 strbuf_addstr(tok, token_from_item(item, tok_buf));
586 free(tok_buf);
587 break;
588 }
f0a90b4e
CC
589 }
590
63ab3f34 591 return 0;
f0a90b4e
CC
592}
593
cc71b0de 594static void add_trailer_item(struct list_head *head, char *tok, char *val)
f0a90b4e 595{
63ab3f34
JT
596 struct trailer_item *new = xcalloc(sizeof(*new), 1);
597 new->token = tok;
598 new->value = val;
8966a394 599 list_add_tail(&new->list, head);
f0a90b4e
CC
600}
601
cc71b0de
JT
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
8966a394
JT
612static void process_command_line_args(struct list_head *arg_head,
613 struct string_list *trailers)
f0a90b4e 614{
f0a90b4e 615 struct string_list_item *tr;
cc71b0de 616 struct arg_item *item;
63ab3f34
JT
617 struct strbuf tok = STRBUF_INIT;
618 struct strbuf val = STRBUF_INIT;
619 const struct conf_info *conf;
8966a394 620 struct list_head *pos;
85039fb6 621
cc71b0de 622 /* Add an arg item for each configured trailer with a command */
8966a394 623 list_for_each(pos, &conf_head) {
cc71b0de 624 item = list_entry(pos, struct arg_item, list);
63ab3f34 625 if (item->conf.command)
cc71b0de
JT
626 add_arg_item(arg_head,
627 xstrdup(token_from_item(item, NULL)),
628 xstrdup(""),
629 &item->conf);
85039fb6 630 }
f0a90b4e 631
cc71b0de 632 /* Add an arg item for each trailer on the command line */
f0a90b4e 633 for_each_string_list_item(tr, trailers) {
63ab3f34 634 if (!parse_trailer(&tok, &val, &conf, tr->string))
cc71b0de
JT
635 add_arg_item(arg_head,
636 strbuf_detach(&tok, NULL),
637 strbuf_detach(&val, NULL),
638 conf);
f0a90b4e 639 }
f0a90b4e 640}
2013d850
CC
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{
5c99995d
CC
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;
2013d850
CC
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 */
5c99995d 701 for (start = count - 1; start >= end_of_title; start--) {
2013d850
CC
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
61cfef4c
CC
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
2013d850
CC
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
d0d2344a 746static void print_lines(FILE *outfile, struct strbuf **lines, int start, int end)
2013d850
CC
747{
748 int i;
749 for (i = start; lines[i] && i < end; i++)
d0d2344a 750 fprintf(outfile, "%s", lines[i]->buf);
2013d850
CC
751}
752
d0d2344a
TK
753static int process_input_file(FILE *outfile,
754 struct strbuf **lines,
8966a394 755 struct list_head *head)
2013d850
CC
756{
757 int count = 0;
61cfef4c 758 int patch_start, trailer_start, trailer_end, i;
63ab3f34
JT
759 struct strbuf tok = STRBUF_INIT;
760 struct strbuf val = STRBUF_INIT;
2013d850
CC
761
762 /* Get the line count */
763 while (lines[count])
764 count++;
765
766 patch_start = find_patch_start(lines, count);
61cfef4c
CC
767 trailer_end = find_trailer_end(lines, patch_start);
768 trailer_start = find_trailer_start(lines, trailer_end);
2013d850
CC
769
770 /* Print lines before the trailers as is */
d0d2344a 771 print_lines(outfile, lines, 0, trailer_start);
2013d850
CC
772
773 if (!has_blank_line_before(lines, trailer_start - 1))
d0d2344a 774 fprintf(outfile, "\n");
2013d850
CC
775
776 /* Parse trailer lines */
61cfef4c 777 for (i = trailer_start; i < trailer_end; i++) {
63ab3f34 778 if (lines[i]->buf[0] != comment_line_char &&
cc71b0de 779 !parse_trailer(&tok, &val, NULL, lines[i]->buf))
63ab3f34
JT
780 add_trailer_item(head,
781 strbuf_detach(&tok, NULL),
cc71b0de 782 strbuf_detach(&val, NULL));
2013d850
CC
783 }
784
61cfef4c 785 return trailer_end;
2013d850 786}
b1d78d77 787
8966a394 788static void free_all(struct list_head *head)
b1d78d77 789{
8966a394
JT
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));
b1d78d77
CC
794 }
795}
796
e1f89863
TK
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)
b1d78d77 829{
8966a394
JT
830 LIST_HEAD(head);
831 LIST_HEAD(arg_head);
b1d78d77 832 struct strbuf **lines;
61cfef4c 833 int trailer_end;
d0d2344a 834 FILE *outfile = stdout;
b1d78d77
CC
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
e1f89863
TK
842 if (in_place)
843 outfile = create_in_place_tempfile(file);
844
b1d78d77 845 /* Print the lines before the trailers */
8966a394 846 trailer_end = process_input_file(outfile, lines, &head);
b1d78d77 847
8966a394 848 process_command_line_args(&arg_head, trailers);
b1d78d77 849
8966a394 850 process_trailers_lists(&head, &arg_head);
b1d78d77 851
8966a394 852 print_all(outfile, &head, trim_empty);
b1d78d77 853
8966a394 854 free_all(&head);
b1d78d77
CC
855
856 /* Print the lines after the trailers as is */
d0d2344a 857 print_lines(outfile, lines, trailer_end, INT_MAX);
b1d78d77 858
e1f89863
TK
859 if (in_place)
860 if (rename_tempfile(&trailers_tempfile, file))
861 die_errno(_("could not rename temporary file to %s"), file);
862
b1d78d77
CC
863 strbuf_list_free(lines);
864}