Coverage Report

Created: 2026-03-31 06:24

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
0
      return c - line;
640
0
    if (!whitespace_found && (isalnum(*c) || *c == '-'))
641
0
      continue;
642
0
    if (c != line && (*c == ' ' || *c == '\t')) {
643
0
      whitespace_found = 1;
644
0
      continue;
645
0
    }
646
0
    break;
647
0
  }
648
0
  return -1;
649
0
}
650
651
/*
652
 * Obtain the token, value, and conf from the given trailer.
653
 *
654
 * separator_pos must not be 0, since the token cannot be an empty string.
655
 *
656
 * If separator_pos is -1, interpret the whole trailer as a token.
657
 */
658
static void parse_trailer(struct strbuf *tok, struct strbuf *val,
659
       const struct conf_info **conf, const char *trailer,
660
       ssize_t separator_pos)
661
0
{
662
0
  struct arg_item *item;
663
0
  size_t tok_len;
664
0
  struct list_head *pos;
665
666
0
  if (separator_pos != -1) {
667
0
    strbuf_add(tok, trailer, separator_pos);
668
0
    strbuf_trim(tok);
669
0
    strbuf_addstr(val, trailer + separator_pos + 1);
670
0
    strbuf_trim(val);
671
0
  } else {
672
0
    strbuf_addstr(tok, trailer);
673
0
    strbuf_trim(tok);
674
0
  }
675
676
  /* Lookup if the token matches something in the config */
677
0
  tok_len = token_len_without_separator(tok->buf, tok->len);
678
0
  if (conf)
679
0
    *conf = &default_conf_info;
680
0
  list_for_each(pos, &conf_head) {
681
0
    item = list_entry(pos, struct arg_item, list);
682
0
    if (token_matches_item(tok->buf, item, tok_len)) {
683
0
      char *tok_buf = strbuf_detach(tok, NULL);
684
0
      if (conf)
685
0
        *conf = &item->conf;
686
0
      strbuf_addstr(tok, token_from_item(item, tok_buf));
687
0
      free(tok_buf);
688
0
      break;
689
0
    }
690
0
  }
691
0
}
692
693
static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
694
               char *val)
695
0
{
696
0
  struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
697
0
  new_item->token = tok;
698
0
  new_item->value = val;
699
0
  list_add_tail(&new_item->list, head);
700
0
  return new_item;
701
0
}
702
703
static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
704
       const struct conf_info *conf,
705
       const struct new_trailer_item *new_trailer_item)
706
0
{
707
0
  struct arg_item *new_item = xcalloc(1, sizeof(*new_item));
708
0
  new_item->token = tok;
709
0
  new_item->value = val;
710
0
  duplicate_conf(&new_item->conf, conf);
711
0
  if (new_trailer_item) {
712
0
    if (new_trailer_item->where != WHERE_DEFAULT)
713
0
      new_item->conf.where = new_trailer_item->where;
714
0
    if (new_trailer_item->if_exists != EXISTS_DEFAULT)
715
0
      new_item->conf.if_exists = new_trailer_item->if_exists;
716
0
    if (new_trailer_item->if_missing != MISSING_DEFAULT)
717
0
      new_item->conf.if_missing = new_trailer_item->if_missing;
718
0
  }
719
0
  list_add_tail(&new_item->list, arg_head);
720
0
}
721
722
void parse_trailers_from_config(struct list_head *config_head)
723
0
{
724
0
  struct arg_item *item;
725
0
  struct list_head *pos;
726
727
  /* Add an arg item for each configured trailer with a command */
728
0
  list_for_each(pos, &conf_head) {
729
0
    item = list_entry(pos, struct arg_item, list);
730
0
    if (item->conf.command)
731
0
      add_arg_item(config_head,
732
0
             xstrdup(token_from_item(item, NULL)),
733
0
             xstrdup(""),
734
0
             &item->conf, NULL);
735
0
  }
736
0
}
737
738
void parse_trailers_from_command_line_args(struct list_head *arg_head,
739
             struct list_head *new_trailer_head)
