/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "module.h" #include "misc.h" #include "pidwait.h" #include #ifdef HAVE_REGEX_H # include #endif 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 |= G_INPUT_READ; else icond |= G_INPUT_WRITE; } if (condition & (G_IO_IN | G_IO_PRI)) icond |= G_INPUT_READ; if (condition & G_IO_OUT) icond |= G_INPUT_WRITE; if (rec->condition & icond) rec->function(rec->data, source, icond); return TRUE; } int g_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 & G_INPUT_READ) cond |= G_IO_IN|G_IO_PRI; if (condition & G_INPUT_WRITE) cond |= G_IO_OUT; result = g_io_add_watch_full(source, priority, cond, irssi_io_invoke, rec, g_free); return result; } int g_input_add(GIOChannel *source, int condition, GInputFunction function, void *data) { return g_input_add_full(source, G_PRIORITY_DEFAULT, condition, function, data); } 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; } 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_strncasecmp(list, item, ptr-list) == 0 && item[ptr-list] == '\0') return TRUE; list = ptr; } return FALSE; } int strarray_length(char **array) { int len; g_return_val_if_fail(array != NULL, 0); len = 0; while (*array) { len++; array++; } return len; } int strarray_find(char **array, const char *item) { char **tmp; int index; g_return_val_if_fail(array != NULL, 0); g_return_val_if_fail(item != NULL, 0); index = 0; for (tmp = array; *tmp != NULL; tmp++, index++) { if (g_strcasecmp(*tmp, item) == 0) return index; } return -1; } int execute(const char *cmd) { char **args; #ifndef WIN32 int pid; #endif g_return_val_if_fail(cmd != NULL, -1); #ifndef WIN32 pid = fork(); if (pid == -1) return FALSE; if (pid != 0) { pidwait_add(pid); return pid; } args = g_strsplit(cmd, " ", -1); execvp(args[0], args); g_strfreev(args); _exit(99); return -1; #else args = g_strsplit(cmd, " ", -1); _spawnvp(_P_DETACH, args[0], args); g_strfreev(args); return 0; #endif } GSList *gslist_find_string(GSList *list, const char *key) { for (list = list; list != NULL; list = list->next) if (strcmp(list->data, key) == 0) return list; return NULL; } GSList *gslist_find_icase_string(GSList *list, const char *key) { for (list = list; list != NULL; list = list->next) if (g_strcasecmp(list->data, key) == 0) return list; return NULL; } void *gslist_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; } /* `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 *gslist_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; } void hash_save_key(char *key, void *value, GSList **list) { *list = g_slist_append(*list, key); } /* save all keys in hash table to linked list - you shouldn't remove any items while using this list, use g_slist_free() after you're done with it */ GSList *hashtable_get_keys(GHashTable *hash) { GSList *list; list = NULL; g_hash_table_foreach(hash, (GHFunc) hash_save_key, &list); return list; } GList *glist_find_string(GList *list, const char *key) { for (list = list; list != NULL; list = list->next) if (strcmp(list->data, key) == 0) return list; return NULL; } GList *glist_find_icase_string(GList *list, const char *key) { for (list = list; list != NULL; list = list->next) if (g_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))) 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); } int regexp_match(const char *str, const char *regexp) { #ifdef HAVE_REGEX_H regex_t preg; int ret; if (regcomp(&preg, regexp, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0) return 0; ret = regexec(&preg, str, 0, NULL, 0); regfree(&preg); return ret == 0; #else return FALSE; #endif } /* Create the directory and all it's parent directories */ int mkpath(const char *path, int mode) { struct stat statbuf; const char *p; char *dir; g_return_val_if_fail(path != NULL, -1); p = g_path_skip_root((char *) path); if (p == NULL) { /* not a full path, maybe not what we wanted but continue anyway.. */ p = path; } for (;;) { if (*p != G_DIR_SEPARATOR && *p != '\0') { p++; continue; } dir = g_strndup(path, (int) (p-path)); if (stat(dir, &statbuf) != 0) { #ifndef WIN32 if (mkdir(dir, mode) == -1) #else if (_mkdir(dir) == -1) #endif { g_free(dir); return -1; } } g_free(dir); if (*p++ == '\0') break; } return 0; } /* convert ~/ to $HOME */ char *convert_home(const char *path) { return *path == '~' && (*(path+1) == '/' || *(path+1) == '\0') ? g_strconcat(g_get_home_dir(), path+1, NULL) : g_strdup(path); } int g_istr_equal(gconstpointer v, gconstpointer v2) { return g_strcasecmp((const char *) v, (const char *) v2) == 0; } int g_istr_cmp(gconstpointer v, gconstpointer v2) { return g_strcasecmp((const char *) v, (const char *) v2); } /* a char* hash function from ASU */ unsigned int g_istr_hash(gconstpointer v) { const char *s = (const char *) v; unsigned int h = 0, g; while (*s != '\0') { h = (h << 4) + i_toupper(*s); if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); h = h ^ g; } s++; } return h /* % M */; } /* 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; } /* convert all low-ascii (<32) to ^ combinations */ char *show_lowascii(const char *channel) { char *str, *p; str = p = g_malloc(strlen(channel)*2+1); while (*channel != '\0') { if ((unsigned char) *channel >= 32) *p++ = *channel; else { *p++ = '^'; *p++ = *channel + 'A'-1; } channel++; } *p = '\0'; return str; } /* 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); 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 'x': /* hex digit */ if (!isxdigit((*data)[1]) || !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': /* control character (\cA = ^A) */ (*data)++; return i_toupper(**data) - 64; default: if (!i_isdigit(**data)) return -1; /* octal */ digit[0] = (*data)[0]; digit[1] = (*data)[1]; digit[2] = (*data)[2]; digit[3] = '\0'; *data += 2; return strtol(digit, NULL, 8); } }