--- /dev/null
+/*
+ * Copyright (c) 2005 Zmanda, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Contact information: Zmanda Inc., 505 N Mathlida Ave, Suite 120
+ * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
+ */
+
+/*
+ * Utilities that aren't quite included in glib
+ *
+ * Author: Dustin J. Mitchell <dustin@zmanda.com>, Ian Turner <ian@zmanda.com>
+ */
+
+#include "amanda.h"
+#include "glib-util.h"
+#include "conffile.h" /* For find_multiplier. */
+
+typedef enum {
+ FLAG_STRING_NAME,
+ FLAG_STRING_SHORT_NAME,
+ FLAG_STRING_NICK
+} FlagString;
+
+static char ** g_flags_to_strv(int value, GType type, FlagString source);
+
+void
+_glib_util_foreach_glue(gpointer data, gpointer func)
+{
+ void (*one_arg_fn)(gpointer) = (void (*)(gpointer))func;
+ one_arg_fn(data);
+}
+
+GValue* g_value_unset_init(GValue* value, GType type) {
+ g_return_val_if_fail(value != NULL, NULL);
+
+ if (G_IS_VALUE(value)) {
+ g_value_unset(value);
+ }
+ g_value_init(value, type);
+ return value;
+}
+
+GValue* g_value_unset_copy(const GValue * from, GValue * to) {
+ g_return_val_if_fail(from != NULL, NULL);
+ g_return_val_if_fail(to != NULL, NULL);
+
+ g_value_unset_init(to, G_VALUE_TYPE(from));
+ g_value_copy(from, to);
+ return to;
+}
+
+void g_list_free_full(GList * list) {
+ GList * cur = list;
+
+ while (cur != NULL) {
+ gpointer data = cur->data;
+ amfree(data);
+ cur = g_list_next(cur);
+ }
+
+ g_list_free(list);
+}
+
+void g_slist_free_full(GSList * list) {
+ GSList * cur = list;
+
+ while (cur != NULL) {
+ gpointer data = cur->data;
+ amfree(data);
+ cur = g_slist_next(cur);
+ }
+
+ g_slist_free(list);
+}
+
+void g_queue_free_full(GQueue * queue) {
+ while (!g_queue_is_empty(queue)) {
+ gpointer data;
+ data = g_queue_pop_head(queue);
+ amfree(data);
+ }
+ g_queue_free(queue);
+}
+
+void g_ptr_array_free_full(GPtrArray * array) {
+ size_t i;
+
+ for (i = 0; i < array->len; i ++) {
+ amfree(g_ptr_array_index(array, i));
+ }
+ g_ptr_array_free(array, TRUE);
+}
+
+gboolean g_value_compare(GValue * a, GValue * b) {
+ if (a == NULL && b == NULL)
+ return TRUE;
+ if (a == NULL || b == NULL)
+ return FALSE;
+ if (G_VALUE_TYPE(a) != G_VALUE_TYPE(b))
+ return FALSE;
+ if (g_value_fits_pointer(a) && g_value_fits_pointer(b)) {
+ return g_value_peek_pointer(a) == g_value_peek_pointer(b);
+ } else {
+ /* Since there is no builtin comparison function, we resort to
+ comparing serialized strings. Yuck. */
+ char * a_str;
+ char * b_str;
+ gboolean rval;
+ a_str = g_strdup_value_contents(a);
+ b_str = g_strdup_value_contents(b);
+ rval = (0 == strcmp(a_str, b_str));
+ amfree(a_str);
+ amfree(b_str);
+ return rval;
+ }
+
+ g_assert_not_reached();
+}
+
+static gboolean g_value_set_boolean_from_string(GValue * val, char * string) {
+ if (strcasecmp(string, "true") == 0 ||
+ strcasecmp(string, "yes") == 0 ||
+ strcmp(string, "1") == 0) {
+ g_value_set_boolean(val, TRUE);
+ } else if (strcasecmp(string, "false") == 0 ||
+ strcasecmp(string, "no") == 0 ||
+ strcmp(string, "0") == 0) {
+ g_value_set_boolean(val, FALSE);
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean g_value_set_int_from_string(GValue * val, char * string) {
+ long int strto_result;
+ char * strto_end;
+ gint64 multiplier;
+ strto_result = strtol(string, &strto_end, 0);
+ multiplier = find_multiplier(strto_end);
+ if (multiplier == G_MAXINT64) {
+ if (strto_result >= 0) {
+ g_value_set_int(val, G_MAXINT);
+ } else {
+ g_value_set_int(val, G_MININT);
+ }
+ return TRUE;
+ } else if (*string == '\0' || multiplier == 0
+ || strto_result < G_MININT / multiplier
+ || strto_result > G_MAXINT / multiplier) {
+ return FALSE;
+ } else {
+ g_value_set_int(val, (int)(strto_result * multiplier));
+ return TRUE;
+ }
+}
+
+static gboolean g_value_set_uint_from_string(GValue * val, char * string) {
+ unsigned long int strto_result;
+ char * strto_end;
+ guint64 multiplier;
+ strto_result = strtoul(string, &strto_end, 0);
+ multiplier = find_multiplier(strto_end); /* casts */
+ if (multiplier == G_MAXINT64) {
+ g_value_set_uint(val, G_MAXUINT);
+ return TRUE;
+ } else if (multiplier == 0 || *string == '\0' ||
+ strto_result > G_MAXUINT / multiplier) {
+ return FALSE;
+ } else {
+ g_value_set_uint(val, (guint)(strto_result * multiplier));
+ return TRUE;
+ }
+}
+
+static gboolean g_value_set_uint64_from_string(GValue * val, char * string) {
+ unsigned long long int strto_result;
+ char * strto_end;
+ guint64 multiplier;
+ strto_result = strtoull(string, &strto_end, 0);
+ multiplier = find_multiplier(strto_end); /* casts */
+ if (multiplier == G_MAXINT64) {
+ g_value_set_uint64(val, G_MAXUINT64);
+ return TRUE;
+ } else if (multiplier == 0 || *string == '\0' ||
+ strto_result > G_MAXUINT64 / multiplier) {
+ return FALSE;
+ } else {
+ g_value_set_uint64(val, (guint64)(strto_result * multiplier));
+ return TRUE;
+ }
+}
+
+/* Flags can contain multiple values. We assume here that values are like
+ * C identifiers (that is, they match /[A-Za-z_][A-Za-z0-9_]+/), although
+ * that doesn't seem to be a requirement of GLib. With that assumption in
+ * mind, we look for the format "FLAG_1 | FLAG_2 | ... | FLAG_N". */
+static gboolean g_value_set_flags_from_string(GValue * val, char * string) {
+ guint value = 0;
+ char * strtok_saveptr;
+ char * string_copy;
+ char * strtok_first_arg;
+ const char delim[] = " \t,|";
+ GFlagsClass * flags_class;
+
+ flags_class = (GFlagsClass*) g_type_class_ref(G_VALUE_TYPE(val));
+ g_return_val_if_fail(flags_class != NULL, FALSE);
+ g_return_val_if_fail(G_IS_FLAGS_CLASS(flags_class), FALSE);
+
+ /* Don't let strtok stop on original. */
+ strtok_first_arg = string_copy = strdup(string);
+
+ for (;;) {
+ GFlagsValue * flag_value;
+ char * token = strtok_r(strtok_first_arg, delim, &strtok_saveptr);
+ strtok_first_arg = NULL;
+
+ if (token == NULL) {
+ break;
+ }
+
+ flag_value = g_flags_get_value_by_name(flags_class, token);
+ if (flag_value == NULL) {
+ flag_value = g_flags_get_value_by_nick(flags_class, token);
+ }
+ if (flag_value == NULL) {
+ g_fprintf(stderr, _("Invalid flag %s for type %s\n"), token,
+ g_type_name(G_VALUE_TYPE(val)));
+ continue;
+ }
+
+ value |= flag_value->value;
+ }
+
+ amfree(string_copy);
+
+ if (value == 0) {
+ g_fprintf(stderr, _("No valid flags for type %s in string %s\n"),
+ g_type_name(G_VALUE_TYPE(val)), string);
+ return FALSE;
+ }
+
+ g_value_set_flags(val, value);
+ return TRUE;
+
+}
+
+/* This function really ought not to be part of Amanda. In my (Ian's) opinion,
+ serialization and deserialization should be a part of the GValue
+ interface. But it's not, and here we are. */
+gboolean g_value_set_from_string(GValue * val, char * string) {
+ g_return_val_if_fail(val != NULL, FALSE);
+ g_return_val_if_fail(G_IS_VALUE(val), FALSE);
+
+ if (G_VALUE_HOLDS_BOOLEAN(val)) {
+ return g_value_set_boolean_from_string(val, string);
+ } else if (G_VALUE_HOLDS_INT(val)) {
+ return g_value_set_int_from_string(val, string);
+ } else if (G_VALUE_HOLDS_UINT(val)) {
+ return g_value_set_uint_from_string(val, string);
+ } else if (G_VALUE_HOLDS_UINT64(val)) {
+ return g_value_set_uint64_from_string(val, string);
+ } else if (G_VALUE_HOLDS_STRING(val)) {
+ g_value_set_string(val, string);
+ return TRUE;
+ } else if (G_VALUE_HOLDS_FLAGS(val)) {
+ return g_value_set_flags_from_string(val, string);
+ }
+
+ return TRUE;
+}
+
+gint
+g_compare_strings(
+ gconstpointer a,
+ gconstpointer b)
+{
+ return strcmp((char *)a, (char *)b);
+}
+
+char * g_strjoinv_and_free(char ** strv, const char * seperator) {
+ char * rval = g_strjoinv(seperator, strv);
+ g_strfreev(strv);
+ return rval;
+}
+
+char ** g_flags_name_to_strv(int value, GType type) {
+ return g_flags_to_strv(value, type, FLAG_STRING_NAME);
+}
+
+char ** g_flags_short_name_to_strv(int value, GType type) {
+ return g_flags_to_strv(value, type, FLAG_STRING_SHORT_NAME);
+}
+
+char ** g_flags_nick_to_strv(int value, GType type) {
+ return g_flags_to_strv(value, type, FLAG_STRING_NICK);
+}
+
+static char * get_name_from_value(GFlagsValue * value, FlagString source) {
+ switch (source) {
+ case FLAG_STRING_NAME:
+ case FLAG_STRING_SHORT_NAME:
+ return strdup(value->value_name);
+ case FLAG_STRING_NICK:
+ return strdup(value->value_nick);
+ default:
+ return NULL;
+ }
+}
+
+/* If freed and notfreed have a common prefix that is different from freed,
+ then return that and free freed. Otherwise, return freed. */
+static char * find_common_prefix(char * freed, const char * notfreed) {
+ char * freed_ptr = freed;
+ const char * notfreed_ptr = notfreed;
+
+ if (freed == NULL) {
+ if (notfreed == NULL) {
+ return NULL;
+ } else {
+ return strdup(notfreed);
+ }
+ } else if (notfreed == NULL) {
+ amfree(freed);
+ return strdup("");
+ }
+
+ while (*freed_ptr == *notfreed_ptr) {
+ freed_ptr ++;
+ notfreed_ptr ++;
+ }
+
+ *freed_ptr = '\0';
+ return freed;
+}
+
+static char ** g_flags_to_strv(int value, GType type,
+ FlagString source) {
+ GPtrArray * rval;
+ GFlagsValue * flagsvalue;
+ char * common_prefix = NULL;
+ int common_prefix_len;
+ GFlagsClass * class;
+
+ g_return_val_if_fail(G_TYPE_IS_FLAGS(type), NULL);
+ g_return_val_if_fail((class = g_type_class_ref(type)) != NULL, NULL);
+ g_return_val_if_fail(G_IS_FLAGS_CLASS(class), NULL);
+
+ rval = g_ptr_array_new();
+ for (flagsvalue = class->values;
+ flagsvalue->value_name != NULL;
+ flagsvalue ++) {
+ if (source == FLAG_STRING_SHORT_NAME) {
+ common_prefix = find_common_prefix(common_prefix,
+ flagsvalue->value_name);
+ }
+
+ if ((flagsvalue->value == 0 && value == 0) ||
+ (flagsvalue->value != 0 && (value & flagsvalue->value))) {
+ g_ptr_array_add(rval, get_name_from_value(flagsvalue, source));
+ }
+ }
+
+ if (source == FLAG_STRING_SHORT_NAME && common_prefix != NULL &&
+ ((common_prefix_len = strlen(common_prefix))) > 0) {
+ char * old;
+ char * new;
+ guint i;
+ for (i = 0; i < rval->len; i ++) {
+ old = g_ptr_array_index(rval, i);
+ new = strdup(old + common_prefix_len);
+ g_ptr_array_index(rval, i) = new;
+ g_free(old);
+ }
+ }
+
+ g_ptr_array_add(rval, NULL);
+
+ amfree(common_prefix);
+ return (char**)g_ptr_array_free(rval, FALSE);
+}
+
+char * g_english_strjoinv(char ** strv, const char * conjunction) {
+ int length;
+ char * last;
+ char * joined;
+ char * rval;
+ strv = g_strdupv(strv);
+
+ length = g_strv_length(strv);
+ last = strv[length - 1];
+ strv[length - 1] = NULL;
+
+ joined = g_strjoinv(", ", strv);
+ rval = g_strdup_printf("%s, %s %s", joined, conjunction, last);
+
+ g_free(joined);
+ g_free(last);
+ g_strfreev(strv);
+ return rval;
+}
+
+char * g_english_strjoinv_and_free(char ** strv, const char * conjunction) {
+ char * rval = g_english_strjoinv(strv, conjunction);
+ g_strfreev(strv);
+ return rval;
+}
+
+#if !(GLIB_CHECK_VERSION(2,6,0))
+guint g_strv_length(gchar ** strv) {
+ int rval = 0;
+
+ if (G_UNLIKELY(strv == NULL))
+ return 0;
+
+ while (*strv != NULL) {
+ rval ++;
+ strv ++;
+ }
+ return rval;
+}
+
+#endif /* GLIB_CHECK_VERSION(2.6.0) */