/*--------------------------------------------------------------
  Authors Pierrick Mode, Serge Steer INRIA 2005, Copyright INRIA
  -------------------------------------------------------------*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "../machine.h"
#include "../graphics/Math.h"

#ifdef WIN32
#include "../os_specific/win_mem_alloc.h" /* MALLOC */
#else
#include "../os_specific/sci_mem_alloc.h" /* MALLOC */
#endif


#define  typ_short "s"
#define  typ_ushort "us"
#define  typ_char "c"
#define  typ_uchar "uc"
#define  typ_double "d"
#define  typ_int "i"

extern int ripole(char *inputfile, char *outputfile, int debug, int verbose);
extern void C2F(mgetnc) (integer *fd, void *res, integer *n, char *type, integer *ierr);
extern void C2F(mtell) (integer *fd, double *offset, integer *err);
extern void C2F(mseek) (integer *fd, integer *offset, char *flag, integer *err);
extern FILE *GetFile(integer *fd);
extern int GetSwap(integer *fd);
extern void sciprint __PARAMS ((char *fmt,...));

/*------------------------------------------------------------------*/
/*------------------------------------------------------------------*/
/*Prototype*/
void xls_read(int *fd, int *cur_pos,double **data, int **chainesind, int *N, int *M, int *err);
void xls_open(int *err, int *fd, char ***sst, int *ns, char ***Sheetnames, int** Abspos,int *nsheets);
static double NumFromRk2(long rk);
static void getBoundsheets(int * fd,char ***Sheetnames, int** Abspos, int *nsheets,int *cur_pos,int *err);
static void getSST(int *fd,short Len,int BIFF,int *ns,char ***sst,int *err);
static void getBOF(int *fd ,int* Data, int *err);
static void getString(int *fd,short *count, short *Len, int flag,char **str,int *err);
static int get_oleheader(int *fd);
/*------------------------------------------------------------------*/
/*------------------------------------------------------------------*/

static double return_a_nan()
{
  static int first = 1;
  static double nan = 1.0;

  if ( first )
    {
      nan = (nan - (double) first)/(nan - (double) first);
      first = 0;
    }
  return (nan);
}



