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

f_desc.c

/*  Gaby
 *  Copyright (C) 1998-2000 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 <pwd.h>
#include "files.h"
#include "tables.h"

#define LINE      150

static gboolean st_fields_every(subtable *sub);
static gboolean   st_fields_only_these(subtable *sub, FILE *f, int *num, GList *views);

static gboolean vp_already_in(GList *vp, GString *pn);
static ViewPluginData* viewplugin_load_one(GString *name);
static ViewPluginData* get_plugin_by_name(char *name, GList *plugins);
static void find_function(action *a, char *st);
static gboolean locations_load(gchar *name);

static property** field_add_property(field *f, gchar *ident, gpointer val);

static void desc_read_line(char *buf, FILE *f, int *num)
{
      do {
            fgets(buf, LINE, f);
            (*num)++;
      } while ( buf[0] == '#' );
}

static void desc_read_continued_line(char *buf, FILE *f, int *num)
{
      /* buf needs to be long enough to hold the string */
      desc_read_line(buf, f, num);
      while ( buf[strlen(buf)-1] == '\\' ) {
            buf[strlen(buf)-1] = 0;
            buf += strlen(buf);
            desc_read_line(buf, f, num);
      }
}

static void compress_string(char *st)
{
      /* remove any excessive spaces between a keyword and its value 
       * ex : 
       *    description = useless field
       *  ->      description=useless field
       */
      char *t;
      
      if ( st == NULL ) return;
      if ( strchr(st, '\n') ) strchr(st, '\n')[0] = 0;
      if ( strlen(st) < 3 ) return;
      if ( ! strchr(st+1, '=') ) return;
/*    
#ifdef DEBUG_GABY
            debug_print("Before : %s\n", st);
#endif
*/
      t = strchr(st, '=' );
      while ( isspace((int)(t[-1])) ) {
            memmove(t-1, t, strlen(t)+1 );
            t = strchr(st, '=');
      }
      
      while ( isspace((int)(t[1])) ) {
            memmove(t+1, t+2, strlen(t) );
      }
/*
#ifdef DEBUG_GABY
            debug_print("After : %s\n", st);
#endif
*/    
}

static gboolean field_add ( field *f, char *st )
{
      strchr(st, ':')[0] = 0;
      f->name = g_new(gchar, strlen(st)+1);
      strcpy(f->name, st);
      f->i18n_name = f->name;
      st += strlen(st)+1;
      st[strlen(st)-1] = 0;

      if ( strchr(st, ':') != NULL )
            strchr(st,':')[0] = 0;
      
      f->properties = g_new0(property*, 1);
      
      f->ok_if = NULL;
      
      f->type = T_STRING;     /* default is string */
      
      if ( strchr(st, ' ') ) strchr(st, ' ')[0] = 0;
      
      if      ( strcasecmp (st, "strings"   ) == 0 ) f->type = T_STRINGS;
      else if ( strcasecmp (st, "integer"   ) == 0 ) f->type = T_INTEGER;
      else if ( strcasecmp (st, "real"      ) == 0 ) f->type = T_REAL;
      else if ( strcasecmp (st, "date"      ) == 0 ) f->type = T_DATE;
      else if ( strcasecmp (st, "boolean"   ) == 0 ) f->type = T_BOOLEAN;
      else if ( strcasecmp (st, "record"    ) == 0 ) f->type = T_RECORD;
      else if ( strcasecmp (st, "multimedia") == 0 ) f->type = T_MULTIMEDIA;
      else if ( strcasecmp (st, "decimal")    == 0 ) f->type = T_DECIMAL;
      else if ( strcasecmp (st, "file")       == 0 ) f->type = T_FILE;
      
      if ( f->type == T_RECORD ) {
            st += strlen(st);
            while ( st[0] == ' ' ) st++;
            if ( ! strncmp("from ", st, 5 ) ) {
                  return FALSE;
            }
            st += 5;
            while ( st[0] == ' ' ) st++;
#ifdef DEBUG_GABY
            debug_print("from table : %s\n", st);
#endif
/*          f->more.from = get_table_pointer(tables, st);*/
            field_add_property(f, "from", get_table_by_name(st));
            if ( (field_get_property(f, "from"))->val == NULL )
                  return FALSE;
/*          if ( ! f->more.from )
                  return FALSE;     */
      }
      
      return TRUE;
}

static property** field_add_property(field *f, gchar *ident, gpointer val)
{
      property **ps = f->properties;
      int i=0;

      while ( ps[i] != NULL ) {
            if ( strcmp(ps[i]->name, ident) == 0 ) break;
            i++;
      }
      
      if ( ps[i] == NULL ) {
            ps = g_realloc(ps, sizeof(property*)*(i+2));
            ps[i+1] = NULL;
            ps[i] = g_new0(property, 1);
            ps[i]->name = g_strdup(ident);
      } else {
            if ( strcmp(ident, "description" ) == 0 ) g_free(ps[i]->val);
            if ( strcmp(ident, "format") == 0 ) {
                  if ( f->type == T_STRING || f->type == T_STRINGS ) {
                        g_string_free(((union data*)(ps[i]->val))->str,1);
                  }
                  g_free(ps[i]->val);
            }
      }

#if 0 /* multimedia fields will be disabled in view plug-ins if not supported */
      
/*#ifndef HAVE_IMLIB*/
#ifndef USE_GNOME
      if ( strcmp(ident,"type") == 0 && strcmp((gchar*)val,"image") == 0 ) {
            g_warning("Gaby was not compiled with Imlib, image fields are not available.\n");
            f->type = T_STRING;
      }
#endif

/*#ifndef HAVE_ESD*/
#ifndef USE_GNOME
      if ( strcmp(ident,"type") == 0 && strcmp((gchar*)val,"sound") == 0 ) {
            g_warning("Gaby was not compiled with ESD, sound fields are not available.\n");
            f->type = T_STRING;
      }
#endif

#endif /* 0 */
      
      ps[i]->val = val;

      return ps;
}

/**
 * field_get_property
 * @f: field you are interested in
 * @ident: name of the property you want
 * 
 * Description:
 * This functions allows you to get values for any property the user (or the
 * creator of the desc file) has defined for the field.
 *
 * Returns: the property you want, NULL if not found
 */
property* field_get_property(field *f, gchar *ident)
{
      property **ps = f->properties;
      int i=0;

#ifdef DEBUG_GABY
      /*debug_print("[field_get_property] looking for %s ", ident);*/
#endif

      while ( ps[i] != NULL ) {
            if ( strcmp(ps[i]->name, ident) == 0 ) break;
            i++;
      }
      
#ifdef DEBUG_GABY
      /*debug_print("%s\n", ( ps[i] == NULL ) ? "not found" : "ok" );*/
#endif
      return ps[i];
}

static void field_property( field *f, char *str)
{
      char des_lang[20];
      char *tmp;
      union data *d;
      
      compress_string(str);
      
      if ( strncmp(str, "description=", 12) == 0 ) {
            /* it looks like :
             *    (More...):string
             *          i18n_fr=(autre...)
             *          description=Title, company name, ...
             *          description_fr=Titre, société, ...
             *    ...
             *    
             * it is currently used as a statusbar message with the
             * form view
             */
            tmp = g_strdup(str+12);
            f->properties = field_add_property(f, "description", tmp );
            return;
      }

      sprintf(des_lang, "description_%s=", language);

      if ( strncmp(str, des_lang, 15) == 0 ) {
            tmp = g_strdup(str+15);
            f->properties = field_add_property(f, "description", tmp );
            return;
      }

      if ( strncmp(str, "description_", 12) == 0 ) {
            return;
      }

      if ( strncmp(str, "format=", 7 ) == 0  ) {
            /* I could perhaps assign every format a numeric value
             * since the check is done within gaby but this would
             * be less extentable.
             */
            tmp = g_strdup(str+7);
            f->properties = field_add_property(f, "format", tmp );
            return;
      }

      if ( strncmp(str, "type=", 5 ) == 0 && f->type == T_MULTIMEDIA ) {
            tmp = g_strdup(str+5);
            f->properties = field_add_property(f, "type", tmp );
            return;
      }

      if ( strncmp(str, "precision=", 10 ) == 0 && f->type == T_DECIMAL ) {
            int *itmp = g_new0(int, 1);
            *itmp = atoi(str+10);
            f->properties = field_add_property(f, "precision", itmp);
            return;
      }

#if 0
      if ( strncmp(str, "ok if ", 6 ) == 0 ) {
            /* since a condition "> 0 and < 2000" may be written as :
             *    ok if > 0 and < 2000
             * or ok if > 0
             *    ok if < 2000
             * or between 0 and 2000      (? this one is tricky)
             *
             * I could parse it here and divide it in small strings
             * (like the 2nd choice)
             */
            tmp_str = g_string_new(str+6);
            f->ok_if = g_list_append(f->ok_if, tmp_str);
            return;
      }
#endif

      if ( strncmp(str, "default=", 8 ) == 0 ) {
            d = g_new0(union data, 1);
            switch ( f->type ) {
                  case T_INTEGER:
                  {
                        d->i = atoi(str+8);
                  } break;
                  case T_DECIMAL:
                  {
                        d->i = 100 * atof(str+8);
                  } break;
                  case T_REAL:
                  {
                        d->d = atof(str+8);
                  } break;
                  case T_DATE:
                  {
                        d->date = g_date_new();
                        g_date_set_parse(d->date, str+8);
                  } break;
                  case T_FILE:
                  case T_STRING:
                  case T_STRINGS:
                  default:
                  {
                        d->str = g_string_new(str+8);
                  } break;
            }
            
            f->properties = field_add_property(f, "default", d);
            
            return;
      }
      
      if ( strncmp(str, "min=", 4 ) == 0 ) {
            d = g_new0(union data, 1);
            switch ( f->type ) {
                  case T_INTEGER: d->i = atoi(str+4); break;
                  case T_REAL:      d->d = atof(str+4); break;
                  case T_DECIMAL: d->i = 100 * atof(str+4); break;
                  case T_DATE:
                  {
                        d->date = g_date_new();
                        g_date_set_parse(d->date, str+8);
                  } break;
                  default:
                  {
#ifdef DEBUG_GABY
                        debug_print("'min' not supported with this type\n");
#endif
                        return;
                  } break;
            }
            f->properties = field_add_property(f, "min", d);
            return;
      }
      
      if ( strncmp(str, "max=", 4 ) == 0 ) {
            d = g_new0(union data, 1);
            switch ( f->type ) {
                  case T_INTEGER: d->i = atoi(str+4); break;
                  case T_REAL:      d->d = atof(str+4); break;
                  case T_DECIMAL: d->i = 100 * atof(str+4); break;
                  case T_DATE:
                  {
                        d->date = g_date_new();
                        g_date_set_parse(d->date, str+8);
                  } break;
                  default:
                  {
#ifdef DEBUG_GABY
                        debug_print("'max' not supported with this type\n");
#endif
                        return;
                  } break;
            }
            f->properties = field_add_property(f, "max", d);
            return;
      }

      if ( f->type == T_STRINGS && strncmp(str, "lines=", 6 ) == 0 ) {
            d = g_new0(union data, 1);
            d->i = atoi(str+6);
            f->properties = field_add_property(f, "lines", d);
            return;
      }
            
      
#ifdef DEBUG_GABY
      debug_print("unknown field property : %s\n", str);
#endif
}

static FILE* desc_open(gchar *name)
{
      static gboolean first_time = TRUE;
      FILE *f;
      GString *fn;
      
      fn = g_string_new("");

      g_string_sprintf(fn, "%s/.gaby/desc.%s", getenv("HOME"), name);
      f = fopen(fn->str, "r");
      if ( f == NULL ) {
            g_string_sprintf(fn, "%s/gaby/desc.%s", SYSCONF_DIR, name);
            f = fopen(fn->str, "r");
            if ( f == NULL ) {
                  gaby_message = g_strdup_printf(_("Unable to open description file.") );
                  gaby_errno = CUSTOM_ERROR;
                  gaby_perror_in_a_box();
                  return NULL;
            }
      }

      if ( first_time == TRUE ) {
#ifdef DEBUG_GABY
            debug_print(_("Using %s as description file.\n"), fn->str);
#endif
            first_time = FALSE;
      }

      g_string_free(fn, 1);
      return f;
}

static gboolean desc_goto_begin(FILE *f, gchar *what, int *num)
{
      char s1[LINE+1], s2[LINE+1];
      char st[LINE+1];
      
      do {
            fgets(st, LINE, f);
            sscanf(st, "%s %s", s1, s2);
            (*num)++;
      } while ( !(strcmp(s1, "Begin") == 0 && strcmp(s2, what) == 0)
              && !feof(f));

      if ( feof(f) ) {
#ifdef DEBUG_GABY
            debug_print(_("No %s description.\n"), what);
#endif
            return FALSE;
      }
      
      return TRUE;
}

static gboolean desc_tables_load_one ( FILE *f, table *t, int *num, char *st )
{
      char lang[15];
      int nbf;
      field *fs=NULL;

      sprintf(lang, "i18n_%s", language);
      
      if ( strchr(st, ':') != NULL ) {
            strchr(st, ':')[0] = 0;
            strcpy(t->short_name, st+strlen(st)+1);
      }
      t->name = g_new(gchar, strlen(st+1)+1);
      strcpy(t->name, st+1);

      /*
       * translations of tables names (?)
       * I like to think about users not knowing about tables and
       * happy with subtables which are the _real_ things). Given
       * that users will _never_ see tables so they don't need to
       * be translated. In fact, table names are just convenience
       * for writing desc files.
       */

      nbf=0;
      t->fields = NULL;
      fgets(st,LINE,f); (*num)++;
      while ( (st[0] == '\t' && st[1] == '\t') || st[0] == '#' ) {
            if ( st[0] == '#' ) {
                  fgets(st, LINE, f); (*num)++;
                  continue;
            }

            if ( st[2] != '\t' ) {
                  fs = g_malloc(sizeof(field)*(nbf+1));
                  memcpy(fs, t->fields, sizeof(field)*nbf);
                  if ( t->fields != NULL ) g_free(t->fields);
                  t->fields = fs;
                  if ( field_add( &t->fields[nbf], st+2)) {
                        nbf++;
                  }
                  fgets(st, LINE, f); (*num)++;
                  continue;
            }
            while ( strncmp(st+3, "i18n_", 5 ) == 0 ) {
                  compress_string(st+3);
                  if ( strncmp(st+3, lang, 7) == 0 ) {
                        t->fields[nbf-1].i18n_name = 
                              g_new(gchar, strlen(st+11)+1);
                        strcpy(t->fields[nbf-1].i18n_name,st+11);
                  }
                  fgets(st, LINE, f); (*num)++;
            }

            while ( st[2] == '\t' ) {
                  if ( strchr(st, '\n') ) strchr(st, '\n')[0] = 0;
                  field_property( &t->fields[nbf-1], st+3);
                  fgets(st,LINE,f); (*num)++;
            }
      }
      t->nb_fields = nbf;
      t->indexes = g_malloc0(sizeof(int*) * (t->nb_fields + 1) );
      t->indexes[0] = NULL;
      t->records = NULL;
      t->nb_records = 0;
      t->max_records = 0;
      t->locations = NULL;

      return TRUE;
}

gboolean tables_load_struct ( gchar *name )
{
      FILE *f;
      table *t;
      char st[LINE+1];
      int num=0;
      
      f = desc_open(name);
      if ( f == NULL )
            return FALSE;

      if ( desc_goto_begin(f, "tables", &num) == FALSE ) {
            fclose(f);
            return FALSE;
      }
      
      fgets(st,LINE,f); num++;
      while ( strncmp(st, "End",3) != 0 ) {
            if ( st[0] == '#' ) {
                  fgets(st,LINE,f); num++;
                  continue;
            }
            
            if ( st[0] != '\t' ) {
                  g_print("Error at line %d.\n", num);
                  return FALSE;
            }
            
            t = g_malloc(sizeof(table));
            list_tables = g_list_append(list_tables, t);
            st[strlen(st)-1] = 0;
            if ( strncmp(st, "\timport ", 8) == 0 ) {
                  char *s;
                  char *descfile;
                  FILE *df;
                  int onum=0;
                  char ost[LINE+1];
                  gboolean found = FALSE;
                  
                  s = st+7;
                  descfile = strrchr(s, ' ') + 1;
                  strchr(s, ' ')[0] = 0;
                  df = desc_open(descfile);
                  if ( df == NULL ) {
                        fgets(st, LINE, f); num++;
                        continue;
                  }
                  if ( desc_goto_begin(df, "tables", &onum) == FALSE ) {
                        fclose(df);
                        fgets(st, LINE, f); num++;
                        continue;
                  }
      
                  fgets(ost, LINE, df); onum++;
                  while ( strncmp(ost, "End",3) != 0 ) {
                        if ( ost[0] == '\t' && ost[1] != '\t' && 
                                    strncmp(ost+1,s,strlen(s))== 0){
                              found = TRUE;
                              break;
                        }
                        fgets(ost, LINE, df); onum++;
                  }
                  if ( found == TRUE ) {
                        desc_tables_load_one(df, t, &onum, ost);
                  }

                  fgets(st, LINE, f); num++;
                        
            } else {
                  desc_tables_load_one(f, t, &num, st);
            }

      }
      
      fclose(f);

      locations_load(name);
      
      return TRUE;
}

table* get_table_by_name( char *st )
{
      GList *l;
      
      l = g_list_first(list_tables);
      while ( l ) {
            if ( g_strcasecmp(((table *)(l->data))->name, st) == 0 ||
                 g_strcasecmp(((table *)(l->data))->short_name, st) == 0 ) {
                  return l->data;
            }
            l = g_list_next(l);
      }
      return NULL;
}

subtable* get_subtable_by_name(gchar *str)
{
      GList *l;
      
      l = g_list_first(list_subtables);
      while ( l ) {
            subtable *st = l->data;
            if ( g_strcasecmp(st->name, str) == 0 )
                  return st;
            if ( g_strcasecmp(st->i18n_name, str) == 0 )
                  return st;
            l = g_list_next(l);
      }
      
      return NULL;
}

/**
 * table_get_field_no
 * @t: table
 * @st: name of the field
 *
 * Description:
 * This functions returns the number of the field named @str in the table
 * @t.
 *
 * Returns: field number
 */
int table_get_field_no(table *t, char *st)
{
      int i, f=0;
      if ( strchr(st, '_') ) {
#ifdef DEBUG_GABY
            debug_print("I believe we face a T_RECORD : %s\n", st);
#endif
            strchr(st, '_')[0] = 0;
            f = 1;
      }
      
      for ( i=0; i<t->nb_fields; i++ ) {
#ifdef DEBUG_GABY
            debug_print("strcmp(%s, %s)\n", t->fields[i].name, st);
#endif
            if ( strcmp(t->fields[i].name, st) == 0 ) {
                  if ( f == 1 ) st[strlen(st)] = '_';
                  return i;
            }
      }

      if ( strstr(st, " of ") != NULL ) {
            return -1;
      }
      
      g_print("Oooops, I'll certainly segfault soon :(\n");
      g_print("You'd better check your description file.\n");
      
      return -2;
}

/**
 * subtable_get_field_no
 * @st: subtable
 * @str: name of the field
 *
 * Description:
 * This functions returns the number of the field named @str in the subtable
 * @st.
 *
 * Returns: field number (-2 if not found)
 */
int subtable_get_field_no(subtable *st, char *str)
{
      int i;
      
      for (i=0; i<st->nb_fields; i++) {
            if ( strcmp(st->fields[i].name, str) == 0 ) {
                  return i;
            }
            if ( strcmp(st->fields[i].i18n_name, str) == 0 ) {
                  return i;
            }
      }

      return -2;
}

static gboolean st_view_already_there(subtable *sub, ViewPluginData *vpd)
{
      GList *views = g_list_first(list_views);
      while ( views ) {
            view *v = views->data;
            if ( v->subtable == sub && v->type == vpd ) return TRUE;
            views = g_list_next(views);
      }
      return FALSE;
}

static GList* parse_viewable_as(subtable *sub, GList *plugins,
                        char *s, gboolean withstandards)
{
      char whole_str[500];
      char elem[50], *el;
      char *pos;
      char *ptstr;
      view *a_view;
      char *standards;
      
      /* adding standard views (defined in Gabyrc) */
      /* note that it is not nice at all to put a subtable-specific view (ala
       * GnomeCard) in the standard views */
      if ( withstandards ) {
            standards = get_config_str("common", "misc",
                                    "standard_views", "form");
            strcpy(whole_str, standards);
            g_free(standards);
            if ( strlen(whole_str) > 0 ) strcat(whole_str, ", ");
      } else {
            whole_str[0] = 0;
      }
      strcat(whole_str, s);
      pos = whole_str;

      ptstr=s;
      while (1) {
            strncpy(elem, pos, 40 );
            if ( strchr(elem, ',') ) 
                  strchr(elem, ',')[0] = 0;
            if ( strchr(elem, '.') )
                  strchr(elem, '.')[0] = 0;
            pos += strlen(elem)+1;
            el = elem;
            while ( el[0] == ' ' ) el++;
            
            if ( strlen(el) <= 1 ) break;
#ifdef DEBUG_GABY
            debug_print("[parse_viewable_as] found: %s\n", el);
#endif

            if ( /* list_views != NULL */ 1 ) {
                  ViewPluginData *vpd = get_plugin_by_name(el, plugins);
#if 1
                  if ( st_view_already_there(sub, vpd))
                        continue;
#endif
                  a_view = g_malloc(sizeof(view));
                  list_views = g_list_append(list_views, a_view);
                  a_view->name = sub->i18n_name;
                  a_view->subtable = sub;
                  a_view->type = vpd;
            }
      }
      
      return list_views;
}

