stamenStatic Menu Generator | 
          
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | e5fe507b67ac4461aec1195a99eb2ba44ffeca2d | 
| parent | acf78d86b24d9223a32b26aeedd672f2b90171a2 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Sat, 29 Jun 2024 18:59:50 +0200 | 
Rewrite CMake project for better integration
| M | .clang-format | | | +++++++++++++++++++++++++++++++++++ ----------------------------------------------- | 
| A | .clang-tidy | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | .codespellrc | | | ++++++ | 
| M | .gitignore | | | +++++++++++++ ------ | 
| A | BUILDING.md | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| M | CMakeLists.txt | | | +++++++++++++++++++++++++++++++++++++++++ ----------------------------------------- | 
| A | CMakePresets.json | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | CODE_OF_CONDUCT.md | | | +++++ | 
| A | CONTRIBUTING.md | | | ++++++++++++++ | 
| A | HACKING.md | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| M | README.md | | | +++++++++++++++++++++++++++++++++++++++ ------------------------------------ | 
| A | cmake/coverage.cmake | | | +++++++++++++++++++++++++++++++++ | 
| A | cmake/dev-mode.cmake | | | ++++++++++++++++ | 
| A | cmake/folders.cmake | | | +++++++++++++++++++++ | 
| A | cmake/install-config.cmake | | | + | 
| A | cmake/install-rules.cmake | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | cmake/lint-targets.cmake | | | +++++++++++++++++++++++++++++++++ | 
| A | cmake/lint.cmake | | | +++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | cmake/prelude.cmake | | | ++++++++++ | 
| A | cmake/project-is-top-level.cmake | | | ++++++ | 
| A | cmake/spell-targets.cmake | | | ++++++++++++++++++++++ | 
| A | cmake/spell.cmake | | | +++++++++++++++++++++++++++++ | 
| A | cmake/variables.cmake | | | +++++++++++++++++++++++++++++++++++++++++ | 
| D | demo/CMakeLists.txt | | | --------------------------------------------------- | 
| D | demo/demo_menu.conf | | | ---------------------------- | 
| D | demo/dynamic.cpp | | | ----------------------------------- | 
| D | demo/main.c | | | --------------------------------- | 
| D | demo/main.cpp | | | ----------------------------------------- | 
| D | demo/shared.h | | | --------- | 
| A | example/CMakeLists.txt | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | example/demo_menu.conf | | | ++++++++++++++++++++++++++++ | 
| A | example/dynamic.cpp | | | ++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | example/example_c.c | | | ++++++++++++++++++++++++++++++++++++++ | 
| A | example/example_cpp.cpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | example/shared.h | | | +++++++++++ | 
| D | include/menu.h | | | -------------------- | 
| D | include/menu.hpp | | | ----------------------------------------------------------------------------- | 
| D | include/stamen.h | | | ------------------------------- | 
| D | include/stamen.hpp | | | ---------------- | 
| A | include/stamen/menu.h | | | ++++++++++++++++++ | 
| A | include/stamen/menu.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | include/stamen/stamen.h | | | +++++++++++++++++++++++++++++++++++ | 
| A | include/stamen/stamen.hpp | | | +++++++++++++ | 
| A | source/c_bindings.cpp | | | +++++++++++++++++++++++++++++++++++ | 
| A | source/generate.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | source/menu.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | source/stamen.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| D | src/CMakeLists.txt | | | ----------------------- | 
| D | src/c_bindings.cpp | | | ----------------------- | 
| D | src/generate.cpp | | | --------------------------------------------------------------------------------- | 
| D | src/menu.cpp | | | --------------------------------------------------------------------------------- | 
| D | src/stamen.cpp | | | ------------------------------------------------- | 
| D | stamenConfig.cmake.in | | | ------- | 
| A | test/CMakeLists.txt | | | +++++++++++++++++++++++++ | 
| A | test/source/stamen_test.cpp | | | ++++++++ | 
55 files changed, 1967 insertions(+), 927 deletions(-)
diff --git a/ .clang-format b/ .clang-format
@@ -1,216 +1,178 @@
---
          Language:        Cpp
          # BasedOnStyle:  Microsoft
          Language: Cpp
          # BasedOnStyle: Chromium
          AccessModifierOffset: -2
          AlignAfterOpenBracket: Align
          AlignArrayOfStructures: None
          AlignConsecutiveAssignments:
            Enabled:         false
            AcrossEmptyLines: false
            AcrossComments:  false
            AlignCompound:   false
            PadOperators:    true
          AlignConsecutiveBitFields:
            Enabled:         false
            AcrossEmptyLines: false
            AcrossComments:  false
            AlignCompound:   false
            PadOperators:    false
          AlignConsecutiveDeclarations:
            Enabled:         false
            AcrossEmptyLines: false
            AcrossComments:  false
            AlignCompound:   false
            PadOperators:    false
          AlignConsecutiveMacros:
            Enabled:         false
            AcrossEmptyLines: false
            AcrossComments:  false
            AlignCompound:   false
            PadOperators:    false
          AlignConsecutiveMacros: false
          AlignConsecutiveAssignments: true
          AlignConsecutiveBitFields: false
          AlignConsecutiveDeclarations: false
          AlignEscapedNewlines: Right
          AlignOperands:   Align
          AlignTrailingComments: true
          AlignOperands: DontAlign
          AlignTrailingComments: false
          AllowAllArgumentsOnNextLine: true
          AllowAllConstructorInitializersOnNextLine: false
          AllowAllParametersOfDeclarationOnNextLine: true
          AllowShortEnumsOnASingleLine: false
          AllowShortBlocksOnASingleLine: Always
          AllowShortCaseLabelsOnASingleLine: true
          AllowShortFunctionsOnASingleLine: true
          AllowShortBlocksOnASingleLine: Empty
          AllowShortCaseLabelsOnASingleLine: false
          AllowShortFunctionsOnASingleLine: Inline
          AllowShortLambdasOnASingleLine: All
          AllowShortIfStatementsOnASingleLine: AllIfsAndElse
          AllowShortLoopsOnASingleLine: false
          AllowShortLoopsOnASingleLine: true
          AlwaysBreakAfterDefinitionReturnType: None
          AlwaysBreakAfterReturnType: None
          AlwaysBreakBeforeMultilineStrings: false
          AlwaysBreakTemplateDeclarations: MultiLine
          AttributeMacros:
            - __capability
          BinPackArguments: true
          BinPackParameters: true
          AlwaysBreakBeforeMultilineStrings: true
          AlwaysBreakTemplateDeclarations: Yes
          BinPackArguments: false
          BinPackParameters: false
          BraceWrapping:
            AfterCaseLabel:  false
            AfterClass:      false
            AfterControlStatement: false
            AfterEnum:       false
            AfterFunction:   false
            AfterNamespace:  false
            AfterCaseLabel: false
            AfterClass: true
            AfterControlStatement: Always
            AfterEnum: true
            AfterFunction: true
            AfterNamespace: false
            AfterObjCDeclaration: false
            AfterStruct:     false
            AfterUnion:      false
            AfterExternBlock: false
            BeforeCatch:     false
            BeforeElse:      false
            BeforeLambdaBody: false
            BeforeWhile:     false
            IndentBraces:    false
            AfterStruct: true
            AfterUnion: true
            AfterExternBlock: true
            BeforeCatch: false
            BeforeElse: true
            BeforeLambdaBody: true
            BeforeWhile: false
            IndentBraces: false
            SplitEmptyFunction: true
            SplitEmptyRecord: true
            SplitEmptyNamespace: true
          BreakBeforeBinaryOperators: None
          BreakBeforeConceptDeclarations: Always
          BreakBeforeBinaryOperators: NonAssignment
          BreakBeforeBraces: Custom
          BreakBeforeInheritanceComma: false
          BreakInheritanceList: BeforeColon
          # BreakBeforeInheritanceComma: true
          BreakInheritanceList: BeforeComma
          BreakBeforeTernaryOperators: true
          BreakConstructorInitializersBeforeComma: false
          BreakConstructorInitializers: BeforeColon
          BreakAfterJavaFieldAnnotations: false
          BreakConstructorInitializersBeforeComma: true
          BreakConstructorInitializers: BeforeComma
          BreakAfterJavaFieldAnnotations: true
          BreakStringLiterals: true
          ColumnLimit:     79
          CommentPragmas:  '^ IWYU pragma:'
          QualifierAlignment: Leave
          ColumnLimit: 79
          CommentPragmas: '^ IWYU pragma:'
          CompactNamespaces: false
          ConstructorInitializerAllOnOneLineOrOnePerLine: false
          ConstructorInitializerIndentWidth: 4
          ContinuationIndentWidth: 4
          Cpp11BracedListStyle: true
          DeriveLineEnding: true
          DeriveLineEnding: false
          DerivePointerAlignment: false
          DisableFormat:   false
          EmptyLineAfterAccessModifier: Never
          EmptyLineBeforeAccessModifier: LogicalBlock
          DisableFormat: false
          ExperimentalAutoDetectBinPacking: false
          PackConstructorInitializers: BinPack
          BasedOnStyle:    ''
          ConstructorInitializerAllOnOneLineOrOnePerLine: false
          AllowAllConstructorInitializersOnNextLine: true
          FixNamespaceComments: true
          ForEachMacros:
            - foreach
            - Q_FOREACH
            - BOOST_FOREACH
          IfMacros:
            - KJ_IF_MAYBE
          IncludeBlocks:   Preserve
          IncludeBlocks: Regroup
          IncludeCategories:
            - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
              Priority:        2
              SortPriority:    0
              CaseSensitive:   false
            - Regex:           '^(<|"(gtest|gmock|isl|json)/)'
              Priority:        3
              SortPriority:    0
              CaseSensitive:   false
            - Regex:           '.*'
              Priority:        1
              SortPriority:    0
              CaseSensitive:   false
          IncludeIsMainRegex: '(Test)?$'
            # Standard library headers come before anything else
            - Regex: '^<[a-z_]+>'
              Priority: -1
            - Regex: '^<.+\.h(pp)?>'
              Priority: 1
            - Regex: '^<.*'
              Priority: 2
            - Regex: '.*'
              Priority: 3
          IncludeIsMainRegex: ''
          IncludeIsMainSourceRegex: ''
          IndentAccessModifiers: false
          IndentCaseLabels: false
          IndentCaseLabels: true
          IndentCaseBlocks: false
          IndentGotoLabels: true
          IndentPPDirectives: None
          IndentExternBlock: AfterExternBlock
          IndentRequiresClause: true
          IndentWidth:     4
          IndentPPDirectives: AfterHash
          IndentExternBlock: NoIndent
          IndentWidth: 2
          IndentWrappedFunctionNames: false
          InsertBraces:    false
          InsertTrailingCommas: None
          JavaScriptQuotes: Leave
          InsertTrailingCommas: Wrapped
          JavaScriptQuotes: Double
          JavaScriptWrapImports: true
          KeepEmptyLinesAtTheStartOfBlocks: true
          LambdaBodyIndentation: Signature
          KeepEmptyLinesAtTheStartOfBlocks: false
          MacroBlockBegin: ''
          MacroBlockEnd:   ''
          MacroBlockEnd: ''
          MaxEmptyLinesToKeep: 1
          NamespaceIndentation: None
          ObjCBinPackProtocolList: Auto
          ObjCBinPackProtocolList: Never
          ObjCBlockIndentWidth: 2
          ObjCBreakBeforeNestedBlockParam: true
          ObjCSpaceAfterProperty: false
          ObjCSpaceBeforeProtocolList: true
          PenaltyBreakAssignment: 2
          PenaltyBreakBeforeFirstCallParameter: 19
          PenaltyBreakBeforeFirstCallParameter: 1
          PenaltyBreakComment: 300
          PenaltyBreakFirstLessLess: 120
          PenaltyBreakOpenParenthesis: 0
          PenaltyBreakString: 1000
          PenaltyBreakTemplateDeclaration: 10
          PenaltyExcessCharacter: 1000000
          PenaltyReturnTypeOnItsOwnLine: 1000
          PenaltyIndentedWhitespace: 0
          PointerAlignment: Right
          PPIndentWidth:   -1
          ReferenceAlignment: Pointer
          ReflowComments:  true
          RemoveBracesLLVM: false
          RequiresClausePosition: OwnLine
          SeparateDefinitionBlocks: Leave
          ShortNamespaceLines: 1
          SortIncludes:    CaseSensitive
          SortJavaStaticImport: Before
          PenaltyReturnTypeOnItsOwnLine: 200
          PointerAlignment: Left
          RawStringFormats:
            - Language: Cpp
              Delimiters:
                - cc
                - CC
                - cpp
                - Cpp
                - CPP
                - 'c++'
                - 'C++'
              CanonicalDelimiter: ''
              BasedOnStyle: google
            - Language: TextProto
              Delimiters:
                - pb
                - PB
                - proto
                - PROTO
              EnclosingFunctions:
                - EqualsProto
                - EquivToProto
                - PARSE_PARTIAL_TEXT_PROTO
                - PARSE_TEST_PROTO
                - PARSE_TEXT_PROTO
                - ParseTextOrDie
                - ParseTextProtoOrDie
                - ParseTestProto
                - ParsePartialTestProto
              CanonicalDelimiter: ''
              BasedOnStyle: google
          ReflowComments: true
          SortIncludes: true
          SortUsingDeclarations: true
          SpaceAfterCStyleCast: false
          SpaceAfterLogicalNot: false
          SpaceAfterTemplateKeyword: true
          SpaceAfterTemplateKeyword: false
          SpaceBeforeAssignmentOperators: true
          SpaceBeforeCaseColon: false
          SpaceBeforeCpp11BracedList: false
          SpaceBeforeCpp11BracedList: true
          SpaceBeforeCtorInitializerColon: true
          SpaceBeforeInheritanceColon: true
          SpaceBeforeParens: ControlStatements
          SpaceBeforeParensOptions:
            AfterControlStatements: true
            AfterForeachMacros: true
            AfterFunctionDefinitionName: false
            AfterFunctionDeclarationName: false
            AfterIfMacros:   true
            AfterOverloadedOperator: false
            AfterRequiresInClause: false
            AfterRequiresInExpression: false
            BeforeNonEmptyParentheses: false
          SpaceAroundPointerQualifiers: Default
          SpaceBeforeParens: ControlStatementsExceptForEachMacros
          SpaceBeforeRangeBasedForLoopColon: true
          SpaceInEmptyBlock: false
          SpaceInEmptyParentheses: false
          SpacesBeforeTrailingComments: 1
          SpacesInAngles:  Never
          SpacesBeforeTrailingComments: 2
          SpacesInAngles: false
          SpacesInConditionalStatement: false
          SpacesInContainerLiterals: true
          SpacesInContainerLiterals: false
          SpacesInCStyleCastParentheses: false
          SpacesInLineCommentPrefix:
            Minimum:         1
            Maximum:         -1
          SpacesInParentheses: false
          SpacesInSquareBrackets: false
          SpaceBeforeSquareBrackets: false
          BitFieldColonSpacing: Both
          Standard:        Latest
          StatementAttributeLikeMacros:
            - Q_EMIT
          Standard: Auto
          StatementMacros:
            - Q_UNUSED
            - QT_REQUIRE_VERSION
          TabWidth:        4
          UseCRLF:         false
          UseTab:          Never
          TabWidth: 8
          UseCRLF: false
          UseTab: Never
          WhitespaceSensitiveMacros:
            - STRINGIZE
            - PP_STRINGIZE
            - BOOST_PP_STRINGIZE
            - NS_SWIFT_NAME
            - CF_SWIFT_NAME
          ...
          diff --git a/ .clang-tidy b/ .clang-tidy
