diff --git a/home/program/bash/default.nix b/home/program/bash/default.nix index 9bc695cc..4caa4f1d 100644 --- a/home/program/bash/default.nix +++ b/home/program/bash/default.nix @@ -1,13 +1,38 @@ -{ util, pkgs, ... }@args: +{ + util, + pkgs, + secret, + config, + ... +}: let - modules = util.catText (util.ls ./module) args; - modulesFile = pkgs.writeText "BashModules" modules; + accent = "${config.module.style.color.accentR};${config.module.style.color.accentG};${config.module.style.color.accentB}"; + negative = "${config.module.style.color.negativeR};${config.module.style.color.negativeG};${config.module.style.color.negativeB}"; + neutral = "${config.module.style.color.neutralR};${config.module.style.color.neutralG};${config.module.style.color.neutralB}"; + positive = "${config.module.style.color.positiveR};${config.module.style.color.positiveG};${config.module.style.color.positiveB}"; + + tgbot = secret.tg.bt; + tgdata = secret.tg.dt "false"; + tgdatasilent = secret.tg.dt "true"; + + modulesRaw = pkgs.writeText "bash-user-modules-raw" (util.catContent (util.ls ./module)); + modulesFile = pkgs.replaceVars modulesRaw { + inherit + accent + negative + neutral + positive + tgbot + tgdata + tgdatasilent + ; + }; in { - inherit modules modulesFile; + inherit modulesFile; bashrc = - modules + (builtins.readFile modulesFile) + '' # Find all functions. function find_function() { diff --git a/home/program/bash/module/Archive.nix b/home/program/bash/module/Archive.nix deleted file mode 100644 index b0ed304f..00000000 --- a/home/program/bash/module/Archive.nix +++ /dev/null @@ -1,423 +0,0 @@ -{ ... }: -{ - text = '' - export _archive_pattern="_[0-9]{12}-[[:alnum:]]{40}" - export _archive_pattern_compressed="_[0-9]{12}-[[:alnum:]]{40}\.t(ar|gz|xz)$" - export _archive_pattern_fast="_[0-9]{12}-[[:alnum:]]{40}\.tar$" - - # Archive using multiple threads. Uses 75% of free RAM. - # All directories by default. - # Supports .archiveignore exclude file. - # Usage: archive [DIRS] - function archive() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(_ls_dir)) - - process() { - # Skip if already an archive. - _is_archive "''${target}" && { - _iterate_skip "Already an archive." - return 0 - }; - - # Cut full paths. - [[ "''${target##*/}" = "" ]] || target="''${target##*/}" - - local date=$(_archive_date) - - # Parse name. - local name=$(parse_pascal ''${target}) - - # Exclude support. - local exclude="" - [[ -f ".archiveignore" ]] && exclude="--exclude-from=.archiveignore" - [[ -f "''${target}/.archiveignore" ]] && exclude="--exclude-from=''${target}/.archiveignore" - - # Create archive. - local hash=$(tar ''${exclude} -c ''${target} | pv -s $(/usr/bin/env du -sb ''${target} | awk '{print $1}') | xz -9e --threads=0 --memlimit=$(_archive_memlimit) | tee ''${name}.txz | sha1sum | cut -d\ -f1) - - # Append hash to target name. - local new_name="''${name}_''${date}-''${hash}.txz" - mv -- ''${name}.txz ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Creates a simple archive. - # If it is a file, it just reformats file name to match archive name. - # For dirs, it first creates a tar archive. - # All files by default. - # Usage: archive_fast [DIRS] - function archive_fast() { - local IFS=$'\n' - local targets=("''${@}") - [[ "''${targets}" = "" ]] && targets=($(_ls_file)) - - process() { - # Skip if already an archive. - _is_archive "''${target}" && { - _iterate_skip "Already an archive." - return 0 - }; - - # Cut full paths. - [[ "''${target##*/}" = "" ]] || target="''${target##*/}" - - # Start timestamp. - local date=$(_archive_date) - - # Exclude support. - local exclude - [[ -f ".archiveignore" ]] && exclude="--exclude-from=.archiveignore" - [[ -f "''${target}/.archiveignore" ]] && exclude="--exclude-from=''${target}/.archiveignore" - - local name - local extension - - if [[ -d "''${target}" ]]; then - name=$(parse_pascal "''${target}") - - # Create archive. - local hash=$(tar ''${exclude} -c "''${target}" | pv -s $(/usr/bin/env du -sb "''${target}" | awk '{print $1}') | tee "''${name}".tar | sha1sum | cut -d\ -f1) - - # Append hash to target name. - local new_name="''${name}_''${date}-''${hash}.tar" - mv -- "''${name}".tar ''${new_name} && echo ''${new_name} - else - name=$(parse_pascal "''${target%.*}") - extension=".''${target##*.}" - - # Check if extension matches name, then drop it. - [[ "''${extension}" = ".''${target%.*}" ]] && extension="" - - local hash=$(pv "''${target}" | sha1sum | cut -d\ -f1) - local new_name="''${name}_''${date}-''${hash}''${extension}" - mv -- "''${target}" "''${new_name}" && echo ''${new_name} - fi - } - - _iterate_targets process ''${targets[@]} - } - - # Check archives integrity. - # Checks all archives by default. - # Usage: archive_check [FILES] - function archive_check() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(_ls_archive)) - - process() { - _archive_check "''${target}" - } - - _iterate_targets process ''${targets[@]} - } - - # Delete old versions of an archive. - # All archives with 1 version by default. - # Usage: archive_prune [NAME] [VERSIONS] - function archive_prune() { - local IFS=$'\n' - local targets=(''${1}) - local versions=''${2} - - [[ "''${targets}" = "" ]] && targets=($(_archive_names)) - [[ "''${versions}" = "" ]] && versions=1 - - if [[ ''${#} -gt 2 ]]; then - help archive_prune - return 2 - fi - - process() { - local prune=($(ls | grep -E "^''${target}''${_archive_pattern}" | sort -r | sed -e "1,''${versions}d")) - - for archive in ''${prune[@]}; do - rm -- "''${archive}" && echo "''${archive}" - done - } - - _iterate_targets process ''${targets[@]} - } - - # Delete specified or all archive files. - # Usage: archive_rm [FILES] - function archive_rm() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(_ls_archive)) - - process() { - rm -- "''${target}" && echo "''${target}" - } - - _iterate_targets process ''${targets[@]} - } - - # Recompress previously created archive_fast with better compression. - # Usage: archive_compress [FILES] - function archive_compress() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=$(_ls_archive_fast) - - process() { - local data=($(_archive_parse "''${target}")) - local tmp="''${data[0]}.txz" - - # Check that old format. - if [[ "''${data[3]}" != "tar" ]]; then - _iterate_skip "Not in .tar format!" - return 0 - fi - - # Check integrity. - _archive_check "''${target}" || return 1 - - # Recompress. - local hash=$(pv "''${target}" | xz -9e --threads=0 --memlimit=$(_archive_memlimit) | tee "''${tmp}" | sha1sum | cut -d\ -f1) - - # Rename. - local new_name="''${data[0]}_''${data[1]}-''${hash}.txz" - mv -- ''${tmp} ''${new_name} && rm ''${target} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Rename archives. - # If no name specified, it simplifies archive's name. - # If no archives specified, apply to all archives. - # Usage: archive_name [ARCHIVE] [NAME] - function archive_name() { - local IFS=$'\n' - local targets="''${1}" - local name="''${2}" - [[ "''${targets}" = "" ]] && targets=($(_ls_archive)) - - process() { - # Simplify name by default. - if [[ "''${name}" = "" || ''${count} -gt 1 ]]; then - name="''${target%_*}" - name="$(parse_pascal ''${name})" - fi - - # Remove old name. - local data="''${target##*_}" - local new_name="''${name}_''${data}" - - # Check for the same name. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # Check for existing target. - if [[ -f "''${new_name}" ]]; then - _error "''${new_name}: Already exists!" - return 1 - fi - - # Rename. - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Extract previously created archive with checksum validation. - # Supports unarchiving exact paths from the remote machines (rsync syntax). - # Usage: unarchive [HOST:FILES] - function unarchive() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=$(_ls_archive_compressed) - - process() { - # Validate. - # _archive_check "''${target}" || return 1 - if ! _is_compressed_archive "''${target}"; then - _iterate_skip "Not a compressed archive." - return 0 - fi - - # Remote archives. - local remote - local file="''${target}" - - if [[ "''${target//\\:/}" == *:* ]]; then - local host="''${target%%:*}" - file="''${target#*:}" - remote=(trysudo ssh ''${host}) - fi - - # Extract. - case "''${file##*.}" in - "txz") - ''${remote[@]} pv -f ''${file} | xz -d | tar -xf - - ;; - "tar") - ''${remote[@]} pv -f ''${file} | tar -xf - - ;; - "tgz") - ''${remote[@]} pv -f ''${file} | gzip -d | tar -xf - - ;; - esac - } - - _iterate_targets process ''${targets[@]} - } - - # Change archive's filesystem time to match creation date. - # Usage: archive_touch [FILES] - function archive_touch() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=$(_ls_archive) - - process() { - local data=($(_archive_parse "''${target}")) - local date=''${data[1]} - touch -t ''${date} -- ''${target} - } - - _iterate_targets process ''${targets[@]} - } - - # Parse archive file name to get: name, date, hash and format. - # Usage: _archive_parse - function _archive_parse() { - local input="''${1}" - local name="''${input%_*}" - local format="''${input##*.}" - local data="''${input##*_}"; data="''${data%.*}" - local date="''${data%%-*}" - local hash="''${data##*-}" - - # No extension if no dots. - [[ "''${input}" = *\.* ]] || format="" - - echo "''${name}" - echo "''${date}" - echo "''${hash}" - echo "''${format}" - } - - # Autocomplete for archive_name function. - # First arg is the archives list, second one is selected archive's current name. - function _comp_archive_name() { - local IFS=$'\n' - COMPREPLY=() - - local cur="''${COMP_WORDS[COMP_CWORD]}" - local prev="''${COMP_WORDS[COMP_CWORD-1]}" - local command="''${COMP_WORDS[0]}" - - if [[ "''${prev}" = "''${command}" ]]; then - COMPREPLY=( $(compgen -W "$(ls | grep -E ''${_archive_pattern})" -- ''${cur}) ) - return 0 - else - local data=($(_archive_parse ''${prev})) - local name="''${data[0]}" - COMPREPLY=( $(compgen -W "''${name}" -- ''${cur}) ) - return 0 - fi - } - - # Autocomplete with archives in current dir. - function _comp_archive_grep() { - _autocomplete_grep ''${_archive_pattern} - } - - # Autocomplete with fast archives in current dir. - function _comp_archive_grep_fast() { - _autocomplete_grep ''${_archive_pattern_fast} - } - - # Get date for a new archive. - function _archive_date() { - date +%Y%m%d%H%M - } - - # Get names of all archives. - function _archive_names() { - local IFS=$'\n' - local archives=($(_ls_archive)) - local names=() - - for archive in ''${archives[@]}; do - local data=($(_archive_parse ''${archive})) - names+=(''${data[0]}) - done - - # Remove copies. - names=($(printf '%s\n' "''${names[@]}" | sort -u)) - - printf '%s\n' "''${names[@]}" - } - - # Autocomplete with names of all archives. - function _comp_archive_names() { - _autocomplete $(_archive_names) - } - - # Check if file is an archive. - function _is_archive() { - local target="''${*}" - local out=$(echo "''${target##*/}" | grep -E ''${_archive_pattern}) - - [[ "''${out}" != "" ]] - } - - # Check if file is a compressed archive. - function _is_compressed_archive() { - local out=$(echo "''${*}" | grep -E ''${_archive_pattern_compressed}) - - [[ "''${out}" != "" ]] - } - - # List all archives. - function _ls_archive() { - ls | grep -E ''${_archive_pattern} - } - - # List fast archives. - function _ls_archive_fast() { - ls | grep -E ''${_archive_pattern_fast} - } - - # List fast archives. - function _ls_archive_compressed() { - ls | grep -E ''${_archive_pattern_compressed} - } - - # Filter input for archives only. - function _filter_archive() { - grep -E ''${_archive_pattern} - } - - function _archive_memlimit() { - local mem_free=$(_mem_free) - local mem_limit=$((mem_free*3/4)) - - echo "''${mem_limit}MiB" - } - - function _archive_check() { - # Extract hash from name. - local data=($(_archive_parse ''${target})) - local saved=''${data[2]} - - # Calculate actual hash. - local actual=$(pv ''${target} | sha1sum | cut -d\ -f1) - - # Compare hashes. - [[ "''${actual}" = "''${saved}" ]] || _error "Archive check failed." - } - - # complete -o filenames -F _comp_archive_grep archive_check unarchive archive_rm archive_touch - # complete -o filenames -F _comp_archive_grep_fast archive_xz - complete -o filenames -F _comp_archive_name archive_name - complete -o filenames -F _comp_archive_names archive_prune - ''; -} diff --git a/home/program/bash/module/Archive.sh b/home/program/bash/module/Archive.sh new file mode 100644 index 00000000..57a947f5 --- /dev/null +++ b/home/program/bash/module/Archive.sh @@ -0,0 +1,419 @@ +export _archive_pattern="_[0-9]{12}-[[:alnum:]]{40}" +export _archive_pattern_compressed="_[0-9]{12}-[[:alnum:]]{40}\.t(ar|gz|xz)$" +export _archive_pattern_fast="_[0-9]{12}-[[:alnum:]]{40}\.tar$" + +# Archive using multiple threads. Uses 75% of free RAM. +# All directories by default. +# Supports .archiveignore exclude file. +# Usage: archive [DIRS] +function archive() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(_ls_dir)) + + process() { + # Skip if already an archive. + _is_archive "${target}" && { + _iterate_skip "Already an archive." + return 0 + } + + # Cut full paths. + [[ ${target##*/} == "" ]] || target="${target##*/}" + + local date=$(_archive_date) + + # Parse name. + local name=$(parse_pascal ${target}) + + # Exclude support. + local exclude="" + [[ -f ".archiveignore" ]] && exclude="--exclude-from=.archiveignore" + [[ -f "${target}/.archiveignore" ]] && exclude="--exclude-from=${target}/.archiveignore" + + # Create archive. + local hash=$(tar ${exclude} -c ${target} | pv -s $(/usr/bin/env du -sb ${target} | awk '{print $1}') | xz -9e --threads=0 --memlimit=$(_archive_memlimit) | tee ${name}.txz | sha1sum | cut -d\ -f1) + + # Append hash to target name. + local new_name="${name}_${date}-${hash}.txz" + mv -- ${name}.txz ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Creates a simple archive. +# If it is a file, it just reformats file name to match archive name. +# For dirs, it first creates a tar archive. +# All files by default. +# Usage: archive_fast [DIRS] +function archive_fast() { + local IFS=$'\n' + local targets=("${@}") + [[ ${targets} == "" ]] && targets=($(_ls_file)) + + process() { + # Skip if already an archive. + _is_archive "${target}" && { + _iterate_skip "Already an archive." + return 0 + } + + # Cut full paths. + [[ ${target##*/} == "" ]] || target="${target##*/}" + + # Start timestamp. + local date=$(_archive_date) + + # Exclude support. + local exclude + [[ -f ".archiveignore" ]] && exclude="--exclude-from=.archiveignore" + [[ -f "${target}/.archiveignore" ]] && exclude="--exclude-from=${target}/.archiveignore" + + local name + local extension + + if [[ -d ${target} ]]; then + name=$(parse_pascal "${target}") + + # Create archive. + local hash=$(tar ${exclude} -c "${target}" | pv -s $(/usr/bin/env du -sb "${target}" | awk '{print $1}') | tee "${name}".tar | sha1sum | cut -d\ -f1) + + # Append hash to target name. + local new_name="${name}_${date}-${hash}.tar" + mv -- "${name}".tar ${new_name} && echo ${new_name} + else + name=$(parse_pascal "${target%.*}") + extension=".${target##*.}" + + # Check if extension matches name, then drop it. + [[ ${extension} == ".${target%.*}" ]] && extension="" + + local hash=$(pv "${target}" | sha1sum | cut -d\ -f1) + local new_name="${name}_${date}-${hash}${extension}" + mv -- "${target}" "${new_name}" && echo ${new_name} + fi + } + + _iterate_targets process ${targets[@]} +} + +# Check archives integrity. +# Checks all archives by default. +# Usage: archive_check [FILES] +function archive_check() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(_ls_archive)) + + process() { + _archive_check "${target}" + } + + _iterate_targets process ${targets[@]} +} + +# Delete old versions of an archive. +# All archives with 1 version by default. +# Usage: archive_prune [NAME] [VERSIONS] +function archive_prune() { + local IFS=$'\n' + local targets=(${1}) + local versions=${2} + + [[ ${targets} == "" ]] && targets=($(_archive_names)) + [[ ${versions} == "" ]] && versions=1 + + if [[ ${#} -gt 2 ]]; then + help archive_prune + return 2 + fi + + process() { + local prune=($(ls | grep -E "^${target}${_archive_pattern}" | sort -r | sed -e "1,${versions}d")) + + for archive in ${prune[@]}; do + rm -- "${archive}" && echo "${archive}" + done + } + + _iterate_targets process ${targets[@]} +} + +# Delete specified or all archive files. +# Usage: archive_rm [FILES] +function archive_rm() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(_ls_archive)) + + process() { + rm -- "${target}" && echo "${target}" + } + + _iterate_targets process ${targets[@]} +} + +# Recompress previously created archive_fast with better compression. +# Usage: archive_compress [FILES] +function archive_compress() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=$(_ls_archive_fast) + + process() { + local data=($(_archive_parse "${target}")) + local tmp="${data[0]}.txz" + + # Check that old format. + if [[ ${data[3]} != "tar" ]]; then + _iterate_skip "Not in .tar format!" + return 0 + fi + + # Check integrity. + _archive_check "${target}" || return 1 + + # Recompress. + local hash=$(pv "${target}" | xz -9e --threads=0 --memlimit=$(_archive_memlimit) | tee "${tmp}" | sha1sum | cut -d\ -f1) + + # Rename. + local new_name="${data[0]}_${data[1]}-${hash}.txz" + mv -- ${tmp} ${new_name} && rm ${target} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Rename archives. +# If no name specified, it simplifies archive's name. +# If no archives specified, apply to all archives. +# Usage: archive_name [ARCHIVE] [NAME] +function archive_name() { + local IFS=$'\n' + local targets="${1}" + local name="${2}" + [[ ${targets} == "" ]] && targets=($(_ls_archive)) + + process() { + # Simplify name by default. + if [[ ${name} == "" || ${count} -gt 1 ]]; then + name="${target%_*}" + name="$(parse_pascal ${name})" + fi + + # Remove old name. + local data="${target##*_}" + local new_name="${name}_${data}" + + # Check for the same name. + [[ ${target} == "${new_name}" ]] && return 0 + + # Check for existing target. + if [[ -f ${new_name} ]]; then + _error "${new_name}: Already exists!" + return 1 + fi + + # Rename. + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Extract previously created archive with checksum validation. +# Supports unarchiving exact paths from the remote machines (rsync syntax). +# Usage: unarchive [HOST:FILES] +function unarchive() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=$(_ls_archive_compressed) + + process() { + # Validate. + # _archive_check "${target}" || return 1 + if ! _is_compressed_archive "${target}"; then + _iterate_skip "Not a compressed archive." + return 0 + fi + + # Remote archives. + local remote + local file="${target}" + + if [[ ${target//\\:/} == *:* ]]; then + local host="${target%%:*}" + file="${target#*:}" + remote=(trysudo ssh ${host}) + fi + + # Extract. + case "${file##*.}" in + "txz") + ${remote[@]} pv -f ${file} | xz -d | tar -xf - + ;; + "tar") + ${remote[@]} pv -f ${file} | tar -xf - + ;; + "tgz") + ${remote[@]} pv -f ${file} | gzip -d | tar -xf - + ;; + esac + } + + _iterate_targets process ${targets[@]} +} + +# Change archive's filesystem time to match creation date. +# Usage: archive_touch [FILES] +function archive_touch() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=$(_ls_archive) + + process() { + local data=($(_archive_parse "${target}")) + local date=${data[1]} + touch -t ${date} -- ${target} + } + + _iterate_targets process ${targets[@]} +} + +# Parse archive file name to get: name, date, hash and format. +# Usage: _archive_parse +function _archive_parse() { + local input="${1}" + local name="${input%_*}" + local format="${input##*.}" + local data="${input##*_}" + data="${data%.*}" + local date="${data%%-*}" + local hash="${data##*-}" + + # No extension if no dots. + [[ ${input} == *\.* ]] || format="" + + echo "${name}" + echo "${date}" + echo "${hash}" + echo "${format}" +} + +# Autocomplete for archive_name function. +# First arg is the archives list, second one is selected archive's current name. +function _comp_archive_name() { + local IFS=$'\n' + COMPREPLY=() + + local cur="${COMP_WORDS[COMP_CWORD]}" + local prev="${COMP_WORDS[COMP_CWORD - 1]}" + local command="${COMP_WORDS[0]}" + + if [[ ${prev} == "${command}" ]]; then + COMPREPLY=($(compgen -W "$(ls | grep -E ${_archive_pattern})" -- ${cur})) + return 0 + else + local data=($(_archive_parse ${prev})) + local name="${data[0]}" + COMPREPLY=($(compgen -W "${name}" -- ${cur})) + return 0 + fi +} + +# Autocomplete with archives in current dir. +function _comp_archive_grep() { + _autocomplete_grep ${_archive_pattern} +} + +# Autocomplete with fast archives in current dir. +function _comp_archive_grep_fast() { + _autocomplete_grep ${_archive_pattern_fast} +} + +# Get date for a new archive. +function _archive_date() { + date +%Y%m%d%H%M +} + +# Get names of all archives. +function _archive_names() { + local IFS=$'\n' + local archives=($(_ls_archive)) + local names=() + + for archive in ${archives[@]}; do + local data=($(_archive_parse ${archive})) + names+=(${data[0]}) + done + + # Remove copies. + names=($(printf '%s\n' "${names[@]}" | sort -u)) + + printf '%s\n' "${names[@]}" +} + +# Autocomplete with names of all archives. +function _comp_archive_names() { + _autocomplete $(_archive_names) +} + +# Check if file is an archive. +function _is_archive() { + local target="${*}" + local out=$(echo "${target##*/}" | grep -E ${_archive_pattern}) + + [[ ${out} != "" ]] +} + +# Check if file is a compressed archive. +function _is_compressed_archive() { + local out=$(echo "${*}" | grep -E ${_archive_pattern_compressed}) + + [[ ${out} != "" ]] +} + +# List all archives. +function _ls_archive() { + ls | grep -E ${_archive_pattern} +} + +# List fast archives. +function _ls_archive_fast() { + ls | grep -E ${_archive_pattern_fast} +} + +# List fast archives. +function _ls_archive_compressed() { + ls | grep -E ${_archive_pattern_compressed} +} + +# Filter input for archives only. +function _filter_archive() { + grep -E ${_archive_pattern} +} + +function _archive_memlimit() { + local mem_free=$(_mem_free) + local mem_limit=$((mem_free * 3 / 4)) + + echo "${mem_limit}MiB" +} + +function _archive_check() { + # Extract hash from name. + local data=($(_archive_parse ${target})) + local saved=${data[2]} + + # Calculate actual hash. + local actual=$(pv ${target} | sha1sum | cut -d\ -f1) + + # Compare hashes. + [[ ${actual} == "${saved}" ]] || _error "Archive check failed." +} + +# complete -o filenames -F _comp_archive_grep archive_check unarchive archive_rm archive_touch +# complete -o filenames -F _comp_archive_grep_fast archive_xz +complete -o filenames -F _comp_archive_name archive_name +complete -o filenames -F _comp_archive_names archive_prune diff --git a/home/program/bash/module/Ask.nix b/home/program/bash/module/Ask.nix deleted file mode 100644 index 42e45450..00000000 --- a/home/program/bash/module/Ask.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ ... }: -{ - text = '' - # Ask general AI. - # Usage: ask - function ask() { - curl http://localhost:11434/api/generate -d "{ - \"model\":\"''${OLLAMA_MODEL}\", - \"raw\":true, - \"prompt\":\"''${*}\" - }" 2> /dev/null | parallel -j1 -- "printf '%s\n' {} | jq -r .response | sed -e 's/^$/\+\+\+/' | tr -d '\n' | sed -e 's/\+\+\+/\n/'" - echo - } - - # Specify ask model. - function ask_model() { - export OLLAMA_MODEL="''${1}" - } - - function _complete_ask_model() { - local IFS=$'\n' - local models=($(ollama list | sed -e "1d" | cut -f1)) - _autocomplete ''${models[@]} - } - - complete -F _complete_ask_model ask_model - ''; -} diff --git a/home/program/bash/module/Ask.sh b/home/program/bash/module/Ask.sh new file mode 100644 index 00000000..1171451f --- /dev/null +++ b/home/program/bash/module/Ask.sh @@ -0,0 +1,23 @@ +# Ask general AI. +# Usage: ask +function ask() { + curl http://localhost:11434/api/generate -d "{ + \"model\":\"${OLLAMA_MODEL}\", + \"raw\":true, + \"prompt\":\"${*}\" + }" 2>/dev/null | parallel -j1 -- "printf '%s\n' {} | jq -r .response | sed -e 's/^$/\+\+\+/' | tr -d '\n' | sed -e 's/\+\+\+/\n/'" + echo +} + +# Specify ask model. +function ask_model() { + export OLLAMA_MODEL="${1}" +} + +function _complete_ask_model() { + local IFS=$'\n' + local models=($(ollama list | sed -e "1d" | cut -f1)) + _autocomplete ${models[@]} +} + +complete -F _complete_ask_model ask_model diff --git a/home/program/bash/module/Autocomplete.nix b/home/program/bash/module/Autocomplete.nix deleted file mode 100644 index 9fa6df9f..00000000 --- a/home/program/bash/module/Autocomplete.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ ... }: -{ - text = '' - # Bash autocomplete. - # There are also options like -o nospace. see man for more info. - # Usage: _foo() { _autocomplete "{foo,bar}" } ; complete -F _foo foo - function _autocomplete() { - local iter use cur - cur=''${COMP_WORDS[COMP_CWORD]} - use="''${@//\\ /___}" - for iter in $use; do - if [[ $iter =~ ^$cur ]]; then - COMPREPLY+=( $(printf "%q" "''${iter//___/ }") ) - fi - done - } - - # Autocomplete by grepping file names. - function _autocomplete_grep() { - local IFS=$'\n' - COMPREPLY=() - - local pattern="''${1}" - local candidates=$("$(ls | grep -E ''${pattern})") - _autocomplete ''${candidates} - } - - # Autocomplete nested program. - function _autocomplete_nested() { - # local IFS=$'\n' - local cur prev words cword split i - _init_completion -s || return - - for ((i = 1; i <= cword; i++)); do - if [[ ''${words[i]} != -* ]]; then - local PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin - local root_command=''${words[i]} - _command_offset ''${i} - return - fi - done - } - ''; -} diff --git a/home/program/bash/module/Autocomplete.sh b/home/program/bash/module/Autocomplete.sh new file mode 100644 index 00000000..f877d7ff --- /dev/null +++ b/home/program/bash/module/Autocomplete.sh @@ -0,0 +1,39 @@ +# Bash autocomplete. +# There are also options like -o nospace. see man for more info. +# Usage: _foo() { _autocomplete "{foo,bar}" } ; complete -F _foo foo +function _autocomplete() { + local iter use cur + cur=${COMP_WORDS[COMP_CWORD]} + use="${@//\\ /___}" + for iter in $use; do + if [[ $iter =~ ^$cur ]]; then + COMPREPLY+=($(printf "%q" "${iter//___/ }")) + fi + done +} + +# Autocomplete by grepping file names. +function _autocomplete_grep() { + local IFS=$'\n' + COMPREPLY=() + + local pattern="${1}" + local candidates=$("$(ls | grep -E ${pattern})") + _autocomplete ${candidates} +} + +# Autocomplete nested program. +function _autocomplete_nested() { + # local IFS=$'\n' + local cur prev words cword split i + _init_completion -s || return + + for ((i = 1; i <= cword; i++)); do + if [[ ${words[i]} != -* ]]; then + local PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin + local root_command=${words[i]} + _command_offset ${i} + return + fi + done +} diff --git a/home/program/bash/module/Battery.nix b/home/program/bash/module/Battery.nix deleted file mode 100644 index 3ca1c899..00000000 --- a/home/program/bash/module/Battery.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ ... }: -{ - text = '' - # Print current battery charge level in percents. - function battery_level() { - head -c -1 /sys/class/power_supply/BAT*/capacity - echo '%' - } - - # Get battery's info. - function battery_info() { - local IFS=$'\n' - local battery=("$(upower --enumerate | grep battery_BAT)") - upower -i "''${battery[0]}" - } - ''; -} diff --git a/home/program/bash/module/Battery.sh b/home/program/bash/module/Battery.sh new file mode 100644 index 00000000..012b4473 --- /dev/null +++ b/home/program/bash/module/Battery.sh @@ -0,0 +1,12 @@ +# Print current battery charge level in percents. +function battery_level() { + head -c -1 /sys/class/power_supply/BAT*/capacity + echo '%' +} + +# Get battery's info. +function battery_info() { + local IFS=$'\n' + local battery=("$(upower --enumerate | grep battery_BAT)") + upower -i "${battery[0]}" +} diff --git a/home/program/bash/module/Binwalk.nix b/home/program/bash/module/Binwalk.nix deleted file mode 100644 index 70544bdf..00000000 --- a/home/program/bash/module/Binwalk.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ ... }: -{ - text = '' - # Extract all formats with binwalk. - # Use -M for recursive extract. - # Usage: binwalke - function binwalke() { - binwalk --dd='.*' "$@" - } - ''; -} diff --git a/home/program/bash/module/Binwalk.sh b/home/program/bash/module/Binwalk.sh new file mode 100644 index 00000000..57322930 --- /dev/null +++ b/home/program/bash/module/Binwalk.sh @@ -0,0 +1,6 @@ +# Extract all formats with binwalk. +# Use -M for recursive extract. +# Usage: binwalke +function binwalke() { + binwalk --dd='.*' "$@" +} diff --git a/home/program/bash/module/Bottle.nix b/home/program/bash/module/Bottle.nix deleted file mode 100644 index fbd646d6..00000000 --- a/home/program/bash/module/Bottle.nix +++ /dev/null @@ -1,90 +0,0 @@ -{ ... }: -{ - text = '' - # Switch bottle. - # Usage: bt - function bt() { - export SHELL_NAME="''${*}" - } - - # Create new bottle. - # Usage: btc [ENV] [EXTRA] - function btc() { - local env="''${1}" - [[ "''${env}" = "" ]] && env="gaming" - bottles-cli new --bottle-name "''${SHELL_NAME}" --environment "''${env}" "''${@:2}" - } - - # Run a file inside a bottle. - # Usage: btre [EXTRA] - function btre() { - bottles-cli run -b "''${SHELL_NAME}" -e "''${@}" - } - - # Run a program inside a bottle. - # Usage: btr [EXTRA] - function btr() { - bottles-cli run -b "''${SHELL_NAME}" -p "''${@}" - } - - # List bottles. - function btl() { - bottles-cli list bottles 2> /dev/null - } - - # List programs in a bottle. - function btlp() { - bottles-cli programs -b "''${SHELL_NAME}" 2> /dev/null - } - - # Add a program to bottle. - # Usage: bta [EXTRA] - function bta() { - local name="''${1}" - local exe=$(realpath "''${2}") - if [[ "''${exe}" = "" ]]; then - help bta - return 2 - fi - - bottles-cli add -b "''${SHELL_NAME}" -n "''${name}" -p "''${exe}" "''${@:3}" 2> /dev/null - } - - # Set bottle env var. - # Usage: bte - function bte() { - local env="''${1}" - if [[ "''${env}" = "" ]]; then - help bte - return 2 - fi - - bottles-cli edit -b "''${SHELL_NAME}" --env-var "''${@}" 2> /dev/null - } - - # Play bottle. - # Usage: btp - function btp() { - local bottle="''${1##*/}" - if [[ "''${bottle}" = "" ]]; then - help btp - return 2 - fi - - local program=$(bottles-cli programs -b "''${bottle}" 2> /dev/null | sed -n -e "s/^- //; 2p") - bottles-cli run -b "''${bottle}" -p "''${program}" - } - - function _comp_bottles_list() { - _autocomplete $(bottles-cli list bottles 2> /dev/null | sed -e "1d; s/^- //") - } - - function _comp_programs_list() { - local IFS=$'\n' - _autocomplete $(bottles-cli programs -b "''${SHELL_NAME}" 2> /dev/null | sed -e "1d; s/^- //") - } - - complete -F _comp_bottles_list bt btp - complete -F _comp_programs_list btr - ''; -} diff --git a/home/program/bash/module/Bottle.sh b/home/program/bash/module/Bottle.sh new file mode 100644 index 00000000..155d60d4 --- /dev/null +++ b/home/program/bash/module/Bottle.sh @@ -0,0 +1,85 @@ +# Switch bottle. +# Usage: bt +function bt() { + export SHELL_NAME="${*}" +} + +# Create new bottle. +# Usage: btc [ENV] [EXTRA] +function btc() { + local env="${1}" + [[ ${env} == "" ]] && env="gaming" + bottles-cli new --bottle-name "${SHELL_NAME}" --environment "${env}" "${@:2}" +} + +# Run a file inside a bottle. +# Usage: btre [EXTRA] +function btre() { + bottles-cli run -b "${SHELL_NAME}" -e "${@}" +} + +# Run a program inside a bottle. +# Usage: btr [EXTRA] +function btr() { + bottles-cli run -b "${SHELL_NAME}" -p "${@}" +} + +# List bottles. +function btl() { + bottles-cli list bottles 2>/dev/null +} + +# List programs in a bottle. +function btlp() { + bottles-cli programs -b "${SHELL_NAME}" 2>/dev/null +} + +# Add a program to bottle. +# Usage: bta [EXTRA] +function bta() { + local name="${1}" + local exe=$(realpath "${2}") + if [[ ${exe} == "" ]]; then + help bta + return 2 + fi + + bottles-cli add -b "${SHELL_NAME}" -n "${name}" -p "${exe}" "${@:3}" 2>/dev/null +} + +# Set bottle env var. +# Usage: bte +function bte() { + local env="${1}" + if [[ ${env} == "" ]]; then + help bte + return 2 + fi + + bottles-cli edit -b "${SHELL_NAME}" --env-var "${@}" 2>/dev/null +} + +# Play bottle. +# Usage: btp +function btp() { + local bottle="${1##*/}" + if [[ ${bottle} == "" ]]; then + help btp + return 2 + fi + + local program=$(bottles-cli programs -b "${bottle}" 2>/dev/null | sed -n -e "s/^- //; 2p") + bottles-cli run -b "${bottle}" -p "${program}" +} + +function _comp_bottles_list() { + _autocomplete $(bottles-cli list bottles 2>/dev/null | sed -e "1d; s/^- //") +} + +function _comp_programs_list() { + local IFS=$'\n' + _autocomplete $(bottles-cli programs -b "${SHELL_NAME}" 2>/dev/null | sed -e "1d; s/^- //") +} + +complete -F _comp_bottles_list bt btp +complete -F _comp_programs_list btr diff --git a/home/program/bash/module/Brightness.nix b/home/program/bash/module/Brightness.nix deleted file mode 100644 index eb45300e..00000000 --- a/home/program/bash/module/Brightness.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ ... }: -{ - text = '' - # Set display brightness to a minimum. - function brmin() { - light -S 0.01 - } - - # Set display brightness to a maximum. - function brmax() { - light -S 100 - } - - # Set display brightness in percent, 50% default. - # Usage: brset [LEVEL] - function brset() { - local level=''${1} - [[ "''${level}" = "" ]] && level=50 - - light -S ''${level} - } - ''; -} diff --git a/home/program/bash/module/Brightness.sh b/home/program/bash/module/Brightness.sh new file mode 100644 index 00000000..3fc898cc --- /dev/null +++ b/home/program/bash/module/Brightness.sh @@ -0,0 +1,18 @@ +# Set display brightness to a minimum. +function brmin() { + light -S 0.01 +} + +# Set display brightness to a maximum. +function brmax() { + light -S 100 +} + +# Set display brightness in percent, 50% default. +# Usage: brset [LEVEL] +function brset() { + local level=${1} + [[ ${level} == "" ]] && level=50 + + light -S ${level} +} diff --git a/home/program/bash/module/Cd.nix b/home/program/bash/module/Cd.nix deleted file mode 100644 index 0a5c1a4f..00000000 --- a/home/program/bash/module/Cd.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ ... }: -{ - text = '' - # CD (back to) directory. - # Goes to the exact-match dir first. If no exact match found, it finds first directory that contains the input (case-insensitive). - # Usage: cdd - function cdd() { - local target="''${1}" - - if [[ "''${target}" = "" ]]; then - help cdd - return 2 - fi - - local array=($(_cdd_directories)) - local result - - # Check for exact match ELSE look for containing. - if _contains ''${target} ''${array[@]}; then - local current="''${PWD%/*}" - result="''${current%\/$target\/*}/''${target}" - else - # Make search case-insensitive. - shopt -s nocasematch - - # Find dir name that contains input. - local found=1 - for (( idx=''${#array[@]}-1 ; idx>=0 ; idx-- )); do - dir="''${array[idx]}" - [[ "''${dir}" =~ "''${target}" ]] && found=0 - [[ ''${found} = 0 ]] && result="/''${dir}''${result}" - done - - # Clean-up??? - shopt -u nocasematch - fi - - # Go there! - if [[ "''${result}" != "" ]]; then - echo "''${result}" - cd "''${result}" - else - return 1 - fi - } - - # CLI cd. Opens CLI file manager. - function ccd() { - local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" - yazi "$@" --cwd-file="$tmp" - if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then - cd -- "$cwd" - fi - rm -f -- "$tmp" - } - - # Get list of all parent dirs. - function _cdd_directories() { - local array - IFS='/' read -r -a array <<< "''${PWD}" - array=("''${array[@]:1}") - unset array[-1] - printf "%s\n" "''${array[@]}" - } - - function _comp_cdd() { - local IFS=$'\n' - local dirs=($(_cdd_directories)) - _autocomplete ''${dirs[@]} - } - - complete -o nosort -o filenames -F _comp_cdd cdd - ''; -} diff --git a/home/program/bash/module/Cd.sh b/home/program/bash/module/Cd.sh new file mode 100644 index 00000000..cb396533 --- /dev/null +++ b/home/program/bash/module/Cd.sh @@ -0,0 +1,69 @@ +# CD (back to) directory. +# Goes to the exact-match dir first. If no exact match found, it finds first directory that contains the input (case-insensitive). +# Usage: cdd +function cdd() { + local target="${1}" + + if [[ ${target} == "" ]]; then + help cdd + return 2 + fi + + local array=($(_cdd_directories)) + local result + + # Check for exact match ELSE look for containing. + if _contains ${target} ${array[@]}; then + local current="${PWD%/*}" + result="${current%\/$target\/*}/${target}" + else + # Make search case-insensitive. + shopt -s nocasematch + + # Find dir name that contains input. + local found=1 + for ((idx = ${#array[@]} - 1; idx >= 0; idx--)); do + dir="${array[idx]}" + [[ ${dir} =~ ${target} ]] && found=0 + [[ ${found} == 0 ]] && result="/${dir}${result}" + done + + # Clean-up??? + shopt -u nocasematch + fi + + # Go there! + if [[ ${result} != "" ]]; then + echo "${result}" + cd "${result}" + else + return 1 + fi +} + +# CLI cd. Opens CLI file manager. +function ccd() { + local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" + yazi "$@" --cwd-file="$tmp" + if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then + cd -- "$cwd" + fi + rm -f -- "$tmp" +} + +# Get list of all parent dirs. +function _cdd_directories() { + local array + IFS='/' read -r -a array <<<"${PWD}" + array=("${array[@]:1}") + unset array[-1] + printf "%s\n" "${array[@]}" +} + +function _comp_cdd() { + local IFS=$'\n' + local dirs=($(_cdd_directories)) + _autocomplete ${dirs[@]} +} + +complete -o nosort -o filenames -F _comp_cdd cdd diff --git a/home/program/bash/module/Color.nix b/home/program/bash/module/Color.nix deleted file mode 100644 index a2afdc2e..00000000 --- a/home/program/bash/module/Color.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ ... }: -{ - text = '' - # Collection of available CLI colors. - # They may differ depending on the terminal used. - # Colors with 'b' prefix are bold colors. - - export color_default="\033[0m" - export color_blue="\033[0;34m" - export color_bblue="\033[1;34m" - export color_cyan="\033[0;36m" - export color_bcyan="\033[1;36m" - export color_green="\033[0;32m" - export color_bgreen="\033[1;32m" - export color_purple="\033[0;35m" - export color_bpurple="\033[1;35m" - export color_red="\033[0;31m" - export color_bred="\033[1;31m" - export color_white="\033[0;37m" - export color_bwhite="\033[1;37m" - export color_yellow="\033[0;33m" - export color_byellow="\033[1;33m" - - # Print all available colors with their names colored in corresponding color. - function color_test() { - echo -e "''${color_default}color_default\n''${color_blue}color_blue\n''${color_bblue}color_bblue\n''${color_cyan}color_cyan\n''${color_bcyan}color_bcyan\n''${color_green}color_green\n''${color_bgreen}color_bgreen\n''${color_purple}color_purple\n''${color_bpurple}color_bpurple\n''${color_red}color_red\n''${color_bred}color_bred\n''${color_white}color_white\n''${color_bwhite}color_bwhite\n''${color_yellow}color_yellow\n''${color_byellow}color_byellow" - } - ''; -} diff --git a/home/program/bash/module/Color.sh b/home/program/bash/module/Color.sh new file mode 100644 index 00000000..4596d827 --- /dev/null +++ b/home/program/bash/module/Color.sh @@ -0,0 +1,24 @@ +# Collection of available CLI colors. +# They may differ depending on the terminal used. +# Colors with 'b' prefix are bold colors. + +export color_default="\033[0m" +export color_blue="\033[0;34m" +export color_bblue="\033[1;34m" +export color_cyan="\033[0;36m" +export color_bcyan="\033[1;36m" +export color_green="\033[0;32m" +export color_bgreen="\033[1;32m" +export color_purple="\033[0;35m" +export color_bpurple="\033[1;35m" +export color_red="\033[0;31m" +export color_bred="\033[1;31m" +export color_white="\033[0;37m" +export color_bwhite="\033[1;37m" +export color_yellow="\033[0;33m" +export color_byellow="\033[1;33m" + +# Print all available colors with their names colored in corresponding color. +function color_test() { + echo -e "${color_default}color_default\n${color_blue}color_blue\n${color_bblue}color_bblue\n${color_cyan}color_cyan\n${color_bcyan}color_bcyan\n${color_green}color_green\n${color_bgreen}color_bgreen\n${color_purple}color_purple\n${color_bpurple}color_bpurple\n${color_red}color_red\n${color_bred}color_bred\n${color_white}color_white\n${color_bwhite}color_bwhite\n${color_yellow}color_yellow\n${color_byellow}color_byellow" +} diff --git a/home/program/bash/module/Copypaste.nix b/home/program/bash/module/Copypaste.nix deleted file mode 100644 index 051ef38e..00000000 --- a/home/program/bash/module/Copypaste.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ ... }: -{ - text = '' - # Copy stdin to system clipboard. *Example:* `echo hi \| copy`. - function copy() { - wl-copy - } - - # Paste system clipboard to stdout. *Example:* `paste > file.txt`. - function paste() { - wl-paste - } - ''; -} diff --git a/home/program/bash/module/Copypaste.sh b/home/program/bash/module/Copypaste.sh new file mode 100644 index 00000000..db691cf6 --- /dev/null +++ b/home/program/bash/module/Copypaste.sh @@ -0,0 +1,9 @@ +# Copy stdin to system clipboard. *Example:* `echo hi \| copy`. +function copy() { + wl-copy +} + +# Paste system clipboard to stdout. *Example:* `paste > file.txt`. +function paste() { + wl-paste +} diff --git a/home/program/bash/module/Cp.nix b/home/program/bash/module/Cp.nix deleted file mode 100644 index 6b648f42..00000000 --- a/home/program/bash/module/Cp.nix +++ /dev/null @@ -1,55 +0,0 @@ -{ ... }: -{ - text = '' - # Replaces default cp with rsync. - # Usage: rcp - function rcp() { - rsync -ahP --chmod=u+w "''${@}" - } - - # Replaces default cp with rsync. - # Only compare file size. - # Usage: rcp_fast - function rcp_fast() { - rsync -ahP --chmod=u+w --size-only "''${@}" - } - - # Replaces default cp with rsync. - # Compare file hashes. - # Usage: rcp_hash - function rcp_hash() { - rsync -ahP --chmod=u+w --checksum "''${@}" - } - - # Copy and also merge all changes (delete dst files that do not exist in src). - # Usage: rcp_merge - function rcp_merge() { - rsync -ahP --chmod=u+w --delete "''${@}" - } - - # Copy and also merge all changes FAST (delete dst files that do not exist in src, only compare size). - # Usage: rcp_merge_fast - function rcp_merge_fast() { - rsync -ahP --chmod=u+w --delete --size-only "''${@}" - } - - # Copy and also merge all changes BY CHECKSUM (delete dst files that do not exist in src, compare hashes). - # Usage: rcp_merge_hash - function rcp_merge_hash() { - rsync -ahP --chmod=u+w --delete --checksum "''${@}" - } - - # Print output of cp_merge without writing anything. - # Usage: rcp_test - function rcp_test() { - rsync -ahP --chmod=u+w --delete -n "''${@}" - } - - # Copy by creating hardlinks. - # Works for directories, too. - # Usage: cp_link - function cp_link() { - /usr/bin/env cp -lr "''${@}" - } - ''; -} diff --git a/home/program/bash/module/Cp.sh b/home/program/bash/module/Cp.sh new file mode 100644 index 00000000..41b9fa4b --- /dev/null +++ b/home/program/bash/module/Cp.sh @@ -0,0 +1,50 @@ +# Replaces default cp with rsync. +# Usage: rcp +function rcp() { + rsync -ahP --chmod=u+w "${@}" +} + +# Replaces default cp with rsync. +# Only compare file size. +# Usage: rcp_fast +function rcp_fast() { + rsync -ahP --chmod=u+w --size-only "${@}" +} + +# Replaces default cp with rsync. +# Compare file hashes. +# Usage: rcp_hash +function rcp_hash() { + rsync -ahP --chmod=u+w --checksum "${@}" +} + +# Copy and also merge all changes (delete dst files that do not exist in src). +# Usage: rcp_merge +function rcp_merge() { + rsync -ahP --chmod=u+w --delete "${@}" +} + +# Copy and also merge all changes FAST (delete dst files that do not exist in src, only compare size). +# Usage: rcp_merge_fast +function rcp_merge_fast() { + rsync -ahP --chmod=u+w --delete --size-only "${@}" +} + +# Copy and also merge all changes BY CHECKSUM (delete dst files that do not exist in src, compare hashes). +# Usage: rcp_merge_hash +function rcp_merge_hash() { + rsync -ahP --chmod=u+w --delete --checksum "${@}" +} + +# Print output of cp_merge without writing anything. +# Usage: rcp_test +function rcp_test() { + rsync -ahP --chmod=u+w --delete -n "${@}" +} + +# Copy by creating hardlinks. +# Works for directories, too. +# Usage: cp_link +function cp_link() { + /usr/bin/env cp -lr "${@}" +} diff --git a/home/program/bash/module/Curl.nix b/home/program/bash/module/Curl.nix deleted file mode 100644 index 4b5406a5..00000000 --- a/home/program/bash/module/Curl.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ ... }: -{ - text = '' - # Download a file from the web. - # Usaee: dl [FILES...] - function dl() { - wcurl --curl-options='--http2 --continue-at -' -- ''${@} - } - ''; -} diff --git a/home/program/bash/module/Curl.sh b/home/program/bash/module/Curl.sh new file mode 100644 index 00000000..c672c9cd --- /dev/null +++ b/home/program/bash/module/Curl.sh @@ -0,0 +1,5 @@ +# Download a file from the web. +# Usaee: dl [FILES...] +function dl() { + wcurl --curl-options='--http2 --continue-at -' -- ${@} +} diff --git a/home/program/bash/module/Date.nix b/home/program/bash/module/Date.nix deleted file mode 100644 index 8fdd6813..00000000 --- a/home/program/bash/module/Date.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ ... }: -{ - text = '' - # Print today date in yyyyMMdd format. - function today() { - date +%Y%m%d - } - - # Current day of week number. - function dow() { - date +%u - } - ''; -} diff --git a/home/program/bash/module/Date.sh b/home/program/bash/module/Date.sh new file mode 100644 index 00000000..45ff32fd --- /dev/null +++ b/home/program/bash/module/Date.sh @@ -0,0 +1,9 @@ +# Print today date in yyyyMMdd format. +function today() { + date +%Y%m%d +} + +# Current day of week number. +function dow() { + date +%u +} diff --git a/home/program/bash/module/Disk.nix b/home/program/bash/module/Disk.nix deleted file mode 100644 index 2a7443b0..00000000 --- a/home/program/bash/module/Disk.nix +++ /dev/null @@ -1,73 +0,0 @@ -{ ... }: -{ - text = '' - # Show only physical drives info. - function pdf() { - df --si | sed -e '1p' -e '/^\/dev\//!d' - } - - # Show total size in SI. - # Current dir by default. - # Usage: tdu [DIRS] - function tdu() { - du -sh --si "''${@}" - } - - # Unlock encrypted disk file. - # Usage: funlock - function funlock() { - local file="''${1}" - - if [[ "''${file}" = "" ]]; then - help funlock - return 2 - fi - - local name=$(parse_alnum "''${file##*/}") - - local loop=$(udisksctl loop-setup --no-user-interaction --file "''${file}") - loop="''${loop##* }"; loop="''${loop%.}" - - local decrypted=$(udisksctl unlock --block-device "''${loop}") - decrypted="''${decrypted##* }"; decrypted="''${decrypted%.}" - - local mount=$(udisksctl mount --no-user-interaction --block-device "''${decrypted}") - mount="''${mount#* at }" - - ya pub dds-cd --str "''${mount}" 2> /dev/null - cd "''${mount}" - } - - # Mount file. - # Usage: fmount - function fmount() { - local file="''${1}" - if [[ "''${file}" = "" ]]; then - help fmount - return 2 - fi - - local loop=$(udisksctl loop-setup --no-user-interaction --file "''${file}") - loop="''${loop##* }"; loop="''${loop%.}" - - local mount=$(udisksctl mount --no-user-interaction --block-device "''${loop}") - mount="''${mount#* at }" - - ya pub dds-cd --str "''${mount}" 2> /dev/null - cd "''${mount}" - } - - # Unmount file. - # Usage: fumount - function fumount() { - local loop="''${1}" - if [[ "''${loop}" = "" ]]; then - help fumount - return 2 - fi - - udisksctl unmount --no-user-interaction --block-device "''${loop}" - udisksctl loop-delete --no-user-interaction --block-device "''${loop}" - } - ''; -} diff --git a/home/program/bash/module/Disk.sh b/home/program/bash/module/Disk.sh new file mode 100644 index 00000000..7ff6705f --- /dev/null +++ b/home/program/bash/module/Disk.sh @@ -0,0 +1,71 @@ +# Show only physical drives info. +function pdf() { + df --si | sed -e '1p' -e '/^\/dev\//!d' +} + +# Show total size in SI. +# Current dir by default. +# Usage: tdu [DIRS] +function tdu() { + du -sh --si "${@}" +} + +# Unlock encrypted disk file. +# Usage: funlock +function funlock() { + local file="${1}" + + if [[ ${file} == "" ]]; then + help funlock + return 2 + fi + + local name=$(parse_alnum "${file##*/}") + + local loop=$(udisksctl loop-setup --no-user-interaction --file "${file}") + loop="${loop##* }" + loop="${loop%.}" + + local decrypted=$(udisksctl unlock --block-device "${loop}") + decrypted="${decrypted##* }" + decrypted="${decrypted%.}" + + local mount=$(udisksctl mount --no-user-interaction --block-device "${decrypted}") + mount="${mount#* at }" + + ya pub dds-cd --str "${mount}" 2>/dev/null + cd "${mount}" +} + +# Mount file. +# Usage: fmount +function fmount() { + local file="${1}" + if [[ ${file} == "" ]]; then + help fmount + return 2 + fi + + local loop=$(udisksctl loop-setup --no-user-interaction --file "${file}") + loop="${loop##* }" + loop="${loop%.}" + + local mount=$(udisksctl mount --no-user-interaction --block-device "${loop}") + mount="${mount#* at }" + + ya pub dds-cd --str "${mount}" 2>/dev/null + cd "${mount}" +} + +# Unmount file. +# Usage: fumount +function fumount() { + local loop="${1}" + if [[ ${loop} == "" ]]; then + help fumount + return 2 + fi + + udisksctl unmount --no-user-interaction --block-device "${loop}" + udisksctl loop-delete --no-user-interaction --block-device "${loop}" +} diff --git a/home/program/bash/module/Docker.nix b/home/program/bash/module/Docker.nix deleted file mode 100644 index 0c3660ed..00000000 --- a/home/program/bash/module/Docker.nix +++ /dev/null @@ -1,100 +0,0 @@ -{ ... }: -{ - text = '' - # Show container's volumes. - # Usage: docker_volumes - function docker_volumes() { - docker inspect -f '{{ .Mounts }}' "''${@}" - } - - # Check if any container exited. - function docker_health() { - docker ps -a | grep Exited - } - - # Find out container's IP address. - # Usage: docker_ip - function docker_ip() { - docker inspect -f '\'''{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'\' "''${1}" | sed "s/^.//" | sed "s/.$//" - } - - # Update all docker images. - function docker_update() { - docker images --format "{{.Repository}}:{{.Tag}}" | xargs -L1 docker pull - } - - # Docker compose shortcut. - function dc() { - docker compose "''${@}" - } - - # Docker compose up. - # Usage: dcu [SERVICES] - function dcu() { - docker compose up -d "''${@}" - } - - # Docker compose down. - # Usage: dcd [SERVICES] - function dcd() { - docker compose down "''${@}" - } - - # Docker compose pull. - # Usage: dcp [SERVICES] - function dcp() { - docker compose pull "''${@}" - } - - # Docker compose logs. - # Usage: dcl [SERVICES] - function dcl() { - docker compose logs -f "''${@}" - } - - # Docker compose restart. - # Usage: dcr [SERVICES] - function dcr() { - docker compose restart "''${@}" - } - - # Docker compose stop. - # Usage: dcs [SERVICES] - function dcs() { - docker compose stop "''${@}" - } - - # Docker compose down & up specified services. - # Usage: dcdu [SERVICES] - function dcdu() { - dcd "''${@}" - dcu "''${@}" - } - - # Docker compose pull & up specified services. - # Usage: dcpu [SERVICES] - function dcpu() { - dcp "''${@}" - dcu "''${@}" - } - - # Docker compose up & attach to logs for specified services. - # Usage: dcul [SERVICES] - function dcul() { - dcu "''${@}" && dcl "''${@}" - } - - # Autocomplete with available services. - function _dc_services() { - _autocomplete "$(docker compose config --services 2> /dev/null)" - } - - # Autocomplete with available container names. - function _dc_containers() { - _autocomplete "$(docker ps --format "\""{{.Names}}"\"")" - } - - complete -F _dc_services dcu dcd dcp dcl dcul dcdu dcr dcs dcpu - complete -F _dc_containers docker_volumes docker_ip - ''; -} diff --git a/home/program/bash/module/Docker.sh b/home/program/bash/module/Docker.sh new file mode 100644 index 00000000..f66ab288 --- /dev/null +++ b/home/program/bash/module/Docker.sh @@ -0,0 +1,95 @@ +# Show container's volumes. +# Usage: docker_volumes +function docker_volumes() { + docker inspect -f '{{ .Mounts }}' "${@}" +} + +# Check if any container exited. +function docker_health() { + docker ps -a | grep Exited +} + +# Find out container's IP address. +# Usage: docker_ip +function docker_ip() { + docker inspect -f '\'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'\' "${1}" | sed "s/^.//" | sed "s/.$//" +} + +# Update all docker images. +function docker_update() { + docker images --format "{{.Repository}}:{{.Tag}}" | xargs -L1 docker pull +} + +# Docker compose shortcut. +function dc() { + docker compose "${@}" +} + +# Docker compose up. +# Usage: dcu [SERVICES] +function dcu() { + docker compose up -d "${@}" +} + +# Docker compose down. +# Usage: dcd [SERVICES] +function dcd() { + docker compose down "${@}" +} + +# Docker compose pull. +# Usage: dcp [SERVICES] +function dcp() { + docker compose pull "${@}" +} + +# Docker compose logs. +# Usage: dcl [SERVICES] +function dcl() { + docker compose logs -f "${@}" +} + +# Docker compose restart. +# Usage: dcr [SERVICES] +function dcr() { + docker compose restart "${@}" +} + +# Docker compose stop. +# Usage: dcs [SERVICES] +function dcs() { + docker compose stop "${@}" +} + +# Docker compose down & up specified services. +# Usage: dcdu [SERVICES] +function dcdu() { + dcd "${@}" + dcu "${@}" +} + +# Docker compose pull & up specified services. +# Usage: dcpu [SERVICES] +function dcpu() { + dcp "${@}" + dcu "${@}" +} + +# Docker compose up & attach to logs for specified services. +# Usage: dcul [SERVICES] +function dcul() { + dcu "${@}" && dcl "${@}" +} + +# Autocomplete with available services. +function _dc_services() { + _autocomplete "$(docker compose config --services 2>/dev/null)" +} + +# Autocomplete with available container names. +function _dc_containers() { + _autocomplete "$(docker ps --format '"'{{.Names}}"\"")" +} + +complete -F _dc_services dcu dcd dcp dcl dcul dcdu dcr dcs dcpu +complete -F _dc_containers docker_volumes docker_ip diff --git a/home/program/bash/module/Dvd.nix b/home/program/bash/module/Dvd.nix deleted file mode 100644 index 126e4491..00000000 --- a/home/program/bash/module/Dvd.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ ... }: -{ - text = '' - # Burn specified iso file to DVD. - # Usage: dvd_burn_iso - function dvd_burn_iso() { - local iso="''${1}" - if [[ "''${iso}" = "" ]]; then - help dvd_burn_iso - return 2 - fi - - growisofs -dvd-compat -speed=8 -use-the-force-luke=tty -Z /dev/sr0="''${iso}" - } - - # Burn specified iso file to CD. - # Usage: cd_burn_iso - function cd_burn_iso() { - local iso="''${1}" - if [[ "''${iso}" = "" ]]; then - help cd_burn_iso - return 2 - fi - - wodim speed=8 -tao dev=/dev/sr0 "''${iso}" - } - - # Burn specified audio files to CD. - # Usage: cd_burn_audio - function cd_burn_audio() { - if [[ "''${*}" = "" ]]; then - help cd_burn_audio - return 2 - fi - - cdrecord -v dev=/dev/sr0 speed=8 -audio -pad "''${*}" - } - - # Spawn Nix shell with required tools. - function dvd_shell() { - SHELL_NAME="dvd" tmpshell dvdplusrwtools cdrkit - } - ''; -} diff --git a/home/program/bash/module/Dvd.sh b/home/program/bash/module/Dvd.sh new file mode 100644 index 00000000..a3764078 --- /dev/null +++ b/home/program/bash/module/Dvd.sh @@ -0,0 +1,39 @@ +# Burn specified iso file to DVD. +# Usage: dvd_burn_iso +function dvd_burn_iso() { + local iso="${1}" + if [[ ${iso} == "" ]]; then + help dvd_burn_iso + return 2 + fi + + growisofs -dvd-compat -speed=8 -use-the-force-luke=tty -Z /dev/sr0="${iso}" +} + +# Burn specified iso file to CD. +# Usage: cd_burn_iso +function cd_burn_iso() { + local iso="${1}" + if [[ ${iso} == "" ]]; then + help cd_burn_iso + return 2 + fi + + wodim speed=8 -tao dev=/dev/sr0 "${iso}" +} + +# Burn specified audio files to CD. +# Usage: cd_burn_audio +function cd_burn_audio() { + if [[ ${*} == "" ]]; then + help cd_burn_audio + return 2 + fi + + cdrecord -v dev=/dev/sr0 speed=8 -audio -pad "${*}" +} + +# Spawn Nix shell with required tools. +function dvd_shell() { + SHELL_NAME="dvd" tmpshell dvdplusrwtools cdrkit +} diff --git a/home/program/bash/module/Ffmpeg.nix b/home/program/bash/module/Ffmpeg.nix deleted file mode 100644 index 5b54909c..00000000 --- a/home/program/bash/module/Ffmpeg.nix +++ /dev/null @@ -1,113 +0,0 @@ -{ ... }: -{ - text = '' - # Mux audio into containers. File names in sound and current dirrectories must match. - # Usage: ffmpeg_mux_audio - function ffmpeg_mux_audio() { - if [[ "''${1}" = "" ]]; then - help ffmpeg_mux_audio - return 2 - fi - - for file in *; do ffmpeg -i "$file" -i "$1"/"$file" -c copy -map 0:v:0 -map 1:a:0 -shortest "$2"/"$file"; done - } - - # Mux cover into music file. - # Usage: ffmpeg_mux_cover - function ffmpeg_mux_cover() { - if [[ "''${1}" = "" ]]; then - help ffmpeg_mux_cover - return 2 - fi - - local format="''${1}" - local cover="''${2}" - - mkdir out - - case "''${format}" in - # "mka"|"mkv") - # for file in *.''${format}; do - # ffmpeg -i "''${file}" -attach "''${cover}" -map 0 -c copy -metadata:s:t mimetype="image/''${cover##*.}" -metadata:s:t:0 filename="cover.''${cover##*.}" "./out/''${file}" || return 1 - # done - # ;; - *) - for file in *.''${format}; do - # ffmpeg -i "''${file}" -i "''${cover}" -map 0 -map 0:-v? -map 1 -codec copy -metadata:s:v title="Album cover" -metadata:s:v comment="Cover (front)" -disposition:v attached_pic ./out/"''${file}" || return 1 - ffmpeg -i "''${file}" -i "''${cover}" -map 0 -map 1 -codec copy -metadata:s:v title="Album cover" -metadata:s:v comment="Cover (front)" -disposition:v attached_pic ./out/"''${file}" || return 1 - done - ;; - esac - - mv out/* . - rm -d out/ && rm "''${2}" - } - - # Generate music metadata from directory structure. - # Top dir is the Artist name like this: `The_Beatles`. - # Next are albums like this: `2010_My_love`. - # Inside are songs like this: `01_sample.flac`. - # Usage: ffmpeg_music_meta - function ffmpeg_music_meta() { - if [[ "''${1}" = "" ]]; then - help ffmpeg_music_meta - return 2 - fi - - local format="''${1}" - - ls *.''${format} &> /dev/null || return 1 - - local artist="''${PWD%/*}"; artist="''${artist##*/}"; artist="''${artist//_/ }" - local album="''${PWD##*/}"; album="''${album#*_}"; album="''${album//_/ }" - local year="''${PWD##*/}"; year="''${year%%_*}" - # local total=$(ls *.''${format} | wc -l) - - mkdir out - - for file in *.''${format}; do - local track="''${file%%_*}"; track=$((10#''${track})); [[ "''${track}" = "" ]] && track=0 - local title="''${file#*_}"; title="''${title%.*}"; title="''${title//_/ }" - - # echo "''${artist}; ''${album}; ''${year}; ''${track}; ''${title}" - # TODO: make it format-specific. - ffmpeg -i "''${file}" -map 0 -c copy -metadata "artists=" -metadata "artist=''${artist}" -metadata "album_artist=''${artist}" -metadata "album=''${album}" -metadata "date=''${year}" -metadata "year=''${year}" -metadata "date_released=''${year}" -metadata "track=''${track}" -metadata "part_number=''${track}" -metadata "title=''${title}" ./out/"''${file}" || return 1 - done - - mv out/* . - rm -d out/ - } - - # Rotate the video clock-wise. - # Usage: ffmpeg_rotate - function ffmpeg_rotate() { - if [[ "''${2}" = "" ]]; then - help ffmpeg_rotate - fi - - local angle="''${1}" - local target="''${2}" - - ffmpeg -display_rotation ''${angle} -i ''${target} -c copy _''${target} && mv _''${target} ''${target} || rm _''${target} - } - - # Get video FPS. - function _ffprobe_fps() { - local fps=$(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate "''${1}") - [[ "''${fps}" = "" ]] && fps=30 || fps=$((fps)) - echo "''${fps}" - } - - # Get recommended keyframe interval for a file. - function _ffprobe_keyint() { - local fps=$(_ffprobe_fps "''${1}") - echo $((fps*5)) - } - - # Get audio bitrage. 128 by default. - function _ffprobe_ba() { - local ba=$(ffprobe -v error -select_streams a:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1:nokey=1 "''${1}") - [[ "''${ba}" != "N/A" ]] && echo $((ba/1024)) || echo 128 - } - ''; -} diff --git a/home/program/bash/module/Ffmpeg.sh b/home/program/bash/module/Ffmpeg.sh new file mode 100644 index 00000000..77381478 --- /dev/null +++ b/home/program/bash/module/Ffmpeg.sh @@ -0,0 +1,117 @@ +# Mux audio into containers. File names in sound and current dirrectories must match. +# Usage: ffmpeg_mux_audio +function ffmpeg_mux_audio() { + if [[ ${1} == "" ]]; then + help ffmpeg_mux_audio + return 2 + fi + + for file in *; do ffmpeg -i "$file" -i "$1"/"$file" -c copy -map 0:v:0 -map 1:a:0 -shortest "$2"/"$file"; done +} + +# Mux cover into music file. +# Usage: ffmpeg_mux_cover +function ffmpeg_mux_cover() { + if [[ ${1} == "" ]]; then + help ffmpeg_mux_cover + return 2 + fi + + local format="${1}" + local cover="${2}" + + mkdir out + + case "${format}" in + # "mka"|"mkv") + # for file in *.${format}; do + # ffmpeg -i "${file}" -attach "${cover}" -map 0 -c copy -metadata:s:t mimetype="image/${cover##*.}" -metadata:s:t:0 filename="cover.${cover##*.}" "./out/${file}" || return 1 + # done + # ;; + *) + for file in *.${format}; do + # ffmpeg -i "${file}" -i "${cover}" -map 0 -map 0:-v? -map 1 -codec copy -metadata:s:v title="Album cover" -metadata:s:v comment="Cover (front)" -disposition:v attached_pic ./out/"${file}" || return 1 + ffmpeg -i "${file}" -i "${cover}" -map 0 -map 1 -codec copy -metadata:s:v title="Album cover" -metadata:s:v comment="Cover (front)" -disposition:v attached_pic ./out/"${file}" || return 1 + done + ;; + esac + + mv out/* . + rm -d out/ && rm "${2}" +} + +# Generate music metadata from directory structure. +# Top dir is the Artist name like this: `The_Beatles`. +# Next are albums like this: `2010_My_love`. +# Inside are songs like this: `01_sample.flac`. +# Usage: ffmpeg_music_meta +function ffmpeg_music_meta() { + if [[ ${1} == "" ]]; then + help ffmpeg_music_meta + return 2 + fi + + local format="${1}" + + ls *.${format} &>/dev/null || return 1 + + local artist="${PWD%/*}" + artist="${artist##*/}" + artist="${artist//_/ }" + local album="${PWD##*/}" + album="${album#*_}" + album="${album//_/ }" + local year="${PWD##*/}" + year="${year%%_*}" + # local total=$(ls *.${format} | wc -l) + + mkdir out + + for file in *.${format}; do + local track="${file%%_*}" + track=$((10#${track})) + [[ ${track} == "" ]] && track=0 + local title="${file#*_}" + title="${title%.*}" + title="${title//_/ }" + + # echo "${artist}; ${album}; ${year}; ${track}; ${title}" + # TODO: make it format-specific. + ffmpeg -i "${file}" -map 0 -c copy -metadata "artists=" -metadata "artist=${artist}" -metadata "album_artist=${artist}" -metadata "album=${album}" -metadata "date=${year}" -metadata "year=${year}" -metadata "date_released=${year}" -metadata "track=${track}" -metadata "part_number=${track}" -metadata "title=${title}" ./out/"${file}" || return 1 + done + + mv out/* . + rm -d out/ +} + +# Rotate the video clock-wise. +# Usage: ffmpeg_rotate +function ffmpeg_rotate() { + if [[ ${2} == "" ]]; then + help ffmpeg_rotate + fi + + local angle="${1}" + local target="${2}" + + ffmpeg -display_rotation ${angle} -i ${target} -c copy _${target} && mv _${target} ${target} || rm _${target} +} + +# Get video FPS. +function _ffprobe_fps() { + local fps=$(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate "${1}") + [[ ${fps} == "" ]] && fps=30 || fps=$((fps)) + echo "${fps}" +} + +# Get recommended keyframe interval for a file. +function _ffprobe_keyint() { + local fps=$(_ffprobe_fps "${1}") + echo $((fps * 5)) +} + +# Get audio bitrage. 128 by default. +function _ffprobe_ba() { + local ba=$(ffprobe -v error -select_streams a:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1:nokey=1 "${1}") + [[ ${ba} != "N/A" ]] && echo $((ba / 1024)) || echo 128 +} diff --git a/home/program/bash/module/File.nix b/home/program/bash/module/File.nix deleted file mode 100644 index 82023d4f..00000000 --- a/home/program/bash/module/File.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ ... }: -{ - text = '' - # Open file/dir in GUI. - # Usage: o - function o() { - xdg-open "''${@}" - } - ''; -} diff --git a/home/program/bash/module/File.sh b/home/program/bash/module/File.sh new file mode 100644 index 00000000..c1fc21dd --- /dev/null +++ b/home/program/bash/module/File.sh @@ -0,0 +1,5 @@ +# Open file/dir in GUI. +# Usage: o +function o() { + xdg-open "${@}" +} diff --git a/home/program/bash/module/Find.nix b/home/program/bash/module/Find.nix deleted file mode 100644 index 9224ae1f..00000000 --- a/home/program/bash/module/Find.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ ... }: -{ - text = '' - # Find all file extensions. - function find_ext() { - local types=($(find -type f | sed -e "s/.*\///" -e "s/^\.//" -e "/\./!d" -e "s/.*\.//")) - echo "''${types[@]}" | tr ' ' '\n' | sort -u - } - ''; -} diff --git a/home/program/bash/module/Find.sh b/home/program/bash/module/Find.sh new file mode 100644 index 00000000..266cba21 --- /dev/null +++ b/home/program/bash/module/Find.sh @@ -0,0 +1,5 @@ +# Find all file extensions. +function find_ext() { + local types=($(find -type f | sed -e "s/.*\///" -e "s/^\.//" -e "/\./!d" -e "s/.*\.//")) + echo "${types[@]}" | tr ' ' '\n' | sort -u +} diff --git a/home/program/bash/module/Fix.nix b/home/program/bash/module/Fix.nix deleted file mode 100644 index b2ded6bc..00000000 --- a/home/program/bash/module/Fix.nix +++ /dev/null @@ -1,25 +0,0 @@ -{ ... }: -{ - text = '' - # Fix when ethernet mistakenly detects 100 Mb instead of 1000 Mb. - # SPEED is one of 10/100/1000 etc. - # Usage: fix_ethernet_speed - function fix_ethernet_speed() { - local device="''${1}" - local speed="''${2}" - - if [[ "''${device}" = "" || "''${speed}" = "" ]]; then - help fix_ethernet_speed - return 2 - fi - - ethtool -s "''${device}" speed "''${speed}" - } - - # Delete lost Gradle lock files. - function fix_gradle_lock() { - cd "''${HOME}/.gradle" && find -type f | grep \\.lock$ | xargs -- rm - cd - - } - ''; -} diff --git a/home/program/bash/module/Fix.sh b/home/program/bash/module/Fix.sh new file mode 100644 index 00000000..5f0ff923 --- /dev/null +++ b/home/program/bash/module/Fix.sh @@ -0,0 +1,20 @@ +# Fix when ethernet mistakenly detects 100 Mb instead of 1000 Mb. +# SPEED is one of 10/100/1000 etc. +# Usage: fix_ethernet_speed +function fix_ethernet_speed() { + local device="${1}" + local speed="${2}" + + if [[ ${device} == "" || ${speed} == "" ]]; then + help fix_ethernet_speed + return 2 + fi + + ethtool -s "${device}" speed "${speed}" +} + +# Delete lost Gradle lock files. +function fix_gradle_lock() { + cd "${HOME}/.gradle" && find -type f | grep \\.lock$ | xargs -- rm + cd - +} diff --git a/home/program/bash/module/Git.nix b/home/program/bash/module/Git.nix deleted file mode 100644 index 5250c2ec..00000000 --- a/home/program/bash/module/Git.nix +++ /dev/null @@ -1,338 +0,0 @@ -{ ... }: -{ - text = '' - # Git push. - function gps() { - git push "''${@}" - } - - # Git push all (branches). Useful for pushing all stuff to a new remote. - function gpsa() { - local remotes=($(git remote)) - for remote in ''${remotes[@]}; do - echo -n "''${remote}: " - git push "''${remote}" --tags "refs/remotes/origin/*:refs/heads/*" - done - } - - # Git force push. - function gpsf() { - git push --force "''${@}" - } - - # Git pull. - function gpl() { - git pull "''${@}" - } - - # Git log. - function gl() { - git log --show-signature "''${@}" - } - - # Git status. - function gs() { - git status "''${@}" - } - - # Git stash. - function gst() { - git stash "''${@}" - } - - # Cd to git's root dir. - function gcd() { - local path=$(git rev-parse --show-toplevel) - [[ "''${path}" = "" ]] && return 1 - cd "''${path}" - } - - # Git diff. - function gd() { - git diff --patience "''${@}" - } - - # Git diff added. - function gda() { - git diff --cached --patience "''${@}" - } - - # Git commit. - function gc() { - git commit -m "''${@}" - } - - # Git clone with tree filter. - function gct() { - git clone --filter tree:0 ''${@} - } - - # Git clone full repo. - function gcf() { - git clone ''${@} - } - - # Git clone latest commit only. - function gcl() { - git clone --depth=1 --single-branch ''${@} - } - - # Git signed commit. - function gcs() { - git commit -S -m "''${@}" - } - - # Git checkout. - function gch() { - git checkout "''${@}" - } - - # Git checkout branch. - # Usage: gchb - function gchb() { - git checkout -b "''${@}" - } - - # Git branch. - function gb() { - git branch --all "''${@}" - } - - # Git branch delete. - # Usage: gbd - function gbd() { - git branch -D "''${@}" - } - - # Git branch delete all except current. - function gbda() { - git branch | grep -v ^* | xargs git branch -D - } - - # Git fetch all. - function gf() { - git fetch --all -v -p - } - - # Git tag. - function gt() { - git tag "''${@}" - } - - # Git ignore files. - function gi() { - git ls-files -ci --exclude-standard -z | xargs -0 git rm --cached - } - - # Git patch from staged diff. - # Usage: gpd - function gpd() { - git diff --staged --patch --binary --minimal - } - - # Git patch from commit. - # Usage: gpc [REF] [COUNT] - function gpc() { - local ref="''${1}" - local count="''${2}" - [[ "''${ref}" = "" ]] && ref="HEAD" - [[ "''${count}" = "" ]] && count=1 - git format-patch --stdout --minimal --patch --binary -''${count} "''${ref}" - } - - # Git patch apply. - # Usage: gpa - function gpa() { - git apply --index "''${@}" - } - - # Unstage changes. - # Usage: grs [FILES] - function grs() { - local target=''${@} - [[ "''${target}" = "" ]] && target="." - git restore --staged "''${target}" - } - - # Run git garbage collection. - function ggc() { - git gc --aggressive --no-cruft --prune=now - } - - # Check git file integrity. - function gfsck() { - git fsck - } - - # Preview diff while adding. Adds current dir by default. - # Usage: ga [FILES] - function ga() { - local target=''${@} - [[ "''${target}" = "" ]] && target="." - - git diff ''${target} - git add ''${target} - } - - # Rebase by X commits or from root. When COUNT is 0 - rebase from root. Default is 2. - # Usage: gr [COMMIT COUNT] - function gr() { - local base="''${1}" - - # Rebase last 2 commits by default. - if [[ "''${base}" = "" ]]; then - base="2" - fi - - # If 0, rebase from root. else from specified base. - if [[ "''${base}" = "0" ]]; then - git rebase -i --root - else - git rebase -i HEAD~''${base} - fi - } - - # Specify git user as Dmitry Voronin with provided email. - # Usage: gu [EMAIL] - function gu() { - local name="Dmitry Voronin" - local email="''${1}" - - if [[ "''${name}" = "" || "''${email}" = "" ]]; then - echo "usage: gu [EMAIL]" - return 2 - fi - - git config user.name "''${name}" - git config user.email "''${email}" - } - - # Get my git repo. - # Usage: gg - function gg() { - local repo="''${1}" - - if [[ "''${repo}" = "" ]]; then - help gg - return 2 - fi - - git clone ssh://git@git.voronind.com:22144/voronind/"''${repo}" - } - - # See diff for a specific commit. Last commit by default. - # Usage: gdc [COMMITHASH] - function gdc() { - local hash="''${1}" - [[ "''${hash}" = "" ]] && hash="HEAD" - git diff "''${hash}^!" - } - - # Get version number based on commit count. - function gv() { - git rev-list HEAD --count - } - - # Open the remote web url in default browser. - # Usage: gw [REMOTE] - function gw() { - local remote="''${1}" - [[ "''${remote}" = "" ]] && remote="$(git remote | head -n1)" - - local url="$(git remote get-url ''${remote})" - open "''${url}" - } - - # Search for string in whole git history. - # Usage: gsearch - function gsearch() { - local target="''${*}" - - if [[ "''${target}" = "" ]]; then - help gsearch - return 2 - fi - - git log -p -G "''${target}" - } - - # Sign the old commits. 0 to resign from root. - # Usage: gsign [COMMIT_COUNT] - function gsign() { - local base="''${1}" - - # Resign last commit by default. - if [[ "''${base}" = "" ]]; then - base="1" - fi - - # If 0, rebase from root. else from specified base. - if [[ "''${base}" = "0" ]]; then - git rebase --exec 'git commit --amend --no-edit -n -S' -i --root - else - git rebase --exec 'git commit --amend --no-edit -n -S' -i HEAD~''${base} - fi - } - - # Show current branch. - function _git_current_branch() { - git branch --show-current 2> /dev/null - } - - # Show origin's url. - function _git_origin_url() { - git remote get-url origin - } - - # Get this dotfiles url. - function _git_dotfiles_url() { - echo 'https://git.voronind.com/voronind/linux.git' - } - - # Check if current git repo is this dotfiles. - function _git_is_dotfiles() { - # [[ "$(_git_origin_url)" = "$(_git_dotfiles_url)" ]] - local dir="''${PWD}" - - while [[ "''${dir}" != "" ]]; do - if [[ -d "''${dir}/.git" ]]; then - if [[ "''${dir}" = "''${HOME}" ]] || [[ "''${dir}" = "$(realpath ''${HOME})" ]]; then - return 0 - else - return 1 - fi - fi - - dir="''${dir%/*}" - done - } - - # Autocomplete. - _completion_loader git &> /dev/null - __git_complete gps _git_push &> /dev/null - __git_complete gpsf _git_push &> /dev/null - __git_complete gpl _git_pull &> /dev/null - __git_complete gl _git_log &> /dev/null - __git_complete gs _git_status &> /dev/null - __git_complete gst _git_stash &> /dev/null - __git_complete gd _git_diff &> /dev/null - __git_complete gdc _git_diff &> /dev/null - __git_complete gc _git_commit &> /dev/null - __git_complete gch _git_checkout &> /dev/null - __git_complete gchb _git_checkout &> /dev/null - __git_complete gb _git_branch &> /dev/null - __git_complete gbd _git_branch &> /dev/null - __git_complete gf _git_fetch &> /dev/null - __git_complete gt _git_tag &> /dev/null - __git_complete gp _git_apply &> /dev/null - __git_complete ga _git_add &> /dev/null - __git_complete gw _git_pull &> /dev/null - - # Autocomplete with my git emails. - function _gu() { - _autocomplete hi@voronind.com dd.voronin@fsight.ru - } - - complete -F _gu gu - ''; -} diff --git a/home/program/bash/module/Git.sh b/home/program/bash/module/Git.sh new file mode 100644 index 00000000..a9edd2ae --- /dev/null +++ b/home/program/bash/module/Git.sh @@ -0,0 +1,333 @@ +# Git push. +function gps() { + git push "${@}" +} + +# Git push all (branches). Useful for pushing all stuff to a new remote. +function gpsa() { + local remotes=($(git remote)) + for remote in ${remotes[@]}; do + echo -n "${remote}: " + git push "${remote}" --tags "refs/remotes/origin/*:refs/heads/*" + done +} + +# Git force push. +function gpsf() { + git push --force "${@}" +} + +# Git pull. +function gpl() { + git pull "${@}" +} + +# Git log. +function gl() { + git log --show-signature "${@}" +} + +# Git status. +function gs() { + git status "${@}" +} + +# Git stash. +function gst() { + git stash "${@}" +} + +# Cd to git's root dir. +function gcd() { + local path=$(git rev-parse --show-toplevel) + [[ ${path} == "" ]] && return 1 + cd "${path}" +} + +# Git diff. +function gd() { + git diff --patience "${@}" +} + +# Git diff added. +function gda() { + git diff --cached --patience "${@}" +} + +# Git commit. +function gc() { + git commit -m "${@}" +} + +# Git clone with tree filter. +function gct() { + git clone --filter tree:0 ${@} +} + +# Git clone full repo. +function gcf() { + git clone ${@} +} + +# Git clone latest commit only. +function gcl() { + git clone --depth=1 --single-branch ${@} +} + +# Git signed commit. +function gcs() { + git commit -S -m "${@}" +} + +# Git checkout. +function gch() { + git checkout "${@}" +} + +# Git checkout branch. +# Usage: gchb +function gchb() { + git checkout -b "${@}" +} + +# Git branch. +function gb() { + git branch --all "${@}" +} + +# Git branch delete. +# Usage: gbd +function gbd() { + git branch -D "${@}" +} + +# Git branch delete all except current. +function gbda() { + git branch | grep -v ^* | xargs git branch -D +} + +# Git fetch all. +function gf() { + git fetch --all -v -p +} + +# Git tag. +function gt() { + git tag "${@}" +} + +# Git ignore files. +function gi() { + git ls-files -ci --exclude-standard -z | xargs -0 git rm --cached +} + +# Git patch from staged diff. +# Usage: gpd +function gpd() { + git diff --staged --patch --binary --minimal +} + +# Git patch from commit. +# Usage: gpc [REF] [COUNT] +function gpc() { + local ref="${1}" + local count="${2}" + [[ ${ref} == "" ]] && ref="HEAD" + [[ ${count} == "" ]] && count=1 + git format-patch --stdout --minimal --patch --binary -${count} "${ref}" +} + +# Git patch apply. +# Usage: gpa +function gpa() { + git apply --index "${@}" +} + +# Unstage changes. +# Usage: grs [FILES] +function grs() { + local target=${@} + [[ ${target} == "" ]] && target="." + git restore --staged "${target}" +} + +# Run git garbage collection. +function ggc() { + git gc --aggressive --no-cruft --prune=now +} + +# Check git file integrity. +function gfsck() { + git fsck +} + +# Preview diff while adding. Adds current dir by default. +# Usage: ga [FILES] +function ga() { + local target=${@} + [[ ${target} == "" ]] && target="." + + git diff ${target} + git add ${target} +} + +# Rebase by X commits or from root. When COUNT is 0 - rebase from root. Default is 2. +# Usage: gr [COMMIT COUNT] +function gr() { + local base="${1}" + + # Rebase last 2 commits by default. + if [[ ${base} == "" ]]; then + base="2" + fi + + # If 0, rebase from root. else from specified base. + if [[ ${base} == "0" ]]; then + git rebase -i --root + else + git rebase -i HEAD~${base} + fi +} + +# Specify git user as Dmitry Voronin with provided email. +# Usage: gu [EMAIL] +function gu() { + local name="Dmitry Voronin" + local email="${1}" + + if [[ ${name} == "" || ${email} == "" ]]; then + echo "usage: gu [EMAIL]" + return 2 + fi + + git config user.name "${name}" + git config user.email "${email}" +} + +# Get my git repo. +# Usage: gg +function gg() { + local repo="${1}" + + if [[ ${repo} == "" ]]; then + help gg + return 2 + fi + + git clone ssh://git@git.voronind.com:22144/voronind/"${repo}" +} + +# See diff for a specific commit. Last commit by default. +# Usage: gdc [COMMITHASH] +function gdc() { + local hash="${1}" + [[ ${hash} == "" ]] && hash="HEAD" + git diff "${hash}^!" +} + +# Get version number based on commit count. +function gv() { + git rev-list HEAD --count +} + +# Open the remote web url in default browser. +# Usage: gw [REMOTE] +function gw() { + local remote="${1}" + [[ ${remote} == "" ]] && remote="$(git remote | head -n1)" + + local url="$(git remote get-url ${remote})" + open "${url}" +} + +# Search for string in whole git history. +# Usage: gsearch +function gsearch() { + local target="${*}" + + if [[ ${target} == "" ]]; then + help gsearch + return 2 + fi + + git log -p -G "${target}" +} + +# Sign the old commits. 0 to resign from root. +# Usage: gsign [COMMIT_COUNT] +function gsign() { + local base="${1}" + + # Resign last commit by default. + if [[ ${base} == "" ]]; then + base="1" + fi + + # If 0, rebase from root. else from specified base. + if [[ ${base} == "0" ]]; then + git rebase --exec 'git commit --amend --no-edit -n -S' -i --root + else + git rebase --exec 'git commit --amend --no-edit -n -S' -i HEAD~${base} + fi +} + +# Show current branch. +function _git_current_branch() { + git branch --show-current 2>/dev/null +} + +# Show origin's url. +function _git_origin_url() { + git remote get-url origin +} + +# Get this dotfiles url. +function _git_dotfiles_url() { + echo 'https://git.voronind.com/voronind/linux.git' +} + +# Check if current git repo is this dotfiles. +function _git_is_dotfiles() { + # [[ "$(_git_origin_url)" = "$(_git_dotfiles_url)" ]] + local dir="${PWD}" + + while [[ ${dir} != "" ]]; do + if [[ -d "${dir}/.git" ]]; then + if [[ ${dir} == "${HOME}" ]] || [[ ${dir} == "$(realpath ${HOME})" ]]; then + return 0 + else + return 1 + fi + fi + + dir="${dir%/*}" + done +} + +# Autocomplete. +_completion_loader git &>/dev/null +__git_complete gps _git_push &>/dev/null +__git_complete gpsf _git_push &>/dev/null +__git_complete gpl _git_pull &>/dev/null +__git_complete gl _git_log &>/dev/null +__git_complete gs _git_status &>/dev/null +__git_complete gst _git_stash &>/dev/null +__git_complete gd _git_diff &>/dev/null +__git_complete gdc _git_diff &>/dev/null +__git_complete gc _git_commit &>/dev/null +__git_complete gch _git_checkout &>/dev/null +__git_complete gchb _git_checkout &>/dev/null +__git_complete gb _git_branch &>/dev/null +__git_complete gbd _git_branch &>/dev/null +__git_complete gf _git_fetch &>/dev/null +__git_complete gt _git_tag &>/dev/null +__git_complete gp _git_apply &>/dev/null +__git_complete ga _git_add &>/dev/null +__git_complete gw _git_pull &>/dev/null + +# Autocomplete with my git emails. +function _gu() { + _autocomplete hi@voronind.com dd.voronin@fsight.ru +} + +complete -F _gu gu diff --git a/home/program/bash/module/Gpg.nix b/home/program/bash/module/Gpg.nix deleted file mode 100644 index 36366ed3..00000000 --- a/home/program/bash/module/Gpg.nix +++ /dev/null @@ -1,96 +0,0 @@ -{ ... }: -{ - text = '' - # Check smartcard pin. - function scunlock() { - pkill keyboxd &> /dev/null - # pkill gpg-agent &> /dev/null - echo verify | gpg --card-edit --no-tty --command-fd=0 - } - - # Encrypt files to myself. - # Usage: encrypt - function encrypt() { - local IFS=$'\n' - local targets=(''${@}) - - if [[ "''${targets}" = "" ]]; then - help encrypt - return 2 - fi - - process() { - gpg --encrypt --armor --recipient hi@voronind.com --output "''${target}.gpg" "''${target}" - } - - _iterate_targets process ''${targets[@]} - } - - # Decrypt files to myself. - # Usage: decrypt [FILES] - function decrypt() { - local IFS=$'\n' - local targets=(''${@}) - - [[ "''${targets}" = "" ]] && targets=(*.gpg) - - process() { - gpg --decrypt --output "''${target%.gpg}" "''${target}" - } - - _iterate_targets process ''${targets[@]} - } - - # Sign a file. - # Usage: sign - function sign() { - local IFS=$'\n' - local targets=(''${@}) - - if [[ "''${targets}" = "" ]]; then - help sign - return 2 - fi - - process() { - gpg --detach-sig --armor --output "''${target}.sig" "''${target}" - } - - _iterate_targets process ''${targets[@]} - } - - # Verify a signature. All .sig files by default. - # Usage: verify [FILES] - function verify() { - local IFS=$'\n' - local targets=(''${@}) - - [[ "''${targets}" = "" ]] && targets=(*.sig) - - process() { - gpg --verify "''${target}" - } - - _iterate_targets process ''${targets[@]} - } - - # Find user keys using keyservers. - # Usage: gpg_find - function gpg_find() { - local email="''${1}" - - if [[ "''${email}" = "" ]]; then - help gpg_find - return 2 - fi - - gpg --locate-keys "''${email}" \ - || gpg --locate-keys --auto-key-locate hkps://keys.openpgp.org "''${email}" - } - - # Update keys. - function gpg_refresh() { - gpg --refresh-keys - } - ''; -} diff --git a/home/program/bash/module/Gpg.sh b/home/program/bash/module/Gpg.sh new file mode 100644 index 00000000..ff6fc6d7 --- /dev/null +++ b/home/program/bash/module/Gpg.sh @@ -0,0 +1,91 @@ +# Check smartcard pin. +function scunlock() { + pkill keyboxd &>/dev/null + # pkill gpg-agent &> /dev/null + echo verify | gpg --card-edit --no-tty --command-fd=0 +} + +# Encrypt files to myself. +# Usage: encrypt +function encrypt() { + local IFS=$'\n' + local targets=(${@}) + + if [[ ${targets} == "" ]]; then + help encrypt + return 2 + fi + + process() { + gpg --encrypt --armor --recipient hi@voronind.com --output "${target}.gpg" "${target}" + } + + _iterate_targets process ${targets[@]} +} + +# Decrypt files to myself. +# Usage: decrypt [FILES] +function decrypt() { + local IFS=$'\n' + local targets=(${@}) + + [[ ${targets} == "" ]] && targets=(*.gpg) + + process() { + gpg --decrypt --output "${target%.gpg}" "${target}" + } + + _iterate_targets process ${targets[@]} +} + +# Sign a file. +# Usage: sign +function sign() { + local IFS=$'\n' + local targets=(${@}) + + if [[ ${targets} == "" ]]; then + help sign + return 2 + fi + + process() { + gpg --detach-sig --armor --output "${target}.sig" "${target}" + } + + _iterate_targets process ${targets[@]} +} + +# Verify a signature. All .sig files by default. +# Usage: verify [FILES] +function verify() { + local IFS=$'\n' + local targets=(${@}) + + [[ ${targets} == "" ]] && targets=(*.sig) + + process() { + gpg --verify "${target}" + } + + _iterate_targets process ${targets[@]} +} + +# Find user keys using keyservers. +# Usage: gpg_find +function gpg_find() { + local email="${1}" + + if [[ ${email} == "" ]]; then + help gpg_find + return 2 + fi + + gpg --locate-keys "${email}" || + gpg --locate-keys --auto-key-locate hkps://keys.openpgp.org "${email}" +} + +# Update keys. +function gpg_refresh() { + gpg --refresh-keys +} diff --git a/home/program/bash/module/Group.nix b/home/program/bash/module/Group.nix deleted file mode 100644 index a19ae26f..00000000 --- a/home/program/bash/module/Group.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ ... }: -{ - text = '' - # Group files by extension. - # Usage: group_ext [FILES] - function group_ext() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(_ls_file)) - - process() { - local ext=''${target##*.} - [[ -d "''${target}" ]] && { _iterate_skip "Is a directory."; return 0; } - [[ "''${ext}" = "''${target}" ]] && { _iterate_skip "No extension."; return 0; } - - mkdir ''${ext} 2> /dev/null - - mv -- ''${target} ./''${ext}/''${target} - } - - _iterate_targets process ''${targets[@]} - } - - # Group files and dirs by year. - # Usage: group_year [FILES] - function group_year() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(ls)) - - process() { - local year=$(stat --format=%y ''${target}) - year=''${year%%-*} - - mkdir ''${year} 2> /dev/null - - mv -- ''${target} ./''${year}/''${target} - } - - _iterate_targets process ''${targets[@]} - } - - # Copy files from current year to the named dir. - # Usage: group_year_copy [FILES] - function group_year_copy() { - local IFS=$'\n' - local selected_year="''${1}" - local targets=(''${@:2}) - - if [[ "''${selected_year}" = "" ]]; then - help group_year_copy - return 2 - fi - - # All files by default. - [[ "''${targets}" = "" ]] && targets=($(ls)) - - mkdir ''${selected_year} 2> /dev/null - - process() { - local year=$(stat --format=%y ''${target}) - year=''${year%%-*} - - if [[ "''${year}" = "''${selected_year}" ]]; then - rcp -- ''${target} ./''${selected_year}/ - else - _iterate_skip "Skip: ''${year}" - fi - } - - _iterate_targets process ''${targets[@]} - } - ''; -} diff --git a/home/program/bash/module/Group.sh b/home/program/bash/module/Group.sh new file mode 100644 index 00000000..7cbd39f3 --- /dev/null +++ b/home/program/bash/module/Group.sh @@ -0,0 +1,75 @@ +# Group files by extension. +# Usage: group_ext [FILES] +function group_ext() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(_ls_file)) + + process() { + local ext=${target##*.} + [[ -d ${target} ]] && { + _iterate_skip "Is a directory." + return 0 + } + [[ ${ext} == "${target}" ]] && { + _iterate_skip "No extension." + return 0 + } + + mkdir ${ext} 2>/dev/null + + mv -- ${target} ./${ext}/${target} + } + + _iterate_targets process ${targets[@]} +} + +# Group files and dirs by year. +# Usage: group_year [FILES] +function group_year() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(ls)) + + process() { + local year=$(stat --format=%y ${target}) + year=${year%%-*} + + mkdir ${year} 2>/dev/null + + mv -- ${target} ./${year}/${target} + } + + _iterate_targets process ${targets[@]} +} + +# Copy files from current year to the named dir. +# Usage: group_year_copy [FILES] +function group_year_copy() { + local IFS=$'\n' + local selected_year="${1}" + local targets=(${@:2}) + + if [[ ${selected_year} == "" ]]; then + help group_year_copy + return 2 + fi + + # All files by default. + [[ ${targets} == "" ]] && targets=($(ls)) + + mkdir ${selected_year} 2>/dev/null + + process() { + local year=$(stat --format=%y ${target}) + year=${year%%-*} + + if [[ ${year} == "${selected_year}" ]]; then + rcp -- ${target} ./${selected_year}/ + else + _iterate_skip "Skip: ${year}" + fi + } + + _iterate_targets process ${targets[@]} +} diff --git a/home/program/bash/module/Help.nix b/home/program/bash/module/Help.nix deleted file mode 100644 index c08172e1..00000000 --- a/home/program/bash/module/Help.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ ... }: -{ - text = '' - # Get help about dotfiles bash function. - # Usage: help - function help() { - local fun="''${1}" - - if [[ "''${fun}" = "" ]] || [[ "$(find_function | grep ''${fun})" = "" ]]; then - help help - return 2 - fi - - cat ~/.bashrc | sed -n -e "/^function ''${fun}()/q;p" | tac | sed -n -e "/^[^#]/q;p" | tac | sed -e "s/^# \+//" -e "\$i \ " | sed "1{/^$/d}" | sed "1{/^ *$/d}" - } - - # Short for help. - # Usage: h - function h() { - help "''${@}" - } - - # Autocomplete with available functions. - function _help_functions() { - _autocomplete $(find_function) - } - - complete -F _help_functions help h - ''; -} diff --git a/home/program/bash/module/Help.sh b/home/program/bash/module/Help.sh new file mode 100644 index 00000000..620ca92e --- /dev/null +++ b/home/program/bash/module/Help.sh @@ -0,0 +1,25 @@ +# Get help about dotfiles bash function. +# Usage: help +function help() { + local fun="${1}" + + if [[ ${fun} == "" ]] || [[ "$(find_function | grep ${fun})" == "" ]]; then + help help + return 2 + fi + + cat ~/.bashrc | sed -n -e "/^function ${fun}()/q;p" | tac | sed -n -e "/^[^#]/q;p" | tac | sed -e "s/^# \+//" -e "\$i \ " | sed "1{/^$/d}" | sed "1{/^ *$/d}" +} + +# Short for help. +# Usage: h +function h() { + help "${@}" +} + +# Autocomplete with available functions. +function _help_functions() { + _autocomplete $(find_function) +} + +complete -F _help_functions help h diff --git a/home/program/bash/module/Ls.nix b/home/program/bash/module/Ls.nix deleted file mode 100644 index ca001d75..00000000 --- a/home/program/bash/module/Ls.nix +++ /dev/null @@ -1,71 +0,0 @@ -{ ... }: -{ - text = '' - # Unset possible system-defined aliases. - unalias l ll lll llll la lla &> /dev/null - unset l ll lll llll la lla &> /dev/null - - # List files in dirs. - # Current dir by default. - # Usage: l [DIRS] - function l() { - # ls -lhv --si --group-directories-first --color=auto -- "$@" - ccd "$@" - } - - # List last modified files first. - # Current dir by default. - # Usage: ll [DIRS] - function ll() { - ls -lhv --si --group-directories-first --color=auto -- "$@" - # ls -lhvtr --si --color=auto -- "$@" - } - - # List files in tree structure. - # Current dir by default. - # Depth can be omitted by passing `-` (dash). - # Usage: lll [DEPTH] [DIRS] - function lll() { - local IFS=$'\n' - local depth="''${1}" - local target=("''${@:2}") - - [[ "''${target}" = "" ]] && target="." - [[ "''${depth}" = "" ]] && depth=666 - [[ "''${depth}" = "-" ]] && depth=666 - - tree -a -L "''${depth}" -- "''${target[@]}" - } - - # List files recursively. - # Current dir by default. - # Usage: llll [DIRS] - function llll() { - ls -RlAhv --si --group-directories-first --color=auto -- "$@" - } - - # List all files in dirs, incl. hidden files. - # Current dir by default. - # Usage: la [DIRS] - function la() { - ls -lAh --si --group-directories-first --color=auto -- "$@" - } - - # List all files in dirs, incl. hidden files, sorted by mtime. - # Current dir by default. - # Usage: lla [DIRS] - function lla() { - ls -lAhtr --si --color=auto -- "$@" - } - - # List only files. - function _ls_file() { - ls --classify | grep -v \/$ - } - - # List only dirs. - function _ls_dir() { - ls --classify | grep \/$ | sed -e "s/\/$//" - } - ''; -} diff --git a/home/program/bash/module/Ls.sh b/home/program/bash/module/Ls.sh new file mode 100644 index 00000000..2027639e --- /dev/null +++ b/home/program/bash/module/Ls.sh @@ -0,0 +1,66 @@ +# Unset possible system-defined aliases. +unalias l ll lll llll la lla &>/dev/null +unset l ll lll llll la lla &>/dev/null + +# List files in dirs. +# Current dir by default. +# Usage: l [DIRS] +function l() { + # ls -lhv --si --group-directories-first --color=auto -- "$@" + ccd "$@" +} + +# List last modified files first. +# Current dir by default. +# Usage: ll [DIRS] +function ll() { + ls -lhv --si --group-directories-first --color=auto -- "$@" + # ls -lhvtr --si --color=auto -- "$@" +} + +# List files in tree structure. +# Current dir by default. +# Depth can be omitted by passing `-` (dash). +# Usage: lll [DEPTH] [DIRS] +function lll() { + local IFS=$'\n' + local depth="${1}" + local target=("${@:2}") + + [[ ${target} == "" ]] && target="." + [[ ${depth} == "" ]] && depth=666 + [[ ${depth} == "-" ]] && depth=666 + + tree -a -L "${depth}" -- "${target[@]}" +} + +# List files recursively. +# Current dir by default. +# Usage: llll [DIRS] +function llll() { + ls -RlAhv --si --group-directories-first --color=auto -- "$@" +} + +# List all files in dirs, incl. hidden files. +# Current dir by default. +# Usage: la [DIRS] +function la() { + ls -lAh --si --group-directories-first --color=auto -- "$@" +} + +# List all files in dirs, incl. hidden files, sorted by mtime. +# Current dir by default. +# Usage: lla [DIRS] +function lla() { + ls -lAhtr --si --color=auto -- "$@" +} + +# List only files. +function _ls_file() { + ls --classify | grep -v \/$ +} + +# List only dirs. +function _ls_dir() { + ls --classify | grep \/$ | sed -e "s/\/$//" +} diff --git a/home/program/bash/module/Name.nix b/home/program/bash/module/Name.nix deleted file mode 100644 index 3278b085..00000000 --- a/home/program/bash/module/Name.nix +++ /dev/null @@ -1,400 +0,0 @@ -{ ... }: -{ - text = '' - # Rename dirs to `snake_case` and files to `PascalCase`. Careful with structured file names like archives! - # Usage: name [FILES] - function name() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(ls)) - - process() { - # Skip archive. - if $(_is_archive "''${target}"); then - _iterate_skip "File is an archive, skip." - return 0 - fi - - if [[ -d "''${target}" ]]; then - local new_name=$(parse_snake ''${target}) - [[ -e "''${new_name}" ]] && return 0 - - mv -- ''${target} ''${new_name} && echo ''${new_name} - else - local ext=".''${target##*.}" - local name=''${target%.*} - [[ "''${ext}" = ".''${target}" ]] && ext="" - - local new_name="$(parse_pascal ''${name})''${ext}" - [[ -e "''${new_name}" ]] && return 0 - - mv -- ''${target} ''${new_name} && echo ''${new_name} - fi - } - - _iterate_targets process ''${targets[@]} - } - - # Rename files with provided parser, i.e. `parse_simple`. - # All files by default. - # Usage: name_parse [FILES] - function name_parse() { - local IFS=$'\n' - local parser=''${1} - local targets=(''${@:2}) - [[ "''${targets}" = "" ]] && targets=([^.]*) - - if [[ "''${parser}" = "" ]]; then - help name_parse - return 2 - fi - - process() { - # Skip archive. - if $(_is_archive "''${target}"); then - _iterate_skip "File is an archive, skip." - return 0 - fi - - # parse new name. - local ext="" - local name="''${target}" - - # ext only for files. - if [[ -f "''${target}" ]]; then - ext=".''${target##*.}" - name="''${target%.*}" - fi - - # Files w/o extension support. - [[ "''${ext#.}" = "''${name}" ]] && ext="" - - # Get new name. - local new_name=$(''${parser} "''${name}")''${ext,,} - - # check if same name. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # check if target name already exists. - if [[ -f "''${new_name}" ]]; then - _error "''${new_name}: Already exists!" - return 1 - fi - - # rename target. - mv -- "''${target}" "''${new_name}" && echo "''${new_name}" - } - - _iterate_targets process ''${targets[@]} - } - - # Rename all files to their hashes while keeping extensions. - # All files by default. - # Usage: name_hash [FILES] - function name_hash() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(_ls_file)) - - process() { - # extract extension. - local extension="''${target##*.}" - if [[ "''${extension}" = "''${target}" ]]; then - extension="" - else - extension=".''${extension}" - fi - - # hash the new name. - local hash=$(pv "''${target}" | sha1sum | cut -d\ -f1) - new_name="''${hash,,}''${extension,,}" - - # check if same name. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # rename target. - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Check hashes for previously renamed files. - # All files by default. - # Usage: name_hash_check [FILES] - function name_hash_check() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=([^.]*) - - process() { - # extract hashes. - local stored="''${target%%.*}" - local actual=$(pv "''${target}" | sha1sum | cut -d\ -f1) - - # compare hashes. - if [[ "''${stored}" != "''${actual}" ]]; then - _error "Failed." - return 1 - fi - } - - _iterate_targets process ''${targets[@]} - } - - # Rename files for Jellyfin shows, i.e. `Episode S01E01.mkv` - # All files by default. - # Usage: name_show [FILES] - function name_show() { - local IFS=$'\n' - local season="$(realpath .)"; season="''${season##*\ }" - local episode=0 - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(_ls_file)) - - # Error when no season number specified. - if [[ "''${season}" = "" ]]; then - _error "Could not determine season number." - return 2 - fi - - process() { - ((episode++)) - - # extract new name. - local new_name="Episode S''${season}E$(printf %02d ''${episode}).''${target##*.}" - - # Skip on no change. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # rename target. - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Rename files for Kavita manga format. - # All files by default. - # Usage: name_manga [FILES] - function name_manga() { - local IFS=$'\n' - local manga=''${PWD##*/} - local season=''${1} - local episode=0 - local targets=(''${@:2}) - [[ "''${targets}" = "" ]] && targets=($(_ls_file)) - - # Error when no season number specified. - if [[ "''${season}" = "" ]]; then - help name_manga - return 2 - fi - - process() { - ((episode++)) - - # Extract new name. - local new_name="''${manga} Vol.''${season} Ch.''${episode}.''${target##*.}" - - # Skip on no change. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # Rename target. - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Rename files for ffmpeg_music_meta format. - # All files by default. - # Usage: name_music [FILES] - function name_music() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(ls)) - - process() { - # Extract new name. - local ext=''${target##*.} - - if [[ -d "''${target}" ]]; then - local new_name="$(parse_startcase $(parse_simple ''${target%.*}))" - else - local new_name="$(parse_startcase $(parse_simple ''${target%.*})).''${ext}" - fi - - # Skip on no change. - [[ "''${target%/}" = "''${new_name}" ]] && return 0 - - # Rename target. - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Rename files with new extension. - # All files by default. - # Usage: name_ext [FILES] - function name_ext() { - local IFS=$'\n' - local extension=''${1} - local targets=(''${@:2}) - [[ "''${targets}" = "" ]] && targets=($(_ls_file)) - - # Error when no new extension specified. - if [[ "''${extension}" = "" ]]; then - help name_ext - return 2 - fi - - process() { - # Extract new name. - local new_name="''${target%.*}"."''${extension}" - - # Skip on no change. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # Rename target. - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Change file name prefix. - # All matching files by default. - # Usage: name_prefix [FILES] - function name_prefix() { - local IFS=$'\n' - local old=''${1} - local new=''${2} - local targets=(''${@:3}) - [[ "''${targets}" = "" ]] && targets=(''${old}*) - - process() { - # Create new name. - local new_name="''${new}''${target#$old}" - - # Skip on no change. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # Rename. - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Change file name postfix. - # All matching files by default. - # Usage: name_postfix [FILES] - function name_postfix() { - local IFS=$'\n' - local old=''${1} - local new=''${2} - local targets=(''${@:3}) - [[ "''${targets}" = "" ]] && targets=(*''${old}) - - process() { - # Create new name. - local new_name="''${target%$old}''${new}" - - # Skip on no change. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # Rename. - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Replace part of the name. - # All matching files by default. - # Usage: name_replace [FILES] - function name_replace() { - local IFS=$'\n' - local old=''${1} - local new=''${2} - local targets=(''${@:3}) - [[ "''${targets}" = "" ]] && targets=(*''${old}*) - - process() { - # Create new name. - local new_name="''${target//$old/$new}" - - # Skip on no change. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # Rename. - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - # Fix numbering for numbered files. I.e if there are 10 items and some of them start without zero, then append zero to it. 1..10 -> 01..10. - # Usage: name_fix_numbering [FILES] - function name_fix_numbering() { - local IFS=$'\n' - local highest=0 - local power=0 - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(ls | grep "^[0-9]")) - - # Count leading zeroes. - for target in "''${targets[@]}"; do - # Check that starts with a digit. - [[ "''${target}" =~ ^[0-9] ]] || continue - - local digits=($(parse_ints "''${target}")) - local digit="''${digits[0]}" - digit=$((10#''${digit})) - - [[ "''${digit}" -gt "''${highest}" ]] && highest="''${digit}" - done - - local i=''${highest} - while [[ i -gt 0 ]]; do - ((power++)) - i=$((''${i}/10)) - done - - process() { - # Check that starts with a digit. - if [[ ! "''${target}" =~ ^[0-9] ]]; then - _error "Does not start with a digit!" - return 1 - fi - - # Prepare new name. - local digits=($(parse_ints "''${target}")) - local digit="''${digits[0]}" - digit=$((10#''${digit})) - local new_name=$(printf "%0''${power}d" "''${digit}")"''${target#''${digits[0]}}" - - # Skip if the same name. - [[ "''${target}" = "''${new_name}" ]] && return 0 - - # Check that file does not exist. - if [[ -e "''${new_name}" ]]; then - _error "''${new_name}: File exists!" - return 1 - fi - - mv -- ''${target} ''${new_name} && echo ''${new_name} - } - - _iterate_targets process ''${targets[@]} - } - - function _comp_name_parse() { - _autocomplete $(find_function | grep ^parse) - } - - complete -o filenames -F _comp_name_parse name_parse - ''; -} diff --git a/home/program/bash/module/Name.sh b/home/program/bash/module/Name.sh new file mode 100644 index 00000000..533f0f62 --- /dev/null +++ b/home/program/bash/module/Name.sh @@ -0,0 +1,396 @@ +# Rename dirs to `snake_case` and files to `PascalCase`. Careful with structured file names like archives! +# Usage: name [FILES] +function name() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(ls)) + + process() { + # Skip archive. + if $(_is_archive "${target}"); then + _iterate_skip "File is an archive, skip." + return 0 + fi + + if [[ -d ${target} ]]; then + local new_name=$(parse_snake ${target}) + [[ -e ${new_name} ]] && return 0 + + mv -- ${target} ${new_name} && echo ${new_name} + else + local ext=".${target##*.}" + local name=${target%.*} + [[ ${ext} == ".${target}" ]] && ext="" + + local new_name="$(parse_pascal ${name})${ext}" + [[ -e ${new_name} ]] && return 0 + + mv -- ${target} ${new_name} && echo ${new_name} + fi + } + + _iterate_targets process ${targets[@]} +} + +# Rename files with provided parser, i.e. `parse_simple`. +# All files by default. +# Usage: name_parse [FILES] +function name_parse() { + local IFS=$'\n' + local parser=${1} + local targets=(${@:2}) + [[ ${targets} == "" ]] && targets=("[^.]*") + + if [[ ${parser} == "" ]]; then + help name_parse + return 2 + fi + + process() { + # Skip archive. + if $(_is_archive "${target}"); then + _iterate_skip "File is an archive, skip." + return 0 + fi + + # parse new name. + local ext="" + local name="${target}" + + # ext only for files. + if [[ -f ${target} ]]; then + ext=".${target##*.}" + name="${target%.*}" + fi + + # Files w/o extension support. + [[ ${ext#.} == "${name}" ]] && ext="" + + # Get new name. + local new_name=$(${parser} "${name}")${ext,,} + + # check if same name. + [[ ${target} == "${new_name}" ]] && return 0 + + # check if target name already exists. + if [[ -f ${new_name} ]]; then + _error "${new_name}: Already exists!" + return 1 + fi + + # rename target. + mv -- "${target}" "${new_name}" && echo "${new_name}" + } + + _iterate_targets process ${targets[@]} +} + +# Rename all files to their hashes while keeping extensions. +# All files by default. +# Usage: name_hash [FILES] +function name_hash() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(_ls_file)) + + process() { + # extract extension. + local extension="${target##*.}" + if [[ ${extension} == "${target}" ]]; then + extension="" + else + extension=".${extension}" + fi + + # hash the new name. + local hash=$(pv "${target}" | sha1sum | cut -d\ -f1) + new_name="${hash,,}${extension,,}" + + # check if same name. + [[ ${target} == "${new_name}" ]] && return 0 + + # rename target. + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Check hashes for previously renamed files. +# All files by default. +# Usage: name_hash_check [FILES] +function name_hash_check() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=("[^.]*") + + process() { + # extract hashes. + local stored="${target%%.*}" + local actual=$(pv "${target}" | sha1sum | cut -d\ -f1) + + # compare hashes. + if [[ ${stored} != "${actual}" ]]; then + _error "Failed." + return 1 + fi + } + + _iterate_targets process ${targets[@]} +} + +# Rename files for Jellyfin shows, i.e. `Episode S01E01.mkv` +# All files by default. +# Usage: name_show [FILES] +function name_show() { + local IFS=$'\n' + local season="$(realpath .)" + season="${season##*\ }" + local episode=0 + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(_ls_file)) + + # Error when no season number specified. + if [[ ${season} == "" ]]; then + _error "Could not determine season number." + return 2 + fi + + process() { + ((episode++)) + + # extract new name. + local new_name="Episode S${season}E$(printf %02d ${episode}).${target##*.}" + + # Skip on no change. + [[ ${target} == "${new_name}" ]] && return 0 + + # rename target. + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Rename files for Kavita manga format. +# All files by default. +# Usage: name_manga [FILES] +function name_manga() { + local IFS=$'\n' + local manga=${PWD##*/} + local season=${1} + local episode=0 + local targets=(${@:2}) + [[ ${targets} == "" ]] && targets=($(_ls_file)) + + # Error when no season number specified. + if [[ ${season} == "" ]]; then + help name_manga + return 2 + fi + + process() { + ((episode++)) + + # Extract new name. + local new_name="${manga} Vol.${season} Ch.${episode}.${target##*.}" + + # Skip on no change. + [[ ${target} == "${new_name}" ]] && return 0 + + # Rename target. + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Rename files for ffmpeg_music_meta format. +# All files by default. +# Usage: name_music [FILES] +function name_music() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(ls)) + + process() { + # Extract new name. + local ext=${target##*.} + + if [[ -d ${target} ]]; then + local new_name="$(parse_startcase $(parse_simple ${target%.*}))" + else + local new_name="$(parse_startcase $(parse_simple ${target%.*})).${ext}" + fi + + # Skip on no change. + [[ ${target%/} == "${new_name}" ]] && return 0 + + # Rename target. + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Rename files with new extension. +# All files by default. +# Usage: name_ext [FILES] +function name_ext() { + local IFS=$'\n' + local extension=${1} + local targets=(${@:2}) + [[ ${targets} == "" ]] && targets=($(_ls_file)) + + # Error when no new extension specified. + if [[ ${extension} == "" ]]; then + help name_ext + return 2 + fi + + process() { + # Extract new name. + local new_name="${target%.*}"."${extension}" + + # Skip on no change. + [[ ${target} == "${new_name}" ]] && return 0 + + # Rename target. + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Change file name prefix. +# All matching files by default. +# Usage: name_prefix [FILES] +function name_prefix() { + local IFS=$'\n' + local old=${1} + local new=${2} + local targets=(${@:3}) + [[ ${targets} == "" ]] && targets=(${old}*) + + process() { + # Create new name. + local new_name="${new}${target#$old}" + + # Skip on no change. + [[ ${target} == "${new_name}" ]] && return 0 + + # Rename. + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Change file name postfix. +# All matching files by default. +# Usage: name_postfix [FILES] +function name_postfix() { + local IFS=$'\n' + local old=${1} + local new=${2} + local targets=(${@:3}) + [[ ${targets} == "" ]] && targets=(*${old}) + + process() { + # Create new name. + local new_name="${target%$old}${new}" + + # Skip on no change. + [[ ${target} == "${new_name}" ]] && return 0 + + # Rename. + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Replace part of the name. +# All matching files by default. +# Usage: name_replace [FILES] +function name_replace() { + local IFS=$'\n' + local old=${1} + local new=${2} + local targets=(${@:3}) + [[ ${targets} == "" ]] && targets=(*${old}*) + + process() { + # Create new name. + local new_name="${target//$old/$new}" + + # Skip on no change. + [[ ${target} == "${new_name}" ]] && return 0 + + # Rename. + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +# Fix numbering for numbered files. I.e if there are 10 items and some of them start without zero, then append zero to it. 1..10 -> 01..10. +# Usage: name_fix_numbering [FILES] +function name_fix_numbering() { + local IFS=$'\n' + local highest=0 + local power=0 + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(ls | grep "^[0-9]")) + + # Count leading zeroes. + for target in "${targets[@]}"; do + # Check that starts with a digit. + [[ ${target} =~ ^[0-9] ]] || continue + + local digits=($(parse_ints "${target}")) + local digit="${digits[0]}" + digit=$((10#${digit})) + + [[ ${digit} -gt ${highest} ]] && highest="${digit}" + done + + local i=${highest} + while [[ i -gt 0 ]]; do + ((power++)) + i=$((i / 10)) + done + + process() { + # Check that starts with a digit. + if [[ ! ${target} =~ ^[0-9] ]]; then + _error "Does not start with a digit!" + return 1 + fi + + # Prepare new name. + local digits=($(parse_ints "${target}")) + local digit="${digits[0]}" + digit=$((10#${digit})) + local new_name=$(printf "%0${power}d" "${digit}")"${target#${digits[0]}}" + + # Skip if the same name. + [[ ${target} == "${new_name}" ]] && return 0 + + # Check that file does not exist. + if [[ -e ${new_name} ]]; then + _error "${new_name}: File exists!" + return 1 + fi + + mv -- ${target} ${new_name} && echo ${new_name} + } + + _iterate_targets process ${targets[@]} +} + +function _comp_name_parse() { + _autocomplete $(find_function | grep ^parse) +} + +complete -o filenames -F _comp_name_parse name_parse diff --git a/home/program/bash/module/Nix.nix b/home/program/bash/module/Nix.nix deleted file mode 100644 index 32b306e2..00000000 --- a/home/program/bash/module/Nix.nix +++ /dev/null @@ -1,90 +0,0 @@ -{ ... }: -{ - text = '' - # Spawn shell with specified nix environment. - # Uses flake.nix in current dir by default. - # Usage: shell [NAME] - function shell() { - local target="''${1}" - [[ "''${target}" = "" ]] && target="default" - - SHELL_NAME="''${target}" nix develop ".#''${target}" - } - - # Spawn temporary nix-shell with specified packages. - # Usage: tmpshell - function tmpshell() { - local IFS=$'\n' - local input=("''${@}") - local pkgs=() - local tag="''${1}" - - if [[ "''${input}" = "" ]]; then - help tmpshell - return 2 - fi - - for pkg in ''${input[@]}; do - pkgs+=("nixpkgs#''${pkg}") - done - - SHELL_NAME="''${tag}" NIXPKGS_ALLOW_INSECURE=1 NIXPKGS_ALLOW_UNFREE=1 nix shell --impure ''${pkgs[@]} - } - - function nix_depends() { - nix why-depends /run/current-system nixpkgs#''${1} - } - - # Run stuff directrly from Nixpks. - # Usage: nixpkgs_run [COMMAND] - function nixpkgs_run() { - local rev="''${1}" - local pkg="''${2}" - local cmd="''${@:3}" - - if [[ "''${pkg}" = "" ]]; then - help nixpkgs_run - return 2 - fi - - [[ "''${cmd}" = "" ]] && cmd="''${pkg}" - - SHELL_NAME="''${pkg}" NIXPKGS_ALLOW_INSECURE=1 NIXPKGS_ALLOW_UNFREE=1 nix shell --impure github:NixOS/nixpkgs/''${rev}#''${pkg} -c ''${cmd} - } - - # Prefetch to nix store. - # Usage: prefetch - function prefetch() { - local url="''${1}" - local name="''${1##*/}" - name=$(parse_alnum "''${name%%\?*}") - - if [[ "''${url}" = "" ]]; then - help prefetch - return 2 - fi - - local result=$(nix hash convert --to sri --hash-algo sha256 $(nix-prefetch-url --name "''${name}" "''${url}")) - printf "%s" ''${result} | copy - printf "%s\n" ''${result} - } - - # Run nix locally with no builders. - # Usage: nix_local - function nix_local() { - nix --option max-jobs $(_core_count) --builders "" --substituters https://cache.nixos.org ''${@} - } - - # Run test app from other people PRs. - # Usage: nix_test github:user/nixpkgs/# - function nix_test() { - if [[ "''${@}" = "" ]]; then - help nix_test - return 2 - fi - - local name=''${*##*#} - SHELL_NAME="''${name}" NIXPKGS_ALLOW_INSECURE=1 NIXPKGS_ALLOW_UNFREE=1 nix --option max-jobs $(_core_count) --builders "" --substituters https://cache.nixos.org shell --impure ''${@} - } - ''; -} diff --git a/home/program/bash/module/Nix.sh b/home/program/bash/module/Nix.sh new file mode 100644 index 00000000..d68dd8b5 --- /dev/null +++ b/home/program/bash/module/Nix.sh @@ -0,0 +1,85 @@ +# Spawn shell with specified nix environment. +# Uses flake.nix in current dir by default. +# Usage: shell [NAME] +function shell() { + local target="${1}" + [[ ${target} == "" ]] && target="default" + + SHELL_NAME="${target}" nix develop ".#${target}" +} + +# Spawn temporary nix-shell with specified packages. +# Usage: tmpshell +function tmpshell() { + local IFS=$'\n' + local input=("${@}") + local pkgs=() + local tag="${1}" + + if [[ ${input} == "" ]]; then + help tmpshell + return 2 + fi + + for pkg in ${input[@]}; do + pkgs+=("nixpkgs#${pkg}") + done + + SHELL_NAME="${tag}" NIXPKGS_ALLOW_INSECURE=1 NIXPKGS_ALLOW_UNFREE=1 nix shell --impure ${pkgs[@]} +} + +function nix_depends() { + nix why-depends /run/current-system nixpkgs#${1} +} + +# Run stuff directrly from Nixpks. +# Usage: nixpkgs_run [COMMAND] +function nixpkgs_run() { + local rev="${1}" + local pkg="${2}" + local cmd="${@:3}" + + if [[ ${pkg} == "" ]]; then + help nixpkgs_run + return 2 + fi + + [[ ${cmd} == "" ]] && cmd="${pkg}" + + SHELL_NAME="${pkg}" NIXPKGS_ALLOW_INSECURE=1 NIXPKGS_ALLOW_UNFREE=1 nix shell --impure github:NixOS/nixpkgs/${rev}#${pkg} -c ${cmd} +} + +# Prefetch to nix store. +# Usage: prefetch +function prefetch() { + local url="${1}" + local name="${1##*/}" + name=$(parse_alnum "${name%%\?*}") + + if [[ ${url} == "" ]]; then + help prefetch + return 2 + fi + + local result=$(nix hash convert --to sri --hash-algo sha256 $(nix-prefetch-url --name "${name}" "${url}")) + printf "%s" ${result} | copy + printf "%s\n" ${result} +} + +# Run nix locally with no builders. +# Usage: nix_local +function nix_local() { + nix --option max-jobs $(_core_count) --builders "" --substituters https://cache.nixos.org ${@} +} + +# Run test app from other people PRs. +# Usage: nix_test github:user/nixpkgs/# +function nix_test() { + if [[ ${@} == "" ]]; then + help nix_test + return 2 + fi + + local name=${*##*#} + SHELL_NAME="${name}" NIXPKGS_ALLOW_INSECURE=1 NIXPKGS_ALLOW_UNFREE=1 nix --option max-jobs $(_core_count) --builders "" --substituters https://cache.nixos.org shell --impure ${@} +} diff --git a/home/program/bash/module/Notify.nix b/home/program/bash/module/Notify.nix deleted file mode 100644 index 87065baf..00000000 --- a/home/program/bash/module/Notify.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ secret, ... }: -{ - text = '' - # Send Telegram notification. - # Usage: notify - function notify() { - curl -X POST -H 'Content-Type: Application/json' -d "${secret.tg.dt "false"}" ${secret.tg.bt} &> /dev/null - } - - # Send silent Telegram notification. - # Usage: notify_silent - function notify_silent() { - curl -X POST -H 'Content-Type: Application/json' -d "${secret.tg.dt "true"}" ${secret.tg.bt} &> /dev/null - } - ''; -} diff --git a/home/program/bash/module/Notify.sh b/home/program/bash/module/Notify.sh new file mode 100644 index 00000000..4d66e047 --- /dev/null +++ b/home/program/bash/module/Notify.sh @@ -0,0 +1,11 @@ +# Send Telegram notification. +# Usage: notify +function notify() { + curl -X POST -H 'Content-Type: Application/json' -d "@tgdata@" @tgbot@ &>/dev/null +} + +# Send silent Telegram notification. +# Usage: notify_silent +function notify_silent() { + curl -X POST -H 'Content-Type: Application/json' -d "@tgdatasilent@" @tgbot@ &>/dev/null +} diff --git a/home/program/bash/module/Own.nix b/home/program/bash/module/Own.nix deleted file mode 100644 index c1c69745..00000000 --- a/home/program/bash/module/Own.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ ... }: -{ - text = '' - # Change file ownership to specified user id and restrict access to him. - # Root user by default. This directory recursively by default. - # Usage: own [USER] [FILES] - function own() { - local IFS=$'\n' - local files=("''${@:2}") - local user="''${1}" - local group="''${1}" - - # default to current dir. - if [ "''${files[*]}" = "" ]; then - files=(".") - fi - - # default to current user. - if [ "''${user}" = "" ]; then - user="''${UID}" - fi - - # If not root, default to users group. - [[ "''${user}" = 0 ]] && group="0" || group="100" - - for file in "''${files[@]}"; do - # set ownership. - chown "''${user}":"''${group}" -R "''${file}" &> /dev/null - - # remove access from group and others. - chmod -077 -R "''${file}" - done - } - - function _complete_own() { - _autocomplete $(_get_users) - } - - complete -F _complete_own own - ''; -} diff --git a/home/program/bash/module/Own.sh b/home/program/bash/module/Own.sh new file mode 100644 index 00000000..c041132c --- /dev/null +++ b/home/program/bash/module/Own.sh @@ -0,0 +1,36 @@ +# Change file ownership to specified user id and restrict access to him. +# Root user by default. This directory recursively by default. +# Usage: own [USER] [FILES] +function own() { + local IFS=$'\n' + local files=("${@:2}") + local user="${1}" + local group="${1}" + + # default to current dir. + if [ "${files[*]}" = "" ]; then + files=(".") + fi + + # default to current user. + if [ "${user}" = "" ]; then + user="${UID}" + fi + + # If not root, default to users group. + [[ ${user} == 0 ]] && group="0" || group="100" + + for file in "${files[@]}"; do + # set ownership. + chown "${user}":"${group}" -R "${file}" &>/dev/null + + # remove access from group and others. + chmod -077 -R "${file}" + done +} + +function _complete_own() { + _autocomplete $(_get_users) +} + +complete -F _complete_own own diff --git a/home/program/bash/module/Pack.nix b/home/program/bash/module/Pack.nix deleted file mode 100644 index eaff2720..00000000 --- a/home/program/bash/module/Pack.nix +++ /dev/null @@ -1,209 +0,0 @@ -{ ... }: -{ - text = '' - export _unpack_supported=".tar$|.tgz$|.txz$|.tar.gz$|.tar.xz$|.zip$|.iso$|.rar$" - - # Pack files into desired format. - # All files and directories by default. - # Usage: pack [FILES] - function pack() { - local IFS=$'\n' - local output="''${1}" - local targets=("''${@:2}") - local format="''${output##*.}" - local name="''${output%.*}" - - # report no output. - if [[ "''${output}" = "" ]]; then - help pack - return 2 - fi - - # report no format. - if [[ "''${format}" = "" ]]; then - _error "Could not determine output format." - help pack - return 2 - fi - - # All targets by default. - [[ "''${targets}" = "" ]] && targets=(*) - - case "''${format}" in - "tgz") - _pack_tgz "''${output}" "''${targets[@]}" - ;; - "txz") - _pack_txz "''${output}" "''${targets[@]}" - ;; - "tar") - _pack_tar "''${output}" "''${targets[@]}" - ;; - "zip") - _pack_zip "''${output}" "''${targets[@]}" - ;; - "gz") - _pack_gz "''${output}" "''${targets[@]}" - ;; - "xz") - _pack_xz "''${output}" "''${targets[@]}" - ;; - "iso") - _pack_iso "''${output}" "''${targets[@]}" - ;; - *) - _error "''${target}: Format not supported." - return 2 - ;; - esac - } - - # Attempt to unpack. - # All supported formats by default. - # Usage: unpack [FILES] - function unpack() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=($(_ls_files | grep -E ''${_unpack_supported})) - - process() { - # Use full path to file. - target=''$(realpath "''${target}") - - # Check for archive. - if $(_is_archive "''${target}"); then - unarchive "''${target}" - return 0 - fi - - # Unpack file type. - local type="''${target##*.}" - - [[ "''${target}" =~ .tar.gz$ ]] && type="tar.gz" - [[ "''${target}" =~ .tar.xz$ ]] && type="tar.xz" - - # Make a dir for files. - local dir="''${target%.$type}"; dir="''${dir##*/}" - mkdir "''${dir}" > /dev/null - pushd "''${dir}" > /dev/null - - # Unpack content. - case "''${type,,}" in - "zip") - _unpack_zip "''${target}" - ;; - "7z") - _unpack_7z "''${target}" - ;; - "tgz"|"tar.gz") - _unpack_tgz "''${target}" - ;; - "txz"|"tar.xz") - _unpack_txz "''${target}" - ;; - "tar") - _unpack_tar "''${target}" - ;; - "iso") - _unpack_iso "''${target}" - ;; - "rar") - _unpack_rar "''${target}" - ;; - "xz") - _unpack_xz "''${target}" - ;; - "gz") - _unpack_gz "''${target}" - ;; - *) - _error "''${target}: Format not supported." - return 2 - ;; - esac - - # Cd back. - popd > /dev/null - } - - _iterate_targets process ''${targets[@]} - } - - function _pack_zip() { - zip -9 -r "''${@}" - } - - function _pack_tgz() { - tar -c "''${@:2}" | pv -s $(/usr/bin/env du -csb "''${@:2}" | sed -n -e '$p' | awk '{print $1}') | gzip -1 > "''${1}" - } - - function _pack_txz() { - tar -c "''${@:2}" | pv -s $(/usr/bin/env du -csb "''${@:2}" | sed -n -e '$p' | awk '{print $1}') | xz -9e > "''${1}" - } - - function _pack_tar() { - tar -c "''${@:2}" | pv -s $(/usr/bin/env du -csb "''${@:2}" | sed -n -e '$p' | awk '{print $1}') > "''${1}" - } - - function _pack_gz() { - pv "''${2}" | gzip -1 > "''${1}" - } - - function _pack_xz() { - pv "''${2}" | xz -9e > "''${1}" - } - - function _pack_iso() { - local input=("''${@:2}") - local output="''${1}" - local args=() - - for arg in ''${input[@]}; do - [[ -d "''${arg}" ]] || { - _error "''${arg} is not a directory." - return 1 - }; - - args+=("''${arg}=''${arg}") - done - - genisoimage -J -r -pad -o "''${output}" -graft-points "''${args[@]}" - } - - function _unpack_zip() { - unzip "''${1}" - } - - function _unpack_7z() { - 7za x "''${1}" - } - - function _unpack_tgz() { - pv "''${1}" | gzip -d | tar -xf - - } - - function _unpack_txz() { - pv "''${1}" | xz -d | tar -xf - - } - - function _unpack_tar() { - pv "''${1}" | tar -xf - - } - - function _unpack_iso() { - 7za x "''${1}" - } - - function _unpack_rar() { - unrar x "''${1}" - } - - function _unpack_gz() { - pv "''${1}" | gzip -d > "''${1%.gz}" - } - - function _unpack_xz() { - pv "''${1}" | xz -d > "''${1%.xz}" - } - ''; -} diff --git a/home/program/bash/module/Pack.sh b/home/program/bash/module/Pack.sh new file mode 100644 index 00000000..3c23e458 --- /dev/null +++ b/home/program/bash/module/Pack.sh @@ -0,0 +1,205 @@ +export _unpack_supported=".tar$|.tgz$|.txz$|.tar.gz$|.tar.xz$|.zip$|.iso$|.rar$" + +# Pack files into desired format. +# All files and directories by default. +# Usage: pack [FILES] +function pack() { + local IFS=$'\n' + local output="${1}" + local targets=("${@:2}") + local format="${output##*.}" + local name="${output%.*}" + + # report no output. + if [[ ${output} == "" ]]; then + help pack + return 2 + fi + + # report no format. + if [[ ${format} == "" ]]; then + _error "Could not determine output format." + help pack + return 2 + fi + + # All targets by default. + [[ ${targets} == "" ]] && targets=(*) + + case "${format}" in + "tgz") + _pack_tgz "${output}" "${targets[@]}" + ;; + "txz") + _pack_txz "${output}" "${targets[@]}" + ;; + "tar") + _pack_tar "${output}" "${targets[@]}" + ;; + "zip") + _pack_zip "${output}" "${targets[@]}" + ;; + "gz") + _pack_gz "${output}" "${targets[@]}" + ;; + "xz") + _pack_xz "${output}" "${targets[@]}" + ;; + "iso") + _pack_iso "${output}" "${targets[@]}" + ;; + *) + _error "${target}: Format not supported." + return 2 + ;; + esac +} + +# Attempt to unpack. +# All supported formats by default. +# Usage: unpack [FILES] +function unpack() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=($(_ls_files | grep -E ${_unpack_supported})) + + process() { + # Use full path to file. + target=$(realpath "${target}") + + # Check for archive. + if $(_is_archive "${target}"); then + unarchive "${target}" + return 0 + fi + + # Unpack file type. + local type="${target##*.}" + + [[ ${target} =~ .tar.gz$ ]] && type="tar.gz" + [[ ${target} =~ .tar.xz$ ]] && type="tar.xz" + + # Make a dir for files. + local dir="${target%.$type}" + dir="${dir##*/}" + mkdir "${dir}" >/dev/null + pushd "${dir}" >/dev/null + + # Unpack content. + case "${type,,}" in + "zip") + _unpack_zip "${target}" + ;; + "7z") + _unpack_7z "${target}" + ;; + "tgz" | "tar.gz") + _unpack_tgz "${target}" + ;; + "txz" | "tar.xz") + _unpack_txz "${target}" + ;; + "tar") + _unpack_tar "${target}" + ;; + "iso") + _unpack_iso "${target}" + ;; + "rar") + _unpack_rar "${target}" + ;; + "xz") + _unpack_xz "${target}" + ;; + "gz") + _unpack_gz "${target}" + ;; + *) + _error "${target}: Format not supported." + return 2 + ;; + esac + + # Cd back. + popd >/dev/null + } + + _iterate_targets process ${targets[@]} +} + +function _pack_zip() { + zip -9 -r "${@}" +} + +function _pack_tgz() { + tar -c "${@:2}" | pv -s $(/usr/bin/env du -csb "${@:2}" | sed -n -e '$p' | awk '{print $1}') | gzip -1 >"${1}" +} + +function _pack_txz() { + tar -c "${@:2}" | pv -s $(/usr/bin/env du -csb "${@:2}" | sed -n -e '$p' | awk '{print $1}') | xz -9e >"${1}" +} + +function _pack_tar() { + tar -c "${@:2}" | pv -s $(/usr/bin/env du -csb "${@:2}" | sed -n -e '$p' | awk '{print $1}') >"${1}" +} + +function _pack_gz() { + pv "${2}" | gzip -1 >"${1}" +} + +function _pack_xz() { + pv "${2}" | xz -9e >"${1}" +} + +function _pack_iso() { + local input=("${@:2}") + local output="${1}" + local args=() + + for arg in ${input[@]}; do + [[ -d ${arg} ]] || { + _error "${arg} is not a directory." + return 1 + } + + args+=("${arg}=${arg}") + done + + genisoimage -J -r -pad -o "${output}" -graft-points "${args[@]}" +} + +function _unpack_zip() { + unzip "${1}" +} + +function _unpack_7z() { + 7za x "${1}" +} + +function _unpack_tgz() { + pv "${1}" | gzip -d | tar -xf - +} + +function _unpack_txz() { + pv "${1}" | xz -d | tar -xf - +} + +function _unpack_tar() { + pv "${1}" | tar -xf - +} + +function _unpack_iso() { + 7za x "${1}" +} + +function _unpack_rar() { + unrar x "${1}" +} + +function _unpack_gz() { + pv "${1}" | gzip -d >"${1%.gz}" +} + +function _unpack_xz() { + pv "${1}" | xz -d >"${1%.xz}" +} diff --git a/home/program/bash/module/Parse.nix b/home/program/bash/module/Parse.nix deleted file mode 100644 index d5ae89a6..00000000 --- a/home/program/bash/module/Parse.nix +++ /dev/null @@ -1,173 +0,0 @@ -{ ... }: -{ - text = '' - export _PARSE_ALLOWED_CHARS="_-" - export _PARSE_SPLIT_CHARS="\.\ _-" - - # Parse data and output simplified format. - # Usage: parse_simple - function parse_simple() { - echo "''${*}" | sed \ - -e "s/[''${_PARSE_SPLIT_CHARS}]/_/g" \ - -e "s/[^[:alnum:]''${_PARSE_ALLOWED_CHARS}]//g" \ - -e "s/_\+/_/g" -e "s/-\+/-/g" \ - -e "s/_-/_/g" -e "s/-_/_/g" \ - -e "s/_\+/_/g" \ - -e "s/^_//" -e "s/_$//" - } - - # Parse to PascalCase. - # Usage: parse_pascal - function parse_pascal() { - local parts=($(_get_parts $(parse_simple "''${*}"))) - local result - - for part in "''${parts[@]}"; do - local word="''${part,,}" - word="''${word^}" - result="''${result}''${word}" - done - - echo "''${result}" - } - - # Parse to snake_case. - # Usage: parse_snake - function parse_snake() { - local parts=($(_get_parts $(parse_simple "''${*}"))) - local result - - for part in "''${parts[@]}"; do - local word="''${part,,}" - result="''${result}_''${word}" - done - - echo "''${result#_}" - } - - # Parse to kebab-case. - # Usage: parse_kebab - function parse_kebab() { - local parts=($(_get_parts $(parse_simple "''${*}"))) - local result - - for part in "''${parts[@]}"; do - local word="''${part,,}" - result="''${result}-''${word}" - done - - echo "''${result#-}" - } - - # Parse to camelCase. - # Usage: parse_camel - function parse_camel() { - local parts=($(_get_parts $(parse_simple "''${*}"))) - local result - - for part in "''${parts[@]}"; do - local word="''${part,,}" - word="''${word^}" - result="''${result}''${word}" - done - - echo "''${result,}" - } - - # Parse to SNAKE_CASE_UPPERCASE. **NOT STABLE! Repeating results in different output.** - # Usage: parse_snake_uppercase - function parse_snake_uppercase() { - local parts=($(_get_parts $(parse_simple "''${*}"))) - local result - - for part in "''${parts[@]}"; do - local word="''${part^^}" - result="''${result}_''${word}" - done - - echo "''${result#_}" - } - - # Parse data keeping only alphanumeric characters. - # Usage: parse_alnum - function parse_alnum() { - echo "''${*}" | sed -e "s/[^[:alnum:]]//g" - } - - # Parse integers from mixed string. - # Usage: parse_ints - function parse_ints() { - echo "''${*}" | tr '\n' ' ' | sed -e 's/[^0-9]/ /g' -e 's/^ *//g' -e 's/ *$//g' | tr -s ' ' | sed 's/ /\n/g' - } - - # Parse string to lowercase. - # Usage: parse_lowercase - function parse_lowercase() { - echo "''${*,,}" - } - - # Parse string to uppercase. - # Usage: parse_uppercase - function parse_uppercase() { - echo "''${*^^}" - } - - # Parse string to title case. - # Usage: parse_titlecase - function parse_titlecase() { - local IFS=$'\n' - local parts=($(_parse_split ''${@})) - local minors=("is" "at" "of" "to" "in" "for" "the" "a" "an" "and" "but" "or" "on" "was" "were" "been" "be" "do" "did" "does") - - echo -n "$(parse_sentencecase ''${parts[0]})" - for part in ''${parts[@]:1}; do - if _contains $(echo ''${part,,} | sed -e "s/[''${_PARSE_SPLIT_CHARS}]//g") ''${minors[@]}; then - echo -n "''${part,,}" - else - echo -n "$(parse_sentencecase ''${part})" - fi - done - - echo - } - - # Parse string to sentence case. - # Usage: parse_sentencecase - function parse_sentencecase() { - local lower="''${*,,}" - echo "''${lower^}" - } - - # Parse string to start case. - # Usage: parse_startcase - function parse_startcase() { - local IFS=$'\n' - local lower="''${*,,}" - local parts=($(_parse_split ''${lower})) - - for part in ''${parts[@]}; do - echo -n "''${part^}" - done - - echo - } - - # Parse string to pretty Json. - # Usage: parse_json - function parse_json() { - echo "''${*}" | jq - } - - # Split string by separators. - # Usage: _parse_split - function _parse_split() { - echo "''${*}" | sed -e "s/[A-Z]\+/\n&/g" -e "s/[0-9]\+/\n&\n/g" -e "s/[''${_PARSE_SPLIT_CHARS}]/&\n/g" | sed -e "/^$/d" - } - - # Get name parts. - # Usage: _get_parts - function _get_parts() { - _parse_split "''${*}" | sed -e "s/[''${_PARSE_SPLIT_CHARS}]//g" | sed -e "/^$/d" - } - ''; -} diff --git a/home/program/bash/module/Parse.sh b/home/program/bash/module/Parse.sh new file mode 100644 index 00000000..0d10b312 --- /dev/null +++ b/home/program/bash/module/Parse.sh @@ -0,0 +1,168 @@ +export _PARSE_ALLOWED_CHARS="_-" +export _PARSE_SPLIT_CHARS="\.\ _-" + +# Parse data and output simplified format. +# Usage: parse_simple +function parse_simple() { + echo "${*}" | sed \ + -e "s/[${_PARSE_SPLIT_CHARS}]/_/g" \ + -e "s/[^[:alnum:]${_PARSE_ALLOWED_CHARS}]//g" \ + -e "s/_\+/_/g" -e "s/-\+/-/g" \ + -e "s/_-/_/g" -e "s/-_/_/g" \ + -e "s/_\+/_/g" \ + -e "s/^_//" -e "s/_$//" +} + +# Parse to PascalCase. +# Usage: parse_pascal +function parse_pascal() { + local parts=($(_get_parts $(parse_simple "${*}"))) + local result + + for part in "${parts[@]}"; do + local word="${part,,}" + word="${word^}" + result="${result}${word}" + done + + echo "${result}" +} + +# Parse to snake_case. +# Usage: parse_snake +function parse_snake() { + local parts=($(_get_parts $(parse_simple "${*}"))) + local result + + for part in "${parts[@]}"; do + local word="${part,,}" + result="${result}_${word}" + done + + echo "${result#_}" +} + +# Parse to kebab-case. +# Usage: parse_kebab +function parse_kebab() { + local parts=($(_get_parts $(parse_simple "${*}"))) + local result + + for part in "${parts[@]}"; do + local word="${part,,}" + result="${result}-${word}" + done + + echo "${result#-}" +} + +# Parse to camelCase. +# Usage: parse_camel +function parse_camel() { + local parts=($(_get_parts $(parse_simple "${*}"))) + local result + + for part in "${parts[@]}"; do + local word="${part,,}" + word="${word^}" + result="${result}${word}" + done + + echo "${result,}" +} + +# Parse to SNAKE_CASE_UPPERCASE. **NOT STABLE! Repeating results in different output.** +# Usage: parse_snake_uppercase +function parse_snake_uppercase() { + local parts=($(_get_parts $(parse_simple "${*}"))) + local result + + for part in "${parts[@]}"; do + local word="${part^^}" + result="${result}_${word}" + done + + echo "${result#_}" +} + +# Parse data keeping only alphanumeric characters. +# Usage: parse_alnum +function parse_alnum() { + echo "${*}" | sed -e "s/[^[:alnum:]]//g" +} + +# Parse integers from mixed string. +# Usage: parse_ints +function parse_ints() { + echo "${*}" | tr '\n' ' ' | sed -e 's/[^0-9]/ /g' -e 's/^ *//g' -e 's/ *$//g' | tr -s ' ' | sed 's/ /\n/g' +} + +# Parse string to lowercase. +# Usage: parse_lowercase +function parse_lowercase() { + echo "${*,,}" +} + +# Parse string to uppercase. +# Usage: parse_uppercase +function parse_uppercase() { + echo "${*^^}" +} + +# Parse string to title case. +# Usage: parse_titlecase +function parse_titlecase() { + local IFS=$'\n' + local parts=($(_parse_split ${@})) + local minors=("is" "at" "of" "to" "in" "for" "the" "a" "an" "and" "but" "or" "on" "was" "were" "been" "be" "do" "did" "does") + + echo -n "$(parse_sentencecase ${parts[0]})" + for part in ${parts[@]:1}; do + if _contains $(echo ${part,,} | sed -e "s/[${_PARSE_SPLIT_CHARS}]//g") ${minors[@]}; then + echo -n "${part,,}" + else + echo -n "$(parse_sentencecase ${part})" + fi + done + + echo +} + +# Parse string to sentence case. +# Usage: parse_sentencecase +function parse_sentencecase() { + local lower="${*,,}" + echo "${lower^}" +} + +# Parse string to start case. +# Usage: parse_startcase +function parse_startcase() { + local IFS=$'\n' + local lower="${*,,}" + local parts=($(_parse_split ${lower})) + + for part in ${parts[@]}; do + echo -n "${part^}" + done + + echo +} + +# Parse string to pretty Json. +# Usage: parse_json +function parse_json() { + echo "${*}" | jq +} + +# Split string by separators. +# Usage: _parse_split +function _parse_split() { + echo "${*}" | sed -e "s/[A-Z]\+/\n&/g" -e "s/[0-9]\+/\n&\n/g" -e "s/[${_PARSE_SPLIT_CHARS}]/&\n/g" | sed -e "/^$/d" +} + +# Get name parts. +# Usage: _get_parts +function _get_parts() { + _parse_split "${*}" | sed -e "s/[${_PARSE_SPLIT_CHARS}]//g" | sed -e "/^$/d" +} diff --git a/home/program/bash/module/Permission.nix b/home/program/bash/module/Permission.nix deleted file mode 100644 index f861cc14..00000000 --- a/home/program/bash/module/Permission.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ ... }: -{ - text = '' - # Recursively change permissions to allow read sharing with group and others. - function perm_share() { - find . -type d -exec chmod 755 {} \;; find . -type f -exec chmod 644 {} \; - } - - # Recursively change permissions to restrict access for group and others. - function perm() { - find . -type d -exec chmod 700 {} \;; find . -type f -exec chmod 600 {} \; - } - ''; -} diff --git a/home/program/bash/module/Permission.sh b/home/program/bash/module/Permission.sh new file mode 100644 index 00000000..c7a7b03c --- /dev/null +++ b/home/program/bash/module/Permission.sh @@ -0,0 +1,11 @@ +# Recursively change permissions to allow read sharing with group and others. +function perm_share() { + find . -type d -exec chmod 755 {} \; + find . -type f -exec chmod 644 {} \; +} + +# Recursively change permissions to restrict access for group and others. +function perm() { + find . -type d -exec chmod 700 {} \; + find . -type f -exec chmod 600 {} \; +} diff --git a/home/program/bash/module/Picture.nix b/home/program/bash/module/Picture.nix deleted file mode 100644 index 41b0412a..00000000 --- a/home/program/bash/module/Picture.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ ... }: -{ - text = '' - # Quick edit a picture and copy to clipboard. - # Usage: pic_copy - function pic_copy() { - swappy -f "''${1}" -o - | copy - } - - # Quick edit a pictures inplace. - # Usage: pic_edit - function pic_edit() { - local IFS=$'\n' - for file in "''${@}"; do - swappy -f "''${file}" -o "''${file}" - done - } - ''; -} diff --git a/home/program/bash/module/Picture.sh b/home/program/bash/module/Picture.sh new file mode 100644 index 00000000..f02f5116 --- /dev/null +++ b/home/program/bash/module/Picture.sh @@ -0,0 +1,14 @@ +# Quick edit a picture and copy to clipboard. +# Usage: pic_copy +function pic_copy() { + swappy -f "${1}" -o - | copy +} + +# Quick edit a pictures inplace. +# Usage: pic_edit +function pic_edit() { + local IFS=$'\n' + for file in "${@}"; do + swappy -f "${file}" -o "${file}" + done +} diff --git a/home/program/bash/module/Print.nix b/home/program/bash/module/Print.nix deleted file mode 100644 index cd09d4a0..00000000 --- a/home/program/bash/module/Print.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ ... }: -{ - text = '' - # Printf shortcut. - # Usage: print [TEXT] - function print() { - printf "%s" "''${*}" - } - ''; -} diff --git a/home/program/bash/module/Print.sh b/home/program/bash/module/Print.sh new file mode 100644 index 00000000..fe3fc611 --- /dev/null +++ b/home/program/bash/module/Print.sh @@ -0,0 +1,5 @@ +# Printf shortcut. +# Usage: print [TEXT] +function print() { + printf "%s" "${*}" +} diff --git a/home/program/bash/module/Ps.nix b/home/program/bash/module/Ps.nix deleted file mode 100644 index 0a9afe10..00000000 --- a/home/program/bash/module/Ps.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ ... }: -{ - text = '' - # Find process and filter. - # Usage: fps [PROCESS] - function fps() { - local process="''${1}" - - if [[ "''${process}" = "" ]]; then - ps aux - else - ps aux | sed -n -e "1p" -e "/''${process}/Ip" | sed -e "/sed -n -e 1p -e/d" - fi - } - ''; -} diff --git a/home/program/bash/module/Ps.sh b/home/program/bash/module/Ps.sh new file mode 100644 index 00000000..c85aaef7 --- /dev/null +++ b/home/program/bash/module/Ps.sh @@ -0,0 +1,11 @@ +# Find process and filter. +# Usage: fps [PROCESS] +function fps() { + local process="${1}" + + if [[ ${process} == "" ]]; then + ps aux + else + ps aux | sed -n -e "1p" -e "/${process}/Ip" | sed -e "/sed -n -e 1p -e/d" + fi +} diff --git a/home/program/bash/module/Ps1.nix b/home/program/bash/module/Ps1.nix deleted file mode 100644 index 436b98a1..00000000 --- a/home/program/bash/module/Ps1.nix +++ /dev/null @@ -1,159 +0,0 @@ -{ config, ... }: -let - accent = "${config.module.style.color.accentR};${config.module.style.color.accentG};${config.module.style.color.accentB}"; - negative = "${config.module.style.color.negativeR};${config.module.style.color.negativeG};${config.module.style.color.negativeB}"; - neutral = "${config.module.style.color.neutralR};${config.module.style.color.neutralG};${config.module.style.color.neutralB}"; - positive = "${config.module.style.color.positiveR};${config.module.style.color.positiveG};${config.module.style.color.positiveB}"; -in -{ - text = '' - export PROMPT_COMMAND=(__prompt_command "''${PROMPT_COMMAND[@]}") - - function __prompt_color() { - local color="''${1}" - if [[ "''${color}" = "" ]]; then - printf "\[\x1b[0m\]" - else - printf "\[\x1b[38;2;''${color}m\]" - fi - } - - # Custom terminal prompt format. - function __prompt_command() { - local last_status="''${?}" - local is_error=false - local is_root=false - - if [[ ''${last_status} != 0 && ''${last_status} != 130 ]]; then - is_error=true - fi - if [[ "''${UID}" -eq 0 ]]; then - is_root=true - fi - - # Add newline. - PS1="\n" - - # Set error red. - if ''${is_error}; then - PS1+="$(__prompt_color '${negative}')" - PS1+="[" - else - PS1+="$(__prompt_color)" - PS1+="[" - fi - - # Add time. - # PS1+="$(__prompt_color '${accent}')" - # PS1+="$(date +%H:%M) " - - # Set root red. - if ''${is_root}; then - PS1+="$(__prompt_color '${negative}')" - else - PS1+="$(__prompt_color '${neutral}')" - fi - - # Add user, host and working dir. - PS1+="\u@\h " - PS1+="$(__prompt_color '${positive}')" - PS1+="\w" - # PS1+="\''${PWD}" - - # Add git branch if available. - local git_branch="$(_git_current_branch)" - if [[ "''${git_branch}" != "" ]]; then - PS1+=" $(__prompt_color '${accent}')@''${git_branch}" - fi - - # Set error red. - if ''${is_error}; then - PS1+="$(__prompt_color '${negative}')" - PS1+="] " - else - PS1+="$(__prompt_color)" - PS1+="] " - fi - - # If error, show code. - if ''${is_error}; then - PS1+="$(__prompt_color '${negative}')(" - PS1+="''${last_status}" - local error_type="$(_ps1error ''${last_status})" - [[ "''${error_type}" != "" ]] && PS1+=" ''${error_type}" - PS1+=")$(__prompt_color) " - fi - - # Command on new line. - PS1+="\n" - - # Show nix shell name or shell depth. - if [ -n "''${SHELL_NAME}" ]; then - PS1+="''${SHELL_NAME} " - fi - - # Show remote connections. - if [ -n "''${SSH_TTY}" ]; then - PS1+=">" - fi - - PS1+="$(__prompt_color)" - - # Set user tag. - if ''${is_root}; then - PS1+="# " - else - PS1+="$ " - fi - - # Reset color. - PS1+="\[\033[0m\]" - } - - # Convert error code into short description. - # Usage: _ps1error - function _ps1error() { - local type - case ''${1} in - 1) type="GENERAL" ;; - 2) type="MISUSE" ;; - 126) type="CANTEXEC" ;; - 127) type="CMDNF" ;; - 129) type="SIGHUP" ;; - 130) type="SIGINT" ;; - 131) type="SIGQUIT" ;; - 132) type="SIGILL" ;; - 133) type="SIGTRAP" ;; - 134) type="SIGABRT" ;; - 135) type="SIGBUS" ;; - 136) type="SIGFPE" ;; - 137) type="SIGKILL" ;; - 138) type="SIGUSR1" ;; - 139) type="SIGSEGV" ;; - 140) type="SIGUSR2" ;; - 141) type="SIGPIPE" ;; - 142) type="SIGALRM" ;; - 143) type="SIGTERM" ;; - 144) type="" ;; - 145) type="SIGCHLD" ;; - 146) type="SIGCONT" ;; - 147) type="SIGSTOP" ;; - 148) type="SIGTSTP" ;; - 149) type="SIGTTIN" ;; - 150) type="SIGTTOU" ;; - 151) type="SIGURG" ;; - 152) type="SIGXCPU" ;; - 153) type="SIGXFSZ" ;; - 154) type="SIGVTALRM" ;; - 155) type="SIGPROF" ;; - 156) type="SIGWINCH" ;; - 157) type="SIGIO" ;; - 158) type="SIGPWR" ;; - 159) type="SIGSYS" ;; - *) type="" ;; - esac - - echo -n "''${type}" - } - ''; -} diff --git a/home/program/bash/module/Ps1.sh b/home/program/bash/module/Ps1.sh new file mode 100644 index 00000000..e62c0006 --- /dev/null +++ b/home/program/bash/module/Ps1.sh @@ -0,0 +1,144 @@ +export PROMPT_COMMAND=(__prompt_command "${PROMPT_COMMAND[@]}") + +function __prompt_color() { + local color="${1}" + if [[ ${color} == "" ]]; then + printf "\[\x1b[0m\]" + else + printf "\[\x1b[38;2;${color}m\]" + fi +} + +# Custom terminal prompt format. +function __prompt_command() { + local last_status="${?}" + local is_error=false + local is_root=false + + if [[ ${last_status} != 0 && ${last_status} != 130 ]]; then + is_error=true + fi + if [[ ${UID} -eq 0 ]]; then + is_root=true + fi + + # Add newline. + PS1="\n" + + # Set error red. + if ${is_error}; then + PS1+="$(__prompt_color '@negative@')" + PS1+="[" + else + PS1+="$(__prompt_color)" + PS1+="[" + fi + + # Set root red. + if ${is_root}; then + PS1+="$(__prompt_color '@negative@')" + else + PS1+="$(__prompt_color '@neutral@')" + fi + + # Add user, host and working dir. + PS1+="\u@\h " + PS1+="$(__prompt_color '@positive@')" + PS1+="\w" + # PS1+="\${PWD}" + + # Add git branch if available. + local git_branch="$(_git_current_branch)" + if [[ ${git_branch} != "" ]]; then + PS1+=" $(__prompt_color '@accent@')@${git_branch}" + fi + + # Set error red. + if ${is_error}; then + PS1+="$(__prompt_color '@negative@')" + PS1+="] " + else + PS1+="$(__prompt_color)" + PS1+="] " + fi + + # If error, show code. + if ${is_error}; then + PS1+="$(__prompt_color '@negative@')(" + PS1+="${last_status}" + local error_type="$(_ps1error ${last_status})" + [[ ${error_type} != "" ]] && PS1+=" ${error_type}" + PS1+=")$(__prompt_color) " + fi + + # Command on new line. + PS1+="\n" + + # Show nix shell name or shell depth. + if [ -n "${SHELL_NAME}" ]; then + PS1+="${SHELL_NAME} " + fi + + # Show remote connections. + if [ -n "${SSH_TTY}" ]; then + PS1+=">" + fi + + PS1+="$(__prompt_color)" + + # Set user tag. + if ${is_root}; then + PS1+="# " + else + PS1+="$ " + fi + + # Reset color. + PS1+="\[\033[0m\]" +} + +# Convert error code into short description. +# Usage: _ps1error +function _ps1error() { + local type + case ${1} in + 1) type="GENERAL" ;; + 2) type="MISUSE" ;; + 126) type="CANTEXEC" ;; + 127) type="CMDNF" ;; + 129) type="SIGHUP" ;; + 130) type="SIGINT" ;; + 131) type="SIGQUIT" ;; + 132) type="SIGILL" ;; + 133) type="SIGTRAP" ;; + 134) type="SIGABRT" ;; + 135) type="SIGBUS" ;; + 136) type="SIGFPE" ;; + 137) type="SIGKILL" ;; + 138) type="SIGUSR1" ;; + 139) type="SIGSEGV" ;; + 140) type="SIGUSR2" ;; + 141) type="SIGPIPE" ;; + 142) type="SIGALRM" ;; + 143) type="SIGTERM" ;; + 144) type="" ;; + 145) type="SIGCHLD" ;; + 146) type="SIGCONT" ;; + 147) type="SIGSTOP" ;; + 148) type="SIGTSTP" ;; + 149) type="SIGTTIN" ;; + 150) type="SIGTTOU" ;; + 151) type="SIGURG" ;; + 152) type="SIGXCPU" ;; + 153) type="SIGXFSZ" ;; + 154) type="SIGVTALRM" ;; + 155) type="SIGPROF" ;; + 156) type="SIGWINCH" ;; + 157) type="SIGIO" ;; + 158) type="SIGPWR" ;; + 159) type="SIGSYS" ;; + *) type="" ;; + esac + + echo -n "${type}" +} diff --git a/home/program/bash/module/Qr.nix b/home/program/bash/module/Qr.nix deleted file mode 100644 index 083da59d..00000000 --- a/home/program/bash/module/Qr.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ ... }: -{ - text = '' - function qr() { - qrencode -t ansiutf8 - } - ''; -} diff --git a/home/program/bash/module/Qr.sh b/home/program/bash/module/Qr.sh new file mode 100644 index 00000000..33ff8451 --- /dev/null +++ b/home/program/bash/module/Qr.sh @@ -0,0 +1,3 @@ +function qr() { + qrencode -t ansiutf8 +} diff --git a/home/program/bash/module/Radj.nix b/home/program/bash/module/Radj.nix deleted file mode 100644 index 9b1ee35f..00000000 --- a/home/program/bash/module/Radj.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ ... }: -{ - text = '' - # Adjust ryzen temp limit. - # Usage: radj [TEMP] - function radj() { - local limit="''${1}" - if [[ "''${limit}" = "" ]]; then - systemctl start radj.service - else - systemctl stop radj.service - ryzenadj --tctl-temp=''${limit} - fi - } - ''; -} diff --git a/home/program/bash/module/Radj.sh b/home/program/bash/module/Radj.sh new file mode 100644 index 00000000..7a327083 --- /dev/null +++ b/home/program/bash/module/Radj.sh @@ -0,0 +1,11 @@ +# Adjust ryzen temp limit. +# Usage: radj [TEMP] +function radj() { + local limit="${1}" + if [[ ${limit} == "" ]]; then + systemctl start radj.service + else + systemctl stop radj.service + ryzenadj --tctl-temp=${limit} + fi +} diff --git a/home/program/bash/module/Random.nix b/home/program/bash/module/Random.nix deleted file mode 100644 index e32160cc..00000000 --- a/home/program/bash/module/Random.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ ... }: -{ - text = '' - # Generate random string. - # Usage: random - function random() { - local length="''${1}" - if [[ "''${length}" = "" ]]; then - help random - return 2 - fi - head /dev/urandom | tr -dc A-Za-z0-9 | head -c''${length} - } - - # Picks a random file or directory. - function random_file() { - local IFS=$'\n' - local dirs=($(ls)) - local total=''${#dirs[@]} - ((total--)) - local index=$(shuf -i 0-''${total} -n 1) - - printf "%s" ''${dirs[$index]} - } - ''; -} diff --git a/home/program/bash/module/Random.sh b/home/program/bash/module/Random.sh new file mode 100644 index 00000000..c1d14082 --- /dev/null +++ b/home/program/bash/module/Random.sh @@ -0,0 +1,21 @@ +# Generate random string. +# Usage: random +function random() { + local length="${1}" + if [[ ${length} == "" ]]; then + help random + return 2 + fi + head /dev/urandom | tr -dc A-Za-z0-9 | head -c${length} +} + +# Picks a random file or directory. +function random_file() { + local IFS=$'\n' + local dirs=($(ls)) + local total=${#dirs[@]} + ((total--)) + local index=$(shuf -i 0-${total} -n 1) + + printf "%s" ${dirs[$index]} +} diff --git a/home/program/bash/module/Recursive.nix b/home/program/bash/module/Recursive.nix deleted file mode 100644 index 07e836fc..00000000 --- a/home/program/bash/module/Recursive.nix +++ /dev/null @@ -1,85 +0,0 @@ -{ ... }: -{ - text = '' - # Run something recursively over all directories. - # Usage: recursive - function recursive() { - if [[ "''${*}" = "" ]]; then - help recursive - return 2 - fi - - local IFS=$'\n' - local current="''${PWD}" - local dirs=$(find -type d) - local total=$(find -type d | wc -l) # TODO: don't call find twice. won't work with "echo ''${dirs}". - local count=0 - local failed=0 - - for dir in ''${dirs}; do - # increment counter. - ((count++)) - - # cd into the next dir. - cd "''${current}" || failed=''${?} - cd "''${dir}" || failed=''${?} - - # echo status. - echo -e "''${color_bblue}[''${count}/''${total}] ''${dir}''${color_default}" - - # run command. - ''${*} || failed=''${?} - - # Add newline if not the last one. - [[ "''${count}" = "''${total}" ]] || echo - done - - # return back on complete. - cd "''${current}" || failed=''${?} - - return ''${failed} - } - - # Run something recursively over directories of 1 depth (excluding current dir). - # Usage: recursive1 - function recursive1() { - if [[ "''${*}" = "" ]]; then - help recursive1 - return 2 - fi - - local IFS=$'\n' - local current="''${PWD}" - local dirs=$(find -mindepth 1 -maxdepth 1 -type d) - local total=$(find -mindepth 1 -maxdepth 1 -type d | wc -l) # TODO: don't call find twice. won't work with "echo ''${dirs}". - local count=0 - local failed=0 - - for dir in ''${dirs}; do - # increment counter. - ((count++)) - - # cd into the next dir. - cd "''${current}" - cd "''${dir}" - - # echo status. - echo -e "''${color_bblue}[''${count}/''${total}] ''${dir}''${color_default}" - - # run command. - ''${*} || failed=''${?} - - # Add newline if not the last one. - [[ "''${count}" = "''${total}" ]] || echo - done - - # return back on complete. - cd "''${current}" - - return ''${failed} - } - - # autocomplete. - complete -F _autocomplete_nested recursive recursive1 - ''; -} diff --git a/home/program/bash/module/Recursive.sh b/home/program/bash/module/Recursive.sh new file mode 100644 index 00000000..d24a1995 --- /dev/null +++ b/home/program/bash/module/Recursive.sh @@ -0,0 +1,80 @@ +# Run something recursively over all directories. +# Usage: recursive +function recursive() { + if [[ ${*} == "" ]]; then + help recursive + return 2 + fi + + local IFS=$'\n' + local current="${PWD}" + local dirs=$(find -type d) + local total=$(find -type d | wc -l) # TODO: don't call find twice. won't work with "echo ${dirs}". + local count=0 + local failed=0 + + for dir in ${dirs}; do + # increment counter. + ((count++)) + + # cd into the next dir. + cd "${current}" || failed=${?} + cd "${dir}" || failed=${?} + + # echo status. + echo -e "${color_bblue}[${count}/${total}] ${dir}${color_default}" + + # run command. + ${*} || failed=${?} + + # Add newline if not the last one. + [[ ${count} == "${total}" ]] || echo + done + + # return back on complete. + cd "${current}" || failed=${?} + + return ${failed} +} + +# Run something recursively over directories of 1 depth (excluding current dir). +# Usage: recursive1 +function recursive1() { + if [[ ${*} == "" ]]; then + help recursive1 + return 2 + fi + + local IFS=$'\n' + local current="${PWD}" + local dirs=$(find -mindepth 1 -maxdepth 1 -type d) + local total=$(find -mindepth 1 -maxdepth 1 -type d | wc -l) # TODO: don't call find twice. won't work with "echo ${dirs}". + local count=0 + local failed=0 + + for dir in ${dirs}; do + # increment counter. + ((count++)) + + # cd into the next dir. + cd "${current}" + cd "${dir}" + + # echo status. + echo -e "${color_bblue}[${count}/${total}] ${dir}${color_default}" + + # run command. + ${*} || failed=${?} + + # Add newline if not the last one. + [[ ${count} == "${total}" ]] || echo + done + + # return back on complete. + cd "${current}" + + return ${failed} +} + +# autocomplete. +complete -F _autocomplete_nested recursive recursive1 diff --git a/home/program/bash/module/Reload.nix b/home/program/bash/module/Reload.nix deleted file mode 100644 index 04555f5b..00000000 --- a/home/program/bash/module/Reload.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ ... }: -{ - text = '' - function reload() { - source ~/.bashrc - } - trap reload USR1 - ''; -} diff --git a/home/program/bash/module/Reload.sh b/home/program/bash/module/Reload.sh new file mode 100644 index 00000000..2809fa50 --- /dev/null +++ b/home/program/bash/module/Reload.sh @@ -0,0 +1,4 @@ +function reload() { + source ~/.bashrc +} +trap reload USR1 diff --git a/home/program/bash/module/Shopt.nix b/home/program/bash/module/Shopt.nix deleted file mode 100644 index 85d49c0c..00000000 --- a/home/program/bash/module/Shopt.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ ... }: -{ - text = '' - shopt -s dotglob - shopt -s globstar - shopt -s autocd - shopt -s extglob - - # Enable vim mode. - bind 'set editing-mode vi' - bind 'set show-mode-in-prompt on' - bind 'set keyseq-timeout 0' - - # Set the mode string and cursor to indicate the vim mode - # For the number after `\e[`: - # 0: blinking block - # 1: blinking block (default) - # 2: steady block - # 3: blinking underline - # 4: steady underline - # 5: blinking bar (xterm) - # 6: steady bar (xterm) - bind 'set vi-ins-mode-string \1\e[6 q\2' - bind 'set vi-cmd-mode-string \1\e[2 q\2' - - exec {BASH_XTRACEFD}>/dev/null - ''; -} diff --git a/home/program/bash/module/Shopt.sh b/home/program/bash/module/Shopt.sh new file mode 100644 index 00000000..64484418 --- /dev/null +++ b/home/program/bash/module/Shopt.sh @@ -0,0 +1,23 @@ +shopt -s dotglob +shopt -s globstar +shopt -s autocd +shopt -s extglob + +# Enable vim mode. +bind 'set editing-mode vi' +bind 'set show-mode-in-prompt on' +bind 'set keyseq-timeout 0' + +# Set the mode string and cursor to indicate the vim mode +# For the number after `\e[`: +# 0: blinking block +# 1: blinking block (default) +# 2: steady block +# 3: blinking underline +# 4: steady underline +# 5: blinking bar (xterm) +# 6: steady bar (xterm) +bind 'set vi-ins-mode-string \1\e[6 q\2' +bind 'set vi-cmd-mode-string \1\e[2 q\2' + +exec {BASH_XTRACEFD}>/dev/null diff --git a/home/program/bash/module/Ssh.nix b/home/program/bash/module/Ssh.nix deleted file mode 100644 index 01e61952..00000000 --- a/home/program/bash/module/Ssh.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ ... }: -{ - text = '' - # Kill all ssh sockets. - function sshka() { - rm ~/.ssh/*.socket - } - ''; -} diff --git a/home/program/bash/module/Ssh.sh b/home/program/bash/module/Ssh.sh new file mode 100644 index 00000000..368aac03 --- /dev/null +++ b/home/program/bash/module/Ssh.sh @@ -0,0 +1,4 @@ +# Kill all ssh sockets. +function sshka() { + rm ~/.ssh/*.socket +} diff --git a/home/program/bash/module/Su.nix b/home/program/bash/module/Su.nix deleted file mode 100644 index 0ba3c542..00000000 --- a/home/program/bash/module/Su.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ ... }: -{ - text = '' - # Su shortcut for lazy me. - # Root by default. - # Usage: s [USER] - function s() { - su - ''${1} - } - alias su="SHELL_NAME=su su" - - # Run something as root. Runs command as a current user if su is not available. - # Usage: sudo - function sudo() { - if command -v su &> /dev/null; then - su -c "$(echo ''${*} | tr '\n' ' ')" - else - ''${*} - fi - } - - # Run something as current user. If fails, try to run with sudo. - # Usage: trysudo - function trysudo() { - ''${*} || sudo ''${*} - } - - function _complete_s() { - _autocomplete $(_get_users) - } - - complete -F _complete_s s - complete -F _autocomplete_nested sudo trysudo - ''; -} diff --git a/home/program/bash/module/Su.sh b/home/program/bash/module/Su.sh new file mode 100644 index 00000000..38a7b1d5 --- /dev/null +++ b/home/program/bash/module/Su.sh @@ -0,0 +1,30 @@ +# Su shortcut for lazy me. +# Root by default. +# Usage: s [USER] +function s() { + su - ${1} +} +alias su="SHELL_NAME=su su" + +# Run something as root. Runs command as a current user if su is not available. +# Usage: sudo +function sudo() { + if command -v su &>/dev/null; then + su -c "$(echo ${*} | tr '\n' ' ')" + else + ${*} + fi +} + +# Run something as current user. If fails, try to run with sudo. +# Usage: trysudo +function trysudo() { + ${*} || sudo ${*} +} + +function _complete_s() { + _autocomplete $(_get_users) +} + +complete -F _complete_s s +complete -F _autocomplete_nested sudo trysudo diff --git a/home/program/bash/module/Switch.nix b/home/program/bash/module/Switch.nix deleted file mode 100644 index 221efad1..00000000 --- a/home/program/bash/module/Switch.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ ... }: -{ - text = '' - # Install on a Nintendo Switch. - # Usage: switch_install - function switch_install() { - local IFS=$'\n' - local targets=(''${@}) - [[ "''${targets}" = "" ]] && targets=(*.ns[pz]) - - local id=$(_switch_id) - _switch_mount - - install() { - gio copy -p "''${target}" ''${id}5:\ SD\ Card\ install/ - } - - _iterate_targets install ''${targets[@]} - } - - # Backup a Nintendo Switch saves and album. - function switch_backup() { - local id=$(_switch_id) - _switch_mount - mkdir switch_backup || rm -r switch_backup/* - mkdir switch_backup/{save,album} - pushd switch_backup/save - cp -r /run/user/''${UID}/gvfs/mtp\:host\=-_DBI_*/7\:\ Saves/* . - popd - pushd switch_backup/album - cp -r /run/user/''${UID}/gvfs/mtp\:host\=-_DBI_*/8\:\ Album/* . - popd - pushd switch_backup - archive_fast album - archive save - } - - function _switch_id() { - gio mount -l -i | rg 'mtp://-_DBI_' | sed "s/^.*=//" | head -1 - } - - function _switch_mount() { - test -d /run/user/''${UID}/gvfs/mtp\:host\=-_DBI_* || gio mount "$(_switch_id)" - } - ''; -} diff --git a/home/program/bash/module/Switch.sh b/home/program/bash/module/Switch.sh new file mode 100644 index 00000000..e5eec6d2 --- /dev/null +++ b/home/program/bash/module/Switch.sh @@ -0,0 +1,41 @@ +# Install on a Nintendo Switch. +# Usage: switch_install +function switch_install() { + local IFS=$'\n' + local targets=(${@}) + [[ ${targets} == "" ]] && targets=("*.ns[pz]") + + local id=$(_switch_id) + _switch_mount + + install() { + gio copy -p "${target}" ${id}5:\ SD\ Card\ install/ + } + + _iterate_targets install ${targets[@]} +} + +# Backup a Nintendo Switch saves and album. +function switch_backup() { + local id=$(_switch_id) + _switch_mount + mkdir switch_backup || rm -r switch_backup/* + mkdir switch_backup/{save,album} + pushd switch_backup/save + cp -r /run/user/${UID}/gvfs/mtp\:host\=-_DBI_*/7\:\ Saves/* . + popd + pushd switch_backup/album + cp -r /run/user/${UID}/gvfs/mtp\:host\=-_DBI_*/8\:\ Album/* . + popd + pushd switch_backup + archive_fast album + archive save +} + +function _switch_id() { + gio mount -l -i | rg 'mtp://-_DBI_' | sed "s/^.*=//" | head -1 +} + +function _switch_mount() { + test -d /run/user/${UID}/gvfs/mtp\:host\=-_DBI_* || gio mount "$(_switch_id)" +} diff --git a/home/program/bash/module/Tmux.nix b/home/program/bash/module/Tmux.nix deleted file mode 100644 index 880124c2..00000000 --- a/home/program/bash/module/Tmux.nix +++ /dev/null @@ -1,99 +0,0 @@ -{ ... }: -{ - text = '' - # Create/attach to named session. - # By default uses current dir name. - # Usage: ta [NAME] - function ta() { - local name="''${1}" - - # Set default name. - # [[ "''${name}" = "" ]] && name="main" - [[ "''${name}" = "" ]] && name=$(parse_alnum "''${PWD##*/}") - - # Create session. - tmux new-session -s "''${name}" -d &> /dev/null - - # Attach to session. - if _is_tmux; then - tmux switch-client -t "''${name}" - else - tmux attach-session -t "''${name}" - fi - } - - # Detach from running session. - function td() { - tmux detach-client - } - - # Detach all other tmux clients. - function tda() { - tmux detach-client -s $(tmux display-message -p '#S') - } - - # List running sessions. - function tl() { - tmux list-sessions - } - - # Assign name to session. Uses current dir name by default. - # Usage: tns [NAME] - function tns() { - local name="''${1}" - - [[ "''${name}" = "" ]] && name=$(parse_alnum "''${PWD##*/}") - - tmux rename-session "''${name}" - } - - # Assign name to window. Uses current dir name by default. - # Usage: tnw [NAME] - function tnw() { - local name="''${1}" - - [[ "''${name}" = "" ]] && name=$(parse_alnum "''${PWD##*/}") - - tmux rename-window "''${name}" - } - - # Kill specified session. - # By default uses current dir name. - # Usage: tk [NAME] - function tk() { - local name="''${1}" - - [[ "''${name}" = "" ]] && name=$(parse_alnum "''${PWD##*/}") - - tmux kill-session -t "''${name}" - } - - # Kill all sessions. - function tka() { - local sessions=$(tmux list-sessions | sed -e 's/:.*//') - - for session in $sessions; do - tmux kill-session -t "$session" - done - } - - # Autocomplete with running sessions once. - function _complete_tmux_session() { - _autocomplete "$(tmux list-sessions 2> /dev/null | sed -e 's/:.*//')" - } - - # Autocomplete with running sessions. - function _complete_tmux_sessions() { - _autocomplete "$(tmux list-sessions 2> /dev/null | sed -e 's/:.*//')" - } - - # Autocomplete with current dir name and dirs inside this one. - function _complete_tmux_name() { - _autocomplete "''${PWD##*/}"$'\n'$(ls --classify | grep /$ | sed -e 's/\/$//') - } - - complete -F _complete_tmux_session ta - complete -F _complete_tmux_sessions tk - complete -o nosort -F _complete_tmux_name tns tnw - ''; -} diff --git a/home/program/bash/module/Tmux.sh b/home/program/bash/module/Tmux.sh new file mode 100644 index 00000000..c3210fbd --- /dev/null +++ b/home/program/bash/module/Tmux.sh @@ -0,0 +1,94 @@ +# Create/attach to named session. +# By default uses current dir name. +# Usage: ta [NAME] +function ta() { + local name="${1}" + + # Set default name. + # [[ "${name}" = "" ]] && name="main" + [[ ${name} == "" ]] && name=$(parse_alnum "${PWD##*/}") + + # Create session. + tmux new-session -s "${name}" -d &>/dev/null + + # Attach to session. + if _is_tmux; then + tmux switch-client -t "${name}" + else + tmux attach-session -t "${name}" + fi +} + +# Detach from running session. +function td() { + tmux detach-client +} + +# Detach all other tmux clients. +function tda() { + tmux detach-client -s $(tmux display-message -p '#S') +} + +# List running sessions. +function tl() { + tmux list-sessions +} + +# Assign name to session. Uses current dir name by default. +# Usage: tns [NAME] +function tns() { + local name="${1}" + + [[ ${name} == "" ]] && name=$(parse_alnum "${PWD##*/}") + + tmux rename-session "${name}" +} + +# Assign name to window. Uses current dir name by default. +# Usage: tnw [NAME] +function tnw() { + local name="${1}" + + [[ ${name} == "" ]] && name=$(parse_alnum "${PWD##*/}") + + tmux rename-window "${name}" +} + +# Kill specified session. +# By default uses current dir name. +# Usage: tk [NAME] +function tk() { + local name="${1}" + + [[ ${name} == "" ]] && name=$(parse_alnum "${PWD##*/}") + + tmux kill-session -t "${name}" +} + +# Kill all sessions. +function tka() { + local sessions=$(tmux list-sessions | sed -e 's/:.*//') + + for session in $sessions; do + tmux kill-session -t "$session" + done +} + +# Autocomplete with running sessions once. +function _complete_tmux_session() { + _autocomplete "$(tmux list-sessions 2>/dev/null | sed -e 's/:.*//')" +} + +# Autocomplete with running sessions. +function _complete_tmux_sessions() { + _autocomplete "$(tmux list-sessions 2>/dev/null | sed -e 's/:.*//')" +} + +# Autocomplete with current dir name and dirs inside this one. +function _complete_tmux_name() { + _autocomplete "${PWD##*/}"$'\n'$(ls --classify | grep /$ | sed -e 's/\/$//') +} + +complete -F _complete_tmux_session ta +complete -F _complete_tmux_sessions tk +complete -o nosort -F _complete_tmux_name tns tnw diff --git a/home/program/bash/module/Transcode.nix b/home/program/bash/module/Transcode.nix deleted file mode 100644 index 512e53e4..00000000 --- a/home/program/bash/module/Transcode.nix +++ /dev/null @@ -1,109 +0,0 @@ -{ ... }: -{ - text = '' - # Convert between different formats. - # By default tries to convert all files. - # Usage: transcode [FILES] - function transcode() { local IFS=$'\n' - local format=''${1} - local targets=(''${@:2}) - [[ "''${targets}" = "" ]] && targets=($(_ls_file)) - - # Report no format. - if [[ "''${format}" = "" ]] || [[ "''${format}" =~ "." ]]; then - _error "No format specified." - help transcode - return 2 - fi - - process() { - # Define context names and status. - local from="''${target##*.}" - local to="''${format}" - local output="''${target##*/}" - output="''${output%.*}.''${to}" - - # Skip if file exists. - [[ -f "''${output}" ]] && { _iterate_skip "Already exists."; return 0; } - - # Support multiple inputs. - [[ "''${to}" = "flac" ]] && from="" - [[ "''${to}" = "jxl" ]] && from="" - [[ "''${to}" = "mka" ]] && from="" - [[ "''${to}" = "mkv" ]] && from="" - [[ "''${to}" = "mp3" ]] && from="" - - # Send convert. - case "''${from}-''${to}" in - "gz-xz"|"tgz-txz") - _transcode_gz-xz "''${target}" "''${output}" - ;; - "xz-gz"|"txz-tgz") - _transcode_xz-gz "''${target}" "''${output}" - ;; - "-mp3") - _transcode_mp3 "''${target}" "''${output}" - ;; - "-flac") - _transcode_flac "''${target}" "''${output}" - ;; - "-mka") - _transcode_mka "''${target}" "''${output}" - ;; - "-mkv") - _transcode_mkv "''${target}" "''${output}" - ;; - "-jxl") - _transcode_jxl "''${target}" "''${output}" - ;; - *) - _error "Conversion ''${target##*.}-''${to} not supported." - return 1 - ;; - esac - } - - _iterate_targets process ''${targets[@]} - } - - function _transcode_gz-xz() { - [[ -f "''${2}" ]] && return 1 - pv "''${1}" | gzip -d | xz -9e > "''${2}" - } - - function _transcode_xz-gz() { - [[ -f "''${2}" ]] && return 1 - pv "''${1}" | xz -d | gzip -1 > "''${2}" - } - - function _transcode_mp3() { - ffmpeg -n -i "''${1}" -c:a libmp3lame -b:a 320k -f mp3 "''${2}" - } - - function _transcode_flac() { - ffmpeg -n -i "''${1}" -c:a flac -f flac "''${2}" - } - - function _transcode_mka() { - local braudio=$(_ffprobe_ba "''${1}") - [[ ''${braudio} -gt 128 ]] && braudio=128 - - ffmpeg -n -i "''${1}" -ac 2 -c:a libopus -b:a ''${braudio}k -vn "''${2}" - } - - function _transcode_mkv() { - local keyint=$(_ffprobe_keyint "''${1}") - local braudio=$(_ffprobe_ba "''${1}") - local fps=$(_ffprobe_fps "''${1}") - [[ ''${braudio} -gt 128 ]] && braudio=128 - [[ ''${fps} -gt 30 ]] && fps=30 - - # ffmpeg -n -i "''${1}" -c:a libopus -b:a ''${braudio}k -c:v libsvtav1 -crf 30 -svtav1-params "fast-decode=1:tune=0" -preset 8 -pix_fmt yuv420p10le -g ''${keyint} -vf "scale=-2:min'(1080,ih)'" "''${2}" - ffmpeg -n -i "''${1}" -map 0 -map -v -map V -map -t -dn -c:s srt -ac 2 -c:a libopus -b:a ''${braudio}k -c:v libsvtav1 -crf 30 -svtav1-params "tune=0" -pix_fmt yuv420p10le -g ''${keyint} -vf "scale=-2:min'(1080,ih)' , fps=''${fps}" "''${2}" - } - - function _transcode_jxl() { - cjxl -e 10 --lossless_jpeg=1 -- "''${1}" "''${2}" - } - ''; -} diff --git a/home/program/bash/module/Transcode.sh b/home/program/bash/module/Transcode.sh new file mode 100644 index 00000000..ea632167 --- /dev/null +++ b/home/program/bash/module/Transcode.sh @@ -0,0 +1,108 @@ +# Convert between different formats. +# By default tries to convert all files. +# Usage: transcode [FILES] +function transcode() { + local IFS=$'\n' + local format=${1} + local targets=(${@:2}) + [[ ${targets} == "" ]] && targets=($(_ls_file)) + + # Report no format. + if [[ ${format} == "" ]] || [[ ${format} =~ "." ]]; then + _error "No format specified." + help transcode + return 2 + fi + + process() { + # Define context names and status. + local from="${target##*.}" + local to="${format}" + local output="${target##*/}" + output="${output%.*}.${to}" + + # Skip if file exists. + [[ -f ${output} ]] && { + _iterate_skip "Already exists." + return 0 + } + + # Support multiple inputs. + [[ ${to} == "flac" ]] && from="" + [[ ${to} == "jxl" ]] && from="" + [[ ${to} == "mka" ]] && from="" + [[ ${to} == "mkv" ]] && from="" + [[ ${to} == "mp3" ]] && from="" + + # Send convert. + case "${from}-${to}" in + "gz-xz" | "tgz-txz") + _transcode_gz-xz "${target}" "${output}" + ;; + "xz-gz" | "txz-tgz") + _transcode_xz-gz "${target}" "${output}" + ;; + "-mp3") + _transcode_mp3 "${target}" "${output}" + ;; + "-flac") + _transcode_flac "${target}" "${output}" + ;; + "-mka") + _transcode_mka "${target}" "${output}" + ;; + "-mkv") + _transcode_mkv "${target}" "${output}" + ;; + "-jxl") + _transcode_jxl "${target}" "${output}" + ;; + *) + _error "Conversion ${target##*.}-${to} not supported." + return 1 + ;; + esac + } + + _iterate_targets process ${targets[@]} +} + +function _transcode_gz-xz() { + [[ -f ${2} ]] && return 1 + pv "${1}" | gzip -d | xz -9e >"${2}" +} + +function _transcode_xz-gz() { + [[ -f ${2} ]] && return 1 + pv "${1}" | xz -d | gzip -1 >"${2}" +} + +function _transcode_mp3() { + ffmpeg -n -i "${1}" -c:a libmp3lame -b:a 320k -f mp3 "${2}" +} + +function _transcode_flac() { + ffmpeg -n -i "${1}" -c:a flac -f flac "${2}" +} + +function _transcode_mka() { + local braudio=$(_ffprobe_ba "${1}") + [[ ${braudio} -gt 128 ]] && braudio=128 + + ffmpeg -n -i "${1}" -ac 2 -c:a libopus -b:a ${braudio}k -vn "${2}" +} + +function _transcode_mkv() { + local keyint=$(_ffprobe_keyint "${1}") + local braudio=$(_ffprobe_ba "${1}") + local fps=$(_ffprobe_fps "${1}") + [[ ${braudio} -gt 128 ]] && braudio=128 + [[ ${fps} -gt 30 ]] && fps=30 + + # ffmpeg -n -i "${1}" -c:a libopus -b:a ${braudio}k -c:v libsvtav1 -crf 30 -svtav1-params "fast-decode=1:tune=0" -preset 8 -pix_fmt yuv420p10le -g ${keyint} -vf "scale=-2:min'(1080,ih)'" "${2}" + ffmpeg -n -i "${1}" -map 0 -map -v -map V -map -t -dn -c:s srt -ac 2 -c:a libopus -b:a ${braudio}k -c:v libsvtav1 -crf 30 -svtav1-params "tune=0" -pix_fmt yuv420p10le -g ${keyint} -vf "scale=-2:min'(1080,ih)' , fps=${fps}" "${2}" +} + +function _transcode_jxl() { + cjxl -e 10 --lossless_jpeg=1 -- "${1}" "${2}" +} diff --git a/home/program/bash/module/Try.nix b/home/program/bash/module/Try.nix deleted file mode 100644 index d33ce474..00000000 --- a/home/program/bash/module/Try.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ ... }: -{ - text = '' - # Retry command every 2 sec until it completes successfully. - # Usage: try - function try() { - if [[ "''${*}" = "" ]]; then - help try - return 2 - fi - - local result=-1 - - while [ "$result" != 0 ]; do - ''${*} - result=$? - if [ "$result" != 0 ]; then - sleep 2 - fi - done - } - - # autocomplete. - complete -F _autocomplete_nested try - ''; -} diff --git a/home/program/bash/module/Try.sh b/home/program/bash/module/Try.sh new file mode 100644 index 00000000..3a7737ce --- /dev/null +++ b/home/program/bash/module/Try.sh @@ -0,0 +1,21 @@ +# Retry command every 2 sec until it completes successfully. +# Usage: try +function try() { + if [[ ${*} == "" ]]; then + help try + return 2 + fi + + local result=-1 + + while [ "$result" != 0 ]; do + ${*} + result=$? + if [ "$result" != 0 ]; then + sleep 2 + fi + done +} + +# autocomplete. +complete -F _autocomplete_nested try diff --git a/home/program/bash/module/Util.nix b/home/program/bash/module/Util.nix deleted file mode 100644 index 97688a3c..00000000 --- a/home/program/bash/module/Util.nix +++ /dev/null @@ -1,159 +0,0 @@ -{ ... }: -{ - text = '' - # Get the number of avaialble cores (threads). - function _core_count() { - cat /proc/cpuinfo | grep ^processor | wc -l - } - - # Get the number of available memory (in mebibytes). - function _mem_free() { - free -m | sed -n -e '2p' | awk '{print $7}' - } - - # Function-wrapper to iterate with specified function with provided files. - # By default Iterates on all non-hidden files and directories. - # List of variables available to FUNCTION: target - current file, count - current item index, total - sum of targets, failed - count of previously failed items, skipped - count of skipped files, status - status line (not recommended to use). - # Usage: _iterate_targets [FILES] - function _iterate_targets() { - local IFS=$'\n' - local foo="''${1}" - local targets=("''${@:2}") - local total=''${#targets[@]} - local count=0 - local failed=0 - local skipped=0 - local code=0 - - # set dafult value to target all supported archives. - if [[ "''${targets}" = "" ]]; then - _error "No targets provided." - return 1 - fi - - # iterate each target. - for target in "''${targets[@]}"; do - # increment counter. - ((count++)) - - # status info. - local status="[''${count}/''${total}] ''${target}" - _info "''${status}" - - # Call function. - ''${foo} "''${target}" - - # Show error. - if [[ ''${?} != 0 ]]; then - ((failed++)) - _error "''${status}: Failed." - fi - - # Add newline if not the last one. - [[ "''${count}" = "''${total}" ]] || _info - done - - # Show skipped. - if [[ ''${skipped} != 0 ]]; then - _warn - _warn "''${color_byellow}Skipped: ''${skipped}.''${color_default}" - fi - - # Show error. - if [[ ''${failed} != 0 ]]; then - [[ "''${skipped}" = 0 ]] && _error - _error "''${color_bred}Failed: ''${failed}.''${color_default}" - false - fi - } - - # Skip current iteration. - # Usage: _iterate_skip [MESSAGE] - function _iterate_skip() { - ((skipped++)) - - [[ "''${*}" != "" ]] && _warn "''${color_byellow}''${*}''${color_default}" - } - - # Report an error. - # Always returns code 1. - # Usage: _error - function _error() { - >&2 echo -e "''${color_bred}''${*}''${color_default}" - return 1 - } - - # Report a warning. - # Usage: _warn - function _warn() { - >&2 echo -e "''${color_byellow}''${*}''${color_default}" - } - - # Report a debug. - # Usage: _debug - function _debug() { - >&2 echo -e "''${color_bwhite}''${*}''${color_default}" - } - - # Report an info. - # Usage: _info - function _info() { - >&2 echo -e "''${color_bwhite}''${*}''${color_default}" - } - - # Check if array contains an element (strict). - # Usage: _contains - function _contains() { - local IFS=$'\n' - local target="''${1}" - local array="''${@:2}" - - if [[ "''${target}" = "" ]] || [[ "''${array}" = "" ]]; then - help _contains - return 2 - fi - - for item in ''${array[@]}; do - [[ "''${item}" = "''${target}" ]] && return 0 - done - - return 1 - } - - # Find an index of an element in array. - # Usage: _index_of - function _index_of() { - local element="''${1}" - local array="''${@:2}" - local index=0 - - for item in ''${array[@]}; do - [[ "''${item}" = "''${element}" ]] && break - ((index++)) - done - - echo "''${index}" - } - - # Check if inside Tmux. - function _is_tmux() { - [[ "''${TERM_PROGRAM}" = "tmux" ]] - } - - # Check if root. - function _is_root() { - [[ "''${UID}" = 0 ]] - } - - # Ring a bell. - function _bell() { - echo -e '\a' - } - - # Get users. - function _get_users() { - local users=("voronind" "dasha") - echo ''${users[@]} - } - ''; -} diff --git a/home/program/bash/module/Util.sh b/home/program/bash/module/Util.sh new file mode 100644 index 00000000..bc3ecd73 --- /dev/null +++ b/home/program/bash/module/Util.sh @@ -0,0 +1,154 @@ +# Get the number of avaialble cores (threads). +function _core_count() { + cat /proc/cpuinfo | grep ^processor | wc -l +} + +# Get the number of available memory (in mebibytes). +function _mem_free() { + free -m | sed -n -e '2p' | awk '{print $7}' +} + +# Function-wrapper to iterate with specified function with provided files. +# By default Iterates on all non-hidden files and directories. +# List of variables available to FUNCTION: target - current file, count - current item index, total - sum of targets, failed - count of previously failed items, skipped - count of skipped files, status - status line (not recommended to use). +# Usage: _iterate_targets [FILES] +function _iterate_targets() { + local IFS=$'\n' + local foo="${1}" + local targets=("${@:2}") + local total=${#targets[@]} + local count=0 + local failed=0 + local skipped=0 + local code=0 + + # set dafult value to target all supported archives. + if [[ ${targets} == "" ]]; then + _error "No targets provided." + return 1 + fi + + # iterate each target. + for target in "${targets[@]}"; do + # increment counter. + ((count++)) + + # status info. + local status="[${count}/${total}] ${target}" + _info "${status}" + + # Call function. + ${foo} "${target}" + + # Show error. + if [[ ${?} != 0 ]]; then + ((failed++)) + _error "${status}: Failed." + fi + + # Add newline if not the last one. + [[ ${count} == "${total}" ]] || _info + done + + # Show skipped. + if [[ ${skipped} != 0 ]]; then + _warn + _warn "${color_byellow}Skipped: ${skipped}.${color_default}" + fi + + # Show error. + if [[ ${failed} != 0 ]]; then + [[ ${skipped} == 0 ]] && _error + _error "${color_bred}Failed: ${failed}.${color_default}" + false + fi +} + +# Skip current iteration. +# Usage: _iterate_skip [MESSAGE] +function _iterate_skip() { + ((skipped++)) + + [[ ${*} != "" ]] && _warn "${color_byellow}${*}${color_default}" +} + +# Report an error. +# Always returns code 1. +# Usage: _error +function _error() { + >&2 echo -e "${color_bred}${*}${color_default}" + return 1 +} + +# Report a warning. +# Usage: _warn +function _warn() { + >&2 echo -e "${color_byellow}${*}${color_default}" +} + +# Report a debug. +# Usage: _debug +function _debug() { + >&2 echo -e "${color_bwhite}${*}${color_default}" +} + +# Report an info. +# Usage: _info +function _info() { + >&2 echo -e "${color_bwhite}${*}${color_default}" +} + +# Check if array contains an element (strict). +# Usage: _contains +function _contains() { + local IFS=$'\n' + local target="${1}" + local array="${@:2}" + + if [[ ${target} == "" ]] || [[ ${array} == "" ]]; then + help _contains + return 2 + fi + + for item in ${array[@]}; do + [[ ${item} == "${target}" ]] && return 0 + done + + return 1 +} + +# Find an index of an element in array. +# Usage: _index_of +function _index_of() { + local element="${1}" + local array="${@:2}" + local index=0 + + for item in ${array[@]}; do + [[ ${item} == "${element}" ]] && break + ((index++)) + done + + echo "${index}" +} + +# Check if inside Tmux. +function _is_tmux() { + [[ ${TERM_PROGRAM} == "tmux" ]] +} + +# Check if root. +function _is_root() { + [[ ${UID} == 0 ]] +} + +# Ring a bell. +function _bell() { + echo -e '\a' +} + +# Get users. +function _get_users() { + local users=("voronind" "dasha") + echo ${users[@]} +} diff --git a/home/program/bash/module/Vdl.nix b/home/program/bash/module/Vdl.nix deleted file mode 100644 index 87cdd8c2..00000000 --- a/home/program/bash/module/Vdl.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ ... }: -{ - text = '' - # Download video from URL. When no `[LINK]` specified, it tries to update previously downloaded link. - # Usage: vdl [LINK] - function vdl() { - # Check that ffmpeg and ffprobe are available. - if [[ "$(ffmpeg -version)" = "" ]] || [[ "$(ffprobe -version)" = "" ]]; then - _error "ffmpeg and ffprobe are required." - return 1 - fi - - local target="''${@}" # What to download/update. - - # If no [LINK] provided, try to read from `Src.txt`. - [[ "''${target}" = "" ]] && target="$(cat Src.txt)" - - # If could not get [LINK] eventually, show an error and exit. - if [[ "''${target}" = "" ]]; then - _error "Could not determine [LINK] to download." - help vdl - return 2 - fi - - # Save [LINK] for later use. - [[ -f "Src.txt" ]] || echo "''${target}" > Src.txt - - # Download [LINK] content. - yt-dlp -4 -S 'res:1080,codec:av1,codec:vp9,codec:h264' --download-archive Index.txt --embed-thumbnail --embed-subs --write-auto-subs --embed-metadata --merge-output-format mkv -cio '%(playlist_index)000006d_%(id)s.%(ext)s' ''${target} # || _vdl_retry - } - - # Temporary fix for vk downloads. - # Usage: vdl_vk - function vdl_vk() { - vdl --format mp4 "''${@}" - } - - # Download all videos from file with links. - # Usage: vdl_file - function vdl_file() { - vdl -a "''${@}" - } - ''; -} diff --git a/home/program/bash/module/Vdl.sh b/home/program/bash/module/Vdl.sh new file mode 100644 index 00000000..64322d49 --- /dev/null +++ b/home/program/bash/module/Vdl.sh @@ -0,0 +1,39 @@ +# Download video from URL. When no `[LINK]` specified, it tries to update previously downloaded link. +# Usage: vdl [LINK] +function vdl() { + # Check that ffmpeg and ffprobe are available. + if [[ "$(ffmpeg -version)" == "" ]] || [[ "$(ffprobe -version)" == "" ]]; then + _error "ffmpeg and ffprobe are required." + return 1 + fi + + local target="${@}" # What to download/update. + + # If no [LINK] provided, try to read from `Src.txt`. + [[ ${target} == "" ]] && target="$(cat Src.txt)" + + # If could not get [LINK] eventually, show an error and exit. + if [[ ${target} == "" ]]; then + _error "Could not determine [LINK] to download." + help vdl + return 2 + fi + + # Save [LINK] for later use. + [[ -f "Src.txt" ]] || echo "${target}" >Src.txt + + # Download [LINK] content. + yt-dlp -4 -S 'res:1080,codec:av1,codec:vp9,codec:h264' --download-archive Index.txt --embed-thumbnail --embed-subs --write-auto-subs --embed-metadata --merge-output-format mkv -cio '%(playlist_index)000006d_%(id)s.%(ext)s' ${target} # || _vdl_retry +} + +# Temporary fix for vk downloads. +# Usage: vdl_vk +function vdl_vk() { + vdl --format mp4 "${@}" +} + +# Download all videos from file with links. +# Usage: vdl_file +function vdl_file() { + vdl -a "${@}" +} diff --git a/home/program/bash/module/Vi.nix b/home/program/bash/module/Vi.nix deleted file mode 100644 index 960b7eae..00000000 --- a/home/program/bash/module/Vi.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ ... }: -{ - text = '' - # Neovim shortcut. - function v() { - nvim -- "''${@}" - } - ''; -} diff --git a/home/program/bash/module/Vi.sh b/home/program/bash/module/Vi.sh new file mode 100644 index 00000000..a7e7d849 --- /dev/null +++ b/home/program/bash/module/Vi.sh @@ -0,0 +1,4 @@ +# Neovim shortcut. +function v() { + nvim -- "${@}" +} diff --git a/home/program/bash/module/Watch.nix b/home/program/bash/module/Watch.nix deleted file mode 100644 index 6a951a00..00000000 --- a/home/program/bash/module/Watch.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ ... }: -{ - text = '' - # Watch command output with 2 seconds interval. - # Usage: w - function w() { - if [[ "''${*}" = "" ]]; then - help w - return 2 - fi - - watch -n 2 "''${@}" - } - - # Watch command output with minimal interval. - # Usage: ww - function ww() { - if [[ "''${*}" = "" ]]; then - help ww - return 2 - fi - - watch -n 0 "''${@}" - } - - # Autocomplete. - complete -F _autocomplete_nested w ww - ''; -} diff --git a/home/program/bash/module/Watch.sh b/home/program/bash/module/Watch.sh new file mode 100644 index 00000000..01236c8d --- /dev/null +++ b/home/program/bash/module/Watch.sh @@ -0,0 +1,24 @@ +# Watch command output with 2 seconds interval. +# Usage: w +function w() { + if [[ ${*} == "" ]]; then + help w + return 2 + fi + + watch -n 2 "${@}" +} + +# Watch command output with minimal interval. +# Usage: ww +function ww() { + if [[ ${*} == "" ]]; then + help ww + return 2 + fi + + watch -n 0 "${@}" +} + +# Autocomplete. +complete -F _autocomplete_nested w ww diff --git a/home/program/bash/module/Zapret.nix b/home/program/bash/module/Zapret.nix deleted file mode 100644 index 5b3fd733..00000000 --- a/home/program/bash/module/Zapret.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ ... }: -{ - text = '' - # FRKN. - # SOURCE: https://github.com/bol-van/zapret - function zapret() { - iptables -t mangle -I POSTROUTING -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 201 --queue-bypass - nfqws --pidfile=/run/nfqws.pid --qnum=201 ''${@} - } - ''; -} diff --git a/home/program/bash/module/Zapret.sh b/home/program/bash/module/Zapret.sh new file mode 100644 index 00000000..e446eade --- /dev/null +++ b/home/program/bash/module/Zapret.sh @@ -0,0 +1,6 @@ +# FRKN. +# SOURCE: https://github.com/bol-van/zapret +function zapret() { + iptables -t mangle -I POSTROUTING -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 201 --queue-bypass + nfqws --pidfile=/run/nfqws.pid --qnum=201 ${@} +} diff --git a/lib/Util.nix b/lib/Util.nix index 58517c7e..1275b372 100644 --- a/lib/Util.nix +++ b/lib/Util.nix @@ -19,12 +19,16 @@ # Concat all file paths by `file` key. catFile = - files: args: builtins.foldl' (acc: mod: acc + (builtins.readFile (import mod args).file)) "" files; + files: args: + builtins.foldl' (acc: mod: acc + (builtins.readFile (import mod args).file) + "\n") "" files; # Concat all files as a set. catSet = files: args: builtins.foldl' (acc: mod: acc // mod) { } (map (file: import file args) files); + # Concat all file contents. + catContent = files: builtins.foldl' (acc: mod: acc + (builtins.readFile mod) + "\n") "" files; + # Systemd service that does not restart with system switch. mkStaticSystemdService = params: