stamenStatic Menu Generator | 
          
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | 472fad60cd717eef6ead0ed88668462e630f78c3 | 
| parent | 45ebb3b798a9cf306ccfce512db8755e22cc472e | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Thu, 13 Jun 2024 20:00:12 +0200 | 
New .clang-format, full reformat
| M | .clang-format | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ---------------------- | 
| M | demo/dynamic.cpp | | | ++++++++++++++++++ ------------------ | 
| M | demo/main.c | | | +++++++++++++ ------------- | 
| M | demo/main.cpp | | | +++++++++++++ ------------- | 
| M | include/menu.h | | | ++++++++++++++++++++++++++++++++++++++++++ ---------------------------------------- | 
| M | include/stamen.h | | | ++ -- | 
| M | src/c_bindings.cpp | | | ++ -- | 
| M | src/generate.cpp | | | +++++++++++++++++++++++++++++++++++++++++ ----------------------------------------- | 
| M | src/menu.cpp | | | +++++++++++++++++++++++++++++++++++++++++ ----------------------------------------- | 
| M | src/stamen.cpp | | | ++++++++++++++++++++++++++++++++++ --------------------------------- | 
10 files changed, 293 insertions(+), 249 deletions(-)
diff --git a/ .clang-format b/ .clang-format
@@ -1,26 +1,45 @@
---
          Language:        Cpp
          # BasedOnStyle:  LLVM
          # BasedOnStyle:  Microsoft
          AccessModifierOffset: -2
          AlignAfterOpenBracket: true
          AlignArrayOfStructures: Right
          AlignConsecutiveMacros: true
          AlignConsecutiveAssignments: None
          AlignConsecutiveBitFields: None
          AlignConsecutiveDeclarations: None
          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
          AlignEscapedNewlines: Right
          AlignOperands:   Align
          AlignTrailingComments: true
          AllowAllArgumentsOnNextLine: true
          AllowAllConstructorInitializersOnNextLine: true
          AllowAllParametersOfDeclarationOnNextLine: true
          AllowShortEnumsOnASingleLine: true
          AllowShortBlocksOnASingleLine: true
          AllowShortEnumsOnASingleLine: false
          AllowShortBlocksOnASingleLine: Always
          AllowShortCaseLabelsOnASingleLine: true
          AllowShortFunctionsOnASingleLine: All
          AllowShortFunctionsOnASingleLine: true
          AllowShortLambdasOnASingleLine: All
          AllowShortIfStatementsOnASingleLine: true
          AllowShortLoopsOnASingleLine: true
          AllowShortIfStatementsOnASingleLine: AllIfsAndElse
          AllowShortLoopsOnASingleLine: false
          AlwaysBreakAfterDefinitionReturnType: None
          AlwaysBreakAfterReturnType: None
          AlwaysBreakBeforeMultilineStrings: false
        
        
          @@ -32,7 +51,7 @@ 
          BinPackParameters: true
        
        
          BraceWrapping:
            AfterCaseLabel:  false
            AfterClass:      false
            AfterControlStatement: Never
            AfterControlStatement: false
            AfterEnum:       false
            AfterFunction:   false
            AfterNamespace:  false
        
        
          @@ -49,8 +68,8 @@ 
          BraceWrapping:
        
        
            SplitEmptyRecord: true
            SplitEmptyNamespace: true
          BreakBeforeBinaryOperators: None
          BreakBeforeConceptDeclarations: true
          BreakBeforeBraces: Attach
          BreakBeforeConceptDeclarations: Always
          BreakBeforeBraces: Custom
          BreakBeforeInheritanceComma: false
          BreakInheritanceList: BeforeColon
          BreakBeforeTernaryOperators: true
        
        
          @@ -58,10 +77,10 @@ 
          BreakConstructorInitializersBeforeComma: false
        
        
          BreakConstructorInitializers: BeforeColon
          BreakAfterJavaFieldAnnotations: false
          BreakStringLiterals: true
          ColumnLimit:     80
          ColumnLimit:     79
          CommentPragmas:  '^ IWYU pragma:'
          QualifierAlignment: Leave
          CompactNamespaces: false
          ConstructorInitializerAllOnOneLineOrOnePerLine: false
          ConstructorInitializerIndentWidth: 4
          ContinuationIndentWidth: 4
          Cpp11BracedListStyle: true
        
        
          @@ -71,6 +90,10 @@ 
          DisableFormat:   false
        
        
          EmptyLineAfterAccessModifier: Never
          EmptyLineBeforeAccessModifier: LogicalBlock
          ExperimentalAutoDetectBinPacking: false
          PackConstructorInitializers: BinPack
          BasedOnStyle:    ''
          ConstructorInitializerAllOnOneLineOrOnePerLine: false
          AllowAllConstructorInitializersOnNextLine: true
          FixNamespaceComments: true
          ForEachMacros:
            - foreach
        
        
          @@ -100,9 +123,10 @@ 
          IndentCaseBlocks: false
        
        
          IndentGotoLabels: true
          IndentPPDirectives: None
          IndentExternBlock: AfterExternBlock
          IndentRequires:  false
          IndentWidth:     2
          IndentRequiresClause: true
          IndentWidth:     4
          IndentWrappedFunctionNames: false
          InsertBraces:    false
          InsertTrailingCommas: None
          JavaScriptQuotes: Leave
          JavaScriptWrapImports: true
        
        
          @@ -121,15 +145,19 @@ 
          PenaltyBreakAssignment: 2
        
        
          PenaltyBreakBeforeFirstCallParameter: 19
          PenaltyBreakComment: 300
          PenaltyBreakFirstLessLess: 120
          PenaltyBreakOpenParenthesis: 0
          PenaltyBreakString: 1000
          PenaltyBreakTemplateDeclaration: 10
          PenaltyExcessCharacter: 1000000
          PenaltyReturnTypeOnItsOwnLine: 60
          PenaltyReturnTypeOnItsOwnLine: 1000
          PenaltyIndentedWhitespace: 0
          PointerAlignment: Right
          PPIndentWidth:   -1
          ReferenceAlignment: Pointer
          ReflowComments:  true
          RemoveBracesLLVM: false
          RequiresClausePosition: OwnLine
          SeparateDefinitionBlocks: Leave
          ShortNamespaceLines: 1
          SortIncludes:    CaseSensitive
          SortJavaStaticImport: Before
        
        
          @@ -143,6 +171,16 @@ 
          SpaceBeforeCpp11BracedList: false
        
        
          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
          SpaceBeforeRangeBasedForLoopColon: true
          SpaceInEmptyBlock: false
        
        
          @@ -165,7 +203,7 @@ 
          StatementAttributeLikeMacros:
        
        
          StatementMacros:
            - Q_UNUSED
            - QT_REQUIRE_VERSION
          TabWidth:        8
          TabWidth:        4
          UseCRLF:         false
          UseTab:          Never
          WhitespaceSensitiveMacros:
        
        diff --git a/ demo/dynamic.cpp b/ demo/dynamic.cpp
          @@ -8,31 +8,31 @@ 
          const stamen::display_f &stamen::display = stamen::builtin_display;
        
        
          int finish(int) { exit(1); }
          int operation1(int) {
            std::cout << "1" << std::endl;
            return 1;
              std::cout << "1" << std::endl;
              return 1;
          }
          int operation2(int) {
            std::cout << "2" << std::endl;
            return 1;
              std::cout << "2" << std::endl;
              return 1;
          }
          int operation3(int) {
            std::cout << "3" << std::endl;
            return 1;
              std::cout << "3" << std::endl;
              return 1;
          }
          int main() {
            // read the configuration
            stamen::read("./bin/demo_menu.conf");
            // register free functions
            stamen::insert("finish", finish);
            stamen::insert("operation1", operation1);
            stamen::insert("operation2", operation2);
            stamen::insert("operation3", operation3);
            // start the menu on specific panel
            stamen::dynamic("menu_main");
            return 0;
              // read the configuration
              stamen::read("./bin/demo_menu.conf");
              // register free functions
              stamen::insert("finish", finish);
              stamen::insert("operation1", operation1);
              stamen::insert("operation2", operation2);
              stamen::insert("operation3", operation3);
              // start the menu on specific panel
              stamen::dynamic("menu_main");
              return 0;
          }
        
        diff --git a/ demo/main.c b/ demo/main.c
