Coverage Report

Created: 2026-10-17 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/trailer.c
Line
Count
Source
1
#define USE_THE_REPOSITORY_VARIABLE
2
3
#include "git-compat-util.h"
4
#include "config.h"
5
#include "environment.h"
6
#include "gettext.h"
7
#include "string-list.h"
8
#include "run-command.h"
9
#include "commit.h"
10
#include "strvec.h"
11
#include "trailer.h"
12
#include "list.h"
13
#include "tempfile.h"
14
15
/*
16
 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
17
 */
18
19
struct trailer_block {
20
  /*
21
   * True if there is a blank line before the location pointed to by
22
   * "start".
23
   */
24
  int blank_line_before_trailer;
25
26
  /*
27
   * The locations of the start and end positions of the trailer block
28
   * found, as offsets from the beginning of the source text from which
29
   * this trailer block was parsed. If no trailer block is found, these
30
   * are both set to 0.
31
   */
32
  size_t start, end;
33
34
  /*
35
   * Array of trailers found.
36
   */
37
  char **trailers;
38
  size_t trailer_nr;
39
};
40
41
struct conf_info {
42
  char *name;
43
  char *key;
44
  char *command;
45
  char *cmd;
46
  enum trailer_where where;
47
  enum trailer_if_exists if_exists;
48
  enum trailer_if_missing if_missing;
49
};
50
51
static struct conf_info default_conf_info;
52
53
struct trailer_item {
54
  struct list_head list;
55
  /*
56
   * If this is not a trailer line, the line is stored in value
57
   * (excluding the terminating newline) and token is NULL.
58
   */
59
  char *token;
60
  char *value;
61
};
62
63
struct arg_item {
64
  struct list_head list;
65
  char *token;
66
  char *value;
67
  struct conf_info conf;
68
};
69
70
static LIST_HEAD(conf_head);
71
72
static const char *separators = ":";
73
74
static int configured;
75
76
0
#define TRAILER_ARG_STRING "$ARG"
77
78
static const char *git_generated_prefixes[] = {
79
  "Signed-off-by: ",
80
  "(cherry picked from commit ",
81
  NULL
82
};
83
84
/* Iterate over the elements of the list. */
85
#define list_for_each_dir(pos, head, is_reverse) \
86
0
  for (pos = is_reverse ? (head)->prev : (head)->next; \
87
0
    pos != (head); \
88
0
    pos = is_reverse ? pos->prev : pos->next)
89
90
static int after_or_end(enum trailer_where where)
91
0
{
92
0
  return (where == WHERE_AFTER) || (where == WHERE_END);
93
0
}
94
95
/*
96
 * Return the length of the string not including any final
97
 * punctuation. E.g., the input "Signed-off-by:" would return
98
 * 13, stripping the trailing punctuation but retaining
99
 * internal punctuation.
100
 */
101
static size_t token_len_without_separator(const char *token, size_t len)
102
0
{
103
0
  while (len > 0 && !isalnum(token[len - 1]))
104
0
    len--;
105
0
  return len;
106
0
}
107
108
static int same_token(struct trailer_item *a, struct arg_item *b)
109
0
{
110
0
  size_t a_len, b_len, min_len;
111
112
0
  if (!a->token)
113
0
    return 0;
114
115
0
  a_len = token_len_without_separator(a->token, strlen(a->token));
116
0
  b_len = token_len_without_separator(b->token, strlen(b->token));
117
0
  min_len = (a_len > b_len) ? b_len : a_len;
118
119
0
  return !strncasecmp(a->token, b->token, min_len);
120
0
}
121
122
static int same_value(struct trailer_item *a, struct arg_item *b)
123
0
{
124
0
  return !strcasecmp(a->value, b->value);
125
0
}
126
127
static int same_trailer(struct trailer_item *a, struct arg_item *b)
128
0
{
129
0
  return same_token(a, b) && same_value(a, b);
130
0
}
131
132
static inline int is_blank_line(const char *str)
133
0
{
134
0
  const char *s = str;
135
0
  while (*s && *s != '\n' && isspace(*s))
136
0
    s++;
137
0
  return !*s || *s == '\n';
138
0
}
139
140
static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)
141
0
{
142
0
  const char *ptr = strstr(sb->buf, a);
143
0
  if (ptr)
144
0
    strbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));
145
0
}
146
147
static void free_trailer_item(struct trailer_item *item)
148
0
{
149
0
  free(item->token);
150
0
  free(item->value);
151
0
  free(item);
152
0
}
153
154
static void free_arg_item(struct arg_item *item)
155
0
{
156
0
  free(item->conf.name);
157
0
  free(item->conf.key);
158
0
  free(item->conf.command);
159
0
  free(item->conf.cmd);
160
0
  free(item->token);
161
0
  free(item->value);
162
0
  free(item);
163
0
}
164
165
static char last_non_space_char(const char *s)
166
0
{
167
0
  int i;
168
0
  for (i = strlen(s) - 1; i >= 0; i--)
169
0
    if (!isspace(s[i]))
170
0
      return s[i];
171
0
  return '\0';
172
0
}
173
174
static struct trailer_item *trailer_from_arg(struct arg_item *arg_tok)
175
0
{
176
0
  struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
177
0
  new_item->token = arg_tok->token;
178
0
  new_item->value = arg_tok->value;
179
0
  arg_tok->token = arg_tok->value = NULL;
180
0
  free_arg_item(arg_tok);
181
0
  return new_item;
182
0
}
183
184
static void add_arg_to_input_list(struct trailer_item *on_tok,
185
          struct arg_item *arg_tok)
186
0
{
187
0
  int aoe = after_or_end(arg_tok->conf.where);
188
0
  struct trailer_item *to_add = trailer_from_arg(arg_tok);
189
0
  if (aoe)
190
0
    list_add(&to_add->list, &on_tok->list);
191
0
  else
192
0
    list_add_tail(&to_add->list, &on_tok->list);
193
0
}
194
195
static int check_if_different(struct trailer_item *in_tok,
196
            struct arg_item *arg_tok,
197
            int check_all,
198
            struct list_head *head)