void xls_read(int *fd, int *cur_pos,double **data, int **chainesind, int *N, int *M, int *err)
{
  /*---------------Déclaration Des Variables*--------------------*/
  unsigned short Opcode, Len;   /*Code Operationnel et Longueur du tag a lire*/
  double *valeur;    /*Tableau Recapitulatif (Final) des valeurs de la feuille Excel*/
  double pos;

  int one=1;
  int three=3;

  int i;  /*Variables de boucle*/
  int hauteur, longueur, capacite;   /*Hauteur, longueur de la feuille,  */
  int taille; /*Nombre de types de caractèers a enregistrer*/
  char *sheetname;   /*Nom de la feuille*/
  int rkvalue; /*RK value*/
  /*for RK */
  unsigned short row, col, xf;/*Index to row, to column, and to XF record*/
  /*for MULRK */
  unsigned short ixfe;
  short colFirst,colLast,ncol;/*Index to rox, to first column (fc)*/
  /* for LABELSST */
  short labelsst1[3];
  int indsst;/*Index to SST record*/
  /* for DIMENSIONS */
  int f_row,l_row;
  unsigned short f_col, l_col, notused;
  /* for FORMULA */
  double resultat;/*Result of the formula*/
  short optionflag;/*Option flags*/
  int formula_notused; /*Not used*/
  double NaN=return_a_nan();

  int BOFData[7]; /*[BIFF  Version DataType Identifier Year HistoryFlags LowestXlsVersion]*/
  /* initialization of pointers corresponding to malloc's */
  valeur=(double *)NULL;
  sheetname=(char *)NULL;
  *chainesind= (int *) NULL;
  *err=0;

  *cur_pos=*cur_pos;
  C2F(mseek) (fd, cur_pos, "set", err);
  if (*err > 0) goto ErrL;

  /* first record should be a BOF */
  getBOF(fd ,BOFData, err);

  if (*err > 0) return;
  if (BOFData[0]<0) { /* not a BOF */
    *err=2;
    return;
  }
  if (BOFData[0] != 8) { /* not a BIFF8 */
    *err=3;
    return;
  }

  C2F(mtell) (fd, &pos, err);
  if (*err > 0) goto ErrL;
  *cur_pos=(int)pos;

  while(1)
    {
      C2F(mseek) (fd, cur_pos, "set", err);
      if (*err > 0) goto ErrL;
      /*Enregistrement de l'Opcode et de la Len du tag*/
      C2F(mgetnc) (fd, &Opcode, &one, typ_ushort, err);
      if (*err > 0) goto ErrL;
      C2F(mgetnc) (fd, &Len, &one, typ_ushort, err);
      if (*err > 0) goto ErrL;
     switch(Opcode) {
     case 10:/*EOF */
       *N=hauteur;
       *M=longueur;
       *data=valeur;
       *cur_pos=*cur_pos+4+Len;
       return;
     case 638: /*RK*/
       C2F(mgetnc) (fd, (void*)&row, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*)&col, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*)&xf , &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*) &rkvalue , &one, typ_int, err);
       if (*err > 0) goto ErrL;
       valeur[col*(hauteur)+row]= NumFromRk2(rkvalue);
       break;
     case 515: /*Number*/
       C2F(mgetnc) (fd, (void*)&row, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*)&col, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*)&xf , &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*) &resultat , &one, typ_double, err);
       if (*err > 0) goto ErrL;
       valeur[col*(hauteur)+row]=resultat ;
       break;

     case 189: /*MULRK*/
       C2F(mgetnc) (fd, (void*)&row, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd,  (void*)&colFirst, &one, typ_short, err);
       if (*err > 0) goto ErrL;
       /*List of nc=lc-fc+1  XF/RK structures*/
       ncol=(Len-6)/6;	
       for (i=0;i<ncol;i++) {
	 C2F(mgetnc) (fd, (void*) &ixfe, &one, typ_short, err);
	 if (*err > 0) goto ErrL;
	 C2F(mgetnc) (fd, (void*) &rkvalue, &one, typ_int, err);
	 if (*err > 0) goto ErrL;
	 valeur[row+(colFirst+i)*hauteur]=NumFromRk2(rkvalue);
       }

       /*Index of last column*/
       C2F(mgetnc) (fd, (void*) &colLast, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       break;

     case 253:/*LABELSST*/
       C2F(mgetnc) (fd, (void*) labelsst1, &three, typ_short, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*) &indsst , &one, typ_int, err);
       if (*err > 0) goto ErrL;
       /*Allocation dans le tableau final*/
       (*chainesind)[(labelsst1[1])*(hauteur)+labelsst1[0]]=indsst+1;
       break;
     case 512:/* DIMENSIONS*/
       C2F(mgetnc) (fd, (void*) &f_row, &one, typ_int, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*) &l_row, &one, typ_int, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*) &f_col, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*) &l_col, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*) &notused, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
	
       /*Calcul de longueur, hauteur et capacite dela feuille*/
       hauteur=l_row;/*-f_row;*/
       longueur=l_col;/*-f_col;*/
       capacite=hauteur*longueur;
	
       /*Déclaration des tableaux de synthèse*/
       if ((valeur=(void*) MALLOC((capacite+1)*sizeof(double)))==NULL)  goto ErrL;
       if ((*chainesind=(int *) MALLOC((capacite+1)*sizeof(int)))==NULL)  goto ErrL;
       for (i=0;i<=capacite;i++) {
	 (*chainesind)[i]=0;
	 valeur[i]=NaN;
       }
       break;
     case 6:/* FORMULA*/
       C2F(mgetnc) (fd, (void*) &row, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*) &col, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*) &xf, &one, typ_ushort, err);
       if (*err > 0) goto ErrL;
	
       C2F(mgetnc) (fd, (void*) &resultat, &one, typ_double, err);
       if (*err > 0) goto ErrL;

       valeur[(col*hauteur+row)]=resultat;

       C2F(mgetnc) (fd, (void*)&optionflag, &one, typ_short, err);
       if (*err > 0) goto ErrL;
	
       C2F(mgetnc) (fd, (void*) &formula_notused, &one, typ_int, err);
       if (*err > 0) goto ErrL;
	
       /*Formuled data*/
       taille=Len-2-2-2-8-2-4;
       /*char formuladata[taille];
	 C2F(mgetnc) (fd, (void*) formuladata, &taille, typ_char, err);
	 if (*err > 0) goto ErrL;*/

       break;		
     }
     *cur_pos=*cur_pos+4+Len;
    }
  return;
 ErrL:
  {
    FREE(sheetname);
    FREE(valeur);
    FREE(*chainesind);
    if (*err==0)
      *err=1; /* malloc problem */
    else
      *err=2; /* read problem */
    return;
  }
}


