#!/bin/bash

################################################################################################################################################################
#
# SBWG - steeph's bash website generator
# Last changed: 2021-03-21
# Version: See variable below
# https://log.steeph.de/entries/SBWG.html
#
# You can place this file in a directory in your $PATH or execute it from anywhere. It is independent from the website source directory.
#
################################################################################################################################################################

shopt -s extglob								# Needed for deleting old tagpages but no png files, can not be in a function.

version=0.8.6-wip

sitename="placeholder title"

url="https://www.example.com/"

# the directory that contains the source files/file structure - no trailing slash - can be overwritten with option -i
#shellbase="."
shellbase=$(pwd)

# base directory of the webserver - no trailing slash - "/" for web root
webbase="/" # Webbases other than / are not actually supported (yet?)

# Set default style
#style=style
style=robour

# default output directory (where the generated web site is written to) - can be overwritten with option -o
odir="${shellbase}/html"

# default width/height for image gallery thumbnails in pixels - I say default, but there's currently no option to overwrite this (except with styles).
thumbsize=200
thumbsizemini=60
previewsize=800

# number of entries per page - If a list of entries (tagpage or all.html) contains more than this amount of entries it's split in several pages.
perpage=10

# size of tag icons (in pixels, either one number for both width and height or two different numbers in the format "20x16")

#tagiconsize="20x16" # Is not used yet

# extra line(s) that should be inserted in every html header
headinsert=""
										# Set default style
scriptname="$(basename ${0})"


error_exit() {									# Function to exit with error code after a cleanup attempt. Used to abort with a
										#   message in case of an error.
  local str="${@}"
  printf '\xE2\x9D\x8C '"${str}"'\n'
  clean_up 1
}


clean_up() {									# Things that should be done if if the script crashes or is otherwise interupted.
  rm -rf "${tmpdir}"								# Delete our temporary directory with all contents.
  exit ${1}									# Exit with provided error code, with 0 if none was provided.
}


trap clean_up SIGHUP SIGINT SIGTERM						# Clean up if the script is killed.


v() {
  if option_set v; then								# Only if in verbose or very verbose or debug mode ...
    local str="${@}"
    printf '\xE2\x84\xB9  '"${str}"'\n'						#   ... then display this message with a info symbol in front of it.
  fi
}


vv() {
  if option_set_multi v; then							# Only if in very verbose or debug mode ...
    local str="${@}"
    printf '\xE2\x84\xB9  '"${str}"'\n'						#   ... then display this message with an info symbol in front of it.

  fi
}


d() {
  if option_set d; then								# Only if in debug mode ...
    local str="${@}"
#    printf '\xE2\x9D\x8C\xF0\x9F\x90\x9B '"${str}"'\n'				#   ... then display this message with a cross and a bug symbol in front of it.
     >&2 printf '\xF0\x9F\x90\x9B '"${str}"'\n'					#   ... then display this message with a bug symbol in front of it.
  fi
}

skip() {									# I use this function to disable single lines of code while debugging or testing.
  d "Skipping the following line:\n${@}"					# Print this line instead of executing it.
}

call_function_if_declared() {
  if declare -F "${1}" > /dev/null
  then
    v "Found hook '${1}'."
    ${1}
    return 0
  else
#    d "Hook '${1}' is not declared."
    return 1
  fi
}

grab_tag() {									# $1: list of tags, $2: tag name/type, $3: default value (optional)
  if [ ${#} -lt 2 ]; then return 1; fi						# If not at least 2 arguments are given, fail.
  local ret=$(echo "${1}" | grep -m 1 "^${2}:" | cut -c $((${#2}+2))-)		# Find tag if it exists, first match, cut off its name.
  ret="${ret:-$3}"								# If empty (no tag occurance found) use default/fallback value.
  echo "${ret}"
  return 0
}

looks_like_tag() {								# $1: string to check whether it looks like a tag definition
  local num=100
  case "${1:0:9}" in								# The first 9 characters of the line
    *([[:space:]])) num=1 ;;							# Contain only whitespaces/is practically empty
    +([[:alnum:]]):[^:space:]*) num=1 ;;&					# Contain a colon not followed by a whitespace
    author:*|cat:*|top:*|lang:*|created:*|edited:*|sort:*|title:*) num=0;;	# Start with of of these tag types
    "<!--"*) num=1 ;;								# Start with an HTML comment opening
    "-->") num=1 ;;								# Are only an HTML comment closing
    *) num=2 ;;
  esac
  return ${num}

#  if [[ ! ${1} = *[^[:space:]]* ]]
#  then				# If the line has a colon at the 9th place at the latest that is not followed by a whitespace or if the line is practically empty.
#    return 1
#  else if  ( [[ ${1} =~ ^.{1,8}[:] ]] && [[ ! ${1} = *[:][[:space:]]* ]] && [[ ! ${1} =~ ^["<!--"] ]] )
#    then
#      if [[ "${1}" == "author:*|cat:*|top:*|lang:*" ]]
#      then
#        return 1
#      else
#        return 0
#      fi
#    fi
#    return 2
#  fi
}

grab_tags() {									# $1: file path/name from which the tag header should be extracted
  local linenum=1
  local nontaglines=0
  local tags=""
  while IFS= read -r line
  do
    local n=$(looks_like_tag "${line}";echo $?)
    if [[ ${n} == 0 ]]
    then
      tags="${tags}"$'\n'"${line}"						# ... add it to the block that will be echoed to where the function was called from.
      nontaglines=0								# Set counter back to 0. Only care about successive lines that don't look like tags.
    else
      nontaglines=$((nontaglines + n))						# Keep track of how many lines in a row didn't look like a tagline.
    fi
    [[ ${nontaglines} -ge 3 ]] && break						# If it's three in a row already, don't bother to check the rest/following lines.
    linenum=$((linenum + 1))							# If the end of the loop is reached, increase line counter for the next iteration.
  done < "${1}"
  linenum=$((linenum - 1))							# Have to substract the line that has been counted despite not looking like a tag.

  while IFS= read -r line
  do
    if [[ ${line} = top:* ]]
    then
      while [[ ${line} = *:*:* ]]							# If the tag line contains two colons, it's a sub-tag.
      do
        line=$(printf "${line}"|cut -d ':' -f 1-2)				# Cut off the child and ...
        tags="${tags}"$'\n'"${line}"						# ... add the remaining parent to the list of tags.
      done
    fi
  done <<< "${tags}"

  tags="taglines:${linenum}${tags}"						# Use the always empty first line to store the line counter so that we know later
  tags=$(printf '%s\n' "${tags}"|sort -t : -k1r,1 -k2,2|uniq)			# Sort tags according to how I happened to like them today and remove duplicates.
										#   on where to cut off the file to produce only the actual entry content.
  echo "${tags}"								# Echo the result, one tag per line to be parsed ba other funtions.
}




