Although yet untested (but it compiles and is still unused) add UAT to the repo.
authorLuis Ontanon <luis.ontanon@gmail.com>
Sun, 28 Jan 2007 10:31:32 +0000 (10:31 -0000)
committerLuis Ontanon <luis.ontanon@gmail.com>
Sun, 28 Jan 2007 10:31:32 +0000 (10:31 -0000)
UAT is an API to handle User Accessible Tables,
an UAT is basically an array of arbitrary structs that has a file representation
as a mean for mantaining things like:
- the snmp_users_table
- dfilter macros
- ipsec/ssl key bindings
- k12 configuration,
- and many other table-like user modifiable preferences

comming soon gtk's uat_window() and prefs_add_uat()

uat.h is fairly doc[uo]m[m]?ented, a README with a simple example of how is to be used will be available as I write them

svn path=/trunk/; revision=20586

epan/Makefile.am
epan/Makefile.common
epan/Makefile.nmake
epan/uat-int.h [new file with mode: 0644]
epan/uat.c [new file with mode: 0644]
epan/uat.h [new file with mode: 0644]
epan/uat_load.l [new file with mode: 0644]

index 8103543c755eac4d61dfb11438a9d04550bc589c..f2b35d3ae0a8183f728005908c97840d65064754 100644 (file)
@@ -116,6 +116,9 @@ radius_dict.c: radius_dict.l
 
 load_snmp_users_file.c: load_snmp_users_file.l
        $(LEX) -oload_snmp_users_file.c $(srcdir)/load_snmp_users_file.l
+
+uat_load.c: uat_load.l
+       $(LEX) -ouat_load.c $(srcdir)/uat_load.l
        
 dtd_parse.c : dtd_parse.l
        $(LEX) -odtd_parse.c $(srcdir)/dtd_parse.l
index 3f1e6dd094eae4a4817b366ca1fc7e0555c022f3..666bd0f36452adc7c55430fee150dd17e76b8ac2 100644 (file)
@@ -88,6 +88,8 @@ LIBWIRESHARK_SRC =            \
        to_str.c                \
        tvbparse.c              \
        tvbuff.c                \
+       uat.c                   \
+       uat_load.c \
        unicode-utils.c         \
        value_string.c          \
        xdlc.c                  \
@@ -185,6 +187,8 @@ LIBWIRESHARK_INCLUDES =     \
        to_str.h                \
        tvbparse.h              \
        tvbuff.h                \
+       uat.h   \
+       uat-int.h       \
        unicode-utils.h         \
        value_string.h          \
        x264_prt_id.h           \
index fcf9622516308aacb09b9e79c214671d7e4e6c0e..f8c771e0d785d1db2e9804adc386193febd0e69f 100644 (file)
@@ -210,6 +210,9 @@ dtd_preparse.c : dtd_preparse.l
 load_snmp_users_file.c : load_snmp_users_file.l
        $(LEX) -oload_snmp_users_file.c load_snmp_users_file.l
 
+uat_load.c : uat_load.l
+       $(LEX) -ouat_load.c uat_load.l
+
 dtd_grammar.h: dtd_grammar.c
 
 LEMON=..\tools\lemon