void xls_open(int *err, int *fd, char ***sst, int *ns, char ***Sheetnames, int** Abspos,int *nsheets)
{
  /* if opt==1 it is supposed that the current file position is at the beginning of oleheader
   * if opt==0 it is supposed that the current file position is at the  beginning of workbook stream
   */

  /* we suppose that the ole file as a simple structure: 
   * Workbook stream should follows immediately the header 
   * and is strored in sequential sections 
   */

  /*return *err:
    0 = OK
    1 = not an OLE file
    2 = no Workbook included
    3 = memory allocation problem
    4 = incorrect file
    5 = not a BIFF8 xls file
   */
  /*---------------Déclaration Des Variables*--------------------*/
  int k,one=1; 
  int cur_pos, init_pos;
  double pos;
  unsigned short Opcode, Len; 
  /*BOF data*/
  int BOFData[7]; /*[BIFF  Version DataType Identifier Year HistoryFlags LowestXlsVersion]*/
  *nsheets=0;
  *err=0;
  /*---------------Déclaration Des Variables*--------------------*/
  cur_pos=0;

  /*  if (get_oleheader(fd)) {
    *err=1;
    return;
    }*/
  C2F(mtell) (fd, &pos, err);
  cur_pos=(int)pos;
  init_pos=cur_pos;

  /* first record should be a BOF */
  getBOF(fd ,BOFData, err);
  if (*err > 0) return;

  if (BOFData[0]<0) { /* not a BOF */
    *err=4;
    return;
  }
  if (BOFData[0] != 8) { /* not a BIFF8 */
    *err=5;
    return;
  }

  C2F(mtell) (fd, &pos, err);
  if (*err > 0) goto Err2;
  cur_pos=(int)pos;

  /* loops on records till an EOF is found */
  while(1) {
    C2F(mseek) (fd, &cur_pos, "set", err);
    if (*err > 0) goto Err2;
    /*Enregistrement de l'Opcode et de la Len du tag*/
    C2F(mgetnc) (fd, &Opcode, &one, typ_ushort, err);
    if (*err > 0) goto Err2;
    C2F(mgetnc) (fd, &Len, &one, typ_ushort, err);
    if (*err > 0) goto Err2;

    switch(Opcode) {
    case 10: /*EOF*/
      cur_pos=cur_pos+4+Len;
      return ;
    case 133: /* Boundsheets */
      getBoundsheets(fd,Sheetnames, Abspos,nsheets,&cur_pos,err);
      for (k=0;k<*nsheets;k++) (*Abspos)[k]+=init_pos;
      if (*err > 0) return;
      break;
    case 252: /* SST= Shared String table*/
      getSST(fd,Len,BOFData[0],ns,sst,err);
      if (*err > 0) return;
      cur_pos=cur_pos+4+Len;
      break;
    default:
      cur_pos=cur_pos+4+Len;
    }
  }

  return;
 Err2:
  *err=4; /* read problem */
  return;

}

