Merge pull request #1 from atsampson/master
[calfbox.git] / cmd.c
blobd08f2da9c8ea0946ddcd5d6c39d1ef55aba872cb
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2011 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "blob.h"
20 #include "cmd.h"
21 #include "dom.h"
22 #include "errors.h"
23 #include <assert.h>
24 #include <glib.h>
25 #include <malloc.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <uuid/uuid.h>
30 void cbox_command_target_init(struct cbox_command_target *ct, cbox_process_cmd cmd, void *user_data)
32 ct->process_cmd = cmd;
33 ct->user_data = user_data;
36 gboolean cbox_execute_on(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd_name, const char *args, GError **error, ...)
38 va_list av;
40 va_start(av, error);
41 gboolean res = cbox_execute_on_v(ct, fb, cmd_name, args, av, error);
42 va_end(av);
43 return res;
46 gboolean cbox_execute_on_v(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd_name, const char *args, va_list av, GError **error)
48 int argcount = 0;
49 struct cbox_osc_command cmd;
50 uint8_t *extra_data;
51 // XXXKF might be not good enough for weird platforms
52 int unit_size = sizeof(double);
53 // this must be a power of 2 to guarantee proper alignment
54 assert(unit_size >= sizeof(int) && (unit_size == 4 || unit_size == 8));
55 cmd.command = cmd_name;
56 cmd.arg_types = args;
57 for (int i = 0; args[i]; i++)
58 argcount = i + 1;
59 // contains pointers to all the values, plus values themselves in case of int/double
60 // (casting them to pointers is ugly, and va_arg does not return a lvalue)
61 cmd.arg_values = malloc(sizeof(void *) * argcount + unit_size * argcount);
62 extra_data = (uint8_t *)&cmd.arg_values[argcount];
64 for (int i = 0; i < argcount; i++)
66 int iv;
67 double fv;
68 void *pv = extra_data + unit_size * i;
69 switch(args[i])
71 case 's':
72 cmd.arg_values[i] = va_arg(av, char *);
73 break;
74 case 'i':
75 iv = va_arg(av, int);
76 memcpy(pv, &iv, sizeof(int));
77 cmd.arg_values[i] = pv;
78 break;
79 case 'f': // double really
80 fv = (double)va_arg(av, double);
81 memcpy(pv, &fv, sizeof(double));
82 cmd.arg_values[i] = pv;
83 break;
84 case 'b':
85 cmd.arg_values[i] = va_arg(av, struct cbox_blob *);
86 break;
87 case 'o':
88 cmd.arg_values[i] = va_arg(av, struct cbox_objhdr *);
89 break;
90 case 'u':
91 cmd.arg_values[i] = va_arg(av, struct cbox_uuid *);
92 break;
93 default:
94 g_error("Invalid format character '%c' for command '%s'", args[i], cmd_name);
95 assert(0);
98 gboolean result = ct->process_cmd(ct, fb, &cmd, error);
99 free(cmd.arg_values);
100 return result;
103 gboolean cbox_osc_command_dump(const struct cbox_osc_command *cmd)
105 g_message("Command = %s, args = %s", cmd->command, cmd->arg_types);
106 for (int i = 0; cmd->arg_types[i]; i++)
108 switch(cmd->arg_types[i])
110 case 's':
111 g_message("Args[%d] = '%s'", i, (const char *)cmd->arg_values[i]);
112 break;
113 case 'o':
115 struct cbox_objhdr *oh = cmd->arg_values[i];
116 char buf[40];
117 uuid_unparse(oh->instance_uuid.uuid, buf);
118 g_message("Args[%d] = uuid:'%s'", i, buf);
119 break;
121 case 'i':
122 g_message("Args[%d] = %d", i, *(int *)cmd->arg_values[i]);
123 break;
124 case 'f':
125 g_message("Args[%d] = %f", i, *(double *)cmd->arg_values[i]);
126 break;
127 case 'b':
129 struct cbox_blob *b = cmd->arg_values[i];
130 g_message("Args[%d] = (%p, %d)", i, b->data, (int)b->size);
131 break;
133 default:
134 g_error("Invalid format character '%c' for command '%s'", cmd->arg_types[i], cmd->command);
135 assert(0);
136 return FALSE;
139 return TRUE;
142 gboolean cbox_check_fb_channel(struct cbox_command_target *fb, const char *command, GError **error)
144 if (fb)
145 return TRUE;
147 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Feedback channel required for command '%s'", command);
148 return FALSE;
152 gboolean cbox_execute_sub(struct cbox_command_target *ct, struct cbox_command_target *fb, const struct cbox_osc_command *cmd, const char *new_command, GError **error)
154 struct cbox_osc_command subcmd;
155 subcmd.command = new_command;
156 subcmd.arg_types = cmd->arg_types;
157 subcmd.arg_values = cmd->arg_values;
158 return ct->process_cmd(ct, fb, &subcmd, error);
161 gboolean cbox_parse_path_part_int(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, int *index, int min_index, int max_index, GError **error)
163 char *numcopy = NULL;
164 if (!cbox_parse_path_part_str(cmd, path, subcommand, &numcopy, error))
165 return FALSE;
166 if (!*subcommand)
167 return TRUE;
168 char *endptr = NULL;
169 *index = strtol(numcopy, &endptr, 10);
170 if (!*numcopy && *endptr)
172 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid index %s for command %s", numcopy, cmd->command);
173 g_free(numcopy);
174 *subcommand = NULL;
175 return TRUE;
177 if (*index < min_index || *index > max_index)
179 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Index %s out of range [%d, %d] for command %s", numcopy, min_index, max_index, cmd->command);
180 g_free(numcopy);
181 *subcommand = NULL;
182 return TRUE;
184 g_free(numcopy);
185 return TRUE;
188 gboolean cbox_parse_path_part_str(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, char **path_element, GError **error)
190 *path_element = NULL;
191 *subcommand = NULL;
192 int plen = strlen(path);
193 if (!strncmp(cmd->command, path, plen))
195 const char *num = cmd->command + plen;
196 const char *slash = strchr(num, '/');
197 if (!slash)
199 cbox_set_command_error_with_msg(error, cmd, "needs at least one extra path element");
200 return TRUE;
203 *path_element = g_strndup(num, slash-num);
204 *subcommand = slash;
205 return TRUE;
207 return FALSE;