static GList* parse_not_viewable_as(subtable *sub, char *s)
{
      char whole_str[500];
      char elem[50], *el;
      char *pos = whole_str;
      GList *views;

      strcpy(whole_str, s);
      
      while (1) {
            strncpy(elem, pos, 40 );
            if ( strchr(elem, ',') ) 
                  strchr(elem, ',')[0] = 0;
            if ( strchr(elem, '.') )
                  strchr(elem, '.')[0] = 0;
            pos += strlen(elem)+1;
            el = elem;
            while ( el[0] == ' ' ) el++;
            
            if ( strlen(el) <= 1 ) break;
#ifdef DEBUG_GABY
            debug_print("[parse_not_viewable_as] found: %s\n", el);
#endif
            views = g_list_first(list_views);
            while ( views != NULL && views->data != NULL ) {
                  view *v = views->data;
                  views = g_list_next(views);
                  if ( v->subtable != sub ) continue;
                  if ( v->type == NULL ) continue;

                  if ( strcmp(v->type->name, el) == 0 ) {
                        list_views = g_list_remove(list_views, v);
                  }
            }
      }

      
      return list_views;
}

gboolean st_load_struct (GList *plugins, gchar *name )
{
      /* I should make a function for "error at line %d" :) */
      
      /*
       * This function is too long, I should cut it in smaller parts. 
       */

      GList *tables = g_list_first(list_tables);
      FILE *f;
      char st[LINE+1], *s;
      int num=0;
      subtable *sub;
      char *desc_fields[] = {
            "every fields.",
            "only the following fields"
      };
      int type_desc;
      view *a_view;
      char lang[15];

      f = desc_open(name);
      if ( f == NULL )
            return FALSE;
      
      sprintf(lang, "i18n_%s", language);
      
      if ( desc_goto_begin(f, "subtables", &num) == FALSE ) {
            fclose(f);
            /* the default is the first table using the 'form' view. */

            /* it could be extended to create a subtable for each table
             * but the idea is to provide support for the simplest desc
             * files - those are not supposed to have more than one table
             */
            
            sub = g_malloc(sizeof(subtable));
            list_subtables = g_list_append(list_subtables, sub);
            sub->name = ((table*)(tables->data))->name;
            sub->cond = NULL;
            sub->i18n_name = sub->name;
            sub->table = (table*)(tables->data);
            
            a_view = g_malloc(sizeof(view));
            list_views = g_list_append(list_views, a_view);
            a_view->name = sub->i18n_name;
            a_view->subtable = sub;
            a_view->type = get_plugin_by_name("form",plugins);
            st_fields_every(sub);
            
            return TRUE;
      }
      
      fgets(st,LINE,f); num++;
      while ( strncmp(st, "End",3) != 0 ) {
            if ( st[0] == '#' ) {
                  fgets(st,LINE,f); num++;
                  continue;
            }
            if ( st[0] != '\t' ) {
                  g_print("Error at line %d.\n", num);
                  return FALSE;
            }
/*
 *    Phone Book:AddressBook
 */
            s = st;
            sub = g_malloc(sizeof(subtable));
            list_subtables = g_list_append(list_subtables, sub);
            strchr(st, ':')[0] = 0;
            sub->name = g_new(gchar, strlen(st+1)+1);
            strcpy(sub->name, st+1);
            sub->cond = NULL;
            sub->i18n_name = sub->name;
            s += strlen(st)+1;
            strchr(s, '\n')[0] = 0;
            sub->table = get_table_by_name(s);
/*
 *          i18n_*
 */
            do {
                  fgets(st,LINE,f); num++;
            } while ( st[0] == '#' );
            
            while ( strncmp(st+2,"i18n",4) == 0 ) {
                  compress_string(st+2);
                  if ( strncmp(st+2, lang, 7) == 0 ) {
                        sub->i18n_name = g_new(gchar, strlen(st+10)+1);
                        strcpy(sub->i18n_name, st+10);
                  }
                  
                  fgets(st,LINE,f); num++;
            }
            
/*
 *          viewable as list,form.
 */
            s = st;
            while ( st[0] == '#' ) {
                  fgets(st, LINE, f); num++;
            }
            
            if ( strncmp(st+2, "viewable as ", 12) == 0 ) {
                  parse_viewable_as(sub, plugins,     s+14, TRUE);
            } else {
                  if ( strncmp(st+2, "only viewable as ", 17) == 0 ) {
                        parse_viewable_as(sub, plugins, s+19, FALSE);
                  } else {
                        parse_viewable_as(sub, plugins, "", FALSE);
                  }
            }

            
            do {
                  fgets(st, LINE, f); num++;
            } while ( st[0] == '#' );

            if ( strncmp(st+2, "absolutely not viewable as ", 27) == 0 ) {
                  parse_not_viewable_as(sub, s+29);
                  fgets(st, LINE, f); num++;
            }
/*
 *          with every fields
 * (or            with only these fields
 * )
 */
            while ( st[0] == '#' ) {
                  fgets(st, LINE, f); num++;
            }
            if ( strncmp(st+2, "with ", 5) != 0 ) {
                  g_print("Error at line %d.\n", num);
                  return FALSE;
            }
            s = st + 7;
            strchr(s, '\n')[0] = 0;
            type_desc = 0;
            while (type_desc < 2 && strcmp(s, desc_fields[type_desc])!=0 ) {
                  type_desc++;
            }
            
            switch ( type_desc ) {
                  case 0: /* with every fields */
                  {
                        st_fields_every(sub);
                  } break;
                  case 1: /* with only these fields */
                  {
                        st_fields_only_these(sub, f, &num, list_views);
#ifdef DEBUG_GABY
                        debug_print("[st_load_stuct] after sfot\n");
#endif
                  } break;
                  default: /* bzzzt wrong */
                  {
                        g_print("Error at line %d.\n", num);
                        return FALSE;
                  } break;
            }
            
            fgets(st,LINE,f); num++;
      }

/*    if ( views != list_views ) list_views = views;
*/
      fclose(f);

#ifdef DEBUG_GABY
      debug_print("[st_load_struct] the end\n");
#endif

      return TRUE;
}

struct window_info* misc_load_main (GList *view_plugins)
{
      GList *l;
      view *w;
      gchar *windowname;
      gchar *whatilook=NULL;
      char st[LINE+1], *s;
      int num=0;
      FILE *f;
      ViewPluginData *vpd=NULL;
      struct window_info *wi = g_new(struct window_info, 1);
      GList *views = g_list_first(list_views);

      wi->x = -1;
      wi->y = -1;
      wi->width = -1;
      wi->height = -1;
      wi->visible = FALSE;
      
      f = desc_open(appname);
      if ( f == NULL ) {
            wi->view = views->data;
            return wi;
      }
      
      if ( desc_goto_begin(f, "misc", &num) == FALSE ) {
            fclose(f);
            /* nothing ? let's return the first. */
            wi->view = views->data;
            return wi;
      }
      
      fgets(st,LINE,f); num++;

      while ( strncmp(st, "End",3) != 0 ) {
            num++;
            if ( st[0] == '#' ) {
                  fgets(st,LINE,f); num++;
                  continue;
            }
            if ( st[0] != '\t' ) {
                  g_print("Error at line %d.\n", num);
#ifdef DEBUG_GABY
                  debug_print("%s", st);
#endif
                  wi->view = views->data;
                  return wi;
            }

            st[strlen(st)-1] = 0;
            if ( strcmp(st+1, "Main window")==0) {
                  fgets(st, LINE, f); num++;
                  strchr(st, ':')[0] = 0;
                  
                  vpd = get_plugin_by_name(st+2, view_plugins);
                  
                  whatilook=st+strlen(st)+1;
                  strchr(whatilook, '\n')[0] = 0;
                  whatilook = g_strdup(whatilook);
                  
                  fgets(st, LINE, f); num++;
                  while (st[0] == '\t' && st[1] == '\t' && st[2] == '\t'){
                        compress_string(st+2);
                        if ( strchr(st, '=') ) {
                              strchr(st, '=')[0] = 0;
                        } else {
                              fgets(st, LINE, f); num++;
                              continue;
                        }
                        s = st+3;
                        if ( strcmp(s, "size") == 0 ) {
                              s += 5;
                              wi->width = atoi(s);
                              s = strchr(s, 'x')+1;
                              wi->height = atoi(s);
                              fgets(st, LINE, f); num++;
                              continue;
                        }
                  }
                  break;
            }

            fgets(st, LINE, f); num++;
      }
      fclose(f);

      l = g_list_first(list_views);
      while ( l != NULL ) {
            w = l->data;
            windowname = w->subtable->name;
            if ( strcmp(windowname, whatilook) == 0 && w->type == vpd ) {
                  g_free(whatilook);
                  wi->view = w;
                  return wi;
            }
            l = g_list_next(l);
      }
      g_free(whatilook);
      
      wi->view = g_list_first(list_views)->data;
      return wi;
}
                        
static gboolean st_fields_every(subtable *sub)
{
      int i;
      table *t;
      t = sub->table;
      sub->nb_fields = t->nb_fields;
      sub->fields = g_malloc(sizeof(st_field)*sub->nb_fields);
      for ( i=0; i<sub->nb_fields; i++ ) {
            sub->fields[i].name = t->fields[i].name;
            sub->fields[i].i18n_name = t->fields[i].i18n_name;
            sub->fields[i].no = i;
            sub->fields[i].type = t->fields[i].type;
            if ( sub->fields[i].type == T_RECORD ) {
                  sub->fields[i].link_format = g_list_append(NULL,0);
            } else {
                  sub->fields[i].link_format = NULL;
            }
      }
      return TRUE;
}

static GList* create_link_format(gchar *st, table *t, int *dep)
{
      GList *l = NULL;
      gchar *st_orig = st;
      
      if ( st[0] == '{' ) {
            st++;
            while ( strchr(st, ',') ) {
                  strchr(st, ',')[0] = 0;
                  while ( isspace((int)(st[0])) ) st++;
                  l = g_list_append(l, GINT_TO_POINTER( 
                                    table_get_field_no(t, st)));
                  st += strlen(st)+1;
            }
            while ( strchr(st, '}') ) {
                  strchr(st, '}')[0] = 0;
                  while ( isspace((int)(st[0])) ) st++;
                  l = g_list_append(l, GINT_TO_POINTER( 
                                    table_get_field_no(t, st)));
                  st += strlen(st+1);
            }
      } else {
            l = g_list_append(l, GINT_TO_POINTER(table_get_field_no(t, st)));
      }

      *dep = st - st_orig;

      return g_list_first(l);
}

