#!/bin/sh -efu
#
# Copyright (c) 2014-2015 Mike Frysinger <vapier@gentoo.org>
# Copyright (c) 2014-2015 Dmitry V. Levin <ldv@strace.io>
# Copyright (c) 2014-2023 The strace developers.
# All rights reserved.
#
# SPDX-License-Identifier: LGPL-2.1-or-later
export LC_ALL=C
usage()
{
cat <<EOF
Usage: $0 <input> <output>
Generate xlat header files from <input> (a file or dir of files) and write
the generated headers to <output>.
EOF
exit 1
}
print_m4_record()
{
local val output
val="$1"; shift
output="$1"; shift
[ -n "$output" ] || return 0
if [ "$first_enum" = 1 ]; then
printf '%s' "$val"
else
printf ',\n%s' "$val"
fi >> "$output"
first_enum=0
}
cond_def()
{
local line
line="$1"; shift
local xlat_type
xlat_type="$1"; shift
local val
val="${line%%[!A-Za-z0-9_]*}"
local t def=
t="${line#*[ ]}"
if [ "$line" != "$t" ]; then
while [ "$def" != "$t" ]; do
def="$t"
t="${t##[ ]}"
done
fi
if [ -n "$def" ]; then
[ -n "$unconditional" ] ||
printf '%s\n' \
"#if defined($val) || (defined(HAVE_DECL_$val) && HAVE_DECL_$val)"
[ -n "$nocheckval" ] ||
printf '%s\n' \
"DIAG_PUSH_IGNORE_TAUTOLOGICAL_COMPARE" \
"static_assert(($val) == ($def), \"$val != $def\");" \
"DIAG_POP_IGNORE_TAUTOLOGICAL_COMPARE"
[ -n "$unconditional" ] ||
printf '%s\n' \
"#else" \
"# define $val $def" \
"#endif"
fi
if [ XT_SORTED = "$xlat_type" ]; then
[ -n "$unconditional" ] ||
printf '%s\n' \
"#if defined($val) || (defined(HAVE_DECL_$val) && HAVE_DECL_$val)"
check_sort_order "$val"
[ -n "$unconditional" ] ||
printf '%s\n' "#endif"
fi
}
check_sort_order()
{
local val
val="$1"; shift
cat <<-EOF
#if defined XLAT_PREV_VAL
static_assert((unsigned long long) ($val)
> (unsigned long long) (XLAT_PREV_VAL),
"Incorrect order in #sorted xlat: $val"
" is not larger than the previous value");
#endif
#undef XLAT_PREV_VAL
#define XLAT_PREV_VAL ($val)
EOF
}
print_xlat()
{
local val
val="$1"; shift
[ 1 = "$value_indexed" ] && printf " [%s] =" "${val}"
if [ -z "${val_type-}" ]; then
echo " XLAT(${val}),"
else
echo " XLAT_TYPE(${val_type}, ${val}),"
fi
echo " #define XLAT_VAL_$xlat_flag_cnt ((${val_type:-unsigned}) (${val}))"
echo " #define XLAT_STR_$xlat_flag_cnt STRINGIFY(${val})"
xlat_flag_cnt=$((xlat_flag_cnt + 1))
[ -z "$enum" ] || print_m4_record "$val" "$output_m4"
}
print_xlat_pair()
{
local val str macro
val="$1"; shift
str="$1"; shift
macro="$1"; shift
[ 1 = "$value_indexed" ] && printf " [%s] =" "${val}"
if [ -z "${val_type-}" ]; then
echo " XLAT_PAIR(${val}, \"${str}\"),"
else
echo " XLAT_TYPE_PAIR(${val_type}, ${val}, \"${str}\"),"
fi
echo " #define XLAT_VAL_$xlat_flag_cnt ((${val_type:-unsigned}) (${val}))"
echo " #define XLAT_STR_$xlat_flag_cnt \"${str}\""
xlat_flag_cnt=$((xlat_flag_cnt + 1))
[ -z "$enum" ] || print_m4_record "$macro" "$output_m4"
}
cond_xlat()
{
echo "$1" | {
local val def m is_shift='' xlat
read val def
m="${val%%|*}"
[ "${m}" = "${m#1<<}" ] || is_shift=1
if [ -z "$is_shift$nocheckval" ]; then
xlat="$(print_xlat "${val}")"
elif [ -n "$is_shift" ]; then
m="${m#1<<}"
xlat="$(print_xlat_pair "1ULL<<${val#1<<}" "${val}" "$m")"
else
xlat="$(print_xlat_pair "${def}" "${val}" "$m")"
fi
if [ -z "${def}${unconditional}" ]; then
printf '%s\n' \
"#if defined(${m}) || (defined(HAVE_DECL_${m}) && HAVE_DECL_${m})" \
" ${xlat}" \
"#endif"
else
printf '%s\n' "$xlat"
fi
}
# Since we're calling print_xlat/print_xlat_pair in subprocess
xlat_flag_cnt=$((xlat_flag_cnt + 1))
first_enum=0
}
gen_header()
{
local input="$1" output="$2" name="$3" output_m4="$4"
exec 3>&1
echo "generating ${output}" >&2
(
local defs="${0%/*}/../defs.h"
local mpers="${0%/*}/../mpers_xlat.h"
local decl="extern const struct xlat ${name}[];"
local in_defs= in_mpers=
local xlat_type="XT_NORMAL"
local includes=""
local enum=""
first_enum=1
value_indexed=0
xlat_flag_cnt=0
if grep -F -q -x "$decl" "$defs"; then
in_defs=1
elif grep -F -q -x "$decl" "$mpers"; then
in_mpers=1
fi
cat <<-EOF
/* Generated by $0 from $1; do not edit. */
#include "gcc_compat.h"
#include "static_assert.h"
EOF
local unconditional='' nocheckval='' line
# 1st pass: output directives.
while read -r line; do
case "$line" in
*/\**)
line=$(printf "%s" "$line" |
sed "s|[[:space:]]*/\*.*\*/[[:space:]]*||")
;;
esac
case $line in
'#conditional')
unconditional=
;;
'#unconditional')
unconditional=1
;;
'#checkval')
nocheckval=
;;
'#nocheckval')
nocheckval=1
;;
'#val_type '*)
# to be processed during 2nd pass
;;
'#sorted'|'#sorted '*)
xlat_type="XT_SORTED"
;;
'#value_indexed')
value_indexed=1
xlat_type="XT_INDEXED"
;;
'#enum')
[ -z "$output_m4" ] || enum=1
;;
'#include '*)
includes="${includes} ${line###include }"
;;
'#'*)
echo "${line}"
;;
[A-Z_]*)
cond_def "$line" "$xlat_type"
;;
'1<<'[A-Z_]*) # symbolic constants with shift
[ XT_SORTED != "$xlat_type" ] ||
check_sort_order "1ULL<<${line#1<<}"
;;
[0-9]*) # numeric constants
[ XT_SORTED != "$xlat_type" ] ||
check_sort_order "${line}"
;;
esac
done < "$input"
cat <<-EOF
#undef XLAT_PREV_VAL
#ifndef XLAT_MACROS_ONLY
EOF
[ -z "$unconditional" -o -z "$enum" ] || {
echo "ignoring #enum due to #unconditional for ${input}" >&2
enum=""
}
[ 1 != "$enum" ] || (
echo "generating ${output_m4}" >&2
printf 'dnl Generated by %s from %s; do not edit.\n' \
"$0" "$input"
printf 'AC_DEFUN([st_CHECK_ENUMS_%s],[\n' "$name"
printf 'AC_CHECK_DECLS(m4_normalize([\n'
) > "${output_m4}"
if [ -n "$in_defs" ]; then
cat <<-EOF
# ifndef IN_MPERS
EOF
elif [ -n "$in_mpers" ]; then
cat <<-EOF
# ifdef IN_MPERS
${decl}
# else
EOF
else
cat <<-EOF
# ifdef IN_MPERS
# error static const struct xlat ${name} in mpers mode
# else
EOF
fi
echo "DIAG_PUSH_IGNORE_TAUTOLOGICAL_CONSTANT_COMPARE"
echo "static const struct xlat_data ${name}_xdata[] = {"
unconditional=
nocheckval=
val_type=
# 2nd pass: output everything.
while read -r line; do
case "$line" in
*/\**)
line=$(printf "%s" "$line" |
sed "s|[[:space:]]*/\*.*\*/[[:space:]]*||")
;;
esac
case ${line} in
'#conditional')
unconditional=
;;
'#unconditional')
unconditional=1
;;
'#checkval')
nocheckval=
;;
'#nocheckval')
nocheckval=1
;;
'#sorted'|'#sorted '*)
;;
'#value_indexed')
;;
'#enum')
;;
'#include '*)
;;
'#val_type '*)
val_type="${line#\#val_type }"
;;
[A-Z_!]*) # symbolic constants
cond_xlat "${line}"
;;
'1<<'[A-Z_]*) # symbolic constants with shift
cond_xlat "${line}"
;;
[0-9]*) # numeric constants
print_xlat "${line}"
;;
*) # verbatim lines
echo "${line}"
;;
esac
done < "${input}"
echo '};'
[ 1 != "$enum" ] || (
printf '\n]),,, [\n'
[ -z "$includes" ] || printf '#include %s\n' $includes
printf '])])])\n'
# Providing macro name to main
echo "st_CHECK_ENUMS_${name}" >&3
) >> "${output_m4}"
if [ -n "$in_defs" ]; then
:
elif [ -n "$in_mpers" ]; then
cat <<-EOF
# if !(defined HAVE_M32_MPERS || defined HAVE_MX32_MPERS)
static
# endif
EOF
else
cat <<-EOF
static
EOF
fi
cat <<-EOF
const struct xlat ${name}[1] = { {
.data = ${name}_xdata,
.size = ARRAY_SIZE(${name}_xdata),
.type = ${xlat_type},
EOF
echo " .flags_mask = 0"
for i in $(seq 0 "$((xlat_flag_cnt - 1))"); do
cat <<-EOF
# ifdef XLAT_VAL_${i}
| XLAT_VAL_${i}
# endif
EOF
done
echo " ,"
echo " .flags_strsz = 0"
for i in $(seq 0 "$((xlat_flag_cnt - 1))"); do
cat <<-EOF
# ifdef XLAT_STR_${i}
+ sizeof(XLAT_STR_${i})
# endif
EOF
done
echo " ,"
cat <<-EOF
} };
DIAG_POP_IGNORE_TAUTOLOGICAL_CONSTANT_COMPARE
EOF
for i in $(seq 0 "$((xlat_flag_cnt - 1))"); do
cat <<-EOF
# undef XLAT_STR_${i}
# undef XLAT_VAL_${i}
EOF
done
cat <<-EOF
# endif /* !IN_MPERS */
#endif /* !XLAT_MACROS_ONLY */
EOF
) >"${output}"
exec 3>&-
}
gen_make()
{
local output="$1"
local name
shift
echo "generating ${output}" >&2
(
printf "XLAT_INPUT_FILES = "
printf 'xlat/%s.in ' "$@"
echo
printf "XLAT_HEADER_FILES = "
printf 'xlat/%s.h ' "$@"
echo
for name; do
printf '$(top_srcdir)/src/xlat/%s.h: $(top_srcdir)/src/xlat/%s.in $(top_srcdir)/src/xlat/gen.sh\n' \
"${name}" "${name}"
echo ' $(AM_V_GEN)$(top_srcdir)/src/xlat/gen.sh $< $@'
done
) >"${output}"
}
gen_git()
{
local output="$1"
shift
echo "generating ${output}" >&2
(
printf '/%s\n' .gitignore Makemodule.am st_check_enums.m4
printf '/%s.h\n' "$@"
printf '/%s.m4\n' "$@"
) >"${output}"
}
gen_m4_entry()
{
local output
output="$1"; shift
echo "generating $output" >&2
{
printf 'AC_DEFUN([st_CHECK_ENUMS],[\n'
while read fun; do
printf '\t%s\n' "$fun"
done
printf '])\n'
} >"$output"
}
main()
{
case $# in
0) set -- "${0%/*}" "${0%/*}" ;;
2) ;;
3) ;;
*) usage ;;
esac
local input="$1"
local output="$2"
local output_m4="${3:-}"
local name
local jobs=0
local ncpus="$(getconf _NPROCESSORS_ONLN)"
local pids=
[ "${ncpus}" -ge 1 ] ||
ncpus=1
if [ -d "${input}" ]; then
(
local f names=
set +f
set -- "${input}"/*.in
set -f
for f; do
[ -f "${f}" ] || continue
name=${f##*/}
name=${name%.in}
gen_header "${f}" "${output}/${name}.h" "${name}" \
"${output}/${name}.m4" &
pids="$pids $!"
names="${names} ${name}"
: $(( jobs += 1 ))
if [ "${jobs}" -gt "$(( ncpus * 2 ))" ]; then
read wait_pid rest
pids="$rest"
wait -n 2>/dev/null || wait "$wait_pid"
: $(( jobs -= 1 ))
fi <<- EOF
$pids
EOF
done
gen_git "${output}/.gitignore" ${names} &
gen_make "${output}/Makemodule.am" ${names} &
wait
) | sort | gen_m4_entry "${output}/st_check_enums.m4"
else
name=${input##*/}
name=${name%.in}
gen_header "${input}" "${output}" "${name}" "${output_m4}" \
> /dev/null
fi
}
main "$@"