static char rcsid[] = "@(#)$Id: bindata.c,v 1.7 2006/04/09 07:37:08 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.7 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@ozone.FMI.FI>
 *****************************************************************************/

#include "headers.h"
#include "mmaputil.h"
#include "cs_imp.h"
#include "s_me.h"
#include "bindata.h"

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif

DEBUG_VAR(Debug,__FILE__,"charset");

static unsigned char * us_str P_((char *str));
static unsigned char * us_str(str)
     char *str;
{
    return (unsigned char *)str;
}

#ifdef MMAP
#include <sys/mman.h>

#ifndef MAP_FAILED
#define MAP_FAILED    (void *)(-1)
#endif

#endif

#define TEXT_MAGIC  "ELMME+\n"
/* TEXT_MAGIC is 8 bytes (including \0) */

struct bindata_header_1 {
    uint8             text_magic[sizeof TEXT_MAGIC];
    uint16            magic;
    uint16            header_size;
    uint16            page_size;
    uint16            map_rev_map_div;          /* MAP_REV_MAP_DIV */

    uint16            csets_size;
    uint16            csets_pagecount;
};

#define MAP_FILENAME_SIZE    32

struct csets_1 {   /* both type 'binary' and 'ascii_set' is used same record */
    uint8             filename[MAP_FILENAME_SIZE]; /* filename on elm.map.txt
						      zero filled  */
    uint16            map[256];                 /* charset byte to unicode */
    uint8             revmap[MAP_REV_MAP_DIV];  /* unicode to charset byte mapping */
};


#define BINDATA_magic            0xFB01

enum bindata_format { bindata_bad = 0, bindata_format_1 = BINDATA_magic };

struct bindata_format_1 {
    int                     byte_swapped;
    int                     mmapped;
    
    struct bindata_header_1   *header;
    size_t                   header_length;

    struct csets_1         *csets;
    size_t                    csets_length;
    off_t                     csets_offset;
    int                       csets_count;
};