199
0
{
200
0
  enum trailer_where where = arg_tok->conf.where;
201
0
  struct list_head *next_head;
202
0
  do {
203
0
    if (same_trailer(in_tok, arg_tok))
204
0
      return 0;
205
    /*
206
     * if we want to add a trailer after another one,
207
     * we have to check those before this one
208
     */
209
0
    next_head = after_or_end(where) ? in_tok->list.prev
210
0
            : in_tok->list.next;
211
0
    if (next_head == head)
212
0
      break;
213
0
    in_tok = list_entry(next_head, struct trailer_item, list);
214
0
  } while (check_all);
215
0
  return 1;
216
0
}
217
218
static char *apply_command(struct conf_info *conf, const char *arg)
219
0
{
220
0
  struct strbuf cmd = STRBUF_INIT;
221
0
  struct strbuf buf = STRBUF_INIT;
222
0
  struct child_process cp = CHILD_PROCESS_INIT;
223
0
  char *result;
224
225
0
  if (conf->cmd) {
226
0
    strbuf_addstr(&cmd, conf->cmd);
227
0
    strvec_push(&cp.args, cmd.buf);
228
0
    if (arg)
229
0
      strvec_push(&cp.args, arg);
230
0
  } else if (conf->command) {
231
0
    strbuf_addstr(&cmd, conf->command);
232
0
    if (arg)
233
0
      strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
234
0
    strvec_push(&cp.args, cmd.buf);
235
0
  }
236
0
  strvec_pushv(&cp.env, (const char **)local_repo_env);
237
0
  cp.no_stdin = 1;
238
0
  cp.use_shell = 1;
239
240
0
  if (capture_command(&cp, &buf, 1024)) {
241
0
    error(_("running trailer command '%s' failed"), cmd.buf);
242
0
    strbuf_release(&buf);
243
0
    result = xstrdup("");
244
0
  } else {
245
0
    strbuf_trim(&buf);
246
0
    result = strbuf_detach(&buf, NULL);
247
0
  }
248
249
0
  strbuf_release(&cmd);
250
0
  return result;
251
0
}
252
253
static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
254
0
{
255
0
  if (arg_tok->conf.command || arg_tok->conf.cmd) {
256
0
    char *value_to_free = NULL;
257
0
    char *arg;
258
259
0
    if (arg_tok->value && arg_tok->value[0]) {
260
0
      arg = arg_tok->value;
261
0
    } else {
262
0
      if (in_tok && in_tok->value)
263
0
        arg = xstrdup(in_tok->value);
264
0
      else
265
0
        arg = xstrdup("");
266
0
      value_to_free = arg_tok->value;
267
0
    }
268
269
0
    arg_tok->value = apply_command(&arg_tok->conf, arg);
270
271
0
    free(value_to_free);
272
0
    free(arg);
273
0
  }
274
0
}
275
276
static void apply_arg_if_exists(struct trailer_item *in_tok,
277
        struct arg_item *arg_tok,
278
        struct trailer_item *on_tok,
279
        struct list_head *head)
280
0
{
281
0
  switch (arg_tok->conf.if_exists) {
282
0
  case EXISTS_DO_NOTHING:
283
0
    free_arg_item(arg_tok);
284
0
    break;
285
0
  case EXISTS_REPLACE:
286
0
    apply_item_command(in_tok, arg_tok);
287
0
    add_arg_to_input_list(on_tok, arg_tok);
288
0
    list_del(&in_tok->list);
289
0
    free_trailer_item(in_tok);
290
0
    break;
291
0
  case EXISTS_ADD:
292
0
    apply_item_command(in_tok, arg_tok);
293
0
    add_arg_to_input_list(on_tok, arg_tok);
294
0
    break;
295
0
  case EXISTS_ADD_IF_DIFFERENT:
296
0
    apply_item_command(in_tok, arg_tok);
297
0
    if (check_if_different(in_tok, arg_tok, 1, head))
298
0
      add_arg_to_input_list(on_tok, arg_tok);
299
0
    else
300
0
      free_arg_item(arg_tok);
301
0
    break;
302
0
  case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
303
0
    apply_item_command(in_tok, arg_tok);
304
0
    if (check_if_different(on_tok, arg_tok, 0, head))
305
0
      add_arg_to_input_list(on_tok, arg_tok);
306
0
    else
307
0
      free_arg_item(arg_tok);
308
0
    break;
309
0
  default:
310
0
    BUG("trailer.c: unhandled value %d",
311
0
        arg_tok->conf.if_exists);
312
0
  }
313
0
}
314
315
static void apply_arg_if_missing(struct list_head *head,
316
         struct arg_item *arg_tok)
317
0
{
318
0
  enum trailer_where where;
319
0
  struct trailer_item *to_add;
320
321
0
  switch (arg_tok->conf.if_missing) {
322
0
  case MISSING_DO_NOTHING:
323
0
    free_arg_item(arg_tok);
324
0
    break;
325
0
  case MISSING_ADD:
326
0
    where = arg_tok->conf.where;
327
0
    apply_item_command(NULL, arg_tok);
328
0
    to_add = trailer_from_arg(arg_tok);
329
0
    if (after_or_end(where))
330
0
      list_add_tail(&to_add->list, head);
331
0
    else
332
0
      list_add(&to_add->list, head);
333
0
    break;
334
0
  default:
335
0
    BUG("trailer.c: unhandled value %d",
336
0
        arg_tok->conf.if_missing);
337
0
  }
338
0
}
339
340
static int find_same_and_apply_arg(struct list_head *head,
341
           struct arg_item *arg_tok)
342
0
{
343
0
  struct list_head *pos;
344
0
  struct trailer_item *in_tok;
345
0
  struct trailer_item *on_tok;
346
347
0
  enum trailer_where where = arg_tok->conf.where;
348
0
  int middle = (where == WHERE_AFTER) || (where == WHERE_BEFORE);
349
0
  int backwards = after_or_end(where);
350
0
  struct trailer_item *start_tok;
351
352
0
  if (list_empty(head))
353
0
    return 0;
354
355
0
  start_tok = list_entry(backwards ? head->prev : head->next,
356
0
             struct trailer_item,
357
0
             list);
358
359
0
  list_for_each_dir(pos, head, backwards) {
360
0
    in_tok = list_entry(pos, struct trailer_item, list);
361
0
    if (!same_token(in_tok, arg_tok))
362
0
      continue;
363
0
    on_tok = middle ? in_tok : start_tok;
364
0
    apply_arg_if_exists(in_tok, arg_tok, on_tok, head);
365
0
    return 1;
366
0
  }
367
0
  return 0;
368
0
}
369
370
void process_trailers_lists(struct list_head *head,
371
          struct list_head *arg_head)
