i#2260 web: Produce doyxgen suitable for web embedding (#2397)

Copies DynamoRIO's method of producing doxygen suitable for
embedding in Github Pages jekyll.
Since we need custom menu tweaking, we now use our own
copy of CMake_rundoxygen rather than sharing DR's as-is.

Issue: #2260
diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt
index d2e7cc3..bd29d26 100644
--- a/docs/CMakeLists.txt
+++ b/docs/CMakeLists.txt
@@ -77,97 +77,117 @@
   endif (APPLE)
 endif (WIN32)
 
-set(options_for_docs "${PROJECT_BINARY_DIR}/docs/options-docs.dox.in")
-
-add_custom_command(
-  OUTPUT "${options_for_docs}"
-  DEPENDS "${PROJECT_SOURCE_DIR}/common/options-docs.c"
-  "${PROJECT_SOURCE_DIR}/${tooldir}/optionsx.h"
-  COMMAND ${CMAKE_CPP_FOR_OPS}
-  ARGS -E ${CPP_NO_LINENUM} "${PROJECT_SOURCE_DIR}/common/options-docs.c"
-  ${CPP_INC}${PROJECT_SOURCE_DIR}/${tooldir} ${DEFINES} > "${options_for_docs}"
-  VERBATIM)
-
 include(${DynamoRIO_DIR}/docs_doxyutils.cmake)
 check_doxygen_version(${DOXYGEN_EXECUTABLE} "${DOXYGEN_VERSION}" DOXYGEN_VERSION)
 
-set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
-add_custom_command(
-  OUTPUT ${doxyfile}
-  DEPENDS ${dox_files}
-          ${headers}
-          ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
-          ${CMAKE_CURRENT_SOURCE_DIR}/CMake_doxyfile.cmake
-          ${PROJECT_SOURCE_DIR}/${tooldir}/docs/options-base.dox.in
-          ${options_for_docs}
-  COMMAND ${CMAKE_COMMAND}
-  # script does not inherit any vars so we must pass them all in
-  # Be sure to put a space after -D for 1st arg at least.
-  # When adding new DRMF extension docs, also add to Doxyfile.in and to
-  # DR's api/docs/ext.gendox.
-  ARGS -D srcdir=${PROJECT_SOURCE_DIR}/${tooldir}/docs
-       -D
-       srclist=${PROJECT_SOURCE_DIR}/framework/drmf.dox\;${PROJECT_SOURCE_DIR}/drsyscall/drsyscall.dox\;${PROJECT_SOURCE_DIR}/drsymcache/drsymcache.dox\;${PROJECT_SOURCE_DIR}/umbra/umbra.dox\;${PROJECT_SOURCE_DIR}/drfuzz/drfuzz.dox
-       -Dcommondir=${CMAKE_CURRENT_SOURCE_DIR}
-       -Doutfile=${doxyfile}
-       -Dversion_number=${TOOL_VERSION_NUMBER}
-       -DDOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE}
-       -DVMKERNEL=${VMKERNEL}
-       -DPERL_TO_EXE=${PERL_TO_EXE}
-       -Doptions_for_docs=${options_for_docs}
-       -Dtoolname=${toolname}
-       -Dtoolname_cap_spc=${toolname_cap_spc}
-       -Ddoxygen_ver=${DOXYGEN_VERSION}
-       -DDynamoRIO_DIR=${DynamoRIO_DIR}
-       -DTOOL_DR_MEMORY=${TOOL_DR_MEMORY}
-       -DPACKAGED_WITH_DYNAMORIO=${BUILDING_SUB_PACKAGE}
-       -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake_doxyfile.cmake
-  VERBATIM # recommended: p260
-  )
+macro (generate_html name dest_dir embeddable)
+  set(options_for_docs "${dest_dir}/options-docs.dox.in")
 
