From fd975c77fda8dc2485eede3c15aee3fc3d236a9f Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Thu, 23 May 2013 19:40:27 -0400 Subject: Add %sort() and %dribble_merge() Add %sort(), which binary-sorts a single list of values, and %dribble_merge(), which takes a quoted length, a separator, and some expressions and produces a list of lists of values using the separator, where no list is larger than the length. --- src/format.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 286 insertions(+), 1 deletion(-) (limited to 'src/format.c') diff --git a/src/format.c b/src/format.c index aaa7f57..9ee2444 100644 --- a/src/format.c +++ b/src/format.c @@ -1,5 +1,5 @@ /* - * Copyright 2008,2010,2011,2012 Red Hat, Inc. + * Copyright 2008,2010,2011,2012,2013 Red Hat, Inc. * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -2752,6 +2752,135 @@ format_default(struct plugin_state *state, Slapi_PBlock *pb, Slapi_Entry *e, return ret; } +/* Evaluate the one list, and sort it. */ +static int +format_compare_bv(const void *p1, const void *p2) +{ + const struct berval * const *v1 = p1, * const *v2 = p2; + const struct berval *bv1 = *v1, *bv2 = *v2; + int initial, c; + + if (bv1->bv_len == bv2->bv_len) { + return memcmp(bv1->bv_val, bv2->bv_val, bv1->bv_len); + } else { + initial = MIN(bv1->bv_len, bv2->bv_len); + c = memcmp(bv1->bv_val, bv2->bv_val, initial); + if (c != 0) { + return c; + } + if (bv1->bv_val < bv2->bv_val) { + return -1; + } + if (bv1->bv_val > bv2->bv_val) { + return 1; + } + } + return 0; +} + +static int +format_sort(struct plugin_state *state, Slapi_PBlock *pb, Slapi_Entry *e, + const char *group, const char *set, + const char *args, const char *disallowed, + char *outbuf, int outbuf_len, + struct format_choice **outbuf_choices, + char ***rel_attrs, char ***ref_attrs, + struct format_inref_attr ***inref_attrs, + struct format_ref_attr_list ***ref_attr_list, + struct format_ref_attr_list ***inref_attr_list) +{ + int ret, argc, i; + unsigned int *lengths; + char **argv, **values; + struct berval bv, **choices; + + ret = format_parse_args(state, args, &argc, &argv); + if (ret != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "sort: error parsing arguments\n"); + return -EINVAL; + } + if (argc < 1) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "sort: one argument is required\n"); + format_free_parsed_args(argv); + return -EINVAL; + } + if (argc > 1) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "sort: only one argument is allowed\n"); + format_free_parsed_args(argv); + return -EINVAL; + } + if (outbuf_choices == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "sort: returns a list, but a list " + "would not be appropriate\n"); + format_free_parsed_args(argv); + return -EINVAL; + } + + /* Evaluate this argument. */ + choices = NULL; + values = format_get_data_set(state, pb, e, group, set, + argv[0], disallowed, + rel_attrs, ref_attrs, inref_attrs, + ref_attr_list, inref_attr_list, + &lengths); + if (values != NULL) { + /* Walk the list of values. */ + for (i = 0; values[i] != NULL; i++) { + /* Add it to the list. */ + bv.bv_val = values[i]; + bv.bv_len = lengths[i]; + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "sort: input %d = \"%.*s\"\n", + i + 1, (int) bv.bv_len, bv.bv_val); + format_add_bv_list(&choices, &bv); + } + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "sort: expanded \"%s\" to produce " + "%d values for \"%s\"\n", argv[0], i, + slapi_entry_get_dn(e)); + format_free_data_set(values, lengths); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "sort: expanding \"%s\" produced " + "no values for \"%s\"\n", argv[0], + slapi_entry_get_dn(e)); + i = 0; + } + + if (choices != NULL) { + qsort(choices, i, sizeof(choices[0]), format_compare_bv); + for (i = 0; choices[i] != NULL; i++) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "sort: returning \"%.*s\" as a " + "value for \"%s\"\n", + (int) choices[i]->bv_len, + choices[i]->bv_val, + slapi_entry_get_dn(e)); + continue; + } + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "sort: returning %d values for \"%s\"\n", i, + slapi_entry_get_dn(e)); + format_add_choice(outbuf_choices, outbuf, &choices); + ret = 0; + } else { + ret = -ENOENT; + } + + format_free_parsed_args(argv); + + return ret; +} + /* Evaluate all of the arguments, and concatentate all of the lists of results * to produce one long list. */ static int @@ -3243,6 +3372,160 @@ format_internal_sequence_number(struct plugin_state *state, return ret; } +/* Condense the list as much as possible, but possibly still produce multiple + * values. */ +static int +format_dribble_merge(struct plugin_state *state, Slapi_PBlock *pb, + Slapi_Entry *e, + const char *group, const char *set, + const char *args, const char *disallowed, + char *outbuf, int outbuf_len, + struct format_choice **outbuf_choices, + char ***rel_attrs, + char ***ref_attrs, + struct format_inref_attr ***inref_attrs, + struct format_ref_attr_list ***ref_attr_list, + struct format_ref_attr_list ***inref_attr_list) +{ + int ret, i, j, argc, slen, count, buf_used = 0; + unsigned int *lengths, max; + char **argv, **values; + const char *sep; + char *buf = NULL; + struct berval **choices, bv; + + ret = format_parse_args(state, args, &argc, &argv); + if (ret != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "dribble_merge: error parsing arguments\n"); + return -EINVAL; + } + if (argc < 2) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "dribble_merge: requires at least " + "two arguments\n"); + format_free_parsed_args(argv); + return -EINVAL; + } + max = atoi(argv[0]); + if (max < 1) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "dribble_merge: small maximum group size\n"); + format_free_parsed_args(argv); + return -EINVAL; + } + sep = argv[1]; + slen = strlen(sep); + choices = NULL; + if (outbuf_choices == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "dribble_merge: returns a list, but a list " + "would not be appropriate\n"); + format_free_parsed_args(argv); + return -EINVAL; + } + for (i = 2, ret = 0, count = 0; i < argc; i++) { + /* Expand this argument. */ + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "dribble_merge: expanding ->%s<-\n", argv[i]); + values = format_get_data_set(state, pb, e, group, set, + argv[i], disallowed, + rel_attrs, ref_attrs, inref_attrs, + ref_attr_list, inref_attr_list, + &lengths); + if (values == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "dribble_merge: no values for " + "->%s<-\n", argv[i]); + continue; + } + for (j = 0; values[j] != NULL; j++) { + /* Check if this is the first subset, or if there's + * space for this value in a subset that we're + * currently working on filling. */ + if ((buf == NULL) || + (buf_used + lengths[j] + slen > max)) { + if (buf_used > 0) { + /* Add the old subset. */ + bv.bv_val = buf; + bv.bv_len = buf_used; + format_add_bv_list(&choices, &bv); + } + /* Allocate space for the next subset. */ + buf = malloc(max); + buf_used = 0; + count = 0; + if (buf == NULL) { + format_free_bv_list(choices); + format_free_data_set(values, lengths); + format_free_parsed_args(argv); + return -ENOMEM; + } + } + /* If the value's just too big, return an error. */ + if (lengths[j] > max) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "dribble_merge: value \"%.*s\"" + " was too big for ->%s<\n", + lengths[j], values[j], + argv[i]); + format_free_bv_list(choices); + format_free_data_set(values, lengths); + format_free_parsed_args(argv); + free(buf); + return -ENOBUFS; + } else { + /* Log this value. */ + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "dribble_merge: got %d-byte " + "value for ->%s<\n", + lengths[j], argv[i]); + } + /* If this isn't the first result, fill in the + * separator. Then fill in the value. */ + if (count > 0) { + memcpy(buf + buf_used, sep, slen); + buf_used += slen; + } + memcpy(buf + buf_used, values[j], lengths[j]); + buf_used += lengths[j]; + count++; + } + format_free_data_set(values, lengths); + } + /* Grab any leftovers. */ + if (buf_used > 0) { + bv.bv_val = buf; + bv.bv_len = buf_used; + format_add_bv_list(&choices, &bv); + } + format_free_parsed_args(argv); + if (choices != NULL) { + for (i = 0; choices[i] != NULL; i++) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "dribble_merge: returning \"%.*s\" " + "as a value for \"%s\"\n", + (int) choices[i]->bv_len, + choices[i]->bv_val, + slapi_entry_get_dn(e)); + continue; + } + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "dribble_merge: returning %d values for " + "\"%s\"\n", i, slapi_entry_get_dn(e)); + format_add_choice(outbuf_choices, outbuf, &choices); + ret = 0; + } else { + ret = -ENOENT; + } + return ret; +} + /* Choose a formatting function by name. */ static void * format_lookup_fn(const char *fnname) @@ -3287,6 +3570,8 @@ format_lookup_fn(const char *fnname) {"link", format_link}, {"unique", format_unique}, {"internal_sequence_number", format_internal_sequence_number}, + {"dribble_merge", format_dribble_merge}, + {"sort", format_sort}, }; for (i = 0; i < sizeof(fns) / sizeof(fns[0]); i++) { if ((fns[i].name != NULL) && -- cgit