diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/commands.c | 324 | ||||
-rw-r--r-- | src/core/commands.h | 72 | ||||
-rw-r--r-- | src/core/misc.c | 38 | ||||
-rw-r--r-- | src/core/misc.h | 10 |
4 files changed, 269 insertions, 175 deletions
diff --git a/src/core/commands.c b/src/core/commands.c index 225f8213..5e0a27ba 100644 --- a/src/core/commands.c +++ b/src/core/commands.c @@ -66,6 +66,7 @@ void command_free(COMMAND_REC *rec) signal_emit("commandlist remove", 1, rec); g_free_not_null(rec->category); + g_strfreev(rec->options); g_free(rec->cmd); g_free(rec); } @@ -93,7 +94,7 @@ void command_unbind(const char *cmd, SIGNAL_FUNC func) } } -void command_runsub(const char *cmd, const char *data, void *p1, void *p2) +void command_runsub(const char *cmd, const char *data, void *server, void *item) { char *subcmd, *defcmd, *args; @@ -106,30 +107,101 @@ void command_runsub(const char *cmd, const char *data, void *p1, void *p2) while (*args == ' ') args++; g_strdown(subcmd); - if (!signal_emit(subcmd, 3, args, p1, p2)) { + if (!signal_emit(subcmd, 3, args, server, item)) { defcmd = g_strdup_printf("default command %s", cmd); - if (!signal_emit(defcmd, 3, data, p1, p2)) - signal_emit("unknown command", 3, strchr(subcmd, ' ')+1, p1, p2); + if (!signal_emit(defcmd, 3, data, server, item)) + signal_emit("unknown command", 3, strchr(subcmd, ' ')+1, server, item); g_free(defcmd); } g_free(subcmd); } -COMMAND_REC *command_find(const char *command) +COMMAND_REC *command_find(const char *cmd) { GSList *tmp; - int len; + + g_return_val_if_fail(cmd != NULL, NULL); for (tmp = commands; tmp != NULL; tmp = tmp->next) { COMMAND_REC *rec = tmp->data; - if (g_strcasecmp(rec->cmd, command) == 0) + if (g_strcasecmp(rec->cmd, cmd) == 0) return rec; } return NULL; } +#define iscmdtype(c) \ + ((c) == '-' || (c) == '+' || (c) == '@') + +static GSList *optlist_find(GSList *optlist, const char *option) +{ + while (optlist != NULL) { + char *name = optlist->data; + if (iscmdtype(*name)) name++; + + if (g_strcasecmp(name, option) == 0) + return optlist; + + optlist = optlist->next; + } + + return NULL; +} + +void command_set_options(const char *cmd, const char *options) +{ + COMMAND_REC *rec; + char **optlist, **tmp, *name, *str; + GSList *list, *oldopt; + + g_return_if_fail(cmd != NULL); + g_return_if_fail(options != NULL); + + rec = command_find(cmd); + g_return_if_fail(rec != NULL); + + optlist = g_strsplit(options, " ", -1); + + if (rec->options == NULL) { + /* first call - use specified args directly */ + rec->options = optlist; + return; + } + + /* save old options to linked list */ + list = NULL; + for (tmp = rec->options; *tmp != NULL; tmp++) + list = g_slist_append(list, g_strdup(*tmp)); + g_strfreev(rec->options); + + /* merge the options */ + for (tmp = optlist; *tmp != NULL; tmp++) { + name = iscmdtype(**tmp) ? (*tmp)+1 : *tmp; + + oldopt = optlist_find(list, name); + if (oldopt != NULL) { + /* already specified - overwrite old defination */ + g_free(oldopt->data); + oldopt->data = g_strdup(*tmp); + } else { + /* new option, append to list */ + list = g_slist_append(list, g_strdup(*tmp)); + } + } + g_strfreev(optlist); + + /* linked list -> string[] */ + g_free(rec->options); + str = gslist_to_string(list, " "); + rec->options = g_strsplit(str, " ", -1); + g_free(str); + + g_slist_foreach(list, (GFunc) g_free, NULL); + g_slist_free(list); +} + char *cmd_get_param(char **data) { char *pos; @@ -146,7 +218,7 @@ char *cmd_get_param(char **data) return pos; } -char *cmd_get_quoted_param(char **data) +static char *cmd_get_quoted_param(char **data) { char *pos, quote; @@ -171,67 +243,7 @@ char *cmd_get_quoted_param(char **data) return pos; } -static char *get_opt_args(char **data) -{ - /* -cmd1 -cmd2 -cmd3 ... */ - char *p, *ret; - int stopnext; - - g_return_val_if_fail(data != NULL, NULL); - g_return_val_if_fail(*data != NULL, NULL); - - stopnext = FALSE; - ret = NULL; - for (p = *data;;) { - if (*p != '-' || stopnext) { - if (p == *data) return ""; - - ret = *data; - *data = p; - - while (isspace(p[-1]) && p > ret) p--; - if (*p != '\0') *p = '\0'; - return ret; - } - - if (p[1] == '-') { - /* -- argument means end of arguments even if - next word starts with - */ - stopnext = TRUE; - } - - while (!isspace(*p) && *p != '\0') p++; - while (isspace(*p)) p++; - } -} - -static void cmd_params_pack(char ***subargs, char *end, char *start, char *newstart) -{ - char ***tmp; - char *data; - int bufsize, datalen, len; - - bufsize = (int) (end-newstart)+1; - - data = g_malloc(bufsize); datalen = 0; - for (tmp = subargs; *tmp != NULL; tmp++) { - if (**tmp < start || **tmp > end) - continue; - - len = strlen(**tmp)+1; - if (datalen+len > bufsize) - g_error("cmd_params_pack() : buffer overflow!"); - - memcpy(data+datalen, **tmp, len); - **tmp = newstart+datalen; - datalen += len; - } - - g_memmove(newstart, data, datalen); - g_free(data); -} - -int arg_find(char **array, const char *item) +static int option_find(char **array, const char *item) { char **tmp; int index; @@ -241,101 +253,83 @@ int arg_find(char **array, const char *item) index = 0; for (tmp = array; *tmp != NULL; tmp++, index++) { - if (g_strcasecmp(*tmp + (**tmp == '@'), item) == 0) + if (g_strcasecmp(*tmp + iscmdtype(**tmp), item) == 0) return index; } return -1; } -static int get_multi_args(char **data, int checkonly, va_list va) +static int get_cmd_options(char **data, int ignore_unknown, + const char *cmd, GHashTable *options) { - /* -cmd1 arg1 -cmd2 "argument two" -cmd3 */ - GString *returnargs; - char **args, **arglist, *arg, *origdata; - char **nextarg, ***subargs; - int eat, pos; - - eat = 0; - args = (char **) va_arg(va, char **); - g_return_val_if_fail(args != NULL && *args != NULL && **args != '\0', 0); - - arglist = g_strsplit(*args, " ", -1); - eat = strarray_length(arglist); - - subargs = g_new(char **, eat+1); - for (pos = 0; pos < eat; pos++) { - subargs[pos] = (char **) va_arg(va, char **); - if (subargs[pos] == NULL) { - g_free(subargs); - g_warning("get_multi_args() : subargument == NULL"); - return eat; - } - *subargs[pos] = ""; - } - subargs[eat] = NULL; + COMMAND_REC *rec; + char *option, *arg, **optlist; + int pos; - origdata = *data; - returnargs = g_string_new(NULL); - nextarg = NULL; + /* get option definations */ + rec = cmd == NULL ? NULL : command_find(cmd); + optlist = rec == NULL ? NULL : rec->options; + + option = NULL; pos = -1; for (;;) { if (**data == '-') { + if (option != NULL && *optlist[pos] == '+') { + /* required argument missing! */ + *data = optlist[pos] + 1; + return CMDERR_OPTION_ARG_MISSING; + } + (*data)++; if (**data == '-') { - /* -- argument means end of arguments even + /* -- option means end of options even if next word starts with - */ (*data)++; while (isspace(**data)) (*data)++; break; } - arg = cmd_get_param(data); - g_string_sprintfa(returnargs, "-%s ", arg); + option = cmd_get_param(data); - /* check if this argument can have parameter */ - pos = arg_find(arglist, arg); - nextarg = pos == -1 ? NULL : subargs[pos]; + /* check if this option can have argument */ + pos = optlist == NULL ? -1 : option_find(optlist, option); + if (pos == -1 && !ignore_unknown) { + /* unknown option! */ + *data = option; + return CMDERR_OPTION_UNKNOWN; + } + if (pos != -1) { + /* if we used a shortcut of parameter, put + the whole parameter name in options table */ + option = optlist[pos] + iscmdtype(*optlist[pos]); + } + if (options != NULL) g_hash_table_insert(options, option, ""); + + if (pos == -1 || !iscmdtype(*optlist[pos])) + option = NULL; while (isspace(**data)) (*data)++; continue; } - if (nextarg == NULL) + if (option == NULL) break; - if (*arglist[pos] == '@' && !isdigit(**data)) + if (*optlist[pos] == '@' && !isdigit(**data)) break; /* expected a numeric argument */ - /* save the sub-argument to `nextarg' */ + /* save the argument */ arg = cmd_get_quoted_param(data); - *nextarg = arg; nextarg = NULL; + if (options != NULL) { + g_hash_table_remove(options, option); + g_hash_table_insert(options, option, arg); + } + option = NULL; while (isspace(**data)) (*data)++; } - if (!checkonly) { - /* ok, this is a bit stupid. this will pack the arguments in - `data' like "-arg1 subarg -arg2 sub2\0" -> - "-arg1 -arg2\0subarg\0sub2\0" this is because it's easier - to free only _one_ string instead of two (`args') when - using PARAM_FLAG_MULTIARGS. */ - if (returnargs->len == 0) - *args = ""; - else { - cmd_params_pack(subargs, **data == '\0' ? *data : (*data)-1, - origdata, origdata+returnargs->len); - - g_string_truncate(returnargs, returnargs->len-1); - strcpy(origdata, returnargs->str); - *args = origdata; - } - } - - g_string_free(returnargs, TRUE); - g_strfreev(arglist); - g_free(subargs); - - return eat; + return 0; } char *cmd_get_callfuncs(const char *data, int *count, va_list *args) @@ -356,34 +350,38 @@ char *cmd_get_callfuncs(const char *data, int *count, va_list *args) return ret; } -char *cmd_get_params(const char *data, int count, ...) +typedef struct { + char *data; + GHashTable *options; +} CMD_TEMP_REC; + +int cmd_get_params(const char *data, gpointer *free_me, int count, ...) { + CMD_TEMP_REC *rec; + GHashTable **opthash; char **str, *arg, *datad, *old; va_list args; - int cnt, eat, len; + int cnt, error, len; - g_return_val_if_fail(data != NULL, NULL); + g_return_val_if_fail(data != NULL, FALSE); va_start(args, count); - /* get the length of the arguments in string */ - if ((count & (PARAM_FLAG_MULTIARGS|PARAM_FLAG_OPTARGS)) == 0) + /* get the length of the options in string */ + if ((count & PARAM_FLAG_OPTIONS) == 0) len = 0; else { old = datad = g_strdup(data); - if (count & PARAM_FLAG_MULTIARGS) - get_multi_args(&datad, TRUE, args); - else - get_opt_args(&datad); + get_cmd_options(&datad, TRUE, NULL, NULL); len = (int) (datad-old); g_free(old); } - /* send the text to custom functions to handle - skip arguments */ + /* send the text to custom functions to handle - skip options */ old = datad = cmd_get_callfuncs(data+len, &count, &args); if (len > 0) { - /* put the arguments + the new data to one string */ + /* put the options + the new data to one string */ datad = g_malloc(len+1 + strlen(old)+1); memcpy(datad, data, len); datad[len] = ' '; @@ -393,20 +391,25 @@ char *cmd_get_params(const char *data, int count, ...) old = datad; } + rec = g_new0(CMD_TEMP_REC, 1); + rec->data = old; + *free_me = rec; + /* and now handle the string */ + error = FALSE; cnt = PARAM_WITHOUT_FLAGS(count); while (cnt-- > 0) { - if (count & PARAM_FLAG_MULTIARGS) { - eat = get_multi_args(&datad, FALSE, args)+1; - count &= ~PARAM_FLAG_MULTIARGS; + if (count & PARAM_FLAG_OPTIONS) { + arg = (char *) va_arg(args, char *); + opthash = (GHashTable **) va_arg(args, GHashTable **); - cnt -= eat-1; - while (eat-- > 0) - str = (char **) va_arg(args, char **); + rec->options = *opthash = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal); + error = get_cmd_options(&datad, count & PARAM_FLAG_UNKNOWN_OPTIONS, arg, *opthash); + if (error) break; + + count &= ~PARAM_FLAG_OPTIONS; + cnt++; continue; - } else if (count & PARAM_FLAG_OPTARGS) { - arg = get_opt_args(&datad); - count &= ~PARAM_FLAG_OPTARGS; } else if (cnt == 0 && count & PARAM_FLAG_GETREST) { /* get rest */ arg = datad; @@ -421,7 +424,24 @@ char *cmd_get_params(const char *data, int count, ...) } va_end(args); - return old; + if (error) { + signal_emit("error command", 2, GINT_TO_POINTER(error), datad); + signal_stop(); + + cmd_params_free(rec); + *free_me = NULL; + } + + return !error; +} + +void cmd_params_free(void *free_me) +{ + CMD_TEMP_REC *rec = free_me; + + if (rec->options != NULL) g_hash_table_destroy(rec->options); + g_free(rec->data); + g_free(rec); } void cmd_get_add_func(CMD_GET_FUNC func) diff --git a/src/core/commands.h b/src/core/commands.h index 42fb20c0..a199a1f5 100644 --- a/src/core/commands.h +++ b/src/core/commands.h @@ -6,10 +6,13 @@ typedef struct { char *category; char *cmd; -} -COMMAND_REC; + char **options; +} COMMAND_REC; enum { + CMDERR_OPTION_UNKNOWN = -2, /* unknown -option */ + CMDERR_OPTION_ARG_MISSING = -1, /* argument missing for -option */ + CMDERR_ERRNO, /* get the error from errno */ CMDERR_NOT_ENOUGH_PARAMS, /* not enough parameters given */ CMDERR_NOT_CONNECTED, /* not connected to IRC server */ @@ -20,7 +23,7 @@ enum { }; #define cmd_return_error(a) { signal_emit("error command", 1, GINT_TO_POINTER(a)); signal_stop(); return; } -#define cmd_param_error(a) { g_free(params); cmd_return_error(a); } +#define cmd_param_error(a) { cmd_params_free(free_arg); cmd_return_error(a); } extern GSList *commands; extern char *current_command; @@ -29,12 +32,29 @@ void command_bind_to(int pos, const char *cmd, const char *category, SIGNAL_FUNC #define command_bind(a, b, c) command_bind_to(1, a, b, c) #define command_bind_first(a, b, c) command_bind_to(0, a, b, c) #define command_bind_last(a, b, c) command_bind_to(2, a, b, c) - void command_unbind(const char *cmd, SIGNAL_FUNC func); -void command_runsub(const char *cmd, const char *data, void *p1, void *p2); +/* Run subcommand, `cmd' contains the base command, first word in `data' + contains the subcommand */ +void command_runsub(const char *cmd, const char *data, void *server, void *item); + +COMMAND_REC *command_find(const char *cmd); + +/* Specify options that command can accept. `options' contains list of + options separated with space, each option can contain a special + char in front of it: + + '-': optional argument + '+': argument required + '@': optional numeric argument -COMMAND_REC *command_find(const char *command); + for example if options = "save -file +nick", you can use + /command -save -file [<filename>] -nick <nickname> + + You can call this command multiple times for same command, options + will be merged. If there's any conflicts with option types, the last + call will override the previous */ +void command_set_options(const char *cmd, const char *options); /* count can have these flags: */ #define PARAM_WITHOUT_FLAGS(a) ((a) & 0x00ffffff) @@ -42,30 +62,40 @@ COMMAND_REC *command_find(const char *command); #define PARAM_FLAG_NOQUOTES 0x01000000 /* final argument gets all the rest of the arguments */ #define PARAM_FLAG_GETREST 0x02000000 -/* optional arguments (-cmd -cmd2 -cmd3) */ -#define PARAM_FLAG_OPTARGS 0x04000000 -/* arguments can have arguments too. Example: +/* command contains options - first you need to specify them with + command_set_options() function. Example: - -cmd arg -noargcmd -cmd2 "another arg -optnumarg rest of the text + -cmd requiredarg -noargcmd -cmd2 "another arg" -optnumarg rest of text You would call this with: - args = "cmd cmd2 @optnumarg"; - cmd_get_params(data, 5 | PARAM_FLAG_MULTIARGS | PARAM_FLAG_GETREST, - &args, &arg_cmd, &arg_cmd2, &arg_optnum, &rest); + // only once in init + command_set_options("mycmd", "+cmd noargcmd -cmd2 @optnumarg"); + + GHashTable *optlist; + + cmd_get_params(data, &free_me, 1 | PARAM_FLAG_OPTIONS | + PARAM_FLAG_GETREST, "mycmd", &optlist, &rest); - The variables are filled as following: + The optlist hash table is filled: - args = "-cmd -noargcmd -cmd2 -optnumarg" - arg_cmd = "arg" - arg_cmd2 = "another arg" - rest = "rest of the text" - arg_optnum = "" - this is because "rest" isn't a numeric value + "cmd" = "requiredarg" + "noargcmd" = "" + "cmd2" = "another arg" + "optnumarg" = "" - this is because "rest" isn't a numeric value */ -#define PARAM_FLAG_MULTIARGS 0x08000000 +#define PARAM_FLAG_OPTIONS 0x04000000 +/* don't complain about unknown options */ +#define PARAM_FLAG_UNKNOWN_OPTIONS 0x08000000 char *cmd_get_param(char **data); -char *cmd_get_params(const char *data, int count, ...); +/* get parameters from command - you should point free_me somewhere and + cmd_params_free() it after you don't use any of the parameters anymore. + + Returns TRUE if all ok, FALSE if error occured. */ +int cmd_get_params(const char *data, gpointer *free_me, int count, ...); + +void cmd_params_free(void *free_me); typedef char* (*CMD_GET_FUNC) (const char *data, int *count, va_list *args); void cmd_get_add_func(CMD_GET_FUNC func); diff --git a/src/core/misc.c b/src/core/misc.c index bc73f5a0..952c7149 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -247,7 +247,8 @@ void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, void *data) return NULL; } -char *gslist_to_string(GSList *list, int offset, const char *delimiter) +/* `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; @@ -266,6 +267,41 @@ char *gslist_to_string(GSList *list, int offset, const char *delimiter) 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) diff --git a/src/core/misc.h b/src/core/misc.h index 3df1dc94..7fa866df 100644 --- a/src/core/misc.h +++ b/src/core/misc.h @@ -25,7 +25,15 @@ GList *glist_find_string(GList *list, const char *key); GList *glist_find_icase_string(GList *list, const char *key); void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, void *data); -char *gslist_to_string(GSList *list, int offset, const char *delimiter); + +/* `list' contains pointer to structure with a char* to string. */ +char *gslistptr_to_string(GSList *list, int offset, const char *delimiter); +/* `list' contains char* */ +char *gslist_to_string(GSList *list, const char *delimiter); + +/* 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); /* strstr() with case-ignoring */ char *stristr(const char *data, const char *key); |