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
,
281 static int set_where(struct conf_info
*item
, const char *value
)
283 if (!strcasecmp("after", value
))
284 item
->where
= WHERE_AFTER
;
285 else if (!strcasecmp("before", value
))
286 item
->where
= WHERE_BEFORE
;
287 else if (!strcasecmp("end", value
))
288 item
->where
= WHERE_END
;
289 else if (!strcasecmp("start", value
))
290 item
->where
= WHERE_START
;
296 static int set_if_exists(struct conf_info
*item
, const char *value
)
298 if (!strcasecmp("addIfDifferent", value
))
299 item
->if_exists
= EXISTS_ADD_IF_DIFFERENT
;
300 else if (!strcasecmp("addIfDifferentNeighbor", value
))
301 item
->if_exists
= EXISTS_ADD_IF_DIFFERENT_NEIGHBOR
;
302 else if (!strcasecmp("add", value
))
303 item
->if_exists
= EXISTS_ADD
;
304 else if (!strcasecmp("replace", value
))
305 item
->if_exists
= EXISTS_REPLACE
;
306 else if (!strcasecmp("doNothing", value
))
307 item
->if_exists
= EXISTS_DO_NOTHING
;
313 static int set_if_missing(struct conf_info
*item
, const char *value
)
315 if (!strcasecmp("doNothing", value
))
316 item
->if_missing
= MISSING_DO_NOTHING
;
317 else if (!strcasecmp("add", value
))
318 item
->if_missing
= MISSING_ADD
;
324 static void duplicate_conf(struct conf_info
*dst
, struct conf_info
*src
)
328 dst
->name
= xstrdup(src
->name
);
330 dst
->key
= xstrdup(src
->key
);
332 dst
->command
= xstrdup(src
->command
);
335 static struct trailer_item
*get_conf_item(const char *name
)
337 struct trailer_item
*item
;
338 struct trailer_item
*previous
;
340 /* Look up item with same name */
341 for (previous
= NULL
, item
= first_conf_item
;
343 previous
= item
, item
= item
->next
) {
344 if (!strcasecmp(item
->conf
.name
, name
))
348 /* Item does not already exists, create it */
349 item
= xcalloc(sizeof(struct trailer_item
), 1);
350 duplicate_conf(&item
->conf
, &default_conf_info
);
351 item
->conf
.name
= xstrdup(name
);
354 first_conf_item
= item
;
356 previous
->next
= item
;
357 item
->previous
= previous
;
363 enum trailer_info_type
{ TRAILER_KEY
, TRAILER_COMMAND
, TRAILER_WHERE
,
364 TRAILER_IF_EXISTS
, TRAILER_IF_MISSING
};
368 enum trailer_info_type type
;
369 } trailer_config_items
[] = {
370 { "key", TRAILER_KEY
},
371 { "command", TRAILER_COMMAND
},
372 { "where", TRAILER_WHERE
},
373 { "ifexists", TRAILER_IF_EXISTS
},
374 { "ifmissing", TRAILER_IF_MISSING
}
377 static int git_trailer_default_config(const char *conf_key
, const char *value
, void *cb
)
379 const char *trailer_item
, *variable_name
;
381 if (!skip_prefix(conf_key
, "trailer.", &trailer_item
))
384 variable_name
= strrchr(trailer_item
, '.');
385 if (!variable_name
) {
386 if (!strcmp(trailer_item
, "where")) {
387 if (set_where(&default_conf_info
, value
) < 0)
388 warning(_("unknown value '%s' for key '%s'"),
390 } else if (!strcmp(trailer_item
, "ifexists")) {
391 if (set_if_exists(&default_conf_info
, value
) < 0)
392 warning(_("unknown value '%s' for key '%s'"),
394 } else if (!strcmp(trailer_item
, "ifmissing")) {
395 if (set_if_missing(&default_conf_info
, value
) < 0)
396 warning(_("unknown value '%s' for key '%s'"),
398 } else if (!strcmp(trailer_item
, "separators")) {
399 separators
= xstrdup(value
);
405 static int git_trailer_config(const char *conf_key
, const char *value
, void *cb
)
407 const char *trailer_item
, *variable_name
;
408 struct trailer_item
*item
;
409 struct conf_info
*conf
;
411 enum trailer_info_type type
;
414 if (!skip_prefix(conf_key
, "trailer.", &trailer_item
))
417 variable_name
= strrchr(trailer_item
, '.');
422 for (i
= 0; i
< ARRAY_SIZE(trailer_config_items
); i
++) {
423 if (strcmp(trailer_config_items
[i
].name
, variable_name
))
425 name
= xstrndup(trailer_item
, variable_name
- trailer_item
- 1);
426 type
= trailer_config_items
[i
].type
;
433 item
= get_conf_item(name
);
440 warning(_("more than one %s"), conf_key
);
441 conf
->key
= xstrdup(value
);
443 case TRAILER_COMMAND
:
445 warning(_("more than one %s"), conf_key
);
446 conf
->command
= xstrdup(value
);
449 if (set_where(conf
, value
))
450 warning(_("unknown value '%s' for key '%s'"), value
, conf_key
);
452 case TRAILER_IF_EXISTS
:
453 if (set_if_exists(conf
, value
))
454 warning(_("unknown value '%s' for key '%s'"), value
, conf_key
);
456 case TRAILER_IF_MISSING
:
457 if (set_if_missing(conf
, value
))
458 warning(_("unknown value '%s' for key '%s'"), value
, conf_key
);
461 die("internal bug in trailer.c");