aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xcosu196
-rw-r--r--cosu.17
2 files changed, 93 insertions, 110 deletions
diff --git a/cosu b/cosu
index 454481b..4adf443 100755
--- a/cosu
+++ b/cosu
@@ -1,8 +1,6 @@
-#!/bin/bash
+#!/bin/sh
-set -e
-
-VERSION="1.0.0"
+VERSION="1.1.0"
usage() {
cat <<EOF
@@ -12,6 +10,7 @@ Conditionally execute a command with sudo based on file permissions.
OPTIONS:
-h, --help Show this help message
+ --version Show version information
FLAGS (before each path):
-r, --read Check read permission
@@ -34,122 +33,103 @@ EOF
}
needs_sudo=0
-declare -a checks
-
-parse_args() {
- local current_flags=()
-
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --help)
- usage
- ;;
- --)
- shift
- if [[ $# -eq 0 ]]; then
- echo "Error: No command specified after '--'" >&2
- exit 1
- fi
- command_args=("$@")
- return
- ;;
- --read)
- current_flags+=("r")
- shift
- ;;
- --write)
- current_flags+=("w")
- shift
- ;;
- --execute)
- current_flags+=("x")
- shift
- ;;
- -*)
- local flags="${1#-}"
- if [[ ! "$flags" =~ ^[rwxh]+$ ]]; then
- echo "Error: Unknown flag '$1'" >&2
- echo "Use -h or --help for usage information" >&2
- exit 1
- fi
-
- if [[ "$flags" == *h* ]]; then
- usage
- fi
-
- local i
- for (( i=0; i<${#flags}; i++ )); do
- local flag="${flags:$i:1}"
- case "$flag" in
- r|w|x)
- current_flags+=("$flag")
- ;;
- esac
- done
- shift
- ;;
- *)
- if [[ ${#current_flags[@]} -eq 0 ]]; then
- echo "Error: Path '$1' specified without permission flags" >&2
- echo "Use -h or --help for usage information" >&2
- exit 1
- fi
-
- local path="$1"
- for flag in "${current_flags[@]}"; do
- checks+=("$flag:$path")
- done
- current_flags=()
- shift
- ;;
+current_flags=""
+found_sep=0
+
+expand_short_flags() {
+ f="$1"
+ while [ -n "$f" ]; do
+ c="${f%"${f#?}"}"
+ f="${f#?}"
+ case "$c" in
+ h) usage ;;
+ r|w|x) current_flags="${current_flags}${c}" ;;
esac
done
-
- echo "Error: No '--' separator found" >&2
- echo "Use -h or --help for usage information" >&2
- exit 1
}
-check_permissions() {
- for check in "${checks[@]}"; do
- local perm="${check%%:*}"
- local path="${check#*:}"
-
- case "$perm" in
- r)
- if [[ ! -r "$path" ]]; then
- needs_sudo=1
- return
- fi
- ;;
- w)
- if [[ ! -w "$path" ]]; then
- needs_sudo=1
- return
- fi
- ;;
- x)
- if [[ ! -x "$path" ]]; then
- needs_sudo=1
- return
- fi
- ;;
+check_flags_on_path() {
+ f="$1"
+ path="$2"
+ while [ -n "$f" ]; do
+ c="${f%"${f#?}"}"
+ f="${f#?}"
+ case "$c" in
+ r) if [ ! -r "$path" ]; then needs_sudo=1; fi ;;
+ w) if [ ! -w "$path" ]; then needs_sudo=1; fi ;;
+ x) if [ ! -x "$path" ]; then needs_sudo=1; fi ;;
esac
done
}
-declare -a command_args
-
-if [[ $# -eq 0 ]]; then
+if [ $# -eq 0 ]; then
usage
fi
-parse_args "$@"
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --help)
+ usage
+ ;;
+ --version)
+ echo "cosu $VERSION"
+ exit 0
+ ;;
+ --read)
+ current_flags="${current_flags}r"
+ shift
+ ;;
+ --write)
+ current_flags="${current_flags}w"
+ shift
+ ;;
+ --execute)
+ current_flags="${current_flags}x"
+ shift
+ ;;
+ --)
+ shift
+ found_sep=1
+ break
+ ;;
+ -*)
+ flags="${1#-}"
+ case "$flags" in
+ *[!rwxh]*)
+ echo "Error: Unknown flag '$1'" >&2
+ echo "Use -h or --help for usage information" >&2
+ exit 1
+ ;;
+ esac
+ expand_short_flags "$flags"
+ shift
+ ;;
+ *)
+ if [ -z "$current_flags" ]; then
+ echo "Error: Path '$1' specified without permission flags" >&2
+ echo "Use -h or --help for usage information" >&2
+ exit 1
+ fi
+ check_flags_on_path "$current_flags" "$1"
+ current_flags=""
+ shift
+ ;;
+ esac
+done
+
+if [ "$found_sep" -eq 0 ]; then
+ echo "Error: No '--' separator found" >&2
+ echo "Use -h or --help for usage information" >&2
+ exit 1
+fi
-check_permissions
+if [ $# -eq 0 ]; then
+ echo "Error: No command specified after '--'" >&2
+ exit 1
+fi
-if [[ $needs_sudo -eq 1 ]]; then
- exec sudo "${command_args[@]}"
+if [ "$needs_sudo" -eq 1 ]; then
+ exec sudo "$@"
else
- exec "${command_args[@]}"
+ exec "$@"
fi
diff --git a/cosu.1 b/cosu.1
index 5b9ee0b..adf240e 100644
--- a/cosu.1
+++ b/cosu.1
@@ -1,4 +1,4 @@
-.TH COSU 1 "February 2026" "cosu 1.0.0" "User Commands"
+.TH COSU 1 "February 2026" "cosu 1.1.0" "User Commands"
.SH NAME
cosu \- conditionally execute commands with sudo based on file permissions
.SH SYNOPSIS
@@ -18,6 +18,9 @@ If all checks pass, the command runs normally without privilege escalation.
.TP
.BR \-h ", " \-\-help
Display help message and exit.
+.TP
+.B \-\-version
+Print version information and exit.
.SH FLAGS
The following flags specify which permissions to check on the subsequent path.
Multiple flags can be specified before a single path. Short flags can be
@@ -159,4 +162,4 @@ There is NO WARRANTY, to the extent permitted by law.
.SH SEE ALSO
.BR sudo (8),
.BR test (1),
-.BR bash (1)
+.BR sh (1)