]> git.plutz.net Git - cgilite/blob - storage.sh
new function RXLITERAL() for escaping regex characters
[cgilite] / 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 RXLITERAL(){
98   # sed -E 's;[].*+?^${}()|\[];\\&;g'
99   local in out=''
100   [ $# -gt 0 ] && in="$*" || in="$(cat)"
101   while [ "$in" ]; do case $in in
102     [.+^\$\{\}\(\)\[\]\*\?\|\\]*)
103       out="${out}\\${in%"${in#?}"}"; in="${in#?}";
104       ;;
105     *)out="${out}${in%%[.+^\$\{\}\(\)\[\]\*\?\|\\]*}"
106       in="${in#"${in%%[.+^\$\{\}\(\)\[\]\*\?\|\\]*}"}"
107       ;;
108   esac; done
109   printf '%s\n' "$out"
110 }
111
112 DBM() {
113   local file="$1" cmd="$2"
114   local k v key value
115   shift 2;
116
117   case "$cmd" in
118     check|contains)
119       key="$(STRING "$1")"
120       while read -r k v; do if [ "$k" = "$key" ]; then
121         return 0
122       fi; done <"$file" 2>&-
123       return 1
124       ;;
125     get)
126       key="$(STRING "$1")"
127       while read -r k v; do if [ "$k" = "$key" ]; then
128         UNSTRING "$v"
129         return 0
130       fi; done <"$file" 2>&-
131       return 1
132       ;;
133     set|store)
134       key="$(STRING "$1")" value="$(STRING "$2")"
135       LOCK "$file" || return 1
136       { while read -r k v; do
137           [ "$k" = "$key" ] || printf '%s\t%s\n' "$k" "$v"
138         done <"$file" 2>&-
139         printf '%s\t%s\n' "$key" "$value"
140       } >"${file}.$$.tmp"
141       mv "${file}.$$.tmp" "${file}"
142       RELEASE "$file"
143       return 0
144       ;;
145     add|insert)
146       k="$1" key="$(STRING "$1")" value="$(STRING "$2")"
147       LOCK "$file" || return 1
148       if DBM "$file" check "$k"; then
149         RELEASE "$file"
150         return 1
151       else
152         printf '%s\t%s\n' "$key" "$value" >>"${file}"
153         RELEASE "$file"
154         return 0
155       fi
156       ;;
157     update|replace)
158       k="$1" key="$(STRING "$1")" value="$(STRING "$2")"
159       LOCK "$file" || return 1
160       if ! DBM "$file" check "$k"; then
161         RELEASE "$file"
162         return 1
163       fi
164       { while read -r k v; do
165           [ "$k" = "$key" ] \
166           && printf '%s\t%s\n' "$key" "$value" \
167           || printf '%s\t%s\n' "$k" "$v"
168         done <"$file" 2>&-
169       } >"${file}.$$.tmp"
170       mv "${file}.$$.tmp" "${file}"
171       RELEASE "$file"
172       return 0
173       ;;
174     append)
175       key="$(STRING "$1")" value="$(STRING "$2")"
176       LOCK "$file" || return 1
177       if ! DBM "$file" check "$1"; then
178         RELEASE "$file"
179         return 1
180       fi
181       { while read -r k v; do
182           [ "$k" = "$key" ] \
183           && printf '%s\t%s\n' "$key" "$v$value" \
184           || printf '%s\t%s\n' "$k" "$v"
185         done <"$file" 2>&-
186       } >"${file}.$$.tmp"
187       mv "${file}.$$.tmp" "${file}"
188       RELEASE "$file"
189       return 0
190       ;;
191     delete|remove)
192       key="$(STRING "$1")"
193       LOCK "$file" || return 1
194       { while read -r k v; do
195           [ "$k" = "$key" ] || printf '%s\t%s\n' "$k" "$v"
196         done <"$file" 2>&-
197       } >"${file}.$$.tmp"
198       mv "${file}.$$.tmp" "${file}"
199       RELEASE "$file"
200       return 0
201       ;;
202   esac
203 }