]> git.ipfire.org Git - thirdparty/git.git/blame - trailer.c
trailer: clarify failure modes in parse_trailer
[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
fdbf4510
JT
546/*
547 * Return the location of the first separator in line, or -1 if there is no
548 * separator.
549 */
550static int find_separator(const char *line, const char *separators)
551{
552 int loc = strcspn(line, separators);
553 if (!line[loc])
554 return -1;
555 return loc;
556}
557
558/*
559 * Obtain the token, value, and conf from the given trailer.
560 *
561 * separator_pos must not be 0, since the token cannot be an empty string.
562 *
563 * If separator_pos is -1, interpret the whole trailer as a token.
564 */
565static void parse_trailer(struct strbuf *tok, struct strbuf *val,
566 const struct conf_info **conf, const char *trailer,
567 int separator_pos)
f0a90b4e 568{
cc71b0de 569 struct arg_item *item;
63ab3f34
JT
570 int tok_len;
571 struct list_head *pos;
572
fdbf4510
JT
573 if (separator_pos != -1) {
574 strbuf_add(tok, trailer, separator_pos);
f0a90b4e 575 strbuf_trim(tok);
fdbf4510 576 strbuf_addstr(val, trailer + separator_pos + 1);
f0a90b4e
CC
577 strbuf_trim(val);
578 } else {
579 strbuf_addstr(tok, trailer);
580 strbuf_trim(tok);
581 }
f0a90b4e
CC
582
583 /* Lookup if the token matches something in the config */
63ab3f34 584 tok_len = token_len_without_separator(tok->buf, tok->len);
cc71b0de
JT
585 if (conf)
586 *conf = &default_conf_info;
8966a394 587 list_for_each(pos, &conf_head) {
cc71b0de 588 item = list_entry(pos, struct arg_item, list);
63ab3f34
JT
589 if (token_matches_item(tok->buf, item, tok_len)) {
590 char *tok_buf = strbuf_detach(tok, NULL);
cc71b0de
JT
591 if (conf)
592 *conf = &item->conf;
63ab3f34
JT
593 strbuf_addstr(tok, token_from_item(item, tok_buf));
594 free(tok_buf);
595 break;
596 }
f0a90b4e 597 }
f0a90b4e
CC
598}
599
cc71b0de 600static void add_trailer_item(struct list_head *head, char *tok, char *val)
f0a90b4e 601{
63ab3f34
JT
602 struct trailer_item *new = xcalloc(sizeof(*new), 1);
603 new->token = tok;
604 new->value = val;
8966a394 605 list_add_tail(&new->list, head);
f0a90b4e
CC
606}
607
cc71b0de
JT
608static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
609 const struct conf_info *conf)
610{
611 struct arg_item *new = xcalloc(sizeof(*new), 1);
612 new->token = tok;
613 new->value = val;
614 duplicate_conf(&new->conf, conf);
615 list_add_tail(&new->list, arg_head);
616}
617
8966a394
JT
618static void process_command_line_args(struct list_head *arg_head,
619 struct string_list *trailers)
f0a90b4e 620{
f0a90b4e 621 struct string_list_item *tr;
cc71b0de 622 struct arg_item *item;
63ab3f34
JT
623 struct strbuf tok = STRBUF_INIT;
624 struct strbuf val = STRBUF_INIT;
625 const struct conf_info *conf;
8966a394 626 struct list_head *pos;
85039fb6 627
fdbf4510
JT
628 /*
629 * In command-line arguments, '=' is accepted (in addition to the
630 * separators that are defined).
631 */
632 char *cl_separators = xstrfmt("=%s", separators);
633
cc71b0de 634 /* Add an arg item for each configured trailer with a command */
8966a394 635 list_for_each(pos, &conf_head) {
cc71b0de 636 item = list_entry(pos, struct arg_item, list);
63ab3f34 637 if (item->conf.command)
cc71b0de
JT
638 add_arg_item(arg_head,
639 xstrdup(token_from_item(item, NULL)),
640 xstrdup(""),
641 &item->conf);
85039fb6 642 }
f0a90b4e 643
cc71b0de 644 /* Add an arg item for each trailer on the command line */
f0a90b4e 645 for_each_string_list_item(tr, trailers) {
fdbf4510
JT
646 int separator_pos = find_separator(tr->string, cl_separators);
647 if (separator_pos == 0) {
648 struct strbuf sb = STRBUF_INIT;
649 strbuf_addstr(&sb, tr->string);
650 strbuf_trim(&sb);
651 error(_("empty trailer token in trailer '%.*s'"),
652 (int) sb.len, sb.buf);
653 strbuf_release(&sb);
654 } else {
655 parse_trailer(&tok, &val, &conf, tr->string,
656 separator_pos);
cc71b0de
JT
657 add_arg_item(arg_head,
658 strbuf_detach(&tok, NULL),
659 strbuf_detach(&val, NULL),
660 conf);
fdbf4510 661 }
f0a90b4e 662 }
fdbf4510
JT
663
664 free(cl_separators);
f0a90b4e 665}
2013d850
CC
666
667static struct strbuf **read_input_file(const char *file)
668{
669 struct strbuf **lines;
670 struct strbuf sb = STRBUF_INIT;
671
672 if (file) {
673 if (strbuf_read_file(&sb, file, 0) < 0)
674 die_errno(_("could not read input file '%s'"), file);
675 } else {
676 if (strbuf_read(&sb, fileno(stdin), 0) < 0)
677 die_errno(_("could not read from stdin"));
678 }
679
680 lines = strbuf_split(&sb, '\n');
681
682 strbuf_release(&sb);
683
684 return lines;
685}
686
687/*
688 * Return the (0 based) index of the start of the patch or the line
689 * count if there is no patch in the message.
690 */
691static int find_patch_start(struct strbuf **lines, int count)
692{
693 int i;
694
695 /* Get the start of the patch part if any */
696 for (i = 0; i < count; i++) {
697 if (starts_with(lines[i]->buf, "---"))
698 return i;
699 }
700
701 return count;
702}
703
704/*
705 * Return the (0 based) index of the first trailer line or count if
706 * there are no trailers. Trailers are searched only in the lines from
707 * index (count - 1) down to index 0.
708 */
709static int find_trailer_start(struct strbuf **lines, int count)
710{
5c99995d
CC
711 int start, end_of_title, only_spaces = 1;
712
713 /* The first paragraph is the title and cannot be trailers */
714 for (start = 0; start < count; start++) {
715 if (lines[start]->buf[0] == comment_line_char)
716 continue;
717 if (contains_only_spaces(lines[start]->buf))
718 break;
719 }
720 end_of_title = start;
2013d850
CC
721
722 /*
723 * Get the start of the trailers by looking starting from the end
724 * for a line with only spaces before lines with one separator.
725 */
5c99995d 726 for (start = count - 1; start >= end_of_title; start--) {
2013d850
CC
727 if (lines[start]->buf[0] == comment_line_char)
728 continue;
729 if (contains_only_spaces(lines[start]->buf)) {
730 if (only_spaces)
731 continue;
732 return start + 1;
733 }
734 if (strcspn(lines[start]->buf, separators) < lines[start]->len) {
735 if (only_spaces)
736 only_spaces = 0;
737 continue;
738 }
739 return count;
740 }
741
742 return only_spaces ? count : 0;
743}
744
61cfef4c
CC
745/* Get the index of the end of the trailers */
746static int find_trailer_end(struct strbuf **lines, int patch_start)
747{
748 struct strbuf sb = STRBUF_INIT;
749 int i, ignore_bytes;
750
751 for (i = 0; i < patch_start; i++)
752 strbuf_addbuf(&sb, lines[i]);
753 ignore_bytes = ignore_non_trailer(&sb);
754 strbuf_release(&sb);
755 for (i = patch_start - 1; i >= 0 && ignore_bytes > 0; i--)
756 ignore_bytes -= lines[i]->len;
757
758 return i + 1;
759}
760
2013d850
CC
761static int has_blank_line_before(struct strbuf **lines, int start)
762{
763 for (;start >= 0; start--) {
764 if (lines[start]->buf[0] == comment_line_char)
765 continue;
766 return contains_only_spaces(lines[start]->buf);
767 }
768 return 0;
769}
770
d0d2344a 771static void print_lines(FILE *outfile, struct strbuf **lines, int start, int end)
2013d850
CC
772{
773 int i;
774 for (i = start; lines[i] && i < end; i++)
d0d2344a 775 fprintf(outfile, "%s", lines[i]->buf);
2013d850
CC
776}
777
d0d2344a
TK
778static int process_input_file(FILE *outfile,
779 struct strbuf **lines,
8966a394 780 struct list_head *head)
2013d850
CC
781{
782 int count = 0;
61cfef4c 783 int patch_start, trailer_start, trailer_end, i;
63ab3f34
JT
784 struct strbuf tok = STRBUF_INIT;
785 struct strbuf val = STRBUF_INIT;
2013d850
CC
786
787 /* Get the line count */
788 while (lines[count])
789 count++;
790
791 patch_start = find_patch_start(lines, count);
61cfef4c
CC
792 trailer_end = find_trailer_end(lines, patch_start);
793 trailer_start = find_trailer_start(lines, trailer_end);
2013d850
CC
794
795 /* Print lines before the trailers as is */
d0d2344a 796 print_lines(outfile, lines, 0, trailer_start);
2013d850
CC
797
798 if (!has_blank_line_before(lines, trailer_start - 1))
d0d2344a 799 fprintf(outfile, "\n");
2013d850
CC
800
801 /* Parse trailer lines */
61cfef4c 802 for (i = trailer_start; i < trailer_end; i++) {
fdbf4510
JT
803 int separator_pos;
804 if (lines[i]->buf[0] == comment_line_char)
805 continue;
806 separator_pos = find_separator(lines[i]->buf, separators);
807 if (separator_pos >= 1) {
808 parse_trailer(&tok, &val, NULL, lines[i]->buf,
809 separator_pos);
63ab3f34
JT
810 add_trailer_item(head,
811 strbuf_detach(&tok, NULL),
cc71b0de 812 strbuf_detach(&val, NULL));
fdbf4510 813 }
2013d850
CC
814 }
815
61cfef4c 816 return trailer_end;
2013d850 817}
b1d78d77 818
8966a394 819static void free_all(struct list_head *head)
b1d78d77 820{
8966a394
JT
821 struct list_head *pos, *p;
822 list_for_each_safe(pos, p, head) {
823 list_del(pos);
824 free_trailer_item(list_entry(pos, struct trailer_item, list));
b1d78d77
CC
825 }
826}
827
e1f89863
TK
828static struct tempfile trailers_tempfile;
829
830static FILE *create_in_place_tempfile(const char *file)
831{
832 struct stat st;
833 struct strbuf template = STRBUF_INIT;
834 const char *tail;
835 FILE *outfile;
836
837 if (stat(file, &st))
838 die_errno(_("could not stat %s"), file);
839 if (!S_ISREG(st.st_mode))
840 die(_("file %s is not a regular file"), file);
841 if (!(st.st_mode & S_IWUSR))
842 die(_("file %s is not writable by user"), file);
843
844 /* Create temporary file in the same directory as the original */
845 tail = strrchr(file, '/');
846 if (tail != NULL)
847 strbuf_add(&template, file, tail - file + 1);
848 strbuf_addstr(&template, "git-interpret-trailers-XXXXXX");
849
850 xmks_tempfile_m(&trailers_tempfile, template.buf, st.st_mode);
851 strbuf_release(&template);
852 outfile = fdopen_tempfile(&trailers_tempfile, "w");
853 if (!outfile)
854 die_errno(_("could not open temporary file"));
855
856 return outfile;
857}
858
859void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)
b1d78d77 860{
8966a394
JT
861 LIST_HEAD(head);
862 LIST_HEAD(arg_head);
b1d78d77 863 struct strbuf **lines;
61cfef4c 864 int trailer_end;
d0d2344a 865 FILE *outfile = stdout;
b1d78d77
CC
866
867 /* Default config must be setup first */
868 git_config(git_trailer_default_config, NULL);
869 git_config(git_trailer_config, NULL);
870
871 lines = read_input_file(file);
872
e1f89863
TK
873 if (in_place)
874 outfile = create_in_place_tempfile(file);
875
b1d78d77 876 /* Print the lines before the trailers */
8966a394 877 trailer_end = process_input_file(outfile, lines, &head);
b1d78d77 878
8966a394 879 process_command_line_args(&arg_head, trailers);
b1d78d77 880
8966a394 881 process_trailers_lists(&head, &arg_head);
b1d78d77 882
8966a394 883 print_all(outfile, &head, trim_empty);
b1d78d77 884
8966a394 885 free_all(&head);
b1d78d77
CC
886
887 /* Print the lines after the trailers as is */
d0d2344a 888 print_lines(outfile, lines, trailer_end, INT_MAX);
b1d78d77 889
e1f89863
TK
890 if (in_place)
891 if (rename_tempfile(&trailers_tempfile, file))
892 die_errno(_("could not rename temporary file to %s"), file);
893
b1d78d77
CC
894 strbuf_list_free(lines);
895}