diff --git a/epan/uat-int.h b/epan/uat-int.h
new file mode 100644 (file)
index 0000000..e3eb29c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  uat-int.h
+ *
+ *  User Accessible Tables
+ *  Mantain an array of user accessible data strucures
+ *  Internal interface
+ *
+ *  (c) 2007, Luis E. Garcia Ontanon
+ *
+ */
+#ifndef _UAT_INT_H_
+#define _UAT_INT_H_
+
+#include "uat.h"
+
+typedef struct _uat_fld_rep_t uat_fld_rep_t;
+typedef struct _uat_rep_t uat_rep_t;
+
+typedef void (*uat_rep_fld_free_cb_t)(uat_fld_rep_t*);
+typedef void (*uat_rep_free_cb_t)(uat_rep_t*);
+
+typedef struct _uat_fld_t {
+       char* name;
+       uat_text_mode_t mode;
+       uat_fld_chk_cb_t chk_cb;
+       uat_fld_set_cb_t set_cb;
+       uat_fld_tostr_cb_t tostr_cb;
+       
+       guint colnum;
+       uat_fld_rep_t* rep;
+       uat_rep_fld_free_cb_t free_rep;
+       
+       struct _uat_fld_t* next;
+} uat_fld_t;
+
+struct _uat_t {
+       char* name;     
+       size_t record_size;
+       char* filename;
+       void** user_ptr;
+       guint* nrows_p;
+       uat_copy_cb_t copy_cb;
+       uat_update_cb_t update_cb;
+       uat_free_cb_t free_cb;
+
+       uat_fld_t* fields;
+       guint ncols;
+       GArray* user_data;
+       gboolean finalized;
+       gboolean locked;
+       
+       uat_rep_t* rep;
+       uat_rep_free_cb_t free_rep;
+};
+
+gchar* uat_get_actual_filename(uat_t* uat, gboolean for_writing);
+void uat_init(void);
+void uat_reset(void);
+void* uat_add_record(uat_t*, const void* orig_rec_ptr);
+void uat_remove_record_idx(uat_t*, guint rec_idx);
+void uat_destroy(uat_t*);
+gboolean uat_save(uat_t* dt, char** error);
+gboolean uat_load(uat_t* dt, char** error);
+
+#define UAT_UPDATE(uat) do { *((uat)->user_ptr) = (void*)((uat)->user_data->data); *((uat)->nrows_p) = (uat)->user_data->len; } while(0)
+
+#endif
diff --git a/epan/uat.c b/epan/uat.c
new file mode 100644 (file)
index 0000000..41c911b
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ *  uat.c
+ *
+ *  User Accessible Tables
+ *  Mantain an array of user accessible data strucures
+ *
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include <glib.h>
+#include <epan/emem.h>
+#include <epan/filesystem.h>
+
+#include "uat-int.h"
+
+static GPtrArray* all_uats = NULL;
+
+void uat_init(void) {
+       all_uats = g_ptr_array_new();
+}
+
+uat_t* uat_start(const char* name,
+                                size_t size,
+                                char* filename,
+                                void** data_ptr,
+                                guint* num_items_ptr,
+                                uat_copy_cb_t copy_cb,
+                                uat_update_cb_t update_cb,
+                                uat_free_cb_t free_cb) {
+       
+       uat_t* uat = g_malloc(sizeof(uat_t));
+       g_ptr_array_add(all_uats,uat);
+       
+       g_assert(name && size && filename && data_ptr && num_items_ptr);
+       
+       uat->name = g_strdup(name);
+       uat->record_size = size;
+       uat->filename = g_strdup(filename);
+       uat->user_ptr = data_ptr;
+       uat->nrows_p = num_items_ptr;
+       uat->copy_cb = copy_cb;
+       uat->update_cb = update_cb;
+       uat->free_cb = free_cb;
+       
+       uat->fields = NULL;
+       uat->ncols = 0;
+       uat->user_data = g_array_new(FALSE,FALSE,uat->record_size);
+       uat->finalized = FALSE;
+       uat->rep = NULL;
+       uat->free_rep = NULL;
+       
+       return uat;
+}
+
+void uat_add_field(uat_t* uat,
+                                  const char* name,
+                                  uat_text_mode_t mode,
+                                  uat_fld_chk_cb_t chk_cb,
+                                  uat_fld_set_cb_t set_cb,
+                                  uat_fld_tostr_cb_t tostr_cb) {
+       
+       uat_fld_t* f = g_malloc(sizeof(uat_fld_t));
+
+       g_assert( name && set_cb && tostr_cb && (! uat->finalized ) 
+                         && (mode == PT_TXTMOD_STRING || mode == PT_TXTMOD_HEXBYTES) );
+       
+       f->name = g_strdup(name);
+       f->mode = mode;
+       f->chk_cb = chk_cb;
+       f->set_cb = set_cb;
+       f->tostr_cb = tostr_cb;
+
+       f->rep = NULL;
+       f->free_rep = NULL;     
+       f->colnum = uat->ncols;
+       f->next = NULL;
+
+       uat->ncols++;
+       
+       if (uat->fields) {
+               uat_fld_t* c;
+               for (c = uat->fields; c->next; c = c->next) ;
+               c->next = f;
+       } else {
+               uat->fields = f;        
+       }
+}
+
+void uat_finalize(uat_t* uat) {
+       UAT_UPDATE(uat);
+       uat->finalized = TRUE;
+}
+
+uat_t* uat_new(const char* uat_name,
+                          size_t size,
+                          char* filename,
+                          void** data_ptr,
+                          guint* numitems_ptr,
+                          uat_copy_cb_t copy_cb,
+                          uat_update_cb_t update_cb,
+                          uat_free_cb_t free_cb,
+                          ...) {
+       uat_t* uat = uat_start(uat_name, size, filename, data_ptr, numitems_ptr, copy_cb, update_cb, free_cb);
+       va_list ap;
+       char* name;
+       uat_text_mode_t mode;
+       uat_fld_chk_cb_t chk_cb;
+       uat_fld_set_cb_t set_cb;
+       uat_fld_tostr_cb_t tostr_cb;
+       va_start(ap,free_cb);
+       
+       name = va_arg(ap,char*);
+       
+       do {
+               mode = va_arg(ap,uat_text_mode_t);
+               chk_cb = va_arg(ap,uat_fld_chk_cb_t);
+               set_cb = va_arg(ap,uat_fld_set_cb_t);
+               tostr_cb = va_arg(ap,uat_fld_tostr_cb_t);
+
+               uat_add_field(uat, name, mode, chk_cb, set_cb, tostr_cb);
+               
+               name = va_arg(ap,char*);
+       } while (name);
+       
+       va_end(ap);
+       
+       uat_finalize(uat);
+       
+       return uat;
+}
+
+void* uat_add_record(uat_t* uat, const void* data) {
+       void* rec;
+       
+       g_assert( uat->finalized );
+       
+       g_array_append_vals (uat->user_data, data, 1);
+       
+       rec = uat->user_data->data + (uat->record_size * (uat->user_data->len-1));
+       
+       if (uat->copy_cb) {
+               uat->copy_cb(rec, data, uat->record_size);
+       }
+       
+       
+       UAT_UPDATE(uat);
+       
+       return rec;
+}
+
+void uat_remove_record_idx(uat_t* uat, guint idx) {
+       
+       g_assert( uat->finalized && idx < uat->user_data->len);
+
+       g_array_remove_index(uat->user_data, idx);
+       
+       UAT_UPDATE(uat);
+
+}
+
+
+gchar* uat_get_actual_filename(uat_t* uat, gboolean for_writing) {
+       gchar* pers_fname =  get_persconffile_path(uat->filename,for_writing);
+       
+       if (! file_exists(pers_fname)) {
+               gchar* data_fname = get_datafile_path(uat->filename);
+               
+               if (file_exists(data_fname)) {
+                       return data_fname;
+               }
+       }
+       
+       return pers_fname;
+}
+
+static void putfld(FILE* fp, void* rec, uat_fld_t* f) {
+       guint fld_len;
+       char* fld_ptr;
+       
+       f->tostr_cb(rec,&fld_ptr,&fld_len);
+       
+       switch(f->mode){
+               case  PT_TXTMOD_STRING: {
+                       guint i;
+                       
+                       putc('"',fp);
+                       
+                       for(i=0;i<fld_len;i++) {
+                               char c = fld_ptr[i];
+                               
+                               if (c == '"') {
+                                       fputs("\134\042",fp);
+                               } else if (isprint(c)) {
+                                       putc(c,fp);
+                               } else {
+                                       fprintf(fp,"\\x%.2x",c);
+                               }
+                       }
+                       
+                       putc('"',fp);
+                       return;
+               }
+               case PT_TXTMOD_HEXBYTES: {
+                       guint i;
+                       
+                       for(i=0;i<fld_len;i++) {
+                               fprintf(fp,"%.2x",fld_ptr[i]);
+                       }
+                       
+                       return;
+               }
+               default:
+                       g_assert_not_reached();
+       }
+}
+
+gboolean uat_save(uat_t* uat, char** error) {
+       guint i;
+       gchar* fname = uat_get_actual_filename(uat,TRUE);
+       FILE* fp = fopen(fname,"w");
+       
+
+       if (!fp) {
+               *error = ep_strdup_printf("uat_save: error opening '%s': %s",fname,strerror(errno));
+               return FALSE;
+       }
+
+       *error = NULL;
+
+       for ( i = 0 ; i < uat->user_data->len - 1 ; i++ ) {
+               void* rec = uat->user_data->data + (uat->record_size * (uat->user_data->len-1));
+               uat_fld_t* f;
+               
+               f = uat->fields;
+               
+               putfld(fp, rec, f);
+                       
+               while (( f = f->next )) {
+                       fputs(",",fp);
+                       putfld(fp, rec, f);                             
+               }
+
+               fputs("\n",fp);
+       }
+
+       fclose(fp);
+       
+       return TRUE;
+}
+
+void uat_destroy(uat_t* uat) {
+       g_ptr_array_remove(all_uats,uat);
+       
+}
+
+void* uat_dup(uat_t* uat, guint* len_p) {
+       guint size = (uat->record_size * uat->user_data->len);
+       *len_p = size;
+       return size ? g_memdup(uat->user_data->data,size) : NULL ;
+}
+
+void* uat_se_dup(uat_t* uat, guint* len_p) {
+       guint size = (uat->record_size * uat->user_data->len);
+       *len_p = size;
+       return size ? se_memdup(uat->user_data->data,size) : NULL ;
+}
+
+void uat_cleanup(void) {
+
+       while( all_uats->len ) {
+               uat_destroy((uat_t*)all_uats->pdata);
+       }
+
+       g_ptr_array_free(all_uats,TRUE);
+}
+
diff --git a/epan/uat.h b/epan/uat.h
new file mode 100644 (file)
index 0000000..5430894
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ *  uat.h
+ *
+ *  User Accessible Tables
+ *  Mantain an array of user accessible data strucures
+ *  
+ *  (c) 2007, Luis E. Garcia Ontanon
+ *
+ */
+
+#ifndef _UAT_H_
+#define _UAT_H_
+
+/*
+ * uat mantains a dynamically allocated table accessible to the user
+ * via a file and/or gui tables.
+ *
+ * the file is located either in userdir(when first read or when writen) or
+ * in datadir for defaults (read only , it will be always written to userdir).
+ *
+ * the behaviour of the table is controlled by a series of callbacks
+ * the caller must provide.
+ *
+ * BEWARE that the user can change an uat at (almost) any time, 
+ * That is pointers to records in an uat are valid only during the call
+ * to the function that obtains them (do not store them).
+ *
+ * UATs are meant for short tables of user data (passwords and such) there's
+ * no quick access, you must iterate through them each time to fetch the record
+ * you are looking for. Use uat_dup() or uat_se_dup() if necessary.
+ *
+ * Only users via gui or editing the file can add/remove records your code cannot.
+ */
+
+/* obscure data type to handle an uat */
+typedef struct _uat_t uat_t;
+
+/********************************************
+ * Callbacks:
+ * these instruct uat on how to deal with user info and data in records
+ ********************************************/
+
+/********
+ * Callbacks for the entire table (these deal with entire records)
+ ********/
+
+/*
+ * Copy CB
+ * used to copy a record
+ * optional, memcpy will be used if not given
+ * copy(dest,orig,len)
+ */
+typedef void* (*uat_copy_cb_t)(void*, const void*, unsigned);
+
+/*
+ *
+ * Free CB
+ *
+ * destroy a record's child data
+ * (do not free the container, it will be handled by uat)
+ * it is optional, no child data will be freed if no present
+ * free(record)
+ */
+typedef void (*uat_free_cb_t)(void*);
+
+/*
+ * Update CB
+ *
+ * to be called after all record fields has been updated
+ * optional, record will be updated always if not given
+ * update(record,&error)
+ */
+typedef void (*uat_update_cb_t)(void* , char** );
+
+
+/*******
+ * Callbacks for single fields (these deal with single values)
+ * the caller should provide one of these for every field!
+ ********/
+
+/* 
+ * given an input string (ptr, len) checks if the value is OK for a field in the record.
+ * it will return TRUE if OK or else
+ * it will return FALSE and may set *error to inform the user on what's
+ * wrong with the given input
+ * optional, if not given any input is considered OK and the set cb will be called
+ * chk(record, ptr, len, &error)
+ */
+typedef gboolean (*uat_fld_chk_cb_t)(void*, const char*, unsigned, char**);
+
+/*
+ * Set Field CB
+ *
+ * given an input string (ptr, len) sets the value of a field in the record,
+ * it will return TRUE if OK or else
+ * it will return FALSE and may set *error to inform the user on what's
+ * wrong with the given input
+ * it is mandatory
+ * set(record, ptr, len)
+ */
+typedef void (*uat_fld_set_cb_t)(void*, const char*, unsigned);
+
+/*
+ * given a record returns a string representation of the field
+ * mandatory
+ * tostr(record, &ptr, &len)
+ */
+typedef void (*uat_fld_tostr_cb_t)(void*, char**, unsigned*);
+
+
+
+
+/*********** 
+ * Text Mode
+ *
+ * used for file and dialog representation of fileds in columns,
+ * when the file is read it modifies the way the value is passed back to the fld_set_cb 
+ * (see definition bellow for description)
+ ***********/
+
+typedef enum _uat_text_mode_t {
+       PT_TXTMOD_NONE,
+       /* not used */
+       
+       PT_TXTMOD_STRING,
+       /*
+        file:
+                reads:
+                        ,"\x20\x00\x30", as " \00",3
+                        ,"", as "",0
+                        ,, as NULL,0
+                writes:
+                        ,"\x20\x30\x00\x20", for " 0\0 ",4
+                        ,"", for *, 0
+                        ,, for NULL, *
+        dialog:
+                accepts \x?? and other escapes
+                gets "",0 on empty string
+        */
+       PT_TXTMOD_HEXBYTES,
+       /*
+        file:
+                reads:
+                        ,A1b2C3d4, as "\001\002\003\004",4
+                        ,, as NULL,0
+                writes:
+                        ,, on NULL, *
+                        ,a1b2c3d4, on "\001\002\003\004",4
+        dialog:
+                "a1b2c3d4" as "\001\002\003\004",4
+                "a1 b2:c3d4" as "\001\002\003\004",4
+                "" as NULL,0
+                "invalid" as NULL,3
+                "a1b" as NULL, 1
+        */
+} uat_text_mode_t;
+
+
+/*
+ * uat_new()
+ *
+ * creates a new uat
+ *
+ * name: the name of the table
+ *
+ * data_ptr: a pointer to a null terminated array of pointers to the data
+ *
+ * default_data: a pinter to a struct containing default values
+ *
+ * size: the size of the structure
+ *
+ * filename: the filename to be used (either in userdir or datadir)
+ *
+ * copy_cb: a function that copies the data in the struct
+ *
+ * update_cb: will be called when a record is updated
+ *
+ * free_cb: will be called to destroy a struct in the dataset
+ *
+ *
+ * followed by a list of N quintuplets terminated by a NULL, each quituplet has:
+ *
+ *   field_name: a string with the name of the field ([a-zA-Z0-9_-]+)
+ *
+ *   field_mode: see comments for enum _uat_text_mode_t below
+ *
+ *   field_chk_cb: a function that given a string will check the given value 
+ *
+ *   field_set_cb: a function that given a string will set the value in the data structure
+ * 
+ *   field_tostr_cb: a function that given a record generates a string,len pair representing this file
+ * 
+ */
+uat_t* uat_new(const char* name,
+                          size_t size,
+                          char* filename,
+                          void** data_ptr,
+                          guint* num_items,
+                          uat_copy_cb_t copy_cb,
+                          uat_update_cb_t update_cb,
+                          uat_free_cb_t free_cb,
+                          ...);
+
+
+/* 
+ * uat_start()
+ * as uat_new() but leaves the dyntable without fields
+ */
+uat_t* uat_start(const char* name,
+                                size_t size,
+                                char* filename,
+                                void** data_ptr,
+                                guint* num_items,
+                                uat_copy_cb_t copy_cb,
+                                uat_update_cb_t update_cb,
+                                uat_free_cb_t free_cb);
+
+/* 
+ * uat_add_field()
+ * adds a field to a uat created with uat_start(),
+ * see uat_new() for description of arguments
+ */
+void uat_add_field(uat_t*,
+                                  const char* name,
+                                  uat_text_mode_t mode,
+                                  uat_fld_chk_cb_t chk_cb,
+                                  uat_fld_set_cb_t set_cb,
+                                  uat_fld_tostr_cb_t tostr_cb);
+
+/* 
+ * uat_finalize()
+ * once fields have been added it makes the uat usable, leaves it locked.
+ */
+void uat_finalize(uat_t*);
+
+/*
+ * uat_dup()
+ * uat_se_dup()
+ * make a reliable copy of an uat for internal use,
+ * so that pointers to records can be kept through calls.
+ * return NULL on zero len.
+ */
+void* uat_dup(uat_t*, guint* len_p); /* to be freed */
+void* uat_se_dup(uat_t*, guint* len_p);
+
+#endif
+
diff --git a/epan/uat_load.l b/epan/uat_load.l
new file mode 100644 (file)
index 0000000..1bd2474
--- /dev/null
@@ -0,0 +1,284 @@
+%option noyywrap
+%option nounput
+%option prefix="uat_load_"
+%option never-interactive
+
+%{
+       /*
+        * one parser to fit them all
+        */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+       
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+       
+#include <glib.h>
+
+#include <epan/emem.h>
+#include "uat-int.h"
+
+       static uat_t* uat;
+       static uat_fld_t* uat_fld;
+       static gchar* ptr;
+       static guint len;
+       static gchar* error;
+       static void* record;
+       
+       static char* unbinstring(const char* si, guint in_len, guint* len_p);
+       static char* undquote(const char* si, guint in_len, guint* len_p);
+       
+#define ERROR(txt) do { error = txt; yyterminate(); } while(0)
+
+#define SET_FIELD() \
+       { gchar* err; \
+       if (uat_fld->chk_cb) { \
+               if ( ! uat_fld->chk_cb(record, ptr, len, &err) ) { \
+                       ERROR(err); \
+               }\
+       }\
+       uat_fld->set_cb(record, ptr, len);\
+       g_free(ptr);\
+       } while(0)
+
+
+%}
+
+quoted_string \042([^\042]|\134\042)*\042
+binstring ([0-9a-zA-Z][0-9a-zA-Z])+
+separator ,
+newline [ \t]*[\r]?\n
+
+%x START_OF_LINE NEXT_FIELD SEPARATOR END_OF_RECORD
+%%
+
+<START_OF_LINE,NEXT_FIELD>{quoted_string} {
+       ptr = undquote(yytext,yyleng,&len);
+               
+       if (( uat_fld = uat_fld->next )) {
+               BEGIN SEPARATOR;
+       } else {
+               BEGIN END_OF_RECORD;
+       }
+}
+
+<START_OF_LINE,NEXT_FIELD>{binstring} {
+       ptr = unbinstring(yytext,yyleng,&len);
+       
+       if (!ptr) {
+               ERROR("uneven hexstring");
+       }
+       
+       if (( uat_fld = uat_fld->next )) {
+               BEGIN SEPARATOR;
+       } else {
+               BEGIN END_OF_RECORD;
+       }
+}
+
+<SEPARATOR>{separator} {
+       SET_FIELD();
+       uat_fld = uat_fld->next;
+       if (! uat_fld ) {
+               ERROR("more fields than required");
+       }
+}
+
+<END_OF_RECORD>{newline} {
+       void* rec;
+       gchar* err = NULL;
+       
+       SET_FIELD();
+       
+       rec = uat_add_record(uat, record);
+       
+       if (uat->update_cb)
+               uat->update_cb(rec,&err);
+       
+       if (err) {
+               ERROR(err);
+       }
+       
+       uat_fld = uat->fields;
+       ptr = NULL;
+       len = 0;
+       memset(record,0,uat->record_size);
+       
+       BEGIN START_OF_LINE;
+ }
+
+{newline} { ERROR("incomplete record"); }
+. { ERROR("unexpected input"); }
+%%
+
+static int xton(char d) {
+       switch(d) {
+               case '0': return 0;
+               case '1': return 1; 
+               case '2': return 2;
+               case '3': return 3;
+               case '4': return 4;
+               case '5': return 5;
+               case '6': return 6;
+               case '7': return 7;
+               case '8': return 8;
+               case '9': return 9;
+               case 'a':  case 'A': return 10;
+               case 'b':  case 'B': return 11;
+               case 'c':  case 'C': return 12;
+               case 'd':  case 'D': return 13;
+               case 'e':  case 'E': return 14;
+               case 'f':  case 'F': return 15;
+               default: return -1;
+       }
+}
+
+static char* unbinstring(const char* si, guint in_len, guint* len_p) {
+       char* buf;
+       guint len = in_len/2;
+       int i = 0;
+       
+       if (in_len%2) {
+               return NULL;
+       }
+       
+       buf= g_malloc(len); /* wastes one byte for every '\\' in text */
+       *len_p = len;
+
+       while(in_len) {
+               int d1 = xton(*(si++));
+               int d0 = xton(*(si++));
+               
+               buf[i++] = (d1 * 16) + d0;
+               
+               in_len -= 2;
+       }
+       
+       return buf;
+}
+
+static char* undquote(const char* si, guint in_len, guint* len_p) {
+       char* buf = g_malloc(in_len); /* wastes one byte for every '\\' in text */
+       char* p = buf;
+       guint len = 0;
+       char* end = buf+in_len;
+       const guint8* s = (void*)si;
+       
+       for (s++; p < end; s++) {
+               switch(*s) {
+                       case '\0':
+                               *(p-1) = '\0';
+                               goto done;
+                       case '\\':
+                               switch(*(++s)) {
+                                       case 'a': *(p++) = '\a'; len++; break;
+                                       case 'b': *(p++) = '\b'; len++; break;
+                                       case 'e': *(p++) = '\e'; len++; break;
+                                       case 'f': *(p++) = '\f'; len++; break;
+                                       case 'n': *(p++) = '\n'; len++; break;
+                                       case 'r': *(p++) = '\r'; len++; break;
+                                       case 't': *(p++) = '\t'; len++; break;
+                                       case 'v': *(p++) = '\v'; len++; break;
+                                               
+                                       case '0':
+                                       case '1': 
+                                       case '2': 
+                                       case '3': 
+                                       case '4': 
+                                       case '5': 
+                                       case '6': 
+                                       case '7': 
+                                       {
+                                               int c0 = 0;
+                                               int c1 = 0;
+                                               int c2 = 0;
+                                               int c = 0;
+                                               
+                                               c0 = (*s) - '0';
+                                               
+                                               if ( s[1] >= '0' && s[1] <= '7' ) {
+                                                       c1 = c0;
+                                                       c0 = (*++s) - '0';
+                                                       
+                                                       if ( s[1] >= '0' && s[1] <= '7' ) {
+                                                               c2 = c1;
+                                                               c1 = c0;
+                                                               c0 = (*++s) - '0';
+                                                       }
+                                               }
+                                               c = (64 * c2) + (8 * c1) + c0;
+                                               *(p++) = (char) (c > 255 ? 255 : c);
+                                               len++;
+                                               break;
+                                       }
+                                               
+                                       case 'x':
+                                       {
+                                               char c1 = *(s+1);
+                                               char c0 = *(s+2);
+                                                                                               
+                                               if (isxdigit(c1) && isxdigit(c0)) {
+                                                       *(p++) = (xton(c1) * 0x10) + xton(c0);
+                                                       s += 2;
+                                               } else {
+                                                       *(p++) = *s;
+                                               }
+                                               len++;
+                                               break;
+                                       }
+                                       default:
+                                               *p++ = *s;
+                                               break;
+                               }
+                               break;
+                       default:
+                               *(p++) = *s;
+                               len++;
+                               break;
+               }
+       }
+       
+done:
+               
+       while ( p < end ) *(p++) = '\0';
+       buf[len] = '\0';
+       len--;
+       if (len_p) *len_p = len;
+       return buf;
+}
+
+gboolean uat_load(uat_t* dt_in, char** err) {
+       gchar* fname = uat_get_actual_filename(uat, FALSE);
+       
+       g_assert(uat->finalized && uat->locked);
+       
+       uat = dt_in;
+       
+       ;
+       if (!(yyin = fopen(fname,"r"))) {
+               *err = strerror(errno);
+               return FALSE;
+       }
+       
+       error = NULL;
+       uat_fld = uat->fields;
+       record = g_malloc0(uat->record_size);
+       
+       BEGIN START_OF_LINE;
+       
+       yylex();
+       
+       if (error) {
+               UAT_UPDATE(uat);
+               *err = ep_strdup(error);
+               return FALSE;
+       } else {
+               UAT_UPDATE(uat);                
+               *err = NULL;
+               return TRUE;
+       }
+}