]> www.wagner.pp.ru Git - oss/catdoc.git/blob - src/xlsparse.c
Add detection of ZIP-archive and report that this type of file (i.e. OOXML or OpenDoc...
[oss/catdoc.git] / src / xlsparse.c
1 /*****************************************************************/
2 /* BIFF-stream (excel file) parsing                              */
3 /*                                                               */
4 /* This file is part of catdoc project                           */
5 /* (c) David Rysdam  1998                                        */
6 /* (c) Victor Wagner 1998-2003, (c) Alex Ott 2003                      */
7 /*****************************************************************/
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11 #include <stdlib.h>
12 #include <string.h>
13 #include "xls.h"
14 #include "catdoc.h"
15 #include "xltypes.h"
16 #include "float.h"
17 #include <math.h>
18 #include <time.h>
19 #ifndef HAVE_STRFTIME
20 #include "../compat/strftime.h"
21 #endif
22 static unsigned char rec[MAX_MS_RECSIZE];
23 int biff_version=0;
24 short int *formatTable=NULL;
25 char *forced_date_format = NULL;
26 size_t formatTableIndex = 0;
27 size_t formatTableSize = 0;
28 double date_shift = 25569.0;
29 #define FLT_FORMAT(a,b,c) a #b c
30 #define MK_FORMAT(x) FLT_FORMAT("%.",x,"g")                     
31 char number_format[8]=MK_FORMAT(DBL_DIG);
32
33 void CleanUpFormatIdxUsed(void);
34
35 void do_table(FILE *input,char *filename) {    
36         long rectype;
37         long reclen,build_year=0,build_rel=0,offset=0;
38         int eof_flag=0;
39         int itemsread=1;
40         date_shift=25569.0; /* Windows 1900 date system */
41         CleanUpFormatIdxUsed();
42         while (itemsread) {
43                 catdoc_read(rec,2,1,input);
44                 biff_version=getshort(rec,0);
45                 catdoc_read(rec,2,1,input);
46                 reclen=getshort(rec,0);
47                 if ( biff_version == 0x0809 || biff_version == 0x0409 ||
48                                  biff_version == 0x0209 || biff_version == 0x0009 ) {
49                         if (reclen==8 || reclen==16) {
50                                 if (biff_version == 0x0809 ) {
51                                         itemsread=catdoc_read(rec,4,1,input);
52                                         if (itemsread == 0) 
53                                                 break;
54                                         build_year=getshort(rec+2,0);
55                                         build_rel=getshort(rec,0);
56                                         (void) build_rel;
57                                         if(build_year > 5 ) {
58                                                 catdoc_read(rec,8,1,input);
59                                                 biff_version=8;
60                                                 offset=12;
61                                         }
62                                         else {
63                                                 biff_version=7;
64                                                 offset=4;
65                                         }
66                                 } else if (biff_version == 0x0209 ) {
67                                         biff_version=3;
68                                         offset=2;
69                                 } else if (biff_version == 0x0409 ) {
70                                         offset=2;
71                                         biff_version=4;
72                                 } else {
73                                         biff_version=2;
74                                 }
75                                 itemsread=catdoc_read(rec,reclen-offset,1,input);
76                                 break;
77                         } else {
78                                 fprintf(stderr,"%s: Invalid BOF record\n",filename);
79                                 return;
80                         } 
81                 } else {
82                         itemsread=catdoc_read(rec,126,1,input);
83                 }
84         }
85         if (catdoc_eof(input)) {
86                 fprintf(stderr,"%s: No BOF record found\n",filename);
87                 exit(1);
88         }    
89         while(itemsread){
90                 unsigned char buffer[2];
91
92                 itemsread = catdoc_read(buffer, 2, 1, input);
93                 if (catdoc_eof(input)) {
94                         process_item(MSEOF,0,NULL);
95                         return;
96                 }
97                 
98                 if(itemsread == 0)
99                         break;
100
101                 rectype=getshort(buffer,0);
102                 itemsread = catdoc_read(buffer, 2, 1, input);
103                 if(itemsread == 0)
104                         break;
105                 reclen=getshort(buffer,0);
106                 if (reclen && reclen <MAX_MS_RECSIZE &&reclen >0){
107                         itemsread = catdoc_read(rec, 1, reclen, input);
108                         rec[reclen] = '\0';
109                 }
110                 if(eof_flag) {
111                         if (rectype != BOF) {
112                                 break;
113                         }    
114                 }
115 /*              fprintf(stderr,"Rectype 0x%04X reclen=%d\n",rectype, reclen); */
116                 process_item(rectype,reclen,rec);
117                 if (rectype == MSEOF) {
118                         eof_flag=1;
119                 } else {
120                         eof_flag=0;     
121                 }  
122         }
123         return;
124 }
125 unsigned char **sst=NULL;/* Shared string table parsed into array of strings in
126                                                                                                                 output encoding*/
127 int sstsize = 0; /*Number of strings in SST*/
128 unsigned char *sstBuffer=NULL; /*Unparsed sst to accumulate all its parts*/
129 int sstBytes = 0; /*Size of SST Data, already accumulated in the buffer */
130 int codepage=1251; /*default*/
131 int prev_rectype=0;
132 /* holds a pointer to formula value, becouse value itself would be in
133  * next biff record
134  */
135 unsigned char **saved_reference = NULL;
136
137 void process_item (int rectype, int reclen, unsigned char *rec) {
138         if (rectype != CONTINUE && prev_rectype == SST) {
139                 /* we have accumulated  unparsed SST, and now encountered
140                  * another record, which indicates that SST is ended */
141                 /* fprintf(stderr,"parse sst!\n");*/
142                 parse_sst(sstBuffer,sstBytes);
143         }       
144         switch (rectype) {
145         case FILEPASS: {
146                 fprintf(stderr,"File is encrypted\n");
147                 exit(69);
148                 break;
149         }
150         case WRITEPROT: 
151                 /* File is write protected, but we only read it */
152                 break;
153         case 0x42: {
154                 if (source_charset) break;
155                 codepage=getshort(rec,0);
156                 /*fprintf(stderr,"CODEPAGE %d\n",codepage); */
157                 if (codepage!=1200) {
158                         const char *cp = charset_from_codepage(codepage); 
159                         source_charset=read_charset(cp);
160                 }                
161                 break;
162         }  
163         case FORMAT: {
164                 int format_code;
165                 format_code=getshort(rec,0);
166                 SetFormatIdxUsed(format_code);
167                 /* this debug code prints format string */
168                 /*                                                        
169                         int i;
170                         char *ptr;
171                         fprintf(stderr,"Format %x \"",format_code);
172                         if (rec[2] == reclen - 3 && rec[3] != 0) {
173                         for (i=0,ptr=rec+3;i<rec[2];i++,ptr++) {
174                         fputc(*ptr,stderr);
175                         }
176                         } else {
177                         for (i=0,ptr=rec+5;i<rec[2];i++,ptr+=2) {
178                         fputc(*ptr,stderr);
179                         }
180                         }
181                         fprintf (stderr,"\"\n");
182                 */
183                 break;
184         }                        
185         case SST: {
186                 /* Just copy SST into buffer, and wait until we get
187                  * all CONTINUE records
188                  */
189 /*              fprintf(stderr,"SST\n"); */
190                 /* If exists first SST entry, then just drop it and start new*/
191                 if (sstBuffer != NULL) 
192                         free(sstBuffer);
193                 if (sst != NULL)
194                         free(sst);
195                 
196                 sstBuffer=(unsigned char*)malloc(reclen);
197                 sstBytes = reclen;
198                 if (sstBuffer == NULL ) {
199                         perror("SSTptr alloc error! ");
200                         exit(1);
201                 }         
202                 memcpy(sstBuffer,rec,reclen);
203                 break;
204         }       
205         case CONTINUE: {
206                 if (prev_rectype != SST) {
207                         return; /* to avoid changing of prev_rectype;*/
208                 }    
209                 sstBuffer=realloc(sstBuffer,sstBytes+reclen);
210                 if (sstBuffer == NULL ) {
211                         perror("SSTptr realloc error! ");
212                         exit(1);
213                 }         
214                 memcpy(sstBuffer+sstBytes,rec,reclen);
215                 sstBytes+=reclen;
216                 return;
217         }                          
218         case LABEL: {
219                 int row,col;
220                 unsigned char **pcell;
221                 unsigned char *src=(unsigned char *)rec+6;
222
223                 saved_reference=NULL;
224                 row = getshort(rec,0); 
225                 col = getshort(rec,2);
226                 /*              fprintf(stderr,"LABEL!\n"); */
227                 pcell=allocate(row,col);
228                 *pcell=copy_unicode_string(&src);
229                 break;
230         }     
231         case BLANK: { int row,col;unsigned char **pcell;
232                         row = getshort(rec,0);
233                         col = getshort(rec,2);
234                         pcell=allocate(row,col);
235                         *pcell=NULL;
236                         break;
237         }
238         case MULBLANK: {
239                 int row, startcol,endcol;
240                 unsigned char **pcell;
241                 row = getshort(rec,0);
242                 startcol = getshort(rec,2);
243                 endcol=getshort(rec,reclen-2);
244                 pcell=allocate(row,endcol);
245                 *pcell=NULL;
246                 (void)startcol;
247                 break;
248         }          
249         case CONSTANT_STRING: {
250                 int row = getshort(rec,0); 
251                 int col = getshort(rec,2);
252                 unsigned char **pcell;
253                 int string_no=getshort(rec,6);
254                 if (!sst) {
255                         fprintf(stderr,"CONSTANT_STRING before SST parsed\n");
256                         exit(1);
257                 }    
258                 /*                                                                      fprintf(stderr,"col=%d row=%d no=%d\n",col,row,string_no); */
259                                                                         
260                 saved_reference=NULL;
261                 pcell=allocate(row,col);
262                 if (string_no>=sstsize|| string_no < 0 ) {
263                         fprintf(stderr,"string index out of boundary\n"); 
264                         exit(1);         
265                 } else if (sst[string_no] !=NULL) {     
266                         int len;
267                         unsigned char *outptr;
268                         len=strlen((char *)sst[string_no]);
269                         outptr=*pcell=malloc(len+1);
270                         strcpy((char *)outptr,(char *)sst[string_no]);
271                 } else {
272                         *pcell=malloc(1);
273                         **pcell = 0;
274                 }       
275                 break;
276         }
277         case 0x03:
278         case 0x103:
279         case 0x303:
280         case NUMBER: {
281                 int row,col;
282                 unsigned char **pcell;
283
284                 saved_reference=NULL;
285                 row = getshort(rec,0)-startrow; 
286                 col = getshort(rec,2);
287                 pcell=allocate(row,col);
288                 *pcell=(unsigned char *)strdup(format_double(rec,6,getshort(rec,4)));
289                 break;
290         }
291         case INTEGER_CELL: {
292                 int row,col;
293                 unsigned char **pcell;
294
295                 row = getshort(rec,0)-startrow;
296                 col = getshort(rec,2);
297                 pcell=allocate(row,col);
298                 *pcell=(unsigned char *)strdup(format_int(getshort(rec,7),getshort(rec,4)));              
299                 break;
300
301         }                                 
302         case RK: {
303                 int row,col,format_code;
304                 unsigned char **pcell;
305
306                 saved_reference=NULL;
307                 row = getshort(rec,0)-startrow; 
308                 col = getshort(rec,2);
309                 pcell=allocate(row,col);
310                 format_code = getshort(rec,4);
311                 *pcell=(unsigned char *)strdup(format_rk(rec+6,format_code));
312                 break;
313         }
314         case MULRK: {
315                 int row,col,startcol,endcol,offset,format_code;
316                 unsigned char **pcell;
317                 row = getshort(rec,0)-startrow; 
318                 startcol = getshort(rec,2);
319                 endcol = getshort(rec,reclen-2);
320                 saved_reference=NULL;
321
322                 for (offset=4,col=startcol;col<=endcol;offset+=6,col++) { 
323                         pcell=allocate(row,col);
324                         format_code=getshort(rec,offset);
325                         *pcell=(unsigned char *)strdup(format_rk(rec+offset+2,format_code));
326
327                 }                
328                 break;
329         } 
330         case FORMULA: { 
331                 int row,col;
332                 unsigned char **pcell;
333                 saved_reference=NULL;
334                 row = getshort(rec,0)-startrow; 
335                 col = getshort(rec,2);
336                 pcell=allocate(row,col);
337                 if (((unsigned char)rec[12]==0xFF)&&(unsigned char)rec[13]==0xFF) {
338                         /* not a floating point value */
339                         if (rec[6]==1) {
340                                 /*boolean*/
341                                 char buf[2]="0";
342                                 buf[0]+=rec[9];
343                                 *pcell=(unsigned char *)strdup(buf);
344                         } else if (rec[6]==2) {
345                                 /*error*/
346                                 char buf[6]="ERROR";
347                                 *pcell=(unsigned char *)strdup(buf);
348                         } else if (rec[6]==0) {
349                                 saved_reference=pcell;
350                         }   
351                 } else {
352                         int format_code=getshort(rec,4);
353                         *pcell=(unsigned char *)strdup(format_double(rec,6,format_code));
354                 }                
355                 break;
356         }
357         case STRING: {
358                 unsigned char *src=(unsigned char *)rec;
359                 if (!saved_reference) {
360                         fprintf(stderr,"String record without preceeding string formula\n");
361                         break;
362                 }
363                 *saved_reference=copy_unicode_string(&src);
364                 break;
365         }           
366         case BOF: {
367                 if (rowptr) {
368                         fprintf(stderr,"BOF when current sheet is not flushed\n");
369                         free_sheet();
370                 }
371                 break;
372         }         
373         case XF:
374         case 0x43: /*from perl module Spreadsheet::ParseExecel */                 
375                 {
376                         short int formatIndex = getshort(rec,2);
377                         /* we are interested only in format index here */ 
378                         if (formatTableIndex >= formatTableSize) {
379                                 formatTable=realloc(formatTable,
380                                                                                                                 (formatTableSize+=16)*sizeof(short int));
381                                                   
382                                 if (!formatTable) {
383                                         fprintf(stderr,"Out of memory for format table");
384                                         exit (1);
385                                 }         
386                         }       
387                         formatTable[formatTableIndex++] = formatIndex;
388                         break;
389                 } 
390         case MS1904: /* Macintosh 1904 date system */ 
391                 date_shift=24107.0; 
392                 break;   
393                                                  
394                                          
395         case MSEOF: {
396                 if (!rowptr) break;
397                 print_sheet();
398                 free_sheet();
399                 break;
400         }
401         case ROW: { 
402                 /*              fprintf(stderr,"Row! %d %d %d\n",getshort(rec,0), getshort(rec+2,0),getshort(rec+4,0));  */
403                 break; 
404         } 
405         case INDEX: {
406                 /*              fprintf(stderr,"INDEX! %d %d\n", getlong(rec+4,0), getlong(rec+8,0));  */
407                 break;
408         }
409         default: {
410 #if     0       
411                 fprintf(stderr,"Unknown record 0x%x\n length %d\n",rectype,reclen);             
412 #endif 
413         }
414         }
415         prev_rectype=rectype; 
416 }  
417
418 /*
419  * Extracts string from sst and returns mallocked copy of it
420  */
421 unsigned char *copy_unicode_string (unsigned char **src) {
422         int count=0;
423         int flags = 0;
424         int start_offset=0;
425         int to_skip=0;  /* Used to counmt data after end of string */ 
426         int offset = 1; /* Variable length of the first field  */
427         int charsize;
428         /*      char *realstart=*src; */
429         unsigned char *dest;/* where to copy string */
430         unsigned char *s,*d,*c;
431
432         int i,u,l,len;
433
434         /*      for(i=0;i<20;i++) */
435         /*              fprintf(stderr,"%02x ",(*src)[i]); */
436                 /*      fprintf(stderr,"\n"); */
437
438         flags = *(*src+1+offset);
439         if (! ( flags == 0 || flags == 1 || flags == 8 || flags == 9 ||
440                                         flags == 4 || flags == 5 || flags == 0x0c || flags == 0x0d ) ) {
441                 count=**src;
442                 flags = *(*src+offset);
443                 offset --;
444                 if (! ( flags == 0 || flags == 1 || flags == 8 || flags == 9 ||
445                                                 flags == 4 || flags == 5 || flags == 0x0c || flags == 0x0d ) ) {
446                         /*                      fprintf(stderr,"Strange flags = %d, returning NULL\n", flags); */
447                         return NULL;
448                 }
449         }
450         else {
451                 count=getshort(*src,0);
452         }   
453         charsize=(flags &0x01) ? 2 : 1;
454
455         switch (flags & 12 ) {
456         case 0x0c: /* Far East with RichText formating */
457                 to_skip=4*getshort(*src,2+offset)+getlong(*src, 4+offset);
458                 start_offset=2+offset+2+4;
459                 /*              fprintf(stderr,"Far East with RichText formating\n"); */
460                 break;
461
462         case 0x08: /* With RichText formating */
463                 to_skip=4*getshort(*src,2+offset);
464                 start_offset=2+offset+2;
465                 /*              fprintf(stderr,"With RichText formating %d\n",getshort(*src,2+offset)); */
466                 break;
467
468         case 0x04: /* Far East */
469                 to_skip=getlong(*src, 2+offset);
470                 start_offset=2+offset+4;
471                 /*              fprintf(stderr,"Far East\n"); */
472                 break;
473
474         default:
475                 to_skip=0;
476                 start_offset=2+offset;
477                 /*              fprintf(stderr,"Default string\n"); */
478         }
479
480         /*      fprintf(stderr,"count=%d skip=%d start_offset=%d\n", */
481         /*                                      count, to_skip, start_offset); */
482         /* Á ÚÄÅÓØ ÍÙ ËÏÐÉÒÕÅÍ ÓÔÒÏËÕ   */
483         if ( (dest=malloc(count+1)) == NULL ) {
484                 perror("Dest string alloc error");
485                 *src+=(to_skip+start_offset+(count*charsize));
486                 exit(0);
487         }
488         *src+=start_offset;
489         len = count;
490         *dest=0;l=0;
491         for (s=*src,d=dest,i=0;i<count;i++,s+=charsize) {
492                 /*              fprintf(stderr,"l=%d len=%d count=%d charsize=%d\n",l,len,count,charsize); */
493                 if ( (charsize == 1 && (*s == 1 || *s == 0)) ||
494                                  (charsize == 2 && (*s == 1 || *s == 0) && *(s+1) != 4)) {
495                         /*                      fprintf(stderr,"extchar (unicode)=%02x %02x\n",*s, *(s+1)); */
496                         charsize=(*s &0x01) ? 2 : 1;
497                         if (charsize == 2)
498                                 s-=1;
499                         count++;
500                         continue;
501                 }
502                 if ( charsize == 2 ){
503                         u=(unsigned short)getshort(s,0);
504                         c=(unsigned char *)convert_char(u);
505                         /*                      fprintf(stderr,"char=%02x %02x\n", *s, *(s+1)); */
506                 } else {
507                         if (!source_charset) {
508                                 check_charset(&source_csname,source_csname);    
509                                 /* fprintf(stderr,"charset=%s\n",source_csname);*/
510                                 source_charset=read_charset(source_csname);
511                         }       
512                         u=(unsigned short)to_unicode(source_charset,(unsigned char)*s);
513                         c=(unsigned char *)convert_char(u);
514                 }
515                 if (c != NULL) {
516                         int dl = strlen((char *)c);
517                         while (l+dl>=len) {
518                                 len+=16;
519                                 dest=realloc(dest,len+1);
520                         }
521                         d=dest+l;
522                         strcpy((char *)d,(char *)c);
523                         l+=dl;
524                 }      
525         }
526         *src=s+to_skip;
527         return dest;
528 }
529
530
531 /* 
532  * Format code is index into format table (which is list of XF records
533  * in the file 
534  * Second word of XF record is format type idnex 
535  * format index between 0x0E and 0x16 also between 0x2D and ox2F denotes
536  * date if it is not used for explicitly stored formats.
537  * BuiltInDateFormatIdx converts format index into index of explicit
538  * built-in date formats sutable for strftime.
539  */
540 int BuiltInDateFormatIdx (int index) {
541         int offset;
542         offset=1; /* offset of date formats */
543         /* 0 is used as false -- format not found */
544         if ((index>= 0x0E) && (index<=0x16)) {
545                 return offset+index-0x0E;
546         } else {
547                 if ((index>=0x2d) && (index<=0x2F)) {
548                         return offset+index-0x2d+9;
549                 } else {        
550                         return 0;
551                 }
552         }
553 }       
554
555 /* 
556  * GetBuiltInDateFormat stores and returns
557  * built in xls2csv strftime formats.
558  */
559 #define NUMOFDATEFORMATS 12
560 char *GetBuiltInDateFormat(int dateindex) {
561         static char *formats[]={
562                 /* reserved  */ NULL, /* BuiltInDateFormatIdx use dateindex=0 as flag format not found */
563                 /* 0x0E */ "%m-%d-%y",          /* 01 */
564                 /* 0x0F */ "%d-%b-%y",          /* 02 */
565                 /* 0x10 */ "%d-%b",             /* 03 */
566                 /* 0x11 */ "%b-%d",             /* 04 */
567                 /* 0x12 */ "%l:%M %p",          /* 05 */
568                 /* 0x13 */ "%l:%M:%S %p",               /* 06 */
569                 /* 0x14 */ "%H:%M",             /* 07 */
570                 /* 0x15 */ "%H:%M:%S",          /* 08 */
571                 /* 0x16 */ "%m-%d-%y %H:%M",    /* 09 */
572                 /* 0x2d */ "%M:%S",             /* 10 */
573                 /* 0x2e */ "%H:%M:%S",          /* 11 */
574                 /* 0x2f */ "%M:%S",             /* 12 */
575 #if 0
576                 /* 0xa4 */ "%m.%d.%Y %l:%M:%S %p"       /* 13 */
577 #endif
578         };
579         if (dateindex>0 && dateindex <= NUMOFDATEFORMATS) {
580           return formats[dateindex];
581         } else  
582                 return NULL;
583 }       
584
585 static char FormatIdxUsed[NUMOFDATEFORMATS];
586
587 void CleanUpFormatIdxUsed() {
588         int i;
589         for (i=0;i<NUMOFDATEFORMATS; i++)
590                 FormatIdxUsed[i]=0;
591 }
592
593 /* 
594  * format index between 0x0E and 0x16 also between 0x2D and ox2F denotes
595  * date in case when they are built-in Excel97 formats.
596  * Nevertheless, those indexes can be used for explicitly stored formats,
597  * which are not dates in general.
598  * SetFormatIdxUsed marks this formats as already used 
599  * and excludes them from list of built-in formats 
600  * preventing misformatting of corresponding data.
601  */
602 void SetFormatIdxUsed(int format_code) {
603         int dateindex;
604         /*fprintf(stderr,"Format idx %x to be set to dirty\n",format_code);
605          */
606         dateindex=BuiltInDateFormatIdx(format_code);
607         if (dateindex) {
608           FormatIdxUsed[dateindex]=1;
609           /*fprintf(stderr,"Date idx %d is set to be dirty\n",dateindex); */
610         }
611 }       
612
613 /* 
614  * format index between 0x0E and 0x16 also between 0x2D and ox2F denotes
615  * date in case when they are built-in Excel97 formats.
616  * Nevertheless, those indexes can be used for explicitly stored formats,
617  * which are not dates in general.
618  * SetFormatIdxUsed marks this formats as already used 
619  * and excludes them from list of built-in formats 
620  * preventing misformatting of corresponding data.
621  * IsFormatIdxUsed tests this case.
622  */
623 char IsFormatIdxUsed(int format_code) {
624         int dateindex;
625         dateindex=BuiltInDateFormatIdx(format_code);
626         if (dateindex) {
627                 /*        fprintf(stderr,"Date idx %d is dirty\n",dateindex); */
628           return FormatIdxUsed[dateindex]==1;
629         }
630         else return 0;
631 }       
632
633
634 /* Checks if format denoted by given code is date
635  * Format code is index into format table (which is list of XF records
636  * in the file 
637  * Second word of XF record is format type inex 
638  * format index between 0x0E and 0x16 also between 0x2D and ox2F denotes
639  * date.
640  * If so, it returns strftime format for this date. Otherwise returns
641  * NULL
642  */
643
644
645 char *isDateFormat(int format_code) {
646         int index;
647         int dateindex;
648         if (format_code>=formatTableIndex) {
649                 fprintf(stderr,"Format code %d is used before definition\n",format_code);
650                 return NULL;
651         }
652         
653         index = formatTable[format_code];
654         if (IsFormatIdxUsed(index)) {
655                 fprintf(stderr,"Format %x is redefined\n",index);
656           /* this format is something user-defined --- not a standard built-in date*/
657           return NULL;
658         }
659         dateindex=BuiltInDateFormatIdx(index);
660         if (dateindex) {
661           if (forced_date_format) return forced_date_format;
662                 return GetBuiltInDateFormat(dateindex);
663         } else  
664                 return NULL;
665 }       
666
667
668
669 time_t float2date(double d);
670 /*
671  * Extracts floating point value and formats it 
672  */ 
673
674 char *number2string(double d,short int format_code) { 
675         static char buffer [128];
676         char *datefmt;
677         if ((datefmt=isDateFormat(format_code))!=NULL) {
678                 time_t t = float2date(d);       
679                 strftime(buffer, 127,datefmt, gmtime(&t));  
680         } else {
681                 sprintf(buffer,number_format,d);
682         }
683         return buffer;
684 }
685                 
686 char *format_double(unsigned char *rec,int offset,int format_code) {    
687         union { unsigned char cc[8];
688                 double d;} dconv;
689         unsigned char *d,*s; 
690         int i;
691 # ifdef WORDS_BIGENDIAN     
692         for(s=rec+offset+8,d=dconv.cc,i=0;
693                         i<8;i++) *(d++)=*(--s);
694 # else       
695         for(s=rec+offset,d=dconv.cc,i=0;
696                         i<8;i++) *(d++)=*(s++);
697 # endif     
698         return number2string(dconv.d,format_code);                                      
699 }
700
701 /*
702  * Formats integer value into static buffer
703  */
704 char *format_int(int value,int format_code) {
705         static char buffer[12];
706         sprintf(buffer,"%i",value);
707         return buffer;
708 }       
709 /*
710  * Formats RK record
711  */
712 char* format_rk(unsigned char *rec,short int format_code) {
713         double value=0.0;
714         int i;
715
716         if ( *(rec) & 0x02 )
717         {
718                 value=(double)(getlong(rec,0)>>2);
719         }
720         else { 
721                 union { unsigned char cc[8];
722                         double d;} dconv;
723                 unsigned char *d,*s;
724                 for(i=0;i<8;i++)
725                         dconv.cc[i]='\0';
726 # ifdef WORDS_BIGENDIAN     
727                 for(s=rec+4,d=dconv.cc,i=0; i<4;i++) 
728                         *(d++)=*(--s);
729                 dconv.cc[3]=dconv.cc[3] & 0xfc;
730 # else       
731                 for(s=rec,d=dconv.cc+4,i=0;
732                                 i<4;i++) *(d++)=*(s++);
733                 dconv.cc[4]=dconv.cc[4] & 0xfc;
734 # endif     
735                 value=dconv.d;
736         }
737         if ( *(rec) & 0x01 ) 
738                 value=value*0.01;
739         return number2string(value,format_code);
740 }
741
742
743 /* 
744  * Converts excel date into time_t
745  */
746 time_t float2date(double f) { 
747         /* Hacked version. Excell stores date as floating point count of days
748          * since 1.1.1900. or 1.1.1904
749          * We are substracting value of 1.1.1970 and multiplying
750          * by 86400 thus getting seconds from the epoch
751          */
752         return rint((f-date_shift)*86400); 
753 }
754
755 /*
756  * Parses SST into array of strings
757  */
758 void parse_sst(unsigned char *sstbuf,int bufsize) {
759         int i; /* index into sst */
760         unsigned char *curString; /* pointer into unparsed buffer*/
761         unsigned char *barrier=(unsigned char *)sstbuf+bufsize; /*pointer to end of buffer*/
762         unsigned char **parsedString;/*pointer into parsed array*/ 
763                         
764         sstsize = getlong(sstbuf+4,0);
765         sst=(unsigned char **)malloc(sstsize*sizeof(unsigned char *));
766         
767         if (sst == NULL) {
768                 perror("SST allocation error");
769                 exit(1);
770         }
771         memset(sst,0,sstsize*sizeof(char *));
772         for (i=0,parsedString=sst,curString=sstbuf+8;
773                          i<sstsize && curString<barrier; i++,parsedString++) {
774                 /*              fprintf(stderr,"copying %d string\n",i); */
775                 *parsedString = copy_unicode_string(&curString);
776         }       
777         /*      fprintf(stderr,"end sst i=%d sstsize=%d\n",i,sstsize); */
778
779 }