372
0
{
373
0
  struct list_head *pos, *p;
374
0
  struct arg_item *arg_tok;
375
376
0
  list_for_each_safe(pos, p, arg_head) {
377
0
    int applied = 0;
378
0
    arg_tok = list_entry(pos, struct arg_item, list);
379
380
0
    list_del(pos);
381
382
0
    applied = find_same_and_apply_arg(head, arg_tok);
383
384
0
    if (!applied)
385
0
      apply_arg_if_missing(head, arg_tok);
386
0
  }
387
0
}
388
389
int trailer_set_where(enum trailer_where *item, const char *value)
390
0
{
391
0
  if (!value)
392
0
    *item = WHERE_DEFAULT;
393
0
  else if (!strcasecmp("after", value))
394
0
    *item = WHERE_AFTER;
395
0
  else if (!strcasecmp("before", value))
396
0
    *item = WHERE_BEFORE;
397
0
  else if (!strcasecmp("end", value))
398
0
    *item = WHERE_END;
399
0
  else if (!strcasecmp("start", value))
400
0
    *item = WHERE_START;
401
0
  else
402
0
    return -1;
403
0
  return 0;
404
0
}
405
406
int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)
407
0
{
408
0
  if (!value)
409
0
    *item = EXISTS_DEFAULT;
410
0
  else if (!strcasecmp("addIfDifferent", value))
411
0
    *item = EXISTS_ADD_IF_DIFFERENT;
412
0
  else if (!strcasecmp("addIfDifferentNeighbor", value))
413
0
    *item = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
414
0
  else if (!strcasecmp("add", value))
415
0
    *item = EXISTS_ADD;
416
0
  else if (!strcasecmp("replace", value))
417
0
    *item = EXISTS_REPLACE;
418
0
  else if (!strcasecmp("doNothing", value))
419
0
    *item = EXISTS_DO_NOTHING;
420
0
  else
421
0
    return -1;
422
0
  return 0;
423
0
}
424
425
int trailer_set_if_missing(enum trailer_if_missing *item, const char *value)
426
0
{
427
0
  if (!value)
428
0
    *item = MISSING_DEFAULT;
429
0
  else if (!strcasecmp("doNothing", value))
430
0
    *item = MISSING_DO_NOTHING;
431
0
  else if (!strcasecmp("add", value))
432
0
    *item = MISSING_ADD;
433
0
  else
434
0
    return -1;
435
0
  return 0;
436
0
}
437
438
static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
439
0
{
440
0
  *dst = *src;
441
0
  dst->name = xstrdup_or_null(src->name);
442
0
  dst->key = xstrdup_or_null(src->key);
443
0
  dst->command = xstrdup_or_null(src->command);
444
0
  dst->cmd = xstrdup_or_null(src->cmd);
445
0
}
446
447
static struct arg_item *get_conf_item(const char *name)
448
0
{
449
0
  struct list_head *pos;
450
0
  struct arg_item *item;
451
452
  /* Look up item with same name */
453
0
  list_for_each(pos, &conf_head) {
454
0
    item = list_entry(pos, struct arg_item, list);
455
0
    if (!strcasecmp(item->conf.name, name))
456
0
      return item;
457
0
  }
458
459
  /* Item does not already exists, create it */
460
0
  CALLOC_ARRAY(item, 1);
461
0
  duplicate_conf(&item->conf, &default_conf_info);
462
0
  item->conf.name = xstrdup(name);
463
464
0
  list_add_tail(&item->list, &conf_head);
465
466
0
  return item;
467
0
}
468
469
enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
470
      TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
471
472
static struct {
473
  const char *name;
474
  enum trailer_info_type type;
475
} trailer_config_items[] = {
476
  { "key", TRAILER_KEY },
477
  { "command", TRAILER_COMMAND },
478
  { "cmd", TRAILER_CMD },
479
  { "where", TRAILER_WHERE },
480
  { "ifexists", TRAILER_IF_EXISTS },
481
  { "ifmissing", TRAILER_IF_MISSING }
482
};
483
484
static int git_trailer_default_config(const char *conf_key, const char *value,
485
              const struct config_context *ctx UNUSED,
486
              void *cb UNUSED)
487
0
{
488
0
  const char *trailer_item, *variable_name;
489
490
0
  if (!skip_prefix(conf_key, "trailer.", &trailer_item))
491
0
    return 0;
492
493
0
  variable_name = strrchr(trailer_item, '.');
494
0
  if (!variable_name) {
495
0
    if (!strcmp(trailer_item, "where")) {
496
0
      if (trailer_set_where(&default_conf_info.where,
497
0
                value) < 0)
498
0
        warning(_("unknown value '%s' for key '%s'"),
499
0
          value, conf_key);
500
0
    } else if (!strcmp(trailer_item, "ifexists")) {
501
0
      if (trailer_set_if_exists(&default_conf_info.if_exists,
502
0
              value) < 0)
503
0
        warning(_("unknown value '%s' for key '%s'"),
504
0
          value, conf_key);
505
0
    } else if (!strcmp(trailer_item, "ifmissing")) {
506
0
      if (trailer_set_if_missing(&default_conf_info.if_missing,
507
0
               value) < 0)
508
0
        warning(_("unknown value '%s' for key '%s'"),
509
0
          value, conf_key);
510
0
    } else if (!strcmp(trailer_item, "separators")) {
511
0
      if (!value)
512
0
        return config_error_nonbool(conf_key);
513
0
      separators = xstrdup(value);
514
0
    }
515
0
  }
516
0
  return 0;
517
0
}
518
519
static int git_trailer_config(const char *conf_key, const char *value,
520
            const struct config_context *ctx UNUSED,
521
            void *cb UNUSED)
