summaryrefslogtreecommitdiffstats
path: root/src/format.c
diff options
context:
space:
mode:
authorNalin Dahyabhai <nalin@redhat.com>2013-05-23 19:40:27 -0400
committerNalin Dahyabhai <nalin@redhat.com>2013-05-23 19:46:59 -0400
commitfd975c77fda8dc2485eede3c15aee3fc3d236a9f (patch)
tree50393edced9b28c9cdfd1f11f74791993a95c4f4 /src/format.c
parentdd71353dad22dcb07d1c9e90c7c634aa78ec0f77 (diff)
downloadslapi-nis-fd975c77fda8dc2485eede3c15aee3fc3d236a9f.tar.gz
slapi-nis-fd975c77fda8dc2485eede3c15aee3fc3d236a9f.tar.xz
slapi-nis-fd975c77fda8dc2485eede3c15aee3fc3d236a9f.zip
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.
Diffstat (limited to 'src/format.c')
-rw-r--r--src/format.c287
1 files changed, 286 insertions, 1 deletions
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) &&