From bf6db4e57895430d84503e800cec9e8b407212a8 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 6 May 2021 17:00:00 -0400 Subject: Added a real configuration file (plus a dependency on libconfuse) --- Makefile | 45 +++++++++--------- configure | 5 +- files/dmtool.bash | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ files/dmtool.conf | 13 +++++ src/dmtool.bash | 139 ------------------------------------------------------ src/dmtool.cc | 23 ++++----- src/settings.cc | 56 +++++++++------------- 7 files changed, 212 insertions(+), 208 deletions(-) create mode 100644 files/dmtool.bash create mode 100644 files/dmtool.conf delete mode 100644 src/dmtool.bash diff --git a/Makefile b/Makefile index 0fd8730..5ea8202 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC=g++ -LIBS= +LIBS=libconfuse CFLAGS=-c -Wall -fPIC -std=c++20 LDFLAGS= SOURCES=src/armor.cc src/cmd.cc src/cmd_fsops.cc src/cmd_manipulate.cc src/cmd_query.cc src/cmd_usage.cc src/creature.cc src/dice.cc src/dmtool.cc src/entry.cc src/feature.cc src/item.cc src/rules.cc src/settings.cc src/spellcasting.cc src/spell.cc src/utils.cc src/weapon.cc @@ -21,87 +21,88 @@ install: $(EXECUTABLE) parsed cp -r parsed/* $(DESTDIR)$(PREFIX)/share/dmtool/ install -d $(DESTDIR)$(PREFIX)/bin/ install -m 755 $(EXECUTABLE) $(DESTDIR)$(PREFIX)/bin/ - install -m 644 src/dmtool.bash /usr/share/bash-completion/completions/dmtool + install -m 644 files/dmtool.bash $(DESTDIR)/usr/share/bash-completion/completions/dmtool + install -m 644 files/dmtool.conf $(DESTDIR)/etc/dmtool.conf $(EXECUTABLE): $(OBJECTS) - $(CC) $(OBJECTS) -o $@ $(LDFLAGS) + $(CC) $(OBJECTS) -o $@ $(LDFLAGS) `pkg-config $(LIBS) --libs` src/armor.o: src/armor.cc src/armor.h src/item.h src/json.hpp src/entry.h \ src/jsonable.h src/creature.h src/rules.h src/utils.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/cmd.o: src/cmd.cc src/cmd.h src/settings.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/cmd_fsops.o: src/cmd_fsops.cc src/cmd.h src/utils.h src/json.hpp \ src/entry.h src/jsonable.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/cmd_manipulate.o: src/cmd_manipulate.cc src/cmd.h src/utils.h \ src/json.hpp src/entry.h src/jsonable.h src/creature.h src/rules.h \ - src/item.h src/spellcasting.h src/feature.h src/spell.h - $(CC) $(CFLAGS) $< -o $@ + src/item.h src/spellcasting.h src/feature.h src/spell.h src/settings.h + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/cmd_query.o: src/cmd_query.cc src/cmd.h src/utils.h src/json.hpp \ src/entry.h src/jsonable.h src/creature.h src/rules.h src/dice.h \ src/weapon.h src/item.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/cmd_usage.o: src/cmd_usage.cc src/cmd.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/creature.o: src/creature.cc src/creature.h src/json.hpp src/rules.h \ src/jsonable.h src/utils.h src/entry.h src/dice.h src/feature.h \ src/weapon.h src/item.h src/armor.h src/attack.h src/spellcasting.h \ src/spell.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/dice.o: src/dice.cc src/dice.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/dmtool.o: src/dmtool.cc src/cmd.h src/utils.h src/json.hpp \ src/entry.h src/jsonable.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/entry.o: src/entry.cc src/entry.h src/json.hpp src/jsonable.h \ src/utils.h src/feature.h src/item.h src/spell.h src/creature.h \ src/rules.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/feature.o: src/feature.cc src/json.hpp src/feature.h src/entry.h \ src/jsonable.h src/spellcasting.h src/spell.h src/rules.h src/utils.h \ src/attack.h src/weapon.h src/item.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/item.o: src/item.cc src/json.hpp src/item.h src/entry.h \ src/jsonable.h src/weapon.h src/rules.h src/utils.h src/armor.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/rules.o: src/rules.cc src/rules.h src/jsonable.h src/json.hpp \ src/utils.h src/entry.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/settings.o: src/settings.cc src/settings.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/spellcasting.o: src/spellcasting.cc src/spellcasting.h src/feature.h \ src/json.hpp src/entry.h src/jsonable.h src/spell.h src/rules.h \ src/utils.h src/creature.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/spell.o: src/spell.cc src/spell.h src/json.hpp src/entry.h \ src/jsonable.h src/utils.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/utils.o: src/utils.cc src/utils.h src/json.hpp src/entry.h \ src/jsonable.h src/settings.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` src/weapon.o: src/weapon.cc src/weapon.h src/item.h src/json.hpp \ src/entry.h src/jsonable.h src/rules.h src/utils.h src/creature.h \ src/dice.h - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` clean: rm -f src/*.o $(LIBRARY) $(EXECUTABLE) diff --git a/configure b/configure index 70217f2..5000f93 100755 --- a/configure +++ b/configure @@ -8,7 +8,7 @@ EXECUTABLE="dmtool" LIBRARY= # List of libs as given to pkg-config -LIBS= +LIBS="libconfuse" CFLAGS="-c -Wall -fPIC -std=c++20" @@ -28,7 +28,8 @@ install: \$(EXECUTABLE) parsed cp -r parsed/* \$(DESTDIR)\$(PREFIX)/share/dmtool/ install -d \$(DESTDIR)\$(PREFIX)/bin/ install -m 755 \$(EXECUTABLE) \$(DESTDIR)\$(PREFIX)/bin/ - install -m 644 src/dmtool.bash `pkg-config --variable=completionsdir bash-completion`/dmtool + install -m 644 files/dmtool.bash \$(DESTDIR)`pkg-config --variable=completionsdir bash-completion`/dmtool + install -m 644 files/dmtool.conf \$(DESTDIR)/etc/dmtool.conf " # Below here shouldn't need editing diff --git a/files/dmtool.bash b/files/dmtool.bash new file mode 100644 index 0000000..ffc75f7 --- /dev/null +++ b/files/dmtool.bash @@ -0,0 +1,139 @@ +# bash completion file for dmtool + +# Modified from bash completion for password-store + +_dmtool_complete_entries () { + local prefix1="${HOME}/.dmtool/" + prefix1="${prefix1%/}/" + local prefix2="/usr/share/dmtool/" + prefix2="${prefix2%/}/" + local suffix=".json" + local autoexpand=${1:-0} + + local IFS=$'\n' + local items=($(compgen -f $prefix1$cur)) + items+=($(compgen -f $prefix2$cur)) + + # Remember the value of the first item, to see if it is a directory. If + # it is a directory, then don't add a space to the completion + local firstitem="" + # Use counter, can't use ${#items[@]} as we skip hidden directories + local i=0 item + + for item in ${items[@]}; do + [[ $item =~ /\.[^/]*$ ]] && continue + + # if there is a unique match, and it is a directory with one entry + # autocomplete the subentry as well (recursively) + if [[ ${#items[@]} -eq 1 && $autoexpand -eq 1 ]]; then + while [[ -d $item ]]; do + local subitems=($(compgen -f "$item/")) + local filtereditems=( ) item2 + for item2 in "${subitems[@]}"; do + [[ $item2 =~ /\.[^/]*$ ]] && continue + filtereditems+=( "$item2" ) + done + if [[ ${#filtereditems[@]} -eq 1 ]]; then + item="${filtereditems[0]}" + else + break + fi + done + fi + + # append / to directories + [[ -d $item ]] && item="$item/" + + item="${item%$suffix}" + local prefix1ed="${item#$prefix1}" + COMPREPLY+=("${prefix1ed#$prefix2}") + if [[ $i -eq 0 ]]; then + firstitem=$item + fi + let i+=1 + done + + # The only time we want to add a space to the end is if there is only + # one match, and it is not a directory + if [[ $i -gt 1 || ( $i -eq 1 && -d $firstitem ) ]]; then + compopt -o nospace + fi +} + +_dmtool_skills() +{ + echo -e "athletics\nacrobatics\nstealth\narcana\nhistory\ninvestigation\nnature\nreligion\ninsight\nmedicine\nperception\nsurvival\ndeception\nintimidation\nperformance\npersuasion\nsleight of hand\nanimal handling" +} + +_dmtool_abilities() +{ + echo -e "str\ndex\ncon\nint\nwis\ncha" +} + +_dmtool_complete_skills_abilities() +{ + local IFS=$'\n' + COMPREPLY+=($(compgen -W "$(_dmtool_skills)" -- ${cur})) + COMPREPLY+=($(compgen -W "$(_dmtool_abilities)" -- ${cur})) +} + +_dmtool() +{ + COMPREPLY=() + local cur="${COMP_WORDS[COMP_CWORD]}" + local commands="ls cp mkdir mv rm attacks roll damage heal reset set edit add del help" + if [[ $COMP_CWORD -gt 1 ]]; then + local lastarg="${COMP_WORDS[$COMP_CWORD-1]}" + case "${COMP_WORDS[1]}" in + ls|mkdir|rm|reset) + _dmtool_complete_entries + ;; + cp|mv|add) + if [[ $COMP_CWORD -le 3 ]]; then + _dmtool_complete_entries + fi + ;; + attacks|roll|damage|heal|set|edit|del) + if [[ $COMP_CWORD -le 2 ]]; then + _dmtool_complete_entries + else + case "${COMP_WORDS[1]}" in + roll) + _dmtool_complete_skills_abilities + # Add in attacks + local IFS=$'\n' + opts="$(${COMP_WORDS[0]} attacks ${COMP_WORDS[2]})" + COMPREPLY+=($(compgen -W "$opts" -- ${cur})) + ;; + damage) + COMPREPLY+=($(compgen -W "--magical -m --silvered -s --adamantine -a" -- ${cur})) + if [[ $COMP_CWORD -eq 4 ]]; then + COMPREPLY+=($(compgen -W "slashing piercing bludgeoning poison acid fire cold radiant necrotic lightning thunder force psychic" -- ${cur})) + fi + ;; + set) + if [[ $COMP_CWORD -eq 3 ]]; then + _dmtool_complete_skills_abilities + COMPREPLY+=($(compgen -W "proficiency name" -- ${cur})) + elif [[ $COMP_CWORD -eq 4 ]]; then + local skills="$(_dmtool_skills)" + if [[ "$skills" =~ "$lastarg" ]]; then + COMPREPLY+=($(compgen -W "none proficient expert" -- ${cur})) + fi + fi + ;; + del) + #TODO: Add items and spells + _dmtool_complete_skills_abilities + ;; + esac + fi + ;; + esac + else + COMPREPLY+=($(compgen -W "${commands}" -- ${cur})) + _dmtool_complete_entries 1 + fi +} + +complete -o filenames -F _dmtool dmtool diff --git a/files/dmtool.conf b/files/dmtool.conf new file mode 100644 index 0000000..87ea4e5 --- /dev/null +++ b/files/dmtool.conf @@ -0,0 +1,13 @@ +# This is the configuration file for dmtool + +# Directories to search for PHB entries +weapons = "/usr/share/dmtool/weapons/" +armor = "/usr/share/dmtool/armor/" +spells = "/usr/share/dmtool/spells/" +creatures = "/usr/share/dmtool/creatures/" + +# Directory to save all custom entries +savedir = "${HOME}/.dmtool/" + +# Editor to use in the "dmtool edit" command +editor = "vi" diff --git a/src/dmtool.bash b/src/dmtool.bash deleted file mode 100644 index ffc75f7..0000000 --- a/src/dmtool.bash +++ /dev/null @@ -1,139 +0,0 @@ -# bash completion file for dmtool - -# Modified from bash completion for password-store - -_dmtool_complete_entries () { - local prefix1="${HOME}/.dmtool/" - prefix1="${prefix1%/}/" - local prefix2="/usr/share/dmtool/" - prefix2="${prefix2%/}/" - local suffix=".json" - local autoexpand=${1:-0} - - local IFS=$'\n' - local items=($(compgen -f $prefix1$cur)) - items+=($(compgen -f $prefix2$cur)) - - # Remember the value of the first item, to see if it is a directory. If - # it is a directory, then don't add a space to the completion - local firstitem="" - # Use counter, can't use ${#items[@]} as we skip hidden directories - local i=0 item - - for item in ${items[@]}; do - [[ $item =~ /\.[^/]*$ ]] && continue - - # if there is a unique match, and it is a directory with one entry - # autocomplete the subentry as well (recursively) - if [[ ${#items[@]} -eq 1 && $autoexpand -eq 1 ]]; then - while [[ -d $item ]]; do - local subitems=($(compgen -f "$item/")) - local filtereditems=( ) item2 - for item2 in "${subitems[@]}"; do - [[ $item2 =~ /\.[^/]*$ ]] && continue - filtereditems+=( "$item2" ) - done - if [[ ${#filtereditems[@]} -eq 1 ]]; then - item="${filtereditems[0]}" - else - break - fi - done - fi - - # append / to directories - [[ -d $item ]] && item="$item/" - - item="${item%$suffix}" - local prefix1ed="${item#$prefix1}" - COMPREPLY+=("${prefix1ed#$prefix2}") - if [[ $i -eq 0 ]]; then - firstitem=$item - fi - let i+=1 - done - - # The only time we want to add a space to the end is if there is only - # one match, and it is not a directory - if [[ $i -gt 1 || ( $i -eq 1 && -d $firstitem ) ]]; then - compopt -o nospace - fi -} - -_dmtool_skills() -{ - echo -e "athletics\nacrobatics\nstealth\narcana\nhistory\ninvestigation\nnature\nreligion\ninsight\nmedicine\nperception\nsurvival\ndeception\nintimidation\nperformance\npersuasion\nsleight of hand\nanimal handling" -} - -_dmtool_abilities() -{ - echo -e "str\ndex\ncon\nint\nwis\ncha" -} - -_dmtool_complete_skills_abilities() -{ - local IFS=$'\n' - COMPREPLY+=($(compgen -W "$(_dmtool_skills)" -- ${cur})) - COMPREPLY+=($(compgen -W "$(_dmtool_abilities)" -- ${cur})) -} - -_dmtool() -{ - COMPREPLY=() - local cur="${COMP_WORDS[COMP_CWORD]}" - local commands="ls cp mkdir mv rm attacks roll damage heal reset set edit add del help" - if [[ $COMP_CWORD -gt 1 ]]; then - local lastarg="${COMP_WORDS[$COMP_CWORD-1]}" - case "${COMP_WORDS[1]}" in - ls|mkdir|rm|reset) - _dmtool_complete_entries - ;; - cp|mv|add) - if [[ $COMP_CWORD -le 3 ]]; then - _dmtool_complete_entries - fi - ;; - attacks|roll|damage|heal|set|edit|del) - if [[ $COMP_CWORD -le 2 ]]; then - _dmtool_complete_entries - else - case "${COMP_WORDS[1]}" in - roll) - _dmtool_complete_skills_abilities - # Add in attacks - local IFS=$'\n' - opts="$(${COMP_WORDS[0]} attacks ${COMP_WORDS[2]})" - COMPREPLY+=($(compgen -W "$opts" -- ${cur})) - ;; - damage) - COMPREPLY+=($(compgen -W "--magical -m --silvered -s --adamantine -a" -- ${cur})) - if [[ $COMP_CWORD -eq 4 ]]; then - COMPREPLY+=($(compgen -W "slashing piercing bludgeoning poison acid fire cold radiant necrotic lightning thunder force psychic" -- ${cur})) - fi - ;; - set) - if [[ $COMP_CWORD -eq 3 ]]; then - _dmtool_complete_skills_abilities - COMPREPLY+=($(compgen -W "proficiency name" -- ${cur})) - elif [[ $COMP_CWORD -eq 4 ]]; then - local skills="$(_dmtool_skills)" - if [[ "$skills" =~ "$lastarg" ]]; then - COMPREPLY+=($(compgen -W "none proficient expert" -- ${cur})) - fi - fi - ;; - del) - #TODO: Add items and spells - _dmtool_complete_skills_abilities - ;; - esac - fi - ;; - esac - else - COMPREPLY+=($(compgen -W "${commands}" -- ${cur})) - _dmtool_complete_entries 1 - fi -} - -complete -o filenames -F _dmtool dmtool diff --git a/src/dmtool.cc b/src/dmtool.cc index 30f73b5..3c5d3a1 100644 --- a/src/dmtool.cc +++ b/src/dmtool.cc @@ -41,18 +41,19 @@ std::vector extractFlags(std::vector& args) { } int main(int argc, char *argv[]) { - std::string exename = argv[0]; - std::vector args(&argv[1], &argv[argc]); - std::vector flags = extractFlags(args); - cmd::mkdir({cmd::getTruePath("")}); - if(args.empty()) { - std::cout << cmd::list(args); - return 0; - } - std::string cmd = args[0]; - std::vector argsOrig(args); - args.erase(args.begin()); + // Full thing is surrounded in try/catch to pleasantly print out any errors try { + std::string exename = argv[0]; + std::vector args(&argv[1], &argv[argc]); + std::vector flags = extractFlags(args); + cmd::mkdir({cmd::getTruePath("")}); + if(args.empty()) { + std::cout << cmd::list(args); + return 0; + } + std::string cmd = args[0]; + std::vector argsOrig(args); + args.erase(args.begin()); checkArgs(cmd, args); if(cmd == "ls") std::cout << cmd::list(args); else if(cmd == "cp") std::cout << cmd::cp(args); diff --git a/src/settings.cc b/src/settings.cc index 380ae62..ba15945 100644 --- a/src/settings.cc +++ b/src/settings.cc @@ -3,44 +3,32 @@ #include #include #include +#include +#include namespace settings { - const std::map dummySettings { - {"weapons", "/usr/share/dmtool/weapons/"}, - {"armor", "/usr/share/dmtool/armor/"}, - {"spells", "/usr/share/dmtool/spells/"}, - {"creatures", "/usr/share/dmtool/creatures/"}, - {"savedir", "~/.dmtool/"}, - {"editor", "vi"} - }; - - - // Update the input string. - // Obtained from https://stackoverflow.com/a/23442780 - void autoExpandEnvironmentVariables(std::string& text) { - std::size_t tilde; - while((tilde = text.find("~")) != std::string::npos) { - text.replace(tilde, tilde+1, "${HOME}"); - } - static std::regex env("\\$(?:\\{([^}]+)\\}|([^/]+))"); - std::smatch match; - while(std::regex_search(text, match, env)) { - std::string matchStr = match[1].str().empty()? match[2].str() : match[1].str(); - auto s = getenv(matchStr.c_str()); - const std::string var(s == NULL? "" : s); - text.replace(match[0].first, match[0].second, var); - } - } - - // Returns the setting, or an exception in the case of an error std::string getString(const std::string& key) { - if(! dummySettings.contains(key)) { - throw std::invalid_argument("Cannot find key: \"" + key + "\""); + cfg_opt_t opts[] = { + CFG_STR("weapons", "/usr/share/dmtool/weapons/", CFGF_NONE), + CFG_STR("armor", "/usr/share/dmtool/armor/", CFGF_NONE), + CFG_STR("spells", "/usr/share/dmtool/spells/", CFGF_NONE), + CFG_STR("creatures", "/usr/share/dmtool/creatures/", CFGF_NONE), + CFG_STR("savedir", NULL, CFGF_NONE), + CFG_STR("editor", "vi", CFGF_NONE), + CFG_END() + }; + cfg_t *cfg = cfg_init(opts, CFGF_IGNORE_UNKNOWN); + if(cfg_parse(cfg, "/etc/dmtool.conf") == CFG_PARSE_ERROR) { + throw std::runtime_error("Configuration file /etc/dmtool.conf could not be read: " + std::string(strerror(errno))); + } + try { + std::string result(cfg_getstr(cfg, key.c_str())); + cfg_free(cfg); + return result; + } catch(std::exception& e) { + throw std::runtime_error("Cannot find key in configuration file: \"" + key + "\""); } - std::string ret = dummySettings.at(key); - autoExpandEnvironmentVariables(ret); - return ret; } - + const std::vector objectTypes {"weapons", "armor", "spells"}; } -- cgit v1.2.3