Logo Search packages:      
Sourcecode: gaby version File versions  Download package

records.c

/*  Gaby
 *  Copyright (C) 1998-1999 Frederic Peters
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "gaby.h"
#include "f_desc.h"
#include "records.h"

#ifdef USE_SQL
#  include <postgres.h>
#  include <libpq-fe.h>
extern PGconn *sqlconn;
extern void create_record_from_sql_res(table *t, PGresult *res, int n);
#endif

#include <math.h>

#ifdef USE_SQL
/**
 * get_record_no
 * @t: table in which the record is searched
 * @id: id of the record to search for
 *
 * Description:
 * Search for a given record.
 *
 * Returns: Pointer to a record (NULL if not found)
 **/
record* get_record_no(table *t, int id)
{
      PGresult *res;
      char sqlstr[1024];

      debug_print("get_record_no called with id == %d\n", id);
      if ( id <= 0 ) return NULL;

      sprintf(sqlstr, "SELECT * from %s WHERE id = %d", t->name, id);
      res = PQexec(sqlconn, sqlstr);

      create_record_from_sql_res(t, res, 0);
      t->records[0]->id = id;

      return t->records[0];
}
#else /* ! USE_SQL */
record* get_record_no(table *t, int id)
{
      int i;
      
      if ( id <= 0 ) return NULL;
      
      for ( i=0; i<t->max_records; i++ ) {
            if ( t->records[i]->id == id )
                  return t->records[i];
      }
      debug_print("Sorry, record didn't exist.\n");
      return NULL;
}
#endif /* ! USE_SQL */

#if 0
/* it's currently best to use get_subtable_linked_field (see below) */

void get_subtable_record(subtable *st, int id, record *r)
{
      int i;
      table *t;
      GList *l;
      GString *s;
      int id_inside;
      record *intable = get_record_no(st->table, id);
      record *riot;     /* record in other table */
      
      r->id = id;
      
      for ( i=0; i < st->nb_fields; i++ ) {
            r->cont[i] = intable->cont[st->fields[i].no];
            
            if ( st->table->fields[st->fields[i].no].type == T_RECORD ) {
                  r->cont[i].str = g_string_new("");
                  id_inside = intable->cont[st->fields[i].no].i;
                  t = (field_get_property( 
                        &st->table->fields[st->fields[i].no], 
                        "from"))->val;
/*                t = st->table->fields[st->fields[i].no].more.from; */
                  riot = get_record_no(t, id_inside);
                  if ( riot == NULL )
                        continue;
                  l = st->fields[i].link_format;
                  /* the caller will have to free this */
                  s = r->cont[i].str;
                  while ( l != NULL ) {
                        s = g_string_append(s, 
                              riot->cont[GPOINTER_TO_INT(l->data)].str->str);
                        s = g_string_append(s, " ");
                        l = g_list_next(l);
                  }
            }
      }
      
}

#endif /* 0 */

GString* get_subtable_record_field(subtable *st, record *riot, int field_no)
{
      GList *l;
      GString *str = g_string_new("");
      
      l = st->fields[field_no].link_format;
      
      while ( l != NULL ) {
            str = g_string_append(str, 
                  riot->cont[GPOINTER_TO_INT(l->data)].str->str);
            str = g_string_append(str, " ");
            l = g_list_next(l);
      }
      str = g_string_truncate(str, str->len-1);

      return str;
}

