3 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
6 enum action_where
{ WHERE_END
, WHERE_AFTER
, WHERE_BEFORE
, WHERE_START
};
7 enum action_if_exists
{ EXISTS_ADD_IF_DIFFERENT_NEIGHBOR
, EXISTS_ADD_IF_DIFFERENT
,
8 EXISTS_ADD
, EXISTS_REPLACE
, EXISTS_DO_NOTHING
};
9 enum action_if_missing
{ MISSING_ADD
, MISSING_DO_NOTHING
};
15 enum action_where where
;
16 enum action_if_exists if_exists
;
17 enum action_if_missing if_missing
;
20 static struct conf_info default_conf_info
;
23 struct trailer_item
*previous
;
24 struct trailer_item
*next
;
27 struct conf_info conf
;
30 static struct trailer_item
*first_conf_item
;
32 static char *separators
= ":";
34 static int after_or_end(enum action_where where
)
36 return (where
== WHERE_AFTER
) || (where
== WHERE_END
);
40 * Return the length of the string not including any final
41 * punctuation. E.g., the input "Signed-off-by:" would return
42 * 13, stripping the trailing punctuation but retaining
43 * internal punctuation.
45 static size_t token_len_without_separator(const char *token
, size_t len
)
47 while (len
> 0 && !isalnum(token
[len
- 1]))
52 static int same_token(struct trailer_item
*a
, struct trailer_item
*b
)
54 size_t a_len
= token_len_without_separator(a
->token
, strlen(a
->token
));
55 size_t b_len
= token_len_without_separator(b
->token
, strlen(b
->token
));
56 size_t min_len
= (a_len
> b_len
) ? b_len
: a_len
;
58 return !strncasecmp(a
->token
, b
->token
, min_len
);
61 static int same_value(struct trailer_item
*a
, struct trailer_item
*b
)
63 return !strcasecmp(a
->value
, b
->value
);
66 static int same_trailer(struct trailer_item
*a
, struct trailer_item
*b
)
68 return same_token(a
, b
) && same_value(a
, b
);
71 static void free_trailer_item(struct trailer_item
*item
)
73 free(item
->conf
.name
);
75 free(item
->conf
.command
);
76 free((char *)item
->token
);
77 free((char *)item
->value
);
81 static void update_last(struct trailer_item
**last
)
84 while ((*last
)->next
!= NULL
)
85 *last
= (*last
)->next
;
88 static void update_first(struct trailer_item
**first
)
91 while ((*first
)->previous
!= NULL
)
92 *first
= (*first
)->previous
;
95 static void add_arg_to_input_list(struct trailer_item
*on_tok
,
96 struct trailer_item
*arg_tok
,
97 struct trailer_item
**first
,
98 struct trailer_item
**last
)
100 if (after_or_end(arg_tok
->conf
.where
)) {
101 arg_tok
->next
= on_tok
->next
;
102 on_tok
->next
= arg_tok
;
103 arg_tok
->previous
= on_tok
;
105 arg_tok
->next
->previous
= arg_tok
;
108 arg_tok
->previous
= on_tok
->previous
;
109 on_tok
->previous
= arg_tok
;
110 arg_tok
->next
= on_tok
;
111 if (arg_tok
->previous
)
112 arg_tok
->previous
->next
= arg_tok
;
117 static int check_if_different(struct trailer_item
*in_tok
,
118 struct trailer_item
*arg_tok
,
121 enum action_where where
= arg_tok
->conf
.where
;
125 if (same_trailer(in_tok
, arg_tok
))
128 * if we want to add a trailer after another one,
129 * we have to check those before this one
131 in_tok
= after_or_end(where
) ? in_tok
->previous
: in_tok
->next
;
136 static void remove_from_list(struct trailer_item
*item
,
137 struct trailer_item
**first
,
138 struct trailer_item
**last
)
140 struct trailer_item
*next
= item
->next
;
141 struct trailer_item
*previous
= item
->previous
;
144 item
->next
->previous
= previous
;
150 item
->previous
->next
= next
;
151 item
->previous
= NULL
;
156 static struct trailer_item
*remove_first(struct trailer_item
**first
)
158 struct trailer_item
*item
= *first
;
161 item
->next
->previous
= NULL
;
167 static void apply_arg_if_exists(struct trailer_item
*in_tok
,
168 struct trailer_item
*arg_tok
,
169 struct trailer_item
*on_tok
,
170 struct trailer_item
**in_tok_first
,
171 struct trailer_item
**in_tok_last
)
173 switch (arg_tok
->conf
.if_exists
) {
174 case EXISTS_DO_NOTHING
:
175 free_trailer_item(arg_tok
);
178 add_arg_to_input_list(on_tok
, arg_tok
,
179 in_tok_first
, in_tok_last
);
180 remove_from_list(in_tok
, in_tok_first
, in_tok_last
);
181 free_trailer_item(in_tok
);
184 add_arg_to_input_list(on_tok
, arg_tok
,
185 in_tok_first
, in_tok_last
);
187 case EXISTS_ADD_IF_DIFFERENT
:
188 if (check_if_different(in_tok
, arg_tok
, 1))
189 add_arg_to_input_list(on_tok
, arg_tok
,
190 in_tok_first
, in_tok_last
);
192 free_trailer_item(arg_tok
);
194 case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR
:
195 if (check_if_different(on_tok
, arg_tok
, 0))
196 add_arg_to_input_list(on_tok
, arg_tok
,
197 in_tok_first
, in_tok_last
);
199 free_trailer_item(arg_tok
);
204 static void apply_arg_if_missing(struct trailer_item
**in_tok_first
,
205 struct trailer_item
**in_tok_last
,
206 struct trailer_item
*arg_tok
)
208 struct trailer_item
**in_tok
;
209 enum action_where where
;
211 switch (arg_tok
->conf
.if_missing
) {
212 case MISSING_DO_NOTHING
:
213 free_trailer_item(arg_tok
);
216 where
= arg_tok
->conf
.where
;
217 in_tok
= after_or_end(where
) ? in_tok_last
: in_tok_first
;
219 add_arg_to_input_list(*in_tok
, arg_tok
,
220 in_tok_first
, in_tok_last
);
222 *in_tok_first
= arg_tok
;
223 *in_tok_last
= arg_tok
;
229 static int find_same_and_apply_arg(struct trailer_item
**in_tok_first
,
230 struct trailer_item
**in_tok_last
,
231 struct trailer_item
*arg_tok
)
233 struct trailer_item
*in_tok
;
234 struct trailer_item
*on_tok
;
235 struct trailer_item
*following_tok
;
237 enum action_where where
= arg_tok
->conf
.where
;
238 int middle
= (where
== WHERE_AFTER
) || (where
== WHERE_BEFORE
);
239 int backwards
= after_or_end(where
);
240 struct trailer_item
*start_tok
= backwards
? *in_tok_last
: *in_tok_first
;
242 for (in_tok
= start_tok
; in_tok
; in_tok
= following_tok
) {
243 following_tok
= backwards
? in_tok
->previous
: in_tok
->next
;
244 if (!same_token(in_tok
, arg_tok
))
246 on_tok
= middle
? in_tok
: start_tok
;
247 apply_arg_if_exists(in_tok
, arg_tok
, on_tok
,
248 in_tok_first
, in_tok_last
);
254 static void process_trailers_lists(struct trailer_item
**in_tok_first
,
255 struct trailer_item
**in_tok_last
,
256 struct trailer_item
**arg_tok_first
)
258 struct trailer_item
*arg_tok
;
259 struct trailer_item
*next_arg
;
264 for (arg_tok
= *arg_tok_first
; arg_tok
; arg_tok
= next_arg
) {
267 next_arg
= arg_tok
->next
;
268 remove_from_list(arg_tok
, arg_tok_first
, NULL
);
270 applied
= find_same_and_apply_arg(in_tok_first
,
275 apply_arg_if_missing(in_tok_first
,