/* This macro requires ANSI C preprocessor */
#if __STDC__
#define SYSCALL(x) { int ret_=x; int err_=errno;\
if (ret_ == -1)   \
DPRINT(Debug,1,(&Debug,"bindata: syscall %s failed: errno=%d (%s)\n", \
	  #x,err_,error_description(err_))); }
#else
#define SYSCALL(x)  x
#endif

static void free_bindata_1 P_((struct bindata_format_1 *v));
static void free_bindata_1(v) 
     struct bindata_format_1 *v; 
{
    if (!v->mmapped) {  /* malloced */
	if (v->header_length)
	    free(v->header);
	if (v->csets_length)
	    free(v->csets);
    } else {
#ifdef MMAP
	if (v->header_length)
	    SYSCALL(munmap((void  *)v->header,v->header_length));
	if (v->csets_length)
	    SYSCALL(munmap((void  *)v->csets,v->csets_length));
#else
	panic("BINDATA PANIC",__FILE__,__LINE__,"free_bindata_1",
	      "bindata: Internal error -- mmap not available",0);
#endif
	v->mmapped = 0;
    }
    v->header = NULL;
    v->csets = NULL;

    v->header_length = 0;
    v->csets_length = 0;
}

/* Allocate maximun needed space */
static void malloc_bindata_1 P_((struct bindata_format_1 *v, int cscount));
static void malloc_bindata_1(v,cscount)
     struct bindata_format_1 *v;
     int cscount;
{
#ifdef MMAP
    uint16 pagesize   = getpagesize();
#else
    uint16 pagesize   = 512;
#endif
    uint16 header_pages, csets_pages;

    v->byte_swapped = 0;    /* generated -- not swapped */
    v->mmapped      = 0;    /* malloced -- not mapped   */

    v->header_length = sizeof (struct bindata_header_1);
    v->header        = safe_malloc(v->header_length);
    header_pages     = pages(v->header_length,pagesize);


    v->csets_length  = cscount * sizeof (struct csets_1);
    v->csets         = safe_malloc(v->csets_length);
    csets_pages      = pages(v->csets_length,pagesize);
    v->csets_count   = 0;
    v->csets_offset  = pagesize * (off_t) header_pages;

    strfcpy((char *)v->header->text_magic,
	    TEXT_MAGIC,sizeof v->header->text_magic);
    v->header->magic           = bindata_format_1;
    v->header->header_size     = sizeof (struct bindata_header_1);
    v->header->page_size       = pagesize;
    v->header->map_rev_map_div = MAP_REV_MAP_DIV;
    v->header->csets_size   = sizeof (struct csets_1);
    v->header->csets_pagecount  = csets_pages;
}

#define READ_VALUE(x,byte_swapped) (byte_swapped ? SWAP(x) : x)

static void dump_header P_((struct bindata_format_1 *v,
			    int byte_swapped,
			    CONST char *filename));

static int write_bindata_1 P_((struct bindata_format_1 *v, int fd, 
			       CONST char *filename));
static int write_bindata_1(v, fd, filename)
     struct bindata_format_1 *v;
     int fd;
     CONST char *filename;
{   
    uint16 pagesize     = READ_VALUE(v->header->page_size,v->byte_swapped);
    uint16 csets_pages  = READ_VALUE(v->header->csets_pagecount,
				     v->byte_swapped);

    if (!write_range(fd,0,v->header_length,(unsigned char*)v->header)) {
	int err = errno;
	
	DPRINT(Debug,1,(&Debug,
			"bindata: write_range failed: errno=%d (%s)\n", \
			err,error_description(err))); 

	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			  "File %.50s is not writeable: %s"),
		  filename, error_description(err));
	
	return 0;
    }
    
    if (!write_range(fd,v->csets_offset,v->csets_length,
		     (unsigned char *)(v->csets))) {
	int err = errno;
	
	DPRINT(Debug,1,(&Debug,
			"bindata: write_range failed: errno=%d (%s)\n", \
			err,error_description(err))); 

	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			  "File %.50s is not writeable: %s"),
		  filename, error_description(err));
	
	return 0;
    }

    if (v->csets_length < csets_pages * (size_t) pagesize) {
	/* Be sure that file is big enough */
	
	if (!write_range(fd,
			 v->csets_offset + 
			 csets_pages * (off_t) pagesize -1,
			 1, us_str(" "))) {
	    int err = errno;
	    
	    DPRINT(Debug,1,(&Debug,
			    "bindata: write_range failed: errno=%d (%s)\n", \
			    err,error_description(err))); 

	    lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			      "File %.50s is not writeable: %s"),
		      filename, error_description(err));	    
	    return 0;
	}
    } 
    dump_header(v,v->byte_swapped,filename);
    return 1;
}

static void dump_header(v,byte_swapped,filename)
     struct bindata_format_1 *v;
     int byte_swapped;
     CONST char *filename;
{
    if (v->header) {
	DPRINT(Debug,40,(&Debug,"============ Header dump: %-15s  =======\n",
			 filename));
	DPRINT(Debug,40,(&Debug,"BYTE SWAPPED = %d\n",byte_swapped));
	DPRINT(Debug,40,(&Debug,
			 "magic %04X  header_size %04X  page_size %04X\n",
			 READ_VALUE(v->header->magic,v->byte_swapped),
			 READ_VALUE(v->header->header_size,v->byte_swapped),
			 READ_VALUE(v->header->page_size,v->byte_swapped)));
	DPRINT(Debug,40,(&Debug,
			 "map_rev_map_div %04X  csets_size %04X\n",
			 READ_VALUE(v->header->map_rev_map_div,v->byte_swapped),
			 READ_VALUE(v->header->csets_size,v->byte_swapped)));
	DPRINT(Debug,40,(&Debug,
			 "csets_pagecount %04X\n",
			 READ_VALUE(v->header->csets_pagecount,v->byte_swapped)));
	DPRINT(Debug,40,(&Debug,"============ End    dump: %-15s  =======\n",
			 filename));
    }

}


static int calculate_sizes P_((struct bindata_format_1 *v,
			       CONST char *filename));