prepare() {
  #tmpdir="${TEMP_DIR}/${scriptname}.$$.${RANDOM}"				# Generate a path were we will store temporary files during this run of the script.
  tmpdir="$(mktemp -d --suffix=.SBWG)"						# Generate a path were we will store temporary files during this run of the script.
  d "tmpdir: ${tmpdir}"
  d webbbase: ${webbase}
  d shellbase: ${shellbase}
  d odir: ${odir}

  if [ -d "${shellbase}"/tags/ ]; then rm -f "${shellbase}"/tags/*; fi		# Remove all tag lists created in the previous run (if any).
  IFS=$'\n'									# Needed for something. (I forgot where.)

  if ls "${odir}"/* &>/dev/null; then vv "The output directory \'${odir}\' is not empty. If you want to start clean, empty or remove the directory."; fi

  mkdir -p "${odir}/tags/"							# Create tags directory if it doesn't already exit.
  cp "${shellbase}/tagicons/"*.png "${odir}/tags/"				# Copy all tag icons - if there are any - to the html directory.

  call_function_if_declared hook_prepare

  v "Finished preparations. ✅"
}


copy_styles() {									#  $1: Style prefix
  mkdir -p "${odir}/css"

  d "style: ${style}"
										# Copy all files that end in .css and either start with the provided style name
										#   or are named just the style name to the styles directory in the output directory.
  cp -u "${shellbase}/styles/${style}"{.css,-*.css} "${odir}/css/" &>/dev/null
  v "Using style '${style}': "$(ls -p "${shellbase}/styles/${style}"{.css,-*.css} 2>/dev/null | tr "\n" " ")
#  cp "${shellbase}/styles/${style}"{.css,-*.css} "${odir}/css/" &>/dev/null || v "No CSS files for the style '${style}' exist."
#  if [ $? -ne 0 ]; then found_something=true; fi
#  v "Using style \"${style}\": ${shellbase}/styles/${style}"{.css,-*.css}
# The part of the following commented out block that is still needed was moved to gen_head.
#  for sheet in $(ls -p "${shellbase}/styles/${style}"{.css,-*.css} 2>/dev/null); do
#    found_something=true
#    d "sheet: ${sheet}"
#    filename=$(basename "${sheet}")
#    echo "    <link rel=\"stylesheet\" href=\"${webbase}css/${filename}\">" >> "${tmpdir}/head"
#    d "    <link rel=\"stylesheet\" href=\"${webbase}css/${filename}\">"
#  done
  v "Finished copying stylesheets. ✅"
}



copy_files() {
  mkdir -p "${odir}/files"
#  shopt -s dotglob								# So that dotfiles (such as .htaccess) are copied by cp.
  cp -ru "${shellbase}"/files "${odir}/"

  cp -u "${shellbase}/logo.svg" "${odir}/logo.svg"

#  find "${odir}/" -type f -exec chmod -x {} +					# At least try to not have any executable files in the web dir.

  call_function_if_declared hook_files

  v "Finished copying files and downloads. ✅"
}


# Always set $pagetitle before calling gen_head. Otherwise the variable may contain a previously used string.
gen_head() {
  echo '<!DOCTYPE html>'
  echo '<html>'
  echo '  <head>'
  echo '    <meta charset="UTF-8">'
  echo '    <meta name="generated" content="'"$(date)"'">'
  echo '    <meta name="generator" content="SBWG '"${version}"'">'
  echo '    <title>'"${pagetitle}"'</title>'
  echo "    <link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS Feed for all entries from ${sitename}\" href=\"/all.rss\" />"


  local sheet
#  for sheet in $(ls -p "${shellbase}/styles/${style}"{.css,-*.css} 2>/dev/null); do
  for sheet in "${shellbase}/styles/${style}"{.css,-*.css}; do
    local filename=$(basename "${sheet}")
    [[ -f "${sheet}" ]] \
      && echo "    <link rel=\"stylesheet\" href=\"${webbase}css/${filename}\">"
  done

  option_set m && echo "    <style>header,aside,div.entry-title-wrapper{background-color:#$(printf '%x\n' $(shuf -i 100-255 -n 1))$(printf '%x\n' $(shuf -i 100-255 -n 1))$(printf '%x\n' $(shuf -i 100-255 -n 1));}</style>"

  [[ -z ${headinsert} ]] || echo "${headinsert}"				# Insert these lines if the variable is

  call_function_if_declared hook_head

  echo '  </head>'
  echo '  <body>'
  echo '    <header>'
  echo '      <a href="/"><img id="logo" src="/logo.svg" /></a>'
  echo '      <a href="/"><h1>'"${sitename}"'</h1></a>'
  echo '    </header>'

  pagetitle="${sitename}"							# Set back to default in case $pagetitle it not set before calling gen_head the next time.
}




gen_head_old() {
  echo '<!DOCTYPE html>' > "${tmpdir}/head"
  echo '<html>' >> "${tmpdir}/head"
  echo '  <head>' >> "${tmpdir}/head"
  echo '    <meta charset="UTF-8">' >> "${tmpdir}/head"
  echo '    <meta name="generated" content="'"$(date)"'">' >> "${tmpdir}/head"
  echo '    <meta name="generator" content="SBWG '"${version}"'">' >> "${tmpdir}/head"
  echo '  </head>' >> "${tmpdir}/head"
  echo '  <body>' >> "${tmpdir}/head"
  echo '    <header>' >> "${tmpdir}/head"
  echo '      <a href="/"><img id="logo" src="/logo.svg" /></a>' >> "${tmpdir}/head"
  echo '      <a href="/"><h1>'"${pagetitle}"'</h1></a>' >> "${tmpdir}/head"
  echo '    </header>' >> "${tmpdir}/head"
}




# CREATE A SINGLE ENTRY
gen_entry() {									# $1: The entry for which its page should be generated.
  local entry="${1}"
  vv "- Generating entry \"${entry}\"."
#  test -f "${shellbase}/entries/${entry}" || error_exit "${shellbase}/entries/${entry} is not an existing file."
  if [[ $(file -bi "${shellbase}/entries/${entry}") != text* ]]			# Check again for individually generated entries.
  then
    v "${entry} is not a text file. Not generating it."
    return 2									# Don't generate anything if the file does not seem to be a text file of any sort.
  fi
  local entryfile="${odir}/entries/${entry}.html"
#  title=$(sed -n '2p' "${shellbase}/entries/${entry}")
#  local tags=$(sed -n '/-->/q;p' "${shellbase}/entries/${entry}"|tail -n +2)	# Tag lines start at line 2 and end before the HTML comment is closed.
  local tags=$(grab_tags "${shellbase}/entries/${entry}")
  local target=$(grab_tag "${tags}" redirect)					# Check if there is an entry to which this entry should redirect.
  [[ ! -z "${target}" ]] && html_redirect "entries/${entry}.html" "entries/${target}.html" && return 2 # If so, redirect the page.

  local title=$(grab_tag "${tags}" title)
  # print header
  pagetitle="${title} | ${sitename}" >> "${entryfile}"
  gen_head > "${entryfile}"
  # print side bar
  echo "<section>" >> "${entryfile}"						# This is just to enable styling.
  cat "${tmpdir}/sidebar" >> "${entryfile}"
  printf "<main id=\"entry\" class=\"" >> "${entryfile}"			# This is just to enable styling.
  printf "${tags}" | grep -E -- '^cat:|^top:|^lang:|^author:' | tr " " "-" | tr "\n" " " >> "${entryfile}"			# This is just to enable styling.
  echo "\">" >> "${entryfile}"							# This is just to enable styling.
  echo "<div class=\"entry-title-wrapper\">" >> "${entryfile}"			# This is just to enable styling.
  # print title
  echo "<h2 id=\"entry-title\">${title}</h2>" >> "${entryfile}"
  # print last modified date
#  local edited=$(ls -l --time-style=+"%Y-%m-%d" "${shellbase}/entries/${entry}" | awk '{ print $6 }') # Get last modified date of $entry
#  local created=$(sed -n '3p' "${shellbase}/entries/${entry}")			# The entry's creation date is stored in the third line of the source file.
  created=$(grab_tag "${tags}" created)
  edited=$(grab_tag "${tags}" edited)
  echo "<div class=\"page-info\">" >> "${entryfile}"				# This is just to enable styling.
  printf "${created}" >> "${entryfile}"
  if [[ -n ${edited} ]]; then printf " (edited ${edited})" >> "${entryfile}"; fi
  local tag
  for tag in ${tags}; do
    case ${tag} in (author:*|cat:*|top:*|lang:*)				# If the tag starts with either of those (the tag types that visitors should be able to filter by
      if [ -f "${shellbase}/tagicons/${tag}.png" ]
      then
        echo "<span class=\"tag icon ${tag}\"><a href=\"${webbase}tags/${tag}.html\" title=\"Show all entries tagged with ${tag}\"><img src=\"${webbase}tags/${tag}.png\" /></a></span>" >> "${entryfile}" # Print a list of tags for this entry, linked to the corresponding tagpage.
      else
        echo "<span class=\"tag ${tag}\"><a href=\"${webbase}tags/${tag}.html\">${tag}</a></span>" >> "${entryfile}" # Print a list of tags for this entry, linked to the corresponding tagpage.
      fi
    ;;
    esac
  done
  echo "</div>" >> "${entryfile}"						# /page-info
  echo "</div>" >> "${entryfile}"						# /title-wrapper
  # print entry content
  echo "<div id=\"content\">" >> "${entryfile}"					# This is just to enable styling.
#  cat "${shellbase}/entries/${entry}" >> "${entryfile}"			# The content of the source file in its entirety is included in the HTML file.
  cat "${shellbase}/entries/${entry}" | tail -n +$(grab_tag "${tags}" taglines) >> "${entryfile}"
  echo "</div>" >> "${entryfile}" 						# /content
  # attach gallery (if there is one for this entry)
  echo "<div id=\"gallery\">" >> "${entryfile}"
  local gallerypath="${shellbase}/galleries/${entry}"
  ls "${gallerypath}" &> /dev/null						# See if there if a gallery folder for this entry.
  shopt -s nocaseglob
  if [[ $? -eq 0 ]]; then							# If there is a gallery directory
    local images=""
    images=$(ls -td "${gallerypath}"/*.{jpeg,jpg,png} 2> /dev/null)
    if [ -n "${images}" ];							# If $images is not still empty
    then									#  (In other words: if there is at least one file with one of the extensions)
#      echo "<div class=\"gallery\>" >> "${outfile}"				# This is just to enable styling.
      echo "<p><a href=\"${webbase}galleries/${entry}.html\">View Gallery</p></a>" >> "${entryfile}"
#      echo "</div>" >> "${entryfile}"						# /gallery
      local image
      for image in ${images}; do
        local img=$(basename "${image}")
        echo "<a class=\"gallery-image thumb\" href=\"${webbase}galleries/${entry}/${img}\"><img class=\"thumb\" src=\"${webbase}galleries/${entry}/thumbnails/${img}\"/></a>" >> "${entryfile}"
      done
    fi
  fi
  echo "</div>" >> "${entryfile}" 						# /gallery
  echo "</main>" >> "${entryfile}"
  # print footer
  echo "</section>" >> "${entryfile}"						# This is just to enable styling.
  cat "${shellbase}/footer" >> "${entryfile}"					# The content of the footer file is included in every page, entry and tagpage.

  call_function_if_declared hook_entry
}


# CREATE ENTRIES
gen_entries() {
  mkdir -p "${odir}/entries"
#  for entry in $(ls -tp "${shellbase}/entries/" | grep -v /); do		# Look into each entry source file (no directories) again.
  if [[ -n "${desired_entry}" ]]						# If an entry name was given with option -e to generate a single entry.
  then
    gen_entry "${desired_entry}"
  else										# If no argument (single entry name) was given to option -e
    local entry
    for entry in "${ENTRYLIST[@]}"; do						# Look into each entry again.
#      test -f "${entry}" || error_exit "${entry} is not an existing file."

#      if [[ $(file -bi "${entry}") != text* ]]					# This has already been checked when the array was filled. Idc, check again.
#      then
#        vv "${entry} is not a text file. Skipping it."
#        continue								# Continue to next iteration if the file doesn't seem to be a text file of any type.
#      fi
      gen_entry $(basename "${entry}")						# Generate the page for that entry.
    done
  fi

  v "Finished generating entries. ✅"
}








# CREATE SINGLE GALLERY
gen_gallery() {									# $1: Gallery name
  local gallery="${1}"
  if option_set v; then printf " - ${gallery} "; fi
  cp -ru "${shellbase}/galleries/${gallery}" "${odir}/galleries/"		# Copy all the stuff. Nothing but supported image files should be in there.
  local imagefile
  for imagefile in ${shellbase}/galleries/${gallery}/*; do
#    d g1 - ${gallery} - ${imagefile}
    if [[ $(file -bi "${imagefile}") == image* ]]; then				# If the file is indeed some image fyle (regardless of file name) ...
      cp "${imagefile}" "${odir}/galleries/${gallery}/"				#   ... then copy the original to the output gallery directory.
    else
      v ""; v "${imagefile} does not seem to be an image file. I'll ignore it."	# Note that the convertation to various image sizes is handled separately below.
    fi
  done

#  find /path/to/files -iname '*.jpg.........' -exec mogrify ........... {} +	# Better alternative to the above line.
  local images=""
  images=$(ls -tdr "${gallerypath}"*.{jpeg,jpg,png} 2> /dev/null)
  if [ -n "${images}" ];							# If $images is not still empty ...
  then										#   ... (In other words: if there is at least one file with one of the extensions)
    if [ -s "${shellbase}/entries/${gallery}" ]; then				# If a corresponding blog post exists
#      local title=$(sed -n '2p' "${shellbase}/entries/${gallery}")			# The entry's title is the second line of the source file.
#      local tags=$(sed -n '/-->/q;p' "${shellbase}/entries/${gallery}"|tail -n +2)	# Tag lines start at line 2 and end before the HTML comment is closed.
      local tags=$(grab_tags "${shellbase}/entries/${gallery}")
      local title=$(grab_tag "${tags}" title)
      pagetitle="${title} | ${sitename}" >> "${odir}/galleries/${gallery}.html"
    else
      pagetitle="${gallery} | ${sitename}" >> "${odir}/galleries/${gallery}.html"
    fi
    gen_head > "${odir}/galleries/${gallery}.html"
    echo "<section>" >> "${odir}/galleries/${gallery}.html"
    cat "${tmpdir}/sidebar" >> "${odir}/galleries/${gallery}.html"
    echo "<main id=\"gallery\">" >> "${odir}/galleries/${gallery}.html"		# This is to enable styling for gallery pages.
    if [ -s "${shellbase}/entries/${gallery}" ]					# If a corresponding blog post exists
    then
      echo "<a name=\"top\"><h2 id=\"page-title\">${title}</h2></a>" >> "${odir}/galleries/${gallery}.html" # Print title of the blog post because it's neater than the gallery directory name.
      echo "<p><a title=\"There is a blog post appertaining to this gallery. Click here to go to the blog post.\" href=\"${webbase}entries/${gallery}.html\">View corresponding blog post</a></p>" >> "${odir}/galleries/${gallery}.html" # Also link to that blog post.
    else									# If no correstponding blog post exists
      echo "<a name=\"top\"><h2 id=\"page-title\">Image Gallery: ${gallery}</h2></a>" >> "${odir}/galleries/${gallery}.html" # Print a title based on the gallery directory name.
    fi
    echo "<div id=\"gallery-minis\">" >> "${odir}/galleries/${gallery}.html"

    mkdir -p "${odir}/galleries/${gallery}/minis/"					# Make sure the subdirectory for this image size exists in this gallery directory.
    mkdir -p "${odir}/galleries/${gallery}/thumbnails/"
    mkdir -p "${odir}/galleries/${gallery}/previews/"

# The following few lines are just commented out for debug/testing purpos.
# I think the the comment line above this one is obsolete.
    local image
    for image in ${images}; do							# Create thumbnails and other sizes for all the gallery images.
      test -f ${image} || error_exit "${image} not an exosting file."
      [[ ! $(file -bi "${image}") == image* ]] && continue			# Skip this file if it's not an image.
      local img=$(basename "${image}")						#   I use this script on shared hosting. This is why I use
#      printf "."								#   nice for the conversions. There is no other reason.

										# If the mini of this image already exists, skip generating it
      [[ ! -f "${odir}/galleries/${gallery}/minis/${img}" ]] \
        && nice -n 19 convert "${image}" -resize "${thumbsizemini}"x"${thumbsizemini}" "${odir}/galleries/${gallery}/minis/${img}" \
        && if option_set v; then printf "."; fi \
        || if option_set v; then printf ","; fi
										# If the thumbnail of this image already exists, skip generating it
      [[ ! -f "${odir}/galleries/${gallery}/thumbnails/${img}" ]] \
        && nice -n 19 convert "${image}" -resize "${thumbsize}"x"${thumbsize}" "${odir}/galleries/${gallery}/thumbnails/${img}" \
        && if option_set v; then printf "."; fi \
        || if option_set v; then printf ","; fi
										# If the preview of this image already exists, skip generating it
      [[ ! -f "${odir}/galleries/${gallery}/previews/${img}" ]] \
        && nice -n 19 convert "${image}" -resize "${previewsize}"x"${previewsize}" "${odir}/galleries/${gallery}/previews/${img}" \
        && if option_set v; then printf "."; fi \
        || if option_set v; then printf ","; fi
      echo "<a class=\"gallery-image mini\" href=\"#${img}\"><img src=\"${webbase}galleries/${gallery}/minis/${img}\"/></a>" >> "${odir}/galleries/${gallery}.html"
    done

    echo "</div>" >> "${odir}/galleries/${gallery}.html"			# /gallery-minis
    echo "<div id=\"gallery-previews\">" >> "${odir}/galleries/${gallery}.html"
    local image
    for image in ${images}; do
      test -f "${image}" || error_exit "${image} not an exosting file."
      local img=$(basename "${image}")
      echo "<br />" >> "${odir}/galleries/${gallery}.html"
      echo "<a name=\"${img}\" class=\"gallery-image preview\" href=\"${webbase}galleries/${gallery}/${img}\"><img src=\"${webbase}galleries/${gallery}/previews/${img}\"/></a>" >> "${odir}/galleries/${gallery}.html"
      echo "<br />" >> "${odir}/galleries/${gallery}.html"
      echo "<a title=\"Do clickery here to go to the top of the gallery page.\" href=\"#top\" class=\"gototop\"> Go to top ⬆" >> "${odir}/galleries/${gallery}.html"
    done
    echo "</div>" >> "${odir}/galleries/${gallery}.html"				# /gallery-previews
    echo "</main>" >> "${odir}/galleries/${gallery}.html"				# /gallery
    echo "</section>" >> "${odir}/galleries/${gallery}.html"
    cat "${shellbase}/footer" >> "${odir}/galleries/${gallery}.html"
  else									# If there is no file with one of the supported extensions in the directory
#    v "The following gallery does not contain any supported image files: ${gallery}"
    v "does not contain any supported image files."
  fi
  if option_set v; then echo; fi
#  find "${gallerypath}" -not -iname "*.png" -not -iname "*.jpeg" -not -iname "*.jpg" -not -type d -exec v Not a supported file extension: {} \;
										# Point out every file in the gallery folders that has not one of the supported file extensions, ignore directories.
  call_function_if_declared hook_gallery

}


# CREATE IMAGE GALLERY PAGES
gen_galleries() {
  mkdir -p "${odir}/galleries"
  v "Generating various image sizes of galleries:"
  vv ". = Generated one file			, = Skipped an existing file"
  if [[ -n ${desired_gallery} ]]; then						# If the option to (re-)generate a single gallery is used ...
#    gallerypath="${shellbase}/galleries/${gallery}/"
    gen_gallery "${desired_gallery}"						#   ... generate just this single gallery ...
  else
    shopt -s nocaseglob								# Should already be set, but to be save, set it again. (Because .jpg/.JPG etc.)
    local gallerypath
    for gallerypath in "${shellbase}"/galleries/*/; do
      local gallery=$(basename "${gallerypath}")
      gen_gallery "${gallery}"
    done
  fi
  v "Finished generating image gallery pages. ✅"
}

















