From b75bcd93e9e26b7fe8c7427687e0006f068f305c Mon Sep 17 00:00:00 2001 From: Ahmad Khalifa Date: Wed, 10 Sep 2025 17:22:24 +0100 Subject: [PATCH] Add shell completion for mkdwarfs (bash, zsh) - Basic completion for common shells for mkdwarfs command. - Installed only when WITH_TOOLS=ON and NOT WIN32. - Potentially other commands could be added, but mkdwarfs is the most complicated of them. - It's not perfect completion and could use some improvements to "compression" options that allow a sophisticated syntax including category/algo/algo_options. --- CMakeLists.txt | 12 +++ doc/completions/bash/_mkdwarfs | 165 +++++++++++++++++++++++++++++++++ doc/completions/zsh/_mkdwarfs | 101 ++++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 doc/completions/bash/_mkdwarfs create mode 100644 doc/completions/zsh/_mkdwarfs diff --git a/CMakeLists.txt b/CMakeLists.txt index 32f56e481..c12571b2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,11 @@ if(WITH_ALL_BENCHMARKS) set(WITH_BENCHMARKS ON) endif() +if(NOT WIN32) + set(BASH_INSTALL_PATH "bash-completion/completions" CACHE STRING "Bash completion install dir") + set(ZSH_INSTALL_PATH "zsh/site-functions" CACHE STRING "Zsh completion install dir") +endif() + set(DWARFS_CXX_STANDARD 20) # Libraries that we can fetch on demand if necessary @@ -295,6 +300,13 @@ if(WITH_TOOLS) endif() endforeach() + if(NOT WIN32) + file(GLOB SHELLCOMP_BASH_FILES ${CMAKE_CURRENT_SOURCE_DIR}/doc/completions/bash/*) + file(GLOB SHELLCOMP_ZSH_FILES ${CMAKE_CURRENT_SOURCE_DIR}/doc/completions/zsh/*) + install(FILES "${SHELLCOMP_BASH_FILES}" DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${BASH_INSTALL_PATH}) + install(FILES "${SHELLCOMP_ZSH_FILES}" DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${ZSH_INSTALL_PATH}) + endif() + target_link_libraries(mkdwarfs_main PRIVATE dwarfs_reader dwarfs_writer dwarfs_rewrite) target_link_libraries(dwarfsck_main PRIVATE dwarfs_reader) target_link_libraries(dwarfsextract_main PRIVATE dwarfs_extractor) diff --git a/doc/completions/bash/_mkdwarfs b/doc/completions/bash/_mkdwarfs new file mode 100644 index 000000000..dc8e1614c --- /dev/null +++ b/doc/completions/bash/_mkdwarfs @@ -0,0 +1,165 @@ +# SPDX-License-Identifier: MIT +# Author: Ahmad Khalifa +# +# bash completion for mkdwarfs +# +# synopsis +# mkdwarfs [OPTIONS...] +# + +# TODO: unreliable? maybe not if app keeps the two groups intact. +__mkdwarfs_list_comp_algos() +{ + # extract algorithms block + # trim algo leading space, delete algo args, remove empty line + # delete algo descriptions + # double print with trailing ':' except 'null' - they complete with no space + mkdwarfs -H | \ + sed -e '1,/Compression algorithms/d;/Categories:/,$d' \ + -e 's/^[ ]\{1,2\}//;/^ /d;/^$/d' \ + -e 's/ .*$//' \ + -ne 'p;/null/!s/$/:/p' +} + +__mkdwarfs_additional_options() +{ + if [[ "$(mkdwarfs -h | grep -e ' *--man')" ]]; then + echo "--man" + fi +} + +_mkdwarfs_completion() +{ + local cur prev words cword + _comp_initialize || return + + local OPTIONS_GENERAL=( + --bloom-filter-size + --categorize + --change-block-size + --chmod + --compress-niceness + --debug-filter + --file-hash + --header + --history-compression + --hotness-list + --incompressible-block-size + --incompressible-fragments + --incompressible-min-input-size + --incompressible-ratio + --incompressible-zstd-level + --input-list + --keep-all-times + --log-level + --log-with-context + --man + --max-similarity-size + --metadata-compression + --no-category-metadata + --no-category-names + --no-create-timestamp + --no-history + --no-history-command-line + --no-history-timestamps + --no-metadata-version-history + --no-progress + --no-section-index + --num-scanner-workers + --num-segmenter-workers + --order + --progress + --rebuild-metadata + --recompress + --recompress-categories + --remove-empty-dirs + --remove-header + --schema-compression + --set-group + --set-owner + --set-time + --time-resolution + --with-devices + --with-specials + -B --max-lookback-blocks + -C --compression + -F --filter + -f --force + -h --help + -H --long-help + -i --input + -l --compress-level + -L --memory-limit + -N --num-workers + -o --output + -P --pack-metadata + -S --block-size-bits + -W --window-size + -w --window-step + $(__mkdwarfs_additional_options) + ) + + local OPTION_ARG__log_level=( error warn info verbose debug trace ) + local OPTION_ARG__compress_level=( 0 1 2 3 4 5 6 7 8 9 ) + local OPTION_ARG__recompress=( none block metadata all ) + local OPTION_ARG__categorize=( fits pcmaudio incompressible ) + # TODO: find a better way to extract these at runtime + local OPTION_ARG__file_hash=( ) + local OPTION_ARG__progress=( ascii none simple unicode ) + local OPTION_ARG__pack_metadata=( auto all none chunk_table directories + shared_files names names_index symlinks symlinks_index force plain ) + + # catch option with known arguments first + case $prev in + --log-level | --compress-level | --recompress | \ + --categorize | --file-hash | --progress | --pack-metadata) + prevoption=${prev//-/_} + _comp_compgen -- -W '"${OPTION_ARG'$prevoption'[@]}"' + return 0 + ;; + -P) + _comp_compgen -- -W '"${OPTION_ARG__pack_metadata[@]}"' + return 0 + ;; + -l) + _comp_compgen -- -W '"${OPTION_ARG__compress_level[@]}"' + return 0 + ;; + --set-owner) + _comp_compgen -- uids + return 0 + ;; + --set-group) + _comp_compgen -- gids + return 0 + ;; + -i | --input) + _comp_compgen -a filedir -d + return 0 + ;; + --input-list | -o | --output) + _comp_compgen -a filedir + return 0 + ;; + --compression | --schema-compression | \ + --metadata-compression | --history-compression) + # TODO: complete algo args based $prev and ':' or ',' + # --prev :=<_>,=<_> + _comp_compgen -- -W '$(__mkdwarfs_list_comp_algos)' + return 0 + ;; + esac + + + if [[ $cur == -* ]]; then + # cursor on an option, show options only + _comp_compgen -- -W '"${OPTIONS_GENERAL[@]}"' + else + # show all options and files + _comp_compgen -- -W '"${OPTIONS_GENERAL[@]}"' + _comp_compgen -a filedir + fi + + return 0 +} && +complete -F _mkdwarfs_completion mkdwarfs diff --git a/doc/completions/zsh/_mkdwarfs b/doc/completions/zsh/_mkdwarfs new file mode 100644 index 000000000..c7e3d071a --- /dev/null +++ b/doc/completions/zsh/_mkdwarfs @@ -0,0 +1,101 @@ +#compdef mkdwarfs +# +# SPDX-License-Identifier: MIT +# Author: Ahmad Khalifa +# +# zsh completion for mkdwarfs +# +# synopsis +# mkdwarfs [OPTIONS...] +# + +local context state line ret=1 + +# TODO: unreliable? maybe not if app keeps the two groups intact. +__mkdwarfs_list_comp_algos() +{ + # extract algorithms block + # trim algo leading space, delete algo args, remove empty line + # delete algo descriptions + # double print with trailing '\:' except 'null' + mkdwarfs -H | \ + sed -e '1,/Compression algorithms/d;/Categories:/,$d' \ + -e 's/^[ ]\{1,2\}//;/^ /d;/^$/d' \ + -e 's/ .*$//' \ + -ne 'p;/null/!s/$/\\:/p' +} + +__mkdwarfs_disable_man() +{ + if [[ ! "$(mkdwarfs -h | grep -e ' *--man')" ]]; then + echo -n "!" + fi +} + +_arguments -S \ + "--bloom-filter-size" \ + "--categorize=-:cattype:_values -s , cattype fits pcmaudio incompressible" \ + "--change-block-size" \ + "--chmod" \ + "--compress-niceness" \ + "--debug-filter" \ + "--file-hash:hashfnc:" \ + "--header" \ + "--history-compression:algos:($(__mkdwarfs_list_comp_algos))" \ + "--hotness-list" \ + "--incompressible-block-size" \ + "--incompressible-fragments" \ + "--incompressible-min-input-size" \ + "--incompressible-ratio" \ + "--incompressible-zstd-level" \ + "--input-list[file containing list of file paths relative to root directory or - for stdin]:filename:_files" \ + "--keep-all-times[save atime and ctime in addition to mtime]" \ + "--log-level:level:(error warn info verbose debug trace)" \ + "--log-with-context" \ + "--max-similarity-size" \ + "--metadata-compression:algos:($(__mkdwarfs_list_comp_algos))" \ + "--no-category-metadata" \ + "--no-category-names" \ + "--no-create-timestamp" \ + "--no-history" \ + "--no-history-command-line" \ + "--no-history-timestamps" \ + "--no-metadata-version-history" \ + "--no-progress" \ + "--no-section-index" \ + "--num-scanner-workers" \ + "--num-segmenter-workers" \ + "--order" \ + "--progress:progress:(ascii none simple unicode)" \ + "--rebuild-metadata" \ + "--recompress:level:(none block metadata all)" \ + "--recompress-categories" \ + "--remove-empty-dirs" \ + "--remove-header" \ + "--schema-compression:algos:($(__mkdwarfs_list_comp_algos))" \ + "--set-group[group (gid) for whole file system]" \ + "--set-owner[owner (uid) for whole file system]" \ + "--set-time[timestamp for whole file system (unixtime or 'now')]" \ + "--time-resolution" \ + "--with-devices" \ + "--with-specials" \ + {-B,--max-lookback-blocks} \ + {-C,--compression=-}"[block compression algorithm]:algos:($(__mkdwarfs_list_comp_algos))" \ + {-F,--filter} \ + {-f,--force}"[force overwrite of existing output image]" \ + {-h,--help}"[help message]" \ + {-H,--long-help}"[full help message]" \ + {-i,--input}"[path to root directory or source filesystem]:directory:_files -/" \ + {-l,--compress-level}"[compression level (0=fast, 9=best)]:level:(0 1 2 3 4 5 6 7 8 9)" \ + {-L,--memory-limit} \ + {-N,--num-workers}"[number of writer (compression) worker threads]" \ + {-o,--output}"[filesystem output name or - for stdout]:filename:_files" \ + {-P,--pack-metadata}":packtype:_values packtype auto all none \ + chunk_table directories shared_files names names_index symlinks \ + symlinks_index force plain" \ + {-S,--block-size-bits} \ + {-W,--window-size} \ + {-w,--window-step} \ + "$(__mkdwarfs_disable_man)--man[show manual page and exit]" && ret=0 + +return ret