static double NumFromRk2(long rk)
{
  double num;
  if(rk & 0x02)
    {
      /* int*/
      num = (double) (rk >> 2);
    }
  else
    {
      /* hi words of IEEE num*/
      *((int *)&num+1) = rk & 0xfffffffc;
      *((int *)&num) = 0;
    }
  if(rk & 0x01)
    /* divide by 100*/
    num /= 100;
  return num;
}

static void getBOF(int *fd ,int* Data, int *err)
{
  /* return Data a vector [BIFF  Version DataType Identifier Year HistoryFlags LowestXlsVersion]
   * works for BIFF2 to BIFF8 records */
  int BIFF;
  short Version;
  short DataType;
  short Identifier=0;
  short Year=0;
  int HistoryFlags=0;
  int LowestXlsVersion=0;

  unsigned short Opcode;
  unsigned short Len;
  int one=1;

  C2F(mgetnc) (fd, (void*)&Opcode, &one, typ_ushort, err);
  if (*err > 0) return;
  C2F(mgetnc) (fd, (void*)&Len, &one, typ_ushort, err);
  if (*err > 0) return;

  switch(Opcode) {
  case 2057:     /*Begin of file, BOF for BIFF5 BIFF7 BIFF8 BIFF8X*/
    C2F(mgetnc) (fd, (void*)&Version, &one, typ_short, err);
    if (*err > 0) return;
    C2F(mgetnc) (fd, (void*)&DataType, &one, typ_short, err);
    if (*err > 0) return;
    C2F(mgetnc) (fd, (void*)&Identifier, &one, typ_short, err);
    if (*err > 0) return;
    C2F(mgetnc) (fd, (void*)&Year, &one, typ_short, err);
    if (*err > 0) return;
    if (Len==16) {
      C2F(mgetnc) (fd, (void*)&HistoryFlags, &one, typ_int, err);
      if (*err > 0) return;
      C2F(mgetnc) (fd, (void*)&LowestXlsVersion, &one, typ_int, err);
      if (*err > 0) return;
      BIFF=8;
      if (Version != 1536) return;
    }
    else
      BIFF=7;
    break;
  case 1033 : /*Interprétation du BIFF4  0409 H*/
    C2F(mgetnc) (fd, (void*)&Version, &one, typ_short, err);
    if (*err > 0) return;
    C2F(mgetnc) (fd, (void*)&DataType, &one, typ_short, err);
    if (*err > 0) return;
    BIFF=4;
    break;
  case 521 : /*Interprétation du BIFF3  0209 H*/
    C2F(mgetnc) (fd, (void*)&Version, &one, typ_short, err);
    if (*err > 0) return;
    C2F(mgetnc) (fd, (void*)&DataType, &one, typ_short, err);
    if (*err > 0) return;
    BIFF=3;
    break;
  case 9 : /*Interprétation du BIFF2  0009 H*/
    C2F(mgetnc) (fd, (void*)&Version, &one, typ_short, err);
    if (*err > 0) return;
    C2F(mgetnc) (fd, (void*)&DataType, &one, typ_short, err);
    if (*err > 0) return;
    BIFF=2;
    break;
  default:
    BIFF=-1; /* not a BOF record */
    Version=0;
    DataType=0;
  }
  Data[0]=BIFF;
  Data[1]=Version;
  Data[2]=DataType;
  Data[3]=Identifier;
  Data[4]=Year;
  Data[5]=HistoryFlags;
  Data[6]=LowestXlsVersion;

}

