#!/bin/sh
#
# addlicense -- a script to insert license comments automatically
#
# This script is part of OSN Commons. Copyright (C) 2024 OSN Developers.
#
# OSN Commons is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OSN Commons is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OSN Commons. If not, see .
#
me=$0
canonical_name=$(echo $me | rev | cut -d'/' -f1 | rev)
if [ $(echo $me | cut -c1) = "/" ]; then
me=$canonical_name
fi
version="1.0.0"
usage() {
echo "Usage: $me [options...] [[directory...] | [-]]"
echo "Automatically insert license comments to the project's "
echo "source files."
echo ""
echo "Options:"
echo " -e, --exclude Add an exclusion pattern"
echo " -i, --include Add an inclusion pattern"
echo " -E, --exclude-file Exclude a file path"
echo " -I, --include-file Include a file path"
echo " -h, --help Show this help and exit"
echo " -v, --version Show version info and exit"
echo " -f, --file Specify a file containing the license"
echo " notice to insert"
echo ""
echo "Bug reports and general questions should be sent to "
echo "."
exit 0
}
show_version() {
echo "$canonical_name (OSN Commons) v$version"
echo ""
echo "Copyright (C) 2024 OSN Developers."
echo "License GPLv3+: GNU GPL version 3 or later ."
echo "This is free software: you are free to change and redistribute it."
echo "There is NO WARRANTY, to the extent permitted by law."
echo ""
echo "Written by Ar Rakin."
exit 0
}
trim_string() {
echo "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
}
targets=""
exclpats=""
inclpats=""
exclfiles=""
inclfiles=""
license_notice_file=""
license_notice="/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/"
stdin_read=""
while [ $# -gt 0 ]; do
case "$1" in
-h | --help)
usage
;;
-v | --version)
show_version
;;
-e | --exclude)
if [ -z "$2" ]; then
echo "$me: option requires an argument -- '$1'" >&2
echo "Try '$me --help' for more detailed information." >&2
exit 2
fi
if [ "$2" != "$(echo "$2" | tr -d ':')" ]; then
echo "$me: exclusion patterns must not contain colons (':')" >&2
exit 2
fi
if [ -z "$exclpats" ]; then
exclpats="$2"
else
exclpats="$exclpats:$2"
fi
shift
;;
-i | --include)
if [ -z "$2" ]; then
echo "$me: option requires an argument -- '$1'" >&2
echo "Try '$me --help' for more detailed information." >&2
exit 2
fi
if [ "$2" != "$(echo "$2" | tr -d ':')" ]; then
echo "$me: inclusion patterns must not contain colons (':')" >&2
exit 2
fi
if [ -z "$inclpats" ]; then
inclpats="$2"
else
inclpats="$inclpats:$2"
fi
shift
;;
-E | --exclude-file)
if [ -z "$2" ]; then
echo "$me: option requires an argument -- '$1'" >&2
echo "Try '$me --help' for more detailed information." >&2
exit 2
fi
if [ "$2" != "$(echo "$2" | tr -d ':')" ]; then
echo "$me: file paths must not contain colons (':')" >&2
exit 2
fi
if [ ! -e "$2" ]; then
echo "$me: $2: No such file or directory" >&2
exit 2
fi
file=$(readlink -f "$2")
if [ $? -ne 0 ]; then
echo "$me: $2: Ignoring" >&2
else
if [ -z "$exclfiles" ]; then
exclfiles="$file"
else
exclfiles="$exclfiles:$file"
fi
fi
shift
;;
-I | --include-file)
if [ -z "$2" ]; then
echo "$me: option requires an argument -- '$1'" >&2
echo "Try '$me --help' for more detailed information." >&2
exit 2
fi
if [ "$2" != "$(echo "$2" | tr -d ':')" ]; then
echo "$me: file paths must not contain colons (':')" >&2
exit 2
fi
if [ ! -e "$2" ]; then
echo "$me: $2: No such file or directory" >&2
exit 2
fi
file=$(readlink -f "$2")
if [ $? -ne 0 ]; then
echo "$me: $2: Ignoring" >&2
else
if [ -z "$inclfiles" ]; then
inclfiles="$file"
else
inclfiles="$inclfiles:$file"
fi
fi
shift
;;
-f | --file)
if [ -z "$2" ]; then
echo "$me: option requires an argument -- '$1'" >&2
echo "Try '$me --help' for more detailed information." >&2
exit 2
fi
if [ "$2" = "-" ]; then
if [ -t 0 ]; then
echo "$me: nothing to read from standard input" >&2
exit 2
fi
if [ ! -z "$stdin_read" ] && [ "$stdin_read" != "notice" ]; then
echo "$me: multiple options are being directed to standard input" >&2
exit 2
fi
license_notice="$(
cat
echo E
)"
stdin_read="notice"
else
if [ ! -f "$2" ]; then
echo "$me: $2: No such file or directory" >&2
exit 2
fi
license_notice_file="$2"
license_notice="$(
cat "$2"
echo E
)"
fi
license_notice=${license_notice%E}
shift
;;
-)
if [ -t 0 ]; then
echo "$me: nothing to read from standard input" >&2
exit 2
fi
if [ ! -z "$stdin_read" ] && [ "$stdin_read" != "targets" ]; then
echo "$me: multiple options are being directed to standard input" >&2
exit 2
fi
while IFS='' read -r line; do
if [ "$line" != "$(echo "$line" | tr -d ':')" ] || [ -z "$line" ]; then
echo "$me: target paths must not contain colons (':')" >&2
exit 2
fi
if [ -z "$targets" ]; then
targets="$line"
else
targets="$targets:$line"
fi
done
stdin_read="targets"
;;
-*)
echo "$me: Invalid option -- '$1'" >&2
echo "Try '$me --help' for more detailed information." >&2
exit 2
;;
*)
if [ "$1" != "$(echo "$1" | tr -d ':')" ] || [ -z "$1" ]; then
echo "$me: target paths must not contain colons (':')" >&2
exit 2
fi
if [ -z "$targets" ]; then
targets="$1"
else
targets="$targets:$1"
fi
;;
esac
shift
done
if [ -z "$targets" ]; then
echo "$me: No target operands specified" >&2
echo "Try '$me --help' for more detailed information" >&2
exit 2
fi
success=0
insert_license() {
set -e
local file="$1"
local tmpfile="$(mktemp)"
echo "$license_notice" >$tmpfile
cat $file >>$tmpfile
mv $tmpfile $file
set +e
}
process_files() {
echo "$targets" | while IFS=':' read -r target; do
local stat_out=$(stat "$target" 2>&1)
if [ $? -ne 0 ]; then
echo "$me: $(echo $stat_out | cut -c 7-)" >&2
continue
fi
echo "$me: scanning: $target"
local find_cmd="find '$target' -type f ! -path \"*/*~\" \
! -path \"*/.git/*\" ! -path \"*/.svn/*\" \
! -path \"*/.hg/*\" ! -path \"*/CVS/*\" \
! -path \"*/.bzr/*\" ! -path \"$me\""
if [ ! -z "$exclpats" ]; then
local ifs="$IFS"
IFS=":"
for exclpat in $exclpats; do
find_cmd="$find_cmd ! -path \"$exclpat\""
done
IFS="$ifs"
fi
if [ ! -z "$inclpats" ]; then
local ifs="$IFS"
IFS=":"
for inclpat in $inclpats; do
find_cmd="$find_cmd -path \"$inclpat\""
done
IFS="$ifs"
fi
local files=$(eval $find_cmd)
if [ -z "$files" ]; then
echo "$me: no files found" >&2
continue
fi
local r_license_notice_file
if [ ! -z "$license_notice_file" ]; then
r_license_notice_file=$(readlink -f "$license_notice_file")
fi
for file in $files; do
local rpath=$(readlink -f "$file")
if [ ! -z "$license_notice_file" ] && [ "$rpath" = "$r_license_notice_file" ]; then
continue
fi
if [ ! -z "$exclfiles" ]; then
local ifs="$IFS"
IFS=":"
for exclfile in $exclfiles; do
if [ "$rpath" = "$exclfile" ]; then
continue 2
fi
done
IFS="$ifs"
fi
if [ ! -z "$inclfiles" ]; then
local ifs="$IFS"
IFS=":"
for inclfile in $inclfiles; do
if [ "$rpath" = "$inclfile" ]; then
break
fi
done
if [ "$rpath" != "$inclfile" ]; then
continue
fi
IFS="$ifs"
fi
if file "$file" -b | grep -Eq 'binary|ELF|executable|data'; then
echo "$me: skipping binary/data file: $file"
continue
fi
local license_notice_size=$(echo "$license_notice" | wc -c)
local part=$(sed ':a; /^$/d; $!{N; ba}; s/^[[:space:]]*//' $file | head -c $license_notice_size)
if [ "$license_notice" = "$part" ]; then
echo "$me: license notice already present in: $file"
success=1
continue
fi
echo "$me: inserting license comment to: $file"
insert_license "$file"
done
success=1
done
}
process_files
if [ $success -ne 1 ]; then
exit 1
fi