GString* get_subtable_linked_field(subtable *st, int id, int field_no)
{
      table *t;
      GString *str;
      int id_inside;
      record *riot;
      record *intable = get_record_no(st->table, id);
      
      /* the caller will have to g_string_free this */
      str = g_string_new("");

      if ( intable == NULL )
            return str;
      
      if ( st->fields[field_no].type != T_RECORD )
            return str;
      
      id_inside = intable->cont[st->fields[field_no].no].i;
/*    t = st->table->fields[st->fields[field_no].no].more.from; */
      t = (field_get_property( &st->table->fields[st->fields[field_no].no],
                  "from"))->val;
#ifdef DEBUG_GABY
      debug_print("[gslf] id %d from : %s\n", id_inside, t->name);
#endif

      riot = get_record_no(t, id_inside);
      if ( riot == NULL )
            return str;

      g_string_free(str, 1);
      
      str = get_subtable_record_field(st, riot, field_no );

#ifdef DEBUG_GABY
      debug_print("[gslf] str : %s\n", str->str);
#endif

      return str;
}

GString* get_stringed_date(GDate *date)
{
      GString *str;
      gchar st[128];

      /* TODO (post-2.0): internal support for more date formats
       *  this is really easy, let's make it harder :)
       *  it would be cool to provide the user a way to define exactly
       *  how he wants dates, this setup is good for a default value but
       *  something complex must be done (oh no :) )
       *
       * delayed since we can rely on glib for now
       */
      st[0] = 0;
      
      if ( date != NULL )
            g_date_strftime(st, 127, "%x", date);

      str = g_string_new(st);

      return str;
}

/**
 * get_table_stringed_field
 * @t: table you're speaking of
 * @r: record you're speaking of
 * @field_no: field (of the table) you want as a string
 *
 * Description:
 * This function returns a string with the information of the field @field_no
 * form the record @r whatever the type of the field is. This allows
 * plug-ins to not worry about dates, number, ... (doesn't include T_RECORD).
 * 
 * Returns: Pointer to a string with the information (you have to
 * g_string_free it)
 **/
GString* get_table_stringed_field(table *t, record *r, int field_no)
{
      /* note that the caller has to g_string_free the returned string */
      GString *str;
      int id;

#ifdef DEBUG_GABY
      /*
      debug_print("[gssf] record %d (%p)\n", id, r );
      */
#endif
      if ( r == NULL ) {
            property *p;
            p = field_get_property(&t->fields[field_no], "default");
            if ( p != NULL ) {
                  if ( t->fields[field_no].type == T_STRING ) {
                        str = g_string_new(((GString*)(p->val))->str);
                        return str;
                  } else {
                        /* TODO (post-2.0): default value for other
                         * types; actually I would need a function who
                         * would take 'union data*, field_type' and
                         * return a string; much like this function but
                         * with a smaller scope (this function would
                         * use it)
                         * Delayed after 2.0 since it is really not
                         * critical */
                        return g_string_new("");
                  }
            } else {
                  return g_string_new("");
            }
      }

      id = r->id;

      switch ( t->fields[field_no].type ) {
            case T_STRING:
            case T_STRINGS:
            case T_MULTIMEDIA:
            case T_FILE:
            {
                  if ( r->cont[field_no].str ) {
                        str = g_string_new(r->cont[field_no].str->str);
                  } else {
                        str = g_string_new("");
                  }
            } break;
            case T_RECORD:
            case T_INTEGER:
            {
                  str = g_string_new("");
                  g_string_sprintf(str, "%d", r->cont[field_no].i);
            } break;
            case T_REAL:
            {
                  str = g_string_new("");
                  g_string_sprintf(str, "%f", r->cont[field_no].d);
            } break;
            case T_DATE:
            {
                  str = get_stringed_date(r->cont[field_no].date);
            } break;
            case T_BOOLEAN:
            {
                  if ( r->cont[field_no].b == TRUE ) {
                        str = g_string_new(_("yes"));
                  } else {
                        str = g_string_new(_("no"));
                  }
            } break;
            case T_DECIMAL:
            {
                  /* TODO (post-2.0): decimal, handling precision */
                  str = g_string_new("");
                  g_string_sprintf(str, "%.2f", r->cont[field_no].i/100.0);
                  debug_print("[get_table_stringed_field] decimal: %s\n", str->str);
            } break;
            default:
            {
                  str = g_string_new("");
            } break;
      }
      
      return str;
}

