Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/freebsd/poudriere.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBryan Drewery <bryan@shatow.net>2022-09-14 23:03:31 +0300
committerBryan Drewery <bryan@shatow.net>2022-11-05 18:47:57 +0300
commitffefb5706663204f56271bad77621022abfdd9cf (patch)
tree832f4105c278742c78eeccd541cc5e2f42bf258e /src
parent4a74af201588e35faf734b51c78ca2c8e9a92b46 (diff)
display_output: Allow filtering and re-ordering columns
This brings some major refactoring.
Diffstat (limited to 'src')
-rw-r--r--src/share/poudriere/include/display.sh249
1 files changed, 184 insertions, 65 deletions
diff --git a/src/share/poudriere/include/display.sh b/src/share/poudriere/include/display.sh
index ca9c218f..ff17c5f3 100644
--- a/src/share/poudriere/include/display.sh
+++ b/src/share/poudriere/include/display.sh
@@ -24,56 +24,120 @@
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
+EX_DATAERR=65
+EX_SOFTWARE=70
+
+DISPLAY_SEP=$'\002'
+DISPLAY_DYNAMIC_FORMAT_DEFAULT="%%-%ds"
+DISPLAY_TRIM_TRAILING_FIELD=1
+
display_setup() {
[ $# -eq 1 ] || [ $# -eq 2 ] || eargs display_setup format [column_sort]
+ local IFS
+ local -; set -f
+
_DISPLAY_DATA=
- _DISPLAY_FORMAT="$1"
+ _DISPLAY_FORMAT="${1:-dynamic}"
+ _DISPLAY_HEADER=
_DISPLAY_COLUMN_SORT="${2-}"
_DISPLAY_FOOTER=
+ _DISPLAY_LINES=0
+ _DISPLAY_COLS=0
+
+ # encode
+ set -- ${_DISPLAY_FORMAT}
+ IFS="${DISPLAY_SEP}"
+ _DISPLAY_FORMAT="$@"
+ unset IFS
}
display_add() {
- local arg line tab
+ [ $# -gt 0 ] || eargs display_add col [col...]
+ local IFS
- unset line
- tab=$'\t'
- # Ensure blank arguments and spaced-arguments are respected.
- # This is mostly to deal with sorting later
- for arg do
- if [ -z "${arg}" ]; then
- arg=" "
+ if [ -z "${_DISPLAY_HEADER}" ]; then
+ local arg argi line argformat format
+
+ argi=1
+ unset line
+ format=
+ for arg do
+ # Collect header custom formats if using dynamic
+ if [ "${_DISPLAY_FORMAT}" == "dynamic" ]; then
+ case "${arg}" in
+ *:*%%*)
+ argformat="${arg#*:}"
+ arg="${arg%%:*}"
+ ;;
+ *)
+ argformat="${DISPLAY_DYNAMIC_FORMAT_DEFAULT}"
+ ;;
+ esac
+ format="${format:+${format}${DISPLAY_SEP}}${argformat}"
+ fi
+ line="${line:+${line}${DISPLAY_SEP}}${arg}"
+ hash_set _display_header "${arg}" "${argi}"
+ argi=$((argi + 1))
+ done
+ _DISPLAY_COLS=$((argi - 1))
+ _DISPLAY_HEADER="${line}"
+ if [ "${_DISPLAY_FORMAT}" == "dynamic" ]; then
+ _DISPLAY_FORMAT="${format}"
fi
- line="${line:+${line}${tab}}${arg}"
- done
+
+ return
+ fi
+
# Add in newline
- if [ -n "${_DISPLAY_DATA}" ]; then
+ if [ -n "${_DISPLAY_DATA-}" ]; then
_DISPLAY_DATA="${_DISPLAY_DATA}"$'\n'
fi
- _DISPLAY_DATA="${_DISPLAY_DATA:+${_DISPLAY_DATA}}${line}"
- return 0
+ _DISPLAY_LINES=$((_DISPLAY_LINES + 1))
+ # encode
+ IFS="${DISPLAY_SEP}"
+ _DISPLAY_DATA="${_DISPLAY_DATA:+${_DISPLAY_DATA}}""$@"
+ unset IFS
}
display_footer() {
- local arg line tab
+ local IFS
+
+ # encode
+ IFS="${DISPLAY_SEP}"
+ _DISPLAY_FOOTER="$@"
+ unset IFS
+}
- unset line
- tab=$'\t'
- # Ensure blank arguments and spaced-arguments are respected.
- for arg do
+_display_check_lengths() {
+ local cnt arg max_length
+ local IFS
+ local -; set -f
+
+ # decode
+ IFS="${DISPLAY_SEP}"
+ set -- $@
+ unset IFS
+
+ cnt=0
+ for arg in "$@"; do
+ cnt=$((cnt + 1))
if [ -z "${arg}" ]; then
- arg=" "
+ continue
+ fi
+ stripansi "${arg}" arg
+ hash_get _display_lengths "${cnt}" max_length || max_length=0
+ if [ "${#arg}" -gt "${max_length}" ]; then
+ hash_set _display_lengths "${cnt}" "${#arg}"
fi
- line="${line:+${line}${tab}}${arg}"
done
- # Add in newline
- _DISPLAY_FOOTER="${line}"
- return 0
}
+# display_output [col ...]
display_output() {
- local cnt lengths length format arg flag quiet line n
- local header header_format
+ local lengths format arg flag quiet line n
+ local cols header_format
local OPTIND=1
+ local IFS
local -
set -f
@@ -93,45 +157,84 @@ display_output() {
shift $((OPTIND-1))
- format="${_DISPLAY_FORMAT}"
+ # cols to filter/reorder on
+ cols=
+ if [ "$#" -gt 0 ]; then
+ local col awktmp
- # Determine optimal format
- n=0
- while IFS= mapfile_read_loop_redir line; do
- n=$((n + 1))
- if [ "${n}" -eq 1 ]; then
- if [ "${quiet}" -eq 1 ]; then
- continue
- fi
- header="${line}"
- fi
- IFS=$'\t'
- set -- ${line}
- unset IFS
- cnt=0
+ _DISPLAY_COLS=0
for arg in "$@"; do
- hash_get lengths ${cnt} max_length || max_length=0
- stripansi "${arg}" arg
- if [ ${#arg} -gt ${max_length} ]; then
- # Keep the hash var local to this function
- _hash_var_name "lengths" "${cnt}"
- local ${_hash_var_name}
- # Set actual value
- hash_set lengths ${cnt} ${#arg}
+ if ! hash_remove _display_header "${arg}" col; then
+ err ${EX_DATAERR:?} "No column named '${arg}'"
fi
- cnt=$((cnt + 1))
+ # cols="$3,$2,$1" for awk printing
+ cols="${cols:+${cols},}\$${col}"
+ _DISPLAY_COLS=$((_DISPLAY_COLS + 1))
done
+
+ # Re-order and filter using awk(1) back into our internal vars.
+ awktmp=$(mktemp -t display_output)
+ {
+ echo "${_DISPLAY_FORMAT}"
+ echo "${_DISPLAY_HEADER}"
+ echo "${_DISPLAY_DATA}" |
+ sort -t "${DISPLAY_SEP}" ${_DISPLAY_COLUMN_SORT}
+ echo "${_DISPLAY_FOOTER}"
+ } > "${awktmp}.in"
+ awk -F"${DISPLAY_SEP}" -vOFS="${DISPLAY_SEP}" \
+ "{print ${cols}}" "${awktmp}.in" > "${awktmp}"
+ n=-1
+ while IFS= mapfile_read_loop "${awktmp}" line; do
+ case "${n}" in
+ -1)
+ unset _DISPLAY_DATA
+ _DISPLAY_FORMAT="${line}"
+ ;;
+ 0)
+ _DISPLAY_HEADER="${line}"
+ ;;
+ "$((_DISPLAY_LINES + 1))")
+ if [ -n "${_DISPLAY_FOOTER}" ]; then
+ _DISPLAY_FOOTER="${line}"
+ fi
+ ;;
+ *)
+ if [ -n "${_DISPLAY_DATA-}" ]; then
+ _DISPLAY_DATA="${_DISPLAY_DATA}"$'\n'
+ fi
+ _DISPLAY_DATA="${_DISPLAY_DATA:+${_DISPLAY_DATA}}${line}"
+ ;;
+ esac
+ n=$((n + 1))
+ done
+ rm -f "${awktmp}" "${awktmp}.in"
+ else
+ _DISPLAY_DATA="$(echo "${_DISPLAY_DATA}" |
+ sort -t "${DISPLAY_SEP}" ${_DISPLAY_COLUMN_SORT})"
+ fi
+
+ # Determine optimal format from filtered data
+ _display_check_lengths "${_DISPLAY_HEADER}"
+ _display_check_lengths "${_DISPLAY_FOOTER}"
+ while IFS= mapfile_read_loop_redir line; do
+ _display_check_lengths "${line}"
done <<-EOF
${_DISPLAY_DATA}
- ${_DISPLAY_FOOTER}
EOF
# Set format lengths if format is dynamic width
+ # decode
+ IFS="${DISPLAY_SEP}"
+ set -- ${_DISPLAY_FORMAT}
+ unset IFS
+ format="$@"
case "${format}" in
*%%*)
+ local length
+
set -- ${format}
lengths=
- n=0
+ n=1
for arg in "$@"; do
# Check if this is a format argument
case "${arg}" in
@@ -140,9 +243,18 @@ display_output() {
esac
case ${arg} in
*%d*)
- hash_get lengths ${n} length
+ hash_remove _display_lengths "${n}" length
+ if [ "${DISPLAY_TRIM_TRAILING_FIELD}" -eq 1 ] &&
+ [ "${n}" -eq "${_DISPLAY_COLS}" ]; then
+ case "${arg}" in
+ *-*) length=0 ;;
+ esac
+ fi
lengths="${lengths:+${lengths} }${length}"
;;
+ *)
+ hash_unset _display_lengths "${n}" || :
+ ;;
esac
n=$((n + 1))
done
@@ -150,32 +262,39 @@ display_output() {
;;
esac
- # Show header separately so it is not sorted
+ # Header
if [ "${quiet}" -eq 0 ]; then
- stripansi "${header}" header
+ stripansi "${_DISPLAY_HEADER}" _DISPLAY_HEADER
stripansi "${format}" header_format
- IFS=$'\t'
- set -- ${header}
+ # decode
+ IFS="${DISPLAY_SEP}"
+ set -- ${_DISPLAY_HEADER}
unset IFS
printf "${header_format}\n" "$@"
fi
- # Sort as configured in display_setup()
- echo "${_DISPLAY_DATA}" | tail -n +2 | \
- sort -t $'\t' ${_DISPLAY_COLUMN_SORT} | \
- while IFS= mapfile_read_loop_redir line; do
- IFS=$'\t'
+ # Data
+ while IFS= mapfile_read_loop_redir line; do
+ # decode
+ IFS="${DISPLAY_SEP}"
set -- ${line}
unset IFS
printf "${format}\n" "$@"
- done
+ done <<-EOF
+ ${_DISPLAY_DATA}
+ EOF
+
+ # Footer
if [ -n "${_DISPLAY_FOOTER}" ]; then
- IFS=$'\t'
+ # decode
+ IFS="${DISPLAY_SEP}"
set -- ${_DISPLAY_FOOTER}
unset IFS
printf "${format}\n" "$@"
fi
unset _DISPLAY_DATA _DISPLAY_FORMAT \
- _DISPLAY_COLUMN_SORT _DISPLAY_FOOTER
+ _DISPLAY_COLUMN_SORT \
+ _DISPLAY_LINES _DISPLAY_COLS \
+ _DISPLAY_FOOTER _DISPLAY_HEADER
}