]> git.plutz.net Git - rigidfind/blobdiff - cgilite/storage.sh
Merge commit '9ffde1819707b7a9b5ef18f15a9a5819c876f253' as 'cgilite'
[rigidfind] / cgilite / storage.sh
diff --git a/cgilite/storage.sh b/cgilite/storage.sh
new file mode 100755 (executable)
index 0000000..17ea0d0
--- /dev/null
@@ -0,0 +1,188 @@
+#!/bin/sh
+
+# Copyright 2018 - 2021 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_storage" ] && return 0
+include_storage="$0"
+
+CR="\r"
+BR='
+'
+
+LOCK(){
+  local lock="${1}.lock" timeout="${2-20}" block
+
+  if [ \! -w "${lock%/*}" ] || [ -e "$lock" -a \! -f "$lock" ]; then
+    debug "Impossible to get lock: $lock"
+    return 1
+  fi
+
+  while [ $timeout -gt 0 ]; do
+    printf '%i\n' $$ >>"${lock}"
+    read block <"$lock"
+    if [ "$block" = $$ ]; then
+      return 0
+    elif ! { ps -eo pid |grep -qwF "$block"; }; then
+      debug "Trying to override stale lock: $lock"
+      if LOCK "$lock" 1; then
+        rm -- "$lock"
+        RELEASE "$lock"
+      fi
+    else
+      timeout=$((timeout - 1))
+      [ $timeout -gt 0 ] && sleep 1
+    fi
+  done
+
+  debug "Timeout while trying to get lock: $lock"
+  return 1
+}
+
+RELEASE(){
+  local lock="${1}.lock" block
+
+  read block <"$lock"
+  if [ "$block" = $$ ]; then
+    rm -- "$lock"
+    return 0
+  else
+    debug "Refusing to release foreign lock: $lock"
+    return 1
+  fi
+}
+
+STRING(){
+  local in out=''
+  [ $# -gt 0 ] && in="$*" || in="$(cat)"
+  while [ "$in" ]; do case $in in
+    \\*) out="${out}\\\\"; in="${in#\\}" ;;
+    "$BR"*) out="${out}\\n"; in="${in#${BR}}" ;;
+    "$CR"*) out="${out}\\r"; in="${in#${CR}}" ;;
+    "  "*) out="${out}\\t"; in="${in#  }" ;;
+    +*) out="${out}\\+"; in="${in#+}" ;;
+    " "*) out="${out}+"; in="${in# }" ;;
+    *) out="${out}${in%%[\\${CR}${BR}  + ]*}"; in="${in#"${in%%[\\${BR}${CR}   + ]*}"}" ;;
+  esac; done
+  printf '%s' "${out:-\\}"
+}
+
+UNSTRING(){
+  local in out=''
+  [ $# -gt 0 ] && in="$*" || in="$(cat)"
+  while [ "$in" ]; do case $in in
+    \\\\*) out="${out}\\"; in="${in#\\\\}" ;;
+    \\n*) out="${out}${BR}"; in="${in#\\n}" ;;
+    \\r*) out="${out}${CR}"; in="${in#\\r}" ;;
+    \\t*) out="${out}  "; in="${in#\\t}" ;;
+    \\+*) out="${out}+"; in="${in#\\+}" ;;
+    +*) out="${out} "; in="${in#+}" ;;
+    \\*) in="${in#\\}" ;;
+    *) out="${out}${in%%[\\+]*}"; in="${in#"${in%%[\\+]*}"}" ;;
+  esac; done
+  printf '%s\n' "$out"
+}
+
+DBM() {
+  local file="$1" cmd="$2"
+  local k v key value
+  shift 2;
+
+  case "$cmd" in
+    check|contains)
+      key="$(STRING "$1")"
+      while read -r k v; do if [ "$k" = "$key" ]; then
+        return 0
+      fi; done <"$file" 2>&-
+      return 1
+      ;;
+    get)
+      key="$(STRING "$1")"
+      while read -r k v; do if [ "$k" = "$key" ]; then
+        UNSTRING "$v"
+        return 0
+      fi; done <"$file" 2>&-
+      return 1
+      ;;
+    set|store)
+      key="$(STRING "$1")" value="$(STRING "$2")"
+      LOCK "$file" || return 1
+      { while read -r k v; do
+          [ "$k" = "$key" ] || printf '%s\t%s\n' "$k" "$v"
+        done <"$file" 2>&-
+        printf '%s\t%s\n' "$key" "$value"
+      } >"${file}.$$.tmp"
+      mv "${file}.$$.tmp" "${file}"
+      RELEASE "$file"
+      return 0
+      ;;
+    add|insert)
+      k="$1" key="$(STRING "$1")" value="$(STRING "$2")"
+      LOCK "$file" || return 1
+      if DBM "$file" check "$k"; then
+        RELEASE "$file"
+        return 1
+      else
+        printf '%s\t%s\n' "$key" "$value" >>"${file}"
+        RELEASE "$file"
+        return 0
+      fi
+      ;;
+    update|replace)
+      k="$1" key="$(STRING "$1")" value="$(STRING "$2")"
+      LOCK "$file" || return 1
+      if ! DBM "$file" check "$k"; then
+        RELEASE "$file"
+        return 1
+      fi
+      { while read -r k v; do
+          [ "$k" = "$key" ] \
+          && printf '%s\t%s\n' "$key" "$value" \
+          || printf '%s\t%s\n' "$k" "$v"
+        done <"$file" 2>&-
+      } >"${file}.$$.tmp"
+      mv "${file}.$$.tmp" "${file}"
+      RELEASE "$file"
+      return 0
+      ;;
+    append)
+      key="$(STRING "$1")" value="$(STRING "$2")"
+      LOCK "$file" || return 1
+      if ! DBM "$file" check "$1"; then
+        RELEASE "$file"
+        return 1
+      fi
+      { while read -r k v; do
+          [ "$k" = "$key" ] \
+          && printf '%s\t%s\n' "$key" "$v$value" \
+          || printf '%s\t%s\n' "$k" "$v"
+        done <"$file" 2>&-
+      } >"${file}.$$.tmp"
+      mv "${file}.$$.tmp" "${file}"
+      RELEASE "$file"
+      return 0
+      ;;
+    delete|remove)
+      key="$(STRING "$1")"
+      LOCK "$file" || return 1
+      { while read -r k v; do
+          [ "$k" = "$key" ] || printf '%s\t%s\n' "$k" "$v"
+        done <"$file" 2>&-
+      } >"${file}.$$.tmp"
+      mv "${file}.$$.tmp" "${file}"
+      RELEASE "$file"
+      return 0
+      ;;
+  esac
+}