mirror of
https://github.com/mhx/dwarfs.git
synced 2025-09-09 04:19:10 -04:00
feat: add support for built-in manual pages
This commit is contained in:
parent
3c3de6ce9e
commit
6994f9691e
@ -54,6 +54,7 @@ RUN apt install -y \
|
|||||||
libgoogle-glog-dev \
|
libgoogle-glog-dev \
|
||||||
libutfcpp-dev \
|
libutfcpp-dev \
|
||||||
libflac++-dev \
|
libflac++-dev \
|
||||||
|
python3-mistletoe \
|
||||||
bash-completion
|
bash-completion
|
||||||
COPY install-static-libs.sh /usr/local/bin/install-static-libs.sh
|
COPY install-static-libs.sh /usr/local/bin/install-static-libs.sh
|
||||||
RUN bash /usr/local/bin/install-static-libs.sh
|
RUN bash /usr/local/bin/install-static-libs.sh
|
||||||
|
@ -80,7 +80,7 @@ if [[ "-$BUILD_TYPE-" == *-nojemalloc-* ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "-$BUILD_TYPE-" == *-noperfmon-* ]]; then
|
if [[ "-$BUILD_TYPE-" == *-noperfmon-* ]]; then
|
||||||
CMAKE_ARGS="${CMAKE_ARGS} -DENABLE_PERFMON=0"
|
CMAKE_ARGS="${CMAKE_ARGS} -DENABLE_PERFMON=0 -DWITH_MAN_OPTION=0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "-$BUILD_TYPE-" == *-static-* ]]; then
|
if [[ "-$BUILD_TYPE-" == *-static-* ]]; then
|
||||||
|
@ -30,6 +30,7 @@ include(CheckCXXSourceCompiles)
|
|||||||
option(WITH_TESTS "build with tests" OFF)
|
option(WITH_TESTS "build with tests" OFF)
|
||||||
option(WITH_BENCHMARKS "build with benchmarks" OFF)
|
option(WITH_BENCHMARKS "build with benchmarks" OFF)
|
||||||
option(WITH_FUZZ "build with fuzzing binaries" OFF)
|
option(WITH_FUZZ "build with fuzzing binaries" OFF)
|
||||||
|
option(WITH_MAN_OPTION "build with --man option" ON)
|
||||||
option(ENABLE_PERFMON "enable performance monitor in all tools" ON)
|
option(ENABLE_PERFMON "enable performance monitor in all tools" ON)
|
||||||
option(ENABLE_FLAC "build with FLAC support" ON)
|
option(ENABLE_FLAC "build with FLAC support" ON)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@ -116,6 +117,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
|||||||
# Apply /MT or /MTd (multithread, static version of the run-time library)
|
# Apply /MT or /MTd (multithread, static version of the run-time library)
|
||||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug>:Embedded>")
|
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug>:Embedded>")
|
||||||
|
add_compile_definitions(_WIN32_WINNT=0x0601 WINVER=0x0601)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||||
@ -381,9 +383,7 @@ if(NOT
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(
|
list(APPEND LIBDWARFS_SRC
|
||||||
APPEND
|
|
||||||
LIBDWARFS_SRC
|
|
||||||
src/dwarfs/block_cache.cpp
|
src/dwarfs/block_cache.cpp
|
||||||
src/dwarfs/block_compressor.cpp
|
src/dwarfs/block_compressor.cpp
|
||||||
src/dwarfs/block_compressor_parser.cpp
|
src/dwarfs/block_compressor_parser.cpp
|
||||||
@ -442,7 +442,24 @@ list(
|
|||||||
src/dwarfs/terminal.cpp
|
src/dwarfs/terminal.cpp
|
||||||
src/dwarfs/util.cpp
|
src/dwarfs/util.cpp
|
||||||
src/dwarfs/wcwidth.c
|
src/dwarfs/wcwidth.c
|
||||||
src/dwarfs/worker_group.cpp)
|
src/dwarfs/worker_group.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WITH_MAN_OPTION)
|
||||||
|
include(${CMAKE_SOURCE_DIR}/cmake/render_manpage.cmake)
|
||||||
|
|
||||||
|
list(APPEND LIBDWARFS_SRC
|
||||||
|
src/dwarfs/pager.cpp
|
||||||
|
src/dwarfs/render_manpage.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(man mkdwarfs dwarfs dwarfsck dwarfsextract)
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/src/dwarfs")
|
||||||
|
add_manpage_source(doc/${man}.md NAME ${man}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/dwarfs/${man}_manpage.cpp)
|
||||||
|
list(APPEND LIBDWARFS_SRC ${CMAKE_CURRENT_BINARY_DIR}/src/dwarfs/${man}_manpage.cpp)
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Just an example for setting per file compile options
|
# Just an example for setting per file compile options
|
||||||
# set_source_files_properties(src/dwarfs/segmenter.cpp PROPERTIES COMPILE_FLAGS -march=tigerlake)
|
# set_source_files_properties(src/dwarfs/segmenter.cpp PROPERTIES COMPILE_FLAGS -march=tigerlake)
|
||||||
@ -640,6 +657,10 @@ if(WITH_TESTS)
|
|||||||
utils_test
|
utils_test
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(WITH_MAN_OPTION)
|
||||||
|
list(APPEND DWARFS_TESTS manpage_test)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(FLAC_FOUND)
|
if(FLAC_FOUND)
|
||||||
list(APPEND DWARFS_TESTS flac_compressor_test)
|
list(APPEND DWARFS_TESTS flac_compressor_test)
|
||||||
endif()
|
endif()
|
||||||
@ -777,6 +798,7 @@ foreach(tgt dwarfs dwarfs_compression dwarfs_categorizer
|
|||||||
${tgt}
|
${tgt}
|
||||||
PRIVATE DWARFS_HAVE_LIBZSTD
|
PRIVATE DWARFS_HAVE_LIBZSTD
|
||||||
DWARFS_STATIC_BUILD=${STATIC_BUILD_DO_NOT_USE}
|
DWARFS_STATIC_BUILD=${STATIC_BUILD_DO_NOT_USE}
|
||||||
|
$<$<BOOL:${WITH_MAN_OPTION}>:DWARFS_BUILTIN_MANPAGE>
|
||||||
$<$<BOOL:${USE_JEMALLOC}>:DWARFS_USE_JEMALLOC>
|
$<$<BOOL:${USE_JEMALLOC}>:DWARFS_USE_JEMALLOC>
|
||||||
$<$<BOOL:${LIBMAGIC_FOUND}>:DWARFS_HAVE_LIBMAGIC>
|
$<$<BOOL:${LIBMAGIC_FOUND}>:DWARFS_HAVE_LIBMAGIC>
|
||||||
$<$<BOOL:${LIBLZ4_FOUND}>:DWARFS_HAVE_LIBLZ4>
|
$<$<BOOL:${LIBLZ4_FOUND}>:DWARFS_HAVE_LIBLZ4>
|
||||||
|
41
cmake/render_manpage.cmake
Normal file
41
cmake/render_manpage.cmake
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) Marcus Holland-Moritz
|
||||||
|
#
|
||||||
|
# This file is part of dwarfs.
|
||||||
|
#
|
||||||
|
# dwarfs 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.
|
||||||
|
#
|
||||||
|
# dwarfs 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
|
||||||
|
# dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.25.0)
|
||||||
|
|
||||||
|
function(add_manpage_source markdown)
|
||||||
|
set(_options)
|
||||||
|
set(_oneValueArgs NAME OUTPUT)
|
||||||
|
set(_multiValueArgs)
|
||||||
|
cmake_parse_arguments(_MANPAGE "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
find_program(_PYTHON_EXE python python3)
|
||||||
|
if(NOT _PYTHON_EXE)
|
||||||
|
find_package(Python3 REQUIRED)
|
||||||
|
set(_PYTHON_EXE "${Python3_EXECUTABLE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(_MANPAGE_GENERATOR "${CMAKE_SOURCE_DIR}/cmake/render_manpage.py")
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${_MANPAGE_OUTPUT}"
|
||||||
|
COMMAND "${_PYTHON_EXE}" "${_MANPAGE_GENERATOR}"
|
||||||
|
"${_MANPAGE_NAME}" "${CMAKE_CURRENT_SOURCE_DIR}/${markdown}" "${_MANPAGE_OUTPUT}"
|
||||||
|
DEPENDS "${markdown}" "${_MANPAGE_GENERATOR}"
|
||||||
|
)
|
||||||
|
endfunction()
|
328
cmake/render_manpage.py
Normal file
328
cmake/render_manpage.py
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
import mistletoe
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class RenderContext:
|
||||||
|
def __init__(self):
|
||||||
|
self.line = 0
|
||||||
|
self.level = 0
|
||||||
|
self.indent = 4
|
||||||
|
self.section = None
|
||||||
|
|
||||||
|
def get_indent(self, add=0):
|
||||||
|
return self.indent * (max(0, self.level + add))
|
||||||
|
|
||||||
|
|
||||||
|
class Element:
|
||||||
|
def __init__(self, tags, content=None, comment=None):
|
||||||
|
self.tags = tags
|
||||||
|
self.content = content
|
||||||
|
self.comment = comment
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Element({self.tags}, {self.content}, {self.comment})"
|
||||||
|
|
||||||
|
def tags_to_style(self):
|
||||||
|
style = []
|
||||||
|
if "b" in self.tags:
|
||||||
|
style.append("fmt::emphasis::bold")
|
||||||
|
if "i" in self.tags:
|
||||||
|
style.append("fmt::emphasis::italic")
|
||||||
|
if "head" in self.tags:
|
||||||
|
style.append(
|
||||||
|
"fmt::fg(fmt::terminal_color::bright_green) | fmt::emphasis::bold"
|
||||||
|
)
|
||||||
|
if "code" in self.tags:
|
||||||
|
style.append(
|
||||||
|
"fmt::fg(fmt::terminal_color::bright_blue) | fmt::emphasis::bold"
|
||||||
|
)
|
||||||
|
if "block" in self.tags:
|
||||||
|
style.append("fmt::emphasis::faint")
|
||||||
|
if len(style) == 0:
|
||||||
|
return "{}"
|
||||||
|
return " | ".join(style)
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
return f'{{{self.tags_to_style()} /* {self.tags} */, R"({self.content})"}} /* {self.comment} */'
|
||||||
|
|
||||||
|
|
||||||
|
def apply(elements, *tags):
|
||||||
|
return [Element({*tags, *e.tags}, e.content, e.comment) for e in elements]
|
||||||
|
|
||||||
|
|
||||||
|
class Line:
|
||||||
|
def __init__(self, elements=None, indent_first=0, indent=None, comment=None):
|
||||||
|
self.elements = [] if elements is None else elements
|
||||||
|
self.indent_first = indent_first
|
||||||
|
self.indent = indent_first if indent is None else indent
|
||||||
|
self.comment = comment
|
||||||
|
|
||||||
|
def split(self):
|
||||||
|
rv = []
|
||||||
|
cur = []
|
||||||
|
indent_first = self.indent_first
|
||||||
|
for e in self.elements:
|
||||||
|
if e.comment == "line break":
|
||||||
|
rv.append(Line(cur, indent_first, self.indent, comment=self.comment))
|
||||||
|
indent_first = self.indent
|
||||||
|
cur = []
|
||||||
|
else:
|
||||||
|
cur.append(e)
|
||||||
|
rv.append(Line(cur, indent_first, self.indent, comment=self.comment))
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def join(self):
|
||||||
|
rv = []
|
||||||
|
for e in self.elements:
|
||||||
|
if e.comment == "line break":
|
||||||
|
rv.append(Element(set(), " ", "whitespace"))
|
||||||
|
else:
|
||||||
|
rv.append(e)
|
||||||
|
return Line(rv, self.indent_first, self.indent, comment=self.comment)
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
template = (
|
||||||
|
"constexpr uint32_t const line{0}_indent_first{{{2}}};\n"
|
||||||
|
"constexpr uint32_t const line{0}_indent_next{{{3}}};\n"
|
||||||
|
"constexpr std::array<element, {1}> const line{0}_elements{{{{\n"
|
||||||
|
"{4}"
|
||||||
|
"}}}};\n\n"
|
||||||
|
)
|
||||||
|
rv = ""
|
||||||
|
if self.comment is not None:
|
||||||
|
rv += "// " + self.comment + "\n"
|
||||||
|
rv += template.format(
|
||||||
|
context.line,
|
||||||
|
len(self.elements),
|
||||||
|
self.indent_first,
|
||||||
|
self.indent,
|
||||||
|
"".join([f" {e.render(context)},\n" for e in self.elements]),
|
||||||
|
)
|
||||||
|
context.line += 1
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
class Paragraph:
|
||||||
|
def __init__(self, elements, comment=None):
|
||||||
|
self.elements = elements
|
||||||
|
self.comment = comment
|
||||||
|
|
||||||
|
def render(self, context, indent_first=None, indent=None):
|
||||||
|
if indent_first is None:
|
||||||
|
indent_first = context.get_indent()
|
||||||
|
if indent is None:
|
||||||
|
indent = indent_first
|
||||||
|
line = Line(self.elements, indent_first, indent, comment=self.comment)
|
||||||
|
if context.section is not None and context.section == "SYNOPSIS":
|
||||||
|
lines = line.split()
|
||||||
|
else:
|
||||||
|
lines = [line.join()]
|
||||||
|
return "".join([l.render(context) for l in lines]) + Line().render(context)
|
||||||
|
|
||||||
|
|
||||||
|
class Heading:
|
||||||
|
def __init__(self, elements, level, section=None, comment=None):
|
||||||
|
self.elements = elements
|
||||||
|
self.level = level
|
||||||
|
self.section = section
|
||||||
|
self.comment = comment
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Heading({self.level}, {self.section}, {self.comment})"
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
context.level = 2
|
||||||
|
indent = context.get_indent(-2 if self.level <= 2 else -1)
|
||||||
|
context.section = self.section
|
||||||
|
rv = Line(apply(self.elements, "head"), indent, comment=self.comment).render(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
if self.level == 1:
|
||||||
|
rv += Line().render(context)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
class ListItem:
|
||||||
|
def __init__(self, paragraphs, comment=None):
|
||||||
|
self.paragraphs = paragraphs
|
||||||
|
self.comment = comment
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"ListItem({self.paragraphs}, {self.comment})"
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
rv = ""
|
||||||
|
for i, p in enumerate(self.paragraphs):
|
||||||
|
handled = False
|
||||||
|
if i == 0:
|
||||||
|
content_len = 0
|
||||||
|
for i in range(len(p.elements) - 1):
|
||||||
|
cur = p.elements[i]
|
||||||
|
nxt = p.elements[i + 1]
|
||||||
|
content_len += len(cur.content)
|
||||||
|
if cur.content.endswith(":") and nxt.comment == "line break":
|
||||||
|
cur.content = cur.content[:-1]
|
||||||
|
content_len -= 1
|
||||||
|
if content_len < (2 * context.indent - 1):
|
||||||
|
cur.content += " " * (2 * context.indent - content_len)
|
||||||
|
p.elements = p.elements[: i + 1] + p.elements[i + 2 :]
|
||||||
|
rv += p.render(
|
||||||
|
context,
|
||||||
|
indent_first=context.get_indent(),
|
||||||
|
indent=context.get_indent(2),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
rv += Line(
|
||||||
|
p.elements[:i + 1], context.get_indent(), comment=p.comment
|
||||||
|
).render(context)
|
||||||
|
p.elements = p.elements[i + 2 :]
|
||||||
|
rv += p.render(context, indent_first=context.get_indent(2))
|
||||||
|
handled = True
|
||||||
|
break
|
||||||
|
if not handled:
|
||||||
|
rv += p.render(context)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
class BlockCode:
|
||||||
|
def __init__(self, element):
|
||||||
|
self.element = element
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
lines = self.element.content.split("\n")
|
||||||
|
rv = ""
|
||||||
|
for line in lines:
|
||||||
|
rv += Line(
|
||||||
|
[Element({"block"}, line)],
|
||||||
|
context.get_indent(1),
|
||||||
|
comment=self.element.comment,
|
||||||
|
).render(context)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
class ManpageRenderer(mistletoe.base_renderer.BaseRenderer):
|
||||||
|
def __init__(self, document_name, *extras, **kwargs):
|
||||||
|
self.__document_name = document_name
|
||||||
|
super().__init__(*extras, **kwargs)
|
||||||
|
|
||||||
|
def render_inner(self, token, tags=None):
|
||||||
|
rv = []
|
||||||
|
for child in token.children:
|
||||||
|
c = self.render(child)
|
||||||
|
if isinstance(c, list):
|
||||||
|
rv.extend(c)
|
||||||
|
else:
|
||||||
|
rv.append(c)
|
||||||
|
if tags is not None:
|
||||||
|
for child in rv:
|
||||||
|
child.tags.update(tags)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def render_thematic_break(token):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def render_line_break(token):
|
||||||
|
return Element(set(), "", "line break")
|
||||||
|
|
||||||
|
def render_inline_code(self, token):
|
||||||
|
assert len(token.children) == 1
|
||||||
|
return Element({"code"}, token.children[0].content, "inline code")
|
||||||
|
|
||||||
|
def render_raw_text(self, token, escape=True):
|
||||||
|
return Element(set(), token.content, "raw text")
|
||||||
|
|
||||||
|
def render_strikethrough(self, token):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def render_escape_sequence(self, token):
|
||||||
|
return self.render_inner(token)
|
||||||
|
|
||||||
|
def render_strong(self, token):
|
||||||
|
return self.render_inner(token, {"b"})
|
||||||
|
|
||||||
|
def render_emphasis(self, token):
|
||||||
|
return self.render_inner(token, {"i"})
|
||||||
|
|
||||||
|
def render_image(self, token):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def render_heading(self, token):
|
||||||
|
inner = self.render_inner(token, {"h{}".format(token.level)})
|
||||||
|
section = None
|
||||||
|
if len(inner) == 1:
|
||||||
|
assert isinstance(inner[0], Element)
|
||||||
|
section = inner[0].content
|
||||||
|
return Heading(inner, token.level, section)
|
||||||
|
|
||||||
|
def render_paragraph(self, token):
|
||||||
|
inner = self.render_inner(token)
|
||||||
|
return Paragraph(inner, "paragraph")
|
||||||
|
|
||||||
|
def render_block_code(self, token):
|
||||||
|
inner = self.render_inner(token)
|
||||||
|
assert len(inner) == 1
|
||||||
|
assert isinstance(inner[0], Element)
|
||||||
|
return BlockCode(inner[0])
|
||||||
|
|
||||||
|
def render_list(self, token):
|
||||||
|
return [self.render(child) for child in token.children]
|
||||||
|
|
||||||
|
def render_list_item(self, token):
|
||||||
|
inner = self.render_inner(token)
|
||||||
|
assert isinstance(inner, list)
|
||||||
|
assert isinstance(inner[0], Paragraph)
|
||||||
|
return ListItem(inner, "list item")
|
||||||
|
|
||||||
|
def render_table(self, token):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def render_table_row(self, token):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def render_math(self, token):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def render_table_cell(self, token):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def render_document(self, token):
|
||||||
|
rv = """#include <array>
|
||||||
|
#include "dwarfs/manpage.h"
|
||||||
|
|
||||||
|
namespace dwarfs::manpage {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
"""
|
||||||
|
ctx = RenderContext()
|
||||||
|
for child in token.children:
|
||||||
|
r = self.render(child)
|
||||||
|
rv += f"#if 0\n{r}\n#endif\n"
|
||||||
|
if isinstance(r, list):
|
||||||
|
for e in r:
|
||||||
|
rv += e.render(ctx)
|
||||||
|
else:
|
||||||
|
rv += r.render(ctx)
|
||||||
|
rv += f"constexpr std::array<line, {ctx.line}> const document_array{{{{\n"
|
||||||
|
for i in range(ctx.line):
|
||||||
|
rv += f" {{line{i}_indent_first, line{i}_indent_next, line{i}_elements}},\n"
|
||||||
|
rv += f"""}}}};
|
||||||
|
}} // namespace
|
||||||
|
|
||||||
|
document get_{self.__document_name}_manpage() {{ return document_array; }}
|
||||||
|
|
||||||
|
}} // namespace dwarfs::manpage
|
||||||
|
"""
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
doc_name = sys.argv[1]
|
||||||
|
input_file = sys.argv[2]
|
||||||
|
output_file = sys.argv[3]
|
||||||
|
|
||||||
|
with open(input_file, "r") as fin:
|
||||||
|
with ManpageRenderer(doc_name) as renderer:
|
||||||
|
doc = renderer.render(mistletoe.Document(fin))
|
||||||
|
with open(output_file, "w") as fout:
|
||||||
|
fout.write(doc)
|
51
include/dwarfs/manpage.h
Normal file
51
include/dwarfs/manpage.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/**
|
||||||
|
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||||
|
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||||
|
*
|
||||||
|
* This file is part of dwarfs.
|
||||||
|
*
|
||||||
|
* dwarfs 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.
|
||||||
|
*
|
||||||
|
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <fmt/color.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
namespace dwarfs::manpage {
|
||||||
|
|
||||||
|
struct element {
|
||||||
|
fmt::text_style style;
|
||||||
|
std::string_view text;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct line {
|
||||||
|
uint32_t indent_first;
|
||||||
|
uint32_t indent_next;
|
||||||
|
std::span<element const> elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
using document = std::span<line const>;
|
||||||
|
|
||||||
|
document get_mkdwarfs_manpage();
|
||||||
|
document get_dwarfs_manpage();
|
||||||
|
document get_dwarfsck_manpage();
|
||||||
|
document get_dwarfsextract_manpage();
|
||||||
|
|
||||||
|
} // namespace dwarfs::manpage
|
30
include/dwarfs/pager.h
Normal file
30
include/dwarfs/pager.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/**
|
||||||
|
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||||
|
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||||
|
*
|
||||||
|
* This file is part of dwarfs.
|
||||||
|
*
|
||||||
|
* dwarfs 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.
|
||||||
|
*
|
||||||
|
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace dwarfs {
|
||||||
|
|
||||||
|
bool show_in_pager(std::string text);
|
||||||
|
|
||||||
|
} // namespace dwarfs
|
32
include/dwarfs/render_manpage.h
Normal file
32
include/dwarfs/render_manpage.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/**
|
||||||
|
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||||
|
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||||
|
*
|
||||||
|
* This file is part of dwarfs.
|
||||||
|
*
|
||||||
|
* dwarfs 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.
|
||||||
|
*
|
||||||
|
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "dwarfs/manpage.h"
|
||||||
|
|
||||||
|
namespace dwarfs {
|
||||||
|
|
||||||
|
std::string render_manpage(manpage::document doc, size_t width, bool color);
|
||||||
|
|
||||||
|
} // namespace dwarfs
|
@ -26,9 +26,14 @@
|
|||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
#include "dwarfs/manpage.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace dwarfs {
|
namespace dwarfs {
|
||||||
|
|
||||||
struct logger_options;
|
struct logger_options;
|
||||||
|
struct iolayer;
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
tool_header(std::string_view tool_name, std::string_view extra_info = "");
|
tool_header(std::string_view tool_name, std::string_view extra_info = "");
|
||||||
@ -36,4 +41,8 @@ tool_header(std::string_view tool_name, std::string_view extra_info = "");
|
|||||||
void add_common_options(boost::program_options::options_description& opts,
|
void add_common_options(boost::program_options::options_description& opts,
|
||||||
logger_options& logopts);
|
logger_options& logopts);
|
||||||
|
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
void show_manpage(manpage::document doc, iolayer const& iol);
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace dwarfs
|
} // namespace dwarfs
|
||||||
|
96
src/dwarfs/pager.cpp
Normal file
96
src/dwarfs/pager.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/**
|
||||||
|
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||||
|
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||||
|
*
|
||||||
|
* This file is part of dwarfs.
|
||||||
|
*
|
||||||
|
* dwarfs 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.
|
||||||
|
*
|
||||||
|
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
#include <boost/process.hpp>
|
||||||
|
|
||||||
|
#include "dwarfs/pager.h"
|
||||||
|
|
||||||
|
namespace dwarfs {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
namespace bp = boost::process;
|
||||||
|
|
||||||
|
struct pager_def {
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::string> args;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<pager_def> const pagers{
|
||||||
|
{"less", {"-R"}},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto find_executable(std::string name) { return bp::search_path(name); }
|
||||||
|
|
||||||
|
std::pair<boost::filesystem::path, std::vector<std::string>> find_pager() {
|
||||||
|
if (auto pager_env = std::getenv("PAGER")) {
|
||||||
|
std::string_view sv(pager_env);
|
||||||
|
if (sv.starts_with('"') && sv.ends_with('"')) {
|
||||||
|
sv.remove_prefix(1);
|
||||||
|
sv.remove_suffix(1);
|
||||||
|
}
|
||||||
|
if (sv == "cat") {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
boost::filesystem::path p{std::string(sv)};
|
||||||
|
if (boost::filesystem::exists(p)) {
|
||||||
|
return {p.string(), {}};
|
||||||
|
}
|
||||||
|
if (auto exe = find_executable(pager_env); !exe.empty()) {
|
||||||
|
return {exe, {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& p : pagers) {
|
||||||
|
if (auto exe = find_executable(std::string(p.name)); !exe.empty()) {
|
||||||
|
return {exe, p.args};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool show_in_pager(std::string text) {
|
||||||
|
auto [pager_exe, pager_args] = find_pager();
|
||||||
|
|
||||||
|
if (pager_exe.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::io_service ios;
|
||||||
|
bp::child proc(pager_exe, bp::args(pager_args),
|
||||||
|
bp::std_in =
|
||||||
|
boost::asio::const_buffer(text.data(), text.size()),
|
||||||
|
bp::std_out > stdout, ios);
|
||||||
|
ios.run();
|
||||||
|
proc.wait();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dwarfs
|
87
src/dwarfs/render_manpage.cpp
Normal file
87
src/dwarfs/render_manpage.cpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/**
|
||||||
|
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||||
|
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||||
|
*
|
||||||
|
* This file is part of dwarfs.
|
||||||
|
*
|
||||||
|
* dwarfs 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.
|
||||||
|
*
|
||||||
|
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "dwarfs/render_manpage.h"
|
||||||
|
|
||||||
|
namespace dwarfs {
|
||||||
|
|
||||||
|
std::string render_manpage(manpage::document const doc, size_t const width,
|
||||||
|
bool const color) {
|
||||||
|
static constexpr std::string_view punct = ".,:;!?";
|
||||||
|
static constexpr size_t right_margin = 4;
|
||||||
|
size_t const effective_width = width - right_margin;
|
||||||
|
std::string out;
|
||||||
|
auto out_it = std::back_inserter(out);
|
||||||
|
|
||||||
|
for (auto const& l : doc) {
|
||||||
|
uint32_t indent = l.indent_first;
|
||||||
|
uint32_t column = indent;
|
||||||
|
|
||||||
|
fmt::format_to(out_it, "{}", std::string(indent, ' '));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < l.elements.size(); ++i) {
|
||||||
|
auto e = l.elements[i];
|
||||||
|
auto* next = (i + 1 < l.elements.size()) ? &l.elements[i + 1] : nullptr;
|
||||||
|
auto t = e.text;
|
||||||
|
auto style = color ? e.style : fmt::text_style{};
|
||||||
|
|
||||||
|
while (column + t.size() > effective_width) {
|
||||||
|
auto wp = t.rfind(' ', effective_width - column);
|
||||||
|
|
||||||
|
if (wp == std::string_view::npos && column == indent) {
|
||||||
|
wp = effective_width - column;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wp != std::string_view::npos) {
|
||||||
|
fmt::format_to(out_it, style, "{}", t.substr(0, wp));
|
||||||
|
column += wp;
|
||||||
|
t = t.substr(wp + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
indent = l.indent_next;
|
||||||
|
fmt::format_to(out_it, "\n{}", std::string(indent, ' '));
|
||||||
|
column = indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column + t.size() > effective_width) {
|
||||||
|
throw std::logic_error("line too long");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column + t.size() == effective_width && next &&
|
||||||
|
next->text.size() == 1 &&
|
||||||
|
punct.find(next->text[0]) != std::string_view::npos) {
|
||||||
|
indent = l.indent_next;
|
||||||
|
fmt::format_to(out_it, "\n{}", std::string(indent, ' '));
|
||||||
|
column = indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::format_to(out_it, style, "{}", t);
|
||||||
|
column += t.size();
|
||||||
|
}
|
||||||
|
fmt::format_to(out_it, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dwarfs
|
@ -27,6 +27,13 @@
|
|||||||
#include "dwarfs/tool.h"
|
#include "dwarfs/tool.h"
|
||||||
#include "dwarfs/version.h"
|
#include "dwarfs/version.h"
|
||||||
|
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
#include "dwarfs/iolayer.h"
|
||||||
|
#include "dwarfs/pager.h"
|
||||||
|
#include "dwarfs/render_manpage.h"
|
||||||
|
#include "dwarfs/terminal.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace po = boost::program_options;
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
@ -71,10 +78,24 @@ void add_common_options(po::options_description& opts,
|
|||||||
("log-with-context",
|
("log-with-context",
|
||||||
po::value<std::optional<bool>>(&logopts.with_context)->zero_tokens(),
|
po::value<std::optional<bool>>(&logopts.with_context)->zero_tokens(),
|
||||||
"enable context logging regardless of level")
|
"enable context logging regardless of level")
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
("man",
|
||||||
|
"show manual page and exit")
|
||||||
|
#endif
|
||||||
("help,h",
|
("help,h",
|
||||||
"output help message and exit")
|
"output help message and exit")
|
||||||
;
|
;
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
void show_manpage(manpage::document doc, iolayer const& iol) {
|
||||||
|
auto const fancy = iol.term->is_fancy(iol.out);
|
||||||
|
auto content = render_manpage(doc, iol.term->width(), fancy);
|
||||||
|
if (!show_in_pager(content)) {
|
||||||
|
iol.out << content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace dwarfs
|
} // namespace dwarfs
|
||||||
|
@ -145,6 +145,9 @@ struct dwarfs_userdata {
|
|||||||
dwarfs_userdata& operator=(dwarfs_userdata const&) = delete;
|
dwarfs_userdata& operator=(dwarfs_userdata const&) = delete;
|
||||||
|
|
||||||
bool is_help{false};
|
bool is_help{false};
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
bool is_man{false};
|
||||||
|
#endif
|
||||||
options opts;
|
options opts;
|
||||||
stream_logger lgr;
|
stream_logger lgr;
|
||||||
filesystem_v2 fs;
|
filesystem_v2 fs;
|
||||||
@ -966,6 +969,9 @@ void usage(std::ostream& os, std::filesystem::path const& progname) {
|
|||||||
<< " -o tidy_max_age=TIME tidy blocks after this time (10m)\n"
|
<< " -o tidy_max_age=TIME tidy blocks after this time (10m)\n"
|
||||||
#if DWARFS_PERFMON_ENABLED
|
#if DWARFS_PERFMON_ENABLED
|
||||||
<< " -o perfmon=name[,...] enable performance monitor\n"
|
<< " -o perfmon=name[,...] enable performance monitor\n"
|
||||||
|
#endif
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
<< " --man show manual page and exit\n"
|
||||||
#endif
|
#endif
|
||||||
<< "\n";
|
<< "\n";
|
||||||
|
|
||||||
@ -1009,6 +1015,13 @@ int option_hdl(void* data, char const* arg, int key,
|
|||||||
userdata.is_help = true;
|
userdata.is_help = true;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
if (::strncmp(arg, "--man", 5) == 0) {
|
||||||
|
userdata.is_man = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1252,6 +1265,12 @@ int dwarfs_main(int argc, sys_char** argv, iolayer const& iol) {
|
|||||||
struct fuse_cmdline_opts fuse_opts;
|
struct fuse_cmdline_opts fuse_opts;
|
||||||
|
|
||||||
if (fuse_parse_cmdline(&args, &fuse_opts) == -1 || !fuse_opts.mountpoint) {
|
if (fuse_parse_cmdline(&args, &fuse_opts) == -1 || !fuse_opts.mountpoint) {
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
if (userdata.is_man) {
|
||||||
|
show_manpage(manpage::get_dwarfs_manpage(), iol);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
usage(iol.out, opts.progname);
|
usage(iol.out, opts.progname);
|
||||||
return userdata.is_help ? 0 : 1;
|
return userdata.is_help ? 0 : 1;
|
||||||
}
|
}
|
||||||
@ -1266,6 +1285,12 @@ int dwarfs_main(int argc, sys_char** argv, iolayer const& iol) {
|
|||||||
int mt, fg;
|
int mt, fg;
|
||||||
|
|
||||||
if (fuse_parse_cmdline(&args, &mountpoint, &mt, &fg) == -1 || !mountpoint) {
|
if (fuse_parse_cmdline(&args, &mountpoint, &mt, &fg) == -1 || !mountpoint) {
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
if (userdata.is_man) {
|
||||||
|
show_manpage(manpage::get_dwarfs_manpage(), iol);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
usage(iol.out, opts.progname);
|
usage(iol.out, opts.progname);
|
||||||
return userdata.is_help ? 0 : 1;
|
return userdata.is_help ? 0 : 1;
|
||||||
}
|
}
|
||||||
@ -1337,6 +1362,13 @@ int dwarfs_main(int argc, sys_char** argv, iolayer const& iol) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
if (userdata.is_man) {
|
||||||
|
show_manpage(manpage::get_dwarfs_manpage(), iol);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!opts.seen_mountpoint) {
|
if (!opts.seen_mountpoint) {
|
||||||
usage(iol.out, opts.progname);
|
usage(iol.out, opts.progname);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -115,6 +115,13 @@ int dwarfsck_main(int argc, sys_char** argv, iolayer const& iol) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
if (vm.count("man")) {
|
||||||
|
show_manpage(manpage::get_dwarfsck_manpage(), iol);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
auto constexpr usage = "Usage: dwarfsck [OPTIONS...]\n";
|
auto constexpr usage = "Usage: dwarfsck [OPTIONS...]\n";
|
||||||
|
|
||||||
if (vm.count("help") or !vm.count("input")) {
|
if (vm.count("help") or !vm.count("input")) {
|
||||||
|
@ -105,6 +105,13 @@ int dwarfsextract_main(int argc, sys_char** argv, iolayer const& iol) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
if (vm.count("man")) {
|
||||||
|
show_manpage(manpage::get_dwarfsextract_manpage(), iol);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
auto constexpr usage = "Usage: dwarfsextract [OPTIONS...]\n";
|
auto constexpr usage = "Usage: dwarfsextract [OPTIONS...]\n";
|
||||||
|
|
||||||
if (vm.count("help") or !vm.count("input")) {
|
if (vm.count("help") or !vm.count("input")) {
|
||||||
|
@ -660,6 +660,13 @@ int mkdwarfs_main(int argc, sys_char** argv, iolayer const& iol) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DWARFS_BUILTIN_MANPAGE
|
||||||
|
if (vm.count("man")) {
|
||||||
|
show_manpage(manpage::get_mkdwarfs_manpage(), iol);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
auto constexpr usage = "Usage: mkdwarfs [OPTIONS...]\n";
|
auto constexpr usage = "Usage: mkdwarfs [OPTIONS...]\n";
|
||||||
|
|
||||||
if (vm.count("long-help")) {
|
if (vm.count("long-help")) {
|
||||||
|
64
test/manpage_test.cpp
Normal file
64
test/manpage_test.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/**
|
||||||
|
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||||
|
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||||
|
*
|
||||||
|
* This file is part of dwarfs.
|
||||||
|
*
|
||||||
|
* dwarfs 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.
|
||||||
|
*
|
||||||
|
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "dwarfs/render_manpage.h"
|
||||||
|
|
||||||
|
using namespace dwarfs;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::map<std::string, manpage::document> const docs = {
|
||||||
|
{"mkdwarfs", manpage::get_mkdwarfs_manpage()},
|
||||||
|
{"dwarfs", manpage::get_dwarfs_manpage()},
|
||||||
|
{"dwarfsck", manpage::get_dwarfsck_manpage()},
|
||||||
|
{"dwarfsextract", manpage::get_dwarfsextract_manpage()},
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class manpage_render_test
|
||||||
|
: public ::testing::TestWithParam<std::tuple<std::string, bool>> {};
|
||||||
|
|
||||||
|
TEST_P(manpage_render_test, basic) {
|
||||||
|
auto [name, color] = GetParam();
|
||||||
|
auto doc = docs.at(name);
|
||||||
|
for (size_t width = 20; width <= 200; width += 1) {
|
||||||
|
auto out = render_manpage(doc, width, color);
|
||||||
|
EXPECT_GT(out.size(), 1000);
|
||||||
|
EXPECT_THAT(out, ::testing::HasSubstr(name));
|
||||||
|
EXPECT_THAT(out, ::testing::HasSubstr("SYNOPSIS"));
|
||||||
|
EXPECT_THAT(out, ::testing::HasSubstr("DESCRIPTION"));
|
||||||
|
EXPECT_THAT(out, ::testing::HasSubstr("AUTHOR"));
|
||||||
|
EXPECT_THAT(out, ::testing::HasSubstr("COPYRIGHT"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
dwarfs, manpage_render_test,
|
||||||
|
::testing::Combine(::testing::Values("mkdwarfs", "dwarfs", "dwarfsck",
|
||||||
|
"dwarfsextract"),
|
||||||
|
::testing::Bool()));
|
Loading…
x
Reference in New Issue
Block a user