meso-web

Sources of the |Méso|Star> website
git clone git://git.meso-star.fr/meso-web.git
Log | Files | Refs | README | LICENSE

commit e59cf57b9dedd66c88ada95a31176d94fdfa8671
parent f807d60c1c08812d665d7e451114e87946194f38
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 12 Sep 2025 16:15:54 +0200

Use the Star-Typesetting toolchain instead of internal tools

In reality, Star-Typesetting brings together tools prototyped here and
is therefore simply a reissue of what was done by internal tools.

The hooks and internal scripts have simply been updated to reflect the
change in names and locations of the tools. And the print_downloads
routine included in the meso-web.sh script has become a standalone
script.

Diffstat:
MMakefile | 35+++++++++--------------------------
MREADME.md | 1+
Mconfig.mk | 4----
Mhtrdr/hooks/01-generate-man.sh | 2+-
Mhtrdr/hooks/01-setup-redirections.sh | 2+-
Mschiff/hooks/01-generate-man.sh | 2+-
Mschiff/schiff-downloads.sh | 3+--
Dscripts/hooks.sh | 131-------------------------------------------------------------------------------
Dscripts/index.sh | 33---------------------------------
Dscripts/list.sh | 114-------------------------------------------------------------------------------
Dscripts/meso-web.sh | 186-------------------------------------------------------------------------------
Ascripts/print_downloads.sh | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msolstice/hooks/01-generate-man.sh | 2+-
Msolstice/solstice-downloads.sh | 3+--
Mstardis/hooks/01-generate-man.sh | 4++--
Dtools/generate_header.c | 886-------------------------------------------------------------------------------
16 files changed, 148 insertions(+), 1390 deletions(-)