@@ -9,29 +9,29 @@
const stamen_display_f stamen_display = stamen_builtin_display;
          int operation1(void) {
            printf("operation 1\n");
            printf("Some operation is done\n");
            return 1;
              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;
              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;
              printf("operation 3\n");
              printf("Yet another operation is done\n");
              return 1;
          }
          int finish(void) {
            printf("finishing...\n");
            exit(0);
              printf("finishing...\n");
              exit(0);
          }
          int main(void) {
            menu_main(0);
            return 0;
              menu_main(0);
              return 0;
          }
        
        diff --git a/ demo/main.cpp b/ demo/main.cpp
@@ -8,29 +8,29 @@
const stamen::display_f &stamen::display = stamen::builtin_display;
          int operation1(int) {
            std::cout << "operation 1" << std::endl;
            std::cout << "Some operation is done" << std::endl;
            return 1;
              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;
              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;
              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);
              std::cout << "finishing..." << std::endl;
              exit(0);
          }
          int main() {
            menu_main(0);
            return 0;
              menu_main(0);
              return 0;
          }
        
        diff --git a/ include/menu.h b/ include/menu.h
@@ -12,82 +12,86 @@
namespace stamen {
          class Menu {
            friend class Generator;
              friend class Generator;
            static std::unordered_map<std::string, Menu> menu_lookup;
            static std::unordered_map<std::string, callback_f> free_lookup;
            static std::string display_stub_default;
              static std::unordered_map<std::string, Menu> menu_lookup;
              static std::unordered_map<std::string, callback_f> free_lookup;
              static std::string display_stub_default;
            struct private_ctor_t {};
              struct private_ctor_t {};
          public:
            // Tag type dispatch
            Menu(private_ctor_t, const std::string &code, const std::string &prompt)
                : Menu(code, prompt) {}
            public:
              // Tag type dispatch
              Menu(private_ctor_t, const std::string &code, const std::string &prompt)
                  : Menu(code, prompt) {}
            Menu(const Menu &) = delete;
            Menu &operator=(const Menu &) = delete;
              Menu(const Menu &) = delete;
              Menu &operator=(const Menu &) = delete;
            static int dynamic(const std::string &code) {
              display_stub_default = code;
              return display_stub(-1);
            };
            static void read(const std::string &s);
            static void print(const std::string &entry) { print(entry, 1); }
            static void insert(const std::string &s, callback_f callback) {
              free_lookup.emplace(s, callback);
            }
              static int dynamic(const std::string &code) {
                  display_stub_default = code;
                  return display_stub(-1);
              };
              static void read(const std::string &s);
              static void print(const std::string &entry) { print(entry, 1); }
              static void insert(const std::string &s, callback_f callback) {
                  free_lookup.emplace(s, callback);
              }
            [[nodiscard]] const std::string &getCode() const { return code; }
            [[nodiscard]] const std::string &getTitle() const { return title; }
              [[nodiscard]] const std::string &getCode() const { return code; }
              [[nodiscard]] const std::string &getTitle() const { return title; }
            [[nodiscard]] const item_t *getItemv() const { return entries.items.data(); }
            [[nodiscard]] std::size_t getSize() const { return entries.items.size(); }
              [[nodiscard]] const item_t *getItemv() const {
                  return entries.items.data();
              }
              [[nodiscard]] std::size_t getSize() const { return entries.items.size(); }
            [[nodiscard]] const std::string &getCode(std::size_t idx) const {
              return entries.codes[idx].code;
            }
              [[nodiscard]] const std::string &getCode(std::size_t idx) const {
                  return entries.codes[idx].code;
              }
            [[nodiscard]] const std::string &getPrompt(std::size_t idx) const {
              return entries.codes[idx].prompt;
            }
              [[nodiscard]] const std::string &getPrompt(std::size_t idx) const {
                  return entries.codes[idx].prompt;
              }
            [[nodiscard]] callback_f getCallback(std::size_t idx) const {
              return entries.items[idx].callback;
            }
              [[nodiscard]] callback_f getCallback(std::size_t idx) const {
                  return entries.items[idx].callback;
              }
          private:
            Menu(std::string code, std::string prompt)
                : code(std::move(code)), title(std::move(prompt)) {}
            private:
              Menu(std::string code, std::string prompt)
                  : code(std::move(code)), title(std::move(prompt)) {}
            static void print(const std::string &entry, const int depth);
            static int display_stub(int idx);
              static void print(const std::string &entry, const int depth);
              static int display_stub(int idx);
            struct Entries {
              struct code_t {
                const std::string code;
                const std::string prompt;
              };
              struct Entries {
                  struct code_t {
                      std::string code;
                      std::string prompt;
                  };
              ~Entries() {
                for (const auto [_, prompt] : items) delete[] prompt;
              }
                  ~Entries() {
                      for (const auto [_, prompt] : items) {
                          delete[] prompt;
                      }
                  }
              std::vector<code_t> codes;
              std::vector<item_t> items;
                  std::vector<code_t> codes;
                  std::vector<item_t> items;
              void insert(const std::string &code, const std::string &prompt,
                          callback_f callback = display_stub) {
                char *buffer = new char[prompt.size() + 1];
                strcpy(buffer, prompt.c_str());
                items.emplace_back(callback, buffer);
                  void insert(const std::string &code, const std::string &prompt,
                              callback_f callback = display_stub) {
                      char *buffer = new char[prompt.size() + 1];
                      strcpy(buffer, prompt.c_str());
                      items.emplace_back(callback, buffer);
                codes.emplace_back(code, prompt);
              }
            };
                      codes.emplace_back(code, prompt);
                  }
              };
            const std::string code, title;
            Entries entries;
              const std::string code, title;
              Entries entries;
          };
          } // namespace stamen
        
        diff --git a/ include/stamen.h b/ include/stamen.h
          @@ -10,8 +10,8 @@ 
          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;
              stamen_callback_f callback;
              const char *prompt;
          };
          typedef int (*stamen_display_f)(const char *, const stamen_item_t[], int);
        
        diff --git a/ src/c_bindings.cpp b/ src/c_bindings.cpp
          @@ -8,12 +8,12 @@ 
          int stamen_dynamic(const char *code) { return dynamic(code); }
        
        
          void stamen_read(const char *filename) { return read(filename); }
          void stamen_insert(const char *code, stamen_callback_f callback) {
            return insert(code, callback);
              return insert(code, callback);
          }
          int stamen_builtin_display(const char *title, const stamen_item_t itemv[],
                                     int size) {
            return builtin_display(title, itemv, size);
              return builtin_display(title, itemv, size);
          }
          } // namespace stamen
        
        diff --git a/ src/generate.cpp b/ src/generate.cpp
