root/usr.sbin/installer/libdfui/form.c
/*
 * Copyright (c)2004 Cat's Eye Technologies.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 *   Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 *   Neither the name of Cat's Eye Technologies nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * form.c
 * $Id: form.c,v 1.18 2005/03/04 21:26:20 cpressey Exp $
 */

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include <libaura/mem.h>

#define NEEDS_DFUI_STRUCTURE_DEFINITIONS
#include "dfui.h"
#undef  NEEDS_DFUI_STRUCTURE_DEFINITIONS
#include "dump.h"

/*** INFOS ***/

struct dfui_info *
dfui_info_new(const char *name, const char *short_desc, const char *long_desc)
{
        struct dfui_info *i;

        AURA_MALLOC(i, dfui_info);
        i->name = aura_strdup(name);
        i->short_desc = aura_strdup(short_desc);
        i->long_desc = aura_strdup(long_desc);

        return(i);
}

void
dfui_info_free(struct dfui_info *i)
{
        free(i->name);
        free(i->short_desc);
        free(i->long_desc);
        AURA_FREE(i, dfui_info);
}

const char *
dfui_info_get_name(const struct dfui_info *i)
{
        if (i == NULL)
                return("");
        return(i->name);
}

const char *
dfui_info_get_short_desc(const struct dfui_info *i)
{
        if (i == NULL)
                return("");
        return(i->short_desc);
}

const char *
dfui_info_get_long_desc(const struct dfui_info *i)
{
        if (i == NULL)
                return("");
        return(i->long_desc);
}

void
dfui_info_set_name(struct dfui_info *i, const char *name)
{
        if (i == NULL)
                return;
        if (i->name != NULL)
                free(i->name);
        i->name = aura_strdup(name);
}

void
dfui_info_set_short_desc(struct dfui_info *i, const char *short_desc)
{
        if (i == NULL)
                return;
        if (i->short_desc != NULL)
                free(i->short_desc);
        i->short_desc = aura_strdup(short_desc);
}

void
dfui_info_set_long_desc(struct dfui_info *i, const char *long_desc)
{
        if (i == NULL)
                return;
        if (i->long_desc != NULL)
                free(i->long_desc);
        i->long_desc = aura_strdup(long_desc);
}

/*** PROPERTIES ***/

struct dfui_property *
dfui_property_new(const char *name, const char *value)
{
        struct dfui_property *p;

        AURA_MALLOC(p, dfui_property);
        p->name = aura_strdup(name);
        p->value = aura_strdup(value);

        return(p);
}

void
dfui_property_free(struct dfui_property *p)
{
        if (p == NULL)
                return;
        free(p->name);
        free(p->value);
        AURA_FREE(p, dfui_property);
}

void
dfui_properties_free(struct dfui_property *head)
{
        struct dfui_property *p;

        for (p = head; p != NULL; ) {
                head = p->next;
                dfui_property_free(p);
                p = head;
        }
}

struct dfui_property *
dfui_property_find(struct dfui_property *head, const char *name)
{
        struct dfui_property *p;

        for (p = head; p != NULL; p = p->next) {
                if (strcmp(name, p->name) == 0)
                        return(p);
        }

        return(NULL);
}

const char *
dfui_property_get(struct dfui_property *head, const char *name)
{
        struct dfui_property *p;

        if ((p = dfui_property_find(head, name)) != NULL)
                return(p->value);
        return("");
}

struct dfui_property *
dfui_property_set(struct dfui_property **head, const char *name, const char *value)
{
        struct dfui_property *p;

        if (head == NULL)
                return(NULL);

        if ((p = dfui_property_find(*head, name)) != NULL) {
                free(p->value);
                p->value = aura_strdup(value);
                return(p);
        }

        p = dfui_property_new(name, value);
        p->next = *head;
        *head = p;

        return(p);
}

const char *
dfui_property_get_name(const struct dfui_property *p)
{
        return(p->name);
}

const char *
dfui_property_get_value(const struct dfui_property *p)
{
        return(p->value);
}

