Merge Irssi 0.8.16-rc1
[silc.git] / apps / irssi / src / core / misc.c
1 /*
2  misc.c : irssi
3
4     Copyright (C) 1999 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "module.h"
22 #include "misc.h"
23
24 #ifdef HAVE_REGEX_H
25 #  include <regex.h>
26 #endif
27
28 typedef struct {
29         int condition;
30         GInputFunction function;
31         void *data;
32 } IRSSI_INPUT_REC;
33
34 static int irssi_io_invoke(GIOChannel *source, GIOCondition condition,
35                            void *data)
36 {
37         IRSSI_INPUT_REC *rec = data;
38         int icond = 0;
39
40         if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
41                 /* error, we have to call the function.. */
42                 if (rec->condition & G_IO_IN)
43                         icond |= G_INPUT_READ;
44                 else
45                         icond |= G_INPUT_WRITE;
46         }
47
48         if (condition & (G_IO_IN | G_IO_PRI))
49                 icond |= G_INPUT_READ;
50         if (condition & G_IO_OUT)
51                 icond |= G_INPUT_WRITE;
52
53         if (rec->condition & icond)
54                 rec->function(rec->data, source, icond);
55
56         return TRUE;
57 }
58
59 int g_input_add_full(GIOChannel *source, int priority, int condition,
60                      GInputFunction function, void *data)
61 {
62         IRSSI_INPUT_REC *rec;
63         unsigned int result;
64         GIOCondition cond;
65
66         rec = g_new(IRSSI_INPUT_REC, 1);
67         rec->condition = condition;
68         rec->function = function;
69         rec->data = data;
70
71         cond = (GIOCondition) (G_IO_ERR|G_IO_HUP|G_IO_NVAL);
72         if (condition & G_INPUT_READ)
73                 cond |= G_IO_IN|G_IO_PRI;
74         if (condition & G_INPUT_WRITE)
75                 cond |= G_IO_OUT;
76
77         result = g_io_add_watch_full(source, priority, cond,
78                                      irssi_io_invoke, rec, g_free);
79
80         return result;
81 }
82
83 int g_input_add(GIOChannel *source, int condition,
84                 GInputFunction function, void *data)
85 {
86         return g_input_add_full(source, G_PRIORITY_DEFAULT, condition,
87                                 function, data);
88 }
89
90 /* easy way to bypass glib polling of io channel internal buffer */
91 int g_input_add_poll(int fd, int priority, int condition,
92                      GInputFunction function, void *data)
93 {
94         GIOChannel *source = g_io_channel_unix_new(fd);
95         int ret = g_input_add_full(source, priority, condition, function, data);
96         g_io_channel_unref(source);
97         return ret;
98 }
99
100 int g_timeval_cmp(const GTimeVal *tv1, const GTimeVal *tv2)
101 {
102         if (tv1->tv_sec < tv2->tv_sec)
103                 return -1;
104         if (tv1->tv_sec > tv2->tv_sec)
105                 return 1;
106
107         return tv1->tv_usec < tv2->tv_usec ? -1 :
108                 tv1->tv_usec > tv2->tv_usec ? 1 : 0;
109 }
110
111 long get_timeval_diff(const GTimeVal *tv1, const GTimeVal *tv2)
112 {
113         long secs, usecs;
114
115         secs = tv1->tv_sec - tv2->tv_sec;
116         usecs = tv1->tv_usec - tv2->tv_usec;
117         if (usecs < 0) {
118                 usecs += 1000000;
119                 secs--;
120         }
121         usecs = usecs/1000 + secs * 1000;
122
123         return usecs;
124 }
125
126 int find_substr(const char *list, const char *item)
127 {
128         const char *ptr;
129
130         g_return_val_if_fail(list != NULL, FALSE);
131         g_return_val_if_fail(item != NULL, FALSE);
132
133         if (*item == '\0')
134                 return FALSE;
135
136         for (;;) {
137                 while (i_isspace(*list)) list++;
138                 if (*list == '\0') break;
139
140                 ptr = strchr(list, ' ');
141                 if (ptr == NULL) ptr = list+strlen(list);
142
143                 if (g_strncasecmp(list, item, ptr-list) == 0 &&
144                     item[ptr-list] == '\0')
145                         return TRUE;
146
147                 list = ptr;
148         }
149
150         return FALSE;
151 }
152
153 int strarray_length(char **array)
154 {
155         int len;
156
157         g_return_val_if_fail(array != NULL, 0);
158
159         len = 0;
160         while (*array) {
161                 len++;
162                 array++;
163         }
164         return len;
165 }
166
167 int strarray_find(char **array, const char *item)
168 {
169         char **tmp;
170         int index;
171
172         g_return_val_if_fail(array != NULL, 0);
173         g_return_val_if_fail(item != NULL, 0);
174
175         index = 0;
176         for (tmp = array; *tmp != NULL; tmp++, index++) {
177                 if (g_strcasecmp(*tmp, item) == 0)
178                         return index;
179         }
180
181         return -1;
182 }
183
184 GSList *gslist_find_string(GSList *list, const char *key)
185 {
186         for (list = list; list != NULL; list = list->next)
187                 if (strcmp(list->data, key) == 0) return list;
188
189         return NULL;
190 }
191
192 GSList *gslist_find_icase_string(GSList *list, const char *key)
193 {
194         for (list = list; list != NULL; list = list->next)
195                 if (g_strcasecmp(list->data, key) == 0) return list;
196
197         return NULL;
198 }
199
200 void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data)
201 {
202         void *ret;
203
204         while (list != NULL) {
205                 ret = func(list->data, (void *) data);
206                 if (ret != NULL) return ret;
207
208                 list = list->next;
209         }
210
211         return NULL;
212 }
213
214 /* `list' contains pointer to structure with a char* to string. */
215 char *gslistptr_to_string(GSList *list, int offset, const char *delimiter)
216 {
217         GString *str;
218         char **data, *ret;
219
220         str = g_string_new(NULL);
221         while (list != NULL) {
222                 data = G_STRUCT_MEMBER_P(list->data, offset);
223
224                 if (str->len != 0) g_string_append(str, delimiter);
225                 g_string_append(str, *data);
226                 list = list->next;
227         }
228
229         ret = str->str;
230         g_string_free(str, FALSE);
231         return ret;
232 }
233
234 /* `list' contains char* */
235 char *gslist_to_string(GSList *list, const char *delimiter)
236 {
237         GString *str;
238         char *ret;
239
240         str = g_string_new(NULL);
241         while (list != NULL) {
242                 if (str->len != 0) g_string_append(str, delimiter);
243                 g_string_append(str, list->data);
244
245                 list = list->next;
246         }
247
248         ret = str->str;
249         g_string_free(str, FALSE);
250         return ret;
251 }
252
253 void hash_save_key(char *key, void *value, GSList **list)
254 {
255         *list = g_slist_append(*list, key);
256 }
257
258 /* save all keys in hash table to linked list - you shouldn't remove any
259    items while using this list, use g_slist_free() after you're done with it */
260 GSList *hashtable_get_keys(GHashTable *hash)
261 {
262         GSList *list;
263
264         list = NULL;
265         g_hash_table_foreach(hash, (GHFunc) hash_save_key, &list);
266         return list;
267 }
268
269 GList *glist_find_string(GList *list, const char *key)
270 {
271         for (list = list; list != NULL; list = list->next)
272                 if (strcmp(list->data, key) == 0) return list;
273
274         return NULL;
275 }
276
277 GList *glist_find_icase_string(GList *list, const char *key)
278 {
279         for (list = list; list != NULL; list = list->next)
280                 if (g_strcasecmp(list->data, key) == 0) return list;
281
282         return NULL;
283 }
284
285 char *stristr(const char *data, const char *key)
286 {
287         const char *max;
288         int keylen, datalen, pos;
289
290         keylen = strlen(key);
291         datalen = strlen(data);
292
293         if (keylen > datalen)
294                 return NULL;
295         if (keylen == 0)
296                 return (char *) data;
297
298         max = data+datalen-keylen;
299         pos = 0;
300         while (data <= max) {
301                 if (key[pos] == '\0')
302                         return (char *) data;
303
304                 if (i_toupper(data[pos]) == i_toupper(key[pos]))
305                         pos++;
306                 else {
307                         data++;
308                         pos = 0;
309                 }
310         }
311
312         return NULL;
313 }
314
315 #define isbound(c) \
316         ((unsigned char) (c) < 128 && \
317         (i_isspace(c) || i_ispunct(c)))
318
319 static char *strstr_full_case(const char *data, const char *key, int icase)
320 {
321         const char *start, *max;
322         int keylen, datalen, pos, match;
323
324         keylen = strlen(key);
325         datalen = strlen(data);
326
327         if (keylen > datalen)
328                 return NULL;
329         if (keylen == 0)
330                 return (char *) data;
331
332         max = data+datalen-keylen;
333         start = data; pos = 0;
334         while (data <= max) {
335                 if (key[pos] == '\0') {
336                         if (data[pos] != '\0' && !isbound(data[pos])) {
337                                 data++;
338                                 pos = 0;
339                                 continue;
340                         }
341                         return (char *) data;
342                 }
343
344                 match = icase ? (i_toupper(data[pos]) == i_toupper(key[pos])) :
345                                  data[pos] == key[pos];
346
347                 if (match && (pos != 0 || data == start || isbound(data[-1])))
348                         pos++;
349                 else {
350                         data++;
351                         pos = 0;
352                 }
353         }
354
355         return NULL;
356 }
357
358 char *strstr_full(const char *data, const char *key)
359 {
360         return strstr_full_case(data, key, FALSE);
361 }
362
363 char *stristr_full(const char *data, const char *key)
364 {
365         return strstr_full_case(data, key, TRUE);
366 }
367
368 int regexp_match(const char *str, const char *regexp)
369 {
370 #ifdef HAVE_REGEX_H
371         regex_t preg;
372         int ret;
373
374         if (regcomp(&preg, regexp, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0)
375                 return 0;
376
377         ret = regexec(&preg, str, 0, NULL, 0);
378         regfree(&preg);
379
380         return ret == 0;
381 #else
382         return FALSE;
383 #endif
384 }
385
386 /* Create the directory and all it's parent directories */
387 int mkpath(const char *path, int mode)
388 {
389         struct stat statbuf;
390         const char *p;
391         char *dir;
392
393         g_return_val_if_fail(path != NULL, -1);
394
395         p = g_path_skip_root((char *) path);
396         if (p == NULL) {
397                 /* not a full path, maybe not what we wanted
398                    but continue anyway.. */
399                 p = path;
400         }
401         for (;;) {
402                 if (*p != G_DIR_SEPARATOR && *p != '\0') {
403                         p++;
404                         continue;
405                 }
406
407                 dir = g_strndup(path, (int) (p-path));
408                 if (stat(dir, &statbuf) != 0) {
409 #ifndef WIN32
410                         if (mkdir(dir, mode) == -1)
411 #else
412                         if (_mkdir(dir) == -1)
413 #endif
414                         {
415                                 g_free(dir);
416                                 return -1;
417                         }
418                 }
419                 g_free(dir);
420
421                 if (*p++ == '\0')
422                         break;
423         }
424
425         return 0;
426 }
427
428 /* convert ~/ to $HOME */
429 char *convert_home(const char *path)
430 {
431         const char *home;
432
433         if (*path == '~' && (*(path+1) == '/' || *(path+1) == '\0')) {
434                 home = g_get_home_dir();
435                 if (home == NULL)
436                         home = ".";
437
438                 return g_strconcat(home, path+1, NULL);
439         } else {
440                 return g_strdup(path);
441         }
442 }
443
444 int g_istr_equal(gconstpointer v, gconstpointer v2)
445 {
446         return g_strcasecmp((const char *) v, (const char *) v2) == 0;
447 }
448
449 int g_istr_cmp(gconstpointer v, gconstpointer v2)
450 {
451         return g_strcasecmp((const char *) v, (const char *) v2);
452 }
453
454 /* a char* hash function from ASU */
455 unsigned int g_istr_hash(gconstpointer v)
456 {
457         const char *s = (const char *) v;
458         unsigned int h = 0, g;
459
460         while (*s != '\0') {
461                 h = (h << 4) + i_toupper(*s);
462                 if ((g = h & 0xf0000000UL)) {
463                         h = h ^ (g >> 24);
464                         h = h ^ g;
465                 }
466                 s++;
467         }
468
469         return h /* % M */;
470 }
471
472 /* Find `mask' from `data', you can use * and ? wildcards. */
473 int match_wildcards(const char *cmask, const char *data)
474 {
475         char *mask, *newmask, *p1, *p2;
476         int ret;
477
478         newmask = mask = g_strdup(cmask);
479         for (; *mask != '\0' && *data != '\0'; mask++) {
480                 if (*mask != '*') {
481                         if (*mask != '?' && i_toupper(*mask) != i_toupper(*data))
482                                 break;
483
484                         data++;
485                         continue;
486                 }
487
488                 while (*mask == '?' || *mask == '*') mask++;
489                 if (*mask == '\0') {
490                         data += strlen(data);
491                         break;
492                 }
493
494                 p1 = strchr(mask, '*');
495                 p2 = strchr(mask, '?');
496                 if (p1 == NULL || (p2 < p1 && p2 != NULL)) p1 = p2;
497
498                 if (p1 != NULL) *p1 = '\0';
499
500                 data = stristr(data, mask);
501                 if (data == NULL) break;
502
503                 data += strlen(mask);
504                 mask += strlen(mask)-1;
505
506                 if (p1 != NULL) *p1 = p1 == p2 ? '?' : '*';
507         }
508
509         while (*mask == '*') mask++;
510
511         ret = data != NULL && *data == '\0' && *mask == '\0';
512         g_free(newmask);
513
514         return ret;
515 }
516
517 /* Return TRUE if all characters in `str' are numbers.
518    Stop when `end_char' is found from string. */
519 int is_numeric(const char *str, char end_char)
520 {
521         g_return_val_if_fail(str != NULL, FALSE);
522
523         if (*str == '\0' || *str == end_char)
524                 return FALSE;
525
526         while (*str != '\0' && *str != end_char) {
527                 if (!i_isdigit(*str)) return FALSE;
528                 str++;
529         }
530
531         return TRUE;
532 }
533
534 /* replace all `from' chars in string to `to' chars. returns `str' */
535 char *replace_chars(char *str, char from, char to)
536 {
537         char *p;
538
539         for (p = str; *p != '\0'; p++) {
540                 if (*p == from) *p = to;
541         }
542         return str;
543 }
544
545 int octal2dec(int octal)
546 {
547         int dec, n;
548
549         dec = 0; n = 1;
550         while (octal != 0) {
551                 dec += n*(octal%10);
552                 octal /= 10; n *= 8;
553         }
554
555         return dec;
556 }
557
558 int dec2octal(int decimal)
559 {
560         int octal, pos;
561
562         octal = 0; pos = 0;
563         while (decimal > 0) {
564                 octal += (decimal & 7)*(pos == 0 ? 1 : pos);
565                 decimal /= 8;
566                 pos += 10;
567         }
568
569         return octal;
570 }
571
572 /* string -> uoff_t */
573 uoff_t str_to_uofft(const char *str)
574 {
575         uoff_t ret;
576
577         ret = 0;
578         while (*str != '\0') {
579                 ret = ret*10 + (*str - '0');
580                 str++;
581         }
582
583         return ret;
584 }
585
586 /* convert all low-ascii (<32) to ^<A..> combinations */
587 char *show_lowascii(const char *str)
588 {
589         char *ret, *p;
590
591         ret = p = g_malloc(strlen(str)*2+1);
592         while (*str != '\0') {
593                 if ((unsigned char) *str >= 32)
594                         *p++ = *str;
595                 else {
596                         *p++ = '^';
597                         *p++ = *str + 'A'-1;
598                 }
599                 str++;
600         }
601         *p = '\0';
602
603         return ret;
604 }
605
606 /* Get time in human readable form with localtime() + asctime() */
607 char *my_asctime(time_t t)
608 {
609         struct tm *tm;
610         char *str;
611         int len;
612
613         tm = localtime(&t);
614         str = g_strdup(asctime(tm));
615
616         len = strlen(str);
617         if (len > 0) str[len-1] = '\0';
618         return str;
619 }
620
621 /* Returns number of columns needed to print items.
622    save_column_widths is filled with length of each column. */
623 int get_max_column_count(GSList *items, COLUMN_LEN_FUNC len_func,
624                          int max_width, int max_columns,
625                          int item_extra, int item_min_size,
626                          int **save_column_widths, int *rows)
627 {
628         GSList *tmp;
629         int **columns, *columns_width, *columns_rows;
630         int item_pos, items_count;
631         int ret, len, max_len, n, col;
632
633         items_count = g_slist_length(items);
634         if (items_count == 0) {
635                 *save_column_widths = NULL;
636                 *rows = 0;
637                 return 0;
638         }
639
640         len = max_width/(item_extra+item_min_size);
641         if (len <= 0) len = 1;
642         if (max_columns <= 0 || len < max_columns)
643                 max_columns = len;
644
645         columns = g_new0(int *, max_columns);
646         columns_width = g_new0(int, max_columns);
647         columns_rows = g_new0(int, max_columns);
648
649         for (n = 1; n < max_columns; n++) {
650                 columns[n] = g_new0(int, n+1);
651                 columns_rows[n] = items_count <= n+1 ? 1 :
652                         (items_count+n)/(n+1);
653         }
654
655         /* for each possible column count, save the column widths and
656            find the biggest column count that fits to screen. */
657         item_pos = 0; max_len = 0;
658         for (tmp = items; tmp != NULL; tmp = tmp->next) {
659                 len = item_extra+len_func(tmp->data);
660                 if (max_len < len)
661                         max_len = len;
662
663                 for (n = 1; n < max_columns; n++) {
664                         if (columns_width[n] > max_width)
665                                 continue; /* too wide */
666
667                         col = item_pos/columns_rows[n];
668                         if (columns[n][col] < len) {
669                                 columns_width[n] += len-columns[n][col];
670                                 columns[n][col] = len;
671                         }
672                 }
673
674                 item_pos++;
675         }
676
677         for (n = max_columns-1; n >= 1; n--) {
678                 if (columns_width[n] <= max_width &&
679                     columns[n][n] > 0)
680                         break;
681         }
682         ret = n+1;
683
684         *save_column_widths = g_new(int, ret);
685         if (ret == 1) {
686                 **save_column_widths = max_len;
687                 *rows = 1;
688         } else {
689                 memcpy(*save_column_widths, columns[ret-1], sizeof(int)*ret);
690                 *rows = columns_rows[ret-1];
691         }
692
693         for (n = 1; n < max_columns; n++)
694                 g_free(columns[n]);
695         g_free(columns_width);
696         g_free(columns_rows);
697         g_free(columns);
698
699         return ret;
700 }
701
702 /* Return a column sorted copy of a list. */
703 GSList *columns_sort_list(GSList *list, int rows)
704 {
705         GSList *tmp, *sorted;
706         int row, skip;
707
708         if (list == NULL || rows == 0)
709                 return list;
710
711         sorted = NULL;
712
713         for (row = 0; row < rows; row++) {
714                 tmp = g_slist_nth(list, row);
715                 skip = 1;
716                 for (; tmp != NULL; tmp = tmp->next) {
717                         if (--skip == 0) {
718                                 skip = rows;
719                                 sorted = g_slist_append(sorted, tmp->data);
720                         }
721                 }
722         }
723
724         g_return_val_if_fail(g_slist_length(sorted) ==
725                              g_slist_length(list), sorted);
726         return sorted;
727 }
728
729 /* Expand escape string, the first character in data should be the
730    one after '\'. Returns the expanded character or -1 if error. */
731 int expand_escape(const char **data)
732 {
733         char digit[4];
734
735         switch (**data) {
736         case 't':
737                 return '\t';
738         case 'r':
739                 return '\r';
740         case 'n':
741                 return '\n';
742         case 'e':
743                 return 27; /* ESC */
744
745         case 'x':
746                 /* hex digit */
747                 if (!i_isxdigit((*data)[1]) || !i_isxdigit((*data)[2]))
748                         return -1;
749
750                 digit[0] = (*data)[1];
751                 digit[1] = (*data)[2];
752                 digit[2] = '\0';
753                 *data += 2;
754                 return strtol(digit, NULL, 16);
755         case 'c':
756                 /* control character (\cA = ^A) */
757                 (*data)++;
758                 return i_toupper(**data) - 64;
759         case '0': case '1': case '2': case '3':
760         case '4': case '5': case '6': case '7':
761                 /* octal */
762                 digit[1] = digit[2] = digit[3] = '\0';
763                 digit[0] = (*data)[0];
764                 if ((*data)[1] >= '0' && (*data)[1] <= '7') {
765                         ++*data;
766                         digit[1] = **data;
767                         if ((*data)[1] >= '0' && (*data)[1] <= '7') {
768                                 ++*data;
769                                 digit[2] = **data;
770                         }
771                 }
772                 return strtol(digit, NULL, 8);
773         default:
774                 return -1;
775         }
776 }
777
778 /* Escape all '"', "'" and '\' chars with '\' */
779 char *escape_string(const char *str)
780 {
781         char *ret, *p;
782
783         p = ret = g_malloc(strlen(str)*2+1);
784         while (*str != '\0') {
785                 if (*str == '"' || *str == '\'' || *str == '\\')
786                         *p++ = '\\';
787                 *p++ = *str++;
788         }
789         *p = '\0';
790
791         return ret;
792 }
793
794 int strocpy(char *dest, const char *src, size_t dstsize)
795 {
796         if (dstsize == 0)
797                 return -1;
798
799         while (*src != '\0' && dstsize > 1) {
800                 *dest++ = *src++;
801                 dstsize--;
802         }
803
804         *dest++ = '\0';
805         return *src == '\0' ? 0 : -1;
806 }
807
808 int nearest_power(int num)
809 {
810         int n = 1;
811
812         while (n < num) n <<= 1;
813         return n;
814 }
815
816 int parse_time_interval(const char *time, int *msecs)
817 {
818         const char *desc;
819         int number, sign, len, ret, digits;
820
821         *msecs = 0;
822
823         /* max. return value is around 24 days */
824         number = 0; sign = 1; ret = TRUE; digits = FALSE;
825         while (i_isspace(*time))
826                 time++;
827         if (*time == '-') {
828                 sign = -sign;
829                 time++;
830                 while (i_isspace(*time))
831                         time++;
832         }
833         for (;;) {
834                 if (i_isdigit(*time)) {
835                         number = number*10 + (*time - '0');
836                         time++;
837                         digits = TRUE;
838                         continue;
839                 }
840
841                 if (!digits)
842                         return FALSE;
843
844                 /* skip punctuation */
845                 while (*time != '\0' && i_ispunct(*time) && *time != '-')
846                         time++;
847
848                 /* get description */
849                 for (len = 0, desc = time; i_isalpha(*time); time++)
850                         len++;
851
852                 while (i_isspace(*time))
853                         time++;
854
855                 if (len == 0) {
856                         if (*time != '\0')
857                                 return FALSE;
858                         *msecs += number * 1000; /* assume seconds */
859                         *msecs *= sign;
860                         return TRUE;
861                 }
862
863                 if (g_ascii_strncasecmp(desc, "days", len) == 0) {
864                         if (number > 24) {
865                                 /* would overflow */
866                                 return FALSE;
867                         }
868                         *msecs += number * 1000*3600*24;
869                 } else if (g_ascii_strncasecmp(desc, "hours", len) == 0)
870                         *msecs += number * 1000*3600;
871                 else if (g_ascii_strncasecmp(desc, "minutes", len) == 0 ||
872                          g_ascii_strncasecmp(desc, "mins", len) == 0)
873                         *msecs += number * 1000*60;
874                 else if (g_ascii_strncasecmp(desc, "seconds", len) == 0 ||
875                          g_ascii_strncasecmp(desc, "secs", len) == 0)
876                         *msecs += number * 1000;
877                 else if (g_ascii_strncasecmp(desc, "milliseconds", len) == 0 ||
878                          g_ascii_strncasecmp(desc, "millisecs", len) == 0 ||
879                          g_ascii_strncasecmp(desc, "mseconds", len) == 0 ||
880                          g_ascii_strncasecmp(desc, "msecs", len) == 0)
881                         *msecs += number;
882                 else {
883                         ret = FALSE;
884                 }
885
886                 /* skip punctuation */
887                 while (*time != '\0' && i_ispunct(*time) && *time != '-')
888                         time++;
889
890                 if (*time == '\0')
891                         break;
892
893                 number = 0;
894                 digits = FALSE;
895         }
896
897         *msecs *= sign;
898         return ret;
899 }
900
901 int parse_size(const char *size, int *bytes)
902 {
903         const char *desc;
904         int number, len;
905
906         *bytes = 0;
907
908         /* max. return value is about 1.6 years */
909         number = 0;
910         while (*size != '\0') {
911                 if (i_isdigit(*size)) {
912                         number = number*10 + (*size - '0');
913                         size++;
914                         continue;
915                 }
916
917                 /* skip punctuation */
918                 while (*size != '\0' && i_ispunct(*size))
919                         size++;
920
921                 /* get description */
922                 for (len = 0, desc = size; i_isalpha(*size); size++)
923                         len++;
924
925                 if (len == 0) {
926                         if (number == 0) {
927                                 /* "0" - allow it */
928                                 return TRUE;
929                         }
930
931                         *bytes += number*1024; /* assume kilobytes */
932                         return FALSE;
933                 }
934
935                 if (g_ascii_strncasecmp(desc, "gbytes", len) == 0)
936                         *bytes += number * 1024*1024*1024;
937                 if (g_ascii_strncasecmp(desc, "mbytes", len) == 0)
938                         *bytes += number * 1024*1024;
939                 if (g_ascii_strncasecmp(desc, "kbytes", len) == 0)
940                         *bytes += number * 1024;
941                 if (g_ascii_strncasecmp(desc, "bytes", len) == 0)
942                         *bytes += number;
943
944                 /* skip punctuation */
945                 while (*size != '\0' && i_ispunct(*size))
946                         size++;
947         }
948
949         return TRUE;
950 }
951
952 char *ascii_strup(char *str)
953 {
954         char *s;
955
956         for (s = str; *s; s++)
957                 *s = g_ascii_toupper (*s);
958         return str;
959 }
960
961 char *ascii_strdown(char *str)
962 {
963         char *s;
964
965         for (s = str; *s; s++)
966                 *s = g_ascii_tolower (*s);
967         return str;
968 }