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