#!/bin/bash
# Update tags file. Written by Laszlo Ersek <lersek@redhat.com>. Public domain.
#
# This script is to be invoked by git hooks. These hooks are run by git in the
# root directory of the repository; this script relies on that. If the user is
# careful enough, the script can be run manually.
#
# The script collects "relevant" source files that git (or the user) has
# changed (as in, ctime) since the script last updated the tags file. The
# script excludes the tags belonging to the changed files from the old tags
# file, then it merges the remaining tags with the tags that it (re)generates
# for the changed files. Tags belonging to deleted files are not removed.
#
# The tags file is kept sorted by relative pathname. The list of changed files
# is also sorted by relative pathname. This way exclusion and merging is linear
# in the number of tags plus changed files, at the price of having to sort the
# list of changed files (O(n log n)). You should remove your current tags file
# before running this script for the first time.
#
# The script relies on the "sponge" utility from the "moreutils" package,
# because merging produces output before all inputs are read to end. Yet
# another temporary file could have worked in "sponge"'s place.
#
# Some function-like macros are already identified as such for ctags.
set -e -u -C -o pipefail
unset CTAGS LANG || true
export LC_ALL=POSIX
BNAME="$(basename -- "$0")"
TMPSTEM="${TMPDIR:-/tmp}/$BNAME.$$"
trap 'rm -f -- "$TMPSTEM".list "$TMPSTEM".tags' EXIT
if test -e tags; then
printf -- '%s: partial tags update\n' "$BNAME" >&2
FIND_RESTR='-cnewer tags'
OLDTAGS=tags
else
printf -- '%s: full tags update\n' "$BNAME" >&2
FIND_RESTR=
OLDTAGS=/dev/null
fi
ARCHES='i386|ia64|x86_64|x86'
find . -regextype posix-extended \
-type d \( \
-regex '\./(\.git|\.hg|Documentation)' \
-o -regex '\./arch/[^/]+' \
! -regex '\./arch/('"$ARCHES"')' \
-o -regex '\./include/asm-[^/]+' \
! -regex '\./include/asm-('"$ARCHES"'|generic)' \
\) -prune \
-o $FIND_RESTR -type f -regex '[^-[:space:]][^[:space:]]*\.[chS]' \
-print \
| sort \
| tee -- "$TMPSTEM".list \
| ctags --filter --sort=no --langmap=c:.c.h -I EXPORT_SYMBOL+ \
-I DECLARE_TASKLET+ -I module_init+ -I MODULE_LICENSE+ \
-I EXPORT_SYMBOL_GPL+ > "$TMPSTEM".tags
# "--nocheck-order" could have been passed to "join" too
join -1 1 -2 2 -t $'\t' -v 2 -- "$TMPSTEM".list "$OLDTAGS" \
| sort -t $'\t' --merge --stable -k 2,2 -- - "$TMPSTEM".tags \
| sponge tags