]> git.plutz.net Git - serve0/blob - storage.sh
Squashed 'cgilite/' changes from 1d27862..6bdb2db
[serve0] / storage.sh
1 #!/bin/sh
2
3 # Copyright 2018, 2019, 2021 Paul Hänsch
4 #
5 # This is a file format helper, part of CGIlite.
6
7 # CGIlite is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # CGIlite is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with CGIlite.  If not, see <http://www.gnu.org/licenses/>. 
19
20 [ -n "$include_storage" ] && return 0
21 include_storage="$0"
22
23 CR="\r"
24 BR='
25 '
26
27 LOCK(){
28   local lock="${1}.lock" timeout="${2-20}" block
29
30   if [ \! -w "${lock%/*}" ] || [ -e "$lock" -a \! -f "$lock" ]; then
31     debug "Impossible to get lock: $lock"
32     return 1
33   fi
34
35   while [ $timeout -gt 0 ]; do
36     printf '%i\n' $$ >>"${lock}"
37     read block <"$lock"
38     if [ "$block" = $$ ]; then
39       return 0
40     elif ! { ps -eo pid |grep -qwF "$block"; }; then
41       debug "Trying to override stale lock: $lock"
42       if LOCK "$lock" 1; then
43         rm -- "$lock"
44         RELEASE "$lock"
45       fi
46     else
47       timeout=$((timeout - 1))
48       [ $timeout -gt 0 ] && sleep 1
49     fi
50   done
51
52   debug "Timeout while trying to get lock: $lock"
53   return 1
54 }
55
56 RELEASE(){
57   local lock="${1}.lock" block
58
59   read block <"$lock"
60   if [ "$block" = $$ ]; then
61     rm -- "$lock"
62     return 0
63   else
64     debug "Refusing to release foreign lock: $lock"
65     return 1
66   fi
67 }
68
69 STRING(){
70   local in out=''
71   [ $# -gt 0 ] && in="$*" || in="$(cat)"
72   while [ "$in" ]; do case $in in
73     \\*) out="${out}\\\\"; in="${in#\\}" ;;
74     "$BR"*) out="${out}\\n"; in="${in#${BR}}" ;;
75     "$CR"*) out="${out}\\r"; in="${in#${CR}}" ;;
76     "   "*) out="${out}\\t"; in="${in#  }" ;;
77     +*) out="${out}\\+"; in="${in#+}" ;;
78     " "*) out="${out}+"; in="${in# }" ;;
79     *) out="${out}${in%%[\\${CR}${BR}   + ]*}"; in="${in#"${in%%[\\${BR}${CR}   + ]*}"}" ;;
80   esac; done
81   printf '%s' "${out:-\\}"
82 }
83
84 UNSTRING(){
85   local in out=''
86   [ $# -gt 0 ] && in="$*" || in="$(cat)"
87   while [ "$in" ]; do case $in in
88     \\\\*) out="${out}\\"; in="${in#\\\\}" ;;
89     \\n*) out="${out}${BR}"; in="${in#\\n}" ;;
90     \\r*) out="${out}${CR}"; in="${in#\\r}" ;;
91     \\t*) out="${out}   "; in="${in#\\t}" ;;
92     \\+*) out="${out}+"; in="${in#\\+}" ;;
93     +*) out="${out} "; in="${in#+}" ;;
94     \\*) in="${in#\\}" ;;
95     *) out="${out}${in%%[\\+]*}"; in="${in#"${in%%[\\+]*}"}" ;;
96   esac; done
97   printf '%s\n' "$out"
98 }
99
100 DBM() {
101   local file="$1" cmd="$2"
102   local k v key value
103   shift 2;
104
105   case "$cmd" in
106     check|contains)
107       key="$(STRING "$1")"
108       while read -r k v; do if [ "$k" = "$key" ]; then
109         return 0
110       fi; done <"$file" 2>&-
111       return 1
112       ;;
113     get)
114       key="$(STRING "$1")"
115       while read -r k v; do if [ "$k" = "$key" ]; then
116         UNSTRING "$v"
117         return 0
118       fi; done <"$file" 2>&-
119       return 1
120       ;;
121     set|store)
122       key="$(STRING "$1")" value="$(STRING "$2")"
123       LOCK "$file" || return 1
124       { while read -r k v; do
125           [ "$k" = "$key" ] || printf '%s\t%s\n' "$k" "$v"
126         done <"$file" 2>&-
127         printf '%s\t%s\n' "$key" "$value"
128       } >"${file}.$$.tmp"
129       mv "${file}.$$.tmp" "${file}"
130       RELEASE "$file"
131       return 0
132       ;;
133     add|insert)
134       k="$1" key="$(STRING "$1")" value="$(STRING "$2")"
135       LOCK "$file" || return 1
136       if DBM "$file" check "$k"; then
137         RELEASE "$file"
138         return 1
139       else
140         printf '%s\t%s\n' "$key" "$value" >>"${file}"
141         RELEASE "$file"
142         return 0
143       fi
144       ;;
145     update|replace)
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       fi
152       { while read -r k v; do
153           [ "$k" = "$key" ] \
154           && printf '%s\t%s\n' "$key" "$value" \
155           || printf '%s\t%s\n' "$k" "$v"
156         done <"$file" 2>&-
157       } >"${file}.$$.tmp"
158       mv "${file}.$$.tmp" "${file}"
159       RELEASE "$file"
160       return 0
161       ;;
162     append)
163       key="$(STRING "$1")" value="$(STRING "$2")"
164       LOCK "$file" || return 1
165       if ! DBM "$file" check "$1"; then
166         RELEASE "$file"
167         return 1
168       fi
169       { while read -r k v; do
170           [ "$k" = "$key" ] \
171           && printf '%s\t%s\n' "$key" "$v$value" \
172           || printf '%s\t%s\n' "$k" "$v"
173         done <"$file" 2>&-
174       } >"${file}.$$.tmp"
175       mv "${file}.$$.tmp" "${file}"
176       RELEASE "$file"
177       return 0
178       ;;
179     delete|remove)
180       key="$(STRING "$1")"
181       LOCK "$file" || return 1
182       { while read -r k v; do
183           [ "$k" = "$key" ] || printf '%s\t%s\n' "$k" "$v"
184         done <"$file" 2>&-
185       } >"${file}.$$.tmp"
186       mv "${file}.$$.tmp" "${file}"
187       RELEASE "$file"
188       return 0
189       ;;
190   esac
191 }