static int calculate_sizes (v,filename)
     struct bindata_format_1 *v;
     CONST char *filename;
{
    uint16 pagesize =	READ_VALUE(v->header->page_size,v->byte_swapped);
    uint16 header_pages, csets_pages;

    if (READ_VALUE(v->header->header_size,v->byte_swapped) != 
	sizeof (struct bindata_header_1) ||
	READ_VALUE(v->header->csets_size,v->byte_swapped) != 
	sizeof (struct csets_1)) {

	lib_error(CATGETS(elm_msg_cat, MeSet, MeCorruptedMapfile,
			  "Mapfile %.30s is corrupted"),
		  filename);
	DPRINT(Debug,1,(&Debug,"calculate_sizes failed #1 -- sizeof check\n"));
	return 0;
    }

    if (READ_VALUE(v->header->map_rev_map_div,v->byte_swapped) != 
	MAP_REV_MAP_DIV) {
	lib_error(CATGETS(elm_msg_cat, MeSet, MeCorruptedMapfile,
			  "Mapfile %.30s is corrupted"),
		  filename);
	DPRINT(Debug,1,(&Debug,"calculate_sizes failed #2 -- MAP_REV_MAP_DIV\n"));
	return 0;

    }

    header_pages     = pages(v->header_length,pagesize);
    v->csets_offset = pagesize * (off_t) header_pages;
    csets_pages     = READ_VALUE(v->header->csets_pagecount,v->byte_swapped);

    v->csets_length  = pagesize * (off_t) csets_pages;
    /* May give too big value */
    v->csets_count   = v->csets_length / sizeof (struct csets_1);

    return 1;
}

static void debug_output P_((struct bindata_format_1 *v,
			     int status, int pagesize, char *str,
			     CONST char *filename));
static void debug_output(v,status,pagesize,str,filename)
     struct bindata_format_1 *v;
     int status; 
     int pagesize;
     char *str;
     CONST char *filename;
{
    DPRINT(Debug,1,(&Debug,
		    "%s: %s: status=%d, csets_count=%d, pagesize=%d\n",
		    filename,str,status,v->csets_count, pagesize));
    DPRINT(Debug,1,(&Debug,
		    "%s: %s: header_length=%d, csets_length=%d,\n",
		    filename,str,v->header_length,v->csets_length));

    if (v->header) {
	DPRINT(Debug,5,(&Debug,
			"%s: %s: swapped=%d, page_size=%d, csets_pages=%d\n",
			filename,str,v->byte_swapped,
			READ_VALUE(v->header->page_size,
				   v->byte_swapped),
			READ_VALUE(v->header->csets_pagecount,
				   v->byte_swapped)));
    }
}


static int read_bindata_1 P_((struct bindata_format_1 *v, int fd, 
			      int byte_swapped, CONST char *filename));
static int read_bindata_1(v, fd, byte_swapped, filename)
     struct bindata_format_1 *v;
     int fd;
     int byte_swapped;
     CONST char *filename;
{
    int status = 0;
    uint16 pagesize = 0;

    v->byte_swapped  = byte_swapped;
    v->mmapped       = 0;

    v->header_length = sizeof (struct bindata_header_1);
    v->header        = safe_malloc(v->header_length);

    if (!read_range(fd,0,v->header_length,(unsigned char*)v->header)) {
	int err = errno;
	
	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileUnreadable,
			  "File %.50s is unreadable: %s"),
		  filename, error_description(err));
	
	status = 0;
	goto fail_1;
    }

    pagesize =	READ_VALUE(v->header->page_size,v->byte_swapped);

    dump_header(v,byte_swapped,filename);
    if (!calculate_sizes(v,filename)) {
	status = 0;
	goto fail_1;
    }

    v->csets        = safe_malloc(v->csets_length);
    if (!read_range(fd,v->csets_offset,v->csets_length,
		    (unsigned char *)(v->csets))) {
	int err = errno;
	
	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileUnreadable,
			  "File %.50s is unreadable: %s"),
		  filename, error_description(err));
	
	status = 0;
	goto fail_2;
    }

    status = 1;

    if (!status) {

    fail_2:
	free(v->csets);
	v->csets = NULL;
	v->csets_length = 0;

    fail_1:
	free(v->header);
	v->header = NULL;
	v->header_length = 0;
    
    }

    debug_output(v,status,pagesize,"read",filename);

    return status;
}