static void getSST(int *fd,short Len,int BIFF,int *ns,char ***sst,int *err)
{
  int i,one=1;
  /* SST data */
  int ntot; /*total number of strings */
  int nm;/*Number of following strings*/
  short count=0;

  char *transfert;  /*temp*/
  transfert=(char *)NULL;
  *ns=0;
  *sst=NULL;

  if(BIFF==8) {
    /*Total number of strings in the workbook*/
    C2F(mgetnc) (fd, (void*)&ntot, &one, typ_int, err);
    if (*err > 0) goto ErrL;
    C2F(mgetnc) (fd, (void*)&nm, &one, typ_int, err);
    if (*err > 0) goto ErrL;
    *ns=nm;
    count+=8;
    if (nm !=0) {
      if( (*sst=(char **)MALLOC(nm*sizeof(char*)))==NULL)  goto ErrL;
      for (i=0;i<nm;i++) (*sst)[i]=NULL;
      for(i=0;i<nm;i++) {/* LOOP ON STRINGS */
	*err=i;/*for debug*/
	getString(fd,&count,&Len,1,&((*sst)[i]),err);
	if (*err > 0) goto ErrL;
	/*printf("i=%d, %s\n",i,(*sst)[i]);*/
      }
    }
  }
  return;
 ErrL:
  if (*sst != NULL) {
    for (i=0;i<nm;i++)
      if ( (*sst)[i]!= NULL ) FREE((*sst)[i]);
    FREE(*sst);
  }

  if (*err==0)
    *err=3; /* malloc problem */
  else
    *err=4; /* read problem */
}

