summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormvglasow <michael -at- vonglasow.com>2015-11-11 14:26:50 +0100
committermvglasow <michael -at- vonglasow.com>2015-11-11 16:00:15 +0100
commit033eabc0e8d2d14c94caec35e65635bbe94f0538 (patch)
tree52c88b4f332b89be0d8b306668c61e64e889be39
parent36b16c73223a09e2912c2a0918807cbccec60c75 (diff)
downloadnavit-033eabc0e8d2d14c94caec35e65635bbe94f0538.tar.gz
Refactor:core:Some more documentation and logging in command.c
Signed-off-by: mvglasow <michael -at- vonglasow.com>
-rw-r--r--navit/command.c199
1 files changed, 188 insertions, 11 deletions
diff --git a/navit/command.c b/navit/command.c
index e1d49293b..f469e9c2b 100644
--- a/navit/command.c
+++ b/navit/command.c
@@ -29,13 +29,20 @@ osd[@type=="xxx"].active=0;osd[@type=="yyy"].active=0
struct result {
- struct attr attr;
+ struct attr attr; /**< The attribute */
double val;
- const char *var;
- int varlen;
- const char *attrn;
- int attrnlen;
- int allocated;
+ const char *var; /**< If {@code allocated} is false, the name of the object to be resolved.
+ * Else, it is the name of the object successfully retrieved and stored in
+ * {@code attr}, or {@code NULL} if retrieval failed.
+ * Only the first {@code varlen} characters are significant.
+ */
+ int varlen; /**< Number of significant characters in {@code var} */
+ const char *attrn; /**< The name of an object that has been resolved but not yet retrieved,
+ * {@code NULL} otherwise. Only the first {@code attrnlen} characters are
+ * significant.
+ */
+ int attrnlen; /**< Number of significant characters in {@code attrn} */
+ int allocated; /**< Whether the result has been calculated */
};
struct result_list {
@@ -212,6 +219,21 @@ command_attr_type(struct result *res)
return attr_from_name(attrn);
}
+/**
+ * @brief Retrieves an attribute from an object.
+ *
+ * This function will retrieve the first matching attribute by calling the {@code get_attr} method for
+ * the object type. If {@code object} does not refer to a valid object, or the {@code get_attr} method
+ * for the object type could not be defined, the function fails and zero is returned.
+ *
+ * @param ctx The context (ignored)
+ * @param object The object for which the attribute is to be retrieved.
+ * @param attr_type The type of attribute to retrieve
+ * @param ret Points to a {@code struct attr} to which the attribute will be copied
+ *
+ * @return True if a matching attribute was found, false if no matching attribute was found or an error
+ * occurred
+ */
static int
command_object_get_attr(struct context *ctx, struct attr *object, enum attr_type attr_type, struct attr *ret)
{
@@ -245,6 +267,27 @@ command_object_remove_attr(struct context *ctx, struct attr *object, struct attr
}
+/**
+ * @brief Retrieves the current value of an attribute and stores it in {@code res}.
+ *
+ * If {@code ctx->skip} is true, the function aborts and no action is taken.
+ *
+ * Before calling this function, object references in {@code res} must be resolved. That is,
+ * {@code res->attr} holds a copy of {@code ctx->attr} and the first {@code res->attrnlen} characters of
+ * {@code res->attrn} correspond to the object name.
+ *
+ * After this function completes, {@code res->allocated} is true, and {@code res->attrn} and
+ * {@code res->attrnlen} are reset.
+ *
+ * If the attribute was successfully retrieved, the first {@code res->varlen} characters of
+ * {@code res->var} correspond to an object name and {@code res->attr} holds the attribute.
+ *
+ * If the attribute could not be retrieved, {@code res->attr.type} is set to {@code attr_none}, and
+ * {@code res->var} and {@code res->varlen} are reset.
+ *
+ * @param ctx The context
+ * @param res The result
+ */
static void
command_get_attr(struct context *ctx, struct result *res)
{
@@ -296,6 +339,20 @@ command_set_attr(struct context *ctx, struct result *res, struct result *newres)
*res=*newres;
}
+/**
+ * @brief Resolves an object reference.
+ *
+ * Prior to calling this function, {@code res} must contain a valid, unresolved object reference:
+ * {@code res->attr.type} must be {@code attr_none}, and the first {@code res->varlen} characters of
+ * {@code res->var} must correspond to an object name.
+ *
+ * After the function returns, {@code res->attr} holds a copy of {@code ctx->attr} and the first
+ * {@code res->attrnlen} characters of {@code res->attrn} correspond to the object name.
+ * {@code res->var} and {@code res->varlen} are reset.
+ *
+ * @param ctx The context
+ * @param res The result
+ */
static void
resolve_object(struct context *ctx, struct result *res)
{
@@ -308,6 +365,24 @@ resolve_object(struct context *ctx, struct result *res)
}
}
+/**
+ * @brief Resolves and retrieves an object and stores it in {@code res}.
+ *
+ * Prior to calling this function, {@code res} must contain a valid, unresolved object reference:
+ * {@code res->attr.type} must be {@code attr_none}, and the first {@code res->varlen} characters of
+ * {@code res->var} must correspond to an object name.
+ *
+ * If {@code ctx->skip} is true, the object reference will be resolved but the object will not be
+ * retrieved: the first {@code res->attrnlen} characters of {@code res->attrn} correspond to the object
+ * name after the function returns, while {@code res->var} and {@code res->varlen} are reset.
+ *
+ * If {@code ctx->skip} is false, {@code res->allocated} is true after this function completes. The
+ * object is stored in {@code res->attr} if it was successfully retrieved, otherwise {@code res->var}
+ * and {@code res->varlen} are reset.
+ *
+ * @param ctx The context
+ * @param res The result
+ */
static void
resolve(struct context *ctx, struct result *res)
{
@@ -567,6 +642,37 @@ result_set(struct context *ctx, enum set_type set_type, const char *op, int len,
ctx->error=internal;
}
+/**
+ * @brief Evaluates a value and stores its result.
+ *
+ * This function evaluates the first value in {@code ctx->expr}. A value can be either an object name
+ * (such as {@code vehicle.position_speed}) or a literal value.
+ *
+ * If evaluation is successful, the result is stored in {@code res->attr}.
+ *
+ * If an object name is encountered, the result has an attribute type of {@code attr_none} and the first
+ * {@code res->varlen} characters of {@code res->var} will point to the object name.
+ *
+ * If a literal value is encountered, the result's attribute type is set to the corresponding generic
+ * data type and its value is stored with the attribute.
+ *
+ * After this function returns, {@code ctx->expr} contains the rest of the expression string, which was
+ * not evaluated. Leading spaces before the value will be discarded with the value.
+ *
+ * If {@code ctx->expr}, after eliminating any leading whitespace, does not begin with a valid value,
+ * one of the following errors is stored in {@code ctx->error}:
+ * <ul>
+ * <li>{@code illegal_number_format} An illegal number format, such as a second decimal dot, was
+ * encountered.</li>
+ * <li>{@code missing_double_quote} A double quote without a matching second double quote was found.</li>
+ * <li>{@code eof_reached} The expression string is empty.</li>
+ * <li>{@code illegal_character} The expression string begins with a character which is illegal in a
+ * value. This may happen when the expression string begins with an operator.</li>
+ * </ul>
+ *
+ * @param ctx The context to evaluate
+ * @param res Points to a {@code struct res} in which the result will be stored
+ */
static void
eval_value(struct context *ctx, struct result *res) {
const char *op;
@@ -650,11 +756,37 @@ eval_value(struct context *ctx, struct result *res) {
if (!*op)
ctx->error=eof_reached;
else {
- dbg(lvl_error,"illegal character 0x%x\n",*op);
+ /*
+ * If we get here, ctx->expr does not begin with a variable or a literal value. This is not an
+ * error if this function is being called to test if an expression begins with a value.
+ */
+ dbg(lvl_debug, "character 0x%x is illegal in a value\n",*op);
ctx->error=illegal_character;
}
}
+/**
+ * @brief Retrieves the next object reference from an expression.
+ *
+ * This function scans the expression string {@code ctx->expr} for the next object reference. Anything
+ * other than an object reference (whitespace characters, literal values, operators and even illegal
+ * characters) is discarded until either the end of the string is reached or an object reference is
+ * encountered.
+ *
+ * After this function completes successfully, {@code res->attr.type} is {@code attr_none} and the first
+ * {@code res->varlen} characters of {@code res->var} point to the object name.
+ *
+ * Object names retrieved by this function are unqualified, i.e. {@code vehicle.position_speed} will be
+ * retrieved as {@code vehicle} on the first call (return value 2) and {@code position_speed} on the
+ * second call (return value 1).
+ *
+ * @param ctx The context
+ * @param res Points to a {@code struct result} where the result will be stored.
+ *
+ * @return If a complete object name has been retrieved, the return value is 1. If a partial object name
+ * has been retrieved (e.g. {@code vehicle} from {@code vehicle.position_speed}), the return value is 2.
+ * If no object references were found, the return value is 0.
+ */
static int
get_next_object(struct context *ctx, struct result *res) {
@@ -1559,6 +1691,11 @@ command_saved_evaluate(struct command_saved *cs)
cs->idle_ev = event_add_idle(100, cs->idle_cb);
}
+/**
+ * @brief Recreates all callbacks for a saved command
+ *
+ * @param cs The saved command
+ */
static void
command_saved_callbacks_changed(struct command_saved *cs)
{
@@ -1567,6 +1704,8 @@ command_saved_callbacks_changed(struct command_saved *cs)
struct object_func *func;
struct attr attr;
+ dbg(lvl_debug, "enter: cs=%p, cs->async=%d, cs->command=%s\n", cs, cs->async, cs->command);
+
if (cs->register_ev) {
event_remove_idle(cs->register_ev);
cs->register_ev = NULL;
@@ -1593,7 +1732,16 @@ command_saved_callbacks_changed(struct command_saved *cs)
cs->num_cbs = 0;
// Now, re-create all the callbacks
+#if 0
+ if (!command_register_callbacks(cs) && (!cs->ctx.error)) {
+ // We try this as an idle call again
+ dbg(lvl_debug, "could not register callbacks, will retry as an idle call\n");
+ cs->register_cb = callback_new_1(callback_cast(command_saved_callbacks_changed), cs);
+ cs->register_ev = event_add_idle(300, cs->register_cb);
+ }
+#else
command_register_callbacks(cs);
+#endif
}
/**
@@ -1602,7 +1750,16 @@ command_saved_callbacks_changed(struct command_saved *cs)
* This function registers callbacks for each attribute used in a saved command, causing the command to
* be re-evaluated whenever its value might change.
*
+ * This function will fail if an object used in the expression could not be resolved. This may happen
+ * during startup if this function is called before all objects have been created. In this case, the
+ * caller should schedule the function to be called again at a later time.
+ *
+ * It will also fail if an error is encountered. This can be determined by examining
+ * {@code cs->ctx.error} after the function returns.
+ *
* @param cs The command
+ *
+ * @return True if all callbacks were successfully registered, false if the function failed
*/
static int
command_register_callbacks(struct command_saved *cs)
@@ -1611,19 +1768,28 @@ command_register_callbacks(struct command_saved *cs)
int status;
struct object_func *func;
struct callback *cb;
+ int tmpoffset; /* For debugging. Because we work with pointers into the same string instance.
+ * we can figure out offsets by using simple pointer arithmetics.
+ */
- dbg(lvl_error, "enter: cs=%p, cs->async=%d, cs->command=%s\n", cs, cs->async, cs->command);
+ dbg(lvl_debug, "enter: cs=%p, cs->async=%d, cs->command=%s\n", cs, cs->async, cs->command);
attr = cs->context_attr;
cs->ctx.expr = cs->command;
cs->ctx.attr = &attr;
prev = cs->context_attr;
while ((status = get_next_object(&cs->ctx, &cs->res)) != 0) {
+ tmpoffset = cs->res.var - cs->command;
resolve(&cs->ctx, &cs->res);
- if (cs->ctx.error || (cs->res.attr.type == attr_none)) {
- // We could not resolve an object, perhaps because it has not been created
- dbg(lvl_error, "could not resolve an object: cs=%p, cs->ctx.error=%d, cs->ctx.expr=%s\n", cs, cs->ctx.error, cs->ctx.expr);
+ if (cs->ctx.error) {
+ /* An error occurred while parsing the command */
+ tmpoffset = cs->ctx.expr - cs->command;
+ dbg(lvl_error, "parsing error: cs=%p, cs->ctx.error=%d\n\t%s\n\t%*s\n", cs, cs->ctx.error, cs->command, tmpoffset + 1, "^");
+ return 0;
+ } else if (cs->res.attr.type == attr_none) {
+ /* We could not resolve an object, perhaps because it has not been created */
+ dbg(lvl_error, "could not resolve object in cs=%p:\n\t%s\n\t%*s\n", cs, cs->command, tmpoffset + 1, "^");
return 0;
}
@@ -1669,12 +1835,22 @@ command_register_callbacks(struct command_saved *cs)
return 1;
}
+/**
+ * @brief Creates a new saved command.
+ *
+ * @param command The command string
+ * @param attr The context attribute for the saved command
+ * @param cb The callback to call whenver the command is re-evaluated
+ * @param async Whether the saved command should be flagged as asynchronous, causing it to be evaluated
+ * in an idle callback
+ */
struct command_saved *
command_saved_attr_new(char *command, struct attr *attr, struct callback *cb, int async)
{
struct command_saved *ret;
ret = g_new0(struct command_saved, 1);
+ dbg(lvl_debug, "enter, ret=%p, command=%s\n", ret, command);
ret->command = g_strdup(command);
ret->context_attr = *attr;
ret->cb = cb;
@@ -1683,6 +1859,7 @@ command_saved_attr_new(char *command, struct attr *attr, struct callback *cb, in
if (!command_register_callbacks(ret)) {
// We try this as an idle call again
+ dbg(lvl_debug, "could not register callbacks, will retry as an idle call\n");
ret->register_cb = callback_new_1(callback_cast(command_saved_callbacks_changed), ret);
ret->register_ev = event_add_idle(300, ret->register_cb);
}