stamenStatic Menu Generator | 
          
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | f4fed18ad7916907f96caaa641143d3ed00f02f6 | 
| parent | 69b2fecb3c6afe1358abb309dd8a3a95cdf417e7 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Thu, 30 Nov 2023 02:16:26 +0000 | 
Provide C version of the library
* Remove old .ccls
* Add C demo code
* Provide C binding for the library
| D | .ccls | | | ---- | 
| M | .gitignore | | | ++ | 
| M | CMakeLists.txt | | | ++++++ -- | 
| M | demo/CMakeLists.txt | | | +++++++++++++++++++++++++++++++ -- | 
| A | demo/main.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| M | demo/main.cpp | | | + - | 
| M | include/stamen.h | | | +++++ -- | 
| A | include/stamenc.h | | | +++++++++++++++++++++++++++ | 
| M | src/CMakeLists.txt | | | +++++++++++++++++ ------- | 
| M | src/generate.cpp | | | ++++++++++++++++++++++ ------------------ | 
| A | src/stamenc.cpp | | | +++++++++++++++++++++++ | 
11 files changed, 199 insertions(+), 36 deletions(-)
diff --git a/ .ccls b/ .ccls
@@ -1,4 +0,0 @@
clang++
          -style=file
          -std=c++17
          -Iinclude
        
        diff --git a/ .gitignore b/ .gitignore
@@ -1,4 +1,6 @@
build
          .cache
          demo/demo_menu.c
          demo/demo_menu.cpp
          demo/demo_menu.h
          demo/demo_menu.hpp
        
        diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -3,14 +3,18 @@ 
          set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
        
        
          project(
              Stamen
              VERSION 0.0.11
              VERSION 0.0.12
              DESCRIPTION "Static menu generator"
              LANGUAGES CXX
              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)
          add_subdirectory(src)
          add_subdirectory(demo)
        
        diff --git a/ demo/CMakeLists.txt b/ demo/CMakeLists.txt
          @@ -8,14 +8,14 @@ 
          target_link_libraries(demo
        
        
          )
          add_custom_command(
              OUTPUT demo_menu.h demo_menu.cpp
              OUTPUT demo_menu.hpp demo_menu.cpp
              COMMAND ${CMAKE_BINARY_DIR}/bin/stamen-generate ${CMAKE_CURRENT_SOURCE_DIR}/demo_menu.conf
              DEPENDS demo_menu.conf stamen-generate
              COMMENT "Generating menu files"
          )
          add_custom_target(GenerateMenu
              DEPENDS demo_menu.h demo_menu.cpp
              DEPENDS demo_menu.hpp demo_menu.cpp
              COMMENT "Check if re-generation of menu files is required"
          )
          
          @@ -28,3 +28,32 @@ 
          set_target_properties(demo PROPERTIES
        
        
          )
          configure_file(demo_menu.conf ${CMAKE_BINARY_DIR}/bin/demo_menu.conf COPYONLY)
          add_executable(cdemo
              main.c
              ${CMAKE_CURRENT_SOURCE_DIR}/demo_menu.c
          )
          target_link_libraries(cdemo
              stamenc
          )
          add_custom_command(
              OUTPUT demo_menu.h demo_menu.c
              COMMAND ${CMAKE_BINARY_DIR}/bin/stamen-generate ${CMAKE_CURRENT_SOURCE_DIR}/demo_menu.conf c
              DEPENDS demo_menu.conf stamen-generate
              COMMENT "Generating menu files"
          )
          add_custom_target(GenerateMenuC
              DEPENDS demo_menu.h demo_menu.c
              COMMENT "Check if re-generation of menu files is required"
          )
          add_dependencies(cdemo GenerateMenuC)
          set_target_properties(cdemo PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
          )
        
        diff --git a/ demo/main.c b/ demo/main.c
