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