740
0
{
741
0
  struct strbuf tok = STRBUF_INIT;
742
0
  struct strbuf val = STRBUF_INIT;
743
0
  const struct conf_info *conf;
744
0
  struct list_head *pos;
745
746
  /*
747
   * In command-line arguments, '=' is accepted (in addition to the
748
   * separators that are defined).
749
   */
750
0
  char *cl_separators = xstrfmt("=%s", separators);
751
752
  /* Add an arg item for each trailer on the command line */
753
0
  list_for_each(pos, new_trailer_head) {
754
0
    struct new_trailer_item *tr =
755
0
      list_entry(pos, struct new_trailer_item, list);
756
0
    ssize_t separator_pos = find_separator(tr->text, cl_separators);
757
758
0
    if (separator_pos == 0) {
759
0
      struct strbuf sb = STRBUF_INIT;
760
0
      strbuf_addstr(&sb, tr->text);
761
0
      strbuf_trim(&sb);
762
0
      error(_("empty trailer token in trailer '%.*s'"),
763
0
            (int) sb.len, sb.buf);
764
0
      strbuf_release(&sb);
765
0
    } else {
766
0
      parse_trailer(&tok, &val, &conf, tr->text,
767
0
              separator_pos);
768
0
      add_arg_item(arg_head,
769
0
             strbuf_detach(&tok, NULL),
770
0
             strbuf_detach(&val, NULL),
771
0
             conf, tr);
772
0
    }
773
0
  }
774
775
0
  free(cl_separators);
776
0
}
777
778
int validate_trailer_args(const struct strvec *cli_args)
779
0
{
780
0
  char *cl_separators;
781
0
  int ret = 0;
782
783
0
  trailer_config_init();
784
785
0
  cl_separators = xstrfmt("=%s", separators);
786
787
0
  for (size_t i = 0; i < cli_args->nr; i++) {
788
0
    const char *txt = cli_args->v[i];
789
0
    ssize_t separator_pos;
790
791
0
    if (!*txt) {
792
0
      ret = error(_("empty --trailer argument"));
793
0
      goto out;
794
0
    }
795
0
    separator_pos = find_separator(txt, cl_separators);
796
0
    if (separator_pos == 0) {
797
0
      ret = error(_("invalid trailer '%s': missing key before separator"),
798
0
            txt);
799
0
      goto out;
800
0
    }
801
0
  }
802
0
out:
803
0
  free(cl_separators);
804
0
  return ret;
805
0
}
806
807
static const char *next_line(const char *str)
808
0
{
809
0
  const char *nl = strchrnul(str, '\n');
810
0
  return nl + !!*nl;
811
0
}
812
813
/*
814
 * Return the position of the start of the last line. If len is 0, return -1.
815
 */
816
static ssize_t last_line(const char *buf, size_t len)
817
0
{
818
0
  ssize_t i;
819
0
  if (len == 0)
820
0
    return -1;
821
0
  if (len == 1)
822
0
    return 0;
823
  /*
824
   * Skip the last character (in addition to the null terminator),
825
   * because if the last character is a newline, it is considered as part
826
   * of the last line anyway.
827
   */
828
0
  i = len - 2;
829
830
0
  for (; i >= 0; i--) {
831
0
    if (buf[i] == '\n')
832
0
      return i + 1;
833
0
  }
834
0
  return 0;
835
0
}
836
837
/*
838
 * Find the end of the log message as an offset from the start of the input
839
 * (where callers of this function are interested in looking for a trailers
840
 * block in the same input). We have to consider two categories of content that
841
 * can come at the end of the input which we want to ignore (because they don't
842
 * belong in the log message):
843
 *
844
 * (1) the "patch part" which begins with a "---" divider and has patch
845
 * information (like the output of git-format-patch), and
846
 *
847
 * (2) any trailing comment lines, blank lines like in the output of "git
848
 * commit -v", or stuff below the "cut" (scissor) line.
849
 *
850
 * As a formula, the situation looks like this:
851
 *
852
 *     INPUT = LOG MESSAGE + IGNORED
853
 *
854
 * where IGNORED can be either of the two categories described above. It may be
855
 * that there is nothing to ignore. Now it may be the case that the LOG MESSAGE
856
 * contains a trailer block, but that's not the concern of this function.
857
 */