@@ -0,0 +1,65 @@
#include "demo_menu.h"
          #include "stamenc.h"
          #include <stdio.h>
          #include <stdlib.h>
          #include <string.h>
          const display_f display = stamen_builtin_display;
          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 menu_static_run(void) { return menu_main(); }
          int menu_dynamic_run(void) { return stamen_start("menu_main"); }
          int menu_dynamic_print(void) {
            stamen_print("menu_main");
            return 1;
          }
          int main(int argc, const char *argv[]) {
            const char *filename = "./demo_menu.conf";
            char buffer[FILENAME_MAX] = {0};
            const char *sep = strrchr(argv[0], '/');
            if (sep) {
              const size_t size = sep - argv[0] + 1;
              memcpy(buffer, argv[0], size);
              memcpy(buffer + size, filename, strlen(filename));
            }
            stamen_read(sep ? buffer : filename);
            stamen_insert("finish", finish);
            stamen_insert("operation1", operation1);
            stamen_insert("operation2", operation2);
            stamen_insert("operation3", operation3);
            static const item_t items[] = {
                {   menu_static_run, "Run statically generated menu"},
                {  menu_dynamic_run,              "Run dynamic menu"},
                {menu_dynamic_print,            "Print dynamic menu"},
                {            finish,                          "Quit"},
            };
            display("Menu demo program", items, sizeof(items) / sizeof(item_t));
            return 0;
          }
        
        diff --git a/ demo/main.cpp b/ demo/main.cpp
@@ -1,6 +1,6 @@
#include <iostream>
          #include "demo_menu.h"
          #include "demo_menu.hpp"
          using stamen::Menu;
          diff --git a/ include/stamen.h b/ include/stamen.h
@@ -1,6 +1,8 @@
#ifndef STAMEN_H
          #define STAMEN_H
          #include "stamenc.h"
          #include <exception>
          #include <format>
          #include <fstream>
        
        
          @@ -31,6 +33,7 @@ 
          public:
        
        
                : Menu(code, callback) {}
            struct item_t {
              item_t() {}
              item_t(const callback_f func, const std::string &prompt)
                  : callback(func), prompt(prompt) {}
          
          @@ -44,8 +47,8 @@ 
          public:
        
        
              int operator()(void) const { return callback ? callback() : start(code); }
            private:
              const std::string prompt, code;
              const callback_f callback = nullptr;
              std::string prompt, code;
              callback_f callback = nullptr;
            };
            typedef int (*display_f)(const std::string &, const item_t[], std::size_t);
        
        diff --git a/ include/stamenc.h b/ include/stamenc.h
@@ -0,0 +1,27 @@
#ifndef CSTAMEN_H
          #define CSTAMEN_H
          #ifdef __cplusplus
          #define EXTERNC extern "C"
          #else
          #define EXTERNC extern
          #endif
          typedef int (*callback_f)(void);
          typedef struct item_t item_t;
          struct item_t {
            callback_f callback;
            const char *prompt;
          };
          typedef int (*display_f)(const char *, const item_t[], int);
          extern const display_f display;
          EXTERNC void stamen_read(const char *file);
          EXTERNC void stamen_print(const char *entry);
          EXTERNC int stamen_start(const char *entry);
          EXTERNC void stamen_insert(const char *code, callback_f callback);
          EXTERNC int stamen_builtin_display(const char *title, const item_t items[], int size);
          #endif
        
        diff --git a/ src/CMakeLists.txt b/ src/CMakeLists.txt