static void getString(int *fd,short *PosInRecord, short *RecordLen, int flag,char **str,int *err)
{
  short ln=0;
  short Opcode;/* to store tag information */
  int BytesToBeRead,one=1,strindex;
  char OptionFlag=0;
  int sz; /* for extended string data */
  short rt;/* for rich string data */
  int i=*err;/*for debug*/
  double pos;/*for debug*/
  int UTFEncoding,extendedString,richString;
  int j,l1;

  *str=(char *)NULL;
  *err=0;
  ln=0;

  /*check for continue tag */
 if (flag&&(*PosInRecord==*RecordLen)) {/* data limit encountered */
    /*check for continue tag */
      /*lecture de l'Opcode et de la RecordLen du tag*/
      C2F(mgetnc) (fd, &Opcode, &one, typ_ushort, err);
      if ((*err > 0)||(Opcode!=60)) goto ErrL;
      C2F(mgetnc) (fd, RecordLen, &one, typ_ushort, err);
      if (*err > 0) goto ErrL;
      *PosInRecord=0;
  }

 /* get the number of characters included in the string (number of bytes or number of couple of bytes) */
  if (flag){  /* getString called by getSST */
    C2F(mgetnc) (fd, (void*)&ln, &one, typ_short, err);
    if (*err > 0) goto ErrL;
    *PosInRecord+=2;
  }
  else { /* getString called by getBoundsheets */
    C2F(mgetnc) (fd, (void*)&ln, &one, typ_char, err);
    if (*err > 0) goto ErrL;
    *PosInRecord+=1;
  }

  /*get the encoding options */
  C2F(mgetnc) (fd, (void*)&OptionFlag, &one, typ_char, err);
  if (*err > 0) goto ErrL;
  *PosInRecord+=1;

  UTFEncoding = (OptionFlag&0x01) == 1;
  extendedString = (OptionFlag & 0x04) != 0;
  richString = (OptionFlag & 0x08) != 0;

 if (richString) { /*richString*/
    C2F(mgetnc) (fd, (void*)&rt, &one, typ_short, err);
    *PosInRecord+=2;
    if (*err > 0) goto ErrL;
  }

  if (extendedString) {/* extendedString */
    C2F(mgetnc) (fd, (void*)&sz, &one, typ_int, err);
    if (*err > 0) goto ErrL;
    *PosInRecord+=4;
  }

  /* number of bytes to be read */
  BytesToBeRead = (UTFEncoding)? ln*2 : ln;


  if ((*str= (char*) MALLOC((BytesToBeRead+1)*sizeof(char)))==NULL)  goto ErrL;
  /* read the bytes */

  if (!flag||(*PosInRecord+BytesToBeRead<=*RecordLen)) {	
    /* all bytes are in the same record */
    C2F(mgetnc) (fd, (void*)*str, &BytesToBeRead, typ_char, err);
    if (*err > 0) goto ErrL;
    *PosInRecord+=BytesToBeRead;
  }
  else {/* char stream contains at least one "continue" */
    int bytesRead = *RecordLen-*PosInRecord; /* number of bytes before continue */
    strindex=0; /*current position in str*/
    /* read bytes before the "continue"  */
    /* according to documentation  bytesRead should be strictly positive */
    C2F(mgetnc) (fd, (void*)(*str+strindex), &bytesRead, typ_char, err);
    if (*err > 0) goto ErrL;
    strindex+=bytesRead;
    *PosInRecord+=bytesRead;
    while (BytesToBeRead-bytesRead > 0){
      /*"continue" tag assumed, verify */
      C2F(mgetnc) (fd, &Opcode, &one, typ_ushort, err);
      if ((*err > 0)||(Opcode!=60)) goto ErrL;
      C2F(mgetnc) (fd, RecordLen, &one, typ_ushort, err);
      if (*err > 0) goto ErrL;
      *PosInRecord=0;
      /* encoding option may change !!!! */
      C2F(mgetnc) (fd, (void*)&OptionFlag, &one, typ_char, err);
      if (*err > 0) goto ErrL;
      *PosInRecord+=1;

      if ((!UTFEncoding && (OptionFlag == 0))||(UTFEncoding && (OptionFlag != 0))) {
	/*string encoding does not change */
	l1=Min(BytesToBeRead-bytesRead,*RecordLen-*PosInRecord);
	C2F(mgetnc) (fd, (void*)(*str+strindex), &l1, typ_char, err);
	if (*err > 0) goto ErrL;
	bytesRead+=l1;
	strindex+=l1;
	*PosInRecord+=l1;
      }
      else if (UTFEncoding && (OptionFlag  == 0)) {
	/* character  encoding changes from twobytes to a single byte*/
        /* may this happen ???? */
	l1=Min(BytesToBeRead-bytesRead,*RecordLen-*PosInRecord);
	for (j=0;j<l1;j++){
	  C2F(mgetnc) (fd, (void*)(*str+strindex), &one, typ_char, err);
	  if (*err > 0) goto ErrL;
	  (*str)[strindex+1]='\0';
	  strindex+=2;
	  *PosInRecord+=2;
	  UTFEncoding =0;
	}
      }
      else {
	/* character encoding changes from a single byte to two bytes */
	/* first, convert read characters to two bytes*/
	char *str1=*str;
        strindex=0;
	if ((str= (char*) MALLOC((2*BytesToBeRead+1)*sizeof(char)))==NULL)  goto ErrL;
	for (j=0;j<bytesRead;j++) {
	  (*str)[strindex]=str1[j];
	  (*str)[strindex+1]='\0';
	  strindex+=2;
	}
	FREE(str1);
	BytesToBeRead=BytesToBeRead*2;
	bytesRead=bytesRead*2;
	/* read following two bytes characters */
	l1=Min((BytesToBeRead-bytesRead)*2,*RecordLen-*PosInRecord);
	C2F(mgetnc) (fd, (void*)(*str+strindex), &l1, typ_char, err);
	if (*err > 0) goto ErrL;
	bytesRead+=l1;
	strindex+=l1;
	*PosInRecord+=l1;
	UTFEncoding =1;
      }

    }

  } /*all character read */

  /* For extended strings, skip over the extended string data*/
  /* may continuation records appear here? */
  l1=4*rt;
  if (richString) {C2F(mseek) (fd, &l1, "cur", err);*PosInRecord+=l1;}
  if (extendedString) {C2F(mseek) (fd, &sz, "cur", err);*PosInRecord+=sz;}

  /* add string terminaison */
  if (UTFEncoding) {
    /* Scilab currently do not support unicode, so we remove the second byte*/
    strindex=0;
    for (j=0;j<BytesToBeRead;j+=2) {
      (*str)[strindex]=(*str)[j];
      strindex++;
    }
    BytesToBeRead=BytesToBeRead/2;
  }
  (*str)[BytesToBeRead]='\0';


  return;
 ErrL:
  if (*err == 0) {
    FREE(*str);
    *err=3; /* malloc problem */
  }
  else
    *err=4; /* read problem */
}	