858
static size_t find_end_of_log_message(const char *input, int no_divider)
859
0
{
860
0
  size_t end;
861
0
  const char *s;
862
863
  /* Assume the naive end of the input is already what we want. */
864
0
  end = strlen(input);
865
866
  /* Optionally skip over any patch part ("---" line and below). */
867
0
  if (!no_divider) {
868
0
    for (s = input; *s; s = next_line(s)) {
869
0
      const char *v;
870
871
0
      if (skip_prefix(s, "---", &v) && isspace(*v)) {
872
0
        end = s - input;
873
0
        break;
874
0
      }
875
0
    }
876
0
  }
877
878
  /* Skip over other ignorable bits. */
879
0
  return end - ignored_log_message_bytes(input, end);
880
0
}
881
882
/*
883
 * Return the position of the first trailer line or len if there are no
884
 * trailers.
885
 */
886
static size_t find_trailer_block_start(const char *buf, size_t len)
887
0
{
888
0
  const char *s;
889
0
  ssize_t end_of_title, l;
890
0
  int only_spaces = 1;
891
0
  int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
892
  /*
893
   * Number of possible continuation lines encountered. This will be
894
   * reset to 0 if we encounter a trailer (since those lines are to be
895
   * considered continuations of that trailer), and added to
896
   * non_trailer_lines if we encounter a non-trailer (since those lines
897
   * are to be considered non-trailers).
898
   */
899
0
  int possible_continuation_lines = 0;
900
901
  /* The first paragraph is the title and cannot be trailers */
902
0
  for (s = buf; s < buf + len; s = next_line(s)) {
903
0
    if (starts_with_mem(s, buf + len - s, comment_line_str))
904
0
      continue;
905
0
    if (is_blank_line(s))
906
0
      break;
907
0
  }
908
0
  end_of_title = s - buf;
909
910
  /*
911
   * Get the start of the trailers by looking starting from the end for a
912
   * blank line before a set of non-blank lines that (i) are all
913
   * trailers, or (ii) contains at least one Git-generated trailer and
914
   * consists of at least 25% trailers.
915
   */
916
0
  for (l = last_line(buf, len);
917
0
       l >= end_of_title;
918
0
       l = last_line(buf, l)) {
919
0
    const char *bol = buf + l;
920
0
    const char **p;
921
0
    ssize_t separator_pos;
922
923
0
    if (starts_with_mem(bol, buf + len - bol, comment_line_str)) {
924
0
      non_trailer_lines += possible_continuation_lines;
925
0
      possible_continuation_lines = 0;
926
0
      continue;
927
0
    }
928
0
    if (is_blank_line(bol)) {
929
0
      if (only_spaces)
930
0
        continue;
931
0
      non_trailer_lines += possible_continuation_lines;
932
0
      if (recognized_prefix &&
933
0
          trailer_lines * 3 >= non_trailer_lines)
934
0
        return next_line(bol) - buf;
935
0
      else if (trailer_lines && !non_trailer_lines)
936
0
        return next_line(bol) - buf;
937
0
      return len;
938
0
    }
939
0
    only_spaces = 0;
940
941
0
    for (p = git_generated_prefixes; *p; p++) {
942
0
      if (starts_with(bol, *p)) {
943
0
        trailer_lines++;
944
0
        possible_continuation_lines = 0;
945
0
        recognized_prefix = 1;
946
0
        goto continue_outer_loop;
947
0
      }
948
0
    }
949
950
0
    separator_pos = find_separator(bol, separators);
951
0
    if (separator_pos >= 1 && !isspace(bol[0])) {
952
0
      struct list_head *pos;
953
954
0
      trailer_lines++;
955
0
      possible_continuation_lines = 0;
956
0
      if (recognized_prefix)
957
0
        continue;
958
0
      list_for_each(pos, &conf_head) {
959
0
        struct arg_item *item;
960
0
        item = list_entry(pos, struct arg_item, list);
961
0
        if (token_matches_item(bol, item,
962
0
                   separator_pos)) {
963
0
          recognized_prefix = 1;
964
0
          break;
965
0
        }
966
0
      }
967
0
    } else if (isspace(bol[0]))
968
0
      possible_continuation_lines++;
969
0
    else {
970
0
      non_trailer_lines++;
971
0
      non_trailer_lines += possible_continuation_lines;
972
0
      possible_continuation_lines = 0;
973
0
    }
974
0
continue_outer_loop:
975
0
    ;
976
0
  }
977
978
0
  return len;
979
0
}
980
981
static int ends_with_blank_line(const char *buf, size_t len)
982
0
{
983
0
  ssize_t ll = last_line(buf, len);
984
0
  if (ll < 0)
985
0
    return 0;
986
0
  return is_blank_line(buf + ll);
987
0
}
988
989
static void unfold_value(struct strbuf *val)
990
0
{
991
0
  struct strbuf out = STRBUF_INIT;
992
0
  size_t i;
993
994
0
  strbuf_grow(&out, val->len);
995
0
  i = 0;
996
0
  while (i < val->len) {
997
0
    char c = val->buf[i++];
998
0
    if (c == '\n') {
999
      /* Collapse continuation down to a single space. */
1000
0
      while (i < val->len && isspace(val->buf[i]))
1001
0
        i++;
1002
0
      strbuf_addch(&out, ' ');
1003
0
    } else {
1004
0
      strbuf_addch(&out, c);
1005
0
    }
1006
0
  }
1007
1008
  /* Empty lines may have left us with whitespace cruft at the edges */
1009
0
  strbuf_trim(&out);
1010
1011
  /* output goes back to val as if we modified it in-place */
1012
0
  strbuf_swap(&out, val);
1013
0
  strbuf_release(&out);
1014
0
}
1015
1016
static struct trailer_block *trailer_block_new(void)
1017
0
{
1018
0
  struct trailer_block *trailer_block = xcalloc(1, sizeof(*trailer_block));
1019
0
  return trailer_block;
1020
0
}
1021
1022
static struct trailer_block *trailer_block_get(const struct process_trailer_options *opts,
1023
                 const char *str)