# CREATE SINGLE PAGE
gen_page() {
  local page="${1}"
  vv "- Generating page \"${page}\"."
#  test -f "${shellbase}/pages/${page}" || error_exit "${shellbase}/pages/${page} is not an existing file."
  if [[ $(file -bi "${shellbase}/pages/${page}") != text* ]]			# Check again for individually generated pages.
  then
    v "${page} is not a text file. Not generating it."
    return 2									# Don't generate anything if the file does not seem to be a text file of any sort.
  fi
  local pagefile="${odir}/${page}.html"
#  local title=$(sed -n '2p' "${shellbase}/pages/${page}")				# The title is used as <title> tag and printed as <h2> in the body
#  local tags=$(sed -n '/-->/q;p' "${shellbase}/pages/${page}"|tail -n +2)	# Tag lines start at line 2 and end before the HTML comment is closed.
  local tags=$(grab_tags "${shellbase}/pages/${page}")
  local title=$(grab_tag "${tags}" title)
  # print header
  pagetitle="${title} | ${sitename}" >> "${pagefile}"
  gen_head > "${pagefile}"
  # print side bar
  echo "<section>" >> "${pagefile}"						# This is just to enable styling.
  cat "${tmpdir}/sidebar" >> "${pagefile}"
  echo "<main id=\"page\">" >> "${pagefile}"					# This is just to enable styling.
  # print title
  echo "<h2 id=\"page-title\">${title}</h2>" >> "${pagefile}"
  # print page content
  echo "<div id=\"content\">" >> "${pagefile}"					# This is just to enable styling.
#  cat "${shellbase}/pages/${page}" >> "${pagefile}"				# The content of the source file in its entirety is included in the HTML file.
  cat "${shellbase}/pages/${page}" | tail -n +$(grab_tag "${tags}" taglines) >> "${pagefile}"
  echo "</div>" >> "${pagefile}"						# /content
  echo "</main>" >> "${pagefile}"
  # print footer
  echo "</section>" >> "${pagefile}"						# This is just to enable styling.
  cat "${shellbase}/footer" >> "${pagefile}"					# The content of the footer file is included in every page, entry and tagpage.

  call_function_if_declared hook_page
}

