Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/show-dependencies563
1 files changed, 224 insertions, 339 deletions
diff --git a/bin/show-dependencies b/bin/show-dependencies
index 95b596a..bce97d1 100755
--- a/bin/show-dependencies
+++ b/bin/show-dependencies
@@ -3,7 +3,7 @@
# shellcheck source=conf/default.conf
. "${0%/*}/../conf/default.conf"
-# TODO: read information from database
+# TODO: can this be faster?
# Create a lock file.
@@ -54,360 +54,249 @@ fi
tmp_dir=$(mktemp -d 'tmp.show-dependencies.1.XXXXXXXXXX' --tmpdir)
trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT
-sort -u "${work_dir}/build-order" | \
- grep -v '^\(\S\+\) \1$' | \
- grep '^\S\+ \S\+$' > \
- "${tmp_dir}/original-build-order"
-
-sort -k1,1 "${work_dir}/build-list" > \
- "${tmp_dir}/build-list.sorted-by-package"
-
-{
- sort -k1,1 "${tmp_dir}/original-build-order" | \
- join -1 1 -2 1 -o 1.1,1.2 - "${tmp_dir}/build-list.sorted-by-package"
- sort -k2,2 "${tmp_dir}/original-build-order" | \
- join -1 2 -2 1 -o 1.1,1.2 - "${tmp_dir}/build-list.sorted-by-package"
-} | \
- sort -u | \
- sponge "${tmp_dir}/original-build-order"
-
-find "${work_dir}/package-infos" -maxdepth 1 -printf '%f\n' | \
- sed 's|\(\.[^.]\+\)\{4\}$||' | \
- sort -u > \
- "${tmp_dir}/known-packages"
-
-find "${work_dir}/package-infos" -maxdepth 1 -name '*.groups' -printf '%f %p\n' | \
- while read -r name pf; do
- xargs -r printf "${name%.*.*.*.*}"' %s\n' < \
- "${pf}"
- done > \
- "${tmp_dir}/known-groups"
-
-find "${work_dir}/package-infos" -maxdepth 1 -name '*.packages' -printf '%f %p\n' | \
- while read -r name pf; do
- grep -vxF "${name%.*.*.*.*}" "${pf}" | \
- xargs -r printf "${name%.*.*.*.*}"' %s\n'
- done > \
- "${tmp_dir}/known-split-packages"
-
-find "${work_dir}/package-infos" -maxdepth 1 -name '*.run-depends' -printf '%f %p\n' | \
- while read -r name pf; do
- xargs -r printf '%s '"${name%.*.*.*.*}"'\n' < \
- "${pf}"
- done > \
- "${tmp_dir}/known-run-depends"
-
for target_package in "$@"; do
- output="${webserver_directory}/graphs/${target_package}.png"
-
- if [ "${target_package}" = 'ALL' ]; then
-
- # groups and split packages built by jobs on the build list
- cat "${tmp_dir}/known-groups" "${tmp_dir}/known-split-packages" | \
- sort -k1,1 | \
- join -1 1 -2 1 -o 1.2 - "${tmp_dir}/build-list.sorted-by-package" | \
- sort -u > \
- "${tmp_dir}/relevant-stuff"
-
- {
- # groups and split packages built by jobs on the build list - which are also dependencies of packages on the build-list
- sort -k1,1 "${tmp_dir}/original-build-order" | \
- join -1 1 -2 1 -o 2.1 - "${tmp_dir}/relevant-stuff"
- # build list jobs themself
- cut -d' ' -f1 < "${work_dir}/build-list"
- } | \
- sort -u | \
- sponge "${tmp_dir}/relevant-stuff"
-
- {
- sort -k1,1 "${tmp_dir}/original-build-order" | \
- join -1 1 -2 1 -o 1.1,1.2 - "${tmp_dir}/relevant-stuff" | \
- sort -k2,2 | \
- join -1 2 -2 1 -o 1.1,1.2 - "${tmp_dir}/relevant-stuff"
- sed 's/.*/\0 \0/' "${tmp_dir}/relevant-stuff"
- } > \
- "${tmp_dir}/build-order"
-
- rm "${tmp_dir}/relevant-stuff"
-
- find "${work_dir}/package-states" -name '*.done' -printf '%f\n' | \
- sed 's|\(\.[^.]\+\)\{4\}$||' | \
- sort -u | \
- tee "${tmp_dir}/done-packages" | \
- xargs -r printf '4 staging-package %s\n' > \
- "${tmp_dir}/knots"
-
- {
- cat \
- "${tmp_dir}/known-groups" \
- "${tmp_dir}/known-run-depends" \
- "${tmp_dir}/known-split-packages"
- awk '{print $1 " " $1}' < \
- "${tmp_dir}/known-packages"
- } | \
- sort -k1,1 > \
- "${tmp_dir}/known-connections.sorted.1"
-
- sort -k2,2 \
- "${tmp_dir}/known-connections.sorted.1" > \
- "${tmp_dir}/known-connections.sorted.2"
-
- {
- # staging -> something -> build-list
- join -1 1 -2 1 -o 1.1,1.2 \
- "${tmp_dir}/known-connections.sorted.1" \
- "${tmp_dir}/done-packages" | \
- sort -k2,2 | \
- join -1 2 -2 1 -o 1.1,1.2,2.1,2.2 \
- - \
- "${tmp_dir}/known-connections.sorted.1" | \
- sort -k4,4 | \
- join -1 4 -2 1 -o 1.1,1.2,1.3,1.4 \
- - \
- "${tmp_dir}/build-list.sorted-by-package" | \
- sed '
- s|^\(\S\+ \S\+\) |\1\n|
- '
- # staging -> something -> staging
- join -1 1 -2 1 -o 1.1,1.2 \
- "${tmp_dir}/known-connections.sorted.1" \
- "${tmp_dir}/done-packages" | \
- sort -k2,2 | \
- join -1 2 -2 1 -o 1.1,1.2,2.1,2.2 \
- - \
- "${tmp_dir}/known-connections.sorted.1" | \
- sort -k4,4 | \
- join -1 4 -2 1 -o 1.1,1.2,1.3,1.4 \
- - \
- "${tmp_dir}/done-packages" | \
- sed '
- s|^\(\S\+ \S\+\) |\1\n|
- '
- # build-list -> something -> staging
- join -1 1 -2 1 -o 1.1,1.2 \
- "${tmp_dir}/known-connections.sorted.1" \
- "${tmp_dir}/build-list.sorted-by-package" | \
- sort -k2,2 | \
- join -1 2 -2 1 -o 1.1,1.2,2.1,2.2 \
- - \
- "${tmp_dir}/known-connections.sorted.1" | \
- sort -k4,4 | \
- join -1 4 -2 1 -o 1.1,1.2,1.3,1.4 \
- - \
- "${tmp_dir}/done-packages" | \
- sed '
- s|^\(\S\+ \S\+\) |\1\n|
- '
- cat "${tmp_dir}/build-order"
- } | \
- sort -u | \
- sponge "${tmp_dir}/build-order"
-
- rm \
- "${tmp_dir}/done-packages" \
- "${tmp_dir}/known-connections.sorted.1" \
- "${tmp_dir}/known-connections.sorted.2"
-
- else
-
- grep " $(str_to_regex "${target_package}")\$" "${tmp_dir}/original-build-order" | \
- sort -u > \
- "${tmp_dir}/build-order"
- printf '0 target-package %s\n' "${target_package}" > \
- "${tmp_dir}/knots"
-
- last_sum=''
- current_sum=$(sha512sum "${tmp_dir}/build-order")
- while ! [ "${last_sum}" = "${current_sum}" ]; do
-
- last_sum="${current_sum}"
-
- awk '{print $1}' "${tmp_dir}/build-order" | \
- sort -u > \
- "${tmp_dir}/new"
- {
- cat \
- "${tmp_dir}/original-build-order" \
- "${tmp_dir}/known-split-packages"
- sed '/ base\(-devel\)\?$/d' "${tmp_dir}/known-groups"
- } | \
- sort -k2,2 | \
- join -1 2 -2 1 -o 1.1,1.2 - "${tmp_dir}/new" | \
- sponge -a "${tmp_dir}/build-order"
+# output="${webserver_directory}/graphs/${target_package}.png"
+ output="${webserver_directory}/test.png"
- rm "${tmp_dir}/new"
-
- sort -u "${tmp_dir}/build-order" | \
- sponge "${tmp_dir}/build-order"
-
- current_sum=$(sha512sum "${tmp_dir}/build-order")
-
- done
-
- fi
+ # shellcheck disable=SC2016
+ {
+ printf 'CREATE TEMPORARY TABLE `relevant_binary_packages` (`id` BIGINT, UNIQUE KEY (`id`));\n'
+ printf 'CREATE TEMPORARY TABLE `relevant_binary_packages_copy` (`id` BIGINT, UNIQUE KEY (`id`));\n'
+ printf 'CREATE TEMPORARY TABLE `relevant_install_targets` (`id` BIGINT, UNIQUE KEY (`id`));\n'
-# grep -v '^\(\S\+\) \1$' "${tmp_dir}/build-order" | \
-# sponge "${tmp_dir}/build-order"
+ if [ "${target_package}" = 'ALL' ]; then
- # shellcheck disable=SC2129
- tr ' ' '\n' < \
- "${tmp_dir}/build-order" | \
- sort -u | \
- join -j 1 - "${tmp_dir}/build-list.sorted-by-package" | \
- while read -r pkg rev mod_rev repo; do
- if [ -f "${work_dir}/package-states/${pkg}.${rev}.${mod_rev}.${repo}.broken" ]; then
- printf '1 broken-build-list-package %s\n' "${pkg}"
- elif [ -f "${work_dir}/package-states/${pkg}.${rev}.${mod_rev}.${repo}.blocked" ]; then
- printf '2 blocked-build-list-package %s\n' "${pkg}"
- else
- printf '3 build-list-package %s\n' "${pkg}"
- fi
- done >> \
- "${tmp_dir}/knots"
+ printf 'INSERT IGNORE INTO `relevant_binary_packages` (`id`)'
+ printf ' SELECT DISTINCT `binary_packages`.`id`'
+ printf ' FROM `repositories`'
+ mysql_join_repositories_repository_stabilities
+ printf ' AND `repository_stabilities`.`name` IN ("unbuilt","staging")'
+ mysql_join_repositories_binary_packages
+ printf ';\n'
- {
- sort -u "${work_dir}/deletion-list"
- tr ' ' '\n' < \
- "${tmp_dir}/build-order" | \
- sort -u
- } | \
- sort | \
- uniq -d | \
- xargs -r printf '5 deletion-list-package %s\n' >> \
- "${tmp_dir}/knots"
+ else
- {
- awk '{print $2}' "${tmp_dir}/known-split-packages" | \
- sort -u
- tr ' ' '\n' < \
- "${tmp_dir}/build-order" | \
- sort -u
- } | \
- sort | \
- uniq -d | \
- xargs -r printf '6 split-package %s\n' >> \
- "${tmp_dir}/knots"
+ printf 'CALL calculate_dependencies_of_package_upto_first_built_one(from_base64("%s"));\n' \
+ "$(
+ printf '%s' "${target_package}" | \
+ base64 -w0
+ )"
- {
- cat "${tmp_dir}/known-packages"
- tr ' ' '\n' < \
- "${tmp_dir}/build-order" | \
- sort -u
- } | \
- sort | \
- uniq -d | \
- xargs -r printf '7 package %s\n' >> \
- "${tmp_dir}/knots"
+ fi
- {
- awk '{print $2}' "${tmp_dir}/known-groups" | \
- sort -u
- tr ' ' '\n' < \
- "${tmp_dir}/build-order" | \
- sort -u
+ printf 'INSERT IGNORE INTO `relevant_binary_packages_copy` (`id`)'
+ printf ' SELECT `relevant_binary_packages`.`id` FROM `relevant_binary_packages`'
+ printf ';\n'
+ printf 'INSERT IGNORE INTO `relevant_install_targets` (`id`)'
+ printf ' SELECT DISTINCT `install_target_providers`.`install_target`'
+ printf ' FROM `relevant_binary_packages`'
+ mysql_join_binary_packages_install_target_providers 'relevant_binary_packages'
+ mysql_join_install_target_providers_dependencies
+ mysql_join_dependencies_dependency_types
+ printf ' AND `dependency_types`.`relevant_for_building`'
+ printf ' JOIN `relevant_binary_packages_copy` ON `relevant_binary_packages_copy`.`id`=`dependencies`.`dependent`'
+ printf ';\n'
+
+ # we return lines with either:
+ # "knot" $type $identifier $label
+ # or
+ # "edge" $type $from_knot $to_knot
+ printf 'SELECT DISTINCT'
+ printf ' "knot",'
+ printf 'IF(`build_assignments`.`is_%s`,"%s-build-list-pkgbase",' \
+ 'blocked' 'broken' \
+ 'broken' 'broken'
+ printf '"build-list-pkgbase"'
+ printf ')),'
+ printf 'CONCAT("pkgbase:",`package_sources`.`id`),'
+ printf '`package_sources`.`pkgbase`'
+ printf ' FROM `relevant_binary_packages`'
+ printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
+ mysql_join_binary_packages_repositories
+ mysql_join_repositories_repository_stabilities
+ printf ' AND `repository_stabilities`.`name`="unbuilt"'
+ mysql_join_binary_packages_build_assignments
+ mysql_join_build_assignments_package_sources
+ printf ';\n'
+
+ printf 'SELECT DISTINCT'
+ printf ' "knot",'
+ printf 'CONCAT("pkgname-",`repository_stabilities`.`name`),'
+ printf 'CONCAT("pkgname:",`binary_packages`.`id`),'
+ printf '`binary_packages`.`pkgname`'
+ printf ' FROM `relevant_binary_packages`'
+ printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
+ mysql_join_binary_packages_repositories
+ mysql_join_repositories_repository_stabilities
+ printf ';\n'
+
+ printf 'SELECT DISTINCT'
+ printf ' "knot","install-target",'
+ printf 'CONCAT("install-target:",`install_targets`.`id`),'
+ printf '`install_targets`.`name`'
+ printf ' FROM `relevant_install_targets`'
+ printf ' JOIN `install_targets` ON `install_targets`.`id`=`relevant_install_targets`.`id`'
+ printf ';\n'
+
+ printf 'SELECT DISTINCT'
+ printf ' "edge","builds"'
+ printf ',CONCAT("%s:",`%s`.`id`)' \
+ 'pkgbase' 'package_sources' \
+ 'pkgname' 'binary_packages'
+ printf ' FROM `relevant_binary_packages`'
+ printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
+ mysql_join_binary_packages_repositories
+ mysql_join_repositories_repository_stabilities
+ printf ' AND `repository_stabilities`.`name`="unbuilt"'
+ mysql_join_binary_packages_build_assignments
+ mysql_join_build_assignments_package_sources
+ printf ';\n'
+
+ printf 'SELECT DISTINCT'
+ printf ' "edge","provides"'
+ printf ',CONCAT("%s:",`%s`.`id`)' \
+ 'pkgname' 'binary_packages' \
+ 'install-target' 'install_targets'
+ printf ' FROM `relevant_binary_packages`'
+ printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
+ mysql_join_binary_packages_install_target_providers
+ printf ' JOIN `relevant_install_targets` ON `relevant_install_targets`.`id`=`install_target_providers`.`install_target`'
+ mysql_join_install_target_providers_install_targets
+ printf ';\n'
+
+ printf 'SELECT DISTINCT'
+ printf ' "edge",CONCAT("depends:",`dependency_types`.`name`)'
+ printf ',CONCAT("%s:",`%s`.`id`)' \
+ 'install-target' 'install_targets' \
+ 'pkgname' 'binary_packages'
+ printf ' FROM `relevant_binary_packages`'
+ printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
+ mysql_join_binary_packages_dependencies
+ printf ' JOIN `relevant_install_targets` ON `relevant_install_targets`.`id`=`dependencies`.`depending_on`'
+ mysql_join_dependencies_dependency_types
+ printf ' AND `dependency_types`.`relevant_for_building`'
+ mysql_join_dependencies_install_targets
+ printf ';\n'
} | \
- sort | \
- uniq -d | \
- xargs -r printf '8 group %s\n' >> \
- "${tmp_dir}/knots"
-
- tr ' ' '\n' < \
- "${tmp_dir}/build-order" | \
- sort -u | \
- xargs -r printf '100 unknown %s\n' >> \
+ ${mysql_command} -N --raw --batch | \
+ sed '
+ y/\t/ /
+ /^knot /{
+ s/^\S\+ //
+ w '"${tmp_dir}"'/knots
+ d
+ }
+ /^edge /{
+ s/^\S\+ //
+ w '"${tmp_dir}"'/edges
+ d
+ }
+ ' >&2
+
+ mkdir "${tmp_dir}/find-identical"
+
+ awk '{
+ print $1 >> "'"${tmp_dir}"'/find-identical/characteristics-"$2
+ }' < \
"${tmp_dir}/knots"
-
- sort -k3,3 -k1n,1 "${tmp_dir}/knots" | \
- uniq -f2 | \
- awk '{print $3 " " $1 " " $2}' | \
- sort -k2,3 | \
+ awk '{
+ print $1 " X " $3 >> "'"${tmp_dir}"'/find-identical/characteristics-"$2
+ print $1 " " $2 " X" >> "'"${tmp_dir}"'/find-identical/characteristics-"$3
+ }' < \
+ "${tmp_dir}/edges"
+
+ modifier=$(
+ find "${tmp_dir}/find-identical" -maxdepth 1 \
+ -type f \
+ -name 'characteristics-*' | \
+ while read -r file; do
+ printf '%s ' "${file##*/}"
+ sort -u "${file}" | \
+ sha512sum | \
+ awk '{print $1}'
+ done | \
+ sort -k2,2 | \
+ uniq -Df1 | \
+ sed 's,^characteristics-,,' | \
+ sed '
+ :a
+ $!N
+ s/^\(\S\+\) \(\S\+\)\n\(\S\+ \)\2$/\1+\3\2/
+ ta
+ P
+ D
+ ' | \
+ cut -d' ' -f1 | \
+ sed '
+ s/^/s,\\(/
+ s/+\([^+[:space:]]\+\)$/\\)\\(\\s\\|$\\),\1\\2,g;/
+ s/+/\\|/g
+ '
+ )
+ rm -rf --one-file-system "${tmp_dir}/find-identical"
+
+ sed "${modifier}" "${tmp_dir}/knots" | \
+ sort -k2,2 -k1,1 | \
+ sed '
+ :a
+ $!N
+ s/^\(\S\+ \S\+ \)\(\S\+\)\n\1\(\S\+\)$/\1\2<nl>\3/
+ ta
+ P
+ D
+ ' | \
sponge "${tmp_dir}/knots"
- mkdir "${tmp_dir}/neighbours"
- cat "${tmp_dir}/knots" "${tmp_dir}/build-order" | \
- awk '{
- print $1 " " $2 > "'"${tmp_dir}/neighbours/"'"$1;
- print $1 " " $2 > "'"${tmp_dir}/neighbours/"'"$2;
- }'
-
- find "${tmp_dir}/neighbours" -maxdepth 1 -type f | \
- while read -r file; do
- sed '
- s@\(^\| \)'"$(str_to_regex "${file##*/}")"'\( \|$\)@\1<knot>\2@
- s@\(^\| \)'"$(str_to_regex "${file##*/}")"'\( \|$\)@\1<knot>\2@
- ' "${file}" | \
- sort -u | \
- sponge "${file}"
- done
-
- find "${tmp_dir}/neighbours" -maxdepth 1 -type f -exec \
- sha512sum {} \; | \
- sed 's|^\(\S\+\) .*/\([^/]\+\)$|\2 \1|' | \
- sort -k2,2 | \
- uniq -f1 -D | \
- awk '{print $2 " " $1}' > \
- "${tmp_dir}/sums"
-
- rm -rf --one-file-system "${tmp_dir}/neighbours"
-
- sed '
- :a
- $!N
- s|^\(\S\+\) \([^\n]\+\)\n\1 |\1 \2<nl>|
- ta
- P
- D
- ' "${tmp_dir}/sums" | \
- join -1 1 -2 1 -o 2.2,1.2 - "${tmp_dir}/sums" | \
- while read -r original replacement; do
- sed -i '
- s@\( \|^\)'"$(str_to_regex "${original}")"'\( \|$\)@\1'"${replacement}"'\2@
- s@\( \|^\)'"$(str_to_regex "${original}")"'\( \|$\)@\1'"${replacement}"'\2@
- ' "${tmp_dir}/build-order" "${tmp_dir}/knots"
- done
-
- grep -v '^\(\S\+\) \1$' "${tmp_dir}/build-order" | \
+ sed "${modifier}" "${tmp_dir}/edges" | \
sort -u | \
- sponge "${tmp_dir}/build-order"
- sort -u "${tmp_dir}/knots" | \
- sponge "${tmp_dir}/knots"
+ sponge "${tmp_dir}/edges"
{
printf '%s\n' \
'digraph dependencies {' \
' fontname=dejavu;'
- cut -d' ' -f1,3 < \
- "${tmp_dir}/knots" | \
- while read -r who what; do
- if [ "${what}" = 'broken-build-list-package' ]; then
+ # knots: $type $identifier $label
+ # edges: $type $from_knot $to_knot
+
+ while read -r type id label; do
+ case "${type}" in
+ 'broken-build-list-pkgbase')
color='#ff0000'
- elif [ "${what}" = 'blocked-build-list-package' ]; then
+ ;;
+ 'blocked-build-list-pkgbase')
color='#d00000'
- elif [ "${what}" = 'build-list-package' ]; then
+ ;;
+ 'build-list-pkgbase')
color='#800000'
- elif [ "${what}" = 'deletion-list-package' ]; then
- color='#808080'
- elif [ "${what}" = 'group' ]; then
- color='#0000ff'
- elif [ "${what}" = 'package' ]; then
- color='#000000'
- elif [ "${what}" = 'split-package' ]; then
- color='#8080ff'
- elif [ "${what}" = 'staging-package' ]; then
+ ;;
+ 'pkgname-unbuilt')
+ color='#800000'
+ ;;
+ 'pkgname-staging')
color='#008000'
- elif [ "${what}" = 'target-package' ]; then
- color='#00ff00'
- else
+ ;;
+ 'pkgname-stable'|'pkgname-testing'|'pkgname-standalone')
+ color='#000000'
+ ;;
+ 'install-target')
+ color='#000080'
+ ;;
+ *)
color='#ff80ff'
- fi
- printf ' "%s" [fontcolor="%s"];\n' "${who}" "${color}"
- done
+ label="${label} (${type})"
+ ;;
+ esac
+ printf ' "%s" [label="%s", fontcolor="%s"];\n' "${id}" "${label}" "${color}"
+ done < \
+ "${tmp_dir}/knots"
- sed 's|\\|\\\\|g' "${tmp_dir}/build-order" | \
- xargs -rn2 printf ' "%s" -> "%s";\n' | \
- sort -u
+ while read -r type from_knot to_knot; do
+ printf '"%s" -> "%s";\n' \
+ "${from_knot}" "${to_knot}"
+ done < \
+ "${tmp_dir}/edges"
printf '%s\n' \
'}'
@@ -421,16 +310,12 @@ for target_package in "$@"; do
' "${tmp_dir}/input"
line_count=$(wc -l < "${tmp_dir}/input")
- if [ "${line_count}" -gt 500 ] && [ "${target_package}" != 'ALL' ]; then
- sed -i '/"base\(-devel\)\?"/d' "${tmp_dir}/input"
- line_count=$(wc -l < "${tmp_dir}/input")
- if [ "${line_count}" -gt 700 ]; then
- rm -f "${output}"
- >&2 printf 'Skipping graph for "%s" - would be too big (%d).\n' \
- "${target_package}" \
- "${line_count}"
- continue
- fi
+ if [ "${target_package}" != 'ALL' ] && [ "${line_count}" -gt 1000 ]; then
+ rm -f "${output}"
+ >&2 printf 'Skipping graph for "%s" - would be too big (%d).\n' \
+ "${target_package}" \
+ "${line_count}"
+ continue
fi
printf 'small enough (%s): %d\n' \
"${target_package}" \