1024
0
{
1025
0
  struct trailer_block *trailer_block = trailer_block_new();
1026
0
  size_t end_of_log_message = 0, trailer_block_start = 0;
1027
0
  struct strbuf **trailer_lines, **ptr;
1028
0
  char **trailer_strings = NULL;
1029
0
  size_t nr = 0, alloc = 0;
1030
0
  char **last = NULL;
1031
1032
0
  trailer_config_init();
1033
1034
0
  end_of_log_message = find_end_of_log_message(str, opts->no_divider);
1035
0
  trailer_block_start = find_trailer_block_start(str, end_of_log_message);
1036
1037
0
  trailer_lines = strbuf_split_buf(str + trailer_block_start,
1038
0
           end_of_log_message - trailer_block_start,
1039
0
           '\n',
1040
0
           0);
1041
0
  for (ptr = trailer_lines; *ptr; ptr++) {
1042
0
    if (last && isspace((*ptr)->buf[0])) {
1043
0
      struct strbuf sb = STRBUF_INIT;
1044
0
      strbuf_attach(&sb, *last, strlen(*last), strlen(*last));
1045
0
      strbuf_addbuf(&sb, *ptr);
1046
0
      *last = strbuf_detach(&sb, NULL);
1047
0
      continue;
1048
0
    }
1049
0
    ALLOC_GROW(trailer_strings, nr + 1, alloc);
1050
0
    trailer_strings[nr] = strbuf_detach(*ptr, NULL);
1051
0
    last = find_separator(trailer_strings[nr], separators) >= 1
1052
0
      ? &trailer_strings[nr]
1053
0
      : NULL;
1054
0
    nr++;
1055
0
  }
1056
0
  strbuf_list_free(trailer_lines);
1057
1058
0
  trailer_block->blank_line_before_trailer = ends_with_blank_line(str,
1059
0
                  trailer_block_start);
1060
0
  trailer_block->start = trailer_block_start;
1061
0
  trailer_block->end = end_of_log_message;
1062
0
  trailer_block->trailers = trailer_strings;
1063
0
  trailer_block->trailer_nr = nr;
1064
1065
0
  return trailer_block;
1066
0
}
1067
1068
/*
1069
 * Parse trailers in "str", populating the trailer_block and "trailer_objects"
1070
 * linked list structure.
1071
 */
1072
struct trailer_block *parse_trailers(const struct process_trailer_options *opts,
1073
             const char *str,
1074
             struct list_head *trailer_objects)
