summaryrefslogtreecommitdiffstats
path: root/src/core/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/misc.c')
-rw-r--r--src/core/misc.c1094
1 files changed, 1094 insertions, 0 deletions
diff --git a/src/core/misc.c b/src/core/misc.c
new file mode 100644
index 0000000..f0bbf4e
--- /dev/null
+++ b/src/core/misc.c
@@ -0,0 +1,1094 @@
+/*
+ misc.c : irssi
+
+ Copyright (C) 1999 Timo Sirainen
+
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include <irssi/src/core/misc.h>
+#include <irssi/src/core/commands.h>
+
+typedef struct {
+ int condition;
+ GInputFunction function;
+ void *data;
+} IRSSI_INPUT_REC;
+
+static int irssi_io_invoke(GIOChannel *source, GIOCondition condition,
+ void *data)
+{
+ IRSSI_INPUT_REC *rec = data;
+ int icond = 0;
+
+ if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ /* error, we have to call the function.. */
+ if (rec->condition & G_IO_IN)
+ icond |= I_INPUT_READ;
+ else
+ icond |= I_INPUT_WRITE;
+ }
+
+ if (condition & (G_IO_IN | G_IO_PRI))
+ icond |= I_INPUT_READ;
+ if (condition & G_IO_OUT)
+ icond |= I_INPUT_WRITE;
+
+ if (rec->condition & icond)
+ rec->function(rec->data, source, icond);
+
+ return TRUE;
+}
+
+int i_input_add_full(GIOChannel *source, int priority, int condition, GInputFunction function,
+ void *data)
+{
+ IRSSI_INPUT_REC *rec;
+ unsigned int result;
+ GIOCondition cond;
+
+ rec = g_new(IRSSI_INPUT_REC, 1);
+ rec->condition = condition;
+ rec->function = function;
+ rec->data = data;
+
+ cond = (GIOCondition) (G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+ if (condition & I_INPUT_READ)
+ cond |= G_IO_IN|G_IO_PRI;
+ if (condition & I_INPUT_WRITE)
+ cond |= G_IO_OUT;
+
+ result = g_io_add_watch_full(source, priority, cond,
+ irssi_io_invoke, rec, g_free);
+
+ return result;
+}
+
+int i_input_add(GIOChannel *source, int condition, GInputFunction function, void *data)
+{
+ return i_input_add_full(source, G_PRIORITY_DEFAULT, condition, function, data);
+}
+
+/* easy way to bypass glib polling of io channel internal buffer */
+int i_input_add_poll(int fd, int priority, int condition, GInputFunction function, void *data)
+{
+ GIOChannel *source = g_io_channel_unix_new(fd);
+ int ret = i_input_add_full(source, priority, condition, function, data);
+ g_io_channel_unref(source);
+ return ret;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+int g_timeval_cmp(const GTimeVal *tv1, const GTimeVal *tv2)
+{
+ if (tv1->tv_sec < tv2->tv_sec)
+ return -1;
+ if (tv1->tv_sec > tv2->tv_sec)
+ return 1;
+
+ return tv1->tv_usec < tv2->tv_usec ? -1 :
+ tv1->tv_usec > tv2->tv_usec ? 1 : 0;
+}
+
+long get_timeval_diff(const GTimeVal *tv1, const GTimeVal *tv2)
+{
+ long secs, usecs;
+
+ secs = tv1->tv_sec - tv2->tv_sec;
+ usecs = tv1->tv_usec - tv2->tv_usec;
+ if (usecs < 0) {
+ usecs += 1000000;
+ secs--;
+ }
+ usecs = usecs/1000 + secs * 1000;
+
+ return usecs;
+}
+#pragma GCC diagnostic pop
+
+#if GLIB_CHECK_VERSION(2, 56, 0)
+/* nothing */
+#else
+/* compatibility code for old GLib */
+GDateTime *g_date_time_new_from_iso8601(const gchar *iso_date, GTimeZone *default_tz)
+{
+ GTimeVal time;
+ if (g_time_val_from_iso8601(iso_date, &time)) {
+ return g_date_time_new_from_timeval_utc(&time);
+ } else {
+ return NULL;
+ }
+}
+#endif
+
+int find_substr(const char *list, const char *item)
+{
+ const char *ptr;
+
+ g_return_val_if_fail(list != NULL, FALSE);
+ g_return_val_if_fail(item != NULL, FALSE);
+
+ if (*item == '\0')
+ return FALSE;
+
+ for (;;) {
+ while (i_isspace(*list)) list++;
+ if (*list == '\0') break;
+
+ ptr = strchr(list, ' ');
+ if (ptr == NULL) ptr = list+strlen(list);
+
+ if (g_ascii_strncasecmp(list, item, ptr-list) == 0 &&
+ item[ptr-list] == '\0')
+ return TRUE;
+
+ list = ptr;
+ }
+
+ return FALSE;
+}
+
+int strarray_find(char **array, const char *item)
+{
+ char **tmp;
+ int index;
+
+ g_return_val_if_fail(array != NULL, -1);
+ g_return_val_if_fail(item != NULL, -1);
+
+ index = 0;
+ for (tmp = array; *tmp != NULL; tmp++, index++) {
+ if (g_ascii_strcasecmp(*tmp, item) == 0)
+ return index;
+ }
+
+ return -1;
+}
+
+GSList *i_slist_find_string(GSList *list, const char *key)
+{
+ for (; list != NULL; list = list->next)
+ if (g_strcmp0(list->data, key) == 0) return list;
+
+ return NULL;
+}
+
+GSList *i_slist_find_icase_string(GSList *list, const char *key)
+{
+ for (; list != NULL; list = list->next)
+ if (g_ascii_strcasecmp(list->data, key) == 0) return list;
+
+ return NULL;
+}
+
+void *i_slist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data)
+{
+ void *ret;
+
+ while (list != NULL) {
+ ret = func(list->data, (void *) data);
+ if (ret != NULL) return ret;
+
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+void i_slist_free_full(GSList *list, GDestroyNotify free_func)
+{
+ GSList *tmp;
+
+ if (list == NULL)
+ return;
+
+ for (tmp = list; tmp != NULL; tmp = tmp->next)
+ free_func(tmp->data);
+
+ g_slist_free(list);
+}
+
+GSList *i_slist_remove_string(GSList *list, const char *str)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(list, str, (GCompareFunc) g_strcmp0);
+ if (l != NULL)
+ return g_slist_remove_link(list, l);
+
+ return list;
+}
+
+GSList *i_slist_delete_string(GSList *list, const char *str, GDestroyNotify free_func)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(list, str, (GCompareFunc) g_strcmp0);
+ if (l != NULL) {
+ free_func(l->data);
+ return g_slist_delete_link(list, l);
+ }
+
+ return list;
+}
+
+/* `list' contains pointer to structure with a char* to string. */
+char *gslistptr_to_string(GSList *list, int offset, const char *delimiter)
+{
+ GString *str;
+ char **data, *ret;
+
+ str = g_string_new(NULL);
+ while (list != NULL) {
+ data = G_STRUCT_MEMBER_P(list->data, offset);
+
+ if (str->len != 0) g_string_append(str, delimiter);
+ g_string_append(str, *data);
+ list = list->next;
+ }
+
+ ret = str->str;
+ g_string_free(str, FALSE);
+ return ret;
+}
+
+/* `list' contains char* */
+char *i_slist_to_string(GSList *list, const char *delimiter)
+{
+ GString *str;
+ char *ret;
+
+ str = g_string_new(NULL);
+ while (list != NULL) {
+ if (str->len != 0) g_string_append(str, delimiter);
+ g_string_append(str, list->data);
+
+ list = list->next;
+ }
+
+ ret = str->str;
+ g_string_free(str, FALSE);
+ return ret;
+}
+
+/* remove all the options from the optlist hash table that are valid for the
+ * command cmd */
+GList *optlist_remove_known(const char *cmd, GHashTable *optlist)
+{
+ GList *list, *tmp, *next;
+
+ list = g_hash_table_get_keys(optlist);
+ if (cmd != NULL && list != NULL) {
+ for (tmp = list; tmp != NULL; tmp = next) {
+ char *option = tmp->data;
+ next = tmp->next;
+
+ if (command_have_option(cmd, option))
+ list = g_list_remove(list, option);
+ }
+ }
+
+ return list;
+}
+
+GList *i_list_find_string(GList *list, const char *key)
+{
+ for (; list != NULL; list = list->next)
+ if (g_strcmp0(list->data, key) == 0) return list;
+
+ return NULL;
+}
+
+GList *i_list_find_icase_string(GList *list, const char *key)
+{
+ for (; list != NULL; list = list->next)
+ if (g_ascii_strcasecmp(list->data, key) == 0) return list;
+
+ return NULL;
+}
+
+char *stristr(const char *data, const char *key)
+{
+ const char *max;
+ int keylen, datalen, pos;
+
+ keylen = strlen(key);
+ datalen = strlen(data);
+
+ if (keylen > datalen)
+ return NULL;
+ if (keylen == 0)
+ return (char *) data;
+
+ max = data+datalen-keylen;
+ pos = 0;
+ while (data <= max) {
+ if (key[pos] == '\0')
+ return (char *) data;
+
+ if (i_toupper(data[pos]) == i_toupper(key[pos]))
+ pos++;
+ else {
+ data++;
+ pos = 0;
+ }
+ }
+
+ return NULL;
+}
+
+#define isbound(c) \
+ ((unsigned char) (c) < 128 && \
+ (i_isspace(c) || i_ispunct(c)))
+
+static char *strstr_full_case(const char *data, const char *key, int icase)
+{
+ const char *start, *max;
+ int keylen, datalen, pos, match;
+
+ keylen = strlen(key);
+ datalen = strlen(data);
+
+ if (keylen > datalen)
+ return NULL;
+ if (keylen == 0)
+ return (char *) data;
+
+ max = data+datalen-keylen;
+ start = data; pos = 0;
+ while (data <= max) {
+ if (key[pos] == '\0') {
+ if (data[pos] != '\0' && !isbound(data[pos])) {
+ data++;
+ pos = 0;
+ continue;
+ }
+ return (char *) data;
+ }
+
+ match = icase ? (i_toupper(data[pos]) == i_toupper(key[pos])) :
+ data[pos] == key[pos];
+
+ if (match && (pos != 0 || data == start || isbound(data[-1])))
+ pos++;
+ else {
+ data++;
+ pos = 0;
+ }
+ }
+
+ return NULL;
+}
+
+char *strstr_full(const char *data, const char *key)
+{
+ return strstr_full_case(data, key, FALSE);
+}
+
+char *stristr_full(const char *data, const char *key)
+{
+ return strstr_full_case(data, key, TRUE);
+}
+
+/* convert ~/ to $HOME */
+char *convert_home(const char *path)
+{
+ const char *home;
+
+ if (*path == '~' && (*(path+1) == '/' || *(path+1) == '\0')) {
+ home = g_get_home_dir();
+ if (home == NULL)
+ home = ".";
+
+ return g_strconcat(home, path+1, NULL);
+ } else {
+ return g_strdup(path);
+ }
+}
+
+int i_istr_equal(gconstpointer v, gconstpointer v2)
+{
+ return g_ascii_strcasecmp((const char *) v, (const char *) v2) == 0;
+}
+
+int i_istr_cmp(gconstpointer v, gconstpointer v2)
+{
+ return g_ascii_strcasecmp((const char *) v, (const char *) v2);
+}
+
+guint i_istr_hash(gconstpointer v)
+{
+ const signed char *p;
+ guint32 h = 5381;
+
+ for (p = v; *p != '\0'; p++)
+ h = (h << 5) + h + g_ascii_toupper(*p);
+
+ return h;
+}
+
+/* Find `mask' from `data', you can use * and ? wildcards. */
+int match_wildcards(const char *cmask, const char *data)
+{
+ char *mask, *newmask, *p1, *p2;
+ int ret;
+
+ newmask = mask = g_strdup(cmask);
+ for (; *mask != '\0' && *data != '\0'; mask++) {
+ if (*mask != '*') {
+ if (*mask != '?' && i_toupper(*mask) != i_toupper(*data))
+ break;
+
+ data++;
+ continue;
+ }
+
+ while (*mask == '?' || *mask == '*') mask++;
+ if (*mask == '\0') {
+ data += strlen(data);
+ break;
+ }
+
+ p1 = strchr(mask, '*');
+ p2 = strchr(mask, '?');
+ if (p1 == NULL || (p2 < p1 && p2 != NULL)) p1 = p2;
+
+ if (p1 != NULL) *p1 = '\0';
+
+ data = stristr(data, mask);
+ if (data == NULL) break;
+
+ data += strlen(mask);
+ mask += strlen(mask)-1;
+
+ if (p1 != NULL) *p1 = p1 == p2 ? '?' : '*';
+ }
+
+ while (*mask == '*') mask++;
+
+ ret = data != NULL && *data == '\0' && *mask == '\0';
+ g_free(newmask);
+
+ return ret;
+}
+
+/* Return TRUE if all characters in `str' are numbers.
+ Stop when `end_char' is found from string. */
+int is_numeric(const char *str, char end_char)
+{
+ g_return_val_if_fail(str != NULL, FALSE);
+
+ if (*str == '\0' || *str == end_char)
+ return FALSE;
+
+ while (*str != '\0' && *str != end_char) {
+ if (!i_isdigit(*str)) return FALSE;
+ str++;
+ }
+
+ return TRUE;
+}
+
+/* replace all `from' chars in string to `to' chars. returns `str' */
+char *replace_chars(char *str, char from, char to)
+{
+ char *p;
+
+ for (p = str; *p != '\0'; p++) {
+ if (*p == from) *p = to;
+ }
+ return str;
+}
+
+int octal2dec(int octal)
+{
+ int dec, n;
+
+ dec = 0; n = 1;
+ while (octal != 0) {
+ dec += n*(octal%10);
+ octal /= 10; n *= 8;
+ }
+
+ return dec;
+}
+
+int dec2octal(int decimal)
+{
+ int octal, pos;
+
+ octal = 0; pos = 0;
+ while (decimal > 0) {
+ octal += (decimal & 7)*(pos == 0 ? 1 : pos);
+ decimal /= 8;
+ pos += 10;
+ }
+
+ return octal;
+}
+
+/* string -> uoff_t */
+uoff_t str_to_uofft(const char *str)
+{
+#ifdef UOFF_T_LONG_LONG
+ return (uoff_t)strtoull(str, NULL, 10);
+#else
+ return (uoff_t)strtoul(str, NULL, 10);
+#endif
+}
+
+/* convert all low-ascii (<32) to ^<A..> combinations */
+char *show_lowascii(const char *str)
+{
+ char *ret, *p;
+
+ ret = p = g_malloc(strlen(str)*2+1);
+ while (*str != '\0') {
+ if ((unsigned char) *str >= 32)
+ *p++ = *str;
+ else {
+ *p++ = '^';
+ *p++ = *str + 'A'-1;
+ }
+ str++;
+ }
+ *p = '\0';
+
+ return ret;
+}
+
+/* Get time in human readable form with localtime() + asctime() */
+char *my_asctime(time_t t)
+{
+ struct tm *tm;
+ char *str;
+ int len;
+
+ tm = localtime(&t);
+ if (tm == NULL)
+ return g_strdup("???");
+
+ str = g_strdup(asctime(tm));
+
+ len = strlen(str);
+ if (len > 0) str[len-1] = '\0';
+ return str;
+}
+
+/* Returns number of columns needed to print items.
+ save_column_widths is filled with length of each column. */
+int get_max_column_count(GSList *items, COLUMN_LEN_FUNC len_func,
+ int max_width, int max_columns,
+ int item_extra, int item_min_size,
+ int **save_column_widths, int *rows)
+{
+ GSList *tmp;
+ int **columns, *columns_width, *columns_rows;
+ int item_pos, items_count;
+ int ret, len, max_len, n, col;
+
+ items_count = g_slist_length(items);
+ if (items_count == 0) {
+ *save_column_widths = NULL;
+ *rows = 0;
+ return 0;
+ }
+
+ len = max_width/(item_extra+item_min_size);
+ if (len <= 0) len = 1;
+ if (max_columns <= 0 || len < max_columns)
+ max_columns = len;
+
+ columns = g_new0(int *, max_columns);
+ columns_width = g_new0(int, max_columns);
+ columns_rows = g_new0(int, max_columns);
+
+ for (n = 1; n < max_columns; n++) {
+ columns[n] = g_new0(int, n+1);
+ columns_rows[n] = items_count <= n+1 ? 1 :
+ (items_count+n)/(n+1);
+ }
+
+ /* for each possible column count, save the column widths and
+ find the biggest column count that fits to screen. */
+ item_pos = 0; max_len = 0;
+ for (tmp = items; tmp != NULL; tmp = tmp->next) {
+ len = item_extra+len_func(tmp->data);
+ if (max_len < len)
+ max_len = len;
+
+ for (n = 1; n < max_columns; n++) {
+ if (columns_width[n] > max_width)
+ continue; /* too wide */
+
+ col = item_pos/columns_rows[n];
+ if (columns[n][col] < len) {
+ columns_width[n] += len-columns[n][col];
+ columns[n][col] = len;
+ }
+ }
+
+ item_pos++;
+ }
+
+ for (n = max_columns-1; n >= 1; n--) {
+ if (columns_width[n] <= max_width &&
+ columns[n][n] > 0)
+ break;
+ }
+ ret = n+1;
+
+ *save_column_widths = g_new(int, ret);
+ if (ret == 1) {
+ **save_column_widths = max_len;
+ *rows = 1;
+ } else {
+ memcpy(*save_column_widths, columns[ret-1], sizeof(int)*ret);
+ *rows = columns_rows[ret-1];
+ }
+
+ for (n = 1; n < max_columns; n++)
+ g_free(columns[n]);
+ g_free(columns_width);
+ g_free(columns_rows);
+ g_free(columns);
+
+ return ret;
+}
+
+/* Return a column sorted copy of a list. */
+GSList *columns_sort_list(GSList *list, int rows)
+{
+ GSList *tmp, *sorted;
+ int row, skip;
+
+ if (list == NULL || rows == 0)
+ return list;
+
+ sorted = NULL;
+
+ for (row = 0; row < rows; row++) {
+ tmp = g_slist_nth(list, row);
+ skip = 1;
+ for (; tmp != NULL; tmp = tmp->next) {
+ if (--skip == 0) {
+ skip = rows;
+ sorted = g_slist_append(sorted, tmp->data);
+ }
+ }
+ }
+
+ g_return_val_if_fail(g_slist_length(sorted) ==
+ g_slist_length(list), sorted);
+ return sorted;
+}
+
+/* Expand escape string, the first character in data should be the
+ one after '\'. Returns the expanded character or -1 if error. */
+int expand_escape(const char **data)
+{
+ char digit[4];
+
+ switch (**data) {
+ case 't':
+ return '\t';
+ case 'r':
+ return '\r';
+ case 'n':
+ return '\n';
+ case 'e':
+ return 27; /* ESC */
+ case '\\':
+ return '\\';
+
+ case 'x':
+ /* hex digit */
+ if (!i_isxdigit((*data)[1]) || !i_isxdigit((*data)[2]))
+ return -1;
+
+ digit[0] = (*data)[1];
+ digit[1] = (*data)[2];
+ digit[2] = '\0';
+ *data += 2;
+ return strtol(digit, NULL, 16);
+ case 'c':
+ /* check for end of string */
+ if ((*data)[1] == '\0')
+ return 0;
+ /* control character (\cA = ^A) */
+ (*data)++;
+ return i_toupper(**data) - 64;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ /* octal */
+ digit[1] = digit[2] = digit[3] = '\0';
+ digit[0] = (*data)[0];
+ if ((*data)[1] >= '0' && (*data)[1] <= '7') {
+ ++*data;
+ digit[1] = **data;
+ if ((*data)[1] >= '0' && (*data)[1] <= '7') {
+ ++*data;
+ digit[2] = **data;
+ }
+ }
+ return strtol(digit, NULL, 8);
+ default:
+ return -1;
+ }
+}
+
+/* Escape all '"', "'" and '\' chars with '\' */
+char *escape_string(const char *str)
+{
+ char *ret, *p;
+
+ p = ret = g_malloc(strlen(str)*2+1);
+ while (*str != '\0') {
+ if (*str == '"' || *str == '\'' || *str == '\\')
+ *p++ = '\\';
+ *p++ = *str++;
+ }
+ *p = '\0';
+
+ return ret;
+}
+
+/* Escape all '\' chars with '\' */
+char *escape_string_backslashes(const char *str)
+{
+ char *ret, *p;
+
+ p = ret = g_malloc(strlen(str)*2+1);
+ while (*str != '\0') {
+ if (*str == '\\')
+ *p++ = '\\';
+ *p++ = *str++;
+ }
+ *p = '\0';
+
+ return ret;
+}
+
+int nearest_power(int num)
+{
+ int n = 1;
+
+ while (n < num) n <<= 1;
+ return n;
+}
+
+/* Parses unsigned integers from strings with decent error checking.
+ * Returns true on success, false otherwise (overflow, no valid number, etc)
+ * There's a 31 bit limit so the output can be assigned to signed positive ints */
+int parse_uint(const char *nptr, char **endptr, int base, guint *number)
+{
+ char *endptr_;
+ gulong parsed;
+
+ /* strtoul accepts whitespace and plus/minus signs, for some reason */
+ if (!i_isdigit(*nptr)) {
+ return FALSE;
+ }
+
+ errno = 0;
+ parsed = strtoul(nptr, &endptr_, base);
+
+ if (errno || endptr_ == nptr || parsed >= (1U << 31)) {
+ return FALSE;
+ }
+
+ if (endptr) {
+ *endptr = endptr_;
+ }
+
+ if (number) {
+ *number = (guint) parsed;
+ }
+
+ return TRUE;
+}
+
+static int parse_number_sign(const char *input, char **endptr, int *sign)
+{
+ int sign_ = 1;
+
+ while (i_isspace(*input))
+ input++;
+
+ if (*input == '-') {
+ sign_ = -sign_;
+ input++;
+ }
+
+ *sign = sign_;
+ *endptr = (char *) input;
+ return TRUE;
+}
+
+static int parse_time_interval_uint(const char *time, guint *msecs)
+{
+ const char *desc;
+ guint number;
+ int len, ret, digits;
+
+ *msecs = 0;
+
+ /* max. return value is around 24 days */
+ number = 0; ret = TRUE; digits = FALSE;
+ while (i_isspace(*time))
+ time++;
+ for (;;) {
+ if (i_isdigit(*time)) {
+ char *endptr;
+ if (!parse_uint(time, &endptr, 10, &number)) {
+ return FALSE;
+ }
+ time = endptr;
+ digits = TRUE;
+ continue;
+ }
+
+ if (!digits)
+ return FALSE;
+
+ /* skip punctuation */
+ while (*time != '\0' && i_ispunct(*time) && *time != '-')
+ time++;
+
+ /* get description */
+ for (len = 0, desc = time; i_isalpha(*time); time++)
+ len++;
+
+ while (i_isspace(*time))
+ time++;
+
+ if (len == 0) {
+ if (*time != '\0')
+ return FALSE;
+ *msecs += number * 1000; /* assume seconds */
+ return TRUE;
+ }
+
+ if (g_ascii_strncasecmp(desc, "days", len) == 0) {
+ if (number > 24) {
+ /* would overflow */
+ return FALSE;
+ }
+ *msecs += number * 1000*3600*24;
+ } else if (g_ascii_strncasecmp(desc, "hours", len) == 0)
+ *msecs += number * 1000*3600;
+ else if (g_ascii_strncasecmp(desc, "minutes", len) == 0 ||
+ g_ascii_strncasecmp(desc, "mins", len) == 0)
+ *msecs += number * 1000*60;
+ else if (g_ascii_strncasecmp(desc, "seconds", len) == 0 ||
+ g_ascii_strncasecmp(desc, "secs", len) == 0)
+ *msecs += number * 1000;
+ else if (g_ascii_strncasecmp(desc, "milliseconds", len) == 0 ||
+ g_ascii_strncasecmp(desc, "millisecs", len) == 0 ||
+ g_ascii_strncasecmp(desc, "mseconds", len) == 0 ||
+ g_ascii_strncasecmp(desc, "msecs", len) == 0)
+ *msecs += number;
+ else {
+ ret = FALSE;
+ }
+
+ /* skip punctuation */
+ while (*time != '\0' && i_ispunct(*time) && *time != '-')
+ time++;
+
+ if (*time == '\0')
+ break;
+
+ number = 0;
+ digits = FALSE;
+ }
+
+ return ret;
+}
+
+static int parse_size_uint(const char *size, guint *bytes)
+{
+ const char *desc;
+ guint number, multiplier, limit;
+ int len;
+
+ *bytes = 0;
+
+ /* max. return value is about 1.6 years */
+ number = 0;
+ while (*size != '\0') {
+ if (i_isdigit(*size)) {
+ char *endptr;
+ if (!parse_uint(size, &endptr, 10, &number)) {
+ return FALSE;
+ }
+ size = endptr;
+ continue;
+ }
+
+ /* skip punctuation */
+ while (*size != '\0' && i_ispunct(*size))
+ size++;
+
+ /* get description */
+ for (len = 0, desc = size; i_isalpha(*size); size++)
+ len++;
+
+ if (len == 0) {
+ if (number == 0) {
+ /* "0" - allow it */
+ return TRUE;
+ }
+
+ *bytes += number*1024; /* assume kilobytes */
+ return FALSE;
+ }
+
+ multiplier = 0;
+ limit = 0;
+
+ if (g_ascii_strncasecmp(desc, "gbytes", len) == 0) {
+ multiplier = 1U << 30;
+ limit = 2U << 0;
+ }
+ if (g_ascii_strncasecmp(desc, "mbytes", len) == 0) {
+ multiplier = 1U << 20;
+ limit = 2U << 10;
+ }
+ if (g_ascii_strncasecmp(desc, "kbytes", len) == 0) {
+ multiplier = 1U << 10;
+ limit = 2U << 20;
+ }
+ if (g_ascii_strncasecmp(desc, "bytes", len) == 0) {
+ multiplier = 1;
+ limit = 2U << 30;
+ }
+
+ if (limit && number > limit) {
+ return FALSE;
+ }
+
+ *bytes += number * multiplier;
+
+ /* skip punctuation */
+ while (*size != '\0' && i_ispunct(*size))
+ size++;
+ }
+
+ return TRUE;
+}
+
+int parse_size(const char *size, int *bytes)
+{
+ guint bytes_;
+ int ret;
+
+ ret = parse_size_uint(size, &bytes_);
+
+ if (bytes_ > (1U << 31)) {
+ return FALSE;
+ }
+
+ *bytes = bytes_;
+ return ret;
+}
+
+int parse_time_interval(const char *time, int *msecs)
+{
+ guint msecs_;
+ char *number;
+ int ret, sign;
+
+ parse_number_sign(time, &number, &sign);
+
+ ret = parse_time_interval_uint(number, &msecs_);
+
+ if (msecs_ > (1U << 31)) {
+ return FALSE;
+ }
+
+ *msecs = msecs_ * sign;
+ return ret;
+}
+
+
+char *ascii_strup(char *str)
+{
+ char *s;
+
+ for (s = str; *s; s++)
+ *s = g_ascii_toupper (*s);
+ return str;
+}
+
+char *ascii_strdown(char *str)
+{
+ char *s;
+
+ for (s = str; *s; s++)
+ *s = g_ascii_tolower (*s);
+ return str;
+}
+
+char **strsplit_len(const char *str, int len, gboolean onspace)
+{
+ char **ret = g_new(char *, 1);
+ int n;
+ int offset;
+
+ for (n = 0; *str != '\0'; n++, str += offset) {
+ offset = MIN(len, strlen(str));
+ if (onspace && strlen(str) > len) {
+ /*
+ * Try to find a space to split on and leave
+ * the space on the previous line.
+ */
+ int i;
+ for (i = len - 1; i > 0; i--) {
+ if (str[i] == ' ') {
+ offset = i;
+ break;
+ }
+ }
+ }
+ ret[n] = g_strndup(str, offset);
+ ret = g_renew(char *, ret, n + 2);
+ }
+ ret[n] = NULL;
+
+ return ret;
+}
+
+char *binary_to_hex(unsigned char *buffer, size_t size)
+{
+ static const char hex[] = "0123456789ABCDEF";
+ char *result = NULL;
+ int i;
+
+ if (buffer == NULL || size == 0)
+ return NULL;
+
+ result = g_malloc(3 * size);
+
+ for (i = 0; i < size; i++) {
+ result[i * 3 + 0] = hex[(buffer[i] >> 4) & 0xf];
+ result[i * 3 + 1] = hex[(buffer[i] >> 0) & 0xf];
+ result[i * 3 + 2] = i == size - 1 ? '\0' : ':';
+ }
+
+ return result;
+}