-set(docsgen ${CMAKE_CURRENT_BINARY_DIR}/html/index.html)
-add_custom_command(
-  OUTPUT ${docsgen} # among other files
-  DEPENDS ${dox_files} ${dox_files_gen} ${sub_files} ${in_files} ${imgs}
-          ${footer}
-          ${doxyfile}
-          ${DynamoRIO_DIR}/docs_rundoxygen.cmake
-  # We want doxygen to fail if it has warnings like "can't find header
-  # files" so we run it through a script
-  COMMAND ${CMAKE_COMMAND}
-  # Be sure to put a space after -D for 1st arg at least
-  ARGS -D DOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE}
-       -Ddoxygen_ver=${DOXYGEN_VERSION}
-       -Dversion_number=${TOOL_VERSION_NUMBER}
-       -Dmodule_string_long="DRMF Modules"
-       -Dmodule_string_short="DRMF Module"
-       -Dfiles_string="DRMF Files"
-       -Dstructs_string="DRMF Data Structures"
-       -Dhome_url="http://www.drmemory.org"
-       -Dhome_title="Dr. Memory Home Page"
-       # XXX: once we have a logo (in progress), put it here
-       -Dlogo_imgfile="favicon.png"
-       -P ${DynamoRIO_DIR}/docs_rundoxygen.cmake
-  VERBATIM # recommended: p260
-  )
-set_directory_properties(PROPERTIES
-  ADDITIONAL_MAKE_CLEAN_FILES "html;latex;rtf")
+  add_custom_command(
+    OUTPUT "${options_for_docs}"
+    DEPENDS "${PROJECT_SOURCE_DIR}/common/options-docs.c"
+    "${PROJECT_SOURCE_DIR}/${tooldir}/optionsx.h"
+    COMMAND ${CMAKE_CPP_FOR_OPS}
+    ARGS -E ${CPP_NO_LINENUM} "${PROJECT_SOURCE_DIR}/common/options-docs.c"
+    ${CPP_INC}${PROJECT_SOURCE_DIR}/${tooldir} ${DEFINES} > "${options_for_docs}"
+    VERBATIM)
 
-add_custom_target(htmldocs ALL
-  DEPENDS ${doxyfile}
-          ${footer}
-          ${docsgen}
-  )
+  set(doxyfile ${dest_dir}/Doxyfile)
+  add_custom_command(
+    OUTPUT ${doxyfile}
+    DEPENDS ${dox_files}
+            ${headers}
+            ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
+            ${CMAKE_CURRENT_SOURCE_DIR}/CMake_doxyfile.cmake
+            ${PROJECT_SOURCE_DIR}/${tooldir}/docs/options-base.dox.in
+            ${options_for_docs}
+    COMMAND ${CMAKE_COMMAND}
+    # script does not inherit any vars so we must pass them all in
+    # Be sure to put a space after -D for 1st arg at least.
+    # When adding new DRMF extension docs, also add to Doxyfile.in and to
+    # DR's api/docs/ext.gendox.
+    ARGS -D srcdir=${PROJECT_SOURCE_DIR}/${tooldir}/docs
+         -D
+         srclist=${PROJECT_SOURCE_DIR}/framework/drmf.dox\;${PROJECT_SOURCE_DIR}/drsyscall/drsyscall.dox\;${PROJECT_SOURCE_DIR}/drsymcache/drsymcache.dox\;${PROJECT_SOURCE_DIR}/umbra/umbra.dox\;${PROJECT_SOURCE_DIR}/drfuzz/drfuzz.dox
+         -Dcommondir=${CMAKE_CURRENT_SOURCE_DIR}
+         -Ddest_dir=${dest_dir}
+         -Doutfile=${doxyfile}
+         -Dembeddable=${embeddable}
+         -Dversion_number=${TOOL_VERSION_NUMBER}
+         -DDOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE}
+         -DVMKERNEL=${VMKERNEL}
+         -DPERL_TO_EXE=${PERL_TO_EXE}
+         -Doptions_for_docs=${options_for_docs}
+         -Dtoolname=${toolname}
+         -Dtoolname_cap_spc=${toolname_cap_spc}
+         -Ddoxygen_ver=${DOXYGEN_VERSION}
+         -DDynamoRIO_DIR=${DynamoRIO_DIR}
+         -DTOOL_DR_MEMORY=${TOOL_DR_MEMORY}
+         -DPACKAGED_WITH_DYNAMORIO=${BUILDING_SUB_PACKAGE}
+         -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake_doxyfile.cmake
+    WORKING_DIRECTORY ${dest_dir}
+    VERBATIM # recommended: p260
+    )
 