/*** FIELDS ***/

struct dfui_field *
dfui_field_new(const char *id, struct dfui_info *info)
{
        struct dfui_field *fi;

        AURA_MALLOC(fi, dfui_field);
        fi->id = aura_strdup(id);
        fi->info = info;
        fi->option_head = NULL;
        fi->property_head = NULL;
        fi->next = NULL;
        fi->callback.func = NULL;
        fi->callback.userdata = NULL;

        dfui_field_property_set(fi, "editable", "true");

        return(fi);
}

void
dfui_field_free(struct dfui_field *fi)
{
        free(fi->id);
        dfui_info_free(fi->info);
        dfui_options_free(fi->option_head);
        dfui_properties_free(fi->property_head);
        AURA_FREE(fi, dfui_field);
}

void
dfui_fields_free(struct dfui_field *head)
{
        struct dfui_field *fi;

        fi = head;
        while (fi != NULL) {
                head = fi->next;
                dfui_field_free(fi);
                fi = head;
        }
}

struct dfui_field *
dfui_field_get_next(const struct dfui_field *fi)
{
        if (fi == NULL)
                return(NULL);
        return(fi->next);
}

const char *
dfui_field_get_id(const struct dfui_field *fi)
{
        if (fi == NULL)
                return(NULL);
        return(fi->id);
}

struct dfui_info *
dfui_field_get_info(const struct dfui_field *fi)
{
        if (fi == NULL)
                return(NULL);
        return(fi->info);
}

struct dfui_option *
dfui_field_option_add(struct dfui_field *fi, const char *value)
{
        struct dfui_option *o;

        if (fi == NULL)
                return(NULL);
        o = dfui_option_new(value);
        o->next = fi->option_head;
        fi->option_head = o;

        return(o);
}

struct dfui_option *
dfui_field_option_get_first(const struct dfui_field *fi)
{
        if (fi == NULL)
                return(NULL);
        return(fi->option_head);
}

struct dfui_property *
dfui_field_property_set(struct dfui_field *fi, const char *name, const char *value)
{
        return(dfui_property_set(&fi->property_head, name, value));
}

const char *
dfui_field_property_get(const struct dfui_field *fi, const char *name)
{
        return(dfui_property_get(fi->property_head, name));
}

int
dfui_field_property_is(const struct dfui_field *fi, const char *name, const char *value)
{
        struct dfui_property *h;

        if (fi == NULL)
                return(0);
        if ((h = dfui_property_find(fi->property_head, name)) == NULL)
                return(0);
        return(!strcmp(h->value, value));
}

void
dfui_field_set_callback(struct dfui_field *fi, dfui_callback_field_changed func,
                        void *userdata)
{
        if (fi == NULL)
                return;
        fi->callback.func = func;
        fi->callback.userdata = userdata;
}

int
dfui_field_has_callback(const struct dfui_field *fi)
{
        if (fi == NULL)
                return(0);
        return(fi->callback.func != NULL);
}

/*** OPTIONS ***/

struct dfui_option *
dfui_option_new(const char *value)
{
        struct dfui_option *o;

        AURA_MALLOC(o, dfui_option);
        o->value = aura_strdup(value);
        o->next = NULL;

        return(o);
}

void
dfui_option_free(struct dfui_option *o)
{
        if (o == NULL)
                return;
        free(o->value);
        AURA_FREE(o, dfui_option);
}

void
dfui_options_free(struct dfui_option *head)
{
        struct dfui_option *o;

        o = head;
        while (o != NULL) {
                head = o->next;
                dfui_option_free(o);
                o = head;
        }
}

struct dfui_option *
dfui_option_get_next(const struct dfui_option *o)
{
        if (o == NULL)
                return(NULL);
        return(o->next);
}

const char *
dfui_option_get_value(const struct dfui_option *o)
{
        if (o == NULL)
                return("");
        return(o->value);
}

/*** ACTIONS ***/