1075
0
{
1076
0
  struct trailer_block *trailer_block;
1077
0
  struct strbuf tok = STRBUF_INIT;
1078
0
  struct strbuf val = STRBUF_INIT;
1079
0
  size_t i;
1080
1081
0
  trailer_block = trailer_block_get(opts, str);
1082
1083
0
  for (i = 0; i < trailer_block->trailer_nr; i++) {
1084
0
    int separator_pos;
1085
0
    char *trailer = trailer_block->trailers[i];
1086
0
    if (starts_with(trailer, comment_line_str))
1087
0
      continue;
1088
0
    separator_pos = find_separator(trailer, separators);
1089
0
    if (separator_pos >= 1) {
1090
0
      parse_trailer(&tok, &val, NULL, trailer,
1091
0
              separator_pos);
1092
0
      if (opts->unfold)
1093
0
        unfold_value(&val);
1094
0
      add_trailer_item(trailer_objects,
1095
0
           strbuf_detach(&tok, NULL),
1096
0
           strbuf_detach(&val, NULL));
1097
0
    } else if (!opts->only_trailers) {
1098
0
      strbuf_addstr(&val, trailer);
1099
0
      strbuf_strip_suffix(&val, "\n");
1100
0
      add_trailer_item(trailer_objects,
1101
0
           NULL,
1102
0
           strbuf_detach(&val, NULL));
1103
0
    }
1104
0
  }
1105
1106
0
  return trailer_block;
1107
0
}
1108
1109
void free_trailers(struct list_head *trailers)
1110
0
{
1111
0
  struct list_head *pos, *p;
1112
0
  list_for_each_safe(pos, p, trailers) {
1113
0
    list_del(pos);
1114
0
    free_trailer_item(list_entry(pos, struct trailer_item, list));
1115
0
  }
1116
0
}
1117
1118
size_t trailer_block_start(struct trailer_block *trailer_block)
1119
0
{
1120
0
  return trailer_block->start;
1121
0
}
1122
1123
size_t trailer_block_end(struct trailer_block *trailer_block)
1124
0
{
1125
0
  return trailer_block->end;
1126
0
}
1127
1128
int blank_line_before_trailer_block(struct trailer_block *trailer_block)
1129
0
{
1130
0
  return trailer_block->blank_line_before_trailer;
1131
0
}
1132
1133
void trailer_block_release(struct trailer_block *trailer_block)
1134
0
{
1135
0
  size_t i;
1136
0
  for (i = 0; i < trailer_block->trailer_nr; i++)
1137
0
    free(trailer_block->trailers[i]);
1138
0
  free(trailer_block->trailers);
1139
0
  free(trailer_block);
1140
0
}
1141
1142
void format_trailers(const struct process_trailer_options *opts,
1143
         struct list_head *trailers,
1144
         struct strbuf *out)
1145
0
{
1146
0
  struct strbuf tok = STRBUF_INIT;
1147
0
  struct strbuf val = STRBUF_INIT;
1148
0
  size_t origlen = out->len;
1149
0
  struct list_head *pos;
1150
0
  struct trailer_item *item;
1151
1152
0
  list_for_each(pos, trailers) {
1153
0
    item = list_entry(pos, struct trailer_item, list);
1154
0
    if (item->token) {
1155
0
      strbuf_reset(&tok);
1156
0
      strbuf_addstr(&tok, item->token);
1157
0
      strbuf_reset(&val);
1158
0
      strbuf_addstr(&val, item->value);
1159
1160
      /*
1161
       * Skip key/value pairs where the value was empty. This
1162
       * can happen from trailers specified without a
1163
       * separator, like `--trailer "Reviewed-by"` (no
1164
       * corresponding value).
1165
       */
1166
0
      if (opts->trim_empty && !strlen(item->value))
1167
0
        continue;
1168
1169
0
      if (!opts->filter || opts->filter(&tok, opts->filter_data)) {
1170
0
        if (opts->separator && out->len != origlen)
1171
0
          strbuf_addbuf(out, opts->separator);
1172
0
        if (!opts->value_only)
1173
0
          strbuf_addbuf(out, &tok);
1174
0
        if (!opts->key_only && !opts->value_only) {
1175
0
          if (opts->key_value_separator)
1176
0
            strbuf_addbuf(out, opts->key_value_separator);
1177
0
          else {
1178
0
            char c = last_non_space_char(tok.buf);
1179
0
            if (c && !strchr(separators, c))
1180
0
              strbuf_addf(out, "%c ", separators[0]);
1181
0
          }
1182
0
        }
1183
0
        if (!opts->key_only)
1184
0
          strbuf_addbuf(out, &val);
1185
0
        if (!opts->separator)
1186
0
          strbuf_addch(out, '\n');
1187
0
      }
1188
0
    } else if (!opts->only_trailers) {
1189
0
      if (opts->separator && out->len != origlen) {
1190
0
        strbuf_addbuf(out, opts->separator);
1191
0
      }
1192
0
      strbuf_addstr(out, item->value);
1193
0
      if (opts->separator)
1194
0
        strbuf_rtrim(out);
1195
0
      else
1196
0
        strbuf_addch(out, '\n');
1197
0
    }
1198
0
  }
1199
1200
0
  strbuf_release(&tok);
1201
0
  strbuf_release(&val);
1202
0
}
1203
1204
void format_trailers_from_commit(const struct process_trailer_options *opts,
1205
         const char *msg,
1206
         struct strbuf *out)