522
0
{
523
0
  const char *trailer_item, *variable_name;
524
0
  struct arg_item *item;
525
0
  struct conf_info *conf;
526
0
  char *name = NULL;
527
0
  enum trailer_info_type type;
528
529
0
  if (!skip_prefix(conf_key, "trailer.", &trailer_item))
530
0
    return 0;
531
532
0
  variable_name = strrchr(trailer_item, '.');
533
0
  if (!variable_name)
534
0
    return 0;
535
536
0
  variable_name++;
537
0
  for (size_t i = 0; i < ARRAY_SIZE(trailer_config_items); i++) {
538
0
    if (strcmp(trailer_config_items[i].name, variable_name))
539
0
      continue;
540
0
    name = xstrndup(trailer_item,  variable_name - trailer_item - 1);
541
0
    type = trailer_config_items[i].type;
542
0
    break;
543
0
  }
544
545
0
  if (!name)
546
0
    return 0;
547
548
0
  item = get_conf_item(name);
549
0
  conf = &item->conf;
550
0
  free(name);
551
552
0
  switch (type) {
553
0
  case TRAILER_KEY:
554
0
    if (conf->key)
555
0
      warning(_("more than one %s"), conf_key);
556
0
    if (!value)
557
0
      return config_error_nonbool(conf_key);
558
0
    conf->key = xstrdup(value);
559
0
    break;
560
0
  case TRAILER_COMMAND:
561
0
    if (conf->command)
562
0
      warning(_("more than one %s"), conf_key);
563
0
    if (!value)
564
0
      return config_error_nonbool(conf_key);
565
0
    conf->command = xstrdup(value);
566
0
    break;
567
0
  case TRAILER_CMD:
568
0
    if (conf->cmd)
569
0
      warning(_("more than one %s"), conf_key);
570
0
    if (!value)
571
0
      return config_error_nonbool(conf_key);
572
0
    conf->cmd = xstrdup(value);
573
0
    break;
574
0
  case TRAILER_WHERE:
575
0
    if (trailer_set_where(&conf->where, value))
576
0
      warning(_("unknown value '%s' for key '%s'"), value, conf_key);
577
0
    break;
578
0
  case TRAILER_IF_EXISTS:
579
0
    if (trailer_set_if_exists(&conf->if_exists, value))
580
0
      warning(_("unknown value '%s' for key '%s'"), value, conf_key);
581
0
    break;
582
0
  case TRAILER_IF_MISSING:
583
0
    if (trailer_set_if_missing(&conf->if_missing, value))
584
0
      warning(_("unknown value '%s' for key '%s'"), value, conf_key);
585
0
    break;
586
0
  default:
587
0
    BUG("trailer.c: unhandled type %d", type);
588
0
  }
589
0
  return 0;
590
0
}
591
592
void trailer_config_init(void)
593
0
{
594
0
  if (configured)
595
0
    return;
596
597
  /* Default config must be setup first */
598
0
  default_conf_info.where = WHERE_END;
599
0
  default_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
600
0
  default_conf_info.if_missing = MISSING_ADD;
601
0
  repo_config(the_repository, git_trailer_default_config, NULL);
602
0
  repo_config(the_repository, git_trailer_config, NULL);
603
0
  configured = 1;
604
0
}
605
606
static const char *token_from_item(struct arg_item *item, char *tok)
607
0
{
608
0
  if (item->conf.key)
609
0
    return item->conf.key;
610
0
  if (tok)
611
0
    return tok;
612
0
  return item->conf.name;
613
0
}
614
615
static int token_matches_item(const char *tok, struct arg_item *item, size_t tok_len)
616
0
{
617
0
  if (!strncasecmp(tok, item->conf.name, tok_len))
618
0
    return 1;
619
0
  return item->conf.key ? !strncasecmp(tok, item->conf.key, tok_len) : 0;
620
0
}
621
622
/*
623
 * If the given line is of the form
624
 * "<token><optional whitespace><separator>..." or "<separator>...", return the
625
 * location of the separator. Otherwise, return -1.  The optional whitespace
626
 * is allowed there primarily to allow things like "Bug #43" where <token> is
627
 * "Bug" and <separator> is "#".
628
 *
629
 * The separator-starts-line case (in which this function returns 0) is
630
 * distinguished from the non-well-formed-line case (in which this function
631
 * returns -1) because some callers of this function need such a distinction.
632
 */
633
static ssize_t find_separator(const char *line, const char *separators)
634
0
{
635
0
  int whitespace_found = 0;
636
0
  const char *c;
637
0
  for (c = line; *c; c++) {
638
0
    if (strchr(separators, *c)) {
639
      /* avoid accidental URL matches */
640
0
      if (!whitespace_found && starts_with(c, "://"))
641
0
        return -1;
642
0
      return c - line;
643
0
    }
644
0
    if (!whitespace_found && (isalnum(*c) || *c == '-'))
645
0
      continue;
646
0
    if (c != line && (*c == ' ' || *c == '\t')) {
647
0
      whitespace_found = 1;
648
0
      continue;
649
0
    }
650
0
    break;
651
0
  }
652
0
  return -1;
653
0
}
654
655
/*
656
 * Obtain the token, value, and conf from the given trailer.
657
 *
658
 * separator_pos must not be 0, since the token cannot be an empty string.
659
 *
660
 * If separator_pos is -1, interpret the whole trailer as a token.
661
 */
662
static void parse_trailer(struct strbuf *tok, struct strbuf *val,
663
       const struct conf_info **conf, const char *trailer,
664
       ssize_t separator_pos)
665
0
{
666
0
  struct arg_item *item;
667
0
  size_t tok_len;
668
0
  struct list_head *pos;
669
670
0
  if (separator_pos != -1) {
671
0
    strbuf_add(tok, trailer, separator_pos);
672
0
    strbuf_trim(tok);
673
0
    strbuf_addstr(val, trailer + separator_pos + 1);
674
0
    strbuf_trim(val);
675
0
  } else {
676
0
    strbuf_addstr(tok, trailer);
677
0
    strbuf_trim(tok);
678
0
  }
679
680
  /* Lookup if the token matches something in the config */
681
0
  tok_len = token_len_without_separator(tok->buf, tok->len);
682
0
  if (conf)
683
0
    *conf = &default_conf_info;
684
0
  list_for_each(pos, &conf_head) {
685
0
    item = list_entry(pos, struct arg_item, list);
686
0
    if (token_matches_item(tok->buf, item, tok_len)) {
687
0
      char *tok_buf = strbuf_detach(tok, NULL);
688
0
      if (conf)
689
0
        *conf = &item->conf;
690
0
      strbuf_addstr(tok, token_from_item(item, tok_buf));
691
0
      free(tok_buf);
692
0
      break;
693
0
    }
694
0
  }
695
0
}
696
697
static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
698
               char *val)
699
0
{
700
0
  struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
701
0
  new_item->token = tok;
702
0
  new_item->value = val;
703
0
  list_add_tail(&new_item->list, head);
704
0
  return new_item;
705
0
}
706
707
static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
708
       const struct conf_info *conf,
709
       const struct new_trailer_item *new_trailer_item)
710
0
{
711
0
  struct arg_item *new_item = xcalloc(1, sizeof(*new_item));
712
0
  new_item->token = tok;
713
0
  new_item->value = val;
714
0
  duplicate_conf(&new_item->conf, conf);
715
0
  if (new_trailer_item) {
716
0
    if (new_trailer_item->where != WHERE_DEFAULT)
717
0
      new_item->conf.where = new_trailer_item->where;
718
0
    if (new_trailer_item->if_exists != EXISTS_DEFAULT)
719
0
      new_item->conf.if_exists = new_trailer_item->if_exists;
720
0
    if (new_trailer_item->if_missing != MISSING_DEFAULT)
721
0
      new_item->conf.if_missing = new_trailer_item->if_missing;
722
0
  }
723
0
  list_add_tail(&new_item->list, arg_head);
724
0
}
725
726
void parse_trailers_from_config(struct list_head *config_head)
727
0
{
728
0
  struct arg_item *item;
729
0
  struct list_head *pos;
730
731
  /* Add an arg item for each configured trailer with a command */
732
0
  list_for_each(pos, &conf_head) {
733
0
    item = list_entry(pos, struct arg_item, list);
734
0
    if (item->conf.command)
735
0
      add_arg_item(config_head,
736
0
             xstrdup(token_from_item(item, NULL)),
737
0
             xstrdup(""),
738
0
             &item->conf, NULL);
739
0
  }
740
0
}
741
742
void parse_trailers_from_command_line_args(struct list_head *arg_head,
743
             struct list_head *new_trailer_head)