struct dfui_action *
dfui_action_new(const char *id, struct dfui_info *info)
{
        struct dfui_action *a;

        AURA_MALLOC(a, dfui_action);
        a->id = aura_strdup(id);
        a->info = info;
        a->next = NULL;
        a->property_head = NULL;

        return(a);
}

void
dfui_action_free(struct dfui_action *a)
{
        free(a->id);
        dfui_info_free(a->info);
        dfui_properties_free(a->property_head);
        AURA_FREE(a, dfui_action);
}

void
dfui_actions_free(struct dfui_action *head)
{
        struct dfui_action *a;

        a = head;
        while (a != NULL) {
                head = a->next;
                dfui_action_free(a);
                a = head;
        }
}

struct dfui_action *
dfui_action_get_next(const struct dfui_action *a)
{
        if (a == NULL)
                return(NULL);
        return(a->next);
}

const char *
dfui_action_get_id(const struct dfui_action *a)
{
        if (a == NULL)
                return(NULL);
        return(a->id);
}

struct dfui_info *
dfui_action_get_info(const struct dfui_action *a)
{
        if (a == NULL)
                return(NULL);
        return(a->info);
}

struct dfui_property *
dfui_action_property_set(struct dfui_action *a, const char *name, const char *value)
{
        return(dfui_property_set(&a->property_head, name, value));
}

const char *
dfui_action_property_get(const struct dfui_action *a, const char *name)
{
        return(dfui_property_get(a->property_head, name));
}

int
dfui_action_property_is(const struct dfui_action *a, const char *name, const char *value)
{
        struct dfui_property *h;

        if (a == NULL)
                return(0);
        if ((h = dfui_property_find(a->property_head, name)) == NULL)
                return(0);
        return(!strcmp(h->value, value));
}

/*** FORMS ***/

struct dfui_form *
dfui_form_new(const char *id, struct dfui_info *info)
{
        struct dfui_form *f;

        AURA_MALLOC(f, dfui_form);
        f->id = aura_strdup(id);
        f->info = info;
        f->multiple = 0;
        f->extensible = 0;
        f->field_head = NULL;
        f->action_head = NULL;
        f->dataset_head = NULL;
        f->property_head = NULL;

        return(f);
};

/*
 * Convenience function for creating a form.
 * This function takes a list of any number of strings.
 * This list MUST be terminated by a NULL pointer.
 * The first four strings are the id, name, short description, and long
 * description of the form.
 * Each subsequent string determines what the strings following it represent:
 *    "f": create a field (id, name, short desc, long desc).
 *    "o": add an option to the last field (value).
 *    "a": create an action (id, name, short desc, long desc).
 *    "p": add a property to the last object (name, value).
 */
struct dfui_form *
dfui_form_create(const char *id, const char *name,
                 const char *short_desc, const char *long_desc, ...)
{
#define DFUI_FORM_CREATE_FORM   0
#define DFUI_FORM_CREATE_FIELD  1
#define DFUI_FORM_CREATE_ACTION 2

        struct dfui_form *f;
        struct dfui_info *i;
        va_list args;
        int state = DFUI_FORM_CREATE_FORM;
        char *arg;
        void *object = NULL;
        const char *a_id;
        char *a_name, *a_short_desc, *a_long_desc;

        i = dfui_info_new(name, short_desc, long_desc);
        f = dfui_form_new(id, i);

        va_start(args, long_desc);
        while ((arg = va_arg(args, char *)) != NULL) {
                switch (arg[0]) {
                case 'f':
                        a_id = va_arg(args, const char *);
                        a_name = va_arg(args, char *);
                        a_short_desc = va_arg(args, char *);
                        a_long_desc = va_arg(args, char *);
                        i = dfui_info_new(a_name, a_short_desc, a_long_desc);
                        object = (void *)dfui_form_field_add(f, a_id, i);
                        state = DFUI_FORM_CREATE_FIELD;
                        break;
                case 'a':
                        a_id = va_arg(args, const char *);
                        a_name = va_arg(args, char *);
                        a_short_desc = va_arg(args, char *);
                        a_long_desc = va_arg(args, char *);
                        i = dfui_info_new(a_name, a_short_desc, a_long_desc);
                        object = (void *)dfui_form_action_add(f, a_id, i);
                        state = DFUI_FORM_CREATE_ACTION;
                        break;
                case 'o':
                        a_name = va_arg(args, char *);
                        if (state == DFUI_FORM_CREATE_FIELD) {
                                dfui_field_option_add(object, a_name);
                        } else {
                                dfui_debug("form_create: can't add option to non-field\n");
                        }
                        break;
                case 'h':
                case 'p':
                        a_id = va_arg(args, char *);
                        a_short_desc = va_arg(args, char *);

                        if (state == DFUI_FORM_CREATE_FIELD) {
                                dfui_field_property_set(object, a_id, a_short_desc);
                        } else if (state == DFUI_FORM_CREATE_ACTION) {
                                dfui_action_property_set(object, a_id, a_short_desc);
                        } else if (state == DFUI_FORM_CREATE_FORM) {
                                dfui_form_property_set(f, a_id, a_short_desc);
                        } else {
                                dfui_debug("form_create: can't add property in this state\n");
                        }
                        break;

                default:
                        dfui_debug("form_create: unknown option `%c'\n", arg[0]);
                        break;
                }
        }

        va_end(args);
        return(f);
}