# CREATE PAGES
gen_pages() {
  if [[ -n "${desired_page}" ]]							# If a page name was given with option -p to generate a single page.
  then
    gen_page "${desired_page}"
  else
    local page
    for page in $(ls -tp "${shellbase}/pages/" | grep -v /); do			# Look into each file (no dirs) in the pages/ directory & create HTML file from it.
      test -f "${shellbase}/pages/${page}" || error_exit "${shellbase}/pages/${page} is not an existing file."
      gen_page "${page}"
    done
  fi
  v "Finished generating pages. ✅"
}












# CREATE SIDEBAR
gen_sidebar() {
  ENTRYLIST=()									# Start with empty array to store the entry names in.
  local entry
  for entry in "${shellbase}"/entries/*; do
#    d Looking into ${entry}
    local filetype=$(file -b --mime-type "${entry}")				# Determine mime type of the supposed entry source file.
    if [[ ${filetype} = text/* ]]; then						# If it is a plain text file ...
      ENTRYLIST+=("${entry}")							#   ... append it to the array so that it will be used in the blog generation.
      if [[ ${filetype} != text/plain && ${filetype} != text/html ]]; then
        vv "${entry} is neither plain text nor HTML. Using it anyway."		# Let stdout know if it's a text file but not an expected type.
      fi
    else									# If it's not even a text file ...
      vv "${entry} is not a text file. Not using that thing."			#   ... let stdout know that there is a wrong file in the entries directory.
    fi
  done

  declare -a tagslist_unsorted=()						# Start with empty array
  local num=0									# Start at item 0 in the array.
  local entry
#  for entry in $(ls -tp "${shellbase}/entries/" | grep -v /); do		# Look into each entry source file, no directories.
  for entry in "${ENTRYLIST[@]}"; do						# Look into each entry

#    d enrty: ${entry}

#    local tags=$(sed -n '/-->/q;p' "${shellbase}/entries/${entry}"|tail -n +4)	# Tag lines start at line 4 and end before the HTML comment is closed.
#    local tags=$(sed -n '/-->/q;p' "${entry}"|tail -n +2)			# Tag lines start at line 2 and end before the HTML comment is closed.
    local tags=$(grab_tags "${entry}")
    local tag
    for tag in ${tags}; do							# For each tag that's found in this entry
      local tagslist_unsorted[${num}]=${tag}					#   put the tag as a new item into the array.
      let "num+=1"								# Next time next item.
    done
  done
#  readarray -t tagslist < <(printf '%s\n' "${tagslist_unsorted[@]}"|sort -r|uniq) # Put unsorted tagslist sorted into array tagslist, one item per line.
  readarray -t tagslist < <(printf '%s\n' "${tagslist_unsorted[@]}"|sort -t : -k1r,1 -k2,2|uniq) # Put unsorted tagslist sorted into array tagslist, one item per line.

  v "Finished generating tagslist for sidebar. ✅"


  local firstcat=false								# Turns true when the first category tag has been found in the below loop.
  local firstlang=false								# Turns true when the first language tag has been found in the below loop.
  local firsttop=false								# Turns true when the first topic tag has been found in the below loop.
  local firstauthor=false							# Turns true when the first author tag has been found in the below loop.
  # Turn the sorted list into HTML code for the sidebar
  echo "<aside><h3><a title=\"A page listing all blog posts regardless of language, author, category and topic in full.\" href=\"${webbase}all.html\">Weblog</a></h3>" > "${tmpdir}/sidebar"
  local item
#  for item in "${tagslist[@]}"
  for ((i=0; i <= ${#tagslist[@]}; i++))
  do
    item="${tagslist[${i}]}"
    local oddity=odd
    case ${item} in
      cat:*)									# If the item starts with "cat"
        if ! ${firstcat}; then firstcat=true; echo "<details id=\"cat\"><summary>Blog Categories</summary>" >> "${tmpdir}/sidebar"; fi # Add heading before first occurance
        local tag=$(echo "${item}" | cut -c 5-)
        if [[ ${tag} == *:* ]]; then						# If the tag still contains a colon (meaning it is a subcatagory)
          printf "<li class=\"cat subcat ${oddity}\"><a title=\"Show blog posts in the category ${item}.\" href=\"${webbase}tags/${item}.html\">" >> "${tmpdir}/sidebar"
          printf "${tag}" | sed 's/^[^:]*://g' >> "${tmpdir}/sidebar"		# Remove the parent tag name.
          echo "</a></li>" >> "${tmpdir}/sidebar"
        else
          echo "<li class=\"cat ${oddity}\"><a title=\"Show blog posts with the category ${item}.\" href=\"${webbase}tags/${item}.html\">${tag}</a></li>" >> "${tmpdir}/sidebar"
        fi
        if [[ ${oddity} == odd ]]; then oddity=even; else oddity=odd; fi	# Alternate between odd and even for CSS class.
        ;;
      top:*)									# If the item starts with "top"
        if ! ${firsttop}; then firsttop=true; echo "<details class=\"top\" open=\"\"><summary>Topics</summary>" >> "${tmpdir}/sidebar"; fi # Add heading before first occurance
        local tag=$(echo "${item}" | cut -c 5-)
        if [[ ${tag} == *:* ]]; then						# If the tag still contains a colon (meaning it is a subtopic)
          printf "<li class=\"top subtop ${oddity}\"><a title=\"List blog posts regarding the topic ${item}.\" href=\"${webbase}tags/${item}.html\">" >> "${tmpdir}/sidebar"
          printf "${tag}" | sed 's/^[^:]*://g' >> "${tmpdir}/sidebar"
          echo "</a></li>" >> "${tmpdir}/sidebar"
        else
          echo "<li class=\"top ${oddity}\"><a title=\"Show blog posts regarding the topic ${item}.\" href=\"${webbase}tags/${item}.html\">${tag}</a></li>" >> "${tmpdir}/sidebar"
        fi
        if [[ ${oddity} == odd ]]; then oddity=even; else oddity=odd; fi	# Alternate between odd and even for CSS class.
        ;;
      lang:*)									# If the item starts with "lang"
        if ! ${firstlang}; then firstlang=true; echo "<details class=\"lang\"><summary>By Language</summary>" >> "${tmpdir}/sidebar"; fi # Add heading before first occurance
        local tag=$(echo "${item}" | cut -c 6-)
        echo "<li class=\"lang ${oddity}\"><a title=\"Show blog posts with the language tag ${item}.\" href=\"${webbase}tags/${item}.html\">${tag}</a></li>" >> "${tmpdir}/sidebar"
        if [[ ${oddity} == odd ]]; then oddity=even; else oddity=odd; fi	# Alternate between odd and even for CSS class.
        ;;
      author:*)									# If the item starts with "author"
        if ! ${firstauthor}; then firstauthor=true; echo "<details class=\"author\"><summary>By Author</summary>" >> "${tmpdir}/sidebar"; fi # Add heading before first occurance
        local tag=$(echo "${item}" | cut -c 8-)
        echo "<li class=\"author ${oddity}\"><a title=\"Show blog posts supposedly written by ${item}.\" href=\"${webbase}tags/${item}.html\">${tag}</a></li>" >> "${tmpdir}/sidebar"
        if [[ ${oddity} == odd ]]; then oddity=even; else oddity=odd; fi	# Alternate between odd and even for CSS class.
        ;;
      *)									# Every other item
#        echo "<h3>${line}</h3>" >> "${tmpdir}/sidebar"				# gets turned into a heading of the following tags.
        ;;
    esac
    if [[ ${item%%:*} != ${tagslist[${i}+1]%%:*} ]]				# If the next item in the tagslist is if a different tag type than the current one.
    then
      case ${item} in
       top:*|cat:*|lang:*|author:*)						# If the current item is of one of the tag types that are listed in the sidebar.
#d
#d "this: ${item%%:*}"
#d "next: ${tagslist[${i}+1]%%:*}"
#d
          echo "</details>" >> "${tmpdir}/sidebar"
        ;;
      esac
    fi
  done

  echo "<details><summary>Galleries</summary>" >> "${tmpdir}/sidebar"
  GALLERYLIST=()
  for gallery in $(find "${shellbase}"/galleries/ -maxdepth 1 -mindepth 1 -type d|sort); do
    local has_pics=false
    for pic in "${gallery}"/*; do
      if [[ $(file -bi "${pic}") == image* ]]; then
        has_pics=true;
        break;
      fi
    done
    if ! ${has_pics}; then
      vv "Gallery directory $(basename ${gallery}) contains no images."
    else
      GALLERYLIST+=($(basename "${gallery}"))
    fi
  done
  for gallery in "${GALLERYLIST[@]}"; do
#    d gallery: ${gallery}
    echo "<li><a title=\"Show what's in the gallery ${gallery}.\" href=\"${webbase}galleries/${gallery}.html\">${gallery}</a></li>" >> "${tmpdir}/sidebar" # Add each gallery to the list
  done
  echo "</details>" >> "${tmpdir}/sidebar"
  echo "<li class=\"imprint-link\"><a title=\"Yeah, I guess I'm obliged to include this.\" href=\"${webbase}impressum.html\">Impressum</a></li>" >> "${tmpdir}/sidebar"
  echo "</aside>" >> "${tmpdir}/sidebar"

  v "Finished generating sidebar file. ✅"

  call_function_if_declared hook_sidebar

  return 0
}






# CREATE HTML FILE THAT IMMEDIETLY REDIRECTS TO ANOTHER URL
html_redirect() {							# $1: Path of HTML file that should redirect, $2: Path of target file (where to redirect to)
  echo '<meta http-equiv="refresh" content="0; url='"${webbase}${2}"'" />' > "${odir}/${1}"
  return 0
}













# CREATE SINGLE TAGPAGE
gen_tagpage() {									# $1: Tag name (Generate all.html and its pages when no argument is given.)
  local tag="${1:-all}"								# Use provided tag name. If unset, use "all", generating all.html.
  vv "- Generating tagpage \"${tag}\"."
  local entrycount=-1								# For paging we will count the number of entries processed so far for each tag.
  local lastentry=$(($(echo "${ENTRYLISTS[${tag}]}" | wc -l) - 1))		# The number ob lines in the list of entries for this tag is the number of ...
										#   ... entries that will be included in this (possibly multi-page) tagpage.
  local lastpage=0								# Will be used to store the number of the last page that will be generated.
  local pagecount=0								#   The first page is page 0.
  mkdir -p "${shellbase}/tags/"							# Create tags directory if it doesn't already exit.
  lastpage=$((((lastentry + (perpage - 1)) / perpage) - 1)) # Determine the number of pages necessary (number of entries devided by $perpage, always rounded up)
  if [[ ${tag} == all* ]]; then
    local outfile="${odir}/${tag}.html"
  else
    local outfile="${odir}/tags/${tag}.html"
  fi

#  local tagname=$(echo "${tag}" | sed 's/^[^:]*://g')				# Cut off the tag type from the tag name
#d "1: ${1}"
#d "tag: ${tag}"
#  local tagname="${1:+Entries tagged }${1:-All Entries}"
  [[ ${tag} == all ]] \
    && local tagname="All Entries" \
    || local tagname="Entries tagged '${tag}'"
  pagetitle="${tagname} | ${sitename}" >> "${outfile}"
  gen_head > "${outfile}"
  echo "<section>" >> "${outfile}"						# This is just to enable styling.
  cat "${tmpdir}/sidebar" >> "${outfile}"
  echo "<main id=\"tagpage\">" >> "${outfile}"					# This is just to enable styling.
#  if [[ ${tag} != all* ]]; then							# Only print title and pageinfo if it's tagpage of individual tag and not all.html.
#    echo "<h2 id=\"page-title\">${tagname}</h2>" >> "${outfile}"
#    if [[ ${tag} == top* ]]							# Tagpages of 'topic' tags are different from those of other tag types.
#      then
#        echo "<em class=\"pageinfo\">These entries are available on the topic ${tagname}.</em>" >> "${outfile}"
#      else									# If this tagpage is not for a 'topic' tag
#        echo "<em class=\"pageinfo\">This page shows all entries tagged with ${tagname}, newest/last edited entries first.</em>" >> "${outfile}"
#    fi
#  fi
  echo "<h2 id=\"page-title\">${tagname}</h2>" >> "${outfile}"
  echo "<div id=\"filters\">" >> "${outfile}"					# It is needed for styling.
  if [[ ${tag} != all && ${tag} != author:* ]]					# If this is not all.html and not an author tagpage (or combined author tagpage).
  then										# Generate filter links where entries from more than one author are available.
    local secs=$(printf '%s\n' "${!ENTRYLISTS[@]}"|grep ^author:|grep -v + )	# Get all existing authors (no combined tags)
    local sec
    local filters
    local num
    for sec in ${secs}								# Cycle through all authors to look for entries with the current tag.
    do
      num=$(echo "${ENTRYLISTS[${sec}+${tag}]}"|grep -v ^$|wc -l)		# Number of non-empty lines of entries for the combined tagpage of this author + this tag.
      [[ $num -gt 0 ]] && filters="${filters}<span class=\"filter\"><a title=\"${num} entries from ${sec} tagged with ${tag}\" href=\"${webbase}tags/${sec}+${tag}.html\">${sec}</a></span>"
    done
										# Only display the filter links if there are at least two 'href's in the string ...
										#   ... of filters, meaning that there is probably more than one filter to display.
    [[ ${filters} == *href*href* ]] && echo "<details><summary>Filter further</summary> by ${filters}</details>" >> "${outfile}"
  fi
#d "tag: ${tag}"
  [[ ${tag} == author:*+*:* ]] && echo "<span class=\"remove-filter\"><a href=\"${webbase}tags/${tag#*+}.html\">Remove Filter</a></span>" >> "${outfile}"
  echo "</div>" >> "${outfile}"							# / #filters
  echo "<div id=\"content\">" >> "${outfile}"					# This is just to enable styling.

  local oddity=odd
  local entry
  while IFS= read -r entry							# Reading each entry name from the current tag's array item.
  do										# Every line starts with the date, followed by a colon and the title of the entry.
    local entryname=$(echo "${entry}" | sed 's/^[^:]*://g')			# Cut off the date and the colon from the beginning.
    if [ -z "${entryname}" ]; then continue; fi					# If the line is empty, stop right there.
    entrycount=$((entrycount + 1))						# Counting how many entries have been processed so far for paging.
#    local tags=$(sed -n '/-->/q;p' "${shellbase}/entries/${entryname}"|tail -n +2)	# Tag lines start at line 2 and end before the HTML comment is closed.
    local tags=$(grab_tags "${shellbase}/entries/${entryname}")

    local target=$(grab_tag "${tags}" redirect)					# Check if there is an entry to which this entry should redirect.
    if [[ -f "${shellbase}/entries/${target}" ]]				# If there is one and an entry by that name exists
    then
      echo "<!-- The following is an entry redirection caused by the entry '${entryname}'. -->" >> "${outfile}"
      entryname="${target}"							# Switch this entry generating iteration to the redirect target entry, keeping...
      tags=$(grab_tags "${shellbase}/entries/${entryname}")			# ... the already set output file name but getting the tags of the target entry.
    fi

#    local title=$(sed -n '2p' "${shellbase}/entries/${entryname}")			# The entry's title is the second line of the source file.
#    local edited=$(ls -l --time-style=+"%Y-%m-%d" "${shellbase}/entries/${entryname}" | awk '{ print $6 }') # Get last modified date of $entry.
#    local created=$(sed -n '3p' "${shellbase}/entries/${entryname}")				# The entry's creation date is stored in the third line of the source file.
    local created=$(grab_tag "${tags}" created)
    local edited=$(grab_tag "${tags}" edited)
#    local sort=$(grab_tag "${tags}" sort)
    local title=$(grab_tag "${tags}" title)
    if [[ "${tag}" == top:* ]] || [[ "${tag}" == *+top:* ]]			# Topic tags are handled differently though.
    then									# The content of the entries is not printed to the tagpage of a topic.
      if [[ ${oddity} == odd ]]; then oddity=even; else oddity=odd; fi		# Alternate between odd and even for CSS class.
      printf "<div class=\"top-title-wrapper ${oddity} " >> "${outfile}"	# This is just to enable styling.
      printf "${tags}" | grep -E -- '^cat:|^top:|^lang:|^author:' | tr " " "-" | tr "\n" " " >> "${outfile}"		# This is just to enable styling.
      echo "\">" >> "${outfile}"						# This is just to enable styling.
#      if [ -z "${title}" ]; then title="(Entry Without Title)"; fi		# If the title line in the source file is empty
      echo "<div class=\"top-entry\"><a href=\"${webbase}entries/${entryname}.html\">${title:-(Entry Without Title)}</a></div>" >> "${outfile}" # Add linked entry title
      printf "<div class=\"entry-info\"><a href=\"${webbase}entries/${entryname}.html\">created on ${created}" >> "${outfile}" # Add created date
#      if [[ -n ${sort} ]]; then printf " (${sort})" >> "${outfile}"; fi		# Display additional sort criteria if there is on.
      if [[ -n ${edited} ]]; then printf " (last edited on ${edited})" >> "${outfile}"; fi # Add edited date in one was set in the entry's header.
      echo "</a></div>" >> "${outfile}"						# /entry-info
      echo "</div>" >> "${outfile}"						# /top-title-wrapper
    else									# If this is a tagpage of a topic tag.
      echo "<!-- next entry -->" >> "${outfile}"
      printf "<div class=\"entry-wrapper " >> "${outfile}"			# This is just to enable styling.
      printf "${tags}" | grep -E -- '^cat:|^top:|^lang:|^author:' | tr " " "-" | tr "\n" " " >> "${outfile}"		# This is just to enable styling.
      echo "\">" >> "${outfile}"						# This is just to enable styling.
      # print entry content and stuff to tagpage
      echo "<div class=\"entry-title-wrapper\">" >> "${outfile}"			# This is just to enable styling.
      echo "<a href=\"${webbase}entries/${entryname}.html\"><h3 class=\"entry-title\">${title}</h3></a>" >> "${outfile}"
      # Add last modified date of the entry, link to entry so that entries without titles can be clicked
      printf "<div class=\"entry-info\"><a href=\"${webbase}entries/${entryname}.html\">${created}" >> "${outfile}"
      if [[ -n ${edited} ]]; then printf " (edited ${edited})" >> "${outfile}"; fi
      echo "</a></div>" >> "${outfile}"						# /entry-info
      local tag2
      for tag2 in ${tags}; do							# For each tag of the currently processed entry print a linked tag name or icon.
        case ${tag2} in author:*|cat:*|top:*|lang:*)				# If the tag starts with either of those (the tag types that visitors should be able to filter by
          if [ -f "${shellbase}/tagicons/${tag2}.png" ]				# If a PNG file for this tag exists, use this instead of it's name.
          then									# Tag icons must already be of the correct size.
            echo "<span class=\"tag icon ${tag2}\"><a title=\"Show all entries tagged with ${tag2}\" href=\"${webbase}tags/${tag2}.html\"><img src=\"${webbase}tags/${tag2}.png\" /></a></span>" >> "${outfile}" # Print a list of tags for this entry, linked to the correspondi$
          else									# Print a list of tags for this entry, linked to the corresponding tagpage.
            echo "<span class=\"tag ${tag2}\"><a title=\"Show all entries tagged with ${tag2}\" href=\"${webbase}tags/${tag2}.html\">${tag2}</a></span>" >> "${outfile}"
          fi
        ;;
        esac
      done
      echo "</div>" >> "${outfile}"						# /entry-info
      echo "</div>" >> "${outfile}"						# /title-wrapper
      echo >> "${outfile}"
      echo "<div class=\"entry-content-wrapper\">" >> "${outfile}"		# This is just to enable styling.
#      cat "${shellbase}/entries/${entryname}" >> "${outfile}"
      cat "${shellbase}/entries/${entryname}" | tail -n +$(grab_tag "${tags}" taglines) >> "${outfile}"
      echo "</div>" >> "${outfile}"						# /entry-content-wrapper

      local gallerypath="${shellbase}/galleries/${entryname}"
      ls "${gallerypath}" &> /dev/null						# See if there if a gallery folder for this entry.
      shopt -s nocaseglob
      if [[ $? -eq 0 ]]; then							# If there is a gallery directory
        local images=""
        images=$(ls -td "${gallerypath}"/*.{jpeg,jpg,png} 2> /dev/null)
        if [[ -n ${images} ]];							# If $images is not still empty
        then									#  (In other words: if there is at least one file with one of the extensions)
          echo "<div class=\"gallery\">" >> "${outfile}"				# This is just to enable styling.
          echo "<p><a title=\"This blog post has pictures attached. Click here to view those pictures in the gallery view.\" href=\"${webbase}galleries/${entryname}.html\">View Gallery</a></p>" >> "${outfile}"
          local image
          for image in ${images}; do
            local img=$(basename "${image}")
            echo "<a title=\"Click to view the original image file. If you wish to obtain a larger resolution image contact the admin pf this site. They may be saving space on their webserver by not uploading large image files.\" class=\"gallery-image mini\" href=\"${webbase}galleries/${entryname}/${img}\"><img class=\"mini\" src=\"${webbase}galleries/${entryname}/minis/${img}\"/></a>" >> "${outfile}"
          done
          echo "</div>" >> "${outfile}"						# /gallery
        fi
      fi
      echo >> "${outfile}"
    fi
#    echo "</div>" >> "${outfile}"						# /entry-wrapper

    if [[ ${tag} != top:* ]]							# Tags of the type top: don't get page breaks.
    then
      if [[ ${lastpage} -gt 0 ]]						# If there are more than 1 page to generate.
      then
        if [[ $((( ${entrycount} + 1) % ${perpage} )) -eq 0 ]] || [[ ${lastentry} == $((entrycount + 1)) ]] # If the next entry would be supposed to be on the next ...
        then			# ...  page or this is the last entry of this tag (last entries of single-page tags are excluded through below conditions anyway)
          echo "<div class=\"pager\">" >> "${outfile}"				# This is just to enable styling.
          echo "Page ${pagecount} of ${lastpage}" >> "${outfile}"		# Print current page number and last page number.
          if [[ ${pagecount} -gt 0 ]]; then					# If this is not the first page
            if [[ ${tag} == "all" ]]; then					# If we are generating all.html instead of a single tagpage.
              echo " - <a href=\"${webbase}${tag}-$((${pagecount} - 1)).html\">Go to previous page</a>" >> "${outfile}" # Print link to previous page
            else								# If we are generating a tagpage for an individual tag as opposed to all.html
              echo " - <a href=\"${webbase}tags/${tag}-$((${pagecount} - 1)).html\">Go to previous page</a>" >> "${outfile}" # Print link to previous page
            fi
          fi
          if [[ ${pagecount} -lt ${lastpage} ]]; then				# If this is not the last page
            if [[ ${tag} == all ]]; then					# If we are generating all.html instead of a single tagpage.
              echo " - <a href=\"${webbase}${tag}-$((${pagecount} + 1)).html\">Go to next page</a>" >> "${outfile}" # Print link to next page
            else								# If we are generating a tagpage for an individual tag as opposed to all.html
              echo " - <a href=\"${webbase}tags/${tag}-$((${pagecount} + 1)).html\">Go to next page</a>" >> "${outfile}" # Print link to next page
            fi
          fi
          echo "</div>" >> "${outfile}"						# /pager
          pagecount=$((pagecount + 1))
          if [[ ${tag} == all* ]]; then
            outfile="${odir}/${tag}-${pagecount}.html"				# Update the output filename so that every following entry will go to a new page.
          else
            outfile="${odir}/tags/${tag}-${pagecount}.html"			# Update the output filename so that every following entry will go to a new page.
          fi

          pagetitle="${title} | ${sitename}" >> "${outfile}"
          gen_head > "${outfile}"
          echo "<section>" >> "${outfile}"					# This is just to enable styling.
          cat "${tmpdir}/sidebar" >> "${outfile}"
          tagname=$(echo "${tag}" | sed 's/^[^:]*://g')				# Cut off the tag type from the tag name
          echo "<main id=\"tagpage\">" >> "${outfile}"				# This is just to enable styling.
          if [[ "${tag}" != all* ]]; then					# Only print title and pageinfo if it's tagpage of individual tag and not all.html.
            echo "<h2 id=\"page-title\">${tagname}</h2>" >> "${outfile}"
            if [[ ${tag} == top* ]]						# Tagpages of 'topic' tags are different from those of other tag types.
              then
                echo "<em class=\"pageinfo\">These entries are available on the topic ${tagname}.</em>" >> "${outfile}"
              else								# If this tagpage is not for a 'topic' tag
                echo "<em class=\"pageinfo\">This page shows all entries tagged with ${tagname}, newest/last edited entries first.</em>" >> "${outfile}"
            fi
          fi
          echo "<div id=\"content\">" >> "${outfile}"				# This is just to enable styling.

        fi
      fi
    fi

  done <<< "${ENTRYLISTS[${tag}]}"						# End of while IFS= loop.
  echo "</div>" >> "${outfile}"							# /content
  echo "</main>" >> "${outfile}"
  echo "</section>" >> "${outfile}"						# This is just to enable styling.
  cat "${shellbase}/footer" >> "${outfile}"					# Print the footer file before starting the next page.

  if [[ ${tag} == all ]]; then							# If we are generating all.html instead of a single tagpage.
#    if [ ${lastpage} -gt 0 ]; then ln -f "${odir}/all.html" "${odir}/all-0.html"; fi	# The "previous" link on page 1 links to ...-0.html instead of ....html.
    if [[ ${lastpage} -gt 0 ]]; then html_redirect "all-0.html" "all.html"; fi	# The "previous" link on page 1 links to ...-0.html instead of ....html.
  else
#    if [ ${lastpage} -gt 0 ]; then ln -f "${odir}/tags/${tag}.html" "${odir}/tags/${tag}-0.html"; fi # The "previous" link on page 1 links to ...-0.html instead of ....html.
    if [[ ${lastpage} -gt 0 ]]; then html_redirect "tags/${tag}-0.html" "tags/${tag}.html"; fi # The "previous" link on page 1 links to ...-0.html instead of ....html.
  fi

  call_function_if_declared hook_tagpage
}



# CREATE TAGPAGES
gen_tagpages() {
  declare -A ENTRYLISTS=()							# Array used to store entry names (one per line) for each tag.
  local entry
  for entry in $(ls -tp "${shellbase}/entries/" | grep -v /); do		# First look at every entry ...
    test -f "${shellbase}/entries/${entry}" || error_exit "${shellbase}/entries/${entry}: no file there"
#    local tags=$(sed -n '/-->/q;p' "${shellbase}/entries/${entry}"|tail -n +2)	# (tag lines start at line 2 and end before the HTML comment is closed.)
    local tags=$(grab_tags "${shellbase}/entries/${entry}")
#    local created=$(sed -n '3p' "${shellbase}/entries/${entry}")		# The entry's creation date is stored in the third line of the source file.
#    local created=$(echo "${tags}" | grep -m 1 '^created:' | cut -c 9-)		# Find created date tag if it exists. First match.
#    created="${created:-1970-01-01}"						# If empty (no created date was found) use default/fallback value.
#    local edited=$(echo "${tags}" | grep -m 1 '^edited:' | cut -c 8-)		# Find edited date tag if it exists. First match.
#    edited="${edited:-1970-01-01}"						# If empty (no created date was found) use default/fallback value.
    local created=$(grab_tag "${tags}" created)
    local edited=$(grab_tag "${tags}" edited)
    local sort=$(grab_tag "${tags}" sort)
    if [[ -n ${sort} ]]
    then
      local sortby="${sort}"
    else
      local sortby=$(printf "${created:-1970-01-01}\n${edited}"|sort -r|head -n 1) # Pick the larger/later one of the two values, regardless of their formats.
    fi
    local tag
    for tag in ${tags}; do							# ... and put its name into all taglists it belongs in.
      case ${tag}
      in
        (author:*|cat:*|top:*|lang:*)						# If the tag starts with either of those, then add a new line with the ...
          ENTRYLISTS[${tag}]="${ENTRYLISTS[${tag}]}"$'\n'"${sortby}:${entry}"	#  ... entry name to the array item corresponding to each tag of this entry.
        ;;&
        (author:*)
          local second_tags=$(printf "${tags}" | grep -E -- '^cat:|^top:|^lang:|^author:')
          local second_tag
          while IFS= read -r second_tag
          do
            [[ "${tag}" != "${second_tag}" ]] && ENTRYLISTS[${tag}+${second_tag}]="${ENTRYLISTS[${tag}+${second_tag}]}"$'\n'"${sortby}:${entry}"
          done <<< "${second_tags}"
        ;;
#        (cat:*:*|top:*:*)							# If it is a subcatagory or a subtopic
#          local subtag=$(printf "${tag}"|cut -d ':' -f 1-2)			#   cut off the child tag and
#          ENTRYLISTS[${subtag}]="${ENTRYLISTS[${subtag}]}"$'\n'"${sortby}:${entry}" #   add entry name to the array item corresponding to the parent tag as well.
#        ;;
      esac
    done
#    if [[ "${created}" =~ ^[0-9] ]]						# If the creation date starts with a number (i.e. is a date in the first place)
    if [[ "${sortby}" =~ ^[0-9] ]] || [[ "${sort}" =~ ^[0-9] ]]			# If the date starts with a number (i.e. is a date in the first place)
    then
      ENTRYLISTS[all]="${ENTRYLISTS[all]}"$'\n'"${sortby}:${entry}"		# Add new line with entry name to "all", too.
    fi
  done

  if [[ -n "${desired_tagpage}" ]]						# If a tagpage name was given with option -t to generate a single tagpage.
  then
    gen_tagpage "${desired_tagpage}"
  else
    local tag
    for tag in "${!ENTRYLISTS[@]}"						# Cycle through all tags.
    do
      ENTRYLISTS[${tag}]=$(echo "${ENTRYLISTS[${tag}]}"|sort -r)		# Sort entries of that tag reverse chronologically.
      gen_tagpage "${tag}"							# Generate the tagpage for that tag.
    done
  fi

  v "Finished generating tagpages. ✅"

  return 0
}











# CREATE RSS FEED
gen_rss() {
#  echo "✘ RSS feed generation is not implemented, yet."
#  return

  {
    echo "<?xml version=\"1.0\"?>"
    echo "<rss version=\"2.0\" xmlns:blogChannel=\"http://backend.userland.com/blogChannelModule\">"
    echo "  <channel>"
    echo "    <title>${sitename}</title>"
    echo "    <link>${url}</link>"
    echo "    <description>All entries from ${sitename} (${url})</description>"
#    echo "    <language>en-uk</language>"
    echo "    <lastBuildDate>"$(date --rfc-2822)"</lastBuildDate>"
    echo "    <docs>https://www.rssboard.org/rss-specification</docs>"
    echo "    <generator>SBWG ${version}</generator>"
#    echo "    <webMaster></webMaster>"
    echo "    <ttl>60</ttl>"
  } > "${odir}/all.rss"

  local entry
  for entry in "${ENTRYLIST[@]}"; do
    local entryname=$(basename "${entry}")
    vv "- Generating RSS entry '${entryname}'."
    local tags=$(grab_tags "${entry}")
    local title=$(grab_tag "${tags}" title)
    local pubdate=$(grab_tag "${tags}" created)
    test -z "${pubdate}" && pubdate=$(grab_tag "${tags}" edited)
    [[ ! "${pubdate}" =~ ^[0-9] ]] && continue
#    if ! pubdate=$(date --rfc-2822 --date="${pubdate}"); then pubdate="Thu, 01 Jan 1970"; fi
    pubdate=$(date --rfc-2822 --date="${pubdate}")
    content=$(cat "${entry}" | tail -n +$(grab_tag "${tags}" taglines))
    local author=$(grab_tag "${tags}" author)

    {
      echo "    <item>"
      echo "      <title>${title:-(ᵔᴥᵔ) Untitled Post}</title>"
#      echo "      <link>${url}entries/${entry}.html</link>"
      echo "      <link>${url}entries/${entryname}.html</link>"
      echo "      <author>${author:-$sitename}</author>"
#      echo "      <author>(${author:-$sitename})</author>"
      echo "      <description><![CDATA[${content:-(ᵔᴥᵔ) Empty Entry}]]></description>"
      echo "      <pubDate>${pubdate}</pubDate>"
      echo "      <guid>${url}entries/${entryname}</guid>"
      echo "      <category>${tags}</category>" | tr "\n" " "
      echo "    </item>"
    } >> "${odir}/all.rss"
  done

  {
    echo "  </channel>"
    echo "</rss>"
  } >> "${odir}/all.rss"

  call_function_if_declared hook_rss

  v "Finished generating all.rss. ✅"
}










print_help() {
  cat << END_OF_HELP
Usage:
	${scriptname} [-c] [-b] [-g [GALLERY]] [-e [ENTRY]] [-p [PAGE]]
	  [-t [TAGPAGE]] [-r] [-f] [-s [STYLE]] [-i INPUT_DIR]
	  [-o OUTPUT_DIR] [-w WEBPATH] [-n ENTRIES_PER_PAGE]
	  [-z WIDTHxHEIGHT] [-v[v]] [-h] [-d]

	-c|--complete
		(Re-)generate the complete website. Further options to generate
		individual parts of the website are useless.

	-b|--blog
		(Re-)generate the blog.

	-g|--gallery|--galleries [GALLERY]
		(Re-)generate the gallery GALLERY. If GALLERY is not provided,
		(re-)generate all galleries.

	-e|--entry|--entries [ENTRY]
		(Re-)generate the entry ENTRY. If ENTRY is not provided,
		(re-)generate all entries.

	-p|--page [PAGE]
		(Re-)generate the page PAGE. If PAGE is not provided,
		(re-)generate all pages.

	-t|--tagpage [TAGPAGE]
		(Re-)generate the tagpage TAGPAGE. If TAGPAGE is not provided,
		(re-)generate all tagpages.

	-r|--rss
		(Re-)generate the blogs RSS feed.


	-f|--files
		Copy the files/ from the input directory to the output
		directory.

	-s|--style|--styles [STYLE]
		Copy relevant stylesheet files from the input directory to the
		output directory. If (parts of) the site is generated, too,
		link STYLE in the HTML head. If STYLE is not provided the
		default style.

	-i|--input INPUT_DIR
		Read the website source files from INPUT_DIR. If INPUT_DIR is
		not provided, use the default directory hardcoded in the script
		file.

	-o|--output OUTPUT_DIR
		Write the generated website to OUTPUT_DIR. If OUTPUT_DIR is
		not provided, use the default directory hardcoded in the script
		file.

	-w|--webpath WEBPATH
		Prepent WEBPATH to all HTML links that are generated by the
		script.

	-n|--perpage N
		Don't put more than N entries on one tagpage. 

	-z|--thumbnail WIDTHxHEIGHT
		Resize gallery images down to be no wider than WIDTH pixels and no
		higher than HEIGHT pixels when displayed as thumbnails at the
		bottom of an entry page.

	-v|--verbose
		Be verbose. Writes interesting messages to stdout. Providing this
		option more once equals the option -vv (--very-verbose).

	-vv|--very-verbose
		Be very verbose. Writes many interesting messages to stdout.
		This option alse sets the option -v (--verbose).

	-h|-?|--help
		Display this usage message on stdout and exit successfully.

	-d|--debug)
		Included messages intended for debugging purposes on stdout.
		This option also sets the options -v (--verbose) and -vv
		(--very-verbose).
END_OF_HELP
  exit
}


option_set() {									# Check if an option is set.
  echo "${options}" | grep -q "${1}"						# Check whether the provided option letter ($1) is in the string
}										#   which would mean it was set by set_option.

option_set_multi() {								# Check if an option is set more than once.
  echo "${options}" | grep -q "\(${1}\).*\1"					# Returns 1 if the provided character appears more than once in $options.
}

check_options() {								# Check for conflicting options and stuff.
  d "options: ${options}"
  if option_set h; then								# help - Do nothing after printing help.
    exit									# The script should have been exited already anyhow.
  fi
  if option_set_multi e; then
    v "More than one option to generate entries is set. I'll only use the last one."
  fi
  if option_set_multi p; then
    v "More than one option to generate pages is set. I'll only use the last one."
  fi
  if option_set_multi t; then
    v "More than one option to generate tagpages is set. I'll only use the last one."
  fi
  if option_set_multi s; then
    v "More than one style option is set. I'll only use the last one."
  fi
  if option_set_multi i; then
    v "More than one option for the input directory is set. I'll only use the last one."
  fi
  if option_set_multi o; then
    v "More than one option for the output directory is set. I'll only use the last one."
  fi
  if option_set_multi w; then
    v "More than one option for the webpath is set. I'll only use the last one."
  fi
  if option_set_multi n; then
    v "More than one option to set the number of entries per page is set. I'll only use the last one."
  fi
  if option_set_multi z; then
    v "More than one option to set the thumbnail size is set. I'll only use the last one."
  fi
  if option_set_multi h; then
    v "I can only display one help topic or the entire help. Use '${scriptname} --help' to get all the help."
  fi
}


set_option() {
  options=$(printf "${options}${1}")						# Add option letter to the string so we can easily check later whether it was set.
  d "Arg for set_option '${1}': '${2}'"
  case "${1}" in
    g) [[ ! "${2}" =~ (^-|^$) ]] && desired_gallery="${2}"			# This is not actually implemented in the gallery generation function, yet.
    ;;
    e) [[ ! "${2}" =~ (^-|^$) ]] && desired_entry="${2}"			# This is not actually implemented in the entry generation function, yet.
    ;;
    p) [[ ! "${2}" =~ (^-|^$) ]] && desired_page="${2}"				# This is not actually implemented in the page generation function, yet.
    ;;
    t) [[ ! "${2}" =~ (^-|^$) ]] && desired_tagpage="${2}"			# This is not actually implemented in the tagpage generation function, yet.
    ;;
    s) [[ ! "${2}" =~ (^-|^$) ]] && style="${2:-style}"				# Do nothing if no argument for option -s is provided. This ensures that the option
    ;;										#   -s that is included in option -c will not overwrite a chosen style name.
    i) [[ ! "${2}" =~ (^-|^$) ]] && shellbase="${2}" || error_exit "Option -i or --input needs an argument."
    ;;
    o) [[ ! "${2}" =~ (^-|^$) ]] && odir="${2}" || error_exit "Option -o or --output needs an argument."
    ;;
    w) [[ ! "${2}" =~ (^-|^$) ]] && webbase="${2}" || error_exit "Option -w or --webpath needs an argument."
    ;;
    n) [[ ! "${2}" =~ (^-|^$) ]] && perpage="${2}" || error_exit "Option -n or --perpage needs an argument."
    ;;
    z) [[ ! "${2}" =~ (^-|^$) ]] && thumbsize="${2}" || error_exit "Option -z or --thumbnail needs an argument."
    ;;
    h) print_help
    ;;
  esac
  [[ ! "${2}" =~ (^-|^$) ]]							# Return 0 if the next argument belongs to the first one.
										#   This enables the possibility to use this function to check whether there is an
										#   argument provided for an option. (Whether the next argument exists but does
										#   not start with "-".)
}



if test -f "${shellbase}/settings"; then					# If there is a settings file in directory where the website source resides ...
  source "${shellbase}/settings"						#   ... overwrite the variables that are being set at the top of this script file.
else
  error_exit "There is no settings file in \"${shellbase}\". Are you sure this is where the source files of the website you want to generate are? If the path is correct and you don't want to set any setting, touch \"${shellbase}/settings\" to create an empty settings file."
fi


if [ ${#} -eq 0 ]; then error_exit "I don't know what to do. Please --help!"; fi # Don't do anything if no argument/option is provided.
while [ ${#} -gt 0 ]
do
  case "${1}" in
    -c|--complete) set_option bgfs						# A complete re-build consists of generating blog, galleries, entries and pages and
#    -c|--complete) set_option bgeptfs						# A complete re-build consists of generating blog, galleries, entries and pages and
    ;;										#   copying files and styles.
    -b|--blog) set_option b
    ;;
    -g|--gallery|--galleries) set_option g "${2}" && shift			# Set option g, skip next argument if it's an argument belonging to -g
    ;;
    -e|--entry|--entries) set_option e "${2}" && shift				# Set option e, skip next argument if it's an argument belonging to -e
    ;;
    -p|--page|--pages) set_option p "${2}" && shift				# Set option p, skip next argument if it's an argument belonging to -p
    ;;
    -t|--tagpage|--tagpages) set_option t "${2}" && shift			# Set option t, skip next argument if it's an argument belonging to -t
    ;;
#    -r|--rss) set_option r       # This is part of option -b now.
#    ;;
    -f|--files) set_option f
    ;;
    -s|--style|--styles) set_option s "${2}" && shift				# Set option s, skip next argument if it's an argument belonging to -s
    ;;
    -i|--input) set_option i "${2}" && shift					# Set option i, skip next argument if it's an argument belonging to -i
    ;;
    -o|--output) set_option o "${2}" && shift					# Set option o, skip next argument if it's an argument belonging to -o
    ;;
    -w|--webpath) set_option w "${2}" && shift					# Set option w, skip next argument if it's an argument belonging to -w
    ;;
    -n|--perpage) set_option n "${2}" && shift					# Set option n, skip next argument if it's an argument belonging to -n
    ;;
    -z|--thumbnail) set_option z "${2}" && shift				# Set option z, skip next argument if it's an argument belonging to -z
    ;;
    -m|--magic) set_option m; v "Mixing in some magic color. 🌈"
    ;;
    -v|--verbose) set_option v
    ;;
    -vv|--very-verbose) set_option vv
    ;;
    -h|-\?|--help) set_option h "${2}"						# Include second argument because I may want to have different help pages in future.
    ;;
    -d|--debug) set_option dvv							# Set debug mode, which includes very verbose mode.
    ;;
    -*) error_exit "Unfamiliar option '${1}' - see '${scriptname} --help'"
    ;;
    *) error_exit "Undesired argument '${1}' - see '${scriptname} --help'"
    ;;
  esac
  shift
done


check_options

#dir="${BASH_SOURCE%/*}"							# This block was used to source shell files from the same directory.
#if [[ ! -d "${dir}" ]]; then dir="${PWD}"; fi
#d dir: ${dir}

prepare
option_set s && copy_styles							# Option -s or --style or --styles
option_set b && {								# Option -b or --blog
  gen_sidebar
  gen_tagpages
  gen_entries
  gen_rss
}
option_set p && gen_sidebar && gen_pages					# Option -p or --page or --pages
option_set e && gen_sidebar && gen_entries					# Option -e or --entry or --entries
option_set t && gen_sidebar && gen_tagpages					# Option -t or --tagpage or --tagpages
option_set g && gen_sidebar && gen_galleries					# Option -g or --gallery or --galleries
option_set f && copy_files							# Option -f or --files

date --iso-8601 > "${shellbase}/last"						# Log last complete generation date and time.

call_function_if_declared hook_end

v "I'm done now. ✅"

clean_up


error_exit "Script ended unexpededly-ish."


################################################################################################################################################################
# todo:
#
# v0.9.0 milestones:
# - bug: footer is nnot printed on tagpages with pager
# - list entries with subtops indented below their parent's entries
# - responsive css
# - let visitors filter by author and tags at the same time
# - generate single page, tagpage, entry with command line options
# - test command line options
# - either move preparations to the functions where they are needed or put everything that is needed for any option in prepare funtion
#
# v1.0.0 milestones:
# - check and improve rss validity
# - extensive testing
# - code quality improvements
# - finish README (list and explain tag types, options and so on)
# - finish HOWTO
# - finish INSTALL
#
#
# - stickied entries for topic tagpages
# - don't print title-wrappers of stickied entries. possible criteria: sort (sortby) starts with "stick". no, better when it starts with a letter
# - escape in HTML content only where not used for markup: &nbsp; →   (U+00A0), &amp; → & (U+0026), &lt; → < (U+003C), &gt; → > (U+003E), &quot; → " (U+0022), &apos; → ' (U+0027)
# - don't generate desired tagpage if there will be no entries on it
# - explain variables in comments where they are declared and possibly also their usage in relevant places
# - change all variable names to lower case, maybe make obvious by name that a variable is global
# - make sure all function return a (meaningful) exit code
# - create an rss file for each tagpage
# - also look for entries, pages, galleries in subdirectories
# - list entries with no top: tag under top:none or something similar?
# - also generate tagpages that exclude entries that have a specific tag? (like cat:incomplete)
# - option to choose how detailed the html sstructure should be?
# - finish reditect implemention
# - sort tags in title-wrappers according to tag type?
# - convert/escape urls in rss tags (no spaces and stuff)
# - convert/properly escape content/description in rss feed (html escape stuff for spaces, <, >, ...)
# - allow more file name extensions for gallery image files (upper/lower, ..., more types, SVG?, JPEG2000?)
# - test hooks for functions in settings file
# - inlude parent tags and tag types in classes of tag spans?
# - correct date format (RFC 2822) auch für pubDate in RSS feeds
# - exclude stickied posts from rss feeds
# - properly escape special XML characters in RSS feed
# - append thumbnails of related pictures to rss items
# - create install script
# - vv: info about individual tagpages
# - introduce "sort:" tagtype, beats edited and created
# - introduce "redirect:"
# - clean up HTML code (broken tags)
# - continue README
# - write INSTALL
# - write HOWTO
# - improve image gallery generator
#    - gallery page html and styling
#    - create index.html in html/galleries/ with list of galleries?
# - use SVGs für tagicons?
# - CSS: collapse tags, dropdown for langguage (and authors?), leave topics expanded by default
# - get the global arrays in order. check where they actually have to be global, find clearer names for them, introduce one for entries that contains a newline separated list of tags for that entry (all tags including author, sort and title)
# - convert images in parallel if parallel is available
# - use find instead of ls to be safer and include files in subdirectories but not the directories themselves
# - check file types of source and image files
# - video support in galleries
# - audio player at the end of an entry with a relevant audio file
# - [] tags for image and video embedding and other stuff?
# - add webbase to src="/ and href="/ and the like?
#    - identify relative links/URLs and add $webbase to them
#    - test with webbase other than "/"
# - cut off long entries on tagpages and all.html?
# - remove commented out lines that are not needed anymore
# - test subcategories
# - test subsubsubsubetccategories and topics
# - test odir option arguments
# - test whether missing image files in the odir get regenerated (and dots are printed with option v)
# - if no file in the input directory was touched since the last generation, don't do anything
#     - should be possible to be overwritten with option --force
# - maybe: only generate new/edited stuff by default, use -a to force to generate all entries/galleries/pages
#     - maybe also use option --force to overwrite that
# - test -o and -i options
# - sidebar is not generated when using option -g or -p without -b or -c - it is needed though
# - gallery generation status info gets printed without option -v - that shouldn't be
# - "Only print title and pageinfo if it's tagpage of individual tag and not all.html.": Use case instead of if, include a line for all.html like "This page shows..."
# - bug: CSS files don't get overwritten if file exists in output css dir?
################################################################################################################################################################
