123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- #
- # subroutines
- #
- # Common subroutines for the Dragora GNU/Linux-Libre website scripts
- #
- #
- # Copyright (C) 2021, 2022 Michael Siegel
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- ## Error handling
- _abort() {
- ## Run _cleanup if "$TMP_DIR" exists and exit the script with a return code
- ## indicating an error
- _cleanup
- exit 1
- }
- _cleanup() {
- ## Remove any temporary files
- [[ -d "$TMP_DIR" ]] && rm -rf -- "${TMP_DIR:?}"/*
- }
- _perr() {
- ## Print a message to stderr
- printf '%s\n' "$PROGNAME: $1" >&2
- # Don't print PROGNAME for error messages in interactive mode
- }
- ## Environment checks
- _command_available() {
- ## Tell whether the given external commands are available
- local cmd=
- local cmd_path=
- local err=0
- while [[ "$#" -gt 0 ]]
- do
- cmd="$1"
- cmd_path="$(command -v -- "$cmd")"
- if [[ -z "$cmd_path" ]]
- then
- _perr "command not found -- '$cmd'"
- err=1
- elif [[ ! -x "$cmd_path" ]]
- then
- _perr "command not executable -- '$cmd_path'"
- err=1
- fi
- shift
- done
- [[ "$err" -eq 1 ]] && return 1
- return 0
- }
- _command_is_gnu() {
- ## Tell whether the given external commands are from GNU
- local cmd=
- local err=0
- local gnu_version=
- while [[ "$#" -gt 0 ]]
- do
- cmd="$1"
- case "$cmd" in
- find)
- gnu_version='^find (GNU findutils)'
- ;;
- sed)
- gnu_version='^sed (GNU sed)'
- ;;
- stat)
- gnu_version='^stat (GNU coreutils)'
- ;;
- esac
- "$cmd" --version | sed 1q | grep -q -- "$gnu_version" || \
- { _perr "GNU version of command required -- '$cmd'"; err=1; }
- shift
- done
- [[ "$err" -gt 0 ]] && return 1
- return 0
- }
- _probe_dirs_files() {
- ## Probe for existence of needed directories and files
- # Printing error messages right away and only returning at the end because we
- # want to catch all missing files and directories at once.
- local f= # init later?
- local err=0
- # Probe for top-level source directory
- if [[ ! -d "$SOURCE_DIR" ]]
- then
- _perr "directory not found -- '$SOURCE_DIR'"
- err=1
- else
- # Probe for sub-directories and files
- local d=
- for d in "$COMMON_DIR" "$PAGES_DIR"
- do
- if [[ ! -d "$d" ]]
- then
- _perr "directory not found -- '$d'"
- err=1
- continue
- fi
- case "$d" in
- "$COMMON_DIR")
- local sd=
- for sd in "$d/$CSS_DIR" "$d/$IMG_DIR"
- do
- if [[ ! -d "$sd" ]]
- then
- _perr "directory not found -- '$sd'"
- err=1
- continue
- fi
- case "$sd" in
- "$d/$CSS_DIR")
- f="$d/$CSS_DIR/$STYLESHEET"
- [[ -f "$f" ]] || { _perr "stylesheet not found -- '$f'"; err=1; }
- ;;
- "$d/$IMG_DIR")
- f="$d/$IMG_DIR/$FAVICON"
- [[ -f "$f" ]] || { _perr "favicon not found -- '$f'"; err=1; }
- f="$d/$IMG_DIR/$LOGO"
- [[ -f "$f" ]] || { _perr "logo not found -- '$f'"; err=1; }
- ;;
- esac
- done
- unset sd
- if [[ ! -f "$NAVTREE_FILE" ]]
- then
- _perr "navtree not found -- '$NAVTREE_FILE'"
- err=1
- fi
- ;;
- "$PAGES_DIR")
- [[ -z "$(find -- "$PAGES_DIR" -mindepth 1 -maxdepth 1)" ]] && \
- { _perr "directory must not be empty -- '$PAGES_DIR'"; err=1; }
- if [[ ! -d "$PAGES_DIR_MASTER" ]]
- then
- _perr "master page directory not found -- '$PAGES_DIR_MASTER'"
- err=1
- else
- # Check whether master tree is basically sane
- for f in "$HEADER_PARAMS" "$FOOTER_PARAMS" "$HOME_PAGE"
- do
- f="$PAGES_DIR_MASTER/$f"
- [[ -f "$f" ]] || { _perr "file not found -- '$f'"; err=1; }
- # Improve error message.
- done
- # Determine if subtrees of all page directories are identical to
- # master subtree.
- mkdir -p -- "$TMP_DIR" || err=1
- find -- "$PAGES_DIR_MASTER" -printf '%P\n' | sort \
- > "$TMP_DIR"/treemaster
- local pd=
- for pd in $(_get_lang_dirs)
- do
- find -- "$PAGES_DIR/$pd" -printf '%P\n' | sort \
- > "$TMP_DIR"/treecopy
- cmp -s -- "$TMP_DIR"/treemaster "$TMP_DIR"/treecopy || { _perr \
- "'$PAGES_DIR/$pd': subtree not identical to '$PAGES_DIR_MASTER'";
- err=1; }
- # Improve error message
- done
- unset pd
- fi
- ;;
- esac
- done
- fi
- mkdir -p -- "$OUTPUT_DIR" || err=1
- [[ "$err" -gt 0 ]] && return 1
- return 0
- }
- _env_checks() {
- ## Perform environment checks
- _command_available $TOOLS $GNU_TOOLS || return 1
- _command_is_gnu $GNU_TOOLS || return 1
- _probe_dirs_files || return 1
- }
- ## Retrieval
- _get_lang_dirs() {
- ## Find all language-specific page directories
- find -- "$PAGES_DIR" -maxdepth 1 -type d -not -name '.*' -name '??' | \
- sed 's/^.*\///'
- # `-not` is not POSIX, but widely supported.
- }
- _get_navtree() {
- ## Read all site navigation items into a global immutable array
- navtree=()
- local line=
- while read -r line
- do
- [[ "$line" =~ (^#)|(^[[:space:]]*$) ]] || navtree+=("$line")
- done < "$NAVTREE_FILE"
- unset line
- declare -r navtree
- }
- _get_pagetree() {
- ## Read page tree into a global immutable array
- pagetree=()
- local page_dir=
- while read -r page_dir
- do
- pagetree+=("${page_dir}/")
- # Append a slash so that the amount of slashes immediatley indicates the
- # sublevel of a page directory.
- # ($PAGES_DIR_MASTER).
- done < <(find -- "$PAGES_DIR_MASTER" -type d | sort | \
- sed -e "s,${PAGES_DIR_MASTER}/*,," -e '/^doc\/handbook/ d')
- unset page_dir
- declare -r pagetree
- }
- _get_sublevel() {
- local slashes="${1//[^\/]}"
- printf '%s' "${#slashes}"
- }
- _get_tree_flow() {
- ## [Add description]
- tree_flow=()
- local item=
- for item in "$@"
- do
- tree_flow+=("$(_get_sublevel "$item")")
- done
- unset item
- # echo "${tree_flow[@]}" ##TESTING
- declare -r tree_flow # Why on earth does this prevent echo-ing the whole
- # array later (like above)?!
- }
- ## Display
- _mk_tree_item_indent() {
- ## Compile the indentation string for a tree item
- local -r indent_base_cont='├─ ' # Find better name?
- local -r indent_base_end='└─ ' # Find better name?
- local -r indent_space=' ' # 3 spaces
- local -r indent_trunk='│ '
- local -r item="$1"
- local -r index_item="$2"
- local -r index_start="$(($index_item + 1))"
- local -r index_end="$((${#tree_flow[@]} - 1))"
- local indent=
- local indent_base=
- local next_lower_pos=
- local next_prev_pos=
- local next_same_pos=
- local prev=
- local sublevel=
- sublevel="${tree_flow[index_item]}"
- # Using tree_flow because it can represent pagetree as well as navtree.
- # Determine indent_base
- prev="$((sublevel - 1))"
- local i=
- for ((i="$index_start"; i<="$index_end"; ++i))
- do
- if [[ -z "$next_same_pos" ]] && [[ "${tree_flow[i]}" -eq "$sublevel" ]]
- then
- next_same_pos="$i"
- elif [[ -z "$next_prev_pos" ]] && [[ "${tree_flow[i]}" -eq "$prev" ]]
- then
- next_prev_pos="$i"
- fi
- done
- unset i
- if [[ -z "$next_same_pos" ]]
- then
- indent_base="$indent_base_end"
- elif [[ -n "$next_prev_pos" && "$next_prev_pos" -lt "$next_same_pos" ]]
- then
- indent_base="$indent_base_end"
- else
- indent_base="$indent_base_cont"
- fi
- indent="$indent_base"
- # Compile additonal indentation string for sublevels greater than 1 (prepend
- # to $indent_base)
- local s=
- for ((s="$sublevel"; s > 1; --s))
- do
- prev="$((s - 1))"
- next_prev_pos=
- next_lower_pos=
- # Determine whether and where the previous sublevel and any sublevel lower
- # than that occurs later in the tree.
- local i=
- for ((i="$index_start"; i<="$index_end"; ++i))
- do
- if [[ -z "$next_prev_pos" ]] && [[ "${tree_flow[i]}" -eq "$prev" ]]
- then
- next_prev_pos="$i"
- elif [[ -z "$next_lower_pos" ]] && [[ "${tree_flow[i]}" -lt "$prev" ]]
- then
- next_lower_pos="$i"
- fi
- done
- unset i
- if [[ -z "$next_prev_pos" ]]
- # No further parent occurs
- then
- indent="${indent_space}${indent}"
- elif [[ -n "$next_prev_pos" ]] && [[ -z "$next_lower_pos" ]]
- # Further parent occurs, but no further ancestor occurs
- then
- indent="${indent_trunk}${indent}"
- elif [[ -n "$next_prev_pos" ]] && [[ -n "$next_lower_pos" ]]
- # Further parent occurs and further ancestor occurs
- then
- # Next parent occurs before next ancestor:
- if [[ "$next_prev_pos" -lt "$next_lower_pos" ]]
- then
- indent="${indent_trunk}${indent}"
- # Next parent occurs after next ancestor:
- elif [[ "$next_prev_pos" -gt "$next_lower_pos" ]]
- then
- indent="${indent_space}${indent}"
- fi
- fi
- done
- unset s
- printf '%s' "$indent"
- }
- _show_pagetree() {
- local -r index_start=0
- local indent=
- local item=
- local tree_mode=
- local -r index_end="$((${#pagetree[@]} - 1))"
- local -r index_width="${#index_end}"
- case "$1" in
- -add)
- tree_mode=add
- ;;
- -del)
- tree_mode=del
- ;;
- *)
- _perr "invalid option -- '$1'"
- return 1
- ;;
- esac
- _get_tree_flow "${pagetree[@]}" # _mk_tree_item_indent needs this
- local i=
- for ((i="$index_start"; i<="$index_end"; ++i))
- do
- if [[ "$i" -eq 0 ]]
- then
- case "$tree_mode" in
- add)
- item='[new top-level page]'
- ;;
- del)
- continue
- ;;
- esac
- else
- item="${pagetree[i]%/}" # Omit trailing slash before omitting
- # everything up until the last slash below.
- fi
- indent="$(_mk_tree_item_indent "$item" "$i")"
- printf " %${index_width}s %s%s%s\n" "$i" "$indent" "${item##*/}"
- done
- unset i
- }
|