void
dfui_form_free(struct dfui_form *f)
{
        free(f->id);
        dfui_info_free(f->info);
        dfui_fields_free(f->field_head);
        dfui_actions_free(f->action_head);
        dfui_datasets_free(f->dataset_head);
        dfui_properties_free(f->property_head);
        AURA_FREE(f, dfui_form);
}

struct dfui_field *
dfui_form_field_add(struct dfui_form *f, const char *id, struct dfui_info *info)
{
        struct dfui_field *fi;

        if (f == NULL)
                return(NULL);
        fi = dfui_field_new(id, info);
        fi->next = f->field_head;
        f->field_head = fi;

        return(fi);
}

struct dfui_field *
dfui_form_field_attach(struct dfui_form *f, struct dfui_field *fi)
{
        if (f == NULL)
                return(NULL);
        fi->next = f->field_head;
        f->field_head = fi;

        return(fi);
}

struct dfui_action *
dfui_form_action_add(struct dfui_form *f, const char *id, struct dfui_info *info)
{
        struct dfui_action *a;

        if (f == NULL)
                return(NULL);
        a = dfui_action_new(id, info);
        a->next = f->action_head;
        f->action_head = a;

        return(a);
}

struct dfui_action *
dfui_form_action_attach(struct dfui_form *f, struct dfui_action *a)
{
        if (f == NULL)
                return(NULL);
        a->next = f->action_head;
        f->action_head = a;

        return(a);
}

void
dfui_form_dataset_add(struct dfui_form *f, struct dfui_dataset *ds)
{
        if (f == NULL || ds == NULL)
                return;
        ds->next = f->dataset_head;
        f->dataset_head = ds;
}

struct dfui_dataset *
dfui_form_dataset_get_first(const struct dfui_form *f)
{
        if (f == NULL)
                return(NULL);
        return(f->dataset_head);
}

int
dfui_form_dataset_count(const struct dfui_form *f)
{
        int n = 0;
        struct dfui_dataset *ds;

        if (f == NULL)
                return(0);

        ds = f->dataset_head;
        while (ds != NULL) {
                n++;
                ds = ds->next;
        }

        return(n);
}

void
dfui_form_datasets_free(struct dfui_form *f)
{
        if (f == NULL)
                return;
        dfui_datasets_free(f->dataset_head);
        f->dataset_head = NULL;
}

struct dfui_field *
dfui_form_field_find(const struct dfui_form *f, const char *id)
{
        struct dfui_field *fi;

        if (f == NULL)
                return(NULL);

        fi = f->field_head;
        while (fi != NULL) {
                if (!strcmp(id, fi->id))
                        return(fi);
                fi = fi->next;
        }

        return(NULL);
}

