aboutsummaryrefslogtreecommitdiff
#!/bin/sh
set -eu

MSGS=''
add_msg() {
	MSGS="$MSGS $1"
}


MSG_USAGE="$(cat <<-'EOF'
	Usage:  git permalink [-phV] FILE [LINENO]
EOF
)"
add_msg USAGE

MSG_HELP="$(cat <<-'EOF'
	Options:
	  -p             only print the link, don't try to open it
	  -h, --help     show this help message
	  -V, --version  print the version number

	See "man git-permalink" for more information.
EOF
)"
add_msg HELP

MSG_MISSING_FILE="$(cat <<-'EOF'
	Missing FILE argument.
EOF
)"
add_msg MISSING_FILE

MSG_UNSUPPORTED_ORIGIN="$(cat <<-'EOF'
	Unsupported origin: %s.

	Add a template override to use git-permalink (see
	"man git-permalink" for instructions).
EOF
)"
add_msg UNSUPPORTED_ORIGIN

MSG_OPEN="$(cat <<-'EOF'
	Opening %s
EOF
)"
add_msg OPEN

#
# End translatable strings
#



dump_translatable_strings() {
	for msg in $MSGS; do
		eval "echo \"\$MSG_$msg\"" > src/locale/"$msg".en.txt
	done

	cat <<-'EOF'
		#!/bin/sh
		# shellcheck disable=2034
		set -eu

	EOF
	for msg in $MSGS; do
		# shellcheck disable=2016
		printf "MSG_%s=\"\$(cat '@LOCALEDIR@/@LANG@/LC_MESSAGES/@NAME@/%s.@LANG@.txt')\"\n" \
			"$msg" "$msg"
	done
}

if [ -n "${GIT_PERMALINK_DUMP_TRANSLATABLE_STRINGS:-}" ]; then
	dump_translatable_strings
	exit
fi


if [ -r '@LIBEXECDIR@/@NAME@/load-messages.sh' ]; then
	# shellcheck disable=1091
	. '@LIBEXECDIR@/@NAME@/load-messages.sh'
fi



#
# Documentation functions
#

usage() {
	printf '%s\n' "$MSG_USAGE"
}

help() {
	printf '\n%s\n' "$MSG_HELP"
}

version() {
	printf 'git-permalink-@VERSION@ @DATE@\n'
}



#
# Core functions
#

normalize_origin() {
	if echo "$ORIGIN" | grep -q '^git@'; then
		NAME="$(echo "$ORIGIN" | cut -d: -f2 | cut -d/ -f1)"
		URL="$(echo "$ORIGIN" | cut -d@ -f2 | cut -d: -f1)"
		ORIGIN="https://$URL/$NAME/$REPOSITORY"
	fi
}

lineno_with_l() {
	if echo "$MYLINENO" | grep -q -- -; then
		P1="$(echo "$MYLINENO" | cut -d- -f1)"
		P2="$(echo "$MYLINENO" | cut -d- -f2)"
		printf '#L%s-L%s' "$P1" "$P2"
	elif [ -n "$MYLINENO" ]; then
		printf '#L%s' "${MYLINENO}"
	else
		printf ''
	fi
}

euandreh() {
	printf 'https://euandreh.xyz/%s.git/tree/%s?id=%s%s\n' \
		"$REPOSITORY" "$FILE" "$COMMIT" "${MYLINENO:+#n$MYLINENO}"
}

sourcehut() {
	printf '%s/tree/%s/item/%s%s\n' \
		"$ORIGIN" "$COMMIT" "$FILE" "${MYLINENO:+#L$MYLINENO}"
}

kernel() {
	printf '%s/tree/%s?id=%s%s\n' \
		"$ORIGIN" "$FILE" "$COMMIT" "${MYLINENO:+#n$MYLINENO}"
}