static int mmap_bindata_1 P_((struct bindata_format_1 *v, int fd, 
			      int byte_swapped, CONST char *filename));
static int mmap_bindata_1(v, fd, byte_swapped, filename)
     struct bindata_format_1 *v;
     int fd;
     int byte_swapped;
     CONST char *filename;
{
    int status = 0;
#ifdef MMAP
    uint16 real_pagesize   = getpagesize();
    uint16 pagesize = 0;
    void * result;

    v->byte_swapped  = byte_swapped;
    v->mmapped       = 1;

    v->header_length = sizeof (struct bindata_header_1);
    result           = mmap(0,v->header_length,PROT_READ,MAP_SHARED,fd,0);
    if (result == MAP_FAILED) {
	int err = errno;
	
	lib_error(CATGETS(elm_msg_cat, MeSet, MeFailedMMap,
			  "Failed to mmap %.50s: %30s"),
		  filename, error_description(err));

	v->header        = NULL;
	v->header_length = 0;
	status           = 0;
	goto fail_1;
    } 
	
    v->header = result;
    pagesize =	READ_VALUE(v->header->page_size,v->byte_swapped);
    
    if (pagesize < real_pagesize || pagesize % real_pagesize != 0) {
	lib_error(CATGETS(elm_msg_cat, MeSet, MeIncompatiblePagesize,
			  "Pagesize of %.50s (%d) incompatible with system pagesize (%d)"),
		  filename, pagesize,real_pagesize);
	status = 0;
	goto fail_2;	
    }
    
    dump_header(v,byte_swapped,filename);
    if (!calculate_sizes(v,filename)) {
	status = 0;
	goto fail_2;
    }

    result           = mmap(0,v->csets_length,PROT_READ,MAP_SHARED,fd,
			    v->csets_offset);
    if (result == MAP_FAILED) {
	int err = errno;
	
	lib_error(CATGETS(elm_msg_cat, MeSet, MeFailedMMap,
			  "Failed to mmap %.50s: %30s"),
		  filename, error_description(err));

	v->csets        = NULL;
	v->csets_length = 0;
	status           = 0;
	goto fail_2;
    } 	
    v->csets = result;


    status = 1;

    if (!status) {
	
    fail_2:
	SYSCALL(munmap((void *)v->header,v->header_length));
	v->header        = NULL;
	v->header_length = 0;	
    }

 fail_1:


    debug_output(v,status,pagesize,"mmap",filename);    
#endif

    return status;
}

static void trim_bindata_1 P_((struct bindata_format_1 *v));
static void trim_bindata_1(v)
     struct bindata_format_1 *v;
{ 
   /* If malloced, truncate according of usage */
   if (!v->mmapped && !v->byte_swapped) {
       uint16 pagesize   = v->header->page_size;
   
       int n                       = v->csets_count;
       if (n < 1) n = 1;
       
       v->csets_length             = n * sizeof (struct csets_1);
       v->header->csets_pagecount = pages(v->csets_length,pagesize);
       v->csets                    = safe_realloc(v->csets,v->csets_length);

   }
   
   /* zero fill unused area */
   if (v->csets_count * sizeof (struct csets_1) < v->csets_length) {

       /* bzero is defined on hdrs/defs.h
	* notice pointer arithmetic 
	*/
       bzero((void *)(v->csets + v->csets_count),
	     v->csets_length - v->csets_count * sizeof (struct csets_1));
   }
}


struct bindata_mapped_data {
    enum bindata_format   format;
    union {
	struct bindata_format_1  f1;
    } v;
};

void free_bindata(v)
     struct bindata_mapped_data * v; 
{
    switch(v->format) {
    case bindata_format_1: free_bindata_1(&(v->v.f1)); 
	break;
    }
    v->format = bindata_bad;
    free(v);
}


