summaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/hbar.c171
-rw-r--r--src/util/parseconf.sh115
-rwxr-xr-xsrc/util/shmk.sh200
-rw-r--r--src/util/shtests.sh66
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
+
+