--- /dev/null
+#!/bin/sh
+
+# Copyright 2023, 2024 Paul Hänsch
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+[ -n "$include_db23" ] && return 0
+include_db23="$0"
+
+. "$_EXEC/cgilite/storage.sh"
+
+DB2() {
+ local call data file key val seq
+ data="${BR}${1}${BR}" call="$2"
+ shift 2
+
+ case $call in
+ new|discard)
+ printf ''
+ ;;
+ open|load) file="$1"
+ cat "$file" || return 1
+ ;;
+ check|contains) key="$(STRING "$1")" val=''
+ val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}"
+ [ "$val" = '' ] && return 1
+ ;;
+ count) key="$(STRING "$1")" val='' seq=0
+ val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}"
+ [ "$val" = '' ] || val="${val} "
+ while [ "$val" != '' ]; do
+ seq=$((seq + 1)) val="${val#* }"
+ done
+ printf "%i\n" "$seq"
+ [ $seq = 0 ] && return 1
+ ;;
+ get) key="$(STRING "$1")" seq="${2:-1}"
+ val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}"
+ [ "$val" = '' ] && return 1 || val="${val} "
+ while [ $seq -gt 1 ]; do
+ seq=$((seq - 1)) val="${val#* }"
+ done
+ [ "$val" = '' ] && return 1
+ UNSTRING "${val%% *}"
+ ;;
+ iterate|raw) key="$(STRING "$1")"
+ val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}"
+ [ "$val" = '' ] && return 1
+ printf '%s\n' $val
+ ;;
+ delete|remove) key="$(STRING "$1")"
+ val="${data#*"${BR}${key}" *"${BR}"}"
+ key="${data%"${BR}${key}" *"${BR}"*}"
+ [ "${key}${BR}${val}" = "${data}" ] && return 1
+ printf '%s' "${key#"${BR}"}${BR}${val%"${BR}"}"
+ ;;
+ set|store) key="$(STRING "$1")" val=""
+ shift 1
+ val="$(for v in "$@"; do STRING "$v"; printf \\t; done)"
+ if [ "${data#*"${BR}${key}" *}" != "$data" ]; then
+ data="${data%"${BR}${key}" *"${BR}"*}${BR}${key} ${val% }${BR}${data#*"${BR}${key}" *"${BR}"}"
+ data="${data#"${BR}"}" data="${data%"${BR}"}"
+ else
+ data="${data#"${BR}"}${key} ${val% }${BR}"
+ data="${data#"${BR}"}"
+ fi
+ printf %s\\n "${data}"
+ ;;
+ append) key="$(STRING "$1")" val=""
+ val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}"
+ if [ "$val" = '' ]; then
+ printf %s\\n "${data}"
+ return 1
+ else
+ shift 1
+ val="${val}$(for v in "$@"; do printf \\t; STRING "$v"; done)"
+ data="${data%"${BR}${key}" *"${BR}"*}${BR}${key} ${val% }${BR}${data#*"${BR}${key}" *"${BR}"}"
+ data="${data#"${BR}"}" data="${data%"${BR}"}"
+ printf %s\\n "${data}"
+ fi
+ ;;
+ flush|save|write) file="$1"
+ data="${data#"${BR}"}" data="${data%"${BR}"}"
+ printf '%s\n' "$data" >"$file" || return 1
+ ;;
+ esac
+ return 0
+}
+
+DB3() {
+ # wrapper function that allows easyer use of DB2
+ # by always keeping file data in $db3_data
+
+ case "$1" in
+ new|discard|open|load|delete|remove|set|store|append)
+ db3_data="$(DB2 "$db3_data" "$@")"
+ return "$?"
+ ;;
+ get|count|check|contains|iterate|raw|flush|save|write)
+ DB2 "$db3_data" "$@"
+ return "$?"
+ ;;
+ esac
+}