/**
 * get_table_stringed_field_id
 * @t: table you're speaking of
 * @id: id of the record you're speaking of
 * @field_no: field (of the table) you want as a string
 *
 * Description:
 * This functions does the same job that get_subtable_record_field() does
 * excepted that the second parameter is a &int. This is useful when you don't
 * have a faster way to acceed the &record structure than get_record_no()
 * (which is used in this function).
 *
 * Returns: a string with the information you want, you have to free it
 **/
GString* get_table_stringed_field_id(table *t, int id, int field_no)
{
      record *r = get_record_no(t, id);
      return get_table_stringed_field(t, r, field_no);
}

/**
 * get_subtable_stringed_field
 * @st: subtable you're speaking of
 * @r: record you're speaking of
 * @field_no: field (of the subtable) you want as a string
 *
 * Description:
 * This function returns a string with the information of the field @field_no
 * form the record @r whatever the type of the field is. This allows
 * plug-ins to not worry about dates, integer and (important) links to other
 * tables (type 'record'). (this is a wrapper for get_table_stringed_field)
 * 
 * Returns: Pointer to a string with the information (you have to free it)
 **/
GString* get_subtable_stringed_field(subtable *st, record *r, int field_no)
{
      /* note that the caller has to g_string_free the returned string */
      GString *str;
      int fn;

#ifdef DEBUG_GABY
      /*
      debug_print("[gssf] record %d (%p)\n", id, r );
      */
#endif

      fn = st->fields[field_no].no;
#ifdef DEBUG_GABY
      /*debug_print("[gssf] fn : %d, field_no : %d\n", fn, field_no);*/
#endif

      if ( st->fields[field_no].no == -1 || r == NULL ) {
            /* we have to show a list of records but this is the view
             * plug-in job so he'll get nothing :) 
             * 
             * I could perhaps create a huge string with that list but
             * ... this is not done :)
             */
            
            str = g_string_new("");
            return str;
      }

      if ( st->fields[field_no].type == T_RECORD ) {
            str = get_subtable_linked_field(st, r->id, field_no);
            return str;
      }

      return get_table_stringed_field(st->table, r, fn);
}

/**
 * get_subtable_stringed_field_id
 * @st: subtable you're speaking of
 * @id: id of the record you're speaking of
 * @field_no: field (of the subtable) you want as a string
 *
 * Description:
 * This functions does the same job that get_subtable_record_field() does
 * excepted that the second parameter is a &int. This is useful when you don't
 * have a faster way to acceed the &record structure than get_record_no()
 * (which is used in this function).
 *
 * Returns: a string with the information you want, you have to free it
 **/
GString* get_subtable_stringed_field_id(subtable *st, int id, int field_no)
{
      /* there is a good reason to have this out of
       *  get_subtable_stringed_field and that reason is speed.
       */
      record *r = get_record_no(st->table, id);
      return get_subtable_stringed_field(st, r, field_no);
}

gboolean field_string_check(union data *cont, field *f)
{
      gchar *str;
      property *format;
      gchar *fmt;
      
#ifdef DEBUG_GABY
/*    debug_print("Checking string\n");   */
#endif
      if ( cont == NULL ) cont->str = g_string_new("");
      
      format = field_get_property(f, "format");
      
      if ( format == NULL ) return TRUE;
      
      fmt = format->val;
      str = cont->str->str;

      if ( strlen(str) == 0 ) {
            /* the string is empty, we allow that */
            return TRUE;
      }
      
      if ( strcmp(fmt, "email" ) == 0 ) {
#ifdef DEBUG_GABY
            debug_print("Checking that '%s' is an email address\n", str);
#endif
            /* this is a poor man check but it works */
            if ( strchr(str, '@') && ! strchr(str, ' ') ) {
                  return TRUE;
            } else {
                  return FALSE;
            }
      }
      
      return TRUE;
}