static view* get_view_by_name(GList *views, subtable *ost, gchar *str)
{
      view *v;
      
      views = g_list_first(views);
#ifdef DEBUG_GABY
      debug_print("[gsbn] Looking for a view called %s, with subtable %p\n", 
                  str, ost);
#endif

      if ( views == NULL ) {
            /* looks like we were called from a non-gui gaby, this means
             * views is empty so we'll create a fake view just for you */
            v = g_new0(view, 1);
            v->name = strdup("[blah]");
            v->subtable = ost;
            v->type = NULL;
            return v;
      }
      
      while ( views != NULL ) {
            v = views->data;
            views = g_list_next(views);
            if ( v == NULL ) continue;
            if ( v->type == NULL ) continue;
#ifdef DEBUG_GABY
            debug_print("[gsbn] we face %p and %s\n", v->subtable,
                        v->type->name );
#endif
            if ( v->subtable == ost && 
                        strcasecmp(v->type->name, str) == 0 ) {
                  return v;
            }
      }

#ifdef DEBUG_GABY
      debug_print("[gvbn] problem\n");
#endif
      
      return NULL;
}

static gboolean   st_fields_only_these(subtable *sub, FILE *f, int *num, GList *views)
{
      int i=0;
      table *t;
      table *ttt;
      char st[LINE+1], *s;
      st_field *sf=NULL;
      char lang[15];
      int d;
      subtable *ost;
      
      t = sub->table;
      sub->fields = NULL;
      
      sprintf(lang, "i18n_%s", language);
      
      fgets(st,LINE,f); (*num)++;
      while ( strncmp(st, "\t\t\t", 3 ) == 0 || st[0] == '#' ) {
            if ( st[0] == '#' ) {
                  fgets(st,LINE,f); (*num)++;
                  continue;
            }
                  
            strchr(st, '\n')[0] = 0;
            
            if ( st[3] != '\t' ) {
                  sub->fields = g_realloc(sub->fields, sizeof(st_field)*(i+1));
                  sf = &sub->fields[i];
                  strchr(st, ':')[0] = 0;
                  s = st+3;
                  sf->no = table_get_field_no(t, s);
                  sf->v = NULL;
                  sf->link_format = NULL;
            
                  if ( sf->no == -1 ) {
                        sf->type = T_RECORDS;
                  } else {
                        sf->type = t->fields[sf->no].type;
                  }
#ifdef DEBUG_GABY
                  debug_print("field number : %d, type : %d\n", sf->no, 
                              sf->type );
#endif
                  if ( sf->type == T_RECORD ) {
/*                      t->fields[sf->no].more.from;*/
                        ttt = (field_get_property( &t->fields[sf->no], 
                                    "from"))->val;
#ifdef DEBUG_GABY
                        debug_print("I'll take the data from table %s\n",
                                    ttt->name );
#endif
                        s = strchr(s, '_' )+1;
                        sf->link_format = create_link_format(s,ttt,&d);
                        s += d;
                        
                  }

                  if ( sf->type == T_RECORDS ) {
                        s = strstr(s, "of ");
                        s += 3;
                        ost = get_subtable_by_name(s);
                        /* T_RECORDS support:
                         *  it allows fields like this one :
                         *    \t\t\tList of tracks:
                         *    \t\t\t\twhere I am CD
                         *  but also :
                         *    \t\t\txlist of tracks:
                         *          ...
                         *  (every view plug-in can be used but it will
                         *  only work correctly if the plug-in support
                         *  the "what" data (list and xlist are
                         *  currently the only ones to support this)).
                         */
                        s = st+3;
                        strchr(s, ' ')[0] = 0;
                        sf->v = get_view_by_name(views, ost, s);
                        s[strlen(s)] = ' ';
#ifdef DEBUG_GABY
                        debug_print("[T_RECORDS] from %p\n", sf->v );
#endif
                        
                  }
                  s += strlen(s)+1;
#ifdef DEBUG_GABY
                  debug_print("[st_fields_only_these] s is %s\n", s);
#endif
                  if ( strlen(s) == 0 || ( sf->type != T_RECORDS && 
                              strcmp(t->fields[sf->no].name,s+1)==0)){
                        if ( sf->type == T_RECORDS && sf->v != NULL )  {
                              sf->name = sf->v->name;
                              sf->i18n_name = sf->v->name;
                        } else {
                              sf->name = t->fields[sf->no].name;
                              sf->i18n_name = t->fields[sf->no].i18n_name;
                        }
                  } else {
                        sf->name = g_new(gchar, strlen(s)+1);
                        sf->i18n_name = sf->name;
                        strcpy(sf->name, s);
                  }
                  i++;
            } else {
                  compress_string(st+4);
                  if ( strncmp(lang, st+4, 7) == 0 ) {
#ifdef DEBUG_GABY
                        debug_print("[st_fie...] freeing %p (%s)\n", 
                                    sf->i18n_name, sf->i18n_name );
#endif
                        if ( sf->name != sf->i18n_name )
                              g_free(sf->i18n_name);
                        sf->i18n_name = g_new(gchar, strlen(st+12)+1);
                        strcpy(sf->i18n_name, st+12);
                  }
                  if ( sf->type == T_RECORDS && 
                              strncmp("where I am", st+4, 10)== 0 && 
                              sf->v != NULL ) {
                        s = st+15;
                        d = table_get_field_no(sf->v->subtable->table, s);
                        
#ifdef DEBUG_GABY
                        debug_print("[sfot] where : %s (field %d)\n", s, d);
#endif
                        sf->link_format = g_list_append(sf->link_format,
                                    GINT_TO_POINTER(d));
                  }
            }
            fgets(st,LINE,f); (*num)++;
      }
      
      sub->nb_fields = i;

      fseek(f, -(strlen(st)), SEEK_CUR);

      return TRUE;
}

GList* viewplugins_load(gchar *name)
{
/* Scans config file to know which view plugins to load */
      /* TODO (post-2.0): handle 'absolutely not viewable as'
       * this is only a memory issue (and not that much memory) */
      FILE *f;
      int num=1;
      char st[LINE+1], *s;
      char *elem, *ptstr;
      char *delim=",.";
      GString *plugin_name;
      ViewPluginData *vpd;
      char whole_str[500];
      char *standards;
      gboolean first_time = TRUE;
      GList *vp = NULL;
      
      f = desc_open(name);
      if ( f == NULL )
            return FALSE;
      
      if ( desc_goto_begin(f, "subtables", &num) == FALSE ) {
            fclose(f);
            return FALSE;
      }
      
      standards = get_config_str("common", "misc", "standard_views", "form");
      strcpy(whole_str, standards);
      g_free(standards);
      if ( strlen(whole_str) > 0 ) strcat(whole_str, ", ");

      fgets(st,LINE,f); num++;
      
      while ( strncmp(st, "End",3) != 0 ) {
            if ( strncmp(st, "\t\tviewable as ", 14) == 0 || 
                  strncmp(st, "\t\tonly viewable as ", 19) == 0) {
                  s = strstr(st, "as")+3;
                  if ( first_time ) {
                        strcat(whole_str, s);
                        ptstr = whole_str;
                        first_time = FALSE;
                  } else {
                        ptstr = s;
                  }
                  while (1) {
                        /* TODO (post-2.0): 'never use strtok' 
                         *  this will also allow to load plug-ins
                         *  specified in Gabyrc but that do _not_
                         *  appear at all in the desc file in a cleaner
                         *  way.
                         *
                         *  Delayed since I don't want to break that
                         *  now :)
                         */
                        
                        elem = strtok(ptstr, delim);
                        if ( elem == NULL ) break;
                        while ( elem[0] == ' ' ) elem++;
                        plugin_name = g_string_new(elem);
                        if ( ! vp_already_in(vp, plugin_name) && 
                                    plugin_name->len > 1 ) {
                              vpd = viewplugin_load_one(plugin_name);
                              if ( vpd != NULL ) {
                                    vp = g_list_append(vp, vpd);
                              }
                        }
                        g_string_free(plugin_name,1);
                        ptstr = NULL;
                  }
            }
            fgets(st, LINE, f);
      }
      
      fclose(f);
      
      return vp;
}

static gboolean vp_already_in(GList *vp, GString *pn)
{
      ViewPluginData *vpd;
      
      vp = g_list_first(vp);

      while ( vp ) {
            vpd = vp->data;
            if ( strcasecmp( vpd->name, pn->str )==0 )
                  return TRUE;
            vp = g_list_next(vp);
      }
      
      return FALSE;
}

#ifdef FOLLOW_MIGUEL
#include "py_vplo.c"
#else
static ViewPluginData* viewplugin_load_one(GString *name)
{
      ViewPluginData *vpd = g_new0(ViewPluginData,1);
      GString *modfile;
      char message[1000];
      
      if ( !vpd ) {
            g_print(_("Allocation error. You should better leave\n"));
            return NULL;
      }

      /*
#ifdef FOLLOW_MIGUEL
      if ( strcmp(name->str, "form") == 0 ) {
            vpd->name = g_string_new("form");
            vpd->i18n_name = g_string_new(_("Form"));
            vpd->type = ONE_RECORD;
            vpd->view_create = form_create;
            vpd->view_fill = form_fill;
            vpd->configure = NULL;
            return vpd;
      }
      
      if ( strcmp(name->str, "list") == 0 ) {
            vpd->name = g_string_new("list");
            vpd->i18n_name = g_string_new(_("List"));
            vpd->type = ALL_RECORDS;
            vpd->view_create = list_create;
            vpd->view_fill = list_fill;
            vpd->configure = NULL;
            return vpd;
      }
#endif
      */

      modfile = g_string_new("");
      g_string_sprintf(modfile, PLUGINS_DIR "/view/lib%s" SO_EXTENSION,
                                                name->str);

      vpd->handle = g_module_open(modfile->str, 0);
      
      if ( ! vpd->handle ) {
            sprintf(message, _("Unable to open view plugin : %s\n%s"), modfile->str, g_module_error() );
            g_string_free(modfile, 1);
            g_free(vpd);
            gaby_message = g_strdup(message);
            gaby_errno = CUSTOM_ERROR;
            gaby_perror_in_a_box();
            return NULL;
      }
      
      g_module_symbol(vpd->handle, "init_view_plugin", 
                        (gpointer *)&vpd->init_plugin);
      if ( vpd->configure == NULL ) {
            g_module_symbol(vpd->handle, "configure", 
                        (gpointer *)&vpd->configure);
      }
      vpd->init_plugin(vpd);

      g_string_free(modfile,1);
      return vpd;
}
#endif /* ! FOLLOW_MIGUEL */