744
0
{
745
0
  struct strbuf tok = STRBUF_INIT;
746
0
  struct strbuf val = STRBUF_INIT;
747
0
  const struct conf_info *conf;
748
0
  struct list_head *pos;
749
750
  /*
751
   * In command-line arguments, '=' is accepted (in addition to the
752
   * separators that are defined).
753
   */
754
0
  char *cl_separators = xstrfmt("=%s", separators);
755
756
  /* Add an arg item for each trailer on the command line */
757
0
  list_for_each(pos, new_trailer_head) {
758
0
    struct new_trailer_item *tr =
759
0
      list_entry(pos, struct new_trailer_item, list);
760
0
    ssize_t separator_pos = find_separator(tr->text, cl_separators);
761
762
0
    if (separator_pos == 0) {
763
0
      struct strbuf sb = STRBUF_INIT;
764
0
      strbuf_addstr(&sb, tr->text);
765
0
      strbuf_trim(&sb);
766
0
      error(_("empty trailer token in trailer '%.*s'"),
767
0
            (int) sb.len, sb.buf);
768
0
      strbuf_release(&sb);
769
0
    } else {
770
0
      parse_trailer(&tok, &val, &conf, tr->text,
771
0
              separator_pos);
772
0
      add_arg_item(arg_head,
773
0
             strbuf_detach(&tok, NULL),
774
0
             strbuf_detach(&val, NULL),
775
0
             conf, tr);
776
0
    }
777
0
  }
778
779
0
  free(cl_separators);
780
0
}
781
782
int validate_trailer_args(const struct strvec *cli_args)
783
0
{
784
0
  char *cl_separators;
785
0
  int ret = 0;
786
787
0
  trailer_config_init();
788
789
0
  cl_separators = xstrfmt("=%s", separators);
790
791
0
  for (size_t i = 0; i < cli_args->nr; i++) {
792
0
    const char *txt = cli_args->v[i];
793
0
    ssize_t separator_pos;
794
795
0
    if (!*txt) {
796
0
      ret = error(_("empty --trailer argument"));
797
0
      goto out;
798
0
    }
799
0
    separator_pos = find_separator(txt, cl_separators);
800
0
    if (separator_pos == 0) {
801
0
      ret = error(_("invalid trailer '%s': missing key before separator"),
802
0
            txt);
803
0
      goto out;
804
0
    }
805
0
  }
806
0
out:
807
0
  free(cl_separators);
808
0
  return ret;
809
0
}
810
811
static const char *next_line(const char *str)
812
0
{
813
0
  const char *nl = strchrnul(str, '\n');
814
0
  return nl + !!*nl;
815
0
}
816
817
/*
818
 * Return the position of the start of the last line. If len is 0, return -1.
819
 */
820
static ssize_t last_line(const char *buf, size_t len)
821
0
{
822
0
  ssize_t i;
823
0
  if (len == 0)
824
0
    return -1;
825
0
  if (len == 1)
826
0
    return 0;
827
  /*
828
   * Skip the last character (in addition to the null terminator),
829
   * because if the last character is a newline, it is considered as part
830
   * of the last line anyway.
831
   */
832
0
  i = len - 2;
833
834
0
  for (; i >= 0; i--) {
835
0
    if (buf[i] == '\n')
836
0
      return i + 1;
837
0
  }
838
0
  return 0;
839
0
}
840
841
/*
842
 * Find the end of the log message as an offset from the start of the input
843
 * (where callers of this function are interested in looking for a trailers
844
 * block in the same input). We have to consider two categories of content that
845
 * can come at the end of the input which we want to ignore (because they don't
846
 * belong in the log message):
847
 *
848
 * (1) the "patch part" which begins with a "---" divider and has patch
849
 * information (like the output of git-format-patch), and
850
 *
851
 * (2) any trailing comment lines, blank lines like in the output of "git
852
 * commit -v", or stuff below the "cut" (scissor) line.
853
 *
854
 * As a formula, the situation looks like this:
855
 *
856
 *     INPUT = LOG MESSAGE + IGNORED
857
 *
858
 * where IGNORED can be either of the two categories described above. It may be
859
 * that there is nothing to ignore. Now it may be the case that the LOG MESSAGE
860
 * contains a trailer block, but that's not the concern of this function.
861
 */
862
static size_t find_end_of_log_message(const char *input, int no_divider)
863
0
{
864
0
  size_t end;
865
0
  const char *s;
866
867
  /* Assume the naive end of the input is already what we want. */
868
0
  end = strlen(input);
869
870
  /* Optionally skip over any patch part ("---" line and below). */
871
0
  if (!no_divider) {
872
0
    for (s = input; *s; s = next_line(s)) {
873
0
      const char *v;
874
875
0
      if (skip_prefix(s, "---", &v) && isspace(*v)) {
876
0
        end = s - input;
877
0
        break;
878
0
      }
879
0
    }
880
0
  }
881
882
  /* Skip over other ignorable bits. */
883
0
  return end - ignored_log_message_bytes(input, end);
884
0
}
885
886
/*
887
 * Return the position of the first trailer line or len if there are no
888
 * trailers.
889
 */
