diff --git a/bin/ffconv b/bin/ffconv index fce48ac..fe1429d 100755 --- a/bin/ffconv +++ b/bin/ffconv @@ -12,8 +12,7 @@ sfexamples+=("ffconv mp3 opus;Convert all mp3 files to opus") sfexamples+=("ffconv -m trash mp4 mkv;Convert all mp4 to mkv and move the original ones to './trash'") sfexamples+=("ffconv -d ~/music -l wma mp3;List all wma files from '~/music' and ask for converting them to mp3") -# sf -- script framework (https://github.com/Deleh/sf) -sftrs=$'\e[0m';sftbf=$'\e[1m';sftdim=$'\e[2m';sftul=$'\e[4m';sftblink=$'\e[5m';sftinv=$'\e[7m';sfthide=$'\e[8m';sftclr=$'\e[1A\e[K';sftk=$'\e[30m';sftr=$'\e[31m';sftg=$'\e[32m';sfty=$'\e[33m';sftb=$'\e[34m';sftm=$'\e[35m';sftc=$'\e[36m';sftw=$'\e[97m';function sferr { echo "${sftbf}${sftr}ERROR${sftrs} $1";[ -z "$2" ]&&exit 1;};function sfwarn { echo "${sftbf}${sfty}WARNING${sftrs} $1";};function sfask { if [ "$2" == "" ];then read -p "$1? [${sftbf}Y${sftrs}/${sftbf}n${sftrs}] " sfin;[[ "$sfin" =~ y|Y|^$ ]]&&sfin=true||sfin=false;else read -p "$1? [${sftbf}y${sftrs}/${sftbf}N${sftrs}] " sfin;[[ "$sfin" =~ n|N|^$ ]]&&sfin=false||sfin=true;fi;};function sfget { [ "$2" != "" ]&&read -p "$1 [${sftbf}$2${sftrs}]: " sfin||read -p "$1: " sfin;[ "$sfin" == "" ]&&[ "$2" != "" ]&&sfin="$2";};function _sferr { echo "${sftbf}${sftr}SF PARSE ERROR${sftrs} $1";exit 1;};OLDIFS=$IFS;IFS=";";_sfphead="";_sfpdesc="";_sfodesc="";_sfexamples="";_sfpargs=();declare -A _sfflags;declare -A _sfargs;for a in "${sfargs[@]}";do _sfsubst=${a//";"};_sfcount="$(((${#a} - ${#_sfsubst})))";if [ $_sfcount -eq 1 ];then read -r -a _sfparsearr<<<"${a}";_sfpargs+=("${_sfparsearr[0]}");_sfphead="$_sfphead ${_sfparsearr[0]}";_sfpdesc="$_sfpdesc ${_sfparsearr[0]};${_sfparsearr[1]}\n";elif [ $_sfcount -eq 2 ];then read -r -a _sfparsearr<<<"${a}";_sfflags["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";_sfflags["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";declare ${_sfparsearr[0]}=false;_sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]};${_sfparsearr[2]}\n";elif [ $_sfcount -eq 4 ];then read -r -a _sfparsearr<<<"${a}";_sfargs["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";_sfargs["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";declare ${_sfparsearr[0]}="${_sfparsearr[3]}";_sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]} ${_sfparsearr[2]};${_sfparsearr[4]} (default: ${_sfparsearr[3]})\n";else _sferr "Wrong argument declaration: $a";fi;done;for e in "${sfexamples[@]}";do _sfsubst=${e//";"};_sfcount="$(((${#e} - ${#_sfsubst})))";if [ $_sfcount -eq 1 ];then read -r -a _sfparsearr<<<"${e}";_sfexamples="$_sfexamples ${_sfparsearr[0]};${_sfparsearr[1]}\n";else _sferr "Wrong example declaration: $e";fi;done;IFS=$OLDIFS;function _sfusage { echo -n "Usage: $(basename $0)";[ "$_sfodesc" != "" ]&&echo -n " [OPTIONS]";echo -e "$_sfphead";[ ! -z ${sfdesc+x} ]&&echo -e "\n$sfdesc";if [ "$_sfpdesc" != "" ];then echo -e "\nPOSITIONAL ARGUMENTS";echo -e "$_sfpdesc"|column -c 80 -s ";" -t -W 2;fi;if [ "$_sfodesc" != "" ];then echo -e "\nOPTIONS";echo -e "$_sfodesc"|column -c 80 -s ";" -t -W 2;fi;if [ "$_sfexamples" != "" ];then echo -e "\nEXAMPLES";echo -e "$_sfexamples"|column -c 80 -s ";" -t -W 2;fi;if [ ! -z ${sfextra+x} ];then echo -e "\n$sfextra";fi;exit 0;};for a in "$@";do [ "$a" == "-h" ]||[ "$a" == "--help" ]&&_sfusage;done;while(("$#"));do if [ ! -z ${_sfflags["$1"]} ];then declare ${_sfflags["$1"]}=true;elif [ ! -z ${_sfargs["$1"]} ];then if [ -n "$2" ]&&[ "${2:0:1}" != "-" ];then declare ${_sfargs["$1"]}="$2";shift;else sferr "Argument for '$1' missing";fi;else if [ "${1:0:1}" == "-" ];then sferr "Unsupported argument: $1";else if [ "${#_sfpargs[@]}" != 0 ];then declare ${_sfpargs[0]}="$1";_sfpargs=("${_sfpargs[@]:1}");else sferr "Too many positional arguments";fi;fi;fi;shift;done;if [ ${#_sfpargs[@]} != 0 ];then for p in "${_sfpargs[@]}";do sferr "Positional argument '$p' missing" 0;done;exit 1;fi;unset a e _sfphead _sfpdesc _sfodesc _sfexamples _sfpargs _sfflags _sfargs _sferr _sfusage +source "$(dirname $0)/../lib/sf" # Handle default directory [ "$directory" == "current work directory" ] && directory="." diff --git a/bin/ffcut b/bin/ffcut index 11bb4f2..6eb4649 100755 --- a/bin/ffcut +++ b/bin/ffcut @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# sf variables +# sf sfdesc="Extract a part of a file." sfargs+=("FILE;File which will be cutted") sfargs+=("from;f;TIMESTAMP/SECONDS;0;Extract from TIMESTAMP/SECONDS") @@ -9,8 +9,7 @@ sfargs+=("to;t;TIMESTAMP/DURATION;end;Extract to TIMESTAMP/DURATION") sfexamples+=("ffcut -t 5 video.mp4 -o cut.webm;Extract the first five seconds of 'video.mp4' to 'cut.webm'") sfexamples+=("ffcut -f 00:10:30 -t 00:14:15 video.mp4;Extract the part from 00:10:30 to 00:14:15 from 'video.mp4'") -# sf -- script framework (https://github.com/Deleh/sf) -sftrs=$'\e[0m';sftbf=$'\e[1m';sftdim=$'\e[2m';sftul=$'\e[4m';sftblink=$'\e[5m';sftinv=$'\e[7m';sfthide=$'\e[8m';sftclr=$'\e[1A\e[K';sftk=$'\e[30m';sftr=$'\e[31m';sftg=$'\e[32m';sfty=$'\e[33m';sftb=$'\e[34m';sftm=$'\e[35m';sftc=$'\e[36m';sftw=$'\e[97m';function sferr { echo "${sftbf}${sftr}ERROR${sftrs} $1";[ -z "$2" ]&&exit 1;};function sfwarn { echo "${sftbf}${sfty}WARNING${sftrs} $1";};function sfask { if [ "$2" == "" ];then read -p "$1? [${sftbf}Y${sftrs}/${sftbf}n${sftrs}] " sfin;[[ "$sfin" =~ y|Y|^$ ]]&&sfin=true||sfin=false;else read -p "$1? [${sftbf}y${sftrs}/${sftbf}N${sftrs}] " sfin;[[ "$sfin" =~ n|N|^$ ]]&&sfin=false||sfin=true;fi;};function sfget { [ "$2" != "" ]&&read -p "$1 [${sftbf}$2${sftrs}]: " sfin||read -p "$1: " sfin;[ "$sfin" == "" ]&&[ "$2" != "" ]&&sfin="$2";};function _sferr { echo "${sftbf}${sftr}SF PARSE ERROR${sftrs} $1";exit 1;};OLDIFS=$IFS;IFS=";";_sfphead="";_sfpdesc="";_sfodesc="";_sfexamples="";_sfpargs=();declare -A _sfflags;declare -A _sfargs;for a in "${sfargs[@]}";do _sfsubst=${a//";"};_sfcount="$(((${#a} - ${#_sfsubst})))";if [ $_sfcount -eq 1 ];then read -r -a _sfparsearr<<<"${a}";_sfpargs+=("${_sfparsearr[0]}");_sfphead="$_sfphead ${_sfparsearr[0]}";_sfpdesc="$_sfpdesc ${_sfparsearr[0]};${_sfparsearr[1]}\n";elif [ $_sfcount -eq 2 ];then read -r -a _sfparsearr<<<"${a}";_sfflags["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";_sfflags["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";declare ${_sfparsearr[0]}=false;_sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]};${_sfparsearr[2]}\n";elif [ $_sfcount -eq 4 ];then read -r -a _sfparsearr<<<"${a}";_sfargs["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";_sfargs["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";declare ${_sfparsearr[0]}="${_sfparsearr[3]}";_sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]} ${_sfparsearr[2]};${_sfparsearr[4]} (default: ${_sfparsearr[3]})\n";else _sferr "Wrong argument declaration: $a";fi;done;for e in "${sfexamples[@]}";do _sfsubst=${e//";"};_sfcount="$(((${#e} - ${#_sfsubst})))";if [ $_sfcount -eq 1 ];then read -r -a _sfparsearr<<<"${e}";_sfexamples="$_sfexamples ${_sfparsearr[0]};${_sfparsearr[1]}\n";else _sferr "Wrong example declaration: $e";fi;done;IFS=$OLDIFS;function _sfusage { echo -n "Usage: $(basename $0)";[ "$_sfodesc" != "" ]&&echo -n " [OPTIONS]";echo -e "$_sfphead";[ ! -z ${sfdesc+x} ]&&echo -e "\n$sfdesc";if [ "$_sfpdesc" != "" ];then echo -e "\nPOSITIONAL ARGUMENTS";echo -e "$_sfpdesc"|column -c 80 -s ";" -t -W 2;fi;if [ "$_sfodesc" != "" ];then echo -e "\nOPTIONS";echo -e "$_sfodesc"|column -c 80 -s ";" -t -W 2;fi;if [ "$_sfexamples" != "" ];then echo -e "\nEXAMPLES";echo -e "$_sfexamples"|column -c 80 -s ";" -t -W 2;fi;if [ ! -z ${sfextra+x} ];then echo -e "\n$sfextra";fi;exit 0;};for a in "$@";do [ "$a" == "-h" ]||[ "$a" == "--help" ]&&_sfusage;done;while(("$#"));do if [ ! -z ${_sfflags["$1"]} ];then declare ${_sfflags["$1"]}=true;elif [ ! -z ${_sfargs["$1"]} ];then if [ -n "$2" ]&&[ "${2:0:1}" != "-" ];then declare ${_sfargs["$1"]}="$2";shift;else sferr "Argument for '$1' missing";fi;else if [ "${1:0:1}" == "-" ];then sferr "Unsupported argument: $1";else if [ "${#_sfpargs[@]}" != 0 ];then declare ${_sfpargs[0]}="$1";_sfpargs=("${_sfpargs[@]:1}");else sferr "Too many positional arguments";fi;fi;fi;shift;done;if [ ${#_sfpargs[@]} != 0 ];then for p in "${_sfpargs[@]}";do sferr "Positional argument '$p' missing" 0;done;exit 1;fi;unset a e _sfphead _sfpdesc _sfodesc _sfexamples _sfpargs _sfflags _sfargs _sferr _sfusage +source "$(dirname $0)/../lib/sf" # Handle missing parameters [ "$from" == 0 ] && [ "$to" == "end" ] && sferr "Set at least '--from' or '--to'" diff --git a/flake.nix b/flake.nix index 5fac73c..ebf0f49 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,7 @@ ''; installPhase = '' install -m 755 -D bin/ffconv $out/bin/ffconv + install -m 644 -D lib/sf $out/lib/sf ''; }; ffcut = pkgs.stdenv.mkDerivation { @@ -30,6 +31,7 @@ ''; installPhase = '' install -m 755 -D bin/ffcut $out/bin/ffcut + install -m 644 -D lib/sf $out/lib/sf ''; }; }; diff --git a/lib/sf b/lib/sf new file mode 100644 index 0000000..b3beb93 --- /dev/null +++ b/lib/sf @@ -0,0 +1,217 @@ +#!/usr/bin/env bash + +# sf -- script framework (https://github.com/Deleh/sf) + +# Text formatting variables +sftrs=$'\e[0m' +sftbf=$'\e[1m' +sftdim=$'\e[2m' +sftul=$'\e[4m' +sftblk=$'\e[5m' +sftinv=$'\e[7m' +sfthd=$'\e[8m' +sftclr=$'\e[1A\e[K' +sftk=$'\e[30m' +sftr=$'\e[31m' +sftg=$'\e[32m' +sfty=$'\e[33m' +sftb=$'\e[34m' +sftm=$'\e[35m' +sftc=$'\e[36m' +sftw=$'\e[97m' + +# Public output functions +function sferr { + echo "${sftbf}${sftr}ERROR${sftrs} $1" + [ -z "$2" ] && exit 1 +} + +function sfwarn { + echo "${sftbf}${sfty}WARNING${sftrs} $1" +} + +# Public input functions +function sfask { + if [ "$2" == "" ]; then + read -p "$1? [${sftbf}Y${sftrs}/${sftbf}n${sftrs}] " sfin + [[ "$sfin" =~ y|Y|^$ ]] && sfin=true || sfin=false + else + read -p "$1? [${sftbf}y${sftrs}/${sftbf}N${sftrs}] " sfin + [[ "$sfin" =~ n|N|^$ ]] && sfin=false || sfin=true + fi +} + +function sfget { + [ "$2" != "" ] && read -p "$1 [${sftbf}$2${sftrs}]: " sfin || read -p "$1: " sfin + [ "$sfin" == "" ] && [ "$2" != "" ] && sfin="$2" +} + +# Internal error function +function _sferr { + echo "${sftbf}${sftr}SF PARSE ERROR${sftrs} $1" + exit 1 +} + +# Declare variables for parsing +OLDIFS=$IFS +IFS=";" +_sfphead="" +_sfpdesc="" +_sfodesc="" +_sfexamples="" +_sfpargs=() +declare -A _sfflags +declare -A _sfargs + +# Parse sf arguments +for a in "${sfargs[@]}"; do + + # Get amount of ; + _sfsubst=${a//";"} + _sfcount="$(((${#a} - ${#_sfsubst})))" + + if [ $_sfcount -eq 1 ]; then + + # Read positional argument declaration + read -r -a _sfparsearr <<< "${a}" + + # Add to positional argument arry + _sfpargs+=("${_sfparsearr[0]}") + + # Set usage header and description + _sfphead="$_sfphead ${_sfparsearr[0]}" + _sfpdesc="$_sfpdesc ${_sfparsearr[0]};${_sfparsearr[1]}\n" + + # Flags + elif [ $_sfcount -eq 2 ]; then + + # Read flag declaration + read -r -a _sfparsearr <<< "${a}" + + # Set mappings + _sfflags["-${_sfparsearr[1]}"]="${_sfparsearr[0]}" + _sfflags["--${_sfparsearr[0]}"]="${_sfparsearr[0]}" + + # Set default value + declare ${_sfparsearr[0]}=false + + # Set description + _sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]};${_sfparsearr[2]}\n" + + # Arguments + elif [ $_sfcount -eq 4 ]; then + + # Read argument declaration + read -r -a _sfparsearr <<< "${a}" + + # Set mappings + _sfargs["-${_sfparsearr[1]}"]="${_sfparsearr[0]}" + _sfargs["--${_sfparsearr[0]}"]="${_sfparsearr[0]}" + + # Set default value + declare ${_sfparsearr[0]}="${_sfparsearr[3]}" + + # Set description + _sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]} ${_sfparsearr[2]};${_sfparsearr[4]} (default: ${_sfparsearr[3]})\n" + + else + _sferr "Wrong argument declaration: $a" + fi +done + +# Parse examples +for e in "${sfexamples[@]}"; do + + # Get amount of ; + _sfsubst=${e//";"} + _sfcount="$(((${#e} - ${#_sfsubst})))" + + if [ $_sfcount -eq 1 ]; then + + # Read example + read -r -a _sfparsearr <<< "${e}" + + _sfexamples="$_sfexamples ${_sfparsearr[0]};${_sfparsearr[1]}\n" + else + _sferr "Wrong example declaration: $e" + fi +done + +IFS=$OLDIFS + +# Usage function +function _sfusage { + echo -n "Usage: $(basename $0)" + [ "$_sfodesc" != "" ] && echo -n " [OPTIONS]" + echo -e "$_sfphead" + [ ! -z ${sfdesc+x} ] && echo -e "\n$sfdesc" + if [ "$_sfpdesc" != "" ]; then + echo -e "\nPOSITIONAL ARGUMENTS" + echo -e "$_sfpdesc" | column -c 80 -s ";" -t -W 2 + fi + if [ "$_sfodesc" != "" ]; then + echo -e "\nOPTIONS" + echo -e "$_sfodesc" | column -c 80 -s ";" -t -W 2 + fi + if [ "$_sfexamples" != "" ]; then + echo -e "\nEXAMPLES" + echo -e "$_sfexamples" | column -c 80 -s ";" -t -W 2 + fi + if [ ! -z ${sfextra+x} ]; then + echo -e "\n$sfextra" + fi + exit 0 +} + +# Check for help flag +for a in "$@"; do + # Check if help flag ist set + [ "$a" == "-h" ] || [ "$a" == "--help" ] && _sfusage +done + +# Parse arguments +while (( "$#" )); do + + # Check if flag + if [ ! -z ${_sfflags["$1"]} ]; then + declare ${_sfflags["$1"]}=true + + # Check if argument + elif [ ! -z ${_sfargs["$1"]} ]; then + + # Check if argument has value + if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then + declare ${_sfargs["$1"]}="$2" + shift + else + sferr "Argument for '$1' missing" + fi + + # Handle positional arguments and wrong arguments/flags + else + # Check if arg starts with - + if [ "${1:0:1}" == "-" ]; then + sferr "Unsupported argument: $1" + else + # Set positional argument + if [ "${#_sfpargs[@]}" != 0 ]; then + declare ${_sfpargs[0]}="$1" + _sfpargs=("${_sfpargs[@]:1}") + else + sferr "Too many positional arguments" + fi + fi + fi + shift +done + +# Check if positional arguments left +if [ ${#_sfpargs[@]} != 0 ]; then + for p in "${_sfpargs[@]}"; do + sferr "Positional argument '$p' missing" 0 + done + exit 1 +fi + +# Unset all internal variables and functions +unset a e _sfphead _sfpdesc _sfodesc _sfexamples _sfpargs _sfflags _sfargs _sferr _sfusage