gboolean record_check(table *t, record *r)
{
      /* (?)
       *  I just thought about this : why not make field types plugins ?
       *  because this would be a pain :)
       */
      
      int i;
      typedef gboolean (*CheckFunc) (union data *cont, field *f);
      CheckFunc one;
      CheckFunc alls[] = {
            field_string_check,     /* T_STRING */
            field_string_check,     /* T_STRINGS      */
            NULL,             /* T_INTEGER      */
            NULL,             /* T_REAL   */
            NULL,             /* T_DATE   */
            NULL,             /* T_BOOLEAN      */
            NULL,             /* T_RECORD */
            NULL,             /* T_RECORDS      */
            NULL,             /* T_MULTIMEDIA */
            NULL,             /* T_DECIMAL      */
            NULL              /* T_FILE   */
      };

      for ( i=0; i < t->nb_fields; i++ ) {
            one = alls[t->fields[i].type];
#ifdef DEBUG_GABY
            debug_print("[rc] checking field %d on %d\n", i, t->nb_fields);
#endif
            if ( one != NULL &&
                  one(&r->cont[i], &t->fields[i]) == FALSE )
                        return FALSE;
      }

      return TRUE;
}

/**
 * get_related_record
 * @sf: subtable T_RECORDS field 
 * @id: id to match
 *
 * Description:
 * This functions searchs in a table for the record related to the given one.
 *
 * Returns: a GList filled with positions (not id !) of records in the related
 * table (or filled with -1 if no related records)
 */
GList* get_related_records(st_field *sf, int id)
      /* why not simply return NULL for an empty list ?
       * (because NULL is sometimes interpreted as 'every records') */
{
      /*
       * the GList _have_ to be freed by the caller !
       */
      subtable *st;
      GList *list = NULL;
      GList *rel;
      int field_nb;
      table *t;
      int i;
      
      if ( sf->type != T_RECORDS )
            return NULL;
      
      if ( id == -1 ) {
            list = g_list_append(list, GINT_TO_POINTER(-1));
            return list;
      }

      st = sf->v->subtable;
      t = st->table;
#ifdef DEBUG_GABY
      debug_print("[grr] subtable name : %s\n", st->name);
#endif
      rel = g_list_first(sf->link_format);

      while ( rel != NULL ) {
            field_nb = GPOINTER_TO_INT(rel->data);
            rel = g_list_next(rel);
            for ( i=0; i<t->max_records; i++ ) {
                  if ( t->records[i] == NULL || t->records[i]->id == 0 )
                        continue;
                  if ( t->records[i]->cont[field_nb].i == id ) {
#ifdef DEBUG_GABY
                        debug_print("[grr] found : %d\n", i);
#endif
                        list = g_list_append(list, 
                                    GINT_TO_POINTER(i));
                  }
            }
      }

      if ( list == NULL ) {
            list = g_list_append(list, GINT_TO_POINTER(-1));
      }
      
      return g_list_first(list);
}

/**
 * get_conditional_records_list
 * @st: the concerned subtable
 * @other: additional condition (may be NULL)
 * 
 * Description:
 * This functions searchs for records matching the subtable's conditions as
 * well as other conditions (eventually).
 *
 * Returns: a GList filled with positions (not id !) of records
 */
GList* get_conditional_records_list(subtable *st, condition *other)
{
      GList *res_list = NULL;
      int i;
      table *t;
      condition c;
      record *r;
      
      c.type = C_AND;
      c.val_true = TRUE;
      c.field_no = 0;
      c.c.conditions = NULL;
      if ( st->cond != NULL ) {
            c.c.conditions = g_list_append(c.c.conditions, st->cond);
      }
      if ( other != NULL ) {
            c.c.conditions = g_list_append(c.c.conditions, other);
      }
      
      t = st->table;

      for ( i=0; i < t->max_records; i++ ) {
            if ( t->records[i] == NULL || t->records[i]->id == 0 )
                  continue;
            r = t->records[i];
            if ( record_meets_condition(t, r, &c) ) {
                  res_list = g_list_append(res_list, GINT_TO_POINTER(i));
            }
      }

      return res_list;
}

