/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/*
Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt
SPDX-License-Identifier: BSD-2-Clause-FreeBSD
SPDX-FileCopyrightText: 2021 The Libfossil Authors
SPDX-ArtifactOfProjectName: Libfossil
SPDX-FileType: Code
Heavily indebted to the Fossil SCM project (https://fossil-scm.org).
*****************************************************************************
A test/demo app for working with the libfossil config db API.
*/
#include "libfossil.h"
static struct ConfigApp {
bool doGlobal;
bool doLocal;
bool doRepo;
bool showValues;
const char * lsGlob;
fsl_confdb_e mode0;
} ConfigApp = {
0,0,0,
0/*showValues*/,
NULL/*lsGlob*/,
FSL_CONFDB_GLOBAL/*mode0*/
};
static fcli_cliflag cliFlagsGlobal[] = {
FCLI_FLAG_BOOL("g", "global", &ConfigApp.doGlobal,
"Use the global config db."),
FCLI_FLAG_BOOL("c", "checkout", &ConfigApp.doLocal,
"Use the checkout-local config."),
FCLI_FLAG_BOOL("r", "repository", &ConfigApp.doRepo,
"Use the repository-local config."),
fcli_cliflag_empty_m
};
static fcli_cliflag cliFlagsLs[] = {
FCLI_FLAG(0, "glob", "string", &ConfigApp.lsGlob,
"Only list properties matching this glob."),
FCLI_FLAG_BOOL("v", "values", &ConfigApp.showValues,
"List property values."),
fcli_cliflag_empty_m
};
static fcli_cliflag cliFlagsSet[] = {
fcli_cliflag_empty_m
};
static fcli_cliflag cliFlagsUnset[] = {
// TODO?: --glob to unset all matching a glob.
fcli_cliflag_empty_m
};
static int fapp_ls(fsl_cx * f, fsl_confdb_e mode,
char const * nameGlob, char showVals){
int rc;
fsl_db * db = fsl_config_for_role(f, mode);
fsl_stmt st = fsl_stmt_empty;
int nameWidth = -30;
if(!db){
if(FSL_CONFDB_GLOBAL==mode){ /* special case: auto-open config db */
rc = fsl_config_open(f, NULL);
if(!rc){
db = fsl_cx_db_config(f);
assert(db);
}
}
if(!db){
return fcli_err_set(FSL_RC_MISUSE,
"Config db role #%d is not opened.",
mode);
}
}
rc = fsl_db_prepare(db, &st, "SELECT name, value FROM %s ORDER BY name",
fsl_config_table_for_role(mode));
while(FSL_RC_STEP_ROW==fsl_stmt_step(&st)){
char const * name = fsl_stmt_g_text(&st, 0, NULL);
if(nameGlob && !fsl_str_glob(nameGlob, name)) continue;
if(showVals){
f_out("%*s %s\n",
nameWidth, name,
fsl_stmt_g_text(&st,1,NULL));
}else{
f_out("%s\n", name);
}
}
fsl_stmt_finalize(&st);
return rc;
}
static int fcmd_ls(fcli_command const *fc){
fsl_cx * f = fcli_cx();
int didSomething = 0;
int rc = fcli_process_flags(fc->flags);
if(rc || (rc = fcli_has_unused_flags(0))){
return rc;
}
/* f_out("Global config db: %s\n", fsl_cx_db_file_config(f, NULL)); */
#define DUMP(MODE) \
++didSomething; \
rc = fapp_ls(f, MODE, ConfigApp.lsGlob, ConfigApp.showValues)
if(ConfigApp.doGlobal){
DUMP(FSL_CONFDB_GLOBAL);
}
if(!rc && ConfigApp.doRepo){
DUMP(FSL_CONFDB_REPO);
}
if(!rc && ConfigApp.doLocal){
DUMP(FSL_CONFDB_CKOUT);
}
#undef DUMP
assert(didSomething);
return rc;
}
static int fcmd_set(fcli_command const *fc){
const char * key = NULL;
const char * val = NULL;
fsl_cx * f = fcli_cx();
int rc = fcli_process_flags(fc->flags);
if(rc || (rc = fcli_has_unused_flags(0))){
return rc;
}
key = fcli_next_arg(1);
if(!key){
return fcli_err_set(FSL_RC_MISUSE,"Missing property key argument.");
}
val = fcli_next_arg(1);
if(val){
FCLI_V(("Setting %s = %s\n", key, val));
rc = fsl_config_set_text(f, ConfigApp.mode0, key, val);
}else{
char * v = fsl_config_get_text(f, ConfigApp.mode0, key, 0);
if(v){
f_out("%s\n", v);
fsl_free(v);
}else{
rc = fcli_err_set(FSL_RC_NOT_FOUND,
"Config key not found: %s", key);
}
}
return rc;
}
static int fcmd_unset(fcli_command const *fc){
const char * key = NULL;
fsl_cx * f = fcli_cx();
unsigned int count = 0;
int rc = fcli_process_flags(fc->flags);
if(rc || (rc = fcli_has_unused_flags(0))){
return rc;
}
rc = fsl_config_transaction_begin(f, ConfigApp.mode0);
if(!rc){
while( !rc && (key = fcli_next_arg(1)) ){
++count;
FCLI_V(("Unsetting: %s\n", key));
rc = fsl_config_unset(f, ConfigApp.mode0, key);
}
fsl_config_transaction_end(f, ConfigApp.mode0, rc ? 1 : 0);
}
if(!count){
rc = fcli_err_set(FSL_RC_MISUSE,"Missing property key argument(s).");
}
return rc;
}
static const fcli_command ConfigCmds[] = {
{"ls", NULL, "List config properties.",
fcmd_ls, NULL, cliFlagsLs},
{"set", NULL, "Get/set individual config properties.",
fcmd_set, NULL, cliFlagsSet},
{"unset", "us\0", "Unset config properties. "
"Pass it all property keys to unset.",
fcmd_unset, NULL, cliFlagsUnset},
{NULL,NULL,NULL,NULL}
};
static void fcli_local_help(void){
puts("Config commands and their options:\n");
fcli_command_help(ConfigCmds, false, false);
}
static int fapp_main(fsl_cx * const f){
/*
Set the main config mode for commands which use only
one config. Priority order: -c -r -g
*/
if(ConfigApp.doGlobal){
int rc;
ConfigApp.mode0 = FSL_CONFDB_GLOBAL;
rc = fsl_config_open( f, NULL );
if(rc) return rc;
}
else if(ConfigApp.doLocal) ConfigApp.mode0 = FSL_CONFDB_CKOUT;
else if(ConfigApp.doRepo) ConfigApp.mode0 = FSL_CONFDB_REPO;
else{
return fcli_err_set(FSL_RC_MISUSE,
"No config db specified. Use one of: "
"-g[lobal], -r[epo], -c[heckout]");
}
return fcli_dispatch_commands(ConfigCmds, 0);
}
int main(int argc, char const * const * argv ){
int rc = 0;
fcli_help_info FCliHelp = {
"List and manipulate fossil configuration vars.",
"ls|set|unset [options]",
fcli_local_help
};
rc = fcli_setup_v2(argc, argv, cliFlagsGlobal, &FCliHelp);
if(!rc){
rc = fapp_main(fcli_cx());
}
return fcli_end_of_main(rc);
}