1
0

Implemented generator updates (closes #54) (#55)

This commit is contained in:
Patrick Gaskin
2020-06-23 17:05:22 -04:00
committed by GitHub
parent 40f27b37aa
commit ea1c43b6a1
6 changed files with 160 additions and 42 deletions

View File

@@ -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) {

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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, &lt);
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

View File

@@ -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,27 +171,46 @@ 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);
NM_LOG("global: parsing new config");
nm_config_t *cfg = nm_config_parse(nm_global_menu_config_files, &err);
if (err) {
NM_LOG("... error: %s", err);
NM_LOG("global: freeing old config and replacing with error item");
nm_global_config_replace(NULL, err);
NM_RETURN_ERR("parse config files: %s", err);
if (updated) {
NM_LOG("global: parsing new config");
nm_config_t *cfg = nm_config_parse(nm_global_menu_config_files, &err);
if (err) {
NM_LOG("... error: %s", err);
NM_LOG("global: freeing old config and replacing with error item");
nm_global_config_replace(NULL, err);
NM_RETURN_ERR("parse config files: %s", err);
}
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_LOG("global: running generators");
nm_config_generate(cfg);
bool g_updated = nm_config_generate(nm_global_menu_config, false);
NM_LOG("global:%s generators updated", g_updated ? "" : " no");
NM_LOG("global: freeing old config and replacing with new one");
nm_global_config_replace(cfg, NULL);
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);
NM_LOG("global: done swapping config");
NM_RETURN_OK(true);
#undef NM_ERR_RET
}