]> www.wagner.pp.ru Git - oss/catdoc.git/blob - src/xlsparse.c
Make clang static analyzer 3.5 happy
[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                 fprintf(stderr,"File is write protected\n");
152                 break;
153         }
154                 
155         case 0x42: {
156                 if (source_charset) break;
157                 codepage=getshort(rec,0);
158                 /*fprintf(stderr,"CODEPAGE %d\n",codepage); */
159                 if (codepage!=1200) {
160                         const char *cp = charset_from_codepage(codepage); 
161                         source_charset=read_charset(cp);
162                 }                
163                 break;
164         }  
165         case FORMAT: {
166                 int format_code;
167                 format_code=getshort(rec,0);
168                 SetFormatIdxUsed(format_code);
169                 /* this debug code prints format string */
170                 /*                                                        
171                         int i;
172                         char *ptr;
173                         fprintf(stderr,"Format %x \"",format_code);
174                         if (rec[2] == reclen - 3 && rec[3] != 0) {
175                         for (i=0,ptr=rec+3;i<rec[2];i++,ptr++) {
176                         fputc(*ptr,stderr);
177                         }
178                         } else {
179                         for (i=0,ptr=rec+5;i<rec[2];i++,ptr+=2) {
180                         fputc(*ptr,stderr);
181                         }
182                         }
183                         fprintf (stderr,"\"\n");
184                 */
185                 break;
186         }                        
187         case SST: {
188                 /* Just copy SST into buffer, and wait until we get
189                  * all CONTINUE records
190                  */
191 /*              fprintf(stderr,"SST\n"); */
192                 /* If exists first SST entry, then just drop it and start new*/
193                 if (sstBuffer != NULL) 
194                         free(sstBuffer);
195                 if (sst != NULL)
196                         free(sst);
197                 
198                 sstBuffer=(unsigned char*)malloc(reclen);
199                 sstBytes = reclen;
200                 if (sstBuffer == NULL ) {
201                         perror("SSTptr alloc error! ");
202                         exit(1);
203                 }         
204                 memcpy(sstBuffer,rec,reclen);
205                 break;
206         }       
207         case CONTINUE: {
208                 if (prev_rectype != SST) {
209                         return; /* to avoid changing of prev_rectype;*/
210                 }    
211                 sstBuffer=realloc(sstBuffer,sstBytes+reclen);
212                 if (sstBuffer == NULL ) {
213                         perror("SSTptr realloc error! ");
214                         exit(1);
215                 }         
216                 memcpy(sstBuffer+sstBytes,rec,reclen);
217                 sstBytes+=reclen;
218                 return;
219         }                          
220         case LABEL: {
221                 int row,col;
222                 unsigned char **pcell;
223                 unsigned char *src=(unsigned char *)rec+6;
224
225                 saved_reference=NULL;
226                 row = getshort(rec,0); 
227                 col = getshort(rec,2);
228                 /*              fprintf(stderr,"LABEL!\n"); */
229                 pcell=allocate(row,col);
230                 *pcell=copy_unicode_string(&src);
231                 break;
232         }     
233         case BLANK: { int row,col;unsigned char **pcell;
234                         row = getshort(rec,0);
235                         col = getshort(rec,2);
236                         pcell=allocate(row,col);
237                         *pcell=NULL;
238                         break;
239         }
240         case MULBLANK: {
241                 int row, startcol,endcol;
242                 unsigned char **pcell;
243                 row = getshort(rec,0);
244                 startcol = getshort(rec,2);
245                 endcol=getshort(rec,reclen-2);
246                 pcell=allocate(row,endcol);
247                 *pcell=NULL;
248                 (void)startcol;
249                 break;
250         }          
251         case CONSTANT_STRING: {
252                 int row = getshort(rec,0); 
253                 int col = getshort(rec,2);
254                 unsigned char **pcell;
255                 int string_no=getshort(rec,6);
256                 if (!sst) {
257                         fprintf(stderr,"CONSTANT_STRING before SST parsed\n");
258                         exit(1);
259                 }    
260                 /*                                                                      fprintf(stderr,"col=%d row=%d no=%d\n",col,row,string_no); */
261                                                                         
262                 saved_reference=NULL;
263                 pcell=allocate(row,col);
264                 if (string_no>=sstsize|| string_no < 0 ) {
265                         fprintf(stderr,"string index out of boundary\n"); 
266                         exit(1);         
267                 } else if (sst[string_no] !=NULL) {     
268                         int len;
269                         unsigned char *outptr;
270                         len=strlen((char *)sst[string_no]);
271                         outptr=*pcell=malloc(len+1);
272                         strcpy((char *)outptr,(char *)sst[string_no]);
273                 } else {
274                         *pcell=malloc(1);
275                         **pcell = 0;
276                 }       
277                 break;
278         }
279         case 0x03:
280         case 0x103:
281         case 0x303:
282         case NUMBER: {
283                 int row,col;
284                 unsigned char **pcell;
285
286                 saved_reference=NULL;
287                 row = getshort(rec,0)-startrow; 
288                 col = getshort(rec,2);
289                 pcell=allocate(row,col);
290                 *pcell=(unsigned char *)strdup(format_double(rec,6,getshort(rec,4)));
291                 break;
292         }
293         case INTEGER_CELL: {
294                 int row,col;
295                 unsigned char **pcell;
296
297                 row = getshort(rec,0)-startrow;
298                 col = getshort(rec,2);
299                 pcell=allocate(row,col);
300                 *pcell=(unsigned char *)strdup(format_int(getshort(rec,7),getshort(rec,4)));              
301                 break;
302
303         }                                 
304         case RK: {
305                 int row,col,format_code;
306                 unsigned char **pcell;
307
308                 saved_reference=NULL;
309                 row = getshort(rec,0)-startrow; 
310                 col = getshort(rec,2);
311                 pcell=allocate(row,col);
312                 format_code = getshort(rec,4);
313                 *pcell=(unsigned char *)strdup(format_rk(rec+6,format_code));
314                 break;
315         }
316         case MULRK: {
317                 int row,col,startcol,endcol,offset,format_code;
318                 unsigned char **pcell;
319                 row = getshort(rec,0)-startrow; 
320                 startcol = getshort(rec,2);
321                 endcol = getshort(rec,reclen-2);
322                 saved_reference=NULL;
323
324                 for (offset=4,col=startcol;col<=endcol;offset+=6,col++) { 
325                         pcell=allocate(row,col);
326                         format_code=getshort(rec,offset);
327                         *pcell=(unsigned char *)strdup(format_rk(rec+offset+2,format_code));
328
329                 }                
330                 break;
331         } 
332         case FORMULA: { 
333                 int row,col;
334                 unsigned char **pcell;
335                 saved_reference=NULL;
336                 row = getshort(rec,0)-startrow; 
337                 col = getshort(rec,2);
338                 pcell=allocate(row,col);
339                 if (((unsigned char)rec[12]==0xFF)&&(unsigned char)rec[13]==0xFF) {
340                         /* not a floating point value */
341                         if (rec[6]==1) {
342                                 /*boolean*/
343                                 char buf[2]="0";
344                                 buf[0]+=rec[9];
345                                 *pcell=(unsigned char *)strdup(buf);
346                         } else if (rec[6]==2) {
347                                 /*error*/
348                                 char buf[6]="ERROR";
349                                 *pcell=(unsigned char *)strdup(buf);
350                         } else if (rec[6]==0) {
351                                 saved_reference=pcell;
352                         }   
353                 } else {
354                         int format_code=getshort(rec,4);
355                         *pcell=(unsigned char *)strdup(format_double(rec,6,format_code));
356                 }                
357                 break;
358         }
359         case STRING: {
360                 unsigned char *src=(unsigned char *)rec;
361                 if (!saved_reference) {
362                         fprintf(stderr,"String record without preceeding string formula\n");
363                         break;
364                 }
365                 *saved_reference=copy_unicode_string(&src);
366                 break;
367         }           
368         case BOF: {
369                 if (rowptr) {
370                         fprintf(stderr,"BOF when current sheet is not flushed\n");
371                         free_sheet();
372                 }
373                 break;
374         }         
375         case XF:
376         case 0x43: /*from perl module Spreadsheet::ParseExecel */                 
377                 {
378                         short int formatIndex = getshort(rec,2);
379                         /* we are interested only in format index here */ 
380                         if (formatTableIndex >= formatTableSize) {
381                                 formatTable=realloc(formatTable,
382                                                                                                                 (formatTableSize+=16)*sizeof(short int));
383                                                   
384                                 if (!formatTable) {
385                                         fprintf(stderr,"Out of memory for format table");
386                                         exit (1);
387                                 }         
388                         }       
389                         formatTable[formatTableIndex++] = formatIndex;
390                         break;
391                 } 
392         case MS1904: /* Macintosh 1904 date system */ 
393                 date_shift=24107.0; 
394                 break;   
395                                                  
396                                          
397         case MSEOF: {
398                 if (!rowptr) break;
399                 print_sheet();
400                 free_sheet();
401                 break;
402         }
403         case ROW: { 
404                 /*              fprintf(stderr,"Row! %d %d %d\n",getshort(rec,0), getshort(rec+2,0),getshort(rec+4,0));  */
405                 break; 
406         } 
407         case INDEX: {
408                 /*              fprintf(stderr,"INDEX! %d %d\n", getlong(rec+4,0), getlong(rec+8,0));  */
409                 break;
410         }
411         default: {
412 #if     0       
413                 fprintf(stderr,"Unknown record 0x%x\n length %d\n",rectype,reclen);             
414 #endif 
415         }
416         }
417         prev_rectype=rectype; 
418 }  
419
420 /*
421  * Extracts string from sst and returns mallocked copy of it
422  */
423 unsigned char *copy_unicode_string (unsigned char **src) {
424         int count=0;
425         int flags = 0;
426         int start_offset=0;
427         int to_skip=0;  /* Used to counmt data after end of string */ 
428         int offset = 1; /* Variable length of the first field  */
429         int charsize;
430         /*      char *realstart=*src; */
431         unsigned char *dest;/* where to copy string */
432         unsigned char *s,*d,*c;
433
434         int i,u,l,len;
435
436         /*      for(i=0;i<20;i++) */
437         /*              fprintf(stderr,"%02x ",(*src)[i]); */
438                 /*      fprintf(stderr,"\n"); */
439
440         flags = *(*src+1+offset);
441         if (! ( flags == 0 || flags == 1 || flags == 8 || flags == 9 ||
442                                         flags == 4 || flags == 5 || flags == 0x0c || flags == 0x0d ) ) {
443                 count=**src;
444                 flags = *(*src+offset);
445                 offset --;
446                 if (! ( flags == 0 || flags == 1 || flags == 8 || flags == 9 ||
447                                                 flags == 4 || flags == 5 || flags == 0x0c || flags == 0x0d ) ) {
448                         /*                      fprintf(stderr,"Strange flags = %d, returning NULL\n", flags); */
449                         return NULL;
450                 }
451         }
452         else {
453                 count=getshort(*src,0);
454         }   
455         charsize=(flags &0x01) ? 2 : 1;
456
457         switch (flags & 12 ) {
458         case 0x0c: /* Far East with RichText formating */
459                 to_skip=4*getshort(*src,2+offset)+getlong(*src, 4+offset);
460                 start_offset=2+offset+2+4;
461                 /*              fprintf(stderr,"Far East with RichText formating\n"); */
462                 break;
463
464         case 0x08: /* With RichText formating */
465                 to_skip=4*getshort(*src,2+offset);
466                 start_offset=2+offset+2;
467                 /*              fprintf(stderr,"With RichText formating %d\n",getshort(*src,2+offset)); */
468                 break;
469
470         case 0x04: /* Far East */
471                 to_skip=getlong(*src, 2+offset);
472                 start_offset=2+offset+4;
473                 /*              fprintf(stderr,"Far East\n"); */
474                 break;
475
476         default:
477                 to_skip=0;
478                 start_offset=2+offset;
479                 /*              fprintf(stderr,"Default string\n"); */
480         }
481
482         /*      fprintf(stderr,"count=%d skip=%d start_offset=%d\n", */
483         /*                                      count, to_skip, start_offset); */
484         /* Á ÚÄÅÓØ ÍÙ ËÏÐÉÒÕÅÍ ÓÔÒÏËÕ   */
485         if ( (dest=malloc(count+1)) == NULL ) {
486                 perror("Dest string alloc error");
487                 *src+=(to_skip+start_offset+(count*charsize));
488                 exit(0);
489         }
490         *src+=start_offset;
491         len = count;
492         *dest=0;l=0;
493         for (s=*src,d=dest,i=0;i<count;i++,s+=charsize) {
494                 /*              fprintf(stderr,"l=%d len=%d count=%d charsize=%d\n",l,len,count,charsize); */
495                 if ( (charsize == 1 && (*s == 1 || *s == 0)) ||
496                                  (charsize == 2 && (*s == 1 || *s == 0) && *(s+1) != 4)) {
497                         /*                      fprintf(stderr,"extchar (unicode)=%02x %02x\n",*s, *(s+1)); */
498                         charsize=(*s &0x01) ? 2 : 1;
499                         if (charsize == 2)
500                                 s-=1;
501                         count++;
502                         continue;
503                 }
504                 if ( charsize == 2 ){
505                         u=(unsigned short)getshort(s,0);
506                         c=(unsigned char *)convert_char(u);
507                         /*                      fprintf(stderr,"char=%02x %02x\n", *s, *(s+1)); */
508                 } else {
509                         if (!source_charset) {
510                                 check_charset(&source_csname,source_csname);    
511                                 /* fprintf(stderr,"charset=%s\n",source_csname);*/
512                                 source_charset=read_charset(source_csname);
513                         }       
514                         u=(unsigned short)to_unicode(source_charset,(unsigned char)*s);
515                         c=(unsigned char *)convert_char(u);
516                 }
517                 if (c != NULL) {
518                         int dl = strlen((char *)c);
519                         while (l+dl>=len) {
520                                 len+=16;
521                                 dest=realloc(dest,len+1);
522                         }
523                         d=dest+l;
524                         strcpy((char *)d,(char *)c);
525                         l+=dl;
526                 }      
527         }
528         *src=s+to_skip;
529         return dest;
530 }
531
532
533 /* 
534  * Format code is index into format table (which is list of XF records
535  * in the file 
536  * Second word of XF record is format type idnex 
537  * format index between 0x0E and 0x16 also between 0x2D and ox2F denotes
538  * date if it is not used for explicitly stored formats.
539  * BuiltInDateFormatIdx converts format index into index of explicit
540  * built-in date formats sutable for strftime.
541  */
542 int BuiltInDateFormatIdx (int index) {
543         int offset;
544         offset=1; /* offset of date formats */
545         /* 0 is used as false -- format not found */
546         if ((index>= 0x0E) && (index<=0x16)) {
547                 return offset+index-0x0E;
548         } else  
549                 if ((index>=0x2d) && (index<=0x2F)) {
550                         return offset+index-0x2d+9;
551                 } else if (index==0xa4) {       
552                         return 12+offset;
553                 } else  
554                         return 0;
555 }       
556
557 /* 
558  * GetBuiltInDateFormat stores and returns
559  * built in xls2csv strftime formats.
560  */
561 #define NUMOFDATEFORMATS 13
562 char *GetBuiltInDateFormat(int dateindex) {
563         static char *formats[]={
564                 /* reserved  */ NULL, /* BuiltInDateFormatIdx use dateindex=0 as flag format not found */
565                 /* 0x0E */ "%m-%d-%y",          /* 01 */
566                 /* 0x0F */ "%d-%b-%y",          /* 02 */
567                 /* 0x10 */ "%d-%b",             /* 03 */
568                 /* 0x11 */ "%b-%d",             /* 04 */
569                 /* 0x12 */ "%l:%M %p",          /* 05 */
570                 /* 0x13 */ "%l:%M:%S %p",               /* 06 */
571                 /* 0x14 */ "%H:%M",             /* 07 */
572                 /* 0x15 */ "%H:%M:%S",          /* 08 */
573                 /* 0x16 */ "%m-%d-%y %H:%M",    /* 09 */
574                 /* 0x2d */ "%M:%S",             /* 10 */
575                 /* 0x2e */ "%H:%M:%S",          /* 11 */
576                 /* 0x2f */ "%M:%S",             /* 12 */
577                 /* 0xa4 */ "%m.%d.%Y %l:%M:%S %p"       /* 13 */
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[0]=dconv.cc[0] & 0xfc;
730 # else       
731                 for(s=rec,d=dconv.cc+4,i=0;
732                                 i<4;i++) *(d++)=*(s++);
733                 dconv.cc[3]=dconv.cc[3] & 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 }