stamen

Stamen - static menu generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE

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

Diffstat:
D.ccls | 4----
M.gitignore | 2++
MCMakeLists.txt | 8++++++--
Mdemo/CMakeLists.txt | 33+++++++++++++++++++++++++++++++--
Ademo/main.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdemo/main.cpp | 2+-
Minclude/stamen.h | 7+++++--
Ainclude/stamenc.h | 27+++++++++++++++++++++++++++
Msrc/CMakeLists.txt | 24+++++++++++++++++-------
Msrc/generate.cpp | 40++++++++++++++++++++++------------------
Asrc/stamenc.cpp | 23+++++++++++++++++++++++
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; +}