Coverage Report

Created: 2026-01-15 06:28

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