/** * Parse the "routes" section, a string on the form * a>b, c>d, e>f, ... * where a... are non-unique, non-negative integers * and input channel a gets copied to output channel b, etc. * @param param the configuration block to read * @param filter a route_filter whose min_channels and sources[] to set * @return true on success, false on error */ static bool route_filter_parse(const struct config_param *param, struct route_filter *filter, GError **error_r) { /* TODO: * With a more clever way of marking "don't copy to output N", * This could easily be merged into a single loop with some * dynamic g_realloc() instead of one count run and one g_malloc(). */ gchar **tokens; int number_of_copies; // A cowardly default, just passthrough stereo const char *routes = config_get_block_string(param, "routes", "0>0, 1>1"); filter->min_input_channels = 0; filter->min_output_channels = 0; tokens = g_strsplit(routes, ",", 255); number_of_copies = g_strv_length(tokens); // Start by figuring out a few basic things about the routing set for (int c=0; c<number_of_copies; ++c) { // String and int representations of the source/destination gchar **sd; int source, dest; // Squeeze whitespace g_strstrip(tokens[c]); // Split the a>b string into source and destination sd = g_strsplit(tokens[c], ">", 2); if (g_strv_length(sd) != 2) { g_set_error(error_r, config_quark(), 1, "Invalid copy around %d in routes spec: %s", param->line, tokens[c]); g_strfreev(sd); g_strfreev(tokens); return false; } source = strtol(sd[0], NULL, 10); dest = strtol(sd[1], NULL, 10); // Keep track of the highest channel numbers seen // as either in- or outputs if (source >= filter->min_input_channels) filter->min_input_channels = source + 1; if (dest >= filter->min_output_channels) filter->min_output_channels = dest + 1; g_strfreev(sd); } if (!audio_valid_channel_count(filter->min_output_channels)) { g_strfreev(tokens); g_set_error(error_r, audio_format_quark(), 0, "Invalid number of output channels requested: %d", filter->min_output_channels); return false; } // Allocate a map of "copy nothing to me" filter->sources = g_malloc(filter->min_output_channels * sizeof(signed char)); for (int i=0; i<filter->min_output_channels; ++i) filter->sources[i] = -1; // Run through the spec again, and save the // actual mapping output <- input for (int c=0; c<number_of_copies; ++c) { // String and int representations of the source/destination gchar **sd; int source, dest; // Split the a>b string into source and destination sd = g_strsplit(tokens[c], ">", 2); if (g_strv_length(sd) != 2) { g_set_error(error_r, config_quark(), 1, "Invalid copy around %d in routes spec: %s", param->line, tokens[c]); g_strfreev(sd); g_strfreev(tokens); return false; } source = strtol(sd[0], NULL, 10); dest = strtol(sd[1], NULL, 10); filter->sources[dest] = source; g_strfreev(sd); } g_strfreev(tokens); return true; }
config_t* load_config(GError** err){ GKeyFile *k = g_key_file_new(); // load config file gchar* config_file = g_build_filename(g_get_home_dir(), ".podcastrc", NULL); if(!g_key_file_load_from_file(k, config_file, G_KEY_FILE_NONE, err)){ g_free(config_file); return NULL; } g_free(config_file); // initialize config structures config_t* config = (config_t*) malloc(sizeof(config_t)); if(config == NULL){ g_set_error(err, config_quark(), CONFIG_ERR_MEMORY_ALLOC, "malloc: Failed to alloc memory for read the config_t* structure.\n"); return NULL; } config->main_config = (main_config_t*) malloc(sizeof(main_config_t)); if(config->main_config == NULL){ g_set_error(err, config_quark(), CONFIG_ERR_MEMORY_ALLOC, "malloc: Failed to alloc memory for read the main_config_t* structure.\n"); return NULL; } // storage variables gsize num_keys; gchar **keys; gchar *tmp = NULL; // load main config parameters if(!g_key_file_has_group(k, "main")){ g_set_error(err, config_quark(), CONFIG_ERR_NO_MAIN, "g_key_file_has_group: Your configuration file should have a [main] section.\n"); return NULL; } keys = g_key_file_get_keys(k, "main", &num_keys, err); for(int i=0; i<num_keys; i++){ tmp = g_key_file_get_value(k, "main", keys[i], err); if(*err != NULL){ return NULL; } if(g_strcmp0(keys[i], "download_command") == 0){ config->main_config->download_command = g_strdup(tmp); g_free(tmp); } else if(g_strcmp0(keys[i], "player_command") == 0){ config->main_config->player_command = g_strdup(tmp); g_free(tmp); } else if(g_strcmp0(keys[i], "media_directory") == 0){ config->main_config->media_directory = g_strdup(tmp); g_free(tmp); } else{ g_set_error(err, config_quark(), CONFIG_INVALID_PARAM, "Invalid configuration parameter: %s.\n", keys[i]); g_free(tmp); return NULL; } } g_strfreev(keys); // load feed urls if(!g_key_file_has_group(k, "podcast")){ g_set_error(err, config_quark(), CONFIG_ERR_NO_PODCAST, "g_key_file_has_group: Your configuration file should have a [podcast] section.\n"); return NULL; } config->podcasts = NULL; keys = g_key_file_get_keys(k, "podcast", &num_keys, err); for(config->pod_len = 0; config->pod_len < num_keys; config->pod_len++){ tmp = g_key_file_get_value(k, "podcast", keys[config->pod_len], err); if(*err != NULL){ return NULL; } config->podcasts = (podcast_t**) realloc( config->podcasts, (config->pod_len + 1) * sizeof(podcast_t*) ); if(config->podcasts == NULL){ g_set_error(err, config_quark(), CONFIG_ERR_MEMORY_ALLOC, "malloc: Failed to alloc memory for read the podcast_t** structure.\n"); return NULL; } config->podcasts[config->pod_len] = (podcast_t*) malloc(sizeof(podcast_t)); if(config->podcasts[config->pod_len] == NULL){ g_set_error(err, config_quark(), CONFIG_ERR_MEMORY_ALLOC, "malloc: Failed to alloc memory for read the podcast_t* structure.\n"); return NULL; } config->podcasts[config->pod_len]->id = g_strdup(keys[config->pod_len]); config->podcasts[config->pod_len]->feed_url = g_strdup(tmp); g_free(tmp); } g_strfreev(keys); g_key_file_free(k); return config; }