890
static size_t find_trailer_block_start(const char *buf, size_t len)
891
0
{
892
0
  const char *s;
893
0
  ssize_t end_of_title, l;
894
0
  int only_spaces = 1;
895
0
  int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
896
  /*
897
   * Number of possible continuation lines encountered. This will be
898
   * reset to 0 if we encounter a trailer (since those lines are to be
899
   * considered continuations of that trailer), and added to
900
   * non_trailer_lines if we encounter a non-trailer (since those lines
901
   * are to be considered non-trailers).
902
   */
903
0
  int possible_continuation_lines = 0;
904
905
  /* The first paragraph is the title and cannot be trailers */
906
0
  for (s = buf; s < buf + len; s = next_line(s)) {
907
0
    if (starts_with_mem(s, buf + len - s, comment_line_str))
908
0
      continue;
909
0
    if (is_blank_line(s))
910
0
      break;
911
0
  }
912
0
  end_of_title = s - buf;
913
914
  /*
915
   * Get the start of the trailers by looking starting from the end for a
916
   * blank line before a set of non-blank lines that (i) are all
917
   * trailers, or (ii) contains at least one Git-generated trailer and
918
   * consists of at least 25% trailers.
919
   */
920
0
  for (l = last_line(buf, len);
921
0
       l >= end_of_title;
922
0
       l = last_line(buf, l)) {
923
0
    const char *bol = buf + l;
924
0
    const char **p;
925
0
    ssize_t separator_pos;
926
927
0
    if (starts_with_mem(bol, buf + len - bol, comment_line_str)) {
928
0
      non_trailer_lines += possible_continuation_lines;
929
0
      possible_continuation_lines = 0;
930
0
      continue;
931
0
    }
932
0
    if (is_blank_line(bol)) {
933
0
      if (only_spaces)
934
0
        continue;
935
0
      non_trailer_lines += possible_continuation_lines;
936
0
      if (recognized_prefix &&
937
0
          trailer_lines * 3 >= non_trailer_lines)
938
0
        return next_line(bol) - buf;
939
0
      else if (trailer_lines && !non_trailer_lines)
940
0
        return next_line(bol) - buf;
941
0
      return len;
942
0
    }
943
0
    only_spaces = 0;
944
945
0
    for (p = git_generated_prefixes; *p; p++) {
946
0
      if (starts_with(bol, *p)) {
947
0
        trailer_lines++;
948
0
        possible_continuation_lines = 0;
949
0
        recognized_prefix = 1;
950
0
        goto continue_outer_loop;
951
0
      }
952
0
    }
953
954
0
    separator_pos = find_separator(bol, separators);
955
0
    if (separator_pos >= 1 && !isspace(bol[0])) {
956
0
      struct list_head *pos;
957
958
0
      trailer_lines++;
959
0
      possible_continuation_lines = 0;
960
0
      if (recognized_prefix)
961
0
        continue;
962
0
      list_for_each(pos, &conf_head) {
963
0
        struct arg_item *item;
964
0
        item = list_entry(pos, struct arg_item, list);
965
0
        if (token_matches_item(bol, item,
966
0
                   separator_pos)) {
967
0
          recognized_prefix = 1;
968
0
          break;
969
0
        }
970
0
      }
971
0
    } else if (isspace(bol[0]))
972
0
      possible_continuation_lines++;
973
0
    else {
974
0
      non_trailer_lines++;
975
0
      non_trailer_lines += possible_continuation_lines;
976
0
      possible_continuation_lines = 0;
977
0
    }
978
0
continue_outer_loop:
979
0
    ;
980
0
  }
981
982
0
  return len;
983
0
}
984
985
static int ends_with_blank_line(const char *buf, size_t len)
986
0
{
987
0
  ssize_t ll = last_line(buf, len);
988
0
  if (ll < 0)
989
0
    return 0;
990
0
  return is_blank_line(buf + ll);
991
0
}
992
993
static void unfold_value(struct strbuf *val)
994
0
{
995
0
  size_t i;
996
0
  size_t pos = 0;
997
998
0
  i = 0;
999
0
  while (i < val->len) {
1000
0
    char c = val->buf[i++];
1001
0
    if (c == '\n') {
1002
      /* Collapse continuation down to a single space. */
1003
0
      while (i < val->len && isspace(val->buf[i]))
1004
0
        i++;
1005
0
      c = ' ';
1006
0
    }
1007
0
    val->buf[pos++] = c;
1008
0
  }
1009
0
  strbuf_setlen(val, pos);
1010
1011
  /* Empty lines may have left us with whitespace cruft at the edges */
1012
0
  strbuf_trim(val);
1013
0
}
1014
1015
static struct trailer_block *trailer_block_new(void)
1016
0
{
1017
0
  struct trailer_block *trailer_block = xcalloc(1, sizeof(*trailer_block));
1018
0
  return trailer_block;
1019
0
}
1020
1021
static struct trailer_block *trailer_block_get(const struct process_trailer_options *opts,
1022
                 const char *str)
1023
0
{
1024
0
  struct trailer_block *trailer_block = trailer_block_new();
1025
0
  size_t end_of_log_message = 0, trailer_block_start = 0;
1026
0
  struct strbuf **trailer_lines, **ptr;
1027
0
  char **trailer_strings = NULL;
1028
0
  size_t nr = 0, alloc = 0;
1029
0
  char **last = NULL;
1030
1031
0
  trailer_config_init();
1032
1033
0
  end_of_log_message = find_end_of_log_message(str, opts->no_divider);
1034
0
  trailer_block_start = find_trailer_block_start(str, end_of_log_message);
1035
1036
0
  trailer_lines = strbuf_split_buf(str + trailer_block_start,
1037
0
           end_of_log_message - trailer_block_start,
1038
0
           '\n',
1039
0
           0);
1040
0
  for (ptr = trailer_lines; *ptr; ptr++) {
1041
0
    if (last && isspace((*ptr)->buf[0])) {
1042
0
      struct strbuf sb = STRBUF_INIT;
1043
0
      strbuf_attach(&sb, *last, strlen(*last), strlen(*last) + 1);
1044
0
      strbuf_addbuf(&sb, *ptr);
1045
0
      *last = strbuf_detach(&sb, NULL);
1046
0
      continue;
1047
0
    }
1048
0
    ALLOC_GROW(trailer_strings, nr + 1, alloc);
1049
0
    trailer_strings[nr] = strbuf_detach(*ptr, NULL);
1050
0
    last = find_separator(trailer_strings[nr], separators) >= 1
1051
0
      ? &trailer_strings[nr]
1052
0
      : NULL;
1053
0
    nr++;
1054
0
  }
1055
0
  strbuf_list_free(trailer_lines);
1056
1057
0
  trailer_block->blank_line_before_trailer = ends_with_blank_line(str,
1058
0
                  trailer_block_start);
1059
0
  trailer_block->start = trailer_block_start;
1060
0
  trailer_block->end = end_of_log_message;
1061
0
  trailer_block->trailers = trailer_strings;
1062
0
  trailer_block->trailer_nr = nr;
1063
1064
0
  return trailer_block;
1065
0
}
1066
1067
/*
1068
 * Parse trailers in "str", populating the trailer_block and "trailer_objects"
1069
 * linked list structure.
1070
 */
1071
struct trailer_block *parse_trailers(const struct process_trailer_options *opts,
1072
             const char *str,
1073
             struct list_head *trailer_objects)