int write_bindata(v,filename)
     struct bindata_mapped_data *v; 
     CONST char *filename;
{
    char *tmpname;
    int fd, err;
    int status = 0;
    
    if (0 != access(filename,WRITE_ACCESS)) {
	int err = errno;
	
	DPRINT(Debug,1,(&Debug,
			"bindata: access failed: errno=%d (%s)\n", \
			err,error_description(err))); 

	if (err != ENOENT) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			      "File %.50s is not writeable: %s"),
		      filename, error_description(err));
	
	    return 0;	
	}
    }
    
    tmpname = safe_strdup(filename);
    tmpname = strmcat(tmpname,".N");
    
    err = can_open(tmpname,"w");
    if (err) {	
	DPRINT(Debug,1,(&Debug,
			"bindata: can_open failed: code=%d (%s)\n", \
			err,error_description(err))); 

	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			  "File %.50s is not writeable: %s"),
		  tmpname, error_description(err));
	
	status = 0;
	goto fail;
    }

    fd = open(tmpname,O_WRONLY|O_CREAT|O_EXCL,0644);
    if (-1 == fd) {
	int err = errno;

	DPRINT(Debug,1,(&Debug,
			"bindata: open failed: errno=%d (%s)\n", \
			err,error_description(err))); 

	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			  "File %.50s is not writeable: %s"),
		  tmpname, error_description(err));	
	status = 0;
	goto fail;
    }

     switch(v->format) {
     case bindata_format_1: 
	 status = write_bindata_1(&(v->v.f1),fd,tmpname); 
	 break;
     }

     if (-1 == close(fd)) {
	 int err = errno;

	 DPRINT(Debug,1,(&Debug,
			 "bindata: close failed: errno=%d (%s)\n", \
			 err,error_description(err))); 

	 lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			   "File %.50s is not writeable: %s"),
		   tmpname, error_description(err));	
	 status = 0;
	 goto fail;
     }

     if (status) {
	 if (-1 == rename(tmpname,filename)) {
	     int err = errno;
	     lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotRenamed,
			       "Failed to rename temporary file to %.50s: %.30s"),
		       filename, error_description(err));	
	     status = 0;
	     goto fail;
	 }
     }

 fail:
     if (tmpname)
	 free(tmpname);
     return status;
}



int get_bindata(ptr,filename)
     struct bindata_mapped_data **ptr; 
     CONST char *filename;
{
    struct bindata_mapped_data * res = *ptr;
    int status = 0;

    int fd, err;
    uint8             text_magic[sizeof TEXT_MAGIC];
    uint16            magic;
    int n;

    err = can_open(filename,"r");
    if (err) {
	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileUnreadable,
			  "File %.50s is unreadable: %s"),
		  filename, error_description(err));
	return 0;
    }

    fd = open(filename,O_RDONLY);
    if (-1 == fd) {
	int err = errno;

	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileUnreadable,
			  "File %.50s is unreadable: %s"),
		  filename, error_description(err));
	return 0;
    }

    n = read(fd,text_magic, sizeof text_magic);
    if (n != 8) {  /* text_magic is assumed to be 8 bytes or
		      there is error
		    */
	lib_error(CATGETS(elm_msg_cat, MeSet, MeCorruptedMapfile,
			  "Mapfile %.30s is corrupted"),
		  filename);
	close(fd);
	DPRINT(Debug,1,(&Debug,"get_bindata failed #1 -- text_magic read\n"));
	return 0;
    }

    if (0 != memcmp(text_magic,TEXT_MAGIC,8)) {
	lib_error(CATGETS(elm_msg_cat, MeSet, MeCorruptedMapfile,
			  "Mapfile %.30s is corrupted"),
		  filename);
	close(fd);
	DPRINT(Debug,1,(&Debug,
			"get_bindata failed #1 -- text_magic is wrong: %.8s\n",
			text_magic));
	return 0;
    }

    n = read(fd,(void *)&magic, sizeof magic);
    if (n != 2) {  /* magic is assumed to be 2 bytes or
		    * there is 'Configure' error...
		    */
	lib_error(CATGETS(elm_msg_cat, MeSet, MeCorruptedMapfile,
			  "Mapfile %.30s is corrupted"),
		  filename);
	close(fd);
	DPRINT(Debug,1,(&Debug,"get_bindata failed #1 -- magic read\n"));
	return 0;
    }

    if (res)
	free_bindata(res);
    res = NULL;

    res = safe_malloc(sizeof (struct bindata_mapped_data));
    res->format = bindata_bad;


    if (bindata_format_1 == magic) {
	res->format = bindata_format_1;
	if (!mmap_bindata_1(&(res->v.f1),fd,0,filename) &&
	    !read_bindata_1(&(res->v.f1),fd,0,filename)) {
	    status = 0;
	} else
	    status = 1;
    } else if (bindata_format_1 == SWAP(magic)) {
	res->format = bindata_format_1;
	if (!mmap_bindata_1(&(res->v.f1),fd,1,filename) &&
	    !read_bindata_1(&(res->v.f1),fd,1,filename)) {
	    status = 0;
	} else
	    status = 1;
    } else {
	lib_error(CATGETS(elm_msg_cat, MeSet, MeCorruptedMapfile,
			  "Mapfile %.30s is corrupted"),
		  filename);
	DPRINT(Debug,1,(&Debug, "get_bindata failed #2 -- magic check\n"));
	status = 0;
    }
    
    if (!status) {
	if (res)
	    free(res);
	res = NULL;
    }
    close(fd);
    
    *ptr = res;
    return status;
}

