contrib/git-credential-gnome-keyring.c: small stylistic cleanups
[gitweb.git] / urlmatch.c
index 4f38cc7bc6a8a66815816e526942ac791cfbb22a..ec87cba75099ebe05e936430ca3b688275e01a4a 100644 (file)
@@ -281,9 +281,11 @@ char *url_normalize(const char *url, struct url_info *out_info)
                url_len--;
        }
        for (;;) {
-               const char *seg_start = norm.buf + norm.len;
+               const char *seg_start;
+               size_t seg_start_off = norm.len;
                const char *next_slash = url + strcspn(url, "/?#");
                int skip_add_slash = 0;
+
                /*
                 * RFC 3689 indicates that any . or .. segments should be
                 * unescaped before being checked for.
@@ -297,6 +299,8 @@ char *url_normalize(const char *url, struct url_info *out_info)
                        strbuf_release(&norm);
                        return NULL;
                }
+
+               seg_start = norm.buf + seg_start_off;
                if (!strcmp(seg_start, ".")) {
                        /* ignore a . segment; be careful not to remove initial '/' */
                        if (seg_start == path_start + 1) {
@@ -466,3 +470,70 @@ int match_urls(const struct url_info *url,
                *exactusermatch = usermatched;
        return pathmatchlen;
 }
+
+int urlmatch_config_entry(const char *var, const char *value, void *cb)
+{
+       struct string_list_item *item;
+       struct urlmatch_config *collect = cb;
+       struct urlmatch_item *matched;
+       struct url_info *url = &collect->url;
+       const char *key, *dot;
+       struct strbuf synthkey = STRBUF_INIT;
+       size_t matched_len = 0;
+       int user_matched = 0;
+       int retval;
+
+       key = skip_prefix(var, collect->section);
+       if (!key || *(key++) != '.') {
+               if (collect->cascade_fn)
+                       return collect->cascade_fn(var, value, cb);
+               return 0; /* not interested */
+       }
+       dot = strrchr(key, '.');
+       if (dot) {
+               char *config_url, *norm_url;
+               struct url_info norm_info;
+
+               config_url = xmemdupz(key, dot - key);
+               norm_url = url_normalize(config_url, &norm_info);
+               free(config_url);
+               if (!norm_url)
+                       return 0;
+               matched_len = match_urls(url, &norm_info, &user_matched);
+               free(norm_url);
+               if (!matched_len)
+                       return 0;
+               key = dot + 1;
+       }
+
+       if (collect->key && strcmp(key, collect->key))
+               return 0;
+
+       item = string_list_insert(&collect->vars, key);
+       if (!item->util) {
+               matched = xcalloc(1, sizeof(*matched));
+               item->util = matched;
+       } else {
+               matched = item->util;
+               /*
+                * Is our match shorter?  Is our match the same
+                * length, and without user while the current
+                * candidate is with user?  Then we cannot use it.
+                */
+               if (matched_len < matched->matched_len ||
+                   ((matched_len == matched->matched_len) &&
+                    (!user_matched && matched->user_matched)))
+                       return 0;
+               /* Otherwise, replace it with this one. */
+       }
+
+       matched->matched_len = matched_len;
+       matched->user_matched = user_matched;
+       strbuf_addstr(&synthkey, collect->section);
+       strbuf_addch(&synthkey, '.');
+       strbuf_addstr(&synthkey, key);
+       retval = collect->collect_fn(synthkey.buf, value, collect->cb);
+
+       strbuf_release(&synthkey);
+       return retval;
+}