1207
0
{
1208
0
  LIST_HEAD(trailer_objects);
1209
0
  struct trailer_block *trailer_block = parse_trailers(opts, msg, &trailer_objects);
1210
1211
  /* If we want the whole block untouched, we can take the fast path. */
1212
0
  if (!opts->only_trailers && !opts->unfold && !opts->filter &&
1213
0
      !opts->separator && !opts->key_only && !opts->value_only &&
1214
0
      !opts->key_value_separator) {
1215
0
    strbuf_add(out, msg + trailer_block->start,
1216
0
         trailer_block->end - trailer_block->start);
1217
0
  } else
1218
0
    format_trailers(opts, &trailer_objects, out);
1219
1220
0
  free_trailers(&trailer_objects);
1221
0
  trailer_block_release(trailer_block);
1222
0
}
1223
1224
void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
1225
0
{
1226
0
  struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
1227
0
  strbuf_init(&iter->key, 0);
1228
0
  strbuf_init(&iter->val, 0);
1229
0
  opts.no_divider = 1;
1230
0
  iter->internal.trailer_block = trailer_block_get(&opts, msg);
1231
0
  iter->internal.cur = 0;
1232
0
}
1233
1234
int trailer_iterator_advance(struct trailer_iterator *iter)
1235
0
{
1236
0
  if (iter->internal.cur < iter->internal.trailer_block->trailer_nr) {
1237
0
    char *line = iter->internal.trailer_block->trailers[iter->internal.cur++];
1238
0
    int separator_pos = find_separator(line, separators);
1239
1240
0
    iter->raw = line;
1241
0
    strbuf_reset(&iter->key);
1242
0
    strbuf_reset(&iter->val);
1243
0
    parse_trailer(&iter->key, &iter->val, NULL,
1244
0
            line, separator_pos);
1245
    /* Always unfold values during iteration. */
1246
0
    unfold_value(&iter->val);
1247
0
    return 1;
1248
0
  }
1249
0
  return 0;
1250
0
}
1251
1252
void trailer_iterator_release(struct trailer_iterator *iter)
1253
0
{
1254
0
  trailer_block_release(iter->internal.trailer_block);
1255
0
  strbuf_release(&iter->val);
1256
0
  strbuf_release(&iter->key);
1257
0
}
1258
1259
struct tempfile *trailer_create_in_place_tempfile(const char *file)
1260
0
{
1261
0
  struct tempfile *tempfile = NULL;
1262
0
  struct stat st;
1263
0
  struct strbuf filename_template = STRBUF_INIT;
1264
0
  const char *tail;
1265
1266
0
  if (stat(file, &st)) {
1267
0
    error_errno(_("could not stat %s"), file);
1268
0
    return NULL;
1269
0
  }
1270
0
  if (!S_ISREG(st.st_mode)) {
1271
0
    error(_("file %s is not a regular file"), file);
1272
0
    return NULL;
1273
0
  }
1274
0
  if (!(st.st_mode & S_IWUSR)) {
1275
0
    error(_("file %s is not writable by user"), file);
1276
0
    return NULL;
1277
0
  }
1278
  /* Create temporary file in the same directory as the original */
1279
0
  tail = find_last_dir_sep(file);
1280
0
  if (tail)
1281
0
    strbuf_add(&filename_template, file, tail - file + 1);
1282
0
  strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
1283
1284
0
  tempfile = mks_tempfile_m(filename_template.buf, st.st_mode);
1285
1286
0
  strbuf_release(&filename_template);
1287
1288
0
  return tempfile;
1289
0
}
1290
1291
int amend_strbuf_with_trailers(struct strbuf *buf,
1292
        const struct strvec *trailer_args)