savannah() {
	ORIGIN="$(echo "$ORIGIN" | sed 's|gnu.org/git|gnu.org/cgit|')"
	printf '%s/tree/%s?id=%s%s\n' \
		"$ORIGIN" "$FILE" "$COMMIT" "${MYLINENO:+#n$MYLINENO}"
}

notabug() {
	normalize_origin
	printf '%s/src/%s/%s%s\n' "$ORIGIN" "$COMMIT" "$FILE" "$(lineno_with_l)"
}

codeberg() {
	normalize_origin
	printf '%s/src/commit/%s/%s%s\n' \
		"$ORIGIN" "$COMMIT" "$FILE" "$(lineno_with_l)"
}

bitbucket() {
	normalize_origin
        MYLINENO="${MYLINENO:+#lines-$(echo "$MYLINENO" | tr '-' ':')}"
	printf '%s/src/%s/%s%s\n' \
		"$ORIGIN" "$COMMIT" "$FILE" "$MYLINENO"
}

pagure() {
	printf '%s/blob/%s/f/%s%s\n' \
		"$ORIGIN" "$COMMIT" "$FILE" "${MYLINENO:+#_$MYLINENO}"
}

gitlab() {
	normalize_origin
	printf '%s/-/blob/%s/%s%s\n' \
		"$ORIGIN" "$COMMIT" "$FILE" "${MYLINENO:+#L$MYLINENO}"
}

github() {
	normalize_origin
	printf '%s/blob/%s/%s%s\n' \
		"$ORIGIN" "$COMMIT" "$FILE" "$(lineno_with_l)"
}

guess_permalink() {
	if [ -n "$OVERRIDE_CF" ]; then
		# shellcheck disable=2059
		printf "$OVERRIDE_CF\n" "$COMMIT" "$FILE"
	elif [ -n "$OVERRIDE_FC" ]; then
		# shellcheck disable=2059
		printf "$OVERRIDE_FC\n" "$FILE" "$COMMIT"
	else
		case "$ORIGIN" in
			*euandreh.xyz*)
				euandreh
				;;
			*git.sr.ht*)
				sourcehut
				;;
			*git.kernel.org*)
				kernel
				;;
			*git.savannah.gnu.org*)
				savannah
				;;
			*notabug.org*)
				notabug
				;;
			*codeberg.org*)
				codeberg
				;;
			*bitbucket.org*)
				bitbucket
				;;
			*pagure.io*)
				pagure
				;;
			*gitlab.com*)
				gitlab
				;;
			*github.com*)
				github
				;;
			*)
				# shellcheck disable=2059
				printf "$MSG_UNSUPPORTED_ORIGIN\n" "$ORIGIN" >&2
				exit 1
				;;
		esac
	fi
}



#
# Main
#

for flag in "$@"; do
	case "$flag" in
		--)
			break
			;;
		--help)
			usage
			help
			exit
			;;
		--version)
			version
			exit
			;;
		*)
			;;
	esac
done

PRINTONLY=false
while getopts 'phV' flag; do
	case "$flag" in
		p)
			PRINTONLY=true
			;;
		h)
			usage
			help
			exit
			;;
		V)
			version
			exit
			;;
		*)
			usage >&2
			exit 2
			;;
	esac
done
shift $((OPTIND - 1))

if [ -z "${1:-}" ]; then
	printf '%s\n\n' "$MSG_MISSING_FILE" >&2
	usage >&2
	exit 2
fi

FILE="$1"
MYLINENO="${2:-}"
COMMIT="$(git rev-parse HEAD)"
ORIGIN="$(git config remote.origin.url)"
OVERRIDE_CF="$(git config git-permalink.template-commit-file ||:)"
OVERRIDE_FC="$(git config git-permalink.template-file-commit ||:)"
REPOSITORY="$(basename "$PWD")"

LINK="$(guess_permalink)"
if [ "$PRINTONLY" = true ]; then
	echo "$LINK"
else
	# shellcheck disable=2059
	printf "$MSG_OPEN\n" "$LINK" >&2
	xdg-open "$LINK"
fi