/* * This both sets a hook function, and calls it on the current value (if any) */ bool SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook) { struct _variable *current, *previous; if (!space) return false; if (!valid_variable_name(name)) return false; for (previous = space, current = space->next; current; previous = current, current = current->next) { if (strcmp(current->name, name) == 0) { /* found entry, so update */ current->assign_hook = hook; (*hook) (current->value); return true; } } /* not present, make new entry */ current = pg_malloc(sizeof *current); current->name = pg_strdup(name); current->value = NULL; current->assign_hook = hook; current->next = NULL; previous->next = current; (*hook) (NULL); return true; }
/* * Attach substitute and/or assign hook functions to the named variable. * If you need only one hook, pass NULL for the other. * * If the variable doesn't already exist, create it with value NULL, just so * we have a place to store the hook function(s). (The substitute hook might * immediately change the NULL to something else; if not, this state is * externally the same as the variable not being defined.) * * The substitute hook, if given, is immediately called on the variable's * value. Then the assign hook, if given, is called on the variable's value. * This is meant to let it update any derived psql state. If the assign hook * doesn't like the current value, it will print a message to that effect, * but we'll ignore it. Generally we do not expect any such failure here, * because this should get called before any user-supplied value is assigned. */ void SetVariableHooks(VariableSpace space, const char *name, VariableSubstituteHook shook, VariableAssignHook ahook) { struct _variable *current, *previous; if (!space || !name) return; if (!valid_variable_name(name)) return; for (previous = space, current = space->next; current; previous = current, current = current->next) { int cmp = strcmp(current->name, name); if (cmp == 0) { /* found entry, so update */ current->substitute_hook = shook; current->assign_hook = ahook; if (shook) current->value = (*shook) (current->value); if (ahook) (void) (*ahook) (current->value); return; } if (cmp > 0) break; /* it's not there */ } /* not present, make new entry */ current = pg_malloc(sizeof *current); current->name = pg_strdup(name); current->value = NULL; current->substitute_hook = shook; current->assign_hook = ahook; current->next = previous->next; previous->next = current; if (shook) current->value = (*shook) (current->value); if (ahook) (void) (*ahook) (current->value); }
/* * Set the variable named "name" to value "value", * or delete it if "value" is NULL. * * Returns true if successful, false if not; in the latter case a suitable * error message has been printed, except for the unexpected case of * space or name being NULL. */ bool SetVariable(VariableSpace space, const char *name, const char *value) { struct _variable *current, *previous; if (!space || !name) return false; if (!valid_variable_name(name)) { /* Deletion of non-existent variable is not an error */ if (!value) return true; psql_error("invalid variable name: \"%s\"\n", name); return false; } for (previous = space, current = space->next; current; previous = current, current = current->next) { int cmp = strcmp(current->name, name); if (cmp == 0) { /* * Found entry, so update, unless assign hook returns false. * * We must duplicate the passed value to start with. This * simplifies the API for substitute hooks. Moreover, some assign * hooks assume that the passed value has the same lifespan as the * variable. Having to free the string again on failure is a * small price to pay for keeping these APIs simple. */ char *new_value = value ? pg_strdup(value) : NULL; bool confirmed; if (current->substitute_hook) new_value = (*current->substitute_hook) (new_value); if (current->assign_hook) confirmed = (*current->assign_hook) (new_value); else confirmed = true; if (confirmed) { if (current->value) pg_free(current->value); current->value = new_value; /* * If we deleted the value, and there are no hooks to * remember, we can discard the variable altogether. */ if (new_value == NULL && current->substitute_hook == NULL && current->assign_hook == NULL) { previous->next = current->next; free(current->name); free(current); } } else if (new_value) pg_free(new_value); /* current->value is left unchanged */ return confirmed; } if (cmp > 0) break; /* it's not there */ } /* not present, make new entry ... unless we were asked to delete */ if (value) { current = pg_malloc(sizeof *current); current->name = pg_strdup(name); current->value = pg_strdup(value); current->substitute_hook = NULL; current->assign_hook = NULL; current->next = previous->next; previous->next = current; } return true; }