static ViewPluginData* get_plugin_by_name(char *name, GList *plugins)
{
      GList *tpi;

      if ( plugins == NULL ) return NULL;
      
      tpi = g_list_first(plugins);
      
      while ( name[0] == ' ' ) name++;

      while (strcasecmp(((ViewPluginData*)tpi->data)->name, name) !=0 ) {
            tpi = g_list_next(tpi);
            if ( tpi == NULL ) return NULL;
      }
      if ( tpi == NULL ) return NULL;

      return tpi->data;
      
}

static gboolean read_action_script(action *act, FILE *f, int *num)
{
      char st[LINE+1];
      char script[3000]; /* this is the maximum size for a script written in
                      * the desc file (not #included)
                      */
      char *pos = script;
      
      /* the first line must be "\t\t{\n" and the last "\t\t}\b"
       * _or_ (for included files) the _only_ line has to be
       * "\t\t#include <scriptname>"
       *
       * _or_ for scripts-fu the _only_ line has to be \t\t<type>:<file>
       * (ex : \t\tpython:hello.py)
       */
      
#ifdef DEBUG_GABY
      debug_print("[read_action_script] begin\n");
#endif
      fgets(st, LINE, f); (*num)++;
      
      if ( strncmp(st,  "\t\t#include ", 11) == 0 ) {
            strchr(st, '\n')[0] = 0;
#ifdef DEBUG_GABY
            debug_print("[read_action_script] we face an #include : %s\n",st+1);
#endif
            act->what.script = g_strdup(st+2);
            return TRUE;
      }

      if ( strchr(st+2, ':') != NULL ) { /* script_fu */
            strchr(st, '\n')[0] = 0;
            act->what.script = g_strdup(st+2);
            return TRUE;
      }
      
      if ( strcmp(st, "\t\t{\n") != 0 )
            return FALSE;
      
      fgets(st, LINE, f); (*num)++;;
      while ( strcmp(st, "\t\t}\n") != 0 ) {
            strcpy(pos, st+2);
            pos += strlen(pos);
            fgets(st, LINE, f); (*num)++;
      }

      act->what.script = g_strdup(script);
#ifdef DEBUG_GABY
      debug_print("[read_action_script] the full script is :\n%s", 
                                          act->what.script);
#endif
      return TRUE;
}

gboolean actions_load(gchar *name)
{
      FILE *f;
      int num=1;
      char st[LINE+1];
      char lang[15];
      action *act;

#ifdef DEBUG_GABY
      debug_print("[actions_load] begin\n");
#endif
      
      f = desc_open(name);
      if ( f == NULL )
            return FALSE;
      
      if ( desc_goto_begin(f, "actions", &num) == FALSE ) {
            fclose(f);
            return FALSE;
      }
      
      sprintf(lang, "i18n_%s", language);
      
      fgets(st,LINE,f); num++;
      while ( strncmp(st, "End",3) != 0 ) {
            if ( st[0] != '\t' ) {
                  fgets(st,LINE,f); num++;
                  continue;
            }
            strchr(st, '\n')[0] = 0;
            act = g_new0(action, 1);
            list_actions = g_list_append(list_actions, act);
            act->i18n_name = g_malloc(strlen(st+1)+1);
            strcpy(act->i18n_name, st+1);
            fgets(st,LINE,f); num++;
            while ( strncmp(st+2,"i18n",4) == 0 ) {
                  strchr(st, '\n')[0] = 0;
                  compress_string(st+2);
                  if ( strncmp(st+2, lang, 7) == 0 ) {
                        g_free(act->i18n_name);
                        act->i18n_name = g_new(gchar, strlen(st+10)+1);
                        strcpy(act->i18n_name, st+10);
                  }
                  fgets(st,LINE,f); num++;
            }
            
            act->event = 0;
            while ( strncmp(st+2, "event", 5) == 0 ) {
                  compress_string(st+2);
                  
                  if        ( strcmp(st+8, "menu") == 0 )
                        act->event |= EVENT_MENU;
                  else if ( strcmp(st+8, "startup") == 0 )
                        act->event |= EVENT_STARTUP;
                  
                  fgets(st,LINE,f); num++;
            }
            if ( act->event == 0 ) act->event = EVENT_MENU;
            
            strchr(st, '\n')[0] = 0;
            find_function(act, st+2);
            if ( act->type == SCRIPT || act->type == SCRIPT_FU ) {
                  if ( ! read_action_script(act, f, &num) ) {
                        g_print("Error at line %d, aborting.\n", num);
                        exit(-5);  /* we don't want problems later */
                  }
            }
            fgets(st,LINE,f); num++;
      }
      fclose(f);
      
      return TRUE;
}

static ActionPluginData* get_actions_plugin_by_name( char *name)
{
      ActionPluginData *apd;
      action *act;
      GList *actions = g_list_first(list_actions);

      while ( actions != NULL ) {
            act = actions->data;
            actions = g_list_next(actions);
            if ( act == NULL ) continue;
            if ( act->type != PLUG_IN ) continue;
            if ( act->what.plugin == NULL ) continue;

            if ( strcmp(act->what.plugin->name, name) == 0 )
                  return act->what.plugin;
      }

      /* TODO (post-2.0): get_actions_plugin_by_name subject to memory leak
       * if not found it allocates a new one ?
       * wtf ?
       * 
       * (delayed since they will be cleaned by free_actions_plugins five
       * minutes later)
       */
      apd = g_new0(ActionPluginData, 1);
      apd->loaded = 0;
      apd->name = g_strdup(name);
      apd->handle = NULL;

      return apd;
}

static void find_function(action *a, char *st)
{
/*
 * Actually it looks like (from desc.gaby) :
 *    net_mail(AB:First Name, AB:Last Name, AB:E-Mail)
 * but this is not perfect and very restrictive (I think), the goal would be
 * something with different parameter types (plain string (as above), record
 * struct, table struct, field numbers, ...)
 * this would give things like :
 * 
 *    stat_number(AddressBook)
 *          to know how many records are in the table 'AddressBook'
 *          
 *    math_sum(CDs, &CD:Duration)
 *          to know the total length of all CDs
 *          (note the '&' before CD:Duration so we can see the difference
 *          between 'net_mail' paramters and this one, the '&' would
 *          result in sending field number instead of field contents)
 * 
 * The lines could also be continued on the next line if they end with a '\'
 *
 * and why not a special 'fake' plug-in :
 *
      !script(AB:First Name, AB:Last Name, AB:E-Mail)
      {
      #! /bin/bash
      echo Mailing to $1 $2
      mail '$1 $2 <$3>'
      }
 *
 * or :
 * 
      !script(...)
      {
      #! /usr/bin/perl
      ...
      }
 * 
 * and (still dreaming :) ) :
 *
      !script(...)
      #include mailto.sh
 *
 * this may seems great but it is :)
 * 
 *
 * footnote : it _is_ not possible to define strings (ie field contents) from
 *          different tables.
 *
 * Update : it seemed great but it is now implemented.
 *
 */
      char *s;
      struct action_param ap;

      /* find plug-in name (NULL if it is a script) */
      
      strchr(st, '(')[0] = 0;
      a->what.plugin = NULL;
      if ( strncmp(st, "!script", 7) == 0 ) {
            if ( strncmp(st+7, "_fu", 3) == 0 ) {
                  a->type = SCRIPT_FU;
            } else {
                  a->type = SCRIPT;
            }
            s = st+strlen(st)+1;
      } else {
            a->type = PLUG_IN;
            strchr(st, '_')[0] = 0;
            a->what.plugin = get_actions_plugin_by_name(st);
            s = st+strlen(st)+1;
            a->name = g_strdup(s);
            s += strlen(s)+1;
      }
      
      /* the end of a param is a ',' , not a ')'. */
      strchr(s, ')')[0] = ',';
      
      a->nb_params = 0;
      a->params = NULL;
      while ( s[1] != 0 ) {
            a->nb_params++;
            strchr(s, ',')[0] = 0;
            if ( strchr(s, ':') == NULL ) {
                  /* we face a table (P_TABLE) or a direct value
                   * (P_DIRECT) */
                  ap.type = P_TABLE;
                  ap.table = get_table_by_name(s);
                  if ( ap.table == NULL ) {
                        ap.type = P_DIRECT;
                        ap.val.str = g_string_new(s);
                  }
            } else {
                  strchr(s,':')[0] = 0;
                  if ( s[0] == '&' ) {
                        /* we face a field number (P_FIELD_NO) */
                        ap.type = P_FIELD_NO;
                        s++;
                  } else {
                        ap.type = P_FIELD;
                  }
                  ap.table = get_table_by_name(s);
                  s += strlen(s)+1;
                  ap.field_no = table_get_field_no(ap.table, s);
            }
            
            a->params = g_realloc(a->params, 
                        sizeof(struct action_param)*a->nb_params);
            memcpy(a->params + (a->nb_params-1), &ap, 
                              sizeof(struct action_param) );
            
            s += strlen(s)+1;
            while ( *s == ' ' ) s++;      /* syntactic sugar */
      }
      
}

static gchar* parse_filename(gchar *st, gchar *table_name)
{
      char fake_path[PATH_MAX];
      gchar *res;
      
      if ( st == NULL )
            return NULL;

      if ( strchr(st, '\n') ) strchr(st, '\n')[0] = 0;
      
#ifdef DEBUG_GABY
      debug_print("Parsing %s\n", st);
#endif
      if ( st[strlen(st)-1] == '/' ) {
            if ( st[0] == '~' ) {
                  sprintf(fake_path, "%s%stable.%s", getenv("HOME"), st+1, table_name);
            } else {
                  sprintf(fake_path, "%stable.%s", st, table_name);
            }
      } else {
            if ( st[0] == '~' ) {
                  sprintf(fake_path, "%s%s", getenv("HOME"), st+1);
            } else {
                  sprintf(fake_path, "%s", st);
            }
      }

      res = g_new0(gchar, strlen(fake_path)+1);
      strcpy(res, fake_path);
      return res;
}

