-
Notifications
You must be signed in to change notification settings - Fork 35
update copyright.sh
Martijn Dekker edited this page Apr 25, 2025
·
2 revisions
This script helps maintain proper copyright attribution in the ksh 93u+m code base. It:
- Updates the COPYRIGHT file with all the contributors to ksh 93u+m since the reboot, based on the commit history. The authors are sorted by number of commits in descending order and inserted under the first CONTRIBUTORS header in that file. Authors already listed there but who have no commits are sorted at the bottom.
- Updates the ksh 93u+m
(c) YEAR-YEAR Contributors to ksh 93u+m
notices in the copyright headers and--about
messages. The second year is updated to the current year. - Adds any missing contributors to each file to that file's copyright header based on that file's commit history.
The script does not stage or commit the changes it makes. 2 and 3 are only done for files that have changed since the new year, including files that have uncommitted or unstaged changes.
set -fCu; IFS='' # safe mode
PATH=/opt/ast/bin:$PATH # use path-bound built-ins
error_out()
{
echo "$0: $@" >&2
exit 1
}
PATH=/dev/null let ".sh.version >= 20220728" 2>/dev/null || error_out 'requires ksh 93u+m/1.0'
# special-case github's flukes in names of commit authors
# (they were fine in the original commits, then merging/squashing changed them)
function fix_authorname
{
nameref a=$1
case $a in
lev105) a='Lev Kujawski' ;;
pghvlaans) a='K. Eugene Carlson' ;;
sterlingjensen) a='Sterling Jensen' ;;
vmihalko) a='Vincent Mihalkovic' ;;
esac
}
# update file if it changed from $tmpfile
function update_file
{
[[ -s $1 ]] && cmp -s $1 $tmpfile
case $? in
0) ;;
1) print -r "updating $1"
cat $tmpfile >|$1 || exit ;;
*) error_out "error in cmp" ;;
esac
}
# centre an author line to 70 characters
function centre_line
{
nameref l=$1
((${#l} > 70)) && prinf '%q: WARNING: author line too long: %q\n' "$0" "$l" >&2
while ((${#l} < 69)); do
l=" $l "
done
((${#l}==69)) && l+=' '
}
[[ -d .git ]] || error_out "run this from the ksh 93u+m git repo's main directory"
git_branch=$(git branch --show-current)
[[ -n $git_branch ]] || error_out "could not get current git branch"
current_year=$(date +%Y)
[[ $current_year =~ ^[0-9]{4}$ ]] || error_out "could not get current year"
first_commit=$(git log --since="$current_year-01-01 00:00 UTC" --pretty=format:'%H' | tail -n1)
[[ $first_commit =~ ^[0-9a-f]{40}$ ]] || error_out "could not get the first commit of $current_year"
current_commit=$(git log -1 --pretty=format:'%H')
[[ $current_commit =~ ^[0-9a-f]{40}$ ]] || error_out "could not get current commit"
# get author/email associations from log, then override them with those specified in COPYRIGHT
typeset -A email
git log reboot..$git_branch '--pretty=tformat:%ae %an' | while IFS=' ' read e n; do
fix_authorname n
email[$n]=$e
done
set_email=$(awk '{
if(!state && $0 ~ /CONTRIBUTORS/)
state=1;
else if(state==1)
{
spacingline=$0;
state=2;
}
else if(state==2)
{
if($0==spacingline)
exit;
sub(/^#[[:blank:]]*/, "email['\''");
sub(/ </, "'\'']='\''");
sub(/>.*/, "'\''");
print;
}
}' COPYRIGHT) || exit
eval "$set_email" || exit
unset set_email
tmpfile=${TMPDIR:-/tmp}/update-copyright-years.${$}${RANDOM}
trap 'exec rm -f $tmpfile' EXIT
# update COPYRIGHT file
# get all the authors from the git log, sorted by number of commits in descending order
# (awk cannot portably pass strings with newlines via -v options, so pass as an env var)
export contributors=$(
{
git log reboot..$git_branch '--pretty=tformat:%an'
printf '%s\n' "${!email[@]}" # keep existing names from COPYRIGHT even if they have no commits
} | while read n; do
fix_authorname n
print -r $n
done | sort | uniq -c | sort -rn | while IFS=' ' read throwaway_number name; do
c="$name <${email[$name]:-no@email.invalid}>"
centre_line c
print -r "#${c}#"
done
)
awk -v "current_year=$current_year" '{
if(!state)
{
sub(/2020-.... Contributors to ksh 93u\+m/, "2020-" current_year " Contributors to ksh 93u+m");
print;
if($0 ~ /CONTRIBUTORS/)
state=1;
}
else if(state==1)
{
print;
spacingline=$0;
state=2;
}
else if(state==2)
{
print ENVIRON["contributors"];
state=3;
}
else if(state==3)
{
if($0==spacingline)
{
print;
state=4;
}
}
else
print;
}' COPYRIGHT >|$tmpfile
unset contributors
update_file COPYRIGHT
# update headers in source files
(git diff --name-only; git diff --name-only --cached; git diff-tree --name-only -r $first_commit..HEAD) \
| while read -r file
do
[[ -f $file ]] || continue
read -r n <$file
[[ $n == '/***********************************************************************' \
|| $n == '########################################################################' ]] \
|| continue
sedscript=''
# exclude version.h from adding author names -- most changes to it are just version increments
if [[ $file != src/cmd/ksh93/include/version.h ]]
then
# comment character
cc=${n:2:1}
# find the line number of the last author line (after which to insert new authors)
n="^[$cc] .*<.*@.*\..*>.* [$cc]$"
alineno=$(awk -v ere=$n '{ if($0 ~ ere) found++; else if(found) { print NR-1; exit; } }' "$file")
((alineno > 0)) || continue
# get authors, most frequent contributor first
git log $first_commit..HEAD --pretty=tformat:%an "$file" | sort | uniq -c | sort -rn \
| while IFS=' ' read -r throwaway_number name
do
fix_authorname name
aline="$name <${email[$name]}>"
centre_line aline
# if not already in file, add to sed script
if ! grep -q "^[$cc]$aline[$cc]$" "$file"; then
sedscript=${sedscript:-"$alineno { "}$'a\\\n'${cc}${aline}${cc}$'\n'
fi
done
[[ -n $sedscript ]] && sedscript+='}'
fi
# add sed commands to update copyright line
sedscript+=$'\n'"/(c) [0-9]\{4\}-[0-9]\{4\} Contributors to ksh 93u+m/ s/-[0-9]\{4\}/-$current_year/"
sedscript+=$'\n'"/$current_year/ ! s/ Copyright (c) \([0-9]\{4\}\) Contributors to ksh 93u+m /Copyright (c) \1-$current_year Contributors to ksh 93u+m/"
# update the file if the sed script makes a difference
sed "$sedscript" $file >|$tmpfile || exit
update_file $file
done