irssi/src/core/misc.c
Timo Sirainen bd6fe052bc Added time, size and level setting types. Breaks some settings - I'll add
automatic converter to these settings later. Meanwhile you CVS users can
fix your config files yourself :)

Time settings allow using "days", "hours", "minutes", "seconds" and
"milliseconds" or several of their abbreviations. For example "5d 4h
5msecs".

Size settings allow using "gbytes", "mbytes", "kbytes" and "bytes" or their
abbrevations. For example "5MB".

Level settings are currently handled pretty much the way they were before.


git-svn-id: http://svn.irssi.org/repos/irssi/trunk@3080 dbcabf3a-b0e7-0310-adc4-f8d773084564
2002-12-28 17:54:13 +00:00

932 lines
18 KiB
C

/*
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 <errno.h>
#ifdef HAVE_REGEX_H
# include <regex.h>
#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)
{
const char *home;
if (*path == '~' && (*(path+1) == '/' || *(path+1) == '\0')) {
home = g_get_home_dir();
if (home == NULL)
home = ".";
return g_strconcat(home, path+1, NULL);
} else {
return g_strdup(path);
}
}
int 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;
}
/* string -> uoff_t */
uoff_t str_to_uofft(const char *str)
{
uoff_t ret;
ret = 0;
while (*str != '\0') {
ret = ret*10 + (*str - '0');
str++;
}
return ret;
}
/* convert all low-ascii (<32) to ^<A..> 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 (!i_isxdigit((*data)[1]) || !i_isxdigit((*data)[2]))
return -1;
digit[0] = (*data)[1];
digit[1] = (*data)[2];
digit[2] = '\0';
*data += 2;
return strtol(digit, NULL, 16);
case 'c':
/* 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);
}
}
/* Escape all '"', "'" and '\' chars with '\' */
char *escape_string(const char *str)
{
char *ret, *p;
p = ret = g_malloc(strlen(str)*2+1);
while (*str != '\0') {
if (*str == '"' || *str == '\'' || *str == '\\')
*p++ = '\\';
*p++ = *str++;
}
*p = '\0';
return ret;
}
int nearest_power(int num)
{
int n = 1;
while (n < num) n <<= 1;
return n;
}
int parse_time_interval(const char *time, int *msecs)
{
const char *desc;
int number, len;
*msecs = 0;
/* max. return value is about 1.6 years */
number = 0;
for (;;) {
if (i_isdigit(*time)) {
number = number*10 + (*time - '0');
time++;
continue;
}
/* skip punctuation */
while (*time != '\0' && i_ispunct(*time))
time++;
/* get description */
for (len = 0, desc = time; i_isalpha(*time); time++)
len++;
if (len == 0) {
if (number == 0) {
/* "0" - allow it */
return TRUE;
}
*msecs += number; /* assume seconds */
return FALSE;
}
if (g_strncasecmp(desc, "weeks", len) == 0)
*msecs += number * 1000*3600*7;
if (g_strncasecmp(desc, "days", len) == 0)
*msecs += number * 1000*3600;
else if (g_strncasecmp(desc, "minutes", len) == 0 ||
g_strncasecmp(desc, "mins", len) == 0)
*msecs += number * 1000*60;
else if (g_strncasecmp(desc, "seconds", len) == 0 ||
g_strncasecmp(desc, "secs", len) == 0)
*msecs += number * 1000;
else if (g_strncasecmp(desc, "milliseconds", len) == 0 ||
g_strncasecmp(desc, "millisecs", len) == 0 ||
g_strncasecmp(desc, "mseconds", len) == 0 ||
g_strncasecmp(desc, "msecs", len) == 0)
*msecs += number;
/* skip punctuation */
while (*time != '\0' && i_ispunct(*time))
time++;
if (*time == '\0')
break;
number = 0;
}
return TRUE;
}
int parse_size(const char *size, int *bytes)
{
const char *desc;
int number, len;
*bytes = 0;
/* max. return value is about 1.6 years */
number = 0;
while (*size != '\0') {
if (i_isdigit(*size)) {
number = number*10 + (*size - '0');
size++;
continue;
}
/* skip punctuation */
while (*size != '\0' && i_ispunct(*size))
size++;
/* get description */
for (len = 0, desc = size; i_isalpha(*size); size++)
len++;
if (len == 0) {
if (number == 0) {
/* "0" - allow it */
return TRUE;
}
*bytes += number*1024; /* assume kilobytes */
return FALSE;
}
if (g_strncasecmp(desc, "gbytes", len) == 0)
*bytes += number * 1024*1024*1024;
if (g_strncasecmp(desc, "mbytes", len) == 0)
*bytes += number * 1024*1024;
if (g_strncasecmp(desc, "kbytes", len) == 0)
*bytes += number * 1024;
if (g_strncasecmp(desc, "bytes", len) == 0)
*bytes += number;
/* skip punctuation */
while (*size != '\0' && i_ispunct(*size))
size++;
}
return TRUE;
}