Edit

IABSD.fr/xenocara/lib/xcb-util-xrm/src/match.c

Branch :

  • Show log

    Commit

  • Author : jasper
    Date : 2016-11-07 19:20:35
    Hash : 44c5a3cd
    Message : add xcb-util-xrm-1.0 ok matthieu@

  • lib/xcb-util-xrm/src/match.c
  • /*
     * vim:ts=4:sw=4:expandtab
     *
     * Copyright © 2016 Ingo Bürk
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     * Except as contained in this notice, the names of the authors or their
     * institutions shall not be used in advertising or otherwise to promote the
     * sale, use or other dealings in this Software without prior written
     * authorization from the authors.
     *
     */
    #include "externals.h"
    
    #include "match.h"
    #include "util.h"
    
    /* Forward declarations */
    static int __match_matches(int num_components, xcb_xrm_component_t *cur_comp_db,
            xcb_xrm_component_t *cur_comp_name, xcb_xrm_component_t *cur_comp_class,
            bool has_class, int position, xcb_xrm_match_ignore_t ignore, xcb_xrm_match_t **match);
    static xcb_xrm_match_flags_t __match_matches_component(xcb_xrm_component_t *comp_db,
            xcb_xrm_component_t *comp_name, xcb_xrm_component_t *comp_class);
    static int __match_compare(int length, xcb_xrm_match_t *best, xcb_xrm_match_t *candidate);
    static xcb_xrm_match_t *__match_new(int length);
    static void __match_copy(xcb_xrm_match_t *src, xcb_xrm_match_t *dest, int length);
    static void __match_free(xcb_xrm_match_t *match);
    
    /*
     * Finds the matching entry in the database given a full name / class query string.
     *
     */
    int __xcb_xrm_match(xcb_xrm_database_t *database, xcb_xrm_entry_t *query_name, xcb_xrm_entry_t *query_class,
            xcb_xrm_resource_t *resource) {
        xcb_xrm_match_t *best_match = NULL;
        xcb_xrm_entry_t *cur_entry = TAILQ_FIRST(database);
    
        int num = __xcb_xrm_entry_num_components(query_name);
    
        while (cur_entry != NULL) {
            xcb_xrm_match_t *cur_match = NULL;
    
            /* First we check whether the current database entry even matches. */
            bool has_class = query_class != NULL;
            xcb_xrm_component_t *first_comp_name = TAILQ_FIRST(&(query_name->components));
            xcb_xrm_component_t *first_comp_class = has_class ? TAILQ_FIRST(&(query_class->components)) : NULL;
            xcb_xrm_component_t *first_comp_db = TAILQ_FIRST(&(cur_entry->components));
            if (__match_matches(num, first_comp_db, first_comp_name, first_comp_class, has_class,
                        0, MI_UNDECIDED, &cur_match) == 0) {
                cur_match->entry = cur_entry;
    
                /* The first matching entry is the first one we pick as the best matching entry. */
                if (best_match == NULL) {
                    best_match = cur_match;
                } else {
                    /* Otherwise, check whether this match is better than the current best. */
                    if (__match_compare(num, best_match, cur_match) == 0) {
                        __match_free(best_match);
                        best_match = cur_match;
                    } else {
                        __match_free(cur_match);
                    }
                }
            } else {
                __match_free(cur_match);
            }
    
            /* Advance to the next database entry. */
            cur_entry = TAILQ_NEXT(cur_entry, entries);
        }
    
        if (best_match != NULL) {
            resource->value = strdup(best_match->entry->value);
            if (resource->value == NULL) {
                __match_free(best_match);
                return -FAILURE;
            }
    
            __match_free(best_match);
            return SUCCESS;
        }
    
        return -FAILURE;
    }
    
    static int __match_matches(int num_components, xcb_xrm_component_t *cur_comp_db,
            xcb_xrm_component_t *cur_comp_name, xcb_xrm_component_t *cur_comp_class,
            bool has_class, int position, xcb_xrm_match_ignore_t ignore, xcb_xrm_match_t **match) {
        xcb_xrm_match_flags_t comp_match;
    
        if (*match == NULL) {
            *match = __match_new(num_components);
            if (*match == NULL)
                return -FAILURE;
        }
    
        /* Check if we reached the end of the recursion. */
        if (cur_comp_name == NULL || (has_class && cur_comp_class == NULL) || cur_comp_db == NULL) {
            if (cur_comp_db == NULL && cur_comp_name == NULL && (!has_class || cur_comp_class == NULL)) {
                return SUCCESS;
            }
    
            return -FAILURE;
        }
    
        comp_match = __match_matches_component(cur_comp_db, cur_comp_name, cur_comp_class);
    
        /* If we have a matching component in a loose binding, we need to continue
         * matching both normally and ignoring this match. */
        if (ignore == MI_UNDECIDED && cur_comp_db->binding_type == BT_LOOSE &&
                (comp_match & MF_NAME || comp_match & MF_CLASS || comp_match & MF_WILDCARD)) {
    
            /* Store references / copies to the current parameters for the second call. */
            xcb_xrm_component_t *copy_comp_db = cur_comp_db;
            xcb_xrm_component_t *copy_comp_name = cur_comp_name;
            xcb_xrm_component_t *copy_comp_class = cur_comp_class;
            xcb_xrm_match_t *copy_match = __match_new(num_components);
            __match_copy(*match, copy_match, num_components);
    
            /* First, we try to match normally. */
            if (__match_matches(num_components, cur_comp_db, cur_comp_name, cur_comp_class, has_class,
                        position, MI_DO_NOT_IGNORE, match) == 0) {
                __match_free(copy_match);
                return SUCCESS;
            }
    
            /* We had no success the first time around, so let's try to reset and
             * go again, but this time ignoring this match. */
            __match_free(*match);
            *match = copy_match;
            if (__match_matches(num_components, copy_comp_db, copy_comp_name, copy_comp_class, has_class,
                        position, MI_IGNORE, match) == 0) {
                return SUCCESS;
            }
    
            /* Give up. */
            return -FAILURE;
        }
    
        /* Store the match flags on the match so we can use them later for precedence evaluation. */
        (*match)->flags[position] = comp_match;
        if (comp_match == MF_NONE)
            return -FAILURE;
    
    #define ADVANCE(entry) do {                      \
        if (entry != NULL)                           \
            entry = TAILQ_NEXT((entry), components); \
    } while (0)
    
        /* We have matched this component, so advance to the next one. */
        ADVANCE(cur_comp_name);
        ADVANCE(cur_comp_class);
        /* We advance the pointer to the database entry component if both of the following are true:
         *  1. This match isn't ignored due to a loose binding path.
         *  2. It was an actual match and not skipped due to a loose binding. */
        if (ignore != MI_IGNORE &&
                (comp_match & MF_NAME || comp_match & MF_CLASS || comp_match & MF_WILDCARD)) {
            ADVANCE(cur_comp_db);
        }
    
    #undef ADVANCE
    
        /* Recursively descend to the next component. */
        return __match_matches(num_components, cur_comp_db, cur_comp_name, cur_comp_class, has_class,
                position + 1, MI_UNDECIDED, match);
    }
    
    static xcb_xrm_match_flags_t __match_matches_component(xcb_xrm_component_t *comp_db,
            xcb_xrm_component_t *comp_name, xcb_xrm_component_t *comp_class) {
        xcb_xrm_match_flags_t result = MF_NONE;
        if (comp_db->binding_type == BT_LOOSE)
            result = MF_PRECEDING_LOOSE;
    
        if (comp_db->type == CT_NORMAL) {
            if (strcmp(comp_db->name, comp_name->name) == 0) {
                result |= MF_NAME;
            } else if (comp_class != NULL && strcmp(comp_db->name, comp_class->name) == 0) {
                result |= MF_CLASS;
            } else {
                if (comp_db->binding_type == BT_TIGHT)
                    return MF_NONE;
    
                /* We remove this flag again because we need to apply
                 * it to the last component in the matching chain for
                 * the loose binding. */
                result &= ~MF_PRECEDING_LOOSE;
                result |= MF_SKIPPED;
            }
        } else {
            result |= MF_WILDCARD;
        }
    
        return result;
    }
    
    static int __match_compare(int length, xcb_xrm_match_t *best, xcb_xrm_match_t *candidate) {
        for (int i = 0; i < length; i++) {
            xcb_xrm_match_flags_t mt_best = best->flags[i];
            xcb_xrm_match_flags_t mt_candidate = candidate->flags[i];
    
            /* Precedence rule #1: Matching components, including '?', outweigh '*'. */
            if (mt_best & MF_SKIPPED &&
                    (mt_candidate & MF_NAME || mt_candidate & MF_CLASS || mt_candidate & MF_WILDCARD))
                return SUCCESS;
    
            /* Precedence rule #2: Matching name outweighs both matching class and '?'.
             *                     Matching class outweighs '?'. */
            if ((mt_best & MF_CLASS || mt_best & MF_WILDCARD) && mt_candidate & MF_NAME)
                return SUCCESS;
    
            if (mt_best & MF_WILDCARD && mt_candidate & MF_CLASS)
                return SUCCESS;
    
            /* Precedence rule #3: A preceding exact match outweighs a preceding '*'. */
            if (mt_best & MF_PRECEDING_LOOSE && !(mt_candidate & MF_PRECEDING_LOOSE))
                return SUCCESS;
        }
    
        return -FAILURE;
    }
    
    static xcb_xrm_match_t *__match_new(int length) {
        xcb_xrm_match_t *match = calloc(1, sizeof(struct xcb_xrm_match_t));
        if (match == NULL)
            return NULL;
    
        match->entry = NULL;
        match->flags = calloc(1, length * sizeof(xcb_xrm_match_flags_t));
        if (match->flags == NULL) {
            FREE(match);
            return NULL;
        }
    
        return match;
    }
    
    static void __match_copy(xcb_xrm_match_t *src, xcb_xrm_match_t *dest, int length) {
        memcpy(dest->flags, src->flags, length * sizeof(xcb_xrm_match_flags_t));
    }
    
    static void __match_free(xcb_xrm_match_t *match) {
        FREE(match->flags);
        FREE(match);
    }