4 Copyright (C) 2000 Timo Sirainen
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.
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.
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
23 #include "special-vars.h"
29 #define ALIGN_RIGHT 0x01
30 #define ALIGN_CUT 0x02
31 #define ALIGN_PAD 0x04
33 #define isvarchar(c) \
34 (i_isalnum(c) || (c) == '_')
37 (i_isdigit(c) || (c) == '*' || (c) == '~' || (c) == '-')
39 static SPECIAL_HISTORY_FUNC history_func = NULL;
41 static char *get_argument(char **cmd, char **arglist)
45 int max, arg, argcount;
50 argcount = arglist == NULL ? 0 : strarray_length(arglist);
53 /* get all arguments */
54 } else if (**cmd == '~') {
55 /* get last argument */
56 arg = max = argcount-1;
58 if (i_isdigit(**cmd)) {
60 arg = max = (**cmd)-'0';
65 /* get more than one argument */
67 if (!i_isdigit(**cmd))
68 max = -1; /* get all the rest */
77 str = g_string_new(NULL);
78 while (arg >= 0 && arg < argcount && (arg <= max || max == -1)) {
79 g_string_append(str, arglist[arg]);
80 g_string_append_c(str, ' ');
83 if (str->len > 0) g_string_truncate(str, str->len-1);
86 g_string_free(str, FALSE);
90 static char *get_internal_setting(const char *key, int type, int *free_ret)
93 case SETTING_TYPE_BOOLEAN:
94 return settings_get_bool(key) ? "yes" : "no";
95 case SETTING_TYPE_INT:
97 return g_strdup_printf("%d", settings_get_int(key));
98 case SETTING_TYPE_STRING:
99 return (char *) settings_get_str(key);
105 static char *get_long_variable_value(const char *key, SERVER_REC *server,
106 void *item, int *free_ret)
115 func = expando_find_long(key);
117 current_expando = key;
118 return func(server, item, free_ret);
121 /* internal setting? */
122 type = settings_get_type(key);
124 return get_internal_setting(key, type, free_ret);
126 /* environment variable? */
134 static char *get_long_variable(char **cmd, SERVER_REC *server,
135 void *item, int *free_ret, int getname)
137 char *start, *var, *ret;
139 /* get variable name */
141 while (isvarchar((*cmd)[1])) (*cmd)++;
143 var = g_strndup(start, (int) (*cmd-start)+1);
148 ret = get_long_variable_value(var, server, item, free_ret);
153 /* return the value of the variable found from `cmd'.
154 if 'getname' is TRUE, return the name of the variable instead it's value */
155 static char *get_variable(char **cmd, SERVER_REC *server, void *item,
156 char **arglist, int *free_ret, int *arg_used,
164 if (arg_used != NULL) *arg_used = TRUE;
165 return getname ? g_strdup_printf("%c", **cmd) :
166 get_argument(cmd, arglist);
169 if (i_isalpha(**cmd) && isvarchar((*cmd)[1])) {
170 /* long variable name.. */
171 return get_long_variable(cmd, server, item, free_ret, getname);
174 /* single character variable. */
177 return g_strdup_printf("%c", **cmd);
180 func = expando_find_char(**cmd);
186 str[0] = **cmd; str[1] = '\0';
187 current_expando = str;
188 return func(server, item, free_ret);
192 static char *get_history(char **cmd, void *item, int *free_ret)
194 char *start, *text, *ret;
196 /* get variable name */
198 while (**cmd != '\0' && **cmd != '!') (*cmd)++;
200 if (history_func == NULL)
203 text = g_strndup(start, (int) (*cmd-start));
204 ret = history_func(text, item, free_ret);
208 if (**cmd == '\0') (*cmd)--;
212 static char *get_special_value(char **cmd, SERVER_REC *server, void *item,
213 char **arglist, int *free_ret, int *arg_used,
216 char command, *value, *p;
219 if ((flags & PARSE_FLAG_ONLY_ARGS) && !isarg(**cmd)) {
221 return g_strdup_printf("$%c", **cmd);
225 /* find text from command history */
226 if (flags & PARSE_FLAG_GETNAME)
229 return get_history(cmd, item, free_ret);
233 if (**cmd == '#' || **cmd == '@') {
235 if ((*cmd)[1] != '\0')
239 char *temp_cmd = "*";
241 if (flags & PARSE_FLAG_GETNAME)
245 return get_argument(&temp_cmd, arglist);
249 value = get_variable(cmd, server, item, arglist, free_ret,
250 arg_used, flags & PARSE_FLAG_GETNAME);
252 if (flags & PARSE_FLAG_GETNAME)
255 if (command == '#') {
256 /* number of words */
257 if (value == NULL || *value == '\0') {
258 if (value != NULL && *free_ret) {
266 for (p = value; *p != '\0'; p++) {
267 if (*p == ' ' && (p[1] != ' ' && p[1] != '\0'))
270 if (*free_ret) g_free(value);
273 return g_strdup_printf("%d", len);
276 if (command == '@') {
277 /* number of characters */
278 if (value == NULL) return "0";
281 if (*free_ret) g_free(value);
284 return g_strdup_printf("%d", len);
290 /* get alignment arguments (inside the []) */
291 static int get_alignment_args(char **data, int *align, int *flags, char *pad)
296 *flags = ALIGN_CUT|ALIGN_PAD;
299 /* '!' = don't cut, '-' = right padding */
301 while (*str != '\0' && *str != ']' && !i_isdigit(*str)) {
303 *flags &= ~ALIGN_CUT;
304 else if (*str == '-')
305 *flags |= ALIGN_RIGHT;
306 else if (*str == '.')
307 *flags &= ~ALIGN_PAD;
310 if (!i_isdigit(*str))
311 return FALSE; /* expecting number */
313 /* get the alignment size */
314 while (i_isdigit(*str)) {
315 *align = (*align) * 10 + (*str-'0');
319 /* get the pad character */
320 while (*str != '\0' && *str != ']') {
325 if (*str++ != ']') return FALSE;
331 /* return the aligned text */
332 static char *get_alignment(const char *text, int align, int flags, char pad)
337 g_return_val_if_fail(text != NULL, NULL);
339 str = g_string_new(text);
342 if ((flags & ALIGN_CUT) && align > 0 && str->len > align)
343 g_string_truncate(str, align);
345 /* add pad characters */
346 if (flags & ALIGN_PAD) {
347 while (str->len < align) {
348 if (flags & ALIGN_RIGHT)
349 g_string_prepend_c(str, pad);
351 g_string_append_c(str, pad);
356 g_string_free(str, FALSE);
360 /* Parse and expand text after '$' character. return value has to be
361 g_free()'d if `free_ret' is TRUE. */
362 char *parse_special(char **cmd, SERVER_REC *server, void *item,
363 char **arglist, int *free_ret, int *arg_used, int flags)
365 static char **nested_orig_cmd = NULL; /* FIXME: KLUDGE! */
366 char command, *value;
369 int align, align_flags;
372 int brackets, nest_free;
378 command = **cmd; (*cmd)++;
382 if (!get_alignment_args(cmd, &align, &align_flags,
383 &align_pad) || **cmd == '\0') {
393 nest_free = FALSE; nest_value = NULL;
396 int toplevel = nested_orig_cmd == NULL;
398 if (toplevel) nested_orig_cmd = cmd;
405 nest_value = parse_special(cmd, server, item, arglist,
406 &nest_free, arg_used,
410 while ((*nested_orig_cmd)[1] != '\0') {
411 (*nested_orig_cmd)++;
412 if (**nested_orig_cmd == ')')
417 if (toplevel) nested_orig_cmd = NULL;
423 /* special value is inside {...} (foo${test}bar -> fooXXXbar) */
428 value = get_special_value(cmd, server, item, arglist,
429 free_ret, arg_used, flags);
431 g_error("parse_special() : buffer overflow!");
433 if (value != NULL && *value != '\0' && (flags & PARSE_FLAG_ISSET_ANY))
437 while (**cmd != '}' && (*cmd)[1] != '\0')
441 if (nest_free) g_free(nest_value);
443 if (command == '[' && (flags & PARSE_FLAG_GETNAME) == 0) {
447 if (value == NULL) return "";
449 p = get_alignment(value, align, align_flags, align_pad);
450 if (*free_ret) g_free(value);
459 static void gstring_append_escaped(GString *str, const char *text, int flags)
461 char esc[4], *escpos;
464 if (flags & PARSE_FLAG_ESCAPE_VARS)
466 if (flags & PARSE_FLAG_ESCAPE_THEME) {
472 g_string_append(str, text);
477 while (*text != '\0') {
478 for (escpos = esc; *escpos != '\0'; escpos++) {
479 if (*text == *escpos) {
480 g_string_append_c(str, '%');
484 g_string_append_c(str, *text);
489 /* parse the whole string. $ and \ chars are replaced */
490 char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
491 const char *data, int *arg_used, int flags)
493 char code, **arglist, *ret;
497 g_return_val_if_fail(cmd != NULL, NULL);
498 g_return_val_if_fail(data != NULL, NULL);
500 /* create the argument list */
501 arglist = g_strsplit(data, " ", -1);
503 if (arg_used != NULL) *arg_used = FALSE;
505 str = g_string_new(NULL);
506 while (*cmd != '\0') {
509 g_string_append_c(str, ';');
511 chr = expand_escape(&cmd);
512 g_string_append_c(str, chr != -1 ? chr : *cmd);
515 } else if (code == '$') {
518 ret = parse_special((char **) &cmd, server, item,
519 arglist, &need_free, arg_used,
522 gstring_append_escaped(str, ret, flags);
523 if (need_free) g_free(ret);
527 if (*cmd == '\\' || *cmd == '$')
530 g_string_append_c(str, *cmd);
538 g_string_free(str, FALSE);
542 #define is_split_char(str, start) \
543 ((str)[0] == ';' && ((start) == (str) || \
544 ((str)[-1] != '\\' && (str)[-1] != '$')))
546 /* execute the commands in string - commands can be split with ';' */
547 void eval_special_string(const char *cmd, const char *data,
548 SERVER_REC *server, void *item)
550 const char *cmdchars;
551 char *orig, *str, *start, *ret;
552 int arg_used, arg_used_ever;
556 arg_used_ever = FALSE;
557 cmdchars = settings_get_str("cmdchars");
559 /* get a list of all the commands to run */
560 orig = start = str = g_strdup(cmd);
562 if (is_split_char(str, start)) {
564 while (*str == ' ') str++;
565 } else if (*str != '\0') {
570 ret = parse_special_string(start, server, item,
573 if (arg_used) arg_used_ever = TRUE;
575 if (strchr(cmdchars, *ret) == NULL) {
576 /* no command char - let's put it there.. */
579 ret = g_strdup_printf("%c%s", *cmdchars, old);
582 commands = g_slist_append(commands, ret);
585 } while (*start != '\0');
587 /* run the command, if no arguments were ever used, append all of them
588 after each command */
589 while (commands != NULL) {
590 ret = commands->data;
592 if (!arg_used_ever && *data != '\0') {
595 ret = g_strconcat(old, " ", data, NULL);
601 signal_emit("send command", 3, ret, server, item);
603 if (server != NULL && !server_unref(server)) {
604 /* the server was destroyed */
609 /* FIXME: window item would need reference counting as well,
610 eg. "/EVAL win close;say hello" wouldn't work now.. */
613 commands = g_slist_remove(commands, commands->data);
618 void special_history_func_set(SPECIAL_HISTORY_FUNC func)
623 static void update_signals_hash(GHashTable **hash, int *signals)
629 *hash = g_hash_table_new((GHashFunc) g_direct_hash,
630 (GCompareFunc) g_direct_equal);
633 while (*signals != -1) {
634 signal_id = GINT_TO_POINTER(*signals);
635 arg_type = GPOINTER_TO_INT(g_hash_table_lookup(*hash, signal_id));
636 if (arg_type != 0 && arg_type != signals[1]) {
637 /* same signal is used for different purposes ..
638 not sure if this should ever happen, but change
639 the argument type to none so it will at least
641 arg_type = EXPANDO_ARG_NONE;
644 if (arg_type == 0) arg_type = signals[1];
645 g_hash_table_insert(*hash, signal_id,
646 GINT_TO_POINTER(arg_type));
651 static void get_signal_hash(void *signal_id, void *arg_type, int **pos)
653 (*pos)[0] = GPOINTER_TO_INT(signal_id);
654 (*pos)[1] = GPOINTER_TO_INT(arg_type);
658 static int *get_signals_list(GHashTable *hash)
663 /* no expandos in text - never needs updating */
667 pos = signals = g_new(int, g_hash_table_size(hash)*2 + 1);
668 g_hash_table_foreach(hash, (GHFunc) get_signal_hash, &pos);
671 g_hash_table_destroy(hash);
677 #define TASK_UNBIND 2
678 #define TASK_GET_SIGNALS 3
680 static int *special_vars_signals_task(const char *text, int funccount,
681 SIGNAL_FUNC *funcs, int task)
685 int need_free, *expando_signals;
688 while (*text != '\0') {
689 if (*text == '\\' && text[1] != '\0') {
692 } else if (*text == '$' && text[1] != '\0') {
695 expando = parse_special((char **) &text, NULL, NULL,
696 NULL, &need_free, NULL,
703 expando_bind(expando, funccount, funcs);
706 expando_unbind(expando, funccount, funcs);
708 case TASK_GET_SIGNALS:
709 expando_signals = expando_get_signals(expando);
710 if (expando_signals != NULL) {
711 update_signals_hash(&signals,
713 g_free(expando_signals);
717 if (need_free) g_free(expando);
724 if (task == TASK_GET_SIGNALS)
725 return get_signals_list(signals);
730 void special_vars_add_signals(const char *text,
731 int funccount, SIGNAL_FUNC *funcs)
733 special_vars_signals_task(text, funccount, funcs, TASK_BIND);
736 void special_vars_remove_signals(const char *text,
737 int funccount, SIGNAL_FUNC *funcs)
739 special_vars_signals_task(text, funccount, funcs, TASK_UNBIND);
742 int *special_vars_get_signals(const char *text)
744 return special_vars_signals_task(text, 0, NULL, TASK_GET_SIGNALS);