-# XXX: once we have a logo (in progress), put it here
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/favicon.png
-  ${CMAKE_CURRENT_BINARY_DIR}/html/favicon.png
-  COPYONLY)
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/favicon.ico
-  ${CMAKE_CURRENT_BINARY_DIR}/html/favicon.ico
-  COPYONLY)
+  set(docsgen ${dest_dir}/html/index.html)
+  add_custom_command(
+    OUTPUT ${docsgen} # among other files
+    DEPENDS ${dox_files} ${dox_files_gen} ${sub_files} ${in_files} ${imgs}
+            ${footer}
+            ${doxyfile}
+            ${CMAKE_CURRENT_SOURCE_DIR}/CMake_rundoxygen.cmake
+    # We want doxygen to fail if it has warnings like "can't find header
+    # files" so we run it through a script
+    COMMAND ${CMAKE_COMMAND}
+    # Be sure to put a space after -D for 1st arg at least
+    ARGS -D DOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE}
+         -Ddoxygen_ver=${DOXYGEN_VERSION}
+         -Dversion_number=${TOOL_VERSION_NUMBER}
+         -Dmodule_string_long="DRMF Modules"
+         -Dmodule_string_short="DRMF Module"
+         -Dfiles_string="DRMF Files"
+         -Dstructs_string="DRMF Data Structures"
+         -Dhome_url="http://www.drmemory.org"
+         -Dhome_title="Dr. Memory Home Page"
+         -Dlogo_imgfile="favicon.png"
+         -Dembeddable=${embeddable}
+         -Dproj_srcdir=${PROJECT_SOURCE_DIR}
+         -Dproj_bindir=${PROJECT_BINARY_DIR}
+         # XXX: We used to share DR's code at ${DynamoRIO_DIR}/docs_rundoxygen.cmake
+         # but for embedded docs we have custom steps.
+         # We could try to share the rest.
+         -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake_rundoxygen.cmake
+    WORKING_DIRECTORY ${dest_dir}
+    VERBATIM # recommended: p260
+    )
+  set_directory_properties(PROPERTIES
+    ADDITIONAL_MAKE_CLEAN_FILES "html;latex;rtf")
 
-# FIXME: generate pdf: check for epstopdf and latex first
+  add_custom_target(${name} ALL
+    DEPENDS ${doxyfile}
+            ${dox_files}
+            ${footer}
+            ${docsgen}
+    )
+
+  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/favicon.png
+    ${dest_dir}/html/favicon.png
+    COPYONLY)
+  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/favicon.ico
+    ${dest_dir}/html/favicon.ico
+    COPYONLY)
+endmacro()
+
+generate_html(htmldocs ${CMAKE_CURRENT_BINARY_DIR} OFF)
+
+# We create two output versions of the docs:
+# 1) A standalone html version for local viewing.
+# 2) A stripped-down version suitable for embedding on our web site.
+set(embed_outdir ${CMAKE_CURRENT_BINARY_DIR}/embed)
+file(MAKE_DIRECTORY ${embed_outdir})
+generate_html(embed_docs ${embed_outdir} ON)
+# We must build the treeview version first, as we use its menu files.
+add_dependencies(embed_docs htmldocs)
 
 ###########################################################################
 # install rules
@@ -177,6 +197,11 @@
   DESTINATION "${INSTALL_PREFIX}${toolname}/docs"
   )
 
+install(DIRECTORY
+  ${CMAKE_CURRENT_BINARY_DIR}/embed/html
+  DESTINATION "${INSTALL_PREFIX}${toolname}/docs_embed"
+  )
+
 # drmemory/docs/html/index.html is hard to find, so add a more-visible
 # access point:
 install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/${INSTALL_PREFIX}docs\")")
