diff --git a/go-core.bash b/go-core.bash index 61cf511..ae66fdc 100755 --- a/go-core.bash +++ b/go-core.bash @@ -26,11 +26,14 @@ # https://mike-bland.com/ # https://github.com/mbland +_GO_EC_GENERR="${_GO_EC_GENERR:-70}" +_GO_EC_DEPMISS="${_GO_EC_DEPMISS:-72}" + if [[ "${BASH_VERSINFO[0]}" -lt '3' || ( "${BASH_VERSINFO[0]}" -eq '3' && "${BASH_VERSINFO[1]}" -lt '2' ) ]]; then printf "This module requires bash version 3.2 or greater:\n %s %s\n" \ "$BASH" "$BASH_VERSION" - exit 1 + exit "$_GO_EXIT_DEPMISS" fi # The version of the framework @@ -46,7 +49,7 @@ declare -r -x _GO_CORE_VERSION='v1.7.0' declare -r -x _GO_CORE_URL='https://github.com/mbland/go-script-bash' declare __go_orig_dir="$PWD" -cd "${0%/*}" || exit 1 +cd "${0%/*}" || exit "$_GO_EC_GENERR" # Path to the project's root directory # @@ -56,21 +59,23 @@ cd "${0%/*}" || exit 1 declare -x _GO_ROOTDIR="$PWD" if [[ "${BASH_SOURCE[0]:0:1}" != '/' ]]; then - cd "$__go_orig_dir/${BASH_SOURCE[0]%/*}" || exit 1 + cd "$__go_orig_dir/${BASH_SOURCE[0]%/*}" || exit "$_GO_EC_GENERR" else - cd "${BASH_SOURCE[0]%/*}" || exit 1 + cd "${BASH_SOURCE[0]%/*}" || exit "$_GO_EC_GENERR" fi # Path to the ./go script framework's directory declare -r -x _GO_CORE_DIR="$PWD" +. "$_GO_CORE_DIR/lib/internal/sysexits" + # Set _GO_STANDALONE if your script is a standalone program. # # See the "Standalone mode" section of README.md for more information. if [[ -z "$_GO_STANDALONE" ]]; then - cd "$_GO_ROOTDIR" || exit 1 + cd "$_GO_ROOTDIR" || exit "$_GO_EC_GENERR" else - cd "$__go_orig_dir" || exit 1 + cd "$__go_orig_dir" || exit "$_GO_EC_GENERR" fi unset __go_orig_dir @@ -193,6 +198,8 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" done printf '%s' "$line" done <<<"${result//$'\n'/$'\n\x1f'}"$'\x1f' + + return "$_GO_EC_OK" } # Prints the stack trace at the point of the call. @@ -205,17 +212,17 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" # skip_callers: The number of callers to skip over when printing the stack @go.print_stack_trace() { local skip_callers="$1" - local result=0 + local result="$_GO_EC_OK" local i if [[ -n "$skip_callers" && ! "$skip_callers" =~ ^[1-9][0-9]*$ ]]; then printf '%s argument %s not a positive integer; printing full stack\n' \ "$FUNCNAME" "'$skip_callers'" >&2 - result=1 + result="$_GO_EC_GENERR" elif [[ "$skip_callers" -ge "${#FUNCNAME[@]}" ]]; then printf '%s argument %d exceeds stack size %d; printing full stack\n' \ "$FUNCNAME" "$skip_callers" "$((${#FUNCNAME[@]} - 1))" >&2 - result=1 + result="$_GO_EC_GENERR" fi if [[ "$result" -ne '0' ]]; then @@ -263,15 +270,15 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" # search_func: Helper function implementing the search operation # # Returns: -# Zero if `search_func` ever returns zero, nonzero otherwise +# _GO_EC_OK if `search_func` ever returns zero, _GO_EC_GENERR otherwise @go.search_plugins() { local __gsp_plugins_dir="$_GO_SCRIPTS_DIR/plugins" while true; do if "$1" "$__gsp_plugins_dir"; then - return + return "$_GO_EC_OK" elif [[ "$__gsp_plugins_dir" == "$_GO_PLUGINS_DIR" ]]; then - return 1 + return "$_GO_EC_GENERR" fi __gsp_plugins_dir="${__gsp_plugins_dir%/plugins/*}/plugins" done @@ -289,7 +296,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" case "$cmd" in '') _@go.source_builtin 'help' 1>&2 - return 1 + return "$_GO_EC_USAGE" ;; -h|-help|--help) cmd='help' @@ -297,12 +304,12 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" -*) @go.printf "Unknown flag: $cmd\n\n" _@go.source_builtin 'help' 1>&2 - return 1 + return "$_GO_EC_USAGE" ;; edit) if [[ -z "$EDITOR" ]]; then echo "Cannot edit $@: \$EDITOR not defined." - return 1 + return "$_GO_EC_CONFIG" fi "$EDITOR" "$@" return @@ -314,7 +321,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" cd|pushd|unenv) @go.printf "$cmd is only available after using \"$_GO_CMD env\" %s\n" \ "to set up your shell environment." >&2 - return 1 + return "$_GO_EC_USAGE" ;; esac @@ -329,7 +336,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" local __go_argv if ! _@go.set_command_path_and_argv "$cmd" "$@"; then - return 1 + return "$_GO_EC_GENERR" fi if [[ "${__go_cmd_path#$_GO_SCRIPTS_DIR}" =~ /plugins/[^/]+/bin/ ]]; then @@ -366,7 +373,7 @@ _@go.run_command_script() { @go.printf \ "The first line of %s does not contain #!/path/to/interpreter.\n" \ "$cmd_path" >&2 - return 1 + return "$_GO_EC_BADFRMT" fi interpreter="${interpreter%$'\r'}" @@ -383,7 +390,7 @@ _@go.run_command_script() { . "$cmd_path" "$@" elif [[ -z "$interpreter" ]]; then @go.printf "Could not parse interpreter from first line of $cmd_path.\n" >&2 - return 1 + return "$_GO_EC_BADFRMT" else if [[ -z "$_GO_CMD_NAME" ]]; then local origIFS="$IFS" @@ -401,23 +408,26 @@ _@go.set_scripts_dir() { if [[ "$#" -ne '1' ]]; then echo "ERROR: there should be exactly one command script dir specified" >&2 - return 1 + return "$_GO_EC_CONFIG" elif [[ ! -e "$scripts_dir" ]]; then echo "ERROR: command script directory $scripts_dir does not exist" >&2 - return 1 + return "$_GO_EC_CONFIG" elif [[ ! -d "$scripts_dir" ]]; then echo "ERROR: $scripts_dir is not a directory" >&2 - return 1 + return "$_GO_EC_CONFIG" elif [[ ! -r "$scripts_dir" || ! -x "$scripts_dir" ]]; then echo "ERROR: you do not have permission to access the $scripts_dir" \ "directory" >&2 - return 1 + return "$_GO_EC_NOPERM" fi _GO_SCRIPTS_DIR="$scripts_dir" + return "$_GO_EC_OK" } -if ! _@go.set_scripts_dir "$@"; then - exit 1 +_@go.set_scripts_dir "$@" && ec=0 || ec="$?" + +if [[ "$ec" -ne "$_GO_EC_OK" ]]; then + exit "$ec" elif [[ -z "$COLUMNS" ]]; then # On Travis, $TERM is set to 'dumb', but `tput cols` still fails. if command -v tput >/dev/null && tput cols >/dev/null 2>&1; then diff --git a/go-template b/go-template index fe51f57..2e54f3c 100755 --- a/go-template +++ b/go-template @@ -57,7 +57,7 @@ download_go_script_bash_tarball() { if [[ "$protocol" == "$url" ]]; then printf 'GO_SCRIPT_BASH_DOWNLOAD_URL has no protocol: %s\n' "$url" >&2 - return 1 + return "$_GO_EC_CONFIG" elif [[ "$(git --version)" =~ windows && "$protocol" == 'file' ]]; then url="file://$(cygpath -m "${url#file://}")" fi @@ -74,28 +74,28 @@ download_go_script_bash_tarball() { download_cmd=(wget -O - "$url") else printf "Failed to find cURL, wget, or fetch\n" >&2 - return 1 + return "$_GO_EC_DEPMISS" fi if ! command -v tar >/dev/null; then printf "Failed to find tar\n" >&2 - return 1 + return "$_GO_EC_DEPMISS" fi printf "Downloading framework from '%s'...\n" "$url" if ! "${download_cmd[@]}" | tar -xzf - || [[ "${PIPESTATUS[0]}" -ne '0' ]]; then printf "Failed to download from '%s'.\n" "$url" >&2 - return 1 + return "$_GO_EC_NOTFND" elif [[ ! -d "$core_dir_parent" ]] && ! mkdir -p "$core_dir_parent" ; then printf "Failed to create scripts dir '%s'\n" "$core_dir_parent" >&2 rm -rf "$unpacked_dir" - return 1 + return "$_GO_EC_CANTCREAT" elif ! mv "$unpacked_dir" "$GO_SCRIPT_BASH_CORE_DIR"; then printf "Failed to install downloaded directory in '%s'\n" \ "$GO_SCRIPT_BASH_CORE_DIR" >&2 rm -rf "$unpacked_dir" - return 1 + return "$_GO_EC_CANTCREAT" fi printf "Download of '%s' successful.\n\n" "$url" } @@ -106,7 +106,7 @@ git_clone_go_script_bash() { -b "$GO_SCRIPT_BASH_VERSION" "$GO_SCRIPT_BASH_REPO_URL" \ "$GO_SCRIPT_BASH_CORE_DIR"; then printf "Failed to clone '%s'; aborting.\n" "$GO_SCRIPT_BASH_REPO_URL" >&2 - return 1 + return "$_GO_EC_GENERR" fi printf "Clone of '%s' successful.\n\n" "$GO_SCRIPT_BASH_REPO_URL" } @@ -115,7 +115,7 @@ if [[ ! -e "$GO_SCRIPT_BASH_CORE_DIR/go-core.bash" ]]; then if ! download_go_script_bash_tarball; then printf "Using git clone as fallback\n" if ! git_clone_go_script_bash; then - exit 1 + exit "$_GO_EC_GENERR" fi fi fi diff --git a/lib/bats-main b/lib/bats-main index 74ec0b1..f977b50 100644 --- a/lib/bats-main +++ b/lib/bats-main @@ -180,7 +180,7 @@ fi if [[ "$word_index" -eq '0' ]]; then echo '--coverage' '--edit' '--list' if [[ "${1:0:1}" == '-' ]]; then - return + return "$_GO_EC_OK" fi fi @go 'glob' '--complete' "$test_word_index" "${_GO_BATS_GLOB_ARGS[@]}" "$@" diff --git a/lib/bats/helpers b/lib/bats/helpers index 76a281a..c4619a7 100644 --- a/lib/bats/helpers +++ b/lib/bats/helpers @@ -287,7 +287,7 @@ test_break_after_num_iterations() { for __tbani_caller_var in "${@:2}"; do printf -- '%s: %s\n' "$__tbani_caller_var" "${!__tbani_caller_var}" >&2 done - exit 1 + exit "$_GO_EC_GENERR" fi } export -f test_break_after_num_iterations @@ -551,7 +551,7 @@ __create_bats_test_script() { if [[ -z "$script_path" ]]; then echo "No test script name or path specified" >&2 - exit 1 + exit "$_GO_EC_USAGE" elif [[ "$script_dir" == "$script_path" ]]; then script_dir='' fi diff --git a/lib/complete b/lib/complete index 50f492a..6414a38 100644 --- a/lib/complete +++ b/lib/complete @@ -28,7 +28,12 @@ compgen_exit_index="$((${#compreply[@]} - 1))" - if [[ "${compreply[$compgen_exit_index]}" -ne '0' ]]; then + if [[ "${compreply[$compgen_exit_index]}" -eq '1' ]]; then + return "$_GO_EC_NOTFND" + elif [[ "${compreply[$compgen_exit_index]}" -eq '2' ]]; then + return "$_GO_EC_USAGE" + elif [[ "${compreply[$compgen_exit_index]}" -ne '0' ]]; then + # probably never occurs return "${compreply[$compgen_exit_index]}" fi unset "compreply[$compgen_exit_index]" @@ -77,4 +82,6 @@ fi done done + + return "$_GO_EC_OK" } diff --git a/lib/diff b/lib/diff index c6f3271..7de34c9 100644 --- a/lib/diff +++ b/lib/diff @@ -61,9 +61,9 @@ export _GO_DIFF_EDITOR="${_GO_DIFF_EDITOR:-vimdiff}" "$_GO_DIFF_EDITOR" "$lhs" "$rhs" fi else - return '0' + return "$_GO_EC_OK" fi - return '1' + return "$_GO_EC_SIGN1" } # Log differences between two directory structures diff --git a/lib/existence b/lib/existence index 80d4366..b18180a 100644 --- a/lib/existence +++ b/lib/existence @@ -25,10 +25,15 @@ # Arguments: # label: Label describing the type of file # file_path: Path to the file to check +# +# Returns: +# _GO_EC_OK if file exists, _GO_EC_NOTFND otherwise @go.check_file_exists() { if [[ ! -e "$2" ]]; then @go.printf "%s doesn't exist: %s\n" "$1" "$2" >&2 - return 1 + return "$_GO_EC_NOTFND" + else + return "$_GO_EC_OK" fi } @@ -37,11 +42,15 @@ # Arguments: # label: Label describing the type of file # file_path: Path to the file to check +# +# Returns: +# _GO_EC_OK if file exists and is readable, _GO_EC_GENERR otherwise @go.check_file_readable() { if [[ ! -r "$2" ]]; then @go.printf "%s doesn't exist or isn't readable: %s\n" "$1" "$2" >&2 - return 1 + return "$_GO_EC_GENERR" fi + return "$_GO_EC_OK" } # Selects the first installed command from a list of possible commands/names. @@ -54,18 +63,22 @@ # ...: List of commands from which to select the first available # # Returns: -# Zero if any of the commands are installed, nonzero otherwise +# _GO_EC_OK if any of the commands are installed, _GO_EC_NOTFND otherwise @go.pick_command() { local __go_pick_cmd for __go_pick_cmd in "${@:2}"; do if command -v "$__go_pick_cmd" >/dev/null; then - printf -v "$1" -- '%s' "$__go_pick_cmd" - return + printf -v "$1" -- '%s' "$__go_pick_cmd" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]]; then + return $((_GO_EC_GENERR+ec)) + else + return "$_GO_EC_OK" + fi fi done @go.printf 'None of the following commands were found on the system:\n' >&2 printf ' %s\n' "${@:2}" >&2 - return 1 + return "$_GO_EC_NOTFND" } # Checks whether a required command is installed on the system. @@ -78,11 +91,13 @@ # err_msg: Name of the command or other info to use in the error message # # Returns: -# Zero if the command is installed, nonzero otherwise +# _GO_EC_OK if the command is installed, _GO_EC_DEPMISS otherwise @go.check_command_installed() { - if ! command -v "$1" >/dev/null; then + if command -v "$1" >/dev/null; then + return "$_GO_EC_OK" + else @go.printf 'Please install %s on your system and try again.\n' \ "${2:-$1}" >&2 - return 1 + return "$_GO_EC_DEPMISS" fi } diff --git a/lib/file b/lib/file index a0fc50d..026b334 100644 --- a/lib/file +++ b/lib/file @@ -27,7 +27,7 @@ if [[ ! "$_GO_MAX_FILE_DESCRIPTORS" =~ $__GO_FILE_DESCRIPTOR_PATTERN || printf "_GO_MAX_FILE_DESCRIPTORS is %s, must be a number greater than %d.\n" \ "\"$_GO_MAX_FILE_DESCRIPTORS\"" "$__GO_START_FD" >&2 @go.print_stack_trace '2' >&2 - exit 1 + exit "$_GO_EC_CONFIG" fi . "$_GO_USE_MODULES" 'strings' 'validation' @@ -80,7 +80,7 @@ fi *) echo "Unknown mode: $mode" >&2 @go.print_stack_trace '1' >&2 - exit 1 + exit "$_GO_EC_GENERR" ;; esac @@ -89,16 +89,20 @@ fi if ! eval "exec ${i}${bash_mode}${file_path_or_fd}"; then echo "Failed to open fd $i for $file_path_or_fd in mode '$mode' at:" >&2 @go.print_stack_trace '1' >&2 - exit 1 + exit "$_GO_EC_ARGERR" + fi + printf -v "$fd_var_reference" -- '%s' "$i" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]]; then + return $((_GO_EC_GENERR+ec)) + else + return "$_GO_EC_OK" fi - printf -v "$fd_var_reference" -- '%s' "$i" - return fi done echo "No file descriptors < $_GO_MAX_FILE_DESCRIPTORS available; failed at:" \ >&2 @go.print_stack_trace '1' >&2 - exit 1 + exit "$_GO_EC_NOTFND" } # Calls `printf` on its arguments for each of a list of file descriptors. @@ -117,7 +121,7 @@ fi # ...: Arguments to pass to the `printf` builtin @go.fds_printf() { local output_fds=() - local result=0 + local result="$_GO_EC_OK" @go.split ',' "$1" 'output_fds' shift @@ -126,7 +130,7 @@ fi if [[ ! "$output_fd" =~ $__GO_FILE_DESCRIPTOR_PATTERN ]]; then echo "Invalid file descriptor value \"$output_fd\" for $FUNCNAME at:" >&2 @go.print_stack_trace '1' >&2 - exit 1 + exit "$_GO_EC_NOTFND" fi done @@ -134,7 +138,7 @@ fi if ! printf "$@" >&"$output_fd"; then echo "Failed to print to fd $output_fd at:" >&2 @go.print_stack_trace '1' >&2 - result=1 + result="$_GO_EC_IOERR" fi done return "$result" @@ -151,20 +155,20 @@ fi if [[ "$#" -eq 0 ]]; then echo "No file descriptors to close specified at:" >&2 @go.print_stack_trace '1' >&2 - return 1 + return "$_GO_EC_USAGE" fi for fd in "$@"; do if [[ ! "$fd" =~ $__GO_FILE_DESCRIPTOR_PATTERN ]]; then echo "Bad file descriptor \"$fd\" at:" >&2 @go.print_stack_trace '1' >&2 - return 1 + return "$_GO_EC_BADFRMT" elif ! eval "exec $fd>&-"; then # Believe it or not, this case seems impossible to trigger given the $fd # check above. Keeping it despite not being able to cover it with a test. echo "Failed to close file descriptor \"$fd\" at:" >&2 @go.print_stack_trace '1' >&2 - return 1 + return "$_GO_EC_GENERR" fi done } diff --git a/lib/fileutil b/lib/fileutil index 54c9b2f..61ad798 100644 --- a/lib/fileutil +++ b/lib/fileutil @@ -113,7 +113,7 @@ local __go_source_file_errors=() local src local dest - local result='0' + local result="$_GO_EC_OK" _@go.parse_copy_files_safely_args "$@" @@ -223,7 +223,7 @@ _@go.find_missing_parent_path() { __go_missing_parent="$1" if [[ ! -d "$1" ]]; then - return 1 + return "$_GO_EC_NOTFND" fi } @@ -314,7 +314,7 @@ _@go.set_source_files_for_copy_files_safely() { if [[ -n "$__go_src_dir" && "$#" -eq '0' ]]; then @go.collect_file_paths "$__go_src_dir" __go_src_files=("${__go_collected_file_paths[@]#$__go_src_dir/}") - return + return "$_GO_EC_OK" fi __go_src_dir="${__go_src_dir:-$PWD}" __go_src_files=() @@ -337,5 +337,9 @@ _@go.set_source_files_for_copy_files_safely() { __go_src_files+=("$canonical_src") fi done - return "${#__go_source_file_errors[@]}" + + if [[ "${#__go_source_file_errors[@]}" -gt 0 ]]; then + return "$_GO_EC_GENERR" + fi + return "$_GO_EC_OK" } diff --git a/lib/internal/argv b/lib/internal/argv index 296ad20..64684bd 100644 --- a/lib/internal/argv +++ b/lib/internal/argv @@ -6,8 +6,10 @@ _@go.check_flag_has_no_arguments() { if [[ "$#" -ne 0 ]]; then echo "ERROR: $flag takes no arguments" >&2 - return 1 + return "$_GO_EC_USAGE" fi + + return "$_GO_EC_OK" } _@go.check_flag_has_one_argument() { @@ -16,16 +18,20 @@ _@go.check_flag_has_one_argument() { if [[ "$#" -eq 0 ]]; then echo "ERROR: no argument given after $flag" >&2 - return 1 + return "$_GO_EC_USAGE" elif [[ "$#" -ne 1 ]]; then echo "ERROR: only one argument should follow $flag" >&2 - return 1 + return "$_GO_EC_USAGE" fi + + return "$_GO_EC_OK" } _@go.check_argv_empty_if_no_flags() { if [[ "$#" -ne '0' ]]; then echo 'ERROR: with no flag specified, the argument list should be empty' >&2 - return 1 + return "$_GO_EC_USAGE" fi + + return "$_GO_EC_OK" } diff --git a/lib/internal/command_descriptions b/lib/internal/command_descriptions index 11961c8..247715a 100644 --- a/lib/internal/command_descriptions +++ b/lib/internal/command_descriptions @@ -19,8 +19,11 @@ _@go.format_summary() { printf -v summary " %-${longest_name_len}s %s\n" "$cmd_name" "$summary" if [[ "${#summary}" -le "$COLUMNS" ]]; then - printf "$summary" - return + printf "$summary" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]]; then + return $((_GO_EC_GENERR+ec)) + fi + return "$_GO_EC_OK" fi # Pad 4 spaces from first format string, plus 2 more to indent wrapped lines. @@ -30,7 +33,7 @@ _@go.format_summary() { if [[ "$padding_size" -ge "$((COLUMNS/2))" ]]; then printf "$summary" - return + return "$_GO_EC_OK" fi printf -v padding "%-${padding_size}s" '' @@ -71,8 +74,9 @@ _@go.command_summary() { local __go_cmd_name __go_cmd_desc='' - if ! _@go.check_command_path_and_parse_command_name "$cmd_path"; then - return 1 + _@go.check_command_path_and_parse_command_name "$cmd_path" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]]; then + return "$ec" fi local line @@ -92,7 +96,7 @@ _@go.command_summary() { if [[ "$?" -ne '0' ]]; then echo "ERROR: problem reading $cmd_path" >&2 - return 1 + return "$_GO_EC_GENERR" elif [[ -z "$__go_cmd_desc" ]]; then __go_cmd_desc='No description available' fi @@ -105,8 +109,9 @@ _@go.command_description() { local __go_cmd_name __go_cmd_desc='' - if ! _@go.check_command_path_and_parse_command_name "$cmd_path"; then - return 1 + _@go.check_command_path_and_parse_command_name "$cmd_path" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]] ; then + return "$ec" fi local line @@ -156,7 +161,7 @@ _@go.command_description() { if [[ "$?" -ne '0' ]]; then echo "ERROR: problem reading $cmd_path" >&2 - return 1 + return "$_GO_EC_GENERR" elif [[ -z "$__go_cmd_desc" ]]; then __go_cmd_desc='No description available' else @@ -176,10 +181,10 @@ _@go.check_command_path_and_parse_command_name() { if [[ -z "$cmd_path" ]]; then echo "ERROR: no command script specified" >&2 - return 1 + return "$_GO_EC_ARGERR" elif [[ ! -e "$cmd_path" ]]; then echo "ERROR: command script \"$cmd_path\" does not exist" >&2 - return 1 + return "$_GO_EC_NOTFND" elif [[ "$cmd_path" =~ $subcommand_pattern ]]; then __go_cmd_name="${BASH_REMATCH[1]//.d\// }" else diff --git a/lib/internal/commands b/lib/internal/commands index 46f088f..b72f2f1 100644 --- a/lib/internal/commands +++ b/lib/internal/commands @@ -60,7 +60,7 @@ _@go.find_commands() { done if [[ "${#__go_command_scripts[@]}" -eq '0' ]]; then - return 1 + return "$_GO_EC_NOTFND" elif [[ -z "$_GO_STANDALONE" ]]; then __go_command_scripts=("${__go_command_scripts[@]#$_GO_ROOTDIR/}") else diff --git a/lib/internal/complete b/lib/internal/complete index a187dd3..64ac1e9 100644 --- a/lib/internal/complete +++ b/lib/internal/complete @@ -22,7 +22,7 @@ _@go.complete_top_level_commands() { # return status. _@go.complete_command_path() { if [[ "$#" -eq 0 ]]; then - return 1 + return "$_GO_EC_SIGN1" fi __go_complete_word_index="$1" @@ -31,16 +31,16 @@ _@go.complete_command_path() { if [[ "$#" -le 1 || "$__go_complete_word_index" -eq '0' ]]; then _@go.complete_top_level_commands - return 1 + return "$_GO_EC_SIGN1" elif ! _@go.set_command_path_and_argv "$@"; then - return 1 + return "$_GO_EC_SIGN1" fi (( __go_complete_word_index -= ($# - ${#__go_argv[@]}) )) if [[ "$__go_complete_word_index" -lt '0' ]]; then # This (sub)command itself is the completion target. echo "${__go_cmd_path##*/}" - return 1 + return "$_GO_EC_SIGN1" elif [[ "$__go_complete_word_index" -eq '0' ]]; then # Complete subcommand scripts. diff --git a/lib/internal/path b/lib/internal/path index e9a5075..063401f 100644 --- a/lib/internal/path +++ b/lib/internal/path @@ -10,7 +10,7 @@ _@go.list_available_commands() { echo "ERROR: No commands available in:" >&2 local IFS=$'\n' echo "${@/#/ }" >&2 - return 1 + return "$_GO_EC_NOTFND" fi echo "Available commands are:" @@ -20,7 +20,7 @@ _@go.list_available_commands() { _@go.set_command_path_and_argv() { if [[ "$#" -eq '0' || -z "$*" ]]; then - return 1 + return "$_GO_EC_ARGERR" fi local cmd_args=("$@") @@ -38,14 +38,14 @@ _@go.set_command_path_and_argv() { break elif [[ -e "$try_path" ]]; then @go.printf "$try_path is not an executable script\n" >&2 - return 1 + return "$_GO_EC_CONFIG" fi done if [[ -z "$cmd_path" ]]; then printf "Unknown command: ${cmd_name}\n\n" >&2 _@go.list_available_commands "${_GO_SEARCH_PATHS[@]}" >&2 - return 1 + return "$_GO_EC_USAGE" fi local cmd_arg_index=1 @@ -63,7 +63,7 @@ _@go.set_command_path_and_argv() { break elif [[ ! (-f "$try_path" && -x "$try_path") ]]; then @go.printf "$try_path is not an executable script\n" >&2 - return 1 + return "$_GO_EC_CONFIG" fi __go_cmd_name+=("$arg") diff --git a/lib/internal/set-search-paths b/lib/internal/set-search-paths index b540b39..8b61fec 100644 --- a/lib/internal/set-search-paths +++ b/lib/internal/set-search-paths @@ -12,7 +12,7 @@ _@go.set_search_paths_add_plugin_paths() { fi done fi - return 1 + return "$_GO_EC_SIGN1" } _@go.set_search_paths() { diff --git a/lib/internal/sysexits b/lib/internal/sysexits new file mode 100644 index 0000000..1a36761 --- /dev/null +++ b/lib/internal/sysexits @@ -0,0 +1,75 @@ +#! /bin/bash + +declare -r -x -i _GO_EC_OFFSET=0 +# This is useful in case go-script-bash is used together with other frameworks +# or commands that overlap in their exit codes and the user wants to +# distinguish among them. + +declare -r -x -i _GO_EC_OK=0 + +declare -r -x -i _GO_EC_GENERR=_GO_EC_OFFSET+64 +# A general error that is not covered by the other exit codes. + +declare -r -x -i _GO_EC_EXT1=_GO_EC_OFFSET+65 +declare -r -x -i _GO_EC_EXT2=_GO_EC_OFFSET+66 +# These should be used to return error codes from external commands. Eg when +# calling `compgen` or `printf`. Most of these commands exit with `1` on general +# error and `2` on usage error. Their return code should be captured in a +# a variable, eg `ec`, and returned as `return $((_GO_EC_GENERR+ec)). + +declare -r -x -i _GO_EC_USAGE=_GO_EC_OFFSET+67 +# The application was called with an invalid option, parameter, command, etc. +# This error refers to end-user supplied input to the application, not wrong +# function arguments, etc. + +declare -r -x -i _GO_EC_NOINPUT=_GO_EC_OFFSET+68 +# Some input was expected but none was given. This refers to either expected +# input from the user (eg via a prompt) or command/function input that is +# empty (eg an empty file). + +declare -r -x -i _GO_EC_BADFRMT=_GO_EC_OFFSET+69 +# An argument or a file is not in the format expected. This is used in +# validation errors or wrongly formatted files (eg scipts missing the shebang). +# For bad arguments to functions, _GO_EC_ARGERR should be used. + +declare -r -x -i _GO_EC_NOTFND=_GO_EC_OFFSET+70 +# A resource was not found. "Resource" might refer to some file/folder on the +# filesystem. This return code should also be used by functions that perform a +# search for the case that no result was found. + +declare -r -x -i _GO_EC_ARGERR=_GO_EC_OFFSET+71 +# A function was passed an argument that is invalid. See also _GO_EC_BADFRMT. + +declare -r -x -i _GO_EC_DEPMISS=_GO_EC_OFFSET+72 +# A required program is missing. + +declare -r -x -i _GO_EC_CANTCREAT=_GO_EC_OFFSET+73 +# An operation failed to create a target. It can be used in filesystem +# operations like `mv` and `tar x` or in network requests. + +declare -r -x -i _GO_EC_IOERR=_GO_EC_OFFSET+74 +# An input/output operation failed. Here, IO is defined loosely. Eg writing to +# the console failed. + +declare -r -x -i _GO_EC_SIGN1=_GO_EC_OFFSET+75 +declare -r -x -i _GO_EC_SIGN2=_GO_EC_OFFSET+76 +# These should not be used as error codes. If a function should return two or +# three different return values to signal two or three different cases, these +# _GO_EC_OK should be used to signal the first, most common or expected one and +# these two signals the rest. Eg, `@go.file_diff` returns _GO_EC_OK if the +# supplied files are the same or _GO_EC_SIGN1 if not (in both cases, there is no +# actual error). + +declare -r -x -i _GO_EC_NOPERM=_GO_EC_OFFSET+77 +# The user running the script does not have permission to perform an operation, +# either locally or over the network. + +declare -r -x -i _GO_EC_CONFIG=_GO_EC_OFFSET+78 +# There is an issue with the configuration of either the system, go-script or a +# network resource. Eg, an environment variable is missing, a set parameter +# exceeds a maximum value, etc. + +declare -r -x -i _GO_EC_BASE=_GO_EC_OFFSET+64 +declare -r -x -i _GO_EC_MAX=_GO_EC_OFFSET+78 +# These are useful in checking whether an exit code belongs to go-script's +# exit codes. diff --git a/lib/internal/use b/lib/internal/use index a5ee0a9..a8a2615 100644 --- a/lib/internal/use +++ b/lib/internal/use @@ -87,7 +87,7 @@ _@go.find_module() { if [[ -n "$_GO_INJECT_MODULE_PATH" ]]; then __go_module_file="$_GO_INJECT_MODULE_PATH/$__go_module_name" if [[ -f "$__go_module_file" ]]; then - return + return "$_GO_EC_OK" fi fi __go_module_file="$_GO_CORE_DIR/lib/$__go_module_name" @@ -135,7 +135,7 @@ _@go.use_modules() { if ! _@go.find_module; then @go.printf 'ERROR: Module %s not found at:\n' "$__go_module_name" >&2 @go.print_stack_trace 1 >&2 - exit 1 + exit "$_GO_EC_NOTFND" fi # If we found the module in our project, but we're installed as a plugin, @@ -176,7 +176,7 @@ _@go.use_modules() { @go.printf 'ERROR: Failed to import %s module from %s at:\n' \ "$__go_module_name" "$__go_module_file" >&2 @go.print_stack_trace 1 >&2 - exit 1 + exit "$_GO_EC_IOERR" fi done } diff --git a/lib/kcov-ubuntu b/lib/kcov-ubuntu index fcd79a7..ada0598 100644 --- a/lib/kcov-ubuntu +++ b/lib/kcov-ubuntu @@ -54,13 +54,13 @@ run_kcov() { if ! command -v apt-get >/dev/null; then @go.printf 'Coverage is only available on Linux platforms %s.\n' \ 'with apt-get' >&2 - return 1 + return "$_GO_EC_GENERR" elif [[ -d "$coverage_dir" ]]; then @go.printf '%s %s\n' "The $coverage_dir directory already exists." \ 'Please move or remove this directory first.' >&2 - return 1 + return "$_GO_EC_GENERR" elif ! ( [[ -f "$kcov_path" ]] || __clone_and_build_kcov "$kcov_dir" ); then - return 1 + return "$_GO_EC_CANTCREAT" fi local kcov_flags=("--include-pattern=$include_pattern" @@ -86,7 +86,7 @@ run_kcov() { fi else @go.printf 'kcov exited with errors.\n' - return 1 + return "$_GO_EC_GENERR" fi } @@ -105,7 +105,7 @@ __clone_and_build_kcov() { @go.printf 'Cloning kcov repository from %s...\n' "$__KCOV_URL" if ! @go get git-repo "$__KCOV_URL" "$__KCOV_VERSION" "$kcov_dir"; then - return 1 + return "$_GO_EC_GENERR" fi fi @@ -116,17 +116,17 @@ __clone_and_build_kcov() { if ! sudo apt-get install -y "${__KCOV_DEV_PACKAGES[@]}"; then @go.printf "Failed to install dev packages needed to build kcov." >&2 - return 1 + return "$_GO_EC_GENERR" fi fi @go.printf 'Building kcov...\n' if cd "$kcov_dir" >/dev/null && cmake . && make && cd - >/dev/null; then - return + return "$_GO_EC_OK" fi @go.printf 'Failed to build kcov.\n' >&2 - return 1 + return "$_GO_EC_GENERR" } __check_kcov_dev_packages_installed() { diff --git a/lib/log b/lib/log index 793a9a0..8e4a880 100644 --- a/lib/log +++ b/lib/log @@ -223,7 +223,7 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$' @go.log() { local args=("$@") local log_level="${args[0]^^}" - local exit_status=0 + local exit_status="$_GO_EC_OK" local log_msg local stripped_log_msg local level_fd @@ -235,7 +235,7 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$' if ! _@go.log_level_index "$log_level"; then @go.log ERROR "Unknown log level $log_level; defaulting to WARN" @go.log WARN "${args[@]}" - return 1 + return "$_GO_EC_GENERR" fi if [[ "$log_level" =~ ERROR|QUIT|FATAL ]]; then @@ -305,17 +305,26 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$' @go.log_timestamp() { _@go.log_init + local ec=0 + case "$__GO_LOG_TIMESTAMP_IMPL" in printf) - printf -v __go_log_timestamp "%($_GO_LOG_TIMESTAMP_FORMAT)T" - return "$?" + printf -v __go_log_timestamp "%($_GO_LOG_TIMESTAMP_FORMAT)T" && ec=0 || ec="$?" ;; date) - __go_log_timestamp="$(date "+$_GO_LOG_TIMESTAMP_FORMAT")" - return "$?" + __go_log_timestamp="$(date "+$_GO_LOG_TIMESTAMP_FORMAT")" && ec=0 || ec="$?" ;; + *) + ec=127 esac - return 1 + + if [[ "$ec" -eq 0 ]];then + return "$_GO_EC_OK" + elif [[ "$ec" -eq 127 ]]; then + return "$_GO_EC_DEPMISS" + else + return "$_GO_EC_GENERR" + fi } # Adds a new log level or updates an existing one. @@ -430,7 +439,7 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$' @go.critical_section_end() { if [[ "$__GO_LOG_CRITICAL_SECTION" -ne '0' ]]; then ((--__GO_LOG_CRITICAL_SECTION)) - return 0 + return "$_GO_EC_OK" fi } @@ -471,7 +480,7 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$' @go.log RUN "$cmd_string" if [[ -n "$_GO_DRY_RUN" ]]; then - return + return "$_GO_EC_OK" fi local __go_log_level_file_descriptors=('1') @@ -607,7 +616,7 @@ _@go.log_init() { local run_fd if [[ -n "$__GO_LOG_INIT" ]]; then - return + return "$_GO_EC_OK" fi readonly __GO_LOG_INIT='done' @@ -701,10 +710,10 @@ _@go.log_level_index() { for ((i=0; i != "${#_GO_LOG_LEVELS[@]}"; ++i)); do if [[ "${_GO_LOG_LEVELS[$i]}" == "$1" ]]; then __go_log_level_index="$i" - return + return "$_GO_EC_OK" fi done - return 1 + return "$_GO_EC_NOTFND" } # Checks whether the log message is of sufficient priority to emit @@ -752,11 +761,11 @@ _@go.log_load() { if [[ "${#__GO_LOG_LEVELS_FORMAT_CODES[@]}" != "$num_levels" ]]; then echo "Should have $num_levels log level format codes," \ "only have ${#__GO_LOG_LEVELS_FORMAT_CODES[@]}" >&2 - exit 1 + exit "$_GO_EC_CONFIG" elif [[ "${#__GO_LOG_LEVELS_FILE_DESCRIPTORS[@]}" != "$num_levels" ]]; then echo "Should have $num_levels log level file descriptors," \ "only have ${#__GO_LOG_LEVELS_FILE_DESCRIPTORS[@]}" >&2 - exit 1 + exit "$_GO_EC_CONFIG" fi } diff --git a/lib/path b/lib/path index a63a7dd..260b643 100644 --- a/lib/path +++ b/lib/path @@ -145,19 +145,19 @@ for current in "${@:2}"; do if [[ -e "$current" ]] && ! "$operation" "$current"; then - return 1 + return "$_GO_EC_GENERR" elif [[ -d "$current" ]]; then if [[ -n "$do_bfs" ]]; then bfs_queue+=("$current") elif ! @go.walk_file_system "$operation" "$current"/*; then - return 1 + return "$_GO_EC_GENERR" fi fi done for current in "${bfs_queue[@]}"; do if ! @go.walk_file_system '--bfs' "$operation" "$current"/*; then - return 1 + return "$_GO_EC_GENERR" fi done } @@ -191,7 +191,7 @@ for component in "${components[@]}"; do current_path+="${component:-/}" if ! "$operation" "$current_path"; then - return 1 + return "$_GO_EC_GENERR" elif [[ -n "$component" ]]; then current_path+='/' fi @@ -257,7 +257,7 @@ # path: Current path passed in by @go.walk_path_forward _@go.realpath_impl() { if ! cd -P "$1" >/dev/null 2>&1; then - return 1 + return "$_GO_EC_GENERR" fi printf -v '__grpi_real_prefix' '%s' "$1" printf -v '__grpi_real_dir' '%s' "$PWD" diff --git a/lib/platform b/lib/platform index e749d9f..8dbc952 100644 --- a/lib/platform +++ b/lib/platform @@ -22,7 +22,7 @@ _@go.platform_os_release() { local value if [[ ! -f "$__GO_ETC_OS_RELEASE" ]]; then - return 1 + return "$_GO_EC_NOTFND" fi while read -r line; do @@ -65,7 +65,7 @@ _@go.platform_ostype() { _@go.platform() { if [[ -n "$_GO_PLATFORM_ID" ]]; then - return + return "$_GO_EC_OK" elif ! _@go.platform_os_release; then _@go.platform_ostype fi diff --git a/lib/prompt b/lib/prompt index 393a9f4..fba1ab1 100644 --- a/lib/prompt +++ b/lib/prompt @@ -60,7 +60,7 @@ if [[ -z "${!1}" && -n "$4" ]]; then @go.printf '%s\n' "$4" >&2 - return 1 + return "$_GO_EC_NOINPUT" fi } @@ -89,14 +89,15 @@ fi @go.validate_identifier_or_die 'Input prompt response variable name' "$1" - if ! @go.prompt_for_input "$@"; then - return 1 + @go.prompt_for_input "$@" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]]; then + return "$ec" elif [[ "$or_die" == 'true' ]]; then @go.validate_input_or_die "$description" "${!1}" elif ! @go.validate_input "${!1}"; then @go.printf '"%s" is an invalid response, as it contains %s.\n' \ "${!1}" 'unescaped shell metacharacters or control operators' >&2 - return 1 + return "$_GO_EC_BADFRMT" fi } @@ -127,7 +128,7 @@ @go.printf 'Invalid `default` parameter "%s" for %s at:\n' \ "$default" "$FUNCNAME" >&2 @go.print_stack_trace '1' >&2 - exit 1 + exit "$_GO_EC_BADFRMT" ;; esac @@ -135,9 +136,9 @@ @go.read_prompt_response 'response' "$default" if [[ "$response" =~ ^[Yy]([Ee][Ss])?$ ]]; then - return 0 + return "$_GO_EC_OK" elif [[ "$response" =~ ^[Nn]([Oo])?$ ]]; then - return 1 + return "$_GO_EC_SIGN1" else if [[ -n "$response" ]]; then @go.printf '\n"%s" is an invalid response.\n' "$response" >&2 diff --git a/lib/subcommands b/lib/subcommands index 5b731ef..68f34d4 100644 --- a/lib/subcommands +++ b/lib/subcommands @@ -19,14 +19,14 @@ # Returns: # zero on success, nonzero on error (when `_GO_CMD_ARGV` is not empty) @go.show_subcommands() { - local result='0' + local result="$_GO_EC_OK" local out_fd='1' local cmd_name="${_GO_CMD_NAME[*]}" if [[ "${#_GO_CMD_ARGV[@]}" -ne 0 ]]; then @go.printf '"%s" is not an available subcommand of "%s".\n\n' \ "${_GO_CMD_ARGV[0]}" "$cmd_name" >&2 - result='1' + result="$_GO_EC_USAGE" out_fd='2' fi diff --git a/lib/testing/log b/lib/testing/log index e0a7ac9..432f082 100644 --- a/lib/testing/log +++ b/lib/testing/log @@ -54,7 +54,7 @@ export _GO_LOG_TIMESTAMP_FORMAT= local __go_log_level_index=0 if ! _@go.log_level_index "$label"; then printf 'Unknown log level label: %s\n' "$label" >&2 - exit 1 + exit "$_GO_EC_NOTFND" fi printf '%s' "${__GO_LOG_LEVELS_FORMATTED[$__go_log_level_index]}" restore_bats_shell_options "$?" @@ -174,7 +174,7 @@ __@go.assert_log_equals() { if ! shift 2; then printf 'ERROR: Wrong number of arguments for log line %d.\n' "$i" >&2 - return 1 + return "$_GO_EC_ARGERR" fi else expected+=("$1") @@ -205,7 +205,7 @@ __@go.parse_log_level_label() { fi if ! _@go.log_level_index "$try_level"; then - return 1 + return "$_GO_EC_NOTFND" elif [[ "$level_label" != "$try_level" ]]; then __log_level_label="$level_label" else diff --git a/lib/testing/stack-trace b/lib/testing/stack-trace index dfec99f..5c84257 100644 --- a/lib/testing/stack-trace +++ b/lib/testing/stack-trace @@ -104,11 +104,11 @@ __@go.stack_trace_item() { if [[ -z "$function_name" ]]; then printf 'No function name specified for `%s`.\n' "${FUNCNAME[1]}" >&2 - exit 1 + exit "$_GO_EC_ARGERR" elif [[ "$function_name" =~ ^(main|source)$ && -z "$target_line" ]]; then printf 'No target line from `%s` specified for `%s`.\n' \ "$function_name" "${FUNCNAME[1]}" >&2 - exit 1 + exit "$_GO_EC_ARGERR" fi if [[ "$function_name" == 'main' || "$function_name" == 'source' ]]; then @@ -163,8 +163,11 @@ __@go.stack_trace_item() { case "$state" in SUCCESS) - printf ' %s:%d %s\n' "$filepath" "$lineno" "$function_name" - return + printf ' %s:%d %s\n' "$filepath" "$lineno" "$function_name" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]]; then + return $((_GO_EC_GENERR+ec)) + fi + return "$_GO_EC_OK" ;; MAIN|FOUND_FUNCTION) printf 'Line not found in `%s` from "%s": "%s"\n' \ @@ -174,7 +177,7 @@ __@go.stack_trace_item() { printf 'Function `%s` not found in "%s".\n' "$function_name" "$filepath" >&2 ;; esac - return '1' + return "$_GO_EC_NOTFND" } # Implementation for `@go.set_go_core_stack_trace_components` @@ -199,7 +202,7 @@ __@go.set_go_core_stack_trace_components() { if [[ "$stack_item" =~ $go_core_file ]]; then GO_CORE_STACK_TRACE_COMPONENTS+=("$stack_item") elif [[ "${#_GO_CORE_STACK_TRACE_COMPONENTS[@]}" -ne '0' ]]; then - return + return "$_GO_EC_OK" fi done rm "$script" "$TEST_GO_SCRIPTS_DIR/print-stack-trace" @@ -220,7 +223,7 @@ __@go.count_lines() { if [[ -z "$result_name" ]]; then printf 'No result variable specified for `%s`.\n' "${FUNCNAME[1]}" >&2 - exit 1 + exit "$_GO_EC_ARGERR" fi while read line; do @@ -238,9 +241,9 @@ __@go.count_lines() { __@go.check_file_path_specified_and_present() { if [[ -z "$1" ]]; then printf 'No file specified for `%s`.\n' "${FUNCNAME[1]}" >&2 - exit 1 + exit "$_GO_EC_ARGERR" elif [[ ! -e "$1" ]]; then printf 'Create "%s" before calling `%s`.\n' "$1" "${FUNCNAME[1]}" >&2 - exit 1 + exit "$_GO_EC_GENERR" fi } diff --git a/lib/validation b/lib/validation index 9be7e9d..61bd913 100644 --- a/lib/validation +++ b/lib/validation @@ -36,7 +36,11 @@ readonly __GO_VALID_IDENTIFIER_PATTERN='^[[:alpha:]_][[:alnum:]_]*$' # Returns: # 0 on success; nonzero on failure @go.validate_input() { - [[ ! "$1" =~ $__GO_VALIDATE_INPUT_PATTERN ]] + if [[ "$1" =~ $__GO_VALIDATE_INPUT_PATTERN ]]; then + return "$_GO_EC_BADFRMT" + else + return "$_GO_EC_OK" + fi } # Wraps `@go.validate_input` to print an error and exit on failure @@ -56,7 +60,7 @@ readonly __GO_VALID_IDENTIFIER_PATTERN='^[[:alpha:]_][[:alnum:]_]*$' "$description" "$value" "${FUNCNAME[$((skip_callers - 1))]}" \ 'unescaped shell metacharacters or control operators' >&2 @go.print_stack_trace "$skip_callers" >&2 - exit 1 + exit "$_GO_EC_BADFRMT" fi } @@ -70,7 +74,11 @@ readonly __GO_VALID_IDENTIFIER_PATTERN='^[[:alpha:]_][[:alnum:]_]*$' # Returns: # 0 on success; nonzero on failure @go.validate_identifier() { - [[ "$1" =~ $__GO_VALID_IDENTIFIER_PATTERN ]] + if [[ "$1" =~ $__GO_VALID_IDENTIFIER_PATTERN ]]; then + return "$_GO_EC_OK" + else + return "$_GO_EC_BADFRMT" + fi } # Wraps `@go.validate_identifier` to print an error and exit on failure @@ -98,6 +106,6 @@ readonly __GO_VALID_IDENTIFIER_PATTERN='^[[:alpha:]_][[:alnum:]_]*$' printf '%s "%s" for %s %s at:\n' \ "$description" "$value" "$called_func" "$err_msg" >&2 @go.print_stack_trace "$skip_callers" >&2 - exit 1 + exit "$_GO_EC_BADFRMT" fi } diff --git a/libexec/aliases b/libexec/aliases index ecfbbcd..2836527 100755 --- a/libexec/aliases +++ b/libexec/aliases @@ -31,14 +31,14 @@ _@go.aliases() { for c in "${_GO_ALIAS_CMDS[@]}"; do echo "$c" done - return + return "$_GO_EC_OK" elif [[ "$1" == '--complete' ]]; then # Tab completions local word_index="$2" if [[ "$word_index" -eq 0 ]]; then echo "--exists" fi - return + return "$_GO_EC_OK" fi . "$_GO_CORE_DIR/lib/internal/argv" @@ -49,17 +49,17 @@ _@go.aliases() { flag="${1#--}" shift if ! _@go.check_flag_has_one_argument "--$flag" "$@"; then - return 1 + return "$_GO_EC_USAGE" fi ;; -*) @go.printf "ERROR: unknown flag: $1\n" >&2 - return 1 + return "$_GO_EC_USAGE" ;; esac if [[ -z "$flag" ]] && ! _@go.check_argv_empty_if_no_flags "$@"; then - return 1 + return "$_GO_EC_USAGE" fi # Help filter @@ -67,7 +67,7 @@ _@go.aliases() { local pattern='{{_GO_ALIAS_CMDS}}' local replacement="${_GO_ALIAS_CMDS[@]}" echo "${1//$pattern/$replacement}" - return + return "$_GO_EC_OK" fi for c in "${_GO_ALIAS_CMDS[@]}"; do @@ -84,10 +84,10 @@ _@go.aliases() { "\"$_GO_CMD env\" to set up your shell environment." fi fi - return + return "$_GO_EC_OK" done - return 1 + return "$_GO_EC_NOTFND" } _@go.aliases "$@" diff --git a/libexec/builtins b/libexec/builtins index 8cb485d..a99e14e 100755 --- a/libexec/builtins +++ b/libexec/builtins @@ -59,8 +59,10 @@ _@go.builtin_summaries() { local __go_cmd_desc for cmd_name in "${_GO_BUILTIN_CMDS[@]}"; do - if ! _@go.command_summary "$_GO_CORE_DIR/libexec/$cmd_name"; then - return 1 + + _@go.command_summary "$_GO_CORE_DIR/libexec/$cmd_name" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]] ; then + return "$ec" fi _@go.format_summary "$cmd_name" "$__go_cmd_desc" $longest_name_len done @@ -70,14 +72,14 @@ _@go.builtins() { if [[ "$#" -eq '0' ]]; then local IFS=$'\n' echo "${_GO_BUILTIN_CMDS[*]}" - return + return "$_GO_EC_OK" elif [[ "$1" == '--complete' ]]; then # Tab completions local word_index="$2" if [[ "$word_index" -eq '0' ]]; then echo '--exists' '--summaries' fi - return + return "$_GO_EC_OK" fi . "$_GO_CORE_DIR/lib/internal/argv" @@ -88,7 +90,7 @@ _@go.builtins() { flag="$1" shift if ! _@go.check_flag_has_no_arguments "$flag" "$@"; then - return 1 + return "$_GO_EC_USAGE" fi _@go.builtin_summaries return @@ -97,35 +99,35 @@ _@go.builtins() { flag="$1" shift if ! _@go.check_flag_has_one_argument "$flag" "$@"; then - return 1 + return "$_GO_EC_USAGE" fi for cmd_name in "${_GO_BUILTIN_CMDS[@]}"; do if [[ "$cmd_name" == "$1" ]]; then - return + return "$_GO_EC_OK" fi done - return 1 + return "$_GO_EC_NOTFND" ;; --help-filter) flag="$1" shift # Help filter if ! _@go.check_flag_has_one_argument "$flag" "$@"; then - return 1 + return "$_GO_EC_USAGE" fi local pattern='{{_GO_BUILTIN_SUMMARIES}}' local replacement="$(_@go.builtin_summaries)" echo "${1//$pattern/$replacement}" - return + return "$_GO_EC_OK" ;; -*) echo "ERROR: unknown flag: $1" - return 1 + return "$_GO_EC_USAGE" ;; esac if ! _@go.check_argv_empty_if_no_flags "$@"; then - return 1 + return "$_GO_EC_USAGE" fi } diff --git a/libexec/commands b/libexec/commands index ad65a5d..6ea3ebb 100755 --- a/libexec/commands +++ b/libexec/commands @@ -64,7 +64,7 @@ _@go.commands_parse_search_paths() { for search_path in "${__go_commands_search_paths[@]}"; do if [[ ! -d "$search_path" ]]; then @go.printf "Command search path %s does not exist.\n" "$search_path" >&2 - return 1 + return "$_GO_EC_NOTFND" fi done } @@ -77,7 +77,7 @@ _@go.commands_parse_argv() { ;; -*) echo "Unknown option: $1" - return 1 + return "$_GO_EC_USAGE" ;; esac @@ -86,19 +86,20 @@ _@go.commands_parse_argv() { elif [[ "$1" =~ : || -d "$1" ]]; then if [[ "$#" -ne 1 ]]; then @go.printf 'Cannot specify any arguments after search paths.\n' >&2 - return 1 + return "$_GO_EC_USAGE" elif ! _@go.commands_parse_search_paths "$1"; then - return 1 + return "$_GO_EC_NOTFND" fi else if _@go.source_builtin 'aliases' --exists "$1"; then @go.printf '%s is a shell alias.\n' "$1" >&2 - return 1 + return "$_GO_EC_GENERR" fi local __go_cmd_path - if ! _@go.set_command_path_and_argv "$@"; then - return 1 + _@go.set_command_path_and_argv "$@" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]]; then + return "$?" fi __go_commands_search_paths=("${__go_cmd_path}.d") fi @@ -111,7 +112,7 @@ _@go.commands_tab_completions() { if [[ "$word_index" -eq '0' ]]; then echo '--paths' '--summaries' if [[ "${1:0:1}" == '-' || "$#" -gt 1 ]]; then - return + return "$_GO_EC_OK" fi fi @@ -119,13 +120,13 @@ _@go.commands_tab_completions() { shift ((--word_index)) elif [[ "$1" =~ : || -d "$1" ]]; then - return 1 + return "$_GO_EC_SIGN1" fi local args=("$@") local last_arg_index="$((${#args[@]} - 1))" if [[ "$word_index" -lt "$last_arg_index" ]]; then - return 1 + return "$_GO_EC_SIGN1" fi _@go.commands "${args[@]:0:$word_index}" } @@ -141,8 +142,9 @@ _@go.commands() { local __go_commands_action="list" local __go_commands_search_paths=() - if ! _@go.commands_parse_argv "$@"; then - return 1 + _@go.commands_parse_argv "$@" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]] ; then + return "$ec" fi . "$_GO_CORE_DIR/lib/internal/commands" @@ -152,7 +154,7 @@ _@go.commands() { local __go_longest_name_len if ! _@go.find_commands "${__go_commands_search_paths[@]}"; then - return 1 + return "$_GO_EC_NOTFND" fi case "$__go_commands_action" in diff --git a/libexec/complete b/libexec/complete index 9de742b..c31e5c0 100755 --- a/libexec/complete +++ b/libexec/complete @@ -69,6 +69,7 @@ _@go.complete_args() { local args=("$@") local cmd_name="${args[0]}" local word="${args[$word_index]}" + local ec=0 . "$_GO_CORE_DIR/lib/internal/complete" @@ -90,6 +91,7 @@ _@go.complete_args() { return fi + ec="unset" case "$cmd_name" in cd|pushd) @go.compgen -d -- "$word" @@ -103,12 +105,12 @@ _@go.complete_args() { cmd_name='help' ;; -*|unenv) - return 1 + return "$_GO_EC_GENERR" ;; esac if _@go.source_builtin 'aliases' --exists "$cmd_name"; then - @go.compgen -f -- "$word" + @go.compgen -f -- "$word" && ec=0 || ec="$?" return fi diff --git a/libexec/demo-core.d/log b/libexec/demo-core.d/log index 2b049b3..19fa5db 100755 --- a/libexec/demo-core.d/log +++ b/libexec/demo-core.d/log @@ -68,7 +68,7 @@ log_demo() { if [[ "$2" -eq 0 ]]; then echo "-v --verbose" fi - return + return "$_GO_EC_OK" fi . "$_GO_USE_MODULES" 'log' diff --git a/libexec/demo-core.d/prompt b/libexec/demo-core.d/prompt index 6b4e2c9..544dc4d 100755 --- a/libexec/demo-core.d/prompt +++ b/libexec/demo-core.d/prompt @@ -18,7 +18,7 @@ _@go.prompt_demo() { # No default value; returns error on no input. if ! @go.prompt_for_input 'name' $'What is your name?\n' '' \ 'Run away, Sir or Madam Not Appearing in this Film! Run away!'; then - return 1 + return "$_GO_EC_NOINPUT" fi @go.printf 'Nice to meet you, %s!\n' "$name" @@ -28,7 +28,7 @@ _@go.prompt_demo() { @go.prompt_for_input 'quest' $'What is your quest?\n' "$quest" elif ! @go.prompt_for_yes_or_no "Might I suggest: $quest" 'yes'; then @go.printf 'OK, no quest. Suit yourself!\n' - return 1 + return "$_GO_EC_SIGN1" fi @go.printf 'Your quest is: %s\n' "$quest" } diff --git a/libexec/demo-core.d/select-option b/libexec/demo-core.d/select-option index b55789e..7f83a1f 100755 --- a/libexec/demo-core.d/select-option +++ b/libexec/demo-core.d/select-option @@ -27,7 +27,7 @@ select_option_demo() { if ! @go.select_option 'selected' "${options[@]}"; then @go.printf 'You declined to select an option. Exiting...\n\n' - return 1 + return "$_GO_EC_NOINPUT" else @go.printf 'You selected: "%s"\n\n' "$selected" fi @@ -35,7 +35,7 @@ select_option_demo() { if ! @go.prompt_for_yes_or_no 'Would you like to select another option?' \ 'yes'; then @go.printf 'Exiting...\n' - return 0 + return fi done } diff --git a/libexec/env b/libexec/env index 22e52b4..cf93054 100755 --- a/libexec/env +++ b/libexec/env @@ -26,7 +26,7 @@ _@go.env() { if [[ "$word_index" -eq '0' ]]; then echo '-' fi - return + return "$_GO_EC_OK" fi local go_func="$1" @@ -39,7 +39,7 @@ _@go.env() { @go.printf "%s%s\n" \ "Check $_GO_CORE_URL/tree/master/lib/env to see if an implementation " \ "has been added, or feel free to send a pull request to add it." >&2 - return 1 + return "$_GO_EC_GENERR" fi local spaces_error=('ERROR: %s must not contain spaces in order' @@ -47,7 +47,7 @@ _@go.env() { if _@go.has_spaces "$default_name"; then @go.printf "${spaces_error[*]}" "the \"$default_name\" script" >&2 - return 1 + return "$_GO_EC_CONFIG" fi if [[ -z "$go_func" ]]; then @@ -67,7 +67,7 @@ _@go.env() { @go.printf 'ERROR: %s must contain a line of the form "%s" %s%s\n' \ "$shell_impl" "$eval_pattern" "illustrating the command used to " \ "evaluate the generated shell commands." >&2 - return 1 + return "$_GO_EC_BADFRMT" fi read -r -d '' help_msg <&2 - return 1 + return "$_GO_EC_CONFIG" fi read -r -d '' env_script < "$shell_impl" diff --git a/libexec/get.d/file b/libexec/get.d/file index f6fc7a6..dca7351 100755 --- a/libexec/get.d/file +++ b/libexec/get.d/file @@ -98,7 +98,7 @@ _@go.get_file_download_command() { fi @go.printf 'Please install curl or wget before running "%s".\n' \ "${_GO_CMD_NAME[*]}" >&2 - return 1 + return "$_GO_EC_DEPMISS" ;; esac } @@ -116,27 +116,27 @@ _@go.get_file_impl() { local download_dir="$1" local filename="$2" local url="$3" - local result='0' + local result="$_GO_EC_OK" local errfile="${url##*/}.get-error" local errmsg local __go_get_file_download_cmd=() if ! _@go.get_file_download_command "$filename"; then - return 1 + return "$_GO_EC_DEPMISS" fi if [[ -n "$filename" ]]; then if [[ -f "$filename" ]]; then @go.printf 'File already exists; not overwriting: %s\n' "$filename" >&2 - return 1 + return "$_GO_EC_CANTCREAT" elif [[ ! -d "$download_dir" ]] && ! mkdir -p "$download_dir"; then @go.printf "Download dir doesn't exist and can't be created: %s\n" \ "$download_dir" >&2 - return 1 + return "$_GO_EC_CANTCREAT" elif [[ ! -w "$download_dir" ]]; then @go.printf "You don't have permission to write to download dir: %s\n" \ "$download_dir" >&2 - return 1 + return "$_GO_EC_NOPERM" fi errfile="${filename}.get-error" fi @@ -154,7 +154,7 @@ _@go.get_file_impl() { if [[ -f "$filename" ]]; then rm "$filename" fi - result='1' + result="$_GO_EC_GENERR" elif [[ -n "$filename" ]]; then @go.printf 'Downloaded "%s" as: %s\n' "$url" "$filename" @@ -186,7 +186,7 @@ _@go.get_file() { if [[ "$#" -ne '1' ]]; then @go help "${_GO_CMD_NAME[@]}" >&2 - return 1 + return "$_GO_EC_USAGE" fi url="$1" diff --git a/libexec/get.d/git-repo b/libexec/get.d/git-repo index 9064aa8..acb6252 100755 --- a/libexec/get.d/git-repo +++ b/libexec/get.d/git-repo @@ -32,13 +32,13 @@ _@go.get_git_repo_impl() { if ! command -v git >/dev/null; then @go.printf 'Please install git before running "%s".\n' \ "${_GO_CMD_NAME[*]}" >&2 - return 1 + return "$_GO_EC_DEPMISS" elif [[ -e "$clone_dir" ]]; then @go.printf '"%s" already exists; not updating.\n' "$clone_dir" - return 1 + return "$_GO_EC_GENERR" elif ! "${git_clone[@]}" -b "$git_ref" "$repo_url" "$clone_dir"; then @go.printf 'Failed to clone %s.\n' "$repo_msg" >&2 - return 1 + return "$_GO_EC_IOERR" fi @go.printf 'Successfully cloned %s.\n' "$repo_msg" } @@ -62,7 +62,7 @@ _@go.get_git_repo() { if [[ "$#" -ne '3' ]]; then @go help "${_GO_CMD_NAME[@]}" >&2 - return 1 + return "$_GO_EC_USAGE" fi if [[ -e "$repo_url" ]]; then diff --git a/libexec/glob b/libexec/glob index 94217f7..0d8dd2c 100755 --- a/libexec/glob +++ b/libexec/glob @@ -52,7 +52,7 @@ _@go.glob_impl() { # directory (preferring the file if they share the same name). if [[ -f "$pattern$__go_glob_suffix" ]]; then __go_glob_matches+=("$pattern$__go_glob_suffix") - return + return "$_GO_EC_OK" elif [[ -d "$pattern" ]]; then pattern="${pattern%/}/*" fi @@ -63,7 +63,7 @@ _@go.glob_impl() { _@go.glob_impl_helper "$pattern" if [[ "${#__go_glob_impl_matches[@]}" -eq '0' ]]; then - return 1 + return "$_GO_EC_SIGN1" fi __go_glob_matches+=("${__go_glob_impl_matches[@]}") } @@ -119,7 +119,7 @@ _@go.glob_tab_completion_parse_argv() { if [[ "$word_index" -eq "$((i+1))" ]]; then # No completion for the GLOBIGNORE spec. - return 1 + return "$_GO_EC_SIGN1" elif [[ "${__go_glob_ignore_spec:0:1}" =~ $shell_quote ]]; then # The shell won't expand arguments or remove quotes during completion. local spec_len="${#__go_glob_ignore_spec}" @@ -130,17 +130,17 @@ _@go.glob_tab_completion_parse_argv() { if [[ "$word_index" -lt "$i" ]]; then __go_glob_complete_flags_only='true' - return + return "$_GO_EC_OK" elif [[ -d "$arg" ]]; then if [[ "$word_index" -eq "$((i+1))" ]]; then # No completion for the file suffix. - return 1 + return "$_GO_EC_SIGN1" elif [[ "$word_index" -ne "$i" ]]; then # Don't set rootdir if rootdir is the completion target argument. __go_glob_rootdir="${arg%/}" __go_glob_suffix="${args[$((i+1))]}" fi - return + return "$_GO_EC_OK" fi done } @@ -178,10 +178,10 @@ _@go.glob_tab_completion() { local __go_glob_complete_flags_only if ! _@go.glob_tab_completion_parse_argv "$@"; then - return 1 + return "$_GO_EC_SIGN1" elif [[ "$__go_glob_complete_flags_only" == 'true' ]]; then echo "${__go_glob_flags[@]}" - return + return "$_GO_EC_OK" elif [[ -z "$__go_glob_rootdir" ]]; then echo "${__go_glob_flags[@]}" . "$_GO_USE_MODULES" 'complete' @@ -195,7 +195,7 @@ _@go.glob_tab_completion() { _@go.glob_trim_matches if [[ "${#__go_glob_matches[@]}" -eq '0' ]]; then - return 1 + return "$_GO_EC_SIGN1" elif [[ "${#__go_glob_matches[@]}" -eq '1' ]]; then echo "${__go_glob_matches[0]}" else @@ -209,10 +209,10 @@ _@go.glob_set_globignore() { if [[ -z "$rootdir" ]]; then echo "ERROR: root dir not set before setting GLOBIGNORE" >&2 - return 1 + return "$_GO_EC_CONFIG" elif [[ ! -d "$rootdir" ]]; then echo "ERROR: root dir not valid when setting GLOBIGNORE: $rootdir " >&2 - return 1 + return "$_GO_EC_CONFIG" fi if [[ -n "$globignore_spec" ]]; then @@ -239,7 +239,7 @@ _@go.glob_parse_argv() { ;; *) @go.printf "Unknown flag: $1" >&2 - return 1 + return "$_GO_EC_USAGE" ;; esac done @@ -249,11 +249,11 @@ _@go.glob_parse_argv() { if [[ -z "$__go_glob_rootdir" ]]; then @go.printf "Root directory argument not specified.\n" >&2 - return 1 + return "$_GO_EC_ARGERR" elif [[ ! -d "$__go_glob_rootdir" ]]; then @go.printf "Root directory argument %s is not a directory.\n" \ "$__go_glob_rootdir" >&2 - return 1 + return "$_GO_EC_ARGERR" fi __go_glob_suffix="$1" @@ -261,11 +261,11 @@ _@go.glob_parse_argv() { if [[ -z "$__go_glob_suffix" ]]; then @go.printf "File suffix argument not specified.\n" >&2 - return 1 + return "$_GO_EC_ARGERR" fi if ! _@go.glob_set_globignore "$globignore" "$__go_glob_rootdir"; then - return 1 + return "$_GO_EC_SIGN1" fi __go_glob_patterns=("${@:-*}") } @@ -275,7 +275,7 @@ _@go.glob() { if [[ "$1" == '--complete' ]]; then shift _@go.glob_tab_completion "$@" - return + return "$_GO_EC_OK" fi local __go_glob_trim @@ -284,7 +284,7 @@ _@go.glob() { local __go_glob_patterns if ! _@go.glob_parse_argv "$@"; then - return 1 + return "$_GO_EC_SIGN1" fi local pattern @@ -294,7 +294,7 @@ _@go.glob() { if ! _@go.glob_impl "$__go_glob_rootdir/$pattern"; then @go.printf "\"$pattern\" does not match any %s files in %s.\n" \ "$__go_glob_suffix" "$__go_glob_rootdir" >&2 - return 1 + return "$_GO_EC_NOTFND" fi done diff --git a/libexec/help b/libexec/help index 7c3d207..8611adc 100755 --- a/libexec/help +++ b/libexec/help @@ -90,14 +90,15 @@ _@go.usage() { _@go.help_message_for_command() { if _@go.source_builtin 'aliases' --help "$1"; then - return + return "$_GO_EC_OK" fi local __go_cmd_path local __go_argv - if ! _@go.set_command_path_and_argv "$@"; then - return 1 + _@go.set_command_path_and_argv "$@" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]] ; then + return "$ec" fi local cmd_name="${__go_cmd_path//.d\// }" @@ -111,7 +112,7 @@ _@go.help_message_for_command() { # _shouldn't_ ever happen. @go.printf "ERROR: failed to parse description from %s\n" \ "$__go_cmd_path" >&2 - return 1 + return "$_GO_EC_BADFRMT" fi local filter_pattern='# [Hh]elp [Ff]ilter['$'\n\r'']' diff --git a/libexec/modules b/libexec/modules index 3afe2af..e174ea0 100755 --- a/libexec/modules +++ b/libexec/modules @@ -63,10 +63,10 @@ _@go.modules_help() { __go_module_file="$_GO_USE_MODULES" elif [[ "$#" -ne '1' ]]; then @go.printf "Please specify only one module name.\n" >&2 - return 1 + return "$_GO_EC_ARGERR" elif ! _@go.find_module; then @go.printf "Unknown module: $1\n" >&2 - return 1 + return "$_GO_EC_NOTFND" fi local __go_cmd_desc @@ -76,7 +76,7 @@ _@go.modules_help() { if ! _@go.command_description "$__go_module_file"; then @go.printf "ERROR: failed to parse description from %s\n" \ "$__go_module_file" >&2 - return 1 + return "$_GO_EC_BADFRMT" fi @go.printf "$__go_module_name - $__go_cmd_desc\n" } @@ -102,7 +102,7 @@ _@go.modules_summaries() { for module_path in "${__go_modules[@]}"; do if ! _@go.command_summary "$module_path"; then @go.printf "ERROR: failed to parse summary from %s\n" "$module_path" >&2 - return 1 + return "$_GO_EC_BADFRMT" fi __go_modules_summaries+=("$__go_cmd_desc") done @@ -134,7 +134,7 @@ _@go.modules_produce_listing() { if [[ -z "$action" ]]; then __go_modules_listing=("${modules[@]}") - return + return "$_GO_EC_OK" fi . "$_GO_USE_MODULES" 'format' @@ -148,7 +148,7 @@ _@go.modules_produce_listing() { summaries) local __go_modules_summaries=() if ! _@go.modules_summaries; then - return 1 + return "$_GO_EC_BADFRMT" fi @go.zip_items modules __go_modules_summaries ' ' modules ;; @@ -156,7 +156,7 @@ _@go.modules_produce_listing() { # Should only happen if _@go.modules is updated and this case statement # isn't. @go.printf 'ERROR: Unknown action: %s\n' "$action" >&2 - return 1 + return "$_GO_EC_USAGE" esac __go_modules_listing=("${modules[@]}") } @@ -241,22 +241,24 @@ _@go.modules_list() { if [[ "$__go_module_name" == '*' ]]; then if [[ "${#module_specs[@]}" -ne 1 ]]; then @go.printf "Do not specify other patterns when '*' is present.\n" >&2 - return 1 + return "$_GO_EC_USAGE" fi _@go.modules_search elif [[ "$__go_module_name" =~ \*|/$ ]]; then _@go.modules_search "$__go_module_name" elif ! _@go.find_module; then @go.printf "Unknown module: $__go_module_name\n" >&2 - return 1 + return "$_GO_EC_NOTFND" else __go_modules+=("$__go_module_file") fi done local __go_modules_listing=() - if ! _@go.modules_produce_listing "$action"; then - return 1 + + _@go.modules_produce_listing "$action" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]] ; then + return "$ec" fi printf '%s\n' "${__go_modules_listing[@]}" } @@ -310,9 +312,9 @@ _@go.modules_tab_completion() { if [[ ( "$word_index" -ne '0' && "${first:0:1}" == '-' ) && ( ! "$first" =~ $flags_pattern || "$first" == '--imported' ) ]]; then - return 1 + return "$_GO_EC_SIGN1" elif [[ "$first" =~ ^(-h|-help|--help)$ && "$word_index" -gt '1' ]]; then - return 1 + return "$_GO_EC_SIGN1" fi if [[ "$#" -eq '0' || "$word_index" -eq '0' ]]; then @@ -343,9 +345,11 @@ _@go.modules_tab_completion() { fi local __go_modules_listing=() - if ! _@go.modules_produce_listing; then + + _@go.modules_produce_listing && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]] ; then # Shouldn't happen, since modules_produce_listing isn't parsing summaries. - return 1 + return "$ec" fi completions+=("${__go_modules_listing[@]}") @go.complete_remove_completions_already_present \ @@ -356,7 +360,7 @@ _@go.modules_tab_completion() { _@go.modules_list_imported() { if [[ "$#" -ne 0 ]]; then @go.printf 'The --imported option takes no other arguments.\n' >&2 - return 1 + return "$_GO_EC_USAGE" fi # Collect imported module info before _GO_USE_MODULES imports possibly more. @@ -399,7 +403,7 @@ _@go.modules() { ;; -*) @go.printf "Unknown option: $action\n" >&2 - return 1 + return "$_GO_EC_USAGE" ;; *) # Here we treat $action as a potential glob pattern. diff --git a/libexec/new b/libexec/new index 276f750..88d519d 100755 --- a/libexec/new +++ b/libexec/new @@ -48,7 +48,7 @@ _@go.new_tab_completions() { if [[ "$word_index" -eq '0' ]]; then printf -- '--command --internal --public --test --type' - return + return "$_GO_EC_OK" fi ((--word_index)) @@ -64,27 +64,27 @@ _@go.new_tab_completions() { ;; --internal) if [[ "$word_index" -ne '0' ]] || ! cd "$_GO_SCRIPTS_DIR/lib"; then - return 1 + return "$_GO_EC_GENERR" fi ;; --public) if [[ "$word_index" -ne '0' ]] || ! cd "$_GO_ROOTDIR/lib"; then - return 1 + return "$_GO_EC_GENERR" fi ;; --test) if [[ "$word_index" -ne '0' ]] || ! cd "$_GO_ROOTDIR/$_GO_TEST_DIR"; then - return 1 + return "$_GO_EC_GENERR" fi ;; --type) if [[ "$word_index" -ne '1' ]]; then - return 1 + return "$_GO_EC_GENERR" fi shift ;; *) - return 1 + return "$_GO_EC_USAGE" ;; esac @go.compgen -f -- "$1" @@ -114,25 +114,25 @@ _@go.new_file() { if [[ -z "$file_path" ]]; then @go.printf 'No %sfile path specified.\n' "$file_type" >&2 - return 1 + return "$_GO_EC_ARGERR" elif [[ ! "$permissions" =~ $permissions_pattern ]]; then @go.printf 'Invalid permissions specification "%s" for %sfile: %s\n' \ "$permissions" "$file_type" "$relpath" >&2 - return 1 + return "$_GO_EC_ARGERR" elif [[ ! -d "$parent_dir" ]] && ! mkdir -p "$parent_dir"; then @go.printf "Couldn't create parent directory for new %sfile: %s\n" \ "$file_type" "$relpath" >&2 - return 1 + return "$_GO_EC_CANTCREAT" elif [[ -f "$file_path" ]]; then @go.printf '%sfile already exists: %s\n' "$file_type" "$relpath" >&2 - return 1 + return "$_GO_EC_GENERR" elif ! printf -- '%s\n' "$@" >"$file_path"; then @go.printf 'Failed to create new %sfile: %s\n' "$file_type" "$relpath" >&2 - return 1 + return "$_GO_EC_CANTCREAT" elif ! chmod "$permissions" "$file_path"; then @go.printf 'Failed to set permissions for new %sfile to "%s": %s\n' \ "$file_type" "$permissions" "$relpath" >&2 - return 1 + return "$_GO_EC_NOPERM" fi } @@ -168,7 +168,7 @@ _@go.new_command_scripts() { if [[ "$#" -eq '0' ]]; then printf 'No command script name specified.\n' >&2 - return 1 + return "$_GO_EC_ARGERR" fi for cmd in "$@"; do @@ -182,8 +182,9 @@ _@go.new_command_scripts() { fi new_scripts+=("$cmd_path") - if ! _@go.new_command_script "$cmd" "$cmd_path" "$is_last_cmd"; then - return 1 + _@go.new_command_script "$cmd" "$cmd_path" "$is_last_cmd" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]] ; then + return "$ec" fi done @@ -213,8 +214,9 @@ _@go.new_module() { ;; esac - if ! _@go.new_file "$module_type" "$module_path" '644' "${impl[@]}"; then - return 1 + _@go.new_file "$module_type" "$module_path" '644' "${impl[@]}" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]] ; then + return "$ec" elif command -v "$EDITOR" >/dev/null; then "$EDITOR" "$module_path" fi @@ -248,8 +250,9 @@ _@go.new_test() { '@test "$SUITE: short description of your first test case" {' '}') - if ! _@go.new_file "Bats test" "$test_path" '644' "${impl[@]}"; then - return 1 + _@go.new_file "Bats test" "$test_path" '644' "${impl[@]}" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]] ; then + return "$ec" elif command -v "$EDITOR" >/dev/null; then "$EDITOR" "$test_path" fi @@ -260,7 +263,7 @@ _@go.new() { if [[ "$#" -eq '0' ]]; then @go 'help' "${_GO_CMD_NAME[@]}" >&2 - return 1 + return "$_GO_EC_USAGE" fi shift @@ -283,8 +286,9 @@ _@go.new() { _@go.new_test "$_GO_ROOTDIR/$_GO_TEST_DIR/$1" ;; --type) - if ! _@go.new_file "$1" "$2" "${@:3}"; then - return 1 + _@go.new_file "$1" "$2" "${@:3}" && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]]; then + return "$ec" elif [[ "$#" -le '3' ]] && command -v "$EDITOR" >/dev/null; then "$EDITOR" "$2" fi @@ -292,7 +296,7 @@ _@go.new() { *) printf 'The first argument is "%s", but must be one of:\n %s\n' \ "$mode" '--command --internal --public --test --type' >&2 - return 1 + return "$_GO_EC_USAGE" esac } diff --git a/libexec/path b/libexec/path index 0921ec2..5ba962c 100755 --- a/libexec/path +++ b/libexec/path @@ -21,17 +21,18 @@ _@go.path() { if [[ "$#" -ne '0' ]]; then if _@go.source_builtin 'aliases' --exists "$1"; then echo '[alias]' - return + return "$_GO_EC_OK" elif _@go.source_builtin 'builtins' --exists "$1"; then echo -n '[builtin] ' fi local __go_cmd_path - if _@go.set_command_path_and_argv "$@"; then + _@go.set_command_path_and_argv "$@" && ec=0 || ec="$?" + if [[ "$ec" -eq 0 ]]; then echo "${__go_cmd_path#$_GO_ROOTDIR/}" else - return 1 + return "$ec" fi fi } diff --git a/libexec/plugins b/libexec/plugins index fe09450..d805c3a 100755 --- a/libexec/plugins +++ b/libexec/plugins @@ -55,7 +55,7 @@ _@go.plugins() { # Tab completions _@go.source_builtin 'commands' "$@" "$__go_plugins_pathspec" else - return 1 + return "$_GO_EC_NOTFND" fi } diff --git a/scripts/changes b/scripts/changes index f08d04e..cb149ff 100755 --- a/scripts/changes +++ b/scripts/changes @@ -17,10 +17,13 @@ _changes() { # Tab completions local word_index="$2" if [[ "$word_index" -gt 1 ]]; then - return 1 + return "$_GO_EC_GENERR" fi - git tag - return + git tag && ec=0 || ec="$?" + if [[ "$ec" -ne 0 ]]; then + return "$ec" + fi + return "$_GO_EC_OK" fi local start_ref="$1" @@ -28,10 +31,10 @@ _changes() { if [[ -z "$start_ref" ]]; then echo "Start ref not specified." >&2 - return 1 + return "$_GO_EC_USAGE" elif [[ -z "$end_ref" ]]; then echo "End ref not specified." >&2 - return 1 + return "$_GO_EC_USAGE" fi git log '--pretty=format:%h %an <%ae>%n %s%n' \ diff --git a/tests/vars.bats b/tests/vars.bats index 9eedb0c..3bb3064 100644 --- a/tests/vars.bats +++ b/tests/vars.bats @@ -44,6 +44,25 @@ quotify_expected() { "declare -rx _GO_CORE_URL=\"$_GO_CORE_URL\"" "declare -rx _GO_CORE_VERSION=\"$_GO_CORE_VERSION\"" "declare -x _GO_COVERALLS_URL=\"$_GO_COVERALLS_URL\"" + 'declare -irx _GO_EC_ARGERR="71"' + 'declare -irx _GO_EC_BADFRMT="69"' + 'declare -irx _GO_EC_BASE="64"' + 'declare -irx _GO_EC_CANTCREAT="73"' + 'declare -irx _GO_EC_CONFIG="78"' + 'declare -irx _GO_EC_DEPMISS="72"' + 'declare -irx _GO_EC_EXT1="65"' + 'declare -irx _GO_EC_EXT2="66"' + 'declare -irx _GO_EC_GENERR="64"' + 'declare -irx _GO_EC_IOERR="74"' + 'declare -irx _GO_EC_MAX="78"' + 'declare -irx _GO_EC_NOINPUT="68"' + 'declare -irx _GO_EC_NOPERM="77"' + 'declare -irx _GO_EC_NOTFND="70"' + 'declare -irx _GO_EC_OFFSET="0"' + 'declare -irx _GO_EC_OK="0"' + 'declare -irx _GO_EC_SIGN1="75"' + 'declare -irx _GO_EC_SIGN2="76"' + 'declare -irx _GO_EC_USAGE="67"' 'declare -a _GO_IMPORTED_MODULES=()' 'declare -a _GO_IMPORTED_MODULE_CALLERS=()' 'declare -a _GO_IMPORTED_MODULE_FILES=()' @@ -118,6 +137,25 @@ quotify_expected() { "declare -rx _GO_CORE_URL=\"$_GO_CORE_URL\"" "declare -rx _GO_CORE_VERSION=\"$_GO_CORE_VERSION\"" "declare -x _GO_COVERALLS_URL=\"$_GO_COVERALLS_URL\"" + "declare -irx _GO_EC_ARGERR=\"71\"" + "declare -irx _GO_EC_BADFRMT=\"69\"" + "declare -irx _GO_EC_BASE=\"64\"" + "declare -irx _GO_EC_CANTCREAT=\"73\"" + "declare -irx _GO_EC_CONFIG=\"78\"" + "declare -irx _GO_EC_DEPMISS=\"72\"" + "declare -irx _GO_EC_EXT1=\"65\"" + "declare -irx _GO_EC_EXT2=\"66\"" + "declare -irx _GO_EC_GENERR=\"64\"" + "declare -irx _GO_EC_IOERR=\"74\"" + "declare -irx _GO_EC_MAX=\"78\"" + "declare -irx _GO_EC_NOINPUT=\"68\"" + "declare -irx _GO_EC_NOPERM=\"77\"" + "declare -irx _GO_EC_NOTFND=\"70\"" + "declare -irx _GO_EC_OFFSET=\"0\"" + "declare -irx _GO_EC_OK=\"0\"" + "declare -irx _GO_EC_SIGN1=\"75\"" + "declare -irx _GO_EC_SIGN2=\"76\"" + "declare -irx _GO_EC_USAGE=\"67\"" "declare -a _GO_IMPORTED_MODULES=(${expected_modules[*]})" "declare -a _GO_IMPORTED_MODULE_CALLERS=(${expected_module_callers[*]})" "declare -a _GO_IMPORTED_MODULE_FILES=(${expected_module_files[*]})" @@ -168,6 +206,25 @@ quotify_expected() { "_GO_CORE_URL: $_GO_CORE_URL" \ "_GO_CORE_VERSION: $_GO_CORE_VERSION" \ "_GO_COVERALLS_URL: $_GO_COVERALLS_URL" \ + "_GO_EC_ARGERR: $_GO_EC_ARGERR" \ + "_GO_EC_BADFRMT: $_GO_EC_BADFRMT" \ + "_GO_EC_BASE: $_GO_EC_BASE" \ + "_GO_EC_CANTCREAT: $_GO_EC_CANTCREAT" \ + "_GO_EC_CONFIG: $_GO_EC_CONFIG" \ + "_GO_EC_DEPMISS: $_GO_EC_DEPMISS" \ + "_GO_EC_EXT1: $_GO_EC_EXT1" \ + "_GO_EC_EXT2: $_GO_EC_EXT2" \ + "_GO_EC_GENERR: $_GO_EC_GENERR" \ + "_GO_EC_IOERR: $_GO_EC_IOERR" \ + "_GO_EC_MAX: $_GO_EC_MAX" \ + "_GO_EC_NOINPUT: $_GO_EC_NOINPUT" \ + "_GO_EC_NOPERM: $_GO_EC_NOPERM" \ + "_GO_EC_NOTFND: $_GO_EC_NOTFND" \ + "_GO_EC_OFFSET: $_GO_EC_OK" \ + "_GO_EC_OK: $_GO_EC_OK" \ + "_GO_EC_SIGN1: $_GO_EC_SIGN1" \ + "_GO_EC_SIGN2: $_GO_EC_SIGN2" \ + "_GO_EC_USAGE: $_GO_EC_USAGE" \ "_GO_KCOV_DIR: $_GO_KCOV_DIR" \ "_GO_ROOTDIR: $TEST_GO_ROOTDIR" \ "_GO_SCRIPT: $TEST_GO_SCRIPT" \