static void locations_every_homes(table *t, char *file, int *offset)
{
      char path_2[PATH_MAX];
      char temp[PATH_MAX];
      char filename[PATH_MAX];
      struct location *loc;
      struct passwd *pw;
      char *gaby = g_strdup("gaby");
      
      if ( strchr(file, '\n') ) strchr(file, '\n')[0] = 0;
      
      if ( file[strlen(file)-1] == '/' ) {
            sprintf(path_2, "%stable.%s", file, t->name );
      } else {
            strcpy(path_2, file);
      }

      strcpy(temp, g_get_tmp_dir());

      pw = getpwent();
      while ( pw != NULL ) {
            if ( strcmp(temp, pw->pw_dir) == 0 ) {
                  /* we don't want to use a known world writable dir */
                  pw = getpwent();
                  continue;
            }
            sprintf(filename, "%s%s", pw->pw_dir, path_2);
            
            loc = g_new0(struct location, 1);
            loc->max_index = 0;
            loc->offset = *offset;
            loc->type = gaby;
            loc->table = t;
            loc->disabled = FALSE;
            *offset += ( 1 << 16 );
            loc->filename = g_strdup(filename);
            t->locations = g_list_append(t->locations, loc);
            pw = getpwent();
      }     
}

static gboolean locations_load(gchar *name)
{
      /* Warning: this function isn't really well written and you should be
       * careful if you add a field to the location struct (the change have
       * to be applied to several place in this function). The same applies
       * if you want to change a behaviour (making read-only the default for
       * example). Once again : be careful. */
      GList *tmp = g_list_first(list_tables);
      FILE *f;
      char st[LINE+1], *str;
      int num=1;
      table *t;
      struct location *loc;
      int offset=1;
      GList *tables = g_list_first(list_tables);

      f = desc_open(name);

      /* no locations section -> default to ~/.gaby */
      if ( f == NULL || desc_goto_begin(f, "locations", &num) == FALSE ) {
            if ( f != NULL ) fclose(f);
            while ( tmp != NULL ) {
                  t = tmp->data;
                  tmp = g_list_next(tmp);
                  if ( t->locations != NULL )
                        continue;
                  loc = g_new0(struct location, 1);
                  loc->max_index = 0;
                  loc->type = g_strdup("gaby");
                  loc->table = t;
                  loc->offset = offset;
                  loc->filename = parse_filename( "~/.gaby/", t->name);
                  loc->readonly = FALSE;
                  loc->disabled = FALSE;
                  t->locations = g_list_append(t->locations, loc);
            }
            return TRUE;
      }

      do {
            fgets(st,LINE,f); num++;
      } while ( st[0] == '#' );
      
      while ( strncmp(st, "End",3) != 0 ) {
            strchr(st, '\n')[0] = 0;
            offset = 1;
            
            /* others -> all tables where locations == NULL */
            if ( strcmp(st+1, "others") == 0 ) {
                  do {
                        fgets(st,LINE,f); num++;
                  } while ( st[0] == '#' );
                  while ( st[0] == '\t' && st[1] == '\t' ) {
                        strchr(st, '\n')[0] = 0;
                        tmp = g_list_first(tables);
                        while ( tmp != NULL ) {
                              t = tmp->data;
                              tmp = g_list_next(tmp);
                              if ( t->locations != NULL )
                                    continue;
                              loc = g_new0(struct location, 1);
                              loc->type = g_strdup("gaby");
                              loc->max_index = 0;
                              loc->table = t;
                              loc->offset = offset;
                              loc->filename = parse_filename( 
                                          st+2, t->name);
                              loc->readonly = FALSE;
                              loc->disabled = FALSE;
                              t->locations = g_list_append( 
                                          t->locations, loc);
                        }
                        do {
                              fgets(st,LINE,f); num++;
                        } while ( st[0] == '#' );
                        offset += ( 1 << 16 );
                  }
                  break;
            }
            
            t = get_table_by_name(st+1);
#ifdef DEBUG_GABY
            debug_print("table : %p %s\n", t, st+1);
#endif
            if ( t == NULL ) return FALSE;
            
            do {
                  fgets(st,LINE,f); num++;
            } while ( st[0] == '#' );
            while ( st[0] == '\t' && st[1] == '\t' ) {
                  if ( st[2] == '~' && st[3] == '*' ) {
                        locations_every_homes(t, st+4, &offset);
                        do {
                              fgets(st,LINE,f); num++;
                        } while ( st[0] == '#' );
                        continue;
                  } else {
                        loc = g_new0(struct location, 1);
                        loc->max_index = 0;
                        loc->readonly = FALSE;
                        loc->disabled = FALSE;
                        loc->filename = parse_filename(st+2, t->name);
                        loc->offset = offset;
                        offset += (1 << 16); /* this put a limitation
                                          * of 65536 records by
                                          * file :) */
                        loc->type = g_strdup("gaby");
                        loc->table = t;
                  }
                  
                  do {
                        fgets(st,LINE,f); num++;
                  } while ( st[0] == '#' );
                  while (st[0]=='\t' && st[1]=='\t' && st[2]=='\t') {
                        compress_string(st+3);
                        if ( strncmp(st+3, "format=", 7) == 0 ) {
                              str = st+3+7;
                              if ( loc->type ) {
                                    g_free(loc->type);
                              }
                              loc->type = g_malloc(strlen(str)+1);
                              strcpy(loc->type, str);
#ifdef DEBUG_GABY
                              debug_print("file type : %s\n", str);
#endif
                              do {
                                    fgets(st,LINE,f); num++;
                              } while ( st[0] == '#' );
                              continue;
                        }
                        if ( strncmp(st+3, "reread=", 7) == 0 ) {
                              loc->reread = atoi(st+3+7);
#ifdef DEBUG_GABY
                              debug_print("I'll reread %s every %d minutes\n",
                                    loc->filename, loc->reread );
#endif
                              do {
                                    fgets(st,LINE,f); num++;
                              } while ( st[0] == '#' );
                              continue;
                        }
                        if ( strncmp(st+3, "read-only", 9) == 0 ) {
                              loc->readonly = TRUE;
                              do {
                                    fgets(st,LINE,f); num++;
                              } while ( st[0] == '#' );
                              continue;
                        }
                        
                        do {
                              fgets(st,LINE,f); num++;
                        } while ( st[0] == '#' );
                  }

                  /* 
                   *  this _can't_ be done in the desc file since this is
                   *  a user option -> get_config_bool()
                   */
                  loc->disabled = get_config_bool("app", appname, 
                                          loc->filename, FALSE );
                  
                  t->locations = g_list_append(t->locations, loc);
                  
            }
      }
      
      fclose(f);
      
      tmp=(tables == NULL ) ? NULL : g_list_first(tables);
      offset = 1;
      while ( tmp != NULL ) {
            t = tmp->data;
            tmp = g_list_next(tmp);
            if ( t->locations != NULL )
                  continue;
            
            loc = g_new0(struct location, 1);
            loc->max_index = 0;
            loc->offset = offset;
            loc->type = g_strdup("gaby");
            loc->table = t;
            loc->disabled = FALSE;
            loc->readonly = FALSE;
            offset += ( 1 << 16 );
            loc->filename = parse_filename( "~/.gaby/", t->name);
            t->locations = g_list_append(t->locations, loc);
      }
      
      return TRUE;
}

GList* misc_load_other_windows (GList *view_plugins)
{
      FILE *f;
      char st[LINE+1];
      int num=0;
      gchar *whatilook, *windowname;
      GList *rl = NULL;
      view *w;
      ViewPluginData *vpd;
      GList *l;
      gboolean found=FALSE;
      char *s;
      struct window_info *winfo;
      
      f = desc_open(appname);
      if ( f == NULL )
            return NULL;
      
      if ( desc_goto_begin(f, "misc", &num) == FALSE ) {
            fclose(f);
            return NULL;
      }
      
      fgets(st,LINE,f); num++;

      while ( strncmp(st, "End",3) != 0 ) {
            num++;
            if ( st[0] == '#' ) {
                  fgets(st,LINE,f); num++;
                  continue;
            }
            if ( st[0] != '\t' ) {
                  g_print("Error at line %d.\n", num);
#ifdef DEBUG_GABY
                  debug_print("%s", st);
#endif
                  return NULL;
            }
            
            st[strlen(st)-1] = 0;
            if ( strcmp(st+1, "Other windows")==0) {
                  fgets(st, LINE, f);
                  while (st[0]=='#' || ( st[0]=='\t' && st[1]=='\t' )){
                        if ( st[0]=='#' || strncmp(st, "\t\t\t",3)==0){
                              fgets(st,LINE,f);
                              num++;
                              continue;
                        }
                        /* TODO (post-2.0): function for winfo creation 
                         * (code related, delayed after 2.0)
                         * it should be shared with misc_load_main() */
                        strchr(st, ':')[0] = 0;
                        vpd = get_plugin_by_name(st+2, view_plugins);
                        if ( vpd == NULL ) {
                              fgets(st, LINE, f);
                              num++;
                              continue;
                        }
                        whatilook=st+strlen(st)+1;
                        strchr(whatilook, '\n')[0] = 0;
                        
                        l = g_list_first(list_views);
                        while ( l != NULL ) {
                              w = l->data;
                              l = g_list_next(l);
                              windowname = w->subtable->name;
                              if (strcmp(windowname, whatilook)==0 &&
                                          w->type == vpd ) {
                                    winfo = g_new0(struct window_info, 1);
                                    winfo->x = -1;
                                    winfo->y = -1;
                                    winfo->height = -1;
                                    winfo->width = -1;
                                    winfo->view = w;
                                    winfo->visible = TRUE;
                                    rl = g_list_append(rl, winfo);
#ifdef DEBUG_GABY
                                    debug_print("[mlow] adding : %s %s\n", vpd->name , w->subtable->name );
#endif
                                    fgets(st,LINE,f); num++;
                                    found = TRUE;
                                    while (strncmp(st, "\t\t\t",3)
                                                == 0 ) {
                                          compress_string(st+3);
                                          s = st+3;
                                          if ( strchr(s, '=') )
                                                strchr(s, '=')[0] = 0;
                                          if ( strcmp(s, "size")
                                                      == 0 ) {
                                                s += 5;
                                                winfo->width = 
                                                      atoi(s);
                                                strchr(s, 'x')[0] = 0;
                                                s += strlen(s);
                                                winfo->height =
                                                      atoi(s);
                                                fgets(st, LINE,
                                                      f);
                                                num++;
                                                continue;
                                          }
                                          if (strcmp(s,"visible=no")
                                                      == 0 ) {
                                                winfo->visible=
                                                      FALSE;
                                                fgets(st, LINE,
                                                      f);
                                                num++;
                                                continue;
                                          }
                                          fgets(st,LINE,f);num++;
                                    }
                              }
                        }
                        if ( found == TRUE ) {
                              found = FALSE;
                        } else {
                              fgets(st,LINE,f); num++;
                        }
                  }
                  break;
            }
            fgets(st, LINE, f); num++;
      }
      
      fclose(f);

      return rl;
}