static void getBoundsheets(int * fd,char ***Sheetnames, int** Abspos, int *nsheets,int *cur_pos,int *err)
{/* the global workbook contains a sequence of boudsheets this procedure reads all
 * the sequence and returns a vector o sheetnames, a vector of absolute sheet positions*/
  int abspos; /* Absolute stream position of BoF*/
  char visibility,sheettype;/*Visiblity , Sheet type*/
  int pos;
  unsigned short Opcode;
  unsigned short Len;
  int one=1;
  int ns,i;

  *Sheetnames=(char **)NULL;
  *Abspos=(int *)NULL;
  *err=0;

  /* memorize the first boundsheet beginning */
  pos=*cur_pos;
  /* Count number of boundsheets */
  ns=0;
  while(1) {
    C2F(mseek) (fd, cur_pos, "set", err);
    if (*err > 0) goto ErrL;
    C2F(mgetnc) (fd, &Opcode, &one, typ_ushort, err);
    if (*err > 0) goto ErrL;
    C2F(mgetnc) (fd, &Len, &one, typ_ushort, err);
    if (*err > 0) goto ErrL;
    if (Opcode==133) {
      C2F(mgetnc) (fd, (void*)&abspos, &one, typ_int, err);
      if (*err > 0) goto ErrL;
      C2F(mgetnc) (fd, (void*)&visibility, &one, typ_char, err);
      if (*err > 0) goto ErrL;
      C2F(mgetnc) (fd, (void*)&sheettype, &one, typ_char, err);
      if (sheettype==0) {/* worksheet */
	ns++;
      }
      *cur_pos=*cur_pos+4+Len;
    }
    else
      break;

    }

  *nsheets=ns;
  /*alloc the Sheetnames ans Abspos arrays */
   if( (*Sheetnames=(char **)MALLOC(ns*sizeof(char*)))==NULL)  goto ErrL;
   if( (*Abspos=(int *)MALLOC(ns*sizeof(int)))==NULL)  goto ErrL;

   /* rescan boundsheet sequence to get the data */
   *cur_pos=pos;
   i=-1;
   while(1) {
     C2F(mseek) (fd, cur_pos, "set", err);
     if (*err > 0) goto ErrL;
     C2F(mgetnc) (fd, &Opcode, &one, typ_ushort, err);
     C2F(mgetnc) (fd, &Len, &one, typ_ushort, err);
     if (Opcode==133) {
       C2F(mgetnc) (fd, (void*)&abspos, &one, typ_int, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*)&visibility, &one, typ_char, err);
       if (*err > 0) goto ErrL;
       C2F(mgetnc) (fd, (void*)&sheettype, &one, typ_char, err);
       if (sheettype==0) {/* worksheet */
	 short count=0;
	 i++;
	 (*Abspos)[i]=abspos;
	 getString(fd,&count,&Len, 0,&((*Sheetnames)[i]),err);
	 if (*err > 0) goto ErrL;
       }
       *cur_pos=*cur_pos+4+Len;
     }
     else
       break;

   }
   return;
 ErrL:
   if (*Sheetnames != NULL) {
     for (i=0;i<ns;i++)
       if ( (*Sheetnames)[i]!= NULL ) FREE((*Sheetnames)[i]);
     FREE(*Sheetnames);
   }
   FREE(*Abspos);
   if (*err==0)
     *err=3; /* malloc problem */
   else
     *err=4; /* read problem */
}
static int get_oleheader(int *fd)
{
  unsigned char MAGIC[8] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
  unsigned char header[512];
  int c,ierr;

  C2F(mgetnc) (fd, (void *)header,(c=512,&c), typ_uchar, &ierr);
  if (ierr !=0)  return 1;
  if (memcmp (header, MAGIC, sizeof (MAGIC)) != 0) return 1;
  return 0;
}