@@ -0,0 +1,172 @@
---
          # Enable ALL the things! Except not really
          # misc-non-private-member-variables-in-classes: the options don't do anything
          # modernize-use-nodiscard: too aggressive, attribute is situationally useful
          Checks: "*,\
            -altera-*,\
            -fuchsia-*,\
            -llvmlibc-*,\
            -*-avoid-goto,\
            -*-braces-around-statements,\
            -*-c-arrays,\
            -*-vararg,\
            -*-array*decay,\
            -bugprone-argument-comment,\
            -bugprone-easily-swappable-parameters,\
            -cert-dcl50-cpp,\
            -concurrency-mt-unsafe,\
            -cppcoreguidelines-avoid-magic-numbers,\
            -fuchsia-multiple-inheritance,\
            -hicpp-signed-bitwise,\
            -llvm-header-guard,\
            -llvm-include-order,\
            -misc-no-recursion,\
            -misc-non-private-member-variables-in-classes,\
            -modernize-use-nodiscard,\
            -modernize-use-trailing-return-type,\
            -readability-function-cognitive-complexity,\
            -readability-magic-numbers
          "
          WarningsAsErrors: ''
          CheckOptions:
            - key: 'bugprone-argument-comment.StrictMode'
              value: 'true'
          # Prefer using enum classes with 2 values for parameters instead of bools
            - key: 'bugprone-argument-comment.CommentBoolLiterals'
              value: 'true'
            - key: 'bugprone-misplaced-widening-cast.CheckImplicitCasts'
              value: 'true'
            - key: 'bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression'
              value: 'true'
            - key: 'bugprone-suspicious-string-compare.WarnOnLogicalNotComparison'
              value: 'true'
            - key: 'readability-simplify-boolean-expr.ChainedConditionalReturn'
              value: 'true'
            - key: 'readability-simplify-boolean-expr.ChainedConditionalAssignment'
              value: 'true'
            - key: 'readability-uniqueptr-delete-release.PreferResetCall'
              value: 'true'
            - key: 'cppcoreguidelines-init-variables.MathHeader'
              value: '<cmath>'
            - key: 'cppcoreguidelines-narrowing-conversions.PedanticMode'
              value: 'true'
            - key: 'readability-else-after-return.WarnOnUnfixable'
              value: 'true'
            - key: 'readability-else-after-return.WarnOnConditionVariables'
              value: 'true'
            - key: 'readability-inconsistent-declaration-parameter-name.Strict'
              value: 'true'
            - key: 'readability-qualified-auto.AddConstToQualified'
              value: 'true'
            - key: 'readability-redundant-access-specifiers.CheckFirstDeclaration'
              value: 'true'
          # These seem to be the most common identifier styles
            - key: 'readability-identifier-naming.AbstractClassCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ClassCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ClassConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ClassMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ClassMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstantMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstantParameterCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstantPointerParameterCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstexprFunctionCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstexprMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstexprVariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.EnumCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.EnumConstantCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.FunctionCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalConstantPointerCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalFunctionCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalPointerCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalVariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.InlineNamespaceCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.LocalConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.LocalConstantPointerCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.LocalPointerCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.LocalVariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.MacroDefinitionCase'
              value: 'UPPER_CASE'
            - key: 'readability-identifier-naming.MemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.MethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.NamespaceCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ParameterCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ParameterPackCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PointerParameterCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PrivateMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PrivateMemberPrefix'
              value: 'm_'
            - key: 'readability-identifier-naming.PrivateMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ProtectedMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ProtectedMemberPrefix'
              value: 'm_'
            - key: 'readability-identifier-naming.ProtectedMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PublicMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PublicMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ScopedEnumConstantCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.StaticConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.StaticVariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.StructCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.TemplateParameterCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.TemplateTemplateParameterCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.TypeAliasCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.TypedefCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.TypeTemplateParameterCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.TypeTemplateParameterIgnoredRegexp'
              value: 'expr-type'
            - key: 'readability-identifier-naming.UnionCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ValueTemplateParameterCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.VariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.VirtualMethodCase'
              value: 'lower_case'
          ...
        
        diff --git a/ .codespellrc b/ .codespellrc
@@ -0,0 +1,6 @@
[codespell]
          builtin = clear,rare,en-GB_to_en-US,names,informal,code
          check-filenames =
          check-hidden =
          skip = */.git,*/build,*/prefix
          quiet-level = 2
        
        diff --git a/ .gitignore b/ .gitignore
@@ -1,6 +1,13 @@
build
          .cache
          demo/demo_menu.c
          demo/demo_menu.cpp
          demo/demo_menu.h
          demo/demo_menu.hpp
          **/.DS_Store
          .idea/
          .vs/
          .vscode/
          build/
          cmake-build-*/
          prefix/
          .clangd
          CMakeLists.txt.user
          CMakeUserPresets.json
          compile_commands.json
          env.bat
          env.ps1
        
        diff --git a/ BUILDING.md b/ BUILDING.md
@@ -0,0 +1,89 @@
# Building with CMake
          ## Build
          This project doesn't require any special command-line flags to build to keep
          things simple.
          Here are the steps for building in release mode with a single-configuration
          generator, like the Unix Makefiles one:
          ```sh
          cmake -S . -B build -D CMAKE_BUILD_TYPE=Release
          cmake --build build
          ```
          Here are the steps for building in release mode with a multi-configuration
          generator, like the Visual Studio ones:
          ```sh
          cmake -S . -B build
          cmake --build build --config Release
          ```
          ### Building with MSVC
          Note that MSVC by default is not standards compliant and you need to pass some
          flags to make it behave properly. See the `flags-msvc` preset in the
          [CMakePresets.json](CMakePresets.json) file for the flags and with what
          variable to provide them to CMake during configuration.
          ### Building on Apple Silicon
          CMake supports building on Apple Silicon properly since 3.20.1. Make sure you
          have the [latest version][1] installed.
          ## Install
          This project doesn't require any special command-line flags to install to keep
          things simple. As a prerequisite, the project has to be built with the above
          commands already.
          The below commands require at least CMake 3.15 to run, because that is the
          version in which [Install a Project][2] was added.
          Here is the command for installing the release mode artifacts with a
          single-configuration generator, like the Unix Makefiles one:
          ```sh
          cmake --install build
          ```
          Here is the command for installing the release mode artifacts with a
          multi-configuration generator, like the Visual Studio ones:
          ```sh
          cmake --install build --config Release
          ```
          ### CMake package
          This project exports a CMake package to be used with the [`find_package`][3]
          command of CMake:
          * Package name: `stamen`
          * Target name: `stamen::stamen`
          Example usage:
          ```cmake
          find_package(stamen REQUIRED)
          # Declare the imported target as a build requirement using PRIVATE, where
          # project_target is a target created in the consuming project
          target_link_libraries(
              project_target PRIVATE
              stamen::stamen
          )
          ```
          ### Note to packagers
          The `CMAKE_INSTALL_INCLUDEDIR` is set to a path other than just `include` if
          the project is configured as a top level project to avoid indirectly including
          other libraries when installed to a common prefix. Please review the
          [install-rules.cmake](cmake/install-rules.cmake) file for the full set of
          install rules.
          [1]: https://cmake.org/download/
          [2]: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project
          [3]: https://cmake.org/cmake/help/latest/command/find_package.html
        
        diff --git a/ CMakeLists.txt b/ CMakeLists.txt