@@ -9,47 +9,47 @@
namespace stamen {
          class Generator {
          public:
            static void generateInclude(std::ostream &os, bool cpp) {
              os << "#ifndef STAMEN_MENU_H\n";
              os << "#define STAMEN_MENU_H\n\n";
            public:
              static void generateInclude(std::ostream &os, bool cpp) {
                  os << "#ifndef STAMEN_MENU_H\n";
                  os << "#define STAMEN_MENU_H\n\n";
              if (cpp) os << "#include \"stamen.hpp\"\n\n";
              else os << "#include \"stamen.h\"\n\n";
                  if (cpp) os << "#include \"stamen.hpp\"\n\n";
                  else os << "#include \"stamen.h\"\n\n";
              for (const auto &[code, menu] : Menu::menu_lookup) {
                os << std::format("int {}(int);\n", menu.getCode());
              }
                  for (const auto &[code, menu] : Menu::menu_lookup) {
                      os << std::format("int {}(int);\n", menu.getCode());
                  }
              os << "\n#endif\n";
            }
                  os << "\n#endif\n";
              }
            static void generateSource(std::ostream &os, bool cpp) {
              if (cpp) os << "#include \"stamen.hpp\"\n\n";
              else os << "#include \"stamen.h\"\n\n";
              static void generateSource(std::ostream &os, bool cpp) {
                  if (cpp) os << "#include \"stamen.hpp\"\n\n";
                  else os << "#include \"stamen.h\"\n\n";
              os << "#include \"shared.h\"\n\n";
              for (const auto &[code, menu] : Menu::menu_lookup) {
                os << std::format("int {}(int) {{\n", menu.getCode());
                  os << "#include \"shared.h\"\n\n";
                  for (const auto &[code, menu] : Menu::menu_lookup) {
                      os << std::format("int {}(int) {{\n", menu.getCode());
                if (cpp) os << "\tstatic const stamen::item_t items[] = ";
                else os << "\tstatic const stamen_item_t items[] = ";
                      if (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 << "{\n";
                      for (int i = 0; i < menu.getSize(); i++) {
                          os << "\t\t{ " << menu.getCode(i);
                          os << ", \"" << menu.getPrompt(i) << "\" },\n";
                      }
                      os << "\t};\n";
                if (cpp) os << "\treturn stamen::display";
                else os << "\treturn stamen_display";
                      if (cpp) os << "\treturn stamen::display";
                      else os << "\treturn stamen_display";
                os << std::format("(\"{}\"", menu.getTitle());
                os << ", items, sizeof(items) / sizeof(items[0]));\n";
                os << "}\n\n";
                      os << std::format("(\"{}\"", menu.getTitle());
                      os << ", items, sizeof(items) / sizeof(items[0]));\n";
                      os << "}\n\n";
                  }
              }
            }
          };
          } // namespace stamen
        
        
          @@ -57,25 +57,25 @@ 
          public:
        
        
          using namespace stamen;
          int main(const int argc, const char *argv[]) {
            const auto args = std::span(argv, size_t(argc));
              const auto args = std::span(argv, size_t(argc));
            if (argc != 2 && argc != 3) {
              std::cout << argv[0] << " config_file [c/cpp]" << std::endl;
              return 1;
            }
              if (argc != 2 && argc != 3) {
                  std::cout << argv[0] << " config_file [c/cpp]" << std::endl;
                  return 1;
              }
            const bool cpp = argc == 2 || std::string(args[2]) == "cpp";
              const bool cpp = argc == 2 || std::string(args[2]) == "cpp";
            std::string path = args[1];
            Menu::read(path);
              std::string path = args[1];
              Menu::read(path);
            std::string::size_type pos = path.rfind('.');
            std::string base = pos != std::string::npos ? path.substr(0, pos) : path;
            std::string ext = cpp ? "pp" : "";
              std::string::size_type pos = path.rfind('.');
              std::string base = pos != std::string::npos ? path.substr(0, pos) : path;
              std::string ext = cpp ? "pp" : "";
            std::ofstream source(base + ".c" + ext), include(base + ".h" + ext);
            Generator::generateSource(source, cpp);
            Generator::generateInclude(include, cpp);
              std::ofstream source(base + ".c" + ext), include(base + ".h" + ext);
              Generator::generateSource(source, cpp);
              Generator::generateInclude(include, cpp);
            return 0;
              return 0;
          }
        
