/** * cs_he_initial_get - Get the initial, or parent, value of a config item * @param cs Config items * @param he HashElem representing config item * @param result Buffer for results or error messages * @retval int Result, e.g. #CSR_SUCCESS * * If a config item is inherited from another, then this will get the parent's * value. Otherwise, it will get the config item's initial value. */ int cs_he_initial_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result) { if (!cs || !he) return CSR_ERR_CODE; struct Inheritance *i = NULL; const struct ConfigDef *cdef = NULL; const struct ConfigSetType *cst = NULL; if (he->type & DT_INHERITED) { i = he->data; cdef = i->parent->data; cst = cs_get_type_def(cs, i->parent->type); } else { cdef = he->data; cst = cs_get_type_def(cs, he->type); } if (!cst) { mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, DTYPE(he->type)); return CSR_ERR_CODE; } return cst->string_get(cs, NULL, cdef, result); }
/** * cs_str_native_set - Natively set the value of a string config item * @param cs Config items * @param name Name of config item * @param value Native pointer/value to set * @param err Buffer for error messages * @retval int Result, e.g. #CSR_SUCCESS */ int cs_str_native_set(const struct ConfigSet *cs, const char *name, intptr_t value, struct Buffer *err) { if (!cs || !name) return CSR_ERR_CODE; /* LCOV_EXCL_LINE */ struct HashElem *he = cs_get_elem(cs, name); if (!he) { mutt_buffer_printf(err, "Unknown var '%s'", name); return CSR_ERR_UNKNOWN; } const struct ConfigDef *cdef = NULL; const struct ConfigSetType *cst = NULL; void *var = NULL; if (he->type & DT_INHERITED) { struct Inheritance *i = he->data; cdef = i->parent->data; var = &i->var; cst = cs_get_type_def(cs, i->parent->type); } else { cdef = he->data; var = cdef->var; cst = cs_get_type_def(cs, he->type); } if (!cst) { mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type); return CSR_ERR_CODE; } int rc = cst->native_set(cs, var, cdef, value, err); if (CSR_RESULT(rc) == CSR_SUCCESS) { if (he->type & DT_INHERITED) he->type = cdef->type | DT_INHERITED; if (!(rc & CSR_SUC_NO_CHANGE)) cs_notify_listeners(cs, he, cdef->name, CE_SET); } return rc; }
/** * destroy - Callback function for the Hash Table - Implements ::hashelem_free_t * @param type Object type, e.g. #DT_STRING * @param obj Object to destroy * @param data ConfigSet associated with the object */ static void destroy(int type, void *obj, intptr_t data) { if (!obj || (data == 0)) return; /* LCOV_EXCL_LINE */ struct ConfigSet *cs = (struct ConfigSet *) data; const struct ConfigSetType *cst = NULL; if (type & DT_INHERITED) { struct Inheritance *i = obj; FREE(&i->name); FREE(&i); } else { struct ConfigDef *cdef = obj; cst = cs_get_type_def(cs, type); if (cst && cst->destroy) cst->destroy(cs, cdef->var, cdef); /* If we allocated the initial value, clean it up */ if (cdef->type & DT_INITIAL_SET) FREE(&cdef->initial); } }
/** * cs_he_initial_set - Set the initial value of a config item * @param cs Config items * @param he HashElem representing config item * @param value Value to set * @param err Buffer for error messages * @retval int Result, e.g. #CSR_SUCCESS */ int cs_he_initial_set(const struct ConfigSet *cs, struct HashElem *he, const char *value, struct Buffer *err) { if (!cs || !he) return CSR_ERR_CODE; struct ConfigDef *cdef = NULL; const struct ConfigSetType *cst = NULL; if (he->type & DT_INHERITED) { struct Inheritance *i = he->data; cdef = i->parent->data; mutt_debug(LL_DEBUG1, "Variable '%s' is inherited type\n", cdef->name); return CSR_ERR_CODE; } cdef = he->data; cst = cs_get_type_def(cs, he->type); if (!cst) { mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type); return CSR_ERR_CODE; } int rc = cst->string_set(cs, NULL, cdef, value, err); if (CSR_RESULT(rc) != CSR_SUCCESS) return rc; cs_notify_listeners(cs, he, he->key.strkey, CE_INITIAL_SET); return CSR_SUCCESS; }
/** * reg_one_var - Register one config item * @param cs Config items * @param cdef Variable definition * @param err Buffer for error messages * @retval ptr New HashElem representing the config item */ static struct HashElem *reg_one_var(const struct ConfigSet *cs, struct ConfigDef *cdef, struct Buffer *err) { if (!cs || !cdef) return NULL; /* LCOV_EXCL_LINE */ if (cdef->type == DT_SYNONYM) return create_synonym(cs, cdef, err); const struct ConfigSetType *cst = cs_get_type_def(cs, cdef->type); if (!cst) { mutt_buffer_printf(err, "Variable '%s' has an invalid type %d", cdef->name, cdef->type); return NULL; } struct HashElem *he = mutt_hash_typed_insert(cs->hash, cdef->name, cdef->type, (void *) cdef); if (!he) return NULL; /* LCOV_EXCL_LINE */ if (cst && cst->reset) cst->reset(cs, cdef->var, cdef, err); return he; }
/** * cs_he_string_set - Set a config item by string * @param cs Config items * @param he HashElem representing config item * @param value Value to set * @param err Buffer for error messages * @retval int Result, e.g. #CSR_SUCCESS */ int cs_he_string_set(const struct ConfigSet *cs, struct HashElem *he, const char *value, struct Buffer *err) { if (!cs || !he) return CSR_ERR_CODE; struct ConfigDef *cdef = NULL; const struct ConfigSetType *cst = NULL; void *var = NULL; if (he->type & DT_INHERITED) { struct Inheritance *i = he->data; cdef = i->parent->data; var = &i->var; cst = cs_get_type_def(cs, i->parent->type); } else { cdef = he->data; var = cdef->var; cst = cs_get_type_def(cs, he->type); } if (!cst) { mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type); return CSR_ERR_CODE; } if (!var) return CSR_ERR_CODE; /* LCOV_EXCL_LINE */ int rc = cst->string_set(cs, var, cdef, value, err); if (CSR_RESULT(rc) != CSR_SUCCESS) return rc; if (he->type & DT_INHERITED) { struct Inheritance *i = he->data; he->type = i->parent->type | DT_INHERITED; } if (!(rc & CSR_SUC_NO_CHANGE)) cs_notify_listeners(cs, he, he->key.strkey, CE_SET); return rc; }
/** * cs_he_string_get - Get a config item as a string * @param cs Config items * @param he HashElem representing config item * @param result Buffer for results or error messages * @retval int Result, e.g. #CSR_SUCCESS */ int cs_he_string_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result) { if (!cs || !he) return CSR_ERR_CODE; struct Inheritance *i = NULL; const struct ConfigDef *cdef = NULL; const struct ConfigSetType *cst = NULL; void *var = NULL; if (he->type & DT_INHERITED) { i = he->data; cdef = i->parent->data; cst = cs_get_type_def(cs, i->parent->type); } else { cdef = he->data; cst = cs_get_type_def(cs, he->type); } if ((he->type & DT_INHERITED) && (DTYPE(he->type) != 0)) { var = &i->var; /* Local value */ } else { var = cdef->var; /* Normal var */ } if (!cst) { mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, DTYPE(he->type)); return CSR_ERR_CODE; } return cst->string_get(cs, var, cdef, result); }
/** * cs_he_native_get - Natively get the value of a HashElem config item * @param cs Config items * @param he HashElem representing config item * @param err Buffer for results or error messages * @retval intptr_t Native pointer/value */ intptr_t cs_he_native_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *err) { if (!cs || !he) return INT_MIN; struct Inheritance *i = NULL; const struct ConfigDef *cdef = NULL; const struct ConfigSetType *cst = NULL; void *var = NULL; if (he->type & DT_INHERITED) { i = he->data; cdef = i->parent->data; cst = cs_get_type_def(cs, i->parent->type); } else { cdef = he->data; cst = cs_get_type_def(cs, he->type); } if ((he->type & DT_INHERITED) && (DTYPE(he->type) != 0)) { var = &i->var; } else { var = cdef->var; } if (!cst) { mutt_buffer_printf(err, "Variable '%s' has an invalid type %d", cdef->name, he->type); return INT_MIN; } return cst->native_get(cs, var, cdef, err); }
/** * cs_he_reset - Reset a config item to its initial value * @param cs Config items * @param he HashElem representing config item * @param err Buffer for error messages * @retval int Result, e.g. #CSR_SUCCESS */ int cs_he_reset(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *err) { if (!cs || !he) return CSR_ERR_CODE; /* An inherited var that's already pointing to its parent. * Return 'success', but don't send a notification. */ if ((he->type & DT_INHERITED) && (DTYPE(he->type) == 0)) return CSR_SUCCESS; const struct ConfigSetType *cst = NULL; const struct ConfigDef *cdef = NULL; int rc = CSR_SUCCESS; if (he->type & DT_INHERITED) { struct Inheritance *i = he->data; cst = cs_get_type_def(cs, i->parent->type); cdef = i->parent->data; if (cst && cst->destroy) cst->destroy(cs, (void **) &i->var, cdef); he->type = DT_INHERITED; } else { cst = cs_get_type_def(cs, he->type); cdef = he->data; if (cst) rc = cst->reset(cs, cdef->var, cdef, err); } if ((CSR_RESULT(rc) == CSR_SUCCESS) && !(rc & CSR_SUC_NO_CHANGE)) cs_notify_listeners(cs, he, he->key.strkey, CE_RESET); return rc; }
/** * dump_config_neo - Dump the config in the style of NeoMutt * @param cs Config items * @param he HashElem representing config item * @param value Current value of the config item * @param initial Initial value of the config item * @param flags Flags, see #ConfigDumpFlags * @param fp File pointer to write to */ void dump_config_neo(struct ConfigSet *cs, struct HashElem *he, struct Buffer *value, struct Buffer *initial, ConfigDumpFlags flags, FILE *fp) { if (!he || !value || !initial || !fp) return; const char *name = he->key.strkey; if ((flags & CS_DUMP_ONLY_CHANGED) && (mutt_str_strcmp(value->data, initial->data) == 0)) return; if (he->type == DT_SYNONYM) { const struct ConfigDef *cdef = he->data; const char *syn = (const char *) cdef->initial; fprintf(fp, "# synonym: %s -> %s\n", name, syn); return; } bool show_name = !(flags & CS_DUMP_HIDE_NAME); bool show_value = !(flags & CS_DUMP_HIDE_VALUE); if (show_name && show_value) fprintf(fp, "set "); if (show_name) fprintf(fp, "%s", name); if (show_name && show_value) fprintf(fp, " = "); if (show_value) fprintf(fp, "%s", value->data); if (show_name || show_value) fprintf(fp, "\n"); if (flags & CS_DUMP_SHOW_DEFAULTS) { const struct ConfigSetType *cst = cs_get_type_def(cs, he->type); if (cst) fprintf(fp, "# %s %s %s\n", cst->name, name, value->data); } }
void config_set(void) { log_line(__func__); struct Buffer err; mutt_buffer_init(&err); err.dsize = 256; err.data = mutt_mem_calloc(1, err.dsize); mutt_buffer_reset(&err); struct ConfigSet *cs = cs_new(30); if (!TEST_CHECK(cs != NULL)) return; cs_add_listener(cs, log_listener); cs_add_listener(cs, log_listener); /* dupe */ cs_remove_listener(cs, log_listener); cs_remove_listener(cs, log_listener); /* non-existant */ const struct ConfigSetType cst_dummy = { "dummy", NULL, NULL, NULL, NULL, NULL, NULL, }; if (TEST_CHECK(!cs_register_type(cs, DT_STRING, &cst_dummy))) { TEST_MSG("Expected error\n"); } else { TEST_MSG("This test should have failed\n"); return; } const struct ConfigSetType cst_dummy2 = { "dummy2", dummy_string_set, dummy_string_get, dummy_native_set, dummy_native_get, dummy_reset, dummy_destroy, }; if (TEST_CHECK(!cs_register_type(cs, 25, &cst_dummy2))) { TEST_MSG("Expected error\n"); } else { TEST_MSG("This test should have failed\n"); return; } bool_init(cs); bool_init(cs); /* second one should fail */ if (TEST_CHECK(!cs_register_variables(cs, Vars, 0))) { TEST_MSG("Expected error\n"); } else { TEST_MSG("This test should have failed\n"); return; } const char *name = "Unknown"; int result = cs_str_string_set(cs, name, "hello", &err); if (TEST_CHECK(CSR_RESULT(result) == CSR_ERR_UNKNOWN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 1\n"); return; } result = cs_str_string_get(cs, name, &err); if (TEST_CHECK(CSR_RESULT(result) == CSR_ERR_UNKNOWN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 2\n"); return; } result = cs_str_native_set(cs, name, IP "hello", &err); if (TEST_CHECK(CSR_RESULT(result) == CSR_ERR_UNKNOWN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 3\n"); return; } intptr_t native = cs_str_native_get(cs, name, &err); if (TEST_CHECK(native == INT_MIN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 4\n"); return; } struct HashElem *he = cs_get_elem(cs, "Banana"); if (!TEST_CHECK(he != NULL)) return; set_list(cs); const struct ConfigSetType *cst = cs_get_type_def(cs, 15); if (!TEST_CHECK(!cst)) return; cs_free(&cs); FREE(&err.data); log_line(__func__); }