/*
 * starting today (May 20th 1999), I begin to test the comparison functions
 * (they exist since March 29th).
 *
 * The current status is :
 *
 *  cnd_or : yes,  cnd_and : yes
 *
 *
 *                \ / STRING  INT  REAL  DATE  BOOLEAN
 *  cnd_is         :   yes    no   no    no     no
 *  cnd_is_not     :   no     no   no    no     no
 *  cnd_is_greater :   no     no   no    no     n/a
 *  cnd_is_less    :   no     no   no    no     n/a
 *  cnd_start_with :   yes    n/a  n/a   n/a    n/a
 *  cnd_has        :   yes    n/a  n/a   n/a    n/a
 *  cnd_regex      :   yes    n/a  n/a   n/a    n/a
 *
 *  n/a : not applicable, n/i : not implemented
 *
 *  Some n/a could be implemented but they are not really necessary
 *  (example: 15972 and 1.1237 both start with 1 -- useful ?)
 */

gboolean record_meets_condition(table *t, record *r, condition *c)
{
      typedef gboolean (*cnd_fct) (union data, char*, field_type);
      cnd_fct function[] = {
            NULL,             /* C_OR */
            NULL,             /* C_AND */
            cnd_is,                 /* C_IS */
            cnd_is_not,       /* C_IS_NOT */
            cnd_is_greater,         /* C_IS_GREATER */
            cnd_is_less,            /* C_IS_LESS */
            cnd_start_with,         /* C_START_WITH */
            cnd_has,          /* C_HAS */
            cnd_regex         /* C_REGEX */
      };
      GList *tmp;
      condition *c2;

      if ( r == NULL )
            return FALSE;

      switch ( c->type ) {
            
            case C_OR:
            {
                  if ( c->c.conditions == NULL ) return c->val_true;
                  tmp = g_list_first(c->c.conditions);
                  while ( tmp != NULL ) {
                        c2 = tmp->data;
                        if ( record_meets_condition(t, r, c2) )
                              return c->val_true;
                        tmp = g_list_next(tmp);
                  }
                  return (! c->val_true);
            } break;
            
            case C_AND:
            {
                  if ( c->c.conditions == NULL ) return c->val_true;
                  tmp = g_list_first(c->c.conditions);
                  while ( tmp != NULL ) {
                        c2 = tmp->data;
                        if ( ! record_meets_condition(t, r, c2) )
                              return ( ! c->val_true );
                        tmp = g_list_next(tmp);
                  }
                  return c->val_true;
            } break;
            
            case C_IS:
            case C_IS_NOT: /*
                        *  this should be removed and
                        *  c->val_true = FALSE used instead
                        *
                        *  this may also stay like this : "there's more
                        *  than one way to do it" - Larry Wall
                        */
            case C_IS_GREATER:
            case C_IS_LESS:
            case C_START_WITH:
            case C_HAS:
            case C_REGEX:
            {
                  return ( function[c->type] ( r->cont[c->field_no], 
                                    c->c.val->str, 
                                    t->fields[c->field_no].type
                                       ) == c->val_true );
            } break;
#if 0
                  return cnd_is( r->cont[c->field_no], 
                        c->c.vals.v1, t->fields[c->field_no].type );
            case C_IS_NOT:
            {
                  return cnd_is_not( r->cont[c->field_no], 
                        c->c.vals.v1, t->fields[c->field_no].type );
            } break;
            case C_IS_GREATER:
            {
                  return cnd_is_greater( r->cont[c->field_no], 
                        c->c.vals.v1, t->fields[c->field_no].type );
            } break;
            case C_IS_LESS:
            {
                  return cnd_is_less( r->cont[c->field_no], 
                        c->c.vals.v1, t->fields[c->field_no].type );
            } break;
            case C_START_WITH:
            {
                  return cnd_start_with( r->cont[c->field_no], 
                        c->c.vals.v1, t->fields[c->field_no].type );
            } break;
            case C_HAS:
            {
                  return cnd_has( r->cont[c->field_no], 
                        c->c.vals.v1, t->fields[c->field_no].type );
            } break;
#endif
            default:
            {
                  return TRUE;
            } break;
      }
      return TRUE;
}