        diff --git a/ src/menu.cpp b/ src/menu.cpp
          @@ -15,62 +15,63 @@ 
          std::unordered_map<std::string, callback_f> Menu::free_lookup;
        
        
          std::string Menu::display_stub_default;
          void Menu::read(const std::string &s) {
            std::string line, delim, code, prompt;
            std::fstream fs(s);
            char tmp = 0;
              std::string line, delim, code, prompt;
              std::fstream fs(s);
              char tmp = 0;
            auto last = menu_lookup.end();
            while (std::getline(fs, line)) {
              if (line.empty()) continue;
              std::istringstream ss(line);
              ss >> delim >> code;
              ss.ignore(1, ' '), std::getline(ss, prompt);
              if (delim == "+") {
                const auto [iter, succ] = menu_lookup.emplace(
                    std::piecewise_construct, std::forward_as_tuple(code),
                    std::forward_as_tuple(private_ctor_t{}, code, prompt));
                last = iter;
              } else {
                last->second.entries.insert(code, prompt);
              auto last = menu_lookup.end();
              while (std::getline(fs, line)) {
                  if (line.empty()) continue;
                  std::istringstream ss(line);
                  ss >> delim >> code;
                  ss.ignore(1, ' '), std::getline(ss, prompt);
                  if (delim == "+") {
                      const auto [iter, succ] = menu_lookup.emplace(
                          std::piecewise_construct, std::forward_as_tuple(code),
                          std::forward_as_tuple(private_ctor_t{}, code, prompt));
                      last = iter;
                  } else {
                      last->second.entries.insert(code, prompt);
                  }
              }
            }
          }
          void Menu::print(const std::string &code, const int depth) {
            const auto it = menu_lookup.find(code);
            if (it == menu_lookup.end()) return;
            const Menu *menu = &it->second;
              const auto it = menu_lookup.find(code);
              if (it == menu_lookup.end()) return;
              const Menu *menu = &it->second;
            if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code);
              if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code);
            for (int i = 0; i < menu->getSize(); i++) {
              std::cout << std::format("{}{} ({})\n", std::string(depth << 1, ' '),
                                       menu->getPrompt(i), menu->getCode(i));
              menu->print(code, depth + 1);
            }
              for (int i = 0; i < menu->getSize(); i++) {
                  std::cout << std::string(depth << 1, ' ');
                  std::cout << menu->getPrompt(i);
                  std::cout << std::format(" ({})\n", menu->getCode(i));
                  menu->print(code, depth + 1);
              }
          }
          int Menu::display_stub(int idx) {
            static std::deque<const Menu *> st;
              static std::deque<const Menu *> st;
            const std::string &code =
                st.size() ? st.back()->getCode(idx) : display_stub_default;
              const std::string &code =
                  st.size() ? st.back()->getCode(idx) : display_stub_default;
            const auto ml_it = menu_lookup.find(code);
            if (ml_it != menu_lookup.end()) {
              const Menu &menu = ml_it->second;
              st.push_back(&menu);
              int ret = stamen_builtin_display(menu.title.c_str(), menu.getItemv(),
                                               menu.getSize());
              st.pop_back();
              return ret;
            }
              const auto ml_it = menu_lookup.find(code);
              if (ml_it != menu_lookup.end()) {
                  const Menu &menu = ml_it->second;
                  st.push_back(&menu);
                  int ret = builtin_display(menu.title.c_str(), menu.getItemv(),
                                            menu.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); }
              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;
              std::cout << "Stamen: nothing to do..." << std::endl;
              return 1;
          }
          } // namespace stamen
        
        diff --git a/ src/stamen.cpp b/ src/stamen.cpp
          @@ -12,48 +12,49 @@ 
          namespace stamen {
        
        
          int dynamic(const char *code) { return Menu::dynamic(code); }
          void read(const char *filename) { Menu::read(filename); }
          void insert(const char *code, callback_f callback) {
            Menu::insert(code, callback);
              Menu::insert(code, callback);
          }
          int builtin_display(const char *title, const item_t itemv[], int size) {
            const size_t digits = size_t(std::log10(size)) + 1;
            const auto items = std::span(itemv, size_t(size));
            int choice = 0;
            while (true) {
              std::cout << std::format("{}:\n", title);
              for (auto i = 0ul; i < size; i++) {
                std::cout << std::format(" {:{}}. {}\n", i, digits, items[i].prompt);
              }
              const size_t digits = size_t(std::log10(size)) + 1;
              const auto items = std::span(itemv, size_t(size));
              int choice = 0;
              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 << std::format("{}:\n", title);
                  for (auto i = 0ul; i < size; i++) {
                      std::cout << std::format(" {:{}}. {}\n", i, digits,
                                               items[i].prompt);
                  }
                  std::cout << std::format("Choice: {}\n\n", items[choice].prompt);
                  const int res = items[choice].callback(choice);
                  if (res > 1)
                    return res - 1;
                  else
                    break;
                } else if (std::cin.eof()) {
                  std::cerr << "encountered end of input!\n";
                  return std::numeric_limits<int>::max();
                } else {
                  std::cin.clear();
                  std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                }
                std::cout << "Invalid option, please choose again!\n";
                  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 << std::format("Choice: {}\n\n",
                                                   items[choice].prompt);
                          const int res = items[choice].callback(choice);
                          if (res > 1) return res - 1;
                          else break;
                      } else if (std::cin.eof()) {
                          std::cerr << "encountered end of input!\n";
                          return std::numeric_limits<int>::max();
                      } else {
                          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;
              }
              std::cout << std::endl;
            }
            return 1;
              return 1;
          }
          } // namespace stamen