1074
0
{
1075
0
  struct trailer_block *trailer_block;
1076
0
  struct strbuf tok = STRBUF_INIT;
1077
0
  struct strbuf val = STRBUF_INIT;
1078
0
  size_t i;
1079
1080
0
  trailer_block = trailer_block_get(opts, str);
1081
1082
0
  for (i = 0; i < trailer_block->trailer_nr; i++) {
1083
0
    int separator_pos;
1084
0
    char *trailer = trailer_block->trailers[i];
1085
0
    if (starts_with(trailer, comment_line_str))
1086
0
      continue;
1087
0
    separator_pos = find_separator(trailer, separators);
1088
0
    if (separator_pos >= 1) {
1089
0
      parse_trailer(&tok, &val, NULL, trailer,
1090
0
              separator_pos);
1091
0
      if (opts->unfold)
1092
0
        unfold_value(&val);
1093
0
      add_trailer_item(trailer_objects,
1094
0
           strbuf_detach(&tok, NULL),
1095
0
           strbuf_detach(&val, NULL));
1096
0
    } else if (!opts->only_trailers) {
1097
0
      strbuf_addstr(&val, trailer);
1098
0
      strbuf_strip_suffix(&val, "\n");
1099
0
      add_trailer_item(trailer_objects,
1100
0
           NULL,
1101
0
           strbuf_detach(&val, NULL));
1102
0
    }
1103
0
  }
1104
1105
0
  return trailer_block;
1106
0
}
1107
1108
void free_trailers(struct list_head *trailers)
1109
0
{
1110
0
  struct list_head *pos, *p;
1111
0
  list_for_each_safe(pos, p, trailers) {
1112
0
    list_del(pos);
1113
0
    free_trailer_item(list_entry(pos, struct trailer_item, list));
1114
0
  }
1115
0
}
1116
1117
size_t trailer_block_start(struct trailer_block *trailer_block)
1118
0
{
1119
0
  return trailer_block->start;
1120
0
}
1121
1122
size_t trailer_block_end(struct trailer_block *trailer_block)
1123
0
{
1124
0
  return trailer_block->end;
1125
0
}
1126
1127
int blank_line_before_trailer_block(struct trailer_block *trailer_block)
1128
0
{
1129
0
  return trailer_block->blank_line_before_trailer;
1130
0
}
1131
1132
void trailer_block_release(struct trailer_block *trailer_block)
1133
0
{
1134
0
  size_t i;
1135
0
  for (i = 0; i < trailer_block->trailer_nr; i++)
1136
0
    free(trailer_block->trailers[i]);
1137
0
  free(trailer_block->trailers);
1138
0
  free(trailer_block);
1139
0
}
1140
1141
void format_trailers(const struct process_trailer_options *opts,
1142
         struct list_head *trailers,
1143
         struct strbuf *out)
1144
0
{
1145
0
  struct strbuf tok = STRBUF_INIT;
1146
0
  struct strbuf val = STRBUF_INIT;
1147
0
  size_t origlen = out->len;
1148
0
  struct list_head *pos;
1149
0
  struct trailer_item *item;
1150
1151
0
  list_for_each(pos, trailers) {
1152
0
    item = list_entry(pos, struct trailer_item, list);
1153
0
    if (item->token) {
1154
0
      strbuf_reset(&tok);
1155
0
      strbuf_addstr(&tok, item->token);
1156
0
      strbuf_reset(&val);
1157
0
      strbuf_addstr(&val, item->value);
1158
1159
      /*
1160
       * Skip key/value pairs where the value was empty. This
1161
       * can happen from trailers specified without a
1162
       * separator, like `--trailer "Reviewed-by"` (no
1163
       * corresponding value).
1164
       */
1165
0
      if (opts->trim_empty && !strlen(item->value))
1166
0
        continue;
1167
1168
0
      if (!opts->filter || opts->filter(&tok, opts->filter_data)) {
1169
0
        if (opts->separator && out->len != origlen)
1170
0
          strbuf_addbuf(out, opts->separator);
1171
0
        if (!opts->value_only)
1172
0
          strbuf_addbuf(out, &tok);
1173
0
        if (!opts->key_only && !opts->value_only) {
1174
0
          if (opts->key_value_separator)
1175
0
            strbuf_addbuf(out, opts->key_value_separator);
1176
0
          else {
1177
0
            char c = last_non_space_char(tok.buf);
1178
0
            if (c && !strchr(separators, c))
1179
0
              strbuf_addf(out, "%c ", separators[0]);
1180
0
          }
1181
0
        }
1182
0
        if (!opts->key_only)
1183
0
          strbuf_addbuf(out, &val);
1184
0
        if (!opts->separator)
1185
0
          strbuf_addch(out, '\n');
1186
0
      }
1187
0
    } else if (!opts->only_trailers) {
1188
0
      if (opts->separator && out->len != origlen) {
1189
0
        strbuf_addbuf(out, opts->separator);
1190
0
      }
1191
0
      strbuf_addstr(out, item->value);
1192
0
      if (opts->separator)
1193
0
        strbuf_rtrim(out);
1194
0
      else
1195
0
        strbuf_addch(out, '\n');
1196
0
    }
1197
0
  }
1198
1199
0
  strbuf_release(&tok);
1200
0
  strbuf_release(&val);
1201
0
}
1202
1203
void format_trailers_from_commit(const struct process_trailer_options *opts,
1204
         const char *msg,
1205
         struct strbuf *out)
