+++ /dev/null
-/*
- special-vars.c : irssi
-
- Copyright (C) 2000 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 "signals.h"
-#include "special-vars.h"
-#include "expandos.h"
-#include "settings.h"
-#include "servers.h"
-#include "misc.h"
-
-#define ALIGN_RIGHT 0x01
-#define ALIGN_CUT 0x02
-#define ALIGN_PAD 0x04
-
-#define isvarchar(c) \
- (i_isalnum(c) || (c) == '_')
-
-#define isarg(c) \
- (i_isdigit(c) || (c) == '*' || (c) == '~' || (c) == '-')
-
-static SPECIAL_HISTORY_FUNC history_func = NULL;
-
-static char *get_argument(char **cmd, char **arglist)
-{
- GString *str;
- char *ret;
- int max, arg, argcount;
-
- arg = 0;
- max = -1;
-
- argcount = arglist == NULL ? 0 : strarray_length(arglist);
-
- if (**cmd == '*') {
- /* get all arguments */
- } else if (**cmd == '~') {
- /* get last argument */
- arg = max = argcount-1;
- } else {
- if (i_isdigit(**cmd)) {
- /* first argument */
- arg = max = (**cmd)-'0';
- (*cmd)++;
- }
-
- if (**cmd == '-') {
- /* get more than one argument */
- (*cmd)++;
- if (!i_isdigit(**cmd))
- max = -1; /* get all the rest */
- else {
- max = (**cmd)-'0';
- (*cmd)++;
- }
- }
- (*cmd)--;
- }
-
- str = g_string_new(NULL);
- while (arg >= 0 && arg < argcount && (arg <= max || max == -1)) {
- g_string_append(str, arglist[arg]);
- g_string_append_c(str, ' ');
- arg++;
- }
- if (str->len > 0) g_string_truncate(str, str->len-1);
-
- ret = str->str;
- g_string_free(str, FALSE);
- return ret;
-}
-
-static char *get_internal_setting(const char *key, int type, int *free_ret)
-{
- switch (type) {
- case SETTING_TYPE_BOOLEAN:
- return settings_get_bool(key) ? "yes" : "no";
- case SETTING_TYPE_INT:
- *free_ret = TRUE;
- return g_strdup_printf("%d", settings_get_int(key));
- case SETTING_TYPE_STRING:
- return (char *) settings_get_str(key);
- }
-
- return NULL;
-}
-
-static char *get_long_variable_value(const char *key, SERVER_REC *server,
- void *item, int *free_ret)
-{
- EXPANDO_FUNC func;
- const char *ret;
- int type;
-
- *free_ret = FALSE;
-
- /* expando? */
- func = expando_find_long(key);
- if (func != NULL) {
- current_expando = key;
- return func(server, item, free_ret);
- }
-
- /* internal setting? */
- type = settings_get_type(key);
- if (type != -1)
- return get_internal_setting(key, type, free_ret);
-
- /* environment variable? */
- ret = g_getenv(key);
- if (ret != NULL)
- return (char *) ret;
-
- return NULL;
-}
-
-static char *get_long_variable(char **cmd, SERVER_REC *server,
- void *item, int *free_ret, int getname)
-{
- char *start, *var, *ret;
-
- /* get variable name */
- start = *cmd;
- while (isvarchar((*cmd)[1])) (*cmd)++;
-
- var = g_strndup(start, (int) (*cmd-start)+1);
- if (getname) {
- *free_ret = TRUE;
- return var;
- }
- ret = get_long_variable_value(var, server, item, free_ret);
- g_free(var);
- return ret;
-}
-
-/* return the value of the variable found from `cmd'.
- if 'getname' is TRUE, return the name of the variable instead it's value */
-static char *get_variable(char **cmd, SERVER_REC *server, void *item,
- char **arglist, int *free_ret, int *arg_used,
- int getname)
-{
- EXPANDO_FUNC func;
-
- if (isarg(**cmd)) {
- /* argument */
- *free_ret = TRUE;
- if (arg_used != NULL) *arg_used = TRUE;
- return getname ? g_strdup_printf("%c", **cmd) :
- get_argument(cmd, arglist);
- }
-
- if (i_isalpha(**cmd) && isvarchar((*cmd)[1])) {
- /* long variable name.. */
- return get_long_variable(cmd, server, item, free_ret, getname);
- }
-
- /* single character variable. */
- if (getname) {
- *free_ret = TRUE;
- return g_strdup_printf("%c", **cmd);
- }
- *free_ret = FALSE;
- func = expando_find_char(**cmd);
- if (func == NULL)
- return NULL;
- else {
- char str[2];
-
- str[0] = **cmd; str[1] = '\0';
- current_expando = str;
- return func(server, item, free_ret);
- }
-}
-
-static char *get_history(char **cmd, void *item, int *free_ret)
-{
- char *start, *text, *ret;
-
- /* get variable name */
- start = ++(*cmd);
- while (**cmd != '\0' && **cmd != '!') (*cmd)++;
-
- if (history_func == NULL)
- ret = NULL;
- else {
- text = g_strndup(start, (int) (*cmd-start));
- ret = history_func(text, item, free_ret);
- g_free(text);
- }
-
- if (**cmd == '\0') (*cmd)--;
- return ret;
-}
-
-static char *get_special_value(char **cmd, SERVER_REC *server, void *item,
- char **arglist, int *free_ret, int *arg_used,
- int flags)
-{
- char command, *value, *p;
- int len;
-
- if ((flags & PARSE_FLAG_ONLY_ARGS) && !isarg(**cmd)) {
- *free_ret = TRUE;
- return g_strdup_printf("$%c", **cmd);
- }
-
- if (**cmd == '!') {
- /* find text from command history */
- if (flags & PARSE_FLAG_GETNAME)
- return "!";
-
- return get_history(cmd, item, free_ret);
- }
-
- command = 0;
- if (**cmd == '#' || **cmd == '@') {
- command = **cmd;
- if ((*cmd)[1] != '\0')
- (*cmd)++;
- else {
- /* default to $* */
- char *temp_cmd = "*";
-
- if (flags & PARSE_FLAG_GETNAME)
- return "*";
-
- *free_ret = TRUE;
- return get_argument(&temp_cmd, arglist);
- }
- }
-
- value = get_variable(cmd, server, item, arglist, free_ret,
- arg_used, flags & PARSE_FLAG_GETNAME);
-
- if (flags & PARSE_FLAG_GETNAME)
- return value;
-
- if (command == '#') {
- /* number of words */
- if (value == NULL || *value == '\0') {
- if (value != NULL && *free_ret) {
- g_free(value);
- *free_ret = FALSE;
- }
- return "0";
- }
-
- len = 1;
- for (p = value; *p != '\0'; p++) {
- if (*p == ' ' && (p[1] != ' ' && p[1] != '\0'))
- len++;
- }
- if (*free_ret) g_free(value);
-
- *free_ret = TRUE;
- return g_strdup_printf("%d", len);
- }
-
- if (command == '@') {
- /* number of characters */
- if (value == NULL) return "0";
-
- len = strlen(value);
- if (*free_ret) g_free(value);
-
- *free_ret = TRUE;
- return g_strdup_printf("%d", len);
- }
-
- return value;
-}
-
-/* get alignment arguments (inside the []) */
-static int get_alignment_args(char **data, int *align, int *flags, char *pad)
-{
- char *str;
-
- *align = 0;
- *flags = ALIGN_CUT|ALIGN_PAD;
- *pad = ' ';
-
- /* '!' = don't cut, '-' = right padding */
- str = *data;
- while (*str != '\0' && *str != ']' && !i_isdigit(*str)) {
- if (*str == '!')
- *flags &= ~ALIGN_CUT;
- else if (*str == '-')
- *flags |= ALIGN_RIGHT;
- else if (*str == '.')
- *flags &= ~ALIGN_PAD;
- str++;
- }
- if (!i_isdigit(*str))
- return FALSE; /* expecting number */
-
- /* get the alignment size */
- while (i_isdigit(*str)) {
- *align = (*align) * 10 + (*str-'0');
- str++;
- }
-
- /* get the pad character */
- while (*str != '\0' && *str != ']') {
- *pad = *str;
- str++;
- }
-
- if (*str++ != ']') return FALSE;
-
- *data = str;
- return TRUE;
-}
-
-/* return the aligned text */
-static char *get_alignment(const char *text, int align, int flags, char pad)
-{
- GString *str;
- char *ret;
-
- g_return_val_if_fail(text != NULL, NULL);
-
- str = g_string_new(text);
-
- /* cut */
- if ((flags & ALIGN_CUT) && align > 0 && str->len > align)
- g_string_truncate(str, align);
-
- /* add pad characters */
- if (flags & ALIGN_PAD) {
- while (str->len < align) {
- if (flags & ALIGN_RIGHT)
- g_string_prepend_c(str, pad);
- else
- g_string_append_c(str, pad);
- }
- }
-
- ret = str->str;
- g_string_free(str, FALSE);
- return ret;
-}
-
-/* Parse and expand text after '$' character. return value has to be
- g_free()'d if `free_ret' is TRUE. */
-char *parse_special(char **cmd, SERVER_REC *server, void *item,
- char **arglist, int *free_ret, int *arg_used, int flags)
-{
- static char **nested_orig_cmd = NULL; /* FIXME: KLUDGE! */
- char command, *value;
-
- char align_pad;
- int align, align_flags;
-
- char *nest_value;
- int brackets, nest_free;
-
- *free_ret = FALSE;
- if (**cmd == '\0')
- return NULL;
-
- command = **cmd; (*cmd)++;
- switch (command) {
- case '[':
- /* alignment */
- if (!get_alignment_args(cmd, &align, &align_flags,
- &align_pad) || **cmd == '\0') {
- (*cmd)--;
- return NULL;
- }
- break;
- default:
- command = 0;
- (*cmd)--;
- }
-
- nest_free = FALSE; nest_value = NULL;
- if (**cmd == '(' && (*cmd)[1] != '\0') {
- /* subvariable */
- int toplevel = nested_orig_cmd == NULL;
-
- if (toplevel) nested_orig_cmd = cmd;
- (*cmd)++;
- if (**cmd != '$') {
- /* ... */
- nest_value = *cmd;
- } else {
- (*cmd)++;
- nest_value = parse_special(cmd, server, item, arglist,
- &nest_free, arg_used,
- flags);
- }
-
- if (nest_value == NULL || *nest_value == '\0')
- return NULL;
-
- while ((*nested_orig_cmd)[1] != '\0') {
- (*nested_orig_cmd)++;
- if (**nested_orig_cmd == ')')
- break;
- }
- cmd = &nest_value;
-
- if (toplevel) nested_orig_cmd = NULL;
- }
-
- if (**cmd != '{')
- brackets = FALSE;
- else {
- /* special value is inside {...} (foo${test}bar -> fooXXXbar) */
- if ((*cmd)[1] == '\0')
- return NULL;
- (*cmd)++;
- brackets = TRUE;
- }
-
- value = get_special_value(cmd, server, item, arglist,
- free_ret, arg_used, flags);
- if (**cmd == '\0')
- g_error("parse_special() : buffer overflow!");
-
- if (value != NULL && *value != '\0' && (flags & PARSE_FLAG_ISSET_ANY))
- *arg_used = TRUE;
-
- if (brackets) {
- while (**cmd != '}' && (*cmd)[1] != '\0')
- (*cmd)++;
- }
-
- if (nest_free) g_free(nest_value);
-
- if (command == '[' && (flags & PARSE_FLAG_GETNAME) == 0) {
- /* alignment */
- char *p;
-
- if (value == NULL) return "";
-
- p = get_alignment(value, align, align_flags, align_pad);
- if (*free_ret) g_free(value);
-
- *free_ret = TRUE;
- return p;
- }
-
- return value;
-}
-
-static void gstring_append_escaped(GString *str, const char *text, int flags)
-{
- char esc[4], *escpos;
-
- escpos = esc;
- if (flags & PARSE_FLAG_ESCAPE_VARS)
- *escpos++ = '%';
- if (flags & PARSE_FLAG_ESCAPE_THEME) {
- *escpos++ = '{';
- *escpos++ = '}';
- }
-
- if (escpos == esc) {
- g_string_append(str, text);
- return;
- }
-
- *escpos = '\0';
- while (*text != '\0') {
- for (escpos = esc; *escpos != '\0'; escpos++) {
- if (*text == *escpos) {
- g_string_append_c(str, '%');
- break;
- }
- }
- g_string_append_c(str, *text);
- text++;
- }
-}
-
-/* parse the whole string. $ and \ chars are replaced */
-char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
- const char *data, int *arg_used, int flags)
-{
- char code, **arglist, *ret;
- GString *str;
- int need_free, chr;
-
- g_return_val_if_fail(cmd != NULL, NULL);
- g_return_val_if_fail(data != NULL, NULL);
-
- /* create the argument list */
- arglist = g_strsplit(data, " ", -1);
-
- if (arg_used != NULL) *arg_used = FALSE;
- code = 0;
- str = g_string_new(NULL);
- while (*cmd != '\0') {
- if (code == '\\') {
- if (*cmd == ';')
- g_string_append_c(str, ';');
- else {
- chr = expand_escape(&cmd);
- g_string_append_c(str, chr != -1 ? chr : *cmd);
- }
- code = 0;
- } else if (code == '$') {
- char *ret;
-
- ret = parse_special((char **) &cmd, server, item,
- arglist, &need_free, arg_used,
- flags);
- if (ret != NULL) {
- gstring_append_escaped(str, ret, flags);
- if (need_free) g_free(ret);
- }
- code = 0;
- } else {
- if (*cmd == '\\' || *cmd == '$')
- code = *cmd;
- else
- g_string_append_c(str, *cmd);
- }
-
- cmd++;
- }
- g_strfreev(arglist);
-
- ret = str->str;
- g_string_free(str, FALSE);
- return ret;
-}
-
-#define is_split_char(str, start) \
- ((str)[0] == ';' && ((start) == (str) || \
- ((str)[-1] != '\\' && (str)[-1] != '$')))
-
-/* execute the commands in string - commands can be split with ';' */
-void eval_special_string(const char *cmd, const char *data,
- SERVER_REC *server, void *item)
-{
- const char *cmdchars;
- char *orig, *str, *start, *ret;
- int arg_used, arg_used_ever;
- GSList *commands;
-
- commands = NULL;
- arg_used_ever = FALSE;
- cmdchars = settings_get_str("cmdchars");
-
- /* get a list of all the commands to run */
- orig = start = str = g_strdup(cmd);
- do {
- if (is_split_char(str, start)) {
- *str++ = '\0';
- while (*str == ' ') str++;
- } else if (*str != '\0') {
- str++;
- continue;
- }
-
- ret = parse_special_string(start, server, item,
- data, &arg_used, 0);
- if (*ret != '\0') {
- if (arg_used) arg_used_ever = TRUE;
-
- if (strchr(cmdchars, *ret) == NULL) {
- /* no command char - let's put it there.. */
- char *old = ret;
-
- ret = g_strdup_printf("%c%s", *cmdchars, old);
- g_free(old);
- }
- commands = g_slist_append(commands, ret);
- }
- start = str;
- } while (*start != '\0');
-
- /* run the command, if no arguments were ever used, append all of them
- after each command */
- while (commands != NULL) {
- ret = commands->data;
-
- if (!arg_used_ever && *data != '\0') {
- char *old = ret;
-
- ret = g_strconcat(old, " ", data, NULL);
- g_free(old);
- }
-
- if (server != NULL)
- server_ref(server);
- signal_emit("send command", 3, ret, server, item);
-
- if (server != NULL && !server_unref(server)) {
- /* the server was destroyed */
- server = NULL;
- item = NULL;
- }
-
- /* FIXME: window item would need reference counting as well,
- eg. "/EVAL win close;say hello" wouldn't work now.. */
-
- g_free(ret);
- commands = g_slist_remove(commands, commands->data);
- }
- g_free(orig);
-}
-
-void special_history_func_set(SPECIAL_HISTORY_FUNC func)
-{
- history_func = func;
-}
-
-static void update_signals_hash(GHashTable **hash, int *signals)
-{
- void *signal_id;
- int arg_type;
-
- if (*hash == NULL) {
- *hash = g_hash_table_new((GHashFunc) g_direct_hash,
- (GCompareFunc) g_direct_equal);
- }
-
- while (*signals != -1) {
- signal_id = GINT_TO_POINTER(*signals);
- arg_type = GPOINTER_TO_INT(g_hash_table_lookup(*hash, signal_id));
- if (arg_type != 0 && arg_type != signals[1]) {
- /* same signal is used for different purposes ..
- not sure if this should ever happen, but change
- the argument type to none so it will at least
- work. */
- arg_type = EXPANDO_ARG_NONE;
- }
-
- if (arg_type == 0) arg_type = signals[1];
- g_hash_table_insert(*hash, signal_id,
- GINT_TO_POINTER(arg_type));
- signals += 2;
- }
-}
-
-static void get_signal_hash(void *signal_id, void *arg_type, int **pos)
-{
- (*pos)[0] = GPOINTER_TO_INT(signal_id);
- (*pos)[1] = GPOINTER_TO_INT(arg_type);
- (*pos) += 2;
-}
-
-static int *get_signals_list(GHashTable *hash)
-{
- int *signals, *pos;
-
- if (hash == NULL) {
- /* no expandos in text - never needs updating */
- return NULL;
- }
-
- pos = signals = g_new(int, g_hash_table_size(hash)*2 + 1);
- g_hash_table_foreach(hash, (GHFunc) get_signal_hash, &pos);
- *pos = -1;
-
- g_hash_table_destroy(hash);
- return signals;
-
-}
-
-#define TASK_BIND 1
-#define TASK_UNBIND 2
-#define TASK_GET_SIGNALS 3
-
-static int *special_vars_signals_task(const char *text, int funccount,
- SIGNAL_FUNC *funcs, int task)
-{
- GHashTable *signals;
- char *expando;
- int need_free, *expando_signals;
-
- signals = NULL;
- while (*text != '\0') {
- if (*text == '\\' && text[1] != '\0') {
- /* escape */
- text += 2;
- } else if (*text == '$' && text[1] != '\0') {
- /* expando */
- text++;
- expando = parse_special((char **) &text, NULL, NULL,
- NULL, &need_free, NULL,
- PARSE_FLAG_GETNAME);
- if (expando == NULL)
- continue;
-
- switch (task) {
- case TASK_BIND:
- expando_bind(expando, funccount, funcs);
- break;
- case TASK_UNBIND:
- expando_unbind(expando, funccount, funcs);
- break;
- case TASK_GET_SIGNALS:
- expando_signals = expando_get_signals(expando);
- if (expando_signals != NULL) {
- update_signals_hash(&signals,
- expando_signals);
- g_free(expando_signals);
- }
- break;
- }
- if (need_free) g_free(expando);
- } else {
- /* just a char */
- text++;
- }
- }
-
- if (task == TASK_GET_SIGNALS)
- return get_signals_list(signals);
-
- return NULL;
-}
-
-void special_vars_add_signals(const char *text,
- int funccount, SIGNAL_FUNC *funcs)
-{
- special_vars_signals_task(text, funccount, funcs, TASK_BIND);
-}
-
-void special_vars_remove_signals(const char *text,
- int funccount, SIGNAL_FUNC *funcs)
-{
- special_vars_signals_task(text, funccount, funcs, TASK_UNBIND);
-}
-
-int *special_vars_get_signals(const char *text)
-{
- return special_vars_signals_task(text, 0, NULL, TASK_GET_SIGNALS);
-}