1293
0
{
1294
0
  struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
1295
0
  LIST_HEAD(new_trailer_head);
1296
0
  struct strbuf out = STRBUF_INIT;
1297
0
  size_t i;
1298
0
  int ret = 0;
1299
1300
0
  opts.no_divider = 1;
1301
1302
0
  for (i = 0; i < trailer_args->nr; i++) {
1303
0
    const char *text = trailer_args->v[i];
1304
0
    struct new_trailer_item *item;
1305
1306
0
    if (!*text) {
1307
0
      ret = error(_("empty --trailer argument"));
1308
0
      goto out;
1309
0
    }
1310
0
    item = xcalloc(1, sizeof(*item));
1311
0
    item->text = xstrdup(text);
1312
0
    list_add_tail(&item->list, &new_trailer_head);
1313
0
  }
1314
1315
0
  process_trailers(&opts, &new_trailer_head, buf, &out);
1316
1317
0
  strbuf_swap(buf, &out);
1318
0
out:
1319
0
  strbuf_release(&out);
1320
0
  free_trailers(&new_trailer_head);
1321
1322
0
  return ret;
1323
0
}
1324
1325
static int write_file_in_place(const char *path, const struct strbuf *buf)
1326
0
{
1327
0
  struct tempfile *tempfile = trailer_create_in_place_tempfile(path);
1328
0
  if (!tempfile)
1329
0
    return -1;
1330
1331
0
  if (write_in_full(tempfile->fd, buf->buf, buf->len) < 0)
1332
0
    return error_errno(_("could not write to temporary file"));
1333
1334
0
  if (rename_tempfile(&tempfile, path))
1335
0
    return error_errno(_("could not rename temporary file to %s"), path);
1336
1337
0
  return 0;
1338
0
}
1339
1340
int amend_file_with_trailers(const char *path,
1341
           const struct strvec *trailer_args)
1342
0
{
1343
0
  struct strbuf buf = STRBUF_INIT;
1344
0
  int ret = 0;
1345
1346
0
  if (!trailer_args)
1347
0
    BUG("amend_file_with_trailers called with NULL trailer_args");
1348
0
  if (!trailer_args->nr)
1349
0
    return 0;
1350
1351
0
  if (validate_trailer_args(trailer_args)) {
1352
0
    ret = -1;
1353
0
    goto out;
1354
0
  }
1355
0
  if (strbuf_read_file(&buf, path, 0) < 0)
1356
0
    ret = error_errno(_("could not read '%s'"), path);
1357
0
  else
1358
0
    amend_strbuf_with_trailers(&buf, trailer_args);
1359
1360
0
  if (!ret)
1361
0
    ret = write_file_in_place(path, &buf);
1362
0
out:
1363
0
  strbuf_release(&buf);
1364
0
  return ret;
1365
0
}
1366
1367
void process_trailers(const struct process_trailer_options *opts,
1368
          struct list_head *new_trailer_head,
1369
          struct strbuf *input, struct strbuf *out)
1370
0
{
1371
0
  LIST_HEAD(head);
1372
0
  struct trailer_block *trailer_block;
1373
1374
0
  trailer_block = parse_trailers(opts, input->buf, &head);
1375
1376
  /* Print the lines before the trailer block */
1377
0
  if (!opts->only_trailers)
1378
0
    strbuf_add(out, input->buf, trailer_block_start(trailer_block));
1379
1380
0
  if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block))
1381
0
    strbuf_addch(out, '\n');
1382
1383
0
  if (!opts->only_input) {
1384
0
    LIST_HEAD(config_head);
1385
0
    LIST_HEAD(arg_head);
1386
0
    parse_trailers_from_config(&config_head);
1387
0
    parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
1388
0
    list_splice(&config_head, &arg_head);
1389
0
    process_trailers_lists(&head, &arg_head);
1390
0
  }
1391
1392
  /* Print trailer block. */
1393
0
  format_trailers(opts, &head, out);
1394
0
  free_trailers(&head);
1395
1396
  /* Print the lines after the trailer block as is. */
1397
0
  if (!opts->only_trailers)
1398
0
    strbuf_add(out, input->buf + trailer_block_end(trailer_block),
1399
0
         input->len - trailer_block_end(trailer_block));
1400
0
  trailer_block_release(trailer_block);
1401
0
}