static char * u8s_to_cs P_((uint8 *s));
static char * u8s_to_cs(s)
     uint8 *s;
{
    return (char *)s;
}



static int add_bindata_1_map P_((struct bindata_format_1 *v,
				 uint16 bytevector[256],
				 unsigned char revvector[MAP_REV_MAP_DIV],
				 const char *filename));
				 
static int add_bindata_1_map(v,bytevector,revvector,filename)
     struct bindata_format_1 *v;
     uint16 bytevector[256];
     unsigned char revvector[MAP_REV_MAP_DIV];
     CONST char *filename;
{
    int status = 0,i;
    struct csets_1 *y;
    int L;

   if (v->mmapped || v->byte_swapped) {
       DPRINT(Debug,1,(&Debug,"add_bindata_1_map failed #1 -- not malloced\n"));
       return 0;
   }

   if (strlen(filename) >= sizeof (y->filename)) {
       DPRINT(Debug,1,(&Debug,"add_bindata_1_map failed #2 -- too long filename\n"));
       return 0;
   }


   L = (v->csets_count+1) * sizeof (v->csets[0]);
   if (v->csets_length < L) {
       uint16 pagesize   = v->header->page_size;
       v->csets_length = L;
       v->header->csets_pagecount = pages(v->csets_length,pagesize);
       v->csets = safe_realloc(v->csets, v->csets_length);
   }

   y = & (v->csets[v->csets_count] );

   /* strncpy will \0 fill remainig space if needed 
      There is \0 on end because strlen(filename) < sizeof (y->filename) 
   */
   strncpy(u8s_to_cs(y->filename),filename,sizeof (y->filename));


   for (i = 0; i < 256; i++)
       y->map[i] = bytevector[i];
   for (i = 0; i < MAP_REV_MAP_DIV; i++)
       y->revmap[i] = revvector[i];

   v->csets_count++;
   status = 1;

   return status;

}
				 
int add_bindata_map(v,bytevector,revvector,filename)
     struct bindata_mapped_data * v; 
     uint16 bytevector[256];
     unsigned char revvector[MAP_REV_MAP_DIV];
     CONST char *filename;
{
    int r = 0;

    switch(v->format) {
    case bindata_format_1: 
	r = add_bindata_1_map(&(v->v.f1),
			      bytevector,revvector,filename);
	break;
    }
    
    return r;
}

struct bindata_mapped_data * malloc_bindata(cscount)
     int cscount;
{
    struct bindata_mapped_data *v = safe_malloc(sizeof (*v));

    v->format = bindata_format_1;
    malloc_bindata_1(&(v->v.f1),cscount);

    return v;
}


void trim_bindata(v)
     struct bindata_mapped_data * v; 
{

    switch(v->format) {
    case bindata_format_1: 
	trim_bindata_1(&(v->v.f1));
	break;
    }
}


static int write_bindata_text_1 P_((struct bindata_format_1 *v, 
				    const char *dir, char ***filelist));
