treewide: rename 'struct exclude_list' to 'struct pattern_list'
[gitweb.git] / list-objects-filter-options.c
index 75d0236ee2da9feead511042085e3ae3f9f7c8a1..ba1425cb4a071f02e9daae57f3f7ce4194b12408 100644 (file)
@@ -6,6 +6,7 @@
 #include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
+#include "trace.h"
 #include "url.h"
 
 static int parse_combine_filter(
@@ -35,11 +36,8 @@ static int gently_parse_list_objects_filter(
 {
        const char *v0;
 
-       if (filter_options->choice) {
-               strbuf_addstr(
-                       errbuf, _("multiple filter-specs cannot be combined"));
-               return 1;
-       }
+       if (filter_options->choice)
+               BUG("filter_options already populated");
 
        if (!strcmp(arg, "blob:none")) {
                filter_options->choice = LOFC_BLOB_NONE;
@@ -122,14 +120,12 @@ static int parse_combine_subfilter(
        struct strbuf *subspec,
        struct strbuf *errbuf)
 {
-       size_t new_index = filter_options->sub_nr++;
+       size_t new_index = filter_options->sub_nr;
        char *decoded;
        int result;
 
-       ALLOC_GROW(filter_options->sub, filter_options->sub_nr,
-                  filter_options->sub_alloc);
-       memset(&filter_options->sub[new_index], 0,
-              sizeof(*filter_options->sub));
+       ALLOC_GROW_BY(filter_options->sub, filter_options->sub_nr, 1,
+                     filter_options->sub_alloc);
 
        decoded = url_percent_decode(subspec->buf);
 
@@ -181,14 +177,91 @@ static int parse_combine_filter(
        return result;
 }
 
-int parse_list_objects_filter(struct list_objects_filter_options *filter_options,
-                             const char *arg)
+static int allow_unencoded(char ch)
+{
+       if (ch <= ' ' || ch == '%' || ch == '+')
+               return 0;
+       return !strchr(RESERVED_NON_WS, ch);
+}
+
+static void filter_spec_append_urlencode(
+       struct list_objects_filter_options *filter, const char *raw)
 {
        struct strbuf buf = STRBUF_INIT;
-       filter_options->filter_spec = strdup(arg);
-       if (gently_parse_list_objects_filter(filter_options, arg, &buf))
-               die("%s", buf.buf);
-       return 0;
+       strbuf_addstr_urlencode(&buf, raw, allow_unencoded);
+       trace_printf("Add to combine filter-spec: %s\n", buf.buf);
+       string_list_append(&filter->filter_spec, strbuf_detach(&buf, NULL));
+}
+
+/*
+ * Changes filter_options into an equivalent LOFC_COMBINE filter options
+ * instance. Does not do anything if filter_options is already LOFC_COMBINE.
+ */
+static void transform_to_combine_type(
+       struct list_objects_filter_options *filter_options)
+{
+       assert(filter_options->choice);
+       if (filter_options->choice == LOFC_COMBINE)
+               return;
+       {
+               const int initial_sub_alloc = 2;
+               struct list_objects_filter_options *sub_array =
+                       xcalloc(initial_sub_alloc, sizeof(*sub_array));
+               sub_array[0] = *filter_options;
+               memset(filter_options, 0, sizeof(*filter_options));
+               filter_options->sub = sub_array;
+               filter_options->sub_alloc = initial_sub_alloc;
+       }
+       filter_options->sub_nr = 1;
+       filter_options->choice = LOFC_COMBINE;
+       string_list_append(&filter_options->filter_spec, xstrdup("combine:"));
+       filter_spec_append_urlencode(
+               filter_options,
+               list_objects_filter_spec(&filter_options->sub[0]));
+       /*
+        * We don't need the filter_spec strings for subfilter specs, only the
+        * top level.
+        */
+       string_list_clear(&filter_options->sub[0].filter_spec, /*free_util=*/0);
+}
+
+void list_objects_filter_die_if_populated(
+       struct list_objects_filter_options *filter_options)
+{
+       if (filter_options->choice)
+               die(_("multiple filter-specs cannot be combined"));
+}
+
+void parse_list_objects_filter(
+       struct list_objects_filter_options *filter_options,
+       const char *arg)
+{
+       struct strbuf errbuf = STRBUF_INIT;
+       int parse_error;
+
+       if (!filter_options->choice) {
+               string_list_append(&filter_options->filter_spec, xstrdup(arg));
+
+               parse_error = gently_parse_list_objects_filter(
+                       filter_options, arg, &errbuf);
+       } else {
+               /*
+                * Make filter_options an LOFC_COMBINE spec so we can trivially
+                * add subspecs to it.
+                */
+               transform_to_combine_type(filter_options);
+
+               string_list_append(&filter_options->filter_spec, xstrdup("+"));
+               filter_spec_append_urlencode(filter_options, arg);
+               ALLOC_GROW_BY(filter_options->sub, filter_options->sub_nr, 1,
+                             filter_options->sub_alloc);
+
+               parse_error = gently_parse_list_objects_filter(
+                       &filter_options->sub[filter_options->sub_nr - 1], arg,
+                       &errbuf);
+       }
+       if (parse_error)
+               die("%s", errbuf.buf);
 }
 
 int opt_parse_list_objects_filter(const struct option *opt,
@@ -196,27 +269,43 @@ int opt_parse_list_objects_filter(const struct option *opt,
 {
        struct list_objects_filter_options *filter_options = opt->value;
 
-       if (unset || !arg) {
+       if (unset || !arg)
                list_objects_filter_set_no_filter(filter_options);
-               return 0;
+       else
+               parse_list_objects_filter(filter_options, arg);
+       return 0;
+}
+
+const char *list_objects_filter_spec(struct list_objects_filter_options *filter)
+{
+       if (!filter->filter_spec.nr)
+               BUG("no filter_spec available for this filter");
+       if (filter->filter_spec.nr != 1) {
+               struct strbuf concatted = STRBUF_INIT;
+               strbuf_add_separated_string_list(
+                       &concatted, "", &filter->filter_spec);
+               string_list_clear(&filter->filter_spec, /*free_util=*/0);
+               string_list_append(
+                       &filter->filter_spec, strbuf_detach(&concatted, NULL));
        }
 
-       return parse_list_objects_filter(filter_options, arg);
+       return filter->filter_spec.items[0].string;
 }
 
-void expand_list_objects_filter_spec(
-       const struct list_objects_filter_options *filter,
-       struct strbuf *expanded_spec)
+const char *expand_list_objects_filter_spec(
+       struct list_objects_filter_options *filter)
 {
-       strbuf_init(expanded_spec, strlen(filter->filter_spec));
-       if (filter->choice == LOFC_BLOB_LIMIT)
-               strbuf_addf(expanded_spec, "blob:limit=%lu",
+       if (filter->choice == LOFC_BLOB_LIMIT) {
+               struct strbuf expanded_spec = STRBUF_INIT;
+               strbuf_addf(&expanded_spec, "blob:limit=%lu",
                            filter->blob_limit_value);
-       else if (filter->choice == LOFC_TREE_DEPTH)
-               strbuf_addf(expanded_spec, "tree:%lu",
-                           filter->tree_exclude_depth);
-       else
-               strbuf_addstr(expanded_spec, filter->filter_spec);
+               string_list_clear(&filter->filter_spec, /*free_util=*/0);
+               string_list_append(
+                       &filter->filter_spec,
+                       strbuf_detach(&expanded_spec, NULL));
+       }
+
+       return list_objects_filter_spec(filter);
 }
 
 void list_objects_filter_release(
@@ -226,7 +315,7 @@ void list_objects_filter_release(
 
        if (!filter_options)
                return;
-       free(filter_options->filter_spec);
+       string_list_clear(&filter_options->filter_spec, /*free_util=*/0);
        free(filter_options->sparse_oid_value);
        for (sub = 0; sub < filter_options->sub_nr; sub++)
                list_objects_filter_release(&filter_options->sub[sub]);
@@ -236,7 +325,7 @@ void list_objects_filter_release(
 
 void partial_clone_register(
        const char *remote,
-       const struct list_objects_filter_options *filter_options)
+       struct list_objects_filter_options *filter_options)
 {
        /*
         * Record the name of the partial clone remote in the
@@ -259,7 +348,7 @@ void partial_clone_register(
         * the default for subsequent fetches from this remote.
         */
        core_partial_clone_filter_default =
-               xstrdup(filter_options->filter_spec);
+               xstrdup(expand_list_objects_filter_spec(filter_options));
        git_config_set("core.partialclonefilter",
                       core_partial_clone_filter_default);
 }
@@ -275,7 +364,8 @@ void partial_clone_get_default_filter_spec(
        if (!core_partial_clone_filter_default)
                return;
 
-       filter_options->filter_spec = strdup(core_partial_clone_filter_default);
+       string_list_append(&filter_options->filter_spec,
+                          core_partial_clone_filter_default);
        gently_parse_list_objects_filter(filter_options,
                                         core_partial_clone_filter_default,
                                         &errbuf);