#include "common.h"
#include "question.h"
#include "template.h"
#include "strutl.h"
#include "configuration.h"
#include "database.h"
#include "frontend.h"
#include <assert.h>
struct question *question_new(const char *tag)
{
struct question *q;
q = NEW(struct question);
memset(q, 0, sizeof(struct question));
q->ref = 1;
q->tag = STRDUP(tag);
q->priority = NULL;
return q;
}
void question_delete(struct question *question)
{
struct questionowner **ownerp;
DELETE(question->tag);
if (question->template)
template_deref(question->template);
for (ownerp = &question->owners; *ownerp != NULL;)
{
struct questionowner *currentp = *ownerp;
*ownerp = currentp->next;
DELETE(currentp->owner);
DELETE(currentp);
}
free(question->priority);
DELETE(question);
}
void question_ref(struct question *q)
{
++q->ref;
}
void question_deref(struct question *q)
{
if (q == NULL) return;
if (--q->ref == 0)
question_delete(q);
}
struct question *question_dup(const struct question *q)
{
struct question *ret = question_new(q->tag);
struct questionvariable *qv = q->variables;
struct questionowner *qo = q->owners;
ret->value = STRDUP(q->value);
ret->flags = q->flags;
ret->template = q->template;
template_ref(ret->template);
/* ret->template = template_dup(q->template); */
while (qv)
{
question_variable_add(ret,qv->variable,qv->value);
qv = qv->next;
}
while (qo)
{
question_owner_add(ret,qo->owner);
qo = qo->next;
}
return ret;
}
void question_setvalue(struct question *q, const char *value)
{
/* Be careful about the self-assignment case... */
if (q->value != value)
{
DELETE(q->value);
q->value = STRDUP(value);
}
}
/* Note that Default-* fields contain *untranslated* choices, so it's usual
* to call this with lang="" and then compare the answer with untranslated
* choices.
*/
const char *question_getvalue(const struct question *q, const char *lang)
{
if (q->value)
return q->value;
return template_lget(q->template, lang, "default");
}
const char *question_get_variable(const struct question *q, const char *var)
{
struct questionvariable *qvi = q->variables;
for (; qvi != 0; qvi = qvi->next)
if (strcmp(qvi->variable, var) == 0)
return qvi->value;
return NULL;
}
void question_variable_add(struct question *q, const char *var,
const char *value)
{
struct questionvariable *qvi = q->variables;
struct questionvariable **qlast = &q->variables;
INFO(INFO_DEBUG, "Adding [%s] -> [%s]", var, value);
for (; qvi != 0; qlast = &qvi->next, qvi = qvi->next)
if (strcmp(qvi->variable, var) == 0 && qvi->value != value)
{
DELETE(qvi->value);
qvi->value = STRDUP(value);
return;
}
qvi = NEW(struct questionvariable);
memset(qvi, 0, sizeof(struct questionvariable));
qvi->variable = STRDUP(var);
qvi->value = STRDUP(value);
*qlast = qvi;
}
void question_owner_add(struct question *q, const char *owner)
{
struct questionowner **ownerp = &q->owners;
while (*ownerp != 0)
{
if (strcmp((*ownerp)->owner, owner) == 0)
return;
ownerp = &(*ownerp)->next;
}
*ownerp = NEW(struct questionowner);
memset(*ownerp, 0, sizeof(struct questionowner));
(*ownerp)->owner = STRDUP(owner);
(*ownerp)->next = 0;
}
void question_owner_delete(struct question *q, const char *owner)
{
struct questionowner **ownerp;
for (ownerp = &q->owners; *ownerp != 0;)
{
if (strcmp((*ownerp)->owner, owner) == 0)
{
struct questionowner *currentp = *ownerp;
*ownerp = currentp->next;
DELETE(currentp->owner);
DELETE(currentp);
}
else
{
ownerp = &(*ownerp)->next;
}
}
}
static const struct {
const char *name;
unsigned int value;
} flags[] = {
{ DC_QFLAG_SEEN, (1 << 0) },
{ 0, 0 }
};
static int flag_value(const char *type)
{
int i;
if (!type)
return 0;
for (i = 0; flags[i].name != NULL; i++)
{
if (0 == strcmp(flags[i].name, type))
return flags[i].value;
}
return 0;
}
void question_set_flag(struct question *q, const char *flag)
{
q->flags |= flag_value(flag);
}
void question_clear_flag(struct question *q, const char *flag)
{
q->flags &= ~flag_value(flag);
}
int question_get_flag(const struct question *q, const char *flag)
{
return q->flags & flag_value(flag);
}
/* used by question_expand_vars */
static const char * lookup_vars(const char * name,
struct questionvariable * variables)
{
struct questionvariable * current;
/* Skip directives */
if ('!' == name[0]) {
return NULL;
}
for (current = variables; NULL != current; current = current->next) {
if (0 == strcmp(current->variable, name)) {
return current->value;
}
}
return "";
}
static char *question_expand_vars(const struct question *q, const char *field)
{
return strexpand(field, LOOKUP_FUNCTION(lookup_vars), q->variables);
}
char *question_get_raw_field(const struct question *q, const char *lang,
const char *field)
{
char *ret = NULL;
assert(q);
assert(field);
if (strcmp(field, "value") == 0)
ret = question_expand_vars(q, question_getvalue(q, lang));
else if (strcasecmp(field, "owners") == 0)
{
struct questionowner *owner = q->owners;
while (owner)
{
if (ret)
{
char *oldret = ret;
ret = realloc(ret, strlen(ret) + 2 + strlen(owner->owner) + 1);
if (ret)
{
strcat(ret, ", ");
strcat(ret, owner->owner);
}
else
ret = oldret;
}
else
ret = strdup(owner->owner);
owner = owner->next;
}
}
else
ret = question_expand_vars(q, template_lget(q->template, lang, field));
/* question_get_field must always return a valid string. */
if (ret == NULL)
return strdup("");
else
return ret;
}
static const char * lookup_directive(const char * directive, struct frontend * fe)
{
assert('!' == directive[0]);
return fe->methods.lookup_directive(fe, directive + 1 /* drop '!' */);
}
char * question_get_field(struct frontend * fe, const struct question * q,
const char * lang, const char * field)
{
char * raw;
char * ret;
raw = question_get_raw_field(q, lang, field);
ret = strexpand(raw, LOOKUP_FUNCTION(lookup_directive), fe);
free(raw);
return ret;
}
const char *question_get_priority(const struct question *q)
{
return q->priority;
}
const char *question_get_tag(const struct question *q)
{
return q->tag;
}
/*
* Function: question_get_text
* Input: struct frontend *obj - frontend object
* const char *template - template name
* const char *fallback - string to use if not available
* Output: const char * - ptr to string, translated if possible
* Description: get the translated version of a string
* Assumptions: None.
*/
const char *
question_get_text(struct frontend *obj, const char *template,
const char *fallback)
{
struct question *q = obj->qdb->methods.get(obj->qdb, template);
const char *text;
text = (q ? q_get_description(obj, q) : fallback);
question_deref(q);
return text;
}