diff options
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | config.c | 364 | ||||
-rw-r--r-- | config.h | 44 | ||||
-rw-r--r-- | midimonster.c | 29 | ||||
-rw-r--r-- | midimonster.h | 24 |
5 files changed, 314 insertions, 154 deletions
@@ -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 @@ -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; +} @@ -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 |