diff --git a/docs/CMake_doxyfile.cmake b/docs/CMake_doxyfile.cmake
index 6aa032e..51b6aa9 100644
--- a/docs/CMake_doxyfile.cmake
+++ b/docs/CMake_doxyfile.cmake
@@ -52,8 +52,10 @@
 # inputs:
 # * srcdir
 # * srclist
+# * dest_dir
 # * commondir
 # * outfile
+# * embeddable
 # * version_number
 # * DOXYGEN_EXECUTABLE
 # * VMKERNEL
@@ -65,7 +67,7 @@
 # * TOOL_DR_MEMORY
 # * PACKAGED_WITH_DYNAMORIO
 
-set(outdir "${CMAKE_CURRENT_BINARY_DIR}")
+set(outdir "${dest_dir}")
 get_filename_component(optionsdir "${options_for_docs}" PATH)
 # store cmake paths prior to cygwin conversion
 set(srcdir_orig "${srcdir}")
@@ -197,10 +199,21 @@
     "\\1 PACKAGED_WITH_DYNAMORIO" string "${string}")
 endif()
 
+if (embeddable)
+  # Turn off the frames.
+  string(REGEX REPLACE "(GENERATE_TREEVIEW[ \t]*= )YES" "\\1 NO" string "${string}")
+  # Turn off the confusing search box.
+  string(REGEX REPLACE "(SEARCHENGINE[ \t]*= )YES" "\\1 NO" string "${string}")
+endif ()
+
 # XXX: share w/ list of source paths in CMakeLists.txt ${headers}
 set(headers "${commondir}/../drsyscall/drsyscall.h ${commondir}/../umbra/umbra.h ${commondir}/../drfuzz/drfuzz.h ${commondir}/../drfuzz/drfuzz_mutator.h")
 set(headers "${headers} ${commondir}/../drsymcache/drsymcache.h")
