blob: d7cf485cf073c4687a077906083c5fb1c2389810 [file] [log] [blame]
/*
* Here is a very simple set of routines to write an Excel worksheet
* Microsoft BIFF format. The Excel version is set to 2.0 so that it
* will work with all versions of Excel.
*
* Author: Don Capps
*/
/*
* Note: rows and colums should not exceed 255 or this code will
* act poorly
*/
#ifdef Windows
#include <Windows.h>
#endif
#include <sys/types.h>
#include <stdio.h>
#include <sys/file.h>
#if defined(__AIX__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__Android__)
#include <fcntl.h>
#else
#include <sys/fcntl.h>
#endif
#if defined(OSV5) || defined(linux) || defined (__FreeBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__) || defined(__DragonFly__)
#include <string.h>
#endif
#if defined(linux) || defined(__DragonFly__) || defined(macosx)
#include <unistd.h>
#include <stdlib.h>
#endif
#if (defined(solaris) && defined( __LP64__ )) || defined(__s390x__) || defined(FreeBSD)
/* If we are building for 64-bit Solaris, all functions that return pointers
* must be declared before they are used; otherwise the compiler will assume
* that they return ints and the top 32 bits of the pointer will be lost,
* causing segmentation faults. The following includes take care of this.
* It should be safe to add these for all other OSs too, but we're only
* doing it for Solaris now in case another OS turns out to be a special case.
*/
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#endif
/* Little Endian */
#define ENDIAN_1 1
/* Big Endian */
#define ENDIAN_2 2
/* Middle Endian */
#define ENDIAN_3 3
/* Middle Endian */
#define ENDIAN_4 4
int junk, *junkp;
#ifdef HAVE_ANSIC_C
/************************************************************************/
/* Here is the API... Enjoy */
/************************************************************************/
/* Create worksheet */
int create_xls(char *);
/* Args: Filename */
/* */
/* Close worksheet */
void close_xls(int);
/* Args: file descriptor */
/* */
/* Put a 16 bit integer in worksheet */
void do_int(int,int,int,int);
/* Args: file descriptor, */
/* value, */
/* row, */
/* column */
/* Put a double in 8 byte float */
void do_float(int,double,int,int);
/* Args: file descriptor, */
/* value, */
/* row, */
/* column */
/* Put a string in worksheet */
void do_label(int,char *,int,int);
/* Args: file descriptor, */
/* string, */
/* row, */
/* column */
/************************************************************************/
char libbif_version[] = "Libbif Version $Revision: 3.25 $";
void do_eof(int ); /* Used internally */
void do_header(int ); /* Used internally */
int endian(void);
#endif
#define BOF 0x9
#define INTEGER 0x2
#define FLOAT 0x3
#define LABEL 0x4
#define EXCEL_VERS 0x2
#define WORKSHEET 0x10
struct bof_record{ /* Beginning of file */
char hi_opcode;
char lo_opcode;
char hi_length;
char lo_length;
char hi_version; /* Excel version */
char lo_version;
char hi_filetype;
char lo_filetype;
};
struct int_record {
char hi_opcode; /* Type 2 of record */
char lo_opcode;
char hi_length;
char lo_length;
char hi_row;
char lo_row;
char hi_column;
char lo_column;
char rgbhi;
char rgbmed;
char rgblo;
char hi_data;
char lo_data;
};
struct label_record {
char hi_opcode; /* Type 4 of record */
char lo_opcode;
char hi_length;
char lo_length;
char hi_row;
char lo_row;
char hi_column;
char lo_column;
char rgbhi;
char rgbmed;
char rgblo;
char string_length;
char str_array[256];
};
struct float_record { /* Type 3 record */
char hi_opcode;
char lo_opcode;
char hi_length;
char lo_length;
char hi_row;
char lo_row;
char hi_column;
char lo_column;
char rgbhi;
char rgbmed;
char rgblo;
double data;
};
/*
* Write the EOF and close the file
*/
#ifdef HAVE_ANSIC_C
void
close_xls(int fd)
{
#else
close_xls(fd)
int fd;
{
#endif
do_eof(fd);
close(fd);
}
/*
* Create xls worksheet. Create file and put the BOF record in it.
*/
#ifdef HAVE_ANSIC_C
int
create_xls(char *name)
{
#else
create_xls(name)
char *name;
{
#endif
int fd;
unlink(name);
#ifdef Windows
fd=open(name,O_BINARY|O_CREAT|O_RDWR,0666);
#else
fd=open(name,O_CREAT|O_RDWR,0666);
#endif
if(fd<0)
{
printf("Error opening file %s\n",name);
exit(-1);
}
do_header(fd);
return(fd);
}
#ifdef HAVE_ANSIC_C
void
do_header(int fd) /* Stick the BOF at the beginning of the file */
{
#else
do_header(fd)
int fd;
{
#endif
struct bof_record bof;
bof.hi_opcode=BOF;
bof.lo_opcode = 0x0;
bof.hi_length=0x4;
bof.lo_length=0x0;
bof.hi_version=EXCEL_VERS;
bof.lo_version=0x0;
bof.hi_filetype=WORKSHEET;
bof.lo_filetype=0x0;
junk=write(fd,&bof,sizeof(struct bof_record));
}
/*
* Put an integer (16 bit) in the worksheet
*/
#ifdef HAVE_ANSIC_C
void
do_int(int fd,int val, int row, int column)
{
#else
do_int(fd,val,row,column)
int fd,val,row,column;
{
#endif
struct int_record intrec;
short s_row,s_column;
s_row=(short)row;
s_column=(short)column;
intrec.hi_opcode=INTEGER;
intrec.lo_opcode=0x00;
intrec.hi_length=0x09;
intrec.lo_length=0x00;
intrec.rgbhi=0x0;
intrec.rgbmed=0x0;
intrec.rgblo=0x0;
intrec.hi_row=(char)s_row&0xff;
intrec.lo_row=(char)(s_row>>8)&0xff;
intrec.hi_column=(char)(s_column&0xff);
intrec.lo_column=(char)(s_column>>8)&0xff;
intrec.hi_data=(val & 0xff);
intrec.lo_data=(val & 0xff00)>>8;
junk=write(fd,&intrec,13);
}
/* Note: This routine converts Big Endian to Little Endian
* and writes the record out.
*/
/*
* Put a double in the worksheet as 8 byte float in IEEE format.
*/
#ifdef HAVE_ANSIC_C
void
do_float(int fd, double value, int row, int column)
{
#else
do_float(fd, value, row, column)
int fd;
double value;
int row,column;
{
#endif
struct float_record floatrec;
short s_row,s_column;
unsigned char *sptr,*dptr;
s_row=(short)row;
s_column=(short)column;
floatrec.hi_opcode=FLOAT;
floatrec.lo_opcode=0x00;
floatrec.hi_length=0xf;
floatrec.lo_length=0x00;
floatrec.rgbhi=0x0;
floatrec.rgbmed=0x0;
floatrec.rgblo=0x0;
floatrec.hi_row=(char)(s_row&0xff);
floatrec.lo_row=(char)((s_row>>8)&0xff);
floatrec.hi_column=(char)(s_column&0xff);
floatrec.lo_column=(char)((s_column>>8)&0xff);
sptr =(unsigned char *) &value;
dptr =(unsigned char *) &floatrec.data;
if(endian()==ENDIAN_2) /* Big Endian */
{
dptr[0]=sptr[7]; /* Convert to Little Endian */
dptr[1]=sptr[6];
dptr[2]=sptr[5];
dptr[3]=sptr[4];
dptr[4]=sptr[3];
dptr[5]=sptr[2];
dptr[6]=sptr[1];
dptr[7]=sptr[0];
}
if(endian()==ENDIAN_3) /* Middle Endian */
{
dptr[0]=sptr[4]; /* 16 bit swapped ARM */
dptr[1]=sptr[5];
dptr[2]=sptr[6];
dptr[3]=sptr[7];
dptr[4]=sptr[0];
dptr[5]=sptr[1];
dptr[6]=sptr[2];
dptr[7]=sptr[3];
}
if(endian()==ENDIAN_1) /* Little Endian */
{
dptr[0]=sptr[0]; /* Do not convert to Little Endian */
dptr[1]=sptr[1];
dptr[2]=sptr[2];
dptr[3]=sptr[3];
dptr[4]=sptr[4];
dptr[5]=sptr[5];
dptr[6]=sptr[6];
dptr[7]=sptr[7];
}
if(endian()==-1) /* Unsupported architecture */
{
dptr[0]=0;
dptr[1]=0;
dptr[2]=0;
dptr[3]=0;
dptr[4]=0;
dptr[5]=0;
dptr[6]=0;
dptr[7]=0;
printf("Excel output not supported on this architecture.\n");
}
junk=write(fd,&floatrec,11); /* Don't write floatrec. Padding problems */
junk=write(fd,&floatrec.data,8); /* Write value seperately */
}
/*
* Put a string as a label in the worksheet.
*/
#ifdef HAVE_ANSIC_C
void
do_label(int fd, char *string, int row, int column)
{
#else
do_label(fd, string, row, column)
int fd;
char *string;
int row,column;
{
#endif
struct label_record labelrec;
short s_row,s_column;
int i;
for(i=0;i<255;i++)
labelrec.str_array[i]=0;
s_row=(short)row;
s_column=(short)column;
i=strlen(string);
labelrec.hi_opcode=LABEL;
labelrec.lo_opcode=0x00;
labelrec.hi_length=0x08; /* 264 total bytes */
labelrec.lo_length=0x01;
labelrec.rgblo=0x0;
labelrec.rgbmed=0x0;
labelrec.rgbhi=0x0;
labelrec.hi_row=(char)(s_row&0xff);
labelrec.lo_row=(char)((s_row>>8)&0xff);
labelrec.hi_column=(char)(s_column&0xff);
labelrec.lo_column=(char)((s_column>>8)&0xff);
labelrec.string_length=i;
if(i > 255) /* If too long then terminate it early */
string[254]=0;
i=strlen(string);
strcpy(labelrec.str_array,string);
junk=write(fd,&labelrec,sizeof(struct label_record));
}
/*
* Write the EOF in the file
*/
#ifdef HAVE_ANSIC_C
void
do_eof(int fd)
{
#else
do_eof(fd)
int fd;
{
#endif
char buf[]={0x0a,0x00,0x00,0x00};
junk=write(fd,buf,4);
}
/*
* Routine to determine the Endian-ness of the system. This
* is needed for Iozone to convert doubles (floats) into
* Little-endian format. This is needed for Excel to be
* able to interpret the file
*/
int
endian(void)
{
long long foo = 0x0102030405060708LL;
long foo1 = 0x012345678;
unsigned char *c,c1,c2,c3,c4,c5,c6,c7,c8;
c=(unsigned char *)&foo;
c1=*c++;
c2=*c++;
c3=*c++;
c4=*c++;
c5=*c++;
c6=*c++;
c7=*c++;
c8=*c;
/*--------------------------------------------------------------*/
/* printf("%x %x %x %x %x %x %x %x\n",c1,c2,c3,c4,c5,c6,c7,c8); */
/*--------------------------------------------------------------*/
/* Little Endian format ? ( Intel ) */
if( (c1==0x08) && (c2==0x07) && (c3==0x06) && (c4==0x05) &&
(c5==0x04) && (c6==0x03) && (c7==0x02) && (c8==0x01) )
return(ENDIAN_1);
/* Big Endian format ? ( Sparc, Risc... */
if( (c1==0x01) && (c2==0x02) && (c3==0x03) && (c4==0x04) &&
(c5==0x05) && (c6==0x06) && (c7==0x07) && (c8==0x08) )
return(ENDIAN_2);
/* Middle Endian format ? ( ARM ... ) */
if( (c1==0x04) && (c2==0x03) && (c3==0x02) && (c4==0x01) &&
(c5==0x08) && (c6==0x07) && (c7==0x06) && (c8==0x05) )
return(ENDIAN_3);
c=(unsigned char *)&foo1;
c1=*c++;
c2=*c++;
c3=*c++;
c4=*c++;
/* Another middle endian format ? ( PDP-11 ... ) */
if( (c1==0x34) && (c2==0x12) && (c3==0x78) && (c4==0x56))
return(ENDIAN_4);
return(-1);
}