From be5007d57c56a4fb6db7cac77a23af982cd7b682 Mon Sep 17 00:00:00 2001 From: Squibid Date: Sun, 31 Aug 2025 07:34:13 -0400 Subject: [PATCH] initial commit --- .gitignore | 2 + .gitmodules | 6 +++ README.md | 10 ++++ completions/_wom.zsh | 22 ++++++++ include/api.h | 47 +++++++++++++++++ include/conf.h | 15 ++++++ include/lua/wom.h | 20 +++++++ include/lua/wom_fs.h | 26 +++++++++ include/subcmds.h | 11 ++++ lib/ds | 1 + lib/log.c | 1 + meson.build | 51 ++++++++++++++++++ src/api.c | 120 ++++++++++++++++++++++++++++++++++++++++++ src/conf.c | 54 +++++++++++++++++++ src/lua/wom.c | 87 ++++++++++++++++++++++++++++++ src/lua/wom_fs.c | 96 +++++++++++++++++++++++++++++++++ src/main.c | 95 +++++++++++++++++++++++++++++++++ src/subcmds/clock.c | 98 ++++++++++++++++++++++++++++++++++ src/subcmds/dev.c | 105 ++++++++++++++++++++++++++++++++++++ src/subcmds/motd.c | 112 +++++++++++++++++++++++++++++++++++++++ src/subcmds/project.c | 9 ++++ src/subcmds/subcmds.c | 16 ++++++ test.lua | 9 ++++ 23 files changed, 1013 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 README.md create mode 100644 completions/_wom.zsh create mode 100644 include/api.h create mode 100644 include/conf.h create mode 100644 include/lua/wom.h create mode 100644 include/lua/wom_fs.h create mode 100644 include/subcmds.h create mode 160000 lib/ds create mode 160000 lib/log.c create mode 100644 meson.build create mode 100644 src/api.c create mode 100644 src/conf.c create mode 100644 src/lua/wom.c create mode 100644 src/lua/wom_fs.c create mode 100644 src/main.c create mode 100644 src/subcmds/clock.c create mode 100644 src/subcmds/dev.c create mode 100644 src/subcmds/motd.c create mode 100644 src/subcmds/project.c create mode 100644 src/subcmds/subcmds.c create mode 100644 test.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5341b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +compile_commands.json +.cache diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0257eb5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/log.c"] + path = lib/log.c + url = https://github.com/rxi/log.c +[submodule "lib/ds"] + path = lib/ds + url = https://git.squi.bid/squibid/ds diff --git a/README.md b/README.md new file mode 100644 index 0000000..6270171 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Womblic +command line helper tool which does everything for me + +usage: +```bash +wom +``` + +# TODO +add dev env creator diff --git a/completions/_wom.zsh b/completions/_wom.zsh new file mode 100644 index 0000000..7d22537 --- /dev/null +++ b/completions/_wom.zsh @@ -0,0 +1,22 @@ +#compdef wom + +_arguments \ + '1:flag:->flags' \ + '*:: :->args' + +case "$state" in + flags) + local -a opts + opts=( + '-c:Path to config file' + '-v:Show version and exit' + '-h:Show help text' + $(wom subcmds) + ) + _describe 'flags' opts + ;; + args) + case $line[1] in + -c) _files ;; + esac +esac diff --git a/include/api.h b/include/api.h new file mode 100644 index 0000000..2805316 --- /dev/null +++ b/include/api.h @@ -0,0 +1,47 @@ +#pragma once + +#include "ds.h" + +typedef struct { + char *name; + void (*cb)(void *, int argc, char *argv[]); + void *data; +} wom_subcmd_t; + +/** + * @brief pointer to all registered subcommands + */ +extern ds_sll_t *subcmds; + +/** + * @brief register a new subcommand + * + * @param name name which should be used to call subcommand + * @param cb function to run when name is called + * @param data pointer to data to pass to the cb + * @return 0 on success + */ +int register_subcmd(char *name, void (*cb)(void *, int argc, char *argv[]), void *data); + + +/** + * @brief list out all registered subcmds + * + * @return list of subcmds + */ +char **list_subcmds(); + +/** + * @brief run subcmds mentioned in argv + * + * @param argc argc + * @param argv argv + */ +void run_subcmds(int argc, char *argv[]); + +/** + * @brief unregister and free all subcmds + * + * @return 0 on success + */ +int cleanup_subcmds(); diff --git a/include/conf.h b/include/conf.h new file mode 100644 index 0000000..2f5abab --- /dev/null +++ b/include/conf.h @@ -0,0 +1,15 @@ +#pragma once + +/** + * @brief attempt to find the config path + * + * @return the path + */ +char *conf_config_path(void); + +/** + * @brief attempt to find the state path + * + * @return the path + */ +char *conf_state_path(void); diff --git a/include/lua/wom.h b/include/lua/wom.h new file mode 100644 index 0000000..8f8b9f1 --- /dev/null +++ b/include/lua/wom.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +/** + * @brief list of all functions + */ +extern struct luaL_Reg *womlib; + +int wom_register_subcmd(lua_State *L); + +/** + * @brief push a lua table with all the subcmds onto the lua stack + * + * @param L the lua state + * @return + */ +int wom_list_subcmds(lua_State *L); diff --git a/include/lua/wom_fs.h b/include/lua/wom_fs.h new file mode 100644 index 0000000..cfdfd3c --- /dev/null +++ b/include/lua/wom_fs.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +/** + * @brief list of all filesystem functions + */ +extern struct luaL_Reg *womfslib; + +/** + * @brief create a fs iter for lua + * + * @param L the lua state + * @return + */ +int wom_fs_dir(lua_State *L); + +/** + * @brief get the type of a directory in lua + * + * @param L the lua state + * @return + */ +int wom_fs_type(lua_State *L); diff --git a/include/subcmds.h b/include/subcmds.h new file mode 100644 index 0000000..6fdb456 --- /dev/null +++ b/include/subcmds.h @@ -0,0 +1,11 @@ +#pragma once + +/** + * The funcs below are not in need of documentation because they follow the + * subcmd pattern + */ + +void timer_subcmd(void *, int argc, char *argv[]); +void subcmds_dev(void *, int argc, char *argv[]); +void motd_subcmd(void *, int argc, char *argv[]); +void subcmds_subcmd(void *, int argc, char *argv[]); diff --git a/lib/ds b/lib/ds new file mode 160000 index 0000000..16470f2 --- /dev/null +++ b/lib/ds @@ -0,0 +1 @@ +Subproject commit 16470f259286856e5c48cf02bd3b653bfaa72c9b diff --git a/lib/log.c b/lib/log.c new file mode 160000 index 0000000..f9ea349 --- /dev/null +++ b/lib/log.c @@ -0,0 +1 @@ +Subproject commit f9ea34994bd58ed342d2245cd4110bb5c6790153 diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..7eaff82 --- /dev/null +++ b/meson.build @@ -0,0 +1,51 @@ +project('womblic', 'c', + version: '0.0.1', + license: 'GPLv3') + +add_project_arguments([ + '-DVERSION="@0@"'.format(meson.project_version()), + '-DLOG_USE_COLOR' # enable colored logs +], language: 'c') + +# get all the source files for the executable +luafiles = files( + 'src/lua/wom.c', + 'src/lua/wom_fs.c' +) + +subcmds = files( + 'src/subcmds/clock.c', + 'src/subcmds/dev.c', + 'src/subcmds/motd.c', + 'src/subcmds/project.c', + 'src/subcmds/subcmds.c', +) + +srcfiles = files( + 'src/main.c', + 'src/api.c', + 'src/conf.c' +) + luafiles + subcmds + +# build the executable +executable('wom', srcfiles, + dependencies: [ + dependency('lua', version: '>=5.1 <6.0'), + ], + include_directories: [ + include_directories('include'), + + include_directories('lib/log.c/src'), + include_directories('lib/ds'), + ], + link_with: [ + static_library('ds', 'lib/ds/ds.c', + include_directories: 'lib/ds'), + + static_library('log.c', 'lib/log.c/src/log.c', + include_directories: 'lib/log.c/src'), + ] +) +install_data('completions/_wom.zsh', + install_dir: '/usr/local/share/zsh/site-functions/' +) diff --git a/src/api.c b/src/api.c new file mode 100644 index 0000000..369abd7 --- /dev/null +++ b/src/api.c @@ -0,0 +1,120 @@ +#include +#include +#include + +#include "ds.h" +#include "log.h" + +#include "api.h" + +ds_sll_t *subcmds; + +int +register_subcmd(char *name, void (*cb)(void *, int argc, char *argv[]), void *data) +{ + char *c; + ds_sll_t *newnode = calloc(1, sizeof(ds_sll_t)); + wom_subcmd_t *newsubcmd = calloc(1, sizeof(wom_subcmd_t)); + + /* make sure the subcmd name doesn't contain a space */ + for (c = name; *c != '\0'; c++) { + if (isspace(*c)) { + log_fatal("subcmd '%s' can't contain any whitespace characters", name); + return 1; + } + } + + /* setup data */ + newsubcmd->name = name; + newsubcmd->cb = cb; + newsubcmd->data = data; + newnode->data = newsubcmd; + newnode->next = NULL; + + /* if this is the first node */ + if (!subcmds) { + subcmds = newnode; + return 0; + } + + /* otherwise append the node to the end of the current nodes */ + ds_ll_foreach(ds_sll_t, subcmds) { + if (!cur->next) { + cur->next = newnode; + break; + } + } + + return 0; +} + +char +**list_subcmds(void) +{ + int i; + char **subcmdp; + ds_sll_t *node; + wom_subcmd_t *subcmd; + + /* get the number of elements in the linked list */ + /* TEST: for some reason we need to allocate an extra space in the array or + * else */ + for (i = 1, node = subcmds; node; node = node->next, i++) + ; + + subcmdp = calloc(i, sizeof(char *)); + + for (i = 0, node = subcmds; node; node = node->next, i++) { + subcmd = node->data; + if (subcmd->name) { + subcmdp[i] = subcmd->name; + } + } + + return subcmdp; +} + +void +run_subcmds(int argc, char *argv[]) +{ + int i, j; + char **args; + wom_subcmd_t *subcmd; + + /* check for subcommands to run */ + for (i = 1; i < argc; i++) { + ds_ll_foreach(ds_sll_t, subcmds) { + subcmd = cur->data; + if (strcmp(argv[i], subcmd->name) == 0) { + goto args; + } + } + } + + return; +args: + args = calloc(argc - i, sizeof(char *)); + for (j = 0; i < argc; i++, j++) { + args[j] = argv[i]; + } + + subcmd->cb(subcmd->data, j, args); + free(args); +} + +int +cleanup_subcmds(void) +{ + ds_sll_t *prevnode, *node; + + for (node = subcmds; node;) { + free(node->data); + prevnode = node; + node = node->next; + free(prevnode); + } + + free(node); + + return 0; +} diff --git a/src/conf.c b/src/conf.c new file mode 100644 index 0000000..65c7eb5 --- /dev/null +++ b/src/conf.c @@ -0,0 +1,54 @@ +#include +#include + +#include "conf.h" + +char *_conf_state_path = NULL; + +char +*conf_config_path(void) +{ + char *path, *config_path; + + config_path = NULL; + + if ((path = getenv("XDG_CONFIG_HOME"))) { + config_path = calloc( + strlen(path) + strlen("/wom/init.lua") + 1, + sizeof(char) + ); + config_path = strcat(path, "/wom/init.lua"); + } else if ((path = getenv("HOME"))) { + config_path = calloc( + strlen(path) + strlen("/.config/wom/init.lua") + 1, + sizeof(char) + ); + config_path = strcat(path, "/.config/wom/init.lua"); + } + + return config_path; +} + +char +*conf_state_path(void) +{ + char *path, *state_path; + + if (_conf_state_path) { + return _conf_state_path; + } + + state_path = NULL; + + if ((path = getenv("XDG_DATA_HOME"))) { + state_path = calloc(strlen(path), sizeof(char)); + state_path = strcat(path, "/wom/"); + } else if ((path = getenv("HOME"))) { + state_path = calloc(strlen(path), sizeof(char)); + state_path = strcat(path, "/.local/share/wom/"); + } + + _conf_state_path = state_path; + + return _conf_state_path; +} diff --git a/src/lua/wom.c b/src/lua/wom.c new file mode 100644 index 0000000..32115a7 --- /dev/null +++ b/src/lua/wom.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +#include "lua/wom.h" +#include "api.h" + +struct luaL_Reg *womlib = (luaL_Reg[]){ + { .name = "register_subcmd", .func = wom_register_subcmd }, + { .name = "list_subcmds", .func = wom_list_subcmds }, + { NULL, NULL } +}; + +static lua_State *l; + +/** + * @brief wrap a lua function in C + */ +static inline void +wrapper_func(void *data, int argc, char *argv[]) +{ + int i; + + lua_rawgeti(l, LUA_REGISTRYINDEX, *(int *)data); + lua_pushvalue(l, 1); + + /* populate lua table with argv */ + lua_createtable(l, 0, argc); + for (i = 0; i < argc; i++) { + lua_pushstring(l, argv[i]); + lua_rawseti(l, -2, i + 1); + } + + lua_call(l, 1, 0); + l = NULL; + + return; +} + +int +wom_register_subcmd(lua_State *L) +{ + char *name; + int ret, *cb = calloc(1, sizeof(int)); + + luaL_checktype(L, 1, LUA_TSTRING); + name = (char *)luaL_checkstring(L, 1); + + luaL_checktype(L, 2, LUA_TFUNCTION); + + /* store the function */ + *cb = luaL_ref(L, LUA_REGISTRYINDEX); + l = L; + + ret = register_subcmd(name, wrapper_func, cb); + if (ret != 0) { + lua_error(L); + } + + lua_pushinteger(L, ret); + + return 1; +} + +int +wom_list_subcmds(lua_State *L) +{ + int i; + char **p = list_subcmds(); + + /* get the number of elements in the array */ + for (i = 0; p[i]; i++) + ; + + lua_createtable(L, i, 0); + + /* fill up the lua table from the c array */ + for (i = 0; p[i]; i++) { + lua_pushstring(L, p[i]); + lua_rawseti(L, -2, i + 1); + } + + free(p); + + return 1; +} diff --git a/src/lua/wom_fs.c b/src/lua/wom_fs.c new file mode 100644 index 0000000..540d405 --- /dev/null +++ b/src/lua/wom_fs.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "lua/wom_fs.h" + +struct luaL_Reg *womfslib = (luaL_Reg[]){ + { .name = "dir", .func = wom_fs_dir }, + { .name = "type", .func = wom_fs_type }, + { NULL, NULL } +}; + +static int +dir_gc(lua_State *L) +{ + DIR *d = *(DIR **)lua_touserdata(L, 1); + + if (d) { + closedir(d); + } + + return 0; +} + +static int +dir_iter(lua_State *L) +{ + DIR *d = *(DIR **)lua_touserdata(L, lua_upvalueindex(1)); + struct dirent *entry; + + if ((entry = readdir(d)) != NULL) { + lua_pushstring(L, entry->d_name); + return 1; + } + + return 0; /* no more values to return */ +} + +int +wom_fs_dir(lua_State *L) +{ + const char *path; + DIR **d; + + path = luaL_checkstring(L, 1); + + /* create directory pointer in the user data */ + d = lua_newuserdata(L, sizeof(DIR *)); + + /* set its metatable */ + lua_newtable(L); + lua_pushcfunction(L, dir_gc); + lua_setfield(L, -2, "__gc"); + + lua_setmetatable(L, -2); + + /* try to open the specified directory */ + if ((*d = opendir(path)) == NULL) { + luaL_error(L, "cannot open %s: %s", path, strerror(errno)); + } + + /* creates and returns the iterator function with the data *DIR attached */ + lua_pushcclosure(L, dir_iter, 1); + + return 1; +} + +int +wom_fs_type(lua_State *L) +{ + const char *path; + struct stat s; + + path = luaL_checkstring(L, 1); + + if (stat(path, &s) != 0) { + luaL_error(L, "cannot open '%s': %s", path, strerror(errno)); + } + + switch (s.st_mode & 0170000) { + case __S_IFDIR: lua_pushstring(L, "directory"); break; + case __S_IFCHR: lua_pushstring(L, "chardevice"); break; + case __S_IFBLK: lua_pushstring(L, "blockdevice"); break; + case __S_IFREG: lua_pushstring(L, "file"); break; + case __S_IFIFO: lua_pushstring(L, "fifo"); break; + case __S_IFLNK: lua_pushstring(L, "symlink"); break; + case __S_IFSOCK: lua_pushstring(L, "socket"); break; + default: luaL_error(L, "not able to determine type of %s", path); break; + } + + return 1; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..846e73f --- /dev/null +++ b/src/main.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "conf.h" +#include "lua/wom.h" +#include "lua/wom_fs.h" +#include "subcmds.h" +#include "api.h" + +static lua_State +*load_wom_lua_lib(lua_State *L) +{ + /* open up default libraries */ + luaL_openlibs(L); + + /* load in the wom library */ + lua_newtable(L); + + /* load in version info */ + lua_pushstring(L, "version"); + lua_pushstring(L, VERSION); + lua_settable(L, -3); + + /* load in the filesystem library */ + lua_pushstring(L, "fs"); + lua_createtable(L, 0, 0); + luaL_setfuncs(L, womfslib, 0); + lua_settable(L, -3); + + /* load in all the clua functions */ + luaL_setfuncs(L, womlib, 0); + lua_setglobal(L, "wom"); + + return L; +} + +int +main(int argc, char *argv[]) +{ + int c, l; + /* FIXME: config path can't be freed after being passed to lualib which causes + * a minor memory leak */ + char *config_path = { 0 }; + lua_State *L; + + /* general options for womblic */ + while ((c = getopt(argc, argv, "hvc:")) != -1) { + switch (c) { + case 'c': + l = strlen(optarg); + config_path = calloc(l + 1, sizeof(char)); + strncpy(config_path, optarg, l); + break; + case 'v': printf("%s-%s\n", argv[0], VERSION); break; + case 'h': + default: + printf("help text\n"); + break; + } + } + + /* setup for subcommands */ + register_subcmd("motd", motd_subcmd, NULL); + register_subcmd("timer", timer_subcmd, NULL); + register_subcmd("subcmds", subcmds_subcmd, NULL); + register_subcmd("dev", subcmds_dev, NULL); + + /* if the user didn't specify a config path */ + if (!config_path) { + config_path = conf_config_path(); + } + + /* load lua library */ + L = luaL_newstate(); + L = load_wom_lua_lib(L); + + /* run lua code */ + if (luaL_loadfile(L, config_path) || lua_pcall(L, 0, 0, 0)) { + lua_error(L); + } else { + lua_pop(L, lua_gettop(L)); + } + + run_subcmds(argc, argv); + + lua_close(L); + cleanup_subcmds(); + + return 0; +} diff --git a/src/subcmds/clock.c b/src/subcmds/clock.c new file mode 100644 index 0000000..02a5f57 --- /dev/null +++ b/src/subcmds/clock.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include + +#include "subcmds.h" + +/** + * @brief convert a sting to a time in seconds ie: + * 12s -> 12 seconds + * + * 12m -> 720 seconds + * + * 12h -> 43,200 seconds + * + * 12d -> 1,036,800 seconds + * + * @param str input time + * @return time in seconds + */ +static int +string2time(char *str) +{ + char *d; + int i; + double time; + + /* find the length of the string */ + for (i = 0; str[i] != '\0'; i++) + ; + + /* if there is a trailing character make sure to remove it */ + if (str[i] < '0' || str[i] > '9') { + i--; + } + + /* allocate space for numerical string */ + d = (char *)calloc(i, sizeof(char)); + strncpy(d, str, i); + + /* atoi the numerical string into an int */ + time = atof(d); + free(d); + + /* modify the time using the last trailing character */ + switch (str[i]) { + case 's': break; + case 'm': time *= 60; break; + case 'h': time *= 60 * 60; break; + case 'd': time *= 60 * 60 * 24; break; + default: return -1; break; + } + + return time; +} + + +void +timer_subcmd(void *, int argc, char *argv[]) +{ + int i, l, sum; + char *s; + + for (i = 1, sum = 0, l = 0; i < argc; i++) { + sum += string2time(argv[i]); + l += strlen(argv[i]); + } + + /* allocate and populate a string with the length of the timer */ + s = (char *)calloc(l + i - 1, sizeof(char)); + for (i = 1; i < argc; i++) { + /* TODO: reorder based on magnitude */ + s = strcat(s, argv[i]); + if (i != argc - 1) { + s = strcat(s, " "); + } + } + + if (sum <= 0) { + exit(1); + } + + /* print out the number of time */ + printf("Waiting for %s...\n", s); + + /* TODO: instead of sleeping implement polling that way if need be we can have + * a countdown */ + sleep(sum); + + puts("returned"); + + return; +} + +static void +stopwatch_subcmd(void *, int argc, char *argv[]) +{ +} diff --git a/src/subcmds/dev.c b/src/subcmds/dev.c new file mode 100644 index 0000000..cfca8e4 --- /dev/null +++ b/src/subcmds/dev.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "conf.h" +#include "subcmds.h" + +static void +add(char *path) +{ + char *wpath, *l; + FILE *f; + + wpath = calloc(strlen(conf_state_path()) + 4, sizeof(char)); + strcpy(wpath, conf_state_path()); + strcat(wpath, "dev"); + + /* ensure that the file exists */ + if (access(wpath, F_OK) != 0) { + fclose(fopen(wpath, "w")); + } + + /* check if it already exists */ + f = fopen(wpath, "r"); + l = calloc(PATH_MAX, sizeof(char)); + while (fgets(l, PATH_MAX, f)) { + l[strlen(l) - 1] = '\0'; + if (strcmp(l, path) == 0) { + printf("'%s' is already in the list of projects\n", path); + free(l); + free(wpath); + fclose(f); + return; + } + bzero(l, PATH_MAX); + } + fclose(f); + + /* add the project to the list */ + f = fopen(wpath, "a"); + if (fprintf(f, "%s\n", path) < 0) { + printf("failed to add new project to dev list\n"); + exit(1); + } + fclose(f); + + free(wpath); + return; +} + +void +subcmds_dev(void *, int argc, char *argv[]) +{ + char cwd[PATH_MAX]; + char *wpath, *editor; + + if (argc > 1) { + if (strcmp(argv[1], "add") == 0) { + if (argc > 2) { + /* if the directory doesn't exist, then make it */ + if (!opendir(conf_state_path())) { + if (errno == ENOENT) { + if (mkdir(conf_state_path(), 0755) < 0) { + printf("Failed to create state path: %s\n", conf_state_path()); + exit(1); + } + } + } + + /* add the directory to the path */ + add(argv[2]); + } else { + if (getcwd(cwd, PATH_MAX) == NULL) { + exit(1); + } + + /* add the directory to the path */ + add(cwd); + } + } else if (strcmp(argv[1], "edit") == 0) { + wpath = calloc(strlen(conf_state_path()) + 4, sizeof(char)); + strcpy(wpath, conf_state_path()); + strcat(wpath, "dev"); + + editor = getenv("EDITOR"); + if (editor) { + execvp(editor, (char *[]){ editor, wpath, NULL }); + goto free_stuff; + } + printf("Failed to open editor\n"); + free_stuff: + free(editor); + free(wpath); + } else if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "h") == 0) { + printf("Help text\n"); + } else { + } + } +} diff --git a/src/subcmds/motd.c b/src/subcmds/motd.c new file mode 100644 index 0000000..206e60b --- /dev/null +++ b/src/subcmds/motd.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "subcmds.h" + +static const char *months[12] = { + "January", "Febuary", "March", "April", "May", "June", "July", "August", + "September", "October", "November", "December" +}; + +static int +num_mail(char *path) +{ + long newmail = 0; + int len; + DIR *d, *sd, *ssd; + struct dirent *dir; + char *spath, *sspath; + + d = opendir(path); + if (d == NULL) + return -1; + + while ((dir = readdir(d)) != NULL) { + if ((dir->d_type != DT_DIR && dir->d_type != DT_LNK) || dir->d_name[0] == '.') + continue; + + /* allocate space for path and populate it */ + len = strlen(path) + strlen(dir->d_name) + 3; + spath = (char *)malloc(sizeof(char) * len); + if (snprintf(spath, len, "%s/%s", path, dir->d_name) < 0) + return -2; + + sd = opendir(spath); + if (sd == NULL) + continue; + + /* find all inboxes, and new mail within */ + while ((dir = readdir(sd)) != NULL) { + if (((dir->d_type != DT_DIR && dir->d_type != DT_LNK) || dir->d_name[0] == '.') + || !strcasestr(dir->d_name, "inbox")) + continue; + + len = strlen(spath) + strlen(dir->d_name) + 6; + sspath = (char *)malloc(sizeof(char) * len); + if (snprintf(sspath, len, "%s/%s/new", spath, dir->d_name) < 0) + return -3; + + ssd = opendir(sspath); + free(sspath); + if (ssd == NULL) + continue; + + /* count all the new mail */ + while ((dir = readdir(ssd)) != NULL) + if (dir->d_name[0] != '.') + newmail++; + + closedir(ssd); + } + + closedir(sd); + } + + free(spath); + free(dir); + closedir(d); + + return newmail; +} + +void +motd_subcmd(void *, int argc, char *argv[]) +{ + time_t t = time(NULL); + struct tm tm = *localtime(&t); + char *daysuffix; + int newmail; + + /* allocate space for day string */ + daysuffix = (char *)malloc(sizeof(char) * 3); + + /* day of month */ + if (tm.tm_mday > 3 && tm.tm_mday < 21) + snprintf(daysuffix, 3, "th"); + else if ((tm.tm_mday / 1) % 10 == 1) + snprintf(daysuffix, 3, "st"); + else if ((tm.tm_mday / 1) % 10 == 2) + snprintf(daysuffix, 3, "nd"); + else if ((tm.tm_mday / 1) % 10 == 3) + snprintf(daysuffix, 3, "rd"); + else + snprintf(daysuffix, 3, "th"); + + /* format and return */ + printf("Today is \033[0;35m%s\033[m \033[0;36m%d%s\033[m, \033[0;33m%d\033[0m.\n", + months[tm.tm_mon], tm.tm_mday, daysuffix, tm.tm_year + 1900); + + if ((newmail = num_mail(strcat(getenv("XDG_DATA_HOME"), "/mail"))) > 0) { + printf("You've got \033[0;36m%d\033[m unread \033[0;35memail%s\033[0m!\n", + newmail, newmail > 1 ? "s" : ""); + } + + free(daysuffix); + + return; +} diff --git a/src/subcmds/project.c b/src/subcmds/project.c new file mode 100644 index 0000000..e683706 --- /dev/null +++ b/src/subcmds/project.c @@ -0,0 +1,9 @@ +#include "subcmds.h" + +/* TODO: figure out how to properly store all the licenses in a nice and + * parseable way */ + +static void +license_subcmd(void *, int argc, char *argv[]) +{ +} diff --git a/src/subcmds/subcmds.c b/src/subcmds/subcmds.c new file mode 100644 index 0000000..82fe2cd --- /dev/null +++ b/src/subcmds/subcmds.c @@ -0,0 +1,16 @@ +#include + +#include "api.h" +#include "subcmds.h" + +void +subcmds_subcmd(void *, int argc, char *argv[]) +{ + char **p; + int i; + + p = list_subcmds(); + for (i = 0; p[i]; i++) { + puts(p[i]); + } +} diff --git a/test.lua b/test.lua new file mode 100644 index 0000000..ad5d80f --- /dev/null +++ b/test.lua @@ -0,0 +1,9 @@ +wom.register_subcmd("helloworld", function() + print("hello world") + + for i in wom.fs.dir(".") do + if wom.fs.type(i) == "directory" then + print(i) + end + end +end)