# Functions for parsing dependency field values
#
# Copyright (C) 2012, 2018, 2019 Patrick McDermott
#
# This file is part of opkbuild.
#
# opkbuild 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.
#
# opkbuild 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 opkbuild. If not, see .
## @brief Parse a single package dependency string
## @details \fBob_parse_dep\fP() parses package dependencies in the format
## \fIpkg:archqual (rel ver) [arches] \fP, where the components
## \fI:archqual\fP, \fI(rel ver)\fP, \fI[arches]\fP, and \fI\fP
## are optional.
## @option -P pkg_var The name of the variable in which to store the
## package name.
## @option -q archqual_var The name of the variable in which to store the
## architecture qualifier, if any.
## @option -r rel_var The name of the variable in which to store the
## relationship operator, if any.
## @option -v ver_var The name of the variable in which to store the
## version, if any.
## @option -A arches_var The name of the variable in which to store the
## architecture restrictions, if any.
## @option -l plats_var The name of the variable in which to store the
## platform restrictions, if any.
## @option -a host_arch The host architecture. If given, the dependency will
## only be printed on standard output if it applies to
## the given architecture.
## @option -p host_plat The host platform. If given, the dependency will
## only be printed on standard output if it applies to
## the given platform.
## @operand dep req The dependency to parse.
## @return Returns 0.
## @stdout If \fB-a\fP and \fB\p\fP aren't given, the entire dependency string
## is printed, with normalized formatting. If \fB-a\fP and/or \fB-p\fP
## are given and the dependency applies to their arguments, the
## dependency string is printed, except for the architecture (if
## \fB-a\fP given) and/or platform (if \fB-p\fP given) restrictions,
## with normalized formatting. Normalized formatting is a string of the
## format \fIpkg:archqual\ (rel\ ver)\ [arches]\ \fP.
## @pure maybe This function has side effects when used with any of the options
## \fB-P\fP, \fB-q\fP, \fB-r\fP, \fB-v\fP, \fB-A\fP, or \fB-l\fP.
ob_parse_dep()
{
local opt=
local pkg_var=
local archqual_var=
local rel_var=
local ver_var=
local arches_var=
local plats_var=
local host_arch=
local host_plat=
local dep=
local dep_re=
local pkg=
local archqual=
local rel=
local ver=
local arches=
local plats=
local comp=
local comp_var=
OPTIND=1
while getopts 'P:q:r:v:A:l:a:p:' opt; do
case "${opt}" in
P)
pkg_var="${OPTARG}"
if ! _ob_validate_var_name "${pkg_var}"; then
_ob_abort
fi
;;
q)
archqual_var="${OPTARG}"
if ! _ob_validate_var_name "${archqual_var}"
then
_ob_abort
fi
;;
r)
rel_var="${OPTARG}"
if ! _ob_validate_var_name "${rel_var}"; then
_ob_abort
fi
;;
v)
ver_var="${OPTARG}"
if ! _ob_validate_var_name "${ver_var}"; then
_ob_abort
fi
;;
A)
arches_var="${OPTARG}"
if ! _ob_validate_var_name "${arches_var}"; then
_ob_abort
fi
;;
l)
plats_var="${OPTARG}"
if ! _ob_validate_var_name "${plats_var}"; then
_ob_abort
fi
;;
a)
host_arch="${OPTARG}"
;;
p)
host_plat="${OPTARG}"
;;
?)
_ob_abort
;;
esac
done
shift $((${OPTIND} - 1))
dep="${1}"
shift 1 || _ob_abort
# pkg[:archqual] [(rel ver)] [\[arches\]] []
dep_re='s/^ *([^ \(\[<]+) *(\((<<|<=|=|>=|>>) *(.+)\))?'
dep_re="${dep_re}"' *(\[(.+)\])? *(<(.+)>)? *$/\1\n\3\n\4\n\6\n\8/;'
# BusyBox sed supports -r and (as of version 1.22.0) -E.
dep="$(printf '%s\n' "${dep}" | sed -nr '
H;
${
g;
s/[\t\n]/ /g;
'"${dep_re}"'
p;
};
')"
{
IFS=':' read pkg archqual
read rel || :
read ver || :
read arches || :
read plats || :
} <<-EOF
${dep}
EOF
# Set the specified variables.
for comp in pkg archqual rel ver arches plats; do
comp_var="$(eval "printf '%s' \"\${${comp}_var}\"")"
[ -z "${comp_var}" ] && continue
eval "${comp_var}=\"\${${comp}}\""
done
if [ -n "${host_arch}" ] && ! ob_arch_is_concerned \
"${host_arch}" "${arches}"; then
return 0
fi
if [ -n "${host_plat}" ] && ! ob_plat_is_concerned \
"${host_plat}" "${plats}"; then
return 0
fi
printf '%s' "${pkg}"
[ -n "${archqual}" ] && printf ':%s' "${archqual}"
[ -n "${ver}" ] && printf ' (%s %s)' "${rel}" "${ver}"
[ -z "${host_arch}" ] && [ -n "${arches}" ] && \
printf ' [%s]' "${arches}"
[ -z "${host_plat}" ] && [ -n "${plats}" ] && \
printf ' [%s]' "${plats}"
printf '\n'
return 0
}
## @brief Reduce dependencies to a given architecture and platform
## @details \fBob_reduce_deps\fP() reduces a list of dependencies to only those
## that apply to a given architecture and platform. A list is either a
## "normal" list or a "union" list. A normal list is an "AND" list
## composed of "OR" lists, which in turn are composed of simple
## dependencies. A union list is an AND list composed of only simple
## dependencies. That is, a normal list is of the form
## \fIdep\ |\ dep,\ dep\fP, and a union list is of the form
## \fIdep,\ dep\fP. The "Conflicts", "Provides", and "Replaces"
## package relationships are union lists.
## @option -a host_arch The architecture to which dependencies should be
## reduced. Required.
## @option -p host_plat The platform to which dependencies should be reduced.
## Required.
## @option -u - Treat \fIdeps\fP as a union list.
## @operand deps req The list of dependencies to reduce.
## @return Returns 0 on success.
## @stdout Prints the reduced list of dependencies.
## @pure yes This function has no side effects.
ob_reduce_deps()
{
local opt=
local host_arch=
local host_plat=
local deps=
local dep_and=
local dep_or=
local dep_list=
local dep_or_list=
local dep=
union='false'
OPTIND=1
while getopts 'a:p:u' opt; do
case "${opt}" in
a)
host_arch="${OPTARG}"
;;
p)
host_plat="${OPTARG}"
;;
u)
union='true'
;;
?)
_ob_abort
;;
esac
done
shift $((${OPTIND} - 1))
local deps="${1}"
shift 1 || _ob_abort
IFS=','
for dep_and in ${deps}; do
unset IFS
if ! ${union}; then
IFS='|'
dep_or_list=
for dep_or in ${dep_and}; do
unset IFS
dep="$(ob_parse_dep -a "${host_arch}" \
-p "${host_plat}" "${dep_or}")"
if [ -n "${dep}" ]; then
if [ -n "${dep_or_list}" ]; then
dep_or_list="${dep_or_list} | "
fi
dep_or_list="${dep_or_list}${dep}"
fi
done
unset IFS
if [ -n "${dep_or_list}" ]; then
if [ -n "${dep_list}" ]; then
dep_list="${dep_list}, "
fi
dep_list="${dep_list}${dep_or_list}"
fi
else
dep="$(ob_parse_dep -a "${host_arch}" \
-p "${host_plat}" "${dep_and}")"
if [ -n "${dep}" ]; then
if [ -n "${dep_list}" ]; then
dep_list="${dep_list}, "
fi
dep_list="${dep_list}${dep}"
fi
fi
done
unset IFS
printf '%s' "${dep_list}"
return 0
}