@@ -1,99 +1,101 @@
cmake_minimum_required(VERSION 3.25.2)
          set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
          cmake_minimum_required(VERSION 3.14)
          include(cmake/prelude.cmake)
          project(
              stamen
              VERSION 1.1.0
              VERSION 1.1.1
              DESCRIPTION "Static menu generator"
              HOMEPAGE_URL "https://git.dimitrijedobrota.com/stamen"
              LANGUAGES C CXX
          )
          set(CMAKE_CXX_STANDARD 20)
          set(CMAKE_CXX_STANDARD_REQUIRED ON)
          set(CMAKE_CXX_EXTENSIONS OFF)
          set(CMAKE_C_STANDARD 17)
          set(CMAKE_C_STANDARD_REQUIRED ON)
          set(CMAKE_C_EXTENSIONS OFF)
          if(NOT CMAKE_BUILD_TYPE)
          	set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
          endif()
          include(cmake/project-is-top-level.cmake)
          include(cmake/variables.cmake)
          include(GNUInstallDirs)
          find_package(poafloc 1.1 CONFIG REQUIRED)
          set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
          set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
          set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
          # ---- Declare library ----
          set(INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR} CACHE PATH "Installation directory for libraries")
          set(INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR} CACHE PATH "Installation directory for executables")
          set(INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE PATH "Installation directory for header files")
          if(WIN32 AND NOT CYGWIN)
          	set(DEF_INSTALL_CMAKEDIR CMake)
          else()
          	set(DEF_INSTALL_CMAKEDIR share/cmake/${PROJECT_NAME})
          endif()
          set(INSTALL_CMAKEDIR ${DEF_INSTALL_CMAKEDIR} CACHE PATH "Installation directory for CMake files")
          message(STATUS "Build type set to ${CMAKE_BUILD_TYPE}")
          message(STATUS "Project will be installed to ${CMAKE_INSTALL_PREFIX}")
          foreach(p LIB BIN INCLUDE CMAKE)
          	file(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/${INSTALL_${p}DIR} _path)
          	message(STATUS "installing ${p} components to ${_path}")
          	unset(_path)
          endforeach()
          find_package(poafloc 1 CONFIG REQUIRED)
          add_subdirectory(src)
          add_subdirectory(demo)
          install(
          	TARGETS
                  stamen-generate
          		stamen
          	EXPORT
          		stamenTargets
          	ARCHIVE
          		DESTINATION ${INSTALL_LIBDIR}
          		COMPONENT lib
          	RUNTIME
          		DESTINATION ${INSTALL_BINDIR}
          		COMPONENT bin
          	LIBRARY
          		DESTINATION ${INSTALL_LIBDIR}
          		COMPONENT lib
          	PUBLIC_HEADER
          		DESTINATION ${INSTALL_INCLUDEDIR}/stamen
          		COMPONENT dev
          add_library(
              stamen_stamen
              source/stamen.cpp
              source/menu.cpp
              source/c_bindings.cpp
          )
          install(
          	EXPORT stamenTargets
          	NAMESPACE "stamen::"
          	DESTINATION ${INSTALL_CMAKEDIR}
          	COMPONENT dev
          target_link_libraries(stamen_stamen PUBLIC poafloc::poafloc)
          add_library(stamen::stamen ALIAS stamen_stamen)
          include(GenerateExportHeader)
          generate_export_header(
              stamen_stamen
              BASE_NAME stamen
              EXPORT_FILE_NAME export/stamen/stamen_export.hpp
              CUSTOM_CONTENT_FROM_VARIABLE pragma_suppress_c4251
          )
          include(CMakePackageConfigHelpers)
          if(NOT BUILD_SHARED_LIBS)
            target_compile_definitions(stamen_stamen PUBLIC STAMEN_STATIC_DEFINE)
          endif()
          write_basic_package_version_file(
          	${CMAKE_CURRENT_BINARY_DIR}/stamenConfigVersion.cmake
          	VERSION ${PROJECT_VERSION}
          	COMPATIBILITY SameMajorVersion
          set_target_properties(
              stamen_stamen PROPERTIES
              CXX_VISIBILITY_PRESET hidden
              VISIBILITY_INLINES_HIDDEN YES
              VERSION "${PROJECT_VERSION}"
              SOVERSION "${PROJECT_VERSION_MAJOR}"
              EXPORT_NAME stamen
              OUTPUT_NAME stamen
          )
          configure_package_config_file(
          	${PROJECT_SOURCE_DIR}/stamenConfig.cmake.in
          	${CMAKE_CURRENT_BINARY_DIR}/stamenConfig.cmake
          	INSTALL_DESTINATION ${INSTALL_CMAKEDIR}
          target_include_directories(
              stamen_stamen ${warning_guard}
              PUBLIC
              "\$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
          )
          install(
          	FILES
          		${CMAKE_CURRENT_BINARY_DIR}/stamenConfig.cmake
          		${CMAKE_CURRENT_BINARY_DIR}/stamenConfigVersion.cmake
          	DESTINATION
          		${INSTALL_CMAKEDIR}
          target_include_directories(
              stamen_stamen SYSTEM
              PUBLIC
              "\$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/export>"
          )
          target_compile_features(stamen_stamen PUBLIC cxx_std_20)
          # ---- Declare executable ----
          add_executable(stamen_exe source/generate.cpp)
          add_executable(stamen::exe ALIAS stamen_exe)
          target_link_libraries(stamen_exe PRIVATE stamen::stamen)
          set_property(TARGET stamen_exe PROPERTY OUTPUT_NAME stamen)
          target_compile_features(stamen_exe PRIVATE cxx_std_20)
          # ---- Install rules ----
          if(NOT CMAKE_SKIP_INSTALL_RULES)
            include(cmake/install-rules.cmake)
          endif()
          # ---- Examples ----
          if(PROJECT_IS_TOP_LEVEL)
            option(BUILD_EXAMPLES "Build examples tree." "${stamen_DEVELOPER_MODE}")
            if(BUILD_EXAMPLES)
              add_subdirectory(example)
            endif()
          endif()
          # ---- Developer mode ----
          if(NOT stamen_DEVELOPER_MODE)
            return()
          elseif(NOT PROJECT_IS_TOP_LEVEL)
            message(
                AUTHOR_WARNING
                "Developer mode is intended for developers of stamen"
            )
          endif()
          include(cmake/dev-mode.cmake)
        
        diff --git a/ CMakePresets.json b/ CMakePresets.json
@@ -0,0 +1,160 @@
{
            "version": 2,
            "cmakeMinimumRequired": {
              "major": 3,
              "minor": 14,
              "patch": 0
            },
            "configurePresets": [
              {
                "name": "cmake-pedantic",
                "hidden": true,
                "warnings": {
                  "dev": true,
                  "deprecated": true,
                  "uninitialized": true,
                  "unusedCli": true,
                  "systemVars": false
                },
                "errors": {
                  "dev": true,
                  "deprecated": true
                }
              },
              {
                "name": "dev-mode",
                "hidden": true,
                "inherits": "cmake-pedantic",
                "cacheVariables": {
                  "stamen_DEVELOPER_MODE": "ON"
                }
              },
              {
                "name": "cppcheck",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_CPPCHECK": "cppcheck;--inline-suppr"
                }
              },
              {
                "name": "clang-tidy",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_CLANG_TIDY": "clang-tidy;--header-filter=^${sourceDir}/"
                }
              },
              {
                "name": "ci-std",
                "description": "This preset makes sure the project actually builds with at least the specified standard",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_EXTENSIONS": "OFF",
                  "CMAKE_CXX_STANDARD": "20",
                  "CMAKE_CXX_STANDARD_REQUIRED": "ON"
                }
              },
              {
                "name": "flags-gcc-clang",
                "description": "These flags are supported by both GCC and Clang",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_FLAGS": "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS=1 -fstack-protector-strong -fcf-protection=full -fstack-clash-protection -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast",
                  "CMAKE_EXE_LINKER_FLAGS": "-Wl,--allow-shlib-undefined,--as-needed,-z,noexecstack,-z,relro,-z,now,-z,nodlopen",
                  "CMAKE_SHARED_LINKER_FLAGS": "-Wl,--allow-shlib-undefined,--as-needed,-z,noexecstack,-z,relro,-z,now,-z,nodlopen"
                }
              },
              {
                "name": "flags-appleclang",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_FLAGS": "-fstack-protector-strong -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast"
                }
              },
              {
                "name": "flags-msvc",
                "description": "Note that all the flags after /W4 are required for MSVC to conform to the language standard",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_FLAGS": "/sdl /guard:cf /utf-8 /diagnostics:caret /w14165 /w44242 /w44254 /w44263 /w34265 /w34287 /w44296 /w44365 /w44388 /w44464 /w14545 /w14546 /w14547 /w14549 /w14555 /w34619 /w34640 /w24826 /w14905 /w14906 /w14928 /w45038 /W4 /permissive- /volatile:iso /Zc:inline /Zc:preprocessor /Zc:enumTypes /Zc:lambda /Zc:__cplusplus /Zc:externConstexpr /Zc:throwingNew /EHsc",
                  "CMAKE_EXE_LINKER_FLAGS": "/machine:x64 /guard:cf",
                  "CMAKE_SHARED_LINKER_FLAGS": "/machine:x64 /guard:cf"
                }
              },
              {
                "name": "ci-linux",
                "inherits": ["flags-gcc-clang", "ci-std"],
                "generator": "Unix Makefiles",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_BUILD_TYPE": "Release"
                }
              },
              {
                "name": "ci-darwin",
                "inherits": ["flags-appleclang", "ci-std"],
                "generator": "Xcode",
                "hidden": true
              },
              {
                "name": "ci-win64",
                "inherits": ["flags-msvc", "ci-std"],
                "generator": "Visual Studio 17 2022",
                "architecture": "x64",
                "hidden": true
              },
              {
                "name": "coverage-linux",
                "binaryDir": "${sourceDir}/build/coverage",
                "inherits": "ci-linux",
                "hidden": true,
                "cacheVariables": {
                  "ENABLE_COVERAGE": "ON",
                  "CMAKE_BUILD_TYPE": "Coverage",
                  "CMAKE_CXX_FLAGS_COVERAGE": "-Og -g --coverage -fkeep-inline-functions -fkeep-static-functions",
                  "CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage",
                  "CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage"
                }
              },
              {
                "name": "ci-coverage",
                "inherits": ["coverage-linux", "dev-mode"],
                "cacheVariables": {
                  "COVERAGE_HTML_COMMAND": ""
                }
              },
              {
                "name": "ci-sanitize",
                "binaryDir": "${sourceDir}/build/sanitize",
                "inherits": ["ci-linux", "dev-mode"],
                "cacheVariables": {
                  "CMAKE_BUILD_TYPE": "Sanitize",
                  "CMAKE_CXX_FLAGS_SANITIZE": "-U_FORTIFY_SOURCE -O2 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common"
                }
              },
              {
                "name": "ci-build",
                "binaryDir": "${sourceDir}/build",
                "hidden": true
              },
              {
                "name": "ci-multi-config",
                "description": "Speed up multi-config generators by generating only one configuration instead of the defaults",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CONFIGURATION_TYPES": "Release"
                }
              },
              {
                "name": "ci-macos",
                "inherits": ["ci-build", "ci-darwin", "dev-mode", "ci-multi-config"]
              },
              {
                "name": "ci-ubuntu",
                "inherits": ["ci-build", "ci-linux", "clang-tidy", "cppcheck", "dev-mode"]
              },
              {
                "name": "ci-windows",
                "inherits": ["ci-build", "ci-win64", "dev-mode", "ci-multi-config"]
              }
            ]
          }
        
        diff --git a/ CODE_OF_CONDUCT.md b/ CODE_OF_CONDUCT.md
@@ -0,0 +1,5 @@
# Code of Conduct
          * You will be judged by your contributions first, and your sense of humor
            second.
          * Nobody owes you anything.
        
        diff --git a/ CONTRIBUTING.md b/ CONTRIBUTING.md
@@ -0,0 +1,14 @@
# Contributing
          ## Code of Conduct
          Please see the [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) document.
          ## Getting started
          Helpful notes for developers can be found in the [`HACKING.md`](HACKING.md)
          document.
          In addition to he above, if you use the presets file as instructed, then you
          should NOT check it into source control, just as the CMake documentation
          suggests.
        
        diff --git a/ HACKING.md b/ HACKING.md
