]> git.plutz.net Git - shellwiki/blob - storage.sh
17ea0d051eaf27233f8b5cd96f7396974c7a87f1
[shellwiki] / storage.sh
1 #!/bin/sh
2
3 # Copyright 2018 - 2021 Paul Hänsch
4
5 # Permission to use, copy, modify, and/or distribute this software for any
6 # purpose with or without fee is hereby granted, provided that the above
7 # copyright notice and this permission notice appear in all copies.
8
9 # THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12 # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15 # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17 [ -n "$include_storage" ] && return 0
18 include_storage="$0"
19
20 CR="\r"
21 BR='
22 '
23
24 LOCK(){
25   local lock="${1}.lock" timeout="${2-20}" block
26
27   if [ \! -w "${lock%/*}" ] || [ -e "$lock" -a \! -f "$lock" ]; then
28     debug "Impossible to get lock: $lock"
29     return 1
30   fi
31
32   while [ $timeout -gt 0 ]; do
33     printf '%i\n' $$ >>"${lock}"
34     read block <"$lock"
35     if [ "$block" = $$ ]; then
36       return 0
37     elif ! { ps -eo pid |grep -qwF "$block"; }; then
38       debug "Trying to override stale lock: $lock"
39       if LOCK "$lock" 1; then
40         rm -- "$lock"
41         RELEASE "$lock"
42       fi
43     else
44       timeout=$((timeout - 1))
45       [ $timeout -gt 0 ] && sleep 1
46     fi
47   done
48
49   debug "Timeout while trying to get lock: $lock"
50   return 1
51 }
52
53 RELEASE(){
54   local lock="${1}.lock" block
55
56   read block <"$lock"
57   if [ "$block" = $$ ]; then
58     rm -- "$lock"
59     return 0
60   else
61     debug "Refusing to release foreign lock: $lock"
62     return 1
63   fi
64 }
65
66 STRING(){
67   local in out=''
68   [ $# -gt 0 ] && in="$*" || in="$(cat)"
69   while [ "$in" ]; do case $in in
70     \\*) out="${out}\\\\"; in="${in#\\}" ;;
71     "$BR"*) out="${out}\\n"; in="${in#${BR}}" ;;
72     "$CR"*) out="${out}\\r"; in="${in#${CR}}" ;;
73     "   "*) out="${out}\\t"; in="${in#  }" ;;
74     +*) out="${out}\\+"; in="${in#+}" ;;
75     " "*) out="${out}+"; in="${in# }" ;;
76     *) out="${out}${in%%[\\${CR}${BR}   + ]*}"; in="${in#"${in%%[\\${BR}${CR}   + ]*}"}" ;;
77   esac; done
78   printf '%s' "${out:-\\}"
79 }
80
81 UNSTRING(){
82   local in out=''
83   [ $# -gt 0 ] && in="$*" || in="$(cat)"
84   while [ "$in" ]; do case $in in
85     \\\\*) out="${out}\\"; in="${in#\\\\}" ;;
86     \\n*) out="${out}${BR}"; in="${in#\\n}" ;;
87     \\r*) out="${out}${CR}"; in="${in#\\r}" ;;
88     \\t*) out="${out}   "; in="${in#\\t}" ;;
89     \\+*) out="${out}+"; in="${in#\\+}" ;;
90     +*) out="${out} "; in="${in#+}" ;;
91     \\*) in="${in#\\}" ;;
92     *) out="${out}${in%%[\\+]*}"; in="${in#"${in%%[\\+]*}"}" ;;
93   esac; done
94   printf '%s\n' "$out"
95 }
96
97 DBM() {
98   local file="$1" cmd="$2"
99   local k v key value
100   shift 2;
101
102   case "$cmd" in
103     check|contains)
104       key="$(STRING "$1")"
105       while read -r k v; do if [ "$k" = "$key" ]; then
106         return 0
107       fi; done <"$file" 2>&-
108       return 1
109       ;;
110     get)
111       key="$(STRING "$1")"
112       while read -r k v; do if [ "$k" = "$key" ]; then
113         UNSTRING "$v"
114         return 0
115       fi; done <"$file" 2>&-
116       return 1
117       ;;
118     set|store)
119       key="$(STRING "$1")" value="$(STRING "$2")"
120       LOCK "$file" || return 1
121       { while read -r k v; do
122           [ "$k" = "$key" ] || printf '%s\t%s\n' "$k" "$v"
123         done <"$file" 2>&-
124         printf '%s\t%s\n' "$key" "$value"
125       } >"${file}.$$.tmp"
126       mv "${file}.$$.tmp" "${file}"
127       RELEASE "$file"
128       return 0
129       ;;
130     add|insert)
131       k="$1" key="$(STRING "$1")" value="$(STRING "$2")"
132       LOCK "$file" || return 1
133       if DBM "$file" check "$k"; then
134         RELEASE "$file"
135         return 1
136       else
137         printf '%s\t%s\n' "$key" "$value" >>"${file}"
138         RELEASE "$file"
139         return 0
140       fi
141       ;;
142     update|replace)
143       k="$1" key="$(STRING "$1")" value="$(STRING "$2")"
144       LOCK "$file" || return 1
145       if ! DBM "$file" check "$k"; then
146         RELEASE "$file"
147         return 1
148       fi
149       { while read -r k v; do
150           [ "$k" = "$key" ] \
151           && printf '%s\t%s\n' "$key" "$value" \
152           || printf '%s\t%s\n' "$k" "$v"
153         done <"$file" 2>&-
154       } >"${file}.$$.tmp"
155       mv "${file}.$$.tmp" "${file}"
156       RELEASE "$file"
157       return 0
158       ;;
159     append)
160       key="$(STRING "$1")" value="$(STRING "$2")"
161       LOCK "$file" || return 1
162       if ! DBM "$file" check "$1"; then
163         RELEASE "$file"
164         return 1
165       fi
166       { while read -r k v; do
167           [ "$k" = "$key" ] \
168           && printf '%s\t%s\n' "$key" "$v$value" \
169           || printf '%s\t%s\n' "$k" "$v"
170         done <"$file" 2>&-
171       } >"${file}.$$.tmp"
172       mv "${file}.$$.tmp" "${file}"
173       RELEASE "$file"
174       return 0
175       ;;
176     delete|remove)
177       key="$(STRING "$1")"
178       LOCK "$file" || return 1
179       { while read -r k v; do
180           [ "$k" = "$key" ] || printf '%s\t%s\n' "$k" "$v"
181         done <"$file" 2>&-
182       } >"${file}.$$.tmp"
183       mv "${file}.$$.tmp" "${file}"
184       RELEASE "$file"
185       return 0
186       ;;
187   esac
188 }