ec9728ad4c8a24d7ad2a61f52bb3b0a789a8a007
[debian/amanda] / common-src / glib-util.c
1 /*
2  * Copyright (c) 2005 Zmanda, Inc.  All Rights Reserved.
3  * 
4  * This library is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License version 2.1 as 
6  * published by the Free Software Foundation.
7  * 
8  * This library is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
11  * License for more details.
12  * 
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this library; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
16  * 
17  * Contact information: Zmanda Inc., 505 N Mathlida Ave, Suite 120
18  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
19  */
20
21 /*
22  * Utilities that aren't quite included in glib
23  *
24  * Author: Dustin J. Mitchell <dustin@zmanda.com>, Ian Turner <ian@zmanda.com>
25  */
26
27 #include "amanda.h"
28 #include "glib-util.h"
29 #include "conffile.h" /* For find_multiplier. */
30
31 typedef enum {
32     FLAG_STRING_NAME,
33     FLAG_STRING_SHORT_NAME,
34     FLAG_STRING_NICK
35 } FlagString;
36
37 static char ** g_flags_to_strv(int value, GType type, FlagString source);
38
39 void
40 _glib_util_foreach_glue(gpointer data, gpointer func)
41 {
42     void (*one_arg_fn)(gpointer) = (void (*)(gpointer))func;
43     one_arg_fn(data);
44 }
45
46 GValue* g_value_unset_init(GValue* value, GType type) {
47     g_return_val_if_fail(value != NULL, NULL);
48
49     if (G_IS_VALUE(value)) {
50         g_value_unset(value);
51     }
52     g_value_init(value, type);
53     return value;
54 }
55
56 GValue* g_value_unset_copy(const GValue * from, GValue * to) {
57     g_return_val_if_fail(from != NULL, NULL);
58     g_return_val_if_fail(to != NULL, NULL);
59
60     g_value_unset_init(to, G_VALUE_TYPE(from));
61     g_value_copy(from, to);
62     return to;
63 }
64
65 void g_list_free_full(GList * list) {
66     GList * cur = list;
67
68     while (cur != NULL) {
69         gpointer data = cur->data;
70         amfree(data);
71         cur = g_list_next(cur);
72     }
73
74     g_list_free(list);
75 }
76
77 void g_slist_free_full(GSList * list) {
78     GSList * cur = list;
79
80     while (cur != NULL) {
81         gpointer data = cur->data;
82         amfree(data);
83         cur = g_slist_next(cur);
84     }
85
86     g_slist_free(list);
87 }
88
89 void g_queue_free_full(GQueue * queue) {
90     while (!g_queue_is_empty(queue)) {
91         gpointer data;
92         data = g_queue_pop_head(queue);
93         amfree(data);
94     }
95     g_queue_free(queue);
96 }
97
98 void g_ptr_array_free_full(GPtrArray * array) {
99     size_t i;
100
101     for (i = 0; i < array->len; i ++) {
102         amfree(g_ptr_array_index(array, i));
103     }
104     g_ptr_array_free(array, TRUE);
105 }
106
107 gboolean g_value_compare(GValue * a, GValue * b) {
108     if (a == NULL && b == NULL)
109         return TRUE;
110     if (a == NULL || b == NULL)
111         return FALSE;
112     if (G_VALUE_TYPE(a) != G_VALUE_TYPE(b))
113         return FALSE;
114     if (g_value_fits_pointer(a) && g_value_fits_pointer(b)) {
115         return g_value_peek_pointer(a) == g_value_peek_pointer(b);
116     } else {
117         /* Since there is no builtin comparison function, we resort to
118            comparing serialized strings. Yuck. */
119         char * a_str;
120         char * b_str;
121         gboolean rval;
122         a_str = g_strdup_value_contents(a);
123         b_str = g_strdup_value_contents(b);
124         rval = (0 == strcmp(a_str, b_str));
125         amfree(a_str);
126         amfree(b_str);
127         return rval;
128     }
129     
130     g_assert_not_reached();
131 }
132
133 static gboolean g_value_set_boolean_from_string(GValue * val, char * string) {
134     if (strcasecmp(string, "true") == 0 ||
135         strcasecmp(string, "yes") == 0 ||
136         strcmp(string, "1") == 0) {
137         g_value_set_boolean(val, TRUE);
138     } else if (strcasecmp(string, "false") == 0 ||
139                strcasecmp(string, "no") == 0 ||
140                strcmp(string, "0") == 0) {
141         g_value_set_boolean(val, FALSE);
142     } else {
143         return FALSE;
144     }
145
146     return TRUE;
147 }
148
149 static gboolean g_value_set_int_from_string(GValue * val, char * string) {
150     long int strto_result;
151     char * strto_end;
152     gint64 multiplier;
153     strto_result = strtol(string, &strto_end, 0);
154     multiplier = find_multiplier(strto_end);
155     if (multiplier == G_MAXINT64) {
156         if (strto_result >= 0) {
157             g_value_set_int(val, G_MAXINT);
158         } else {
159             g_value_set_int(val, G_MININT);
160         }
161         return TRUE;
162     } else if (*string == '\0' || multiplier == 0
163                || strto_result < G_MININT / multiplier
164                || strto_result > G_MAXINT / multiplier) {
165         return FALSE;
166     } else { 
167         g_value_set_int(val, (int)(strto_result * multiplier));
168         return TRUE;
169     }
170 }
171
172 static gboolean g_value_set_uint_from_string(GValue * val, char * string) {
173     unsigned long int strto_result;
174     char * strto_end;
175     guint64 multiplier;
176     strto_result = strtoul(string, &strto_end, 0);
177     multiplier = find_multiplier(strto_end); /* casts */
178     if (multiplier == G_MAXINT64) {
179         g_value_set_uint(val, G_MAXUINT);
180         return TRUE;
181     } else if (multiplier == 0 || *string == '\0' ||
182                strto_result > G_MAXUINT / multiplier) {
183         return FALSE;
184     } else {
185         g_value_set_uint(val, (guint)(strto_result * multiplier));
186         return TRUE;
187     }
188 }
189
190 static gboolean g_value_set_uint64_from_string(GValue * val, char * string) {
191     unsigned long long int strto_result;
192     char * strto_end;
193     guint64 multiplier;
194     strto_result = strtoull(string, &strto_end, 0);
195     multiplier = find_multiplier(strto_end); /* casts */
196     if (multiplier == G_MAXINT64) {
197         g_value_set_uint64(val, G_MAXUINT64);
198         return TRUE;
199     } else if (multiplier == 0 || *string == '\0' ||
200         strto_result > G_MAXUINT64 / multiplier) {
201         return FALSE;
202     } else {
203         g_value_set_uint64(val, (guint64)(strto_result * multiplier));
204         return TRUE;
205     }
206 }
207
208 /* Flags can contain multiple values. We assume here that values are like
209  * C identifiers (that is, they match /[A-Za-z_][A-Za-z0-9_]+/), although
210  * that doesn't seem to be a requirement of GLib. With that assumption in
211  * mind, we look for the format "FLAG_1 | FLAG_2 | ... | FLAG_N". */
212 static gboolean g_value_set_flags_from_string(GValue * val, char * string) {
213     guint value = 0;
214     char * strtok_saveptr;
215     char * string_copy;
216     char * strtok_first_arg;
217     const char delim[] = " \t,|";
218     GFlagsClass * flags_class;
219     
220     flags_class = (GFlagsClass*) g_type_class_ref(G_VALUE_TYPE(val));
221     g_return_val_if_fail(flags_class != NULL, FALSE);
222     g_return_val_if_fail(G_IS_FLAGS_CLASS(flags_class), FALSE);
223
224     /* Don't let strtok stop on original. */
225     strtok_first_arg = string_copy = strdup(string);
226     
227     for (;;) {
228         GFlagsValue * flag_value;
229         char * token = strtok_r(strtok_first_arg, delim, &strtok_saveptr);
230         strtok_first_arg = NULL;
231
232         if (token == NULL) {
233             break;
234         }
235         
236         flag_value = g_flags_get_value_by_name(flags_class, token);
237         if (flag_value == NULL) {
238             flag_value = g_flags_get_value_by_nick(flags_class, token);
239         }
240         if (flag_value == NULL) {
241             g_fprintf(stderr, _("Invalid flag %s for type %s\n"), token,
242                     g_type_name(G_VALUE_TYPE(val)));
243             continue;
244         }
245
246         value |= flag_value->value;
247     }
248     
249     amfree(string_copy);
250     
251     if (value == 0) {
252         g_fprintf(stderr, _("No valid flags for type %s in string %s\n"),
253                 g_type_name(G_VALUE_TYPE(val)), string);
254         return FALSE;
255     }
256     
257     g_value_set_flags(val, value);
258     return TRUE;
259
260 }
261
262 /* This function really ought not to be part of Amanda. In my (Ian's) opinion,
263    serialization and deserialization should be a part of the GValue
264    interface. But it's not, and here we are. */
265 gboolean g_value_set_from_string(GValue * val, char * string) {
266     g_return_val_if_fail(val != NULL, FALSE);
267     g_return_val_if_fail(G_IS_VALUE(val), FALSE);
268
269     if (G_VALUE_HOLDS_BOOLEAN(val)) {
270         return g_value_set_boolean_from_string(val, string);
271     } else if (G_VALUE_HOLDS_INT(val)) {
272         return g_value_set_int_from_string(val, string);
273     } else if (G_VALUE_HOLDS_UINT(val)) {
274         return g_value_set_uint_from_string(val, string);
275     } else if (G_VALUE_HOLDS_UINT64(val)) {
276         return g_value_set_uint64_from_string(val, string);
277     } else if (G_VALUE_HOLDS_STRING(val)) {
278         g_value_set_string(val, string);
279         return TRUE;
280     } else if (G_VALUE_HOLDS_FLAGS(val)) {
281         return g_value_set_flags_from_string(val, string);
282     }
283
284     return TRUE;
285 }
286
287 gint
288 g_compare_strings(
289     gconstpointer a,
290     gconstpointer b)
291 {
292     return strcmp((char *)a, (char *)b);
293 }
294
295 char * g_strjoinv_and_free(char ** strv, const char * seperator) {
296     char * rval = g_strjoinv(seperator, strv);
297     g_strfreev(strv);
298     return rval;
299 }
300
301 char ** g_flags_name_to_strv(int value, GType type) {
302     return g_flags_to_strv(value, type, FLAG_STRING_NAME);
303 }
304
305 char ** g_flags_short_name_to_strv(int value, GType type) {
306     return g_flags_to_strv(value, type, FLAG_STRING_SHORT_NAME);
307 }
308
309 char ** g_flags_nick_to_strv(int value, GType type) {
310     return g_flags_to_strv(value, type, FLAG_STRING_NICK);
311 }
312
313 static char * get_name_from_value(GFlagsValue * value, FlagString source) {
314     switch (source) {
315     case FLAG_STRING_NAME:
316     case FLAG_STRING_SHORT_NAME:
317         return strdup(value->value_name);
318     case FLAG_STRING_NICK:
319         return strdup(value->value_nick);
320     default:
321         return NULL;
322     }
323 }
324
325 /* If freed and notfreed have a common prefix that is different from freed,
326    then return that and free freed. Otherwise, return freed. */
327 static char * find_common_prefix(char * freed, const char * notfreed) {
328     char * freed_ptr = freed;
329     const char * notfreed_ptr = notfreed;
330
331     if (freed == NULL) {
332         if (notfreed == NULL) {
333             return NULL;
334         } else {
335             return strdup(notfreed);
336         }
337     } else if (notfreed == NULL) {
338         amfree(freed);
339         return strdup("");
340     }
341
342     while (*freed_ptr == *notfreed_ptr) {
343         freed_ptr ++;
344         notfreed_ptr ++;
345     }
346
347     *freed_ptr = '\0';
348     return freed;
349 }
350
351 static char ** g_flags_to_strv(int value, GType type,
352                                FlagString source) {
353     GPtrArray * rval;
354     GFlagsValue * flagsvalue;
355     char * common_prefix = NULL;
356     int common_prefix_len;
357     GFlagsClass * class;
358
359     g_return_val_if_fail(G_TYPE_IS_FLAGS(type), NULL);
360     g_return_val_if_fail((class = g_type_class_ref(type)) != NULL, NULL);
361     g_return_val_if_fail(G_IS_FLAGS_CLASS(class), NULL);
362         
363     rval = g_ptr_array_new();
364     for (flagsvalue = class->values;
365          flagsvalue->value_name != NULL;
366          flagsvalue ++) {
367         if (source == FLAG_STRING_SHORT_NAME) {
368             common_prefix = find_common_prefix(common_prefix,
369                                                flagsvalue->value_name);
370         }
371                                                
372         if ((flagsvalue->value == 0 && value == 0) ||
373             (flagsvalue->value != 0 && (value & flagsvalue->value))) {
374             g_ptr_array_add(rval, get_name_from_value(flagsvalue, source));
375         }
376     }
377
378     if (source == FLAG_STRING_SHORT_NAME && common_prefix != NULL &&
379         ((common_prefix_len = strlen(common_prefix))) > 0) {
380         char * old;
381         char * new;
382         guint i;
383         for (i = 0; i < rval->len; i ++) {
384             old = g_ptr_array_index(rval, i);
385             new = strdup(old + common_prefix_len);
386             g_ptr_array_index(rval, i) = new;
387             g_free(old);
388         }
389     }
390     
391     g_ptr_array_add(rval, NULL);
392
393     amfree(common_prefix);
394     return (char**)g_ptr_array_free(rval, FALSE);
395 }
396
397 char * g_english_strjoinv(char ** strv, const char * conjunction) {
398     int length;
399     char * last;
400     char * joined;
401     char * rval;
402     strv = g_strdupv(strv);
403
404     length = g_strv_length(strv);
405     last = strv[length - 1];
406     strv[length - 1] = NULL;
407     
408     joined = g_strjoinv(", ", strv);
409     rval = g_strdup_printf("%s, %s %s", joined, conjunction, last);
410
411     g_free(joined);
412     g_free(last);
413     g_strfreev(strv);
414     return rval;
415 }
416
417 char * g_english_strjoinv_and_free(char ** strv, const char * conjunction) {
418     char * rval = g_english_strjoinv(strv, conjunction);
419     g_strfreev(strv);
420     return rval;   
421 }
422
423 #if !(GLIB_CHECK_VERSION(2,6,0))
424 guint g_strv_length(gchar ** strv) {
425     int rval = 0;
426
427     if (G_UNLIKELY(strv == NULL))
428         return 0;
429
430     while (*strv != NULL) {
431         rval ++;
432         strv ++;
433     }
434     return rval;
435 }
436
437 #endif /* GLIB_CHECK_VERSION(2.6.0) */