#!/bin/sh # # context-color # Copyright (C) 2018, 2019 Guillaume Gelin # # 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 . PROGNAME="$(basename "$0")" readonly PROGNAME DEFAULT_CONTEXT="whoami; hostname" readonly DEFAULT_CONTEXT DEFAULT_EXCLUDES="0,7,15" readonly DEFAULT_EXCLUDES DEFAULT_METHOD="sum" readonly DEFAULT_METHOD __context_color_usage() { cat <<- EOF usage: $PROGNAME [OPTIONS] Print a color sequence based on a command output's hash. COMMON OPTIONS: --help, -h Print this help. --background, -b Use a background sequence rather than foreground. --id, -i Print the color id rather than the color sequence. --prompt, -p Declare the sequence as non-printable for prompts. --context , -c Define the context command on which result the color will be generated. The default context is "$DEFAULT_CONTEXT". --exclude , -e Comma separated list of color ids not to be used. Multiple --exclude/-e arguments can be specified. The default excluded colors are: "$DEFAULT_EXCLUDES" --method , -m Choose which hashing method to use. "sum" will tend to give adjacent colors for adjacent context outputs. "md5sum" will give more randomization on colors. The default method is: "$DEFAULT_METHOD" DEBUG OPTIONS: --debug, -d Output the sequence as a human-readable string and more useful information. --force , -f Ignore the context and force a color id instead. EOF } __context_color_debug() { sequence="$(__context_color_sequence)" cat <<- EOF Setup: Background mode: ${CC_BACKGROUND} Id mode: ${CC_ID} Prompt mode: ${CC_PROMPT} Context command: ${CC_CONTEXT} Excluded color ids: ${CC_EXCLUDES} Method: ${CC_METHOD} Forced color id: ${CC_FORCE_COLOR} Colors: Terminfo: $(__context_color_terminfo_count) Excluded: $(__context_color_excludes_count) Included: $(__context_color_count) Output: Context hash: $(__context_color_hash) Color id: $(__context_color_id) Sequence: ${sequence}$(echo "$sequence" | cat -v)$(tput sgr0) EOF } __context_color_terminfo_count() { hexcount=$(infocmp -1 | sed -n -e 's/^\t*colors#\([0-9]x\?[0-9]*\),.*/\1/p') printf "%d" "$hexcount" } __context_color_excludes_count() { echo "$CC_EXCLUDES" | tr "," '\n' | wc -l } __context_color_count(){ echo $(( $(__context_color_terminfo_count) - $(__context_color_excludes_count) )) } __context_color_hash() { if [ "$CC_METHOD" = "md5sum" ] then hash=$(eval "$CC_CONTEXT" | md5sum | head -c 6 | tr '[:lower:]' '[:upper:]') printf "%d\n" "0x$hash" else hash=$(eval "$CC_CONTEXT" | sum | cut -d' ' -f1) printf "%.0f\n" "$hash" fi } __context_color_id() { if [ -n "$CC_FORCE_COLOR" ] then id="$CC_FORCE_COLOR" else id=$(( $(__context_color_hash) % ($(__context_color_count) - 1) )) fi for excluded_id in $(echo "$CC_EXCLUDES" | tr ", " '\n') do if [ "$id" -ge "$excluded_id" ] then id=$((id + 1)) fi done echo "$id" } __context_color_sequence() { if [ -n "$CC_BACKGROUND" ] then capname="setab" else capname="setaf" fi sequence="$(tput $capname "$(__context_color_id)")" if [ -n "$CC_PROMPT" ] then sequence="\\[${sequence}\\]" fi printf "%s" "$sequence" } context_color() { for arg do case "$arg" in --help) args="${args}-h " ;; --background) args="${args}-b " ;; --id) args="${args}-i " ;; --prompt) args="${args}-p " ;; --context) args="${args}-c " ;; --debug) args="${args}-d " ;; --force) args="${args}-f " ;; --exclude) args="${args}-e " ;; --method) args="${args}-m " ;; *) args="$args \"$arg\" " ;; esac done eval set -- "$args" while getopts "hbipc:e:m:df:" OPTION do case $OPTION in h) __context_color_usage exit 0 ;; b) readonly CC_BACKGROUND=1 ;; i) readonly CC_ID=1 ;; p) readonly CC_PROMPT=1 ;; c) CC_CONTEXT="$OPTARG" ;; e) for arg in $(echo "$OPTARG" | tr "," '\n') do if ! { [ "$arg" -le "$(__context_color_terminfo_count)" ] && [ "$arg" -ge "0" ]; } 2> /dev/null then >&2 echo "Invalid color id: $arg" return 1 fi if [ -z "$CC_EXCLUDES" ] then CC_EXCLUDES="$arg" elif ! (echo "$CC_EXCLUDES" | tr ", " '\n' | grep -qw "$arg") then CC_EXCLUDES="$CC_EXCLUDES, $arg" fi done ;; m) CC_METHOD="$OPTARG" ;; d) readonly CC_DEBUG=1 ;; f) readonly CC_FORCE_COLOR="$OPTARG" ;; *) __context_color_usage exit 1 esac done if [ -z "$CC_CONTEXT" ] then CC_CONTEXT="$DEFAULT_CONTEXT" fi if [ -z "$CC_EXCLUDES" ] then CC_EXCLUDES="$DEFAULT_EXCLUDES" fi if [ -z "$CC_METHOD" ] then CC_METHOD="$DEFAULT_METHOD" fi if [ -n "$CC_DEBUG" ] then __context_color_debug elif [ -n "$CC_ID" ] then __context_color_id else __context_color_sequence fi return 0 } context_color "$@"