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