struct dfui_field *
dfui_form_field_get_first(const struct dfui_form *f)
{
        if (f == NULL)
                return(NULL);
        return(f->field_head);
}

int
dfui_form_field_count(const struct dfui_form *f)
{
        int n = 0;
        struct dfui_field *fi;

        if (f == NULL)
                return(0);

        fi = f->field_head;
        while (fi != NULL) {
                n++;
                fi = fi->next;
        }

        return(n);
}

struct dfui_action *
dfui_form_action_find(const struct dfui_form *f, const char *id)
{
        struct dfui_action *a = f->action_head;

        while (a != NULL) {
                if (!strcmp(id, a->id))
                        return(a);
                a = a->next;
        }

        return(NULL);
}

struct dfui_action *
dfui_form_action_get_first(const struct dfui_form *f)
{
        if (f == NULL)
                return(NULL);
        return(f->action_head);
}

int
dfui_form_action_count(const struct dfui_form *f)
{
        int n = 0;
        struct dfui_action *a;

        if (f == NULL)
                return(0);

        a = f->action_head;
        while (a != NULL) {
                n++;
                a = a->next;
        }

        return(n);
}

struct dfui_property *
dfui_form_property_set(struct dfui_form *f, const char *name, const char *value)
{
        return(dfui_property_set(&f->property_head, name, value));
}

const char *
dfui_form_property_get(const struct dfui_form *f, const char *name)
{
        return(dfui_property_get(f->property_head, name));
}

int
dfui_form_property_is(const struct dfui_form *f, const char *name, const char *value)
{
        struct dfui_property *h;

        if (f == NULL)
                return(0);
        if ((h = dfui_property_find(f->property_head, name)) == NULL)
                return(0);
        return(!strcmp(h->value, value));
}

const char *
dfui_form_get_id(const struct dfui_form *f)
{
        if (f == NULL)
                return(NULL);   /* XXX ? */
        return(f->id);
}

struct dfui_info *
dfui_form_get_info(const struct dfui_form *f)
{
        if (f == NULL)
                return(NULL);
        return(f->info);
}

void
dfui_form_set_multiple(struct dfui_form *f, int multiple)
{
        if (f == NULL)
                return;
        f->multiple = multiple;
}

int
dfui_form_is_multiple(const struct dfui_form *f)
{
        if (f == NULL)
                return(0);
        return(f->multiple);
}

void
dfui_form_set_extensible(struct dfui_form *f, int extensible)
{
        if (f == NULL)
                return;
        f->extensible = extensible;
}

int
dfui_form_is_extensible(const struct dfui_form *f)
{
        if (f == NULL)
                return(0);
        return(f->extensible);
}

int
dfui_form_trigger_callbacks(struct dfui_form *f, const char *field_id,
                            const char *new_value)
{
        struct dfui_field *fi;

        for (fi = f->field_head; fi != NULL; fi = fi->next) {
                if (strcmp(fi->id, field_id) == 0)
                        break;
        }
        if (fi == NULL || fi->callback.func == NULL)
                return(0);

        dfui_dataset_set_value(f->dataset_head, field_id, new_value);

        dfui_debug("triggered callbacks: field_id=`%s', new_value=`%s'\n",
                   field_id, new_value);
        return(fi->callback.func(f, fi, new_value, fi->callback.userdata));
}

/*** CELLDATAS ***/

struct dfui_celldata *
dfui_celldata_new(const char *field_id, const char *value)
{
        struct dfui_celldata *c;

        AURA_MALLOC(c, dfui_celldata);
        c->field_id = aura_strdup(field_id);
        c->value = aura_strdup(value);

        return(c);
}

void
dfui_celldata_free(struct dfui_celldata *c)
{
        if (c == NULL)
                return;
        free(c->field_id);
        free(c->value);
        AURA_FREE(c, dfui_celldata);
}

void
dfui_celldatas_free(struct dfui_celldata *head)
{
        struct dfui_celldata *c;

        c = head;
        while (c != NULL) {
                head = c->next;
                dfui_celldata_free(c);
                c = head;
        }
}