gboolean cnd_is (union data a, char *b, field_type type)
{
      int t;
      GDate *dt;
      
      switch ( type ) {
            case T_STRING:
            case T_STRINGS:
            case T_MULTIMEDIA:
            case T_FILE:
            {
                  return ( strcmp(a.str->str, b) == 0 );
            } break;
            case T_INTEGER:
            case T_RECORD:
            case T_DECIMAL:
            {
                  return ( a.i == atoi(b) );
            } break;
            case T_REAL:
            {
                  return ( a.d == atof(b) );
            } break;
            case T_DATE:
            {
                  dt = g_date_new();
                  g_date_set_parse(dt, b);
                  if ( dt->month == G_DATE_BAD_MONTH ) {
                        t = 17;     /* doesn't matter */
                  } else {
                        t = g_date_compare(a.date, dt);
                  }
                  g_date_free(dt);
                  return ( t == 0 );
            } break;
            case T_BOOLEAN:
            {
                  if ( strcmp(b, _("yes")) == 0 || 
                              strcmp(b, _("true")) == 0 ) {
                        return ( a.b == TRUE );
                  } else {
                        return ( a.b == FALSE );
                  }
            } break;
            case T_RECORDS:
            {
#ifdef DEBUG_GABY
                  debug_print("[condition_is] does this really means sth ?\n");
#endif
                  return TRUE;
            } break;
            default:
            {
                  return TRUE;
            } break;
      }
}

gboolean cnd_is_not(union data a, char *b, field_type type)
{
      return ! ( cnd_is(a, b, type) );
}

gboolean cnd_start_with (union data a, char *b, field_type type)
{
      if ( type != T_STRING && type != T_STRINGS ) {
#ifdef DEBUG_GABY
            debug_print("Not applicable\n");
#endif
            return TRUE;
      }

      return ( strncmp(a.str->str, b, strlen(b)) == 0 );
}

gboolean cnd_has (union data a, char *b, field_type type)
{
      if ( type != T_STRING && type != T_STRINGS ) {
#ifdef DEBUG_GABY
            debug_print("Not applicable\n");
#endif
            return TRUE;
      }

      return ( strstr(a.str->str, b) != NULL );
}

gboolean cnd_regex (union data a, char *b, field_type type)
{
#ifdef HAVE_REGEX_H
      regex_t regex;
      gboolean result;
      
      if ( type != T_STRING && type != T_STRINGS ) {
#ifdef DEBUG_GABY
            debug_print("Not applicable\n");
#endif
            return TRUE;
      }

      regcomp(&regex, b, REG_EXTENDED);
      result = ( regexec(&regex, a.str->str, 0, NULL, 0) == 0 );
      regfree(&regex);

      return result;
#else
      return TRUE;
#endif
}

gboolean cnd_is_greater (union data a, char *b, field_type type)
{
      int t;
      GDate *dt;
      
      switch ( type ) {
            case T_STRING:
            case T_STRINGS:
            {
                  return ( strcmp(a.str->str, b) > 0 );
            } break;
            case T_INTEGER:
            case T_DECIMAL:
            {
                  return ( a.i > atoi(b) );
            } break;
            case T_REAL:
            {
                  return ( a.d > atof(b) );
            } break;
            case T_DATE:
            {
                  dt = g_date_new();
                  g_date_set_parse(dt, b);
                  t = g_date_compare(a.date, dt);
                  g_date_free(dt);
                  return ( t > 0 );
            } break;
            default:
            {
#ifdef DEBUG_GABY
                  debug_print("Not applicable\n");
#endif
                  return TRUE;
            } break;
      }
}

