mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-05 13:30:02 -05:00
254 lines
6.7 KiB
C
254 lines
6.7 KiB
C
/* PipeWire */
|
|
/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
#include "config.h"
|
|
|
|
#include <getopt.h>
|
|
#include <signal.h>
|
|
#include <locale.h>
|
|
#include <unistd.h>
|
|
|
|
#include <spa/utils/result.h>
|
|
#include <spa/utils/json.h>
|
|
|
|
#include "pipewire/pipewire.h"
|
|
#include "pipewire/log.h"
|
|
|
|
#define DEFAULT_NAME "pipewire.conf"
|
|
#define DEFAULT_PREFIX ""
|
|
|
|
struct data {
|
|
const char *opt_name;
|
|
const char *opt_prefix;
|
|
const char *opt_cmd;
|
|
bool opt_recurse;
|
|
bool opt_newline;
|
|
bool opt_colors;
|
|
struct pw_properties *conf;
|
|
struct pw_properties *assemble;
|
|
int count;
|
|
bool array;
|
|
};
|
|
|
|
static void print_all_properties(struct data *d, struct pw_properties *props)
|
|
{
|
|
pw_properties_serialize_dict(stdout,
|
|
&props->dict,
|
|
(d->opt_newline ? PW_PROPERTIES_FLAG_NL : 0) |
|
|
(d->opt_recurse ? PW_PROPERTIES_FLAG_RECURSE : 0) |
|
|
(d->opt_colors ? PW_PROPERTIES_FLAG_COLORS : 0) |
|
|
(d->array ? PW_PROPERTIES_FLAG_ARRAY : 0) |
|
|
PW_PROPERTIES_FLAG_ENCLOSE);
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
static void list_paths(struct data *d)
|
|
{
|
|
const struct spa_dict_item *it;
|
|
|
|
spa_dict_for_each(it, &d->conf->dict) {
|
|
if (spa_strstartswith(it->key, "config.path")) {
|
|
pw_properties_set(d->assemble, it->key, it->value);
|
|
}
|
|
if (spa_strstartswith(it->key, "override.") &&
|
|
spa_strendswith(it->key, ".config.path")) {
|
|
pw_properties_set(d->assemble, it->key, it->value);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int do_merge_section(void *data, const char *location, const char *section,
|
|
const char *str, size_t len)
|
|
{
|
|
struct data *d = data;
|
|
struct spa_json it[2];
|
|
int l;
|
|
const char *value;
|
|
|
|
spa_json_init(&it[0], str, len);
|
|
if ((l = spa_json_next(&it[0], &value)) <= 0)
|
|
return 0;
|
|
|
|
if (spa_json_is_array(value, l)) {
|
|
char key[128];
|
|
|
|
spa_json_enter(&it[0], &it[1]);
|
|
while ((l = spa_json_next(&it[1], &value)) > 0) {
|
|
if (spa_json_is_container(value, l))
|
|
l = spa_json_container_len(&it[1], value, l);
|
|
|
|
snprintf(key, sizeof(key), "%d-%s", d->count++, location);
|
|
pw_properties_setf(d->assemble, key, "%.*s", l, value);
|
|
}
|
|
d->array = true;
|
|
}
|
|
else if (spa_json_is_object(value, l)) {
|
|
pw_properties_update_string(d->assemble, str, len);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int do_list_section(void *data, const char *location, const char *section,
|
|
const char *str, size_t len)
|
|
{
|
|
struct data *d = data;
|
|
char key[128];
|
|
snprintf(key, sizeof(key), "%d-%s", d->count++, location);
|
|
pw_properties_setf(d->assemble, key, "%.*s", (int)len, str);
|
|
return 0;
|
|
}
|
|
|
|
static void section_for_each(struct data *d, const char *section,
|
|
int (*callback) (void *data, const char *location, const char *section,
|
|
const char *str, size_t len))
|
|
{
|
|
const char *str;
|
|
char key[128];
|
|
|
|
pw_conf_section_for_each(&d->conf->dict, section, callback, d);
|
|
str = pw_properties_get(d->assemble, "config.ext");
|
|
if (str != NULL) {
|
|
snprintf(key, sizeof(key), "%s.%s", section, str);
|
|
pw_conf_section_for_each(&d->conf->dict, key, callback, d);
|
|
}
|
|
}
|
|
|
|
static void show_help(const char *name, bool error)
|
|
{
|
|
fprintf(error ? stderr : stdout, "%1$s : PipeWire config manager.\n"
|
|
"Usage:\n"
|
|
" %1$s [options] paths List config paths (default action)\n"
|
|
" %1$s [options] list [SECTION] List config section\n"
|
|
" %1$s [options] merge SECTION Merge a config section\n\n"
|
|
"Options:\n"
|
|
" -h, --help Show this help\n"
|
|
" --version Show version\n"
|
|
" -n, --name Config Name (default '%2$s')\n"
|
|
" -p, --prefix Config Prefix (default '%3$s')\n"
|
|
" -L, --no-newline Omit newlines after values\n"
|
|
" -r, --recurse Reformat config sections recursively\n"
|
|
" -N, --no-colors disable color output\n"
|
|
" -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n",
|
|
name, DEFAULT_NAME, DEFAULT_PREFIX);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct data d = { 0, };
|
|
struct pw_properties *props = NULL;
|
|
int res = 0, c;
|
|
static const struct option long_options[] = {
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "version", no_argument, NULL, 'V' },
|
|
{ "name", required_argument, NULL, 'n' },
|
|
{ "prefix", required_argument, NULL, 'p' },
|
|
{ "no-newline", no_argument, NULL, 'L' },
|
|
{ "recurse", no_argument, NULL, 'r' },
|
|
{ "no-colors", no_argument, NULL, 'N' },
|
|
{ "color", optional_argument, NULL, 'C' },
|
|
{ NULL, 0, NULL, 0}
|
|
};
|
|
|
|
d.opt_name = DEFAULT_NAME;
|
|
d.opt_prefix = NULL;
|
|
d.opt_recurse = false;
|
|
d.opt_newline = true;
|
|
if (isatty(fileno(stdout)) && getenv("NO_COLOR") == NULL)
|
|
d.opt_colors = true;
|
|
d.opt_cmd = "paths";
|
|
|
|
pw_init(&argc, &argv);
|
|
|
|
while ((c = getopt_long(argc, argv, "hVn:p:LrNC", long_options, NULL)) != -1) {
|
|
switch (c) {
|
|
case 'h':
|
|
show_help(argv[0], false);
|
|
return 0;
|
|
case 'V':
|
|
printf("%s\n"
|
|
"Compiled with libpipewire %s\n"
|
|
"Linked with libpipewire %s\n",
|
|
argv[0],
|
|
pw_get_headers_version(),
|
|
pw_get_library_version());
|
|
return 0;
|
|
case 'n':
|
|
d.opt_name = optarg;
|
|
break;
|
|
case 'p':
|
|
d.opt_prefix = optarg;
|
|
break;
|
|
case 'L':
|
|
d.opt_newline = false;
|
|
break;
|
|
case 'r':
|
|
d.opt_recurse = true;
|
|
break;
|
|
case 'N' :
|
|
d.opt_colors = false;
|
|
break;
|
|
case 'C' :
|
|
if (optarg == NULL || !strcmp(optarg, "auto"))
|
|
break; /* nothing to do, tty detection was done
|
|
before parsing options */
|
|
else if (!strcmp(optarg, "never"))
|
|
d.opt_colors = false;
|
|
else if (!strcmp(optarg, "always"))
|
|
d.opt_colors = true;
|
|
else {
|
|
fprintf(stderr, "Unknown color: %s\n", optarg);
|
|
show_help(argv[0], true);
|
|
return -1;
|
|
}
|
|
break;
|
|
default:
|
|
show_help(argv[0], true);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (optind < argc)
|
|
d.opt_cmd = argv[optind++];
|
|
|
|
props = pw_properties_new(
|
|
PW_KEY_CONFIG_NAME, d.opt_name,
|
|
PW_KEY_CONFIG_PREFIX, d.opt_prefix,
|
|
NULL);
|
|
|
|
d.conf = pw_properties_new(NULL, NULL);
|
|
if ((res = pw_conf_load_conf_for_context (props, d.conf)) < 0) {
|
|
fprintf(stderr, "error loading config: %s\n", spa_strerror(res));
|
|
goto done;
|
|
}
|
|
|
|
d.assemble = pw_properties_new(NULL, NULL);
|
|
|
|
if (spa_streq(d.opt_cmd, "paths")) {
|
|
list_paths(&d);
|
|
}
|
|
else if (spa_streq(d.opt_cmd, "list")) {
|
|
if (optind == argc) {
|
|
pw_properties_update(d.assemble, &d.conf->dict);
|
|
} else {
|
|
section_for_each(&d, argv[optind++], do_list_section);
|
|
}
|
|
}
|
|
else if (spa_streq(d.opt_cmd, "merge")) {
|
|
if (optind == argc) {
|
|
fprintf(stderr, "%s requires a section\n", d.opt_cmd);
|
|
res = -EINVAL;
|
|
goto done;
|
|
}
|
|
section_for_each(&d, argv[optind++], do_merge_section);
|
|
}
|
|
print_all_properties(&d, d.assemble);
|
|
|
|
done:
|
|
pw_properties_free(d.conf);
|
|
pw_properties_free(d.assemble);
|
|
pw_properties_free(props);
|
|
|
|
pw_deinit();
|
|
return res;
|
|
}
|