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