static int write_bindata_text_1(v, dir, filelist)
     struct bindata_format_1 *v;
     CONST char *dir;
     char ***filelist;
{
    int status = 0;
    char **list = NULL;
    int m,Y = 0;;

    if (!v->csets_count)
	goto fail1;

    list = safe_malloc((v->csets_count +1) * sizeof (list[0]));

    for (m = 0; m < v->csets_count; m++) {

	char * name = NULL;
	FILE *F;
	int i;
	int err;

	if (! v->csets[m].filename[0])
	    continue;

	name = elm_message(FRM("%s/%s"),
			   dir,v->csets[m].filename);

	err = can_open(name,"w");
	if (err) {	
	    DPRINT(Debug,1,(&Debug,
			    "bindata: can_open failed: code=%d (%s)\n", \
			    err,error_description(err))); 
	    
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			      "File %.50s is not writeable: %s"),
		      name, error_description(err));
	    
	    status = 0;
	    goto fail;
	}

	F = fopen(name,"w");
	if (!F) {
	    int err = errno;
	    DPRINT(Debug,1,(&Debug,
			    "bindata: fopen failed: code=%d (%s)\n", 
			    err,error_description(err))); 
	    
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			      "File %.50s is not writeable: %s"),
		      name, error_description(err));
	    
	    status = 0;
	    goto fail;
	}
	
	fprintf(F,"# %s\n",v->csets[m].filename);

	for (i = 0; i < 256; i++) {
	    uint16 x = READ_VALUE(v->csets[m].map[i],v->byte_swapped);

	    if (MAPPING_NONE == x) {
		fprintf(F,
			"0x%02X\t\t# NO VALUE\n",
			i);
	    } else {
		fprintf(F,
			"0x%02X\t0x%04X\t#\n",
			i,x);
	    }
	}

	
	if (EOF == fflush(F)) {
	    int err = errno;
	    DPRINT(Debug,1,(&Debug,
			    "bindata: fflush failed: code=%d (%s)\n", \
			    err,error_description(err))); 
	    
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			      "File %.50s is not writeable: %s"),
		      name, error_description(err));
	    
	    status = 0;
	}

	fclose(F);

	list[Y++] = name;
	name = NULL;

    fail:
	if (name)
	    free(name);
    }

fail1:
    list[Y] = NULL;

    *filelist = list;
    return status;
}


int write_bindata_text(v,dir,filelist)
     struct bindata_mapped_data *v; 
     CONST char * dir;
     char ***filelist;
{
    int status = 0;
    char ** list = NULL;
    
    switch(v->format) {
    case bindata_format_1: 
	status = write_bindata_text_1(&(v->v.f1),dir,&list); 
	break;
    }

    *filelist = list;
    return status;
}

struct bindata_map_1 {
    struct bindata_format_1  *header;
    int                       cset_number;
};

struct bindata_map {
    enum bindata_format   format;
    union {
	struct bindata_map_1    f1;
    } v;
};

struct bindata_map * give_mapping_1 P_((struct bindata_format_1 *v, 
					const char *mapname));
struct bindata_map * give_mapping_1(v, mapname)
     struct bindata_format_1 *v;
     CONST char *mapname;
{
    struct bindata_map *ret = NULL;
    int m;

    for (m = 0; m < v->csets_count; m++) {
	if (0 == strcmp(u8s_to_cs(v->csets[m].filename),
			mapname))
	    break;
    }
    if (m >= v->csets_count)
	return NULL;

    ret = safe_malloc(sizeof (*ret));
    /* bzero is defined on hdrs/defs.h
     */

    bzero((void *)ret, sizeof (*ret));

    ret->format          = bindata_format_1;
    ret->v.f1.header       = v;
    ret->v.f1.cset_number  = m;

    return ret;
}

struct bindata_map * give_mapping(v,mapname)
     struct bindata_mapped_data *v;
     CONST char *mapname;
{
    struct bindata_map *ret = NULL;

    switch(v->format) {
    case bindata_format_1: 
	ret = give_mapping_1(&(v->v.f1),mapname); 
	break;
    }


    return ret;
}