@@ -0,0 +1,170 @@
# Hacking
          Here is some wisdom to help you build and test this project as a developer and
          potential contributor.
          If you plan to contribute, please read the [CONTRIBUTING](CONTRIBUTING.md)
          guide.
          ## Developer mode
          Build system targets that are only useful for developers of this project are
          hidden if the `stamen_DEVELOPER_MODE` option is disabled. Enabling this
          option makes tests and other developer targets and options available. Not
          enabling this option means that you are a consumer of this project and thus you
          have no need for these targets and options.
          Developer mode is always set to on in CI workflows.
          ### Presets
          This project makes use of [presets][1] to simplify the process of configuring
          the project. As a developer, you are recommended to always have the [latest
          CMake version][2] installed to make use of the latest Quality-of-Life
          additions.
          You have a few options to pass `stamen_DEVELOPER_MODE` to the configure
          command, but this project prefers to use presets.
          As a developer, you should create a `CMakeUserPresets.json` file at the root of
          the project:
          ```json
          {
            "version": 2,
            "cmakeMinimumRequired": {
              "major": 3,
              "minor": 14,
              "patch": 0
            },
            "configurePresets": [
              {
                "name": "dev",
                "binaryDir": "${sourceDir}/build/dev",
                "inherits": ["dev-mode", "ci-<os>"],
                "cacheVariables": {
                  "CMAKE_BUILD_TYPE": "Debug"
                }
              }
            ],
            "buildPresets": [
              {
                "name": "dev",
                "configurePreset": "dev",
                "configuration": "Debug"
              }
            ],
            "testPresets": [
              {
                "name": "dev",
                "configurePreset": "dev",
                "configuration": "Debug",
                "output": {
                  "outputOnFailure": true
                }
              }
            ]
          }
          ```
          You should replace `<os>` in your newly created presets file with the name of
          the operating system you have, which may be `win64`, `linux` or `darwin`. You
          can see what these correspond to in the
          [`CMakePresets.json`](CMakePresets.json) file.
          `CMakeUserPresets.json` is also the perfect place in which you can put all
          sorts of things that you would otherwise want to pass to the configure command
          in the terminal.
          > **Note**
          > Some editors are pretty greedy with how they open projects with presets.
          > Some just randomly pick a preset and start configuring without your consent,
          > which can be confusing. Make sure that your editor configures when you
          > actually want it to, for example in CLion you have to make sure only the
          > `dev-dev preset` has `Enable profile` ticked in
          > `File > Settings... > Build, Execution, Deployment > CMake` and in Visual
          > Studio you have to set the option `Never run configure step automatically`
          > in `Tools > Options > CMake` **prior to opening the project**, after which
          > you can manually configure using `Project > Configure Cache`.
          ### Configure, build and test
          If you followed the above instructions, then you can configure, build and test
          the project respectively with the following commands from the project root on
          any operating system with any build system:
          ```sh
          cmake --preset=dev
          cmake --build --preset=dev
          ctest --preset=dev
          ```
          If you are using a compatible editor (e.g. VSCode) or IDE (e.g. CLion, VS), you
          will also be able to select the above created user presets for automatic
          integration.
          Please note that both the build and test commands accept a `-j` flag to specify
          the number of jobs to use, which should ideally be specified to the number of
          threads your CPU has. You may also want to add that to your preset using the
          `jobs` property, see the [presets documentation][1] for more details.
          ### Developer mode targets
          These are targets you may invoke using the build command from above, with an
          additional `-t <target>` flag:
          #### `coverage`
          Available if `ENABLE_COVERAGE` is enabled. This target processes the output of
          the previously run tests when built with coverage configuration. The commands
          this target runs can be found in the `COVERAGE_TRACE_COMMAND` and
          `COVERAGE_HTML_COMMAND` cache variables. The trace command produces an info
          file by default, which can be submitted to services with CI integration. The
          HTML command uses the trace command's output to generate an HTML document to
          `<binary-dir>/coverage_html` by default.
          #### `docs`
          Available if `BUILD_MCSS_DOCS` is enabled. Builds to documentation using
          Doxygen and m.css. The output will go to `<binary-dir>/docs` by default
          (customizable using `DOXYGEN_OUTPUT_DIRECTORY`).
          #### `format-check` and `format-fix`
          These targets run the clang-format tool on the codebase to check errors and to
          fix them respectively. Customization available using the `FORMAT_PATTERNS` and
          `FORMAT_COMMAND` cache variables.
          #### `spell-check` and `spell-fix`
          These targets run the codespell tool on the codebase to check errors and to fix
          them respectively. Customization available using the `SPELL_COMMAND` cache
          variable.
          ## Running tests on Windows with `BUILD_SHARED_LIBS=ON`
          If you are building a shared library on Windows, you must add the path to the
          DLL file to `PATH` when you want to run tests. One way you could do that is by
          using PowerShell and writing a script for it, e.g. `env.ps1` at the project
          root:
          ```powershell
          $oldPrompt = (Get-Command prompt).ScriptBlock
          function prompt() { "(Debug) $(& $oldPrompt)" }
          $VsInstallPath = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -Property InstallationPath
          $Env:Path += ";$VsInstallPath\Common7\IDE;$Pwd\build\dev\Debug"
          ```
          Then you can source this script by running `. env.ps1`. This particular
          example will only work for Debug builds.
          ### Passing `PATH` to editors
          Make sure you launch your editor of choice from the console with the above
          script sourced. Look for `(Debug)` in the prompt to confirm, then run e.g.
          `code .` for VScode or `devenv .` for Visual Studio.
          [1]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
          [2]: https://cmake.org/download/
        
        diff --git a/ README.md b/ README.md