1206
0
{
1207
0
  LIST_HEAD(trailer_objects);
1208
0
  struct trailer_block *trailer_block = parse_trailers(opts, msg, &trailer_objects);
1209
1210
  /* If we want the whole block untouched, we can take the fast path. */
1211
0
  if (!opts->only_trailers && !opts->unfold && !opts->filter &&
1212
0
      !opts->separator && !opts->key_only && !opts->value_only &&
1213
0
      !opts->key_value_separator) {
1214
0
    strbuf_add(out, msg + trailer_block->start,
1215
0
         trailer_block->end - trailer_block->start);
1216
0
  } else
1217
0
    format_trailers(opts, &trailer_objects, out);
1218
1219
0
  free_trailers(&trailer_objects);
1220
0
  trailer_block_release(trailer_block);
1221
0
}
1222
1223
void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
1224
0
{
1225
0
  struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
1226
0
  strbuf_init(&iter->key, 0);
1227
0
  strbuf_init(&iter->val, 0);
1228
0
  opts.no_divider = 1;
1229
0
  iter->internal.trailer_block = trailer_block_get(&opts, msg);
1230
0
  iter->internal.cur = 0;
1231
0
}
1232
1233
int trailer_iterator_advance(struct trailer_iterator *iter)
1234
0
{
1235
0
  if (iter->internal.cur < iter->internal.trailer_block->trailer_nr) {
1236
0
    char *line = iter->internal.trailer_block->trailers[iter->internal.cur++];
1237
0
    int separator_pos = find_separator(line, separators);
1238
1239
0
    iter->raw = line;
1240
0
    strbuf_reset(&iter->key);
1241
0
    strbuf_reset(&iter->val);
1242
0
    parse_trailer(&iter->key, &iter->val, NULL,
1243
0
            line, separator_pos);
1244
    /* Always unfold values during iteration. */
1245
0
    unfold_value(&iter->val);
1246
0
    return 1;
1247
0
  }
1248
0
  return 0;
1249
0
}
1250
1251
void trailer_iterator_release(struct trailer_iterator *iter)
1252
0
{
1253
0
  trailer_block_release(iter->internal.trailer_block);
1254
0
  strbuf_release(&iter->val);
1255
0
  strbuf_release(&iter->key);
1256
0
}
1257
1258
struct tempfile *trailer_create_in_place_tempfile(const char *file)
1259
0
{
1260
0
  struct tempfile *tempfile = NULL;
1261
0
  struct stat st;
1262
0
  struct strbuf filename_template = STRBUF_INIT;
1263
0
  const char *tail;
1264
1265
0
  if (stat(file, &st)) {
1266
0
    error_errno(_("could not stat %s"), file);
1267
0
    return NULL;
1268
0
  }
1269
0
  if (!S_ISREG(st.st_mode)) {
1270
0
    error(_("file %s is not a regular file"), file);
1271
0
    return NULL;
1272
0
  }
1273
0
  if (!(st.st_mode & S_IWUSR)) {
1274
0
    error(_("file %s is not writable by user"), file);
1275
0
    return NULL;
1276
0
  }
1277
  /* Create temporary file in the same directory as the original */
1278
0
  tail = find_last_dir_sep(file);
1279
0
  if (tail)
1280
0
    strbuf_add(&filename_template, file, tail - file + 1);
1281
0
  strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
1282
1283
0
  tempfile = mks_tempfile_m(filename_template.buf, st.st_mode);
1284
1285
0
  strbuf_release(&filename_template);
1286
1287
0
  return tempfile;
1288
0
}
1289
1290
int amend_strbuf_with_trailers(struct strbuf *buf,
1291
        const struct strvec *trailer_args)
1292
0
{
1293
0
  struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
1294
0
  LIST_HEAD(new_trailer_head);
1295
0
  struct strbuf out = STRBUF_INIT;
1296
0
  size_t i;
1297
0
  int ret = 0;
1298
1299
0
  opts.no_divider = 1;
1300
1301
0
  for (i = 0; i < trailer_args->nr; i++) {
1302
0
    const char *text = trailer_args->v[i];
1303
0
    struct new_trailer_item *item;
1304
1305
0
    if (!*text) {
1306
0
      ret = error(_("empty --trailer argument"));
1307
0
      goto out;
1308
0
    }
1309
0
    item = xcalloc(1, sizeof(*item));
1310
0
    item->text = xstrdup(text);
1311
0
    list_add_tail(&item->list, &new_trailer_head);
1312
0
  }
1313
1314
0
  process_trailers(&opts, &new_trailer_head, buf, &out);
1315
1316
0
  strbuf_swap(buf, &out);
1317
0
out:
1318
0
  strbuf_release(&out);
1319
0
  free_trailers(&new_trailer_head);
1320
1321
0
  return ret;
1322
0
}
1323
1324
static int write_file_in_place(const char *path, const struct strbuf *buf)
1325
0
{
1326
0
  struct tempfile *tempfile = trailer_create_in_place_tempfile(path);
1327
0
  if (!tempfile)
1328
0
    return -1;
1329
1330
0
  if (write_in_full(tempfile->fd, buf->buf, buf->len) < 0)
1331
0
    return error_errno(_("could not write to temporary file"));
1332
1333
0
  if (rename_tempfile(&tempfile, path))
1334
0
    return error_errno(_("could not rename temporary file to %s"), path);
1335
1336
0
  return 0;
1337
0
}
1338
1339
int amend_file_with_trailers(const char *path,
1340
           const struct strvec *trailer_args)
1341
0
{
1342
0
  struct strbuf buf = STRBUF_INIT;
1343
0
  int ret = 0;
1344
1345
0
  if (!trailer_args)
1346
0
    BUG("amend_file_with_trailers called with NULL trailer_args");
1347
0
  if (!trailer_args->nr)
1348
0
    return 0;
1349
1350
0
  if (validate_trailer_args(trailer_args)) {
1351
0
    ret = -1;
1352
0
    goto out;
1353
0
  }
1354
0
  if (strbuf_read_file(&buf, path, 0) < 0)
1355
0
    ret = error_errno(_("could not read '%s'"), path);
1356
0
  else
1357
0
    amend_strbuf_with_trailers(&buf, trailer_args);
1358
1359
0
  if (!ret)
1360
0
    ret = write_file_in_place(path, &buf);
1361
0
out:
1362
0
  strbuf_release(&buf);
1363
0
  return ret;
1364
0
}
1365
1366
void process_trailers(const struct process_trailer_options *opts,
1367
          struct list_head *new_trailer_head,
1368
          struct strbuf *input, struct strbuf *out)
1369
0
{
1370
0
  LIST_HEAD(head);
1371
0
  struct trailer_block *trailer_block;
1372
1373
0
  trailer_block = parse_trailers(opts, input->buf, &head);
1374
1375
  /* Print the lines before the trailer block */
1376
0
  if (!opts->only_trailers)
1377
0
    strbuf_add(out, input->buf, trailer_block_start(trailer_block));
1378
1379
0
  if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block))
1380
0
    strbuf_addch(out, '\n');
1381
1382
0
  if (!opts->only_input) {
1383
0
    LIST_HEAD(config_head);
1384
0
    LIST_HEAD(arg_head);
1385
0
    parse_trailers_from_config(&config_head);
1386
0
    parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
1387
0
    list_splice(&config_head, &arg_head);
1388
0
    process_trailers_lists(&head, &arg_head);
1389
0
  }
1390
1391
  /* Print trailer block. */
1392
0
  format_trailers(opts, &head, out);
1393
0
  free_trailers(&head);
1394
1395
  /* Print the lines after the trailer block as is. */
1396
0
  if (!opts->only_trailers)
1397
0
    strbuf_add(out, input->buf + trailer_block_end(trailer_block),
1398
0
         input->len - trailer_block_end(trailer_block));
1399
0
  trailer_block_release(trailer_block);
1400
0
}