struct dfui_celldata *
dfui_celldata_find(struct dfui_celldata *head, const char *id)
{
        struct dfui_celldata *c;

        c = head;
        while (c != NULL) {
                if (!strcmp(id, c->field_id))
                        return(c);
                c = c->next;
        }

        return(NULL);
}

struct dfui_celldata *
dfui_celldata_get_next(const struct dfui_celldata *cd)
{
        if (cd != NULL) {
                return(cd->next);
        } else {
                return(NULL);
        }
}

const char *
dfui_celldata_get_field_id(const struct dfui_celldata *cd)
{
        if (cd != NULL) {
                return(cd->field_id);
        } else {
                return(NULL);
        }
}

const char *
dfui_celldata_get_value(const struct dfui_celldata *cd)
{
        if (cd != NULL) {
                return(cd->value);
        } else {
                return("");
        }
}

int
dfui_celldata_set_value(struct dfui_celldata *cd, const char *value)
{
        if (cd == NULL)
                return(-1);

        free(cd->value);
        cd->value = aura_strdup(value);
        return(0);
}

/*** DATASETS ***/

struct dfui_dataset *
dfui_dataset_new(void)
{
        struct dfui_dataset *ds;

        AURA_MALLOC(ds, dfui_dataset);
        ds->celldata_head = NULL;
        ds->next = NULL;

        return(ds);
}

struct dfui_dataset *
dfui_dataset_dup(const struct dfui_dataset *ds)
{
        struct dfui_dataset *nds;
        struct dfui_celldata *cd;

        nds = dfui_dataset_new();

        for (cd = ds->celldata_head; cd != NULL; cd = cd->next) {
                dfui_dataset_celldata_add(nds,
                    cd->field_id, cd->value);
        }

        return(nds);
}

void
dfui_dataset_free(struct dfui_dataset *ds)
{
        dfui_celldatas_free(ds->celldata_head);
        AURA_FREE(ds, dfui_dataset);
}

void
dfui_datasets_free(struct dfui_dataset *head)
{
        struct dfui_dataset *ds;

        ds = head;
        while (ds != NULL) {
                head = ds->next;
                dfui_dataset_free(ds);
                ds = head;
        }
}

struct dfui_celldata *
dfui_dataset_celldata_add(struct dfui_dataset *ds, const char *field_id, const char *value)
{
        struct dfui_celldata *c;

        if (ds == NULL)
                return(NULL);

        c = dfui_celldata_new(field_id, value);
        c->next = ds->celldata_head;
        ds->celldata_head = c;

        return(c);
}

struct dfui_celldata *
dfui_dataset_celldata_get_first(const struct dfui_dataset *ds)
{
        if (ds == NULL)
                return(NULL);
        return(ds->celldata_head);
}

struct dfui_celldata *
dfui_dataset_celldata_find(const struct dfui_dataset *ds, const char *field_id)
{
        if (ds == NULL)
                return(NULL);
        return(dfui_celldata_find(ds->celldata_head, field_id));
}

struct dfui_dataset *
dfui_dataset_get_next(const struct dfui_dataset *ds)
{
        if (ds == NULL)
                return(NULL);
        return(ds->next);
}

/*
 * Returns the value of the celldata with the given id found in the
 * given dataset.  If no such celldata is found, a constant zero-length
 * string is returned.  As such, the return value of this function is
 * guaranteed to not be NULL and must NEVER be freed after use.
 */
const char *
dfui_dataset_get_value(const struct dfui_dataset *ds, const char *id)
{
        struct dfui_celldata *cd;

        if ((cd = dfui_dataset_celldata_find(ds, id)) != NULL) {
                return(dfui_celldata_get_value(cd));
        } else {
                return("");
        }
}

/*
 * Allocates a string to hold the value of the celldata with the
 * given id found in the given dataset.  Even if no such celldata
 * is found, an allocated, zero-length string is returned.  As such,
 * the return value of this function is guaranteed to not be NULL,
 * and must ALWAYS be freed after use.
 */
