aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2020-01-05 23:26:04 +0100
committercbdev <cb@cbcdn.com>2020-01-05 23:26:04 +0100
commite08b473d0eeb467ad358ba5314157753b4f37c18 (patch)
treed847784ab3a926054b586877e6d77865af574be1
parentf8ed6c26683c041ec61dac46d740b4b87df811ad (diff)
downloadmidimonster-e08b473d0eeb467ad358ba5314157753b4f37c18.tar.gz
midimonster-e08b473d0eeb467ad358ba5314157753b4f37c18.tar.bz2
midimonster-e08b473d0eeb467ad358ba5314157753b4f37c18.zip
Implement commandline config override (Fixes #26)
-rw-r--r--README.md7
-rw-r--r--config.c364
-rw-r--r--config.h44
-rw-r--r--midimonster.c29
-rw-r--r--midimonster.h24
5 files changed, 314 insertions, 154 deletions
diff --git a/README.md b/README.md
index e955d53..2496dfa 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,8 @@ The MIDImonster takes as it's first argument the name of an optional configurati
to use (`monster.cfg` is used as default if none is specified). The configuration
file syntax is explained in the next section.
+The current MIDIMonster version can be queried by passing *-v* as command-line argument.
+
## Configuration
Each protocol supported by MIDIMonster is implemented by a *backend*, which takes
@@ -76,6 +78,11 @@ To make an instance available for mapping channels, it requires at least the
`[<backend-name> <instance-name>]` configuration stanza. Most backends require
additional configuration for their instances.
+Backend and instance configuration options can also be overriden via command line
+arguments using the syntax `-b <backend>.<option>=<value>` for backend options
+and `-i <instance>.<option>=<value>` for instance options. These overrides
+are applied when the backend/instance is first mentioned in the configuration file.
+
### Channel mapping
The `[map]` section consists of lines of channel-to-channel assignments, reading like
diff --git a/config.c b/config.c
index c7e2f7e..d2b65d4 100644
--- a/config.c
+++ b/config.c
@@ -21,6 +21,8 @@ typedef enum {
static backend* current_backend = NULL;
static instance* current_instance = NULL;
+static size_t noverrides = 0;
+static config_override* overrides = NULL;
#ifdef _WIN32
#define GETLINE_BUFFER 4096
@@ -312,13 +314,179 @@ done:
return rv;
}
+static int config_line(char* line){
+ map_type mapping_type = map_rtl;
+ char* separator = NULL;
+ size_t u;
+
+ line = config_trim_line(line);
+ if(*line == ';' || strlen(line) == 0){
+ //skip comments
+ return 0;
+ }
+ if(*line == '[' && line[strlen(line) - 1] == ']'){
+ if(!strncmp(line, "[backend ", 9)){
+ //backend configuration
+ parser_state = backend_cfg;
+ line[strlen(line) - 1] = 0;
+ current_backend = backend_match(line + 9);
+
+ if(!current_backend){
+ fprintf(stderr, "Cannot configure unknown backend %s\n", line + 9);
+ return 1;
+ }
+
+ //apply overrides
+ for(u = 0; u < noverrides; u++){
+ if(!overrides[u].handled && overrides[u].type == override_backend
+ && !strcmp(overrides[u].target, current_backend->name)){
+ if(current_backend->conf(overrides[u].option, overrides[u].value)){
+ fprintf(stderr, "Configuration override for %s failed for backend %s\n",
+ overrides[u].option, current_backend->name);
+ return 1;
+ }
+ overrides[u].handled = 1;
+ }
+ }
+ }
+ else if(!strcmp(line, "[map]")){
+ //mapping configuration
+ parser_state = map;
+ }
+ else{
+ //backend instance configuration
+ parser_state = instance_cfg;
+
+ //trim braces
+ line[strlen(line) - 1] = 0;
+ line++;
+
+ //find separating space and terminate
+ for(separator = line; *separator && *separator != ' '; separator++){
+ }
+ if(!*separator){
+ fprintf(stderr, "No instance name specified for backend %s\n", line);
+ return 1;
+ }
+ *separator = 0;
+ separator++;
+
+ current_backend = backend_match(line);
+ if(!current_backend){
+ fprintf(stderr, "No such backend %s\n", line);
+ return 1;
+ }
+
+ if(instance_match(separator)){
+ fprintf(stderr, "Duplicate instance name %s\n", separator);
+ return 1;
+ }
+
+ //validate instance name
+ if(strchr(separator, ' ') || strchr(separator, '.')){
+ fprintf(stderr, "Invalid instance name %s\n", separator);
+ return 1;
+ }
+
+ current_instance = current_backend->create();
+ if(!current_instance){
+ fprintf(stderr, "Failed to instantiate backend %s\n", line);
+ return 1;
+ }
+
+ current_instance->name = strdup(separator);
+ current_instance->backend = current_backend;
+ fprintf(stderr, "Created %s instance %s\n", line, separator);
+
+ //apply overrides
+ for(u = 0; u < noverrides; u++){
+ if(!overrides[u].handled && overrides[u].type == override_instance
+ && !strcmp(overrides[u].target, current_instance->name)){
+ if(current_backend->conf_instance(current_instance, overrides[u].option, overrides[u].value)){
+ fprintf(stderr, "Configuration override for %s failed for instance %s\n",
+ overrides[u].option, current_instance->name);
+ return 1;
+ }
+ overrides[u].handled = 1;
+ }
+ }
+ }
+ }
+ else if(parser_state == map){
+ mapping_type = map_rtl;
+ //find separator
+ for(separator = line; *separator && *separator != '<' && *separator != '>'; separator++){
+ }
+
+ switch(*separator){
+ case '>':
+ mapping_type = map_ltr;
+ //fall through
+ case '<': //default
+ *separator = 0;
+ separator++;
+ break;
+ case 0:
+ default:
+ fprintf(stderr, "Not a channel mapping: %s\n", line);
+ return 1;
+ }
+
+ if((mapping_type == map_ltr && *separator == '<')
+ || (mapping_type == map_rtl && *separator == '>')){
+ mapping_type = map_bidir;
+ separator++;
+ }
+
+ line = config_trim_line(line);
+ separator = config_trim_line(separator);
+
+ if(mapping_type == map_ltr || mapping_type == map_bidir){
+ if(config_map(separator, line)){
+ fprintf(stderr, "Failed to map channel %s to %s\n", line, separator);
+ return 1;
+ }
+ }
+ if(mapping_type == map_rtl || mapping_type == map_bidir){
+ if(config_map(line, separator)){
+ fprintf(stderr, "Failed to map channel %s to %s\n", separator, line);
+ return 1;
+ }
+ }
+ }
+ else{
+ //pass to parser
+ //find separator
+ separator = strchr(line, '=');
+ if(!separator){
+ fprintf(stderr, "Not an assignment: %s\n", line);
+ return 1;
+ }
+
+ *separator = 0;
+ separator++;
+ line = config_trim_line(line);
+ separator = config_trim_line(separator);
+
+ if(parser_state == backend_cfg && current_backend->conf(line, separator)){
+ fprintf(stderr, "Failed to configure backend %s\n", current_backend->name);
+ return 1;
+ }
+ else if(parser_state == instance_cfg && current_backend->conf_instance(current_instance, line, separator)){
+ fprintf(stderr, "Failed to configure instance %s\n", current_instance->name);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
int config_read(char* cfg_filepath){
int rv = 1;
size_t line_alloc = 0;
ssize_t status;
- map_type mapping_type = map_rtl;
FILE* source = NULL;
- char* line_raw = NULL, *line, *separator;
+ char* line_raw = NULL;
//create heap copy of file name because original might be in readonly memory
char* source_dir = strdup(cfg_filepath), *source_file = NULL;
@@ -355,146 +523,88 @@ int config_read(char* cfg_filepath){
}
for(status = getline(&line_raw, &line_alloc, source); status >= 0; status = getline(&line_raw, &line_alloc, source)){
- line = config_trim_line(line_raw);
- if(*line == ';' || strlen(line) == 0){
- //skip comments
- continue;
+ if(config_line(line_raw)){
+ goto bail;
}
- if(*line == '[' && line[strlen(line) - 1] == ']'){
- if(!strncmp(line, "[backend ", 9)){
- //backend configuration
- parser_state = backend_cfg;
- line[strlen(line) - 1] = 0;
- current_backend = backend_match(line + 9);
-
- if(!current_backend){
- fprintf(stderr, "Cannot configure unknown backend %s\n", line + 9);
- goto bail;
- }
- }
- else if(!strcmp(line, "[map]")){
- //mapping configuration
- parser_state = map;
- }
- else{
- //backend instance configuration
- parser_state = instance_cfg;
-
- //trim braces
- line[strlen(line) - 1] = 0;
- line++;
-
- //find separating space and terminate
- for(separator = line; *separator && *separator != ' '; separator++){
- }
- if(!*separator){
- fprintf(stderr, "No instance name specified for backend %s\n", line);
- goto bail;
- }
- *separator = 0;
- separator++;
+ }
- current_backend = backend_match(line);
- if(!current_backend){
- fprintf(stderr, "No such backend %s\n", line);
- goto bail;
- }
+ //TODO check whether all overrides have been applied
- if(instance_match(separator)){
- fprintf(stderr, "Duplicate instance name %s\n", separator);
- goto bail;
- }
+ rv = 0;
+bail:
+ free(source_dir);
+ if(source){
+ fclose(source);
+ }
+ free(line_raw);
+ return rv;
+}
- //validate instance name
- if(strchr(separator, ' ') || strchr(separator, '.')){
- fprintf(stderr, "Invalid instance name %s\n", separator);
- goto bail;
- }
+int config_add_override(override_type type, char* data_raw){
+ int rv = 1;
+ //heap a copy because the original data is probably not writable
+ char* data = strdup(data_raw);
- current_instance = current_backend->create();
- if(!current_instance){
- fprintf(stderr, "Failed to instantiate backend %s\n", line);
- goto bail;
- }
+ if(!data){
+ fprintf(stderr, "Failed to allocate memory\n");
+ goto bail;
+ }
- current_instance->name = strdup(separator);
- current_instance->backend = current_backend;
- fprintf(stderr, "Created %s instance %s\n", line, separator);
- }
- }
- else if(parser_state == map){
- mapping_type = map_rtl;
- //find separator
- for(separator = line; *separator && *separator != '<' && *separator != '>'; separator++){
- }
+ char* option = strchr(data, '.');
+ char* value = strchr(data, '=');
- switch(*separator){
- case '>':
- mapping_type = map_ltr;
- //fall through
- case '<': //default
- *separator = 0;
- separator++;
- break;
- case 0:
- default:
- fprintf(stderr, "Not a channel mapping: %s\n", line);
- goto bail;
- }
+ if(!option || !value){
+ fprintf(stderr, "Override %s is not a valid assignment\n", data_raw);
+ goto bail;
+ }
- if((mapping_type == map_ltr && *separator == '<')
- || (mapping_type == map_rtl && *separator == '>')){
- mapping_type = map_bidir;
- separator++;
- }
+ //terminate strings
+ *option = 0;
+ option++;
- line = config_trim_line(line);
- separator = config_trim_line(separator);
+ *value = 0;
+ value++;
- if(mapping_type == map_ltr || mapping_type == map_bidir){
- if(config_map(separator, line)){
- fprintf(stderr, "Failed to map channel %s to %s\n", line, separator);
- goto bail;
- }
- }
- if(mapping_type == map_rtl || mapping_type == map_bidir){
- if(config_map(line, separator)){
- fprintf(stderr, "Failed to map channel %s to %s\n", separator, line);
- goto bail;
- }
- }
- }
- else{
- //pass to parser
- //find separator
- separator = strchr(line, '=');
- if(!separator){
- fprintf(stderr, "Not an assignment: %s\n", line);
- goto bail;
- }
+ config_override new = {
+ .type = type,
+ .handled = 0,
+ .target = strdup(config_trim_line(data)),
+ .option = strdup(config_trim_line(option)),
+ .value = strdup(config_trim_line(value))
+ };
- *separator = 0;
- separator++;
- line = config_trim_line(line);
- separator = config_trim_line(separator);
+ if(!new.target || !new.option || !new.value){
+ fprintf(stderr, "Failed to allocate memory\n");
+ goto bail;
+ }
- if(parser_state == backend_cfg && current_backend->conf(line, separator)){
- fprintf(stderr, "Failed to configure backend %s\n", current_backend->name);
- goto bail;
- }
- else if(parser_state == instance_cfg && current_backend->conf_instance(current_instance, line, separator)){
- fprintf(stderr, "Failed to configure instance %s\n", current_instance->name);
- goto bail;
- }
- }
+ overrides = realloc(overrides, (noverrides + 1) * sizeof(config_override));
+ if(!overrides){
+ noverrides = 0;
+ fprintf(stderr, "Failed to allocate memory\n");
+ goto bail;
}
+ overrides[noverrides] = new;
+ noverrides++;
rv = 0;
bail:
- free(source_dir);
- if(source){
- fclose(source);
- }
- free(line_raw);
+ free(data);
return rv;
}
+
+void config_free(){
+ size_t u;
+
+ for(u = 0; u < noverrides; u++){
+ free(overrides[u].target);
+ free(overrides[u].option);
+ free(overrides[u].value);
+ }
+
+ noverrides = 0;
+ free(overrides);
+ overrides = NULL;
+
+ parser_state = none;
+}
diff --git a/config.h b/config.h
index 7cc2a42..d15aed2 100644
--- a/config.h
+++ b/config.h
@@ -1 +1,45 @@
+/*
+ * Channel specification glob
+ */
+typedef struct /*_mm_channel_glob*/ {
+ size_t offset[2];
+ union {
+ void* impl;
+ uint64_t u64[2];
+ } limits;
+ uint64_t values;
+} channel_glob;
+
+/*
+ * (Multi-)Channel specification
+ */
+typedef struct /*_mm_channel_spec*/ {
+ char* spec;
+ uint8_t internal;
+ size_t channels;
+ size_t globs;
+ channel_glob* glob;
+} channel_spec;
+
+/*
+ * Command-line override types
+ */
+typedef enum {
+ override_backend,
+ override_instance
+} override_type;
+
+/*
+ * Command-line override data
+ */
+typedef struct /*_mm_config_override*/ {
+ override_type type;
+ uint8_t handled;
+ char* target;
+ char* option;
+ char* value;
+} config_override;
+
int config_read(char* file);
+int config_add_override(override_type type, char* data);
+void config_free();
diff --git a/midimonster.c b/midimonster.c
index 2ec165b..583601e 100644
--- a/midimonster.c
+++ b/midimonster.c
@@ -274,9 +274,30 @@ static int args_parse(int argc, char** argv, char** cfg_file){
version();
return 1;
}
-
- //if nothing else matches, it's probably the configuration file
- *cfg_file = argv[u];
+ else if(!strcmp(argv[u], "-i")){
+ if(!argv[u + 1]){
+ fprintf(stderr, "Missing instance override specification\n");
+ return 1;
+ }
+ if(config_add_override(override_instance, argv[u + 1])){
+ return 1;
+ }
+ u++;
+ }
+ else if(!strcmp(argv[u], "-b")){
+ if(!argv[u + 1]){
+ fprintf(stderr, "Missing backend override specification\n");
+ return 1;
+ }
+ if(config_add_override(override_backend, argv[u + 1])){
+ return 1;
+ }
+ u++;
+ }
+ else{
+ //if nothing else matches, it's probably the configuration file
+ *cfg_file = argv[u];
+ }
}
return 0;
@@ -317,6 +338,7 @@ int main(int argc, char** argv){
map_free();
fds_free();
plugins_close();
+ config_free();
return usage(argv[0]);
}
@@ -403,6 +425,7 @@ bail:
fds_free();
event_free();
plugins_close();
+ config_free();
return rv;
}
diff --git a/midimonster.h b/midimonster.h
index c5a6cf5..d6f04e7 100644
--- a/midimonster.h
+++ b/midimonster.h
@@ -189,29 +189,6 @@ typedef struct _backend_instance {
char* name;
} instance;
-/*
- * Channel specification glob
- */
-typedef struct /*_mm_channel_glob*/ {
- size_t offset[2];
- union {
- void* impl;
- uint64_t u64[2];
- } limits;
- uint64_t values;
-} channel_glob;
-
-/*
- * (Multi-)Channel specification
- */
-typedef struct /*_mm_channel_spec*/ {
- char* spec;
- uint8_t internal;
- size_t channels;
- size_t globs;
- channel_glob* glob;
-} channel_spec;
-
/*
* Instance channel structure
* Backends may either manage their own channel registry
@@ -283,7 +260,6 @@ MM_API instance* mm_instance_find(char* backend, uint64_t ident);
* function.
*/
MM_API channel* mm_channel(instance* i, uint64_t ident, uint8_t create);
-//TODO channel* mm_channel_find()
/*
* Register (manage = 1) or unregister (manage = 0) a file descriptor