diff options
author | davidovski <david@davidovski.xyz> | 2022-06-27 01:14:42 +0100 |
---|---|---|
committer | davidovski <david@davidovski.xyz> | 2022-06-27 01:14:42 +0100 |
commit | 7bba6cd7612293796e905885f9ed3072877798ab (patch) | |
tree | dd01942dbdd474a4df62ecf74479f5e78d83f1c8 /src/util | |
parent | 79fb6f41104cd0d88891598ddfc3c30d1bb0352d (diff) |
added shmk, building all with shmk
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/hbar.c | 171 | ||||
-rw-r--r-- | src/util/parseconf.sh | 115 | ||||
-rwxr-xr-x | src/util/shmk.sh | 200 | ||||
-rw-r--r-- | src/util/shtests.sh | 66 |
4 files changed, 552 insertions, 0 deletions
diff --git a/src/util/hbar.c b/src/util/hbar.c new file mode 100644 index 0000000..59cf888 --- /dev/null +++ b/src/util/hbar.c @@ -0,0 +1,171 @@ +/* + * hbar + * + * create a horizontal progres bar across the screen + */ + +#include <sys/ioctl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <unistr.h> +#include <stdbool.h> +#include <string.h> +#include <getopt.h> + +#include "colors.h" + +#define DEFAULT_COLOR BLACK BG_WHITE +#define DEFAULT_RESET WHITE BG_DEFAULT + +#define SAVE_POS "\033[s" +#define LOAD_POS "\033[u" + +void human_format(int bytes, char *output) { + char *suffix[] = {"B", "KB", "MB", "GB", "TB"}; + char length = sizeof(suffix) / sizeof(suffix[0]); + + int i = 0; + double dblBytes = bytes; + + if (bytes > 1024) { + for (i = 0; (bytes / 1024) > 0 && i<length-1; i++, bytes /= 1024) + dblBytes = bytes / 1024.0; + } + + sprintf(output, "%.0lf%s", dblBytes, suffix[i]); +} + +size_t len(char *s) { + size_t len = 0; + for (; *s; s++) + if ((*s & 0XC0) != 0x80) + len++; + return len; +} + +void printat(char *s, size_t index) { + index++; + for (; *s; s++) { + if ((*s & 0XC0) != 0x80) { + if (index <= 0) + return; + index--; + } + if (index <= 0) { + fwrite(s, 1, 1, stdout); + } + } +} + +int main(int argc, char **argv) { + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + int width = w.ws_col; + + char *text = ""; + char *unit = ""; + int total = 0; + int completed = 0; + int line = 0; + bool terminate = false; + bool human = false; + + char *color = DEFAULT_COLOR; + char *reset = DEFAULT_RESET; + + int opt; + int option_index = 0; + + const char *optstring = "T:u:c:r:l:th"; + static const struct option opts[] = { + {"text", required_argument, 0, 'T'}, + {"unit", optional_argument, 0, 'u'}, + {"color", optional_argument, 0, 'c'}, + {"reset", optional_argument, 0, 'r'}, + {"line", optional_argument, 0, 'l'}, + {"terminate", no_argument, 0, 't'}, + {"human-readable", no_argument, 0, 'h'} + }; + + while ((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) { + switch (opt) { + case 'T': + text = optarg; + break; + case 'c': + color = optarg; + break; + case 'r': + reset = optarg; + break; + case 'u': + unit = optarg; + break; + case 't': + terminate = true; + break; + case 'h': + human = true; + break; + case 'l': + line = atoi(optarg); + break; + } + } + + + if (argc < optind + 2) { + printf(RESET "\n"); + return 1; + } + + completed = atoi(argv[optind]); + total = atoi(argv[optind+1]); + + if (line != -1) + printf("\033[%dA", line, 0); + + char *count = malloc(width); + if (human) { + char *c = malloc(width); + char *t = malloc(width); + human_format(completed, c); + human_format(total, t); + sprintf(count, "[%s%s/%s%s]", c, unit, t, unit); + } else { + sprintf(count, "[%d%s/%d%s]", completed, unit, total, unit); + } + + printf(RESET "\r"); + printf(color); + for (int i = 0; i <= width; i++) { + int reset_at = 0; + if (total > 0) { + float percent = (float) completed / (float) total; + reset_at = percent * width; + } + + if (i == reset_at) { + printf(reset); + } + + if (text && i < len(text)) { + printat(text, i); + } else if (i + 1 > width - len(count)) { + printat(count, i - width + len(count)); + } else { + printf(" "); + } + } + + if (line != -1) { + printf("\033[%dB", line); + } + if (terminate) { + printf(RESET "\n"); + } + + return 0; +} + diff --git a/src/util/parseconf.sh b/src/util/parseconf.sh new file mode 100644 index 0000000..d215a99 --- /dev/null +++ b/src/util/parseconf.sh @@ -0,0 +1,115 @@ +#!/bin/sh + +usage () { + printf "Usage $0 " + echo << "EOF" +OPTIONS... [FILTER] + +Print the parsed config file filtering by the keys +Arguments: + -f file read configuration from a file, uses /dev/sdtin otherwise + -v only print values + -c n print the last [n] +EOF +} + +# parse a single config file line +# +parse_line() { + [ "$#" = 0 ] && return + + local line="$@" + local key=$1 + + shift + local value="$@" + value=${value%#*} + + case $key in + "include") + cat $value | parse + return + ;; + "]") + IFS=. + set -- $list + list="${*%${!#}}" + IFS=" " + printf "\n" + return + ;; + "}") + [ "$level" = "${level%.*}" ] && + level="" || level=${level%.*} + return + ;; + esac + + case ${value##* } in + "{") + level="${level}${level:+.}${key}" + ;; + "[") + list="${list}${list:+.}${key}" + printf "$level${level:+.}$key:" + ;; + *) + + [ "${#list}" = "0" ] && + printf "$level${level:+.}$key:$value\n" || + printf "$line " + ;; + esac +} + +# print the parsed values from the config file in key:value format +# +parse () { + local file="$1" + + export level="" + export list="" + while IFS= read -r line; do + parse_line $line + done < "/dev/stdin" +} + +# Use the env variable if exists +[ -z ${CONF_FILE} ] && CONF_FILE="/dev/stdin" + +# initialise options +print_keys=true +count= + +while getopts ":f:c:v" opt; do + case "${opt}" in + f) + [ "${OPTARG}" = "-" ] && + CONF_FILE="/dev/stdin" || + CONF_FILE="${OPTARG}" + ;; + + v) + print_keys=false + ;; + c) + count="${OPTARG}q" + ;; + *) + esac +done + +shift $((OPTIND-1)) + +[ $# = 0 ] && + pattern=".*" || + pattern=$(echo $@ | sed "s/\*/[^:]*/g") + +$print_keys && + pattern="s/^($pattern:.+)/\1/p;${count}" || + pattern="s/^$pattern:(.+)/\1/p;${count}" + +# strip whitespace +sed "s/^\s*#.*$\|\s(\s\+)\|^\s\|\s^\|;*$//g" $CONF_FILE | + parse $@ | + sed -rn $pattern diff --git a/src/util/shmk.sh b/src/util/shmk.sh new file mode 100755 index 0000000..0174857 --- /dev/null +++ b/src/util/shmk.sh @@ -0,0 +1,200 @@ +#!/bin/sh +# +# A tool to build a single executable from a collection of shell scripts + +#include colors + +usage () { + cat << EOF +${BLUE}Available Options: + +EOF +} + +find_file () { + for p in $search_path; do + for f in $p/$1.sh $p/$1; do + $verbose \ + && printf "${LIGHT_BLACK}checking: $f\n" 1>&2 + + [ -f "$f" ] && { + echo "$f" + return 0 + } + done + done + return 1 +} + +parse_lines () { + while IFS= read -r line; do + case "$line" in + "#include "*) + cat $(find_file ${line#\#include}) | parse_lines + ;; + "#>"*) + eval ${line#'#>'} + ;; + "#!"*) + printf "%s\n" "$line" + ;; + "#"*);; + *) + printf "%s\n" "$line" + ;; + esac + done +} + +build_shmk () { + [ -f "$1" ] || return 1 + [ ! -z "$2" ] && output="$2" + [ -z "$output" ] && output=$(basename $1) + output=${output%.*} + + $clean && { + rm -f $output + exit + } + + $verbose && echo "building $1 to $output" + + [ ! -d "${output%/*}" ] && mkdir -p "${output%/*}" + + cat $1 \ + | parse_lines > ${output} && + [ ! -z ${entry_function} ] && echo "$entry_function" >> ${output} + chmod +x ${output} +} + +interpret_shmk () { + . $1 + local cmdlist="" + + DIST="./dist" + PROGS="$PROGS $(sed -rn "s/^prog_(.*)\s*\(\)\s*\{/\1/p" $1)" + LIBS="$LIBS $(sed -rn "s/^lib_(.*)\s*\(\)\s*\{/\1/p" $1)" + CHECKS="$CHECKS $(sed -rn "s/^check_(.*)\s*\(\)\s*\{/\1/p" $1)" + + shift + [ -z "$1" ] && set -- clean build check install + while [ ! -z "$1" ]; do + case "$1" in + install) + for prog in $PROGS; do + prog=$(basename $prog) + prog="${prog%.*}" + cmdlist="$cmdlist + install -Dm755 $DIST/$prog ${DESTDIR}/${PREFIX}/bin/$prog #Install program $prog" + done + for lib in $LIBS; do + lib=$(basename $lib) + lib="${lib%.*}" + cmdlist="$cmdlist + install -Dm755 $DIST/$lib ${DESTDIR}/${PREFIX}/lib/$lib #Install library $lib" + done + ;; + clean) + [ -d "$DIST" ] && cmdlist="$cmdlist + rm -r $DIST #Clean" + ;; + check) + for check in $CHECKS; do + command -v check_$check > /dev/null 2>&1 && cmdlist="$cmdlist + check_$check #Check $check" + done + ;; + *) # build all programs + search_path="$DIST $search_path" + + for lib in $LIBS; do + name=$(basename $lib) + build_cmd=lib_$lib + command -v $build_cmd > /dev/null 2>&1 || { + build_cmd="build_shmk $lib $DIST/$name" + } + cmdlist="$cmdlist + $build_cmd #Build library $name" + done + + for prog in $PROGS; do + name=$(basename $prog) + build_cmd=prog_$prog + command -v $build_cmd > /dev/null 2>&1 || { + build_cmd="build_shmk $prog $DIST/$name" + } + cmdlist="$cmdlist + $build_cmd #Build program $name" + done + + # build all libs + ;; + esac + shift + done + + local out len i + $verbose && out=/dev/stdout || out=/dev/null + len=$(($(echo "$cmdlist" | wc -l)-1)) + i=-1 + echo "$cmdlist" | while read -r cmd; do + i=$((i+1)) + printf "${LIGHT_BLACK}[${LIGHT_BLUE}%s${LIGHT_BLACK}/${LIGHT_BLUE}%s${LIGHT_BLACK}] ${LIGHT_WHITE}%s${RESET}\n" "$i" "$len" "${cmd#*#}" + ${cmd%#*} 2>&1 > $out || { + printf "${RED}Error $?\n" + exit 1 + } + done +} + +search_path=". +/usr/lib +/usr/local/lib +/usr/share/shmk +" + +verbose=false +clean=false + +while getopts ":e:I:o:chv" opt; do + case "${opt}" in + e) + entry_function="$OPTARG" + ;; + o) + output="$OPTARG" + ;; + c) + clean=true + ;; + I) + search_path="$search_path + $OPTARG" + ;; + v) + verbose=true + ;; + h) + usage && exit 0 + ;; + esac +done + +shift $((OPTIND-1)) + +[ -z $1 ] && { + exit 1 +} + +echo "$0 $*" +[ -f "$1" ] && shebang="$(head -1 $1)" +case "$shebang" in + "#!"*"shmk"*) + interpret_shmk $@ + ;; + *) + build_shmk $@ + ;; +esac + + diff --git a/src/util/shtests.sh b/src/util/shtests.sh new file mode 100644 index 0000000..69da232 --- /dev/null +++ b/src/util/shtests.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# +# Simple shell test suite +# +# will run all functions with a name starting with test_ +# the return value of said function will determine if its a pass or fail +# +# to run a shell file full of unit tests, run: +# shtests [FILE] +# + +#include colors + +PASS="${BLUE}[ ${GREEN}PASS${BLUE} ]${RESET}" +FAIL="${BLUE}[ ${RED}FAIL${BLUE} ]${RESET}" + +V=false + +runtest () { + test_name=$(echo $1 | sed "s/_/ /g") + test_func="$2" + printf "${BLUE}[ ] ${RESET}$test_name "; + if "$test_func" ; then + printf "\r$PASS\n" + return 0 + else + printf "\r$FAIL\n" + return 1 + fi +} + +# TODO use getopt for this +if [ "$1" = "-v" ]; then + shift + V=true +fi + +if [ $# = "0" ]; then + printf "${RED}No tests file has been provided\n" + exit 1; +else + . $@ +fi + +tests=$(sed -n "s/^test_\(.*\)\s*()\s*{/\1/p" $@) +total=$(echo $tests | wc -w) +passed=0 +failed=0 + +printf "${BLUE}Running $total tests: \n" +for name in $tests; do + if runtest "$name" "test_$name"; then + passed=$((passed+1)) + else + failed=$((failed+1)) + fi +done + +printf "\n${BLUE}Summary for $total tests:\n" +printf "\t${PASS} $passed\n" +printf "\t${FAIL} $failed\n" +printf "\n" + +[ "$passed" = "$total" ] || exit 1 + + |