gboolean cnd_is_less (union data a, char *b, field_type type)
{
      int t;
      GDate *dt;
      
      switch ( type ) {
            case T_STRING:
            case T_STRINGS:
            {
                  return ( strcmp(a.str->str, b) < 0 );
            } break;
            case T_INTEGER:
            case T_DECIMAL:
            {
                  return ( a.i < atoi(b) );
            } break;
            case T_REAL:
            {
                  return ( a.d < atof(b) );
            } break;
            case T_DATE:
            {
                  dt = g_date_new();
                  g_date_set_parse(dt, b);
                  t = g_date_compare(a.date, dt);
                  g_date_free(dt);
                  return ( t < 0 );
            } break;
            default:
            {
#ifdef DEBUG_GABY
                  debug_print("Not applicable\n");
#endif
                  return TRUE;
            } break;
      }
}



/** end of filtering stuffs **/

int id_record_field(gchar *str, subtable *st, int field_no)
{
      table *t;
      int i;
      record *r;
      GString *thisr;

/*    t = st->table->fields[st->fields[field_no].no].more.from;*/
      t = (field_get_property( &st->table->fields[st->fields[field_no].no], 
                  "from"))->val;

      for ( i=0; i < t->max_records; i++ ) {
            r = t->records[i];
            if ( r == NULL || r->id == 0 )
                  continue;
            
            thisr = get_subtable_record_field(st, r, field_no);
            
            if ( strcmp(thisr->str, str) == 0 ) {
                  g_string_free(thisr, 1);
                  return r->id;
            }
            g_string_free(thisr, 1);
      }
      
      return 0;
      
}

/**
 * get_value_for_that_string
 * @val: pointer to the union data that will hold the converted data
 * @type: type of the string
 * @str: string to convert
 *
 * Description:
 * This functions converts a given @str according its gaby type
 **/
void get_value_for_that_string(union data *val, field_type type, gchar *str)
{
      if ( val == NULL ) return;

      switch ( type ) {
            case T_STRING:
            case T_STRINGS:
            case T_MULTIMEDIA:
            case T_FILE:
            {
                  if ( val->str == NULL )
                        val->str = g_string_new("");
                  val->str = g_string_assign(val->str, str);
            } break;
            case T_INTEGER:
            case T_RECORD:
            {
                  val->i = atoi(str);
            } break;
            case T_REAL:
            {
                  val->d = atof(str);
            } break;
            case T_DECIMAL:
            {
                  val->i = (int)(rint(atof(str)*100));
            } break;
            case T_DATE:
            {
                  if ( str && strlen(str) != 0 ) {
                        if ( val->date == NULL )
                              val->date = g_date_new();
                        g_date_set_parse(val->date, str);
                        if ( val->date->month == G_DATE_BAD_MONTH ) {
                              g_date_free(val->date);
                              val->date = NULL;
                        }
                  } else {
                        val->date = NULL;
                  }
            } break;
            case T_BOOLEAN:
            {
                  /* we assume FALSE unless it is TRUE */
                  val->b = FALSE;
                  
                  /* TODO (post-2.0) : strings for boolean type
                   * we only support 'yes' and 'true' as a valid answer,
                   * should things like 'true', 0, 'on', ... be valid ?
                   * note that this will need to be handled in cnd_is
                   * Delayed since it is 'oh so stupid'
                   */
                  if ( strcmp(str, "yes") == 0 ) {
                        val->b = TRUE;
                  }
                  if ( strcmp(str, _("yes")) == 0 ) {
                        val->b = TRUE;
                  }
                  if ( strcmp(str, _("true")) == 0 ) {
                        val->b = TRUE;
                  }
            } break;
            case T_RECORDS: {} break; /* never in tables */
      }
}