diff --git a/Makefile b/Makefile @@ -18,8 +18,6 @@ include config.mk -GEN_HEADER=tools/generate_header - LINT=$(HTML:.html=.lint) $(SH:.sh=.shlint) templates/redirect.lint MD=$(SHTML:.sh=.md) @@ -27,45 +25,30 @@ default: build build clean distclean lint install: hooks index @$(MAKE) -f.hooks -f.index -fMakefile $@__ \ - HTML=" $$($(SHELL) ./scripts/list.sh html | tr '\n' ' ')" \ - SHTML="$$($(SHELL) ./scripts/list.sh shtml | tr '\n' ' ')" \ - HOOK=" $$($(SHELL) ./scripts/list.sh hook | tr '\n' ' ')" \ - SH=" $$($(SHELL) ./scripts/list.sh shell | tr '\n' ' ')" + HTML=" $$($(SHELL) sty-list html | tr '\n' ' ')" \ + SHTML="$$($(SHELL) sty-list shtml | tr '\n' ' ')" \ + HOOK=" $$($(SHELL) sty-list hook | tr '\n' ' ')" \ + SH=" $$($(SHELL) sty-list shell | tr '\n' ' ')" hooks index: - $(SHELL) scripts/$@.sh > .$@ + $(SHELL) sty-$@ > .$@ build__: $(HTML) $(HOOK:.sh=.hook) -# Make generate_header a dependency of hook execution (since they can use it) -$(HOOK:.sh=.hook): $(GEN_HEADER) - clean__: rm -f .hooks rm -f $(HTML) rm -f $(SHTML:.sh=.md) -distclean__: clean__ - rm -f $(GEN_HEADER) $(GEN_HEADER).o - install__: $(HTML) $(SIG) @rsync -avzrR --delete-after --progress \ - meso.css $(HTML) $$($(SHELL) ./scripts/list.sh subdir) $(RESOURCES) \ + meso.css $(HTML) $$($(SHELL) sty-list subdir) $(RESOURCES) \ $(PREFIX) ################################################################################ -# Tools -################################################################################ -.c.o: - $(CC) $(CFLAGS) -c $< -o $@ - -tools/generate_header: tools/generate_header.o - $(CC) $(CFLAGS) -o $@ tools/generate_header.o $(LDFLAGS) - -################################################################################ # Generate content ################################################################################ -$(HTML): $(GEN_HEADER) menu.tsv templates/footer.html +$(HTML): menu.tsv templates/footer.html .sh.md: @cd -- "$$(dirname "$<")"; \ @@ -74,7 +57,7 @@ $(HTML): $(GEN_HEADER) menu.tsv templates/footer.html .md.html: @echo "Building $@" @{ \ - $(GEN_HEADER) $${PWD}/$@; \ + sty-genhead $${PWD}/$@; \ $(MD2HTML) $<; \ cat templates/footer.html; \ } > $@ @@ -96,6 +79,6 @@ $(HTML): $(GEN_HEADER) menu.tsv templates/footer.html cd -- "$${i%%/*}"; \ i="$${i#*/}"; \ fi; \ - shellcheck -o all -x "$${i}" + shellcheck -e SC1091 -o all -x "$${i}" lint__: $(LINT) diff --git a/README.md b/README.md @@ -5,6 +5,7 @@ generating it. ## Requirments +- star-ty - POSIX make - POSIX shell - git-wad diff --git a/config.mk b/config.mk @@ -7,7 +7,3 @@ MD2HTML=md2html # Additional resources to install RESOURCES=fonts - -# Compilation/Linker flags used to compile build system tools -CFLAGS=-std=c99 -Wall -Wextra -Os -LDFLAGS=-Wl,-z,relro,-z,now -pie diff --git a/htrdr/hooks/01-generate-man.sh b/htrdr/hooks/01-generate-man.sh @@ -32,7 +32,7 @@ man2html() # output_filename { { cd .. - tools/generate_header "${OLDPWD##*/}/$1" + sty-genhead "${OLDPWD##*/}/$1" mandoc -O man=../man%S/%N.%S.html,fragment -I os=UNIX -T html cat ./templates/footer.html cd "${OLDPWD}" diff --git a/htrdr/hooks/01-setup-redirections.sh b/htrdr/hooks/01-setup-redirections.sh @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -. "../scripts/meso-web.sh" +. "sty.sh" set -e diff --git a/schiff/hooks/01-generate-man.sh b/schiff/hooks/01-generate-man.sh @@ -32,7 +32,7 @@ man2html() # output_filename { { cd .. - tools/generate_header "${OLDPWD##*/}/$1" + sty-genhead "${OLDPWD##*/}/$1" sh ./scripts/convert_man.sh cat ./templates/footer.html cd "${OLDPWD}" diff --git a/schiff/schiff-downloads.sh b/schiff/schiff-downloads.sh @@ -16,7 +16,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. . "./config.sh.in" -. "../scripts/meso-web.sh" set -e @@ -24,7 +23,7 @@ set -e echo '# Downloads' # List of archives to download -print_downloads "${PWD%%*/}" Schiff Linux +sh ../scripts/print_downloads.sh "${PWD%%*/}" Schiff Linux # Release notes sed -n '/^## Release notes/,/^## License/p' "${readme}" | sed '$d' diff --git a/scripts/hooks.sh b/scripts/hooks.sh @@ -1,131 +0,0 @@ -#!/bin/sh - -# Copyright (C) 2017-2025 |Méso|Star> (contact@meso-star.com) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -. "./scripts/meso-web.sh" - -set -e - - -# Print on standard output the Makefile targets used to automate hook -# management - -shtml="$(sh ./scripts/list.sh shtml)" -hook="$(sh ./scripts/list.sh hook)" - -sections | while read -r i; do - shtml_section="$(printf '%s\n' "${shtml}" \ - | sed -n "/^${i}\//{s/\.sh/\.md/g;p;}" | tr '\n' ' ')" - hook_section="$(printf '%s\n' "${hook}" \ - | sed -n "/^${i}\//p" | tr '\n' ' ' \ - | sed -e 's/\.sh[[:space:]]/.hook /g')" - - # Define Makefile target that makes section hooks prerequisites for - # the HTML content of the section. - # - # It is the files generated by each of the hooks that are - # prerequisites for the HTML files, not the scripts of the hooks - # themselves, because even if the scripts have not changed, their - # output may have been updated, which can impact the HTML content - # generated at build time. - # - # Furthermore, it is not the HTML file itself that depends on the - # hook's output, but rather the intermediate Markdown dynamically - # generated by the script, whose output may depend on the execution of - # hooks. - printf '%s: %s\n' "${shtml_section}" "${hook_section}" -done - -printf '%s\n' "${hook}" | while read -r i; do - # Divide the path into two parts: - # - the top-level directory, i.e. the section - # - the shell script to be executed from the section directory - section="${i%%/*}" - hook="${i##"${section}"/}" - - # The hook is the first one in the current section: it has the highest - # priority and therefore has no prerequisites. - if [ "${section}" != "${prev_section}" ]; then - dep="" - tgt_list="" - prev_priority="" - fi - - # Retrieve the hook priority - priority="$(basename "${i}")" - priority="${priority%%-*}" - - # Define a name for the hook used as a prefix for Makefile targets in - # order to automate its execution and the management of the file it - # generates. To ensure that this name is unique, and thus avoid - # conflicts between different hooks, it is constructed from the names - # of the section and file corresponding to the hook. - file="$(basename "${hook}")" - prefix="${section}-${file%%.*}" - - # Set the file in which the names of the files generated by the hook - # are stored as resources for the website, i.e., the files that must - # be deployed with the website. - tgt="${i%%.*}.hook" - - if [ "${priority}" = "${prev_priority}" ]; then - # The hook's priority is the same as the previous one. Add its - # target to the list of prerequisites for hooks with lower priority - tgt_list="${tgt_list} ${tgt}" - else - # The priority of the current hook is lower than those processed - # previously. Define as a prerequisite the execution of hooks whose - # priority precedes the priority of the current hook. - dep="${tgt_list}" - - # The execution of the current hook becomes a prerequisite for hooks - # with lower priority. - tgt_list="${tgt}" - fi - - # Define the Makefile target that automate the hook executation. Its - # pre-requisites are the hook script and the output of the # previous - # hook in the section, i.e. with an higher priority. So that - # execution priority is ensured. - # - # Finally, make this target a prerequisite for building the website - # in order to enforce its execution during the build call. - printf '%s: %s %s\n' "${tgt}" "${i}" "${dep}" - printf ' @cd -- %s; ' "${section}" - printf "\$(SHELL) %s > \$\${OLDPWD}/\$@ || rm -f \$\${OLDPWD}/\$@\n" \ - "${hook}" - - # Set the target for cleaning files generated by executing the hook. - # Run it on "distclean" and not on "clean", as their generation can be - # costly while effectively being files generated to "distribute" the - # website. - printf '%s-clean:\n' "${prefix}" - printf ' if [ -f %s ]; then cat %s | xargs rm -f; fi\n' \ - "${tgt}" "${tgt}" - printf ' rm -f %s\n' "${tgt}" - printf 'distclean__: %s-clean\n' "${prefix}" - - # Set the target that installs website resources generated by the hook - printf '%s-install: %s\n' "${prefix}" "${tgt}" - printf " @rsync -avzrR --files-from=%s ./ \$(PREFIX)\n" \ - "${tgt}" - printf 'install__: %s-install\n' "${prefix}" - - # Save the current section and priority of the hook that has just been - # processed - prev_section="${section}" - prev_priority="${priority}" -done diff --git a/scripts/index.sh b/scripts/index.sh @@ -1,33 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2017-2025 |Méso|Star> (contact@meso-star.com) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -. "./scripts/meso-web.sh" - -set -e - -# Print to standard output the Makefile targets that make the section -# index file a prerequisite for the section's HTML content. - -html="$(sh ./scripts/list.sh html)" - -sections | while read -r i; do - html_section="$(printf '%s\n' "${html}" \ - | sed -n "/^${i}\//p" \ - | tr '\n' ' ')" - - printf '%s: %s/index.tsv\n' "${html_section}" "${i}" -done diff --git a/scripts/list.sh b/scripts/list.sh @@ -1,114 +0,0 @@ -#!/bin/sh - -# Copyright (C) 2017-2025 |Méso|Star> (contact@meso-star.com) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -. "./scripts/meso-web.sh" - -set -e - -if [ "$#" -lt 1 ]; then - >&2 printf 'usage: %s <html|hook|shell|sig>\n' "${0##*/}" - exit 1 -fi - -######################################################################## -# Helper functions -######################################################################## -# List of HTML files to generate from Markdown files or shell scripts -# stored in each section -html() -{ - _find_args="$(\ - printf '%s\n' "${sections}" \ - | sed -e 's/^/! -path /' \ - | tr '\n' ' ')" - - eval "exec find ${search_dirs} ${_find_args} -type d -prune -o \ - \( \ - -name \"*.md\" \ - -o -name \"*.sh\" \ - \) -exec sh -c 'printf \"%s.html\n\" \"\${1%.*}\"' -- {} \; \ - | sort" -} - -# List of shell scripts that dynamically generate HTML content -shtml() -{ - _find_args="$(\ - printf '%s\n' "${sections}" \ - | sed -e 's/^/! -path /' \ - | tr '\n' ' ')" - - eval "exec find ${search_dirs} ${_find_args} -type d -prune -o \ - -name \"*.sh\" -print \ - | sort" -} - -# List of hooks, i.e. shell scripts to be run by section -hook() -{ - _find_args="$(\ - printf '%s\n' "${sections}" \ - | sed -e 's/^/-path "/;s/$/\/hooks\/*" -o/' \ - | tr '\n' ' ' \ - | sed 's/ -o $//')" - - eval "find ${search_dirs} \ - \( \( ${_find_args} \) -type f -name \"[0-9][0-9]-*.sh\" \) \ - | sort" -} - -# List the shell scripts relevant to the compilation system, i.e. the -# shell script at the root of the working tree and those in each section -# used to generate HTML pages. -shell() -{ - _find_args="$(\ - printf '%s\n' "${sections}" \ - | sed -e 's/^\(.\{0,\}\)$/! -path \1 ! -path \1\/hooks/' \ - | tr '\n' ' ')" - - eval "exec find scripts ${search_dirs} \ - ! -path scripts ${_find_args} -type d -prune -o -name \"*.sh\" -print \ - | sort" -} - -# List the relevent section subdirectories which are: -# - downloads -# - images -# - thumbs -subdir() -{ - downloads="-path \1/downloads" - images="-path \1/images -o -path \1/thumbs" - - _find_args="$(\ - printf '%s\n' "${sections}" \ - | sed "s;^\(.\{0,\}\)$;${downloads} -o ${images} -o;" \ - | tr '\n' ' ' \ - | sed 's/ -o $//')" - - eval "exec find ${search_dirs} ${_find_args} -type d | sort" -} - -######################################################################## -# Script -######################################################################## -# List of sections to be dealt with -sections="$(sections)" -search_dirs="$(printf '%s\n' "${sections}" | tr '\n' ' ')" - -"$@" diff --git a/scripts/meso-web.sh b/scripts/meso-web.sh @@ -1,186 +0,0 @@ -#!/bin/sh - -# Copyright (C) 2017-2025 |Méso|Star> (contact@meso-star.com) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -# Delete irrelevant data -strip_dummy() # stdin -{ - # - Remove comments - # - Remove empty lines - # - Remove heading an trailing spaces - sed 's/#.\{0,\}$//g' \ - | sed '/^[[:space:]]\{0,\}$/d' \ - | sed -e 's/^[[:space:]]\{1,\}//g' -e 's/[[:space:]]\{1,\}$//g' -} - -# List the sections of the menu -sections() -{ - # - Remove irrelevant data from the menu file - # - Get the section field - # - Protect spaces in the section name - strip_dummy < menu.tsv \ - | cut -d' ' -f2 \ - | sed -e 's/[[:space:]]/\\ /g' \ - | xargs -I{} sh -c \ - "if [ -f \"\$1/index.tsv\" ]; then \ - printf '%s\n' \"\$1\"; \ - fi" -- {} -} - -# Print the absolute dir of the input file -absdir() # file -{ - if [ -d "$1" ]; then - cd -- "$1" || exit 1 - else - cd -- "$(dirname "$1")" || exit 1 - fi - echo "${PWD}" - cd "${OLDPWD}" || exit 1 -} - -# Print relative path from input file to a dir -relpath_to_dir() # file, dir -( - # Build directory from worktree to path - _dir0="$(absdir "$1")" - _dir1="$(absdir "$2")" - - _dir0="$(printf '%s\n' "${_dir0}" | sed "s;^${_dir1}[/]\{0,\};;g")" - - # Ensure that the directory is a subpath of the worktree - if ! [ -d "${_dir1}/${_dir0}" ]; then - >&2 printf '%s: %s is not a subdirectory of %s\n' \ - "$0" "${_dir0}" "${_dir1}" - return 1 - fi - - echo "${_dir0}" | sed 's/\//\n/g' | while read -r _i; do - printf "../" - done -) - -# Inline the HTML formatting of a table listing archive to download -print_downloads() # section, archive_prefix, OS ... -( - _section="$1" - _prefix="$2" - - shift 2 - - for _i in "$@"; do - case "${_i}" in - [Ll]inux) _os_linux="1" ;; - [Ww]indows) _os_windows="1" ;; - *) ;; # Unsuported OS - esac - done - - cd -- "${_section}" || exit 1 - - # Table header - echo '<table class="list">' - echo ' <tr>' - echo ' <th>Version</th>' - if [ -n "${_os_linux}" ]; then - echo ' <th>GNU/Linux 64-bits</th>' - fi - if [ -n "${_os_windows}" ]; then - echo ' <th>Windows 64-bits</th>' - fi - echo ' <th>Sources</th>' - echo ' </tr>' - - # Define the basic regular expression of a version number - _version_re="[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}" - _version_re="${_version_re}\(-r[0-9]\{1,\}\)\{0,1\}" - - # Browse all tarball files in the "downloads" subdirectory. Sort them - # in descending order according to lexicographical order. This may - # result in an incorrect order with regard to the version number. For - # example, version 9 will be considered higher than version 10. Some - # implementations offer an option to process version strings - # naturally, but this is not POSIX-compliant. So let's leave it as is, - # as there are currently no sorting issues. - find downloads -name "${_prefix}-*.tar.gz" \ - | grep -e "${_prefix}-${_version_re}-[^\(Sources\)].\{0,\}\.tar\.gz" \ - | sort -r \ - | while read -r _arch; do - - # Extract the version from - _version=$(echo "${_arch}" \ - | sed "s/downloads\/${_prefix}-\(${_version_re}\).\{0,\}$/\1/g") - - # Setup archive names - _linux="${_arch}" - _windows="downloads/${_prefix}-${_version}-Win64.zip" - _source1="downloads/${_prefix}-${_version}-Sources.zip" - _source2="downloads/${_prefix}-${_version}-Source.zip" - _source3="downloads/${_prefix}-${_version}-Sources.tar.gz" - _source4="downloads/${_prefix}-${_version}-Source.tar.gz" - - # Define the list to be referenced in each cell of the current - # version: first the GNU/Linux archive, then the Windows archive, - # and finally the corresponding sources. Since sources can have - # multiple names, find the one that matches the archive version, - # otherwise use a default name. The existence of the various - # archives available for download is verified below. The purpose - # here is simply a matter of providing three file names that could - # fill the cells in the row. - _dl="" - if [ -n "${_os_linux}" ]; then _dl="${_dl} ${_linux}"; fi - if [ -n "${_os_windows}" ]; then _dl="${_dl} ${_windows}"; fi - if [ -f "${_source1}" ]; then _dl="${_dl} ${_source1}"; - elif [ -f "${_source2}" ]; then _dl="${_dl} ${_source2}"; - elif [ -f "${_source3}" ]; then _dl="${_dl} ${_source3}"; - else _dl="${_dl} ${_source4}"; fi - - printf ' <tr>\n' # Let's get started on filling the line - - # Print the version in the first cell of the row - printf ' <td>%s</td>\n' "${_version}" - - # Iterate over the 3 filenames previously define and provide a link - # to it if their exist onto disk. For instance, a GNU/Linux archive - # can be provided while a Windows version is missing. - for _i in ${_dl}; do - - printf ' <td>\n' - - if [ -f "${_i}" ]; then - # The archive exists. Display a link to it whose label depends - # on the archive type (tarball or zip) - printf ' [<a href="%s">' "${_i}" - [ "${_i#*tar.gz}" != "${_i}" ] && printf 'tarball' || printf 'zip' - printf '</a>]\n' - fi - - # Display a link to the archive signature, if it exists - if [ -f "${_i}.sig" ]; then - printf ' [<a href="%s.sig">pgp</a>]\n' "${_i}" - fi - printf ' </td>\n' - done - - printf ' </tr>\n' # That's all for this row - done - - # The table is complete. Don't forget to add a line break after it to - # signal to Markdown that the embedded HTML code has ended. - printf '</table>\n' - printf '\n' -) diff --git a/scripts/print_downloads.sh b/scripts/print_downloads.sh @@ -0,0 +1,130 @@ +#!/bin/sh + +# Copyright (C) 2017-2025 |Méso|Star> (contact@meso-star.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +set -e + +if [ "$#" -lt 3 ]; then + >&2 printf 'usage: %s section prefix\n' "${0##*/}" + exit 1 +fi + +section="$1" +prefix="$2" + +shift 2 + +for i in "$@"; do + case "${i}" in + [Ll]inux) os_linux="1" ;; + [Ww]indows) os_windows="1" ;; + *) ;; # Unsuported OS + esac +done + +cd -- "${section}" || exit 1 + +# Table header +echo '<table class="list">' +echo ' <tr>' +echo ' <th>Version</th>' +if [ -n "${os_linux}" ]; then + echo ' <th>GNU/Linux 64-bits</th>' +fi +if [ -n "${os_windows}" ]; then + echo ' <th>Windows 64-bits</th>' +fi +echo ' <th>Sources</th>' +echo ' </tr>' + +# Define the basic regular expression of a version number +version_re="[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}" +version_re="${version_re}\(-r[0-9]\{1,\}\)\{0,1\}" + +# Browse all tarball files in the "downloads" subdirectory. Sort them +# in descending order according to lexicographical order. This may +# result in an incorrect order with regard to the version number. For +# example, version 9 will be considered higher than version 10. Some +# implementations offer an option to process version strings +# naturally, but this is not POSIX-compliant. So let's leave it as is, +# as there are currently no sorting issues. + find downloads -name "${prefix}-*.tar.gz" \ +| grep -e "${prefix}-${version_re}-[^\(Sources\)].\{0,\}\.tar\.gz" \ +| sort -r \ +| while read -r arch; do + + # Extract the version from + version=$(echo "${arch}" \ + | sed "s/downloads\/${prefix}-\(${version_re}\).\{0,\}$/\1/g") + + # Setup archive names + linux="${arch}" + windows="downloads/${prefix}-${version}-Win64.zip" + source1="downloads/${prefix}-${version}-Sources.zip" + source2="downloads/${prefix}-${version}-Source.zip" + source3="downloads/${prefix}-${version}-Sources.tar.gz" + source4="downloads/${prefix}-${version}-Source.tar.gz" + + # Define the list to be referenced in each cell of the current + # version: first the GNU/Linux archive, then the Windows archive, + # and finally the corresponding sources. Since sources can have + # multiple names, find the one that matches the archive version, + # otherwise use a default name. The existence of the various + # archives available for download is verified below. The purpose + # here is simply a matter of providing three file names that could + # fill the cells in the row. + dl="" + if [ -n "${os_linux}" ]; then dl="${dl} ${linux}"; fi + if [ -n "${os_windows}" ]; then dl="${dl} ${windows}"; fi + if [ -f "${source1}" ]; then dl="${dl} ${source1}"; + elif [ -f "${source2}" ]; then dl="${dl} ${source2}"; + elif [ -f "${source3}" ]; then dl="${dl} ${source3}"; + else dl="${dl} ${source4}"; fi + + printf ' <tr>\n' # Let's get started on filling the line + + # Print the version in the first cell of the row + printf ' <td>%s</td>\n' "${version}" + + # Iterate over the 3 filenames previously define and provide a link + # to it if their exist onto disk. For instance, a GNU/Linux archive + # can be provided while a Windows version is missing. + for i in ${dl}; do + + printf ' <td>\n' + + if [ -f "${i}" ]; then + # The archive exists. Display a link to it whose label depends + # on the archive type (tarball or zip) + printf ' [<a href="%s">' "${i}" + [ "${i#*tar.gz}" != "${i}" ] && printf 'tarball' || printf 'zip' + printf '</a>]\n' + fi + + # Display a link to the archive signature, if it exists + if [ -f "${i}.sig" ]; then + printf ' [<a href="%s.sig">pgp</a>]\n' "${i}" + fi + printf ' </td>\n' + done + + printf ' </tr>\n' # That's all for this row +done + +# The table is complete. Don't forget to add a line break after it to +# signal to Markdown that the embedded HTML code has ended. +printf '</table>\n' +printf '\n' diff --git a/solstice/hooks/01-generate-man.sh b/solstice/hooks/01-generate-man.sh @@ -32,7 +32,7 @@ man2html() # output_filename { { cd .. - tools/generate_header "${OLDPWD##*/}/$1" + sty-genhead "${OLDPWD##*/}/$1" sh ./scripts/convert_man.sh cat ./templates/footer.html cd "${OLDPWD}" diff --git a/solstice/solstice-downloads.sh b/solstice/solstice-downloads.sh @@ -16,7 +16,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. . "./config.sh.in" -. "../scripts/meso-web.sh" set -e @@ -24,7 +23,7 @@ set -e echo '# Downloads' # List of archives to download -print_downloads "${PWD%%/*}" Solstice Linux Windows +sh ../scripts/print_downloads.sh "${PWD%%/*}" Solstice Linux Windows # Release notes sed -n '/^## Release notes/,/^## License/p' "${readme}" | sed '$d' diff --git a/stardis/hooks/01-generate-man.sh b/stardis/hooks/01-generate-man.sh @@ -15,8 +15,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +. "sty.sh" . "./config.sh.in" -. "../scripts/meso-web.sh" set -e @@ -39,7 +39,7 @@ man2html() # output_filename { cd .. - tools/generate_header "${OLDPWD##*/}/$1" + sty-genhead "${OLDPWD##*/}/$1" mandoc -O man=../man%S/%N.%S.html,fragment -I os=UNIX -T html cat ./templates/footer.html cd "${OLDPWD}" diff --git a/tools/generate_header.c b/tools/generate_header.c @@ -1,886 +0,0 @@ -/* Copyright (C) 2017-2025 |Méso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#define _XOPEN_SOURCE 500 /* realpath */ -#define _POSIX_C_SOURCE 200809L /* stndup */ - -#include <assert.h> -#include <errno.h> -#include <libgen.h> /* basename & dirname */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#define MENU_FILENAME "menu.tsv" /* File that lists the menu entries */ -#define MENU_MAX_ENTRIES 16 /* Maximum number of menu entries */ - -#define INDEX_FILENAME "index.tsv" /* List the indexed content of a section */ -#define INDEX_MAX_ENTRIES 32 /* Maximum number of indexed items per section */ - -#define DEFAULT_LANG "en" /* Default language */ - -static const char LANG[] = "@LANG@"; -const size_t SZLANG = sizeof(LANG) - 1/* '\0' */; - -static const char* g_cmd = NULL; /* Command name. Setuped on start-up */ - -struct menu { - struct mentry { - char* label; - char* section; - char* mem__; /* Allocated memory */ - } items[MENU_MAX_ENTRIES]; - - int nitems; -}; -static const struct menu MENU_NULL = {0}; - -struct index { - struct ientry { - char* label; - char* uri; - char* lang; - char* template; /* Original URI, i.e. not expanded */ - char* lang_list; /* List of available languages */ - char* mem__; /* Allocated memory */ - } items[INDEX_MAX_ENTRIES]; - - int nitems; -}; -static const struct index INDEX_NULL = {0}; - -struct str { - char* buf; - size_t sz; /* Capacity */ -}; -static const struct str STR_NULL = {0}; - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static void -str_release(struct str* s) -{ - assert(s); - if(s->buf) free(s->buf); - *s = STR_NULL; -} - -/* Read from fp the next non empty line. Returns null if an error occurs */ -static char* -rline(FILE* fp, struct str* s, int* lines_count/*#read lines*/) -{ - size_t nlines = 0; - - do { - size_t n = 0; /* #read chars for the current line */ - - do { - char *ptr = NULL; - - /* Allocate memory space */ - if(!s->buf) s->buf = realloc(s->buf, s->sz=128); - else if(n > 0) s->buf = realloc(s->buf, s->sz+=32); - if(!s->buf) { perror("realloc"); goto error; } - - /* Read line data */ - ptr = fgets(s->buf+n, s->sz - n, fp); - if(!ptr) { - if(ferror(fp)) { perror("fgets"); goto error; } - s->buf[n] = '\0'; /* No read char */ - } - - n = strlen(s->buf); /* Update the number of read chars */ - - } while(!strrchr(s->buf, '\n') && !feof(fp)); /* Are there any chars left? */ - ++nlines; - - s->buf[strcspn(s->buf, "\n\r#\0")] = '\0'; /* Remove new line and comments */ - - } while(strlen(s->buf)==strspn(s->buf, " \t") && !feof(fp)); /* Empty line */ - -exit: - *lines_count = nlines; - return s->buf; -error: - str_release(s); - goto exit; -} - -static void -menu_release(struct menu* menu) -{ - assert(menu); - for(int i=0; i < menu->nitems; ++i) { - if(menu->items[i].mem__) free(menu->items[i].mem__); - } - *menu = MENU_NULL; -} - -static int -menu_load(struct menu* menu, const char* workdir) -{ - struct str s = STR_NULL; - char* line = NULL; - FILE* fp = NULL; - int iline = 0; /* Line index */ - int n = 0; - int err = 0; - - assert(menu && workdir); - - *menu = MENU_NULL; - - /* Open the menu file */ - if(!(fp=fopen(MENU_FILENAME, "r"))) { - perror(__func__); - fprintf(stderr, - "%s: expecting the \"%s\" file in the \"%s\" working directory\n", - g_cmd, MENU_FILENAME, workdir); - goto error; - } - - for(line=rline(fp, &s, &n); line && line[0]!='\0'; line=rline(fp, &s, &n)) { - struct mentry* item = menu->items + menu->nitems; - char* ctx = NULL; - - iline += n; - - if(menu->nitems >= MENU_MAX_ENTRIES) { - /* There is too many menu entries to parse */ - errno = ENOMEM; - perror(__func__); - goto error; - } - - ++menu->nitems; - - /* Duplicate the line. It will be divided into sub-strings that the - * items's member variables will point to. */ - if(!(item->mem__ = strdup(line))) { perror("strdup"); goto error; } - - /* Setup item member variables */ - if(!(item->label = strtok_r(item->mem__, "\t", &ctx)) - || !(item->section = strtok_r(NULL, "\t", &ctx))) { - fprintf(stderr, "%s:%s:%d: invalid menu entry -- %s\n", - g_cmd, MENU_FILENAME, iline, line); - goto error; - } - } - - if(!line) goto error; /* A null lines means there is an error */ - -exit: - if(fp) fclose(fp); - str_release(&s); - return err; -error: - menu_release(menu); - err = 1; - goto exit; -} - -/* Resolve a pathname. Unlike realpath, handles paths to non-existent files */ -static char* -get_realpath(const char* path) -{ - char* out = NULL; /* realpath */ - char* buf = NULL; /* working copy of input path */ - char* tmp = NULL; /* temporary path */ - char *p0, *p1; - - if((out = realpath(path, NULL))) goto exit; - if(errno != ENOENT) goto error; - - /* The path does not name an existing file */ - - /* Ensure that path is absolute or start by "./" or "../" */ - if(path[0] == '/' || !strncmp(path, "./", 2) || !strncmp(path, "../", 3)) { - if(!(buf = strdup(path))) goto error; - } else { - if(!(buf = malloc(2/*"./"*/ + strlen(path) + 1/*'\0'*/))) goto error; - strcpy(buf, "./"); - strcat(buf, path); - } - - /* Remove path components until a path is found that exists */ - for(p0=strrchr(buf, '/'); p0; p1=p0, *p1='\0', p0=strrchr(buf, '/'), *p1='/') { - - *p0 = '\0'; - tmp = realpath(buf, NULL); - *p0 = '/'; - - if(!tmp) continue; /* Keep removing path component */ - - /* Concatante the existing path with the remaining compopnents */ - if(!(out = malloc(strlen(tmp) + strlen(p0) + 1/*'\0'*/))) goto error; - strcpy(out, tmp); - strcat(out, p0); - break; - } - -exit: - if(buf) free(buf); - if(tmp) free(tmp); - return out; -error: - perror(__func__); - if(out) { free(out); out = NULL; } - goto exit; -} - -/* Return the relative path from path to workdir. - * Path must lie in workdir. - * Return null if an error occurs */ -static char* -get_relpath(const char* workdir, const char* path) -{ - const size_t sz = strlen(workdir); - const char* ptr = NULL; - char* buf = NULL; - int n = 0; /* Number of directory to travels */ - int i; - - if(strncmp(workdir, path, sz) || path[sz] != '/') { - fprintf(stderr, "%s:%s: %s is not a subpath of %s\n", - g_cmd, __func__, path, workdir); - goto error; - } - - /* Count the number of directories between the workdir and the path */ - for(n = 0, ptr=path + sz + 1; (ptr=strchr(ptr, '/')); ++n) { - /* Successive '/' should be counted as a single '/' */ - while(++ptr, *ptr=='/'); - } - - if(!(buf = malloc(n*3/*"../"*/+1/*'\0'*/))) { perror("malloc"); goto error; } - for(i=0, buf[0]='\0'; i<n; strcat(buf, "../"), ++i); - -exit: - return buf; -error: - if(buf) { free(buf); buf = NULL; } - goto exit; -} - -/* Returns a pointer to a new index file in which the @LANG@ macro is resolved. - * Each entry in the original index file containing an @LANG@ macro is - * duplicated as many times as there are languages listed for that entry. In - * each of them, the @LANG@ macro is replaced by a value from the language in - * the order in which the languages are defined in the original index file. Then - * follow the language used to expand the @LANG@ macro and finally the - * original uri and the list of languages. - * - * For example, the following original string : - * - * Index\tindex-@LANG@.html\tfr:en\n - * - * is resolved in - * - * Index\tindex-fr.html\tfr\tindex-@LANG@.html\tfr:en\n - * Index\tindex-en.html\ten\tindex-@LANG@.html\tfr:en\n - * - * Return null if an error occurs */ -static FILE* -resolve_index(const char* path) -{ - char* lang_list = NULL; - - FILE* fp = NULL; - FILE* fp2 = NULL; - - struct str s = STR_NULL; - char* line = NULL; - int iline = 0; /* Line index */ - int n = 0; - - assert(path); - - /* Open the original index */ - if(!(fp = fopen(path, "r"))) { - fprintf(stderr,"%s: unable to find the file %s\n", g_cmd, path); - goto error; - } - - /* Open the resolved index */ - if(!(fp2 = tmpfile())) { perror("tmpfile"); goto error; } - - for(line=rline(fp, &s, &n); line && line[0]!='\0'; line=rline(fp, &s, &n)) { - char *label, *uri, *langs, *ctx; - - iline += n; - - label = strtok_r(line, "\t", &ctx); - uri = strtok_r(NULL, "\t", &ctx); - langs = strtok_r(NULL, "\t", &ctx); - - if(!label || !uri) { - fprintf(stderr, "%s:%s:%d: invalid index entry\n", g_cmd, path, iline); - goto error; - } - - if(!langs) { - /* There is no several languages, just write the original entry */ - fprintf(fp2, "%s\t%s\n", label, uri); - - } else { - char* l = NULL; - - /* Keep a copy of the string listing the available languages. This will be - * the last field of the expanded entries */ - if(lang_list) free(lang_list); - if(!(lang_list=strdup(langs))) { perror("strdup"); goto error; } - - /* Add as many index entries as there are languages available */ - for(l=strtok_r(langs, ":", &ctx); l; l=strtok_r(NULL, ":", &ctx)) { - char *ptr0, *ptr1; - - fprintf(fp2, "%s\t", label); /* Firstly, print the entry label */ - - /* Then print the expanded URI, i.e. the URI in which each instance of - * the @LANG@ macro is replaced by the current language */ - for(ptr0=uri; (ptr1 = strstr(ptr0, LANG)); ptr0=ptr1+SZLANG) { - *ptr1 = '\0'; - fprintf(fp2, "%s%s", ptr0, l); - *ptr1 = LANG[0]; - } - fprintf(fp2, "%s\t", ptr0); /* Rest of the URI */ - - /* Print the URI language as the 3rd field */ - fprintf(fp2, "%s\t", l); - - /* And finally, print the original URI (4th field) and the list of - * available languages (5th field). */ - fprintf(fp2, "%s\t%s\n", uri, lang_list); - } - } - } - - rewind(fp2); - -exit: - str_release(&s); - if(fp) fclose(fp); - if(lang_list) free(lang_list); - return fp2; -error: - if(fp2) { fclose(fp2); fp2 = NULL; } - goto exit; -} - -static void -index_release(struct index* index) -{ - assert(index); - for(int i=0; i < index->nitems; ++i) { - if(index->items[i].mem__) free(index->items[i].mem__); - } - *index = INDEX_NULL; -} - -static int -index_load(struct index* index, const char* section) -{ - struct str s = STR_NULL; - FILE* fp = NULL; - char* path = NULL; - char* line = NULL; - size_t sz = 0; - int i = 0; - int n = 0; - int err = 0; - - assert(index && section); - - /* Allocate the string to store the absolute path to the index file */ - sz = 2 /* "./" */ - + strlen(section) + 1/* '/' */ - + strlen(INDEX_FILENAME) + 1/* '\0' */; - if(!(path = malloc(sz))) { perror("malloc"); goto error; } - - /* Define the absolute path to the index file */ - i = snprintf(path, sz, "./%s/%s", section, INDEX_FILENAME); - if(i >= (int)sz) abort(); /* Unexpected error */ - - /* Resolve the index file, i.e. expand the @LANG@ macro */ - if(!(fp = resolve_index(path))) goto error; - - for(line=rline(fp, &s, &n); line && line[0]!='\0'; line=rline(fp, &s, &n)) { - struct ientry* item = index->items + index->nitems; - char* ctx = NULL; - - if(index->nitems >= INDEX_MAX_ENTRIES) { - /* There is too many menu entries to parse */ - errno = ENOMEM; - perror(__func__); - goto error; - } - - ++index->nitems; - - /* Duplicate the line. It will be divided into sub-strings that the - * items's member variables will point to. */ - if(!(item->mem__ = strdup(line))) { perror("strdup"); goto error; } - - item->label = strtok_r(item->mem__, "\t", &ctx); - item->uri = strtok_r(NULL, "\t", &ctx); - item->lang = strtok_r(NULL, "\t", &ctx); - item->template = strtok_r(NULL, "\t", &ctx); - item->lang_list = strtok_r(NULL, "\t", &ctx); - - if(!item->lang) item->lang = DEFAULT_LANG; - if(!item->template) item->template = item->uri; - if(!item->lang_list) item->lang_list = DEFAULT_LANG; - - /* Already check when resolving index */ - assert(item->label && item->uri); - } - - if(!line) goto error; /* A null lines means there is an error */ - if(index->nitems == 0) goto error; /* An empty index is an error */ - -exit: - if(path) free(path); - if(fp) fclose(fp); - str_release(&s); - return err; -error: - index_release(index); - err = 1; - goto exit; -} - -/* Returns null if no entry is found */ -const struct mentry* -menu_find(const struct menu* menu, const char* section) -{ - int i = 0; - assert(menu && section); - - for(i = 0; i < menu->nitems; ++i) { - if(!strcmp(menu->items[i].section, section)) break; - } - - return i < menu->nitems ? menu->items + i : NULL; -} - -/* Returns null if no entry is found */ -const struct ientry* -index_find - (const struct index* index, - const char* file) /* relative to the section */ -{ - int i = 0; - assert(index && file); - - for(i = 0; i < index->nitems; ++i) { - if(!strcmp(index->items[i].uri, file)) break; - } - - return i < index->nitems ? index->items + i : NULL; -} - -/* Return null if an error occurs */ -static char* -get_section(const char* workdir, const char* path) -{ - const size_t sz = strlen(workdir); - char *buf; - const char *b, *e; - - assert(workdir); - - if(strncmp(path, workdir, sz) != 0 || path[sz] != '/') { - fprintf(stderr, "%s:%s: %s is not a subpath of %s\n", - g_cmd, __func__, path, workdir); - return NULL; - } - - buf = (b = path+sz, *b != '/') || !(e = strchr(++b, '/')) - ? strdup(b) : strndup(b, e-b); - - if(!buf) perror(__func__); - return buf; -} - -/* Return path relative to workdir/section. path must be a subpath into - * workdir/section. Return null if an error occurs */ -static char* -get_section_path(const char* workdir, const char* section, const char* path) -{ - const size_t sz0 = strlen(workdir); - const size_t sz1 = strlen(section); - char* buf; - - if(strncmp(path, workdir, sz0) != 0 - || strncmp(path+sz0+1, section, sz1) != 0 - || path[sz0] != '/' - || path[sz0+1+sz1] != '/') { - fprintf(stderr, "%s:%s: %s is not a subpath in %s/%s\n", - g_cmd, __func__, path, workdir, section); - return NULL; - } - - buf = strdup(path + sz0 + 1/*'/ */ + sz1 + 1/*'/'*/); - if(!buf) perror("strdup"); - - return buf; -} - -/* Return null if an error occurs */ -static char* -get_workdir(void) -{ - char *buf, *ptr; - size_t sz = 256; /* Initial buffer size */ - - for(buf = ptr = NULL; !ptr; sz*=2) { - - if(!(buf = realloc(buf, sz))) { - perror("realloc"); - goto error; - } - - if(!(ptr = getcwd(buf, sz)) && errno != ERANGE) { - perror("getcwd"); /* Unforeseen error */ - goto error; - } - } - -exit: - return buf; -error: - if(buf) { free(buf); buf = NULL; } - goto exit; -} - -static void -print_head(const char* root, const char* label, const char* lang) -{ - assert(root && label && label); - - printf("<!DOCTYPE html>\n"); - printf("<html lang=\"%s\">\n", lang); - printf("<head>\n"); - printf(" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"); - printf(" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"); - printf(" <title>|M|S> %s</title>\n", label); - printf(" <link rel=\"stylesheet\" title=\"default\" href=\"%smeso.css\">\n", root); - printf("</head>\n"); - printf("<body>\n"); -} - -static void -print_menu1 - (const char* root, - const struct menu* menu, - const struct mentry* selected_entry) -{ - struct index index = INDEX_NULL; - assert(root && menu && selected_entry); - - printf("<div id=\"menu\">\n"); - - for(int i=0; i < menu->nitems; ++i) { - const struct mentry* entry = menu->items + i; - - if(i) printf(" &emsp13;|&emsp13;\n"); - - if(selected_entry == entry) { - printf(" %s\n", entry->label); - - } else if(!index_load(&index, entry->section)) { - printf(" <a href=\"%s%s/%s\">%s</a>\n", - root, entry->section, index.items[0].uri, entry->label); - index_release(&index); - - } else if(!strncmp(entry->section, "http://", 7) - || !strncmp(entry->section, "https://",8)) { - /* The target is an URL */ - printf(" <a href=\"%s\">%s</a>\n", entry->section, entry->label); - - } else { - /* The target is neither a local directory nor a URL. Let's assume it is - * a remote directory, still relative to the current root once the local - * content is installed, but managed elsewhere. */ - printf(" <a href=\"%s%s\">%s</a>\n", root, entry->section, entry->label); - } - } - - printf("</div>\n<hr>\n"); -} - -/* Return null if an error occurs */ -static char* -expand_lang(char* template, const char* value) -{ - char* buf = NULL; - char* p0 = NULL; - char* p1 = NULL; - size_t valen = 0; - size_t sz = 0; - - assert(template && value); - - valen = strlen(value); - - /* Calculate the size of the expanded string */ - for(p0=template; (p1 = strstr(p0, LANG)); p0=p1+SZLANG) { - *p1 = '\0'; - sz += strlen(p0) + valen; - *p1 = LANG[0]; - } - sz += strlen(p0)/* Rest of the template string */ + 1/* '\0' */; - - /* Allocate the expanded string */ - if(!(buf=malloc(sz))) goto error; - - /* Expand the string */ - buf[0] = '\0'; - for(p0=template; (p1 = strstr(p0, LANG)); p0=p1+SZLANG) { - *p1 = '\0'; - strcat(buf, p0); - strcat(buf, value); - *p1 = LANG[0]; - } - strcat(buf, p0); /* Rest of template string */ - -exit: - return buf; -error: - perror(__func__); - if(buf) { free(buf); buf = NULL; } - goto exit; -} - -static int -print_translations - (const char* root, - const char* section, - const struct ientry* entry) -{ - char* l = NULL; - char* lang_list = NULL; - char* ctx = NULL; - int i = 0; - int err = 0; - assert(entry); - - /* The URI and template are identical, meaning that no translation is - * available for this HTML content since its template has no @LANG@ macro */ - if(!strcmp(entry->uri, entry->template)) goto exit; - - /* Duplicate the lang list */ - if(!(lang_list=strdup(entry->lang_list))) { perror("malloc"); goto error; } - - /* Retrieve the 2 first langs in the list */ - l = strtok_r(lang_list, ":", &ctx); - l = strtok_r(NULL, ":", &ctx); - - if(!l) goto exit; /* There is only one lang, i.e. there is no translation */ - - - /* Duplicate the lang list again since it was updated by tokenization */ - free(lang_list); - if(!(lang_list=strdup(entry->lang_list))) { perror("malloc"); goto error; } - - printf(" <span style=\"float: right;\">\n"); - - for(i=0, l=strtok_r(lang_list, ":", &ctx); l; l=strtok_r(NULL, ":", &ctx)) { - - if(i) printf(" &emsp13;/&emsp13;\n"); - i += (i==0); - - if(!strcmp(l, entry->lang)) { - /* This is the current translation */ - printf(" <span class=\"cur\">%s</span>\n", l); - - } else { - char* translation = NULL; - - if(!(translation = expand_lang(entry->template, l))) goto error; - printf(" <a href=\"%s%s/%s\">%s</a>\n", root, section, translation, l); - free(translation); - } - } - - printf(" </span>\n"); - -exit: - if(lang_list) free(lang_list); - return err; -error: - err = 1; - goto exit; -} - -static int -print_menu2 - (const char* root, - const char* section, - const struct index* index, - const struct ientry* selected_entry) /* Can be NULL */ -{ - char* uri = NULL; - int err = 0; - - assert(root && section && index); - - printf("<div id=\"sub-menu\">\n"); - - for(int i = 0, n = index->nitems; i < n; ++i) { - const struct ientry* entry = index->items + i; - const char* lang = selected_entry ? selected_entry->lang : entry->lang; - - if(i) printf(" &emsp13;.&emsp13;\n"); - - /* * Resolve the URI language with the selected entry if it exists, or the - * current entry if not */ - if(!(uri=expand_lang(entry->template, lang))) goto error; - - /* This is the indexed content */ - if(selected_entry && !strcmp(uri, selected_entry->uri)) { - printf(" <span class=\"cur\">%s</span>\n", selected_entry->label); - print_translations(root, section, selected_entry); - - /* The entry links to an http[s] URL */ - } else if(!strncmp(entry->uri, "http://", 7) - || !strncmp(entry->uri, "https://",8)) { - printf(" <a href=\"%s\">%s</a>\n", entry->uri, entry->label); - - /* The entry links to a local web page for the section */ - } else { - printf(" <a href=\"%s%s/%s\">%s</a>\n", - root, section, entry->uri, entry->label); - } - - /* Remove elements with the same template for the current label to avoid - * duplicates when the indexed content offers multiple translations. Note - * that the following loop ensures that the index increment by the main - * loop remains valid, i.e., does not skip an entry. */ - while(i+1 < n - && !strcmp(entry->template, index->items[i+1].template) - && !strcmp(entry->label, index->items[i+1].label)) { - i += 1; - } - - free(uri); - uri = NULL; - } - - printf("</div>\n"); - -exit: - if(uri) free(uri); - return err; -error: - err = 1; - goto exit; -} - -/******************************************************************************* - * The command - ******************************************************************************/ -int -main(int argc, char** argv) -{ - struct menu menu = MENU_NULL; - const struct mentry* mentry = NULL; - - struct index index = INDEX_NULL; - const struct ientry* ientry = NULL; - - char* path = NULL; /* absolute path */ - char* dname = NULL; /* dirname of the absolute path */ - char* bname = NULL; /* basename of the absolute path */ - char* workdir = NULL; /* path from where the command is run */ - char* root = NULL; /* relative path from path to workdir */ - char* section = NULL; /* section name */ - char* file = NULL; /* path from the section to the file */ - - int res = EXIT_SUCCESS; - - if(argc < 2) { - fprintf(stderr, "usage: %s path\n", argv[0]); - goto error; - } - - g_cmd = argv[0]; - - /* Retrieve the absolute path to the input file */ - if(!(path = get_realpath(argv[1]))) goto error; - - /* Extract the directory and file from the input path. Start with the file, as - * the dirname function could add a '\0' character to the path to terminate - * the directory name instead of copying it to local storage. */ - bname = strdup(basename(path)); - dname = strdup(dirname(path)); - - /* Still retrieve the absolute path to the input file since it was overwritten - * by the basename/dirname calls */ - free(path); - if(!(path = get_realpath(argv[1]))) goto error; - - /* Get the absolute path of the working directory */ - if(!(workdir = get_workdir())) goto error; - - /* Get the section to which the file belongs, i.e. the first subdirectory of - * its path relative to the working directory */ - if(!(section = get_section(workdir, dname))) goto error; - - /* Get the path to the file relatively to the section */ - if(!(file = get_section_path(workdir, section, path))) goto error; - - /* Get the relative path from file to working directory. It is used to - * reference the CSS file relatively to the HTML content */ - if(!(root = get_relpath(workdir, path))) goto error; - - /* Load the menu file of the working directory */ - if(menu_load(&menu, workdir)) goto error; - - /* Load the index file of the section */ - if(index_load(&index, section)) goto error; - - /* Find the menu entry corresponding to the section of the input file */ - if(!(mentry = menu_find(&menu, section))) goto error; - - /* Find the index entry corresponding to the input file. It may be NULL if the - * content is not indexed by the section. */ - ientry = index_find(&index, file); - - /* Print the HTML header for the input content */ - print_head(root, mentry->label, ientry ? ientry->lang : DEFAULT_LANG); - print_menu1(root, &menu, mentry); - print_menu2(root, section, &index, ientry); - - printf("<div id=\"content\">\n"); - -exit: - menu_release(&menu); - index_release(&index); - - if(path) free(path); - if(dname) free(dname); - if(bname) free(bname); - if(workdir) free(workdir); - if(root) free(root); - if(section) free(section); - if(file) free(file); - return res; -error: - res = EXIT_FAILURE; - goto exit; -}