311 lines
8.9 KiB
Bash
311 lines
8.9 KiB
Bash
#!/bin/bash
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
# to be sourced
|
|
|
|
# General shell helpers
|
|
|
|
# exit with non-zero exit code; if there is only one param:
|
|
# exit with msg $1 and exit code from last command (or 99 if = 0)
|
|
# otherwise, exit with $1 and use remaining arguments as msg
|
|
function die() {
|
|
rc=$?
|
|
if [ $# -gt 1 ]; then
|
|
rc="$1"
|
|
shift
|
|
fi
|
|
[ "$rc" -ne 0 ] || rc=99
|
|
echo >&2 "$@"
|
|
exit $rc
|
|
}
|
|
|
|
# filter out paths that are not files
|
|
# input $@, output via echo;
|
|
# note: pass `-` for stdin
|
|
# note: outputs nothing if all input files are "bad" (eg. not existing), but it
|
|
# is left for caller to decide if this is an erorr condition;
|
|
# note: whitespaces are considered "bad" as part of filename, it's an error.
|
|
function filter-out-bad-files() {
|
|
if [[ $# = 1 && "$1" = '-' ]]; then
|
|
echo -
|
|
return 0
|
|
fi
|
|
if [ $# = 0 ]; then
|
|
die 10 "no files passed, use '-' when reading from pipe (|)"
|
|
fi
|
|
local any=0 diagmsgs=/dev/stderr re=$'[\t \n]'
|
|
[ -n "${QUIET_COMPAT-}" ] && diagmsgs=/dev/null
|
|
for x in "$@"; do
|
|
if [ -e "$x" ]; then
|
|
if [[ "$x" =~ $re ]]; then
|
|
die 11 "err: filename contains whitespaces: $x."
|
|
fi
|
|
echo "$x"
|
|
any=1
|
|
else
|
|
echo >&"$diagmsgs" filtering "$x" out
|
|
fi
|
|
done
|
|
if [ $any = 0 ]; then
|
|
echo >&"$diagmsgs" 'all files (for given query) filtered out'
|
|
fi
|
|
}
|
|
|
|
# Basics of regexp explained, as a reference for mostly-C programmers:
|
|
# (bash) "regexp-$VAR-regexp" - bash' VARs are placed into "QUOTED" strings
|
|
# /\);?$/ - match end of function declaration, $ is end of string
|
|
# ^[ \t]* - (heuristic), anything but comment, eg to exclude function docs
|
|
# /STH/, /END/ - (awk), print all lines sice STH matched, up to END, inclusive
|
|
|
|
# "Whitespace only"
|
|
WB='[ \t\n]'
|
|
|
|
# Helpers below print the thing that is looked for, for further grep'ping/etc.
|
|
# That simplifies process of excluding comments or spares us state machine impl.
|
|
#
|
|
# We take advantage of current/common linux codebase formatting here.
|
|
#
|
|
# Functions in this section require input file/s passed as args
|
|
# (usually one, but more could be supplied in case of renames in kernel),
|
|
# '-' could be used as an (only) file argument to read from stdin/pipe.
|
|
|
|
# wrapper over find-something-decl() functions below, to avoid repetition
|
|
# pass $what as $1, $end as $2, and $files to look in as rest of args
|
|
function find-decl() {
|
|
test $# -ge 3 # ensure that there are at least 3 params
|
|
local what end files
|
|
what="$1"
|
|
end="$2"
|
|
shift 2
|
|
files="$(filter-out-bad-files "$@")" || die
|
|
if [ -z "$files" ]; then
|
|
return 0
|
|
fi
|
|
# shellcheck disable=SC2086
|
|
awk "
|
|
/^$WB*\*/ {next}
|
|
$what, $end
|
|
" $files
|
|
}
|
|
|
|
# yield $1 function declaration (signature), don't pass return type in $1
|
|
# looks only in files specified ($2, $3...)
|
|
function find-fun-decl() {
|
|
test $# -ge 2
|
|
local what end
|
|
what="/$WB*([(]\*)?$1$WB*($|[()])/"
|
|
end='/\);?$/'
|
|
shift
|
|
find-decl "$what" "$end" "$@"
|
|
}
|
|
|
|
# yield $1 enum declaration (type/body)
|
|
function find-enum-decl() {
|
|
test $# -ge 2
|
|
local what end
|
|
what="/^$WB*enum$WB+$1"' \{$/'
|
|
end='/\};$/'
|
|
shift
|
|
find-decl "$what" "$end" "$@"
|
|
}
|
|
|
|
# yield $1 struct declaration (type/body)
|
|
function find-struct-decl() {
|
|
test $# -ge 2
|
|
local what end
|
|
what="/^$WB*struct$WB+$1"' \{$/'
|
|
end='/^\};$/' # that's (^) different from enum-decl
|
|
shift
|
|
find-decl "$what" "$end" "$@"
|
|
}
|
|
|
|
# yield first line of $1 macro definition
|
|
function find-macro-decl() {
|
|
test $# -ge 2
|
|
local what end
|
|
# only unindented defines, only whole-word match
|
|
what="/^#define$WB+$1"'([ \t\(]|$)/'
|
|
end=1 # only first line; use find-macro-implementation-decl for full body
|
|
shift
|
|
find-decl "$what" "$end" "$@"
|
|
}
|
|
|
|
# yield full macro implementation
|
|
function find-macro-implementation-decl() {
|
|
test $# -ge 2
|
|
local what end
|
|
# only unindented defines, only whole-word match
|
|
what="/^#define$WB+$1"'([ \t\(]|$)/'
|
|
# full implementation, until a line not ending in a backslash.
|
|
# Does not handle macros with comments embedded within the definition.
|
|
end='/[^\\]$/'
|
|
shift
|
|
find-decl "$what" "$end" "$@"
|
|
}
|
|
|
|
# yield first line of $1 typedef definition (simple typedefs only)
|
|
# this probably won't handle typedef struct { \n int foo;\n};
|
|
function find-typedef-decl() {
|
|
test $# -ge 2
|
|
local what end
|
|
what="/^typedef .* $1"';$/'
|
|
end=1
|
|
shift
|
|
find-decl "$what" "$end" "$@"
|
|
}
|
|
|
|
# gen() - DSL-like function to wrap around all the other
|
|
#
|
|
# syntax:
|
|
# gen DEFINE if (KIND [METHOD of]) NAME [(matches|lacks) PATTERN|absent] in <list-of-files>
|
|
|
|
# where:
|
|
# DEFINE is HAVE_ or NEED_ #define to print;
|
|
# `if` is there to just read it easier and made syntax easier to check;
|
|
#
|
|
# NAME is the name for what we are looking for;
|
|
#
|
|
# KIND specifies what kind of declaration/definition we are looking for,
|
|
# could be: fun, enum, struct, method, macro, typedef,
|
|
# 'implementation of macro'
|
|
# for KIND=method, we are looking for function ptr named METHOD in struct
|
|
# named NAME (two optional args are then necessary (METHOD & of));
|
|
#
|
|
# for KIND='implementation of macro' we are looking for the full
|
|
# implementation of the macro, not just its first line. This is usually
|
|
# combined with "matches" or "lacks".
|
|
#
|
|
# next [optional] args could be used:
|
|
# matches PATTERN - use to grep for the PATTERN within definition
|
|
# (eg, for ext_ack param)
|
|
# lacks - use to add #define only if there is no match of the PATTERN,
|
|
# *but* the NAME is *found*
|
|
# absent - the NAME that we grep for must be not found
|
|
# (ie: function not exisiting)
|
|
#
|
|
# without this optional params, behavior is the same as with
|
|
# `matches .` - use to grep just for existence of NAME;
|
|
#
|
|
# `in` is there to ease syntax, similar to `if` before.
|
|
#
|
|
# <list-of-files> is just space-separate list of files to look in,
|
|
# single (-) for stdin.
|
|
#
|
|
# PATTERN is an awk pattern, will be wrapped by two slashes (/)
|
|
function gen() {
|
|
test $# -ge 6 || die 20 "too few arguments, $# given, at least 6 needed"
|
|
local define if_kw kind name in_kw # mandatory
|
|
local of_kw method_name operator pattern # optional
|
|
local src_line="${BASH_SOURCE[0]}:${BASH_LINENO[0]}"
|
|
define="$1"
|
|
if_kw="$2"
|
|
kind="$3"
|
|
local orig_args_cnt=$#
|
|
shift 3
|
|
[ "$if_kw" != if ] && die 21 "$src_line: 'if' keyword expected, '$if_kw' given"
|
|
case "$kind" in
|
|
fun|enum|struct|macro|typedef)
|
|
name="$1"
|
|
shift
|
|
;;
|
|
method)
|
|
test $# -ge 5 || die 22 "$src_line: too few arguments, $orig_args_cnt given, at least 8 needed"
|
|
method_name="$1"
|
|
of_kw="$2"
|
|
name="$3"
|
|
shift 3
|
|
[ "$of_kw" != of ] && die 23 "$src_line: 'of' keyword expected, '$of_kw' given"
|
|
;;
|
|
implementation)
|
|
test $# -ge 5 || die 28 "$src_line: too few arguments, $orig_args_cnt given, at least 8 needed"
|
|
of_kw="$1"
|
|
kind="$2"
|
|
name="$3"
|
|
shift 3
|
|
[ "$of_kw" != of ] && die 29 "$src_line: 'of' keyword expected, '$of_kw' given"
|
|
[ "$kind" != macro ] && die 30 "$src_line: implementation only supports 'macro', '$kind' given"
|
|
kind=macro-implementation
|
|
;;
|
|
*) die 24 "$src_line: unknown KIND ($kind) to look for" ;;
|
|
esac
|
|
operator="$1"
|
|
case "$operator" in
|
|
absent)
|
|
pattern='.'
|
|
in_kw="$2"
|
|
shift 2
|
|
;;
|
|
matches|lacks)
|
|
pattern="$2"
|
|
in_kw="$3"
|
|
shift 3
|
|
;;
|
|
in)
|
|
operator=matches
|
|
pattern='.'
|
|
in_kw=in
|
|
shift
|
|
;;
|
|
*) die 25 "$src_line: unknown OPERATOR ($operator) to look for" ;;
|
|
esac
|
|
[ "$in_kw" != in ] && die 26 "$src_line: 'in' keyword expected, '$in_kw' given"
|
|
test $# -ge 1 || die 27 "$src_line: too few arguments, at least one filename expected"
|
|
|
|
local first_decl=
|
|
if [ "$kind" = method ]; then
|
|
first_decl="$(find-struct-decl "$name" "$@")" || exit 40
|
|
# prepare params for next lookup phase
|
|
set -- - # overwrite $@ to be single dash (-)
|
|
name="$method_name"
|
|
kind=fun
|
|
elif [[ $# = 1 && "$1" = '-' ]]; then
|
|
# avoid losing stdin provided to gen() due to redirection (<<<)
|
|
first_decl="$(cat -)"
|
|
fi
|
|
|
|
# lookup the NAME
|
|
local body
|
|
body="$(find-$kind-decl "$name" "$@" <<< "$first_decl")" || exit 41
|
|
awk -v define="$define" -v pattern="$pattern" -v "$operator"=1 '
|
|
BEGIN {
|
|
# prepend "identifier boundary" to pattern, also append
|
|
# it, but only for patterns not ending with such already
|
|
#
|
|
# eg: "foo" -> "\bfoo\b"
|
|
# "struct foo *" -> "\bstruct foo *"
|
|
|
|
# Note that mawk does not support "\b", so we have our
|
|
# own approximation, NI
|
|
NI = "[^A-Za-z0-9_]" # "Not an Indentifier"
|
|
|
|
if (!match(pattern, NI "$"))
|
|
pattern = pattern "(" NI "|$)"
|
|
pattern = "(^|" NI ")" pattern
|
|
}
|
|
/./ { not_empty = 1 }
|
|
$0 ~ pattern { found = 1 }
|
|
END {
|
|
if (lacks && !found && not_empty || matches && found || absent && !found)
|
|
print "#define", define
|
|
}
|
|
' <<< "$body"
|
|
}
|
|
|
|
# tell if given flag is enabled in .config
|
|
# return 0 if given flag is enabled, 1 otherwise
|
|
# inputs:
|
|
# $1 - flag to check (whole word, without _MODULE suffix)
|
|
# env flag $CONFFILE
|
|
#
|
|
# there are two "config" formats supported, to ease up integrators lifes
|
|
# .config (without leading #~ prefix):
|
|
#~ # CONFIG_ACPI_EC_DEBUGFS is not set
|
|
#~ CONFIG_ACPI_AC=y
|
|
#~ CONFIG_ACPI_VIDEO=m
|
|
# and autoconf.h, which would be:
|
|
#~ #define CONFIG_ACPI_AC 1
|
|
#~ #define CONFIG_ACPI_VIDEO_MODULE 1
|
|
function config_has() {
|
|
grep -qE "^(#define )?$1((_MODULE)? 1|=m|=y)$" "$CONFFILE"
|
|
}
|