be0ad6558db75b11cb7a388b40ad7f0be5ed1d90
   1#include "cache.h"
   2/*
   3 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
   4 */
   5
   6enum action_where { WHERE_END, WHERE_AFTER, WHERE_BEFORE, WHERE_START };
   7enum action_if_exists { EXISTS_ADD_IF_DIFFERENT_NEIGHBOR, EXISTS_ADD_IF_DIFFERENT,
   8                        EXISTS_ADD, EXISTS_REPLACE, EXISTS_DO_NOTHING };
   9enum action_if_missing { MISSING_ADD, MISSING_DO_NOTHING };
  10
  11struct 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
  20static struct conf_info default_conf_info;
  21
  22struct 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
  30static struct trailer_item *first_conf_item;
  31
  32static char *separators = ":";
  33
  34static 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 */
  45static 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
  52static 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
  61static int same_value(struct trailer_item *a, struct trailer_item *b)
  62{
  63        return !strcasecmp(a->value, b->value);
  64}
  65
  66static int same_trailer(struct trailer_item *a, struct trailer_item *b)
  67{
  68        return same_token(a, b) && same_value(a, b);
  69}
  70
  71static 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
  81static void update_last(struct trailer_item **last)
  82{
  83        if (*last)
  84                while ((*last)->next != NULL)
  85                        *last = (*last)->next;
  86}
  87
  88static void update_first(struct trailer_item **first)
  89{
  90        if (*first)
  91                while ((*first)->previous != NULL)
  92                        *first = (*first)->previous;
  93}
  94
  95static 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
 117static 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
 136static 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
 156static 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
 167static 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
 204static 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
 229static 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
 254static 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}