diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 02:42:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 02:42:50 +0000 |
commit | 8cb83eee5a58b1fad74c34094ce3afb9e430b5a4 (patch) | |
tree | a9b2e7baeca1be40eb734371e3c8b11b02294497 /tools | |
parent | Initial commit. (diff) | |
download | util-linux-upstream/2.33.1.tar.xz util-linux-upstream/2.33.1.zip |
Adding upstream version 2.33.1.upstream/2.33.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Makemodule.am | 10 | ||||
-rwxr-xr-x | tools/checkcompletion.sh | 45 | ||||
-rwxr-xr-x | tools/checkconfig.sh | 64 | ||||
-rwxr-xr-x | tools/checkdecl.sh | 21 | ||||
-rwxr-xr-x | tools/checkincludes.pl | 24 | ||||
-rwxr-xr-x | tools/checkmans.sh | 187 | ||||
-rwxr-xr-x | tools/checkusage.sh | 159 | ||||
-rwxr-xr-x | tools/checkxalloc.sh | 23 | ||||
-rwxr-xr-x | tools/git-version-gen | 160 |
9 files changed, 693 insertions, 0 deletions
diff --git a/tools/Makemodule.am b/tools/Makemodule.am new file mode 100644 index 0000000..bc22926 --- /dev/null +++ b/tools/Makemodule.am @@ -0,0 +1,10 @@ + +EXTRA_DIST += \ + tools/git-version-gen \ + tools/checkcompletion.sh \ + tools/checkconfig.sh \ + tools/checkdecl.sh \ + tools/checkincludes.pl \ + tools/checkmans.sh \ + tools/checkusage.sh \ + tools/checkxalloc.sh diff --git a/tools/checkcompletion.sh b/tools/checkcompletion.sh new file mode 100755 index 0000000..f3fdda4 --- /dev/null +++ b/tools/checkcompletion.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# +# This script checks if we have bash-completion scripts for the all compiled +# binaries. +# +# Copyright (C) 2016 Karel Zak <kzak@redhat.com> +# + + +die() { + echo "error: $1" + exit 1 +} + +usage() { + echo "Usage:" + echo " $0 [<top_srcdir>]" +} + +# unwanted scripts -- use grep -E, e.g. (aaa|bbb|ccc) +completion_exclude="(nologin)" + +top_srcdir=${1-"."} +completion_dir="${top_srcdir}/bash-completion" + +[ -d "${completion_dir}" ] || die "not found ${completion_dir}" + +bin_files=$(cd ${top_srcdir} && find * -maxdepth 0 -perm /u+x \ + \! -type d \ + \! -name \*.sh \! -name \*.cache \! -name \*.status \ + \! -name configure \! -name libtool | sort) + +completion_files=$(cd ${completion_dir}/ && find * ! -name '*.am' | sort -u) +completion_missing=$(comm -3 <(echo "$completion_files") <(echo "$bin_files")) + +if [ -n "$completion_missing" -a -n "$completion_exclude" ]; then + completion_missing=$(echo "$completion_missing" | grep -v -E "$completion_exclude") +fi + +if [ -n "$completion_missing" ]; then + echo "Missing completion scripts:" + echo "$completion_missing" +fi + diff --git a/tools/checkconfig.sh b/tools/checkconfig.sh new file mode 100755 index 0000000..5f2063a --- /dev/null +++ b/tools/checkconfig.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +# +# This script checks for HAVE_ and ENABLE_ macros which are +# not included in config.h.in +# +# Usage: checkconfig.sh <top_srcdir> <srcfile> [<srcfile> ...] +# +# Copyright (C) 2007 Matthias Koenig <mkoenig@suse.de> +# Copyright (C) 2008 Karel Zak <kzak@redhat.com> +# + + +die() { + echo "error: $1" + exit 1 +} + +usage() { + echo "Usage:" + echo " $0 <top_srcdir> <srcfile> [<srcfile> ...]" + echo "Example:" + echo " find . -name '*.c' | xargs $0 \$(git rev-parse --show-toplevel)" +} + +if [ $# -eq 0 ]; then + usage + exit 1 +fi +srcdir=$1 +config="$srcdir/config.h.in" + +[ -d "$srcdir" ] || die "$srcdir: not such directory." +[ -f "$config" ] || die "$config: not such file." + +shift + +while [ "$#" -ne 0 ]; do + srcfile=$1 + shift + + [ ! -f "$srcfile" ] && continue; + + # Note that we use HAVE_ macros since util-linux-ng-2.14. The + # previous version also have used ENABLE_ too. + # + # ENABLE_ and HAVE_ macros shouldn't be used for any other pupose that + # for config/build options. + # + DEFINES=$(sed -n -e 's/.*[ \t(]\+\(HAVE_[[:alnum:]]\+[^ \t);]*\).*/\1/p' \ + -e 's/.*[ \t(]\+\(ENABLE_[[:alnum:]]\+[^ \t);]*\).*/\1/p' \ + $srcfile | sort -u) + [ -z "$DEFINES" ] && continue + + for d in $DEFINES; do + case $d in + HAVE_CONFIG_H) continue + ;; + *) grep -q "$d\( \|\>\)" $config || \ + echo $(echo $srcfile | sed 's:\\'$srcdir/'::') ": $d" + ;; + esac + done +done diff --git a/tools/checkdecl.sh b/tools/checkdecl.sh new file mode 100755 index 0000000..bd9ee69 --- /dev/null +++ b/tools/checkdecl.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# +# This script checkd for #ifdef HAVE_DECL_SYMBOL in code. +# +# Autoconf docs: +# +# Unlike the other autoconf ‘AC_CHECK_*S’ macros, when a symbol is not +# declared, HAVE_DECL_symbol is defined to ‘0’ instead of leaving +# HAVE_DECL_symbol undeclared. When you are sure that the check was performed, +# use HAVE_DECL_symbol in #if. +# + +if [ ! -f ./configure ]; then + echo "Not found configure script" + exit 1 +fi + +for decl in $(awk '/HAVE_DECL_.*ac_have_decl/ { print $2 }' configure); do + git grep -nE '[[:blank:]]*#[[:blank:]]*if(ndef|def)[[:blank:]]*'$decl; +done | sort -u diff --git a/tools/checkincludes.pl b/tools/checkincludes.pl new file mode 100755 index 0000000..8e6b716 --- /dev/null +++ b/tools/checkincludes.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +# +# checkincludes: Find files included more than once in (other) files. +# Copyright abandoned, 2000, Niels Kristian Bech Jensen <nkbj@image.dk>. + +foreach $file (@ARGV) { + open(FILE, $file) or die "Cannot open $file: $!.\n"; + + my %includedfiles = (); + + while (<FILE>) { + if (m/^\s*#\s*include\s*[<"](\S*)[>"]/o) { + ++$includedfiles{$1}; + } + } + + foreach $filename (keys %includedfiles) { + if ($includedfiles{$filename} > 1) { + print "$file: $filename is included more than once.\n"; + } + } + + close(FILE); +} diff --git a/tools/checkmans.sh b/tools/checkmans.sh new file mode 100755 index 0000000..a48549e --- /dev/null +++ b/tools/checkmans.sh @@ -0,0 +1,187 @@ +#!/bin/bash +# +# Find all man pages, and check they do not have groff syntax errors +# or warnings. +# +# Sami Kerola <kerolasa@iki.fi> + +set -e # exit on errors +set -o pipefail # exit if pipe writer fails +set -u # disallow usage of unset variables +set -C # disallow redirection file overwriting +SCRIPT_INVOCATION_SHORT_NAME=$(basename ${0}) +trap 'echo "${SCRIPT_INVOCATION_SHORT_NAME}: exit on error"; exit 1' ERR + +usage() { + echo "Usage: ${0} [-vVh]" + echo " -v verbose messaging" + echo " -V print version and exit" + echo " -h print this help and exit" +} + +VERBOSE='false' +while getopts vVh OPTIONS; do + case ${OPTIONS} in + v) + VERBOSE='true' + ;; + V) + echo "util-linux: ${SCRIPT_INVOCATION_SHORT_NAME}: v2.1" + exit 0 + ;; + h) + usage + exit 0 + ;; + *) + usage + exit 1 + esac +done + +# Try to find missing manuals matching build targets with manual files. +declare -A MAN_LIST BIN_LIST + +declare -i ITER +declare -i COUNT_GROG=0 +declare -i COUNT_TROFF=0 +declare -i COUNT_REPEATS=0 +declare -a REPEATS +declare -A KNOWN_REPEATS +KNOWN_REPEATS[mount.8]='foo l2 l c overlay' +KNOWN_REPEATS[hexdump.1]='l' +KNOWN_REPEATS[flock.1]='"$0"' +KNOWN_REPEATS[switch_root.8]='$DIR' +KNOWN_REPEATS[logger.1]='-' + +remove_repeats() +{ + set +u + for KN in ${KNOWN_REPEATS[${I##*/}]}; do + if [ "${KN}" = "${REPEATS[$1]}" ]; then + if $VERBOSE; then + echo "info: ${I} removing repeat: ${REPEATS[$1]}" + fi + unset REPEATS[$1] + fi + done + set -u +} + +cd $(git rev-parse --show-toplevel) + +for I in $( + find . -type f -name '*[[:alpha:]].[1-8]' |grep -v "autom4te.cache\|\.libs/\|\.git" +); do + MAN_FILE=${I##*/} + MAN_LIST[${MAN_FILE%%.[0-9]}]=1 + if awk '{if (1 < NR) {exit 1}; if ($1 ~ /^.so$/) {exit 0}}' ${I}; then + # Some manuals, such as x86_64, call inclusion and they + # should not be tested any further. + if ${VERBOSE}; then + printf "skipping: ${I}: includes " + awk '{print $2}' ${I} + fi + continue + fi + if ${VERBOSE}; then + echo "testing: ${I}" + fi + RET=1 + cat ${I} | troff -mandoc -ww -z |& grep "<" && RET=$? + if [ $RET = 0 ]; then + echo "From: cat ${I} | troff -mandoc -ww -z" + echo "==================================================" + ((++COUNT_TROFF)) + fi + GROG=1 + if command -v lexgrog &> /dev/null; then + if ! lexgrog ${I} >/dev/null; then + echo "error: run: lexgrog ${I}" + echo "==================================================" + ((++COUNT_GROG)) + fi + elif command -v grog &> /dev/null; then + if ! grog ${I} | grep man >/dev/null; then + echo "error: grog ${I} is not a man file" + echo "==================================================" + ((++COUNT_GROG)) + fi + else + GROG=0 + fi + REPEATS=( $(cat ${I} | troff -mandoc -Tascii 2>/dev/null | grotty | + col -b | + sed -e 's/\s\+/\n/g; /^$/d' | + awk 'BEGIN { p="" } { if (0 < length($0)) { if (p == $0) { print } } p = $0 }') ) + if [ 0 -lt "${#REPEATS[@]}" ]; then + ITER=${#REPEATS[@]}+1 + while ((ITER--)); do + remove_repeats ${ITER} + done + if [ 0 -lt "${#REPEATS[@]}" ]; then + echo "warning: ${I} has repeating words: ${REPEATS[@]}" + echo "==================================================" + ((++COUNT_REPEATS)) + fi + fi +done + +# Create a list of build targets. +for I in $(find $(git rev-parse --show-toplevel) -name 'Make*.am' | xargs awk ' +$1 ~ /_SOURCES/ { + if ($1 ~ /^test/ || + $1 ~ /^no(inst|dist)/ || + $1 ~ /^sample/ || + $1 ~ /^BUILT/) { + next + } + sub(/_SOURCES/, "") + if ($1 ~ /^lib.*_la/) { + next + } + sub(/_static/, "") + gsub(/_/, ".") + sub(/switch.root/, "switch_root") + sub(/pivot.root/, "pivot_root") + print $1 +}'); do + BIN_LIST[$I]=1 +done + +# Find if build target does not have manual. +set +u +for I in ${!BIN_LIST[@]}; do + if [ -v ${MAN_LIST[$I]} ]; then + echo "warning: ${SCRIPT_INVOCATION_SHORT_NAME}: ${I} does not have man page" + fi +done +set -u + +echo "${SCRIPT_INVOCATION_SHORT_NAME}: ${#BIN_LIST[*]} build targets were found" +echo "${SCRIPT_INVOCATION_SHORT_NAME}: ${#MAN_LIST[*]} files were tested" + +if [ ${GROG} = 0 ]; then +echo "warning: neither grog nor lexgrog commands were found" +fi + +if [ ${COUNT_GROG} -ne 0 ]; then + echo "error: ${SCRIPT_INVOCATION_SHORT_NAME}: ${COUNT_GROG} files failed (lex)grog man-page test" +fi +if [ ${COUNT_TROFF} -ne 0 ]; then + echo "error: ${SCRIPT_INVOCATION_SHORT_NAME}: ${COUNT_TROFF} files failed troff parsing test" +fi +if [ ${COUNT_REPEATS} -ne 0 ]; then + echo "error: ${SCRIPT_INVOCATION_SHORT_NAME}: ${COUNT_REPEATS} files have repeating words" +fi +ITER=${#MAN_LIST[*]}-${COUNT_GROG}-${COUNT_TROFF}-${COUNT_REPEATS} +echo "${SCRIPT_INVOCATION_SHORT_NAME}: ${ITER} man-pages approved" + +if [ ${COUNT_GROG} -ne 0 -o ${COUNT_TROFF} -ne 0 -o ${COUNT_REPEATS} -ne 0 ]; then + exit 1 +fi + +if ! ${VERBOSE}; then + echo "${SCRIPT_INVOCATION_SHORT_NAME}: success" +fi +exit 0 diff --git a/tools/checkusage.sh b/tools/checkusage.sh new file mode 100755 index 0000000..69d69fd --- /dev/null +++ b/tools/checkusage.sh @@ -0,0 +1,159 @@ +#!/bin/bash + +## This script is potentially dangerous! Don't run it on +## arbitrary commands. + +export LC_ALL=C + +if [ "$#" -lt 1 ]; then + echo "usage: $0 program..." >&2 + echo " or try 'make checkusage' to check all built programs" >&2 + exit 1 +fi + +builddir="." +cmds=$(echo $@ | tr ' ' '\n' | sort) + +# set env to dump all output to files +test -z "$CU_DUMP" || rm -f /tmp/checkusage--{help,version,unknownopt} + +## Set alternative options for --help, --version +## or --unknownopt. "x" means "not implemented". +## +## Examples: +## alt_whereis__help="-h" # in past whereis(1) had no longopt for --help +## alt_more__help="x" # more(1) had neither --help nor -h + +alt_fsck__unknownopt="x" # fsck passes unknown opts to fsck.ext4, etc. +alt_mkfs__unknownopt="x" # dito +alt_kill__unknownopt="inval pids" # trick, kill does not use errtryhelp() +if [ $(id -ru) -eq 0 ]; then + alt_sulogin__unknownopt="x" # would hang at pwd prompt +fi + +function exec_option { + local cmdb=$1 + local cmd=$2 + opt=$3 + local tofile="/tmp/checkusage$opt" + + local alt="alt_${cmdb}${opt}" + alt=${alt//-/_} + alt=${alt//./_} + alt=$(eval printf -- \"\$${alt}\") + if test -n "$alt"; then + if test "$alt" = "x"; then + return 1 + fi + opt=$alt + fi + + test -z "$CU_DUMP" || { + echo "##########################################################" + echo "#### $cmdb" + $cmd $opt + } >> "$tofile" 2>&1 + + out=$("$cmd" $opt 2>/dev/null) + err=$("$cmd" $opt 2>&1 >/dev/null) + ret=$? + + # hardcoded ... nologin should always return false + if test "$cmdb" = "nologin" && + test "$opt" = "--help" -o "$opt" = "--version"; then + if test "$ret" -eq 0 -o "$ret" -ge 128; then + echo "$cmdb, $opt, should return false: $ret" + fi + ret=0 + fi + + return 0 +} + + +function check_help { + local cb=$1 + local c=$2 + + if ! exec_option "$cb" "$c" --help; then + return 1 + fi + + if test $ret != 0; then + echo "$cb: $opt, returns error" + else + if test -z "$out"; then + echo "$cb: $opt, no stdout" + fi + if test -n "$err"; then + echo "$cb: $opt, non-empty stderr" + fi + fi + return 0 +} + +function check_version { + local cb=$1 + local c=$2 + + if ! exec_option "$cb" "$c" --version; then + return 1 + fi + + if test $ret != 0; then + echo "$cb: $opt, returns error" + else + if test -z "$out"; then + echo "$cb: $opt, no stdout" + fi + if test -n "$err"; then + echo "$cb: $opt, non-empty stderr" + fi + fi +} + +function check_unknownopt { + local cb=$1 + local c=$2 + local nohelp=$3 + + if ! exec_option "$cb" "$c" --unknownopt; then + return 1 + fi + + if test $ret = 0; then + echo "$cb: $opt, returns no error" + elif test $ret -ge 128; then + echo "$cb: $opt, abnormal exit: $ret" + fi + if test -n "$out"; then + echo "$cb: $opt, non-empty stdout" + fi + if test -z "$err"; then + echo "$cb: $opt, no stderr" + elif test -z "$nohelp" -o "$nohelp" != "yes"; then + out_len=$(echo "$out" | wc -l) + err_len=$(echo "$err" | wc -l) + if test "$err_len" -gt 2; then + echo "$cb: $opt, stderr too long: $err_len" + elif test "$err_len" -lt 2; then + echo "$cb: $opt, stderr too short: $err_len" + fi + fi +} + +for cb in $cmds; do + c="$builddir/$cb" + if ! type "$c" &>/dev/null; then + echo "$cb: does not exist" + continue + fi + + nohelp="no" + if ! check_help "$cb" "$c"; then + nohelp="yes" + fi + check_version "$cb" "$c" + check_unknownopt "$cb" "$c" "$nohelp" +done + diff --git a/tools/checkxalloc.sh b/tools/checkxalloc.sh new file mode 100755 index 0000000..3c34254 --- /dev/null +++ b/tools/checkxalloc.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Find files which include the xalloc.h header, but which still call +# the unwrapped calloc and malloc. +# + +cd "$(git rev-parse --show-toplevel)" || { + echo "error: failed to chdir to git root" + exit 1 +} + +git grep -zl '#include "xalloc.h"' | + xargs -0 grep -nE '\b(([cm]|re)alloc|strdup|asprintf)[[:space:]]*\([^)]' + +result=$? + +if [ $result -eq 123 ]; then + exit 0 # not found +elif [ $result -eq 0 ]; then + exit 1 # found +fi + +exit $result diff --git a/tools/git-version-gen b/tools/git-version-gen new file mode 100755 index 0000000..b287cb7 --- /dev/null +++ b/tools/git-version-gen @@ -0,0 +1,160 @@ +#!/bin/sh +# Print a version string. +scriptversion=2011-02-19.19; # UTC + +# Copyright (C) 2007-2011 Free Software Foundation, Inc. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1|2) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version" \ + '[TAG-NORMALIZATION-SED-SCRIPT]' + exit 1;; +esac + +tarball_version_file=$1 +tag_sed_script="${2:-s/x/x/}" +nl=' +' + +# Avoid meddling by environment variable of the same name. +v= +v_from_git= + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || v= + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +# Otherwise, if there is at least one git commit involving the working +# directory, and "git describe" output looks sensible, use that to +# derive a version string. +elif test "`git log -1 --pretty=format:x . 2>&1`" = x \ + && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \ + && case $v in + v[0-9]*) ;; + *) (exit 1) ;; + esac +then + + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-g/-/'`; + + case $v in + *-rc*) + ;; + *) + # Change the first '-' to a '.', so version-comparing tools work properly. + v=`echo "$v" | sed 's/-/./'`; + ;; + esac + v_from_git=1 +else + v=UNKNOWN +fi + +v=`echo "$v" |sed 's/^v//'` + +# Test whether to append the "-dirty" suffix only if the version +# string we're using came from git. I.e., skip the test if it's "UNKNOWN" +# or if it came from .tarball-version. +if test -n "$v_from_git"; then + # Don't declare a version "dirty" merely because a time stamp has changed. + git update-index --refresh > /dev/null 2>&1 + + dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty= + case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; + esac +fi + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d "$nl" + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: |