]>
Commit | Line | Data |
---|---|---|
9385b5d7 CC |
1 | #include "cache.h" |
2 | /* | |
3 | * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org> | |
4 | */ | |
5 | ||
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 }; | |
10 | ||
11 | struct conf_info { | |
12 | char *name; | |
13 | char *key; | |
14 | char *command; | |
15 | enum action_where where; | |
16 | enum action_if_exists if_exists; | |
17 | enum action_if_missing if_missing; | |
18 | }; | |
19 | ||
20 | static struct conf_info default_conf_info; | |
21 | ||
22 | struct trailer_item { | |
23 | struct trailer_item *previous; | |
24 | struct trailer_item *next; | |
25 | const char *token; | |
26 | const char *value; | |
27 | struct conf_info conf; | |
28 | }; | |
29 | ||
30 | static struct trailer_item *first_conf_item; | |
31 | ||
32 | static char *separators = ":"; | |
33 | ||
34 | static int after_or_end(enum action_where where) | |
35 | { | |
36 | return (where == WHERE_AFTER) || (where == WHERE_END); | |
37 | } | |
38 | ||
39 | /* | |
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. | |
44 | */ | |
45 | static size_t token_len_without_separator(const char *token, size_t len) | |
46 | { | |
47 | while (len > 0 && !isalnum(token[len - 1])) | |
48 | len--; | |
49 | return len; | |
50 | } | |
51 | ||
52 | static int same_token(struct trailer_item *a, struct trailer_item *b) | |
53 | { | |
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; | |
57 | ||
58 | return !strncasecmp(a->token, b->token, min_len); | |
59 | } | |
60 | ||
61 | static int same_value(struct trailer_item *a, struct trailer_item *b) | |
62 | { | |
63 | return !strcasecmp(a->value, b->value); | |
64 | } | |
65 | ||
66 | static int same_trailer(struct trailer_item *a, struct trailer_item *b) | |
67 | { | |
68 | return same_token(a, b) && same_value(a, b); | |
69 | } | |
4103818d CC |
70 | |
71 | static void free_trailer_item(struct trailer_item *item) | |
72 | { | |
73 | free(item->conf.name); | |
74 | free(item->conf.key); | |
75 | free(item->conf.command); | |
76 | free((char *)item->token); | |
77 | free((char *)item->value); | |
78 | free(item); | |
79 | } | |
80 | ||
81 | static void update_last(struct trailer_item **last) | |
82 | { | |
83 | if (*last) | |
84 | while ((*last)->next != NULL) | |
85 | *last = (*last)->next; | |
86 | } | |
87 | ||
88 | static void update_first(struct trailer_item **first) | |
89 | { | |
90 | if (*first) | |
91 | while ((*first)->previous != NULL) | |
92 | *first = (*first)->previous; | |
93 | } | |
94 | ||
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) | |
99 | { | |
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; | |
104 | if (arg_tok->next) | |
105 | arg_tok->next->previous = arg_tok; | |
106 | update_last(last); | |
107 | } else { | |
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; | |
113 | update_first(first); | |
114 | } | |
115 | } | |
116 | ||
117 | static int check_if_different(struct trailer_item *in_tok, | |
118 | struct trailer_item *arg_tok, | |
119 | int check_all) | |
120 | { | |
121 | enum action_where where = arg_tok->conf.where; | |
122 | do { | |
123 | if (!in_tok) | |
124 | return 1; | |
125 | if (same_trailer(in_tok, arg_tok)) | |
126 | return 0; | |
127 | /* | |
128 | * if we want to add a trailer after another one, | |
129 | * we have to check those before this one | |
130 | */ | |
131 | in_tok = after_or_end(where) ? in_tok->previous : in_tok->next; | |
132 | } while (check_all); | |
133 | return 1; | |
134 | } | |
135 | ||
136 | static void remove_from_list(struct trailer_item *item, | |
137 | struct trailer_item **first, | |
138 | struct trailer_item **last) | |
139 | { | |
140 | struct trailer_item *next = item->next; | |
141 | struct trailer_item *previous = item->previous; | |
142 | ||
143 | if (next) { | |
144 | item->next->previous = previous; | |
145 | item->next = NULL; | |
146 | } else if (last) | |
147 | *last = previous; | |
148 | ||
149 | if (previous) { | |
150 | item->previous->next = next; | |
151 | item->previous = NULL; | |
152 | } else if (first) | |
153 | *first = next; | |
154 | } | |
155 | ||
156 | static struct trailer_item *remove_first(struct trailer_item **first) | |
157 | { | |
158 | struct trailer_item *item = *first; | |
159 | *first = item->next; | |
160 | if (item->next) { | |
161 | item->next->previous = NULL; | |
162 | item->next = NULL; | |
163 | } | |
164 | return item; | |
165 | } | |
166 | ||
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) | |
172 | { | |
173 | switch (arg_tok->conf.if_exists) { | |
174 | case EXISTS_DO_NOTHING: | |
175 | free_trailer_item(arg_tok); | |
176 | break; | |
177 | case EXISTS_REPLACE: | |
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); | |
182 | break; | |
183 | case EXISTS_ADD: | |
184 | add_arg_to_input_list(on_tok, arg_tok, | |
185 | in_tok_first, in_tok_last); | |
186 | break; | |
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); | |
191 | else | |
192 | free_trailer_item(arg_tok); | |
193 | break; | |
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); | |
198 | else | |
199 | free_trailer_item(arg_tok); | |
200 | break; | |
201 | } | |
202 | } | |
203 | ||
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) | |
207 | { | |
208 | struct trailer_item **in_tok; | |
209 | enum action_where where; | |
210 | ||
211 | switch (arg_tok->conf.if_missing) { | |
212 | case MISSING_DO_NOTHING: | |
213 | free_trailer_item(arg_tok); | |
214 | break; | |
215 | case MISSING_ADD: | |
216 | where = arg_tok->conf.where; | |
217 | in_tok = after_or_end(where) ? in_tok_last : in_tok_first; | |
218 | if (*in_tok) { | |
219 | add_arg_to_input_list(*in_tok, arg_tok, | |
220 | in_tok_first, in_tok_last); | |
221 | } else { | |
222 | *in_tok_first = arg_tok; | |
223 | *in_tok_last = arg_tok; | |
224 | } | |
225 | break; | |
226 | } | |
227 | } | |
228 | ||
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) | |
232 | { | |
233 | struct trailer_item *in_tok; | |
234 | struct trailer_item *on_tok; | |
235 | struct trailer_item *following_tok; | |
236 | ||
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; | |
241 | ||
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)) | |
245 | continue; | |
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); | |
249 | return 1; | |
250 | } | |
251 | return 0; | |
252 | } | |
253 | ||
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) | |
257 | { | |
258 | struct trailer_item *arg_tok; | |
259 | struct trailer_item *next_arg; | |
260 | ||
261 | if (!*arg_tok_first) | |
262 | return; | |
263 | ||
264 | for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) { | |
265 | int applied = 0; | |
266 | ||
267 | next_arg = arg_tok->next; | |
268 | remove_from_list(arg_tok, arg_tok_first, NULL); | |
269 | ||
270 | applied = find_same_and_apply_arg(in_tok_first, | |
271 | in_tok_last, | |
272 | arg_tok); | |
273 | ||
274 | if (!applied) | |
275 | apply_arg_if_missing(in_tok_first, | |
276 | in_tok_last, | |
277 | arg_tok); | |
278 | } | |
279 | } |