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"
28 #define ALIGN_RIGHT 0x01
29 #define ALIGN_CUT 0x02
30 #define ALIGN_PAD 0x04
32 #define isvarchar(c) \
33 (isalnum(c) || (c) == '_')
35 static SPECIAL_HISTORY_FUNC history_func = NULL;
37 static char *get_argument(char **cmd, char **arglist)
41 int max, arg, argcount;
46 argcount = strarray_length(arglist);
49 /* get all arguments */
50 } else if (**cmd == '~') {
51 /* get last argument */
52 arg = max = argcount-1;
56 arg = max = (**cmd)-'0';
61 /* get more than one argument */
64 max = -1; /* get all the rest */
73 str = g_string_new(NULL);
74 while (arg < argcount && (arg <= max || max == -1)) {
75 g_string_append(str, arglist[arg]);
76 g_string_append_c(str, ' ');
79 if (str->len > 0) g_string_truncate(str, str->len-1);
82 g_string_free(str, FALSE);
86 static char *get_internal_setting(const char *key, int type, int *free_ret)
89 case SETTING_TYPE_BOOLEAN:
90 return settings_get_bool(key) ? "yes" : "no";
91 case SETTING_TYPE_INT:
93 return g_strdup_printf("%d", settings_get_int(key));
94 case SETTING_TYPE_STRING:
95 return (char *) settings_get_str(key);
101 static char *get_long_variable_value(const char *key, SERVER_REC *server,
102 void *item, int *free_ret)
111 func = expando_find_long(key);
113 return func(server, item, free_ret);
115 /* internal setting? */
116 type = settings_get_type(key);
118 return get_internal_setting(key, type, free_ret);
120 /* environment variable? */
128 static char *get_long_variable(char **cmd, SERVER_REC *server,
129 void *item, int *free_ret, int getname)
131 char *start, *var, *ret;
133 /* get variable name */
135 while (isvarchar((*cmd)[1])) (*cmd)++;
137 var = g_strndup(start, (int) (*cmd-start)+1);
142 ret = get_long_variable_value(var, server, item, free_ret);
147 /* return the value of the variable found from `cmd'.
148 if 'getname' is TRUE, return the name of the variable instead it's value */
149 static char *get_variable(char **cmd, SERVER_REC *server, void *item,
150 char **arglist, int *free_ret, int *arg_used,
155 if (isdigit(**cmd) || **cmd == '*' || **cmd == '-' || **cmd == '~') {
158 if (arg_used != NULL) *arg_used = TRUE;
159 return getname ? g_strdup_printf("%c", **cmd) :
160 get_argument(cmd, arglist);
163 if (isalpha(**cmd) && isvarchar((*cmd)[1])) {
164 /* long variable name.. */
165 return get_long_variable(cmd, server, item, free_ret, getname);
168 /* single character variable. */
171 return g_strdup_printf("%c", **cmd);
174 func = expando_find_char(**cmd);
175 return func == NULL ? NULL : func(server, item, free_ret);
178 static char *get_history(char **cmd, void *item, int *free_ret)
180 char *start, *text, *ret;
182 /* get variable name */
184 while (**cmd != '\0' && **cmd != '!') (*cmd)++;
186 if (history_func == NULL)
189 text = g_strndup(start, (int) (*cmd-start)+1);
190 ret = history_func(text, item, free_ret);
194 if (**cmd == '\0') (*cmd)--;
198 static char *get_special_value(char **cmd, SERVER_REC *server, void *item,
199 char **arglist, int *free_ret, int *arg_used,
202 char command, *value, *p;
206 /* find text from command history */
207 if (flags & PARSE_FLAG_GETNAME)
210 return get_history(cmd, item, free_ret);
214 if (**cmd == '#' || **cmd == '@') {
216 if ((*cmd)[1] != '\0')
220 char *temp_cmd = "*";
222 if (flags & PARSE_FLAG_GETNAME)
226 return get_argument(&temp_cmd, arglist);
230 value = get_variable(cmd, server, item, arglist, free_ret,
231 arg_used, flags & PARSE_FLAG_GETNAME);
233 if (flags & PARSE_FLAG_GETNAME)
236 if (command == '#') {
237 /* number of words */
238 if (value == NULL || *value == '\0') {
239 if (value != NULL && *free_ret) {
247 for (p = value; *p != '\0'; p++) {
248 if (*p == ' ' && (p[1] != ' ' && p[1] != '\0'))
251 if (*free_ret) g_free(value);
254 return g_strdup_printf("%d", len);
257 if (command == '@') {
258 /* number of characters */
259 if (value == NULL) return "0";
262 if (*free_ret) g_free(value);
265 return g_strdup_printf("%d", len);
271 /* get alignment arguments (inside the []) */
272 static int get_alignment_args(char **data, int *align, int *flags, char *pad)
277 *flags = ALIGN_CUT|ALIGN_PAD;
280 /* '!' = don't cut, '-' = right padding */
282 while (*str != '\0' && *str != ']' && !isdigit(*str)) {
284 *flags &= ~ALIGN_CUT;
285 else if (*str == '-')
286 *flags |= ALIGN_RIGHT;
287 else if (*str == '.')
288 *flags &= ~ALIGN_PAD;
292 return FALSE; /* expecting number */
294 /* get the alignment size */
295 while (isdigit(*str)) {
296 *align = (*align) * 10 + (*str-'0');
300 /* get the pad character */
301 while (*str != '\0' && *str != ']') {
306 if (*str++ != ']') return FALSE;
312 /* return the aligned text */
313 static char *get_alignment(const char *text, int align, int flags, char pad)
318 g_return_val_if_fail(text != NULL, NULL);
320 str = g_string_new(text);
323 if ((flags & ALIGN_CUT) && align > 0 && str->len > align)
324 g_string_truncate(str, align);
326 /* add pad characters */
327 if (flags & ALIGN_PAD) {
328 while (str->len < align) {
329 if (flags & ALIGN_RIGHT)
330 g_string_prepend_c(str, pad);
332 g_string_append_c(str, pad);
337 g_string_free(str, FALSE);
341 /* Parse and expand text after '$' character. return value has to be
342 g_free()'d if `free_ret' is TRUE. */
343 char *parse_special(char **cmd, SERVER_REC *server, void *item,
344 char **arglist, int *free_ret, int *arg_used, int flags)
346 static char **nested_orig_cmd = NULL; /* FIXME: KLUDGE! */
347 char command, *value;
350 int align, align_flags;
353 int brackets, nest_free;
357 command = **cmd; (*cmd)++;
361 if (!get_alignment_args(cmd, &align, &align_flags,
362 &align_pad) || **cmd == '\0') {
372 nest_free = FALSE; nest_value = NULL;
375 int toplevel = nested_orig_cmd == NULL;
377 if (toplevel) nested_orig_cmd = cmd;
384 nest_value = parse_special(cmd, server, item, arglist,
385 &nest_free, arg_used,
389 while ((*nested_orig_cmd)[1] != '\0') {
390 (*nested_orig_cmd)++;
391 if (**nested_orig_cmd == ')')
396 if (toplevel) nested_orig_cmd = NULL;
402 /* special value is inside {...} (foo${test}bar -> fooXXXbar) */
407 value = get_special_value(cmd, server, item, arglist,
408 free_ret, arg_used, flags);
410 g_error("parse_special() : buffer overflow!");
412 if (value != NULL && *value != '\0' && (flags & PARSE_FLAG_ISSET_ANY))
416 while (**cmd != '}' && (*cmd)[1] != '\0')
420 if (nest_free) g_free(nest_value);
422 if (command == '[' && (flags & PARSE_FLAG_GETNAME) == 0) {
426 if (value == NULL) return "";
428 p = get_alignment(value, align, align_flags, align_pad);
429 if (*free_ret) g_free(value);
438 static void gstring_append_escaped(GString *str, const char *text)
440 while (*text != '\0') {
442 g_string_append_c(str, '%');
443 g_string_append_c(str, *text);
448 /* parse the whole string. $ and \ chars are replaced */
449 char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
450 const char *data, int *arg_used, int flags)
452 char code, **arglist, *ret;
456 g_return_val_if_fail(cmd != NULL, NULL);
457 g_return_val_if_fail(data != NULL, NULL);
459 /* create the argument list */
460 arglist = g_strsplit(data, " ", -1);
462 if (arg_used != NULL) *arg_used = FALSE;
464 str = g_string_new(NULL);
465 while (*cmd != '\0') {
469 g_string_append_c(str, '\t');
472 g_string_append_c(str, '\n');
475 g_string_append_c(str, *cmd);
478 } else if (code == '$') {
481 ret = parse_special((char **) &cmd, server, item,
482 arglist, &need_free, arg_used,
485 if ((flags & PARSE_FLAG_ESCAPE_VARS) == 0)
486 g_string_append(str, ret);
488 gstring_append_escaped(str, ret);
489 if (need_free) g_free(ret);
493 if (*cmd == '\\' || *cmd == '$')
496 g_string_append_c(str, *cmd);
504 g_string_free(str, FALSE);
508 #define is_split_char(str, start) \
509 ((str)[0] == ';' && ((start) == (str) || \
510 ((str)[-1] != '\\' && (str)[-1] != '$')))
512 /* execute the commands in string - commands can be split with ';' */
513 void eval_special_string(const char *cmd, const char *data,
514 SERVER_REC *server, void *item)
516 const char *cmdchars;
517 char *orig, *str, *start, *ret;
518 int arg_used, arg_used_ever;
522 arg_used_ever = FALSE;
523 cmdchars = settings_get_str("cmdchars");
525 /* get a list of all the commands to run */
526 orig = start = str = g_strdup(cmd);
528 if (is_split_char(str, start))
530 else if (*str != '\0') {
535 ret = parse_special_string(start, server, item,
537 if (arg_used) arg_used_ever = TRUE;
539 if (strchr(cmdchars, *ret) == NULL) {
540 /* no command char - let's put it there.. */
543 ret = g_strdup_printf("%c%s", *cmdchars, old);
546 commands = g_slist_append(commands, ret);
548 } while (*start != '\0');
550 /* run the command, if no arguments were ever used, append all of them
551 after each command */
552 while (commands != NULL) {
553 ret = commands->data;
555 if (!arg_used_ever && *data != '\0') {
558 ret = g_strconcat(old, " ", data, NULL);
561 signal_emit("send command", 3, ret, server, item);
564 commands = g_slist_remove(commands, commands->data);
569 void special_history_func_set(SPECIAL_HISTORY_FUNC func)
574 static void special_vars_signals_do(const char *text, int funccount,
575 SIGNAL_FUNC *funcs, int bind)
580 while (*text != '\0') {
581 if (*text == '\\' && text[1] != '\0') {
583 } else if (*text == '$' && text[1] != '\0') {
585 ret = parse_special((char **) &text, NULL, NULL,
586 NULL, &need_free, NULL,
590 expando_bind(ret, funccount, funcs);
592 expando_unbind(ret, funccount, funcs);
593 if (need_free) g_free(ret);
601 void special_vars_add_signals(const char *text,
602 int funccount, SIGNAL_FUNC *funcs)
604 special_vars_signals_do(text, funccount, funcs, TRUE);
607 void special_vars_remove_signals(const char *text,
608 int funccount, SIGNAL_FUNC *funcs)
610 special_vars_signals_do(text, funccount, funcs, FALSE);