Repositories / jai.git
jai.git
Clone (read-only): git clone http://git.guha-anderson.com/git/jai.git
@@ -14,7 +14,10 @@ jai.1: jai.1.md sysusersdir = $(prefix)/lib/sysusers.d sysusers_DATA = jai.conf -EXTRA_DIST = jai.1 jai.1.md jai.conf.in logo.svg +bashcompdir = $(datadir)/bash-completion/helpers +bashcomp_DATA = bash-completion/jai + +EXTRA_DIST = jai.1 jai.1.md jai.conf.in jai.bash logo.svg CLEANFILES = *~ DISTCLEANFILES = jai.conf @@ -36,11 +39,13 @@ install-exec-hook: -chmod 04511 $(DESTDIR)$(bindir)/jai install-data-hook: - test ! -r $(DESTDIR)/etc/passwd || systemd-sysusers --root=$(DESTDIR)/ + test ! -r "$(DESTDIR)/etc/passwd" || \ + systemd-sysusers --root="$(DESTDIR)/" uninstall-hook: - @if gecos=$$(getent passwd @UNTRUSTED_USER@ 2>/dev/null | cut -d: -f5) \ + @if test -r "$(DESTDIR)/etc/passwd" && \ + gecos=$$(getent passwd @UNTRUSTED_USER@ 2>/dev/null | cut -d: -f5) \ && test "$$gecos" = "JAI sandbox untrusted user"; then \ echo "userdel @UNTRUSTED_USER@"; \ - userdel @UNTRUSTED_USER@; \ + userdel -R "$(DESTDIR)/" @UNTRUSTED_USER@; \ fi
@@ -0,0 +1,36 @@ +# -*- shell-script -*- + +_jai() +{ + local cur prev words cword + _init_completion -n = || return + + # Ask jai itself for completions. Pass every word except $0, + # including the current (possibly incomplete) word. + local -a comp_args=( "${words[@]:1}" ) + local output + output=$(jai --complete "${comp_args[@]}" 2>/dev/null) || return + + # If jai returns the special completion "_command_offset N", it + # means subcommand starts at offset N, so delegate to + # bash-completion's _command_offset which handles completing an + # arbitrary command (c.f. sudo). + if [[ $output == _command_offset\ * ]]; then + local offset=${output#_command_offset } + _command_offset "$offset" + return + fi + + # Otherwise jai returned sorted candidate completions, one per + # line, including a trailing space if the argument is complete, so + # no need for bash to add spaces. + compopt -o nospace -o nosort + + COMPREPLY=() + while IFS= read -r line; do + # Note bash COMP_WORDBREAKS splits on '=' and only completes + # the part after it, so we need to strip off the first =. + COMPREPLY+=( "${line#--*=}" ) + done <<< "$output" +} && +complete -F _jai jai
@@ -46,7 +46,7 @@ complete_path(int dfd, CompSet &c, bool dir_only) if (dir_only && de->d_type != DT_DIR) continue; c.output("{}{}", (arg.parent_path() / d_name(de)).string(), - de->d_type == DT_DIR ? "/" : ""); + de->d_type == DT_DIR ? "/" : " "); } } @@ -76,9 +76,9 @@ complete_env(CompSet &c, bool eq) if (auto pos = var.find('='); pos == var.npos) continue; else - var = var.substr(0, eq ? pos + 1 : pos); + var = var.substr(0, pos); if (var.starts_with(arg)) - c.output("{}", var); + c.output("{}{}", var, eq ? "=" : " "); } } @@ -87,7 +87,7 @@ Config::complete(Completions c) { using enum Completions::Disposition; if (c.kind >= 0) { - std::println("_command_offset {}", c.kind); + std::println("_command_offset {}", c.kind - 1); return 0; } if (c.kind == kNoCompletions) @@ -106,7 +106,7 @@ Config::complete(Completions c) auto arg = c.arg(); for (std::string_view sv : {"casual", "strict", "bare"}) { if (sv.starts_with(arg)) - cs.output("{}", sv); + cs.output("{} ", sv); } } else if (std::ranges::contains(