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