Codify the canonical C style and add reindent tooling
AGENTS.md described style in one paragraph that said little more than
"follow the surrounding code", which was not actionable once files had
drifted apart. Replace it with an explicit specification.
The canonical style is Emacs cc-mode "stroustrup" with tabs enabled: a
4-column offset with 8-column tabs, so depth 1 is four spaces, depth 2 is
one tab, depth 3 is a tab plus four spaces. Most of src/ already followed
this; what looked like randomly mixed tabs and spaces was simply cc-mode
output. Document the byte ladder explicitly, since editors that expand
tabs or assume a 4-column tab silently corrupt it.
Rules beyond indentation are derived from counted majorities in the
existing code rather than invented: errctx over e (92 to 45), padded
control parens (140 to 7), pointer binding to the identifier (486 to 5),
and always-brace (only four unbraced bodies exist). Brace and else
placement are called out as house conventions that cc-mode does not
enforce, so "run the indenter" and "follow the guide" cannot conflict.
Also record the naming, error-handling, and API-surface rules that a
consistency sweep showed were being broken silently -- in particular that
a *_RETURN macro inside an ATTEMPT block skips CLEANUP.
Add the tooling to apply it:
.dir-locals.el applies the style to every c-mode buffer
scripts/reindent.el batch reindent via Emacs
scripts/reindent.sh reindent or --check the tree
scripts/hooks/pre-commit reindents staged sources
reindent.el deliberately avoids Emacs' tabify: its tabify-regexp is
" [ \t]+", which is not anchored to the start of a line, so it rewrites
runs of spaces anywhere and destroys the hand-aligned value columns in
the bit-flag tables in actor.h and iterator.h. Only leading whitespace is
ever rewritten, and lines starting inside a string literal are skipped.
The hook checks staged content rather than the working tree, so what is
committed is what was verified. It re-stages a fixed file only when the
index and working tree agree, otherwise a partial `git add -p` would
sweep unstaged work into the commit.
Enable with: git config core.hooksPath scripts/hooks
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:17:26 -04:00
|
|
|
#!/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.
|
2026-08-01 20:57:37 -04:00
|
|
|
#
|
|
|
|
|
# `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'
|
Codify the canonical C style and add reindent tooling
AGENTS.md described style in one paragraph that said little more than
"follow the surrounding code", which was not actionable once files had
drifted apart. Replace it with an explicit specification.
The canonical style is Emacs cc-mode "stroustrup" with tabs enabled: a
4-column offset with 8-column tabs, so depth 1 is four spaces, depth 2 is
one tab, depth 3 is a tab plus four spaces. Most of src/ already followed
this; what looked like randomly mixed tabs and spaces was simply cc-mode
output. Document the byte ladder explicitly, since editors that expand
tabs or assume a 4-column tab silently corrupt it.
Rules beyond indentation are derived from counted majorities in the
existing code rather than invented: errctx over e (92 to 45), padded
control parens (140 to 7), pointer binding to the identifier (486 to 5),
and always-brace (only four unbraced bodies exist). Brace and else
placement are called out as house conventions that cc-mode does not
enforce, so "run the indenter" and "follow the guide" cannot conflict.
Also record the naming, error-handling, and API-surface rules that a
consistency sweep showed were being broken silently -- in particular that
a *_RETURN macro inside an ATTEMPT block skips CLEANUP.
Add the tooling to apply it:
.dir-locals.el applies the style to every c-mode buffer
scripts/reindent.el batch reindent via Emacs
scripts/reindent.sh reindent or --check the tree
scripts/hooks/pre-commit reindents staged sources
reindent.el deliberately avoids Emacs' tabify: its tabify-regexp is
" [ \t]+", which is not anchored to the start of a line, so it rewrites
runs of spaces anywhere and destroys the hand-aligned value columns in
the bit-flag tables in actor.h and iterator.h. Only leading whitespace is
ever rewritten, and lines starting inside a string literal are skipped.
The hook checks staged content rather than the working tree, so what is
committed is what was verified. It re-stages a fixed file only when the
index and working tree agree, otherwise a partial `git add -p` would
sweep unstaged work into the commit.
Enable with: git config core.hooksPath scripts/hooks
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:17:26 -04:00
|
|
|
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
|
|
|
|
|
|
2026-07-30 18:22:47 -04:00
|
|
|
# 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
|
|
|
|
|
}
|
|
|
|
|
|
Codify the canonical C style and add reindent tooling
AGENTS.md described style in one paragraph that said little more than
"follow the surrounding code", which was not actionable once files had
drifted apart. Replace it with an explicit specification.
The canonical style is Emacs cc-mode "stroustrup" with tabs enabled: a
4-column offset with 8-column tabs, so depth 1 is four spaces, depth 2 is
one tab, depth 3 is a tab plus four spaces. Most of src/ already followed
this; what looked like randomly mixed tabs and spaces was simply cc-mode
output. Document the byte ladder explicitly, since editors that expand
tabs or assume a 4-column tab silently corrupt it.
Rules beyond indentation are derived from counted majorities in the
existing code rather than invented: errctx over e (92 to 45), padded
control parens (140 to 7), pointer binding to the identifier (486 to 5),
and always-brace (only four unbraced bodies exist). Brace and else
placement are called out as house conventions that cc-mode does not
enforce, so "run the indenter" and "follow the guide" cannot conflict.
Also record the naming, error-handling, and API-surface rules that a
consistency sweep showed were being broken silently -- in particular that
a *_RETURN macro inside an ATTEMPT block skips CLEANUP.
Add the tooling to apply it:
.dir-locals.el applies the style to every c-mode buffer
scripts/reindent.el batch reindent via Emacs
scripts/reindent.sh reindent or --check the tree
scripts/hooks/pre-commit reindents staged sources
reindent.el deliberately avoids Emacs' tabify: its tabify-regexp is
" [ \t]+", which is not anchored to the start of a line, so it rewrites
runs of spaces anywhere and destroys the hand-aligned value columns in
the bit-flag tables in actor.h and iterator.h. Only leading whitespace is
ever rewritten, and lines starting inside a string literal are skipped.
The hook checks staged content rather than the working tree, so what is
committed is what was verified. It re-stages a fixed file only when the
index and working tree agree, otherwise a partial `git add -p` would
sweep unstaged work into the commit.
Enable with: git config core.hooksPath scripts/hooks
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:17:26 -04:00
|
|
|
copies=''
|
|
|
|
|
for f in $files; do
|
|
|
|
|
dest="$tmp/$(printf '%s' "$f" | tr '/' '_')"
|
2026-07-30 18:22:47 -04:00
|
|
|
cp "$(abspath "$f")" "$dest"
|
Codify the canonical C style and add reindent tooling
AGENTS.md described style in one paragraph that said little more than
"follow the surrounding code", which was not actionable once files had
drifted apart. Replace it with an explicit specification.
The canonical style is Emacs cc-mode "stroustrup" with tabs enabled: a
4-column offset with 8-column tabs, so depth 1 is four spaces, depth 2 is
one tab, depth 3 is a tab plus four spaces. Most of src/ already followed
this; what looked like randomly mixed tabs and spaces was simply cc-mode
output. Document the byte ladder explicitly, since editors that expand
tabs or assume a 4-column tab silently corrupt it.
Rules beyond indentation are derived from counted majorities in the
existing code rather than invented: errctx over e (92 to 45), padded
control parens (140 to 7), pointer binding to the identifier (486 to 5),
and always-brace (only four unbraced bodies exist). Brace and else
placement are called out as house conventions that cc-mode does not
enforce, so "run the indenter" and "follow the guide" cannot conflict.
Also record the naming, error-handling, and API-surface rules that a
consistency sweep showed were being broken silently -- in particular that
a *_RETURN macro inside an ATTEMPT block skips CLEANUP.
Add the tooling to apply it:
.dir-locals.el applies the style to every c-mode buffer
scripts/reindent.el batch reindent via Emacs
scripts/reindent.sh reindent or --check the tree
scripts/hooks/pre-commit reindents staged sources
reindent.el deliberately avoids Emacs' tabify: its tabify-regexp is
" [ \t]+", which is not anchored to the start of a line, so it rewrites
runs of spaces anywhere and destroys the hand-aligned value columns in
the bit-flag tables in actor.h and iterator.h. Only leading whitespace is
ever rewritten, and lines starting inside a string literal are skipped.
The hook checks staged content rather than the working tree, so what is
committed is what was verified. It re-stages a fixed file only when the
index and working tree agree, otherwise a partial `git add -p` would
sweep unstaged work into the commit.
Enable with: git config core.hooksPath scripts/hooks
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:17:26 -04:00
|
|
|
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 '/' '_')"
|
2026-07-30 18:22:47 -04:00
|
|
|
if ! cmp -s "$(abspath "$f")" "$dest"; then
|
Codify the canonical C style and add reindent tooling
AGENTS.md described style in one paragraph that said little more than
"follow the surrounding code", which was not actionable once files had
drifted apart. Replace it with an explicit specification.
The canonical style is Emacs cc-mode "stroustrup" with tabs enabled: a
4-column offset with 8-column tabs, so depth 1 is four spaces, depth 2 is
one tab, depth 3 is a tab plus four spaces. Most of src/ already followed
this; what looked like randomly mixed tabs and spaces was simply cc-mode
output. Document the byte ladder explicitly, since editors that expand
tabs or assume a 4-column tab silently corrupt it.
Rules beyond indentation are derived from counted majorities in the
existing code rather than invented: errctx over e (92 to 45), padded
control parens (140 to 7), pointer binding to the identifier (486 to 5),
and always-brace (only four unbraced bodies exist). Brace and else
placement are called out as house conventions that cc-mode does not
enforce, so "run the indenter" and "follow the guide" cannot conflict.
Also record the naming, error-handling, and API-surface rules that a
consistency sweep showed were being broken silently -- in particular that
a *_RETURN macro inside an ATTEMPT block skips CLEANUP.
Add the tooling to apply it:
.dir-locals.el applies the style to every c-mode buffer
scripts/reindent.el batch reindent via Emacs
scripts/reindent.sh reindent or --check the tree
scripts/hooks/pre-commit reindents staged sources
reindent.el deliberately avoids Emacs' tabify: its tabify-regexp is
" [ \t]+", which is not anchored to the start of a line, so it rewrites
runs of spaces anywhere and destroys the hand-aligned value columns in
the bit-flag tables in actor.h and iterator.h. Only leading whitespace is
ever rewritten, and lines starting inside a string literal are skipped.
The hook checks staged content rather than the working tree, so what is
committed is what was verified. It re-stages a fixed file only when the
index and working tree agree, otherwise a partial `git add -p` would
sweep unstaged work into the commit.
Enable with: git config core.hooksPath scripts/hooks
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:17:26 -04:00
|
|
|
echo "$f"
|
|
|
|
|
status=1
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
exit $status
|