/*
 * get_plugin_real_name
 * @type: 0 = format, 1 = print
 * @f: the file handle
 * @name: where to put the real name
 * 
 * Description:
 * Search a plug-in infos file for @name
 *
 * Returns: pointer to the result (@name)
 *
 * Comment: this has nothing to do with plug-in so I didn't began the comment
 * with 2 dashes
 */
char* get_plugin_real_name(int type, FILE *f, char *name)
{
      char st[100];
      char looked_for[30];
      int len;
#ifdef FOLLOW_MIGUEL
      if ( type == 0 && strcmp(name, "gaby") == 0 ) strcpy(name, "Gaby");
#endif
      if ( f == NULL ) return name;
      
#if 0
      if ( type == 1 ) {
            /* print plug-ins are not yet using the new format */
            return old_get_plugin_real_name(type, f, name);
      }
#endif
      
      /* this function could be much shorter with a call to get_plugin_info
       * (Thu, 24 Feb 2000 15:57:55 +0100) */
      strcpy(looked_for, name);
      strcat(looked_for, " {\n");
      fseek(f, 0, SEEK_SET);
      fgets(st, 99, f);
      while ( ! feof(f) && strcmp(st, looked_for) != 0 ) {
            fgets(st, 99, f);
      }

      if ( feof(f) ) return name; /* oops, not found */
      
      strcpy(looked_for, "\tname = ");
            /* notice that we are really strict on this */
      len = strlen(looked_for);
      
      fgets(st, 99, f);
      while ( st[0] != '}' && g_strncasecmp(st, looked_for, len) != 0 ) {
            fgets(st, 99, f);
      }

      if ( st[0] == '}' ) return name;
      
      st[strlen(st)-1] = 0;
      strcpy(name, st+len);
      
      return name;
}

/*
 * get_plugin_info
 * @f: the file handle
 * @plugin: the plug-in name (usually lower case)
 * @option: option to look for
 * @buf: where to put the string (should be large enough)
 * 
 * Description:
 * Search a plug-in infos file for the option named @option belonging to the
 * plug-in named @plug-in
 **/
char* get_plugin_info(FILE *f, char *plugin, char *option, char *buf)
{
      char st[100];
      char looked_for[30];
      int len;

      if ( f == NULL ) return NULL;
      
      strcpy(looked_for, plugin);
      strcat(looked_for, " {\n");
      fseek(f, 0, SEEK_SET);
      fgets(st, 99, f);
      while ( ! feof(f) && strcmp(st, looked_for) != 0 ) {
            fgets(st, 99, f);
      }

      if ( feof(f) ) return NULL; /* oops, not found */
      
      looked_for[0] = '\t';
      strcpy(looked_for+1, option);
      strcat(looked_for, " = ");
      len = strlen(looked_for);
      
      fgets(st, 99, f);
      while ( st[0] != '}' && g_strncasecmp(st, looked_for, len) != 0 ) {
            fgets(st, 99, f);
      }

      if ( st[0] == '}' ) return NULL;
      
      st[strlen(st)-1] = 0;
      strcpy(buf, st+len);
      
      return buf;
}

/**
 * format_plugin_can_do
 * @f: file handle of .../infos
 * @name: name of the plugin
 * @t: table who could be loaded
 * @way: 0 if loading, 1 if saving
 *
 * Description:
 * this function looks if a given plug-in is able to load (or save) a file for
 * a given table.
 *
 * Returns: TRUE, FALSE, not much choice
 **/
gboolean format_plugin_can_do (FILE *f, char *name, table *t, gint way)
{
      char st[100];
      char looked_for[30];
      char lf2[50];
      int len;
      long pos;
      
      if ( f == NULL ) return TRUE; /* and the plug-in will have to shout
                               'no!' when we'll try to do it */
      
      strcpy(looked_for, name);
      strcat(looked_for, " {\n");
      rewind(f);
      fgets(st, 99, f);
      while ( ! feof(f) && strcmp(st, looked_for) != 0 ) {
            fgets(st, 99, f);
      }

      if ( feof(f) ) return TRUE; /* the default is 'true' */

      pos = ftell(f);
      
      if ( way == 0 ) strcpy(looked_for, "\tload = ");
      else        strcpy(looked_for, "\tsave = ");
      len = strlen(looked_for);
      
      fgets(st, 99, f);
      while ( st[0] != '}' && g_strncasecmp(st, looked_for, len) != 0 ) {
            fgets(st, 99, f);
      }
      
      if ( st[0] == '}' ) return TRUE;
      
      st[strlen(st)-1] = 0;
      if ( strcmp(st+len, "no") == 0 ) return FALSE;
      
      fseek(f, pos, SEEK_SET);
      
      strcpy(looked_for, "\ttable = ");
      len = strlen(looked_for);

      sprintf(lf2, "%s:%s", appname, t->name);

      while ( st[0] != '}' ) {
            if ( g_strncasecmp(st, looked_for, len ) == 0 ) {
                  st[strlen(st)-1] = 0;
                  if ( g_strcasecmp(st+len, lf2) == 0 || 
                              strcmp(st+len, "*") == 0 ) {
                        return TRUE;
                  }
            }
            fgets(st, 99, f);
      }
      
      return FALSE;
}

#if 0 /* old print plug-in arch */
GList* printplugins_load()
{
      PrintPluginData ppd;
      PrintPluginData *r_ppd = NULL;
      GList *list=NULL;
      char filename[PATH_MAX];
      FILE *f;
      char initfct[100];
#ifdef FOLLOW_MIGUEL
      /* ODOT: detection of available print plug-ins in miguel mode */
      gchar *available[] = { "html", "html2", "latex", NULL };
      int current = 0;
#else
      char dirname[PATH_MAX];
      DIR *dir;
      struct dirent *ent;
#endif

      /* 
       * may the user have plug-ins in her/his home directory ?
       *
       * this issue should be discussed in w_print.c
       */

      /* OTOD: read plug-ins/print/infos 
       *  the list should be generated from this file and not through obscure
       *  ways... :) */

#ifdef FOLLOW_MIGUEL
      while ( available[current] != NULL ) {
            ppd.handle = g_module_open(NULL, G_MODULE_BIND_LAZY);
            sprintf(initfct, "%s_init_print_plugin", available[current]);
            g_module_symbol(ppd.handle, initfct, 
                        (gpointer*)&ppd.init_print_plugin );
            if ( ppd.init_print_plugin == NULL ) {
                  current++;
                  continue;
            }
            if ( ppd.init_print_plugin(&ppd) ) {
                  r_ppd = g_new0(PrintPluginData, 1);
                  memcpy(r_ppd, &ppd, sizeof(PrintPluginData));
                  r_ppd->loaded = 1;
                  strcpy(filename, PLUGINS_DIR "/print/infos");
                  f = fopen(filename, "r");
                  r_ppd->name = g_strdup(available[current]);
                  strcpy(filename, available[current]);
                  r_ppd->i18n_name = g_strdup( 
                        get_plugin_real_name(1, f, filename));
            } else {
                  current++;
                  continue;
            }
            current++;
            list = g_list_append(list, r_ppd);
      }
#else /* ! FOLLOW_MIGUEL */

      dir = opendir(PLUGINS_DIR "/print");

      if ( dir == NULL ) { /* oops */
            g_print("You don't have " PLUGINS_DIR "/print\n");
            return list;
      }
      
      while ( 1 ) {
            ent = readdir(dir);
            if ( ent == NULL ) break;
            if ( strncmp(ent->d_name, "lib", 3) == 0 && 
                  strncmp( ent->d_name + strlen(ent->d_name) - 3, 
                        ".so", 3 ) == 0 ) {
                  sprintf(filename, "%s/print/%s", 
                                    PLUGINS_DIR, ent->d_name);
#ifdef DEBUG_GABY
                  debug_print("[printplugins_load] Is %s ok ?\n", filename);
#endif

                  ppd.handle = g_module_open(filename, 0);
                  if ( ppd.handle == NULL ) {
                        continue;
                  }
                  
                  ent->d_name[strlen(ent->d_name)-3] = 0;
#ifdef DEBUG_GABY
                  debug_print("[ppl] d_name: %s\n", ent->d_name+3);
#endif
                  sprintf(initfct, "%s_init_print_plugin", ent->d_name+3);
                  g_module_symbol(ppd.handle, initfct, 
                              (gpointer*)&ppd.init_print_plugin );
                  if ( ppd.init_print_plugin == NULL ) {
                        g_module_symbol(ppd.handle,"init_print_plugin",
                              (gpointer*)&ppd.init_print_plugin );
                  }
                  if ( ppd.init_print_plugin == NULL ) {
                        g_module_close(ppd.handle);
                        continue;
                  }
                  if ( ppd.init_print_plugin(&ppd) ) {
                        r_ppd = g_new0(PrintPluginData, 1);
                        memcpy(r_ppd, &ppd, sizeof(PrintPluginData));
                        g_module_close(r_ppd->handle);
                        r_ppd->handle = NULL;
                        r_ppd->loaded = 0;
                        strcpy(dirname, strrchr(filename, '/')+1+3 );
                        strchr(dirname, '.')[0] = 0;
                        strcpy(filename, PLUGINS_DIR "/print/infos");
                        f = fopen(filename, "r");
                        r_ppd->name = g_strdup(dirname);
                        r_ppd->i18n_name = g_strdup( 
                              get_plugin_real_name(1, f, dirname) );
#ifdef DEBUG_GABY
                        debug_print("[printplugins_load] adding %s (%s)\n",
                                    r_ppd->name, r_ppd->i18n_name);
#endif
                        list = g_list_append(list, r_ppd);
                  } else {
                        g_module_close(ppd.handle);
                        continue;
                  }
            }
      }
      closedir(dir);
#endif /* ! FOLLOW_MIGUEL */
      
      return list;
}
#else

GList* printplugins_load()
{
      GList *list = NULL;
      FILE *f;
      PrintPluginData *ppd;

      f = fopen(PLUGINS_DIR "/print/infos", "r");
      if ( f == NULL ) return NULL;

      while ( ! feof(f) ) {
            char st[80];
            fgets(st, 79, f);
            if ( st[0] == '#' || st[0] == '\n' || st[0] == 0 )
                  continue;
            
            if ( strchr(st, '{') ) {
                  strchr(st, '{')[-1] = 0;
                  ppd = g_new0(PrintPluginData, 1);
                  ppd->name = g_strdup(st);
                  do {
                        fgets(st, 79, f);
                        if ( st[0] == '\t') {
                              char *s = st+1;
                              compress_string(s);
                              if ( strncmp(s, "name=", 5) == 0 ) {
                                    ppd->i18n_name = g_strdup(s+5);
                              }
                        }
                  } while ( st[0] != '}' );
                  list = g_list_append(list, ppd);
            }
      }

      fclose(f);

      return list;
}

