Coverage Report

Created: 2024-09-16 06:12

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