37
src/config.c
37
src/config.c
@@ -579,32 +579,39 @@ static nm_config_parse__append__ret_t nm_config_parse__append_generator(nm_confi
|
||||
return NM_CONFIG_PARSE__APPEND__RET_OK;
|
||||
}
|
||||
|
||||
void nm_config_generate(nm_config_t *cfg) {
|
||||
NM_LOG("config: removing any previously generated items");
|
||||
for (nm_config_t *prev = NULL, *cur = cfg; cur; cur = cur->next) {
|
||||
if (prev && cur->generated) { // we can't get rid of the first item and it won't be generated anyways (plus doing it this way simplifies the loop)
|
||||
prev->next = cur->next; // skip the generated item
|
||||
cur->next = NULL; // so we only free that item
|
||||
nm_config_free(cur);
|
||||
cur = prev; // continue with the new current item
|
||||
} else {
|
||||
prev = cur; // the item was kept, so it's now the previous one
|
||||
}
|
||||
}
|
||||
bool nm_config_generate(nm_config_t *cfg, bool force_update) {
|
||||
bool changed = false;
|
||||
|
||||
NM_LOG("config: running generators");
|
||||
for (nm_config_t *cur = cfg; cur; cur = cur->next) {
|
||||
if (cur->type == NM_CONFIG_TYPE_GENERATOR) {
|
||||
NM_LOG("config: running generator %s:%s", cur->value.generator->desc, cur->value.generator->arg);
|
||||
|
||||
if (force_update)
|
||||
cur->value.generator->time = (struct timespec){0, 0};
|
||||
|
||||
size_t sz;
|
||||
nm_menu_item_t **items = nm_generator_do(cur->value.generator, &sz);
|
||||
if (!items) {
|
||||
NM_LOG("config: ... no items generated");
|
||||
NM_LOG("config: ... no new items generated");
|
||||
if (force_update)
|
||||
NM_LOG("config: ... possible bug: no items were generated even with force_update");
|
||||
continue;
|
||||
}
|
||||
|
||||
NM_LOG("config: ... %zu items generated", sz);
|
||||
NM_LOG("config: ... %zu items generated, removing previously generated items and replacing with new ones", sz);
|
||||
|
||||
changed = true;
|
||||
|
||||
// remove all generated items immediately after the generator
|
||||
for (nm_config_t *t_prev = cur, *t_cur = cur->next; t_cur && t_cur->generated; t_cur = t_cur->next) {
|
||||
t_prev->next = t_cur->next; // skip the generated item
|
||||
t_cur->next = NULL; // so we only free that item
|
||||
nm_config_free(t_cur);
|
||||
t_cur = t_prev; // continue with the new current item
|
||||
}
|
||||
|
||||
// add the new ones
|
||||
for (ssize_t i = sz-1; i >= 0; i--) {
|
||||
nm_config_t *tmp = calloc(1, sizeof(nm_config_t));
|
||||
tmp->type = NM_CONFIG_TYPE_MENU_ITEM;
|
||||
@@ -632,6 +639,8 @@ void nm_config_generate(nm_config_t *cfg) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
nm_menu_item_t **nm_config_get_menu(nm_config_t *cfg, size_t *n_out) {
|
||||
|
||||
@@ -39,8 +39,9 @@ void nm_config_files_free(nm_config_file_t *files);
|
||||
nm_config_t *nm_config_parse(nm_config_file_t *files, char **err_out);
|
||||
|
||||
// nm_config_generate runs all generators synchronously and sequentially. Any
|
||||
// previously generated items are automatically removed.
|
||||
void nm_config_generate(nm_config_t *cfg);
|
||||
// previously generated items are automatically removed if updates are required.
|
||||
// If the config was modified, true is returned.
|
||||
bool nm_config_generate(nm_config_t *cfg, bool force_update);
|
||||
|
||||
// nm_config_get_menu gets a malloc'd array of pointers to the menu items
|
||||
// defined in the config. These pointers will be valid until nm_config_free is
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "generator.h"
|
||||
@@ -13,12 +14,19 @@ nm_menu_item_t **nm_generator_do(nm_generator_t *gen, size_t *sz_out) {
|
||||
NM_LOG("generator: running generator (%s) (%s) (%d) (%p)", gen->desc, gen->arg, gen->loc, gen->generate);
|
||||
|
||||
char *err;
|
||||
struct timespec old = gen->time;
|
||||
size_t sz = (size_t)(-1); // this should always be set by generate upon success, but we'll initialize it just in case
|
||||
nm_menu_item_t **items = gen->generate(gen->arg, &sz, &err);
|
||||
nm_menu_item_t **items = gen->generate(gen->arg, &gen->time, &sz, &err);
|
||||
|
||||
if (items && old.tv_sec == gen->time.tv_sec && old.tv_nsec == gen->time.tv_nsec)
|
||||
NM_LOG("generator: bug: new items were returned, but time wasn't changed");
|
||||
|
||||
if (!old.tv_sec && !old.tv_nsec && !err && !items)
|
||||
NM_LOG("generator: warning: no existing items (time == 0), but no new items or error were returned");
|
||||
|
||||
if (err) {
|
||||
if (items)
|
||||
NM_LOG("generator: warning: items should be null on error");
|
||||
NM_LOG("generator: bug: items should be null on error");
|
||||
|
||||
NM_LOG("generator: generator error (%s) (%s), replacing with error item: %s", gen->desc, gen->arg, err);
|
||||
sz = 1;
|
||||
@@ -34,15 +42,18 @@ nm_menu_item_t **nm_generator_do(nm_generator_t *gen, size_t *sz_out) {
|
||||
free(err);
|
||||
}
|
||||
|
||||
if (!err && !items && (old.tv_sec != gen->time.tv_sec || old.tv_nsec != gen->time.tv_nsec))
|
||||
NM_LOG("generator: bug: the time should have been updated if new items were returned");
|
||||
|
||||
if (items) {
|
||||
if (sz == (size_t)(-1))
|
||||
NM_LOG("generator: warning: size should have been set by generate, but wasn't");
|
||||
NM_LOG("generator: bug: size should have been set by generate, but wasn't");
|
||||
if (!sz)
|
||||
NM_LOG("generator: warning: items should be null when size is 0");
|
||||
NM_LOG("generator: bug: items should be null when size is 0");
|
||||
|
||||
for (size_t i = 0; i < sz; i++) {
|
||||
if (items[i]->loc)
|
||||
NM_LOG("generator: warning: generator should not set the menu item location, as it will be overridden");
|
||||
NM_LOG("generator: bug: generator should not set the menu item location, as it will be overridden");
|
||||
|
||||
items[i]->loc = gen->loc;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include "menu.h"
|
||||
|
||||
// nm_generator_fn_t generates menu items. It must return a malloc'd array of
|
||||
@@ -13,29 +14,44 @@ extern "C" {
|
||||
// set if provided, NULL must be returned, and sz_out is undefined. If no
|
||||
// entries are generated, NULL must be returned with sz_out set to 0. All
|
||||
// strings should also be malloc'd.
|
||||
typedef nm_menu_item_t **(*nm_generator_fn_t)(const char *arg, size_t *sz_out, char **err_out);
|
||||
//
|
||||
// time_in_out will not be NULL, and contains zero or the last modification time
|
||||
// for the generator. If it is zero, the generator should generate the items as
|
||||
// usual and update the time. If it is nonzero, the generator should return NULL
|
||||
// without making changes if the time is up to date (the check should be as
|
||||
// quick as possible), and if not, it should update the items and update the
|
||||
// time to match. If the generator does not have a way of checking for updates
|
||||
// quickly, it should only update the item and set the time to a nonzero value
|
||||
// if the time is zero, and return NULL if the time is nonzero. Note that this
|
||||
// time doesn't have to account for different arguments or multiple instances,
|
||||
// as changes in those will always cause the time to be set to zero.
|
||||
typedef nm_menu_item_t **(*nm_generator_fn_t)(const char *arg, struct timespec *time_in_out, size_t *sz_out, char **err_out);
|
||||
|
||||
typedef struct {
|
||||
char *desc; // only used for making the errors more meaningful (it is the title)
|
||||
char *arg;
|
||||
nm_menu_location_t loc;
|
||||
nm_generator_fn_t generate; // should be as quick as possible with a short timeout, as it will block startup
|
||||
struct timespec time;
|
||||
} nm_generator_t;
|
||||
|
||||
// nm_generator_do runs a generator and returns the generated items, if any, or
|
||||
// an item which shows the error returned by the generator.
|
||||
// an item which shows the error returned by the generator. If NULL is returned,
|
||||
// no items needed to be updated (set time to zero to force an update) (sz_out
|
||||
// is undefined).
|
||||
nm_menu_item_t **nm_generator_do(nm_generator_t *gen, size_t *sz_out);
|
||||
|
||||
#define NM_GENERATOR(name) nm_generator_##name
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define NM_GENERATOR_(name) extern "C" nm_menu_item_t **NM_GENERATOR(name)(const char *arg, size_t *sz_out, char **err_out)
|
||||
#define NM_GENERATOR_(name) extern "C" nm_menu_item_t **NM_GENERATOR(name)(const char *arg, struct timespec *time_in_out, size_t *sz_out, char **err_out)
|
||||
#else
|
||||
#define NM_GENERATOR_(name) nm_menu_item_t **NM_GENERATOR(name)(const char *arg, size_t *sz_out, char **err_out)
|
||||
#define NM_GENERATOR_(name) nm_menu_item_t **NM_GENERATOR(name)(const char *arg, struct timespec *time_in_out, size_t *sz_out, char **err_out)
|
||||
#endif
|
||||
|
||||
#define NM_GENERATORS \
|
||||
X(_test) \
|
||||
X(_test_time) \
|
||||
X(kfmon)
|
||||
|
||||
#define X(name) NM_GENERATOR_(name);
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
#define _GNU_SOURCE // asprintf
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "generator.h"
|
||||
#include "menu.h"
|
||||
@@ -11,6 +17,9 @@
|
||||
NM_GENERATOR_(_test) {
|
||||
#define NM_ERR_RET NULL
|
||||
|
||||
if (time_in_out->tv_sec || time_in_out->tv_nsec)
|
||||
NM_RETURN_OK(NULL); // updates not supported (or needed, for that matter)
|
||||
|
||||
char *tmp;
|
||||
long n = strtol(arg, &tmp, 10);
|
||||
NM_ASSERT(*arg && !*tmp && n >= 0 && n <= 10, "invalid count '%s': must be an integer from 1-10", arg);
|
||||
@@ -31,15 +40,66 @@ NM_GENERATOR_(_test) {
|
||||
items[i]->action->on_success = true;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, time_in_out); // note: any nonzero value would work, but this generator is for testing and as an example
|
||||
|
||||
*sz_out = n;
|
||||
NM_RETURN_OK(items);
|
||||
|
||||
#undef NM_ERR_RET
|
||||
}
|
||||
|
||||
NM_GENERATOR_(_test_time) {
|
||||
#define NM_ERR_RET NULL
|
||||
|
||||
if (arg && *arg)
|
||||
NM_RETURN_ERR("_test_time does not accept any arguments");
|
||||
|
||||
// note: this used as an example and for testing
|
||||
|
||||
NM_LOG("_test_time: checking for updates");
|
||||
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
|
||||
struct tm lt;
|
||||
localtime_r(&ts.tv_sec, <);
|
||||
|
||||
if (time_in_out->tv_sec && ts.tv_sec - time_in_out->tv_sec < 10) {
|
||||
NM_LOG("_test_time: last update is nonzero and last update time is < 10s, skipping");
|
||||
NM_RETURN_OK(NULL);
|
||||
}
|
||||
|
||||
NM_LOG("_test_time: updating");
|
||||
|
||||
// note: you'd usually do the slower logic here
|
||||
|
||||
nm_menu_item_t **items = calloc(1, sizeof(nm_menu_item_t*));
|
||||
items[0] = calloc(1, sizeof(nm_menu_item_t));
|
||||
asprintf(&items[0]->lbl, "%d:%02d:%02d", lt.tm_hour, lt.tm_min, lt.tm_sec);
|
||||
items[0]->action = calloc(1, sizeof(nm_menu_action_t));
|
||||
items[0]->action->act = NM_ACTION(dbg_msg);
|
||||
items[0]->action->arg = strdup("It worked!");
|
||||
items[0]->action->on_failure = true;
|
||||
items[0]->action->on_success = true;
|
||||
|
||||
time_in_out->tv_sec = ts.tv_sec;
|
||||
|
||||
*sz_out = 1;
|
||||
NM_RETURN_OK(items);
|
||||
|
||||
#undef NM_ERR_RET
|
||||
}
|
||||
|
||||
NM_GENERATOR_(kfmon) {
|
||||
#define NM_ERR_RET NULL
|
||||
|
||||
struct stat sb;
|
||||
if (stat(KFMON_IPC_SOCKET, &sb))
|
||||
NM_RETURN_ERR("error checking '%s': stat: %s", KFMON_IPC_SOCKET, strerror(errno));
|
||||
|
||||
if (time_in_out->tv_sec == sb.st_mtim.tv_sec && time_in_out->tv_nsec == sb.st_mtim.tv_nsec)
|
||||
NM_RETURN_OK(NULL);
|
||||
|
||||
// Default with no arg or an empty arg is to request a gui-listing
|
||||
const char *kfmon_cmd = NULL;
|
||||
if (!arg || !*arg || !strcmp(arg, "gui")) {
|
||||
@@ -85,6 +145,7 @@ NM_GENERATOR_(kfmon) {
|
||||
// Destroy the list now that we've dumped it into an array of nm_menu_item_t
|
||||
kfmon_teardown_list(&list);
|
||||
|
||||
*time_in_out = sb.st_mtim;
|
||||
NM_RETURN_OK(items);
|
||||
|
||||
#undef NM_ERR_RET
|
||||
|
||||
38
src/init.c
38
src/init.c
@@ -160,6 +160,7 @@ static void nm_global_config_replace(nm_config_t *cfg, const char *err) {
|
||||
|
||||
bool nm_global_config_update(char **err_out) {
|
||||
#define NM_ERR_RET true
|
||||
|
||||
char *err;
|
||||
|
||||
NM_LOG("global: scanning for config files");
|
||||
@@ -170,11 +171,9 @@ bool nm_global_config_update(char **err_out) {
|
||||
nm_global_config_replace(NULL, err);
|
||||
NM_RETURN_ERR("scan for config files: %s", err);
|
||||
}
|
||||
|
||||
NM_LOG("global:%s changes detected", updated ? "" : " no");
|
||||
if (!updated)
|
||||
NM_RETURN_OK(false);
|
||||
|
||||
if (updated) {
|
||||
NM_LOG("global: parsing new config");
|
||||
nm_config_t *cfg = nm_config_parse(nm_global_menu_config_files, &err);
|
||||
if (err) {
|
||||
@@ -184,13 +183,34 @@ bool nm_global_config_update(char **err_out) {
|
||||
NM_RETURN_ERR("parse config files: %s", err);
|
||||
}
|
||||
|
||||
NM_LOG("global: running generators");
|
||||
nm_config_generate(cfg);
|
||||
|
||||
NM_LOG("global: freeing old config and replacing with new one");
|
||||
NM_LOG("global: config updated, freeing old config and replacing with new one");
|
||||
nm_global_config_replace(cfg, NULL);
|
||||
|
||||
NM_LOG("global: done swapping config");
|
||||
NM_RETURN_OK(true);
|
||||
}
|
||||
|
||||
NM_LOG("global: running generators");
|
||||
bool g_updated = nm_config_generate(nm_global_menu_config, false);
|
||||
NM_LOG("global:%s generators updated", g_updated ? "" : " no");
|
||||
|
||||
if (g_updated) {
|
||||
NM_LOG("global: generators updated, freeing old items and replacing with new ones");
|
||||
|
||||
if (nm_global_menu_config_n)
|
||||
nm_global_menu_config_n = 0;
|
||||
|
||||
if (nm_global_menu_config_items) {
|
||||
free(nm_global_menu_config_items);
|
||||
nm_global_menu_config_items = NULL;
|
||||
}
|
||||
|
||||
nm_global_menu_config_items = nm_config_get_menu(nm_global_menu_config, &nm_global_menu_config_n);
|
||||
if (!nm_global_menu_config_items)
|
||||
NM_LOG("could not allocate memory");
|
||||
|
||||
NM_LOG("done replacing items");
|
||||
}
|
||||
|
||||
NM_RETURN_OK(updated || g_updated);
|
||||
|
||||
#undef NM_ERR_RET
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user