diff options
author | mvglasow <michael -at- vonglasow.com> | 2015-11-11 14:26:50 +0100 |
---|---|---|
committer | mvglasow <michael -at- vonglasow.com> | 2015-11-11 16:00:15 +0100 |
commit | 033eabc0e8d2d14c94caec35e65635bbe94f0538 (patch) | |
tree | 52c88b4f332b89be0d8b306668c61e64e889be39 /navit | |
parent | 36b16c73223a09e2912c2a0918807cbccec60c75 (diff) | |
download | navit-033eabc0e8d2d14c94caec35e65635bbe94f0538.tar.gz |
Refactor:core:Some more documentation and logging in command.c
Signed-off-by: mvglasow <michael -at- vonglasow.com>
Diffstat (limited to 'navit')
-rw-r--r-- | navit/command.c | 199 |
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); } |