@@ -1,6 +1,22 @@
add_library(stamen-include INTERFACE)
          target_include_directories(stamen-include INTERFACE ../include)
          add_library(stamen stamen.cpp)
          target_link_libraries(stamen PUBLIC stamen-include)
          set_target_properties(stamen PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              PUBLIC_HEADER ../include/stamen.h
          )
          add_library(stamenc stamenc.cpp stamen.cpp)
          target_link_libraries(stamenc PUBLIC stamen-include)
          set_target_properties(stamenc PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              PUBLIC_HEADER ../include/stamenc.h
          )
          add_executable(stamen-generate generate.cpp)
          target_link_libraries(stamen-generate PRIVATE stamen-include)
          target_include_directories(stamen-generate PUBLIC ../include)
        
        
          @@ -11,16 +27,10 @@ 
          set_target_properties(stamen-generate PROPERTIES
        
        
              RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
          )
          add_library(stamen stamen.cpp)
          target_link_libraries(stamen PUBLIC stamen-include)
          set_target_properties(stamen PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              PUBLIC_HEADER ../include/stamen.h
          )
          install(TARGETS stamen-generate DESTINATION bin)
          install(TARGETS stamen
              LIBRARY DESTINATION lib
              PUBLIC_HEADER DESTINATION include
          )
          diff --git a/ src/generate.cpp b/ src/generate.cpp
          @@ -13,63 +13,67 @@ 
          public:
        
        
              }
            };
            static void generateInclude(std::ostream &os) {
            static void generateInclude(std::ostream &os, bool cpp) {
              os << "#ifndef STAMEN_MENU_H\n";
              os << "#define STAMEN_MENU_H\n\n";
              os << "#include <stamen.h>\n\n";
              os << "namespace stamen {\n\n";
              os << std::format("#include <stamen{}.h>\n\n", !cpp ? "c" : "");
              if (cpp) os << "namespace stamen {\n\n";
              for (const auto &[code, _] : Menu::getLookup()) {
                const Menu *menu = Menu::getMenu(code);
                if (!menu) throw EGenerate();
                if (menu->callback) continue;
                os << std::format("int {}(void);\n", menu->code);
              }
              os << "\n}\n";
              if (cpp) os << "\n}\n";
              os << "#endif\n";
            }
            static void generateSource(std::ostream &os) {
              os << "#include <stamen.h>\n";
              os << "#include \"shared.h\"\n";
              os << "\nnamespace stamen {\n";
            static void generateSource(std::ostream &os, bool cpp) {
              os << std::format("#include <stamen{}.h>\n", !cpp ? "c" : "");
              os << "#include \"shared.h\"\n\n";
              if (cpp) os << "namespace stamen {\n";
              for (const auto &[code, _] : Menu::getLookup()) {
                const Menu *menu = Menu::getMenu(code);
                if (!menu) throw EGenerate();
                if (menu->callback) continue;
                os << std::format("int {}(void) {{\n", menu->code);
                os << std::format("\tstatic const Menu::item_t items[] = {{\n");
                os << std::format("\tstatic const {}item_t items[] = {{\n",
                                  cpp ? "Menu::" : "");
                for (const auto &item : menu->items) {
                  os << std::format("\t\t{{{}, \"{}\"}},\n", item.getCode(),
                  os << std::format("\t\t{{ {}, \"{}\" }},\n", item.getCode(),
                                    item.getPrompt());
                }
                os << std::format("\t}};\n");
                os << std::format("\treturn Menu::display(\"{}\", items, "
                                  "sizeof(items) / sizeof(Menu::item_t));\n",
                                  menu->title);
                os << std::format("\treturn {0}display(\"{1}\", items, "
                                  "sizeof(items) / sizeof({0}item_t));\n",
                                  cpp ? "Menu::" : "", menu->title);
                os << std::format("}}\n\n");
              }
              os << "\n}\n";
              if (cpp) os << "\n}\n";
            }
          };
          } // namespace stamen
          int main(const int argc, const char *argv[]) {
            if (argc != 2) {
            if (argc != 2 && argc != 3) {
              std::cout << "please enter exaclty one config file" << std::endl;
              return 1;
            }
            const bool cpp = argc == 2 || std::string(argv[2]) == "cpp";
            std::string path = argv[1];
            Menu::read(path);
            std::string::size_type pos = path.rfind('.');
            std::string base = pos != std::string::npos ? path.substr(0, pos) : path;
            std::ofstream cpp(base + ".cpp"), h(base + ".h");
            std::string ext = cpp ? "pp" : "";
            Generator::generateSource(cpp);
            Generator::generateInclude(h);
            std::ofstream source(base + ".c" + ext), include(base + ".h" + ext);
            Generator::generateSource(source, cpp);
            Generator::generateInclude(include, cpp);
            return 0;
          }
        
        diff --git a/ src/stamenc.cpp b/ src/stamenc.cpp
@@ -0,0 +1,23 @@
#include "../include/stamen.h"
          using namespace stamen;
          const Menu::display_f Menu::display = nullptr;
          void stamen_read(const char *file) { stamen::Menu::read(file); }
          void stamen_print(const char *entry) { stamen::Menu::print(entry); }
          int stamen_start(const char *entry) { return stamen::Menu::start(entry); }
          void stamen_insert(const char *code, callback_f callback) {
            Menu::insert(code, callback);
          }
          int stamen_builtin_display(const char *title, const item_t items[], int size) {
            Menu::item_t *litems = new Menu::item_t[size];
            if (!litems) return -1;
            for (int i = 0; i < size; i++) {
              litems[i] = Menu::item_t(items[i].callback, items[i].prompt);
            }
            const int ret = builtinDisplay(title, litems, size);
            delete[] litems;
            return ret;
          }