-set(headers "${headers} ${outdir}/../drmf/include/drmemory_framework.h")
+if (embeddable)
+  set(headers "${headers} ${outdir}/../../drmf/include/drmemory_framework.h")
+else ()
+  set(headers "${headers} ${outdir}/../drmf/include/drmemory_framework.h")
+endif ()
 if (TOOL_DR_MEMORY)
   string(REGEX REPLACE
     "using.dox" "using.dox errors.dox reports.dox light.dox fuzzer.dox coverage.dox"
diff --git a/docs/CMake_rundoxygen.cmake b/docs/CMake_rundoxygen.cmake
new file mode 100644
index 0000000..41f3700
--- /dev/null
+++ b/docs/CMake_rundoxygen.cmake
@@ -0,0 +1,358 @@
+# **********************************************************
+# Copyright (c) 2011-2021 Google, Inc.  All rights reserved.
+# Copyright (c) 2009-2010 VMware, Inc.  All rights reserved.
+# **********************************************************
+
+# Dr. Memory: the memory debugger
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation;
+# version 2.1 of the License, and no later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+###########################################################################
+# Usage:
+# + Add a custom command to invoke this script in the docs build dir
+# + Assumes a favicon.ico is present in the target build dir
+#
+# Args:
+# + DOXYGEN_EXECUTABLE  : path to doxygen
+# + doxygen_ver         : version of doxygen
+# + version_number      : version number to put in the docs
+# + module_string_long  : replacement text for Modules
+# + module_string_short : replacement text for Module
+# + files_string        : replacement text for Files; optional
+# + structs_string      : replacement text for Data Structures; optional
+# + home_url            : home page URL
+# + home_title          : home page title
+# + logo_imgfile        : image file with logo
+# + embeddable          : whether to edit content for jekyll embedding
+# + proj_srcdir         : project base source dir, for editing titles
+# + proj_bindir         : project base binary dir, for editing titles
+#
+###########################################################################
+
+# Any quotes carry over and it's simplest to assume not there.
+string(REPLACE "\"" "" module_string_long ${module_string_long})
+string(REPLACE "\"" "" module_string_short ${module_string_short})
+if (DEFINED files_string)
+  string(REPLACE "\"" "" files_string ${files_string})
+endif ()
+if (DEFINED structs_string)
+  string(REPLACE "\"" "" structs_string ${structs_string})
+endif ()
+string(REPLACE "\"" "" home_url ${home_url})
+string(REPLACE "\"" "" home_title ${home_title})
+string(REPLACE "\"" "" logo_imgfile ${logo_imgfile})
+
+# First, generate header.html and footer.html suitable for current
+# doxygen version and then tweak them.
+# Note that we must generate a css file and we must name it doxygen.css
+# or else we have to later copy it to html dir.
+execute_process(COMMAND
+  ${DOXYGEN_EXECUTABLE} -w html header.html footer.html doxygen.css
+  RESULT_VARIABLE doxygen_w_result
+  ERROR_VARIABLE doxygen_w_error
+  OUTPUT_QUIET
+  )
+if (doxygen_w_result OR doxygen_w_error)
+  message(FATAL_ERROR "*** ${DOXYGEN_EXECUTABLE} failed: ***\n${doxygen_w_error}")
+endif (doxygen_w_result OR doxygen_w_error)
+
+# Add favicon to header
+file(READ ${CMAKE_CURRENT_BINARY_DIR}/header.html string)
+string(REGEX REPLACE
+  "<title>"
+  "<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"favicon.ico\"/>\n<title>"
+  string "${string}")
+if (doxygen_ver VERSION_LESS "1.7.0")
+  # For some reason older doxygen doesn't replace its own vars w/ custom header
+  string(REGEX REPLACE "\\$relpath\\$" "" string "${string}")
+endif ()
+if (embeddable)
+  # Add jekyll header.
+  set(string "---
+title: \"$title\"
+layout: default
+---
+${string}")
+endif ()
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/header.html "${string}")
+
+# Tweak footer to have version
+file(READ ${CMAKE_CURRENT_BINARY_DIR}/footer.html string)
+set(foot "<img border=0 src=\"favicon.png\"> &nbsp; ")
+set(foot "${foot} $projectname version ${version_number} --- $datetime")
+set(foot "${foot} &nbsp; <img border=0 src=\"favicon.png\">")
+# Recent doxygen
+string(REGEX REPLACE "\\$generated.*doxygenversion" "${foot}" string "${string}")
+# Older doxygen
+string(REGEX REPLACE "Generated.*doxygenversion" "${foot}" string "${string}")
+# Prefer center to default float:right
+string(REGEX REPLACE "class=\"footer\""
+  "class=\"footer\" style=\"float:none;text-align:center\""
+  string "${string}")
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/footer.html "${string}")
+
+if (doxygen_ver STRGREATER "1.7.2")
+  # Create a layout file so we can control the treeview index
+  execute_process(COMMAND
+    ${DOXYGEN_EXECUTABLE} -l
+    RESULT_VARIABLE doxygen_l_result
+    ERROR_VARIABLE doxygen_l_error
+    OUTPUT_QUIET
+    )
+  if (doxygen_l_result OR doxygen_l_error)
+    message(FATAL_ERROR "*** ${DOXYGEN_EXECUTABLE} failed: ***\n${doxygen_l_error}")
+  endif (doxygen_l_result OR doxygen_l_error)
+
+  file(READ ${CMAKE_CURRENT_BINARY_DIR}/DoxygenLayout.xml string)
+  # Rename "Modules" (done below for older doxygen)
+  string(REGEX REPLACE
+    "tab type=\"modules\" visible=\"yes\" title=\"\""
+    "tab type=\"modules\" visible=\"yes\" title=\"${module_string_long}\""
+    string "${string}")
+  if (doxygen_ver STRGREATER "1.7.5")
+    # Add link to home page (done below for older doxygen; skipped for 1.7.3-1.7.5)
+    string(REGEX REPLACE
+      "</navindex>"
+      "<tab type=\"user\" url=\"${home_url}\" title=\"${home_title}\"/>\n</navindex>"
+      string "${string}")
+  endif ()
+  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/DoxygenLayout.xml "${string}")
+endif ()
+
+# Run doxygen and fail on warnings.
+# Now update to latest doxygen.  Suppress warnings since they're misleading:
+# they say "please run doxygen -u" but we're currently doing just that.
+execute_process(COMMAND
+  ${DOXYGEN_EXECUTABLE}
+  RESULT_VARIABLE doxygen_u_result
+  ERROR_VARIABLE doxygen_u_error
+  OUTPUT_QUIET
+  )
+if (doxygen_u_result OR doxygen_u_error)
+  message(FATAL_ERROR "*** ${DOXYGEN_EXECUTABLE} failed: ***\n${doxygen_u_error}")
+endif (doxygen_u_result OR doxygen_u_error)
+
+# Doxygen does not put header.html on the top-level frame so we add favicon manually
+file(READ ${CMAKE_CURRENT_BINARY_DIR}/html/index.html string)
+string(REGEX REPLACE
+  "<title>"
+  "<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"favicon.ico\"/>\n<title>"
+  string "${string}")
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/index.html "${string}")
+
+if (embeddable)
+  # We need to make some edits for insertion into our jekyll site.
+  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/keywords.js "---\nlayout: null\n---\n")
+  file(GLOB all_html ${CMAKE_CURRENT_BINARY_DIR}/html/*.html)
+  foreach (html ${all_html})
+    file(READ ${html} string)
+    # Remove headers and body tag.
+    string(REGEX REPLACE "<!-- HTML header.*<body>\n" "" string "${string}")
+    # Remove body and html close tags.
+    string(REGEX REPLACE "</body>.*</html>\n" "" string "${string}")
+    # Remove full paths.
+    string(REGEX REPLACE "\ntitle: \"${proj_bindir}" "\ntitle: \"" string "${string}")
+    string(REGEX REPLACE "\ntitle: \"${proj_srcdir}" "\ntitle: \"" string "${string}")
+    # Add permalink to front matter so the web site hides the docs/ subdir.
+    get_filename_component(linkname ${html} NAME)
+    string(REGEX REPLACE "(\nlayout: default\n)" "\\1permalink: /${linkname}\n"
+      string "${string}")
+    # Our images are in an images/ subdir.
+    string(REGEX REPLACE "<img src=\"" "<img src=\"/images/" string "${string}")
+
+    # Collect type info for keyword searches with direct anchor links (else the
+    # search finds the page but the user then has to manually search within the long
+    # page -- and there are no doygen options to split each onto its own page).
+    # The format here is the javascript object used by search.js to set up lunr.
+    # XXX: This is fragile and highly dependent on the precise doxygen output.
+    get_filename_component(fname ${html} NAME_WE)
+    # The ;'s mess up the MATCHALL results so we remove them.
+    string(REPLACE ";" "_" nosemis "${string}")
+    string(REGEX MATCHALL "<tr class=\"memitem[^\n]*</tr>" types "${nosemis}")
+    foreach (type ${types})
+      string(REGEX MATCH "href=\"[^\"]+\"" url "${type}")
+      string(REGEX REPLACE "href=\"([^\"]+)\"" "\\1" url "${url}")
+      string(REGEX MATCH "\">[_a-zA-Z0-9]+</a>" name "${type}")
+      string(REGEX REPLACE "\">([_a-zA-Z0-9]+)</a>" "\\1" name "${name}")
+      if (name STREQUAL "")
+        continue ()
+      endif ()
+      # Support searching by partial names.
+      set(extra "")
+      string(REPLACE "_" " " separate "${name}")
+      if (NOT separate STREQUAL name)
+        set(extra "${extra} ${separate}")
+      endif ()
+      set(prefix "${name}")
+      while (prefix MATCHES "[^_]_[^_]")
+        string(REGEX REPLACE "_[^_]*$" "" prefix "${prefix}")
+        set(extra "${extra} ${prefix}")
+      endwhile ()
+      set(keywords "window.data[\"${fname}-${name}\"]={\
+\"name\":\"${fname}-${name}\",\
+\"title\":\"${name} in ${fname} header\",\
+\"url\":\"${url}\",\
+\"content\":\"${name} ${extra}\"};\n")
+      file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/html/keywords.js "${keywords}")
+    endforeach ()
+    # Similarly, put in explicit search entries for sections so searches go
+    # straight to the anchor link.
+    string(REGEX MATCHALL "\n<h1><a class=\"anchor\" id=\"[^\"]*\"></a>\n[^\n]+</h1>"
+      sections "${nosemis}")
+    foreach (section ${sections})
+      string(REGEX REPLACE ".* id=\"([^\"]+)\".*" "\\1" id "${section}")
+      string(REGEX REPLACE ".*</a>\n([^<]+)</h1>" "\\1" name "${section}")
+      if (name STREQUAL "" OR id STREQUAL "" OR name MATCHES "<")
+        message(FATAL_ERROR "Failed to find section name or anchor: ${section}")
+      endif ()
+      set(url "/${fname}.html#${id}")
+      # Quotes cause JavaScript syntax errors.
+      string(REPLACE "\"" "" name "${name}")
+      # Spaces would be ok but it feels nicer w/o them in the key.
+      string(REPLACE " " "_" key "${name}")
+      set(keywords "window.data[\"${fname}-${key}\"]={\
+\"name\":\"${fname}-${key}\",\
+\"title\":\"${name}\",\
+\"url\":\"${url}\",\
+\"content\":\"${name}\"};\n")
+      file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/html/keywords.js "${keywords}")
+    endforeach ()
+
+    file(WRITE ${html} "${string}")
+  endforeach ()
+
+  # We leverage the javascript menu from the non-embedded version.
+  file(GLOB all_js ${CMAKE_CURRENT_BINARY_DIR}/../html/*.js)
+  set(found_user_docs OFF)
+  foreach (js ${all_js})
+    if (js MATCHES "navtree.js" OR
+        js MATCHES "resize.js" OR
+        js MATCHES "navtreeindex")
+      continue ()
+    endif ()
+    file(READ ${js} string)
+    # Ask jekyll to inline, instead of just naming for JS.
+    string(REGEX REPLACE ", \"([^\"]+)\" \\]" ", {% include_relative \\1.js %} ]"
+      string "${string}")
+    if (js MATCHES "navtreedata.js")
+      # Add a jekyll header.
+      set(string "---\npermalink: /navtreedata.js\n---\n${string}")
+      # Remove the index array from navtreedata.js.
+      string(REGEX REPLACE "var NAVTREEINDEX =[^;]*;" "" string "${string}")
+      # Remove one layer from navtreedata.js.
+      string(REGEX REPLACE "\n *\\[ \"Dr. Memory\", \"index.html\", \\[" ""
+        string "${string}")
+      # Remove the home page at the end.
+      string(REGEX REPLACE "\n[^\n]*Dr. Memory Home Page[^\n]*\n" "" string "${string}")
+      # Insert a layer in navtreedata.js, using the end of the top layer we removed.
+      string(REGEX REPLACE "\n( *\\[ \"Deprecated List)"
+        "\n[ \"API Reference\", \"files.html\", [\n\\1"
+        string "${string}")
+    else ()
+      # Remove name so we can inline.
+      string(REGEX REPLACE "var [^\n]* =\n" "" string "${string}")
+      # End in a comma for inlining.
+      string(REGEX REPLACE "\\];" "]," string "${string}")
+      # Update directory index names which are hashes that differ between the
+      # treeview and embed generations.
+      string(REGEX MATCHALL "\"[a-zA-Z_0-9]+\", \"dir_[a-f0-9]+\\.html\""
+        dir_hashes "${string}")
+      foreach (dir_hash ${dir_hashes})
+        string(REGEX REPLACE "\"([a-zA-Z_0-9]+)\".*" "\\1" key "${dir_hash}")
+        # We go search the dir_*.html embed files looking for key.
+        file(GLOB dir_html ${CMAKE_CURRENT_BINARY_DIR}/html/dir_*.html)
+        set(found_hash OFF)
+        foreach (html ${dir_html})
+          file(READ ${html} html_string)
+          if (html_string MATCHES "${key} Directory Reference")
+            get_filename_component(fname ${html} NAME)
+            string(REGEX REPLACE "\"${key}\", \"dir_[a-f0-9]+\\.html\""
+              "\"${key}\", \"${fname}\"" string "${string}")
+            set(found_hash ON)
+            break ()
+          endif ()
+        endforeach ()
+        if (NOT found_hash)
+          message(FATAL_ERROR "Cannot find corresonding page for ${key}")
+        endif ()
+      endforeach ()
+    endif ()
+    # Put the modified contents into our dir.
+    get_filename_component(fname ${js} NAME)
+    file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/${fname} "${string}")
+  endforeach ()
+
+else ()
+  # Edit navbar.
+  if (doxygen_ver VERSION_LESS "1.7.3")
+    # Add link to home page to treeview pane
+    file(APPEND
+      ${CMAKE_CURRENT_BINARY_DIR}/html/tree.html
+      "<p>&nbsp;<a target=\"main\" href=\"${home_url}/\">${home_title}</a></p>")
+
+    # Add our logo to treeview pane (yes there is no closing </body></html>)
+    file(APPEND
+      ${CMAKE_CURRENT_BINARY_DIR}/html/tree.html
+      "<p>&nbsp;<br><img src=\"${logo_imgfile}\"></p>")
+  else ()
+    # This is a fragile insertion into the javascript used for the navbar.
+    # We insert at the end of the final function (initNavTree).
+    # This works in doxygen 1.7.4-1.8.1.
+    set(add_logo "
+          var imgdiv = document.createElement(\"div\");
+          document.getElementById(\"nav-tree-contents\").appendChild(imgdiv);
+          var img = document.createElement(\"img\");
+          imgdiv.appendChild(img);
+          img.src = \"${logo_imgfile}\";
+          img.style.marginLeft = \"80px\";
+          img.style.marginTop = \"30px\";
+      ")
+    file(READ ${CMAKE_CURRENT_BINARY_DIR}/html/navtree.js string)
+    string(REGEX REPLACE
+      "\\}\n*$"
+      "${add_logo}\n}"
+      string "${string}")
+    # Doxygen 1.9.1 has this license-end line.
+    string(REGEX REPLACE
+      "\\}\n/\\* @license-end \\*/\n$"
+      "${add_logo}\n}"
+      string "${string}")
+    # While we're here we replace Files and Data Structures, if requested
+    if (DEFINED files_string)
+      string(REGEX REPLACE "\"Files\"" "\"${files_string}\"" string "${string}")
+    endif ()
+    if (DEFINED structs_string)
+      string(REGEX REPLACE "\"Data Structures\"" "\"${structs_string}\""
+        string "${string}")
+    endif ()
+    file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/navtree.js "${string}")
+  endif ()
+endif ()
+
+# For modules/extensions (i#277/PR 540817) we use doxygen groups which show up under
+# top-level "Modules" which we rename here
+if (doxygen_ver VERSION_LESS "1.7.3")
+  file(READ ${CMAKE_CURRENT_BINARY_DIR}/html/tree.html string)
+  string(REGEX REPLACE "Modules" "${module_string_long}" string "${string}")
+  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/tree.html "${string}")
+endif ()
+if (EXISTS ${CMAKE_CURRENT_BINARY_DIR}/html/modules.html)
+  file(READ ${CMAKE_CURRENT_BINARY_DIR}/html/modules.html string)
+  string(REGEX REPLACE "Module" "${module_string_short}" string "${string}")
+  string(REGEX REPLACE "module" "${module_string_short}" string "${string}")
+  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/html/modules.html "${string}")
+endif ()
+
+# We do not copy samples or favicon for build dir: those are install rules
diff --git a/docs/favicon.ico b/docs/favicon.ico
index f2ffe34..5ea1057 100644
--- a/docs/favicon.ico
+++ b/docs/favicon.ico
Binary files differ
diff --git a/docs/favicon.png b/docs/favicon.png
index 5657537..6a774d1 100644
--- a/docs/favicon.png
+++ b/docs/favicon.png
Binary files differ