Jintao 50c46e6857 Add LingYao
Change-Id: Iae6634ce565940904ee320c678d0f77473bebb90
2025-01-03 16:08:55 +08:00

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"
}