#!/bin/sh
-# Copyright 2017 - 2020 Paul Hänsch
+# Copyright 2017 - 2021 Paul Hänsch
#
# This is CGIlite.
# A collection of posix shell functions for writing CGI scripts.
PATH(){
local str seg out
+ # normalize path
+ # read from stdin if no arguments are provided
+
[ $# -eq 0 ] && str="$(cat)" || str="$*"
while [ "$str" ]; do
seg=${str%%/*}; str="${str#*/}"
[ "${str}" -a "${out}" ] && printf %s "$out" || printf %s/ "${out%/}"
}
-HEX_DECODE='
- s;\\;\\\\;g; :HEXDECODE_X; s;%([^0-9A-F]);\\045\1;g; tHEXDECODE_X;
- # Hexadecimal { %00 - %FF } will be transformed to octal { \000 - \377 } for posix printf
- s;%[0123].;&\\0;g; s;%[4567].;&\\1;g; s;%[89AB].;&\\2;g; s;%[CDEF].;&\\3;g;
- s;%[048C][0-7]\\.;&0;g; s;%[048C][89A-F]\\.;&1;g; s;%[159D][0-7]\\.;&2;g; s;%[159D][89A-F]\\.;&3;g;
- s;%[26AE][0-7]\\.;&4;g; s;%[26AE][89A-F]\\.;&5;g; s;%[37BF][0-7]\\.;&6;g; s;%[37BF][89A-F]\\.;&7;g;
- s;%.[08](\\..);\10;g; s;%.[19](\\..);\11;g; s;%.[2A](\\..);\12;g; s;%.[3B](\\..);\13;g;
- s;%.[4C](\\..);\14;g; s;%.[5D](\\..);\15;g; s;%.[6E](\\..);\16;g; s;%.[7F](\\..);\17;g;
-'
-
HEX_DECODE(){
- printf -- "$(printf %s "$1" |sed -E "$HEX_DECODE")"
+ local pfx="$1" in="$2" out
+ # Print out Data encoded as Hex
+ #
+ # Arguments:
+ # pfx - required, prefix for a hex tupel, e.g. "\x", "%" "\", may be empty
+ # in - required, string to be decoded
+ #
+ # anything that does not constitute a tupel of valid Hex numerals
+ # will be copied to the output literally
+
+ while [ "$in" ]; do
+ case $in in
+ "$pfx"[0-9a-fA-F][0-9a-fA-F]*) in="${in#${pfx}}";;
+ \\*) in="${in#?}"; out="${out}\\\\"; continue;;
+ %*) in="${in#?}"; out="${out}%%"; continue;;
+ *) out="${out}${in%"${in#?}"}"; in="${in#?}"; continue;;
+ esac;
+
+ # Hex escaes for printf (e.g. \x41) are not portable
+ # The portable way for Hex output is transforming Hex to Octal
+ # (e.g. \x41 = \101)
+ case $in in
+ [0123]?*) out="${out}\\0";;
+ [4567]?*) out="${out}\\1";;
+ [89aAbB]?*) out="${out}\\2";;
+ [c-fC-F]?*) out="${out}\\3";;
+ esac
+ case $in in
+ [048cC][0-7]*) out="${out}0";;
+ [048cC][89a-fA-F]*) out="${out}1";;
+ [159dD][0-7]*) out="${out}2";;
+ [159dD][89a-fA-F]*) out="${out}3";;
+ [26aAeE][0-7]*) out="${out}4";;
+ [26aAeE][89a-fA-F]*) out="${out}5";;
+ [37bBfF][0-7]*) out="${out}6";;
+ [37bBfF][89a-fA-F]*) out="${out}7";;
+ esac
+ case $in in
+ ?[08]*) out="${out}0";;
+ ?[19]*) out="${out}1";;
+ ?[2aA]*) out="${out}2";;
+ ?[3bB]*) out="${out}3";;
+ ?[4cC]*) out="${out}4";;
+ ?[5dD]*) out="${out}5";;
+ ?[6eE]*) out="${out}6";;
+ ?[7fF]*) out="${out}7";;
+ esac
+ in="${in#?}"
+ in="${in#?}"
+ done
+ printf -- "$out"
}
if [ -z "$REQUEST_METHOD" ]; then
kill $cgilite_watchdog
SERVER_PROTOCOL="${SERVER_PROTOCOL%${CR}}"
- PATH_INFO="$(HEX_DECODE "${REQUEST_URI%\?*}" |PATH)"
+ PATH_INFO="$(HEX_DECODE % "${REQUEST_URI%\?*}" |PATH)"
[ "${REQUEST_URI}" = "${REQUEST_URI#*\?}" ] \
&& QUERY_STRING='' \
|| QUERY_STRING="${REQUEST_URI#*\?}"
str="${str#*&${name}=}"
cnt=$((cnt - 1))
done
- printf -- "$(printf %s "${str%%&*}" |sed -E 's;\+; ;g;'"$HEX_DECODE")"
+ HEX_DECODE % "$(printf %s "${str%%&*}" |tr + \ )"
}
cgilite_keys(){
}
COOKIE(){
- HEX_DECODE "$(
+ HEX_DECODE % "$(
HEADER Cookie \
| grep -oE '(^|; ?)'"$1"'=[^;]*' \
| sed -En "${2:-1}"'{s;^[^=]+=;;; s;\+; ;g; p;}'
#!/bin/sh
-# Copyright 2018, 2019 Paul Hänsch
+# Copyright 2018, 2019, 2021 Paul Hänsch
#
# This is a file format helper, part of CGIlite.
#
'
LOCK(){
- local lock timeout block
- lock="${1}.lock"
- timeout="${2-20}"
- if [ \! -w "${lock%/*}" ] || [ -e "$lock" -a \! -d "$lock" ]; then
+ 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 ! mkdir "$lock" 2>&-; do
- block="$(cat "$lock/pid" || printf 1)"
- if ! { ps -eo pid |grep -qwF "$block"; }; then
- debug "Overriding stale lock: $lock"
- break
- fi
- if [ $timeout -le 0 ]; then
- debug "Timeout while trying to get lock: $lock"
- return 1
+ 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
- timeout=$((timeout - 1))
- sleep 1
done
- printf '%i\n' $$ >"${lock}/pid"
- return 0
+
+ debug "Timeout while trying to get lock: $lock"
+ return 1
}
RELEASE(){
- local lock
- lock="${1}.lock"
- if [ "$(cat "$lock/pid")" = "$$" ]; then
- rm "$lock/pid"
- if ! rmdir "$lock"; then
- debug "Cannot remove tainted lock: $lock"
- printf '%i\n' $$ >"${lock}/pid"
- return 1
- fi
+ local lock="${1}.lock" block
+
+ read block <"$lock"
+ if [ "$block" = $$ ]; then
+ rm -- "$lock"
return 0
else
debug "Refusing to release foreign lock: $lock"