/**
 * set_table_stringed_field
 * @t: the subtable you're in
 * @r: the record you want to change
 * @field_no: the field number you want to change
 * @str: the string you want its value in the record
 *
 * Description:
 * This function set the field @field_no of the &record @r to the value given
 * by the string @str whatever the type of the field is.
 **/
void set_table_stringed_field(table *t, record *r, int field_no, gchar *str)
{
      union data *dt;
      
      if ( r == NULL || r->cont == NULL )
            return;
      
      dt = &r->cont[field_no];
      get_value_for_that_string(dt, t->fields[field_no].type, str);

      /* 991227: this was commented (with a global 'updated'). Why ? */
      /* 000104: 'cause the form plug-in creates a temporary record and use
       * this function to set its content */
      /* Note: those comments were subject to the y2k-bug. What a pain :) */
      
      t->updated = t->updated || (r->id != 0);
}

/**
 * set_subtable_stringed_field
 * @st: the subtable you're in
 * @r: the record you want to change
 * @field_no: the field number you want to change
 * @str: the string you want its value in the record
 *
 * Description:
 * This function set the field @field_no of the &record @r to the value given
 * by the string @str whatever the type of the field is. (this is a wrapper for
 * set_table_stringed_field)
 **/
void set_subtable_stringed_field(subtable *st, record *r, int field_no,
                         gchar *str)
{
      union data *dt;
      
      if ( r == NULL || r->cont == NULL )
            return;
      
      dt = &r->cont[st->fields[field_no].no];
      
      if ( st->fields[field_no].type == T_RECORD ) {
            dt->i = id_record_field(str, st, field_no);
            st->table->updated = TRUE;
            return;
      }
      if ( st->fields[field_no].type == T_RECORDS ) return;
      
      set_table_stringed_field(st->table, r, st->fields[field_no].no, str);
}

gboolean records_are_different(table *t, record *r1, record *r2)
{
      int i;
      union data *f1, *f2;

      if ( r1 == NULL && r2 != NULL ) return TRUE;
      if ( r2 == NULL ) return TRUE;

      for ( i=0; i<t->nb_fields; i++ ) {
            f1 = &r1->cont[i];
            f2 = &r2->cont[i];
            if ( f1->anything == f2->anything ) continue;
                                    /* this would mean == NULL */
            if ( ( ! f1->anything &&   f2->anything ) || 
                 (   f1->anything && ! f2->anything ) ) {
                  return TRUE;
            }
            switch ( t->fields[i].type ) {
                  case T_STRING:
                  case T_STRINGS:
                  case T_MULTIMEDIA:
                  case T_FILE:
                  {
                        if ( strcmp(f1->str->str, f2->str->str) != 0 )
                              return TRUE;
                  } break;
                  case T_INTEGER:
                  case T_RECORD:
                  case T_DECIMAL:
                  {
                        if ( f1->i != f2->i ) return TRUE;
                  } break;
                  case T_REAL:
                  {
                        if ( f1->d != f2->d ) return TRUE;
                  } break;
                  case T_DATE:
                  {
                        /* f1->date == f2->date is only true when both
                         * values are NULL */
                        if ( f1->date == f2->date ) break;
                        if ( f1->date == NULL ) return TRUE;
                        if ( f2->date == NULL ) return TRUE;
                        if ( g_date_compare(f1->date, f2->date) != 0 )
                              return TRUE;
                  } break;
                  case T_BOOLEAN:
                  {
                        if ( f1->b != f2->b ) return TRUE;
                  } break;
                  case T_RECORDS: {;} break;
            }
                  
      }
      
      return FALSE;
}


Generated by  Doxygen 1.6.0   Back to index