@@ -1,8 +1,7 @@
# Stamen
          # stamen
          Static menu generator written in C++20
          ## Description
          This project allows for a creation of static menus, to be used in C or C++, on
        
        
          @@ -20,23 +19,34 @@ 
          The main advantage of stamen is two modes it can operate in:
        
        
          2) Dynamic mode, where menus are generated on the fly (possibly in runtime) without recompilation.
          ## Getting Started
          ### Dependencies
          ## Dependencies
          * CMake 3.25.2 or latter
          * Compiler with C++20 support
          * [Poafloc Library](https://github.com/DimitrijeDobrota/poafloc)
          * [Poafloc 1.1](https://github.com/DimitrijeDobrota/poafloc)
          ## Building and installing
          See the [BUILDING](BUILDING.md) document.
          ### Installing
          ## Usage
          * Clone the repo
          * Make a build folder and cd into it
          * Run `cmake <path to cloned repo>` to set up the build scripts
          * Run `make` to build the project
          * Run `cmake --build . --target install` to install the project
          > Please reference example folder for relevant usage example.
          > Refer to example/CMakeLists.txt to see how to integrate stamen into build system
          There are a few things needed before you begin.
          * All types and functions with prefixes `stamen_` and `stamen_menu_` are also
          available in namespaces `stamen::` and `stamen::menu::` in C++ for easier use.
          * Panel and item codes must be one word. In addition they must be valid C/C++
          function names if static menu is to be build correctly.
          * Each free function must have `int name(int);` signature as prescribed by
          `stamen_callback_f`. Passed int it is intended to detonate the position of an
          item in the previous panel which is essential for dynamic menu implementation,
          but it is not required to be used like that.
          ### Configuration
          
          @@ -74,29 +84,11 @@ 
          reference to another panel or any other function (from now on referred to as
        
        
          `free function`).
          ### Usage
          > Please reference demo folder for relevant usage example.
          > Refer to demo/CMakeLists.txt to see how to integrate stamen into build system
          ### Static menu
          There are a few things needed before you begin.
          * All types and functions with prefixes `stamen_` and `stamen_menu_` are also
          available in namespaces `stamen::` and `stamen::menu::` in C++ for easier use.
          * Panel and item codes must be one word. In addition they must be valid C/C++
          function names if static menu is to be build correctly.
          * Each free function must have `int name(int);` signature as prescribed by
          `stamen_callback_f`. Passed int it is intended to detonate the position of an
          item in the previous panel which is essential for dynamic menu implementation,
          but it is not required to be used like that.
          #### Static menu
          > Please refer to `stamen --help` for list of all options
          > Please refer to `stamen-generate --help` for list of all options
          After writing a configuration file, run `stamen-generate <config file>` which
          After writing a configuration file, run `stamen <config file>` which
          will create source file and include file in the current directory with the name
          as the configuration file but with extensions `.cpp` and `.hpp` respectively.
          You can create  files with extensions `.c` and `.h` by appending adding `--c`
        
        
          @@ -120,7 +112,7 @@ 
          Generated source file should be compiled with the rest of your code. If
        
        
          You can call any function to display the menu starting from that specific pane.
          #### Custom display function
          ### Custom display function
          Please refer to the implementation of `stamen_builtin_display` to get a general
          idea of the direction.
        
        
          @@ -135,7 +127,7 @@ 
          measure how many panels back should be backtracked after a free function
        
        
          terminates, but you can use in any way you see fit.
          #### Dynamic menu
          ### Dynamic menu
          In dynamic mode, configuration file is read every time the program is run. In
          order to invoke the menu you need to add the following snippet to your C
        
        
          @@ -177,6 +169,10 @@ 
          written in C or C++.
        
        
          ## Version History
          - 1.2
              * Modernize CMake project
              * Modernize codebase
          - 1.1
              * Separate C and C++ interfaces
              * Separate dynamic mode into menu namespace
        
        
          @@ -188,7 +184,14 @@ 
          written in C or C++.
        
        
              * Initial Release
          ## Contributing
          See the [CONTRIBUTING](CONTRIBUTING.md) document.
          ## License
          This project is licensed under the MIT License - see the LICENSE.md file for details
          This project is licensed under the MIT License -
          see the [LICENSE](LICENSE.md) file for details
          diff --git a/ cmake/coverage.cmake b/ cmake/coverage.cmake
@@ -0,0 +1,33 @@
# ---- Variables ----
          # We use variables separate from what CTest uses, because those have
          # customization issues
          set(
              COVERAGE_TRACE_COMMAND
              lcov -c -q
              -o "${PROJECT_BINARY_DIR}/coverage.info"
              -d "${PROJECT_BINARY_DIR}"
              --include "${PROJECT_SOURCE_DIR}/*"
              CACHE STRING
              "; separated command to generate a trace for the 'coverage' target"
          )
          set(
              COVERAGE_HTML_COMMAND
              genhtml --legend -f -q
              "${PROJECT_BINARY_DIR}/coverage.info"
              -p "${PROJECT_SOURCE_DIR}"
              -o "${PROJECT_BINARY_DIR}/coverage_html"
              CACHE STRING
              "; separated command to generate an HTML report for the 'coverage' target"
          )
          # ---- Coverage target ----
          add_custom_target(
              coverage
              COMMAND ${COVERAGE_TRACE_COMMAND}
              COMMAND ${COVERAGE_HTML_COMMAND}
              COMMENT "Generating coverage report"
              VERBATIM
          )
        
        diff --git a/ cmake/dev-mode.cmake b/ cmake/dev-mode.cmake
@@ -0,0 +1,16 @@
include(cmake/folders.cmake)
          include(CTest)
          if(BUILD_TESTING)
            add_subdirectory(test)
          endif()
          option(ENABLE_COVERAGE "Enable coverage support separate from CTest's" OFF)
          if(ENABLE_COVERAGE)
            include(cmake/coverage.cmake)
          endif()
          include(cmake/lint-targets.cmake)
          include(cmake/spell-targets.cmake)
          add_folders(Project)
        
        diff --git a/ cmake/folders.cmake b/ cmake/folders.cmake
@@ -0,0 +1,21 @@
set_property(GLOBAL PROPERTY USE_FOLDERS YES)
          # Call this function at the end of a directory scope to assign a folder to
          # targets created in that directory. Utility targets will be assigned to the
          # UtilityTargets folder, otherwise to the ${name}Targets folder. If a target
          # already has a folder assigned, then that target will be skipped.
          function(add_folders name)
            get_property(targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
            foreach(target IN LISTS targets)
              get_property(folder TARGET "${target}" PROPERTY FOLDER)
              if(DEFINED folder)
                continue()
              endif()
              set(folder Utility)
              get_property(type TARGET "${target}" PROPERTY TYPE)
              if(NOT type STREQUAL "UTILITY")
                set(folder "${name}")
              endif()
              set_property(TARGET "${target}" PROPERTY FOLDER "${folder}Targets")
            endforeach()
          endfunction()
        
        diff --git a/ cmake/install-config.cmake b/ cmake/install-config.cmake
@@ -0,0 +1,1 @@
include("${CMAKE_CURRENT_LIST_DIR}/stamenTargets.cmake")
        
        diff --git a/ cmake/install-rules.cmake b/ cmake/install-rules.cmake
@@ -0,0 +1,77 @@
if(PROJECT_IS_TOP_LEVEL)
            set(
                CMAKE_INSTALL_INCLUDEDIR "include/stamen-${PROJECT_VERSION}"
                CACHE STRING ""
            )
            set_property(CACHE CMAKE_INSTALL_INCLUDEDIR PROPERTY TYPE PATH)
          endif()
          include(CMakePackageConfigHelpers)
          include(GNUInstallDirs)
          # find_package(<package>) call for consumers to find this project
          set(package stamen)
          install(
              DIRECTORY
              include/
              "${PROJECT_BINARY_DIR}/export/"
              DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
              COMPONENT stamen_Development
          )
          install(
              TARGETS stamen_stamen
              EXPORT stamenTargets
              RUNTIME #
              COMPONENT stamen_Runtime
              LIBRARY #
              COMPONENT stamen_Runtime
              NAMELINK_COMPONENT stamen_Development
              ARCHIVE #
              COMPONENT stamen_Development
              INCLUDES #
              DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
          )
          write_basic_package_version_file(
              "${package}ConfigVersion.cmake"
              COMPATIBILITY SameMajorVersion
          )
          # Allow package maintainers to freely override the path for the configs
          set(
              stamen_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${package}"
              CACHE STRING "CMake package config location relative to the install prefix"
          )
          set_property(CACHE stamen_INSTALL_CMAKEDIR PROPERTY TYPE PATH)
          mark_as_advanced(stamen_INSTALL_CMAKEDIR)
          install(
              FILES cmake/install-config.cmake
              DESTINATION "${stamen_INSTALL_CMAKEDIR}"
              RENAME "${package}Config.cmake"
              COMPONENT stamen_Development
          )
          install(
              FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake"
              DESTINATION "${stamen_INSTALL_CMAKEDIR}"
              COMPONENT stamen_Development
          )
          install(
              EXPORT stamenTargets
              NAMESPACE stamen::
              DESTINATION "${stamen_INSTALL_CMAKEDIR}"
              COMPONENT stamen_Development
          )
          install(
          	TARGETS stamen_exe
              RUNTIME COMPONENT stamen_Runtime
          )
          if(PROJECT_IS_TOP_LEVEL)
            include(CPack)
          endif()
        
        diff --git a/ cmake/lint-targets.cmake b/ cmake/lint-targets.cmake
@@ -0,0 +1,33 @@
set(
              FORMAT_PATTERNS
              source/*.cpp source/*.hpp
              include/*.hpp
              test/*.cpp test/*.hpp
              CACHE STRING
              "; separated patterns relative to the project source dir to format"
          )
          set(FORMAT_COMMAND clang-format CACHE STRING "Formatter to use")
          add_custom_target(
              format-check
              COMMAND "${CMAKE_COMMAND}"
              -D "FORMAT_COMMAND=${FORMAT_COMMAND}"
              -D "PATTERNS=${FORMAT_PATTERNS}"
              -P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake"
              WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
              COMMENT "Linting the code"
              VERBATIM
          )
          add_custom_target(
              format-fix
              COMMAND "${CMAKE_COMMAND}"
              -D "FORMAT_COMMAND=${FORMAT_COMMAND}"
              -D "PATTERNS=${FORMAT_PATTERNS}"
              -D FIX=YES
              -P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake"
              WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
              COMMENT "Fixing the code"
              VERBATIM
          )
        
        diff --git a/ cmake/lint.cmake b/ cmake/lint.cmake
@@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.14)
          macro(default name)
            if(NOT DEFINED "${name}")
              set("${name}" "${ARGN}")
            endif()
          endmacro()
          default(FORMAT_COMMAND clang-format)
          default(
              PATTERNS
              source/*.cpp source/*.hpp
              include/*.hpp
              test/*.cpp test/*.hpp
          )
          default(FIX NO)
          set(flag --output-replacements-xml)
          set(args OUTPUT_VARIABLE output)
          if(FIX)
            set(flag -i)
            set(args "")
          endif()
          file(GLOB_RECURSE files ${PATTERNS})
          set(badly_formatted "")
          set(output "")
          string(LENGTH "${CMAKE_SOURCE_DIR}/" path_prefix_length)
          foreach(file IN LISTS files)
            execute_process(
                COMMAND "${FORMAT_COMMAND}" --style=file "${flag}" "${file}"
                WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
                RESULT_VARIABLE result
                ${args}
            )
            if(NOT result EQUAL "0")
              message(FATAL_ERROR "'${file}': formatter returned with ${result}")
            endif()
            if(NOT FIX AND output MATCHES "\n<replacement offset")
              string(SUBSTRING "${file}" "${path_prefix_length}" -1 relative_file)
              list(APPEND badly_formatted "${relative_file}")
            endif()
            set(output "")
          endforeach()
          if(NOT badly_formatted STREQUAL "")
            list(JOIN badly_formatted "\n" bad_list)
            message("The following files are badly formatted:\n\n${bad_list}\n")
            message(FATAL_ERROR "Run again with FIX=YES to fix these files.")
          endif()
        
        diff --git a/ cmake/prelude.cmake b/ cmake/prelude.cmake
@@ -0,0 +1,10 @@
# ---- In-source guard ----
          if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
            message(
                FATAL_ERROR
                "In-source builds are not supported. "
                "Please read the BUILDING document before trying to build this project. "
                "You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first."
            )
          endif()
        
        diff --git a/ cmake/project-is-top-level.cmake b/ cmake/project-is-top-level.cmake
@@ -0,0 +1,6 @@
# This variable is set by project() in CMake 3.21+
          string(
              COMPARE EQUAL
              "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}"
              PROJECT_IS_TOP_LEVEL
          )
        
        diff --git a/ cmake/spell-targets.cmake b/ cmake/spell-targets.cmake
@@ -0,0 +1,22 @@
set(SPELL_COMMAND codespell CACHE STRING "Spell checker to use")
          add_custom_target(
              spell-check
              COMMAND "${CMAKE_COMMAND}"
              -D "SPELL_COMMAND=${SPELL_COMMAND}"
              -P "${PROJECT_SOURCE_DIR}/cmake/spell.cmake"
              WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
              COMMENT "Checking spelling"
              VERBATIM
          )
          add_custom_target(
              spell-fix
              COMMAND "${CMAKE_COMMAND}"
              -D "SPELL_COMMAND=${SPELL_COMMAND}"
              -D FIX=YES
              -P "${PROJECT_SOURCE_DIR}/cmake/spell.cmake"
              WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
              COMMENT "Fixing spelling errors"
              VERBATIM
          )
        
        diff --git a/ cmake/spell.cmake b/ cmake/spell.cmake
@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.14)
          macro(default name)
            if(NOT DEFINED "${name}")
              set("${name}" "${ARGN}")
            endif()
          endmacro()
          default(SPELL_COMMAND codespell)
          default(FIX NO)
          set(flag "")
          if(FIX)
            set(flag -w)
          endif()
          execute_process(
              COMMAND "${SPELL_COMMAND}" ${flag}
              WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
              RESULT_VARIABLE result
          )
          if(result EQUAL "65")
            message(FATAL_ERROR "Run again with FIX=YES to fix these errors.")
          elseif(result EQUAL "64")
            message(FATAL_ERROR "Spell checker printed the usage info. Bad arguments?")
          elseif(NOT result EQUAL "0")
            message(FATAL_ERROR "Spell checker returned with ${result}")
          endif()
        
        diff --git a/ cmake/variables.cmake b/ cmake/variables.cmake
@@ -0,0 +1,41 @@
# ---- Developer mode ----
          # Developer mode enables targets and code paths in the CMake scripts that are
          # only relevant for the developer(s) of stamen
          # Targets necessary to build the project must be provided unconditionally, so
          # consumers can trivially build and package the project
          if(PROJECT_IS_TOP_LEVEL)
            option(stamen_DEVELOPER_MODE "Enable developer mode" OFF)
            option(BUILD_SHARED_LIBS "Build shared libs." OFF)
          endif()
          # ---- Suppress C4251 on Windows ----
          # Please see include/stamen/stamen.hpp for more details
          set(pragma_suppress_c4251 "
          /* This needs to suppress only for MSVC */
          #if defined(_MSC_VER) && !defined(__ICL)
          #  define STAMEN_SUPPRESS_C4251 _Pragma(\"warning(suppress:4251)\")
          #else
          #  define STAMEN_SUPPRESS_C4251
          #endif
          ")
          # ---- Warning guard ----
          # target_include_directories with the SYSTEM modifier will request the compiler
          # to omit warnings from the provided paths, if the compiler supports that
          # This is to provide a user experience similar to find_package when
          # add_subdirectory or FetchContent is used to consume this project
          set(warning_guard "")
          if(NOT PROJECT_IS_TOP_LEVEL)
            option(
                stamen_INCLUDES_WITH_SYSTEM
                "Use SYSTEM modifier for stamen's includes, disabling warnings"
                ON
            )
            mark_as_advanced(stamen_INCLUDES_WITH_SYSTEM)
            if(stamen_INCLUDES_WITH_SYSTEM)
              set(warning_guard SYSTEM)
            endif()
          endif()
        
        diff --git a/ demo/CMakeLists.txt b/ demo/CMakeLists.txt
@@ -1,51 +0,0 @@
set(GENERATE_OUT "${CMAKE_BINARY_DIR}/bin")
          configure_file(demo_menu.conf ${GENERATE_OUT}/demo_menu.conf COPYONLY)
          add_custom_command(
              OUTPUT ${GENERATE_OUT}/demo_menu.hpp ${GENERATE_OUT}/demo_menu.cpp
              COMMAND ${GENERATE_OUT}/stamen-generate --user -d test_display --cpp ${GENERATE_OUT}/demo_menu.conf
              DEPENDS demo_menu.conf stamen-generate
              COMMENT "Generating menu files"
          )
          add_executable(demo main.cpp ${GENERATE_OUT}/demo_menu.cpp)
          # target_link_libraries(demo stamen) - no need to link
          target_include_directories(demo PRIVATE ../include)
          set_target_properties(demo PROPERTIES LINKER_LANGUAGE CXX)
          target_include_directories(demo PRIVATE ${GENERATE_OUT} ${CMAKE_CURRENT_SOURCE_DIR})
          set_target_properties(demo PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              RUNTIME_OUTPUT_DIRECTORY "${GENERATE_OUT}"
          )
          add_custom_command(
              OUTPUT ${GENERATE_OUT}/demo_menu.h ${GENERATE_OUT}/demo_menu.c
              COMMAND ${GENERATE_OUT}/stamen-generate --user --c ${GENERATE_OUT}/demo_menu.conf
              DEPENDS demo_menu.conf stamen-generate
              COMMENT "Generating cmenu files"
          )
          add_executable(cdemo main.c ${GENERATE_OUT}/demo_menu.c)
          target_link_libraries(cdemo stamen)
          set_target_properties(cdemo PROPERTIES LINKER_LANGUAGE C)
          target_include_directories(cdemo PRIVATE ${GENERATE_OUT} ${CMAKE_CURRENT_SOURCE_DIR})
          set_target_properties(cdemo PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              RUNTIME_OUTPUT_DIRECTORY "${GENERATE_OUT}"
          )
          add_executable(dynamic dynamic.cpp)
          target_link_libraries(dynamic stamen)
          target_include_directories(dynamic PRIVATE ../include)
          set_target_properties(dynamic PROPERTIES LINKER_LANGUAGE CXX)
          set_target_properties(dynamic PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
          )
        
        diff --git a/ demo/demo_menu.conf b/ demo/demo_menu.conf
@@ -1,28 +0,0 @@
+ menu_main  Main Menu
          - submenu_1  Enter Submenu 1
          - submenu_2  Enter Submenu 2
          - finish Quit
          + submenu_1 Submenu 1
          - submenu_3   Enter Submenu 3
          - operation1  Operation 1
          - operation2  Operation 2
          - operation3  Operation 3
          + submenu_2 Submenu 2
          - submenu_3   Enter Submenu 3
          - operation1  Operation 1
          - operation2  Operation 2
          - operation3  Operation 3
          + submenu_3   Submenu 3
          - operation1  Operation 1
          - operation2  Operation 2
          - operation3  Operation 3
        
        diff --git a/ demo/dynamic.cpp b/ demo/dynamic.cpp
@@ -1,35 +0,0 @@
#include <iostream>
          #include "menu.hpp"
          int finish(int) { exit(1); }
          int operation1(int) {
              std::cout << "1" << std::endl;
              return 1;
          }
          int operation2(int) {
              std::cout << "2" << std::endl;
              return 1;
          }
          int operation3(int) {
              std::cout << "3" << std::endl;
              return 1;
          }
          int main() {
              // read the configuration
              stamen::menu::read("./bin/demo_menu.conf");
              // register free functions
              stamen::menu::insert("finish", finish);
              stamen::menu::insert("operation1", operation1);
              stamen::menu::insert("operation2", operation2);
              stamen::menu::insert("operation3", operation3);
              // start the menu on specific panel
              stamen::menu::dynamic("menu_main", stamen::builtin_display);
              return 0;
          }
        
        diff --git a/ demo/main.c b/ demo/main.c
@@ -1,33 +0,0 @@
#include "demo_menu.h"
          #include "stamen.h"
          #include <stdio.h>
          #include <stdlib.h>
          int operation1(void) {
              printf("operation 1\n");
              printf("Some operation is done\n");
              return 1;
          }
          int operation2(void) {
              printf("operation 2\n");
              printf("Some other operation is done\n");
              return 1;
          }
          int operation3(void) {
              printf("operation 3\n");
              printf("Yet another operation is done\n");
              return 1;
          }
          int finish(void) {
              printf("finishing...\n");
              exit(0);
          }
          int main(void) {
              menu_main(0);
              return 0;
          }
        
        diff --git a/ demo/main.cpp b/ demo/main.cpp
@@ -1,41 +0,0 @@
#include "demo_menu.hpp"
          #include "stamen.hpp"
          #include <iostream>
          int test_display(const char *title, const stamen::item_t itemv[], int size) {
              for (auto i = 0ul; i < size; i++) {
                  std::cout << i + 1 << ": " << itemv[i].prompt << '\n';
              }
              std::cout << "Auto calling option 1...\n";
              itemv[1].callback(1);
              return 0;
          }
          int operation1(int) {
              std::cout << "operation 1" << std::endl;
              std::cout << "Some operation is done" << std::endl;
              return 1;
          }
          int operation2(int) {
              std::cout << "operation 2" << std::endl;
              std::cout << "Some other operation is done" << std::endl;
              return 1;
          }
          int operation3(int) {
              std::cout << "operation 3" << std::endl;
              std::cout << "Yet another operation is done" << std::endl;
              return 1;
          }
          int finish(int) {
              std::cout << "finishing..." << std::endl;
              exit(0);
          }
          int main() {
              menu_main(0);
              return 0;
          }
        
        diff --git a/ demo/shared.h b/ demo/shared.h
@@ -1,9 +0,0 @@
#ifndef STAMEN_DEMO_SHARED_H
          #define STAMEN_DEMO_SHARED_H
          int finish(int);
          int operation1(int);
          int operation2(int);
          int operation3(int);
          #endif
        
        diff --git a/ example/CMakeLists.txt b/ example/CMakeLists.txt
@@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.14)
          project(stamenExamples CXX)
          include(../cmake/project-is-top-level.cmake)
          include(../cmake/folders.cmake)
          if(PROJECT_IS_TOP_LEVEL)
            find_package(stamen REQUIRED)
          endif()
          configure_file(demo_menu.conf demo_menu.conf COPYONLY)
          configure_file(shared.h shared.h COPYONLY)
          add_custom_target(run-examples)
          add_custom_command(
              OUTPUT demo_menu.hpp demo_menu.cpp
              COMMAND stamen::exe -d test_display --cpp demo_menu.conf
              DEPENDS demo_menu.conf stamen::exe
          	WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
              COMMENT "Generating menu files"
          )
          add_custom_command(
              OUTPUT demo_menu.h demo_menu.c
              COMMAND stamen::exe --c demo_menu.conf
              DEPENDS demo_menu.conf stamen::exe
          	WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
              COMMENT "Generating cmenu files"
          )
          function(add_example NAME EXT)
            add_executable("${NAME}" "${NAME}.${EXT}")
            target_include_directories("${NAME}" PRIVATE "${PROJECT_BINARY_DIR}")
            target_link_libraries("${NAME}" PRIVATE stamen::stamen)
            target_compile_features("${NAME}" PRIVATE cxx_std_20)
            add_custom_target("run_${NAME}" COMMAND "${NAME}" demo_menu.conf VERBATIM)
            add_dependencies("run_${NAME}" "${NAME}")
            add_dependencies(run-examples "run_${NAME}")
          endfunction()
          add_example(example_c c)
          target_sources(example_c PRIVATE "demo_menu.c")
          add_example(example_cpp cpp)
          target_sources(example_cpp PRIVATE "demo_menu.cpp")
          add_example(dynamic cpp)
          add_folders(Example)
        
        diff --git a/ example/demo_menu.conf b/ example/demo_menu.conf
@@ -0,0 +1,28 @@
+ menu_main  Main Menu
          - submenu_1  Enter Submenu 1
          - submenu_2  Enter Submenu 2
          - finish Quit
          + submenu_1 Submenu 1
          - submenu_3   Enter Submenu 3
          - operation1  Operation 1
          - operation2  Operation 2
          - operation3  Operation 3
          + submenu_2 Submenu 2
          - submenu_3   Enter Submenu 3
          - operation1  Operation 1
          - operation2  Operation 2
          - operation3  Operation 3
          + submenu_3   Submenu 3
          - operation1  Operation 1
          - operation2  Operation 2
          - operation3  Operation 3
        
        diff --git a/ example/dynamic.cpp b/ example/dynamic.cpp
@@ -0,0 +1,46 @@
#include <iostream>
          #include <span>
          #include "stamen/menu.hpp"
          int finish(size_t /* unused */)
          {
            exit(0);
          }
          int operation1(size_t /* unused */)
          {
            std::cout << "1" << std::endl;
            return 1;
          }
          int operation2(size_t /* unused */)
          {
            std::cout << "2" << std::endl;
            return 1;
          }
          int operation3(size_t /* unused */)
          {
            std::cout << "3" << std::endl;
            return 1;
          }
          int main(int argc, char* argv[])
          {
            const std::span args(argv, argv + argc);
            // read the configuration
            for (const auto& arg : args.subspan(1)) stamen::menu::read(arg);
            // register free functions
            stamen::menu::insert("finish", finish);
            stamen::menu::insert("operation1", operation1);
            stamen::menu::insert("operation2", operation2);
            stamen::menu::insert("operation3", operation3);
            // start the menu on specific panel
            stamen::menu::dynamic("menu_main", stamen::builtin_display);
            return 0;
          }
        
        diff --git a/ example/example_c.c b/ example/example_c.c
@@ -0,0 +1,38 @@
#include <stdio.h>
          #include <stdlib.h>
          #include "demo_menu.h"
          #include "stamen/stamen.h"
          int operation1(size_t /* unsused */)
          {
            printf("operation 1\n");
            printf("Some operation is done\n");
            return 1;
          }
          int operation2(size_t /* unsused */)
          {
            printf("operation 2\n");
            printf("Some other operation is done\n");
            return 1;
          }
          int operation3(size_t /* unsused */)
          {
            printf("operation 3\n");
            printf("Yet another operation is done\n");
            return 1;
          }
          int finish(size_t /* unsused */)
          {
            printf("finishing...\n");
            exit(0);
          }
          int main(void)
          {
            menu_main(0);
            return 0;
          }
        
        diff --git a/ example/example_cpp.cpp b/ example/example_cpp.cpp
@@ -0,0 +1,52 @@
#include <iostream>
          #include <span>
          #include "demo_menu.hpp"
          #include "stamen/stamen.hpp"
          int test_display(const char* title, const stamen::item_t itemv[], size_t size)
          {
            const auto items = std::span(itemv, itemv + size);
            std::cout << title << std::endl;
            for (auto i = 0UL; i < size; i++)
            {
              std::cout << i + 1 << ": " << items[i].prompt << '\n';
            }
            std::cout << "Auto calling option 1...\n";
            items[1].callback(1);
            return 0;
          }
          int operation1(size_t /* unused */)
          {
            std::cout << "operation 1" << std::endl;
            std::cout << "Some operation is done" << std::endl;
            return 1;
          }
          int operation2(size_t /* unused */)
          {
            std::cout << "operation 2" << std::endl;
            std::cout << "Some other operation is done" << std::endl;
            return 1;
          }
          int operation3(size_t /* unused */)
          {
            std::cout << "operation 3" << std::endl;
            std::cout << "Yet another operation is done" << std::endl;
            return 1;
          }
          int finish(size_t /* unused */)
          {
            std::cout << "finishing..." << std::endl;
            exit(0);
          }
          int main()
          {
            menu_main(0);
            return 0;
          }
        
        diff --git a/ example/shared.h b/ example/shared.h
@@ -0,0 +1,11 @@
#ifndef STAMEN_DEMO_SHARED_H
          #define STAMEN_DEMO_SHARED_H
          #include <stddef.h> // NOLINT
          int finish(size_t);
          int operation1(size_t);
          int operation2(size_t);
          int operation3(size_t);
          #endif
        
        diff --git a/ include/menu.h b/ include/menu.h
@@ -1,20 +0,0 @@
#ifndef STAMEN_MENU_H
          #define STAMEN_MENU_H
          #include "stamen.h"
          #ifdef __cplusplus
          extern "C" {
          namespace stamen {
          #endif
          void stamen_menu_read(const char *filename);
          void stamen_menu_insert(const char *code, stamen_callback_f callback);
          int stamen_menu_dynamic(const char *code, stamen_display_f display);
          #ifdef __cplusplus
          } // namespace stamen
          } // extern "C"
          #endif
          #endif
        
        diff --git a/ include/menu.hpp b/ include/menu.hpp
@@ -1,77 +0,0 @@
#ifndef STAMEN_MENU_HPP
          #define STAMEN_MENU_HPP
          #include "stamen.hpp"
          #include <cstring>
          #include <iostream>
          #include <string>
          #include <unordered_map>
          #include <vector>
          namespace stamen {
          namespace menu {
          class menu_t;
          extern std::unordered_map<std::string, callback_f> free_lookup;
          extern std::unordered_map<std::string, menu_t> menu_lookup;
          extern std::string display_stub_default;
          extern display_f display;
          void read(const char *filename);
          void insert(const char *code, callback_f callback);
          int dynamic(const char *code, display_f display);
          int display_stub(int idx);
          class menu_t {
              struct private_ctor_t {};
              friend void read(const char *filename);
            public:
              // Tag type dispatch
              menu_t(private_ctor_t, const std::string &code, const std::string &prompt)
                  : menu_t(code, prompt) {}
              ~menu_t() noexcept {
                  for (const auto [_, prompt] : items) {
                      delete[] prompt;
                  }
              }
              const std::string &getCode() const { return code; }
              const std::string &getTitle() const { return title; }
              const item_t *getItemv() const { return items.data(); }
              std::size_t getSize() const { return items.size(); }
              auto getCallback(std::size_t idx) const { return items[idx].callback; }
              const auto &getCode(std::size_t idx) const { return codes[idx].code; }
              const auto &getPrompt(std::size_t idx) const { return codes[idx].prompt; }
            private:
              menu_t(std::string code, std::string prompt)
                  : code(std::move(code)), title(std::move(prompt)) {}
              menu_t(const menu_t &) = delete;
              menu_t &operator=(const menu_t &) = delete;
              menu_t(menu_t &&) = delete;
              menu_t &operator=(menu_t &&) = delete;
              void insert(const std::string &code, const std::string &prompt,
                          callback_f callback = display_stub);
              struct code_t {
                  std::string code;
                  std::string prompt;
              };
              const std::string code, title;
              std::vector<code_t> codes;
              std::vector<item_t> items;
          };
          } // namespace menu
          } // namespace stamen
          #endif
        
        diff --git a/ include/stamen.h b/ include/stamen.h
@@ -1,31 +0,0 @@
#ifndef STAMEN_STAMEN_H
          #define STAMEN_STAMEN_H
          #ifdef __cplusplus
          extern "C" {
          namespace stamen {
          #endif
          typedef int (*stamen_callback_f)(int);
          typedef struct stamen_item_t stamen_item_t;
          struct stamen_item_t {
              stamen_callback_f callback;
              const char *prompt;
          };
          typedef int (*stamen_display_f)(const char *, const stamen_item_t[], int);
          #if !defined __cplusplus || defined WITH_C_BINDINGS
          int stamen_builtin_display(const char *title, const stamen_item_t itemv[],
                                     int size);
          #endif
          #ifdef __cplusplus
          } // namespace stamen
          } // extern "C"
          #endif
          #endif
        
        diff --git a/ include/stamen.hpp b/ include/stamen.hpp
@@ -1,16 +0,0 @@
#ifndef STAMEN_STAMEN_HPP
          #define STAMEN_STAMEN_HPP
          #include "stamen.h"
          namespace stamen {
          using callback_f = stamen_callback_f;
          using display_f = stamen_display_f;
          using item_t = stamen_item_t;
          int builtin_display(const char *title, const item_t itemv[], int size);
          } // namespace stamen
          #endif
        
        diff --git a/ include/stamen/menu.h b/ include/stamen/menu.h
@@ -0,0 +1,18 @@
#pragma once
          #include "stamen/stamen.h"
          #ifdef __cplusplus
          extern "C"
          {
          namespace stamen {
          #endif
          void stamen_menu_read(const char* filename);
          void stamen_menu_insert(const char* code, stamen_callback_f callback);
          int stamen_menu_dynamic(const char* code, stamen_display_f display);
          #ifdef __cplusplus
          }  // namespace stamen
          }  // extern "C"
          #endif
        
        diff --git a/ include/stamen/menu.hpp b/ include/stamen/menu.hpp
@@ -0,0 +1,89 @@
#pragma once
          #include <cstring>
          #include <iostream>
          #include <string>
          #include <unordered_map>
          #include <vector>
          #include "stamen/stamen.hpp"
          namespace stamen::menu {
          class menu_t;
          // NOLINTBEGIN
          extern std::unordered_map<std::string, callback_f> free_lookup;
          extern std::unordered_map<std::string, menu_t> menu_lookup;
          extern std::string display_stub_default;
          extern display_f display;
          // NOLINTEND
          void read(const char* filename);
          void insert(const char* code, callback_f callback);
          int dynamic(const char* code, display_f disp);
          int display_stub(std::size_t idx);
          class menu_t
          {
            struct private_ctor_t
            {
            };
            friend void read(const char* filename);
          public:
            // Tag type dispatch
            menu_t(private_ctor_t,  // NOLINT
                   const std::string& code,
                   const std::string& prompt)
                : menu_t(code, prompt)
            {
            }
            menu_t(const menu_t&)            = delete;
            menu_t& operator=(const menu_t&) = delete;
            menu_t(menu_t&&)                 = delete;
            menu_t& operator=(menu_t&&)      = delete;
            ~menu_t() noexcept
            {
              for (const auto [_, prompt] : m_items)
              {
                delete[] prompt;  // NOLINT
              }
            }
            const std::string& get_code() const { return m_code; }
            const std::string& get_title() const { return m_title; }
            const item_t* get_itemv() const { return m_items.data(); }
            std::size_t get_size() const { return m_items.size(); }
            auto get_callback(std::size_t idx) const { return m_items[idx].callback; }
            const auto& get_code(std::size_t idx) const { return m_codes[idx].code; }
            const auto& get_prompt(std::size_t idx) const { return m_codes[idx].prompt; }
          private:
            menu_t(std::string code, std::string prompt)
                : m_code(std::move(code))
                , m_title(std::move(prompt))
            {
            }
            void insert(const std::string& code,
                        const std::string& prompt,
                        callback_f callback = display_stub);
            struct code_t
            {
              std::string code;
              std::string prompt;
            };
            std::string m_code;
            std::string m_title;
            std::vector<code_t> m_codes;
            std::vector<item_t> m_items;
          };
          }  // namespace stamen::menu
        
        diff --git a/ include/stamen/stamen.h b/ include/stamen/stamen.h
@@ -0,0 +1,35 @@
#pragma once
          #include "stddef.h"  // NOLINT
          #ifdef __cplusplus
          extern "C"
          {
          namespace stamen {
          #endif
          typedef int (*stamen_callback_f)(size_t);  // NOLINT
          typedef struct stamen_item_t stamen_item_t;  // NOLINT
          struct stamen_item_t
          {
            stamen_callback_f callback;
            const char* prompt;
          };
          typedef int (*stamen_display_f)(const char*,  // NOLINT
                                          const stamen_item_t[],
                                          size_t);
          #if !defined __cplusplus || defined WITH_C_BINDINGS
          int stamen_builtin_display(const char* title,
                                     const stamen_item_t itemv[],
                                     size_t size);
          #endif
          #ifdef __cplusplus
          }  // namespace stamen
          }  // extern "C"
          #endif
        
        diff --git a/ include/stamen/stamen.hpp b/ include/stamen/stamen.hpp
@@ -0,0 +1,13 @@
#pragma once
          #include "stamen/stamen.h"
          namespace stamen {
          using callback_f = stamen_callback_f;
          using display_f  = stamen_display_f;
          using item_t     = stamen_item_t;
          int builtin_display(const char* title, const item_t itemv[], size_t size);
          }  // namespace stamen
        
        diff --git a/ source/c_bindings.cpp b/ source/c_bindings.cpp
@@ -0,0 +1,35 @@
#include "stamen/menu.hpp"
          #include "stamen/stamen.hpp"
          extern "C"
          {
          namespace stamen {
          int stamen_builtin_display(const char* title,
                                     const stamen_item_t itemv[],
                                     size_t size)
          {
            return builtin_display(title, itemv, size);
          }
          }  // namespace stamen
          namespace stamen::menu {
          void stamen_menu_read(const char* filename)
          {
            return read(filename);
          }
          void stamen_menu_insert(const char* code, stamen_callback_f callback)
          {
            return insert(code, callback);
          }
          int stamen_menu_dynamic(const char* code, stamen_display_f disp)
          {
            return dynamic(code, disp);
          }
          }  // namespace stamen::menu
          }
        
        diff --git a/ source/generate.cpp b/ source/generate.cpp
@@ -0,0 +1,160 @@
#include <format>
          #include <fstream>
          #include <iostream>
          #include <string>
          #include "poafloc/poafloc.hpp"
          #include "stamen/menu.hpp"
          #include "stamen/stamen.hpp"
          struct arguments_t
          {
            std::string config;
            std::string display;
            std::string header = "shared.h";
            bool cpp           = false;
            bool user          = false;
          };
          void generate_include(std::ostream& ost)
          {
            ost << "#ifndef STAMEN_MENU_H\n";
            ost << "#define STAMEN_MENU_H\n\n";
            for (const auto& [code, menu] : stamen::menu::menu_lookup)
            {
              ost << std::format("int {}(size_t);\n", menu.get_code());
            }
            ost << "\n#endif\n";
          }
          void generate_source(std::ostream& ost, const arguments_t& args)
          {
            ost << std::format("#include \"{}\"\n", args.header);
            if (args.user)
            {
              if (args.cpp) ost << "#include \"stamen.hpp\"\n\n";
              else ost << "#include \"stamen.h\"\n\n";
            }
            else
            {
              if (args.cpp) ost << "#include <stamen/stamen.hpp>\n\n";
              else ost << "#include <stamen/stamen.h>\n\n";
            }
            ost << std::format("extern int {}(const char *title, ", args.display);
            if (args.cpp) ost << "const stamen::item_t itemv[], size_t size);\n\n";
            else ost << "const stamen_item_t itemv[], size_t size);\n\n";
            for (const auto& [code, menu] : stamen::menu::menu_lookup)
            {
              ost << std::format("int {}(size_t /* unused */) {{\n", menu.get_code());
              if (args.cpp) ost << "\tstatic const stamen::item_t items[] = ";
              else ost << "\tstatic const stamen_item_t items[] = ";
              ost << "{\n";
              for (auto i = 0UL; i < menu.get_size(); i++)
              {
                ost << "\t\t{ " << menu.get_code(i);
                ost << ", \"" << menu.get_prompt(i) << "\" },\n";
              }
              ost << "\t};\n";
              ost << std::format("\treturn {}(\"{}\"", args.display, menu.get_title());
              ost << ", items, sizeof(items) / sizeof(items[0]));\n";
              ost << "}\n\n";
            }
          }
          int parse_opt(int key, const char* arg, poafloc::Parser* parser)
          {
            auto* arguments = static_cast<arguments_t*>(parser->input());
            switch (key)
            {
              case 'd':
                arguments->display = arg;
                break;
              case 'h':
                arguments->header = arg;
                break;
              case 'u':
                arguments->user = true;
                break;
              case 666:
                arguments->cpp = false;
                break;
              case 777:
                arguments->cpp = true;
                break;
              case poafloc::ARG:
                if (!arguments->config.empty())
                {
                  poafloc::failure(parser, 0, 0, "Too many arguments");
                  poafloc::help(parser, stderr, poafloc::STD_USAGE);
                }
                arguments->config = arg;
                break;
              case poafloc::NO_ARGS:
                poafloc::failure(parser, 0, 0, "Missing an argument");
                poafloc::help(parser, stderr, poafloc::STD_USAGE);
                break;
              case poafloc::END:
                if (arguments->display.empty())
                {
                  if (arguments->cpp) arguments->display = "stamen::builtin_display";
                  else arguments->display = "stamen_builtin_display";
                }
                break;
              default:
                break;
            }
            return 0;
          }
          static const poafloc::option_t options[] {
              {nullptr, 0, nullptr, 0, "Output mode", 1},
              {"c", 666, nullptr, 0, "Generate files for C"},
              {"cpp", 777, nullptr, 0, "Generate files for C++"},
              {nullptr, 0, nullptr, 0, "Output settings", 2},
              {"display", 'd', "FUNC", 0, "Set display function to be called"},
              {"user", 'u', nullptr, 0, "Include user stamen headers"},
              {"header", 'h', "HDR", 0, "Header with free functions, default: shared.h"},
              {nullptr, 0, nullptr, 0, "Informational Options", -1},
              {nullptr},
          };
          static const poafloc::arg_t arg {
              options,
              parse_opt,
              "config_file",
              "",
          };
          int main(int argc, char* argv[])
          {
            arguments_t args;
            if (poafloc::parse(&arg, argc, argv, 0, &args) != 0)
            {
              std::cerr << "There was an error while parsing arguments";
              return 1;
            }
            const auto& config = args.config;
            stamen::menu::read(config.c_str());
            const std::string::size_type pos = args.config.rfind('.');
            const std::string ext            = args.cpp ? "pp" : "";
            const std::string base =
                pos != std::string::npos ? config.substr(0, pos) : config;
            std::ofstream include(base + ".h" + ext);
            generate_include(include);
            std::ofstream source(base + ".c" + ext);
            generate_source(source, args);
            return 0;
          }
        
        diff --git a/ source/menu.cpp b/ source/menu.cpp
@@ -0,0 +1,99 @@
#include <deque>
          #include <format>
          #include <fstream>
          #include <iostream>
          #include <sstream>
          #include <tuple>
          #include <utility>
          #include "stamen/menu.hpp"
          namespace stamen::menu {
          // NOLINTBEGIN
          std::unordered_map<std::string, menu_t> menu_lookup;
          std::unordered_map<std::string, callback_f> free_lookup;
          std::string display_stub_default;
          display_f display;
          // NOLINTEND
          void read(const char* filename)
          {
            std::fstream fst(filename);
            std::string line;
            std::string delim;
            std::string code;
            std::string prompt;
            auto last = menu_lookup.end();
            while (std::getline(fst, line))
            {
              if (line.empty()) continue;
              std::istringstream iss(line);
              iss >> delim >> code >> std::ws;
              std::getline(iss, prompt);
              if (delim != "+") last->second.insert(code, prompt);
              else
              {
                const auto [iter, succ] = menu_lookup.emplace(
                    std::piecewise_construct,
                    std::forward_as_tuple(code),
                    std::forward_as_tuple(menu_t::private_ctor_t {}, code, prompt));
                last = iter;
              }
            }
          }
          void insert(const char* code, callback_f callback)
          {
            free_lookup.emplace(code, callback);
          }
          int dynamic(const char* code, display_f disp)
          {
            menu::display_stub_default = code;
            menu::display              = disp;
            return display_stub(0);
          }
          int display_stub(std::size_t idx)
          {
            static std::deque<const menu_t*> stack;
            const std::string& code =
                !stack.empty() ? stack.back()->get_code(idx) : display_stub_default;
            const auto ml_it = menu_lookup.find(code);
            if (ml_it != menu_lookup.end())
            {
              const auto& m = ml_it->second;  // NOLINT
              stack.push_back(&m);
              const int ret =
                  display(m.get_title().c_str(), m.get_itemv(), m.get_size());
              stack.pop_back();
              return ret;
            }
            const auto fl_it = free_lookup.find(code);
            if (fl_it != free_lookup.end()) return fl_it->second(0);
            std::cout << "Stamen: nothing to do..." << std::endl;
            return 1;
          }
          void menu_t::insert(const std::string& code,
                              const std::string& prompt,
                              callback_f callback)
          {
            char* buffer = new char[prompt.size() + 1];  // NOLINT
            strcpy(buffer, prompt.c_str()); // NOLINT
            m_items.emplace_back(callback, buffer);
            m_codes.emplace_back(code, prompt);
          }
          }  // namespace stamen::menu
        
        diff --git a/ source/stamen.cpp b/ source/stamen.cpp
@@ -0,0 +1,61 @@
#include <cmath>
          #include <format>
          #include <iostream>
          #include "stamen/stamen.hpp"
          #include "stamen/menu.h"
          namespace stamen {
          int builtin_display(const char* title, const item_t itemv[], size_t size)
          {
            const auto items  = std::span(itemv, size);
            const size_t dgts = static_cast<size_t>(std::log10(size)) + 1;
            int choice        = 0;
            while (true)
            {
              std::cout << std::format("{}:\n", title);
              for (auto i = 0UL; i < size; i++)
              {
                std::cout << std::format(" {:{}}. {}\n", i, dgts, items[i].prompt);
              }
              while (true)
              {
                std::cout << "Choose an option: ";
                if (std::cin >> choice && choice >= -1
                    && choice < static_cast<int>(size))
                {
                  if (choice == -1)
                  {
                    std::cout << "Choice: back\n";
                    return 1;
                  }
                  const auto uchoice = static_cast<size_t>(choice);
                  std::cout << "Choice: " << items[uchoice].prompt << "\n\n";
                  const int res = items[uchoice].callback(uchoice);
                  if (res < 2) break;
                  return res - 1;
                }
                if (std::cin.eof())
                {
                  std::cerr << "encountered end of input!\n";
                  return std::numeric_limits<int>::max();
                }
                std::cin.clear();
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                std::cout << "Invalid option, please choose again!\n";
              }
              std::cout << std::endl;
            }
            return 1;
          }
          }  // namespace stamen
        
        diff --git a/ src/CMakeLists.txt b/ src/CMakeLists.txt
@@ -1,23 +0,0 @@
add_library(stamen STATIC stamen.cpp menu.cpp c_bindings.cpp)
          target_include_directories(stamen PRIVATE ../include)
          target_compile_definitions(stamen PRIVATE WITH_C_BINDINGS)
          set_target_properties(stamen PROPERTIES LINKER_LANGUAGE CXX)
          set_target_properties(stamen PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              DEBUG_POSTFIX "d"
              PUBLIC_HEADER "include/stamen.h;include/stamen.hpp;include/menu.h;include/menu.hpp"
              MACOSX_RPATH ON
              WINDOWS_EXPORT_ALL_SYMBOLS ON
          )
          add_executable(stamen-generate generate.cpp)
          target_link_libraries(stamen-generate PRIVATE poafloc stamen)
          target_include_directories(stamen-generate PRIVATE ../include)
          set_target_properties(stamen-generate PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
          )
        
        diff --git a/ src/c_bindings.cpp b/ src/c_bindings.cpp
@@ -1,23 +0,0 @@
#include "menu.hpp"
          #include "stamen.hpp"
          namespace stamen {
          int stamen_builtin_display(const char *title, const stamen_item_t itemv[], int size) {
              return builtin_display(title, itemv, size);
          }
          namespace menu {
          void stamen_menu_read(const char *filename) { return read(filename); }
          void stamen_menu_insert(const char *code, stamen_callback_f callback) {
              return insert(code, callback);
          }
          int stamen_menu_dynamic(const char *code, stamen_display_f display) {
              return dynamic(code, display);
          }
          } // namespace menu
          } // namespace stamen
        
        diff --git a/ src/generate.cpp b/ src/generate.cpp
@@ -1,132 +0,0 @@
#include <poafloc/poafloc.hpp>
          #include "menu.hpp"
          #include "stamen.hpp"
          #include <format>
          #include <fstream>
          #include <iostream>
          #include <string>
          struct arguments_t {
              std::string config;
              std::string display;
              std::string header = "shared.h";
              bool cpp = false;
              bool user = false;
          } opt;
          void generateIncludeHeaders(std::ostream &os) {}
          void generateInclude(std::ostream &os) {
              os << "#ifndef STAMEN_MENU_H\n";
              os << "#define STAMEN_MENU_H\n\n";
              for (const auto &[code, menu] : stamen::menu::menu_lookup) {
                  os << std::format("int {}(int);\n", menu.getCode());
              }
              os << "\n#endif\n";
          }
          void generateSource(std::ostream &os) {
              os << std::format("#include \"{}\"\n", opt.header);
              if (opt.user) {
                  if (opt.cpp) os << "#include \"stamen.hpp\"\n\n";
                  else os << "#include \"stamen.h\"\n\n";
              } else {
                  if (opt.cpp) os << "#include <stamen/stamen.hpp>\n\n";
                  else os << "#include <stamen/stamen.h>\n\n";
              }
              os << std::format("extern int {}(const char *title, ", opt.display);
              if (opt.cpp) os << "const stamen::item_t itemv[], int size);\n\n";
              else os << "const stamen_item_t itemv[], int size);\n\n";
              for (const auto &[code, menu] : stamen::menu::menu_lookup) {
                  os << std::format("int {}(int) {{\n", menu.getCode());
                  if (opt.cpp) os << "\tstatic const stamen::item_t items[] = ";
                  else os << "\tstatic const stamen_item_t items[] = ";
                  os << "{\n";
                  for (int i = 0; i < menu.getSize(); i++) {
                      os << "\t\t{ " << menu.getCode(i);
                      os << ", \"" << menu.getPrompt(i) << "\" },\n";
                  }
                  os << "\t};\n";
                  os << std::format("\treturn {}(\"{}\"", opt.display, menu.getTitle());
                  os << ", items, sizeof(items) / sizeof(items[0]));\n";
                  os << "}\n\n";
              }
          }
          int parse_opt(int key, const char *arg, poafloc::Parser *parser) {
              auto arguments = (arguments_t *)parser->input();
              switch (key) {
              case 'd': arguments->display = arg; break;
              case 'h': arguments->header = arg; break;
              case 'u': arguments->user = true; break;
              case 666: arguments->cpp = false; break;
              case 777: arguments->cpp = true; break;
              case poafloc::ARG:
                  if (!arguments->config.empty()) {
                      poafloc::failure(parser, 0, 0, "Too many arguments");
                      poafloc::help(parser, stderr, poafloc::STD_USAGE);
                  }
                  arguments->config = arg;
                  break;
              case poafloc::NO_ARGS:
                  poafloc::failure(parser, 0, 0, "Missing an argument");
                  poafloc::help(parser, stderr, poafloc::STD_USAGE);
              case poafloc::END:
                  if (arguments->display.empty()) {
                      if (arguments->cpp) arguments->display = "stamen::builtin_display";
                      else arguments->display = "stamen_builtin_display";
                  }
                  break;
              }
              return 0;
          }
          static const poafloc::option_t options[]{
              {0, 0, 0, 0, "Output mode", 1},
              {"c", 666, 0, 0, "Generate files for C"},
              {"cpp", 777, 0, 0, "Generate files for C++"},
              {0, 0, 0, 0, "Output settings", 2},
              {"display", 'd', "FUNC", 0, "Set display function to be called"},
              {"user", 'u', 0, 0, "Include user stamen headers"},
              {"header", 'h', "NAME", 0,
               "Header with free functions, default: shared.h"},
              {0, 0, 0, 0, "Informational Options", -1},
              {0},
          };
          static const poafloc::arg_t arg{
              options,
              parse_opt,
              "config_file",
              "",
          };
          int main(int argc, char *argv[]) {
              if (poafloc::parse(&arg, argc, argv, 0, &opt)) {
                  std::cerr << "There was an error while parsing arguments";
                  return 0;
              }
              const auto &config = opt.config;
              stamen::menu::read(config.c_str());
              std::string::size_type pos = opt.config.rfind('.');
              std::string base =
                  pos != std::string::npos ? config.substr(0, pos) : config;
              std::string ext = opt.cpp ? "pp" : "";
              std::ofstream source(base + ".c" + ext), include(base + ".h" + ext);
              generateInclude(include);
              generateSource(source);
              return 0;
          }
        
        diff --git a/ src/menu.cpp b/ src/menu.cpp
@@ -1,85 +0,0 @@
#include "menu.hpp"
          #include <deque>
          #include <format>
          #include <fstream>
          #include <iostream>
          #include <sstream>
          #include <tuple>
          #include <utility>
          namespace stamen {
          namespace menu {
          std::unordered_map<std::string, menu_t> menu_lookup;
          std::unordered_map<std::string, callback_f> free_lookup;
          std::string display_stub_default;
          display_f display;
          void read(const char *filename) {
              std::string line, delim, code, prompt;
              std::fstream fs(filename);
              auto last = menu_lookup.end();
              while (std::getline(fs, line)) {
                  if (line.empty()) continue;
                  std::istringstream ss(line);
                  ss >> delim >> code >> std::ws;
                  std::getline(ss, prompt);
                  if (delim != "+") last->second.insert(code, prompt);
                  else {
                      const auto [iter, succ] = menu_lookup.emplace(
                          std::piecewise_construct, std::forward_as_tuple(code),
                          std::forward_as_tuple(menu_t::private_ctor_t{}, code, prompt));
                      last = iter;
                  }
              }
          }
          void insert(const char *code, callback_f callback) {
              free_lookup.emplace(code, callback);
          }
          int dynamic(const char *code, display_f display) {
              menu::display_stub_default = code;
              menu::display = display;
              return display_stub(-1);
          }
          int display_stub(int idx) {
              static std::deque<const menu_t *> st;
              const std::string &code =
                  !st.empty() ? st.back()->getCode(idx) : display_stub_default;
              const auto ml_it = menu_lookup.find(code);
              if (ml_it != menu_lookup.end()) {
                  const auto &m = ml_it->second;
                  st.push_back(&m);
                  int ret = display(m.getTitle().c_str(), m.getItemv(), m.getSize());
                  st.pop_back();
                  return ret;
              }
              const auto fl_it = free_lookup.find(code);
              if (fl_it != free_lookup.end()) return fl_it->second(0);
              std::cout << "Stamen: nothing to do..." << std::endl;
              return 1;
          }
          void menu_t::insert(const std::string &code, const std::string &prompt,
                              callback_f callback) {
              char *buffer = new char[prompt.size() + 1];
              strcpy(buffer, prompt.c_str());
              items.emplace_back(callback, buffer);
              codes.emplace_back(code, prompt);
          }
          } // namespace menu
          } // namespace stamen
        
        diff --git a/ src/stamen.cpp b/ src/stamen.cpp
@@ -1,49 +0,0 @@
#include "stamen.hpp"
          #include "menu.h"
          #include <cmath>
          #include <format>
          #include <iostream>
          namespace stamen {
          int builtin_display(const char *title, const item_t itemv[], int size) {
              const auto items = std::span(itemv, size_t(size));
              const size_t dgts = size_t(std::log10(size)) + 1;
              int choice = 0;
              while (true) {
                  std::cout << std::format("{}:\n", title);
                  for (auto i = 0ul; i < size; i++) {
                      std::cout << std::format(" {:{}}. {}\n", i, dgts, items[i].prompt);
                  }
                  while (true) {
                      std::cout << "Choose an option: ";
                      if (std::cin >> choice && choice >= -1 && choice < (int)size) {
                          if (choice == -1) {
                              std::cout << "Choice: back\n";
                              return 1;
                          }
                          std::cout << "Choice: " << items[choice].prompt << "\n\n";
                          const int res = items[choice].callback(choice);
                          if (res < 2) break;
                          return res - 1;
                      } else if (std::cin.eof()) {
                          std::cerr << "encountered end of input!\n";
                          return std::numeric_limits<int>::max();
                      }
                      std::cin.clear();
                      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                      std::cout << "Invalid option, please choose again!\n";
                  }
                  std::cout << std::endl;
              }
              return 1;
          }
          } // namespace stamen
        
        diff --git a/ stamenConfig.cmake.in b/ stamenConfig.cmake.in
@@ -1,7 +0,0 @@
@PACKAGE_INIT@
          include("${CMAKE_CURRENT_LIST_DIR}/stamenTargets.cmake")
          check_required_components(
              "stamen-generate"
          	"stamen"
          )
        
        diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt
@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.14)
          project(stamenTests LANGUAGES CXX)
          include(../cmake/project-is-top-level.cmake)
          include(../cmake/folders.cmake)
          # ---- Dependencies ----
          if(PROJECT_IS_TOP_LEVEL)
            find_package(stamen REQUIRED)
            enable_testing()
          endif()
          # ---- Tests ----
          add_executable(stamen_test source/stamen_test.cpp)
          target_link_libraries(stamen_test PRIVATE stamen::stamen)
          target_compile_features(stamen_test PRIVATE cxx_std_20)
          add_test(NAME stamen_test COMMAND stamen_test)
          # ---- End-of-file commands ----
          add_folders(Test)
        
        diff --git a/ test/source/stamen_test.cpp b/ test/source/stamen_test.cpp
@@ -0,0 +1,8 @@
#include <string>
          #include "stamen/stamen.hpp"
          int main()
          {
            return 0;
          }