2 #include "string-list.h"
5 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
8 enum action_where
{ WHERE_END
, WHERE_AFTER
, WHERE_BEFORE
, WHERE_START
};
9 enum action_if_exists
{ EXISTS_ADD_IF_DIFFERENT_NEIGHBOR
, EXISTS_ADD_IF_DIFFERENT
,
10 EXISTS_ADD
, EXISTS_REPLACE
, EXISTS_DO_NOTHING
};
11 enum action_if_missing
{ MISSING_ADD
, MISSING_DO_NOTHING
};
17 enum action_where where
;
18 enum action_if_exists if_exists
;
19 enum action_if_missing if_missing
;
22 static struct conf_info default_conf_info
;
25 struct trailer_item
*previous
;
26 struct trailer_item
*next
;
29 struct conf_info conf
;
32 static struct trailer_item
*first_conf_item
;
34 static char *separators
= ":";
36 static int after_or_end(enum action_where where
)
38 return (where
== WHERE_AFTER
) || (where
== WHERE_END
);
42 * Return the length of the string not including any final
43 * punctuation. E.g., the input "Signed-off-by:" would return
44 * 13, stripping the trailing punctuation but retaining
45 * internal punctuation.
47 static size_t token_len_without_separator(const char *token
, size_t len
)
49 while (len
> 0 && !isalnum(token
[len
- 1]))
54 static int same_token(struct trailer_item
*a
, struct trailer_item
*b
)
56 size_t a_len
= token_len_without_separator(a
->token
, strlen(a
->token
));
57 size_t b_len
= token_len_without_separator(b
->token
, strlen(b
->token
));
58 size_t min_len
= (a_len
> b_len
) ? b_len
: a_len
;
60 return !strncasecmp(a
->token
, b
->token
, min_len
);
63 static int same_value(struct trailer_item
*a
, struct trailer_item
*b
)
65 return !strcasecmp(a
->value
, b
->value
);
68 static int same_trailer(struct trailer_item
*a
, struct trailer_item
*b
)
70 return same_token(a
, b
) && same_value(a
, b
);
73 static inline int contains_only_spaces(const char *str
)
76 while (*s
&& isspace(*s
))
81 static void free_trailer_item(struct trailer_item
*item
)
83 free(item
->conf
.name
);
85 free(item
->conf
.command
);
86 free((char *)item
->token
);
87 free((char *)item
->value
);
91 static char last_non_space_char(const char *s
)
94 for (i
= strlen(s
) - 1; i
>= 0; i
--)
100 static void print_tok_val(const char *tok
, const char *val
)
102 char c
= last_non_space_char(tok
);
105 if (strchr(separators
, c
))
106 printf("%s%s\n", tok
, val
);
108 printf("%s%c %s\n", tok
, separators
[0], val
);
111 static void print_all(struct trailer_item
*first
, int trim_empty
)
113 struct trailer_item
*item
;
114 for (item
= first
; item
; item
= item
->next
) {
115 if (!trim_empty
|| strlen(item
->value
) > 0)
116 print_tok_val(item
->token
, item
->value
);
120 static void update_last(struct trailer_item
**last
)
123 while ((*last
)->next
!= NULL
)
124 *last
= (*last
)->next
;
127 static void update_first(struct trailer_item
**first
)
130 while ((*first
)->previous
!= NULL
)
131 *first
= (*first
)->previous
;
134 static void add_arg_to_input_list(struct trailer_item
*on_tok
,
135 struct trailer_item
*arg_tok
,
136 struct trailer_item
**first
,
137 struct trailer_item
**last
)
139 if (after_or_end(arg_tok
->conf
.where
)) {
140 arg_tok
->next
= on_tok
->next
;
141 on_tok
->next
= arg_tok
;
142 arg_tok
->previous
= on_tok
;
144 arg_tok
->next
->previous
= arg_tok
;
147 arg_tok
->previous
= on_tok
->previous
;
148 on_tok
->previous
= arg_tok
;
149 arg_tok
->next
= on_tok
;
150 if (arg_tok
->previous
)
151 arg_tok
->previous
->next
= arg_tok
;
156 static int check_if_different(struct trailer_item
*in_tok
,
157 struct trailer_item
*arg_tok
,
160 enum action_where where
= arg_tok
->conf
.where
;
164 if (same_trailer(in_tok
, arg_tok
))
167 * if we want to add a trailer after another one,
168 * we have to check those before this one
170 in_tok
= after_or_end(where
) ? in_tok
->previous
: in_tok
->next
;
175 static void remove_from_list(struct trailer_item
*item
,
176 struct trailer_item
**first
,
177 struct trailer_item
**last
)
179 struct trailer_item
*next
= item
->next
;
180 struct trailer_item
*previous
= item
->previous
;
183 item
->next
->previous
= previous
;
189 item
->previous
->next
= next
;
190 item
->previous
= NULL
;
195 static struct trailer_item
*remove_first(struct trailer_item
**first
)
197 struct trailer_item
*item
= *first
;
200 item
->next
->previous
= NULL
;
206 static void apply_arg_if_exists(struct trailer_item
*in_tok
,
207 struct trailer_item
*arg_tok
,
208 struct trailer_item
*on_tok
,
209 struct trailer_item
**in_tok_first
,
210 struct trailer_item
**in_tok_last
)
212 switch (arg_tok
->conf
.if_exists
) {
213 case EXISTS_DO_NOTHING
:
214 free_trailer_item(arg_tok
);
217 add_arg_to_input_list(on_tok
, arg_tok
,
218 in_tok_first
, in_tok_last
);
219 remove_from_list(in_tok
, in_tok_first
, in_tok_last
);
220 free_trailer_item(in_tok
);
223 add_arg_to_input_list(on_tok
, arg_tok
,
224 in_tok_first
, in_tok_last
);
226 case EXISTS_ADD_IF_DIFFERENT
:
227 if (check_if_different(in_tok
, arg_tok
, 1))
228 add_arg_to_input_list(on_tok
, arg_tok
,
229 in_tok_first
, in_tok_last
);
231 free_trailer_item(arg_tok
);
233 case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR
:
234 if (check_if_different(on_tok
, arg_tok
, 0))
235 add_arg_to_input_list(on_tok
, arg_tok
,
236 in_tok_first
, in_tok_last
);
238 free_trailer_item(arg_tok
);
243 static void apply_arg_if_missing(struct trailer_item
**in_tok_first
,
244 struct trailer_item
**in_tok_last
,
245 struct trailer_item
*arg_tok
)
247 struct trailer_item
**in_tok
;
248 enum action_where where
;
250 switch (arg_tok
->conf
.if_missing
) {
251 case MISSING_DO_NOTHING
:
252 free_trailer_item(arg_tok
);
255 where
= arg_tok
->conf
.where
;
256 in_tok
= after_or_end(where
) ? in_tok_last
: in_tok_first
;
258 add_arg_to_input_list(*in_tok
, arg_tok
,
259 in_tok_first
, in_tok_last
);
261 *in_tok_first
= arg_tok
;
262 *in_tok_last
= arg_tok
;
268 static int find_same_and_apply_arg(struct trailer_item
**in_tok_first
,
269 struct trailer_item
**in_tok_last
,
270 struct trailer_item
*arg_tok
)
272 struct trailer_item
*in_tok
;
273 struct trailer_item
*on_tok
;
274 struct trailer_item
*following_tok
;
276 enum action_where where
= arg_tok
->conf
.where
;
277 int middle
= (where
== WHERE_AFTER
) || (where
== WHERE_BEFORE
);
278 int backwards
= after_or_end(where
);
279 struct trailer_item
*start_tok
= backwards
? *in_tok_last
: *in_tok_first
;
281 for (in_tok
= start_tok
; in_tok
; in_tok
= following_tok
) {
282 following_tok
= backwards
? in_tok
->previous
: in_tok
->next
;
283 if (!same_token(in_tok
, arg_tok
))
285 on_tok
= middle
? in_tok
: start_tok
;
286 apply_arg_if_exists(in_tok
, arg_tok
, on_tok
,
287 in_tok_first
, in_tok_last
);
293 static void process_trailers_lists(struct trailer_item
**in_tok_first
,
294 struct trailer_item
**in_tok_last
,
295 struct trailer_item
**arg_tok_first
)
297 struct trailer_item
*arg_tok
;
298 struct trailer_item
*next_arg
;
303 for (arg_tok
= *arg_tok_first
; arg_tok
; arg_tok
= next_arg
) {
306 next_arg
= arg_tok
->next
;
307 remove_from_list(arg_tok
, arg_tok_first
, NULL
);
309 applied
= find_same_and_apply_arg(in_tok_first
,
314 apply_arg_if_missing(in_tok_first
,
320 static int set_where(struct conf_info
*item
, const char *value
)
322 if (!strcasecmp("after", value
))
323 item
->where
= WHERE_AFTER
;
324 else if (!strcasecmp("before", value
))
325 item
->where
= WHERE_BEFORE
;
326 else if (!strcasecmp("end", value
))
327 item
->where
= WHERE_END
;
328 else if (!strcasecmp("start", value
))
329 item
->where
= WHERE_START
;
335 static int set_if_exists(struct conf_info
*item
, const char *value
)
337 if (!strcasecmp("addIfDifferent", value
))
338 item
->if_exists
= EXISTS_ADD_IF_DIFFERENT
;
339 else if (!strcasecmp("addIfDifferentNeighbor", value
))
340 item
->if_exists
= EXISTS_ADD_IF_DIFFERENT_NEIGHBOR
;
341 else if (!strcasecmp("add", value
))
342 item
->if_exists
= EXISTS_ADD
;
343 else if (!strcasecmp("replace", value
))
344 item
->if_exists
= EXISTS_REPLACE
;
345 else if (!strcasecmp("doNothing", value
))
346 item
->if_exists
= EXISTS_DO_NOTHING
;
352 static int set_if_missing(struct conf_info
*item
, const char *value
)
354 if (!strcasecmp("doNothing", value
))
355 item
->if_missing
= MISSING_DO_NOTHING
;
356 else if (!strcasecmp("add", value
))
357 item
->if_missing
= MISSING_ADD
;
363 static void duplicate_conf(struct conf_info
*dst
, struct conf_info
*src
)
367 dst
->name
= xstrdup(src
->name
);
369 dst
->key
= xstrdup(src
->key
);
371 dst
->command
= xstrdup(src
->command
);
374 static struct trailer_item
*get_conf_item(const char *name
)
376 struct trailer_item
*item
;
377 struct trailer_item
*previous
;
379 /* Look up item with same name */
380 for (previous
= NULL
, item
= first_conf_item
;
382 previous
= item
, item
= item
->next
) {
383 if (!strcasecmp(item
->conf
.name
, name
))
387 /* Item does not already exists, create it */
388 item
= xcalloc(sizeof(struct trailer_item
), 1);
389 duplicate_conf(&item
->conf
, &default_conf_info
);
390 item
->conf
.name
= xstrdup(name
);
393 first_conf_item
= item
;
395 previous
->next
= item
;
396 item
->previous
= previous
;
402 enum trailer_info_type
{ TRAILER_KEY
, TRAILER_COMMAND
, TRAILER_WHERE
,
403 TRAILER_IF_EXISTS
, TRAILER_IF_MISSING
};
407 enum trailer_info_type type
;
408 } trailer_config_items
[] = {
409 { "key", TRAILER_KEY
},
410 { "command", TRAILER_COMMAND
},
411 { "where", TRAILER_WHERE
},
412 { "ifexists", TRAILER_IF_EXISTS
},
413 { "ifmissing", TRAILER_IF_MISSING
}
416 static int git_trailer_default_config(const char *conf_key
, const char *value
, void *cb
)
418 const char *trailer_item
, *variable_name
;
420 if (!skip_prefix(conf_key
, "trailer.", &trailer_item
))
423 variable_name
= strrchr(trailer_item
, '.');
424 if (!variable_name
) {
425 if (!strcmp(trailer_item
, "where")) {
426 if (set_where(&default_conf_info
, value
) < 0)
427 warning(_("unknown value '%s' for key '%s'"),
429 } else if (!strcmp(trailer_item
, "ifexists")) {
430 if (set_if_exists(&default_conf_info
, value
) < 0)
431 warning(_("unknown value '%s' for key '%s'"),
433 } else if (!strcmp(trailer_item
, "ifmissing")) {
434 if (set_if_missing(&default_conf_info
, value
) < 0)
435 warning(_("unknown value '%s' for key '%s'"),
437 } else if (!strcmp(trailer_item
, "separators")) {
438 separators
= xstrdup(value
);
444 static int git_trailer_config(const char *conf_key
, const char *value
, void *cb
)
446 const char *trailer_item
, *variable_name
;
447 struct trailer_item
*item
;
448 struct conf_info
*conf
;
450 enum trailer_info_type type
;
453 if (!skip_prefix(conf_key
, "trailer.", &trailer_item
))
456 variable_name
= strrchr(trailer_item
, '.');
461 for (i
= 0; i
< ARRAY_SIZE(trailer_config_items
); i
++) {
462 if (strcmp(trailer_config_items
[i
].name
, variable_name
))
464 name
= xstrndup(trailer_item
, variable_name
- trailer_item
- 1);
465 type
= trailer_config_items
[i
].type
;
472 item
= get_conf_item(name
);
479 warning(_("more than one %s"), conf_key
);
480 conf
->key
= xstrdup(value
);
482 case TRAILER_COMMAND
:
484 warning(_("more than one %s"), conf_key
);
485 conf
->command
= xstrdup(value
);
488 if (set_where(conf
, value
))
489 warning(_("unknown value '%s' for key '%s'"), value
, conf_key
);
491 case TRAILER_IF_EXISTS
:
492 if (set_if_exists(conf
, value
))
493 warning(_("unknown value '%s' for key '%s'"), value
, conf_key
);
495 case TRAILER_IF_MISSING
:
496 if (set_if_missing(conf
, value
))
497 warning(_("unknown value '%s' for key '%s'"), value
, conf_key
);
500 die("internal bug in trailer.c");
505 static int parse_trailer(struct strbuf
*tok
, struct strbuf
*val
, const char *trailer
)
508 struct strbuf seps
= STRBUF_INIT
;
509 strbuf_addstr(&seps
, separators
);
510 strbuf_addch(&seps
, '=');
511 len
= strcspn(trailer
, seps
.buf
);
512 strbuf_release(&seps
);
514 return error(_("empty trailer token in trailer '%s'"), trailer
);
515 if (len
< strlen(trailer
)) {
516 strbuf_add(tok
, trailer
, len
);
518 strbuf_addstr(val
, trailer
+ len
+ 1);
521 strbuf_addstr(tok
, trailer
);
527 static const char *token_from_item(struct trailer_item
*item
, char *tok
)
530 return item
->conf
.key
;
533 return item
->conf
.name
;
536 static struct trailer_item
*new_trailer_item(struct trailer_item
*conf_item
,
537 char *tok
, char *val
)
539 struct trailer_item
*new = xcalloc(sizeof(*new), 1);
543 duplicate_conf(&new->conf
, &conf_item
->conf
);
544 new->token
= xstrdup(token_from_item(conf_item
, tok
));
547 duplicate_conf(&new->conf
, &default_conf_info
);
554 static int token_matches_item(const char *tok
, struct trailer_item
*item
, int tok_len
)
556 if (!strncasecmp(tok
, item
->conf
.name
, tok_len
))
558 return item
->conf
.key
? !strncasecmp(tok
, item
->conf
.key
, tok_len
) : 0;
561 static struct trailer_item
*create_trailer_item(const char *string
)
563 struct strbuf tok
= STRBUF_INIT
;
564 struct strbuf val
= STRBUF_INIT
;
565 struct trailer_item
*item
;
568 if (parse_trailer(&tok
, &val
, string
))
571 tok_len
= token_len_without_separator(tok
.buf
, tok
.len
);
573 /* Lookup if the token matches something in the config */
574 for (item
= first_conf_item
; item
; item
= item
->next
) {
575 if (token_matches_item(tok
.buf
, item
, tok_len
))
576 return new_trailer_item(item
,
577 strbuf_detach(&tok
, NULL
),
578 strbuf_detach(&val
, NULL
));
581 return new_trailer_item(NULL
,
582 strbuf_detach(&tok
, NULL
),
583 strbuf_detach(&val
, NULL
));
586 static void add_trailer_item(struct trailer_item
**first
,
587 struct trailer_item
**last
,
588 struct trailer_item
*new)
597 new->previous
= *last
;
602 static struct trailer_item
*process_command_line_args(struct string_list
*trailers
)
604 struct trailer_item
*arg_tok_first
= NULL
;
605 struct trailer_item
*arg_tok_last
= NULL
;
606 struct string_list_item
*tr
;
608 for_each_string_list_item(tr
, trailers
) {
609 struct trailer_item
*new = create_trailer_item(tr
->string
);
610 add_trailer_item(&arg_tok_first
, &arg_tok_last
, new);
613 return arg_tok_first
;
616 static struct strbuf
**read_input_file(const char *file
)
618 struct strbuf
**lines
;
619 struct strbuf sb
= STRBUF_INIT
;
622 if (strbuf_read_file(&sb
, file
, 0) < 0)
623 die_errno(_("could not read input file '%s'"), file
);
625 if (strbuf_read(&sb
, fileno(stdin
), 0) < 0)
626 die_errno(_("could not read from stdin"));
629 lines
= strbuf_split(&sb
, '\n');
637 * Return the (0 based) index of the start of the patch or the line
638 * count if there is no patch in the message.
640 static int find_patch_start(struct strbuf
**lines
, int count
)
644 /* Get the start of the patch part if any */
645 for (i
= 0; i
< count
; i
++) {
646 if (starts_with(lines
[i
]->buf
, "---"))
654 * Return the (0 based) index of the first trailer line or count if
655 * there are no trailers. Trailers are searched only in the lines from
656 * index (count - 1) down to index 0.
658 static int find_trailer_start(struct strbuf
**lines
, int count
)
660 int start
, only_spaces
= 1;
663 * Get the start of the trailers by looking starting from the end
664 * for a line with only spaces before lines with one separator.
666 for (start
= count
- 1; start
>= 0; start
--) {
667 if (lines
[start
]->buf
[0] == comment_line_char
)
669 if (contains_only_spaces(lines
[start
]->buf
)) {
674 if (strcspn(lines
[start
]->buf
, separators
) < lines
[start
]->len
) {
682 return only_spaces
? count
: 0;
685 static int has_blank_line_before(struct strbuf
**lines
, int start
)
687 for (;start
>= 0; start
--) {
688 if (lines
[start
]->buf
[0] == comment_line_char
)
690 return contains_only_spaces(lines
[start
]->buf
);
695 static void print_lines(struct strbuf
**lines
, int start
, int end
)
698 for (i
= start
; lines
[i
] && i
< end
; i
++)
699 printf("%s", lines
[i
]->buf
);
702 static int process_input_file(struct strbuf
**lines
,
703 struct trailer_item
**in_tok_first
,
704 struct trailer_item
**in_tok_last
)
707 int patch_start
, trailer_start
, i
;
709 /* Get the line count */
713 patch_start
= find_patch_start(lines
, count
);
714 trailer_start
= find_trailer_start(lines
, patch_start
);
716 /* Print lines before the trailers as is */
717 print_lines(lines
, 0, trailer_start
);
719 if (!has_blank_line_before(lines
, trailer_start
- 1))
722 /* Parse trailer lines */
723 for (i
= trailer_start
; i
< patch_start
; i
++) {
724 struct trailer_item
*new = create_trailer_item(lines
[i
]->buf
);
725 add_trailer_item(in_tok_first
, in_tok_last
, new);
731 static void free_all(struct trailer_item
**first
)
734 struct trailer_item
*item
= remove_first(first
);
735 free_trailer_item(item
);
739 void process_trailers(const char *file
, int trim_empty
, struct string_list
*trailers
)
741 struct trailer_item
*in_tok_first
= NULL
;
742 struct trailer_item
*in_tok_last
= NULL
;
743 struct trailer_item
*arg_tok_first
;
744 struct strbuf
**lines
;
747 /* Default config must be setup first */
748 git_config(git_trailer_default_config
, NULL
);
749 git_config(git_trailer_config
, NULL
);
751 lines
= read_input_file(file
);
753 /* Print the lines before the trailers */
754 patch_start
= process_input_file(lines
, &in_tok_first
, &in_tok_last
);
756 arg_tok_first
= process_command_line_args(trailers
);
758 process_trailers_lists(&in_tok_first
, &in_tok_last
, &arg_tok_first
);
760 print_all(in_tok_first
, trim_empty
);
762 free_all(&in_tok_first
);
764 /* Print the lines after the trailers as is */
765 print_lines(lines
, patch_start
, INT_MAX
);
767 strbuf_list_free(lines
);