#endif

GList *load_window_bindings(gchar *name, GList *plugins)
{
      GList *main_loop;
      struct w_bindings *wb;
      FILE *f;
      char st[LINE+1];
      gchar *left;
      gchar *right;
      gchar buffer[1000]; /* yes i know ugly */
      gchar *name_of_subtable;
      subtable *subtable;
      int num=0;
      ViewPluginData *vpd;
      
#ifdef DEBUG_GABY 
      debug_print("Reading bindings\n");  
#endif
      f = desc_open(name);
      if ( f == NULL )
            return NULL;
      
      if ( desc_goto_begin(f, "misc", &num) == FALSE ) {
            fclose(f);
            return NULL;
      }
      
      main_loop = NULL;
      wb = NULL;
      name_of_subtable = NULL;
      fgets(st,LINE,f); num++;
      while ( strncmp(st, "End",3) != 0 ) {
            if (st[0]=='\t' && st[1]=='\t' && st[2]!='\t')
            {
                  compress_string(st);
                  wb = NULL;
                  g_free(name_of_subtable);
                  name_of_subtable=NULL;
                  name_of_subtable = g_strdup(st);
            }
            if (st[0]=='\t' && st[1]=='\t' &&  st[2]=='\t' && st[3]!='\t')
            {
                  if (strncasecmp(st+3,"bind to",7)==0 && 
                        name_of_subtable!= NULL)
                  {
                        left = name_of_subtable;
                        right = strchr(name_of_subtable,':');
                        if (right== NULL)
                        {
                              g_print("Cannot find separator at "
                                    "line %d\n", num);
                              return NULL;
                        }
                        right[0]='\0';
                        right=right+1;
                        subtable = get_subtable_by_name(right);
                        left = left +2; /* cut of the tab */
                        vpd = get_plugin_by_name(left, plugins);
                        if ( vpd != NULL ) {
                              g_snprintf( buffer, 999, "%s [%s]", 
                                    subtable->i18n_name, 
                                    vpd->i18n_name);
                              wb= g_malloc(sizeof(struct w_bindings));
                              wb->window_name = g_strdup (buffer);
                              wb->bound_windows = NULL;
                              main_loop = g_list_append(main_loop,wb);
                              g_free(name_of_subtable);
                              name_of_subtable=NULL;
                        }
                  }
            }
            if (st[0]=='\t' && st[1]=='\t' &&
                st[2]=='\t' && st[3]=='\t' && st[4]!='\t')
            {
                  if (wb != NULL)
                  {
                        compress_string(st);
                        left = st;
                        right = strchr(st,':');
                        if (right== NULL)
                        {
                              g_print("Cannot find separator at "
                                    "line %d\n", num);
                              return NULL;
                        }
                        right[0]='\0';
                        right=right+1;
                        subtable = get_subtable_by_name(right);
                        left = left +4; /* cut of the tabs */
                        vpd = get_plugin_by_name(left, plugins);
                        if ( vpd != NULL ) {
                              g_snprintf(buffer,999,"%s [%s]", 
                                    subtable->i18n_name, 
                                    vpd->i18n_name);
                              wb->bound_windows = g_list_append(
                                          wb->bound_windows, 
                                          g_strdup(buffer));
                        }
                  }           
            }

            fgets(st,LINE,f); num++;
      }
      if ( name_of_subtable ) g_free(name_of_subtable);

      return main_loop;
}

/**
 * get_plugin_options
 * @section: the section you want to load
 *
 * Description:
 * This function load a given section from the 'plugins_options' section of the
 * current desc file.
 *
 * Returns: A Glist filled with g_malloc'ed gchar* (please free them)
 **/
GList* get_plugin_options(char *section)
{
      FILE *f;
      int num=0;
      char st[LINE+1];
      char *s;
      char lang[15];
      GList *ret = NULL;
      GList *tmp;
      
      f = desc_open(appname);
      if ( f == NULL )
            return NULL;
      
      if ( desc_goto_begin(f, "plugins_options", &num) == FALSE ) {
            rewind(f);
            do {
                  fgets(st, LINE, f);
                  st[strlen(st)-1] = 0;
                  if ( strrchr(st, ' ') ) strrchr(st, ' ')[0] = 0;
            } while ( (strcmp(st, "import plugins_options from") != 0) && 
                        !feof(f));
            if ( feof(f) ) {
                  fclose(f);
                  return NULL;
            }
#ifdef DEBUG_GABY
            debug_print("but there is an import :)\n");
#endif
            fclose(f);
            s = st+strlen(st)+1;
gpo_of:
            f = desc_open(s);
            if ( f == NULL ) return NULL;
            if ( desc_goto_begin(f, "plugins_options", &num) == FALSE ) {
                  fclose(f);
                  return NULL;
            }
      }
      
      fgets(st,LINE,f); num++;

      while ( strncmp(st, "End",3) != 0 ) {
            if ( st[0] == '#' ) {
                  fgets(st,LINE,f); num++;
                  continue;
            }
            if ( st[0] == '\t' && 
                        strncmp(st+1, section, strlen(section)) == 0 ) {
                  s = st+1+strlen(section);
                  if ( strchr(s, '(') ) {
                        s = strchr(s, '(');
                        if ( strncmp(s, "(imported from ", 15) == 0 ) {
                              s+=15;
                              strchr(s, ')')[0] = 0;
#ifdef DEBUG_GABY
                              debug_print("[plugin_options] section is imported from %s\n", s);
#endif
                              /* officially the first goto in
                               * my life (of C programmer) */
                              goto gpo_of;
                        }
                  }
                  fgets(st, LINE, f); num++;
                  while ( st[0] == '#' || 
                              (st[0] == '\t' && st[1] == '\t') ) {
                        if ( st[0] == '#' ) {
                              fgets(st, LINE, f); num++;
                              continue;
                        }
                        st[strlen(st)-1] = 0;
                        ret = g_list_append(ret, g_strdup(st+2));
                        fgets(st, LINE, f); num++;
                  }
                  break;
            }
            fgets(st, LINE, f); num++;
      }

      if ( ret == NULL ) return ret;

      sprintf(lang, "i18n_%s", language);
      
      tmp = g_list_first(ret);
      while ( tmp != NULL ) {
            s = tmp->data;
            if ( s[0] == '\t' ) {
                  if ( strncmp(s+1, lang, 7) == 0 ) {
#ifdef DEBUG_GABY
                        debug_print("[...] %s\n", s+1);
#endif
                        g_free(tmp->prev->data);
                        tmp->prev->data = g_strdup(strchr(s, '=')+1);
                  }
                  g_free(s);
                  tmp = g_list_remove(tmp, s);
            } else {
                  tmp = g_list_next(tmp);
            }
      }
#ifdef DEBUG_GABY
      tmp = g_list_first(ret);
      while ( tmp != NULL ) {
            debug_print("%s\n", (char*)tmp->data);
            tmp = g_list_next(tmp);
      }
#endif
      return ret;
}

static void free_tables()
{
      GList *ts, *ls;
      struct location *loc;
      table *t;
      int i, j;

      if ( list_tables == NULL ) return;
      ts = g_list_first(list_tables);
      while ( ts != NULL ) {
            t = ts->data;
            g_free(t->name);
            t->name = NULL;
            for (i=0; i<t->nb_fields; i++) {
                  if ( t->fields[i].name != t->fields[i].i18n_name ) {
                        g_free( t->fields[i].i18n_name );
                        t->fields[i].i18n_name = NULL;
                  }
                  g_free( t->fields[i].name );
                  t->fields[i].name = NULL;
                  j = 0;
                  while ( t->fields[i].properties[j] != NULL ) {
                        /* TODO (post-2.0): this doesn't free every
                         * thing (for complex prop->val)
                         * Delayed since it would only allow to free a
                         * few strings and that's not worth the trouble
                         * for now...
                         */
                        g_free(t->fields[i].properties[j]->name);
                        t->fields[i].properties[j]->name = NULL;
                        /*g_free(t->fields[i].properties[j]->val);*/
                        j++;
                  }
                  g_free( t->fields[i].properties );
                  t->fields[i].properties = NULL;
            }
            g_free(t->fields);
            t->fields = NULL;

            ls = t->locations;
            while ( ls != NULL ) {
                  loc = ls->data;
                  g_free(loc->filename);
                  g_free(loc->type);
                  loc->filename = NULL;
                  loc->type = NULL;
                  g_free(loc);
                  ls = g_list_next(ls);
            }
            g_list_free(t->locations);
            t->locations = NULL;
#ifdef INDEXES_ARE_ARRAYS
            for ( i=0; i<t->nb_fields; i++ ) {
                  if ( t->indexes[i] ) g_free(t->indexes[i]);
            }
#else
            for ( i=0; i<t->nb_fields; i++ ) {
                  if ( t->indexes[i] ) g_list_free(t->indexes[i]);
            }
#endif
            
            if ( t->indexes ) {
                  g_free(t->indexes);
                  t->indexes = NULL;
            }
            
            ts = g_list_next(ts);
            g_free(t);
      }
      g_list_free(list_tables);
      list_tables = NULL;
}

static void free_subtables()
{
      GList *list;
      subtable *st;
      st_field *stf;
      int i;

      if ( list_subtables == NULL )
            return;
      list = g_list_first(list_subtables);
      
      while ( list != NULL ) {
            st = list->data;
            for ( i=0; i<st->nb_fields; i++) {
                  stf = &(st->fields[i]);
#if 0 /* TODO (post-2.0): the name are not freed because there are not always
       * allocated (sometimes they are the same as in tables)
       * Delayed: not that much memory involved
       */
      
                  if ( stf->i18n_name != stf->name )
                        g_free(stf->i18n_name);
                  g_free(stf->name);
#endif
                  if ( stf->link_format )
                        g_list_free(stf->link_format);
            }
            g_free(st->fields);
            st->fields = NULL;
            
            if ( st->i18n_name != st->name )  {
                  g_free(st->i18n_name);
                  st->i18n_name = NULL;
            }
            g_free(st->name);
            st->name = NULL;
            g_free(st);

            list = g_list_next(list);
      }
      
      g_list_free(list_subtables);
      list_subtables = NULL;
}

/**
 * free_everything
 *
 * Description:
 * This function will free every little structure allocated by Gaby. Its use
 * should be very limited but is mandatory for PyApache. (actually it only
 * frees records, tables and subtables)
 **/
void free_everything()
{
      GList *list;
      table *t;

      list = g_list_first(list_tables);
      while ( list != NULL ) {
            t = list->data;
            table_free_records(t);
            list = g_list_next(list);
      }

      free_tables();
      free_subtables();
      g_free(appname);
      appname = NULL;
}


Generated by  Doxygen 1.6.0   Back to index