summaryrefslogtreecommitdiff
path: root/src/core/special-vars.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/special-vars.c')
-rw-r--r--src/core/special-vars.c635
1 files changed, 635 insertions, 0 deletions
diff --git a/src/core/special-vars.c b/src/core/special-vars.c
new file mode 100644
index 00000000..26788a65
--- /dev/null
+++ b/src/core/special-vars.c
@@ -0,0 +1,635 @@
+/*
+ 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 "settings.h"
+#include "misc.h"
+#include "irssi-version.h"
+
+#include <sys/utsname.h>
+
+#define ALIGN_RIGHT 0x01
+#define ALIGN_CUT 0x02
+
+static EXPANDO_FUNC char_expandos[256];
+static GHashTable *expandos;
+static time_t client_start_time;
+static SPECIAL_HISTORY_FUNC history_func;
+
+static char *get_argument(char **cmd, char **arglist)
+{
+ GString *str;
+ char *ret;
+ int max, arg, argcount;
+
+ arg = 0;
+ max = -1;
+
+ argcount = strarray_length(arglist);
+
+ if (**cmd == '*') {
+ /* get all arguments */
+ } else if (**cmd == '~') {
+ /* get last argument */
+ arg = max = argcount-1;
+ } else {
+ if (isdigit(**cmd)) {
+ /* first argument */
+ arg = max = (**cmd)-'0';
+ (*cmd)++;
+ }
+
+ if (**cmd == '-') {
+ /* get more than one argument */
+ (*cmd)++;
+ if (!isdigit(**cmd))
+ max = -1; /* get all the rest */
+ else {
+ max = (**cmd)-'0';
+ (*cmd)++;
+ }
+ }
+ (*cmd)--;
+ }
+
+ str = g_string_new(NULL);
+ while (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, void *server, void *item, int *free_ret)
+{
+ EXPANDO_FUNC func;
+ char *ret;
+ int type;
+
+ *free_ret = FALSE;
+
+ /* expando? */
+ func = g_hash_table_lookup(expandos, key);
+ if (func != NULL)
+ 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) {
+ *free_ret = TRUE;
+ return ret;
+ }
+
+ return NULL;
+}
+
+static char *get_long_variable(char **cmd, void *server, void *item, int *free_ret)
+{
+ char *start, *var, *ret;
+
+ /* get variable name */
+ start = *cmd;
+ while (isalnum((*cmd)[1])) (*cmd)++;
+
+ var = g_strndup(start, (int) (*cmd-start)+1);
+ ret = get_long_variable_value(var, server, item, free_ret);
+ g_free(var);
+ return ret;
+}
+
+/* return the value of the variable found from `cmd' */
+static char *get_variable(char **cmd, void *server, void *item, char **arglist, int *free_ret, int *arg_used)
+{
+ if (isdigit(**cmd) || **cmd == '*' || **cmd == '-' || **cmd == '~') {
+ /* argument */
+ *free_ret = TRUE;
+ if (arg_used != NULL) *arg_used = TRUE;
+ return get_argument(cmd, arglist);
+ }
+
+ if (isalpha(**cmd) && isalnum((*cmd)[1])) {
+ /* long variable name.. */
+ return get_long_variable(cmd, server, item, free_ret);
+ }
+
+ /* single character variable. */
+ *free_ret = FALSE;
+ return char_expandos[(int) **cmd] == NULL ? NULL :
+ char_expandos[(int) **cmd](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)+1);
+ ret = history_func(text, item, free_ret);
+ g_free(text);
+ }
+
+ if (**cmd == '\0') (*cmd)--;
+ return ret;
+}
+
+static char *get_special_value(char **cmd, void *server, void *item, char **arglist, int *free_ret, int *arg_used)
+{
+ char command, *value, *p;
+ int len;
+
+ if (**cmd == '!') {
+ /* find text from command history */
+ 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 = "*";
+
+ *free_ret = TRUE;
+ return get_argument(&temp_cmd, arglist);
+ }
+ }
+
+ value = get_variable(cmd, server, item, arglist, free_ret, arg_used);
+
+ 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;
+ *pad = ' ';
+
+ /* '!' = don't cut, '-' = right padding */
+ str = *data;
+ while (*str != '\0' && *str != ']' && !isdigit(*str)) {
+ if (*str == '!')
+ *flags &= ~ALIGN_CUT;
+ else if (*str == '-')
+ *flags |= ALIGN_RIGHT;
+ str++;
+ }
+ if (!isdigit(*str))
+ return FALSE; /* expecting number */
+
+ /* get the alignment size */
+ while (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 */
+ 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, void *server, void *item, char **arglist, int *free_ret, int *arg_used)
+{
+ 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;
+
+ 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 == '(') {
+ /* 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);
+ }
+
+ 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) */
+ (*cmd)++;
+ brackets = TRUE;
+ }
+
+ value = get_special_value(cmd, server, item, arglist, free_ret, arg_used);
+ if (**cmd == '\0')
+ g_error("parse_special() : buffer overflow!");
+
+ if (brackets) {
+ while (**cmd != '}' && (*cmd)[1] != '\0')
+ (*cmd)++;
+ }
+
+ if (nest_free) g_free(nest_value);
+
+ if (command == '[') {
+ /* 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;
+}
+
+/* parse the whole string. $ and \ chars are replaced */
+char *parse_special_string(const char *cmd, void *server, void *item, const char *data, int *arg_used)
+{
+ char code, **arglist, *ret;
+ GString *str;
+ int need_free;
+
+ 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 == '\\'){
+ g_string_append_c(str, *cmd);
+ code = 0;
+ } else if (code == '$') {
+ char *ret;
+
+ ret = parse_special((char **) &cmd, server, item, arglist, &need_free, arg_used);
+ if (ret != NULL) {
+ g_string_append(str, ret);
+ 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;
+}
+
+/* execute the commands in string - commands can be split with ';' */
+void eval_special_string(const char *cmd, const char *data, void *server, void *item)
+{
+ const char *cmdchars;
+ char *orig, *str, *start, *ret;
+ int arg_used;
+
+ cmdchars = settings_get_str("cmdchars");
+ orig = start = str = g_strdup(cmd);
+ do {
+ if (*str == ';' && (start == str || (str[-1] != '\\' && str[-1] != '$')))
+ *str++ = '\0';
+ else if (*str != '\0') {
+ str++;
+ continue;
+ }
+
+ ret = parse_special_string(start, server, item, data, &arg_used);
+ 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);
+ }
+ if (!arg_used && *data != '\0') {
+ /* append the string with all the arguments */
+ char *old = ret;
+
+ ret = g_strconcat(old, " ", data, NULL);
+ g_free(old);
+ }
+ signal_emit("send command", 3, ret, server, item);
+ g_free(ret);
+
+ start = str;
+ } while (*start != '\0');
+
+ g_free(orig);
+}
+
+/* Create expando - overrides any existing ones. */
+void expando_create(const char *key, EXPANDO_FUNC func)
+{
+ gpointer origkey, origvalue;
+
+ g_return_if_fail(key != NULL || *key == '\0');
+ g_return_if_fail(func != NULL);
+
+ if (key[1] == '\0') {
+ /* single character expando */
+ char_expandos[(int) *key] = func;
+ return;
+ }
+
+ if (g_hash_table_lookup_extended(expandos, key, &origkey, &origvalue)) {
+ g_free(origkey);
+ g_hash_table_remove(expandos, key);
+ }
+ g_hash_table_insert(expandos, g_strdup(key), func);
+}
+
+/* Destroy expando */
+void expando_destroy(const char *key, EXPANDO_FUNC func)
+{
+ gpointer origkey, origvalue;
+
+ g_return_if_fail(key != NULL || *key == '\0');
+ g_return_if_fail(func != NULL);
+
+ if (key[1] == '\0') {
+ /* single character expando */
+ if (char_expandos[(int) *key] == func)
+ char_expandos[(int) *key] = NULL;
+ return;
+ }
+
+ if (g_hash_table_lookup_extended(expandos, key, &origkey, &origvalue)) {
+ g_free(origkey);
+ g_hash_table_remove(expandos, key);
+ }
+}
+
+void special_history_func_set(SPECIAL_HISTORY_FUNC func)
+{
+ history_func = func;
+}
+
+/* time client was started, $time() format */
+static char *expando_clientstarted(void *server, void *item, int *free_ret)
+{
+ *free_ret = TRUE;
+ return g_strdup_printf("%ld", (long) client_start_time);
+}
+
+/* client version text string */
+static char *expando_version(void *server, void *item, int *free_ret)
+{
+ return IRSSI_VERSION;
+}
+
+/* current value of CMDCHARS */
+static char *expando_cmdchars(void *server, void *item, int *free_ret)
+{
+ return (char *) settings_get_str("cmdchars");
+}
+
+/* client release date (numeric version string) */
+static char *expando_releasedate(void *server, void *item, int *free_ret)
+{
+ return IRSSI_VERSION_DATE;
+}
+
+/* current working directory */
+static char *expando_workdir(void *server, void *item, int *free_ret)
+{
+ *free_ret = TRUE;
+ return g_get_current_dir();
+}
+
+/* time of day (hh:mm) */
+static char *expando_time(void *server, void *item, int *free_ret)
+{
+ time_t now = time(NULL);
+ struct tm *tm;
+
+ tm = localtime(&now);
+ *free_ret = TRUE;
+ return g_strdup_printf("%02d:%02d", tm->tm_hour, tm->tm_min);
+}
+
+/* a literal '$' */
+static char *expando_dollar(void *server, void *item, int *free_ret)
+{
+ return "$";
+}
+
+/* system name */
+static char *expando_sysname(void *server, void *item, int *free_ret)
+{
+ struct utsname un;
+
+ if (uname(&un) != 0)
+ return NULL;
+
+ *free_ret = TRUE;
+ return g_strdup(un.sysname);
+
+}
+
+/* system release */
+static char *expando_sysrelease(void *server, void *item, int *free_ret)
+{
+ struct utsname un;
+
+ if (uname(&un) != 0)
+ return NULL;
+
+ *free_ret = TRUE;
+ return g_strdup(un.release);
+
+}
+
+void special_vars_init(void)
+{
+ client_start_time = time(NULL);
+
+ memset(char_expandos, 0, sizeof(char_expandos));
+ expandos = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal);
+ history_func = NULL;
+
+ char_expandos['F'] = expando_clientstarted;
+ char_expandos['J'] = expando_version;
+ char_expandos['K'] = expando_cmdchars;
+ char_expandos['V'] = expando_releasedate;
+ char_expandos['W'] = expando_workdir;
+ char_expandos['Z'] = expando_time;
+ char_expandos['$'] = expando_dollar;
+
+ expando_create("sysname", expando_sysname);
+ expando_create("sysrelease", expando_sysrelease);
+}
+
+void special_vars_deinit(void)
+{
+ expando_destroy("sysname", expando_sysname);
+ expando_destroy("sysrelease", expando_sysrelease);
+
+ g_hash_table_destroy(expandos);
+}