2 #include "string-list.h"
3 #include "run-command.h"
4 #include "string-list.h"
7 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
10 enum action_where
{ WHERE_AFTER
, WHERE_BEFORE
};
11 enum action_if_exists
{ EXISTS_ADD_IF_DIFFERENT
, EXISTS_ADD_IF_DIFFERENT_NEIGHBOR
,
12 EXISTS_ADD
, EXISTS_OVERWRITE
, EXISTS_DO_NOTHING
};
13 enum action_if_missing
{ MISSING_ADD
, MISSING_DO_NOTHING
};
19 unsigned command_uses_arg
: 1;
20 enum action_where where
;
21 enum action_if_exists if_exists
;
22 enum action_if_missing if_missing
;
25 #define TRAILER_ARG_STRING "$ARG"
28 struct trailer_item
*previous
;
29 struct trailer_item
*next
;
32 struct conf_info conf
;
35 static struct trailer_item
*first_conf_item
;
37 static int same_token(struct trailer_item
*a
, struct trailer_item
*b
, int alnum_len
)
39 return !strncasecmp(a
->token
, b
->token
, alnum_len
);
42 static int same_value(struct trailer_item
*a
, struct trailer_item
*b
)
44 return !strcasecmp(a
->value
, b
->value
);
47 static int same_trailer(struct trailer_item
*a
, struct trailer_item
*b
, int alnum_len
)
49 return same_token(a
, b
, alnum_len
) && same_value(a
, b
);
52 /* Get the length of buf from its beginning until its last alphanumeric character */
53 static size_t alnum_len(const char *buf
, size_t len
)
55 while (len
> 0 && !isalnum(buf
[len
- 1]))
60 static inline int contains_only_spaces(const char *str
)
63 while (*s
&& isspace(*s
))
68 static inline void strbuf_replace(struct strbuf
*sb
, const char *a
, const char *b
)
70 const char *ptr
= strstr(sb
->buf
, a
);
72 strbuf_splice(sb
, ptr
- sb
->buf
, strlen(a
), b
, strlen(b
));
75 static void free_trailer_item(struct trailer_item
*item
)
77 free(item
->conf
.name
);
79 free(item
->conf
.command
);
80 free((char *)item
->token
);
81 free((char *)item
->value
);
85 static void print_tok_val(const char *tok
, const char *val
)
87 char c
= tok
[strlen(tok
) - 1];
89 printf("%s: %s\n", tok
, val
);
91 printf("%s%s\n", tok
, val
);
94 static void print_all(struct trailer_item
*first
, int trim_empty
)
96 struct trailer_item
*item
;
97 for (item
= first
; item
; item
= item
->next
) {
98 if (!trim_empty
|| strlen(item
->value
) > 0)
99 print_tok_val(item
->token
, item
->value
);
103 static void add_arg_to_input_list(struct trailer_item
*in_tok
,
104 struct trailer_item
*arg_tok
)
106 if (arg_tok
->conf
.where
== WHERE_AFTER
) {
107 arg_tok
->next
= in_tok
->next
;
108 in_tok
->next
= arg_tok
;
109 arg_tok
->previous
= in_tok
;
111 arg_tok
->next
->previous
= arg_tok
;
113 arg_tok
->previous
= in_tok
->previous
;
114 in_tok
->previous
= arg_tok
;
115 arg_tok
->next
= in_tok
;
116 if (arg_tok
->previous
)
117 arg_tok
->previous
->next
= arg_tok
;
121 static int check_if_different(struct trailer_item
*in_tok
,
122 struct trailer_item
*arg_tok
,
123 int alnum_len
, int check_all
)
125 enum action_where where
= arg_tok
->conf
.where
;
129 if (same_trailer(in_tok
, arg_tok
, alnum_len
))
132 * if we want to add a trailer after another one,
133 * we have to check those before this one
135 in_tok
= (where
== WHERE_AFTER
) ? in_tok
->previous
: in_tok
->next
;
140 static void apply_arg_if_exists(struct trailer_item
*in_tok
,
141 struct trailer_item
*arg_tok
,
144 switch (arg_tok
->conf
.if_exists
) {
145 case EXISTS_DO_NOTHING
:
146 free_trailer_item(arg_tok
);
148 case EXISTS_OVERWRITE
:
149 free((char *)in_tok
->value
);
150 in_tok
->value
= xstrdup(arg_tok
->value
);
151 free_trailer_item(arg_tok
);
154 add_arg_to_input_list(in_tok
, arg_tok
);
156 case EXISTS_ADD_IF_DIFFERENT
:
157 if (check_if_different(in_tok
, arg_tok
, alnum_len
, 1))
158 add_arg_to_input_list(in_tok
, arg_tok
);
160 free_trailer_item(arg_tok
);
162 case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR
:
163 if (check_if_different(in_tok
, arg_tok
, alnum_len
, 0))
164 add_arg_to_input_list(in_tok
, arg_tok
);
166 free_trailer_item(arg_tok
);
171 static void remove_from_list(struct trailer_item
*item
,
172 struct trailer_item
**first
)
175 item
->next
->previous
= item
->previous
;
177 item
->previous
->next
= item
->next
;
182 static struct trailer_item
*remove_first(struct trailer_item
**first
)
184 struct trailer_item
*item
= *first
;
187 item
->next
->previous
= NULL
;
193 static void process_input_token(struct trailer_item
*in_tok
,
194 struct trailer_item
**arg_tok_first
,
195 enum action_where where
)
197 struct trailer_item
*arg_tok
;
198 struct trailer_item
*next_arg
;
200 int after
= where
== WHERE_AFTER
;
201 int tok_alnum_len
= alnum_len(in_tok
->token
, strlen(in_tok
->token
));
203 for (arg_tok
= *arg_tok_first
; arg_tok
; arg_tok
= next_arg
) {
204 next_arg
= arg_tok
->next
;
205 if (!same_token(in_tok
, arg_tok
, tok_alnum_len
))
207 if (arg_tok
->conf
.where
!= where
)
209 remove_from_list(arg_tok
, arg_tok_first
);
210 apply_arg_if_exists(in_tok
, arg_tok
, tok_alnum_len
);
212 * If arg has been added to input,
213 * then we need to process it too now.
215 if ((after
? in_tok
->next
: in_tok
->previous
) == arg_tok
)
220 static void update_last(struct trailer_item
**last
)
223 while ((*last
)->next
!= NULL
)
224 *last
= (*last
)->next
;
227 static void update_first(struct trailer_item
**first
)
230 while ((*first
)->previous
!= NULL
)
231 *first
= (*first
)->previous
;
234 static void apply_arg_if_missing(struct trailer_item
**in_tok_first
,
235 struct trailer_item
**in_tok_last
,
236 struct trailer_item
*arg_tok
)
238 struct trailer_item
**in_tok
;
239 enum action_where where
;
241 switch (arg_tok
->conf
.if_missing
) {
242 case MISSING_DO_NOTHING
:
243 free_trailer_item(arg_tok
);
246 where
= arg_tok
->conf
.where
;
247 in_tok
= (where
== WHERE_AFTER
) ? in_tok_last
: in_tok_first
;
249 add_arg_to_input_list(*in_tok
, arg_tok
);
252 *in_tok_first
= arg_tok
;
253 *in_tok_last
= arg_tok
;
259 static void process_trailers_lists(struct trailer_item
**in_tok_first
,
260 struct trailer_item
**in_tok_last
,
261 struct trailer_item
**arg_tok_first
)
263 struct trailer_item
*in_tok
;
264 struct trailer_item
*arg_tok
;
269 /* Process input from end to start */
270 for (in_tok
= *in_tok_last
; in_tok
; in_tok
= in_tok
->previous
)
271 process_input_token(in_tok
, arg_tok_first
, WHERE_AFTER
);
273 update_last(in_tok_last
);
278 /* Process input from start to end */
279 for (in_tok
= *in_tok_first
; in_tok
; in_tok
= in_tok
->next
)
280 process_input_token(in_tok
, arg_tok_first
, WHERE_BEFORE
);
282 update_first(in_tok_first
);
284 /* Process args left */
285 while (*arg_tok_first
) {
286 arg_tok
= remove_first(arg_tok_first
);
287 apply_arg_if_missing(in_tok_first
, in_tok_last
, arg_tok
);
291 static int set_where(struct conf_info
*item
, const char *value
)
293 if (!strcasecmp("after", value
))
294 item
->where
= WHERE_AFTER
;
295 else if (!strcasecmp("before", value
))
296 item
->where
= WHERE_BEFORE
;
302 static int set_if_exists(struct conf_info
*item
, const char *value
)
304 if (!strcasecmp("addIfDifferent", value
))
305 item
->if_exists
= EXISTS_ADD_IF_DIFFERENT
;
306 else if (!strcasecmp("addIfDifferentNeighbor", value
))
307 item
->if_exists
= EXISTS_ADD_IF_DIFFERENT_NEIGHBOR
;
308 else if (!strcasecmp("add", value
))
309 item
->if_exists
= EXISTS_ADD
;
310 else if (!strcasecmp("overwrite", value
))
311 item
->if_exists
= EXISTS_OVERWRITE
;
312 else if (!strcasecmp("doNothing", value
))
313 item
->if_exists
= EXISTS_DO_NOTHING
;
319 static int set_if_missing(struct conf_info
*item
, const char *value
)
321 if (!strcasecmp("doNothing", value
))
322 item
->if_missing
= MISSING_DO_NOTHING
;
323 else if (!strcasecmp("add", value
))
324 item
->if_missing
= MISSING_ADD
;
330 static struct trailer_item
*get_conf_item(const char *name
)
332 struct trailer_item
*item
;
333 struct trailer_item
*previous
;
335 /* Look up item with same name */
336 for (previous
= NULL
, item
= first_conf_item
;
338 previous
= item
, item
= item
->next
) {
339 if (!strcasecmp(item
->conf
.name
, name
))
343 /* Item does not already exists, create it */
344 item
= xcalloc(sizeof(struct trailer_item
), 1);
345 item
->conf
.name
= xstrdup(name
);
348 first_conf_item
= item
;
350 previous
->next
= item
;
351 item
->previous
= previous
;
357 enum trailer_info_type
{ TRAILER_KEY
, TRAILER_COMMAND
, TRAILER_WHERE
,
358 TRAILER_IF_EXISTS
, TRAILER_IF_MISSING
};
362 enum trailer_info_type type
;
363 } trailer_config_items
[] = {
364 { "key", TRAILER_KEY
},
365 { "command", TRAILER_COMMAND
},
366 { "where", TRAILER_WHERE
},
367 { "ifexists", TRAILER_IF_EXISTS
},
368 { "ifmissing", TRAILER_IF_MISSING
}
371 static int git_trailer_config(const char *conf_key
, const char *value
, void *cb
)
373 const char *trailer_item
, *variable_name
;
374 struct trailer_item
*item
;
375 struct conf_info
*conf
;
377 enum trailer_info_type type
;
380 if (!skip_prefix(conf_key
, "trailer.", &trailer_item
))
383 variable_name
= strrchr(trailer_item
, '.');
384 if (!variable_name
) {
385 warning(_("two level trailer config variable %s"), conf_key
);
390 for (i
= 0; i
< ARRAY_SIZE(trailer_config_items
); i
++) {
391 if (strcmp(trailer_config_items
[i
].name
, variable_name
))
393 name
= xstrndup(trailer_item
, variable_name
- trailer_item
- 1);
394 type
= trailer_config_items
[i
].type
;
401 item
= get_conf_item(name
);
408 warning(_("more than one %s"), conf_key
);
409 conf
->key
= xstrdup(value
);
411 case TRAILER_COMMAND
:
413 warning(_("more than one %s"), conf_key
);
414 conf
->command
= xstrdup(value
);
415 conf
->command_uses_arg
= !!strstr(conf
->command
, TRAILER_ARG_STRING
);
418 if (set_where(conf
, value
))
419 warning(_("unknown value '%s' for key '%s'"), value
, conf_key
);
421 case TRAILER_IF_EXISTS
:
422 if (set_if_exists(conf
, value
))
423 warning(_("unknown value '%s' for key '%s'"), value
, conf_key
);
425 case TRAILER_IF_MISSING
:
426 if (set_if_missing(conf
, value
))
427 warning(_("unknown value '%s' for key '%s'"), value
, conf_key
);
430 die("internal bug in trailer.c");
435 static int parse_trailer(struct strbuf
*tok
, struct strbuf
*val
, const char *trailer
)
437 size_t len
= strcspn(trailer
, "=:");
439 return error(_("empty trailer token in trailer '%s'"), trailer
);
440 if (len
< strlen(trailer
)) {
441 strbuf_add(tok
, trailer
, len
);
443 strbuf_addstr(val
, trailer
+ len
+ 1);
446 strbuf_addstr(tok
, trailer
);
452 static int read_from_command(struct child_process
*cp
, struct strbuf
*buf
)
455 return error("running trailer command '%s' failed", cp
->argv
[0]);
456 if (strbuf_read(buf
, cp
->out
, 1024) < 1)
457 return error("reading from trailer command '%s' failed", cp
->argv
[0]);
462 static const char *apply_command(const char *command
, const char *arg
)
464 struct strbuf cmd
= STRBUF_INIT
;
465 struct strbuf buf
= STRBUF_INIT
;
466 struct child_process cp
;
467 const char *argv
[] = {NULL
, NULL
};
470 strbuf_addstr(&cmd
, command
);
472 strbuf_replace(&cmd
, TRAILER_ARG_STRING
, arg
);
475 memset(&cp
, 0, sizeof(cp
));
477 cp
.env
= local_repo_env
;
482 if (read_from_command(&cp
, &buf
)) {
483 strbuf_release(&buf
);
484 result
= xstrdup("");
486 result
= strbuf_detach(&buf
, NULL
);
488 strbuf_release(&cmd
);
492 static void duplicate_conf(struct conf_info
*dst
, struct conf_info
*src
)
496 dst
->name
= xstrdup(src
->name
);
498 dst
->key
= xstrdup(src
->key
);
500 dst
->command
= xstrdup(src
->command
);
503 static const char *token_from_item(struct trailer_item
*item
)
506 return item
->conf
.key
;
508 return item
->conf
.name
;
511 static struct trailer_item
*new_trailer_item(struct trailer_item
*conf_item
,
512 char *tok
, char *val
)
514 struct trailer_item
*new = xcalloc(sizeof(*new), 1);
518 duplicate_conf(&new->conf
, &conf_item
->conf
);
519 new->token
= xstrdup(token_from_item(conf_item
));
521 if (conf_item
->conf
.command_uses_arg
|| !val
) {
522 new->value
= apply_command(conf_item
->conf
.command
, val
);
531 static int token_matches_item(const char *tok
, struct trailer_item
*item
, int alnum_len
)
533 if (!strncasecmp(tok
, item
->conf
.name
, alnum_len
))
535 return item
->conf
.key
? !strncasecmp(tok
, item
->conf
.key
, alnum_len
) : 0;
538 static struct trailer_item
*create_trailer_item(const char *string
)
540 struct strbuf tok
= STRBUF_INIT
;
541 struct strbuf val
= STRBUF_INIT
;
542 struct trailer_item
*item
;
545 if (parse_trailer(&tok
, &val
, string
))
548 tok_alnum_len
= alnum_len(tok
.buf
, tok
.len
);
550 /* Lookup if the token matches something in the config */
551 for (item
= first_conf_item
; item
; item
= item
->next
) {
552 if (token_matches_item(tok
.buf
, item
, tok_alnum_len
)) {
553 strbuf_release(&tok
);
554 return new_trailer_item(item
,
556 strbuf_detach(&val
, NULL
));
560 return new_trailer_item(NULL
,
561 strbuf_detach(&tok
, NULL
),
562 strbuf_detach(&val
, NULL
));
565 static void add_trailer_item(struct trailer_item
**first
,
566 struct trailer_item
**last
,
567 struct trailer_item
*new)
576 new->previous
= *last
;
581 static struct trailer_item
*process_command_line_args(struct string_list
*trailers
)
583 struct trailer_item
*arg_tok_first
= NULL
;
584 struct trailer_item
*arg_tok_last
= NULL
;
585 struct string_list_item
*tr
;
586 struct trailer_item
*item
;
588 for_each_string_list_item(tr
, trailers
) {
589 struct trailer_item
*new = create_trailer_item(tr
->string
);
590 add_trailer_item(&arg_tok_first
, &arg_tok_last
, new);
593 /* Add conf commands that don't use $ARG */
594 for (item
= first_conf_item
; item
; item
= item
->next
) {
595 if (item
->conf
.command
&& !item
->conf
.command_uses_arg
) {
596 struct trailer_item
*new = new_trailer_item(item
, NULL
, NULL
);
597 add_trailer_item(&arg_tok_first
, &arg_tok_last
, new);
601 return arg_tok_first
;
604 static struct strbuf
**read_input_file(const char *file
)
606 struct strbuf
**lines
;
607 struct strbuf sb
= STRBUF_INIT
;
610 if (strbuf_read_file(&sb
, file
, 0) < 0)
611 die_errno(_("could not read input file '%s'"), file
);
613 if (strbuf_read(&sb
, fileno(stdin
), 0) < 0)
614 die_errno(_("could not read from stdin"));
617 lines
= strbuf_split(&sb
, '\n');
625 * Return the (0 based) index of the start of the patch or the line
626 * count if there is no patch in the message.
628 static int find_patch_start(struct strbuf
**lines
, int count
)
632 /* Get the start of the patch part if any */
633 for (i
= 0; i
< count
; i
++) {
634 if (starts_with(lines
[i
]->buf
, "---"))
642 * Return the (0 based) index of the first trailer line or count if
643 * there are no trailers. Trailers are searched only in the lines from
644 * index (count - 1) down to index 0.
646 static int find_trailer_start(struct strbuf
**lines
, int count
)
648 int start
, only_spaces
= 1;
651 * Get the start of the trailers by looking starting from the end
652 * for a line with only spaces before lines with one ':'.
654 for (start
= count
- 1; start
>= 0; start
--) {
655 if (lines
[start
]->buf
[0] == comment_line_char
)
657 if (contains_only_spaces(lines
[start
]->buf
)) {
662 if (strchr(lines
[start
]->buf
, ':')) {
670 return only_spaces
? count
: 0;
673 static int has_blank_line_before(struct strbuf
**lines
, int start
)
675 for (;start
>= 0; start
--) {
676 if (lines
[start
]->buf
[0] == comment_line_char
)
678 return contains_only_spaces(lines
[start
]->buf
);
683 static void print_lines(struct strbuf
**lines
, int start
, int end
)
686 for (i
= start
; lines
[i
] && i
< end
; i
++)
687 printf("%s", lines
[i
]->buf
);
690 static int process_input_file(struct strbuf
**lines
,
691 struct trailer_item
**in_tok_first
,
692 struct trailer_item
**in_tok_last
)
695 int patch_start
, trailer_start
, i
;
697 /* Get the line count */
701 patch_start
= find_patch_start(lines
, count
);
702 trailer_start
= find_trailer_start(lines
, patch_start
);
704 /* Print lines before the trailers as is */
705 print_lines(lines
, 0, trailer_start
);
707 if (!has_blank_line_before(lines
, trailer_start
- 1))
710 /* Parse trailer lines */
711 for (i
= trailer_start
; i
< patch_start
; i
++) {
712 struct trailer_item
*new = create_trailer_item(lines
[i
]->buf
);
713 add_trailer_item(in_tok_first
, in_tok_last
, new);
719 static void free_all(struct trailer_item
**first
)
722 struct trailer_item
*item
= remove_first(first
);
723 free_trailer_item(item
);
727 void process_trailers(const char *file
, int trim_empty
, struct string_list
*trailers
)
729 struct trailer_item
*in_tok_first
= NULL
;
730 struct trailer_item
*in_tok_last
= NULL
;
731 struct trailer_item
*arg_tok_first
;
732 struct strbuf
**lines
;
735 git_config(git_trailer_config
, NULL
);
737 lines
= read_input_file(file
);
739 /* Print the lines before the trailers */
740 patch_start
= process_input_file(lines
, &in_tok_first
, &in_tok_last
);
742 arg_tok_first
= process_command_line_args(trailers
);
744 process_trailers_lists(&in_tok_first
, &in_tok_last
, &arg_tok_first
);
746 print_all(in_tok_first
, trim_empty
);
748 free_all(&in_tok_first
);
750 /* Print the lines after the trailers as is */
751 print_lines(lines
, patch_start
, INT_MAX
);
753 strbuf_list_free(lines
);