123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- #!/bin/bash
- # This file is part of GNU libmicrohttpd
- # Copyright (C) 2024 Karlson2k (Evgeny Grin), Christian Grothoff
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public
- # License as published by the Free Software Foundation; either
- # version 2.1 of the License, or (at your option) any later version.
- # This library is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- # Lesser General Public License for more details.
- # You should have received a copy of the GNU Lesser General Public
- # License along with this library; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin Street, Fifth Floor,
- # Boston, MA 02110-1301 USA
- export LC_ALL=C
- export LANG=C
- if command -v recsel >/dev/null 2>&1 ; then : ; else
- echo "Error: The command 'recsel' is missing. Please install recutils." >&2
- exit 1
- fi
- if command -v recset >/dev/null 2>&1 ; then : ; else
- echo "Error: The command 'recset' is missing. Please install recutils." >&2
- exit 1
- fi
- if command -v recfmt >/dev/null 2>&1 ; then : ; else
- echo "Error: The command 'recfmt' is missing. Please install recutils." >&2
- exit 1
- fi
- if (( 0 + 1 )) 2>/dev/null && test "$(( 2 + 2 ))" = "4" 2>/dev/null ; then : ; else
- echo "Error: Built-in shell math is required" >&2
- exit 1
- fi
- if declare -a ARGS 2>/dev/null ; then : ; else
- echo "Error: Indexed arrays support is required" >&2
- exit 1
- fi
- if [[ "false" ]] ; then : ; else
- echo "Error: Compound command support is required" >&2
- exit 1
- fi
- if [[ $'\n' = '
- ' ]] ; then : ; else
- echo "Error: ANSI-C quoting support is required" >&2
- exit 1
- fi
- if [[ "abc" =~ 'b' ]] && [[ "xyz" =~ [x-z]{3} ]] ; then : ; else
- echo "Error: Regular expression match support is required" >&2
- exit 1
- fi
- test_var="abc ABC Abc"
- if test "${test_var^}" = "Abc ABC Abc" && test "${test_var^^}" = "ABC ABC ABC" && test "${test_var,,}" = "abc abc abc"; then : ; else
- echo "Error: Shell upper- and lowercase variable conversion support required" >&2
- exit 1
- fi
- if test "${test_var// /_}" = "abc_ABC_Abc" ; then : ; else
- echo "Error: Shell variable replacement conversion support required" >&2
- exit 1
- fi
- unset test_var
- if [[ "${0}" =~ (^|/|\\)'d_options.sh'$ ]]; then
- options_type='daemon'
- elif [[ "${0}" =~ (^|/|\\)'r_options.sh'$ ]]; then
- options_type='response'
- else
- echo "Wrong name ('$0') of the script file" >&2
- exit 1
- fi
- # parameters
- max_width=79
- if [[ "$options_type" = 'daemon' ]]; then
- input_rec='d_options.rec'
- rec_name='D_Options'
- hdr_marker="Daemon"
- one_char_opt_name='D'
- short_opt_name="$options_type"
- else
- input_rec="r_options.rec"
- rec_name='R_Options'
- hdr_marker="Response"
- one_char_opt_name='R'
- short_opt_name="resp"
- fi
- tmp_rec_name="${rec_name}_preproc"
- tmp_rec_file="${input_rec%.rec}_preproc.rec"
- # fixed strings
- flat_arg_descr='the value of the parameter'
- err_exit() {
- local msg="$1"
- local err=$2
-
- [[ -z $msg ]] && msg="Error!"
- ( [[ -z $err ]] || (( err < 1 )) ) && err=2
- echo "$msg" >&2
- exit $err
- }
- # cut string an newline character
- cut_str_nl() {
- local str="$1"
- declare -g cut_str_nl_res=''
- if [[ "$str" =~ $'\n' ]]; then
- cut_str_nl_res="${str%%$'\n'*}"
- return 0
- fi
- return 1
- }
- # cut string to given length at word boundary if possible
- # process embedded new line characters
- cut_str_word () {
- local str="$1"
- local len=$2
- declare -g cut_str_word_res=''
- [[ $len -le 0 ]] && return 1
- if cut_str_nl "${str:0:$(( len + 1 ))}"; then
- cut_str_word_res="${cut_str_nl_res}"
- cut_str_word_res="${cut_str_word_res% }"
- return 0
- fi
- if [[ ${#str} -le $len ]]; then
- cut_str_word_res="${str}"
- return 0
- fi
- if [[ "${str:${len}:1}" = " " ]]; then
- cut_str_word_res="${str:0:${len}}"
- cut_str_word_res="${cut_str_word_res% }"
- return 0
- fi
- cut_str_word_res="${str:0:${len}}"
- cut_str_word_res="${cut_str_word_res% *}"
- cut_str_word_res="${cut_str_word_res% }"
- return 0
- }
- format_doxy() {
- local prefix1="$1" # first line prefix
- local desc="$2"
- local prefix2="$3" # prefix on all other lines
- local width="$4"
- local tmp_str
- declare -g format_doxy_res=''
- [[ -z $width ]] && width=$max_width
- prefix1="${prefix1%"${prefix1##*[! ]}"} " # force single trailing space
- [[ -z $prefix2 ]] && prefix2="$prefix1"
- [[ ${#prefix1} -ge $width ]] && err_exit "Too long prefix ('${prefix1}') for width $width."
- desc="${desc#"${desc%%[! ]*}"}"
- desc="${desc%"${desc##*[! ]}"}" # trim desc
- local width_r=$(( width - ${#prefix1} ))
- local tmp_str="${prefix1//?/ }" # Space-only string with the same length
- prefix2="
- ${prefix2}${tmp_str:${#prefix2}}"
- cut_str_word "$desc" $width_r || return 1
- format_doxy_res="${prefix1}${cut_str_word_res}"
- format_doxy_res="${format_doxy_res% }"
- desc="${desc:${#cut_str_word_res}}"
- desc="${desc#"${desc%%[! ]*}"}" # trim leading spaces
- desc="${desc#$'\n'}" # remove leading newline character
- while [[ -n "$desc" ]]; do
- cut_str_word "$desc" $width_r || return 1
- tmp_str="${prefix2}${cut_str_word_res}"
- format_doxy_res+="${tmp_str% }"
- desc="${desc:${#cut_str_word_res}}"
- desc="${desc#"${desc%%[! ]*}"}" # trim leading spaces
- desc="${desc#$'\n'}" # remove leading newline character
- done
- return 0
- }
- def_name_for_type() {
- case $1 in
- 'enum MHD_Bool') echo -n "bool_val";;
- 'unsigned int') echo -n "uint_val";;
- 'uint_fast64_t') echo -n "uint64_val";;
- 'uint_fast32_t') echo -n "uint32_val";;
- 'uint_fast16_t') echo -n "uint16_val";;
- 'size_t') echo -n "sizet_val";;
- *) local tp="${1,,}" && printf '%s' "${tp// /_}_val";;
- esac
- }
- capitalise_first() {
- local first_char="${1:0:1}"
- printf '%s' "${first_char^^}${1:1}"
- }
- echo "Trimming lines in '$input_rec'..."
- ${SED-sed} -E -e 's/ +$//' -i "$input_rec" || err_exit
- echo "OK"
- echo "Checking '$input_rec'..."
- recfix --check "$input_rec" || exit 3
- echo "OK"
- cat << _EOF_ > "$tmp_rec_file"
- %rec: ${tmp_rec_name}
- %key: EName
- %mandatory: Value
- %mandatory: Name
- %type: Value int
- %sort: Value
- %singular: EName UName SName Value
- _EOF_
- echo "Processing input file..."
- for N in $(recsel -t "${rec_name}" -R Value "$input_rec")
- do
- NAME=$(recsel -t "${rec_name}" -P Name -e "Value = $N" "$input_rec")
- if [[ -z $NAME ]]; then
- echo "The 'Name' field is empty for 'Value=$N'" >&2
- exit 2
- fi
- echo -n '.'
- COMMENT=$(recsel -t "${rec_name}" -P Comment -e "Value = $N" "$input_rec")
- if [[ -z $COMMENT ]]; then
- echo "The 'Comment' field is empty for '$NAME' ('Value=$N')" >&2
- exit 2
- fi
- TYPE=$(recsel -t "${rec_name}" -P Type -e "Value = $N" "$input_rec")
- EComment="" # The initial part of doxy comment for the enum value
- EName="" # The name of the enum value
- UName="" # The name of the union member
- UType="" # The type of the union member
- UTypeSp='' # The type of the union member with space at the end for non-pointers
- SComment="" # The doxy comment for the set macro/function
- SName="" # The name of the set macro/function
- MArguments="" # The arguments for the macro
- CLBody="" # The Compound Literal body (for the set macro)
- SFArguments="" # The arguments for the static function
- SFBody="" # The static function body
- StBody='' # The data struct body (if any)
-
- nested='maybe' # The option has nested struct parameters ('yes'/'no'/'maybe')
- clean_name="${NAME//_/ }"
- clean_name="${clean_name,,}" # Lowercase space-delimited
- echo -n "$N: ${clean_name// /_}"
- EName="${clean_name^^}"
- EName="MHD_${one_char_opt_name^^}_O_${EName// /_}" # Uppercase '_'-joined
-
- UName="v_${clean_name// /_}" # lowercase '_'-joined
-
- SName="${clean_name^^}"
- SName="MHD_${one_char_opt_name^^}_OPTION_${SName// /_}" # Uppercase '_'-joined
-
- format_doxy ' * ' "$COMMENT" || err_exit
- EComment="$format_doxy_res"
-
- format_doxy ' * ' "$COMMENT" || err_exit
- SComment="$format_doxy_res"
-
- # Read option parameters
- ARGS=( )
- DESCRS=( )
- MEMBERS=( )
- M=1
- while
- ARGM=$(recsel -t "${rec_name}" -P Argument${M} -e "Value = $N" "$input_rec")
- [[ -n $ARGM ]]
- do
- ARGS[$M]="$ARGM"
- DESCRS[$M]=$(recsel -t "${rec_name}" -P Description${M} -e "Value = $N" "$input_rec")
- MEMBERS[$M]=$(recsel -t "${rec_name}" -P Member${M} -e "Value = $N" "$input_rec")
- (( M++ ))
- echo -n '.'
- done
-
- # Basic data checks
- (( M - 1 == ${#ARGS[@]} )) || err_exit
-
- if [[ ${#ARGS[@]} -eq 0 ]]; then
- [[ -z $TYPE ]] && err_exit "No 'Argument1' is specified for '$NAME' ('Value=$N') without 'Type'" >&2
- nested='no'
- ARGS[1]=''
- DESCRS[1]="$flat_arg_descr"
- MEMBERS[1]=''
- elif [[ ${#ARGS[@]} -eq 1 ]]; then
- nested='no'
- else
- nested='yes'
- [[ -z $TYPE ]] && err_exit "No 'Type' is specified for non-flat (nested, with multiple parameters) '$NAME' ('Value=$N')" >&2
- fi
-
- # Process option parameters
- for (( M=1 ; M <= ${#ARGS[@]} ; M++ )) ; do
-
- arg_name='' # The name of the current argument
- arg_type='' # The type of the data of the current argument
- arg_descr='' # The description of the current argument
- nest_member='' # The name of the member of the nested structure
- [[ "${ARGS[$M]}" =~ (^' '|' '$) ]] && err_exit "'Argument${M}' value '${ARGS[$M]}' for '$NAME' ('Value=$N') is not trimmed"
- [[ "${DESCRS[$M]}" =~ (^' '|' '$) ]] && err_exit "'Description${M}' value '${DESCRS[$M]}' for '$NAME' ('Value=$N') is not trimmed"
- [[ "${MEMBERS[$M]}" =~ (^' '|' '$) ]] && err_exit "'Member${M}' value '${MEMBERS[$M]}' for '$NAME' ('Value=$N') is not trimmed"
- # Pre-process parameters data
- if [[ -n ${ARGS[$M]} ]]; then
- arg_name="${ARGS[$M]##* }"
- arg_name="${arg_name#\*}"
- arg_type="${ARGS[$M]%${arg_name}}"
- arg_type="${arg_type% }"
- else
- if [[ $nested = 'yes' ]]; then
- err_exit "Empty or no 'Argument${M}' ('$arg_type') for '$NAME' ('Value=$N')"
- else
- [[ -z $TYPE ]] && err_exit "No 'Argument1' is specified for '$NAME' ('Value=$N') without 'Type'" >&2
- arg_name="$(def_name_for_type "$TYPE")"
- arg_type="$TYPE"
- fi
- fi
- arg_descr="${DESCRS[$M]}"
- nest_membr="${MEMBERS[$M]}"
- [[ -z $arg_name ]] && err_exit # Should not happen
- if [[ $nested = 'yes' ]]; then
- # non-flat, nested
- [[ -z $arg_type ]] && err_exit "No argument type in 'Argument${M}' ('${ARGS[$M]}') for $NAME ('Value=$N')"
- [[ $TYPE = $arg_type ]] && \
- err_exit "The same 'Type' and type for in 'Argument${M}' ('$arg_type') used for non-flat (nested) '$NAME' ('Value=$N')"
- [[ -z $arg_descr ]] && \
- err_exit "Empty or no 'Description${M}' for argument '${ARGS[$M]}' for non-flat (nested) '$NAME' ('Value=$N')"
- if [[ "$arg_name" = "$nest_membr" ]]; then
- echo "The name for 'Argument${M}' ('${ARGS[$M]}') is the same as the 'Member${M}' ('$nest_membr') for non-flat (nested) '$NAME' ('Value=$N')" >&2
- nest_membr="v_${nest_membr}"
- echo "Auto-correcting the struct member name to '$nest_membr' to avoid wrong macro expansion" >&2
- fi
- else
- # flat, non-nested
- if [[ -z $arg_type ]]; then
- if [[ -z $TYPE ]]; then
- err_exit "Both 'Type' and type for in 'Argument${M}' ('${ARGS[$M]}') are empty for '$NAME' ('Value=$N')"
- else
- arg_type="$TYPE"
- fi
- else
- if [[ -z $TYPE ]]; then
- TYPE="$arg_type"
- elif [[ $TYPE != $arg_type ]]; then
- err_exit "Different 'Type' ('$TYPE') and type for in 'Argument${M}' ('$arg_type') used for '$NAME' ('Value=$N')"
- fi
- fi
- [[ -z $arg_descr ]] && arg_descr="$flat_arg_descr"
- [[ -n $nest_membr ]] && \
- err_exit "'Member${M}' is provided for non-nested (flat) '$NAME' ('Value=$N')"
- fi
-
- [[ "$arg_type" =~ \*$ ]] || arg_type+=' ' # Position '*' correctly
- [[ "$arg_name" = "${UName}" ]] && err_exit "The name ('$arg_name') of the argument 'Argument${M}' ('${ARGS[$M]}') for '$NAME' ('Value=$N') conflicts with the union member name ('${UName}'). Macro would not work."
- [[ "$arg_name" = "opt" ]] && err_exit "The name ('$arg_name') of the argument 'Argument${M}' ('${ARGS[$M]}') for '$NAME' ('Value=$N') conflicts with the option struct member name ('opt'). Macro would not work."
- [[ "$arg_name" = "val" ]] && err_exit "The name ('$arg_name') of the argument 'Argument${M}' ('${ARGS[$M]}') for '$NAME' ('Value=$N') conflicts with the option struct member name ('val'). Macro would not work."
- [[ "${arg_name,,}" = "${arg_name}" ]] || err_exit "The name ('$arg_name') of the argument 'Argument${M}' ('${ARGS[$M]}') for '$NAME' ('Value=$N') has capital letter(s)"
- [[ $nested = 'yes' ]] && [[ -z $nest_membr ]] && nest_membr="v_${arg_name}"
- [[ "${#arg_name}" -ge 15 ]] && echo "Warning: too long (${#arg_name} chars) parameter name '${arg_name}'." >&2
-
- [[ $M -gt 1 ]] && [[ $nested = 'no' ]] && err_exit
-
- # Use parameters data
-
- format_doxy ' * @param '"$arg_name " "$arg_descr" ' * '|| err_exit
- SComment+=$'\n'"$format_doxy_res"
-
- [[ $M -gt 1 ]] && MArguments+=','
- MArguments+="$arg_name"
-
- if [[ $nested = 'yes' ]]; then
- [[ $M -gt 1 ]] && SFArguments+=','
- SFArguments+=$'\n'" ${arg_type}$arg_name"
- else
- if (( ${#SName} + ${#arg_type} + ${#arg_name} <= $max_width )); then
- SFArguments+="${arg_type}$arg_name"
- else
- SFArguments+=$'\n'" ${arg_type}$arg_name"
- fi
- fi
-
- #[[ $M -gt 1 ]] && CLBody+=', \'$'\n'" "
- [[ $M -gt 1 ]] && CLBody+=', \##removeme##'$'\n'" " # '##removeme##' is a workaround for requtils bug
- CLBody+=".val.${UName}"
- [[ $nested = 'yes' ]] && CLBody+=".${nest_membr}"
- CLBody+=" = ($arg_name)"
- [[ $M -gt 1 ]] && SFBody+=$'\n'" "
- SFBody+="opt_val.val.${UName}"
- [[ $nested = 'yes' ]] && SFBody+=".${nest_membr}"
- SFBody+=" = ${arg_name};"
-
- if [[ $nested = 'yes' ]] && [[ "$TYPE" =~ ^'struct ' ]]; then
- StBody+=$'\n'
- StBody+=" /**"$'\n'
- format_doxy ' * ' "$(capitalise_first "$arg_descr")" || err_exit
- StBody+="$format_doxy_res"$'\n'" */"$'\n'
- StBody+=" ${arg_type}$nest_membr;"
- fi
- echo -n '.'
- done
-
- UType="$TYPE"
- if [[ $nested = 'yes' ]] && [[ "$TYPE" =~ ^'struct ' ]]; then
- need_struct_decl='yes'
- else
- need_struct_decl='no'
- fi
- [[ "$UType" =~ \*$ ]] && UTypeSp="$UType" || UTypeSp="$UType " # Position '*' correctly
-
- recins -t "${tmp_rec_name}" \
- -f Name -v "$NAME" \
- -f Value -v "$N" \
- -f hdr_marker -v "$hdr_marker" \
- -f EComment -v "$EComment" \
- -f EName -v "$EName" \
- -f UName -v "$UName" \
- -f UType -v "$UType" \
- -f UTypeSp -v "$UTypeSp" \
- -f SComment -v "$SComment" \
- -f SName -v "$SName" \
- -f MArguments -v "$MArguments" \
- -f CLBody -v "$CLBody" \
- -f SFArguments -v "$SFArguments" \
- -f SFBody -v "$SFBody" \
- -f StBody -v "$StBody" \
- --verbose "$tmp_rec_file" || err_exit
- echo '.'
- done
- echo "finished."
- echo "Updating header file..."
- header_name='microhttpd2.h'
- start_of_marker=" = MHD ${hdr_marker} Option "
- end_of_start_marker=' below are generated automatically = '
- end_of_end_marker=' above are generated automatically = '
- I=0
- cp "../src/include/${header_name}" "./${header_name}" || err_exit
- middle_of_marker='enum values'
- echo "${middle_of_marker}..."
- in_file="${header_name}"
- out_file="${header_name%.h}_tmp$((++I)).h"
- recfmt -f options_enum.template < "$tmp_rec_file" > "header_insert${I}.h" || err_exit
- middle_of_marker='enum values'
- start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit
- ${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h
- }
- /'"$end_marker"'/p
- /'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit
- middle_of_marker='structures'
- echo "${middle_of_marker}..."
- in_file="${out_file}"
- out_file="${header_name%.h}_tmp$((++I)).h"
- recsel -e "StBody != ''" "$tmp_rec_file" | recfmt -f options_struct.template > "header_insert${I}.h" || err_exit
- start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit
- ${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h
- }
- /'"$end_marker"'/p
- /'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit
- middle_of_marker='union members'
- echo "${middle_of_marker}..."
- in_file="${out_file}"
- out_file="${header_name%.h}_tmp$((++I)).h"
- recfmt -f options_union.template < "$tmp_rec_file" > "header_insert${I}.h" || err_exit
- start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit
- ${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h
- }
- /'"$end_marker"'/p
- /'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit
- middle_of_marker='macros'
- echo "${middle_of_marker}..."
- in_file="${out_file}"
- out_file="${header_name%.h}_tmp$((++I)).h"
- recfmt -f options_macro.template < "$tmp_rec_file" | ${SED-sed} -e 's/##removeme##//g' - > "header_insert${I}.h" || err_exit
- start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit
- ${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h
- }
- /'"$end_marker"'/p
- /'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit
- middle_of_marker='static functions'
- echo "${middle_of_marker}..."
- in_file="${out_file}"
- out_file="${header_name%.h}_tmp$((++I)).h"
- recfmt -f options_func.template < "$tmp_rec_file" > "header_insert${I}.h" || err_exit
- start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit
- ${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h
- }
- /'"$end_marker"'/p
- /'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit
- echo "finished."
- if diff "../src/include/${header_name}" "${out_file}" > /dev/null; then
- echo "The updated header '${header_name}' content is equal to the previous content, the file remains the same."
- else
- mv "${out_file}" "../src/include/${header_name}" || err_exit
- echo "The header '${header_name}' has been updated with the new content."
- fi
- echo "Cleanup..."
- rm -f "$tmp_rec_file" ${header_name%.h}_tmp?.h header_insert?.h
- echo "completed."
|