static unsigned char lookup_binmap_rev_1 P_((struct bindata_map_1 *map,
				      unsigned int val,
				      int * found));
static unsigned char lookup_binmap_rev_1(map,val,found) 
     struct bindata_map_1 *map;
     unsigned int val;
     int * found;
{
    unsigned char c;
    int i;

    int byte_swapped = map->header->byte_swapped;
    struct csets_1 * cset = & (map->header->csets[map->cset_number]);

    /* map is two byte,
       revmap is one byte 
    */

    *found = 0;
    c = cset->revmap[val % MAP_REV_MAP_DIV];

    /* Check case no collision */
    if (READ_VALUE(cset->map[c],byte_swapped) == val) {
	*found = 1;
	return c;
    }

    if (!c)
	return 0;
	
    /* Do scan on case of collision */
    for (i = 0; i < 256; i++) {
	if (val == READ_VALUE(cset->map[i],byte_swapped)) {
	    *found = 1;
	    return i;
	}
    }
    return 0;  /* Not found */
}

static uint16 lookup_binmap_1 P_((unsigned int ch,
				  struct bindata_map_1 *map,
				  int *found));
static uint16 lookup_binmap_1(ch,map,found)
     unsigned int ch;
     struct bindata_map_1 *map;
     int *found;
{
    uint16  val;

    int byte_swapped = map->header->byte_swapped;
    struct csets_1 * cset = & (map->header->csets[map->cset_number]);

    /* map is two byte,
       revmap is one byte 
    */
	   
    val = READ_VALUE(cset->map[ch],byte_swapped);
    if (val != MAPPING_NONE) {
	*found = 1;
	return val;
    }
    
    return 0x003F;  /* '?' */
}

unsigned char lookup_binmap_rev(map,val,found) 
     struct bindata_map *map;
     unsigned int val;
     int * found;
{
    *found = 0;

    switch(map->format) {
    case bindata_format_1:
	return lookup_binmap_rev_1(& (map->v.f1),val,found);
    }
   
    return 0;
}

uint16 lookup_binmap(ch,map,found)
     unsigned int ch;
     struct bindata_map *map;
     int *found;
{
    *found = 0;

    switch(map->format) {
    case bindata_format_1:
	return lookup_binmap_1(ch,& (map->v.f1),found);
    }

    return 0x003F;  /* '?' */
}

/* ---------------------------------------------------------------------- */

struct bindata_mapped_data * default_bindata()
{
    static struct bindata_mapped_data * res = NULL;
    static struct bindata_mapped_data * res_global = NULL;

    if (! bindata_path[0]) {
	DPRINT(Debug,4,(&Debug,
			"BINDATA path not expanded yet.\n"));

	if (bindata_path_global[0] && 
	    strcmp(bindata_path_global,"none") && !res_global) {
	    static int tried = 0;

	    if (!tried) {
		DPRINT(Debug,4,(&Debug,
				"Loading (global) BINDATA information ... (file %s)\n",
				bindata_path_global));
		if (!get_bindata(&res_global,bindata_path_global)) {
		    DPRINT(Debug,4,(&Debug,
				    "... loading of BINDATA information failed\n"));
		}
		tried = 1;
	    }
	    return res_global;	    
	}

	if (res_global) {
	    DPRINT(Debug,4,(&Debug,
				" ... using global.\n"));
	}
	return res_global;
    }

    if (0 != strcmp(bindata_path,"none") && !res) {	
	static int tried = 0;

	if (0 == strcmp(bindata_path,bindata_path_global) && res_global) {

	    DPRINT(Debug,4,(&Debug,
			    "Using global as user BINDATA  ... (file %s)\n",
			    bindata_path));

	    res = res_global;
	} else	if (!tried) {
	    DPRINT(Debug,4,(&Debug,
			    "Loading (user) BINDATA information ... (file %s)\n",
			    bindata_path));
	    if (!get_bindata(&res,bindata_path)) {
		DPRINT(Debug,4,(&Debug,
				"... loading of BINDATA information failed\n"));
	    }
	    tried = 1;
	}
    }
    return res;
}



/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1