#include "common.h" #include "config.h" #include #include #define CONFIG_FILE "/spiffs/config.txt" static int save_config(void); static bool is_end_of_string(int c) { return c <= 0 || c == '\n' || c == '\r'; } /* * Note: the string must be mutable; use strdup() if necessary. * The "separator" allows the string to be separated into multiple * arguments; no other decoding is done. */ static int set_config_string(char *str, unsigned int separator) { char *p, *q; unsigned char c; p = str; do { const char *var = p; do { c = *p++; } while (isalnum(c) || c == '.' || c == '-'); if (c != '=') return -EINVAL; /* Invalid config line (blank, comment...) */ p[-1] = '\0'; q = p; do { c = *q++; } while (!is_end_of_string(c) && c != separator); /* Overlong line */ if (q >= str + MAX_CONFIG_LINE) return -EOVERFLOW; q[-1] = '\0'; size_t nvar = sysvar_find(sysvar_ns_config, var); if (nvar) { setvar_fromstr(nvar, p); } p = q; } while (c == separator); return 0; } static void finish_config_update(bool save) { if (sysvar_changed < sysvar_ns[sysvar_ns_status].first) { if (save) save_config(); /* Can do other things here... */ } sysvar_changed = sysvar_count; } /* * At the moment "url" just allows values to be separated by semicolons; * no other decoding is done, and '&' is not supported. */ int set_config_url_string(const char *str) { char *wstr = strdup(str); if (!wstr) return -ENOMEM; int err = set_config_string(wstr, ';'); free(wstr); finish_config_update(true); return err; } static void skip_rest_of_line(FILE *f) { int c; do { c = getc(f); } while (!is_end_of_string(c)); } /* Calling with with f == NULL just finalizes the update */ int read_config(FILE *f, bool save) { char *linebuf = NULL; int err = -1; if (f) { linebuf = malloc(MAX_CONFIG_LINE); if (!linebuf) { err = -ENOMEM; goto exit; } while (fgets(linebuf, MAX_CONFIG_LINE, f)) { if (set_config_string(linebuf, -1) == -EOVERFLOW) skip_rest_of_line(f); } } err = 0; if (linebuf) free(linebuf); exit: finish_config_update(save); return err; }; int write_sysvars(FILE *f, bool status) { size_t ns = status ? sysvar_ns_status : sysvar_ns_config; const sysvar_ns_t * const nsi = &sysvar_ns[ns]; for (enum sysvar_enum var = nsi->first; var < nsi[1].first; var++) { fputs(sysvar_name[var], f); putc('=', f); fputs(notempty(getvar_tostr(var)), f); putc('\n', f); } return ferror(f) ? -1 : 0; } static int save_config(void) { int err = -ENOENT; FILE *f = fopen(CONFIG_FILE, "w"); if (f) { err = write_sysvars(f, false); fclose(f); } if (err) printf("[CONF] Failed to save configuration (error %d)\n", err); return err; } static const esp_vfs_spiffs_conf_t spiffs_conf = { .base_path = "/spiffs", .partition_label = NULL, .max_files = 4, .format_if_mount_failed = true }; void init_config(void) { if (!esp_spiffs_mounted(spiffs_conf.partition_label)) { esp_err_t err; err = esp_vfs_spiffs_register(&spiffs_conf); if (err) printf("[CONF] Failed to mount %s (error 0x%x)\n", spiffs_conf.base_path, err); } sysvar_reset(sysvar_ns_config); FILE *f = fopen(CONFIG_FILE, "r"); if (!f) printf("[CONF] No configuration file found, using defaults\n"); read_config(f, false); if (f) fclose(f); } void log_config_status(void) { const sysvar_ns_t *nsi = &sysvar_ns[0]; for (enum sysvar_enum var = 1; var < sysvar_count; var++) { if (var >= nsi[1].first) nsi++; logmsg(nsi->name, "%s = %s\n", sysvar_name[var], notempty(getvar_tostr(var))); } }