#!/bin/sh # # Reindent C sources to the project's canonical style (cc-mode "stroustrup", # 4-column offset, 8-column tabs). See AGENTS.md -> "Coding Style". # # scripts/reindent.sh reindent every tracked C source in place # scripts/reindent.sh FILE... reindent only the named files # scripts/reindent.sh --check ... report non-conforming files, change nothing # # Exit status: 0 if everything already conforms (or was reindented), 1 if # --check found a file that needs reindenting, 2 on a usage or environment # error. set -eu root=$(git rev-parse --show-toplevel) elisp="$root/scripts/reindent.el" # Directories whose C sources are hand-maintained. Anything outside these is # vendored (deps/) or generated (include/akgl/SDL_GameControllerDB.h) and is # left alone. # # `tools` and `examples` joined the list with the documentation harness. Both # hold ordinary hand-written C -- the doc-example validators and the two # tutorial games -- and while they were outside the scope neither `--check` nor # the pre-commit hook could see them, so the one thing that keeps this tree a # fixed point of `indent-region` was not being applied to its newest sources. SCOPE='src include tests util tools examples' GENERATED='include/akgl/SDL_GameControllerDB.h' check=0 if [ "${1:-}" = "--check" ]; then check=1 shift fi if ! command -v emacs >/dev/null 2>&1; then echo "reindent: emacs not found; cannot verify or apply the canonical style" >&2 exit 2 fi if [ ! -f "$elisp" ]; then echo "reindent: missing $elisp" >&2 exit 2 fi # Build the file list: explicit arguments, or every tracked C source in scope. if [ "$#" -gt 0 ]; then files=$(for f in "$@"; do printf '%s\n' "$f"; done) else files=$(cd "$root" && git ls-files $SCOPE | grep -E '\.[ch]$' || true) fi # Drop generated files and anything that no longer exists on disk. files=$(printf '%s\n' "$files" | grep -v -x -F "$GENERATED" || true) files=$(cd "$root" && for f in $files; do [ -f "$f" ] && printf '%s\n' "$f"; done) if [ -z "$files" ]; then exit 0 fi if [ "$check" -eq 0 ]; then # Reindent in place. A non-zero status here is a real failure -- never # swallow it, or a broken Emacs would look like "everything already conforms". # shellcheck disable=SC2086 (cd "$root" && emacs --batch -Q -l "$elisp" -- $files) || { echo "reindent: emacs failed; no files were reindented" >&2 exit 2 } exit 0 fi # --check: reindent throwaway copies and report which originals differ. The # copies keep their extension so cc-mode still selects the right major mode. tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT INT TERM # Accept both repo-relative and absolute paths: the default file list is # relative, but callers (including the pre-commit hook) pass absolute ones. abspath() { case "$1" in /*) printf '%s' "$1" ;; *) printf '%s/%s' "$root" "$1" ;; esac } copies='' for f in $files; do dest="$tmp/$(printf '%s' "$f" | tr '/' '_')" cp "$(abspath "$f")" "$dest" copies="$copies $dest" done # shellcheck disable=SC2086 emacs --batch -Q -l "$elisp" -- $copies >/dev/null || { echo "reindent: emacs failed; cannot determine whether sources conform" >&2 exit 2 } status=0 for f in $files; do dest="$tmp/$(printf '%s' "$f" | tr '/' '_')" if ! cmp -s "$(abspath "$f")" "$dest"; then echo "$f" status=1 fi done exit $status