char *
dfui_dataset_dup_value(const struct dfui_dataset *ds, const char *id)
{
        return(aura_strdup(dfui_dataset_get_value(ds, id)));
}

int
dfui_dataset_set_value(struct dfui_dataset *ds, const char *id, const char *value)
{
        struct dfui_celldata *cd;

        if (ds == NULL)
                return(-1);

        cd = dfui_dataset_celldata_find(ds, id);
        return(dfui_celldata_set_value(cd, value));
}

/*** RESPONSES ***/

struct dfui_response *
dfui_response_new(const char *form_id, const char *action_id)
{
        struct dfui_response *r;

        AURA_MALLOC(r, dfui_response);
        r->form_id = aura_strdup(form_id);
        r->action_id = aura_strdup(action_id);
        r->dataset_head = NULL;

        return(r);
}

void
dfui_response_free(struct dfui_response *r)
{
        free(r->form_id);
        free(r->action_id);
        dfui_datasets_free(r->dataset_head);
        AURA_FREE(r, dfui_response);
}

void
dfui_response_dataset_add(struct dfui_response *r, struct dfui_dataset *ds)
{
        if (ds == NULL || r == NULL)
                return;
        ds->next = r->dataset_head;
        r->dataset_head = ds;
}

struct dfui_dataset *
dfui_response_dataset_get_first(const struct dfui_response *r)
{
        if (r == NULL)
                return(NULL);
        return(r->dataset_head);
}

int
dfui_response_dataset_count(const struct dfui_response *r)
{
        int n = 0;
        struct dfui_dataset *ds;

        if (r == NULL)
                return(0);

        ds = r->dataset_head;
        while (ds != NULL) {
                n++;
                ds = ds->next;
        }

        return(n);
}

const char *
dfui_response_get_form_id(const struct dfui_response *r)
{
        if (r == NULL)
                return(NULL);   /* XXX ? */
        return(r->form_id);
}

const char *
dfui_response_get_action_id(const struct dfui_response *r)
{
        if (r == NULL)
                return(NULL);   /* XXX ? */
        return(r->action_id);
}

/* PROGRESS BARS */

struct dfui_progress *
dfui_progress_new(struct dfui_info *info, int amount)
{
        struct dfui_progress *pr;

        AURA_MALLOC(pr, dfui_progress);
        pr->info = info;
        pr->amount = amount;
        pr->streaming = 0;
        pr->msg_line = NULL;

        return(pr);
}

void
dfui_progress_free(struct dfui_progress *pr)
{
        if (pr == NULL)
                return;
        dfui_info_free(pr->info);
        if (pr->msg_line != NULL)
                free(pr->msg_line);
        AURA_FREE(pr, dfui_progress);
}

struct dfui_info *
dfui_progress_get_info(const struct dfui_progress *pr)
{
        if (pr == NULL)
                return(NULL);
        return(pr->info);
}

void
dfui_progress_set_amount(struct dfui_progress *pr, int amount)
{
        if (pr == NULL)
                return;
        pr->amount = amount;
}

int
dfui_progress_get_amount(const struct dfui_progress *pr)
{
        if (pr == NULL)
                return(0);
        return(pr->amount);
}

void
dfui_progress_set_streaming(struct dfui_progress *pr, int streaming)
{
        if (pr == NULL)
                return;
        pr->streaming = streaming;
}

int
dfui_progress_get_streaming(const struct dfui_progress *pr)
{
        if (pr == NULL)
                return(0);
        return(pr->streaming);
}

void
dfui_progress_set_msg_line(struct dfui_progress *pr, const char *msg_line)
{
        if (pr == NULL)
                return;
        if (pr->msg_line != NULL)
                free(pr->msg_line);
        pr->msg_line = aura_strdup(msg_line);
}

const char *
dfui_progress_get_msg_line(const struct dfui_progress *pr)
{
        if (pr == NULL)
                return("");
        if (pr->msg_line == NULL)
                return("");
        return(pr->msg_line);
}